jisaacso219 commited on
Commit
cfbd08c
Β·
verified Β·
1 Parent(s): 2082b5c

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +94 -72
app.py CHANGED
@@ -5,11 +5,11 @@ import spotipy
5
  from spotipy.oauth2 import SpotifyOAuth
6
  import random
7
 
8
- # β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”
9
- # 1) Redirect URI (must match your Spotify Dashboard)
10
  REDIRECT_URI = "https://jisaacso219-rng-shuffle.hf.space/"
11
 
12
- # 2) Scopes now include `streaming`
13
  SCOPE = (
14
  "streaming "
15
  "user-read-playback-state "
@@ -36,28 +36,59 @@ def get_auth_url():
36
 
37
  def check_login(code: str):
38
  """
39
- Called on page‐load (demo.load). Exchanges the `code` for an access token,
40
- then writes that token into window.ACCESS_TOKEN for the SDK.
41
  """
42
  global sp, user_playlists
 
43
  if not code:
44
- # Wait for the redirect with ?code=
45
- return gr.update(visible=False), gr.update(visible=False, choices=[])
46
- # Exchange for token (as_dict so we can grab the raw access_token)
 
 
 
 
 
47
  tokinfo = sp_oauth.get_access_token(code, as_dict=True)
48
  access_token = tokinfo["access_token"]
49
  sp = spotipy.Spotify(auth=access_token)
50
 
51
- # Fetch playlists
52
- pls = sp.current_user_playlists(limit=50)["items"]
53
- user_playlists = {p["name"]: p["id"] for p in pls}
54
 
55
- # Embed a script to set window.ACCESS_TOKEN for the Web Playback SDK
56
- js_token = f"<script>window.ACCESS_TOKEN = '{access_token}';</script>"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
57
 
58
  return (
59
- gr.update(visible=True, value="βœ… Logged in! Choose a playlist below." + js_token),
60
  gr.update(visible=True, choices=list(user_playlists.keys())),
 
61
  )
62
 
63
  def load_playlist_info(pl_name: str):
@@ -67,14 +98,15 @@ def load_playlist_info(pl_name: str):
67
  owner = data["owner"]["display_name"]
68
  desc = data.get("description", "")
69
  html = f"""
70
- <img src="{img}" width="300px" style="border-radius:10px;"/><br/>
71
  <strong>{pl_name}</strong> by {owner}<br/>{desc}
72
  """
73
  return html, gr.update(visible=True)
74
 
75
  def shuffle_and_play(pl_name: str):
76
  pid = user_playlists[pl_name]
77
- # Gather + shuffle URIs
 
78
  tracks, results = [], sp.playlist_tracks(pid)
79
  tracks.extend(results["items"])
80
  while results["next"]:
@@ -83,92 +115,82 @@ def shuffle_and_play(pl_name: str):
83
  uris = [t["track"]["uri"] for t in tracks if t["track"]]
84
  random.shuffle(uris)
85
 
86
- # Generate a JS snippet that kicks off playback via the Web Playback SDK
87
  uris_json = json.dumps(uris)
88
- player_js = f"""
89
  <script>
90
- (async () => {{
91
- const token = window.ACCESS_TOKEN;
92
- const device_id = window._webDeviceId;
93
- console.log("▢️ Playing on", device_id);
94
- await fetch(
95
- 'https://api.spotify.com/v1/me/player/play?device_id=' + device_id,
96
- {{
97
- method: 'PUT',
98
- headers: {{
99
- 'Authorization': 'Bearer ' + token,
100
- 'Content-Type': 'application/json'
101
- }},
102
- body: JSON.stringify({{ uris: {uris_json} }})
103
- }}
104
- );
105
- }})();
106
  </script>
107
  """
108
- return gr.update(value="▢️ Now playing in‐browser!" + player_js, visible=True)
109
 
 
110
  with gr.Blocks() as demo:
111
- # Instruction to open in standalone tab
112
  gr.Markdown("""
113
  ⚠️ **Please open this Space in its own tab** (click the πŸ”— icon top-right)
114
- so the OAuth flow and embedded player can work correctly.
115
  """)
116
 
117
- # Inject the Web Playback SDK
118
- gr.HTML("""
119
- <script src="https://sdk.scdn.co/spotify-player.js"></script>
120
- <script>
121
- window.onSpotifyWebPlaybackSDKReady = () => {
122
- window._player = new Spotify.Player({
123
- name: 'RNG Web Player',
124
- getOAuthToken: cb => { cb(window.ACCESS_TOKEN); },
125
- volume: 0.5
126
- });
127
- _player.addListener('ready', ({ device_id }) => {
128
- console.log('🟒 Web Playback ready, device_id:', device_id);
129
- window._webDeviceId = device_id;
130
- });
131
- _player.connect();
132
- };
133
- </script>
134
- """)
135
-
136
- # 1️⃣ Login
137
  login_btn = gr.Button(
138
  "πŸ” Step 1: Login to Spotify",
139
  link=get_auth_url()
140
  )
141
 
142
- # Hidden box for the OAuth `code`
143
- code_box = gr.Textbox(visible=False, elem_id="auth_code")
 
 
 
 
 
144
 
145
- # 2️⃣ Playlist selection
146
- status = gr.Markdown(visible=False)
147
- dropdown = gr.Dropdown(choices=[], label="Step 2: Select a Playlist", visible=False)
148
 
149
- # 3️⃣ Playlist info & shuffle
150
- info_html = gr.HTML(visible=False)
151
  shuffle_btn = gr.Button("πŸ”€ Step 3: Shuffle & Play", visible=False)
152
  result = gr.HTML(visible=False)
153
 
154
- # On page-load: grab `?code=` and run check_login
155
  demo.load(
156
  fn=check_login,
157
  inputs=[code_box],
158
- outputs=[status, dropdown],
159
  js="""
160
  () => {
161
- const c = new URLSearchParams(window.location.search).get('code') || "";
162
- return c;
163
  }
164
  """
165
  )
166
 
167
- # Show playlist info once selected
168
- dropdown.change(load_playlist_info, [dropdown], [info_html, shuffle_btn])
 
 
 
169
 
170
- # Shuffle & play in-browser
171
- shuffle_btn.click(shuffle_and_play, [dropdown], [result])
 
 
 
172
 
173
  if __name__ == "__main__":
174
  demo.launch()
 
 
5
  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 "
 
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
+ """
87
 
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):
 
98
  owner = data["owner"]["display_name"]
99
  desc = data.get("description", "")
100
  html = f"""
101
+ <img src="{img}" width="300" style="border-radius:8px;"/><br/>
102
  <strong>{pl_name}</strong> by {owner}<br/>{desc}
103
  """
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"]:
 
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()
196
+