import 'package:audio_service/audio_service.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import '../player/player_provider.dart'; import 'audio_cache_manager.dart'; import 'cache_settings_provider.dart'; import 'db.dart'; /// Listens to the player's currently-playing track. When it changes, /// computes the next-N tracks ahead in the queue and pins them via /// AudioCacheManager (source: autoPrefetch). After pinning, runs an /// eviction pass against the operator-set cap. /// /// The window N comes from cacheSettingsProvider.prefetchWindow /// (default 5, configurable in Settings). class Prefetcher { Prefetcher(this._ref) { // The mediaItem stream changes whenever the active track changes // (skipNext/skipPrev or natural progression). Settings changes (e.g. // operator bumps prefetch window) also trigger a reconcile. _ref.listen>(mediaItemProvider, (_, __) => _reconcile()); _ref.listen>(cacheSettingsProvider, (_, __) => _reconcile()); } final Ref _ref; Future _reconcile() async { final settings = _ref.read(cacheSettingsProvider).value; if (settings == null) return; final queue = _ref.read(queueProvider).value ?? const []; final current = _ref.read(mediaItemProvider).value; if (queue.isEmpty || current == null) return; final currentIdx = queue.indexWhere((m) => m.id == current.id); if (currentIdx < 0) return; final endIdx = (currentIdx + settings.prefetchWindow).clamp(0, queue.length - 1); final mgr = _ref.read(audioCacheManagerProvider); for (var i = currentIdx; i <= endIdx; i++) { final trackId = queue[i].id; if (await mgr.isCached(trackId)) continue; // Fire-and-forget; downloads happen in the background. Errors // inside pin() are caught + cleaned up internally. // ignore: unawaited_futures mgr.pin(trackId, source: CacheSource.autoPrefetch); } // Eviction pass after pinning new files. if (settings.capBytes > 0) { await mgr.evict(targetBytes: settings.capBytes); } } } /// Read this provider once at app start to activate the prefetcher. /// Constructor wires the listeners. final prefetcherProvider = Provider((ref) { return Prefetcher(ref); });