mirror of
https://github.com/gabehf/Fladder.git
synced 2026-03-09 07:28:14 -07:00
feature: Improved sync capability
This commit is contained in:
parent
f3e920ac79
commit
c5c7f71b84
31 changed files with 500 additions and 344 deletions
|
|
@ -32,20 +32,29 @@ class SeriesDetailViewNotifier extends StateNotifier<SeriesModel?> {
|
|||
final response = await api.usersUserIdItemsItemIdGet(itemId: seriesModel.id);
|
||||
if (response.body == null) return null;
|
||||
newState = response.bodyOrThrow as SeriesModel;
|
||||
final seasons = await api.showsSeriesIdSeasonsGet(seriesId: seriesModel.id);
|
||||
newState = newState.copyWith(seasons: SeasonModel.seasonsFromDto(seasons.body?.items, ref));
|
||||
|
||||
final episodes = await api.showsSeriesIdEpisodesGet(seriesId: seriesModel.id, fields: [
|
||||
ItemFields.mediastreams,
|
||||
ItemFields.mediasources,
|
||||
ItemFields.overview,
|
||||
ItemFields.candownload,
|
||||
]);
|
||||
|
||||
final newEpisodes = EpisodeModel.episodesFromDto(
|
||||
episodes.body?.items,
|
||||
ref,
|
||||
);
|
||||
final episodesCanDownload = newEpisodes.any((episode) => episode.canDownload == true);
|
||||
final seasons = await api.showsSeriesIdSeasonsGet(seriesId: seriesModel.id);
|
||||
newState = newState.copyWith(
|
||||
availableEpisodes: EpisodeModel.episodesFromDto(
|
||||
episodes.body?.items,
|
||||
ref,
|
||||
),
|
||||
seasons: SeasonModel.seasonsFromDto(seasons.body?.items, ref)
|
||||
.map((element) => element.copyWith(canDownload: true))
|
||||
.toList(),
|
||||
);
|
||||
|
||||
newState = newState.copyWith(
|
||||
canDownload: episodesCanDownload,
|
||||
availableEpisodes: newEpisodes,
|
||||
);
|
||||
|
||||
final related = await ref.read(relatedUtilityProvider).relatedContent(seriesModel.id);
|
||||
|
|
|
|||
|
|
@ -1,7 +1,10 @@
|
|||
import 'package:flutter/widgets.dart';
|
||||
|
||||
import 'package:background_downloader/background_downloader.dart';
|
||||
import 'package:riverpod_annotation/riverpod_annotation.dart';
|
||||
|
||||
import 'package:fladder/providers/settings/client_settings_provider.dart';
|
||||
import 'package:fladder/util/localization_helper.dart';
|
||||
|
||||
part 'background_download_provider.g.dart';
|
||||
|
||||
|
|
@ -14,13 +17,7 @@ class BackgroundDownloader extends _$BackgroundDownloader {
|
|||
..configure(
|
||||
globalConfig: globalConfig(maxDownloads),
|
||||
)
|
||||
..trackTasks()
|
||||
..configureNotification(
|
||||
running: const TaskNotification('Downloading', 'file: {filename}'),
|
||||
complete: const TaskNotification('Download finished', 'file: {filename}'),
|
||||
paused: const TaskNotification('Download paused', 'file: {filename}'),
|
||||
progressBar: true,
|
||||
);
|
||||
..trackTasks();
|
||||
}
|
||||
|
||||
void setMaxConcurrent(int value) {
|
||||
|
|
@ -29,6 +26,16 @@ class BackgroundDownloader extends _$BackgroundDownloader {
|
|||
);
|
||||
}
|
||||
|
||||
void updateTranslations(BuildContext context) async {
|
||||
state.configureNotification(
|
||||
running: TaskNotification(context.localized.notificationDownloadingDownloading, '{filename}\n{networkSpeed}'),
|
||||
complete: TaskNotification(context.localized.notificationDownloadingFinished, '{filename}'),
|
||||
paused: TaskNotification(context.localized.notificationDownloadingPaused, '{filename}'),
|
||||
error: TaskNotification(context.localized.notificationDownloadingError, '{filename}'),
|
||||
progressBar: true,
|
||||
);
|
||||
}
|
||||
|
||||
(String, dynamic) globalConfig(int value) => value == 0
|
||||
? ("", "")
|
||||
: (
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ part of 'background_download_provider.dart';
|
|||
// **************************************************************************
|
||||
|
||||
String _$backgroundDownloaderHash() =>
|
||||
r'dc27f708fc2f1695d37afcb99f8814bc024037af';
|
||||
r'9d866549ed7632e855ba30de2765368960889cff';
|
||||
|
||||
/// See also [BackgroundDownloader].
|
||||
@ProviderFor(BackgroundDownloader)
|
||||
|
|
|
|||
|
|
@ -11,30 +11,40 @@ part 'sync_provider_helpers.g.dart';
|
|||
@riverpod
|
||||
class SyncChildren extends _$SyncChildren {
|
||||
@override
|
||||
List<SyncedItem> build(SyncedItem arg) {
|
||||
final syncedItemIsar = ref.watch(syncProvider.notifier).isar;
|
||||
final allChildren = <SyncedItem>[];
|
||||
List<SyncedItem> toProcess = [arg];
|
||||
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 currentLevel = toProcess.map(
|
||||
(parent) {
|
||||
final children = syncedItemIsar?.iSyncedItems.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();
|
||||
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 allChildren;
|
||||
|
||||
return all;
|
||||
}
|
||||
}
|
||||
|
||||
@riverpod
|
||||
class SyncDownloadStatus extends _$SyncDownloadStatus {
|
||||
@override
|
||||
DownloadStream? build(SyncedItem arg) {
|
||||
final nestedChildren = ref.watch(syncChildrenProvider(arg));
|
||||
DownloadStream? build(SyncedItem arg, List<SyncedItem> children) {
|
||||
final nestedChildren = children;
|
||||
|
||||
ref.watch(downloadTasksProvider(arg.id));
|
||||
for (var element in nestedChildren) {
|
||||
|
|
@ -64,20 +74,23 @@ class SyncDownloadStatus extends _$SyncDownloadStatus {
|
|||
@riverpod
|
||||
class SyncStatuses extends _$SyncStatuses {
|
||||
@override
|
||||
FutureOr<SyncStatus> build(SyncedItem arg) async {
|
||||
final nestedChildren = ref.watch(syncChildrenProvider(arg));
|
||||
FutureOr<SyncStatus> build(SyncedItem arg, List<SyncedItem>? children) async {
|
||||
final nestedChildren = children;
|
||||
|
||||
ref.watch(downloadTasksProvider(arg.id));
|
||||
for (var element in nestedChildren) {
|
||||
ref.watch(downloadTasksProvider(element.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;
|
||||
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;
|
||||
}
|
||||
|
|
@ -88,17 +101,21 @@ class SyncStatuses extends _$SyncStatuses {
|
|||
@riverpod
|
||||
class SyncSize extends _$SyncSize {
|
||||
@override
|
||||
int? build(SyncedItem arg) {
|
||||
final nestedChildren = ref.watch(syncChildrenProvider(arg));
|
||||
int? build(SyncedItem arg, List<SyncedItem>? children) {
|
||||
final nestedChildren = children;
|
||||
|
||||
ref.watch(downloadTasksProvider(arg.id));
|
||||
for (var element in nestedChildren) {
|
||||
ref.watch(downloadTasksProvider(element.id));
|
||||
}
|
||||
int size = arg.fileSize ?? 0;
|
||||
for (var element in nestedChildren) {
|
||||
size += element.fileSize ?? 0;
|
||||
|
||||
if (nestedChildren != null) {
|
||||
for (var element in nestedChildren) {
|
||||
ref.watch(downloadTasksProvider(element.id));
|
||||
}
|
||||
for (var element in nestedChildren) {
|
||||
size += element.fileSize ?? 0;
|
||||
}
|
||||
}
|
||||
|
||||
return size;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ part of 'sync_provider_helpers.dart';
|
|||
// RiverpodGenerator
|
||||
// **************************************************************************
|
||||
|
||||
String _$syncChildrenHash() => r'f6fdb1aa36d6655976baa5fbe0d8a6b812d7e95b';
|
||||
String _$syncChildrenHash() => r'c5a90d630d49f59ad4fbaacb5154f1205799f5ab';
|
||||
|
||||
/// Copied from Dart SDK
|
||||
class _SystemHash {
|
||||
|
|
@ -31,10 +31,10 @@ class _SystemHash {
|
|||
|
||||
abstract class _$SyncChildren
|
||||
extends BuildlessAutoDisposeNotifier<List<SyncedItem>> {
|
||||
late final SyncedItem arg;
|
||||
late final SyncedItem root;
|
||||
|
||||
List<SyncedItem> build(
|
||||
SyncedItem arg,
|
||||
SyncedItem root,
|
||||
);
|
||||
}
|
||||
|
||||
|
|
@ -49,10 +49,10 @@ class SyncChildrenFamily extends Family<List<SyncedItem>> {
|
|||
|
||||
/// See also [SyncChildren].
|
||||
SyncChildrenProvider call(
|
||||
SyncedItem arg,
|
||||
SyncedItem root,
|
||||
) {
|
||||
return SyncChildrenProvider(
|
||||
arg,
|
||||
root,
|
||||
);
|
||||
}
|
||||
|
||||
|
|
@ -61,7 +61,7 @@ class SyncChildrenFamily extends Family<List<SyncedItem>> {
|
|||
covariant SyncChildrenProvider provider,
|
||||
) {
|
||||
return call(
|
||||
provider.arg,
|
||||
provider.root,
|
||||
);
|
||||
}
|
||||
|
||||
|
|
@ -85,9 +85,9 @@ class SyncChildrenProvider
|
|||
extends AutoDisposeNotifierProviderImpl<SyncChildren, List<SyncedItem>> {
|
||||
/// See also [SyncChildren].
|
||||
SyncChildrenProvider(
|
||||
SyncedItem arg,
|
||||
SyncedItem root,
|
||||
) : this._internal(
|
||||
() => SyncChildren()..arg = arg,
|
||||
() => SyncChildren()..root = root,
|
||||
from: syncChildrenProvider,
|
||||
name: r'syncChildrenProvider',
|
||||
debugGetCreateSourceHash:
|
||||
|
|
@ -97,7 +97,7 @@ class SyncChildrenProvider
|
|||
dependencies: SyncChildrenFamily._dependencies,
|
||||
allTransitiveDependencies:
|
||||
SyncChildrenFamily._allTransitiveDependencies,
|
||||
arg: arg,
|
||||
root: root,
|
||||
);
|
||||
|
||||
SyncChildrenProvider._internal(
|
||||
|
|
@ -107,17 +107,17 @@ class SyncChildrenProvider
|
|||
required super.allTransitiveDependencies,
|
||||
required super.debugGetCreateSourceHash,
|
||||
required super.from,
|
||||
required this.arg,
|
||||
required this.root,
|
||||
}) : super.internal();
|
||||
|
||||
final SyncedItem arg;
|
||||
final SyncedItem root;
|
||||
|
||||
@override
|
||||
List<SyncedItem> runNotifierBuild(
|
||||
covariant SyncChildren notifier,
|
||||
) {
|
||||
return notifier.build(
|
||||
arg,
|
||||
root,
|
||||
);
|
||||
}
|
||||
|
||||
|
|
@ -126,13 +126,13 @@ class SyncChildrenProvider
|
|||
return ProviderOverride(
|
||||
origin: this,
|
||||
override: SyncChildrenProvider._internal(
|
||||
() => create()..arg = arg,
|
||||
() => create()..root = root,
|
||||
from: from,
|
||||
name: null,
|
||||
dependencies: null,
|
||||
allTransitiveDependencies: null,
|
||||
debugGetCreateSourceHash: null,
|
||||
arg: arg,
|
||||
root: root,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
|
@ -145,13 +145,13 @@ class SyncChildrenProvider
|
|||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
return other is SyncChildrenProvider && other.arg == arg;
|
||||
return other is SyncChildrenProvider && other.root == root;
|
||||
}
|
||||
|
||||
@override
|
||||
int get hashCode {
|
||||
var hash = _SystemHash.combine(0, runtimeType.hashCode);
|
||||
hash = _SystemHash.combine(hash, arg.hashCode);
|
||||
hash = _SystemHash.combine(hash, root.hashCode);
|
||||
|
||||
return _SystemHash.finish(hash);
|
||||
}
|
||||
|
|
@ -160,8 +160,8 @@ class SyncChildrenProvider
|
|||
@Deprecated('Will be removed in 3.0. Use Ref instead')
|
||||
// ignore: unused_element
|
||||
mixin SyncChildrenRef on AutoDisposeNotifierProviderRef<List<SyncedItem>> {
|
||||
/// The parameter `arg` of this provider.
|
||||
SyncedItem get arg;
|
||||
/// The parameter `root` of this provider.
|
||||
SyncedItem get root;
|
||||
}
|
||||
|
||||
class _SyncChildrenProviderElement
|
||||
|
|
@ -170,18 +170,20 @@ class _SyncChildrenProviderElement
|
|||
_SyncChildrenProviderElement(super.provider);
|
||||
|
||||
@override
|
||||
SyncedItem get arg => (origin as SyncChildrenProvider).arg;
|
||||
SyncedItem get root => (origin as SyncChildrenProvider).root;
|
||||
}
|
||||
|
||||
String _$syncDownloadStatusHash() =>
|
||||
r'5a0f8537a977c52e6083bd84265631ea5d160637';
|
||||
r'1036352200e1138b4ef70e524c0baf13bb9cd452';
|
||||
|
||||
abstract class _$SyncDownloadStatus
|
||||
extends BuildlessAutoDisposeNotifier<DownloadStream?> {
|
||||
late final SyncedItem arg;
|
||||
late final List<SyncedItem> children;
|
||||
|
||||
DownloadStream? build(
|
||||
SyncedItem arg,
|
||||
List<SyncedItem> children,
|
||||
);
|
||||
}
|
||||
|
||||
|
|
@ -197,9 +199,11 @@ class SyncDownloadStatusFamily extends Family<DownloadStream?> {
|
|||
/// See also [SyncDownloadStatus].
|
||||
SyncDownloadStatusProvider call(
|
||||
SyncedItem arg,
|
||||
List<SyncedItem> children,
|
||||
) {
|
||||
return SyncDownloadStatusProvider(
|
||||
arg,
|
||||
children,
|
||||
);
|
||||
}
|
||||
|
||||
|
|
@ -209,6 +213,7 @@ class SyncDownloadStatusFamily extends Family<DownloadStream?> {
|
|||
) {
|
||||
return call(
|
||||
provider.arg,
|
||||
provider.children,
|
||||
);
|
||||
}
|
||||
|
||||
|
|
@ -233,8 +238,11 @@ class SyncDownloadStatusProvider extends AutoDisposeNotifierProviderImpl<
|
|||
/// See also [SyncDownloadStatus].
|
||||
SyncDownloadStatusProvider(
|
||||
SyncedItem arg,
|
||||
List<SyncedItem> children,
|
||||
) : this._internal(
|
||||
() => SyncDownloadStatus()..arg = arg,
|
||||
() => SyncDownloadStatus()
|
||||
..arg = arg
|
||||
..children = children,
|
||||
from: syncDownloadStatusProvider,
|
||||
name: r'syncDownloadStatusProvider',
|
||||
debugGetCreateSourceHash:
|
||||
|
|
@ -245,6 +253,7 @@ class SyncDownloadStatusProvider extends AutoDisposeNotifierProviderImpl<
|
|||
allTransitiveDependencies:
|
||||
SyncDownloadStatusFamily._allTransitiveDependencies,
|
||||
arg: arg,
|
||||
children: children,
|
||||
);
|
||||
|
||||
SyncDownloadStatusProvider._internal(
|
||||
|
|
@ -255,9 +264,11 @@ class SyncDownloadStatusProvider extends AutoDisposeNotifierProviderImpl<
|
|||
required super.debugGetCreateSourceHash,
|
||||
required super.from,
|
||||
required this.arg,
|
||||
required this.children,
|
||||
}) : super.internal();
|
||||
|
||||
final SyncedItem arg;
|
||||
final List<SyncedItem> children;
|
||||
|
||||
@override
|
||||
DownloadStream? runNotifierBuild(
|
||||
|
|
@ -265,6 +276,7 @@ class SyncDownloadStatusProvider extends AutoDisposeNotifierProviderImpl<
|
|||
) {
|
||||
return notifier.build(
|
||||
arg,
|
||||
children,
|
||||
);
|
||||
}
|
||||
|
||||
|
|
@ -273,13 +285,16 @@ class SyncDownloadStatusProvider extends AutoDisposeNotifierProviderImpl<
|
|||
return ProviderOverride(
|
||||
origin: this,
|
||||
override: SyncDownloadStatusProvider._internal(
|
||||
() => create()..arg = arg,
|
||||
() => create()
|
||||
..arg = arg
|
||||
..children = children,
|
||||
from: from,
|
||||
name: null,
|
||||
dependencies: null,
|
||||
allTransitiveDependencies: null,
|
||||
debugGetCreateSourceHash: null,
|
||||
arg: arg,
|
||||
children: children,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
|
@ -292,13 +307,16 @@ class SyncDownloadStatusProvider extends AutoDisposeNotifierProviderImpl<
|
|||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
return other is SyncDownloadStatusProvider && other.arg == arg;
|
||||
return other is SyncDownloadStatusProvider &&
|
||||
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);
|
||||
}
|
||||
|
|
@ -309,6 +327,9 @@ class SyncDownloadStatusProvider extends AutoDisposeNotifierProviderImpl<
|
|||
mixin SyncDownloadStatusRef on AutoDisposeNotifierProviderRef<DownloadStream?> {
|
||||
/// The parameter `arg` of this provider.
|
||||
SyncedItem get arg;
|
||||
|
||||
/// The parameter `children` of this provider.
|
||||
List<SyncedItem> get children;
|
||||
}
|
||||
|
||||
class _SyncDownloadStatusProviderElement
|
||||
|
|
@ -318,16 +339,21 @@ class _SyncDownloadStatusProviderElement
|
|||
|
||||
@override
|
||||
SyncedItem get arg => (origin as SyncDownloadStatusProvider).arg;
|
||||
@override
|
||||
List<SyncedItem> get children =>
|
||||
(origin as SyncDownloadStatusProvider).children;
|
||||
}
|
||||
|
||||
String _$syncStatusesHash() => r'f05ee53368d1de130714bba09132e08aba15bc44';
|
||||
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,
|
||||
);
|
||||
}
|
||||
|
||||
|
|
@ -343,9 +369,11 @@ class SyncStatusesFamily extends Family<AsyncValue<SyncStatus>> {
|
|||
/// See also [SyncStatuses].
|
||||
SyncStatusesProvider call(
|
||||
SyncedItem arg,
|
||||
List<SyncedItem>? children,
|
||||
) {
|
||||
return SyncStatusesProvider(
|
||||
arg,
|
||||
children,
|
||||
);
|
||||
}
|
||||
|
||||
|
|
@ -355,6 +383,7 @@ class SyncStatusesFamily extends Family<AsyncValue<SyncStatus>> {
|
|||
) {
|
||||
return call(
|
||||
provider.arg,
|
||||
provider.children,
|
||||
);
|
||||
}
|
||||
|
||||
|
|
@ -379,8 +408,11 @@ class SyncStatusesProvider
|
|||
/// See also [SyncStatuses].
|
||||
SyncStatusesProvider(
|
||||
SyncedItem arg,
|
||||
List<SyncedItem>? children,
|
||||
) : this._internal(
|
||||
() => SyncStatuses()..arg = arg,
|
||||
() => SyncStatuses()
|
||||
..arg = arg
|
||||
..children = children,
|
||||
from: syncStatusesProvider,
|
||||
name: r'syncStatusesProvider',
|
||||
debugGetCreateSourceHash:
|
||||
|
|
@ -391,6 +423,7 @@ class SyncStatusesProvider
|
|||
allTransitiveDependencies:
|
||||
SyncStatusesFamily._allTransitiveDependencies,
|
||||
arg: arg,
|
||||
children: children,
|
||||
);
|
||||
|
||||
SyncStatusesProvider._internal(
|
||||
|
|
@ -401,9 +434,11 @@ class SyncStatusesProvider
|
|||
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(
|
||||
|
|
@ -411,6 +446,7 @@ class SyncStatusesProvider
|
|||
) {
|
||||
return notifier.build(
|
||||
arg,
|
||||
children,
|
||||
);
|
||||
}
|
||||
|
||||
|
|
@ -419,13 +455,16 @@ class SyncStatusesProvider
|
|||
return ProviderOverride(
|
||||
origin: this,
|
||||
override: SyncStatusesProvider._internal(
|
||||
() => create()..arg = arg,
|
||||
() => create()
|
||||
..arg = arg
|
||||
..children = children,
|
||||
from: from,
|
||||
name: null,
|
||||
dependencies: null,
|
||||
allTransitiveDependencies: null,
|
||||
debugGetCreateSourceHash: null,
|
||||
arg: arg,
|
||||
children: children,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
|
@ -438,13 +477,16 @@ class SyncStatusesProvider
|
|||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
return other is SyncStatusesProvider && other.arg == arg;
|
||||
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);
|
||||
}
|
||||
|
|
@ -455,6 +497,9 @@ class SyncStatusesProvider
|
|||
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
|
||||
|
|
@ -464,15 +509,19 @@ class _SyncStatusesProviderElement
|
|||
|
||||
@override
|
||||
SyncedItem get arg => (origin as SyncStatusesProvider).arg;
|
||||
@override
|
||||
List<SyncedItem>? get children => (origin as SyncStatusesProvider).children;
|
||||
}
|
||||
|
||||
String _$syncSizeHash() => r'138702f2dd69ab28d142bab67ab4a497bb24f252';
|
||||
String _$syncSizeHash() => r'81797ecc4a6f600691b6f1fe0c16bae0228ec920';
|
||||
|
||||
abstract class _$SyncSize extends BuildlessAutoDisposeNotifier<int?> {
|
||||
late final SyncedItem arg;
|
||||
late final List<SyncedItem>? children;
|
||||
|
||||
int? build(
|
||||
SyncedItem arg,
|
||||
List<SyncedItem>? children,
|
||||
);
|
||||
}
|
||||
|
||||
|
|
@ -488,9 +537,11 @@ class SyncSizeFamily extends Family<int?> {
|
|||
/// See also [SyncSize].
|
||||
SyncSizeProvider call(
|
||||
SyncedItem arg,
|
||||
List<SyncedItem>? children,
|
||||
) {
|
||||
return SyncSizeProvider(
|
||||
arg,
|
||||
children,
|
||||
);
|
||||
}
|
||||
|
||||
|
|
@ -500,6 +551,7 @@ class SyncSizeFamily extends Family<int?> {
|
|||
) {
|
||||
return call(
|
||||
provider.arg,
|
||||
provider.children,
|
||||
);
|
||||
}
|
||||
|
||||
|
|
@ -523,8 +575,11 @@ class SyncSizeProvider extends AutoDisposeNotifierProviderImpl<SyncSize, int?> {
|
|||
/// See also [SyncSize].
|
||||
SyncSizeProvider(
|
||||
SyncedItem arg,
|
||||
List<SyncedItem>? children,
|
||||
) : this._internal(
|
||||
() => SyncSize()..arg = arg,
|
||||
() => SyncSize()
|
||||
..arg = arg
|
||||
..children = children,
|
||||
from: syncSizeProvider,
|
||||
name: r'syncSizeProvider',
|
||||
debugGetCreateSourceHash:
|
||||
|
|
@ -534,6 +589,7 @@ class SyncSizeProvider extends AutoDisposeNotifierProviderImpl<SyncSize, int?> {
|
|||
dependencies: SyncSizeFamily._dependencies,
|
||||
allTransitiveDependencies: SyncSizeFamily._allTransitiveDependencies,
|
||||
arg: arg,
|
||||
children: children,
|
||||
);
|
||||
|
||||
SyncSizeProvider._internal(
|
||||
|
|
@ -544,9 +600,11 @@ class SyncSizeProvider extends AutoDisposeNotifierProviderImpl<SyncSize, int?> {
|
|||
required super.debugGetCreateSourceHash,
|
||||
required super.from,
|
||||
required this.arg,
|
||||
required this.children,
|
||||
}) : super.internal();
|
||||
|
||||
final SyncedItem arg;
|
||||
final List<SyncedItem>? children;
|
||||
|
||||
@override
|
||||
int? runNotifierBuild(
|
||||
|
|
@ -554,6 +612,7 @@ class SyncSizeProvider extends AutoDisposeNotifierProviderImpl<SyncSize, int?> {
|
|||
) {
|
||||
return notifier.build(
|
||||
arg,
|
||||
children,
|
||||
);
|
||||
}
|
||||
|
||||
|
|
@ -562,13 +621,16 @@ class SyncSizeProvider extends AutoDisposeNotifierProviderImpl<SyncSize, int?> {
|
|||
return ProviderOverride(
|
||||
origin: this,
|
||||
override: SyncSizeProvider._internal(
|
||||
() => create()..arg = arg,
|
||||
() => create()
|
||||
..arg = arg
|
||||
..children = children,
|
||||
from: from,
|
||||
name: null,
|
||||
dependencies: null,
|
||||
allTransitiveDependencies: null,
|
||||
debugGetCreateSourceHash: null,
|
||||
arg: arg,
|
||||
children: children,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
|
@ -580,13 +642,16 @@ class SyncSizeProvider extends AutoDisposeNotifierProviderImpl<SyncSize, int?> {
|
|||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
return other is SyncSizeProvider && other.arg == arg;
|
||||
return other is SyncSizeProvider &&
|
||||
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);
|
||||
}
|
||||
|
|
@ -597,6 +662,9 @@ class SyncSizeProvider extends AutoDisposeNotifierProviderImpl<SyncSize, int?> {
|
|||
mixin SyncSizeRef on AutoDisposeNotifierProviderRef<int?> {
|
||||
/// The parameter `arg` of this provider.
|
||||
SyncedItem get arg;
|
||||
|
||||
/// The parameter `children` of this provider.
|
||||
List<SyncedItem>? get children;
|
||||
}
|
||||
|
||||
class _SyncSizeProviderElement
|
||||
|
|
@ -606,6 +674,8 @@ class _SyncSizeProviderElement
|
|||
|
||||
@override
|
||||
SyncedItem get arg => (origin as SyncSizeProvider).arg;
|
||||
@override
|
||||
List<SyncedItem>? get children => (origin as SyncSizeProvider).children;
|
||||
}
|
||||
// ignore_for_file: type=lint
|
||||
// ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member, deprecated_member_use_from_same_package
|
||||
|
|
|
|||
|
|
@ -22,6 +22,7 @@ import 'package:fladder/models/items/episode_model.dart';
|
|||
import 'package:fladder/models/items/images_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/download_stream.dart';
|
||||
|
|
@ -238,6 +239,7 @@ class SyncNotifier extends StateNotifier<SyncSettingsModel> {
|
|||
fladderSnackbar(context, title: context.localized.syncAddItemForSyncing(item.detailedName(context) ?? "Unknown"));
|
||||
final newSync = switch (item) {
|
||||
EpisodeModel episode => await syncSeries(item.parentBaseModel, episode: episode),
|
||||
SeasonModel season => await syncSeries(item.parentBaseModel, season: season),
|
||||
SeriesModel series => await syncSeries(series),
|
||||
MovieModel movie => await syncMovie(movie),
|
||||
_ => null
|
||||
|
|
@ -367,7 +369,7 @@ class SyncNotifier extends StateNotifier<SyncSettingsModel> {
|
|||
if (data == null) return data;
|
||||
if (!itemPath.existsSync()) return data;
|
||||
if (data.isEmpty) return data;
|
||||
final saveDirectory = Directory(path.joinAll([itemPath.path, "Chapters"]));
|
||||
final saveDirectory = Directory(path.joinAll([itemPath.path, SyncedItem.chaptersPath]));
|
||||
|
||||
await saveDirectory.create(recursive: true);
|
||||
|
||||
|
|
@ -378,7 +380,7 @@ class SyncNotifier extends StateNotifier<SyncSettingsModel> {
|
|||
if (response.bodyBytes.isEmpty) return null;
|
||||
file.writeAsBytesSync(response.bodyBytes);
|
||||
return event.copyWith(
|
||||
imageUrl: path.joinAll(["Chapters", fileName]),
|
||||
imageUrl: path.joinAll([SyncedItem.chaptersPath, fileName]),
|
||||
);
|
||||
}).toList();
|
||||
return saveChapters.nonNulls.toList();
|
||||
|
|
@ -415,7 +417,7 @@ class SyncNotifier extends StateNotifier<SyncSettingsModel> {
|
|||
return syncedItem;
|
||||
}
|
||||
|
||||
Future<DownloadStream?> syncVideoFile(SyncedItem syncItem, bool skipDownload) async {
|
||||
Future<DownloadStream?> syncFile(SyncedItem syncItem, bool skipDownload) async {
|
||||
cleanupTemporaryFiles();
|
||||
|
||||
final playbackResponse = await api.itemsItemIdPlaybackInfoPost(
|
||||
|
|
@ -439,6 +441,7 @@ class SyncNotifier extends StateNotifier<SyncSettingsModel> {
|
|||
final mediaSegments = (await api.mediaSegmentsGet(id: syncItem.id))?.body;
|
||||
|
||||
syncItem = syncItem.copyWith(
|
||||
fChapters: await saveChapterImages(item?.overview.chapters, directory) ?? [],
|
||||
subtitles: subtitles,
|
||||
fTrickPlayModel: trickPlayFile,
|
||||
mediaSegments: mediaSegments,
|
||||
|
|
@ -447,23 +450,24 @@ class SyncNotifier extends StateNotifier<SyncSettingsModel> {
|
|||
await updateItem(syncItem);
|
||||
|
||||
final currentTask = ref.read(downloadTasksProvider(syncItem.id));
|
||||
final user = ref.read(userProvider);
|
||||
|
||||
final downloadString = path.joinAll([
|
||||
"${ref.read(userProvider)?.server}",
|
||||
"Items",
|
||||
"${syncItem.id}/Download?api_key=${ref.read(userProvider)?.credentials.token}"
|
||||
]);
|
||||
if (user == null) return null;
|
||||
|
||||
final downloadUrl = path.joinAll([user.server, "Items", syncItem.id, "Download"]);
|
||||
|
||||
try {
|
||||
if (!skipDownload && currentTask.task == null) {
|
||||
final downloadTask = DownloadTask(
|
||||
url: Uri.parse(downloadString).toString(),
|
||||
url: Uri.parse(downloadUrl).toString(),
|
||||
directory: syncItem.directory.path,
|
||||
filename: syncItem.videoFileName,
|
||||
updates: Updates.statusAndProgress,
|
||||
baseDirectory: BaseDirectory.root,
|
||||
urlQueryParameters: {"api_key": user.credentials.token},
|
||||
headers: user.credentials.header(ref),
|
||||
requiresWiFi: ref.read(clientSettingsProvider.select((value) => value.requireWifi)),
|
||||
retries: 5,
|
||||
retries: 3,
|
||||
allowPause: true,
|
||||
);
|
||||
|
||||
|
|
@ -490,7 +494,7 @@ class SyncNotifier extends StateNotifier<SyncSettingsModel> {
|
|||
(state) => state.copyWith(status: status),
|
||||
);
|
||||
|
||||
if (status == TaskStatus.complete) {
|
||||
if (status == TaskStatus.complete || status == TaskStatus.canceled) {
|
||||
ref.read(downloadTasksProvider(syncItem.id).notifier).update((state) => DownloadStream.empty());
|
||||
}
|
||||
},
|
||||
|
|
@ -522,6 +526,10 @@ extension SyncNotifierHelpers on SyncNotifier {
|
|||
Future<SyncedItem> createSyncItem(BaseItemDto response, {SyncedItem? parent}) async {
|
||||
final ItemBaseModel item = ItemBaseModel.fromBaseDto(response, ref);
|
||||
|
||||
final existingSyncedItem = getSyncedItem(item);
|
||||
|
||||
if (existingSyncedItem != null) return existingSyncedItem;
|
||||
|
||||
final Directory? parentDirectory = parent?.directory;
|
||||
|
||||
final directory = Directory(path.joinAll([(parentDirectory ?? saveDirectory)?.path ?? "", item.id]));
|
||||
|
|
@ -543,31 +551,17 @@ extension SyncNotifierHelpers on SyncNotifier {
|
|||
userData: item.userData,
|
||||
);
|
||||
|
||||
//Save item if parent so the user is aware.
|
||||
if (parent == null) {
|
||||
isar?.write((isar) => syncedItems?.put(ISyncedItem.fromSynced(syncItem, syncPath)));
|
||||
}
|
||||
|
||||
final origChapters = Chapter.chaptersFromInfo(item.id, response.chapters ?? [], ref);
|
||||
|
||||
return syncItem.copyWith(
|
||||
fChapters: await saveChapterImages(origChapters, directory) ?? [],
|
||||
fileSize: response.mediaSources?.firstOrNull?.size ?? 0,
|
||||
syncing: false,
|
||||
videoFileName: response.path?.split('/').lastOrNull ?? "",
|
||||
);
|
||||
}
|
||||
|
||||
// Need to move the file after downloading on Android
|
||||
Future<void> moveFile(DownloadTask downloadTask, SyncedItem syncItem) async {
|
||||
final currentLocation = File(await downloadTask.filePath());
|
||||
final wantedLocation = syncItem.videoFile;
|
||||
if (currentLocation.path != wantedLocation.path) {
|
||||
await currentLocation.copy(wantedLocation.path);
|
||||
await currentLocation.delete();
|
||||
}
|
||||
}
|
||||
|
||||
Future<SyncedItem?> syncMovie(ItemBaseModel item, {bool skipDownload = false}) async {
|
||||
final response = await api.usersUserIdItemsItemIdGetBaseItem(
|
||||
itemId: item.id,
|
||||
|
|
@ -580,21 +574,21 @@ extension SyncNotifierHelpers on SyncNotifier {
|
|||
|
||||
if (!syncItem.directory.existsSync()) return null;
|
||||
|
||||
await syncVideoFile(syncItem, skipDownload);
|
||||
await syncFile(syncItem, skipDownload);
|
||||
|
||||
isar?.write((isar) => syncedItems?.put(ISyncedItem.fromSynced(syncItem, syncPath)));
|
||||
|
||||
return syncItem;
|
||||
}
|
||||
|
||||
Future<SyncedItem?> syncSeries(SeriesModel item, {EpisodeModel? episode}) async {
|
||||
Future<SyncedItem?> syncSeries(SeriesModel item, {SeasonModel? season, EpisodeModel? episode}) async {
|
||||
final response = await api.usersUserIdItemsItemIdGetBaseItem(
|
||||
itemId: item.id,
|
||||
);
|
||||
|
||||
List<SyncedItem> newItems = [];
|
||||
|
||||
SyncedItem? itemToDownload;
|
||||
List<SyncedItem>? itemsToDownload = [];
|
||||
|
||||
SyncedItem seriesItem = await createSyncItem(response.bodyOrThrow);
|
||||
newItems.add(seriesItem);
|
||||
|
|
@ -627,8 +621,8 @@ extension SyncNotifierHelpers on SyncNotifier {
|
|||
final seasons = seasonsResponse.body?.items ?? [];
|
||||
|
||||
for (var i = 0; i < seasons.length; i++) {
|
||||
final season = seasons[i];
|
||||
final syncedSeason = await createSyncItem(season, parent: seriesItem);
|
||||
final newSeason = seasons[i];
|
||||
final syncedSeason = await createSyncItem(newSeason, parent: seriesItem);
|
||||
newItems.add(syncedSeason);
|
||||
final episodesResponse = await api.showsSeriesIdEpisodesGet(
|
||||
isMissing: false,
|
||||
|
|
@ -651,16 +645,23 @@ extension SyncNotifierHelpers on SyncNotifier {
|
|||
ItemFields.chapters,
|
||||
ItemFields.trickplay,
|
||||
],
|
||||
seasonId: season.id,
|
||||
seasonId: newSeason.id,
|
||||
seriesId: seriesItem.id,
|
||||
);
|
||||
|
||||
final episodes = episodesResponse.body?.items ?? [];
|
||||
for (var i = 0; i < episodes.length; i++) {
|
||||
final item = episodes[i];
|
||||
final newEpisode = await createSyncItem(item, parent: syncedSeason);
|
||||
|
||||
final episodeResults = await Future.wait(
|
||||
episodes.map((ep) async {
|
||||
final newEpisode = await createSyncItem(ep, parent: syncedSeason);
|
||||
return (ep, newEpisode);
|
||||
}),
|
||||
);
|
||||
|
||||
for (final (ep, newEpisode) in episodeResults) {
|
||||
newItems.add(newEpisode);
|
||||
if (episode?.id == item.id) {
|
||||
itemToDownload = newEpisode;
|
||||
if (episode?.id == ep.id || newSeason.id == season?.id) {
|
||||
itemsToDownload.add(newEpisode);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -673,8 +674,9 @@ extension SyncNotifierHelpers on SyncNotifier {
|
|||
.toList()),
|
||||
);
|
||||
|
||||
if (itemToDownload != null) {
|
||||
await syncVideoFile(itemToDownload, false);
|
||||
for (var i = 0; i < itemsToDownload.length; i++) {
|
||||
final item = itemsToDownload[i];
|
||||
await syncFile(item, false);
|
||||
}
|
||||
|
||||
return seriesItem;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue