feat: Kino-Feature mit TMDb-Proxy, Favoriten, Admin-Token-Einstellung
This commit is contained in:
@@ -528,4 +528,25 @@ if (!db.prepare("SELECT name FROM sqlite_master WHERE type='table' AND name='cal
|
||||
db.exec('ALTER TABLE upload_shares ADD COLUMN short_url TEXT DEFAULT NULL');
|
||||
}
|
||||
|
||||
|
||||
// ── Movie-Favoriten ───────────────────────────────────────────────────────────
|
||||
if (!db.prepare("SELECT name FROM sqlite_master WHERE type='table' AND name='movie_favorites'").get()) {
|
||||
db.exec(`
|
||||
CREATE TABLE movie_favorites (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
user_id INTEGER NOT NULL REFERENCES users(id) ON DELETE CASCADE,
|
||||
tmdb_id INTEGER NOT NULL,
|
||||
title TEXT NOT NULL DEFAULT '',
|
||||
poster_path TEXT NOT NULL DEFAULT '',
|
||||
release_date_de TEXT NOT NULL DEFAULT '',
|
||||
added_at DATETIME DEFAULT NULL,
|
||||
UNIQUE(user_id, tmdb_id)
|
||||
)
|
||||
`);
|
||||
}
|
||||
|
||||
// TMDb-Token Default
|
||||
if (!db.prepare("SELECT value FROM admin_settings WHERE key='tmdb_token'").get())
|
||||
db.prepare("INSERT INTO admin_settings (key,value) VALUES ('tmdb_token','')").run();
|
||||
|
||||
module.exports = db;
|
||||
|
||||
@@ -69,6 +69,7 @@ fs.readdirSync(toolsDir, { withFileTypes: true })
|
||||
app.use('/api/tools/snippets', require('./tools/snippets/routes'));
|
||||
app.use('/api/tools/qrcodes', require('./tools/qrcodes/routes'));
|
||||
app.use('/api/upload-shares', require('./tools/dateien/upload-share'));
|
||||
app.use('/api/tools/media', require('./tools/media/routes'));
|
||||
app.use('/api/search', require('./routes/search'));
|
||||
|
||||
// Öffentliche Upload-Seite: React-App ausliefern – App erkennt /u/ Pfad selbst
|
||||
|
||||
90
backend/src/tools/media/routes.js
Normal file
90
backend/src/tools/media/routes.js
Normal 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;
|
||||
Reference in New Issue
Block a user