Files
spotify-gui/spotifycontroller.py
2023-03-21 22:21:45 -07:00

496 lines
18 KiB
Python

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
# SpotifyGUI - Made by Brandon Brunson
# import speech_recognition as sr
if os.name == 'posix':
os.system("xset -display :0 s 21600")
if os.path.isfile("updated.cfg"):
print("NEW VER")
os.remove("updated.cfg")
sleep(5)
q = queue.Queue()
# import canvas
# Set the Spotify app's client ID and client secret
# client_id = "69b82a34d0fb40be80b020eae8e80f25"
# client_secret = "455575b0e3db44acbbfaa0c419bc3c10"
redirect_uri = "http://127.0.0.1:8888/callback"
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, album_art_img_open):
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 * (get_colors(album_art_img_open)[0] ** 2) + 0.587 * (get_colors(album_art_img_open)[1] ** 2) + 0.114 * (get_colors(album_art_img_open)[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 wait_time
# count +=1
# if count < 69:
# wait_time = 6500
# elif count >= 69:
# wait_time = 3600000
list_of_devices = spotify.devices()
unloadNow_playing()
if list_of_devices == "{'devices': []}":
unloadDevices_list()
loadSearching_Devices()
root.after(1000, get_devices)
else:
if spotify.current_playback() != None:
unloadSearching_Devices()
unloadDevices_list()
loadNow_playing()
return
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(6500, get_devices)
# 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, 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)
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(frame_artist_song, image=play_img, borderwidth=0)
pause_button = ttk.Label(frame_artist_song, image=pause_img, borderwidth=0)
next_button = ttk.Label(frame_artist_song, image=next_img, borderwidth=0)
previous_button = ttk.Label(frame_artist_song, 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("<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())
album_art_label.bind("<Button-1>", lambda e:likeSong())
# 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:
get_devices()
root.after(100, update_song_label)
# Update the song label every 1 second
else:
if current_playback.get("item"):
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
else:
track_name = "Loading..."
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)
bg_color = "#" + '%02x%02x%02x' % (get_colors(album_art_img_open))
# 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, album_art_img_open)
# 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=(100,0))
elif playing_status == False:
pause_button.grid_forget()
play_button.grid(row=3, column=1, pady=(100,0))
else:
pass
def loadNow_playing():
frame_artist_song.grid(row=0, column=1, rowspan=3, pady=(20,0))
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=(100,0))
play_button.grid(row=3, column=1, pady=(100,0))
next_button.grid(row=3, column=1, padx=(200,0), pady=(100,0))
progress_bar.grid(row=3, column=0, columnspan=3)
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 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()
loadNow_playing()
update_song_label()
# Run the GUI
root.mainloop()