Add granular post-processing toggles for metadata services, cover art, and lyrics
This commit is contained in:
parent
5f72fe79a6
commit
cae2bd0c62
4 changed files with 141 additions and 23 deletions
|
|
@ -4198,7 +4198,7 @@ def handle_settings():
|
||||||
if 'active_media_server' in new_settings:
|
if 'active_media_server' in new_settings:
|
||||||
config_manager.set_active_media_server(new_settings['active_media_server'])
|
config_manager.set_active_media_server(new_settings['active_media_server'])
|
||||||
|
|
||||||
for service in ['spotify', 'plex', 'jellyfin', 'navidrome', 'soulseek', 'download_source', 'settings', 'database', 'metadata_enhancement', 'file_organization', 'playlist_sync', 'tidal', 'tidal_download', 'qobuz', 'listenbrainz', 'acoustid', 'lastfm', 'genius', 'import', 'lossy_copy', 'ui_appearance', 'youtube', 'content_filter', 'itunes', 'm3u_export']:
|
for service in ['spotify', 'plex', 'jellyfin', 'navidrome', 'soulseek', 'download_source', 'settings', 'database', 'metadata_enhancement', 'file_organization', 'playlist_sync', 'tidal', 'tidal_download', 'qobuz', 'listenbrainz', 'acoustid', 'lastfm', 'genius', 'import', 'lossy_copy', 'ui_appearance', 'youtube', 'content_filter', 'itunes', 'm3u_export', 'musicbrainz', 'deezer', 'audiodb']:
|
||||||
if service in new_settings:
|
if service in new_settings:
|
||||||
for key, value in new_settings[service].items():
|
for key, value in new_settings[service].items():
|
||||||
config_manager.set(f'{service}.{key}', value)
|
config_manager.set(f'{service}.{key}', value)
|
||||||
|
|
@ -14314,6 +14314,8 @@ def _generate_lrc_file(file_path: str, context: dict, artist: dict, album_info:
|
||||||
Generate LRC lyrics file using LRClib API.
|
Generate LRC lyrics file using LRClib API.
|
||||||
Elegant addition to post-processing - extracts metadata from existing context.
|
Elegant addition to post-processing - extracts metadata from existing context.
|
||||||
"""
|
"""
|
||||||
|
if not config_manager.get('metadata_enhancement.lrclib_enabled', True):
|
||||||
|
return False
|
||||||
try:
|
try:
|
||||||
# Extract track information from existing context (same as metadata enhancement)
|
# Extract track information from existing context (same as metadata enhancement)
|
||||||
original_search = context.get("original_search_result", {})
|
original_search = context.get("original_search_result", {})
|
||||||
|
|
@ -14698,7 +14700,9 @@ def _embed_source_ids(audio_file, metadata: dict):
|
||||||
# ── 2d. Tidal lookup for ISRC fallback, copyright, and source IDs ──
|
# ── 2d. Tidal lookup for ISRC fallback, copyright, and source IDs ──
|
||||||
tidal_isrc = None
|
tidal_isrc = None
|
||||||
tidal_copyright = None
|
tidal_copyright = None
|
||||||
if track_title and artist_name:
|
if not config_manager.get('tidal.embed_tags', True):
|
||||||
|
pass
|
||||||
|
elif track_title and artist_name:
|
||||||
try:
|
try:
|
||||||
if tidal_client and tidal_client.is_authenticated():
|
if tidal_client and tidal_client.is_authenticated():
|
||||||
td_result = tidal_client.search_track(artist_name, track_title)
|
td_result = tidal_client.search_track(artist_name, track_title)
|
||||||
|
|
@ -14729,7 +14733,9 @@ def _embed_source_ids(audio_file, metadata: dict):
|
||||||
qobuz_isrc = None
|
qobuz_isrc = None
|
||||||
qobuz_copyright = None
|
qobuz_copyright = None
|
||||||
qobuz_label = None
|
qobuz_label = None
|
||||||
if track_title and artist_name:
|
if not config_manager.get('qobuz.embed_tags', True):
|
||||||
|
pass
|
||||||
|
elif track_title and artist_name:
|
||||||
try:
|
try:
|
||||||
qz_client = qobuz_enrichment_worker.client if qobuz_enrichment_worker else None
|
qz_client = qobuz_enrichment_worker.client if qobuz_enrichment_worker else None
|
||||||
if qz_client and qz_client.is_authenticated():
|
if qz_client and qz_client.is_authenticated():
|
||||||
|
|
@ -14768,7 +14774,9 @@ def _embed_source_ids(audio_file, metadata: dict):
|
||||||
# ── 2f. Last.fm lookup for tags (genre merge) and URL ──
|
# ── 2f. Last.fm lookup for tags (genre merge) and URL ──
|
||||||
lastfm_tags = []
|
lastfm_tags = []
|
||||||
lastfm_url = None
|
lastfm_url = None
|
||||||
if track_title and artist_name:
|
if not config_manager.get('lastfm.embed_tags', True):
|
||||||
|
pass
|
||||||
|
elif track_title and artist_name:
|
||||||
try:
|
try:
|
||||||
lf_client = lastfm_worker.client if lastfm_worker else None
|
lf_client = lastfm_worker.client if lastfm_worker else None
|
||||||
if lf_client:
|
if lf_client:
|
||||||
|
|
@ -14794,7 +14802,9 @@ def _embed_source_ids(audio_file, metadata: dict):
|
||||||
# state directly and skip immediately if Genius is rate-limited, rather
|
# state directly and skip immediately if Genius is rate-limited, rather
|
||||||
# than entering search_song which would sleep for up to 120s.
|
# than entering search_song which would sleep for up to 120s.
|
||||||
genius_url = None
|
genius_url = None
|
||||||
if track_title and artist_name:
|
if not config_manager.get('genius.embed_tags', True):
|
||||||
|
pass
|
||||||
|
elif track_title and artist_name:
|
||||||
try:
|
try:
|
||||||
import core.genius_client as _genius_module
|
import core.genius_client as _genius_module
|
||||||
if time.time() < _genius_module._rate_limit_until:
|
if time.time() < _genius_module._rate_limit_until:
|
||||||
|
|
@ -14980,6 +14990,8 @@ def _embed_source_ids(audio_file, metadata: dict):
|
||||||
|
|
||||||
def _download_cover_art(album_info: dict, target_dir: str):
|
def _download_cover_art(album_info: dict, target_dir: str):
|
||||||
"""Downloads cover.jpg into the specified directory."""
|
"""Downloads cover.jpg into the specified directory."""
|
||||||
|
if not config_manager.get('metadata_enhancement.cover_art_download', True):
|
||||||
|
return
|
||||||
try:
|
try:
|
||||||
cover_path = os.path.join(target_dir, "cover.jpg")
|
cover_path = os.path.join(target_dir, "cover.jpg")
|
||||||
if os.path.exists(cover_path):
|
if os.path.exists(cover_path):
|
||||||
|
|
|
||||||
|
|
@ -4210,25 +4210,82 @@
|
||||||
|
|
||||||
<!-- Metadata Enhancement Settings -->
|
<!-- Metadata Enhancement Settings -->
|
||||||
<div class="settings-group">
|
<div class="settings-group">
|
||||||
<h3>🎵 Metadata Enhancement</h3>
|
<h3>🎵 Post-Processing</h3>
|
||||||
|
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label class="checkbox-label">
|
<label class="checkbox-label">
|
||||||
<input type="checkbox" id="metadata-enabled" checked>
|
<input type="checkbox" id="metadata-enabled" checked
|
||||||
Enable metadata enhancement with Spotify data
|
onchange="document.getElementById('post-processing-options').style.display = this.checked ? 'block' : 'none'">
|
||||||
|
Enable post-processing on downloaded files
|
||||||
</label>
|
</label>
|
||||||
|
<small class="settings-hint">Master toggle — disabling this skips all metadata, artwork, and lyrics processing</small>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="form-group">
|
<div id="post-processing-options">
|
||||||
<label class="checkbox-label">
|
<!-- Core Features -->
|
||||||
<input type="checkbox" id="embed-album-art" checked>
|
<div class="post-processing-section">
|
||||||
Embed high-quality album art from Spotify
|
<div class="post-processing-section-title">Core Features</div>
|
||||||
</label>
|
<div class="form-group">
|
||||||
</div>
|
<label class="checkbox-label">
|
||||||
|
<input type="checkbox" id="embed-album-art" checked>
|
||||||
|
Embed album art into file
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label class="checkbox-label">
|
||||||
|
<input type="checkbox" id="cover-art-download" checked>
|
||||||
|
Download cover.jpg to album folder
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label class="checkbox-label">
|
||||||
|
<input type="checkbox" id="lrclib-enabled" checked>
|
||||||
|
Generate .lrc lyrics files (LRClib)
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="form-group">
|
<!-- Metadata Services -->
|
||||||
<label>Supported Formats:</label>
|
<div class="post-processing-section">
|
||||||
<div class="supported-formats">MP3, FLAC, MP4/M4A, OGG</div>
|
<div class="post-processing-section-title">Metadata Services</div>
|
||||||
|
<small class="settings-hint" style="margin-bottom: 8px; display: block;">Choose which services embed IDs, genres, and metadata into your files during download</small>
|
||||||
|
|
||||||
|
<div class="post-processing-grid">
|
||||||
|
<label class="checkbox-label">
|
||||||
|
<input type="checkbox" id="embed-musicbrainz" checked>
|
||||||
|
MusicBrainz
|
||||||
|
</label>
|
||||||
|
<label class="checkbox-label">
|
||||||
|
<input type="checkbox" id="embed-deezer" checked>
|
||||||
|
Deezer
|
||||||
|
</label>
|
||||||
|
<label class="checkbox-label">
|
||||||
|
<input type="checkbox" id="embed-audiodb" checked>
|
||||||
|
AudioDB
|
||||||
|
</label>
|
||||||
|
<label class="checkbox-label">
|
||||||
|
<input type="checkbox" id="embed-tidal" checked>
|
||||||
|
Tidal
|
||||||
|
</label>
|
||||||
|
<label class="checkbox-label">
|
||||||
|
<input type="checkbox" id="embed-qobuz" checked>
|
||||||
|
Qobuz
|
||||||
|
</label>
|
||||||
|
<label class="checkbox-label">
|
||||||
|
<input type="checkbox" id="embed-lastfm" checked>
|
||||||
|
Last.fm
|
||||||
|
</label>
|
||||||
|
<label class="checkbox-label">
|
||||||
|
<input type="checkbox" id="embed-genius" checked>
|
||||||
|
Genius
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-group">
|
||||||
|
<label>Supported Formats:</label>
|
||||||
|
<div class="supported-formats">MP3, FLAC, MP4/M4A, OGG</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -4827,9 +4827,19 @@ async function loadSettingsData() {
|
||||||
// Populate Database settings
|
// Populate Database settings
|
||||||
document.getElementById('max-workers').value = settings.database?.max_workers || '5';
|
document.getElementById('max-workers').value = settings.database?.max_workers || '5';
|
||||||
|
|
||||||
// Populate Metadata Enhancement settings
|
// Populate Post-Processing settings
|
||||||
document.getElementById('metadata-enabled').checked = settings.metadata_enhancement?.enabled !== false;
|
document.getElementById('metadata-enabled').checked = settings.metadata_enhancement?.enabled !== false;
|
||||||
document.getElementById('embed-album-art').checked = settings.metadata_enhancement?.embed_album_art !== false;
|
document.getElementById('embed-album-art').checked = settings.metadata_enhancement?.embed_album_art !== false;
|
||||||
|
document.getElementById('cover-art-download').checked = settings.metadata_enhancement?.cover_art_download !== false;
|
||||||
|
document.getElementById('lrclib-enabled').checked = settings.metadata_enhancement?.lrclib_enabled !== false;
|
||||||
|
document.getElementById('embed-musicbrainz').checked = settings.musicbrainz?.embed_tags !== false;
|
||||||
|
document.getElementById('embed-deezer').checked = settings.deezer?.embed_tags !== false;
|
||||||
|
document.getElementById('embed-audiodb').checked = settings.audiodb?.embed_tags !== false;
|
||||||
|
document.getElementById('embed-tidal').checked = settings.tidal?.embed_tags !== false;
|
||||||
|
document.getElementById('embed-qobuz').checked = settings.qobuz?.embed_tags !== false;
|
||||||
|
document.getElementById('embed-lastfm').checked = settings.lastfm?.embed_tags !== false;
|
||||||
|
document.getElementById('embed-genius').checked = settings.genius?.embed_tags !== false;
|
||||||
|
document.getElementById('post-processing-options').style.display = settings.metadata_enhancement?.enabled !== false ? 'block' : 'none';
|
||||||
|
|
||||||
// Populate File Organization settings
|
// Populate File Organization settings
|
||||||
document.getElementById('file-organization-enabled').checked = settings.file_organization?.enabled !== false;
|
document.getElementById('file-organization-enabled').checked = settings.file_organization?.enabled !== false;
|
||||||
|
|
@ -5571,7 +5581,8 @@ async function saveSettings(quiet = false) {
|
||||||
tidal: {
|
tidal: {
|
||||||
client_id: document.getElementById('tidal-client-id').value,
|
client_id: document.getElementById('tidal-client-id').value,
|
||||||
client_secret: document.getElementById('tidal-client-secret').value,
|
client_secret: document.getElementById('tidal-client-secret').value,
|
||||||
redirect_uri: document.getElementById('tidal-redirect-uri').value
|
redirect_uri: document.getElementById('tidal-redirect-uri').value,
|
||||||
|
embed_tags: document.getElementById('embed-tidal').checked
|
||||||
},
|
},
|
||||||
plex: {
|
plex: {
|
||||||
base_url: document.getElementById('plex-url').value,
|
base_url: document.getElementById('plex-url').value,
|
||||||
|
|
@ -5604,10 +5615,12 @@ async function saveSettings(quiet = false) {
|
||||||
enabled: document.getElementById('acoustid-enabled').checked
|
enabled: document.getElementById('acoustid-enabled').checked
|
||||||
},
|
},
|
||||||
lastfm: {
|
lastfm: {
|
||||||
api_key: document.getElementById('lastfm-api-key').value
|
api_key: document.getElementById('lastfm-api-key').value,
|
||||||
|
embed_tags: document.getElementById('embed-lastfm').checked
|
||||||
},
|
},
|
||||||
genius: {
|
genius: {
|
||||||
access_token: document.getElementById('genius-access-token').value
|
access_token: document.getElementById('genius-access-token').value,
|
||||||
|
embed_tags: document.getElementById('embed-genius').checked
|
||||||
},
|
},
|
||||||
itunes: {
|
itunes: {
|
||||||
country: document.getElementById('itunes-country').value || 'US'
|
country: document.getElementById('itunes-country').value || 'US'
|
||||||
|
|
@ -5621,14 +5634,26 @@ async function saveSettings(quiet = false) {
|
||||||
quality: document.getElementById('tidal-download-quality').value || 'lossless'
|
quality: document.getElementById('tidal-download-quality').value || 'lossless'
|
||||||
},
|
},
|
||||||
qobuz: {
|
qobuz: {
|
||||||
quality: document.getElementById('qobuz-quality').value || 'lossless'
|
quality: document.getElementById('qobuz-quality').value || 'lossless',
|
||||||
|
embed_tags: document.getElementById('embed-qobuz').checked
|
||||||
},
|
},
|
||||||
database: {
|
database: {
|
||||||
max_workers: parseInt(document.getElementById('max-workers').value)
|
max_workers: parseInt(document.getElementById('max-workers').value)
|
||||||
},
|
},
|
||||||
metadata_enhancement: {
|
metadata_enhancement: {
|
||||||
enabled: document.getElementById('metadata-enabled').checked,
|
enabled: document.getElementById('metadata-enabled').checked,
|
||||||
embed_album_art: document.getElementById('embed-album-art').checked
|
embed_album_art: document.getElementById('embed-album-art').checked,
|
||||||
|
cover_art_download: document.getElementById('cover-art-download').checked,
|
||||||
|
lrclib_enabled: document.getElementById('lrclib-enabled').checked
|
||||||
|
},
|
||||||
|
musicbrainz: {
|
||||||
|
embed_tags: document.getElementById('embed-musicbrainz').checked
|
||||||
|
},
|
||||||
|
deezer: {
|
||||||
|
embed_tags: document.getElementById('embed-deezer').checked
|
||||||
|
},
|
||||||
|
audiodb: {
|
||||||
|
embed_tags: document.getElementById('embed-audiodb').checked
|
||||||
},
|
},
|
||||||
file_organization: {
|
file_organization: {
|
||||||
enabled: document.getElementById('file-organization-enabled').checked,
|
enabled: document.getElementById('file-organization-enabled').checked,
|
||||||
|
|
|
||||||
|
|
@ -2067,6 +2067,30 @@ body {
|
||||||
padding: 6px 10px;
|
padding: 6px 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Post-Processing Settings */
|
||||||
|
.post-processing-section {
|
||||||
|
background: rgba(255, 255, 255, 0.02);
|
||||||
|
border: 1px solid rgba(255, 255, 255, 0.06);
|
||||||
|
border-radius: 10px;
|
||||||
|
padding: 14px 16px;
|
||||||
|
margin-bottom: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.post-processing-section-title {
|
||||||
|
font-size: 11px;
|
||||||
|
font-weight: 600;
|
||||||
|
text-transform: uppercase;
|
||||||
|
letter-spacing: 1.2px;
|
||||||
|
color: rgba(var(--accent-rgb), 0.8);
|
||||||
|
margin-bottom: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.post-processing-grid {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: 1fr 1fr;
|
||||||
|
gap: 4px 16px;
|
||||||
|
}
|
||||||
|
|
||||||
/* Read-only Fields */
|
/* Read-only Fields */
|
||||||
.readonly-field {
|
.readonly-field {
|
||||||
padding: 8px 12px;
|
padding: 8px 12px;
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue