Index transcripts into library search (FTS), both UIs
The 🔍 library search now matches spoken words, not just titles /
channels / descriptions.
- database.rs: add a `transcript` column to the `video_search` FTS5 index.
Fresh DBs get it directly; existing ones are migrated (FTS5 has no ADD
COLUMN, so the table is recreated and search_meta cleared to force a
one-time reindex). `sync_search_index` reads each video's first subtitle
(mtime-gated like the description), flattens it via the shared `vtt`
parser (`transcript_text`, capped at 256 KB), and indexes it. Search
snippets now use column -1 so the excerpt comes from whichever column
matched (description or transcript).
- library.rs: `build_search_entries` passes the first subtitle path.
- Both UIs: search copy updated to mention transcripts.
Tests: a unit test (a word only in the .vtt is found), a migration test
(seed an old 5-column index + stale meta, open, confirm the recreated
index indexes transcript text), and the search integration test now seeds
a subtitle and asserts a spoken-only word hits. Also bumped the test
server's startup budget so the ffmpeg-heavy dedup test can't starve a
sibling server into a flaky timeout. 120 tests pass.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
parent
4136a9468b
commit
e840d55332
5 changed files with 132 additions and 18 deletions
10
tests/api.rs
10
tests/api.rs
|
|
@ -66,7 +66,9 @@ impl Server {
|
|||
}
|
||||
|
||||
fn wait_ready(&self) {
|
||||
for _ in 0..150 {
|
||||
// Generous budget: the ffmpeg-heavy dedup test can spike CPU and delay
|
||||
// a sibling server's startup, so don't flake under parallel load.
|
||||
for _ in 0..400 {
|
||||
if let Some((code, _)) = curl(&self.req_args("/", "GET"), None) {
|
||||
if code != 0 { return; } // 0 = connection refused (not up yet)
|
||||
}
|
||||
|
|
@ -338,6 +340,9 @@ fn full_text_search_indexes_titles_and_descriptions() {
|
|||
std::fs::write(chan.join("Cooking Sourdough [vid123].mkv"), b"").unwrap();
|
||||
std::fs::write(chan.join("Cooking Sourdough [vid123].description"),
|
||||
b"an artisan bread tutorial").unwrap();
|
||||
// A subtitle sidecar whose spoken words appear nowhere in the title/desc.
|
||||
std::fs::write(chan.join("Cooking Sourdough [vid123].en.vtt"),
|
||||
b"WEBVTT\n\n00:00:01.000 --> 00:00:04.000\nwhisk the poolish gently\n").unwrap();
|
||||
|
||||
// Rescan so the server picks up the new file and reindexes it.
|
||||
let (code, _) = s.post("/api/rescan", "");
|
||||
|
|
@ -354,6 +359,9 @@ fn full_text_search_indexes_titles_and_descriptions() {
|
|||
// A word only in the description hits (proves the sidecar got indexed).
|
||||
assert!(s.get("/api/search?q=artisan").1.contains("vid123"), "description search should hit");
|
||||
|
||||
// A word only spoken in the transcript hits (proves the .vtt got indexed).
|
||||
assert!(s.get("/api/search?q=poolish").1.contains("vid123"), "transcript search should hit");
|
||||
|
||||
// An unrelated query does not.
|
||||
assert!(!s.get("/api/search?q=quantumchromodynamics").1.contains("vid123"),
|
||||
"unrelated query must not hit");
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue