1. 起因 最近有个朋友给我发了个小电影。
地址是https://xxxxxx.m3u8, 当我使用微信直接打开的时候是可以播放的,但是使用edge打开的时候却直接跳转到了下载连接里,无奈,只能下载下来一个m3u8的文件。
这边先简单解释一下什么是m3u8的视频格式。
根据维基百科的解释,
M3U8 是 Unicode 版本的 M3U,用 UTF-8 编码。"M3U" 和 "M3U8" 文件都是苹果公司使用的 HTTP Live Streaming(HLS) 协议格式的基础,这种协议格式可以在 iPhone 和 Macbook 等设备播放。
我们使用Visual Studio Code或者其他编辑器打开m3u8文件,我们可以比较清楚地看到,其实就是一个utf-8编码的播放列表。
文章图片
我们使用播放器播放的时候,实际上是在加载这些地址的视频片段。当我们直接访问这些链接的时候也是可以访问的。但是这样看起来依旧不太舒服,我们已经习惯了看一整段MP4的视频,并且还想把它存到本地自己搭建的NAS里,这样就可以长期播放了不是嘛。
2. 经过 【爬虫|使用Python下载本地的m3u8文件】为了方便,我先是下载了一些下载站里的m3u8下载器,但是免费的东西总是会有一定的局限的。这些下载工具虽然UI做的精美,但是尝试了几个之后发现根本不能用。其中有一个倒是成功把每个片段的文件下载下来了。然后再进行合并的时候,根本没有合并成功,却把之前下载的文件全删光了。
文章图片
要不,还是自己实现吧。
由于只是实现一个小功能,并没有什么性能上的要求,因此决定使用python快速解决。
突然,我在pypi上发现了一个神奇的库。
文章图片
import m3u8_to_mp4m3u8_to_mp4.download('http://videoserver.com/playlist.m3u8',tmpdir='/tmp/m3u8_xx')
根据提示,只需要两句话就能够把视频文件下载下来,并且使用ffmpeg(该包需要在电脑上先配置ffmpeg, 即执行指令"ffmpeg -version"有结果)将其打包成一个Mp4文件。
于是我兴致冲冲地用了一下,但是结果令人心凉。果然,轮子还是得自己造才行。
3. 造轮子 造轮子也不是从种树开始的,有方便的工具尽管可以使用,这边使用的Pypi里面的m3u8这个库,可以很快解析m3u8文件内容,同时还使用了pycryptodom这个库中的AES解密(实际上在本次案例中并没有用到)。
# encoding=utf-8
import m3u8
import requests
import datetime
import os
from Crypto.Cipher import AES
from Crypto import Random
import glob
# Request header, not necessary, see website change
headers = {
"User-Agent": "Mozilla/5.0 (Windows NT 10.0;
WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.198 Safari/537.36"
}def download(ts_urls, download_path, keys=[]):
if not os.path.exists(download_path):
os.mkdir(download_path)decrypt = True
if len(keys == 0) or keys[0] is None:# m3u8 will get [None] if not key or []
decrypt = Falsefor i in range(len(ts_urls)):
ts_url = ts_urls[i]
file_name = ts_url.uri
print("start download %s" %file_name)
start = datetime.datetime.now().replace(microsecond=0)
try:
response = requests.get(file_name, stream=True, verify=False)
except Exception as e:
print(e)
returnts_path = download_path+"/{0}.ts".format(i)
if decrypt:
key = keys[i]
iv = Random.new().read(AES.block_size)
cryptor = AES.new(key.encode('utf-8'), AES.MODE_CBC)with open(ts_path,"wb+") as file:
for chunk in response.iter_content(chunk_size=1024):
if chunk:
if decrypt:
file.write(cryptor.decrypt(chunk))
else:
file.write(chunk)end = datetime.datetime.now().replace(microsecond=0)
print("total time:%s"%(end-start))def merge_to_mp4(dest_file, source_path, delete=False):
with open(dest_file, 'wb') as fw:
files = glob.glob(source_path + '/*.ts')
for file in files:
with open(file, 'rb') as fr:
fw.write(fr.read())
print(f'\r{file} Merged! Total:{len(files)}', end="")
if delete:
os.remove(file)if __name__ == "__main__":
url = "test.m3u8"
video = m3u8.load(url)
print(video.data)
download(video.segments, 'tmp', video.keys)
merge_to_mp4('result.mp4', 'tmp')
我们将步骤分为两部分,(1)为下载ts文件, (2)为合并ts文件为mp4
(1)下载ts文件 下载ts文件相对简单。根据m3u8库解析出来的地址,我们直接进行request请求获得相应的response。 这时候需要注意的是,如果m3u8解析出来的文件中含有key,说明该文件是通过该key值进行AES加密的,需要对response进行解密后再保存下来,如果没有可以直接保存。
在m3u8库解析m3u8文件中,如果文件不带有key,那么获取到的keys=[None], 它的长度是1,所以不能通过直接判断keys的长度是否为0来决定需不需要解密。
(2)合并成Mp4文件 合并的步骤比较简单,就是将ts按顺序读取之后写到同一个文件里。这边需要注意的是,在保存ts文件时要按照一定的顺序保存,合并时也使用该顺序,否则就会有一种乱序插入的感觉。
推荐阅读
- 爬虫|关于blob加密视频的基于m3u8和ts文件下载转MP4视频的python爬虫实现
- python爬虫|python——视频爬虫(m3u8文件中.ts视频流的解密下载)
- 使用Python多进程教你下载M3U8加密或非加密视频!
- 经验分享|这是我见过最牛逼的接口自动化测试框架没有之一(基于python+requests+pytest+allure实现)
- 自动化测试|Python接口自动化测试框架(Pytest+Allure+jsonpath+xlrd+excel、支持Restful接口规范)
- 软件测试|python+Pytest+Allure+Git+Jenkins接口自动化框架
- python|接口自动化测试实战之pytest框架+allure讲解
- OAK使用教程|如何将OAK相机当做网络摄像头使用()
- OAK应用案例|【开源】使用PhenoCV-WeedCam进行更智能、更精确的杂草管理