mirror of
https://github.com/gabehf/Fladder.git
synced 2026-03-07 21:48:14 -08:00
feat: Sync offline/online playback when able (#431)
Co-authored-by: PartyDonut <PartyDonut@users.noreply.github.com>
This commit is contained in:
parent
15ac3566e2
commit
092836328f
42 changed files with 1002 additions and 497 deletions
|
|
@ -48,8 +48,42 @@ class UserData with UserDataMappable {
|
|||
);
|
||||
}
|
||||
|
||||
static dto.UserItemDataDto toDto(UserData? data) {
|
||||
if (data == null) {
|
||||
return const dto.UserItemDataDto();
|
||||
}
|
||||
return dto.UserItemDataDto(
|
||||
isFavorite: data.isFavourite,
|
||||
playCount: data.playCount,
|
||||
playbackPositionTicks: data.playbackPositionTicks,
|
||||
played: data.played,
|
||||
unplayedItemCount: data.unPlayedItemCount,
|
||||
lastPlayedDate: data.lastPlayed,
|
||||
playedPercentage: data.progress,
|
||||
);
|
||||
}
|
||||
|
||||
Duration get playBackPosition => Duration(milliseconds: playbackPositionTicks ~/ 10000);
|
||||
|
||||
// Returns null if unplayed with no progress
|
||||
static bool? isPlayed(Duration position, Duration totalDuration) {
|
||||
Duration startBuffer = totalDuration * 0.05;
|
||||
Duration endBuffer = totalDuration * 0.90;
|
||||
|
||||
Duration validStart = startBuffer;
|
||||
Duration validEnd = endBuffer;
|
||||
|
||||
if (position <= validStart) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (position <= validEnd) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static UserData? determineLastUserData(List<UserData?> data) {
|
||||
return data.where((data) => data != null).reduce((a, b) {
|
||||
final aDate = a?.lastPlayed;
|
||||
|
|
|
|||
|
|
@ -66,35 +66,36 @@ class OfflinePlaybackModel extends PlaybackModel {
|
|||
|
||||
@override
|
||||
Future<PlaybackModel?> playbackStopped(Duration position, Duration? totalDuration, Ref ref) async {
|
||||
final progress = position.inMilliseconds / (item.overview.runTime?.inMilliseconds ?? 0) * 100;
|
||||
final isPlayed = UserData.isPlayed(position, item.overview.runTime ?? Duration.zero);
|
||||
final userData = syncedItem.userData?.copyWith(
|
||||
playbackPositionTicks: isPlayed != false ? 0 : position.toRuntimeTicks,
|
||||
progress: isPlayed != false ? 0.0 : progress,
|
||||
played: isPlayed,
|
||||
lastPlayed: DateTime.now().toUtc(),
|
||||
);
|
||||
final newItem = syncedItem.copyWith(
|
||||
userData: userData,
|
||||
);
|
||||
await ref.read(syncProvider.notifier).updateItem(newItem);
|
||||
return null;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<PlaybackModel?> updatePlaybackPosition(Duration position, bool isPlaying, Ref ref) async {
|
||||
final progress = position.inMilliseconds / (item.overview.runTime?.inMilliseconds ?? 0) * 100;
|
||||
final isPlayed = UserData.isPlayed(position, item.overview.runTime ?? Duration.zero);
|
||||
final userData = syncedItem.userData?.copyWith(
|
||||
playbackPositionTicks: isPlayed != false ? 0 : position.toRuntimeTicks,
|
||||
progress: isPlayed != false ? 0.0 : progress,
|
||||
played: isPlayed,
|
||||
lastPlayed: DateTime.now().toUtc(),
|
||||
);
|
||||
final newItem = syncedItem.copyWith(
|
||||
userData: syncedItem.userData?.copyWith(
|
||||
playbackPositionTicks: position.toRuntimeTicks,
|
||||
progress: progress,
|
||||
played: isPlayed(position, item.overview.runTime ?? Duration.zero),
|
||||
),
|
||||
userData: userData,
|
||||
);
|
||||
await ref.read(syncProvider.notifier).updateItem(newItem);
|
||||
return this;
|
||||
}
|
||||
|
||||
bool isPlayed(Duration position, Duration totalDuration) {
|
||||
Duration startBuffer = totalDuration * 0.05;
|
||||
Duration endBuffer = totalDuration * 0.90;
|
||||
|
||||
Duration validStart = startBuffer;
|
||||
Duration validEnd = endBuffer;
|
||||
|
||||
if (position >= validStart && position <= validEnd) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
return null;
|
||||
}
|
||||
|
||||
@override
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import 'dart:developer';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/material.dart' hide ConnectionState;
|
||||
|
||||
import 'package:background_downloader/background_downloader.dart';
|
||||
import 'package:chopper/chopper.dart';
|
||||
|
|
@ -26,6 +26,7 @@ import 'package:fladder/models/syncing/sync_item.dart';
|
|||
import 'package:fladder/models/video_stream_model.dart';
|
||||
import 'package:fladder/profiles/default_profile.dart';
|
||||
import 'package:fladder/providers/api_provider.dart';
|
||||
import 'package:fladder/providers/connectivity_provider.dart';
|
||||
import 'package:fladder/providers/service_provider.dart';
|
||||
import 'package:fladder/providers/settings/video_player_settings_provider.dart';
|
||||
import 'package:fladder/providers/sync_provider.dart';
|
||||
|
|
@ -132,7 +133,7 @@ class PlaybackModelHelper {
|
|||
await _createOfflinePlaybackModel(
|
||||
newItem,
|
||||
null,
|
||||
await ref.read(syncProvider.notifier).getSyncedItem(newItem),
|
||||
await ref.read(syncProvider.notifier).getSyncedItem(newItem.id),
|
||||
oldModel: currentModel,
|
||||
);
|
||||
if (newModel == null) return null;
|
||||
|
|
@ -147,11 +148,12 @@ class PlaybackModelHelper {
|
|||
PlaybackModel? oldModel,
|
||||
}) async {
|
||||
final ItemBaseModel? syncedItemModel = syncedItem?.itemModel;
|
||||
if (syncedItemModel == null || syncedItem == null || !syncedItem.dataFile.existsSync()) return null;
|
||||
if (syncedItemModel == null || syncedItem == null || !await syncedItem.videoFile.exists()) return null;
|
||||
|
||||
final children = await ref.read(syncProvider.notifier).getChildren(syncedItem);
|
||||
final syncedItems = children.where((element) => element.videoFile.existsSync()).toList();
|
||||
final itemQueue = syncedItems.map((e) => e.createItemModel(ref));
|
||||
final children = await ref.read(syncProvider.notifier).getSiblings(syncedItem);
|
||||
final syncedItems =
|
||||
children.where((element) => element.videoFile.existsSync() && element.id != syncedItem.id).toList();
|
||||
final itemQueue = syncedItems.map((e) => e.itemModel).nonNulls;
|
||||
|
||||
return OfflinePlaybackModel(
|
||||
item: syncedItemModel,
|
||||
|
|
@ -173,11 +175,11 @@ class PlaybackModelHelper {
|
|||
bool showPlaybackOptions = false,
|
||||
Duration? startPosition,
|
||||
}) async {
|
||||
if (item == null) return null;
|
||||
final userId = ref.read(userProvider)?.id;
|
||||
if (userId?.isEmpty == true) return null;
|
||||
|
||||
try {
|
||||
if (item == null) return null;
|
||||
final userId = ref.read(userProvider)?.id;
|
||||
if (userId?.isEmpty == true) return null;
|
||||
|
||||
final queue = oldModel?.queue ?? libraryQueue ?? await collectQueue(item);
|
||||
|
||||
final firstItemToPlay = switch (item) {
|
||||
|
|
@ -191,7 +193,7 @@ class PlaybackModelHelper {
|
|||
|
||||
if (fullItem == null) return null;
|
||||
|
||||
SyncedItem? syncedItem = await ref.read(syncProvider.notifier).getSyncedItem(fullItem);
|
||||
SyncedItem? syncedItem = await ref.read(syncProvider.notifier).getSyncedItem(fullItem.id);
|
||||
|
||||
final firstItemIsSynced = syncedItem != null && syncedItem.status == TaskStatus.complete;
|
||||
|
||||
|
|
@ -201,7 +203,9 @@ class PlaybackModelHelper {
|
|||
if (firstItemIsSynced) PlaybackType.offline,
|
||||
};
|
||||
|
||||
if ((showPlaybackOptions || firstItemIsSynced) && context != null) {
|
||||
final isOffline = ref.read(connectivityStatusProvider.select((value) => value == ConnectionState.offline));
|
||||
|
||||
if (((showPlaybackOptions || firstItemIsSynced) && !isOffline) && context != null) {
|
||||
final playbackType = await showPlaybackTypeSelection(
|
||||
context: context,
|
||||
options: options,
|
||||
|
|
@ -241,16 +245,7 @@ class PlaybackModelHelper {
|
|||
);
|
||||
}
|
||||
} catch (e) {
|
||||
log(e.toString());
|
||||
SyncedItem? syncedItem = await ref.read(syncProvider.notifier).getSyncedItem(item);
|
||||
if (syncedItem != null) {
|
||||
return await _createOfflinePlaybackModel(
|
||||
item,
|
||||
item.streamModel,
|
||||
syncedItem,
|
||||
oldModel: oldModel,
|
||||
);
|
||||
}
|
||||
log("Error creating playback model: ${e.toString()}");
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
import 'package:flutter/material.dart';
|
||||
|
||||
import 'package:fladder/models/video_stream_model.dart';
|
||||
import 'package:fladder/screens/shared/adaptive_dialog.dart';
|
||||
import 'package:fladder/util/localization_helper.dart';
|
||||
|
||||
Future<PlaybackType?> showPlaybackTypeSelection({
|
||||
|
|
@ -10,17 +9,18 @@ Future<PlaybackType?> showPlaybackTypeSelection({
|
|||
}) async {
|
||||
PlaybackType? playbackType;
|
||||
|
||||
await showDialogAdaptive(
|
||||
await showDialog(
|
||||
context: context,
|
||||
builder: (context) {
|
||||
return PlaybackDialogue(
|
||||
useSafeArea: false,
|
||||
builder: (context) => Dialog(
|
||||
child: PlaybackDialogue(
|
||||
options: options,
|
||||
onClose: (type) {
|
||||
playbackType = type;
|
||||
Navigator.of(context).pop();
|
||||
},
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
);
|
||||
return playbackType;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,8 +1,10 @@
|
|||
import 'dart:convert';
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:drift/drift.dart';
|
||||
import 'package:drift_flutter/drift_flutter.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:path/path.dart' as p;
|
||||
import 'package:path_provider/path_provider.dart';
|
||||
|
||||
import 'package:fladder/models/item_base_model.dart';
|
||||
|
|
@ -17,11 +19,13 @@ import 'package:fladder/providers/user_provider.dart';
|
|||
|
||||
part 'database_item.g.dart';
|
||||
|
||||
const _databseName = 'syncedDatabase';
|
||||
|
||||
@TableIndex(name: 'database_id', columns: {#userId, #id})
|
||||
class DatabaseItems extends Table {
|
||||
TextColumn get userId => text().withLength(min: 1)();
|
||||
TextColumn get id => text().withLength(min: 1)();
|
||||
BoolColumn get syncing => boolean()();
|
||||
BoolColumn get syncing => boolean().withDefault(const Constant(false))();
|
||||
TextColumn get sortName => text().nullable()();
|
||||
TextColumn get parentId => text().nullable()();
|
||||
TextColumn get path => text().nullable()();
|
||||
|
|
@ -32,6 +36,7 @@ class DatabaseItems extends Table {
|
|||
TextColumn get images => text().nullable()();
|
||||
TextColumn get chapters => text().nullable()();
|
||||
TextColumn get subtitles => text().nullable()();
|
||||
BoolColumn get unSyncedData => boolean().withDefault(const Constant(false))();
|
||||
TextColumn get userData => text().nullable()();
|
||||
|
||||
@override
|
||||
|
|
@ -47,14 +52,15 @@ class AppDatabase extends _$AppDatabase {
|
|||
String get userId => ref.read(userProvider.select((value) => value?.id ?? ""));
|
||||
|
||||
@override
|
||||
int get schemaVersion => 2;
|
||||
int get schemaVersion => 1;
|
||||
|
||||
Future<void> clearDatabase() {
|
||||
return transaction(() async {
|
||||
for (final table in allTables) {
|
||||
await delete(table).go();
|
||||
}
|
||||
});
|
||||
Future<void> clearDatabase() async {
|
||||
final dbPath = await getApplicationSupportDirectory();
|
||||
final dbFile = File(p.join(dbPath.path, '$_databseName.sqlite'));
|
||||
|
||||
if (await dbFile.exists()) {
|
||||
await dbFile.delete(recursive: true);
|
||||
}
|
||||
}
|
||||
|
||||
Selectable<SyncedItem> getItem(String id) =>
|
||||
|
|
@ -69,6 +75,10 @@ class AppDatabase extends _$AppDatabase {
|
|||
..orderBy([(t) => OrderingTerm(expression: t.sortName)]))
|
||||
.map(databaseConverter);
|
||||
|
||||
Selectable<SyncedItem> get getAllItems => ((select(databaseItems)..where((tbl) => tbl.userId.equals(userId)))
|
||||
..orderBy([(t) => OrderingTerm(expression: t.sortName)]))
|
||||
.map(databaseConverter);
|
||||
|
||||
Selectable<SyncedItem> getChildren(String parentId) =>
|
||||
((select(databaseItems)..where((tbl) => (tbl.parentId.equals(parentId) & tbl.userId.equals(userId))))
|
||||
..orderBy([(t) => OrderingTerm(expression: t.sortName)]))
|
||||
|
|
@ -90,11 +100,9 @@ class AppDatabase extends _$AppDatabase {
|
|||
if (itemType == null) return [];
|
||||
|
||||
final int maxDepth = switch (itemType) {
|
||||
FladderItemType.episode => 0,
|
||||
FladderItemType.movie => 0,
|
||||
FladderItemType.season => 1,
|
||||
FladderItemType.series => 2,
|
||||
_ => 1,
|
||||
_ => 0,
|
||||
};
|
||||
|
||||
final all = <SyncedItem>[];
|
||||
|
|
@ -151,6 +159,7 @@ class AppDatabase extends _$AppDatabase {
|
|||
chapters: Value(jsonEncode(item.fChapters.map((e) => e.toJson()).toList())),
|
||||
subtitles: Value(jsonEncode(item.subtitles.map((e) => e.toJson()).toList())),
|
||||
userData: Value(item.userData != null ? jsonEncode(item.userData?.toJson()) : null),
|
||||
unSyncedData: Value(item.unSyncedData),
|
||||
);
|
||||
}
|
||||
|
||||
|
|
@ -176,6 +185,7 @@ class AppDatabase extends _$AppDatabase {
|
|||
? (jsonDecode(dataItem.subtitles!) as List).map((e) => SubStreamModel.fromJson(e)).toList()
|
||||
: [],
|
||||
userData: dataItem.userData != null ? UserData.fromJson(jsonDecode(dataItem.userData!)) : null,
|
||||
unSyncedData: dataItem.unSyncedData,
|
||||
);
|
||||
|
||||
return syncedItem.copyWith(
|
||||
|
|
@ -185,34 +195,11 @@ class AppDatabase extends _$AppDatabase {
|
|||
|
||||
static QueryExecutor _openConnection() {
|
||||
return driftDatabase(
|
||||
name: 'syncedDatabase',
|
||||
name: _databseName,
|
||||
native: const DriftNativeOptions(
|
||||
databaseDirectory: getApplicationSupportDirectory,
|
||||
),
|
||||
// If you need web support, see https://drift.simonbinder.eu/platforms/web/
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
MigrationStrategy get migration {
|
||||
return MigrationStrategy(
|
||||
onCreate: (Migrator m) {
|
||||
return m.createAll();
|
||||
},
|
||||
onUpgrade: (Migrator m, int from, int to) async {
|
||||
if (from == 1) {
|
||||
final allItems = await select(databaseItems).get();
|
||||
m.deleteTable(databaseItems.actualTableName);
|
||||
m.createAll();
|
||||
await batch((batch) {
|
||||
batch.insertAll(
|
||||
databaseItems,
|
||||
allItems,
|
||||
mode: InsertMode.insertOrReplace,
|
||||
);
|
||||
});
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -33,9 +33,10 @@ class $DatabaseItemsTable extends DatabaseItems
|
|||
late final GeneratedColumn<bool> syncing = GeneratedColumn<bool>(
|
||||
'syncing', aliasedName, false,
|
||||
type: DriftSqlType.bool,
|
||||
requiredDuringInsert: true,
|
||||
requiredDuringInsert: false,
|
||||
defaultConstraints:
|
||||
GeneratedColumn.constraintIsAlways('CHECK ("syncing" IN (0, 1))'));
|
||||
GeneratedColumn.constraintIsAlways('CHECK ("syncing" IN (0, 1))'),
|
||||
defaultValue: const Constant(false));
|
||||
static const VerificationMeta _sortNameMeta =
|
||||
const VerificationMeta('sortName');
|
||||
@override
|
||||
|
|
@ -94,6 +95,16 @@ class $DatabaseItemsTable extends DatabaseItems
|
|||
late final GeneratedColumn<String> subtitles = GeneratedColumn<String>(
|
||||
'subtitles', aliasedName, true,
|
||||
type: DriftSqlType.string, requiredDuringInsert: false);
|
||||
static const VerificationMeta _unSyncedDataMeta =
|
||||
const VerificationMeta('unSyncedData');
|
||||
@override
|
||||
late final GeneratedColumn<bool> unSyncedData = GeneratedColumn<bool>(
|
||||
'un_synced_data', aliasedName, false,
|
||||
type: DriftSqlType.bool,
|
||||
requiredDuringInsert: false,
|
||||
defaultConstraints: GeneratedColumn.constraintIsAlways(
|
||||
'CHECK ("un_synced_data" IN (0, 1))'),
|
||||
defaultValue: const Constant(false));
|
||||
static const VerificationMeta _userDataMeta =
|
||||
const VerificationMeta('userData');
|
||||
@override
|
||||
|
|
@ -115,6 +126,7 @@ class $DatabaseItemsTable extends DatabaseItems
|
|||
images,
|
||||
chapters,
|
||||
subtitles,
|
||||
unSyncedData,
|
||||
userData
|
||||
];
|
||||
@override
|
||||
|
|
@ -141,8 +153,6 @@ class $DatabaseItemsTable extends DatabaseItems
|
|||
if (data.containsKey('syncing')) {
|
||||
context.handle(_syncingMeta,
|
||||
syncing.isAcceptableOrUnknown(data['syncing']!, _syncingMeta));
|
||||
} else if (isInserting) {
|
||||
context.missing(_syncingMeta);
|
||||
}
|
||||
if (data.containsKey('sort_name')) {
|
||||
context.handle(_sortNameMeta,
|
||||
|
|
@ -190,6 +200,12 @@ class $DatabaseItemsTable extends DatabaseItems
|
|||
context.handle(_subtitlesMeta,
|
||||
subtitles.isAcceptableOrUnknown(data['subtitles']!, _subtitlesMeta));
|
||||
}
|
||||
if (data.containsKey('un_synced_data')) {
|
||||
context.handle(
|
||||
_unSyncedDataMeta,
|
||||
unSyncedData.isAcceptableOrUnknown(
|
||||
data['un_synced_data']!, _unSyncedDataMeta));
|
||||
}
|
||||
if (data.containsKey('user_data')) {
|
||||
context.handle(_userDataMeta,
|
||||
userData.isAcceptableOrUnknown(data['user_data']!, _userDataMeta));
|
||||
|
|
@ -229,6 +245,8 @@ class $DatabaseItemsTable extends DatabaseItems
|
|||
.read(DriftSqlType.string, data['${effectivePrefix}chapters']),
|
||||
subtitles: attachedDatabase.typeMapping
|
||||
.read(DriftSqlType.string, data['${effectivePrefix}subtitles']),
|
||||
unSyncedData: attachedDatabase.typeMapping
|
||||
.read(DriftSqlType.bool, data['${effectivePrefix}un_synced_data'])!,
|
||||
userData: attachedDatabase.typeMapping
|
||||
.read(DriftSqlType.string, data['${effectivePrefix}user_data']),
|
||||
);
|
||||
|
|
@ -254,6 +272,7 @@ class DatabaseItem extends DataClass implements Insertable<DatabaseItem> {
|
|||
final String? images;
|
||||
final String? chapters;
|
||||
final String? subtitles;
|
||||
final bool unSyncedData;
|
||||
final String? userData;
|
||||
const DatabaseItem(
|
||||
{required this.userId,
|
||||
|
|
@ -269,6 +288,7 @@ class DatabaseItem extends DataClass implements Insertable<DatabaseItem> {
|
|||
this.images,
|
||||
this.chapters,
|
||||
this.subtitles,
|
||||
required this.unSyncedData,
|
||||
this.userData});
|
||||
@override
|
||||
Map<String, Expression> toColumns(bool nullToAbsent) {
|
||||
|
|
@ -306,6 +326,7 @@ class DatabaseItem extends DataClass implements Insertable<DatabaseItem> {
|
|||
if (!nullToAbsent || subtitles != null) {
|
||||
map['subtitles'] = Variable<String>(subtitles);
|
||||
}
|
||||
map['un_synced_data'] = Variable<bool>(unSyncedData);
|
||||
if (!nullToAbsent || userData != null) {
|
||||
map['user_data'] = Variable<String>(userData);
|
||||
}
|
||||
|
|
@ -344,6 +365,7 @@ class DatabaseItem extends DataClass implements Insertable<DatabaseItem> {
|
|||
subtitles: subtitles == null && nullToAbsent
|
||||
? const Value.absent()
|
||||
: Value(subtitles),
|
||||
unSyncedData: Value(unSyncedData),
|
||||
userData: userData == null && nullToAbsent
|
||||
? const Value.absent()
|
||||
: Value(userData),
|
||||
|
|
@ -367,6 +389,7 @@ class DatabaseItem extends DataClass implements Insertable<DatabaseItem> {
|
|||
images: serializer.fromJson<String?>(json['images']),
|
||||
chapters: serializer.fromJson<String?>(json['chapters']),
|
||||
subtitles: serializer.fromJson<String?>(json['subtitles']),
|
||||
unSyncedData: serializer.fromJson<bool>(json['unSyncedData']),
|
||||
userData: serializer.fromJson<String?>(json['userData']),
|
||||
);
|
||||
}
|
||||
|
|
@ -387,6 +410,7 @@ class DatabaseItem extends DataClass implements Insertable<DatabaseItem> {
|
|||
'images': serializer.toJson<String?>(images),
|
||||
'chapters': serializer.toJson<String?>(chapters),
|
||||
'subtitles': serializer.toJson<String?>(subtitles),
|
||||
'unSyncedData': serializer.toJson<bool>(unSyncedData),
|
||||
'userData': serializer.toJson<String?>(userData),
|
||||
};
|
||||
}
|
||||
|
|
@ -405,6 +429,7 @@ class DatabaseItem extends DataClass implements Insertable<DatabaseItem> {
|
|||
Value<String?> images = const Value.absent(),
|
||||
Value<String?> chapters = const Value.absent(),
|
||||
Value<String?> subtitles = const Value.absent(),
|
||||
bool? unSyncedData,
|
||||
Value<String?> userData = const Value.absent()}) =>
|
||||
DatabaseItem(
|
||||
userId: userId ?? this.userId,
|
||||
|
|
@ -423,6 +448,7 @@ class DatabaseItem extends DataClass implements Insertable<DatabaseItem> {
|
|||
images: images.present ? images.value : this.images,
|
||||
chapters: chapters.present ? chapters.value : this.chapters,
|
||||
subtitles: subtitles.present ? subtitles.value : this.subtitles,
|
||||
unSyncedData: unSyncedData ?? this.unSyncedData,
|
||||
userData: userData.present ? userData.value : this.userData,
|
||||
);
|
||||
DatabaseItem copyWithCompanion(DatabaseItemsCompanion data) {
|
||||
|
|
@ -446,6 +472,9 @@ class DatabaseItem extends DataClass implements Insertable<DatabaseItem> {
|
|||
images: data.images.present ? data.images.value : this.images,
|
||||
chapters: data.chapters.present ? data.chapters.value : this.chapters,
|
||||
subtitles: data.subtitles.present ? data.subtitles.value : this.subtitles,
|
||||
unSyncedData: data.unSyncedData.present
|
||||
? data.unSyncedData.value
|
||||
: this.unSyncedData,
|
||||
userData: data.userData.present ? data.userData.value : this.userData,
|
||||
);
|
||||
}
|
||||
|
|
@ -466,6 +495,7 @@ class DatabaseItem extends DataClass implements Insertable<DatabaseItem> {
|
|||
..write('images: $images, ')
|
||||
..write('chapters: $chapters, ')
|
||||
..write('subtitles: $subtitles, ')
|
||||
..write('unSyncedData: $unSyncedData, ')
|
||||
..write('userData: $userData')
|
||||
..write(')'))
|
||||
.toString();
|
||||
|
|
@ -486,6 +516,7 @@ class DatabaseItem extends DataClass implements Insertable<DatabaseItem> {
|
|||
images,
|
||||
chapters,
|
||||
subtitles,
|
||||
unSyncedData,
|
||||
userData);
|
||||
@override
|
||||
bool operator ==(Object other) =>
|
||||
|
|
@ -504,6 +535,7 @@ class DatabaseItem extends DataClass implements Insertable<DatabaseItem> {
|
|||
other.images == this.images &&
|
||||
other.chapters == this.chapters &&
|
||||
other.subtitles == this.subtitles &&
|
||||
other.unSyncedData == this.unSyncedData &&
|
||||
other.userData == this.userData);
|
||||
}
|
||||
|
||||
|
|
@ -521,6 +553,7 @@ class DatabaseItemsCompanion extends UpdateCompanion<DatabaseItem> {
|
|||
final Value<String?> images;
|
||||
final Value<String?> chapters;
|
||||
final Value<String?> subtitles;
|
||||
final Value<bool> unSyncedData;
|
||||
final Value<String?> userData;
|
||||
final Value<int> rowid;
|
||||
const DatabaseItemsCompanion({
|
||||
|
|
@ -537,13 +570,14 @@ class DatabaseItemsCompanion extends UpdateCompanion<DatabaseItem> {
|
|||
this.images = const Value.absent(),
|
||||
this.chapters = const Value.absent(),
|
||||
this.subtitles = const Value.absent(),
|
||||
this.unSyncedData = const Value.absent(),
|
||||
this.userData = const Value.absent(),
|
||||
this.rowid = const Value.absent(),
|
||||
});
|
||||
DatabaseItemsCompanion.insert({
|
||||
required String userId,
|
||||
required String id,
|
||||
required bool syncing,
|
||||
this.syncing = const Value.absent(),
|
||||
this.sortName = const Value.absent(),
|
||||
this.parentId = const Value.absent(),
|
||||
this.path = const Value.absent(),
|
||||
|
|
@ -554,11 +588,11 @@ class DatabaseItemsCompanion extends UpdateCompanion<DatabaseItem> {
|
|||
this.images = const Value.absent(),
|
||||
this.chapters = const Value.absent(),
|
||||
this.subtitles = const Value.absent(),
|
||||
this.unSyncedData = const Value.absent(),
|
||||
this.userData = const Value.absent(),
|
||||
this.rowid = const Value.absent(),
|
||||
}) : userId = Value(userId),
|
||||
id = Value(id),
|
||||
syncing = Value(syncing);
|
||||
id = Value(id);
|
||||
static Insertable<DatabaseItem> custom({
|
||||
Expression<String>? userId,
|
||||
Expression<String>? id,
|
||||
|
|
@ -573,6 +607,7 @@ class DatabaseItemsCompanion extends UpdateCompanion<DatabaseItem> {
|
|||
Expression<String>? images,
|
||||
Expression<String>? chapters,
|
||||
Expression<String>? subtitles,
|
||||
Expression<bool>? unSyncedData,
|
||||
Expression<String>? userData,
|
||||
Expression<int>? rowid,
|
||||
}) {
|
||||
|
|
@ -590,6 +625,7 @@ class DatabaseItemsCompanion extends UpdateCompanion<DatabaseItem> {
|
|||
if (images != null) 'images': images,
|
||||
if (chapters != null) 'chapters': chapters,
|
||||
if (subtitles != null) 'subtitles': subtitles,
|
||||
if (unSyncedData != null) 'un_synced_data': unSyncedData,
|
||||
if (userData != null) 'user_data': userData,
|
||||
if (rowid != null) 'rowid': rowid,
|
||||
});
|
||||
|
|
@ -609,6 +645,7 @@ class DatabaseItemsCompanion extends UpdateCompanion<DatabaseItem> {
|
|||
Value<String?>? images,
|
||||
Value<String?>? chapters,
|
||||
Value<String?>? subtitles,
|
||||
Value<bool>? unSyncedData,
|
||||
Value<String?>? userData,
|
||||
Value<int>? rowid}) {
|
||||
return DatabaseItemsCompanion(
|
||||
|
|
@ -625,6 +662,7 @@ class DatabaseItemsCompanion extends UpdateCompanion<DatabaseItem> {
|
|||
images: images ?? this.images,
|
||||
chapters: chapters ?? this.chapters,
|
||||
subtitles: subtitles ?? this.subtitles,
|
||||
unSyncedData: unSyncedData ?? this.unSyncedData,
|
||||
userData: userData ?? this.userData,
|
||||
rowid: rowid ?? this.rowid,
|
||||
);
|
||||
|
|
@ -672,6 +710,9 @@ class DatabaseItemsCompanion extends UpdateCompanion<DatabaseItem> {
|
|||
if (subtitles.present) {
|
||||
map['subtitles'] = Variable<String>(subtitles.value);
|
||||
}
|
||||
if (unSyncedData.present) {
|
||||
map['un_synced_data'] = Variable<bool>(unSyncedData.value);
|
||||
}
|
||||
if (userData.present) {
|
||||
map['user_data'] = Variable<String>(userData.value);
|
||||
}
|
||||
|
|
@ -697,6 +738,7 @@ class DatabaseItemsCompanion extends UpdateCompanion<DatabaseItem> {
|
|||
..write('images: $images, ')
|
||||
..write('chapters: $chapters, ')
|
||||
..write('subtitles: $subtitles, ')
|
||||
..write('unSyncedData: $unSyncedData, ')
|
||||
..write('userData: $userData, ')
|
||||
..write('rowid: $rowid')
|
||||
..write(')'))
|
||||
|
|
@ -722,7 +764,7 @@ typedef $$DatabaseItemsTableCreateCompanionBuilder = DatabaseItemsCompanion
|
|||
Function({
|
||||
required String userId,
|
||||
required String id,
|
||||
required bool syncing,
|
||||
Value<bool> syncing,
|
||||
Value<String?> sortName,
|
||||
Value<String?> parentId,
|
||||
Value<String?> path,
|
||||
|
|
@ -733,6 +775,7 @@ typedef $$DatabaseItemsTableCreateCompanionBuilder = DatabaseItemsCompanion
|
|||
Value<String?> images,
|
||||
Value<String?> chapters,
|
||||
Value<String?> subtitles,
|
||||
Value<bool> unSyncedData,
|
||||
Value<String?> userData,
|
||||
Value<int> rowid,
|
||||
});
|
||||
|
|
@ -751,6 +794,7 @@ typedef $$DatabaseItemsTableUpdateCompanionBuilder = DatabaseItemsCompanion
|
|||
Value<String?> images,
|
||||
Value<String?> chapters,
|
||||
Value<String?> subtitles,
|
||||
Value<bool> unSyncedData,
|
||||
Value<String?> userData,
|
||||
Value<int> rowid,
|
||||
});
|
||||
|
|
@ -804,6 +848,9 @@ class $$DatabaseItemsTableFilterComposer
|
|||
ColumnFilters<String> get subtitles => $composableBuilder(
|
||||
column: $table.subtitles, builder: (column) => ColumnFilters(column));
|
||||
|
||||
ColumnFilters<bool> get unSyncedData => $composableBuilder(
|
||||
column: $table.unSyncedData, builder: (column) => ColumnFilters(column));
|
||||
|
||||
ColumnFilters<String> get userData => $composableBuilder(
|
||||
column: $table.userData, builder: (column) => ColumnFilters(column));
|
||||
}
|
||||
|
|
@ -859,6 +906,10 @@ class $$DatabaseItemsTableOrderingComposer
|
|||
ColumnOrderings<String> get subtitles => $composableBuilder(
|
||||
column: $table.subtitles, builder: (column) => ColumnOrderings(column));
|
||||
|
||||
ColumnOrderings<bool> get unSyncedData => $composableBuilder(
|
||||
column: $table.unSyncedData,
|
||||
builder: (column) => ColumnOrderings(column));
|
||||
|
||||
ColumnOrderings<String> get userData => $composableBuilder(
|
||||
column: $table.userData, builder: (column) => ColumnOrderings(column));
|
||||
}
|
||||
|
|
@ -911,6 +962,9 @@ class $$DatabaseItemsTableAnnotationComposer
|
|||
GeneratedColumn<String> get subtitles =>
|
||||
$composableBuilder(column: $table.subtitles, builder: (column) => column);
|
||||
|
||||
GeneratedColumn<bool> get unSyncedData => $composableBuilder(
|
||||
column: $table.unSyncedData, builder: (column) => column);
|
||||
|
||||
GeneratedColumn<String> get userData =>
|
||||
$composableBuilder(column: $table.userData, builder: (column) => column);
|
||||
}
|
||||
|
|
@ -954,6 +1008,7 @@ class $$DatabaseItemsTableTableManager extends RootTableManager<
|
|||
Value<String?> images = const Value.absent(),
|
||||
Value<String?> chapters = const Value.absent(),
|
||||
Value<String?> subtitles = const Value.absent(),
|
||||
Value<bool> unSyncedData = const Value.absent(),
|
||||
Value<String?> userData = const Value.absent(),
|
||||
Value<int> rowid = const Value.absent(),
|
||||
}) =>
|
||||
|
|
@ -971,13 +1026,14 @@ class $$DatabaseItemsTableTableManager extends RootTableManager<
|
|||
images: images,
|
||||
chapters: chapters,
|
||||
subtitles: subtitles,
|
||||
unSyncedData: unSyncedData,
|
||||
userData: userData,
|
||||
rowid: rowid,
|
||||
),
|
||||
createCompanionCallback: ({
|
||||
required String userId,
|
||||
required String id,
|
||||
required bool syncing,
|
||||
Value<bool> syncing = const Value.absent(),
|
||||
Value<String?> sortName = const Value.absent(),
|
||||
Value<String?> parentId = const Value.absent(),
|
||||
Value<String?> path = const Value.absent(),
|
||||
|
|
@ -988,6 +1044,7 @@ class $$DatabaseItemsTableTableManager extends RootTableManager<
|
|||
Value<String?> images = const Value.absent(),
|
||||
Value<String?> chapters = const Value.absent(),
|
||||
Value<String?> subtitles = const Value.absent(),
|
||||
Value<bool> unSyncedData = const Value.absent(),
|
||||
Value<String?> userData = const Value.absent(),
|
||||
Value<int> rowid = const Value.absent(),
|
||||
}) =>
|
||||
|
|
@ -1005,6 +1062,7 @@ class $$DatabaseItemsTableTableManager extends RootTableManager<
|
|||
images: images,
|
||||
chapters: chapters,
|
||||
subtitles: subtitles,
|
||||
unSyncedData: unSyncedData,
|
||||
userData: userData,
|
||||
rowid: rowid,
|
||||
),
|
||||
|
|
|
|||
|
|
@ -18,7 +18,6 @@ import 'package:fladder/models/items/media_segments_model.dart';
|
|||
import 'package:fladder/models/items/media_streams_model.dart';
|
||||
import 'package:fladder/models/items/trick_play_model.dart';
|
||||
import 'package:fladder/models/syncing/i_synced_item.dart';
|
||||
import 'package:fladder/providers/sync_provider.dart';
|
||||
import 'package:fladder/util/localization_helper.dart';
|
||||
|
||||
part 'sync_item.freezed.dart';
|
||||
|
|
@ -42,6 +41,7 @@ class SyncedItem with _$SyncedItem {
|
|||
ImagesData? fImages,
|
||||
@Default([]) List<Chapter> fChapters,
|
||||
@Default([]) List<SubStreamModel> subtitles,
|
||||
@Default(false) bool unSyncedData,
|
||||
@UserDataJsonSerializer() UserData? userData,
|
||||
// ignore: invalid_annotation_target
|
||||
@JsonKey(includeFromJson: false, includeToJson: false) ItemBaseModel? itemModel,
|
||||
|
|
@ -67,6 +67,13 @@ class SyncedItem with _$SyncedItem {
|
|||
[]);
|
||||
|
||||
File get dataFile => File(joinAll(["$path", "data.json"]));
|
||||
BaseItemDto? get data {
|
||||
return dataFile.existsSync()
|
||||
? BaseItemDto.fromJson(jsonDecode(dataFile.readAsStringSync()))
|
||||
.copyWith(userData: UserData.toDto(userData), path: videoFile.existsSync() ? videoFile.path : '')
|
||||
: null;
|
||||
}
|
||||
|
||||
Directory get trickPlayDirectory => Directory(joinAll(["$path", trickPlayPath]));
|
||||
File get videoFile => File(joinAll(["$path", "$videoFileName"]));
|
||||
Directory get directory => Directory(path ?? "");
|
||||
|
|
@ -104,10 +111,6 @@ class SyncedItem with _$SyncedItem {
|
|||
return true;
|
||||
}
|
||||
|
||||
Future<List<SyncedItem>> getChildren(Ref ref) async => await ref.read(syncProvider.notifier).getChildren(this);
|
||||
Future<List<SyncedItem>> getNestedChildren(Ref ref) async =>
|
||||
await ref.read(syncProvider.notifier).getNestedChildren(this);
|
||||
|
||||
Future<int> get getDirSize async {
|
||||
var files = await directory.list(recursive: true).toList();
|
||||
var dirSize = files.fold(0, (int sum, file) => sum + file.statSync().size);
|
||||
|
|
|
|||
|
|
@ -30,6 +30,7 @@ mixin _$SyncedItem {
|
|||
ImagesData? get fImages => throw _privateConstructorUsedError;
|
||||
List<Chapter> get fChapters => throw _privateConstructorUsedError;
|
||||
List<SubStreamModel> get subtitles => throw _privateConstructorUsedError;
|
||||
bool get unSyncedData => throw _privateConstructorUsedError;
|
||||
@UserDataJsonSerializer()
|
||||
UserData? get userData =>
|
||||
throw _privateConstructorUsedError; // ignore: invalid_annotation_target
|
||||
|
|
@ -64,6 +65,7 @@ abstract class $SyncedItemCopyWith<$Res> {
|
|||
ImagesData? fImages,
|
||||
List<Chapter> fChapters,
|
||||
List<SubStreamModel> subtitles,
|
||||
bool unSyncedData,
|
||||
@UserDataJsonSerializer() UserData? userData,
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
ItemBaseModel? itemModel});
|
||||
|
|
@ -100,6 +102,7 @@ class _$SyncedItemCopyWithImpl<$Res, $Val extends SyncedItem>
|
|||
Object? fImages = freezed,
|
||||
Object? fChapters = null,
|
||||
Object? subtitles = null,
|
||||
Object? unSyncedData = null,
|
||||
Object? userData = freezed,
|
||||
Object? itemModel = freezed,
|
||||
}) {
|
||||
|
|
@ -160,6 +163,10 @@ class _$SyncedItemCopyWithImpl<$Res, $Val extends SyncedItem>
|
|||
? _value.subtitles
|
||||
: subtitles // ignore: cast_nullable_to_non_nullable
|
||||
as List<SubStreamModel>,
|
||||
unSyncedData: null == unSyncedData
|
||||
? _value.unSyncedData
|
||||
: unSyncedData // ignore: cast_nullable_to_non_nullable
|
||||
as bool,
|
||||
userData: freezed == userData
|
||||
? _value.userData
|
||||
: userData // ignore: cast_nullable_to_non_nullable
|
||||
|
|
@ -209,6 +216,7 @@ abstract class _$$SyncItemImplCopyWith<$Res>
|
|||
ImagesData? fImages,
|
||||
List<Chapter> fChapters,
|
||||
List<SubStreamModel> subtitles,
|
||||
bool unSyncedData,
|
||||
@UserDataJsonSerializer() UserData? userData,
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
ItemBaseModel? itemModel});
|
||||
|
|
@ -244,6 +252,7 @@ class __$$SyncItemImplCopyWithImpl<$Res>
|
|||
Object? fImages = freezed,
|
||||
Object? fChapters = null,
|
||||
Object? subtitles = null,
|
||||
Object? unSyncedData = null,
|
||||
Object? userData = freezed,
|
||||
Object? itemModel = freezed,
|
||||
}) {
|
||||
|
|
@ -304,6 +313,10 @@ class __$$SyncItemImplCopyWithImpl<$Res>
|
|||
? _value._subtitles
|
||||
: subtitles // ignore: cast_nullable_to_non_nullable
|
||||
as List<SubStreamModel>,
|
||||
unSyncedData: null == unSyncedData
|
||||
? _value.unSyncedData
|
||||
: unSyncedData // ignore: cast_nullable_to_non_nullable
|
||||
as bool,
|
||||
userData: freezed == userData
|
||||
? _value.userData
|
||||
: userData // ignore: cast_nullable_to_non_nullable
|
||||
|
|
@ -334,6 +347,7 @@ class _$SyncItemImpl extends _SyncItem {
|
|||
this.fImages,
|
||||
final List<Chapter> fChapters = const [],
|
||||
final List<SubStreamModel> subtitles = const [],
|
||||
this.unSyncedData = false,
|
||||
@UserDataJsonSerializer() this.userData,
|
||||
@JsonKey(includeFromJson: false, includeToJson: false) this.itemModel})
|
||||
: _fChapters = fChapters,
|
||||
|
|
@ -384,6 +398,9 @@ class _$SyncItemImpl extends _SyncItem {
|
|||
return EqualUnmodifiableListView(_subtitles);
|
||||
}
|
||||
|
||||
@override
|
||||
@JsonKey()
|
||||
final bool unSyncedData;
|
||||
@override
|
||||
@UserDataJsonSerializer()
|
||||
final UserData? userData;
|
||||
|
|
@ -394,7 +411,7 @@ class _$SyncItemImpl extends _SyncItem {
|
|||
|
||||
@override
|
||||
String toString() {
|
||||
return 'SyncedItem(id: $id, syncing: $syncing, parentId: $parentId, userId: $userId, path: $path, markedForDelete: $markedForDelete, sortName: $sortName, fileSize: $fileSize, videoFileName: $videoFileName, mediaSegments: $mediaSegments, fTrickPlayModel: $fTrickPlayModel, fImages: $fImages, fChapters: $fChapters, subtitles: $subtitles, userData: $userData, itemModel: $itemModel)';
|
||||
return 'SyncedItem(id: $id, syncing: $syncing, parentId: $parentId, userId: $userId, path: $path, markedForDelete: $markedForDelete, sortName: $sortName, fileSize: $fileSize, videoFileName: $videoFileName, mediaSegments: $mediaSegments, fTrickPlayModel: $fTrickPlayModel, fImages: $fImages, fChapters: $fChapters, subtitles: $subtitles, unSyncedData: $unSyncedData, userData: $userData, itemModel: $itemModel)';
|
||||
}
|
||||
|
||||
/// Create a copy of SyncedItem
|
||||
|
|
@ -422,6 +439,7 @@ abstract class _SyncItem extends SyncedItem {
|
|||
final ImagesData? fImages,
|
||||
final List<Chapter> fChapters,
|
||||
final List<SubStreamModel> subtitles,
|
||||
final bool unSyncedData,
|
||||
@UserDataJsonSerializer() final UserData? userData,
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
final ItemBaseModel? itemModel}) = _$SyncItemImpl;
|
||||
|
|
@ -456,6 +474,8 @@ abstract class _SyncItem extends SyncedItem {
|
|||
@override
|
||||
List<SubStreamModel> get subtitles;
|
||||
@override
|
||||
bool get unSyncedData;
|
||||
@override
|
||||
@UserDataJsonSerializer()
|
||||
UserData? get userData; // ignore: invalid_annotation_target
|
||||
@override
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue