feat(player): add radio recording and real waveform
This commit is contained in:
@@ -12,6 +12,7 @@ import '../modelos/preset_ecualizador.dart';
|
||||
import '../servicios/servicio_audio.dart';
|
||||
import '../servicios/servicio_ecualizador.dart';
|
||||
import '../servicios/servicio_favoritos.dart';
|
||||
import '../servicios/servicio_grabacion_radio.dart';
|
||||
import '../servicios/servicio_radio.dart';
|
||||
import '../servicios/servicio_timer.dart';
|
||||
|
||||
@@ -22,15 +23,18 @@ class EstadoRadio extends ChangeNotifier {
|
||||
ServicioFavoritos? favoritos,
|
||||
ServicioRadio? radio,
|
||||
ServicioEcualizador? servicioEcualizador,
|
||||
ServicioGrabacionRadio? servicioGrabacion,
|
||||
Future<File> Function()? resolverArchivoCustom,
|
||||
bool iniciarAutomaticamente = true,
|
||||
}) : audio = audio ?? ServicioAudio(),
|
||||
favoritos = favoritos ?? ServicioFavoritos(),
|
||||
radio = radio ?? ServicioRadio(),
|
||||
servicioEcualizador = servicioEcualizador ?? ServicioEcualizador(),
|
||||
_resolverArchivoCustom = resolverArchivoCustom {
|
||||
}) : audio = audio ?? ServicioAudio(),
|
||||
favoritos = favoritos ?? ServicioFavoritos(),
|
||||
radio = radio ?? ServicioRadio(),
|
||||
servicioEcualizador = servicioEcualizador ?? ServicioEcualizador(),
|
||||
grabacion = servicioGrabacion ?? ServicioGrabacionRadio(),
|
||||
_resolverArchivoCustom = resolverArchivoCustom {
|
||||
timer = ServicioTimer(this.audio);
|
||||
_escucharErroresReproduccion();
|
||||
_escucharGrabacion();
|
||||
if (iniciarAutomaticamente) {
|
||||
_initFuture = _init();
|
||||
}
|
||||
@@ -40,10 +44,12 @@ class EstadoRadio extends ChangeNotifier {
|
||||
final ServicioFavoritos favoritos;
|
||||
final ServicioRadio radio;
|
||||
final ServicioEcualizador servicioEcualizador;
|
||||
final ServicioGrabacionRadio grabacion;
|
||||
final Future<File> Function()? _resolverArchivoCustom;
|
||||
|
||||
late final ServicioTimer timer;
|
||||
StreamSubscription<EstadoReproduccion>? _suscripcionEstadoAudio;
|
||||
StreamSubscription<EstadoGrabacionRadio>? _suscripcionGrabacion;
|
||||
Future<void>? _initFuture;
|
||||
int _revisionReproduccion = 0;
|
||||
Emisora? _emisoraSeleccionada;
|
||||
@@ -110,6 +116,10 @@ class EstadoRadio extends ChangeNotifier {
|
||||
return tienePresetEcualizadorPorEmisora(actual.uuid);
|
||||
}
|
||||
|
||||
EstadoGrabacionRadio get estadoGrabacion => grabacion.estado;
|
||||
bool get grabacionActiva => grabacion.estado.activa;
|
||||
String? get directorioGrabacion => grabacion.directorioConfigurado;
|
||||
|
||||
/// Lista principal (home): custom + populares, sin duplicados.
|
||||
List<Emisora> get emisorasInicio {
|
||||
final mapa = <String, Emisora>{};
|
||||
@@ -128,6 +138,7 @@ class EstadoRadio extends ChangeNotifier {
|
||||
}
|
||||
|
||||
Future<void> _init() async {
|
||||
await grabacion.inicializar();
|
||||
await _cargarEcualizadorPersistido();
|
||||
await Future.wait([
|
||||
cargarPopulares(),
|
||||
@@ -146,6 +157,16 @@ class EstadoRadio extends ChangeNotifier {
|
||||
});
|
||||
}
|
||||
|
||||
void _escucharGrabacion() {
|
||||
_suscripcionGrabacion = grabacion.estadoStream.listen((estado) {
|
||||
if (estado.tipo == EstadoGrabacionRadioTipo.error &&
|
||||
estado.error != null) {
|
||||
_errorController.add('Error al grabar la radio: ${estado.error}');
|
||||
}
|
||||
notifyListeners();
|
||||
});
|
||||
}
|
||||
|
||||
Future<void> _cargarEcualizadorPersistido() async {
|
||||
try {
|
||||
final config = await servicioEcualizador.cargar();
|
||||
@@ -296,7 +317,8 @@ class EstadoRadio extends ChangeNotifier {
|
||||
_paisCercanoDetectado = pais;
|
||||
_emisorasCercanas = await radio.buscar(pais: pais, limit: 30);
|
||||
} catch (_) {
|
||||
_errorCercanas = 'No pudimos detectar emisoras cercanas. Usa filtros por pais.';
|
||||
_errorCercanas =
|
||||
'No pudimos detectar emisoras cercanas. Usa filtros por pais.';
|
||||
_emisorasCercanas = [];
|
||||
} finally {
|
||||
_cargandoCercanas = false;
|
||||
@@ -331,6 +353,34 @@ class EstadoRadio extends ChangeNotifier {
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> iniciarGrabacion({Duration? duracion}) async {
|
||||
final actual = emisoraActual;
|
||||
if (actual == null) {
|
||||
_errorController.add('Primero selecciona una emisora para grabar.');
|
||||
return;
|
||||
}
|
||||
try {
|
||||
await grabacion.iniciar(actual, duracion: duracion);
|
||||
} catch (e) {
|
||||
_errorController.add('No se pudo iniciar la grabación: $e');
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> detenerGrabacion() => grabacion.detener();
|
||||
|
||||
Future<void> cambiarDirectorioGrabacion(String path) async {
|
||||
await grabacion.guardarDirectorio(path);
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
Future<void> restaurarDirectorioGrabacion() async {
|
||||
await grabacion.limpiarDirectorioConfigurado();
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
Future<String> directorioGrabacionEfectivo() =>
|
||||
grabacion.directorioEfectivo();
|
||||
|
||||
Future<void> togglePlay() async {
|
||||
await audio.togglePlay();
|
||||
notifyListeners();
|
||||
@@ -339,7 +389,10 @@ class EstadoRadio extends ChangeNotifier {
|
||||
Future<bool> toggleFavorito(Emisora emisora) async {
|
||||
final esFav = await favoritos.toggleFavorito(emisora);
|
||||
if (!esFav) {
|
||||
await deshabilitarPresetEcualizadorPorEmisora(emisora.uuid, notificar: false);
|
||||
await deshabilitarPresetEcualizadorPorEmisora(
|
||||
emisora.uuid,
|
||||
notificar: false,
|
||||
);
|
||||
}
|
||||
await cargarFavoritos();
|
||||
return esFav;
|
||||
@@ -418,7 +471,9 @@ class EstadoRadio extends ChangeNotifier {
|
||||
if (notificar) notifyListeners();
|
||||
}
|
||||
|
||||
Future<void> cambiarModoEcualizadorEmisoraActual({required bool usarPropio}) async {
|
||||
Future<void> cambiarModoEcualizadorEmisoraActual({
|
||||
required bool usarPropio,
|
||||
}) async {
|
||||
final actual = emisoraActual;
|
||||
if (actual == null) return;
|
||||
if (usarPropio) {
|
||||
@@ -433,7 +488,8 @@ class EstadoRadio extends ChangeNotifier {
|
||||
bool guardarPorEmisora = true,
|
||||
}) async {
|
||||
final actual = emisoraActual;
|
||||
final usarPresetPropio = guardarPorEmisora &&
|
||||
final usarPresetPropio =
|
||||
guardarPorEmisora &&
|
||||
actual != null &&
|
||||
_presetsEmisoraMap.containsKey(actual.uuid);
|
||||
|
||||
@@ -476,9 +532,10 @@ class EstadoRadio extends ChangeNotifier {
|
||||
}
|
||||
|
||||
final data = jsonDecode(await archivo.readAsString()) as List;
|
||||
_emisorasCustom = data
|
||||
.map((e) => Emisora.fromMap(Map<String, dynamic>.from(e as Map)))
|
||||
.toList();
|
||||
_emisorasCustom =
|
||||
data
|
||||
.map((e) => Emisora.fromMap(Map<String, dynamic>.from(e as Map)))
|
||||
.toList();
|
||||
} catch (_) {
|
||||
_emisorasCustom = [];
|
||||
}
|
||||
@@ -510,7 +567,8 @@ class EstadoRadio extends ChangeNotifier {
|
||||
}
|
||||
|
||||
// Compatibilidad con el nombre histórico (typo original).
|
||||
Future<void> eliminarEmitoraCustom(String uuid) => eliminarEmisoraCustom(uuid);
|
||||
Future<void> eliminarEmitoraCustom(String uuid) =>
|
||||
eliminarEmisoraCustom(uuid);
|
||||
|
||||
// ── Export / Import ───────────────────────────────────────────────────────
|
||||
|
||||
@@ -541,9 +599,10 @@ class EstadoRadio extends ChangeNotifier {
|
||||
}
|
||||
|
||||
final customRaw = data['emisorasCustom'] as List? ?? [];
|
||||
_emisorasCustom = customRaw
|
||||
.map((e) => Emisora.fromMap(Map<String, dynamic>.from(e as Map)))
|
||||
.toList();
|
||||
_emisorasCustom =
|
||||
customRaw
|
||||
.map((e) => Emisora.fromMap(Map<String, dynamic>.from(e as Map)))
|
||||
.toList();
|
||||
await _guardarEmisorasCustom();
|
||||
|
||||
final principalRaw = data['presetPrincipalEcualizador'];
|
||||
@@ -600,8 +659,10 @@ class EstadoRadio extends ChangeNotifier {
|
||||
@override
|
||||
void dispose() {
|
||||
_suscripcionEstadoAudio?.cancel();
|
||||
_suscripcionGrabacion?.cancel();
|
||||
_errorController.close();
|
||||
audio.dispose();
|
||||
unawaited(grabacion.dispose());
|
||||
timer.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user