Files
OFApp/server/media-api.js
T
Trey T 236f36aae6 Add app auth, dashboard, scheduler, video management, and new scrapers
- JWT-based app authentication with user roles, folder/route access control
- Dashboard with storage stats, health checks, and recent activity
- Auto-download/scrape scheduler (12h interval) with per-user and per-job configs
- Video upload, tagging, HLS transcoding, and detail pages
- New scrapers: LeakGallery, Mega (megajs), yt-dlp
- FlareSolverr integration for Cloudflare-protected sites
- Gallery: advanced filtering (date, size, search), sort modes, equal-mix shuffle
- Forum sites management with stored cookies/auth
- GridWall/GridCell components for responsive media grid
- Media API with folder-access permissions

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-16 07:48:10 -05:00

67 lines
2.3 KiB
JavaScript

import { Router } from 'express';
import { getMediaFiles, getSetting, getUserFolderAccess } from './db.js';
const router = Router();
const MEDIA_PATH = process.env.MEDIA_PATH || './data/media';
// GET /api/media?users=folder1,folder2&type=video,image
router.get('/api/media', (req, res) => {
const { users, type } = req.query;
if (!users) return res.status(400).json({ error: 'users parameter is required' });
if (!type) return res.status(400).json({ error: 'type parameter is required' });
const folders = users.split(',').map(u => u.trim()).filter(Boolean);
const types = type.split(',').map(t => t.trim().toLowerCase()).filter(Boolean);
if (folders.length === 0) return res.status(400).json({ error: 'at least one user/folder is required' });
const validTypes = ['video', 'image'];
const invalid = types.find(t => !validTypes.includes(t));
if (invalid) return res.status(400).json({ error: `invalid type: ${invalid}. Must be video, image, or both` });
// Enforce folder access for non-admin users
if (req.user && req.user.role !== 'admin') {
const allowed = getUserFolderAccess(req.user.id);
if (allowed.length > 0) {
const denied = folders.filter(f => !allowed.includes(f));
if (denied.length > 0) {
return res.status(403).json({ error: `access denied to folders: ${denied.join(', ')}` });
}
}
}
// If both types requested, query all; otherwise query the single type
const typeFilter = types.length === 2 ? 'all' : types[0];
const { rows } = getMediaFiles({
folders,
type: typeFilter,
offset: 0,
limit: 999999999,
});
const hlsEnabled = getSetting('hls_enabled') === 'true' || process.env.HLS_ENABLED === 'true';
const results = rows.map(r => {
const item = {
folder: r.folder,
path: `${MEDIA_PATH}/${r.folder}/${r.filename}`,
type: r.type,
};
if (r.type === 'video' && hlsEnabled) {
item.hlsUrl = `/api/hls/${encodeURIComponent(r.folder)}/${encodeURIComponent(r.filename)}/master.m3u8`;
} else if (r.type === 'video') {
item.mediaUrl = `/api/gallery/media/${encodeURIComponent(r.folder)}/${encodeURIComponent(r.filename)}`;
}
if (r.type === 'image') {
item.mediaUrl = `/api/gallery/media/${encodeURIComponent(r.folder)}/${encodeURIComponent(r.filename)}`;
}
return item;
});
res.json(results);
});
export default router;