-
Notifications
You must be signed in to change notification settings - Fork 10
/
main.py
349 lines (298 loc) · 15.1 KB
/
main.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
import base64
import datetime
import os
import threading
from PyQt5 import QtCore
from PyQt5.QtWidgets import *
from PyQt5.QtGui import *
import FileOperator
import TestFileSystem
from MainWindow import Ui_MainWindow
from icon import img
import TitleSpider
global canRun
def GetSeries(dataList):
return int(dataList.split('_')[1])
class MainApp(QMainWindow, Ui_MainWindow):
def __init__(self):
global canRun
QMainWindow.__init__(self)
if not self.SetUI():
canRun = False
return
else:
canRun = True
self.setupUi(self)
self.SetBaseInfo()
self.InitMenuBar()
self.HandleButtons()
self.setFixedSize(self.width(), self.height())
self.SetLogText()
self.progressBar.setValue(0)
self.InitCheckBox()
self.path = os.getcwd()
self.saveName = "set.ini"
self.joinedPath = os.path.join(self.path, self.saveName) # 配置文件的位置
self.isTextFileExists = False
self.isTextFirstColumnHaveContent = False
self.isTextSecondColumnHaveContent = False
# self.InitOutPutPath() # 发布绿色版时注释
self.Log(
"欢迎使用本工具,本工具发布于B站@落点dev。\n温馨提示:\n使用程序处理5GB以上的文件夹,出现未响应是正常现象。响应时间和硬盘读写速度挂钩,请耐心等待程序响应即可。\n\n"
"期间你可以打开输出目录以观察处理进度,程序出现其它问题可以b站评论区评论或私信,如需快速回复可联系qq:3152319989")
def InitOutPutPath(self):
self.isTextFileExists = FileOperator.SaveForOutput(self.path, self.saveName)
if self.isTextFileExists is False: # 如果没有文件,在已经创建文件的前提下,等待用户输入手动填入OutputText. 相关事件在button事件实现
# line = FileOperator.ReadForOutput() #
pass
else: # 存在文件 则读取文件,并自动赋给文本框
self.lines = FileOperator.ReadForOutput(self.joinedPath)
if len(self.lines) >= 2:
if self.lines[0] != '':
if os.path.isdir(self.lines[0].strip()):
self.downloadDirEdit.setText(self.lines[0].strip())
self.isTextFirstColumnHaveContent = True
if self.lines[1] != '':
if os.path.isdir(self.lines[1].strip()):
self.outputDirEdit.setText(self.lines[1].strip())
self.isTextSecondColumnHaveContent = True
else:
pass
# self.Log("检测到配置文件set.ini 为空")
# QMessageBox.critical(self, "错误", "配置文件set.ini !")
def InitCheckBox(self):
# self.txtFileCheckBox.setChecked(True) # 默认保存Txt
# self.deleteFileCheckBox.setChecked(False) # 默认保留源目录
# self.copyToOutput.setChecked(True) # 默认使用复制的方式
self.txtFileCheckBox.setChecked(False) # 默认不保存Txt
self.deleteFileCheckBox.setChecked(True) # 默认不保留源目录
self.moveToOutput.setChecked(True) # 默认不使用复制的方式
self.localMode.setChecked(True)
def MutiThreadCopy(self, mp4List, outputPath):
t = threading.Thread(target=FileOperator.CopyFile, args=(mp4List, outputPath))
t.start()
t.join()
def MutiThreadMove(self, mp4List, outputPath):
t = threading.Thread(target=FileOperator.MoveFile, args=(mp4List, outputPath))
t.start()
t.join()
def CheckIsChecked(self): # 按下按钮的事件里调用,检查checkbox状态,并提示。最后给对应的bool变量赋值
self.isSaveTxt = self.txtFileCheckBox.isChecked()
self.isDeleteDir = self.deleteFileCheckBox.isChecked()
if self.copyToOutput.isChecked() or self.moveToOutput.isChecked():
pass
else:
QMessageBox.critical(self, "错误", "请至少勾选一种输出方式!")
if self.localMode.isChecked() or self.spiderMode.isChecked():
pass
else:
QMessageBox.critical(self, "错误", "请至少勾选一种处理模式(本地模式 或 爬虫模式)!")
if self.copyToOutput.isChecked():
self.isCopyOutput = True
else:
self.isCopyOutput = False
if self.moveToOutput.isChecked():
self.isMoveOutput = True
else:
self.isMoveOutput = False
if self.localMode.isChecked():
self.isLocalMode = True
else:
self.isLocalMode = False
if self.spiderMode.isChecked():
QMessageBox.warning(self, "警告", "爬虫模式依赖网络,关闭本窗口前请确保代理服务是关闭状态")
self.isSpiderMode = True
else:
self.isSpiderMode = False
def SetLogText(self):
self.activityLogEdit.setReadOnly(True)
def Log(self, msg):
self.statusbar.showMessage(msg)
self.activityLogEdit.appendPlainText('[{0}]'.format(str(datetime.datetime.now())[0:19]))
self.activityLogEdit.appendPlainText(msg)
self.activityLogEdit.appendPlainText('')
def LogOnBar(self, msg):
self.statusbar.showMessage(msg)
def HandleButtons(self):
self.downloadDirButton.clicked.connect(self.OpenDownloadDir)
self.outputDirButton.clicked.connect(self.OpenOutputDir)
self.renameButton.clicked.connect(self.RenameFile)
self.copyToOutput.clicked.connect(self.DisableMove)
self.moveToOutput.clicked.connect(self.DisableCopy)
self.localMode.clicked.connect(self.DisableSpiderMode)
self.spiderMode.clicked.connect(self.DisableLocalMode)
# 处理checkbox冲突
def DisableCopy(self):
if self.copyToOutput.isChecked():
self.copyToOutput.setChecked(False)
def DisableMove(self):
if self.moveToOutput.isChecked():
self.moveToOutput.setChecked(False)
def DisableSpiderMode(self):
if self.spiderMode.isChecked():
self.spiderMode.setChecked(False)
self.renameButton.setText("一键解密+整理+重命名")
def DisableLocalMode(self):
if self.localMode.isChecked():
self.localMode.setChecked(False)
self.renameButton.setText("一键解密+爬取+整理+重命名")
def InitMenuBar(self):
# 添加menu“帮助”的事件
aboutAction = QAction('&关于', self)
# aboutAction.setStatusTip('关于')
aboutAction.triggered.connect(self.ShowAboutDialog)
# 已有菜单栏,此处只需要添加菜单
mainPageMenu = self.menubar.addMenu('&主页')
helpMenu = self.menubar.addMenu('&帮助')
# 菜单绑定之前添加的事件
helpMenu.addAction(aboutAction)
# 设置UI
def SetUI(self):
tmp = None
try:
tmp = open('tmp.png', "wb+")
tmp.write(base64.b64decode(img))
except Exception as e:
QMessageBox.critical(self, "错误", "您当前安装在了C盘,软件权限不足:\n解决方案一:请关闭本程序并每次使用管理员权限运行本软件 \n解决方案二:卸载本软件再重新安装至其它非系统盘 比如:D盘、E盘!")
return False
finally:
if tmp:
tmp.close()
icon = QIcon('tmp.png')
os.remove("tmp.png")
self.setWindowIcon(icon)
return True
def ShowAboutDialog(self):
about_text = "<p>描述:这是一款致力于解决BiliBili UWP版下载后的视频加密、命名信息丢失和存放位置不合理等痛点的软件</p><p>版本:4.5</p><p>@Author:LZY</p><p>@github:love" \
"-in-cpp</p> "
QMessageBox.about(self, '说明', about_text)
def OpenDownloadDir(self):
if self.isTextFirstColumnHaveContent is False:
dName = QFileDialog.getExistingDirectory(self, '选择下载文件夹', '/')
self.downloadDirEdit.setText(dName)
else:
dName = QFileDialog.getExistingDirectory(self, '选择下载文件夹', self.lines[0].strip())
self.downloadDirEdit.setText(dName)
def OpenOutputDir(self):
if self.isTextSecondColumnHaveContent is False:
dName = QFileDialog.getExistingDirectory(self, '选择输出文件夹', '/')
self.outputDirEdit.setText(dName)
else:
dName = QFileDialog.getExistingDirectory(self, '选择输出文件夹', self.lines[1].strip())
self.outputDirEdit.setText(dName)
def SetBaseInfo(self):
self.setWindowTitle('BiliBili UWP版视频解密整理工具')
self.downloadDirEdit.setToolTip(r"例如:E:\BiliDownload\44938322")
self.downloadDirEdit.setPlaceholderText("路径请具体到单个数字名称的文件夹,暂不支持文件夹的批量处理")
self.outputDirEdit.setPlaceholderText("您希望处理后的文件被保存到的地方")
# def FindFiles(self,downloadPath):
def RenameFile(self):
self.CheckIsChecked()
self.progressBar.setValue(0)
# 进入目录查找dvi文件
downloadPath = self.downloadDirEdit.toPlainText()
outputPath = self.outputDirEdit.toPlainText()
if os.path.isdir(downloadPath) is False or os.path.isdir(self.downloadDirEdit.toPlainText().strip()) is False:
self.Log('UWP下载目录的路径存在非法输入!')
else:
self.Log("进入目录:{0}".format(downloadPath))
dviInfoList = FileOperator.GetDviInfo(downloadPath) # 获取dvi文件信息
if dviInfoList[0] is False:
self.Log(
'没有找到.dvi文件!请检查下载目录后重试!请确保使用的是「bilibili '
'uwp客户端」下载的视频而非「桌面客户端」或其它客户端,请谨慎甄别,本工具不能处理其他客户端下载的视频;如果确认使用的是uwp客户端下载的视频,请仔细查看视频 1分20秒 '
'「选择下载目录」的片段')
else:
# 在outputDir下新建名为dvi[3]文件夹
try:
outputPath = FileOperator.MakeDir(outputPath, dviInfoList[3])
except Exception as e:
QMessageBox.critical(self, "错误", "已经存在同名文件夹! Error:" + str(e))
return
if self.isSpiderMode:
self.Log("开始爬取BV:{0}, 标题:{1} 的所有视频标题,请稍后...".format(dviInfoList[1], dviInfoList[3]))
try:
TitleSpider.GetTxt(dviInfoList[1], outputPath)
except Exception as e:
QMessageBox.critical(self, "错误", "请检查网络后重试 Error:" + str(e))
return
# 调用爬虫产生.txt
global fileName
fileName = TitleSpider.fileName
self.LogOnBar('已成功爬取文件: {0} ! 注:只显示部分文件名'.format(fileName[0:35]))
self.Log('已成功爬取文件: {0} !'.format(fileName))
elif self.isLocalMode:
self.Log("开始遍历获取BV:{0}, 标题:{1} 的所有视频标题,请稍后...".format(dviInfoList[1], dviInfoList[3]))
localVideoTitleList = FileOperator.GetLocalVideoTitle(downloadPath, dviInfoList[2])
fileName = FileOperator.GetTxt(localVideoTitleList, dviInfoList[3], outputPath)
self.Log('已成功获取文件: {0} !'.format(fileName))
else:
self.Log("impossible")
# 找到所有downloadPath的.mp4文件
mp4List = FileOperator.FindSpecialMp4Files(downloadPath, dviInfoList[2])[0] # mp4真正在的地方
# Log
mp4nameList = FileOperator.FindSpecialMp4Files(downloadPath, dviInfoList[2])[1]
try:
mp4nameList.sort(key=GetSeries)
except Exception as e:
QMessageBox.critical(self, "错误", "存在干扰文件!排序错误,请联系作者!" + str(e))
return
s = "查询到以下mp4文件:\n"
for item in mp4nameList:
s += (item + '\n')
self.Log(s)
if os.path.isdir(outputPath) is False or os.path.isdir(self.outputDirEdit.toPlainText().strip()) is False:
self.Log('输出目录的路径存在非法输入!')
else:
# 记忆输出目录
# FileOperator.WriteForOutput(self.joinedPath, os.path.dirname(downloadPath), self.outputDirEdit.toPlainText()) # 发布绿色版时注释
# 解密
self.Log("开始解密...")
FileOperator.DecryptMp4(downloadPath, dviInfoList[2])
self.Log("解密完毕!")
# 复制
self.CopyOrMove(self.isCopyOutput, mp4List, outputPath)
# 重命名
self.Log("开始重命名...")
FileOperator.DoRename(outputPath, fileName, dviInfoList[2], self.isLocalMode)
self.Log("重命名完毕!")
# 进度条100%
self.progressBar.setValue(100)
# 是否保存.txt文件
if self.isSaveTxt is True:
pass
else:
self.Log("正在删除程序运行过程中产生的.txt文件")
FileOperator.DeleteTxt(outputPath, fileName)
self.Log("删除.txt文件成功!")
# 是否删除源文件夹
if self.isDeleteDir is True:
self.Log("正在删除源文件夹")
FileOperator.DeleteDir(downloadPath)
self.Log("删除源文件夹成功!")
else:
pass
# 重命名输出文件夹 搁置
# 输出方式:复制或移动
def CopyOrMove(self, isCopyTo, mp4List, outputPath):
if isCopyTo is True:
self.Log("进入目录:{0}".format(outputPath))
self.Log("开始复制... 这可能需要一段时间...")
self.MutiThreadCopy(mp4List, outputPath) # 多线程复制
self.Log("复制完毕!")
else:
self.Log("进入目录:{0}".format(outputPath))
self.Log("开始移动... 这可能需要一段时间...")
self.MutiThreadMove(mp4List, outputPath) # 多线程移动
self.Log("移动完毕!")
def DSpiderMode(self):
pass
def DoLocalMode(self):
pass
if __name__ == '__main__':
QtCore.QCoreApplication.setAttribute(QtCore.Qt.AA_EnableHighDpiScaling)
app = QApplication([])
window = MainApp()
if canRun:
window.show()
app.exec_()