|
|
|
<?php
|
|
|
|
/**
|
|
|
|
* M3u8 Controller
|
|
|
|
*/
|
|
|
|
require_once __DIR__ . '/../../../lib/DirScanner.php';
|
|
|
|
require_once __DIR__ . '/../../../plugins/Parsedown.php';
|
|
|
|
require_once __DIR__ . '/../../../plugins/Common.php';
|
|
|
|
|
|
|
|
Class M3u8Controller extends Controller {
|
|
|
|
protected $allFilesCacheKey = 'MainBotAllFiles';
|
|
|
|
|
|
|
|
//参数
|
|
|
|
//@id - 文件id
|
|
|
|
//@cid - 数据缓存id
|
|
|
|
//支持nginx secure防盗链:md5={$md5}&expires={$expires}
|
|
|
|
//支持只传video id,从索引数据中获取文件信息
|
|
|
|
public function actionIndex() {
|
|
|
|
$videoId = $this->get('id', '');
|
|
|
|
$cacheParentDataId = $this->get('cid', '');
|
|
|
|
if (empty($videoId)) {
|
|
|
|
throw new Exception("参数缺失!", 403);
|
|
|
|
}
|
|
|
|
|
|
|
|
//TODO: 防盗链检查
|
|
|
|
|
|
|
|
|
|
|
|
if (!empty($cacheParentDataId)) {
|
|
|
|
$cacheSeconds = 86400;
|
|
|
|
$cachedParentData = Common::getCacheFromFile($cacheParentDataId, $cacheSeconds);
|
|
|
|
if (empty($cachedParentData)) {
|
|
|
|
$err = '缓存数据已失效,如果重新点击目录依然打不开,请联系管理员。';
|
|
|
|
throw new Exception($err, 404);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!empty($cachedParentData) && empty($cachedParentData[$videoId])) {
|
|
|
|
$erro = "缓存数据中找不到当前视频,请返回上一页重新进入!";
|
|
|
|
throw new Exception($err, 404);
|
|
|
|
}else if (!empty($cachedParentData[$videoId])) {
|
|
|
|
//渲染m3u8内容
|
|
|
|
$m3u8 = $cachedParentData[$videoId];
|
|
|
|
$m3u8Content = $this->getM3u8Content($m3u8['realpath'], $cachedParentData);
|
|
|
|
if (!empty($m3u8Content)) {
|
|
|
|
return $this->renderM3u8($m3u8Content);
|
|
|
|
}else {
|
|
|
|
$err = 'm3u8内容为空!';
|
|
|
|
throw new Exception($err, 500);
|
|
|
|
}
|
|
|
|
}else { //尝试从索引数据中获取文件信息
|
|
|
|
$cacheDir = 'index';
|
|
|
|
$expireSeconds = 86400 * 365; //缓存 365 天
|
|
|
|
$cacheData = Common::getCacheFromFile($this->allFilesCacheKey . '_stats', $expireSeconds, $cacheDir);
|
|
|
|
if (!empty($cacheData)) {
|
|
|
|
$dirNum = $cacheData['dirnum'];
|
|
|
|
$index = Common::getIndexNumByFileId($videoId, $dirNum) + 1;
|
|
|
|
$cacheKey = $this->allFilesCacheKey . "_{$index}";
|
|
|
|
$cacheFiles = Common::getCacheFromFile($cacheKey, $expireSeconds, $cacheDir);
|
|
|
|
|
|
|
|
if (!empty($cacheFiles[$videoId])) {
|
|
|
|
$m3u8 = $cacheFiles[$videoId];
|
|
|
|
$m3u8Content = $this->getM3u8Content($m3u8['realpath']);
|
|
|
|
if (!empty($m3u8Content)) {
|
|
|
|
return $this->renderM3u8($m3u8Content);
|
|
|
|
}else {
|
|
|
|
$err = 'm3u8内容为空!';
|
|
|
|
throw new Exception($err, 500);
|
|
|
|
}
|
|
|
|
}else {
|
|
|
|
$err = '索引数据中找不到此视频!';
|
|
|
|
throw new Exception($err, 500);
|
|
|
|
}
|
|
|
|
}else {
|
|
|
|
$err = '没有索引数据,请参考文档生成索引!';
|
|
|
|
throw new Exception($err, 500);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
//生成m3u8内容,支持ts防盗链
|
|
|
|
/**
|
|
|
|
* M3u8 content sample:
|
|
|
|
#EXTM3U
|
|
|
|
#EXT-X-VERSION:3
|
|
|
|
#EXT-X-TARGETDURATION:6
|
|
|
|
#EXT-X-PLAYLIST-TYPE:VOD
|
|
|
|
#EXTINF:5,
|
|
|
|
0.ts
|
|
|
|
#EXTINF:5,
|
|
|
|
1.ts
|
|
|
|
#EXTINF:5,
|
|
|
|
2.ts
|
|
|
|
#EXTINF:5,
|
|
|
|
3.ts
|
|
|
|
#EXTINF:5,
|
|
|
|
4.ts
|
|
|
|
#EXTINF:0.31697,
|
|
|
|
5.ts
|
|
|
|
#EXT-X-ENDLIST
|
|
|
|
**/
|
|
|
|
protected function getM3u8Content($m3u8_realpath, $cachedParentData = array()) {
|
|
|
|
$m3u8Content = file_get_contents($m3u8_realpath);
|
|
|
|
if (empty($m3u8Content) || strpos($m3u8Content, 'EXTM3U') === false) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
$lines = preg_split("/[\r\n]/", $m3u8Content);
|
|
|
|
|
|
|
|
$newContent = '';
|
|
|
|
foreach($lines as $index => $line) {
|
|
|
|
if (strpos($line, '.ts') !== false) {
|
|
|
|
$newContent .= $this->getRelativePathOfTs($line, $m3u8_realpath, $cachedParentData) . "\n";
|
|
|
|
}else if (!empty($line)) {
|
|
|
|
$newContent .= $line . "\n";
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return $newContent;
|
|
|
|
}
|
|
|
|
|
|
|
|
//返回ts相对当前m3u8文件的相对路径
|
|
|
|
//TODO: 支持防盗链
|
|
|
|
protected function getRelativePathOfTs($ts_filename, $m3u8_realpath, $cachedParentData = array()) {
|
|
|
|
if (!empty($cachedParentData)) {
|
|
|
|
$matchedTs = null;
|
|
|
|
foreach($cachedParentData as $item) {
|
|
|
|
if (!empty($item['extension']) && $item['extension'] == 'ts' && strpos($item['path'], $ts_filename) !== false) {
|
|
|
|
$matchedTs = $item;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!empty($matchedTs)) {
|
|
|
|
return $matchedTs['path'];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
$webroot = FSC::$app['config']['content_directory'];
|
|
|
|
$rootDir = __DIR__ . '/../../../www/' . $webroot;
|
|
|
|
$rootDir = realpath($rootDir);
|
|
|
|
$m3u8Dir = dirname($m3u8_realpath);
|
|
|
|
$relativeDir = str_replace("{$rootDir}/", '', $m3u8Dir);
|
|
|
|
return "/{$webroot}{$relativeDir}/{$ts_filename}";
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|