chore: update Angular build cache and TypeScript definitions
This commit is contained in:
+189
-14
@@ -148,11 +148,40 @@ export function insertSearchHistory({ userId, query, filters }) {
|
||||
return { id, created_at };
|
||||
}
|
||||
|
||||
export function listSearchHistory({ userId, limit = 50, before }) {
|
||||
const rows = before
|
||||
? db.prepare(`SELECT * FROM search_history WHERE user_id = ? AND created_at < ? ORDER BY created_at DESC LIMIT ?`).all(userId, before, limit)
|
||||
: db.prepare(`SELECT * FROM search_history WHERE user_id = ? ORDER BY created_at DESC LIMIT ?`).all(userId, limit);
|
||||
return rows;
|
||||
export function listSearchHistory({ userId, limit = 50, before, q }) {
|
||||
const hasBefore = Boolean(before);
|
||||
const hasQ = typeof q === 'string' && q.trim().length > 0;
|
||||
const like = `%${(q || '').trim()}%`;
|
||||
if (hasBefore && hasQ) {
|
||||
return db.prepare(`
|
||||
SELECT * FROM search_history
|
||||
WHERE user_id = ? AND created_at < ? AND (query LIKE ? OR COALESCE(filters_json,'') LIKE ?)
|
||||
ORDER BY created_at DESC
|
||||
LIMIT ?
|
||||
`).all(userId, before, like, like, limit);
|
||||
}
|
||||
if (hasBefore) {
|
||||
return db.prepare(`
|
||||
SELECT * FROM search_history
|
||||
WHERE user_id = ? AND created_at < ?
|
||||
ORDER BY created_at DESC
|
||||
LIMIT ?
|
||||
`).all(userId, before, limit);
|
||||
}
|
||||
if (hasQ) {
|
||||
return db.prepare(`
|
||||
SELECT * FROM search_history
|
||||
WHERE user_id = ? AND (query LIKE ? OR COALESCE(filters_json,'') LIKE ?)
|
||||
ORDER BY created_at DESC
|
||||
LIMIT ?
|
||||
`).all(userId, like, like, limit);
|
||||
}
|
||||
return db.prepare(`
|
||||
SELECT * FROM search_history
|
||||
WHERE user_id = ?
|
||||
ORDER BY created_at DESC
|
||||
LIMIT ?
|
||||
`).all(userId, limit);
|
||||
}
|
||||
|
||||
export function deleteSearchHistoryById(userId, id) {
|
||||
@@ -164,19 +193,20 @@ export function deleteAllSearchHistory(userId) {
|
||||
}
|
||||
|
||||
// -------------------- Watch History --------------------
|
||||
export function upsertWatchHistory({ userId, provider, videoId, title, watchedAt, progressSeconds = 0, durationSeconds = 0, lastPositionSeconds = 0 }) {
|
||||
export function upsertWatchHistory({ userId, provider, videoId, title, thumbnail, watchedAt, progressSeconds = 0, durationSeconds = 0, lastPositionSeconds = 0 }) {
|
||||
const now = nowIso();
|
||||
const watched_at = watchedAt || now;
|
||||
// Insert or update on unique (user_id, provider, video_id)
|
||||
db.prepare(`INSERT INTO watch_history (id, user_id, provider, video_id, title, watched_at, progress_seconds, duration_seconds, last_position_seconds, last_watched_at)
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
||||
db.prepare(`INSERT INTO watch_history (id, user_id, provider, video_id, title, thumbnail, watched_at, progress_seconds, duration_seconds, last_position_seconds, last_watched_at)
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
||||
ON CONFLICT(user_id, provider, video_id) DO UPDATE SET
|
||||
title=COALESCE(excluded.title, title),
|
||||
thumbnail=COALESCE(excluded.thumbnail, watch_history.thumbnail),
|
||||
progress_seconds=MAX(excluded.progress_seconds, watch_history.progress_seconds),
|
||||
duration_seconds=MAX(excluded.duration_seconds, watch_history.duration_seconds),
|
||||
last_position_seconds=excluded.last_position_seconds,
|
||||
last_watched_at=excluded.last_watched_at`).run(
|
||||
cryptoRandomId(), userId, provider, videoId, title || null, watched_at, progressSeconds, durationSeconds, lastPositionSeconds, now
|
||||
cryptoRandomId(), userId, provider, videoId, title || null, thumbnail || null, watched_at, progressSeconds, durationSeconds, lastPositionSeconds, now
|
||||
);
|
||||
// Return the row id
|
||||
const row = db.prepare(`SELECT * FROM watch_history WHERE user_id = ? AND provider = ? AND video_id = ?`).get(userId, provider, videoId);
|
||||
@@ -194,11 +224,41 @@ export function updateWatchHistoryById(id, { progressSeconds, lastPositionSecond
|
||||
return db.prepare(`SELECT * FROM watch_history WHERE id = ?`).get(id);
|
||||
}
|
||||
|
||||
export function listWatchHistory({ userId, limit = 50, before }) {
|
||||
const rows = before
|
||||
? db.prepare(`SELECT * FROM watch_history WHERE user_id = ? AND watched_at < ? ORDER BY watched_at DESC LIMIT ?`).all(userId, before, limit)
|
||||
: db.prepare(`SELECT * FROM watch_history WHERE user_id = ? ORDER BY watched_at DESC LIMIT ?`).all(userId, limit);
|
||||
return rows;
|
||||
export function listWatchHistory({ userId, limit = 50, before, q }) {
|
||||
const hasBefore = Boolean(before);
|
||||
const hasQ = typeof q === 'string' && q.trim().length > 0;
|
||||
const like = `%${(q || '').trim()}%`;
|
||||
if (hasBefore && hasQ) {
|
||||
return db.prepare(`
|
||||
SELECT * FROM watch_history
|
||||
WHERE user_id = ? AND watched_at < ?
|
||||
AND (COALESCE(title,'') LIKE ? OR provider LIKE ? OR video_id LIKE ?)
|
||||
ORDER BY watched_at DESC
|
||||
LIMIT ?
|
||||
`).all(userId, before, like, like, like, limit);
|
||||
}
|
||||
if (hasBefore) {
|
||||
return db.prepare(`
|
||||
SELECT * FROM watch_history
|
||||
WHERE user_id = ? AND watched_at < ?
|
||||
ORDER BY watched_at DESC
|
||||
LIMIT ?
|
||||
`).all(userId, before, limit);
|
||||
}
|
||||
if (hasQ) {
|
||||
return db.prepare(`
|
||||
SELECT * FROM watch_history
|
||||
WHERE user_id = ? AND (COALESCE(title,'') LIKE ? OR provider LIKE ? OR video_id LIKE ?)
|
||||
ORDER BY watched_at DESC
|
||||
LIMIT ?
|
||||
`).all(userId, like, like, like, limit);
|
||||
}
|
||||
return db.prepare(`
|
||||
SELECT * FROM watch_history
|
||||
WHERE user_id = ?
|
||||
ORDER BY watched_at DESC
|
||||
LIMIT ?
|
||||
`).all(userId, limit);
|
||||
}
|
||||
|
||||
export function deleteWatchHistoryById(userId, id) {
|
||||
@@ -208,3 +268,118 @@ export function deleteWatchHistoryById(userId, id) {
|
||||
export function deleteAllWatchHistory(userId) {
|
||||
db.prepare(`DELETE FROM watch_history WHERE user_id = ?`).run(userId);
|
||||
}
|
||||
|
||||
// -------------------- Likes (via tags/video_tags) --------------------
|
||||
function ensureTag(userId, name) {
|
||||
const tag = db.prepare(`SELECT id FROM tags WHERE user_id = ? AND name = ?`).get(userId, name);
|
||||
if (tag && tag.id) return tag.id;
|
||||
const id = cryptoRandomId();
|
||||
db.prepare(`INSERT INTO tags (id, user_id, name, created_at) VALUES (?, ?, ?, ?)`)
|
||||
.run(id, userId, name, nowIso());
|
||||
return id;
|
||||
}
|
||||
|
||||
export function likeVideo({ userId, provider, videoId }) {
|
||||
const tagId = ensureTag(userId, 'like');
|
||||
// Upsert-like behavior; ignore if exists
|
||||
db.prepare(`INSERT OR IGNORE INTO video_tags (user_id, provider, video_id, tag_id) VALUES (?, ?, ?, ?)`)
|
||||
.run(userId, provider, videoId, tagId);
|
||||
return { provider, video_id: videoId };
|
||||
}
|
||||
|
||||
export function unlikeVideo({ userId, provider, videoId }) {
|
||||
const tag = db.prepare(`SELECT id FROM tags WHERE user_id = ? AND name = ?`).get(userId, 'like');
|
||||
if (!tag) return { removed: false };
|
||||
const info = db.prepare(`DELETE FROM video_tags WHERE user_id = ? AND provider = ? AND video_id = ? AND tag_id = ?`)
|
||||
.run(userId, provider, videoId, tag.id);
|
||||
return { removed: (info.changes || 0) > 0 };
|
||||
}
|
||||
|
||||
export function isVideoLiked({ userId, provider, videoId }) {
|
||||
const tag = db.prepare(`SELECT id FROM tags WHERE user_id = ? AND name = ?`).get(userId, 'like');
|
||||
if (!tag) return false;
|
||||
const row = db.prepare(`SELECT 1 FROM video_tags WHERE user_id = ? AND provider = ? AND video_id = ? AND tag_id = ?`)
|
||||
.get(userId, provider, videoId, tag.id);
|
||||
return Boolean(row);
|
||||
}
|
||||
|
||||
export function listLikedVideos({ userId, limit = 100, q }) {
|
||||
try {
|
||||
console.log(`[listLikedVideos] Récupération des vidéos aimées pour l'utilisateur ${userId}`);
|
||||
|
||||
// Vérifier que la table tags existe
|
||||
const tableExists = db.prepare(`
|
||||
SELECT name FROM sqlite_master
|
||||
WHERE type='table' AND name='tags'
|
||||
`).get();
|
||||
|
||||
if (!tableExists) {
|
||||
console.error('[listLikedVideos] La table tags n\'existe pas');
|
||||
return [];
|
||||
}
|
||||
|
||||
// Récupérer le tag 'like' de l'utilisateur
|
||||
const tag = db.prepare(`
|
||||
SELECT id FROM tags
|
||||
WHERE user_id = ? AND name = ?
|
||||
`).get(userId, 'like');
|
||||
|
||||
if (!tag) {
|
||||
console.log(`[listLikedVideos] Aucun tag 'like' trouvé pour l'utilisateur ${userId}`);
|
||||
return [];
|
||||
}
|
||||
|
||||
console.log(`[listLikedVideos] Tag ID: ${tag.id}`);
|
||||
|
||||
// Vérifier que la table video_tags existe
|
||||
const videoTagsExists = db.prepare(`
|
||||
SELECT name FROM sqlite_master
|
||||
WHERE type='table' AND name='video_tags'
|
||||
`).get();
|
||||
|
||||
if (!videoTagsExists) {
|
||||
console.error('[listLikedVideos] La table video_tags n\'existe pas');
|
||||
return [];
|
||||
}
|
||||
|
||||
// Récupérer les vidéos aimées avec les métadonnées de l'historique
|
||||
// Note: La colonne thumbnail n'existe pas dans la table watch_history
|
||||
const hasQ = typeof q === 'string' && q.trim().length > 0;
|
||||
const like = `%${(q || '').trim()}%`;
|
||||
const base = `
|
||||
SELECT
|
||||
vt.provider,
|
||||
vt.video_id,
|
||||
COALESCE(wh.title, '') AS title,
|
||||
COALESCE(wh.thumbnail, '') AS thumbnail,
|
||||
wh.last_watched_at AS last_watched_at
|
||||
FROM video_tags vt
|
||||
LEFT JOIN watch_history wh
|
||||
ON wh.user_id = vt.user_id
|
||||
AND wh.provider = vt.provider
|
||||
AND wh.video_id = vt.video_id
|
||||
WHERE vt.user_id = ? AND vt.tag_id = ?
|
||||
`;
|
||||
const orderLimit = `
|
||||
ORDER BY COALESCE(wh.last_watched_at, wh.watched_at) DESC
|
||||
LIMIT ?
|
||||
`;
|
||||
const query = hasQ
|
||||
? `${base} AND (COALESCE(wh.title,'') LIKE ? OR vt.provider LIKE ? OR vt.video_id LIKE ?)
|
||||
${orderLimit}`
|
||||
: `${base} ${orderLimit}`;
|
||||
|
||||
console.log('[listLikedVideos] Exécution de la requête:', query.replace(/\s+/g, ' ').trim());
|
||||
|
||||
const rows = hasQ
|
||||
? db.prepare(query).all(userId, tag.id, like, like, like, limit)
|
||||
: db.prepare(query).all(userId, tag.id, limit);
|
||||
console.log(`[listLikedVideos] ${rows.length} vidéos trouvées`);
|
||||
|
||||
return rows;
|
||||
} catch (error) {
|
||||
console.error('[listLikedVideos] Erreur:', error.message);
|
||||
console.error(error.stack);
|
||||
throw error; // Renvoyer l'erreur pour qu'elle soit gérée par le routeur
|
||||
}
|
||||
}
|
||||
|
||||
+65
-9
@@ -37,6 +37,10 @@ import {
|
||||
updateWatchHistoryById,
|
||||
deleteWatchHistoryById,
|
||||
deleteAllWatchHistory,
|
||||
likeVideo,
|
||||
unlikeVideo,
|
||||
listLikedVideos,
|
||||
isVideoLiked,
|
||||
} from './db.mjs';
|
||||
|
||||
const app = express();
|
||||
@@ -977,7 +981,8 @@ r.post('/user/history/search', authMiddleware, (req, res) => {
|
||||
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 rows = listSearchHistory({ userId: req.user.id, limit, before });
|
||||
const q = typeof req.query.q === 'string' ? req.query.q : undefined;
|
||||
const rows = listSearchHistory({ userId: req.user.id, limit, before, q });
|
||||
return res.json(rows);
|
||||
});
|
||||
|
||||
@@ -992,21 +997,46 @@ r.delete('/user/history/search', authMiddleware, (req, res) => {
|
||||
return res.status(204).end();
|
||||
});
|
||||
|
||||
// Delete a single search history item
|
||||
r.delete('/user/history/search/:id', authMiddleware, (req, res) => {
|
||||
const { id } = req.params;
|
||||
if (!id) return res.status(400).json({ error: 'id is required' });
|
||||
try {
|
||||
deleteSearchHistoryById(req.user.id, id);
|
||||
return res.status(204).end();
|
||||
} catch (e) {
|
||||
return res.status(500).json({ error: 'delete_failed', details: String(e?.message || e) });
|
||||
}
|
||||
});
|
||||
|
||||
// --- History: Watch ---
|
||||
r.post('/user/history/watch', authMiddleware, (req, res) => {
|
||||
const { provider, videoId, title, watchedAt, progressSeconds, durationSeconds, lastPositionSeconds } = req.body || {};
|
||||
const { provider, videoId, title, thumbnail, watchedAt, progressSeconds, durationSeconds, lastPositionSeconds } = req.body || {};
|
||||
if (!provider || !videoId) return res.status(400).json({ error: 'provider and videoId are required' });
|
||||
const row = upsertWatchHistory({ userId: req.user.id, provider, videoId, title, watchedAt, progressSeconds, durationSeconds, lastPositionSeconds });
|
||||
const row = upsertWatchHistory({ userId: req.user.id, provider, videoId, title, thumbnail, watchedAt, progressSeconds, durationSeconds, lastPositionSeconds });
|
||||
return res.status(201).json(row);
|
||||
});
|
||||
|
||||
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 rows = listWatchHistory({ userId: req.user.id, limit, before });
|
||||
const q = typeof req.query.q === 'string' ? req.query.q : undefined;
|
||||
const rows = listWatchHistory({ userId: req.user.id, limit, before, q });
|
||||
return res.json(rows);
|
||||
});
|
||||
|
||||
// Delete a single watch history item
|
||||
r.delete('/user/history/watch/:id', authMiddleware, (req, res) => {
|
||||
const { id } = req.params;
|
||||
if (!id) return res.status(400).json({ error: 'id is required' });
|
||||
try {
|
||||
deleteWatchHistoryById(req.user.id, id);
|
||||
return res.status(204).end();
|
||||
} catch (e) {
|
||||
return res.status(500).json({ error: 'delete_failed', details: String(e?.message || e) });
|
||||
}
|
||||
});
|
||||
|
||||
r.patch('/user/history/watch/:id', authMiddleware, (req, res) => {
|
||||
const { progressSeconds, lastPositionSeconds } = req.body || {};
|
||||
const row = updateWatchHistoryById(req.params.id, { progressSeconds, lastPositionSeconds });
|
||||
@@ -1014,17 +1044,43 @@ r.patch('/user/history/watch/:id', authMiddleware, (req, res) => {
|
||||
return res.json(row);
|
||||
});
|
||||
|
||||
r.delete('/user/history/watch/:id', authMiddleware, (req, res) => {
|
||||
deleteWatchHistoryById(req.user.id, req.params.id);
|
||||
return res.status(204).end();
|
||||
});
|
||||
|
||||
r.delete('/user/history/watch', authMiddleware, (req, res) => {
|
||||
if (String(req.query.all || '') !== '1') return res.status(400).json({ error: 'set all=1' });
|
||||
deleteAllWatchHistory(req.user.id);
|
||||
return res.status(204).end();
|
||||
});
|
||||
|
||||
// --- Likes ---
|
||||
r.get('/user/likes', authMiddleware, (req, res) => {
|
||||
const limit = Math.min(500, Number(req.query.limit || 100));
|
||||
const q = typeof req.query.q === 'string' ? req.query.q : undefined;
|
||||
const rows = listLikedVideos({ userId: req.user.id, limit, q });
|
||||
return res.json(rows);
|
||||
});
|
||||
|
||||
r.post('/user/likes', authMiddleware, (req, res) => {
|
||||
const { provider, videoId } = req.body || {};
|
||||
if (!provider || !videoId) return res.status(400).json({ error: 'provider and videoId are required' });
|
||||
const row = likeVideo({ userId: req.user.id, provider, videoId });
|
||||
return res.status(201).json(row);
|
||||
});
|
||||
|
||||
r.delete('/user/likes', authMiddleware, (req, res) => {
|
||||
const provider = req.query.provider ? String(req.query.provider) : '';
|
||||
const videoId = req.query.videoId ? String(req.query.videoId) : '';
|
||||
if (!provider || !videoId) return res.status(400).json({ error: 'provider and videoId are required' });
|
||||
const result = unlikeVideo({ userId: req.user.id, provider, videoId });
|
||||
return res.json(result);
|
||||
});
|
||||
|
||||
r.get('/user/likes/status', authMiddleware, (req, res) => {
|
||||
const provider = req.query.provider ? String(req.query.provider) : '';
|
||||
const videoId = req.query.videoId ? String(req.query.videoId) : '';
|
||||
if (!provider || !videoId) return res.status(400).json({ error: 'provider and videoId are required' });
|
||||
const liked = isVideoLiked({ userId: req.user.id, provider, videoId });
|
||||
return res.json({ liked });
|
||||
});
|
||||
|
||||
app.use('/api', r);
|
||||
// Mount dedicated Rumble router (browse, search, video)
|
||||
app.use('/api/rumble', rumbleRouter);
|
||||
|
||||
Reference in New Issue
Block a user