diff --git a/file/next.png b/file/next.png new file mode 100644 index 0000000..3bb67b5 Binary files /dev/null and b/file/next.png differ diff --git a/file/pause.png b/file/pause.png index 944ada1..106344d 100644 Binary files a/file/pause.png and b/file/pause.png differ diff --git a/file/play.png b/file/play.png index 7c128d1..0558620 100644 Binary files a/file/play.png and b/file/play.png differ diff --git a/file/prev.png b/file/prev.png new file mode 100644 index 0000000..9aea793 Binary files /dev/null and b/file/prev.png differ diff --git a/file/林俊杰-裂缝中的阳光.lrc b/file/林俊杰-裂缝中的阳光.lrc new file mode 100644 index 0000000..843e480 --- /dev/null +++ b/file/林俊杰-裂缝中的阳光.lrc @@ -0,0 +1,73 @@ +[ti:裂缝中的阳光] +[ar:林俊杰] +[al:因你而在] +[by:] +[offset:0] +[00:00.00]裂缝中的阳光 - 林俊杰 (JJ Lin) +[00:04.25]词:吴青峰 +[00:08.51]曲:林俊杰 +[00:12.76]编曲:林俊杰 +[00:17.02]制作人:林俊杰 +[00:21.28]有多少创伤卡在咽喉 +[00:24.27] +[00:26.76]有多少眼泪滴湿枕头 +[00:29.84] +[00:32.38]有多少你觉得不能够 +[00:35.38] +[00:36.66]被懂的痛 只能沉默 +[00:41.13] +[00:43.68]有多少夜晚没有尽头 +[00:49.15]有多少的寂寞 无人诉说 +[00:54.85]有多少次的梦 还没做 已成空 +[01:06.11]等到黑夜翻面之后 +[01:08.98]会是新的白昼 +[01:11.74]等到海啸退去之后 +[01:14.62]只是潮起潮落 +[01:18.11]别到最后你才发觉 +[01:21.59]心里头的野兽 +[01:24.53]还没到最终就已经罢休 +[01:28.83]心脏没有那么脆弱 +[01:31.62]总还会有执着 +[01:33.64] +[01:34.43]人生不会只有收获 +[01:37.21]总难免有伤口 +[01:39.91] +[01:40.66]不要害怕生命中 +[01:43.45] +[01:44.30]不完美的角落 +[01:46.35] +[01:47.09]阳光在每个裂缝中散落 +[02:00.78]呜呜 +[02:09.34]Yeah +[02:16.70]就算一切重来又怎样 +[02:21.41] +[02:22.47]让你的心在我 心上跳动 +[02:28.02]每个逐渐暗下来的夜 一起走过 +[02:37.25] +[02:39.36]等到黑夜翻面之后 +[02:42.24]会是新的白昼 +[02:44.17] +[02:45.07]等到海啸退去之后 +[02:47.80]只是潮起潮落 +[02:50.21] +[02:51.41]别到最后你才发觉 +[02:53.93] +[02:54.87]心里头的野兽 +[02:57.00] +[02:57.72]还没到最终就已经罢休 +[03:01.93]心脏没有那么脆弱 +[03:04.69]总还会有执着 +[03:06.81] +[03:07.60]人生不会只有收获 +[03:10.36]总难免有伤口 +[03:12.93] +[03:13.83]不要害怕生命中 +[03:16.57] +[03:17.47]不完美的角落 +[03:19.55] +[03:20.27]阳光在每个裂缝中散落 +[03:25.48] +[03:28.17]不如就勇敢打破 +[03:30.95] +[03:31.62]生命中的裂缝 +[03:34.46]阳光就逐渐洒满了其中 \ No newline at end of file diff --git a/file/林俊杰-裂缝中的阳光.wav b/file/林俊杰-裂缝中的阳光.wav new file mode 100644 index 0000000..f3c2b1b Binary files /dev/null and b/file/林俊杰-裂缝中的阳光.wav differ diff --git a/musicplayer.py b/musicplayer.py index dd2b0a6..a030889 100644 --- a/musicplayer.py +++ b/musicplayer.py @@ -5,7 +5,7 @@ import time import re from PyQt6.QtWidgets import ( QApplication, QMainWindow, QWidget, QVBoxLayout, QLabel, - QPushButton, QSlider, QHBoxLayout + QPushButton, QSlider, QHBoxLayout, QFileDialog ) from PyQt6.QtCore import QTimer, Qt, QSize, QPropertyAnimation, QPoint from PyQt6.QtGui import QPixmap, QIcon, QPalette, QColor @@ -18,6 +18,8 @@ class MusicPlayer(QMainWindow): self.initUI() pygame.mixer.init() + self.playlist = [] # 歌曲列表(文件路径) + self.current_song_index = -1 # 当前播放歌曲索引 self.lyrics = [] # 解析后的歌词列表 [(时间戳, 歌词)] self.current_lyric_index = 0 # 当前歌词索引 self.is_playing = False # 播放状态 @@ -26,7 +28,9 @@ class MusicPlayer(QMainWindow): self.pause_time = 0 # 暂停时记录的播放进度(秒) self.animation = None # 存储滚动动画对象 self.last_lyric = None # 上一次显示的歌词,用于避免重复启动动画 - self.load_lyrics() + self.music_file = None # 当前音乐文件路径 + self.lyrics_file = None # 当前歌词文件路径 + self.cover_file = None # 当前封面文件路径 def initUI(self): self.central_widget = QWidget() @@ -41,7 +45,11 @@ class MusicPlayer(QMainWindow): # 封面 self.cover_label = QLabel() self.layout.addWidget(self.cover_label, alignment=Qt.AlignmentFlag.AlignCenter) - self.load_cover() + + # 添加一个按钮用于加载外部音乐文件夹 + self.load_folder_button = QPushButton("选择音乐文件夹") + self.load_folder_button.clicked.connect(self.open_folder_dialog) + self.layout.addWidget(self.load_folder_button, alignment=Qt.AlignmentFlag.AlignCenter) # 歌词显示区域(固定大小容器,便于动画处理,不随内容改变大小) self.lyrics_container = QWidget() @@ -62,15 +70,29 @@ class MusicPlayer(QMainWindow): self.slider.sliderReleased.connect(self.seek_music) self.layout.addWidget(self.slider) - # 播放控制区域 + # 播放控制区域(增加上一首、播放/暂停、下一首) self.control_layout = QHBoxLayout() self.control_layout.setAlignment(Qt.AlignmentFlag.AlignCenter) + self.prev_button = QPushButton() + self.prev_button.setIcon(QIcon(self.resource_path("file/prev.png"))) + self.prev_button.setIconSize(QSize(40, 40)) + self.prev_button.setStyleSheet("border: none;") + self.prev_button.clicked.connect(self.play_previous_song) + self.control_layout.addWidget(self.prev_button) + self.play_button = QPushButton() self.play_button.setIcon(QIcon(self.resource_path("file/play.png"))) - self.play_button.setIconSize(QSize(52, 52)) # 明确设置图标尺寸 + self.play_button.setIconSize(QSize(52, 52)) self.play_button.setStyleSheet("border: none;") self.play_button.clicked.connect(self.toggle_play_pause) self.control_layout.addWidget(self.play_button) + + self.next_button = QPushButton() + self.next_button.setIcon(QIcon(self.resource_path("file/next.png"))) + self.next_button.setIconSize(QSize(40, 40)) + self.next_button.setStyleSheet("border: none;") + self.next_button.clicked.connect(self.play_next_song) + self.control_layout.addWidget(self.next_button) self.layout.addLayout(self.control_layout) # 定时器,每500毫秒更新一次进度和歌词 @@ -92,18 +114,28 @@ class MusicPlayer(QMainWindow): self.central_widget.setStyleSheet(f"background-color: {color};") def load_cover(self): - cover_path = self.resource_path("file/cover.jpg") - if os.path.exists(cover_path): - pixmap = QPixmap(cover_path) - self.cover_label.setPixmap(pixmap.scaled(256, 256, Qt.AspectRatioMode.KeepAspectRatio)) + # 如果用户选择了封面文件,优先加载,否则加载默认封面 + if self.cover_file and os.path.exists(self.cover_file): + pixmap = QPixmap(self.cover_file) else: - self.cover_label.setText("封面未找到") + cover_path = self.resource_path("file/cover.jpg") + if os.path.exists(cover_path): + pixmap = QPixmap(cover_path) + else: + self.cover_label.setText("封面未找到") + return + self.cover_label.setPixmap(pixmap.scaled(256, 256, Qt.AspectRatioMode.KeepAspectRatio)) def load_lyrics(self): - lyrics_path = self.resource_path("file/林俊杰-光阴副本.lrc") - if os.path.exists(lyrics_path): + # 根据用户选择的歌词文件加载歌词,如果没有选择则使用默认歌词文件 + if self.lyrics_file and os.path.exists(self.lyrics_file): + path = self.lyrics_file + else: + # 默认歌词文件(可以根据需要调整) + path = self.resource_path("file/默认歌词.lrc") + if os.path.exists(path): try: - with open(lyrics_path, 'r', encoding='utf-8') as file: + with open(path, 'r', encoding='utf-8') as file: lines = file.readlines() self.lyrics = [] for line in lines: @@ -120,6 +152,34 @@ class MusicPlayer(QMainWindow): else: self.lyrics_browser.setText("歌词文件未找到") + def open_folder_dialog(self): + folder_path = QFileDialog.getExistingDirectory(self, "选择音乐文件夹", "") + if folder_path: + # 读取该文件夹下所有音乐文件(支持wav和mp3) + self.playlist = [] + for file in os.listdir(folder_path): + if file.lower().endswith(('.wav', '.mp3')): + self.playlist.append(os.path.join(folder_path, file)) + self.playlist.sort() # 可按文件名排序 + if self.playlist: + # 将当前索引设置为第一首 + self.current_song_index = 0 + self.load_current_song_resources() + self.play_music() + else: + self.lyrics_browser.setText("文件夹中没有找到音乐文件") + + def load_current_song_resources(self): + # 设置当前音乐文件路径及相关资源(歌词和封面) + self.music_file = self.playlist[self.current_song_index] + base, _ = os.path.splitext(self.music_file) + lyrics_candidate = base + ".lrc" + cover_candidate = base + ".jpg" + self.lyrics_file = lyrics_candidate if os.path.exists(lyrics_candidate) else None + self.cover_file = cover_candidate if os.path.exists(cover_candidate) else None + self.load_cover() + self.load_lyrics() + def toggle_play_pause(self): if not self.is_playing: if self.pause_time > 0: @@ -130,29 +190,30 @@ class MusicPlayer(QMainWindow): self.pause_music() def play_music(self): - music_path = self.resource_path("file/林俊杰-光阴副本.wav") - if os.path.exists(music_path): + if self.music_file and os.path.exists(self.music_file): try: - pygame.mixer.music.load(music_path) + pygame.mixer.music.load(self.music_file) pygame.mixer.music.play(start=0) - self.music_length = pygame.mixer.Sound(music_path).get_length() + self.music_length = pygame.mixer.Sound(self.music_file).get_length() self.start_time = time.time() self.pause_time = 0 self.timer.start(500) self.play_button.setIcon(QIcon(self.resource_path("file/pause.png"))) self.is_playing = True except Exception as e: - pass + self.lyrics_browser.setText("播放错误: " + str(e)) + else: + self.lyrics_browser.setText("") def resume_music(self): try: - pygame.mixer.music.unpause() # 直接恢复播放 + pygame.mixer.music.unpause() self.start_time = time.time() - self.pause_time self.timer.start(500) self.play_button.setIcon(QIcon(self.resource_path("file/pause.png"))) self.is_playing = True except Exception as e: - pass + self.lyrics_browser.setText("恢复播放错误: " + str(e)) def pause_music(self): if self.is_playing: @@ -174,6 +235,18 @@ class MusicPlayer(QMainWindow): self.play_button.setIcon(QIcon(self.resource_path("file/pause.png"))) self.is_playing = True + def play_next_song(self): + if self.playlist: + self.current_song_index = (self.current_song_index + 1) % len(self.playlist) + self.load_current_song_resources() + self.play_music() + + def play_previous_song(self): + if self.playlist: + self.current_song_index = (self.current_song_index - 1) % len(self.playlist) + self.load_current_song_resources() + self.play_music() + def find_lyric_index(self, current_time): for i, (timestamp, _) in enumerate(self.lyrics): if current_time < timestamp: @@ -181,12 +254,13 @@ class MusicPlayer(QMainWindow): return len(self.lyrics) - 1 def update_lyrics_display(self): + if not self.lyrics: + self.lyrics_browser.setText("歌词为空") + return if self.current_lyric_index < len(self.lyrics): _, lyric = self.lyrics[self.current_lyric_index] - # 仅当歌词发生变化时再更新(避免重复重启动画) if self.last_lyric != lyric: self.last_lyric = lyric - # 如果歌词宽度超过容器,则滚动显示 if self.lyrics_browser.fontMetrics().horizontalAdvance(lyric) > self.lyrics_container.width(): self.scroll_lyrics(lyric) else: @@ -207,7 +281,6 @@ class MusicPlayer(QMainWindow): if self.animation is not None: self.animation.stop() self.animation = QPropertyAnimation(self.lyrics_browser, b"pos") - # 根据歌词长度调整滚动时间 duration = 8000 + (label_width - container_width) * 20 self.animation.setDuration(duration) self.animation.setStartValue(start_pos)