如何使用PHP批量修改m3u8文件中的URL(兼容PHP 5版本)
如何使用PHP批量修改m3u8文件中的URL(兼容PHP 5版本)
我理解你的需求:要批量处理m3u8文件,给非HTTP(S)开头的媒体路径拼接基础URL,URL编码后套上proxy.php?前缀,同时要兼容PHP 5.x版本,还要覆盖EXTINF行和媒体路径同行/异行的两种常见m3u8格式。下面给你分场景提供解决方案:
核心思路分析
m3u8的媒体文件路径有两种常见写法:
- 和
#EXTINF同行:#EXTINF:4.004, radio002live-15084.ts - 单独占一行:
#EXTINF:4.004, radio002live-15084.ts
我们需要用正则或逐行遍历的方式匹配到所有媒体路径,判断是否为HTTP(S)绝对URL:
- 若不是:拼接基础URL生成完整绝对URL
- 若是:直接使用原URL
然后对完整URL做rawurlencode编码(比urlencode更适合URL整体编码),最后拼接proxy.php?前缀替换原路径。
方案1:正则批量替换(PHP 5.3+ 推荐)
PHP 5.3及以上支持preg_replace_callback,这个方法更安全且代码可读性高,适合大多数场景:
<?php // 1. 读取m3u8文件内容 $m3u8Content = file_get_contents('input.m3u8'); if ($m3u8Content === false) { die('无法读取m3u8文件'); } // 2. 配置参数 $baseUrl = 'http://someurl.tld/path/'; // 你的基础URL $proxyPrefix = 'proxy.php?'; // 你的代理前缀 // 3. 定义处理媒体路径的回调函数 $processCallback = function($matches) use ($baseUrl, $proxyPrefix) { $extinfPart = $matches[1]; $mediaPath = trim($matches[2]); // 空路径直接返回原内容 if (empty($mediaPath)) { return $matches[0]; } // 判断是否已经是HTTP(S)绝对URL if (stripos($mediaPath, 'http://') === 0 || stripos($mediaPath, 'https://') === 0) { $fullUrl = $mediaPath; } else { // 拼接基础URL,自动处理首尾斜杠避免重复 $fullUrl = rtrim($baseUrl, '/') . '/' . ltrim($mediaPath, '/'); } // URL编码(用rawurlencode符合RFC3986标准,适配所有URL字符) $encodedUrl = rawurlencode($fullUrl); // 拼接代理前缀并返回 return rtrim($extinfPart) . ' ' . $proxyPrefix . $encodedUrl; }; // 4. 正则匹配并替换 // 正则说明:匹配EXTINF行+同行/下一行的媒体路径,兼容行尾空白、下一行是标签的情况 $pattern = '/(#EXTINF:\d+(?:\.\d+)?,)(?:\s*)(.*?)(?:\s*$|\s*#)/ms'; $newM3u8Content = preg_replace_callback($pattern, $processCallback, $m3u8Content); // 5. 保存处理后的内容 if (file_put_contents('output.m3u8', $newM3u8Content) === false) { die('无法写入处理后的m3u8文件'); } echo 'm3u8文件处理完成!'; ?>
代码解释
- 正则
/(#EXTINF:\d+(?:\.\d+)?,)(?:\s*)(.*?)(?:\s*$|\s*#)/ms:#EXTINF:\d+(?:\.\d+)?,:匹配带整数/小数时长的EXTINF前缀(?:\s*):匹配逗号后的空白字符(兼容有无空格的情况)(.*?):非贪婪匹配媒体路径(兼容同行/异行)(?:\s*$|\s*#):匹配路径的结束位置(行尾或下一个m3u8标签)
rawurlencode:严格按照URL编码标准处理,比如把://编码为%3A%2F%2F,完全符合你需要的格式use ($baseUrl, $proxyPrefix):在回调函数中引入外部变量(PHP 5.3闭包特性)
方案2:PHP 5.2 兼容方案
如果你的环境是PHP 5.2(不支持闭包和preg_replace_callback),可以用preg_replace的/e修饰符实现(注意:/e在PHP5.5+被废弃,仅适合PHP5.2场景):
<?php // 1. 定义处理函数(必须全局或在当前作用域可访问) function processMediaPath($mediaPath, $baseUrl, $proxyPrefix) { $mediaPath = trim($mediaPath); if (empty($mediaPath)) { return ''; } if (stripos($mediaPath, 'http://') === 0 || stripos($mediaPath, 'https://') === 0) { $fullUrl = $mediaPath; } else { $fullUrl = rtrim($baseUrl, '/') . '/' . ltrim($mediaPath, '/'); } $encodedUrl = rawurlencode($fullUrl); return $proxyPrefix . $encodedUrl; } // 2. 读取文件 $m3u8Content = file_get_contents('input.m3u8'); if ($m3u8Content === false) { die('无法读取m3u8文件'); } // 3. 配置参数 $baseUrl = 'http://someurl.tld/path/'; $proxyPrefix = 'proxy.php?'; // 4. 转义参数避免引号冲突 $escapedBaseUrl = addslashes($baseUrl); $escapedProxyPrefix = addslashes($proxyPrefix); // 5. 正则替换(使用/e修饰符执行代码) $pattern = '/(#EXTINF:\d+(?:\.\d+)?,)(?:\s*)(.*?)(?:\s*$|\s*#)/ms'; $newM3u8Content = preg_replace($pattern, "\"$1 \" . processMediaPath(\"$2\", \"$escapedBaseUrl\", \"$escapedProxyPrefix\")", $m3u8Content); // 6. 保存文件 if (file_put_contents('output.m3u8', $newM3u8Content) === false) { die('无法写入处理后的m3u8文件'); } echo 'm3u8文件处理完成!'; ?>
方案3:大文件逐行处理(PHP 5.x 全兼容)
如果你的m3u8文件特别大(比如几百MB),一次性读取到内存会导致性能问题,可以用逐行遍历的方式处理,内存占用更低:
<?php function processMediaPath($mediaPath, $baseUrl, $proxyPrefix) { $mediaPath = trim($mediaPath); if (empty($mediaPath)) { return ''; } if (stripos($mediaPath, 'http://') === 0 || stripos($mediaPath, 'https://') === 0) { $fullUrl = $mediaPath; } else { $fullUrl = rtrim($baseUrl, '/') . '/' . ltrim($mediaPath, '/'); } return $proxyPrefix . rawurlencode($fullUrl); } // 1. 打开文件句柄 $inputHandle = fopen('input.m3u8', 'r'); $outputHandle = fopen('output.m3u8', 'w'); if (!$inputHandle || !$outputHandle) { die('无法打开文件'); } // 2. 配置参数 $baseUrl = 'http://someurl.tld/path/'; $proxyPrefix = 'proxy.php?'; $isExtinfLine = false; // 标记上一行是否是EXTINF行 // 3. 逐行读取处理 while (($line = fgets($inputHandle)) !== false) { $trimmedLine = trim($line); // 匹配EXTINF行 if (strpos($trimmedLine, '#EXTINF:') === 0) { $isExtinfLine = true; $commaPos = strrpos($trimmedLine, ','); if ($commaPos !== false) { $extinfPart = substr($line, 0, $commaPos + 1); $mediaPath = trim(substr($line, $commaPos + 1)); if (!empty($mediaPath)) { // 同行有路径,直接处理替换 $processedPath = processMediaPath($mediaPath, $baseUrl, $proxyPrefix); fwrite($outputHandle, rtrim($extinfPart) . ' ' . $processedPath . "\n"); $isExtinfLine = false; } else { // 只有EXTINF前缀,下一行是路径 fwrite($outputHandle, $line); } } else { // 异常格式,直接写入 fwrite($outputHandle, $line); $isExtinfLine = false; } } elseif ($isExtinfLine && !empty($trimmedLine)) { // 上一行是EXTINF行,当前行是媒体路径 $processedPath = processMediaPath($trimmedLine, $baseUrl, $proxyPrefix); fwrite($outputHandle, $processedPath . "\n"); $isExtinfLine = false; } else { // 其他行直接写入 fwrite($outputHandle, $line); $isExtinfLine = false; } } // 4. 关闭文件句柄 fclose($inputHandle); fclose($outputHandle); echo 'm3u8文件处理完成!'; ?>
注意事项
- 权限问题:确保PHP有读取原m3u8文件和写入新文件的权限
- 格式兼容性:测试不同格式的m3u8文件,比如带注释、不同时长格式的EXTINF行
- URL编码:必须用
rawurlencode而不是urlencode,前者会严格编码所有特殊字符,符合URL规范 - 基础URL拼接:用
rtrim($baseUrl, '/') . '/' . ltrim($mediaPath, '/')避免出现//的情况
内容来源于stack exchange
相关产品推荐
相关产品推荐

