Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ Most editors keep a project in a format built for their own app, not for you or
- **Timeline editor**: a main track of clips that play end to end, text on overlay tracks (colour, box and outline, for titles and captions alike), music on audio tracks. Trim, split, reorder and zoom, with filmstrips and waveforms.
- **Live preview**: one master clock plays the cut back in the browser as you edit.
- **Footage from a Drive folder**: start a project from a Google Drive folder shared with the link, and every video in it and in the folders inside it comes in as that project's own footage. Each clip is logged as it lands: what it shows, the best lines said with their times, the best moments to cut away to, and anything readable on screen. It runs in the background, so an agent can read a whole shoot's logs and choose what goes in.
- **Highlights**: every logged clip is read against what the video is for. You get the soundbites and the b-roll worth an editor's look, each scored with the reason why, and the clips not worth anyone's time set aside with theirs. Review them on one page where each pick plays from its in to its out, keep or drop with a key, and export what you keep as a timeline for Premiere Pro or DaVinci Resolve (the picks end to end on the camera files, nothing re-encoded) or as a sheet. Ask for a change uses them too: "make a 60 second cut from the kept soundbites".
- **Highlights**: every logged clip is read against what the video is for. You get the soundbites and the b-roll worth an editor's look, each scored with the reason why, and the clips not worth anyone's time set aside with theirs. Say how many you want, and once every clip is read the best that many are chosen from all of them, as a set: the mix the brief asks for, never the same moment twice, and when you drop one the next comes in. Review them on one page where each pick plays from its in to its out, keep or drop with a key, and export what you keep as a timeline for Premiere Pro or DaVinci Resolve (the picks end to end on the camera files, nothing re-encoded) or as a sheet. Ask for a change uses them too: "make a 60 second cut from the kept soundbites".
- **Media library**: upload clips, stills and music, or import them from Google Drive, and use them in any project. Videos go straight to the managed media service, resumably and up to 30 GB, so a multi-gigabyte master plays, gets a transcript and exports without passing through the app. Each upload shows its progress, and one that drops picks up where it stopped.
- **Formats**: 16:9, 9:16 for Reels, TikTok and Shorts, 1:1, 4:5 and 4:3, or the shape of your own footage. Titles and logos keep their size and place when the shape changes, and clips either fit with bars or fill the frame.
- **Crop**: keep part of any clip, logo or picture-in-picture, free or held to 16:9, 9:16, 1:1, 4:5 or the video's own shape, with a scrubber to check the subject stays inside. The preview places a cropped clip exactly as the export does.
Expand Down
24 changes: 20 additions & 4 deletions agent.md
Original file line number Diff line number Diff line change
Expand Up @@ -114,16 +114,31 @@ and the reason. They are read from the logs and transcripts in the background,
a folder at a time, and clips logged later join by themselves.

```
POST /api/projects/{id}/highlights { "brief"?: "…", "again"?: true }
POST /api/projects/{id}/highlights { "brief"?: "…", "again"?: true, "wanted"?: 25 }
PUT /api/projects/{id}/highlights/wanted { "wanted": 25 | null } how many; nothing is read again
POST /api/projects/{id}/highlights/stop stop reading; picks so far stay, asking again resumes
GET /api/projects/{id}/highlights best first, 100 a page (limit up to 1000, offset)
&kind=soundbite|broll &min_score=2..5 &folder=Day 1/Cam B
&pick=open|keep|drop|not_dropped|all &sort=score|clip &skipped=1
&pick=best|open|keep|drop|not_dropped|all &sort=score|clip &skipped=1
PATCH /api/projects/{id}/highlights/{hid} { "pick"?: "keep"|"drop"|null, "start"?, "end"?, "speaker"?, "text"? }
POST /api/projects/{id}/highlights/items { "clip": "<clipId>", "start"?, "end"?, "kind"?, "text"? }
GET /api/projects/{id}/highlights/export?format=csv|xml&pick=keep|not_dropped&root=&fps=
GET /api/projects/{id}/highlights/export?format=csv|xml&pick=best|keep|not_dropped&root=&fps=
```

`wanted` is how many highlights the video wants (1 to 200; null, the default:
every pick worth a look). Write the number there, never in the brief: each
reading sees a dozen clips, so a count in the brief only holds for each dozen.
With a number, every clip is still read, then one more model call goes over
all the picks and puts them in order for the brief: the best set of that many
first (the mix the brief asks for, one clip may give most of it, never the
same moment twice), then stand-ins. `pick=best` lists that set: kept picks
first (they count toward the number; when more are kept than wanted, the best
of them), then the model's order. Dropping one brings in the next with no new
call. A new number orders them again; picks from clips read later do too, at
most every 15 minutes while footage is still coming in. The list says where it
stands: `wanted`, `counts.best`, and `ranking: { status: waiting | running |
done | failed, error, due }`.

`again` reads every clip again; what a person kept or dropped stays as it is,
and each reading learns from those calls (the latest 15 kept and 15 dropped)
beside the brief. A `PATCH` with `pick` is how you teach it.
Expand Down Expand Up @@ -213,7 +228,8 @@ in line, and asks Drive again now for the clips waiting on it. `DELETE /api/proj
| POST | `/api/projects/{id}/footage/folders` | `{ url, language? }` adds a Drive folder shared with the link |
| POST | `/api/projects/{id}/footage/sync` | Takes in files added to the folders since → `{ added }` |
| POST | `/api/projects/{id}/footage/retry` | Puts failed imports and logs back in line → `{ imports, logs }` |
| POST | `/api/projects/{id}/highlights` | Find the footage's highlights `{ brief?, again? }` (in the background) |
| POST | `/api/projects/{id}/highlights` | Find the footage's highlights `{ brief?, again?, wanted? }` (in the background) |
| PUT | `/api/projects/{id}/highlights/wanted` | How many highlights `{ wanted: 1..200 \| null }`: the best that many are chosen from all the picks |
| POST | `/api/projects/{id}/highlights/stop` | Stop reading: clips in line go back, later clips wait until asked again |
| GET | `/api/projects/{id}/highlights` | The highlights, best first, filtered and paged → `{ clips, counts, highlights, next_offset, paused }` |
| PATCH | `/api/projects/{id}/highlights/{hid}` | A person's call `{ pick?: keep\|drop\|null, start?, end?, speaker?, text? }` |
Expand Down
13 changes: 13 additions & 0 deletions schema.sql
Original file line number Diff line number Diff line change
Expand Up @@ -230,3 +230,16 @@ ALTER TABLE project_footage ADD COLUMN upload_url TEXT;
ALTER TABLE project_footage ADD COLUMN upload_expires TEXT;
ALTER TABLE project_footage ADD COLUMN upload_size INTEGER;
ALTER TABLE project_footage ADD COLUMN upload_done INTEGER;

-- How many highlights the project wants; NULL: every pick worth a look. Once
-- every clip is read, one more model call puts the picks in order for the
-- brief, the best set of that many first (src/server/highlights.ts). The
-- ordering: NULL | waiting | running | done | failed, with the error, and
-- when it was last claimed (a claim older than five minutes is taken over).
ALTER TABLE edit_projects ADD COLUMN highlights_wanted INTEGER;
ALTER TABLE edit_projects ADD COLUMN highlights_rank_status TEXT;
ALTER TABLE edit_projects ADD COLUMN highlights_rank_error TEXT;
ALTER TABLE edit_projects ADD COLUMN highlights_rank_at TEXT;
-- A pick's place in that order, 1 the best. The best are the kept picks,
-- then the first in this order; NULL: not placed yet.
ALTER TABLE footage_highlights ADD COLUMN rank INTEGER;
Loading
Loading