下载解压 .ts文件
1.python exe程序
1 from tkinter import * 2 import time 3 import urllib.request 4 import http.cookiejar 5 import urllib.error 6 import urllib.parse 7 import re 8 import socket 9 import os 10 from pathlib import Path 11 12 from Cryptodome.Cipher import AES 13 from concurrent.futures import ThreadPoolExecutor 14 LOG_LINE_NUM = 0 15 16 class MY_GUI(): 17 # 初始化方法 构造方法 18 def __init__(self, init_window_name): 19 self.init_window_name=init_window_name 20 self.playlist_url = None 21 self.max_num = 250 22 self.header = {"Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8", 23 "Accept-Language": "zh-CN,zh;q=0.8,en-US;q=0.5,en;q=0.3", 24 "User-Agent": "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/38.0.2125.122 Safari/537.36 SE 2.X MetaSr 1.0", 25 "Connection": "keep-alive"} 26 self.cjar = http.cookiejar.CookieJar() 27 self.cookie = urllib.request.HTTPCookieProcessor(self.cjar) 28 self.opener = urllib.request.build_opener(self.cookie) 29 urllib.request.install_opener(self.opener) 30 31 self.pool = ThreadPoolExecutor(max_workers=10) 32 self.sum = 0 33 # 利用socket模块,使得每次重新下载的时间变短 34 socket.setdefaulttimeout(20) 35 36 def download_file(self, url, target): 37 # 解决下载不完全问题且避免陷入死循环 38 try: 39 print(END, str("下载:"+url)+'\n') 40 # self.write_log_to_Text(str("下载:"+url)) 41 urllib.request.urlretrieve(url, target) 42 except socket.timeout: 43 count = 1 44 while count <= 5: 45 try: 46 urllib.request.urlretrieve(url, target) 47 break 48 except socket.timeout: 49 err_info = url + ' Reloading for %d time' % count if count == 1 else 'Reloading for %d times' % count 50 print(END, str(err_info) + '\n') 51 count += 1 52 except: 53 # 解决远程主机关闭问题 54 self.download_file(url, target) 55 if count > 5: 56 print(str('downloading fialed!')) 57 except: 58 # 解决远程主机关闭问题 59 self.download_file(url, target) 60 61 # 打开 m3u8 62 def open_web(self, url): 63 try: 64 response = self.opener.open(url, timeout=3) 65 except urllib.error.URLError as e: 66 print(END, str('open ' + url + ' error') + '\n') 67 self.write_log_to_Text(str('open ' + url + ' error')) 68 if hasattr(e, 'code'): 69 print(END, str(e.code) + '\n') 70 self.write_log_to_Text(str(e.code)) 71 if hasattr(e, 'reason'): 72 print(END, str(e.reason) + '\n') 73 self.write_log_to_Text(str(e.reason)) 74 else: 75 return response.read() 76 77 '''第一步、解析m3u8''' 78 def get_available_IP(self): 79 print(END, str('开始获取真实的url') + '\n') 80 self.write_log_to_Text(str('开始获取真实的url')) 81 data = self.open_web(self.m3u8_url).decode('utf-8') 82 tv_lists = re.findall(',\n(.*).ts\n', data) 83 return tv_lists 84 85 # 下载.ts 86 def download_for_multi_process(self, ts): 87 ts_url = ts + ".ts" 88 str_style = "%05d" % self.sum 89 path_str = str_style + '.ts' 90 downUrl = self.url + ts_url 91 downPath = self.sourceFile + path_str 92 if os.path.isfile(downPath) and os.path.getsize(downPath) > 0: 93 print(END, str("file already exist") + '\n') 94 self.write_log_to_Text(str("file already exist")) 95 else: 96 self.download_file(downUrl, downPath) 97 self.sum = self.sum + 1 98 99 # 开始线程 100 def download_with_multi_process(self, ts_list): 101 print(END, str('开始多线程下载') + '\n') 102 self.write_log_to_Text(str("开始多线程下载")) 103 task = self.pool.map(self.download_for_multi_process, ts_list) # 此时非阻塞 104 for t in task: # 此时会变成阻塞 105 pass 106 107 '''第四步、合并ts文件''' 108 def merge_ts_file_with_os(self): 109 print(END, str('开始合并') + '\n') 110 self.write_log_to_Text(str("开始合并")) 111 # 合并ts文件 112 os.chdir(self.chdir) 113 shell_str = f'copy /b *.ts {self.merge_ts_path}' 114 os.system(shell_str) 115 os.system(f'del /Q *.ts') 116 os.chdir(self.sourceFile) 117 os.system(f'del /Q *.ts') 118 os.chdir(self.chdir) 119 self.write_log_to_Text(str("合并完成")) 120 print(END, str('合并完成') + '\n') 121 122 # 解密 123 def decodeFile(self): 124 key = self.key 125 self.chdir = self.sourceFile 126 if len(key): 127 cryptor = AES.new(key, AES.MODE_CBC, key) 128 # 读取ts文件 129 sourceFile = self.sourceFile 130 savefile_path = self.saveFile 131 list_file = os.listdir(sourceFile) 132 for file in list_file: 133 c_fule_name = file 134 file_line = file 135 if ".ts" in c_fule_name: 136 fo = open(os.path.join(sourceFile, file_line), 'rb'); 137 if len(key): # AES 解密,有key就是需要解密 138 with open(os.path.join(savefile_path, c_fule_name), 'ab') as f: 139 f.write(cryptor.decrypt(fo.read())) 140 print(END, str("解密完成") + '\n') 141 self.write_log_to_Text(str("解密完成")) 142 self.chdir = self.saveFile 143 self.merge_ts_file_with_os() # 合并ts文件 144 145 #设置窗口 146 def set_init_window(self): 147 self.init_window_name.title("ts文件下载工具_v2.0 by: 龙林基") #窗口名 148 self.init_window_name.geometry('500x600+10+10') 149 #标签 150 self.init_data_label = Label(self.init_window_name, text="请求数据") 151 self.init_data_label.grid(row=0, column=0) 152 153 self.request_m3u8_label= Label(self.init_window_name, text="m3u8_url:") 154 self.request_m3u8_label.grid(row=2, column=0) 155 self.request_separator_label = Label(self.init_window_name, text="separator:") 156 self.request_separator_label.grid(row=11, column=0) 157 self.request_fileName_label = Label(self.init_window_name, text="file_name:") 158 self.request_fileName_label.grid(row=21, column=0) 159 self.request_key_label = Label(self.init_window_name, text="key:") 160 self.request_key_label.grid(row=31, column=0) 161 self.request_sourceFile_label = Label(self.init_window_name, text="sourceFile:") 162 self.request_sourceFile_label.grid(row=41, column=0) 163 self.request_saveFile_label = Label(self.init_window_name, text="saveFile:") 164 self.request_saveFile_label.grid(row=51, column=0) 165 166 self.log_label = Label(self.init_window_name, text="日志") 167 self.log_label.grid(row=120, column=0) 168 169 170 #文本框 171 self.request_m3u8_Text = Text(self.init_window_name, width=50, height=1.5) #m3u8_url录入框 172 self.request_m3u8_Text.grid(row=2, column=2, rowspan=2) 173 self.request_separator_Text = Text(self.init_window_name, width=50, height=1.5) # separator录入框 174 self.request_separator_Text.grid(row=11, column=2, rowspan=2) 175 self.request_fileName_Text = Text(self.init_window_name, width=50, height=1.5) # fileName录入框 176 self.request_fileName_Text.grid(row=21, column=2, rowspan=2) 177 self.request_key_Text = Text(self.init_window_name, width=50, height=1.5) # ket录入框 178 self.request_key_Text.grid(row=31, column=2, rowspan=2) 179 self.request_sourceFile_Text = Text(self.init_window_name, width=50, height=1.5) # sourceFile录入框 180 self.request_sourceFile_Text.grid(row=41, column=2, rowspan=2) 181 self.request_sourceFile_Text.insert(END,"D:\\soft\\python\\project\\download\\") 182 self.request_saveFile_Text = Text(self.init_window_name, width=50, height=1.5) # saveFile录入框 183 self.request_saveFile_Text.grid(row=51, column=2, rowspan=2) 184 self.request_saveFile_Text.insert(END,"D:\\soft\\python\\project\\download\\decode\\") 185 # 按钮 186 self.submit_button = Button(self.init_window_name, text="submit", width=5, 187 command=self.runmain) # 调用内部方法 加()为直接调用 188 self.submit_button.grid(row=60, column=2) 189 self.reset_button = Button(self.init_window_name, text="reset", width=5, 190 command=self.reset) # 调用内部方法 加()为直接调用 191 self.reset_button.grid(row=60, column=3) 192 193 self.log_data_Text = Text(self.init_window_name, width=66, height=30) # 日志框 194 self.log_data_Text.grid(row=130, column=0, columnspan=20) 195 196 # 滚动条 197 self.result_data_scrollbar_y = Scrollbar(self.init_window_name) # 创建纵向滚动条 198 self.result_data_scrollbar_y.config(command=self.log_data_Text.yview) # 将创建的滚动条通过command参数绑定到需要拖动的Text上 199 self.log_data_Text.config(yscrollcommand=self.result_data_scrollbar_y.set) # Text反向绑定滚动条 200 self.result_data_scrollbar_y.grid(row=130, column=23, rowspan=15, sticky='NS') 201 202 # 主函数 203 def runmain(self): 204 m3u8s = [] 205 names = [] 206 keys = [] 207 substr = str(self.request_separator_Text.get(1.0,END)) 208 if len(str(self.request_m3u8_Text.get(1.0,END))) > 1: 209 m3u8s = str(self.request_m3u8_Text.get(1.0,END)).split(",") 210 if len(str(self.request_fileName_Text.get(1.0,END))) > 1: 211 names = str(self.request_fileName_Text.get(1.0,END)).split(",") 212 if len(str(self.request_key_Text.get(1.0,END))) > 1: 213 keys = str(self.request_key_Text.get(1.0,END)).split(",") 214 # 下载路径 215 self.sourceFile = str(self.request_sourceFile_Text.get(1.0,END)).split("\n")[0] 216 self.saveFile = str(self.request_saveFile_Text.get(1.0,END)).split("\n")[0] 217 # 判断路径是否存在 218 my_file = Path(self.sourceFile) 219 if not my_file.exists(): 220 os.makedirs(my_file) 221 my_file = Path(self.saveFile) 222 if not my_file.exists(): 223 os.makedirs(my_file) 224 225 # 循环下载 226 for i in range(len(m3u8s)): 227 m3u8_url = m3u8s[i] 228 web_url = m3u8_url.partition(substr)[0]; 229 file_name = re.sub('\s+', '', names[i]).strip() 230 if keys.__len__() is 0: 231 key = '' 232 else: 233 key = keys[i].encode('utf8') 234 merge_ts_path = f'{file_name}.mp4' 235 self.m3u8_url = m3u8_url 236 self.url = web_url 237 self.merge_ts_path = merge_ts_path 238 self.key = key 239 self.sum =0 240 # down = MY_GUI(web_url, m3u8_url, merge_ts_path, key) # 构造方法 241 ts_list = self.get_available_IP() # 第一步、获取真正的ts 列表 242 self.download_with_multi_process(ts_list) # 开始多线程下载 243 self.decodeFile() 244 245 # reset 246 def reset(self): 247 self.log_data_Text.delete(1.0,END) 248 self.request_m3u8_Text.delete(1.0,END) 249 self.request_separator_Text.delete(1.0,END) 250 self.request_fileName_Text.delete(1.0,END) 251 self.request_key_Text.delete(1.0,END) 252 #获取当前时间 253 def get_current_time(self): 254 current_time = time.strftime('%Y-%m-%d %H:%M:%S',time.localtime(time.time())) 255 return current_time 256 257 258 #日志动态打印 259 def write_log_to_Text(self,logmsg): 260 global LOG_LINE_NUM 261 current_time = self.get_current_time() 262 logmsg_in = str(current_time) +" " + str(logmsg) + "\n" #换行 263 self.log_data_Text.insert(END, logmsg_in) 264 265 266 def gui_start(): 267 init_window = Tk() #实例化出一个父窗口 268 ZMJ_PORTAL = MY_GUI(init_window) 269 # 设置根窗口默认属性 270 ZMJ_PORTAL.set_init_window() 271 272 init_window.mainloop() #父窗口进入事件循环,可以理解为保持窗口运行,否则界面不展示 273 274 275 gui_start()
2.python exe update
1 from tkinter import * 2 import time 3 import urllib.request 4 import http.cookiejar 5 import urllib.error 6 import urllib.parse 7 import re 8 import socket 9 import os 10 from pathlib import Path 11 12 from Cryptodome.Cipher import AES 13 from concurrent.futures import ThreadPoolExecutor 14 LOG_LINE_NUM = 0 15 16 class MY_GUI(): 17 # 初始化方法 构造方法 18 def __init__(self, init_window_name): 19 self.init_window_name=init_window_name 20 self.playlist_url = None 21 self.total=0 22 self.max_num = 250 23 self.header = {"Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8", 24 "Accept-Language": "zh-CN,zh;q=0.8,en-US;q=0.5,en;q=0.3", 25 "User-Agent": "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/38.0.2125.122 Safari/537.36 SE 2.X MetaSr 1.0", 26 "Connection": "keep-alive"} 27 self.cjar = http.cookiejar.CookieJar() 28 self.cookie = urllib.request.HTTPCookieProcessor(self.cjar) 29 self.opener = urllib.request.build_opener(self.cookie) 30 urllib.request.install_opener(self.opener) 31 32 self.pool = ThreadPoolExecutor(max_workers=10) 33 self.sum = 0 34 # 利用socket模块,使得每次重新下载的时间变短 35 socket.setdefaulttimeout(20) 36 37 def download_file(self, url, target): 38 # 解决下载不完全问题且避免陷入死循环 39 try: 40 urllib.request.urlretrieve(url, target) 41 except socket.timeout: 42 count = 1 43 while count <= 5: 44 try: 45 urllib.request.urlretrieve(url, target) 46 break 47 except socket.timeout: 48 err_info = url + ' Reloading for %d time' % count if count == 1 else 'Reloading for %d times' % count 49 print(END, str(err_info) + '\n') 50 count += 1 51 except: 52 # 解决远程主机关闭问题 53 self.download_file(url, target) 54 if count > 5: 55 print(str('downloading fialed!')) 56 except: 57 # 解决远程主机关闭问题 58 self.download_file(url, target) 59 60 # 打开 m3u8 61 def open_web(self, url): 62 try: 63 response = self.opener.open(url, timeout=3) 64 except urllib.error.URLError as e: 65 print(END, str('open ' + url + ' error') + '\n') 66 if hasattr(e, 'code'): 67 print(END, str(e.code) + '\n') 68 if hasattr(e, 'reason'): 69 print(END, str(e.reason) + '\n') 70 else: 71 return response.read() 72 73 '''第一步、解析m3u8''' 74 def get_available_IP(self): 75 print(END, str('开始获取真实的url') + '\n') 76 data = self.open_web(self.m3u8_url).decode('utf-8') 77 tv_lists = re.findall(',\n(.*).ts\n', data) 78 self.total = tv_lists.__len__() 79 return tv_lists 80 81 # 下载.ts 82 def download_for_multi_process(self, ts): 83 ts_url = ts + ".ts" 84 str_style = "%05d" % self.sum 85 path_str = str_style + '.ts' 86 downUrl = self.url + ts_url 87 downPath = self.sourceFile + path_str 88 if os.path.isfile(downPath) and os.path.getsize(downPath) > 0: 89 print(END, str("file already exist") + '\n') 90 else: 91 self.download_file(downUrl, downPath) 92 self.sum = self.sum + 1 93 jd = str(self.sum)+"/"+str(self.total) 94 print(END, str("进度:" +jd) + '\n') 95 96 # 开始线程 97 def download_with_multi_process(self, ts_list): 98 print(END, str('开始多线程下载') + '\n') 99 task = self.pool.map(self.download_for_multi_process, ts_list) # 此时非阻塞 100 for t in task: # 此时会变成阻塞 101 pass 102 103 '''第四步、合并ts文件''' 104 def merge_ts_file_with_os(self): 105 print(END, str('开始合并') + '\n') 106 # 合并ts文件 107 os.chdir(self.chdir) 108 shell_str = f'copy /b *.ts {self.merge_ts_path}' 109 os.system(shell_str) 110 os.system(f'del /Q *.ts') 111 os.chdir(self.sourceFile) 112 os.system(f'del /Q *.ts') 113 os.chdir(self.chdir) 114 print(END, str('合并完成') + '\n') 115 116 # 解密 117 def decodeFile(self): 118 key = self.key 119 self.chdir = self.sourceFile 120 if len(key): 121 # key=bytes(key) 122 cryptor = AES.new(key, AES.MODE_CBC, key) 123 # 读取ts文件 124 sourceFile = self.sourceFile 125 savefile_path = self.saveFile 126 list_file = os.listdir(sourceFile) 127 for file in list_file: 128 c_fule_name = file 129 file_line = file 130 if ".ts" in c_fule_name: 131 fo = open(os.path.join(sourceFile, file_line), 'rb'); 132 if len(key): # AES 解密,有key就是需要解密 133 with open(os.path.join(savefile_path, c_fule_name), 'ab') as f: 134 f.write(cryptor.decrypt(fo.read())) 135 print(END, str("解密完成") + '\n') 136 self.chdir = self.saveFile 137 self.merge_ts_file_with_os() # 合并ts文件 138 139 #设置窗口 140 def set_init_window(self): 141 self.init_window_name.title("ts文件下载工具_v2.0 by: 龙林基") #窗口名 142 self.init_window_name.geometry('500x600+10+10') 143 #标签 144 self.init_data_label = Label(self.init_window_name, text="请求数据") 145 self.init_data_label.grid(row=0) 146 147 self.request_m3u8_label= Label(self.init_window_name, text="m3u8_url:") 148 self.request_m3u8_label.grid(row=2) 149 self.request_m3u8_Text = Entry(self.init_window_name, width=55) 150 self.request_m3u8_Text.grid(row=2, column=1, pady=5) 151 152 self.request_separator_label = Label(self.init_window_name, text="separator:") 153 self.request_separator_label.grid(row=3) 154 self.request_separator_Text = Entry(self.init_window_name, width=55) # separator录入框 155 self.request_separator_Text.grid(row=3, column=1, pady=5) 156 157 self.request_fileName_label = Label(self.init_window_name, text="file_name:") 158 self.request_fileName_label.grid(row=4) 159 self.request_fileName_Text = Entry(self.init_window_name, width=55) # fileName录入框 160 self.request_fileName_Text.grid(row=4, column=1, pady=5) 161 self.request_fileName_Text.insert(0, "fileName") 162 163 self.request_key_label = Label(self.init_window_name, text="key:") 164 self.request_key_label.grid(row=5) 165 self.request_key_Text = Entry(self.init_window_name, width=55) # ket录入框 166 self.request_key_Text.grid(row=5, column=1, pady=5) 167 168 169 self.request_sourceFile_label = Label(self.init_window_name, text="sourceFile:") 170 self.request_sourceFile_label.grid(row=6) 171 self.request_sourceFile_Text = Entry(self.init_window_name, width=55) # sourceFile录入框 172 self.request_sourceFile_Text.grid(row=6, column=1, pady=5) 173 self.request_sourceFile_Text.insert(0, "D:\\soft\\python\\project\\download\\dd\\") 174 175 self.request_saveFile_label = Label(self.init_window_name, text="saveFile:") 176 self.request_saveFile_label.grid(row=8) 177 self.request_saveFile_Text = Entry(self.init_window_name, width=55) # saveFile录入框 178 self.request_saveFile_Text.grid(row=8, column=1, pady=5) 179 self.request_saveFile_Text.insert(0, "D:\\soft\\python\\project\\download\\decode\\") 180 181 # 按钮 182 self.submit_button = Button(self.init_window_name, text="submit", width=5, 183 command=self.runmain) # 调用内部方法 加()为直接调用 184 self.submit_button.grid(row=12, column=0, sticky=W, pady=5) 185 self.reset_button = Button(self.init_window_name, text="reset", width=5, 186 command=self.reset) # 调用内部方法 加()为直接调用 187 self.reset_button.grid(row=12, column=1, sticky=W, pady=5) 188 189 # 主函数 190 def runmain(self): 191 m3u8s = [] 192 names = [] 193 keys = [] 194 substr = str(self.request_separator_Text.get()) 195 if len(str(self.request_m3u8_Text.get())) > 1: 196 m3u8s = str(self.request_m3u8_Text.get()).split(",") 197 if len(str(self.request_fileName_Text.get())) > 1: 198 names = str(self.request_fileName_Text.get()).split(",") 199 if len(str(self.request_key_Text.get())) > 1: 200 keys = str(self.request_key_Text.get()).split(",") 201 # 下载路径 202 self.sourceFile = str(self.request_sourceFile_Text.get()).split("\n")[0] 203 self.saveFile = str(self.request_saveFile_Text.get()).split("\n")[0] 204 # 判断路径是否存在 205 my_file = Path(self.sourceFile) 206 if not my_file.exists(): 207 os.makedirs(my_file) 208 my_file = Path(self.saveFile) 209 if not my_file.exists(): 210 os.makedirs(my_file) 211 212 # 循环下载 213 for i in range(len(m3u8s)): 214 m3u8_url = m3u8s[i] 215 web_url = m3u8_url.partition(substr)[0]; 216 file_name = re.sub('\s+', '', names[i]).strip() 217 if keys.__len__() is 0: 218 key = '' 219 else: 220 key = keys[i].encode('utf8') 221 merge_ts_path = f'{file_name}.mp4' 222 self.m3u8_url = m3u8_url 223 self.url = web_url 224 self.merge_ts_path = merge_ts_path 225 self.key = key 226 self.sum =0 227 # down = MY_GUI(web_url, m3u8_url, merge_ts_path, key) # 构造方法 228 ts_list = self.get_available_IP() # 第一步、获取真正的ts 列表 229 self.download_with_multi_process(ts_list) # 开始多线程下载 230 self.decodeFile() 231 232 # reset 233 def reset(self): 234 # self.log_data_Text.delete() 235 self.request_m3u8_Text.delete(0, END) 236 self.request_separator_Text.delete(0, END) 237 self.request_fileName_Text.delete(0, END) 238 self.request_key_Text.delete(0, END) 239 #获取当前时间 240 def get_current_time(self): 241 current_time = time.strftime('%Y-%m-%d %H:%M:%S',time.localtime(time.time())) 242 return current_time 243 244 245 #日志动态打印 246 def write_log_to_Text(self,logmsg): 247 global LOG_LINE_NUM 248 current_time = self.get_current_time() 249 logmsg_in = str(current_time) +" " + str(logmsg) + "\n" #换行 250 self.log_data_Text.insert(END, logmsg_in) 251 252 253 def gui_start(): 254 init_window = Tk() #实例化出一个父窗口 255 ZMJ_PORTAL = MY_GUI(init_window) 256 # 设置根窗口默认属性 257 ZMJ_PORTAL.set_init_window() 258 259 init_window.mainloop() #父窗口进入事件循环,可以理解为保持窗口运行,否则界面不展示 260 261 262 gui_start()
4.更换pip源为阿里云:
pip config set global.index-url https://mirrors.aliyun.com/pypi/simple/
5.可以执行如下命令:pip install pycryptodomex ,即安装新版本的加密解密库
然后引入改成如下方式:
from Cryptodome.Hash import SHA256 from Cryptodome.Cipher import AES from Cryptodome.Cipher import DES