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 format_track(track): def simplify_track_info(track):
track_format = "{title} - {artist}\0info\x1f{track_id}\n" result = {}
return track_format.format( result["id"] = track["id"]
title=track["name"], result["fullname"] = f'{track["name"]} - {track["artists"][0]["name"]}'
artist=track["artists"][0]["name"], return result
track_id=track["id"]
)
def display_default(): def get_cached_tracks():
liked_tracks = get_top_tracks() if len(track_cache) == 0:
for item in liked_tracks: tt = get_top_tracks(500)
print(format_track(item), end="") for track in tt:
track_cache[track["id"]] = simplify_track_info(track)
return [track_cache[k] for k in track_cache]
def play_track(track_id: str): def search_track(query: str):
try: search_rs = sp.search(query, limit=10, type='track')["tracks"]["items"]
sp.add_to_queue(f"spotify:track:{track_id}") return [simplify_track_info(t) for t in search_rs]
except spotipy.SpotifyException as err:
print(f"\0message\x1f{err.msg}") def play_track(track_id: str, track_fullname: str, now=True):
if track_id == "":
return 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) time.sleep(0.1)
sp.next_track() sp.next_track()
@memory.cache(cache_validation_callback=expires_after(seconds=180)) def format_track(track):
def search_track(query: str): track_format = "{fullname}\0info\x1f{track_id}\n"
try: return track_format.format(
query_res = sp.search(query, limit=30, type='track')["tracks"]["items"] fullname=track["fullname"],
if len(query_res) == 0: track_id=track["id"]
print("\0message\x1fNo result in search") )
return
for result in query_res: def display_track_list(track_list=None):
print(format_track(result), end="") if track_list is None:
except spotipy.SpotifyException as err: track_list = get_cached_tracks()
print(f"\0message\x1f{err.msg}") for item in track_list:
return print(format_track(item), end="")
controls = {
"Next": sp.next_track,
"Play": sp.start_playback,
"Pause": sp.pause_playback,
"Previous": sp.previous_track,
}
def display_controls():
for k, _ in controls.items():
print(k)
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;
}