@khorsheed/dsh-reader 0.3.0 → 0.3.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (89) hide show
  1. package/CHANGELOG.md +29 -0
  2. package/README.en.md +26 -121
  3. package/README.i18n.yaml +3 -3
  4. package/README.md +26 -121
  5. package/cordis.patch.yml +7 -0
  6. package/lib/client.js +29105 -6692
  7. package/lib/index.js +3500 -321
  8. package/lib/typert.host.js +2145 -259
  9. package/lib/typert.remote-client.d.ts +97 -3
  10. package/lib/typert.remote-client.js +1590 -250
  11. package/lib/types/captions.d.ts +99 -0
  12. package/lib/types/captions.js +236 -0
  13. package/lib/types/client/MediaPlayer.d.ts +55 -0
  14. package/lib/types/client/MediaPlayer.js +106 -0
  15. package/lib/types/client/NotesView.d.ts +57 -0
  16. package/lib/types/client/NotesView.js +124 -0
  17. package/lib/types/client/ReaderPane.d.ts +20 -3
  18. package/lib/types/client/ReaderPane.js +860 -164
  19. package/lib/types/client/SpeakerLegend.d.ts +18 -0
  20. package/lib/types/client/SpeakerLegend.js +26 -0
  21. package/lib/types/client/canvas-sync.d.ts +84 -0
  22. package/lib/types/client/canvas-sync.js +59 -0
  23. package/lib/types/client/contract.d.ts +130 -3
  24. package/lib/types/client/extract-article.d.ts +38 -1
  25. package/lib/types/client/extract-article.js +561 -33
  26. package/lib/types/client/icons.d.ts +1 -1
  27. package/lib/types/client/icons.js +1 -1
  28. package/lib/types/client/index.js +81 -2
  29. package/lib/types/client/locales.d.ts +279 -11
  30. package/lib/types/client/locales.js +278 -10
  31. package/lib/types/client/notes.d.ts +142 -0
  32. package/lib/types/client/notes.js +334 -0
  33. package/lib/types/client/parse-rss.js +40 -0
  34. package/lib/types/client/preset-visibility.d.ts +93 -0
  35. package/lib/types/client/preset-visibility.js +115 -0
  36. package/lib/types/client/selectors.d.ts +5 -0
  37. package/lib/types/client/selectors.js +5 -0
  38. package/lib/types/client/session.d.ts +21 -1
  39. package/lib/types/client/session.js +12 -0
  40. package/lib/types/client/store.js +4 -1
  41. package/lib/types/client/translate.d.ts +26 -1
  42. package/lib/types/client/translate.js +29 -12
  43. package/lib/types/client/useLocalTranscript.d.ts +43 -0
  44. package/lib/types/client/useLocalTranscript.js +129 -0
  45. package/lib/types/client/useMedia.d.ts +68 -0
  46. package/lib/types/client/useMedia.js +299 -0
  47. package/lib/types/client/useMediaStates.d.ts +9 -0
  48. package/lib/types/client/useMediaStates.js +43 -0
  49. package/lib/types/client/useNotes.d.ts +110 -0
  50. package/lib/types/client/useNotes.js +423 -0
  51. package/lib/types/client/usePolish.d.ts +47 -0
  52. package/lib/types/client/usePolish.js +162 -0
  53. package/lib/types/document-fetch.d.ts +106 -0
  54. package/lib/types/document-fetch.js +100 -0
  55. package/lib/types/index.d.ts +15 -0
  56. package/lib/types/index.js +3 -0
  57. package/lib/types/local-transcribe.d.ts +171 -0
  58. package/lib/types/local-transcribe.js +473 -0
  59. package/lib/types/media-host.d.ts +91 -0
  60. package/lib/types/media-host.js +230 -0
  61. package/lib/types/media.d.ts +100 -0
  62. package/lib/types/media.js +207 -0
  63. package/lib/types/notes-types.d.ts +158 -0
  64. package/lib/types/notes-types.js +8 -0
  65. package/lib/types/notes.d.ts +213 -0
  66. package/lib/types/notes.js +718 -0
  67. package/lib/types/polish.d.ts +41 -0
  68. package/lib/types/polish.js +136 -0
  69. package/lib/types/remote.d.ts +118 -0
  70. package/lib/types/remote.js +151 -1
  71. package/lib/types/service.d.ts +81 -6
  72. package/lib/types/service.js +286 -65
  73. package/lib/types/skill.d.ts +20 -0
  74. package/lib/types/skill.js +78 -0
  75. package/lib/types/store.js +30 -8
  76. package/lib/types/tool.d.ts +23 -0
  77. package/lib/types/tool.js +40 -0
  78. package/lib/types/tools.d.ts +69 -0
  79. package/lib/types/tools.js +400 -0
  80. package/lib/types/types.d.ts +90 -2
  81. package/lib/types/types.js +66 -0
  82. package/lib/types/xiaoyuzhou.d.ts +43 -0
  83. package/lib/types/xiaoyuzhou.js +134 -0
  84. package/lib/types/ytdlp.d.ts +111 -0
  85. package/lib/types/ytdlp.js +269 -0
  86. package/package.json +33 -20
  87. package/lib/invariant.js +0 -38
  88. package/lib/types/invariant.d.ts +0 -29
  89. package/lib/types/invariant.js +0 -38
@@ -0,0 +1,106 @@
1
+ /**
2
+ * The document fetcher: the reader's own instance of the official HTTP fetch
3
+ * provider, so feeds and article pages arrive as the origin served them.
4
+ *
5
+ * Why not `ctx.web.fetch` alone: the seam runs whichever provider the host
6
+ * selects, and from 0.2.1-rc.2 the web-search settings switch for the DeepSeek
7
+ * Search/Fetch API moves `web.fetchProvider` too. That provider answers
8
+ * Markdown (`body.kind: 'text'`), a constant 200, and the request URL as the
9
+ * final URL — so `classifyPayload` reads every feed as a page, the extractor
10
+ * gets no HTML, and the cross-origin redirect handling never fires. The
11
+ * request carries only a URL; no caller can ask for the HTTP provider (seam
12
+ * registry S22).
13
+ *
14
+ * So when the mounted `web` is the official service (it carries
15
+ * `registerFetchProvider`), the reader fetches through its own
16
+ * `HttpFetchProvider` — the same class the default `http` row registers, with
17
+ * that row's default limits, so the policy (public addresses only, connection
18
+ * pinned, same-origin redirects, charset decoding) and the errors
19
+ * (`WEB_REDIRECT_BLOCKED` naming the target origin) are exactly today's.
20
+ * `ctx.web` stays the permission: without it nothing is fetched. A host
21
+ * without the provider package (or any other `web` shape, such as a test's
22
+ * plain `{ fetch }`) is fetched through as before.
23
+ *
24
+ * Retire when the seam lets one request name its provider (S22).
25
+ *
26
+ * @module @khorsheed/dsh-reader/document-fetch
27
+ */
28
+ /** The shape of one fetch result, declared structurally (no host import). */
29
+ export interface DocumentFetchResult {
30
+ readonly url: string;
31
+ readonly statusCode: number;
32
+ readonly body: {
33
+ readonly kind: 'html' | 'text';
34
+ readonly content: string;
35
+ };
36
+ readonly truncated: boolean;
37
+ }
38
+ /** Anything that fetches one URL: the web seam or a provider. */
39
+ export interface DocumentFetcher {
40
+ fetch(request: {
41
+ readonly url: string;
42
+ }, signal?: AbortSignal): Promise<DocumentFetchResult>;
43
+ }
44
+ /** The limits the official provider takes (the `http` row's resolved config). */
45
+ interface HttpFetchLimits {
46
+ readonly maxResponseBytes: number;
47
+ readonly maxBodyChars: number;
48
+ readonly timeoutMs: number;
49
+ readonly maxRedirects: number;
50
+ readonly userAgent: string;
51
+ }
52
+ /** The provider package's runtime face, structurally. */
53
+ interface HttpProviderModule {
54
+ readonly HttpFetchProvider: new (limits: HttpFetchLimits) => DocumentFetcher;
55
+ readonly DEFAULT_USER_AGENT?: string;
56
+ }
57
+ /**
58
+ * The reader's own fetch limits: the `http` row's defaults
59
+ * (`@deepseek-ai/dsh-web-fetch-http` Config, 0.1.5 through 0.2.1-rc.3) except
60
+ * `maxBodyChars`. The row's 100 000 is sized for a page handed to a model; the
61
+ * reader extracts the article itself from the raw HTML, and a modern page
62
+ * carries several hundred KB of inline script before its last paragraph
63
+ * (measured: claude.com's articles, 620 KB, cut mid-sentence at 100 000). 2 000 000
64
+ * is what deployments ran the row at while the reader still fetched through
65
+ * it, and the per-source storage budget (`MAX_BODY_CHARS_PER_SOURCE`) is
66
+ * sized to match. A deployment changes these with the reader's own
67
+ * `fetchLimits`: the row's config does not reach this instance.
68
+ */
69
+ export declare const DOCUMENT_FETCH_DEFAULTS: {
70
+ readonly maxResponseBytes: 5000000;
71
+ readonly maxBodyChars: 2000000;
72
+ readonly timeoutMs: 30000;
73
+ readonly maxRedirects: 5;
74
+ };
75
+ /** The limits a deployment may override (the user agent stays the provider's). */
76
+ export type DocumentFetchLimits = Partial<Pick<HttpFetchLimits, 'maxResponseBytes' | 'maxBodyChars' | 'timeoutMs' | 'maxRedirects'>>;
77
+ /** Whether a mounted `web` is the official service rather than some other shape. */
78
+ export declare function isOfficialWebService(web: unknown): boolean;
79
+ /** Loads the provider package; injectable so tests never need the real one. */
80
+ export type HttpProviderLoader = () => Promise<HttpProviderModule | undefined>;
81
+ /**
82
+ * Pick the fetcher for raw documents.
83
+ *
84
+ * Built once per service: the first fetch on an official web service loads
85
+ * the provider package and keeps the instance; a failed load is remembered
86
+ * too, and every later fetch goes through the seam.
87
+ */
88
+ export declare class DocumentFetch {
89
+ private readonly web;
90
+ private readonly limits;
91
+ private readonly load;
92
+ private provider;
93
+ /**
94
+ * @param web - the probed web seam, read on every call (it may come and go).
95
+ * @param limits - the reader's configured fetch limits, over the defaults.
96
+ * @param load - the provider package loader (tests inject one).
97
+ */
98
+ constructor(web: () => unknown, limits?: DocumentFetchLimits, load?: HttpProviderLoader);
99
+ /**
100
+ * The fetcher for this call, or undefined when no web capability is mounted.
101
+ * @returns the reader's own provider on an official web service, else the seam itself.
102
+ */
103
+ current(): Promise<DocumentFetcher | undefined>;
104
+ }
105
+ export {};
106
+ //# sourceMappingURL=document-fetch.d.ts.map
@@ -0,0 +1,100 @@
1
+ /**
2
+ * The document fetcher: the reader's own instance of the official HTTP fetch
3
+ * provider, so feeds and article pages arrive as the origin served them.
4
+ *
5
+ * Why not `ctx.web.fetch` alone: the seam runs whichever provider the host
6
+ * selects, and from 0.2.1-rc.2 the web-search settings switch for the DeepSeek
7
+ * Search/Fetch API moves `web.fetchProvider` too. That provider answers
8
+ * Markdown (`body.kind: 'text'`), a constant 200, and the request URL as the
9
+ * final URL — so `classifyPayload` reads every feed as a page, the extractor
10
+ * gets no HTML, and the cross-origin redirect handling never fires. The
11
+ * request carries only a URL; no caller can ask for the HTTP provider (seam
12
+ * registry S22).
13
+ *
14
+ * So when the mounted `web` is the official service (it carries
15
+ * `registerFetchProvider`), the reader fetches through its own
16
+ * `HttpFetchProvider` — the same class the default `http` row registers, with
17
+ * that row's default limits, so the policy (public addresses only, connection
18
+ * pinned, same-origin redirects, charset decoding) and the errors
19
+ * (`WEB_REDIRECT_BLOCKED` naming the target origin) are exactly today's.
20
+ * `ctx.web` stays the permission: without it nothing is fetched. A host
21
+ * without the provider package (or any other `web` shape, such as a test's
22
+ * plain `{ fetch }`) is fetched through as before.
23
+ *
24
+ * Retire when the seam lets one request name its provider (S22).
25
+ *
26
+ * @module @khorsheed/dsh-reader/document-fetch
27
+ */
28
+ /**
29
+ * The reader's own fetch limits: the `http` row's defaults
30
+ * (`@deepseek-ai/dsh-web-fetch-http` Config, 0.1.5 through 0.2.1-rc.3) except
31
+ * `maxBodyChars`. The row's 100 000 is sized for a page handed to a model; the
32
+ * reader extracts the article itself from the raw HTML, and a modern page
33
+ * carries several hundred KB of inline script before its last paragraph
34
+ * (measured: claude.com's articles, 620 KB, cut mid-sentence at 100 000). 2 000 000
35
+ * is what deployments ran the row at while the reader still fetched through
36
+ * it, and the per-source storage budget (`MAX_BODY_CHARS_PER_SOURCE`) is
37
+ * sized to match. A deployment changes these with the reader's own
38
+ * `fetchLimits`: the row's config does not reach this instance.
39
+ */
40
+ export const DOCUMENT_FETCH_DEFAULTS = {
41
+ maxResponseBytes: 5_000_000,
42
+ maxBodyChars: 2_000_000,
43
+ timeoutMs: 30_000,
44
+ maxRedirects: 5,
45
+ };
46
+ /** Whether a mounted `web` is the official service rather than some other shape. */
47
+ export function isOfficialWebService(web) {
48
+ return typeof web?.registerFetchProvider === 'function';
49
+ }
50
+ const loadOfficialProvider = () => import('@deepseek-ai/dsh-web-fetch-http').then(module => module, () => undefined);
51
+ /**
52
+ * Pick the fetcher for raw documents.
53
+ *
54
+ * Built once per service: the first fetch on an official web service loads
55
+ * the provider package and keeps the instance; a failed load is remembered
56
+ * too, and every later fetch goes through the seam.
57
+ */
58
+ export class DocumentFetch {
59
+ web;
60
+ limits;
61
+ load;
62
+ provider;
63
+ /**
64
+ * @param web - the probed web seam, read on every call (it may come and go).
65
+ * @param limits - the reader's configured fetch limits, over the defaults.
66
+ * @param load - the provider package loader (tests inject one).
67
+ */
68
+ constructor(web, limits = {}, load = loadOfficialProvider) {
69
+ this.web = web;
70
+ this.limits = limits;
71
+ this.load = load;
72
+ }
73
+ /**
74
+ * The fetcher for this call, or undefined when no web capability is mounted.
75
+ * @returns the reader's own provider on an official web service, else the seam itself.
76
+ */
77
+ async current() {
78
+ const web = this.web();
79
+ if (web === undefined || web === null)
80
+ return undefined;
81
+ const seam = web;
82
+ if (!isOfficialWebService(web))
83
+ return seam;
84
+ this.provider ??= this.load().then(module => {
85
+ if (module === undefined || typeof module.HttpFetchProvider !== 'function')
86
+ return undefined;
87
+ try {
88
+ return new module.HttpFetchProvider({
89
+ ...DOCUMENT_FETCH_DEFAULTS,
90
+ ...this.limits,
91
+ userAgent: module.DEFAULT_USER_AGENT ?? 'deepseek-harness/0.0.1 (+https://github.com/deepseek-ai)',
92
+ });
93
+ }
94
+ catch {
95
+ return undefined;
96
+ }
97
+ });
98
+ return (await this.provider) ?? seam;
99
+ }
100
+ }
@@ -12,18 +12,23 @@
12
12
  * @module @khorsheed/dsh-reader
13
13
  */
14
14
  import type { Context } from '@deepseek-ai/cordis';
15
+ import type { DocumentFetchLimits } from './document-fetch.ts';
15
16
  import { ReaderService } from './service.ts';
17
+ import { ReaderNotesStore } from './notes.ts';
16
18
  export { ReaderService, classifyPayload, normalizeUrl } from './service.ts';
17
19
  export { ReaderRemoteService } from './remote.ts';
18
20
  export type { ReaderRemoteConfig } from './remote.ts';
19
21
  export { MAX_CACHED_BODIES, MAX_TAGS, ReaderStore, ReaderStoreError, boundAnnotations, boundPayloads, emptyStateDoc, normalizeStateDoc, pruneOrphanTags, resolveReaderStateRoot, serializeStateDoc, } from './store.ts';
20
22
  export type { ReaderStoreRead } from './store.ts';
23
+ export * from './notes.ts';
21
24
  export * from './schedule.ts';
22
25
  export * from './types.ts';
23
26
  declare module '@deepseek-ai/cordis' {
24
27
  interface Context {
25
28
  /** The reader plugin's service core. */
26
29
  reader: ReaderService;
30
+ /** The reader's notes, discussions and article snapshots. */
31
+ readerNotes: ReaderNotesStore;
27
32
  }
28
33
  }
29
34
  /** Cordis plugin name used by loader diagnostics. */
@@ -32,6 +37,16 @@ export declare const name = "reader";
32
37
  export interface ReaderConfig {
33
38
  /** Directory holding `state.json`; defaults to `$DSH_HOME/state/dsh-reader`. */
34
39
  readonly stateRoot?: string;
40
+ /** A yt-dlp binary to use instead of the reader's managed copy (video metadata and captions). */
41
+ readonly ytdlpPath?: string;
42
+ /**
43
+ * Fetch limits for feeds and pages: `maxResponseBytes` (default 5 000 000),
44
+ * `maxBodyChars` (2 000 000 — whole pages, not the row's model-sized 100 000),
45
+ * `timeoutMs` (30 000), `maxRedirects` (5). The reader fetches through its own
46
+ * instance of the official `http` provider, so limits raised on that row do
47
+ * not reach it; set them here.
48
+ */
49
+ readonly fetchLimits?: DocumentFetchLimits;
35
50
  }
36
51
  /**
37
52
  * Plugin body: provide the service core, then mount the Remote data face.
@@ -1,8 +1,10 @@
1
1
  import { ReaderService } from "./service.js";
2
+ import { ReaderNotesStore } from "./notes.js";
2
3
  import { ReaderRemoteService } from "./remote.js";
3
4
  export { ReaderService, classifyPayload, normalizeUrl } from "./service.js";
4
5
  export { ReaderRemoteService } from "./remote.js";
5
6
  export { MAX_CACHED_BODIES, MAX_TAGS, ReaderStore, ReaderStoreError, boundAnnotations, boundPayloads, emptyStateDoc, normalizeStateDoc, pruneOrphanTags, resolveReaderStateRoot, serializeStateDoc, } from "./store.js";
7
+ export * from "./notes.js";
6
8
  export * from "./schedule.js";
7
9
  export * from "./types.js";
8
10
  /** Cordis plugin name used by loader diagnostics. */
@@ -21,5 +23,6 @@ export const name = 'reader';
21
23
  export function apply(ctx, config = {}) {
22
24
  const service = new ReaderService(ctx, config);
23
25
  ctx.provide('reader', service);
26
+ ctx.provide('readerNotes', new ReaderNotesStore(config));
24
27
  ctx.plugin(ReaderRemoteService, {});
25
28
  }
@@ -0,0 +1,171 @@
1
+ import { type Cue } from './captions.ts';
2
+ import type { ReaderMedia } from './media.ts';
3
+ import type { LocalTranscriptStatus } from './types.ts';
4
+ /** The part of the `transcribe` service the reader calls. */
5
+ export interface TranscribeEngine {
6
+ modelsFor(request: {
7
+ language?: 'zh' | 'en' | 'auto';
8
+ diarize?: false;
9
+ }): string[];
10
+ missingBytes(ids: readonly string[]): Promise<number>;
11
+ run(request: {
12
+ input: {
13
+ files: string[];
14
+ };
15
+ fromSec?: number;
16
+ language: 'zh' | 'en' | 'auto';
17
+ diarize: false;
18
+ owner: string;
19
+ label?: string;
20
+ }, options: {
21
+ signal?: AbortSignal;
22
+ onProgress?: (progress: {
23
+ stage: string;
24
+ progress: number;
25
+ ahead?: number;
26
+ }) => void;
27
+ onSegments?: (segments: readonly EngineSegment[]) => void;
28
+ }): Promise<{
29
+ segments: EngineSegment[];
30
+ language?: string;
31
+ }>;
32
+ /** Who spoke when (an engine older than this has none: transcripts stay one voice). */
33
+ speakers?(request: {
34
+ input: {
35
+ files: string[];
36
+ };
37
+ speech?: {
38
+ start: number;
39
+ end: number;
40
+ }[];
41
+ owner: string;
42
+ label?: string;
43
+ }, options: {
44
+ signal?: AbortSignal;
45
+ onProgress?: (progress: {
46
+ stage: string;
47
+ progress: number;
48
+ ahead?: number;
49
+ }) => void;
50
+ }): Promise<{
51
+ single: boolean;
52
+ turns: {
53
+ cluster: number;
54
+ start: number;
55
+ end: number;
56
+ }[];
57
+ }>;
58
+ }
59
+ /** A speaker turn as the engine reports it. */
60
+ export interface EngineTurn {
61
+ readonly cluster: number;
62
+ readonly start: number;
63
+ readonly end: number;
64
+ }
65
+ /** One finished stretch as the engine reports it. */
66
+ export interface EngineSegment {
67
+ readonly start: number;
68
+ readonly end: number;
69
+ readonly tokens: readonly {
70
+ readonly text: string;
71
+ readonly start: number;
72
+ readonly end: number;
73
+ }[];
74
+ }
75
+ /**
76
+ * Engine stretches as caption-like cues: one per sentence (the engine's
77
+ * stretches are cut at pauses, not sentence ends), so the transcript merges
78
+ * into the same paragraphs platform captions do. A sentence that runs on
79
+ * without a period is closed at a comma or a pause once it is long, or a
80
+ * single cue would make a minute-and-a-half paragraph (measured 2026-10-20).
81
+ *
82
+ * @param segments - the engine's segments, in order.
83
+ */
84
+ export declare function cuesFromSegments(segments: readonly EngineSegment[], speakers?: readonly (number | undefined)[]): (Cue & {
85
+ speaker?: number;
86
+ })[];
87
+ /**
88
+ * Each token's speaker, as an index in order of first appearance (A, B, …):
89
+ * the turn covering the token's middle, else the nearest one; then runs of a
90
+ * voice shorter than {@link MIN_TURN_SEC} are folded into the voice before
91
+ * them — a diarizer's boundary wobble would otherwise split sentences.
92
+ *
93
+ * @param segments - the transcript's segments.
94
+ * @param turns - the engine's turns.
95
+ */
96
+ export declare function speakerPerToken(segments: readonly EngineSegment[], turns: readonly EngineTurn[]): number[];
97
+ /**
98
+ * The transcript with its speakers: paragraphs never cross a change of voice,
99
+ * and each carries `data-spk` (the speaker's index) beside its `data-t`.
100
+ */
101
+ export declare function renderSpeakerTranscript(segments: readonly EngineSegment[], turns: readonly EngineTurn[]): string;
102
+ /** The marker a locally transcribed paragraph carries (the 来源 line reads it). */
103
+ export declare const LOCAL_MARK = "data-src=\"local\"";
104
+ /** The engine's segments as transcript markup, each paragraph marked local. */
105
+ export declare function renderLocalTranscript(segments: readonly EngineSegment[]): string;
106
+ /**
107
+ * The language to ask for: always detection (SenseVoice). Measured
108
+ * 2026-10-20: a platform says nothing reliable about the spoken language — a
109
+ * Bilibili upload of an English interview came out of the Chinese model as
110
+ * lower-case English with Chinese punctuation — and on Chinese speech
111
+ * SenseVoice reads about as well as Paraformer (a 90 s sample: each missed
112
+ * two or three characters the other got right).
113
+ */
114
+ export declare function languageFor(_media: ReaderMedia): 'auto';
115
+ /** What the transcriber needs from the reader. */
116
+ export interface LocalTranscriberDeps {
117
+ /** The engine, probed per start. */
118
+ readonly engine: () => TranscribeEngine | undefined;
119
+ /** Where temporary audio goes. */
120
+ readonly tmpRoot: string;
121
+ /** Fetch a video's audio track into `dir`. */
122
+ readonly videoAudio: (url: string, dir: string, options: {
123
+ signal: AbortSignal;
124
+ onProgress: (fraction: number) => void;
125
+ }) => Promise<string>;
126
+ /** How often a restart checks for the engine before resuming jobs (ms; default 2 s). */
127
+ readonly resumePollMs?: number;
128
+ /** Keep the finished transcript as the entry's body (`speakers`: how many voices, once known). */
129
+ readonly store: (entryId: string, url: string, html: string, language: string | undefined, speakers?: number) => Promise<void>;
130
+ }
131
+ /** The reader's local transcription jobs, by entry. */
132
+ export declare class LocalTranscriber {
133
+ private readonly deps;
134
+ private readonly jobs;
135
+ /** Jobs a previous process left mid-way, waiting for the engine. */
136
+ private readonly resumable;
137
+ private resumeTimer;
138
+ private disposing;
139
+ constructor(deps: LocalTranscriberDeps);
140
+ /** An entry's job as the browser sees it. */
141
+ status(entryId: string): LocalTranscriptStatus;
142
+ /**
143
+ * Start (or rejoin) an entry's transcription.
144
+ *
145
+ * @param request - the entry, its media, and whether the model download was confirmed.
146
+ */
147
+ start(request: {
148
+ entryId: string;
149
+ url: string;
150
+ media: ReaderMedia;
151
+ title?: string;
152
+ confirmModels?: boolean;
153
+ }): Promise<LocalTranscriptStatus>;
154
+ /**
155
+ * Leave or stop an entry's job: a queued (or still fetching) job is
156
+ * withdrawn either way; a running one only with `force`.
157
+ */
158
+ cancel(entryId: string, force: boolean): LocalTranscriptStatus;
159
+ /** Whether the engine is installed right now. */
160
+ engineAvailable(): boolean;
161
+ /** Stop everything (plugin disposal) — keeping each job's record, so the next process resumes it. */
162
+ dispose(): void;
163
+ /**
164
+ * Run (or resume) one job: fetch the audio unless the record has it,
165
+ * transcribe from where the saved segments end, store, then the speakers.
166
+ *
167
+ * @param resumeDir - a previous process's directory for this job.
168
+ */
169
+ private execute;
170
+ }
171
+ //# sourceMappingURL=local-transcribe.d.ts.map