File size: 5,088 Bytes
dbdd344
 
9faaa03
 
dbdd344
 
9c0c6aa
 
52a6c11
9c0c6aa
f4177e5
52a6c11
9faaa03
52a6c11
 
 
 
 
dbdd344
 
 
 
 
52a6c11
 
dbdd344
 
52a6c11
9faaa03
 
b414958
a7abb2d
 
 
b414958
52a6c11
a7abb2d
52a6c11
a7abb2d
f8df622
6c734a0
a7abb2d
52a6c11
 
 
 
a7abb2d
52a6c11
 
34ae2ff
671016b
52a6c11
f8df622
a7abb2d
b6b5233
52a6c11
f8df622
52a6c11
f8df622
52a6c11
 
 
 
 
f8df622
52a6c11
f8df622
a7abb2d
9c0c6aa
52a6c11
f8df622
52a6c11
 
f8df622
 
 
 
a7abb2d
f8df622
 
52a6c11
 
f8df622
 
 
52a6c11
 
 
 
 
 
f8df622
 
 
52a6c11
 
 
 
f4177e5
9c0c6aa
 
 
a7abb2d
 
 
 
 
 
9c0c6aa
 
f4177e5
9c0c6aa
dbdd344
f4177e5
9c0c6aa
 
34ae2ff
9c0c6aa
 
 
 
f8df622
f4177e5
f8df622
52a6c11
9c0c6aa
f8df622
b6b5233
a7abb2d
 
 
 
 
 
 
b6b5233
f8df622
52a6c11
9c0c6aa
 
 
 
 
 
 
 
 
 
 
52a6c11
9faaa03
 
b6b5233
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
import os
import gradio as gr
import spotipy
from spotipy.oauth2 import SpotifyOAuth
import random

# β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”
# 1. Hard-code your Redirect URI here:
REDIRECT_URI = "https://jisaacso219-rng-shuffle.hf.space/"

# 2. Client creds in Secrets:
CLIENT_ID     = os.environ["SPOTIFY_CLIENT_ID"]
CLIENT_SECRET = os.environ["SPOTIFY_CLIENT_SECRET"]
SCOPE         = (
    "user-read-playback-state "
    "user-modify-playback-state "
    "playlist-read-private"
)

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():
    url = sp_oauth.get_authorize_url()
    print(f"[DEBUG] Generated auth URL: {url}")
    return url

def check_login(code: str):
    print(f"[DEBUG] check_login called with code: {code}")
    global sp, user_playlists
    # Hide controls if no code yet
    if not code:
        return gr.update(visible=False), gr.update(visible=False, choices=[])
    # Exchange code for token
    token = sp_oauth.get_access_token(code, as_dict=False)
    sp = spotipy.Spotify(auth=token)
    pls = sp.current_user_playlists(limit=50)["items"]
    user_playlists = {p["name"]: p["id"] for p in pls}
    print(f"[DEBUG] Retrieved playlists: {list(user_playlists.keys())}")
    return (
        gr.update(visible=True, value="βœ… Logged in! Choose a playlist below."),
        gr.update(visible=True, choices=list(user_playlists.keys())),
    )

def load_playlist_info(pl_name: str):
    print(f"[DEBUG] load_playlist_info called for: {pl_name}")
    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):
    print(f"[DEBUG] shuffle_and_play called for: {pl_name}")
    pid     = user_playlists[pl_name]
    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"]]
    devices = sp.devices()["devices"]
    if not devices:
        print("[DEBUG] No active devices found")
        return gr.update(value="⚠️ No active device. Open Spotify and try again.", visible=True)
    sp.start_playback(device_id=devices[0]["id"], uris=uris)
    now = sp.current_playback()
    if now and now.get("item"):
        it      = now["item"]
        art     = it["album"]["images"][0]["url"]
        title   = it["name"]
        artists = ", ".join(a["name"] for a in it["artists"])
        html = f"""
          <img src="{art}" width="200px" style="border-radius:8px;"/><br/>
          ▢️ <strong>{title}</strong><br/>
          by {artists}
        """
        return html
    return "▢️ Playing your shuffled playlist!"

with gr.Blocks() as demo:
    gr.Markdown("## 🎡 RNG Spotify Playlist Shuffler")
    gr.Markdown("Login, pick a playlist, then shuffle & playβ€”all in one flow.")

    # 1️⃣ Login button: redirect the TOP window out of the iframe
    login_btn = gr.Button("πŸ” Step 1: Login to Spotify")
    login_btn.click(
        fn=None, inputs=None, outputs=None,
        js=f"""
        () => {{
          console.log("πŸ‘‰ [JS] Login button clicked, redirecting...");
          window.top.location.href = '{get_auth_url()}';
        }}
        """
    )

    # Hidden box to capture the `code` after redirect
    code_box = gr.Textbox(visible=False, elem_id="auth_code")

    # Status + playlist selector (hidden until login finishes)
    status   = gr.Markdown(visible=False)
    dropdown = gr.Dropdown(choices=[], label="Step 2: Select a Playlist", visible=False)

    # Playlist info + shuffle controls
    info_html   = gr.HTML(visible=False)
    shuffle_btn = gr.Button("πŸ”€ Step 3: Shuffle & Play", visible=False)
    result      = gr.HTML(visible=False)

    # πŸ”„ On every page-load: pull `?code=` from URL into our hidden box and call check_login()
    demo.load(
        fn=check_login,
        inputs=[code_box],
        outputs=[status, dropdown],
        js="""
        () => {
          const url = window.location.href;
          const code = new URLSearchParams(window.location.search).get('code') || "";
          console.log("🌐 [JS] Current URL:", url);
          console.log("πŸ”‘ [JS] Parsed code:", code);
          return code;
        }
        """
    )

    dropdown.change(
        fn=load_playlist_info,
        inputs=[dropdown],
        outputs=[info_html, shuffle_btn]
    )

    shuffle_btn.click(
        fn=shuffle_and_play,
        inputs=[dropdown],
        outputs=[result]
    )

if __name__ == "__main__":
    demo.launch()