Add DRM downloads, scrapers, gallery index, and UI improvements

- DRM video download pipeline with pywidevine subprocess for Widevine key acquisition
- Scraper system: forum threads, Coomer/Kemono API, and MediaLink (Fapello) scrapers
- SQLite-backed media index for instant gallery loads with startup scan
- Duplicate detection and gallery filtering/sorting
- HLS video component, log viewer, and scrape management UI
- Dockerfile updated for Python/pywidevine, docker-compose volume for CDM

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Trey t
2026-02-16 11:29:11 -06:00
parent c60de19348
commit 1e5f54f60b
28 changed files with 4736 additions and 203 deletions
+81
View File
@@ -114,3 +114,84 @@ export function getGalleryFiles({ folder, folders, type, sort, offset, limit } =
const query = buildQuery({ folder, folders: folders ? folders.join(',') : undefined, type, sort, offset, limit });
return request(`/api/gallery/files${query}`);
}
export function rescanMedia() {
return request('/api/gallery/rescan', { method: 'POST' });
}
export function getRescanStatus() {
return request('/api/gallery/rescan/status');
}
export function generateThumbs() {
return request('/api/gallery/generate-thumbs', { method: 'POST' });
}
export function getThumbsStatus() {
return request('/api/gallery/generate-thumbs/status');
}
export function scanDuplicates() {
return request('/api/gallery/scan-duplicates', { method: 'POST' });
}
export function getDuplicateScanStatus() {
return request('/api/gallery/scan-duplicates/status');
}
export function getDuplicateGroups(offset = 0, limit = 20) {
const query = buildQuery({ offset, limit });
return request(`/api/gallery/duplicates${query}`);
}
export function cleanDuplicates() {
return request('/api/gallery/duplicates/clean', { method: 'POST' });
}
export function deleteMediaFile(folder, filename) {
return request(`/api/gallery/media/${encodeURIComponent(folder)}/${encodeURIComponent(filename)}`, { method: 'DELETE' });
}
export function startForumScrape(config) {
return request('/api/scrape/forum', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(config),
});
}
export function startCoomerScrape(config) {
return request('/api/scrape/coomer', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(config),
});
}
export function startMediaLinkScrape(config) {
return request('/api/scrape/medialink', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(config),
});
}
export function getScrapeJobs() {
return request('/api/scrape/jobs');
}
export function getScrapeJob(jobId) {
return request(`/api/scrape/jobs/${jobId}`);
}
export function cancelScrapeJob(jobId) {
return request(`/api/scrape/jobs/${jobId}/cancel`, { method: 'POST' });
}
export function detectForumPages(url) {
return request('/api/scrape/forum/detect-pages', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ url }),
});
}