python利用线程池多线程下载ts并合并

ts文件是一段一段的切片视频我们需要把他们下载好用ffmpeg合并,这样合并的视频比直接拼接的视频流畅没那么卡顿。

import os
import urllib.request
from concurrent.futures import ThreadPoolExecutor
import time


# 下载文件函数
def Down_file(download, file_name):
	urllib.request.urlretrieve(download, file_name)


# 保存txt文件
def Save_file_list(root_dir, root_dir_1, file_list):
	with open(root_dir_1 + 'ts_list.txt', 'w', encoding='utf-8') as F:  # 指定保存txt文件的地址
		for file in range(len(file_list)):
			F.write(fr'file {root_dir}{file}.ts')  # 把所有ts名称写如txt文件 格式:file '***.ts'
			F.write('\n')


A = 9
start_time = time.time()
M3u8_file = f'./流媒体/{A}/m3u8.txt'
Tss_file = f'./流媒体/{A}/contents_ts/'
Root_dir = fr"D:/pycharmScript/合并ts流媒体/Project_1/流媒体/{A}/contents_ts/"
Root_dir_1 = f'./流媒体/{A}/'
# 多线程
with open(M3u8_file) as f:
	# 下载列表
	download_list = f.readlines()
# 保存的文件列表
file_name_list = [Tss_file + str(i) + '.ts' for i in range(len(download_list))]
Save_file_list(Root_dir, Root_dir_1, file_name_list)
# 线程池的创立
flag = True
download_flag = True
if flag:
	# 开设线程多少
	task_list = []
	n_threads = 50  # 越大越快?
	with ThreadPoolExecutor(max_workers=n_threads) as pool:
		for i in range(len(download_list)):
			task_list.append(pool.submit(Down_file, download_list[i], file_name_list[i]))
		# 判断所有下载线程是否全部结束
		first_time = time.time()
		flag_time = time.time()
		flag_A = len(task_list)
		while download_flag:
			second_time = time.time()
			if len(task_list) == 0:
				print('下载完成!正在转码保存')
				break
			for i in task_list:
				if i.done():
					task_list.remove(i)
			second_time = time.time()
			if second_time - first_time > 1:
				j = 100*(len(download_list) - len(task_list)) / len(download_list)
				print(str(j)+"%")
				first_time = second_time
			if second_time - flag_time > 30:
				flag_time = second_time
				if flag_A == len(task_list):
					download_flag = False
					print('结束')
					pool.shutdown(wait=False)
				else:
					flag_A = len(task_list)
if download_flag:
	# 合并保存
	tss_list_file = fr' D:\pycharmScript\合并ts流媒体\Project_1\流媒体\{A}\ts_list.txt'
	concat_mp4 = fr' D:\pycharmScript\合并ts流媒体\Project_1\流媒体\{A}\concat.mp4'
	cmd_code = f'ffmpeg -f concat -safe 0 -y -i {tss_list_file} -c copy -strict -2 {concat_mp4}'
	os.system(cmd_code)
	end_time = time.time()
	print(f'结束,运行时间为{end_time - start_time}s')
else:
	print('下载失败')

文件目录可以自己改,主要是用了个视频下载插件

ca8dbd59faba4ca2a0e46f9360595660.png

这个插件可以直接提取m3u8文件省去判断的步骤。

对于加密的ts文件,这个程序并没有完善。#这个问题在后续改进中已经基本解决了

运行前目录:

db980c3b509b4e50b92edf8bcba13267.png

需要通过插件把视频的m3u8文件移动过来。注意这边通过插件获得的m3u8文件是一行一行的ts文件的下载地址所以直接用

urllib.request.urlretrieve(download, file_name)

 下载就行了

c4aa22f77b3b4aec959788517de15fe9.png

 

运行后目录:

88a5d9daeea7446e81a9198d22270a73.png

 

 concat.mp4就是生成的视频

 

#---------------------------------------------------------------------------------

后面有改进了一版本更加合理

import os
import requests
from concurrent.futures import ThreadPoolExecutor
import time


def Download_file(url, file_name):
	global download_flag
	global download_files_Timeout
	global download_files_Exception
	try:
		# 使用requests库下载TS文件
		response = requests.get(url, stream=True, timeout=100, )
		# 将文件保存到本地
		with open(file_name, 'wb') as F:
			F.write(response.content)
		# print(f'{url}下载完成')
	except requests.exceptions.Timeout:
		download_files_Timeout.append(url)
		download_flag = False
	except Exception as e:
		download_flag = False
		print(e)
		download_files_Exception.append(url)


# 保存txt文件
def Save_file_list(root_dir, root_dir_1, file_list):
	with open(root_dir_1 + 'ts_list.txt', 'w', encoding='utf-8') as F:  # 指定保存txt文件的地址
		for file in range(len(file_list)):
			F.write(fr'file {root_dir}{file}.ts')  # 把所有ts名称写如txt文件 格式:file '***.ts'
			F.write('\n')


A = 4
start_time = time.time()
M3u8_file = f'./流媒体/{A}/m3u8.txt'
Tss_file = f'./流媒体/{A}/contents_ts/'
Root_dir = fr"D:/pycharmScript/合并ts流媒体/Project_1/流媒体/{A}/contents_ts/"
Root_dir_1 = f'./流媒体/{A}/'
# 读取m3u8
with open(M3u8_file) as f:
	# 下载列表f.readlines()会读到换行符要用strip删掉
	download_list = [i.strip() for i in f.readlines()]
	print(f'共有{len(download_list)}个文件')
# 保存的文件列表
file_name_list = [Tss_file + str(i) + '.ts' for i in range(len(download_list))]
Save_file_list(Root_dir, Root_dir_1, file_name_list)
# 线程池的创立
flag = True
download_flag = False
download_files_Timeout = []
download_files_Exception = []

if flag:
	# 开设线程多少
	task_list = []
	n_threads = 50  # 越大越快?
	with ThreadPoolExecutor(max_workers=n_threads) as pool:
		for i in range(len(download_list)):
			task_list.append(pool.submit(Download_file, download_list[i], file_name_list[i]))
		# 判断所有下载线程是否全部结束
		first_time = time.time()
		flag_time = time.time()
		flag_A = len(task_list)
		while True:
			second_time = time.time()
			if len(task_list) == 0:
				print('下载完成!正在转码保存')
				break
			for i in task_list:
				if i.done():
					task_list.remove(i)
			second_time = time.time()
			if second_time - first_time > 1:
				j = 100 * (len(download_list) - len(task_list)) / len(download_list)
				print(str(j) + "%")
				first_time = second_time

if download_flag:
	# 合并保存
	tss_list_file = fr' D:\pycharmScript\合并ts流媒体\Project_1\流媒体\{A}\ts_list.txt'
	concat_mp4 = fr' D:\pycharmScript\合并ts流媒体\Project_1\流媒体\{A}\concat.mp4'
	cmd_code = f'ffmpeg -f concat -safe 0 -y -i {tss_list_file} -c copy -strict -2 {concat_mp4}'
	os.system(cmd_code)
	end_time = time.time()
	print(f'结束,运行时间为{end_time - start_time}s')
else:
	print('转换失败')
	print(f'下载超时:{download_files_Timeout}\n下载失败:{download_files_Exception}')

#####################23年6月3日###########################

#######这次把一些加密的ts给搞定了

import os
import requests
from Crypto.Cipher import AES
from concurrent.futures import ThreadPoolExecutor
import time


def Download_file(url, file_name):
    global download_flag
    global download_files_Timeout
    global download_files_Exception
    global key
    try:
        # 使用requests库下载TS文件
        response = requests.get(url, stream=True, timeout=100, )
        cipher = AES.new(key, AES.MODE_CBC, response.content[:16])
        decrypted_content = cipher.decrypt(response.content[16:])

        # 将文件保存到本地
        with open(file_name, 'wb') as F:
            F.write(decrypted_content)
    # print(f'{url}下载完成')
    except requests.exceptions.Timeout:
        download_files_Timeout.append(url)
        download_flag = False
    except Exception as e:
        download_flag = False
        print(e)
        download_files_Exception.append(url)


# 保存txt文件
def Save_file_list(root_dir, root_dir_1, file_list):
    with open(root_dir_1 + 'ts_list.txt', 'w', encoding='utf-8') as F:  # 指定保存txt文件的地址
        for file in range(len(file_list)):
            F.write(fr'file {root_dir}{file}.ts')  # 把所有ts名称写如txt文件 格式:file '***.ts'
            F.write('\n')


def create_makedirs(A):
    current_dir = os.getcwd()
    # 创建相对路径
    relative_path = os.path.join(current_dir, fr'有加密解包测试\{A}\contents_ts')
    # 创建文件夹
    if not os.path.exists(relative_path):
        os.makedirs(relative_path)
        print('第一次创建目录')


start_time = time.time()

A = 1
M3u8_file = f'./有加密解包测试/{A}/m3u8.txt'
Tss_file = f'./有加密解包测试/{A}/contents_ts/'
Root_dir = fr"D:/pycharmScript/合并ts流媒体/Project_1/有加密解包测试/{A}/contents_ts/"
Root_dir_1 = f'./有加密解包测试/{A}/'
# 创建文件夹
create_makedirs(A)
# 打开M3U8文件并复制加密密钥的URL
Basic_website = 'https://exmple.com'
m3u8_url = Basic_website + '/xxxx/500kb/hls/index.m3u8'
key_url = Basic_website + '/xxxx/500kb/hls/key.key'

# key=dd3d3eeacfbebab9
# 获取加密密钥
response = requests.get(key_url)
key = response.content
print(type(key))
# 获取M3U8文件内容
response = requests.get(m3u8_url)
m3u8_content = response.content.decode('utf-8')
# 取得每个文件的url
lines = m3u8_content.split('\n')
download_list = [Basic_website + line for line in lines if line.endswith('.ts')]
num_files = len(download_list)
print(f'共有{num_files}文件')

# 保存的文件列表
file_name_list = [Tss_file + str(i) + '.ts' for i in range(len(download_list))]
Save_file_list(Root_dir, Root_dir_1, file_name_list)

# 线程池的创立
flag = True
download_flag = True
download_files_Timeout = []
download_files_Exception = []

if flag:
    # 开设线程多少
    task_list = []
    n_threads = 50  # 越大越快?
    with ThreadPoolExecutor(max_workers=n_threads) as pool:
        for i in range(num_files):
            task_list.append(pool.submit(Download_file, download_list[i], file_name_list[i]))
        # 判断所有下载线程是否全部结束
        first_time = time.time()
        flag_time = time.time()
        while True:
            second_time = time.time()
            if len(task_list) == 0:
                print('下载完成!正在转码保存')
                break
            for i in task_list:
                if i.done():
                    task_list.remove(i)
            second_time = time.time()
            if second_time - first_time > 1:
                j = 100 * (len(download_list) - len(task_list)) / len(download_list)
                print(str(j) + "%")
                first_time = second_time

if download_flag:
    # 合并保存
    tss_list_file = fr' D:\pycharmScript\合并ts流媒体\Project_1\有加密解包测试\{A}\ts_list.txt'
    concat_mp4 = fr' D:\pycharmScript\合并ts流媒体\Project_1\有加密解包测试\{A}\concat.mp4'
    cmd_code = f'ffmpeg -f concat -safe 0 -y -i {tss_list_file} -c copy -strict -2 {concat_mp4}'
    os.system(cmd_code)
    end_time = time.time()
    print(f'结束,运行时间为{end_time - start_time}s')
else:
    print('转换失败')
    print(f'下载超时:{download_files_Timeout}\n下载失败:{download_files_Exception}')

# 解密每个片段的内容
# for line in lines:
#     if line.endswith('.ts'):
#         segment_url = line
#         segment_url = Basic_website + segment_url
#         print(segment_url)
#         response = requests.get(segment_url)
#         cipher = AES.new(key, AES.MODE_CBC, response.content[:16])
#         decrypted_content = cipher.decrypt(response.content[16:])
#
#         # 保存解密后的内容到本地文件
#         with open(segment_url.split('/')[-1], 'wb') as f:
#             f.write(decrypted_content)
#
# # 1. `AES.new(key, AES.MODE_CBC, iv)`:创建一个新的AES加密器对象,其中`key`是加密密钥,`AES.MODE_CBC`是加密模式(这里使用的是CBC模式),`iv`是初始化向量。
# 2. `response.content[:16]`:获取响应内容的前16个字节作为初始化向量(IV)。
# 3. `cipher.decrypt(response.content[16:])`:使用AES加密器对象`cipher`解密响应内容的剩余部分,即从第16个字节开始到文件末尾的内容。解密后的内容将被存储在变量`decrypted_content`中。

############20230614###########

后续就考虑把这个封装成一个类好移植一些。不过好像也没有这个必要,毕竟就是写着玩的脚本。还有就是一些代码的结构还可以优化让速度更快一点。值得一提的是,python里面的多线程应该不是真正的多线程。线程池里面最多开50个确实是比10个快,应该与线程池和电脑操作系统的管理有关。

至于那些文件路径就靠cv过去小改一下就行了吧。文件路径要注意绝对路径和相对路径,还有ffmeng指令是用的os里面命令类似Windows下控制台cmd里输入。没装这个软件的可以搜搜其他大佬的博客,关键一点就是设置好环境变量基本就可以使用了。

或者也可以把那个if后面的flag置0,用二进制文件写入的方法合并视频,就是这样合并出来的视频可能有些地方会有点卡顿。

还有就是关于加密m3u8的下载,其实关键是找到m3u8文件和key文件。至于怎么找到,因为博主本身对前端东西了解不是特别多,只能说用浏览器插件,或者按f12打开调试窗口看网络请求,找到key文件和m3u8文件。一般视频网站的加密方法就类似上面写的,更高级的就是把key文件再进行了一次加密,这里面套路就不研究了。

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Python中,创建线程池有多种方式。其中一种方式是使用multiprocessing.dummy模块中的Pool类。可以通过以下代码创建一个线程池: from multiprocessing.dummy import Pool as ThreadPool pool = ThreadPool() 另外一种方式是使用concurrent.futures模块中的ThreadPoolExecutor类。可以通过以下代码创建一个线程池: from concurrent.futures import ThreadPoolExecutor pool = ThreadPoolExecutor() 这两种方式都可以创建一个线程池,用于执行多线程任务。线程池在系统启动时即创建大量空闲的线程,程序只要将一个函数提交给线程池线程池就会启动一个空闲的线程来执行它。当该函数执行结束后,该线程并不会死亡,而是再次返回到线程池中变成空闲状态,在线程池等待任务执行。线程池的优点是可以重复利用线程,避免了频繁创建和销毁线程的开销。\[1\]\[2\]\[3\] #### 引用[.reference_title] - *1* *2* [python 多线程 线程池的四种实现方式](https://blog.csdn.net/ye__mo/article/details/123664568)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^koosearch_v1,239^v3^insert_chatgpt"}} ] [.reference_item] - *3* [python多线程系列—线程池ThreadPoolExecutor(八)](https://blog.csdn.net/weixin_54542209/article/details/126284730)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^koosearch_v1,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值