Spaces:
Running
Running
File size: 5,457 Bytes
dbdd344 2082b5c dbdd344 9faaa03 dbdd344 2082b5c 52a6c11 2082b5c 52a6c11 9faaa03 dbdd344 52a6c11 dbdd344 52a6c11 9faaa03 b414958 702b499 b414958 52a6c11 2082b5c f8df622 2082b5c 6c734a0 2082b5c 52a6c11 2082b5c 52a6c11 2082b5c 34ae2ff 671016b 52a6c11 2082b5c b6b5233 52a6c11 f8df622 52a6c11 f8df622 2082b5c f8df622 52a6c11 2082b5c 52a6c11 f8df622 52a6c11 f8df622 2082b5c f8df622 52a6c11 2082b5c 702b499 2082b5c 702b499 2082b5c 702b499 9c0c6aa 2082b5c 9c0c6aa 2082b5c 9c0c6aa 2082b5c 9c0c6aa f8df622 2082b5c f8df622 52a6c11 9c0c6aa f8df622 b6b5233 702b499 2082b5c 702b499 b6b5233 f8df622 52a6c11 2082b5c 702b499 2082b5c 702b499 52a6c11 9faaa03 |
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 |
import os
import json
import gradio as gr
import spotipy
from spotipy.oauth2 import SpotifyOAuth
import random
# ββββββββββββββββ
# 1) Redirect URI (must match your Spotify Dashboard)
REDIRECT_URI = "https://jisaacso219-rng-shuffle.hf.space/"
# 2) Scopes now include `streaming`
SCOPE = (
"streaming "
"user-read-playback-state "
"user-modify-playback-state "
"playlist-read-private"
)
CLIENT_ID = os.environ["SPOTIFY_CLIENT_ID"]
CLIENT_SECRET = os.environ["SPOTIFY_CLIENT_SECRET"]
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 (demo.load). Exchanges the `code` for an access token,
then writes that token into window.ACCESS_TOKEN for the SDK.
"""
global sp, user_playlists
if not code:
# Wait for the redirect with ?code=
return gr.update(visible=False), gr.update(visible=False, choices=[])
# Exchange for token (as_dict so we can grab the raw access_token)
tokinfo = sp_oauth.get_access_token(code, as_dict=True)
access_token = tokinfo["access_token"]
sp = spotipy.Spotify(auth=access_token)
# Fetch playlists
pls = sp.current_user_playlists(limit=50)["items"]
user_playlists = {p["name"]: p["id"] for p in pls}
# Embed a script to set window.ACCESS_TOKEN for the Web Playback SDK
js_token = f"<script>window.ACCESS_TOKEN = '{access_token}';</script>"
return (
gr.update(visible=True, value="β
Logged in! Choose a playlist below." + js_token),
gr.update(visible=True, choices=list(user_playlists.keys())),
)
def load_playlist_info(pl_name: str):
pid = user_playlists[pl_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="300px" style="border-radius:10px;"/><br/>
<strong>{pl_name}</strong> by {owner}<br/>{desc}
"""
return html, gr.update(visible=True)
def shuffle_and_play(pl_name: str):
pid = user_playlists[pl_name]
# Gather + shuffle URIs
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)
# Generate a JS snippet that kicks off playback via the Web Playback SDK
uris_json = json.dumps(uris)
player_js = f"""
<script>
(async () => {{
const token = window.ACCESS_TOKEN;
const device_id = window._webDeviceId;
console.log("βΆοΈ Playing on", device_id);
await fetch(
'https://api.spotify.com/v1/me/player/play?device_id=' + device_id,
{{
method: 'PUT',
headers: {{
'Authorization': 'Bearer ' + token,
'Content-Type': 'application/json'
}},
body: JSON.stringify({{ uris: {uris_json} }})
}}
);
}})();
</script>
"""
return gr.update(value="βΆοΈ Now playing inβbrowser!" + player_js, visible=True)
with gr.Blocks() as demo:
# Instruction to open in standalone tab
gr.Markdown("""
β οΈ **Please open this Space in its own tab** (click the π icon top-right)
so the OAuth flow and embedded player can work correctly.
""")
# Inject the Web Playback SDK
gr.HTML("""
<script src="https://sdk.scdn.co/spotify-player.js"></script>
<script>
window.onSpotifyWebPlaybackSDKReady = () => {
window._player = new Spotify.Player({
name: 'RNG Web Player',
getOAuthToken: cb => { cb(window.ACCESS_TOKEN); },
volume: 0.5
});
_player.addListener('ready', ({ device_id }) => {
console.log('π’ Web Playback ready, device_id:', device_id);
window._webDeviceId = device_id;
});
_player.connect();
};
</script>
""")
# 1οΈβ£ Login
login_btn = gr.Button(
"π Step 1: Login to Spotify",
link=get_auth_url()
)
# Hidden box for the OAuth `code`
code_box = gr.Textbox(visible=False, elem_id="auth_code")
# 2οΈβ£ Playlist selection
status = gr.Markdown(visible=False)
dropdown = gr.Dropdown(choices=[], label="Step 2: Select a Playlist", visible=False)
# 3οΈβ£ Playlist info & shuffle
info_html = gr.HTML(visible=False)
shuffle_btn = gr.Button("π Step 3: Shuffle & Play", visible=False)
result = gr.HTML(visible=False)
# On page-load: grab `?code=` and run check_login
demo.load(
fn=check_login,
inputs=[code_box],
outputs=[status, dropdown],
js="""
() => {
const c = new URLSearchParams(window.location.search).get('code') || "";
return c;
}
"""
)
# Show playlist info once selected
dropdown.change(load_playlist_info, [dropdown], [info_html, shuffle_btn])
# Shuffle & play in-browser
shuffle_btn.click(shuffle_and_play, [dropdown], [result])
if __name__ == "__main__":
demo.launch()
|