tecuts commited on
Commit
a4eb804
·
verified ·
1 Parent(s): e84fc15

Create app.py

Browse files
Files changed (1) hide show
  1. app.py +253 -0
app.py ADDED
@@ -0,0 +1,253 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import shutil
2
+ from fastapi import FastAPI, HTTPException, Request
3
+ from deezspot.deezloader import DeeLogin
4
+ import requests
5
+ import os
6
+ import logging
7
+ from typing import Optional
8
+ from fastapi.staticfiles import StaticFiles
9
+ from dotenv import load_dotenv
10
+ from pydantic import BaseModel
11
+ from urllib.parse import quote
12
+ import uuid
13
+ import gc
14
+
15
+
16
+ # Set up logging
17
+ logging.basicConfig(level=logging.INFO)
18
+ logger = logging.getLogger(__name__)
19
+
20
+ app = FastAPI(title="Deezer API")
21
+ # Load environment variables
22
+ load_dotenv()
23
+
24
+ # Mount a static files directory to serve downloaded files
25
+
26
+ os.makedirs("downloads", exist_ok=True)
27
+ app.mount("/downloads", StaticFiles(directory="downloads"), name="downloads")
28
+
29
+ # Deezer API base URL
30
+ DEEZER_API_URL = "https://api.deezer.com"
31
+
32
+ # Deezer ARL token (required for deezspot downloads)
33
+ ARL_TOKEN = os.getenv('ARL')
34
+
35
+
36
+
37
+
38
+ # 定义请求体模型
39
+ class DownloadRequest(BaseModel):
40
+ url: str
41
+ quality: str
42
+
43
+
44
+ def cleanup_dir(dir_path: Path):
45
+ """Background task to clean up directory"""
46
+ try:
47
+ shutil.rmtree(dir_path)
48
+ logger.info(f"Cleaned up directory: {dir_path}")
49
+ except Exception as e:
50
+ logger.error(f"Error cleaning up {dir_path}: {e}")
51
+
52
+
53
+ @app.get("/")
54
+ def read_root():
55
+ return {"message": "running"}
56
+
57
+
58
+ # Download a track and return a download URL
59
+ class deezloadRequest(BaseModel):
60
+ url: str
61
+ quality: str
62
+ arl: str
63
+
64
+
65
+ def convert_deezer_short_link_async(short_link: str) -> str:
66
+ try:
67
+ response = requests.get(short_link, allow_redirects=True)
68
+ return response.url
69
+ except requests.RequestException as e:
70
+ print(f"An error occurred: {e}")
71
+ return ""
72
+
73
+
74
+ # Helper function to get track info
75
+ def get_track_info(track_id: str):
76
+ try:
77
+ response = requests.get(f"{DEEZER_API_URL}/track/{track_id}")
78
+ if response.status_code != 200:
79
+ raise HTTPException(status_code=404, detail="Track not found")
80
+ return response.json()
81
+ except requests.exceptions.RequestException as e:
82
+ logger.error(f"Network error fetching track metadata: {e}")
83
+ raise HTTPException(status_code=500, detail=str(e))
84
+ except Exception as e:
85
+ logger.error(f"Error fetching track metadata: {e}")
86
+ raise HTTPException(status_code=500, detail=str(e))
87
+
88
+
89
+ # Fetch track metadata from Deezer API
90
+ @app.get("/track/{track_id}")
91
+ def get_d_track(track_id: str):
92
+ return get_track_info(track_id)
93
+
94
+
95
+ # Download a track and return a download URL
96
+ @app.post("/download/track")
97
+ def download_d_track(request: Request, deezload_request: deezloadRequest):
98
+ try:
99
+ if deezload_request.arl is None or deezload_request.arl.strip() == "":
100
+ ARL = ARL_TOKEN
101
+ else:
102
+ ARL = deezload_request.arl
103
+ logger.info(f'arl: {ARL}')
104
+ url = deezload_request.url
105
+ if 'dzr.page' in url or 'deezer.page' in url or 'link.deezer' in url:
106
+ url = convert_deezer_short_link_async(url)
107
+ quality = deezload_request.quality
108
+ dl = DeeLogin(arl=ARL)
109
+
110
+ if quality not in ["MP3_320", "MP3_128", "FLAC"]:
111
+ raise HTTPException(status_code=400, detail="Invalid quality specified")
112
+
113
+ # 提取 track_id (假设 url 格式为 https://api.deezer.com/track/{track_id})
114
+ track_id = url.split("/")[-1]
115
+
116
+ # Fetch track info
117
+ track_info = get_track_info(track_id)
118
+ track_link = track_info.get("link")
119
+ if not track_link:
120
+ raise HTTPException(status_code=404, detail="Track link not found")
121
+
122
+ # Sanitize filename
123
+ track_title = track_info.get("title", "track")
124
+ artist_name = track_info.get("artist", {}).get("name", "unknown")
125
+ file_extension = "flac" if quality == "FLAC" else "mp3"
126
+ expected_filename = f"{artist_name} - {track_title}.{file_extension}".replace("/", "_") # Sanitize filename
127
+
128
+ # Clear the downloads directory
129
+ for root, dirs, files in os.walk("downloads"):
130
+ for file in files:
131
+ os.remove(os.path.join(root, file))
132
+ for dir in dirs:
133
+ shutil.rmtree(os.path.join(root, dir))
134
+
135
+ # Download the track using deezspot
136
+ try:
137
+ # 下载文件的代码
138
+ dl.download_trackdee(
139
+ link_track=track_link,
140
+ output_dir="downloads",
141
+ quality_download=quality,
142
+ recursive_quality=False,
143
+ recursive_download=False
144
+ )
145
+ except Exception as e:
146
+ logger.error(f"Error downloading file: {e}")
147
+ raise HTTPException(status_code=500, detail="File download failed")
148
+
149
+ # Recursively search for the file in the downloads directory
150
+ filepath = None
151
+ for root, dirs, files in os.walk("downloads"):
152
+ for file in files:
153
+ if file.endswith(f'.{file_extension}'):
154
+ filepath = os.path.join(root, file)
155
+ break
156
+ if filepath:
157
+ break
158
+
159
+ if not filepath:
160
+ raise HTTPException(status_code=500, detail=f"{file_extension} file not found after download")
161
+ if filepath:
162
+ file_size = os.path.getsize(filepath)
163
+ logger.info(f"Downloaded file size: {file_size} bytes")
164
+
165
+ # Return the download URL
166
+ relative_path = quote(str(os.path.relpath(filepath, "downloads")))
167
+ # Auto-detect base URL from request
168
+ base_url = str(request.base_url).rstrip('/')
169
+ download_url = f"{base_url}/downloads/{relative_path}"
170
+ logger.info(f"Download successful: {download_url}")
171
+ gc.collect()
172
+ return {"download_url": download_url}
173
+ except Exception as e:
174
+ logger.error(f"Error downloading track: {e}")
175
+ raise HTTPException(status_code=500, detail=str(e))
176
+
177
+
178
+ # Pydantic model for album request
179
+ class AlbumRequest(BaseModel):
180
+ album_id: str
181
+
182
+
183
+ # Fetch album data
184
+ @app.post("/z_album")
185
+ def fetch_d_album(request: AlbumRequest):
186
+ album_id = request.album_id
187
+ try:
188
+ response = requests.get(f"{DEEZER_API_URL}/album/{album_id}")
189
+ response.raise_for_status()
190
+ album_data = response.json()
191
+ tracks = album_data.get("tracks", {}).get("data", [])
192
+ result = []
193
+ for track in tracks:
194
+ title = track.get("title")
195
+ link = track.get("link")
196
+ if title and link:
197
+ result.append({
198
+ "title": title,
199
+ "link": link
200
+ })
201
+ return result
202
+ except requests.exceptions.RequestException as e:
203
+ logger.error(f"Network error fetching album: {e}")
204
+ raise HTTPException(status_code=500, detail=str(e))
205
+ except Exception as e:
206
+ logger.error(f"Error fetching album: {e}")
207
+ raise HTTPException(status_code=500, detail=str(e))
208
+
209
+
210
+ # Pydantic model for playlist request
211
+ class PlaylistRequest(BaseModel):
212
+ playlist_id: str
213
+
214
+
215
+ # Fetch playlist data
216
+ @app.post("/z_playlist")
217
+ def fetch_playlist(request: PlaylistRequest):
218
+ playlist_id = request.playlist_id # This is correct in app_pre.py
219
+ try:
220
+ response = requests.get(f"{DEEZER_API_URL}/playlist/{playlist_id}")
221
+ response.raise_for_status()
222
+ playlist_data = response.json()
223
+ tracks = playlist_data.get("tracks", {}).get("data", [])
224
+ result = []
225
+ for track in tracks:
226
+ title = track.get("title")
227
+ link = track.get("link")
228
+ if title and link:
229
+ result.append({
230
+ "title": title,
231
+ "link": link
232
+ })
233
+ return result
234
+ except requests.exceptions.RequestException as e:
235
+ logger.error(f"Network error fetching playlist: {e}") # Fixed error message
236
+ raise HTTPException(status_code=500, detail=str(e))
237
+ except Exception as e:
238
+ logger.error(f"Error fetching playlist: {e}") # Fixed error message
239
+ raise HTTPException(status_code=500, detail=str(e))
240
+
241
+
242
+ # Search tracks using Deezer API
243
+ @app.get("/z_search")
244
+ def search_tracks(query: str, limit: Optional[int] = 10):
245
+ try:
246
+ response = requests.get(f"{DEEZER_API_URL}/search", params={"q": query, "limit": limit})
247
+ return response.json()
248
+ except requests.exceptions.RequestException as e:
249
+ logger.error(f"Network error searching tracks: {e}")
250
+ raise HTTPException(status_code=500, detail=str(e))
251
+ except Exception as e:
252
+ logger.error(f"Error searching tracks: {e}")
253
+ raise HTTPException(status_code=500, detail=str(e))