protendai/nativephp-media
NativePHP Media brings image, audio, and video handling to your NativePHP desktop apps: pick and store files, generate thumbnails and conversions, and manage media libraries with a simple, Laravel-style API.
Architecture fit
This is a NativePHP mobile plugin: a Laravel-side PHP facade (Media::), native sources compiled into the app bundle (resources/android/src, resources/ios/Sources), and a JS package (@protendai/nativephp-media) exposing one function per bridge function, event constants/listeners, and TypeScript types. Playback lives natively (platform media session, system resume card, media buttons, PiP); Laravel owns queue intent and app state; the front end reacts to bridge events.
Integration feasibility
High for a NativePHP mobile codebase. Composer install → publish config (nativephp-media-config tag) → set NATIVEPHP_MEDIA_LIBRARY → rebuild; a single post_compile hook (nativephp:media:configure) applies the native configuration.
withMedia(), assertMediaQueued(), assertVideoOpened(), assertMediaPermissionRequested()) and the shipped Pest suite.Technical risk
com.protendai.media.VideoActivity; resource paths and the post_compile hook were renamed even before the first public release. Pin exact versions and re-run device tests on every bump.NATIVEPHP_MEDIA_LIBRARY, so apps that only play their own files/URLs avoid a Google Play photo/video permission declaration entirely. Enabling audio/video re-introduces that declaration work plus iOS usage strings.nativephp.permissions always wins — convenient, but a drift hazard unless a single source of truth is declared.requestMetadata.mediaUri only from this app. The fix is good; the lesson is that this plugin exposes OS-level IPC and warrants a security read on every upgrade.Key questions
nativephp.permissions?Stack fit
Laravel + NativePHP mobile shell; Vite-built JS package; Pest for tests; iOS 16.0 minimum; Android with READ_MEDIA_VISUAL_USER_SELECTED (API 34) for partial "Select videos" access and legacy paths below that. The bridge is function-level, so it composes with Blade, Livewire, Inertia, or a Vue/React SPA without architectural preference.
Migration path
Greenfield — there is no earlier public version, so no code migration. If replacing browser <audio>/<video> in a wrapper app:
setQueue/openVideo with startPosition = -1 and skipTo resume the item's saved position, while next, previous, and auto-advance always start at 0. Any prior assumption that position carries forward across track changes must be re-tested — it was actively wrong on iOS before this release.Compatibility
Pin the NativePHP mobile version alongside this package and verify: Composer constraints, the nativephp-media-config publish tag, the nativephp:media:configure post_compile hook (the old nativephp:media:enable-pip no longer exists), the NATIVEPHP_MEDIA_LIBRARY values (true/audio/video), and precedence against your app's nativephp.permissions. Test matrix: iOS 16/17/18; Android 13/14/15; permission granted / denied / limited; process kill during playback; PiP enter, exit, and dismiss; Bluetooth and notification controls; interruption by calls or other audio apps.
Sequencing 0. Spike (3–5 days): own-file and URL playback on both platforms; confirm the build hook and config publish work in your CI.
stop() releasing the audio session.Gate each step on a full native rebuild and smoke test on both platforms; the most expensive failures in this release (dropped events after activity recreation, stale command replay) were invisible in PHP-only tests.
Maintenance Pin exact versions and expect churn: native source locations and the post_compile hook name both changed inside the first public release, and PiP depends on a specific activity class name. The dominant ongoing cost is tracking upstream NativePHP breaking changes, because these hooks and sources are compiled into your app binary. Retain a fork path (MIT, sources included) as insurance.
Support Support scripts must cover distinct, user-visible failure classes: permission denied vs. limited (iOS "Selected Photos", Android 14 "Select videos"); no audio after closing video or PiP (fixed here); resume landing in the wrong place (fixed here); media buttons not responding. Because logs are now redacted, add an explicit in-app diagnostics opt-in rather than relying on device logs.
Scaling
Scaling is per-device, not per-server: binary size, native build time, cold start, battery, and audio-session contention are the levers. Watch large libraries — iOS previously re-read and re-sorted the entire Music library per page, now fixed, but confirm paging behavior at 10k+ items before enabling library browsing broadly. Queue depth and concurrent players are constrained by the shared-player design (a second openVideo() during PiP used to stack a player), so validate one-player-at-a-time assumptions in product UX.
Failure modes Fixed in this release: bridge events dropped or crashing once the activity finished; queued commands replayed after a failed service connection; permission requests lost to activity recreation; saved positions written under the wrong track id (seek past end); Android not saving outgoing positions on manual changes; media session allowing arbitrary URI queueing; video close stopping shared music playback; PiP window stacking; app termination when a library permission was requested without a usage string.
Residual risks to monitor: OS audio-focus arbitration, Bluetooth/OEM battery optimizations, any newly exposed IPC surface in future versions, and any custom-activity work that breaks the anchored PiP patch.
Ramp-up A Laravel/PHP developer is productive on the facade and JS bridge in ~2–3 days using the Boost guidelines and FakeBridge. Native-level debugging (Kotlin/Swift) is needed for edge cases, so keep at least one mobile-capable engineer or a partner agreement available. Budget roughly one engineer-week for permission UX and compliance copy, two for video/PiP polish and device QA, plus one app-store review cycle before enabling device library access in production.
How can I help you explore Laravel packages today?