MovixOpenSource/package.json
Movix 4bba4cab40 chore: drop firebase + perf wave + mobile v2.5.3 + uqload HLS + multi-receiver cast + SW gateway
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>
2026-05-10 13:28:51 +02:00

109 lines
3.8 KiB
JSON

{
"name": "streaming-site",
"private": true,
"version": "0.1.0",
"type": "module",
"scripts": {
"dev": "vite",
"build": "vite build",
"build:cf": "vite build",
"build:coolify": "vite build",
"build:report": "vite build && node scripts/bundle-report.mjs",
"lint": "eslint .",
"preview": "vite preview",
"start": "node server/index.js",
"start:coolify": "node server/index.js",
"dev:server": "node server/index.js",
"app:publish": "node scripts/publish-app.mjs",
"wasm:watchparty-sync:setup": "rustup target add wasm32-unknown-unknown && cargo install wasm-bindgen-cli",
"wasm:watchparty-sync:build": "cargo build --manifest-path wasm/watchparty-sync/Cargo.toml --target wasm32-unknown-unknown --release && wasm-bindgen --target web --out-dir public/wasm/watchparty-sync wasm/watchparty-sync/target/wasm32-unknown-unknown/release/watchparty_sync.wasm",
"wasm:watchparty-sync:build:dev": "cargo build --manifest-path wasm/watchparty-sync/Cargo.toml --target wasm32-unknown-unknown && wasm-bindgen --target web --out-dir public/wasm/watchparty-sync wasm/watchparty-sync/target/wasm32-unknown-unknown/debug/watchparty_sync.wasm"
},
"dependencies": {
"@dnd-kit/core": "^6.3.1",
"@dnd-kit/modifiers": "^9.0.0",
"@dnd-kit/sortable": "^10.0.0",
"@dnd-kit/utilities": "^3.2.2",
"@emoji-mart/data": "^1.2.1",
"@emoji-mart/react": "^1.1.1",
"@headlessui/react": "^2.2.0",
"@hono/node-server": "^1.19.14",
"@radix-ui/react-dialog": "^1.1.15",
"@radix-ui/react-dropdown-menu": "^2.1.16",
"@radix-ui/react-select": "^2.2.6",
"@radix-ui/react-slot": "^1.2.3",
"@radix-ui/react-tooltip": "^1.2.8",
"@tanstack/react-virtual": "^3.13.24",
"axios": "^1.13.2",
"canvas-confetti": "^1.9.4",
"class-variance-authority": "^0.7.1",
"clsx": "^2.1.1",
"dashjs": "^5.1.1",
"date-fns": "^4.1.0",
"embla-carousel": "^8.6.0",
"embla-carousel-autoplay": "^8.6.0",
"embla-carousel-react": "^8.6.0",
"emoji-mart": "^5.6.0",
"file-saver": "^2.0.5",
"framer-motion": "11.11.10",
"hls.js": "^1.6.16",
"hono": "^4.12.14",
"i18next": "^25.8.10",
"i18next-browser-languagedetector": "^8.2.1",
"jszip": "^3.10.1",
"lenis": "^1.3.23",
"lucide-react": "^0.344.0",
"mpegts.js": "^1.8.0",
"pako": "^2.1.0",
"qrcode": "^1.5.4",
"react": "^18.3.1",
"react-colorful": "^5.6.1",
"react-country-flag": "^3.1.0",
"react-dom": "^18.3.1",
"react-force-graph-2d": "^1.29.1",
"react-helmet-async": "^2.0.5",
"react-i18next": "^16.5.4",
"react-input-mask": "^2.0.4",
"react-loading-skeleton": "^3.5.0",
"react-markdown": "^10.1.0",
"react-router-dom": "^6.22.3",
"react-snowfall": "^2.4.0",
"remark-emoji": "^5.0.1",
"remark-gfm": "^4.0.1",
"shaka-player": "^5.0.0",
"socket.io-client": "^4.8.1",
"sonner": "^2.0.7",
"tailwind-merge": "^3.4.0",
"tailwindcss-animate": "^1.0.7",
"uuid": "^11.1.0",
"video.js": "^8.23.4",
"web-haptics": "^0.0.6"
},
"devDependencies": {
"@eslint/js": "^9.9.1",
"@types/file-saver": "^2.0.7",
"@types/react": "^18.3.5",
"@types/react-dom": "^18.3.0",
"@types/react-input-mask": "^3.0.6",
"@types/uuid": "^10.0.0",
"@types/video.js": "^7.3.58",
"@vitejs/plugin-basic-ssl": "^2.3.0",
"@vitejs/plugin-react": "^6.0.1",
"autoprefixer": "^10.4.18",
"eslint": "^9.9.1",
"eslint-plugin-react-hooks": "^5.1.0-rc.0",
"eslint-plugin-react-refresh": "^0.4.11",
"eslint-plugin-unused-imports": "^4.3.0",
"globals": "^15.9.0",
"postcss": "^8.4.35",
"rollup-plugin-visualizer": "^7.0.1",
"tailwindcss": "^3.4.1",
"typescript": "^5.5.3",
"typescript-eslint": "^8.3.0",
"vite": "^8.0.11",
"wrangler": "^4.15.1"
},
"overrides": {
"framer-motion": "11.11.10"
}
}