mirror of
https://gitee.com/koogua/course-tencent-cloud.git
synced 2025-06-24 20:06:09 +08:00
点播课时
This commit is contained in:
parent
9e44dce13c
commit
4665bdda33
@ -3,6 +3,7 @@
|
||||
namespace App\Http\Admin\Controllers;
|
||||
|
||||
use App\Traits\Response as ResponseTrait;
|
||||
use Phalcon\Mvc\View;
|
||||
|
||||
/**
|
||||
* @RoutePrefix("/admin")
|
||||
@ -13,7 +14,7 @@ class PublicController extends \Phalcon\Mvc\Controller
|
||||
use ResponseTrait;
|
||||
|
||||
/**
|
||||
* @Route("/auth", name="admin.auth")
|
||||
* @Get("/auth", name="admin.auth")
|
||||
*/
|
||||
public function authAction()
|
||||
{
|
||||
@ -25,17 +26,20 @@ class PublicController extends \Phalcon\Mvc\Controller
|
||||
}
|
||||
|
||||
/**
|
||||
* @Route("/forbidden", name="admin.forbidden")
|
||||
* @Get("/forbidden", name="admin.forbidden")
|
||||
*/
|
||||
public function forbiddenAction()
|
||||
{
|
||||
$this->view->setRenderLevel(View::LEVEL_LAYOUT);
|
||||
$this->view->setLayout('error');
|
||||
|
||||
if ($this->request->isAjax()) {
|
||||
return $this->jsonError(['msg' => '无相关操作权限']);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @Route("/ip2region", name="admin.ip2region")
|
||||
* @Get("/ip2region", name="admin.ip2region")
|
||||
*/
|
||||
public function ip2regionAction()
|
||||
{
|
||||
|
@ -137,7 +137,7 @@ class Role extends Service
|
||||
|
||||
if (in_array('admin.course.list', $routes)) {
|
||||
$list[] = 'admin.course.chapters';
|
||||
$list[] = 'admin.chapter.sections';
|
||||
$list[] = 'admin.chapter.lessons';
|
||||
}
|
||||
|
||||
if (array_intersect(['admin.course.add', 'admin.course.edit'], $routes)) {
|
||||
|
14
app/Http/Admin/Views/layouts/error.volt
Normal file
14
app/Http/Admin/Views/layouts/error.volt
Normal file
@ -0,0 +1,14 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-Hans-CN">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
|
||||
<title>出错啦</title>
|
||||
{{ icon_link("favicon.ico") }}
|
||||
{{ css_link("lib/layui/css/layui.css") }}
|
||||
{{ css_link("web/css/error.css") }}
|
||||
</head>
|
||||
<body>
|
||||
{{ content() }}
|
||||
</body>
|
||||
</html>
|
@ -7,6 +7,7 @@ use App\Services\Frontend\Chapter\ChapterInfo as ChapterInfoService;
|
||||
use App\Services\Frontend\Chapter\CommentList as ChapterCommentListService;
|
||||
use App\Services\Frontend\Chapter\Learning as ChapterLearningService;
|
||||
use App\Services\Frontend\Chapter\OpposeVote as ChapterOpposeVoteService;
|
||||
use App\Services\Frontend\Course\ChapterList as CourseChapterListService;
|
||||
|
||||
/**
|
||||
* @RoutePrefix("/chapter")
|
||||
@ -23,7 +24,20 @@ class ChapterController extends Controller
|
||||
|
||||
$chapter = $service->handle($id);
|
||||
|
||||
$this->view->chapter = $chapter;
|
||||
$service = new CourseChapterListService();
|
||||
|
||||
$chapters = $service->handle($chapter['course']['id']);
|
||||
|
||||
if ($chapter['model'] == 'vod') {
|
||||
$this->view->pick('chapter/show_vod');
|
||||
} elseif ($chapter['model'] == 'live') {
|
||||
$this->view->pick('chapter/show_live');
|
||||
} elseif ($chapter['model'] == 'read') {
|
||||
$this->view->pick('chapter/show_read');
|
||||
}
|
||||
|
||||
$this->view->setVar('chapter', $chapter);
|
||||
$this->view->setVar('chapters', $chapters);
|
||||
}
|
||||
|
||||
/**
|
||||
|
23
app/Http/Web/Views/chapter/menu.volt
Normal file
23
app/Http/Web/Views/chapter/menu.volt
Normal file
@ -0,0 +1,23 @@
|
||||
<div class="layui-card">
|
||||
<div class="layui-card-header">课程目录</div>
|
||||
<div class="layui-card-body">
|
||||
<div class="sidebar-chapter-list">
|
||||
{% for item in chapters %}
|
||||
<div class="chapter-title layui-elip">{{ item.title }}</div>
|
||||
<ul class="sidebar-lesson-list">
|
||||
{% for lesson in item.children %}
|
||||
{% set url = url({'for':'web.chapter.show','id':lesson.id}) %}
|
||||
{% set active = (chapter.id == lesson.id) ? 'active' : 'normal' %}
|
||||
<li class="lesson-title layui-elip">
|
||||
{% if lesson.me.owned == 1 %}
|
||||
<a class="{{ active }}" href="{{ url }}" title="{{ lesson.title|e }}">{{ lesson.title }}</a>
|
||||
{% else %}
|
||||
<span class="deny" title="{{ lesson.title|e }}">{{ lesson.title }}</span>
|
||||
{% endif %}
|
||||
</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
12
app/Http/Web/Views/chapter/show.volt
Normal file
12
app/Http/Web/Views/chapter/show.volt
Normal file
@ -0,0 +1,12 @@
|
||||
{% extends 'templates/full.volt' %}
|
||||
|
||||
{% block content %}
|
||||
|
||||
<div class="layout-main clearfix">
|
||||
<div class="layout-content"></div>
|
||||
<div class="layout-sidebar">
|
||||
{{ partial('chapter/menu') }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% endblock %}
|
0
app/Http/Web/Views/chapter/show_live.volt
Normal file
0
app/Http/Web/Views/chapter/show_live.volt
Normal file
0
app/Http/Web/Views/chapter/show_read.volt
Normal file
0
app/Http/Web/Views/chapter/show_read.volt
Normal file
92
app/Http/Web/Views/chapter/show_vod.volt
Normal file
92
app/Http/Web/Views/chapter/show_vod.volt
Normal file
@ -0,0 +1,92 @@
|
||||
{% extends 'templates/full.volt' %}
|
||||
|
||||
{% block content %}
|
||||
|
||||
{% set course_url = url({'for':'web.course.show','id':chapter.course.id}) %}
|
||||
|
||||
<div class="breadcrumb">
|
||||
<span class="layui-breadcrumb">
|
||||
<span><i class="layui-icon layui-icon-return"></i> <a href="{{ course_url }}">返回课程主页</a></span>
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div class="layout-main clearfix">
|
||||
<div class="layout-content">
|
||||
<div id="player" class="container"></div>
|
||||
<div class="comment-list container"></div>
|
||||
</div>
|
||||
<div class="layout-sidebar">
|
||||
{{ partial('chapter/menu') }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% endblock %}
|
||||
|
||||
{% block inline_js %}
|
||||
|
||||
<script src="//imgcache.qq.com/open/qcloud/video/vcplayer/TcPlayer-2.3.2.js"></script>
|
||||
|
||||
<script>
|
||||
|
||||
var interval = null;
|
||||
var intervalTime = 5000;
|
||||
var requestId = getRequestId();
|
||||
var chapterId = '{{ chapter.id }}';
|
||||
var playUrl = 'https://1255691183.vod2.myqcloud.com/81258db0vodtransgzp1255691183/89b3d8955285890796532522693/v.f220.m3u8?t=5ee5c6ed&exper=0&us=697028&sign=2b7fd89eff92236184eadbaa14a895dd';
|
||||
var position = 0;
|
||||
|
||||
var player = new TcPlayer('player', {
|
||||
m3u8: playUrl,
|
||||
autoplay: true,
|
||||
width: 760,
|
||||
height: 450,
|
||||
listener: function (msg) {
|
||||
if (msg.type === 'play') {
|
||||
start();
|
||||
} else if (msg.type === 'pause') {
|
||||
stop();
|
||||
} else if (msg.type === 'end') {
|
||||
stop();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
if (position > 0) {
|
||||
player.currentTime(position);
|
||||
}
|
||||
|
||||
function start() {
|
||||
if (interval != null) {
|
||||
clearInterval(interval);
|
||||
interval = null;
|
||||
}
|
||||
interval = setInterval(learning, intervalTime);
|
||||
}
|
||||
|
||||
function stop() {
|
||||
clearInterval(interval);
|
||||
interval = null;
|
||||
}
|
||||
|
||||
function learning() {
|
||||
$.ajax({
|
||||
type: 'GET',
|
||||
url: '/admin/vod/learning',
|
||||
data: {
|
||||
request_id: requestId,
|
||||
chapter_id: chapterId,
|
||||
interval: intervalTime,
|
||||
position: player.currentTime(),
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function getRequestId() {
|
||||
var id = Date.now().toString(36);
|
||||
id += Math.random().toString(36).substr(3);
|
||||
return id;
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
{% endblock %}
|
@ -1,6 +1,7 @@
|
||||
{%- macro vod_lesson_info(lesson) %}
|
||||
{% set url = lesson.me.owned ? url({'for':'web.chapter.show','id':lesson.id}) : 'javascript:' %}
|
||||
<a href="{{ url }}">
|
||||
{% set priv = lesson.me.owned ? 'allow' : 'deny' %}
|
||||
<a class="{{ priv }}" href="{{ url }}">
|
||||
<i class="layui-icon layui-icon-play"></i>
|
||||
<span class="title">{{ lesson.title }}</span>
|
||||
{% if lesson.free == 1 %}
|
||||
@ -15,8 +16,9 @@
|
||||
|
||||
{%- macro live_lesson_info(lesson) %}
|
||||
{% set url = lesson.me.owned ? url({'for':'web.chapter.show','id':lesson.id}) : 'javascript:' %}
|
||||
{% set priv = lesson.me.owned ? 'allow' : 'deny' %}
|
||||
{% set over_flag = lesson.attrs.end_time < time() ? '已结束' : '' %}
|
||||
<a href="{{ url }}">
|
||||
<a class="{{ priv }}" href="{{ url }}">
|
||||
<i class="layui-icon layui-icon-video"></i>
|
||||
<span class="title">{{ lesson.title }}</span>
|
||||
{% if lesson.free == 1 %}
|
||||
@ -31,7 +33,8 @@
|
||||
|
||||
{%- macro read_lesson_info(lesson) %}
|
||||
{% set url = lesson.me.owned ? url({'for':'web.chapter.show','id':lesson.id}) : 'javascript:' %}
|
||||
<a href="{{ url }}">
|
||||
{% set priv = lesson.me.owned ? 'allow' : 'deny' %}
|
||||
<a class="{{ priv }}" href="{{ url }}">
|
||||
<i class="layui-icon layui-icon-read"></i>
|
||||
<span class="title">{{ lesson.title|e }}</span>
|
||||
{% if lesson.free == 1 %}
|
||||
|
82
app/Http/Web/Views/templates/chapter.volt
Normal file
82
app/Http/Web/Views/templates/chapter.volt
Normal file
@ -0,0 +1,82 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-CN-Hans">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
|
||||
<meta name="keywords" content="{{ site_seo.getKeywords() }}">
|
||||
<meta name="description" content="{{ site_seo.getDescription() }}">
|
||||
<meta name="csrf-token" content="{{ csrfToken.getToken() }}">
|
||||
<title>{{ site_seo.getTitle() }}</title>
|
||||
{{ icon_link('favicon.ico') }}
|
||||
{{ css_link('lib/layui/css/layui.css') }}
|
||||
{{ css_link('web/css/common.css') }}
|
||||
{% block link_css %}{% endblock %}
|
||||
{% block inline_css %}{% endblock %}
|
||||
</head>
|
||||
|
||||
{% set course_url = url({'for':'web.course.show','id':chapter.course.id}) %}
|
||||
|
||||
<body class="chapter-bg">
|
||||
<div class="chapter-main">
|
||||
<div class="header clearfix">
|
||||
<div class="back fl">
|
||||
<span><i class="layui-icon layui-icon-return"></i> <a href="{{ course_url }}">返回课程主页</a></span>
|
||||
</div>
|
||||
<div class="stats fr">
|
||||
<span class="user">203</span>
|
||||
<span class="agree">300</span>
|
||||
<span class="oppose">50</span>
|
||||
</div>
|
||||
<div class="action fr">
|
||||
<span class="user">203</span>
|
||||
<span class="agree">300</span>
|
||||
<span class="oppose">50</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="content">
|
||||
{% block content %}{% endblock %}
|
||||
</div>
|
||||
<div class="chapter-sidebar-btn">
|
||||
<i class="switch-icon layui-icon layui-icon-shrink-right"></i>
|
||||
</div>
|
||||
</div>
|
||||
<div class="chapter-sidebar">
|
||||
<div class="layui-tab layui-tab-brief">
|
||||
<ul class="layui-tab-title">
|
||||
<li class="layui-this">目录</li>
|
||||
<li>讨论</li>
|
||||
<li>资料</li>
|
||||
</ul>
|
||||
<div class="layui-tab-content">
|
||||
<div class="layui-tab-item layui-show" id="tab-courses" data-url="#"></div>
|
||||
<div class="layui-tab-item" id="tab-favorites" data-url="#"></div>
|
||||
<div class="layui-tab-item" id="tab-friends" data-url="#"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{{ js_include('lib/layui/layui.all.js') }}
|
||||
{{ js_include('web/js/common.js') }}
|
||||
<script>
|
||||
var $ = layui.jquery;
|
||||
$('.chapter-sidebar-btn').on('click', function () {
|
||||
var switchIcon = $(this).children('.switch-icon');
|
||||
var mainBlock = $('.chapter-main');
|
||||
var sidebarBlock = $('.chapter-sidebar');
|
||||
var spreadLeft = 'layui-icon-spread-left';
|
||||
var shrinkRight = 'layui-icon-shrink-right';
|
||||
if (switchIcon.hasClass(spreadLeft)) {
|
||||
switchIcon.removeClass(spreadLeft).addClass(shrinkRight);
|
||||
mainBlock.css('right', 0);
|
||||
sidebarBlock.css('width', 0);
|
||||
} else {
|
||||
switchIcon.removeClass(shrinkRight).addClass(spreadLeft);
|
||||
mainBlock.css('right', 320);
|
||||
sidebarBlock.css('width', 320);
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
{% block include_js %}{% endblock %}
|
||||
{% block inline_js %}{% endblock %}
|
||||
</body>
|
||||
</html>
|
136
app/Services/Frontend/Chapter/ChapterBasic.php
Normal file
136
app/Services/Frontend/Chapter/ChapterBasic.php
Normal file
@ -0,0 +1,136 @@
|
||||
<?php
|
||||
|
||||
namespace App\Services\Frontend\Chapter;
|
||||
|
||||
use App\Models\Chapter as ChapterModel;
|
||||
use App\Models\Course as CourseModel;
|
||||
use App\Repos\Chapter as ChapterRepo;
|
||||
use App\Services\ChapterVod as ChapterVodService;
|
||||
use App\Services\Frontend\ChapterTrait;
|
||||
use App\Services\Frontend\Service as FrontendService;
|
||||
use App\Services\Live as LiveService;
|
||||
use WhichBrowser\Parser as BrowserParser;
|
||||
|
||||
class ChapterBasic extends FrontendService
|
||||
{
|
||||
|
||||
use ChapterTrait;
|
||||
|
||||
public function handle($id)
|
||||
{
|
||||
$chapter = $this->checkChapterCache($id);
|
||||
|
||||
return $this->handleChapter($chapter);
|
||||
}
|
||||
|
||||
protected function handleChapter(ChapterModel $chapter)
|
||||
{
|
||||
/**
|
||||
* @var array $attrs
|
||||
*/
|
||||
$attrs = $chapter->attrs;
|
||||
|
||||
$result = [];
|
||||
|
||||
switch ($attrs['model']) {
|
||||
case CourseModel::MODEL_VOD:
|
||||
$result = $this->formatChapterVod($chapter);
|
||||
break;
|
||||
case CourseModel::MODEL_LIVE:
|
||||
$result = $this->formatChapterLive($chapter);
|
||||
break;
|
||||
case CourseModel::MODEL_READ:
|
||||
$result = $this->formatChapterRead($chapter);
|
||||
break;
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
protected function formatChapterVod(ChapterModel $chapter)
|
||||
{
|
||||
$chapterVodService = new ChapterVodService();
|
||||
|
||||
$playUrls = $chapterVodService->getPlayUrls($chapter->id);
|
||||
|
||||
/**
|
||||
* @var array $attrs
|
||||
*/
|
||||
$attrs = $chapter->attrs;
|
||||
|
||||
return [
|
||||
'id' => $chapter->id,
|
||||
'title' => $chapter->title,
|
||||
'summary' => $chapter->summary,
|
||||
'model' => $attrs['model'],
|
||||
'play_urls' => $playUrls,
|
||||
'user_count' => $chapter->user_count,
|
||||
'agree_count' => $chapter->agree_count,
|
||||
'oppose_count' => $chapter->oppose_count,
|
||||
'comment_count' => $chapter->comment_count,
|
||||
];
|
||||
}
|
||||
|
||||
protected function formatChapterLive(ChapterModel $chapter)
|
||||
{
|
||||
$headers = getallheaders();
|
||||
|
||||
$browserParser = new BrowserParser($headers);
|
||||
|
||||
$liveService = new LiveService();
|
||||
|
||||
$stream = "chapter-{$chapter->id}";
|
||||
|
||||
$format = $browserParser->isType('desktop') ? 'flv' : 'hls';
|
||||
|
||||
$playUrls = $liveService->getPullUrls($stream, $format);
|
||||
|
||||
$chapterRepo = new ChapterRepo();
|
||||
|
||||
$live = $chapterRepo->findChapterLive($chapter->id);
|
||||
|
||||
/**
|
||||
* @var array $attrs
|
||||
*/
|
||||
$attrs = $chapter->attrs;
|
||||
|
||||
return [
|
||||
'id' => $chapter->id,
|
||||
'title' => $chapter->title,
|
||||
'summary' => $chapter->summary,
|
||||
'model' => $attrs['model'],
|
||||
'play_urls' => $playUrls,
|
||||
'start_time' => $live->start_time,
|
||||
'end_time' => $live->end_time,
|
||||
'user_count' => $chapter->user_count,
|
||||
'agree_count' => $chapter->agree_count,
|
||||
'oppose_count' => $chapter->oppose_count,
|
||||
'comment_count' => $chapter->comment_count,
|
||||
];
|
||||
}
|
||||
|
||||
protected function formatChapterRead(ChapterModel $chapter)
|
||||
{
|
||||
$chapterRepo = new ChapterRepo();
|
||||
|
||||
$read = $chapterRepo->findChapterRead($chapter->id);
|
||||
|
||||
/**
|
||||
* @var array $attrs
|
||||
*/
|
||||
$attrs = $chapter->attrs;
|
||||
|
||||
return [
|
||||
'id' => $chapter->id,
|
||||
'title' => $chapter->title,
|
||||
'summary' => $chapter->summary,
|
||||
'model' => $attrs['model'],
|
||||
'content' => $read->content,
|
||||
'user_count' => $chapter->user_count,
|
||||
'agree_count' => $chapter->agree_count,
|
||||
'oppose_count' => $chapter->oppose_count,
|
||||
'comment_count' => $chapter->comment_count,
|
||||
];
|
||||
}
|
||||
|
||||
}
|
@ -57,6 +57,8 @@ class ChapterInfo extends FrontendService
|
||||
{
|
||||
$result = $this->formatChapter($chapter);
|
||||
|
||||
$result['course'] = $this->handleCourse($this->course);
|
||||
|
||||
$me = [
|
||||
'agreed' => 0,
|
||||
'opposed' => 0,
|
||||
@ -81,6 +83,21 @@ class ChapterInfo extends FrontendService
|
||||
return $result;
|
||||
}
|
||||
|
||||
protected function handleCourse(CourseModel $course)
|
||||
{
|
||||
return [
|
||||
'id' => $course->id,
|
||||
'title' => $course->title,
|
||||
'cover' => $course->cover,
|
||||
'market_price' => $course->market_price,
|
||||
'vip_price' => $course->vip_price,
|
||||
'model' => $course->model,
|
||||
'level' => $course->level,
|
||||
'user_count' => $course->user_count,
|
||||
'lesson_count' => $course->lesson_count,
|
||||
];
|
||||
}
|
||||
|
||||
protected function formatChapter(ChapterModel $chapter)
|
||||
{
|
||||
$item = [];
|
||||
@ -102,14 +119,20 @@ class ChapterInfo extends FrontendService
|
||||
|
||||
protected function formatChapterVod(ChapterModel $chapter)
|
||||
{
|
||||
$chapterVodService = new ChapterVodService();
|
||||
$service = new ChapterVodService();
|
||||
|
||||
$playUrls = $chapterVodService->getPlayUrls($chapter->id);
|
||||
$playUrls = $service->getPlayUrls($chapter->id);
|
||||
|
||||
/**
|
||||
* @var array $attrs
|
||||
*/
|
||||
$attrs = $chapter->attrs;
|
||||
|
||||
return [
|
||||
'id' => $chapter->id,
|
||||
'title' => $chapter->title,
|
||||
'summary' => $chapter->summary,
|
||||
'model' => $attrs['model'],
|
||||
'play_urls' => $playUrls,
|
||||
'user_count' => $chapter->user_count,
|
||||
'agree_count' => $chapter->agree_count,
|
||||
@ -136,10 +159,16 @@ class ChapterInfo extends FrontendService
|
||||
|
||||
$live = $chapterRepo->findChapterLive($chapter->id);
|
||||
|
||||
/**
|
||||
* @var array $attrs
|
||||
*/
|
||||
$attrs = $chapter->attrs;
|
||||
|
||||
return [
|
||||
'id' => $chapter->id,
|
||||
'title' => $chapter->title,
|
||||
'summary' => $chapter->summary,
|
||||
'model' => $attrs['model'],
|
||||
'play_urls' => $playUrls,
|
||||
'start_time' => $live->start_time,
|
||||
'end_time' => $live->end_time,
|
||||
@ -156,10 +185,16 @@ class ChapterInfo extends FrontendService
|
||||
|
||||
$read = $chapterRepo->findChapterRead($chapter->id);
|
||||
|
||||
/**
|
||||
* @var array $attrs
|
||||
*/
|
||||
$attrs = $chapter->attrs;
|
||||
|
||||
return [
|
||||
'id' => $chapter->id,
|
||||
'title' => $chapter->title,
|
||||
'summary' => $chapter->summary,
|
||||
'model' => $attrs['model'],
|
||||
'content' => $read->content,
|
||||
'user_count' => $chapter->user_count,
|
||||
'agree_count' => $chapter->agree_count,
|
||||
|
48
app/Services/Frontend/Course/CourseBasic.php
Normal file
48
app/Services/Frontend/Course/CourseBasic.php
Normal file
@ -0,0 +1,48 @@
|
||||
<?php
|
||||
|
||||
namespace App\Services\Frontend\Course;
|
||||
|
||||
use App\Models\Course as CourseModel;
|
||||
use App\Services\Frontend\CourseTrait;
|
||||
use App\Services\Frontend\Service as FrontendService;
|
||||
|
||||
class CourseBasic extends FrontendService
|
||||
{
|
||||
|
||||
use CourseTrait;
|
||||
|
||||
public function handle($id)
|
||||
{
|
||||
$course = $this->checkCourseCache($id);
|
||||
|
||||
return $this->handleCourse($course);
|
||||
}
|
||||
|
||||
protected function handleCourse(CourseModel $course)
|
||||
{
|
||||
return [
|
||||
'id' => $course->id,
|
||||
'title' => $course->title,
|
||||
'cover' => $course->cover,
|
||||
'summary' => $course->summary,
|
||||
'details' => $course->details,
|
||||
'keywords' => $course->keywords,
|
||||
'market_price' => $course->market_price,
|
||||
'vip_price' => $course->vip_price,
|
||||
'study_expiry' => $course->study_expiry,
|
||||
'refund_expiry' => $course->refund_expiry,
|
||||
'rating' => $course->rating,
|
||||
'model' => $course->model,
|
||||
'level' => $course->level,
|
||||
'attrs' => $course->attrs,
|
||||
'user_count' => $course->user_count,
|
||||
'lesson_count' => $course->lesson_count,
|
||||
'package_count' => $course->package_count,
|
||||
'review_count' => $course->review_count,
|
||||
'comment_count' => $course->comment_count,
|
||||
'consult_count' => $course->consult_count,
|
||||
'favorite_count' => $course->favorite_count,
|
||||
];
|
||||
}
|
||||
|
||||
}
|
4
public/static/crossdomain.xml
Normal file
4
public/static/crossdomain.xml
Normal file
@ -0,0 +1,4 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<cross-domain-policy>
|
||||
<allow-access-from domain="*"/>
|
||||
</cross-domain-policy>
|
8673
public/static/lib/ckplayer/ckplayer.js
Normal file
8673
public/static/lib/ckplayer/ckplayer.js
Normal file
File diff suppressed because one or more lines are too long
120
public/static/lib/ckplayer/ckplayer.min.js
vendored
Normal file
120
public/static/lib/ckplayer/ckplayer.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
BIN
public/static/lib/ckplayer/ckplayer.swf
Normal file
BIN
public/static/lib/ckplayer/ckplayer.swf
Normal file
Binary file not shown.
116
public/static/lib/ckplayer/ckplayer.xml
Normal file
116
public/static/lib/ckplayer/ckplayer.xml
Normal file
File diff suppressed because one or more lines are too long
28
public/static/lib/ckplayer/hls/LICENSE
Normal file
28
public/static/lib/ckplayer/hls/LICENSE
Normal file
@ -0,0 +1,28 @@
|
||||
Copyright (c) 2017 Dailymotion (http://www.dailymotion.com)
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
src/remux/mp4-generator.js and src/demux/exp-golomb.js implementation in this project
|
||||
are derived from the HLS library for video.js (https://github.com/videojs/videojs-contrib-hls)
|
||||
|
||||
That work is also covered by the Apache 2 License, following copyright:
|
||||
Copyright (c) 2013-2015 Brightcove
|
||||
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
16470
public/static/lib/ckplayer/hls/hls.js
Normal file
16470
public/static/lib/ckplayer/hls/hls.js
Normal file
File diff suppressed because it is too large
Load Diff
7
public/static/lib/ckplayer/hls/hls.min.js
vendored
Normal file
7
public/static/lib/ckplayer/hls/hls.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
46
public/static/lib/ckplayer/language.xml
Normal file
46
public/static/lib/ckplayer/language.xml
Normal file
@ -0,0 +1,46 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<language>
|
||||
<adCountdown>[$second]</adCountdown><!--广告播放结束倒计时-->
|
||||
<skipDelay>[$second]</skipDelay>
|
||||
<buttonOver>
|
||||
<play>点击播放</play>
|
||||
<pause>暂停播放</pause>
|
||||
<mute>静音</mute>
|
||||
<escMute>恢复音量</escMute>
|
||||
<full>全屏</full>
|
||||
<escFull>退出全屏</escFull>
|
||||
<previousPage>上一集</previousPage>
|
||||
<nextPage>下一集</nextPage>
|
||||
<definition>点击选择清晰度</definition>
|
||||
<subtitle>选择字幕</subtitle>
|
||||
</buttonOver>
|
||||
<volumeSliderOver>
|
||||
音量:[$volume]%
|
||||
</volumeSliderOver>
|
||||
<buffer>[$percentage]%</buffer>
|
||||
<timeSliderOver><!--鼠标经过进度条显示的时间格式-->
|
||||
[$timeh]:[$timei]:[$times]
|
||||
</timeSliderOver>
|
||||
<liveAndVod>
|
||||
[$timeh]:[$timei]:[$times]
|
||||
</liveAndVod>
|
||||
<live>
|
||||
直播中 [$liveTimeY]-[$liveTimem]-[$liveTimed] [$liveTimeh]:[$liveTimei]:[$liveTimes]
|
||||
</live>
|
||||
<m3u8Definition>
|
||||
<name>流畅</name>
|
||||
<name>低清</name>
|
||||
<name>标清</name>
|
||||
<name>高清</name>
|
||||
<name>超清</name>
|
||||
<name>蓝光</name>
|
||||
<name>未知</name>
|
||||
</m3u8Definition>
|
||||
<error>
|
||||
<cannotFindUrl>视频地址不存在</cannotFindUrl>
|
||||
<streamNotFound>加载失败</streamNotFound>
|
||||
<formatError>视频格式错误</formatError>
|
||||
</error>
|
||||
<definition>自动</definition>
|
||||
<subtitle>默认</subtitle>
|
||||
</language>
|
833
public/static/lib/ckplayer/style.xml
Normal file
833
public/static/lib/ckplayer/style.xml
Normal file
File diff suppressed because one or more lines are too long
@ -1,3 +1,25 @@
|
||||
::-webkit-scrollbar-button {
|
||||
height: 0;
|
||||
width: 0;
|
||||
display: none;
|
||||
}
|
||||
|
||||
.sidebar-chapter-list::-webkit-scrollbar {
|
||||
background-color: #fff;
|
||||
width: 5px;
|
||||
height: 15px;
|
||||
}
|
||||
|
||||
.sidebar-chapter-list::-webkit-scrollbar-thumb {
|
||||
border-radius: 5px;
|
||||
background: #393D49;
|
||||
}
|
||||
|
||||
.sidebar-chapter-list::-webkit-scrollbar-track {
|
||||
border: 0;
|
||||
background-color: #fff;
|
||||
}
|
||||
|
||||
.body {
|
||||
background-color: #f2f2f2;
|
||||
}
|
||||
@ -414,6 +436,11 @@
|
||||
display: block;
|
||||
}
|
||||
|
||||
.lesson-item .deny {
|
||||
cursor: default;
|
||||
color: gray;
|
||||
}
|
||||
|
||||
.lesson-item .title {
|
||||
margin: 0 5px;
|
||||
}
|
||||
@ -672,6 +699,97 @@
|
||||
margin-right: 10px;
|
||||
}
|
||||
|
||||
.sidebar-chapter-list {
|
||||
max-height: 500px;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.sidebar-chapter-list .chapter-title {
|
||||
padding-top: 10px;
|
||||
margin-bottom: 10px;
|
||||
color: #333;
|
||||
background-color: #fff;
|
||||
}
|
||||
|
||||
.sidebar-lesson-list {
|
||||
margin-left: 15px;
|
||||
}
|
||||
|
||||
.sidebar-lesson-list .lesson-title {
|
||||
line-height: 30px;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.lesson-title .active {
|
||||
color: red;
|
||||
}
|
||||
|
||||
.lesson-title .deny {
|
||||
color: gray;
|
||||
}
|
||||
|
||||
.chapter-bg {
|
||||
background-color: #f2f2f2;
|
||||
}
|
||||
|
||||
.chapter-main {
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
position: absolute;
|
||||
}
|
||||
|
||||
.chapter-main .header {
|
||||
height: 50px;
|
||||
line-height: 50px;
|
||||
color: rgba(255, 255, 255, 0.7);
|
||||
background-color: #393D49;
|
||||
}
|
||||
|
||||
.chapter-main .header a {
|
||||
color: rgba(255, 255, 255, 0.7);
|
||||
}
|
||||
|
||||
.chapter-main .header .back {
|
||||
float: left;
|
||||
margin-left: 5px;
|
||||
}
|
||||
|
||||
.chapter-main .header .stats {
|
||||
float: left;
|
||||
margin-left: 50px;
|
||||
margin-right: 150px;
|
||||
}
|
||||
|
||||
.chapter-main .header .action {
|
||||
float: right;
|
||||
margin-right: 150px;
|
||||
}
|
||||
|
||||
.chapter-main .content {
|
||||
}
|
||||
|
||||
.chapter-sidebar {
|
||||
top: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
width: 0;
|
||||
position: absolute;
|
||||
background-color: white;
|
||||
}
|
||||
|
||||
.chapter-sidebar-btn {
|
||||
top: 15px;
|
||||
right: 15px;
|
||||
position: absolute;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.chapter-sidebar-btn .switch-icon {
|
||||
color: rgba(255, 255, 255, 0.7);
|
||||
}
|
||||
|
||||
.vip-header {
|
||||
font-size: 18px;
|
||||
text-align: center;
|
||||
|
Loading…
x
Reference in New Issue
Block a user