您当前的位置: 首页 > 慢生活 > 程序人生 网站首页程序人生
m3u8下载器php版,新增支持key解密
发布时间:2026-07-07 02:52:47编辑:雪饮阅读()
-
m3u8下载器php版,新增支持ts单独目录及key加载与解码优化为视频完整性优先(防崩溃)
解决了那个key密钥没有解密成功问题以及编码拷贝在某些滤镜下直接崩溃问题,以及ts单独目录,不和主下载目录混用

完整源代码:
<?php
/*
*命令参考
* -m:m3u8地址
* -o:输出路径
* D:\phpstudy_pro\Extensions\php\php7.4.3nts\php.exe -c D:\phpstudy_pro\Extensions\php\php7.4.3nts\php.ini C:\Users\Administrator\PhpstormProjects\untitled\m3u8_download.php -m https://hot.qoqkkhy.com/20230720/V4Docige/1000kb/hls/index.m3u8 -o C:\Users\Administrator\PhpstormProjects\untitled\ChangAn.mp4
*
*
* 参考
* ffmpeg -threads 0 -i "https://hot.qoqkkhy.com/20230720/V4Docige/1000kb/hls/index.m3u8" -c copy -y "C:\Users\Administrator\PhpstormProjects\untitled\ChangAn.mp4"
* */
$ffmpegPath='D:\software\ffmpeg-master-latest-win64-gpl\ffmpeg-master-latest-win64-gpl\bin\ffmpeg.exe';
print_r($argv);
$m=null;
$o=null;
if($argv[1]=="-m"){
$m=$argv[2];
}
if($argv[3]=="-o"){
$o=$argv[4];
}
if(!$m){
exit();
}
if(!$o){
exit();
}
//取得m3u8资源根链接
$result22=parse_url($m);
$m3u8_root=$result22["scheme"]."://".$result22["host"];
$path = $result22['path'];
$m3u8_path = dirname($path) . '/';
print_r("m3u8_path:".$m3u8_path.PHP_EOL);
//创建当前任务目录
print_r("开始创建当前任务目录\r\n");
$dir="m3u8_download/".md5($m);
if(!is_dir($dir)){
$flag21=mkdir($dir,0777,true);
if($flag21){
print_r("当前任务目录创建成功\r\n");
}
}
else{
print_r("当前任务目录已存在,无需创建\r\n");
}
//下载m3u8文件
print_r("开始下载m3u8原始文件\r\n");
$m3u8_original_file=$dir."/original.m3u8";
$m3u8_original_file_exist=file_exists($m3u8_original_file);
if ($m3u8_original_file_exist){
print_r("m3u8原始文件已存在,无须下载\r\n");
$m3u8_content=file_get_contents($m3u8_original_file);
}
if (!$m3u8_original_file_exist){
try{
$m3u8_content=file_get_contents($m);
if(!$m3u8_content){
print_r("下载m3u8原始文件失败(62)".PHP_EOL);
exit();
}
file_put_contents($m3u8_original_file,$m3u8_content);
}
catch(\Exception $e){
print_r("下载m3u8原始文件失败(96)\r\n");
exit();
}
}
//提取key(如果有)
/*
* key信息示例:
* #EXT-X-KEY:METHOD=AES-128,URI="key.key",IV=0x64376662636236306634353834373861
* */
$m3u8_original_file_handle = fopen($m3u8_original_file,"r");
$keyUri = ""; // 原始key文件的URI
$keyLocalFile = ""; // 本地key文件路径
$keyIV = ""; // IV值
$keyMethod = ""; // 加密方式
while(!feof($m3u8_original_file_handle))
{
$line = fgets($m3u8_original_file_handle);
$line = trim($line);
if(strpos($line, '#EXT-X-KEY:') === 0){
print_r("检测到加密信息: ".$line."\r\n");
// 提取METHOD
if(preg_match('/METHOD=([^,]+)/', $line, $matches)){
$keyMethod = $matches[1];
print_r("加密方式: ".$keyMethod."\r\n");
}
// 提取URI
if(preg_match('/URI="([^"]+)"/', $line, $matches)){
$keyUri = $matches[1];
print_r("key URI: ".$keyUri."\r\n");
}
// 提取IV
if(preg_match('/IV=([^,]+)/', $line, $matches)){
$keyIV = trim($matches[1]);
print_r("IV: ".$keyIV."\r\n");
}
// 下载key文件
if($keyUri){
// 拼接key的完整URL
$keyUrl = $keyUri;
$parsedKey = parse_url($keyUri);
if(!isset($parsedKey["scheme"])){
// 相对路径,拼接m3u8所在目录
if(strpos($keyUri, '/') === 0){
$keyUrl = $m3u8_root . $keyUri;
} else {
$keyUrl = $m3u8_root . $m3u8_path . $keyUri;
}
}
print_r("开始下载key文件: ".$keyUrl."\r\n");
$keyLocalFile = $dir . "/key.key";
if (file_exists($keyLocalFile)){
print_r("key文件已下载,无须下载\r\n");
}
else{
try{
$keyData = file_get_contents($keyUrl);
if(!$keyData){
print_r("下载key文件失败\r\n");
exit();
}
file_put_contents($keyLocalFile, $keyData);
print_r("key文件下载成功,长度: ".strlen($keyData)." 字节\r\n");
}
catch (\Exception $e){
print_r("下载key文件失败(97) 错误信息:".$e->getMessage()."\r\n");
exit();
}
}
}
}
}
fclose($m3u8_original_file_handle);
//提取ts链接集合
print_r("开始提取ts链接集合\r\n");
$ts_list=[];
$m3u8_original_file_handle = fopen($m3u8_original_file,"r");
$tsSpecifyDir="";
while(!feof($m3u8_original_file_handle))
{
$pattern="/.ts$/";
$line=fgets($m3u8_original_file_handle);
//可能包含有换行符,这里需要做换行处理
$line=trim($line);
//ts链接包含目录
if(strpos($line,"/")!==false){
}
//ts链接不包含目录
if(strpos($line,"/")===false){
//$line=$m3u8_path.$line;
}
if(preg_match($pattern,$line)){
array_push($ts_list,$line);
}
}
fclose($m3u8_original_file_handle);
print_r("提取到的ts链接集合\r\n");
print_r($ts_list);
//开始创建当前任务ts目录
print_r("开始创建当前任务ts目录\r\n");
$dir_ts=$dir."/ts";
if(!is_dir($dir_ts)){
$flag21=mkdir($dir_ts,0777,true);
if($flag21){
print_r("当前任务ts目录创建成功\r\n");
}
}
else{
print_r("当前任务ts目录已存在,无需创建\r\n");
}
//开始下载ts文件
$ts_complete_count=0;
foreach($ts_list as $val){
$md576=md5($val);
$file77=$dir_ts."/".$md576.".ts";
$c_ts=$m3u8_root.$val;
//ts链接不包含目录
if(strpos($val,"/")===false){
// $line=$m3u8_path.$line;
$c_ts=$m3u8_root.$m3u8_path.$val;
}
//需要判断m3u8中ts文件是否使用的绝对路径
$result103=parse_url($val);
if(isset($result103["scheme"])){
$c_ts=$val;
}
print_r("正在下载ts:".$c_ts."\r\n");
//进度
$rate_of_progress=$ts_complete_count/count($ts_list)*100;
print_r("-------------ts下载进度:".$rate_of_progress."%-------------------\r\n");
if (file_exists($file77)){
$ts_complete_count++;
print_r($c_ts."文件已下载(已下载文件路径:".$file77."),无须下载".PHP_EOL);
}
else{
//如果file_get_contents失败,可以通过curl去排查详细错误
$c_ts_content=file_get_contents($c_ts);
if(!$c_ts_content){
print_r("下载ts:".$c_ts."失败(95)\r\n");
exit();
}
$flag85=file_put_contents($file77,$c_ts_content);
if(!$flag85){
$ts_complete_count++;
print_r("下载ts:".$c_ts."失败(101)\r\n");
exit();
}
$ts_complete_count++;
print_r("下载ts:".$c_ts."成功\r\n");
}
}
if($ts_complete_count!=count($ts_list)){
print_r("当前任务ts下载不完整,可重新尝试\r\n");
exit();
}
print_r("当前任务所有ts下载完成\r\n");
//开始替换生成新的本地m3u8文件
print_r("开始替换生成新的本地m3u8文件\r\n");
$m3u8_new_file=$dir."/new.m3u8";
if (file_exists($m3u8_new_file)){
print_r("新m3u8本地文件已存在,无须生成\r\n");
}
else{
$m3u8_content_new=$m3u8_content;
// 替换key URI为本地路径
if($keyUri && $keyLocalFile){
// $m3u8_content_new=str_replace($keyUri, "key.bin", $m3u8_content_new);
}
$minTsFileSizeKb=-1;
$minTsFileFullPath="";
$minTsFileOriginPath="";
foreach($ts_list as $val){
$md576=md5($val);
$file77="ts/".$md576.".ts";
print_r("正在替换ts:".$val."为:".$file77.PHP_EOL);
$file77FullPath=$dir."/".$file77;
if (file_exists($file77FullPath) && is_file($file77FullPath)) {
// filesize 返回字节 B
$byte = filesize($file77FullPath);
// 转为 KB 除以 1024,保留2位小数
$kb = number_format($byte / 1024, 2);
if($minTsFileSizeKb==-1){
$minTsFileSizeKb=$kb;
$minTsFileFullPath=$file77FullPath;
$minTsFileOriginPath=$val;
}
if($kb!=-1){
if($kb<$minTsFileSizeKb){
$minTsFileSizeKb=$kb;
$minTsFileFullPath=$file77FullPath;
$minTsFileOriginPath=$val;
}
}
// echo "文件大小:{$kb} KB";
} else {
print_r("替换目标ts文件不存在或不是有效文件".PHP_EOL);
exit();
}
$m3u8_content_new=str_replace($val,$file77,$m3u8_content_new);
}
print_r("最小ts文件的大小:".$minTsFileSizeKb."kb".PHP_EOL);
print_r("最小ts文件的完整路径:".$minTsFileFullPath.PHP_EOL);
print_r("最小ts文件的原路径:".$minTsFileOriginPath.PHP_EOL);
file_put_contents($m3u8_new_file,$m3u8_content_new);
print_r("新m3u8本地文件生成成功\r\n");
}
/*
* -allowed_extensions
* 放行所有后缀,包含.key
* */
/*
* - -c:v copy — 视频流直接拷贝,不重新编码,速度很快
* 用 -c copy 时, aac_adtstoasc 滤镜遇到损坏的 AAC 帧直接崩溃退出
- -c:a aac — 音频流重新编码为 AAC,遇到损坏帧会自动跳过,不会崩溃
*
* */
$command=$ffmpegPath.' -threads 0 -allowed_extensions ALL -i "'.realpath($m3u8_new_file).'" -c:a aac -y "'.$o.'"';
print_r("本地合并ts:".$command."\r\n");
$output = exec($command);
echo $output;
ffmpeg版本支持:
C:\Users\Administrator>D:\software\ffmpeg-master-latest-win64-gpl\ffmpeg-master-latest-win64-gpl\bin\ffmpeg.exe -version
ffmpeg version N-125472-g97cbffe917-20260705 Copyright (c) 2000-2026 the FFmpeg developers
built with gcc 15.2.0 (crosstool-NG 1.28.0.23_185f348)
configuration: --prefix=/ffbuild/prefix --pkg-config-flags=--static --pkg-config=pkg-config --cross-prefix=x86_64-w64-mingw32- --arch=x86_64 --target-os=mingw32 --enable-gpl --enable-version3 --disable-debug --disable-w32threads --enable-pthreads --enable-iconv --enable-zlib --enable-libxml2 --enable-libvmaf --enable-fontconfig --enable-libharfbuzz --enable-libfreetype --enable-libfribidi --enable-vulkan --enable-libshaderc --enable-libvorbis --disable-libxcb --disable-xlib --disable-libpulse --enable-gmp --enable-lzma --enable-liblcevc-dec --enable-opencl --enable-amf --enable-libaom --enable-libaribb24 --enable-avisynth --enable-chromaprint --enable-libdav1d --enable-libdavs2 --enable-libdvdread --enable-libdvdnav --disable-libfdk-aac --enable-ffnvcodec --enable-cuda-llvm --enable-frei0r --enable-libgme --enable-libkvazaar --enable-libaribcaption --enable-libass --enable-libbluray --enable-libjxl --enable-libmp3lame --enable-libopus --enable-libplacebo --enable-librist --enable-libssh --enable-libtheora --enable-libvpx --enable-libwebp --enable-libzmq --enable-lv2 --enable-libvpl --enable-openal --enable-liboapv --enable-libopencore-amrnb --enable-libopencore-amrwb --enable-libopenh264 --enable-libopenjpeg --enable-libopenmpt --enable-librav1e --enable-librubberband --enable-schannel --enable-sdl2 --enable-libsnappy --enable-libsoxr --enable-libsrt --enable-libsvtav1 --enable-libtwolame --enable-libuavs3d --disable-libdrm --enable-vaapi --enable-libvidstab --enable-libvvenc --disable-whisper --enable-libx264 --enable-libx265 --enable-libxavs2 --enable-libxvid --enable-libzimg --enable-libzvbi --extra-cflags=-DLIBTWOLAME_STATIC --extra-cxxflags= --extra-libs=-lgomp --extra-ldflags=-pthread --extra-ldexeflags= --cc=x86_64-w64-mingw32-gcc --cxx=x86_64-w64-mingw32-g++ --ar=x86_64-w64-mingw32-gcc-ar --ranlib=x86_64-w64-mingw32-gcc-ranlib --nm=x86_64-w64-mingw32-gcc-nm --extra-version=20260705
libavutil 61. 2.100 / 61. 2.100
libavcodec 63. 3.100 / 63. 3.100
libavformat 63. 3.100 / 63. 3.100
libavdevice 63. 2.100 / 63. 2.100
libavfilter 12. 2.100 / 12. 2.100
libswscale 10. 2.100 / 10. 2.100
libswresample 7. 2.100 / 7. 2.100
Exiting with exit code 0
源代码下载:
关键字词:m3u8,php,下载器,ts,key,解码,完整,崩溃,优化
上一篇:m3u8下载器php版(新增优化了支持ts在非根目录)
下一篇:返回列表
相关文章
- m3u8下载器php版(新增优化了支持ts在非根目录)
- 大气层系统如何提取prod.keys?
- javascript仿php的$_GET设置修改get地址栏参数(返回新
- 编写一个lib库修复的php脚本(用于glibc升级失败后对ff
- 在php中如何实现rtsp流转换成mp4文件并且异步多进程且
- ffmpeg转推rtsp为rtmp到nginx流媒体(多个推流拉流地址
- 使用ffmpeg将海康开放平台rtsp回放流转rtmp流及转存为
- 6.事件events(及value绑定、change监听处理)
- 11-添加&修改功能(insert、update标签、useGeneratedK
- 08-JDBC-API详解-PreparedStatement-原理(useServerPr