diff --git a/.cache b/.cache new file mode 100644 index 0000000..08732ef --- /dev/null +++ b/.cache @@ -0,0 +1 @@ +{"access_token": "BQB6UF8iw-WD97zuAcBy3j1JRn-HI3CUOYi-cFI0CG72nxPGXPkHdzOse6jAIVBrkkGgzDlqdgbGJWoEqzLFr60Ri9dKwIPjfOzDMrghO4Qm86r_ks81plxBDTqbmmJCpv518cFsu_w5JFEhGa6AruwxzYWrLhcZAPkAGCFGub-nj0gaWZnHGEyRBTdU5w", "token_type": "Bearer", "expires_in": 3600, "scope": "user-modify-playback-state user-read-playback-state", "expires_at": 1674288882, "refresh_token": "AQANdyTU2KRSkgJSsueOmP3-n3VFXKSeh90xje6HINu6becuKHhx0tauC4ruB7v6GbtRd45wkF13AsUXjGDHxRMSvGFky2KBhck6sEZTSJID0qd4pmsWoC8xOvzFzsAlaeA"} \ No newline at end of file diff --git a/.cache-thebrandon45 b/.cache-thebrandon45 deleted file mode 100644 index 3814815..0000000 --- a/.cache-thebrandon45 +++ /dev/null @@ -1 +0,0 @@ -{"access_token": "BQBDvdX_R5Ytk8IWYKvSfQ-xvTD3fJHLEXy5-EwVm6ukngDvG5Hlz6F0LWdjLuUMVmcJi24IYljaF2l396aeE0kcnX8X12rYcyMpy6kIUH3i_jL1xg4egPSZ8Lr41l7xHIw7BJQkuXN-yewbNo2quC_i32BtpYncurovWcSKYjLjfO8LC9_L7Z3IWhu1bw", "token_type": "Bearer", "expires_in": 3600, "scope": "user-modify-playback-state user-read-playback-state", "expires_at": 1673754361, "refresh_token": "AQBb9zPnT-MmK7H70IzzenfZBz1VV-0eZWhD73x2br6xQPx1jctO-uuGZu38EvvXapCpdJZ8oLAk4Z4Ci2gmZGGfMsmsofMNVB402gjRrSzfV94BpkuYJtyV0fS5bSK5Nrg"} \ No newline at end of file diff --git a/install.sh b/install.sh new file mode 100644 index 0000000..ad0f1b5 --- /dev/null +++ b/install.sh @@ -0,0 +1,24 @@ +echo "Downloading Spotify GUI" +curl http://files.bbrunson.com/spotify-gui/install.zip -o install.zip +echo "This installation requires sudo privileges" +sudo ls +echo "Installing Python 3" +sudo apt-get install -y python3 python3-distutils python3-tk +echo "Installing Pip 3" +curl -s https://bootstrap.pypa.io/get-pip.py | python3 +echo "Installing spotify-gui to /opt" +sudo mkdir -p /opt/spotify-gui +sudo unzip -o install.zip -d /opt +sudo chmod +x /opt/spotify-gui/update.py +echo "Installing dependencies" +echo -e "librespot\nPillow\nPyAutoGUI\nrequests\nspotipy\nsv_ttk\nsyncedlyrics" > requirements.txt +sudo mv requirements.txt /opt/spotify-gui/requirements.txt +sudo pip3 install -r /opt/spotify-gui/requirements.txt +echo "Creating systemd service" +echo -e "[Unit]\nDescription=Spotify GUI\nAfter=network-online.target\nWants=network-online.target\n[Service]\nType=simple\nUser=pi\nWorkingDirectory=/opt/spotify-gui\nExecStart=xinit ./start.sh $* -- :0 vt$XDG_VTNR -nocursor\n[Install]\nWantedBy=multi-user.target" > spotify-gui.service +sudo mv spotify-gui.service /etc/systemd/system/spotify-gui.service +sudo chmod +x /etc/systemd/system/spotify-gui.service +sudo systemctl daemon-reload +sudo systemctl enable spotify-gui.service +echo "Installation complete. Updating and starting Spotify GUI." +sudo service spotify-gui start \ No newline at end of file diff --git a/program-embedded-util.py b/program-embedded-util.py new file mode 100644 index 0000000..13933b8 --- /dev/null +++ b/program-embedded-util.py @@ -0,0 +1,472 @@ +import spotipy +import spotipy.util +import tkinter as ttk +from tkinter import ttk as tk +import sv_ttk +import requests +import os +import syncedlyrics +from PIL import Image, ImageTk +from io import BytesIO +import math +from time import sleep + +# 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" + +# 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 + + +# def wait_for_display(): +# while True: +# try: +# test = ttk.Tk() +# test.destroy() +# return True +# except: +# pass + +# Get the user's Spotify authorization token +scope = "user-read-playback-state,user-modify-playback-state" + +# if len(sys.argv) == 1: +# pass +# elif sys.argv[1] == "--setup": +# try: +# os.remove(".cache-" + username) +# except OSError: +# pass +# elif sys.argv[1] == "--clear": +# os.remove(".cache-" + username) +# sys.exit("Deleting token from cache and exiting...") +# elif sys.argv[1] == "--clearChromeCache": +# if os.name() == "posix": +# os.system("rm -rf ~/.cache/chromium") +# os.system("rm -rf ~/.config/chromium") +# sys.exit("Deleting Chrome cache and exiting...") + +# def oauthLogin(): +# if os.path.isfile(".cache-" + username) == False: +# sleep(25) +# pyautogui.press('tab') +# pyautogui.press('tab') +# pyautogui.press('tab') +# pyautogui.press('tab') +# pyautogui.write(username) +# pyautogui.press('tab') +# pyautogui.write(password) +# pyautogui.press('enter') +# sleep(90) +# if platform.system() == "Linux": +# os.system("killall chromium-browser") + +# threading.Thread(target=oauthLogin).start() + +# oauthLogin() + + + +# Create a Spotify object with the user's authorization token +def createToken(): + token = spotipy.util.prompt_for_user_token(username, scope, client_id, client_secret, redirect_uri) + spotify = spotipy.Spotify(auth=token) + return spotify + +spotify = createToken() + +bg_color = "#000000" + +# 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 play(): + spotify.start_playback() + +# Function to call the Spotify API to pause the current track +def pause(): + spotify.pause_playback() + +def next(): + spotify.next_track() + +def previous(): + spotify.previous_track() + +# def maxvolume(): +# spotify.volume(100) + +# def minvolume(): +# spotify.volume(0) + +# def randomvolume(): +# spotify.volume(random.randint(0,100)) + +# def volumeslider(self): +# spotify.volume(int(volumeslider_button.get())) + +# def search(event): +# searched = track_search.get() +# if searched.startswith(":t"): +# track_searched_isolated = searched.replace(":t", "") +# track_search_results = spotify.search(q='track:' + track_searched_isolated, type='track') +# searched_track_id = track_search_results["tracks"]["items"][0]["id"] +# spotify.start_playback(uris=["spotify:track:" + searched_track_id]) +# if searched.startswith(":a"): +# artist_searched_isolated = searched.replace(":a", "") +# artist_search_results = spotify.search(q='artist:' + artist_searched_isolated, type='artist') +# searched_artist_id = artist_search_results["artists"]["items"][0]["uri"] +# spotify.start_playback(context_uri=searched_artist_id) +# if searched.startswith(":l"): +# album_searched_isolated = searched.replace(":l", "") +# album_search_results = spotify.search(q='album:' + album_searched_isolated, type='album') +# searched_album_id = album_search_results["albums"]["items"][0]["uri"] +# spotify.start_playback(context_uri=searched_album_id) +# if searched.startswith(":p"): +# playlist_searched_isolated = searched.replace(":p", "") +# playlist_search_results = spotify.search(q='playlist:' + playlist_searched_isolated, type='playlist') +# searched_playlist_id = playlist_search_results["playlists"]["items"][0]["uri"] +# spotify.start_playback(context_uri=searched_playlist_id) + +def start_playback_on_device(): + device_selections = devices_list.curselection() + try: + list_of_devices = spotify.devices() + except (spotipy.exceptions.SpotifyException, requests.exceptions.HTTPError): + createToken() + pass + 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(): + list_of_devices = spotify.devices() + unloadNow_playing() + if list_of_devices == "{'devices': []}": + unloadDevices_list() + loadSearching_Devices() + root.after(1000, get_devices) + else: + current_playback = spotify.current_playback() + if current_playback != None: + unloadSearching_Devices() + unloadDevices_list() + loadNow_playing() + update_song_label() + else: + unloadSearching_Devices() + loadDevices_list() + devices_list.delete(0, ttk.END) + try: + list_of_devices = spotify.devices() + except (spotipy.exceptions.SpotifyException, requests.exceptions.HTTPError): + createToken() + pass + 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 loadLyrics_pressed(): +# unloadNow_playing() +# loadLyrics() + +# def unloadLyrics_pressed(): +# unloadLyrics() +# loadNow_playing() + + +# def hide_devices(): +# get_devices_button.grid() +# devices_list.grid_remove() +# start_playback_on_device_button.grid_remove() +# hide_devices_button.grid_remove() + + +play_img = ttk.PhotoImage(file="icons/play-circle-x2.png") +pause_img = ttk.PhotoImage(file="icons/pause-circle-x2.png") +next_img = ttk.PhotoImage(file="icons/skip-next-x2.png") +previous_img = ttk.PhotoImage(file="icons/skip-previous-x2.png") +play_img_black = ttk.PhotoImage(file="icons/play-circle-x2-black.png") +pause_img_black = ttk.PhotoImage(file="icons/pause-circle-x2-black.png") +next_img_black = ttk.PhotoImage(file="icons/skip-next-x2-black.png") +previous_img_black = ttk.PhotoImage(file="icons/skip-previous-x2-black.png") +# lyrics_img = ttk.PhotoImage(file="icons/lyrics.png") +album_art_img = "" +# album_art_img = ttk.PhotoImage(file="album_art.png") + +# canvas = ttk.Canvas(root, width=480, height=320) +# canvas.create_image(0, 0, image=album_art_img, anchor="nw") +# canvas.grid() + +# canvas = ttk.Canvas(root, width=480, height=320) + +frame_artist_song = ttk.Frame(root, width=(1280/3), height=400, bg=bg_color) +# frame_controls = tk.Frame(root) +# lyrics_button = tk.Frame(root) +album_art_frame = ttk.Frame(root, bg=bg_color) +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.grid_columnconfigure(2, 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) +# maxvolume_button = tk.Button(root, text="Max Volume", command=maxvolume) +# minvolume_button = tk.Button(root, text="Min Volume", command=minvolume) +# randomvolume_button = tk.Button(root, text="Random Volume", command=randomvolume) +# volumeslider_button = tk.Scale(root, from_=100, to=0, orient=ttk.VERTICAL, length=240, command=volumeslider) +#doaudio_analysis = tk.Button(root, text="Audio Analysis", command=doaudioanalysis) +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) +# track_progress_label = tk.Label(root, text="") +# track_duration_label = tk.Label(root, text="") +# track_combined_label = tk.Label(root, text="") +# track_search = tk.Entry(root, text="") +# track_search_button = tk.Button(root, text="Search", command=search) +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) +# hide_devices_button = tk.Button(root, text="Hide Devices", command=hide_devices) +# username_label = tk.Label(root, text="Username: " + spotify.me()["display_name"]) +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)) +# background_image_label = tk.Label(root, image=album_art_img) +lyrics_label = tk.Label(lyrics_label_frame, text="", font=("Helvetica", 32), wraplength=(1280/3), justify=ttk.CENTER, background=bg_color) +# loadLyrics_button = ttk.Button(lyrics_button, image=lyrics_img, command=loadLyrics_pressed, borderwidth=0) +# album_art_canvas = ttk.Canvas(root) +# album_art_canvas_create_image = album_art_canvas.create_image(0, 0, image=album_art_img) +album_art_label = tk.Label(album_art_frame, image=album_art_img) + +# root.bind("", search) +# lyrics_label.bind("", lambda e:unloadLyrics_pressed()) +play_button.bind("", lambda e:play()) +pause_button.bind("", lambda e:pause()) +next_button.bind("", lambda e:next()) +previous_button.bind("", lambda e:previous()) + +# Function to update the song label with the current track's name +def update_song_label(): + global lrc + global album_art_img + # Get the current playback information + while True: + try: + current_playback = spotify.current_playback() + break + except (spotipy.exceptions.SpotifyException, requests.exceptions.HTTPError, requests.exceptions.ReadTimeout): + createToken() + sleep(0.25) + # If there is no current playback, set the text of the song label to "No playback" + if current_playback is None: + # nothing_playing_obj = '{"item": {"artists": [{"name": "Nothing Playing"}],"duration_ms": 0,"name": "Nothing Playing"},"progress_ms": 0}' + # current_playback = json.loads(nothing_playing_obj) + get_devices() + # Update the song label every 1 second + else: + track_name = current_playback["item"]["name"] + artist_name = current_playback["item"]["artists"][0]["name"] + track_duration = current_playback["item"]["duration_ms"] + track_progress = current_playback["progress_ms"] + # current_volume = current_playback["device"]["volume_percent"] + playing_status = current_playback["is_playing"] + device_name = current_playback["device"]["name"] + album_art_url = current_playback["item"]["album"]["images"][0]["url"] + track_progress_min = track_progress//(1000*60)%60 + track_progress_sec = (track_progress//1000)%60 + # track_duration_min = track_duration//(1000*60)%60 + # track_duration_sec = (track_duration//1000)%60 + # open_url = urlopen(album_art_url) + # raw_image_data = open_url.read() + # open_url.close() + # album_art = ImageTk.PhotoImage(data=raw_image_data) + # background_image_label.config(image=album_art) + # print(background_image_label.cget("image")) + # if raw_image_data == background_image_label.cget("image"): + # print("this is the same image") + # else: + # pass + # loadNow_playing() + if track_name == song_label.cget("text"): + track_progress_formatted = ("{}:{:02d}".format(track_progress_min, track_progress_sec)) + # track_progress_label.config(text=track_progress_formatted) + progress_bar.config(maximum=track_duration) + progress_bar.config(value=track_progress) + for line in str(lrc).splitlines(): + if track_progress_formatted in line: + lyric = line.split("]")[1] + # wrapped_lyric = textwrap.fill(lyric, 21) + lyrics_label.config(text=lyric) + root.after(800, update_song_label) + else: + # album_art_data = Image.open(requests.get(album_art_url, stream=True).raw) + # album_art_data.save("album_art.jpg") + device_name_label.config(text=device_name) + song_label.config(text=track_name) + artist_label.config(text=artist_name) + # track_duration_label.config(text=("{}:{:02d}".format(track_duration_min, track_duration_sec))) + # volumeslider_button.set(value=current_volume) + lyrics_label.config(text="") + lrc = syncedlyrics.search("[" + track_name + "] [" + artist_name + "]") + album_art_img_data = requests.get(album_art_url).content + album_art_img_open = Image.open(BytesIO(album_art_img_data)) + album_art_img = ImageTk.PhotoImage(album_art_img_open.resize((300,300))) + album_art_label.config(image=album_art_img) + bg_color_img = album_art_img_open.resize((1,1), resample=0) + bg_color_img_pixel = bg_color_img.getpixel((0,0)) + bg_color = "#" + '%02x%02x%02x' % (bg_color_img_pixel) + 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) + if math.sqrt(0.299 * (bg_color_img_pixel[0] ** 2) + 0.587 * (bg_color_img_pixel[1] ** 2) + 0.114 * (bg_color_img_pixel[2] ** 2)) > 170: + song_label.config(foreground="black") + artist_label.config(foreground="black") + device_name_label.config(foreground="black") + lyrics_label.config(foreground="black") + play_button.config(image=play_img_black) + pause_button.config(image=pause_img_black) + next_button.config(image=next_img_black) + previous_button.config(image=previous_img_black) + else: + song_label.config(foreground="white") + artist_label.config(foreground="white") + device_name_label.config(foreground="white") + lyrics_label.config(foreground="white") + play_button.config(image=play_img) + pause_button.config(image=pause_img) + next_button.config(image=next_img) + previous_button.config(image=previous_img) + # album_art_label.grid_forget() + # album_art_label.grid() + root.after(500, update_song_label) + # if album_art_url == "12345": + # open_url = urlopen(album_art_url) + # raw_image_data = open_url.read() + # open_url.close() + # album_art = ImageTk.PhotoImage(data=raw_image_data) + # background_image_label.config(image=album_art) + # else: + # pass + 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(): + # background_image_label.place(x=0, y=0) + # volumeslider_button.grid(row=1, column=1, rowspan=3, sticky="e", padx=(0,20)) + # lyrics_button.grid(row=1, column=1, padx=(0,380)) + frame_artist_song.grid(row=0, column=1, rowspan=3, pady=(20,0)) + # frame_controls.grid(row=2, column=1) + # loadLyrics_button.grid() + 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(): + # volumeslider_button.grid_forget() + # frame_artist_song.grid_forget() + # frame_controls.grid_forget() + 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() + # loadLyrics_button.grid_forget() + # lyrics_label_frame.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 loadLyrics(): +# album_art_frame.grid(row=0, column=1) +# album_art_label.grid() +# lyrics_label.grid(row=0, column=1) + +# def unloadLyrics(): +# album_art_frame.grid_forget() +# album_art_label.grid_forget() +# lyrics_label.grid_forget() + +# Start updating the song label +setup() +loadNow_playing() +update_song_label() + +# Run the GUI +root.mainloop() \ No newline at end of file diff --git a/program-embedded.py b/program-embedded.py index 5a39283..0c73ecb 100644 --- a/program-embedded.py +++ b/program-embedded.py @@ -1,19 +1,15 @@ import spotipy -import spotipy.util +from spotipy.oauth2 import SpotifyOAuth import tkinter as ttk from tkinter import ttk as tk -import random import sv_ttk -from urllib.request import urlopen import requests -from time import sleep -import sys import os -import platform import syncedlyrics from PIL import Image, ImageTk from io import BytesIO import math +from time import sleep # Set the Spotify app's client ID and client secret client_id = "69b82a34d0fb40be80b020eae8e80f25" @@ -24,70 +20,31 @@ redirect_uri = "http://127.0.0.1:8888/callback" username = "thebrandon45" password = "Mariposa2502$" -def wait_for_connection(): - while True: - try: - urlopen('http://142.250.189.174', timeout=1) - return True - except: - pass +# os.environ["DISPLAY"] = ":0" -# def wait_for_display(): +# def setup(): +# checkDisplay() + +# def checkDisplay(): # while True: -# try: -# test = ttk.Tk() -# test.destroy() -# return True -# except: -# pass +# 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" -if len(sys.argv) == 1: - pass -elif sys.argv[1] == "--setup": - try: - os.remove(".cache-" + username) - except OSError: - pass -elif sys.argv[1] == "--clear": - os.remove(".cache-" + username) - sys.exit("Deleting token from cache and exiting...") -elif sys.argv[1] == "--clearChromeCache": - if platform.system() == "Linux": - os.system("rm -rf ~/.cache/chromium") - os.system("rm -rf ~/.config/chromium") - sys.exit("Deleting Chrome cache and exiting...") - -# def oauthLogin(): -# if os.path.isfile(".cache-" + username) == False: -# sleep(25) -# pyautogui.press('tab') -# pyautogui.press('tab') -# pyautogui.press('tab') -# pyautogui.press('tab') -# pyautogui.write(username) -# pyautogui.press('tab') -# pyautogui.write(password) -# pyautogui.press('enter') -# sleep(90) -# if platform.system() == "Linux": -# os.system("killall chromium-browser") - -# threading.Thread(target=oauthLogin).start() - -# oauthLogin() - -token = spotipy.util.prompt_for_user_token(username, scope, client_id, client_secret, redirect_uri) # Create a Spotify object with the user's authorization token -def createToken(): - spotify = spotipy.Spotify(auth=token) - return spotify - -spotify = createToken() +spotify = spotipy.Spotify(auth_manager=SpotifyOAuth(client_id=client_id, client_secret=client_secret, redirect_uri=redirect_uri, scope=scope, requests_timeout=30)) bg_color = "#000000" +count = 0 # Create the tkinter window root = ttk.Tk() @@ -111,54 +68,19 @@ def next(): def previous(): spotify.previous_track() -def maxvolume(): - spotify.volume(100) - -def minvolume(): - spotify.volume(0) - -def randomvolume(): - spotify.volume(random.randint(0,100)) - -def volumeslider(self): - spotify.volume(int(volumeslider_button.get())) - -def search(event): - searched = track_search.get() - if searched.startswith(":t"): - track_searched_isolated = searched.replace(":t", "") - track_search_results = spotify.search(q='track:' + track_searched_isolated, type='track') - searched_track_id = track_search_results["tracks"]["items"][0]["id"] - spotify.start_playback(uris=["spotify:track:" + searched_track_id]) - if searched.startswith(":a"): - artist_searched_isolated = searched.replace(":a", "") - artist_search_results = spotify.search(q='artist:' + artist_searched_isolated, type='artist') - searched_artist_id = artist_search_results["artists"]["items"][0]["uri"] - spotify.start_playback(context_uri=searched_artist_id) - if searched.startswith(":l"): - album_searched_isolated = searched.replace(":l", "") - album_search_results = spotify.search(q='album:' + album_searched_isolated, type='album') - searched_album_id = album_search_results["albums"]["items"][0]["uri"] - spotify.start_playback(context_uri=searched_album_id) - if searched.startswith(":p"): - playlist_searched_isolated = searched.replace(":p", "") - playlist_search_results = spotify.search(q='playlist:' + playlist_searched_isolated, type='playlist') - searched_playlist_id = playlist_search_results["playlists"]["items"][0]["uri"] - spotify.start_playback(context_uri=searched_playlist_id) def start_playback_on_device(): device_selections = devices_list.curselection() - try: - list_of_devices = spotify.devices() - except (spotipy.exceptions.SpotifyException, requests.exceptions.HTTPError): - createToken() - pass + 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(): + # count +=1 + # if count >= 277: + list_of_devices = spotify.devices() unloadNow_playing() if list_of_devices == "{'devices': []}": @@ -176,30 +98,11 @@ def get_devices(): unloadSearching_Devices() loadDevices_list() devices_list.delete(0, ttk.END) - try: - list_of_devices = spotify.devices() - except (spotipy.exceptions.SpotifyException, requests.exceptions.HTTPError): - createToken() - pass 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 loadLyrics_pressed(): - unloadNow_playing() - loadLyrics() - -def unloadLyrics_pressed(): - unloadLyrics() - loadNow_playing() - - -# def hide_devices(): -# get_devices_button.grid() -# devices_list.grid_remove() -# start_playback_on_device_button.grid_remove() -# hide_devices_button.grid_remove() play_img = ttk.PhotoImage(file="icons/play-circle-x2.png") @@ -210,19 +113,10 @@ play_img_black = ttk.PhotoImage(file="icons/play-circle-x2-black.png") pause_img_black = ttk.PhotoImage(file="icons/pause-circle-x2-black.png") next_img_black = ttk.PhotoImage(file="icons/skip-next-x2-black.png") previous_img_black = ttk.PhotoImage(file="icons/skip-previous-x2-black.png") -# lyrics_img = ttk.PhotoImage(file="icons/lyrics.png") album_art_img = "" -# album_art_img = ttk.PhotoImage(file="album_art.png") -# canvas = ttk.Canvas(root, width=480, height=320) -# canvas.create_image(0, 0, image=album_art_img, anchor="nw") -# canvas.grid() - -canvas = ttk.Canvas(root, width=480, height=320) frame_artist_song = ttk.Frame(root, width=(1280/3), height=400, bg=bg_color) -# frame_controls = tk.Frame(root) -# lyrics_button = tk.Frame(root) album_art_frame = ttk.Frame(root, bg=bg_color) lyrics_label_frame = ttk.Frame(root, width=(1280/3), height=400, bg=bg_color) lyrics_label_frame.grid_propagate(0) @@ -232,7 +126,6 @@ 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.grid_columnconfigure(2, weight=1) root.configure(background=bg_color) @@ -244,35 +137,18 @@ 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) -maxvolume_button = tk.Button(root, text="Max Volume", command=maxvolume) -minvolume_button = tk.Button(root, text="Min Volume", command=minvolume) -randomvolume_button = tk.Button(root, text="Random Volume", command=randomvolume) -volumeslider_button = tk.Scale(root, from_=100, to=0, orient=ttk.VERTICAL, length=240, command=volumeslider) -#doaudio_analysis = tk.Button(root, text="Audio Analysis", command=doaudioanalysis) -artist_label = tk.Label(frame_artist_song, text="", font=("Helvetica", 32), wraplength=(1280/3), justify=ttk.CENTER, background=bg_color) -song_label = tk.Label(frame_artist_song, text="", font=("Helvetica", 24), wraplength=(1280/3), justify=ttk.CENTER, background=bg_color) -track_progress_label = tk.Label(root, text="") -track_duration_label = tk.Label(root, text="") -# track_combined_label = tk.Label(root, text="") -track_search = tk.Entry(root, text="") -track_search_button = tk.Button(root, text="Search", command=search) +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) -# hide_devices_button = tk.Button(root, text="Hide Devices", command=hide_devices) -username_label = tk.Label(root, text="Username: " + spotify.me()["display_name"]) 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)) -# background_image_label = tk.Label(root, image=album_art_img) lyrics_label = tk.Label(lyrics_label_frame, text="", font=("Helvetica", 32), wraplength=(1280/3), justify=ttk.CENTER, background=bg_color) -# loadLyrics_button = ttk.Button(lyrics_button, image=lyrics_img, command=loadLyrics_pressed, borderwidth=0) -# album_art_canvas = ttk.Canvas(root) -# album_art_canvas_create_image = album_art_canvas.create_image(0, 0, image=album_art_img) album_art_label = tk.Label(album_art_frame, image=album_art_img) -root.bind("", search) -lyrics_label.bind("", lambda e:unloadLyrics_pressed()) + play_button.bind("", lambda e:play()) pause_button.bind("", lambda e:pause()) next_button.bind("", lambda e:next()) @@ -283,16 +159,9 @@ def update_song_label(): global lrc global album_art_img # Get the current playback information - while True: - try: - current_playback = spotify.current_playback() - break - except (spotipy.exceptions.SpotifyException, requests.exceptions.HTTPError): - createToken() + 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: - # nothing_playing_obj = '{"item": {"artists": [{"name": "Nothing Playing"}],"duration_ms": 0,"name": "Nothing Playing"},"progress_ms": 0}' - # current_playback = json.loads(nothing_playing_obj) get_devices() # Update the song label every 1 second else: @@ -300,44 +169,24 @@ def update_song_label(): artist_name = current_playback["item"]["artists"][0]["name"] track_duration = current_playback["item"]["duration_ms"] track_progress = current_playback["progress_ms"] - current_volume = current_playback["device"]["volume_percent"] playing_status = current_playback["is_playing"] device_name = current_playback["device"]["name"] album_art_url = current_playback["item"]["album"]["images"][0]["url"] track_progress_min = track_progress//(1000*60)%60 track_progress_sec = (track_progress//1000)%60 - track_duration_min = track_duration//(1000*60)%60 - track_duration_sec = (track_duration//1000)%60 - # open_url = urlopen(album_art_url) - # raw_image_data = open_url.read() - # open_url.close() - # album_art = ImageTk.PhotoImage(data=raw_image_data) - # background_image_label.config(image=album_art) - # print(background_image_label.cget("image")) - # if raw_image_data == background_image_label.cget("image"): - # print("this is the same image") - # else: - # pass - # loadNow_playing() if track_name == song_label.cget("text"): track_progress_formatted = ("{}:{:02d}".format(track_progress_min, track_progress_sec)) - track_progress_label.config(text=track_progress_formatted) progress_bar.config(maximum=track_duration) progress_bar.config(value=track_progress) for line in str(lrc).splitlines(): if track_progress_formatted in line: lyric = line.split("]")[1] - # wrapped_lyric = textwrap.fill(lyric, 21) lyrics_label.config(text=lyric) root.after(800, update_song_label) else: - # album_art_data = Image.open(requests.get(album_art_url, stream=True).raw) - # album_art_data.save("album_art.jpg") device_name_label.config(text=device_name) song_label.config(text=track_name) artist_label.config(text=artist_name) - track_duration_label.config(text=("{}:{:02d}".format(track_duration_min, track_duration_sec))) - volumeslider_button.set(value=current_volume) lyrics_label.config(text="") lrc = syncedlyrics.search("[" + track_name + "] [" + artist_name + "]") album_art_img_data = requests.get(album_art_url).content @@ -358,7 +207,7 @@ def update_song_label(): previous_button.config(background=bg_color) lyrics_label_frame.config(background=bg_color) lyrics_label.config(background=bg_color) - if math.sqrt(0.299 * (bg_color_img_pixel[0] ** 2) + 0.587 * (bg_color_img_pixel[1] ** 2) + 0.114 * (bg_color_img_pixel[2] ** 2)) > 186: + if math.sqrt(0.299 * (bg_color_img_pixel[0] ** 2) + 0.587 * (bg_color_img_pixel[1] ** 2) + 0.114 * (bg_color_img_pixel[2] ** 2)) > 170: song_label.config(foreground="black") artist_label.config(foreground="black") device_name_label.config(foreground="black") @@ -376,17 +225,7 @@ def update_song_label(): pause_button.config(image=pause_img) next_button.config(image=next_img) previous_button.config(image=previous_img) - # album_art_label.grid_forget() - # album_art_label.grid() root.after(500, update_song_label) - # if album_art_url == "12345": - # open_url = urlopen(album_art_url) - # raw_image_data = open_url.read() - # open_url.close() - # album_art = ImageTk.PhotoImage(data=raw_image_data) - # background_image_label.config(image=album_art) - # else: - # pass if playing_status == True: play_button.grid_forget() pause_button.grid(row=3, column=1, pady=(100,0)) @@ -397,15 +236,10 @@ def update_song_label(): pass def loadNow_playing(): - # background_image_label.place(x=0, y=0) - # volumeslider_button.grid(row=1, column=1, rowspan=3, sticky="e", padx=(0,20)) - # lyrics_button.grid(row=1, column=1, padx=(0,380)) frame_artist_song.grid(row=0, column=1, rowspan=3, pady=(20,0)) - # frame_controls.grid(row=2, column=1) - # loadLyrics_button.grid() device_name_label.grid(row=0, column=1) - artist_label.grid(row=1, column=1) - song_label.grid(row=2, 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)) @@ -416,16 +250,15 @@ def loadNow_playing(): lyrics_label.grid() def unloadNow_playing(): - volumeslider_button.grid_forget() - frame_artist_song.grid_forget() - # frame_controls.grid_forget() + 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() - # loadLyrics_button.grid_forget() + lyrics_label.grid_forget() + album_art_label.grid_forget() def loadDevices_list(): devices_list.grid(row=1, column=1, pady=10) @@ -441,18 +274,9 @@ def loadSearching_Devices(): def unloadSearching_Devices(): searching_for_devices_label.grid_forget() -def loadLyrics(): - album_art_frame.grid(row=0, column=1) - album_art_label.grid() - lyrics_label.grid(row=0, column=1) - -def unloadLyrics(): - album_art_frame.grid_forget() - album_art_label.grid_forget() - lyrics_label.grid_forget() # Start updating the song label -wait_for_connection() +# setup() loadNow_playing() update_song_label() diff --git a/spotify-gui.service b/spotify-gui.service new file mode 100644 index 0000000..8fdf1ff --- /dev/null +++ b/spotify-gui.service @@ -0,0 +1,11 @@ +[Unit] +Description=Spotify GUI +After=network-online.target +Wants=network-online.target +[Service] +Type=simple +User=pi +WorkingDirectory=/opt/spotify-gui +ExecStart=xinit ./start.sh $* -- :0 vt$XDG_VTNR -nocursor +[Install] +WantedBy=multi-user.target \ No newline at end of file diff --git a/todo.txt b/todo.txt index 32f51b0..841236a 100644 --- a/todo.txt +++ b/todo.txt @@ -8,4 +8,22 @@ selection resets after 1 second on devices list; need to remove root.after in up 01/14/23: -smooth progress bar \ No newline at end of file +smooth progress bar + +01/15/23: +turns off screen and stops polling api after inactivity, reauthenticates when + screen is turned back on + +01/18/23: +turn off screen when holding down on album art. + +01/20/2023: +background gradient + +01/21/2023: +it errors on "track_name = current_playback["item"]["name"]" +'TypeError: NoneType', current_playback is None basically... even though there is already an if statement checking if it is None. +ERROR HAPPENS ON SOME KIDZ BOP SONGS??? + +01/21/2023: +add canvas videos to background if available. USE: https://github.com/Delitefully/spotify-canvas-downloader/blob/master/src/canvas.py \ No newline at end of file diff --git a/update.py b/update.py new file mode 100644 index 0000000..609648e --- /dev/null +++ b/update.py @@ -0,0 +1,18 @@ +import urllib +from urllib.request import urlopen +from zipfile import ZipFile +from io import BytesIO + +while True: + try: + urlopen('http://bbrunson.com', timeout=1) + break + except: + pass + +try: + with ZipFile(BytesIO((urlopen('http://files.bbrunson.com/spotify-gui/update.zip')).read())) as zipObj: + zipObj.extractall() +except urllib.error.HTTPError: + pass +exec(open('program-embedded.py').read()) \ No newline at end of file