IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> Python知识库 -> 七夕送女朋友什么礼物好?我反手写了个音乐播放器,女友眼冒金光 -> 正文阅读

[Python知识库]七夕送女朋友什么礼物好?我反手写了个音乐播放器,女友眼冒金光

七夕送女朋友什么礼物好?我反手写了个音乐播放器,女友眼冒金光!

这是一个用 Python 的 tkinter 库做的一个网络音乐播放器。我不说它的 UI 设计的有多好看,但是它的功能绝对是全站首个!坚持看到底,你不点赞算我输!

成果展示

在这里插入图片描述
程序截图
在这里插入图片描述
在这里插入图片描述

前期准备

程序结构:
在这里插入图片描述

文件或文件夹描述
_pycache_导入模块形成的文件夹
musics存储下载的音乐
get.py爬取音乐
gui.pyGUI界面
lrc.py歌词操作
main.pyw入口文件,双击运行
player.py播放器文件
requirements.txt所需库

所需第三方库(requirement.txt):

mutagen==1.45.1
pygame==2.0.1
requests==2.26.0
Pillow==8.3.1

安装:

pip install mutagen		# 查看歌曲长度
pip install pygame		# 播放音乐
pip install requests	# 爬取音乐
pip install pillow		# 显示图片

或者:

pip install -r requirements.txt

一、爬取音乐

下面的代码为 get.py 里的内容。
具体教程见 酷我音乐搜索、下载详解

import requests

search_url = 'http://www.kuwo.cn/api/www/search/searchMusicBykeyWord'

search_headers = {
    'Referer': 'http://www.kuwo.cn/search/list?key=',
    'Cookie': '_ga=GA1.2.12......',
    'csrf': 'YO4OH2VYH1A'}

search_params = {
'key': 'str',   # 查找关键字
'pn': '1',  # 页数
'rn': '20', # 项数
'httpsStatus': '1',
'reqId': '6e028fc0-db8f-11eb-b6f5-ff7d54a57f2b'
}

from_url = 'http://www.kuwo.cn/url'
  
from_params = {
    'rid': '148526468', # 歌曲 rid
    'type': 'convert_url3',
    'br': '128kmp3',
}

lrc_url = 'http://m.kuwo.cn/newh5/singles/songinfoandlrc?musicId={rid}'

headers = {'user-agent': 'Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.114 Safari/537.36 Edg/91.0.864.59'}

class Kuwo:
    def search_kuwo(self, kw):#
        search_params.update({'key': kw})

        response = requests.get(search_url,
                                params=search_params,
                                headers={**headers, **search_headers},
                                timeout=2,
                               ).json()

        datas = response.get('data', {}).get('list', {})

        result = [[r.get('name', ''),
                   r.get('artist', ''),
                   r.get('album', ''),
                   r.get('songTimeMinutes', ''),
                   r.get('pic', ''),
                   r.get('pic120', ''),
                   r.get('rid', '')]
                  for r in datas]

        return result

    def get_music_url(self, rid):#
        from_params['rid'] = rid
        url = requests.get(from_url, params=from_params, headers=headers, timeout=2).json()['url']

        return url

    def get_music_content(self, rid):
        url = self.get_music_url(rid)
        content = requests.get(url, headers=headers, timeout=2).content

        return content

    def get_music_lrc(self, rid):
        lrc_data = requests.get(lrc_url.format(rid=rid), headers=headers, timeout=2).json()

        lrc_list = lrc_data.get('data', {}).get('lrclist', [{1: '无歌词', 2: '0'}])

        lrc = [list(l.values()) for l in lrc_list]

        return lrc

    def get_pic(self, url):
        pic = requests.get(url, headers=headers).content
        
        return pic

二、歌词操作

下面代码为 lrc.py 里的代码。

import re

class Lrc:
	def __init__(self):
		self.LRC = [[0.0, '无歌词']]
		self.Times = [0.0]
		self.Words = ['无歌词']

	def decode_from_str(self, lrc: str):
		lrc = lrc.strip('\n')
		res1 = self.SP_DTWDSTR.findall(lrc)
		res2 = list()
		res3 = dict()
		result = dict()
		for r in res1:
			res2.append([self.SP_DTSTR.findall(r[0]), r[1]])
		for r in res2:
			for t in r[0]:
				res3[self.tosec(t)] = r[1]

		result = sorted(list(res3.items()), key=lambda x: x[0])

		self.decode(result)

		return self.LRC

	def decode(self, lrcs):
		self.LRC = lrcs
		r = list(zip(*lrcs))
		self.Words = list(r[0])
		self.Times = list(map(self.tosec, list(r[1])))

	# 将字符串时间变为秒
	def tosec(self, t:str):
		res1 = t.split(':')[::-1]
		res2 = [float(r) * (60 ** i) for i, r in enumerate(res1)]
		result = sum(res2)

		return result

	# 根据浮点数播放进度获取对应歌词索引
	def get_index(self, t:float):
		times = [*self.Times, t]
		times.sort()
		
		return times.index(t) - 1

三、播放器

下面代码为 player.py 里的内容。

播放器的方法其实和 pygame.mixer.music 的方法差别不大。但要注意的是第 1~2 行代码是用于去除导入 pygame 模块时自动打印的信息。

from os import environ
environ['PYGAME_HIDE_SUPPORT_PROMPT'] = '1'

import pygame
from io import BytesIO

class Player:
    def __init__(self):
        pygame.mixer.init()
        self.music = pygame.mixer.music

    def reset(self):
        self.music.stop()
        pygame.mixer.pre_init()

    def load(self, filename):
        self.music.load(filename)

    def play(self):
        self.music.play()

    def pause(self):
        self.music.pause()

    def unpause(self):
        self.music.unpause()

    def stop(self):
        self.music.stop()

    def get_length(self):
        return self.music.get_length()

    def get_pos(self):
        return self.music.get_pos()

    def set_pos(self, value=0):
        self.music.rewind()
        self.music.set_pos(value)

    def get_volume(self):
        return self.music.get_volume()

    def set_volume(self, value=0.5):
        return self.music.get_volume(value)

    def addsong(self, filename):
        self.music.quene(filename)

四、GUI 界面

下面代码为 gui.py 中的代码。

有关教程见

from tkinter import *
from tkinter import ttk
from io import BytesIO
from mutagen.mp3 import MP3
from PIL import Image, ImageTk
import get
import player
import lrc

class Window(Tk):
    ischanging = False
    last_pos = 0

    words = ['']
    times = [0]
    
    def __init__(self):

        Tk.__init__(self)
        self.title('JIE 音乐')
        self.geometry('650x400')
        self.resizable(0, 0)

        self.set_notebook()
        self.set_control()

        self.set_weight()

        self.update()

        self.after(100, self.timer)

        self.mainloop()

    def set_notebook(self):
        self.nb = ttk.Notebook(self)
        self.nb.grid(row=0, column=0, sticky='nswe', padx=2, pady=1)

        self.set_search_frame()
        self.set_lrc_frame()

        self.nb.add(self.search_frame, text=' 搜索 ')
        self.nb.add(self.lrc_frame, text=' 歌词 ')

    # 控制框
    def set_control(self):
        self.control_frame = Frame(self)
        self.control_frame.grid(row=1, column=0, sticky='nswe', padx=2, pady=1)

        self.ctrl_pic = Canvas(self.control_frame, height=40, width=40)
        self.ctrl_pic.grid(row=0, column=0)

        self.play_btn = Label(self.control_frame, text='?', font=('宋体', 24, 'bold'), 
                              width=2, height=1, relief='flat')
        self.play_btn.bind('<Enter>', lambda event: self.play_btn.configure(fg='orange'))
        self.play_btn.bind('<Leave>', lambda event: self.play_btn.configure(fg='black'))
        self.play_btn.bind('<Button-1>', self.play_or_pause)
        self.play_btn.grid(row=0, column=1, sticky='nswe')

        self.var = IntVar()
        self.var.set(0)

        self.bar = Scale(self.control_frame, label='无歌曲', orient='horizontal', 
                         variable=self.var, showvalue=False, from_=0, to=0, 
                         command=self.change, width=10, length=500)
        self.bar.grid(row=0, column=2, sticky='nwe')

        self.download_btn = Label(self.control_frame, text='↓', font=('微软雅黑', 15), width=2)
        self.download_btn.bind('<Enter>', lambda event: self.download_btn.configure(fg='orange'))
        self.download_btn.bind('<Leave>', lambda event: self.download_btn.configure(fg='black'))
        self.download_btn.bind('<Button-1>', self.download)
        self.download_btn.grid(row=0, column=3, sticky='nswe')

    # 搜索界面
    def set_search_frame(self):
        self.search_frame = Frame(self.nb)

        self.inputbox = ttk.Entry(self.search_frame, width=14)
        self.inputbox.bind('<Return>',lambda event: self.get_datas(self.inputbox.get()))
        self.inputbox.grid(row=0, column=0, sticky='nswe', padx=(2, 0), pady=2)

        self.surebtn = ttk.Button(self.search_frame, text='搜索', width=6, 
                                  command=lambda: self.get_datas(self.inputbox.get()))
        self.surebtn.grid(row=0, column=1, columnspan=2, sticky='nswe', padx=(0, 2), pady=2)
        
        columns = [0, 1, 2, 3, 4]
        self.songstable = ttk.Treeview(self.search_frame, columns=columns, show='headings')
        
        self.songstable.column(0, width=25, anchor='w', stretch='no')
        self.songstable.heading(0, text='')
        self.songstable.column(1, width=200, anchor='w')
        self.songstable.heading(1, text='歌曲')
        self.songstable.column(2, width=70, anchor='w')
        self.songstable.heading(2, text='歌手')
        self.songstable.column(3, width=100, anchor='w')
        self.songstable.heading(3, text='专辑')
        self.songstable.column(4, width=45, anchor='w', stretch='no')
        self.songstable.heading(4, text='时长')

        self.songstable.grid(row=1, column=0, columnspan=2, sticky='nswe')
        self.songstable.bind('<Double-Button-1>', 
                             lambda event: self.selected(self.songstable.item(self.songstable.selection()[0], 'value')))

        self.songscroll = ttk.Scrollbar(self.search_frame, orient='vertical', 
                                        command=self.songstable.yview)
        self.songscroll.grid(row=1, column=2, sticky='nswe')

        self.songstable.configure(yscrollcommand=self.songscroll.set)

    # 歌词界面
    def set_lrc_frame(self):
        self.lrc_frame = Frame(self.nb)

        self.lrc_title = Label(self.lrc_frame, text='无歌曲', font=('微软雅黑', 15), anchor='w')
        self.lrc_title.grid(row=0, column=1, sticky='nswe', padx=(0, 40), pady=(40, 0))
        self.lrc_title2 = Label(self.lrc_frame, text='佚名', font=('微软雅黑', 10), fg='grey', anchor='w')
        self.lrc_title2.grid(row=1, column=1, sticky='nswe', padx=(0, 40))

        self.lrc_list = Listbox(self.lrc_frame, relief='flat', font=('微软雅黑', 12), 
                                highlightthickness=0, selectmode='single',
                                bg='SystemButtonFace', fg='#303030',
                                selectbackground='SystemButtonFace', selectforeground='orange')
        self.lrc_list.grid(row=2, column=1, sticky='nswe', padx=(0, 40), pady=(10, 40))
        self.lrc_list.insert('end', *([''] * 3), '无歌词')

        self.lrc_pic = Canvas(self.lrc_frame, width=240, height=240, relief='flat')
        self.lrc_pic.grid(row=0, column=0, rowspan=3, padx=40, pady=40)

    def set_weight(self):
        self.grid_rowconfigure(0, weight=1)
        self.grid_columnconfigure(0, weight=1)

        self.search_frame.grid_rowconfigure(1, weight=1)
        self.search_frame.grid_columnconfigure(0, weight=1)

        self.lrc_frame.grid_rowconfigure(2, weight=1)
        self.lrc_frame.grid_columnconfigure(1, weight=1)

    # 搜索
    def get_datas(self, kw='str'):
        t = self.songstable.get_children()
        for item in t:
            self.songstable.delete(item)

        datas = kuwo.search_kuwo(kw)

        for index, value in enumerate(datas):
            self.songstable.insert('','end',value=[index+1, *value])

    # 选中歌曲
    def selected(self, datas):
        player.reset()

        self.mdatas = datas
        self.last_pos = 0
        self.index = 0

        self.lrc = lrcdecoder.decode(kuwo.get_music_lrc(datas[-1]))
        self.words = lrcdecoder.Words
        self.times = lrcdecoder.Times

        self.content = kuwo.get_music_content(datas[-1])
        self.song_name = datas[1]
        self.song_artist = datas[2]

        self.pic_small = self.Tkpic(kuwo.get_pic(datas[-2]), 40)
        self.pic_large = self.Tkpic(kuwo.get_pic(datas[-3]), 240)

        self.ctrl_pic.create_image(0, 0, anchor='nw', image=self.pic_small)
        self.lrc_pic.create_image(0, 0, anchor='nw', image=self.pic_large)

        self.play_btn.configure(text='||')

        self.lrc_title.configure(text=self.song_name)
        self.lrc_title2.configure(text=self.song_artist)
     
        self.lrc_list.delete(0, 'end')
        self.lrc_list.insert('end', *[*([''] * 2), *self.words])

        byte = BytesIO(self.content)

        self.bar.configure(from_=0, to=MP3(byte).info.length, label=f'{datas[1]} - {datas[2]}')
        player.load(byte)
        player.play()

    # 将网络 png 图片用于 tkinter 中
    def Tkpic(self, pic, res):
        byte_obj = BytesIO(pic)

        pic = Image.open(byte_obj)
        pic = pic.resize((res, res), Image.ANTIALIAS)
        pic = ImageTk.PhotoImage(pic)

        return pic

    # 拖动进度条时
    def change(self, value):
        self.ischanging = True

    # 暂停、继续
    def play_or_pause(self, event):
        if self.play_btn['text'] == '||' :
            player.pause()
            self.play_btn.configure(text='?')
        else:
            player.unpause()
            self.play_btn.configure(text='||')

    # 下载音乐
    def download(self, event):
        with open(f'musics/{self.song_name} - {self.song_artist}.mp3', 'wb+') as f:
            f.write(self.content)

    # 定时器
    def timer(self):
        # 歌词同步
        if self.ischanging:
            self.ischanging = False
            self.last_pos = self.var.get() - player.get_pos() / 1000
            player.set_pos(self.var.get())
        else:
            self.var.set(player.get_pos() / 1000 + self.last_pos)

        # 歌词高亮
        index = lrcdecoder.get_index(player.get_pos() / 1000 + self.last_pos)
        self.lrc_list.selection_clear(0, 'end')
        self.lrc_list.selection_set(index + 2)

        # 滚动到指定位置
        index = index / len(self.words)
        index = index if index >= 0 else 0

        self.lrc_list.yview_moveto(index)

        self.after(200, self.timer)

kuwo = get.Kuwo()
player = player.Player()
lrcdecoder = lrc.Lrc()

五、启动程序

下面代码为 main.pyw 中的代码。双击此文件可以直接运行。

import gui

if __name__ == '__main__':
    gui.Window()

后记

这个音乐播放器还有一些不完善的地方,比如只能在有网络的情况下搜索,否则会报错;没有播放列表等等。

小伙伴们可以自己尝试改善这个音乐播放器,捕获网络异常,或者增加一些功能。欢迎评论区留言或者私信作者喔!


解析入口

本文:tkinter 做的音乐播放器

问题解决:

源码下载:https://download.csdn.net/download/weixin_48448842/20978386

点击上面的超链接可以查看对应部分的讲解和代码。
这是一些解决播放器问题的、用于播放器中一些复杂部分的讲解。


作者博客:https://blog.csdn.net/weixin_48448842
作者写这个花了大半月,很累,麻烦点个赞支持一下谢谢!

  Python知识库 最新文章
Python中String模块
【Python】 14-CVS文件操作
python的panda库读写文件
使用Nordic的nrf52840实现蓝牙DFU过程
【Python学习记录】numpy数组用法整理
Python学习笔记
python字符串和列表
python如何从txt文件中解析出有效的数据
Python编程从入门到实践自学/3.1-3.2
python变量
上一篇文章      下一篇文章      查看所有文章
加:2021-08-16 11:42:01  更:2021-08-16 11:42:13 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/15 10:29:37-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码