297 lines
10 KiB
JavaScript
297 lines
10 KiB
JavaScript
/**
|
|
* AniWorld - Selection Manager Module
|
|
*
|
|
* Handles series selection for downloads.
|
|
*
|
|
* Dependencies: constants.js, api-client.js, ui-utils.js, series-manager.js
|
|
*/
|
|
|
|
var AniWorld = window.AniWorld || {};
|
|
|
|
AniWorld.SelectionManager = (function() {
|
|
'use strict';
|
|
|
|
const API = AniWorld.Constants.API;
|
|
|
|
// State
|
|
let selectedSeries = new Set();
|
|
|
|
/**
|
|
* Initialize the selection manager
|
|
*/
|
|
function init() {
|
|
bindEvents();
|
|
}
|
|
|
|
/**
|
|
* Bind UI events
|
|
*/
|
|
function bindEvents() {
|
|
const selectAllBtn = document.getElementById('select-all');
|
|
if (selectAllBtn) {
|
|
selectAllBtn.addEventListener('click', toggleSelectAll);
|
|
}
|
|
|
|
const downloadBtn = document.getElementById('download-selected');
|
|
if (downloadBtn) {
|
|
downloadBtn.addEventListener('click', downloadSelected);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Toggle series selection
|
|
* @param {string} key - Series key
|
|
* @param {boolean} selected - Whether to select or deselect
|
|
*/
|
|
function toggleSerieSelection(key, selected) {
|
|
// Only allow selection of series with missing episodes
|
|
const serie = AniWorld.SeriesManager.findByKey(key);
|
|
if (!serie || serie.missing_episodes === 0) {
|
|
// Uncheck the checkbox if it was checked for a complete series
|
|
const checkbox = document.querySelector('input[data-key="' + key + '"]');
|
|
if (checkbox) checkbox.checked = false;
|
|
return;
|
|
}
|
|
|
|
if (selected) {
|
|
selectedSeries.add(key);
|
|
} else {
|
|
selectedSeries.delete(key);
|
|
}
|
|
|
|
updateSelectionUI();
|
|
}
|
|
|
|
/**
|
|
* Check if a series is selected
|
|
* @param {string} key - Series key
|
|
* @returns {boolean}
|
|
*/
|
|
function isSelected(key) {
|
|
return selectedSeries.has(key);
|
|
}
|
|
|
|
/**
|
|
* Update selection UI (buttons and card styles)
|
|
*/
|
|
function updateSelectionUI() {
|
|
const downloadBtn = document.getElementById('download-selected');
|
|
const selectAllBtn = document.getElementById('select-all');
|
|
|
|
// Get series that can be selected (have missing episodes)
|
|
const selectableSeriesData = AniWorld.SeriesManager.getFilteredSeriesData().length > 0 ?
|
|
AniWorld.SeriesManager.getFilteredSeriesData() : AniWorld.SeriesManager.getSeriesData();
|
|
const selectableSeries = selectableSeriesData.filter(function(serie) {
|
|
return serie.missing_episodes > 0;
|
|
});
|
|
const selectableKeys = selectableSeries.map(function(serie) {
|
|
return serie.key;
|
|
});
|
|
|
|
downloadBtn.disabled = selectedSeries.size === 0;
|
|
|
|
const allSelectableSelected = selectableKeys.every(function(key) {
|
|
return selectedSeries.has(key);
|
|
});
|
|
|
|
if (selectedSeries.size === 0) {
|
|
selectAllBtn.innerHTML = '<i class="fas fa-check-double"></i><span>Select All</span>';
|
|
} else if (allSelectableSelected && selectableKeys.length > 0) {
|
|
selectAllBtn.innerHTML = '<i class="fas fa-times"></i><span>Deselect All</span>';
|
|
} else {
|
|
selectAllBtn.innerHTML = '<i class="fas fa-check-double"></i><span>Select All</span>';
|
|
}
|
|
|
|
// Update card appearances
|
|
document.querySelectorAll('.series-card').forEach(function(card) {
|
|
const key = card.dataset.key;
|
|
const isSelectedCard = selectedSeries.has(key);
|
|
card.classList.toggle('selected', isSelectedCard);
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Toggle select all / deselect all
|
|
*/
|
|
function toggleSelectAll() {
|
|
// Get series that can be selected (have missing episodes)
|
|
const selectableSeriesData = AniWorld.SeriesManager.getFilteredSeriesData().length > 0 ?
|
|
AniWorld.SeriesManager.getFilteredSeriesData() : AniWorld.SeriesManager.getSeriesData();
|
|
const selectableSeries = selectableSeriesData.filter(function(serie) {
|
|
return serie.missing_episodes > 0;
|
|
});
|
|
const selectableKeys = selectableSeries.map(function(serie) {
|
|
return serie.key;
|
|
});
|
|
|
|
const allSelectableSelected = selectableKeys.every(function(key) {
|
|
return selectedSeries.has(key);
|
|
});
|
|
|
|
if (allSelectableSelected && selectedSeries.size > 0) {
|
|
// Deselect all selectable series
|
|
selectableKeys.forEach(function(key) {
|
|
selectedSeries.delete(key);
|
|
});
|
|
document.querySelectorAll('.series-checkbox:not([disabled])').forEach(function(cb) {
|
|
cb.checked = false;
|
|
});
|
|
} else {
|
|
// Select all selectable series
|
|
selectableKeys.forEach(function(key) {
|
|
selectedSeries.add(key);
|
|
});
|
|
document.querySelectorAll('.series-checkbox:not([disabled])').forEach(function(cb) {
|
|
cb.checked = true;
|
|
});
|
|
}
|
|
|
|
updateSelectionUI();
|
|
}
|
|
|
|
/**
|
|
* Clear all selections
|
|
*/
|
|
function clearSelection() {
|
|
selectedSeries.clear();
|
|
document.querySelectorAll('.series-checkbox').forEach(function(cb) {
|
|
cb.checked = false;
|
|
});
|
|
updateSelectionUI();
|
|
}
|
|
|
|
/**
|
|
* Download selected series
|
|
*/
|
|
async function downloadSelected() {
|
|
console.log('=== downloadSelected - Using key as primary identifier ===');
|
|
if (selectedSeries.size === 0) {
|
|
AniWorld.UI.showToast('No series selected', 'warning');
|
|
return;
|
|
}
|
|
|
|
try {
|
|
const selectedKeys = Array.from(selectedSeries);
|
|
console.log('=== Starting download for selected series ===');
|
|
console.log('Selected keys:', selectedKeys);
|
|
|
|
let totalEpisodesAdded = 0;
|
|
let failedSeries = [];
|
|
|
|
// For each selected series, get its missing episodes and add to queue
|
|
for (var i = 0; i < selectedKeys.length; i++) {
|
|
const key = selectedKeys[i];
|
|
const serie = AniWorld.SeriesManager.findByKey(key);
|
|
if (!serie || !serie.episodeDict) {
|
|
console.error('Serie not found or has no episodeDict for key:', key, serie);
|
|
failedSeries.push(key);
|
|
continue;
|
|
}
|
|
|
|
// Validate required fields
|
|
if (!serie.key) {
|
|
console.error('Serie missing key:', serie);
|
|
failedSeries.push(key);
|
|
continue;
|
|
}
|
|
|
|
// Convert episodeDict format {season: [episodes]} to episode identifiers
|
|
const episodes = [];
|
|
Object.entries(serie.episodeDict).forEach(function(entry) {
|
|
const season = entry[0];
|
|
const episodeNumbers = entry[1];
|
|
if (Array.isArray(episodeNumbers)) {
|
|
episodeNumbers.forEach(function(episode) {
|
|
episodes.push({
|
|
season: parseInt(season),
|
|
episode: episode
|
|
});
|
|
});
|
|
}
|
|
});
|
|
|
|
if (episodes.length === 0) {
|
|
console.log('No episodes to add for serie:', serie.name);
|
|
continue;
|
|
}
|
|
|
|
// Use folder name as fallback if serie name is empty
|
|
const serieName = serie.name && serie.name.trim() ? serie.name : serie.folder;
|
|
|
|
// Add episodes to download queue
|
|
const requestBody = {
|
|
serie_id: serie.key,
|
|
serie_folder: serie.folder,
|
|
serie_name: serieName,
|
|
episodes: episodes,
|
|
priority: 'NORMAL'
|
|
};
|
|
console.log('Sending queue add request:', requestBody);
|
|
|
|
const response = await AniWorld.ApiClient.post(API.QUEUE_ADD, requestBody);
|
|
|
|
if (!response) {
|
|
failedSeries.push(key);
|
|
continue;
|
|
}
|
|
|
|
const data = await response.json();
|
|
console.log('Queue add response:', response.status, data);
|
|
|
|
// Log validation errors in detail
|
|
if (data.detail && Array.isArray(data.detail)) {
|
|
console.error('Validation errors:', JSON.stringify(data.detail, null, 2));
|
|
}
|
|
|
|
if (response.ok && data.status === 'success') {
|
|
totalEpisodesAdded += episodes.length;
|
|
} else {
|
|
console.error('Failed to add to queue:', data);
|
|
failedSeries.push(key);
|
|
}
|
|
}
|
|
|
|
// Show result message
|
|
console.log('=== Download request complete ===');
|
|
console.log('Total episodes added:', totalEpisodesAdded);
|
|
console.log('Failed series (keys):', failedSeries);
|
|
|
|
if (totalEpisodesAdded > 0) {
|
|
const message = failedSeries.length > 0
|
|
? 'Added ' + totalEpisodesAdded + ' episode(s) to queue (' + failedSeries.length + ' series failed)'
|
|
: 'Added ' + totalEpisodesAdded + ' episode(s) to download queue';
|
|
AniWorld.UI.showToast(message, 'success');
|
|
} else {
|
|
const errorDetails = failedSeries.length > 0
|
|
? 'Failed series (keys): ' + failedSeries.join(', ')
|
|
: 'No episodes were added. Check browser console for details.';
|
|
console.error('Failed to add episodes. Details:', errorDetails);
|
|
AniWorld.UI.showToast('Failed to add episodes to queue. Check console for details.', 'error');
|
|
}
|
|
} catch (error) {
|
|
console.error('Download error:', error);
|
|
AniWorld.UI.showToast('Failed to start download', 'error');
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Get selected series count
|
|
* @returns {number}
|
|
*/
|
|
function getSelectionCount() {
|
|
return selectedSeries.size;
|
|
}
|
|
|
|
// Public API
|
|
return {
|
|
init: init,
|
|
toggleSerieSelection: toggleSerieSelection,
|
|
isSelected: isSelected,
|
|
updateSelectionUI: updateSelectionUI,
|
|
toggleSelectAll: toggleSelectAll,
|
|
clearSelection: clearSelection,
|
|
downloadSelected: downloadSelected,
|
|
getSelectionCount: getSelectionCount
|
|
};
|
|
})();
|