Compare commits

..

10 Commits

Author SHA1 Message Date
Brandon4466
3a227f4e9e gitPUSH 2023-06-04 22:14:27 -07:00
Brandon4466
2163e5120c first push to GitPUSH 2023-06-04 22:14:08 -07:00
Brandon4466
1e40570d57 ** 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.
2023-05-25 17:25:20 -07:00
Brandon4466
90b1448d93 seperated controls from artist song frame
this fixes the controls moving if song name long
modified updater to restart completely if killed/encounters exception
2023-04-08 16:16:45 -07:00
Brandon4466
d346b639f5 fixed calling get_color many times unnecessarily
refactored calling get_devices to stop unneeded API calls
2023-03-28 01:26:28 -07:00
Brandon4466
cf6f02acd1 updated background dominant color calculation
reworked updater to work as handler, will restart if exception
2023-03-27 00:13:26 -07:00
Brandon4466
ef4d0fee66 fixed bug in liking songs 2023-03-21 22:21:45 -07:00
Brandon4466
1c4024be7d Changed update so it doesn't retry to fast
Refactored code
2023-03-08 20:26:45 -08:00
Brandon4466
8a5afa9a36 added liking songs by tapping album art
liked songs change play button to heart
2023-03-04 17:17:36 -08:00
Brandon4466
88c877c50f added liking by double tapping album art
add exception handling to update.py, prepping for handling script
2023-03-04 13:46:12 -08:00
15 changed files with 450 additions and 152 deletions

2
.cache
View File

@@ -1 +1 @@
{"access_token": "BQDrxTAIyV04s8ELEHxjvK118srmPFqciwk8FVd15twhg-Vh3LOY1sBzU3M6_K6oRH7sODe4740H3SnBZ-aDDSombJOFVCi26WaEEtUM0v8mgTJQvabM1yacXLOUC3uGtWXzZ5twdBfllOJD0sqMRvllkbwvfOsi09QiUifN50vcpaL0uLWHBIZ7SbBoUN4", "token_type": "Bearer", "expires_in": 3600, "scope": "user-modify-playback-state user-read-playback-state", "expires_at": 1677924835, "refresh_token": "AQCOARNKfmTOZ0eINGRwD7Yo5JWarv7bH5LnTvoz_mMEDhXR_luBWsl8wq010xTTbLO3c4XLeCqIKUwmPsJX5uQn4mUP-JWKgETATkkii6vlKTS7xR3DntNwfquOIjV-9CQ"}
{"access_token": "BQCJWwTXh76cuyzzWvBtUE6WQ_i7f7FyS5bEUH8ZjVT9ZGTJujaAdgZhwSP_OQckJo7Ayb8PMatvwpLYWFbj-WP32maO8hM-C_Og_Sch6Otf5s7v23vhT_B9q3As2xpZJN-15eSj2kRFSB6XTLNKkC3v4Wv0pDeLCEXBZeo-6EsqyB12v2ymzT4IOsmAYCP_cTpOD77Hz6rIxkpgGx8", "token_type": "Bearer", "expires_in": 3600, "scope": "user-library-modify user-library-read user-modify-playback-state user-read-playback-state", "expires_at": 1685945575, "refresh_token": "AQAby3zEGc-H8o8zciCRMZm-O6Gj3FAup6Vb0sRrbtiO48VyMTJMzU4DoJ_wrhk8LmOiN8Hvt0Fb_Ag-09XVEDgQe3VUBDD3HdoMk6aZA1n02VLygxQNMNUQgAGw6oUUdI0"}

16
.vscode/launch.json vendored Normal file
View File

@@ -0,0 +1,16 @@
{
// Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes.
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"name": "Python: Current File",
"type": "python",
"request": "launch",
"program": "${file}",
"console": "integratedTerminal",
"justMyCode": true
}
]
}

BIN
icons/pause-heart-black.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 983 B

BIN
icons/pause-heart.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

BIN
icons/play-heart-black.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

BIN
icons/play-heart.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

View File

@@ -1,7 +1,9 @@
librespot
Pillow
PyAutoGUI
requests
spotipy
sv_ttk
syncedlyrics
numpy
flask
psutil

BIN
spotifycontroller.prof Normal file

Binary file not shown.

View File

@@ -9,29 +9,44 @@ 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
# import speech_recognition as sr
if os.name == 'posix':
os.system("xset -display :0 s 21600")
client_id = "df61ecadf09941eb87e693d37f3ad178"
client_secret = "ba97992d614e48d6b0d023998b2957cb"
os.system("xset -display :0 s 86400")
if os.path.isfile("updated.cfg"):
print("A new update was downloaded and installed. Starting in 5 seconds...")
print("NEW VER")
os.remove("updated.cfg")
sleep(5)
else:
client_id = "69b82a34d0fb40be80b020eae8e80f25"
client_secret = "455575b0e3db44acbbfaa0c419bc3c10"
q = queue.Queue()
# import canvas
# Set the Spotify app's client ID and client secret
client_id = "69b82a34d0fb40be80b020eae8e80f25"
client_secret = "455575b0e3db44acbbfaa0c419bc3c10"
# 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"
# misc client id and secret (SpotifyGUI 3)
# client_id = "21ca0dc87c9441ceaf875a05d0199756"
# client_secret = "dd430e634ae94471aa70dfc22936be10"
# Set the user's Spotify username
username = "thebrandon45"
password = "Mariposa2502$"
@@ -54,7 +69,7 @@ password = "Mariposa2502$"
# Get the user's Spotify authorization token
scope = "user-read-playback-state,user-modify-playback-state"
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)
@@ -66,17 +81,18 @@ spotify = spotipy.Spotify(auth_manager=oauth)
# print(SpotifyOAuth.refresh_access_token(refresh_token=access_token))
bg_color = "#000000"
# count = 0
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)
if os.name == 'posix':
root.overrideredirect(1)
sv_ttk.use_dark_theme()
@@ -92,11 +108,73 @@ def controlNext():
spotify.next_track()
def controlPrevious():
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():
print(spotify.current_playback()["item"]["id"])
spotify.current_user_saved_tracks_add(tracks=(spotify.current_playback()["item"]["id"]))
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
@@ -105,38 +183,54 @@ def start_playback_on_device():
# 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
# global wait_time0
# count +=1
# if count < 69:
# wait_time = 6500
# elif count >= 69:
# wait_time = 3600000
# count += 1
list_of_devices = spotify.devices()
unloadNow_playing()
if list_of_devices == "{'devices': []}":
unloadDevices_list()
loadSearching_Devices()
root.after(1000, get_devices)
else:
# 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()
count = 0
# unloadSearching_Devices()
unloadDevices_list()
loadNow_playing()
return
root.after(800, update_song_label)
else:
count += 1
if count > 420:
kill(kill=sleep)
# unloadSearching_Devices()
# loadDevices_list()
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"]):
# exec(f'dev_{num_of_device} = tk.Button(root, text=list_of_devices["devices"][num_of_device]["name"], command=start_playback_on_device(device_id=num_of_device))')
# exec(f'dev_{num_of_device}.grid(row={num_of_device}, column=1)')
devices_list.insert(num_of_device, list_of_devices["devices"][num_of_device]["name"])
root.after(6500, get_devices)
root.after(8500, get_devices)
# def wakeup():
# global count
# count = 0
# def wakeup():
# global count
@@ -199,47 +293,90 @@ def addCorners(im, rad):
# return back
def get_colors(image_file, numcolors=1, resize=150):
# 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
paletted = image_file.convert('P', palette=Image.ADAPTIVE, colors=numcolors)
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])
# 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
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):
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)
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, 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)
@@ -253,10 +390,10 @@ 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)
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)
@@ -267,66 +404,75 @@ 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)
play_button.configure()
# 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())
album_art_label.bind("<Double-Button-1>", lambda e:likeSong())
# previous_button.bind("<Double-Button-1>", lambda e:controlPrevious(double=True))
album_art_label.bind("<Button-1>", lambda e:likeSong())
# 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())
import json
# Function to update the song label with the current track's name
def update_song_label():
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:
get_devices()
root.after(100, update_song_label)
unloadNow_playing()
loadDevices_list()
root.after(200, get_devices)
# Update the song label every 1 second
else:
if current_playback.get("item"):
elif current_playback.get("item") is not None:
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"]
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
else:
track_name = "Loading..."
artist_name = "Loading..."
track_duration = 1
track_progress = 1
if track_name == song_label.cget("text"):
track_progress_formatted = ("{}:{:02d}".format(track_progress_min, track_progress_sec))
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]
lyrics_label.config(text=lyric)
# if track_progress < 5000:
# threading.Thread(target=upNext).start()
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))
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)
@@ -343,49 +489,37 @@ 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 * (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:
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)
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()
# 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))
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=(100,0))
play_button.grid(row=3, column=1, pady=(0,30))
else:
pass
else:
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=(20,0))
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=(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)
# 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")
lyrics_label_frame.grid(row=0, column=2, rowspan=4)
@@ -403,8 +537,9 @@ def unloadNow_playing():
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)
pass
# 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()
@@ -416,6 +551,9 @@ def loadSearching_Devices():
def unloadSearching_Devices():
searching_for_devices_label.grid_forget()
def loadSleep():
sleep_frame.grid()
# def recognize(recognizer, audio):
@@ -453,8 +591,11 @@ def unloadSearching_Devices():
# Start updating the song label
# setup()
# Run the GUI
loadNow_playing()
update_song_label()
# Run the GUI
root.mainloop()

Binary file not shown.

Binary file not shown.

10
testing.py Normal file
View File

@@ -0,0 +1,10 @@
from pynput import mouse
from functools import partial
def click(x, y, button, pressed, foo):
return False
bar = partial(click, foo="bar")
with mouse.Listener(on_click=bar) as listener:
listener.join()

View File

@@ -67,3 +67,35 @@ switch to spotify web api for track information to avoid api limit???
new updater, use version numbering to pull updates if server has a newer version and dont update if newer version isnt available.
create a single json file that has a version list with download links to each version. have updater.py download the json file, read and compare to currently downloaded version.
if newer version is in json file, follow link in json file to download, otherwise continue to start the program.
03/04/2023:
redo background color algo. PROMINENT COLOR not average like it is now.
03/04/2023:
add shuffle and repeat buttons
03/07/2023:
make it so the displays turns off automatically after some time (maybe depending on time, ie. late at night)
04/03/2023:
make it so update.py that kills the main script after 6 hours and turn the display off, if the displays wakes up then it will restart the main script
04/08/2023:
background of artist image
hold previous button to start song over
04/10/2023:
if title is held for over 5 seconds then also shutdown the update.py by throwing an exception that the update.py will catch and then kill the update script
also, if you hold it for over 10 seconds then shutdown the pi entirely. all by sending certain sigterms with exit()
04/13/2023:
update.py modified to handle click (untested), spotify.py functions created. TODO: tie get_devices into sleep & kill functions as those are setup to kill script and handoff to update.py
04/28/2023:
periodically check the spotify api, if it is playing something then click a button to wake the display
04/29/2023:
experiment with converting project to flask/js and browser based.
05/07/2023:
detect previous song change by checking if song is at less than 5 seconds into song, if yes then go to previous song, if not then restart song.

View File

@@ -2,18 +2,67 @@ import urllib
from urllib.request import urlopen
from zipfile import ZipFile
from io import BytesIO
from time import sleep
import subprocess
from pynput import mouse
from functools import partial
from os import name, path, remove, system
# while True:
# try:
# urlopen('http://bbrunson.com', timeout=1)
# print("Connection to server established.")
# break
# except:
# pass
if name == 'nt':
print("Windows detected. Shutting down...")
exit()
# try:
# print("Checking for updates...")
# with ZipFile(BytesIO((urlopen('http://files.bbrunson.com/spotify-gui/update.zip')).read())) as zipObj:
# zipObj.extractall()
# print("Update successfully installed.")
# except urllib.error.HTTPError:
# print("No update available.")
# pass
def click(x, y, button, pressed, foo):
return False
bar = partial(click, foo="bar", button=True, pressed=True)
while True:
try:
urlopen('http://bbrunson.com', timeout=1)
break
except:
pass
print("Connection to server established.")
try:
if path.exists('web/update/update.zip'):
print("Update found. Installing...")
with ZipFile('web/update/update.zip') as zipObj:
zipObj.extractall()
remove('web/update/update.zip')
print("Uploaded update successfully installed.")
print("Checking for updates...")
with ZipFile(BytesIO((urlopen('http://files.bbrunson.com/spotify-gui/update.zip')).read())) as zipObj:
zipObj.extractall()
print("Update successfully downloaded and installed.")
except urllib.error.HTTPError:
print("No update available.")
pass
exec(open('spotifycontroller.py').read())
subprocess.Popen(['python3', 'web/web.py'])
subprocess.check_call(['python3', 'spotifycontroller.py'])
except Exception as e:
if e.args[0] == 1:
print("Program has requested sleep mode.")
system("xset -display :0 s 30")
with mouse.Listener(on_move=bar, on_click=bar, on_scroll=bar) as listener:
listener.join()
else:
print(e)
print("Restart procedure initiated: Checking server connection, checking for update, and then reestablishing the application in 10 seconds.")
sleep(10)
continue

48
web/web.py Normal file
View File

@@ -0,0 +1,48 @@
from flask import Flask, Blueprint, render_template, redirect, url_for, request, flash
from werkzeug.utils import secure_filename
import os
import signal
import psutil
app = Flask(__name__)
app.config['SECRET_KEY'] = 'spotify-gui'
app.config['UPLOAD_FOLDER'] = 'update'
@app.route('/', methods=['GET', 'POST'])
def update():
if request.method == 'POST':
if 'file' not in request.files:
flash('No file part')
return redirect(request.url)
file = request.files['file']
if file.filename == '':
flash('No selected file')
return redirect(request.url)
if file and file.filename.endswith('.zip'):
file.save('web/update/update.zip')
return redirect(request.url)
return '''
<!doctype html>
<title>Upload new File</title>
<h1>Upload new File</h1>
<form method=post enctype=multipart/form-data>
<input type=file name=file>
<input type=submit value=Upload>
</form><br>
<form method="POST" action="/restart">
<button>Restart</button>
</form>
'''
@app.route('/restart', methods=['POST'])
def restart():
for line in os.popen("ps ax | grep " + "spotifycontroller.py" + " | grep -v grep"):
fields = line.split()
pid = fields[0]
os.kill(int(pid), signal.SIGKILL)
exit()
return redirect(url_for('update'))
if __name__ == "__main__":
app.run(host='0.0.0.0', port=80)