Extend video catalog source metadata
This commit is contained in:
@@ -2,7 +2,7 @@
|
||||
// repeat query — from any device, or after a restart — never waits on YouTube.
|
||||
// Capped by row count (default 250,000 queries) AND bytes (default 2 GiB), LRU.
|
||||
import { db } from './db.js';
|
||||
import { ingest, withLocalThumbnails, trimCatalog } from './video-catalog.js';
|
||||
import { ingest, withLocalThumbnails, trimCatalog, foldSearchText } from './video-catalog.js';
|
||||
|
||||
export const MAX_ROWS = Number(process.env.SEARCH_CACHE_MAX_ROWS) || 250_000;
|
||||
export const MAX_BYTES = Number(process.env.SEARCH_CACHE_MAX_BYTES) || 2 * 1024 ** 3;
|
||||
@@ -70,15 +70,28 @@ export async function rememberVideos(cards) {
|
||||
|
||||
// Cards whose title/channel contain EVERY word of the query, most-seen first.
|
||||
export async function searchVideos(q, limit = 60) {
|
||||
const words = String(q).toLowerCase().split(/\s+/).filter(Boolean).slice(0, 8);
|
||||
const folded = foldSearchText(q).trim();
|
||||
const words = folded.split(/\s+/).filter(Boolean).slice(0, 8);
|
||||
if (!words.length) return [];
|
||||
const like = (w) => '%' + w.replace(/[\\%_]/g, '\\$&') + '%';
|
||||
const r = await db.execute({
|
||||
sql: `SELECT card FROM video_meta WHERE ${words.map(() => "hay LIKE ? ESCAPE '\\'").join(' AND ')}
|
||||
sql: `SELECT card,seen FROM video_meta WHERE ${words.map(() => "hay LIKE ? ESCAPE '\\'").join(' AND ')}
|
||||
ORDER BY seen DESC, updated_at DESC LIMIT ?`,
|
||||
args: [...words.map(like), limit],
|
||||
args: [...words.map(like), Math.max(limit * 10, 200)],
|
||||
});
|
||||
return withLocalThumbnails(r.rows.map((x) => { try { return JSON.parse(x.card); } catch { return null; } }).filter(Boolean));
|
||||
const titleHasAll = title => words.every(word => foldSearchText(title).includes(word));
|
||||
const phrase = folded;
|
||||
const ranked = r.rows.map(row => {
|
||||
try {
|
||||
const card = JSON.parse(row.card);
|
||||
const title = foldSearchText(card.title);
|
||||
const channel = foldSearchText(card.channel);
|
||||
const score = (title === phrase || title.includes(phrase) ? 4 : 0) +
|
||||
(titleHasAll(card.title) ? 2 : 0) + (words.some(word => channel.includes(word)) ? 1 : 0);
|
||||
return { card: { ...card, url: card.url || `https://www.youtube.com/watch?v=${card.id}` }, score, seen: Number(row.seen) || 0 };
|
||||
} catch { return null; }
|
||||
}).filter(Boolean).sort((a, b) => b.score - a.score || b.seen - a.seen).slice(0, limit).map(x => x.card);
|
||||
return withLocalThumbnails(ranked);
|
||||
}
|
||||
|
||||
export async function trimVideos(max = MAX_VIDEOS) {
|
||||
|
||||
Reference in New Issue
Block a user