feat: Kino-Feature mit TMDb-Proxy, Favoriten, Admin-Token-Einstellung

This commit is contained in:
2026-06-06 16:08:54 +02:00
parent 97da66616b
commit 9969079bdd
5 changed files with 360 additions and 33 deletions

View File

@@ -0,0 +1,90 @@
const express = require('express');
const router = express.Router();
const db = require('../../db');
const TMDB_BASE = 'https://api.themoviedb.org/3';
function getToken() {
return db.prepare("SELECT value FROM admin_settings WHERE key='tmdb_token'").get()?.value || '';
}
async function tmdb(path, params = {}) {
const token = getToken();
if (!token) throw new Error('Kein TMDb API-Token konfiguriert');
const url = new URL(TMDB_BASE + path);
url.searchParams.set('language', 'de-DE');
url.searchParams.set('region', 'DE');
for (const [k, v] of Object.entries(params)) url.searchParams.set(k, v);
const res = await fetch(url.toString(), {
headers: { Authorization: `Bearer ${token}`, Accept: 'application/json' }
});
if (!res.ok) throw new Error(`TMDb Fehler: ${res.status}`);
return res.json();
}
// GET /api/tools/media/now-playing
router.get('/now-playing', async (req, res) => {
try {
const data = await tmdb('/movie/now_playing');
res.json(data.results.slice(0, 10));
} catch (e) {
res.status(500).json({ error: e.message });
}
});
// GET /api/tools/media/upcoming
router.get('/upcoming', async (req, res) => {
try {
const data = await tmdb('/movie/upcoming');
res.json(data.results.slice(0, 10));
} catch (e) {
res.status(500).json({ error: e.message });
}
});
// GET /api/tools/media/favorites
router.get('/favorites', (req, res) => {
const rows = db.prepare(
'SELECT * FROM movie_favorites WHERE user_id=? ORDER BY added_at DESC'
).all(req.user.id);
res.json(rows);
});
// POST /api/tools/media/favorites
router.post('/favorites', (req, res) => {
const { tmdb_id, title, poster_path, release_date_de } = req.body;
if (!tmdb_id) return res.status(400).json({ error: 'tmdb_id fehlt' });
try {
db.prepare(
'INSERT OR IGNORE INTO movie_favorites (user_id,tmdb_id,title,poster_path,release_date_de) VALUES (?,?,?,?,?)'
).run(req.user.id, tmdb_id, title||'', poster_path||'', release_date_de||'');
res.json({ ok: true });
} catch (e) {
res.status(500).json({ error: e.message });
}
});
// DELETE /api/tools/media/favorites/:tmdb_id
router.delete('/favorites/:tmdb_id', (req, res) => {
db.prepare('DELETE FROM movie_favorites WHERE user_id=? AND tmdb_id=?')
.run(req.user.id, Number(req.params.tmdb_id));
res.json({ ok: true });
});
// GET /api/tools/media/settings (admin only gibt Token maskiert zurück)
router.get('/settings', (req, res) => {
if (!req.user.is_admin) return res.status(403).json({ error: 'Nur Admin' });
const token = getToken();
res.json({ configured: !!token });
});
// PUT /api/tools/media/settings (admin only)
router.put('/settings', (req, res) => {
if (!req.user.is_admin) return res.status(403).json({ error: 'Nur Admin' });
const { tmdb_token } = req.body;
if (typeof tmdb_token !== 'string') return res.status(400).json({ error: 'tmdb_token fehlt' });
db.prepare('INSERT OR REPLACE INTO admin_settings (key,value) VALUES (?,?)').run('tmdb_token', tmdb_token.trim());
res.json({ ok: true });
});
module.exports = router;