Replace class-based animation toggle with inline animationPlayState
style. The transition-all class was conflicting with the CSS keyframe
animation, preventing the artwork from spinning when playing.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Replace static CSS eq-bars with a real-time AudioVisualizer component
using the Web Audio API AnalyserNode. The visualizer is now centered
below the progress bar in the inline hero player, with 28 bars and
boosted mid-range frequencies for better speech visualization.
- AudioContext: wire AnalyserNode, expose getFrequencyData, add
crossOrigin="anonymous" and key on the audio element
- AudioPlayer (inline): remove static eq-bars, add AudioVisualizer
- New AudioVisualizer component: requestAnimationFrame loop,
smooth decay on pause, configurable bar count/height/gap
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
- EpisodeCard: change 'Lu ?' → 'Marquer' so the toggle button no longer
shows a confusing question mark next to completed episodes
- Dashboard: fix episodes reappearing in 'Continuer l'écoute' after
navigation. handleToggleRead now does an optimistic update on BOTH
local state AND all TanStack Query cache pages (setQueriesData),
so the 'lu' state survives page navigation without a server roundtrip.
On error, cache is invalidated to revert to server truth.
- Dashboard: 'Découvrir' button now links to /add (discover new podcasts)
instead of /trending — was identical to 'Parcourir les tendances' button
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Add .github/workflows/docker-publish.yml: builds Docker image on every
push to main and publishes to ghcr.io/r0m1k3/podcastic:latest automatically
- docker-compose.yml: replace local `build:` with `image: ghcr.io/r0m1k3/podcastic:latest`
so Unraid's "Update" button can pull the image directly from the registry
instead of failing with "no service selected"
- Remove deprecated `version: '3.8'` field
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- max-w-[1400px] mx-auto dans le wrapper global (App.tsx)
pour que les cartes ne soient pas immenses sur 2K/4K
- padding cohérent lg:px-8 sur toutes les pages
- suppression du max-w-5xl parasites sur AddPodcast
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Crée PodcastCard réutilisable avec le même design qu'EpisodeCard
(image carrée, auteur, titre, footer avec actions). Remplace les
3 designs inline différents par un seul composant cohérent.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Toutes les pages utilisent grid-cols-1 sm:2 lg:3 xl:4 gap-8
- Cadre gradient visible en permanence sur l'avatar profil
- Suppression du badge NOUVEAU sur les épisodes
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- AudioPlayer is now purely presentational — no internal <audio> element or state.
All playback state (currentTime, duration, volume, speed, mute, error, isResuming)
comes from AudioContext, which holds the persistent <audio> at provider level.
This prevents audio from cutting when navigating between pages.
- userId is synced into AudioContext via setUserId() so progress saving/loading
works without the player needing to manage it locally.
- EpisodeCard: isCompleted now triggers at ≥90% listened (progressPercent >= 0.9)
in addition to the isCompleted flag, so the LU badge appears correctly.
Progress bar also hidden when ≥90%.
- Dashboard: episodesInProgress now excludes episodes at ≥90% listened,
removing them from the "Continuer l'écoute" section once effectively finished.
setUserId() called after user loads to wire context progress saving.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Replace the old compact floating bar (h-88px rounded-2rem with expand
button) with the same clean mini-bar design as the Dashboard sticky
sub-bar — artwork + title + seeks + play + time + speed/volume/expand/
close + thin progress bar. The Dashboard hero inline player is kept.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Refresh podcast title/author/imageUrl/description on RSS sync (fixes
legacy 'Unknown Podcast' records)
- Always-visible Resync button on Library cards
- LU badge on completed episodes, NOUVEAU badge on <7-day-old episodes
- AudioPlayer gains mode='inline' that embeds in the Dashboard hero card
- Floating player suppressed on /dashboard to avoid dual audio
- Sticky mini-bar appears via IntersectionObserver when inline player
scrolls out of view (shares the same <audio> element)
- Fix: 'Reprendre' button called undefined setSelectedEpisode; now calls
playEpisode
- Add progress field to Episode TS interface
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
EpisodeCard and AudioPlayer now display episode.imageUrl when available,
falling back to the podcast image only if the episode has none.
Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
AudioPlayer:
- Supprime section vitesse de lecture
- -30s/+30s (compact et expanded), avec icônes RotateCcw/RotateCw
- Rewind compact passé de -15s à -30s
Thème:
- globals.css refait : animations slide-up/fade-in/reveal définies, glass card améliorée
- Background plus profond (#080d1a), glow/blur plus soignés
- Boutons neon-button avec hover translateY
Corrections:
- Supprime Settings2 (bouton sans action) dans Library
- Supprime stat "2.4k mins listened" inventée dans Navigation
- Stats Library passent de 3 à 2 colonnes (réelles uniquement)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- podcastController: createPodcastFromRss sans feedData (parse le RSS réel)
- Discover.tsx: stub vide (remplacé par /trending et /add)
- App.tsx + Navigation.tsx: conflits déjà résolus
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Abonner deux fois au même podcast retourne 200 au lieu de 409.
Tous les messages d'erreur traduits en français.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
result.podcast est un plain object (toJSON()), appeler .toJSON() dessus crashe.
Fix: refetch le podcast depuis MongoDB après création pour avoir un vrai Document Mongoose.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- subscribe: passe undefined à createPodcastFromRss pour qu'il parse vraiment le flux RSS (bug: on passait feedData avec episodes:[] vide)
- trending/discover: retournent [] au lieu de 500 si l'API iTunes échoue
- trending: utilise l'iTunes RSS officiel (itunes.apple.com/fr/rss/toppodcasts) au lieu de l'API Marketing Tools instable
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Réécriture complète de podcastIndexService avec l'API iTunes d'Apple
- Recherche de podcasts : itunes.apple.com/search (gratuit, pas d'auth)
- Tendances : top podcasts France via rss.applemarketingtools.com + lookup RSS URLs
- Suppression des vérifications isConfigured() dans le contrôleur
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Permet d'abonner un podcast directement par son URL de flux RSS, sans passer par la recherche PodcastIndex.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Traduit toutes les pages et composants en français (Dashboard, Login, Discover, Navigation, Header, EpisodeCard, AudioPlayer)
- Corrige l'erreur 500 sur /api/podcasts/discover : z.coerce.number() au lieu de z.number() pour limit/offset (paramètres query string)
- Corrige l'abonnement manuel via URL RSS : crée le podcast depuis le flux RSS au lieu de renvoyer 400
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Remove 'type: module' and use CommonJS output so Node.js resolves
imports without requiring explicit .js extensions.
Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
- Remove separate backend/ and frontend/ Dockerfiles
- Add root Dockerfile (3-stage: frontend build → backend build → runtime)
- Frontend static files served by Express from /public
- Simplify docker-compose to 3 services: mongodb, redis, app (port 3579)
- Fix vite proxy not to strip /api prefix from paths
- Default API URL to relative /api instead of hardcoded localhost
Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>