网易云音乐UI界面
网易云音乐UI界面
接着上期的网易云音乐搜索和下载,现在再添加一个功能。用pyqt5制作一个UI界面
效果展示:
主窗口中有个控件:行编辑输入框、搜索按钮、表格
代码
class MyQLabel(QLabel):
"""自定义标签"""
# 自定义信号
clicked_signal = pyqtSignal()
def __init__(self, parent=None):
super().__init__(parent)
def mouseReleaseEvent(self, QMouseEvent):
self.clicked_signal.emit()
# 可在外部与槽函数连接
def connect(self, callback):
self.clicked_signal.connect(callback)
class NeteaseUI(QWidget):
"""UI界面"""
def __init__(self):
super().__init__()
self.songs = []
self.line = QLineEdit(self)
self.button = QPushButton("搜索", self)
self.table = QTableWidget(self)
self.initUI()
def initUI(self):
self.lineEditstyle()
self.buttonstyle()
self.tablestyle()
self.setGeometry(600, 350, 600, 480)
self.setWindowTitle("网易云音乐 V 1.0 zly")
self.setWindowIcon(QIcon('images/icon.png'))
self.show()
def search(self):
"""搜索按钮事件"""
song_name = self.line.text()
if not song_name:
return
text = str({
"hlpretag": '<span class="s-fc7">',
"hlposttag": "</span>",
"s": song_name,
"type": "1",
"offset": "0",
"total": "true",
"limit": "30",
"csrf_token": ""
})
self.songs = SearchMusic(text).search()
for i, song in enumerate(self.songs):
for j, s in enumerate(song):
self.table.setItem(i, j, QTableWidgetItem(str(song[s])))
# 给每行点击后绑定下载事件
self.table.clicked.connect(self.download)
def download(self, QIndex):
"""下载事件"""
NeteaseCloudMusic(self.songs[QIndex.row()]).music()
def tablestyle(self):
"""表格样式"""
self.table.setGeometry(35, 90, 528, 360)
self.table.setStyleSheet(""" border-width: 1px; border-style: solid; border-color: rgb(255, 0, 0); border:0 """)
# 20行3列
self.table.setRowCount(20)
self.table.setColumnCount(3)
# 设置表头内容
self.table.setHorizontalHeaderLabels(['id', '歌曲', '歌手'])
# 设置水平方向自伸展模式
self.table.horizontalHeader().setSectionResizeMode(QHeaderView.Stretch)
# 设置表格整行选中
self.table.setSelectionBehavior(QAbstractItemView.SelectRows)
# 表格禁止编辑
self.table.setEditTriggers(QAbstractItemView.NoEditTriggers)
# 表头样式
self.table.horizontalHeader().setStyleSheet(""" border-width:1px; border-style:solid; border-color:grey; """)
self.table.verticalHeader().setStyleSheet(""" border-width:1px; border-style:solid; border-color:grey; """)
self.table.setStyleSheet("""QTableWidget::item{ padding-left: 15px; font-size: 30px; font-family: "微软雅黑 黑体 楷体 宋体"; font-weight: 500; color: black; border-width: 1px; border-color: burlywood; border-style: solid; border-radius: 5px; outline: none;} """)
def lineEditstyle(self):
"""行编辑样式"""
self.line.setPlaceholderText("\t请输入歌名")
self.line.setGeometry(35, 30, 400, 50)
self.line.setStyleSheet(''' padding-left:20px; font-size: 20px; font-family: "微软雅黑 黑体 楷体 宋体"; font-weight: 500; color: black; border-width: 1px; border-color: burlywood; border-style: solid; border-radius: 5px; outline: none; background-color: rgb(211, 202, 202); ''')
def buttonstyle(self):
"""按钮样式"""
self.button.setCursor(Qt.PointingHandCursor)
self.button.setIcon(QIcon('images/search.png'))
self.button.clicked.connect(self.search)
self.button.setGeometry(445, 30, 120, 50)
self.button.setStyleSheet(""" font-size: 20px; font-family: "微软雅黑 黑体 楷体 宋体"; font-weight: 500; color: white; border-width: 1px; border-color: burlywood; border-style: solid; border-radius: 5px; outline: none; background-color: rgb(38, 16, 231); """)
代码解释
自定义标签
class MyQLabel(QLabel):...
重写鼠标事件
def mouseReleaseEvent(self, QMouseEvent):
self.clicked_signal.emit()
发送信号,当标签被点击了调用相应的事件
def connect(self, callback):
self.clicked_signal.connect(callback)
主窗口类
class NeteaseUI(QWidget):...
控件样式
def tablestyle(self):
"""表格样式"""
self.table.setGeometry(35, 90, 528, 360)
self.table.setStyleSheet(""" border-width: 1px; border-style: solid; border-color: rgb(255, 0, 0); border:0 """)
# 20行3列
self.table.setRowCount(20)
self.table.setColumnCount(3)
# 设置表头内容
self.table.setHorizontalHeaderLabels(['id', '歌曲', '歌手'])
# 设置水平方向自伸展模式
self.table.horizontalHeader().setSectionResizeMode(QHeaderView.Stretch)
# 设置表格整行选中
self.table.setSelectionBehavior(QAbstractItemView.SelectRows)
# 表格禁止编辑
self.table.setEditTriggers(QAbstractItemView.NoEditTriggers)
# 表头样式
self.table.horizontalHeader().setStyleSheet(""" border-width:1px; border-style:solid; border-color:grey; """)
self.table.verticalHeader().setStyleSheet(""" border-width:1px; border-style:solid; border-color:grey; """)
self.table.setStyleSheet("""QTableWidget::item{ padding-left: 15px; font-size: 30px; font-family: "微软雅黑 黑体 楷体 宋体"; font-weight: 500; color: black; border-width: 1px; border-color: burlywood; border-style: solid; border-radius: 5px; outline: none;} """)
def lineEditstyle(self):
"""行编辑样式"""
self.line.setPlaceholderText("\t请输入歌名")
self.line.setGeometry(35, 30, 400, 50)
self.line.setStyleSheet(''' padding-left:20px; font-size: 20px; font-family: "微软雅黑 黑体 楷体 宋体"; font-weight: 500; color: black; border-width: 1px; border-color: burlywood; border-style: solid; border-radius: 5px; outline: none; background-color: rgb(211, 202, 202); ''')
def buttonstyle(self):
"""按钮样式"""
self.button.setCursor(Qt.PointingHandCursor)
self.button.setIcon(QIcon('images/search.png'))
self.button.clicked.connect(self.search)
self.button.setGeometry(445, 30, 120, 50)
self.button.setStyleSheet(""" font-size: 20px; font-family: "微软雅黑 黑体 楷体 宋体"; font-weight: 500; color: white; border-width: 1px; border-color: burlywood; border-style: solid; border-radius: 5px; outline: none; background-color: rgb(38, 16, 231); """)
搜索功能
def search(self):
"""搜索按钮事件"""
song_name = self.line.text()
if not song_name:
return
text = str({
"hlpretag": '<span class="s-fc7">',
"hlposttag": "</span>",
"s": song_name,
"type": "1",
"offset": "0",
"total": "true",
"limit": "30",
"csrf_token": ""
})
self.songs = SearchMusic(text).search()
for i, song in enumerate(self.songs):
for j, s in enumerate(song):
self.table.setItem(i, j, QTableWidgetItem(str(song[s])))
# 给每行点击后绑定下载事件
self.table.clicked.connect(self.download)
接着上篇的全部代码
import os
import sys
from base64 import b64encode
import requests
from Crypto.Cipher import AES
from PyQt5.QtCore import pyqtSignal, Qt
from PyQt5.QtGui import QIcon
from PyQt5.QtWidgets import (QApplication, QTableWidget, QWidget,
QPushButton, QLineEdit, QLabel,
QHeaderView, QAbstractItemView, QTableWidgetItem)
from pretty.pretty import HeaderPrettyDict
class MyQLabel(QLabel):
"""自定义标签"""
# 自定义信号
clicked_signal = pyqtSignal()
def __init__(self, parent=None):
super().__init__(parent)
def mouseReleaseEvent(self, QMouseEvent):
self.clicked_signal.emit()
# 可在外部与槽函数连接
def connect(self, callback):
self.clicked_signal.connect(callback)
class NeteaseUI(QWidget):
"""UI界面"""
def __init__(self):
super().__init__()
self.songs = []
self.line = QLineEdit(self)
self.button = QPushButton("搜索", self)
self.table = QTableWidget(self)
self.initUI()
def initUI(self):
self.lineEditstyle()
self.buttonstyle()
self.tablestyle()
self.setGeometry(600, 350, 600, 480)
self.setWindowTitle("网易云音乐 V 1.0 zly")
self.setWindowIcon(QIcon('images/icon.png'))
self.show()
def search(self):
"""搜索按钮事件"""
song_name = self.line.text()
if not song_name:
return
text = str({
"hlpretag": '<span class="s-fc7">',
"hlposttag": "</span>",
"s": song_name,
"type": "1",
"offset": "0",
"total": "true",
"limit": "30",
"csrf_token": ""
})
self.songs = SearchMusic(text).search()
for i, song in enumerate(self.songs):
for j, s in enumerate(song):
self.table.setItem(i, j, QTableWidgetItem(str(song[s])))
# 给每行点击后绑定下载事件
self.table.clicked.connect(self.download)
def download(self, QIndex):
"""下载事件"""
NeteaseCloudMusic(self.songs[QIndex.row()]).music()
def tablestyle(self):
"""表格样式"""
self.table.setGeometry(35, 90, 528, 360)
self.table.setStyleSheet(""" border-width: 1px; border-style: solid; border-color: rgb(255, 0, 0); border:0 """)
# 20行3列
self.table.setRowCount(20)
self.table.setColumnCount(3)
# 设置表头内容
self.table.setHorizontalHeaderLabels(['id', '歌曲', '歌手'])
# 设置水平方向自伸展模式
self.table.horizontalHeader().setSectionResizeMode(QHeaderView.Stretch)
# 设置表格整行选中
self.table.setSelectionBehavior(QAbstractItemView.SelectRows)
# 表格禁止编辑
self.table.setEditTriggers(QAbstractItemView.NoEditTriggers)
# 表头样式
self.table.horizontalHeader().setStyleSheet(""" border-width:1px; border-style:solid; border-color:grey; """)
self.table.verticalHeader().setStyleSheet(""" border-width:1px; border-style:solid; border-color:grey; """)
self.table.setStyleSheet("""QTableWidget::item{ padding-left: 15px; font-size: 30px; font-family: "微软雅黑 黑体 楷体 宋体"; font-weight: 500; color: black; border-width: 1px; border-color: burlywood; border-style: solid; border-radius: 5px; outline: none;} """)
def lineEditstyle(self):
"""行编辑样式"""
self.line.setPlaceholderText("\t请输入歌名")
self.line.setGeometry(35, 30, 400, 50)
self.line.setStyleSheet(''' padding-left:20px; font-size: 20px; font-family: "微软雅黑 黑体 楷体 宋体"; font-weight: 500; color: black; border-width: 1px; border-color: burlywood; border-style: solid; border-radius: 5px; outline: none; background-color: rgb(211, 202, 202); ''')
def buttonstyle(self):
"""按钮样式"""
self.button.setCursor(Qt.PointingHandCursor)
self.button.setIcon(QIcon('images/search.png'))
self.button.clicked.connect(self.search)
self.button.setGeometry(445, 30, 120, 50)
self.button.setStyleSheet(""" font-size: 20px; font-family: "微软雅黑 黑体 楷体 宋体"; font-weight: 500; color: white; border-width: 1px; border-color: burlywood; border-style: solid; border-radius: 5px; outline: none; background-color: rgb(38, 16, 231); """)
class Encrypt:
def __init__(self, text):
self.data = {
'encSecKey': '01ec48cb405730aa77f993a988cc1f5bc1938511d75f49eddc581f2fe2aaf18988853200564b2d4b1312cf6e0bb344425addce5a4c81b38b89a5973900946bd100b0f1865d22d2a8e5dd8be208eb5d6eb2f71309a165daeffe95355e1e44edd65bdf28088fe4f5e835a7d9f7569fc2530f9d17c00b51cfafbe421eb462247ea3'
}
self.text = text
self.key = '0CoJUm6Qyw8W8jud'
def get_form_data(self):
"""生成表单参数"""
# 随机秘钥参数,可以用固定值
i = "4JknCzx6uEXUwxpU"
# 两次加密
first_encrypt = self.AES_encpyt(self.text, self.key)
self.data['params'] = self.AES_encpyt(first_encrypt, i)
return self.data
def AES_encpyt(self, text, key):
"""AES加密"""
# AES加密明文必须为16的整数倍
padding = 16 - len(text.encode()) % 16
text += padding * chr(padding)
aes = AES.new(key.encode(), AES.MODE_CBC, b'0102030405060708')
enctext = aes.encrypt(text.encode())
return b64encode(enctext).decode('utf-8')
class SearchMusic:
def __init__(self, text):
self.url = 'https://music.163.com/weapi/cloudsearch/get/web?csrf_token='
reqstr = ''' authority: music.163.com method: POST path: /weapi/song/enhance/player/url/v1?csrf_token= scheme: https accept: */* accept-encoding: gzip, deflate, br accept-language: zh-CN,zh;q=0.9 cache-control: no-cache content-length: 434 content-type: application/x-www-form-urlencoded cookie: __root_domain_v=.163.com; _qddaz=QD.28yaab.3jymc6.kf0ihnaf; _ntes_nnid=e7c5f90265b4d5a5bcb511efebf7a890,1600596980395; _ntes_nuid=e7c5f90265b4d5a5bcb511efebf7a890; _iuqxldmzr_=32; WM_TID=OlHvFOuIVclAQFQUAEJvJZyLuh3MwtGb; NMTID=00ODCot1Uq8CvcXIUIMmKBlPfRiyfoAAAF3NHwibw; WM_NI=%2BWiHzgkFWg%2BON3YYI0rQzlpsOW8x4BPGt%2FWRNpkD3r2Utv8U1gx6RZgvmmJQ0IpSBgdk1GvY9uIQW6BfIN7lVoHo8z1BIoa%2FdLUgKwpx6twUKJtgDlexKOu7LqWGuYApZzg%3D; WM_NIKE=9ca17ae2e6ffcda170e2e6eeb9c844a3b1aba3b24489eb8eb6d15b929a9baaaa5cace70087b64e8ab18299d02af0fea7c3b92ae989a7a9f96da99a9988aa458eed97bacc3cb28fb68df3798d89f899b74a9499bcd0d65a8eb0a5a5b27af28bbc97bb5ff3b9b8d7d152a5aaa38ec95bf497c0b4c16da8b5ffa8f553fbab87b2d63e82ba87afb66896b18890bb72f39e8790e425a8949b88ca7db4a8fa95f65f8996bc88c768a7a885b0f83d90af99a8f85383b0969be637e2a3; hb_MA-9F44-2FC2BD04228F_source=www.baidu.com; JSESSIONID-WYYY=bERBG86BVbD29X%5C35acjg8ndIoGYPEZvQ8fc0t7WUnMu3KTujvG1zqfSMIG%2By4%2FZRz9hC%2FwBN0Mf%2B%2B1RJBK2TeR96X7l%2BmS%2FHhuuqBwl7yxwe4jQ%5ChzFoFgKylb3ZdOnw6%2FqsqaUYUrJ12EVVy0m66JVlQez0T5ijmgZuOsk0KcMnUe4%3A1611553513123; WEVNSM=1.0.0; WNMCID=kctjbv.1611551714155.01.0 origin: https://music.163.com pragma: no-cache referer: https://music.163.com/ sec-fetch-dest: empty sec-fetch-mode: cors sec-fetch-site: same-origin 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 '''
self.headers = HeaderPrettyDict().pretty(reqstr)
self.text = text
def search(self):
"""搜索音乐,返回音乐列表"""
data = Encrypt(self.text).get_form_data()
res = requests.post(self.url, headers=self.headers, data=data)
songlist = []
songs = res.json()['result']['songs']
for song in songs:
item = { }
# id、歌名、歌手、封面
item['song_id'] = song['id']
item['song_name'] = song['name']
item['singer'] = song['ar'][0]['name']
# item['song_pic_url'] = song['al']['picUrl']
songlist.append(item)
return songlist
class NeteaseCloudMusic:
def __init__(self, song):
self.url = 'https://music.163.com/weapi/song/enhance/player/url/v1?csrf_token='
reqstr = ''' authority: music.163.com method: POST path: /weapi/song/enhance/player/url/v1?csrf_token= scheme: https accept: */* accept-encoding: gzip, deflate, br accept-language: zh-CN,zh;q=0.9 cache-control: no-cache content-length: 434 content-type: application/x-www-form-urlencoded cookie: __root_domain_v=.163.com; _qddaz=QD.28yaab.3jymc6.kf0ihnaf; _ntes_nnid=e7c5f90265b4d5a5bcb511efebf7a890,1600596980395; _ntes_nuid=e7c5f90265b4d5a5bcb511efebf7a890; _iuqxldmzr_=32; WM_TID=OlHvFOuIVclAQFQUAEJvJZyLuh3MwtGb; NMTID=00ODCot1Uq8CvcXIUIMmKBlPfRiyfoAAAF3NHwibw; WM_NI=%2BWiHzgkFWg%2BON3YYI0rQzlpsOW8x4BPGt%2FWRNpkD3r2Utv8U1gx6RZgvmmJQ0IpSBgdk1GvY9uIQW6BfIN7lVoHo8z1BIoa%2FdLUgKwpx6twUKJtgDlexKOu7LqWGuYApZzg%3D; WM_NIKE=9ca17ae2e6ffcda170e2e6eeb9c844a3b1aba3b24489eb8eb6d15b929a9baaaa5cace70087b64e8ab18299d02af0fea7c3b92ae989a7a9f96da99a9988aa458eed97bacc3cb28fb68df3798d89f899b74a9499bcd0d65a8eb0a5a5b27af28bbc97bb5ff3b9b8d7d152a5aaa38ec95bf497c0b4c16da8b5ffa8f553fbab87b2d63e82ba87afb66896b18890bb72f39e8790e425a8949b88ca7db4a8fa95f65f8996bc88c768a7a885b0f83d90af99a8f85383b0969be637e2a3; hb_MA-9F44-2FC2BD04228F_source=www.baidu.com; JSESSIONID-WYYY=bERBG86BVbD29X%5C35acjg8ndIoGYPEZvQ8fc0t7WUnMu3KTujvG1zqfSMIG%2By4%2FZRz9hC%2FwBN0Mf%2B%2B1RJBK2TeR96X7l%2BmS%2FHhuuqBwl7yxwe4jQ%5ChzFoFgKylb3ZdOnw6%2FqsqaUYUrJ12EVVy0m66JVlQez0T5ijmgZuOsk0KcMnUe4%3A1611553513123; WEVNSM=1.0.0; WNMCID=kctjbv.1611551714155.01.0 origin: https://music.163.com pragma: no-cache referer: https://music.163.com/ sec-fetch-dest: empty sec-fetch-mode: cors sec-fetch-site: same-origin 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 '''
self.headers = HeaderPrettyDict().pretty(reqstr)
self.text = '{"ids":"[' + str(song['song_id']) + ']","level":"standard","encodeType":"aac","csrf_token":""}'
self.name = song['song_name']
self.singer = song['singer']
def music(self):
"""获取音乐的url"""
data = Encrypt(self.text).get_form_data()
res = requests.post(self.url, headers=self.headers, data=data)
song_url = res.json()['data'][0]['url']
self.save(self.download(song_url))
def download(self, url):
"""下载音乐"""
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'}
res = requests.get(url, headers=headers)
return res.content
def save(self, content):
"""保存音乐"""
# 当前文件目录
path = os.path.dirname(__file__)
# 检查'data'目录是否存在,不存在则创建目录
if not os.path.exists(path+'\\data'):
os.mkdir(path+'\\data')
# 音乐保存路径
music_path = path+'\\data'+f'\\{ self.name} { self.singer}.m4a'
# 保存
if not os.path.exists(music_path):
with open(music_path, 'wb') as f:
f.write(content)
if __name__ == '__main__':
def run():
app = QApplication(sys.argv)
g = NeteaseUI()
sys.exit(app.exec_())
run()
还没有评论,来说两句吧...