Preserve album bundle track numbers
Keep album-bundle staging from replacing known per-track album numbers with the filename parser's default when staged files do not expose a real track number. Carry staging tag numbers through the cache, fall back to task metadata for private release staging, and cap hybrid album batches to one worker when Soulseek is first in the source order.
This commit is contained in:
parent
a3ba79a9ce
commit
0bea332aed
3 changed files with 89 additions and 7 deletions
|
|
@ -38,8 +38,6 @@ import re
|
|||
from dataclasses import dataclass
|
||||
from typing import Any, Callable
|
||||
|
||||
from core.imports.filename import extract_track_number_from_filename
|
||||
|
||||
# `shutil` and `SequenceMatcher` are imported inline inside try_staging_match()
|
||||
# to keep the lift byte-identical with the original web_server.py function body.
|
||||
|
||||
|
|
@ -61,6 +59,25 @@ def _coerce_positive_int(value: Any, default: int = 0) -> int:
|
|||
return coerced if coerced > 0 else default
|
||||
|
||||
|
||||
def _extract_explicit_track_number(filename: str) -> int:
|
||||
"""Extract a track number only when the filename visibly carries one."""
|
||||
basename = os.path.splitext(os.path.basename(str(filename or '')))[0].strip()
|
||||
if not basename:
|
||||
return 0
|
||||
|
||||
match = re.match(r"^\d[\-\.](\d{1,2})\s*[\-\.]\s*", basename)
|
||||
if match:
|
||||
num = int(match.group(1))
|
||||
return num if 1 <= num <= 99 else 0
|
||||
|
||||
match = re.match(r"^\(?(\d{1,3})\)?\s*[\-\.)\]]\s*", basename)
|
||||
if match:
|
||||
num = int(match.group(1))
|
||||
return num if 1 <= num <= 999 else 0
|
||||
|
||||
return 0
|
||||
|
||||
|
||||
def _staging_title_variants(title: Any, normalize: Callable[[str], str]) -> list[str]:
|
||||
"""Return conservative title variants for release-file matching.
|
||||
|
||||
|
|
@ -320,12 +337,24 @@ def try_staging_match(task_id, batch_id, track, deps: StagingDeps):
|
|||
|
||||
file_track_number = (
|
||||
_coerce_positive_int(best_match.get('track_number'), 0) or
|
||||
extract_track_number_from_filename(best_match.get('full_path', ''))
|
||||
_extract_explicit_track_number(best_match.get('full_path', ''))
|
||||
)
|
||||
file_disc_number = _coerce_positive_int(best_match.get('disc_number'), 1)
|
||||
file_disc_number = _coerce_positive_int(best_match.get('disc_number'), 0)
|
||||
if _private_album_bundle_staging:
|
||||
track_number = file_track_number
|
||||
disc_number = file_disc_number
|
||||
track_number = (
|
||||
file_track_number or
|
||||
_coerce_positive_int(track_info.get('track_number'), 0) or
|
||||
_coerce_positive_int(track_info.get('trackNumber'), 0) or
|
||||
_coerce_positive_int(getattr(track, 'track_number', 0), 0) or
|
||||
1
|
||||
)
|
||||
disc_number = (
|
||||
file_disc_number or
|
||||
_coerce_positive_int(track_info.get('disc_number'), 0) or
|
||||
_coerce_positive_int(track_info.get('discNumber'), 0) or
|
||||
_coerce_positive_int(getattr(track, 'disc_number', 0), 0) or
|
||||
1
|
||||
)
|
||||
else:
|
||||
track_number = (
|
||||
_coerce_positive_int(track_info.get('track_number'), 0) or
|
||||
|
|
@ -337,7 +366,8 @@ def try_staging_match(task_id, batch_id, track, deps: StagingDeps):
|
|||
_coerce_positive_int(track_info.get('disc_number'), 0) or
|
||||
_coerce_positive_int(track_info.get('discNumber'), 0) or
|
||||
_coerce_positive_int(getattr(track, 'disc_number', 0), 0) or
|
||||
file_disc_number
|
||||
file_disc_number or
|
||||
1
|
||||
)
|
||||
track_info['track_number'] = track_number
|
||||
track_info['disc_number'] = disc_number
|
||||
|
|
|
|||
|
|
@ -357,6 +357,49 @@ def test_private_album_bundle_staging_overrides_default_track_info_number(tmp_pa
|
|||
assert ctx['original_search_result']['filename'] == str(src_file)
|
||||
|
||||
|
||||
def test_private_album_bundle_staging_keeps_task_number_when_file_has_no_number(tmp_path):
|
||||
"""Private release staging must not turn every unnumbered release file into track 1."""
|
||||
src_file = tmp_path / 'staging' / 'Katy Perry - Firework.flac'
|
||||
src_file.parent.mkdir()
|
||||
src_file.touch()
|
||||
|
||||
def get_batch_field(_batch_id, field):
|
||||
if field == 'album_bundle_source':
|
||||
return 'soulseek'
|
||||
if field == 'album_bundle_private_staging':
|
||||
return True
|
||||
return None
|
||||
|
||||
deps = _build_deps(
|
||||
transfer_path=str(tmp_path / 'transfer'),
|
||||
staging_files=[
|
||||
{
|
||||
'full_path': str(src_file),
|
||||
'title': 'Firework',
|
||||
'artist': 'Katy Perry',
|
||||
},
|
||||
],
|
||||
get_batch_field=get_batch_field,
|
||||
)
|
||||
_seed_task('t6d', track_info={
|
||||
'_is_explicit_album_download': True,
|
||||
'_explicit_album_context': {'id': 'alb', 'name': 'Teenage Dream: The Complete Confection'},
|
||||
'_explicit_artist_context': {'id': 'art', 'name': 'Katy Perry'},
|
||||
'track_number': 4,
|
||||
'disc_number': 1,
|
||||
})
|
||||
|
||||
ds.try_staging_match(
|
||||
't6d', 'b1',
|
||||
_Track(name='Firework', artists=['Katy Perry']),
|
||||
deps,
|
||||
)
|
||||
|
||||
ctx = matched_downloads_context['staging_t6d']
|
||||
assert ctx['track_info']['track_number'] == 4
|
||||
assert ctx['original_search_result']['track_number'] == 4
|
||||
|
||||
|
||||
def test_staging_title_match_accepts_feature_suffix_from_release_file(tmp_path):
|
||||
"""Album releases can include featured artists in filenames."""
|
||||
src_file = tmp_path / 'staging' / '05-kendrick_lamar-money_trees_(feat._jay_rock).flac'
|
||||
|
|
|
|||
|
|
@ -1068,6 +1068,13 @@ def _get_batch_max_concurrent(is_album=False, source=None):
|
|||
mode = config_manager.get('download_source.mode', 'soulseek')
|
||||
if mode == 'soulseek':
|
||||
return 1
|
||||
if mode == 'hybrid':
|
||||
hybrid_order = config_manager.get('download_source.hybrid_order', []) or []
|
||||
if isinstance(hybrid_order, str):
|
||||
hybrid_order = [hybrid_order]
|
||||
first_source = next((str(s).strip().lower() for s in hybrid_order if str(s).strip()), '')
|
||||
if first_source == 'soulseek':
|
||||
return 1
|
||||
return _get_max_concurrent()
|
||||
|
||||
# --- Session Download Statistics ---
|
||||
|
|
@ -16531,6 +16538,8 @@ def _get_staging_file_cache(batch_id):
|
|||
'title': meta['title'] or '',
|
||||
'artist': meta['albumartist'] or meta['artist'] or '',
|
||||
'album': meta['album'] or '',
|
||||
'track_number': meta.get('track_number'),
|
||||
'disc_number': meta.get('disc_number'),
|
||||
'extension': ext,
|
||||
})
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue