- Removed unused logo and icon assets left over from earlier branding, including the ones the README pointed at (#58)
- The Save side of the delivery card is now called Export, and an export or an upload already under way can be stopped: a Stop button stands beside it while one runs, and pressing it hands the card back so an accidentally long selection can be adjusted and sent again (#56)
- Browse EMBER no longer flashes a list of every dataset in the archive before cutting it down to the ones that hold video: rows now appear as the scan confirms each dataset, and the list says it is still looking until the first one lands (#52)
- The page now shows one step at a time: the source picker gives way to the player and the Save/Upload card once a video opens, the pose card gives way to the file it read, and Change video / Change pose file put each picker back (#51)
- The app's guidance copy now names the actions it is talking about: the empty stage reads "Load a video to extract a clip", and the caption shown while a snippet still needs a description names saving and uploading instead of "sending it on" (#50)
- The dropzone no longer offers Load the sample (mice): it fetched the file from a sibling site the app has not been served next to since it moved to its own domain, so the button only ever failed; Browse EMBER is the way to open a video without one of your own (#50)
- A source video that carries sound is now copied into
sourcedata/under BEP047's_audiovideosuffix instead of_video, with its codec, sample rate, channel count and bit depth recorded in its sidecar; the extracted clip drops the audio as before and keeps the plain_videoname (#47)
- A video now opens on the first frame of the snippet it marks out, instead of leaving the playhead back at the start of the recording a fifth of the track away from the band; "Reset range" puts it back there too (#45)
- The sidecar written beside an extracted clip now reports its pixel format, bit depth and full codec string, which until now only the copied-along source video's sidecar carried (#44)
- Browse EMBER now highlights the video file you picked, the way it already highlights the open dataset, and a video opens with a snippet already marked out over the middle of the trim track instead of with nothing selected (#43)
- Save and Upload now write a BIDS-Study-shaped tree instead of a single ad hoc folder: the source video under
sourcedata/rawbids/, the extracted clip or frame and anything rendered from it underderivatives/clip-extractor/, each named per BEP047 and described by its own sidecar anddataset_description.json(#41)
- Fixed Browse EMBER showing embargoed datasets nobody owns while leaving out most of the actually-public ones; which datasets are public is now settled against the archive's own API instead of guessed from whether their file listing could be read off the public bucket, which turned out not to track embargo status at all (#40)
- A dataset's file list that fails to load during the archive scan is now retried a couple of times before being treated as unreadable, so a transient network hiccup does not permanently hide it (#40)
- A video whose container and size looked fine but whose network request or decoder never answered used to leave the player loading forever; it now gives up after 30 seconds with a message pointing at the likely codec or connection problem (#36)
- The Show overlay control is now a switch in the top right of the player card, beside the picture it draws on, and it only appears while the pose annotations step is switched on (#33)
- The Show overlay switch is now carried in the address bar, so a link to a streamed recording opens with the overlay the way it was left, including when the pose file is sent along by hand (#33)
- The address bar now carries the streamed video, the pose file, the marks and the description, so reloading the tab or sending somebody the link opens on the same clip instead of an empty page (#32)
- Signing in with EMBER no longer costs you what was on screen: the session is held across the round trip to the archive and put back on the way in (#32)
- A link naming an embargoed archive asset now opens for a signed-in visitor, by asking EMBER for the same signed link the browse pane asks for (#32)
- The header now carries a one-line description of what the app is for, under the title (#31)
- The Save/Upload toggle is only shown once you are signed in with EMBER, since signed out there is nowhere to upload to; signing out falls back to Save and signing back in returns to the side you had picked (#31)
- The browse pane's scrolling lists now carry a slim, rounded, accent-tinted scrollbar instead of the platform's gray slab with stepper arrows, matching the tables on the DANDI usage page (#30)
- A remote video in a container that cannot be streamed, such as an
.aviover 1 GB, is now refused with an explanation instead of being downloaded whole into a tab that cannot hold it (#29) - The browse pane marks such a file
no streamingin the listing, so it reads as one before it is picked (#29) - A URL that cannot be streamed for any other reason is no longer downloaded past 1 GB either, whatever its container (#29)
- A refused video now points at the Encoding Helper, as a link rather than a bare URL, instead of describing a conversion to make by hand (#29)
- Only the app's own links are made clickable in a message, so a crafted
?url=cannot put a link to somewhere else on the page (#29) - A refusal now reads as three centered lines — the file, what is wrong with it, and the Encoding Helper as a named link — rather than as one paragraph (#29)
- A remote recording over 1 GB whose container does not record where its frames are is now refused as soon as its header says so, rather than looking for them by reading the file itself — which for a 300 GB
.mkvwas hours of a byte count going up with nothing on the stage (#29)
- A video that will not open now says so in one place — the browse pane for a video picked out of it, the stage for a URL or a dropped file — and each attempt clears the last one's message, so two refusals are never read side by side (#29)
- Dropped the play triangle from each video row in the browse pane, where it read as a dropdown that would expand rather than as the row that opens the video (#28)
- The dataset picker's admin-owner check no longer sends your EMBER sign-in token to the companion admin-check service; that service now answers from its own archive credentials, so your token only ever goes to EMBER itself (#27)
- A new Browse EMBER source pane finds an existing video in the archive and streams it, so a clip can be pulled out of an archived recording without first knowing its URL (#26)
- Public datasets are read from EMBER's public S3 manifests rather than its API, so browsing what is public needs no sign-in and no token (#26)
- Signing in adds the embargoed datasets you are listed as an owner of to the same list, marked
embargoed, and opens a video in one through a signed link the archive hands out on demand (#26) - Each video source is now marked by an icon on the source picker, and the pose annotations switch names the formats it accepts on their own line (#26)
- The pane lists only the datasets that actually hold video, so nothing offered in it is a dead end (#26)
- Sizes now scale into GB and TB, so a 336 GB archived recording reads as that rather than as six figures of megabytes — in the browse pane and every other readout that shows a size (#26)
- The filter box matches a dataset by its number, its title or the name of a file inside it, and public titles are cached between visits so it works straight away on a return (#26)
- A video chosen from the archive records the archive's own asset URL as its source, rather than the content-addressed bucket URL its frames are actually read from (#26)
- Renamed the URL pane from Stream from EMBER to Stream from a URL, which is what it has always accepted (#26)
- Added
src/lib/archives.ts(the bucket listing, dataset index and video assets),src/lib/embargoed.ts(the API side of the pane) andsrc/lib/archiveNames.ts(the cached dataset titles), with unit and integration coverage (#26)
- The player now says when it is waiting on video rather than sitting unchanged until the picture arrives: opening a recording names it on the stage and counts the index off as it streams in, and a seek that has to wait on a frame raises a spinner over the picture (#25)
- A URL that cannot be range-read is still downloaded whole, but that download now counts itself off against the file's size instead of showing nothing at all until it finishes (#25)
- A video that could not be loaded now says so on the stage, which previously went back to inviting a file with the reason left in the browser console (#25)
- Added
src/ui/stageStatus.ts, holding the timing that keeps the indicator off screen for a wait that ends quickly and up for a run of slow ones, with unit and integration coverage (#25)
- A recording longer than half an hour is now trimmed against half an hour either side of where you are looking, moved by a new slider under the timeline spanning the whole recording, rather than one track across the entire thing where a single pixel of travel covers a minute and a half of video (#24)
- Sliding that window carries the playhead and the snippet with it, so a range of the right length can be pushed across a recording to find the moment it belongs to instead of being left behind wherever it was marked (#24)
- Dragging one trim marker on a windowed timeline now sets the other at the edge of what the timeline covers rather than at the end of the recording, so a first drag marks a snippet of the window instead of one running from there to the end of the day (#24)
- How much of a long recording the timeline covers is now set beside the transport, from five minutes either side to thirty, and the choice is remembered between visits (#24)
- The transport row now carries that width control at its left end and the speed control at its right, labelled and set either side of the play buttons (#24)
- Dropped the
[ ] I Oshortcuts that marked a snippet end at the playhead; either end is set by dragging its marker, arrowing it once focused, or typing a frame index into the readout (#24) - A trim marker whose frame is outside the stretch the timeline covers now flattens against the edge it left through instead of vanishing, and stays draggable so a snippet can still be started from it (#24)
- Added
src/lib/timeline.ts, holding the arithmetic for which stretch of a recording the timeline covers and how it is divided, with unit coverage (#24)
- Dropped two hints from the cards: the line under the Stream box describing what it does, and the note saying a streamed original is not sent back with the selection (#23)
- No snippet this app writes carries audio any more. Every route but one already dropped it; a fast trim stream-copied the source's tracks straight through, so a recording being de-identified could send voices to the archive in a track nobody was shown (#23)
- Extracting a selection from a streamed video no longer downloads the whole recording first: it is cut straight out of the stream, so a five second clip an hour into a 10.6 GB file reads about 1.5 MB rather than all 10.6 GB, which never finished (#23)
- A video named by URL now streams over range requests instead of being downloaded whole before it can play: a 10.6 GB recording on the EMBER bucket opens after about 71 MB (#23)
- A long recording no longer locks the tab up while it opens: the frame index is taken from the rate the container records rather than by walking every packet, which for a 16-hour file meant 1.76 million of them with nothing yielding to the browser in between (#23)
- The timeline ruler now carries a recording that runs for hours, with gradations up to twelve-hourly and an hours field in its labels, instead of hundreds of ticks under a smear of overlapping ones (#23)
- A seek into a stretch the read-ahead is still decoding is now served as soon as that frame lands rather than after the whole window, so scrubbing over fresh video no longer feels stuck (#23)
- Opening a second video now releases the first, whose decoded frames and in-flight reads were left behind (#23)
- Opening a long recording now reports how much of its index has been read, rather than looking stalled while it is (#23)
- Added
src/lib/streaming.ts, a frame-indexed video backend built on mediabunny directly, with sleap-io.js's own backends kept as the fallbacks (#23) - A streamed selection is trimmed by mediabunny rather than ffmpeg.wasm, which copies the frames over untouched when the cut may start on a key frame and re-encodes when it must be frame-exact or carry a blur (#23)
- Added
mediabunnyas a direct dependency, at the cost of a dependency entry rather than download weight, since sleap-io.js already brings it into the bundle (#23) - A file whose frames do not sit at the rate its header records is still indexed packet by packet, and that walk now yields to the event loop as it goes (#23)
- A video backend may now offer a
close(), which the streaming one uses to drop its frames and cancel its reads (#23) - Moved the ruler's gradation and label arithmetic into
lib/format.ts, where it is unit tested (#23) - Added unit coverage for the new backend against a stand-in for mediabunny, above all that a constant-rate file is opened without walking a single packet (#23)
- The snippet encode's progress now measures the snippet rather than the recording it was cut out of, so the bar spans the encode instead of creeping to a fraction of itself and then jumping to full (#22)
- A frame-exact cut decodes the source up to the selection before it can encode anything, and the status line now names that step rather than holding the bar at 0% through it (#22)
- The rendered pose overlay's encode reports progress at all, having sat at 0% from the first frame to the last (#22)
- The upload pane now raises the same red human-subjects banner as bbqs-uploader when the selected dataset's draft description carries the
CONTAINS HUMAN SUBJECTSmarker, holding the Upload button until the de-identification and IRB notice is confirmed (#20) - That warning brings out a blur tool under the player: circular areas dragged over a face or anything else identifying, each a focusable ring that can be nudged, resized and removed from the keyboard as well as from the buttons beside the controls (#20)
- The blur is burned into everything a delivery writes rather than only the preview: the extracted frame, every frame of the rendered pose overlay, and the snippet, which any blur forces to be re-encoded (#20)
- The original content no longer travels with a blurred selection, since it still holds the pixels that were covered, and the switch offering it comes back once the areas are cleared (#20)
- The provenance record now names each blurred area's centre, radius and strength, so a reader can see which parts of the frame carry no data (#20)
- The player no longer stretches a video whose frame is taller than the room there is for it: the frame is letterboxed at its own proportions, and the blur rings and the pointer that places them are mapped through the same fit (#20)
- Added unit coverage for the blur's canvas painting against a recording stand-in for a 2D context, jsdom having no canvas of its own (#20)
- The annotations card now takes an ndx-pose
.nwbas well as a SLEAP.slp, reading bothPoseEstimationpredictions andPoseTrainingannotations into the same pose model, so the overlay, the sidecar and the provenance record treat the two formats alike (#19) - An
.nwb's pose series is measured off its data array, a predictions file having no recorded frame count: more samples than the video has frames refuses the pair, and fewer says on the card how much of the video the file covers (#19) - A dense
.nwbseries is re-indexed by sample order when its real-seconds timestamps were read as spread-out frame indices, which happens below about 1 fps (#19) - Card notices now name the format in hand rather than always saying
.slp, and?pose=joins?slp=as the URL parameter for either format (#19)
- Added
h5wasmas a direct dependency for the shallow HDF5 walk that measures the pose series, at the cost of a dependency entry rather than download weight, since sleap-io.js already brings it into the bundle (#19) - Moved
@talmolab/sleap-io.jsto^0.5.9, the first release exporting the NWB readers (#19) - Added
tests/fixtures/make_nwb_fixtures.py, which regenerates the committed.nwbfixtures from a minimal hand-built ndx-pose layout (#19)
- The SLEAP card now refuses a
.slpthat was labeled against a different video, listing the frame count, frame size or labeled frame range that does not fit and asking for another file, instead of overlaying a pose that lands on the wrong pixels (#18) - The same check runs when a video is loaded under an already-loaded
.slp, so swapping either side of the pair is caught (#18) - A
.slpwhose recorded video name is not the loaded video now gets an amber notice rather than a red refusal, names being a prompt to look rather than grounds to refuse, and a differing fps or a multi-video.slpis flagged there too instead of only in the console (#18) - A
.slpthat cannot be read at all now says so on the card too, instead of failing silently to the console (#18)
- Removed the "Selection" summary line under the trim track, since the handles, the band on the track and the In/Out readouts already report the range (#17)
- Dropped the word "Play" from the play button, leaving the glyph now that the step buttons carry arrows instead of triangles (#17)
- Playback again loops inside the marked range: with the trim handles the playhead is no longer moved when a range is marked, so pressing play started outside the band and ran the whole way up to Out before wrapping (#17)
- Replaced the frame step buttons' triangle glyphs with the arrow keys they are bound to, so they no longer read as a smaller copy of the play button, and gave both an explicit screen reader label (#16)
- Adopted version bumping and changelog conventions, and backfilled this changelog from the project's history to date (#16)
Everything built before the project began tracking versions.
- Replaced the trim buttons with In and Out handles dragged directly on the timeline (#15)
- Added the BBQS, CON, and Talmo Lab watermarks and a version indicator to the footer (#14)
- Unified the save and upload routes so both deliver the same tar.gz bundle (#13)
- Added download and upload delivery modes for extracted selections (#12)
- Added EMBER OAuth sign-in and an upload destination picker (#11)
- Added Google Analytics behind a GDPR cookie consent banner (#10)
- Redesigned the UI with a light and dark theme toggle and a simplified extraction flow (#8)
- Added the video player itself: frame range selection, extraction to an upload-ready payload, and a pose overlay (#1)
- Added GitHub issue templates for bug reports and feature requests (#9)
- Added REUSE licensing compliance and a pre-commit hook (#7)
- Bumped codecov/codecov-action from 5 to 7 (#5)
- Added Playwright integration tests, Storybook, and Chromatic visual review (#4)
- Migrated from a single-file HTML page to modular TypeScript built with Vite (#3)
- Deployed the app to the GitHub Pages root on push to main (#2)
- Added the initial repository scaffolding, along with the preview and Dependabot workflows