d754e28ddf8fe4da1d23f7896579f1480309cf0b
Three reported suspicions. Two confirmed by reading, one not.
1. CONFIRMED, self-inflicted. Playing a song from the phone and pressing
NEXT jumped to a radio station.
3398d02 taught skipToNext/skipToPrevious to fall back to station skipping
when there is no local queue, so the car's buttons would not be dead for
radio. But queue-less does not mean radio: tapping ONE track goes through
reproducirPistaLocal, which never builds a queue -- only folder playback
sets _colaLocal. That is exactly why the report said "at least the first
time".
emisoraActual cannot tell them apart either: _cambiarFuente fills it in for
every source, so a local MP3 arrives as an Emisora whose url is its
content:// document URI. The media id's scheme is the real discriminator,
the same test that already keeps the recorder off local files. A local
track now skips nowhere, which is the correct behaviour for a single item.
2. CONFIRMED mechanism. A failed station made the app disappear from the
Android Auto pane.
The error path published STATE_ERROR and then cleared everything:
`emisoraActual = null; mediaItem.add(null)`. That leaves the session in an
error state with no metadata at all, and Auto drops a session with nothing
to show -- reported as "if a station fails it seems to crash, and going to
1/3 it fails".
Both are kept now. Nothing outside servicio_audio.dart consumes mediaItem
(verified), so the phone is unaffected, and the car gains two things: the
screen can still name the station that failed instead of going blank, and
previous/next stay usable, so a driver can skip out of a dead station
instead of being stranded -- _saltarEmisora needs emisoraActual to know
where it is in the list. The error state itself is unchanged.
3. NOT CONFIRMED. A local track occasionally jumping to another one mid-play.
An advance requires a genuine `completed` from just_audio, so either the
player reports the end early -- plausible for a content:// SAF source,
whose duration is not always exact -- or something else moved the track.
Reading the code cannot separate those, so nothing was changed on a guess.
The advance now logs the decision with the processing state, position and
duration that caused it, so the next occurrence arrives with its reason
attached.
Tests: 1192 -> 1195.
PluriWave
Radio mundial con ecualizador personalizable, reconocimiento de canciones y UI premium.
Features
- +53.000 emisoras de 238 países (Radio Browser API)
- Ecualizador por emisora — guarda tu preset favorito para cada radio
- Reconocimiento de canciones — "¿Qué suena?" sin salir de la app
- Timer de auto-apagado — perfecto para dormir
- Reproducción en segundo plano — sigue sonando con la pantalla apagada
- Favoritos — acceso rápido a emisoras preferidas
- Compartir — envía emisoras a tus amigos
- UI premium — Material You, visualizador de audio, animaciones fluidas
Monetización
- 14 días PRO gratis al instalar
- Free: radio + favoritos + timer + background (con ads)
- PRO €2.99/año: sin ads + ecualizador + reconocimiento ilimitado
Stack
- Frontend: Flutter (Android + iOS)
- Radio API: Radio Browser (gratis, +53K emisoras)
- Audio: just_audio + audio_service
- Ecualizador: just_audio equalizer (Android nativo)
- Reconocimiento: AudD API (1000 req/mes free)
- Ads: Google AdMob
- Compras: in_app_purchase
CI/CD
Workflow Gitea Actions en .gitea/workflows/ci.yml, runner macmini-flutter.
Jobs: flutter pub get → flutter test → build apk --release → build appbundle --release
Artifacts: APK y AAB guardados en Gitea con nombre pluriwave-apk-<sha> / pluriwave-aab-<sha>.
Notificaciones: Telegram al completar (éxito ✅ / fallo ❌).
Secrets necesarios en el repo:
| Secret | Uso |
|---|---|
TELEGRAM_BOT_TOKEN |
Notificaciones CI |
TELEGRAM_CHAT_ID |
Canal de destino |
Signing: build de release usa clave debug (válido para CI interno). Para Play Store se requiere keystore como secret adicional.
Desarrollador
FreeTimeLab — freetimelab.es
Licencia
MIT
Description
PluriWave — Radio mundial con ecualizador, reconocimiento de canciones y UI premium
49 MiB
Languages
Dart
86%
HTML
7.1%
Kotlin
5.1%
JavaScript
1.6%
Swift
0.1%