jisaacso219 commited on
Commit
9cf08d5
Β·
verified Β·
1 Parent(s): cfbd08c

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +67 -76
app.py CHANGED
@@ -6,17 +6,13 @@ from spotipy.oauth2 import SpotifyOAuth
6
  import random
7
 
8
  # β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”
9
- # 1) Redirect URI (must exactly match your Spotify Dashboard)
10
  REDIRECT_URI = "https://jisaacso219-rng-shuffle.hf.space/"
11
-
12
- # 2) Include `streaming` in your scopes for Web Playback SDK
13
  SCOPE = (
14
  "streaming "
15
  "user-read-playback-state "
16
  "user-modify-playback-state "
17
  "playlist-read-private"
18
  )
19
-
20
  CLIENT_ID = os.environ["SPOTIFY_CLIENT_ID"]
21
  CLIENT_SECRET = os.environ["SPOTIFY_CLIENT_SECRET"]
22
 
@@ -27,60 +23,55 @@ sp_oauth = SpotifyOAuth(
27
  scope=SCOPE,
28
  show_dialog=True,
29
  )
30
-
31
  sp = None
32
  user_playlists = {}
33
 
34
  def get_auth_url():
35
- return sp_oauth.get_authorize_url()
 
 
36
 
37
  def check_login(code: str):
38
- """
39
- Called via demo.load on every page‐load. Once we have `code`,
40
- exchange it, fetch playlists, _and_ inject our Web Playback SDK.
41
- """
42
  global sp, user_playlists
43
-
44
  if not code:
45
- # still waiting for Spotify to redirect back
46
  return (
47
- gr.update(visible=False), # status
48
- gr.update(visible=False), # dropdown
49
- gr.update(visible=False), # html_token
50
  )
51
-
52
- # 1) Exchange code for token
53
  tokinfo = sp_oauth.get_access_token(code, as_dict=True)
54
  access_token = tokinfo["access_token"]
 
55
  sp = spotipy.Spotify(auth=access_token)
56
-
57
- # 2) Fetch playlists
58
  items = sp.current_user_playlists(limit=50)["items"]
59
  user_playlists = {p["name"]: p["id"] for p in items}
 
60
 
61
- # 3) Build the script injection:
62
- # - set window.ACCESS_TOKEN
63
- # - load the SDK
64
- # - initialize the player and grab device_id
65
  sdk_js = f"""
66
  <script>
 
67
  window.ACCESS_TOKEN = "{access_token}";
68
  </script>
69
  <script src="https://sdk.scdn.co/spotify-player.js"></script>
70
  <script>
71
  window.onSpotifyWebPlaybackSDKReady = () => {{
 
72
  const player = new Spotify.Player({{
73
  name: 'RNG Web Player',
74
- getOAuthToken: cb => {{ cb(window.ACCESS_TOKEN); }},
75
  volume: 0.5
76
  }});
77
- player.addListener('ready', ({{
78
- device_id
79
- }}) => {{
80
- console.log('🟒 Web Playback ready, device_id:', device_id);
 
81
  window._webDeviceId = device_id;
82
  }});
83
- player.connect();
 
84
  }};
85
  </script>
86
  """
@@ -88,10 +79,11 @@ def check_login(code: str):
88
  return (
89
  gr.update(visible=True, value="βœ… Logged in! Choose a playlist below."),
90
  gr.update(visible=True, choices=list(user_playlists.keys())),
91
- gr.update(visible=True, value=sdk_js)
92
  )
93
 
94
  def load_playlist_info(pl_name: str):
 
95
  pid = user_playlists[pl_name]
96
  data = sp.playlist(pid)
97
  img = data["images"][0]["url"] if data["images"] else ""
@@ -104,92 +96,91 @@ def load_playlist_info(pl_name: str):
104
  return html, gr.update(visible=True)
105
 
106
  def shuffle_and_play(pl_name: str):
107
- pid = user_playlists[pl_name]
 
 
 
 
108
 
109
- # gather & shuffle URIs
110
  tracks, results = [], sp.playlist_tracks(pid)
111
  tracks.extend(results["items"])
112
  while results["next"]:
113
- results = sp.next(results)
114
- tracks.extend(results["items"])
115
  uris = [t["track"]["uri"] for t in tracks if t["track"]]
116
  random.shuffle(uris)
 
117
 
118
- # now hand off to the in‐page player via fetch()
119
  uris_json = json.dumps(uris)
120
  play_js = f"""
 
121
  <script>
122
- (async () => {{
123
- const token = window.ACCESS_TOKEN;
124
- const device_id = window._webDeviceId;
125
- console.log("▢️ Playing {len(uris)} tracks on", device_id);
126
- await fetch(
127
- 'https://api.spotify.com/v1/me/player/play?device_id=' + device_id,
128
- {{
129
- method: 'PUT',
130
- headers: {{
131
- 'Authorization': 'Bearer ' + token,
132
- 'Content-Type': 'application/json'
133
- }},
134
- body: JSON.stringify({{ uris: {uris_json} }})
 
 
 
 
 
 
 
 
 
 
135
  }}
136
- );
137
- }})();
138
  </script>
139
  """
140
  return gr.update(value="▢️ Now playing in-browser!" + play_js, visible=True)
141
 
142
- # β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”
143
  with gr.Blocks() as demo:
144
  gr.Markdown("""
145
- ⚠️ **Please open this Space in its own tab** (click the πŸ”— icon top-right)
146
- so that OAuth and the embedded player can work correctly.
147
  """)
148
 
149
- # β€” Step 1: Login β€” opens a new Tab outside the Hugging Face iframe
150
  login_btn = gr.Button(
151
  "πŸ” Step 1: Login to Spotify",
152
  link=get_auth_url()
153
  )
154
 
155
- # β€” demo.load() needs a β€œcode” placeholder to call check_login(code)
156
  code_box = gr.Textbox(visible=False, elem_id="auth_code")
157
  status = gr.Markdown(visible=False)
158
  dropdown = gr.Dropdown(choices=[], label="Step 2: Select a Playlist", visible=False)
 
159
 
160
- # β€” this hidden HTML will receive our SDK + init scripts
161
- html_token = gr.HTML(visible=False)
162
-
163
- # β€” Step 3a: show playlist info once they choose one
164
  info_html = gr.HTML(visible=False)
 
 
165
 
166
- # β€” Step 3b: shuffle & play
167
- shuffle_btn = gr.Button("πŸ”€ Step 3: Shuffle & Play", visible=False)
168
- result = gr.HTML(visible=False)
169
-
170
- # β€” on every load, grab ?code=… and call check_login
171
  demo.load(
172
  fn=check_login,
173
  inputs=[code_box],
174
  outputs=[status, dropdown, html_token],
175
  js="""
176
  () => {
177
- return new URLSearchParams(window.location.search).get('code') || "";
 
 
178
  }
179
  """
180
  )
181
 
182
- dropdown.change(
183
- fn=load_playlist_info,
184
- inputs=[dropdown],
185
- outputs=[info_html, shuffle_btn]
186
- )
187
-
188
- shuffle_btn.click(
189
- fn=shuffle_and_play,
190
- inputs=[dropdown],
191
- outputs=[result]
192
- )
193
 
194
  if __name__ == "__main__":
195
  demo.launch()
 
6
  import random
7
 
8
  # β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”
 
9
  REDIRECT_URI = "https://jisaacso219-rng-shuffle.hf.space/"
 
 
10
  SCOPE = (
11
  "streaming "
12
  "user-read-playback-state "
13
  "user-modify-playback-state "
14
  "playlist-read-private"
15
  )
 
16
  CLIENT_ID = os.environ["SPOTIFY_CLIENT_ID"]
17
  CLIENT_SECRET = os.environ["SPOTIFY_CLIENT_SECRET"]
18
 
 
23
  scope=SCOPE,
24
  show_dialog=True,
25
  )
 
26
  sp = None
27
  user_playlists = {}
28
 
29
  def get_auth_url():
30
+ url = sp_oauth.get_authorize_url()
31
+ print("[SERVER] Auth URL:", url)
32
+ return url
33
 
34
  def check_login(code: str):
35
+ print("[SERVER] check_login code:", code)
 
 
 
36
  global sp, user_playlists
 
37
  if not code:
 
38
  return (
39
+ gr.update(visible=False),
40
+ gr.update(visible=False),
41
+ gr.update(visible=False),
42
  )
 
 
43
  tokinfo = sp_oauth.get_access_token(code, as_dict=True)
44
  access_token = tokinfo["access_token"]
45
+ print("[SERVER] Received token:", access_token[:8], "…")
46
  sp = spotipy.Spotify(auth=access_token)
 
 
47
  items = sp.current_user_playlists(limit=50)["items"]
48
  user_playlists = {p["name"]: p["id"] for p in items}
49
+ print("[SERVER] Playlists:", list(user_playlists.keys()))
50
 
51
+ # inject token + SDK loader + init + debug listeners
 
 
 
52
  sdk_js = f"""
53
  <script>
54
+ console.log("[SDK] Setting ACCESS_TOKEN");
55
  window.ACCESS_TOKEN = "{access_token}";
56
  </script>
57
  <script src="https://sdk.scdn.co/spotify-player.js"></script>
58
  <script>
59
  window.onSpotifyWebPlaybackSDKReady = () => {{
60
+ console.log("[SDK] SDK Ready");
61
  const player = new Spotify.Player({{
62
  name: 'RNG Web Player',
63
+ getOAuthToken: cb => cb(window.ACCESS_TOKEN),
64
  volume: 0.5
65
  }});
66
+ // log errors
67
+ ['initialization_error','authentication_error','account_error','playback_error']
68
+ .forEach(evt => player.addListener(evt, e => console.error('[SDK ' + evt + ']', e)));
69
+ player.addListener('ready', {{ device_id }} => {{
70
+ console.log('[SDK] Player ready, device_id:', device_id);
71
  window._webDeviceId = device_id;
72
  }});
73
+ player.addListener('player_state_changed', state => console.log('[SDK] state_changed', state));
74
+ player.connect().then(s => console.log('[SDK] connect()', s));
75
  }};
76
  </script>
77
  """
 
79
  return (
80
  gr.update(visible=True, value="βœ… Logged in! Choose 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(pl_name: str):
86
+ print("[SERVER] load_playlist_info:", pl_name)
87
  pid = user_playlists[pl_name]
88
  data = sp.playlist(pid)
89
  img = data["images"][0]["url"] if data["images"] else ""
 
96
  return html, gr.update(visible=True)
97
 
98
  def shuffle_and_play(pl_name: str):
99
+ print("[SERVER] shuffle_and_play:", pl_name)
100
+ pid = user_playlists.get(pl_name)
101
+ if not pid:
102
+ print("[SERVER] No playlist selected")
103
+ return gr.update(value="⚠️ No playlist selected.", visible=True)
104
 
105
+ # collect & shuffle URIs
106
  tracks, results = [], sp.playlist_tracks(pid)
107
  tracks.extend(results["items"])
108
  while results["next"]:
109
+ results = sp.next(results); tracks.extend(results["items"])
 
110
  uris = [t["track"]["uri"] for t in tracks if t["track"]]
111
  random.shuffle(uris)
112
+ print(f"[SERVER] Shuffled {len(uris)} tracks")
113
 
114
+ # JS snippet with debug output in the UI
115
  uris_json = json.dumps(uris)
116
  play_js = f"""
117
+ <div id="player_debug" style="color:white;font-family:monospace;"></div>
118
  <script>
119
+ console.log("[JS] Starting playback, device_id:", window._webDeviceId);
120
+ console.log("[JS] URIs length:", {len(uris)});
121
+ (async () => {{
122
+ try {{
123
+ const resp = await fetch(
124
+ 'https://api.spotify.com/v1/me/player/play?device_id=' + window._webDeviceId,
125
+ {{
126
+ method: 'PUT',
127
+ headers: {{
128
+ 'Authorization': 'Bearer ' + window.ACCESS_TOKEN,
129
+ 'Content-Type': 'application/json'
130
+ }},
131
+ body: JSON.stringify({{uris: {uris_json}}})
132
+ }}
133
+ );
134
+ const text = await resp.text();
135
+ console.log("[JS] fetch status:", resp.status, text);
136
+ document.getElementById("player_debug").innerText =
137
+ `[JS] fetch status: ${resp.status} – ${text}`;
138
+ }} catch(e) {{
139
+ console.error("[JS] Playback error:", e);
140
+ document.getElementById("player_debug").innerText =
141
+ "[JS] Playback error: " + e;
142
  }}
143
+ }})();
 
144
  </script>
145
  """
146
  return gr.update(value="▢️ Now playing in-browser!" + play_js, visible=True)
147
 
 
148
  with gr.Blocks() as demo:
149
  gr.Markdown("""
150
+ ⚠️ **Open in its own tab** (click the πŸ”— icon top-right)
151
+ after logging in, check your browser console for SDK logs.
152
  """)
153
 
 
154
  login_btn = gr.Button(
155
  "πŸ” Step 1: Login to Spotify",
156
  link=get_auth_url()
157
  )
158
 
159
+ # placeholders for code, status, playlists, sdk scripts
160
  code_box = gr.Textbox(visible=False, elem_id="auth_code")
161
  status = gr.Markdown(visible=False)
162
  dropdown = gr.Dropdown(choices=[], label="Step 2: Select a Playlist", visible=False)
163
+ html_token = gr.HTML(visible=False) # will inject our SDK + init + debug
164
 
 
 
 
 
165
  info_html = gr.HTML(visible=False)
166
+ shuffle_btn= gr.Button("πŸ”€ Step 3: Shuffle & Play", visible=False)
167
+ result = gr.HTML(visible=False)
168
 
 
 
 
 
 
169
  demo.load(
170
  fn=check_login,
171
  inputs=[code_box],
172
  outputs=[status, dropdown, html_token],
173
  js="""
174
  () => {
175
+ const c = new URLSearchParams(window.location.search).get('code') || "";
176
+ console.log("[JS] demo.load code:", c);
177
+ return c;
178
  }
179
  """
180
  )
181
 
182
+ dropdown.change(load_playlist_info, [dropdown], [info_html, shuffle_btn])
183
+ shuffle_btn.click(shuffle_and_play, [dropdown], [result])
 
 
 
 
 
 
 
 
 
184
 
185
  if __name__ == "__main__":
186
  demo.launch()