This commit is contained in:
nose
2025-12-13 00:18:30 -08:00
parent 85750247cc
commit 30eb628aa3
18 changed files with 1056 additions and 407 deletions

View File

@@ -8,6 +8,9 @@ import sys
from SYS.logger import log
import subprocess as _subprocess
import shutil as _shutil
import re as _re
from config import resolve_output_dir
from ._shared import (
Cmdlet,
@@ -34,9 +37,7 @@ except ImportError:
try:
from metadata import (
read_tags_from_file,
write_tags_to_file,
dedup_tags_by_namespace,
write_metadata
)
HAS_METADATA_API = True
except ImportError:
@@ -105,8 +106,6 @@ def _run(result: Any, args: Sequence[str], config: Dict[str, Any]) -> int:
source_hashes: List[str] = []
source_url: List[str] = []
source_tags: List[str] = [] # NEW: collect tags from source files
source_relationships: List[str] = [] # NEW: collect relationships from source files
for item in files_to_merge:
raw_path = get_pipe_object_path(item)
target_path = None
@@ -191,7 +190,11 @@ def _run(result: Any, args: Sequence[str], config: Dict[str, Any]) -> int:
output_path = output_override
else:
first_file = source_files[0]
output_path = first_file.parent / f"{first_file.stem} (merged).{_ext_for_format(output_format)}"
try:
base_dir = resolve_output_dir(config)
except Exception:
base_dir = first_file.parent
output_path = Path(base_dir) / f"{first_file.stem} (merged).{_ext_for_format(output_format)}"
# Ensure output directory exists
output_path.parent.mkdir(parents=True, exist_ok=True)
@@ -217,61 +220,14 @@ def _run(result: Any, args: Sequence[str], config: Dict[str, Any]) -> int:
merged_tags: List[str] = [f"title:{output_path.stem}"]
# Create .tag sidecar file for the merged output using unified API
tags_path = output_path.with_suffix(output_path.suffix + '.tag')
try:
# Merge tags from source files using metadata API
if source_tags and HAS_METADATA_API:
# Use dedup function to normalize and deduplicate
merged_source_tags = dedup_tags_by_namespace(source_tags)
merged_tags.extend(merged_source_tags)
log(f"Merged {len(merged_source_tags)} unique tags from source files", file=sys.stderr)
elif source_tags:
# Fallback: simple deduplication if metadata API unavailable
merged_tags.extend(list(dict.fromkeys(source_tags))) # Preserve order, remove duplicates
# Write merged tags to sidecar file
if HAS_METADATA_API and write_tags_to_file:
# Use unified API for file writing
source_hashes_list = source_hashes if source_hashes else None
source_url_list = source_url if source_url else None
write_tags_to_file(tags_path, merged_tags, source_hashes_list, source_url_list)
else:
# Fallback: manual file writing
tags_lines = []
# Add hash first (if available)
if source_hashes:
tags_lines.append(f"hash:{source_hashes[0]}")
# Add regular tags
tags_lines.extend(merged_tags)
# Add known url
if source_url:
for url in source_url:
tags_lines.append(f"url:{url}")
# Add relationships (if available)
if source_relationships:
for rel in source_relationships:
tags_lines.append(f"relationship:{rel}")
with open(tags_path, 'w', encoding='utf-8') as f:
f.write('\n'.join(tags_lines) + '\n')
log(f"Created sidecar: {tags_path.name}", file=sys.stderr)
# Also create .metadata file using centralized function
try:
if HAS_METADATA_API and write_metadata:
write_metadata(output_path, source_hashes[0] if source_hashes else None, source_url, source_relationships)
log(f"Created metadata: {output_path.name}.metadata", file=sys.stderr)
except Exception as e:
log(f"Warning: Could not create metadata file: {e}", file=sys.stderr)
except Exception as e:
log(f"Warning: Could not create sidecar: {e}", file=sys.stderr)
# Merge tags from source files into the emitted PipeObject only.
# Sidecar files (.tag/.metadata) are written only during explicit filesystem export (add-file to a path).
if source_tags and HAS_METADATA_API:
merged_source_tags = dedup_tags_by_namespace(source_tags)
merged_tags.extend(merged_source_tags)
log(f"Merged {len(merged_source_tags)} unique tags from source files", file=sys.stderr)
elif source_tags:
merged_tags.extend(list(dict.fromkeys(source_tags))) # Preserve order, remove duplicates
# Emit a PipeObject-compatible dict so the merged file can be piped to next command
try:
@@ -287,6 +243,7 @@ def _run(result: Any, args: Sequence[str], config: Dict[str, Any]) -> int:
tag=merged_tags,
url=source_url,
media_kind=file_kind,
store="PATH",
)
# Clear previous results to ensure only the merged file is passed down
ctx.clear_last_result()
@@ -424,6 +381,33 @@ def _merge_audio(files: List[Path], output: Path, output_format: str) -> bool:
logger.info(f"[merge-file] Chapter: {title} @ {chapters[-1]['time_str']} (duration: {duration_sec:.2f}s)")
current_time_ms += int(duration_sec * 1000)
# If these came from a playlist/album, titles often look like:
# "Book Name - Chapter"
# If *all* titles share the same "Book Name" prefix, strip it.
if len(chapters) >= 2:
split_re = _re.compile(r"^(?P<prefix>.+?)\s+-\s+(?P<chapter>.+)$")
prefixes: List[str] = []
stripped_titles: List[str] = []
all_match = True
for ch in chapters:
raw_title = str(ch.get('title') or '').strip()
m = split_re.match(raw_title)
if not m:
all_match = False
break
prefix = m.group('prefix').strip()
chapter_title = m.group('chapter').strip()
if not prefix or not chapter_title:
all_match = False
break
prefixes.append(prefix.casefold())
stripped_titles.append(chapter_title)
if all_match and prefixes and len(set(prefixes)) == 1:
for idx, ch in enumerate(chapters):
ch['title'] = stripped_titles[idx]
logger.info(f"[merge-file] Stripped common title prefix for chapters: {prefixes[0]}")
# Step 2: Create concat demuxer file
concat_file = output.parent / f".concat_{output.stem}.txt"