django項(xiàng)目--在線播放

在線課堂

一、功能需求分析

1. 分析

在線直播,或點(diǎn)播設(shè)計(jì)到視頻的存儲(chǔ),轉(zhuǎn)碼,加密,播放很多細(xì)節(jié),個(gè)人單獨(dú)開發(fā)不現(xiàn)實(shí)。本項(xiàng)目的在線課堂選擇在線播放視頻的形式。實(shí)際項(xiàng)目中一般選擇云點(diǎn)播或者內(nèi)嵌視頻網(wǎng)站的方式進(jìn)行。本項(xiàng)目選擇是用百度云VOD點(diǎn)播來實(shí)現(xiàn)。

2. 功能

  • 視頻展示頁面

  • 視頻播放詳情

二、模型設(shè)計(jì)

1. 表字段分析

  • 老師表

    • 姓名

    • 職稱

    • 簡介

    • 頭像

  • 課程分類表

    • 名稱
  • 課程表

    • 課程名稱

    • 封面

    • 視頻地址

    • 時(shí)長

    • 簡介

    • 大綱

    • 老師

    • 分類

2.模型定義

在course/models.py中定義如下模型

from django.db import models
?
from utils.models import BaseModel
?
?
class Teacher(BaseModel):
 name = models.CharField('講師姓名', max_length=150, help_text='講師姓名')
 title = models.CharField('職稱', max_length=150, help_text='職稱')
 profile = models.TextField('簡介', help_text='簡介')
 photo = models.URLField('頭像url', default='', help_text='頭像url')
?
 class Meta:
 db_table = 'tb_teachers'
 verbose_name = '講師'
 verbose_name_plural = verbose_name
?
 def __str__(self):
 return self.name
?
?
class CourseCategory(BaseModel):
 name = models.CharField('課程分類名', max_length=100, help_text='課程分類名')
?
 class Meta:
 db_table = 'tb_course_category'
 verbose_name = '課程分類'
 verbose_name_plural = verbose_name
?
 def __str__(self):
 return self.name
?
?
class Course(BaseModel):
 title = models.CharField('課程名', max_length=150, help_text='課程名')
 cover_url = models.URLField('封面url', help_text='封面url')
 video_url = models.URLField('課程視頻url', help_text='課程視頻url')
 duration = models.DurationField('課程時(shí)長', help_text='課程時(shí)長')
 profile = models.TextField('課程簡介', null=True, blank=True, help_text='課程簡介')
 outline = models.TextField('課程大綱', null=True, blank=True, help_text='課程大綱')
 teacher = models.ForeignKey('Teacher', on_delete=models.SET_NULL, null=True, blank=True)
 category = models.ForeignKey('CourseCategory', on_delete=models.SET_NULL, null=True, blank=True)
?
 class Meta:
 db_table = 'tb_course'
 verbose_name = '課程'
 verbose_name_plural = verbose_name
?
 def __str__(self):
 return self.title

三、百度云VOD音視頻點(diǎn)播

1. 開通百度VOD音頻點(diǎn)播功能

  1. 注冊后登陸首頁,找到vod服務(wù)


開通即送55元包


2.添加媒資

官方使用說明

1563342650830.png
1563343001477.png
1563343108603.png

3.導(dǎo)入測試數(shù)據(jù)

# 在mysql數(shù)據(jù)庫中添加自己的測試數(shù)據(jù)
INSERT INTO `tb_teachers`
(create_time, update_time, is_delete, name, title,`profile`,photo)
 VALUES
 ( '2019-07-17 14:26:05.000000', '2019-07-17 14:26:09.000000', '0', '心藍(lán)', 'python高級講師', 'python學(xué)院最帥的老師', '/media/xinlan.jpg');
?
# 導(dǎo)入課程分類數(shù)據(jù)
INSERT INTO `tb_course_category` VALUES ('1', '2019-07-17 14:34:33.000000', '2019-07-17 14:34:36.000000', '0', 'python基礎(chǔ)');
INSERT INTO `tb_course_category` VALUES ('2', '2019-07-17 14:34:52.000000', '2019-07-17 14:34:55.000000', '0', 'python高級');
INSERT INTO `tb_course_category` VALUES ('3', '2019-07-17 14:35:20.000000', '2019-07-17 14:35:16.000000', '0', 'python框架');
?
# 導(dǎo)入課程數(shù)據(jù)
insert into tb_course (title, cover_url, video_url, duration, `profile`, outline, teacher_id, category_id, create_time, update_time, is_delete) values
('你的測試視頻1名稱', '你的測試視頻縮略圖URL', '你的測試視頻URL', 212000000, '你的測試視頻簡介', '你的視頻大綱', 1, 2, now(), now(), 0),
?
('你的測試視頻2名稱', '你的測試視頻縮略圖URL', '你的測試視頻URL', '你的測試視頻時(shí)長整數(shù)表示微秒', '你的測試視頻簡介', '你的視頻大綱', 1, 2, now(), now(), 0);

四、視頻展示列表

1.接口設(shè)計(jì)

  1. 接口說明:
類目 說明
請求方法 GET
url定義 /course/
參數(shù)格式 無參數(shù)
  1. 返回結(jié)果:

    文檔下載頁面

2.后端代碼

  1. 視圖
# 在course/views.py文件下創(chuàng)建如下視圖
    from django.shortcuts import render, Http404
    from django.views import View
    ?
    from . import models
    ?
    ?
    def course_list(request):
     """
     在線課程列表
     url:/course/
     :param request:
     :return:
     """
     courses = models.Course.objects.only('title', 'cover_url', 'teacher__title', 'teacher__name').filter(
     is_delete=False).select_related(
     'teacher')
     return render(request, 'course/course.html', context={'courses': courses})
  1. 路由
# 在course/urs.py中定義如下路由
    from django.urls import path
    ?
    from . import views
    ?
    app_name = 'course'
    ?
    urlpatterns = [
     path('', views.course_list, name='index'),
    ]

3.前端代碼

  1. html
<!-- 創(chuàng)建模板templates/course/course.html -->
    {% extends 'base/base.html' %}
    {% load static %}
    {% block title %}在線課堂{% endblock %}
    {% block link %}
     <link rel="stylesheet" href="{% static 'css/course/course.css' %}">
    {% endblock %}
    {% block main_start %}
     <main id="course-container">
     <div class="w1200">
     <ul class="course-list">
     {% for course in courses %}
     <li class="course-item">
     <a href="{% url 'course:course_detail' course.id %}">
     <img class="course-img" src="{{ course.cover_url }}"
     alt="{{ course.title }}">
     <div class="course-content">
     <p class="course-info">{{ course.title }}</p>
     <p class="course-author">{{ course.teacher.name}}({{ course.teacher.title }})</p>
     <p class="course-price free">免費(fèi)</p>
     </div>
     </a>
     </li>
     {% endfor %}
     </ul>
     </div>
     </main>
    {% endblock %}
2.  css
        /* ==== 修改course.css如下 ====*/
        /* ========== top-wrap start =========  */
        #top-wrap {
         background: #fff;
         line-height: 60px;
         box-shadow: 0 4px 4px rgba(0,0,0,.1);
        }
        #top-wrap .top-title {
         float: left;
         font-size: 22px;
         margin-right: 140px;
        ?
        }
        #top-wrap .top-nav {
         display: flex;
         justify-content: space-between;
         width: 400px;
         color: #878787;
         font-size: 18px;
        }
        #top-wrap .top-nav li.active {
         color: #212121;
        }
        #top-wrap .top-nav  li:hover {
         text-shadow: 1px 1px 2px #212121;
        }
        /* ========== top-wrap end =========  */
        /* ========== course-container  start =========  */
        #course-container {
         flex: 1;
        }
        #course-container .course-list {
         display: flex;
         flex-flow: row wrap;
         margin-bottom: 20px;
        }
        #course-container .course-list .course-item {
         margin: 20px;
         width: 260px;
         height: 260px;
         background: #fff;
         position: relative;
         /* float: left; */
        }
        .course-list .course-item .course-img {
         width: 100%;
         height: 61.8%;
        }
        .course-list .course-item .course-content {
         padding: 0 20px;
         height: 150px;
         box-sizing: border-box;
        }
        .course-list .course-item:hover {
         box-shadow: 0 4px 8px rgba(0,0,0,.1);
        }
        .course-item .course-content .course-info {
         font-size: 16px;
         line-height: 1.5;
         max-height: 50px;
         overflow: hidden;
        }
        .course-item .course-content .course-author{
         color: #848383;
         font-size: 14px;
         line-height: 36px;
         overflow: hidden;
         text-overflow: ellipsis;
         white-space: nowrap;
        }
        .course-item .course-content .course-price {
         position: absolute;
         bottom: 10px;
         right: 21px;
         font-size: 16px;
         line-height: 30px;
         color: #6fa026;
        ?
        }
        .course-item .course-content .course-price span{
         font-size: 14px;
        }
        .course-item .course-content .course-price.free {
         color: green;
         float: right;
        }
        /* ========== course-container  end =========  */
        ?

五、視頻播放詳情頁面

1.接口設(shè)計(jì)

  1. 接口說明:
類目 說明
請求方法 GET
url定義 /course/<int:course_id>/
參數(shù)格式 路徑參數(shù)
  1. 參數(shù)說明:
參數(shù)名 類型 是否必須 描述
course_id 整數(shù) 視頻id
  1. 返回結(jié)果:

    視頻播放詳情頁面

2. 后端代碼

  1. 視圖
    # 在course/views.py文件下創(chuàng)建如下視圖
    class CourseDetailView(View):
     """
     課程詳情視圖
     url:/course/<int:course_id>/
     """
    ?
     def get(self, request, course_id):
     course = models.Course.objects.only('title', 'cover_url', 'video_url', 'profile', 'outline', 'teacher__name',
     'teacher__photo', 'teacher__title', 'teacher__profile').select_related(
     'teacher').filter(is_delete=False, id=course_id).first()
    ?
     if course:
    ?
     return render(request, 'course/course_detail.html', context={'course': course})
     else:
     return Http404('此課程不存在')
    
  1. 路由
    # 在course/urs.py中添加如下路由
    path('<int:course_id>/', views.CourseDetailView.as_view(), name='course_detail')

3.前端代碼

  1. html
    <!-- 創(chuàng)建模板templates/course/course_detail.html -->
{% extends 'base/base.html' %}
{% load static %}
{% block title %}視頻詳情-{{ course.title }}{% endblock %}
{% block link %}
    <link rel="stylesheet" href="{% static 'css/course/course-detail.css' %}">
{% endblock %}
{% block main_start %}
    <main id="main">
        <div class="w1200">
            <div class="course-contain">
                <div class="course-top-contain">
                    <h4 class="course-title">{{ course.title }}</h4>
                    <div class="course-other clearfix">
                        <div class="share">
                             <i></i>
                            <span>分享</span>
                            <div class="share-list">
                                    <div class="bshare-custom icon-medium"><div class="bsPromo bsPromo2"></div><a title="分享到QQ空間" class="bshare-qzone"></a><a title="分享到新浪微博" class="bshare-sinaminiblog"></a><a title="分享到QQ好友" class="bshare-qqim" href="javascript:void(0);"></a><a title="分享到微信" class="bshare-weixin" href="javascript:void(0);"></a><a title="更多平臺(tái)" class="bshare-more bshare-more-icon more-style-addthis"></a><span class="BSHARE_COUNT bshare-share-count" style="float: none;">62.9K</span></div><script type="text/javascript" charset="utf-8" src="http://static.bshare.cn/b/buttonLite.js#style=-1&amp;uuid=&amp;pophcol=2&amp;lang=zh"></script><script type="text/javascript" charset="utf-8" src="http://static.bshare.cn/b/bshareC0.js"></script>
                            </div>
                        </div>
                        <div class="buy-list">
                            <span class="price">免費(fèi)</span>
                        </div>
                    </div>
                </div>

                <div class="course-video" id="course-video">
                    <span class="course-data" style="display: none" data-video-url="{{ course.video_url }}"
                          data-cover-url="{{ course.cover_url }}"></span>
                </div>

                <div class="course-bottom-contain">
                    <div class="course-detail-list">
                        <div class="course-item clearfix">
                            <h5 class="course-title">{{ course.teacher.name }}</h5>
                            <div class="teacher-box clearfix">
                                <img src="{{ course.teacher.photo }}" alt="{{ course.teacher.name }}"
                                     title="{{ course.teacher.name }}" class="teacher-avatar">
                                <div class="teacher-info">
                                    <p class="teacher-name">{{ course.teacher.name }}</p>
                                    <p class="teacher-identify"> {{ course.teacher.title }}</p>
                                </div>
                            </div>
                            <div class="item-content">
                                {{ course.teacher.profile }}
                            </div>
                        </div>
                        <div class="course-item clearfix">
                            <h5 class="course-title">課程簡介</h5>
                            <div class="item-content">
                                {{ course.profile }}
                            </div>
                        </div>
                        <div class="course-item clearfix">
                            <h5 class="course-title">課程大綱</h5>
                            <div class="item-content">
                                <p>{{ course.outline }}</p>
                            </div>
                        </div>
                        <div class="course-item clearfix">
                            <h5 class="course-title">幫助中心</h5>
                            <div class="item-content">
                                <p>1. 購買后的課程在線可反復(fù)觀看學(xué)習(xí),視頻有效期以具體課程信息為準(zhǔn)。 </p>
                                <p>2. 課程暫不支持下載觀看,均為在線觀看視頻。 </p>
                                <p>3. 課程一經(jīng)購買,不可轉(zhuǎn)讓、不可退款;僅限購買賬號(hào)觀看。</p>
                                <p>4. 如有問題請咨詢客服: 400-1567-315 </p>
                            </div>
                        </div>
                        <div class="course-item clearfix">
                            <h5 class="course-title">關(guān)于潭州課堂</h5>
                            <div class="item-content">
                                湖南潭州教育網(wǎng)絡(luò)科技有限公司擁有千余人的優(yōu)秀師資團(tuán)隊(duì),是一家?guī)熧Y豐富、教育產(chǎn)品類別眾多的在線培訓(xùn)公司。公司總部座落于美麗的星城長沙,2015年9月正式入駐芯城科技園目前擁有近兩萬平米辦公面積。
                                在潭州學(xué)習(xí)的學(xué)員已突破1000萬人次在線學(xué)員覆蓋全球,包括中國、加拿大、日本、美國、韓國等諸多國家。
                            </div>
                        </div>

                    </div>
                    <div class="course-side">
                        <h4 class="side-title">推薦課程</h4>
                    </div>
                </div>
            </div>
        </div>
    </main>

{% endblock %}
{% block script %}
    <script src="https://cdn.bdstatic.com/jwplayer/latest/cyberplayer.js"></script>
    <script src="{% static 'js/course/course_detail.js' %}"></script>
{% endblock %}


    ?
  1. js
    // 創(chuàng)建js文件 static/js/course/course.js
    $(function () {
      let $course_data = $(".course-data");
      let sVideoUrl = $course_data.data('video-url');
      let sCoverUrl = $course_data.data('cover-url');

      let player = cyberplayer("course-video").setup({
        width: '100%',
        height: 650,
        file: sVideoUrl,
        image: sCoverUrl,
        autostart: false,
        stretching: "uniform",
        repeat: false,
        volume: 100,
        controls: true,
        ak: '你自己的ak'
      });

    });
    
3.  css
        /*== 修改course_detail.css 代碼如下===*/
        #main {
            flex: 1;
        }

        .course-contain {
            width: 100%;
        }

        .course-contain .course-top-contain, #course-video{
            width: 100%;
            background: #fff;
            /*padding:0 20px;*/
        }
        .course-top-contain .course-title {
            font-size: 30px;
            line-height: 2.5;
            margin: 0 20px;
        }

        .course-top-contain .course-other {
            line-height: 3.5;
            margin: 0 20px;
        }
        .course-other  .share {
            float: left;
        }
        .share span {
             margin-right: 8px;
        }
        .share i {
                display: inline-block;
            vertical-align: middle;
            width: 14px;
            height: 14px;
            margin-right: 5px;
            background: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA4AAAAOCAYAAAAfSC3RAAABM0lEQVQokY2Sr0tEQRSFv/dYsIiwYVFkg/deERZBbEaDiJpEBP8Ck6jYxCBuMlgNikGTIIjBIIhbBDGISUSwzIwW4wbtrmHnwWNxf5w099z7MXeYk1SrVbpJ1IrANjAKnAXvbgpdqaYegPF4Xha1hbSH2yQHZVrpCIpaCdj9p/WVxoFhUdsQtcVY94vaHhCAErAD/EboGTgoiNpYLAYiVAMmgU9gLnj3GP0ToAy8Bu8aBWArg6JmgbXg3VF+t+BdHahndQo0WvZvAFed3p6Bh8B3zvsB7kRtKj8oamOiNiFqCUAavHsHKsAmMAMMAtfAvahdiFpZ1NaBN+AFeBK1YtIuOaI2AhwD00AC9OXa+23/MXj3EbybB1ZbIIChrskJ3p0DtRb7stesLtEMQQU4Dd7d/gHKLE8gSrHhJgAAAABJRU5ErkJggg==);
        }
        .share .share-list{
            display: none;
            /*width: 130px;*/
            position: relative;
            background: #fff;
            text-align: center;
            line-height: 45px;
            left: 8px;
            /*box-shadow: 0 1px 2px #ccc;*/
            /*border: 1px solid #ddd;*/
            border-radius: 5px;
            z-index: 1;
        }
        .share:hover .share-list{
            display: inline-block;
        }
        .share-list:after{
            content: "";
            border: 12px solid transparent;
            border-right-color: #eee;
            position: absolute;
            top: 0px;
            left: -25px;
        }
        .share-list a{
            cursor: pointer;
        }
        .share-list a img{
            vertical-align: middle;
        }
        .course-other .buy-list {
            float: right;
        }

        .price {
            color: #f76363;
            float: right;
            margin-right: 20px;
            line-height: 40px;
            font-size: 20px;
        }

        .buy-btn {
            background-color: #ff8d3f;
            border: none;
            width: 120px;
            text-align: center;
            line-height: 40px;
            color: #fff;
            border-radius: 5px;
            font-size: 16px;
            float: right;
        }

        .course-video {
            border-top: 1px solid #ddd;
            height: 400px;
        }

        .course-bottom-contain {
            width: 100%;
            margin-top: 30px;
        }

        .curse-bottom-contain {
            margin-top: 30px;
            width: 100%;
        }

        .course-detail-list {
            width: 800px;
            float: left;
        }

        .course-detail-list .course-item {
            margin-bottom: 20px;
            background-color: #fff;
            padding: 20px;
        }

        .course-item .course-title{
            border-left: 6px solid #5b86db;
            padding: 0 10px;
            color: #202020;
            font-size: 18px;
        }

        .course-item .teacher-box {
            margin-top: 20px;
        }

        .teacher-box  .teacher-avatar {
            width: 62px;
            height: 62px;
            border-radius: 50%;
            float: left;
        }

        .teacher-box .teacher-info {
            float: left;
            margin-left: 10px;
            position: relative;
            font-size: 16px;
            color: #696969;
            height: 62px;
            width: 90%;
        }

        .teacher-name {
            position: absolute;
            left: 0;
            top: 8px;
        }
        .teacher-identify {
            position: absolute;
            bottom: 8px;
            left: 0;
        }

        .item-content {
            font-size: 14px;
            color: #888;
            line-height: 2em;
            margin-top: 20px;
        }

        .item-course-title {
            font-size: 18px;
            color: #202020;
            float: left;
            line-height: 40px;
        }

        .item-buy-list {
            float: right;
            margin-top: 0;
        }

        .course-side {
            float: right;
            width: 360px;
            background-color: #fff;
            padding: 20px;
            padding-bottom: 0;
            box-sizing: border-box;
        }
        .course-side  .side-title {
            font-size: 18px;
            line-height: 2.4;
        }

       
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容