import 'package:hooks_riverpod/hooks_riverpod.dart' show Ref; import 'package:logging/logging.dart' show Logger; import 'package:riverpod_annotation/riverpod_annotation.dart'; import 'package:shelfsdk/audiobookshelf_api.dart' show Author; import 'package:vaani/api/api_provider.dart' show authenticatedApiProvider; import 'package:vaani/api/library_provider.dart' show currentLibraryProvider; part 'library_browser_provider.g.dart'; /// Simple series data for display purposes class SimpleSeries { final String id; final String name; final int? numBooks; SimpleSeries({ required this.id, required this.name, this.numBooks, }); factory SimpleSeries.fromJson(Map json) { try { return SimpleSeries( id: json['id'] as String, name: json['name'] as String, numBooks: json['numBooks'] as int? ?? json['num_books'] as int? ?? (json['books'] as List?)?.length, ); } catch (e) { _logger.warning('Error parsing series: $e. JSON: $json'); rethrow; } } } final _logger = Logger('LibraryBrowserProvider'); /// Provider for fetching all authors in the current library @riverpod Future> libraryAuthors(Ref ref) async { final api = ref.watch(authenticatedApiProvider); final currentLibrary = await ref.watch(currentLibraryProvider.future); if (currentLibrary == null) { _logger.warning('No current library found'); return []; } final authors = await api.libraries.getAuthors(libraryId: currentLibrary.id); if (authors == null) { _logger.warning('Failed to fetch authors for library ${currentLibrary.id}'); return []; } _logger.fine('Fetched ${authors.length} authors'); return authors; } /// Provider for fetching all genres in the current library @riverpod Future> libraryGenres(Ref ref) async { final api = ref.watch(authenticatedApiProvider); final currentLibrary = await ref.watch(currentLibraryProvider.future); if (currentLibrary == null) { _logger.warning('No current library found'); return []; } // Use raw API call to avoid Series deserialization issues in LibraryFilterData final genres = await api.getJson>( path: '/api/libraries/${currentLibrary.id}/filterdata', requiresAuth: true, fromJson: (json) { if (json is Map && json.containsKey('genres')) { final genresList = json['genres'] as List; return genresList.map((e) => e.toString()).toList(); } return []; }, ); if (genres == null) { _logger.warning('Failed to fetch genres for library ${currentLibrary.id}'); return []; } _logger.fine('Fetched ${genres.length} genres'); return genres; } /// Provider for fetching all series in the current library @riverpod Future> librarySeries(Ref ref) async { final api = ref.watch(authenticatedApiProvider); final currentLibrary = await ref.watch(currentLibraryProvider.future); if (currentLibrary == null) { _logger.warning('No current library found'); return []; } try { // First try: Get from filterdata endpoint (same as genres) final filterDataSeries = await api.getJson>( path: '/api/libraries/${currentLibrary.id}/filterdata', requiresAuth: true, fromJson: (json) { _logger.info('FilterData API response keys: ${json is Map ? (json as Map).keys : json.runtimeType}'); if (json is Map && json.containsKey('series')) { final seriesList = json['series'] as List; _logger.info('Found ${seriesList.length} series in filterdata'); return seriesList .map((e) => SimpleSeries.fromJson(e as Map)) .toList(); } return []; }, ); if (filterDataSeries != null && filterDataSeries.isNotEmpty) { _logger.fine('Fetched ${filterDataSeries.length} series from filterdata'); return filterDataSeries; } // Second try: Get from series endpoint _logger.info('Trying series endpoint...'); final seriesList = await api.getJson>( path: '/api/libraries/${currentLibrary.id}/series', requiresAuth: true, fromJson: (json) { _logger.info('Series API response keys: ${json is Map ? (json as Map).keys : json.runtimeType}'); if (json is Map) { if (json.containsKey('results')) { final results = json['results'] as List; _logger.info('Found ${results.length} series in results'); return results .map((e) => SimpleSeries.fromJson(e as Map)) .toList(); } } return []; }, ); if (seriesList == null) { _logger.warning('Failed to fetch series for library ${currentLibrary.id}'); return []; } _logger.fine('Fetched ${seriesList.length} series'); return seriesList; } catch (e, stackTrace) { _logger.severe('Error fetching series: $e', e, stackTrace); rethrow; } }