363 lines
10 KiB
Dart
363 lines
10 KiB
Dart
part of '../di_providers.dart';
|
|
|
|
typedef TmdbMediaKey = ({String tmdbId, String mediaType});
|
|
typedef TmdbSeasonKey = ({String tmdbId, int seasonNumber});
|
|
|
|
|
|
typedef _TmdbReady = ({TmdbRequestConfig config, String language});
|
|
|
|
|
|
final _tmdbReadyProvider = FutureProvider<_TmdbReady?>((ref) async {
|
|
final settings = await ref.watch(tmdbSettingsProvider.future);
|
|
if (!settings.canRequest) return null;
|
|
|
|
if (settings.accessMode == TmdbAccessMode.smAccount) {
|
|
final account = await ref.watch(smAccountProvider.future);
|
|
if (!account.canUseDataPlane) return null;
|
|
final notifier = ref.read(smAccountProvider.notifier);
|
|
final token = await notifier.getAccessToken();
|
|
if (token == null) return null;
|
|
return (
|
|
config: TmdbRequestConfig(
|
|
apiBaseUrl: account.tmdbApiBaseUrl,
|
|
imageBaseUrl: TmdbSettingsState.officialImageBaseUrl,
|
|
accessToken: token,
|
|
apiKey: '',
|
|
onUnauthorized: () => notifier.getAccessToken(force: true),
|
|
),
|
|
language: settings.language,
|
|
);
|
|
}
|
|
|
|
return (
|
|
config: TmdbRequestConfig(
|
|
apiBaseUrl: settings.effectiveApiBaseUrl,
|
|
imageBaseUrl: settings.effectiveImageBaseUrl,
|
|
accessToken: settings.effectiveAccessToken,
|
|
apiKey: settings.effectiveApiKey,
|
|
),
|
|
language: settings.language,
|
|
);
|
|
});
|
|
|
|
|
|
final _tmdbCacheScopeProvider = FutureProvider<DiscoverPageCacheScope?>((
|
|
ref,
|
|
) async {
|
|
final settings = await ref.watch(tmdbSettingsProvider.future);
|
|
if (!settings.canRequest) return null;
|
|
|
|
if (settings.accessMode == TmdbAccessMode.smAccount) {
|
|
final account = await ref.watch(smAccountProvider.future);
|
|
if (!account.hasActiveSession) return null;
|
|
return DiscoverPageCacheScope(
|
|
apiBaseUrl: account.tmdbApiBaseUrl,
|
|
imageBaseUrl: TmdbSettingsState.officialImageBaseUrl,
|
|
language: settings.language,
|
|
);
|
|
}
|
|
|
|
return DiscoverPageCacheScope(
|
|
apiBaseUrl: settings.effectiveApiBaseUrl,
|
|
imageBaseUrl: settings.effectiveImageBaseUrl,
|
|
language: settings.language,
|
|
);
|
|
});
|
|
|
|
|
|
final tmdbEnrichedDetailProvider =
|
|
FutureProvider.family<TmdbEnrichedDetail?, TmdbMediaKey>((ref, key) async {
|
|
final ready = await ref.watch(_tmdbReadyProvider.future);
|
|
if (ready == null || key.tmdbId.isEmpty) return null;
|
|
return ref
|
|
.read(tmdbGatewayProvider)
|
|
.getEnrichedDetail(
|
|
ready.config,
|
|
key.tmdbId,
|
|
key.mediaType,
|
|
language: ready.language,
|
|
);
|
|
});
|
|
|
|
|
|
final tmdbImagesProvider = Provider.family<TmdbImageSet?, TmdbMediaKey>((
|
|
ref,
|
|
key,
|
|
) {
|
|
return ref.watch(tmdbEnrichedDetailProvider(key)).value?.images;
|
|
});
|
|
|
|
|
|
final tmdbMediaDetailProvider = Provider.family<TmdbMediaDetail?, TmdbMediaKey>(
|
|
(ref, key) {
|
|
return ref.watch(tmdbEnrichedDetailProvider(key)).value?.detail;
|
|
},
|
|
);
|
|
|
|
|
|
final tmdbSeasonDetailProvider =
|
|
FutureProvider.family<TmdbSeasonDetail?, TmdbSeasonKey>((ref, key) async {
|
|
final ready = await ref.watch(_tmdbReadyProvider.future);
|
|
if (ready == null || key.tmdbId.isEmpty) return null;
|
|
return ref
|
|
.read(tmdbGatewayProvider)
|
|
.getSeasonDetail(
|
|
ready.config,
|
|
key.tmdbId,
|
|
key.seasonNumber,
|
|
language: ready.language,
|
|
);
|
|
});
|
|
|
|
enum _TmdbRecommendationRowKind {
|
|
trendingDay,
|
|
trendingWeek,
|
|
popularMovies,
|
|
popularTv,
|
|
airingTodayTv,
|
|
topRatedMovies,
|
|
}
|
|
|
|
final _tmdbRecommendationRowProvider =
|
|
AsyncNotifierProvider.family<
|
|
_TmdbRecommendationRowNotifier,
|
|
TmdbRecommendationsRes?,
|
|
_TmdbRecommendationRowKind
|
|
>(_TmdbRecommendationRowNotifier.new);
|
|
|
|
final tmdbTrendingDayProvider = _tmdbRecommendationRowProvider(
|
|
_TmdbRecommendationRowKind.trendingDay,
|
|
);
|
|
|
|
final tmdbTrendingWeekProvider = _tmdbRecommendationRowProvider(
|
|
_TmdbRecommendationRowKind.trendingWeek,
|
|
);
|
|
|
|
final tmdbPopularMoviesProvider = _tmdbRecommendationRowProvider(
|
|
_TmdbRecommendationRowKind.popularMovies,
|
|
);
|
|
|
|
final tmdbPopularTvProvider = _tmdbRecommendationRowProvider(
|
|
_TmdbRecommendationRowKind.popularTv,
|
|
);
|
|
|
|
final tmdbAiringTodayTvProvider = _tmdbRecommendationRowProvider(
|
|
_TmdbRecommendationRowKind.airingTodayTv,
|
|
);
|
|
|
|
final tmdbTopRatedMoviesProvider = _tmdbRecommendationRowProvider(
|
|
_TmdbRecommendationRowKind.topRatedMovies,
|
|
);
|
|
|
|
|
|
typedef TmdbDiscoverKey = ({
|
|
String mediaType,
|
|
String sortBy,
|
|
String? withGenres,
|
|
String? voteCountGte,
|
|
});
|
|
|
|
final tmdbDiscoverProvider =
|
|
AsyncNotifierProvider.family<
|
|
_TmdbDiscoverRowNotifier,
|
|
TmdbRecommendationsRes?,
|
|
TmdbDiscoverKey
|
|
>(_TmdbDiscoverRowNotifier.new);
|
|
|
|
class _TmdbRecommendationRowNotifier
|
|
extends AsyncNotifier<TmdbRecommendationsRes?> {
|
|
final _TmdbRecommendationRowKind arg;
|
|
|
|
_TmdbRecommendationRowNotifier(this.arg);
|
|
|
|
@override
|
|
Future<TmdbRecommendationsRes?> build() async {
|
|
final rowKey = _rowKeyForKind(arg);
|
|
|
|
final scope = await ref.watch(_tmdbCacheScopeProvider.future);
|
|
if (scope == null) return null;
|
|
|
|
final cached = await _readDiscoverRowCacheByScope(ref, scope, rowKey);
|
|
if (cached != null) {
|
|
unawaited(_refreshFromNetwork(arg, rowKey));
|
|
return cached;
|
|
}
|
|
|
|
final ready = await ref.watch(_tmdbReadyProvider.future);
|
|
if (ready == null) return null;
|
|
return _fetchAndCache(arg, ready, rowKey);
|
|
}
|
|
|
|
Future<void> _refreshFromNetwork(
|
|
_TmdbRecommendationRowKind kind,
|
|
String rowKey,
|
|
) async {
|
|
try {
|
|
final ready = await ref.read(_tmdbReadyProvider.future);
|
|
if (ready == null) return;
|
|
final fresh = await _fetchAndCache(kind, ready, rowKey);
|
|
if (fresh != null) {
|
|
state = AsyncValue.data(fresh);
|
|
}
|
|
} catch (e) {
|
|
AppLogger.warn('TMDB', 'refresh discover row $rowKey failed', e);
|
|
}
|
|
}
|
|
|
|
Future<TmdbRecommendationsRes?> _fetchAndCache(
|
|
_TmdbRecommendationRowKind kind,
|
|
_TmdbReady ready,
|
|
String rowKey,
|
|
) async {
|
|
final fresh = await _fetchRow(kind, ready);
|
|
if (fresh != null) {
|
|
await _writeDiscoverRowCache(ref, ready, rowKey, fresh);
|
|
}
|
|
return fresh;
|
|
}
|
|
|
|
Future<TmdbRecommendationsRes?> _fetchRow(
|
|
_TmdbRecommendationRowKind kind,
|
|
_TmdbReady ready,
|
|
) {
|
|
final gateway = ref.read(tmdbGatewayProvider);
|
|
return switch (kind) {
|
|
_TmdbRecommendationRowKind.trendingDay => gateway.getTrending(
|
|
ready.config,
|
|
language: ready.language,
|
|
mediaType: 'all',
|
|
timeWindow: 'day',
|
|
),
|
|
_TmdbRecommendationRowKind.trendingWeek => gateway.getTrending(
|
|
ready.config,
|
|
language: ready.language,
|
|
mediaType: 'all',
|
|
timeWindow: 'week',
|
|
),
|
|
_TmdbRecommendationRowKind.popularMovies => gateway.getPopularMovies(
|
|
ready.config,
|
|
language: ready.language,
|
|
),
|
|
_TmdbRecommendationRowKind.popularTv => gateway.getPopularTv(
|
|
ready.config,
|
|
language: ready.language,
|
|
),
|
|
_TmdbRecommendationRowKind.airingTodayTv => gateway.getAiringTodayTv(
|
|
ready.config,
|
|
language: ready.language,
|
|
),
|
|
_TmdbRecommendationRowKind.topRatedMovies => gateway.getTopRatedMovies(
|
|
ready.config,
|
|
language: ready.language,
|
|
),
|
|
};
|
|
}
|
|
}
|
|
|
|
class _TmdbDiscoverRowNotifier extends AsyncNotifier<TmdbRecommendationsRes?> {
|
|
final TmdbDiscoverKey arg;
|
|
|
|
_TmdbDiscoverRowNotifier(this.arg);
|
|
|
|
@override
|
|
Future<TmdbRecommendationsRes?> build() async {
|
|
final rowKey = _rowKeyForDiscover(arg);
|
|
|
|
final scope = await ref.watch(_tmdbCacheScopeProvider.future);
|
|
if (scope == null) return null;
|
|
|
|
final cached = await _readDiscoverRowCacheByScope(ref, scope, rowKey);
|
|
if (cached != null) {
|
|
unawaited(_refreshFromNetwork(arg, rowKey));
|
|
return cached;
|
|
}
|
|
|
|
final ready = await ref.watch(_tmdbReadyProvider.future);
|
|
if (ready == null) return null;
|
|
return _fetchAndCache(arg, ready, rowKey);
|
|
}
|
|
|
|
Future<void> _refreshFromNetwork(TmdbDiscoverKey key, String rowKey) async {
|
|
try {
|
|
final ready = await ref.read(_tmdbReadyProvider.future);
|
|
if (ready == null) return;
|
|
final fresh = await _fetchAndCache(key, ready, rowKey);
|
|
if (fresh != null) {
|
|
state = AsyncValue.data(fresh);
|
|
}
|
|
} catch (e) {
|
|
AppLogger.warn('TMDB', 'refresh discover row $rowKey failed', e);
|
|
}
|
|
}
|
|
|
|
Future<TmdbRecommendationsRes?> _fetchAndCache(
|
|
TmdbDiscoverKey key,
|
|
_TmdbReady ready,
|
|
String rowKey,
|
|
) async {
|
|
final fresh = await ref
|
|
.read(tmdbGatewayProvider)
|
|
.getDiscover(
|
|
ready.config,
|
|
key.mediaType,
|
|
language: ready.language,
|
|
sortBy: key.sortBy,
|
|
withGenres: key.withGenres,
|
|
voteCountGte: key.voteCountGte,
|
|
);
|
|
if (fresh != null) {
|
|
await _writeDiscoverRowCache(ref, ready, rowKey, fresh);
|
|
}
|
|
return fresh;
|
|
}
|
|
}
|
|
|
|
DiscoverPageCacheScope _cacheScopeForReady(_TmdbReady ready) {
|
|
return DiscoverPageCacheScope(
|
|
apiBaseUrl: ready.config.apiBaseUrl,
|
|
imageBaseUrl: ready.config.imageBaseUrl,
|
|
language: ready.language,
|
|
);
|
|
}
|
|
|
|
Future<TmdbRecommendationsRes?> _readDiscoverRowCacheByScope(
|
|
Ref ref,
|
|
DiscoverPageCacheScope scope,
|
|
String rowKey,
|
|
) async {
|
|
final store = await ref.read(discoverPageCacheStoreProvider.future);
|
|
final entry = await store.readRow(scope, rowKey);
|
|
return entry?.data;
|
|
}
|
|
|
|
Future<void> _writeDiscoverRowCache(
|
|
Ref ref,
|
|
_TmdbReady ready,
|
|
String rowKey,
|
|
TmdbRecommendationsRes data,
|
|
) async {
|
|
final store = await ref.read(discoverPageCacheStoreProvider.future);
|
|
await store.writeRow(_cacheScopeForReady(ready), rowKey, data);
|
|
}
|
|
|
|
String _rowKeyForKind(_TmdbRecommendationRowKind kind) {
|
|
return switch (kind) {
|
|
_TmdbRecommendationRowKind.trendingDay => 'trending:day',
|
|
_TmdbRecommendationRowKind.trendingWeek => 'trending:week',
|
|
_TmdbRecommendationRowKind.popularMovies => 'popular:movie',
|
|
_TmdbRecommendationRowKind.popularTv => 'popular:tv',
|
|
_TmdbRecommendationRowKind.airingTodayTv => 'airing_today:tv',
|
|
_TmdbRecommendationRowKind.topRatedMovies => 'top_rated:movie',
|
|
};
|
|
}
|
|
|
|
String _rowKeyForDiscover(TmdbDiscoverKey key) {
|
|
return [
|
|
'discover',
|
|
key.mediaType,
|
|
key.sortBy,
|
|
key.withGenres ?? '',
|
|
key.voteCountGte ?? '',
|
|
].join(':');
|
|
}
|