Thaimozhi commited on
Commit ·
4aa5df4
1
Parent(s): 3526668
Improve leech progress stats and rename templates
Browse files- plugins/file_rename.py +32 -7
- tests/test_file_rename.py +17 -0
plugins/file_rename.py
CHANGED
|
@@ -287,6 +287,22 @@ async def resolve_remote_filename(url: str, fallback: str = "downloaded_file") -
|
|
| 287 |
return guess_filename_from_url(url, fallback=fallback)
|
| 288 |
|
| 289 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 290 |
async def _update_leech_status(task: dict, status: str, current: int = 0, total: int = 0, speed: float = 0.0, elapsed: float = 0.0):
|
| 291 |
if not task.get("status_message"):
|
| 292 |
return
|
|
@@ -317,9 +333,14 @@ async def _update_leech_status(task: dict, status: str, current: int = 0, total:
|
|
| 317 |
else:
|
| 318 |
size_text = humanbytes(current) if current else "0 B"
|
| 319 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 320 |
text = (
|
| 321 |
f"🧲 Task {task['task_id']}\n"
|
| 322 |
-
f"Name: {
|
| 323 |
f"Status: {status}\n"
|
| 324 |
f"Progress: {bar} {percent:.0f}%\n"
|
| 325 |
f"Speed: {humanbytes(speed) if speed else '—'}/s\n"
|
|
@@ -354,6 +375,7 @@ async def _download_url_with_progress(url: str, output_path: str, task: dict):
|
|
| 354 |
raise RuntimeError(f"Download failed with status {resp.status}")
|
| 355 |
total = int(resp.headers.get("Content-Length", 0) or 0)
|
| 356 |
downloaded = 0
|
|
|
|
| 357 |
with open(output_path, "wb") as handle:
|
| 358 |
async for chunk in resp.content.iter_chunked(1024 * 64):
|
| 359 |
if task.get("cancelled"):
|
|
@@ -365,8 +387,9 @@ async def _download_url_with_progress(url: str, output_path: str, task: dict):
|
|
| 365 |
now = time.time()
|
| 366 |
elapsed = max(1, int(now - task.get("started_at", now)))
|
| 367 |
speed = downloaded / elapsed if elapsed else 0
|
| 368 |
-
if (now - task.get("last_status_update", 0)) >=
|
| 369 |
await _update_leech_status(task, "Downloading", current=downloaded, total=total, speed=speed, elapsed=elapsed)
|
|
|
|
| 370 |
return output_path
|
| 371 |
|
| 372 |
|
|
@@ -447,7 +470,9 @@ async def _process_leech_file(client, user_id, input_path, display_name, origina
|
|
| 447 |
|
| 448 |
season, episode = extract_season_episode(file_name)
|
| 449 |
quality = extract_quality(file_name)
|
| 450 |
-
|
|
|
|
|
|
|
| 451 |
|
| 452 |
replacements = {
|
| 453 |
'{season}': season or 'XX',
|
|
@@ -462,10 +487,9 @@ async def _process_leech_file(client, user_id, input_path, display_name, origina
|
|
| 462 |
stem = format_template
|
| 463 |
if task.get('custom_name'):
|
| 464 |
stem = task['custom_name']
|
| 465 |
-
new_filename =
|
| 466 |
-
|
| 467 |
-
|
| 468 |
-
new_filename = f"{new_filename}{ext_original}"
|
| 469 |
|
| 470 |
metadata_path = f"metadata/{new_filename}"
|
| 471 |
thumb_path = None
|
|
@@ -594,6 +618,7 @@ async def _run_leech_task(client, task: dict):
|
|
| 594 |
|
| 595 |
display_name = await resolve_remote_filename(url, fallback=task.get("requested_name", "downloaded_file"))
|
| 596 |
task["display_name"] = display_name
|
|
|
|
| 597 |
await _update_leech_status(task, "Preparing")
|
| 598 |
|
| 599 |
ext = os.path.splitext(display_name)[1].lower()
|
|
|
|
| 287 |
return guess_filename_from_url(url, fallback=fallback)
|
| 288 |
|
| 289 |
|
| 290 |
+
def build_leech_output_name(stem: str, ext_original: str, prefix: str = None, suffix: str = None) -> str:
|
| 291 |
+
if not stem:
|
| 292 |
+
stem = 'downloaded_file'
|
| 293 |
+
if not os.path.splitext(stem)[1]:
|
| 294 |
+
stem = f"{stem}{ext_original or '.bin'}"
|
| 295 |
+
return _apply_prefix_suffix_to_name(stem, '', prefix, suffix).rstrip('.')
|
| 296 |
+
|
| 297 |
+
|
| 298 |
+
def resolve_leech_format_template(rename_formats, original_name: str, default_template: str):
|
| 299 |
+
for fmt in rename_formats:
|
| 300 |
+
hint = fmt.get("hint", "")
|
| 301 |
+
if hint and hint.lower() in (original_name or "").lower():
|
| 302 |
+
return fmt.get("template", default_template), hint
|
| 303 |
+
return default_template, None
|
| 304 |
+
|
| 305 |
+
|
| 306 |
async def _update_leech_status(task: dict, status: str, current: int = 0, total: int = 0, speed: float = 0.0, elapsed: float = 0.0):
|
| 307 |
if not task.get("status_message"):
|
| 308 |
return
|
|
|
|
| 333 |
else:
|
| 334 |
size_text = humanbytes(current) if current else "0 B"
|
| 335 |
|
| 336 |
+
if task.get("display_name"):
|
| 337 |
+
display_name = task["display_name"]
|
| 338 |
+
else:
|
| 339 |
+
display_name = "Unknown"
|
| 340 |
+
|
| 341 |
text = (
|
| 342 |
f"🧲 Task {task['task_id']}\n"
|
| 343 |
+
f"Name: {display_name}\n"
|
| 344 |
f"Status: {status}\n"
|
| 345 |
f"Progress: {bar} {percent:.0f}%\n"
|
| 346 |
f"Speed: {humanbytes(speed) if speed else '—'}/s\n"
|
|
|
|
| 375 |
raise RuntimeError(f"Download failed with status {resp.status}")
|
| 376 |
total = int(resp.headers.get("Content-Length", 0) or 0)
|
| 377 |
downloaded = 0
|
| 378 |
+
await _update_leech_status(task, "Downloading", current=downloaded, total=total, speed=0, elapsed=0)
|
| 379 |
with open(output_path, "wb") as handle:
|
| 380 |
async for chunk in resp.content.iter_chunked(1024 * 64):
|
| 381 |
if task.get("cancelled"):
|
|
|
|
| 387 |
now = time.time()
|
| 388 |
elapsed = max(1, int(now - task.get("started_at", now)))
|
| 389 |
speed = downloaded / elapsed if elapsed else 0
|
| 390 |
+
if (now - task.get("last_status_update", 0)) >= 1 or downloaded >= total:
|
| 391 |
await _update_leech_status(task, "Downloading", current=downloaded, total=total, speed=speed, elapsed=elapsed)
|
| 392 |
+
await _update_leech_status(task, "Downloading", current=downloaded, total=total, speed=0, elapsed=max(1, int(time.time() - task.get("started_at", time.time()))))
|
| 393 |
return output_path
|
| 394 |
|
| 395 |
|
|
|
|
| 470 |
|
| 471 |
season, episode = extract_season_episode(file_name)
|
| 472 |
quality = extract_quality(file_name)
|
| 473 |
+
default_template = await codeflixbots.get_format_template(user_id) or os.path.splitext(file_name)[0]
|
| 474 |
+
rename_formats = await codeflixbots.get_rename_formats(user_id)
|
| 475 |
+
format_template, selected_hint = resolve_leech_format_template(rename_formats, original_file_name, default_template)
|
| 476 |
|
| 477 |
replacements = {
|
| 478 |
'{season}': season or 'XX',
|
|
|
|
| 487 |
stem = format_template
|
| 488 |
if task.get('custom_name'):
|
| 489 |
stem = task['custom_name']
|
| 490 |
+
new_filename = build_leech_output_name(stem, ext_original, prefix, suffix)
|
| 491 |
+
if selected_hint:
|
| 492 |
+
task['last_selected_hint'] = selected_hint
|
|
|
|
| 493 |
|
| 494 |
metadata_path = f"metadata/{new_filename}"
|
| 495 |
thumb_path = None
|
|
|
|
| 618 |
|
| 619 |
display_name = await resolve_remote_filename(url, fallback=task.get("requested_name", "downloaded_file"))
|
| 620 |
task["display_name"] = display_name
|
| 621 |
+
task["requested_name"] = task.get("custom_name") or display_name
|
| 622 |
await _update_leech_status(task, "Preparing")
|
| 623 |
|
| 624 |
ext = os.path.splitext(display_name)[1].lower()
|
tests/test_file_rename.py
CHANGED
|
@@ -89,6 +89,8 @@ from plugins.file_rename import (
|
|
| 89 |
guess_filename_from_url,
|
| 90 |
prepare_leech_workspace,
|
| 91 |
infer_media_type_hint,
|
|
|
|
|
|
|
| 92 |
)
|
| 93 |
|
| 94 |
|
|
@@ -130,3 +132,18 @@ def test_infer_media_type_hint_prefers_video_extension():
|
|
| 130 |
|
| 131 |
def test_infer_media_type_hint_falls_back_to_hint_when_no_extension():
|
| 132 |
assert infer_media_type_hint('random-token', fallback_hint='video') == 'video'
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 89 |
guess_filename_from_url,
|
| 90 |
prepare_leech_workspace,
|
| 91 |
infer_media_type_hint,
|
| 92 |
+
build_leech_output_name,
|
| 93 |
+
resolve_leech_format_template,
|
| 94 |
)
|
| 95 |
|
| 96 |
|
|
|
|
| 132 |
|
| 133 |
def test_infer_media_type_hint_falls_back_to_hint_when_no_extension():
|
| 134 |
assert infer_media_type_hint('random-token', fallback_hint='video') == 'video'
|
| 135 |
+
|
| 136 |
+
|
| 137 |
+
def test_build_leech_output_name_uses_custom_name_with_extension():
|
| 138 |
+
assert build_leech_output_name('aaa.mkv', '.mkv', None, None) == 'aaa.mkv'
|
| 139 |
+
|
| 140 |
+
|
| 141 |
+
def test_build_leech_output_name_uses_template_when_custom_name_has_no_extension():
|
| 142 |
+
assert build_leech_output_name('aaa', '.mkv', None, None) == 'aaa.mkv'
|
| 143 |
+
|
| 144 |
+
|
| 145 |
+
def test_resolve_leech_format_template_uses_matching_hint():
|
| 146 |
+
templates = [
|
| 147 |
+
{'hint': 'Agents', 'template': 'Agents of the Four Seasons Dance of Spring S{season}E{episode} {quality} WEB-DL Multi Audio ESub'},
|
| 148 |
+
]
|
| 149 |
+
assert resolve_leech_format_template(templates, 'Agents of the Four Seasons Dance of Spring S01E12 720p.mkv', 'default') == ('Agents of the Four Seasons Dance of Spring S{season}E{episode} {quality} WEB-DL Multi Audio ESub', 'Agents')
|