-
Notifications
You must be signed in to change notification settings - Fork 0
/
mp3player.py
169 lines (118 loc) · 5.13 KB
/
mp3player.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
import os
import traceback
import tkinter as tk
from tkinter import *
import pygame
from pygame import mixer
from config import *
class MP3Player:
def __init__(self, dirPath: str) -> None:
from songlist import SongList
from controlmenu import ControlMenu
from timeslider import TimeSlider
from statusbar import StatusBar
master = Tk()
self.master = master
master.title(TITLE)
master.geometry(WINDOW_SIZE)
master.resizable(RESIZE_W, RESIZE_H)
master.configure(
bg=COLOR_THEME["bg_color"]
)
pygame.init()
mixer.init()
self.mixer = mixer
# --- Variables
self.dirPath = dirPath if (dirPath[-1] == '\\' or dirPath[-1] == '/') else f'{dirPath}/'
self.songs = self.getMP3()
self.songIndex = 0
self.selectedSong = None
self.volume = 50 / 100
self.isPlaying = False
self.loopEnabled = False
self.playedAnySongs = False
self.job = None
# --- Widgets
statusBar = StatusBar(mp3Player=self)
self.statusBar = statusBar
songList = SongList(mp3Player=self)
self.songList = songList
controlMenu = ControlMenu(mp3Player=self)
self.controlMenu = controlMenu
timeSlider = TimeSlider(mp3Player=self)
self.timeSlider = timeSlider
self.lyricsDisplay = None
self.master.bind('<space>', self.controlMenu.pauseResume)
def playSong(self, resetPos=True):
# To avoid index bug after refreshing from adding / removing songs
# selectedSong = self.songs[self.songIndex]
selectedSong = self.selectedSong
songPath = f'{self.dirPath}\\{selectedSong}'.replace('\\', '/')
try:
# This line might throw error
songLen = int(self.mixer.Sound(songPath).get_length())
self.playedAnySongs = True
# Cancel previous counting
if (self.job != None):
self.master.after_cancel(self.job)
self.job = None
self.isPlaying = False
# Update the total seconds / time to the new song's
self.timeSlider.updateSongLen(songLen)
# Change the window's title to the song name
self.master.title(selectedSong)
# Reset the button's text since it's playing now
self.controlMenu.pauseResumeButton.config(text=self.controlMenu.PAUSE_TEXT)
self.isPlaying = True
# Resets the counting
if (resetPos):
from lyricsdisplay import LyricsDisplay
self.lyricsDisplay = LyricsDisplay(mp3Player=self, dirPath=MP3_FOLDER_PATH, mp3Name=selectedSong)
# Clears the previous lyrics lines
self.statusBar.updateText('')
self.timeSlider.reset()
self.MUSIC_END = pygame.USEREVENT + 1
self.mixer.music.set_endevent(self.MUSIC_END)
self.mixer.music.load(songPath)
self.mixer.music.play(loops=0, start=self.timeSlider.posTime) # This throws exception too
self.mixer.music.set_volume(self.volume)
self.__countPosition()
except Exception as e:
print(traceback.format_exc())
print(f'[ERROR] Unable to open file [{selectedSong}]')
def __countPosition(self):
# For debugging, just to make sure its integer
DELAY = int(100)
# Solution:
# https://stackoverflow.com/questions/66579693/check-if-a-song-has-ended-in-pygame
for event in pygame.event.get():
if (event.type == self.MUSIC_END):
endPos = int(self.timeSlider.posTime)
songLen = int(self.timeSlider.songLen)
# Moves the position to the last second when the song is ended
if (endPos != songLen):
self.timeSlider.updatePosition(songLen)
if (self.loopEnabled):
self.playSong()
self.statusBar.updateText('')
self.timeSlider.reset()
else:
self.isPlaying = False
return
# Updates the position if it's playing
if (self.isPlaying):
newPos = self.timeSlider.posTime + (DELAY / 1000)
# Some lyrics may not be displayed with this line
# newPos = self.mixer.music.get_pos() / 1000
# print(f'newPos = {newPos} | songLen = {self.timeSlider.songLen}')
# print(f'newPos = {newPos:.1f} | get_pos() = {(self.mixer.music.get_pos() / 1000):.1f}')
self.timeSlider.updatePosition(newPos)
if (self.lyricsDisplay != None):
if (self.lyricsDisplay.hasLyrics):
self.lyricsDisplay.displayLyrics()
self.job = self.master.after(DELAY, self.__countPosition)
# For init / update
def getMP3(self):
return [i for i in os.listdir(self.dirPath) if (i.endswith('.mp3'))]
def start(self):
self.master.mainloop()