python3+tkinter 网易云音乐多线程下载器第三版

python3+tkinter 网易云音乐多线程下载器第三版_第1张图片

一,新功能介绍

相比第一版,第三版增加了多线程下载功能,利用threading和queue队列实现多线程下载。

搜索功能:1.网易歌单链接https://music.163.com/#/playlist?id=2525632823,

                     或者              https://music.163.com/#/discover/toplist?id=3779629

                   2.歌曲名或者歌手,默认搜索20首

python3+tkinter 网易云音乐多线程下载器第三版_第2张图片python3+tkinter 网易云音乐多线程下载器第三版_第3张图片

音乐播放功能实现上一首下一首切换(listbox选择后点击播放,点下一首会自动播放),循环播放。

thinter的优点就是简单,毛病就是运行会很卡,多线程下载还是那个样,这个小工具可以批量下载,也可以在listbox多选后下载。

二,未实现功能

制作播放进度条,发现线程暂停和恢复有点问题,暂未完美实现,下次准备用pyqt5实现这个功能点。

 

 

python3+tkinter 网易云音乐多线程下载器第三版_第4张图片python3+tkinter 网易云音乐多线程下载器第三版_第5张图片python3+tkinter 网易云音乐多线程下载器第三版_第6张图片

三,代码演示

GUI界面

"""
之前做过一个版本,接口失效,现在更新版本
"""
import re,os
from tkinter import Tk,Label,StringVar,Listbox,Scrollbar,SINGLE,HORIZONTAL,Radiobutton,messagebox,\
    ALL,filedialog,Canvas,END,MULTIPLE,EXTENDED
from tkinter.ttk import Button,Entry
from neteasy_music.music_down import Music_download
from neteasy_music.music_play import Playclass
from tkinter import messagebox

class App:
    song_dic={}
    index=0
    def __init__(self):
        self.Mu=Music_download()#导入下载模块
        # self.Play=Playclass()#导入播放模块
        self.w=Tk()
        self.w.title('网易云音乐下载器V3.0')
        self.w.geometry('450x450')
        self.w.resizable(False,False)#锁住
        # self.w.config(bg='#535353')
        self.creat_base_surface()#设置基本框架
        self.surface_place_set()#设置布局
        self.tk_config_()
        self.w.mainloop()

    def creat_base_surface(self):
        self.E_get_url=StringVar()
        self.E_save_file=StringVar()
        self.E_get_searchflag=StringVar()
        self.R_process_get=StringVar()#线程
        self.pause_flag=StringVar(value='^_^')
        self.S_songlist_r=Scrollbar()
        self.S_songlist_l=Scrollbar(orient=HORIZONTAL)
        self.L_title=Label(self.w,text='网易云音乐下载播放器升级版',fg='blue')
        self.L_enterurl=Label(self.w,text='请输入URL歌单或单曲名或歌手!')
        self.E_url=Entry(self.w,textvariable=self.E_get_url)
        self.B_search=Button(self.w,text='搜索')
        self.L_songbox=Listbox(self.w)
        self.L_songlist=Label(self.w,text='【歌曲列表】',fg='#BA55D3')
        self.B_all_down=Button(self.w,text='批量下载')
        self.B_single_down=Button(self.w,text='单个下载')
        self.L_all_down=Label(self.w,bg='#EEE5DE')
        self.L_single_down=Label(self.w,bg='#EEE5DE')
        self.E_save=Entry(self.w,textvariable=self.E_save_file)
        self.B_save=Button(self.w,text='目录')
        self.R_down=Radiobutton(self.w,value='d',text='下载',variable=self.E_get_searchflag,fg='black')
        self.R_play=Radiobutton(self.w,value='p',text='播放',variable=self.E_get_searchflag,fg='black')
        self.B_play_song=Button(self.w,text='播放')
        self.B_pause=Button(self.w,textvariable=self.pause_flag)
        self.B_stop_play=Button(self.w,text='停止')
        self.B_previous_song=Button(self.w,text='上一首')
        self.B_next_song=Button(self.w,text='下一首')
        self.Show_canvas1=Canvas(self.w,bg='#B2DFEE')
        self.Show_canvas_process=Canvas(self.w,bg='white')
        self.Show_canvas2=Canvas(self.w,bg='#7EC0EE')
        self.R_process_s=Radiobutton(self.w,text='单线程',variable=self.R_process_get,value='s',fg='#BA55D3')
        self.R_process_d=Radiobutton(self.w,text='多线程',variable=self.R_process_get,value='m',fg='#BA55D3')

    def surface_place_set(self):
        self.L_title.place(x=10,y=10,width=180,height=30)
        self.L_enterurl.place(x=10,y=45,width=180,height=30)
        self.E_url.place(x=10,y=80,width=180,height=30)
        self.B_search.place(x=200,y=70,width=50,height=40)
        self.L_songbox.place(x=260,y=30,width=170,height=180)
        self.L_songlist.place(x=260,y=6,width=170,height=25)
        self.S_songlist_r.place(x=428,y=30,width=15,height=180)
        self.S_songlist_l.place(x=260,y=209,width=170,height=15)
        self.B_all_down.place(x=10,y=118,width=60,height=30)
        self.B_single_down.place(x=10,y=158,width=60,height=30)
        self.L_all_down.place(x=80,y=118,width=170,height=30)
        self.L_single_down.place(x=80,y=158,width=170,height=30)
        self.R_process_s.place(x=80,y=190,width=70,height=20)
        self.R_process_d.place(x=170,y=190,width=70,height=20)
        self.E_save.place(x=10,y=215,width=180,height=30)
        self.B_save.place(x=200,y=215,width=50,height=30)
        self.R_down.place(x=200,y=15,width=45,height=20)
        self.R_play.place(x=200,y=40,width=45,height=20)
        self.B_play_song.place(x=10,y=250,width=50,height=30)
        self.B_pause.place(x=10,y=285,width=50,height=30)
        self.B_stop_play.place(x=10,y=322,width=50,height=30)
        self.B_previous_song.place(x=10,y=362,width=50,height=35)
        self.B_next_song.place(x=10,y=402,width=50,height=35)
        self.Show_canvas1.place(x=70,y=250,width=370,height=30)
        self.Show_canvas_process.place(x=70,y=275,width=370,height=15)
        self.Show_canvas2.place(x=70,y=285,width=370,height=160)

    def tk_config_(self):
        self.S_songlist_r.config(command=self.L_songbox.yview)
        self.L_songbox['yscrollcommand']=self.S_songlist_r.set
        self.S_songlist_l.config(command=self.L_songbox.xview)
        self.L_songbox['xscrollcommand']=self.S_songlist_l.set
        self.L_songbox.config(selectmode=EXTENDED)
        self.E_get_searchflag.set('d')#设置下载
        self.R_process_get.set('m')#设置多线程
        self.B_save.config(command=self.file_open)
        self.B_search.config(command=self.search_song)
        self.B_single_down.config(command=self.single_down)
        self.B_all_down.config(command=self.all_songs_down)
        self.B_play_song.config(command=self.play_song)
        self.B_pause.config(command=self.pause_play)
        self.B_stop_play.config(command=self.stop_play)
        self.B_previous_song.config(command=self.play_previous_song)
        self.B_next_song.config(command=self.play_next_song)

    def file_open(self):
        self.file_=filedialog.askdirectory()
        self.E_save_file.set(self.file_)


    def show_listbox(self,msg):#显示在box,dic
        if isinstance(msg,dict):
            for id,name in msg.items():
                self.L_songbox.insert(END,id+'-'+name)
        elif isinstance(msg,list):
            for name in msg:
                self.L_songbox.insert(END,name)

    def show_canvas_msg(self,msg,w,h,color):#显示歌单信息
        self.Show_canvas1.create_text(w,h,text=msg,fill=color)

    def clear_box(self):#清除
        self.L_songbox.delete(0,END)

    def clear_canvas(self):#清空canvas
        self.Show_canvas1.delete(ALL)

    def creat_process_line(self):#进度条
        pass

    def drow_canvas_line(self):
        self.drow_line()

    def drow_line(self):
        self.Show_canvas1.create_line(0,24,370,24,fill='black',width=1)

    def search_song(self):
        """
        https://music.163.com/#/discover/toplist?id=2884035
        https://music.163.com/#/playlist?id=2337333174
        """
        pa_playlist=r'((https|http)?:\/\/)(music.163.com\/#\/)(playlist|discover\/toplist)\?id=[\d]{2,}'
        pa_name=r'[a-z]+|[A-Z]+|[\u4e00-\u9fa5]+'#汉字 字母数字
        self.clear_box()
        if re.match(pa_playlist,self.E_get_url.get()):#匹配歌单
            try:
                self.song_dic=self.Mu.get_music_list_res(self.E_get_url.get())
                # print(song_dic)

                # self.clear_canvas()
                self.show_listbox(self.song_dic)
                self.show_canvas_msg('歌单共有歌曲:{}首'.format(len(self.song_dic)),60,10,'blue')
            except Exception:
                messagebox.showerror(title='错误',message='此歌单未解析出歌曲')
        elif re.match(pa_name,self.E_get_url.get()) and '/' not in self.E_get_url.get():#匹配歌名,无//
            print('歌名')
            self.song_dic=self.Mu.search_songs_id(self.E_get_url.get())
            self.show_listbox(self.song_dic)
            self.show_canvas_msg('暂搜索歌曲:{}首'.format(len(self.song_dic)), 60, 10, 'blue')
        elif self.E_get_url.get()=='' and self.E_get_searchflag.get()=='p':
            if os.path.exists(self.E_save_file.get()):
                file_lis=os.listdir(self.E_save_file.get())
                file_lis=[i for i in file_lis if re.match("(.+.mp3)|(.+.wma)|(.+.wav)",i)]#清除非音乐文件
                self.show_listbox(file_lis)
                print(file_lis)
            else:
                messagebox.showwarning(title='警告',message='请选择目录')
        else:
            messagebox.showwarning(title='警告',message='清输入正确的歌单或者歌名')

    def single_down(self):#下载标识,线程,路径,id
        dic={}
        print('*'*100)
        print('下载单曲')
        print(self.song_dic)
        print(self.L_songbox.curselection())
        print(len(self.L_songbox.curselection()))
        print('*'*100)
        if self.E_get_searchflag.get()=='d':
            if os.path.exists(self.E_save_file.get()):
                if len(self.L_songbox.curselection())>0:
                    mp3_list=list(self.L_songbox.curselection())
                    for i in mp3_list:
                        id = self.L_songbox.get(i).split('-')[0]
                        name = self.L_songbox.get(i).split('-')[1]
                        dic[id]=name
                    self.Mu.down_single_song(self.E_save_file.get(),dic)
                    if len(mp3_list)>1:
                        self.L_single_down.config(text='成功下载{}首歌曲'.format(len(mp3_list)),fg='green')
                    elif len(mp3_list)==1:
                        self.L_single_down.config(text='成功下载歌曲{}'.format(self.L_songbox.get(self.L_songbox.curselection()).split('-')[1]),fg='green')

                else:
                    messagebox.showwarning(title='警告',message='请选择单曲')
            else:
                messagebox.showerror(title='警告',message='文件夹路径不存在')
        else:
            messagebox.showerror(title='错误',message='请选择下载控件')


    def all_songs_down(self):
        print('*'*100)
        print('批量下载')
        print(self.song_dic)
        print(self.R_process_get.get())
        print(self.E_save_file.get())
        print('*'*100)
        if self.E_get_searchflag.get()=='d':
            if len(self.song_dic)>0:
                if os.path.exists(self.E_save_file.get()):
                    self.Mu.down_songs(self.R_process_get.get(),self.E_save_file.get(), self.song_dic)
                    self.L_all_down.config(text='列表已全部下载完成',fg='green')
                else:
                    messagebox.showwarning(title='警告',message='文件夹错误')
            else:
                messagebox.showwarning(title='警告',message='列表为空')
        else:
            messagebox.showerror(title='错误', message='请选择下载控件')


    def play_song(self):
        if self.E_get_searchflag.get()=='p':
            print('播放歌曲')
            print(self.L_songbox.curselection())
            if len(self.L_songbox.curselection())==0:
                messagebox.showwarning(title='警告',message='列表为空')
            else:
                self.play=Playclass()
                print(self.pause_flag.get())
                file_mp3=self.L_songbox.get(self.L_songbox.curselection())
                if re.match('.+(mp3|wma|wav)',file_mp3):
                    self.play.play_music_mp3(self.E_save_file.get()+'/'+file_mp3)
                    self.pause_flag.set('暂停')
                else:
                    messagebox.showerror(title='错误',message='文件不是标准mp3文件')
        else:
            messagebox.showerror(title='错误',message='请选择播放控件')

    def pause_play(self):
        # print('暂停')
        self.play.pause_button_click(self.pause_flag)

    def stop_play(self):
        self.play.stop_button_click()

    def play_previous_song(self):#上一首
        try:
            self.index=min(self.L_songbox.curselection())
            print(self.index)
            if len(self.L_songbox.curselection())>0:
                self.index = self.L_songbox.curselection()[0]
                self.index -= 1
                if self.index<0:  # 选择空
                    self.index = self.L_songbox.size()-1
                    self.L_songbox.selection_clear(0)  # 此处要清除掉di一行
            print('bf', self.index)
            self.L_songbox.select_set(self.index)
            self.L_songbox.selection_clear(self.index +1)  # 此处会多选,要清除掉
            self.play_song()
        except:
            messagebox.showwarning(title='警告',message='列表为空')


    def play_next_song(self):#下一首
        try:
            self.index=min(self.L_songbox.curselection())
            print(self.index)
            if len(self.L_songbox.curselection())>0:
                self.index = self.L_songbox.curselection()[0]
                self.index += 1
                if self.index==self.L_songbox.size():  # 选择空
                    self.index = 0
                    self.L_songbox.selection_clear(self.L_songbox.size()-1)  # 此处要清除掉最后一行
            print('bf', self.index)
            self.L_songbox.select_set(self.index)
            self.L_songbox.selection_clear(self.index - 1)  # 此处会多选,要清除掉
            self.play_song()
        except:
            messagebox.showwarning(title='警告',message='列表为空')


if __name__ == '__main__':
    a=App()

多线程下载代码

#coding=gbk
import os
import time
import threading
import queue
import requests
from functools import reduce
lock=threading.Lock()

class MyThreading(threading.Thread):
    def __init__(self,headers,base_url,path,que):
        super().__init__()
        self.headers=headers
        self.base_url=base_url
        self.path=path
        self.que=que

    def replace_char(self,s, oldChar, newChar):
        return reduce(lambda s, char: s.replace(char, newChar), oldChar, s)

    def downloader(self):
        #队列的id和name组合url和mp3名
        msg=self.que.get().split(':')
        url=self.base_url+msg[0]
        name=msg[1]
        if not os.path.exists(self.path):
            os.mkdir(self.path)
            print('文件夹{}已创建'.format(self.path))
        try:
            if not os.path.exists(self.path+'/'+name+'.mp3'):
                data = requests.get(url, headers=self.headers).content
                with open(self.path+'/'+name+'.mp3','wb') as f:
                    f.write(data)
                    print('歌曲:{}已下载完成'.format(name))
            else:
                print('歌曲:{}已存在'.format(name))
        except Exception as f:
            print('解析错误',f.args)



    def run(self):
        while True:
            if not self.que.empty():
                #执行下载程序会阻塞
                self.downloader()
                # time.sleep(0.1)
            else:
                print('队列空')
                break

class Threading_fuc:
    que = queue.Queue()
    def __init__(self,dic,headers,base_url,path):
        self.dic=dic
        self.headers=headers
        self.base_url=base_url
        self.path=path

    def replace_char(self,s, oldChar, newChar):
            return reduce(lambda s, char: s.replace(char, newChar), oldChar, s)

    def threading_run(self):
        start=time.time()
        for id,name in self.dic.items():
            name=self.replace_char(name,',/|\t?:',' ')
            self.que.put(id+':'+name)
        for n in range(1,5):
           tar=MyThreading(self.headers,self.base_url,self.path,self.que)
           tar.start()
           tar.join()
        end=time.time()
        print('耗时:{}'.format(end-start))

 

你可能感兴趣的:(python3+tkinter 网易云音乐多线程下载器第三版)