diff --git a/0.1.1.py b/0.1.1.py new file mode 100644 index 0000000..4d71959 --- /dev/null +++ b/0.1.1.py @@ -0,0 +1,283 @@ +import sys +import requests +from PyQt5.QtWidgets import ( + QApplication, QMainWindow, QWidget, QVBoxLayout, QHBoxLayout, + QTreeWidget, QTreeWidgetItem, QLineEdit, QLabel, QComboBox, + QPushButton, QSplitter, QTabWidget, QTextEdit, QMessageBox +) +from PyQt5.QtWebEngineWidgets import QWebEngineView +from PyQt5.QtCore import Qt, QUrl, pyqtSignal, QThread +from PyQt5.QtGui import QColor, QBrush + +# --- 配置区 --- +# 你的 API 基础地址 +BASE_URL = "https://shanwogou.cn/audio/" +API_URL = BASE_URL + "api.php" +USER_API_URL = BASE_URL + "user_info_api.php" +PLAY_URL_TEMPLATE = BASE_URL + "play.php?play={music_id}" + +# --- API 交互模块 --- +def fetch_api_data(url, params=None): + """通用函数,用于发送 GET 请求并解析 JSON 响应""" + try: + response = requests.get(url, params=params, timeout=15) + response.raise_for_status() + return response.json() + except requests.exceptions.RequestException as e: + print(f"API 请求失败: {e}") + return None + +def get_all_music(): + """获取所有音乐""" + return fetch_api_data(API_URL, params={"action": "getAllMusic"}) + +def get_music_tags(): + """获取所有音乐标签""" + return fetch_api_data(API_URL, params={"action": "getMusicTags"}) + +def get_announcements(): + """获取公告""" + return fetch_api_data(API_URL, params={"action": "getAnnouncements"}) + +# --- 后台数据加载线程 --- +class DataLoaderThread(QThread): + """后台线程,用于加载数据,防止UI卡顿""" + music_loaded_signal = pyqtSignal(dict) + tags_loaded_signal = pyqtSignal(dict) + announcements_loaded_signal = pyqtSignal(dict) + + def run(self): + """线程执行函数""" + # 顺序加载数据 + music_data = get_all_music() + self.music_loaded_signal.emit(music_data) + + tags_data = get_music_tags() + self.tags_loaded_signal.emit(tags_data) + + announcements_data = get_announcements() + self.announcements_loaded_signal.emit(announcements_data) + +# --- 主窗口类 --- +class SunsetMusicApp(QMainWindow): + def __init__(self): + super().__init__() + self.setWindowTitle("落日音乐 for PC") + self.setGeometry(100, 100, 1200, 700) + + # 数据存储 + self.all_music = [] + self.filtered_music = [] + + # 初始化过滤条件属性(修复错误的关键) + self.current_keyword = "" + self.current_tag = "所有标签" + + self._init_ui() + self._load_data_in_background() + + def _init_ui(self): + """初始化用户界面""" + central_widget = QWidget() + self.setCentralWidget(central_widget) + main_layout = QVBoxLayout(central_widget) + + # 1. 顶部控制区 + top_control_layout = QHBoxLayout() + + # 搜索框 + self.search_input = QLineEdit() + self.search_input.setPlaceholderText("输入关键词搜索...") + self.search_input.textChanged.connect(self._filter_music_by_search) + top_control_layout.addWidget(self.search_input) + + # 标签筛选下拉框 + self.tag_combo = QComboBox() + self.tag_combo.addItem("所有标签") + self.tag_combo.currentIndexChanged.connect(self._filter_music_by_tag) + top_control_layout.addWidget(self.tag_combo) + + # 刷新按钮 + refresh_button = QPushButton("刷新列表") + refresh_button.clicked.connect(self._load_data_in_background) + top_control_layout.addWidget(refresh_button) + + main_layout.addLayout(top_control_layout) + + # 2. 中部主内容区(分割窗口) + splitter = QSplitter(Qt.Horizontal) + + # 左侧音乐列表 + self.music_tree = QTreeWidget() + self.music_tree.setColumnCount(4) + self.music_tree.setHeaderLabels(["ID", "标题", "艺术家", "标签"]) + # 设置列宽 + self.music_tree.setColumnWidth(0, 60) + self.music_tree.setColumnWidth(1, 350) + self.music_tree.setColumnWidth(2, 200) + self.music_tree.setColumnWidth(3, 120) + # 设置为单选 + self.music_tree.setSelectionMode(QTreeWidget.SingleSelection) + self.music_tree.itemClicked.connect(self._on_music_item_clicked) + splitter.addWidget(self.music_tree) + + # 右侧内容区(标签页) + self.right_tab_widget = QTabWidget() + + # 播放页签 + self.play_web_view = QWebEngineView() + # 初始加载一个占位页面 + self.play_web_view.setUrl(QUrl("about:blank")) + self.right_tab_widget.addTab(self.play_web_view, "播放器") + + # 公告页签 + self.announcements_text = QTextEdit() + self.announcements_text.setReadOnly(True) + self.right_tab_widget.addTab(self.announcements_text, "公告") + + splitter.addWidget(self.right_tab_widget) + # 设置拉伸比例,让右侧占更多空间 + splitter.setSizes([600, 800]) + + main_layout.addWidget(splitter) + + def _load_data_in_background(self): + """启动后台线程加载数据""" + self._show_loading_indicator(True) + + self.loader_thread = DataLoaderThread() + self.loader_thread.music_loaded_signal.connect(self._on_music_data_loaded) + self.loader_thread.tags_loaded_signal.connect(self._on_tags_data_loaded) + self.loader_thread.announcements_loaded_signal.connect(self._on_announcements_data_loaded) + self.loader_thread.finished.connect(lambda: self._show_loading_indicator(False)) + self.loader_thread.start() + + def _show_loading_indicator(self, is_loading): + """显示或隐藏加载状态""" + if is_loading: + self.statusBar().showMessage("正在加载数据...") + self.setEnabled(False) # 禁用UI + else: + self.statusBar().clearMessage() + self.setEnabled(True) # 启用UI + + def _on_music_data_loaded(self, data): + """处理加载完成的音乐数据""" + if data and data.get("success", False): + self.all_music = data.get("data", []) + self.filtered_music = self.all_music.copy() + self._populate_music_tree() + print(f"成功加载 {len(self.all_music)} 首音乐。") + else: + QMessageBox.warning(self, "警告", "音乐数据加载失败!") + print("音乐数据加载失败。") + + def _on_tags_data_loaded(self, data): + """处理加载完成的标签数据""" + if data and data.get("success", False): + tags = data.get("data", []) + self.tag_combo.clear() + self.tag_combo.addItem("所有标签") + self.tag_combo.addItems(tags) + print(f"成功加载 {len(tags)} 个标签。") + else: + QMessageBox.warning(self, "警告", "标签数据加载失败!") + print("标签数据加载失败。") + + def _on_announcements_data_loaded(self, data): + """处理加载完成的公告数据""" + self.announcements_text.clear() + if data and data.get("success", False): + announcements = data.get("data", []) + for ann in announcements: + # 假设字段是 'nr' (内容) 和 'time' (时间) + content = ann.get("nr", "无内容") + time_str = ann.get("time", "未知时间") + self.announcements_text.append(f"【{time_str}】\n{content}\n") + print(f"成功加载 {len(announcements)} 条公告。") + else: + self.announcements_text.setText("公告加载失败或暂无公告。") + print("公告数据加载失败。") + + def _populate_music_tree(self): + """将音乐数据填充到 TreeWidget 中""" + self.music_tree.clear() + for music in self.filtered_music: + item = QTreeWidgetItem([ + str(music.get("id", "")), + music.get("title", "未知标题"), + music.get("artist", "未知艺术家"), + music.get("category", "未知标签") + ]) + # 存储完整音乐对象,方便后续使用 + item.setData(0, Qt.UserRole, music) + self.music_tree.addTopLevelItem(item) + + def _filter_music_by_search(self): + """根据搜索框内容过滤音乐列表""" + keyword = self.search_input.text().lower() + self._apply_filters(keyword=keyword) + + def _filter_music_by_tag(self): + """根据选择的标签过滤音乐列表""" + selected_tag = self.tag_combo.currentText() + self._apply_filters(tag=selected_tag) + + def _apply_filters(self, keyword=None, tag=None): + """应用所有过滤器(搜索词和标签)""" + # 使用类变量存储当前过滤条件 + if keyword is not None: + self.current_keyword = keyword + if tag is not None: + self.current_tag = tag + + filtered = [ + music for music in self.all_music + if (self.current_tag == "所有标签" or music.get("category", "") == self.current_tag) and + (self.current_keyword == "" or self.current_keyword in music.get("title", "").lower() or + self.current_keyword in music.get("artist", "").lower() or + self.current_keyword in music.get("category", "").lower()) + ] + + self.filtered_music = filtered + self._populate_music_tree() + self._highlight_search_results() + + def _highlight_search_results(self): + """高亮显示搜索结果中的关键词""" + keyword = self.current_keyword + if not keyword: + return + + # 注意:QTreeWidget本身不直接支持HTML格式,这里我们使用简单的颜色标记方式 + for i in range(self.music_tree.topLevelItemCount()): + item = self.music_tree.topLevelItem(i) + for col in range(item.columnCount()): + text = item.text(col) + if keyword.lower() in text.lower(): + # 找到匹配项,设置背景色 + item.setBackground(col, QBrush(QColor(255, 255, 153))) # 浅黄色背景 + else: + # 没有匹配项,恢复默认背景 + item.setBackground(col, QBrush(QColor(255, 255, 255))) + + def _on_music_item_clicked(self, item, column): + """当音乐列表项被点击时,在播放器中加载对应的播放页面""" + music = item.data(0, Qt.UserRole) + if not music: + return + + music_id = music.get("id") + if music_id: + play_url = PLAY_URL_TEMPLATE.format(music_id=music_id) + self.play_web_view.setUrl(QUrl(play_url)) + # 切换到播放器标签页 + self.right_tab_widget.setCurrentIndex(0) + print(f"正在播放: {music.get('title')} URL: {play_url}") + +# --- 程序入口 --- +if __name__ == '__main__': + app = QApplication(sys.argv) + window = SunsetMusicApp() + window.show() + sys.exit(app.exec_())