学在西电录播课使用python下载,通过解析m3u8协议、多线程下载ts视频块以及ffmpeg合并

news2025/1/16 6:31:27

本文涵盖的内容仅供个人学习使用,如果侵犯学校权利,麻烦联系我删除。

初衷

研究生必修选逃, 期末复习怕漏过重点题目,但是看学在西电的录播回放课一卡一卡的,于是想在空余时间一个个下载下来,然后到时候就突击复习。

环境

因为懒得用二进制安装ffmpeg,所以用的Ubuntu22.04。年轻的本科生windows选手们可以自行学习二进制安装ffmpeg。

sudo apt install ffmpeg
ffmpeg -version
# 要有python3,安装步骤略过
...
# pip依赖
pip install aiohttp
pip install tqdm
pip install m3u8

预备知识

关于网站部分,本文写于2024-12-05,不保证后面会不会改

学在西电就是在学习通上再加了一层,加了点新东西。录播在这个地方看。(本文默认已经登录成功)
在这里插入图片描述
下面的图就是录播播放界面,由于有学生姓名和学号的水印,我打码了。
左边是拍老师和黑板的录像,右边是展示ppt的录像。
在这里插入图片描述
为了捕获请求,我们先打开开发者面板的网络面板,点击下面的某堂课跳转,然后在页面刷新后获取到加载时的请求,通过关键词过滤m3u8,得到重要的三个请求。
在这里插入图片描述
注意这里有两个playback.m3u8,通过上面图中那个另外的请求playVideo?info=...的响应,我们可以看到pptVideoteacherTrack这两个路径,分别对应ppt和老师黑板的m3u8文件的url。

{
    "type": "2",
    "videoPath": {
        "pptVideo": "....m3u8",
        "teacherTrack": "....m3u8",
        "studentFull": "....m3u8"
    },
    "liveId": ...,
    "isshowpl": 0
}

在学在西电里,视频文件是被切分为许多个几秒的视频块(ts文件,是Transport Stream不是Typescript),通过一个m3u8协议文件保存对应视频的各个小视频块的文件名、序列号、持续时间等信息。
在这里插入图片描述
m3u8文件内容如下,还好学在西电这里没有做加密,没有#EXT-X-KEY:METHOD=AES-128,URI...这么一行,所以我们可以用这些ts文件名直接下载(当然前面还要有http之类的前缀)。
在这里插入图片描述
最后,使用伟大牛逼的 ffmpeg 可以将这些ts文件合并为 mp4 文件。
在这里插入图片描述
在这里插入图片描述

具体代码

1. 下载各ts

基于m3u8库解析m3u8文件,aiohttp做协程下载,tqdm做进度条方便查看,最后记得threading加锁。
考虑到偶尔的下载异常,加了个3次重试。
url按照下图获取。

在这里插入图片描述
实测5分钟左右下完。

# download.py
import shutil
import threading
import m3u8
import os
import logging
import re
import asyncio
import aiohttp
from tqdm import tqdm

pbar:tqdm = None
pbar_lock = threading.Lock()

async def download_segment(session, ts_url, true_url, output_dir, cnt):
    global pbar
    filename = os.path.join(output_dir, true_url)
    try:
    	# 实际在这里下载
        async with session.get(ts_url) as resp:
            resp.raise_for_status()
            with open(filename, 'wb') as f:
                async for chunk in resp.content.iter_chunked(1024):
                    if chunk:
                        f.write(chunk)
            logging.info(f"下载完成: {true_url}")
            with pbar_lock:
                pbar.update(1)
    except Exception as e:
        logging.error(f"下载失败: {true_url}, 错误信息: {e}")
        # 3次重试机会
        if cnt == 3:
            logging.error(f"重试次数达到上限,跳过下载: {true_url}")
            # 把需要手动下的单独保存
            with open(f'{output_dir}.err', 'a', encoding='utf-8') as file:
                file.write(ts_url + '\n')
            # 并且这个下了一半的ts文件需要删掉,防止弄混
            if os.path.exists(filename):
                os.remove(filename)
            with pbar_lock:
                pbar.update(1)
        else:
        	# 重试一下,且计数器+1
            await download_segment(session, ts_url, true_url, output_dir, cnt+1)

async def download_m3u8(m3u8_url, output_dir):
    global pbar
    # 日志
    logging_file = f'{output_dir}-download.log'
    err_file = f'{output_dir}.err'
    if os.path.exists(logging_file):
        os.remove(logging_file)
    if os.path.exists(err_file):
        os.remove(err_file)
    if os.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 URL
    base_url = re.split(r"[a-zA-Z0-9-_\.]+\.m3u8", m3u8_url)[0]
    logging.info(f"提取到的base URL: {base_url}")
     # 创建 aiohttp session
    async with aiohttp.ClientSession() as session:
        # 异步下载所有 ts 片段
        tasks = []
        pbar = tqdm(total=len(m3u8_obj.segments))
        logging.info(f"segment 个数: {len(m3u8_obj.segments)}")
        for _, segment in enumerate(m3u8_obj.segments):
        	# 真正ts的下载url是要拼起来的
            ts_url = base_url + segment.uri
            task = asyncio.create_task(download_segment(session, ts_url, segment.uri, output_dir, 0))
            tasks.append(task)
        await asyncio.gather(*tasks)
    # 任务完成后关闭进度条
    pbar.close()
    logging.info(f"下载完成")
    
m3u8_url = "http://.../playback.m3u8"
output_dir = "2-4-1-1"
asyncio.run(download_m3u8(m3u8_url, output_dir))

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按顺序拼接。

实测1分钟左右合并完成。

# merge.py
import os

def main(dir_name):
    filename = 'ts_list.txt'

    if os.path.exists(filename):
	    os.remove(filename)
    f = open(filename, 'a', encoding='utf-8')

    names = []
    with os.scandir(dir_name) as entries:
	    for entry in entries:
		    # 检查是否为文件
		    if entry.is_file():
			    names.append(entry.name)

    # 注意要先排序,按顺序写入文件名
    names.sort(key=lambda x: int(x.split('_')[0]))
    for name in names:
	    f.write(f"file  {os.path.join(dir_name,name)}\n")
	    
    f.close()

    mp4_name = f"{dir_name}.mp4"
    if os.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.py
python merge.py

其他

  1. 其实也可以直接用ffmpeg一次完成: ffmpeg -i http://.../playback.m3u8 -c copy 2-4-1.mp4,只是似乎是串行依次下载ts,速度不快。
  2. 我也有搜到用IDM下载或者potplayer播放,学长/弟/姐/妹可以自行尝试。
    • 学在西电课程回放稳定播放方法
    • 手把手教你用IDM下载学在西电课程回放视频
  3. 关于ppt视频的忽略音频流,ffmpeg可以设置参数,我没看这个
  4. 关于字幕生成,免费方案是B站必剪支持15分钟内视频的字幕生成,可以在必剪里裁剪和生成,但是有点麻烦而且效果很差。其他方案请自行研究。
  5. 似乎也有直接可用的m3u8播放器,请自行研究。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2256679.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

基于php+mysql的旅游网站——记忆旅行 旅游分享 攻略分享 设计与实现 源码 配置 文档

旅游网站 1.项目描述2. 概述3.项目功能4.界面展示5.源码获取 1.项目描述 摘 要 随着互联网的不断发展,计算机网络逐渐普及到人们的生活,为人们带来了便捷。互联网的趋势扩大,运用到家家户户中。各行各业都在考虑利用互联网将自己的信息推广…

2024.12.6——攻防世界php_rce

知识点:PHP框架 RCE远程命令执行 PHP常用框架:php常用的7大框架_php框架-CSDN博客 1.小型项目:CodeIngiter 2.中型项目:CakePHP、Zend Framework、Laravel、Thinkphp 3.大型重量级项目:Yii、Symfony、Laravel 使用…

autogen 人工输入模式

一、Allowing Human Feedback in Agents 允许代理中的人类反馈 发起聊天 (initiate_chat) 功能:用于启动对话过程。 参数:max_turns:限制对话的最大回合数。如果设置为3,意味着对话将在第三个回合后自动终止,除非提前…

网络渗透实验四(渗透课)

实验目的和要求实验目的:通过对目标靶机的渗透过程,了解CTF竞赛模式,理解CTF涵盖的知识范围,如MISC、PPC、WEB等,通过实践,加强团队协作能力,掌握初步CTF实战能力及信息收集能力。熟悉网络扫描、…

C++_关于异常处理throw-try-catch

文章目录 作用1. 无异常捕获2. 有异常捕获 作用 简单说,异常处理机制主要作用是让程序能够继续执行,或者以一种可控的方式终止,而非让程序因为错误直接崩溃 一个简单的动态数组类,来看看有异常捕获和无异常捕获的区别 1. 无异常…

LabVIEW氢同位素单质气体定量分装系统

氢同位素单质气体在多个行业中有重要应用,如能源和化工。传统的分装方法面临精度和自动化程度不足的问题。为此,开发了一套基于LabVIEW和质量流量控制器的定量分装系统,提高分装精度和效率,同时减少资源浪费和环境污染。 项目背景…

第427场周赛: 转换数组、用点构造面积最大的矩形 Ⅰ、长度可被 K 整除的子数组的最大元素和、用点构造面积最大的矩形 Ⅱ

Q1、转换数组 1、题目描述 给你一个整数数组 nums&#xff0c;它表示一个循环数组。请你遵循以下规则创建一个大小 相同 的新数组 result &#xff1a; 对于每个下标 i&#xff08;其中 0 < i < nums.length&#xff09;&#xff0c;独立执行以下操作&#xff1a; 如…

华为服务器使用U盘重装系统

一、准备工作 下载官方系统&#xff08;注意服务器CPU的架构是x86-64还是aarch64&#xff0c;不然可能报意想不到的错&#xff09;制作启动U盘&#xff08;下载rufus制作工具&#xff0c;注意文件系统选FAT32还是NTFS&#xff09; 二、安装步骤 将U盘插入USB接口重启服务器…

java八股-流量封控系统

文章目录 请求后台管理的频率-流量限制流量限制的业务代码UserFlowRiskControlFilter 短链接中台的流量限制CustomBlockHandler 对指定接口限流UserFlowRiskControlConfigurationSentinelRuleConfig 请求后台管理的频率-流量限制 根据登录用户做出控制&#xff0c;比如 x 秒请…

Nginx限流实践-limit_req和limit_conn的使用说明

注意&#xff1a; 本文内容于 2024-12-07 19:38:40 创建&#xff0c;可能不会在此平台上进行更新。如果您希望查看最新版本或更多相关内容&#xff0c;请访问原文地址&#xff1a;Nginx限流实践。感谢您的关注与支持&#xff01; 一、限流 之前我有记录通过CentOS7定时任务实…

centos9升级OpenSSH

需求 Centos9系统升级OpenSSH和OpenSSL OpenSSH升级为openssh-9.8p1 OpenSSL默认为OpenSSL-3.2.2&#xff08;根据需求进行升级&#xff09; 将源码包编译为rpm包 查看OpenSSH和OpenSSL版本 ssh -V下载源码包并上传到服务器 openssh最新版本下载地址 wget https://cdn.openb…

【CSS in Depth 2 精译_068】11.2 颜色的定义(下):CSS 中的各种颜色表示法简介

当前内容所在位置&#xff08;可进入专栏查看其他译好的章节内容&#xff09; 第四部分 视觉增强技术 ✔️【第 11 章 颜色与对比】 ✔️ 11.1 通过对比进行交流 11.1.1 模式的建立11.1.2 还原设计稿 11.2 颜色的定义 11.2.1 色域与色彩空间11.2.2 CSS 颜色表示法 ✔️ 11.2.2.…

16-02、JVM系列之:内存与垃圾回收篇(二)

JVM系列之&#xff1a;内存与垃圾回收篇(二) ##本篇内容概述&#xff1a; 1、堆Heap Area 2、方法区Method Area 3、运行时数据区总结 4、对象的实例化内存布局和访问定位一、堆 Heap Area 1、堆的核心概念 一个JVM实例只存在一个堆内存&#xff0c;堆也是Java内存管理的核心…

一文了解模式识别顶会ICPR 2024的研究热点与最新趋势

简介 对模式识别研究领域前沿方向的跟踪是提高科研能力和制定科研战略的关键。本文通过图文并茂的方式介绍了ICPR 2024的研究热点与最新趋势&#xff0c;帮助读者了解和跟踪模式识别的前沿研究方向。本推文的作者是黄星宇&#xff0c;审校为邱雪和许东舟。 一、会议介绍 ICPR…

网络安全知识:网络安全网格架构

在数字化转型的主导下&#xff0c;大多数组织利用多云或混合环境&#xff0c;包括本地基础设施、云服务和应用程序以及第三方实体&#xff0c;以及在网络中运行的用户和设备身份。在这种情况下&#xff0c;保护组织资产免受威胁涉及实现一个统一的框架&#xff0c;该框架根据组…

树莓集团是如何链接政、产、企、校四个板块的?

树莓集团作为数字影像行业的积极探索者与推动者&#xff0c;我们通过多维度、深层次的战略举措&#xff0c;将政、产、企、校四个关键板块紧密链接在一起&#xff0c;实现了资源的高效整合与协同发展&#xff0c;共同为数字影像产业的繁荣贡献力量。 与政府的深度合作政府在产业…

关于睡懒觉

我们经常听到一个词&#xff1a;睡懒觉。 我认为&#xff0c;睡懒觉这个词&#xff0c;是错误的。 人&#xff0c;是需要睡眠的&#xff0c;睡不够&#xff0c;就不会醒。睡够了&#xff0c;自然会醒&#xff0c;也不想继续睡。不信你试试&#xff0c;睡够了&#xff0c;你…

简单的动态带特殊符号敏感词校验

简单的动态带特殊符号敏感词校验 敏感词之前进行了简单了解&#xff0c;使用结巴分词自带词库可以实现&#xff0c;具体参考我的如下博文 敏感词校验 此次在此基础进行了部分优化&#xff0c;优化过程本人简单记录一下&#xff0c;具体优化改造步骤如下所示 1.需求 我们公司…

C++(十二)

前言&#xff1a; 本文将进一步讲解C中&#xff0c;条件判断语句以及它是如何运行的以及内部逻辑。 一&#xff0c;if-else,if-else语句。 在if语句中&#xff0c;只能判断两个条件的变量&#xff0c;若想实现判断两个以上条件的变体&#xff0c;就需要使用if-else,if-else语…

【Redis】Redis基础——Redis的安装及启动

一、初识Redis 1. 认识NoSQL 数据结构&#xff1a;对于SQL来说&#xff0c;表是有结构的&#xff0c;如字段约束、字段存储大小等。 关联性&#xff1a;SQL 的关联性体现在两张表之间可以通过外键&#xff0c;将两张表的数据关联查询出完整的数据。 查询方式&#xff1a; 2.…