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('下载失败')
文件目录可以自己改,主要是用了个视频下载插件
这个插件可以直接提取m3u8文件省去判断的步骤。
对于加密的ts文件,这个程序并没有完善。#这个问题在后续改进中已经基本解决了
运行前目录:
需要通过插件把视频的m3u8文件移动过来。注意这边通过插件获得的m3u8文件是一行一行的ts文件的下载地址所以直接用
urllib.request.urlretrieve(download, file_name)
下载就行了
运行后目录:
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文件再进行了一次加密,这里面套路就不研究了。