Add resumable admin deep search

This commit is contained in:
Jonathan Sykes
2026-10-14 01:47:35 +08:00
parent ca10fe6486
commit 58d0e6f140
12 changed files with 600 additions and 22 deletions

View File

@@ -68,6 +68,7 @@ import { registerCatalogRoutes } from './recommendations.js';
import QRCode from 'qrcode';
import { createYtdlpPool } from './ytdlp-pool.js';
import { createWarmQueue } from './warm-queue.js';
import { registerDeepSearchRoutes } from './deep-search.js';
import { dirname, join as pathJoin } from 'node:path';
// A media proxy must not die because one client's stream hit an edge case
@@ -1113,10 +1114,11 @@ async function ytdlpDownloadResponse(videoId, fp, formatArgs, signal, sourceUrl
const tmp = `${tmpdir()}/${tmpBase}.mp4`;
const ctl = new AbortController();
entry = { waiters: 0, tmpBase, ctl };
entry.promise = withSaveSlot(() => {
entry.promise = withSaveSlot(async () => {
if (ctl.signal.aborted) throw new Error('save cancelled');
const url = sourceUrl || await videoCatalog.getSourceUrl(videoId) || `https://www.youtube.com/watch?v=${videoId}`;
return runYtdlpResilient([
sourceUrl || await videoCatalog.getSourceUrl(videoId) || `https://www.youtube.com/watch?v=${videoId}`,
url,
'--no-warnings', '--no-playlist',
...formatArgs,
'--limit-rate', DOWNLOAD_RATE,
@@ -1186,14 +1188,16 @@ async function ytdlpEditedDownloadResponse(videoId, fp, keep, signal, cachedSrc
try {
// 1) Grab the full source (video+audio merged) so ffmpeg has both streams
// — unless the server cache already holds it.
if (!cachedSrc) await withSaveSlot(() => runYtdlpResilient([
await videoCatalog.getSourceUrl(videoId) || `https://www.youtube.com/watch?v=${videoId}`,
'--no-warnings', '--no-playlist',
'-f', SAVE_MUX_FORMAT,
'--merge-output-format', 'mp4',
'--limit-rate', DOWNLOAD_RATE,
'-o', srcTmp,
], { signal }));
if (!cachedSrc) await withSaveSlot(async () => {
const url = await videoCatalog.getSourceUrl(videoId) || `https://www.youtube.com/watch?v=${videoId}`;
return runYtdlpResilient([
url, '--no-warnings', '--no-playlist',
'-f', SAVE_MUX_FORMAT,
'--merge-output-format', 'mp4',
'--limit-rate', DOWNLOAD_RATE,
'-o', srcTmp,
], { signal });
});
// 2) Trim + concat the keep segments into the final custom video.
await runFfmpeg([
'-y', '-hide_banner', '-loglevel', 'error',
@@ -2295,6 +2299,8 @@ app.get('/api/remote/qr/:code', async (c) => {
// ============================================================================
const notes = registerNoteRoutes(app, {
db: notesDb,
catalog: videoCatalog,
media,
getProfile,
profileNameRe: PROFILE_NAME_RE,
runYtdlp: runYtdlpResilient,
@@ -2308,6 +2314,9 @@ const notes = registerNoteRoutes(app, {
{ label: 'Database', path: dirname(process.env.DB_PATH || './data/ytplayer.db') },
] },
});
const deepSearch = registerDeepSearchRoutes(app, { db: notesDb, adminAuth: notes.requireAdminOrToken, autoStart: false,
searchDeep: innertube.searchDeep, related: innertube.related, browseUploads: innertube.browseUploads,
ingest: collectVideoMetadata, putSearch: searchCacheDb.put });
// ============================================================================
// Uploads — the server's own searchable media library (see uploads.js)
@@ -2519,6 +2528,7 @@ app.get('/*', indexHtml);
// ============================================================================
async function main() {
await initDb();
deepSearch.start();
startThumbnails();
await p2pDb.initP2pSchema();
console.log(`[ytplayer] DB ready`);