faster prg monitoring
This commit is contained in:
@@ -170,7 +170,10 @@ async function downloadWholeAlbum(album) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await downloadQueue.startAlbumDownload(url, { name: album.name || 'Unknown Album' });
|
// Use local startDownload function instead of downloadQueue.startAlbumDownload
|
||||||
|
await startDownload(url, 'album', { name: album.name || 'Unknown Album' });
|
||||||
|
// Make the queue visible after queueing
|
||||||
|
downloadQueue.toggleVisibility(true);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
showError('Album download failed: ' + (error?.message || 'Unknown error'));
|
showError('Album download failed: ' + (error?.message || 'Unknown error'));
|
||||||
throw error;
|
throw error;
|
||||||
@@ -228,10 +231,67 @@ async function startDownload(url, type, item, albumType) {
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
const response = await fetch(apiUrl);
|
const response = await fetch(apiUrl);
|
||||||
|
if (!response.ok) {
|
||||||
|
throw new Error(`Server returned ${response.status}`);
|
||||||
|
}
|
||||||
|
|
||||||
const data = await response.json();
|
const data = await response.json();
|
||||||
downloadQueue.addDownload(item, type, data.prg_file);
|
|
||||||
|
// Handle artist downloads which return multiple album_prg_files
|
||||||
|
if (type === 'artist' && data.album_prg_files && Array.isArray(data.album_prg_files)) {
|
||||||
|
// Add each album to the download queue separately
|
||||||
|
const queueIds = [];
|
||||||
|
data.album_prg_files.forEach(prgFile => {
|
||||||
|
const queueId = downloadQueue.addDownload(item, 'album', prgFile, apiUrl, false);
|
||||||
|
queueIds.push({queueId, prgFile});
|
||||||
|
});
|
||||||
|
|
||||||
|
// Wait a short time before checking the status to give server time to create files
|
||||||
|
await new Promise(resolve => setTimeout(resolve, 1000));
|
||||||
|
|
||||||
|
// Start monitoring each entry after confirming PRG files exist
|
||||||
|
for (const {queueId, prgFile} of queueIds) {
|
||||||
|
try {
|
||||||
|
const statusResponse = await fetch(`/api/prgs/${prgFile}`);
|
||||||
|
if (statusResponse.ok) {
|
||||||
|
// Only start monitoring after confirming the PRG file exists
|
||||||
|
const entry = downloadQueue.downloadQueue[queueId];
|
||||||
|
if (entry) {
|
||||||
|
// Start monitoring regardless of visibility
|
||||||
|
downloadQueue.startEntryMonitoring(queueId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (statusError) {
|
||||||
|
console.log(`Initial status check pending for ${prgFile}, will retry on next interval`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (data.prg_file) {
|
||||||
|
// Handle single-file downloads (tracks, albums, playlists)
|
||||||
|
const queueId = downloadQueue.addDownload(item, type, data.prg_file, apiUrl, false);
|
||||||
|
|
||||||
|
// Wait a short time before checking the status to give server time to create the file
|
||||||
|
await new Promise(resolve => setTimeout(resolve, 1000));
|
||||||
|
|
||||||
|
// Ensure the PRG file exists and has initial data by making a status check
|
||||||
|
try {
|
||||||
|
const statusResponse = await fetch(`/api/prgs/${data.prg_file}`);
|
||||||
|
if (statusResponse.ok) {
|
||||||
|
// Only start monitoring after confirming the PRG file exists
|
||||||
|
const entry = downloadQueue.downloadQueue[queueId];
|
||||||
|
if (entry) {
|
||||||
|
// Start monitoring regardless of visibility
|
||||||
|
downloadQueue.startEntryMonitoring(queueId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (statusError) {
|
||||||
|
console.log('Initial status check pending, will retry on next interval');
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
throw new Error('Invalid response format from server');
|
||||||
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
showError('Download failed: ' + (error?.message || 'Unknown error'));
|
showError('Download failed: ' + (error?.message || 'Unknown error'));
|
||||||
|
throw error;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -72,13 +72,17 @@ function renderArtist(artistData, artistId) {
|
|||||||
downloadArtistBtn.textContent = 'Queueing...';
|
downloadArtistBtn.textContent = 'Queueing...';
|
||||||
|
|
||||||
// Queue the entire discography (albums, singles, compilations, and appears_on)
|
// Queue the entire discography (albums, singles, compilations, and appears_on)
|
||||||
downloadQueue.startArtistDownload(
|
// Use our local startDownload function instead of downloadQueue.startArtistDownload
|
||||||
|
startDownload(
|
||||||
artistUrl,
|
artistUrl,
|
||||||
|
'artist',
|
||||||
{ name: artistName, artist: artistName },
|
{ name: artistName, artist: artistName },
|
||||||
'album,single,compilation'
|
'album,single,compilation'
|
||||||
)
|
)
|
||||||
.then(() => {
|
.then(() => {
|
||||||
downloadArtistBtn.textContent = 'Artist queued';
|
downloadArtistBtn.textContent = 'Artist queued';
|
||||||
|
// Make the queue visible after queueing
|
||||||
|
downloadQueue.toggleVisibility(true);
|
||||||
})
|
})
|
||||||
.catch(err => {
|
.catch(err => {
|
||||||
downloadArtistBtn.textContent = 'Download All Discography';
|
downloadArtistBtn.textContent = 'Download All Discography';
|
||||||
@@ -162,13 +166,16 @@ function attachGroupDownloadListeners(artistUrl, artistName) {
|
|||||||
e.target.textContent = `Queueing all ${capitalize(groupType)}s...`;
|
e.target.textContent = `Queueing all ${capitalize(groupType)}s...`;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// Use the artist download function with the group type filter.
|
// Use our local startDownload function with the group type filter
|
||||||
await downloadQueue.startArtistDownload(
|
await startDownload(
|
||||||
artistUrl,
|
artistUrl,
|
||||||
|
'artist',
|
||||||
{ name: artistName || 'Unknown Artist', artist: artistName || 'Unknown Artist' },
|
{ name: artistName || 'Unknown Artist', artist: artistName || 'Unknown Artist' },
|
||||||
groupType // Only queue releases of this specific type.
|
groupType // Only queue releases of this specific type.
|
||||||
);
|
);
|
||||||
e.target.textContent = `Queued all ${capitalize(groupType)}s`;
|
e.target.textContent = `Queued all ${capitalize(groupType)}s`;
|
||||||
|
// Make the queue visible after queueing
|
||||||
|
downloadQueue.toggleVisibility(true);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
e.target.textContent = `Download All ${capitalize(groupType)}s`;
|
e.target.textContent = `Download All ${capitalize(groupType)}s`;
|
||||||
e.target.disabled = false;
|
e.target.disabled = false;
|
||||||
@@ -188,12 +195,105 @@ function attachDownloadListeners() {
|
|||||||
const type = e.currentTarget.dataset.type || 'album';
|
const type = e.currentTarget.dataset.type || 'album';
|
||||||
|
|
||||||
e.currentTarget.remove();
|
e.currentTarget.remove();
|
||||||
downloadQueue.startAlbumDownload(url, { name, type })
|
// Use our local startDownload function instead of downloadQueue.startAlbumDownload
|
||||||
|
startDownload(url, type, { name, type })
|
||||||
.catch(err => showError('Download failed: ' + (err?.message || 'Unknown error')));
|
.catch(err => showError('Download failed: ' + (err?.message || 'Unknown error')));
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Add startDownload function (similar to track.js and main.js)
|
||||||
|
/**
|
||||||
|
* Starts the download process via API
|
||||||
|
*/
|
||||||
|
async function startDownload(url, type, item, albumType) {
|
||||||
|
if (!url || !type) {
|
||||||
|
showError('Missing URL or type for download');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const service = url.includes('open.spotify.com') ? 'spotify' : 'deezer';
|
||||||
|
let apiUrl = `/api/${type}/download?service=${service}&url=${encodeURIComponent(url)}`;
|
||||||
|
|
||||||
|
// Add name and artist if available for better progress display
|
||||||
|
if (item.name) {
|
||||||
|
apiUrl += `&name=${encodeURIComponent(item.name)}`;
|
||||||
|
}
|
||||||
|
if (item.artist) {
|
||||||
|
apiUrl += `&artist=${encodeURIComponent(item.artist)}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
// For artist downloads, include album_type
|
||||||
|
if (type === 'artist' && albumType) {
|
||||||
|
apiUrl += `&album_type=${encodeURIComponent(albumType)}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const response = await fetch(apiUrl);
|
||||||
|
if (!response.ok) {
|
||||||
|
throw new Error(`Server returned ${response.status}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
const data = await response.json();
|
||||||
|
|
||||||
|
// Handle artist downloads which return multiple album_prg_files
|
||||||
|
if (type === 'artist' && data.album_prg_files && Array.isArray(data.album_prg_files)) {
|
||||||
|
// Add each album to the download queue separately
|
||||||
|
const queueIds = [];
|
||||||
|
data.album_prg_files.forEach(prgFile => {
|
||||||
|
const queueId = downloadQueue.addDownload(item, 'album', prgFile, apiUrl, false);
|
||||||
|
queueIds.push({queueId, prgFile});
|
||||||
|
});
|
||||||
|
|
||||||
|
// Wait a short time before checking the status to give server time to create files
|
||||||
|
await new Promise(resolve => setTimeout(resolve, 1000));
|
||||||
|
|
||||||
|
// Start monitoring each entry after confirming PRG files exist
|
||||||
|
for (const {queueId, prgFile} of queueIds) {
|
||||||
|
try {
|
||||||
|
const statusResponse = await fetch(`/api/prgs/${prgFile}`);
|
||||||
|
if (statusResponse.ok) {
|
||||||
|
// Only start monitoring after confirming the PRG file exists
|
||||||
|
const entry = downloadQueue.downloadQueue[queueId];
|
||||||
|
if (entry) {
|
||||||
|
// Start monitoring regardless of visibility
|
||||||
|
downloadQueue.startEntryMonitoring(queueId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (statusError) {
|
||||||
|
console.log(`Initial status check pending for ${prgFile}, will retry on next interval`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (data.prg_file) {
|
||||||
|
// Handle single-file downloads (tracks, albums, playlists)
|
||||||
|
const queueId = downloadQueue.addDownload(item, type, data.prg_file, apiUrl, false);
|
||||||
|
|
||||||
|
// Wait a short time before checking the status to give server time to create the file
|
||||||
|
await new Promise(resolve => setTimeout(resolve, 1000));
|
||||||
|
|
||||||
|
// Ensure the PRG file exists and has initial data by making a status check
|
||||||
|
try {
|
||||||
|
const statusResponse = await fetch(`/api/prgs/${data.prg_file}`);
|
||||||
|
if (statusResponse.ok) {
|
||||||
|
// Only start monitoring after confirming the PRG file exists
|
||||||
|
const entry = downloadQueue.downloadQueue[queueId];
|
||||||
|
if (entry) {
|
||||||
|
// Start monitoring regardless of visibility
|
||||||
|
downloadQueue.startEntryMonitoring(queueId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (statusError) {
|
||||||
|
console.log('Initial status check pending, will retry on next interval');
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
throw new Error('Invalid response format from server');
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
showError('Download failed: ' + (error?.message || 'Unknown error'));
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// UI Helpers
|
// UI Helpers
|
||||||
function showError(message) {
|
function showError(message) {
|
||||||
const errorEl = document.getElementById('error');
|
const errorEl = document.getElementById('error');
|
||||||
|
|||||||
@@ -229,16 +229,57 @@ document.addEventListener('DOMContentLoaded', function() {
|
|||||||
// Handle artist downloads which return multiple album_prg_files
|
// Handle artist downloads which return multiple album_prg_files
|
||||||
if (type === 'artist' && data.album_prg_files && Array.isArray(data.album_prg_files)) {
|
if (type === 'artist' && data.album_prg_files && Array.isArray(data.album_prg_files)) {
|
||||||
// Add each album to the download queue separately
|
// Add each album to the download queue separately
|
||||||
|
const queueIds = [];
|
||||||
data.album_prg_files.forEach(prgFile => {
|
data.album_prg_files.forEach(prgFile => {
|
||||||
downloadQueue.addDownload(item, 'album', prgFile, apiUrl);
|
const queueId = downloadQueue.addDownload(item, 'album', prgFile, apiUrl, false);
|
||||||
|
queueIds.push({queueId, prgFile});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Wait a short time before checking the status to give server time to create files
|
||||||
|
await new Promise(resolve => setTimeout(resolve, 1000));
|
||||||
|
|
||||||
|
// Start monitoring each entry after confirming PRG files exist
|
||||||
|
for (const {queueId, prgFile} of queueIds) {
|
||||||
|
try {
|
||||||
|
const statusResponse = await fetch(`/api/prgs/${prgFile}`);
|
||||||
|
if (statusResponse.ok) {
|
||||||
|
// Only start monitoring after confirming the PRG file exists
|
||||||
|
const entry = downloadQueue.downloadQueue[queueId];
|
||||||
|
if (entry) {
|
||||||
|
// Start monitoring regardless of visibility
|
||||||
|
downloadQueue.startEntryMonitoring(queueId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (statusError) {
|
||||||
|
console.log(`Initial status check pending for ${prgFile}, will retry on next interval`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Show success message for artist download
|
// Show success message for artist download
|
||||||
if (data.message) {
|
if (data.message) {
|
||||||
showSuccess(data.message);
|
showSuccess(data.message);
|
||||||
}
|
}
|
||||||
} else if (data.prg_file) {
|
} else if (data.prg_file) {
|
||||||
// Handle single-file downloads (tracks, albums, playlists)
|
// Handle single-file downloads (tracks, albums, playlists)
|
||||||
downloadQueue.addDownload(item, type, data.prg_file, apiUrl);
|
const queueId = downloadQueue.addDownload(item, type, data.prg_file, apiUrl, false);
|
||||||
|
|
||||||
|
// Wait a short time before checking the status to give server time to create the file
|
||||||
|
await new Promise(resolve => setTimeout(resolve, 1000));
|
||||||
|
|
||||||
|
// Ensure the PRG file exists and has initial data by making a status check
|
||||||
|
try {
|
||||||
|
const statusResponse = await fetch(`/api/prgs/${data.prg_file}`);
|
||||||
|
if (statusResponse.ok) {
|
||||||
|
// Only start monitoring after confirming the PRG file exists
|
||||||
|
const entry = downloadQueue.downloadQueue[queueId];
|
||||||
|
if (entry) {
|
||||||
|
// Start monitoring regardless of visibility
|
||||||
|
downloadQueue.startEntryMonitoring(queueId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (statusError) {
|
||||||
|
console.log('Initial status check pending, will retry on next interval');
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
throw new Error('Invalid response format from server');
|
throw new Error('Invalid response format from server');
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -240,7 +240,10 @@ async function downloadWholePlaylist(playlist) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await downloadQueue.startPlaylistDownload(url, { name: playlist.name || 'Unknown Playlist' });
|
// Use our local startDownload function instead of downloadQueue.startPlaylistDownload
|
||||||
|
await startDownload(url, 'playlist', { name: playlist.name || 'Unknown Playlist' });
|
||||||
|
// Make the queue visible after queueing
|
||||||
|
downloadQueue.toggleVisibility(true);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
showError('Playlist download failed: ' + (error?.message || 'Unknown error'));
|
showError('Playlist download failed: ' + (error?.message || 'Unknown error'));
|
||||||
throw error;
|
throw error;
|
||||||
@@ -292,8 +295,10 @@ async function downloadPlaylistAlbums(playlist) {
|
|||||||
const albumUrl = album.external_urls?.spotify || '';
|
const albumUrl = album.external_urls?.spotify || '';
|
||||||
if (!albumUrl) continue;
|
if (!albumUrl) continue;
|
||||||
|
|
||||||
await downloadQueue.startAlbumDownload(
|
// Use our local startDownload function instead of downloadQueue.startAlbumDownload
|
||||||
|
await startDownload(
|
||||||
albumUrl,
|
albumUrl,
|
||||||
|
'album',
|
||||||
{ name: album.name || 'Unknown Album' }
|
{ name: album.name || 'Unknown Album' }
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -310,6 +315,9 @@ async function downloadPlaylistAlbums(playlist) {
|
|||||||
if (downloadAlbumsBtn) {
|
if (downloadAlbumsBtn) {
|
||||||
downloadAlbumsBtn.textContent = 'Queued!';
|
downloadAlbumsBtn.textContent = 'Queued!';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Make the queue visible after queueing all albums
|
||||||
|
downloadQueue.toggleVisibility(true);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
// Propagate any errors encountered.
|
// Propagate any errors encountered.
|
||||||
throw error;
|
throw error;
|
||||||
@@ -344,11 +352,67 @@ async function startDownload(url, type, item, albumType) {
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
const response = await fetch(apiUrl);
|
const response = await fetch(apiUrl);
|
||||||
|
if (!response.ok) {
|
||||||
|
throw new Error(`Server returned ${response.status}`);
|
||||||
|
}
|
||||||
|
|
||||||
const data = await response.json();
|
const data = await response.json();
|
||||||
// Add the download to the queue using the working queue implementation.
|
|
||||||
downloadQueue.addDownload(item, type, data.prg_file);
|
// Handle artist downloads which return multiple album_prg_files
|
||||||
|
if (type === 'artist' && data.album_prg_files && Array.isArray(data.album_prg_files)) {
|
||||||
|
// Add each album to the download queue separately
|
||||||
|
const queueIds = [];
|
||||||
|
data.album_prg_files.forEach(prgFile => {
|
||||||
|
const queueId = downloadQueue.addDownload(item, 'album', prgFile, apiUrl, false);
|
||||||
|
queueIds.push({queueId, prgFile});
|
||||||
|
});
|
||||||
|
|
||||||
|
// Wait a short time before checking the status to give server time to create files
|
||||||
|
await new Promise(resolve => setTimeout(resolve, 1000));
|
||||||
|
|
||||||
|
// Start monitoring each entry after confirming PRG files exist
|
||||||
|
for (const {queueId, prgFile} of queueIds) {
|
||||||
|
try {
|
||||||
|
const statusResponse = await fetch(`/api/prgs/${prgFile}`);
|
||||||
|
if (statusResponse.ok) {
|
||||||
|
// Only start monitoring after confirming the PRG file exists
|
||||||
|
const entry = downloadQueue.downloadQueue[queueId];
|
||||||
|
if (entry) {
|
||||||
|
// Start monitoring regardless of visibility
|
||||||
|
downloadQueue.startEntryMonitoring(queueId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (statusError) {
|
||||||
|
console.log(`Initial status check pending for ${prgFile}, will retry on next interval`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (data.prg_file) {
|
||||||
|
// Handle single-file downloads (tracks, albums, playlists)
|
||||||
|
const queueId = downloadQueue.addDownload(item, type, data.prg_file, apiUrl, false);
|
||||||
|
|
||||||
|
// Wait a short time before checking the status to give server time to create the file
|
||||||
|
await new Promise(resolve => setTimeout(resolve, 1000));
|
||||||
|
|
||||||
|
// Ensure the PRG file exists and has initial data by making a status check
|
||||||
|
try {
|
||||||
|
const statusResponse = await fetch(`/api/prgs/${data.prg_file}`);
|
||||||
|
if (statusResponse.ok) {
|
||||||
|
// Only start monitoring after confirming the PRG file exists
|
||||||
|
const entry = downloadQueue.downloadQueue[queueId];
|
||||||
|
if (entry) {
|
||||||
|
// Start monitoring regardless of visibility
|
||||||
|
downloadQueue.startEntryMonitoring(queueId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (statusError) {
|
||||||
|
console.log('Initial status check pending, will retry on next interval');
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
throw new Error('Invalid response format from server');
|
||||||
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
showError('Download failed: ' + (error?.message || 'Unknown error'));
|
showError('Download failed: ' + (error?.message || 'Unknown error'));
|
||||||
|
throw error;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -189,13 +189,20 @@ class DownloadQueue {
|
|||||||
/**
|
/**
|
||||||
* Adds a new download entry.
|
* Adds a new download entry.
|
||||||
*/
|
*/
|
||||||
addDownload(item, type, prgFile, requestUrl = null) {
|
addDownload(item, type, prgFile, requestUrl = null, startMonitoring = false) {
|
||||||
const queueId = this.generateQueueId();
|
const queueId = this.generateQueueId();
|
||||||
const entry = this.createQueueEntry(item, type, prgFile, queueId, requestUrl);
|
const entry = this.createQueueEntry(item, type, prgFile, queueId, requestUrl);
|
||||||
this.downloadQueue[queueId] = entry;
|
this.downloadQueue[queueId] = entry;
|
||||||
// Re-render and update which entries are processed.
|
// Re-render and update which entries are processed.
|
||||||
this.updateQueueOrder();
|
this.updateQueueOrder();
|
||||||
|
|
||||||
|
// Only start monitoring if explicitly requested
|
||||||
|
if (startMonitoring && this.isEntryVisible(queueId)) {
|
||||||
|
this.startEntryMonitoring(queueId);
|
||||||
|
}
|
||||||
|
|
||||||
this.dispatchEvent('downloadAdded', { queueId, item, type });
|
this.dispatchEvent('downloadAdded', { queueId, item, type });
|
||||||
|
return queueId; // Return the queueId so callers can reference it
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Start processing the entry only if it is visible. */
|
/* Start processing the entry only if it is visible. */
|
||||||
@@ -204,19 +211,38 @@ class DownloadQueue {
|
|||||||
if (!entry || entry.hasEnded) return;
|
if (!entry || entry.hasEnded) return;
|
||||||
if (entry.intervalId) return;
|
if (entry.intervalId) return;
|
||||||
|
|
||||||
entry.intervalId = setInterval(async () => {
|
// Show a preparing message for new entries
|
||||||
if (!this.isEntryVisible(queueId)) {
|
if (entry.isNew) {
|
||||||
clearInterval(entry.intervalId);
|
const logElement = document.getElementById(`log-${entry.uniqueId}-${entry.prgFile}`);
|
||||||
entry.intervalId = null;
|
if (logElement) {
|
||||||
return;
|
logElement.textContent = "Preparing download...";
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Track status check failures
|
||||||
|
entry.statusCheckFailures = 0;
|
||||||
|
const MAX_STATUS_CHECK_FAILURES = 5; // Maximum number of consecutive failures before showing error
|
||||||
|
|
||||||
|
entry.intervalId = setInterval(async () => {
|
||||||
const logElement = document.getElementById(`log-${entry.uniqueId}-${entry.prgFile}`);
|
const logElement = document.getElementById(`log-${entry.uniqueId}-${entry.prgFile}`);
|
||||||
if (entry.hasEnded) {
|
if (entry.hasEnded) {
|
||||||
clearInterval(entry.intervalId);
|
clearInterval(entry.intervalId);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
|
// Show checking status message on first few attempts
|
||||||
|
if (entry.statusCheckFailures > 0 && entry.statusCheckFailures < MAX_STATUS_CHECK_FAILURES && logElement) {
|
||||||
|
logElement.textContent = `Checking download status (attempt ${entry.statusCheckFailures+1})...`;
|
||||||
|
}
|
||||||
|
|
||||||
const response = await fetch(`/api/prgs/${entry.prgFile}`);
|
const response = await fetch(`/api/prgs/${entry.prgFile}`);
|
||||||
|
if (!response.ok) {
|
||||||
|
throw new Error(`Server returned ${response.status}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reset failure counter on success
|
||||||
|
entry.statusCheckFailures = 0;
|
||||||
|
|
||||||
const data = await response.json();
|
const data = await response.json();
|
||||||
|
|
||||||
if (data.type) {
|
if (data.type) {
|
||||||
@@ -286,10 +312,18 @@ class DownloadQueue {
|
|||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Status check failed:', error);
|
console.error('Status check failed:', error);
|
||||||
this.handleTerminalState(entry, queueId, {
|
|
||||||
status: 'error',
|
// Increment failure counter
|
||||||
message: 'Status check error'
|
entry.statusCheckFailures = (entry.statusCheckFailures || 0) + 1;
|
||||||
});
|
|
||||||
|
// Only show error after multiple consecutive failures
|
||||||
|
if (entry.statusCheckFailures >= MAX_STATUS_CHECK_FAILURES) {
|
||||||
|
this.handleTerminalState(entry, queueId, {
|
||||||
|
status: 'error',
|
||||||
|
message: 'Status check error: ' + error.message,
|
||||||
|
can_retry: !!entry.requestUrl
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
this.updateQueueOrder();
|
this.updateQueueOrder();
|
||||||
}, 2000);
|
}, 2000);
|
||||||
@@ -520,10 +554,8 @@ class DownloadQueue {
|
|||||||
// No items in container, append all visible entries
|
// No items in container, append all visible entries
|
||||||
container.innerHTML = ''; // Clear any empty state
|
container.innerHTML = ''; // Clear any empty state
|
||||||
visibleEntries.forEach(entry => {
|
visibleEntries.forEach(entry => {
|
||||||
// Start monitoring if needed
|
// We no longer automatically start monitoring here
|
||||||
if (!entry.intervalId) {
|
// Monitoring is now explicitly started by the methods that create downloads
|
||||||
this.startEntryMonitoring(entry.uniqueId);
|
|
||||||
}
|
|
||||||
container.appendChild(entry.element);
|
container.appendChild(entry.element);
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
@@ -541,10 +573,7 @@ class DownloadQueue {
|
|||||||
|
|
||||||
// Add visible entries in correct order
|
// Add visible entries in correct order
|
||||||
visibleEntries.forEach(entry => {
|
visibleEntries.forEach(entry => {
|
||||||
// Start monitoring if needed
|
// We no longer automatically start monitoring here
|
||||||
if (!entry.intervalId) {
|
|
||||||
this.startEntryMonitoring(entry.uniqueId);
|
|
||||||
}
|
|
||||||
container.appendChild(entry.element);
|
container.appendChild(entry.element);
|
||||||
|
|
||||||
// Mark the entry as not new anymore
|
// Mark the entry as not new anymore
|
||||||
@@ -553,14 +582,9 @@ class DownloadQueue {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Stop monitoring entries that are no longer visible
|
// We no longer start or stop monitoring based on visibility changes here
|
||||||
entries.slice(this.visibleCount).forEach(entry => {
|
// This allows the explicit monitoring control from the download methods
|
||||||
if (entry.intervalId) {
|
|
||||||
clearInterval(entry.intervalId);
|
|
||||||
entry.intervalId = null;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// Update footer
|
// Update footer
|
||||||
footer.innerHTML = '';
|
footer.innerHTML = '';
|
||||||
if (entries.length > this.visibleCount) {
|
if (entries.length > this.visibleCount) {
|
||||||
@@ -917,10 +941,23 @@ class DownloadQueue {
|
|||||||
entry.hasEnded = false;
|
entry.hasEnded = false;
|
||||||
entry.lastUpdated = Date.now();
|
entry.lastUpdated = Date.now();
|
||||||
entry.retryCount = (entry.retryCount || 0) + 1;
|
entry.retryCount = (entry.retryCount || 0) + 1;
|
||||||
|
entry.statusCheckFailures = 0; // Reset failure counter
|
||||||
logEl.textContent = 'Retry initiated...';
|
logEl.textContent = 'Retry initiated...';
|
||||||
|
|
||||||
// Start monitoring the new PRG file
|
// Verify the PRG file exists before starting monitoring
|
||||||
this.startEntryMonitoring(queueId);
|
try {
|
||||||
|
const verifyResponse = await fetch(`/api/prgs/${retryData.prg_file}`);
|
||||||
|
if (verifyResponse.ok) {
|
||||||
|
// Start monitoring the new PRG file
|
||||||
|
this.startEntryMonitoring(queueId);
|
||||||
|
} else {
|
||||||
|
// If verification fails, wait a moment and then start monitoring anyway
|
||||||
|
setTimeout(() => this.startEntryMonitoring(queueId), 2000);
|
||||||
|
}
|
||||||
|
} catch (verifyError) {
|
||||||
|
// If verification fails, wait a moment and then start monitoring anyway
|
||||||
|
setTimeout(() => this.startEntryMonitoring(queueId), 2000);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
logElement.textContent = 'Retry failed: invalid response from server';
|
logElement.textContent = 'Retry failed: invalid response from server';
|
||||||
}
|
}
|
||||||
@@ -940,10 +977,34 @@ class DownloadQueue {
|
|||||||
(item.artist ? `&artist=${encodeURIComponent(item.artist)}` : '');
|
(item.artist ? `&artist=${encodeURIComponent(item.artist)}` : '');
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
// Show a loading indicator
|
||||||
|
if (document.getElementById('queueIcon')) {
|
||||||
|
document.getElementById('queueIcon').classList.add('queue-icon-active');
|
||||||
|
}
|
||||||
|
|
||||||
|
// First create the queue entry with a preparation message
|
||||||
|
const tempItem = {...item, name: item.name || 'Preparing...'};
|
||||||
const response = await fetch(apiUrl);
|
const response = await fetch(apiUrl);
|
||||||
|
|
||||||
if (!response.ok) throw new Error('Network error');
|
if (!response.ok) throw new Error('Network error');
|
||||||
const data = await response.json();
|
const data = await response.json();
|
||||||
this.addDownload(item, 'track', data.prg_file, apiUrl);
|
|
||||||
|
// Add the download to the queue but don't start monitoring yet
|
||||||
|
const queueId = this.addDownload(item, 'track', data.prg_file, apiUrl, false);
|
||||||
|
|
||||||
|
// Ensure the PRG file exists and has initial data by making a status check
|
||||||
|
try {
|
||||||
|
const statusResponse = await fetch(`/api/prgs/${data.prg_file}`);
|
||||||
|
if (statusResponse.ok) {
|
||||||
|
// Only start monitoring after confirming the PRG file exists
|
||||||
|
const entry = this.downloadQueue[queueId];
|
||||||
|
if (entry && this.isEntryVisible(queueId)) {
|
||||||
|
this.startEntryMonitoring(queueId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (statusError) {
|
||||||
|
console.log('Initial status check pending, will retry on next interval');
|
||||||
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
this.dispatchEvent('downloadError', { error, item });
|
this.dispatchEvent('downloadError', { error, item });
|
||||||
throw error;
|
throw error;
|
||||||
@@ -960,10 +1021,31 @@ class DownloadQueue {
|
|||||||
(item.artist ? `&artist=${encodeURIComponent(item.artist)}` : '');
|
(item.artist ? `&artist=${encodeURIComponent(item.artist)}` : '');
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
// Show a loading indicator
|
||||||
|
if (document.getElementById('queueIcon')) {
|
||||||
|
document.getElementById('queueIcon').classList.add('queue-icon-active');
|
||||||
|
}
|
||||||
|
|
||||||
const response = await fetch(apiUrl);
|
const response = await fetch(apiUrl);
|
||||||
if (!response.ok) throw new Error('Network error');
|
if (!response.ok) throw new Error('Network error');
|
||||||
const data = await response.json();
|
const data = await response.json();
|
||||||
this.addDownload(item, 'playlist', data.prg_file, apiUrl);
|
|
||||||
|
// Add the download to the queue but don't start monitoring yet
|
||||||
|
const queueId = this.addDownload(item, 'playlist', data.prg_file, apiUrl, false);
|
||||||
|
|
||||||
|
// Ensure the PRG file exists and has initial data by making a status check
|
||||||
|
try {
|
||||||
|
const statusResponse = await fetch(`/api/prgs/${data.prg_file}`);
|
||||||
|
if (statusResponse.ok) {
|
||||||
|
// Only start monitoring after confirming the PRG file exists
|
||||||
|
const entry = this.downloadQueue[queueId];
|
||||||
|
if (entry && this.isEntryVisible(queueId)) {
|
||||||
|
this.startEntryMonitoring(queueId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (statusError) {
|
||||||
|
console.log('Initial status check pending, will retry on next interval');
|
||||||
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
this.dispatchEvent('downloadError', { error, item });
|
this.dispatchEvent('downloadError', { error, item });
|
||||||
throw error;
|
throw error;
|
||||||
@@ -981,15 +1063,41 @@ class DownloadQueue {
|
|||||||
(item.artist ? `&artist=${encodeURIComponent(item.artist)}` : '');
|
(item.artist ? `&artist=${encodeURIComponent(item.artist)}` : '');
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
// Show a loading indicator
|
||||||
|
if (document.getElementById('queueIcon')) {
|
||||||
|
document.getElementById('queueIcon').classList.add('queue-icon-active');
|
||||||
|
}
|
||||||
|
|
||||||
const response = await fetch(apiUrl);
|
const response = await fetch(apiUrl);
|
||||||
if (!response.ok) throw new Error('Network error');
|
if (!response.ok) throw new Error('Network error');
|
||||||
const data = await response.json();
|
const data = await response.json();
|
||||||
|
|
||||||
|
// Track all queue IDs created
|
||||||
|
const queueIds = [];
|
||||||
|
|
||||||
if (data.album_prg_files && Array.isArray(data.album_prg_files)) {
|
if (data.album_prg_files && Array.isArray(data.album_prg_files)) {
|
||||||
data.album_prg_files.forEach(prgFile => {
|
data.album_prg_files.forEach(prgFile => {
|
||||||
this.addDownload(item, 'album', prgFile, apiUrl);
|
const queueId = this.addDownload(item, 'album', prgFile, apiUrl, false);
|
||||||
|
queueIds.push({queueId, prgFile});
|
||||||
});
|
});
|
||||||
} else if (data.prg_file) {
|
} else if (data.prg_file) {
|
||||||
this.addDownload(item, 'album', data.prg_file, apiUrl);
|
const queueId = this.addDownload(item, 'album', data.prg_file, apiUrl, false);
|
||||||
|
queueIds.push({queueId, prgFile: data.prg_file});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Start monitoring each entry after confirming PRG files exist
|
||||||
|
for (const {queueId, prgFile} of queueIds) {
|
||||||
|
try {
|
||||||
|
const statusResponse = await fetch(`/api/prgs/${prgFile}`);
|
||||||
|
if (statusResponse.ok) {
|
||||||
|
const entry = this.downloadQueue[queueId];
|
||||||
|
if (entry && this.isEntryVisible(queueId)) {
|
||||||
|
this.startEntryMonitoring(queueId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (statusError) {
|
||||||
|
console.log(`Initial status check pending for ${prgFile}, will retry on next interval`);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
this.dispatchEvent('downloadError', { error, item });
|
this.dispatchEvent('downloadError', { error, item });
|
||||||
@@ -1007,10 +1115,31 @@ class DownloadQueue {
|
|||||||
(item.artist ? `&artist=${encodeURIComponent(item.artist)}` : '');
|
(item.artist ? `&artist=${encodeURIComponent(item.artist)}` : '');
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
// Show a loading indicator
|
||||||
|
if (document.getElementById('queueIcon')) {
|
||||||
|
document.getElementById('queueIcon').classList.add('queue-icon-active');
|
||||||
|
}
|
||||||
|
|
||||||
const response = await fetch(apiUrl);
|
const response = await fetch(apiUrl);
|
||||||
if (!response.ok) throw new Error('Network error');
|
if (!response.ok) throw new Error('Network error');
|
||||||
const data = await response.json();
|
const data = await response.json();
|
||||||
this.addDownload(item, 'album', data.prg_file, apiUrl);
|
|
||||||
|
// Add the download to the queue but don't start monitoring yet
|
||||||
|
const queueId = this.addDownload(item, 'album', data.prg_file, apiUrl, false);
|
||||||
|
|
||||||
|
// Ensure the PRG file exists and has initial data by making a status check
|
||||||
|
try {
|
||||||
|
const statusResponse = await fetch(`/api/prgs/${data.prg_file}`);
|
||||||
|
if (statusResponse.ok) {
|
||||||
|
// Only start monitoring after confirming the PRG file exists
|
||||||
|
const entry = this.downloadQueue[queueId];
|
||||||
|
if (entry && this.isEntryVisible(queueId)) {
|
||||||
|
this.startEntryMonitoring(queueId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (statusError) {
|
||||||
|
console.log('Initial status check pending, will retry on next interval');
|
||||||
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
this.dispatchEvent('downloadError', { error, item });
|
this.dispatchEvent('downloadError', { error, item });
|
||||||
throw error;
|
throw error;
|
||||||
|
|||||||
@@ -110,9 +110,13 @@ function renderTrack(track) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
downloadQueue.startTrackDownload(trackUrl, { name: track.name || 'Unknown Track' })
|
// Create a local download function that uses our own API call instead of downloadQueue.startTrackDownload
|
||||||
|
// This mirrors the approach used in main.js that works properly
|
||||||
|
startDownload(trackUrl, 'track', { name: track.name || 'Unknown Track', artist: track.artists?.[0]?.name })
|
||||||
.then(() => {
|
.then(() => {
|
||||||
downloadBtn.innerHTML = `<span>Queued!</span>`;
|
downloadBtn.innerHTML = `<span>Queued!</span>`;
|
||||||
|
// Make the queue visible to show the download
|
||||||
|
downloadQueue.toggleVisibility(true);
|
||||||
})
|
})
|
||||||
.catch(err => {
|
.catch(err => {
|
||||||
showError('Failed to queue track download: ' + (err?.message || 'Unknown error'));
|
showError('Failed to queue track download: ' + (err?.message || 'Unknown error'));
|
||||||
@@ -171,8 +175,36 @@ async function startDownload(url, type, item) {
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
const response = await fetch(apiUrl);
|
const response = await fetch(apiUrl);
|
||||||
|
if (!response.ok) {
|
||||||
|
throw new Error(`Server returned ${response.status}`);
|
||||||
|
}
|
||||||
|
|
||||||
const data = await response.json();
|
const data = await response.json();
|
||||||
downloadQueue.addDownload(item, type, data.prg_file);
|
|
||||||
|
if (!data.prg_file) {
|
||||||
|
throw new Error('Server did not return a valid PRG file');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add the download to the queue but don't start monitoring yet
|
||||||
|
const queueId = downloadQueue.addDownload(item, type, data.prg_file, apiUrl, false);
|
||||||
|
|
||||||
|
// Ensure the PRG file exists and has initial data by making a status check
|
||||||
|
try {
|
||||||
|
// Wait a short time before checking the status to give server time to create the file
|
||||||
|
await new Promise(resolve => setTimeout(resolve, 1000));
|
||||||
|
|
||||||
|
const statusResponse = await fetch(`/api/prgs/${data.prg_file}`);
|
||||||
|
if (statusResponse.ok) {
|
||||||
|
// Only start monitoring after confirming the PRG file exists
|
||||||
|
const entry = downloadQueue.downloadQueue[queueId];
|
||||||
|
if (entry) {
|
||||||
|
// Start monitoring regardless of visibility
|
||||||
|
downloadQueue.startEntryMonitoring(queueId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (statusError) {
|
||||||
|
console.log('Initial status check pending, will retry on next interval');
|
||||||
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
showError('Download failed: ' + (error?.message || 'Unknown error'));
|
showError('Download failed: ' + (error?.message || 'Unknown error'));
|
||||||
throw error;
|
throw error;
|
||||||
|
|||||||
Reference in New Issue
Block a user