|
|
|
@ -0,0 +1,252 @@
|
|
|
|
|
# -*- coding: utf-8 -*-
|
|
|
|
|
|
|
|
|
|
# Form implementation generated from reading ui file 'main.ui'
|
|
|
|
|
#
|
|
|
|
|
# Created by: PyQt5 UI code generator 5.15.4
|
|
|
|
|
#
|
|
|
|
|
# WARNING: Any manual changes made to this file will be lost when pyuic5 is
|
|
|
|
|
# run again. Do not edit this file unless you know what you are doing.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
from PyQt5 import QtCore, QtGui, QtWidgets
|
|
|
|
|
import sys
|
|
|
|
|
import requests
|
|
|
|
|
import json
|
|
|
|
|
import re
|
|
|
|
|
import os
|
|
|
|
|
from get_text import get_text
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class Ui_MainWindow(object):
|
|
|
|
|
def setupUi(self, MainWindow):
|
|
|
|
|
MainWindow.setObjectName("MainWindow")
|
|
|
|
|
MainWindow.resize(800, 600)
|
|
|
|
|
MainWindow.setMinimumSize(QtCore.QSize(800, 600))
|
|
|
|
|
MainWindow.setMaximumSize(QtCore.QSize(800, 600))
|
|
|
|
|
self.centralwidget = QtWidgets.QWidget(MainWindow)
|
|
|
|
|
|
|
|
|
|
self.centralwidget.setObjectName("centralwidget")
|
|
|
|
|
self.beijing = QtWidgets.QLabel(self.centralwidget)
|
|
|
|
|
self.beijing.setGeometry(QtCore.QRect(0, 0, 800, 600))
|
|
|
|
|
self.beijing.setText("")
|
|
|
|
|
self.beijing.setPixmap(QtGui.QPixmap("img/0.jpg"))
|
|
|
|
|
self.beijing.setObjectName("beijing")
|
|
|
|
|
|
|
|
|
|
self.lineEdit_2 = QtWidgets.QLineEdit(self.centralwidget)
|
|
|
|
|
self.lineEdit_2.setGeometry(QtCore.QRect(224, 180, 320, 20))
|
|
|
|
|
self.lineEdit_2.setObjectName("lineEdit_2")
|
|
|
|
|
|
|
|
|
|
self.download = QtWidgets.QPushButton(self.centralwidget)
|
|
|
|
|
self.download.setGeometry(QtCore.QRect(555, 178, 75, 23))
|
|
|
|
|
self.download.setObjectName("download")
|
|
|
|
|
|
|
|
|
|
self.zhantie = QtWidgets.QPushButton(self.centralwidget)
|
|
|
|
|
self.zhantie.setGeometry(QtCore.QRect(325, 230, 150, 46))
|
|
|
|
|
self.zhantie.setObjectName("zhantie")
|
|
|
|
|
|
|
|
|
|
self.textEdit = QtWidgets.QTextEdit(self.centralwidget)
|
|
|
|
|
self.textEdit.setGeometry(QtCore.QRect(150, 300, 500, 200))
|
|
|
|
|
self.textEdit.setObjectName("textEdit")
|
|
|
|
|
|
|
|
|
|
self.label = QtWidgets.QLabel(self.centralwidget)
|
|
|
|
|
self.label.setGeometry(QtCore.QRect(170, 184, 54, 12))
|
|
|
|
|
self.label.setObjectName("label")
|
|
|
|
|
|
|
|
|
|
self.open = QtWidgets.QPushButton(self.centralwidget)
|
|
|
|
|
self.open.setGeometry(QtCore.QRect(560, 530, 75, 23))
|
|
|
|
|
self.open.setObjectName("open")
|
|
|
|
|
|
|
|
|
|
self.moren = QtWidgets.QPushButton(self.centralwidget)
|
|
|
|
|
self.moren.setGeometry(QtCore.QRect(555, 134, 75, 23))
|
|
|
|
|
self.moren.setObjectName("moren")
|
|
|
|
|
|
|
|
|
|
self.lineEdit = QtWidgets.QLineEdit(self.centralwidget)
|
|
|
|
|
self.lineEdit.setGeometry(QtCore.QRect(224, 136, 320, 20))
|
|
|
|
|
self.lineEdit.setObjectName("lineEdit")
|
|
|
|
|
|
|
|
|
|
self.mulu = QtWidgets.QLabel(self.centralwidget)
|
|
|
|
|
self.mulu.setGeometry(QtCore.QRect(170, 140, 54, 12))
|
|
|
|
|
self.mulu.setObjectName("mulu")
|
|
|
|
|
|
|
|
|
|
MainWindow.setCentralWidget(self.centralwidget)
|
|
|
|
|
self.menubar = QtWidgets.QMenuBar(MainWindow)
|
|
|
|
|
self.menubar.setGeometry(QtCore.QRect(0, 0, 800, 23))
|
|
|
|
|
self.menubar.setObjectName("menubar")
|
|
|
|
|
|
|
|
|
|
MainWindow.setMenuBar(self.menubar)
|
|
|
|
|
self.statusbar = QtWidgets.QStatusBar(MainWindow)
|
|
|
|
|
self.statusbar.setObjectName("statusbar")
|
|
|
|
|
|
|
|
|
|
MainWindow.setStatusBar(self.statusbar)
|
|
|
|
|
|
|
|
|
|
self.retranslateUi(MainWindow)
|
|
|
|
|
QtCore.QMetaObject.connectSlotsByName(MainWindow)
|
|
|
|
|
|
|
|
|
|
def retranslateUi(self, MainWindow):
|
|
|
|
|
_translate = QtCore.QCoreApplication.translate
|
|
|
|
|
MainWindow.setWindowTitle(_translate("MainWindow", "MainWindow"))
|
|
|
|
|
self.download.setText(_translate("MainWindow", "下载"))
|
|
|
|
|
self.zhantie.setText(_translate("MainWindow", "粘贴并下载"))
|
|
|
|
|
self.label.setText(_translate("MainWindow", "<html><head/><body><p><span style=\" color:#55ffff;\">链接:</span></p></body></html>"))
|
|
|
|
|
self.open.setText(_translate("MainWindow", "打开文件夹"))
|
|
|
|
|
self.moren.setText(_translate("MainWindow", "恢复默认"))
|
|
|
|
|
self.mulu.setText(_translate("MainWindow", "<html><head/><body><p><span style=\" color:#55ffff;\">下载目录:</span></p></body></html>"))
|
|
|
|
|
|
|
|
|
|
#将四个按钮分别连接对应的槽函数,以实现按钮的功能
|
|
|
|
|
self.moren.clicked.connect(self.moren0)
|
|
|
|
|
self.download.clicked.connect(self.xiazai0)
|
|
|
|
|
self.zhantie.clicked.connect(self.zhantie0)
|
|
|
|
|
self.open.clicked.connect(self.open0)
|
|
|
|
|
|
|
|
|
|
#self.root = os.path.abspath(os.path.dirname(__file__))#程序运行目录
|
|
|
|
|
self.root = os.getcwd() #程序运行目录
|
|
|
|
|
self.download_dir = self.root + '\download' #视频下载位置,每次成功下载视频会更新
|
|
|
|
|
self.headers = {
|
|
|
|
|
'Accept': '*/*',
|
|
|
|
|
'Accept-Language': 'en-US,en;q=0.5',
|
|
|
|
|
'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.84 Safari/537.36'
|
|
|
|
|
} # 定义请求头
|
|
|
|
|
|
|
|
|
|
self.lineEdit.setText(self.root + '\download')
|
|
|
|
|
|
|
|
|
|
def moren0(self):
|
|
|
|
|
'''
|
|
|
|
|
按钮“恢复默认”槽函数
|
|
|
|
|
'''
|
|
|
|
|
self.lineEdit.setText(self.root + '\download')#将单行文本框“lineEdit”里的内容恢复为默认下载目录
|
|
|
|
|
|
|
|
|
|
def xiazai0(self):
|
|
|
|
|
'''
|
|
|
|
|
按钮“下载”槽函数
|
|
|
|
|
'''
|
|
|
|
|
self.url = self.lineEdit_2.text()#获取单行文本框“lineEdit_2”里的内容
|
|
|
|
|
self.begin()
|
|
|
|
|
|
|
|
|
|
def zhantie0(self):
|
|
|
|
|
'''
|
|
|
|
|
按钮“粘贴并下载”槽函数
|
|
|
|
|
'''
|
|
|
|
|
self.url = get_text()
|
|
|
|
|
self.begin()
|
|
|
|
|
|
|
|
|
|
def open0(self):
|
|
|
|
|
'''
|
|
|
|
|
按钮“打开文件夹”槽函数
|
|
|
|
|
'''
|
|
|
|
|
if os.path.exists(self.download_dir):
|
|
|
|
|
os.system("explorer.exe %s" % self.download_dir)
|
|
|
|
|
else:
|
|
|
|
|
self.textEdit.append('文件路径不存在!')
|
|
|
|
|
|
|
|
|
|
def begin(self):
|
|
|
|
|
'''
|
|
|
|
|
先判断输入的合法性再开始下载
|
|
|
|
|
:return:无返回值
|
|
|
|
|
'''
|
|
|
|
|
self.output_root = self.lineEdit.text()#获取单行文本框“lineEdit”里的内容
|
|
|
|
|
try:
|
|
|
|
|
if not os.path.exists(self.output_root):
|
|
|
|
|
os.makedirs(self.output_root)
|
|
|
|
|
except:
|
|
|
|
|
return
|
|
|
|
|
self.lineEdit_2.setText(self.url) #将单行文本框“lineEdit_2”里的内容更新为url,
|
|
|
|
|
self.textEdit.clear() #在开始下载前,清除多行文本框textEdit里的内容
|
|
|
|
|
self.textEdit.repaint() #刷新多行文本框textEdit
|
|
|
|
|
self.run()
|
|
|
|
|
|
|
|
|
|
def _match(self, text, pattern):
|
|
|
|
|
match = re.search(pattern, text)
|
|
|
|
|
if match is None:
|
|
|
|
|
self.textEdit.append('此模式不匹配!\n')
|
|
|
|
|
self.textEdit.repaint()#刷新多行文本框textEdit
|
|
|
|
|
return json.loads(match.group(1))
|
|
|
|
|
|
|
|
|
|
def getHtml(self):
|
|
|
|
|
try:
|
|
|
|
|
response = requests.get(url=self.url, headers=self.headers) # 发请求,拿数据 (获取响应对象)
|
|
|
|
|
#print(f'status_code: {response.status_code}')
|
|
|
|
|
if response.status_code == 200:
|
|
|
|
|
return response
|
|
|
|
|
self.textEdit.append('html 请求错误!\n')
|
|
|
|
|
self.textEdit.repaint()#刷新多行文本框textEdit
|
|
|
|
|
return False
|
|
|
|
|
except:
|
|
|
|
|
self.textEdit.append('html 请求错误!\n')
|
|
|
|
|
self.textEdit.repaint()#刷新多行文本框textEdit
|
|
|
|
|
return False
|
|
|
|
|
|
|
|
|
|
def parseHtml(self, response):
|
|
|
|
|
playinfo = self._match(response.text, '__playinfo__=(.*?)</script><script>') # 视频详情json
|
|
|
|
|
initial_state = self._match(response.text, r'__INITIAL_STATE__=(.*?);\(function\(\)') # 视频内容json
|
|
|
|
|
|
|
|
|
|
video_url = playinfo['data']['dash']['video'][0]['baseUrl'] # 视频分多种格式,直接取分辨率最高的视频 1080p
|
|
|
|
|
audio_url = playinfo['data']['dash']['audio'][0]['baseUrl'] # 取音频地址
|
|
|
|
|
video_name = initial_state['videoData']['title'] # 取视频名字
|
|
|
|
|
return video_url, audio_url, video_name
|
|
|
|
|
|
|
|
|
|
def downloadVideo(self, video_url, audio_url, video_name):
|
|
|
|
|
self.headers.update({"Referer": self.url})
|
|
|
|
|
#print(video_url)
|
|
|
|
|
self.textEdit.append('开始下载视频: \n')
|
|
|
|
|
self.textEdit.repaint()#刷新多行文本框textEdit
|
|
|
|
|
video_content = requests.get(video_url, headers=self.headers)
|
|
|
|
|
audio_content = requests.get(audio_url, headers=self.headers)
|
|
|
|
|
self.textEdit.append('%s视频大小: ' % video_name + str(round(int(video_content.headers['content-length'])/1024**2,2)) + 'M')
|
|
|
|
|
self.textEdit.repaint()#刷新多行文本框textEdit
|
|
|
|
|
self.textEdit.append('%s音频大小: ' % video_name + str(round(int(audio_content.headers['content-length'])/1024**2,2)) + 'M\n')
|
|
|
|
|
self.textEdit.repaint()#刷新多行文本框textEdit
|
|
|
|
|
|
|
|
|
|
# 下载视频
|
|
|
|
|
received_video = 0
|
|
|
|
|
video = f'{self.output_root}/video.mp4'
|
|
|
|
|
with open(video, 'ab') as output:
|
|
|
|
|
while int(video_content.headers['content-length']) > received_video:#通过火狐浏览器的网络监视器我们可以看到,接收到的视频和音频是一段一段的m4s文件,需要循环拼接起来
|
|
|
|
|
self.headers['Range'] = 'bytes=' + str(received_video) + '-'
|
|
|
|
|
response = requests.get(video_url, headers=self.headers)
|
|
|
|
|
output.write(response.content)
|
|
|
|
|
received_video += len(response.content)
|
|
|
|
|
|
|
|
|
|
# 下载音频开始
|
|
|
|
|
audio_content = requests.get(audio_url, headers=self.headers)
|
|
|
|
|
received_audio = 0
|
|
|
|
|
audio = f'{self.output_root}/audio.mp4'
|
|
|
|
|
with open(audio, 'ab') as output:
|
|
|
|
|
while int(audio_content.headers['content-length']) > received_audio:
|
|
|
|
|
self.headers['Range'] = 'bytes=' + str(received_audio) + '-'
|
|
|
|
|
response = requests.get(audio_url, headers=self.headers)
|
|
|
|
|
output.write(response.content)
|
|
|
|
|
received_audio += len(response.content)
|
|
|
|
|
self.textEdit.append('视频下载完成\n')
|
|
|
|
|
self.textEdit.repaint()#刷新多行文本框textEdit
|
|
|
|
|
|
|
|
|
|
video_dst = f'{self.output_root}/{video_name}.mp4'
|
|
|
|
|
self.video_audio_merge(video, audio, video_dst)
|
|
|
|
|
self.download_dir = self.output_root #更新视频下载位置
|
|
|
|
|
self.textEdit.append(f'下载的视频: {video_dst}\n')
|
|
|
|
|
self.textEdit.repaint()#刷新多行文本框textEdit
|
|
|
|
|
#删除原本的视频和音频文件
|
|
|
|
|
os.remove(video)
|
|
|
|
|
os.remove(audio)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def video_audio_merge(self, video_src, audio_src, video_dst):
|
|
|
|
|
'''使用ffmpeg单个视频音频合并'''
|
|
|
|
|
cmd = f'{self.root}/ffmpeg-2022-04-18-git-d5687236ab-essentials_build/bin/ffmpeg.exe -y -i {audio_src} -i {video_src} -vcodec copy -acodec aac -strict -2 -q:v 1 {video_dst}'
|
|
|
|
|
#print('execute cmd:', cmd)
|
|
|
|
|
os.system(cmd)
|
|
|
|
|
# subprocess.Popen(command, shell=True)
|
|
|
|
|
|
|
|
|
|
def run(self):
|
|
|
|
|
response = self.getHtml()
|
|
|
|
|
if response == False:
|
|
|
|
|
return
|
|
|
|
|
video_url, audio_url, video_name = self.parseHtml(response)
|
|
|
|
|
self.downloadVideo(video_url, audio_url, video_name)
|
|
|
|
|
|
|
|
|
|
if __name__ == "__main__":
|
|
|
|
|
app = QtWidgets.QApplication(sys.argv) # 创建一个QApplication,也就是你要开发的软件app
|
|
|
|
|
MainWindow = QtWidgets.QMainWindow() # 创建一个QMainWindow,用来装载你需要的各种组件、控件
|
|
|
|
|
ui = Ui_MainWindow() # ui是你创建的ui类的实例化对象
|
|
|
|
|
ui.setupUi(MainWindow) # 执行类中的setupUi方法,方法的参数是第二步中创建的QMainWindow
|
|
|
|
|
MainWindow.show() # 执行QMainWindow的show()方法,显示这个QMainWindow
|
|
|
|
|
sys.exit(app.exec_()) # 使用exit()或者点击关闭按钮退出QApplication
|