mirror of
https://gitee.com/koogua/course-tencent-cloud.git
synced 2025-06-17 15:55:31 +08:00
初步完成直播websocket讨论
This commit is contained in:
parent
2dee6efb04
commit
b374309c1f
@ -24,4 +24,12 @@ class IndexController extends Controller
|
||||
$this->view->setVar('vip_courses', $indexService->getVipCourses());
|
||||
}
|
||||
|
||||
/**
|
||||
* @Get("/im", name="web.im")
|
||||
*/
|
||||
public function imAction()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -2,47 +2,81 @@
|
||||
|
||||
namespace App\Http\Web\Controllers;
|
||||
|
||||
class LiveController extends Controller
|
||||
use App\Http\Web\Services\Live as LiveService;
|
||||
use App\Traits\Response as ResponseTrait;
|
||||
|
||||
/**
|
||||
* @RoutePrefix("/live")
|
||||
*/
|
||||
class LiveController extends \Phalcon\Mvc\Controller
|
||||
{
|
||||
|
||||
use ResponseTrait;
|
||||
|
||||
/**
|
||||
* @Get("/stats", name="web.live.stats")
|
||||
* @Get("/{id:[0-9]+}/members", name="web.live.members")
|
||||
*/
|
||||
public function statsAction()
|
||||
public function membersAction($id)
|
||||
{
|
||||
$list = [
|
||||
[
|
||||
'username' => '直飞机',
|
||||
'avatar' => 'http://tp1.sinaimg.cn/5619439268/180/40030060651/1',
|
||||
'status' => 'online',
|
||||
'sign' => '高舍炮打的准',
|
||||
'id' => 1,
|
||||
],
|
||||
[
|
||||
'username' => '直飞机2',
|
||||
'avatar' => 'http://tp1.sinaimg.cn/5619439268/180/40030060651/1',
|
||||
'status' => 'online',
|
||||
'sign' => '高舍炮打的准',
|
||||
'id' => 2,
|
||||
],
|
||||
[
|
||||
'username' => '直飞机3',
|
||||
'avatar' => 'http://tp1.sinaimg.cn/5619439268/180/40030060651/1',
|
||||
'status' => 'online',
|
||||
'sign' => '高舍炮打的准',
|
||||
'id' => 3,
|
||||
],
|
||||
];
|
||||
|
||||
$content = ['data' => ['list' => $list]];
|
||||
|
||||
return $this->jsonSuccess($content);
|
||||
}
|
||||
|
||||
/**
|
||||
* @Post("/{id:[0-9]+}/bind", name="web.live.bind")
|
||||
*/
|
||||
public function bindAction($id)
|
||||
{
|
||||
$service = new LiveService();
|
||||
|
||||
$service->bindUser($id);
|
||||
|
||||
return $this->jsonSuccess();
|
||||
}
|
||||
|
||||
/**
|
||||
* @Post("/{id:[0-9]+}/unbind", name="web.live.unbind")
|
||||
*/
|
||||
public function unbindAction($id)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @Get("/users", name="web.live.users")
|
||||
* @Post("/{id:[0-9]+}/message", name="web.live.message")
|
||||
*/
|
||||
public function usersAction()
|
||||
public function messageAction($id)
|
||||
{
|
||||
$service = new LiveService();
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @Post("/login", name="web.live.login")
|
||||
*/
|
||||
public function loginAction()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @Post("/logout", name="web.live.logout")
|
||||
*/
|
||||
public function logoutAction()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @Post("/message", name="web.live.message")
|
||||
*/
|
||||
public function messageAction()
|
||||
{
|
||||
$service->sendMessage($id);
|
||||
|
||||
return $this->jsonSuccess();
|
||||
}
|
||||
|
||||
}
|
||||
|
27
app/Http/Web/Controllers/MessengerController.php
Normal file
27
app/Http/Web/Controllers/MessengerController.php
Normal file
@ -0,0 +1,27 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Web\Controllers;
|
||||
|
||||
use App\Http\Web\Services\Index as IndexService;
|
||||
|
||||
class MessengerController extends Controller
|
||||
{
|
||||
|
||||
/**
|
||||
* @Get("/", name="web.index")
|
||||
*/
|
||||
public function indexAction()
|
||||
{
|
||||
$this->siteSeo->setKeywords($this->siteSettings['keywords']);
|
||||
$this->siteSeo->setDescription($this->siteSettings['description']);
|
||||
|
||||
$indexService = new IndexService();
|
||||
|
||||
$this->view->setVar('slides', $indexService->getSlides());
|
||||
$this->view->setVar('lives', $indexService->getLives());
|
||||
$this->view->setVar('new_courses', $indexService->getNewCourses());
|
||||
$this->view->setVar('free_courses', $indexService->getFreeCourses());
|
||||
$this->view->setVar('vip_courses', $indexService->getVipCourses());
|
||||
}
|
||||
|
||||
}
|
@ -53,13 +53,13 @@ class PublicController extends \Phalcon\Mvc\Controller
|
||||
}
|
||||
|
||||
/**
|
||||
* @Post("/learning", name="web.learning")
|
||||
* @Post("/{id:[0-9]+}/learning", name="web.learning")
|
||||
*/
|
||||
public function learningAction()
|
||||
public function learningAction($id)
|
||||
{
|
||||
$service = new LearningService();
|
||||
|
||||
$service->handle();
|
||||
$service->handle($id);
|
||||
|
||||
return $this->jsonSuccess();
|
||||
}
|
||||
|
67
app/Http/Web/Services/Live.php
Normal file
67
app/Http/Web/Services/Live.php
Normal file
@ -0,0 +1,67 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Web\Services;
|
||||
|
||||
use App\Services\Frontend\ChapterTrait;
|
||||
use GatewayClient\Gateway;
|
||||
|
||||
class Live extends Service
|
||||
{
|
||||
|
||||
use ChapterTrait;
|
||||
|
||||
public function bindUser($id)
|
||||
{
|
||||
$chapter = $this->checkChapterCache($id);
|
||||
|
||||
$user = $this->getCurrentUser();
|
||||
|
||||
$userId = $user->id > 0 ?: $this->session->getId();
|
||||
|
||||
$clientId = $this->request->getPost('client_id');
|
||||
|
||||
$groupName = $this->getGroupName($chapter->id);
|
||||
|
||||
Gateway::$registerAddress = '127.0.0.1:1238';
|
||||
|
||||
Gateway::bindUid($clientId, $userId);
|
||||
|
||||
Gateway::joinGroup($clientId, $groupName);
|
||||
}
|
||||
|
||||
public function sendMessage($id)
|
||||
{
|
||||
$chapter = $this->checkChapterCache($id);
|
||||
|
||||
$from = $this->request->getPost('from');
|
||||
$to = $this->request->getPost('to');
|
||||
|
||||
$content = [
|
||||
'username' => $from['username'],
|
||||
'avatar' => $from['avatar'],
|
||||
'content' => $from['content'],
|
||||
'fromid' => $from['id'],
|
||||
'id' => $to['id'],
|
||||
'type' => $to['type'],
|
||||
'timestamp' => 1000 * time(),
|
||||
'mine' => false,
|
||||
];
|
||||
|
||||
$message = json_encode([
|
||||
'type' => 'show_message',
|
||||
'content' => $content,
|
||||
]);
|
||||
|
||||
$groupName = $this->getGroupName($chapter->id);
|
||||
|
||||
Gateway::$registerAddress = '127.0.0.1:1238';
|
||||
|
||||
Gateway::sendToGroup($groupName, $message);
|
||||
}
|
||||
|
||||
protected function getGroupName($groupId)
|
||||
{
|
||||
return "chapter_{$groupId}";
|
||||
}
|
||||
|
||||
}
|
@ -2,32 +2,28 @@
|
||||
|
||||
{% 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 class="layui-breadcrumb breadcrumb">
|
||||
<a href="/">首页</a>
|
||||
<a href="{{ url({'for':'web.course.list'}) }}">全部课程</a>
|
||||
<a href="{{ url({'for':'web.course.show','id':chapter.course.id}) }}">{{ chapter.course.title }}</a>
|
||||
<a><cite>{{ chapter.title }}</cite></a>
|
||||
</div>
|
||||
|
||||
<div class="layout-main clearfix">
|
||||
<div class="layout-content">
|
||||
<div id="player" class="container"></div>
|
||||
</div>
|
||||
<div class="layout-sidebar">
|
||||
<div class="sidebar-online container">
|
||||
<div class="layui-tab layui-tab-brief">
|
||||
<ul class="layui-tab-title">
|
||||
<li class="layui-this">讨论</li>
|
||||
<li>成员</li>
|
||||
</ul>
|
||||
<div class="layui-tab-content">
|
||||
<div class="layui-tab-item layui-show" id="tab-comments" data-url="#"></div>
|
||||
<div class="layui-tab-item" id="tab-users" data-url="#"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="live-player container">
|
||||
<div id="player"></div>
|
||||
</div>
|
||||
|
||||
<div class="layui-hide">
|
||||
<input type="hidden" name="user.id" value="{{ auth_user.id }}">
|
||||
<input type="hidden" name="user.name" value="{{ auth_user.name }}">
|
||||
<input type="hidden" name="user.avatar" value="{{ auth_user.avatar }}">
|
||||
<input type="hidden" name="chapter.id" value="{{ chapter.id }}">
|
||||
<input type="hidden" name="chapter.plan_id" value="{{ chapter.me.plan_id }}">
|
||||
<input type="hidden" name="chapter.play_urls" value='{{ chapter.play_urls|json_encode }}'>
|
||||
<input type="hidden" name="chapter.learning_url" value="{{ url({'for':'web.learning','id':chapter.id}) }}">
|
||||
<input type="hidden" name="im.members_url" value="{{ url({'for':'web.live.members','id':chapter.id}) }}">
|
||||
<input type="hidden" name="im.bind_user_url" value="{{ url({'for':'web.live.bind','id':chapter.id}) }}">
|
||||
<input type="hidden" name="im.send_msg_url" value="{{ url({'for':'web.live.message','id':chapter.id}) }}">
|
||||
</div>
|
||||
|
||||
{% endblock %}
|
||||
@ -36,119 +32,12 @@
|
||||
|
||||
<script src="//imgcache.qq.com/open/qcloud/video/vcplayer/TcPlayer-2.3.2.js"></script>
|
||||
|
||||
{{ js_include('lib/layui/layui.js') }}
|
||||
{{ js_include('web/js/live.player.js') }}
|
||||
{{ js_include('web/js/live.im.js') }}
|
||||
|
||||
{% endblock %}
|
||||
|
||||
{% block inline_js %}
|
||||
|
||||
<script>
|
||||
|
||||
var interval = null;
|
||||
var intervalTime = 5000;
|
||||
var position = 0;
|
||||
var chapterId = '{{ chapter.id }}';
|
||||
var courseId = '{{ chapter.course.id }}';
|
||||
var planId = '{{ chapter.me.plan_id }}';
|
||||
var userId = '{{ auth_user.id }}';
|
||||
var requestId = getRequestId();
|
||||
var playUrls = JSON.parse('{{ chapter.play_urls|json_encode }}');
|
||||
|
||||
var options = {
|
||||
live: true,
|
||||
autoplay: true,
|
||||
h5_flv: true,
|
||||
width: 760,
|
||||
height: 450
|
||||
};
|
||||
|
||||
if (playUrls.rtmp && playUrls.rtmp.od) {
|
||||
options.rtmp = playUrls.rtmp.od;
|
||||
}
|
||||
|
||||
if (playUrls.rtmp && playUrls.rtmp.hd) {
|
||||
options.rtmp_hd = playUrls.rtmp.hd;
|
||||
}
|
||||
|
||||
if (playUrls.rtmp && playUrls.rtmp.sd) {
|
||||
options.rtmp_sd = playUrls.rtmp.sd;
|
||||
}
|
||||
|
||||
if (playUrls.flv && playUrls.flv.od) {
|
||||
options.flv = playUrls.flv.od;
|
||||
}
|
||||
|
||||
if (playUrls.flv && playUrls.flv.hd) {
|
||||
options.flv_hd = playUrls.flv.hd;
|
||||
}
|
||||
|
||||
if (playUrls.flv && playUrls.flv.sd) {
|
||||
options.flv_sd = playUrls.flv.sd;
|
||||
}
|
||||
|
||||
if (playUrls.m3u8 && playUrls.m3u8.od) {
|
||||
options.m3u8 = playUrls.m3u8.od;
|
||||
}
|
||||
|
||||
if (playUrls.m3u8 && playUrls.m3u8.hd) {
|
||||
options.m3u8_hd = playUrls.m3u8.hd;
|
||||
}
|
||||
|
||||
if (playUrls.m3u8 && playUrls.m3u8.sd) {
|
||||
options.m3u8_sd = playUrls.m3u8.sd;
|
||||
}
|
||||
|
||||
if (userId !== '0' && planId !== '0') {
|
||||
options.listener = function (msg) {
|
||||
if (msg.type === 'play') {
|
||||
start();
|
||||
} else if (msg.type === 'pause') {
|
||||
stop();
|
||||
} else if (msg.type === 'end') {
|
||||
stop();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var player = new TcPlayer('player', options);
|
||||
|
||||
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: 'POST',
|
||||
url: '/learning',
|
||||
data: {
|
||||
request_id: requestId,
|
||||
chapter_id: chapterId,
|
||||
course_id: courseId,
|
||||
user_id: userId,
|
||||
plan_id: planId,
|
||||
interval: intervalTime,
|
||||
position: player.currentTime(),
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function getRequestId() {
|
||||
var id = Date.now().toString(36);
|
||||
id += Math.random().toString(36).substr(3);
|
||||
return id;
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
{% endblock %}
|
99
app/Http/Web/Views/index/im.volt
Normal file
99
app/Http/Web/Views/index/im.volt
Normal file
@ -0,0 +1,99 @@
|
||||
{% extends 'templates/full.volt' %}
|
||||
|
||||
{% block content %}
|
||||
|
||||
{% endblock %}
|
||||
|
||||
{% block inline_js %}
|
||||
|
||||
{{ js_include('lib/layui/layui.js') }}
|
||||
|
||||
<script>
|
||||
|
||||
layui.use(['jquery', 'layim'], function () {
|
||||
|
||||
var bindUserUrl = '/live/42425/bind';
|
||||
var sendMessageUrl = '/live/42425/message';
|
||||
|
||||
var socket = new WebSocket('ws://127.0.0.1:8282');
|
||||
var $ = layui.jquery;
|
||||
var layim = layui.layim;
|
||||
|
||||
layim.config({
|
||||
brief: true,
|
||||
init: {
|
||||
mine: {
|
||||
'username': '直飞机',
|
||||
'avatar': 'http://tp1.sinaimg.cn/5619439268/180/40030060651/1',
|
||||
'status': 'online',
|
||||
'sign': '高舍炮打的准',
|
||||
'id': 1,
|
||||
}
|
||||
},
|
||||
members: {
|
||||
url: '/live/members',
|
||||
data: {
|
||||
chapter_id: 123
|
||||
}
|
||||
}
|
||||
}).chat({
|
||||
name: '直播讨论',
|
||||
type: 'group',
|
||||
avatar: 'http://tp1.sinaimg.cn/5619439268/180/40030060651/1',
|
||||
id: 123,
|
||||
});
|
||||
|
||||
layim.on('sendMessage', function (res) {
|
||||
sendMessage(res.mine, res.to);
|
||||
});
|
||||
|
||||
socket.onopen = function () {
|
||||
console.log('socket connect success');
|
||||
};
|
||||
|
||||
socket.onclose = function () {
|
||||
console.log('socket connect close');
|
||||
};
|
||||
|
||||
socket.onerror = function () {
|
||||
console.log('socket connect error');
|
||||
};
|
||||
|
||||
socket.onmessage = function (e) {
|
||||
var data = JSON.parse(e.data);
|
||||
console.log(data);
|
||||
if (data.type === 'ping') {
|
||||
socket.send('pong...');
|
||||
} else if (data.type === 'bind_user') {
|
||||
bindUser(data.client_id);
|
||||
} else if (data.type === 'show_message') {
|
||||
showMessage(data.content);
|
||||
}
|
||||
};
|
||||
|
||||
function bindUser(clientId) {
|
||||
$.ajax({
|
||||
type: 'POST',
|
||||
url: bindUserUrl,
|
||||
data: {client_id: clientId}
|
||||
});
|
||||
}
|
||||
|
||||
function sendMessage(from, to) {
|
||||
$.ajax({
|
||||
type: 'POST',
|
||||
dataType: 'json',
|
||||
url: sendMessageUrl,
|
||||
data: {from: from, to: to}
|
||||
});
|
||||
}
|
||||
|
||||
function showMessage(message) {
|
||||
layim.getMessage(message);
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
</script>
|
||||
|
||||
{% endblock %}
|
@ -1,338 +0,0 @@
|
||||
|
||||
<div id="video" style="width: 600px; height: 400px;">
|
||||
|
||||
</div>
|
||||
|
||||
{{ js_include('library/chplayer/chplayer.min.js') }}
|
||||
|
||||
<script>
|
||||
|
||||
var videoObject = {
|
||||
container: '#video', //容器的ID
|
||||
variable: 'player',
|
||||
autoplay: true, //是否自动播放
|
||||
loaded: 'loadedHandler', //当播放器加载后执行的函数
|
||||
video: 'http://vod-cdn.koogua.com/20171012_fixed.m3u8'
|
||||
//video: 'http://www.flashls.org/playlists/test_001/stream_1000k_48k_640x360.m3u8'
|
||||
};
|
||||
|
||||
var player = new chplayer(videoObject);
|
||||
|
||||
function loadedHandler() {
|
||||
changeText('.playerstate', '状态:播放器已加载,建立监听:监听元数据,监听其它状态');
|
||||
player.addListener('error', errorHandler); //监听视频加载出错
|
||||
player.addListener('loadedmetadata', loadedMetaDataHandler); //监听元数据
|
||||
player.addListener('play', playHandler); //监听暂停播放
|
||||
player.addListener('pause', pauseHandler); //监听暂停播放
|
||||
player.addListener('timeupdate', timeUpdateHandler); //监听播放时间
|
||||
player.addListener('seeking', seekingHandler); //监听跳转播放
|
||||
player.addListener('seeked', seekedHandler); //监听跳转播放完成
|
||||
player.addListener('volumechange', volumeChangeHandler); //监听音量改变
|
||||
player.addListener('full', fullHandler); //监听全屏/非全屏切换
|
||||
player.addListener('ended', endedHandler); //监听全屏/非全屏切换
|
||||
player.addListener('videochange', videoChangeHandler); //监听视频地址改变
|
||||
}
|
||||
|
||||
function errorHandler() {
|
||||
changeText('.playerstate', '状态:视频加载错误,停止执行其它动作,等待其它操作');
|
||||
}
|
||||
|
||||
function loadedMetaDataHandler() {
|
||||
var metaData = player.getMetaDate();
|
||||
var html = '';
|
||||
if(parseInt(metaData['videoWidth']) > 0) {
|
||||
changeText('.playerstate', '状态:获取到元数据信息,如果数据错误,可以使用延迟获取');
|
||||
html += '总时间:' + metaData['duration'] + '秒,';
|
||||
html += '音量:' + metaData['volume'] + '(范围0-1),';
|
||||
html += '播放器的宽度:' + metaData['width'] + 'px,';
|
||||
html += '播放器的高度:' + metaData['height'] + 'px,';
|
||||
html += '视频的实际宽度:' + metaData['videoWidth'] + 'px,';
|
||||
html += '视频的实际高度:' + metaData['videoHeight'] + 'px,';
|
||||
html += '是否暂停状态:' + metaData['paused'];
|
||||
} else {
|
||||
changeText('.playerstate', '状态:未正确获取到元数据信息');
|
||||
html = '您正在使用移动端或iPad观看本页面,该平台限制了视频加载,只有在点击了播放器后才能加载视频及获取元数据信息';
|
||||
}
|
||||
changeText('.metadata', html);
|
||||
}
|
||||
|
||||
function playHandler() {
|
||||
//player.animateResume();//继续播放所有弹幕
|
||||
changeText('.playstate', '播放状态:播放');
|
||||
window.setTimeout(function() {
|
||||
loadedMetaDataHandler();
|
||||
}, 1000);
|
||||
loadedMetaDataHandler();
|
||||
}
|
||||
|
||||
function pauseHandler() {
|
||||
//player.animatePause();//暂停所有弹幕
|
||||
changeText('.playstate', '播放状态:暂停');
|
||||
loadedMetaDataHandler();
|
||||
}
|
||||
|
||||
function timeUpdateHandler() {
|
||||
changeText('.currenttimestate', '当前播放时间(秒):' + player.time);
|
||||
}
|
||||
|
||||
function seekingHandler() {
|
||||
changeText('.seekstate', '跳转动作:正在进行跳转');
|
||||
}
|
||||
|
||||
function seekedHandler() {
|
||||
changeText('.seekstate', '跳转动作:跳转完成');
|
||||
}
|
||||
|
||||
function volumeChangeHandler() {
|
||||
changeText('.volumechangestate', '当前音量:' + player.volume);
|
||||
}
|
||||
|
||||
function fullHandler() {
|
||||
var html = player.getByElement('.fullstate').innerHTML;
|
||||
if(player.full) {
|
||||
html += ',全屏';
|
||||
} else {
|
||||
html += ',否';
|
||||
}
|
||||
changeText('.fullstate', html);
|
||||
}
|
||||
|
||||
function endedHandler() {
|
||||
changeText('.endedstate', '播放结束');
|
||||
}
|
||||
var videoChangeNum = 0;
|
||||
|
||||
function videoChangeHandler() {
|
||||
videoChangeNum++;
|
||||
changeText('.videochangestate', '视频地址改变了' + videoChangeNum + '次');
|
||||
}
|
||||
|
||||
function seekTime() {
|
||||
var time = parseInt(player.getByElement('.seektime').value);
|
||||
var metaData = player.getMetaDate();
|
||||
var duration = metaData['duration'];
|
||||
if(time < 0) {
|
||||
alert('请填写大于0的数字');
|
||||
return;
|
||||
}
|
||||
if(time > duration) {
|
||||
alert('请填写小于' + duration + '的数字');
|
||||
return;
|
||||
}
|
||||
player.seek(time);
|
||||
}
|
||||
|
||||
function changeVolume() {
|
||||
var volume = player.getByElement('.changevolume').value;
|
||||
volume = Math.floor(volume * 100) / 100;
|
||||
if(volume < 0) {
|
||||
alert('请填写大于0的数字');
|
||||
return;
|
||||
}
|
||||
if(volume > 1) {
|
||||
alert('请填写小于1的数字');
|
||||
return;
|
||||
}
|
||||
player.changeVolume(volume);
|
||||
}
|
||||
|
||||
function changeSize() {
|
||||
player.changeSize(w, h)
|
||||
}
|
||||
|
||||
function frontFun() {
|
||||
alert('点击了前一集');
|
||||
}
|
||||
|
||||
function nextFun() {
|
||||
alert('点击了下一集');
|
||||
}
|
||||
|
||||
function newVideo() {
|
||||
var videoUrl = player.getByElement('.videourl').value;
|
||||
changeVideo(videoUrl);
|
||||
}
|
||||
|
||||
function newVideo2() {
|
||||
var videoUrl = player.getByElement('.videourl2').value;
|
||||
changeVideo(videoUrl);
|
||||
}
|
||||
|
||||
function changeVideo(videoUrl) {
|
||||
if(player == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
var newVideoObject = {
|
||||
container: '#video', //容器的ID
|
||||
variable: 'player',
|
||||
autoplay: true, //是否自动播放
|
||||
loaded: 'loadedHandler', //当播放器加载后执行的函数
|
||||
video: videoUrl
|
||||
};
|
||||
//判断是需要重新加载播放器还是直接换新地址
|
||||
|
||||
if(player.playerType == 'html5video') {
|
||||
if(player.getFileExt(videoUrl) == '.flv' || player.getFileExt(videoUrl) == '.m3u8' || player.getFileExt(videoUrl) == '.f4v' || videoUrl.substr(0, 4) == 'rtmp') {
|
||||
player.removeChild();
|
||||
|
||||
player = null;
|
||||
player = new chplayer();
|
||||
player.embed(newVideoObject);
|
||||
} else {
|
||||
player.newVideo(newVideoObject);
|
||||
}
|
||||
} else {
|
||||
if(player.getFileExt(videoUrl) == '.mp4' || player.getFileExt(videoUrl) == '.webm' || player.getFileExt(videoUrl) == '.ogg') {
|
||||
player = null;
|
||||
player = new chplayer();
|
||||
player.embed(newVideoObject);
|
||||
} else {
|
||||
player.newVideo(newVideoObject);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function newElement() {
|
||||
var attribute = {
|
||||
list: [{
|
||||
type: 'image',
|
||||
url: 'screenshot/logo.png',
|
||||
radius: 30, //圆角弧度
|
||||
width: 30, //定义宽,必需要定义
|
||||
height: 30, //定义高,必需要定义
|
||||
alpha: 0.9, //透明度
|
||||
marginLeft: 10,
|
||||
marginRight: 10,
|
||||
marginTop: 10,
|
||||
marginBottom: 10
|
||||
},
|
||||
{
|
||||
type: 'text', //说明是文本
|
||||
text: '这里是一个普通的元件,不支持HTML', //文本内容
|
||||
fontColor: '#FFFFFF',
|
||||
fontSize: 14,
|
||||
fontFamily: '"Microsoft YaHei", YaHei, "微软雅黑", SimHei,"\5FAE\8F6F\96C5\9ED1", "黑体",Arial',
|
||||
lineHeight: 30,
|
||||
alpha: 1, //透明度
|
||||
//paddingLeft:10,//左边距离
|
||||
paddingRight: 10, //右边距离
|
||||
paddingTop: 0,
|
||||
paddingBottom: 0,
|
||||
marginLeft: 0,
|
||||
marginRight: 0,
|
||||
marginTop: 10,
|
||||
marginBottom: 0,
|
||||
//backgroundColor:'#000000',
|
||||
backAlpha: 0.5,
|
||||
backRadius: 30 //背景圆角弧度
|
||||
}
|
||||
],
|
||||
x: 10, //x轴坐标
|
||||
y: 10, //y轴坐标
|
||||
//position:[1,1],//位置[x轴对齐方式(0=左,1=中,2=右),y轴对齐方式(0=上,1=中,2=下),x轴偏移量(不填写或null则自动判断,第一个值为0=紧贴左边,1=中间对齐,2=贴合右边),y轴偏移量(不填写或null则自动判断,0=紧贴上方,1=中间对齐,2=紧贴下方)]
|
||||
alpha: 1,
|
||||
backgroundColor: '#000000',
|
||||
backAlpha: 0.5,
|
||||
backRadius: 60 //背景圆角弧度
|
||||
};
|
||||
var el = player.addElement(attribute);
|
||||
}
|
||||
|
||||
function newDanmu() {
|
||||
//弹幕说明
|
||||
|
||||
var danmuObj = {
|
||||
list: [{
|
||||
type: 'image',
|
||||
url: 'screenshot/logo.png',
|
||||
radius: 30, //圆角弧度
|
||||
width: 30, //定义宽,必需要定义
|
||||
height: 30, //定义高,必需要定义
|
||||
alpha: 0.9, //透明度
|
||||
marginLeft: 10,
|
||||
marginRight: 10,
|
||||
marginTop: 0,
|
||||
marginBottom: 0
|
||||
},
|
||||
{
|
||||
type: 'text', //说明是文本
|
||||
text: '演示弹幕内容,弹幕只支持普通文本,不支持HTML', //文本内容
|
||||
fontColor: '#FFFFFF',
|
||||
fontSize: 14,
|
||||
fontFamily: '"Microsoft YaHei", YaHei, "微软雅黑", SimHei,"\5FAE\8F6F\96C5\9ED1", "黑体",Arial',
|
||||
lineHeight: 30,
|
||||
alpha: 1, //透明度
|
||||
paddingLeft: 10, //左边距离
|
||||
paddingRight: 10, //右边距离
|
||||
paddingTop: 0,
|
||||
paddingBottom: 0,
|
||||
marginLeft: 0,
|
||||
marginRight: 0,
|
||||
marginTop: 0,
|
||||
marginBottom: 0,
|
||||
backgroundColor: '#000000',
|
||||
backAlpha: 0.5,
|
||||
backRadius: 30 //背景圆角弧度
|
||||
}
|
||||
],
|
||||
x: '100%', //x轴坐标
|
||||
y: "50%", //y轴坐标
|
||||
//position:[2,1,0],//位置[x轴对齐方式(0=左,1=中,2=右),y轴对齐方式(0=上,1=中,2=下),x轴偏移量(不填写或null则自动判断,第一个值为0=紧贴左边,1=中间对齐,2=贴合右边),y轴偏移量(不填写或null则自动判断,0=紧贴上方,1=中间对齐,2=紧贴下方)]
|
||||
alpha: 1,
|
||||
//backgroundColor:'#FFFFFF',
|
||||
backAlpha: 0.8,
|
||||
backRadius: 30 //背景圆角弧度
|
||||
};
|
||||
var danmu = player.addElement(danmuObj);
|
||||
var danmuS = player.getElement(danmu);
|
||||
var obj = {
|
||||
element: danmu,
|
||||
parameter: 'x',
|
||||
static: true, //是否禁止其它属性,true=是,即当x(y)(alpha)变化时,y(x)(x,y)在播放器尺寸变化时不允许变化
|
||||
effect: 'None.easeOut',
|
||||
start: null,
|
||||
end: -danmuS['width'],
|
||||
speed: 10,
|
||||
overStop: true,
|
||||
pauseStop: true,
|
||||
callBack: 'deleteChild'
|
||||
};
|
||||
var danmuAnimate = player.animate(obj);
|
||||
}
|
||||
|
||||
function deleteChild(ele) {
|
||||
if(player) {
|
||||
player.deleteElement(ele);
|
||||
}
|
||||
}
|
||||
|
||||
function changeText(div, text) {
|
||||
player.getByElement(div).innerHTML = text;
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
<p>
|
||||
<a href="http://www.chplayer.com/" target="_blank">官网chplayer.com</a>,版本号:1.0</p>
|
||||
<p>以下仅列出部分功能,全部功能请至官网<a href="http://www.chplayer.com/manual/" target="_blank">《手册》</a>查看</p>
|
||||
<p>
|
||||
<button type="button" onclick="player.play()">播放</button>
|
||||
<button type="button" onclick="player.pause()">暂停</button>
|
||||
<button type="button" onclick="player.playOrPause()">播放/暂停</button>
|
||||
<button type="button" onclick="loadedMetaDataHandler()">获取元数据</button>
|
||||
<button type="button" onclick="newElement()">添加元件</button>
|
||||
<button type="button" onclick="newDanmu()">添加弹幕</button>
|
||||
<a href="http://www.chplayer.com/manual/animate.html" target="_blank">更多弹幕动作</a>
|
||||
</p>
|
||||
<p class="metadata"></p>
|
||||
<p>单独监听功能:</p>
|
||||
<p class="handler">
|
||||
<span class="playstate">播放状态:暂停</span><br />
|
||||
<span class="seekstate">无跳转时间</span><br />
|
||||
<span class="volumechangestate">当前音量:0.8</span><br />
|
||||
<span class="fullstate">是否全屏:否</span><br />
|
||||
<span class="endedstate">还未结束</span><br />
|
||||
<span class="videochangestate">视频地址正常</span><br />
|
||||
<span class="currenttimestate">当前播放时间(秒):0</span>
|
||||
</p>
|
@ -14,11 +14,11 @@ class Learning extends FrontendService
|
||||
|
||||
use ChapterTrait;
|
||||
|
||||
public function handle()
|
||||
public function handle($id)
|
||||
{
|
||||
$post = $this->request->getPost();
|
||||
|
||||
$chapter = $this->checkChapterCache($post['chapter_id']);
|
||||
$chapter = $this->checkChapterCache($id);
|
||||
|
||||
$user = $this->getLoginUser();
|
||||
|
||||
|
@ -52,6 +52,7 @@ trait Auth
|
||||
|
||||
$user->id = 0;
|
||||
$user->name = 'guest';
|
||||
$user->avatar = kg_ci_avatar_img_url(null);
|
||||
|
||||
return $user;
|
||||
}
|
||||
|
@ -14,6 +14,7 @@
|
||||
"qcloudsms/qcloudsms_php": "0.1.*",
|
||||
"qcloud/cos-sdk-v5": "2.*",
|
||||
"workerman/gateway-worker": "^3.0.12",
|
||||
"workerman/gatewayclient": "^3.0.12",
|
||||
"whichbrowser/parser": "^2.0",
|
||||
"hightman/xunsearch": "^1.4.14",
|
||||
"aferrandini/phpqrcode": "1.0.1",
|
||||
|
409
composer.lock
generated
409
composer.lock
generated
@ -4,7 +4,7 @@
|
||||
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
||||
"This file is @generated automatically"
|
||||
],
|
||||
"content-hash": "1de77fca92539b23fbdbad81127bafd1",
|
||||
"content-hash": "d847018715ab8103c3b5e04db1807223",
|
||||
"packages": [
|
||||
{
|
||||
"name": "aferrandini/phpqrcode",
|
||||
@ -58,25 +58,25 @@
|
||||
"time": "2013-07-08T09:39:08+00:00"
|
||||
},
|
||||
{
|
||||
"name": "doctrine/lexer",
|
||||
"version": "1.1.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/doctrine/lexer.git",
|
||||
"reference": "e17f069ede36f7534b95adec71910ed1b49c74ea"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/doctrine/lexer/zipball/e17f069ede36f7534b95adec71910ed1b49c74ea",
|
||||
"reference": "e17f069ede36f7534b95adec71910ed1b49c74ea",
|
||||
"shasum": "",
|
||||
"mirrors": [
|
||||
{
|
||||
"url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%",
|
||||
"preferred": true
|
||||
}
|
||||
]
|
||||
},
|
||||
"name": "doctrine/lexer",
|
||||
"version": "1.1.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/doctrine/lexer.git",
|
||||
"reference": "e17f069ede36f7534b95adec71910ed1b49c74ea"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/doctrine/lexer/zipball/e17f069ede36f7534b95adec71910ed1b49c74ea",
|
||||
"reference": "e17f069ede36f7534b95adec71910ed1b49c74ea",
|
||||
"shasum": "",
|
||||
"mirrors": [
|
||||
{
|
||||
"url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%",
|
||||
"preferred": true
|
||||
}
|
||||
]
|
||||
},
|
||||
"require": {
|
||||
"php": "^7.2"
|
||||
},
|
||||
@ -703,16 +703,16 @@
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Michael Dowling",
|
||||
"email": "mtdowling@gmail.com",
|
||||
"homepage": "https://github.com/mtdowling"
|
||||
}
|
||||
],
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Michael Dowling",
|
||||
"email": "mtdowling@gmail.com",
|
||||
"homepage": "https://github.com/mtdowling"
|
||||
}
|
||||
],
|
||||
"description": "CRON for PHP: Calculate the next or previous run date and determine if a CRON expression is due",
|
||||
"keywords": [
|
||||
"cron",
|
||||
@ -1898,35 +1898,68 @@
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
"MIT"
|
||||
],
|
||||
"homepage": "http://www.workerman.net",
|
||||
"keywords": [
|
||||
"communication",
|
||||
"distributed"
|
||||
],
|
||||
"time": "2019-07-02T11:55:24+00:00"
|
||||
"homepage": "http://www.workerman.net",
|
||||
"keywords": [
|
||||
"communication",
|
||||
"distributed"
|
||||
],
|
||||
"time": "2019-07-02T11:55:24+00:00"
|
||||
},
|
||||
{
|
||||
"name": "workerman/workerman",
|
||||
"version": "v3.5.22",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/walkor/Workerman.git",
|
||||
"reference": "488f108f9e446f31bac4d689bb9f9fe3705862cf"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/walkor/Workerman/zipball/488f108f9e446f31bac4d689bb9f9fe3705862cf",
|
||||
"reference": "488f108f9e446f31bac4d689bb9f9fe3705862cf",
|
||||
"shasum": "",
|
||||
"mirrors": [
|
||||
{
|
||||
"url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%",
|
||||
"preferred": true
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "workerman/gatewayclient",
|
||||
"version": "v3.0.13",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/walkor/GatewayClient.git",
|
||||
"reference": "6f4e76f38947be5cabca2c6fee367151f248d949"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/walkor/GatewayClient/zipball/6f4e76f38947be5cabca2c6fee367151f248d949",
|
||||
"reference": "6f4e76f38947be5cabca2c6fee367151f248d949",
|
||||
"shasum": "",
|
||||
"mirrors": [
|
||||
{
|
||||
"url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%",
|
||||
"preferred": true
|
||||
}
|
||||
]
|
||||
},
|
||||
"type": "library",
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"GatewayClient\\": "./"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"homepage": "http://www.workerman.net",
|
||||
"time": "2018-09-15T03:03:50+00:00"
|
||||
},
|
||||
{
|
||||
"name": "workerman/workerman",
|
||||
"version": "v3.5.22",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/walkor/Workerman.git",
|
||||
"reference": "488f108f9e446f31bac4d689bb9f9fe3705862cf"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/walkor/Workerman/zipball/488f108f9e446f31bac4d689bb9f9fe3705862cf",
|
||||
"reference": "488f108f9e446f31bac4d689bb9f9fe3705862cf",
|
||||
"shasum": "",
|
||||
"mirrors": [
|
||||
{
|
||||
"url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%",
|
||||
"preferred": true
|
||||
}
|
||||
]
|
||||
},
|
||||
"require": {
|
||||
"php": ">=5.3"
|
||||
},
|
||||
@ -1951,14 +1984,14 @@
|
||||
"role": "Developer"
|
||||
}
|
||||
],
|
||||
"description": "An asynchronous event driven PHP framework for easily building fast, scalable network applications.",
|
||||
"homepage": "http://www.workerman.net",
|
||||
"keywords": [
|
||||
"asynchronous",
|
||||
"event-loop"
|
||||
],
|
||||
"time": "2019-09-06T03:42:47+00:00"
|
||||
},
|
||||
"description": "An asynchronous event driven PHP framework for easily building fast, scalable network applications.",
|
||||
"homepage": "http://www.workerman.net",
|
||||
"keywords": [
|
||||
"asynchronous",
|
||||
"event-loop"
|
||||
],
|
||||
"time": "2019-09-06T03:42:47+00:00"
|
||||
},
|
||||
{
|
||||
"name": "xiaochong0302/ip2region",
|
||||
"version": "v1.0.0",
|
||||
@ -1979,16 +2012,16 @@
|
||||
}
|
||||
]
|
||||
},
|
||||
"require": {
|
||||
"php": ">=7.0"
|
||||
},
|
||||
"type": "library",
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Koogua\\Ip2Region\\": "src"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"require": {
|
||||
"php": ">=7.0"
|
||||
},
|
||||
"type": "library",
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Koogua\\Ip2Region\\": "src"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
@ -2054,13 +2087,13 @@
|
||||
"email": "me@yansongda.cn"
|
||||
}
|
||||
],
|
||||
"description": "专注 Alipay 和 WeChat 的支付扩展包",
|
||||
"keywords": [
|
||||
"alipay",
|
||||
"pay",
|
||||
"wechat"
|
||||
],
|
||||
"time": "2019-09-21T15:05:57+00:00"
|
||||
"description": "专注 Alipay 和 WeChat 的支付扩展包",
|
||||
"keywords": [
|
||||
"alipay",
|
||||
"pay",
|
||||
"wechat"
|
||||
],
|
||||
"time": "2019-09-21T15:05:57+00:00"
|
||||
},
|
||||
{
|
||||
"name": "yansongda/supports",
|
||||
@ -2082,16 +2115,16 @@
|
||||
}
|
||||
]
|
||||
},
|
||||
"require": {
|
||||
"guzzlehttp/guzzle": "^6.2",
|
||||
"monolog/monolog": "^1.23 || ^2.0",
|
||||
"php": ">=7.1.3"
|
||||
},
|
||||
"require-dev": {
|
||||
"friendsofphp/php-cs-fixer": "^2.15",
|
||||
"phpunit/phpunit": "^7.5",
|
||||
"predis/predis": "^1.1"
|
||||
},
|
||||
"require": {
|
||||
"guzzlehttp/guzzle": "^6.2",
|
||||
"monolog/monolog": "^1.23 || ^2.0",
|
||||
"php": ">=7.1.3"
|
||||
},
|
||||
"require-dev": {
|
||||
"friendsofphp/php-cs-fixer": "^2.15",
|
||||
"phpunit/phpunit": "^7.5",
|
||||
"predis/predis": "^1.1"
|
||||
},
|
||||
"suggest": {
|
||||
"predis/predis": "Allows to use throttle feature"
|
||||
},
|
||||
@ -2347,16 +2380,16 @@
|
||||
}
|
||||
]
|
||||
},
|
||||
"require": {
|
||||
"cakephp/core": "^3.6.0",
|
||||
"php": ">=5.6.0",
|
||||
"psr/simple-cache": "^1.0.0"
|
||||
},
|
||||
"type": "library",
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Cake\\Cache\\": "."
|
||||
}
|
||||
"require": {
|
||||
"cakephp/core": "^3.6.0",
|
||||
"php": ">=5.6.0",
|
||||
"psr/simple-cache": "^1.0.0"
|
||||
},
|
||||
"type": "library",
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Cake\\Cache\\": "."
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.jp/downloads/",
|
||||
"license": [
|
||||
@ -2695,15 +2728,15 @@
|
||||
],
|
||||
"description": "CakePHP Utility classes such as Inflector, String, Hash, and Security",
|
||||
"homepage": "https://cakephp.org",
|
||||
"keywords": [
|
||||
"cakephp",
|
||||
"hash",
|
||||
"inflector",
|
||||
"security",
|
||||
"string",
|
||||
"utility"
|
||||
],
|
||||
"time": "2019-11-21T14:18:54+00:00"
|
||||
"keywords": [
|
||||
"cakephp",
|
||||
"hash",
|
||||
"inflector",
|
||||
"security",
|
||||
"string",
|
||||
"utility"
|
||||
],
|
||||
"time": "2019-11-21T14:18:54+00:00"
|
||||
},
|
||||
{
|
||||
"name": "jaeger/g-http",
|
||||
@ -2962,16 +2995,16 @@
|
||||
}
|
||||
]
|
||||
},
|
||||
"require": {
|
||||
"php": "^7.1",
|
||||
"riimu/kit-phpencoder": "^2.4",
|
||||
"robmorgan/phinx": "^0.11",
|
||||
"symfony/console": "^2.8|^3.0|^4.0|^5.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"overtrue/phplint": "^1.1",
|
||||
"phpstan/phpstan-shim": "^0.11",
|
||||
"phpunit/phpunit": "^7.0",
|
||||
"require": {
|
||||
"php": "^7.1",
|
||||
"riimu/kit-phpencoder": "^2.4",
|
||||
"robmorgan/phinx": "^0.11",
|
||||
"symfony/console": "^2.8|^3.0|^4.0|^5.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"overtrue/phplint": "^1.1",
|
||||
"phpstan/phpstan-shim": "^0.11",
|
||||
"phpunit/phpunit": "^7.0",
|
||||
"squizlabs/php_codesniffer": "^3.4"
|
||||
},
|
||||
"bin": [
|
||||
@ -2989,16 +3022,16 @@
|
||||
],
|
||||
"description": "Migration generator for Phinx",
|
||||
"homepage": "https://github.com/odan/phinx-migrations-generator",
|
||||
"keywords": [
|
||||
"database",
|
||||
"generator",
|
||||
"migration",
|
||||
"migrations",
|
||||
"mysql",
|
||||
"phinx"
|
||||
],
|
||||
"time": "2019-11-21T15:15:19+00:00"
|
||||
},
|
||||
"keywords": [
|
||||
"database",
|
||||
"generator",
|
||||
"migration",
|
||||
"migrations",
|
||||
"mysql",
|
||||
"phinx"
|
||||
],
|
||||
"time": "2019-11-21T15:15:19+00:00"
|
||||
},
|
||||
{
|
||||
"name": "phalcon/ide-stubs",
|
||||
"version": "v3.4.3",
|
||||
@ -3054,25 +3087,25 @@
|
||||
"time": "2018-12-09T14:11:06+00:00"
|
||||
},
|
||||
{
|
||||
"name": "psr/container",
|
||||
"version": "1.0.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/php-fig/container.git",
|
||||
"reference": "b7ce3b176482dbbc1245ebf52b181af44c2cf55f"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/php-fig/container/zipball/b7ce3b176482dbbc1245ebf52b181af44c2cf55f",
|
||||
"reference": "b7ce3b176482dbbc1245ebf52b181af44c2cf55f",
|
||||
"shasum": "",
|
||||
"mirrors": [
|
||||
{
|
||||
"url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%",
|
||||
"preferred": true
|
||||
}
|
||||
]
|
||||
},
|
||||
"name": "psr/container",
|
||||
"version": "1.0.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/php-fig/container.git",
|
||||
"reference": "b7ce3b176482dbbc1245ebf52b181af44c2cf55f"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/php-fig/container/zipball/b7ce3b176482dbbc1245ebf52b181af44c2cf55f",
|
||||
"reference": "b7ce3b176482dbbc1245ebf52b181af44c2cf55f",
|
||||
"shasum": "",
|
||||
"mirrors": [
|
||||
{
|
||||
"url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%",
|
||||
"preferred": true
|
||||
}
|
||||
]
|
||||
},
|
||||
"require": {
|
||||
"php": ">=5.3.0"
|
||||
},
|
||||
@ -3692,15 +3725,15 @@
|
||||
],
|
||||
"description": "Generic abstractions related to writing services",
|
||||
"homepage": "https://symfony.com",
|
||||
"keywords": [
|
||||
"abstractions",
|
||||
"contracts",
|
||||
"decoupling",
|
||||
"interfaces",
|
||||
"interoperability",
|
||||
"standards"
|
||||
],
|
||||
"time": "2019-11-18T17:27:11+00:00"
|
||||
"keywords": [
|
||||
"abstractions",
|
||||
"contracts",
|
||||
"decoupling",
|
||||
"interfaces",
|
||||
"interoperability",
|
||||
"standards"
|
||||
],
|
||||
"time": "2019-11-18T17:27:11+00:00"
|
||||
},
|
||||
{
|
||||
"name": "symfony/var-dumper",
|
||||
@ -3785,25 +3818,25 @@
|
||||
"time": "2019-12-18T13:41:29+00:00"
|
||||
},
|
||||
{
|
||||
"name": "symfony/yaml",
|
||||
"version": "v5.0.1",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/yaml.git",
|
||||
"reference": "51b684480184fa767b97e28eaca67664e48dd3e9"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/yaml/zipball/51b684480184fa767b97e28eaca67664e48dd3e9",
|
||||
"reference": "51b684480184fa767b97e28eaca67664e48dd3e9",
|
||||
"shasum": "",
|
||||
"mirrors": [
|
||||
{
|
||||
"url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%",
|
||||
"preferred": true
|
||||
}
|
||||
]
|
||||
},
|
||||
"name": "symfony/yaml",
|
||||
"version": "v5.0.1",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/yaml.git",
|
||||
"reference": "51b684480184fa767b97e28eaca67664e48dd3e9"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/yaml/zipball/51b684480184fa767b97e28eaca67664e48dd3e9",
|
||||
"reference": "51b684480184fa767b97e28eaca67664e48dd3e9",
|
||||
"shasum": "",
|
||||
"mirrors": [
|
||||
{
|
||||
"url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%",
|
||||
"preferred": true
|
||||
}
|
||||
]
|
||||
},
|
||||
"require": {
|
||||
"php": "^7.2.5",
|
||||
"symfony/polyfill-ctype": "~1.8"
|
||||
@ -3835,16 +3868,16 @@
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Fabien Potencier",
|
||||
"email": "fabien@symfony.com"
|
||||
},
|
||||
{
|
||||
"name": "Symfony Community",
|
||||
"homepage": "https://symfony.com/contributors"
|
||||
}
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Fabien Potencier",
|
||||
"email": "fabien@symfony.com"
|
||||
},
|
||||
{
|
||||
"name": "Symfony Community",
|
||||
"homepage": "https://symfony.com/contributors"
|
||||
}
|
||||
],
|
||||
"description": "Symfony Yaml Component",
|
||||
"homepage": "https://symfony.com",
|
||||
"time": "2019-11-18T17:27:11+00:00"
|
||||
|
@ -727,6 +727,11 @@
|
||||
color: gray;
|
||||
}
|
||||
|
||||
.live-player {
|
||||
width: 800px;
|
||||
height: 450px;
|
||||
}
|
||||
|
||||
.chapter-bg {
|
||||
background-color: #f2f2f2;
|
||||
}
|
||||
|
96
public/static/web/js/live.im.js
Normal file
96
public/static/web/js/live.im.js
Normal file
@ -0,0 +1,96 @@
|
||||
layui.use(['jquery', 'layim'], function () {
|
||||
|
||||
var $ = layui.jquery;
|
||||
var layim = layui.layim;
|
||||
|
||||
var socket = new WebSocket('ws://127.0.0.1:8282');
|
||||
|
||||
var membersUrl = $('input[name="im.members_url"]').val();
|
||||
var bindUserUrl = $('input[name="im.bind_user_url"]').val();
|
||||
var sendMsgUrl = $('input[name="im.send_msg_url"]').val();
|
||||
|
||||
var group = {
|
||||
id: $('input[name="chapter.id"]').val(),
|
||||
avatar: 'http://tp1.sinaimg.cn/5619439268/180/40030060651/1',
|
||||
name: '直播讨论'
|
||||
};
|
||||
|
||||
var user = {
|
||||
id: $('input[name="user.id"]').val(),
|
||||
name: $('input[name="user.name"]').val(),
|
||||
avatar: $('input[name="user.avatar"]').val(),
|
||||
status: 'online',
|
||||
sign: ''
|
||||
};
|
||||
|
||||
layim.config({
|
||||
brief: true,
|
||||
init: {
|
||||
mine: {
|
||||
'username': user.name,
|
||||
'avatar': user.avatar,
|
||||
'id': user.id,
|
||||
'status': user.status,
|
||||
'sign': user.sign
|
||||
}
|
||||
},
|
||||
members: {
|
||||
url: membersUrl
|
||||
}
|
||||
}).chat({
|
||||
type: 'group',
|
||||
name: group.name,
|
||||
avatar: group.avatar,
|
||||
id: group.id
|
||||
});
|
||||
|
||||
layim.on('sendMessage', function (res) {
|
||||
sendMessage(res.mine, res.to);
|
||||
});
|
||||
|
||||
socket.onopen = function () {
|
||||
console.log('socket connect success');
|
||||
};
|
||||
|
||||
socket.onclose = function () {
|
||||
console.log('socket connect close');
|
||||
};
|
||||
|
||||
socket.onerror = function () {
|
||||
console.log('socket connect error');
|
||||
};
|
||||
|
||||
socket.onmessage = function (e) {
|
||||
var data = JSON.parse(e.data);
|
||||
console.log(data);
|
||||
if (data.type === 'ping') {
|
||||
socket.send('pong...');
|
||||
} else if (data.type === 'bind_user') {
|
||||
bindUser(data.client_id);
|
||||
} else if (data.type === 'show_message') {
|
||||
showMessage(data.content);
|
||||
}
|
||||
};
|
||||
|
||||
function bindUser(clientId) {
|
||||
$.ajax({
|
||||
type: 'POST',
|
||||
url: bindUserUrl,
|
||||
data: {client_id: clientId}
|
||||
});
|
||||
}
|
||||
|
||||
function sendMessage(from, to) {
|
||||
$.ajax({
|
||||
type: 'POST',
|
||||
dataType: 'json',
|
||||
url: sendMsgUrl,
|
||||
data: {from: from, to: to}
|
||||
});
|
||||
}
|
||||
|
||||
function showMessage(message) {
|
||||
layim.getMessage(message);
|
||||
}
|
||||
|
||||
});
|
104
public/static/web/js/live.player.js
Normal file
104
public/static/web/js/live.player.js
Normal file
@ -0,0 +1,104 @@
|
||||
var interval = null;
|
||||
var intervalTime = 5000;
|
||||
var position = 0;
|
||||
var chapterId = $('input[name="chapter.id"]').val();
|
||||
var planId = $('input[name="chapter.plan_id"]').val();
|
||||
var userId = $('input[name="user.id"]').val();
|
||||
var learningUrl = $('input[name="chapter.learning_url"]').val();
|
||||
var playUrls = JSON.parse($('input[name="chapter.play_urls"]').val());
|
||||
var requestId = getRequestId();
|
||||
|
||||
var options = {
|
||||
live: true,
|
||||
autoplay: true,
|
||||
h5_flv: true,
|
||||
width: 800,
|
||||
height: 450
|
||||
};
|
||||
|
||||
if (playUrls.rtmp && playUrls.rtmp.od) {
|
||||
options.rtmp = playUrls.rtmp.od;
|
||||
}
|
||||
|
||||
if (playUrls.rtmp && playUrls.rtmp.hd) {
|
||||
options.rtmp_hd = playUrls.rtmp.hd;
|
||||
}
|
||||
|
||||
if (playUrls.rtmp && playUrls.rtmp.sd) {
|
||||
options.rtmp_sd = playUrls.rtmp.sd;
|
||||
}
|
||||
|
||||
if (playUrls.flv && playUrls.flv.od) {
|
||||
options.flv = playUrls.flv.od;
|
||||
}
|
||||
|
||||
if (playUrls.flv && playUrls.flv.hd) {
|
||||
options.flv_hd = playUrls.flv.hd;
|
||||
}
|
||||
|
||||
if (playUrls.flv && playUrls.flv.sd) {
|
||||
options.flv_sd = playUrls.flv.sd;
|
||||
}
|
||||
|
||||
if (playUrls.m3u8 && playUrls.m3u8.od) {
|
||||
options.m3u8 = playUrls.m3u8.od;
|
||||
}
|
||||
|
||||
if (playUrls.m3u8 && playUrls.m3u8.hd) {
|
||||
options.m3u8_hd = playUrls.m3u8.hd;
|
||||
}
|
||||
|
||||
if (playUrls.m3u8 && playUrls.m3u8.sd) {
|
||||
options.m3u8_sd = playUrls.m3u8.sd;
|
||||
}
|
||||
|
||||
if (userId !== '0' && planId !== '0') {
|
||||
options.listener = function (msg) {
|
||||
if (msg.type === 'play') {
|
||||
start();
|
||||
} else if (msg.type === 'pause') {
|
||||
stop();
|
||||
} else if (msg.type === 'end') {
|
||||
stop();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var player = new TcPlayer('player', options);
|
||||
|
||||
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: 'POST',
|
||||
url: learningUrl,
|
||||
data: {
|
||||
request_id: requestId,
|
||||
chapter_id: chapterId,
|
||||
plan_id: planId,
|
||||
interval: intervalTime,
|
||||
position: player.currentTime(),
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function getRequestId() {
|
||||
var id = Date.now().toString(36);
|
||||
id += Math.random().toString(36).substr(3);
|
||||
return id;
|
||||
}
|
@ -38,8 +38,8 @@ class Events
|
||||
public static function onConnect($clientId)
|
||||
{
|
||||
$message = json_encode([
|
||||
'type' => 'welcome',
|
||||
'message' => 'just enjoy it',
|
||||
'type' => 'bind_user',
|
||||
'client_id' => $clientId,
|
||||
]);
|
||||
|
||||
Gateway::sendToClient($clientId, $message);
|
||||
@ -53,25 +53,7 @@ class Events
|
||||
*/
|
||||
public static function onMessage($clientId, $message)
|
||||
{
|
||||
$content = json_decode($message, true);
|
||||
|
||||
if (!isset($content['type'])) return;
|
||||
|
||||
if ($content['type'] == 'join_group') {
|
||||
|
||||
$_SESSION['group'] = $content['group'];
|
||||
|
||||
Gateway::joinGroup($clientId, $content['group']);
|
||||
|
||||
} elseif ($content['type'] == 'send_danmaku') {
|
||||
|
||||
$message = [
|
||||
'type' => 'show_danmaku',
|
||||
'danmaku' => $content['danmaku'],
|
||||
];
|
||||
|
||||
Gateway::sendToGroup($_SESSION['group'], json_encode($message), [$clientId]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
Loading…
x
Reference in New Issue
Block a user