feat: Add batch download for entire anime seasons
Add ability to download all episodes of a season with one click. Backend changes: - New POST /api/anime/download-season endpoint - Retrieves all episodes and creates download tasks for each - Returns list of task IDs and total episode count Frontend changes: - Add "Toute la saison" button next to episode selector - Button shown immediately when episodes are loaded - Confirmation dialog before starting batch download - Success message showing number of episodes queued Features: - Respects max_parallel limit (default: 3 concurrent downloads) - Proper episode naming (e.g., "Hells Paradise - Episode 01.mp4") - Works with all anime providers (Anime-Sama, Anime-Ultime, etc.) Example usage: - Click "Toute la saison" button on any anime card - Confirm the dialog - All episodes are queued and download automatically Generated with [Claude Code](https://claude.ai/code) via [Happy](https://happy.engineering) Co-Authored-By: Claude <noreply@anthropic.com> Co-Authored-By: Happy <yesreply@happy.engineering>
This commit is contained in:
@@ -259,6 +259,36 @@ async def download_anime_episode(
|
||||
return {"task_id": task.id, "task": task}
|
||||
|
||||
|
||||
@app.post("/api/anime/download-season")
|
||||
async def download_anime_season(
|
||||
url: str,
|
||||
background_tasks: BackgroundTasks,
|
||||
lang: str = "vostfr"
|
||||
):
|
||||
"""Download all episodes of an anime season"""
|
||||
from app.downloaders import get_downloader
|
||||
|
||||
downloader = get_downloader(url)
|
||||
episodes = await downloader.get_episodes(url, lang)
|
||||
|
||||
if not episodes:
|
||||
raise HTTPException(status_code=404, detail="No episodes found")
|
||||
|
||||
# Create download tasks for all episodes
|
||||
task_ids = []
|
||||
for episode in episodes:
|
||||
request = DownloadRequest(url=episode['url'])
|
||||
task = download_manager.create_task(request)
|
||||
task_ids.append(task.id)
|
||||
background_tasks.add_task(download_manager.start_download, task.id)
|
||||
|
||||
return {
|
||||
"message": f"Started downloading {len(task_ids)} episodes",
|
||||
"task_ids": task_ids,
|
||||
"total_episodes": len(episodes)
|
||||
}
|
||||
|
||||
|
||||
# Video Streaming endpoints
|
||||
@app.get("/video/{task_id}")
|
||||
async def stream_video(task_id: str, request: Request):
|
||||
|
||||
+33
-7
@@ -645,6 +645,12 @@
|
||||
</svg>
|
||||
Télécharger
|
||||
</button>
|
||||
<button class="btn-primary" style="background: linear-gradient(45deg, #ff6b6b, #ffa500);" onclick="downloadEntireSeason('${encodeURIComponent(anime.url)}', '${lang}')" title="Télécharger toute la saison">
|
||||
<svg fill="none" stroke="currentColor" viewBox="0 0 24 24" style="width:14px;height:14px;margin-right:4px;">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 16v1a3 3 0 003 3h10a3 3 0 003-3v-1m-4-8l-4-4m0 0L8 8m4-4v12"></path>
|
||||
</svg>
|
||||
Toute la saison
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
@@ -690,15 +696,9 @@
|
||||
selectElement.appendChild(option);
|
||||
});
|
||||
|
||||
// Show download button when episode is selected
|
||||
selectElement.onchange = () => {
|
||||
// Show download buttons immediately (season button is always useful)
|
||||
const actionsDiv = document.getElementById(actionsId);
|
||||
if (selectElement.value) {
|
||||
actionsDiv.style.display = 'flex';
|
||||
} else {
|
||||
actionsDiv.style.display = 'none';
|
||||
}
|
||||
};
|
||||
|
||||
// Store the selected episode URL for download
|
||||
selectElement.dataset.animeUrl = url;
|
||||
@@ -754,6 +754,32 @@
|
||||
}
|
||||
}
|
||||
|
||||
async function downloadEntireSeason(encodedUrl, lang) {
|
||||
const url = decodeURIComponent(encodedUrl);
|
||||
|
||||
if (!confirm(`⚠️ Attention: Vous allez télécharger toute la saison. Cela peut prendre du temps et utiliser beaucoup d'espace disque.\n\nVoulez-vous continuer ?`)) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const response = await fetch(`${API_BASE}/anime/download-season?url=${encodeURIComponent(url)}&lang=${lang}`, {
|
||||
method: 'POST'
|
||||
});
|
||||
|
||||
if (response.ok) {
|
||||
const data = await response.json();
|
||||
loadDownloads();
|
||||
alert(`✅ ${data.message}\n\n${data.total_episodes} épisodes ont été ajoutés à la file de téléchargement!`);
|
||||
} else {
|
||||
const error = await response.json();
|
||||
alert(`Erreur: ${error.detail || 'Impossible de démarrer le téléchargement de la saison'}`);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error:', error);
|
||||
alert('Erreur lors du démarrage du téléchargement de la saison');
|
||||
}
|
||||
}
|
||||
|
||||
async function getProvidersInfo() {
|
||||
if (!searchResultsCache.providers) {
|
||||
const response = await fetch(`${API_BASE}/providers`);
|
||||
|
||||
Reference in New Issue
Block a user