Fix Reorganize All sending every album to Albums folder

The reorganize endpoints built a template context without albumtype,
so ${albumtype} silently fell through to the engine's hardcoded "Album"
default — EPs, singles, and compilations all landed in Albums/.

Wires albumtype through from the albums table's record_type column
(populated by Spotify/Deezer/iTunes enrichment workers) with track-count
fallback when record_type is missing. New helper mirrors the download
pipeline's classification logic so reorganize produces the same folders
as initial placement. Also handles Deezer's raw 'compile' value which
the Deezer worker writes directly without mapping.
This commit is contained in:
Broque Thomas 2026-04-21 08:59:16 -07:00
parent 14359a1f98
commit 739d2f67c1
2 changed files with 53 additions and 0 deletions

View file

@ -13272,6 +13272,10 @@ def reorganize_album_preview(album_id):
'disc_number': disc_number,
'year': year_val,
'quality': quality,
'albumtype': _get_album_type_display(
album_data.get('record_type'),
album_data.get('track_count') or len(tracks)
),
}
# Build new path using the template
@ -13426,6 +13430,10 @@ def reorganize_album_files(album_id):
'disc_number': disc_number,
'year': year_val,
'quality': quality,
'albumtype': _get_album_type_display(
album_data.get('record_type'),
album_data.get('track_count') or len(tracks)
),
}
folder_path, filename = _get_file_path_from_template_raw(template, context)
@ -18472,6 +18480,47 @@ def _create_lossy_copy(final_path):
print(f"[Lossy Copy] Conversion error: {e}")
return None
def _get_album_type_display(raw_type, track_count) -> str:
"""
Return the display form of an album's type for the $albumtype template variable.
Mirrors the inference used in the download pipeline so reorganize output
matches initial placement. Values: 'Album', 'Single', 'EP', 'Compilation'.
"""
raw = (raw_type or '').strip().lower()
try:
tc = int(track_count or 0)
except (TypeError, ValueError):
tc = 0
# Deezer's raw API returns 'compile' (only mapped to 'compilation' in the
# Album dataclass path); the Deezer enrichment worker writes the raw value,
# so both need to match here.
if raw in ('compilation', 'compile'):
return 'Compilation'
if raw == 'album':
return 'Album'
if raw in ('single', 'ep'):
# Match download-pipeline logic: Spotify labels both singles and EPs
# as 'single', so final classification is by track count. Applying the
# same rule to explicit 'ep' keeps reorganize consistent with where
# the files were first placed.
if tc <= 3:
return 'Single'
if tc <= 6:
return 'EP'
return 'Album'
# Unknown/missing — infer from track count
if tc <= 0:
return 'Album'
if tc <= 3:
return 'Single'
if tc <= 6:
return 'EP'
return 'Album'
def _apply_path_template(template: str, context: dict) -> str:
"""
Apply template to build file path.

View file

@ -3600,6 +3600,10 @@ function closeHelperSearch() {
const WHATS_NEW = {
'2.35': [
// --- April 21, 2026 ---
{ date: 'April 21, 2026' },
{ title: 'Fix Reorganize All Ignoring Album Type', desc: 'Reorganize All was sending every album — EPs, singles, and compilations — into the "Albums" folder because the $albumtype template variable silently defaulted to "Album". The variable is now resolved from the album\'s record_type (with track-count fallback) so ${albumtype}s produces the expected Albums/Singles/EPs/Compilations split', page: 'library' },
// --- April 20, 2026 ---
{ date: 'April 20, 2026' },
{ title: 'Discography Backfill Maintenance Job', desc: 'New library maintenance job that scans each artist in your library, fetches their full discography from metadata sources, and creates findings for any missing tracks. Review findings and click "Add to Wishlist" to queue them for download. Respects content filters (live/remix/acoustic/compilation) and release type filters. Opt-in, disabled by default', page: 'library' },