IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> Python知识库 -> 基于Django框架的视频播放器设计 -> 正文阅读

[Python知识库]基于Django框架的视频播放器设计


前言

本文承接上文《基于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:
            # print(file_path)
            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++){
            //alert(a[i].id);
            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(){
            //alert("end");
            clearInterval(time11);
        var a1 = document.getElementById("cccdf").getElementsByTagName("div");
        for(var i1=0;i1<a1.length;i1++){
            //alert(a1[i1].id);
            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++){
            //alert(a[i].id);
            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++){
            //alert(a1[i1].id);
            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"); //设置过去的一个时间点,"yyyy-MM-dd HH:mm:ss"格式化日期
                    var difftime = (old_date - new_date)/1000; //计算时间差,并把毫秒转换成秒
                    day2 = parseInt(difftime/86400); // 天  24*60*60*1000
                    hour2 = parseInt(difftime/3600)-24*day2;    // 小时 60*60 总小时数-过去的小时数=现在的小时数
                    minute2 = parseInt(difftime%3600/60); // 分钟 -(day*24) 以60秒为一整份 取余 剩下秒数 秒数/60 就是分钟数
                    second2 = parseInt(difftime%60);  // 以60秒为一整份 取余 剩下秒数
                }
            });
            var time=function(){
                //今日时间
                //alert("hhh");
                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+';';
                //alert("hhh");
            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) {
                }
            });
            }

项目展示及小结

请添加图片描述在这里插入图片描述在这里插入图片描述

为了避嫌这里就不贴其他视频课程的截图了,总体效果还是不错了,而且界面加了屏幕适配,会自动适应不同分辨率的设备。
使用这样的网站看视频课,你能考上研吗?让我们拭目以待!
想要全部源码请购买我的分享资源,想让我帮忙搭建的同学请私聊我。

  Python知识库 最新文章
Python中String模块
【Python】 14-CVS文件操作
python的panda库读写文件
使用Nordic的nrf52840实现蓝牙DFU过程
【Python学习记录】numpy数组用法整理
Python学习笔记
python字符串和列表
python如何从txt文件中解析出有效的数据
Python编程从入门到实践自学/3.1-3.2
python变量
上一篇文章      下一篇文章      查看所有文章
加:2022-07-17 16:19:02  更:2022-07-17 16:20:56 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/15 12:13:56-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码