feat: Media Streaming - Admin Cache-Clear Button pro Anbieter
This commit is contained in:
@@ -34,6 +34,9 @@ function cacheSet(key, value, ttlMs = 12 * 60 * 60 * 1000) {
|
|||||||
db.prepare('INSERT OR REPLACE INTO xrel_cache (cache_key,value,expires_at) VALUES (?,?,?)')
|
db.prepare('INSERT OR REPLACE INTO xrel_cache (cache_key,value,expires_at) VALUES (?,?,?)')
|
||||||
.run(key, JSON.stringify(value), Date.now() + ttlMs);
|
.run(key, JSON.stringify(value), Date.now() + ttlMs);
|
||||||
}
|
}
|
||||||
|
function cacheDelete(key) {
|
||||||
|
db.prepare('DELETE FROM xrel_cache WHERE cache_key=?').run(key);
|
||||||
|
}
|
||||||
|
|
||||||
// ── TMDb ──────────────────────────────────────────────────────────────────────
|
// ── TMDb ──────────────────────────────────────────────────────────────────────
|
||||||
function getToken() {
|
function getToken() {
|
||||||
@@ -666,6 +669,15 @@ router.get('/streaming/:providerId/new', authenticate, async (req, res) => {
|
|||||||
} catch(e) { res.status(500).json({ error: e.message }); }
|
} catch(e) { res.status(500).json({ error: e.message }); }
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// ── Cache leeren für einen Provider (Admin) ──────────────────────────────────
|
||||||
|
router.post('/streaming/:providerId/clear-cache', authenticate, requireAdmin, (req, res) => {
|
||||||
|
const provider = STREAMING_PROVIDERS.find(p => p.id === req.params.providerId);
|
||||||
|
if (!provider) return res.status(404).json({ error: 'Unbekannter Anbieter' });
|
||||||
|
cacheDelete(`streaming-top10:${provider.id}`);
|
||||||
|
cacheDelete(`streaming-new:${provider.id}`);
|
||||||
|
res.json({ ok: true, provider: provider.id });
|
||||||
|
});
|
||||||
|
|
||||||
router.get('/streaming/:providerId', authenticate, async (req, res) => {
|
router.get('/streaming/:providerId', authenticate, async (req, res) => {
|
||||||
try {
|
try {
|
||||||
const provider = STREAMING_PROVIDERS.find(p => p.id === req.params.providerId);
|
const provider = STREAMING_PROVIDERS.find(p => p.id === req.params.providerId);
|
||||||
@@ -678,7 +690,7 @@ router.get('/streaming/:providerId', authenticate, async (req, res) => {
|
|||||||
// Nur Titel der letzten 2 Jahre (aktuelles Jahr - 1)
|
// Nur Titel der letzten 2 Jahre (aktuelles Jahr - 1)
|
||||||
// Für kleine Kataloge (HBO Max, WOW, RTL+) kein Jahresfilter — TMDb hat dort
|
// Für kleine Kataloge (HBO Max, WOW, RTL+) kein Jahresfilter — TMDb hat dort
|
||||||
// zu wenige Titel mit korrekt erfasstem DE-Datum seit letztem Jahr
|
// zu wenige Titel mit korrekt erfasstem DE-Datum seit letztem Jahr
|
||||||
const SMALL_CATALOGS = new Set(['max', 'wow', 'rtlplus']);
|
const SMALL_CATALOGS = new Set(['max']);
|
||||||
const minYear = new Date().getFullYear() - 1;
|
const minYear = new Date().getFullYear() - 1;
|
||||||
const minDate = SMALL_CATALOGS.has(provider.id) ? null : `${minYear}-01-01`;
|
const minDate = SMALL_CATALOGS.has(provider.id) ? null : `${minYear}-01-01`;
|
||||||
|
|
||||||
|
|||||||
@@ -459,6 +459,28 @@ function StreamingGrid({ onOpenModal, xrelIds, onXrelLoaded, favIds: favIdsProp,
|
|||||||
const [data, setData] = useState(null);
|
const [data, setData] = useState(null);
|
||||||
const [loading, setLoading] = useState(true);
|
const [loading, setLoading] = useState(true);
|
||||||
const [error, setError] = useState('');
|
const [error, setError] = useState('');
|
||||||
|
const [clearing, setClearing] = useState(false);
|
||||||
|
|
||||||
|
const isAdmin = (() => {
|
||||||
|
try { return JSON.parse(atob(localStorage.getItem('sk_token').split('.')[1])).role === 'admin'; }
|
||||||
|
catch { return false; }
|
||||||
|
})();
|
||||||
|
|
||||||
|
const clearCache = async () => {
|
||||||
|
if (!activeProvider || clearing) return;
|
||||||
|
setClearing(true);
|
||||||
|
try {
|
||||||
|
await api(`/tools/media/streaming/${activeProvider}/clear-cache`, { method:'POST', body:{} });
|
||||||
|
// Neu laden
|
||||||
|
setData(null); setLoading(true);
|
||||||
|
const path = subTab === 'new'
|
||||||
|
? `/tools/media/streaming/${activeProvider}/new`
|
||||||
|
: `/tools/media/streaming/${activeProvider}`;
|
||||||
|
const d = await api(path);
|
||||||
|
setData(d);
|
||||||
|
} catch(e) { setError(e.message); }
|
||||||
|
finally { setClearing(false); setLoading(false); }
|
||||||
|
};
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
api('/tools/media/streaming/providers')
|
api('/tools/media/streaming/providers')
|
||||||
@@ -498,6 +520,15 @@ function StreamingGrid({ onOpenModal, xrelIds, onXrelLoaded, favIds: favIdsProp,
|
|||||||
}}>🆕 Neuerscheinungen</button>
|
}}>🆕 Neuerscheinungen</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{isAdmin && activeProvider && (
|
||||||
|
<button onClick={clearCache} disabled={clearing || loading}
|
||||||
|
style={{background:'rgba(255,255,255,0.05)',border:'1px solid rgba(255,255,255,0.1)',
|
||||||
|
borderRadius:7,padding:'4px 10px',color:'rgba(255,255,255,0.4)',fontFamily:'monospace',
|
||||||
|
fontSize:10,cursor:'pointer',marginBottom:10,display:'block'}}>
|
||||||
|
{clearing ? '⏳ Leere…' : '🗑 Cache leeren'}
|
||||||
|
</button>
|
||||||
|
)}
|
||||||
|
|
||||||
{subTab === 'top10' ? (
|
{subTab === 'top10' ? (
|
||||||
<div style={{color:'rgba(255,255,255,0.35)',fontFamily:'monospace',fontSize:11,marginBottom:12,lineHeight:1.6}}>
|
<div style={{color:'rgba(255,255,255,0.35)',fontFamily:'monospace',fontSize:11,marginBottom:12,lineHeight:1.6}}>
|
||||||
Top 10 Filme & Serien pro Anbieter (DE) · nur Titel ab {new Date().getFullYear()-1} · sortiert nach Popularität (Näherung, keine offiziellen Anbieter-Daten)<br/>
|
Top 10 Filme & Serien pro Anbieter (DE) · nur Titel ab {new Date().getFullYear()-1} · sortiert nach Popularität (Näherung, keine offiziellen Anbieter-Daten)<br/>
|
||||||
|
|||||||
Reference in New Issue
Block a user