Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@

### Fixes

- CLI cache: include local media `fileMtime` when writing transcript cache entries so repeated unchanged audio/video extraction can hit cache (#240, #241, thanks @alfozan).
- CLI: pass Codex image attachments to `codex exec` so local image summaries no longer fail before starting (#242, #243, thanks @alfozan).
- Chrome extension: abort stale side-panel summary streams on tab changes so delayed output from a closed or replaced tab cannot render under the new page title.
- Core: extract video IDs from YouTube `/live/` URLs so live and premiere links no longer abort summarization (#232, thanks @devYRPauli).
Expand Down
13 changes: 5 additions & 8 deletions src/cache.ts
Original file line number Diff line number Diff line change
Expand Up @@ -362,20 +362,17 @@ export async function createCacheStore({
typeof transcriptNamespace === "string" && transcriptNamespace.trim().length > 0
? transcriptNamespace.trim()
: null;
const getTranscriptKey = (url: string): string =>
const getTranscriptKey = (url: string, fileMtime?: number | null): string =>
buildTranscriptCacheKey({
url,
namespace: normalizedTranscriptNamespace,
fileMtime,
});

const transcriptCache: TranscriptCache = {
get: async ({ url, fileMtime }) => {
const now = Date.now();
const key = buildTranscriptCacheKey({
url,
namespace: normalizedTranscriptNamespace,
fileMtime,
});
const key = getTranscriptKey(url, fileMtime);
const row = readEntry("transcript", key, now);
if (!row) return null;
const expired = typeof row.expires_at === "number" && row.expires_at <= now;
Expand All @@ -400,8 +397,8 @@ export async function createCacheStore({
metadata: (payload?.metadata as Record<string, unknown> | null | undefined) ?? null,
};
},
set: async ({ url, content, source, ttlMs, metadata, service, resourceKey }) => {
const key = getTranscriptKey(url);
set: async ({ url, content, source, ttlMs, metadata, service, resourceKey, fileMtime }) => {
const key = getTranscriptKey(url, fileMtime);
setJson(
"transcript",
key,
Expand Down
28 changes: 28 additions & 0 deletions tests/cache.store.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -182,6 +182,34 @@ describe("cache store", () => {
otherStore.close();
});

it("keys local transcript cache writes by file mtime", async () => {
const root = mkdtempSync(join(tmpdir(), "summarize-cache-"));
const path = join(root, "cache.sqlite");
const store = await createCacheStore({ path, maxBytes: 1024 * 1024 });
const url = "file:///tmp/audio.opus";

await store.transcriptCache.set({
url,
service: "generic",
resourceKey: null,
ttlMs: 1000,
content: "cached transcript",
source: "yt-dlp",
metadata: null,
fileMtime: 1234,
});

const hit = await store.transcriptCache.get({ url, fileMtime: 1234 });
const staleFile = await store.transcriptCache.get({ url, fileMtime: 5678 });
const noMtime = await store.transcriptCache.get({ url });

expect(hit?.content).toBe("cached transcript");
expect(staleFile).toBeNull();
expect(noMtime).toBeNull();

store.close();
});

it("transcript cache normalizes unknown sources and handles bad payloads", async () => {
const root = mkdtempSync(join(tmpdir(), "summarize-cache-"));
const path = join(root, "cache.sqlite");
Expand Down