ChandimaPrabath commited on
Commit
18d2a16
·
1 Parent(s): 78ececa

music adaptation

Browse files
Files changed (3) hide show
  1. Instance.py +23 -146
  2. app.py +36 -85
  3. old.app.py +0 -174
Instance.py CHANGED
@@ -8,28 +8,24 @@ from tqdm import tqdm
8
  import logging
9
 
10
  CACHE_DIR = os.getenv("CACHE_DIR")
11
-
12
  download_progress = {}
13
 
14
  class Instance:
15
- def __init__(self, id, url, cache_dir, token, repo, load_balancer_api, max_retries=20, initial_delay=1):
16
- self.version = "0.0.0.1 Alpha"
17
  self.id = id
18
  self.url = url
19
  self.CACHE_DIR = cache_dir
20
  self.TOKEN = token
21
  self.REPO = repo
22
- self.FILM_STORE = {}
23
- self.TV_STORE = {}
24
  self.download_threads = {}
25
  self.file_structure = None
26
  self.load_balancer_api = load_balancer_api
27
- self.max_retries = max_retries
28
  self.initial_delay = initial_delay
29
  self.last_report_time = time.time()
30
  self.re_register_event = Event()
31
 
32
- # Ensure CACHE_DIR exists
33
  if not os.path.exists(self.CACHE_DIR):
34
  os.makedirs(self.CACHE_DIR)
35
 
@@ -39,31 +35,27 @@ class Instance:
39
  registration_thread.daemon = True
40
  registration_thread.start()
41
 
42
- # Start the thread to re-index every 2 minutes
43
  indexer_thread = Thread(target=self.get_file_structure_periodically)
44
  indexer_thread.daemon = True
45
  indexer_thread.start()
46
 
47
  def reload_file_structure(self):
48
- """Runs the indexer and loads the file structure from INDEX_FILE."""
49
  self.file_structure = self.load_balancer_api.get_file_structure()
50
  logging.info("File structure reloaded successfully.")
51
 
52
  def get_file_structure_periodically(self):
53
- """Periodically reruns the indexer and reloads the file structure."""
54
  while True:
55
- time.sleep(300) # Wait for 5 minutes
56
  logging.info("Re-running indexer and reloading file structure.")
57
  self.reload_file_structure()
58
 
59
  def compile_report(self):
60
- self.last_report_time = time.time() # Update the last report time
61
  cache_size = self.get_cache_size()
62
  report = {
63
  "instance_id": self.id,
64
  "instance_url": self.url,
65
- "film_store": self.FILM_STORE,
66
- "tv_store": self.TV_STORE,
67
  "cache_size": cache_size
68
  }
69
  return report
@@ -77,11 +69,11 @@ class Instance:
77
 
78
  def monitor_registration(self):
79
  while True:
80
- if time.time() - self.last_report_time > 60: # Check if 1 minute has passed
81
  logging.info('1 minute passed since last report. Re-registering...')
82
  self.register_to_load_balancer()
83
- self.last_report_time = time.time() # Reset the last report time
84
- time.sleep(30) # Check every 30 seconds
85
 
86
  def get_cache_size(self):
87
  total_size = 0
@@ -98,19 +90,7 @@ class Instance:
98
  return json.load(json_file)
99
  return {}
100
 
101
- def download_film(self, file_url, token, cache_path, film_id, title, chunk_size=100 * 1024 * 1024):
102
- """
103
- Downloads a file from the specified URL and saves it to the cache path.
104
- Tracks the download progress.
105
-
106
- Args:
107
- file_url (str): The URL of the file to download.
108
- token (str): The authorization token for the request.
109
- cache_path (str): The path to save the downloaded file.
110
- film_id (str): Unique identifier for the film download.
111
- title (str): The title of the film.
112
- chunk_size (int): Size of each chunk to download.
113
- """
114
  print(f"Downloading file from URL: {file_url} to {cache_path}")
115
  headers = {'Authorization': f'Bearer {token}'}
116
  try:
@@ -118,39 +98,30 @@ class Instance:
118
  response.raise_for_status()
119
 
120
  total_size = int(response.headers.get('content-length', 0))
121
- download_progress[film_id] = {"total": total_size, "downloaded": 0, "status": "Downloading", "start_time": time.time()}
122
 
123
  os.makedirs(os.path.dirname(cache_path), exist_ok=True)
124
  with open(cache_path, 'wb') as file, tqdm(total=total_size, unit='B', unit_scale=True, desc=cache_path) as pbar:
125
  for data in response.iter_content(chunk_size=chunk_size):
126
  file.write(data)
127
  pbar.update(len(data))
128
- download_progress[film_id]["downloaded"] += len(data)
129
 
130
  print(f'File cached to {cache_path} successfully.')
131
- self.FILM_STORE[title] = cache_path
132
- download_progress[film_id]["status"] = "Completed"
133
  except RequestException as e:
134
  print(f"Error downloading file: {e}")
135
- download_progress[film_id]["status"] = "Failed"
136
  except IOError as e:
137
  print(f"Error writing file {cache_path}: {e}")
138
- download_progress[film_id]["status"] = "Failed"
139
  finally:
140
- if download_progress[film_id]["status"] != "Downloading":
141
- download_progress[film_id]["end_time"] = time.time()
142
 
143
  @staticmethod
144
  def get_download_progress(id):
145
- """
146
- Gets the download progress for a specific film.
147
-
148
- Args:
149
- film_id (str): The unique identifier for the film download.
150
-
151
- Returns:
152
- dict: A dictionary containing the total size, downloaded size, progress percentage, status, and ETA.
153
- """
154
  if id in download_progress:
155
  total = download_progress[id]["total"]
156
  downloaded = download_progress[id]["downloaded"]
@@ -168,112 +139,21 @@ class Instance:
168
  return {"total": total, "downloaded": downloaded, "progress": progress, "status": status, "eta": eta}
169
  return {"total": 0, "downloaded": 0, "progress": 0, "status": "Not Found", "eta": None}
170
 
171
-
172
-
173
- def download_episode(self, file_url, token, cache_path, episode_id, title, chunk_size=100 * 1024 * 1024):
174
- """
175
- Downloads a file from the specified URL and saves it to the cache path.
176
- Tracks the download progress.
177
-
178
- Args:
179
- file_url (str): The URL of the file to download.
180
- token (str): The authorization token for the request.
181
- cache_path (str): The path to save the downloaded file.
182
- episode_id (str): Unique identifier for the film download.
183
- title (str): The title of the film.
184
- chunk_size (int): Size of each chunk to download.
185
- """
186
- print(f"Downloading file from URL: {file_url} to {cache_path}")
187
- headers = {'Authorization': f'Bearer {token}'}
188
- try:
189
- response = requests.get(file_url, headers=headers, stream=True)
190
- response.raise_for_status()
191
-
192
- total_size = int(response.headers.get('content-length', 0))
193
- download_progress[episode_id] = {"total": total_size, "downloaded": 0, "status": "Downloading", "start_time": time.time()}
194
-
195
- os.makedirs(os.path.dirname(cache_path), exist_ok=True)
196
- with open(cache_path, 'wb') as file, tqdm(total=total_size, unit='B', unit_scale=True, desc=cache_path) as pbar:
197
- for data in response.iter_content(chunk_size=chunk_size):
198
- file.write(data)
199
- pbar.update(len(data))
200
- download_progress[episode_id]["downloaded"] += len(data)
201
-
202
- print(f'File cached to {cache_path} successfully.')
203
- self.update_tv_store(title, cache_path)
204
- download_progress[episode_id]["status"] = "Completed"
205
- except RequestException as e:
206
- print(f"Error downloading file: {e}")
207
- download_progress[episode_id]["status"] = "Failed"
208
- except IOError as e:
209
- print(f"Error writing file {cache_path}: {e}")
210
- download_progress[episode_id]["status"] = "Failed"
211
- finally:
212
- if download_progress[episode_id]["status"] != "Downloading":
213
- download_progress[episode_id]["end_time"] = time.time()
214
-
215
- def update_tv_store(self, title, cache_path):
216
- """
217
- Updates the TV store JSON with the new file, organizing by title, season, and episode.
218
-
219
- Args:
220
- title (str): The title of the TV show.
221
- cache_path (str): The local path where the file is saved.
222
- """
223
-
224
- # Extract season and episode information from the cache_path
225
- season_part = os.path.basename(os.path.dirname(cache_path)) # Extracts 'Season 1'
226
- episode_part = os.path.basename(cache_path) # Extracts 'Grand Blue Dreaming - S01E01 - Deep Blue HDTV-720p.mp4'
227
-
228
- # Create the structure if not already present
229
- if title not in self.TV_STORE:
230
- self.TV_STORE[title] = {}
231
-
232
- if season_part not in self.TV_STORE[title]:
233
- self.TV_STORE[title][season_part] = {}
234
-
235
- # Assuming episode_part is unique for each episode within a season
236
- self.TV_STORE[title][season_part][episode_part] = cache_path
237
-
238
- print(f'TV store updated with {title}, {season_part}, {episode_part}.')
239
-
240
-
241
  def load_json(self, file_path):
242
- """Load JSON data from a file."""
243
  with open(file_path, 'r') as file:
244
  return json.load(file)
245
 
246
- def find_movie_path(self, title):
247
- """Find the path of the movie in the JSON data based on the title."""
248
  for directory in self.file_structure:
249
- if directory['type'] == 'directory' and directory['path'] == 'films':
250
  for sub_directory in directory['contents']:
251
  if sub_directory['type'] == 'directory':
252
  for item in sub_directory['contents']:
253
  if item['type'] == 'file' and title.lower() in item['path'].lower():
254
  return item['path']
255
  return None
256
-
257
- def find_tv_path(self, title):
258
- """Find the path of the TV show in the JSON data based on the title."""
259
- for directory in self.file_structure:
260
- if directory['type'] == 'directory' and directory['path'] == 'tv':
261
- for sub_directory in directory['contents']:
262
- if sub_directory['type'] == 'directory' and title.lower() in sub_directory['path'].lower():
263
- return sub_directory['path']
264
- return None
265
-
266
- def get_tv_structure(self, title):
267
- """Find the path of the TV show in the JSON data based on the title."""
268
- for directory in self.file_structure:
269
- if directory['type'] == 'directory' and directory['path'] == 'tv':
270
- for sub_directory in directory['contents']:
271
- if sub_directory['type'] == 'directory' and title.lower() in sub_directory['path'].lower():
272
- return sub_directory
273
- return None
274
-
275
- def get_film_id(self, title):
276
- """Generate a film ID based on the title."""
277
  return title.replace(" ", "_").lower()
278
 
279
  def bytes_to_human_readable(self, num, suffix="B"):
@@ -283,9 +163,6 @@ class Instance:
283
  num /= 1024.0
284
  return f"{num:.1f} Y{suffix}"
285
 
286
- def encode_episodeid(self, title, season, episode):
287
- return f"{title}_{season}_{episode}"
288
-
289
  def register_to_load_balancer(self):
290
  retries = 0
291
  delay = self.initial_delay
@@ -304,4 +181,4 @@ class Instance:
304
  retries += 1
305
  logging.warning(f'Attempt {retries} to register instance {self.id} failed. Retrying in {delay} seconds...')
306
  time.sleep(delay)
307
- delay = min(delay * 2, max_delay) # Exponential backoff with maximum delay
 
8
  import logging
9
 
10
  CACHE_DIR = os.getenv("CACHE_DIR")
 
11
  download_progress = {}
12
 
13
  class Instance:
14
+ def __init__(self, id, url, cache_dir, token, repo, load_balancer_api, initial_delay=1):
15
+ self.version = "0.0.1 Alpha"
16
  self.id = id
17
  self.url = url
18
  self.CACHE_DIR = cache_dir
19
  self.TOKEN = token
20
  self.REPO = repo
21
+ self.MUSIC_STORE = {}
 
22
  self.download_threads = {}
23
  self.file_structure = None
24
  self.load_balancer_api = load_balancer_api
 
25
  self.initial_delay = initial_delay
26
  self.last_report_time = time.time()
27
  self.re_register_event = Event()
28
 
 
29
  if not os.path.exists(self.CACHE_DIR):
30
  os.makedirs(self.CACHE_DIR)
31
 
 
35
  registration_thread.daemon = True
36
  registration_thread.start()
37
 
 
38
  indexer_thread = Thread(target=self.get_file_structure_periodically)
39
  indexer_thread.daemon = True
40
  indexer_thread.start()
41
 
42
  def reload_file_structure(self):
 
43
  self.file_structure = self.load_balancer_api.get_file_structure()
44
  logging.info("File structure reloaded successfully.")
45
 
46
  def get_file_structure_periodically(self):
 
47
  while True:
48
+ time.sleep(300) # Every 5 minutes
49
  logging.info("Re-running indexer and reloading file structure.")
50
  self.reload_file_structure()
51
 
52
  def compile_report(self):
53
+ self.last_report_time = time.time()
54
  cache_size = self.get_cache_size()
55
  report = {
56
  "instance_id": self.id,
57
  "instance_url": self.url,
58
+ "music_store": self.MUSIC_STORE,
 
59
  "cache_size": cache_size
60
  }
61
  return report
 
69
 
70
  def monitor_registration(self):
71
  while True:
72
+ if time.time() - self.last_report_time > 60:
73
  logging.info('1 minute passed since last report. Re-registering...')
74
  self.register_to_load_balancer()
75
+ self.last_report_time = time.time()
76
+ time.sleep(30)
77
 
78
  def get_cache_size(self):
79
  total_size = 0
 
90
  return json.load(json_file)
91
  return {}
92
 
93
+ def download_music(self, file_url, token, cache_path, music_id, title, chunk_size=100 * 1024 * 1024):
 
 
 
 
 
 
 
 
 
 
 
 
94
  print(f"Downloading file from URL: {file_url} to {cache_path}")
95
  headers = {'Authorization': f'Bearer {token}'}
96
  try:
 
98
  response.raise_for_status()
99
 
100
  total_size = int(response.headers.get('content-length', 0))
101
+ download_progress[music_id] = {"total": total_size, "downloaded": 0, "status": "Downloading", "start_time": time.time()}
102
 
103
  os.makedirs(os.path.dirname(cache_path), exist_ok=True)
104
  with open(cache_path, 'wb') as file, tqdm(total=total_size, unit='B', unit_scale=True, desc=cache_path) as pbar:
105
  for data in response.iter_content(chunk_size=chunk_size):
106
  file.write(data)
107
  pbar.update(len(data))
108
+ download_progress[music_id]["downloaded"] += len(data)
109
 
110
  print(f'File cached to {cache_path} successfully.')
111
+ self.MUSIC_STORE[title] = cache_path
112
+ download_progress[music_id]["status"] = "Completed"
113
  except RequestException as e:
114
  print(f"Error downloading file: {e}")
115
+ download_progress[music_id]["status"] = "Failed"
116
  except IOError as e:
117
  print(f"Error writing file {cache_path}: {e}")
118
+ download_progress[music_id]["status"] = "Failed"
119
  finally:
120
+ if download_progress[music_id]["status"] != "Downloading":
121
+ download_progress[music_id]["end_time"] = time.time()
122
 
123
  @staticmethod
124
  def get_download_progress(id):
 
 
 
 
 
 
 
 
 
125
  if id in download_progress:
126
  total = download_progress[id]["total"]
127
  downloaded = download_progress[id]["downloaded"]
 
139
  return {"total": total, "downloaded": downloaded, "progress": progress, "status": status, "eta": eta}
140
  return {"total": 0, "downloaded": 0, "progress": 0, "status": "Not Found", "eta": None}
141
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
142
  def load_json(self, file_path):
 
143
  with open(file_path, 'r') as file:
144
  return json.load(file)
145
 
146
+ def find_music_path(self, title):
 
147
  for directory in self.file_structure:
148
+ if directory['type'] == 'directory' and directory['path'] == 'music':
149
  for sub_directory in directory['contents']:
150
  if sub_directory['type'] == 'directory':
151
  for item in sub_directory['contents']:
152
  if item['type'] == 'file' and title.lower() in item['path'].lower():
153
  return item['path']
154
  return None
155
+
156
+ def get_music_id(self, title):
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
157
  return title.replace(" ", "_").lower()
158
 
159
  def bytes_to_human_readable(self, num, suffix="B"):
 
163
  num /= 1024.0
164
  return f"{num:.1f} Y{suffix}"
165
 
 
 
 
166
  def register_to_load_balancer(self):
167
  retries = 0
168
  delay = self.initial_delay
 
181
  retries += 1
182
  logging.warning(f'Attempt {retries} to register instance {self.id} failed. Retrying in {delay} seconds...')
183
  time.sleep(delay)
184
+ delay = min(delay * 2, max_delay)
app.py CHANGED
@@ -5,7 +5,6 @@ from threading import Thread
5
  from Instance import Instance
6
  from api import LoadBalancerAPI
7
  import os
8
- import re
9
  import aiofiles
10
 
11
  # Constants and Configuration
@@ -21,10 +20,16 @@ instance = Instance(id=ID, url=URL, cache_dir=CACHE_DIR, token=TOKEN, repo=REPO,
21
 
22
  app = FastAPI()
23
 
24
- async def serve_video(file_path: str, request: Request):
25
- """Serve video file with support for range requests."""
 
 
 
 
 
 
26
  if not os.path.isfile(file_path):
27
- raise HTTPException(status_code=404, detail="Video file not found")
28
 
29
  file_size = os.path.getsize(file_path)
30
  range_header = request.headers.get('range', None)
@@ -34,8 +39,8 @@ async def serve_video(file_path: str, request: Request):
34
  if mime_type is None:
35
  mime_type = 'application/octet-stream' # Fallback MIME type
36
 
 
37
  if range_header:
38
- # Simple parsing for range values
39
  range_specifier = range_header.replace('bytes=', '').strip()
40
  start, end = (None, None)
41
 
@@ -44,7 +49,7 @@ async def serve_video(file_path: str, request: Request):
44
  start = int(start_str)
45
  end = int(end_str) if end_str else file_size - 1
46
 
47
- # Validate range
48
  if start is None or start >= file_size or (end is not None and end >= file_size) or (end is not None and start > end):
49
  raise HTTPException(status_code=416, detail="Requested range not satisfiable")
50
 
@@ -65,114 +70,60 @@ async def serve_video(file_path: str, request: Request):
65
  read_size = min(chunk_size, remaining)
66
  chunk = await f.read(read_size)
67
 
68
- if not chunk: # EOF
69
  break
70
 
71
  data.extend(chunk)
72
  start += read_size
73
 
74
- return Response(content=bytes(data), status_code=206, headers=headers) # Convert bytearray to bytes
75
 
76
- # Fallback for serving the whole file if no range requested
77
  return FileResponse(file_path, media_type=mime_type)
78
 
79
-
80
  @app.get("/")
81
  async def index():
82
  return instance.version
83
 
84
  @app.get("/api/get/report")
85
  async def get_report():
86
- report=instance.compile_report()
87
  return JSONResponse(report)
88
 
89
- @app.get('/api/get/tv/store')
90
- async def get_tv_store_api():
91
- """Endpoint to get the TV store JSON."""
92
- return JSONResponse(instance.TV_STORE)
93
-
94
- @app.get('/api/get/film/store')
95
- async def get_film_store_api():
96
- """Endpoint to get the TV store JSON."""
97
- return JSONResponse(instance.FILM_STORE)
98
 
99
- @app.get("/api/get/film/{title}")
100
- async def get_movie_api(request: Request, title: str):
101
- """Endpoint to get the movie by title with support for range requests."""
102
  if not title:
103
  raise HTTPException(status_code=400, detail="Title parameter is required")
104
 
105
- # Check if the film is already cached
106
- if title in instance.FILM_STORE:
107
- cache_path = instance.FILM_STORE[title]
108
  if os.path.exists(cache_path):
109
- return await serve_video(cache_path, request)
110
-
111
- movie_path = instance.find_movie_path(title)
112
-
113
- if not movie_path:
114
- raise HTTPException(status_code=404, detail="Movie not found")
115
-
116
- cache_path = os.path.join(CACHE_DIR, movie_path)
117
- file_url = f"https://huggingface.co/{REPO}/resolve/main/{movie_path}"
118
- film_id = instance.get_film_id(title)
119
-
120
- # Start the download in a separate thread if not already downloading
121
- if film_id not in instance.download_threads or not instance.download_threads[film_id].is_alive():
122
- thread = Thread(target=instance.download_film, args=(file_url, TOKEN, cache_path, film_id, title))
123
- instance.download_threads[film_id] = thread
124
- thread.start()
125
 
126
- return JSONResponse({"status": "Download started", "film_id": film_id})
127
-
128
- @app.get("/api/get/tv/{title}/{season}/{episode}")
129
- async def get_tv_show_api(request: Request, title: str, season: str, episode: str):
130
- """Endpoint to get the TV show by title, season, and episode."""
131
- if not title or not season or not episode:
132
- raise HTTPException(status_code=400, detail="Title, season, and episode parameters are required")
133
-
134
- # Check if the episode is already cached
135
- if title in instance.TV_STORE and season in instance.TV_STORE[title]:
136
- for ep in instance.TV_STORE[title][season]:
137
- if episode in ep:
138
- cache_path = instance.TV_STORE[title][season][ep]
139
- if os.path.exists(cache_path):
140
- return await serve_video(cache_path, request)
141
-
142
- tv_path = instance.find_tv_path(title)
143
 
144
- if not tv_path:
145
- raise HTTPException(status_code=404, detail="TV show not found")
146
-
147
- episode_path = None
148
- for directory in instance.file_structure:
149
- if directory['type'] == 'directory' and directory['path'] == 'tv':
150
- for sub_directory in directory['contents']:
151
- if sub_directory['type'] == 'directory' and title.lower() in sub_directory['path'].lower():
152
- for season_dir in sub_directory['contents']:
153
- if season_dir['type'] == 'directory' and season in season_dir['path']:
154
- for episode_file in season_dir['contents']:
155
- if episode_file['type'] == 'file' and episode in episode_file['path']:
156
- episode_path = episode_file['path']
157
- break
158
-
159
- if not episode_path:
160
- raise HTTPException(status_code=404, detail="Episode not found")
161
 
162
- cache_path = os.path.join(CACHE_DIR, episode_path)
163
- file_url = f"https://huggingface.co/{REPO}/resolve/main/{episode_path}"
164
- episode_id = instance.encode_episodeid(title, season, episode)
165
 
166
- # Start the download in a separate thread if not already downloading
167
- if episode_id not in instance.download_threads or not instance.download_threads[episode_id].is_alive():
168
- thread = Thread(target=instance.download_episode, args=(file_url, TOKEN, cache_path, episode_id, title))
169
- instance.download_threads[episode_id] = thread
170
  thread.start()
171
 
172
- return JSONResponse({"status": "Download started", "episode_id": episode_id})
173
 
174
  @app.get("/api/get/progress/{id}")
175
  async def get_progress_api(id: str):
176
- """Endpoint to get the download progress of a movie or TV show episode."""
177
  progress = instance.get_download_progress(id)
178
  return JSONResponse({"id": id, "progress": progress})
 
5
  from Instance import Instance
6
  from api import LoadBalancerAPI
7
  import os
 
8
  import aiofiles
9
 
10
  # Constants and Configuration
 
20
 
21
  app = FastAPI()
22
 
23
+ from fastapi import HTTPException, Request
24
+ from fastapi.responses import Response, FileResponse
25
+ import os
26
+ import mimetypes
27
+ import aiofiles
28
+
29
+ async def serve_media(file_path: str, request: Request):
30
+ """Serve any media file with support for range requests."""
31
  if not os.path.isfile(file_path):
32
+ raise HTTPException(status_code=404, detail="Media file not found")
33
 
34
  file_size = os.path.getsize(file_path)
35
  range_header = request.headers.get('range', None)
 
39
  if mime_type is None:
40
  mime_type = 'application/octet-stream' # Fallback MIME type
41
 
42
+ # Handle range requests
43
  if range_header:
 
44
  range_specifier = range_header.replace('bytes=', '').strip()
45
  start, end = (None, None)
46
 
 
49
  start = int(start_str)
50
  end = int(end_str) if end_str else file_size - 1
51
 
52
+ # Validate the range
53
  if start is None or start >= file_size or (end is not None and end >= file_size) or (end is not None and start > end):
54
  raise HTTPException(status_code=416, detail="Requested range not satisfiable")
55
 
 
70
  read_size = min(chunk_size, remaining)
71
  chunk = await f.read(read_size)
72
 
73
+ if not chunk: # End of file
74
  break
75
 
76
  data.extend(chunk)
77
  start += read_size
78
 
79
+ return Response(content=bytes(data), status_code=206, headers=headers)
80
 
81
+ # Fallback for serving the entire file if no range requested
82
  return FileResponse(file_path, media_type=mime_type)
83
 
 
84
  @app.get("/")
85
  async def index():
86
  return instance.version
87
 
88
  @app.get("/api/get/report")
89
  async def get_report():
90
+ report = instance.compile_report()
91
  return JSONResponse(report)
92
 
93
+ @app.get('/api/get/music/store')
94
+ async def get_media_store_api():
95
+ """Endpoint to get the music store JSON."""
96
+ return JSONResponse(instance.MUSIC_STORE)
 
 
 
 
 
97
 
98
+ @app.get("/api/get/music/{title}")
99
+ async def get_media_api(request: Request, title: str):
100
+ """Endpoint to get the music file (audio or video) by title with support for range requests."""
101
  if not title:
102
  raise HTTPException(status_code=400, detail="Title parameter is required")
103
 
104
+ if title in instance.MUSIC_STORE:
105
+ cache_path = instance.MUSIC_STORE[title]
 
106
  if os.path.exists(cache_path):
107
+ return await serve_media(cache_path, request)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
108
 
109
+ media_path = instance.find_music_path(title)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
110
 
111
+ if not media_path:
112
+ raise HTTPException(status_code=404, detail="Media not found")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
113
 
114
+ cache_path = os.path.join(CACHE_DIR, media_path)
115
+ file_url = f"https://huggingface.co/{REPO}/resolve/main/{media_path}"
116
+ media_id = instance.get_music_id(title)
117
 
118
+ if media_id not in instance.download_threads or not instance.download_threads[media_id].is_alive():
119
+ thread = Thread(target=instance.download_music, args=(file_url, TOKEN, cache_path, media_id, title))
120
+ instance.download_threads[media_id] = thread
 
121
  thread.start()
122
 
123
+ return JSONResponse({"status": "Download started", "music_id": media_id})
124
 
125
  @app.get("/api/get/progress/{id}")
126
  async def get_progress_api(id: str):
127
+ """Endpoint to get the download progress of a media file."""
128
  progress = instance.get_download_progress(id)
129
  return JSONResponse({"id": id, "progress": progress})
old.app.py DELETED
@@ -1,174 +0,0 @@
1
- from flask import Flask, jsonify, request, send_from_directory
2
- from flask_cors import CORS
3
- import os
4
- import json
5
- from threading import Thread
6
- import urllib.parse
7
- from Instance import Instance
8
- from api import LoadBalancerAPI
9
-
10
- app = Flask(__name__)
11
- CORS(app)
12
-
13
- # Constants and Configuration
14
- CACHE_DIR = os.getenv("CACHE_DIR")
15
- INDEX_FILE = os.getenv("INDEX_FILE")
16
- TOKEN = os.getenv("TOKEN")
17
- REPO = os.getenv("REPO")
18
- ID = os.getenv("ID")
19
- URL = os.getenv("URL")
20
- LOAD_BALANCER_URL = os.getenv("LOAD_BALANCER_URL")
21
-
22
- load_balancer_api = LoadBalancerAPI(base_url=LOAD_BALANCER_URL)
23
- instance = Instance(id=ID, url=URL, cache_dir=CACHE_DIR, index_file=INDEX_FILE, token=TOKEN, repo=REPO, load_balancer_api=load_balancer_api)
24
-
25
- # API Endpoints
26
- @app.route('/api/film/<title>', methods=['GET'])
27
- def get_movie_api(title):
28
- """Endpoint to get the movie by title."""
29
- if not title:
30
- return jsonify({"error": "Title parameter is required"}), 400
31
-
32
- # Load the film store JSON
33
- with open(instance.FILM_STORE_JSON_PATH, 'r') as json_file:
34
- film_store_data = json.load(json_file)
35
-
36
- # Check if the film is already cached
37
- if title in film_store_data:
38
- cache_path = film_store_data[title]
39
- if os.path.exists(cache_path):
40
- return send_from_directory(os.path.dirname(cache_path), os.path.basename(cache_path))
41
-
42
- movie_path = instance.find_movie_path(title)
43
-
44
- if not movie_path:
45
- return jsonify({"error": "Movie not found"}), 404
46
-
47
- cache_path = os.path.join(CACHE_DIR, movie_path)
48
- file_url = f"https://huggingface.co/{REPO}/resolve/main/{movie_path}"
49
- proxies = instance.get_system_proxies()
50
- film_id = instance.get_film_id(title)
51
-
52
- # Start the download in a separate thread if not already downloading
53
- if film_id not in instance.download_threads or not instance.download_threads[film_id].is_alive():
54
- thread = Thread(target=instance.download_film, args=(file_url, TOKEN, cache_path, proxies, film_id, title))
55
- instance.download_threads[film_id] = thread
56
- thread.start()
57
-
58
- return jsonify({"status": "Download started", "film_id": film_id})
59
-
60
- @app.route('/api/tv/<title>/<season>/<episode>', methods=['GET'])
61
- def get_tv_show_api(title, season, episode):
62
- """Endpoint to get the TV show by title, season, and episode."""
63
- if not title or not season or not episode:
64
- return jsonify({"error": "Title, season, and episode parameters are required"}), 400
65
-
66
- # Load the TV store JSON
67
- with open(instance.TV_STORE_JSON_PATH, 'r') as json_file:
68
- tv_store_data = json.load(json_file)
69
-
70
- # Check if the episode is already cached
71
- if title in tv_store_data and season in tv_store_data[title]:
72
- for ep in tv_store_data[title][season]:
73
- if episode in ep:
74
- cache_path = tv_store_data[title][season][ep]
75
- print(cache_path)
76
- if os.path.exists(cache_path):
77
- return send_from_directory(os.path.dirname(cache_path), os.path.basename(cache_path))
78
-
79
- tv_path = instance.find_tv_path(title)
80
-
81
- if not tv_path:
82
- return jsonify({"error": "TV show not found"}), 404
83
-
84
- episode_path = None
85
- for directory in instance.file_structure:
86
- if directory['type'] == 'directory' and directory['path'] == 'tv':
87
- for sub_directory in directory['contents']:
88
- if sub_directory['type'] == 'directory' and title.lower() in sub_directory['path'].lower():
89
- for season_dir in sub_directory['contents']:
90
- if season_dir['type'] == 'directory' and season in season_dir['path']:
91
- for episode_file in season_dir['contents']:
92
- if episode_file['type'] == 'file' and episode in episode_file['path']:
93
- episode_path = episode_file['path']
94
- break
95
-
96
- if not episode_path:
97
- return jsonify({"error": "Episode not found"}), 404
98
-
99
- cache_path = os.path.join(CACHE_DIR, episode_path)
100
- file_url = f"https://huggingface.co/{REPO}/resolve/main/{episode_path}"
101
- proxies = instance.get_system_proxies()
102
- episode_id = instance.encode_episodeid(title, season, episode)
103
-
104
- # Start the download in a separate thread if not already downloading
105
- if episode_id not in instance.download_threads or not instance.download_threads[episode_id].is_alive():
106
- thread = Thread(target=instance.download_episode, args=(file_url, TOKEN, cache_path, proxies, episode_id, title))
107
- instance.download_threads[episode_id] = thread
108
- thread.start()
109
-
110
- return jsonify({"status": "Download started", "episode_id": episode_id})
111
-
112
- @app.route('/api/progress/<id>', methods=['GET'])
113
- def get_progress_api(id):
114
- """Endpoint to get the download progress of a movie or TV show episode."""
115
- progress = instance.get_download_progress(id)
116
- return jsonify({"id": id, "progress": progress})
117
-
118
- @app.route('/api/cache/size', methods=['GET'])
119
- def get_cache_size_api():
120
- total_size = 0
121
- for dirpath, dirnames, filenames in os.walk(CACHE_DIR):
122
- for f in filenames:
123
- fp = os.path.join(dirpath, f)
124
- total_size += os.path.getsize(fp)
125
- readable_size = instance.bytes_to_human_readable(total_size)
126
- return jsonify({"cache_size": readable_size})
127
-
128
- @app.route('/api/cache/clear', methods=['POST'])
129
- def clear_cache_api():
130
- for dirpath, dirnames, filenames in os.walk(CACHE_DIR):
131
- for f in filenames:
132
- fp = os.path.join(dirpath, f)
133
- os.remove(fp)
134
- return jsonify({"status": "Cache cleared"})
135
-
136
- @app.route('/api/tv/store', methods=['GET'])
137
- def get_tv_store_api():
138
- """Endpoint to get the TV store JSON."""
139
- if os.path.exists(instance.TV_STORE_JSON_PATH):
140
- with open(instance.TV_STORE_JSON_PATH, 'r') as json_file:
141
- tv_store_data = json.load(json_file)
142
- return jsonify(tv_store_data)
143
- return jsonify({}), 404
144
-
145
- @app.route('/api/film/store', methods=['GET'])
146
- def get_film_store_api():
147
- """Endpoint to get the film store JSON."""
148
- if os.path.exists(instance.FILM_STORE_JSON_PATH):
149
- with open(instance.FILM_STORE_JSON_PATH, 'r') as json_file:
150
- tv_store_data = json.load(json_file)
151
- return jsonify(tv_store_data)
152
- return jsonify({}), 404
153
-
154
- @app.route("/api/film/all")
155
- def get_all_films_api():
156
- return instance.get_all_films()
157
-
158
- @app.route("/api/tv/all")
159
- def get_all_tvshows_api():
160
- return instance.get_all_tv_shows()
161
-
162
- @app.route("/api/get/report",methods=["GET"])
163
- def get_report():
164
- report=instance.compile_report()
165
- return jsonify(report)
166
-
167
- # Routes
168
- @app.route('/')
169
- def index():
170
- return jsonify(instance.version)
171
-
172
- # Main entry point
173
- if __name__ == "__main__":
174
- app.run(debug=True, host="0.0.0.0", port=7860)