@rightkit/video-ui 0.0.0-stage → 0.1.1
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/CHANGELOG.md +10 -0
- package/LICENSE-APACHE +201 -0
- package/LICENSE-MIT +21 -0
- package/README.md +47 -2
- package/dist/appName.d.ts +2 -0
- package/dist/appName.js +8 -0
- package/dist/components/DesktopWorkspace.d.ts +4 -0
- package/dist/components/DesktopWorkspace.js +527 -0
- package/dist/components/Player.d.ts +49 -0
- package/dist/components/Player.js +335 -0
- package/dist/components/RangePreview.d.ts +9 -0
- package/dist/components/RangePreview.js +22 -0
- package/dist/components/Workspace.css +15 -0
- package/dist/demo/DemoSettings.d.ts +1 -0
- package/dist/demo/DemoSettings.js +43 -0
- package/dist/demo/bindings.d.ts +121 -0
- package/dist/demo/bindings.js +57 -0
- package/dist/demo/demo.css +124 -0
- package/dist/demo/useDemo.d.ts +8 -0
- package/dist/demo/useDemo.js +37 -0
- package/dist/editor/CaptionInspector.d.ts +8 -0
- package/dist/editor/CaptionInspector.js +33 -0
- package/dist/editor/ChangesPanel.d.ts +13 -0
- package/dist/editor/ChangesPanel.js +87 -0
- package/dist/editor/ColourInspector.d.ts +5 -0
- package/dist/editor/ColourInspector.js +341 -0
- package/dist/editor/Inspector.d.ts +15 -0
- package/dist/editor/Inspector.js +202 -0
- package/dist/editor/Timeline.d.ts +39 -0
- package/dist/editor/Timeline.js +506 -0
- package/dist/editor/TimelineEditorView.d.ts +9 -0
- package/dist/editor/TimelineEditorView.js +237 -0
- package/dist/editor/TranscriptPanel.d.ts +20 -0
- package/dist/editor/TranscriptPanel.js +180 -0
- package/dist/editor/actions.d.ts +63 -0
- package/dist/editor/actions.js +124 -0
- package/dist/editor/adapter.d.ts +76 -0
- package/dist/editor/adapter.js +181 -0
- package/dist/editor/capabilities.d.ts +37 -0
- package/dist/editor/capabilities.js +33 -0
- package/dist/editor/gesture.d.ts +29 -0
- package/dist/editor/gesture.js +119 -0
- package/dist/editor/index.d.ts +10 -0
- package/dist/editor/index.js +7 -0
- package/dist/editor/keys.d.ts +15 -0
- package/dist/editor/keys.js +42 -0
- package/dist/editor/model.d.ts +88 -0
- package/dist/editor/model.js +87 -0
- package/dist/editor/transcript.d.ts +27 -0
- package/dist/editor/transcript.js +59 -0
- package/dist/editor/vendor/hyperframes/PlayheadIndicator.d.ts +9 -0
- package/dist/editor/vendor/hyperframes/PlayheadIndicator.js +38 -0
- package/dist/editor/vendor/hyperframes/audioWaveformPeaks.d.ts +7 -0
- package/dist/editor/vendor/hyperframes/audioWaveformPeaks.js +47 -0
- package/dist/editor/vendor/hyperframes/rulerGeometry.d.ts +13 -0
- package/dist/editor/vendor/hyperframes/rulerGeometry.js +133 -0
- package/dist/editor/vendor/hyperframes/useInspectorGestureTransaction.d.ts +33 -0
- package/dist/editor/vendor/hyperframes/useInspectorGestureTransaction.js +126 -0
- package/dist/env.d.ts +66 -0
- package/dist/hooks/useEngine.d.ts +10 -0
- package/dist/hooks/useEngine.js +68 -0
- package/dist/hooks/useHdrToSdr.d.ts +11 -0
- package/dist/hooks/useHdrToSdr.js +19 -0
- package/dist/hooks/useProgress.d.ts +10 -0
- package/dist/hooks/useProgress.js +32 -0
- package/dist/index.d.ts +55 -0
- package/dist/index.js +57 -0
- package/dist/legal/legalConfig.d.ts +38 -0
- package/dist/legal/legalConfig.js +77 -0
- package/dist/lib/assemble.d.ts +100 -0
- package/dist/lib/assemble.js +72 -0
- package/dist/lib/asset.d.ts +2 -0
- package/dist/lib/asset.js +3 -0
- package/dist/lib/dialog.d.ts +4 -0
- package/dist/lib/dialog.js +44 -0
- package/dist/lib/engine.d.ts +44 -0
- package/dist/lib/engine.js +51 -0
- package/dist/lib/genright.d.ts +90 -0
- package/dist/lib/genright.js +160 -0
- package/dist/lib/media.d.ts +1045 -0
- package/dist/lib/media.js +171 -0
- package/dist/lib/mediaAdapters.d.ts +18 -0
- package/dist/lib/mediaAdapters.js +46 -0
- package/dist/lib/mic.d.ts +38 -0
- package/dist/lib/mic.js +14 -0
- package/dist/lib/models.d.ts +53 -0
- package/dist/lib/models.js +72 -0
- package/dist/lib/modelsBackend.d.ts +3 -0
- package/dist/lib/modelsBackend.js +68 -0
- package/dist/lib/progress.d.ts +77 -0
- package/dist/lib/progress.js +192 -0
- package/dist/lib/project.d.ts +138 -0
- package/dist/lib/project.js +48 -0
- package/dist/lib/services.d.ts +27 -0
- package/dist/lib/services.js +38 -0
- package/dist/lib/settingsBackend.d.ts +2 -0
- package/dist/lib/settingsBackend.js +57 -0
- package/dist/lib/shell.d.ts +172 -0
- package/dist/lib/shell.js +32 -0
- package/dist/styles.css +24 -0
- package/dist/ui/icons.d.ts +7 -0
- package/dist/ui/icons.js +62 -0
- package/dist/ui/index.d.ts +45 -0
- package/dist/ui/index.js +43 -0
- package/dist/ui/ui.css +104 -0
- package/dist/views/Assets.css +99 -0
- package/dist/views/AssetsView.d.ts +6 -0
- package/dist/views/AssetsView.js +371 -0
- package/dist/views/AutoEditView.d.ts +16 -0
- package/dist/views/AutoEditView.js +452 -0
- package/dist/views/BatchDelivery.d.ts +3 -0
- package/dist/views/BatchDelivery.js +331 -0
- package/dist/views/BundleExport.d.ts +3 -0
- package/dist/views/BundleExport.js +81 -0
- package/dist/views/Exports.css +117 -0
- package/dist/views/ExportsView.d.ts +12 -0
- package/dist/views/ExportsView.js +354 -0
- package/dist/views/FinishedEdit.css +546 -0
- package/dist/views/Home.css +97 -0
- package/dist/views/Home.d.ts +14 -0
- package/dist/views/Home.js +192 -0
- package/dist/views/Media.css +65 -0
- package/dist/views/OnboardingGate.d.ts +10 -0
- package/dist/views/OnboardingGate.js +54 -0
- package/dist/views/Phrases.css +45 -0
- package/dist/views/PhrasesView.d.ts +4 -0
- package/dist/views/PhrasesView.js +104 -0
- package/dist/views/ProgressView.d.ts +19 -0
- package/dist/views/ProgressView.js +36 -0
- package/dist/views/Prompt.css +84 -0
- package/dist/views/PromptPanel.d.ts +8 -0
- package/dist/views/PromptPanel.js +169 -0
- package/dist/views/Recipe.css +84 -0
- package/dist/views/RecipeLibrary.d.ts +7 -0
- package/dist/views/RecipeLibrary.js +104 -0
- package/dist/views/Retention.css +49 -0
- package/dist/views/RetentionView.d.ts +7 -0
- package/dist/views/RetentionView.js +145 -0
- package/dist/views/SettingsView.d.ts +9 -0
- package/dist/views/SettingsView.js +235 -0
- package/dist/views/Shorts.css +112 -0
- package/dist/views/ShortsView.d.ts +4 -0
- package/dist/views/ShortsView.js +205 -0
- package/dist/views/SourcesView.d.ts +6 -0
- package/dist/views/SourcesView.js +417 -0
- package/dist/views/TimelineView.d.ts +20 -0
- package/dist/views/TimelineView.js +12 -0
- package/package.json +68 -3
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
import { invoke } from "@tauri-apps/api/core";
|
|
2
|
+
import { engineCall } from "./engine.js";
|
|
3
|
+
export const projectsList = (roots) => engineCall("projects.list", {
|
|
4
|
+
roots,
|
|
5
|
+
});
|
|
6
|
+
export const sourceList = (dir) => engineCall("source.list", { dir });
|
|
7
|
+
export const sourceVerify = (dir, tag) => engineCall("source.verify", { dir }, tag);
|
|
8
|
+
export const sourceRelink = (dir, source, path) => engineCall("source.relink", { dir, source, path });
|
|
9
|
+
export const transcribe = (dir, source, job, install, tag) => engineCall("transcribe", { dir, source, job, install: install ?? true }, tag);
|
|
10
|
+
export const transcribeCancel = (job) => engineCall("transcribe.cancel", { job });
|
|
11
|
+
export const transcriptShow = (dir, source) => engineCall("transcript.show", { dir, source });
|
|
12
|
+
export const phraseSearch = (dir, query) => engineCall("phrase.search", { dir, query });
|
|
13
|
+
export const autoeditAnalyze = (dir, preset, opts, tag) => engineCall("autoedit.analyze", {
|
|
14
|
+
dir,
|
|
15
|
+
preset,
|
|
16
|
+
...(opts?.source ? { source: opts.source } : {}),
|
|
17
|
+
...(opts?.sources ? { sources: opts.sources } : {}),
|
|
18
|
+
}, tag);
|
|
19
|
+
export const autoeditApply = (dir, preset, opts, tag) => engineCall("autoedit.apply", {
|
|
20
|
+
dir,
|
|
21
|
+
preset,
|
|
22
|
+
...(opts?.source ? { source: opts.source } : {}),
|
|
23
|
+
...(opts?.sources ? { sources: opts.sources } : {}),
|
|
24
|
+
...(opts?.review ? { review: opts.review } : {}),
|
|
25
|
+
}, tag);
|
|
26
|
+
export const captionsExport = (dir, source, format) => engineCall("captions.export", {
|
|
27
|
+
dir,
|
|
28
|
+
source,
|
|
29
|
+
format,
|
|
30
|
+
});
|
|
31
|
+
export const shortsPropose = (dir, source, count = 5) => engineCall("shorts.propose", { dir, source, count });
|
|
32
|
+
export const shortsAccept = (dir, source, proposal, opts, tag) => engineCall("shorts.accept", { dir, source, proposal, ...(opts ?? {}) }, tag);
|
|
33
|
+
/** `clip.export` — render one source range through the full finish path (R7). */
|
|
34
|
+
export const clipExport = (dir, params, tag) => engineCall("clip.export", { dir, ...params }, tag);
|
|
35
|
+
export const rendersList = (dir) => engineCall("renders.list", { dir });
|
|
36
|
+
export const qaRun = (dir, render, preset, tag) => engineCall("qa.run", { dir, render, preset }, tag);
|
|
37
|
+
export const exportFcpxml = (dir, name) => engineCall("export.fcpxml", { dir, name });
|
|
38
|
+
export const exportOtio = (dir, name) => engineCall("export.otio", { dir, name });
|
|
39
|
+
export const variantsList = (dir) => engineCall("variants.list", {
|
|
40
|
+
dir,
|
|
41
|
+
});
|
|
42
|
+
export const variantsCreate = (dir, source) => engineCall("variants.create", { dir, source });
|
|
43
|
+
export const variantsPromote = (dir, variant) => engineCall("variants.promote", { dir, variant });
|
|
44
|
+
export const retentionImport = (dir, csv, render, name) => engineCall("retention.import", { dir, name, csv, render });
|
|
45
|
+
export const retentionList = (dir) => engineCall("retention.list", { dir });
|
|
46
|
+
export const retentionProposals = (dir, name) => engineCall("retention.proposals", { dir, name });
|
|
47
|
+
export const retentionApply = (dir, name, drops, tag) => engineCall("retention.apply", { dir, name, ...(drops ? { drops } : {}) }, tag);
|
|
48
|
+
export const framingApproval = (settings) => (settings.project.framing_approval ?? settings.app.framing_approval ?? "review") === "auto" ? "auto" : "review";
|
|
49
|
+
export const framingLabel = (plan, auto) => plan?.approved
|
|
50
|
+
? plan.approval?.approver === "policy:auto" ? "Approved automatically" : "Framing approved"
|
|
51
|
+
: auto ? "Framing: automatic" : "Needs framing approval";
|
|
52
|
+
export const settingsGet = (dir) => engineCall("settings.get", dir ? { dir } : {});
|
|
53
|
+
/** Fired on `window` after a finishing setting is saved. `detail.dir` is the project it was saved
|
|
54
|
+
* to, or absent for an app-wide save, so a listener for one project can tell them apart. */
|
|
55
|
+
export const SETTINGS_CHANGED = "cutright:settings-changed";
|
|
56
|
+
const notifySettingsChanged = (dir) => window.dispatchEvent(new CustomEvent(SETTINGS_CHANGED, { detail: { dir } }));
|
|
57
|
+
/** `expectedRevision` (the `revision` `settings.get` reported) makes the write a compare-and-set:
|
|
58
|
+
* a file that changed since is refused with code `revision_conflict` and nothing is written. */
|
|
59
|
+
export const settingsSet = (settings, dir, expectedRevision) => engineCall("settings.set", {
|
|
60
|
+
...(dir ? { dir } : {}),
|
|
61
|
+
...(expectedRevision !== undefined ? { expected_revision: expectedRevision } : {}),
|
|
62
|
+
settings,
|
|
63
|
+
}).then((r) => {
|
|
64
|
+
notifySettingsChanged(dir);
|
|
65
|
+
return r;
|
|
66
|
+
});
|
|
67
|
+
/** Project grade plus per-source grades for the project at `dir`. */
|
|
68
|
+
export const colourGet = (args) => engineCall("colour.get", { ...args });
|
|
69
|
+
/** Omit `source` to write the project grade; `grade: null` clears a grade. */
|
|
70
|
+
export const colourSet = (args) => engineCall("colour.set", {
|
|
71
|
+
dir: args.dir,
|
|
72
|
+
...(args.source !== undefined ? { source: args.source } : {}),
|
|
73
|
+
grade: args.grade,
|
|
74
|
+
}).then((r) => {
|
|
75
|
+
notifySettingsChanged(args.dir);
|
|
76
|
+
return r;
|
|
77
|
+
});
|
|
78
|
+
export const colourScopes = (args) => engineCall("colour.scopes", { ...args });
|
|
79
|
+
export const storageStatus = (dir) => engineCall("storage.status", dir ? { dir } : {});
|
|
80
|
+
export const storageClear = (dir) => engineCall("storage.clear", dir ? { dir } : {});
|
|
81
|
+
export const modelsStatus = () => engineCall("models.status");
|
|
82
|
+
/** `models.install` — install or repair one signed-pack model (progress: `model.progress` events on `tag`). */
|
|
83
|
+
export const modelsInstall = (model, tag) => engineCall("models.install", { model }, tag);
|
|
84
|
+
/** R2: plans are per (source × aspect); `aspect` is "9:16" or "1:1". */
|
|
85
|
+
export const reframeAnalyze = (dir, source, aspect, tag) => engineCall("reframe.analyze", { dir, source, ...(aspect ? { aspect } : {}) }, tag);
|
|
86
|
+
export const reframeShow = (dir, source, aspect) => engineCall("reframe.show", {
|
|
87
|
+
dir,
|
|
88
|
+
source,
|
|
89
|
+
...(aspect ? { aspect } : {}),
|
|
90
|
+
});
|
|
91
|
+
export const reframeApprove = (dir, source, opts) => engineCall("reframe.approve", { dir, source, ...opts });
|
|
92
|
+
export const reframeRender = (dir, source, aspect, tag) => engineCall("reframe.render", { dir, source, ...(aspect ? { aspect } : {}) }, tag);
|
|
93
|
+
/** Caption looks the engine renders, with the labels the screens show. */
|
|
94
|
+
export const CAPTION_STYLES = [
|
|
95
|
+
["classic-box", "Classic box"],
|
|
96
|
+
["clean-outline", "Clean outline"],
|
|
97
|
+
["shorts-pop", "Shorts pop"],
|
|
98
|
+
["karaoke-pop", "Karaoke pop"],
|
|
99
|
+
["karaoke-fill", "Karaoke fill"],
|
|
100
|
+
["card-classic", "Card classic"],
|
|
101
|
+
["card-emphasis", "Card emphasis"],
|
|
102
|
+
["minimal-lower", "Minimal lower"],
|
|
103
|
+
];
|
|
104
|
+
/** The caption look used when none is chosen; matches the engine default (`captions_cmd.rs`). */
|
|
105
|
+
export const DEFAULT_CAPTION_STYLE = "classic-box";
|
|
106
|
+
export const captionsPreview = (dir, opts = {}) => engineCall("captions.preview", { dir, ...opts });
|
|
107
|
+
export const playbackOpen = (params) => invoke("playback_open", { params, toolsDir: null });
|
|
108
|
+
/** Rebuilds the open timeline preview with freshly fetched colour chains and restores the
|
|
109
|
+
* playhead. A grade change does not change the revision, so the revision-keyed reopen misses it.
|
|
110
|
+
* Resolves null when no timeline preview is open. */
|
|
111
|
+
/** Fired on `window` after the preview session was rebuilt; a paused player repaints on it. */
|
|
112
|
+
export const PLAYBACK_REFRESHED = "cutright:playback-refreshed";
|
|
113
|
+
export const playbackRefreshFinishing = (dir) => invoke("playback_refresh_finishing", { dir, toolsDir: null }).then((meta) => {
|
|
114
|
+
window.dispatchEvent(new Event(PLAYBACK_REFRESHED));
|
|
115
|
+
return meta;
|
|
116
|
+
});
|
|
117
|
+
export const playbackClose = () => invoke("playback_close");
|
|
118
|
+
export const playbackStatus = () => invoke("playback_status");
|
|
119
|
+
export const playbackControl = (cmd, value, value2) => invoke("playback_control", { cmd, value: value ?? 0, value2: value2 ?? 0 });
|
|
120
|
+
/** Binary frame reply: `[u32 w][u32 h][u32 frame][u32 total][f64 pts]` + RGBA8. */
|
|
121
|
+
export const playbackFrame = () => invoke("playback_frame");
|
|
122
|
+
/** Runtime asset-protocol scope grant (project dirs, referenced media). */
|
|
123
|
+
export const mediaScopeAllow = (paths, recursive) => invoke("media_scope_allow", { paths, recursive: recursive ?? null });
|
|
124
|
+
/** Frames of a source (id or file name) or a render (file name in renders/).
|
|
125
|
+
* Omit times for one representative frame (20% in). */
|
|
126
|
+
export const thumbnails = (dir, target, opts = {}) => engineCall("media.thumbnail", { dir, ...target, ...opts });
|
|
127
|
+
export const retentionShow = (dir, name) => engineCall("retention.show", { dir, name });
|
|
128
|
+
export const rendersDelete = (dir, name) => engineCall("renders.delete", { dir, name });
|
|
129
|
+
export const qaList = (dir) => engineCall("qa.list", { dir });
|
|
130
|
+
/** `render` is a bare file name in renders/. A QA refusal arrives as an error starting `qa_failed:`. */
|
|
131
|
+
export const packageBuild = (dir, params) => engineCall("package.build", { dir, ...params });
|
|
132
|
+
export const sourceProbe = (dir, source) => engineCall("source.probe", { dir, source });
|
|
133
|
+
export const recipeRun = (dir, opts, tag) => engineCall("recipe.run", { dir, ...opts }, tag);
|
|
134
|
+
export const recipeCancel = (job) => engineCall("recipe.cancel", { job });
|
|
135
|
+
export const recipeStatus = (dir) => engineCall("recipe.status", { dir });
|
|
136
|
+
export const captionsShow = (dir, preset) => engineCall("captions.show", {
|
|
137
|
+
dir,
|
|
138
|
+
...(preset ? { preset } : {}),
|
|
139
|
+
});
|
|
140
|
+
// Compatibility alias: editor lane imports `captionCues(dir)`.
|
|
141
|
+
export const captionCues = captionsShow;
|
|
142
|
+
export const editplanList = (dir) => engineCall("editplan.list", { dir });
|
|
143
|
+
// Compatibility alias for consumers that imported the camelCase name.
|
|
144
|
+
export const editPlanList = editplanList;
|
|
145
|
+
// ---- Channel recipes (recipes.*: app-wide library, per-project ref) ----
|
|
146
|
+
/** Engine id rule for library recipes: lowercase letters, digits and '-', 1..64 chars. */
|
|
147
|
+
export const RECIPE_ID_RULE = /^[a-z0-9-]{1,64}$/;
|
|
148
|
+
export const recipesList = () => engineCall("recipes.list", {});
|
|
149
|
+
export const recipesGet = (id) => engineCall("recipes.get", { id });
|
|
150
|
+
export const recipesSave = (args) => engineCall("recipes.save", { ...args });
|
|
151
|
+
export const recipesDelete = (id) => engineCall("recipes.delete", { id });
|
|
152
|
+
/** Saves this project's recipe settings to the library under `id`; a new version when `id` exists. */
|
|
153
|
+
export const recipesSaveFromProject = (args) => engineCall("recipes.save_from_project", { ...args });
|
|
154
|
+
/** Replaces this project's recipe settings with the library recipe `id`. */
|
|
155
|
+
export const recipesApply = (dir, id) => engineCall("recipes.apply", { dir, id });
|
|
156
|
+
export const recipesStatus = (dir) => engineCall("recipes.status", { dir });
|
|
157
|
+
export const batchCreate = (dir, outputs, id) =>
|
|
158
|
+
// The engine replies {status, created, id, batch}; callers want the batch document.
|
|
159
|
+
engineCall("batch.create", { dir, outputs, ...(id ? { id } : {}) }).then((r) => r.batch);
|
|
160
|
+
/**
|
|
161
|
+
* Long-running: pass `job` to cancel with `recipeCancel(job)`; progress arrives as `batch.output` on `tag`.
|
|
162
|
+
* `finishing: "current"` pins the batch to the project's current finishing settings and renders every output again.
|
|
163
|
+
*/
|
|
164
|
+
export const batchRun = (dir, id, job, tag, finishing) => engineCall("batch.run", { dir, id, ...(job ? { job } : {}), ...(finishing ? { finishing } : {}) }, tag);
|
|
165
|
+
export const batchStatus = (dir, id) => engineCall("batch.status", { dir, id });
|
|
166
|
+
export const batchList = (dir) => engineCall("batch.list", { dir });
|
|
167
|
+
/** Sets the cover frame of one output; the reply carries the updated fields of that output row. */
|
|
168
|
+
export const batchCover = (dir, id, key, at_ms) => engineCall("batch.cover", { dir, id, key, at_ms });
|
|
169
|
+
/** `tag` routes the `bundle.progress` frames (`{completed, total}`, write stage) to this call. */
|
|
170
|
+
export const bundleExport = (dir, params, tag) => engineCall("bundle.export", { dir, ...params }, tag);
|
|
171
|
+
export const bundleImport = (params) => engineCall("bundle.import", params);
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { MediaAdapters } from "@rightkit/media-ui";
|
|
2
|
+
/**
|
|
3
|
+
* the host app bindings for @rightkit/media-ui. Media the webview cannot decode is
|
|
4
|
+
* remuxed by `rightkit-playback` (`prepare_registered_media_playback`, registry shared with
|
|
5
|
+
* src-tauri/src/playback_host.rs); the player gets the derivative as an asset URL.
|
|
6
|
+
* The `id` is the registry-issued source id from `useMediaPlaybackId`, never a path.
|
|
7
|
+
*/
|
|
8
|
+
export declare const mediaAdapters: MediaAdapters;
|
|
9
|
+
/** Registry-issued id; RightKit's authoriser checks the host's scope on every platform. */
|
|
10
|
+
export declare const mediaSourceId: (path: string) => Promise<string>;
|
|
11
|
+
/**
|
|
12
|
+
* The `playbackId` for `path`, once the host has registered it. Undefined until then, and
|
|
13
|
+
* stays undefined when the file is outside the media scope (no compatibility copy, as before).
|
|
14
|
+
* Call sites key the player on this value so a late id remounts it cleanly.
|
|
15
|
+
*/
|
|
16
|
+
export declare function useMediaPlaybackId(path: string | null | undefined): string | undefined;
|
|
17
|
+
export declare const isPlayable: (path: string) => boolean;
|
|
18
|
+
export declare const mediaKind: (path: string) => "audio" | "video";
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { useEffect, useState } from "react";
|
|
2
|
+
import { convertFileSrc, invoke } from "@tauri-apps/api/core";
|
|
3
|
+
import { getCurrentWindow } from "@tauri-apps/api/window";
|
|
4
|
+
import { defaultToastBus } from "@rightkit/react-shell";
|
|
5
|
+
/**
|
|
6
|
+
* the host app bindings for @rightkit/media-ui. Media the webview cannot decode is
|
|
7
|
+
* remuxed by `rightkit-playback` (`prepare_registered_media_playback`, registry shared with
|
|
8
|
+
* src-tauri/src/playback_host.rs); the player gets the derivative as an asset URL.
|
|
9
|
+
* The `id` is the registry-issued source id from `useMediaPlaybackId`, never a path.
|
|
10
|
+
*/
|
|
11
|
+
export const mediaAdapters = {
|
|
12
|
+
resolveSrc: (source) => (/^[a-z][a-z0-9+.-]*:/i.test(source) ? source : convertFileSrc(source)),
|
|
13
|
+
preparePlayback: async ({ id, requestId }) => convertFileSrc(await invoke("prepare_registered_media_playback", { source: id, requestId })),
|
|
14
|
+
cancelPlayback: (requestId) => invoke("cancel_media_playback", { requestId }),
|
|
15
|
+
setFullscreen: (on) => getCurrentWindow().setFullscreen(on),
|
|
16
|
+
notify: (message, severity) => {
|
|
17
|
+
defaultToastBus.push(message, severity === "error" ? "error" : "info");
|
|
18
|
+
},
|
|
19
|
+
};
|
|
20
|
+
/** Registry-issued id; RightKit's authoriser checks the host's scope on every platform. */
|
|
21
|
+
export const mediaSourceId = (path) => invoke("authorised_media_source_id", { path });
|
|
22
|
+
/**
|
|
23
|
+
* The `playbackId` for `path`, once the host has registered it. Undefined until then, and
|
|
24
|
+
* stays undefined when the file is outside the media scope (no compatibility copy, as before).
|
|
25
|
+
* Call sites key the player on this value so a late id remounts it cleanly.
|
|
26
|
+
*/
|
|
27
|
+
export function useMediaPlaybackId(path) {
|
|
28
|
+
const [known, setKnown] = useState(null);
|
|
29
|
+
useEffect(() => {
|
|
30
|
+
if (!path)
|
|
31
|
+
return;
|
|
32
|
+
let live = true;
|
|
33
|
+
mediaSourceId(path).then((id) => {
|
|
34
|
+
if (live)
|
|
35
|
+
setKnown({ path, id });
|
|
36
|
+
}, () => undefined);
|
|
37
|
+
return () => {
|
|
38
|
+
live = false;
|
|
39
|
+
};
|
|
40
|
+
}, [path]);
|
|
41
|
+
return path && known?.path === path ? known.id : undefined;
|
|
42
|
+
}
|
|
43
|
+
const AUDIO_EXT = /\.(wav|mp3|m4a|flac|aac|ogg|opus)$/i;
|
|
44
|
+
const MEDIA_EXT = /\.(mp4|mov|mkv|webm|m4v|wav|mp3|m4a|flac|aac|ogg|opus)$/i;
|
|
45
|
+
export const isPlayable = (path) => MEDIA_EXT.test(path);
|
|
46
|
+
export const mediaKind = (path) => (AUDIO_EXT.test(path) ? "audio" : "video");
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/** `offset` is microphone source time minus camera source time, in ticks. */
|
|
2
|
+
export type MicAlignment = {
|
|
3
|
+
offset: number;
|
|
4
|
+
offset_ms: number;
|
|
5
|
+
lag_samples: number;
|
|
6
|
+
sample_rate: number;
|
|
7
|
+
/** 0..1 normalised correlation score. */
|
|
8
|
+
confidence: number;
|
|
9
|
+
confidence_milli: number;
|
|
10
|
+
overlap_samples: number;
|
|
11
|
+
};
|
|
12
|
+
export type MicAttachment = {
|
|
13
|
+
camera: string;
|
|
14
|
+
microphone: string;
|
|
15
|
+
offset: number;
|
|
16
|
+
sample_rate: number;
|
|
17
|
+
lag_samples: number;
|
|
18
|
+
confidence_milli: number;
|
|
19
|
+
};
|
|
20
|
+
export type MicAlignResult = {
|
|
21
|
+
/** `attached` on a new mapping; `already_attached` when the same mapping exists. */
|
|
22
|
+
status: "attached" | "already_attached";
|
|
23
|
+
alignment: MicAlignment;
|
|
24
|
+
attachment: MicAttachment;
|
|
25
|
+
revision: unknown;
|
|
26
|
+
};
|
|
27
|
+
export type MicAlignOptions = {
|
|
28
|
+
/** Search window in milliseconds, 1..=180000 (engine default 30000). */
|
|
29
|
+
maxOffsetMs?: number;
|
|
30
|
+
/** Fail with a cursor mismatch unless the project is at this cursor. */
|
|
31
|
+
expectCursor?: number;
|
|
32
|
+
};
|
|
33
|
+
/**
|
|
34
|
+
* Aligns the external microphone source to the camera source and records the
|
|
35
|
+
* source-level mapping. `camera` and `microphone` are source BLAKE3 ids or
|
|
36
|
+
* unambiguous source names. Long-running: pass a `tag` to correlate progress.
|
|
37
|
+
*/
|
|
38
|
+
export declare function micAlign(dir: string, camera: string, microphone: string, options?: MicAlignOptions, tag?: string): Promise<MicAlignResult>;
|
package/dist/lib/mic.js
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { engineCall } from "./engine.js";
|
|
2
|
+
/**
|
|
3
|
+
* Aligns the external microphone source to the camera source and records the
|
|
4
|
+
* source-level mapping. `camera` and `microphone` are source BLAKE3 ids or
|
|
5
|
+
* unambiguous source names. Long-running: pass a `tag` to correlate progress.
|
|
6
|
+
*/
|
|
7
|
+
export function micAlign(dir, camera, microphone, options = {}, tag) {
|
|
8
|
+
const params = { dir, camera, microphone };
|
|
9
|
+
if (options.maxOffsetMs !== undefined)
|
|
10
|
+
params.max_offset_ms = options.maxOffsetMs;
|
|
11
|
+
if (options.expectCursor !== undefined)
|
|
12
|
+
params.expect_cursor = options.expectCursor;
|
|
13
|
+
return engineCall("mic.align", params, tag);
|
|
14
|
+
}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
export type ModelKind = "image" | "video" | "voice" | "avatar";
|
|
2
|
+
/** Every the host app media kind a catalog row can carry (the eight generate kinds). */
|
|
3
|
+
export type CatalogKind = ModelKind | "music" | "sfx" | "upscale" | "lipsync";
|
|
4
|
+
export type ModelRow = {
|
|
5
|
+
provider: string;
|
|
6
|
+
model: string;
|
|
7
|
+
kind: string;
|
|
8
|
+
label: string;
|
|
9
|
+
estUsd: number | null;
|
|
10
|
+
/** A closed set of allowed durations (seconds). */
|
|
11
|
+
durationsS?: number[];
|
|
12
|
+
minDurationS?: number;
|
|
13
|
+
maxDurationS?: number;
|
|
14
|
+
defaultDurationS?: number;
|
|
15
|
+
};
|
|
16
|
+
export type ModelCatalog = {
|
|
17
|
+
models: ModelRow[];
|
|
18
|
+
structuredDurations: boolean;
|
|
19
|
+
};
|
|
20
|
+
export declare function parseCatalog(result: unknown): ModelCatalog;
|
|
21
|
+
export declare const ofKind: (c: ModelCatalog, kind: CatalogKind) => ModelRow[];
|
|
22
|
+
export declare const providersOf: (c: ModelCatalog, kind: CatalogKind) => string[];
|
|
23
|
+
export declare const findModel: (c: ModelCatalog | null, provider: string, model: string) => ModelRow | undefined;
|
|
24
|
+
export type DurationControl = {
|
|
25
|
+
type: "set";
|
|
26
|
+
options: number[];
|
|
27
|
+
initial: number;
|
|
28
|
+
} | {
|
|
29
|
+
type: "range";
|
|
30
|
+
min: number;
|
|
31
|
+
max: number;
|
|
32
|
+
initial: number;
|
|
33
|
+
} | {
|
|
34
|
+
type: "free";
|
|
35
|
+
initial: number;
|
|
36
|
+
};
|
|
37
|
+
/** What the duration control offers for `m`; the model's default is preselected. */
|
|
38
|
+
export declare function durationControl(m: ModelRow | undefined, fallback?: number): DurationControl;
|
|
39
|
+
/** Preferred model of a kind: the wanted one when listed, else the cheapest priced, else the first. */
|
|
40
|
+
export declare function pickModel(c: ModelCatalog, kind: CatalogKind, want?: {
|
|
41
|
+
provider: string;
|
|
42
|
+
model: string;
|
|
43
|
+
}): ModelRow | undefined;
|
|
44
|
+
export type SpendLine = {
|
|
45
|
+
label: string;
|
|
46
|
+
estUsd: number | null;
|
|
47
|
+
};
|
|
48
|
+
/** Sum of the known per-item estimates; `unpriced` counts items the host app has no estimate for. */
|
|
49
|
+
export declare function spendTotal(lines: SpendLine[]): {
|
|
50
|
+
total: number;
|
|
51
|
+
unpriced: number;
|
|
52
|
+
};
|
|
53
|
+
export declare const formatUsd: (n: number) => string;
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
// `genright.models` as the engine returns it (crates/cutright-engine/src/genright_cmd.rs `models`):
|
|
2
|
+
// {models:[{provider, model, kind, label, est_usd, durations_s?, min_duration_s?, max_duration_s?,
|
|
3
|
+
// default_duration_s?}], structured_durations}
|
|
4
|
+
// `est_usd` is the host app own per-job price estimate (null when unknown).
|
|
5
|
+
const isObj = (v) => typeof v === "object" && v !== null;
|
|
6
|
+
const str = (v) => (typeof v === "string" ? v : undefined);
|
|
7
|
+
const num = (v) => (typeof v === "number" && Number.isFinite(v) ? v : undefined);
|
|
8
|
+
export function parseCatalog(result) {
|
|
9
|
+
const r = isObj(result) ? result : {};
|
|
10
|
+
const rows = Array.isArray(r.models) ? r.models : [];
|
|
11
|
+
const models = [];
|
|
12
|
+
for (const raw of rows) {
|
|
13
|
+
if (!isObj(raw))
|
|
14
|
+
continue;
|
|
15
|
+
const provider = str(raw.provider);
|
|
16
|
+
const model = str(raw.model);
|
|
17
|
+
if (!provider || !model)
|
|
18
|
+
continue;
|
|
19
|
+
const set = Array.isArray(raw.durations_s)
|
|
20
|
+
? raw.durations_s.map(num).filter((n) => n !== undefined)
|
|
21
|
+
: [];
|
|
22
|
+
models.push({
|
|
23
|
+
provider,
|
|
24
|
+
model,
|
|
25
|
+
kind: str(raw.kind) ?? "",
|
|
26
|
+
label: str(raw.label) ?? model,
|
|
27
|
+
estUsd: num(raw.est_usd) ?? null,
|
|
28
|
+
durationsS: set.length > 0 ? [...set].sort((a, b) => a - b) : undefined,
|
|
29
|
+
minDurationS: num(raw.min_duration_s),
|
|
30
|
+
maxDurationS: num(raw.max_duration_s),
|
|
31
|
+
defaultDurationS: num(raw.default_duration_s),
|
|
32
|
+
});
|
|
33
|
+
}
|
|
34
|
+
return { models, structuredDurations: r.structured_durations === true };
|
|
35
|
+
}
|
|
36
|
+
export const ofKind = (c, kind) => c.models.filter((m) => m.kind === kind);
|
|
37
|
+
export const providersOf = (c, kind) => [...new Set(ofKind(c, kind).map((m) => m.provider))];
|
|
38
|
+
export const findModel = (c, provider, model) => c?.models.find((m) => m.provider === provider && m.model === model);
|
|
39
|
+
/** What the duration control offers for `m`; the model's default is preselected. */
|
|
40
|
+
export function durationControl(m, fallback = 4) {
|
|
41
|
+
if (m?.durationsS) {
|
|
42
|
+
const d = m.defaultDurationS;
|
|
43
|
+
return { type: "set", options: m.durationsS, initial: d !== undefined && m.durationsS.includes(d) ? d : m.durationsS[0] };
|
|
44
|
+
}
|
|
45
|
+
if (m?.minDurationS !== undefined && m.maxDurationS !== undefined) {
|
|
46
|
+
const d = m.defaultDurationS ?? m.minDurationS;
|
|
47
|
+
return { type: "range", min: m.minDurationS, max: m.maxDurationS, initial: Math.min(m.maxDurationS, Math.max(m.minDurationS, d)) };
|
|
48
|
+
}
|
|
49
|
+
return { type: "free", initial: m?.defaultDurationS ?? fallback };
|
|
50
|
+
}
|
|
51
|
+
/** Preferred model of a kind: the wanted one when listed, else the cheapest priced, else the first. */
|
|
52
|
+
export function pickModel(c, kind, want) {
|
|
53
|
+
const rows = ofKind(c, kind);
|
|
54
|
+
const exact = want && rows.find((m) => m.provider === want.provider && m.model === want.model);
|
|
55
|
+
if (exact)
|
|
56
|
+
return exact;
|
|
57
|
+
const priced = rows.filter((m) => m.estUsd !== null).sort((a, b) => (a.estUsd ?? 0) - (b.estUsd ?? 0));
|
|
58
|
+
return priced[0] ?? rows[0];
|
|
59
|
+
}
|
|
60
|
+
/** Sum of the known per-item estimates; `unpriced` counts items the host app has no estimate for. */
|
|
61
|
+
export function spendTotal(lines) {
|
|
62
|
+
let total = 0;
|
|
63
|
+
let unpriced = 0;
|
|
64
|
+
for (const l of lines) {
|
|
65
|
+
if (l.estUsd === null)
|
|
66
|
+
unpriced += 1;
|
|
67
|
+
else
|
|
68
|
+
total += l.estUsd;
|
|
69
|
+
}
|
|
70
|
+
return { total, unpriced };
|
|
71
|
+
}
|
|
72
|
+
export const formatUsd = (n) => `$${n.toFixed(n < 1 ? 3 : 2)}`;
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import { errorMessage } from "./engine.js";
|
|
2
|
+
import { modelsInstall, modelsStatus } from "./media.js";
|
|
3
|
+
const LABELS = {
|
|
4
|
+
speech: { label: "Speech model", description: "Transcribes talking-head footage for cuts and captions." },
|
|
5
|
+
face: { label: "Face model", description: "Tracks faces for vertical and square framing." },
|
|
6
|
+
};
|
|
7
|
+
function entry(id, row, installing) {
|
|
8
|
+
const state = installing || row?.in_progress
|
|
9
|
+
? "downloading"
|
|
10
|
+
: row?.ready || row?.state === "ready"
|
|
11
|
+
? "installed"
|
|
12
|
+
: row?.error && row.state !== "missing"
|
|
13
|
+
? "error"
|
|
14
|
+
: "missing";
|
|
15
|
+
return {
|
|
16
|
+
id,
|
|
17
|
+
...LABELS[id],
|
|
18
|
+
sizeBytes: row?.size_hint_bytes,
|
|
19
|
+
state,
|
|
20
|
+
// Signed packs live in the shared Orthic store and other apps use them.
|
|
21
|
+
shared: true,
|
|
22
|
+
error: state === "error" && row?.error ? { code: row.state ?? "error", message: row.error } : undefined,
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
/** RightKit models port over the engine's signed-pack store (`models.status` / `models.install`). */
|
|
26
|
+
export function createModelsBackend() {
|
|
27
|
+
const installing = new Set();
|
|
28
|
+
const listeners = new Set();
|
|
29
|
+
// The engine rejects with a {code, message} object; the kits show `Error.message`.
|
|
30
|
+
const asError = (e) => {
|
|
31
|
+
throw e instanceof Error ? e : new Error(errorMessage(e));
|
|
32
|
+
};
|
|
33
|
+
const list = async () => {
|
|
34
|
+
const s = await modelsStatus().catch(asError);
|
|
35
|
+
return Object.keys(LABELS).map((k) => entry(k, s[k], installing.has(k)));
|
|
36
|
+
};
|
|
37
|
+
const push = async () => {
|
|
38
|
+
const all = await list().catch(() => []);
|
|
39
|
+
for (const e of all)
|
|
40
|
+
for (const l of listeners)
|
|
41
|
+
l(e);
|
|
42
|
+
};
|
|
43
|
+
return {
|
|
44
|
+
list,
|
|
45
|
+
async install(id) {
|
|
46
|
+
const k = id;
|
|
47
|
+
installing.add(k);
|
|
48
|
+
void push();
|
|
49
|
+
try {
|
|
50
|
+
await modelsInstall(k).catch(asError);
|
|
51
|
+
}
|
|
52
|
+
finally {
|
|
53
|
+
installing.delete(k);
|
|
54
|
+
void push();
|
|
55
|
+
}
|
|
56
|
+
},
|
|
57
|
+
// The engine install is not resumable mid-file; cancel is a no-op the UI tolerates.
|
|
58
|
+
async cancel() { },
|
|
59
|
+
// settings-ui never offers Remove for shared entries; the port still requires the method.
|
|
60
|
+
async remove() {
|
|
61
|
+
throw new Error("Removing a signed model pack is not supported; it is shared with other Orthic Labs apps.");
|
|
62
|
+
},
|
|
63
|
+
async subscribe(handler) {
|
|
64
|
+
listeners.add(handler);
|
|
65
|
+
return () => listeners.delete(handler);
|
|
66
|
+
},
|
|
67
|
+
};
|
|
68
|
+
}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
export type EngineEvent = {
|
|
2
|
+
event?: string;
|
|
3
|
+
} & Record<string, unknown>;
|
|
4
|
+
export type EngineFrame = {
|
|
5
|
+
id: number | null;
|
|
6
|
+
tag: string | null;
|
|
7
|
+
event: EngineEvent;
|
|
8
|
+
};
|
|
9
|
+
export type JobView = {
|
|
10
|
+
/** request id (stable across retries). */
|
|
11
|
+
id: string;
|
|
12
|
+
label: string;
|
|
13
|
+
jobId?: string;
|
|
14
|
+
/** journaled | submitted */
|
|
15
|
+
request?: string;
|
|
16
|
+
status?: string;
|
|
17
|
+
stage?: string;
|
|
18
|
+
message?: string;
|
|
19
|
+
imported: boolean;
|
|
20
|
+
};
|
|
21
|
+
export type ProgressState = {
|
|
22
|
+
/** Correlation: frames are accepted only for this tag (then this engine frame id). */
|
|
23
|
+
tag: string | null;
|
|
24
|
+
frameId: number | null;
|
|
25
|
+
assembleId: string | null;
|
|
26
|
+
running: boolean;
|
|
27
|
+
resumed: boolean;
|
|
28
|
+
stages: string[];
|
|
29
|
+
jobs: Record<string, JobView>;
|
|
30
|
+
imports: number;
|
|
31
|
+
render: {
|
|
32
|
+
job?: string;
|
|
33
|
+
frame?: number;
|
|
34
|
+
out_time_ms?: number;
|
|
35
|
+
total_ms?: number;
|
|
36
|
+
} | null;
|
|
37
|
+
timeline: {
|
|
38
|
+
video_clips?: number;
|
|
39
|
+
titles?: number;
|
|
40
|
+
total_frames?: number;
|
|
41
|
+
} | null;
|
|
42
|
+
error: {
|
|
43
|
+
code?: string;
|
|
44
|
+
message: string;
|
|
45
|
+
} | null;
|
|
46
|
+
cancelled: boolean;
|
|
47
|
+
output: string | null;
|
|
48
|
+
done: boolean;
|
|
49
|
+
log: EngineEvent[];
|
|
50
|
+
};
|
|
51
|
+
export declare const initialProgress: ProgressState;
|
|
52
|
+
export declare const assembleRenderJob: (assembleId: string) => string;
|
|
53
|
+
/** Starts tracking one engine call. */
|
|
54
|
+
export declare function beginProgress(tag: string, assembleId: string | null): ProgressState;
|
|
55
|
+
export declare function reduceFrame(state: ProgressState, frame: EngineFrame): ProgressState;
|
|
56
|
+
/** Rebuilds what is known from an `assemble.status` result (after an app restart). */
|
|
57
|
+
export declare function seedFromStatus(state: ProgressState, status: {
|
|
58
|
+
assemble_id: string;
|
|
59
|
+
status: string;
|
|
60
|
+
stage: string;
|
|
61
|
+
error: {
|
|
62
|
+
code?: string;
|
|
63
|
+
message?: string;
|
|
64
|
+
} | null;
|
|
65
|
+
timeline_built: boolean;
|
|
66
|
+
render: {
|
|
67
|
+
output?: string;
|
|
68
|
+
} | null;
|
|
69
|
+
render_exists?: boolean;
|
|
70
|
+
requests: {
|
|
71
|
+
item: string;
|
|
72
|
+
request_id: string;
|
|
73
|
+
state: string | null;
|
|
74
|
+
job_id: string | null;
|
|
75
|
+
imported: boolean;
|
|
76
|
+
}[];
|
|
77
|
+
}): ProgressState;
|