ossclip 0.1.35 → 0.1.37
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.
- package/README.md +11 -1
- package/editor-dist/assets/index-pWbFr8vc.js +168 -0
- package/editor-dist/index.html +1 -1
- package/package.json +4 -4
- package/src/analyze.ts +4 -0
- package/src/cover.ts +18 -0
- package/src/doctor.ts +38 -6
- package/src/edit.ts +204 -34
- package/src/interactive/produce-wizard.ts +13 -2
- package/src/produce.ts +330 -45
- package/src/program.ts +68 -0
- package/src/setup/plan.ts +18 -0
- package/src/whisper-backend.ts +103 -0
- package/editor-dist/assets/index-DSB_SCmp.js +0 -166
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
import type { OssclipConfig } from "@ossclip/core";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Which transcription backend a run uses — local whisper.cpp (the default,
|
|
5
|
+
* forever) or an OpenAI-compatible `/v1/audio/transcriptions` server.
|
|
6
|
+
*
|
|
7
|
+
* Why (2026-09-01 field report): on a weak CPU — an i3 2nd gen — whisper is
|
|
8
|
+
* the dominant cost of a produce run, and Groq's free tier makes it seconds.
|
|
9
|
+
* Remote is OPT-IN: presence of a URL is the switch, so nobody's existing
|
|
10
|
+
* install changes behavior by upgrading.
|
|
11
|
+
*
|
|
12
|
+
* `publishConfigured`'s mould (publish.ts), with one deliberate difference:
|
|
13
|
+
* the API key is OPTIONAL. Self-hosted servers (speaches, whisper.cpp
|
|
14
|
+
* server) run keyless, so requiring a key the way publish does would lock
|
|
15
|
+
* out the privacy-minded half of the audience.
|
|
16
|
+
*
|
|
17
|
+
* Pure over (flag, config, env) so the whole matrix is testable without a
|
|
18
|
+
* config file or a poked process.env.
|
|
19
|
+
*/
|
|
20
|
+
|
|
21
|
+
/** Env-only, like every other secret (env.ts's rule): keys never live in config.json. */
|
|
22
|
+
export const WHISPER_API_KEY_ENV = "OSSCLIP_WHISPER_API_KEY";
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Groq's word-timestamped turbo model — the one the quickstart in the README
|
|
26
|
+
* points at. The default lives HERE rather than in config.ts's DEFAULTS
|
|
27
|
+
* because it is only meaningful once a remote URL exists, and a self-hosted
|
|
28
|
+
* box (whose model names look like "Systran/faster-whisper-large-v3") sets
|
|
29
|
+
* `whisperRemoteModel` anyway.
|
|
30
|
+
*/
|
|
31
|
+
export const DEFAULT_REMOTE_WHISPER_MODEL = "whisper-large-v3-turbo";
|
|
32
|
+
|
|
33
|
+
export type WhisperBackend =
|
|
34
|
+
| { kind: "local" }
|
|
35
|
+
| { kind: "remote"; baseUrl: string; model: string; apiKey?: string };
|
|
36
|
+
|
|
37
|
+
export type WhisperBackendResult =
|
|
38
|
+
| { ok: true; backend: WhisperBackend }
|
|
39
|
+
| { ok: false; message: string };
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* `--whisper-backend` (already zod-parsed by program.ts) beats the config,
|
|
43
|
+
* and "local" ALWAYS wins — it is the escape hatch a user reaches for when
|
|
44
|
+
* the remote server is down or the audio must not leave the machine, so it
|
|
45
|
+
* can never be overridden by a URL sitting in config.json.
|
|
46
|
+
*
|
|
47
|
+
* A typed `--whisper-backend remote` with nothing configured is an ERROR
|
|
48
|
+
* naming both spellings, not a silent fall back to local: the user asked for
|
|
49
|
+
* remote precisely because local is what they are trying to avoid.
|
|
50
|
+
*/
|
|
51
|
+
export function resolveWhisperBackend(
|
|
52
|
+
flag: "local" | "remote" | undefined,
|
|
53
|
+
cfg: Pick<OssclipConfig, "whisperUrl" | "whisperRemoteModel">,
|
|
54
|
+
env: NodeJS.ProcessEnv,
|
|
55
|
+
): WhisperBackendResult {
|
|
56
|
+
if (flag === "local") return { ok: true, backend: { kind: "local" } };
|
|
57
|
+
// typeof + trim, never truthiness: `whisperUrl: " "` in a hand-edited
|
|
58
|
+
// config.json must read as "not configured", not as a URL we then POST to.
|
|
59
|
+
const url = typeof cfg.whisperUrl === "string" ? cfg.whisperUrl.trim() : "";
|
|
60
|
+
if (url.length === 0) {
|
|
61
|
+
if (flag === "remote") {
|
|
62
|
+
return {
|
|
63
|
+
ok: false,
|
|
64
|
+
message:
|
|
65
|
+
"--whisper-backend remote needs a transcription server: set OSSCLIP_WHISPER_URL in the " +
|
|
66
|
+
'environment (or ~/.ossclip/.env), or "whisperUrl" in ~/.ossclip/config.json — the ' +
|
|
67
|
+
"OpenAI-compatible base ending in /v1, e.g. https://api.groq.com/openai/v1.",
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
return { ok: true, backend: { kind: "local" } };
|
|
71
|
+
}
|
|
72
|
+
const model =
|
|
73
|
+
typeof cfg.whisperRemoteModel === "string" && cfg.whisperRemoteModel.trim().length > 0
|
|
74
|
+
? cfg.whisperRemoteModel.trim()
|
|
75
|
+
: DEFAULT_REMOTE_WHISPER_MODEL;
|
|
76
|
+
const key = env[WHISPER_API_KEY_ENV]?.trim() ?? "";
|
|
77
|
+
return {
|
|
78
|
+
ok: true,
|
|
79
|
+
backend: {
|
|
80
|
+
kind: "remote",
|
|
81
|
+
baseUrl: url,
|
|
82
|
+
model,
|
|
83
|
+
// Omitted rather than "" when unset, so the provider sends NO
|
|
84
|
+
// Authorization header at all — a keyless self-hosted server is a
|
|
85
|
+
// supported configuration, not a missing key.
|
|
86
|
+
...(key.length > 0 ? { apiKey: key } : {}),
|
|
87
|
+
},
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* The host for a one-line stage/status label. Falls back to the raw string
|
|
93
|
+
* when `new URL` refuses it: the value is user-typed config, and a stage line
|
|
94
|
+
* must never be the thing that throws — the POST that follows will report a
|
|
95
|
+
* bad URL with far better context.
|
|
96
|
+
*/
|
|
97
|
+
export function remoteWhisperHost(baseUrl: string): string {
|
|
98
|
+
try {
|
|
99
|
+
return new URL(baseUrl).host;
|
|
100
|
+
} catch {
|
|
101
|
+
return baseUrl;
|
|
102
|
+
}
|
|
103
|
+
}
|