refactor: AppDatabase from provider to singleton

This commit is contained in:
Kingkor Roy Tirtho 2025-04-20 12:41:02 +06:00
parent dcf0c62e5d
commit 937906139b
37 changed files with 191 additions and 177 deletions

View File

@ -8,7 +8,6 @@ import 'package:flutter/services.dart';
import 'package:flutter_discord_rpc/flutter_discord_rpc.dart';
import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:flutter_localizations/flutter_localizations.dart';
import 'package:get_it/get_it.dart';
import 'package:home_widget/home_widget.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
@ -31,7 +30,6 @@ import 'package:spotube/hooks/configurators/use_has_touch.dart';
import 'package:spotube/models/database/database.dart';
import 'package:spotube/modules/settings/color_scheme_picker_dialog.dart';
import 'package:spotube/provider/audio_player/audio_player_streams.dart';
import 'package:spotube/provider/database/database.dart';
import 'package:spotube/provider/glance/glance.dart';
import 'package:spotube/provider/server/bonsoir.dart';
import 'package:spotube/provider/server/server.dart';
@ -73,7 +71,9 @@ Future<void> main(List<String> rawArgs) async {
() => KVStoreService.init(),
dependsOn: [SharedPreferences],
);
getIt.registerLazySingleton<AppDatabase>(() => AppDatabase());
getIt.registerSingleton(SpotubeAudioPlayer());
getIt.registerSingleton<WindowManager>(windowManager);
await registerWindowsScheme("spotify");
@ -112,8 +112,6 @@ Future<void> main(List<String> rawArgs) async {
await EncryptedKvStoreService.initialize();
final database = AppDatabase();
if (kIsDesktop) {
await localNotifier.setup(appName: "Spotube");
await WindowManagerTools.initialize();
@ -124,14 +122,11 @@ Future<void> main(List<String> rawArgs) async {
}
runApp(
ProviderScope(
overrides: [
databaseProvider.overrideWith((ref) => database),
],
observers: const [
const ProviderScope(
observers: [
AppLoggerProviderObserver(),
],
child: const Spotube(),
child: Spotube(),
),
);
});

View File

@ -4,13 +4,13 @@ import 'package:drift/drift.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:media_kit/media_kit.dart' hide Track;
import 'package:spotify/spotify.dart' hide Playlist;
import 'package:spotube/collections/vars.dart';
import 'package:spotube/extensions/list.dart';
import 'package:spotube/extensions/track.dart';
import 'package:spotube/models/database/database.dart';
import 'package:spotube/models/local_track.dart';
import 'package:spotube/provider/audio_player/state.dart';
import 'package:spotube/provider/blacklist_provider.dart';
import 'package:spotube/provider/database/database.dart';
import 'package:spotube/provider/discord_provider.dart';
import 'package:spotube/provider/server/sourced_track.dart';
import 'package:spotube/services/audio_player/audio_player.dart';
@ -20,7 +20,7 @@ class AudioPlayerNotifier extends Notifier<AudioPlayerState> {
BlackListNotifier get _blacklist => ref.read(blacklistProvider.notifier);
Future<void> _syncSavedState() async {
final database = ref.read(databaseProvider);
final database = getIt.get<AppDatabase>();
var playerState =
await database.select(database.audioPlayerStateTable).getSingleOrNull();
@ -102,7 +102,7 @@ class AudioPlayerNotifier extends Notifier<AudioPlayerState> {
Future<void> _updatePlayerState(
AudioPlayerStateTableCompanion companion,
) async {
final database = ref.read(databaseProvider);
final database = getIt.get<AppDatabase>();
await (database.update(database.audioPlayerStateTable)
..where((tb) => tb.id.equals(0)))
@ -112,7 +112,7 @@ class AudioPlayerNotifier extends Notifier<AudioPlayerState> {
Future<void> _updatePlaylist(
Playlist playlist,
) async {
final database = ref.read(databaseProvider);
final database = getIt.get<AppDatabase>();
await database.batch((batch) {
batch.update(

View File

@ -11,10 +11,10 @@ import 'package:flutter_inappwebview/flutter_inappwebview.dart'
hide X509Certificate;
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:spotube/collections/routes.dart';
import 'package:spotube/collections/vars.dart';
import 'package:spotube/components/dialogs/prompt_dialog.dart';
import 'package:spotube/extensions/context.dart';
import 'package:spotube/models/database/database.dart';
import 'package:spotube/provider/database/database.dart';
import 'package:spotube/services/logger/logger.dart';
import 'package:spotube/utils/platform.dart';
import 'package:otp_util/otp_util.dart';
@ -51,10 +51,10 @@ class AuthenticationNotifier extends AsyncNotifier<AuthenticationTableData?> {
return dio;
}();
AppDatabase get database => getIt.get<AppDatabase>();
@override
build() async {
final database = ref.watch(databaseProvider);
final data = await (database.select(database.authenticationTable)
..where((s) => s.id.equals(0)))
.getSingleOrNull();
@ -92,7 +92,6 @@ class AuthenticationNotifier extends AsyncNotifier<AuthenticationTableData?> {
}
Future<void> refreshCredentials() async {
final database = ref.read(databaseProvider);
final refreshedCredentials =
await credentialsFromCookie(state.asData!.value!.cookie.value);
@ -102,7 +101,6 @@ class AuthenticationNotifier extends AsyncNotifier<AuthenticationTableData?> {
}
Future<void> login(String cookie) async {
final database = ref.read(databaseProvider);
final refreshedCredentials = await credentialsFromCookie(cookie);
await database
@ -289,7 +287,6 @@ class AuthenticationNotifier extends AsyncNotifier<AuthenticationTableData?> {
Future<void> logout() async {
state = const AsyncData(null);
final database = ref.read(databaseProvider);
await (database.delete(database.authenticationTable)
..where((s) => s.id.equals(0)))
.go();

View File

@ -1,17 +1,17 @@
import 'package:collection/collection.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:spotify/spotify.dart';
import 'package:spotube/collections/vars.dart';
import 'package:spotube/models/current_playlist.dart';
import 'package:spotube/models/database/database.dart';
import 'package:spotube/provider/database/database.dart';
class BlackListNotifier extends AsyncNotifier<List<BlacklistTableData>> {
AppDatabase get db => getIt.get<AppDatabase>();
@override
build() async {
final database = ref.watch(databaseProvider);
final subscription = database
.select(database.blacklistTable)
final subscription = db
.select(db.blacklistTable)
.watch()
.listen((event) => state = AsyncData(event));
@ -19,17 +19,15 @@ class BlackListNotifier extends AsyncNotifier<List<BlacklistTableData>> {
subscription.cancel();
});
return await database.select(database.blacklistTable).get();
return await db.select(db.blacklistTable).get();
}
AppDatabase get _database => ref.read(databaseProvider);
Future<void> add(BlacklistTableCompanion element) async {
_database.into(_database.blacklistTable).insert(element);
db.into(db.blacklistTable).insert(element);
}
Future<void> remove(String elementId) async {
await (_database.delete(_database.blacklistTable)
await (db.delete(db.blacklistTable)
..where((tbl) => tbl.elementId.equals(elementId)))
.go();
}

View File

@ -1,4 +0,0 @@
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:spotube/models/database/database.dart';
final databaseProvider = Provider((ref) => AppDatabase());

View File

@ -1,11 +1,11 @@
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:spotify/spotify.dart';
import 'package:spotube/collections/vars.dart';
import 'package:spotube/models/database/database.dart';
import 'package:spotube/provider/database/database.dart';
class PlaybackHistoryActions {
final Ref ref;
AppDatabase get _db => ref.read(databaseProvider);
AppDatabase get _db => getIt.get<AppDatabase>();
PlaybackHistoryActions(this.ref);

View File

@ -2,13 +2,13 @@ import 'dart:convert';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:shadcn_flutter/shadcn_flutter.dart';
import 'package:spotube/collections/vars.dart';
import 'package:spotube/models/database/database.dart';
import 'package:spotube/provider/database/database.dart';
class RecentlyPlayedItemNotifier extends AsyncNotifier<List<HistoryTableData>> {
@override
build() async {
final database = ref.watch(databaseProvider);
final database = getIt.get<AppDatabase>();
final query = database.customSelect(
"""

View File

@ -4,8 +4,8 @@ import 'dart:convert';
import 'package:drift/drift.dart';
import 'package:drift/extensions/json1.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:spotube/collections/vars.dart';
import 'package:spotube/models/database/database.dart';
import 'package:spotube/provider/database/database.dart';
class PlaybackHistorySummary {
final Duration duration;
@ -47,7 +47,7 @@ class PlaybackHistorySummaryNotifier
extends AsyncNotifier<PlaybackHistorySummary> {
@override
build() async {
final database = ref.watch(databaseProvider);
final database = getIt.get<AppDatabase>();
final uniqItemIdCountingCol =
database.historyTable.itemId.count(distinct: true);

View File

@ -4,7 +4,8 @@ import 'package:collection/collection.dart';
import 'package:drift/drift.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:spotify/spotify.dart';
import 'package:spotube/provider/database/database.dart';
import 'package:spotube/collections/vars.dart';
import 'package:spotube/models/database/database.dart';
import 'package:spotube/provider/history/top.dart';
import 'package:spotube/provider/spotify/spotify.dart';
@ -39,7 +40,7 @@ class HistoryTopAlbumsNotifier extends FamilyPaginatedAsyncNotifier<
HistoryTopAlbumsNotifier() : super();
Selectable<AlbumSimple> createAlbumsQuery({int? limit, int? offset}) {
final database = ref.read(databaseProvider);
final database = getIt.get<AppDatabase>();
final duration = switch (arg) {
HistoryDuration.allTime => '0',

View File

@ -2,8 +2,8 @@ import 'package:collection/collection.dart';
import 'package:drift/drift.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:spotify/spotify.dart';
import 'package:spotube/collections/vars.dart';
import 'package:spotube/models/database/database.dart';
import 'package:spotube/provider/database/database.dart';
import 'package:spotube/provider/history/top.dart';
import 'package:spotube/provider/spotify/spotify.dart';
@ -39,7 +39,7 @@ class HistoryTopPlaylistsNotifier extends FamilyPaginatedAsyncNotifier<
SimpleSelectStatement<$HistoryTableTable, HistoryTableData>
createPlaylistsQuery() {
final database = ref.read(databaseProvider);
final database = getIt.get<AppDatabase>();
return database.select(database.historyTable)
..where(

View File

@ -2,8 +2,8 @@ import 'package:collection/collection.dart';
import 'package:drift/drift.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:spotify/spotify.dart';
import 'package:spotube/collections/vars.dart';
import 'package:spotube/models/database/database.dart';
import 'package:spotube/provider/database/database.dart';
import 'package:spotube/provider/history/top.dart';
import 'package:spotube/provider/spotify/spotify.dart';
@ -56,7 +56,7 @@ class HistoryTopTracksNotifier extends FamilyPaginatedAsyncNotifier<
SimpleSelectStatement<$HistoryTableTable, HistoryTableData>
createTracksQuery() {
final database = ref.read(databaseProvider);
final database = getIt.get<AppDatabase>();
return database.select(database.historyTable)
..where(

View File

@ -5,24 +5,24 @@ import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:scrobblenaut/scrobblenaut.dart';
import 'package:spotify/spotify.dart';
import 'package:spotube/collections/env.dart';
import 'package:spotube/collections/vars.dart';
import 'package:spotube/extensions/artist_simple.dart';
import 'package:spotube/models/database/database.dart';
import 'package:spotube/provider/database/database.dart';
import 'package:spotube/services/logger/logger.dart';
class ScrobblerNotifier extends AsyncNotifier<Scrobblenaut?> {
final StreamController<Track> _scrobbleController =
StreamController<Track>.broadcast();
AppDatabase get db => getIt.get<AppDatabase>();
@override
build() async {
final database = ref.watch(databaseProvider);
final loginInfo = await (database.select(database.scrobblerTable)
final loginInfo = await (db.select(db.scrobblerTable)
..where((t) => t.id.equals(0)))
.getSingleOrNull();
final subscription =
database.select(database.scrobblerTable).watch().listen((event) async {
db.select(db.scrobblerTable).watch().listen((event) async {
try {
if (event.isNotEmpty) {
state = await AsyncValue.guard(
@ -83,8 +83,6 @@ class ScrobblerNotifier extends AsyncNotifier<Scrobblenaut?> {
String username,
String password,
) async {
final database = ref.read(databaseProvider);
final lastFm = await LastFM.authenticate(
apiKey: Env.lastFmApiKey,
apiSecret: Env.lastFmApiSecret,
@ -94,7 +92,7 @@ class ScrobblerNotifier extends AsyncNotifier<Scrobblenaut?> {
if (!lastFm.isAuth) throw Exception("Invalid credentials");
await database.into(database.scrobblerTable).insert(
await db.into(db.scrobblerTable).insert(
ScrobblerTableCompanion.insert(
id: const Value(0),
username: username,
@ -105,8 +103,8 @@ class ScrobblerNotifier extends AsyncNotifier<Scrobblenaut?> {
Future<void> logout() async {
state = const AsyncValue.data(null);
final database = ref.read(databaseProvider);
await database.delete(database.scrobblerTable).go();
await db.delete(db.scrobblerTable).go();
}
void scrobble(Track track) {

View File

@ -1,5 +1,5 @@
import 'package:spotube/collections/vars.dart';
import 'package:spotube/models/database/database.dart';
import 'package:spotube/provider/database/database.dart';
import 'package:spotube/services/logger/logger.dart';
import 'package:dio/dio.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
@ -17,7 +17,7 @@ class SourcedSegments {
Future<List<SkipSegmentTableData>> getAndCacheSkipSegments(
String id, Ref ref) async {
final database = ref.read(databaseProvider);
final database = getIt.get<AppDatabase>();
try {
final cached = await (database.select(database.skipSegmentTable)
..where((s) => s.trackId.equals(id)))

View File

@ -123,7 +123,7 @@ class SyncedLyricsNotifier extends FamilyAsyncNotifier<SubtitleSimple, Track?> {
@override
FutureOr<SubtitleSimple> build(track) async {
try {
final database = ref.watch(databaseProvider);
final database = getIt.get<AppDatabase>();
final spotify = ref.watch(spotifyProvider);
final auth = await ref.watch(authenticationProvider.future);

View File

@ -6,9 +6,9 @@ import 'dart:math';
import 'package:drift/drift.dart';
import 'package:spotube/collections/assets.gen.dart';
import 'package:spotube/collections/env.dart';
import 'package:spotube/collections/vars.dart';
import 'package:spotube/models/database/database.dart';
import 'package:spotube/provider/authentication/authentication.dart';
import 'package:spotube/provider/database/database.dart';
import 'package:spotube/services/logger/logger.dart';
import 'package:collection/collection.dart';
import 'package:dio/dio.dart';

View File

@ -1,9 +1,10 @@
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:spotube/collections/vars.dart';
import 'package:spotube/provider/tray_manager/tray_menu.dart';
import 'package:spotube/provider/user_preferences/user_preferences_provider.dart';
import 'package:spotube/provider/window_manager/window_manager.dart';
import 'package:spotube/utils/platform.dart';
import 'package:tray_manager/tray_manager.dart';
import 'package:window_manager/window_manager.dart';
class SystemTrayManager with TrayListener {
final Ref ref;
@ -40,7 +41,7 @@ class SystemTrayManager with TrayListener {
@override
onTrayIconMouseDown() {
if (kIsWindows) {
ref.read(windowManagerProvider).show();
getIt.get<WindowManager>().show();
} else {
trayManager.popUpContextMenu();
}
@ -49,7 +50,7 @@ class SystemTrayManager with TrayListener {
@override
onTrayIconRightMouseDown() {
if (!kIsWindows) {
ref.read(windowManagerProvider).show();
getIt.get<WindowManager>().show();
} else {
trayManager.popUpContextMenu();
}

View File

@ -1,11 +1,12 @@
import 'dart:io';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:spotube/collections/vars.dart';
import 'package:spotube/provider/audio_player/audio_player.dart';
import 'package:spotube/provider/window_manager/window_manager.dart';
import 'package:spotube/services/audio_player/audio_player.dart';
import 'package:media_kit/media_kit.dart' hide Track;
import 'package:tray_manager/tray_manager.dart';
import 'package:window_manager/window_manager.dart';
final audioPlayerLoopMode = StreamProvider<PlaylistMode>((ref) {
return audioPlayer.loopModeStream;
@ -19,7 +20,7 @@ final audioPlayerPlaying = StreamProvider<bool>((ref) {
});
final trayMenuProvider = Provider((ref) {
final windowManager = ref.watch(windowManagerProvider);
final windowManager = getIt.get<WindowManager>();
final playlistNotifier = ref.watch(audioPlayerProvider.notifier);
final isPlaybackPlaying =

View File

@ -5,11 +5,10 @@ import 'package:path/path.dart';
import 'package:path_provider/path_provider.dart' as paths;
import 'package:shadcn_flutter/shadcn_flutter.dart' hide join;
import 'package:spotify/spotify.dart';
import 'package:spotube/collections/vars.dart';
import 'package:spotube/models/database/database.dart';
import 'package:spotube/modules/settings/color_scheme_picker_dialog.dart';
import 'package:spotube/provider/database/database.dart';
import 'package:spotube/provider/user_preferences/default_download_dir_provider.dart';
import 'package:spotube/provider/window_manager/window_manager.dart';
import 'package:spotube/services/audio_player/audio_player.dart';
import 'package:spotube/services/logger/logger.dart';
import 'package:spotube/services/sourced_track/enums.dart';
@ -20,10 +19,10 @@ import 'package:open_file/open_file.dart';
typedef UserPreferences = PreferencesTableData;
class UserPreferencesNotifier extends Notifier<PreferencesTableData> {
AppDatabase get db => getIt.get<AppDatabase>();
@override
build() {
final db = ref.watch(databaseProvider);
(db.select(db.preferencesTable)..where((tbl) => tbl.id.equals(0)))
.getSingleOrNull()
.then((result) async {
@ -50,7 +49,7 @@ class UserPreferencesNotifier extends Notifier<PreferencesTableData> {
state = event;
if (kIsDesktop) {
await ref.read(windowManagerProvider).setTitleBarStyle(
await getIt.get<WindowManager>().setTitleBarStyle(
state.systemTitleBar
? TitleBarStyle.normal
: TitleBarStyle.hidden,
@ -71,29 +70,25 @@ class UserPreferencesNotifier extends Notifier<PreferencesTableData> {
return PreferencesTable.defaults();
}
Future<String> _getDefaultDownloadDirectory() async {
if (kIsAndroid) return "/storage/emulated/0/Download/Spotube";
// Future<String> _getDefaultDownloadDirectory() async {
// if (kIsAndroid) return "/storage/emulated/0/Download/Spotube";
if (kIsMacOS) {
return join((await paths.getLibraryDirectory()).path, "Caches");
}
// if (kIsMacOS) {
// return join((await paths.getLibraryDirectory()).path, "Caches");
// }
return paths.getDownloadsDirectory().then((dir) {
return join(dir!.path, "Spotube");
});
}
// return paths.getDownloadsDirectory().then((dir) {
// return join(dir!.path, "Spotube");
// });
// }
Future<void> setData(PreferencesTableCompanion data) async {
final db = ref.read(databaseProvider);
final query = db.update(db.preferencesTable)..where((t) => t.id.equals(0));
await query.write(data);
}
Future<void> reset() async {
final db = ref.read(databaseProvider);
final query = db.update(db.preferencesTable);
await query.replace(PreferencesTableCompanion.insert(id: const Value(0)));

View File

@ -1,4 +0,0 @@
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:window_manager/window_manager.dart';
final windowManagerProvider = Provider((ref) => windowManager);

View File

@ -2,8 +2,8 @@ import 'package:collection/collection.dart';
import 'package:drift/drift.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:spotify/spotify.dart';
import 'package:spotube/collections/vars.dart';
import 'package:spotube/models/database/database.dart';
import 'package:spotube/provider/database/database.dart';
import 'package:spotube/provider/user_preferences/user_preferences_provider.dart';
import 'package:spotube/services/sourced_track/enums.dart';
import 'package:spotube/services/sourced_track/exceptions.dart';
@ -66,7 +66,8 @@ class InvidiousSourcedTrack extends SourcedTrack {
);
}
final database = ref.read(databaseProvider);
final database = getIt.get<AppDatabase>();
final cachedSource = await (database.select(database.sourceMatchTable)
..where((s) => s.trackId.equals(track.id!))
..limit(1)
@ -258,7 +259,7 @@ class InvidiousSourcedTrack extends SourcedTrack {
final manifest =
await pipedClient.videos.get(newSourceInfo.id, local: true);
final database = ref.read(databaseProvider);
final database = getIt.get<AppDatabase>();
await database.into(database.sourceMatchTable).insert(
SourceMatchTableCompanion.insert(
trackId: id!,

View File

@ -2,8 +2,8 @@ import 'package:collection/collection.dart';
import 'package:drift/drift.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:spotify/spotify.dart';
import 'package:spotube/collections/vars.dart';
import 'package:spotube/models/database/database.dart';
import 'package:spotube/provider/database/database.dart';
import 'package:spotube/services/sourced_track/enums.dart';
import 'package:spotube/services/sourced_track/exceptions.dart';
import 'package:spotube/services/sourced_track/models/source_info.dart';
@ -41,7 +41,7 @@ class JioSaavnSourcedTrack extends SourcedTrack {
required Ref ref,
bool weakMatch = false,
}) async {
final database = ref.read(databaseProvider);
final database = getIt.get<AppDatabase>();
final cachedSource = await (database.select(database.sourceMatchTable)
..where((s) => s.trackId.equals(track.id!))
..limit(1)
@ -214,7 +214,7 @@ class JioSaavnSourcedTrack extends SourcedTrack {
final (:info, :source) = toSiblingType(item);
final database = ref.read(databaseProvider);
final database = getIt.get<AppDatabase>();
await database.into(database.sourceMatchTable).insert(
SourceMatchTableCompanion.insert(
trackId: id!,

View File

@ -3,8 +3,8 @@ import 'package:drift/drift.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:piped_client/piped_client.dart';
import 'package:spotify/spotify.dart';
import 'package:spotube/collections/vars.dart';
import 'package:spotube/models/database/database.dart';
import 'package:spotube/provider/database/database.dart';
import 'package:spotube/provider/user_preferences/user_preferences_provider.dart';
import 'package:spotube/services/sourced_track/enums.dart';
@ -63,7 +63,7 @@ class PipedSourcedTrack extends SourcedTrack {
);
}
final database = ref.read(databaseProvider);
final database = getIt.get<AppDatabase>();
final cachedSource = await (database.select(database.sourceMatchTable)
..where((s) => s.trackId.equals(track.id!))
..limit(1)
@ -287,7 +287,7 @@ class PipedSourcedTrack extends SourcedTrack {
final manifest = await pipedClient.streams(newSourceInfo.id);
final database = ref.read(databaseProvider);
final database = getIt.get<AppDatabase>();
await database.into(database.sourceMatchTable).insert(
SourceMatchTableCompanion.insert(
trackId: id!,

View File

@ -2,8 +2,8 @@ import 'package:collection/collection.dart';
import 'package:drift/drift.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:spotify/spotify.dart';
import 'package:spotube/collections/vars.dart';
import 'package:spotube/models/database/database.dart';
import 'package:spotube/provider/database/database.dart';
import 'package:spotube/provider/youtube_engine/youtube_engine.dart';
import 'package:spotube/services/logger/logger.dart';
import 'package:spotube/services/song_link/song_link.dart';
@ -66,7 +66,7 @@ class YoutubeSourcedTrack extends SourcedTrack {
return sourcedTrack;
}
final database = ref.read(databaseProvider);
final database = getIt.get<AppDatabase>();
final cachedSource = await (database.select(database.sourceMatchTable)
..where((s) => s.trackId.equals(track.id!))
..limit(1)
@ -261,8 +261,8 @@ class YoutubeSourcedTrack extends SourcedTrack {
.where((item) => item.isNotEmpty);
// Single word and duration match with 3 second tolerance
if (ytWords.any((word) => spWords.contains(word)) &&
(videoInfo.duration - track.duration!)
.abs().inMilliseconds <= 3000) {
(videoInfo.duration - track.duration!).abs().inMilliseconds <=
3000) {
return videoInfo;
}
return null;
@ -356,7 +356,7 @@ class YoutubeSourcedTrack extends SourcedTrack {
.read(youtubeEngineProvider)
.getStreamManifest(newSourceInfo.id);
final database = ref.read(databaseProvider);
final database = getIt.get<AppDatabase>();
await database.into(database.sourceMatchTable).insert(
SourceMatchTableCompanion.insert(

View File

@ -7,11 +7,12 @@ import 'package:flutter_cache_manager/flutter_cache_manager.dart';
import 'package:html/dom.dart' hide Text;
import 'package:shadcn_flutter/shadcn_flutter.dart' hide Element;
import 'package:spotify/spotify.dart';
import 'package:spotube/collections/vars.dart';
import 'package:spotube/models/database/database.dart';
import 'package:spotube/pages/library/user_local_tracks/user_local_tracks.dart';
import 'package:spotube/modules/root/update_dialog.dart';
import 'package:spotube/models/lyrics.dart';
import 'package:spotube/provider/database/database.dart';
import 'package:spotube/services/dio/dio.dart';
import 'package:spotube/services/logger/logger.dart';
import 'package:spotube/services/sourced_track/sourced_track.dart';
@ -333,7 +334,7 @@ abstract class ServiceUtils {
WidgetRef ref,
) async {
if (!Env.enableUpdateChecker) return;
final database = ref.read(databaseProvider);
final database = getIt.get<AppDatabase>();
final checkUpdate = await (database.selectOnly(database.preferencesTable)
..addColumns([database.preferencesTable.checkUpdate])
..where(database.preferencesTable.id.equals(0)))

View File

@ -2,6 +2,10 @@
// GENERATED CODE, DO NOT EDIT BY HAND.
// ignore_for_file: type=lint
import 'package:drift/drift.dart';
import 'package:flutter/material.dart' show ThemeMode;
import 'package:spotify/spotify.dart';
import 'package:spotube/models/database/database.dart';
import 'package:spotube/services/sourced_track/enums.dart';
class AuthenticationTable extends Table
with TableInfo<AuthenticationTable, AuthenticationTableData> {

View File

@ -2,6 +2,10 @@
// GENERATED CODE, DO NOT EDIT BY HAND.
// ignore_for_file: type=lint
import 'package:drift/drift.dart';
import 'package:flutter/material.dart' show ThemeMode;
import 'package:spotify/spotify.dart';
import 'package:spotube/models/database/database.dart';
import 'package:spotube/services/sourced_track/enums.dart';
class AuthenticationTable extends Table
with TableInfo<AuthenticationTable, AuthenticationTableData> {

View File

@ -2,6 +2,10 @@
// GENERATED CODE, DO NOT EDIT BY HAND.
// ignore_for_file: type=lint
import 'package:drift/drift.dart';
import 'package:flutter/material.dart' show ThemeMode;
import 'package:spotify/spotify.dart';
import 'package:spotube/models/database/database.dart';
import 'package:spotube/services/sourced_track/enums.dart';
class AuthenticationTable extends Table
with TableInfo<AuthenticationTable, AuthenticationTableData> {

View File

@ -2,6 +2,10 @@
// GENERATED CODE, DO NOT EDIT BY HAND.
// ignore_for_file: type=lint
import 'package:drift/drift.dart';
import 'package:flutter/material.dart' show ThemeMode;
import 'package:spotify/spotify.dart';
import 'package:spotube/models/database/database.dart';
import 'package:spotube/services/sourced_track/enums.dart';
class AuthenticationTable extends Table
with TableInfo<AuthenticationTable, AuthenticationTableData> {

View File

@ -2,6 +2,10 @@
// GENERATED CODE, DO NOT EDIT BY HAND.
// ignore_for_file: type=lint
import 'package:drift/drift.dart';
import 'package:flutter/material.dart' show ThemeMode;
import 'package:spotify/spotify.dart';
import 'package:spotube/models/database/database.dart';
import 'package:spotube/services/sourced_track/enums.dart';
class AuthenticationTable extends Table
with TableInfo<AuthenticationTable, AuthenticationTableData> {

View File

@ -20,13 +20,13 @@ void main() {
// These simple tests verify all possible schema updates with a simple (no
// data) migration. This is a quick way to ensure that written database
// migrations properly alter the schema.
final versions = GeneratedHelper.versions;
const versions = GeneratedHelper.versions;
for (final (i, fromVersion) in versions.indexed) {
group('from $fromVersion', () {
for (final toVersion in versions.skip(i + 1)) {
test('to $toVersion', () async {
final schema = await verifier.schemaAt(fromVersion);
final db = Database(schema.newConnection());
final db = AppDatabase(schema.newConnection());
await verifier.migrateAndValidate(db, toVersion);
await db.close();
});

View File

@ -1,5 +0,0 @@
import 'package:mocktail/mocktail.dart';
import 'package:spotube/provider/audio_player/audio_player_streams.dart';
class MockAudioPlayerStreamListeners extends Mock
implements AudioPlayerStreamListeners {}

View File

@ -0,0 +1,6 @@
part of 'mocks.dart';
class MockAudioPlayerStreamListeners extends Mock
implements AudioPlayerStreamListeners {}
class MockSpotubeAudioPlayer extends Mock implements SpotubeAudioPlayer {}

View File

@ -0,0 +1,3 @@
part of 'mocks.dart';
class MockKVStoreService extends Mock implements KVStoreService {}

View File

@ -0,0 +1,12 @@
library mocks;
import 'package:mocktail/mocktail.dart';
import 'package:window_manager/window_manager.dart';
import 'package:spotube/services/kv_store/kv_store.dart';
import 'package:spotube/provider/audio_player/audio_player_streams.dart';
import 'package:spotube/services/audio_player/audio_player.dart';
part 'audio_player_mock.dart';
part 'kv_store_mock.dart';
part 'window_manager_mock.dart';

View File

@ -1,4 +1,3 @@
import 'package:mocktail/mocktail.dart';
import 'package:window_manager/window_manager.dart';
part of 'mocks.dart';
class MockWindowManager extends Mock implements WindowManager {}

View File

@ -4,36 +4,54 @@ import 'package:drift/native.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:mocktail/mocktail.dart';
import 'package:spotube/collections/vars.dart';
import 'package:spotube/models/database/database.dart';
import 'package:spotube/provider/audio_player/audio_player_streams.dart';
import 'package:spotube/provider/database/database.dart';
import 'package:spotube/provider/user_preferences/default_download_dir_provider.dart';
import 'package:spotube/provider/user_preferences/user_preferences_provider.dart';
import 'package:spotube/provider/window_manager/window_manager.dart';
import 'package:spotube/services/audio_player/audio_player.dart';
import 'package:spotube/services/kv_store/kv_store.dart';
import 'package:spotube/services/logger/logger.dart';
import 'package:window_manager/window_manager.dart';
import '../create_container.dart';
import '../mocks/audio_player_listeners_mock.dart';
import '../mocks/window_manager_mock.dart';
import '../mocks/mocks.dart';
List<Override> _createDefaultOverrides() => [
databaseProvider.overrideWith(
(ref) {
void main() {
group('UserPreferences', () {
TestWidgetsFlutterBinding.ensureInitialized();
late ProviderContainer container;
late MockWindowManager mockWindowManager;
setUpAll(() {
registerFallbackValue(TitleBarStyle.normal);
AppLogger.initialize(false);
});
setUp(() {
mockWindowManager = MockWindowManager();
getIt.registerSingleton<SpotubeAudioPlayer>(MockSpotubeAudioPlayer());
getIt.registerSingleton<KVStoreService>(MockKVStoreService());
getIt.registerSingleton<WindowManager>(mockWindowManager);
getIt.registerLazySingleton(
() {
final database = AppDatabase(NativeDatabase.memory());
ref.onDispose(database.close);
addTearDown(() {
database.close();
});
return database;
},
),
);
container = createContainer(
overrides: [
audioPlayerStreamListenersProvider.overrideWith(
(ref) {
final streamListeners = MockAudioPlayerStreamListeners();
when(() => streamListeners.updatePalette()).thenReturn(
Future.value(),
);
return streamListeners;
},
),
@ -41,51 +59,37 @@ List<Override> _createDefaultOverrides() => [
(ref) {
return Future.value("/storage/emulated/0/Download/Spotube");
},
)
];
),
],
);
void main() {
group('UserPreferences', () {
setUpAll(() {
registerFallbackValue(TitleBarStyle.normal);
AppLogger.initialize(false);
when(() => mockWindowManager.setTitleBarStyle(any()))
.thenAnswer((_) async {});
});
tearDown(() {
getIt.reset();
});
test('Initial value should be equal the default values', () {
final ref = createContainer(overrides: _createDefaultOverrides());
final preferences = ref.read(userPreferencesProvider);
final preferences = container.read(userPreferencesProvider);
final defaultPreferences = PreferencesTable.defaults();
expect(preferences, defaultPreferences);
});
test('[setSystemTitleBar] should update UI titlebar', () async {
TestWidgetsFlutterBinding.ensureInitialized();
test('setSystemTitleBar should update UI titlebar', () async {
when(() => audioPlayer.setAudioNormalization(any()))
.thenAnswer((_) async {});
final ref = createContainer(overrides: [
..._createDefaultOverrides(),
windowManagerProvider.overrideWith(
(ref) {
final mockWindowManager = MockWindowManager();
when(() => mockWindowManager.setTitleBarStyle(any()))
.thenAnswer((_) => Future.value());
return mockWindowManager;
},
)
]);
final db = ref.read(databaseProvider);
final preferences = ref.read(userPreferencesProvider);
final db = getIt.get<AppDatabase>();
final preferences = container.read(userPreferencesProvider);
await Future.delayed(const Duration(milliseconds: 300));
final preferencesNotifier = ref.read(userPreferencesProvider.notifier);
final preferencesNotifier =
container.read(userPreferencesProvider.notifier);
expect(preferences.systemTitleBar, false);
await preferencesNotifier.setSystemTitleBar(true);
final completer = Completer<bool>();
final subscription = (db.select(db.preferencesTable)
..where((tbl) => tbl.id.equals(0)))
@ -98,14 +102,11 @@ void main() {
subscription.cancel();
});
final systemTitleBar = await completer.future;
preferencesNotifier.setSystemTitleBar(true);
expect(systemTitleBar, true);
verify(
() => ref
.read(windowManagerProvider)
.setTitleBarStyle(TitleBarStyle.normal),
).called(1);
await expectLater(completer.future, completion(equals(true)));
verify(() => mockWindowManager.setTitleBarStyle(TitleBarStyle.hidden))
.called(1);
});
});
}

View File

@ -6,9 +6,7 @@ import 'package:spotube/provider/volume_provider.dart';
import 'package:spotube/services/audio_player/audio_player.dart';
import 'package:spotube/services/kv_store/kv_store.dart';
class MockSpotubeAudioPlayer extends Mock implements SpotubeAudioPlayer {}
class MockKVStoreService extends Mock implements KVStoreService {}
import 'mocks/mocks.dart';
void main() {
TestWidgetsFlutterBinding.ensureInitialized();