alldebrid plugin optimization and mpv playlist fix
This commit is contained in:
@@ -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
@@ -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,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
@@ -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
@@ -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
@@ -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
@@ -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)
|
||||||
|
|||||||
Reference in New Issue
Block a user