Files
ohm_streaming/static/js/watchlist.js
T
root da5403a307 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 <[email protected]>
2026-02-24 09:13:22 +00:00

319 lines
7.3 KiB
JavaScript

/**
* Watchlist management and auto-download UI
* Note: API_BASE is defined in api.js (loaded before this file)
*/
/**
* Get user's watchlist
*/
async function getWatchlist(status = null) {
const token = localStorage.getItem('auth_token');
if (!token) {
throw new Error('Not authenticated');
}
let url = `${API_BASE}/watchlist`;
if (status) {
url += `?status=${status}`;
}
const response = await fetch(url, {
headers: {
'Authorization': `Bearer ${token}`
}
});
if (!response.ok) {
throw new Error('Failed to fetch watchlist');
}
return await response.json();
}
/**
* Add anime to watchlist
*/
async function addToWatchlist(animeData) {
const token = localStorage.getItem('auth_token');
if (!token) {
throw new Error('Not authenticated');
}
const response = await fetch(`${API_BASE}/watchlist`, {
method: 'POST',
headers: {
'Authorization': `Bearer ${token}`,
'Content-Type': 'application/json'
},
body: JSON.stringify(animeData)
});
if (!response.ok) {
const error = await response.json();
throw new Error(error.detail || 'Failed to add to watchlist');
}
return await response.json();
}
/**
* Update watchlist item
*/
async function updateWatchlistItem(itemId, updateData) {
const token = localStorage.getItem('auth_token');
if (!token) {
throw new Error('Not authenticated');
}
const response = await fetch(`${API_BASE}/watchlist/${itemId}`, {
method: 'PUT',
headers: {
'Authorization': `Bearer ${token}`,
'Content-Type': 'application/json'
},
body: JSON.stringify(updateData)
});
if (!response.ok) {
throw new Error('Failed to update watchlist item');
}
return await response.json();
}
/**
* Delete from watchlist
*/
async function deleteFromWatchlist(itemId) {
const token = localStorage.getItem('auth_token');
if (!token) {
throw new Error('Not authenticated');
}
const response = await fetch(`${API_BASE}/watchlist/${itemId}`, {
method: 'DELETE',
headers: {
'Authorization': `Bearer ${token}`
}
});
if (!response.ok) {
throw new Error('Failed to delete from watchlist');
}
return await response.json();
}
/**
* Pause watchlist item
*/
async function pauseWatchlistItem(itemId) {
return await updateWatchlistItem(itemId, { status: 'paused' });
}
/**
* Resume watchlist item
*/
async function resumeWatchlistItem(itemId) {
return await updateWatchlistItem(itemId, { status: 'active' });
}
/**
* Check specific anime for new episodes
*/
async function checkWatchlistItem(itemId) {
const token = localStorage.getItem('auth_token');
if (!token) {
throw new Error('Not authenticated');
}
const response = await fetch(`${API_BASE}/watchlist/${itemId}/check`, {
method: 'POST',
headers: {
'Authorization': `Bearer ${token}`
}
});
if (!response.ok) {
throw new Error('Failed to check for new episodes');
}
return await response.json();
}
/**
* Check all watchlist items
*/
async function checkAllWatchlistItems() {
const token = localStorage.getItem('auth_token');
if (!token) {
throw new Error('Not authenticated');
}
const response = await fetch(`${API_BASE}/watchlist/check-all`, {
method: 'POST',
headers: {
'Authorization': `Bearer ${token}`
}
});
if (!response.ok) {
throw new Error('Failed to check all items');
}
return await response.json();
}
/**
* Get watchlist settings
*/
async function getWatchlistSettings() {
const token = localStorage.getItem('auth_token');
if (!token) {
throw new Error('Not authenticated');
}
const response = await fetch(`${API_BASE}/watchlist/settings`, {
headers: {
'Authorization': `Bearer ${token}`
}
});
if (!response.ok) {
throw new Error('Failed to fetch settings');
}
return await response.json();
}
/**
* Update watchlist settings
*/
async function updateWatchlistSettings(settings) {
const token = localStorage.getItem('auth_token');
if (!token) {
throw new Error('Not authenticated');
}
const response = await fetch(`${API_BASE}/watchlist/settings`, {
method: 'PUT',
headers: {
'Authorization': `Bearer ${token}`,
'Content-Type': 'application/json'
},
body: JSON.stringify(settings)
});
if (!response.ok) {
throw new Error('Failed to update settings');
}
return await response.json();
}
/**
* Get watchlist statistics
*/
async function getWatchlistStats() {
const token = localStorage.getItem('auth_token');
if (!token) {
throw new Error('Not authenticated');
}
const response = await fetch(`${API_BASE}/watchlist/stats`, {
headers: {
'Authorization': `Bearer ${token}`
}
});
if (!response.ok) {
throw new Error('Failed to fetch statistics');
}
return await response.json();
}
/**
* Get scheduler status
*/
async function getSchedulerStatus() {
const token = localStorage.getItem('auth_token');
if (!token) {
throw new Error('Not authenticated');
}
const response = await fetch(`${API_BASE}/watchlist/scheduler/status`, {
headers: {
'Authorization': `Bearer ${token}`
}
});
if (!response.ok) {
throw new Error('Failed to fetch scheduler status');
}
return await response.json();
}
/**
* Start scheduler
*/
async function startScheduler() {
const token = localStorage.getItem('auth_token');
if (!token) {
throw new Error('Not authenticated');
}
const response = await fetch(`${API_BASE}/watchlist/scheduler/start`, {
method: 'POST',
headers: {
'Authorization': `Bearer ${token}`
}
});
if (!response.ok) {
throw new Error('Failed to start scheduler');
}
return await response.json();
}
/**
* Stop scheduler
*/
async function stopScheduler() {
const token = localStorage.getItem('auth_token');
if (!token) {
throw new Error('Not authenticated');
}
const response = await fetch(`${API_BASE}/watchlist/scheduler/stop`, {
method: 'POST',
headers: {
'Authorization': `Bearer ${token}`
}
});
if (!response.ok) {
throw new Error('Failed to stop scheduler');
}
return await response.json();
}
// Make functions available globally
window.getWatchlist = getWatchlist;
window.addToWatchlist = addToWatchlist;
window.updateWatchlistItem = updateWatchlistItem;
window.deleteFromWatchlist = deleteFromWatchlist;
window.pauseWatchlistItem = pauseWatchlistItem;
window.resumeWatchlistItem = resumeWatchlistItem;
window.checkWatchlistItem = checkWatchlistItem;
window.checkAllWatchlistItems = checkAllWatchlistItems;
window.getWatchlistSettings = getWatchlistSettings;
window.updateWatchlistSettings = updateWatchlistSettings;
window.getWatchlistStats = getWatchlistStats;
window.getSchedulerStatus = getSchedulerStatus;
window.startScheduler = startScheduler;
window.stopScheduler = stopScheduler;