diff --git a/plugins/Common.php b/plugins/Common.php index 51d3f4c..e714b87 100644 --- a/plugins/Common.php +++ b/plugins/Common.php @@ -530,7 +530,7 @@ Class Common { //保存数据到文件缓存 //缓存数据格式:{ctime: timestamp, data: anything} - public static function saveCacheToFile($key, $data) { + public static function saveCacheToFile($key, $data, $cacheSubDir = '') { $cacheData = array( "ctime" => time(), "data" => $data, @@ -538,6 +538,11 @@ Class Common { $jsonData = json_encode($cacheData, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE); $cacheDir = __DIR__ . '/../runtime/cache/'; + //子目录支持 + if (!empty($cacheSubDir)) { + $cacheDir .= preg_match('/\/$/', $cacheSubDir) ? $cacheSubDir : "{$cacheSubDir}/"; + } + if (!is_dir($cacheDir)) { mkdir($cacheDir, 0700, true); } @@ -548,8 +553,12 @@ Class Common { //从文件缓存读取数据 //expireSeconds: 缓存失效时间,默认10分钟 - public static function getCacheFromFile($key, $expireSeconds = 600) { + public static function getCacheFromFile($key, $expireSeconds = 600, $cacheSubDir = '') { $cacheDir = __DIR__ . '/../runtime/cache/'; + //子目录支持 + if (!empty($cacheSubDir)) { + $cacheDir .= preg_match('/\/$/', $cacheSubDir) ? $cacheSubDir : "{$cacheSubDir}/"; + } $cache_filename = "{$cacheDir}{$key}.json"; if (file_exists($cache_filename)) { @@ -569,8 +578,12 @@ Class Common { } //删除缓存文件 - public static function cleanFileCache($key) { + public static function cleanFileCache($key, $cacheSubDir = '') { $cacheDir = __DIR__ . '/../runtime/cache/'; + //子目录支持 + if (!empty($cacheSubDir)) { + $cacheDir .= preg_match('/\/$/', $cacheSubDir) ? $cacheSubDir : "{$cacheSubDir}/"; + } $cache_filename = "{$cacheDir}{$key}.json"; if (file_exists($cache_filename)) { diff --git a/themes/beauty/controller/SiteController.php b/themes/beauty/controller/SiteController.php index ca3ef16..1cdbd7d 100644 --- a/themes/beauty/controller/SiteController.php +++ b/themes/beauty/controller/SiteController.php @@ -208,8 +208,9 @@ Class SiteController extends Controller { public function actionPlayer() { $videoUrl = $this->get('url', ''); - if (empty($videoUrl)) { - throw new Exception("缺少视频地址url参数!", 403); + $videoId = $this->get('id', ''); + if (empty($videoUrl) || empty($videoId)) { + throw new Exception("缺少视频地址url或id参数!", 403); } $arr = parse_url($videoUrl); @@ -231,9 +232,58 @@ Class SiteController extends Controller { $this->layout = 'player'; $viewName = 'player'; $params = compact( - 'videoUrl', 'videoFilename', 'copyright' + 'videoUrl', 'videoId', 'videoFilename', 'copyright' ); return $this->render($viewName, $params, $pageTitle); } + //根据视频文件id返回缓存数据: + //{duration: 单位秒的时长, snapshot: base64格式的jpg封面图} + public function actionVideometa() { + $code = 1; + $msg = 'OK'; + $meta = array(); + + $videoId = $this->get('id', ''); + if (empty($videoId)) { + $code = 0; + $msg = '参数不能为空'; + }else { + $cacheKey = $videoId; + $expireSeconds = 86400*30; //有效期30天 + $cacheSubDir = 'video'; + $cachedData = Common::getCacheFromFile($cacheKey, $expireSeconds, $cacheSubDir); + if (!empty($cachedData)) { + $meta = $cachedData; + }else { + $code = 0; + $msg = '此视频无缓存或缓存已过期'; + } + } + + return $this->renderJson(compact('code', 'msg', 'meta')); + } + + //保存视频meta数据到缓存 + public function actionSavevideometa() { + $code = 0; + $msg = 'OK'; + + $videoId = $this->post('id', ''); + $metaData = $this->post('meta', ''); + if (empty($videoId) || empty($metaData)) { + $code = 0; + $msg = '参数不能为空'; + }else { + $cacheKey = $videoId; + $cacheSubDir = 'video'; + $saved = Common::saveCacheToFile($cacheKey, $metaData, $cacheSubDir); + if ($saved !== false) { + $code = 1; + } + } + + return $this->renderJson(compact('code', 'msg')); + } + } diff --git a/themes/beauty/views/site/index.php b/themes/beauty/views/site/index.php index 9402be7..f820a1e 100644 --- a/themes/beauty/views/site/index.php +++ b/themes/beauty/views/site/index.php @@ -258,13 +258,15 @@ eof; $videoUrl = urlencode($file['path']); echo << - - {$file['filename']} + + {$file['filename']}
{$title}
+ video play button + 00:00:00
eof; @@ -289,13 +291,15 @@ eof; $videoUrl = urlencode($file['path']); echo << - - {$file['filename']} + + {$file['filename']}
{$title}
+ video play button + 00:00:00
eof; diff --git a/themes/beauty/views/site/player.php b/themes/beauty/views/site/player.php index 04aac5c..4f684cb 100644 --- a/themes/beauty/views/site/player.php +++ b/themes/beauty/views/site/player.php @@ -27,12 +27,13 @@
-
diff --git a/www/css/beauty.css b/www/css/beauty.css index c3c9552..8b8c859 100644 --- a/www/css/beauty.css +++ b/www/css/beauty.css @@ -122,11 +122,13 @@ a:link{text-decoration:none} .lampshow .text_dark{color:#808080} .lampshow .text_dark a{color:#f4f7fd} -#qrimg{width:160px;height:160px;margin:0 auto 20px auto;background-color:#FFF;padding:5px;border:1px solid #EEE;border-radius:5px} +#qrimg{width:160px;height:160px;margin:0 auto 20px auto;background-color:#FFF;padding:5px;border:1px solid #EEE;border-radius:5px;overflow:hidden} .videoplayer{max-width:800px;margin:0 auto;text-align:center} .videoplayer video{width:100%} .videotitle{max-width:600px;overflow:hidden;text-wrap:nowrap;text-overflow:ellipsis} +.playbtn{width:50px;height:50px;position:absolute;left:50%;top:50%;margin-top:-25px;margin-left:-25px} +.duration{position:absolute;left:5px;bottom:5px;background-color:#222;padding:0 3px;opacity:0.7;border-radius:3px;font-size:12px;color:#FFF} .mt-2{margin-top:2em} .mr-1{margin-right:1em} diff --git a/www/js/beauty.js b/www/js/beauty.js index 6540adf..c42eb5e 100644 --- a/www/js/beauty.js +++ b/www/js/beauty.js @@ -4,6 +4,9 @@ ******Tan */ +//关闭videojs的ga统计 +window.HELP_IMPROVE_VIDEOJS = false; + if ($('#image_site').get(0)) { // 图片浏览 @@ -185,4 +188,103 @@ if ($('#qrimg').length > 0 && typeof(QRCode) != 'undefined') { colorLight : "#ffffff", correctLevel : QRCode.CorrectLevel.L }); +} + +var formatDuration = function(duration) { + var str = '00:00:00'; + + var hours = 0, minutes = 0; + + if (duration > 3600) { + hours = Math.floor(duration / 3600); + } + + if (duration > 60) { + minutes = Math.floor((duration-hours*3600) / 60); + } + + var seconds = Math.floor(duration - hours*3600 - minutes*60); + + str = hours.toString().padStart(2, '0') + ':' + + minutes.toString().padStart(2, '0') + ':' + + seconds.toString().padStart(2, '0'); + + return str; +}; + +//视频列表封面图加载 +var getVideoMetaAndShowIt = function(videoId) { + $.ajax({ + url: '/site/videometa', + method: 'GET', + dataType: 'json', + data: { + id: videoId + } + }).done(function(data) { + if (data.code != 1) { + console.warn('视频数据获取失败', data.msg); + }else { + $('#poster_'+videoId).attr('src', data.meta.snapshot); + $('#poster_'+videoId).parent('a').find('.duration').text(formatDuration(data.meta.duration)); + } + }).fail(function(jqXHR, textStatus, errorThrown) { + console.error('视频数据获取失败,错误信息:' + errorThrown); + }); +}; + +$('.video-poster').each(function(index, el) { + var videoId = $(el).attr('data-id'); + getVideoMetaAndShowIt(videoId); +}); + +//保存视频数据 +var saveVideoMeta = function(videoId, metaData) { + $.ajax({ + url: '/site/savevideometa', + method: 'POST', + dataType: 'json', + data: { + id: videoId, + meta: metaData + } + }).done(function(data) { + if (data.code != 1) { + console.warn('视频数据保存失败', data.msg); + } + }).fail(function(jqXHR, textStatus, errorThrown) { + console.error('视频数据保存失败,错误信息:' + errorThrown); + }); +}; + +//视频播放器 +if ($('#my-player').length > 0 && typeof(videojs) != 'undefined') { + var myPlayer = videojs('my-player', { + controls: true, + autoplay: true, + muted: true, + preload: 'auto' + }); + + myPlayer.one('play', function() { + myPlayer.pause(); + + var canvas = document.createElement('canvas'); + var video = $('video.vjs-tech').get(0); + + //360p + canvas.width = 640; + canvas.height = 360; + + var ctx = canvas.getContext('2d'); + ctx.drawImage( video, 0, 0, canvas.width, canvas.height ); + + var snapshotImg = canvas.toDataURL('image/jpeg'); + saveVideoMeta($('video.vjs-tech').attr('data-id'), { + duration: myPlayer.duration(), + snapshot: snapshotImg + }); + + myPlayer.play(); + }); } \ No newline at end of file