I made a Tkinter MP3 Player with Pygame and am looking for tips to improve. Explanation offered in comments in code, but it generally works by getting all the files in a folder. (Have censored filepaths for privacy.)
import os
import glob
import shutil
import pygame
import tkinter as tk
from pytube import YouTube
import tkinter.messagebox
from tkinter import filedialog as fd
from tkinter import simpledialog
from mutagen.mp3 import MP3
from klaxon import klaxon
import re
import applescript
from youtube_search import YoutubeSearch
def sorted_alphanumeric(data): #for sorting out the music in the tkinter listbox
convert = lambda text: int(text) if text.isdigit() else text.lower()
alphanum_key = lambda key: [ convert(c) for c in re.split('([0-9]+)', key) ]
return sorted(data, key=alphanum_key)
def listdir_nohidden(path): #for getting all files in a folder without
return sorted_alphanumeric(glob.glob(os.path.join(path, '*')))
def move_file(file): #for moving a music file to the folder
try:
name_and_ext = os.path.basename(file)
target = name_and_ext
shutil.copyfile(file, target)
except TypeError:
pass
def mp4_mp3(filepath): #for converting the downloaded mp4 file from pytube into mp3 format
klaxon(title = 'MP3 Player', subtitle = 'Downloading YouTube...', message = 'This will take some time.')
from moviepy.editor import VideoFileClip
try:
clip = VideoFileClip(filepath)
clip.write_videofile(filepath.replace('.mp4', '.mp3'), logger = None)
clip.close()
except:
pass
os.remove(filepath)
name, ext = os.path.splitext(filepath)
os.remove(name + 'TEMP_MPY_wvf_snd.mp3')
def download(link): #for downloading the youtube video
yt = YouTube(link)
video = yt.streams.first()
filepath = video.download('/Users/****/Desktop/****/****/****/****/Files')
mp4_mp3(filepath)
update_music()
def get_results(search): #for getting the youtube video names
results = YoutubeSearch(search, max_results = 10).to_dict()
res = []
for x in range(10):
video = results[x]
info = []
info.append(video['title'])
info.append('youtube.com' + video['url_suffix'])
res.append(info)
return res
def open_window(): #for displaying the youtube video names for downloading
ans = simpledialog.askstring(title = 'YouTube Video Downloader', prompt = 'What would you like to seach for on YouTube?')
if ans is not None:
window2 = tk.Tk()
window2.title(f'YouTube Search Results For {ans}')
res = get_results(ans)
tk.Button(window2, text = res[0][0], command = lambda : download(res[0][1])).grid(row = 0, column = 0, padx = 5, pady = 5)
tk.Button(window2, text = res[1][0], command = lambda : download(res[1][1])).grid(row = 0, column = 1, padx = 5, pady = 5)
tk.Button(window2, text = res[2][0], command = lambda : download(res[2][1])).grid(row = 1, column = 0, padx = 5, pady = 5)
tk.Button(window2, text = res[3][0], command = lambda : download(res[3][1])).grid(row = 1, column = 1, padx = 5, pady = 5)
tk.Button(window2, text = res[4][0], command = lambda : download(res[4][1])).grid(row = 2, column = 0, padx = 5, pady = 5)
tk.Button(window2, text = res[5][0], command = lambda : download(res[5][1])).grid(row = 2, column = 1, padx = 5, pady = 5)
tk.Button(window2, text = res[6][0], command = lambda : download(res[6][1])).grid(row = 3, column = 0, padx = 5, pady = 5)
tk.Button(window2, text = res[7][0], command = lambda : download(res[7][1])).grid(row = 3, column = 1, padx = 5, pady = 5)
tk.Button(window2, text = res[8][0], command = lambda : download(res[8][1])).grid(row = 4, column = 0, padx = 5, pady = 5)
tk.Button(window2, text = res[9][0], command = lambda : download(res[9][1])).grid(row = 4, column = 1, padx = 5, pady = 5)
def convert_mils(mils): #for converting the time from pygame.mixer.music.get_pos() into correct format
seconds = int((mils/1000) % 60)
minutes = int((mils/(1000 * 60)) % 60)
return f'{minutes:02d}:{seconds:02d}'
def convert_secs(seconds): #for converting the seconds from mutagen.mp3.MP3(song).info.length into correct format
m = int(seconds % 3600 // 60)
s = int(seconds % 3600 % 60)
return f'{m:02d}:{s:02d}'
def add_music(): #adding music as the user wishes
try:
res = applescript.AppleScript('display dialog "How would you like to add music?" buttons {"Cancel", "YouTube Search", "File Upload"} default button "File Upload"').run()
for item in res:
new_res = res[item]
if new_res == 'File Upload':
play_pause()
file = get_music()
if file != 'improper format':
move_file(file)
window.after(100, update_music)
else:
open_window()
window.after(100, update_music)
except applescript.ScriptError:
pass
def delete_music():
selected_file = play_list.get(tk.ACTIVE)
confirm = tkinter.messagebox.askquestion('MP3 Player', 'Are you sure you want to delete ' + selected_file + '?')
if confirm == 'yes':
if selected_file == title.get():
pygame.mixer.music.stop()
title.set('')
os.remove(selected_file + '.mp3')
window.after(100, update_music)
def rename_music(): #renameing the song
selected_song = play_list.get(tk.ACTIVE)
ans = simpledialog.askstring(title = 'Music Renaming', prompt = f'What would you like to rename {selected_song} to?')
if ans is not None:
os.rename(selected_song + '.mp3', ans + '.mp3')
title.set(ans)
update_music()
def edit_music(): #displaying options for editing
selected_file = play_list.get(tk.ACTIVE)
try:
response = applescript.AppleScript('set res to display dialog "Actions For Song ' + selected_file + '" buttons {"Cancel", "Rename", "Delete"} default button "Delete"').run()
for item in response:
res = response[item]
if res == "Delete":
delete_music()
else:
rename_music()
except:
pass
def update_music(): #for updating the files in tkinter Listbox
play_list.delete(0, tk.END)
song_list = listdir_nohidden('/Users/****/Desktop/****/****/****/****/Files')
for x in range(len(song_list)):
orig = song_list[x]
name = os.path.basename(orig)
song_list[x] = name
pos = 0
for item in song_list:
play_list.insert(pos, item.replace('.mp3', ''))
pos += 1
if play_list.size() > 10:
scroll.pack(side = 'right', fill = 'y')
else:
scroll.pack_forget()
def unconvert(mins_and_secs): #for changing the '{mins:02d}:{secs:02d}' format into seconds
mins, secs = mins_and_secs.split(':')
if mins[0] == '0':
mins = mins[1:]
if secs[0] == '0':
secs = secs[1:]
more_secs = int(mins) * 60
total_secs = more_secs + int(secs)
return total_secs
def update_time(): #for updating music time tracker
global already_run
if pygame.mixer.music.get_pos() != -1:
current_time = pygame.mixer.music.get_pos()
song = MP3(title.get() + '.mp3')
seconds_total = int(song.info.length)
mins_and_secs = convert_mils(current_time)
current_sec = unconvert(mins_and_secs)
remaining_secs = seconds_total - current_sec
remaining_secs = convert_secs(remaining_secs)
music_time.set(f'{mins_and_secs}\t-{remaining_secs}')
else:
if already_run is True:
music_time.set('')
next_song()
already_run = True
window.after(99, update_time)
def get_music(): #getting a file to add to songs
f = fd.askopenfile(filetypes = (('MP3 Files', '*.mp3'), ('All Files', '*.*')))
if f is not None:
file = f.name
root, ext = os.path.splitext(file)
if ext in ['.mp3']:
return file
else:
tkinter.messagebox.showinfo('MP3 Player', 'Must be MP3 file format!')
return 'improper format'
def play(): #playing the music
if already_run is not True:
update_time()
pygame.mixer.music.stop()
pygame.mixer.music.load(play_list.get(tk.ACTIVE) + '.mp3')
title.set(play_list.get(tk.ACTIVE))
pygame.mixer.music.play()
status.set('Pause')
def pause(): #pausing music
pygame.mixer.music.pause()
def unpause(): #resuming music
pygame.mixer.music.unpause()
def change_volume(volume): #chaning volume with tk.Scale
pygame.mixer.music.set_volume(int(volume)/100)
def on_closing(): #what to do when window is closed
pygame.mixer.music.stop()
pygame.mixer.quit()
window.destroy()
def next_selection(): #to select the next song
song_name = title.get()
index = play_list.get(0, "end").index(song_name)
selection_indices = play_list.curselection()
play_list.selection_clear(selection_indices)
play_list.activate(index)
play_list.selection_set(index)
selection_indices = play_list.curselection()
next_selection = 0
if len(selection_indices) > 0:
last_selection = int(selection_indices[-1])
play_list.selection_clear(selection_indices)
if last_selection < play_list.size() - 1:
next_selection = last_selection + 1
play_list.activate(next_selection)
play_list.selection_set(next_selection)
def next_song(): #to select next song and play it
next_selection()
window.after(99, play)
def pause_play(): #to change the button from pause to resume
stat = status.get()
if stat == 'Pause':
pause()
status.set('Resume')
else:
unpause()
status.set('Pause')
def main(): #main function
global window, title, play_list, music_time, scroll, already_run, status
already_run = False
window = tk.Tk()
window.title('MP3 Player')
window.configure(bg = 'blue')
window.resizable(False, False)
window.protocol('WM_DELETE_WINDOW', on_closing)
controls = tk.Frame()
controls.grid(column = 1, row = 1, sticky = 'nsew', padx = 10, pady = 10)
controls.configure(bg = 'blue')
buttons = tk.Frame()
buttons.grid(column = 0, row = 0, sticky = 'nsew', padx = 10, pady = 10)
buttons.configure(bg = 'blue')
songs = tk.Frame()
songs.grid(column = 0, row = 1, sticky = 'nsew', padx = 10, pady = 10)
songs.configure(bg = 'blue')
song = tk.Frame()
song.grid(column = 1, row = 0, sticky = 'nsew', padx = 10, pady = 10)
song.configure(bg = 'blue')
song_list = listdir_nohidden('/Users/****/Desktop/****/****/****/****/Files')
for x in range(len(song_list)):
orig = song_list[x]
name = os.path.basename(orig)
song_list[x] = name
play_list = tk.Listbox(songs, bg='yellow', selectmode=tk.SINGLE)
scroll = tk.Scrollbar(songs)
play_list.config(yscrollcommand = scroll.set)
scroll.config(command = play_list.yview)
pos = 0
for item in song_list:
play_list.insert(pos, item.replace('.mp3', ''))
pos += 1
pygame.init()
pygame.mixer.init()
title = tk.StringVar()
music_time = tk.StringVar()
status = tk.StringVar()
status.set('Pause')
pause_button = tk.Button(controls, textvariable = status, command = lambda : pause_play())
play_button = tk.Button(controls, text = 'Play', command = lambda : play())
add_music_button = tk.Button(buttons, text = 'Add Music', command = lambda : add_music())
delete_music_button = tk.Button(buttons, text = 'Edit Music', command = lambda : edit_music())
song_title = tk.Label(song, textvariable = title, fg = 'yellow', bg = 'blue')
song_time = tk.Label(song, textvariable = music_time, fg = 'yellow', bg = 'blue')
volume_scale = tk.Scale(controls, from_ = 0, to = 100, tickinterval = 0.00001, showvalue = 0, label = ' Volume', command = change_volume, orient = tk.HORIZONTAL)
volume_scale.set(50)
add_music_button.pack(side='top', fill = 'x')
volume_scale.grid(column = 0, row = 4, sticky = 'nsew', padx = 4, pady = 4)
delete_music_button.pack(side='bottom', fill = 'x')
play_list.pack(side = 'left', fill = 'both')
song_title.pack(side = 'top', fill = 'x')
song_time.pack(side = 'bottom', fill = 'x')
play_button.grid(column = 0, row = 1, sticky = 'nsew', padx = 4, pady = 4)
pause_button.grid(column = 0, row = 2, sticky = 'nsew', padx = 4, pady = 4)
os.chdir('/Users/****/Desktop/****/****/****/****/Files')
window.after(10, update_music())
window.mainloop()
if __name__ == '__main__':
main()
play_pause
is undefined. \$\endgroup\$