Align content-type media caps with official API limits; block MOV on Bluesky - #353
Open
paulocastellano wants to merge 9 commits into
Open
Align content-type media caps with official API limits; block MOV on Bluesky#353paulocastellano wants to merge 9 commits into
paulocastellano wants to merge 9 commits into
Conversation
…on Bluesky Instagram Reel drops to 300 MB (Meta video_url pull), X Post rises to 1 GB / 20 min (tweet_video, not dm_video), Bluesky moves to 2 MB image / 300 MB video and rejects MOV (lexicon is MP4 only). TikTok, Discord and Telegram get explicit caps (Telegram uses the URL path: 5 / 20 MB), and the Telegram image optimizer budget follows to 5 MB. The MOV gate mirrors the GIF gate: ContentType::acceptsMov(), an accepts_mov flag in mediaRules()/toListingArray(), the editor warning in useMedia, and ContentTypeCompatibleWithMedia for API/MCP.
- Bluesky: lexicon decimal bytes (2 000 000 / 300 000 000), 10-minute video limit, publisher skip threshold default raised to match the enum (a test pins config >= enum); publisher comments reflect the MP4-only lexicon. - X: 8 GB tweet_video default entitlement; XPublisher always uses tweet_video (amplify_video is the Ads-creative category). - TikTok: 10-minute developer-upload ceiling (creator_info may lower it). - ContentTypeCompatibleWithMedia now rejects GIF on types that do not accept it, and enforces byte caps (via snapshot size) and video duration (via client-measured meta.duration when present) on publish/schedule for web, REST API and MCP. - Media snapshots written by MediaAttacher, Api\PostController@attachMedia and AttachMediaFromUploadTool now carry size. - Drop the dead Telegram MediaOptimizer change. - Tests: rule unit cases (GIF, size, duration, decimal edge), enum caps, config-vs-enum coherence, endpoint-level MOV/GIF/size rejection on web, API and MCP publish, snapshot size assertions.
…mal units, simplify MOV message - Chunked upload probes the video's duration client-side and sends it as X-Media-Duration; ChunkedAssetReceiver stores it in Media.meta.duration for videos. Nothing wrote meta.duration before, so every duration cap (editor and ContentTypeCompatibleWithMedia) was inert. - Byte caps declared in decimal megabytes (Bluesky's lexicon) are rendered in decimal units for both the cap and the file, in the editor and the server message, so users read "300 MB" instead of "286 MB". - mov_not_allowed copy shortened in all 16 locales; trimmed the Bluesky config comment.
…, plain-English Bluesky config note
- ContentTypeCompatibleWithMedia: GIF/MOV checks follow the hasImage/hasVideo pattern; size/duration loop flattened and messages use interpolation - useMedia.ts / chunkedUpload.ts restored to house formatting with only the needed edits (sizeParams helper, duration header) - Docblocks and inline comments cut to what the code cannot say itself
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Why
A ~990 MB Instagram video was failing at publish: Meta pulls the file from
video_urland its Reel ceiling is 300 MB, butContentType::InstagramReeladvertised 1 GB. While closing that gap, the rest of the per-content_typecaps were audited against the official API pages the publishers actually call, and the caps are now enforced server-side too — the editor was the only thing checking size and duration, so the REST API and MCP could store media the network would reject.What changes (effective on Cloud, after the 10 MB image / 1 GB video hard cap)
content_typeinstagram_reelvideox_postvideodm_video)tweet_videodefault) → effective 1 GBbluesky_posttiktok_videotiktok_photoimagediscord_messagetelegram_postEverything else (IG feed/story 100 MB / 60 s, Threads, Facebook, LinkedIn, YouTube, Pinterest, Mastodon) stays as-is.
Publisher fixes
trypost.platforms.bluesky.video_max_bytesdefault 100 MiB → 300 000 000 so the publisher no longer silently strips a video the editor accepted.ContentTypeTestpinsconfig >= ContentType::BlueskyPost->maxVideoBytes(). Comments inBlueskyPublishernow reflect the MP4-only lexicon.XPublisher::getMediaCategory()always sendstweet_video; the> 15 MB → amplify_videobranch is gone (amplify_videois the Ads-creative category).MediaOptimizerchange from the first revision was dead code (no caller) and is dropped.Server-side enforcement (
ContentTypeCompatibleWithMedia)Runs on publish/schedule for web (
UpdatePostRequest), REST (PUT /posts/{post}), and MCP (update-post-toolwithscheduled,publish-post-tool). In addition to the existing kind / mixed-media / PDF checks it now rejects:acceptsGif()is false (this used to be editor-only).acceptsMov(),Media\Type::isMov()— MIME or.movextension).maxImageBytes()/maxVideoBytes()/maxDocumentBytes(), read from the snapshot'ssize. Items withoutsizeare not checked (legacy snapshots).maxVideoDurationSec()when the snapshot carriesmeta.duration(measured client-side on dashboard uploads; API uploads have no duration — best-effort by design).Media snapshots written by
MediaAttacher,Api\PostController@attachMediaandAttachMediaFromUploadToolnow carrysize(the asset path already did).accepts_movis exposed next toaccepts_gifinmediaRules()/toListingArray()(Inertia,GET /content-types,list-content-types-tool); the editor emitsmov_not_allowed/no_movsin all 16 locales.Global upload allow-list is unchanged (MP4 + MOV still accepted everywhere else). No transcoding, no Instagram resumable upload — out of scope.
Tests
ContentTypeCompatibleWithMediaTest: GIF accept/reject per type, image/video/PDF size caps (incl. the 2 000 001-byte Bluesky edge proving decimal not MiB), size-less items skipped, duration over/at cap/unknown/no-cap.ContentTypeTest: new caps, Bluesky decimal constants, config-vs-enum coherence.UpdatePostRequestTest(MOV rejected, oversize image rejected, draft not blocked), APIPostApiPlatformMetaTest(stored MOV on Bluesky, GIF on LinkedIn, 900 MB Reel), MCPPostPlatformMetaToolTest(stored MOV, 900 MB Reel).PostMediaApiTest/MediaUploadFlowTestassert the snapshotsize.XPublisherTestassertstweet_videoabove 15 MB.vue-tsc, eslint clean.Docs PR: trypostit/trypost-docs#22