业务中,需要获取上传视频的时长,用于外部展示。
目前暂未找到理想的方法,只能用“大炮打蚊子”的方式——使用 ffmpeg。
通过下载 ffmpeg,在 BT 面板中安装 PHP 5.6,并放开 exec 函数。在建立的新站点中,使用 PHP5.6 版本。因为,为了安全 PHP 5.6 允许执行外部程序,且其他站点不使用 PHP 5.6 版本,而后,由 PHP 程序执行 ffmpeg,获取时间时长并存储到数据库中。
1、下载 ffmpeg
打开 https://www.gyan.dev/ffmpeg/builds/
点击左侧导航菜单中的 release builds,在右侧,找到,ffmpeg-release-essentials.zip ,点击以下载。
根据需要下载工具,点击左侧导航菜单中的 tools,在右侧,找到,ffmpeg-tools.zip ,点击以下载。
包含的工具有:
aviocat crypto_bench cws2fws ffescape ffeval ffhash fourcc2pixfmt graph2dot ismindex pktdumper probetest qt-faststart seek_print sidxindex venc_data_dump zmqsend
将压缩包中的 bin 目录解压,如存放在 d:\ffmpeg\bin 中。
2、设置环境变量
在“此电脑”中,点击右键,选择“属性”,之后选择“高级系统设置”。
在“高级选择卡”中点击“环境变量”,在“系统变量”中,找到“Path”,双击编辑后,添加新的环境变量值,如 d:\ffmpeg\bin。
3、新建建站
绑定的域名,填写,127.0.0.2(只用于内部访问), 无数据库,无 FTP,PHP 版本设置为 5.6,且在禁用函数中,移除 exec。
编辑 .user.ini,增加 TP5 站点目录,ffmpeg 程序目录。
open_basedir="d:/wwwroot/syncvideoinfo/;D:/wwwroot/kaoshi/;D:/ffmpeg/bin/;C:/Windows/Temp/;C:/Temp/;D:/BtSoft/temp/session/"
open_basedir="d:/wwwroot/syncvideoinfo/;D:/wwwroot/kaoshi/;D:/ffmpeg/bin/;C:/Windows/Temp/;C:/Temp/;D:/BtSoft/temp/session/"
open_basedir="d:/wwwroot/syncvideoinfo/;D:/wwwroot/kaoshi/;D:/ffmpeg/bin/;C:/Windows/Temp/;C:/Temp/;D:/BtSoft/temp/session/"
4、使用 SyncVideo.php
该程序是基于 TP5 框架的,因为需要设置 .env 所在目录,及解析视频地址。
之后读取数据库中存储的视频地址,并获取时间,然后,写入到数据库中。
/** @var string env 配置文件路径 */
/** @var string env 待解析视频目录 */
$this->lockFile = "./sync.lock";
$this->dbEnvFile = "../kaoshi/.env";
$this->parseVideoDir = "../kaoshi/public";
public function canSync($write = false, $val = 0)
return file_put_contents($this->lockFile, $val) ? true : false;
$data = file_get_contents($this->lockFile);
public function getVideoMins($file)
$command = "ffmpeg -i " . $file . " 2>&1";
$line = implode('', $output);
if (preg_match("/Duration: (.*?), /", $line, $matches)) {
$time = explode(':', $matches[1]);
return (intval($time[0]) * 60 * 60) + intval($time[1]) * 60 + intval($time[2]);
throw new Exception("Please hold on ...");
$this->canSync(true, date('Y-m-d H:i:s'));
$dbConfig = parse_ini_file($this->dbEnvFile, true);
$conn = new PDO("mysql:host={$dbConfig['database']['hostname']};dbname={$dbConfig['database']['database']}",
$dbConfig['database']['username'],
$dbConfig['database']['password']);
$conn->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$list = $conn->query("SELECT id,moive_file FROM fa_exam_course_movie where mins=0");
$res['count'] = count($list->rowCount() > 0 ? $list : []);
foreach ($list as $row) {
$sec = $this->getVideoMins($this->parseVideoDir . $row['moive_file']);
$sql = "update fa_exam_course_movie set mins=:sec,updatetime=:t where id=:id";
$stmt = $conn->prepare($sql);
$stmt->bindParam(':sec', $sec);
$stmt->bindParam(':t', $t);
$stmt->bindParam(':id', $row['id']);
'moive_file' => $row['moive_file'],
'status' => $stmt->rowCount(),
} catch (PDOException $e) {
throw new Exception($e->getMessage());
$data = (new SyncVideo())->sync();
echo date('Y-m-d H:i:s'), " count: ", $data['count'], PHP_EOL;
foreach ($data['list'] as $v) {
echo $v['id'], "\t", $v['moive_file'], "\t", $v['length'], "\t", $v['status'], PHP_EOL;
<?php
/**
* 同步视频长度
*/
class SyncVideo
{
/** @var string 锁文件路径 */
private $lockFile;
/** @var string env 配置文件路径 */
private $dbEnvFile;
/** @var string env 待解析视频目录 */
private $parseVideoDir;
function __construct()
{
$this->lockFile = "./sync.lock";
$this->dbEnvFile = "../kaoshi/.env";
$this->parseVideoDir = "../kaoshi/public";
}
public function canSync($write = false, $val = 0)
{
if ($write) {
return file_put_contents($this->lockFile, $val) ? true : false;
}
$data = file_get_contents($this->lockFile);
if ($data) {
return false;
} else {
return true;
}
}
public function getVideoMins($file)
{
$command = "ffmpeg -i " . $file . " 2>&1";
exec($command, $output);
// 从输出中提取视频信息
$line = implode('', $output);
// 提取视频时长
if (preg_match("/Duration: (.*?), /", $line, $matches)) {
$time = explode(':', $matches[1]);
return (intval($time[0]) * 60 * 60) + intval($time[1]) * 60 + intval($time[2]);
}
return 0;
}
/**
* @throws Exception
*/
public function sync()
{
$res = [
'count' => 0,
'list' => [],
];
if (!$this->canSync()) {
throw new Exception("Please hold on ...");
}
$this->canSync(true, date('Y-m-d H:i:s'));
$dbConfig = parse_ini_file($this->dbEnvFile, true);
try {
// 创建连接
$conn = new PDO("mysql:host={$dbConfig['database']['hostname']};dbname={$dbConfig['database']['database']}",
$dbConfig['database']['username'],
$dbConfig['database']['password']);
// 设置 PDO 错误模式为异常
$conn->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$list = $conn->query("SELECT id,moive_file FROM fa_exam_course_movie where mins=0");
$res['count'] = count($list->rowCount() > 0 ? $list : []);
foreach ($list as $row) {
$t = time();
$sec = $this->getVideoMins($this->parseVideoDir . $row['moive_file']);
// SQL 更新语句
$sql = "update fa_exam_course_movie set mins=:sec,updatetime=:t where id=:id";
// 预处理 SQL 语句
$stmt = $conn->prepare($sql);
// 绑定参数
$stmt->bindParam(':sec', $sec);
$stmt->bindParam(':t', $t);
$stmt->bindParam(':id', $row['id']);
// 执行更新操作
$stmt->execute();
$res['list'][] = [
'id' => $row['id'],
'moive_file' => $row['moive_file'],
'length' => $sec,
'status' => $stmt->rowCount(),
];
}
} catch (PDOException $e) {
throw new Exception($e->getMessage());
}
// 关闭连接
$conn = null;
$this->canSync(true, 0);
return $res;
}
}
try {
$data = (new SyncVideo())->sync();
echo date('Y-m-d H:i:s'), " count: ", $data['count'], PHP_EOL;
foreach ($data['list'] as $v) {
echo $v['id'], "\t", $v['moive_file'], "\t", $v['length'], "\t", $v['status'], PHP_EOL;
}
} catch (Exception $e) {
echo $e;
}
// 程序正常退出
exit(0);
<?php
/**
* 同步视频长度
*/
class SyncVideo
{
/** @var string 锁文件路径 */
private $lockFile;
/** @var string env 配置文件路径 */
private $dbEnvFile;
/** @var string env 待解析视频目录 */
private $parseVideoDir;
function __construct()
{
$this->lockFile = "./sync.lock";
$this->dbEnvFile = "../kaoshi/.env";
$this->parseVideoDir = "../kaoshi/public";
}
public function canSync($write = false, $val = 0)
{
if ($write) {
return file_put_contents($this->lockFile, $val) ? true : false;
}
$data = file_get_contents($this->lockFile);
if ($data) {
return false;
} else {
return true;
}
}
public function getVideoMins($file)
{
$command = "ffmpeg -i " . $file . " 2>&1";
exec($command, $output);
// 从输出中提取视频信息
$line = implode('', $output);
// 提取视频时长
if (preg_match("/Duration: (.*?), /", $line, $matches)) {
$time = explode(':', $matches[1]);
return (intval($time[0]) * 60 * 60) + intval($time[1]) * 60 + intval($time[2]);
}
return 0;
}
/**
* @throws Exception
*/
public function sync()
{
$res = [
'count' => 0,
'list' => [],
];
if (!$this->canSync()) {
throw new Exception("Please hold on ...");
}
$this->canSync(true, date('Y-m-d H:i:s'));
$dbConfig = parse_ini_file($this->dbEnvFile, true);
try {
// 创建连接
$conn = new PDO("mysql:host={$dbConfig['database']['hostname']};dbname={$dbConfig['database']['database']}",
$dbConfig['database']['username'],
$dbConfig['database']['password']);
// 设置 PDO 错误模式为异常
$conn->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$list = $conn->query("SELECT id,moive_file FROM fa_exam_course_movie where mins=0");
$res['count'] = count($list->rowCount() > 0 ? $list : []);
foreach ($list as $row) {
$t = time();
$sec = $this->getVideoMins($this->parseVideoDir . $row['moive_file']);
// SQL 更新语句
$sql = "update fa_exam_course_movie set mins=:sec,updatetime=:t where id=:id";
// 预处理 SQL 语句
$stmt = $conn->prepare($sql);
// 绑定参数
$stmt->bindParam(':sec', $sec);
$stmt->bindParam(':t', $t);
$stmt->bindParam(':id', $row['id']);
// 执行更新操作
$stmt->execute();
$res['list'][] = [
'id' => $row['id'],
'moive_file' => $row['moive_file'],
'length' => $sec,
'status' => $stmt->rowCount(),
];
}
} catch (PDOException $e) {
throw new Exception($e->getMessage());
}
// 关闭连接
$conn = null;
$this->canSync(true, 0);
return $res;
}
}
try {
$data = (new SyncVideo())->sync();
echo date('Y-m-d H:i:s'), " count: ", $data['count'], PHP_EOL;
foreach ($data['list'] as $v) {
echo $v['id'], "\t", $v['moive_file'], "\t", $v['length'], "\t", $v['status'], PHP_EOL;
}
} catch (Exception $e) {
echo $e;
}
// 程序正常退出
exit(0);
5、增加计划任务
任务类型为,访问 URL,每1分钟访问一次,地址为 http://127.0.0.2/SyncVideo.php。
这样就可以了,虽然不是最优方法,先实现再说,时间紧张。