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

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +71 -66
app.py CHANGED
@@ -1,15 +1,19 @@
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 "
@@ -17,6 +21,7 @@ SCOPE = (
17
  "playlist-read-private"
18
  )
19
 
 
20
  sp_oauth = SpotifyOAuth(
21
  client_id=CLIENT_ID,
22
  client_secret=CLIENT_SECRET,
@@ -32,26 +37,35 @@ def get_auth_url():
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");
@@ -60,7 +74,7 @@ def check_login(code: str):
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),
@@ -76,47 +90,52 @@ def check_login(code: str):
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();
@@ -131,64 +150,50 @@ def shuffle_and_play(name: str):
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)
 
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 (in Hugging Face Secrets)
10
  CLIENT_ID = os.environ["SPOTIFY_CLIENT_ID"]
11
  CLIENT_SECRET = os.environ["SPOTIFY_CLIENT_SECRET"]
 
12
 
13
+ # 2) This must match exactly in your Spotify Dashboard
14
+ REDIRECT_URI = "https://jisaacso219-rng-shuffle.hf.space/"
15
+
16
+ # 3) Include `streaming` for the Web Playback SDK
17
  SCOPE = (
18
  "streaming "
19
  "user-read-playback-state "
 
21
  "playlist-read-private"
22
  )
23
 
24
+ # Spotify OAuth helper
25
  sp_oauth = SpotifyOAuth(
26
  client_id=CLIENT_ID,
27
  client_secret=CLIENT_SECRET,
 
37
  return sp_oauth.get_authorize_url()
38
 
39
  def check_login(code: str):
40
+ """
41
+ Called via demo.load on every page-load.
42
+ When `code` is non-empty, exchanges it, fetches playlists,
43
+ and injects the Web Playback SDK + token + iframe-bust.
44
+ """
45
  global sp, user_playlists
46
+
47
+ # still waiting for Spotify to redirect back…
48
  if not code:
49
  return (
50
+ gr.update(visible=False), # status message
51
+ gr.update(visible=False), # playlist dropdown
52
+ gr.update(visible=False), # SDK injection HTML
53
  )
54
+
55
  # exchange code for token
56
  tokinfo = sp_oauth.get_access_token(code, as_dict=True)
57
  access_token = tokinfo["access_token"]
58
  sp = spotipy.Spotify(auth=access_token)
59
+
60
+ # fetch first 50 playlists
61
  items = sp.current_user_playlists(limit=50)["items"]
62
  user_playlists = {p["name"]: p["id"] for p in items}
63
+
64
+ # inject Web Playback SDK + debug scripts + iframe bust
65
  sdk_js = f"""
66
  <script>
67
+ // bust out of HF iframe if still sand-boxed
68
  if (window.self !== window.top) {{
 
69
  window.top.location.href = window.location.href;
70
  }}
71
  console.log("[SDK] ACCESS_TOKEN set");
 
74
  <script src="https://sdk.scdn.co/spotify-player.js"></script>
75
  <script>
76
  window.onSpotifyWebPlaybackSDKReady = () => {{
77
+ console.log("[SDK] SDK Ready");
78
  const player = new Spotify.Player({{
79
  name: 'RNG Web Player',
80
  getOAuthToken: cb => cb(window.ACCESS_TOKEN),
 
90
  }};
91
  </script>
92
  """
93
+
94
  return (
95
  gr.update(visible=True, value="βœ… Logged in! Select a playlist below."),
96
  gr.update(visible=True, choices=list(user_playlists.keys())),
97
  gr.update(visible=True, value=sdk_js),
98
  )
99
 
100
+ def load_playlist_info(playlist_name: str):
101
+ pid = user_playlists[playlist_name]
102
  data = sp.playlist(pid)
103
  img = data["images"][0]["url"] if data["images"] else ""
104
  owner= data["owner"]["display_name"]
105
  desc = data.get("description","")
106
  html = f"""
107
  <img src="{img}" width="300" style="border-radius:8px;"/><br/>
108
+ <strong>{playlist_name}</strong> by {owner}<br/>{desc}
109
  """
110
  return html, gr.update(visible=True)
111
 
112
+ def shuffle_and_play(playlist_name: str):
113
+ pid = user_playlists[playlist_name]
114
+ # gather tracks
115
  tracks, res = [], sp.playlist_tracks(pid)
116
  tracks.extend(res["items"])
117
  while res["next"]:
118
+ res = sp.next(res)
119
+ tracks.extend(res["items"])
120
  uris = [t["track"]["uri"] for t in tracks if t["track"]]
121
+ random.shuffle(uris) # your RNG shuffle
122
+
123
+ uris_json = json.dumps(uris)
124
  play_js = f"""
125
  <div id="player_debug" style="color:white;font-family:monospace;"></div>
126
  <script>
127
+ console.log("[JS] play→", window._webDeviceId, {len(uris)}, "URIs");
128
  (async () => {{
129
  try {{
130
  const resp = await fetch(
131
  'https://api.spotify.com/v1/me/player/play?device_id=' + window._webDeviceId,
132
  {{
133
+ method: 'PUT',
134
+ headers: {{
135
+ 'Authorization': 'Bearer ' + window.ACCESS_TOKEN,
136
+ 'Content-Type': 'application/json'
137
+ }},
138
+ body: JSON.stringify({{ uris: {uris_json} }})
139
  }}
140
  );
141
  const text = await resp.text();
 
150
  }})();
151
  </script>
152
  """
153
+ return gr.update(value="▢️ Now playing in-browser!" + play_js, visible=True)
154
 
155
  with gr.Blocks() as demo:
156
+ # 0) iframe-bust on initial load
157
  gr.HTML("""
158
  <script>
159
  if (window.self !== window.top) {
160
  window.top.location.href = window.location.href;
161
  }
162
+ </script>
163
+ """)
164
+ # 0b) fallback standalone link
165
  gr.HTML('<a href="https://jisaacso219-rng-shuffle.hf.space/" target="_blank">'
166
+ 'πŸ”— Open in standalone tab</a>')
167
+
168
+ gr.Markdown("**1)** Login β†’ **2)** Pick a playlist β†’ **3)** Shuffle & Play in-browser")
169
 
170
+ # Step 1: Login in the same tab
171
  login_btn = gr.Button("πŸ” Step 1: Login to Spotify")
172
+ login_btn.click(
173
+ fn=None, inputs=None, outputs=None,
174
  js=f"() => window.location.href = '{get_auth_url()}'"
175
  )
176
 
177
+ # State & UI placeholders
178
+ code_state = gr.State() # receives the OAuth `code`
179
+ status = gr.Markdown(visible=False)
180
+ playlist_dd= gr.Dropdown([], label="Step 2: Select a Playlist", visible=False)
181
+ sdk_html = gr.HTML(visible=False)
182
+
183
+ info_html = gr.HTML(visible=False)
184
+ shuffle_btn= gr.Button("πŸ”€ Step 3: Shuffle & Play", visible=False)
185
+ result = gr.HTML(visible=False)
186
+
187
+ # On page-load (or after redirect), grab `code` and run check_login
188
+ demo.load(
189
+ fn=check_login,
190
+ inputs=[code_state],
 
191
  outputs=[status, playlist_dd, sdk_html],
192
+ js="() => new URLSearchParams(window.location.search).get('code') || ''"
193
  )
194
 
 
195
  playlist_dd.change(load_playlist_info, [playlist_dd], [info_html, shuffle_btn])
196
  shuffle_btn.click(shuffle_and_play, [playlist_dd], [result])
197
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
198
  if __name__ == "__main__":
199
  demo.launch(server_name="0.0.0.0", ssr_mode=False)