#version 1.1
import os
import sys
import time
import re
import json
import requests
from PyQt5.QtGui import QFont, QIcon
from bs4 import BeautifulSoup
from PyQt5.QtWidgets import (QApplication, QMainWindow, QWidget, QVBoxLayout, QHBoxLayout,
QLabel, QLineEdit, QPushButton, QListWidget, QListWidgetItem,
QProgressBar, QFileDialog, QMessageBox,
QStatusBar, QCheckBox)
from PyQt5.QtCore import Qt, QThread, pyqtSignal
from requests import Session
from requests.adapters import HTTPAdapter
from urllib3 import Retry
class MusicDownloaderThread(QThread):
"""多线程下载类"""
progress_signal = pyqtSignal(int, int, int) # 当前下载序号, 总数量, 进度百分比
finished_signal = pyqtSignal(int, str, bool) # 索引, 文件名, 是否成功
error_signal = pyqtSignal(str)
def __init__(self, music_list, download_path):
super().__init__()
self.music_list = music_list
self.download_path = download_path
self.session = Session()
self.running = True
self.headers = {
'Referer': '',
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36',
}
def run(self):
"""执行下载任务"""
total = len(self.music_list)
for idx, (name, url) in enumerate(self.music_list):
if not self.running:
break
self.progress_signal.emit(idx + 1, total, 0)
try:
# 获取音乐ID
ask_id = re.compile('')
music_id = re.findall(ask_id, url)[0]
# 获取实际下载链接
music_data = self.session.post(
'',
headers=self.headers,
data={'id': music_id, 'type': 'music'},
timeout=30
).json()
# 下载音乐
music_url = music_data.get('url')
filename = re.sub(r'[\\/*?:"<>|]', "", name) + '.mp3'
filepath = os.path.join(self.download_path, filename)
response = self.session.get(
music_url,
headers={'User-Agent': self.headers['User-Agent']},
stream=True,
timeout=60
)
# 带进度下载
total_size = int(response.headers.get('content-length', 0))
downloaded = 0
with open(filepath, 'wb') as f:
for chunk in response.iter_content(chunk_size=1024):
if not self.running:
break
if chunk:
f.write(chunk)
downloaded += len(chunk)
progress = int((downloaded / total_size) * 100) if total_size > 0 else 0
self.progress_signal.emit(idx + 1, total, progress)
if self.running:
self.finished_signal.emit(idx, filename, True)
except Exception as e:
if self.running:
self.finished_signal.emit(idx, name, False)
self.error_signal.emit(f"下载失败: {name} - {str(e)}")
def stop(self):
"""停止下载"""
self.running = False
class MusicDownloaderApp(QMainWindow):
"""音乐下载器主界面"""
def __init__(self):
super().__init__()
# 设置默认下载路径
self.default_path = "E:\\music"
self.setWindowIcon(QIcon("music.ico")) # 支持.ico、.png等格式[3,4,6](@ref)
# self.initUI()
# 尝试从文件加载保存的路径
self.download_path = self.load_saved_path()
os.makedirs(self.download_path, exist_ok=True)
self.init_ui()
self.setup_connections()
# 初始化会话
self.session = Session()
self.session_ask()
def load_saved_path(self):
"""从文件加载保存的下载路径"""
try:
if os.path.exists("download_path.txt"):
with open("download_path.txt", "r") as f:
saved_path = f.read().strip()
if saved_path and os.path.isdir(saved_path):
return saved_path
except:
pass
return self.default_path
def save_path_to_file(self, path):
"""保存路径到文件"""
try:
with open("download_path.txt", "w") as f:
f.write(path)
except Exception as e:
self.show_error(f"保存路径失败: {str(e)}")
def init_ui(self):
"""初始化用户界面"""
self.setWindowTitle("音乐下载器")
self.setGeometry(300, 200, 800, 500)
# 主布局
main_widget = QWidget()
self.setCentralWidget(main_widget)
main_layout = QVBoxLayout(main_widget)
# 顶部搜索区域
search_layout = QHBoxLayout()
self.search_input = QLineEdit()
self.search_input.setPlaceholderText("请输入歌曲名称或歌手...")
search_layout.addWidget(self.search_input)
self.search_btn = QPushButton("搜索")
search_layout.addWidget(self.search_btn)
# 下载路径选择
self.path_btn = QPushButton("选择下载路径")
search_layout.addWidget(self.path_btn)
# 显示当前路径
self.path_label = QLabel(f"当前下载路径: {self.download_path}")
search_layout.addWidget(self.path_label)
main_layout.addLayout(search_layout)
# 搜索结果区域
result_group = QVBoxLayout()
result_group.addWidget(QLabel("搜索结果"))
self.result_list = QListWidget()
self.result_list.setSelectionMode(QListWidget.ExtendedSelection) # 多选模式
result_group.addWidget(self.result_list)
# 下载控制
download_control_layout = QHBoxLayout()
self.download_btn = QPushButton("下载选中")
self.select_all_btn = QPushButton("全选")
download_control_layout.addWidget(self.select_all_btn)
download_control_layout.addWidget(self.download_btn)
result_group.addLayout(download_control_layout)
# 批量下载选项
self.batch_download_check = QCheckBox("批量下载模式")
result_group.addWidget(self.batch_download_check)
main_layout.addLayout(result_group, 1)
# 进度条
self.progress_bar = QProgressBar()
self.progress_bar.setVisible(False)
main_layout.addWidget(self.progress_bar)
# 状态栏 - 版权声明
self.status_bar = QStatusBar()
self.setStatusBar(self.status_bar)
copyright_label = QLabel("该软件仅供学习和娱乐,如有侵权请联系删除")
copyright_label.setFont(QFont("Arial", 8))
self.status_bar.addPermanentWidget(copyright_label)
def setup_connections(self):
"""设置信号连接"""
self.search_btn.clicked.connect(self.search_music)
self.download_btn.clicked.connect(self.download_selected)
self.select_all_btn.clicked.connect(self.select_all_results)
self.path_btn.clicked.connect(self.select_download_path)
def session_ask(self):
"""初始化会话"""
try:
url = ''
self.session.get(url=url, headers={
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36'
}, timeout=10)
except Exception as e:
self.show_error(f"初始化失败: {str(e)}")
def search_music(self):
"""搜索音乐"""
keyword = self.search_input.text().strip()
if not keyword:
self.show_warning("请输入搜索关键词")
return
self.result_list.clear()
self.search_btn.setEnabled(False)
self.search_btn.setText("搜索中...")
try:
# 配置请求重试策略
retry_strategy = Retry(
total=3, # 最大重试次数
backoff_factor=1, # 指数退避因子
status_forcelist=[500, 502, 503, 504], # 触发重试的状态码
allowed_methods=["GET"] # 只对GET请求重试
)
adapter = HTTPAdapter(max_retries=retry_strategy)
self.session.mount("http://", adapter)
self.session.mount("https://", adapter)
play_list = [] # 存储所有<li>元素
for page in range(1, 10):
query_url = f'。。。。/{keyword}/{page}.html'
#print(f"正在请求: {query_url}")
for attempt in range(3): # 每页最多重试3次
try:
response = self.session.get(
query_url,
headers={
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36'
},
timeout=15 # 更合理的超时时间
)
response.raise_for_status() # 检查HTTP状态码[5,9](@ref)
soup = BeautifulSoup(response.text, 'html.parser')
container = soup.find('div', class_='play_list')
# 检查容器是否存在[1,5](@ref)
if container:
query_list = container.find_all('li')
#print(f"第{page}页解析到{len(query_list)}个元素")
play_list.extend(query_list) # 扁平化存储元素
break # 成功则跳出重试循环
else:
print(f"警告:第{page}页未找到play_list容器")
except requests.exceptions.RequestException as e:
print(f"请求异常(尝试{attempt + 1}/3): {e}")
time.sleep(2 ** attempt) # 指数退避等待[9](@ref)
except Exception as e:
print(f"解析异常: {e}")
break
else: # 重试全部失败
pass
status_bar = self.statusBar()
# 检查是否已存在左下角标签
if hasattr(self, 'left_label') and self.left_label is not None:
# 从状态栏移除旧标签
status_bar.removeWidget(self.left_label) # 关键操作[7](@ref)
# 安全销毁旧标签对象
self.left_label.deleteLater()
self.left_label = None
# 创建新标签(带红色字体)
self.left_label = QLabel(f"总共获取到{len(play_list)}个结果")
self.left_label.setStyleSheet("color: red; font: 8pt 'Arial';") # 设置红色字体
# 添加到左下角(左侧区域)
status_bar.addWidget(self.left_label) # 默认左下角[1,7](@ref)
if play_list:
for idx, li in enumerate(play_list, 1):
try:
link = li.find('a')
if link: # 检查<a>标签是否存在[1](@ref)
name = link.text.strip()
href = '' + link['href']
list_item = QListWidgetItem(f"{idx}. {name}")
list_item.setData(Qt.UserRole, (name, href))
self.result_list.addItem(list_item)
except TypeError: # 处理NoneType异常[1](@ref)
print(f"无效的<li>元素: {li}")
else:
self.show_info("未找到相关结果")
except Exception as e:
self.show_error(f"搜索失败: {str(e)}")
import traceback
traceback.print_exc() # 打印完整堆栈[2](@ref)
finally:
self.search_btn.setEnabled(True)
self.search_btn.setText("搜索")
def download_selected(self):
"""下载选中的音乐"""
selected_items = self.result_list.selectedItems()
if not selected_items:
self.show_warning("请先选择要下载的歌曲")
return
# 准备下载列表
download_list = []
for item in selected_items:
name, url = item.data(Qt.UserRole)
download_list.append((name, url))
# 批量下载模式
if self.batch_download_check.isChecked():
self.start_download_thread(download_list)
else:
# 单曲下载模式
for name, url in download_list:
self.start_download_thread([(name, url)])
def start_download_thread(self, download_list):
"""启动下载线程"""
if not download_list:
return
# 创建并启动下载线程
self.download_thread = MusicDownloaderThread(download_list, self.download_path)
self.download_thread.progress_signal.connect(self.update_download_progress)
self.download_thread.finished_signal.connect(self.download_finished)
self.download_thread.error_signal.connect(self.show_error)
self.download_thread.start()
# 显示进度条
self.progress_bar.setVisible(True)
self.progress_bar.setRange(0, 100)
self.progress_bar.setValue(0)
self.download_btn.setEnabled(False)
def update_download_progress(self, current, total, progress):
"""更新下载进度"""
self.progress_bar.setFormat(f"下载中: {current}/{total} ({progress}%)")
self.progress_bar.setValue(progress)
def download_finished(self, index, filename, success):
"""下载完成处理"""
if success:
self.show_info(f"下载完成: {filename}")
# 检查是否全部完成
if index == len(self.download_thread.music_list) - 1:
self.progress_bar.setVisible(False)
self.download_btn.setEnabled(True)
self.show_info("所有下载任务已完成!")
def select_download_path(self):
"""选择下载路径"""
path = QFileDialog.getExistingDirectory(
self,
"选择下载目录",
self.download_path,
QFileDialog.ShowDirsOnly
)
if path:
self.download_path = path
self.path_label.setText(f"当前下载路径: {path}")
self.save_path_to_file(path)
self.show_info(f"下载路径已设置为: {path}")
def select_all_results(self):
"""全选搜索结果"""
self.result_list.selectAll()
def show_error(self, message):
"""显示错误信息"""
QMessageBox.critical(self, "错误", message)
def show_warning(self, message):
"""显示警告信息"""
QMessageBox.warning(self, "警告", message)
def show_info(self, message):
"""显示信息"""
QMessageBox.information(self, "提示", message)
def closeEvent(self, event):
"""关闭窗口时的处理"""
if hasattr(self, 'download_thread') and self.download_thread.isRunning():
self.download_thread.stop()
self.download_thread.wait()
event.accept()
if __name__ == "__main__":
app = QApplication(sys.argv)
downloader = MusicDownloaderApp()
downloader.show()
sys.exit(app.exec_())
通过网盘分享的文件:音乐下载器 1.0.1.rar
链接: pan.baidu.com 提取码: hrrp
可批量下载,批量下载前需要勾选批量下载框,可自定义保存路径
import os
import sys
import time
import re
import json
import requests
from PyQt5.QtGui import QFont, QIcon
from bs4 import BeautifulSoup
from PyQt5.QtWidgets import (QApplication, QMainWindow, QWidget, QVBoxLayout, QHBoxLayout,
QLabel, QLineEdit, QPushButton, QListWidget, QListWidgetItem,
QProgressBar, QFileDialog, QMessageBox,
QStatusBar, QCheckBox)
from PyQt5.QtCore import Qt, QThread, pyqtSignal
from requests import Session
from requests.adapters import HTTPAdapter
from urllib3 import Retry
class MusicDownloaderThread(QThread):
"""多线程下载类"""
progress_signal = pyqtSignal(int, int, int) # 当前下载序号, 总数量, 进度百分比
finished_signal = pyqtSignal(int, str, bool) # 索引, 文件名, 是否成功
error_signal = pyqtSignal(str)
def __init__(self, music_list, download_path):
super().__init__()
self.music_list = music_list
self.download_path = download_path
self.session = Session()
self.running = True
self.headers = {
'Referer': '',
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36',
}
def run(self):
"""执行下载任务"""
total = len(self.music_list)
for idx, (name, url) in enumerate(self.music_list):
if not self.running:
break
self.progress_signal.emit(idx + 1, total, 0)
try:
# 获取音乐ID
ask_id = re.compile('')
music_id = re.findall(ask_id, url)[0]
# 获取实际下载链接
music_data = self.session.post(
'',
headers=self.headers,
data={'id': music_id, 'type': 'music'},
timeout=30
).json()
# 下载音乐
music_url = music_data.get('url')
filename = re.sub(r'[\\/*?:"<>|]', "", name) + '.mp3'
filepath = os.path.join(self.download_path, filename)
response = self.session.get(
music_url,
headers={'User-Agent': self.headers['User-Agent']},
stream=True,
timeout=60
)
# 带进度下载
total_size = int(response.headers.get('content-length', 0))
downloaded = 0
with open(filepath, 'wb') as f:
for chunk in response.iter_content(chunk_size=1024):
if not self.running:
break
if chunk:
f.write(chunk)
downloaded += len(chunk)
progress = int((downloaded / total_size) * 100) if total_size > 0 else 0
self.progress_signal.emit(idx + 1, total, progress)
if self.running:
self.finished_signal.emit(idx, filename, True)
except Exception as e:
if self.running:
self.finished_signal.emit(idx, name, False)
self.error_signal.emit(f"下载失败: {name} - {str(e)}")
def stop(self):
"""停止下载"""
self.running = False
class MusicDownloaderApp(QMainWindow):
"""音乐下载器主界面"""
def __init__(self):
super().__init__()
# 设置默认下载路径
self.default_path = "E:\\music"
self.setWindowIcon(QIcon("music.ico")) # 支持.ico、.png等格式[3,4,6](@ref)
# self.initUI()
# 尝试从文件加载保存的路径
self.download_path = self.load_saved_path()
os.makedirs(self.download_path, exist_ok=True)
self.init_ui()
self.setup_connections()
# 初始化会话
self.session = Session()
self.session_ask()
def load_saved_path(self):
"""从文件加载保存的下载路径"""
try:
if os.path.exists("download_path.txt"):
with open("download_path.txt", "r") as f:
saved_path = f.read().strip()
if saved_path and os.path.isdir(saved_path):
return saved_path
except:
pass
return self.default_path
def save_path_to_file(self, path):
"""保存路径到文件"""
try:
with open("download_path.txt", "w") as f:
f.write(path)
except Exception as e:
self.show_error(f"保存路径失败: {str(e)}")
def init_ui(self):
"""初始化用户界面"""
self.setWindowTitle("音乐下载器")
self.setGeometry(300, 200, 800, 500)
# 主布局
main_widget = QWidget()
self.setCentralWidget(main_widget)
main_layout = QVBoxLayout(main_widget)
# 顶部搜索区域
search_layout = QHBoxLayout()
self.search_input = QLineEdit()
self.search_input.setPlaceholderText("请输入歌曲名称或歌手...")
search_layout.addWidget(self.search_input)
self.search_btn = QPushButton("搜索")
search_layout.addWidget(self.search_btn)
# 下载路径选择
self.path_btn = QPushButton("选择下载路径")
search_layout.addWidget(self.path_btn)
# 显示当前路径
self.path_label = QLabel(f"当前下载路径: {self.download_path}")
search_layout.addWidget(self.path_label)
main_layout.addLayout(search_layout)
# 搜索结果区域
result_group = QVBoxLayout()
result_group.addWidget(QLabel("搜索结果"))
self.result_list = QListWidget()
self.result_list.setSelectionMode(QListWidget.ExtendedSelection) # 多选模式
result_group.addWidget(self.result_list)
# 下载控制
download_control_layout = QHBoxLayout()
self.download_btn = QPushButton("下载选中")
self.select_all_btn = QPushButton("全选")
download_control_layout.addWidget(self.select_all_btn)
download_control_layout.addWidget(self.download_btn)
result_group.addLayout(download_control_layout)
# 批量下载选项
self.batch_download_check = QCheckBox("批量下载模式")
result_group.addWidget(self.batch_download_check)
main_layout.addLayout(result_group, 1)
# 进度条
self.progress_bar = QProgressBar()
self.progress_bar.setVisible(False)
main_layout.addWidget(self.progress_bar)
# 状态栏 - 版权声明
self.status_bar = QStatusBar()
self.setStatusBar(self.status_bar)
copyright_label = QLabel("该软件仅供学习和娱乐,如有侵权请联系删除")
copyright_label.setFont(QFont("Arial", 8))
self.status_bar.addPermanentWidget(copyright_label)
def setup_connections(self):
"""设置信号连接"""
self.search_btn.clicked.connect(self.search_music)
self.download_btn.clicked.connect(self.download_selected)
self.select_all_btn.clicked.connect(self.select_all_results)
self.path_btn.clicked.connect(self.select_download_path)
def session_ask(self):
"""初始化会话"""
try:
url = ''
self.session.get(url=url, headers={
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36'
}, timeout=10)
except Exception as e:
self.show_error(f"初始化失败: {str(e)}")
def search_music(self):
"""搜索音乐"""
keyword = self.search_input.text().strip()
if not keyword:
self.show_warning("请输入搜索关键词")
return
self.result_list.clear()
self.search_btn.setEnabled(False)
self.search_btn.setText("搜索中...")
try:
# 配置请求重试策略
retry_strategy = Retry(
total=3, # 最大重试次数
backoff_factor=1, # 指数退避因子
status_forcelist=[500, 502, 503, 504], # 触发重试的状态码
allowed_methods=["GET"] # 只对GET请求重试
)
adapter = HTTPAdapter(max_retries=retry_strategy)
self.session.mount("http://", adapter)
self.session.mount("https://", adapter)
play_list = [] # 存储所有<li>元素
for page in range(1, 10):
query_url = f'。。。。/{keyword}/{page}.html'
#print(f"正在请求: {query_url}")
for attempt in range(3): # 每页最多重试3次
try:
response = self.session.get(
query_url,
headers={
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36'
},
timeout=15 # 更合理的超时时间
)
response.raise_for_status() # 检查HTTP状态码[5,9](@ref)
soup = BeautifulSoup(response.text, 'html.parser')
container = soup.find('div', class_='play_list')
# 检查容器是否存在[1,5](@ref)
if container:
query_list = container.find_all('li')
#print(f"第{page}页解析到{len(query_list)}个元素")
play_list.extend(query_list) # 扁平化存储元素
break # 成功则跳出重试循环
else:
print(f"警告:第{page}页未找到play_list容器")
except requests.exceptions.RequestException as e:
print(f"请求异常(尝试{attempt + 1}/3): {e}")
time.sleep(2 ** attempt) # 指数退避等待[9](@ref)
except Exception as e:
print(f"解析异常: {e}")
break
else: # 重试全部失败
pass
status_bar = self.statusBar()
# 检查是否已存在左下角标签
if hasattr(self, 'left_label') and self.left_label is not None:
# 从状态栏移除旧标签
status_bar.removeWidget(self.left_label) # 关键操作[7](@ref)
# 安全销毁旧标签对象
self.left_label.deleteLater()
self.left_label = None
# 创建新标签(带红色字体)
self.left_label = QLabel(f"总共获取到{len(play_list)}个结果")
self.left_label.setStyleSheet("color: red; font: 8pt 'Arial';") # 设置红色字体
# 添加到左下角(左侧区域)
status_bar.addWidget(self.left_label) # 默认左下角[1,7](@ref)
if play_list:
for idx, li in enumerate(play_list, 1):
try:
link = li.find('a')
if link: # 检查<a>标签是否存在[1](@ref)
name = link.text.strip()
href = '' + link['href']
list_item = QListWidgetItem(f"{idx}. {name}")
list_item.setData(Qt.UserRole, (name, href))
self.result_list.addItem(list_item)
except TypeError: # 处理NoneType异常[1](@ref)
print(f"无效的<li>元素: {li}")
else:
self.show_info("未找到相关结果")
except Exception as e:
self.show_error(f"搜索失败: {str(e)}")
import traceback
traceback.print_exc() # 打印完整堆栈[2](@ref)
finally:
self.search_btn.setEnabled(True)
self.search_btn.setText("搜索")
def download_selected(self):
"""下载选中的音乐"""
selected_items = self.result_list.selectedItems()
if not selected_items:
self.show_warning("请先选择要下载的歌曲")
return
# 准备下载列表
download_list = []
for item in selected_items:
name, url = item.data(Qt.UserRole)
download_list.append((name, url))
# 批量下载模式
if self.batch_download_check.isChecked():
self.start_download_thread(download_list)
else:
# 单曲下载模式
for name, url in download_list:
self.start_download_thread([(name, url)])
def start_download_thread(self, download_list):
"""启动下载线程"""
if not download_list:
return
# 创建并启动下载线程
self.download_thread = MusicDownloaderThread(download_list, self.download_path)
self.download_thread.progress_signal.connect(self.update_download_progress)
self.download_thread.finished_signal.connect(self.download_finished)
self.download_thread.error_signal.connect(self.show_error)
self.download_thread.start()
# 显示进度条
self.progress_bar.setVisible(True)
self.progress_bar.setRange(0, 100)
self.progress_bar.setValue(0)
self.download_btn.setEnabled(False)
def update_download_progress(self, current, total, progress):
"""更新下载进度"""
self.progress_bar.setFormat(f"下载中: {current}/{total} ({progress}%)")
self.progress_bar.setValue(progress)
def download_finished(self, index, filename, success):
"""下载完成处理"""
if success:
self.show_info(f"下载完成: {filename}")
# 检查是否全部完成
if index == len(self.download_thread.music_list) - 1:
self.progress_bar.setVisible(False)
self.download_btn.setEnabled(True)
self.show_info("所有下载任务已完成!")
def select_download_path(self):
"""选择下载路径"""
path = QFileDialog.getExistingDirectory(
self,
"选择下载目录",
self.download_path,
QFileDialog.ShowDirsOnly
)
if path:
self.download_path = path
self.path_label.setText(f"当前下载路径: {path}")
self.save_path_to_file(path)
self.show_info(f"下载路径已设置为: {path}")
def select_all_results(self):
"""全选搜索结果"""
self.result_list.selectAll()
def show_error(self, message):
"""显示错误信息"""
QMessageBox.critical(self, "错误", message)
def show_warning(self, message):
"""显示警告信息"""
QMessageBox.warning(self, "警告", message)
def show_info(self, message):
"""显示信息"""
QMessageBox.information(self, "提示", message)
def closeEvent(self, event):
"""关闭窗口时的处理"""
if hasattr(self, 'download_thread') and self.download_thread.isRunning():
self.download_thread.stop()
self.download_thread.wait()
event.accept()
if __name__ == "__main__":
app = QApplication(sys.argv)
downloader = MusicDownloaderApp()
downloader.show()
sys.exit(app.exec_())
通过网盘分享的文件:音乐下载器 1.0.1.rar
链接: pan.baidu.com 提取码: hrrp
可批量下载,批量下载前需要勾选批量下载框,可自定义保存路径