Add $year variable support to file path templates

Introduces the $year variable for album, single, and playlist path templates, allowing users to include the release year in file organization. Updates the backend to extract and provide the year, adjusts the web UI to document the new variable, and updates validation logic to recognize $year as valid in templates.
pull/115/head
Broque Thomas 5 months ago
parent 3bd8c4f94c
commit a8935ae8f5

@ -48,6 +48,7 @@
},
"file_organization": {
"enabled": true,
"_template_variables": "Available: $artist, $albumartist, $album, $title, $track, $year, $playlist",
"templates": {
"album_path": "$albumartist/$albumartist - $album/$track - $title",
"single_path": "$artist/$artist - $title/$title",

@ -6948,6 +6948,14 @@ 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)
year = 'Unknown'
spotify_album = context.get("spotify_album", {})
if spotify_album and spotify_album.get('release_date'):
release_date = spotify_album['release_date']
if release_date and len(release_date) >= 4:
year = release_date[:4]
# Determine which template type to use
if playlist_folder_mode:
# PLAYLIST MODE
@ -6960,7 +6968,8 @@ def _build_final_path_for_track(context, spotify_artist, album_info, file_ext):
'album': track_name,
'title': track_name,
'playlist_name': playlist_name,
'track_number': 1
'track_number': 1,
'year': year
}
folder_path, filename_base = _get_file_path_from_template(template_context, 'playlist_path')
@ -6997,7 +7006,8 @@ def _build_final_path_for_track(context, spotify_artist, album_info, file_ext):
'albumartist': spotify_artist["name"] if isinstance(spotify_artist, dict) else spotify_artist.name,
'album': album_info['album_name'],
'title': clean_track_name,
'track_number': track_number
'track_number': track_number,
'year': year
}
folder_path, filename_base = _get_file_path_from_template(template_context, 'album_path')
@ -7032,7 +7042,8 @@ def _build_final_path_for_track(context, spotify_artist, album_info, file_ext):
'albumartist': spotify_artist["name"] if isinstance(spotify_artist, dict) else spotify_artist.name,
'album': album_info.get('album_name', clean_track_name) if album_info else clean_track_name,
'title': clean_track_name,
'track_number': 1
'track_number': 1,
'year': year
}
folder_path, filename_base = _get_file_path_from_template(template_context, 'single_path')
@ -7076,6 +7087,7 @@ def _apply_path_template(template: str, context: dict) -> str:
result = result.replace('$album', context.get('album', 'Unknown Album'))
result = result.replace('$title', context.get('title', 'Unknown Track'))
result = result.replace('$track', f"{context.get('track_number', 1):02d}")
result = result.replace('$year', str(context.get('year', 'Unknown')))
return result

@ -2772,19 +2772,19 @@
<div class="form-group">
<label>Album Path Template:</label>
<input type="text" id="template-album-path" placeholder="$albumartist/$albumartist - $album/$track - $title">
<small style="color: #888;">Variables: $albumartist, $artist, $album, $title, $track</small>
<small style="color: #888;">Variables: $albumartist, $artist, $album, $title, $track, $year</small>
</div>
<div class="form-group">
<label>Single Path Template:</label>
<input type="text" id="template-single-path" placeholder="$artist/$artist - $title/$title">
<small style="color: #888;">Variables: $artist, $title, $album</small>
<small style="color: #888;">Variables: $artist, $title, $album, $year</small>
</div>
<div class="form-group">
<label>Playlist Path Template:</label>
<input type="text" id="template-playlist-path" placeholder="$playlist/$artist - $title">
<small style="color: #888;">Variables: $playlist, $artist, $title</small>
<small style="color: #888;">Variables: $playlist, $artist, $title, $year</small>
</div>
<div class="form-group">

@ -1484,9 +1484,9 @@ function validateFileOrganizationTemplates() {
// Valid variables for each template type
const validVars = {
album: ['$artist', '$albumartist', '$album', '$title', '$track'],
single: ['$artist', '$albumartist', '$album', '$title'],
playlist: ['$artist', '$playlist', '$title']
album: ['$artist', '$albumartist', '$album', '$title', '$track', '$year'],
single: ['$artist', '$albumartist', '$album', '$title', '$year'],
playlist: ['$artist', '$playlist', '$title', '$year']
};
// Get template values
@ -7219,7 +7219,8 @@ async function startMissingTracksProcess(playlistId) {
};
// If this is an artist album download, use album name and include full context
if (playlistId.startsWith('artist_album_')) {
// Match both 'artist_album_' and 'enhanced_search_album_' prefixes
if (playlistId.startsWith('artist_album_') || playlistId.startsWith('enhanced_search_album_')) {
requestBody.playlist_name = process.album?.name || process.playlist.name;
requestBody.is_album_download = true;
requestBody.album_context = process.album; // Full Spotify album object

Loading…
Cancel
Save