Update rofi spotify script to use a better cache architecture

This commit is contained in:
Antonin Ruan
2025-08-19 15:48:30 +02:00
parent 61ffaffeee
commit 009dbc73ae
3 changed files with 86 additions and 59 deletions

View File

@@ -1,2 +1,2 @@
joblib==1.5.1 diskcache==5.6.3
spotipy==2.25.1 spotipy==2.25.1

View File

@@ -1,9 +1,7 @@
#!/home/womax/.config/rofi/scripts/.env/bin/python #!/home/womax/.config/rofi/scripts/.env/bin/python
import joblib from diskcache import Cache
from joblib import expires_after
import os import os
import os.path as path import os.path as path
import shutil
import spotipy import spotipy
from spotipy.oauth2 import SpotifyPKCE from spotipy.oauth2 import SpotifyPKCE
import sys import sys
@@ -20,13 +18,9 @@ cache_dir = path.join(
path.join(os.getenv("HOME"), ".cache")), path.join(os.getenv("HOME"), ".cache")),
"rofi-spotify") "rofi-spotify")
if path.exists(cache_dir) and not path.isdir(cache_dir): track_cache = Cache(path.join(cache_dir, "track_cache"),
shutil.rmtree(cache_dir) eviction_policy="least-recently-stored",
size_limit=int(10e6))
if not path.exists(cache_dir):
os.makedirs(cache_dir, mode=0o755)
memory = joblib.Memory(cache_dir, verbose=0)
scopes = ["user-top-read", "user-modify-playback-state"] scopes = ["user-top-read", "user-modify-playback-state"]
@@ -39,67 +33,111 @@ sp = spotipy.Spotify(
cache_path=path.join(cache_dir, "credentials")) cache_path=path.join(cache_dir, "credentials"))
)) ))
@memory.cache(cache_validation_callback=expires_after(days=30))
def get_top_tracks(limit=200): def get_top_tracks(limit=200):
offset = 0 offset = 0
tracks = [] tracks = []
while offset < limit: while offset < limit:
reading = min(50, limit-offset) reading = min(50, limit-offset)
tracks += sp.current_user_top_tracks( new_tracks = sp.current_user_top_tracks(
limit=reading, limit=reading,
offset=offset)["items"] offset=offset)["items"]
if len(new_tracks) == 0:
return tracks
tracks += new_tracks
offset += reading offset += reading
return tracks return tracks
def simplify_track_info(track):
result = {}
result["id"] = track["id"]
result["fullname"] = f'{track["name"]} - {track["artists"][0]["name"]}'
return result
def get_cached_tracks():
if len(track_cache) == 0:
tt = get_top_tracks(500)
for track in tt:
track_cache[track["id"]] = simplify_track_info(track)
return [track_cache[k] for k in track_cache]
def search_track(query: str):
search_rs = sp.search(query, limit=10, type='track')["tracks"]["items"]
return [simplify_track_info(t) for t in search_rs]
def play_track(track_id: str, track_fullname: str, now=True):
if track_id == "":
return
try:
to_play = {
"id": track_id,
"fullname": track_fullname
}
sp.add_to_queue(f"spotify:track:{to_play['id']}")
track_cache[to_play['id']] = to_play
except spotipy.SpotifyException as err:
print(f"{err.msg}", file=sys.stderr)
return
if now:
time.sleep(0.1)
sp.next_track()
def format_track(track): def format_track(track):
track_format = "{title} - {artist}\0info\x1f{track_id}\n" track_format = "{fullname}\0info\x1f{track_id}\n"
return track_format.format( return track_format.format(
title=track["name"], fullname=track["fullname"],
artist=track["artists"][0]["name"],
track_id=track["id"] track_id=track["id"]
) )
def display_default(): def display_track_list(track_list=None):
liked_tracks = get_top_tracks() if track_list is None:
for item in liked_tracks: track_list = get_cached_tracks()
for item in track_list:
print(format_track(item), end="") print(format_track(item), end="")
def play_track(track_id: str): controls = {
try: "Next": sp.next_track,
sp.add_to_queue(f"spotify:track:{track_id}") "Play": sp.start_playback,
except spotipy.SpotifyException as err: "Pause": sp.pause_playback,
print(f"\0message\x1f{err.msg}") "Previous": sp.previous_track,
return }
time.sleep(0.1)
sp.next_track()
@memory.cache(cache_validation_callback=expires_after(seconds=180)) def display_controls():
def search_track(query: str): for k, _ in controls.items():
try: print(k)
query_res = sp.search(query, limit=30, type='track')["tracks"]["items"]
if len(query_res) == 0:
print("\0message\x1fNo result in search")
return
for result in query_res:
print(format_track(result), end="")
except spotipy.SpotifyException as err:
print(f"\0message\x1f{err.msg}")
return
def main(): def main():
match int(os.getenv("ROFI_RETV", default=-1)): rofi_retv = int(os.getenv("ROFI_RETV", default=-1));
print(rofi_retv, file=sys.stderr)
match rofi_retv:
case 0: case 0:
# Initial call # Initial call
display_default() # enable custom hot key
print("\0use-hot-keys\x1ftrue")
display_controls()
display_track_list()
case 1: case 1:
# Selected entry # Selected entry
play_track(os.getenv("ROFI_INFO", "")) # Default behavior to play the track immediatly
exit(0) if sys.argv[1] in controls.keys():
controls[sys.argv[1]]()
return
play_track(os.getenv("ROFI_INFO", ""), sys.argv[1])
case 2: case 2:
# Custom entry # Custom entry
track = sys.argv[1] track = sys.argv[1]
search_track(track) display_track_list(search_track(track))
memory.reduce_size(bytes_limit="10M")
case 10:
# Custom hot key 1
# Add entry to the queue
if sys.argv[1] in controls.keys():
return
play_track(os.getenv("ROFI_INFO", ""),
sys.argv[1],
now=False)
case _: case _:
exit(1) exit(1)

View File

@@ -1,11 +0,0 @@
* {
main-bg: #11111be6;
main-fg: #cdd6f4ff;
main-br: #cba6f7ff;
main-ex: #f5e0dcff;
select-bg: #b4befeff;
select-fg: #11111bff;
separatorcolor: transparent;
border-color: transparent;
}