dkappe commited on
Commit
5f39c5f
·
verified ·
1 Parent(s): 13db17c

Upload top10.py

Browse files

Get weekly top 10 and write markdown with AISong classification.

Files changed (1) hide show
  1. top10.py +170 -0
top10.py ADDED
@@ -0,0 +1,170 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env python3
2
+ import base64
3
+ import json
4
+ import os
5
+ import re
6
+ import sys
7
+ import tempfile
8
+ import urllib.request
9
+ from datetime import datetime
10
+ from gradio_client import Client, handle_file
11
+
12
+ import contextlib
13
+ import io
14
+
15
+
16
+ def get_itunes_top10():
17
+ """Fetches top 10 tracks, metadata, and audio preview URLs directly from iTunes RSS."""
18
+ url = "https://itunes.apple.com/us/rss/topsongs/limit=10/json"
19
+ req = urllib.request.Request(url, headers={"User-Agent": "Mozilla/5.0"})
20
+
21
+ try:
22
+ with urllib.request.urlopen(req) as resp:
23
+ data = json.loads(resp.read().decode("utf-8"))
24
+
25
+ entries = data["feed"]["entry"]
26
+ entries = entries if isinstance(entries, list) else [entries]
27
+
28
+ songs = []
29
+ for idx, entry in enumerate(entries[:10], 1):
30
+ title = entry["im:name"]["label"]
31
+ artist = entry["im:artist"]["label"]
32
+ genre = entry["category"]["attributes"]["label"]
33
+
34
+ # Extract 30s audio preview URL
35
+ links = entry.get("link", [])
36
+ preview_url = None
37
+ if isinstance(links, list):
38
+ for l in links:
39
+ attrs = l.get("attributes", {})
40
+ if attrs.get("title") == "Preview" or attrs.get("type", "").startswith("audio"):
41
+ preview_url = attrs.get("href")
42
+ break
43
+ elif isinstance(links, dict):
44
+ preview_url = links.get("attributes", {}).get("href")
45
+
46
+ songs.append({
47
+ "rank": idx,
48
+ "title": title,
49
+ "artist": artist,
50
+ "genre": genre,
51
+ "preview_url": preview_url,
52
+ })
53
+ return songs
54
+ except Exception as e:
55
+ print(f"Error fetching iTunes top tracks: {e}", file=sys.stderr)
56
+ return []
57
+
58
+
59
+ def extract_embedded_chart(res, max_width=220):
60
+ """
61
+ Extracts plot data from Gradio's response (file path, raw SVG, or dict)
62
+ and formats it into a self-contained inline element for Markdown tables.
63
+ """
64
+ # 1. Unpack list/tuple wrappers returned by gradio_client
65
+ if isinstance(res, (tuple, list)):
66
+ for item in res:
67
+ if isinstance(item, (dict, str)):
68
+ res = item
69
+ break
70
+
71
+ plot_data = None
72
+ if isinstance(res, dict):
73
+ plot_data = res.get("plot") or res.get("value") or res.get("name") or res.get("path")
74
+ elif isinstance(res, str):
75
+ plot_data = res
76
+
77
+ if not plot_data or not isinstance(plot_data, str):
78
+ return "No plot payload"
79
+
80
+ # 2. Handle temporary file paths created on disk by gradio_client
81
+ if os.path.isfile(plot_data):
82
+ try:
83
+ if plot_data.lower().endswith(".svg"):
84
+ with open(plot_data, "r", encoding="utf-8") as f:
85
+ svg_content = f.read()
86
+ svg_clean = re.sub(r"<\?xml.*?\?>", "", svg_content, flags=re.DOTALL).strip()
87
+ svg_single_line = re.sub(r"\s+", " ", svg_clean)
88
+ return f'<div style="max-width: {max_width}px; max-height: 150px; display: inline-block;">{svg_single_line}</div>'
89
+ else:
90
+ # Base64-encode PNG/JPEG files directly into an inline <img> tag
91
+ with open(plot_data, "rb") as f:
92
+ encoded = base64.b64encode(f.read()).decode("utf-8")
93
+ ext = os.path.splitext(plot_data)[1].lstrip(".").lower() or "png"
94
+ return f'<img src="data:image/{ext};base64,{encoded}" width="{max_width}" alt="Classification Plot" />'
95
+ except Exception as e:
96
+ return f"Error reading chart file: {e}"
97
+
98
+ # 3. Handle raw in-memory SVG string
99
+ if "<svg" in plot_data:
100
+ svg_clean = re.sub(r"<\?xml.*?\?>", "", plot_data, flags=re.DOTALL).strip()
101
+ svg_single_line = re.sub(r"\s+", " ", svg_clean)
102
+ return f'<div style="max-width: {max_width}px; max-height: 150px; display: inline-block;">{svg_single_line}</div>'
103
+
104
+ # 4. Handle raw Base64 data URI string
105
+ if plot_data.startswith("data:image"):
106
+ return f'<img src="{plot_data}" width="{max_width}" alt="Classification Plot" />'
107
+
108
+ return f"Unsupported payload: {plot_data[:40]}..."
109
+
110
+
111
+ def main():
112
+ print("Fetching iTunes Top 10 tracks...", file=sys.stderr)
113
+ songs = get_itunes_top10()
114
+
115
+ if not songs:
116
+ print("Error: Could not retrieve iTunes top tracks.", file=sys.stderr)
117
+ sys.exit(1)
118
+
119
+ print("Connecting to Hugging Face Space (dkappe/AISong)...", file=sys.stderr)
120
+ # Mute stdout while connecting to the Space
121
+ with contextlib.redirect_stdout(io.StringIO()):
122
+ client = Client("dkappe/AISong")
123
+
124
+ date_str = datetime.now().strftime("%Y-%m-%d")
125
+
126
+ results = []
127
+ for song in songs:
128
+ rank = song["rank"]
129
+ title = song["title"]
130
+ artist = song["artist"]
131
+ genre = song["genre"]
132
+ preview_url = song["preview_url"]
133
+
134
+ if not preview_url:
135
+ print(f"[{rank}/10] '{title}' - No audio preview available.", file=sys.stderr)
136
+ results.append((rank, title, artist, genre, "No Preview Available"))
137
+ continue
138
+
139
+ print(f"[{rank}/10] Analyzing '{title}' by {artist}...", file=sys.stderr)
140
+ with tempfile.NamedTemporaryFile(suffix=".m4a", delete=False) as tmp:
141
+ tmp_path = tmp.name
142
+
143
+ try:
144
+ urllib.request.urlretrieve(preview_url, tmp_path)
145
+ res = client.predict(handle_file(tmp_path), api_name="/predict")
146
+
147
+ chart_html = extract_embedded_chart(res, max_width=220)
148
+ results.append((rank, title, artist, genre, chart_html))
149
+ except Exception as e:
150
+ print(f"Error evaluating '{title}': {e}", file=sys.stderr)
151
+ results.append((rank, title, artist, genre, f"Error: {e}"))
152
+ finally:
153
+ if os.path.exists(tmp_path):
154
+ os.remove(tmp_path)
155
+
156
+ # Output Markdown
157
+ print("# iTunes Weekly Top 10 Audio Classification Report")
158
+ print(f"**Date:** {date_str}\n")
159
+ print("Classifier Model: [`dkappe/AISong`](https://huggingface.co/spaces/dkappe/AISong)\n")
160
+ print("| Rank | Song Name | Artist | Genre | Classification Plot |")
161
+ print("| :---: | :--- | :--- | :--- | :---: |")
162
+ for r, t, a, g, p in results:
163
+ clean_title = t.replace("|", "/")
164
+ clean_artist = a.replace("|", "/")
165
+ print(f"| {r} | {clean_title} | {clean_artist} | {g} | {p} |")
166
+
167
+
168
+ if __name__ == "__main__":
169
+ main()
170
+