Add content type filters for watchlist artists
Introduces new filters for live versions, remixes, acoustic versions, and compilation albums to the watchlist artist configuration. Updates the database schema, backend API, and web UI to support these options, allowing users to customize which content types are included for each artist in their watchlist.
This commit is contained in:
parent
d9a8162fb9
commit
a8766828d9
6 changed files with 381 additions and 25 deletions
|
|
@ -85,20 +85,40 @@ class PlexClient:
|
|||
|
||||
def ensure_connection(self) -> bool:
|
||||
"""Ensure connection to Plex server with lazy initialization."""
|
||||
if self._connection_attempted:
|
||||
return self.server is not None
|
||||
|
||||
# If we've successfully connected before and server object exists, return immediately
|
||||
if self._connection_attempted and self.server is not None:
|
||||
return True
|
||||
|
||||
# Prevent concurrent connection attempts
|
||||
if self._is_connecting:
|
||||
return False
|
||||
|
||||
|
||||
self._is_connecting = True
|
||||
try:
|
||||
self._setup_client()
|
||||
return self.server is not None
|
||||
connection_successful = self.server is not None
|
||||
|
||||
# Only mark as attempted if connection succeeded
|
||||
# This allows retries if connection fails
|
||||
if connection_successful:
|
||||
self._connection_attempted = True
|
||||
else:
|
||||
# Reset flag to allow retry on next call
|
||||
self._connection_attempted = False
|
||||
|
||||
return connection_successful
|
||||
finally:
|
||||
self._is_connecting = False
|
||||
self._connection_attempted = True
|
||||
|
||||
|
||||
def reset_connection(self):
|
||||
"""Reset connection state to force reconnection on next ensure_connection() call.
|
||||
Useful when config changes or connection needs to be refreshed."""
|
||||
logger.info("Resetting Plex connection state")
|
||||
self.server = None
|
||||
self.music_library = None
|
||||
self._connection_attempted = False
|
||||
self._last_connection_check = 0
|
||||
|
||||
def _setup_client(self):
|
||||
config = config_manager.get_plex_config()
|
||||
|
||||
|
|
@ -226,13 +246,17 @@ class PlexClient:
|
|||
|
||||
current_time = time.time()
|
||||
|
||||
# Only check connection if enough time has passed or never attempted
|
||||
if (not self._connection_attempted or
|
||||
current_time - self._last_connection_check > self._connection_check_interval):
|
||||
# Always attempt connection if not connected OR cache expired
|
||||
# This ensures we retry failed connections and detect disconnections
|
||||
should_check = (
|
||||
self.server is None or # Not connected - always try
|
||||
current_time - self._last_connection_check > self._connection_check_interval # Cache expired
|
||||
)
|
||||
|
||||
if should_check:
|
||||
self._last_connection_check = current_time
|
||||
|
||||
# Try to connect on first call, but don't block if already connecting
|
||||
# Try to connect, but don't block if already connecting
|
||||
if not self._is_connecting:
|
||||
self.ensure_connection()
|
||||
|
||||
|
|
|
|||
|
|
@ -74,6 +74,150 @@ def clean_track_name_for_search(track_name):
|
|||
|
||||
return cleaned_name
|
||||
|
||||
def is_live_version(track_name: str, album_name: str = "") -> bool:
|
||||
"""
|
||||
Detect if a track or album is a live version.
|
||||
|
||||
Args:
|
||||
track_name: Track name to check
|
||||
album_name: Album name to check (optional)
|
||||
|
||||
Returns:
|
||||
True if this is a live version, False otherwise
|
||||
"""
|
||||
if not track_name:
|
||||
return False
|
||||
|
||||
# Combine track and album names for comprehensive checking
|
||||
text_to_check = f"{track_name} {album_name}".lower()
|
||||
|
||||
# Live version patterns
|
||||
live_patterns = [
|
||||
r'\blive\b', # (Live), Live at, etc.
|
||||
r'\blive at\b', # Live at Madison Square Garden
|
||||
r'\bconcert\b', # Concert, Live Concert
|
||||
r'\bin concert\b', # In Concert
|
||||
r'\bunplugged\b', # MTV Unplugged (usually live)
|
||||
r'\blive session\b', # Live Session
|
||||
r'\blive from\b', # Live from...
|
||||
r'\blive recording\b', # Live Recording
|
||||
r'\bon stage\b', # On Stage
|
||||
]
|
||||
|
||||
for pattern in live_patterns:
|
||||
if re.search(pattern, text_to_check, re.IGNORECASE):
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
def is_remix_version(track_name: str, album_name: str = "") -> bool:
|
||||
"""
|
||||
Detect if a track is a remix.
|
||||
|
||||
Args:
|
||||
track_name: Track name to check
|
||||
album_name: Album name to check (optional)
|
||||
|
||||
Returns:
|
||||
True if this is a remix, False otherwise
|
||||
"""
|
||||
if not track_name:
|
||||
return False
|
||||
|
||||
# Combine track and album names for comprehensive checking
|
||||
text_to_check = f"{track_name} {album_name}".lower()
|
||||
|
||||
# Remix patterns (but NOT remaster/remastered)
|
||||
remix_patterns = [
|
||||
r'\bremix\b', # Remix, Remixed
|
||||
r'\bmix\b(?!.*\bremaster)', # Mix (but not if followed by remaster)
|
||||
r'\bedit\b', # Radio Edit, Extended Edit
|
||||
r'\bversion\b(?=.*\bmix\b)', # Version with Mix (e.g., "Dance Version Mix")
|
||||
r'\bclub mix\b', # Club Mix
|
||||
r'\bdance mix\b', # Dance Mix
|
||||
r'\bradio edit\b', # Radio Edit
|
||||
r'\bextended\b(?=.*\bmix\b)', # Extended Mix
|
||||
r'\bdub\b', # Dub version
|
||||
r'\bvip mix\b', # VIP Mix
|
||||
]
|
||||
|
||||
# But exclude remaster/remastered - those are originals
|
||||
if re.search(r'\bremaster(ed)?\b', text_to_check, re.IGNORECASE):
|
||||
return False
|
||||
|
||||
for pattern in remix_patterns:
|
||||
if re.search(pattern, text_to_check, re.IGNORECASE):
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
def is_acoustic_version(track_name: str, album_name: str = "") -> bool:
|
||||
"""
|
||||
Detect if a track is an acoustic version.
|
||||
|
||||
Args:
|
||||
track_name: Track name to check
|
||||
album_name: Album name to check (optional)
|
||||
|
||||
Returns:
|
||||
True if this is an acoustic version, False otherwise
|
||||
"""
|
||||
if not track_name:
|
||||
return False
|
||||
|
||||
# Combine track and album names for comprehensive checking
|
||||
text_to_check = f"{track_name} {album_name}".lower()
|
||||
|
||||
# Acoustic version patterns
|
||||
acoustic_patterns = [
|
||||
r'\bacoustic\b', # Acoustic, Acoustic Version
|
||||
r'\bstripped\b', # Stripped version
|
||||
r'\bpiano version\b', # Piano Version
|
||||
r'\bunplugged\b', # MTV Unplugged (can be acoustic)
|
||||
]
|
||||
|
||||
for pattern in acoustic_patterns:
|
||||
if re.search(pattern, text_to_check, re.IGNORECASE):
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
def is_compilation_album(album_name: str) -> bool:
|
||||
"""
|
||||
Detect if an album is a compilation/greatest hits album.
|
||||
|
||||
Args:
|
||||
album_name: Album name to check
|
||||
|
||||
Returns:
|
||||
True if this is a compilation album, False otherwise
|
||||
"""
|
||||
if not album_name:
|
||||
return False
|
||||
|
||||
album_lower = album_name.lower()
|
||||
|
||||
# Compilation album patterns
|
||||
compilation_patterns = [
|
||||
r'\bgreatest hits\b', # Greatest Hits
|
||||
r'\bbest of\b', # Best Of
|
||||
r'\banthology\b', # Anthology
|
||||
r'\bcollection\b', # Collection
|
||||
r'\bcompilation\b', # Compilation
|
||||
r'\bthe essential\b', # The Essential...
|
||||
r'\bcomplete\b', # Complete Collection
|
||||
r'\bhits\b', # Hits (standalone or at end)
|
||||
r'\btop\s+\d+\b', # Top 10, Top 40, etc.
|
||||
r'\bvery best\b', # Very Best Of
|
||||
r'\bdefinitive\b', # Definitive Collection
|
||||
]
|
||||
|
||||
for pattern in compilation_patterns:
|
||||
if re.search(pattern, album_lower, re.IGNORECASE):
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
@dataclass
|
||||
class ScanResult:
|
||||
"""Result of scanning a single artist"""
|
||||
|
|
@ -308,9 +452,13 @@ class WatchlistScanner:
|
|||
|
||||
# Check each track
|
||||
for track in tracks:
|
||||
# Check content type filters (live, remix, acoustic, compilation)
|
||||
if not self._should_include_track(track, album_data, watchlist_artist):
|
||||
continue # Skip this track based on content type preferences
|
||||
|
||||
if self.is_track_missing_from_library(track):
|
||||
new_tracks_found += 1
|
||||
|
||||
|
||||
# Add to wishlist
|
||||
if self.add_track_to_wishlist(track, album_data, watchlist_artist):
|
||||
tracks_added_to_wishlist += 1
|
||||
|
|
@ -502,6 +650,71 @@ class WatchlistScanner:
|
|||
logger.warning(f"Error checking release inclusion: {e}")
|
||||
return True # Default to including on error
|
||||
|
||||
def _should_include_track(self, track, album_data, watchlist_artist: WatchlistArtist) -> bool:
|
||||
"""
|
||||
Check if a track should be included based on content type filters.
|
||||
|
||||
Filters:
|
||||
- Live versions
|
||||
- Remixes
|
||||
- Acoustic versions
|
||||
- Compilation albums
|
||||
|
||||
Args:
|
||||
track: Track object or dict
|
||||
album_data: Album data object or dict
|
||||
watchlist_artist: WatchlistArtist object with user preferences
|
||||
|
||||
Returns:
|
||||
True if track should be included, False if should be skipped
|
||||
"""
|
||||
try:
|
||||
# Get track name and album name
|
||||
if isinstance(track, dict):
|
||||
track_name = track.get('name', '')
|
||||
else:
|
||||
track_name = getattr(track, 'name', '')
|
||||
|
||||
if isinstance(album_data, dict):
|
||||
album_name = album_data.get('name', '')
|
||||
else:
|
||||
album_name = getattr(album_data, 'name', '')
|
||||
|
||||
# Get user preferences (default to False = exclude by default)
|
||||
include_live = getattr(watchlist_artist, 'include_live', False)
|
||||
include_remixes = getattr(watchlist_artist, 'include_remixes', False)
|
||||
include_acoustic = getattr(watchlist_artist, 'include_acoustic', False)
|
||||
include_compilations = getattr(watchlist_artist, 'include_compilations', False)
|
||||
|
||||
# Check compilation albums (album-level filter)
|
||||
if not include_compilations:
|
||||
if is_compilation_album(album_name):
|
||||
logger.debug(f"Skipping compilation album: {album_name}")
|
||||
return False
|
||||
|
||||
# Check track content type filters
|
||||
if not include_live:
|
||||
if is_live_version(track_name, album_name):
|
||||
logger.debug(f"Skipping live version: {track_name}")
|
||||
return False
|
||||
|
||||
if not include_remixes:
|
||||
if is_remix_version(track_name, album_name):
|
||||
logger.debug(f"Skipping remix: {track_name}")
|
||||
return False
|
||||
|
||||
if not include_acoustic:
|
||||
if is_acoustic_version(track_name, album_name):
|
||||
logger.debug(f"Skipping acoustic version: {track_name}")
|
||||
return False
|
||||
|
||||
# Track passes all filters
|
||||
return True
|
||||
|
||||
except Exception as e:
|
||||
logger.warning(f"Error checking track content type inclusion: {e}")
|
||||
return True # Default to including on error
|
||||
|
||||
def is_track_missing_from_library(self, track) -> bool:
|
||||
"""
|
||||
Check if a track is missing from the local Plex library.
|
||||
|
|
|
|||
|
|
@ -89,6 +89,10 @@ class WatchlistArtist:
|
|||
include_albums: bool = True
|
||||
include_eps: bool = True
|
||||
include_singles: bool = True
|
||||
include_live: bool = False
|
||||
include_remixes: bool = False
|
||||
include_acoustic: bool = False
|
||||
include_compilations: bool = False
|
||||
|
||||
@dataclass
|
||||
class SimilarArtist:
|
||||
|
|
@ -273,6 +277,9 @@ class MusicDatabase:
|
|||
# Add album type filter columns to watchlist_artists (migration)
|
||||
self._add_watchlist_album_type_filters(cursor)
|
||||
|
||||
# Add content type filter columns to watchlist_artists (migration)
|
||||
self._add_watchlist_content_type_filters(cursor)
|
||||
|
||||
conn.commit()
|
||||
logger.info("Database initialized successfully")
|
||||
|
||||
|
|
@ -623,6 +630,28 @@ class MusicDatabase:
|
|||
logger.error(f"Error adding album type filter columns to watchlist_artists: {e}")
|
||||
# Don't raise - this is a migration, database can still function
|
||||
|
||||
def _add_watchlist_content_type_filters(self, cursor):
|
||||
"""Add content type filter columns to watchlist_artists table"""
|
||||
try:
|
||||
cursor.execute("PRAGMA table_info(watchlist_artists)")
|
||||
columns = [column[1] for column in cursor.fetchall()]
|
||||
|
||||
columns_to_add = {
|
||||
'include_live': ('INTEGER', '0'), # 0 = False (exclude live versions by default)
|
||||
'include_remixes': ('INTEGER', '0'), # 0 = False (exclude remixes by default)
|
||||
'include_acoustic': ('INTEGER', '0'), # 0 = False (exclude acoustic by default)
|
||||
'include_compilations': ('INTEGER', '0') # 0 = False (exclude compilations by default)
|
||||
}
|
||||
|
||||
for column_name, (column_type, default_value) in columns_to_add.items():
|
||||
if column_name not in columns:
|
||||
cursor.execute(f"ALTER TABLE watchlist_artists ADD COLUMN {column_name} {column_type} DEFAULT {default_value}")
|
||||
logger.info(f"Added {column_name} column to watchlist_artists table")
|
||||
|
||||
except Exception as 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
|
||||
|
||||
def close(self):
|
||||
"""Close database connection (no-op since we create connections per operation)"""
|
||||
# Each operation creates and closes its own connection, so nothing to do here
|
||||
|
|
@ -2726,7 +2755,8 @@ class MusicDatabase:
|
|||
# Build SELECT query based on existing columns
|
||||
base_columns = ['id', 'spotify_artist_id', 'artist_name', 'date_added',
|
||||
'last_scan_timestamp', 'created_at', 'updated_at']
|
||||
optional_columns = ['image_url', 'include_albums', 'include_eps', 'include_singles']
|
||||
optional_columns = ['image_url', 'include_albums', 'include_eps', 'include_singles',
|
||||
'include_live', 'include_remixes', 'include_acoustic', 'include_compilations']
|
||||
|
||||
columns_to_select = base_columns + [col for col in optional_columns if col in existing_columns]
|
||||
|
||||
|
|
@ -2745,6 +2775,10 @@ class MusicDatabase:
|
|||
include_albums = bool(row['include_albums']) if 'include_albums' in existing_columns else True
|
||||
include_eps = bool(row['include_eps']) if 'include_eps' in existing_columns else True
|
||||
include_singles = bool(row['include_singles']) if 'include_singles' in existing_columns else True
|
||||
include_live = bool(row['include_live']) if 'include_live' in existing_columns else False
|
||||
include_remixes = bool(row['include_remixes']) if 'include_remixes' in existing_columns else False
|
||||
include_acoustic = bool(row['include_acoustic']) if 'include_acoustic' in existing_columns else False
|
||||
include_compilations = bool(row['include_compilations']) if 'include_compilations' in existing_columns else False
|
||||
|
||||
watchlist_artists.append(WatchlistArtist(
|
||||
id=row['id'],
|
||||
|
|
@ -2757,7 +2791,11 @@ class MusicDatabase:
|
|||
image_url=image_url,
|
||||
include_albums=include_albums,
|
||||
include_eps=include_eps,
|
||||
include_singles=include_singles
|
||||
include_singles=include_singles,
|
||||
include_live=include_live,
|
||||
include_remixes=include_remixes,
|
||||
include_acoustic=include_acoustic,
|
||||
include_compilations=include_compilations
|
||||
))
|
||||
|
||||
return watchlist_artists
|
||||
|
|
|
|||
|
|
@ -17376,7 +17376,9 @@ def watchlist_artist_config(artist_id):
|
|||
conn = sqlite3.connect(str(database.database_path))
|
||||
cursor = conn.cursor()
|
||||
cursor.execute("""
|
||||
SELECT include_albums, include_eps, include_singles, artist_name, image_url
|
||||
SELECT include_albums, include_eps, include_singles,
|
||||
include_live, include_remixes, include_acoustic, include_compilations,
|
||||
artist_name, image_url
|
||||
FROM watchlist_artists
|
||||
WHERE spotify_artist_id = ?
|
||||
""", (artist_id,))
|
||||
|
|
@ -17407,8 +17409,8 @@ def watchlist_artist_config(artist_id):
|
|||
if not artist_info:
|
||||
artist_info = {
|
||||
'id': artist_id,
|
||||
'name': result[3], # artist_name
|
||||
'image_url': result[4], # image_url
|
||||
'name': result[7], # artist_name
|
||||
'image_url': result[8], # image_url
|
||||
'followers': 0,
|
||||
'popularity': 0,
|
||||
'genres': []
|
||||
|
|
@ -17417,7 +17419,11 @@ def watchlist_artist_config(artist_id):
|
|||
config = {
|
||||
'include_albums': bool(result[0]), # Convert INTEGER to boolean
|
||||
'include_eps': bool(result[1]),
|
||||
'include_singles': bool(result[2])
|
||||
'include_singles': bool(result[2]),
|
||||
'include_live': bool(result[3]),
|
||||
'include_remixes': bool(result[4]),
|
||||
'include_acoustic': bool(result[5]),
|
||||
'include_compilations': bool(result[6])
|
||||
}
|
||||
|
||||
return jsonify({
|
||||
|
|
@ -17434,8 +17440,12 @@ def watchlist_artist_config(artist_id):
|
|||
include_albums = data.get('include_albums', True)
|
||||
include_eps = data.get('include_eps', True)
|
||||
include_singles = data.get('include_singles', True)
|
||||
include_live = data.get('include_live', False)
|
||||
include_remixes = data.get('include_remixes', False)
|
||||
include_acoustic = data.get('include_acoustic', False)
|
||||
include_compilations = data.get('include_compilations', False)
|
||||
|
||||
# Validate at least one is selected
|
||||
# Validate at least one release type is selected
|
||||
if not (include_albums or include_eps or include_singles):
|
||||
return jsonify({"success": False, "error": "At least one release type must be selected"}), 400
|
||||
|
||||
|
|
@ -17444,9 +17454,13 @@ def watchlist_artist_config(artist_id):
|
|||
cursor = conn.cursor()
|
||||
cursor.execute("""
|
||||
UPDATE watchlist_artists
|
||||
SET include_albums = ?, include_eps = ?, include_singles = ?, updated_at = CURRENT_TIMESTAMP
|
||||
SET include_albums = ?, include_eps = ?, include_singles = ?,
|
||||
include_live = ?, include_remixes = ?, include_acoustic = ?, include_compilations = ?,
|
||||
updated_at = CURRENT_TIMESTAMP
|
||||
WHERE spotify_artist_id = ?
|
||||
""", (int(include_albums), int(include_eps), int(include_singles), artist_id))
|
||||
""", (int(include_albums), int(include_eps), int(include_singles),
|
||||
int(include_live), int(include_remixes), int(include_acoustic), int(include_compilations),
|
||||
artist_id))
|
||||
conn.commit()
|
||||
|
||||
if cursor.rowcount == 0:
|
||||
|
|
@ -17455,7 +17469,7 @@ def watchlist_artist_config(artist_id):
|
|||
|
||||
conn.close()
|
||||
|
||||
print(f"✅ Updated watchlist config for artist {artist_id}: albums={include_albums}, eps={include_eps}, singles={include_singles}")
|
||||
print(f"✅ Updated watchlist config for artist {artist_id}: albums={include_albums}, eps={include_eps}, singles={include_singles}, live={include_live}, remixes={include_remixes}, acoustic={include_acoustic}, compilations={include_compilations}")
|
||||
|
||||
return jsonify({
|
||||
"success": True,
|
||||
|
|
@ -17463,7 +17477,11 @@ def watchlist_artist_config(artist_id):
|
|||
"config": {
|
||||
'include_albums': include_albums,
|
||||
'include_eps': include_eps,
|
||||
'include_singles': include_singles
|
||||
'include_singles': include_singles,
|
||||
'include_live': include_live,
|
||||
'include_remixes': include_remixes,
|
||||
'include_acoustic': include_acoustic,
|
||||
'include_compilations': include_compilations
|
||||
}
|
||||
})
|
||||
|
||||
|
|
|
|||
|
|
@ -3302,6 +3302,57 @@
|
|||
</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="config-section">
|
||||
<h3 class="config-section-title">Content Filters</h3>
|
||||
<p class="config-section-subtitle">Check to INCLUDE, leave unchecked to EXCLUDE (default: all excluded)</p>
|
||||
|
||||
<div class="config-options">
|
||||
<label class="config-option">
|
||||
<input type="checkbox" id="config-include-live">
|
||||
<div class="config-option-content">
|
||||
<div class="config-option-icon">🎤</div>
|
||||
<div class="config-option-text">
|
||||
<span class="config-option-title">Include Live Versions</span>
|
||||
<span class="config-option-description">Check to include live performances and concerts</span>
|
||||
</div>
|
||||
</div>
|
||||
</label>
|
||||
|
||||
<label class="config-option">
|
||||
<input type="checkbox" id="config-include-remixes">
|
||||
<div class="config-option-content">
|
||||
<div class="config-option-icon">🎧</div>
|
||||
<div class="config-option-text">
|
||||
<span class="config-option-title">Include Remixes</span>
|
||||
<span class="config-option-description">Check to include remix versions and edits</span>
|
||||
</div>
|
||||
</div>
|
||||
</label>
|
||||
|
||||
<label class="config-option">
|
||||
<input type="checkbox" id="config-include-acoustic">
|
||||
<div class="config-option-content">
|
||||
<div class="config-option-icon">🎸</div>
|
||||
<div class="config-option-text">
|
||||
<span class="config-option-title">Include Acoustic Versions</span>
|
||||
<span class="config-option-description">Check to include acoustic and stripped versions</span>
|
||||
</div>
|
||||
</div>
|
||||
</label>
|
||||
|
||||
<label class="config-option">
|
||||
<input type="checkbox" id="config-include-compilations">
|
||||
<div class="config-option-content">
|
||||
<div class="config-option-icon">📀</div>
|
||||
<div class="config-option-text">
|
||||
<span class="config-option-title">Include Compilations</span>
|
||||
<span class="config-option-description">Check to include greatest hits and collections</span>
|
||||
</div>
|
||||
</div>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="watchlist-artist-config-footer">
|
||||
|
|
|
|||
|
|
@ -23572,6 +23572,10 @@ async function openWatchlistArtistConfigModal(artistId, artistName) {
|
|||
document.getElementById('config-include-albums').checked = config.include_albums;
|
||||
document.getElementById('config-include-eps').checked = config.include_eps;
|
||||
document.getElementById('config-include-singles').checked = config.include_singles;
|
||||
document.getElementById('config-include-live').checked = config.include_live || false;
|
||||
document.getElementById('config-include-remixes').checked = config.include_remixes || false;
|
||||
document.getElementById('config-include-acoustic').checked = config.include_acoustic || false;
|
||||
document.getElementById('config-include-compilations').checked = config.include_compilations || false;
|
||||
|
||||
// Store artist ID for saving
|
||||
const modal = document.getElementById('watchlist-artist-config-modal');
|
||||
|
|
@ -23627,8 +23631,12 @@ async function saveWatchlistArtistConfig(artistId) {
|
|||
const includeAlbums = document.getElementById('config-include-albums').checked;
|
||||
const includeEps = document.getElementById('config-include-eps').checked;
|
||||
const includeSingles = document.getElementById('config-include-singles').checked;
|
||||
const includeLive = document.getElementById('config-include-live').checked;
|
||||
const includeRemixes = document.getElementById('config-include-remixes').checked;
|
||||
const includeAcoustic = document.getElementById('config-include-acoustic').checked;
|
||||
const includeCompilations = document.getElementById('config-include-compilations').checked;
|
||||
|
||||
// Validate at least one is selected
|
||||
// Validate at least one release type is selected
|
||||
if (!includeAlbums && !includeEps && !includeSingles) {
|
||||
showToast('Please select at least one release type', 'error');
|
||||
return;
|
||||
|
|
@ -23648,7 +23656,11 @@ async function saveWatchlistArtistConfig(artistId) {
|
|||
body: JSON.stringify({
|
||||
include_albums: includeAlbums,
|
||||
include_eps: includeEps,
|
||||
include_singles: includeSingles
|
||||
include_singles: includeSingles,
|
||||
include_live: includeLive,
|
||||
include_remixes: includeRemixes,
|
||||
include_acoustic: includeAcoustic,
|
||||
include_compilations: includeCompilations
|
||||
})
|
||||
});
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue