直接上代码:
# -*- coding: utf-8 -*-
import pygame,os,sys,random,wx,re,asyncio
from mutagen.mp3 import MP3
import wx.lib.agw.aui as aui
from playwright.async_api import async_playwright
class panel1(wx.Panel):#初始化面板
def __init__(self,parent):
wx.Panel.__init__(self,parent,id=wx.ID_ANY,pos=wx.DefaultPosition,size=(700,400))
pygame.init()
pygame.mixer.init()
self.playing = False
self.index = 0 #初始化播放歌曲索引
self.music = None #初始化播放歌曲
self.dic = {} #初始化歌曲字典
self.click = 0 #初始化播放模式
self.dura = 0 #初始化歌曲时长
self._init_ui() #初始化界面
def _init_ui(self):
self.list=wx.ListCtrl(self,-1,(0,25),(200,300),wx.LC_REPORT,name=u'音乐列表')
self.st=wx.StaticText(self ,1 ,u'请先导入音乐列表')
self.tc = wx.TextCtrl(self, -1,u'F:\music')
self.tc.SetForegroundColour('blue')
self.playbutton = wx.Button(self , 102 , u'播放')#初始化播放/暂停按钮
self.modebutton = wx.Button(self , 104 , u'顺序播放')#初始化播放模式
hbox = wx.BoxSizer(wx.HORIZONTAL)
lhbox = wx.BoxSizer(wx.HORIZONTAL)#左边横向BOX
rhbox = wx.BoxSizer(wx.HORIZONTAL)
vbox0 = wx.BoxSizer(wx.VERTICAL) #第一列
vbox1 = wx.BoxSizer(wx.VERTICAL)
hbox.Add(vbox0)
hbox.Add(vbox1)
vbox0.Add(self.list)
vbox0.Add(self.st)
vbox0.Add(lhbox)
vbox1.Add(rhbox)
lhbox.Add(self.tc)
lhbox.Add(wx.Button(self, 100, u'导入'), 1, 10)
rhbox.Add(wx.Button(self , 101 , u'上一曲') , 1 , wx.ALL, 5)
rhbox.Add(self.playbutton , 102 , wx.ALL, 5)
rhbox.Add(wx.Button(self , 103 , u'下一曲'), 1 , wx.ALL, 5)
rhbox.Add(self.modebutton , 1 , wx.ALL, 5)
self.slider = wx.Slider(self,-1,50,0,100)
rhbox.Add(self.slider, 1, wx.ALL, 5)
self.progress = wx.Gauge(self,pos=wx.DefaultPosition, size=wx.Size( 450,2), style=wx.GA_HORIZONTAL)
vbox1.Add(self.progress,1, wx.ALL, 5)
self.sst = wx.StaticText(self ,1 ,u'当前未播放')
vbox1.Add(self.sst,1, wx.ALL, 5)
self.lyc = wx.StaticText(self ,wx.ID_ANY ,u'', wx.DefaultPosition, wx.Size( 450,30))
self.lyc.SetFont( wx.Font( 18, wx.FONTFAMILY_MODERN, wx.FONTSTYLE_NORMAL, wx.FONTWEIGHT_NORMAL, False, "楷体" ) )
self.lyc.SetForegroundColour('BLUE' )
vbox1.Add(self.lyc,0, wx.EXPAND|wx.ALL, 5)
self.lyc2 = wx.StaticText(self ,wx.ID_ANY ,u'\t', wx.DefaultPosition, wx.Size( 450,60))
self.lyc2.SetFont( wx.Font( 18, wx.FONTFAMILY_MODERN, wx.FONTSTYLE_NORMAL, wx.FONTWEIGHT_NORMAL, False, "楷体" ) )
# self.lyc2.SetForegroundColour( wx.Colour(255 ,102,204 ) )
self.lyc2.SetForegroundColour('green' )
vbox1.Add(self.lyc2,0, wx.EXPAND|wx.ALL, 5)
self.SetSizer(hbox)
self.Layout()
self.Bind(wx.EVT_BUTTON, self.btn_handler)
self.slider.Bind(wx.EVT_SLIDER,self.change_volume)
def CreatListctrl(self,dic): #播放列表
#播放列表
self.list.ClearAll()
self.list.InsertColumn(0,u'序号',width=25)
self.list.InsertColumn(1,u'音乐',width=130)
self.list.InsertColumn(2,u'时长',width=50)
for key in dic.keys():
index = self.list.InsertItem(key,str(key+1))
self.list.SetItem(index,1,dic[key][1])
self.list.SetItem(index,2,str(dic[key][2]))
self.list.Bind(wx.EVT_LIST_ITEM_SELECTED,self.Checkclick)
self.list.Bind(wx.EVT_LIST_ITEM_ACTIVATED,self.DoubleClick)
def DoubleClick(self, evt):#双击或者回车事件:播放
index = evt.GetIndex()
self.playing = False
pygame.mixer.music.stop()
self.player(index)
def player(self,index): #播放音乐
pygame.mixer.music.load(self.getmusic(index)) # 加载音乐
pygame.mixer.music.set_endevent(pygame.USEREVENT) # 设置结束事件
pygame.mixer.music.play(0) # 开始播放音乐
self.music = self.getmusicname(index) # 获取音乐名称
dura = self.dic[index][2] # 获取音乐时长
self.progress.SetRange(dura)
self.sst.Label = u'当前正在播放:%s'%self.music # 显示当前播放音乐
self.sst.Update() # 更新显示
print('正在播放:',self.music) # 打印当前播放音乐
self.playbutton.Label = u'暂停' # 显示暂停按钮
self.playing = True
self.loadlyc()
while self.playing:
self.dr = pygame.mixer.music.get_pos()
self.progress.SetValue(self.dr)
self.lycplayer(self.dr)
for event in pygame.event.get(): # 监听事件
if event.type == pygame.QUIT:
self.playing = False
print('退出')
pygame.quit()
sys.exit()
if event.type == pygame.USEREVENT: # 监听用户事件
index = self.playmode(self.click) # 点击播放模式
self.player(index) # 点击播放模式
def getmusic(self,index): #索引到音乐文件
# music = self.dic[index][0] + '\\' + self.dic[index][1]
return os.path.join(self.dic[index][0],self.dic[index][1])
def getmusicname(self,index): #索引到音乐名称
return self.dic[index][1]
async def downloadlyc(self,msg): #Playwright自动化框架下载歌词
print('search lyric')
async with async_playwright() as playwright:
browser = await playwright.chromium.launch(headless=True)
context = await browser.new_context()
page = await context.new_page()
await page.goto("https://www.musicenc.com/")
await page.locator('#searchInput').fill(msg)
await page.get_by_role("button", name="🔍").click()
results= await page.locator(".list > li > a ").all_inner_texts()
for result in results:
if re.search(result,msg):
text = result
break
else :
text = msg
continue
async with page.expect_popup() as page_info:
await page.locator(".list > li > a ").filter(has_text=text).first.click()
print('Found lyric')
page2 = await page_info.value
# await page2.wait_for_load_state('load')
async with page2.expect_download() as download_info:
await page2.get_by_role("button", name="LyricsDownload").click()
download = await download_info.value
name = msg+'.txt'
path = self.tc.Value
await download.save_as(os.path.join(path,name))
print(f'download{name}')
def loadlyc(self): #加载歌词
lycdic ={}
lyclist = []
lycname = self.music.split('.m')[0]
lycpath = os.path.join(self.tc.Value,lycname+'.txt')
if os.path.exists(lycpath.strip()):
with open(lycpath,'r',encoding='utf-8') as f:
lyc = f.readlines()
for j in range(len(lyc)):
lyric_data = re.findall(r'(\d{2}:\d{2}).*?](.+)',lyc[j])
for i in lyric_data:
times = (int(i[0].split(':')[0])*60 + round(float(i[0].split(':')[1]),3))*1000
lycdic[times] = i[1]
lyclist.append(times)
else:
try :
asyncio.run(self.downloadlyc(lycname)) #下载歌词
except Exception as e :
print(e)
finally:
self.loadlyc() #加载歌词
return lycdic,lyclist
def lycplayer(self,dr): #播放歌词
lycdic,lyclist = self.loadlyc()
for i in range(len(lyclist)-1):
if dr >= lyclist[i] and dr < lyclist[i+1]:
if i % 2 == 0 :
self.lyc.Label = lycdic[lyclist[i]]
self.lyc.Update()
self.lyc2.SetLabelText ('\t'+lycdic[lyclist[i+1]])
self.lyc2.Update()
def findmusic(self,path): #本地查找音乐文件
songs = []
for file in os.listdir(path):
childpath = os.path.join(file)
if file.endswith(('.mp3','m4a','.wav','ogg','wma')): #判断是否音乐文件
song = MP3(os.path.join(path ,file))
duration = int(round(song.info.length,3)*1000)
songs.append([path ,file ,duration])
elif os.path.isdir(childpath):
self.findmusic(childpath)
self.dic = dict(enumerate(songs))
if not self.dic:
wx.MessageBox(u'请先导入音乐')
return
self.CreatListctrl(self.dic)
for i in self.dic.keys():
print(i,self.dic.get(i))
return self.dic
def playmode(self,click):
if click %3 == 0: #顺序播放
self.modebutton.Label = u'顺序播放'
if self.index == max(self.dic.keys()):
wx.MessageBox(u'已没有下一曲')
return
else:
nextindex = self.index + 1
elif click %3 == 1:#单曲循环
self.modebutton.Label = u'单曲循环'
nextindex = self.index
elif click %3 == 2 : #随机播放
self.modebutton.Label = u'随机播放'
nextindex = random.choice(list(self.dic.keys()))
return nextindex
def playbtn(self): # 播放/暂停按钮的切换
if not self.dic:
wx.MessageBox(u'请先导入音乐')
return
if pygame.mixer.music.get_busy(): #已有音乐在播放
self.playbutton.Label = u'播放'
self.sst.Label = u'已暂停播放:%s'%self.music
pygame.mixer.music.pause()
# pyglet.media.Player.pause()
elif pygame.mixer.music.get_pos() == -1: #没有音乐播放过
self.playbutton.Label = u'暂停'
self.playing = True
self.player(self.index)
else : #从暂停位置播放
self.playbutton.Label = u'暂停'
self.playing = True
pygame.mixer.music.unpause()
# pyglet.media.Player.play()
self.sst.Label = u'当前正在播放:%s'%self.music
def prebutton(self):
if self.index :
nextindex = self.index - 1
elif self.click == 2:
nextindex = self.playmode(self.click)
else:
wx.MessageBox(u'已没有上一曲')
return
self.index = nextindex
self.player(self.index)
def nextbutton(self):
if self.index == max(self.dic.keys()):
wx.MessageBox(u'已没有下一曲')
return
elif self.click == 2:
nextindex = self.playmode(self.click)
else:
nextindex = self.index + 1
self.index = nextindex
self.player(self.index)
def btn_handler(self, event):
eid = event.GetId()
if eid == 100: #导入
self.findmusic(self.tc.Value)
self.st.Label = u'当前已选择:%s'%( self.dic[self.index][1])
elif eid == 101:#上一曲
self.prebutton()
elif eid == 102:#播放/暂停
self.playbtn()
elif eid == 103: #下一曲
self.nextbutton()
elif eid == 104: #播放模式
self.click += 1
self.index = self.playmode(self.click)
def Checkclick(self,event): #单击行事件
self.index = event.GetIndex()#行索引
self.st.Label = u'当前已选择:%s'%( self.dic[self.index][1])
def change_volume(self, evt): #改变音量
obj = evt.GetEventObject()
val = obj.GetValue()
volume = float(val / 100)
pygame.mixer.music.set_volume(volume)
class MusicPlayer(wx.Frame):
def __init__(self,parent):
wx.Frame.__init__(self,parent,style=wx.DEFAULT_FRAME_STYLE)
self.SetTitle(u'音乐播放器')
self.SetBackgroundColour((200,200,200))
self.SetSize(700,400)
self._init_ui()
self.Center()
def _init_ui(self):
left=panel1(self)
self._mgr=aui.AuiManager(self,aui.AUI_MGR_TRANSPARENT_HINT)
self._mgr.SetManagedWindow(self)
self._mgr.AddPane(left,aui.AuiPaneInfo().CenterPane().Row(0).Position(0).PaneBorder(False))
self._mgr.Update()
self.Bind(wx.EVT_CLOSE,self.OnClose)
def OnClose(self,event):
pygame.mixer.music.stop() #停止播放音乐
pygame.mixer.quit() #关闭播放器
pygame.quit() #关闭pygame
self._mgr.UnInit() #关闭窗口
self.Destroy()
# event.Skip()
if __name__ =='__main__':
app=wx.App()
frame=MusicPlayer(None)
frame.Show()
app.MainLoop()
代码可运行,基于wxpython图形界面、playwright自动化框架以及pygame游戏音乐库,可实现顺序模式、单曲模式、随机循环模式自动播放mp3音乐,同时自动搜索并爬虫下载播放歌词,有需要的同学可以共同学习。
但在 listctrl中双击播放后再次双击时没有任何响应了,改善的地方还很多,希望各位大佬多多指教,不尽感谢!