前言
本文承接上文《基于Django框架的网络云盘设计》,是整个网站设计中的一部分。本文将以上文实现的网络云盘为基础,通过对云盘文件url的访问实现在线播放,循环播放等功能。全部网站源码详见我的分享资源。
一、简介
本项目是云盘项目的一个扩展功能,旨在创造一套完备的服务系统。当用户点击云盘中可播放的视频文件时,将自动跳转界面至视频播放界面,通过流式传输在线播放视频。相应地配套有播放列表,自动顺序播放,观看时长统计等辅助功能。经过测试项目能稳定运行。
二、详细实现步骤
1.路由配置
使用正则表达式,对某个非文件夹文件进行访问 网盘文件路由如下:
re_path(r'^video/(?P<path>.*)/$', Do.video, name='file_download'),
re_path(r'^download/(?P<file_path>.*)/$', Do.file_download, name='file_download'),
举个例子,如果用户想要访问在网盘中视频课文件夹下的网课第一章文件夹下的第一个视频,路由应该是http://XXX.XXX.XXX.XXX/download/视频课/网课第一章/第一个视频.mp4 当用户访问上述路由,若该文件支持video标签播放,则跳转至视频播放器在线播放。若不支持播放,则沿用上一篇文章所介绍的网盘的功能,进行下载。
2.后台代码设计(对云盘接口的访问)
当用户点击某一文件夹的视频文件后,后台会自动读取该文件夹下所有可播放视频,并将视频url和视频名称传递给播放器页面。Django后台代码如下:
def file_download(request, file_path):
onepath = file_path.split('/')
oneselpath = []
sst = "http://101.42.94.148:8000/download"
for i in onepath:
sst = sst + "/" + i
oneselpath = oneselpath + [sst]
print(oneselpath)
if re.search('.mp4', file_path):
print(file_path)
print()
print(file_path.rfind('/'))
aa = file_path[0:file_path.rfind('/')]
bb1 = file_path[file_path.rfind('/') + 1:len(file_path)]
bb = file_path[file_path.rfind('/') + 1:file_path.rfind('.')]
ccc = aa[aa.rfind('/')+1:len(aa)]
FFF = []
for iii in os.listdir(aa):
if re.search('.mp4', iii):
FFF = FFF + [iii]
return render(request, 'shipinbofang.html',
{"files": file_path, "aaaa": FFF, "name": bb, "base": aa, "name1": bb1, "prebase": ccc})
if os.path.isdir(file_path):
m = 0
selectFiles = []
sizeFiles = []
timeFiles = []
FilesBig = []
FileType = []
FilePath = []
orderlistdir = []
for iii in os.listdir(file_path):
if os.path.isdir(file_path + "/" + iii):
orderlistdir = orderlistdir + [iii]
for jjj in os.listdir(file_path):
if not os.path.isdir(file_path + "/" + jjj):
orderlistdir = orderlistdir + [jjj]
print(orderlistdir)
for files in orderlistdir:
m = m + 1
print(files)
selectFiles = selectFiles + [files]
FilePath = FilePath + [file_path + "/" + files]
if os.path.isdir(file_path + "/" + files):
FileType = FileType + ["文件夹"]
full_size = sum(
sum(os.path.getsize(os.path.join(parent, file)) for file in files) for parent, dirs, files in
os.walk(file_path + "/" + files))
if full_size / (float(1024) * float(1024)) > 1024:
sizeFiles = sizeFiles + [round(full_size / float(1024 * 1024 * 1024), 1)]
FilesBig = FilesBig + ["GB"]
else:
if full_size / float(1024) > 1024:
sizeFiles = sizeFiles + [round(full_size / float(1024 * 1024), 1)]
FilesBig = FilesBig + ["MB"]
else:
sizeFiles = sizeFiles + [round(full_size / float(1024), 1)]
FilesBig = FilesBig + ["KB"]
else:
FileType = FileType + [""]
if os.path.getsize(file_path + "/" + files) / (float(1024) * float(1024)) > 1024:
sizeFiles = sizeFiles + [
round(os.path.getsize(file_path + "/" + files) / float(1024 * 1024 * 1024), 1)]
FilesBig = FilesBig + ["GB"]
else:
if os.path.getsize(file_path + "/" + files) / float(1024) > 1024:
sizeFiles = sizeFiles + [
round(os.path.getsize(file_path + "/" + files) / float(1024 * 1024), 1)]
FilesBig = FilesBig + ["MB"]
else:
sizeFiles = sizeFiles + [round(os.path.getsize(file_path + "/" + files) / float(1024), 1)]
FilesBig = FilesBig + ["KB"]
timeFiles = timeFiles + [time.ctime(os.path.getctime(file_path + "/" + files))]
files = zip(selectFiles, sizeFiles, timeFiles, FilesBig, FileType, FilePath)
onepath = zip(onepath, oneselpath)
return render(request, "yun.html", {"files": files, "path": file_path, "onepath": onepath})
else:
try:
response = StreamingHttpResponse(open(file_path, 'rb'))
response['content_type'] = "application/octet-stream"
response['Content-Disposition'] = 'attachment; filename=' + parse.quote(file_path, encoding="utf-8")
return response
except Exception:
raise Http404
3.后台代码设计(流式视频传输)
如果每次观看视频前都需要将视频全部下载后再观看,这样会造成非常不好的用户体验,也不算是在线播放视频。下面接收一种流式数据传输方式,可将视频数据如流水一般逐渐传递过来。代码如下:
def video(request, path):
range_header = request.META.get('HTTP_RANGE', '').strip()
range_re = re.compile(r'bytes\s*=\s*(\d+)\s*-\s*(\d*)', re.I)
range_match = range_re.match(range_header)
size = os.path.getsize(path)
content_type, encoding = mimetypes.guess_type(path)
content_type = content_type or 'application/octet-stream'
if range_match:
first_byte, last_byte = range_match.groups()
first_byte = int(first_byte) if first_byte else 0
last_byte = first_byte + 1024 * 1024 * 8
if last_byte >= size:
last_byte = size - 1
length = last_byte - first_byte + 1
resp = StreamingHttpResponse(file_iterator(path, offset=first_byte, length=length), status=206,
content_type=content_type)
resp['Content-Length'] = str(length)
resp['Content-Range'] = 'bytes %s-%s/%s' % (first_byte, last_byte, size)
else:
resp = StreamingHttpResponse(FileWrapper(open(path, 'rb')), content_type=content_type)
resp['Content-Length'] = str(size)
resp['Accept-Ranges'] = 'bytes'
return resp
4.前端功能设计(视频播放列表)
前端接收后台传输的视频路由,视频名字等信息,将前端列表控件的类名改为url,并设置onclick点击事件,当点击某视频控件时,js会自动切换到那个视频的url。 html部分:
<div class="message-container" id="cccdf" >
{% for NN in aaaa %}
<div class="message anim" style="--delay: .1s " onclick="videoChange('{{ base }}/{{ NN }}','{{ NN }}')" >
<div class="author-img__wrapper video-author video-p">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="3" stroke-linecap="round" stroke-linejoin="round" class="feather feather-check">
<path d="M20 6L9 17l-5-5"></path>
</svg>
<img class="author-img" src="/static/img/blibli.png">
</div>
<div class="msg-wrapper">
{% if NN == name1 %}
<div class="msg__name video-p-name offline" id={{ base }}/{{ NN }}> {{ prebase }}</div>
{% else %}
<div class="msg__name video-p-name" id={{ base }}/{{ NN }}> {{ prebase }}</div>
{% endif %}
<div class="msg__content video-p-sub">{{ NN }}</div></div>
</div>
{% endfor %}
</div>
js部分:
function videoChange(abc,abcd) {
var introEle = document.getElementById('nnmmmm');
var end = abcd.lastIndexOf(".");
introEle.innerHTML = ''+formatStringLen(abcd.slice(0,end))+'';
var is ="http://XXXXXXX/video/";
document.getElementById("media").src= is + abc;
document.title=abcd;
document.getElementById(abc).className = 'msg__name video-p-name offline';
var a = document.getElementById("cccdf").getElementsByTagName("div");
for(var i=0;i<a.length;i++){
if(a[i].id){
document.getElementById(a[i].id).className = 'msg__name video-p-name';
}
}
document.getElementById(abc).className = 'msg__name video-p-name offline';
}
5.前端功能设计(自动顺序播放)
当当前视频播放结束后会触发js函数,js会定位当前视频在视频列表中的位置,并切换到下一个视频 js代码:
document.getElementById("media").addEventListener("ended",function(){
clearInterval(time11);
var a1 = document.getElementById("cccdf").getElementsByTagName("div");
for(var i1=0;i1<a1.length;i1++){
if(a1[i1].id) {
if (document.getElementById(a1[i1].id).className == "msg__name video-p-name offline") {
var ss = a1[i1 + 5].id;
}
}
}
var introEle = document.getElementById('nnmmmm');
var end = ss.lastIndexOf(".");
var start = ss.lastIndexOf("/");
introEle.innerHTML = ''+formatStringLen(ss.slice(start+1,end));
var is ="http://XXXXXXXXX/video/";
document.getElementById("media").src= is + ss;
document.title = formatStringLen(ss.slice(start+1,end));
var a = document.getElementById("cccdf").getElementsByTagName("div");
for(var i=0;i<a.length;i++){
if(a[i].id){
document.getElementById(a[i].id).className = 'msg__name video-p-name';
}
}
document.getElementById(ss).className = 'msg__name video-p-name offline';
document.getElementById("chett").scrollTop = document.getElementById("chett").scrollTop+65;
6.前端功能设计(每日观看时长,总观看时长,观看时长图表,考研倒计时功能)
这里用到了一个js计时器,js每间隔一分钟向后台报告一次该用户的当前播放时长,并将当日凌晨12点时间存入缓存。若访问当前时间若大于缓存时长,则说明进入第二天,将每日观看时长的变量重新赋值为0。 js代码如下:
function statr(){
var a5 = document.getElementById("cccdf").getElementsByTagName("div");
for(var i5=0;i5<a5.length;i5++){
if(a5[i5].id) {
if (document.getElementById(a5[i5].id).className == "msg__name video-p-name offline") {
document.getElementById("chett").scrollTop = document.getElementById("chett").scrollTop+60*(i5/5);
}
}
}
var post_data = {
"straa": "",
};
$.ajax({
headers: {
"X-CSRFToken": getCookie("csrftoken")
},
url: "http://101.42.94.148:8000/getTime",
type: "POST",
data: post_data,
success: function (data) {
data = JSON.parse(data);
hour = data['hour'];
minute = data['minute'];
second = data['second'];
hour1 = data['hour1'];
minute1 = data['minute1'];
second1 = data['second1'];
var timecc = data['timecc'];
var new_date = new Date();
if(new_date.getTime()>timecc){
hour=0;
minute=0;
second=0;
}
var old_date = new Date("2022-12-25 00:00:00");
var difftime = (old_date - new_date)/1000;
day2 = parseInt(difftime/86400);
hour2 = parseInt(difftime/3600)-24*day2;
minute2 = parseInt(difftime%3600/60);
second2 = parseInt(difftime%60);
}
});
var time=function(){
second=second+1;
if(second==60){
second=0;
minute=minute+1;
}
if(minute==60){
minute=0;
hour = hour+1;
}
if(hour==24){
hour=0;
}
a1=Math.floor(hour/10);
a2=hour%10;
b1=Math.floor(minute/10);
b2=minute%10;
c1=Math.floor(second/10);
c2=second%10;
second1=second1+1;
if(second1==60){
second1=0;
minute1=minute1+1;
}
if(minute1==60){
minute1=0;
hour1 = hour1+1;
}
if(hour1==24){
hour1=0;
}
a3=Math.floor(hour1/10);
a4=hour1%10;
b3=Math.floor(minute1/10);
b4=minute1%10;
c3=Math.floor(second1/10);
c4=second1%10;
second2=second2-1;
if(second2<0){
second2=59;
minute2=minute2-1;
}
if(minute2<0){
minute2=59;
hour2=hour2-1;
}
if(hour2<0){
hour2=23;
day2=day2-1;
}
a5=Math.floor(hour2/10);
a6=hour2%10;
b5=Math.floor(minute2/10);
b6=minute2%10;
c5=Math.floor(second2/10);
c6=second2%10;
e1=Math.floor(day2/100);
e2=Math.floor(day2/10)%10;
e3=day2%10;
var introEle1 = document.getElementById('abc1');
introEle1.innerHTML = '今日学习时长:'+a1+''+a2+':'+b1+''+b2+':'+c1+''+c2+';';
var introEle2 = document.getElementById('abc2');
introEle2.innerHTML = '总学习时长:'+a3+a4+':'+b3+b4+':'+c3+c4+';';
var introEle3 = document.getElementById('abc3');
introEle3.innerHTML = '2023考研倒计时:'+e1+e2+e3+'天'+a5+a6+':'+b5+b6+':'+c5+c6+';';
var post_data = {
"hour":hour,
"minute":minute,
"second":second,
"hour1":hour1,
"minute1":minute1,
"second1":second1,
"timecc":new Date(new Date().toLocaleDateString()).getTime()+86400000
};
$.ajax({
headers: {
"X-CSRFToken": getCookie("csrftoken")
},
url: "http://101.42.94.148:8000/changTime",
type: "POST",
data: post_data,
success: function (data) {
}
});
}
项目展示及小结
为了避嫌这里就不贴其他视频课程的截图了,总体效果还是不错了,而且界面加了屏幕适配,会自动适应不同分辨率的设备。 使用这样的网站看视频课,你能考上研吗?让我们拭目以待! 想要全部源码请购买我的分享资源,想让我帮忙搭建的同学请私聊我。
|