feat: process GutenbergKit media uploads per the app's media settings - #23142
Draft
dcalhoun wants to merge 14 commits into
Draft
feat: process GutenbergKit media uploads per the app's media settings#23142dcalhoun wants to merge 14 commits into
dcalhoun wants to merge 14 commits into
Conversation
Collaborator
Generated by 🚫 Danger |
Contributor
Project dependencies changeslist! Upgraded Dependencies
org.wordpress.gutenbergkit:android:trunk-16aceb17716dbef9fb2289d316e27dc812a569f6, (changed from v0.19.0)
org.wordpress:utils:156-2e542df55715dff22c21def058bea551ee9569d0, (changed from 3.14.0)tree +--- project :libs:editor
-| \--- org.wordpress.gutenbergkit:android:v0.19.0
+| \--- org.wordpress.gutenbergkit:android:trunk-16aceb17716dbef9fb2289d316e27dc812a569f6
+--- project :libs:fluxc
-| \--- org.wordpress:utils:3.14.0
-| +--- org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.6.10 -> 1.9.24 (*)
-| +--- org.apache.commons:commons-text:1.10.0 -> 1.15.0
-| | \--- org.apache.commons:commons-lang3:3.20.0
-| +--- com.android.volley:volley:1.2.0 -> 1.2.1
-| +--- com.google.android.material:material:1.2.1 -> 1.14.0 (*)
-| +--- androidx.swiperefreshlayout:swiperefreshlayout:1.1.0 -> 1.2.0 (*)
-| +--- androidx.recyclerview:recyclerview:1.0.0 -> 1.3.2 (*)
-| +--- org.greenrobot:eventbus:3.3.1 (*)
-| \--- androidx.core:core-ktx:1.5.0 -> 1.16.0 (*)
+| \--- org.wordpress:utils:156-2e542df55715dff22c21def058bea551ee9569d0
+| +--- org.apache.commons:commons-text:1.10.0 -> 1.15.0
+| | \--- org.apache.commons:commons-lang3:3.20.0
+| +--- com.google.android.material:material:1.2.1 -> 1.14.0 (*)
+| +--- androidx.swiperefreshlayout:swiperefreshlayout:1.1.0 -> 1.2.0 (*)
+| +--- androidx.recyclerview:recyclerview:1.0.0 -> 1.3.2 (*)
+| +--- org.greenrobot:eventbus:3.3.1 (*)
+| +--- org.greenrobot:eventbus-java:3.3.1
+| +--- androidx.core:core:1.5.0 -> 1.16.0 (*)
+| \--- org.jetbrains.kotlin:kotlin-stdlib:1.9.24 -> 2.4.10 (*)
-+--- org.wordpress:utils:{strictly 3.14.0} -> 3.14.0 (*)
++--- org.wordpress:utils:{strictly 156-2e542df55715dff22c21def058bea551ee9569d0} -> 156-2e542df55715dff22c21def058bea551ee9569d0 (*)
-\--- org.wordpress.gutenbergkit:android:v0.19.0 (*)
+\--- org.wordpress.gutenbergkit:android:trunk-16aceb17716dbef9fb2289d316e27dc812a569f6 (*) |
Contributor
|
|
Contributor
|
|
Contributor
🤖 Build Failure AnalysisThis build has failures. Claude has analyzed them - check the build annotations for details. |
Codecov Report❌ Patch coverage is Additional details and impacted files@@ Coverage Diff @@
## trunk #23142 +/- ##
=======================================
Coverage 37.81% 37.81%
=======================================
Files 2343 2344 +1
Lines 127397 127488 +91
Branches 17681 17704 +23
=======================================
+ Hits 48170 48209 +39
- Misses 75271 75317 +46
- Partials 3956 3962 +6 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
Consumes the snapshot build of wordpress-mobile/GutenbergKit#357, which adds the MediaUploadDelegate API for host-side media upload processing. To be swapped to a tagged release before merge. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Adds video optimization and strip-image-location accessors to AppPrefsWrapper, and a stripImageLocation passthrough to MediaUtilsWrapper, so the upcoming GutenbergKit media upload processor can read settings and strip GPS EXIF through injectable, testable wrappers. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Implements GutenbergKit's MediaUploadDelegate to process device media picked in the experimental block editor before upload, honoring the app's media settings the same way the legacy editor pipeline does: - Images are optimized per the max size/quality settings via WPMediaUtils.getOptimizedMedia, with GPS EXIF stripped when the strip-location setting is on, and the reported mime type/extension corrected to the actual output format (non-PNG inputs, including HEIC, re-encode to JPEG). - When processing would be a no-op, the original file passes through untouched, avoiding a needless lossy re-encode. - Sideways-captured images are rotated for self-hosted sites when optimization is off (WP.com rotates server-side; issue #5737). - GIFs always pass through to preserve animation. - Videos enforce the free-plan 5-minute duration limit and transcode via WPVideoUtils/m4m only when the optimize-video setting is on, keeping the transcoded file only when smaller than the original. Transcodes are serialized to bound codec/memory pressure. - File types disallowed by the site plan are rejected with a localized message relayed to the editor as a notice. Nothing wires the processor yet; that lands separately. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Adds JVM unit tests for the processor's decision table: optimized image output with corrected mime/extension (incl. HEIC to JPEG and PNG passthrough), no-op short-circuit, GPS stripping onto the optimized output and onto a copy when optimization is off, GIF passthrough, self-hosted orientation fix, plan-disallowed type and over-limit video rejections with localized messages, video passthrough when optimization is disabled, and same-path optimization results treated as unprocessed. Also adds a File-based isProhibitedVideoDuration overload to MediaUtilsWrapper so the processor avoids Uri.fromFile, keeping it testable on the JVM. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Sets the media upload delegate on GutenbergView so device media picked in the experimental block editor is processed per the app's media settings before upload. The fragment stores and forwards the delegate following the existing setNetworkRequestListener pattern (GutenbergView tolerates assignment before or after page load), and the activity constructs the processor with its SiteModel and injected wrappers. If GutenbergKit's upload server fails to start, uploads degrade to the existing WebView path unchanged. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Consumes the PR-build snapshot of WordPress-Utils-Android#156, which stops ImageUtils.getImageOrientation from logging a spurious "Volume data not found" error on every optimized upload staged in the app cache dir (GutenbergKit native media uploads). To be swapped to a tagged release once that PR merges. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
processVideo and processImage are guard-clause decision tables where early returns read clearer than nesting; suppress ReturnCount to match the existing house style rather than fragment the logic. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
EXIF_MIME_TYPES gated the copy-and-strip branch on formats that can
carry GPS, but androidx ExifInterface.saveAttributes() can only rewrite
JPEG, PNG, and WebP. The set omitted PNG (so location-bearing PNGs
uploaded un-stripped, a regression vs. the legacy pipeline) and included
HEIC/HEIF (where saveAttributes throws an IOException that stripLocation
swallows, uploading a still-geotagged copy while appearing to honor the
setting).
Correct the set to {JPEG, PNG, WebP} and cover the PNG-stripped and
HEIC-passthrough cases in the decision-table tests.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
transcodeVideo logs each caught exception via .message, so Detekt's SwallowedException never fires — only TooGenericExceptionCaught does (NullPointerException and Exception are both on its generic-names list). Trim the suppression to the rule that actually triggers. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
GutenbergKit validates uploads in the WebView against the site's allowedMimeTypes (fetched from /wp-block-editor/v1/settings and cached on disk) before the request reaches this delegate, so it rejects most disallowed types with its own localized message first. Document that this app-side check is a fallback for the cache-miss/undefined-settings path and can over-reject when its static MimeTypes table is stricter than the server's cached list. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
PR 357 merged as 016a00e5, and the review follow-up 561 ("harden the
native media upload server") landed on top of it. Track trunk at
16aceb17 instead of the transient PR branch build, picking up 561's
fixes: idle-based body reads so a steadily-streamed large upload is no
longer failed on total duration, rejection of auth-exempt OPTIONS
carrying a body, and cancellable reads so shutdown reaps an active
connection.
Still an untagged snapshot, so the TODO stays. v0.19.0 is tagged but
predates both 357 and 561.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
GutenbergKit 561 made mediaUploadDelegate throw when assigned after the editor page starts loading: the delegate is captured once, in onPageStarted, and the setter now check()s !hasStartedLoading. The old setter started the upload server on assignment and re-synced window.GBKit into a loaded page, so a late set used to work. setMediaUploadDelegate pushed straight into a possibly-loaded view, which would now crash. Store the delegate only and let onCreateView be the single place it reaches the view, moved to immediately after the GutenbergView constructor — on the preloaded-dependencies fast path that constructor already kicks off the load. The ordering holds: FragmentPagerAdapter.instantiateItem defers its transaction to finishUpdate, so the activity's setMediaUploadDelegate call returns before onCreateView runs. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
GutenbergKit 561 added handlesFile, a metadata-only gate consulted before the upload server materializes a request to a temp file. Declining relays the original body straight to WordPress. processFile returns Original for GIFs and non-media, so today those pay a full byte-for-byte copy only to reach the same passthrough. Decline them up front and skip the copy. Disallowed types are claimed rather than declined, so processFile still runs and throws the localized rejection. Declining would relay them to WordPress instead, spending a whole upload on a file the site's plan won't accept and replacing our message with the server's untranslated one. That matters because Gutenberg's own validateMimeTypeForUser early-returns when the cached allowedMimeTypes list is absent, leaving this check as the only validation on a cold cache. handlesFile is an optimization hint, not the enforcement point: it sees only the client-supplied mime type and filename, which can disagree with the file's bytes, so the plan check inside processFile stays authoritative. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Mirrors the existing processFile cases: GIF and non-media are declined so the temp copy is skipped, images and videos are claimed. The disallowed-type case pairs with "disallowed file type throws with localized message", which uses the same mime type — that test would stop reflecting reality if handlesFile ever declined non-media unconditionally, since processFile would no longer run for it. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
dcalhoun
force-pushed
the
feat/process-gutenberg-kit-media-uploads
branch
from
August 17, 2026 13:57
d70d4a0 to
d6db8eb
Compare
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.


Description
Ref CMM-1249.
Device media picked in the GutenbergKit editor (the new, experimental block editor / NBE) currently uploads raw from the WebView straight to the WordPress REST API, bypassing the app's media settings that the legacy editor honors (image optimization/resizing, quality, EXIF location stripping, video optimization).
This PR integrates GutenbergKit's new
MediaUploadDelegate(from GutenbergKit#357) so device uploads are processed natively per the app's media settings before upload, mirroring WordPress-iOS#25824. GutenbergKit runs a localhost relay server while the editor is open, hands each staged file toprocessFile, uploads via its default uploader, and relays WordPress's raw response to the editor verbatim — so attachment objects,media_details.sizes, blob-preview replacement, save-locking and error notices all behave exactly like a direct upload.What
GBKMediaUploadProcessor.processFiledoes (mirrors the iOS decision table, adapted to Android's pipeline)WPMediaUtils.getOptimizedMedia(max dimension + quality from App Settings), with GPS EXIF stripped when the "Remove location" setting is on. The reported mime type/filename is corrected to the actual output format (non-PNG inputs, including HEIC, re-encode to JPEG). When processing would be a no-op (optimization off, no strip, no rotation), the original passes through untouched — no needless lossy re-encode.WPVideoUtils/m4m path) only when the "Optimize videos" setting is on, keeping the transcoded file only when it's smaller than the original. Transcodes are serialized to bound codec/memory pressure. This matches Android's legacy behavior (where video optimization is opt-in), rather than iOS's always-transcode.The delegate is set unconditionally for the post/page GutenbergKit editor. If GutenbergKit's upload server fails to start, uploads degrade to the existing WebView path unchanged.
Testing instructions
All in the GutenbergKit editor (enable the experimental block editor for the site). Change settings between runs in App Settings → Media.
Image optimization:
Location stripping:
3. Insert a JPEG, PNG, or WebP photo with GPS EXIF, with "Remove location" on (try both with optimization on and off).
GIF:
4. Insert an animated GIF.
Video:
5. With "Optimize videos" on, insert a video wider than the width setting.
Disallowed type: