python下载m3u8视频优化版

优化

上一个递归下载m3u8视频,需要自己在代码中修改key地址.
这个版本的优点是,如果m3u8文件中存在

URI="https://*********/key.key"

可以通过正则匹配找到key地址,而不用手动修改了.
缺点是,如果m3u8文件中的key地址不完整,或不是以以上方式描述的key地址,那么还是需要重新手动修改key地址的.

1.导入模块

import os
from concurrent.futures import ThreadPoolExecutor
import re
import requests
from Crypto.Cipher import AES
from tqdm import tqdm

2.执行过程

# m3u8文件中包含完整的key地址
def main():
	# 输入电影名称
    title = input('请输入电影名称:').strip()
    # 是否存在video目录
    if not os.path.isdir('video'):
        os.mkdir('video')
    # 获取ts列表和ts列表的索引列表
    lst, lst_n = get_lst()
    # 下载ts文件
    thread_download(lst, lst_n)
    # 判断是否需要解码
    key = judgment()
    # 如果key=None,不需要,否则需要
    if key is not None:
        key_url = key.group(1)
        thread_decode(lst, lst_n, key_url)
    # 合并ts文件
    merge(len(lst), title)
if __name__ == '__main__':
    main()
# m3u8文件中key地址不完整,就用这个
def main():
    title = input('请输入电影名称:').strip()
    if not os.path.isdir('video'):
        os.mkdir('video')
    lst, lst_n = get_lst()
    thread_download(lst, lst_n)
    key = input('是否需要解密(输入Y/N):').strip().upper()
    if key == 'Y':
        key_url = input('请输入key_url:')
        thread_decode(lst, lst_n, key_url)
    merge(len(lst), title)


if __name__ == '__main__':
    main()

3.获取ts网址列表

def get_lst():
    task = []
    num = []
    with open('1.m3u8', 'r', encoding='utf-8') as f:
        for line in f:
            line = line.strip()
            if line.startswith('#'):
                continue
            line = line
            task.append(line)
    for i in range(len(task)):
        num.append(i)
    return task, num

4.多线程下载

def download(url, name):
    resp = requests.get(url, timeout=10)
    with open(f'video/{name}', 'wb') as f1:
        f1.write(resp.content)
        print(f'{name}下载完成')


def thread_download(lst, lst_n):
    with ThreadPoolExecutor(50) as t:
        for i in lst_n:
            t.submit(download, name=i, url=lst[i])
    check(lst, lst_n)


def check(lst, lst_n):
    lst_ns = []
    for i in lst_n:
        if not os.path.isfile(f'video/{i}'):
            lst_ns.append(i)
    if lst_ns:
        print(lst_ns)
        print('开始递归==================================================')
        thread_download(lst, lst_ns)
    else:
        print('全部下载完成')

5. 判断是否需要解密

def judgment():
    with open('1.m3u8', 'r', encoding='utf-8') as f:
        obj = re.compile(r'URI="(.*?)"')
        key = obj.search(f.read())
    return key

6.如果需要解密

key = judgment()
if key is not None:
    key_url = key.group(1)
    thread_decode(lst, lst_n, key_url)
def decode_ts(name, url):
    resp = requests.get(url)
    key = resp.content
    iv = b'0000000000000000'
    aes = AES.new(key, AES.MODE_CBC, iv)
    with open(f'video/{name}', 'rb') as f:
        de_f = aes.decrypt(f.read())
        with open(f'video/{name}_1', 'wb') as f1:
            f1.write(de_f)
    print(f'{name}解码完成')


def thread_decode(lst, lst_n, key_url):
    with ThreadPoolExecutor(50) as t:
        for i in lst_n:
            t.submit(decode_ts, name=i, url=key_url)
    check_de_ts(lst, lst_n, key_url)


def check_de_ts(lst, lst_n, key_url):
    lst_ns = []
    for i in lst_n:
        if not os.path.isfile(f'video/{i}_1'):
            lst_ns.append(i)
    if lst_ns:
        print(lst_ns)
        print('开始递归==================================================')
        thread_download(lst, lst_ns)
        thread_decode(lst, lst_ns, key_url)
    else:
        print('全部ts解密完成')

7.合并视频

def merge(num, title):
    with open(f'video/{title}.mp4', 'wb') as f:
        for i in tqdm(range(num)):
            if os.path.isfile(f'video/{i}_1'):
                with open(f'video/{i}_1', 'rb') as f1:
                    f.write(f1.read())
            else:
                with open(f'video/{i}', 'rb') as f1:
                    f.write(f1.read())
    print('合并完成')
    delete_ts(num)
def delete_ts(num):
    for i in range(num):
        os.remove(f'video/{i}')
        if os.path.isfile(f'video/{i}_1'):
            os.remove(f'video/{i}_1')

8.完整代码

import os
from concurrent.futures import ThreadPoolExecutor
import re
import requests
from Crypto.Cipher import AES
from tqdm import tqdm


def get_lst():
    task = []
    num = []
    with open('1.m3u8', 'r', encoding='utf-8') as f:
        for line in f:
            line = line.strip()
            if line.startswith('#'):
                continue
            line = line
            task.append(line)
    for i in range(len(task)):
        num.append(i)
    return task, num


def download(url, name):
    resp = requests.get(url, timeout=10)
    with open(f'video/{name}', 'wb') as f1:
        f1.write(resp.content)
        print(f'{name}下载完成')


def thread_download(lst, lst_n):
    with ThreadPoolExecutor(50) as t:
        for i in lst_n:
            t.submit(download, name=i, url=lst[i])
    check(lst, lst_n)


def check(lst, lst_n):
    lst_ns = []
    for i in lst_n:
        if not os.path.isfile(f'video/{i}'):
            lst_ns.append(i)
    if lst_ns:
        print(lst_ns)
        print('开始递归==================================================')
        thread_download(lst, lst_ns)
    else:
        print('全部下载完成')


def merge(num, title):
    with open(f'video/{title}.mp4', 'wb') as f:
        for i in tqdm(range(num)):
            if os.path.isfile(f'video/{i}_1'):
                with open(f'video/{i}_1', 'rb') as f1:
                    f.write(f1.read())
            else:
                with open(f'video/{i}', 'rb') as f1:
                    f.write(f1.read())
    print('合并完成')
    delete_ts(num)


def decode_ts(name, url):
    resp = requests.get(url)
    key = resp.content
    iv = b'0000000000000000'
    aes = AES.new(key, AES.MODE_CBC, iv)
    with open(f'video/{name}', 'rb') as f:
        de_f = aes.decrypt(f.read())
        with open(f'video/{name}_1', 'wb') as f1:
            f1.write(de_f)
    print(f'{name}解码完成')


def thread_decode(lst, lst_n, key_url):
    with ThreadPoolExecutor(50) as t:
        for i in lst_n:
            t.submit(decode_ts, name=i, url=key_url)
    check_de_ts(lst, lst_n, key_url)


def check_de_ts(lst, lst_n, key_url):
    lst_ns = []
    for i in lst_n:
        if not os.path.isfile(f'video/{i}_1'):
            lst_ns.append(i)
    if lst_ns:
        print(lst_ns)
        print('开始递归==================================================')
        thread_download(lst, lst_ns)
        thread_decode(lst, lst_ns, key_url)
    else:
        print('全部ts解密完成')


def delete_ts(num):
    for i in range(num):
        os.remove(f'video/{i}')
        if os.path.isfile(f'video/{i}_1'):
            os.remove(f'video/{i}_1')


def judgment():
    with open('1.m3u8', 'r', encoding='utf-8') as f:
        obj = re.compile(r'URI="(.*?)"')
        key = obj.search(f.read())
    return key


def main():
    title = input('请输入电影名称:').strip()
    if not os.path.isdir('video'):
        os.mkdir('video')
    lst, lst_n = get_lst()
    thread_download(lst, lst_n)
    key = judgment()
    if key is not None:
        key_url = key.group(1)
        thread_decode(lst, lst_n, key_url)
    merge(len(lst), title)


if __name__ == '__main__':
    main()

### M3U8视频下载合并工具推荐 存在多种工具能够满足M3U8视频下载与合并需求。一款被提及的是专门针对M3U8文件设计的下载合并工具,该工具的特点在于其简易的操作流程以及实用性,能够让用户轻松处理来自网络上的分段M3U8文件,并将其合成为一个完整的视频文件[^1]。 另一款值得考虑的选择是结合了“猫抓”网页视频嗅探插件和专门为M3U8格式优化过的批量下载合并工具的一体化解决方案。“猫抓”插件可以帮助定位到目标视频的真实链接,而配套的M3U8下载器则负责执行实际的下载任务并将多个片段无缝拼接起来,形成最终可播放的媒体文件。此组合不仅提高了效率还简化了整个操作过程,非常适合有频繁下载在线课程或其他形式流媒体内容需求的人群使用[^2]。 对于希望自行尝试开发或定制功能的技术爱好者来说,“猫抓+M3U8批量下载合并工具”的开源本提供了丰富的接口和支持文档,允许开发者基于现有框架进一步扩展应用的功能特性。 #### Python脚本实现简单的M3U8下载与合并 如果倾向于编程方式解决问题,则可以通过编写Python脚本来完成同样的工作: ```python import os import requests from concurrent.futures import ThreadPoolExecutor, as_completed def download_ts(url, output_dir='.', filename=None): """Download a single .ts file from URL.""" if not os.path.exists(output_dir): os.makedirs(output_dir) local_filename = f"{output_dir}/{filename}.ts" with requests.get(url, stream=True) as r: r.raise_for_status() with open(local_filename, 'wb') as f: for chunk in r.iter_content(chunk_size=8192): f.write(chunk) def parse_m3u8(m3u8_url): """Parse m3u8 playlist and return list of ts URLs""" response = requests.get(m3u8_url) lines = [line.strip() for line in response.text.splitlines()] ts_urls = [] base_url = '/'.join(m3u8_url.split('/')[:-1]) + '/' for line in lines: if not line.startswith('#'): ts_urls.append(base_url + line) return ts_urls def merge_ts_files(ts_filenames, merged_output_file="merged_video.ts"): """Merge multiple TS files into one video file using ffmpeg command-line tool.""" with open('filelist.txt', mode='wt') as file: for fn in sorted(ts_filenames): file.write(f"file '{fn}'\n") cmd = ['ffmpeg', '-f', 'concat', '-safe', '0', '-i', 'filelist.txt', '-c', 'copy', merged_output_file] process = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE) stdout, stderr = process.communicate() if process.returncode != 0: raise Exception(f"Merging failed:\n{stderr.decode()}") if __name__ == "__main__": m3u8_playlist_url = "http://example.com/path/to/playlist.m3u8" # Parse the M3U8 playlist to get all segment (.ts) URLs. segments = parse_m3u8(m3u8_playlist_url) futures = [] with ThreadPoolExecutor(max_workers=5) as executor: for idx, url in enumerate(segments): future = executor.submit(download_ts, url=url, output_dir='./downloads', filename=str(idx)) futures.append(future) results = [future.result() for future in as_completed(futures)] # After downloading all parts, we can now join them together. merge_ts_files([os.path.join('./downloads', str(i)+".ts") for i in range(len(results))], "final_video.mp4") ``` 上述代码展示了如何利用Python解析M3U8清单文件中的各个TS片段URL,并通过多线程加速这些片段的同时下载;最后借助FFmpeg命令行工具将所有的TS片段连接成一个连续的MP4视频文件。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值