Watchlist: per-artist 'auto-download' toggle (follow-only) — off = discover/surface releases but skip the wishlist add; default on
This commit is contained in:
parent
2428df1144
commit
651b904e92
7 changed files with 161 additions and 7 deletions
|
|
@ -1376,10 +1376,17 @@ class WatchlistScanner:
|
||||||
if scan_state is not None:
|
if scan_state is not None:
|
||||||
scan_state['tracks_found_this_scan'] += 1
|
scan_state['tracks_found_this_scan'] += 1
|
||||||
|
|
||||||
added = self.add_track_to_wishlist(
|
# "Follow only" artists (auto_download off): discover and
|
||||||
track, album_data, artist,
|
# surface the release exactly like normal, but skip the one
|
||||||
scan_run_id=(scan_state or {}).get('scan_run_id', ''),
|
# call that adds it to the wishlist — so nothing auto-downloads
|
||||||
)
|
# and the user can pick what to grab (corruption's request).
|
||||||
|
if getattr(artist, 'auto_download', True):
|
||||||
|
added = self.add_track_to_wishlist(
|
||||||
|
track, album_data, artist,
|
||||||
|
scan_run_id=(scan_state or {}).get('scan_run_id', ''),
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
added = False
|
||||||
|
|
||||||
track_artists = track.get('artists', [])
|
track_artists = track.get('artists', [])
|
||||||
track_artist_name = track_artists[0].get('name', 'Unknown Artist') if track_artists else 'Unknown Artist'
|
track_artist_name = track_artists[0].get('name', 'Unknown Artist') if track_artists else 'Unknown Artist'
|
||||||
|
|
|
||||||
|
|
@ -102,6 +102,10 @@ class WatchlistArtist:
|
||||||
include_instrumentals: bool = False
|
include_instrumentals: bool = False
|
||||||
lookback_days: Optional[int] = None # Per-artist override; None = use global setting
|
lookback_days: Optional[int] = None # Per-artist override; None = use global setting
|
||||||
preferred_metadata_source: Optional[str] = None # Per-artist override; None = use global setting
|
preferred_metadata_source: Optional[str] = None # Per-artist override; None = use global setting
|
||||||
|
# When False ("follow only"), the watchlist scan still discovers + surfaces new
|
||||||
|
# releases for this artist but does NOT auto-add them to the wishlist (so they
|
||||||
|
# don't auto-download). Default True = current behaviour.
|
||||||
|
auto_download: bool = True
|
||||||
profile_id: int = 1
|
profile_id: int = 1
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
|
|
@ -467,6 +471,12 @@ class MusicDatabase:
|
||||||
self._add_soul_id_columns(cursor)
|
self._add_soul_id_columns(cursor)
|
||||||
self._add_listening_history_table(cursor)
|
self._add_listening_history_table(cursor)
|
||||||
|
|
||||||
|
# Per-artist auto_download ("follow only") column. MUST run after the
|
||||||
|
# profile-support migrations above — those recreate watchlist_artists
|
||||||
|
# from an explicit column list, so any column added before them gets
|
||||||
|
# dropped. Adding it here (after the last recreate) makes it stick.
|
||||||
|
self._add_watchlist_auto_download_column(cursor)
|
||||||
|
|
||||||
# Spotify library cache
|
# Spotify library cache
|
||||||
self._add_spotify_library_cache_table(cursor)
|
self._add_spotify_library_cache_table(cursor)
|
||||||
|
|
||||||
|
|
@ -2158,6 +2168,21 @@ class MusicDatabase:
|
||||||
logger.error(f"Error adding content type filter columns to watchlist_artists: {e}")
|
logger.error(f"Error adding content type filter columns to watchlist_artists: {e}")
|
||||||
# Don't raise - this is a migration, database can still function
|
# Don't raise - this is a migration, database can still function
|
||||||
|
|
||||||
|
def _add_watchlist_auto_download_column(self, cursor):
|
||||||
|
"""Add per-artist auto_download column ("follow only" toggle).
|
||||||
|
|
||||||
|
Default 1 (auto-download) = existing behaviour. When 0, the watchlist scan
|
||||||
|
still finds + surfaces new releases for the artist but skips adding them to
|
||||||
|
the wishlist."""
|
||||||
|
try:
|
||||||
|
cursor.execute("PRAGMA table_info(watchlist_artists)")
|
||||||
|
columns = [column[1] for column in cursor.fetchall()]
|
||||||
|
if 'auto_download' not in columns:
|
||||||
|
cursor.execute("ALTER TABLE watchlist_artists ADD COLUMN auto_download INTEGER NOT NULL DEFAULT 1")
|
||||||
|
logger.info("Added auto_download column to watchlist_artists table")
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"Error adding auto_download column to watchlist_artists: {e}")
|
||||||
|
|
||||||
def _add_watchlist_lookback_days_column(self, cursor):
|
def _add_watchlist_lookback_days_column(self, cursor):
|
||||||
"""Add per-artist lookback_days column to watchlist_artists table"""
|
"""Add per-artist lookback_days column to watchlist_artists table"""
|
||||||
try:
|
try:
|
||||||
|
|
@ -9293,7 +9318,8 @@ class MusicDatabase:
|
||||||
'last_scan_timestamp', 'created_at', 'updated_at']
|
'last_scan_timestamp', 'created_at', 'updated_at']
|
||||||
optional_columns = ['image_url', 'itunes_artist_id', 'deezer_artist_id', 'discogs_artist_id', 'musicbrainz_artist_id', 'include_albums', 'include_eps', 'include_singles',
|
optional_columns = ['image_url', 'itunes_artist_id', 'deezer_artist_id', 'discogs_artist_id', 'musicbrainz_artist_id', 'include_albums', 'include_eps', 'include_singles',
|
||||||
'include_live', 'include_remixes', 'include_acoustic', 'include_compilations',
|
'include_live', 'include_remixes', 'include_acoustic', 'include_compilations',
|
||||||
'include_instrumentals', 'lookback_days', 'preferred_metadata_source']
|
'include_instrumentals', 'lookback_days', 'preferred_metadata_source',
|
||||||
|
'auto_download']
|
||||||
|
|
||||||
columns_to_select = base_columns + [col for col in optional_columns if col in existing_columns]
|
columns_to_select = base_columns + [col for col in optional_columns if col in existing_columns]
|
||||||
|
|
||||||
|
|
@ -9331,6 +9357,7 @@ class MusicDatabase:
|
||||||
include_instrumentals = bool(row['include_instrumentals']) if 'include_instrumentals' in existing_columns else False
|
include_instrumentals = bool(row['include_instrumentals']) if 'include_instrumentals' in existing_columns else False
|
||||||
lookback_days = row['lookback_days'] if 'lookback_days' in existing_columns else None
|
lookback_days = row['lookback_days'] if 'lookback_days' in existing_columns else None
|
||||||
preferred_metadata_source = row['preferred_metadata_source'] if 'preferred_metadata_source' in existing_columns else None
|
preferred_metadata_source = row['preferred_metadata_source'] if 'preferred_metadata_source' in existing_columns else None
|
||||||
|
auto_download = bool(row['auto_download']) if 'auto_download' in existing_columns else True
|
||||||
|
|
||||||
watchlist_artists.append(WatchlistArtist(
|
watchlist_artists.append(WatchlistArtist(
|
||||||
id=row['id'],
|
id=row['id'],
|
||||||
|
|
@ -9355,6 +9382,7 @@ class MusicDatabase:
|
||||||
include_instrumentals=include_instrumentals,
|
include_instrumentals=include_instrumentals,
|
||||||
lookback_days=lookback_days,
|
lookback_days=lookback_days,
|
||||||
preferred_metadata_source=preferred_metadata_source,
|
preferred_metadata_source=preferred_metadata_source,
|
||||||
|
auto_download=auto_download,
|
||||||
profile_id=profile_id
|
profile_id=profile_id
|
||||||
))
|
))
|
||||||
|
|
||||||
|
|
|
||||||
42
tests/test_watchlist_auto_download.py
Normal file
42
tests/test_watchlist_auto_download.py
Normal file
|
|
@ -0,0 +1,42 @@
|
||||||
|
"""DB-level coverage for the per-artist watchlist auto_download ("follow only")
|
||||||
|
toggle — the column migrates in, defaults to auto-download, and round-trips
|
||||||
|
through get_watchlist_artists onto the WatchlistArtist dataclass."""
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from database.music_database import MusicDatabase
|
||||||
|
|
||||||
|
|
||||||
|
def _get(db, profile_id=1):
|
||||||
|
return {a.spotify_artist_id: a for a in db.get_watchlist_artists(profile_id=profile_id)}
|
||||||
|
|
||||||
|
|
||||||
|
def test_auto_download_defaults_true(tmp_path):
|
||||||
|
db = MusicDatabase(str(tmp_path / 'm.db'))
|
||||||
|
assert db.add_artist_to_watchlist('sp1', 'Artist One', profile_id=1)
|
||||||
|
artist = _get(db)['sp1']
|
||||||
|
# New watchlist artists keep the existing behaviour: auto-download on.
|
||||||
|
assert artist.auto_download is True
|
||||||
|
|
||||||
|
|
||||||
|
def test_auto_download_persists_when_disabled(tmp_path):
|
||||||
|
db = MusicDatabase(str(tmp_path / 'm.db'))
|
||||||
|
db.add_artist_to_watchlist('sp1', 'Artist One', profile_id=1)
|
||||||
|
|
||||||
|
# Flip it off the same way the config endpoint's UPDATE does.
|
||||||
|
with db._get_connection() as conn:
|
||||||
|
conn.execute(
|
||||||
|
"UPDATE watchlist_artists SET auto_download = 0 WHERE spotify_artist_id = ?",
|
||||||
|
('sp1',),
|
||||||
|
)
|
||||||
|
conn.commit()
|
||||||
|
|
||||||
|
artist = _get(db)['sp1']
|
||||||
|
assert artist.auto_download is False
|
||||||
|
|
||||||
|
|
||||||
|
def test_auto_download_column_exists_after_migration(tmp_path):
|
||||||
|
db = MusicDatabase(str(tmp_path / 'm.db'))
|
||||||
|
with db._get_connection() as conn:
|
||||||
|
cols = [c[1] for c in conn.execute("PRAGMA table_info(watchlist_artists)").fetchall()]
|
||||||
|
assert 'auto_download' in cols
|
||||||
|
|
@ -446,6 +446,52 @@ def test_scan_watchlist_artists_scans_tracks_and_updates_state(monkeypatch):
|
||||||
assert scan_state["recent_wishlist_additions"][0]["track_name"] == "Track One"
|
assert scan_state["recent_wishlist_additions"][0]["track_name"] == "Track One"
|
||||||
|
|
||||||
|
|
||||||
|
def test_follow_only_artist_discovers_but_skips_wishlist_add(monkeypatch):
|
||||||
|
"""auto_download=False ("follow only"): the scan still finds the new track
|
||||||
|
(counts toward new_tracks_found + the scan summary) but must NOT call
|
||||||
|
add_track_to_wishlist — so nothing auto-downloads (corruption's request)."""
|
||||||
|
monkeypatch.setattr(watchlist_scanner_module, "DELAY_BETWEEN_ARTISTS", 0)
|
||||||
|
monkeypatch.setattr(watchlist_scanner_module, "DELAY_BETWEEN_ALBUMS", 0)
|
||||||
|
|
||||||
|
artist = _build_artist()
|
||||||
|
artist.auto_download = False # follow only
|
||||||
|
album = types.SimpleNamespace(id="album-1", name="Album One")
|
||||||
|
album_data = {
|
||||||
|
"name": "Album One",
|
||||||
|
"images": [{"url": "https://example.com/album.jpg"}],
|
||||||
|
"tracks": {"items": [{
|
||||||
|
"id": "track-1", "name": "Track One", "track_number": 1,
|
||||||
|
"disc_number": 1, "artists": [{"name": "Artist One"}],
|
||||||
|
}]},
|
||||||
|
}
|
||||||
|
scanner = _build_scanner(album_data, [artist])
|
||||||
|
scanner._database.has_fresh_similar_artists = lambda *a, **k: False
|
||||||
|
|
||||||
|
add_calls = []
|
||||||
|
monkeypatch.setattr(scanner, "_backfill_missing_ids", lambda *a, **k: None)
|
||||||
|
monkeypatch.setattr(scanner, "get_artist_image_url", lambda *a, **k: "https://example.com/artist.jpg")
|
||||||
|
monkeypatch.setattr(scanner, "get_artist_discography_for_watchlist", lambda *a, **k: [album])
|
||||||
|
monkeypatch.setattr(scanner, "_get_lookback_period_setting", lambda: "30")
|
||||||
|
monkeypatch.setattr(scanner, "_get_rescan_cutoff", lambda: None)
|
||||||
|
monkeypatch.setattr(scanner, "_should_include_release", lambda *a, **k: True)
|
||||||
|
monkeypatch.setattr(scanner, "_should_include_track", lambda *a, **k: True)
|
||||||
|
monkeypatch.setattr(scanner, "is_track_missing_from_library", lambda *a, **k: True)
|
||||||
|
monkeypatch.setattr(scanner, "add_track_to_wishlist", lambda *a, **k: add_calls.append(a) or True)
|
||||||
|
monkeypatch.setattr(scanner, "update_artist_scan_timestamp", lambda *a, **k: True)
|
||||||
|
monkeypatch.setattr(scanner, "update_similar_artists", lambda *a, **k: True)
|
||||||
|
monkeypatch.setattr(scanner, "_backfill_similar_artists_fallback_ids", lambda *a, **k: 0)
|
||||||
|
|
||||||
|
scan_state = {}
|
||||||
|
results = scanner.scan_watchlist_artists([artist], scan_state=scan_state)
|
||||||
|
|
||||||
|
assert results[0].success is True
|
||||||
|
assert results[0].new_tracks_found == 1 # still discovered/surfaced
|
||||||
|
assert results[0].tracks_added_to_wishlist == 0 # but not auto-added
|
||||||
|
assert add_calls == [] # the one gated call never fired
|
||||||
|
assert scan_state["summary"]["new_tracks_found"] == 1
|
||||||
|
assert scan_state["summary"]["tracks_added_to_wishlist"] == 0
|
||||||
|
|
||||||
|
|
||||||
def test_scan_watchlist_artists_skips_placeholder_tracklists(monkeypatch):
|
def test_scan_watchlist_artists_skips_placeholder_tracklists(monkeypatch):
|
||||||
monkeypatch.setattr(watchlist_scanner_module, "DELAY_BETWEEN_ARTISTS", 0)
|
monkeypatch.setattr(watchlist_scanner_module, "DELAY_BETWEEN_ARTISTS", 0)
|
||||||
monkeypatch.setattr(watchlist_scanner_module, "DELAY_BETWEEN_ALBUMS", 0)
|
monkeypatch.setattr(watchlist_scanner_module, "DELAY_BETWEEN_ALBUMS", 0)
|
||||||
|
|
|
||||||
|
|
@ -27829,7 +27829,7 @@ def watchlist_artist_config(artist_id):
|
||||||
artist_name, image_url, spotify_artist_id, itunes_artist_id,
|
artist_name, image_url, spotify_artist_id, itunes_artist_id,
|
||||||
last_scan_timestamp, date_added, include_instrumentals, deezer_artist_id,
|
last_scan_timestamp, date_added, include_instrumentals, deezer_artist_id,
|
||||||
lookback_days, discogs_artist_id, preferred_metadata_source,
|
lookback_days, discogs_artist_id, preferred_metadata_source,
|
||||||
amazon_artist_id, musicbrainz_artist_id
|
amazon_artist_id, musicbrainz_artist_id, auto_download
|
||||||
FROM watchlist_artists
|
FROM watchlist_artists
|
||||||
WHERE spotify_artist_id = ? OR itunes_artist_id = ? OR deezer_artist_id = ?
|
WHERE spotify_artist_id = ? OR itunes_artist_id = ? OR deezer_artist_id = ?
|
||||||
OR discogs_artist_id = ? OR amazon_artist_id = ? OR musicbrainz_artist_id = ?
|
OR discogs_artist_id = ? OR amazon_artist_id = ? OR musicbrainz_artist_id = ?
|
||||||
|
|
@ -27958,6 +27958,8 @@ def watchlist_artist_config(artist_id):
|
||||||
'date_added': result[12],
|
'date_added': result[12],
|
||||||
'lookback_days': result[15] if len(result) > 15 else None,
|
'lookback_days': result[15] if len(result) > 15 else None,
|
||||||
'preferred_metadata_source': result[17] if len(result) > 17 else None,
|
'preferred_metadata_source': result[17] if len(result) > 17 else None,
|
||||||
|
# follow-only toggle (default True/auto-download when column absent)
|
||||||
|
'auto_download': bool(result[20]) if len(result) > 20 and result[20] is not None else True,
|
||||||
}
|
}
|
||||||
|
|
||||||
from core.metadata.registry import get_primary_source
|
from core.metadata.registry import get_primary_source
|
||||||
|
|
@ -27997,6 +27999,9 @@ def watchlist_artist_config(artist_id):
|
||||||
# Validate — only accept known sources, empty string means clear override
|
# Validate — only accept known sources, empty string means clear override
|
||||||
if preferred_metadata_source == '' or preferred_metadata_source not in ('spotify', 'deezer', 'itunes', 'discogs', 'musicbrainz'):
|
if preferred_metadata_source == '' or preferred_metadata_source not in ('spotify', 'deezer', 'itunes', 'discogs', 'musicbrainz'):
|
||||||
preferred_metadata_source = None
|
preferred_metadata_source = None
|
||||||
|
# Follow-only toggle: default True so an older client that omits the
|
||||||
|
# field keeps auto-downloading.
|
||||||
|
auto_download = bool(data.get('auto_download', True))
|
||||||
|
|
||||||
# Validate at least one release type is selected
|
# Validate at least one release type is selected
|
||||||
if not (include_albums or include_eps or include_singles):
|
if not (include_albums or include_eps or include_singles):
|
||||||
|
|
@ -28021,13 +28026,14 @@ def watchlist_artist_config(artist_id):
|
||||||
SET include_albums = ?, include_eps = ?, include_singles = ?,
|
SET include_albums = ?, include_eps = ?, include_singles = ?,
|
||||||
include_live = ?, include_remixes = ?, include_acoustic = ?, include_compilations = ?,
|
include_live = ?, include_remixes = ?, include_acoustic = ?, include_compilations = ?,
|
||||||
include_instrumentals = ?, lookback_days = ?, preferred_metadata_source = ?,
|
include_instrumentals = ?, lookback_days = ?, preferred_metadata_source = ?,
|
||||||
|
auto_download = ?,
|
||||||
last_scan_timestamp = CASE WHEN ? THEN NULL ELSE last_scan_timestamp END,
|
last_scan_timestamp = CASE WHEN ? THEN NULL ELSE last_scan_timestamp END,
|
||||||
updated_at = CURRENT_TIMESTAMP
|
updated_at = CURRENT_TIMESTAMP
|
||||||
WHERE spotify_artist_id = ? OR itunes_artist_id = ? OR deezer_artist_id = ?
|
WHERE spotify_artist_id = ? OR itunes_artist_id = ? OR deezer_artist_id = ?
|
||||||
OR discogs_artist_id = ? OR musicbrainz_artist_id = ?
|
OR discogs_artist_id = ? OR musicbrainz_artist_id = ?
|
||||||
""", (int(include_albums), int(include_eps), int(include_singles),
|
""", (int(include_albums), int(include_eps), int(include_singles),
|
||||||
int(include_live), int(include_remixes), int(include_acoustic), int(include_compilations),
|
int(include_live), int(include_remixes), int(include_acoustic), int(include_compilations),
|
||||||
int(include_instrumentals), lookback_days, preferred_metadata_source, lookback_changed,
|
int(include_instrumentals), lookback_days, preferred_metadata_source, int(auto_download), lookback_changed,
|
||||||
artist_id, artist_id, artist_id, artist_id, artist_id))
|
artist_id, artist_id, artist_id, artist_id, artist_id))
|
||||||
conn.commit()
|
conn.commit()
|
||||||
|
|
||||||
|
|
@ -28051,6 +28057,7 @@ def watchlist_artist_config(artist_id):
|
||||||
'include_acoustic': include_acoustic,
|
'include_acoustic': include_acoustic,
|
||||||
'include_compilations': include_compilations,
|
'include_compilations': include_compilations,
|
||||||
'include_instrumentals': include_instrumentals,
|
'include_instrumentals': include_instrumentals,
|
||||||
|
'auto_download': auto_download,
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -7756,6 +7756,24 @@
|
||||||
|
|
||||||
<div class="watchlist-artist-config-content">
|
<div class="watchlist-artist-config-content">
|
||||||
<div class="watchlist-artist-config-body">
|
<div class="watchlist-artist-config-body">
|
||||||
|
<div class="config-section">
|
||||||
|
<h3 class="config-section-title">Auto-Download</h3>
|
||||||
|
<p class="config-section-subtitle">When on, new releases are added to the wishlist and downloaded automatically. Turn off to <strong>follow only</strong> — still see new releases in scans, but pick what to download yourself.</p>
|
||||||
|
|
||||||
|
<div class="config-options">
|
||||||
|
<label class="config-option">
|
||||||
|
<input type="checkbox" id="config-auto-download" checked>
|
||||||
|
<div class="config-option-content">
|
||||||
|
<div class="config-option-icon">⬇️</div>
|
||||||
|
<div class="config-option-text">
|
||||||
|
<span class="config-option-title">Auto-download new releases</span>
|
||||||
|
<span class="config-option-description">Off = follow only (discover but don't auto-add to wishlist)</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="config-section">
|
<div class="config-section">
|
||||||
<h3 class="config-section-title">Download Preferences</h3>
|
<h3 class="config-section-title">Download Preferences</h3>
|
||||||
<p class="config-section-subtitle">Select which types of releases to monitor for this artist</p>
|
<p class="config-section-subtitle">Select which types of releases to monitor for this artist</p>
|
||||||
|
|
|
||||||
|
|
@ -2498,6 +2498,9 @@ async function openWatchlistArtistConfigModal(artistId, artistName) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set checkbox states
|
// Set checkbox states
|
||||||
|
// Default to true (auto-download) when absent (older configs).
|
||||||
|
const _autoDlToggle = document.getElementById('config-auto-download');
|
||||||
|
if (_autoDlToggle) _autoDlToggle.checked = config.auto_download !== false;
|
||||||
document.getElementById('config-include-albums').checked = config.include_albums;
|
document.getElementById('config-include-albums').checked = config.include_albums;
|
||||||
document.getElementById('config-include-eps').checked = config.include_eps;
|
document.getElementById('config-include-eps').checked = config.include_eps;
|
||||||
document.getElementById('config-include-singles').checked = config.include_singles;
|
document.getElementById('config-include-singles').checked = config.include_singles;
|
||||||
|
|
@ -2985,6 +2988,8 @@ async function saveWatchlistArtistConfig(artistId) {
|
||||||
const includeAcoustic = document.getElementById('config-include-acoustic').checked;
|
const includeAcoustic = document.getElementById('config-include-acoustic').checked;
|
||||||
const includeCompilations = document.getElementById('config-include-compilations').checked;
|
const includeCompilations = document.getElementById('config-include-compilations').checked;
|
||||||
const includeInstrumentals = document.getElementById('config-include-instrumentals').checked;
|
const includeInstrumentals = document.getElementById('config-include-instrumentals').checked;
|
||||||
|
const autoDownloadEl = document.getElementById('config-auto-download');
|
||||||
|
const autoDownload = autoDownloadEl ? autoDownloadEl.checked : true;
|
||||||
const lookbackDaysVal = document.getElementById('config-lookback-days').value;
|
const lookbackDaysVal = document.getElementById('config-lookback-days').value;
|
||||||
const lookbackDays = lookbackDaysVal !== '' ? parseInt(lookbackDaysVal) : null;
|
const lookbackDays = lookbackDaysVal !== '' ? parseInt(lookbackDaysVal) : null;
|
||||||
const activeSourceBtn = document.querySelector('#config-metadata-source-selector .config-msrc-btn.active');
|
const activeSourceBtn = document.querySelector('#config-metadata-source-selector .config-msrc-btn.active');
|
||||||
|
|
@ -3016,6 +3021,7 @@ async function saveWatchlistArtistConfig(artistId) {
|
||||||
include_acoustic: includeAcoustic,
|
include_acoustic: includeAcoustic,
|
||||||
include_compilations: includeCompilations,
|
include_compilations: includeCompilations,
|
||||||
include_instrumentals: includeInstrumentals,
|
include_instrumentals: includeInstrumentals,
|
||||||
|
auto_download: autoDownload,
|
||||||
lookback_days: lookbackDays,
|
lookback_days: lookbackDays,
|
||||||
preferred_metadata_source: preferredMetadataSource,
|
preferred_metadata_source: preferredMetadataSource,
|
||||||
})
|
})
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue