16 Commits

Author SHA1 Message Date
twotalesanimation 842a8fe036 feat(board): paste client feedback from WhatsApp
Deploy / deploy (push) Successful in 3m34s
"Paste feedback" on each board card takes a client's message as it arrived
and splits it into rounds ("FEEDBACK 113 DUE Oct 7 12:00 PM") and per-shot
notes, tolerating WhatsApp copy prefixes, bold markers, wrapped lines,
several codes per note and codes without the show prefix.

A review step shows each note's shot, the version the client was sent,
the player frame and a "move task to Changes" tick (on by default).
Saving creates a FeedbackRound, a player comment per note tagged
source=WHATSAPP (shown as "Client feedback") and a board line under the
round's due time; ticking the line off resolves the comment.

Quoted frames are read as comp frames: shot frameStart or the house 1001,
plus the slate (recorded, else 1 for pipeline previews). Version.duration
is never stored on live, so the length-based slate inference can't be used.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-07 17:31:34 +02:00
twotalesanimation 2817c7e815 fix(annotations): lock annotations to the picture at any player size
Deploy / deploy (push) Successful in 3m7s
Points were normalised to the whole player box, but the video is drawn
object-contain inside it, so any change in the player's shape (window or
panel resize, fullscreen, phone rotation, the annotation toolbar opening)
moved the picture under the marks.

The canvas now sits exactly over the displayed picture (same letterbox
measurement as AspectOverlay), so points are fractions of the image and
new saves are tagged version "2.0". Legacy "1.0" annotations are mapped
onto the picture at load time from their stored canvasWidth/canvasHeight
and the video's intrinsic size; no data migration. The save action now
accepts both versions.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-22 14:34:57 +02:00
twotalesanimation f6a7f0ef5a feat(dashboard): show what is blocked and how the farm is doing
The dashboard was a generic task-manager view of a studio that does not work
that way. Two of its five headline numbers counted Task.dueDate, which no
pipeline code writes - the only writer is a manual field on the task detail
page, so those cards read zero by construction. The rest were unscoped and
cumulative ("approved" across every project ever finished), the activity feed
was the notification table, and the shot queue sampled 15 rows by updatedAt,
a column the farm touches constantly. Nothing about failed exports, QC depth
or the render farm appeared at all, so a RENDER_FAILED was only visible if
someone thought to open the queue and filter for it.

Replaced with pipeline state, which the system maintains on its own:

- Blocked queue: the four failed statuses plus exports that stopped
  progressing, each with the reason read off the actual cause - the failing
  validation check, the QC note, the aerender error, or the machine that
  claimed it and went quiet.
- Farm strip: machines, queue depth by job type, oldest unclaimed job, and
  worker-version drift, since a worker's version decides which fixes it has.
- Five counters that can go down, scoped to live productions, each linking to
  the rows it counted.

The queue page ignored ?status=, so the counters had nowhere to link; it now
takes an initial filter from the URL. Stat cards are hidden from CLIENT users
- the old ones were generic version counts, these are internal pipeline state.

Thresholds live in SystemConfig (dashboard.qcStaleHours, .stalledJobHours) so
they can be tuned without a redeploy.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-10 05:40:40 +02:00
twotalesanimation caaa333f02 feat(pipeline): render-derived shot thumbnails, and bulk shot delete
Deploy / deploy (push) Failing after 19m11s
Two independent features landing together:

Generated shot thumbnails — a shot can now carry a thumbnail promoted
from a render export (Shot.generatedThumbnailUrl/generatedThumbnailFrom)
alongside the existing brief-supplied thumbnailUrl, which always takes
display precedence (lib/shot-thumbnail.ts) so a promoted frame can only
ever fill a gap, never overwrite the brief. Shows with
pipelineConfig.autoShotThumbnail promote automatically on every
finished export (lib/render-pipeline/preview.ts); promoteExportThumbnail/
clearGeneratedThumbnail (actions/shots.ts) are the manual equivalent for
everyone else. Worker-side seek logic for picking the promoted frame is
covered by RenderWorker/VFXReviewWorker.Tests/ThumbnailSeekTests.cs.

Bulk shot delete — the shot-status page's selection bar gets a "Bulk
Actions" dropdown (alongside the existing Change Due Date) with Delete
Shots. bulkDeleteShots() (actions/shots.ts) deletes one at a time rather
than in a single statement: exports_shotId_fkey is ON DELETE RESTRICT
by design, so a shot with renders or deliveries on record is protected
and gets skipped and reported by name rather than aborting the whole
selection or being silently destroyed along with its history.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-27 17:06:55 +02:00
twotalesanimation b151e8cc52 feat(pipeline): per-shot client naming alias for slates, overlays, and delivery
Deploy / deploy (push) Successful in 7m25s
Clients sometimes ship footage under a naming convention that doesn't
match our own shot codes. Adds an optional Shot.clientCode, editable on
the Slate tab: when set, it replaces shotCode only in what the client
actually sees — slate identity, overlay burn-in, and delivered EXR/MOV
file and folder names in Netflix packages — with our own version string
flatly appended (no "_cmp_TT_" studio jargon mixed in). Internal render
paths, EXR filenames on the SAN, comp names, and DB lookups are
untouched. Shots without a client code render identically to before.

Delivery packages denormalize clientCode at build time, same as the
existing shotCode/versionString snapshot, so a later shot edit can't
retroactively change already-built delivery history. The worker now
renames delivered EXR frames and the delivery MOV using the existing
frame-number parser (SequencePattern) so only the base name changes —
frame numbers and padding survive exactly. This is the one piece of the
change that could plausibly ship the wrong bytes, so it's covered by
new unit tests (renaming, padding, MOV rename, checksum-survives-rename,
and a graceful fallback for an unparseable frame number) rather than
resting on manual verification alone.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-26 00:38:12 +02:00
twotalesanimation 7caf41e17c Slates
Deploy / deploy (push) Successful in 6m17s
2026-08-07 07:34:10 +02:00
twotalesanimation 75d5149bea versioning added
Deploy / deploy (push) Successful in 3m12s
2026-06-25 22:19:22 +02:00
twotalesanimation 0274b63c1e moved high res uploads button
Deploy / deploy (push) Successful in 2m39s
2026-06-25 10:14:27 +02:00
twotalesanimation 8caf0f9bd1 CSV TC import
Deploy / deploy (push) Successful in 3m9s
2026-06-14 14:12:45 +02:00
twotalesanimation 4da80d29a9 CSV shot implementation
Deploy / deploy (push) Failing after 46s
2026-06-12 15:06:50 +02:00
twotalesanimation 3f0c5d1dbe new Approval Updates
Deploy / deploy (push) Failing after 3m34s
2026-06-11 12:30:28 +02:00
twotalesanimation e75a15132e Added status tracking & episode due dates
Deploy / deploy (push) Failing after 2m30s
2026-06-03 14:49:12 +02:00
twotalesanimation 3e3a8f7a26 multiple plates
Deploy / deploy (push) Successful in 2m32s
2026-05-21 19:16:03 +02:00
twotalesanimation d39b968dcb added custom grouping on project pages
Deploy / deploy (push) Successful in 2m26s
2026-05-20 22:43:29 +02:00
twotalesanimation bcea123112 Add original footage to shots function
Deploy / deploy (push) Has been cancelled
2026-05-20 11:02:33 +02:00
twotalesanimation 0fbe856dce Initial commit 2026-05-19 22:20:29 +02:00