Changelog¶
2026-09-07¶
- Package quotas now count every byte you store. The cold-storage discount, which counts a video nobody has watched for 30 days at half its size, now applies on pay-as-you-go only, where it lowers the storage meter. On a package the quota is storage you rent, so it counts physical bytes, and the Billing page shows that figure. No package customer was billed under the old rule.
2026-09-05¶
- Every rendition now targets a higher picture quality. The per-title encoder aimed each rendition at a VMAF of 86 on average and 82.8 on its worst half second; it now aims at 90 and 87, and on our test titles the renditions come out 2 to 4 points better on their worst half second. The 1080p and 2160p bitrate ceilings rise with it, from 4500 to 6000 kbps and from 14000 to 20000 kbps, so a ceiling no longer holds the hardest titles under the new target.
- 1080p, 1440p and 2160p encode with less CPU at the same quality. Those renditions no longer search the smallest B-frame block partitions (about 10 to 15% less encoder CPU per rendition for under 1% of bytes) and spend part of the saving on a finer motion search with a single reference frame, which returns 3 to 4% of bytes at the same picture quality.
- 60 fps uploads keep their picture quality at 240p and 360p too. Those renditions play a 60 fps source at half rate, and the 2 September tuning cost their worst moments about two points there; they now switch to the earlier tuning for 60 fps sources, as the 720p rendition did on 3 September.
2026-09-03¶
- The 1440p and 2160p renditions now declare their real H.264 level (5.1 and 5.2, in the stream and in the playlist's
CODECSstring; they said 4.2, the 1080p level, which a 2160p60 stream is far past). A player that cannot decode those levels now skips the renditions instead of selecting them and failing. The encodes themselves are unchanged: the 2160p rendition keeps the single reference frame it has always been encoded with, now stated explicitly rather than implied by the old level's buffer limit. - 60 fps uploads keep their picture quality at 720p. The 2 September encoder tuning cost the hardest moments of 60 fps content more than it cost 30 fps content, and the 720p rendition is the one that plays 60 fps sources at their full rate. That rendition now switches to the earlier tuning for 60 fps streams only: on our 60 fps test titles its worst half-second scores two points higher for about 3.6% more bytes on those titles, and 30 fps uploads are unchanged.
- Uploads are analysed with less CPU. The per-title analysis that runs before every encode no longer makes a trial encode of the source to read its motion statistics, and the probe encode it keeps uses a cheaper motion search: together about a seventh of the analysis cost, 3-4% of an upload's total encode CPU. The per-title models were retrained on the slimmer analysis. On our test corpus every rendition up to 1440p comes out within 1% of its previous size at the same picture quality on the hardest moments; 2160p is the one we are still watching, where four premium master titles read 4% smaller with a lower worst-moment score.
- Picture quality on the hardest moments of each rendition is back to where it was before the 2 September encoder change. That change was tuned on average quality; measured on the worst half-second of each rendition it had given some of that quality back, most on 1080p and on 60 fps content. Each rendition now uses whichever of the two encoder tunings scores better on those moments at the same size: the new tuning stays on 720p, 360p and 240p, the earlier one returns on 1080p and 480p. Renditions come out 2-11% larger than they were last week (1080p the most, 240p the least) and the same size or smaller than before 2 September.
- Videos recorded a little above 30 frames per second keep a smooth picture on the smaller quality levels. Every video is encoded at several quality levels, and the three smallest ones top out at 30 frames per second. Reaching that cap halved the recording's frame rate, which is the right thing for the common rates (60 becomes 30, 50 becomes 25, 48 becomes 24) and the wrong thing just above the cap: a recording at 31 frames per second came out at 15.5 on those levels, throwing away half its frames to clear a single frame of overage, and the frame rate then doubled if the player stepped up to a larger size partway through. Rates that halve cleanly are unchanged, and anything between 31 and 47 now comes out at 30. Screen recordings without a fixed frame rate, whose measured rate often lands just over 30, are what this affects in practice.
2026-09-02¶
- The 240p rendition now uses the H.264 High profile, like every other rendition (its
CODECSentry in the master playlist is nowavc1.64001e). High has been decodable on every device the player supports for years; the Main profile the smallest rendition used to declare withheld the 8x8 transform from the encoder at the one rendition where its bitrate cap binds hardest, and it was the only rendition whose quality tuning had been measured on a different profile than the one it shipped with. - Smaller files at the same picture quality, and cheaper encodes. The per-title encoder now also shapes how bits are spread within each frame and between easy and hard scenes (x264's adaptive quantization and rate curve), tuned per rendition on our test corpus. Renditions come out 1-7% smaller, most on 1080p and on 60 fps content, at a picture quality our side-by-side reviews could not tell apart from before. Encoding takes 17-27% less CPU, so long uploads are ready sooner.
- Sharper picture on the smallest renditions at the same bitrate. The 240p and 360p renditions are now downscaled with a higher-quality resampling filter (Lanczos). At these small sizes the filter choice is visible: fine detail like text on a whiteboard, distant objects and textures survives the downscale instead of blurring away, measurably better on every content category we test. Larger renditions are unaffected — at their scale the filters look the same, so they keep the faster one.
2026-08-31¶
- Videos with a variable frame rate now come out of the encoder at full length. Screen recordings and some phone captures don't have a fixed frame rate, and the piece-by-piece encoding of longer videos lost a sliver of the timeline at every piece boundary: the video ended slightly early and drifted out of sync with its audio, and a boundary landing where the picture was holding still (common in screen recordings) could cut out what the screen was showing for a moment. Pieces are now cut frame-exactly, so the assembled video matches what a single-piece encode would produce, frame for frame.
- Fetching a video from a URL now works when the link has no file name. A fetch took the file name, and with it the file extension, from the URL's path. A link whose path does not end in a name with an extension (a
/watchpage style link, or a download route like/uc?id=...) crashed the fetch behind the scenes, and the video sat unprocessed. Such a link now gets a stand-in name (download.mp4), and a link that does carry a name uses just the name, not the whole path, as the video's default title. - An encode piece waiting on another server now gives up on time. When two servers work on the same video, one waits for a piece the other is encoding, and that wait has a time limit so a vanished server cannot stall the video forever. Under repeated handovers of the same piece, the limit restarted from zero on every handover, so exactly the situation it existed for could still wait without end. The limit now spans the whole wait, however many handovers happen inside it.
- A long video can no longer be assembled from the wrong pieces after a settings change. Videos past a few minutes are encoded in parallel pieces, and a restarted encode reuses the pieces already finished rather than redoing them. It matched them by position only, so if the piece length changed between an encode and its restart, position 2 of the new layout picked up the piece encoded for position 2 of the old one, covering different seconds of your video. Every check downstream passed, because each one compares the finished file against the pieces it was built from. Saved pieces now record the layout they belong to, so a layout change re-encodes instead of mixing the two.
-
A video can no longer get stuck retrying forever on an empty saved piece. If an upload of a finished piece was cut off, it could leave a zero-length file behind. That counted as "this piece is done", so every retry downloaded it, failed to join it to the others, and started over, while the cleanup that would have removed it only runs once a video finishes. Empty pieces are now ignored and re-encoded.
-
Statistics shows how long viewers spent on each quality level of a video. Select a single video and the new Playing time by rendition card ranks the rungs of its ladder by the time actually played on each one, following the page's date range and country filter. A stacked chart above the list shows the mix day by day, so a change to your encoding settings shows up as the colors shifting; each rung below it carries its share of total playing time. It makes two things visible that the ladder alone cannot: a top rung nobody watches is storage and delivery you are paying for with no audience, and watch time bunched on the low rungs means viewers are not reaching the quality you published. Playback on iOS is not counted, because there the operating system picks the rendition without telling the player which one. See statistics.
-
A large video no longer restarts its encode while it is busy moving files. An encode holds its place in the queue by checking in periodically, and a job that goes quiet for half an hour is treated as hung and handed to another worker. Every stage that encodes or analyses checked in, and so did every download, but uploads did not: while a finished piece was being saved to storage, or while a completed video's original was being relocated between storage buckets, the job was silent. On a large source those transfers are long enough to cross the half-hour mark, so an encode that was working the whole time could be taken away and restarted from its last saved point. Uploads now check in as they transfer, as downloads already did, and so do the two steps that join the finished pieces back together.
2026-08-28¶
- A source between two quality levels keeps its own resolution at the top of its ladder. Renditions are named by line count and a video is never upscaled, so a source whose height fell between two levels used to lose the whole level above it: a 1920x818 widescreen film was delivered at 1690x720, a fifth of its pixels gone, and a 1920x1072 crop, eight lines short of 1080p, topped out at 720p. Now a source within 10% below a level gets that level at its own lines, and a source further past a level gets a top rendition at its own dimensions (
818pbeside720pfor that film), with a bitrate cap in proportion to its pixels. Nothing is upscaled any more, the 240p floor included. See encoding tiers. - The embed player's engine was refreshed. The Video.js 10 engine behind the embed player moved up one release. The settings (cog) menu is where you will notice it: the menu sizes itself to the rows it actually holds instead of holding a wider minimum open, and a row's label now stays on one line rather than wrapping onto two. Stepping into a submenu also clears the highlight on the row you came from and puts it back when you return, so only one row ever looks focused. Playback, quality selection, captions, chapters, the seek-bar thumbnail and chapter titles, the loop, zoom and share controls, your logos and overlays, and every player setting are unchanged.
2026-08-27¶
-
A long video can no longer finish encoding with a piece of it missing. Videos past a few minutes are encoded in parallel pieces and joined back together. If the encoder exited cleanly having written only part of a piece, nothing downstream could tell: the join checks that no frames were lost between the pieces and the finished file, and a piece that was short to begin with is short on both sides of that sum. The short piece was also saved for reuse, so restarting the encode picked it back up instead of redoing it. Each piece is now measured against the length it was asked for, before it is saved, and an encode that produced a short one fails and retries cleanly rather than publishing a video that is quietly missing time.
-
A failed encode can no longer report itself as finished. When ffmpeg exits without an error message, we decide whether the run was real by checking that it reported progress. That check read any line of ffmpeg's output, including the lines where it prints your source file's own tags, so a file whose title or comment happened to contain text shaped like a progress report could satisfy the check before encoding had even started. An encode that then produced nothing was reported as a success. The check now reads only ffmpeg's progress line.
-
Your source file's tags no longer travel into the video we serve. Editing suites and cameras write a project title, an author or artist name, a comment and often the full path the project was saved under into the file they export, and chapter markers ride along with them. Those tags were being copied into every quality level and audio track we published, so anyone who downloaded or inspected a rendition could read them. Every rendition is now written with no metadata carried over from the upload, both the HLS qualities and the progressive MP4 download. The uploaded file itself is never rewritten, so the original download still returns the exact file you sent. Titles, descriptions, tags and chapters are unaffected: they come from what you set in HeapStream, and chapters are served as their own track. Videos encoded before this change keep the tags they were published with; re-encoding one clears them.
2026-08-26¶
- Every row you create is yours. Players, live streams, videos, folders, showcases, webhooks, profiles, watermarks, signing keys, tags, captions, audio tracks and posters now record the member who created them, whether from the dashboard or with an API key. Videos and players used to be filed under the project owner whatever the member doing the work, and the other resources kept no record at all; rows created by the system on your behalf (captions generated for a video, tracks copied by a duplicate, the replay of a live stream) follow the video or stream they derive from. Existing rows are attributed to the project owner. The video list's
user_idfilter, which could only ever match the owner, now finds a member's uploads. - Live stream figures follow the project owner. On a project with team members, a live stream created by someone other than the owner recorded its numbers under whoever created it. The "Last broadcast" card on the live stream page then looked for that person's viewer and bandwidth figures, found none, and hid itself, leaving the page blank after a broadcast ended. Live caption minutes were filed the same way, which understated the owner's AI usage. Both now resolve the project owner, the account every other statistic and every plan check already used. The card fills in for past broadcasts too, because the correction is in how the figures are read.
2026-08-25¶
- Out-of-range ids answer 400, not 500.
GET /api/v1/project/{project_id}/videopasseduser_idandfolder_idto the database unchecked, so a value above PostgreSQL's 64-bit integer range (9223372036854775807) failed inside the query and came back as a server error. Both are now bounded like every other id field, so an impossible value is a normal validation error, and the OpenAPI spec documents the range. The session cursor on the playback-statistics endpoint gained the same bound. Found by the suite's own API fuzzer. - Caption translation, documented. Asking for several caption languages has always transcribed the speech once and translated it into the other languages; the auto captions page now explains it, and the API description of
generated_subtitlessays which entry gets the transcript and which get a translation. - Project roles. Every member of a project now has a role: Manager (everything but members, signing keys and deleting the project), Editor (videos, uploads, captions, folders, live streams, statistics) or Viewer (look, don't touch). The Users page shows each member's role and lets the owner change it from a select on the row; the invite form asks for it (Editor by default). Everyone invited before today is a Manager, which is exactly what a member could do until now. Viewers do not take up a seat. On both the dashboard and the REST API, a member whose role lacks a permission gets a clear refusal (
403 permission_denied, naming the permission) instead of a page they cannot use; buttons and menu entries a role cannot use are hidden. A Viewer sees a live stream without its RTMP server URL and stream key (those arelive.manage). Every API operation declares the permission it needs asx-permissionin the OpenAPI spec; see Who can call what. - The REST API accepts team members. Every
/api/v1project route used to answer401 not_project_ownerto anyone but the project owner, while the dashboard admits every invited member. The video upload page is one of the places that hit this: it uploads through the API, so a member saw the page and then got a 401 on the first request. The API now admits exactly whom the dashboard admits, with a member's session or their own API key, and bills the project owner as before. Deleting a project and managing its signing keys stay owner-only. A member's own plan no longer matters on someone else's project; the owner's does, as on the dashboard. The caption editor's dashboard-only route from 2026-08-21 is gone again: the editor reads and writes through the API like everything else. A member's project list (GET /api/v1/project) includes the projects they were invited to, as the dashboard's "All projects" page does. An account suspended by HeapStream is refused on every request, whichever project it is on. See who can call what. - A pending invitation can be revoked. The project owner has a Revoke button on every invited-but-not-yet-accepted row of the Users page; it frees the seat the invitation held.
- The owner no longer takes up a seat. Seats count the people you invite, so a 1-seat plan can invite one person; it could invite nobody before, since the seat was counted as yours.
- Inviting, suspending and re-enabling members, and deleting the project, are owner-only in the dashboard. Members still see who is on the project; the invite button and the per-member actions are only shown to the owner.
2026-08-22¶
- The player preview shows the player you are editing. The Player Edit page's preview pane always embedded the project's default player, so edits to any other player looked like they had no effect. It now embeds the player you are editing. Only the preview was affected; embeds on your own pages always used the right player.
2026-08-21¶
- Viewers can loop a video. Switch on Show Loop for a player and its settings (cog) menu gains a Loop toggle: a viewer turns it on and the video restarts from the beginning every time it ends, until they turn it off. The switch is on the Player Edit page and in the player API (
show_loop), it is off by default so existing players are unchanged, and each viewer's choice resets when the page reloads. - The caption and chapter editor now works for everyone with project access. Opening a track's Edit action as an invited team member showed the editor but never loaded the cues, reporting "Could not load captions (401)", and a save would have failed the same way. The editor was reading and writing through the REST API, which only ever accepts the project owner's credentials, while the page itself is open to any member. It now reads and writes through the dashboard, so whoever can open the editor can also use it. Owners were never affected, and the REST endpoints are unchanged.
- The embed player's engine was refreshed. The Video.js 10 engine behind the embed player moved up five releases, bringing: full keyboard navigation in the settings menus (the cog and its submenus now join the tab order); controls that stay visible while you are actively interacting with them; right-to-left language support; and several HLS playback fixes, so your audio-track choice survives quality switches, sideloaded captions survive engine resets, and growing the preload buffer no longer restarts the load. Automatic quality selection keeps its cap at the player's on-screen size but gained a 720p floor: a small embed is no longer capped below 720p by its size alone, so small players look sharper (bandwidth permitting, as selection still adapts downward freely). The controls, menus, logos, overlays and your player settings are unchanged.
- Chapters now show in the embed player. A chapters file uploaded to a video produced no chapter marks on the player's seek bar: the chapters file is delivered from the media CDN, which never sent the cross-origin permission header (
Access-Control-Allow-Origin) browsers require before letting the player read it, so the file downloaded fine and was then silently discarded. The CDN now sends that header on every media response. Playback, captions, and thumbnails were never affected, and already-uploaded chapter files start showing on their own; nothing needs re-uploading.
2026-08-14¶
- 2K and 4K renditions now get per-title encoding, including for 60fps sources. Per-title encoding, which aims every 6-second slice of a video at a consistent quality target instead of spending a fixed bitrate, previously covered the 240p through 1080p quality levels; 1440p and 2160p renditions used a fixed recipe. Those top renditions dominate a 4K upload's storage and delivery bytes, and on typical film-like content the fixed recipe spends far more than the quality target needs. With per-title treatment extended to both top renditions: a 4K live-action test film came out 53% smaller at 1440p, and native 4K 60fps test clips came out 32-46% smaller at 2160p, in both cases while holding the same worst-half-second quality floor that the lower renditions guarantee. Content that genuinely needs the full bitrate budget (heavy motion and detail at 4K) measures byte-for-byte the same as before, so the reduction only removes spend that bought no visible quality. Nothing changes in how you upload; already-encoded videos keep their existing renditions.
2026-08-13¶
-
Every quality level now holds through the hardest half-second, and hard titles get smaller at the lower levels. The worst-half-second quality guarantee that 1080p gained last release now covers 240p through 720p as well: the per-title encoder aims each 6-second slice so its single worst half-second stays on target, not just its average. Recalibrating the lower levels for the new burst ceiling also undid spend that no longer buys anything: under the old ceiling, hard scenes at low levels were compressed extra finely to fight a limit that has since been raised, so on hard-scene-heavy titles the lower quality levels now come out 5-6% smaller while their worst moments measure better. Calm titles spend a little more at the low levels (typically 4-11% of their already-small size) and their brief dips improve the most. Nothing changes in how you upload or what players receive; already-encoded videos keep their existing renditions.
-
Your email address now works in any capitalization. Sign-in, password reset and sign-up treated
You@example.comandyou@example.comas two different addresses, so signing in with different capitalization than you registered with was rejected as a wrong password, and a password reset for it reported that no such account existed. The same gap let one mailbox register twice. Addresses are now stored and matched in one consistent form, so any capitalization reaches your account. Unsubscribe links match the same way, which fixes a case where mail could continue after opting out.
2026-08-12¶
-
Long videos keep a clean 6-second segment grid all the way through. Long uploads are encoded in parallel chunks and stitched back together. On sources whose container reports a fractionally-off frame rate (a common mp4 quirk: 24.00004 instead of 24), the keyframe grid drifted one frame at each chunk boundary, producing occasional ~10s segments where every player expects 6s ones. Players size their startup and rebuffer targets from the longest segment, and quality switching around those spots got coarser. Segments now come out at exactly the advertised length, and each chunk also sheds one wasted duplicate keyframe.
-
Turning live-stream recording on or off now tells you when it did not happen. If the streaming backend could not apply a recording change, the dashboard and API used to report success anyway, so the page said recording was on while the next broadcast went unrecorded. A refused change now returns an error and the setting keeps its previous value, so what you see always matches what the next broadcast will do.
-
The hardest seconds of a video no longer dip in quality. Every quality level has a bitrate ceiling, so a stream never asks more of a connection than the level a player picked. That same ceiling also pinned the few genuinely hard scenes in a title (a battle, confetti, a fast camera pan), which could dip visibly below the quality of everything around them. Encodes may now burst to twice the ceiling for those moments only: easy scenes spend nothing extra, and the worst seconds of a title now measure close to the rest of it. Titles with hard scenes come out somewhat larger, typically 10-25% more video bytes; calm titles barely change.
-
1080p now holds quality through the hardest half-second of every scene. Our per-title encoder picks a compression level for each 6-second slice of a video so every slice lands on the same quality target. It used to aim at a slice's average quality, and an average can hide a brief dip: a flash cut or a fast pan could drop visibly for half a second while everything around it looked fine. The 1080p level now aims each slice at its worst half-second window instead. Hard moments encode finer, comfortable ones a touch coarser, and on our reference titles the worst half-second of each title measures 1 to 5 VMAF points higher at the same or slightly smaller total size. The lower quality levels keep the previous behavior for now: their bitrate ceilings, not the quality target, set their worst moments.
2026-08-11¶
- An API key now gets its id and secret when you save it. The create page used to generate both up front and ask you to copy them before saving, which meant your browser sent them back to us on submit. Now you give the key a label, and the id and secret are generated when you save and shown on the page that follows. Copy them there: as before, the secret is never shown again.
- The embed player runs a newer engine, with better touch and menu behavior. On phones and tablets, tapping a control no longer hides the control bar out from under you, and pressing a button no longer also pauses the video behind it. Menus and tooltips near the edge of a small player now flip to stay on screen instead of being clipped, and they open more smoothly. Screen readers get fuller announcements as playback state changes. Everything the player offered before still works the same way, including the optional ±10 second rewind buttons: the new engine drops them from its own control bar, so we put them back for the players that have them switched on.
2026-08-10¶
- A freshly restarted server no longer answers the odd request with a 500. Most of our request and response schemas finish building themselves the first time they are used rather than when the server starts. That last step was not safe to run on two requests at once, so in the first moments after a deploy or restart, when many requests arrive together and hit it simultaneously, one of them could fail with a server error. It was brief and rare, but it could land on anything: an API call, or a view beacon from the player, in which case that view went uncounted. Every schema is now built during startup, before the server accepts its first request, so there is nothing left to race.
2026-07-30¶
- A video with disc-style bitmap subtitles no longer fails to encode. Uploads remuxed from Blu-ray or DVD often carry bitmap subtitle streams (PGS, VobSub) alongside the audio and video. Importing embedded subtitles tried to convert those to text, which is impossible without OCR, and the failure took the whole encode down: the video ended up errored despite the media itself being fine. Bitmap subtitle streams are now skipped (text ones still import as before), and the video encodes normally. Importing bitmap subtitles for real, via OCR, is planned.
- Bulk actions and restores no longer strand you on a blank "Redirecting..." page. A dashboard page left open for more than an hour had a stale form token, even though your sign-in lasts a month, and a bulk delete on such a page answered with a redirect no browser would follow: nothing happened, and you were parked on a bare Redirecting... page with no idea why. Form tokens now last as long as your session, so a tab open all day still submits, and a refused submit lands you back on the page you were on with "This page has expired. Reload it and try again." The gallery's bulk bar and the Restore buttons in Recently deleted were both affected.
2026-07-29¶
- The "no access to this project" page is no longer a dead end. Opening a project you don't have access to answers
403, and every link on that page led straight back into the same project: the "return to dashboard" link rebuilt the very URL you had just been denied, and the sidebar still showed that project's whole menu, so Videos, Players, Settings and the rest each returned the same error. There was no way out except editing the address bar. The dashboard link now always goes to your project list, and the sidebar is scoped to a project you can actually open. - Live master playlists are rate-limited per viewer IP. The live playback manifest (
master.m3u8) is rebuilt from live origin state on every request and deliberately never cached, so it now carries an abuse cap: 300 requests per minute per IP — far above what a player needs (one fetch per playback start) and sized so a large audience behind one carrier NAT all starting in the same minute is unaffected. Over the cap the endpoint answers429 Too Many Requestsand the player retries through its normal refetch loop; captions and steering polling are never limited. The manifest build is also briefly memoized server-side, so a stream's viewers share one rebuild per two seconds however many arrive at once.
2026-07-27¶
- The age-gate setting is removed. The
agegatefield on videos and profiles was accepted and stored but never enforced: no player or embed page ever asked viewers to confirm their age, whatever the flag said. Rather than ship it half-built, we removed the field from the API and dashboard; requests that still send it are rejected as an unknown key, the same as any other typo. If an age confirmation step in the player matters to you, tell us and it will come back as a real, enforced feature. - Turning keep-originals off no longer deletes already-stored originals. Disabling keep-originals (on a profile or in project settings) used to sweep the project and delete the stored original of every already-encoded video that resolved to the new setting, behind a confirmation step. It now applies the way every other encoding-time setting does: videos whose encode finishes after the change stop storing an original, and everything already stored stays exactly as it is. A settings edit never destroys media anymore; the
confirm_delete_originalsAPI field is gone along with the412 keep_originals_confirm_requirederror. If you want to reclaim storage from existing originals, tell us — a bulk "delete originals" action is the planned shape for it. - Set a profile on many videos at once. The gallery's bulk bar gains Set profile: select any number of videos — a whole folder, say — pick a profile in the confirmation, and every selected video attaches to it (or detaches, so the project default applies). Player, visibility and domain settings apply on the next load; encoding settings were stamped when each video was created and don't change. This replaces the folder "default profile" experiment, which never left the dashboard: settings now come from exactly one place per video — its profile or the project default — instead of silently changing when a folder was moved.
- Password-protected viewing is hardened across every public page. Video embeds, live-stream embeds, and showcases now share one access gate. Wrong-password attempts are rate-limited everywhere (20 failures per minute per viewer; correct passwords never count toward the limit, so an audience sharing one network can't lock itself out of a legitimate link), and password checks are constant-time. Two long-standing quirks are gone: a bogus
?password=or a stale link token on a public video no longer blocks playback, and unpublishing a project is now really the kill switch — shared passwords and link tokens stop working there, while project members keep their dashboard previews (which now also work on showcases and live streams of an unpublished project).
2026-07-26¶
- Yearly plans are never charged for extra usage. Overage billing is now a monthly-billing option only. On a yearly plan, passing your included bandwidth reduces top playback quality to 720p for the rest of the month instead of adding a charge, so a year of usage can never arrive as one surprise invoice. Your included delivery is granted for the full year, and monthly plans keep the overage toggle exactly as before.
- The sort select now orders folder tiles too. One control, one order: folders sort with the videos, alphabetically on the Title sort and by creation date on the Date sort (by deletion date in Recently deleted, as before). Folder tiles previously kept a fixed alphabetical order, so the default Date sort now shows the newest folders first.
- Recently deleted browses even more like the gallery. Searching now surfaces a matching deleted folder itself, wherever it sat in the tree, as its own restorable tile — previously the search pointed you at its top-level parent. And a Not in a folder tile at the trash root collects the deleted videos not filed in any deleted folder, mirroring the live gallery's un-filed tile.
- The Videos page is gallery-only now. The list view is retired: it had drifted behind the gallery (no bulk selection, no drag-to-folder) and the gallery covers everything it did. Old
?view=listbookmarks simply open the gallery. - Creating a showcase is an explicit step now. Opening a folder's Showcase settings used to create the showcase silently, so a quick look left a disabled entry on the Showcases page forever. The settings page now opens with a Create showcase button instead, and only deliberately created showcases appear in the list. Showcases you already have are untouched.
- Restoring a folder now brings back everything inside it. A folder restore previously revived only what that folder's own delete had removed; it now revives the folder's whole subtree and every deleted video filed in it, whenever and however each was deleted. Every deleted folder tile carries its own Restore button now, subfolders included, and a tile's thumbnails preview what a restore brings back.
- Recently deleted now looks like the gallery. Trashed folder tiles carry the same thumbnail strip as live ones, built from the folder's own deleted videos, so a tile previews what a restore brings back; a banner states the 31-day retention. Browsing, search, filters and sort work as before.
- Recently deleted shows poster art again. The thumbnail route refused deleted videos, so every card on the Recently-deleted page rendered a broken image; a trashed video's poster now keeps serving through its restorable window and stops only once the purge sweep removes it.
2026-07-25¶
- The access-suspended email says what happened. Its subject used to read "Invited to project …" (a copy of the invitation subject); it now reads "Your access to project … has been suspended", matching the message body.
- Unsubscribing now suppresses non-essential email. Every non-essential email carries an unsubscribe link in its footer, and the opt-out is enforced when the email is sent: an unsubscribed address no longer receives product news or other optional mail. Essential email — password resets, security notices, billing and account warnings — is always delivered and carries no unsubscribe link.
- Dashboard forms scope-check reference ids like the API. The profile create/edit pages accepted a crafted request referencing another project's player, watermark or folder, and a deleted reference produced a server error; both are ordinary form errors now, validated the same way the API validates them. On the upload page's fetch-by-URL tab, a foreign or stale profile id produced a server error; that URL is now reported under "Couldn't be fetched" instead. API behavior is unchanged.
- Write responses now carry the same body a GET returns. Fetching, clipping, duplicating or editing a video used to answer with
previewandasset.encoding_progressasnulleven when a subsequent GET filled them; every video endpoint now returns the identical, fully-assembled shape, so you can use a write's response directly instead of re-fetching the video. - List rows show live encoding progress.
asset.encoding_progressused to benullin list responses; while a video is processing, its list row now carries the same per-preset progress the single-video GET reports, so a library view can show encode progress without one GET per video.
2026-07-23¶
- A public live stream's playback URL is signed now, like a private one's. It carries a token and lasts years rather than forever, so nothing you do with the link changes: share it, embed it, it keeps working. Two things are visible through the API:
playback.hls_urlon a public stream now has?st=…&e=…on the end, andplayback.hls_url_expires_at, which used to benullfor public streams, now carries that date. Build playback links from the API response rather than assembling them, and use Reset stream key when you need a shared link to stop working. - Public MP4 download links now expire after about a month. A public video's
static_mp4_linkused to be a permanent URL. It is now a signed link valid for roughly 30 days (24 hours for a private video), which is still long enough to publish behind a download button, but you should read the field again when you rebuild the page rather than storing one forever. If you saved a link in your own database, re-read it. Two knock-on changes: rotating a video'spasswordno longer rotates its MP4 link, and making a public video private shortens the expiry of links minted after that instead of cutting off ones already handed out. - Hover-preview links now expire, and last about a month on public videos. A public video's
previewURL used to be permanent. It is now a signed link like every other playback URL, minted to work for about 30 days (24 hours for a private video). Read the field again when you need a fresh one instead of storing it indefinitely; nothing changes for players that fetch it per page load, which is how the dashboard and the embed use it. One consequence worth knowing: unpublishing a video now shortens the lifetime of links minted from that point on rather than cutting off links already handed out, which keep working until they expire.
2026-07-22¶
- Everyone on a project can manage its settings. People you invite to a project can now open and change the project's Settings, Profiles, Watermarks and Webhooks, not just the person who created it. Deleting the project and its billing stay with the project owner.
- Big showcases stay navigable: search and sections. Every showcase now has a search box over its videos' titles and descriptions, and when the showcase's folder has subfolders the page automatically groups videos into one section per subfolder (alphabetical, deeper folders rolled up, empty ones skipped) — no setting to configure. Both work without JavaScript, like the rest of the page.
2026-07-21¶
- Showcases carry your brand. A showcase can now show a logo (with a click-through link), a full-width hero banner, a favicon, and a social share image, plus a plain-text footer line and a featured video that plays when the page opens. Images are converted to PNG on upload and size-capped per slot; SVG is not accepted. Everything lives on the showcase settings page, in the new Branding images card.
- Showcases: share a whole folder with one link. Any folder can now be published as a showcase — a public, branded page showing every video in the folder and its subfolders as a grid with an inline player, at a stable
/s/…link. Pick public (indexable, with structured data and share tags), unlisted, or password privacy; set a title, description, sort order, and an accent color. Filing a video under the folder is what puts it on the page, so the page maintains itself; private videos stay off it unless you explicitly flip the "Also show private videos" switch. Manage everything from the new Showcases sidebar page or the screen icon inside any folder. Views count in statistics like any embed. Dashboard-only for now — no API. - Fixed: deleting a live stream mid-broadcast threw away its recording. Ending a broadcast any other way (the encoder stopping, the End stream button, or resetting the stream key) exports the recorded session as a replay video. Deleting the stream while it was still live and recording did not: it marked the session ended but never queued the export, so that last recording was lost, with nothing tying the loss back to the delete. Delete now ends the session the same way every other path does, so a live, recording stream you delete still produces its replay. Streams that were not recording, or not live, are unaffected, and the API is unchanged.
- Folders are managed right in the video library. The separate Folders page (and its sidebar item) is gone: everything it did now happens on the Videos page. + New folder above the folder tiles opens a create dialog with the parent pre-filled to where you're standing; hovering a tile reveals a pen (also next to the folder name in the breadcrumb) that opens the edit dialog — rename, recolor, move, or delete from there; and All folders shows the whole tree with video counts when you want the bird's-eye view. Nothing changed in the folder API. See Folders.
2026-07-20¶
- Fixed: player logos were stored smaller than the player shows them, so they never looked crisp. A logo uploaded to a player was shrunk on upload to below the size it is actually displayed at: the control-bar logo was stored 16px tall and shown at 20px, and the title logo was squashed into a 24x24 square whatever its shape and shown at 28px. The browser scaled them back up, which is what made them look soft, most visibly on a high-DPI screen. Logos are now stored at twice the size they are shown at and keep their proportions, a logo is never enlarged on upload (a wide, short one used to be blown up to fit a height it never needed), and the corner logo can use the full 32px the player allows instead of stopping at 28px. Logos you already uploaded are untouched, because the shrinking happened when they were stored — re-upload one to get the sharper version.
- Sort the videos gallery. The Videos page gains a sort control: pick Date (the default) or Title, and flip ascending/descending with the arrow button beside it — at the root, inside folders, in the not-in-a-folder view and on search results alike. Your choice sticks for the session, like the grid/list toggle. The API's video list keeps its fixed newest-first order.
- Deleting is no longer final: videos and folders move to Recently deleted, restorable for 31 days. Deleted videos already waited 31 days before being removed, but nothing could bring them back and deleting a folder was immediate and permanent. Both now land in a new Recently deleted view — the Videos page in a trash mode (enter it with the trash button in the Videos toolbar), with the same search, filters, sorting and layout: restore a video with one click, or restore a deleted folder and get back its subfolders and exactly the videos its deletion deleted, in their original spot. Mid-upload/mid-encode deletes are still removed immediately, deleted items still count as stored media until they're gone, and the API is unchanged — deletion works exactly as before, and its list never shows deleted videos.
2026-07-19¶
- The dashboard's pages are lighter, and hover previews now use the same player as the embed. The Videos and Live pages each shipped a private copy of a video engine just to play thumbnail previews, so opening both in one session downloaded the same megabyte twice, and none of the dashboard's code was minified. Everything is now minified and split, which takes each page's own JavaScript from roughly 135–226 KB down to a few KB; the Upload page's uploader is also separated from the page's own code, so a release that changes one no longer re-downloads the other. Previews themselves moved onto the same video player the embed uses, rather than a second engine maintained alongside it — on the Videos and Live pages that player is a larger download than the engine it replaces, so those two pages are lighter only once it is cached (which it already is if you have opened a video or a player preview). Nothing changed in how any page behaves.
- Fixed: deleting an audio track could silently break the audio of fast clips a month later. A fast clip serves its audio straight from the source's stored segments. Deleting that audio track on the source was allowed, and nothing appeared to change at first — the clip kept playing — but a month later the routine cleanup removed the deleted track's media, and every fast clip that carried the track lost its audio entirely, with nothing connecting the breakage back to the delete. Deleting an audio track that live fast clips kept is now refused (
412 cant_delete_audio_track_with_clips), the same way deleting the source video itself is; delete the clips first. Tracks no fast clip kept stay deletable exactly as before, and plainclips (which own their media) never block anything. - Fixed: clips of high-frame-rate videos could start ~17ms differently across quality levels. A 60fps video's lower quality levels play at 30fps, and a clip's cut point used to snap to the source's 60fps frame grid — a point the 30fps levels can't always land on. Those levels then began the clip up to half a frame early, their playlist declared a slightly different duration than their media actually held, and captions sat the same ~17ms off on those levels. Cut points now snap to the frame grid every quality level shares, so all levels (and captions, and audio) agree on the clip's exact first instant. The cut you request moves by at most one frame of the lowest-frame-rate level (~33ms for a 60fps video) instead of one source frame; videos whose quality levels all share one frame rate are unaffected. Applies to both
clipandfastclip; nothing changed in the API.
2026-07-18¶
- Reusable settings Profiles. Bundle video settings — encoding tier, watermark, caption languages, auto-chapters and shot detection, visibility, allowed domains, player, keep-originals, target folder — into named Profiles and attach one per upload, per video, or project-wide. A Profile carries only the settings it has an opinion on; everything else falls through to the project's Default profile (every project now has one — it's what the project settings page edits). Anything you set directly on a video still wins, always. Encode-time settings and visibility are stamped when a video is created and never change retroactively; domain and player settings apply immediately to every video resolving through the Profile — flip a Profile's player and every attached embed uses it on the next load. The dashboard gets a Settings → Profiles section and pickers on upload and video edit; the API gets full profile CRUD plus
profile_idon upload/fetch/video edit. One behavior refinement rides along: settings omitted on upload mean "inherit from the profile chain" rather than "explicitly off", and the dynamic per-video settings (keep_originals,allowed_domains,player_id) acceptnullon edit to clear the video's own value so its Profile answers again. Up to 100 profiles per project; see Limits. - Shared video links now unfurl, and search engines get a fuller description of your videos. Pasting an embed link into Slack, X, Facebook, iMessage or Discord used to produce a bare URL. It now expands into a card with the video's poster, title and description. On the search side, the structured data every embed page carries gained the piece Google needs to offer key moments: results can now deep-link into a specific second of a video, and a video with chapters offers each chapter as its own entry point. None of this is anything you configure. Private videos are unaffected by design: the card tags are only written when the request is allowed to see the video, so a private link pasted where others can't open it stays a plain link rather than showing its title and poster to the room. See SEO and social sharing.
- Fixed: chapter timestamps in search markup were a thousand times too large. The chapter deep links published for search engines were written in milliseconds where the format calls for seconds, so a chapter starting 11 seconds in was published as starting 11,000 seconds in, and the link attached to it pointed past the end of every video. Chapters on the player's own timeline were always correct; this only affected what search engines were told. They are now in seconds.
- Fixed: a private video's embed page published the password used to open it. The structured data on an embed page included the address of the page itself, copied from the address actually requested. For a private video opened with a password or an owner preview link, that address carried the secret that unlocked it, so the password was written into the page (and into every chapter link on it) where a search engine could read it. The address published is now always the plain embed URL with nothing attached.
- Fixed: a video title containing HTML could break the embed page's search markup. Every embed page carries a block of structured data describing the video (title, description, chapters) so search engines can read it. That block is JSON, and the text in it was written out without escaping the
<character, so a title containing one could end the block early and have the remainder of the title treated as part of the page rather than as data. In practice a title could inject markup into the embed page, including a tag that sends the viewer somewhere else. The block is now escaped, so it parses as data whatever a title contains. No title needs editing and nothing changed in the API. - A reminder email a week before your trial converts. Every package trial now sends one email 7 days before the first charge, stating the plan, the exact amount, the charge date, and a link to cancel online. Cancel before the date and nothing is charged; do nothing and the subscription continues uninterrupted. (Card networks require at least 7 days' notice before a post-trial charge — this is also simply the right thing to do.) See Billing & metering.
- Organize your library into folders. Every plan gets folders: a video lives in at most one folder, folders nest up to 5 levels deep, and a project holds up to 200 of them. The Videos page doubles as the browser, with folder tiles (color, poster thumbnails, counts) above the grid, breadcrumbs to walk back up, and a "Not in a folder" tile for everything you haven't filed. Drag a video tile onto a folder (or a breadcrumb) to move it, select several and use the bulk bar's Move to folder, or set the folder on the video's edit page. The upload page targets a folder too, pre-selected when you open it from inside one, and the API gets full folder CRUD plus
folder_idon video list/edit/upload/fetch. Deleting a folder keeps its videos by default (they move out of any folder); an explicit checkbox deletes them too. Folders are private organization only: links, embeds and playback never change because a video moved. - Filter analytics by folder. The analytics page gains a folder filter that scopes every metric to a folder and all of its subfolders. Views are attributed to the folder the video was in when they were watched, so folder analytics start when a video is filed: earlier views are not included, and moving a video does not move its history.
2026-07-17¶
- A failed payment no longer takes your videos offline. A card failure used to disable playback the moment the payment bounced — viewers got an error page while the billing provider was still retrying the charge. Now a failed payment starts a grace period: playback and your dashboard keep working while the payment retries run, new uploads and AI features pause, a banner and an email point you at the fix, and only when the retries are exhausted does access actually end (with an email saying so — your videos and settings are kept for when you resubscribe). Nothing changed in the API beyond a new
payment_past_dueerror on uploads and AI calls during the grace period. - New plans: bigger storage, stated bandwidth, and a pay-as-you-go line. The packages are now Start ($10/mo, 2 TB storage, 2 TB bandwidth), Pro ($29/mo, 4 TB + 3 TB), and Advanced ($79/mo, 7 TB + 5 TB), each with seats (1/5/10) and a 14-day trial; annual billing saves 20%. Alongside them there's a Metered line for API-first teams: $25 per TB stored per month and $10 per TB delivered, no monthly fee and no minimum. Bandwidth is no longer described as unlimited, because it never really could be: instead the quota is stated up front, and what happens at it is stated too — see the next entry. All quotas are decimal units (1 TB = 10¹² bytes), matching how CDNs price. Details in Billing & metering.
- At your bandwidth cap, playback quality reduces instead of stopping. At 80% of your included bandwidth we email you, once per billing cycle. At 100%, videos keep playing — the quality ladder tops out at 720p for the rest of the month rather than going dark, and an email tells you so. Upgrading lifts it immediately, and packages can opt into overage billing ($0.01 per GB beyond the included bandwidth, off by default, toggled on the billing page) which replaces the cap entirely up to a 50 TB monthly safety backstop. Storage at quota pauses new uploads only; playback never stops.
- Uploads are now checked against the room you actually have, and file-size limits are per plan. Starting an upload (or a fetch) verifies the declared size fits your remaining storage, so many parallel uploads can no longer overshoot a full quota together; the error tells you what it needs and what's free. The per-file ceiling is 20 GB on packages and 50 GB on the Metered line — the API's
metadata.sizebound is now 50 GB (the largest any plan accepts) with your plan's own limit enforced behind it, and the dashboard uploader offers exactly your plan's limit. Seats and project counts now follow your plan as well (someone already on one of your projects joins others without using another seat). - Fixed: the billing page's bandwidth figure was always 0. The "bandwidth this month" tile reads a per-project daily-usage table, and nothing in production ever wrote delivery into it — your real delivery numbers were sitting in our analytics store and never reached billing, so the tile showed 0 GB however much your videos actually served. Delivery is now mirrored into that table every hour, and the tile shows what your account delivered this month. A related gap is closed with it: a project whose library didn't change for a while stopped producing daily storage records, so the month's average-storage figure could read far lower than what you actually held; every project now records its usage every day. Nothing changed in the API.
- Added social login with Google and GitHub
- Fixed: one unreachable link could fail an entire fetch-by-URL submit with a server error. A link that came back with a status — a 404, say — was reported neatly as a link that couldn't be fetched, and everything else in the batch still went through. A link that failed before any status existed did not: a hostname that doesn't resolve, a server refusing the connection, or one that never answered took down the whole submit with a server error, including the good links alongside it. All of them are the same thing from where you sit — the link couldn't be fetched — and they now read that way, as one failed row. The API returns
422 head_request_to_url_failedfor these instead of a500. See Fetch a video. - Fixed: pasting a long list of links into fetch-by-URL could add videos and then show you nothing at all. The fetch box takes 20 links per submit. Paste more and it used to fetch the first 20 and list every leftover link in the results card, so you could see what hadn't run — but that card is carried across the redirect in a cookie, and a long list of links doesn't fit in one. A browser throws an oversized cookie away whole rather than trim it, so the card never appeared: the 20 videos were created, the page looked like nothing had happened, and pasting again — the natural thing to do — made a second copy of all of them. An over-long paste is now refused before anything is fetched, with the box telling you how many links you pasted and keeping them there so you can trim the list. Submits of 20 or fewer are unaffected, as is the API, which takes one URL per call. See Limits.
2026-07-16¶
- A live card now shows the picture you're actually sending. Cards for streams that are on air carry a still of the broadcast, refreshed about once a minute, so you can see your framing without opening anything — and hovering it still plays the stream in place, because a still tells you the encoder was up a moment ago while only moving video tells you it's flowing right now. The still is taken on demand and thrown away; it is never stored and never counts toward your storage. A channel that isn't broadcasting keeps showing a frame from its last replay instead, and a channel that is on air never shows one: an old frame under a Live badge would look like your current picture. See Live streaming.
- The Live page now shows you the broadcast, not a row in a table. A stream that was on air with a thousand people watching used to look exactly like a channel that had never been switched on: the same table row, the same small badge, sorted by the date the channel was created. Every stream is now a card carrying its own picture, and anything with an encoder connected is lifted to the front, whichever page of the list it would otherwise be on, and outlined in its status colour. An on-air card shows what you actually want mid-broadcast: people watching right now and how long the encoder has been connected sit on the picture, with the resolution and bitrate it's sending under the title. None of it appeared anywhere on this page before. Hover the picture to watch the broadcast in place. Go live and End are on the card too, so a stream waiting in preview no longer has to be opened before you can start it. Channels that aren't broadcasting show whether they're public, when they last went out and their replay count, plus a frame from their most recent replay — an on-air card deliberately doesn't, because an old frame under a Live badge would read as your current picture. The numbers are read when the page loads; the stream's own page still refreshes them live as you watch it. See Live streaming.
- Fixed: live streams opened paused. Every viewer had to press play. A live embed is meant to start playing the broadcast on its own, muted, as soon as it has picture. It never did. A browser will start a muted video by itself only once it judges it has enough of the file buffered to play through — which a live broadcast, by its nature, never has: the next second doesn't exist yet. So a live player only ever starts because the page explicitly tells it to, and ours told it to one instant too early, before the streaming engine had attached the broadcast to it. That request was cancelled, nothing tried again, and the player sat on a still frame, paused, until the viewer pressed play. Pressing play always worked, which is what kept this quiet. The page now waits until there is picture to show before starting, and the same fix covers two places that weren't asking at all: the reconnect after a stream hiccups (which came back paused) and your own dashboard preview. On-demand videos were never affected — a complete file does buffer far enough ahead for the browser to start it. Nothing changed in the API.
- Fixed: when a media server failed mid-broadcast, viewers stalled for ~5 seconds instead of switching over. Every live stream is published to two servers, and the player is handed both, so losing one should cost a viewer a segment or so. Losing one actually cost them about five seconds of black. The player did its part, moving to the backup within about ten milliseconds of the failure — but the backup server refused it. A server only starts serving a stream once something has asked that server for it, and nothing ever had: we only ever asked the server that was already carrying the broadcast, so the backup was a name in a list rather than a place a viewer could go. Viewers recovered only because the player eventually gave up and rebuilt itself from scratch, which asked afresh and woke the backup on the way. Both servers are now readied when the stream is handed out, so the switch is what it was designed to be. Nothing changed in the API, and streams already running pick this up on their viewers' next page load.
2026-07-15¶
- Breaking: a video's storyboard is now read from
asset.default_storyboard; the?include=storyboardsexpansion and the storyboarddefaultflag are gone. A video only ever has one storyboard, so the old list was always a single entry whosedefaultwas alwaystrue. That storyboard is now always present onasset.default_storyboard(carrying itsidandvtt_link), with no include to request and nodefaultto check. If you asked for?include=storyboardsor read the storyboard'sdefaultfield, switch todefault_storyboard. The link itself is unchanged. See Posters and storyboards. - Delete confirmations now open in a pop-up instead of a separate page. Deleting a video, live stream, webhook, watermark, API key or subtitle track used to load a whole confirmation page (and a live stream fired a bare browser pop-up). Each now opens its confirmation in a modal over the page you are on, naming what you are deleting and what deletion removes, so you confirm or cancel in place and only the delete itself moves you (back to the list). Nothing changed in the API.
- Fixed: a video's embedded caption tracks could be listed in a different order each time. When a source file carries more than one embedded subtitle track, that list had no defined order, so it could come back arranged differently on different requests. The tracks are now always presented in the source file's stream order (the order they appear in the uploaded video). Videos with zero or one embedded caption track are unaffected.
2026-07-14¶
- Fixed: asking for auto-captions on a single upload did nothing.
metadata.auto_ttpicks the languages to auto-caption that one video in, and the API has always documented it as overriding your project's setting ("if set to NULL, will use the project settings"). It never did. The value was passed along inside the internal job that encodes the video, and the step that hands that job on to the encoder didn't carry it, so it was dropped every time and your project's setting was used instead. Your project's default hid it: the upload form is prefilled with it, so unless you actually changed the languages for a video, the setting that was ignored and the one that got used were the same one. It works now, on both direct uploads and fetch-from-URL: the languages you name for a video win, naming an empty list means caption that video in nothing at all, and leaving it out still inherits the project's. Clips and duplicates inherit it from the video they came from. Nothing changed in the API. - Uploads now start encoding the moment they finish, instead of up to a minute and a half later. The job that gets a video encoded used to be created when the upload began, before the client had sent a byte. It looked for the file, did not find it (of course), and put itself to sleep for 60 seconds — and nothing woke it when the file actually arrived. A finished upload sat in storage while nothing at all happened: encoding began 52 to 82 seconds after the last byte, with the machine idle for every one of them. The job is now created when the upload is completed, just before storage assembles the file, and run as soon as it has. Every direct upload is that much faster to become playable, and nothing changed in the API. Uploads that are started and never completed are still expired after 24 hours, and still release the storage they declared.
- Breaking: direct uploads are now cut into fixed 100 MB chunks, and each part is held to its exact length.
metadata.size, the file size you announce when starting an upload, is the number your storage quota is checked against and your invoice is based on, but nothing used to compare it with the bytes that actually arrived. The chunk size is now ours rather than yours, which means the length of every part follows from the file size alone: a full 100 MB chunk, except the last part, which is the remainder. Each part's pre-signed URL is signed for that exact length, and the storage rejects a body of any other, so no upload can come out bigger than the size it declared. The part URL response now carries that length assize, so your client can read it rather than work it out. At completion the parts are measured in storage before the file is assembled, so an upload that still comes out bigger than declared is discarded rather than completed, and its video is markederrored. If you split your uploads into parts of your own size, they will now be rejected by the storage withSignatureDoesNotMatch: cut them into 100 MB chunks instead (if you drive our endpoints with Uppy, setgetChunkSizeto 100 MB, since its default picks a size per file). The dashboard uploader and the Python script in our docs already do this. See Direct video upload. - Fixed: fetching a video from a URL ignored the file-size limit. A fetch takes the file's size from what the URL reports, and that size skipped the 20 GB limit every other upload is held to, so a URL reporting more than that was accepted. It's now a
422 upload_too_big. The download itself is bounded at the same limit, so a server that reports a small size (or none at all, which is allowed) and then keeps sending is cut off rather than writing as much as it likes. See Fetch a video. - Fixed: the uploader accepted files the API would then reject. The dashboard let you pick a file up to 21 GB while the API's limit is 20 GB, so a file in between passed the picker and failed the upload it had already started. Both read the same limit now. See Limits.
- Fixed: an upload that never finished kept counting against your storage. Starting an upload records the file size you declare for it, and if the file never arrived (the 24-hour window ran out), that size stayed on the books: the video was marked
erroredbut went on using up storage, and being billed for, a file that isn't there. Expiring an upload now releases it. - Fixed: an upload could be listed, aborted or completed from a project it didn't belong to. The
keythat says which upload a call acts on was taken at face value, so any project you could reach was a valid place to name someone else's upload from, and aborting one there destroyed the parts it had already received. Akeythat doesn't belong to the project in the URL is now a404. - Fixed: a mistake in the part list you complete an upload with came back as a server error. Completing with a part you never uploaded, a wrong
ETag, the same part twice, or a part under the 5 MB minimum failed with a 500 (and only after the whole file had been sent, because storage checks the part list at completion). Each of those is now a422that names the part that's wrong. Part order no longer matters either: it carried no meaning, and sending the parts in any other order used to fail. See Direct video upload. - Fixed: resuming an upload with more than 1000 parts re-sent parts that had already arrived. List Parts reported at most the first 1000 parts however many had been uploaded, so a resumed upload of a large file in small chunks was told everything past part 1000 was missing, and sent it all again. It now lists them all. See Direct video upload.
- Scrub-preview thumbnails are sharper in time, and cost less to make. The strip of thumbnails shown when a viewer drags the seek bar is now captured every 6 seconds instead of 7, and each tile is the exact frame at that point rather than the most representative frame from the second that follows it. Previously a tile could show content from up to a second past the time it was labelled with, so dragging to a cut landed slightly late; now the tile you see is the frame you get. Building the storyboard is also about 15x faster, which takes a step out of every video's processing time (most of a minute on a feature-length upload). Existing storyboards are unchanged; this applies to new encodes. See Posters and storyboards.
- Rapid-cut videos now encode with steadier quality. The content-adaptive encoder reads the video encoder's own first-pass statistics to judge how hard each section of a video is. A parsing gap made it skip one frame type: the extra keyframes that rapid scene cuts produce, common in trailers, sports reels, and montages. Sections containing those frames looked easier than they were, so they could be compressed a touch too hard and land slightly under their quality target. The parser now counts them, and a new generation of the quality-prediction model trained on the corrected data ships with it. Bitrates stay essentially the same (within a fraction of a percent across our benchmark sets); sections that were undershooting now land closer to their target. No action needed; applies to new encodes.
2026-07-12¶
- Clips keep the quality you already encoded, and their timeline is exactly even. A plain
clipnow builds itself from the source's existing segments — keeping every whole segment the cut spans exactly as encoded, and re-encoding only the partial segment at each end. Those two end segments are now encoded at the same bitrate as the rendition they came from, rather than at a fixed high quality, so they come out roughly half the size while matching the rest of the clip. Two rough edges of the old cutting step are also gone: the frame timeline could come out subtly uneven where a re-encoded end met the untouched middle, and a clip's reported duration could stop just short of its final frame. Short clips (under about 6 seconds, where there is no whole segment to keep) still re-encode their video, but their audio is now copied rather than re-encoded.fastclipis unchanged, and no API changed — this applies to new clips. See Clip a video. - The last seconds of a viewing session are less likely to go missing from analytics. When a viewer closes the tab, the player sends one final beacon with the session's remaining watch data. If the browser refused that hand-off (a per-page quota shared by all scripts on the embedding page), it was dropped silently — the session simply ended a few seconds early in your statistics. The player now detects the refusal and retries over a second delivery channel that also survives the tab closing, with a plain request as a last resort.
- Fixed: per-request delivery metrics were being dropped, and quality-tier analytics could mislabel a rendition. Three analytics columns were sized for small internal ids while the player actually reports full 64-bit rendition identifiers. Consequences, now fixed: the per-request latency/throughput samples the player batches were discarded whole at ingest (the data behind upcoming CDN latency breakdowns); bandwidth rows recorded a truncated rendition id; and a view's Renditions watched panel matched quality labels ("1080p") on a truncated key, which could in rare cases attribute watch time to the wrong rendition of the same video. New data records full identifiers; sessions recorded before the fix keep their truncated form, and the drill-down labels them on a best-effort match as before.
2026-07-11¶
- See every playback error your viewers hit, and click straight to the affected sessions. The Statistics page has a new Playback errors card: one row per error code with a sample message, how many views it affected, and what share of all views that is — respecting the page's date/video/country filters. Each row links into the per-view drill-down filtered to that exact code, so diagnosing "what is error 3 and who does it hit?" is one click: the list shows every affected session, and opening one shows the device, browser, network, and playback story around the failure. A healthy period shows a green check instead. See Statistics.
- Drill down to any individual view. The Statistics page has a new Views section: every playback session as its own row — when it started, who watched, how far they got, startup time, stalls and errors — with search by viewer id and a one-click Errors only filter. Click a row for the full story of that single playback: per-view quality-of-experience scores (viewer experience, playback success, startup, smoothness, video quality — each graded on the industry-standard 0-100 scale), a milestone timeline with a heat strip of exactly which seconds were watched (replays show darker), startup broken down into page load / player load / first frame, every error with its code and message, the renditions played, and the viewer's device, browser, location, connection, and the page your video was embedded on. Built for "it didn't play for me" support requests: search the viewer's id and the whole session is on one page. See Statistics.
- HDR videos now encode with the standard quality settings instead of the content-adaptive predictor. The per-title quality model was trained exclusively on SDR content and its quality probes measured HDR frames without the HDR-to-SDR conversion the real encode applies — so its choices on HDR uploads were unvalidated. HDR sources now use the fixed, well-tested quality settings until the model gains HDR training data. SDR videos are unaffected.
- Long videos start encoding sooner and quality analysis is more consistent. The per-title quality analysis that runs before encoding now processes each section of a long video in parallel instead of scanning the whole file first, so long uploads spend less time waiting before their renditions appear. Alongside it, a new generation of the quality-prediction model ships: it fixes an inconsistency that could make some film-like content encode with unnecessary quality swings between sections, and tracks each rendition's quality target slightly more safely (a fraction of a percent more bitrate on the higher renditions in exchange for fewer segments dipping below target). No action needed; applies to new encodes.
- Private live streams now control who can watch the embed, matching private videos. A private stream's embed page used to be open to anyone with the link (the expiring signed playback URL inside was the only protection). It now requires viewer access, exactly like a private video's embed: a signed token link (
/et/live/{project_id}/?token=…, minted with your existing signing keys — the JWT'ssubis the live stream id), an optional stream password (set it in the stream's settings; viewers append?password=…), or a logged-in account with project access (your dashboard preview keeps working untouched). Public streams are unchanged. See Live streaming. - Broadcast-embedded captions (CEA-608/708) pass through live streams. Encoders that embed closed captions in the video signal deliver them to viewers unchanged — they appear in the player's CC menu, with no AI transcription billed.
- Live streams now survive a server failure. Every stream gets a backup ingest URL (same stream key) on a second server. Encoders that push to both get automatic viewer failover: if a server dies mid-broadcast, players switch to the backup within seconds, the broadcast stays live, and the replay is exported from whichever server recorded the most. On the API as
ingest.backup_rtmp_url. - Live streams can now caption themselves. Turn on Auto-generate live captions in a stream's settings and viewers get an AI-transcribed caption track in the player's CC menu while you're live. Captions trail the audio by roughly 10-20 seconds (like broadcast TV captioning), the language is auto-detected (or pin it with Caption language), and transcribed minutes count toward your AI usage. Available on every plan; also on the API as
captions/caption_language. - Viewers can now rewind your live streams. Live broadcasts carry a ~30-minute rewind window: viewers can scrub back (pause, replay a moment) and click the LIVE badge to jump back to the edge. On by default; a DVR toggle in the stream's settings (and
dvron the API) pins viewers to the live edge instead. The longer window adds ~10 seconds of broadcast latency. - See how many people are watching your live stream, right now. While you're live, the stream's page shows a live viewer count next to the status badge (updated every few seconds), alongside the encoder health strip. Counts are real-time concurrent viewers — someone who closes the player drops out within about 40 seconds.
- Preview your stream before going live. Turn on Preview before going live in a stream's settings and connecting your encoder no longer starts the broadcast: the stream parks in a new Preview state that only you can see (the stream's page shows the live picture; viewers see it as idle). Check your framing and audio, then click Go live — that's when viewers can watch, webhooks fire, and the recording starts. Stopping the encoder mid-preview quietly returns to Idle with nothing saved. Also on the API:
preview_modeon create/edit andPOST …/go-live. - Live streams now show encoder health while broadcasting. The stream's page displays a live strip next to the status badge — resolution, inbound bitrate, and how long the encoder has been connected (e.g.
1080p · 2.6 Mbps · up 14m) — refreshed every few seconds while you're live. The same data is on the API as a read-onlyhealthobject on the live stream (null unless live). Useful for spotting a struggling uplink before your viewers do. - Live playback now rides our global CDN. Live HLS is delivered through the same CDN as your videos instead of straight off the media server, so large audiences stream smoothly — the origin serves each segment roughly once no matter how many people watch. Playback URLs changed shape with this: private streams keep a signed link that expires (grab a fresh one from the API or dashboard, or just use the embed — it refreshes itself), and public streams now carry a long-lived access token in the URL, so the link you share stays stable. If a public link ever leaks somewhere you don't want it, Reset stream key invalidates it immediately. Embeds and the API are otherwise unchanged.
- Live streaming is now included on every plan. The Start plan includes 1 live stream, Pro includes 3 (up from 1), and Advanced keeps 5. The limit counts the live streams that exist in your account, not broadcasts — a stream is reusable and can go live any number of times. See Live streaming.
2026-07-10¶
- Fixed: TV and broadcast recordings are no longer encoded at double their real frame rate. Broadcast MPEG-2 sources (over-the-air/cable captures and some camera formats) declare a field rate that is exactly twice the real frame rate. We were reading that number, so a 30fps recording was encoded as 60fps with every other frame an exact duplicate — roughly 40% more bytes for zero added detail, paid on storage and on every byte streamed, plus a higher bitrate tier it didn't need. We now read the true average frame rate. This only affected sources that mis-declare their rate this way; ordinary uploads are unchanged. Re-upload an affected video to get corrected, smaller renditions.
- Sources larger than 8K are now refused at upload, with a clear error, instead of failing partway through processing. A video's frame dimensions — not just its file size — now have a limit: a source whose frame is bigger than 8K (7680×4320) is rejected the moment it's uploaded rather than accepted and then failing or stalling during encoding. This only affects genuinely enormous sources — 8K and everything below is unchanged, in any orientation, so a portrait 8K clip is still fine. The reason it matters: a tiny file can claim huge dimensions (a flat frame compresses to a few kilobytes), and processing it would try to allocate the full frame in memory.
- Fixed: HLG videos — the HDR format phones and broadcast cameras record — no longer come out washed out. HeapStream converts HDR sources to standard dynamic range so they look right on ordinary screens. It recognised only one of the two HDR formats in common use (PQ, what HDR10 files and cinema masters carry) and passed the other, HLG, through untouched, so those uploads played back grey and flat. Both are now detected and converted. Re-upload an affected video to get corrected renditions.
- Fixed: a source file whose embedded title, description or other text carries a line break is no longer misread. We read a video's properties — resolution, rotation, which tracks it has — from a report that the media tooling prints. A line break inside one of the file's own text fields could run into that report and be taken for a property, so such a video could be encoded at the wrong rotation or size, or rejected as having no video track at all. Those fields are now read in a format that keeps a file's text separate from its properties.
- Fixed: portrait videos recorded on a phone are no longer encoded at a third of their resolution. Most phones record landscape and attach a "rotate this on playback" instruction rather than storing the frame upright. We were reading the stored dimensions and missing that instruction, so a portrait video looked landscape to the encoder and every rendition was scaled to the wrong axis — the 1080p rendition of a 1080×1920 clip came out 608×1080, and the resolution shown on the video's Edit page was sideways. Rotation is now read from the source, so these videos encode at their true size, like any other portrait upload. Videos uploaded before this fix keep their original renditions; re-upload to get the corrected ones.
- Edit captions and chapters in the browser. Fixing a single typo used to mean downloading the VTT, editing it locally, deleting the track, and uploading a new one — which handed the track a new ID and dropped it from any player that had it selected. Now the Captions and Chapters panels on the Video Edit page have an Edit action that opens a caption editor next to a preview player. Each cue is a row: click its number to jump the player there, hit ⤓ to set a start or end time from the current playhead, and Add cue, Split, or Undo as you go. −0.5s / +0.5s shifts every cue at once, the fix for a track that's uniformly early or late. The cue under the playhead highlights as the video plays, an empty cue or an end-before-start is flagged inline, and Save stays disabled until the track is valid. Chapters get their own rules: they can't overlap, the first must start at
00:00, and adding one splits the chapter you're inside. Everything is also available over the API —GETandPUTon…/text_track/{track_id}/contentread and replace a track's WebVTT body, keeping its ID, title, language, and default flag. The body is validated exactly like an upload (VTT or SRT, detected by content, up to 10 MB, SRT converted to WebVTT), and an invalid body is rejected without touching the stored track. Every cached copy of the old text is invalidated automatically: the CDN URL, burned-in progressive MP4s, and the HLS caption playlist. See Text tracks.
2026-07-06¶
- Rate-limit information now comes back as standard
X-RateLimit-*headers. Every API response used to carry a singleRATE_LIMITheader holding a JSON object, a shape no generated SDK could read as typed values and that no other API returns. It's now split into the conventional scalar headers:X-RateLimit-LimitandX-RateLimit-Remainingon every response (plusX-RateLimit-Concurrent-LimitandX-RateLimit-Concurrent-Remainingon endpoints that cap concurrency), so any HTTP client or SDK can read them directly. A429 Too Many Requestsadditionally returnsX-RateLimit-Reset(Unix time when the window resets) and a standardRetry-After, which most HTTP libraries honour automatically. See Limits. - Live streaming is here. Broadcast live over RTMP with adaptive HLS playback, straight from the dashboard or the API. Create a live stream to get an RTMP server URL and a secret stream key, point OBS (or any encoder) at them, and click Start Streaming. Your stream goes live automatically, no separate "go live" step. Streams can be public (open link) or private (a signed, expiring playback link), and the new Live section shows a preview player, the encoder connection details with a masked/one-click-copy stream key and OBS instructions, a copyable embed code and playback URL, and a live status that updates as your encoder connects and drops. Turn on recording and each broadcast is saved to your video library as a normal video (with captions, storyboards, downloads, and analytics) when it ends. If a stream key ever leaks, Reset stream key rotates it. Webhooks fire
livestream.started/.ended/.replay_ready(and more), and the same operations are available under/api/v1/project/{project_id}/live-stream. Availability depends on your plan. See Live streaming.
2026-07-05¶
- Every video now carries a
previewreel URL, and public videos get a shareable one that never expires. The short hover-preview reel (moments sampled from across a video and played back-to-back, generated on the fly from its existing segments with no extra encoding or storage) is now apreviewfield on every video the API returns from Get video and List videos, instead of a separategetVideoPreviewrequest. For public videos thepreviewURL is fixed and never expires: its access token lives in the path, so it's unguessable yet cacheable at the CDN and safe to hand to any HLS player or drop straight into a page. Private videos keep a signed, expiring URL.previewisnullwhile a video is still uploading or is audio-only. Options to customize the reel's length and sampling will return in a later release. The standaloneGET …/video/<id>/previewendpoint has been removed. See Hover Previews.
2026-07-03¶
- The embed-code dialog now shows the code up front, and copies with one click. Opening Embed for a video used to show an empty box and a "Get embed code" button; now the code is generated the moment the dialog opens and updates instantly as you change options, shown on a dark panel with its URL highlighted. A small thumbnail, title and details (duration, resolution, and whether it's public or private) tell you exactly which video you're embedding, and private videos get a clear note that the code carries their access token. Sizes are one-click pills, including a new Responsive option that scales to fit any page (what YouTube and Vimeo hand out), and a single Copy code button replaces the manual "copy the code above".
- Upload from a list of URLs, and get an honest report. The video-upload page got a visual refresh: the drop area (and the new URL box) are now a clean dark "stage", the two tabs became pills, and the per-upload settings read more clearly. Bigger change: "From a URL" now takes many links at once. Paste one URL per line and each becomes its own video. After you fetch, a Fetch results card shows exactly what happened: an Added to your library list (each linking to its video) and, when some links don't work, a Couldn't be fetched list with the reason for each and a "Put back in form" button to fix and retry. A single URL still works exactly as before.
- The Player editor's settings and preview now match its polished logo panels. The Player settings and Player preview cards were still the old plain style while the three logo uploaders had already been refreshed; they now share the same look (a labelled icon, a one-line description, solid inputs, and a Save changes button), so the whole Player Edit page reads as one consistent design. Nothing about what the settings do changed.
- The Billing page got a clearer redesign. With a subscription, your plan now sits on a dark hero banner showing the price, when it renews ("Renews Jul 12 — in 12 days"), and since when you've been subscribed, with an Active status dot, or a clear prompt to update payment if something's wrong. Storage has a status-coloured bar that warns as it fills, and this billing month's usage is three clean tiles (Bandwidth, Encoding and AI generations), each marked Unlimited and linking to Analytics for the full breakdown. Without a subscription, the pricing page is now "Pick your storage": every plan leads with how much you host (bandwidth, encoding and AI are unlimited on all of them), the shared features are listed once underneath, and the popular plan stands out on-brand instead of stock blue.
- Switch between projects right from the sidebar. The top of the sidebar now shows the project you're in (its initial, name, and your role), and clicking it opens a project switcher listing every project you can access, with All projects and New project shortcuts. Switching used to mean going back to the dashboard and finding the card. The sidebar is also clearer about scope: a Project section (Videos, Players, Users, Statistics, Settings) and an Account section (All projects, Analytics, Invitations, Account) are always labelled, and the project section now stays visible on account pages, so your project is always one click away. ("Dashboard" is now named All projects, which is what it always was.)
- The Analytics page now surfaces your processing & AI usage. Below delivery and storage, a new section shows what your account asked the pipeline to do: tiles for Encoding hours (with rendition and video counts), AI captions, AI translations, and AI chapters, each with its trend, plus a Processing activity chart (encoding hours per day and AI generations per day) and an AI languages breakdown of the caption and translation tracks you've generated. It's all drawn from usage your account already records; an account that hasn't used encoding or AI won't see the section.
- The account Analytics page is clearer and no longer mixes two scales on one chart. The bandwidth-and-storage chart used to plot a per-day flow (bandwidth) and a running level (storage) on two different y-axes in the same plot, a misleading overlap. They're now two stacked panels in a Delivery & storage card, each with its own scale. Above them sit three at-a-glance tiles: Bandwidth served (with the change vs the previous period), Storage today (and how much it grew), and your Busiest day. The filters moved to a row on top with one-click 7d / 30d / 90d presets. Same numbers, just far easier to read.
- The video Edit page is easier to scan and act on. Each collapsible panel now has an icon and a one-line description, and shows a count at a glance: how many Posters and Downloads you have, how many Captions (with a "1 processing" flag while AI captions generate), and so on. The three creation tools (Create clip, Duplicate video, Swap video) are grouped under a Tools heading at the bottom. On the right, the preview and the video's details are now one card: the player at the top, then a Ready / Public status line, a clean list of facts (duration, created, source resolution & codec, storage, the exact renditions viewers get, and the video ID with a copy button), and Embed / Stats / Download buttons, with a quiet Delete video at the very bottom. Nothing you can do changed, only how it's laid out.
- The project Statistics page is now a proper analytics dashboard. The filters moved out of the side panel into a row at the top: one-click 7d / 30d / 90d presets beside a date picker that applies as soon as you pick a range. The headline tiles lost their coloured strips for a calmer look. The main chart now shows views and plays at full scale (watch time gets its own small panel beneath, so counts and minutes no longer fight over one axis) with a dashed previous-period line so you can see whether this week is normal. The completion funnel is a card of its own; each stage shows how far plays get and the drop-off to the next. Top videos now show a thumbnail and a bar you can compare at a glance, top countries show a flag, and big watch-time numbers read in hours. Nothing about how stats are collected changed.
- The Invitations page is clearer about what you're accepting. Each project you've been invited to is a row with the project's initial on a dark tile, its name, and who invited you ("Invited by …"), with a blue Accept invitation and a plain Decline beside it. A one-line note explains that accepting gives you access to that project, and when you have no invites you get a tidy "No pending invitations" message instead of a bare sentence.
- The project's Users page now reads like a team roster. Each member is a row with an avatar (their initial, tinted by state), their email, and their role, including your own row, marked "· you". A quiet badge shows whether they're Active, a Pending invite, or Inactive, and the member count sits at the top ("4 members in this project"). Suspending someone is now a subtle icon instead of a loud red button on every row, while reactivating a suspended member stays a clear Enable access button. The invite page is simpler too: one email field with a note that they'll get an invitation link. Who can do what is unchanged.
- The Players page now shows what each player looks like. Instead of a plain table of names, every player is a card that opens with a little preview of that player's own configuration, drawn live from its settings: whether it shows a title bar, your logos placed in the corners you chose, and a control bar with a play button and progress bar. A minimal player shows a bare frame; a branded one shows its logos where they'll appear. Below the preview: the player's name (with a Default tag on your project's default), its description, and an Edit player button. A dashed Add a player card ends the grid. The dead, permanently-disabled delete button is gone.
- Your projects are now a visual grid instead of a table. The account's All projects page shows each project as a card (a dark tile with the project's initial, its name, and its video count) with a big Open button plus quiet gear (settings) and trash (delete, for projects you administer) icons. A dashed Create a new project card sits at the end of the grid, and stands in as a friendly empty state when you have none. The old page's three loud ALL-CAPS buttons per row are gone; nothing about the projects themselves changed.
- The Webhooks page now tells you which endpoints are healthy. Each webhook is shown as a row with its URL, when it was added, and, the important part, a health badge: a green Healthy, or a red Failing since
· N retries when we've been unable to reach it. A broken integration finally announces itself instead of failing silently. The page also explains itself in one line ("Video lifecycle events are sent as JSON POSTs to every URL below."), the Add webhook button moved to the top, and the add form has a clearer prompt, an example URL, and an Add webhook button. Delivery and retries work exactly as before. - The Watermarks page now shows each watermark, not just its numbers. Instead of a table of raw values, every watermark appears as a preview on a dark video-like stage, with your logo exactly where it will sit on the frame (corner or centre) at its real size and opacity, beside its name and a plain-language summary ("Lower right · 80% opacity · 15% scale · 5% padding"). The name links out to the full-size image, delete is a quiet trash icon, and an empty project gets a friendly prompt to add one. The Add watermark button moved to the top of the page, and the create form leads with a short explanation and a Create watermark button. What watermarks do is unchanged; you can finally see them, that's all.
- API keys are easier to match and manage. The account's API keys page now shows each key's key ID in the list (the same value your API client puts in its config), so you can tell which row a given server is using, next to its label and creation date. The meaningless always-"Active" status column (keys can't be set to anything else) is gone. Creating a key is tidier too: its fields read Label / Key ID / Key secret (in monospace, with copy buttons) and the button is now Save API key. Nothing about how keys work changed.
- The Account settings page has been redesigned. It's now two clear cards side by side: Profile, with your Gravatar avatar, the email you sign in with, and your Country (used on invoices and for VAT); and Password, with Current / New / Confirm fields and a strength hint. Each card has its own Save profile / Change password button. Labels are sentence case and inputs match the rest of the dashboard. New passwords now need at least 8 characters (up from 6). The New project page got the same treatment: a single focused field with a one-line explanation of what a project is.
- The project Settings page is reorganized and explained. The eight settings that used to be one long, unlabeled list are now grouped into cards: Project (name, description, timezone), Playback (published, default player) and Encoding defaults (encoding tier, auto-generate captions, keep originals). Every setting carries a one-line note on what it does ("Unpublishing blocks playback of every video in the project.", "Used for statistics day boundaries.", and so on). Deleting the project now has a clear home here too: a Delete this project box at the bottom of the Project card (visible to the project owner). The settings themselves are unchanged; only the layout and descriptions are new.
- Delete and access-change confirmations now show what you're about to do, and let you cancel. The pages that confirm a deletion (a video, project, watermark, webhook, caption track or API key) or a team access change (suspend or enable a member) used to be near-empty: a bare title and a red button, with no way out except the browser's Back button. Each now leads with what is affected (a video on its thumbnail with its duration, resolution and size; a project by its initial; a webhook by its URL; a teammate by their avatar), states the consequence in one plain sentence ("…and every embed of it stops playing immediately."), and adds a Cancel button beside the action. What the buttons do is unchanged; it's now clear what you're confirming, and easy to step back.
- The Videos page has a new gallery view, and it's the default. Your library now opens as a grid of large thumbnails (like a video platform's library) instead of a table: six across on a wide screen, each tile showing the poster with the duration in the corner, a lock chip on private videos, and the hover preview playing right on the tile. Hovering also reveals quick actions (edit, embed code, analytics), and a ⋮ menu on every tile carries the full set including delete. The familiar list view is one click away via the list/grid toggle at the top of the page, and the page remembers which view you prefer. See The Videos page.
- Select several videos and act on them at once. In the gallery view, tick the checkboxes that appear on tiles (or Select all in the header) and a floating bar offers Make public, Make private, and Delete for the whole selection, with a confirmation step before anything is deleted. Until now visibility and deletion were one-video-at-a-time. See The Videos page.
- The videos list is easier to scan. The list view's thumbnails now double as status: a video that's still encoding shows a spinner on its dark tile, a waiting upload shows an upload glyph, and a failed one is tinted red with a warning mark, visible at a glance before you read any badge. Rows also gained the video's resolution ("4K", "1080p", …), visibility became a quiet icon next to the other facts instead of a second colored badge, and the search, visibility and status filters moved from the side panel to a bar at the top of the page, where the visibility/status dropdowns apply immediately on change (no Search button). The pager now says how many videos are on the page, with labeled Previous/Next buttons.
- Clip metadata is now honest about what a clip can control. The
clip/fastclipendpoints used to accept the full upload metadata object (includingsize,watermark_id,encoding_tier,normalize_audio) and silently ignore all of it: a clip's media derives from the source's already-encoded renditions, so those settings can't be chosen per clip. The clip request now documents only what applies (title,public), the clip inherits the source video's watermark, encoding tier, and audio-normalization metadata, and the dropped fields are still tolerated in requests so existing API clients keep working unchanged. See Clip a Video. - Fixed: deleting a fast clip's source from the dashboard is now blocked, and clipping a video being deleted at the same instant can't strand the clip. The rule that a source video can't be deleted while fast clips reference its segments was only enforced on the API endpoint; the dashboard's delete page bypassed it. The guard now applies everywhere (the dashboard shows the same "delete the clips first" warning the API returns), and a delete racing a fast-clip job in its first seconds now cleanly fails the clip instead of leaving it pointing at media scheduled for cleanup.
- Fast clips now reuse the source's audio too, including the low-bitrate HE-AAC rendition. A
/fastclipclip already reused the source video's encoded segments byte-for-byte; its audio boundaries, however, were re-encoded, and the HE-AAC (aac_60) rendition had to be fully re-encoded every time because re-encoded boundaries corrupted its decode. Fast clips now cut the source's own audio at frame granularity instead of re-encoding anything, so every audio rendition (HE-AAC included) is reused bit-for-bit: clips store even less, finish faster, and the clip's audio is identical to the source's. No API changes; existing clips are unaffected. - Fixed: a fast clip cut very close to a segment boundary no longer drops a fraction of a second. When a
/fastclipcut started within 0.25s before (or ended within 0.25s after) an internal segment boundary, up to 0.25s of frames could silently go missing at the seam and the clip's start could play with lip-sync up to 0.25s off for a few seconds. Such cuts now produce exactly the frames requested. - Fixed: fast-clipping past the end of a shorter audio track no longer misdeclares the clip's audio length. If a video's audio ended before the video (a trailing video-only stretch) and a fast clip's end time reached into that stretch, the clip's audio playlist declared several seconds of audio that didn't exist, which could confuse players near the end of the clip; the clip's audio track also reported the full clip length in the API. Both now report exactly the audio that exists.
- Get a video's preview reel via the API. The hover previews introduced yesterday are now available to your own apps:
GET /api/v1/project/<project_id>/video/<video_id>/previewreturns the URL of an HLS playlist that plays a short reel of moments sampled across the video (configurable stop count, sampling window, and per-stop duration), generated on the fly from the video's existing segments with no extra encoding or storage and playable in any HLS player. See Hover Previews.
2026-07-02¶
- Hover a thumbnail in the Videos list to preview the video. Resting the pointer on a video's thumbnail in the dashboard's Videos list now plays a muted looping preview in place of the poster: a short reel of ~2-second moments sampled evenly across the whole video, so you can recognize a video without opening it. The reel is generated on the fly from the video's existing encoded segments and served through the CDN: no extra encoding, no extra storage, nothing to configure. Previews appear once a video is ready to play; while it's still encoding you'll see the poster as before. See Hover Previews.
- The embed player's control-bar clock now shows time remaining; click it to switch. The time readout to the right of the scrub bar used to show the video's total duration; it now shows the time remaining and clicking it (or pressing Enter/Space while it's focused) toggles between remaining time and total duration. This comes with a refresh of the player's underlying engine (Video.js 10), which also brings smoother menu and overlay animations, a cleaner buffering spinner, and snappier volume feedback. Everything else (the controls, menus, logos, overlays, and your player settings) is unchanged.
- Zoom panning now works on touch screens. When the player's Zoom menu is enabled and a viewer magnifies the video, dragging to pan around the zoomed picture previously only worked with a mouse; touch devices couldn't pan at all. Panning now works with a finger (or pen) too, and the picture re-clamps to its edges when the player is resized or switches in and out of fullscreen while zoomed. No settings changed; this applies wherever the zoom menu is enabled.
- The Share dialog is friendlier to keyboard and screen-reader users. Opening the player's Share dialog now moves keyboard focus into it, Tab cycles through its buttons and fields without escaping behind the dialog, Esc still closes it, and closing hands focus back to where the viewer was. It's also announced as a proper modal dialog to assistive technology. The dialog's look and its share options are unchanged; clicking the video title to open its link also no longer hands the opened page a reference back to the player page (a small tab-hygiene hardening).
2026-07-01¶
- Choose a specific player per embed with
?player_id=. By default an embed renders with the project's default player; you can now override that per embed by addingplayer_id=<player_id>to the embed URL, on both the public embed (/e/…?player_id=…) and the private token embed (/et/…?token=…&player_id=…). The player must belong to the project; an unknown or foreign id simply falls back to the project's default player rather than failing to load. Analytics record whichever player actually rendered. See Secure video playback. - Fixed: autoplay now works on the private (token) embed. Adding
autoplayto a private video's token embed URL (/et/<project_id>/?token=…&autoplay=1) had no effect: the token embed redirects to the token-less player page, and that redirect was silently dropping the query string, so the autoplay flag never reached the player. The redirect now preserves it, so a private token embed autoplays just like the public embed does. As always, browsers block autoplay with sound, so pair it with a muted player if you need playback to start reliably. Public embeds were unaffected. See Secure video playback. - Fixed: replacing a player logo now takes effect right away. When you uploaded a new logo (the in-player logo, the control-bar logo, or the title logo) over an existing one, the CDN could keep serving the old image for a long time, because a logo keeps the same storage path when replaced and our CDN caches by path. Logo URLs already carried a version marker to bust the cache, but the media CDN was ignoring it. The CDN now varies the cached logo on that version marker, so a replaced logo refreshes on the next load with no manual cache purge. Only logos are affected; video, thumbnails, and captions cache exactly as before. No action needed.
2026-06-30¶
- The player editor's logo panels have been given a visual refresh. The three logo uploaders on the Player Edit page (In-player logo, Control-bar logo, and Title logo) are now presented as polished, consistent cards. Each leads with a labeled icon and an Active / Not set status, previews your logo on a dark, video-like stage (with a faint checkerboard so transparent PNGs read clearly), and pairs it with a click-or-drop upload area that lights up as you drag an image over it. The in-player logo's corner picker is shown as a small player frame whose chosen corner fills in. The uploads, the live preview, the position setting, and everything these panels do are unchanged; only the presentation is nicer.
- Capturing a poster from a chosen frame is faster, and now works for clips. The dashboard's Capture from video panel (Video Edit → Posters) grabs the frame straight from the video's HLS playlist and seeks directly to the segment containing your chosen time, instead of scanning the whole rendition, so the capture returns quicker, especially for long videos. It also now captures correctly from a clip, whose media reuses the source video's segments; previously a capture at a time inside a reused part of a clip could fail. The captured frame is unchanged.
2026-06-29¶
- The embed player now opens with its controls showing, not a large center play button. The player used to display a big play button in the middle of the video and only reveal the control bar once you started playback. It now starts with the full control bar visible over the poster image (the scrub bar, time, volume, the settings ⚙ menu and fullscreen) and no center play button; press play from the control bar to start. The rest of the player (logos, title overlay, enabled controls) is unchanged. Nothing to configure.
2026-06-26¶
-
The embed video player is now a fully first-party build. The player has been rebuilt on plain video.js ⧉ with our own controls and styling, replacing the third-party nuevo plugin, and every feature carries over: quality, speed and zoom selection and the share dialog, gathered into a single settings (⚙) menu on the control bar (as before); captions and chapters; the storyboard scrub-bar preview; the in-player and control-bar logos; the title overlay; resume-from-last-position; keyboard shortcuts; and zoom drag-to-pan of the magnified video. The control bar keeps the familiar layout: current time, scrub bar, duration, then volume, the settings cog and fullscreen on the right. Your existing player settings (logos, enabled controls) keep working unchanged. This removes a third-party dependency from the playback path. No action needed.
-
Fixed: captions on a fast clip are no longer out of sync. When you made a fast clip (
/fastclip) of a video that had captions/subtitles, enabling them in the player showed them badly out of sync with the audio, shifted by the clip's start offset. A fast clip reuses the source's encoded segments by byte-range, so its media keeps the source's original timeline, but the clip's caption file had been rebased to start at zero, and nothing told the player how to line the two up. The clip's captions now carry an HLS timing marker (X-TIMESTAMP-MAP) that pins them to the reused media's timeline, so they play in sync. This affects fast clips only (full re-encoded/clipclips and ordinary videos were always in sync), and only newly created fast clips; re-create an existing fast clip to fix its captions. Chapters and the downloadable caption file are unchanged. See Clip a video. - HLS segments are now 6 seconds (down from 8). Newly encoded videos are split into 6-second HLS segments instead of 8-second ones. Shorter segments let the player switch quality (ABR) and respond to seeks a little faster, at the cost of marginally more playlist entries; the forced-keyframe interval moves in step, so every segment stays independently decodable. Already-encoded videos keep their existing segments until re-encoded; playback is unaffected either way.
- Scrub-bar preview thumbnails now avoid blank/black frames. The little preview images shown when you hover or drag the player's seek bar (the storyboard) are captured at a fixed interval across the video. Previously each one grabbed the exact frame at its timestamp, which could land on a fade, scene cut, or otherwise blank frame. Each thumbnail is now chosen as the most representative frame from the first second of its interval, skipping transient blank frames in favor of one with actual content, the same improvement already made to auto-generated posters. The thumbnails stay on the same fixed timeline (each still covers the same moment of the video), so seeking is unchanged; only the picture is a little better.
2026-06-25¶
- The dashboard now shows live encoding progress per quality. While a freshly uploaded video is still processing, the Video Preview panel on its Video Edit page shows live encoding progress in place of the player: a progress bar per quality (
1080p,720p,480p, …) plus an overall percentage, updating on its own with no need to refresh. Because encoding is two-pass, the moment the essential qualities are ready the player appears and you can watch, while a slim strip beneath it keeps showing the higher qualities still being prepared; each turns green as it finishes, and the strip disappears once everything's done. This surfaces, in the dashboard, the same per-rendition progress now available on the API'sencoding_progressfield. See Use Encoding Tiers. - See encoding progress per quality level while a video processes. The single-video API response (
GET …/video/{video_id}) now includes anencoding_progressfield (a map from preset id to a 0–1 fraction) for each rendition that's still encoding, so you can show a live per-rendition progress bar. It's populated only while the video is still processing (statusqueued/ready) and isnullonce encoding has finished; it's omitted from the videos list response to keep that response cheap. A rendition that's already done reads as1. - Long videos now encode in parallel, resumable chunks. Under the hood, a long video's renditions are encoded in fixed-length time chunks that run in parallel on the worker and are staged to object storage as they finish. If an encode is interrupted (a transient failure, a worker restart) it resumes by re-encoding only the chunks that hadn't completed yet, instead of starting the whole video over, so long videos finish sooner and recover cheaply. The chunks are deleted automatically once the video completes. Shorter videos are unaffected (they still encode in a single pass), and the output renditions are byte-for-byte the same as before.
2026-06-23¶
- Public videos' MP4 download links are now unguessable. A public video's downloadable progressive-MP4 URL (the preset's
static_mp4_link, handy as a direct download or a raw<video>source) used to be a fixed path built only from the project, video and preset ids, so anyone could reconstruct it by guessing those ids. The URL now ends in a token derived from the video's password, so it can no longer be reconstructed from the ids alone, while staying a permanent, shareable link that needs no signed, expiring token. Keep reading the link fromstatic_mp4_linkas before; private videos are unchanged (they still get a signed, expiring URL). Changing a video's password rotates its public MP4 URL, and a request carrying a wrong or stale token returns404. An empty password means no protection, the same as for the password-gated embed page. As before, the CDN caches these URLs, so rotating the password or unpublishing has a propagation delay. See Secure video playback. - You can now upload your own chapter track from the dashboard. The Video Edit → Chapters page gains an Upload a chapter file panel alongside Generate chapters with AI, so you can add hand-authored chapter markers (a WebVTT or SRT chapters file) instead of generating them from captions, which is handy for editing AI chapters (download, tweak, re-upload) or for videos without captions. As with generated chapters, a video can have only one chapter track, so the upload panel is hidden once a chapter exists (delete it to replace it). Uploading a chapter file was already possible through the API ("Create text track" with type
chapter); this restores the equivalent in the dashboard. See Auto-generated chapters.
2026-06-22¶
- The Billing page now shows your encoding and bandwidth used this month. On the Subscription Details card, the Encoding and Bandwidth tiles still show Unlimited (your plan doesn't cap them), but each now also reports how much you've actually used in the current billing month, matching the monthly figure already shown for storage. The usage covers all your projects for the month to date; the limits are unchanged.
- The interactive API reference has a new look and a built-in API client. The reference page (linked as Stream API Reference in the docs nav) is now powered by Scalar ⧉, replacing the unmaintained RapiDoc. Everything you had before is still there: browse every endpoint and schema, search, download the OpenAPI spec, and (when you're signed in with an API key) try requests live with your key pre-filled. New on top of that: ready-to-copy code samples in many languages (cURL, Python, JavaScript, Go, Java, C#, PHP, Ruby, …) for every endpoint, a faster full-text search (⌘/Ctrl-K), and a light/dark theme toggle. The deep-links throughout these docs jump straight to the relevant endpoint, as before.
- A poster can no longer be added to a video that failed to encode. Once a video's processing has terminally failed (status
errored), adding a custom poster for it is blocked: there's no playable video for the poster to front. This covers both ways to add one: the Upload new poster panel (and its REST endpoint, which now returns422 video_errored) and the dashboard's Capture from video panel. Both panels gray out on the Video Edit → Posters page with the reason. Posters can still be added to videos that are processing or ready, as before. - A text track can no longer be added to a video that failed to encode. Likewise, once a video's processing has terminally failed (status
errored), uploading a subtitle/caption/chapter track for it is blocked: there's no playable video for the track to accompany. This applies to the Upload a text track panel on the Video Edit page (which grays out with the reason) and its REST endpoint, which now returns422 video_errored. Tracks can still be added to videos that are processing or ready, as before. - The Swap Video panel now grays out when the video can't be swapped. Swapping a video's content with another requires both videos to be completed, so the Swap Video panel on the Video Edit page now disables itself up front, with the reason shown, when the current video isn't completed (for example one that failed to encode, or is still processing), instead of only rejecting the swap after you pick a target and submit. The underlying rule is unchanged (it still re-runs on submit); this only makes the dashboard state it proactively, consistent with the Duplicate and Create Clip panels.
- Auto-captions can now target a specific audio track. Captions are generated per audio track, so a video with more than one audio track (for example a dubbed track, or a separate director's commentary) can be captioned track-by-track, including two tracks in the same language (e.g. a main English track and an English commentary), which previously collapsed into a single caption. By default the video's main audio track is captioned; pass
audio_track_idto the "Generate auto captions" API endpoint (POST …/video/{video_id}/text_track/generate) to caption a particular alternate track. Each generated caption now records which audio track it was made from, and deleting an audio track also removes the captions generated from it. Single-audio videos are unaffected. See Auto-generated captions.
2026-06-20¶
- The Billing page's Choose Your Plan section has been given a visual refresh. The three plans (Start, Pro, and Advanced) are now presented as polished cards, each with a plan icon, a one-line description, and a consistent feature list (storage tier, unlimited bandwidth, unlimited encoding, and all features included). Pro is highlighted as the Most Popular plan, the Monthly / Annual switch now carries a "Save 50%" badge so the annual saving is obvious, and the cards lift gently on hover. The plans, prices, and checkout flow are unchanged.
- For subscribers, the Subscription Details card on the Billing page has also been refreshed. It now leads with your plan, price, and a color-coded status badge, shows your storage usage as a progress bar against your plan's limit, and lays the billing dates and unlimited encoding/bandwidth out as clean tiles. The information shown is unchanged.
- Generate video chapters with AI. You can now turn a video's captions into chapter markers automatically: the platform reads the existing caption track, splits the content into logical sections, writes a short title for each, and adds them as a chapter track that appears on the player timeline (and in the video's SEO metadata). Generate them in the dashboard's Video Edit → Text Tracks → Generate chapters with AI panel (optionally tuning the minimum/maximum chapters per hour), or via the new "Generate chapters" API endpoint (
POST …/video/{video_id}/text_track/chapters/generate). They're generated in the background; the chapter track shows asprocessing, thenready, the same as auto-captions. The video must be ready and already have a caption track, auto-generated or uploaded (chapters are built from the captions, not the audio), and each video can have one chapter track, so delete the existing one to regenerate. See Auto-generated chapters.
2026-06-19¶
- Auto-generated posters now avoid blank/black frames. When a video is processed, its poster thumbnails are captured at a few points across the duration. Previously each one grabbed the exact frame at that timestamp, which often landed on a fade, scene cut, or otherwise blank/black frame with no detail. Each poster is now chosen as the most representative frame from a short window around its timestamp (it analyzes the next ~50 frames and keeps the one that stands out most), so it skips transient blank frames in favor of one with actual content. Manually capturing a poster from a chosen frame in the dashboard is unchanged; it still uses the exact frame you picked.
- The default poster is now the most detailed of the generated frames, not a random one. After a video's posters are generated, the one shown by default used to be picked at random, so even with good candidates the default could be the weakest (e.g. a near-blank) frame. The default is now chosen by scoring each generated poster for visual detail and keeping the most detailed one. For a video that's blank throughout there's still always a well-defined default (the least-blank frame); it never fails or leaves the video without one. You can still set any poster as the default yourself; manually chosen defaults are untouched.
- Terminal failures of AI subtitle generation now surface on the subtitle tracks, matching the video behavior added on 2026-06-18. When subtitle generation fails for good (all retries exhausted), the subtitle tracks it was producing now report status
error(with asubtitle_processing_errorevent) instead of staying stuck atprocessing; previously only the "couldn't detect the audio language" case was reported.flask requeue-job <id>recovers them the same way it does videos: it resets the job and returns the affected subtitle tracks toprocessing. - Likewise, a terminal failure of an alternate audio track's encode now reports the track's status as
errored(with anaudio_processing_errorevent) instead of leaving it stuck atqueued. The uploaded audio file is validated up front, so this only ever reflects a failure of the encode step itself (e.g. transient infrastructure errors that exhaust all retries), not an invalid upload.flask requeue-job <id>returns the track toqueued. - Fixed: fast clips now actually reuse the source's segments instead of silently re-encoding the whole cut. A fast clip re-encodes only the frames at the two cut points and reuses every whole segment in between, but that only works when the re-encoded boundaries match the source's video headers exactly. They never did for normally-encoded videos (the platform encodes in a mode the boundary re-encode wasn't reproducing), so every fast clip of a normal video fell back to a full re-encode: correct output, but with none of the speed or size benefit (and no link back to the source). Fast clips now reproduce the source's encoding, so eligible cuts splice as intended: far faster, far smaller, and linked to the source. (Cuts too short to contain a whole segment still fall back, as documented.)
- A fast clip can no longer be clipped or fast-clipped: neither the
clip/fastclipAPI endpoints nor the dashboard's Create Clip panel will clip a video that is itself a fast clip. A fast clip's media is stored as byte-range references into its source video's segments rather than as an independent encode, so cutting a new clip from it has nothing standalone to reuse or re-encode. Clip from the original source video instead. The API returns422 cant_modify_fastclip(the same rule that already blocks duplicating, or adding tracks to, a fast clip), and on the dashboard the Create Clip panel grays out with the reason. Plain (fully re-encoded) clips stay independent and can still be clipped again. - The video edit page now shows where a fast clip came from: when the video you're editing is a fast clip, it names the source video, links straight to the source's edit page, and shows the cut range taken from it (start–end). This makes a fast clip's provenance obvious and gives you a one-click way back to the original; before, nothing on the page indicated the video was a clip of another. The videos list also flags each fast clip with a small ⚡ badge. Plain videos and independent (fully re-encoded) clips show nothing of the sort.
- In the video edit page's Downloads (Progressive MP4) panel, every rendition of a fast clip now carries a small note (hover the ⓘ by the size) that the listed Size is the stored bytes only: because a fast clip reuses the source's segments, the actual download is usually larger than shown. Normal videos are unaffected.
- The video edit page now shows the video's total storage used, the combined size of every stored artifact (original upload, encoded renditions, posters, storyboards, and subtitle/audio tracks), in a Video info panel beside the preview, alongside its duration and created date.
- The video edit page's Upload a text track and Generate captions with AI panels have been restyled into tidy cards (an icon, a short description, and a two-column field layout for uploads) to match the page's Posters and Create Clip panels. How tracks are uploaded or generated is unchanged; the subtitle file picker is now labeled "Subtitle / caption file".
- The video edit page's Choose a poster picker is now a tidy gallery: uniform 16:9 thumbnails on a responsive grid, with the current poster clearly marked (a highlighted border and a "Current" badge) and the others lifting on hover. Picking a poster works exactly as before.
- The videos list page has been refreshed and tightened up. Each video shows a thumbnail with its duration overlaid, its title and description, upload date and total storage, and clearly labeled status and Public/Private badges (replacing the old cryptic single-letter
E/Pbadges) next to the ⚡ Fast clip badge. The rows are denser and the header slimmer, and there's a friendly empty state that points you to upload your first video (or to clear your filters when a search turns up nothing). - Background processing now starts the moment you trigger it, instead of waiting for the next queue cycle. Jobs (video encoding, URL fetches, clips, duplicates, alternate audio-track encodes, AI subtitle generation, outgoing webhooks, and account emails such as welcome, password reset, and project invites) previously waited to be picked up by the queue's polling cycle, which could add up to ~30 seconds of delay before the work even began. They now begin as soon as the action that created them is committed, so videos start encoding, webhooks fire, and emails send with noticeably less lag. When the relevant worker is already at capacity the job falls back to the normal queue exactly as before, so nothing is lost or double-run under load.
- The Statistics page layout has been refreshed. The Plays over time chart now spans the full width of the page, and the Completion funnel has been condensed into a compact strip in that chart's header rather than taking a half-width panel of its own: the five checkpoints (Played → 25% → 50% → 75% → Completed) shown as small percentages, with each checkpoint's play count and drop-off available on hover. The headline metric tiles keep their own row above. The underlying metrics and funnel checkpoints are unchanged.
- The Statistics page now shows the active date range as a caption at the top (for example, "Last 30 days · 1 Jun – 30 Jun 2026"), so it's clear what period the numbers cover without opening the Filters drawer. The five headline metric tiles also gained a thin color accent along their top edge, matching each tile's trend-sparkline color, so they're easier to tell apart at a glance.
2026-06-18¶
- Videos created from clip, fastclip, or duplicate now start with status
queued("Queued for encoding") instead ofwaiting_for_upload. These videos are derived from an existing video and have their processing job queued the moment they're created (they never wait for an upload), sowaiting_for_uploadwas misleading (a clip still being processed, or one whose job failed, looked like an un-uploaded video). Thestatusfield in the API and thevideo.createdwebhook now reportqueuedfor these videos from creation. Uploaded and fetched-by-URL videos are unchanged: they still startwaiting_for_uploaduntil their bytes arrive, then move toqueued. (Duplicates already reportedqueuedon the video record, but theirvideo.createdwebhook still saidwaiting_for_upload; that webhook is now consistent too.) - When a video's processing job fails for good (all retries exhausted), the video is now marked errored with a
processing_errorevent and avideo.status_updatewebhook, instead of being left silently atqueued/waiting_for_uploadwith the failure only visible in error tracking. This covers upload, fetch, encode, clip and duplicate jobs. Operators can re-run a failed job with the newflask requeue-job <id>command, which resets the job and returns its video toqueued. - The player edit page's three logo uploads (In-player logo, Control-bar logo, and Title logo) have been redesigned. Each is now a single card showing a live preview of the current logo on a video-like checkered backdrop (so transparent PNGs read clearly), with an Active / Not set status. Picking a file, by clicking the drop zone or dragging an image onto it, shows the file's name and an instant preview before you save, and the Save button enables only once you've chosen one; Remove is available only when a logo is set. Each card states the accepted formats, the size limit, and the recommended dimensions for that logo (the help text was previously duplicated). Uploading and removing logos work exactly as before.
- The In-player logo card now also holds the Logo position control (previously in the Player settings form), since it sets where that logo sits over the video. It's a visual corner picker, a small video frame with the available corners (top-left, top-right, bottom-left) as selectable chips, with its own Save position button. Saving it is unchanged (it's still a player setting), and the Player settings form's Save changes still applies the position too.
2026-06-17¶
-
The video edit page has a new Create Clip panel: you can now clip a video from the dashboard (previously clipping was API-only). Scrub the preview to the start and press Set start, scrub to the end and press Set end, then choose Create clip (an independent, fully re-encoded copy) or Create fast clip (reuses the source's segments where possible; much faster and smaller, but stays linked to the source). Your chosen start and end are marked right on the preview's progress bar, with the selected range highlighted between them, and a Preview button next to each jumps the player to that point. The buttons enable once the selected range is at least one second, and the panel is grayed out (with the reason) when the video isn't eligible: not yet completed, or over your storage quota.
-
Dashboard forms whose operation can't currently run now disable themselves up front instead of only failing on submit. When a video isn't eligible for an action, the panel for that action is grayed out and shows the reason as a clear notice, before you fill anything in. This is driven by the same operation rules (preconditions) the API enforces, so the dashboard and the API never disagree about what's allowed; the rules still re-run on submit, so nothing slips through if the disabled state is bypassed.
- The Duplicate Video panel on the video edit page now grays out, with the reason shown, when the video can't be duplicated: for a fast clip, a not-yet-completed video, or when there isn't enough free storage for the copy. (Previously you could fill in the form and only learned it was blocked after pressing Duplicate.)
- The storage check for duplicating a video is now size-aware: because a copy adds another full copy of the source, the duplicate is rejected unless your account has free storage at least equal to the video's size (previously it was only blocked once you were already at or over your quota). The error is a new
403 not_enough_storagewhose message states how much storage the copy needs and how much you have free; the duplicate endpoint no longer returnsupload_quota_reached(the other operations, fetch, clip, fastclip, and direct upload, are unchanged and still returnupload_quota_reached). The dashboard's Duplicate Video panel shows the same sized message. - The up-front graying-out now also covers the rest of the video edit page's panels: Upload text track and Generate captions with AI gray out for a fast clip (fast clips are fixed except for metadata and posters), and Upload poster grays out once a video already has the maximum number of posters. The AI-captions panel previously hid itself only when the video wasn't ready or had no audio; it now also reflects the fast-clip rule (which the API already enforced), and shows the reason consistently with the other panels.
- The video Password field on the edit page (Details) is now masked by default and reveals its value while you have the field focused. It's masked visually rather than being a real password field, so the browser won't offer to "remember" it or autofill saved logins into it.
- The video edit page's Posters panel can now capture a poster from the video itself: scrub the preview to the frame you want (pause where you like), click Use current frame, then Save as poster. The chosen timestamp's frame is extracted server-side from the best rendition and added as a new poster (subject to the same per-video poster limit as uploads).
2026-06-16¶
- The dashboard Statistics page has been redesigned. The page now opens with five headline tiles (Plays, Play rate, Watch time, Completion, and Unique viewers), each with a sparkline and a change badge comparing it to the previous equal-length period. The per-video engagement graph is now an Audience retention chart showing the share of plays still watching each moment with replays highlighted on top and a dashed average-engagement line. A new Completion funnel (Played → 25% → 50% → 75% → Completed) shows where viewers drop off, and the trend and retention charts use gradient fills for readability. Play rate, Completion, and Unique viewers are new on this page (Unique viewers counts distinct viewers; sessions without a viewer id aren't counted).
- The Audience retention chart gained several ways to read it at a glance: a hover preview that shows the video frame at the moment under the cursor (from the scrub-preview storyboard) alongside the timestamp, percentages, and current chapter; automatic key-moment callouts that mark the most-replayed moment (★) and the single biggest drop-off (▼); an engagement heatmap strip under the chart that colors the timeline from green (high retention) to red (low); chapter markers on the timeline when the video has a chapter track; and click-to-seek: clicking the graph opens the video at that exact moment in a new tab. The hover preview appears when the video has scrub-preview thumbnails.
- The dashboard Statistics page now shows the active country filter in the Filters panel (alongside the video filter), each with an × button to clear just that filter while keeping the rest of your selection (date range and the other filter). The country and video filters are now also preserved when you change the date range and press Search. When a country filter is active the Top countries table is hidden (it would only list the selected country) and Top videos expands to full width.
- After fetching a video by URL on the upload page, the new video now appears in an Added to your library panel below the form, with a card that links straight to the video's edit page (so you can set its details without hunting for it in the video list). The panel is built to list several videos once multi-URL fetching is added.
- The dashboard upload page has been redesigned: the drag-and-drop area is now the focal point, with an Upload / From a URL tab switcher, and the upload settings (privacy, watermark, encoding tier, captions) move into a panel beside it instead of stacked above. You can now record from your webcam or capture your screen directly in the uploader (Camera and Screen sources), and the uploader matches the dashboard's light/dark theme. The selected tab is reflected in the page URL (
?tab=…), so submitting a fetch-by-URL re-opens the From a URL tab on reload and its success/error message stays visible. - On the dashboard upload page you can now drop a video anywhere on the page to upload it, not only onto the dotted upload box. A full-page "Drop your video to upload" overlay appears while you drag a file over the window.
- The dashboard upload page now rejects obviously non-media files (e.g. a PDF or ZIP) the moment you pick them, instead of letting the whole upload run and fail afterwards. The check accepts video and audio broadly: any file the browser tags as video/audio, plus a wide list of container extensions (
.mkv,.ts,.mov,.webm,.flv, …), so valid videos the browser can't identify by type aren't wrongly blocked and it only stops files that are clearly neither. The server remains the final authority on what can actually be processed. - The dashboard upload page has a new Make videos public toggle that applies to both upload methods (device upload and fetch-by-URL). It defaults to private (unchanged from how new videos have always been created), so leaving it off keeps videos requiring a signed link, and turning it on makes every video uploaded on the page publicly viewable. The direct-upload API also accepts a
publicfield in the upload metadata. - The embedded video player's Nuevo plugin was upgraded from 14.3.0 to 15.0.0 (the underlying video.js stays at 8.23.8). Viewers get the latest player UI and fixes across all skins; the player's configuration, skins, and behavior are otherwise unchanged. The player assets now load from
/static/videojs.15.0.0/; if you hard-code or cache player asset URLs anywhere, they move with the version.
2026-06-15¶
- Public videos now expose their downloadable MP4 rendition at a fixed, shareable URL that needs no signed token. A preset's
static_mp4_link(API) returns this unauthenticated URL for public videos, while private videos keep their signed, expiring URL; the MP4 is served and cached by the CDN as before. Note: because the public URL is cached at the CDN, making a previously-public video private (or deleting it) does not immediately stop the CDN from serving the already-cached MP4; there is a propagation delay. - On the dashboard upload page, each device upload's thumbnail and filename become a link once the upload finishes; clicking it opens that video's edit page in a new tab, so you can jump straight to a freshly uploaded video without hunting for it in the video list.
- Device uploads on the dashboard upload page now resume after an interruption. If the page is reloaded or the tab is closed mid-upload, the uploader restores the in-progress upload and continues from where it stopped instead of restarting; only the chunks that never arrived are re-sent. Because browsers can't keep a selected file's bytes across a reload, large videos reappear as a "ghost" file prompting you to re-select the same file; once you do, the upload continues. Resuming must happen within the 24-hour multipart-upload window. (The underlying multipart API already supported this via list-parts; this enables it in the dashboard.)
2026-06-12¶
- Low-resolution renditions (240p/360p/480p) of high-frame-rate sources are now capped at 30 fps. Previously their frame rate was halved exactly once, so a 120 fps screen recording or 240 fps slow-motion upload shipped 60 fps or 120 fps low-resolution renditions, which wastes bitrate at resolutions where the extra frames add nothing. Sources up to 60 fps are unaffected (their low rungs already landed at or below 30 fps), and the 720p+ renditions keep frame rates up to 60 fps as before.
- On the dashboard upload page, a fetch-by-URL rejected by an operation rule (e.g. over the storage quota) now shows the message as a form-level error above the fetch form; previously it was attached to the URL field as if the URL itself were invalid. URL-specific errors (e.g. a dead link) still show on the field.
- Swapping a video with itself (
swap_idequal to the video's own id) is now rejected with a new422cant_swap_same_videoerror instead of being accepted as a pointless no-op. The dashboard's Swap Video form was never affected (it doesn't offer the video itself). - Fast clips can no longer be duplicated (
422cant_modify_fastclip, on the API and the dashboard's Duplicate Video panel alike). A fast clip plays back from its source video's stored segments, so a "duplicate" of one was never independent: the copy lost the link to the source and wouldn't play. Duplicate the source video instead, or useclipto create a stand-alone video from the same section. - Generating AI captions now validates its preconditions during request validation: the video must not be a fast clip, must be ready, and must have audio. The
cant_modify_fastclip,video_not_readyandvideo_no_audioerrors (still422) are now reported against the request body; the error codes are unchanged, so clients matching on the code are unaffected. The dashboard's Generate captions form enforces the same rules inline. - The dashboard Swap Video form now requires both videos to be completed, the same rule the API already enforces. Previously the dashboard validated only the target video; a non-completed source is now rejected with an inline error instead of being accepted.
- API endpoint descriptions now list their operation preconditions as a bullet list at the end (e.g. "The video must be completed." for duplicate and clip, "Up to 20 posters are allowed per video." for poster upload), so the OpenAPI/reference docs state the rules a request must satisfy.
- The
duplicateendpoint's description now also lists its storage-quota precondition ("Subject to your account's storage quota.", since a copy doubles storage). Both the API and the dashboard's Duplicate Video form now enforce the quota through one shared precondition (the API keeps returning403 upload_quota_reached; the dashboard shows the same rule as an inline error) instead of a separate check on each side. - The storage-quota check on
fetch,clip,fastclipand the direct (multipart) upload start is now a request-body precondition (it was a separate pre-check). The status and code are unchanged (403 upload_quota_reached), and their descriptions now list "Subject to your account's storage quota." One consequence (forfetch/clip/fastclip): the quota check now runs after basic body validation, so an over-quota request whose body is also malformed gets the422body error first rather than the403. The dashboard upload page's fetch-by-URL now enforces the same quota (shown as an inline error) instead of skipping it. - For
clipandfastclip, the "video must be completed" check (video_not_complete, still422) is now reported against the request body rather than thevideo_idpath parameter. The error code is unchanged, so clients matching on the code are unaffected.
2026-06-11¶
- The dashboard video edit page now has a Duplicate Video panel that creates an independent copy of the video (optionally with a custom title), the same as the duplicate video API endpoint. A copy doubles storage, so it's subject to your account's storage quota; over-quota shows an inline form error.
2026-06-10¶
- The signing-key limit (
max_keys_reached) and the per-user project limit (max_projects_reached) are now reported as request-validation errors: the HTTP status changes from412to422. The error codes and messages are unchanged, so clients matching on the error code are unaffected. - Text-track uploads are now validated during request validation, and an unparseable subtitle returns a new, more accurate
invalid_subtitle_fileerror ("File must be a valid vtt or srt subtitle.") pointing at thefilefield; previously this case returnedtext_track_invalid_type, whose message describes the chapter-type rule. The status stays422; clients matching ontext_track_invalid_typefor upload failures should switch toinvalid_subtitle_file(text_track_invalid_typeis still returned when setting a chapter track as the default). Subtitle files are now also capped at 10 MB (value_too_large). - Video fetch (
POST /video/fetch): an unreachable URL is now rejected during request validation, so thehead_request_to_url_failederror (still422) points at theurlfield. Fixed fetch-by-URL from the dashboard's upload page, which errored on every submit; a dead link now shows an inline error on the URL field. - Generating AI captions from the dashboard now enforces the same preconditions as the API (video ready, has audio, not a fast clip). These precondition failures (and the rare duplicate-webhook race) now render as inline form errors on the dashboard instead of a JSON error response.
- Multipart form fields with string types now take submitted values literally: a title of
true,2024or1e5is stored as-is. Previously such values were JSON-interpreted, rejecting some (truebecame a boolean → "Input should be a valid string") and silently mangling others (1e5saved as"100000.0","quoted"lost its quotes). Applies to the multipart API endpoints (watermark, poster, logo and text-track uploads) and the dashboard forms. - Fixed the dashboard stats page interpreting the date-range picker with the project timezone's historical local-mean-time offset (minutes off, e.g. +00:19 instead of +01:00 for Europe/Tirane); day boundaries now use the correct UTC offset for the selected dates.
- Editing a project with
player_id: 0(or any unknown player id) now returns a422resource_not_existserror instead of a 500.
2026-06-09¶
- Progressive (fMP4) video downloads now have their CDN bandwidth recorded in usage analytics, the same way streaming and progressive-MP4 bandwidth already are. Delivery itself is unchanged for viewers.
2026-06-08¶
- Video tag names containing NUL bytes no longer return a 500 when editing a video. Tags are now sanitized the same way other text fields are (NUL bytes replaced with
�) before being stored. - Performance stats (
GET /stats/performance) now returns a 422 whenend_dtis beforestart_dtinstead of silently returning an empty or incorrect response.
2026-06-07¶
- Editing a video with control characters (NUL bytes) anywhere in the free-form
metaobject no longer returns a 500. NUL bytes, which PostgreSQL cannot store, are now sanitized (replaced with the Unicode replacement character�) like other text fields, so the request succeeds. - Added a duplicate video endpoint (
duplicate) that creates an independent, byte-for-byte copy of a completed video: the original upload, every encoded preset, all audio and text tracks, posters and storyboards. Stored files are copied directly (no re-encode), the copy starts with zeroed analytics, and it neither inherits nor restricts the source video.
2026-06-05¶
- Added an API errors reference page listing every error code the REST API can return, with a stable anchor per code that error responses can link to.
- Added a fast clip endpoint (
fastclip) that creates clips much faster and far smaller by reusing the source video's already-encoded segments and only re-encoding the frames around each cut point. The existingclipendpoint (full re-encode) is unchanged. - Scrub-preview thumbnails (storyboards) are now split across multiple bounded image sheets instead of a single large sprite. Hover previews keep working on very long videos; previously a multi-hour video produced one oversized image that could exceed browser image-decode limits and render broken or blank thumbnails. For videos beyond ~3 hours the capture interval widens automatically so storage stays bounded.
- Video clipping now accepts fractional (decimal) start and end timestamps, so clips can be trimmed on sub-second boundaries (e.g.
start_time: 12.5). Whole-number timestamps continue to work unchanged. - A source video can no longer be deleted while fast clips of it still exist (delete the fast clips first).
- Fixed the clip
end_time_too_bigerror so its message shows the requested end time instead of a literal{end_time}placeholder.
2026-06-04¶
- Fixed a rare progressive-MP4 (download) corruption that could occur when a caption track was renamed while the file was partially cached on the CDN. The embedded caption label is now stored in a fixed-width field, so a rename can no longer shift the file's internal byte offsets.
- Caption track titles now appear as the subtitle track name when a downloaded MP4 is opened in players that show track titles (e.g. VLC, QuickTime).
- Audio track titles now appear as the audio track name in a downloaded MP4, across players that read either common location for it (e.g. Chrome, VLC, QuickTime).
2024-12-10¶
- Added Automatic Cold Storage
2024-09-09¶
- Added confirmation when exiting the page when video uploads are in progress
2024-08-17¶
- Added vertical videos HD encoding
2024-03-16¶
- Added alternate audio tracks
2024-02-16¶
- Added AI captions
2024-01-17¶
- Added audio normalization
2024-01-12¶
- Added encoding tiers
2024-01-09¶
2024-01-08¶
- Added webhooks
2024-01-01¶
- Added audio normalization option when uploading new videos.