** 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:
@@ -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")
|
||||
|
||||
Reference in New Issue
Block a user