feat: Complete watchlist & auto-download system with UI
Implement comprehensive watchlist system with automatic episode detection
and downloading. Features include per-user watchlists, scheduler-based
periodic checks, and a modern web UI.
**Backend Components:**
- WatchlistManager: JSON-based storage with multi-tenant support
- EpisodeChecker: Detects and downloads new episodes automatically
- AutoDownloadScheduler: APScheduler-based periodic task execution
- Complete REST API for CRUD operations and scheduler control
**Frontend Components:**
- Modern watchlist page with dark theme and animations
- Real-time status updates and progress tracking
- Scheduler controls with next-run display
- Add anime directly from search results
**Models & Configuration:**
- WatchlistItem with status, quality, and auto-download settings
- WatchlistSettings for global configuration
- Per-user statistics and provider tracking
**API Endpoints:**
- GET/POST /api/watchlist - List and add items
- PUT/DELETE /api/watchlist/{id} - Update and delete
- POST /api/watchlist/{id}/check - Manual check trigger
- POST /api/watchlist/check-all - Check all due items
- GET/PUT /api/watchlist/settings - Global settings
- GET /api/watchlist/stats - Statistics
- GET/POST /api/watchlist/scheduler/* - Scheduler control
**Configuration Files:**
- config/watchlist.json - User watchlist data
- config/watchlist_settings.json - Global settings
Generated with [Claude Code](https://claude.ai/code)
Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
+13
-14
@@ -1,14 +1,13 @@
|
||||
/**
|
||||
* Watchlist management and auto-download UI
|
||||
* Note: API_BASE is defined in api.js (loaded before this file)
|
||||
*/
|
||||
|
||||
const API_BASE = '/api';
|
||||
|
||||
/**
|
||||
* Get user's watchlist
|
||||
*/
|
||||
async function getWatchlist(status = null) {
|
||||
const token = localStorage.getItem('token');
|
||||
const token = localStorage.getItem('auth_token');
|
||||
if (!token) {
|
||||
throw new Error('Not authenticated');
|
||||
}
|
||||
@@ -35,7 +34,7 @@ async function getWatchlist(status = null) {
|
||||
* Add anime to watchlist
|
||||
*/
|
||||
async function addToWatchlist(animeData) {
|
||||
const token = localStorage.getItem('token');
|
||||
const token = localStorage.getItem('auth_token');
|
||||
if (!token) {
|
||||
throw new Error('Not authenticated');
|
||||
}
|
||||
@@ -61,7 +60,7 @@ async function addToWatchlist(animeData) {
|
||||
* Update watchlist item
|
||||
*/
|
||||
async function updateWatchlistItem(itemId, updateData) {
|
||||
const token = localStorage.getItem('token');
|
||||
const token = localStorage.getItem('auth_token');
|
||||
if (!token) {
|
||||
throw new Error('Not authenticated');
|
||||
}
|
||||
@@ -86,7 +85,7 @@ async function updateWatchlistItem(itemId, updateData) {
|
||||
* Delete from watchlist
|
||||
*/
|
||||
async function deleteFromWatchlist(itemId) {
|
||||
const token = localStorage.getItem('token');
|
||||
const token = localStorage.getItem('auth_token');
|
||||
if (!token) {
|
||||
throw new Error('Not authenticated');
|
||||
}
|
||||
@@ -123,7 +122,7 @@ async function resumeWatchlistItem(itemId) {
|
||||
* Check specific anime for new episodes
|
||||
*/
|
||||
async function checkWatchlistItem(itemId) {
|
||||
const token = localStorage.getItem('token');
|
||||
const token = localStorage.getItem('auth_token');
|
||||
if (!token) {
|
||||
throw new Error('Not authenticated');
|
||||
}
|
||||
@@ -146,7 +145,7 @@ async function checkWatchlistItem(itemId) {
|
||||
* Check all watchlist items
|
||||
*/
|
||||
async function checkAllWatchlistItems() {
|
||||
const token = localStorage.getItem('token');
|
||||
const token = localStorage.getItem('auth_token');
|
||||
if (!token) {
|
||||
throw new Error('Not authenticated');
|
||||
}
|
||||
@@ -169,7 +168,7 @@ async function checkAllWatchlistItems() {
|
||||
* Get watchlist settings
|
||||
*/
|
||||
async function getWatchlistSettings() {
|
||||
const token = localStorage.getItem('token');
|
||||
const token = localStorage.getItem('auth_token');
|
||||
if (!token) {
|
||||
throw new Error('Not authenticated');
|
||||
}
|
||||
@@ -191,7 +190,7 @@ async function getWatchlistSettings() {
|
||||
* Update watchlist settings
|
||||
*/
|
||||
async function updateWatchlistSettings(settings) {
|
||||
const token = localStorage.getItem('token');
|
||||
const token = localStorage.getItem('auth_token');
|
||||
if (!token) {
|
||||
throw new Error('Not authenticated');
|
||||
}
|
||||
@@ -216,7 +215,7 @@ async function updateWatchlistSettings(settings) {
|
||||
* Get watchlist statistics
|
||||
*/
|
||||
async function getWatchlistStats() {
|
||||
const token = localStorage.getItem('token');
|
||||
const token = localStorage.getItem('auth_token');
|
||||
if (!token) {
|
||||
throw new Error('Not authenticated');
|
||||
}
|
||||
@@ -238,7 +237,7 @@ async function getWatchlistStats() {
|
||||
* Get scheduler status
|
||||
*/
|
||||
async function getSchedulerStatus() {
|
||||
const token = localStorage.getItem('token');
|
||||
const token = localStorage.getItem('auth_token');
|
||||
if (!token) {
|
||||
throw new Error('Not authenticated');
|
||||
}
|
||||
@@ -260,7 +259,7 @@ async function getSchedulerStatus() {
|
||||
* Start scheduler
|
||||
*/
|
||||
async function startScheduler() {
|
||||
const token = localStorage.getItem('token');
|
||||
const token = localStorage.getItem('auth_token');
|
||||
if (!token) {
|
||||
throw new Error('Not authenticated');
|
||||
}
|
||||
@@ -283,7 +282,7 @@ async function startScheduler() {
|
||||
* Stop scheduler
|
||||
*/
|
||||
async function stopScheduler() {
|
||||
const token = localStorage.getItem('token');
|
||||
const token = localStorage.getItem('auth_token');
|
||||
if (!token) {
|
||||
throw new Error('Not authenticated');
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user