Open Library in the book store; podcast topic search and transcript search; Recipes (Mealie) with a shopping list synced to Tasks; Contacts linked to Mail and the Calendar
build-container / build (push) Successful in 7m21s
build-container / build (push) Successful in 7m21s
- Store: Open Library search, books fetched from the Internet Archive (free editions only) - Podcast: Podcast Index discovery by name and topic (optional key); search what is said across transcripts and play from that moment - Recipes: browse, import from a link, add ingredients to Mealie's shopping list, mirrored two ways with a Shopping list in Tasks - Contacts: per-person address book, vCard import/export, recipient suggestions in Mail, birthdays in the Calendar Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
0617664d97
commit
176b1b8169
28 files changed
+799
-46
No files matched your search
@@ -1,5 +1,6 @@
|
||||
// Transcripts: reading SRT/VTT/JSON, finding an episode's transcript link in a feed, refusing private addresses, and speech-to-text through a fake Whisper server.
|
||||
const http = require('http'), fs = require('fs'), os = require('os'), path = require('path');
|
||||
process.env.DATA_DIR = fs.mkdtempSync(path.join(os.tmpdir(), 'trt-u-'));
|
||||
const tr = require('../transcripts');
|
||||
let bad = 0; const ok = (c, m) => { console.log((c ? 'PASS ' : 'FAIL ') + m); if (!c) bad++; };
|
||||
|
||||
@@ -44,6 +45,16 @@ let bad = 0; const ok = (c, m) => { console.log((c ? 'PASS ' : 'FAIL ') + m);
|
||||
const r = await tr.whisper(audio, 'audio'); ok(r && r.found && r.lines.length === 2 && r.lines[0].text === 'Welcome to the show.' && r.lines[1].text === 'Today we talk about bread.' && r.source === 'Transcribed on your server', 'speech-to-text segments become timed lines (blank ones dropped, tags removed)');
|
||||
ok(calls.includes('POST /v1/models/Systran/faster-whisper-small') && calls.includes('POST /v1/models') && installed, 'a model that is not installed is downloaded first (trying the older form, then the newer)');
|
||||
ok(r.lines[1].t === 2500 && r.lines[1].e === 2500 + 6000, 'times are in milliseconds');
|
||||
// searching what was said
|
||||
const users = require('../users'), me = users.create({ name: 'Zed', role: 'user', handle: 'Wg' });
|
||||
const put = (name, ep, lines) => fs.writeFileSync(path.join(dir, 'transcripts', name + '.json'), JSON.stringify({ found: true, lines, ep }));
|
||||
put('a1', { key: 'ab:book1', src: '/api/ab/books/x', title: 'Bread Book', artist: 'Baker' }, [{ t: 1000, text: 'Today we talk about sourdough bread.' }, { t: 65000, text: 'Knead the dough well' }, { t: 90000, text: 'Let it rest, then bake the bread.' }]);
|
||||
put('a2', { key: 'pod:77', src: '/api/podcast/podcastitems/77/file', title: 'Not Mine', artist: 'Other' }, [{ t: 0, text: 'sourdough bread everywhere' }]);
|
||||
put('a3', null, [{ t: 0, text: 'sourdough bread with no name' }]);
|
||||
const found = await users.runAs(me, () => tr.search(me, 'Sourdough bread'));
|
||||
ok(found.length === 1 && found[0].title === 'Bread Book' && found[0].hits[0].t === 1000 && found[0].exact, 'a search finds the moment, ignoring capitals; other people\'s podcasts and unnamed files are left out');
|
||||
const two = await users.runAs(me, () => tr.search(me, 'knead well')); ok(two.length === 1 && two[0].hits[0].t === 65000 && !two[0].exact, 'all the words on a line match, not only the exact phrase');
|
||||
ok((await users.runAs(me, () => tr.search(me, 'x'))).length === 0 && (await users.runAs(me, () => tr.search(me, 'zebra'))).length === 0, 'too short, or not said: nothing');
|
||||
srv.close(); fs.rmSync(dir, { recursive: true, force: true });
|
||||
console.log(bad ? '\n' + bad + ' check(s) failed' : '\nAll transcript checks passed'); process.exit(bad ? 1 : 0);
|
||||
})().catch(e => { console.error(e); process.exit(1); });
|
||||
Reference in new issue
Block a user