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()
View Code
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()
View Code
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