Spaces:
Running
Running
File size: 7,499 Bytes
e4f4d5f dc5eb33 e4f4d5f dbdd344 9faaa03 dbdd344 dc5eb33 52a6c11 9faaa03 dbdd344 dc5eb33 e4f4d5f dc5eb33 e4f4d5f dbdd344 52a6c11 dbdd344 7507bae 52a6c11 9faaa03 b414958 a8c78b0 b414958 866595f dc5eb33 2082b5c f8df622 e4f4d5f dc5eb33 e4f4d5f dc5eb33 e4f4d5f dc5eb33 e4f4d5f 866595f dc5eb33 52a6c11 dc5eb33 e4f4d5f dc5eb33 671016b 52a6c11 dc5eb33 e4f4d5f dc5eb33 e4f4d5f dc5eb33 e4f4d5f dc5eb33 e4f4d5f f8df622 2082b5c 866595f dc5eb33 a8c78b0 dc5eb33 e4f4d5f dc5eb33 e4f4d5f 866595f 2082b5c dc5eb33 e4f4d5f dc5eb33 f8df622 866595f dc5eb33 e4f4d5f dc5eb33 52a6c11 9faaa03 e4f4d5f |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 |
import os
import json
import random
import gradio as gr
import spotipy
from spotipy.oauth2 import SpotifyOAuth
# ββββββββββββββββββββββββββββββββββββββββββββββ
# 1) Spotify credentials (stored in HF Secrets)
CLIENT_ID = os.environ["SPOTIFY_CLIENT_ID"]
CLIENT_SECRET = os.environ["SPOTIFY_CLIENT_SECRET"]
# 2) Your Spaceβs URL must match this Redirect URI exactly
REDIRECT_URI = "https://jisaacso219-rng-shuffle.hf.space/"
# 3) Include `streaming` for Web Playback SDK + playback scopes
SCOPE = (
"streaming "
"user-read-playback-state "
"user-modify-playback-state "
"playlist-read-private"
)
sp_oauth = SpotifyOAuth(
client_id=CLIENT_ID,
client_secret=CLIENT_SECRET,
redirect_uri=REDIRECT_URI,
scope=SCOPE,
show_dialog=True,
)
sp = None
user_playlists = {}
def get_auth_url():
return sp_oauth.get_authorize_url()
def check_login(code: str):
"""
Called on pageβload via demo.load(). Once we have a `code`:
- exchange it for an access token
- fetch playlists
- inject the Web Playback SDK + token + debug hooks
"""
global sp, user_playlists
if not code:
return (
gr.update(visible=False), # status
gr.update(visible=False), # dropdown
gr.update(visible=False), # sdk_html
)
# 1) Exchange code for token
tokinfo = sp_oauth.get_access_token(code, as_dict=True)
access_token = tokinfo["access_token"]
sp = spotipy.Spotify(auth=access_token)
# 2) Fetch your playlists
items = sp.current_user_playlists(limit=50)["items"]
user_playlists = {p["name"]: p["id"] for p in items}
# 3) Build SDK + injection scripts
sdk_js = f"""
<script>
// If still sandboxed in an iframe, break out:
if (window.self !== window.top) {{
window.top.location.href = window.location.href;
}}
console.log("[SDK] Setting ACCESS_TOKEN");
window.ACCESS_TOKEN = "{access_token}";
</script>
<script src="https://sdk.scdn.co/spotify-player.js"></script>
<script>
window.onSpotifyWebPlaybackSDKReady = () => {{
console.log("[SDK] SDK Ready");
const player = new Spotify.Player({{
name: 'RNG Web Player',
getOAuthToken: cb => cb(window.ACCESS_TOKEN),
volume: 0.5
}});
['initialization_error','authentication_error','account_error','playback_error']
.forEach(evt => player.addListener(evt, e => console.error(`[SDK ${evt}]`, e)));
player.addListener('ready', ({ device_id }) => {{
console.log('[SDK] Player ready, device_id:', device_id);
window._webDeviceId = device_id;
}});
player.connect().then(success => console.log('[SDK] connect()', success));
}};
</script>
"""
return (
gr.update(visible=True, value="β
Logged in! Select a playlist below."),
gr.update(visible=True, choices=list(user_playlists.keys())),
gr.update(visible=True, value=sdk_js),
)
def load_playlist_info(playlist_name: str):
"""
When a playlist is selected, show its artwork, name, and description.
"""
pid = user_playlists[playlist_name]
data = sp.playlist(pid)
img = data["images"][0]["url"] if data["images"] else ""
owner = data["owner"]["display_name"]
desc = data.get("description", "")
html = f"""
<img src="{img}" width="300" style="border-radius:8px;"/><br/>
<strong>{playlist_name}</strong> by {owner}<br/>{desc}
"""
return html, gr.update(visible=True)
def shuffle_and_play(playlist_name: str):
"""
Gather all track URIs, RNG-shuffle them, then fire off the Web Playback SDK
to play them in-browser on window._webDeviceId.
"""
pid = user_playlists.get(playlist_name)
if not pid:
return gr.update(value="β οΈ No playlist selected.", visible=True)
# fetch + shuffle
tracks, results = [], sp.playlist_tracks(pid)
tracks.extend(results["items"])
while results["next"]:
results = sp.next(results)
tracks.extend(results["items"])
uris = [t["track"]["uri"] for t in tracks if t["track"]]
random.shuffle(uris)
# in-page JS to start playback
uris_json = json.dumps(uris)
play_js = f"""
<div id="player_debug" style="color:white;font-family:monospace;"></div>
<script>
console.log("[JS] Starting playback with", window._webDeviceId);
console.log("[JS] URIs count:", {len(uris)});
(async () => {{
try {{
const resp = await fetch(
'https://api.spotify.com/v1/me/player/play?device_id=' + window._webDeviceId,
{{
method: 'PUT',
headers: {{
'Authorization': 'Bearer ' + window.ACCESS_TOKEN,
'Content-Type': 'application/json'
}},
body: JSON.stringify({{ uris: {uris_json} }})
}}
);
const text = await resp.text();
console.log("[JS] fetch status:", resp.status, text);
document.getElementById("player_debug").innerText =
`[JS] status: ${{resp.status}} β ${{text}}`;
}} catch(e) {{
console.error("[JS] Playback error:", e);
document.getElementById("player_debug").innerText =
"[JS] Playback error: " + e;
}}
}})();
</script>
"""
return gr.update(value="βΆοΈ Now playing in-browser!" + play_js, visible=True)
# ββββββββββββββββββββββββββββββββββββββββββββββ
with gr.Blocks() as demo:
# 0) Auto-bust from any huggingface.co iframe
gr.HTML("""
<script>
if (window.self !== window.top) {
window.top.location.href = window.location.href;
}
</script>
""")
# 0b) Fallback standalone link
gr.HTML('<a href="https://jisaacso219-rng-shuffle.hf.space/" '
'target="_blank">π Open in standalone tab</a>')
gr.Markdown("**1)** Login **2)** Select a playlist **3)** Shuffle & Play in-browser")
# Step 1: Login to Spotify in SAME tab
login_btn = gr.Button("π Step 1: Login to Spotify")
login_btn.click(
fn=None, inputs=None, outputs=None,
js=f"() => window.location.href = '{get_auth_url()}'"
)
# These will be revealed after login
code_box = gr.Textbox(visible=False, elem_id="auth_code")
status = gr.Markdown(visible=False)
playlist_dd= gr.Dropdown(choices=[], label="Select Playlist", visible=False)
sdk_html = gr.HTML(visible=False)
info_html = gr.HTML(visible=False)
shuffle_btn= gr.Button("π Step 3: Shuffle & Play", visible=False)
result = gr.HTML(visible=False)
# On page-load (or after redirect with ?code), run check_login
demo.load(
fn=check_login,
inputs=[code_box],
outputs=[status, playlist_dd, sdk_html],
js="() => new URLSearchParams(window.location.search).get('code') || ''"
)
# Show playlist info, then reveal shuffle button
playlist_dd.change(load_playlist_info, [playlist_dd], [info_html, shuffle_btn])
# Shuffle & play via Web Playback SDK
shuffle_btn.click(shuffle_and_play, [playlist_dd], [result])
if __name__ == "__main__":
demo.launch(server_name="0.0.0.0", ssr_mode=False)
|