春江暮客

春江暮客的个人学习分享网站

使用Python下载m3u8流媒体并嵌入到AMP网页中

2023-08-01 技术
使用Python下载m3u8流媒体并嵌入到AMP网页中

在短视频和直播场景里,很多视频不是直接给你一个 mp4 文件,而是以 m3u8 播放列表加一组 .ts 分片的形式提供。如果你想做本地下载、静态归档,或者把视频转换后嵌入自己的页面,就需要自己处理这条链路。

这篇文章聚焦两件实际会遇到的事:

  1. 用 Python 下载 m3u8 对应的 TS 分片
  2. 把分片合并成 MP4,再嵌入 AMP 页面播放

如果你需要的是“浏览器里直接下载并转成 MP4”的方案,文末我也放了更新版本的延伸阅读。

0. 环境准备

先安装示例里用到的依赖:

pip install m3u8 requests

如果你后面要把 TS 分片合并成 MP4,还需要准备 ffmpeg

1. 什么是m3u8?

M3U8格式基于HTTP Live Streaming (HLS)技术,可以对整个视频进行分割,形成一系列小的文件,然后创建一个索引文件(.m3u8),通过该索引文件可以加载流媒体。这意味着视频播放时,客户端只需要加载索引文件,之后根据索引文件逐个下载小的视频文件进行播放,从而支持了视频边下边播的功能。

2. 根据m3u8文件下载所有视频文件

我们首先需要获取到m3u8文件,并解析出文件中视频片段(.TS文件)的URL。为了处理网络请求,我们需要使用Python的requests库。以下是一段代码:

import m3u8
import requests
import os

def check_path(path):
    if not os.path.exists(path):
        os.makedirs(path)

headers = {
    "User-Agent": "Mozilla/5.0 (X11; Linux x86_64; rv:102.0) Gecko/20100101 Firefox/102.0"
}

domain = 'www.bobobk.com'

def download_m3u8(m3u8url):
    print(m3u8url)
    playlist_filename = os.path.join(*m3u8url.split('/')[3:])
    if os.path.exists(playlist_filename):
        pass
    check_path(os.path.dirname(playlist_filename))

    host = m3u8url.split("://")[1].split('/')[0]
    baseurl, m3u8url = m3u8url.split("://")
    if not os.path.exists(playlist_filename):
        text = requests.get(m3u8url, headers=headers).text
        open(playlist_filename, 'w').write(text.replace(host, domain))

    m3u8_obj = m3u8.load(playlist_filename)
    print(f"playlist:{playlist_filename}")
    baseurl += "://" + m3u8url.split("/")[0]
    print("start check playlist")
    if len(m3u8_obj.playlists) != 0:
        m3u8url = m3u8_obj.playlists[0].uri
        if '://' not in m3u8url:
            if m3u8url[0] == '/':
                m3u8url = baseurl + m3u8_obj.playlists[0].uri
            else:
                m3u8url = baseurl + '/' + os.path.dirname(playlist_filename)+ '/' + m3u8_obj.playlists[0].uri
        print(m3u8url)
        text = requests.get(m3u8url, headers=headers).text
        playlist_filename = os.path.join(*m3u8url.split('/')[3:])
        check_path(os.path.dirname(playlist_filename))
        open(playlist_filename, 'w').write(text.replace(host, domain))
        m3u8_obj = m3u8.load(playlist_filename)
        baseurl, m3u8url = m3u8url.split("://")
        host = m3u8url.split('/')[0]
        print(f"playlist:{playlist_filename}")

    output = os.path.join(*m3u8_obj.segments[0].absolute_uri.split('/')[3:])
    check_path(os.path.dirname(output))
    print("check subdir")
    for segment in m3u8_obj.segments:
        segment_url = segment.absolute_uri
        segment_file_name = os.path.join(*segment_url.split('/')[3:])
        r = requests.get(segment_url, headers=headers, stream=True)
        print(f"{segment_url}\t{segment_file_name}")
        if os.path.exists(segment_file_name):
            continue

        with open(segment_file_name, 'wb') as f:
            for chunk in r.iter_content(chunk_size=1024):
                if chunk:
                    f.write(chunk)


m3u8url = "https://pptv.sd-play.com/202308/01/PfdfHWiJa43/video/index.m3u8"
download_m3u8(m3u8url)

这段脚本处理了一个比较实用的细节:如果遇到 master playlist,会继续向下解析到具体的 media playlist,再逐个下载 .ts 分片。

3. 将m3u8视频嵌入到amp网页

AMP(Accelerated Mobile Pages)网页是一种专为移动设备优化的开源框架,利用该框架,可以创建出快速,美观的网页应用。它用标准化但是灵活的方式,规定了HTML的使用,使得开发响应式网页变得更加简单。

直接将m3u8视频嵌入到AMP中,存在一个问题:目前,AMP并不支持直接播放m3u8文件。因此,我们需要将其转换为MP4格式,再将其嵌入到AMP网页中。

我们可以使用ffmpeg工具来合并.ts文件,并转换为.mp4文件。以下是代码:

for tsfile in "202308/01/PfdfHWiJa43/video/900k_0X480_64k_25/hls/"*.ts;do
echo "file '${tsfile}'" >> tsfile.txt
done

ffmpeg -f concat -safe 0 -i tsfile.txt -c copy 202308/01/PfdfHWiJa43/video/900k_0X480_64k_25/hls/202308.mp4

然后,我们可以在AMP网页中使用amp-video组件来播放这个mp4视频: https://www.bobobk.com/m3u8.html

<!doctype html> 
<html amp> 
<head> 
<meta charset="utf-8"> 
<script async src="https://cdn.ampproject.org/v0.js"></script> 
<title>使用Python下载m3u8流媒体并嵌入到AMP网页中(amp video page)</title> 
<link rel="canonical" href="self.html"> 
<meta name="viewport" content="width=device-width,minimum-scale=1,initial-scale=1"> 
<style amp-boilerplate>body{-webkit-animation:-amp-start 8s steps(1,end) 0s 1 normal both;-moz-animation:-amp-start 8s steps(1,end) 0s 1 normal both;-ms-animation:-amp-start 8s steps(1,end) 0s 1 normal both;animation:-amp-start 8s steps(1,end) 0s 1 normal both}@-webkit-keyframes -amp-start{from{visibility:hidden}to{visibility:visible}}@-moz-keyframes -amp-start{from{visibility:hidden}to{visibility:visible}}@-ms-keyframes -amp-start{from{visibility:hidden}to{visibility:visible}}@-o-keyframes -amp-start{from{visibility:hidden}to{visibility:visible}}@keyframes -amp-start{from{visibility:hidden}to{visibility:visible}}</style>
<noscript><style amp-boilerplate>body{-webkit-animation:none;-moz-animation:none;-ms-animation:none;animation:none}</style></noscript> 
<script async custom-element="amp-video" src="https://cdn.ampproject.org/v0/amp-video-0.1.js"></script> 
</head> 
<body> 
<amp-video controls width="640" height="360" layout="responsive" > <source src="/202308/01/PfdfHWiJa43/video/900k_0X480_64k_25/hls/202308.mp4" type="video/mp4"> </amp-video> 
</body> 
</html>

4. 如何验证下载和嵌入是否成功

建议至少检查下面 3 件事:

  1. 目标目录下是否已经完整生成 .ts 分片文件
  2. ffmpeg 合并后的 MP4 是否可以正常播放
  3. AMP 页面里的视频是否能在移动端正常加载

最直接的方式是先检查分片数量,再检查最终输出:

find 202308/01/PfdfHWiJa43/video/ -name '*.ts' | wc -l
ffmpeg -i 202308/01/PfdfHWiJa43/video/900k_0X480_64k_25/hls/202308.mp4

5. 常见问题与修复

1. 下载到一半失败

通常是源站限流、链接失效,或者部分分片不可访问。

修复:

  1. 补充更稳定的请求头
  2. 重新拉取最新 m3u8 地址
  3. 跳过已存在分片后继续下载

2. ffmpeg 合并失败

通常是 tsfile.txt 列表不完整,或分片顺序不对。

修复:

  1. 确认每个 .ts 文件都存在
  2. 重新生成 tsfile.txt
  3. 再执行一次 ffmpeg -f concat

3. AMP 页面里无法直接播放 m3u8

这不是脚本问题,而是 AMP 本身不直接支持 m3u8 播放。

修复:

  • 先转换成 MP4
  • 再通过 amp-video 嵌入

6. 延伸阅读

7. 总结

本文详细介绍了使用Python下载m3u8流媒体并嵌入到amp网页中的整个过程,希望对你有所帮助。在实际应用中,请确保你完全遵循了使用流媒体服务的协议,并尊重相关的版权法律。

友情链接

其它