feat: Sync offline/online playback when able (#431)

Co-authored-by: PartyDonut <PartyDonut@users.noreply.github.com>
This commit is contained in:
PartyDonut 2025-08-03 13:35:56 +02:00 committed by GitHub
parent 15ac3566e2
commit 092836328f
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
42 changed files with 1002 additions and 497 deletions

View file

@ -74,9 +74,9 @@ class EpisodeDetailsProvider extends StateNotifier<EpisodeDetailModel> {
Future<void> _tryToCreateOfflineState(ItemBaseModel item) async {
final syncNotifier = ref.read(syncProvider.notifier);
final episodeModel = (await syncNotifier.getSyncedItem(item))?.itemModel as EpisodeModel?;
final episodeModel = (await syncNotifier.getSyncedItem(item.id))?.itemModel as EpisodeModel?;
if (episodeModel == null) return;
final seriesSyncedItem = await syncNotifier.getSyncedItem(episodeModel.parentBaseModel);
final seriesSyncedItem = await syncNotifier.getSyncedItem(episodeModel.parentBaseModel.id);
if (seriesSyncedItem == null) return;
final seriesModel = seriesSyncedItem.itemModel as SeriesModel?;
if (seriesModel == null) return;

View file

@ -6,7 +6,6 @@ import 'package:fladder/models/items/movie_model.dart';
import 'package:fladder/providers/api_provider.dart';
import 'package:fladder/providers/related_provider.dart';
import 'package:fladder/providers/service_provider.dart';
import 'package:fladder/providers/sync_provider.dart';
part 'movies_details_provider.g.dart';
@ -30,19 +29,10 @@ class MovieDetails extends _$MovieDetails {
state = newState.copyWith(related: related.body);
return null;
} catch (e) {
_tryToCreateOfflineState(item);
return null;
}
}
void _tryToCreateOfflineState(ItemBaseModel item) async {
final syncNotifier = ref.read(syncProvider.notifier);
final syncedItem = await syncNotifier.getParentItem(item.id);
if (syncedItem == null) return;
final movieModel = syncedItem.itemModel as MovieModel?;
state = movieModel;
}
void setSubIndex(int index) {
state = state?.copyWith(mediaStreams: state?.mediaStreams.copyWith(defaultSubStreamIndex: index));
}

View file

@ -6,7 +6,7 @@ part of 'movies_details_provider.dart';
// RiverpodGenerator
// **************************************************************************
String _$movieDetailsHash() => r'a9d8d2eeb7fa37652f25c1820b5e346efeeb59fc';
String _$movieDetailsHash() => r'322236f235bcd387cfecc3468c0876490d9afc39';
/// Copied from Dart SDK
class _SystemHash {

View file

@ -1,3 +1,5 @@
import 'dart:developer';
import 'package:chopper/chopper.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
@ -9,7 +11,6 @@ import 'package:fladder/models/items/series_model.dart';
import 'package:fladder/providers/api_provider.dart';
import 'package:fladder/providers/related_provider.dart';
import 'package:fladder/providers/service_provider.dart';
import 'package:fladder/providers/sync_provider.dart';
final seriesDetailsProvider =
StateNotifierProvider.autoDispose.family<SeriesDetailViewNotifier, SeriesModel?, String>((ref, id) {
@ -33,6 +34,14 @@ class SeriesDetailViewNotifier extends StateNotifier<SeriesModel?> {
if (response.body == null) return null;
newState = response.bodyOrThrow as SeriesModel;
final seasons = await api.showsSeriesIdSeasonsGet(seriesId: seriesModel.id, fields: [
ItemFields.mediastreams,
ItemFields.mediasources,
ItemFields.overview,
ItemFields.candownload,
ItemFields.childcount,
]);
final episodes = await api.showsSeriesIdEpisodesGet(seriesId: seriesModel.id, fields: [
ItemFields.mediastreams,
ItemFields.mediasources,
@ -45,14 +54,9 @@ class SeriesDetailViewNotifier extends StateNotifier<SeriesModel?> {
episodes.body?.items,
ref,
);
final episodesCanDownload = newEpisodes.any((episode) => episode.canDownload == true);
final seasons = await api.showsSeriesIdSeasonsGet(seriesId: seriesModel.id, fields: [
ItemFields.mediastreams,
ItemFields.mediasources,
ItemFields.overview,
ItemFields.candownload,
ItemFields.childcount,
]);
newState = newState.copyWith(
seasons: SeasonModel.seasonsFromDto(seasons.body?.items, ref)
.map((element) => element.copyWith(
@ -71,26 +75,11 @@ class SeriesDetailViewNotifier extends StateNotifier<SeriesModel?> {
state = newState.copyWith(related: related.body);
return response;
} catch (e) {
_tryToCreateOfflineState(seriesModel);
log("Error fetching series details: $e");
return null;
}
}
Future<void> _tryToCreateOfflineState(ItemBaseModel series) async {
final syncNotifier = ref.read(syncProvider.notifier);
final syncedItem = await syncNotifier.getSyncedItem(series);
if (syncedItem == null) return;
final seriesModel = syncedItem.itemModel as SeriesModel;
final allChildren = (await syncedItem.getNestedChildren(ref)).map((e) => e.itemModel).toList();
if (mounted) {
state = seriesModel.copyWith(
availableEpisodes: allChildren.whereType<EpisodeModel>().toList(),
seasons: allChildren.whereType<SeasonModel>().toList(),
);
}
return;
}
void updateEpisodeInfo(EpisodeModel episode) {
final index = state?.availableEpisodes?.indexOf(episode);