** MAJOR speed improvements **

Refactored code for TRUE multithreaded performance
startup time decreased from 2 secs to 0.2 secs
updates song time decreased from 3 secs to 0.1 secs
introduced proper and full error handler, can fully recover from errors
(including performing a full restart)
implemented web server to push updates and restart application (flask)
getting lyrics is truely mulithreaded now, all in memory.
This commit is contained in:
Brandon4466
2023-05-25 17:25:20 -07:00
parent 90b1448d93
commit 1e40570d57
8 changed files with 251 additions and 113 deletions

View File

@@ -9,10 +9,11 @@ import syncedlyrics
from PIL import Image, ImageTk, ImageDraw, ImageFilter
from io import BytesIO
import math
from time import sleep
from time import sleep, time
import threading
import queue
import numpy
from sys import exit
# SpotifyGUI - Made by Brandon Brunson
@@ -20,7 +21,7 @@ import numpy
if os.name == 'posix':
client_id = "df61ecadf09941eb87e693d37f3ad178"
client_secret = "ba97992d614e48d6b0d023998b2957cb"
os.system("xset -display :0 s 21600")
os.system("xset -display :0 s 1800")
if os.path.isfile("updated.cfg"):
print("NEW VER")
os.remove("updated.cfg")
@@ -42,6 +43,10 @@ redirect_uri = "http://127.0.0.1:8888/callback"
# client_id = "df61ecadf09941eb87e693d37f3ad178"
# client_secret = "ba97992d614e48d6b0d023998b2957cb"
# misc client id and secret (SpotifyGUI 3)
# client_id = "21ca0dc87c9441ceaf875a05d0199756"
# client_secret = "dd430e634ae94471aa70dfc22936be10"
# Set the user's Spotify username
username = "thebrandon45"
password = "Mariposa2502$"
@@ -80,14 +85,15 @@ count = 0
# wait_time = 6500
# hotword = "magical"
# sleep = False
# Create the tkinter window
root = ttk.Tk()
root.title("Media Controller")
root.geometry("1280x400")
root.attributes("-topmost", True)
root.overrideredirect(1)
if os.name == 'posix':
root.overrideredirect(1)
sv_ttk.use_dark_theme()
# Function to call the Spotify API to play the current track
@@ -102,7 +108,20 @@ def controlNext():
spotify.next_track()
def controlPrevious():
spotify.previous_track()
if track_progress < 5000:
spotify.previous_track()
else:
spotify.seek_track(0)
# def sleep():
# while not sleep:
# sleep(900000)
# kill(kill=sleep)
# development purposes
def kill(kill):
if kill is sleep:
exit(1)
def likeSong():
if spotify.current_user_saved_tracks_contains(tracks=[(spotify.current_playback()["item"]["id"])])[0] is False:
@@ -171,13 +190,13 @@ def start_playback_on_device():
def get_devices():
global count
# global count
# global wait_time
# global wait_time0
# count +=1
# if count < 69:
# wait_time = 6500
# elif count >= 69:
# wait_time = 3600000
count += 1
# count += 1
list_of_devices = spotify.devices()
# if list_of_devices == "{'devices': []}":
@@ -185,27 +204,32 @@ def get_devices():
# loadSearching_Devices()
# root.after(1000, get_devices)
# else:
if count > 210:
unloadDevices_list()
loadSleep()
root.after(3600, get_devices)
# if count > 210:
# unloadDevices_list()
# loadSleep()
# root.after(3600, get_devices)
if spotify.current_playback() != None:
count = 0
# unloadSearching_Devices()
unloadDevices_list()
loadNow_playing()
root.after(200, update_song_label)
root.after(800, update_song_label)
else:
count += 1
if count > 210:
kill(kill=sleep)
# unloadSearching_Devices()
# loadDevices_list()
devices_list.delete(0, ttk.END)
# list_of_devices = spotify.devices()
for num_of_device, garbage in enumerate(list_of_devices["devices"]):
devices_list.insert(num_of_device, list_of_devices["devices"][num_of_device]["name"])
else:
devices_list.delete(0, ttk.END)
# list_of_devices = spotify.devices()
for num_of_device, garbage in enumerate(list_of_devices["devices"]):
devices_list.insert(num_of_device, list_of_devices["devices"][num_of_device]["name"])
root.after(8500, get_devices)
def wakeup():
global count
count = 0
# def wakeup():
# global count
# count = 0
# def wakeup():
# global count
@@ -322,8 +346,12 @@ def get_colors(image_file, resize=150):
def getLyrics(artist_name, track_name):
global lrc
lrc = syncedlyrics.search("[" + track_name + "] [" + artist_name + "]")
q.put(lrc)
# def upNext():
# print(spotify.queue())
# up_next_label.config(text="Placeholder")
# def rgb_to_hex(r, g, b):
# return ('{:X}{:X}{:X}').format(r, g, b)
@@ -375,107 +403,123 @@ searching_for_devices_label = tk.Label(root, text="Searching for Devices...", fo
device_name_label = tk.Label(frame_artist_song, text="", font=("Helvetica", 12))
lyrics_label = tk.Label(lyrics_label_frame, text="", font=("Helvetica", 32), wraplength=(1280/3), justify=ttk.CENTER, background=bg_color)
album_art_label = tk.Label(album_art_frame, image=album_art_img)
# up_next_label = tk.Label(frame_artist_song, text="", font=("Helvetica", 12), wraplength=(1280/3), justify=ttk.CENTER, background=bg_color)
play_button.bind("<Button-1>", lambda e:controlPlay())
pause_button.bind("<Button-1>", lambda e:controlPause())
next_button.bind("<Button-1>", lambda e:controlNext())
previous_button.bind("<Button-1>", lambda e:controlPrevious())
# previous_button.bind("<Double-Button-1>", lambda e:controlPrevious(double=True))
album_art_label.bind("<Button-1>", lambda e:likeSong())
sleep_frame.bind("<Button-1>", lambda e:wakeup())
# development restart script (will push to update.py)
song_label.bind("<Double-Button-1>", lambda e:kill())
devices_list.bind("<Double-Button-1>", lambda e:kill())
# sleep_frame.bind("<Button-1>", lambda e:wakeup())
# devices_list.bind("<Button-1>", lambda e:wakeup())
# Function to update the song label with the current track's name
def update_song_label():
global lrc
global album_art_img
global isBright
# Get the current playback information
current_playback = spotify.current_playback()
# If there is no current playback, set the text of the song label to "No playback"
if current_playback is None:
unloadNow_playing()
loadDevices_list()
try:
global lrc
global album_art_img
global isBright
global track_progress
# Get the current playback information
current_playback = spotify.current_playback()
# If there is no current playback, set the text of the song label to "No playback"
if current_playback is None:
unloadNow_playing()
loadDevices_list()
root.after(200, get_devices)
# Update the song label every 1 second
elif current_playback.get("item") is not None:
track_name = current_playback["item"]["name"]
track_progress = current_playback["progress_ms"]
playing_status = current_playback["is_playing"]
track_progress_min = track_progress//(1000*60)%60
track_progress_sec = (track_progress//1000)%60
if track_name == song_label.cget("text"):
track_progress_formatted = ("{}:{:02d}".format(track_progress_min, track_progress_sec))
progress_bar.config(value=track_progress)
for line in str(lrc).splitlines():
if track_progress_formatted in line:
lyric = line.split("]")[1]
lyrics_label.config(text=lyric)
root.after(800, update_song_label)
root.after(200, get_devices)
# Update the song label every 1 second
elif current_playback.get("item") is not None:
track_name = current_playback["item"]["name"]
track_progress = current_playback["progress_ms"]
playing_status = current_playback["is_playing"]
track_progress_min = track_progress//(1000*60)%60
track_progress_sec = (track_progress//1000)%60
if track_name == song_label.cget("text"):
track_progress_formatted = ("{}:{:02d}".format(track_progress_min, track_progress_sec))
progress_bar.config(value=track_progress)
for line in str(lrc).splitlines():
if track_progress_formatted in line:
lyric = line.split("]")[1]
lyrics_label.config(text=lyric)
# if track_progress < 5000:
# threading.Thread(target=upNext).start()
root.after(800, update_song_label)
else:
start_time = time()
artist_name = current_playback["item"]["artists"][0]["name"]
threading.Thread(target=getLyrics, args=(artist_name, track_name)).start()
track_id = current_playback["item"]["id"]
track_duration = current_playback["item"]["duration_ms"]
device_name = current_playback["device"]["name"]
album_art_url = current_playback["item"]["album"]["images"][0]["url"]
device_name_label.config(text=device_name)
song_label.config(text=track_name)
artist_label.config(text=artist_name)
progress_bar.config(maximum=track_duration)
lyrics_label.config(text="")
album_art_img_data = requests.get(album_art_url).content
album_art_img_open = Image.open(BytesIO(album_art_img_data))
# bg_color_img = album_art_img_open.resize((1,1), resample=0)
# bg_color_img_pixel = bg_color_img.getpixel((0,0))
# bg_color_rgb = get_colors(album_art_img_open)
dominant_color = get_colors(album_art_img_open)
bg_color = "#" + '%02x%02x%02x' % (dominant_color)
# print(bg_color)
album_art_img_with_corners = addCorners(album_art_img_open, 15)
# addDropShadow(album_art_img_with_corners)
album_art_img = ImageTk.PhotoImage(album_art_img_with_corners.resize((300,300)))
album_art_label.config(image=album_art_img, background=bg_color)
root.config(background=bg_color)
frame_artist_song.config(background=bg_color)
device_name_label.config(background=bg_color)
song_label.config(background=bg_color)
artist_label.config(background=bg_color)
play_button.config(background=bg_color)
pause_button.config(background=bg_color)
next_button.config(background=bg_color)
previous_button.config(background=bg_color)
lyrics_label_frame.config(background=bg_color)
lyrics_label.config(background=bg_color)
isBright = colorUI(track_id, dominant_color)
# print(oauth.get_cached_token()["access_token"])
# print(current_playback["item"]["id"])
# canvas_url = canvas.get_canvas_for_track(access_token=oauth.get_cached_token()["access_token"], track_id=current_playback["item"]["id"])
# print(canvas_url)
# lrc = q.get()
print(time() - start_time)
root.after(500, update_song_label)
if playing_status == True:
play_button.grid_forget()
pause_button.grid(row=3, column=1, pady=(0,30))
elif playing_status == False:
pause_button.grid_forget()
play_button.grid(row=3, column=1, pady=(0,30))
else:
pass
else:
artist_name = current_playback["item"]["artists"][0]["name"]
threading.Thread(target=getLyrics, args=(artist_name, track_name)).start()
track_id = current_playback["item"]["id"]
track_duration = current_playback["item"]["duration_ms"]
device_name = current_playback["device"]["name"]
album_art_url = current_playback["item"]["album"]["images"][0]["url"]
device_name_label.config(text=device_name)
song_label.config(text=track_name)
artist_label.config(text=artist_name)
progress_bar.config(maximum=track_duration)
lyrics_label.config(text="")
album_art_img_data = requests.get(album_art_url).content
album_art_img_open = Image.open(BytesIO(album_art_img_data))
# bg_color_img = album_art_img_open.resize((1,1), resample=0)
# bg_color_img_pixel = bg_color_img.getpixel((0,0))
# bg_color_rgb = get_colors(album_art_img_open)
dominant_color = get_colors(album_art_img_open)
bg_color = "#" + '%02x%02x%02x' % (dominant_color)
# print(bg_color)
album_art_img_with_corners = addCorners(album_art_img_open, 15)
# addDropShadow(album_art_img_with_corners)
album_art_img = ImageTk.PhotoImage(album_art_img_with_corners.resize((300,300)))
album_art_label.config(image=album_art_img, background=bg_color)
root.config(background=bg_color)
frame_artist_song.config(background=bg_color)
device_name_label.config(background=bg_color)
song_label.config(background=bg_color)
artist_label.config(background=bg_color)
play_button.config(background=bg_color)
pause_button.config(background=bg_color)
next_button.config(background=bg_color)
previous_button.config(background=bg_color)
lyrics_label_frame.config(background=bg_color)
lyrics_label.config(background=bg_color)
isBright = colorUI(track_id, dominant_color)
# print(oauth.get_cached_token()["access_token"])
# print(current_playback["item"]["id"])
# canvas_url = canvas.get_canvas_for_track(access_token=oauth.get_cached_token()["access_token"], track_id=current_playback["item"]["id"])
# print(canvas_url)
lrc = q.get()
root.after(500, update_song_label)
if playing_status == True:
play_button.grid_forget()
pause_button.grid(row=3, column=1, pady=(0,20))
elif playing_status == False:
pause_button.grid_forget()
play_button.grid(row=3, column=1, pady=(0,20))
else:
pass
else:
root.after(1000, get_devices)
root.after(1000, get_devices)
except Exception as e:
print(e)
root.after(5000, update_song_label)
def loadNow_playing():
frame_artist_song.grid(row=0, column=1, rowspan=3, pady=(30,0), sticky="n")
device_name_label.grid(row=0, column=1)
artist_label.grid(row=2, column=1)
song_label.grid(row=1, column=1)
previous_button.grid(row=3, column=1, padx=(0,200), pady=(0,20))
play_button.grid(row=3, column=1, pady=(0,20))
next_button.grid(row=3, column=1, padx=(200,0), pady=(0,20))
# up_next_label.grid(row=3, column=1)
previous_button.grid(row=3, column=1, padx=(0,200), pady=(0,30))
play_button.grid(row=3, column=1, pady=(0,30))
next_button.grid(row=3, column=1, padx=(200,0), pady=(0,30))
progress_bar.grid(row=3, column=0, columnspan=3, sticky="wse")
album_art_frame.grid(row=0, column=0, rowspan=4)
album_art_label.grid(sticky="w")