Add $albumtype template variable with smart EP/Single/Album detection

This commit is contained in:
Broque Thomas 2026-03-14 12:40:25 -07:00
parent 761052145f
commit 3bbbfb125e
3 changed files with 53 additions and 14 deletions

View file

@ -13220,7 +13220,7 @@ def _build_final_path_for_track(context, spotify_artist, album_info, file_ext):
original_search = context.get("original_search_result", {})
playlist_folder_mode = track_info.get("_playlist_folder_mode", False)
# Extract year from spotify_album for template use (safe for all modes)
# Extract year and album_type from spotify_album for template use (safe for all modes)
year = '' # Empty string instead of 'Unknown' to avoid "Unknown albumName"
spotify_album = context.get("spotify_album", {})
if spotify_album and spotify_album.get('release_date'):
@ -13228,6 +13228,37 @@ def _build_final_path_for_track(context, spotify_artist, album_info, file_ext):
if release_date and len(release_date) >= 4:
year = release_date[:4]
# Album type for $albumtype template variable (Album, EP, Single, Compilation)
raw_album_type = ''
if spotify_album:
raw_album_type = spotify_album.get('album_type', '') or ''
total_tracks = (spotify_album.get('total_tracks', 0) or 0) if spotify_album else 0
if raw_album_type.lower() == 'compilation':
album_type_display = 'Compilation'
elif raw_album_type.lower() == 'album':
album_type_display = 'Album'
elif raw_album_type.lower() in ('single', 'ep'):
# Spotify labels both singles and EPs as 'single' — use track count to distinguish
if total_tracks <= 3:
album_type_display = 'Single'
elif total_tracks <= 6:
album_type_display = 'EP'
else:
album_type_display = 'Album'
elif not raw_album_type:
# No album_type from API (e.g. iTunes source) — infer from track count
if total_tracks <= 0:
album_type_display = 'Album' # Unknown, safe default
elif total_tracks <= 3:
album_type_display = 'Single'
elif total_tracks <= 6:
album_type_display = 'EP'
else:
album_type_display = 'Album'
else:
album_type_display = raw_album_type.capitalize() or 'Album'
# Determine which template type to use
if playlist_folder_mode:
# PLAYLIST MODE
@ -13243,7 +13274,8 @@ def _build_final_path_for_track(context, spotify_artist, album_info, file_ext):
'track_number': 1,
'disc_number': 1,
'year': year,
'quality': context.get('_audio_quality', '')
'quality': context.get('_audio_quality', ''),
'albumtype': album_type_display,
}
folder_path, filename_base = _get_file_path_from_template(template_context, 'playlist_path')
@ -13286,7 +13318,8 @@ def _build_final_path_for_track(context, spotify_artist, album_info, file_ext):
'track_number': track_number,
'disc_number': disc_number,
'year': year,
'quality': context.get('_audio_quality', '')
'quality': context.get('_audio_quality', ''),
'albumtype': album_type_display,
}
spotify_album = context.get('spotify_album', {})
total_discs = spotify_album.get('total_discs', 1) if spotify_album else 1
@ -13339,7 +13372,8 @@ def _build_final_path_for_track(context, spotify_artist, album_info, file_ext):
'track_number': 1,
'disc_number': 1,
'year': year,
'quality': context.get('_audio_quality', '')
'quality': context.get('_audio_quality', ''),
'albumtype': album_type_display,
}
folder_path, filename_base = _get_file_path_from_template(template_context, 'single_path')
@ -13576,6 +13610,7 @@ def _apply_path_template(template: str, context: dict) -> str:
# Longest variables first
result = result.replace('$albumartist', clean_context.get('albumartist', clean_context.get('artist', 'Unknown Artist')))
result = result.replace('$albumtype', clean_context.get('albumtype', 'Album'))
result = result.replace('$playlist', clean_context.get('playlist_name', ''))
# Medium length variables
@ -21247,7 +21282,7 @@ def _attempt_download_with_candidates(task_id, candidates, track, batch_id=None)
else:
# Fallback to generic context for playlists/wishlists
spotify_artist_context = {'id': 'from_sync_modal', 'name': track.artists[0] if track.artists else 'Unknown', 'genres': []}
spotify_album_context = {'id': 'from_sync_modal', 'name': track.album, 'release_date': '', 'image_url': None}
spotify_album_context = {'id': 'from_sync_modal', 'name': track.album, 'release_date': '', 'image_url': None, 'album_type': 'album'}
download_payload = candidate.__dict__
@ -37524,7 +37559,8 @@ def import_singles_process():
'name': sp_album_info.get('name', ''),
'release_date': sp_album_info.get('release_date', ''),
'total_tracks': sp_album_info.get('total_tracks', 1),
'image_url': (sp_album_info.get('images', [{}])[0].get('url') if sp_album_info.get('images') else '')
'image_url': (sp_album_info.get('images', [{}])[0].get('url') if sp_album_info.get('images') else ''),
'album_type': sp_album_info.get('album_type', 'album'),
}
album_artists = sp_album_info.get('artists', [])
if album_artists:
@ -37566,7 +37602,8 @@ def import_singles_process():
'name': sp_album.get('name', ''),
'release_date': sp_album.get('release_date', ''),
'total_tracks': sp_album.get('total_tracks', 1),
'image_url': (sp_album.get('images', [{}])[0].get('url') if sp_album.get('images') else '')
'image_url': (sp_album.get('images', [{}])[0].get('url') if sp_album.get('images') else ''),
'album_type': sp_album.get('album_type', 'album'),
}
# Get artist genres
sp_artists = sp_album.get('artists', [])
@ -37599,7 +37636,8 @@ def import_singles_process():
'name': t.album if hasattr(t, 'album') else '',
'release_date': '',
'total_tracks': 1,
'image_url': t.image_url if hasattr(t, 'image_url') else ''
'image_url': t.image_url if hasattr(t, 'image_url') else '',
'album_type': t.album_type if hasattr(t, 'album_type') else 'album',
}
except Exception as sp_err:
logger.warning(f"Spotify lookup failed for '{title}': {sp_err}")
@ -37608,7 +37646,7 @@ def import_singles_process():
if not spotify_artist_data:
spotify_artist_data = {'name': artist or 'Unknown Artist', 'id': '', 'genres': []}
if not spotify_album_data:
spotify_album_data = {'id': '', 'name': '', 'release_date': '', 'total_tracks': 1, 'image_url': ''}
spotify_album_data = {'id': '', 'name': '', 'release_date': '', 'total_tracks': 1, 'image_url': '', 'album_type': 'album'}
if not spotify_track_data:
spotify_track_data = {
'name': title, 'id': '', 'track_number': 1, 'disc_number': 1,

View file

@ -4139,15 +4139,15 @@
<label>Album Path Template:</label>
<input type="text" id="template-album-path"
placeholder="$albumartist/$albumartist - $album/$track - $title">
<small class="settings-hint">Variables: $albumartist, $artist, $artistletter, $album, $title,
$track, $disc, $year, $quality (filename only)</small>
<small class="settings-hint">Variables: $albumartist, $artist, $artistletter, $album, $albumtype,
$title, $track, $disc, $year, $quality (filename only)</small>
</div>
<div class="form-group">
<label>Single Path Template:</label>
<input type="text" id="template-single-path"
placeholder="$artist/$artist - $title/$title">
<small class="settings-hint">Variables: $artist, $artistletter, $title, $album, $year, $quality (filename only)</small>
<small class="settings-hint">Variables: $artist, $artistletter, $title, $album, $albumtype, $year, $quality (filename only)</small>
</div>
<div class="form-group">

View file

@ -4427,8 +4427,8 @@ function validateFileOrganizationTemplates() {
// Valid variables for each template type
const validVars = {
album: ['$artist', '$albumartist', '$artistletter', '$album', '$title', '$track', '$disc', '$year', '$quality'],
single: ['$artist', '$albumartist', '$artistletter', '$album', '$title', '$year', '$quality'],
album: ['$artist', '$albumartist', '$artistletter', '$album', '$albumtype', '$title', '$track', '$disc', '$year', '$quality'],
single: ['$artist', '$albumartist', '$artistletter', '$album', '$albumtype', '$title', '$year', '$quality'],
playlist: ['$artist', '$artistletter', '$playlist', '$title', '$year', '$quality']
};
@ -38321,6 +38321,7 @@ async function showReorganizeModal(albumId) {
{ var: '$albumartist', desc: 'Album artist', example: artistName || 'Album Artist' },
{ var: '$artistletter', desc: 'First letter of artist', example: (artistName || 'A')[0].toUpperCase() },
{ var: '$album', desc: 'Album title', example: albumData ? albumData.title : 'Album' },
{ var: '$albumtype', desc: 'Album/EP/Single', example: 'Album' },
{ var: '$title', desc: 'Track title', example: 'Track Name' },
{ var: '$track', desc: 'Track number (zero-padded)', example: '01' },
{ var: '$disc', desc: 'Disc number (filename only)', example: '01' },