import spotipy from spotipy.oauth2 import SpotifyOAuth import tkinter as ttk from tkinter import ttk as tk import sv_ttk import requests import os import syncedlyrics from PIL import Image, ImageTk, ImageDraw, ImageFilter from io import BytesIO import math from time import sleep import threading import queue import numpy # SpotifyGUI - Made by Brandon Brunson # import speech_recognition as sr if os.name == 'posix': client_id = "df61ecadf09941eb87e693d37f3ad178" client_secret = "ba97992d614e48d6b0d023998b2957cb" os.system("xset -display :0 s 21600") if os.path.isfile("updated.cfg"): print("NEW VER") os.remove("updated.cfg") sleep(5) else: client_id = "69b82a34d0fb40be80b020eae8e80f25" client_secret = "455575b0e3db44acbbfaa0c419bc3c10" q = queue.Queue() # import canvas # development client id and secret (SpotifyGUI) # client_id = "69b82a34d0fb40be80b020eae8e80f25" # client_secret = "455575b0e3db44acbbfaa0c419bc3c10" redirect_uri = "http://127.0.0.1:8888/callback" # embeeded client id and secret (SpotifyGUI 2) # client_id = "df61ecadf09941eb87e693d37f3ad178" # client_secret = "ba97992d614e48d6b0d023998b2957cb" # Set the user's Spotify username username = "thebrandon45" password = "Mariposa2502$" # os.environ["DISPLAY"] = ":0" # def setup(): # checkDisplay() # def checkDisplay(): # while True: # if os.name == 'posix': # if "DISPLAY" in os.environ: # break # else: # sleep(0.25) # pass # else: # break # Get the user's Spotify authorization token scope = "user-read-playback-state,user-modify-playback-state,user-library-read,user-library-modify" oauth = SpotifyOAuth(client_id=client_id, client_secret=client_secret, redirect_uri=redirect_uri, scope=scope, requests_timeout=30) # Create a Spotify object with the user's authorization token spotify = spotipy.Spotify(auth_manager=oauth) # access_token = SpotifyOAuth.get_access_token(spotify) # print(SpotifyOAuth.refresh_access_token(refresh_token=access_token)) bg_color = "#000000" count = 0 # wait_time = 6500 # hotword = "magical" # Create the tkinter window root = ttk.Tk() root.title("Media Controller") root.geometry("1280x400") root.attributes("-topmost", True) root.overrideredirect(1) sv_ttk.use_dark_theme() # Function to call the Spotify API to play the current track def controlPlay(): spotify.start_playback() # Function to call the Spotify API to pause the current track def controlPause(): spotify.pause_playback() def controlNext(): spotify.next_track() def controlPrevious(): spotify.previous_track() def likeSong(): if spotify.current_user_saved_tracks_contains(tracks=[(spotify.current_playback()["item"]["id"])])[0] is False: spotify.current_user_saved_tracks_add(tracks=[(spotify.current_playback()["item"]["id"])]) if isBright is True: play_button.config(image=play_heart_img_black) pause_button.config(image=pause_heart_img_black) else: play_button.config(image=play_heart_img) pause_button.config(image=pause_heart_img) else: spotify.current_user_saved_tracks_delete(tracks=[(spotify.current_playback()["item"]["id"])]) if isBright is True: play_button.config(image=play_img_black) pause_button.config(image=pause_img_black) else: play_button.config(image=play_img) pause_button.config(image=pause_img) def colorUI(track_id, dominant_color): if spotify.current_user_saved_tracks_contains(tracks=[track_id])[0] is True: liked_song = True else: liked_song = False if math.sqrt(0.299 * (dominant_color[0] ** 2) + 0.587 * (dominant_color[1] ** 2) + 0.114 * (dominant_color[2] ** 2)) > 170: is_bright = True if liked_song is True: play_button.config(image=play_heart_img_black) pause_button.config(image=pause_heart_img_black) else: play_button.config(image=play_img_black) pause_button.config(image=pause_img_black) song_label.config(foreground="black") artist_label.config(foreground="black") device_name_label.config(foreground="black") lyrics_label.config(foreground="black") next_button.config(image=next_img_black) previous_button.config(image=previous_img_black) else: is_bright = False if liked_song is True: play_button.config(image=play_heart_img) pause_button.config(image=pause_heart_img) else: play_button.config(image=play_img) pause_button.config(image=pause_img) song_label.config(foreground="white") artist_label.config(foreground="white") device_name_label.config(foreground="white") lyrics_label.config(foreground="white") next_button.config(image=next_img) previous_button.config(image=previous_img) return is_bright def start_playback_on_device(): # global count # global wait_time # count = 0 # wait_time = 6500 device_selections = devices_list.curselection() list_of_devices = spotify.devices() list_of_devices = spotify.devices() device_id = list_of_devices["devices"][device_selections[0]]["id"] spotify.transfer_playback(device_id=device_id) def get_devices(): global count # global count # global wait_time # count +=1 # if count < 69: # wait_time = 6500 # elif count >= 69: # wait_time = 3600000 count += 1 list_of_devices = spotify.devices() # if list_of_devices == "{'devices': []}": # unloadDevices_list() # loadSearching_Devices() # root.after(1000, get_devices) # else: if count > 210: unloadDevices_list() loadSleep() root.after(3600, get_devices) if spotify.current_playback() != None: # unloadSearching_Devices() unloadDevices_list() loadNow_playing() root.after(200, update_song_label) else: # 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"]) root.after(8500, get_devices) def wakeup(): global count count = 0 # def wakeup(): # global count # global wait_time # count = 0 # wait_time = 6500 def addCorners(im, rad): circle = Image.new('L', (rad * 2, rad * 2), 0) draw = ImageDraw.Draw(circle) draw.ellipse((0, 0, rad * 2 - 1, rad * 2 - 1), fill=255) alpha = Image.new('L', im.size, 255) w, h = im.size alpha.paste(circle.crop((0, 0, rad, rad)), (0, 0)) alpha.paste(circle.crop((0, rad, rad, rad * 2)), (0, h - rad)) alpha.paste(circle.crop((rad, 0, rad * 2, rad)), (w - rad, 0)) alpha.paste(circle.crop((rad, rad, rad * 2, rad * 2)), (w - rad, h - rad)) im.putalpha(alpha) return im # def addDropShadow( image, offset=(5,5), background=0xffffff, shadow=0x444444, # border=8, iterations=3): # """ # Add a gaussian blur drop shadow to an image. # image - The image to overlay on top of the shadow. # offset - Offset of the shadow from the image as an (x,y) tuple. Can be # positive or negative. # background - Background colour behind the image. # shadow - Shadow colour (darkness). # border - Width of the border around the image. This must be wide # enough to account for the blurring of the shadow. # iterations - Number of times to apply the filter. More iterations # produce a more blurred shadow, but increase processing time. # """ # # Create the backdrop image -- a box in the background colour with a # # shadow on it. # totalWidth = image.size[0] + abs(offset[0]) + 2*border # totalHeight = image.size[1] + abs(offset[1]) + 2*border # back = Image.new(image.mode, (totalWidth, totalHeight), background) # # Place the shadow, taking into account the offset from the image # shadowLeft = border + max(offset[0], 0) # shadowTop = border + max(offset[1], 0) # back.paste(shadow, [shadowLeft, shadowTop, shadowLeft + image.size[0], # shadowTop + image.size[1]] ) # # Apply the filter to blur the edges of the shadow. Since a small kernel # # is used, the filter must be applied repeatedly to get a decent blur. # n = 0 # while n < iterations: # back = back.filter(ImageFilter.BLUR) # n += 1 # # Paste the input image onto the shadow backdrop # imageLeft = border - min(offset[0], 0) # imageTop = border - min(offset[1], 0) # back.paste(image, (imageLeft, imageTop)) # return back # def get_colors(image_file, resize=150): # # Resize image to speed up processing # image_file.thumbnail((resize, resize)) # max_count_index = numpy.argmax(numpy.unique(numpy.array(image_file).reshape(-1, numpy.array(image_file).shape[-1]), axis=0, return_counts=True)[1]) # # colors = list() # # for i in range(numcolors): # # palette_index = color_counts[i][1] # # dominant_color = palette[palette_index*3:palette_index*3+3] # # colors.append(tuple(dominant_color)) # return tuple(numpy.unique(numpy.array(image_file).reshape(-1, numpy.array(image_file).shape[-1]), axis=0, return_counts=True)[0][max_count_index]) def get_colors(image_file, resize=150): # Resize image to speed up processing image_file.thumbnail((resize, resize)) # Reduce to palette numpy_array = numpy.array(image_file) pixels = numpy_array.reshape(-1, numpy_array.shape[-1]) color_counts = numpy.unique(pixels, axis=0, return_counts=True) max_count_index = numpy.argmax(color_counts[1]) # colors = list() # for i in range(numcolors): # palette_index = color_counts[i][1] # dominant_color = palette[palette_index*3:palette_index*3+3] # colors.append(tuple(dominant_color)) return tuple(color_counts[0][max_count_index]) # def get_colors(image_file, numcolors=1, resize=150): # # Resize image to speed up processing # image_file.thumbnail((resize, resize)) # # Reduce to palette # paletted = image_file.convert('P', palette=Image.ADAPTIVE, colors=numcolors) # # Find dominant colors # palette = paletted.getpalette() # # color_counts = sorted(paletted.getcolors(), reverse=True) # dominant_color = (palette[0], palette[1], palette[2]) # # colors = list() # # for i in range(numcolors): # # palette_index = color_counts[i][1] # # dominant_color = palette[palette_index*3:palette_index*3+3] # # colors.append(tuple(dominant_color)) # return dominant_color def getLyrics(artist_name, track_name): lrc = syncedlyrics.search("[" + track_name + "] [" + artist_name + "]") q.put(lrc) # def rgb_to_hex(r, g, b): # return ('{:X}{:X}{:X}').format(r, g, b) play_img = ttk.PhotoImage(file="icons/play.png") pause_img = ttk.PhotoImage(file="icons/pause.png") play_heart_img = ttk.PhotoImage(file="icons/play-heart.png") pause_heart_img = ttk.PhotoImage(file="icons/pause-heart.png") next_img = ttk.PhotoImage(file="icons/next.png") previous_img = ttk.PhotoImage(file="icons/previous.png") play_img_black = ttk.PhotoImage(file="icons/play-black.png") pause_img_black = ttk.PhotoImage(file="icons/pause-black.png") play_heart_img_black = ttk.PhotoImage(file="icons/play-heart-black.png") pause_heart_img_black = ttk.PhotoImage(file="icons/pause-heart-black.png") next_img_black = ttk.PhotoImage(file="icons/next-black.png") previous_img_black = ttk.PhotoImage(file="icons/previous-black.png") album_art_img = "" frame_artist_song = ttk.Frame(root, width=(1280/3), height=400, bg=bg_color) album_art_frame = ttk.Frame(root) lyrics_label_frame = ttk.Frame(root, width=(1280/3), height=400, bg=bg_color) lyrics_label_frame.grid_propagate(0) sleep_frame = ttk.Frame(root, width=(1280/3), height=400, bg=bg_color) root.grid_rowconfigure(0, weight=1) root.grid_rowconfigure(1, weight=1) root.grid_rowconfigure(2, weight=1) root.grid_columnconfigure(0, weight=1) root.grid_columnconfigure(1, weight=1) root.configure(background=bg_color) lyrics_label_frame.grid_rowconfigure(0, weight=1) lyrics_label_frame.grid_columnconfigure(0, weight=1) # Create the media control buttons and a text label play_button = ttk.Label(root, image=play_img, borderwidth=0) pause_button = ttk.Label(root, image=pause_img, borderwidth=0) next_button = ttk.Label(root, image=next_img, borderwidth=0) previous_button = ttk.Label(root, image=previous_img, borderwidth=0) artist_label = tk.Label(frame_artist_song, text="", font=("Helvetica", 24), wraplength=(1280/3), justify=ttk.CENTER, background=bg_color) song_label = tk.Label(frame_artist_song, text="", font=("Helvetica", 32), wraplength=(1280/3), justify=ttk.CENTER, background=bg_color) get_devices_button = tk.Button(root, text="Get Devices", command=get_devices) start_playback_on_device_button = tk.Button(root, text="Start Playback on Device", command=start_playback_on_device) devices_list = ttk.Listbox(root, selectmode=ttk.SINGLE, font=("Helvetica", 18)) progress_bar = tk.Progressbar(root, orient=ttk.HORIZONTAL, length=1280) searching_for_devices_label = tk.Label(root, text="Searching for Devices...", font=("Helvetica", 24)) 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) play_button.bind("", lambda e:controlPlay()) pause_button.bind("", lambda e:controlPause()) next_button.bind("", lambda e:controlNext()) previous_button.bind("", lambda e:controlPrevious()) album_art_label.bind("", lambda e:likeSong()) sleep_frame.bind("", lambda e:wakeup()) # devices_list.bind("", 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() 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) 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) 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)) 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") lyrics_label_frame.grid(row=0, column=2, rowspan=4) lyrics_label.grid() def unloadNow_playing(): device_name_label.grid_forget() artist_label.grid_forget() song_label.grid_forget() previous_button.grid_forget() play_button.grid_forget() next_button.grid_forget() progress_bar.grid_forget() lyrics_label.grid_forget() album_art_label.grid_forget() def loadDevices_list(): devices_list.grid(row=1, column=1, pady=10) start_playback_on_device_button.grid(row=0, column=1, ipadx=40, ipady=40) def unloadDevices_list(): devices_list.grid_forget() start_playback_on_device_button.grid_forget() def loadSearching_Devices(): searching_for_devices_label.grid() def unloadSearching_Devices(): searching_for_devices_label.grid_forget() def loadSleep(): sleep_frame.grid() # def recognize(recognizer, audio): # try: # words = r.recognize_sphinx(audio) # print(words) # if ("magical") in words: # if (" play") in words: # controlPlay() # elif (" pause") in words: # controlPause() # elif (" next") in words: # controlNext() # elif (" previous") in words: # controlPrevious() # else: # pass # else: # pass # except sr.RequestError as e: # print("Could not request results; {0}".format(e)) # except sr.UnknownValueError: # print("Speech not understood") # r = sr.Recognizer() # m = sr.Microphone() # with m as source: # r.adjust_for_ambient_noise(source) # print("Ambient Noise Calibration Complete") # r.listen_in_background(m, recognize) # Start updating the song label # setup() # Run the GUI loadNow_playing() update_song_label() root.mainloop()