parent
82e91d15bb
commit
417f487f02
@ -0,0 +1,315 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
import wx
|
||||
import urllib.request
|
||||
import pygame # pip install pygame
|
||||
import os
|
||||
import re
|
||||
import time
|
||||
from threading import Thread
|
||||
import math
|
||||
import datetime
|
||||
|
||||
APP_TITLE = u'Echoe音乐播放器'
|
||||
MAX_LYRIC_ROW = 15
|
||||
LYRIC_ROW_REG = '\[[0-9]{2}:[0-9]{2}.[0-9]{2,}\]'
|
||||
MAX_MUSIC_NAME_LEN = 18
|
||||
|
||||
|
||||
class mainFrame(wx.Frame):
|
||||
|
||||
def __init__(self):
|
||||
self.width = 1280
|
||||
self.height = 720
|
||||
self.volume = 0
|
||||
self.local_music_folder = "music_folder"
|
||||
wx.Frame.__init__(self, None, -1, APP_TITLE)
|
||||
self.SetSize(self.width, self.height)
|
||||
self.SetBackgroundColour((120, 140, 140))
|
||||
|
||||
self.local_music_name_list = []
|
||||
self.lyrcis_static_text = []
|
||||
self.play_stop_button = None
|
||||
self.current_music_state = 0
|
||||
self.current_music_index = 0
|
||||
|
||||
self.get_local_music_list()
|
||||
self.current_music_static_text = None
|
||||
|
||||
self.play_bmp = wx.Image("resources/play1.bmp", wx.BITMAP_TYPE_BMP).Rescale(30, 30).ConvertToBitmap()
|
||||
self.stop_bmp = wx.Image("resources/stop.bmp", wx.BITMAP_TYPE_BMP).Rescale(30, 30).ConvertToBitmap()
|
||||
|
||||
|
||||
self.navi_panel = None
|
||||
self.draw_navi_panel()
|
||||
|
||||
self.music_list_panel = None
|
||||
self.draw_music_list_panel()
|
||||
|
||||
|
||||
self.play_music_panel = None
|
||||
self.draw_play_music_panel()
|
||||
|
||||
|
||||
self.music_lyric_panel = None
|
||||
self.draw_music_lyric_panel()
|
||||
|
||||
|
||||
self.down_music_panel = None
|
||||
self.input_url_text_ctrl = None
|
||||
self.down_button = None
|
||||
self.draw_down_music_panel()
|
||||
|
||||
pygame.mixer.init()
|
||||
self.music = pygame.mixer.music
|
||||
self.SONG_FINISHED = pygame.USEREVENT + 1
|
||||
|
||||
def get_path_by_name(self, file_name):
|
||||
|
||||
return os.path.join(self.local_music_folder, file_name)
|
||||
|
||||
def get_local_music_list(self):
|
||||
|
||||
self.local_music_name_list.clear()
|
||||
for local_music_file_name in os.listdir(self.local_music_folder):
|
||||
if local_music_file_name.endswith(".mp3"):
|
||||
self.local_music_name_list.append(local_music_file_name)
|
||||
|
||||
def draw_navi_panel(self):
|
||||
|
||||
self.navi_panel = wx.Panel(self, id=-1, pos=(0, 0), size=(100, self.height - 100))
|
||||
|
||||
local_music_text = wx.StaticText(self.navi_panel, -1, "本地音乐", pos=(20, 20), style=wx.ALIGN_LEFT)
|
||||
local_music_text.SetOwnForegroundColour((0, 0, 0))
|
||||
|
||||
def draw_music_list_panel(self):
|
||||
|
||||
self.get_local_music_list()
|
||||
|
||||
if self.music_list_panel is not None:
|
||||
self.music_list_panel.Destroy()
|
||||
self.music_list_panel = wx.Panel(self, id=-1, pos=(100, 0), size=(300, self.height - 100))
|
||||
|
||||
local_music_num = len(self.local_music_name_list)
|
||||
for music_index in range(local_music_num):
|
||||
music_full_name = self.local_music_name_list[music_index].replace(".mp3", "")
|
||||
if len(music_full_name) > MAX_MUSIC_NAME_LEN:
|
||||
music_full_name = music_full_name[0:MAX_MUSIC_NAME_LEN] + "..."
|
||||
music_text = wx.StaticText(self.music_list_panel, -1, music_full_name,
|
||||
pos=(0, music_index * 40 + 20), size=(270, 30), style=wx.ALIGN_RIGHT)
|
||||
music_text.SetOwnForegroundColour((0, 0, 0))
|
||||
music_text.Refresh()
|
||||
play_button = wx.BitmapButton(self.music_list_panel, -1, self.play_bmp, pos=(280, music_index * 40 + 20),
|
||||
size=(20, 20))
|
||||
play_button.Bind(wx.EVT_LEFT_DOWN, lambda e, index=music_index: self.play_index_music(index))
|
||||
|
||||
def draw_play_music_panel(self):
|
||||
self.play_music_panel = wx.Panel(self, id=-1, pos=(0, self.height - 100), size=(self.width, 100))
|
||||
self.current_music_static_text = wx.StaticText(self.play_music_panel, -1, "请选择歌曲",
|
||||
pos=(100, 15), size=(200, 30), style=wx.ALIGN_RIGHT)
|
||||
self.current_music_static_text.SetOwnForegroundColour((0, 0, 0))
|
||||
last_music_bpm = wx.Image("resources/last_music.bmp", wx.BITMAP_TYPE_BMP).Rescale(30, 30).ConvertToBitmap()
|
||||
next_music_bpm = wx.Image("resources/next_music.bmp", wx.BITMAP_TYPE_BMP).Rescale(30, 30).ConvertToBitmap()
|
||||
|
||||
last_music_button = wx.BitmapButton(self.play_music_panel, -1, last_music_bpm, pos=(340, 15), size=(30, 30))
|
||||
self.play_stop_button = wx.BitmapButton(self.play_music_panel, -1, self.play_bmp, pos=(380, 15), size=(30, 30))
|
||||
next_music_button = wx.BitmapButton(self.play_music_panel, -1, next_music_bpm, pos=(420, 15), size=(30, 30))
|
||||
volume_slider = wx.Slider(self.play_music_panel, -1, 50, 0, 100, pos=(500, 15), size=(570, -1), style=wx.SL_HORIZONTAL)
|
||||
|
||||
|
||||
last_music_button.Bind(wx.EVT_LEFT_DOWN, self.play_last_music)
|
||||
self.play_stop_button.Bind(wx.EVT_LEFT_DOWN, self.play_stop_music)
|
||||
next_music_button.Bind(wx.EVT_LEFT_DOWN, self.play_next_music)
|
||||
volume_slider.Bind(wx.EVT_SLIDER, self.change_volume)
|
||||
|
||||
def redraw_music_lyric_panel(self, start_index=0):
|
||||
|
||||
for x in self.lyrcis_static_text:
|
||||
x.SetLabelText("")
|
||||
x.Refresh()
|
||||
|
||||
|
||||
lyric_list = self.get_lyrics()
|
||||
|
||||
for lyric_index in range(start_index, start_index + MAX_LYRIC_ROW, 1):
|
||||
if lyric_index < len(lyric_list):
|
||||
lyric_relative_index = lyric_index - start_index
|
||||
lyric = lyric_list[lyric_index]
|
||||
self.lyrcis_static_text[lyric_relative_index].SetLabelText(lyric)
|
||||
self.lyrcis_static_text[lyric_relative_index].SetOwnForegroundColour((0, 0, 0))
|
||||
self.lyrcis_static_text[lyric_relative_index].Refresh()
|
||||
|
||||
def draw_music_lyric_panel(self):
|
||||
|
||||
self.music_lyric_panel = wx.Panel(self, id=-1, pos=(400, 60), size=(self.width - 400, self.height - 160))
|
||||
|
||||
|
||||
lyric_list = self.get_lyrics()
|
||||
|
||||
for lyric_index in range(MAX_LYRIC_ROW):
|
||||
if lyric_index < len(lyric_list):
|
||||
lyric = lyric_list[lyric_index]
|
||||
else:
|
||||
lyric = ""
|
||||
lyric_row = wx.StaticText(self.music_lyric_panel, -1, lyric, pos=(300, 30 * lyric_index + 10),
|
||||
size=(200, -1), style=wx.ALIGN_CENTER_HORIZONTAL)
|
||||
lyric_row.SetOwnForegroundColour((0, 0, 0))
|
||||
self.lyrcis_static_text.append(lyric_row)
|
||||
|
||||
def draw_down_music_panel(self):
|
||||
|
||||
self.down_music_panel = wx.Panel(self, id=-1, pos=(400, 0), size=(self.width - 400, 60))
|
||||
|
||||
self.input_url_text_ctrl = wx.TextCtrl(self.down_music_panel, -1, "请输入下载链接", pos=(100, 20), size=(600, 30))
|
||||
self.input_url_text_ctrl.SetOwnBackgroundColour((0, 0,0))
|
||||
|
||||
down_bmp = wx.Image("resources/down.bmp", wx.BITMAP_TYPE_BMP).Rescale(30, 30).ConvertToBitmap()
|
||||
self.down_button = wx.BitmapButton(self.down_music_panel, -1, down_bmp, pos=(700, 20), size=(30, 30))
|
||||
|
||||
self.down_button.Bind(wx.EVT_LEFT_DOWN, self.download_music)
|
||||
|
||||
def get_lyric_path(self):
|
||||
current_music_path = self.get_path_by_name(self.local_music_name_list[self.current_music_index])
|
||||
lyric_path = current_music_path.replace(".mp3", ".lrc")
|
||||
if os.path.exists(lyric_path):
|
||||
return lyric_path
|
||||
else:
|
||||
return None
|
||||
|
||||
def play_music(self):
|
||||
|
||||
current_music_path = self.get_path_by_name(self.local_music_name_list[self.current_music_index])
|
||||
self.music.load(current_music_path)
|
||||
|
||||
self.music.play(loops=1, start=0.0)
|
||||
|
||||
self.redraw_music_lyric_panel()
|
||||
|
||||
self.display_lyric()
|
||||
self.current_music_state = 1
|
||||
self.play_stop_button.SetBitmap(self.stop_bmp)
|
||||
|
||||
current_music_name = self.local_music_name_list[self.current_music_index].replace(".mp3", "")
|
||||
if len(current_music_name) > MAX_MUSIC_NAME_LEN:
|
||||
current_music_name = current_music_name[0:MAX_MUSIC_NAME_LEN] + "..."
|
||||
self.current_music_static_text.SetLabelText(current_music_name)
|
||||
|
||||
def play_index_music(self, music_index):
|
||||
|
||||
self.current_music_index = music_index
|
||||
|
||||
self.play_music()
|
||||
|
||||
def play_stop_music(self, evt):
|
||||
if self.music.get_busy():
|
||||
if 1 == self.current_music_state:
|
||||
print("有音乐在播放,需要暂停")
|
||||
self.music.pause()
|
||||
self.current_music_state = 0
|
||||
self.play_stop_button.SetBitmap(self.play_bmp)
|
||||
else:
|
||||
self.music.unpause()
|
||||
self.current_music_state = 1
|
||||
self.play_stop_button.SetBitmap(self.stop_bmp)
|
||||
else:
|
||||
self.play_music()
|
||||
|
||||
def play_last_music(self, evt):
|
||||
|
||||
if self.current_music_index > 0:
|
||||
self.play_index_music(self.current_music_index - 1)
|
||||
else:
|
||||
self.play_index_music(0)
|
||||
|
||||
def play_next_music(self, evt):
|
||||
|
||||
if self.current_music_index < len(self.local_music_name_list) - 1:
|
||||
self.play_index_music(self.current_music_index + 1)
|
||||
else:
|
||||
self.play_index_music(len(self.local_music_name_list) - 1)
|
||||
|
||||
def change_volume(self, evt):
|
||||
|
||||
obj = evt.GetEventObject()
|
||||
val = obj.GetValue()
|
||||
self.volume = float(val / 100)
|
||||
self.music.set_volume(self.volume)
|
||||
|
||||
def get_lyrics(self):
|
||||
|
||||
current_lyric_path = self.get_lyric_path()
|
||||
if current_lyric_path is None or not os.path.exists(current_lyric_path):
|
||||
return ["暂无歌词"]
|
||||
with open(current_lyric_path, 'r', encoding="utf-8") as file_pointer:
|
||||
content_list = file_pointer.readlines()
|
||||
lyrics_list = []
|
||||
for content in content_list:
|
||||
if re.match(LYRIC_ROW_REG, content):
|
||||
|
||||
index_of_right_blank = content.index(']')
|
||||
lyric_clause = content.replace('\n', '')[index_of_right_blank + 1:]
|
||||
lyrics_list.append(lyric_clause)
|
||||
return lyrics_list
|
||||
|
||||
def display_lyric(self):
|
||||
lyric_refersh_thread = Thread(target=self.refersh_lyrics)
|
||||
lyric_refersh_thread.start()
|
||||
|
||||
def parse_lyrics(self):
|
||||
current_lyric_path = self.get_lyric_path()
|
||||
if current_lyric_path is None or not os.path.exists(current_lyric_path):
|
||||
content_list = ["[00:00.00]暂无歌词"]
|
||||
else:
|
||||
|
||||
with open(current_lyric_path, 'r', encoding="utf-8") as file_pointer:
|
||||
content_list = file_pointer.readlines()
|
||||
lyrics_list = []
|
||||
for content in content_list:
|
||||
if re.match(LYRIC_ROW_REG, content):
|
||||
time_lyric = dict()
|
||||
start_time = float(content[1:3]) * 60 + float(content[4:6]) + float(content[7:9]) / 100
|
||||
index_of_right_blank = content.index(']')
|
||||
time_lyric[start_time] = content.replace('\n', '')[index_of_right_blank + 1:]
|
||||
lyrics_list.append(time_lyric)
|
||||
return lyrics_list
|
||||
|
||||
def refersh_lyrics(self):
|
||||
|
||||
lyrics_time_dict_list = self.parse_lyrics()
|
||||
relative_start_index = 0
|
||||
while self.music.get_busy():
|
||||
current_time = float(self.music.get_pos() / 1000)
|
||||
for lyric_index, lyrics_time_dict in enumerate(lyrics_time_dict_list):
|
||||
lyric_time = list(lyrics_time_dict.keys())[0]
|
||||
if math.fabs(lyric_time - current_time) < 0.7:
|
||||
|
||||
if lyric_index > 0 and lyric_index % MAX_LYRIC_ROW == 0:
|
||||
relative_start_index = lyric_index
|
||||
self.redraw_music_lyric_panel(start_index=relative_start_index)
|
||||
self.lyrcis_static_text[lyric_index - relative_start_index].SetOwnForegroundColour((0, 0, 0))
|
||||
|
||||
self.lyrcis_static_text[lyric_index - relative_start_index].Refresh()
|
||||
break
|
||||
time.sleep(1)
|
||||
|
||||
def download_music(self, evt):
|
||||
|
||||
|
||||
music_down_url = self.input_url_text_ctrl.GetValue()
|
||||
|
||||
down_music_path = os.path.join(self.local_music_folder, datetime.datetime.now().strftime('%Y-%m-%d-%H-%M-%S') + ".mp3")
|
||||
before_music_name = self.local_music_name_list[self.current_music_index]
|
||||
|
||||
urllib.request.urlretrieve(music_down_url, down_music_path)
|
||||
|
||||
self.draw_music_list_panel()
|
||||
|
||||
for index in range(len(self.local_music_name_list)):
|
||||
if before_music_name == self.local_music_name_list[index]:
|
||||
self.current_music_index = index
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
app = wx.App()
|
||||
frame = mainFrame()
|
||||
frame.Show()
|
||||
app.MainLoop()
|
||||
Loading…
Reference in new issue