diff --git a/lib/app/states/app.dart b/lib/app/states/app.dart index 17a7a020..ad202f28 100644 --- a/lib/app/states/app.dart +++ b/lib/app/states/app.dart @@ -1,15 +1,15 @@ import 'dart:async'; import 'package:bloc/bloc.dart'; +import 'package:freezed_annotation/freezed_annotation.dart'; import 'package:invidious/router.dart'; import 'package:logging/logging.dart'; import 'package:receive_sharing_intent/receive_sharing_intent.dart'; -import '../../database.dart'; import '../../globals.dart'; import '../../home/models/db/home_layout.dart'; import '../../settings/models/db/server.dart'; -import 'package:freezed_annotation/freezed_annotation.dart'; +import '../../settings/models/db/settings.dart'; part 'app.freezed.dart'; @@ -19,9 +19,30 @@ class AppCubit extends Cubit { late final StreamSubscription intentDataStreamSubscription; AppCubit(super.initialState) { onReady(); + initState(); + } + + initState() async { + Server? server; + try { + server = await db.getCurrentlySelectedServer(); + } catch (e) { + server = null; + } + HomeLayout homeLayout = db.getHomeLayout(); + bool isLoggedIn = (server?.authToken?.isNotEmpty ?? false) || + (server?.sidCookie?.isNotEmpty ?? false); + + var selectedIndex = + int.parse(db.getSettings(onOpenSettingName)?.value ?? '0'); + if (!isLoggedIn && selectedIndex > 1 || selectedIndex < 0) { + selectedIndex = 0; + } + emit(state.copyWith( + selectedIndex: selectedIndex, homeLayout: homeLayout, server: server)); } - onReady() { + onReady() async { intentDataStreamSubscription = ReceiveSharingIntent.getTextStream().listen((String value) { openAppLink(value); @@ -87,26 +108,6 @@ class AppCubit extends Cubit { @freezed class AppState with _$AppState { - static AppState init() { - late Server? server; - try { - server = db.getCurrentlySelectedServer(); - } catch (e) { - server = null; - } - HomeLayout homeLayout = db.getHomeLayout(); - bool isLoggedIn = (server?.authToken?.isNotEmpty ?? false) || - (server?.sidCookie?.isNotEmpty ?? false); - - var selectedIndex = - int.parse(db.getSettings(onOpenSettingName)?.value ?? '0'); - if (!isLoggedIn && selectedIndex > 1 || selectedIndex < 0) { - selectedIndex = 0; - } - - return AppState(selectedIndex, server, homeLayout); - } - - factory AppState(int selectedIndex, Server? server, HomeLayout homeLayout) = - _AppState; + const factory AppState( + int selectedIndex, Server? server, HomeLayout homeLayout) = _AppState; } diff --git a/lib/app/states/app.freezed.dart b/lib/app/states/app.freezed.dart index da7dbef3..1408e968 100644 --- a/lib/app/states/app.freezed.dart +++ b/lib/app/states/app.freezed.dart @@ -113,7 +113,7 @@ class __$$AppStateImplCopyWithImpl<$Res> /// @nodoc class _$AppStateImpl implements _AppState { - _$AppStateImpl(this.selectedIndex, this.server, this.homeLayout); + const _$AppStateImpl(this.selectedIndex, this.server, this.homeLayout); @override final int selectedIndex; @@ -151,7 +151,7 @@ class _$AppStateImpl implements _AppState { } abstract class _AppState implements AppState { - factory _AppState(final int selectedIndex, final Server? server, + const factory _AppState(final int selectedIndex, final Server? server, final HomeLayout homeLayout) = _$AppStateImpl; @override diff --git a/lib/app/views/tv/screens/tv_home.dart b/lib/app/views/tv/screens/tv_home.dart index 3e31b7d3..1d387e68 100644 --- a/lib/app/views/tv/screens/tv_home.dart +++ b/lib/app/views/tv/screens/tv_home.dart @@ -79,8 +79,10 @@ class TvHomeScreen extends StatelessWidget { var homeCubit = context.read(); var appLayout = context.select((SettingsCubit value) => value.state.appLayout); + var isLoggedIn = + context.select((AppCubit value) => value.isLoggedIn); var allowedPages = appLayout - .where((element) => element.isPermitted(context)) + .where((element) => element.isPermitted(context, isLoggedIn)) .toList(); return BlocBuilder( buildWhen: (previous, current) { diff --git a/lib/database.dart b/lib/database.dart deleted file mode 100644 index e620b4b8..00000000 --- a/lib/database.dart +++ /dev/null @@ -1,400 +0,0 @@ -import 'dart:io'; - -import 'package:invidious/home/models/db/home_layout.dart'; -import 'package:invidious/notifications/models/db/channel_notifications.dart'; -import 'package:invidious/notifications/models/db/subscription_notifications.dart'; -import 'package:invidious/search/models/db/search_history_item.dart'; -import 'package:invidious/settings/models/db/settings.dart'; -import 'package:invidious/settings/states/settings.dart'; -import 'package:invidious/utils/interfaces/db.dart'; -import 'package:invidious/videos/models/db/dearrow_cache.dart'; -import 'package:invidious/videos/models/db/history_video_cache.dart'; -import 'package:invidious/videos/models/db/progress.dart'; -import 'package:logging/logging.dart'; -import 'package:path/path.dart' as p; -import 'package:path_provider/path_provider.dart'; - -import 'downloads/models/downloaded_video.dart'; -import 'notifications/models/db/playlist_notifications.dart'; -import 'objectbox.g.dart'; // created by `flutter pub run build_runner build` -import 'settings/models/db/app_logs.dart'; -import 'settings/models/db/server.dart'; -import 'settings/models/db/video_filter.dart'; - -const selectedServer = 'selected-server'; -const useSponsorBlock = 'use-sponsor-block'; -const sponsorBlockPrefix = 'sponsor-block-'; -const browsingCountry = 'browsing-country'; -const dynamicTheme = 'dynamic-theme'; -const useDashSettingName = 'use-dash'; -const playerRepeat = 'player-repeat'; -const playerShuffle = 'player-shuffle'; -const playerAutoplayOnLoad = 'player-autoplay-on-load'; -const playRecommendedNextSettingName = 'play-recommended-next'; -const useProxySettingName = 'use-proxy'; -const useReturnYoutubeDislikeSettingName = 'use-return-youtube-dislike'; -const blackBackgroundSettingName = 'black-background'; -const subtitleSizeSettingName = 'subtitles-size'; -const rememberLastSubtitle = 'remember-last-subtitle'; -const lastSubtitle = 'last-subtitle'; -const skipSslVerificationSettingName = 'skip-ssl-verification'; -const themeModeSettingName = 'theme-mode'; -const localeSettingName = 'locale'; -const useSearchHistorySettingName = 'use-search-history'; -const searchHistoryLimitSettingName = 'search-history-limit'; -const hideFilteredVideo = 'hide-filtered-videos'; -const remeberPlaybackSpeed = 'remember-playback-speed'; -const lastSpeedSettingName = 'last-speed'; -const lockOrientationFullScreen = 'lock-orientation-fullscreen'; -const skipStepSettingName = 'skip-step'; -const skipExponentialSettingName = 'skip-exponentially'; -const fillFullScreen = 'fill-fullscreen'; -const appLayoutSettingName = 'app-layout'; -const navigationBarLabelBehaviorSettingName = 'navigation-bar-label-behavior'; -const distractionFreeModeSettingName = 'distraction-free-mode'; -const backgroundNotificationsSettingName = 'background-notifications'; -const subscriptionNotifications = 'subscriptions-notifications'; -const backgroundCheckFrequency = "background-check-frequency"; -const subtitleBackground = 'subtitle-background'; -const dearrowSettingName = 'dearrow'; -const dearrowThumbnailsSettingName = "dearrow-thumbnails"; -const fullScreenOnLandscapeSettingName = "fullscreen-on-landscape"; - -const onOpenSettingName = "on-open"; - -const maxLogs = 1000; - -class DbClient extends IDbClient { - /// The Store of this app. - late final Store store; - final log = Logger('DbClient'); - - DbClient._create(this.store); - - /// Create an instance of ObjectBox to use throughout the app. - static Future create() async { - late final Directory docsDir; - try { - docsDir = await getApplicationDocumentsDirectory(); - } catch (e) { - docsDir = Directory.current; - } - // Future openStore() {...} is defined in the generated objectbox.g.dart - var dbPath = p.join(docsDir.path, "impuc-data"); - Store? store; - if (Store.isOpen(dbPath)) { - store = Store.attach(getObjectBoxModel(), dbPath); - } else { - store = await openStore(directory: dbPath); - } - return DbClient._create(store); - } - - bool get isClosed => store.isClosed(); - - @override - close() { - store.close(); - } - - @override - Server? getServer(String url) { - return store - .box() - .query(Server_.url.equals(url)) - .build() - .findFirst(); - } - - @override - upsertServer(Server server) { - // if we only have one server, we select it - store.box().put(server); - super.upsertServer(server); - } - - @override - List getServers() { - return store.box().getAll(); - } - - @override - deleteServerById(int id) { - store.box().remove(id); - } - - @override - saveSetting(SettingsValue setting) { - store.box().put(setting, mode: PutMode.put); - } - - @override - List getAllSettings() { - return store.box().getAll(); - } - - @override - deleteSetting(String name) { - SettingsValue? settings = getSettings(name); - if (settings != null) { - store.box().remove(settings.id); - } - } - - @override - SettingsValue? getSettings(String name) { - return store - .box() - .query(SettingsValue_.name.equals(name)) - .build() - .findFirst(); - } - - @override - double getVideoProgress(String videoId) { - return store - .box() - .query(Progress_.videoId.equals(videoId)) - .build() - .findFirst() - ?.progress ?? - 0; - } - - @override - saveProgress(Progress progress) { - store.box().put(progress); - } - - @override - void useServer(Server server) { - List servers = getServers(); - for (Server s in servers) { - s.inUse = false; - } - - store.box().putMany(servers); - - server.inUse = true; - - store.box().put(server); - } - - @override - List getSearchHistory() { - return _getSearchHistory().map((e) => e.search).toList(); - } - - List _getSearchHistory() { - return (store.box().query() - ..order(SearchHistoryItem_.time, flags: Order.descending)) - .build() - .find(); - } - - @override - void addToSearchHistory(SearchHistoryItem searchHistoryItem) { - store.box().put(searchHistoryItem); - clearExcessSearchHistory(); - } - - @override - void clearExcessSearchHistory() { - final limit = int.parse(getSettings(searchHistoryLimitSettingName)?.value ?? - searchHistoryDefaultLength); - if (store.box().count() > limit) { - store.box().removeMany( - _getSearchHistory().skip(limit).map((e) => e.id).toList()); - } - } - - @override - void clearSearchHistory() { - store.box().removeAll(); - } - - @override - void insertLogs(AppLog log) { - store.box().put(log); - super.insertLogs(log); - } - - @override - void cleanOldLogs() { - var all = store.box().getAll(); - - List ids = all.reversed.skip(maxLogs).map((e) => e.id).toList(); - store.box().removeMany(ids); - log.fine("clearing ${ids.length} logs out of ${all.length}"); - } - - @override - List getAppLogs() { - return store.box().getAll(); - } - - @override - List getAllFilters() { - return store.box().getAll(); - } - - @override - void saveFilter(VideoFilter filter) { - store.box().put(filter); - } - - @override - void deleteFilter(VideoFilter filter) { - store.box().remove(filter.id); - } - - @override - List getAllDownloads() { - return store.box().getAll(); - } - - @override - void upsertDownload(DownloadedVideo vid) { - store.box().put(vid); - } - - @override - void deleteDownload(DownloadedVideo vid) { - store.box().remove(vid.id); - } - - @override - DownloadedVideo? getDownloadById(int id) { - return store.box().get(id); - } - - @override - DownloadedVideo? getDownloadByVideoId(String id) { - return store - .box() - .query(DownloadedVideo_.videoId.equals(id)) - .build() - .findFirst(); - } - - @override - HistoryVideoCache? getHistoryVideoByVideoId(String videoId) { - return store - .box() - .query(HistoryVideoCache_.videoId.equals(videoId)) - .build() - .findFirst(); - } - - @override - void upsertHistoryVideo(HistoryVideoCache vid) { - store.box().put(vid); - } - - // we only want one layout - @override - void upsertHomeLayout(HomeLayout layout) { - store.box().removeAll(); - store.box().put(layout); - } - - @override - HomeLayout getHomeLayout() { - var all = store.box().getAll(); - return all.firstOrNull ?? HomeLayout(); - } - - @override - SubscriptionNotification? getLastSubscriptionNotification() { - return store.box().getAll().lastOrNull; - } - - @override - void setLastSubscriptionNotification(SubscriptionNotification sub) { - store.box().removeAll(); - store.box().put(sub); - } - - @override - ChannelNotification? getChannelNotification(String channelId) { - return store - .box() - .query(ChannelNotification_.channelId.equals(channelId)) - .build() - .findFirst(); - } - - @override - List getAllChannelNotifications() { - return store.box().getAll(); - } - - @override - void deleteChannelNotification(ChannelNotification notif) { - store.box().remove(notif.id); - } - - @override - void upsertChannelNotification(ChannelNotification notif) { - store.box().put(notif); - } - - @override - void setChannelNotificationLastViewedVideo(String channelId, String videoId) { - var notif = getChannelNotification(channelId); - if (notif != null) { - notif.lastSeenVideoId = videoId; - notif.timestamp = DateTime.now().millisecondsSinceEpoch; - upsertChannelNotification(notif); - } - } - - @override - PlaylistNotification? getPlaylistNotification(String channelId) { - return store - .box() - .query(PlaylistNotification_.playlistId.equals(channelId)) - .build() - .findFirst(); - } - - @override - List getAllPlaylistNotifications() { - return store.box().getAll(); - } - - @override - void deletePlaylistNotification(PlaylistNotification notif) { - store.box().remove(notif.id); - } - - @override - void upsertPlaylistNotification(PlaylistNotification notif) { - store.box().put(notif); - } - - @override - void setPlaylistNotificationLastViewedVideo( - String playlistId, int videoCount) { - var notif = getPlaylistNotification(playlistId); - if (notif != null) { - notif.lastVideoCount = videoCount; - notif.timestamp = DateTime.now().millisecondsSinceEpoch; - upsertPlaylistNotification(notif); - } - } - - @override - DeArrowCache? getDeArrowCache(String videoId) { - return store - .box() - .query(DeArrowCache_.videoId.equals(videoId)) - .build() - .findFirst(); - } - - @override - void upsertDeArrowCache(DeArrowCache cache) { - store.box().put(cache); - } -} diff --git a/lib/db_reset/reset_utils.dart b/lib/db_reset/reset_utils.dart new file mode 100644 index 00000000..d54e2bf5 --- /dev/null +++ b/lib/db_reset/reset_utils.dart @@ -0,0 +1,15 @@ +import 'package:logging/logging.dart'; +import 'package:path_provider/path_provider.dart'; + +final _logs = Logger('Migration utils'); + +Future needsReset() async { + var dir = await getApplicationDocumentsDirectory(); + var files = dir.listSync(); + var count = + files.where((element) => element.path.contains("impuc-data")).length; + _logs.fine('Found legacy folder, we need to reset the app'); + return count > 0; +} + +Future clearIsar() async {} diff --git a/lib/db_reset/states/reset.dart b/lib/db_reset/states/reset.dart new file mode 100644 index 00000000..c6df07db --- /dev/null +++ b/lib/db_reset/states/reset.dart @@ -0,0 +1,51 @@ +import 'dart:io'; + +import 'package:back_button_interceptor/back_button_interceptor.dart'; +import 'package:bloc/bloc.dart'; +import 'package:freezed_annotation/freezed_annotation.dart'; +import 'package:logging/logging.dart'; +import 'package:path_provider/path_provider.dart'; + +import '../../settings/models/db/video_filter.dart'; + +part 'reset.freezed.dart'; + +final _log = Logger('DbResetCubit'); + +class DbResetCubit extends Cubit { + DbResetCubit(super.initialState) { + init(); + } + + init() { + // we don't want the user to be able to exit while the DB is migrating + BackButtonInterceptor.add((stopDefaultButtonEvent, RouteInfo routeInfo) { + return true; + }, name: 'migrationInterceptor', zIndex: 0, ifNotYetIntercepted: true); + } + + resetDb() async { + try { + emit(state.copyWith(loading: true)); + + _log.fine('Starting to delete everything'); + + // deleting everything from current app directory + var appDir = await getApplicationDocumentsDirectory(); + + await appDir.delete(recursive: true); + + _log.fine('Deletion complete'); + + emit(state.copyWith(loading: false)); + exit(0); + } catch (e) { + log.severe("Error running migrations", e); + } + } +} + +@freezed +class DbResetState with _$DbResetState { + const factory DbResetState({@Default(false) bool loading}) = _DbResetState; +} diff --git a/lib/db_reset/states/reset.freezed.dart b/lib/db_reset/states/reset.freezed.dart new file mode 100644 index 00000000..8ac90038 --- /dev/null +++ b/lib/db_reset/states/reset.freezed.dart @@ -0,0 +1,133 @@ +// coverage:ignore-file +// GENERATED CODE - DO NOT MODIFY BY HAND +// ignore_for_file: type=lint +// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark + +part of 'reset.dart'; + +// ************************************************************************** +// FreezedGenerator +// ************************************************************************** + +T _$identity(T value) => value; + +final _privateConstructorUsedError = UnsupportedError( + 'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\nPlease check the documentation here for more information: https://github.com/rrousselGit/freezed#custom-getters-and-methods'); + +/// @nodoc +mixin _$DbResetState { + bool get loading => throw _privateConstructorUsedError; + + @JsonKey(ignore: true) + $DbResetStateCopyWith get copyWith => + throw _privateConstructorUsedError; +} + +/// @nodoc +abstract class $DbResetStateCopyWith<$Res> { + factory $DbResetStateCopyWith( + DbResetState value, $Res Function(DbResetState) then) = + _$DbResetStateCopyWithImpl<$Res, DbResetState>; + @useResult + $Res call({bool loading}); +} + +/// @nodoc +class _$DbResetStateCopyWithImpl<$Res, $Val extends DbResetState> + implements $DbResetStateCopyWith<$Res> { + _$DbResetStateCopyWithImpl(this._value, this._then); + + // ignore: unused_field + final $Val _value; + // ignore: unused_field + final $Res Function($Val) _then; + + @pragma('vm:prefer-inline') + @override + $Res call({ + Object? loading = null, + }) { + return _then(_value.copyWith( + loading: null == loading + ? _value.loading + : loading // ignore: cast_nullable_to_non_nullable + as bool, + ) as $Val); + } +} + +/// @nodoc +abstract class _$$DbResetStateImplCopyWith<$Res> + implements $DbResetStateCopyWith<$Res> { + factory _$$DbResetStateImplCopyWith( + _$DbResetStateImpl value, $Res Function(_$DbResetStateImpl) then) = + __$$DbResetStateImplCopyWithImpl<$Res>; + @override + @useResult + $Res call({bool loading}); +} + +/// @nodoc +class __$$DbResetStateImplCopyWithImpl<$Res> + extends _$DbResetStateCopyWithImpl<$Res, _$DbResetStateImpl> + implements _$$DbResetStateImplCopyWith<$Res> { + __$$DbResetStateImplCopyWithImpl( + _$DbResetStateImpl _value, $Res Function(_$DbResetStateImpl) _then) + : super(_value, _then); + + @pragma('vm:prefer-inline') + @override + $Res call({ + Object? loading = null, + }) { + return _then(_$DbResetStateImpl( + loading: null == loading + ? _value.loading + : loading // ignore: cast_nullable_to_non_nullable + as bool, + )); + } +} + +/// @nodoc + +class _$DbResetStateImpl implements _DbResetState { + const _$DbResetStateImpl({this.loading = false}); + + @override + @JsonKey() + final bool loading; + + @override + String toString() { + return 'DbResetState(loading: $loading)'; + } + + @override + bool operator ==(Object other) { + return identical(this, other) || + (other.runtimeType == runtimeType && + other is _$DbResetStateImpl && + (identical(other.loading, loading) || other.loading == loading)); + } + + @override + int get hashCode => Object.hash(runtimeType, loading); + + @JsonKey(ignore: true) + @override + @pragma('vm:prefer-inline') + _$$DbResetStateImplCopyWith<_$DbResetStateImpl> get copyWith => + __$$DbResetStateImplCopyWithImpl<_$DbResetStateImpl>(this, _$identity); +} + +abstract class _DbResetState implements DbResetState { + const factory _DbResetState({final bool loading}) = _$DbResetStateImpl; + + @override + bool get loading; + @override + @JsonKey(ignore: true) + _$$DbResetStateImplCopyWith<_$DbResetStateImpl> get copyWith => + throw _privateConstructorUsedError; +} diff --git a/lib/db_reset/views/screen/reset.dart b/lib/db_reset/views/screen/reset.dart new file mode 100644 index 00000000..2e582459 --- /dev/null +++ b/lib/db_reset/views/screen/reset.dart @@ -0,0 +1,76 @@ +import 'package:auto_route/annotations.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:invidious/main.dart'; +import 'package:invidious/utils/views/tv/components/tv_button.dart'; + +import '../../states/reset.dart'; + +@RoutePage() +class ResetScreen extends StatelessWidget { + const ResetScreen({super.key}); + + @override + Widget build(BuildContext context) { + return BlocProvider( + create: (context) => DbResetCubit(const DbResetState()), + child: Scaffold( + body: SafeArea( + bottom: true, + child: BlocBuilder( + builder: (context, state) { + var cubit = context.read(); + return Padding( + padding: const EdgeInsets.all(8.0), + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + const Icon( + Icons.warning_amber, + size: 50, + ), + const SizedBox( + height: 20, + ), + const Text( + 'In order to keep the application fully open source, the local database backend needs to be switched. Unfortunately there is no simple way to migrate the data.'), + const SizedBox( + height: 20, + ), + const Text( + 'The application needs to be reset', + style: TextStyle(fontWeight: FontWeight.bold), + ), + const SizedBox( + height: 20, + ), + const Text( + 'The application will close once the process is finished'), + const SizedBox( + height: 20, + ), + isTv + ? Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + TvButton( + child: const Padding( + padding: EdgeInsets.all(8.0), + child: Text('Reset application'), + ), + onPressed: (context) => cubit.resetDb()), + ], + ) + : FilledButton.tonal( + onPressed: cubit.resetDb, + child: const Text('Reset application')) + ], + ), + ); + }), + ), + ), + ); + } +} diff --git a/lib/downloads/models/downloaded_video.dart b/lib/downloads/models/downloaded_video.dart index 6a95b0ba..202a47f0 100644 --- a/lib/downloads/models/downloaded_video.dart +++ b/lib/downloads/models/downloaded_video.dart @@ -1,14 +1,16 @@ import 'dart:io'; import 'package:invidious/videos/models/base_video.dart'; -import 'package:objectbox/objectbox.dart'; +import 'package:json_annotation/json_annotation.dart'; import 'package:path_provider/path_provider.dart'; +import 'package:path/path.dart' as p; -@Entity() -class DownloadedVideo extends IdedVideo { - @Id() - int id = 0; +part 'downloaded_video.g.dart'; + +const String _downloadFolder = 'downloads'; +@JsonSerializable() +class DownloadedVideo extends IdedVideo { String title; String? author; String? authorUrl; @@ -21,21 +23,33 @@ class DownloadedVideo extends IdedVideo { @override set videoId(String videoId) => super.videoId = videoId; - @Transient() + @override + String get videoId => super.videoId; + + @JsonKey(includeFromJson: false, includeToJson: false) Future get mediaPath async { - Directory dir = await getApplicationDocumentsDirectory(); + Directory dir = await _getDownloadFolder(); return "${dir.path}/$videoId.${audioOnly ? 'webm' : 'mp4'}"; } - @Transient() + @JsonKey(includeFromJson: false, includeToJson: false) Future get thumbnailPath async { - Directory dir = await getApplicationDocumentsDirectory(); + Directory dir = await _getDownloadFolder(); return "${dir.path}/$videoId.jpg"; } + Future _getDownloadFolder() async { + Directory dir = await getApplicationDocumentsDirectory(); + dir = Directory(p.join(dir.path, _downloadFolder)); + if (!(await dir.exists())) { + await dir.create(recursive: true); + } + + return dir; + } + DownloadedVideo( - {this.id = 0, - required String videoId, + {required String videoId, required this.title, this.author, this.authorUrl, @@ -45,4 +59,9 @@ class DownloadedVideo extends IdedVideo { this.lengthSeconds = 1, this.quality = '720p'}) : super(videoId); + + factory DownloadedVideo.fromJson(Map json) => + _$DownloadedVideoFromJson(json); + + Map toJson() => _$DownloadedVideoToJson(this); } diff --git a/lib/downloads/models/downloaded_video.g.dart b/lib/downloads/models/downloaded_video.g.dart new file mode 100644 index 00000000..96d884ac --- /dev/null +++ b/lib/downloads/models/downloaded_video.g.dart @@ -0,0 +1,33 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'downloaded_video.dart'; + +// ************************************************************************** +// JsonSerializableGenerator +// ************************************************************************** + +DownloadedVideo _$DownloadedVideoFromJson(Map json) => + DownloadedVideo( + videoId: json['videoId'] as String, + title: json['title'] as String, + author: json['author'] as String?, + authorUrl: json['authorUrl'] as String?, + downloadComplete: json['downloadComplete'] as bool? ?? false, + downloadFailed: json['downloadFailed'] as bool? ?? false, + audioOnly: json['audioOnly'] as bool? ?? false, + lengthSeconds: json['lengthSeconds'] as int? ?? 1, + quality: json['quality'] as String? ?? '720p', + ); + +Map _$DownloadedVideoToJson(DownloadedVideo instance) => + { + 'title': instance.title, + 'author': instance.author, + 'authorUrl': instance.authorUrl, + 'downloadComplete': instance.downloadComplete, + 'downloadFailed': instance.downloadFailed, + 'audioOnly': instance.audioOnly, + 'lengthSeconds': instance.lengthSeconds, + 'quality': instance.quality, + 'videoId': instance.videoId, + }; diff --git a/lib/downloads/states/download_manager.dart b/lib/downloads/states/download_manager.dart index e9d64339..3a0c2fa3 100644 --- a/lib/downloads/states/download_manager.dart +++ b/lib/downloads/states/download_manager.dart @@ -48,7 +48,7 @@ class DownloadManagerCubit extends Cubit { setVideos(); } - setVideos() { + setVideos() async { var vids = db.getAllDownloads(); // checking if we have any video that are not fail, not completed and not currently downloading for (var v in vids) { @@ -57,7 +57,7 @@ class DownloadManagerCubit extends Cubit { !state.downloadProgresses.containsKey(v.videoId)) { // this download was interrupted by app restart or crash or something else, we set it as errored v.downloadFailed = true; - db.upsertDownload(v); + await db.upsertDownload(v); } } @@ -71,14 +71,14 @@ class DownloadManagerCubit extends Cubit { .toList()); } - onProgress(int count, int total, DownloadedVideo video) { + onProgress(int count, int total, DownloadedVideo video) async { if (count == total) { var progresses = Map.from(state.downloadProgresses); var downloadProgress = progresses[video.videoId]; progresses.remove(video.videoId); video.downloadComplete = true; - db.upsertDownload(video); + await db.upsertDownload(video); emit(state.copyWith(downloadProgresses: progresses)); setVideos(); for (var f in downloadProgress?.listeners ?? []) { @@ -86,9 +86,9 @@ class DownloadManagerCubit extends Cubit { } } else { EasyThrottle.throttle( - 'download-${video.id}', const Duration(milliseconds: 500), () { + 'download-${video.videoId}', const Duration(milliseconds: 500), () { log.fine( - 'Download of video ${video.id}, $count / $total = ${count / total}, Total: ${state.totalProgress}'); + 'Download of video ${video.videoId}, $count / $total = ${count / total}, Total: ${state.totalProgress}'); var progresses = Map.from(state.downloadProgresses); var downloadProgress = progresses[video.videoId]; @@ -117,7 +117,7 @@ class DownloadManagerCubit extends Cubit { audioOnly: audioOnly, lengthSeconds: vid.lengthSeconds, quality: quality); - db.upsertDownload(downloadedVideo); + await db.upsertDownload(downloadedVideo); String contentUrl; @@ -195,7 +195,7 @@ class DownloadManagerCubit extends Cubit { log.fine('File might not be available, that\'s ok'); } - db.deleteDownload(vid); + await db.deleteDownload(vid); emit(state.copyWith(downloadProgresses: progresses)); setVideos(); @@ -214,7 +214,7 @@ class DownloadManagerCubit extends Cubit { var progresses = Map.from(state.downloadProgresses); progresses.remove(vid.videoId); - db.upsertDownload(vid); + await db.upsertDownload(vid); setVideos(); emit(state.copyWith(downloadProgresses: progresses)); return; diff --git a/lib/downloads/states/downloaded_video.dart b/lib/downloads/states/downloaded_video.dart index 81302bb0..2dd86bd3 100644 --- a/lib/downloads/states/downloaded_video.dart +++ b/lib/downloads/states/downloaded_video.dart @@ -48,7 +48,8 @@ class DownloadedVideoCubit extends Cubit { } void refreshVideo() async { - emit(state.copyWith(video: db.getDownloadById(state.video?.id ?? -1)!)); + emit(state.copyWith( + video: db.getDownloadByVideoId(state.video?.videoId ?? '')!)); setThumbnail(); } @@ -78,7 +79,7 @@ class DownloadedVideoCubit extends Cubit { setComplete() { log.fine("Video ${state.video!.videoId} download complete"); - var downloadById = db.getDownloadById(state.video!.id); + var downloadById = db.getDownloadByVideoId(state.video!.videoId); emit(state.copyWith(progress: 1, video: downloadById)); } } @@ -90,9 +91,9 @@ class DownloadedVideoState with _$DownloadedVideoState { String? thumbnailPath, @Default(0) double progress}) = _DownloadedVideoState; - static DownloadedVideoState init(int videoId) { + static DownloadedVideoState init(String videoId) { DownloadedVideo? video; - var downloadById = db.getDownloadById(videoId); + var downloadById = db.getDownloadByVideoId(videoId); if (downloadById != null) { video = downloadById; } diff --git a/lib/downloads/views/components/downloaded_video.dart b/lib/downloads/views/components/downloaded_video.dart index 5ad8fcd3..28410c61 100644 --- a/lib/downloads/views/components/downloaded_video.dart +++ b/lib/downloads/views/components/downloaded_video.dart @@ -24,7 +24,7 @@ class DownloadedVideoView extends StatelessWidget { var player = context.read(); return BlocProvider( create: (BuildContext context) => DownloadedVideoCubit( - downloadManager, DownloadedVideoState.init(video.id), player), + downloadManager, DownloadedVideoState.init(video.videoId), player), child: BlocBuilder( builder: (context, _) { bool downloadFailed = _.video?.downloadFailed ?? false; diff --git a/lib/downloads/views/screens/download_manager.dart b/lib/downloads/views/screens/download_manager.dart index be16a0ae..7b848b91 100644 --- a/lib/downloads/views/screens/download_manager.dart +++ b/lib/downloads/views/screens/download_manager.dart @@ -49,7 +49,8 @@ class DownloadManagerScreen extends StatelessWidget { itemBuilder: (context, index) { var v = _.videos[index]; return SwipeActionCell( - key: ValueKey('downloaded-video-${v.id}'), + key: + ValueKey('downloaded-video-${v.videoId}'), trailingActions: [ SwipeAction( performsFirstActionWithFullSwipe: true, @@ -62,7 +63,7 @@ class DownloadManagerScreen extends StatelessWidget { ) ], child: DownloadedVideoView( - key: ValueKey(v.id), + key: ValueKey(v.videoId), video: v, )); }, diff --git a/lib/globals.dart b/lib/globals.dart index 7ced2234..13f752e0 100644 --- a/lib/globals.dart +++ b/lib/globals.dart @@ -1,6 +1,7 @@ library app.globals; import 'package:invidious/service.dart'; +import 'package:invidious/utils/file_db.dart'; import 'package:invidious/utils/interfaces/db.dart'; import 'utils/models/country.dart'; @@ -25,6 +26,8 @@ const double innerHorizontalPadding = 16; late IDbClient db; +final FileDB fileDb = FileDB(); + List countryCodes = [ Country('AD', 'Andorra'), Country('AE', 'United Arab Emirates'), diff --git a/lib/home/models/db/home_layout.dart b/lib/home/models/db/home_layout.dart index c2c4b963..8d818fad 100644 --- a/lib/home/models/db/home_layout.dart +++ b/lib/home/models/db/home_layout.dart @@ -2,9 +2,8 @@ import 'package:copy_with_extension/copy_with_extension.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_gen/gen_l10n/app_localizations.dart'; -import 'package:invidious/app/states/app.dart'; +import 'package:freezed_annotation/freezed_annotation.dart'; import 'package:invidious/videos/models/video_in_list.dart'; -import 'package:objectbox/objectbox.dart'; import '../../../downloads/states/download_manager.dart'; import '../../../globals.dart'; @@ -78,12 +77,12 @@ enum HomeDataSource { }; } - bool isPermitted(BuildContext context) { + bool isPermitted(BuildContext context, bool isLoggedIn) { return switch (this) { (HomeDataSource.subscription || HomeDataSource.playlist || HomeDataSource.history) => - context.read().isLoggedIn, + isLoggedIn, (HomeDataSource.searchHistory) => context.read().state.useSearchHistory, (_) => true @@ -207,23 +206,21 @@ enum HomeDataSource { } } -@Entity() +// objectBox @CopyWith(constructor: "_") +@JsonSerializable() class HomeLayout { - @Id() - int id = 0; - - @Transient() + @JsonKey(includeFromJson: false, includeToJson: false) List smallSources = [HomeDataSource.popular]; - @Transient() + @JsonKey(includeFromJson: false, includeToJson: false) HomeDataSource bigSource = HomeDataSource.trending; bool showBigSource = true; HomeLayout(); - HomeLayout._(this.id, this.smallSources, this.bigSource, this.showBigSource); + HomeLayout._(this.smallSources, this.bigSource, this.showBigSource); String get dbBigSource => bigSource.name; @@ -245,4 +242,9 @@ class HomeLayout { HomeDataSource.trending) .toList(); } + + factory HomeLayout.fromJson(Map json) => + _$HomeLayoutFromJson(json); + + Map toJson() => _$HomeLayoutToJson(this); } diff --git a/lib/home/models/db/home_layout.g.dart b/lib/home/models/db/home_layout.g.dart index ba34e668..3ae74a5d 100644 --- a/lib/home/models/db/home_layout.g.dart +++ b/lib/home/models/db/home_layout.g.dart @@ -7,8 +7,6 @@ part of 'home_layout.dart'; // ************************************************************************** abstract class _$HomeLayoutCWProxy { - HomeLayout id(int id); - HomeLayout smallSources(List smallSources); HomeLayout bigSource(HomeDataSource bigSource); @@ -22,7 +20,6 @@ abstract class _$HomeLayoutCWProxy { /// HomeLayout(...).copyWith(id: 12, name: "My name") /// ```` HomeLayout call({ - int? id, List? smallSources, HomeDataSource? bigSource, bool? showBigSource, @@ -35,9 +32,6 @@ class _$HomeLayoutCWProxyImpl implements _$HomeLayoutCWProxy { final HomeLayout _value; - @override - HomeLayout id(int id) => this(id: id); - @override HomeLayout smallSources(List smallSources) => this(smallSources: smallSources); @@ -58,16 +52,11 @@ class _$HomeLayoutCWProxyImpl implements _$HomeLayoutCWProxy { /// HomeLayout(...).copyWith(id: 12, name: "My name") /// ```` HomeLayout call({ - Object? id = const $CopyWithPlaceholder(), Object? smallSources = const $CopyWithPlaceholder(), Object? bigSource = const $CopyWithPlaceholder(), Object? showBigSource = const $CopyWithPlaceholder(), }) { return HomeLayout._( - id == const $CopyWithPlaceholder() || id == null - ? _value.id - // ignore: cast_nullable_to_non_nullable - : id as int, smallSources == const $CopyWithPlaceholder() || smallSources == null ? _value.smallSources // ignore: cast_nullable_to_non_nullable @@ -89,3 +78,21 @@ extension $HomeLayoutCopyWith on HomeLayout { // ignore: library_private_types_in_public_api _$HomeLayoutCWProxy get copyWith => _$HomeLayoutCWProxyImpl(this); } + +// ************************************************************************** +// JsonSerializableGenerator +// ************************************************************************** + +HomeLayout _$HomeLayoutFromJson(Map json) => HomeLayout() + ..showBigSource = json['showBigSource'] as bool + ..dbBigSource = json['dbBigSource'] as String + ..dbSmallSources = (json['dbSmallSources'] as List) + .map((e) => e as String) + .toList(); + +Map _$HomeLayoutToJson(HomeLayout instance) => + { + 'showBigSource': instance.showBigSource, + 'dbBigSource': instance.dbBigSource, + 'dbSmallSources': instance.dbSmallSources, + }; diff --git a/lib/home/states/edit_layout.dart b/lib/home/states/edit_layout.dart index af0e5237..d9f4d476 100644 --- a/lib/home/states/edit_layout.dart +++ b/lib/home/states/edit_layout.dart @@ -8,8 +8,8 @@ class EditLayoutCubit extends Cubit { EditLayoutCubit(super.initialState); @override - void emit(HomeLayout state) { - db.upsertHomeLayout(state); + Future emit(HomeLayout state) async { + await db.upsertHomeLayout(state); super.emit(state); } diff --git a/lib/home/views/components/home.dart b/lib/home/views/components/home.dart index 9991f845..80b9bef2 100644 --- a/lib/home/views/components/home.dart +++ b/lib/home/views/components/home.dart @@ -24,12 +24,13 @@ class HomeView extends StatelessWidget { .then((value) => context.read().updateLayout()); } - List getSmallSources(BuildContext context, HomeLayout layout) { + List getSmallSources( + BuildContext context, HomeLayout layout, bool isLoggedIn) { var textTheme = Theme.of(context).textTheme; var colors = Theme.of(context).colorScheme; var locals = AppLocalizations.of(context)!; return layout.smallSources - .where((element) => element.isPermitted(context)) + .where((element) => element.isPermitted(context, isLoggedIn)) .map((e) => Padding( key: ValueKey(e), padding: const EdgeInsets.only(bottom: 8.0), @@ -65,6 +66,7 @@ class HomeView extends StatelessWidget { var locals = AppLocalizations.of(context)!; var scrolled = context.select((HomeCubit home) => home.state); var home = context.read(); + var isLoggedIn = context.select((AppCubit value) => value.isLoggedIn); return NotificationListener( onNotification: (notificationInfo) { @@ -100,7 +102,8 @@ class HomeView extends StatelessWidget { duration: animationDuration, firstChild: Column( mainAxisSize: MainAxisSize.min, - children: getSmallSources(context, layout)), + children: + getSmallSources(context, layout, isLoggedIn)), secondChild: const Row( children: [ SizedBox.shrink(), diff --git a/lib/home/views/screens/home.dart b/lib/home/views/screens/home.dart index bc613f8b..ffa8c168 100644 --- a/lib/home/views/screens/home.dart +++ b/lib/home/views/screens/home.dart @@ -98,7 +98,7 @@ class _HomeScreenState extends State { var settings = context.watch().state; var allowedPages = settings.appLayout - .where((element) => element.isPermitted(context)) + .where((element) => element.isPermitted(context, app.isLoggedIn)) .toList(); var navigationWidgets = allowedPages .map((e) => e.getBottomBarNavigationWidget(context)) diff --git a/lib/http_overrides.dart b/lib/http_overrides.dart index 59e9eb38..1d4d340e 100644 --- a/lib/http_overrides.dart +++ b/lib/http_overrides.dart @@ -1,7 +1,7 @@ import 'dart:io'; -import 'package:invidious/database.dart'; import 'package:invidious/globals.dart'; +import 'package:invidious/settings/models/db/settings.dart'; class MyHttpOverrides extends HttpOverrides { @override diff --git a/lib/main.dart b/lib/main.dart index d33185a0..ee9e035c 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -10,17 +10,20 @@ import 'package:flutter_gen/gen_l10n/app_localizations.dart'; import 'package:invidious/app/states/app.dart'; import 'package:invidious/downloads/states/download_manager.dart'; import 'package:invidious/globals.dart'; +import 'package:invidious/home/models/db/home_layout.dart'; import 'package:invidious/http_overrides.dart'; import 'package:invidious/media_handler.dart'; import 'package:invidious/notifications/notifications.dart'; import 'package:invidious/player/states/player.dart'; import 'package:invidious/router.dart'; +import 'package:invidious/settings/models/db/settings.dart'; import 'package:invidious/settings/states/settings.dart'; import 'package:invidious/utils.dart'; +import 'package:invidious/utils/sembast_sqflite_database.dart'; import 'package:invidious/workmanager.dart'; import 'package:logging/logging.dart'; -import 'database.dart'; +import 'db_reset/reset_utils.dart'; import 'settings/models/db/app_logs.dart'; const brandColor = Color(0xFF4f0096); @@ -35,12 +38,12 @@ final Logger log = Logger('main'); Future main() async { Logger.root.level = kDebugMode ? Level.FINEST : Level.INFO; // defaults to Level.INFO - Logger.root.onRecord.listen((record) { + Logger.root.onRecord.listen((record) async { debugPrint( '[${record.level.name}] [${record.loggerName}] ${record.message}'); // we don't want debug if (record.level == Level.INFO || record.level == Level.SEVERE) { - db.insertLogs(AppLog( + await db.insertLogs(AppLog( logger: record.loggerName, level: record.level.name, time: record.time, @@ -53,7 +56,19 @@ Future main() async { WidgetsFlutterBinding.ensureInitialized(); // FlutterNativeSplash.preserve(widgetsBinding: widgetsBinding); - db = await DbClient.create(); + db = await SembastSqfDb.create(); + await fileDb.syncWithDb(); + + final needsDbMigration = await needsReset(); + late final bool hasServer; + try { + await db.getCurrentlySelectedServer(); + hasServer = true; + } catch (err) { + hasServer = false; + } + appRouter = + AppRouter(needsDbMigration: needsDbMigration, hasServer: hasServer); initializeNotifications(); @@ -68,7 +83,7 @@ Future main() async { runApp(MultiBlocProvider(providers: [ BlocProvider( create: (context) { - return AppCubit(AppState.init()); + return AppCubit(AppState(0, null, HomeLayout())); }, ), BlocProvider( diff --git a/lib/notifications/models/db/channel_notifications.dart b/lib/notifications/models/db/channel_notifications.dart index 5d95cefa..a3a9e245 100644 --- a/lib/notifications/models/db/channel_notifications.dart +++ b/lib/notifications/models/db/channel_notifications.dart @@ -1,12 +1,10 @@ -import 'package:objectbox/objectbox.dart'; +import 'package:json_annotation/json_annotation.dart'; -@Entity() -class ChannelNotification { - @Id() - int id = 0; +part 'channel_notifications.g.dart'; - @Unique(onConflict: ConflictStrategy.replace) - String channelId; +@JsonSerializable() +class ChannelNotification { + final String channelId; String lastSeenVideoId; @@ -20,4 +18,9 @@ class ChannelNotification { this.lastSeenVideoId, this.timestamp, ); + + factory ChannelNotification.fromJson(Map json) => + _$ChannelNotificationFromJson(json); + + Map toJson() => _$ChannelNotificationToJson(this); } diff --git a/lib/notifications/models/db/channel_notifications.g.dart b/lib/notifications/models/db/channel_notifications.g.dart new file mode 100644 index 00000000..54ed5538 --- /dev/null +++ b/lib/notifications/models/db/channel_notifications.g.dart @@ -0,0 +1,24 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'channel_notifications.dart'; + +// ************************************************************************** +// JsonSerializableGenerator +// ************************************************************************** + +ChannelNotification _$ChannelNotificationFromJson(Map json) => + ChannelNotification( + json['channelId'] as String, + json['channelName'] as String, + json['lastSeenVideoId'] as String, + json['timestamp'] as int, + ); + +Map _$ChannelNotificationToJson( + ChannelNotification instance) => + { + 'channelId': instance.channelId, + 'lastSeenVideoId': instance.lastSeenVideoId, + 'timestamp': instance.timestamp, + 'channelName': instance.channelName, + }; diff --git a/lib/notifications/models/db/playlist_notifications.dart b/lib/notifications/models/db/playlist_notifications.dart index eab8819b..12bf5508 100644 --- a/lib/notifications/models/db/playlist_notifications.dart +++ b/lib/notifications/models/db/playlist_notifications.dart @@ -1,12 +1,10 @@ -import 'package:objectbox/objectbox.dart'; +import 'package:json_annotation/json_annotation.dart'; -@Entity() -class PlaylistNotification { - @Id() - int id = 0; +part 'playlist_notifications.g.dart'; - @Unique(onConflict: ConflictStrategy.replace) - String playlistId; +@JsonSerializable() +class PlaylistNotification { + final String playlistId; int lastVideoCount = 0; @@ -16,4 +14,9 @@ class PlaylistNotification { PlaylistNotification( this.playlistId, this.lastVideoCount, this.timestamp, this.playlistName); + + factory PlaylistNotification.fromJson(Map json) => + _$PlaylistNotificationFromJson(json); + + Map toJson() => _$PlaylistNotificationToJson(this); } diff --git a/lib/notifications/models/db/playlist_notifications.g.dart b/lib/notifications/models/db/playlist_notifications.g.dart new file mode 100644 index 00000000..3acc9bde --- /dev/null +++ b/lib/notifications/models/db/playlist_notifications.g.dart @@ -0,0 +1,25 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'playlist_notifications.dart'; + +// ************************************************************************** +// JsonSerializableGenerator +// ************************************************************************** + +PlaylistNotification _$PlaylistNotificationFromJson( + Map json) => + PlaylistNotification( + json['playlistId'] as String, + json['lastVideoCount'] as int, + json['timestamp'] as int, + json['playlistName'] as String, + ); + +Map _$PlaylistNotificationToJson( + PlaylistNotification instance) => + { + 'playlistId': instance.playlistId, + 'lastVideoCount': instance.lastVideoCount, + 'timestamp': instance.timestamp, + 'playlistName': instance.playlistName, + }; diff --git a/lib/notifications/models/db/subscription_notifications.dart b/lib/notifications/models/db/subscription_notifications.dart index 16a3143b..3603a8a4 100644 --- a/lib/notifications/models/db/subscription_notifications.dart +++ b/lib/notifications/models/db/subscription_notifications.dart @@ -1,13 +1,17 @@ -import 'package:objectbox/objectbox.dart'; +import 'package:json_annotation/json_annotation.dart'; -@Entity() -class SubscriptionNotification { - @Id() - int id = 0; +part 'subscription_notifications.g.dart'; +@JsonSerializable() +class SubscriptionNotification { String lastSeenVideoId; int timestamp; SubscriptionNotification(this.lastSeenVideoId, this.timestamp); + + factory SubscriptionNotification.fromJson(Map json) => + _$SubscriptionNotificationFromJson(json); + + Map toJson() => _$SubscriptionNotificationToJson(this); } diff --git a/lib/notifications/models/db/subscription_notifications.g.dart b/lib/notifications/models/db/subscription_notifications.g.dart new file mode 100644 index 00000000..b165bdc3 --- /dev/null +++ b/lib/notifications/models/db/subscription_notifications.g.dart @@ -0,0 +1,21 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'subscription_notifications.dart'; + +// ************************************************************************** +// JsonSerializableGenerator +// ************************************************************************** + +SubscriptionNotification _$SubscriptionNotificationFromJson( + Map json) => + SubscriptionNotification( + json['lastSeenVideoId'] as String, + json['timestamp'] as int, + ); + +Map _$SubscriptionNotificationToJson( + SubscriptionNotification instance) => + { + 'lastSeenVideoId': instance.lastSeenVideoId, + 'timestamp': instance.timestamp, + }; diff --git a/lib/notifications/notifications.dart b/lib/notifications/notifications.dart index d0d12512..f3a2a205 100644 --- a/lib/notifications/notifications.dart +++ b/lib/notifications/notifications.dart @@ -101,22 +101,22 @@ class NotificationController { payload.containsKey(lastSeenVideo)) { log.fine('Launching channel screen ${receivedAction.payload}'); appRouter.push(ChannelRoute(channelId: payload[channelId]!)); - db.setChannelNotificationLastViewedVideo( + await fileDb.setChannelNotificationLastViewedVideo( payload[channelId]!, payload[lastSeenVideo]!); } else if (receivedAction.channelKey == NotificationTypes.playlist.id && payload.containsKey(playlistId)) { log.fine('Launching playlist screen ${receivedAction.payload}'); - service.getPublicPlaylists(payload[playlistId]!).then((value) { + service.getPublicPlaylists(payload[playlistId]!).then((value) async { appRouter .push(PlaylistViewRoute(playlist: value, canDeleteVideos: false)); - db.setPlaylistNotificationLastViewedVideo( + await fileDb.setPlaylistNotificationLastViewedVideo( value.playlistId, value.videoCount); }); } else if (receivedAction.channelKey == NotificationTypes.subscription.id && payload.containsKey(lastSeenVideo)) { appRouter.push(const SubscriptionRoute()); - db.setLastSubscriptionNotification(SubscriptionNotification( + await fileDb.setLastSubscriptionNotification(SubscriptionNotification( payload[lastSeenVideo]!, DateTime.now().millisecondsSinceEpoch)); } } diff --git a/lib/notifications/state/bell_icon.dart b/lib/notifications/state/bell_icon.dart index 1681e5cc..ffe4c30f 100644 --- a/lib/notifications/state/bell_icon.dart +++ b/lib/notifications/state/bell_icon.dart @@ -21,20 +21,20 @@ class BellIconCubit extends Cubit { onInit(); } - void onInit() { + Future onInit() async { if (settings.state.backgroundNotifications) { - emit(getNotification()); + emit(await getNotification()); } else { emit(false); } } - bool getNotification() { + Future getNotification() async { switch (type) { case BellIconType.channel: - return db.getChannelNotification(itemId) != null; + return await fileDb.getChannelNotification(itemId) != null; case BellIconType.playlist: - return db.getPlaylistNotification(itemId) != null; + return await fileDb.getPlaylistNotification(itemId) != null; } } @@ -57,7 +57,7 @@ class BellIconCubit extends Cubit { switch (type) { case BellIconType.channel: var channel = await service.getChannel(itemId); - db.upsertChannelNotification(ChannelNotification( + await fileDb.upsertChannelNotification(ChannelNotification( itemId, channel.author, channel.latestVideos?.firstOrNull?.videoId ?? '', @@ -65,7 +65,7 @@ class BellIconCubit extends Cubit { break; case BellIconType.playlist: var playlist = await service.getPublicPlaylists(itemId); - db.upsertPlaylistNotification(PlaylistNotification( + await fileDb.upsertPlaylistNotification(PlaylistNotification( itemId, playlist.videoCount, DateTime.now().millisecondsSinceEpoch, @@ -76,16 +76,16 @@ class BellIconCubit extends Cubit { } else { switch (type) { case BellIconType.channel: - var notif = db.getChannelNotification(itemId); + var notif = await fileDb.getChannelNotification(itemId); if (notif != null) { - db.deleteChannelNotification(notif); + await fileDb.deleteChannelNotification(notif); emit(false); } break; case BellIconType.playlist: - var notif = db.getPlaylistNotification(itemId); + var notif = await fileDb.getPlaylistNotification(itemId); if (notif != null) { - db.deletePlaylistNotification(notif); + await fileDb.deletePlaylistNotification(notif); emit(false); } } diff --git a/lib/objectbox-model.json b/lib/objectbox-model.json deleted file mode 100644 index cb7467c2..00000000 --- a/lib/objectbox-model.json +++ /dev/null @@ -1,497 +0,0 @@ -{ - "_note1": "KEEP THIS FILE! Check it into a version control system (VCS) like git.", - "_note2": "ObjectBox manages crucial IDs for your object model. See docs for details.", - "_note3": "If you have VCS merge conflicts, you must resolve them according to ObjectBox docs.", - "entities": [ - { - "id": "1:8038281984607819042", - "lastPropertyId": "7:7993511472188126727", - "name": "Server", - "properties": [ - { - "id": "1:2410037502505490239", - "name": "id", - "type": 6, - "flags": 1 - }, - { - "id": "2:8956955780928852647", - "name": "url", - "type": 9, - "flags": 34848, - "indexId": "1:2407620149040502806" - }, - { - "id": "3:2983333445278944481", - "name": "authToken", - "type": 9 - }, - { - "id": "6:2632576147281157993", - "name": "inUse", - "type": 1 - }, - { - "id": "7:7993511472188126727", - "name": "sidCookie", - "type": 9 - } - ], - "relations": [] - }, - { - "id": "2:2463187106197509769", - "lastPropertyId": "3:4665740245375834282", - "name": "SettingsValue", - "properties": [ - { - "id": "1:3567602159840796895", - "name": "id", - "type": 6, - "flags": 1 - }, - { - "id": "2:525293931186831855", - "name": "name", - "type": 9, - "flags": 34848, - "indexId": "2:1004000799244198133" - }, - { - "id": "3:4665740245375834282", - "name": "value", - "type": 9 - } - ], - "relations": [] - }, - { - "id": "3:8787382286414233697", - "lastPropertyId": "3:8602536477328513343", - "name": "Progress", - "properties": [ - { - "id": "1:5651818511313053101", - "name": "id", - "type": 6, - "flags": 1 - }, - { - "id": "2:4301486387527660152", - "name": "progress", - "type": 8 - }, - { - "id": "3:8602536477328513343", - "name": "videoId", - "type": 9, - "flags": 34848, - "indexId": "3:4343529106190079511" - } - ], - "relations": [] - }, - { - "id": "4:6956330633348216454", - "lastPropertyId": "3:4799661758354837094", - "name": "SearchHistoryItem", - "properties": [ - { - "id": "1:5055652677379509965", - "name": "id", - "type": 6, - "flags": 1 - }, - { - "id": "2:1184322842379216559", - "name": "search", - "type": 9, - "flags": 34848, - "indexId": "5:7262786699272501249" - }, - { - "id": "3:4799661758354837094", - "name": "time", - "type": 6 - } - ], - "relations": [] - }, - { - "id": "5:8446250266008376981", - "lastPropertyId": "6:5023716219165786985", - "name": "AppLog", - "properties": [ - { - "id": "1:3160197863614923332", - "name": "id", - "type": 6, - "flags": 1 - }, - { - "id": "2:3008237629246315185", - "name": "level", - "type": 9 - }, - { - "id": "3:2647517759142178088", - "name": "time", - "type": 10 - }, - { - "id": "4:7759611646931835814", - "name": "message", - "type": 9 - }, - { - "id": "5:1230803493579490035", - "name": "stacktrace", - "type": 9 - }, - { - "id": "6:5023716219165786985", - "name": "logger", - "type": 9 - } - ], - "relations": [] - }, - { - "id": "6:8304874620604193998", - "lastPropertyId": "11:8416925878752879022", - "name": "VideoFilter", - "properties": [ - { - "id": "1:4718003498405944371", - "name": "id", - "type": 6, - "flags": 1 - }, - { - "id": "2:906689741192359458", - "name": "channelId", - "type": 9 - }, - { - "id": "3:19321448094437321", - "name": "value", - "type": 9 - }, - { - "id": "4:7050038042672519102", - "name": "dbType", - "type": 9 - }, - { - "id": "5:2812361632619055204", - "name": "dbOperation", - "type": 9 - }, - { - "id": "7:4560530621575797576", - "name": "filterAll", - "type": 1 - }, - { - "id": "8:6020474727686624632", - "name": "hideFromFeed", - "type": 1 - }, - { - "id": "9:385259419700560741", - "name": "daysOfWeek", - "type": 27 - }, - { - "id": "10:4391992064156904605", - "name": "startTime", - "type": 9 - }, - { - "id": "11:8416925878752879022", - "name": "endTime", - "type": 9 - } - ], - "relations": [] - }, - { - "id": "7:7737259498144569754", - "lastPropertyId": "12:5585556588689024155", - "name": "DownloadedVideo", - "properties": [ - { - "id": "1:4941717954477256691", - "name": "id", - "type": 6, - "flags": 1 - }, - { - "id": "3:2383320059961158921", - "name": "title", - "type": 9 - }, - { - "id": "4:8378509179540529921", - "name": "author", - "type": 9 - }, - { - "id": "5:1179720564271059922", - "name": "authorUrl", - "type": 9 - }, - { - "id": "6:107924486215845740", - "name": "downloadComplete", - "type": 1 - }, - { - "id": "7:4130320220878296738", - "name": "downloadFailed", - "type": 1 - }, - { - "id": "8:6560523765053270041", - "name": "audioOnly", - "type": 1 - }, - { - "id": "10:7860866333154890990", - "name": "quality", - "type": 9 - }, - { - "id": "11:4090966965717168930", - "name": "lengthSeconds", - "type": 6 - }, - { - "id": "12:5585556588689024155", - "name": "videoId", - "type": 9 - } - ], - "relations": [] - }, - { - "id": "9:4192516430920036128", - "lastPropertyId": "6:3954053412124926577", - "name": "HistoryVideoCache", - "properties": [ - { - "id": "1:2955960989783551283", - "name": "id", - "type": 6, - "flags": 1 - }, - { - "id": "2:4777842522603948496", - "name": "title", - "type": 9 - }, - { - "id": "3:4486601293052981453", - "name": "author", - "type": 9 - }, - { - "id": "4:5980827452700393174", - "name": "videoId", - "type": 9 - }, - { - "id": "5:5075309708561338655", - "name": "created", - "type": 10 - }, - { - "id": "6:3954053412124926577", - "name": "thumbnail", - "type": 9 - } - ], - "relations": [] - }, - { - "id": "10:6821162325360407377", - "lastPropertyId": "4:8172441605240321034", - "name": "HomeLayout", - "properties": [ - { - "id": "1:2141116138730176870", - "name": "id", - "type": 6, - "flags": 1 - }, - { - "id": "2:4699190633584236247", - "name": "showBigSource", - "type": 1 - }, - { - "id": "3:2336366876714802775", - "name": "dbBigSource", - "type": 9 - }, - { - "id": "4:8172441605240321034", - "name": "dbSmallSources", - "type": 30 - } - ], - "relations": [] - }, - { - "id": "11:3657792956132207980", - "lastPropertyId": "3:1941341505549292694", - "name": "SubscriptionNotification", - "properties": [ - { - "id": "1:2430225471686599517", - "name": "id", - "type": 6, - "flags": 1 - }, - { - "id": "2:2835271477274198093", - "name": "lastSeenVideoId", - "type": 9 - }, - { - "id": "3:1941341505549292694", - "name": "timestamp", - "type": 6 - } - ], - "relations": [] - }, - { - "id": "12:2070539588161609146", - "lastPropertyId": "5:2886321232475223602", - "name": "ChannelNotification", - "properties": [ - { - "id": "1:8845605462686818448", - "name": "id", - "type": 6, - "flags": 1 - }, - { - "id": "2:2512902621149037266", - "name": "channelId", - "type": 9, - "flags": 34848, - "indexId": "6:1543728882665646543" - }, - { - "id": "3:1308895344490646098", - "name": "lastSeenVideoId", - "type": 9 - }, - { - "id": "4:4272759280615528314", - "name": "timestamp", - "type": 6 - }, - { - "id": "5:2886321232475223602", - "name": "channelName", - "type": 9 - } - ], - "relations": [] - }, - { - "id": "13:8331886434292283747", - "lastPropertyId": "5:5316267761398216511", - "name": "PlaylistNotification", - "properties": [ - { - "id": "1:7681992295553062859", - "name": "id", - "type": 6, - "flags": 1 - }, - { - "id": "2:8328838064368170718", - "name": "playlistId", - "type": 9, - "flags": 34848, - "indexId": "7:4488140487903335246" - }, - { - "id": "3:7229926964587744944", - "name": "lastVideoCount", - "type": 6 - }, - { - "id": "4:6825540046607174830", - "name": "timestamp", - "type": 6 - }, - { - "id": "5:5316267761398216511", - "name": "playlistName", - "type": 9 - } - ], - "relations": [] - }, - { - "id": "15:7475761865024216578", - "lastPropertyId": "4:7876161971726247302", - "name": "DeArrowCache", - "properties": [ - { - "id": "1:2266267358862097191", - "name": "id", - "type": 6, - "flags": 1 - }, - { - "id": "2:6260864673962012294", - "name": "videoId", - "type": 9, - "flags": 34848, - "indexId": "8:11015011236117301" - }, - { - "id": "3:3037776482306894529", - "name": "title", - "type": 9 - }, - { - "id": "4:7876161971726247302", - "name": "url", - "type": 9 - } - ], - "relations": [] - } - ], - "lastEntityId": "15:7475761865024216578", - "lastIndexId": "8:11015011236117301", - "lastRelationId": "0:0", - "lastSequenceId": "0:0", - "modelVersion": 5, - "modelVersionParserMinimum": 5, - "retiredEntityUids": [ - 6897417709810972885, - 2895109148053406327 - ], - "retiredIndexUids": [ - 9110274326691932798 - ], - "retiredPropertyUids": [ - 3422621380867834787, - 971220157301355316, - 7030952573865954657, - 6600296338817128660, - 345286493546760360, - 3278768646220204892, - 6911432365687480646 - ], - "retiredRelationUids": [], - "version": 1 -} \ No newline at end of file diff --git a/lib/objectbox.g.dart b/lib/objectbox.g.dart deleted file mode 100644 index eb68757e..00000000 --- a/lib/objectbox.g.dart +++ /dev/null @@ -1,1386 +0,0 @@ -// GENERATED CODE - DO NOT MODIFY BY HAND -// This code was generated by ObjectBox. To update it run the generator again: -// With a Flutter package, run `flutter pub run build_runner build`. -// With a Dart package, run `dart run build_runner build`. -// See also https://docs.objectbox.io/getting-started#generate-objectbox-code - -// ignore_for_file: camel_case_types, depend_on_referenced_packages -// coverage:ignore-file - -import 'dart:typed_data'; - -import 'package:flat_buffers/flat_buffers.dart' as fb; -import 'package:objectbox/internal.dart'; // generated code can access "internal" functionality -import 'package:objectbox/objectbox.dart'; -import 'package:objectbox_flutter_libs/objectbox_flutter_libs.dart'; - -import 'downloads/models/downloaded_video.dart'; -import 'home/models/db/home_layout.dart'; -import 'notifications/models/db/channel_notifications.dart'; -import 'notifications/models/db/playlist_notifications.dart'; -import 'notifications/models/db/subscription_notifications.dart'; -import 'search/models/db/search_history_item.dart'; -import 'settings/models/db/app_logs.dart'; -import 'settings/models/db/server.dart'; -import 'settings/models/db/settings.dart'; -import 'settings/models/db/video_filter.dart'; -import 'videos/models/db/dearrow_cache.dart'; -import 'videos/models/db/history_video_cache.dart'; -import 'videos/models/db/progress.dart'; - -export 'package:objectbox/objectbox.dart'; // so that callers only have to import this file - -final _entities = [ - ModelEntity( - id: const IdUid(1, 8038281984607819042), - name: 'Server', - lastPropertyId: const IdUid(7, 7993511472188126727), - flags: 0, - properties: [ - ModelProperty( - id: const IdUid(1, 2410037502505490239), - name: 'id', - type: 6, - flags: 1), - ModelProperty( - id: const IdUid(2, 8956955780928852647), - name: 'url', - type: 9, - flags: 34848, - indexId: const IdUid(1, 2407620149040502806)), - ModelProperty( - id: const IdUid(3, 2983333445278944481), - name: 'authToken', - type: 9, - flags: 0), - ModelProperty( - id: const IdUid(6, 2632576147281157993), - name: 'inUse', - type: 1, - flags: 0), - ModelProperty( - id: const IdUid(7, 7993511472188126727), - name: 'sidCookie', - type: 9, - flags: 0) - ], - relations: [], - backlinks: []), - ModelEntity( - id: const IdUid(2, 2463187106197509769), - name: 'SettingsValue', - lastPropertyId: const IdUid(3, 4665740245375834282), - flags: 0, - properties: [ - ModelProperty( - id: const IdUid(1, 3567602159840796895), - name: 'id', - type: 6, - flags: 1), - ModelProperty( - id: const IdUid(2, 525293931186831855), - name: 'name', - type: 9, - flags: 34848, - indexId: const IdUid(2, 1004000799244198133)), - ModelProperty( - id: const IdUid(3, 4665740245375834282), - name: 'value', - type: 9, - flags: 0) - ], - relations: [], - backlinks: []), - ModelEntity( - id: const IdUid(3, 8787382286414233697), - name: 'Progress', - lastPropertyId: const IdUid(3, 8602536477328513343), - flags: 0, - properties: [ - ModelProperty( - id: const IdUid(1, 5651818511313053101), - name: 'id', - type: 6, - flags: 1), - ModelProperty( - id: const IdUid(2, 4301486387527660152), - name: 'progress', - type: 8, - flags: 0), - ModelProperty( - id: const IdUid(3, 8602536477328513343), - name: 'videoId', - type: 9, - flags: 34848, - indexId: const IdUid(3, 4343529106190079511)) - ], - relations: [], - backlinks: []), - ModelEntity( - id: const IdUid(4, 6956330633348216454), - name: 'SearchHistoryItem', - lastPropertyId: const IdUid(3, 4799661758354837094), - flags: 0, - properties: [ - ModelProperty( - id: const IdUid(1, 5055652677379509965), - name: 'id', - type: 6, - flags: 1), - ModelProperty( - id: const IdUid(2, 1184322842379216559), - name: 'search', - type: 9, - flags: 34848, - indexId: const IdUid(5, 7262786699272501249)), - ModelProperty( - id: const IdUid(3, 4799661758354837094), - name: 'time', - type: 6, - flags: 0) - ], - relations: [], - backlinks: []), - ModelEntity( - id: const IdUid(5, 8446250266008376981), - name: 'AppLog', - lastPropertyId: const IdUid(6, 5023716219165786985), - flags: 0, - properties: [ - ModelProperty( - id: const IdUid(1, 3160197863614923332), - name: 'id', - type: 6, - flags: 1), - ModelProperty( - id: const IdUid(2, 3008237629246315185), - name: 'level', - type: 9, - flags: 0), - ModelProperty( - id: const IdUid(3, 2647517759142178088), - name: 'time', - type: 10, - flags: 0), - ModelProperty( - id: const IdUid(4, 7759611646931835814), - name: 'message', - type: 9, - flags: 0), - ModelProperty( - id: const IdUid(5, 1230803493579490035), - name: 'stacktrace', - type: 9, - flags: 0), - ModelProperty( - id: const IdUid(6, 5023716219165786985), - name: 'logger', - type: 9, - flags: 0) - ], - relations: [], - backlinks: []), - ModelEntity( - id: const IdUid(6, 8304874620604193998), - name: 'VideoFilter', - lastPropertyId: const IdUid(11, 8416925878752879022), - flags: 0, - properties: [ - ModelProperty( - id: const IdUid(1, 4718003498405944371), - name: 'id', - type: 6, - flags: 1), - ModelProperty( - id: const IdUid(2, 906689741192359458), - name: 'channelId', - type: 9, - flags: 0), - ModelProperty( - id: const IdUid(3, 19321448094437321), - name: 'value', - type: 9, - flags: 0), - ModelProperty( - id: const IdUid(4, 7050038042672519102), - name: 'dbType', - type: 9, - flags: 0), - ModelProperty( - id: const IdUid(5, 2812361632619055204), - name: 'dbOperation', - type: 9, - flags: 0), - ModelProperty( - id: const IdUid(7, 4560530621575797576), - name: 'filterAll', - type: 1, - flags: 0), - ModelProperty( - id: const IdUid(8, 6020474727686624632), - name: 'hideFromFeed', - type: 1, - flags: 0), - ModelProperty( - id: const IdUid(9, 385259419700560741), - name: 'daysOfWeek', - type: 27, - flags: 0), - ModelProperty( - id: const IdUid(10, 4391992064156904605), - name: 'startTime', - type: 9, - flags: 0), - ModelProperty( - id: const IdUid(11, 8416925878752879022), - name: 'endTime', - type: 9, - flags: 0) - ], - relations: [], - backlinks: []), - ModelEntity( - id: const IdUid(7, 7737259498144569754), - name: 'DownloadedVideo', - lastPropertyId: const IdUid(12, 5585556588689024155), - flags: 0, - properties: [ - ModelProperty( - id: const IdUid(1, 4941717954477256691), - name: 'id', - type: 6, - flags: 1), - ModelProperty( - id: const IdUid(3, 2383320059961158921), - name: 'title', - type: 9, - flags: 0), - ModelProperty( - id: const IdUid(4, 8378509179540529921), - name: 'author', - type: 9, - flags: 0), - ModelProperty( - id: const IdUid(5, 1179720564271059922), - name: 'authorUrl', - type: 9, - flags: 0), - ModelProperty( - id: const IdUid(6, 107924486215845740), - name: 'downloadComplete', - type: 1, - flags: 0), - ModelProperty( - id: const IdUid(7, 4130320220878296738), - name: 'downloadFailed', - type: 1, - flags: 0), - ModelProperty( - id: const IdUid(8, 6560523765053270041), - name: 'audioOnly', - type: 1, - flags: 0), - ModelProperty( - id: const IdUid(10, 7860866333154890990), - name: 'quality', - type: 9, - flags: 0), - ModelProperty( - id: const IdUid(11, 4090966965717168930), - name: 'lengthSeconds', - type: 6, - flags: 0), - ModelProperty( - id: const IdUid(12, 5585556588689024155), - name: 'videoId', - type: 9, - flags: 0) - ], - relations: [], - backlinks: []), - ModelEntity( - id: const IdUid(9, 4192516430920036128), - name: 'HistoryVideoCache', - lastPropertyId: const IdUid(6, 3954053412124926577), - flags: 0, - properties: [ - ModelProperty( - id: const IdUid(1, 2955960989783551283), - name: 'id', - type: 6, - flags: 1), - ModelProperty( - id: const IdUid(2, 4777842522603948496), - name: 'title', - type: 9, - flags: 0), - ModelProperty( - id: const IdUid(3, 4486601293052981453), - name: 'author', - type: 9, - flags: 0), - ModelProperty( - id: const IdUid(4, 5980827452700393174), - name: 'videoId', - type: 9, - flags: 0), - ModelProperty( - id: const IdUid(5, 5075309708561338655), - name: 'created', - type: 10, - flags: 0), - ModelProperty( - id: const IdUid(6, 3954053412124926577), - name: 'thumbnail', - type: 9, - flags: 0) - ], - relations: [], - backlinks: []), - ModelEntity( - id: const IdUid(10, 6821162325360407377), - name: 'HomeLayout', - lastPropertyId: const IdUid(4, 8172441605240321034), - flags: 0, - properties: [ - ModelProperty( - id: const IdUid(1, 2141116138730176870), - name: 'id', - type: 6, - flags: 1), - ModelProperty( - id: const IdUid(2, 4699190633584236247), - name: 'showBigSource', - type: 1, - flags: 0), - ModelProperty( - id: const IdUid(3, 2336366876714802775), - name: 'dbBigSource', - type: 9, - flags: 0), - ModelProperty( - id: const IdUid(4, 8172441605240321034), - name: 'dbSmallSources', - type: 30, - flags: 0) - ], - relations: [], - backlinks: []), - ModelEntity( - id: const IdUid(11, 3657792956132207980), - name: 'SubscriptionNotification', - lastPropertyId: const IdUid(3, 1941341505549292694), - flags: 0, - properties: [ - ModelProperty( - id: const IdUid(1, 2430225471686599517), - name: 'id', - type: 6, - flags: 1), - ModelProperty( - id: const IdUid(2, 2835271477274198093), - name: 'lastSeenVideoId', - type: 9, - flags: 0), - ModelProperty( - id: const IdUid(3, 1941341505549292694), - name: 'timestamp', - type: 6, - flags: 0) - ], - relations: [], - backlinks: []), - ModelEntity( - id: const IdUid(12, 2070539588161609146), - name: 'ChannelNotification', - lastPropertyId: const IdUid(5, 2886321232475223602), - flags: 0, - properties: [ - ModelProperty( - id: const IdUid(1, 8845605462686818448), - name: 'id', - type: 6, - flags: 1), - ModelProperty( - id: const IdUid(2, 2512902621149037266), - name: 'channelId', - type: 9, - flags: 34848, - indexId: const IdUid(6, 1543728882665646543)), - ModelProperty( - id: const IdUid(3, 1308895344490646098), - name: 'lastSeenVideoId', - type: 9, - flags: 0), - ModelProperty( - id: const IdUid(4, 4272759280615528314), - name: 'timestamp', - type: 6, - flags: 0), - ModelProperty( - id: const IdUid(5, 2886321232475223602), - name: 'channelName', - type: 9, - flags: 0) - ], - relations: [], - backlinks: []), - ModelEntity( - id: const IdUid(13, 8331886434292283747), - name: 'PlaylistNotification', - lastPropertyId: const IdUid(5, 5316267761398216511), - flags: 0, - properties: [ - ModelProperty( - id: const IdUid(1, 7681992295553062859), - name: 'id', - type: 6, - flags: 1), - ModelProperty( - id: const IdUid(2, 8328838064368170718), - name: 'playlistId', - type: 9, - flags: 34848, - indexId: const IdUid(7, 4488140487903335246)), - ModelProperty( - id: const IdUid(3, 7229926964587744944), - name: 'lastVideoCount', - type: 6, - flags: 0), - ModelProperty( - id: const IdUid(4, 6825540046607174830), - name: 'timestamp', - type: 6, - flags: 0), - ModelProperty( - id: const IdUid(5, 5316267761398216511), - name: 'playlistName', - type: 9, - flags: 0) - ], - relations: [], - backlinks: []), - ModelEntity( - id: const IdUid(15, 7475761865024216578), - name: 'DeArrowCache', - lastPropertyId: const IdUid(4, 7876161971726247302), - flags: 0, - properties: [ - ModelProperty( - id: const IdUid(1, 2266267358862097191), - name: 'id', - type: 6, - flags: 1), - ModelProperty( - id: const IdUid(2, 6260864673962012294), - name: 'videoId', - type: 9, - flags: 34848, - indexId: const IdUid(8, 11015011236117301)), - ModelProperty( - id: const IdUid(3, 3037776482306894529), - name: 'title', - type: 9, - flags: 0), - ModelProperty( - id: const IdUid(4, 7876161971726247302), - name: 'url', - type: 9, - flags: 0) - ], - relations: [], - backlinks: []) -]; - -/// Shortcut for [Store.new] that passes [getObjectBoxModel] and for Flutter -/// apps by default a [directory] using `defaultStoreDirectory()` from the -/// ObjectBox Flutter library. -/// -/// Note: for desktop apps it is recommended to specify a unique [directory]. -/// -/// See [Store.new] for an explanation of all parameters. -Future openStore( - {String? directory, - int? maxDBSizeInKB, - int? fileMode, - int? maxReaders, - bool queriesCaseSensitiveDefault = true, - String? macosApplicationGroup}) async => - Store(getObjectBoxModel(), - directory: directory ?? (await defaultStoreDirectory()).path, - maxDBSizeInKB: maxDBSizeInKB, - fileMode: fileMode, - maxReaders: maxReaders, - queriesCaseSensitiveDefault: queriesCaseSensitiveDefault, - macosApplicationGroup: macosApplicationGroup); - -/// Returns the ObjectBox model definition for this project for use with -/// [Store.new]. -ModelDefinition getObjectBoxModel() { - final model = ModelInfo( - entities: _entities, - lastEntityId: const IdUid(15, 7475761865024216578), - lastIndexId: const IdUid(8, 11015011236117301), - lastRelationId: const IdUid(0, 0), - lastSequenceId: const IdUid(0, 0), - retiredEntityUids: const [6897417709810972885, 2895109148053406327], - retiredIndexUids: const [9110274326691932798], - retiredPropertyUids: const [ - 3422621380867834787, - 971220157301355316, - 7030952573865954657, - 6600296338817128660, - 345286493546760360, - 3278768646220204892, - 6911432365687480646 - ], - retiredRelationUids: const [], - modelVersion: 5, - modelVersionParserMinimum: 5, - version: 1); - - final bindings = { - Server: EntityDefinition( - model: _entities[0], - toOneRelations: (Server object) => [], - toManyRelations: (Server object) => {}, - getId: (Server object) => object.id, - setId: (Server object, int id) { - object.id = id; - }, - objectToFB: (Server object, fb.Builder fbb) { - final urlOffset = fbb.writeString(object.url); - final authTokenOffset = object.authToken == null - ? null - : fbb.writeString(object.authToken!); - final sidCookieOffset = object.sidCookie == null - ? null - : fbb.writeString(object.sidCookie!); - fbb.startTable(8); - fbb.addInt64(0, object.id); - fbb.addOffset(1, urlOffset); - fbb.addOffset(2, authTokenOffset); - fbb.addBool(5, object.inUse); - fbb.addOffset(6, sidCookieOffset); - fbb.finish(fbb.endTable()); - return object.id; - }, - objectFromFB: (Store store, ByteData fbData) { - final buffer = fb.BufferContext(fbData); - final rootOffset = buffer.derefObject(0); - final urlParam = const fb.StringReader(asciiOptimization: true) - .vTableGet(buffer, rootOffset, 6, ''); - final authTokenParam = const fb.StringReader(asciiOptimization: true) - .vTableGetNullable(buffer, rootOffset, 8); - final sidCookieParam = const fb.StringReader(asciiOptimization: true) - .vTableGetNullable(buffer, rootOffset, 16); - final inUseParam = - const fb.BoolReader().vTableGet(buffer, rootOffset, 14, false); - final object = Server( - url: urlParam, - authToken: authTokenParam, - sidCookie: sidCookieParam, - inUse: inUseParam) - ..id = const fb.Int64Reader().vTableGet(buffer, rootOffset, 4, 0); - - return object; - }), - SettingsValue: EntityDefinition( - model: _entities[1], - toOneRelations: (SettingsValue object) => [], - toManyRelations: (SettingsValue object) => {}, - getId: (SettingsValue object) => object.id, - setId: (SettingsValue object, int id) { - object.id = id; - }, - objectToFB: (SettingsValue object, fb.Builder fbb) { - final nameOffset = fbb.writeString(object.name); - final valueOffset = fbb.writeString(object.value); - fbb.startTable(4); - fbb.addInt64(0, object.id); - fbb.addOffset(1, nameOffset); - fbb.addOffset(2, valueOffset); - fbb.finish(fbb.endTable()); - return object.id; - }, - objectFromFB: (Store store, ByteData fbData) { - final buffer = fb.BufferContext(fbData); - final rootOffset = buffer.derefObject(0); - final nameParam = const fb.StringReader(asciiOptimization: true) - .vTableGet(buffer, rootOffset, 6, ''); - final valueParam = const fb.StringReader(asciiOptimization: true) - .vTableGet(buffer, rootOffset, 8, ''); - final object = SettingsValue(nameParam, valueParam) - ..id = const fb.Int64Reader().vTableGet(buffer, rootOffset, 4, 0); - - return object; - }), - Progress: EntityDefinition( - model: _entities[2], - toOneRelations: (Progress object) => [], - toManyRelations: (Progress object) => {}, - getId: (Progress object) => object.id, - setId: (Progress object, int id) { - object.id = id; - }, - objectToFB: (Progress object, fb.Builder fbb) { - final videoIdOffset = fbb.writeString(object.videoId); - fbb.startTable(4); - fbb.addInt64(0, object.id); - fbb.addFloat64(1, object.progress); - fbb.addOffset(2, videoIdOffset); - fbb.finish(fbb.endTable()); - return object.id; - }, - objectFromFB: (Store store, ByteData fbData) { - final buffer = fb.BufferContext(fbData); - final rootOffset = buffer.derefObject(0); - final idParam = - const fb.Int64Reader().vTableGet(buffer, rootOffset, 4, 0); - final progressParam = - const fb.Float64Reader().vTableGet(buffer, rootOffset, 6, 0); - final videoIdParam = const fb.StringReader(asciiOptimization: true) - .vTableGet(buffer, rootOffset, 8, ''); - final object = Progress(idParam, progressParam, videoIdParam); - - return object; - }), - SearchHistoryItem: EntityDefinition( - model: _entities[3], - toOneRelations: (SearchHistoryItem object) => [], - toManyRelations: (SearchHistoryItem object) => {}, - getId: (SearchHistoryItem object) => object.id, - setId: (SearchHistoryItem object, int id) { - object.id = id; - }, - objectToFB: (SearchHistoryItem object, fb.Builder fbb) { - final searchOffset = fbb.writeString(object.search); - fbb.startTable(4); - fbb.addInt64(0, object.id); - fbb.addOffset(1, searchOffset); - fbb.addInt64(2, object.time); - fbb.finish(fbb.endTable()); - return object.id; - }, - objectFromFB: (Store store, ByteData fbData) { - final buffer = fb.BufferContext(fbData); - final rootOffset = buffer.derefObject(0); - final searchParam = const fb.StringReader(asciiOptimization: true) - .vTableGet(buffer, rootOffset, 6, ''); - final timeParam = - const fb.Int64Reader().vTableGet(buffer, rootOffset, 8, 0); - final object = SearchHistoryItem(searchParam, timeParam) - ..id = const fb.Int64Reader().vTableGet(buffer, rootOffset, 4, 0); - - return object; - }), - AppLog: EntityDefinition( - model: _entities[4], - toOneRelations: (AppLog object) => [], - toManyRelations: (AppLog object) => {}, - getId: (AppLog object) => object.id, - setId: (AppLog object, int id) { - object.id = id; - }, - objectToFB: (AppLog object, fb.Builder fbb) { - final levelOffset = fbb.writeString(object.level); - final messageOffset = - object.message == null ? null : fbb.writeString(object.message!); - final stacktraceOffset = object.stacktrace == null - ? null - : fbb.writeString(object.stacktrace!); - final loggerOffset = fbb.writeString(object.logger); - fbb.startTable(7); - fbb.addInt64(0, object.id); - fbb.addOffset(1, levelOffset); - fbb.addInt64(2, object.time.millisecondsSinceEpoch); - fbb.addOffset(3, messageOffset); - fbb.addOffset(4, stacktraceOffset); - fbb.addOffset(5, loggerOffset); - fbb.finish(fbb.endTable()); - return object.id; - }, - objectFromFB: (Store store, ByteData fbData) { - final buffer = fb.BufferContext(fbData); - final rootOffset = buffer.derefObject(0); - final levelParam = const fb.StringReader(asciiOptimization: true) - .vTableGet(buffer, rootOffset, 6, ''); - final loggerParam = const fb.StringReader(asciiOptimization: true) - .vTableGet(buffer, rootOffset, 14, ''); - final timeParam = DateTime.fromMillisecondsSinceEpoch( - const fb.Int64Reader().vTableGet(buffer, rootOffset, 8, 0)); - final messageParam = const fb.StringReader(asciiOptimization: true) - .vTableGetNullable(buffer, rootOffset, 10); - final stacktraceParam = const fb.StringReader(asciiOptimization: true) - .vTableGetNullable(buffer, rootOffset, 12); - final object = AppLog( - level: levelParam, - logger: loggerParam, - time: timeParam, - message: messageParam, - stacktrace: stacktraceParam) - ..id = const fb.Int64Reader().vTableGet(buffer, rootOffset, 4, 0); - - return object; - }), - VideoFilter: EntityDefinition( - model: _entities[5], - toOneRelations: (VideoFilter object) => [], - toManyRelations: (VideoFilter object) => {}, - getId: (VideoFilter object) => object.id, - setId: (VideoFilter object, int id) { - object.id = id; - }, - objectToFB: (VideoFilter object, fb.Builder fbb) { - final channelIdOffset = object.channelId == null - ? null - : fbb.writeString(object.channelId!); - final valueOffset = - object.value == null ? null : fbb.writeString(object.value!); - final dbTypeOffset = - object.dbType == null ? null : fbb.writeString(object.dbType!); - final dbOperationOffset = object.dbOperation == null - ? null - : fbb.writeString(object.dbOperation!); - final daysOfWeekOffset = fbb.writeListInt64(object.daysOfWeek); - final startTimeOffset = fbb.writeString(object.startTime); - final endTimeOffset = fbb.writeString(object.endTime); - fbb.startTable(12); - fbb.addInt64(0, object.id); - fbb.addOffset(1, channelIdOffset); - fbb.addOffset(2, valueOffset); - fbb.addOffset(3, dbTypeOffset); - fbb.addOffset(4, dbOperationOffset); - fbb.addBool(6, object.filterAll); - fbb.addBool(7, object.hideFromFeed); - fbb.addOffset(8, daysOfWeekOffset); - fbb.addOffset(9, startTimeOffset); - fbb.addOffset(10, endTimeOffset); - fbb.finish(fbb.endTable()); - return object.id; - }, - objectFromFB: (Store store, ByteData fbData) { - final buffer = fb.BufferContext(fbData); - final rootOffset = buffer.derefObject(0); - final valueParam = const fb.StringReader(asciiOptimization: true) - .vTableGetNullable(buffer, rootOffset, 8); - final channelIdParam = const fb.StringReader(asciiOptimization: true) - .vTableGetNullable(buffer, rootOffset, 6); - final object = VideoFilter( - value: valueParam, channelId: channelIdParam) - ..id = const fb.Int64Reader().vTableGet(buffer, rootOffset, 4, 0) - ..dbType = const fb.StringReader(asciiOptimization: true) - .vTableGetNullable(buffer, rootOffset, 10) - ..dbOperation = const fb.StringReader(asciiOptimization: true) - .vTableGetNullable(buffer, rootOffset, 12) - ..filterAll = - const fb.BoolReader().vTableGet(buffer, rootOffset, 16, false) - ..hideFromFeed = - const fb.BoolReader().vTableGet(buffer, rootOffset, 18, false) - ..daysOfWeek = - const fb.ListReader(fb.Int64Reader(), lazy: false) - .vTableGet(buffer, rootOffset, 20, []) - ..startTime = const fb.StringReader(asciiOptimization: true) - .vTableGet(buffer, rootOffset, 22, '') - ..endTime = const fb.StringReader(asciiOptimization: true) - .vTableGet(buffer, rootOffset, 24, ''); - - return object; - }), - DownloadedVideo: EntityDefinition( - model: _entities[6], - toOneRelations: (DownloadedVideo object) => [], - toManyRelations: (DownloadedVideo object) => {}, - getId: (DownloadedVideo object) => object.id, - setId: (DownloadedVideo object, int id) { - object.id = id; - }, - objectToFB: (DownloadedVideo object, fb.Builder fbb) { - final titleOffset = fbb.writeString(object.title); - final authorOffset = - object.author == null ? null : fbb.writeString(object.author!); - final authorUrlOffset = object.authorUrl == null - ? null - : fbb.writeString(object.authorUrl!); - final qualityOffset = fbb.writeString(object.quality); - final videoIdOffset = fbb.writeString(object.videoId); - fbb.startTable(13); - fbb.addInt64(0, object.id); - fbb.addOffset(2, titleOffset); - fbb.addOffset(3, authorOffset); - fbb.addOffset(4, authorUrlOffset); - fbb.addBool(5, object.downloadComplete); - fbb.addBool(6, object.downloadFailed); - fbb.addBool(7, object.audioOnly); - fbb.addOffset(9, qualityOffset); - fbb.addInt64(10, object.lengthSeconds); - fbb.addOffset(11, videoIdOffset); - fbb.finish(fbb.endTable()); - return object.id; - }, - objectFromFB: (Store store, ByteData fbData) { - final buffer = fb.BufferContext(fbData); - final rootOffset = buffer.derefObject(0); - final idParam = - const fb.Int64Reader().vTableGet(buffer, rootOffset, 4, 0); - final videoIdParam = const fb.StringReader(asciiOptimization: true) - .vTableGet(buffer, rootOffset, 26, ''); - final titleParam = const fb.StringReader(asciiOptimization: true) - .vTableGet(buffer, rootOffset, 8, ''); - final authorParam = const fb.StringReader(asciiOptimization: true) - .vTableGetNullable(buffer, rootOffset, 10); - final authorUrlParam = const fb.StringReader(asciiOptimization: true) - .vTableGetNullable(buffer, rootOffset, 12); - final downloadCompleteParam = - const fb.BoolReader().vTableGet(buffer, rootOffset, 14, false); - final downloadFailedParam = - const fb.BoolReader().vTableGet(buffer, rootOffset, 16, false); - final audioOnlyParam = - const fb.BoolReader().vTableGet(buffer, rootOffset, 18, false); - final lengthSecondsParam = - const fb.Int64Reader().vTableGet(buffer, rootOffset, 24, 0); - final qualityParam = const fb.StringReader(asciiOptimization: true) - .vTableGet(buffer, rootOffset, 22, ''); - final object = DownloadedVideo( - id: idParam, - videoId: videoIdParam, - title: titleParam, - author: authorParam, - authorUrl: authorUrlParam, - downloadComplete: downloadCompleteParam, - downloadFailed: downloadFailedParam, - audioOnly: audioOnlyParam, - lengthSeconds: lengthSecondsParam, - quality: qualityParam); - - return object; - }), - HistoryVideoCache: EntityDefinition( - model: _entities[7], - toOneRelations: (HistoryVideoCache object) => [], - toManyRelations: (HistoryVideoCache object) => {}, - getId: (HistoryVideoCache object) => object.id, - setId: (HistoryVideoCache object, int id) { - object.id = id; - }, - objectToFB: (HistoryVideoCache object, fb.Builder fbb) { - final titleOffset = fbb.writeString(object.title); - final authorOffset = - object.author == null ? null : fbb.writeString(object.author!); - final videoIdOffset = fbb.writeString(object.videoId); - final thumbnailOffset = fbb.writeString(object.thumbnail); - fbb.startTable(7); - fbb.addInt64(0, object.id); - fbb.addOffset(1, titleOffset); - fbb.addOffset(2, authorOffset); - fbb.addOffset(3, videoIdOffset); - fbb.addInt64(4, object.created.millisecondsSinceEpoch); - fbb.addOffset(5, thumbnailOffset); - fbb.finish(fbb.endTable()); - return object.id; - }, - objectFromFB: (Store store, ByteData fbData) { - final buffer = fb.BufferContext(fbData); - final rootOffset = buffer.derefObject(0); - final videoIdParam = const fb.StringReader(asciiOptimization: true) - .vTableGet(buffer, rootOffset, 10, ''); - final titleParam = const fb.StringReader(asciiOptimization: true) - .vTableGet(buffer, rootOffset, 6, ''); - final authorParam = const fb.StringReader(asciiOptimization: true) - .vTableGetNullable(buffer, rootOffset, 8); - final thumbnailParam = const fb.StringReader(asciiOptimization: true) - .vTableGet(buffer, rootOffset, 14, ''); - final object = HistoryVideoCache( - videoIdParam, titleParam, authorParam, thumbnailParam) - ..id = const fb.Int64Reader().vTableGet(buffer, rootOffset, 4, 0) - ..created = DateTime.fromMillisecondsSinceEpoch( - const fb.Int64Reader().vTableGet(buffer, rootOffset, 12, 0)); - - return object; - }), - HomeLayout: EntityDefinition( - model: _entities[8], - toOneRelations: (HomeLayout object) => [], - toManyRelations: (HomeLayout object) => {}, - getId: (HomeLayout object) => object.id, - setId: (HomeLayout object, int id) { - object.id = id; - }, - objectToFB: (HomeLayout object, fb.Builder fbb) { - final dbBigSourceOffset = fbb.writeString(object.dbBigSource); - final dbSmallSourcesOffset = fbb.writeList(object.dbSmallSources - .map(fbb.writeString) - .toList(growable: false)); - fbb.startTable(5); - fbb.addInt64(0, object.id); - fbb.addBool(1, object.showBigSource); - fbb.addOffset(2, dbBigSourceOffset); - fbb.addOffset(3, dbSmallSourcesOffset); - fbb.finish(fbb.endTable()); - return object.id; - }, - objectFromFB: (Store store, ByteData fbData) { - final buffer = fb.BufferContext(fbData); - final rootOffset = buffer.derefObject(0); - - final object = HomeLayout() - ..id = const fb.Int64Reader().vTableGet(buffer, rootOffset, 4, 0) - ..showBigSource = - const fb.BoolReader().vTableGet(buffer, rootOffset, 6, false) - ..dbBigSource = const fb.StringReader(asciiOptimization: true) - .vTableGet(buffer, rootOffset, 8, '') - ..dbSmallSources = const fb.ListReader( - fb.StringReader(asciiOptimization: true), - lazy: false) - .vTableGet(buffer, rootOffset, 10, []); - - return object; - }), - SubscriptionNotification: EntityDefinition( - model: _entities[9], - toOneRelations: (SubscriptionNotification object) => [], - toManyRelations: (SubscriptionNotification object) => {}, - getId: (SubscriptionNotification object) => object.id, - setId: (SubscriptionNotification object, int id) { - object.id = id; - }, - objectToFB: (SubscriptionNotification object, fb.Builder fbb) { - final lastSeenVideoIdOffset = fbb.writeString(object.lastSeenVideoId); - fbb.startTable(4); - fbb.addInt64(0, object.id); - fbb.addOffset(1, lastSeenVideoIdOffset); - fbb.addInt64(2, object.timestamp); - fbb.finish(fbb.endTable()); - return object.id; - }, - objectFromFB: (Store store, ByteData fbData) { - final buffer = fb.BufferContext(fbData); - final rootOffset = buffer.derefObject(0); - final lastSeenVideoIdParam = - const fb.StringReader(asciiOptimization: true) - .vTableGet(buffer, rootOffset, 6, ''); - final timestampParam = - const fb.Int64Reader().vTableGet(buffer, rootOffset, 8, 0); - final object = SubscriptionNotification( - lastSeenVideoIdParam, timestampParam) - ..id = const fb.Int64Reader().vTableGet(buffer, rootOffset, 4, 0); - - return object; - }), - ChannelNotification: EntityDefinition( - model: _entities[10], - toOneRelations: (ChannelNotification object) => [], - toManyRelations: (ChannelNotification object) => {}, - getId: (ChannelNotification object) => object.id, - setId: (ChannelNotification object, int id) { - object.id = id; - }, - objectToFB: (ChannelNotification object, fb.Builder fbb) { - final channelIdOffset = fbb.writeString(object.channelId); - final lastSeenVideoIdOffset = fbb.writeString(object.lastSeenVideoId); - final channelNameOffset = fbb.writeString(object.channelName); - fbb.startTable(6); - fbb.addInt64(0, object.id); - fbb.addOffset(1, channelIdOffset); - fbb.addOffset(2, lastSeenVideoIdOffset); - fbb.addInt64(3, object.timestamp); - fbb.addOffset(4, channelNameOffset); - fbb.finish(fbb.endTable()); - return object.id; - }, - objectFromFB: (Store store, ByteData fbData) { - final buffer = fb.BufferContext(fbData); - final rootOffset = buffer.derefObject(0); - final channelIdParam = const fb.StringReader(asciiOptimization: true) - .vTableGet(buffer, rootOffset, 6, ''); - final channelNameParam = - const fb.StringReader(asciiOptimization: true) - .vTableGet(buffer, rootOffset, 12, ''); - final lastSeenVideoIdParam = - const fb.StringReader(asciiOptimization: true) - .vTableGet(buffer, rootOffset, 8, ''); - final timestampParam = - const fb.Int64Reader().vTableGet(buffer, rootOffset, 10, 0); - final object = ChannelNotification(channelIdParam, channelNameParam, - lastSeenVideoIdParam, timestampParam) - ..id = const fb.Int64Reader().vTableGet(buffer, rootOffset, 4, 0); - - return object; - }), - PlaylistNotification: EntityDefinition( - model: _entities[11], - toOneRelations: (PlaylistNotification object) => [], - toManyRelations: (PlaylistNotification object) => {}, - getId: (PlaylistNotification object) => object.id, - setId: (PlaylistNotification object, int id) { - object.id = id; - }, - objectToFB: (PlaylistNotification object, fb.Builder fbb) { - final playlistIdOffset = fbb.writeString(object.playlistId); - final playlistNameOffset = fbb.writeString(object.playlistName); - fbb.startTable(6); - fbb.addInt64(0, object.id); - fbb.addOffset(1, playlistIdOffset); - fbb.addInt64(2, object.lastVideoCount); - fbb.addInt64(3, object.timestamp); - fbb.addOffset(4, playlistNameOffset); - fbb.finish(fbb.endTable()); - return object.id; - }, - objectFromFB: (Store store, ByteData fbData) { - final buffer = fb.BufferContext(fbData); - final rootOffset = buffer.derefObject(0); - final playlistIdParam = const fb.StringReader(asciiOptimization: true) - .vTableGet(buffer, rootOffset, 6, ''); - final lastVideoCountParam = - const fb.Int64Reader().vTableGet(buffer, rootOffset, 8, 0); - final timestampParam = - const fb.Int64Reader().vTableGet(buffer, rootOffset, 10, 0); - final playlistNameParam = - const fb.StringReader(asciiOptimization: true) - .vTableGet(buffer, rootOffset, 12, ''); - final object = PlaylistNotification(playlistIdParam, - lastVideoCountParam, timestampParam, playlistNameParam) - ..id = const fb.Int64Reader().vTableGet(buffer, rootOffset, 4, 0); - - return object; - }), - DeArrowCache: EntityDefinition( - model: _entities[12], - toOneRelations: (DeArrowCache object) => [], - toManyRelations: (DeArrowCache object) => {}, - getId: (DeArrowCache object) => object.id, - setId: (DeArrowCache object, int id) { - object.id = id; - }, - objectToFB: (DeArrowCache object, fb.Builder fbb) { - final videoIdOffset = fbb.writeString(object.videoId); - final titleOffset = - object.title == null ? null : fbb.writeString(object.title!); - final urlOffset = - object.url == null ? null : fbb.writeString(object.url!); - fbb.startTable(5); - fbb.addInt64(0, object.id); - fbb.addOffset(1, videoIdOffset); - fbb.addOffset(2, titleOffset); - fbb.addOffset(3, urlOffset); - fbb.finish(fbb.endTable()); - return object.id; - }, - objectFromFB: (Store store, ByteData fbData) { - final buffer = fb.BufferContext(fbData); - final rootOffset = buffer.derefObject(0); - final videoIdParam = const fb.StringReader(asciiOptimization: true) - .vTableGet(buffer, rootOffset, 6, ''); - final object = DeArrowCache(videoIdParam) - ..id = const fb.Int64Reader().vTableGet(buffer, rootOffset, 4, 0) - ..title = const fb.StringReader(asciiOptimization: true) - .vTableGetNullable(buffer, rootOffset, 8) - ..url = const fb.StringReader(asciiOptimization: true) - .vTableGetNullable(buffer, rootOffset, 10); - - return object; - }) - }; - - return ModelDefinition(model, bindings); -} - -/// [Server] entity fields to define ObjectBox queries. -class Server_ { - /// see [Server.id] - static final id = QueryIntegerProperty(_entities[0].properties[0]); - - /// see [Server.url] - static final url = QueryStringProperty(_entities[0].properties[1]); - - /// see [Server.authToken] - static final authToken = - QueryStringProperty(_entities[0].properties[2]); - - /// see [Server.inUse] - static final inUse = QueryBooleanProperty(_entities[0].properties[3]); - - /// see [Server.sidCookie] - static final sidCookie = - QueryStringProperty(_entities[0].properties[4]); -} - -/// [SettingsValue] entity fields to define ObjectBox queries. -class SettingsValue_ { - /// see [SettingsValue.id] - static final id = - QueryIntegerProperty(_entities[1].properties[0]); - - /// see [SettingsValue.name] - static final name = - QueryStringProperty(_entities[1].properties[1]); - - /// see [SettingsValue.value] - static final value = - QueryStringProperty(_entities[1].properties[2]); -} - -/// [Progress] entity fields to define ObjectBox queries. -class Progress_ { - /// see [Progress.id] - static final id = QueryIntegerProperty(_entities[2].properties[0]); - - /// see [Progress.progress] - static final progress = - QueryDoubleProperty(_entities[2].properties[1]); - - /// see [Progress.videoId] - static final videoId = - QueryStringProperty(_entities[2].properties[2]); -} - -/// [SearchHistoryItem] entity fields to define ObjectBox queries. -class SearchHistoryItem_ { - /// see [SearchHistoryItem.id] - static final id = - QueryIntegerProperty(_entities[3].properties[0]); - - /// see [SearchHistoryItem.search] - static final search = - QueryStringProperty(_entities[3].properties[1]); - - /// see [SearchHistoryItem.time] - static final time = - QueryIntegerProperty(_entities[3].properties[2]); -} - -/// [AppLog] entity fields to define ObjectBox queries. -class AppLog_ { - /// see [AppLog.id] - static final id = QueryIntegerProperty(_entities[4].properties[0]); - - /// see [AppLog.level] - static final level = QueryStringProperty(_entities[4].properties[1]); - - /// see [AppLog.time] - static final time = QueryIntegerProperty(_entities[4].properties[2]); - - /// see [AppLog.message] - static final message = - QueryStringProperty(_entities[4].properties[3]); - - /// see [AppLog.stacktrace] - static final stacktrace = - QueryStringProperty(_entities[4].properties[4]); - - /// see [AppLog.logger] - static final logger = QueryStringProperty(_entities[4].properties[5]); -} - -/// [VideoFilter] entity fields to define ObjectBox queries. -class VideoFilter_ { - /// see [VideoFilter.id] - static final id = - QueryIntegerProperty(_entities[5].properties[0]); - - /// see [VideoFilter.channelId] - static final channelId = - QueryStringProperty(_entities[5].properties[1]); - - /// see [VideoFilter.value] - static final value = - QueryStringProperty(_entities[5].properties[2]); - - /// see [VideoFilter.dbType] - static final dbType = - QueryStringProperty(_entities[5].properties[3]); - - /// see [VideoFilter.dbOperation] - static final dbOperation = - QueryStringProperty(_entities[5].properties[4]); - - /// see [VideoFilter.filterAll] - static final filterAll = - QueryBooleanProperty(_entities[5].properties[5]); - - /// see [VideoFilter.hideFromFeed] - static final hideFromFeed = - QueryBooleanProperty(_entities[5].properties[6]); - - /// see [VideoFilter.daysOfWeek] - static final daysOfWeek = - QueryIntegerVectorProperty(_entities[5].properties[7]); - - /// see [VideoFilter.startTime] - static final startTime = - QueryStringProperty(_entities[5].properties[8]); - - /// see [VideoFilter.endTime] - static final endTime = - QueryStringProperty(_entities[5].properties[9]); -} - -/// [DownloadedVideo] entity fields to define ObjectBox queries. -class DownloadedVideo_ { - /// see [DownloadedVideo.id] - static final id = - QueryIntegerProperty(_entities[6].properties[0]); - - /// see [DownloadedVideo.title] - static final title = - QueryStringProperty(_entities[6].properties[1]); - - /// see [DownloadedVideo.author] - static final author = - QueryStringProperty(_entities[6].properties[2]); - - /// see [DownloadedVideo.authorUrl] - static final authorUrl = - QueryStringProperty(_entities[6].properties[3]); - - /// see [DownloadedVideo.downloadComplete] - static final downloadComplete = - QueryBooleanProperty(_entities[6].properties[4]); - - /// see [DownloadedVideo.downloadFailed] - static final downloadFailed = - QueryBooleanProperty(_entities[6].properties[5]); - - /// see [DownloadedVideo.audioOnly] - static final audioOnly = - QueryBooleanProperty(_entities[6].properties[6]); - - /// see [DownloadedVideo.quality] - static final quality = - QueryStringProperty(_entities[6].properties[7]); - - /// see [DownloadedVideo.lengthSeconds] - static final lengthSeconds = - QueryIntegerProperty(_entities[6].properties[8]); - - /// see [DownloadedVideo.videoId] - static final videoId = - QueryStringProperty(_entities[6].properties[9]); -} - -/// [HistoryVideoCache] entity fields to define ObjectBox queries. -class HistoryVideoCache_ { - /// see [HistoryVideoCache.id] - static final id = - QueryIntegerProperty(_entities[7].properties[0]); - - /// see [HistoryVideoCache.title] - static final title = - QueryStringProperty(_entities[7].properties[1]); - - /// see [HistoryVideoCache.author] - static final author = - QueryStringProperty(_entities[7].properties[2]); - - /// see [HistoryVideoCache.videoId] - static final videoId = - QueryStringProperty(_entities[7].properties[3]); - - /// see [HistoryVideoCache.created] - static final created = - QueryIntegerProperty(_entities[7].properties[4]); - - /// see [HistoryVideoCache.thumbnail] - static final thumbnail = - QueryStringProperty(_entities[7].properties[5]); -} - -/// [HomeLayout] entity fields to define ObjectBox queries. -class HomeLayout_ { - /// see [HomeLayout.id] - static final id = - QueryIntegerProperty(_entities[8].properties[0]); - - /// see [HomeLayout.showBigSource] - static final showBigSource = - QueryBooleanProperty(_entities[8].properties[1]); - - /// see [HomeLayout.dbBigSource] - static final dbBigSource = - QueryStringProperty(_entities[8].properties[2]); - - /// see [HomeLayout.dbSmallSources] - static final dbSmallSources = - QueryStringVectorProperty(_entities[8].properties[3]); -} - -/// [SubscriptionNotification] entity fields to define ObjectBox queries. -class SubscriptionNotification_ { - /// see [SubscriptionNotification.id] - static final id = QueryIntegerProperty( - _entities[9].properties[0]); - - /// see [SubscriptionNotification.lastSeenVideoId] - static final lastSeenVideoId = - QueryStringProperty(_entities[9].properties[1]); - - /// see [SubscriptionNotification.timestamp] - static final timestamp = QueryIntegerProperty( - _entities[9].properties[2]); -} - -/// [ChannelNotification] entity fields to define ObjectBox queries. -class ChannelNotification_ { - /// see [ChannelNotification.id] - static final id = - QueryIntegerProperty(_entities[10].properties[0]); - - /// see [ChannelNotification.channelId] - static final channelId = - QueryStringProperty(_entities[10].properties[1]); - - /// see [ChannelNotification.lastSeenVideoId] - static final lastSeenVideoId = - QueryStringProperty(_entities[10].properties[2]); - - /// see [ChannelNotification.timestamp] - static final timestamp = - QueryIntegerProperty(_entities[10].properties[3]); - - /// see [ChannelNotification.channelName] - static final channelName = - QueryStringProperty(_entities[10].properties[4]); -} - -/// [PlaylistNotification] entity fields to define ObjectBox queries. -class PlaylistNotification_ { - /// see [PlaylistNotification.id] - static final id = - QueryIntegerProperty(_entities[11].properties[0]); - - /// see [PlaylistNotification.playlistId] - static final playlistId = - QueryStringProperty(_entities[11].properties[1]); - - /// see [PlaylistNotification.lastVideoCount] - static final lastVideoCount = - QueryIntegerProperty(_entities[11].properties[2]); - - /// see [PlaylistNotification.timestamp] - static final timestamp = - QueryIntegerProperty(_entities[11].properties[3]); - - /// see [PlaylistNotification.playlistName] - static final playlistName = - QueryStringProperty(_entities[11].properties[4]); -} - -/// [DeArrowCache] entity fields to define ObjectBox queries. -class DeArrowCache_ { - /// see [DeArrowCache.id] - static final id = - QueryIntegerProperty(_entities[12].properties[0]); - - /// see [DeArrowCache.videoId] - static final videoId = - QueryStringProperty(_entities[12].properties[1]); - - /// see [DeArrowCache.title] - static final title = - QueryStringProperty(_entities[12].properties[2]); - - /// see [DeArrowCache.url] - static final url = - QueryStringProperty(_entities[12].properties[3]); -} diff --git a/lib/player/states/player.dart b/lib/player/states/player.dart index 8f7152a3..89c609ee 100644 --- a/lib/player/states/player.dart +++ b/lib/player/states/player.dart @@ -38,6 +38,8 @@ const skipToVideoThrottleName = 'skip-to-video'; const double bigPlayerThreshold = 700; const stepMultiplier = 1.15; +const maxInt = -1 >>> 1; + var log = Logger('MiniPlayerController'); enum PlayerRepeat { noRepeat, repeatAll, repeatOne } @@ -277,7 +279,7 @@ class PlayerCubit extends Cubit with WidgetsBindingObserver { } */ - saveProgress(int timeInSeconds) { + saveProgress(int timeInSeconds) async { if (state.currentlyPlaying != null) { int currentPosition = timeInSeconds; // saving progress @@ -290,12 +292,12 @@ class PlayerCubit extends Cubit with WidgetsBindingObserver { var progress = db_progress.Progress.named( progress: currentProgress, videoId: state.currentlyPlaying!.videoId); - db.saveProgress(progress); + await db.saveProgress(progress); if (progress.progress > 0.1) { EasyThrottle.throttle('invidious-progress-sync-${progress.videoId}', - const Duration(minutes: 10), () { - if (service.isLoggedIn()) { + const Duration(minutes: 10), () async { + if (await service.isLoggedIn()) { service.addToUserHistory(progress.videoId); } }); @@ -346,26 +348,35 @@ class PlayerCubit extends Cubit with WidgetsBindingObserver { } } - onProgress(Duration? position) { + onProgress(Duration? position) async { var newPosition = position ?? Duration.zero; int currentPosition = newPosition.inSeconds; - saveProgress(currentPosition); - log.fine("video event"); + await saveProgress(currentPosition); + log.fine("video progress event"); emit(state.copyWith(position: newPosition)); - if (state.sponsorSegments.isNotEmpty) { + // if we're already within the last 5 seconds we don't skip to avoid infinite skip loop on late outro segment + if (state.sponsorSegments.isNotEmpty && + state.position.inSeconds < + (state.currentlyPlaying?.lengthSeconds ?? maxInt) - 5) { + log.fine('sponsor skipped'); double positionInMs = currentPosition * 1000; Pair nextSegment = state.sponsorSegments.firstWhere( (e) => e.first <= positionInMs && positionInMs <= e.last, orElse: () => const Pair(-1, -1)); + if (nextSegment.first != -1) { + // sometimes when reaching the end of a video, the sponsor skip keeps on being triggered + // we remove on second of last segment if it is the case + var skipTo = nextSegment.last + 1000; + emit(state.copyWith( mediaEvent: const MediaEvent( state: MediaState.playing, type: MediaEventType.sponsorSkipped))); //for some reasons this needs to be last - seek(Duration(milliseconds: nextSegment.last + 1000)); + seek(Duration(milliseconds: skipTo + 1000)); /* final ScaffoldMessengerState? scaffold = scaffoldKey.currentState; diff --git a/lib/player/states/video_player.dart b/lib/player/states/video_player.dart index d08f8f8c..8eff59a7 100644 --- a/lib/player/states/video_player.dart +++ b/lib/player/states/video_player.dart @@ -178,16 +178,16 @@ class VideoPlayerCubit extends MediaPlayerCubit { } @override - toggleDash() { + toggleDash() async { log.fine('toggle dash'); var state = this.state.copyWith(); // saving progress before we reload new video format // saveProgress(videoController?.videoPlayerController?.value.position.inSeconds ?? 0); - player.saveProgress(position().inSeconds); + await player.saveProgress(position().inSeconds); // disposeControllers(); - settings.toggleDash(!isUsingDash()); + await settings.toggleDash(!isUsingDash()); emit(state); playVideo(false); @@ -253,7 +253,7 @@ class VideoPlayerCubit extends MediaPlayerCubit { liveStream: false, ); } else { - String baseUrl = db.getCurrentlySelectedServer().url; + String baseUrl = (await db.getCurrentlySelectedServer()).url; Map resolutions = {}; diff --git a/lib/player/views/components/video_queue.dart b/lib/player/views/components/video_queue.dart index 22fe5175..a3264fc9 100644 --- a/lib/player/views/components/video_queue.dart +++ b/lib/player/views/components/video_queue.dart @@ -48,7 +48,7 @@ class VideoQueue extends StatelessWidget { bool isPlaying = state.offlineCurrentlyPlaying?.videoId == v.videoId; return SwipeActionCell( - key: ValueKey('$index-${v.id}'), + key: ValueKey('$index-${v.videoId}'), trailingActions: isPlaying ? [] : [ diff --git a/lib/router.dart b/lib/router.dart index 4ab0a74a..011d3dc0 100644 --- a/lib/router.dart +++ b/lib/router.dart @@ -3,7 +3,6 @@ import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:invidious/app/views/screens/main.dart'; import 'package:invidious/app/views/tv/screens/tv_home.dart'; -import 'package:invidious/globals.dart'; import 'package:invidious/player/states/player.dart'; import 'package:invidious/player/views/tv/screens/tv_player_view.dart'; import 'package:invidious/playlists/models/playlist.dart'; @@ -54,6 +53,7 @@ import 'package:logging/logging.dart'; import 'channels/views/screens/channel.dart'; import 'channels/views/tv/screens/channel.dart'; +import 'db_reset/views/screen/reset.dart'; import 'downloads/views/screens/download_manager.dart'; import 'home/views/screens/edit_layout.dart'; import 'home/views/screens/home.dart'; @@ -64,28 +64,28 @@ part 'router.gr.dart'; const pathManageSingleServerFromWizard = '/wizard/manage-single-server'; const pathManageSingleServerFromSettings = 'manage-single-server'; -final appRouter = AppRouter(); +late AppRouter appRouter; final log = Logger('Router'); @AutoRouterConfig(replaceInRouteName: 'Screen,Route') class AppRouter extends _$AppRouter { + final bool needsDbMigration; + final bool hasServer; + + AppRouter({required this.needsDbMigration, required this.hasServer}); + @override List get routes { - bool hasServer = false; - try { - db.getCurrentlySelectedServer(); - hasServer = true; - } catch (e) { - hasServer = false; - } return isTv ? [ AutoRoute( page: TvHomeRoute.page, - initial: hasServer, + initial: !needsDbMigration && hasServer, ), - AutoRoute(page: TvWelcomeWizardRoute.page, initial: !hasServer), + AutoRoute( + page: TvWelcomeWizardRoute.page, + initial: !needsDbMigration && !hasServer), AutoRoute(page: TvChannelRoute.page), AutoRoute(page: TvGridRoute.page), AutoRoute(page: TvVideoRoute.page), @@ -105,12 +105,13 @@ class AppRouter extends _$AppRouter { AutoRoute(page: TvFilterEditSettingsRoute.page), AutoRoute(page: TvFilterListSettingsRoute.page), AutoRoute(page: TvTimePickerRoute.page), - AutoRoute(page: TvPlainTextRoute.page) + AutoRoute(page: TvPlainTextRoute.page), + AutoRoute(page: MigrationRoute.page, initial: needsDbMigration) ] : [ AutoRoute( page: MainRoute.page, - initial: hasServer, + initial: !needsDbMigration && hasServer, children: [ AutoRoute(page: HomeRoute.page, initial: true), AutoRoute(page: VideoRoute.page), @@ -135,13 +136,16 @@ class AppRouter extends _$AppRouter { AutoRoute(page: AppLogsRoute.page), AutoRoute(page: PlaylistViewRoute.page), AutoRoute(page: SubscriptionRoute.page), - AutoRoute(page: DeArrowSettingsRoute.page) + AutoRoute(page: DeArrowSettingsRoute.page), ], ), + AutoRoute(page: MigrationRoute.page, initial: needsDbMigration), AutoRoute( page: ManageSingleServerRoute.page, path: pathManageSingleServerFromWizard), - AutoRoute(page: WelcomeWizardRoute.page, initial: !hasServer) + AutoRoute( + page: WelcomeWizardRoute.page, + initial: !needsDbMigration && !hasServer) ]; } } diff --git a/lib/router.gr.dart b/lib/router.gr.dart index fe27325d..967004ce 100644 --- a/lib/router.gr.dart +++ b/lib/router.gr.dart @@ -95,6 +95,12 @@ abstract class _$AppRouter extends RootStackRouter { child: const ManageSubscriptionsScreen(), ); }, + MigrationRoute.name: (routeData) { + return AutoRoutePage( + routeData: routeData, + child: const ResetScreen(), + ); + }, NotificationSettingsRoute.name: (routeData) { return AutoRoutePage( routeData: routeData, @@ -608,6 +614,20 @@ class ManageSubscriptionsRoute extends PageRouteInfo { static const PageInfo page = PageInfo(name); } +/// generated route for +/// [ResetScreen] +class MigrationRoute extends PageRouteInfo { + const MigrationRoute({List? children}) + : super( + MigrationRoute.name, + initialChildren: children, + ); + + static const String name = 'MigrationRoute'; + + static const PageInfo page = PageInfo(name); +} + /// generated route for /// [NotificationSettingsScreen] class NotificationSettingsRoute extends PageRouteInfo { diff --git a/lib/search/models/db/search_history_item.dart b/lib/search/models/db/search_history_item.dart index dc9b8495..e6094ccd 100644 --- a/lib/search/models/db/search_history_item.dart +++ b/lib/search/models/db/search_history_item.dart @@ -1,12 +1,17 @@ -import 'package:objectbox/objectbox.dart'; +import 'package:freezed_annotation/freezed_annotation.dart'; -@Entity() +part 'search_history_item.g.dart'; + +@JsonSerializable() class SearchHistoryItem { - @Id() - int id = 0; - @Unique(onConflict: ConflictStrategy.replace) String search; + int time; SearchHistoryItem(this.search, this.time); + + factory SearchHistoryItem.fromJson(Map json) => + _$SearchHistoryItemFromJson(json); + + Map toJson() => _$SearchHistoryItemToJson(this); } diff --git a/lib/search/models/db/search_history_item.g.dart b/lib/search/models/db/search_history_item.g.dart new file mode 100644 index 00000000..27377196 --- /dev/null +++ b/lib/search/models/db/search_history_item.g.dart @@ -0,0 +1,19 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'search_history_item.dart'; + +// ************************************************************************** +// JsonSerializableGenerator +// ************************************************************************** + +SearchHistoryItem _$SearchHistoryItemFromJson(Map json) => + SearchHistoryItem( + json['search'] as String, + json['time'] as int, + ); + +Map _$SearchHistoryItemToJson(SearchHistoryItem instance) => + { + 'search': instance.search, + 'time': instance.time, + }; diff --git a/lib/service.dart b/lib/service.dart index 08c8491e..aaffcc45 100644 --- a/lib/service.dart +++ b/lib/service.dart @@ -5,7 +5,6 @@ import 'package:flutter_web_auth/flutter_web_auth.dart'; import 'package:http/http.dart' as http; import 'package:http/http.dart'; import 'package:invidious/channels/models/channel_sort_by.dart'; -import 'package:invidious/database.dart'; import 'package:invidious/extensions.dart'; import 'package:invidious/globals.dart'; import 'package:invidious/playlists/models/playlist.dart'; @@ -15,6 +14,7 @@ import 'package:invidious/search/models/search_duration.dart'; import 'package:invidious/search/models/search_results.dart'; import 'package:invidious/search/models/search_sort_by.dart'; import 'package:invidious/search/models/search_type.dart'; +import 'package:invidious/settings/models/db/settings.dart'; import 'package:invidious/settings/models/errors/cannot_add_server_error.dart'; import 'package:invidious/settings/models/errors/invidious_service_error.dart'; import 'package:invidious/settings/models/errors/missing_software_key.dart'; @@ -111,10 +111,10 @@ class Service { return db.getSettings(useProxySettingName)?.value == 'true'; } - Uri buildUrl(String baseUrl, - {Map? pathParams, Map? query}) { + Future buildUrl(String baseUrl, + {Map? pathParams, Map? query}) async { try { - String url = '${db.getCurrentlySelectedServer().url}$baseUrl'; + String url = '${(await db.getCurrentlySelectedServer()).url}$baseUrl'; pathParams?.forEach((key, value) { url = url.replaceAll(key, value); @@ -148,8 +148,8 @@ class Service { handleErrors(Response response) {} Future