您好,登錄后才能下訂單哦!
小編給大家分享一下python如何實現可下載音樂的音樂播放器,相信大部分人都還不怎么了解,因此分享這篇文章給大家參考一下,希望大家閱讀完這篇文章后大有收獲,下面讓我們一起去了解一下吧!
1.確定頁面
SongSheet ------ 顯示歌單
MusicCtrl ------顯示音樂一些控件(播放,跳轉,音量調節)
SearchWindows ------搜索欄(搜索歌曲默認顯示20條,可下載)
songSheet.py
#!/usr/bin/env python # -*- coding:utf-8 -*- # @Author: Minions # @Date: 2019-11-24 19:51:16 # @Last Modified by: Minions # @Last Modified time: 2019-12-17 10:01:53 import tkinter import os from tkinter import ttk import time class SongSheet(tkinter.Frame): def __init__(self, master): self.frame = tkinter.Frame(master, height=230, width=300, bd=1, bg="SkyBlue") self.frame.place(x=0, y=0) self.filePath = "C:\Musics" self.music = "" # 點擊歌曲獲得更新的路徑 self.count = 0 # 計數,共多少歌曲 def run(self): # 搜索按鈕 searchBtn = tkinter.Button(self.frame, text="更新", bg="SkyBlue", command=self.showSheet, width=10, height=1) searchBtn.place(x=0, y=200) # 顯示歌單 def showSheet(self): self.count = 0 musics = os.listdir(self.filePath) tree = ttk.Treeview(self.frame) # 定義列 tree["columns"] = ("song") # 設置列,列還不顯示 tree.column("song", width=95) # 設置表頭 和上面一一對應 tree.heading("song", text="song") # 添加數據 往第0行添加 for music in musics: # 去除空格 music = "".join(music.split(" ")) tree.insert("", 0, text=self.count, values=(music)) self.count += 1 # 鼠標選中一行回調 def selectTree(event): for item in tree.selection(): item_text = tree.item(item, "values") self.music = "".join(item_text) # print(self.music) # 選中行 tree.bind('<<TreeviewSelect>>', selectTree) tree.place(width=300, height=200, x=0, y=0) # 添加滾動條 sy = tkinter.Scrollbar(tree) sy.pack(side=tkinter.RIGHT, fill=tkinter.Y) sy.config(command=tree.yview) tree.config(yscrollcommand=sy.set)
2.寫出音樂控件
musicCtrl.py
#!/usr/bin/env python # -*- coding:utf-8 -*- # @Author: Minions # @Date: 2019-11-24 16:28:18 # @Last Modified by: Minions # @Last Modified time: 2019-12-17 10:25:31 import tkinter from tkinter import ttk import os import time import pygame from mutagen.mp3 import MP3 import random from songSheet import SongSheet class MusicCtrl(object): def __init__(self, master): self.frame = tkinter.Frame(master,height=150, width=700, bd=1, bg="MediumSeaGreen") self.frame.place(height=150, width=700, x=0, y=250) self.nowPaly = True # 是否正在播放音樂 self.filePath = r"C:\Musics" # 從該文件夾讀取 self.musicPath = "" # 用于拼接音樂的路徑 self.songSheet = SongSheet(master) self.songSheet.run() self.music = os.path.join(self.filePath,self.musicPath) # 音樂的路徑 # 整合功能 def run(self): self.playMusic() self.refreshName() self.pauseMusic() self.volume() try: self.songPos() except: print("暫無歌曲載入!") # 播放音樂按鈕 def playMusic(self): playBtn = tkinter.Button(self.frame, text="播放", command=self.playFunc, width=10,height=2) playBtn.place(x=300,y=10) # 實現播放功能 def playFunc(self): pygame.mixer.init() track = pygame.mixer.music.load(self.music) # 載入一個音樂文件用于播放 pygame.mixer.music.play() # 開始播放音樂流 # 暫停播放按鈕 def pauseMusic(self): pauseBtn = tkinter.Button(self.frame, text="暫停/繼續", command=self.pauseFunc, width=10, height=2) pauseBtn.place(x=400, y=10) # 暫停播放功能 def pauseFunc(self): # pygame.mixer.music.get_busy() # 檢測是否正在播放音樂 if self.nowPaly: pygame.mixer.music.pause() self.nowPaly = False else: pygame.mixer.music.unpause() # 恢復音樂播放 self.nowPaly = True # 顯示歌曲名稱以及歌手 def showName(self): songName = tkinter.Label(self.frame, fg="white",font=("華文行楷", 10),bg="MediumSeaGreen", width=25, height=1) songName['text'] = self.songSheet.music.split('.')[0] songName.place(x=35,y=15) self.music = os.path.join(self.filePath,self.songSheet.music) # 更換音樂后應該繼續播放,并且更換音樂時長 self.playFunc() self.songPos() # 音量調節 def volume(self): volumeNum = tkinter.Label(self.frame, text="volume", fg="Aquamarine", font=("華文行楷", 10), bg="MediumSeaGreen", width=5, height=1) volumeNum.place(x=500, y=70) volume = tkinter.Scale(self.frame, from_=0, to=100, orient=tkinter.HORIZONTAL) volume.place(x=550,y=50) def showNum(): pygame.mixer.music.set_volume(volume.get()*0.01) # 參數值范圍為 0.0~1.0 tkinter.Button(self.frame, text="設置", command=showNum, bg="Aqua").place( x=550, y=100) # 音樂絕對定位 def songPos(self): # print(self.music.info.length) pos = tkinter.Scale(self.frame, from_=0, to=round( MP3(self.music).info.length), orient=tkinter.HORIZONTAL, tickinterval=50, length=300) pos.place(x=180, y=60) def showNum(): # 為了對一個 MP3 文件的進行絕對定位,建議首先調用 rewind()函數,不然會一直往后走 pygame.mixer.music.rewind() if pygame.mixer.music.get_busy(): self.curDuration = pos.get() pygame.mixer.music.set_pos(self.curDuration) else: print("請先播放音樂!") tkinter.Button(self.frame, text="設置", command=showNum, bg="Aqua").place( x=490, y=90) # 點擊歌單的歌更新名稱 def refreshName(self): refreshNameBtn = tkinter.Button(self.frame, text="update",command=self.showName, width=10, height=2) refreshNameBtn.place(x=45, y=50)
3.核心爬取音樂
music.py
# -*- coding:utf-8 -*- import requests, hashlib, sys, click, re, base64, binascii, json, os from Cryptodome.Cipher import AES from http import cookiejar class Encrypyed(): """ 解密算法 """ def __init__(self): self.modulus = '00e0b509f6259df8642dbc35662901477df22677ec152b5ff68ace615bb7b725152b3ab17a876aea8a5aa76d2e417629ec4ee341f56135fccf695280104e0312ecbda92557c93870114af6c9d05c4f7f0c3685b7a46bee255932575cce10b424d813cfe4875d3e82047b97ddef52741d546b8e289dc6935b3ece0462db0a22b8e7' self.nonce = '0CoJUm6Qyw8W8jud' self.pub_key = '010001' # 登錄加密算法, 基于https://github.com/stkevintan/nw_musicbox腳本實現 def encrypted_request(self, text): text = json.dumps(text) sec_key = self.create_secret_key(16) enc_text = self.aes_encrypt(self.aes_encrypt(text, self.nonce), sec_key.decode('utf-8')) enc_sec_key = self.rsa_encrpt(sec_key, self.pub_key, self.modulus) data = {'params': enc_text, 'encSecKey': enc_sec_key} return data def aes_encrypt(self, text, secKey): pad = 16 - len(text) % 16 text = text + chr(pad) * pad encryptor = AES.new(secKey.encode('utf-8'), AES.MODE_CBC, b'0102030405060708') ciphertext = encryptor.encrypt(text.encode('utf-8')) ciphertext = base64.b64encode(ciphertext).decode('utf-8') return ciphertext def rsa_encrpt(self, text, pubKey, modulus): text = text[::-1] rs = pow(int(binascii.hexlify(text), 16), int(pubKey, 16), int(modulus, 16)) return format(rs, 'x').zfill(256) def create_secret_key(self, size): return binascii.hexlify(os.urandom(size))[:16] class Song(): """ 歌曲對象,用于存儲歌曲的信息 """ def __init__(self, song_id, song_name, song_num, picUrl, singer_name, song_url=None): self.song_id = song_id self.song_name = song_name self.song_num = song_num self.singer_name = singer_name self.picUrl = picUrl self.song_url = '' if song_url is None else song_url class Crawler(): """ 網易云爬取API """ def __init__(self, timeout=60, cookie_path='.'): self.headers = { 'Accept': '*/*', 'Accept-Encoding': 'gzip,deflate,sdch', 'Accept-Language': 'zh-CN,zh;q=0.8,gl;q=0.6,zh-TW;q=0.4', 'Connection': 'keep-alive', 'Content-Type': 'application/x-www-form-urlencoded', 'Host': 'music.163.com', 'Referer': 'http://music.163.com/search/', 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.132 Safari/537.36' } self.session = requests.Session() self.session.headers.update(self.headers) self.session.cookies = cookiejar.LWPCookieJar(cookie_path) self.download_session = requests.Session() self.timeout = timeout self.ep = Encrypyed() self.result =[] def post_request(self, url, params): """ Post請求 :return: 字典 """ data = self.ep.encrypted_request(params) resp = self.session.post(url, data=data, timeout=self.timeout) result = resp.json() if result['code'] != 200: click.echo('post_request error') else: return result def search(self, search_content, search_type, limit=9): """ 搜索API :params search_content: 搜索內容 :params search_type: 搜索類型 :params limit: 返回結果數量 :return: 字典. """ url = 'http://music.163.com/weapi/cloudsearch/get/web?csrf_token=' params = {'s': search_content, 'type': search_type, 'offset': 0, 'sub': 'false', 'limit': limit} result = self.post_request(url, params) # print(result['result']['songs'][3]['ar'][0]['name']) return result def search_song(self, song_name, song_num, quiet=True, limit=20): """ 根據音樂名搜索 :params song_name: 音樂名 :params song_num: 下載的歌曲數 :params quiet: 自動選擇匹配最優結果 :params limit: 返回結果數量 :return: Song獨享 """ result = self.search(song_name, search_type=1, limit=limit) if result['result']['songCount'] <= 0: click.echo('Song {} not existed.'.format(song_name)) else: songs = result['result']['songs'] if quiet: self.result = [] # 更新result for song in songs: singers = [] # """ picUrl = song['al']['picUrl'] # """ for name in song['ar']: singers.append(name['name']) song_id, song_name = song['id'], song['name'] singer_name = "_".join(singers) song = Song(song_id=song_id, song_name=song_name, song_num=song_num, singer_name=singer_name,picUrl=picUrl) self.result.append(song) picUrl = songs[0]['al']['picUrl'] # """ song_id, song_name = songs[0]['id'], songs[0]['name'] song = Song(song_id=song_id, song_name=song_name, song_num=song_num, singer_name=self.result[0].singer_name, picUrl=picUrl) return song def get_song_url(self, song_id, bit_rate=320000): """ 獲得歌曲的下載地址 :params song_id: 音樂ID<int>. :params bit_rate: {'MD 128k': 128000, 'HD 320k': 320000} :return: 歌曲下載地址 """ url = 'http://music.163.com/weapi/song/enhance/player/url?csrf_token=' csrf = '' params = {'ids': [song_id], 'br': bit_rate, 'csrf_token': csrf} result = self.post_request(url, params) # 歌曲下載地址 song_url = result['data'][0]['url'] # 歌曲不存在 if song_url is None: click.echo('Song {} is not available due to copyright issue.'.format(song_id)) else: return song_url def get_song_by_url(self, song_url, song_name, song_num, singer_name, folder): """ 下載歌曲到本地 :params song_url: 歌曲下載地址 :params song_name: 歌曲名字 :params song_num: 下載的歌曲數 :params folder: 保存路徑 """ # for res in self.result: # print(res.song_name, res.song_id, res.singer_name) # print("--------") # print(song_url, song_name, singer_name) class Netease(): """ 網易云音樂下載 """ def __init__(self, timeout, folder, quiet, cookie_path): self.crawler = Crawler(timeout, cookie_path) self.folder = '.' if folder is None else folder self.quiet = quiet self.url = '' self.pic = '' def download_song_by_search(self, song_name): """ 根據歌曲名進行搜索 :params song_name: 歌曲名字 :params song_num: 下載的歌曲數 """ try: song = self.crawler.search_song(song_name, self.quiet) except: click.echo('download_song_by_serach error') # 如果找到了音樂, 則下載 if song != None: self.download_song_by_id(song.song_id, song.song_name, song.song_num, song.singer_name, self.folder) self.pic = song.picUrl def download_song_by_id(self, song_id, song_name, song_num, singer_name, folder='.'): """ 通過歌曲的ID下載 :params song_id: 歌曲ID :params song_name: 歌曲名 :params song_num: 下載的歌曲數 :params folder: 保存地址 """ try: url = self.crawler.get_song_url(song_id) # 去掉非法字符 song_name = song_name.replace('/', '') song_name = song_name.replace('.', '') self.crawler.get_song_by_url(url, song_name, song_num, singer_name, folder) except: click.echo('download_song_by_id error')
4.將爬取音樂搜索欄整合
searchWindows.py
#!/usr/bin/env python # -*- coding:utf-8 -*- # @Author: Minions # @Date: 2019-11-25 10:31:56 # @Last Modified by: Minions # @Last Modified time: 2019-12-17 12:40:31 import tkinter from tkinter import ttk import os from urllib import request from music import Netease,Crawler import requests class SearchWindows(tkinter.Frame): def __init__(self, master): self.frame = tkinter.Frame(master, height=240, width=500, bd=1, bg="Purple") self.songs = None # 搜索到的所有歌曲(20)的信息 self.frame.place(x=300,y=0) self.info = None # 當前歌曲的信息 self.fileName = "C:\Musics\\" timeout = 60 output = 'Musics' quiet = True cookie_path = 'Cookie' self.netease = Netease(timeout, output, quiet, cookie_path) def run(self): self.searchBar() self.download() # 搜索框 def searchBar(self): entry = tkinter.Entry(self.frame) entry.place(width=200, height=30, x=50, y=10) def getValue(): self.netease.download_song_by_search(entry.get()) self.songs = self.netease.crawler.result self.showSong() searchBtn = tkinter.Button(self.frame, text="搜索", bg="DarkOrchid", command=getValue, width=10, height=1) searchBtn.place(x=270, y=10) # 顯示搜索到的歌曲 def showSong(self): tree = ttk.Treeview(self.frame) # 定義列 tree["columns"] = ("song", "singer", "url") # 設置列,列還不顯示 tree.column("song", width=50) tree.column("singer", width=50) tree.column("url", width=50) # 設置表頭 和上面一一對應 tree.heading("song", text="song") tree.heading("singer", text="singer") tree.heading("url", text="url") count = len(self.songs) for song in reversed(self.songs): url = self.netease.crawler.get_song_url(song.song_id) tree.insert("", 0, text=count, values=(song.song_name, song.singer_name, url)) count -= 1 # 鼠標選中一行回調 def selectTree(event): for item in tree.selection(): item_text = tree.item(item, "values") self.info = item_text # 滾動條 sy = tkinter.Scrollbar(tree) sy.pack(side=tkinter.RIGHT, fill=tkinter.Y) sy.config(command=tree.yview) tree.config(yscrollcommand=sy.set) # 選中行 tree.bind('<<TreeviewSelect>>', selectTree) tree.place(width=300, height=200, x=50, y=50) # 下載選中的歌曲 def download(self): def downloadSong(): if self.info is None: print("該歌曲下載失敗") else: request.urlretrieve(self.info[2], self.fileName+self.info[1]+'-'+self.info[0]+'.mp3') print("%s-%s下載成功" %(self.info[1], self.info[0])) # 下載按鈕 downloadBtn = tkinter.Button(self.frame, text="下載", bg="DarkOrchid", command=downloadSong, width=6, height=1) downloadBtn.place(x=345, y=200)
5.整合所有部分
main.py
#!/usr/bin/env python # -*- coding:utf-8 -*- # @Author: Minions # @Date: 2019-11-24 20:10:15 # @Last Modified by: Minions # @Last Modified time: 2019-12-17 9:55:31 import tkinter from searchWindows import SearchWindows from musicCtrl import MusicCtrl from songSheet import SongSheet import os win = tkinter.Tk() win.title("Minions音樂播放器") win.geometry("700x400") if os.path.exists("C:/Musics"): print("xxx") else: os.mkdir("C:/Musics") searchWin = SearchWindows(win) searchWin.run() songSheetWin = SongSheet(win) songSheetWin.run() musicWin = MusicCtrl(win) musicWin.run() win.mainloop()
以上是“python如何實現可下載音樂的音樂播放器”這篇文章的所有內容,感謝各位的閱讀!相信大家都有了一定的了解,希望分享的內容對大家有所幫助,如果還想學習更多知識,歡迎關注億速云行業資訊頻道!
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。