feat(stations): add quality filters and list ordering
This commit is contained in:
+100
-17
@@ -18,6 +18,8 @@ import '../servicios/servicio_grabacion_radio.dart';
|
||||
import '../servicios/servicio_radio.dart';
|
||||
import '../servicios/servicio_timer.dart';
|
||||
|
||||
enum OrdenEmisoras { nombre, calidad }
|
||||
|
||||
/// Estado global de la app con ChangeNotifier (Provider).
|
||||
class EstadoRadio extends ChangeNotifier {
|
||||
EstadoRadio({
|
||||
@@ -86,8 +88,10 @@ class EstadoRadio extends ChangeNotifier {
|
||||
String? _ultimoPaisBusqueda;
|
||||
String? _ultimoIdiomaBusqueda;
|
||||
String? _ultimoTagBusqueda;
|
||||
int? _ultimoMinBitrateBusqueda;
|
||||
String? _errorCarga;
|
||||
static const _keyEmisoraPreferida = 'emisora_preferida_uuid_v1';
|
||||
static const _keyOrdenListas = 'orden_listas_emisoras_v1';
|
||||
static const _keyTimerSuenoPresets = 'timer_sueno_presets_segundos_v1';
|
||||
static const _timerSuenoPresetsDefecto = <int>[
|
||||
180,
|
||||
@@ -103,13 +107,14 @@ class EstadoRadio extends ChangeNotifier {
|
||||
List<int> _timerSuenoPresetsSegundos = List<int>.from(
|
||||
_timerSuenoPresetsDefecto,
|
||||
);
|
||||
OrdenEmisoras _ordenListas = OrdenEmisoras.calidad;
|
||||
|
||||
List<Emisora> get populares => _populares;
|
||||
List<Emisora> get tendencias => _tendencias;
|
||||
List<Emisora> get resultadosBusqueda => _resultadosBusqueda;
|
||||
List<Emisora> get emisorasCercanas => _emisorasCercanas;
|
||||
List<Emisora> get listaFavoritos => _listaFavoritos;
|
||||
List<Emisora> get emisorasCustom => _emisorasCustom;
|
||||
List<Emisora> get populares => _ordenarEmisoras(_populares);
|
||||
List<Emisora> get tendencias => _ordenarEmisoras(_tendencias);
|
||||
List<Emisora> get resultadosBusqueda => _ordenarEmisoras(_resultadosBusqueda);
|
||||
List<Emisora> get emisorasCercanas => _ordenarEmisoras(_emisorasCercanas);
|
||||
List<Emisora> get listaFavoritos => _ordenarEmisoras(_listaFavoritos);
|
||||
List<Emisora> get emisorasCustom => _ordenarEmisoras(_emisorasCustom);
|
||||
bool get cargandoPopulares => _cargandoPopulares;
|
||||
bool get cargandoBusqueda => _cargandoBusqueda;
|
||||
bool get cargandoMasBusqueda => _cargandoMasBusqueda;
|
||||
@@ -126,6 +131,7 @@ class EstadoRadio extends ChangeNotifier {
|
||||
PresetEcualizador get presetPrincipalEcualizador => _presetPrincipal;
|
||||
bool get ecualizadorActivo => _ecualizadorActivo;
|
||||
bool get ecualizadorDisponible => audio.ecualizadorDisponible;
|
||||
OrdenEmisoras get ordenListas => _ordenListas;
|
||||
List<int> get timerSuenoPresetsSegundos =>
|
||||
List<int>.unmodifiable(_timerSuenoPresetsSegundos);
|
||||
|
||||
@@ -145,6 +151,7 @@ class EstadoRadio extends ChangeNotifier {
|
||||
bool get grabacionActiva => grabacion.estado.activa;
|
||||
String? get directorioGrabacion => grabacion.directorioConfigurado;
|
||||
int get maxBytesGrabacion => grabacion.maxBytes;
|
||||
File? get ultimaGrabacion => grabacion.ultimoArchivo;
|
||||
|
||||
/// Lista principal (home): custom + populares, sin duplicados.
|
||||
List<Emisora> get emisorasInicio {
|
||||
@@ -189,6 +196,7 @@ class EstadoRadio extends ChangeNotifier {
|
||||
Future<void> _init() async {
|
||||
await grabacion.inicializar();
|
||||
await _cargarEcualizadorPersistido();
|
||||
await _cargarOrdenListas();
|
||||
await _cargarEmisoraPreferida();
|
||||
await _cargarTimerSuenoPresets();
|
||||
await Future.wait([
|
||||
@@ -314,6 +322,23 @@ class EstadoRadio extends ChangeNotifier {
|
||||
_emisoraPreferidaUuid = prefs.getString(_keyEmisoraPreferida);
|
||||
}
|
||||
|
||||
Future<void> _cargarOrdenListas() async {
|
||||
final prefs = await SharedPreferences.getInstance();
|
||||
final raw = prefs.getString(_keyOrdenListas);
|
||||
_ordenListas = switch (raw) {
|
||||
'nombre' => OrdenEmisoras.nombre,
|
||||
'calidad' => OrdenEmisoras.calidad,
|
||||
_ => OrdenEmisoras.calidad,
|
||||
};
|
||||
}
|
||||
|
||||
Future<void> cambiarOrdenListas(OrdenEmisoras orden) async {
|
||||
_ordenListas = orden;
|
||||
final prefs = await SharedPreferences.getInstance();
|
||||
await prefs.setString(_keyOrdenListas, orden.name);
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
Future<void> _normalizarEmisoraPreferida() async {
|
||||
final preferida = _resolverEmisoraPreferida();
|
||||
if (preferida?.uuid == _emisoraPreferidaUuid) return;
|
||||
@@ -351,28 +376,27 @@ class EstadoRadio extends ChangeNotifier {
|
||||
String? pais,
|
||||
String? idioma,
|
||||
String? tag,
|
||||
int? minBitrate,
|
||||
}) async {
|
||||
_ultimoNombreBusqueda = nombre;
|
||||
_ultimoPaisBusqueda = pais;
|
||||
_ultimoIdiomaBusqueda = idioma;
|
||||
_ultimoTagBusqueda = tag;
|
||||
_ultimoMinBitrateBusqueda = minBitrate;
|
||||
_offsetBusqueda = 0;
|
||||
_hayMasBusqueda = true;
|
||||
_cargandoBusqueda = true;
|
||||
_resultadosBusqueda = [];
|
||||
notifyListeners();
|
||||
try {
|
||||
final pagina = await radio.buscar(
|
||||
final pagina = await _buscarPaginaFiltrada(
|
||||
nombre: nombre,
|
||||
pais: pais,
|
||||
idioma: idioma,
|
||||
tag: tag,
|
||||
limit: _tamanoPaginaBusqueda,
|
||||
offset: _offsetBusqueda,
|
||||
minBitrate: minBitrate,
|
||||
);
|
||||
_resultadosBusqueda = pagina;
|
||||
_offsetBusqueda = pagina.length;
|
||||
_hayMasBusqueda = pagina.length == _tamanoPaginaBusqueda;
|
||||
} catch (_) {
|
||||
_errorController.add('Error en la busqueda. Comprueba tu conexion.');
|
||||
} finally {
|
||||
@@ -386,13 +410,12 @@ class EstadoRadio extends ChangeNotifier {
|
||||
_cargandoMasBusqueda = true;
|
||||
notifyListeners();
|
||||
try {
|
||||
final pagina = await radio.buscar(
|
||||
final pagina = await _buscarPaginaFiltrada(
|
||||
nombre: _ultimoNombreBusqueda,
|
||||
pais: _ultimoPaisBusqueda,
|
||||
idioma: _ultimoIdiomaBusqueda,
|
||||
tag: _ultimoTagBusqueda,
|
||||
limit: _tamanoPaginaBusqueda,
|
||||
offset: _offsetBusqueda,
|
||||
minBitrate: _ultimoMinBitrateBusqueda,
|
||||
);
|
||||
final porUuid = <String, Emisora>{
|
||||
for (final emisora in _resultadosBusqueda) emisora.uuid: emisora,
|
||||
@@ -407,8 +430,8 @@ class EstadoRadio extends ChangeNotifier {
|
||||
);
|
||||
}
|
||||
_resultadosBusqueda = nuevaLista;
|
||||
_offsetBusqueda += pagina.length;
|
||||
_hayMasBusqueda = pagina.length == _tamanoPaginaBusqueda;
|
||||
// _buscarPaginaFiltrada actualiza offset/hayMas usando p?ginas crudas.
|
||||
_hayMasBusqueda = _hayMasBusqueda && pagina.isNotEmpty;
|
||||
} catch (_) {
|
||||
_errorController.add('No se pudieron cargar mas emisoras.');
|
||||
} finally {
|
||||
@@ -417,6 +440,54 @@ class EstadoRadio extends ChangeNotifier {
|
||||
}
|
||||
}
|
||||
|
||||
Future<List<Emisora>> _buscarPaginaFiltrada({
|
||||
String? nombre,
|
||||
String? pais,
|
||||
String? idioma,
|
||||
String? tag,
|
||||
int? minBitrate,
|
||||
}) async {
|
||||
final acumuladas = <Emisora>[];
|
||||
var intentos = 0;
|
||||
while (intentos < 4 && acumuladas.isEmpty && _hayMasBusqueda) {
|
||||
final pagina = await radio.buscar(
|
||||
nombre: nombre,
|
||||
pais: pais,
|
||||
idioma: idioma,
|
||||
tag: tag,
|
||||
limit: _tamanoPaginaBusqueda,
|
||||
offset: _offsetBusqueda,
|
||||
);
|
||||
_offsetBusqueda += pagina.length;
|
||||
_hayMasBusqueda = pagina.length == _tamanoPaginaBusqueda;
|
||||
acumuladas.addAll(_filtrarMinBitrate(pagina, minBitrate));
|
||||
intentos++;
|
||||
}
|
||||
return acumuladas;
|
||||
}
|
||||
|
||||
List<Emisora> _filtrarMinBitrate(List<Emisora> emisoras, int? minBitrate) {
|
||||
if (minBitrate == null || minBitrate <= 0) return emisoras;
|
||||
return emisoras.where((e) => (e.bitrate ?? 0) >= minBitrate).toList();
|
||||
}
|
||||
|
||||
List<Emisora> _ordenarEmisoras(List<Emisora> emisoras) {
|
||||
final ordenadas = List<Emisora>.from(emisoras);
|
||||
switch (_ordenListas) {
|
||||
case OrdenEmisoras.nombre:
|
||||
ordenadas.sort(
|
||||
(a, b) => a.nombre.toLowerCase().compareTo(b.nombre.toLowerCase()),
|
||||
);
|
||||
case OrdenEmisoras.calidad:
|
||||
ordenadas.sort((a, b) {
|
||||
final porBitrate = (b.bitrate ?? 0).compareTo(a.bitrate ?? 0);
|
||||
if (porBitrate != 0) return porBitrate;
|
||||
return 0;
|
||||
});
|
||||
}
|
||||
return ordenadas;
|
||||
}
|
||||
|
||||
Future<void> cargarEmisorasCercanas() async {
|
||||
_cargandoCercanas = true;
|
||||
_errorCercanas = null;
|
||||
@@ -451,7 +522,10 @@ class EstadoRadio extends ChangeNotifier {
|
||||
throw Exception('No se pudo detectar tu region');
|
||||
}
|
||||
_paisCercanoDetectado = pais;
|
||||
_emisorasCercanas = await radio.buscar(pais: pais, limit: 30);
|
||||
_emisorasCercanas = _filtrarMinBitrate(
|
||||
await radio.buscar(pais: pais, limit: 30),
|
||||
_ultimoMinBitrateBusqueda,
|
||||
);
|
||||
} catch (_) {
|
||||
_errorCercanas =
|
||||
'No pudimos detectar emisoras cercanas. Usa filtros por pais.';
|
||||
@@ -527,6 +601,15 @@ class EstadoRadio extends ChangeNotifier {
|
||||
return launchUrl(uri, mode: LaunchMode.externalApplication);
|
||||
}
|
||||
|
||||
Future<bool> abrirUltimaGrabacion() async {
|
||||
final archivo = ultimaGrabacion;
|
||||
if (archivo == null || !await archivo.exists()) return false;
|
||||
return launchUrl(
|
||||
Uri.file(archivo.path),
|
||||
mode: LaunchMode.externalApplication,
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> cambiarDirectorioGrabacion(String path) async {
|
||||
await grabacion.guardarDirectorio(path);
|
||||
notifyListeners();
|
||||
|
||||
Reference in New Issue
Block a user