alldebrid plugin optimization and mpv playlist fix

This commit is contained in:
2026-04-26 13:48:18 -07:00
parent 67c272db4b
commit c724cb36b1
7 changed files with 147 additions and 31 deletions
+3 -3
View File
@@ -375,7 +375,7 @@
"(filespace\\.com/[a-zA-Z0-9]{12})" "(filespace\\.com/[a-zA-Z0-9]{12})"
], ],
"regexp": "(filespace\\.com/fd/([a-zA-Z0-9]{12}))|((filespace\\.com/[a-zA-Z0-9]{12}))", "regexp": "(filespace\\.com/fd/([a-zA-Z0-9]{12}))|((filespace\\.com/[a-zA-Z0-9]{12}))",
"status": true "status": false
}, },
"filezip": { "filezip": {
"name": "filezip", "name": "filezip",
@@ -463,7 +463,7 @@
"isra\\.cloud/\\?op=report_file&id=([0-9a-zA-Z]{12})" "isra\\.cloud/\\?op=report_file&id=([0-9a-zA-Z]{12})"
], ],
"regexp": "((isra\\.cloud/[0-9a-zA-Z]{12}))|(isra\\.cloud/\\?op=report_file&id=([0-9a-zA-Z]{12}))", "regexp": "((isra\\.cloud/[0-9a-zA-Z]{12}))|(isra\\.cloud/\\?op=report_file&id=([0-9a-zA-Z]{12}))",
"status": true, "status": false,
"hardRedirect": [ "hardRedirect": [
"isra\\.cloud/([0-9a-zA-Z]{12})" "isra\\.cloud/([0-9a-zA-Z]{12})"
] ]
@@ -494,7 +494,7 @@
"mediafire\\.com/(\\?|download/|file/|download\\.php\\?)([0-9a-z]{15})" "mediafire\\.com/(\\?|download/|file/|download\\.php\\?)([0-9a-z]{15})"
], ],
"regexp": "mediafire\\.com/(\\?|download/|file/|download\\.php\\?)([0-9a-z]{15})", "regexp": "mediafire\\.com/(\\?|download/|file/|download\\.php\\?)([0-9a-z]{15})",
"status": false "status": true
}, },
"mixdrop": { "mixdrop": {
"name": "mixdrop", "name": "mixdrop",
+6 -1
View File
@@ -5683,13 +5683,18 @@ mp.register_event('file-loaded', function()
mp.add_timeout(0.1, function() mp.add_timeout(0.1, function()
M._reset_uosc_input_state('file-loaded-web') M._reset_uosc_input_state('file-loaded-web')
end) end)
_format_cache_poll_generation = _format_cache_poll_generation + 1
if _extract_store_hash(url) or not _is_ytdlp_url(url) then
return
end
local ok, err = _cache_formats_from_current_playback('file-loaded') local ok, err = _cache_formats_from_current_playback('file-loaded')
if ok then if ok then
_lua_log('formats: file-loaded cache succeeded for url=' .. url) _lua_log('formats: file-loaded cache succeeded for url=' .. url)
else else
_lua_log('formats: file-loaded cache pending reason=' .. tostring(err or 'unknown')) _lua_log('formats: file-loaded cache pending reason=' .. tostring(err or 'unknown'))
end end
_format_cache_poll_generation = _format_cache_poll_generation + 1
_schedule_playback_format_cache_poll(url, _format_cache_poll_generation, 1) _schedule_playback_format_cache_poll(url, _format_cache_poll_generation, 1)
_prefetch_formats_for_url(url) _prefetch_formats_for_url(url)
end) end)
+7 -6
View File
@@ -7,13 +7,14 @@ border=no
cache=yes cache=yes
# Give HTTP store streams more room to absorb Hydrus/network jitter before # Give HTTP store streams more room to absorb Hydrus/network jitter before
# mpv restarts audio after an underrun. # mpv restarts audio after an underrun.
cache-secs=90 cache-secs=300
cache-pause=yes cache-pause=yes
cache-pause-wait=12 cache-pause-initial=yes
demuxer-readahead-secs=90 cache-pause-wait=30
demuxer-max-bytes=512MiB demuxer-readahead-secs=300
demuxer-max-back-bytes=256MiB demuxer-max-bytes=1GiB
audio-buffer=1.0 demuxer-max-back-bytes=512MiB
audio-buffer=2.0
# Ensure uosc texture/icon fonts are discoverable by libass. # Ensure uosc texture/icon fonts are discoverable by libass.
osd-fonts-dir=~~/scripts/uosc/fonts osd-fonts-dir=~~/scripts/uosc/fonts
+29 -15
View File
@@ -1248,11 +1248,15 @@ class AllDebrid(TableProviderMixin, Provider):
log(f"AllDebrid magnet {magnet_id} has no downloadable files", file=sys.stderr) log(f"AllDebrid magnet {magnet_id} has no downloadable files", file=sys.stderr)
return 0 return 0
try: magnet_path_metadata: Dict[str, Any] = {}
if progress is not None and hasattr(progress, "begin_steps"): magnet_folder_name = str(
progress.begin_steps(total_files) magnet_files.get("filename")
except Exception: or magnet_files.get("name")
pass or magnet_files.get("hash")
or f"magnet-{magnet_id}"
).strip()
if magnet_folder_name:
magnet_path_metadata["folder"] = magnet_folder_name
downloaded = 0 downloaded = 0
for file_idx, node in enumerate(file_entries, 1): for file_idx, node in enumerate(file_entries, 1):
@@ -1261,8 +1265,10 @@ class AllDebrid(TableProviderMixin, Provider):
relpath = str(node.get("_relpath") or file_name or "").strip() relpath = str(node.get("_relpath") or file_name or "").strip()
try: try:
if progress is not None and hasattr(progress, "step"): if progress is not None and hasattr(progress, "set_status"):
progress.step(f"file {file_idx}/{total_files}: {relpath or file_name or 'download'}") progress.set_status(
f"downloading file {file_idx}/{total_files}: {relpath or file_name or 'download'}"
)
except Exception: except Exception:
pass pass
@@ -1282,21 +1288,23 @@ class AllDebrid(TableProviderMixin, Provider):
except Exception as exc: except Exception as exc:
debug(f"[alldebrid] unlock_link failed: {exc}, trying locked URL") debug(f"[alldebrid] unlock_link failed: {exc}, trying locked URL")
target_path = output_dir
rel_path_obj = Path(relpath) rel_path_obj = Path(relpath)
if rel_path_obj.parent: target_path = adjust_output_dir_for_alldebrid(
target_path = output_dir / rel_path_obj.parent output_dir,
try: {**magnet_path_metadata, "relpath": relpath},
target_path.mkdir(parents=True, exist_ok=True) magnet_files,
except Exception: )
target_path = output_dir
suggested_name = sanitize_filename(rel_path_obj.name) or sanitize_filename(file_name)
if not suggested_name:
suggested_name = rel_path_obj.name or file_name or f"file-{file_idx}"
try: try:
result_obj = _download_direct_file( result_obj = _download_direct_file(
file_url, file_url,
target_path, target_path,
quiet=quiet_mode, quiet=quiet_mode,
suggested_filename=rel_path_obj.name, suggested_filename=suggested_name,
pipeline_progress=progress, pipeline_progress=progress,
) )
except Exception as exc: except Exception as exc:
@@ -1315,6 +1323,12 @@ class AllDebrid(TableProviderMixin, Provider):
if downloaded == 0: if downloaded == 0:
log(f"AllDebrid magnet {magnet_id} produced no downloads", file=sys.stderr) log(f"AllDebrid magnet {magnet_id} produced no downloads", file=sys.stderr)
try:
if progress is not None and hasattr(progress, "clear_status"):
progress.clear_status()
except Exception:
pass
return downloaded return downloaded
def search( def search(
+80 -3
View File
@@ -6,6 +6,7 @@ import json
import sqlite3 import sqlite3
import time import time
import os import os
import re
import datetime import datetime
import sys import sys
import tempfile import tempfile
@@ -30,6 +31,7 @@ _LAST_SAVED_CONFIG: Dict[str, Any] = {}
_CONFIG_SAVE_MAX_RETRIES = 5 _CONFIG_SAVE_MAX_RETRIES = 5
_CONFIG_SAVE_RETRY_DELAY = 0.15 _CONFIG_SAVE_RETRY_DELAY = 0.15
_CONFIG_MISSING = object() _CONFIG_MISSING = object()
_PATH_ALIAS_TOKEN_RE = re.compile(r"^\$(?:\((?P<braced>[^)]+)\)|(?P<plain>[A-Za-z0-9_.-]+))$")
class ConfigSaveConflict(Exception): class ConfigSaveConflict(Exception):
@@ -62,6 +64,20 @@ def global_config() -> List[Dict[str, Any]]:
"group": "Display", "group": "Display",
"default": "rainbow", "default": "rainbow",
"choices": ["plain", "bw-striped", "rainbow"], "choices": ["plain", "bw-striped", "rainbow"],
},
{
"key": "download_path_default",
"label": "Default Download Path",
"group": "Downloads",
"type": "string",
"default": "",
},
{
"key": "path_aliases",
"label": "Path Aliases",
"group": "Downloads",
"type": "json",
"default": {},
} }
] ]
@@ -82,6 +98,56 @@ def get_nested_config_value(config: Dict[str, Any], *path: str) -> Any:
return cur return cur
def _normalize_path_alias_name(value: Any) -> Optional[str]:
raw = str(value or "").strip()
if not raw:
return None
match = _PATH_ALIAS_TOKEN_RE.match(raw)
if match:
raw = str(match.group("braced") or match.group("plain") or "").strip()
candidate = raw.strip().strip("()")
if not candidate:
return None
return candidate.lower()
def get_path_aliases(config: Dict[str, Any]) -> Dict[str, str]:
aliases: Dict[str, str] = {}
if not isinstance(config, dict):
return aliases
for block_name in ("path_aliases", "download_paths"):
block = config.get(block_name)
if not isinstance(block, dict):
continue
for key, value in block.items():
alias = _normalize_path_alias_name(key)
if not alias:
continue
if isinstance(value, str) and value.strip():
aliases[alias] = value.strip()
return aliases
def resolve_path_alias(config: Dict[str, Any], value: Any) -> Optional[Path]:
raw = str(value or "").strip()
if not raw.startswith("$"):
return None
alias = _normalize_path_alias_name(raw)
if not alias:
return None
target = get_path_aliases(config).get(alias)
if not target:
return None
return expand_path(target)
def coerce_config_value( def coerce_config_value(
value: Any, value: Any,
existing_value: Any = _CONFIG_MISSING, existing_value: Any = _CONFIG_MISSING,
@@ -271,13 +337,24 @@ def resolve_output_dir(config: Dict[str, Any]) -> Path:
"""Resolve output directory from config with single source of truth. """Resolve output directory from config with single source of truth.
Priority: Priority:
1. config["temp"] - explicitly set temp/output directory 1. config["download_path_default"] - default download/output directory
2. config["outfile"] - fallback to outfile setting 2. config["temp"] - explicitly set temp/output directory
3. System Temp - default fallback directory 3. config["outfile"] - fallback to outfile setting
4. System Temp - default fallback directory
Returns: Returns:
Path to output directory Path to output directory
""" """
default_output = config.get("download_path_default")
if default_output:
try:
aliased = resolve_path_alias(config, default_output)
path = aliased if aliased is not None else expand_path(default_output)
if path.exists() or path.parent.exists():
return path
except Exception as exc:
logger.debug("resolve_output_dir: failed to expand download_path_default value %r: %s", default_output, exc, exc_info=True)
# First try explicit temp setting from config # First try explicit temp setting from config
temp_value = config.get("temp") temp_value = config.get("temp")
if temp_value: if temp_value:
+13 -1
View File
@@ -804,7 +804,19 @@ def resolve_target_dir(
target = parsed.get("path") target = parsed.get("path")
if target: if target:
try: try:
p = Path(str(target)).expanduser().resolve() from SYS.config import resolve_path_alias
from SYS.utils import expand_path
raw_target = str(target or "").strip()
aliased = resolve_path_alias(config, raw_target)
if raw_target.startswith("$") and aliased is None:
log(
f"Unknown path alias {raw_target}. Set it with .config path_aliases.<name> <path>",
file=sys.stderr,
)
return None
p = aliased if aliased is not None else expand_path(raw_target)
if handle_creations: if handle_creations:
p.mkdir(parents=True, exist_ok=True) p.mkdir(parents=True, exist_ok=True)
return p return p
+9 -2
View File
@@ -1454,6 +1454,13 @@ def _queue_items(
except Exception: except Exception:
pass pass
# Batched queue operations should not block on one IPC reply per item.
# On Windows named pipes, waiting for every `loadfile`/`loadlist` ack can
# make multi-select `.mpv` calls stall for several seconds.
command_wait = bool(wait)
if len(items) > 1:
command_wait = False
# Just verify cookies are configured, don't try to set via IPC # Just verify cookies are configured, don't try to set via IPC
_ensure_ytdl_cookies(config) _ensure_ytdl_cookies(config)
@@ -1683,8 +1690,8 @@ def _queue_items(
"request_id": 200 "request_id": 200
} }
try: try:
debug(f"Sending MPV {command_name}: {target_to_send} mode={mode} wait={wait}") debug(f"Sending MPV {command_name}: {target_to_send} mode={mode} wait={command_wait}")
resp = _send_ipc_command(cmd, silent=True, wait=wait) resp = _send_ipc_command(cmd, silent=True, wait=command_wait)
debug(f"MPV {command_name} response: {resp}") debug(f"MPV {command_name} response: {resp}")
except Exception as e: except Exception as e:
debug(f"Exception sending {command_name} to MPV: {e}", file=sys.stderr) debug(f"Exception sending {command_name} to MPV: {e}", file=sys.stderr)