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()