openvibers/openvibe.studio README.md

README.md

main 5.6 KB · 110 lines · markdown

1# OpenVibe.Studio
2
3Photo and Light: two open-source creative apps served inside an OpenVibe account.
4
5**Live site:** https://openvibe.studio · **API docs:** https://openvibe.services · **For AI:** https://openvibe.studio/llms.txt
6**Run it:** `npm ci && npm run dev` (Node 22 or newer) · **Tests:** `npm test`
7
8- **Photo** is [PhotoCraft](https://github.com/storytold/photocraft), a layered photo editor.
9- **Light** is [LightCraft](https://github.com/storytold/lightcraft), a photo library and RAW developer.
10
11The apps are the work of their own authors and are used under their own licences (MIT OR
12Apache-2.0). We build them from pinned release tags, serve them under an OpenVibe sign-in, and
13keep the licence and attribution with every build. See [ATTRIBUTION.md](ATTRIBUTION.md) and
14[STATUS.json](STATUS.json).
15
16## What is in here
17
18 server/ the service: pages, the API, the adapters, the OpenVibe clients
19 public/js/ the browser side: one driver per app, and the layer around it
20 test/ the tests
21 scripts/ the bundle build and the first-frame smoke test
22 .github/workflows/ ci.yml (tests and the pinned security workflow) and bundle.yml (the builds)
23
24## Running it
25
26 npm ci
27 npm start # http://127.0.0.1:5155, the drill port is 15155
28
29 npm test # the tests
30 node scripts/smoke.js --app light --dir artifacts/light # the first-frame check
31
32The service serves the app bundles from `STUDIO_BUNDLE_ROOT` (by default `public/apps`). Without a
33bundle the app page says so instead of showing an empty frame.
34
35## The bundles
36
37A bundle is never kept in git. The `bundles` workflow checks out the pinned upstream tags, builds
38them (Rust stable, `wasm32-unknown-unknown`, Trunk 0.21.14 for PhotoCraft, `cargo xtask web` and
39wasm-bindgen 0.2.129 for LightCraft), runs the smoke test and publishes the result with a
40checksum file. The deploy fetches that artifact, checks the checksums and unpacks it into the
41bundle root.
42
43The smoke test is the gate: the build fails unless the app reaches its first frame in headless
44Chrome. A bundle that loads but never starts is worse than no bundle.
45
46PhotoCraft is built by us on purpose. The published web release archive does not start: its loader
47looks for the module under a path the archive does not have.
48
49## The layer around an app
50
51Every app sits in an iframe inside our own page, with the OpenVibe navbar outside it. One adapter
52per app keeps the same contract, so a base can be swapped:
53
54 open(file) open a file from the person's OpenVibe library
55 save() hand an export to the person's OpenVibe.Download drive
56 commands() the app's own commands
57
58- **LightCraft** speaks the documented host bridge (`lightcraft:open`, `lightcraft:cmd`,
59 `lightcraft:save`) to a same-origin parent, so files open and exports come back.
60- **PhotoCraft** has no scriptable API in the browser: a file is dropped on the app, and there is
61 no export the page can ask for. The app page says so.
62
63## Hand-off from Video and Cam
64
65`GET /edit?media=<OpenVibe.Video id>&from=video` and `GET /edit?pics=<Pics slug>&from=cam` are the signed-in
66hand-off into Studio. Studio asks the source service with the person's own Network token whether that person may
67read the object; a private object of someone else answers 404 exactly as a missing id does. A picture opens in
68Photo (or `app=light`) with its source named (`From OpenVibe.Cam`); the browser layer fetches the bytes through
69`/api/v1/studio/files/pics/:slug/bytes` and puts them into the running app. A video opens in **Trim and publish**: the preview is at
70`/api/v1/studio/media/:id/bytes`; two seconds fields and **Set start here** / **Set end here** take the time from the
71player, **Use this frame as the cover** records the frame, and **Publish on Video** sends the range, cover, title and
72visibility to Video's `/api/v1/video/studio/publish`. Video's existing cuts service re-encodes that range as a new
73copy (the original is unchanged); the page polls `/api/v1/studio/publish/:cut` until the copy is ready. A range under
74one second or outside the video is refused in plain words.
75
76Set `OV_VIDEO_URL` when Video is not at `https://openvibe.video` (the drill). The Studio routes are documented in
77`server/pages.js` (`/edit`) and `server/api.js` (`/media/:id/bytes`, `/publish`).
78
79## Honest limits
80
81- Heavy first load: both apps are native editors compiled to WebAssembly.
82- A phone with 4 GB of RAM can run out of memory on big files: a 4 GB Photoshop file used 2.7-3.0 GB.
83- Online only: neither app caches for offline use.
84- Cut (FilmCraft) is not part of Studio v0.
85
86## Licence
87
88The service is MIT licensed ([LICENSE](LICENSE)). The apps keep their own licences, and the
89attribution is in [ATTRIBUTION.md](ATTRIBUTION.md).
90
91<!-- versions:start -->
92- openvibe-contracts: v0.419.0
93- openvibe-sdk: v0.161.0
94- openvibe-shared: v4.241.0
95<!-- versions:end -->
96
97### Pics image tools
98
99`/image-tools?picture=<slug>` opens an edit of a Pics picture. The authenticated
100`POST /api/v1/studio/images/edit` API accepts `{picture, operation, prompt?}`;
101operation is `upscale`, `remove-background`, or `restyle`. It reads authorized Pics
102bytes and saves a separate private picture through Pics' re-encoding pipeline.
103
104Deployment requires `STUDIO_IMAGE_WORKER_URL`, an operator-controlled image worker
105with `POST /edit` accepting `{operation, prompt, image: {mime_type, base64}}` and
106returning JPEG, PNG or WebP bytes. No person token is sent to that worker; it receives
107only the selected picture. The worker is not supplied by this PR. Without it the
108capability endpoint reports unavailable, the UI disables submission and the API
109returns 503. Tests use an in-process fake; no model calls or spending occurred.
110