前段時間寫的Python自制一款炫酷音樂播放器,有不少小夥伴私信我,對播放器提了不少改進建議,讓我完善播放器的功能。今天音樂播放器2.0版本完成了,大家一起來看看是如何用python自制一款炫酷的音樂播放器的吧~
首先我們還是一起來看看實現的音樂播放器最終效果如何:
由於之前已經介紹過了音樂播放器V1.0版本的大致功能,如果有不瞭解的小夥伴可以先去看看V1.0版本的音樂播放器大致介紹。【python自制一款炫酷音樂播放器,想聽啥隨便搜!】下面我們就介紹這個音樂播放器V2.0版本新加的部分功能製作過程。
直接跳到文末獲取原始碼及exe打包程式。
在之前的V1.0版本中,我們已經完成了播放器UI介面的設計,對播放器的畫面佈局進行排版設計;其次音樂播放器的最重要的核心功能根據關鍵字搜尋自動爬取音樂並且進行音樂播放已經完成了。當然也完成了一些播放器常見的附加功能,如播放方式列表迴圈、單曲迴圈、隨機播放;當前上一首下一首播放;播放暫停開始;音量增加減少等。
V2.0版本中,我們主要增加了以下幾個核心功能:
V1.0版本中,我們已經對排版佈局進行了初步設計。頭部主要包括關鍵字搜尋和音樂來源選擇,以及表單最小化,最大化,關閉功能;底部主要來顯示當前播放音樂,播放進度條,音量控制,上一首/下一首,暫停/開始,播放方式等附加功能;中間主體包含左右兩側,左側用來顯示播放音樂封面圖,右側用來進行音樂列表顯示。
V2.0版本中我們將新增的幾個子列表頁面放在介面中間主體右側,疊加在音樂搜尋顯示列表頁。這裡我們使用的還是pyqt5。新增子列表頁面核心設計程式碼如下:
def init_ui(self):
self.main_layout.addWidget(self.up_widget, 0, 0, 1, 110)
self.main_layout.addWidget(self.left_widget, 1, 0, 90, 20)
self.main_layout.addWidget(self.right_widget, 1, 20, 90, 90) # 22右側部件在第0行第3列,佔8行9列
self.main_layout.addWidget(self.down_widget, 100, 0, 10, 110)
self.main_layout.addWidget(self.close_widget, 0, 105, 1, 5) # 左側部件在第0行第0列,佔1行3列
self.down_layout.addWidget(self.label, 1, 0, 1, 1)
self.setCentralWidget(self.main_widget) # 設定視窗主部件
self.tabWidget = QTabWidget(self)
self.tabWidget.setGeometry(QRect(33, 20, 716, 471))
self.tab = QWidget()
self.tab.setObjectName("tab")
self.tab_layout = QGridLayout()
self.tab.setLayout(self.tab_layout)
self.listwidget = QListWidget(self.tab)
self.label361 = QLabel(self)
self.label361.setText("")
self.label361.setStyleSheet("color:#6DDF6D")
self.tab_layout.addWidget(self.label361, 0, 1, 1, 1)
self.button_1235 = QPushButton(icon('fa.download', color='#D0D0D0', font=24), "下載全部")
self.button_1235.clicked.connect(self.downloadalls)
self.button_1235.setStyleSheet(
'''QPushButton{background:#222225;border-radius:5px;}QPushButton:hover{background:#303030;}''')
self.tab_layout.addWidget(self.button_1235, 0, 2, 1, 1)
self.button_1236 = QPushButton(icon('fa.trash-o', color='#D0D0D0', font=24), "清空列表")
self.button_1236.clicked.connect(self.dell)
self.button_1236.setStyleSheet(
'''QPushButton{background:#222225;border-radius:5px;}QPushButton:hover{background:#303030;}''')
self.tab_layout.addWidget(self.button_1236, 0, 3, 1, 1)
self.listwidget.doubleClicked.connect(lambda: self.change_func(self.listwidget))
self.listwidget.setContextMenuPolicy(Qt.CustomContextMenu)
self.listwidget.customContextMenuRequested[QPoint].connect(self.myListWidgetContext)
self.listwidget.setStyleSheet(self.css)
self.listwidget.setObjectName("listWidget")
self.tab_layout.addWidget(self.listwidget, 1, 0, 1, 4)
self.tabWidget.addTab(self.tab, " 搜尋頁 ")
self.tab2 = QWidget()
self.tab2.setObjectName("tab")
self.tab2_layout = QGridLayout()
self.tab2.setLayout(self.tab2_layout)
self.listwidget2 = QListWidget(self.tab2)
self.listwidget2.doubleClicked.connect(lambda: self.change_funcse(self.listwidget2))
self.listwidget2.setContextMenuPolicy(Qt.CustomContextMenu)
self.listwidget2.customContextMenuRequested[QPoint].connect(self.myListWidgetContext2)
self.listwidget2.setStyleSheet(self.css)
self.listwidget2.setObjectName("listWidget2")
self.listwidget2.setContextMenuPolicy(3)
self.tab2_layout.addWidget(self.listwidget2, 0, 0, 1, 1)
self.tabWidget.addTab(self.tab2, " 最近播放 ")
self.tab3 = QWidget()
self.tab3.setObjectName("tab")
self.tab3_layout = QGridLayout()
self.tab3.setLayout(self.tab3_layout)
self.label223 = QLabel(self)
# self.label5.setScaledContents(True)
pix_img = QPixmap(str(data + '/backdown.png'))
pix = pix_img.scaled(100, 100, Qt.KeepAspectRatio)
self.label223.setPixmap(pix)
# self.label5.setMaximumSize(1,1)
self.tab3_layout.addWidget(self.label223, 0, 0, 1, 1)
self.button_1237 = QPushButton(icon('fa.play', color='#FFFFFF', font=24), "播放全部")
self.button_1237.clicked.connect(self.allplaylove)
self.button_1237.setStyleSheet(
'''QPushButton{background:#EC4141;border-radius:5px;}QPushButton:hover{background:#E92121;}''')
self.tab3_layout.addWidget(self.button_1237, 0, 1, 1, 1)
self.button_1235 = QPushButton(icon('fa.download', color='#D0D0D0', font=24), "下載全部")
self.button_1235.clicked.connect(self.downloadalllove)
self.button_1235.setStyleSheet(
'''QPushButton{background:#222225;border-radius:5px;}QPushButton:hover{background:#303030;}''')
self.tab3_layout.addWidget(self.button_1235, 0, 2, 1, 1)
self.button_1236 = QPushButton(icon('fa.trash-o', color='#D0D0D0', font=24), "清空列表")
self.button_1236.clicked.connect(self.delove)
self.button_1236.setStyleSheet(
'''QPushButton{background:#222225;border-radius:5px;}QPushButton:hover{background:#303030;}''')
self.tab3_layout.addWidget(self.button_1236, 0, 3, 1, 1)
self.listwidget3 = QListWidget(self.tab3)
self.listwidget3.doubleClicked.connect(lambda: self.change_funclove(self.listwidget3))
self.listwidget3.setContextMenuPolicy(Qt.CustomContextMenu) self.listwidget3.customContextMenuRequested[QPoint].connect(self.myListWidgetContext3)
self.listwidget3.setStyleSheet(self.css)
self.listwidget3.setObjectName("listWidget3")
self.tab3_layout.addWidget(self.listwidget3, 1, 0, 1, 4)
self.tabWidget.addTab(self.tab3, " 喜愛的歌 ")
self.tab4 = QWidget()
self.tab4.setObjectName("tab")
self.tab4_layout = QGridLayout()
self.tab4.setLayout(self.tab4_layout)
self.listwidget4 = QListWidget(self.tab4)
# self.listwidget4.doubleClicked.connect(lambda: self.change_func(self.listwidget))
self.listwidget4.setStyleSheet(self.css)
self.listwidget4.setObjectName("listWidget4")
self.tab4_layout.addWidget(self.listwidget4, 0, 0, 1, 1)
self.tabWidget.addTab(self.tab4, " 歌詞 ")
self.tab5 = QWidget()
self.tab5.setObjectName("tab5")
self.tab5_layout = QGridLayout()
self.tab5.setLayout(self.tab5_layout)
self.listwidget5 = QListWidget(self.tab5)
self.listwidget5.doubleClicked.connect(lambda: self.change(self.listwidget5))
self.listwidget5.setContextMenuPolicy(Qt.CustomContextMenu)
self.listwidget5.customContextMenuRequested[QPoint].connect(self.myListWidgetContext5)
self.button_12351 = QPushButton(icon('fa.download', color='#D0D0D0', font=24), "新增目錄")
self.button_12351.clicked.connect(self.add)
self.button_12351.setStyleSheet(
'''QPushButton{background:#222225;border-radius:5px;}QPushButton:hover{background:#303030;}''')
self.tab5_layout.addWidget(self.button_12351, 0, 2, 1, 1)
self.button_12361 = QPushButton(icon('fa.trash-o', color='#D0D0D0', font=24), "清空列表")
self.button_12361.clicked.connect(self.dellocal)
self.button_12361.setStyleSheet(
'''QPushButton{background:#222225;border-radius:5px;}QPushButton:hover{background:#303030;}''')
self.tab5_layout.addWidget(self.button_12361, 0, 3, 1, 1)
self.listwidget5.setStyleSheet(self.css)
self.listwidget5.setObjectName("listWidget5")
self.tab5_layout.addWidget(self.listwidget5, 1, 0, 1, 4)
self.tabWidget.addTab(self.tab5, " 本地歌曲 ")
self.right_layout.addWidget(self.tabWidget, 3, 0, 100, 90)
UI介面佈局實現效果如下:
我們根據V1.0版本完成的,輸入的關鍵字和選擇音樂來源進行音樂爬取,通過多執行緒,將歌曲、歌手、歌曲url地址全都獲取;並將這些爬取的音樂資料列表顯示到搜尋頁面中。雙擊列表頁面中某一首歌曲,即可實現音樂播放功能。
V2.0版本中,我們新增了對當前播放音樂的本地下載、搜尋頁音樂列表一鍵下載、喜愛的歌列表一鍵下載、新增我喜愛的歌曲功能。
音樂下載:
對於音樂下載,我們可以進行當前播放音樂下載、搜尋頁音樂列表一鍵下載、喜愛的歌一鍵下載。如下圖所示:
核心程式碼如下:
# 當前播放音樂下載
def down(self):
if bo == 'local':
downpath = str(filew)
downpath = downpath.replace('/', '\\')
downpath = downpath + SongName[num]
print(downpath)
print('explorer /select,{}'.format(downpath))
call('explorer /select,{}'.format(downpath))
else:
call('explorer /select,{}'.format(to))
# 下載所有音樂
def downloadall(self, typer):
try:
global typerr
typerr = typer
print(typer)
print(typerr)
self.work = downall()
self.work.start()
self.work.trigger.connect(self.disdownall)
except:
print('預設圖片下載錯誤')
pass
# 下載搜尋頁列表所有音樂
def downloadalls(self):
self.downloadall('boing')
# 下載喜愛的歌列表所有音樂
def downloadalllove(self):
self.downloadall('love')
新增喜愛的歌:
對於新增喜愛的歌曲,我們可以通過當前播放音樂的❤標誌,也可以通過右鍵音樂列表進行喜愛的歌曲新增。核心程式碼如下:
# 通過點選❤標誌,對當前播放音樂新增喜愛的歌
def lovesong(self):
if bo == 'boing' or bo == 'boed':
try:
global loves
global loveurls
global lovepics
global lovelrc
if bo == 'boing':
loves.append(songs[num])
loveurls.append(urls[num])
lovepics.append(pic[num])
lovelrc.append(lrcs[num])
elif bo == 'boed':
loves.append(songed[num])
loveurls.append(urled[num])
lovepics.append(picd[num])
lovelrc.append(lrcd[num])
else:
pass
except:
pass
self.work = firstThread()
self.work.start()
self.work.trigger.connect(self.dispng)
r = 0
self.listwidget3.clear()
for i in loves:
self.listwidget3.addItem(i)
self.listwidget3.item(r).setForeground(Qt.white)
r = r + 1
print(loves)
else:
pass
# 通過右鍵音樂列表進行喜愛的歌曲新增
def addItem(self):
try:
global loves
global loveurls
global lovepics
global lovelrc
if list_confident == 'boing':
loves.append(songs[num_m])
loveurls.append(urls[num_m])
lovepics.append(pic[num_m])
lovelrc.append(lrcs[num_m])
else:
loves.append(songed[num_m])
loveurls.append(urled[num_m])
lovepics.append(picd[num_m])
lovelrc.append(lrcd[num_m])
self.work = firstThread()
self.work.start()
self.work.trigger.connect(self.dispng)
except:
pass
r = 0
self.listwidget3.clear()
for i in loves:
self.listwidget3.addItem(i)
self.listwidget3.item(r).setForeground(Qt.white)
r = r + 1
print('done')
print(loves)
這樣我們就可以把歌曲新增到喜愛的歌子列表頁面下,如下圖所示。
對於新增到喜愛的歌曲列表,我們需要在退出音樂播放器程式時,將這些資料儲存到本地檔案,方便後面重新執行時初始化讀取顯示列表。
喜愛歌曲本地儲存:
我們可以把喜愛的歌曲列表、喜愛歌曲圖片、歌曲url連結、歌詞等資料儲存到本地。核心程式碼如下:
def close(self):
reply = QMessageBox.question(self, u'警告', u'確定退出?', QMessageBox.Yes,
QMessageBox.No)
if reply == QMessageBox.Yes:
close = True
try:
mixer.music.stop()
except:
pass
try:
rmtree(str(data))
except Exception as e:
print('刪除錯誤型別是', e.__class__.__name__)
print('刪除錯誤明細是', e)
filepath = '{}/musicdata'.format(apdata)
try:
mkdir(filepath)
except:
pass
print(filepath)
with open(filepath + "/loves", 'w', encoding='utf-8') as f:
f.truncate(0)
print(f.write(str(loves)))
with open(filepath + "/lovepics", 'w', encoding='utf-8') as f:
f.truncate(0)
print(f.write(str(lovepics)))
with open(filepath + "/loveurls", 'w', encoding='utf-8') as f:
f.truncate(0)
print(f.write(str(loveurls)))
with open(filepath + "/lovelrc", 'w', encoding='utf-8') as f:
f.truncate(0)
print(f.write(str(lovelrc)))
with open(filepath + "/voice", 'w', encoding='utf-8') as f:
f.truncate(0)
print(f.write(str(voice)))
try:
rmtree(str(data))
except Exception as e:
print('刪除錯誤型別是', e.__class__.__name__)
print('刪除錯誤明細是', e)
exit()
else:
pass
喜愛的歌初始化讀取:
重新執行音樂播放器時,我們需要載入儲存在原生的喜愛歌曲資料,並將歌曲列表資訊載入顯示到喜愛的歌子列表頁面中。核心程式碼如下:
def run(self):
try:
apdataas = getenv("APPDATA")
filepathas = '{}/musicdata'.format(apdataas)
global lovelrc
global loveurls
global loves
global lovepics
global voice
# 讀取歷史資料開始
try:
with open(filepathas + "/voice", 'r', encoding='utf-8') as f:
a = f.read()
# print(a)
voice = float(a)
print(voice)
self.trigger.emit(str('voicedone'))
except:
self.trigger.emit(str('voicedone'))
pass
with open(filepathas + "/loves", 'r', encoding='utf-8') as f:
a = f.read()
print(a)
strer = a
loves = literal_eval(strer)
with open(filepathas + "/lovepics", 'r', encoding='utf-8') as f:
a = f.read()
print(a)
strer = a
lovepics = literal_eval(strer)
with open(filepathas + "/loveurls", 'r', encoding='utf-8') as f:
a = f.read()
print(a)
strer = a
loveurls = literal_eval(strer)
with open(filepathas + "/lovelrc", 'r', encoding='utf-8') as f:
a = f.read()
print(a)
strer = a
lovelrc = literal_eval(strer)
self.trigger.emit(str('login'))
print(loves)
print('read finish')
except:
print('read error')
pass
# 讀取資料結束
# 下載喜愛的歌列表中首項的歌曲封面
try:
req = get(lovepics[0])
checkfile = open(str(data + '/ls3.png'), 'w+b')
for i in req.iter_content(100000):
checkfile.write(i)
checkfile.close()
lsfile = str(data + '/ls3.png')
safile = str(data + '/first.png')
draw(lsfile, safile)
self.trigger.emit(str('first'))
except:
self.trigger.emit(str('nofirst'))
pass
列表播放:
對於載入的喜愛歌曲列表,我們可以對歌曲進行列表一鍵播放。
# 音樂播放
def bofang(self, num, bo):
print('嘗試進行播放')
try:
import urllib
global pause
global songs
global music
global downloading
downloading = True
self.console_button_3.setIcon(icon('fa.pause', color='#F76677', font=18))
pause = False
try:
mixer.stop()
except:
pass
mixer.init()
try:
self.Timer = QTimer()
self.Timer.start(500)
except:
pass
try:
self.label.setText('正在尋找檔案...')
self.work = WorkThread()
self.work.start()
self.work.trigger.connect(self.display)
except:
print('無法播放,歌曲下載錯誤')
downloading = False
pass
except:
sleep(0.1)
print('播放系統錯誤')
pass
# 播放所有歌曲
def playall(self, typer):
global num
global bo
try:
bo = typer
num = 0
self.bofang(bo, num)
except:
print('playall error')
pass
# 播放喜愛歌曲
def allplaylove(self):
self.playall('love')
對於播放的歌曲,我們可以進行歌詞列印顯示。核心程式碼如下:
def run(self):
if bo == 'love':
try:
proxies = {
'http': 'http://124.72.109.183:8118',
'http': 'http://49.85.1.79:31666'
}
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.198 Safari/537.36',
'X-Requested-With': 'XMLHttpRequest'}
try:
try:
try:
aq = lovepics[num]
aqq = aq.split('/')
except:
pass
if type == 'kugou' and len(aqq) - 1 == 6:
aqqe = str(aqq[0]) + str('//') + str(aqq[2]) + str('/') + str(aqq[3]) + str('/') + str(
'400') + str('/') + str(aqq[5]) + str('/') + str(aqq[6])
print(aqqe)
elif type == 'netease' and len(aqq) - 1 == 4:
aqn = aq.split('?')
b = '?param=500x500'
aqqe = (str(aqn[0]) + str(b))
print(aqqe)
else:
aqqe = lovepics[num]
req = get(aqqe)
checkfile = open(str(data + '/ls1.png'), 'w+b')
for i in req.iter_content(100000):
checkfile.write(i)
checkfile.close()
lsfile = str(data + '/ls1.png')
safile = str(data + '/back.png')
draw(lsfile, safile)
picno = True
except:
print('圖片錯誤')
picno = False
pass
url1 = loveurls[num]
print(url1)
# os.makedirs('music', exist_ok=True)
number = number + 1
path = str(data + '\{}.臨時檔案'.format(number))
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/121.110.430.128 Safari/537.36',
'X-Requested-With': 'XMLHttpRequest'
}
with get(url1, stream=True, headers=headers) as r, open(path, 'wb') as file:
total_size = int(r.headers['content-length'])
content_size = 0
for content in r.iter_content(chunk_size=1024):
if not stopdown:
file.write(content)
content_size += len(content)
plan = (content_size / total_size) * 100
# print(int(plan))
develop = str(int(plan)) + str('%')
self.trigger.emit(str(develop))
else:
print ('down')
break
stopdown = False
to = 'downloadmusic\{}.mp3'.format(songed[num])
makedirs('downloadmusic', exist_ok=True)
except:
self.trigger.emit(str('nofinish'))
pass
try:
lrct = []
f = lovelrc[num] # 按行讀取
# print(f)
lines = f.split('\n')
# print(lines)
for i in lines:
line1 = i.split('[')
try:
line2 = line1[1].split(']')
if line2 == '':
pass
else:
linew = line2[1]
# print(linew)
lrct.append(linew)
self.trigger.emit(str('lrcfinish'))
except:
print('歌詞錯誤')
except:
pass
try:
copyfile(path, to)
except:
pass
downloading = False
self.trigger.emit(str('finish'))
except:
self.trigger.emit(str('nofinish'))
效果如下所示:
首先我們可以對本地資料夾目錄下的音樂進行讀取新增到本地歌曲列表中,根據歌曲的檔名進行列表顯示。核心程式碼如下:
# 本地歌曲資料夾讀取新增
def add(self):
try:
global SongPath
global SongName
global num
global filew
global asas
fileN = QFileDialog.getExistingDirectory(None, "選取資料夾", "")
if not fileN == '':
self.listwidget5.clear()
filew = fileN + '/'
asas = filew
l1 = [name for name in listdir(fileN) if name.endswith('.mp3') or name.endswith('.flac') or name.endswith('.wma') or name.endswith('.MP3') or name.endswith('.FLAC') or name.endswith('.WMA')]
SongNameadd = l1
SongPathadd = [filew + i for i in SongNameadd]
SongName = SongName + SongNameadd
SongPath = SongPath + SongPathadd
print(SongPath)
r = 0
for i in SongName:
self.listwidget5.addItem(i) # 將檔名新增到listWidget
self.listwidget5.item(r).setForeground(Qt.white)
r = r + 1
except:
filew = asas
當然我們還可以對本地載入歌曲進行雙擊播放,首先我們需要將本地歌曲子列表頁面中進行雙擊事件繫結。
self.tab5 = QWidget()
self.tab5.setObjectName("tab5")
self.tab5_layout = QGridLayout()
self.tab5.setLayout(self.tab5_layout)
self.listwidget5 = QListWidget(self.tab5)
self.listwidget5.doubleClicked.connect(lambda: self.change(self.listwidget5))
self.listwidget5.setContextMenuPolicy(Qt.CustomContextMenu)
self.listwidget5.customContextMenuRequested[QPoint].connect(self.myListWidgetContext5)
對子列表頁面進行繫結之後,接下來我們就可以完成雙擊事件,獲取歌曲名稱,並進行本地播放。
# 載入當前播放的本地歌曲
def change(self, listwidget):
global num
global bo
# print (item.flags())
bo = 'local'
num = int(listwidget.currentRow())
print(num)
# self.label.setText(wenjianming)#設定標籤的文字為音樂的名字
f = str(SongName[num]).split('.mp3')
f = str(f[0]).split('.flac')
f = str(f[0]).split('.MP3')
f = str(f[0]).split('.FLAC')
f = str(f[0]).split('.wma')
f = str(f[0]).split('.WMA')
self.label.setText(f[0])
print(listwidget.currentRow())
self.bofanglocal()
# 本地音樂播放
def bofanglocal(self):
try:
global pause
try:
self.photo('local')
except:
pass
self.console_button_3.setIcon(icon('fa.pause', color='#F76677', font=18))
pause = False
fill = SongPath[num]
print(fill)
try:
global timenum
mp3 = str(SongPath[num])
xx = load(mp3)
timenum = xx.info.time_secs
global start
start = True
except:
print('進度條錯誤,播放失敗')
try:
mixer.stop()
except:
pass
try:
mixer.music.load(SongPath[num]) # 載入音樂
mixer.music.play() # 播放音樂
except:
print('MP3音訊檔出現錯誤')
except:
sleep(0.1)
print('system error')
self.next()
pass
音樂播放器的本地音樂播放功能效果如下:
至此,V2.0升級版的音樂播放器就完成了, 一起來看看效果吧!
原始碼及打包exe已上傳,關注文末公眾號回覆【播放器原始碼】即可獲取完整原始碼
Python往期精彩: