updated bandcamp and list parsing
This commit is contained in:
@@ -0,0 +1 @@
|
||||
{"store":"rpi"}
|
||||
+20
-2
@@ -16,6 +16,15 @@ class Bandcamp(Provider):
|
||||
TABLE_AUTO_STAGES = {
|
||||
"bandcamp": ["download-file"],
|
||||
}
|
||||
AUTO_STAGE_USE_SELECTION_ARGS = True
|
||||
|
||||
@staticmethod
|
||||
def _download_selection_args(target_url: str, media_type: str) -> Optional[List[str]]:
|
||||
target = str(target_url or "").strip()
|
||||
kind = str(media_type or "").strip().lower()
|
||||
if not target or kind == "artist":
|
||||
return None
|
||||
return ["-url", target]
|
||||
|
||||
@staticmethod
|
||||
def _base_url(raw_url: str) -> str:
|
||||
@@ -89,6 +98,8 @@ class Bandcamp(Provider):
|
||||
"album" if "/album/" in target else
|
||||
("track" if "/track/" in target else "item")
|
||||
)
|
||||
selection_args = self._download_selection_args(target, kind)
|
||||
selection_action = (["download-file"] + selection_args) if selection_args else None
|
||||
|
||||
results.append(
|
||||
SearchResult(
|
||||
@@ -111,6 +122,8 @@ class Bandcamp(Provider):
|
||||
"url": target,
|
||||
"artist_url": base,
|
||||
},
|
||||
selection_args=selection_args,
|
||||
selection_action=selection_action,
|
||||
)
|
||||
)
|
||||
except Exception as exc:
|
||||
@@ -318,6 +331,8 @@ class Bandcamp(Provider):
|
||||
|
||||
itemtype = item.query_selector(".itemtype")
|
||||
media_type = itemtype.inner_text().strip() if itemtype else "album"
|
||||
selection_args = self._download_selection_args(str(target_url or ""), media_type)
|
||||
selection_action = (["download-file"] + selection_args) if selection_args else None
|
||||
|
||||
results.append(
|
||||
SearchResult(
|
||||
@@ -335,13 +350,16 @@ class Bandcamp(Provider):
|
||||
("Type",
|
||||
media_type),
|
||||
("Url",
|
||||
base_url or str(target_url or "")),
|
||||
str(target_url or "")),
|
||||
],
|
||||
full_metadata={
|
||||
"artist": artist,
|
||||
"type": media_type,
|
||||
"url": base_url or str(target_url or ""),
|
||||
"url": str(target_url or ""),
|
||||
"artist_url": base_url,
|
||||
},
|
||||
selection_args=selection_args,
|
||||
selection_action=selection_action,
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
+7
-2
@@ -630,6 +630,8 @@ class Table:
|
||||
"""Base arguments for the source command"""
|
||||
self.header_lines: List[str] = []
|
||||
"""Optional metadata lines rendered under the title"""
|
||||
self.preserve_order: bool = bool(preserve_order)
|
||||
"""If True, skip automatic sorting so display order matches input order."""
|
||||
self.perseverance: bool = preserve_order
|
||||
"""If True, skip automatic sorting so display order matches input order."""
|
||||
self.interactive: bool = False
|
||||
@@ -687,7 +689,9 @@ class Table:
|
||||
|
||||
def _perseverance(self, perseverance: bool = True) -> "Table":
|
||||
"""Configure whether this table should skip automatic sorting."""
|
||||
self.perseverance = bool(perseverance)
|
||||
keep_order = bool(perseverance)
|
||||
self.perseverance = keep_order
|
||||
self.preserve_order = keep_order
|
||||
return self
|
||||
|
||||
def add_row(self) -> Row:
|
||||
@@ -741,7 +745,8 @@ class Table:
|
||||
self.title = title
|
||||
self.source_command = command
|
||||
self.source_args = args or []
|
||||
self.perseverance = preserve_order
|
||||
self.perseverance = bool(preserve_order)
|
||||
self.preserve_order = bool(preserve_order)
|
||||
return self
|
||||
|
||||
def copy_with_title(self, new_title: str) -> "Table":
|
||||
|
||||
+3
-1
@@ -1920,11 +1920,13 @@ def create_pipe_object_result(
|
||||
Returns:
|
||||
Dict with all PipeObject fields for emission
|
||||
"""
|
||||
store_override = extra.pop("store", None)
|
||||
|
||||
result = build_file_result_payload(
|
||||
title=title,
|
||||
path=file_path,
|
||||
hash_value=hash_value,
|
||||
store=source,
|
||||
store=store_override if store_override is not None else source,
|
||||
tag=tag,
|
||||
source=source,
|
||||
id=identifier,
|
||||
|
||||
+151
-9
@@ -124,6 +124,48 @@ class Download_File(Cmdlet):
|
||||
raise DownloadError("Could not determine downloaded file path")
|
||||
return resolved
|
||||
|
||||
@staticmethod
|
||||
def _selection_run_label(run_args: Sequence[str]) -> str:
|
||||
try:
|
||||
urls = extract_urls_from_selection_args(
|
||||
run_args,
|
||||
extra_url_prefixes=_ALLDEBRID_PREFIXES,
|
||||
)
|
||||
if urls:
|
||||
return str(urls[0])
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
for arg in run_args:
|
||||
text = str(arg or "").strip()
|
||||
if text and not text.startswith("-"):
|
||||
return text
|
||||
return "item"
|
||||
|
||||
@staticmethod
|
||||
def _batch_progress_state(config: Optional[Dict[str, Any]]) -> tuple[bool, int, int, str]:
|
||||
if not isinstance(config, dict):
|
||||
return False, 0, 0, ""
|
||||
|
||||
suppress_nested = bool(config.get("_download_file_suppress_nested_pipe_progress"))
|
||||
if not suppress_nested:
|
||||
return False, 0, 0, ""
|
||||
|
||||
try:
|
||||
total = max(0, int(config.get("_download_file_batch_total") or 0))
|
||||
except Exception:
|
||||
total = 0
|
||||
try:
|
||||
index = max(0, int(config.get("_download_file_batch_index") or 0))
|
||||
except Exception:
|
||||
index = 0
|
||||
try:
|
||||
label = str(config.get("_download_file_batch_label") or "").strip()
|
||||
except Exception:
|
||||
label = ""
|
||||
|
||||
return True, total, index, label
|
||||
|
||||
def _process_explicit_urls(
|
||||
self,
|
||||
*,
|
||||
@@ -139,14 +181,32 @@ class Download_File(Cmdlet):
|
||||
) -> tuple[int,
|
||||
Optional[int]]:
|
||||
downloaded_count = 0
|
||||
suppress_nested, batch_total, batch_index, batch_label = self._batch_progress_state(config)
|
||||
total_urls = len(raw_urls or [])
|
||||
|
||||
try:
|
||||
if total_urls > 1 and not suppress_nested:
|
||||
progress.begin_pipe(total_items=total_urls, items_preview=list(raw_urls[:5]))
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
SearchResult = registry.get("SearchResult")
|
||||
get_provider = registry.get("get_provider")
|
||||
match_provider_name_for_url = registry.get("match_provider_name_for_url")
|
||||
|
||||
for url in raw_urls:
|
||||
for idx, url in enumerate(raw_urls, 1):
|
||||
try:
|
||||
debug(f"Processing URL: {url}")
|
||||
try:
|
||||
display_total = batch_total if batch_total > 0 else total_urls
|
||||
display_index = batch_index if batch_total > 0 else idx
|
||||
display_label = batch_label or str(url)
|
||||
if display_total > 0:
|
||||
progress.set_status(
|
||||
f"downloading {display_index}/{display_total}: {display_label}"
|
||||
)
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
# Check providers first
|
||||
provider_name = None
|
||||
@@ -1455,6 +1515,9 @@ class Download_File(Cmdlet):
|
||||
downloaded_pipe_objects: List[Dict[str, Any]] = []
|
||||
pipe_seq = 0
|
||||
clip_sections_spec = self._build_clip_sections_spec(clip_ranges)
|
||||
suppress_nested, batch_total, batch_index, batch_label = self._batch_progress_state(config)
|
||||
total_urls = len(supported_url or [])
|
||||
aggregate_status_mode = bool(suppress_nested or total_urls > 1)
|
||||
|
||||
if clip_sections_spec:
|
||||
try:
|
||||
@@ -1462,11 +1525,15 @@ class Download_File(Cmdlet):
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
for url in supported_url:
|
||||
for url_index, url in enumerate(supported_url, 1):
|
||||
try:
|
||||
debug(f"[download-file] Processing URL in loop: {url}")
|
||||
debug(f"[download-file] ytdl_format parameter passed in: {ytdl_format}")
|
||||
|
||||
display_total = batch_total if batch_total > 0 else total_urls
|
||||
display_index = batch_index if batch_total > 0 else url_index
|
||||
display_label = batch_label or str(url)
|
||||
|
||||
canonical_url = url
|
||||
if not skip_per_url_preflight or clip_ranges:
|
||||
canonical_url = self._canonicalize_url_for_storage(
|
||||
@@ -1487,7 +1554,16 @@ class Download_File(Cmdlet):
|
||||
log(f"Skipping download (duplicate found): {url}", file=sys.stderr)
|
||||
continue
|
||||
|
||||
PipelineProgress(pipeline_context).begin_steps(2)
|
||||
if aggregate_status_mode:
|
||||
try:
|
||||
if display_total > 0:
|
||||
PipelineProgress(pipeline_context).set_status(
|
||||
f"downloading {display_index}/{display_total}: {display_label}"
|
||||
)
|
||||
except Exception:
|
||||
pass
|
||||
else:
|
||||
PipelineProgress(pipeline_context).begin_steps(2)
|
||||
|
||||
actual_format = ytdl_format
|
||||
actual_playlist_items = playlist_items
|
||||
@@ -1631,7 +1707,8 @@ class Download_File(Cmdlet):
|
||||
write_sub=write_sub,
|
||||
)
|
||||
|
||||
PipelineProgress(pipeline_context).step("downloading")
|
||||
if not aggregate_status_mode:
|
||||
PipelineProgress(pipeline_context).step("downloading")
|
||||
debug(f"Starting download for {url} (format: {actual_format or 'default'}) with {download_timeout_seconds}s activity timeout...")
|
||||
result_obj = _download_with_timeout(opts, timeout_seconds=download_timeout_seconds, config=config)
|
||||
debug(f"Download completed for {url}, building pipe object...")
|
||||
@@ -1953,7 +2030,8 @@ class Download_File(Cmdlet):
|
||||
|
||||
debug(f"Emitting {len(pipe_objects)} result(s) to pipeline...")
|
||||
|
||||
PipelineProgress(pipeline_context).step("finalized")
|
||||
if not aggregate_status_mode:
|
||||
PipelineProgress(pipeline_context).step("finalized")
|
||||
|
||||
stage_ctx = pipeline_context.get_stage_context()
|
||||
emit_enabled = bool(stage_ctx is not None)
|
||||
@@ -1995,6 +2073,7 @@ class Download_File(Cmdlet):
|
||||
) -> int:
|
||||
try:
|
||||
debug("Starting streaming download handler")
|
||||
suppress_nested, _batch_total, _batch_index, _batch_label = self._batch_progress_state(config)
|
||||
|
||||
ytdlp_tool = YtDlpTool(config)
|
||||
|
||||
@@ -2028,10 +2107,11 @@ class Download_File(Cmdlet):
|
||||
else:
|
||||
debug("[download-file] Skipping local UI: running inside pipeline stage")
|
||||
try:
|
||||
progress.begin_pipe(
|
||||
total_items=len(supported_url),
|
||||
items_preview=supported_url,
|
||||
)
|
||||
if not suppress_nested:
|
||||
progress.begin_pipe(
|
||||
total_items=len(supported_url),
|
||||
items_preview=supported_url,
|
||||
)
|
||||
except Exception as err:
|
||||
debug(f"[download-file] PipelineProgress begin_pipe error: {err}")
|
||||
except Exception as e:
|
||||
@@ -2852,14 +2932,26 @@ class Download_File(Cmdlet):
|
||||
original_skip_preflight = None
|
||||
original_timeout = None
|
||||
original_skip_direct = None
|
||||
original_batch_total = None
|
||||
original_batch_index = None
|
||||
original_batch_label = None
|
||||
original_suppress_nested = None
|
||||
try:
|
||||
if isinstance(config, dict):
|
||||
original_skip_preflight = config.get("_skip_url_preflight")
|
||||
original_timeout = config.get("_pipeobject_timeout_seconds")
|
||||
original_skip_direct = config.get("_skip_direct_on_streaming_failure")
|
||||
original_batch_total = config.get("_download_file_batch_total")
|
||||
original_batch_index = config.get("_download_file_batch_index")
|
||||
original_batch_label = config.get("_download_file_batch_label")
|
||||
original_suppress_nested = config.get("_download_file_suppress_nested_pipe_progress")
|
||||
except Exception:
|
||||
original_skip_preflight = None
|
||||
original_timeout = None
|
||||
original_batch_total = None
|
||||
original_batch_index = None
|
||||
original_batch_label = None
|
||||
original_suppress_nested = None
|
||||
|
||||
try:
|
||||
if selection_urls:
|
||||
@@ -2878,10 +2970,44 @@ class Download_File(Cmdlet):
|
||||
failures = 0
|
||||
last_code = 0
|
||||
total_selection = len(selection_runs)
|
||||
preview_items = list(selection_urls[:5]) or [
|
||||
self._selection_run_label(run_args)
|
||||
for run_args in selection_runs[:5]
|
||||
]
|
||||
try:
|
||||
progress.ensure_local_ui(
|
||||
label="download-file",
|
||||
total_items=total_selection,
|
||||
items_preview=preview_items,
|
||||
)
|
||||
except Exception:
|
||||
pass
|
||||
try:
|
||||
progress.begin_pipe(
|
||||
total_items=total_selection,
|
||||
items_preview=preview_items,
|
||||
)
|
||||
except Exception:
|
||||
pass
|
||||
debug(f"[download-file] Processing {total_selection} selected item(s) from table...")
|
||||
for idx, run_args in enumerate(selection_runs, 1):
|
||||
run_label = self._selection_run_label(run_args)
|
||||
debug(f"[download-file] Item {idx}/{total_selection}: {run_args}")
|
||||
debug("[download-file] Re-invoking download-file for selected item...")
|
||||
try:
|
||||
progress.set_status(
|
||||
f"downloading {idx}/{total_selection}: {run_label}"
|
||||
)
|
||||
except Exception:
|
||||
pass
|
||||
try:
|
||||
if isinstance(config, dict):
|
||||
config["_download_file_batch_total"] = total_selection
|
||||
config["_download_file_batch_index"] = idx
|
||||
config["_download_file_batch_label"] = run_label
|
||||
config["_download_file_suppress_nested_pipe_progress"] = True
|
||||
except Exception:
|
||||
pass
|
||||
exit_code = self._run_impl(None, run_args, config)
|
||||
if exit_code == 0:
|
||||
successes += 1
|
||||
@@ -2909,6 +3035,22 @@ class Download_File(Cmdlet):
|
||||
config.pop("_skip_direct_on_streaming_failure", None)
|
||||
else:
|
||||
config["_skip_direct_on_streaming_failure"] = original_skip_direct
|
||||
if original_batch_total is None:
|
||||
config.pop("_download_file_batch_total", None)
|
||||
else:
|
||||
config["_download_file_batch_total"] = original_batch_total
|
||||
if original_batch_index is None:
|
||||
config.pop("_download_file_batch_index", None)
|
||||
else:
|
||||
config["_download_file_batch_index"] = original_batch_index
|
||||
if original_batch_label is None:
|
||||
config.pop("_download_file_batch_label", None)
|
||||
else:
|
||||
config["_download_file_batch_label"] = original_batch_label
|
||||
if original_suppress_nested is None:
|
||||
config.pop("_download_file_suppress_nested_pipe_progress", None)
|
||||
else:
|
||||
config["_download_file_suppress_nested_pipe_progress"] = original_suppress_nested
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
|
||||
Reference in New Issue
Block a user