fixed #142
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
waitress
|
||||
celery
|
||||
flask
|
||||
flask_cors
|
||||
deezspot-spotizerr
|
||||
waitress==3.0.2
|
||||
celery==5.5.3
|
||||
Flask==3.1.1
|
||||
flask_cors==6.0.0
|
||||
deezspot-spotizerr==1.1
|
||||
|
||||
@@ -22,7 +22,7 @@ from routes.utils.watch.db import (
|
||||
remove_specific_albums_from_artist_table,
|
||||
is_album_in_artist_db
|
||||
)
|
||||
from routes.utils.watch.manager import check_watched_artists
|
||||
from routes.utils.watch.manager import check_watched_artists, get_watch_config
|
||||
from routes.utils.get_info import get_spotify_info
|
||||
|
||||
artist_bp = Blueprint('artist', __name__, url_prefix='/api/artist')
|
||||
@@ -159,6 +159,10 @@ def get_artist_info():
|
||||
@artist_bp.route('/watch/<string:artist_spotify_id>', methods=['PUT'])
|
||||
def add_artist_to_watchlist(artist_spotify_id):
|
||||
"""Adds an artist to the watchlist."""
|
||||
watch_config = get_watch_config()
|
||||
if not watch_config.get("enabled", False):
|
||||
return jsonify({"error": "Watch feature is currently disabled globally."}), 403
|
||||
|
||||
logger.info(f"Attempting to add artist {artist_spotify_id} to watchlist.")
|
||||
try:
|
||||
if get_watched_artist(artist_spotify_id):
|
||||
@@ -224,6 +228,10 @@ def get_artist_watch_status(artist_spotify_id):
|
||||
@artist_bp.route('/watch/<string:artist_spotify_id>', methods=['DELETE'])
|
||||
def remove_artist_from_watchlist(artist_spotify_id):
|
||||
"""Removes an artist from the watchlist."""
|
||||
watch_config = get_watch_config()
|
||||
if not watch_config.get("enabled", False):
|
||||
return jsonify({"error": "Watch feature is currently disabled globally."}), 403
|
||||
|
||||
logger.info(f"Attempting to remove artist {artist_spotify_id} from watchlist.")
|
||||
try:
|
||||
if not get_watched_artist(artist_spotify_id):
|
||||
@@ -249,6 +257,10 @@ def list_watched_artists_endpoint():
|
||||
@artist_bp.route('/watch/trigger_check', methods=['POST'])
|
||||
def trigger_artist_check_endpoint():
|
||||
"""Manually triggers the artist checking mechanism for all watched artists."""
|
||||
watch_config = get_watch_config()
|
||||
if not watch_config.get("enabled", False):
|
||||
return jsonify({"error": "Watch feature is currently disabled globally. Cannot trigger check."}), 403
|
||||
|
||||
logger.info("Manual trigger for artist check received for all artists.")
|
||||
try:
|
||||
thread = threading.Thread(target=check_watched_artists, args=(None,))
|
||||
@@ -261,6 +273,10 @@ def trigger_artist_check_endpoint():
|
||||
@artist_bp.route('/watch/trigger_check/<string:artist_spotify_id>', methods=['POST'])
|
||||
def trigger_specific_artist_check_endpoint(artist_spotify_id: str):
|
||||
"""Manually triggers the artist checking mechanism for a specific artist."""
|
||||
watch_config = get_watch_config()
|
||||
if not watch_config.get("enabled", False):
|
||||
return jsonify({"error": "Watch feature is currently disabled globally. Cannot trigger check."}), 403
|
||||
|
||||
logger.info(f"Manual trigger for specific artist check received for ID: {artist_spotify_id}")
|
||||
try:
|
||||
watched_artist = get_watched_artist(artist_spotify_id)
|
||||
@@ -279,6 +295,10 @@ def trigger_specific_artist_check_endpoint(artist_spotify_id: str):
|
||||
@artist_bp.route('/watch/<string:artist_spotify_id>/albums', methods=['POST'])
|
||||
def mark_albums_as_known_for_artist(artist_spotify_id):
|
||||
"""Fetches details for given album IDs and adds/updates them in the artist's local DB table."""
|
||||
watch_config = get_watch_config()
|
||||
if not watch_config.get("enabled", False):
|
||||
return jsonify({"error": "Watch feature is currently disabled globally. Cannot mark albums."}), 403
|
||||
|
||||
logger.info(f"Attempting to mark albums as known for artist {artist_spotify_id}.")
|
||||
try:
|
||||
album_ids = request.json
|
||||
@@ -313,6 +333,10 @@ def mark_albums_as_known_for_artist(artist_spotify_id):
|
||||
@artist_bp.route('/watch/<string:artist_spotify_id>/albums', methods=['DELETE'])
|
||||
def mark_albums_as_missing_locally_for_artist(artist_spotify_id):
|
||||
"""Removes specified albums from the artist's local DB table."""
|
||||
watch_config = get_watch_config()
|
||||
if not watch_config.get("enabled", False):
|
||||
return jsonify({"error": "Watch feature is currently disabled globally. Cannot mark albums."}), 403
|
||||
|
||||
logger.info(f"Attempting to mark albums as missing (delete locally) for artist {artist_spotify_id}.")
|
||||
try:
|
||||
album_ids = request.json
|
||||
|
||||
@@ -20,7 +20,7 @@ from routes.utils.watch.db import (
|
||||
is_track_in_playlist_db # Added import
|
||||
)
|
||||
from routes.utils.get_info import get_spotify_info # Already used, but ensure it's here
|
||||
from routes.utils.watch.manager import check_watched_playlists # For manual trigger
|
||||
from routes.utils.watch.manager import check_watched_playlists, get_watch_config # For manual trigger & config
|
||||
|
||||
logger = logging.getLogger(__name__) # Added logger initialization
|
||||
playlist_bp = Blueprint('playlist', __name__, url_prefix='/api/playlist')
|
||||
@@ -180,6 +180,10 @@ def get_playlist_info():
|
||||
@playlist_bp.route('/watch/<string:playlist_spotify_id>', methods=['PUT'])
|
||||
def add_to_watchlist(playlist_spotify_id):
|
||||
"""Adds a playlist to the watchlist."""
|
||||
watch_config = get_watch_config()
|
||||
if not watch_config.get("enabled", False):
|
||||
return jsonify({"error": "Watch feature is currently disabled globally."}), 403
|
||||
|
||||
logger.info(f"Attempting to add playlist {playlist_spotify_id} to watchlist.")
|
||||
try:
|
||||
# Check if already watched
|
||||
@@ -227,6 +231,10 @@ def get_playlist_watch_status(playlist_spotify_id):
|
||||
@playlist_bp.route('/watch/<string:playlist_spotify_id>', methods=['DELETE'])
|
||||
def remove_from_watchlist(playlist_spotify_id):
|
||||
"""Removes a playlist from the watchlist."""
|
||||
watch_config = get_watch_config()
|
||||
if not watch_config.get("enabled", False):
|
||||
return jsonify({"error": "Watch feature is currently disabled globally."}), 403
|
||||
|
||||
logger.info(f"Attempting to remove playlist {playlist_spotify_id} from watchlist.")
|
||||
try:
|
||||
if not get_watched_playlist(playlist_spotify_id):
|
||||
@@ -242,6 +250,10 @@ def remove_from_watchlist(playlist_spotify_id):
|
||||
@playlist_bp.route('/watch/<string:playlist_spotify_id>/tracks', methods=['POST'])
|
||||
def mark_tracks_as_known(playlist_spotify_id):
|
||||
"""Fetches details for given track IDs and adds/updates them in the playlist's local DB table."""
|
||||
watch_config = get_watch_config()
|
||||
if not watch_config.get("enabled", False):
|
||||
return jsonify({"error": "Watch feature is currently disabled globally. Cannot mark tracks."}), 403
|
||||
|
||||
logger.info(f"Attempting to mark tracks as known for playlist {playlist_spotify_id}.")
|
||||
try:
|
||||
track_ids = request.json
|
||||
@@ -275,7 +287,11 @@ def mark_tracks_as_known(playlist_spotify_id):
|
||||
@playlist_bp.route('/watch/<string:playlist_spotify_id>/tracks', methods=['DELETE'])
|
||||
def mark_tracks_as_missing_locally(playlist_spotify_id):
|
||||
"""Removes specified tracks from the playlist's local DB table."""
|
||||
logger.info(f"Attempting to mark tracks as missing (delete locally) for playlist {playlist_spotify_id}.")
|
||||
watch_config = get_watch_config()
|
||||
if not watch_config.get("enabled", False):
|
||||
return jsonify({"error": "Watch feature is currently disabled globally. Cannot mark tracks."}), 403
|
||||
|
||||
logger.info(f"Attempting to mark tracks as missing (remove locally) for playlist {playlist_spotify_id}.")
|
||||
try:
|
||||
track_ids = request.json
|
||||
if not isinstance(track_ids, list) or not all(isinstance(tid, str) for tid in track_ids):
|
||||
@@ -304,6 +320,10 @@ def list_watched_playlists_endpoint():
|
||||
@playlist_bp.route('/watch/trigger_check', methods=['POST'])
|
||||
def trigger_playlist_check_endpoint():
|
||||
"""Manually triggers the playlist checking mechanism for all watched playlists."""
|
||||
watch_config = get_watch_config()
|
||||
if not watch_config.get("enabled", False):
|
||||
return jsonify({"error": "Watch feature is currently disabled globally. Cannot trigger check."}), 403
|
||||
|
||||
logger.info("Manual trigger for playlist check received for all playlists.")
|
||||
try:
|
||||
# Run check_watched_playlists without an ID to check all
|
||||
@@ -317,6 +337,10 @@ def trigger_playlist_check_endpoint():
|
||||
@playlist_bp.route('/watch/trigger_check/<string:playlist_spotify_id>', methods=['POST'])
|
||||
def trigger_specific_playlist_check_endpoint(playlist_spotify_id: str):
|
||||
"""Manually triggers the playlist checking mechanism for a specific playlist."""
|
||||
watch_config = get_watch_config()
|
||||
if not watch_config.get("enabled", False):
|
||||
return jsonify({"error": "Watch feature is currently disabled globally. Cannot trigger check."}), 403
|
||||
|
||||
logger.info(f"Manual trigger for specific playlist check received for ID: {playlist_spotify_id}")
|
||||
try:
|
||||
# Check if the playlist is actually in the watchlist first
|
||||
|
||||
@@ -73,6 +73,42 @@ document.addEventListener('DOMContentLoaded', () => {
|
||||
downloadQueue.toggleVisibility();
|
||||
});
|
||||
}
|
||||
|
||||
// Attempt to set initial watchlist button visibility from cache
|
||||
const watchlistButton = document.getElementById('watchlistButton') as HTMLAnchorElement | null;
|
||||
if (watchlistButton) {
|
||||
const cachedWatchEnabled = localStorage.getItem('spotizerr_watch_enabled_cached');
|
||||
if (cachedWatchEnabled === 'true') {
|
||||
watchlistButton.classList.remove('hidden');
|
||||
}
|
||||
}
|
||||
|
||||
// Fetch watch config to determine if watchlist button should be visible
|
||||
async function updateWatchlistButtonVisibility() {
|
||||
if (watchlistButton) {
|
||||
try {
|
||||
const response = await fetch('/api/config/watch');
|
||||
if (response.ok) {
|
||||
const watchConfig = await response.json();
|
||||
localStorage.setItem('spotizerr_watch_enabled_cached', watchConfig.enabled ? 'true' : 'false');
|
||||
if (watchConfig && watchConfig.enabled === false) {
|
||||
watchlistButton.classList.add('hidden');
|
||||
} else {
|
||||
watchlistButton.classList.remove('hidden'); // Ensure it's shown if enabled
|
||||
}
|
||||
} else {
|
||||
console.error('Failed to fetch watch config, defaulting to hidden');
|
||||
// Don't update cache on error
|
||||
watchlistButton.classList.add('hidden'); // Hide if config fetch fails
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error fetching watch config:', error);
|
||||
// Don't update cache on error
|
||||
watchlistButton.classList.add('hidden'); // Hide on error
|
||||
}
|
||||
}
|
||||
}
|
||||
updateWatchlistButtonVisibility();
|
||||
});
|
||||
|
||||
function renderAlbum(album: Album) {
|
||||
|
||||
123
src/js/artist.ts
123
src/js/artist.ts
@@ -46,7 +46,28 @@ interface WatchStatusResponse {
|
||||
artist_data?: any; // The artist data from DB if watched
|
||||
}
|
||||
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
// Added: Interface for global watch config
|
||||
interface GlobalWatchConfig {
|
||||
enabled: boolean;
|
||||
[key: string]: any;
|
||||
}
|
||||
|
||||
// Added: Helper function to fetch global watch config
|
||||
async function getGlobalWatchConfig(): Promise<GlobalWatchConfig> {
|
||||
try {
|
||||
const response = await fetch('/api/config/watch');
|
||||
if (!response.ok) {
|
||||
console.error('Failed to fetch global watch config, assuming disabled.');
|
||||
return { enabled: false }; // Default to disabled on error
|
||||
}
|
||||
return await response.json() as GlobalWatchConfig;
|
||||
} catch (error) {
|
||||
console.error('Error fetching global watch config:', error);
|
||||
return { enabled: false }; // Default to disabled on error
|
||||
}
|
||||
}
|
||||
|
||||
document.addEventListener('DOMContentLoaded', async () => {
|
||||
const pathSegments = window.location.pathname.split('/');
|
||||
const artistId = pathSegments[pathSegments.indexOf('artist') + 1];
|
||||
|
||||
@@ -55,13 +76,16 @@ document.addEventListener('DOMContentLoaded', () => {
|
||||
return;
|
||||
}
|
||||
|
||||
const globalWatchConfig = await getGlobalWatchConfig(); // Fetch global config
|
||||
const isGlobalWatchActuallyEnabled = globalWatchConfig.enabled;
|
||||
|
||||
// Fetch artist info directly
|
||||
fetch(`/api/artist/info?id=${encodeURIComponent(artistId)}`)
|
||||
.then(response => {
|
||||
if (!response.ok) throw new Error('Network response was not ok');
|
||||
return response.json() as Promise<ArtistData>;
|
||||
})
|
||||
.then(data => renderArtist(data, artistId))
|
||||
.then(data => renderArtist(data, artistId, isGlobalWatchActuallyEnabled))
|
||||
.catch(error => {
|
||||
console.error('Error:', error);
|
||||
showError('Failed to load artist info.');
|
||||
@@ -72,11 +96,47 @@ document.addEventListener('DOMContentLoaded', () => {
|
||||
queueIcon.addEventListener('click', () => downloadQueue.toggleVisibility());
|
||||
}
|
||||
|
||||
// Attempt to set initial watchlist button visibility from cache
|
||||
const watchlistButton = document.getElementById('watchlistButton') as HTMLAnchorElement | null;
|
||||
if (watchlistButton) {
|
||||
const cachedWatchEnabled = localStorage.getItem('spotizerr_watch_enabled_cached');
|
||||
if (cachedWatchEnabled === 'true') {
|
||||
watchlistButton.classList.remove('hidden');
|
||||
}
|
||||
}
|
||||
|
||||
// Fetch watch config to determine if watchlist button should be visible
|
||||
async function updateWatchlistButtonVisibility() {
|
||||
if (watchlistButton) {
|
||||
try {
|
||||
const response = await fetch('/api/config/watch');
|
||||
if (response.ok) {
|
||||
const watchConfig = await response.json();
|
||||
localStorage.setItem('spotizerr_watch_enabled_cached', watchConfig.enabled ? 'true' : 'false');
|
||||
if (watchConfig && watchConfig.enabled === false) {
|
||||
watchlistButton.classList.add('hidden');
|
||||
} else {
|
||||
watchlistButton.classList.remove('hidden'); // Ensure it's shown if enabled
|
||||
}
|
||||
} else {
|
||||
console.error('Failed to fetch watch config for artist page, defaulting to hidden');
|
||||
// Don't update cache on error
|
||||
watchlistButton.classList.add('hidden'); // Hide if config fetch fails
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error fetching watch config for artist page:', error);
|
||||
// Don't update cache on error
|
||||
watchlistButton.classList.add('hidden'); // Hide on error
|
||||
}
|
||||
}
|
||||
}
|
||||
updateWatchlistButtonVisibility();
|
||||
|
||||
// Initialize the watch button after main artist rendering
|
||||
// This is done inside renderArtist after button element is potentially created.
|
||||
});
|
||||
|
||||
async function renderArtist(artistData: ArtistData, artistId: string) {
|
||||
async function renderArtist(artistData: ArtistData, artistId: string, isGlobalWatchEnabled: boolean) {
|
||||
const loadingEl = document.getElementById('loading');
|
||||
if (loadingEl) loadingEl.classList.add('hidden');
|
||||
|
||||
@@ -84,7 +144,10 @@ async function renderArtist(artistData: ArtistData, artistId: string) {
|
||||
if (errorEl) errorEl.classList.add('hidden');
|
||||
|
||||
// Fetch watch status upfront to avoid race conditions for album button rendering
|
||||
const isArtistActuallyWatched = await getArtistWatchStatus(artistId);
|
||||
let isArtistActuallyWatched = false; // Default
|
||||
if (isGlobalWatchEnabled) { // Only fetch if globally enabled
|
||||
isArtistActuallyWatched = await getArtistWatchStatus(artistId);
|
||||
}
|
||||
|
||||
// Check if explicit filter is enabled
|
||||
const isExplicitFilterEnabled = downloadQueue.isExplicitFilterEnabled();
|
||||
@@ -107,12 +170,25 @@ async function renderArtist(artistData: ArtistData, artistId: string) {
|
||||
artistImageEl.src = artistImageSrc;
|
||||
}
|
||||
|
||||
// Initialize Watch Button after other elements are rendered
|
||||
const watchArtistBtn = document.getElementById('watchArtistBtn') as HTMLButtonElement | null;
|
||||
if (watchArtistBtn) {
|
||||
initializeWatchButton(artistId, isArtistActuallyWatched);
|
||||
const syncArtistBtn = document.getElementById('syncArtistBtn') as HTMLButtonElement | null;
|
||||
|
||||
if (!isGlobalWatchEnabled) {
|
||||
if (watchArtistBtn) {
|
||||
watchArtistBtn.classList.add('hidden');
|
||||
watchArtistBtn.disabled = true;
|
||||
}
|
||||
if (syncArtistBtn) {
|
||||
syncArtistBtn.classList.add('hidden');
|
||||
syncArtistBtn.disabled = true;
|
||||
}
|
||||
} else {
|
||||
console.warn("Watch artist button not found in HTML.");
|
||||
if (watchArtistBtn) {
|
||||
initializeWatchButton(artistId, isArtistActuallyWatched);
|
||||
} else {
|
||||
console.warn("Watch artist button not found in HTML.");
|
||||
}
|
||||
// Sync button visibility is managed by initializeWatchButton
|
||||
}
|
||||
|
||||
// Define the artist URL (used by both full-discography and group downloads)
|
||||
@@ -207,8 +283,8 @@ async function renderArtist(artistData: ArtistData, artistId: string) {
|
||||
|
||||
// Use the definitively fetched watch status for rendering album buttons
|
||||
// const isArtistWatched = watchArtistBtn && watchArtistBtn.dataset.watching === 'true'; // Old way
|
||||
const useThisWatchStatusForAlbums = isArtistActuallyWatched; // New way
|
||||
|
||||
// const useThisWatchStatusForAlbums = isArtistActuallyWatched; // Old way, now combination of global and individual
|
||||
|
||||
for (const [groupType, albums] of Object.entries(albumGroups)) {
|
||||
const groupSection = document.createElement('section');
|
||||
groupSection.className = 'album-group';
|
||||
@@ -253,7 +329,7 @@ async function renderArtist(artistData: ArtistData, artistId: string) {
|
||||
albumCardActions.className = 'album-card-actions';
|
||||
|
||||
// Persistent Mark as Known/Missing button (if artist is watched) - Appears first (left)
|
||||
if (useThisWatchStatusForAlbums && album.id) {
|
||||
if (isGlobalWatchEnabled && isArtistActuallyWatched && album.id) {
|
||||
const toggleKnownBtn = document.createElement('button');
|
||||
toggleKnownBtn.className = 'toggle-known-status-btn persistent-album-action-btn';
|
||||
toggleKnownBtn.dataset.albumId = album.id;
|
||||
@@ -351,7 +427,7 @@ async function renderArtist(artistData: ArtistData, artistId: string) {
|
||||
albumCardActions_AppearsOn.className = 'album-card-actions';
|
||||
|
||||
// Persistent Mark as Known/Missing button for appearing_on albums (if artist is watched) - Appears first (left)
|
||||
if (useThisWatchStatusForAlbums && album.id) {
|
||||
if (isGlobalWatchEnabled && isArtistActuallyWatched && album.id) {
|
||||
const toggleKnownBtn = document.createElement('button');
|
||||
toggleKnownBtn.className = 'toggle-known-status-btn persistent-album-action-btn';
|
||||
toggleKnownBtn.dataset.albumId = album.id;
|
||||
@@ -413,7 +489,7 @@ async function renderArtist(artistData: ArtistData, artistId: string) {
|
||||
if (albumsContainerEl) albumsContainerEl.classList.remove('hidden');
|
||||
|
||||
if (!isExplicitFilterEnabled) {
|
||||
attachAlbumActionListeners(artistId);
|
||||
attachAlbumActionListeners(artistId, isGlobalWatchEnabled);
|
||||
attachGroupDownloadListeners(artistId, artistName);
|
||||
}
|
||||
}
|
||||
@@ -448,7 +524,7 @@ function attachGroupDownloadListeners(artistId: string, artistName: string) {
|
||||
});
|
||||
}
|
||||
|
||||
function attachAlbumActionListeners(artistIdForContext: string) {
|
||||
function attachAlbumActionListeners(artistIdForContext: string, isGlobalWatchEnabled: boolean) {
|
||||
const groupsContainer = document.getElementById('album-groups');
|
||||
if (!groupsContainer) return;
|
||||
|
||||
@@ -457,6 +533,10 @@ function attachAlbumActionListeners(artistIdForContext: string) {
|
||||
const button = target.closest('.toggle-known-status-btn') as HTMLButtonElement | null;
|
||||
|
||||
if (button && button.dataset.albumId) {
|
||||
if (!isGlobalWatchEnabled) {
|
||||
showNotification("Watch feature is currently disabled globally.");
|
||||
return;
|
||||
}
|
||||
const albumId = button.dataset.albumId;
|
||||
const currentStatus = button.dataset.status;
|
||||
|
||||
@@ -685,15 +765,24 @@ async function initializeWatchButton(artistId: string, initialIsWatching: boolea
|
||||
if (currentlyWatching) {
|
||||
await unwatchArtist(artistId);
|
||||
updateWatchButton(artistId, false);
|
||||
// Re-fetch and re-render artist data
|
||||
// Re-fetch and re-render artist data, passing the global watch status again
|
||||
const newArtistData = await (await fetch(`/api/artist/info?id=${encodeURIComponent(artistId)}`)).json() as ArtistData;
|
||||
renderArtist(newArtistData, artistId);
|
||||
// Assuming renderArtist needs the global status, which it does. We need to get it or have it available.
|
||||
// Since initializeWatchButton is called from renderArtist, we can assume isGlobalWatchEnabled is in that scope.
|
||||
// This part is tricky as initializeWatchButton doesn't have isGlobalWatchEnabled.
|
||||
// Let's re-fetch global config or rely on the fact that if this button is clickable, global is on.
|
||||
// For simplicity, the re-render will pick up the global status from its own scope if called from top level.
|
||||
// The click handler itself does not need to pass isGlobalWatchEnabled to renderArtist, renderArtist's caller does.
|
||||
// Let's ensure renderArtist is called correctly after watch/unwatch.
|
||||
const globalWatchConfig = await getGlobalWatchConfig(); // Re-fetch for re-render
|
||||
renderArtist(newArtistData, artistId, globalWatchConfig.enabled);
|
||||
} else {
|
||||
await watchArtist(artistId);
|
||||
updateWatchButton(artistId, true);
|
||||
// Re-fetch and re-render artist data
|
||||
const newArtistData = await (await fetch(`/api/artist/info?id=${encodeURIComponent(artistId)}`)).json() as ArtistData;
|
||||
renderArtist(newArtistData, artistId);
|
||||
const globalWatchConfig = await getGlobalWatchConfig(); // Re-fetch for re-render
|
||||
renderArtist(newArtistData, artistId, globalWatchConfig.enabled);
|
||||
}
|
||||
} catch (error) {
|
||||
// On error, revert button to its state before the click attempt
|
||||
|
||||
@@ -168,6 +168,43 @@ document.addEventListener('DOMContentLoaded', async () => {
|
||||
downloadQueue.toggleVisibility();
|
||||
});
|
||||
}
|
||||
|
||||
// Attempt to set initial watchlist button visibility from cache
|
||||
const watchlistButton = document.getElementById('watchlistButton') as HTMLAnchorElement | null;
|
||||
if (watchlistButton) {
|
||||
const cachedWatchEnabled = localStorage.getItem('spotizerr_watch_enabled_cached');
|
||||
if (cachedWatchEnabled === 'true') {
|
||||
watchlistButton.classList.remove('hidden');
|
||||
}
|
||||
}
|
||||
|
||||
// Fetch watch config to determine if watchlist button should be visible
|
||||
async function updateWatchlistButtonVisibility() {
|
||||
if (watchlistButton) {
|
||||
try {
|
||||
const response = await fetch('/api/config/watch');
|
||||
if (response.ok) {
|
||||
const watchConfig = await response.json();
|
||||
localStorage.setItem('spotizerr_watch_enabled_cached', watchConfig.enabled ? 'true' : 'false');
|
||||
if (watchConfig && watchConfig.enabled === false) {
|
||||
watchlistButton.classList.add('hidden');
|
||||
} else {
|
||||
watchlistButton.classList.remove('hidden'); // Ensure it's shown if enabled
|
||||
}
|
||||
} else {
|
||||
console.error('Failed to fetch watch config for config page, defaulting to hidden');
|
||||
// Don't update cache on error
|
||||
watchlistButton.classList.add('hidden'); // Hide if config fetch fails
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error fetching watch config for config page:', error);
|
||||
// Don't update cache on error
|
||||
watchlistButton.classList.add('hidden'); // Hide on error
|
||||
}
|
||||
}
|
||||
}
|
||||
updateWatchlistButtonVisibility();
|
||||
|
||||
} catch (error: any) {
|
||||
showConfigError(error.message);
|
||||
}
|
||||
|
||||
@@ -79,6 +79,7 @@ document.addEventListener('DOMContentLoaded', function() {
|
||||
const queueIcon = document.getElementById('queueIcon');
|
||||
const emptyState = document.getElementById('emptyState');
|
||||
const loadingResults = document.getElementById('loadingResults');
|
||||
const watchlistButton = document.getElementById('watchlistButton') as HTMLAnchorElement | null;
|
||||
|
||||
// Initialize the queue
|
||||
if (queueIcon) {
|
||||
@@ -124,6 +125,41 @@ document.addEventListener('DOMContentLoaded', function() {
|
||||
});
|
||||
}
|
||||
|
||||
// Attempt to set initial watchlist button visibility from cache
|
||||
if (watchlistButton) {
|
||||
const cachedWatchEnabled = localStorage.getItem('spotizerr_watch_enabled_cached');
|
||||
if (cachedWatchEnabled === 'true') {
|
||||
watchlistButton.classList.remove('hidden');
|
||||
}
|
||||
}
|
||||
|
||||
// Fetch watch config to determine if watchlist button should be visible
|
||||
async function updateWatchlistButtonVisibility() {
|
||||
if (watchlistButton) {
|
||||
try {
|
||||
const response = await fetch('/api/config/watch');
|
||||
if (response.ok) {
|
||||
const watchConfig = await response.json();
|
||||
localStorage.setItem('spotizerr_watch_enabled_cached', watchConfig.enabled ? 'true' : 'false');
|
||||
if (watchConfig && watchConfig.enabled === false) {
|
||||
watchlistButton.classList.add('hidden');
|
||||
} else {
|
||||
watchlistButton.classList.remove('hidden'); // Ensure it's shown if enabled
|
||||
}
|
||||
} else {
|
||||
console.error('Failed to fetch watch config, defaulting to hidden');
|
||||
// Don't update cache on error, rely on default hidden or previous cache state until success
|
||||
watchlistButton.classList.add('hidden'); // Hide if config fetch fails
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error fetching watch config:', error);
|
||||
// Don't update cache on error
|
||||
watchlistButton.classList.add('hidden'); // Hide on error
|
||||
}
|
||||
}
|
||||
}
|
||||
updateWatchlistButtonVisibility();
|
||||
|
||||
// Check for URL parameters
|
||||
const urlParams = new URLSearchParams(window.location.search);
|
||||
const query = urlParams.get('q');
|
||||
|
||||
@@ -61,6 +61,12 @@ interface WatchedPlaylistStatus {
|
||||
playlist_data?: Playlist; // Optional, present if watched
|
||||
}
|
||||
|
||||
// Added: Interface for global watch config
|
||||
interface GlobalWatchConfig {
|
||||
enabled: boolean;
|
||||
[key: string]: any;
|
||||
}
|
||||
|
||||
interface DownloadQueueItem {
|
||||
name: string;
|
||||
artist?: string; // Can be a simple string for the queue
|
||||
@@ -69,7 +75,22 @@ interface DownloadQueueItem {
|
||||
// Add any other properties your item might have, compatible with QueueItem
|
||||
}
|
||||
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
// Added: Helper function to fetch global watch config
|
||||
async function getGlobalWatchConfig(): Promise<GlobalWatchConfig> {
|
||||
try {
|
||||
const response = await fetch('/api/config/watch');
|
||||
if (!response.ok) {
|
||||
console.error('Failed to fetch global watch config, assuming disabled.');
|
||||
return { enabled: false }; // Default to disabled on error
|
||||
}
|
||||
return await response.json() as GlobalWatchConfig;
|
||||
} catch (error) {
|
||||
console.error('Error fetching global watch config:', error);
|
||||
return { enabled: false }; // Default to disabled on error
|
||||
}
|
||||
}
|
||||
|
||||
document.addEventListener('DOMContentLoaded', async () => {
|
||||
// Parse playlist ID from URL
|
||||
const pathSegments = window.location.pathname.split('/');
|
||||
const playlistId = pathSegments[pathSegments.indexOf('playlist') + 1];
|
||||
@@ -79,20 +100,40 @@ document.addEventListener('DOMContentLoaded', () => {
|
||||
return;
|
||||
}
|
||||
|
||||
const globalWatchConfig = await getGlobalWatchConfig(); // Fetch global config
|
||||
const isGlobalWatchActuallyEnabled = globalWatchConfig.enabled;
|
||||
|
||||
// Fetch playlist info directly
|
||||
fetch(`/api/playlist/info?id=${encodeURIComponent(playlistId)}`)
|
||||
.then(response => {
|
||||
if (!response.ok) throw new Error('Network response was not ok');
|
||||
return response.json() as Promise<Playlist>;
|
||||
})
|
||||
.then(data => renderPlaylist(data))
|
||||
.then(data => renderPlaylist(data, isGlobalWatchActuallyEnabled))
|
||||
.catch(error => {
|
||||
console.error('Error:', error);
|
||||
showError('Failed to load playlist.');
|
||||
});
|
||||
|
||||
// Fetch initial watch status
|
||||
fetchWatchStatus(playlistId);
|
||||
// Fetch initial watch status for the specific playlist
|
||||
if (isGlobalWatchActuallyEnabled) {
|
||||
fetchWatchStatus(playlistId); // This function then calls updateWatchButtons
|
||||
} else {
|
||||
// If global watch is disabled, ensure watch-related buttons are hidden/disabled
|
||||
const watchBtn = document.getElementById('watchPlaylistBtn') as HTMLButtonElement;
|
||||
const syncBtn = document.getElementById('syncPlaylistBtn') as HTMLButtonElement;
|
||||
if (watchBtn) {
|
||||
watchBtn.classList.add('hidden');
|
||||
watchBtn.disabled = true;
|
||||
// Remove any existing event listener to prevent actions
|
||||
watchBtn.onclick = null;
|
||||
}
|
||||
if (syncBtn) {
|
||||
syncBtn.classList.add('hidden');
|
||||
syncBtn.disabled = true;
|
||||
syncBtn.onclick = null;
|
||||
}
|
||||
}
|
||||
|
||||
const queueIcon = document.getElementById('queueIcon');
|
||||
if (queueIcon) {
|
||||
@@ -100,12 +141,48 @@ document.addEventListener('DOMContentLoaded', () => {
|
||||
downloadQueue.toggleVisibility();
|
||||
});
|
||||
}
|
||||
|
||||
// Attempt to set initial watchlist button visibility from cache
|
||||
const watchlistButton = document.getElementById('watchlistButton') as HTMLAnchorElement | null;
|
||||
if (watchlistButton) {
|
||||
const cachedWatchEnabled = localStorage.getItem('spotizerr_watch_enabled_cached');
|
||||
if (cachedWatchEnabled === 'true') {
|
||||
watchlistButton.classList.remove('hidden');
|
||||
}
|
||||
}
|
||||
|
||||
// Fetch watch config to determine if watchlist button should be visible
|
||||
async function updateWatchlistButtonVisibility() {
|
||||
if (watchlistButton) {
|
||||
try {
|
||||
const response = await fetch('/api/config/watch');
|
||||
if (response.ok) {
|
||||
const watchConfig = await response.json();
|
||||
localStorage.setItem('spotizerr_watch_enabled_cached', watchConfig.enabled ? 'true' : 'false');
|
||||
if (watchConfig && watchConfig.enabled === false) {
|
||||
watchlistButton.classList.add('hidden');
|
||||
} else {
|
||||
watchlistButton.classList.remove('hidden'); // Ensure it's shown if enabled
|
||||
}
|
||||
} else {
|
||||
console.error('Failed to fetch watch config for playlist page, defaulting to hidden');
|
||||
// Don't update cache on error
|
||||
watchlistButton.classList.add('hidden'); // Hide if config fetch fails
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error fetching watch config for playlist page:', error);
|
||||
// Don't update cache on error
|
||||
watchlistButton.classList.add('hidden'); // Hide on error
|
||||
}
|
||||
}
|
||||
}
|
||||
updateWatchlistButtonVisibility();
|
||||
});
|
||||
|
||||
/**
|
||||
* Renders playlist header and tracks.
|
||||
*/
|
||||
function renderPlaylist(playlist: Playlist) {
|
||||
function renderPlaylist(playlist: Playlist, isGlobalWatchEnabled: boolean) {
|
||||
// Hide loading and error messages
|
||||
const loadingEl = document.getElementById('loading');
|
||||
if (loadingEl) loadingEl.classList.add('hidden');
|
||||
@@ -250,7 +327,11 @@ function renderPlaylist(playlist: Playlist) {
|
||||
|
||||
// Determine if the playlist is being watched to show/hide management buttons
|
||||
const watchPlaylistButton = document.getElementById('watchPlaylistBtn') as HTMLButtonElement;
|
||||
const isPlaylistWatched = watchPlaylistButton && watchPlaylistButton.classList.contains('watching');
|
||||
// isIndividuallyWatched checks if the button is visible and has the 'watching' class.
|
||||
// This implies global watch is enabled if the button is even interactable for individual status.
|
||||
const isIndividuallyWatched = watchPlaylistButton &&
|
||||
watchPlaylistButton.classList.contains('watching') &&
|
||||
!watchPlaylistButton.classList.contains('hidden');
|
||||
|
||||
if (playlist.tracks?.items) {
|
||||
playlist.tracks.items.forEach((item: PlaylistItem, index: number) => {
|
||||
@@ -314,7 +395,7 @@ function renderPlaylist(playlist: Playlist) {
|
||||
actionsContainer.innerHTML += downloadBtnHTML;
|
||||
}
|
||||
|
||||
if (isPlaylistWatched) {
|
||||
if (isGlobalWatchEnabled && isIndividuallyWatched) { // Check global and individual watch status
|
||||
// Initial state is set based on track.is_locally_known
|
||||
const isKnown = track.is_locally_known === true; // Ensure boolean check, default to false if undefined
|
||||
const initialStatus = isKnown ? "known" : "missing";
|
||||
@@ -346,7 +427,7 @@ function renderPlaylist(playlist: Playlist) {
|
||||
if (tracksContainerEl) tracksContainerEl.classList.remove('hidden');
|
||||
|
||||
// Attach download listeners to newly rendered download buttons
|
||||
attachTrackActionListeners();
|
||||
attachTrackActionListeners(isGlobalWatchEnabled);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -374,7 +455,7 @@ function showError(message: string) {
|
||||
/**
|
||||
* Attaches event listeners to all individual track action buttons (download, mark known, mark missing).
|
||||
*/
|
||||
function attachTrackActionListeners() {
|
||||
function attachTrackActionListeners(isGlobalWatchEnabled: boolean) {
|
||||
document.querySelectorAll('.track-download-btn').forEach((btn) => {
|
||||
btn.addEventListener('click', (e: Event) => {
|
||||
e.stopPropagation();
|
||||
@@ -405,6 +486,11 @@ function attachTrackActionListeners() {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!isGlobalWatchEnabled) { // Added check
|
||||
showNotification("Watch feature is currently disabled globally. Cannot change track status.");
|
||||
return;
|
||||
}
|
||||
|
||||
button.disabled = true;
|
||||
try {
|
||||
if (currentStatus === 'missing') {
|
||||
@@ -656,6 +742,18 @@ function updateWatchButtons(isWatched: boolean, playlistId: string) {
|
||||
async function watchPlaylist(playlistId: string) {
|
||||
const watchBtn = document.getElementById('watchPlaylistBtn') as HTMLButtonElement;
|
||||
if (watchBtn) watchBtn.disabled = true;
|
||||
// This function should only be callable if global watch is enabled.
|
||||
// We can add a check here or rely on the UI not presenting the button.
|
||||
// For safety, let's check global config again before proceeding.
|
||||
const globalConfig = await getGlobalWatchConfig();
|
||||
if (!globalConfig.enabled) {
|
||||
showError("Cannot watch playlist, feature is disabled globally.");
|
||||
if (watchBtn) {
|
||||
watchBtn.disabled = false; // Re-enable if it was somehow clicked
|
||||
updateWatchButtons(false, playlistId); // Reset button to non-watching state
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const response = await fetch(`/api/playlist/watch/${playlistId}`, { method: 'PUT' });
|
||||
@@ -668,7 +766,7 @@ async function watchPlaylist(playlistId: string) {
|
||||
const newPlaylistInfoResponse = await fetch(`/api/playlist/info?id=${encodeURIComponent(playlistId)}`);
|
||||
if (!newPlaylistInfoResponse.ok) throw new Error('Failed to re-fetch playlist info after watch.');
|
||||
const newPlaylistData = await newPlaylistInfoResponse.json() as Playlist;
|
||||
renderPlaylist(newPlaylistData);
|
||||
renderPlaylist(newPlaylistData, globalConfig.enabled); // Pass current global enabled state
|
||||
|
||||
showNotification(`Playlist added to watchlist. Tracks are being updated.`);
|
||||
} catch (error: any) {
|
||||
@@ -683,6 +781,17 @@ async function watchPlaylist(playlistId: string) {
|
||||
async function unwatchPlaylist(playlistId: string) {
|
||||
const watchBtn = document.getElementById('watchPlaylistBtn') as HTMLButtonElement;
|
||||
if (watchBtn) watchBtn.disabled = true;
|
||||
// Similarly, check global config
|
||||
const globalConfig = await getGlobalWatchConfig();
|
||||
if (!globalConfig.enabled) {
|
||||
// This case should be rare if UI behaves, but good for robustness
|
||||
showError("Cannot unwatch playlist, feature is disabled globally.");
|
||||
if (watchBtn) {
|
||||
watchBtn.disabled = false;
|
||||
// updateWatchButtons(true, playlistId); // Or keep as is if it was 'watching'
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const response = await fetch(`/api/playlist/watch/${playlistId}`, { method: 'DELETE' });
|
||||
@@ -695,7 +804,7 @@ async function unwatchPlaylist(playlistId: string) {
|
||||
const newPlaylistInfoResponse = await fetch(`/api/playlist/info?id=${encodeURIComponent(playlistId)}`);
|
||||
if (!newPlaylistInfoResponse.ok) throw new Error('Failed to re-fetch playlist info after unwatch.');
|
||||
const newPlaylistData = await newPlaylistInfoResponse.json() as Playlist;
|
||||
renderPlaylist(newPlaylistData);
|
||||
renderPlaylist(newPlaylistData, globalConfig.enabled); // Pass current global enabled state
|
||||
|
||||
showNotification('Playlist removed from watchlist. Track statuses updated.');
|
||||
} catch (error: any) {
|
||||
@@ -710,6 +819,12 @@ async function unwatchPlaylist(playlistId: string) {
|
||||
async function syncPlaylist(playlistId: string) {
|
||||
const syncBtn = document.getElementById('syncPlaylistBtn') as HTMLButtonElement;
|
||||
let originalButtonContent = ''; // Define outside
|
||||
// Check global config
|
||||
const globalConfig = await getGlobalWatchConfig();
|
||||
if (!globalConfig.enabled) {
|
||||
showError("Cannot sync playlist, feature is disabled globally.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (syncBtn) {
|
||||
syncBtn.disabled = true;
|
||||
|
||||
@@ -30,6 +30,42 @@ document.addEventListener('DOMContentLoaded', () => {
|
||||
downloadQueue.toggleVisibility();
|
||||
});
|
||||
}
|
||||
|
||||
// Attempt to set initial watchlist button visibility from cache
|
||||
const watchlistButton = document.getElementById('watchlistButton') as HTMLAnchorElement | null;
|
||||
if (watchlistButton) {
|
||||
const cachedWatchEnabled = localStorage.getItem('spotizerr_watch_enabled_cached');
|
||||
if (cachedWatchEnabled === 'true') {
|
||||
watchlistButton.classList.remove('hidden');
|
||||
}
|
||||
}
|
||||
|
||||
// Fetch watch config to determine if watchlist button should be visible
|
||||
async function updateWatchlistButtonVisibility() {
|
||||
if (watchlistButton) {
|
||||
try {
|
||||
const response = await fetch('/api/config/watch');
|
||||
if (response.ok) {
|
||||
const watchConfig = await response.json();
|
||||
localStorage.setItem('spotizerr_watch_enabled_cached', watchConfig.enabled ? 'true' : 'false');
|
||||
if (watchConfig && watchConfig.enabled === false) {
|
||||
watchlistButton.classList.add('hidden');
|
||||
} else {
|
||||
watchlistButton.classList.remove('hidden'); // Ensure it's shown if enabled
|
||||
}
|
||||
} else {
|
||||
console.error('Failed to fetch watch config for track page, defaulting to hidden');
|
||||
// Don't update cache on error
|
||||
watchlistButton.classList.add('hidden'); // Hide if config fetch fails
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error fetching watch config for track page:', error);
|
||||
// Don't update cache on error
|
||||
watchlistButton.classList.add('hidden'); // Hide on error
|
||||
}
|
||||
}
|
||||
}
|
||||
updateWatchlistButtonVisibility();
|
||||
});
|
||||
|
||||
/**
|
||||
|
||||
@@ -133,13 +133,37 @@ interface WatchedPlaylistOriginal {
|
||||
|
||||
type WatchedItem = (WatchedArtistOriginal & { itemType: 'artist' }) | (WatchedPlaylistOriginal & { itemType: 'playlist' });
|
||||
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
// Added: Interface for global watch config
|
||||
interface GlobalWatchConfig {
|
||||
enabled: boolean;
|
||||
[key: string]: any; // Allow other properties
|
||||
}
|
||||
|
||||
// Added: Helper function to fetch global watch config
|
||||
async function getGlobalWatchConfig(): Promise<GlobalWatchConfig> {
|
||||
try {
|
||||
const response = await fetch('/api/config/watch');
|
||||
if (!response.ok) {
|
||||
console.error('Failed to fetch global watch config, assuming disabled.');
|
||||
return { enabled: false }; // Default to disabled on error
|
||||
}
|
||||
return await response.json() as GlobalWatchConfig;
|
||||
} catch (error) {
|
||||
console.error('Error fetching global watch config:', error);
|
||||
return { enabled: false }; // Default to disabled on error
|
||||
}
|
||||
}
|
||||
|
||||
document.addEventListener('DOMContentLoaded', async function() {
|
||||
const watchedItemsContainer = document.getElementById('watchedItemsContainer');
|
||||
const loadingIndicator = document.getElementById('loadingWatchedItems');
|
||||
const emptyStateIndicator = document.getElementById('emptyWatchedItems');
|
||||
const queueIcon = document.getElementById('queueIcon');
|
||||
const checkAllWatchedBtn = document.getElementById('checkAllWatchedBtn') as HTMLButtonElement | null;
|
||||
|
||||
// Fetch global watch config first
|
||||
const globalWatchConfig = await getGlobalWatchConfig();
|
||||
|
||||
if (queueIcon) {
|
||||
queueIcon.addEventListener('click', () => {
|
||||
downloadQueue.toggleVisibility();
|
||||
@@ -214,8 +238,28 @@ document.addEventListener('DOMContentLoaded', function() {
|
||||
});
|
||||
}
|
||||
|
||||
// Initial load
|
||||
loadWatchedItems();
|
||||
// Initial load is now conditional
|
||||
if (globalWatchConfig.enabled) {
|
||||
if (checkAllWatchedBtn) checkAllWatchedBtn.classList.remove('hidden');
|
||||
loadWatchedItems();
|
||||
} else {
|
||||
// Watch feature is disabled globally
|
||||
showLoading(false);
|
||||
showEmptyState(false);
|
||||
if (checkAllWatchedBtn) checkAllWatchedBtn.classList.add('hidden'); // Hide the button
|
||||
|
||||
if (watchedItemsContainer) {
|
||||
watchedItemsContainer.innerHTML = `
|
||||
<div class="empty-state-container">
|
||||
<img src="/static/images/eye-crossed.svg" alt="Watch Disabled" class="empty-state-icon">
|
||||
<p class="empty-state-message">The Watchlist feature is currently disabled in the application settings.</p>
|
||||
<p class="empty-state-submessage">Please enable it in <a href="/settings" class="settings-link">Settings</a> to use this page.</p>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
// Ensure the main loading indicator is also hidden if it was shown by default
|
||||
if (loadingIndicator) loadingIndicator.classList.add('hidden');
|
||||
}
|
||||
});
|
||||
|
||||
const MAX_NOTIFICATIONS = 3;
|
||||
|
||||
@@ -53,7 +53,7 @@
|
||||
<img src="{{ url_for('static', filename='images/home.svg') }}" alt="Home">
|
||||
</button>
|
||||
|
||||
<a href="/watchlist" class="btn-icon watchlist-icon floating-icon" aria-label="Watchlist" title="Go to Watchlist">
|
||||
<a id="watchlistButton" href="/watchlist" class="btn-icon watchlist-icon floating-icon hidden" aria-label="Watchlist" title="Go to Watchlist">
|
||||
<img src="{{ url_for('static', filename='images/binoculars.svg') }}" alt="Watchlist" onerror="handleImageError(this)"/>
|
||||
</a>
|
||||
|
||||
|
||||
@@ -58,7 +58,7 @@
|
||||
<img src="{{ url_for('static', filename='images/home.svg') }}" alt="Home">
|
||||
</button>
|
||||
|
||||
<a href="/watchlist" class="btn-icon watchlist-icon floating-icon" aria-label="Watchlist" title="Go to Watchlist">
|
||||
<a id="watchlistButton" href="/watchlist" class="btn-icon watchlist-icon floating-icon hidden" aria-label="Watchlist" title="Go to Watchlist">
|
||||
<img src="{{ url_for('static', filename='images/binoculars.svg') }}" alt="Watchlist" onerror="handleImageError(this)"/>
|
||||
</a>
|
||||
|
||||
|
||||
@@ -324,7 +324,7 @@
|
||||
<img src="{{ url_for('static', filename='images/arrow-left.svg') }}" alt="Back" />
|
||||
</a>
|
||||
|
||||
<a href="/watchlist" class="btn-icon watchlist-icon floating-icon" aria-label="Watchlist" title="Go to Watchlist">
|
||||
<a id="watchlistButton" href="/watchlist" class="btn-icon watchlist-icon floating-icon hidden" aria-label="Watchlist" title="Go to Watchlist">
|
||||
<img src="{{ url_for('static', filename='images/binoculars.svg') }}" alt="Watchlist" onerror="handleImageError(this)"/>
|
||||
</a>
|
||||
|
||||
|
||||
@@ -66,7 +66,7 @@
|
||||
<img src="{{ url_for('static', filename='images/settings.svg') }}" alt="Settings" onerror="handleImageError(this)"/>
|
||||
</a>
|
||||
|
||||
<a href="/watchlist" class="btn-icon watchlist-icon floating-icon" aria-label="Watchlist" title="Go to Watchlist">
|
||||
<a id="watchlistButton" href="/watchlist" class="btn-icon watchlist-icon floating-icon hidden" aria-label="Watchlist" title="Go to Watchlist">
|
||||
<img src="{{ url_for('static', filename='images/binoculars.svg') }}" alt="Watchlist" onerror="handleImageError(this)"/>
|
||||
</a>
|
||||
|
||||
|
||||
@@ -65,7 +65,7 @@
|
||||
<img src="{{ url_for('static', filename='images/home.svg') }}" alt="Home">
|
||||
</button>
|
||||
|
||||
<a href="/watchlist" class="btn-icon watchlist-icon floating-icon" aria-label="Watchlist" title="Go to Watchlist">
|
||||
<a id="watchlistButton" href="/watchlist" class="btn-icon watchlist-icon floating-icon hidden" aria-label="Watchlist" title="Go to Watchlist">
|
||||
<img src="{{ url_for('static', filename='images/binoculars.svg') }}" alt="Watchlist" onerror="handleImageError(this)"/>
|
||||
</a>
|
||||
|
||||
|
||||
@@ -52,7 +52,7 @@
|
||||
<img src="{{ url_for('static', filename='images/home.svg') }}" alt="Home">
|
||||
</button>
|
||||
|
||||
<a href="/watchlist" class="btn-icon watchlist-icon floating-icon" aria-label="Watchlist" title="Go to Watchlist">
|
||||
<a id="watchlistButton" href="/watchlist" class="btn-icon watchlist-icon floating-icon hidden" aria-label="Watchlist" title="Go to Watchlist">
|
||||
<img src="{{ url_for('static', filename='images/binoculars.svg') }}" alt="Watchlist" onerror="handleImageError(this)"/>
|
||||
</a>
|
||||
|
||||
|
||||
@@ -49,10 +49,6 @@
|
||||
<a href="/" class="btn-icon home-btn floating-icon settings-icon" aria-label="Return to Home" title="Return to Home">
|
||||
<img src="{{ url_for('static', filename='images/home.svg') }}" alt="Home" onerror="handleImageError(this)"/>
|
||||
</a>
|
||||
|
||||
<a href="/watchlist" class="btn-icon watchlist-icon floating-icon" aria-label="Watchlist" title="Go to Watchlist">
|
||||
<img src="{{ url_for('static', filename='images/binoculars.svg') }}" alt="Watchlist" onerror="handleImageError(this)"/>
|
||||
</a>
|
||||
|
||||
<button
|
||||
id="queueIcon"
|
||||
|
||||
Reference in New Issue
Block a user