// "Shuffle all" source (#427 S4). Always-present; the pool degrades // gracefully with reachability: // online → GET /api/library/shuffle (random over the whole library) // offline → a client shuffle over the entire local cache index // // Offline is a UNION over every cached track regardless of storage // bucket — liked AND recently-played both included. The two-bucket // split (S2) is storage/eviction-only and never filters playback, // which is exactly the property this relies on. import 'dart:math'; import 'package:drift/drift.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import '../library/library_providers.dart' show libraryApiProvider; import '../models/track.dart'; import 'adapters.dart'; import 'audio_cache_manager.dart' show appDbProvider; import 'offline_provider.dart'; class ShuffleSource { ShuffleSource(this._ref); final Ref _ref; /// Returns up to [limit] tracks to play as a shuffle-all. Online: /// server-random. Offline: every cached track (liked + recent), /// shuffled client-side. Empty offline → nothing is cached yet. Future> tracks({int limit = 100}) async { if (_ref.read(offlineProvider)) { return _offline(limit); } final api = await _ref.read(libraryApiProvider.future); return api.shuffle(limit: limit); } Future> _offline(int limit) async { final db = _ref.read(appDbProvider); final cachedIds = (await db.select(db.audioCacheIndex).get()).map((r) => r.trackId).toSet(); if (cachedIds.isEmpty) return const []; final meta = await (db.select(db.cachedTracks) ..where((t) => t.id.isIn(cachedIds.toList()))) .get(); if (meta.isEmpty) return const []; final artistName = { for (final a in await db.select(db.cachedArtists).get()) a.id: a.name }; final albumTitle = { for (final a in await db.select(db.cachedAlbums).get()) a.id: a.title }; final refs = [ for (final t in meta) t.toRef( artistName: artistName[t.artistId] ?? '', albumTitle: albumTitle[t.albumId] ?? '', ) ]; refs.shuffle(Random()); return refs.length > limit ? refs.sublist(0, limit) : refs; } } final shuffleSourceProvider = Provider((ref) => ShuffleSource(ref));