feat(history): refonte page historique en tabs + transcripts conserves et rejoues sans regeneration
This commit is contained in:
+75
-2
@@ -83,6 +83,11 @@ import {
|
||||
sumCompletedDownloadBytes,
|
||||
SUPPORTED_DOWNLOAD_LANGUAGES as SUPPORTED_DL_LANGS,
|
||||
DEFAULT_DOWNLOAD_LANGUAGES as DEFAULT_DL_LANGS,
|
||||
upsertTranscriptHistory,
|
||||
getTranscriptHistoryItem,
|
||||
listTranscriptHistory,
|
||||
deleteTranscriptHistoryById,
|
||||
deleteAllTranscriptHistory,
|
||||
} from './db.mjs';
|
||||
import { getChannelAdapter, setTwitchTokenProvider } from './providers/channel-registry.mjs';
|
||||
import { fetchChannelContent } from './providers/channel-content.mjs';
|
||||
@@ -1976,7 +1981,8 @@ r.get('/user/history/search', authMiddleware, (req, res) => {
|
||||
const limit = Math.min(200, Number(req.query.limit || 50));
|
||||
const before = req.query.before ? String(req.query.before) : undefined;
|
||||
const q = typeof req.query.q === 'string' ? req.query.q : undefined;
|
||||
const rows = listSearchHistory({ userId: req.user.id, limit, before, q });
|
||||
const provider = typeof req.query.provider === 'string' ? req.query.provider : undefined;
|
||||
const rows = listSearchHistory({ userId: req.user.id, limit, before, q, provider });
|
||||
return res.json(rows);
|
||||
});
|
||||
|
||||
@@ -2003,7 +2009,8 @@ r.get('/user/history/watch', authMiddleware, (req, res) => {
|
||||
const limit = Math.min(200, Number(req.query.limit || 50));
|
||||
const before = req.query.before ? String(req.query.before) : undefined;
|
||||
const q = typeof req.query.q === 'string' ? req.query.q : undefined;
|
||||
const rows = listWatchHistory({ userId: req.user.id, limit, before, q });
|
||||
const provider = typeof req.query.provider === 'string' ? req.query.provider : undefined;
|
||||
const rows = listWatchHistory({ userId: req.user.id, limit, before, q, provider });
|
||||
return res.json(rows);
|
||||
});
|
||||
|
||||
@@ -2032,6 +2039,72 @@ r.delete('/user/history/watch', authMiddleware, (req, res) => {
|
||||
return res.status(204).end();
|
||||
});
|
||||
|
||||
// --- History: Transcripts (conservés, rejoués sans régénération) ---
|
||||
r.post('/user/history/transcripts', authMiddleware, (req, res) => {
|
||||
const { provider, videoId, title, thumbnail, lang, languages, lines } = req.body || {};
|
||||
if (!provider || !videoId || !lang) {
|
||||
return res.status(400).json({ error: 'provider, videoId and lang are required' });
|
||||
}
|
||||
if (!Array.isArray(lines) || !lines.length) {
|
||||
return res.status(400).json({ error: 'lines required' });
|
||||
}
|
||||
try {
|
||||
const row = upsertTranscriptHistory({
|
||||
userId: req.user.id, provider, videoId, title, thumbnail, lang, languages, lines,
|
||||
});
|
||||
if (!row) return res.status(400).json({ error: 'invalid_transcript' });
|
||||
return res.status(201).json(row);
|
||||
} catch (e) {
|
||||
return res.status(500).json({ error: 'save_failed', details: String(e?.message || e) });
|
||||
}
|
||||
});
|
||||
|
||||
r.get('/user/history/transcripts', authMiddleware, (req, res) => {
|
||||
const limit = Math.min(200, Number(req.query.limit || 50));
|
||||
const before = req.query.before ? String(req.query.before) : undefined;
|
||||
const q = typeof req.query.q === 'string' ? req.query.q : undefined;
|
||||
const provider = typeof req.query.provider === 'string' ? req.query.provider : undefined;
|
||||
const lang = typeof req.query.lang === 'string' ? req.query.lang : undefined;
|
||||
try {
|
||||
const rows = listTranscriptHistory({ userId: req.user.id, limit, before, q, provider, lang });
|
||||
return res.json(rows);
|
||||
} catch (e) {
|
||||
return res.status(500).json({ error: 'list_failed', details: String(e?.message || e) });
|
||||
}
|
||||
});
|
||||
|
||||
r.get('/user/history/transcripts/:provider/:videoId', authMiddleware, (req, res) => {
|
||||
const { provider, videoId } = req.params;
|
||||
const lang = typeof req.query.lang === 'string' ? req.query.lang : undefined;
|
||||
try {
|
||||
const row = getTranscriptHistoryItem({ userId: req.user.id, provider, videoId, lang });
|
||||
if (!row) return res.status(404).json({ available: false, error: 'not_found' });
|
||||
return res.json({ available: true, fromHistory: true, ...row });
|
||||
} catch (e) {
|
||||
return res.status(500).json({ available: false, error: 'lookup_failed' });
|
||||
}
|
||||
});
|
||||
|
||||
r.delete('/user/history/transcripts/:id', authMiddleware, (req, res) => {
|
||||
try {
|
||||
deleteTranscriptHistoryById(req.user.id, req.params.id);
|
||||
return res.status(204).end();
|
||||
} catch (e) {
|
||||
return res.status(500).json({ error: 'delete_failed', details: String(e?.message || e) });
|
||||
}
|
||||
});
|
||||
|
||||
r.delete('/user/history/transcripts', authMiddleware, (req, res) => {
|
||||
if (String(req.query.all || '') !== '1') return res.status(400).json({ error: 'set all=1' });
|
||||
const provider = typeof req.query.provider === 'string' && req.query.provider ? String(req.query.provider) : undefined;
|
||||
try {
|
||||
deleteAllTranscriptHistory(req.user.id, provider);
|
||||
return res.status(204).end();
|
||||
} catch (e) {
|
||||
return res.status(500).json({ error: 'delete_failed', details: String(e?.message || e) });
|
||||
}
|
||||
});
|
||||
|
||||
// --- Likes ---
|
||||
// NOTE : cookie-aware (et non Bearer seul) : le front s'authentifie via
|
||||
// cookies httpOnly + Bearer en mémoire (perdu au F5) ; exiger le Bearer
|
||||
|
||||
Reference in New Issue
Block a user