- Android PiP : boutons lecture/pause + plein écran dans la fenêtre flottante
(RemoteAction via BroadcastReceiver, icône play/pause synchronisée à l'état).
- Android PiP : masque la barre de paramètres (MiniPill/toolbar) et le padding
status bar pendant le mode PiP via onPictureInPictureModeChanged → JS.
- iOS auto-PiP : commentaire clarifiant la limitation WebKit (inline/MSE non
supporté, seul fullscreen→background déclenche l'auto-PiP).
Android PiP button fix:
- The Android System WebView does not implement the Web Picture-in-Picture
API, so the Movix player's PiP button showed "browser doesn't support PiP".
- New android-pip-shim.ts injects a PiP Web API shim (Android only):
document.pictureInPictureEnabled = true + HTMLVideoElement.requestPictureInPicture
override that routes to native Activity PiP via an ENTER_PIP bridge message.
- bridge.ts handles ENTER_PIP → PipModule.enterPipNow()
- PipModule.enterPipNow() + MainActivity.enterPipNow() trigger enterPictureInPictureMode
- iOS keeps native WebKit PiP (shim is Android-only)
Chromecast on iOS (now actually wired into the build):
- Podfile: pod 'google-cast-sdk', '~> 4.8.4' — since 4.8.4 the SDK ships as a
static framework bundling Protobuf, so NO use_frameworks! is needed (compatible
with the default RN static-library setup). Previous attempt used a non-existent
pod name (google-cast-sdk-no-arc) which broke `pod install`.
- CastModule.swift rewritten to mirror the Android CastModule exactly: pending-load
+ presentCastDialog picker flow, GCKSessionManagerListener emitting
CAST_SESSION_STARTED/RESUMED/ENDED/FAILED with {deviceName, durationSec, error},
loadMedia/stop/getCurrentDeviceName/getCurrentPositionSec/getSessionState.
All Cast API guarded by #if canImport(GoogleCast) so it compiles with or
without the pod (degrades to isSupported=false).
- project.pbxproj: registered CastModule.swift + CastModule.m in the MovixApp
target's Sources phase and wired SWIFT_OBJC_BRIDGING_HEADER (first Swift in the
app target) — done via the xcodeproj gem so the project graph stays valid.
- Bridging header now imports RCTEventEmitter.h.
- AppDelegate.mm: GCKCastContext initialized via setSharedInstanceWithOptions:
(correct ObjC selector) with the Default Media Receiver app ID.
- Info.plist: NSLocalNetworkUsageDescription + NSBonjourServices
(_googlecast._tcp, _CC1AD845._googlecast._tcp) required for device discovery
on iOS 14+.
The existing iOS-settings cast toggle (AirPlay vs Chromecast) now drives a real
Chromecast path: in Chromecast mode the cast shim is injected and the native iOS
CastModule responds supported=true.
https://claude.ai/code/session_01X52sah6aUu3ou26uJWHgzr
Group A — PiP (iOS + Android):
- iOS: AppState 'inactive' injects requestPictureInPicture() from native side
while WKWebView is still alive (fixes visibilitychange race with JS suspension)
- Expose isVideoPlayingRef + onMediaPlayback callback from BrowserScreen → bridge
- BridgeMessageOptions added to bridge.ts for cross-platform onMediaPlayback hook
- Android 12+ (API 31): setAutoEnterEnabled(true) via updatePipParams() called
reactively from PipModule.setVideoPlaying() → covers all backgrounding scenarios
(home button, swipe, screen timeout, power button), not just onUserLeaveHint
Group B — Wake Lock:
- navigator.wakeLock.request('screen') acquired on play, released on pause
- Re-acquired on visibilitychange return if video still playing
- visibilitychange now manages timer + wake lock only (PiP handled natively)
- window.__movixActiveVideo exposed for AppState PiP injection script
Group C — Chromecast on iOS:
- useBrowserUIPrefs: castMode: 'airplay' | 'chromecast' (iOS only, default airplay)
- SettingsScreen: iOS-only "Casting" section with AirPlay/Chromecast toggle
- inject.ts: AirPlay mode skips cast shim → site shows native AirPlay button;
Chromecast mode injects shim → iOS CastModule handles Chromecast routing
- cast.ts: removed Platform.OS !== 'android' guard (iOS CastModule can respond)
- New ios/Movix/Cast/CastModule.swift + CastModule.m (mirrors Android CastModule)
with #if canImport(GoogleCast) guards — compiles with or without SDK
- Podfile: pod 'google-cast-sdk-no-arc' (~> 4.8)
- AppDelegate: GCKCastContext initialized with DEFAULT_MEDIA_RECEIVER_APP_ID
Group D — Address config cache:
- AddressContext loads AsyncStorage cache immediately → near-instant start
- Fresh fetch runs in background, updates cache on success
- Offline resilience: stays on cache silently if network fails
Group E — WebView quality:
- IconLock SVG added to ToolbarIcons (Material Design lock path)
- BrowserToolbar: 🔒 emoji replaced with <IconLock> SVG component
- WebViewBrowser: setSupportMultipleWindows={false} + onOpenWindow={() => {}}
to block window.open() popup ads
https://claude.ai/code/session_01X52sah6aUu3ou26uJWHgzr
Make playback feel native on iOS (and Android) by leveraging OS media
features instead of leaving everything to the WebView default.
Media Session (cross-platform, src/injection/media-session.ts):
- The Movix web player never populates navigator.mediaSession, so the
lock screen / Control Center / notification only showed a bare title.
- Inject a script that sets mediaSession.metadata with the movie title
and artwork (video poster → og:image → icon → /movix.png fallback),
wires play/pause/seek action handlers to the active <video>, and keeps
positionState in sync for the scrubber.
- Hooked on the capture-phase 'play' event so it follows SPA navigation
(episode/movie changes) without re-injection.
- Sets video.autoPictureInPicture / disablePictureInPicture=false to arm
iOS auto-PiP, and relays play/pause to native (MEDIA_PLAYBACK message).
iOS background audio + PiP:
- AppDelegate.mm: configure AVAudioSession (.playback / .moviePlayback)
so audio and Picture-in-Picture keep running when the app is
backgrounded or the screen locks. Without this WKWebView stops the
video on background.
- WebViewBrowser: allowsPictureInPictureMediaPlayback +
allowsAirPlayForMediaPlayback. Combined with autoPictureInPicture, the
video floats in PiP when leaving the app instead of stopping.
Android PiP:
- Manifest: supportsPictureInPicture + resizeableActivity on MainActivity.
- MainActivity.onUserLeaveHint() enters PiP (16:9) when a video is
playing; playing-state is tracked via the new PipModule, fed from the
Media Session script through the bridge.
- New PipModule/PipPackage registered in MainApplication.
https://claude.ai/code/session_01X52sah6aUu3ou26uJWHgzr
Nettoyage massif côté frontend, grosse passe perf, app mobile v2.5.3
(écran qui ne s'éteint plus + fix build), extracteur Uqload HLS sur
extensions/userscript, redirection réseau-bloqué via passerelle
explicative et cast cross-receiver/cross-browser.
Nettoyage Firebase & code mort
- Drop firebase + @firebase/firestore (package.json + vite.config
optimizeDeps + alias)
- Suppression src/config/firebase.ts (jamais utilisé)
- Suppression services morts : adminService, commentService, logService,
notificationService + composants liés (AdminLogin, CommentSection,
CommentItem, ReactionBar) + types/Comment.ts
- Suppression scripts non-utilisés : bundle-report.mjs, publish-app.mjs
- Net : -3 449 lignes
Perf carrousels & TMDB images
- useTmdbImages : 1 seul fetch TMDB pour logo + poster (au lieu de 2),
prefetch idle au mount du carousel + dedup via inflight map (0 doublon
entre prefetch et hooks), include_image_language=fr,en,null pour
récupérer les versions FR-prioritisées
- EmblaCarousel : pré-décode des posters off-DOM (img.decode) en idle
pour éliminer le coût de décode pendant le scroll horizontal, priority
cap statique (eager + fetchpriority=high pour les N premières cards
selon viewport), suppression du tracking visibleSlides (state churn)
- content-visibility:auto sur slides hors viewport (skip layout/paint
natif), exclu des slides Top 10 pour ne pas clipper le digit débordant
- will-change:transform pré-promotion compositing layer (PC only via
hover:hover)
- Embla duration 25 → 15 (snap plus rapide, moins de frames composées)
- Boutons nav carousel : suppression backdrop-blur (cher), bg opaque
- LazyImage : width/height attrs pour éviter CLS + alléger placeholder
Top progress bar (style YouTube)
- TopProgressBar : asymptote vers 85% pendant chunk loads, fill 100%
smooth + fade out à la fin, 120ms grace period pour suppress flash
sur chunks cachés
- lazyWithRetry tracke les loads actifs + dispatch chunk:load:start /
chunk:load:end events sur window
- Routes loaders supportent { silent: true } pour skip le tracking sur
les prefetch (PrefetchLink hover + IDLE_PREFETCH au mount)
SW redirect → passerelle movix.health explicative
- buildMirrorUrl() factorise la construction d'URL miroir avec contexte
- handleNavigation passe ?from=&reason=unreachable&error=&via= à la
gateway pour qu'elle affiche une popup explicative au user au lieu
d'un redirect silencieux
- MOVIX_FORCE_REDIRECT message handler passe reason=api-errors
- blockDetection.ts transmet la dernière erreur axios au SW
- index.html : cache-bust du Cast SDK URL (cb=Date.now()) pour éviter
qu'un SDK stale ne reste coincé en cache navigateur/SW
Cast (Chromecast + AirPlay + Remote Playback API)
- loadMediaOnCastWithFallback : retry sur 3 MIME HLS différents
(application/vnd.apple.mpegurl, x-mpegurl, x-mpegURL) — certains
receivers rejettent le premier choix selon firmware
- Subtitles externes attachés au cast (CastSubtitleTrack) via
chrome.cast.media.Track + TextTrackStyle par défaut (50% black
background, edge outline) lisible sur la majorité des TV
- isRemotePlaybackSupported : fallback W3C Remote Playback API pour
Firefox / Edge avec cast extension bloquée / Brave avec shields →
device discovery + picker via video.remote.prompt()
- initializeAirPlay supporte désormais les deux paths (WebKit AirPlay
pour Safari + Remote Playback pour les autres)
- Détection castSdkBlocked : si le SDK Cast ne charge pas en 5s
(adblock, FAI, shield), affiche un message d'erreur explicite au
lieu du générique "aucun device trouvé"
- 7 nouvelles strings i18n FR + EN pour les états cast :
castUnavailable, castUnavailableNoDevices,
castUnavailableUnsupportedBrowser, castUnavailableSdkBlocked,
castUnavailableHelpChromecast, castUnavailableHelpAirPlay,
castUnavailableSeeHelp
App mobile Android v2.5.3 (versionCode 12)
- AndroidManifest : android:keepScreenOn="true" sur MainActivity →
l'écran ne s'éteint plus pendant le visionnage (avant le user devait
toucher l'écran toutes les 30s pour pas que le téléphone se lock en
plein film)
- build.gradle : exclude androidx.legacy:legacy-support-core-utils
(tiré transitivement par mediarouter → palette) qui embarquait ses
propres androidx.autofill.R$attr → "duplicate class" au mergeDexRelease
contre le module androidx.autofill:autofill:1.1.0 d'appcompat
- gradle.properties : android.nonTransitiveRClass=true pour empêcher
chaque lib de regénérer ses R classes transitivement (même cause
racine du duplicate class)
- version.json bumped 2.5.2 → 2.5.3, nouvel APK (-17KB), release notes
FR : "Correction extraction du lecteur Uqload" + "L'écran ne s'éteint
plus lors de l'utilisation de l'app"
- apkUrl repointé sur le repo Movix-STMG (au lieu de movixcorp)
Userscript + Extensions Chrome/Firefox — Uqload HLS
- extractUqload : préfère désormais le master.m3u8 multi-bitrate (qualité
adaptative) au v.mp4 single-quality. Regex tente d'abord
/master\.m3u8/, sinon n'importe quel .m3u8, et fallback sur l'ancien
pattern v.mp4 si rien trouvé. Les 3 cibles (Chrome MV3, Firefox MV2,
Tampermonkey) sont synchronisées sur la même logique.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Maj url dans l'app mobile, extension et userscript et backend : streamonsport, livetv.
Amélioration du scraper darkiworld/hydracker
Nouvelle source vo/vostfr qui est plutôt pas mal
Renommages des sources vo/vostfr avec le nom du doamine pour mieux se repérer
Correction de la synchronisation des données qui étais buggé sur certains navigateurs autre que chrome sur certaines versions
Amélioration de la détection du blocage dns