dodd869 commited on
Commit
285b466
·
verified ·
1 Parent(s): 63bbaa6

Create main.py

Browse files
Files changed (1) hide show
  1. main.py +121 -0
main.py ADDED
@@ -0,0 +1,121 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os, json, glob, shutil, subprocess, tempfile, time, re, requests
2
+ from pathlib import Path
3
+ import gradio as gr
4
+ from ytmusicapi import YTMusic
5
+ import spotipy
6
+ from spotipy.oauth2 import SpotifyClientCredentials
7
+ from mutagen.id3 import ID3, TIT2, TPE1, TALB, TCON, TYER, TRCK, TPE2, USLT, APIC, COMM
8
+ from mutagen.mp3 import MP3
9
+
10
+ # ------------- CONFIG -------------
11
+ # Secrets must be set in HF Space → Settings → Secrets
12
+ SPOTIFY_CLIENT_ID = os.getenv("SPOTIFY_CLIENT_ID")
13
+ SPOTIFY_CLIENT_SECRET = os.getenv("SPOTIFY_CLIENT_SECRET")
14
+ # ----------------------------------
15
+
16
+ TMP = Path("/tmp") # HF allows full r/w here
17
+ STATIC = Path(__file__).parent # read-only, keep code / assets only
18
+
19
+ ytm = YTMusic()
20
+ sp = None
21
+ if SPOTIFY_CLIENT_ID and SPOTIFY_CLIENT_SECRET:
22
+ sp = spotipy.Spotify(
23
+ auth_manager=SpotifyClientCredentials(
24
+ client_id=SPOTIFY_CLIENT_ID,
25
+ client_secret=SPOTIFY_CLIENT_SECRET,
26
+ )
27
+ )
28
+
29
+ # ------------- HELPERS -------------
30
+ def safe(s: str) -> str:
31
+ return re.sub(r'[\\/:"*?<>|]+', "", s).strip()
32
+
33
+ def ytdlp_fast(url: str, out: str) -> None:
34
+ """Download best-audio → 320 k MP3 using aria2c."""
35
+ subprocess.run(
36
+ [
37
+ "yt-dlp",
38
+ "-f", "bestaudio/best",
39
+ "--extract-audio",
40
+ "--audio-format", "mp3",
41
+ "--audio-quality", "320K",
42
+ "--concurrent-fragments", "16",
43
+ "--external-downloader", "aria2c",
44
+ "--external-downloader-args",
45
+ "aria2c:-x16 -s16 -j16 -k1M --file-allocation=none",
46
+ "--fragment-retries", "infinite",
47
+ "--skip-unavailable-fragments",
48
+ "--no-part",
49
+ "--no-cache-dir",
50
+ "--force-ipv4",
51
+ "--sponsorblock-remove", "all",
52
+ "-o", out,
53
+ url,
54
+ ],
55
+ check=True,
56
+ )
57
+
58
+ def write_tags(
59
+ fn: str,
60
+ title: str,
61
+ artist: str,
62
+ album: str,
63
+ year: str,
64
+ cover: str,
65
+ lyrics: str,
66
+ genre: str = "",
67
+ track: str = "",
68
+ album_artist: str = "",
69
+ description: str = "",
70
+ ):
71
+ audio = MP3(fn, ID3=ID3)
72
+ if audio.tags is None:
73
+ audio.add_tags()
74
+ tags = audio.tags
75
+ tags.add(TIT2(encoding=3, text=title))
76
+ tags.add(TPE1(encoding=3, text=artist))
77
+ tags.add(TALB(encoding=3, text=album))
78
+ if genre:
79
+ tags.add(TCON(encoding=3, text=genre))
80
+ if year:
81
+ tags.add(TYER(encoding=3, text=year))
82
+ if track:
83
+ tags.add(TRCK(encoding=3, text=track))
84
+ if album_artist:
85
+ tags.add(TPE2(encoding=3, text=album_artist))
86
+ if lyrics:
87
+ tags.add(USLT(encoding=3, lang="eng", desc="", text=lyrics))
88
+ if description:
89
+ tags.add(COMM(encoding=3, lang="eng", desc="desc", text=description))
90
+ if cover:
91
+ try:
92
+ img = requests.get(cover, timeout=15).content
93
+ tags.add(
94
+ APIC(
95
+ encoding=3,
96
+ mime="image/jpeg",
97
+ type=3,
98
+ desc="Cover",
99
+ data=img,
100
+ )
101
+ )
102
+ except Exception:
103
+ pass
104
+ audio.save(v2_version=3)
105
+
106
+ # ------------- CORE -------------
107
+ def download_spotify(url: str) -> Path | None:
108
+ if not sp:
109
+ raise RuntimeError("Spotipy credentials not set")
110
+ track = sp.track(url)
111
+ title = track["name"]
112
+ artist = ", ".join(a["name"] for a in track["artists"])
113
+ album = track["album"]["name"]
114
+ date = track["album"].get("release_date", "")
115
+ year = date.split("-")[0] if date else ""
116
+ cover = track["album"]["images"][0]["url"]
117
+ query = f"{title} {artist}"
118
+
119
+ # search YTM
120
+ res = ytm.search(query, filter="songs", limit
121
+