学在西电录播课使用python下载,通过解析m3u8协议、多线程下载ts视频块以及ffmpeg合并
本文涵盖的内容仅供个人学习使用,如果侵犯学校权利,麻烦联系我删除。 因为懒得用二进制安装ffmpeg,所以用的Ubuntu22.04。年轻的本科生windows选手们可以自行学习二进制安装ffmpeg。 关于网站部分,本文写于2024-12-05,不保证后面会不会改 学在西电就是在学习通上再加了一层,加了点新东西。录播在这个地方看。(本文默认已经登录成功) 在学在西电里,视频文件是被切分为许多个几秒的视频块(ts文件,是Transport Stream不是Typescript),通过一个m3u8协议文件保存对应视频的各个小视频块的文件名、序列号、持续时间等信息。 基于 执行后会生成一个download.log,不过这个文件没啥用。主要还是ts文件夹,上述例子就会产生一个名为 基于命令: 实测1分钟左右合并完成。 如果看不懂代码,可以逐步运行查看效果。 运行前记得改两个脚本里的链接和文件名 其实就是比较m3u8里的所有ts文件名和本地已下载成功的ts文件,看哪些还没写,保存到 注意由于下载链接已经包含鉴权信息,所以这个链接给谁都可以直接用,无需特定的会话。初衷
研究生必修选逃,期末复习怕漏过重点题目,但是看学在西电的录播回放课一卡一卡的,于是想在空余时间一个个下载下来,然后到时候就突击复习。环境
sudoaptinstallffmpegffmpeg -version# 要有python3,安装步骤略过...# pip依赖pip installaiohttppip installtqdmpip installm3u8
预备知识
下面的图就是录播播放界面,由于有学生姓名和学号的水印,我打码了。
左边是拍老师和黑板的录像,右边是展示ppt的录像。
为了捕获请求,我们先打开开发者面板的网络面板,点击下面的某堂课跳转,然后在页面刷新后获取到加载时的请求,通过关键词过滤m3u8
,得到重要的三个请求。
注意这里有两个playback.m3u8
,通过上面图中那个另外的请求playVideo?info=...
的响应,我们可以看到pptVideo
和teacherTrack
这两个路径,分别对应ppt和老师黑板的m3u8文件的url。{ "type":"2","videoPath":{ "pptVideo":"....m3u8","teacherTrack":"....m3u8","studentFull":"....m3u8"},"liveId":...,"isshowpl":0}
m3u8文件内容如下,还好学在西电这里没有做加密,没有#EXT-X-KEY:METHOD=AES-128,URI...
这么一行,所以我们可以用这些ts文件名直接下载(当然前面还要有http之类的前缀)。
最后,使用伟大牛逼的 ffmpeg 可以将这些ts文件合并为 mp4 文件。具体代码
1. 下载各ts
m3u8
库解析m3u8文件,aiohttp
做协程下载,tqdm
做进度条方便查看,最后记得threading加锁。
考虑到偶尔的下载异常,加了个2次重试。
url按照下图获取。
实测5分钟左右下完。# download.pyimportshutilimportthreadingimportm3u8importosimportloggingimportreimportasyncioimportaiohttpfromtqdm importtqdmpbar:tqdm =Nonepbar_lock =threading.Lock()asyncdefdownload_segment(session,ts_url,true_url,output_dir,cnt):globalpbar filename =os.path.join(output_dir,true_url)try:# 实际在这里下载asyncwithsession.get(ts_url)asresp:resp.raise_for_status()withopen(filename,'wb')asf:asyncforchunk inresp.content.iter_chunked(1024):ifchunk:f.write(chunk)logging.info(f"下载完成: { true_url}")withpbar_lock:pbar.update(1)exceptException ase:logging.error(f"第{ cnt}次下载失败: { true_url}, 错误信息: { e}")# 3次重试机会ifcnt ==3:logging.error(f"重试次数达到上限,跳过下载: { true_url}")# 把需要手动下的单独保存withopen(f'{ output_dir}.err','a',encoding='utf-8')asfile:file.write(ts_url +'\n')# 并且这个下了一半的ts文件需要删掉,防止弄混ifos.path.exists(filename):os.remove(filename)withpbar_lock:pbar.update(1)else:# 重试一下,且计数器+1awaitdownload_segment(session,ts_url,true_url,output_dir,cnt+1)asyncdefdownload_m3u8(m3u8_url,output_dir):globalpbar # 日志logging_file =f'{ output_dir}-download.log'err_file =f'{ output_dir}.err'ifos.path.exists(logging_file):os.remove(logging_file)ifos.path.exists(err_file):os.remove(err_file)ifos.path.exists(output_dir):shutil.rmtree(output_dir)logging.basicConfig(filename=logging_file,level=logging.INFO,format='%(asctime)s - %(levelname)s - %(message)s')# 创建输出目录os.mkdir(output_dir)# 下载并解析 m3u8 文件logging.info(f"开始解析 m3u8 文件: { m3u8_url}")m3u8_obj =m3u8.load(m3u8_url)# 提取 base URLbase_url =re.split(r"[a-zA-Z0-9-_\.]+\.m3u8",m3u8_url)[0]logging.info(f"提取到的base URL: { base_url}")# 创建 aiohttp sessionasyncwithaiohttp.ClientSession()assession:# 异步下载所有 ts 片段tasks =[]pbar =tqdm(total=len(m3u8_obj.segments))logging.info(f"segment 个数: { len(m3u8_obj.segments)}")for_,segment inenumerate(m3u8_obj.segments):# 真正ts的下载url是要拼起来的ts_url =base_url +segment.uri task =asyncio.create_task(download_segment(session,ts_url,segment.uri,output_dir,1))tasks.append(task)awaitasyncio.gather(*tasks)# 任务完成后关闭进度条pbar.close()logging.info(f"下载完成")m3u8_url ="http://.../playback.m3u8"output_dir ="4-2-1-ppt"asyncio.run(download_m3u8(m3u8_url,output_dir))
./4-2-1-ppt
的文件夹。2. 合并为mp4
ffmpeg -f concat -safe 0 -i ts_list.txt -c copy video.mp4
。注意这个-i
,如果只有少量文件,可以-i "concat:1.ts|2.ts|3.ts|4.ts|.5.ts|"
,但对于我们这种,就只能让他读取一个文件名列表文件,注意这个文件每行都是file+文件路径
。
我代码里首先获取了ts文件夹里的所有ts文件名,但是因为多线程所以乱序,要先排个序才能让ffmpeg按顺序拼接。# merge.pyimportosdefmain(dir_name):filename =f'{ dir_name}_ts_list.txt'ifos.path.exists(filename):os.remove(filename)f =open(filename,'a',encoding='utf-8')names =[]withos.scandir(dir_name)asentries:forentry inentries:# 检查是否为文件ifentry.is_file():names.append(entry.name)# 注意要先排序,按顺序写入文件名names.sort(key=lambdax:int(x.split('_')[0]))forname innames:f.write(f"file { os.path.join(dir_name,name)}\n")f.close()mp4_name =f"{ dir_name}.mp4"ifos.path.exists(mp4_name):os.remove(mp4_name)cmd =rf'ffmpeg -f concat -safe 0 -i ./{ filename}-c copy { mp4_name}'os.system(cmd)main("./4-2-1-ppt")
3. 执行
python download.pypython merge.py
其他:处理少数下载失败的ts
{ output_dir}-url.txt
下,通过点击链接手动下载并保存到ts文件夹中。importm3u8importosimportredefrecord(url,output_dir):withopen(f'{ output_dir}-url.txt','a',encoding='utf-8')asfile:file.write(url +'\n')deffind_err(m3u8_url,output_dir):m3u8_obj =m3u8.load(m3u8_url)# 提取 base URLbase_url =re.split(r"[a-zA-Z0-9-_\.]+\.m3u8",m3u8_url)[0]# 已下载的done =set()withos.scandir(output_dir)asentries:forentry inentries:ifentry.is_file():done.add(entry.name)all=set()for_,segment inenumerate(m3u8_obj.segments):all.add(segment.uri)not_done =all-done tmp =list(not_done)tmp.sort()fori intmp:record(base_url+i,output_dir)m3u8_url ="http://.../playback.m3u8"output_dir ="4-2-1-ppt"find_err(m3u8_url,output_dir)
其他
ffmpeg -i http://.../playback.m3u8 -c copy 2-4-1.mp4
,只是似乎是串行依次下载ts,速度不快。
-
上一篇
-
下一篇
- 最近发表
- 随机阅读
-
- 汽车公司抢先:吉利、零跑强推智驾平权,上汽大众、上汽通用主要“买车一口价”
- 弹幕射击游戏值得玩什么? 十大必玩弹幕射击游戏排名
- 关卡编辑游戏有什么好玩的? 下载量高的关卡编辑游戏精选
- 上海网信办等五个部门关于印发自由贸易试验区数据出境负面清单管理办法和负面清单(2024版)的通知
- python做手机app自动测试
- 带状卷轴动作游戏有哪些? 十大经典带状卷轴动作游戏排行榜
- 联想扬天M460商用办公桌式电脑主机单主机手价2099元
- OPPO Find N5真机出炉:折痕最浅 机身是世界上最薄的
- 算云技术完成了5000万元A轮融资
- 利用 Llama 2 13B Chat
- 基于Java Springboot 设计和实现MySQL餐厅网站和预订系统
- 张建成押注机器人赛道 爱柯迪收购卓尔博能破局吗?
- 核物理学家刘畅离开美国回国:在母校北京大学工作
- 电竞叛客AX RTX 5070显卡,12G大显存,DLSS 技术,只有5099元!
- 容声547L风冷对开门冰箱 京东活动价格低至1243元
- 2025年苹果首款新产品:M4 MacBook Air已经量产
- 苹果造车失败,但小米却取得了巨大的成功 外媒说出了背后的原因
- mongodb 和 hbase 的区别
- 价格崩盘!极摩客GMKK M6准系统迷你主机只需1288元
- 推荐哪个潜艇游戏? 十大经典潜水艇游戏盘点
- 搜索
-
- 友情链接
-