mirror of
https://github.com/gabehf/Fladder.git
synced 2026-03-08 23:18:16 -07:00
feature: Re-implemented syncing
This commit is contained in:
parent
c5c7f71b84
commit
86ff355e21
51 changed files with 3067 additions and 1147 deletions
|
|
@ -72,20 +72,16 @@ class EpisodeDetailsProvider extends StateNotifier<EpisodeDetailModel> {
|
|||
}
|
||||
}
|
||||
|
||||
void _tryToCreateOfflineState(ItemBaseModel item) {
|
||||
Future<void> _tryToCreateOfflineState(ItemBaseModel item) async {
|
||||
final syncNotifier = ref.read(syncProvider.notifier);
|
||||
final episodeModel = syncNotifier.getSyncedItem(item)?.createItemModel(ref) as EpisodeModel?;
|
||||
final episodeModel = (await syncNotifier.getSyncedItem(item))?.itemModel as EpisodeModel?;
|
||||
if (episodeModel == null) return;
|
||||
final seriesSyncedItem = syncNotifier.getSyncedItem(episodeModel.parentBaseModel);
|
||||
final seriesSyncedItem = await syncNotifier.getSyncedItem(episodeModel.parentBaseModel);
|
||||
if (seriesSyncedItem == null) return;
|
||||
final seriesModel = seriesSyncedItem.createItemModel(ref) as SeriesModel?;
|
||||
final seriesModel = seriesSyncedItem.itemModel as SeriesModel?;
|
||||
if (seriesModel == null) return;
|
||||
final episodes = syncNotifier
|
||||
.getNestedChildren(seriesSyncedItem)
|
||||
.map(
|
||||
(e) => e.createItemModel(ref),
|
||||
)
|
||||
.nonNulls
|
||||
final episodes = (await syncNotifier.getNestedChildren(seriesSyncedItem))
|
||||
.map((e) => e.itemModel)
|
||||
.whereType<EpisodeModel>()
|
||||
.toList();
|
||||
state = state.copyWith(
|
||||
|
|
|
|||
|
|
@ -35,11 +35,11 @@ class MovieDetails extends _$MovieDetails {
|
|||
}
|
||||
}
|
||||
|
||||
void _tryToCreateOfflineState(ItemBaseModel item) {
|
||||
void _tryToCreateOfflineState(ItemBaseModel item) async {
|
||||
final syncNotifier = ref.read(syncProvider.notifier);
|
||||
final syncedItem = syncNotifier.getParentItem(item.id);
|
||||
final syncedItem = await syncNotifier.getParentItem(item.id);
|
||||
if (syncedItem == null) return;
|
||||
final movieModel = syncedItem.createItemModel(ref) as MovieModel;
|
||||
final movieModel = syncedItem.itemModel as MovieModel?;
|
||||
state = movieModel;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ part of 'movies_details_provider.dart';
|
|||
// RiverpodGenerator
|
||||
// **************************************************************************
|
||||
|
||||
String _$movieDetailsHash() => r'872ea61464ef8493c7e6c559c526377f1c8f6a6d';
|
||||
String _$movieDetailsHash() => r'a9d8d2eeb7fa37652f25c1820b5e346efeeb59fc';
|
||||
|
||||
/// Copied from Dart SDK
|
||||
class _SystemHash {
|
||||
|
|
|
|||
|
|
@ -29,7 +29,12 @@ class SeasonDetailsNotifier extends StateNotifier<SeasonModel?> {
|
|||
seriesId: newState?.seriesId ?? "",
|
||||
seasonId: newState?.id,
|
||||
season: newState?.season,
|
||||
fields: [ItemFields.overview],
|
||||
fields: [
|
||||
ItemFields.overview,
|
||||
ItemFields.candelete,
|
||||
ItemFields.candownload,
|
||||
ItemFields.parentid,
|
||||
],
|
||||
);
|
||||
newState = newState?.copyWith(episodes: EpisodeModel.episodesFromDto(episodes.body?.items, ref).toList());
|
||||
state = newState;
|
||||
|
|
|
|||
|
|
@ -38,6 +38,7 @@ class SeriesDetailViewNotifier extends StateNotifier<SeriesModel?> {
|
|||
ItemFields.mediasources,
|
||||
ItemFields.overview,
|
||||
ItemFields.candownload,
|
||||
ItemFields.childcount,
|
||||
]);
|
||||
|
||||
final newEpisodes = EpisodeModel.episodesFromDto(
|
||||
|
|
@ -45,10 +46,19 @@ class SeriesDetailViewNotifier extends StateNotifier<SeriesModel?> {
|
|||
ref,
|
||||
);
|
||||
final episodesCanDownload = newEpisodes.any((episode) => episode.canDownload == true);
|
||||
final seasons = await api.showsSeriesIdSeasonsGet(seriesId: seriesModel.id);
|
||||
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(canDownload: true))
|
||||
.map((element) => element.copyWith(
|
||||
canDownload: true,
|
||||
episodes: newEpisodes.where((episode) => episode.season == element.season).toList(),
|
||||
))
|
||||
.toList(),
|
||||
);
|
||||
|
||||
|
|
@ -68,20 +78,16 @@ class SeriesDetailViewNotifier extends StateNotifier<SeriesModel?> {
|
|||
|
||||
Future<void> _tryToCreateOfflineState(ItemBaseModel series) async {
|
||||
final syncNotifier = ref.read(syncProvider.notifier);
|
||||
final syncedItem = syncNotifier.getSyncedItem(series);
|
||||
final syncedItem = await syncNotifier.getSyncedItem(series);
|
||||
if (syncedItem == null) return;
|
||||
final seriesModel = syncedItem.createItemModel(ref) as SeriesModel;
|
||||
final allChildren = syncedItem
|
||||
.getNestedChildren(ref)
|
||||
.map(
|
||||
(e) => e.createItemModel(ref),
|
||||
)
|
||||
.nonNulls
|
||||
.toList();
|
||||
state = seriesModel.copyWith(
|
||||
availableEpisodes: allChildren.whereType<EpisodeModel>().toList(),
|
||||
seasons: allChildren.whereType<SeasonModel>().toList(),
|
||||
);
|
||||
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;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -536,7 +536,10 @@ class JellyService {
|
|||
return api.showsSeriesIdEpisodesGet(
|
||||
seriesId: seriesId,
|
||||
userId: account?.id,
|
||||
fields: fields,
|
||||
fields: [
|
||||
...?fields,
|
||||
ItemFields.parentid,
|
||||
],
|
||||
isMissing: isMissing,
|
||||
limit: limit,
|
||||
sortBy: sortBy,
|
||||
|
|
@ -694,7 +697,10 @@ class JellyService {
|
|||
seriesId: seriesId,
|
||||
isMissing: isMissing,
|
||||
enableUserData: enableUserData,
|
||||
fields: fields,
|
||||
fields: [
|
||||
...?fields,
|
||||
ItemFields.parentid,
|
||||
],
|
||||
);
|
||||
|
||||
Future<Response<QueryFilters>> itemsFilters2Get({
|
||||
|
|
@ -903,37 +909,37 @@ class JellyService {
|
|||
|
||||
Future<Response<dynamic>> deleteItem(String itemId) => api.itemsItemIdDelete(itemId: itemId);
|
||||
|
||||
Future<UserConfiguration?> _updateUserConfiguration(UserConfiguration newUserConfiguration) async {
|
||||
if (account?.id == null) return null;
|
||||
Future<UserConfiguration?> _updateUserConfiguration(UserConfiguration newUserConfiguration) async {
|
||||
if (account?.id == null) return null;
|
||||
|
||||
final response = await api.usersConfigurationPost(
|
||||
userId: account!.id,
|
||||
body: newUserConfiguration,
|
||||
);
|
||||
final response = await api.usersConfigurationPost(
|
||||
userId: account!.id,
|
||||
body: newUserConfiguration,
|
||||
);
|
||||
|
||||
if (response.isSuccessful) {
|
||||
return newUserConfiguration;
|
||||
if (response.isSuccessful) {
|
||||
return newUserConfiguration;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
Future<UserConfiguration?> updateRememberAudioSelections() {
|
||||
final currentUserConfiguration = account?.userConfiguration;
|
||||
if (currentUserConfiguration == null) return Future.value(null);
|
||||
Future<UserConfiguration?> updateRememberAudioSelections() {
|
||||
final currentUserConfiguration = account?.userConfiguration;
|
||||
if (currentUserConfiguration == null) return Future.value(null);
|
||||
|
||||
final updated = currentUserConfiguration.copyWith(
|
||||
rememberAudioSelections: !(currentUserConfiguration.rememberAudioSelections ?? false),
|
||||
);
|
||||
return _updateUserConfiguration(updated);
|
||||
}
|
||||
final updated = currentUserConfiguration.copyWith(
|
||||
rememberAudioSelections: !(currentUserConfiguration.rememberAudioSelections ?? false),
|
||||
);
|
||||
return _updateUserConfiguration(updated);
|
||||
}
|
||||
|
||||
Future<UserConfiguration?> updateRememberSubtitleSelections() {
|
||||
final current = account?.userConfiguration;
|
||||
if (current == null) return Future.value(null);
|
||||
Future<UserConfiguration?> updateRememberSubtitleSelections() {
|
||||
final current = account?.userConfiguration;
|
||||
if (current == null) return Future.value(null);
|
||||
|
||||
final updated = current.copyWith(
|
||||
rememberSubtitleSelections: !(current.rememberSubtitleSelections ?? false),
|
||||
);
|
||||
return _updateUserConfiguration(updated);
|
||||
}
|
||||
final updated = current.copyWith(
|
||||
rememberSubtitleSelections: !(current.rememberSubtitleSelections ?? false),
|
||||
);
|
||||
return _updateUserConfiguration(updated);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,43 +1,34 @@
|
|||
import 'package:isar/isar.dart';
|
||||
import 'package:background_downloader/background_downloader.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:riverpod_annotation/riverpod_annotation.dart';
|
||||
|
||||
import 'package:fladder/models/item_base_model.dart';
|
||||
import 'package:fladder/models/syncing/download_stream.dart';
|
||||
import 'package:fladder/models/syncing/i_synced_item.dart';
|
||||
import 'package:fladder/models/syncing/sync_item.dart';
|
||||
import 'package:fladder/providers/sync_provider.dart';
|
||||
|
||||
part 'sync_provider_helpers.g.dart';
|
||||
|
||||
@riverpod
|
||||
class SyncChildren extends _$SyncChildren {
|
||||
@override
|
||||
List<SyncedItem> build(SyncedItem root) {
|
||||
final isar = ref.watch(syncProvider.notifier).isar;
|
||||
final syncPath = ref.read(syncProvider.notifier).syncPath ?? "";
|
||||
|
||||
if (isar == null) return [];
|
||||
|
||||
final all = <SyncedItem>[];
|
||||
List<SyncedItem> toProcess = [root];
|
||||
|
||||
while (toProcess.isNotEmpty) {
|
||||
final parentIds = toProcess.map((e) => e.id).toList();
|
||||
|
||||
final children = <ISyncedItem>[];
|
||||
for (final id in parentIds) {
|
||||
final results = isar.iSyncedItems.where().parentIdEqualTo(id).sortBySortName().findAll();
|
||||
children.addAll(results);
|
||||
}
|
||||
|
||||
if (children.isEmpty) break;
|
||||
|
||||
final wrapped = children.map((e) => SyncedItem.fromIsar(e, syncPath)).toList();
|
||||
all.addAll(wrapped);
|
||||
toProcess = wrapped;
|
||||
}
|
||||
|
||||
return all;
|
||||
Stream<SyncedItem?> syncedItem(Ref ref, ItemBaseModel? item) {
|
||||
final id = item?.id;
|
||||
if (id == null || id.isEmpty) {
|
||||
return Stream.value(null);
|
||||
}
|
||||
|
||||
return ref.watch(syncProvider.notifier).db.getItem(id).watchSingleOrNull();
|
||||
}
|
||||
|
||||
@riverpod
|
||||
class SyncedChildren extends _$SyncedChildren {
|
||||
@override
|
||||
FutureOr<List<SyncedItem>> build(SyncedItem item) => ref.read(syncProvider.notifier).getChildren(item);
|
||||
}
|
||||
|
||||
@riverpod
|
||||
class SyncedNestedChildren extends _$SyncedNestedChildren {
|
||||
@override
|
||||
FutureOr<List<SyncedItem>> build(SyncedItem item) => ref.read(syncProvider.notifier).getNestedChildren(item);
|
||||
}
|
||||
|
||||
@riverpod
|
||||
|
|
@ -55,49 +46,33 @@ class SyncDownloadStatus extends _$SyncDownloadStatus {
|
|||
int downloadCount = 0;
|
||||
double fullProgress = mainStream.hasDownload ? mainStream.progress : 0.0;
|
||||
|
||||
int fullySyncedChildren = 0;
|
||||
|
||||
for (var i = 0; i < nestedChildren.length; i++) {
|
||||
final childItem = nestedChildren[i];
|
||||
final downloadStream = ref.read(downloadTasksProvider(childItem.id));
|
||||
if (childItem.videoFile.existsSync()) {
|
||||
fullySyncedChildren++;
|
||||
}
|
||||
if (downloadStream.hasDownload) {
|
||||
downloadCount++;
|
||||
fullProgress += downloadStream.progress;
|
||||
mainStream = mainStream.copyWith(status: downloadStream.status);
|
||||
mainStream = mainStream.copyWith(
|
||||
status: mainStream.status != TaskStatus.running ? downloadStream.status : mainStream.status,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
int syncAbleChildren = nestedChildren.where((element) => element.hasVideoFile).length;
|
||||
|
||||
var fullySynced = nestedChildren.isNotEmpty ? fullySyncedChildren == syncAbleChildren : arg.videoFile.existsSync();
|
||||
return mainStream.copyWith(
|
||||
status: fullySynced ? TaskStatus.complete : mainStream.status,
|
||||
progress: fullProgress / downloadCount.clamp(1, double.infinity).toInt(),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@riverpod
|
||||
class SyncStatuses extends _$SyncStatuses {
|
||||
@override
|
||||
FutureOr<SyncStatus> build(SyncedItem arg, List<SyncedItem>? children) async {
|
||||
final nestedChildren = children;
|
||||
|
||||
ref.watch(downloadTasksProvider(arg.id));
|
||||
if (nestedChildren != null) {
|
||||
for (var element in nestedChildren) {
|
||||
ref.watch(downloadTasksProvider(element.id));
|
||||
}
|
||||
|
||||
for (var i = 0; i < nestedChildren.length; i++) {
|
||||
final item = nestedChildren[i];
|
||||
if (item.hasVideoFile && !await item.videoFile.exists()) {
|
||||
return SyncStatus.partially;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (arg.hasVideoFile && !await arg.videoFile.exists()) {
|
||||
return SyncStatus.partially;
|
||||
}
|
||||
return SyncStatus.complete;
|
||||
}
|
||||
}
|
||||
|
||||
@riverpod
|
||||
class SyncSize extends _$SyncSize {
|
||||
@override
|
||||
|
|
@ -112,7 +87,9 @@ class SyncSize extends _$SyncSize {
|
|||
ref.watch(downloadTasksProvider(element.id));
|
||||
}
|
||||
for (var element in nestedChildren) {
|
||||
size += element.fileSize ?? 0;
|
||||
if (element.videoFile.existsSync()) {
|
||||
size += element.fileSize ?? 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ part of 'sync_provider_helpers.dart';
|
|||
// RiverpodGenerator
|
||||
// **************************************************************************
|
||||
|
||||
String _$syncChildrenHash() => r'c5a90d630d49f59ad4fbaacb5154f1205799f5ab';
|
||||
String _$syncedItemHash() => r'7b1178ba78529ebf65425aa4cb8b239a28c7914b';
|
||||
|
||||
/// Copied from Dart SDK
|
||||
class _SystemHash {
|
||||
|
|
@ -29,39 +29,30 @@ class _SystemHash {
|
|||
}
|
||||
}
|
||||
|
||||
abstract class _$SyncChildren
|
||||
extends BuildlessAutoDisposeNotifier<List<SyncedItem>> {
|
||||
late final SyncedItem root;
|
||||
/// See also [syncedItem].
|
||||
@ProviderFor(syncedItem)
|
||||
const syncedItemProvider = SyncedItemFamily();
|
||||
|
||||
List<SyncedItem> build(
|
||||
SyncedItem root,
|
||||
);
|
||||
}
|
||||
/// See also [syncedItem].
|
||||
class SyncedItemFamily extends Family<AsyncValue<SyncedItem?>> {
|
||||
/// See also [syncedItem].
|
||||
const SyncedItemFamily();
|
||||
|
||||
/// See also [SyncChildren].
|
||||
@ProviderFor(SyncChildren)
|
||||
const syncChildrenProvider = SyncChildrenFamily();
|
||||
|
||||
/// See also [SyncChildren].
|
||||
class SyncChildrenFamily extends Family<List<SyncedItem>> {
|
||||
/// See also [SyncChildren].
|
||||
const SyncChildrenFamily();
|
||||
|
||||
/// See also [SyncChildren].
|
||||
SyncChildrenProvider call(
|
||||
SyncedItem root,
|
||||
/// See also [syncedItem].
|
||||
SyncedItemProvider call(
|
||||
ItemBaseModel? item,
|
||||
) {
|
||||
return SyncChildrenProvider(
|
||||
root,
|
||||
return SyncedItemProvider(
|
||||
item,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
SyncChildrenProvider getProviderOverride(
|
||||
covariant SyncChildrenProvider provider,
|
||||
SyncedItemProvider getProviderOverride(
|
||||
covariant SyncedItemProvider provider,
|
||||
) {
|
||||
return call(
|
||||
provider.root,
|
||||
provider.item,
|
||||
);
|
||||
}
|
||||
|
||||
|
|
@ -77,81 +68,75 @@ class SyncChildrenFamily extends Family<List<SyncedItem>> {
|
|||
_allTransitiveDependencies;
|
||||
|
||||
@override
|
||||
String? get name => r'syncChildrenProvider';
|
||||
String? get name => r'syncedItemProvider';
|
||||
}
|
||||
|
||||
/// See also [SyncChildren].
|
||||
class SyncChildrenProvider
|
||||
extends AutoDisposeNotifierProviderImpl<SyncChildren, List<SyncedItem>> {
|
||||
/// See also [SyncChildren].
|
||||
SyncChildrenProvider(
|
||||
SyncedItem root,
|
||||
/// See also [syncedItem].
|
||||
class SyncedItemProvider extends AutoDisposeStreamProvider<SyncedItem?> {
|
||||
/// See also [syncedItem].
|
||||
SyncedItemProvider(
|
||||
ItemBaseModel? item,
|
||||
) : this._internal(
|
||||
() => SyncChildren()..root = root,
|
||||
from: syncChildrenProvider,
|
||||
name: r'syncChildrenProvider',
|
||||
(ref) => syncedItem(
|
||||
ref as SyncedItemRef,
|
||||
item,
|
||||
),
|
||||
from: syncedItemProvider,
|
||||
name: r'syncedItemProvider',
|
||||
debugGetCreateSourceHash:
|
||||
const bool.fromEnvironment('dart.vm.product')
|
||||
? null
|
||||
: _$syncChildrenHash,
|
||||
dependencies: SyncChildrenFamily._dependencies,
|
||||
: _$syncedItemHash,
|
||||
dependencies: SyncedItemFamily._dependencies,
|
||||
allTransitiveDependencies:
|
||||
SyncChildrenFamily._allTransitiveDependencies,
|
||||
root: root,
|
||||
SyncedItemFamily._allTransitiveDependencies,
|
||||
item: item,
|
||||
);
|
||||
|
||||
SyncChildrenProvider._internal(
|
||||
SyncedItemProvider._internal(
|
||||
super._createNotifier, {
|
||||
required super.name,
|
||||
required super.dependencies,
|
||||
required super.allTransitiveDependencies,
|
||||
required super.debugGetCreateSourceHash,
|
||||
required super.from,
|
||||
required this.root,
|
||||
required this.item,
|
||||
}) : super.internal();
|
||||
|
||||
final SyncedItem root;
|
||||
final ItemBaseModel? item;
|
||||
|
||||
@override
|
||||
List<SyncedItem> runNotifierBuild(
|
||||
covariant SyncChildren notifier,
|
||||
Override overrideWith(
|
||||
Stream<SyncedItem?> Function(SyncedItemRef provider) create,
|
||||
) {
|
||||
return notifier.build(
|
||||
root,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Override overrideWith(SyncChildren Function() create) {
|
||||
return ProviderOverride(
|
||||
origin: this,
|
||||
override: SyncChildrenProvider._internal(
|
||||
() => create()..root = root,
|
||||
override: SyncedItemProvider._internal(
|
||||
(ref) => create(ref as SyncedItemRef),
|
||||
from: from,
|
||||
name: null,
|
||||
dependencies: null,
|
||||
allTransitiveDependencies: null,
|
||||
debugGetCreateSourceHash: null,
|
||||
root: root,
|
||||
item: item,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
AutoDisposeNotifierProviderElement<SyncChildren, List<SyncedItem>>
|
||||
createElement() {
|
||||
return _SyncChildrenProviderElement(this);
|
||||
AutoDisposeStreamProviderElement<SyncedItem?> createElement() {
|
||||
return _SyncedItemProviderElement(this);
|
||||
}
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
return other is SyncChildrenProvider && other.root == root;
|
||||
return other is SyncedItemProvider && other.item == item;
|
||||
}
|
||||
|
||||
@override
|
||||
int get hashCode {
|
||||
var hash = _SystemHash.combine(0, runtimeType.hashCode);
|
||||
hash = _SystemHash.combine(hash, root.hashCode);
|
||||
hash = _SystemHash.combine(hash, item.hashCode);
|
||||
|
||||
return _SystemHash.finish(hash);
|
||||
}
|
||||
|
|
@ -159,22 +144,316 @@ class SyncChildrenProvider
|
|||
|
||||
@Deprecated('Will be removed in 3.0. Use Ref instead')
|
||||
// ignore: unused_element
|
||||
mixin SyncChildrenRef on AutoDisposeNotifierProviderRef<List<SyncedItem>> {
|
||||
/// The parameter `root` of this provider.
|
||||
SyncedItem get root;
|
||||
mixin SyncedItemRef on AutoDisposeStreamProviderRef<SyncedItem?> {
|
||||
/// The parameter `item` of this provider.
|
||||
ItemBaseModel? get item;
|
||||
}
|
||||
|
||||
class _SyncChildrenProviderElement
|
||||
extends AutoDisposeNotifierProviderElement<SyncChildren, List<SyncedItem>>
|
||||
with SyncChildrenRef {
|
||||
_SyncChildrenProviderElement(super.provider);
|
||||
class _SyncedItemProviderElement
|
||||
extends AutoDisposeStreamProviderElement<SyncedItem?> with SyncedItemRef {
|
||||
_SyncedItemProviderElement(super.provider);
|
||||
|
||||
@override
|
||||
SyncedItem get root => (origin as SyncChildrenProvider).root;
|
||||
ItemBaseModel? get item => (origin as SyncedItemProvider).item;
|
||||
}
|
||||
|
||||
String _$syncedChildrenHash() => r'2b6ce1611750785060df6317ce0ea25e2dc0aeb4';
|
||||
|
||||
abstract class _$SyncedChildren
|
||||
extends BuildlessAutoDisposeAsyncNotifier<List<SyncedItem>> {
|
||||
late final SyncedItem item;
|
||||
|
||||
FutureOr<List<SyncedItem>> build(
|
||||
SyncedItem item,
|
||||
);
|
||||
}
|
||||
|
||||
/// See also [SyncedChildren].
|
||||
@ProviderFor(SyncedChildren)
|
||||
const syncedChildrenProvider = SyncedChildrenFamily();
|
||||
|
||||
/// See also [SyncedChildren].
|
||||
class SyncedChildrenFamily extends Family<AsyncValue<List<SyncedItem>>> {
|
||||
/// See also [SyncedChildren].
|
||||
const SyncedChildrenFamily();
|
||||
|
||||
/// See also [SyncedChildren].
|
||||
SyncedChildrenProvider call(
|
||||
SyncedItem item,
|
||||
) {
|
||||
return SyncedChildrenProvider(
|
||||
item,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
SyncedChildrenProvider getProviderOverride(
|
||||
covariant SyncedChildrenProvider provider,
|
||||
) {
|
||||
return call(
|
||||
provider.item,
|
||||
);
|
||||
}
|
||||
|
||||
static const Iterable<ProviderOrFamily>? _dependencies = null;
|
||||
|
||||
@override
|
||||
Iterable<ProviderOrFamily>? get dependencies => _dependencies;
|
||||
|
||||
static const Iterable<ProviderOrFamily>? _allTransitiveDependencies = null;
|
||||
|
||||
@override
|
||||
Iterable<ProviderOrFamily>? get allTransitiveDependencies =>
|
||||
_allTransitiveDependencies;
|
||||
|
||||
@override
|
||||
String? get name => r'syncedChildrenProvider';
|
||||
}
|
||||
|
||||
/// See also [SyncedChildren].
|
||||
class SyncedChildrenProvider extends AutoDisposeAsyncNotifierProviderImpl<
|
||||
SyncedChildren, List<SyncedItem>> {
|
||||
/// See also [SyncedChildren].
|
||||
SyncedChildrenProvider(
|
||||
SyncedItem item,
|
||||
) : this._internal(
|
||||
() => SyncedChildren()..item = item,
|
||||
from: syncedChildrenProvider,
|
||||
name: r'syncedChildrenProvider',
|
||||
debugGetCreateSourceHash:
|
||||
const bool.fromEnvironment('dart.vm.product')
|
||||
? null
|
||||
: _$syncedChildrenHash,
|
||||
dependencies: SyncedChildrenFamily._dependencies,
|
||||
allTransitiveDependencies:
|
||||
SyncedChildrenFamily._allTransitiveDependencies,
|
||||
item: item,
|
||||
);
|
||||
|
||||
SyncedChildrenProvider._internal(
|
||||
super._createNotifier, {
|
||||
required super.name,
|
||||
required super.dependencies,
|
||||
required super.allTransitiveDependencies,
|
||||
required super.debugGetCreateSourceHash,
|
||||
required super.from,
|
||||
required this.item,
|
||||
}) : super.internal();
|
||||
|
||||
final SyncedItem item;
|
||||
|
||||
@override
|
||||
FutureOr<List<SyncedItem>> runNotifierBuild(
|
||||
covariant SyncedChildren notifier,
|
||||
) {
|
||||
return notifier.build(
|
||||
item,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Override overrideWith(SyncedChildren Function() create) {
|
||||
return ProviderOverride(
|
||||
origin: this,
|
||||
override: SyncedChildrenProvider._internal(
|
||||
() => create()..item = item,
|
||||
from: from,
|
||||
name: null,
|
||||
dependencies: null,
|
||||
allTransitiveDependencies: null,
|
||||
debugGetCreateSourceHash: null,
|
||||
item: item,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
AutoDisposeAsyncNotifierProviderElement<SyncedChildren, List<SyncedItem>>
|
||||
createElement() {
|
||||
return _SyncedChildrenProviderElement(this);
|
||||
}
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
return other is SyncedChildrenProvider && other.item == item;
|
||||
}
|
||||
|
||||
@override
|
||||
int get hashCode {
|
||||
var hash = _SystemHash.combine(0, runtimeType.hashCode);
|
||||
hash = _SystemHash.combine(hash, item.hashCode);
|
||||
|
||||
return _SystemHash.finish(hash);
|
||||
}
|
||||
}
|
||||
|
||||
@Deprecated('Will be removed in 3.0. Use Ref instead')
|
||||
// ignore: unused_element
|
||||
mixin SyncedChildrenRef
|
||||
on AutoDisposeAsyncNotifierProviderRef<List<SyncedItem>> {
|
||||
/// The parameter `item` of this provider.
|
||||
SyncedItem get item;
|
||||
}
|
||||
|
||||
class _SyncedChildrenProviderElement
|
||||
extends AutoDisposeAsyncNotifierProviderElement<SyncedChildren,
|
||||
List<SyncedItem>> with SyncedChildrenRef {
|
||||
_SyncedChildrenProviderElement(super.provider);
|
||||
|
||||
@override
|
||||
SyncedItem get item => (origin as SyncedChildrenProvider).item;
|
||||
}
|
||||
|
||||
String _$syncedNestedChildrenHash() =>
|
||||
r'ea8dd0e694efa6d6ec0c73d699b5fb3e933f9322';
|
||||
|
||||
abstract class _$SyncedNestedChildren
|
||||
extends BuildlessAutoDisposeAsyncNotifier<List<SyncedItem>> {
|
||||
late final SyncedItem item;
|
||||
|
||||
FutureOr<List<SyncedItem>> build(
|
||||
SyncedItem item,
|
||||
);
|
||||
}
|
||||
|
||||
/// See also [SyncedNestedChildren].
|
||||
@ProviderFor(SyncedNestedChildren)
|
||||
const syncedNestedChildrenProvider = SyncedNestedChildrenFamily();
|
||||
|
||||
/// See also [SyncedNestedChildren].
|
||||
class SyncedNestedChildrenFamily extends Family<AsyncValue<List<SyncedItem>>> {
|
||||
/// See also [SyncedNestedChildren].
|
||||
const SyncedNestedChildrenFamily();
|
||||
|
||||
/// See also [SyncedNestedChildren].
|
||||
SyncedNestedChildrenProvider call(
|
||||
SyncedItem item,
|
||||
) {
|
||||
return SyncedNestedChildrenProvider(
|
||||
item,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
SyncedNestedChildrenProvider getProviderOverride(
|
||||
covariant SyncedNestedChildrenProvider provider,
|
||||
) {
|
||||
return call(
|
||||
provider.item,
|
||||
);
|
||||
}
|
||||
|
||||
static const Iterable<ProviderOrFamily>? _dependencies = null;
|
||||
|
||||
@override
|
||||
Iterable<ProviderOrFamily>? get dependencies => _dependencies;
|
||||
|
||||
static const Iterable<ProviderOrFamily>? _allTransitiveDependencies = null;
|
||||
|
||||
@override
|
||||
Iterable<ProviderOrFamily>? get allTransitiveDependencies =>
|
||||
_allTransitiveDependencies;
|
||||
|
||||
@override
|
||||
String? get name => r'syncedNestedChildrenProvider';
|
||||
}
|
||||
|
||||
/// See also [SyncedNestedChildren].
|
||||
class SyncedNestedChildrenProvider extends AutoDisposeAsyncNotifierProviderImpl<
|
||||
SyncedNestedChildren, List<SyncedItem>> {
|
||||
/// See also [SyncedNestedChildren].
|
||||
SyncedNestedChildrenProvider(
|
||||
SyncedItem item,
|
||||
) : this._internal(
|
||||
() => SyncedNestedChildren()..item = item,
|
||||
from: syncedNestedChildrenProvider,
|
||||
name: r'syncedNestedChildrenProvider',
|
||||
debugGetCreateSourceHash:
|
||||
const bool.fromEnvironment('dart.vm.product')
|
||||
? null
|
||||
: _$syncedNestedChildrenHash,
|
||||
dependencies: SyncedNestedChildrenFamily._dependencies,
|
||||
allTransitiveDependencies:
|
||||
SyncedNestedChildrenFamily._allTransitiveDependencies,
|
||||
item: item,
|
||||
);
|
||||
|
||||
SyncedNestedChildrenProvider._internal(
|
||||
super._createNotifier, {
|
||||
required super.name,
|
||||
required super.dependencies,
|
||||
required super.allTransitiveDependencies,
|
||||
required super.debugGetCreateSourceHash,
|
||||
required super.from,
|
||||
required this.item,
|
||||
}) : super.internal();
|
||||
|
||||
final SyncedItem item;
|
||||
|
||||
@override
|
||||
FutureOr<List<SyncedItem>> runNotifierBuild(
|
||||
covariant SyncedNestedChildren notifier,
|
||||
) {
|
||||
return notifier.build(
|
||||
item,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Override overrideWith(SyncedNestedChildren Function() create) {
|
||||
return ProviderOverride(
|
||||
origin: this,
|
||||
override: SyncedNestedChildrenProvider._internal(
|
||||
() => create()..item = item,
|
||||
from: from,
|
||||
name: null,
|
||||
dependencies: null,
|
||||
allTransitiveDependencies: null,
|
||||
debugGetCreateSourceHash: null,
|
||||
item: item,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
AutoDisposeAsyncNotifierProviderElement<SyncedNestedChildren,
|
||||
List<SyncedItem>> createElement() {
|
||||
return _SyncedNestedChildrenProviderElement(this);
|
||||
}
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
return other is SyncedNestedChildrenProvider && other.item == item;
|
||||
}
|
||||
|
||||
@override
|
||||
int get hashCode {
|
||||
var hash = _SystemHash.combine(0, runtimeType.hashCode);
|
||||
hash = _SystemHash.combine(hash, item.hashCode);
|
||||
|
||||
return _SystemHash.finish(hash);
|
||||
}
|
||||
}
|
||||
|
||||
@Deprecated('Will be removed in 3.0. Use Ref instead')
|
||||
// ignore: unused_element
|
||||
mixin SyncedNestedChildrenRef
|
||||
on AutoDisposeAsyncNotifierProviderRef<List<SyncedItem>> {
|
||||
/// The parameter `item` of this provider.
|
||||
SyncedItem get item;
|
||||
}
|
||||
|
||||
class _SyncedNestedChildrenProviderElement
|
||||
extends AutoDisposeAsyncNotifierProviderElement<SyncedNestedChildren,
|
||||
List<SyncedItem>> with SyncedNestedChildrenRef {
|
||||
_SyncedNestedChildrenProviderElement(super.provider);
|
||||
|
||||
@override
|
||||
SyncedItem get item => (origin as SyncedNestedChildrenProvider).item;
|
||||
}
|
||||
|
||||
String _$syncDownloadStatusHash() =>
|
||||
r'1036352200e1138b4ef70e524c0baf13bb9cd452';
|
||||
r'6ee039e094f1e007ebaeb20ae63430be829cdeb7';
|
||||
|
||||
abstract class _$SyncDownloadStatus
|
||||
extends BuildlessAutoDisposeNotifier<DownloadStream?> {
|
||||
|
|
@ -344,176 +623,7 @@ class _SyncDownloadStatusProviderElement
|
|||
(origin as SyncDownloadStatusProvider).children;
|
||||
}
|
||||
|
||||
String _$syncStatusesHash() => r'64a3499fc7b7bbdbd6594b1eec76cf42a119a041';
|
||||
|
||||
abstract class _$SyncStatuses
|
||||
extends BuildlessAutoDisposeAsyncNotifier<SyncStatus> {
|
||||
late final SyncedItem arg;
|
||||
late final List<SyncedItem>? children;
|
||||
|
||||
FutureOr<SyncStatus> build(
|
||||
SyncedItem arg,
|
||||
List<SyncedItem>? children,
|
||||
);
|
||||
}
|
||||
|
||||
/// See also [SyncStatuses].
|
||||
@ProviderFor(SyncStatuses)
|
||||
const syncStatusesProvider = SyncStatusesFamily();
|
||||
|
||||
/// See also [SyncStatuses].
|
||||
class SyncStatusesFamily extends Family<AsyncValue<SyncStatus>> {
|
||||
/// See also [SyncStatuses].
|
||||
const SyncStatusesFamily();
|
||||
|
||||
/// See also [SyncStatuses].
|
||||
SyncStatusesProvider call(
|
||||
SyncedItem arg,
|
||||
List<SyncedItem>? children,
|
||||
) {
|
||||
return SyncStatusesProvider(
|
||||
arg,
|
||||
children,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
SyncStatusesProvider getProviderOverride(
|
||||
covariant SyncStatusesProvider provider,
|
||||
) {
|
||||
return call(
|
||||
provider.arg,
|
||||
provider.children,
|
||||
);
|
||||
}
|
||||
|
||||
static const Iterable<ProviderOrFamily>? _dependencies = null;
|
||||
|
||||
@override
|
||||
Iterable<ProviderOrFamily>? get dependencies => _dependencies;
|
||||
|
||||
static const Iterable<ProviderOrFamily>? _allTransitiveDependencies = null;
|
||||
|
||||
@override
|
||||
Iterable<ProviderOrFamily>? get allTransitiveDependencies =>
|
||||
_allTransitiveDependencies;
|
||||
|
||||
@override
|
||||
String? get name => r'syncStatusesProvider';
|
||||
}
|
||||
|
||||
/// See also [SyncStatuses].
|
||||
class SyncStatusesProvider
|
||||
extends AutoDisposeAsyncNotifierProviderImpl<SyncStatuses, SyncStatus> {
|
||||
/// See also [SyncStatuses].
|
||||
SyncStatusesProvider(
|
||||
SyncedItem arg,
|
||||
List<SyncedItem>? children,
|
||||
) : this._internal(
|
||||
() => SyncStatuses()
|
||||
..arg = arg
|
||||
..children = children,
|
||||
from: syncStatusesProvider,
|
||||
name: r'syncStatusesProvider',
|
||||
debugGetCreateSourceHash:
|
||||
const bool.fromEnvironment('dart.vm.product')
|
||||
? null
|
||||
: _$syncStatusesHash,
|
||||
dependencies: SyncStatusesFamily._dependencies,
|
||||
allTransitiveDependencies:
|
||||
SyncStatusesFamily._allTransitiveDependencies,
|
||||
arg: arg,
|
||||
children: children,
|
||||
);
|
||||
|
||||
SyncStatusesProvider._internal(
|
||||
super._createNotifier, {
|
||||
required super.name,
|
||||
required super.dependencies,
|
||||
required super.allTransitiveDependencies,
|
||||
required super.debugGetCreateSourceHash,
|
||||
required super.from,
|
||||
required this.arg,
|
||||
required this.children,
|
||||
}) : super.internal();
|
||||
|
||||
final SyncedItem arg;
|
||||
final List<SyncedItem>? children;
|
||||
|
||||
@override
|
||||
FutureOr<SyncStatus> runNotifierBuild(
|
||||
covariant SyncStatuses notifier,
|
||||
) {
|
||||
return notifier.build(
|
||||
arg,
|
||||
children,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Override overrideWith(SyncStatuses Function() create) {
|
||||
return ProviderOverride(
|
||||
origin: this,
|
||||
override: SyncStatusesProvider._internal(
|
||||
() => create()
|
||||
..arg = arg
|
||||
..children = children,
|
||||
from: from,
|
||||
name: null,
|
||||
dependencies: null,
|
||||
allTransitiveDependencies: null,
|
||||
debugGetCreateSourceHash: null,
|
||||
arg: arg,
|
||||
children: children,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
AutoDisposeAsyncNotifierProviderElement<SyncStatuses, SyncStatus>
|
||||
createElement() {
|
||||
return _SyncStatusesProviderElement(this);
|
||||
}
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
return other is SyncStatusesProvider &&
|
||||
other.arg == arg &&
|
||||
other.children == children;
|
||||
}
|
||||
|
||||
@override
|
||||
int get hashCode {
|
||||
var hash = _SystemHash.combine(0, runtimeType.hashCode);
|
||||
hash = _SystemHash.combine(hash, arg.hashCode);
|
||||
hash = _SystemHash.combine(hash, children.hashCode);
|
||||
|
||||
return _SystemHash.finish(hash);
|
||||
}
|
||||
}
|
||||
|
||||
@Deprecated('Will be removed in 3.0. Use Ref instead')
|
||||
// ignore: unused_element
|
||||
mixin SyncStatusesRef on AutoDisposeAsyncNotifierProviderRef<SyncStatus> {
|
||||
/// The parameter `arg` of this provider.
|
||||
SyncedItem get arg;
|
||||
|
||||
/// The parameter `children` of this provider.
|
||||
List<SyncedItem>? get children;
|
||||
}
|
||||
|
||||
class _SyncStatusesProviderElement
|
||||
extends AutoDisposeAsyncNotifierProviderElement<SyncStatuses, SyncStatus>
|
||||
with SyncStatusesRef {
|
||||
_SyncStatusesProviderElement(super.provider);
|
||||
|
||||
@override
|
||||
SyncedItem get arg => (origin as SyncStatusesProvider).arg;
|
||||
@override
|
||||
List<SyncedItem>? get children => (origin as SyncStatusesProvider).children;
|
||||
}
|
||||
|
||||
String _$syncSizeHash() => r'81797ecc4a6f600691b6f1fe0c16bae0228ec920';
|
||||
String _$syncSizeHash() => r'eeb6ab8dc1fdf5696c06e53f04a0e54ad68c6748';
|
||||
|
||||
abstract class _$SyncSize extends BuildlessAutoDisposeNotifier<int?> {
|
||||
late final SyncedItem arg;
|
||||
|
|
|
|||
|
|
@ -8,10 +8,10 @@ import 'package:flutter/material.dart';
|
|||
|
||||
import 'package:background_downloader/background_downloader.dart';
|
||||
import 'package:collection/collection.dart';
|
||||
import 'package:drift_db_viewer/drift_db_viewer.dart';
|
||||
import 'package:file_picker/file_picker.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:http/http.dart' as http;
|
||||
import 'package:isar/isar.dart';
|
||||
import 'package:path/path.dart' as path;
|
||||
import 'package:path_provider/path_provider.dart';
|
||||
|
||||
|
|
@ -20,13 +20,14 @@ import 'package:fladder/models/item_base_model.dart';
|
|||
import 'package:fladder/models/items/chapters_model.dart';
|
||||
import 'package:fladder/models/items/episode_model.dart';
|
||||
import 'package:fladder/models/items/images_models.dart';
|
||||
import 'package:fladder/models/items/item_shared_models.dart';
|
||||
import 'package:fladder/models/items/media_streams_model.dart';
|
||||
import 'package:fladder/models/items/movie_model.dart';
|
||||
import 'package:fladder/models/items/season_model.dart';
|
||||
import 'package:fladder/models/items/series_model.dart';
|
||||
import 'package:fladder/models/items/trick_play_model.dart';
|
||||
import 'package:fladder/models/syncing/database_item.dart';
|
||||
import 'package:fladder/models/syncing/download_stream.dart';
|
||||
import 'package:fladder/models/syncing/i_synced_item.dart';
|
||||
import 'package:fladder/models/syncing/sync_item.dart';
|
||||
import 'package:fladder/models/syncing/sync_settings_model.dart';
|
||||
import 'package:fladder/models/video_stream_model.dart';
|
||||
|
|
@ -38,16 +39,27 @@ import 'package:fladder/providers/sync/background_download_provider.dart';
|
|||
import 'package:fladder/providers/user_provider.dart';
|
||||
import 'package:fladder/screens/shared/fladder_snackbar.dart';
|
||||
import 'package:fladder/util/localization_helper.dart';
|
||||
import 'package:fladder/util/migration/isar_drift_migration.dart';
|
||||
|
||||
final syncProvider = StateNotifierProvider<SyncNotifier, SyncSettingsModel>((ref) => throw UnimplementedError());
|
||||
|
||||
final downloadTasksProvider = StateProvider.family<DownloadStream, String>((ref, id) => DownloadStream.empty());
|
||||
|
||||
class SyncNotifier extends StateNotifier<SyncSettingsModel> {
|
||||
SyncNotifier(this.ref, this.isar, this.mobileDirectory) : super(SyncSettingsModel()) {
|
||||
SyncNotifier(this.ref, this.mobileDirectory) : super(SyncSettingsModel()) {
|
||||
_init();
|
||||
}
|
||||
|
||||
final Ref ref;
|
||||
late final db = AppDatabase(ref);
|
||||
final Directory mobileDirectory;
|
||||
final String subPath = "Synced";
|
||||
|
||||
void migrateFromIsar() async {
|
||||
await isarMigration(ref, db, mainDirectory.path);
|
||||
_initializeQueryStream();
|
||||
}
|
||||
|
||||
void _init() {
|
||||
cleanupTemporaryFiles();
|
||||
ref.listen(
|
||||
|
|
@ -55,35 +67,29 @@ class SyncNotifier extends StateNotifier<SyncSettingsModel> {
|
|||
(previous, next) {
|
||||
if (previous?.id != next?.id) {
|
||||
if (next?.id != null) {
|
||||
_initializeQueryStream(next?.id ?? "");
|
||||
_initializeQueryStream();
|
||||
}
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
final userId = ref.read(userProvider)?.id;
|
||||
if (userId != null) {
|
||||
_initializeQueryStream(userId);
|
||||
}
|
||||
_initializeQueryStream();
|
||||
|
||||
migrateFromIsar();
|
||||
}
|
||||
|
||||
void _initializeQueryStream(String userId) {
|
||||
void _initializeQueryStream() async {
|
||||
final userId = ref.read(userProvider)?.id;
|
||||
if (userId == null) return;
|
||||
_subscription?.cancel();
|
||||
|
||||
final queryStream = getParentSyncItems
|
||||
?.userIdEqualTo(userId)
|
||||
.watch()
|
||||
.asyncMap((event) => event.map((e) => SyncedItem.fromIsar(e, syncPath ?? "")).toList());
|
||||
final queryStream = db.getParentItems.watch();
|
||||
|
||||
final initItems = getParentSyncItems
|
||||
?.userIdEqualTo(userId)
|
||||
.findAll()
|
||||
.mapIndexed((index, element) => SyncedItem.fromIsar(element, syncPath ?? ""))
|
||||
.toList();
|
||||
final initItems = await db.getParentItems.get();
|
||||
|
||||
state = state.copyWith(items: initItems ?? []);
|
||||
state = state.copyWith(items: initItems);
|
||||
|
||||
_subscription = queryStream?.listen((items) {
|
||||
_subscription = queryStream.listen((items) {
|
||||
state = state.copyWith(items: items);
|
||||
});
|
||||
}
|
||||
|
|
@ -117,15 +123,8 @@ class SyncNotifier extends StateNotifier<SyncSettingsModel> {
|
|||
}
|
||||
}
|
||||
|
||||
final Ref ref;
|
||||
final Isar? isar;
|
||||
final Directory mobileDirectory;
|
||||
final String subPath = "Synced";
|
||||
|
||||
StreamSubscription<List<SyncedItem>>? _subscription;
|
||||
|
||||
IsarCollection<String, ISyncedItem>? get syncedItems => isar?.iSyncedItems;
|
||||
|
||||
late final JellyService api = ref.read(jellyApiProvider);
|
||||
|
||||
String? get _savePath => !kIsWeb && (Platform.isWindows || Platform.isLinux || Platform.isMacOS)
|
||||
|
|
@ -151,9 +150,6 @@ class SyncNotifier extends StateNotifier<SyncSettingsModel> {
|
|||
|
||||
String? get syncPath => saveDirectory?.path;
|
||||
|
||||
QueryBuilder<ISyncedItem, ISyncedItem, QAfterFilterCondition>? get getParentSyncItems =>
|
||||
syncedItems?.where().parentIdIsNull();
|
||||
|
||||
Future<int> get directorySize async {
|
||||
if (saveDirectory == null) return 0;
|
||||
var files = await saveDirectory!.list(recursive: true).toList();
|
||||
|
|
@ -168,59 +164,62 @@ class SyncNotifier extends StateNotifier<SyncSettingsModel> {
|
|||
}
|
||||
|
||||
Future<void> refresh() async {
|
||||
state = state.copyWith(
|
||||
items: (await getParentSyncItems?.userIdEqualTo(ref.read(userProvider)?.id).findAllAsync())
|
||||
?.map((e) => SyncedItem.fromIsar(e, syncPath ?? ""))
|
||||
.toList() ??
|
||||
[]);
|
||||
state = state.copyWith(items: (await db.getParentItems.get()));
|
||||
}
|
||||
|
||||
List<SyncedItem> getNestedChildren(SyncedItem item) {
|
||||
final allChildren = <SyncedItem>[];
|
||||
List<SyncedItem> toProcess = [item];
|
||||
while (toProcess.isNotEmpty) {
|
||||
final currentLevel = toProcess.map(
|
||||
(parent) {
|
||||
final children = syncedItems?.where().parentIdEqualTo(parent.id).sortBySortName().findAll();
|
||||
return children?.map((e) => SyncedItem.fromIsar(e, ref.read(syncProvider.notifier).syncPath ?? "")) ??
|
||||
<SyncedItem>[];
|
||||
},
|
||||
);
|
||||
allChildren.addAll(currentLevel.expand((list) => list));
|
||||
toProcess = currentLevel.expand((list) => list).toList();
|
||||
}
|
||||
return allChildren;
|
||||
}
|
||||
Future<List<SyncedItem>> getNestedChildren(SyncedItem item) async => db.getNestedChildren(item);
|
||||
|
||||
List<SyncedItem> getChildren(SyncedItem item) {
|
||||
return (syncedItems?.where().parentIdEqualTo(item.id).sortBySortName().findAll())
|
||||
?.map(
|
||||
(e) => SyncedItem.fromIsar(e, syncPath ?? ""),
|
||||
)
|
||||
.toList() ??
|
||||
[];
|
||||
}
|
||||
Future<List<SyncedItem>> getChildren(SyncedItem root) async => await db.getChildren(root.id).get();
|
||||
|
||||
SyncedItem? getSyncedItem(ItemBaseModel? item) {
|
||||
Future<SyncedItem?> getSyncedItem(ItemBaseModel? item) async {
|
||||
final id = item?.id;
|
||||
if (id == null) return null;
|
||||
final newItem = syncedItems?.get(id);
|
||||
if (newItem == null) return null;
|
||||
return SyncedItem.fromIsar(newItem, syncPath ?? "");
|
||||
return await db.getItem(id).getSingleOrNull();
|
||||
}
|
||||
|
||||
SyncedItem? getParentItem(String id) {
|
||||
ISyncedItem? newItem = syncedItems?.get(id);
|
||||
while (newItem?.parentId != null) {
|
||||
newItem = syncedItems?.get(newItem!.parentId!);
|
||||
Future<SyncedItem?> getParentItem(String id) async => await db.getParent(id).getSingleOrNull();
|
||||
|
||||
Future<SyncedItem> refreshSyncItem(SyncedItem item) async {
|
||||
List<SyncedItem> itemsToSync = await getNestedChildren(item);
|
||||
|
||||
itemsToSync = [item, ...itemsToSync];
|
||||
|
||||
SyncedItem parentItem = item;
|
||||
|
||||
List<SyncedItem> newItems = [];
|
||||
|
||||
for (var i = 0; i < itemsToSync.length; i++) {
|
||||
final itemToSync = itemsToSync[i];
|
||||
final itemResponse = await api.usersUserIdItemsItemIdGetBaseItem(
|
||||
itemId: itemToSync.id,
|
||||
);
|
||||
|
||||
final itemModel = ItemBaseModel.fromBaseDto(itemResponse.bodyOrThrow, ref);
|
||||
|
||||
final syncedParent = await db.getItem(itemToSync.parentId ?? "").getSingleOrNull();
|
||||
|
||||
SyncedItem newSyncedItem = await _syncItemData(syncedParent, itemModel, itemResponse.bodyOrThrow);
|
||||
|
||||
final updatedItem = itemToSync.copyWith(
|
||||
itemModel: newSyncedItem.createItemModel(ref),
|
||||
sortName: newSyncedItem.sortName,
|
||||
syncing: false,
|
||||
fImages: newSyncedItem.fImages,
|
||||
fTrickPlayModel: newSyncedItem.fTrickPlayModel,
|
||||
subtitles: newSyncedItem.subtitles,
|
||||
userData: UserData.determineLastUserData([item.userData, newSyncedItem.userData]),
|
||||
);
|
||||
|
||||
newItems.add(updatedItem);
|
||||
|
||||
if (itemToSync.id == parentItem.id) {
|
||||
parentItem = updatedItem;
|
||||
}
|
||||
}
|
||||
if (newItem == null) return null;
|
||||
return SyncedItem.fromIsar(newItem, syncPath ?? "");
|
||||
}
|
||||
|
||||
ItemBaseModel? getItem(SyncedItem? syncedItem) {
|
||||
if (syncedItem == null) return null;
|
||||
return syncedItem.createItemModel(ref);
|
||||
await db.insertMultipleEntries(newItems);
|
||||
|
||||
return parentItem;
|
||||
}
|
||||
|
||||
Future<void> addSyncItem(BuildContext? context, ItemBaseModel item) async {
|
||||
|
|
@ -229,7 +228,7 @@ class SyncNotifier extends StateNotifier<SyncSettingsModel> {
|
|||
if (saveDirectory == null) {
|
||||
String? selectedDirectory =
|
||||
await FilePicker.platform.getDirectoryPath(dialogTitle: context.localized.syncSelectDownloadsFolder);
|
||||
if (selectedDirectory?.isEmpty == true) {
|
||||
if (selectedDirectory?.isEmpty == true && context.mounted) {
|
||||
fladderSnackbar(context, title: context.localized.syncNoFolderSetup);
|
||||
return;
|
||||
}
|
||||
|
|
@ -244,18 +243,24 @@ class SyncNotifier extends StateNotifier<SyncSettingsModel> {
|
|||
MovieModel movie => await syncMovie(movie),
|
||||
_ => null
|
||||
};
|
||||
fladderSnackbar(context,
|
||||
title: newSync != null
|
||||
? context.localized.startedSyncingItem(item.detailedName(context) ?? "Unknown")
|
||||
: context.localized.unableToSyncItem(item.detailedName(context) ?? "Unknown"));
|
||||
if (context.mounted) {
|
||||
fladderSnackbar(context,
|
||||
title: newSync != null
|
||||
? context.localized.startedSyncingItem(item.detailedName(context) ?? "Unknown")
|
||||
: context.localized.unableToSyncItem(item.detailedName(context) ?? "Unknown"));
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
void viewDatabase(BuildContext context) =>
|
||||
Navigator.of(context, rootNavigator: true).push(MaterialPageRoute(builder: (context) => DriftDbViewer(db)));
|
||||
|
||||
Future<bool> removeSync(BuildContext context, SyncedItem? item) async {
|
||||
try {
|
||||
if (item == null) return false;
|
||||
|
||||
final nestedChildren = getNestedChildren(item);
|
||||
final nestedChildren = await getNestedChildren(item);
|
||||
|
||||
state = state.copyWith(
|
||||
items: state.items
|
||||
|
|
@ -268,13 +273,7 @@ class SyncNotifier extends StateNotifier<SyncSettingsModel> {
|
|||
await ref.read(backgroundDownloaderProvider).cancelTaskWithId(item.taskId!);
|
||||
}
|
||||
|
||||
final deleteFromDatabase = isar?.write((isar) => syncedItems?.deleteAll([...nestedChildren, item]
|
||||
.map(
|
||||
(e) => e.id,
|
||||
)
|
||||
.toList()));
|
||||
|
||||
if (deleteFromDatabase == 0) return false;
|
||||
await db.deleteAllItems([...nestedChildren, item]);
|
||||
|
||||
for (var i = 0; i < nestedChildren.length; i++) {
|
||||
final element = nestedChildren[i];
|
||||
|
|
@ -396,12 +395,7 @@ class SyncNotifier extends StateNotifier<SyncSettingsModel> {
|
|||
return data?.copyWith(path: fileName);
|
||||
}
|
||||
|
||||
void updateItemSync(SyncedItem syncedItem) =>
|
||||
isar?.write((isar) => syncedItems?.put(ISyncedItem.fromSynced(syncedItem, syncPath ?? "")));
|
||||
|
||||
Future<void> updateItem(SyncedItem syncedItem) async {
|
||||
isar?.write((isar) => syncedItems?.put(ISyncedItem.fromSynced(syncedItem, syncPath ?? "")));
|
||||
}
|
||||
Future<void> updateItem(SyncedItem syncedItem) async => db.insertItem(syncedItem);
|
||||
|
||||
Future<SyncedItem> deleteFullSyncFiles(SyncedItem syncedItem, DownloadTask? task) async {
|
||||
await syncedItem.deleteDatFiles(ref);
|
||||
|
|
@ -512,7 +506,7 @@ class SyncNotifier extends StateNotifier<SyncSettingsModel> {
|
|||
|
||||
Future<void> clear() async {
|
||||
await mainDirectory.delete(recursive: true);
|
||||
isar?.write((isar) => syncedItems?.clear());
|
||||
await db.clearDatabase();
|
||||
state = state.copyWith(items: []);
|
||||
}
|
||||
|
||||
|
|
@ -526,10 +520,24 @@ extension SyncNotifierHelpers on SyncNotifier {
|
|||
Future<SyncedItem> createSyncItem(BaseItemDto response, {SyncedItem? parent}) async {
|
||||
final ItemBaseModel item = ItemBaseModel.fromBaseDto(response, ref);
|
||||
|
||||
final existingSyncedItem = getSyncedItem(item);
|
||||
final existingSyncedItem = await getSyncedItem(item);
|
||||
|
||||
if (existingSyncedItem != null) return existingSyncedItem;
|
||||
|
||||
SyncedItem syncItem = await _syncItemData(parent, item, response);
|
||||
|
||||
if (parent == null) {
|
||||
await db.insertItem(syncItem);
|
||||
}
|
||||
|
||||
return syncItem.copyWith(
|
||||
fileSize: response.mediaSources?.firstOrNull?.size ?? 0,
|
||||
syncing: false,
|
||||
videoFileName: response.path?.split('/').lastOrNull ?? "",
|
||||
);
|
||||
}
|
||||
|
||||
Future<SyncedItem> _syncItemData(SyncedItem? parent, ItemBaseModel item, BaseItemDto response) async {
|
||||
final Directory? parentDirectory = parent?.directory;
|
||||
|
||||
final directory = Directory(path.joinAll([(parentDirectory ?? saveDirectory)?.path ?? "", item.id]));
|
||||
|
|
@ -550,16 +558,7 @@ extension SyncNotifierHelpers on SyncNotifier {
|
|||
path: directory.path,
|
||||
userData: item.userData,
|
||||
);
|
||||
|
||||
if (parent == null) {
|
||||
isar?.write((isar) => syncedItems?.put(ISyncedItem.fromSynced(syncItem, syncPath)));
|
||||
}
|
||||
|
||||
return syncItem.copyWith(
|
||||
fileSize: response.mediaSources?.firstOrNull?.size ?? 0,
|
||||
syncing: false,
|
||||
videoFileName: response.path?.split('/').lastOrNull ?? "",
|
||||
);
|
||||
return syncItem;
|
||||
}
|
||||
|
||||
Future<SyncedItem?> syncMovie(ItemBaseModel item, {bool skipDownload = false}) async {
|
||||
|
|
@ -574,9 +573,9 @@ extension SyncNotifierHelpers on SyncNotifier {
|
|||
|
||||
if (!syncItem.directory.existsSync()) return null;
|
||||
|
||||
await syncFile(syncItem, skipDownload);
|
||||
await db.insertItem(syncItem);
|
||||
|
||||
isar?.write((isar) => syncedItems?.put(ISyncedItem.fromSynced(syncItem, syncPath)));
|
||||
await syncFile(syncItem, skipDownload);
|
||||
|
||||
return syncItem;
|
||||
}
|
||||
|
|
@ -595,6 +594,7 @@ extension SyncNotifierHelpers on SyncNotifier {
|
|||
if (!seriesItem.directory.existsSync()) return null;
|
||||
|
||||
final seasonsResponse = await api.showsSeriesIdSeasonsGet(
|
||||
seriesId: item.id,
|
||||
isMissing: false,
|
||||
enableUserData: true,
|
||||
fields: [
|
||||
|
|
@ -615,7 +615,6 @@ extension SyncNotifierHelpers on SyncNotifier {
|
|||
ItemFields.chapters,
|
||||
ItemFields.trickplay,
|
||||
],
|
||||
seriesId: item.id,
|
||||
);
|
||||
|
||||
final seasons = seasonsResponse.body?.items ?? [];
|
||||
|
|
@ -660,23 +659,17 @@ extension SyncNotifierHelpers on SyncNotifier {
|
|||
|
||||
for (final (ep, newEpisode) in episodeResults) {
|
||||
newItems.add(newEpisode);
|
||||
if (episode?.id == ep.id || newSeason.id == season?.id) {
|
||||
if (episode?.id == ep.id || newSeason.id == season?.id && !await newEpisode.videoFile.exists()) {
|
||||
itemsToDownload.add(newEpisode);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
isar?.write(
|
||||
(isar) => syncedItems?.putAll(newItems
|
||||
.map(
|
||||
(e) => ISyncedItem.fromSynced(e, syncPath ?? ""),
|
||||
)
|
||||
.toList()),
|
||||
);
|
||||
await db.insertMultipleEntries(newItems);
|
||||
|
||||
for (var i = 0; i < itemsToDownload.length; i++) {
|
||||
final item = itemsToDownload[i];
|
||||
await syncFile(item, false);
|
||||
syncFile(item, false);
|
||||
}
|
||||
|
||||
return seriesItem;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue