jisaacso219 commited on
Commit
8c6ab50
Β·
verified Β·
1 Parent(s): dc5eb33

Update app.py

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