Commit
·
7d3bb28
1
Parent(s):
888adce
get all films bug fix
Browse files- LoadBalancer.py +76 -75
- TODO.md +2 -4
LoadBalancer.py
CHANGED
|
@@ -30,12 +30,17 @@ class LoadBalancer:
|
|
| 30 |
self.file_structure = None
|
| 31 |
self.previous_file_structure = None # To keep track of previous content
|
| 32 |
|
|
|
|
|
|
|
|
|
|
|
|
|
| 33 |
# Ensure CACHE_DIR exists
|
| 34 |
if not os.path.exists(self.CACHE_DIR):
|
| 35 |
os.makedirs(self.CACHE_DIR)
|
| 36 |
|
| 37 |
-
# Initialize file structure and start prefetching
|
| 38 |
self.file_structure = indexer()
|
|
|
|
| 39 |
self.start_prefetching()
|
| 40 |
|
| 41 |
# Start polling and file checking in separate threads
|
|
@@ -46,11 +51,51 @@ class LoadBalancer:
|
|
| 46 |
# Start periodic tasks
|
| 47 |
asyncio.create_task(self.run_periodic_tasks())
|
| 48 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 49 |
async def run_periodic_tasks(self):
|
| 50 |
"""Run indexer and prefetch functions every 5 minutes."""
|
| 51 |
while not self.stop_event.is_set():
|
| 52 |
-
|
| 53 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 54 |
await asyncio.sleep(300) # Sleep for 5 minutes
|
| 55 |
|
| 56 |
def start_prefetching(self):
|
|
@@ -85,7 +130,8 @@ class LoadBalancer:
|
|
| 85 |
json_cache_path = os.path.join(self.CACHE_DIR, f"{urllib.parse.quote(original_title)}.json")
|
| 86 |
if not os.path.exists(json_cache_path):
|
| 87 |
tasks.append(fetch_and_cache_json(original_title, title, media_type, year))
|
| 88 |
-
|
|
|
|
| 89 |
|
| 90 |
# Run all tasks concurrently
|
| 91 |
await asyncio.gather(*tasks)
|
|
@@ -160,22 +206,13 @@ class LoadBalancer:
|
|
| 160 |
logging.info(f"Instance {instance_url} not found for removal.")
|
| 161 |
|
| 162 |
def update_instances_health(self, instance, cache_size):
|
| 163 |
-
self.instances_health[instance] = {"used":cache_size["cache_size"],
|
| 164 |
"total": "50 GB"}
|
| 165 |
logging.info(f"Updated instance {instance} with cache size {cache_size}")
|
| 166 |
|
| 167 |
def download_film_to_best_instance(self, title):
|
| 168 |
"""
|
| 169 |
-
Downloads a film to the first instance that has more free space on the self.
|
| 170 |
-
The instance_health looks like this:
|
| 171 |
-
{
|
| 172 |
-
"https://unicone-studio-instance1.hf.space": {
|
| 173 |
-
"total": "50 GB",
|
| 174 |
-
"used": "3.33 GB"
|
| 175 |
-
}
|
| 176 |
-
}
|
| 177 |
-
Args:
|
| 178 |
-
title (str): The title of the film.
|
| 179 |
"""
|
| 180 |
best_instance = None
|
| 181 |
max_free_space = -1
|
|
@@ -196,11 +233,10 @@ class LoadBalancer:
|
|
| 196 |
status = result["status"]
|
| 197 |
progress_url = f'{best_instance}/api/get/progress/{film_id}'
|
| 198 |
response = {
|
| 199 |
-
"film_id":film_id,
|
| 200 |
-
"status":status,
|
| 201 |
-
"progress_url":progress_url
|
| 202 |
}
|
| 203 |
-
|
| 204 |
return response
|
| 205 |
else:
|
| 206 |
logging.error("No suitable instance found for downloading the film.")
|
|
@@ -208,18 +244,7 @@ class LoadBalancer:
|
|
| 208 |
|
| 209 |
def download_episode_to_best_instance(self, title, season, episode):
|
| 210 |
"""
|
| 211 |
-
Downloads
|
| 212 |
-
The instance_health looks like this:
|
| 213 |
-
{
|
| 214 |
-
"https://unicone-studio-instance1.hf.space": {
|
| 215 |
-
"total": "50 GB",
|
| 216 |
-
"used": "3.33 GB"
|
| 217 |
-
}
|
| 218 |
-
}
|
| 219 |
-
Args:
|
| 220 |
-
title (str): The title of the Tv show.
|
| 221 |
-
season (str): The season of the Tv show.
|
| 222 |
-
episode (str): The title of the Tv show.
|
| 223 |
"""
|
| 224 |
best_instance = None
|
| 225 |
max_free_space = -1
|
|
@@ -240,11 +265,10 @@ class LoadBalancer:
|
|
| 240 |
status = result["status"]
|
| 241 |
progress_url = f'{best_instance}/api/get/progress/{episode_id}'
|
| 242 |
response = {
|
| 243 |
-
"episode_id":episode_id,
|
| 244 |
-
"status":status,
|
| 245 |
-
"progress_url":progress_url
|
| 246 |
}
|
| 247 |
-
|
| 248 |
return response
|
| 249 |
else:
|
| 250 |
logging.error("No suitable instance found for downloading the film.")
|
|
@@ -253,29 +277,29 @@ class LoadBalancer:
|
|
| 253 |
def find_movie_path(self, title):
|
| 254 |
"""Find the path of the movie in the JSON data based on the title."""
|
| 255 |
for directory in self.file_structure:
|
| 256 |
-
if directory
|
| 257 |
-
for sub_directory in directory
|
| 258 |
-
if sub_directory
|
| 259 |
-
for item in sub_directory
|
| 260 |
-
if item
|
| 261 |
-
return item
|
| 262 |
return None
|
| 263 |
|
| 264 |
def find_tv_path(self, title):
|
| 265 |
"""Find the path of the TV show in the JSON data based on the title."""
|
| 266 |
for directory in self.file_structure:
|
| 267 |
-
if directory
|
| 268 |
-
for sub_directory in directory
|
| 269 |
-
if sub_directory
|
| 270 |
-
return sub_directory
|
| 271 |
return None
|
| 272 |
|
| 273 |
def get_tv_structure(self, title):
|
| 274 |
"""Find the path of the TV show in the JSON data based on the title."""
|
| 275 |
for directory in self.file_structure:
|
| 276 |
-
if directory
|
| 277 |
-
for sub_directory in directory
|
| 278 |
-
if sub_directory
|
| 279 |
return sub_directory
|
| 280 |
return None
|
| 281 |
|
|
@@ -284,32 +308,9 @@ class LoadBalancer:
|
|
| 284 |
return title.replace(" ", "_").lower()
|
| 285 |
|
| 286 |
def get_all_tv_shows(self):
|
| 287 |
-
"""
|
| 288 |
-
|
| 289 |
-
for directory in self.file_structure:
|
| 290 |
-
if directory['type'] == 'directory' and directory['path'] == 'tv':
|
| 291 |
-
for sub_directory in directory['contents']:
|
| 292 |
-
if sub_directory['type'] == 'directory':
|
| 293 |
-
show_title = sub_directory['path'].split('/')[-1]
|
| 294 |
-
tv_shows[show_title] = []
|
| 295 |
-
for season_directory in sub_directory['contents']:
|
| 296 |
-
if season_directory['type'] == 'directory':
|
| 297 |
-
season = season_directory['path'].split('/')[-1]
|
| 298 |
-
for episode in season_directory['contents']:
|
| 299 |
-
if episode['type'] == 'file':
|
| 300 |
-
tv_shows[show_title].append({
|
| 301 |
-
"season": season,
|
| 302 |
-
"episode": episode['path'].split('/')[-1],
|
| 303 |
-
"path": episode['path']
|
| 304 |
-
})
|
| 305 |
-
return tv_shows
|
| 306 |
|
| 307 |
def get_all_films(self):
|
| 308 |
-
"""
|
| 309 |
-
|
| 310 |
-
for directory in self.file_structure:
|
| 311 |
-
if directory['type'] == 'directory' and directory['path'] == 'films':
|
| 312 |
-
for sub_directory in directory['contents']:
|
| 313 |
-
if sub_directory['type'] == 'directory':
|
| 314 |
-
films.append(sub_directory['path'])
|
| 315 |
-
return films
|
|
|
|
| 30 |
self.file_structure = None
|
| 31 |
self.previous_file_structure = None # To keep track of previous content
|
| 32 |
|
| 33 |
+
# Caches for films and TV shows to avoid returning empty lists in case of errors
|
| 34 |
+
self.cached_films = []
|
| 35 |
+
self.cached_tv_shows = {}
|
| 36 |
+
|
| 37 |
# Ensure CACHE_DIR exists
|
| 38 |
if not os.path.exists(self.CACHE_DIR):
|
| 39 |
os.makedirs(self.CACHE_DIR)
|
| 40 |
|
| 41 |
+
# Initialize file structure, update caches, and start prefetching
|
| 42 |
self.file_structure = indexer()
|
| 43 |
+
self.update_media_cache()
|
| 44 |
self.start_prefetching()
|
| 45 |
|
| 46 |
# Start polling and file checking in separate threads
|
|
|
|
| 51 |
# Start periodic tasks
|
| 52 |
asyncio.create_task(self.run_periodic_tasks())
|
| 53 |
|
| 54 |
+
def update_media_cache(self):
|
| 55 |
+
"""Update the cached films and TV shows from the current file_structure.
|
| 56 |
+
Only update if the new data is non-empty, preserving the last valid cache.
|
| 57 |
+
"""
|
| 58 |
+
new_films = []
|
| 59 |
+
new_tv_shows = {}
|
| 60 |
+
|
| 61 |
+
for directory in self.file_structure:
|
| 62 |
+
if directory.get('type') == 'directory':
|
| 63 |
+
if directory.get('path') == 'films':
|
| 64 |
+
for sub_directory in directory.get('contents', []):
|
| 65 |
+
if sub_directory.get('type') == 'directory':
|
| 66 |
+
new_films.append(sub_directory.get('path'))
|
| 67 |
+
elif directory.get('path') == 'tv':
|
| 68 |
+
for sub_directory in directory.get('contents', []):
|
| 69 |
+
if sub_directory.get('type') == 'directory':
|
| 70 |
+
show_title = sub_directory.get('path').split('/')[-1]
|
| 71 |
+
episodes_list = []
|
| 72 |
+
for season_directory in sub_directory.get('contents', []):
|
| 73 |
+
if season_directory.get('type') == 'directory':
|
| 74 |
+
season = season_directory.get('path').split('/')[-1]
|
| 75 |
+
for episode in season_directory.get('contents', []):
|
| 76 |
+
if episode.get('type') == 'file':
|
| 77 |
+
episodes_list.append({
|
| 78 |
+
"season": season,
|
| 79 |
+
"episode": episode.get('path').split('/')[-1],
|
| 80 |
+
"path": episode.get('path')
|
| 81 |
+
})
|
| 82 |
+
if episodes_list:
|
| 83 |
+
new_tv_shows[show_title] = episodes_list
|
| 84 |
+
|
| 85 |
+
if new_films:
|
| 86 |
+
self.cached_films = new_films
|
| 87 |
+
if new_tv_shows:
|
| 88 |
+
self.cached_tv_shows = new_tv_shows
|
| 89 |
+
|
| 90 |
async def run_periodic_tasks(self):
|
| 91 |
"""Run indexer and prefetch functions every 5 minutes."""
|
| 92 |
while not self.stop_event.is_set():
|
| 93 |
+
new_file_structure = indexer() # Re-run indexer
|
| 94 |
+
# Only update if the new file_structure is non-empty
|
| 95 |
+
if new_file_structure:
|
| 96 |
+
self.file_structure = new_file_structure
|
| 97 |
+
self.update_media_cache()
|
| 98 |
+
await self.start_prefetching() # Start prefetching metadata
|
| 99 |
await asyncio.sleep(300) # Sleep for 5 minutes
|
| 100 |
|
| 101 |
def start_prefetching(self):
|
|
|
|
| 130 |
json_cache_path = os.path.join(self.CACHE_DIR, f"{urllib.parse.quote(original_title)}.json")
|
| 131 |
if not os.path.exists(json_cache_path):
|
| 132 |
tasks.append(fetch_and_cache_json(original_title, title, media_type, year))
|
| 133 |
+
else:
|
| 134 |
+
logging.info(f"Skipping.. {original_title} metadata already cached")
|
| 135 |
|
| 136 |
# Run all tasks concurrently
|
| 137 |
await asyncio.gather(*tasks)
|
|
|
|
| 206 |
logging.info(f"Instance {instance_url} not found for removal.")
|
| 207 |
|
| 208 |
def update_instances_health(self, instance, cache_size):
|
| 209 |
+
self.instances_health[instance] = {"used": cache_size["cache_size"],
|
| 210 |
"total": "50 GB"}
|
| 211 |
logging.info(f"Updated instance {instance} with cache size {cache_size}")
|
| 212 |
|
| 213 |
def download_film_to_best_instance(self, title):
|
| 214 |
"""
|
| 215 |
+
Downloads a film to the first instance that has more free space on the self.instances_health list.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 216 |
"""
|
| 217 |
best_instance = None
|
| 218 |
max_free_space = -1
|
|
|
|
| 233 |
status = result["status"]
|
| 234 |
progress_url = f'{best_instance}/api/get/progress/{film_id}'
|
| 235 |
response = {
|
| 236 |
+
"film_id": film_id,
|
| 237 |
+
"status": status,
|
| 238 |
+
"progress_url": progress_url
|
| 239 |
}
|
|
|
|
| 240 |
return response
|
| 241 |
else:
|
| 242 |
logging.error("No suitable instance found for downloading the film.")
|
|
|
|
| 244 |
|
| 245 |
def download_episode_to_best_instance(self, title, season, episode):
|
| 246 |
"""
|
| 247 |
+
Downloads an episode to the first instance that has more free space on the self.instances_health list.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 248 |
"""
|
| 249 |
best_instance = None
|
| 250 |
max_free_space = -1
|
|
|
|
| 265 |
status = result["status"]
|
| 266 |
progress_url = f'{best_instance}/api/get/progress/{episode_id}'
|
| 267 |
response = {
|
| 268 |
+
"episode_id": episode_id,
|
| 269 |
+
"status": status,
|
| 270 |
+
"progress_url": progress_url
|
| 271 |
}
|
|
|
|
| 272 |
return response
|
| 273 |
else:
|
| 274 |
logging.error("No suitable instance found for downloading the film.")
|
|
|
|
| 277 |
def find_movie_path(self, title):
|
| 278 |
"""Find the path of the movie in the JSON data based on the title."""
|
| 279 |
for directory in self.file_structure:
|
| 280 |
+
if directory.get('type') == 'directory' and directory.get('path') == 'films':
|
| 281 |
+
for sub_directory in directory.get('contents', []):
|
| 282 |
+
if sub_directory.get('type') == 'directory':
|
| 283 |
+
for item in sub_directory.get('contents', []):
|
| 284 |
+
if item.get('type') == 'file' and title.lower() in item.get('path').lower():
|
| 285 |
+
return item.get('path')
|
| 286 |
return None
|
| 287 |
|
| 288 |
def find_tv_path(self, title):
|
| 289 |
"""Find the path of the TV show in the JSON data based on the title."""
|
| 290 |
for directory in self.file_structure:
|
| 291 |
+
if directory.get('type') == 'directory' and directory.get('path') == 'tv':
|
| 292 |
+
for sub_directory in directory.get('contents', []):
|
| 293 |
+
if sub_directory.get('type') == 'directory' and title.lower() in sub_directory.get('path').lower():
|
| 294 |
+
return sub_directory.get('path')
|
| 295 |
return None
|
| 296 |
|
| 297 |
def get_tv_structure(self, title):
|
| 298 |
"""Find the path of the TV show in the JSON data based on the title."""
|
| 299 |
for directory in self.file_structure:
|
| 300 |
+
if directory.get('type') == 'directory' and directory.get('path') == 'tv':
|
| 301 |
+
for sub_directory in directory.get('contents', []):
|
| 302 |
+
if sub_directory.get('type') == 'directory' and title.lower() in sub_directory.get('path').lower():
|
| 303 |
return sub_directory
|
| 304 |
return None
|
| 305 |
|
|
|
|
| 308 |
return title.replace(" ", "_").lower()
|
| 309 |
|
| 310 |
def get_all_tv_shows(self):
|
| 311 |
+
"""Return the cached TV shows."""
|
| 312 |
+
return self.cached_tv_shows
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 313 |
|
| 314 |
def get_all_films(self):
|
| 315 |
+
"""Return the cached films."""
|
| 316 |
+
return self.cached_films
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
TODO.md
CHANGED
|
@@ -1,4 +1,2 @@
|
|
| 1 |
-
|
| 2 |
-
* /api/get/film/
|
| 3 |
-
|
| 4 |
-
* /api/get/tv/{title}/{season}/{episode} `Endpoint to get the episode by title, season and episode.`
|
|
|
|
| 1 |
+
Fix BUGS
|
| 2 |
+
* /api/get/film/all `After some times the list becomes empty`
|
|
|
|
|
|