Search: server-known videos first with YouTube results appended as they arrive; On server badge on cards the server holds a copy of
This commit is contained in:
@@ -455,8 +455,24 @@ const searchLibrary = async (q) => {
|
||||
// This server's own library first — it still answers when YouTube is unreachable.
|
||||
try { return (await notesDb.listUploads({ q, limit: 20, listedOnly: true })).map(uploads.card); } catch { return []; }
|
||||
};
|
||||
const withYoutubeUrl = card => card && /^[\w-]{11}$/.test(card.id || '')
|
||||
? { ...card, url: `https://www.youtube.com/watch?v=${card.id}` } : card;
|
||||
// Ids the server holds a ready copy of, refreshed at most once a minute, so
|
||||
// search results can say "On server" without a query per card.
|
||||
let serverCopies = { at: 0, ids: new Set(), loading: null };
|
||||
async function serverCopyIds() {
|
||||
if (Date.now() - serverCopies.at < 60_000) return serverCopies.ids;
|
||||
serverCopies.loading ||= notesDb.db.execute("SELECT video_id FROM media_cache WHERE status = 'ready'")
|
||||
.then((r) => { serverCopies = { at: Date.now(), ids: new Set(r.rows.map((x) => x.video_id)), loading: null }; })
|
||||
.catch(() => { serverCopies.loading = null; serverCopies.at = Date.now(); });
|
||||
await serverCopies.loading;
|
||||
return serverCopies.ids;
|
||||
}
|
||||
let copyIds = new Set();
|
||||
const withYoutubeUrl = card => {
|
||||
if (!card) return card;
|
||||
const out = /^[\w-]{11}$/.test(card.id || '') ? { ...card, url: `https://www.youtube.com/watch?v=${card.id}` } : { ...card };
|
||||
if (copyIds.has(card.id) || String(card.id || '').startsWith('upl_')) out.onServer = true;
|
||||
return out;
|
||||
};
|
||||
|
||||
// GET /api/search/local?q=<query> — videos this server already knows (from every
|
||||
// earlier search) plus its own uploads; answers in milliseconds so the app can
|
||||
@@ -464,7 +480,8 @@ const withYoutubeUrl = card => card && /^[\w-]{11}$/.test(card.id || '')
|
||||
app.get('/api/search/local', async (c) => {
|
||||
const q = (c.req.query('q') || '').trim();
|
||||
if (!q) return c.json({ ok: false, error: 'empty query' }, 400);
|
||||
const [mine, known] = await Promise.all([searchLibrary(q), searchCacheDb.searchVideos(q).catch(() => [])]);
|
||||
const [mine, known, ids] = await Promise.all([searchLibrary(q), searchCacheDb.searchVideos(q).catch(() => []), serverCopyIds()]);
|
||||
copyIds = ids;
|
||||
return c.json({ ok: true, results: [...mine, ...known].map(withYoutubeUrl), local: true });
|
||||
});
|
||||
|
||||
@@ -478,6 +495,7 @@ app.get('/api/search', async (c) => {
|
||||
if (!q) return c.json({ ok: false, error: 'empty query' }, 400);
|
||||
const key = q.toLowerCase();
|
||||
const mine = await searchLibrary(q);
|
||||
copyIds = await serverCopyIds();
|
||||
const answer = (yt, extra = {}) => {
|
||||
const body = JSON.stringify({ ok: true, results: [...mine, ...yt].map(withYoutubeUrl), ...extra });
|
||||
const headers = { 'Content-Type': 'application/json; charset=utf-8', Vary: 'Accept-Encoding' };
|
||||
|
||||
Reference in New Issue
Block a user