mirror of
https://github.com/storytold/effectcraft.git
synced 2026-11-10 18:31:30 -06:00
G8: Plug-in Library: install, preview and update community WebAssembly effects (#2524)
* G8: plug-in library Community Plug-in Library for WebAssembly effect plug-ins (#376): one reviewed catalog (index.json), install / update / remove from Settings > Plug-in Library (also Effect > Plug-in Library...), library.* commands, start-up loading of installed packages with SHA-256 checks. Downloads go through the system curl (https, GitHub hosts), as trained models do. Preview before installing (library.preview): the release is downloaded, checked like an install, and rendered in the sandbox on a built-in sample frame at default settings (2 s at 12 fps) with a Before/After split, plus the effect's parameters, "Open on GitHub" and an optional author video link (catalog field preview.video). Authors need to do nothing for it. Hardening: a module that fails to load leaves the installed copy alone, Update All updates every package in one job, minApp also applies to an explicit version, curl ignores ~/.curlrc and URL globbing, unique temp files, percent-decoded file URLs. Plug-in effect slots grow from 64 to 256 with a clearer overflow error. Docs: plugins.md "Plug-in Library", publishing-plugins.md (author guide from zero to listed), gaps.md; examples/plugin-catalog is the starting content of the catalog repository (schema, validation workflow). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * contributors: credit iseemhlyfta * G8: plug-in library: doc comments on the library's functions Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * G8: plug-in catalog check: require one object with schema 1 and a packages array An empty file or several JSON values passed jq empty, and with SKIP_SCHEMA=1 nothing else checked the catalog's shape. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * G8: plug-in library: one preview texture set; catalog schema requires effects The preview kept a texture set per package version previewed (about 6 MB each) for the whole session; it now keeps one, replaced when another package is previewed. Releases in the catalog schema must list at least one effect id, as the author guide says. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * G8: plug-in catalog schema: effect ids follow the manifest's id rules Letters, digits, '.', '_' and '-', and not the reserved 'ec.' prefix, so library.missing can match them. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * G8: refuse Plug-in Library jobs while automation file access is confined Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * G8: split the Plug-in Library page into toolbar, card, links and actions Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * G8: show Plug-in Library toasts without a Roto job; cache preview frame checks Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * G8: log Plug-in Library packages that fail to load at start-up Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * G8: check an update before replacing a loaded package; Update All past failures - An update to a package that is already loaded is now compiled and its manifest checked (through the previewer, without registering it) before the new file is written and the old version's folder deleted. Before, a release with the right SHA-256 that didn't load replaced the working version and failed at the next start. - Package ids that differ only in case, or end in a dot, would share one folder on Windows and macOS: the catalog keeps the first of them, and ids can't end in a dot (schema too). - Update All: a package that can't be picked or installed no longer stops the others; the result names what was updated and what wasn't. - Downloads: at most 5 redirects, and one that ends off GitHub's hosts is refused, as the module docs say. - A job that panics no longer leaves the library busy; poisoned status locks are used, not dropped. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * G8: rustfmt the update test Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> Co-authored-by: FredApps <153173854+FredApps@users.noreply.github.com>
This commit is contained in:
co-authored by
Claude Opus 5.5
FredApps
parent
bc3b074ad9
commit
4e8398f8ca
@@ -854,6 +854,7 @@ impl Session {
|
||||
/// instances in the background.
|
||||
pub fn poll_roto(&mut self, auto: bool) -> bool {
|
||||
self.poll_models();
|
||||
self.poll_library();
|
||||
let changed = self.poll_roto_job();
|
||||
// Edited instances stay pending while the model they will segment with is still loading.
|
||||
let loading = self.roto_model_loading();
|
||||
|
||||
@@ -176,6 +176,7 @@ pub fn command_specs() -> &'static [CommandSpec] {
|
||||
let mut v = Vec::new();
|
||||
v.extend(file::specs());
|
||||
v.extend(crate::models::specs());
|
||||
v.extend(crate::library::specs());
|
||||
v.extend(edit::specs());
|
||||
v.extend(comp::specs());
|
||||
v.extend(layer::specs());
|
||||
|
||||
@@ -78,6 +78,7 @@ pub use settings::config;
|
||||
pub use settings::prefs;
|
||||
pub use settings::shortcuts;
|
||||
pub use system::guard;
|
||||
pub use system::library;
|
||||
pub use system::logging;
|
||||
pub use system::perf;
|
||||
pub use system::storage;
|
||||
@@ -199,6 +200,13 @@ pub struct Session {
|
||||
/// Loads WebAssembly effect plug-ins (set by the host that links `effectcraft-plugin` with
|
||||
/// its runtime): `effect.plugins.load`.
|
||||
pub plugin_loader: Option<PluginLoader>,
|
||||
/// Renders a module without registering it (`library.preview`; set with `plugin_loader`).
|
||||
pub plugin_previewer: Option<PluginPreviewer>,
|
||||
/// Where effect plug-ins live (hosts set it; else `Plug-ins` next to the settings): the
|
||||
/// Plug-in Library installs into its `Library` folder ([`library`]).
|
||||
pub plugins_dir: Option<std::path::PathBuf>,
|
||||
/// The Plug-in Library's catalog, worker status and the packages this session loaded.
|
||||
pub library: library::Library,
|
||||
/// ScriptUI windows and panels opened by scripts (see [`scriptui`]).
|
||||
pub script_ui: scriptui::ScriptUi,
|
||||
/// The last physical-memory reading (Settings ▸ Memory & CPU budgets); `None` = unknown
|
||||
@@ -275,6 +283,11 @@ pub type ScriptRunner = fn(&mut Session, &ScriptRequest) -> Value;
|
||||
/// describes it (`{id, name, category, version, api, params}`).
|
||||
pub type PluginLoader = fn(&[u8], &str) -> std::result::Result<Value, String>;
|
||||
|
||||
/// Renders one WebAssembly effect module without registering it (the Plug-in Library preview):
|
||||
/// `(bytes, width, height, premultiplied RGBA pixels, times in seconds)` → (manifest JSON, one frame
|
||||
/// per time).
|
||||
pub type PluginPreviewer = fn(&[u8], u32, u32, &[[f32; 4]], &[f64]) -> std::result::Result<(Value, Vec<Vec<[f32; 4]>>), String>;
|
||||
|
||||
impl Default for Session {
|
||||
/// A new unsaved session: a default project that is both current and saved, no path, no
|
||||
/// active jobs, the plain `FsServices`/`NoFootage` defaults, default preferences and keymaps,
|
||||
@@ -330,6 +343,9 @@ impl Default for Session {
|
||||
snapshot: None,
|
||||
script: None,
|
||||
plugin_loader: None,
|
||||
plugin_previewer: None,
|
||||
plugins_dir: None,
|
||||
library: Default::default(),
|
||||
script_ui: scriptui::ScriptUi::default(),
|
||||
sys_memory: None,
|
||||
key_move: None,
|
||||
|
||||
@@ -1015,6 +1015,7 @@ Effect
|
||||
---
|
||||
Manage Effects... | effect.manage
|
||||
Load Effect Plug-in... | effect.plugins.load
|
||||
Plug-in Library... | prefs.open {"page":"library"}
|
||||
---
|
||||
@effects
|
||||
Animation
|
||||
|
||||
@@ -53,6 +53,9 @@ pub enum Item {
|
||||
/// A task's trained models (`roto.models`, `face.models`): authors, licence, size, install,
|
||||
/// choose.
|
||||
Models(effectcraft_segment::Task),
|
||||
/// The Plug-in Library (`library.*`): the community catalog's packages, installed ones,
|
||||
/// install / update / remove.
|
||||
PluginLibrary,
|
||||
}
|
||||
|
||||
/// One page of the Settings dialog.
|
||||
@@ -371,6 +374,17 @@ pub fn pages() -> Vec<Page> {
|
||||
Models(effectcraft_segment::Task::Face),
|
||||
],
|
||||
},
|
||||
Page {
|
||||
id: "library",
|
||||
title: "Plug-in Library",
|
||||
items: vec![
|
||||
Section("Community Effect Plug-ins"),
|
||||
Note(
|
||||
"Effect plug-ins published by the community and reviewed into one catalog. They are WebAssembly modules that run in a sandbox (no files, network or clock), downloaded only when you ask and checked against the catalog's SHA-256.",
|
||||
),
|
||||
PluginLibrary,
|
||||
],
|
||||
},
|
||||
]
|
||||
}
|
||||
|
||||
@@ -423,6 +437,7 @@ pub fn pages_json() -> Value {
|
||||
Item::AudioDevices { key } => json!({"type": "device", "key": key, "label": "Default Output"}),
|
||||
Item::Note(t) => json!({"note": t}),
|
||||
Item::BrowserStorage => json!({"browserStorage": "storage.info / storage.persist / storage.clear (web app)"}),
|
||||
Item::PluginLibrary => json!({"pluginLibrary": "library.list / library.refresh / library.install / library.update / library.uninstall"}),
|
||||
Item::Models(task) => {
|
||||
let p = if *task == effectcraft_segment::Task::Face { "face" } else { "roto" };
|
||||
json!({"models": format!("{p}.models / {p}.model.select / {p}.model.download / {p}.model.install / {p}.model.remove"), "key": format!("{p}.model")})
|
||||
|
||||
@@ -302,6 +302,7 @@ pub fn page_id(name: &str) -> Option<&'static str> {
|
||||
"scripting" | "scriptingexpressions" => "scripting",
|
||||
"roto" | "rotobrush" | "models" => "roto",
|
||||
"face" | "facetracking" => "face",
|
||||
"library" | "pluginlibrary" | "plugins" | "plugin" => "library",
|
||||
_ => return None,
|
||||
};
|
||||
Some(id)
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
pub mod guard;
|
||||
pub mod library;
|
||||
pub mod logging;
|
||||
pub mod perf;
|
||||
pub mod storage;
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user