From 0e6acbed0f673f6e5e376a4f01eae8ce4b65b84d Mon Sep 17 00:00:00 2001 From: austinried <4966622+austinried@users.noreply.github.com> Date: Fri, 7 Nov 2025 11:45:13 +0900 Subject: [PATCH] bring in database switch to just using source models (no extra db fields) start re-implementing sync service --- lib/database/converters.dart | 51 + lib/database/database.dart | 690 +++++ lib/database/database.g.dart | 4249 ++++++++++++++++++++++++++ lib/database/tables.drift | 555 ++++ lib/services/sync_services.dart | 45 + lib/sources/models.dart | 56 +- lib/sources/models.freezed.dart | 1097 +++++-- lib/sources/models.g.dart | 142 - lib/sources/music_source.dart | 10 +- lib/sources/subsonic/mapping.dart | 15 +- lib/sources/subsonic/source.dart | 10 +- pubspec.lock | 78 +- pubspec.yaml | 7 + test/services/sync_service_test.dart | 79 + test/sources/subsonic_test.dart | 238 +- test/util/database.dart | 10 + test/util/http.dart | 6 + test/util/subsonic.dart | 34 + 18 files changed, 6747 insertions(+), 625 deletions(-) create mode 100644 lib/database/converters.dart create mode 100644 lib/database/database.dart create mode 100644 lib/database/database.g.dart create mode 100644 lib/database/tables.drift create mode 100644 lib/services/sync_services.dart delete mode 100644 lib/sources/models.g.dart create mode 100644 test/services/sync_service_test.dart create mode 100644 test/util/database.dart create mode 100644 test/util/http.dart create mode 100644 test/util/subsonic.dart diff --git a/lib/database/converters.dart b/lib/database/converters.dart new file mode 100644 index 0000000..0099df6 --- /dev/null +++ b/lib/database/converters.dart @@ -0,0 +1,51 @@ +import 'dart:convert'; + +import 'package:drift/drift.dart'; +import 'package:fast_immutable_collections/fast_immutable_collections.dart'; + +class DurationSecondsConverter extends TypeConverter { + const DurationSecondsConverter(); + + @override + Duration fromSql(int fromDb) => Duration(seconds: fromDb); + + @override + int toSql(Duration value) => value.inSeconds; +} + +class UriConverter extends TypeConverter { + const UriConverter(); + + @override + Uri fromSql(String fromDb) => Uri.parse(fromDb); + + @override + String toSql(Uri value) => value.toString(); +} + +// class ListQueryConverter extends TypeConverter { +// const ListQueryConverter(); + +// @override +// ListQuery fromSql(String fromDb) => ListQuery.fromJson(jsonDecode(fromDb)); + +// @override +// String toSql(ListQuery value) => jsonEncode(value.toJson()); +// } + +class IListIntConverter extends TypeConverter, String> { + const IListIntConverter(); + + @override + IList fromSql(String fromDb) { + return IList.fromJson( + jsonDecode(fromDb), + (item) => int.parse(item as String), + ); + } + + @override + String toSql(IList value) { + return jsonEncode(value.toJson((e) => jsonEncode(e))); + } +} diff --git a/lib/database/database.dart b/lib/database/database.dart new file mode 100644 index 0000000..be46153 --- /dev/null +++ b/lib/database/database.dart @@ -0,0 +1,690 @@ +import 'package:drift/drift.dart'; +import 'package:drift_flutter/drift_flutter.dart'; +import 'package:path/path.dart' as path; +import 'package:path_provider/path_provider.dart'; + +import '../sources/models.dart' as models; +import 'converters.dart'; + +part 'database.g.dart'; + +// don't exceed SQLITE_MAX_VARIABLE_NUMBER (32766 for version >= 3.32.0) +// https://www.sqlite.org/limits.html +const kSqliteMaxVariableNumber = 32766; + +@DriftDatabase(include: {'tables.drift'}) +class SubtracksDatabase extends _$SubtracksDatabase { + SubtracksDatabase([QueryExecutor? executor]) + : super(executor ?? _openConnection()); + + static QueryExecutor _openConnection() { + return driftDatabase( + name: 'my_database', + native: DriftNativeOptions( + databasePath: () async { + final directory = await getApplicationSupportDirectory(); + return path.join(directory.absolute.path, 'subtracks.sqlite'); + }, + ), + ); + } + + @override + int get schemaVersion => 1; + + @override + MigrationStrategy get migration { + return MigrationStrategy( + beforeOpen: (details) async { + await customStatement('PRAGMA foreign_keys = ON'); + }, + ); + } + + // MultiSelectable albumsList(int sourceId, ListQuery opt) { + // return filterAlbums( + // (_) => _filterPredicate('albums', sourceId, opt), + // (_) => _filterOrderBy(opt), + // (_) => _filterLimit(opt), + // ); + // } + + // MultiSelectable albumsListDownloaded(int sourceId, ListQuery opt) { + // return filterAlbumsDownloaded( + // (_, __) => _filterPredicate('albums', sourceId, opt), + // (_, __) => _filterOrderBy(opt), + // (_, __) => _filterLimit(opt), + // ); + // } + + // MultiSelectable artistsList(int sourceId, ListQuery opt) { + // return filterArtists( + // (_) => _filterPredicate('artists', sourceId, opt), + // (_) => _filterOrderBy(opt), + // (_) => _filterLimit(opt), + // ); + // } + + // MultiSelectable artistsListDownloaded(int sourceId, ListQuery opt) { + // return filterArtistsDownloaded( + // (_, __, ___) => _filterPredicate('artists', sourceId, opt), + // (_, __, ___) => _filterOrderBy(opt), + // (_, __, ___) => _filterLimit(opt), + // ); + // } + + // MultiSelectable playlistsList(int sourceId, ListQuery opt) { + // return filterPlaylists( + // (_) => _filterPredicate('playlists', sourceId, opt), + // (_) => _filterOrderBy(opt), + // (_) => _filterLimit(opt), + // ); + // } + + // MultiSelectable playlistsListDownloaded( + // int sourceId, + // ListQuery opt, + // ) { + // return filterPlaylistsDownloaded( + // (_, __, ___) => _filterPredicate('playlists', sourceId, opt), + // (_, __, ___) => _filterOrderBy(opt), + // (_, __, ___) => _filterLimit(opt), + // ); + // } + + // MultiSelectable songsList(int sourceId, ListQuery opt) { + // return filterSongs( + // (_) => _filterPredicate('songs', sourceId, opt), + // (_) => _filterOrderBy(opt), + // (_) => _filterLimit(opt), + // ); + // } + + // MultiSelectable songsListDownloaded(int sourceId, ListQuery opt) { + // return filterSongsDownloaded( + // (_) => _filterPredicate('songs', sourceId, opt), + // (_) => _filterOrderBy(opt), + // (_) => _filterLimit(opt), + // ); + // } + + // Expression _filterPredicate(String table, int sourceId, ListQuery opt) { + // return opt.filters + // .map((filter) => buildFilter(filter)) + // .fold( + // CustomExpression('$table.source_id = $sourceId'), + // (previousValue, element) => previousValue & element, + // ); + // } + + // OrderBy _filterOrderBy(ListQuery opt) { + // return opt.sort != null + // ? OrderBy([_buildOrder(opt.sort!)]) + // : const OrderBy.nothing(); + // } + + // Limit _filterLimit(ListQuery opt) { + // return Limit(opt.page.limit, opt.page.offset); + // } + + // MultiSelectable albumSongsList(SourceId sid, ListQuery opt) { + // return listQuery( + // select(songs)..where( + // (tbl) => tbl.sourceId.equals(sid.sourceId) & tbl.albumId.equals(sid.id), + // ), + // opt, + // ); + // } + + // MultiSelectable songsByAlbumList(int sourceId, ListQuery opt) { + // return filterSongsByGenre( + // (_, __) => _filterPredicate('songs', sourceId, opt), + // (_, __) => _filterOrderBy(opt), + // (_, __) => _filterLimit(opt), + // ); + // } + + // MultiSelectable playlistSongsList(SourceId sid, ListQuery opt) { + // return listQueryJoined( + // select(songs).join([ + // innerJoin( + // playlistSongs, + // playlistSongs.sourceId.equalsExp(songs.sourceId) & + // playlistSongs.songId.equalsExp(songs.id), + // useColumns: false, + // ), + // ])..where( + // playlistSongs.sourceId.equals(sid.sourceId) & + // playlistSongs.playlistId.equals(sid.id), + // ), + // opt, + // ).map((row) => row.readTable(songs)); + // } + + // Future saveArtists(Iterable artists) async { + // await batch((batch) { + // batch.insertAllOnConflictUpdate(this.artists, artists); + // }); + // } + + // Future deleteArtistsNotIn(int sourceId, Set ids) { + // return transaction(() async { + // final allIds = + // (await (selectOnly(artists) + // ..addColumns([artists.id]) + // ..where(artists.sourceId.equals(sourceId))) + // .map((row) => row.read(artists.id)) + // .get()) + // .whereNotNull() + // .toSet(); + // final downloadIds = (await artistIdsWithDownloadStatus( + // sourceId, + // ).get()).whereNotNull().toSet(); + + // final diff = allIds.difference(downloadIds).difference(ids); + // for (var slice in diff.slices(kSqliteMaxVariableNumber)) { + // await (delete(artists)..where( + // (tbl) => tbl.sourceId.equals(sourceId) & tbl.id.isIn(slice), + // )) + // .go(); + // } + // }); + // } + + // Future saveAlbums(Iterable albums) async { + // await batch((batch) { + // batch.insertAllOnConflictUpdate(this.albums, albums); + // }); + // } + + // Future deleteAlbumsNotIn(int sourceId, Set ids) { + // return transaction(() async { + // final allIds = + // (await (selectOnly(albums) + // ..addColumns([albums.id]) + // ..where(albums.sourceId.equals(sourceId))) + // .map((row) => row.read(albums.id)) + // .get()) + // .whereNotNull() + // .toSet(); + // final downloadIds = (await albumIdsWithDownloadStatus( + // sourceId, + // ).get()).whereNotNull().toSet(); + + // final diff = allIds.difference(downloadIds).difference(ids); + // for (var slice in diff.slices(kSqliteMaxVariableNumber)) { + // await (delete(albums)..where( + // (tbl) => tbl.sourceId.equals(sourceId) & tbl.id.isIn(slice), + // )) + // .go(); + // } + // }); + // } + + // Future savePlaylists( + // Iterable playlistsWithSongs, + // ) async { + // final playlists = playlistsWithSongs.map((e) => e.playist); + // final playlistSongs = playlistsWithSongs.expand((e) => e.songs); + // final sourceId = playlists.first.sourceId.value; + + // await (delete(this.playlistSongs)..where( + // (tbl) => + // tbl.sourceId.equals(sourceId) & + // tbl.playlistId.isIn(playlists.map((e) => e.id.value)), + // )) + // .go(); + + // await batch((batch) { + // batch.insertAllOnConflictUpdate(this.playlists, playlists); + // batch.insertAllOnConflictUpdate(this.playlistSongs, playlistSongs); + // }); + // } + + // Future deletePlaylistsNotIn(int sourceId, Set ids) { + // return transaction(() async { + // final allIds = + // (await (selectOnly(playlists) + // ..addColumns([playlists.id]) + // ..where(playlists.sourceId.equals(sourceId))) + // .map((row) => row.read(playlists.id)) + // .get()) + // .whereNotNull() + // .toSet(); + // final downloadIds = (await playlistIdsWithDownloadStatus( + // sourceId, + // ).get()).whereNotNull().toSet(); + + // final diff = allIds.difference(downloadIds).difference(ids); + // for (var slice in diff.slices(kSqliteMaxVariableNumber)) { + // await (delete(playlists)..where( + // (tbl) => tbl.sourceId.equals(sourceId) & tbl.id.isIn(slice), + // )) + // .go(); + // await (delete(playlistSongs)..where( + // (tbl) => + // tbl.sourceId.equals(sourceId) & tbl.playlistId.isIn(slice), + // )) + // .go(); + // } + // }); + // } + + // Future savePlaylistSongs( + // int sourceId, + // List ids, + // Iterable playlistSongs, + // ) async { + // await (delete(this.playlistSongs)..where( + // (tbl) => tbl.sourceId.equals(sourceId) & tbl.playlistId.isIn(ids), + // )) + // .go(); + // await batch((batch) { + // batch.insertAllOnConflictUpdate(this.playlistSongs, playlistSongs); + // }); + // } + + // Future saveSongs(Iterable songs) async { + // await batch((batch) { + // batch.insertAllOnConflictUpdate(this.songs, songs); + // }); + // } + + // Future deleteSongsNotIn(int sourceId, Set ids) { + // return transaction(() async { + // final allIds = + // (await (selectOnly(songs) + // ..addColumns([songs.id]) + // ..where( + // songs.sourceId.equals(sourceId) & + // songs.downloadFilePath.isNull() & + // songs.downloadTaskId.isNull(), + // )) + // .map((row) => row.read(songs.id)) + // .get()) + // .whereNotNull() + // .toSet(); + + // final diff = allIds.difference(ids); + // for (var slice in diff.slices(kSqliteMaxVariableNumber)) { + // await (delete(songs)..where( + // (tbl) => tbl.sourceId.equals(sourceId) & tbl.id.isIn(slice), + // )) + // .go(); + // await (delete(playlistSongs)..where( + // (tbl) => tbl.sourceId.equals(sourceId) & tbl.songId.isIn(slice), + // )) + // .go(); + // } + // }); + // } + + // Selectable getLastBottomNavState() { + // return select(lastBottomNavState)..where((tbl) => tbl.id.equals(1)); + // } + + // Future saveLastBottomNavState(LastBottomNavStateData update) { + // return into(lastBottomNavState).insertOnConflictUpdate(update); + // } + + // Selectable getLastLibraryState() { + // return select(lastLibraryState)..where((tbl) => tbl.id.equals(1)); + // } + + // Future saveLastLibraryState(LastLibraryStateData update) { + // return into(lastLibraryState).insertOnConflictUpdate(update); + // } + + // Selectable getLastAudioState() { + // return select(lastAudioState)..where((tbl) => tbl.id.equals(1)); + // } + + // Future saveLastAudioState(LastAudioStateCompanion update) { + // return into(lastAudioState).insertOnConflictUpdate(update); + // } + + // Future insertQueue(Iterable songs) async { + // await batch((batch) { + // batch.insertAll(queue, songs); + // }); + // } + + // Future clearQueue() async { + // await delete(queue).go(); + // } + + // Future setCurrentTrack(int index) async { + // await transaction(() async { + // await (update(queue)..where((tbl) => tbl.index.equals(index).not())) + // .write(const QueueCompanion(currentTrack: Value(null))); + // await (update(queue)..where((tbl) => tbl.index.equals(index))).write( + // const QueueCompanion(currentTrack: Value(true)), + // ); + // }); + // } + + // Future createSource( + // SourcesCompanion source, + // SubsonicSourcesCompanion subsonic, + // ) async { + // await transaction(() async { + // final count = await sourcesCount().getSingle(); + // if (count == 0) { + // source = source.copyWith(isActive: const Value(true)); + // } + + // final id = await into(sources).insert(source); + // subsonic = subsonic.copyWith(sourceId: Value(id)); + // await into(subsonicSources).insert(subsonic); + // }); + // } + + // Future updateSource(SubsonicSettings source) async { + // await transaction(() async { + // await into(sources).insertOnConflictUpdate(source.toSourceInsertable()); + // await into( + // subsonicSources, + // ).insertOnConflictUpdate(source.toSubsonicInsertable()); + // }); + // } + + // Future deleteSource(int sourceId) async { + // await transaction(() async { + // await (delete( + // subsonicSources, + // )..where((tbl) => tbl.sourceId.equals(sourceId))).go(); + // await (delete(sources)..where((tbl) => tbl.id.equals(sourceId))).go(); + + // await (delete(songs)..where((tbl) => tbl.sourceId.equals(sourceId))).go(); + // await (delete( + // albums, + // )..where((tbl) => tbl.sourceId.equals(sourceId))).go(); + // await (delete( + // artists, + // )..where((tbl) => tbl.sourceId.equals(sourceId))).go(); + // await (delete( + // playlistSongs, + // )..where((tbl) => tbl.sourceId.equals(sourceId))).go(); + // await (delete( + // playlists, + // )..where((tbl) => tbl.sourceId.equals(sourceId))).go(); + // }); + // } + + // Future setActiveSource(int id) async { + // await batch((batch) { + // batch.update( + // sources, + // const SourcesCompanion(isActive: Value(null)), + // where: (t) => t.id.isNotValue(id), + // ); + // batch.update( + // sources, + // const SourcesCompanion(isActive: Value(true)), + // where: (t) => t.id.equals(id), + // ); + // }); + // } + + // Future updateSettings(AppSettingsCompanion settings) async { + // await into(appSettings).insertOnConflictUpdate(settings); + // } +} + +extension ArtistToDb on models.Artist { + ArtistsCompanion toDb(int sourceId) => ArtistsCompanion.insert( + sourceId: sourceId, + id: id, + name: name, + starred: Value(starred), + ); +} + +// LazyDatabase _openConnection() { +// return LazyDatabase(() async { +// final dbFolder = await getApplicationDocumentsDirectory(); +// final file = File(p.join(dbFolder.path, 'subtracks.sqlite')); +// // return NativeDatabase.createInBackground(file, logStatements: true); + +// return ErrorLoggingDatabase( +// NativeDatabase.createInBackground(file), +// (e, s) => log.severe('SQL error', e, s), +// ); +// }); +// } + +// @Riverpod(keepAlive: true) +// SubtracksDatabase database(DatabaseRef ref) { +// return SubtracksDatabase(); +// } + +// OrderingTerm _buildOrder(SortBy sort) { +// OrderingMode? mode = sort.dir == SortDirection.asc +// ? OrderingMode.asc +// : OrderingMode.desc; +// return OrderingTerm( +// expression: CustomExpression(sort.column), +// mode: mode, +// ); +// } + +// SimpleSelectStatement listQuery( +// SimpleSelectStatement query, +// ListQuery opt, +// ) { +// if (opt.page.limit > 0) { +// query.limit(opt.page.limit, offset: opt.page.offset); +// } + +// if (opt.sort != null) { +// OrderingMode? mode = opt.sort != null && opt.sort!.dir == SortDirection.asc +// ? OrderingMode.asc +// : OrderingMode.desc; +// query.orderBy([ +// (t) => OrderingTerm( +// expression: CustomExpression(opt.sort!.column), +// mode: mode, +// ), +// ]); +// } + +// for (var filter in opt.filters) { +// query.where((tbl) => buildFilter(filter)); +// } + +// return query; +// } + +// JoinedSelectStatement listQueryJoined( +// JoinedSelectStatement query, +// ListQuery opt, +// ) { +// if (opt.page.limit > 0) { +// query.limit(opt.page.limit, offset: opt.page.offset); +// } + +// if (opt.sort != null) { +// OrderingMode? mode = opt.sort != null && opt.sort!.dir == SortDirection.asc +// ? OrderingMode.asc +// : OrderingMode.desc; +// query.orderBy([ +// OrderingTerm( +// expression: CustomExpression(opt.sort!.column), +// mode: mode, +// ), +// ]); +// } + +// for (var filter in opt.filters) { +// query.where(buildFilter(filter)); +// } + +// return query; +// } + +// CustomExpression buildFilter( +// FilterWith filter, +// ) { +// return filter.when( +// equals: (column, value, invert) => CustomExpression( +// '$column ${invert ? '<>' : '='} \'$value\'', +// ), +// greaterThan: (column, value, orEquals) => CustomExpression( +// '$column ${orEquals ? '>=' : '>'} $value', +// ), +// isNull: (column, invert) => CustomExpression( +// '$column ${invert ? 'IS NOT' : 'IS'} NULL', +// ), +// betweenInt: (column, from, to) => CustomExpression( +// '$column BETWEEN $from AND $to', +// ), +// isIn: (column, invert, values) => CustomExpression( +// '$column ${invert ? 'NOT IN' : 'IN'} (${values.join(',')})', +// ), +// ); +// } + +// class AlbumSongsCompanion { +// final AlbumsCompanion album; +// final Iterable songs; + +// AlbumSongsCompanion(this.album, this.songs); +// } + +// class ArtistAlbumsCompanion { +// final ArtistsCompanion artist; +// final Iterable albums; + +// ArtistAlbumsCompanion(this.artist, this.albums); +// } + +// class PlaylistWithSongsCompanion { +// final PlaylistsCompanion playist; +// final Iterable songs; + +// PlaylistWithSongsCompanion(this.playist, this.songs); +// } + +// Future saveArtist( +// SubtracksDatabase db, +// ArtistAlbumsCompanion artistAlbums, +// ) async { +// return db.background((db) async { +// final artist = artistAlbums.artist; +// final albums = artistAlbums.albums; + +// await db.batch((batch) { +// batch.insertAllOnConflictUpdate(db.artists, [artist]); +// batch.insertAllOnConflictUpdate(db.albums, albums); + +// // remove this artistId from albums not found in source +// // don't delete them since they coud have been moved to another artist +// // that we haven't synced yet +// final albumIds = {for (var a in albums) a.id.value}; +// batch.update( +// db.albums, +// const AlbumsCompanion(artistId: Value(null)), +// where: (tbl) => +// tbl.sourceId.equals(artist.sourceId.value) & +// tbl.artistId.equals(artist.id.value) & +// tbl.id.isNotIn(albumIds), +// ); +// }); +// }); +// } + +// Future saveAlbum( +// SubtracksDatabase db, +// AlbumSongsCompanion albumSongs, +// ) async { +// return db.background((db) async { +// final album = albumSongs.album.copyWith(synced: Value(DateTime.now())); +// final songs = albumSongs.songs; + +// final songIds = {for (var a in songs) a.id.value}; +// final hardDeletedSongIds = (await (db.selectOnly(db.songs) +// ..addColumns([db.songs.id]) +// ..where( +// db.songs.sourceId.equals(album.sourceId.value) & +// db.songs.albumId.equals(album.id.value) & +// db.songs.id.isNotIn(songIds) & +// db.songs.downloadFilePath.isNull() & +// db.songs.downloadTaskId.isNull(), +// )) +// .map((row) => row.read(db.songs.id)) +// .get()) +// .whereNotNull(); + +// await db.batch((batch) { +// batch.insertAllOnConflictUpdate(db.albums, [album]); +// batch.insertAllOnConflictUpdate(db.songs, songs); + +// // soft delete songs that have been downloaded so that the user +// // can decide to keep or remove them later +// // TODO: add a setting to skip soft delete and just remove download too +// batch.update( +// db.songs, +// const SongsCompanion(isDeleted: Value(true)), +// where: (tbl) => +// tbl.sourceId.equals(album.sourceId.value) & +// tbl.albumId.equals(album.id.value) & +// tbl.id.isNotIn(songIds) & +// (tbl.downloadFilePath.isNotNull() | tbl.downloadTaskId.isNotNull()), +// ); + +// // safe to hard delete songs that have not been downloaded +// batch.deleteWhere( +// db.songs, +// (tbl) => +// tbl.sourceId.equals(album.sourceId.value) & +// tbl.id.isIn(hardDeletedSongIds), +// ); + +// // also need to remove these songs from any playlists that contain them +// batch.deleteWhere( +// db.playlistSongs, +// (tbl) => +// tbl.sourceId.equals(album.sourceId.value) & +// tbl.songId.isIn(hardDeletedSongIds), +// ); +// }); +// }); +// } + +// Future savePlaylist( +// SubtracksDatabase db, +// PlaylistWithSongsCompanion playlistWithSongs, +// ) async { +// return db.background((db) async { +// final playlist = +// playlistWithSongs.playist.copyWith(synced: Value(DateTime.now())); +// final songs = playlistWithSongs.songs; + +// await db.batch((batch) { +// batch.insertAllOnConflictUpdate(db.playlists, [playlist]); +// batch.insertAllOnConflictUpdate(db.songs, songs); + +// batch.insertAllOnConflictUpdate( +// db.playlistSongs, +// songs.mapIndexed( +// (index, song) => PlaylistSongsCompanion.insert( +// sourceId: playlist.sourceId.value, +// playlistId: playlist.id.value, +// songId: song.id.value, +// position: index, +// ), +// ), +// ); + +// // the new playlist could be shorter than the old one, so we delete +// // playlist songs above our new playlist's length +// batch.deleteWhere( +// db.playlistSongs, +// (tbl) => +// tbl.sourceId.equals(playlist.sourceId.value) & +// tbl.playlistId.equals(playlist.id.value) & +// tbl.position.isBiggerOrEqualValue(songs.length), +// ); +// }); +// }); +// } diff --git a/lib/database/database.g.dart b/lib/database/database.g.dart new file mode 100644 index 0000000..fc28f03 --- /dev/null +++ b/lib/database/database.g.dart @@ -0,0 +1,4249 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'database.dart'; + +// ignore_for_file: type=lint +class Sources extends Table with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + Sources(this.attachedDatabase, [this._alias]); + static const VerificationMeta _idMeta = const VerificationMeta('id'); + late final GeneratedColumn id = GeneratedColumn( + 'id', + aliasedName, + false, + hasAutoIncrement: true, + type: DriftSqlType.int, + requiredDuringInsert: false, + $customConstraints: 'NOT NULL PRIMARY KEY AUTOINCREMENT', + ); + static const VerificationMeta _nameMeta = const VerificationMeta('name'); + late final GeneratedColumn name = GeneratedColumn( + 'name', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + $customConstraints: 'NOT NULL COLLATE NOCASE', + ); + static const VerificationMeta _isActiveMeta = const VerificationMeta( + 'isActive', + ); + late final GeneratedColumn isActive = GeneratedColumn( + 'is_active', + aliasedName, + true, + type: DriftSqlType.bool, + requiredDuringInsert: false, + $customConstraints: 'UNIQUE', + ); + static const VerificationMeta _createdAtMeta = const VerificationMeta( + 'createdAt', + ); + late final GeneratedColumn createdAt = GeneratedColumn( + 'created_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + $customConstraints: + 'NOT NULL DEFAULT (strftime(\'%s\', CURRENT_TIMESTAMP))', + defaultValue: const CustomExpression('strftime(\'%s\', CURRENT_TIMESTAMP)'), + ); + @override + List get $columns => [id, name, isActive, createdAt]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'sources'; + @override + VerificationContext validateIntegrity( + Insertable instance, { + bool isInserting = false, + }) { + final context = VerificationContext(); + final data = instance.toColumns(true); + if (data.containsKey('id')) { + context.handle(_idMeta, id.isAcceptableOrUnknown(data['id']!, _idMeta)); + } + if (data.containsKey('name')) { + context.handle( + _nameMeta, + name.isAcceptableOrUnknown(data['name']!, _nameMeta), + ); + } else if (isInserting) { + context.missing(_nameMeta); + } + if (data.containsKey('is_active')) { + context.handle( + _isActiveMeta, + isActive.isAcceptableOrUnknown(data['is_active']!, _isActiveMeta), + ); + } + if (data.containsKey('created_at')) { + context.handle( + _createdAtMeta, + createdAt.isAcceptableOrUnknown(data['created_at']!, _createdAtMeta), + ); + } + return context; + } + + @override + Set get $primaryKey => {id}; + @override + Source map(Map data, {String? tablePrefix}) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return Source( + id: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}id'], + )!, + name: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}name'], + )!, + isActive: attachedDatabase.typeMapping.read( + DriftSqlType.bool, + data['${effectivePrefix}is_active'], + ), + createdAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}created_at'], + )!, + ); + } + + @override + Sources createAlias(String alias) { + return Sources(attachedDatabase, alias); + } + + @override + bool get dontWriteConstraints => true; +} + +class Source extends DataClass implements Insertable { + final int id; + final String name; + final bool? isActive; + final DateTime createdAt; + const Source({ + required this.id, + required this.name, + this.isActive, + required this.createdAt, + }); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['id'] = Variable(id); + map['name'] = Variable(name); + if (!nullToAbsent || isActive != null) { + map['is_active'] = Variable(isActive); + } + map['created_at'] = Variable(createdAt); + return map; + } + + SourcesCompanion toCompanion(bool nullToAbsent) { + return SourcesCompanion( + id: Value(id), + name: Value(name), + isActive: isActive == null && nullToAbsent + ? const Value.absent() + : Value(isActive), + createdAt: Value(createdAt), + ); + } + + factory Source.fromJson( + Map json, { + ValueSerializer? serializer, + }) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return Source( + id: serializer.fromJson(json['id']), + name: serializer.fromJson(json['name']), + isActive: serializer.fromJson(json['is_active']), + createdAt: serializer.fromJson(json['created_at']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'id': serializer.toJson(id), + 'name': serializer.toJson(name), + 'is_active': serializer.toJson(isActive), + 'created_at': serializer.toJson(createdAt), + }; + } + + Source copyWith({ + int? id, + String? name, + Value isActive = const Value.absent(), + DateTime? createdAt, + }) => Source( + id: id ?? this.id, + name: name ?? this.name, + isActive: isActive.present ? isActive.value : this.isActive, + createdAt: createdAt ?? this.createdAt, + ); + Source copyWithCompanion(SourcesCompanion data) { + return Source( + id: data.id.present ? data.id.value : this.id, + name: data.name.present ? data.name.value : this.name, + isActive: data.isActive.present ? data.isActive.value : this.isActive, + createdAt: data.createdAt.present ? data.createdAt.value : this.createdAt, + ); + } + + @override + String toString() { + return (StringBuffer('Source(') + ..write('id: $id, ') + ..write('name: $name, ') + ..write('isActive: $isActive, ') + ..write('createdAt: $createdAt') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hash(id, name, isActive, createdAt); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is Source && + other.id == this.id && + other.name == this.name && + other.isActive == this.isActive && + other.createdAt == this.createdAt); +} + +class SourcesCompanion extends UpdateCompanion { + final Value id; + final Value name; + final Value isActive; + final Value createdAt; + const SourcesCompanion({ + this.id = const Value.absent(), + this.name = const Value.absent(), + this.isActive = const Value.absent(), + this.createdAt = const Value.absent(), + }); + SourcesCompanion.insert({ + this.id = const Value.absent(), + required String name, + this.isActive = const Value.absent(), + this.createdAt = const Value.absent(), + }) : name = Value(name); + static Insertable custom({ + Expression? id, + Expression? name, + Expression? isActive, + Expression? createdAt, + }) { + return RawValuesInsertable({ + if (id != null) 'id': id, + if (name != null) 'name': name, + if (isActive != null) 'is_active': isActive, + if (createdAt != null) 'created_at': createdAt, + }); + } + + SourcesCompanion copyWith({ + Value? id, + Value? name, + Value? isActive, + Value? createdAt, + }) { + return SourcesCompanion( + id: id ?? this.id, + name: name ?? this.name, + isActive: isActive ?? this.isActive, + createdAt: createdAt ?? this.createdAt, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (id.present) { + map['id'] = Variable(id.value); + } + if (name.present) { + map['name'] = Variable(name.value); + } + if (isActive.present) { + map['is_active'] = Variable(isActive.value); + } + if (createdAt.present) { + map['created_at'] = Variable(createdAt.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('SourcesCompanion(') + ..write('id: $id, ') + ..write('name: $name, ') + ..write('isActive: $isActive, ') + ..write('createdAt: $createdAt') + ..write(')')) + .toString(); + } +} + +class SubsonicSourceOptions extends Table + with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + SubsonicSourceOptions(this.attachedDatabase, [this._alias]); + static const VerificationMeta _sourceIdMeta = const VerificationMeta( + 'sourceId', + ); + late final GeneratedColumn sourceId = GeneratedColumn( + 'source_id', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: false, + $customConstraints: 'NOT NULL PRIMARY KEY', + ); + late final GeneratedColumnWithTypeConverter address = + GeneratedColumn( + 'address', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + $customConstraints: 'NOT NULL', + ).withConverter(SubsonicSourceOptions.$converteraddress); + static const VerificationMeta _usernameMeta = const VerificationMeta( + 'username', + ); + late final GeneratedColumn username = GeneratedColumn( + 'username', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + $customConstraints: 'NOT NULL', + ); + static const VerificationMeta _passwordMeta = const VerificationMeta( + 'password', + ); + late final GeneratedColumn password = GeneratedColumn( + 'password', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + $customConstraints: 'NOT NULL', + ); + static const VerificationMeta _useTokenAuthMeta = const VerificationMeta( + 'useTokenAuth', + ); + late final GeneratedColumn useTokenAuth = GeneratedColumn( + 'use_token_auth', + aliasedName, + false, + type: DriftSqlType.bool, + requiredDuringInsert: false, + $customConstraints: 'NOT NULL DEFAULT 1', + defaultValue: const CustomExpression('1'), + ); + @override + List get $columns => [ + sourceId, + address, + username, + password, + useTokenAuth, + ]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'subsonic_source_options'; + @override + VerificationContext validateIntegrity( + Insertable instance, { + bool isInserting = false, + }) { + final context = VerificationContext(); + final data = instance.toColumns(true); + if (data.containsKey('source_id')) { + context.handle( + _sourceIdMeta, + sourceId.isAcceptableOrUnknown(data['source_id']!, _sourceIdMeta), + ); + } + if (data.containsKey('username')) { + context.handle( + _usernameMeta, + username.isAcceptableOrUnknown(data['username']!, _usernameMeta), + ); + } else if (isInserting) { + context.missing(_usernameMeta); + } + if (data.containsKey('password')) { + context.handle( + _passwordMeta, + password.isAcceptableOrUnknown(data['password']!, _passwordMeta), + ); + } else if (isInserting) { + context.missing(_passwordMeta); + } + if (data.containsKey('use_token_auth')) { + context.handle( + _useTokenAuthMeta, + useTokenAuth.isAcceptableOrUnknown( + data['use_token_auth']!, + _useTokenAuthMeta, + ), + ); + } + return context; + } + + @override + Set get $primaryKey => {sourceId}; + @override + SubsonicSourceOption map(Map data, {String? tablePrefix}) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return SubsonicSourceOption( + sourceId: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}source_id'], + )!, + address: SubsonicSourceOptions.$converteraddress.fromSql( + attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}address'], + )!, + ), + username: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}username'], + )!, + password: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}password'], + )!, + useTokenAuth: attachedDatabase.typeMapping.read( + DriftSqlType.bool, + data['${effectivePrefix}use_token_auth'], + )!, + ); + } + + @override + SubsonicSourceOptions createAlias(String alias) { + return SubsonicSourceOptions(attachedDatabase, alias); + } + + static TypeConverter $converteraddress = const UriConverter(); + @override + List get customConstraints => const [ + 'FOREIGN KEY(source_id)REFERENCES sources(id)ON DELETE CASCADE', + ]; + @override + bool get dontWriteConstraints => true; +} + +class SubsonicSourceOption extends DataClass + implements Insertable { + final int sourceId; + final Uri address; + final String username; + final String password; + final bool useTokenAuth; + const SubsonicSourceOption({ + required this.sourceId, + required this.address, + required this.username, + required this.password, + required this.useTokenAuth, + }); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['source_id'] = Variable(sourceId); + { + map['address'] = Variable( + SubsonicSourceOptions.$converteraddress.toSql(address), + ); + } + map['username'] = Variable(username); + map['password'] = Variable(password); + map['use_token_auth'] = Variable(useTokenAuth); + return map; + } + + SubsonicSourceOptionsCompanion toCompanion(bool nullToAbsent) { + return SubsonicSourceOptionsCompanion( + sourceId: Value(sourceId), + address: Value(address), + username: Value(username), + password: Value(password), + useTokenAuth: Value(useTokenAuth), + ); + } + + factory SubsonicSourceOption.fromJson( + Map json, { + ValueSerializer? serializer, + }) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return SubsonicSourceOption( + sourceId: serializer.fromJson(json['source_id']), + address: serializer.fromJson(json['address']), + username: serializer.fromJson(json['username']), + password: serializer.fromJson(json['password']), + useTokenAuth: serializer.fromJson(json['use_token_auth']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'source_id': serializer.toJson(sourceId), + 'address': serializer.toJson(address), + 'username': serializer.toJson(username), + 'password': serializer.toJson(password), + 'use_token_auth': serializer.toJson(useTokenAuth), + }; + } + + SubsonicSourceOption copyWith({ + int? sourceId, + Uri? address, + String? username, + String? password, + bool? useTokenAuth, + }) => SubsonicSourceOption( + sourceId: sourceId ?? this.sourceId, + address: address ?? this.address, + username: username ?? this.username, + password: password ?? this.password, + useTokenAuth: useTokenAuth ?? this.useTokenAuth, + ); + SubsonicSourceOption copyWithCompanion(SubsonicSourceOptionsCompanion data) { + return SubsonicSourceOption( + sourceId: data.sourceId.present ? data.sourceId.value : this.sourceId, + address: data.address.present ? data.address.value : this.address, + username: data.username.present ? data.username.value : this.username, + password: data.password.present ? data.password.value : this.password, + useTokenAuth: data.useTokenAuth.present + ? data.useTokenAuth.value + : this.useTokenAuth, + ); + } + + @override + String toString() { + return (StringBuffer('SubsonicSourceOption(') + ..write('sourceId: $sourceId, ') + ..write('address: $address, ') + ..write('username: $username, ') + ..write('password: $password, ') + ..write('useTokenAuth: $useTokenAuth') + ..write(')')) + .toString(); + } + + @override + int get hashCode => + Object.hash(sourceId, address, username, password, useTokenAuth); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is SubsonicSourceOption && + other.sourceId == this.sourceId && + other.address == this.address && + other.username == this.username && + other.password == this.password && + other.useTokenAuth == this.useTokenAuth); +} + +class SubsonicSourceOptionsCompanion + extends UpdateCompanion { + final Value sourceId; + final Value address; + final Value username; + final Value password; + final Value useTokenAuth; + const SubsonicSourceOptionsCompanion({ + this.sourceId = const Value.absent(), + this.address = const Value.absent(), + this.username = const Value.absent(), + this.password = const Value.absent(), + this.useTokenAuth = const Value.absent(), + }); + SubsonicSourceOptionsCompanion.insert({ + this.sourceId = const Value.absent(), + required Uri address, + required String username, + required String password, + this.useTokenAuth = const Value.absent(), + }) : address = Value(address), + username = Value(username), + password = Value(password); + static Insertable custom({ + Expression? sourceId, + Expression? address, + Expression? username, + Expression? password, + Expression? useTokenAuth, + }) { + return RawValuesInsertable({ + if (sourceId != null) 'source_id': sourceId, + if (address != null) 'address': address, + if (username != null) 'username': username, + if (password != null) 'password': password, + if (useTokenAuth != null) 'use_token_auth': useTokenAuth, + }); + } + + SubsonicSourceOptionsCompanion copyWith({ + Value? sourceId, + Value? address, + Value? username, + Value? password, + Value? useTokenAuth, + }) { + return SubsonicSourceOptionsCompanion( + sourceId: sourceId ?? this.sourceId, + address: address ?? this.address, + username: username ?? this.username, + password: password ?? this.password, + useTokenAuth: useTokenAuth ?? this.useTokenAuth, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (sourceId.present) { + map['source_id'] = Variable(sourceId.value); + } + if (address.present) { + map['address'] = Variable( + SubsonicSourceOptions.$converteraddress.toSql(address.value), + ); + } + if (username.present) { + map['username'] = Variable(username.value); + } + if (password.present) { + map['password'] = Variable(password.value); + } + if (useTokenAuth.present) { + map['use_token_auth'] = Variable(useTokenAuth.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('SubsonicSourceOptionsCompanion(') + ..write('sourceId: $sourceId, ') + ..write('address: $address, ') + ..write('username: $username, ') + ..write('password: $password, ') + ..write('useTokenAuth: $useTokenAuth') + ..write(')')) + .toString(); + } +} + +class Artists extends Table with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + Artists(this.attachedDatabase, [this._alias]); + static const VerificationMeta _sourceIdMeta = const VerificationMeta( + 'sourceId', + ); + late final GeneratedColumn sourceId = GeneratedColumn( + 'source_id', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + $customConstraints: 'NOT NULL', + ); + static const VerificationMeta _idMeta = const VerificationMeta('id'); + late final GeneratedColumn id = GeneratedColumn( + 'id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + $customConstraints: 'NOT NULL', + ); + static const VerificationMeta _nameMeta = const VerificationMeta('name'); + late final GeneratedColumn name = GeneratedColumn( + 'name', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + $customConstraints: 'NOT NULL COLLATE NOCASE', + ); + static const VerificationMeta _starredMeta = const VerificationMeta( + 'starred', + ); + late final GeneratedColumn starred = GeneratedColumn( + 'starred', + aliasedName, + true, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + $customConstraints: '', + ); + @override + List get $columns => [sourceId, id, name, starred]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'artists'; + @override + VerificationContext validateIntegrity( + Insertable instance, { + bool isInserting = false, + }) { + final context = VerificationContext(); + final data = instance.toColumns(true); + if (data.containsKey('source_id')) { + context.handle( + _sourceIdMeta, + sourceId.isAcceptableOrUnknown(data['source_id']!, _sourceIdMeta), + ); + } else if (isInserting) { + context.missing(_sourceIdMeta); + } + if (data.containsKey('id')) { + context.handle(_idMeta, id.isAcceptableOrUnknown(data['id']!, _idMeta)); + } else if (isInserting) { + context.missing(_idMeta); + } + if (data.containsKey('name')) { + context.handle( + _nameMeta, + name.isAcceptableOrUnknown(data['name']!, _nameMeta), + ); + } else if (isInserting) { + context.missing(_nameMeta); + } + if (data.containsKey('starred')) { + context.handle( + _starredMeta, + starred.isAcceptableOrUnknown(data['starred']!, _starredMeta), + ); + } + return context; + } + + @override + Set get $primaryKey => {sourceId, id}; + @override + models.Artist map(Map data, {String? tablePrefix}) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return models.Artist( + id: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}id'], + )!, + name: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}name'], + )!, + starred: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}starred'], + ), + ); + } + + @override + Artists createAlias(String alias) { + return Artists(attachedDatabase, alias); + } + + @override + List get customConstraints => const [ + 'PRIMARY KEY(source_id, id)', + 'FOREIGN KEY(source_id)REFERENCES sources(id)ON DELETE CASCADE', + ]; + @override + bool get dontWriteConstraints => true; +} + +class ArtistsCompanion extends UpdateCompanion { + final Value sourceId; + final Value id; + final Value name; + final Value starred; + final Value rowid; + const ArtistsCompanion({ + this.sourceId = const Value.absent(), + this.id = const Value.absent(), + this.name = const Value.absent(), + this.starred = const Value.absent(), + this.rowid = const Value.absent(), + }); + ArtistsCompanion.insert({ + required int sourceId, + required String id, + required String name, + this.starred = const Value.absent(), + this.rowid = const Value.absent(), + }) : sourceId = Value(sourceId), + id = Value(id), + name = Value(name); + static Insertable custom({ + Expression? sourceId, + Expression? id, + Expression? name, + Expression? starred, + Expression? rowid, + }) { + return RawValuesInsertable({ + if (sourceId != null) 'source_id': sourceId, + if (id != null) 'id': id, + if (name != null) 'name': name, + if (starred != null) 'starred': starred, + if (rowid != null) 'rowid': rowid, + }); + } + + ArtistsCompanion copyWith({ + Value? sourceId, + Value? id, + Value? name, + Value? starred, + Value? rowid, + }) { + return ArtistsCompanion( + sourceId: sourceId ?? this.sourceId, + id: id ?? this.id, + name: name ?? this.name, + starred: starred ?? this.starred, + rowid: rowid ?? this.rowid, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (sourceId.present) { + map['source_id'] = Variable(sourceId.value); + } + if (id.present) { + map['id'] = Variable(id.value); + } + if (name.present) { + map['name'] = Variable(name.value); + } + if (starred.present) { + map['starred'] = Variable(starred.value); + } + if (rowid.present) { + map['rowid'] = Variable(rowid.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('ArtistsCompanion(') + ..write('sourceId: $sourceId, ') + ..write('id: $id, ') + ..write('name: $name, ') + ..write('starred: $starred, ') + ..write('rowid: $rowid') + ..write(')')) + .toString(); + } +} + +class Albums extends Table with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + Albums(this.attachedDatabase, [this._alias]); + static const VerificationMeta _sourceIdMeta = const VerificationMeta( + 'sourceId', + ); + late final GeneratedColumn sourceId = GeneratedColumn( + 'source_id', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + $customConstraints: 'NOT NULL', + ); + static const VerificationMeta _idMeta = const VerificationMeta('id'); + late final GeneratedColumn id = GeneratedColumn( + 'id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + $customConstraints: 'NOT NULL', + ); + static const VerificationMeta _artistIdMeta = const VerificationMeta( + 'artistId', + ); + late final GeneratedColumn artistId = GeneratedColumn( + 'artist_id', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + $customConstraints: '', + ); + static const VerificationMeta _nameMeta = const VerificationMeta('name'); + late final GeneratedColumn name = GeneratedColumn( + 'name', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + $customConstraints: 'NOT NULL COLLATE NOCASE', + ); + static const VerificationMeta _albumArtistMeta = const VerificationMeta( + 'albumArtist', + ); + late final GeneratedColumn albumArtist = GeneratedColumn( + 'album_artist', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + $customConstraints: 'COLLATE NOCASE', + ); + static const VerificationMeta _createdMeta = const VerificationMeta( + 'created', + ); + late final GeneratedColumn created = GeneratedColumn( + 'created', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: true, + $customConstraints: 'NOT NULL', + ); + static const VerificationMeta _coverArtMeta = const VerificationMeta( + 'coverArt', + ); + late final GeneratedColumn coverArt = GeneratedColumn( + 'cover_art', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + $customConstraints: '', + ); + static const VerificationMeta _genreMeta = const VerificationMeta('genre'); + late final GeneratedColumn genre = GeneratedColumn( + 'genre', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + $customConstraints: '', + ); + static const VerificationMeta _yearMeta = const VerificationMeta('year'); + late final GeneratedColumn year = GeneratedColumn( + 'year', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + $customConstraints: '', + ); + static const VerificationMeta _starredMeta = const VerificationMeta( + 'starred', + ); + late final GeneratedColumn starred = GeneratedColumn( + 'starred', + aliasedName, + true, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + $customConstraints: '', + ); + static const VerificationMeta _frequentRankMeta = const VerificationMeta( + 'frequentRank', + ); + late final GeneratedColumn frequentRank = GeneratedColumn( + 'frequent_rank', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + $customConstraints: '', + ); + static const VerificationMeta _recentRankMeta = const VerificationMeta( + 'recentRank', + ); + late final GeneratedColumn recentRank = GeneratedColumn( + 'recent_rank', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + $customConstraints: '', + ); + @override + List get $columns => [ + sourceId, + id, + artistId, + name, + albumArtist, + created, + coverArt, + genre, + year, + starred, + frequentRank, + recentRank, + ]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'albums'; + @override + VerificationContext validateIntegrity( + Insertable instance, { + bool isInserting = false, + }) { + final context = VerificationContext(); + final data = instance.toColumns(true); + if (data.containsKey('source_id')) { + context.handle( + _sourceIdMeta, + sourceId.isAcceptableOrUnknown(data['source_id']!, _sourceIdMeta), + ); + } else if (isInserting) { + context.missing(_sourceIdMeta); + } + if (data.containsKey('id')) { + context.handle(_idMeta, id.isAcceptableOrUnknown(data['id']!, _idMeta)); + } else if (isInserting) { + context.missing(_idMeta); + } + if (data.containsKey('artist_id')) { + context.handle( + _artistIdMeta, + artistId.isAcceptableOrUnknown(data['artist_id']!, _artistIdMeta), + ); + } + if (data.containsKey('name')) { + context.handle( + _nameMeta, + name.isAcceptableOrUnknown(data['name']!, _nameMeta), + ); + } else if (isInserting) { + context.missing(_nameMeta); + } + if (data.containsKey('album_artist')) { + context.handle( + _albumArtistMeta, + albumArtist.isAcceptableOrUnknown( + data['album_artist']!, + _albumArtistMeta, + ), + ); + } + if (data.containsKey('created')) { + context.handle( + _createdMeta, + created.isAcceptableOrUnknown(data['created']!, _createdMeta), + ); + } else if (isInserting) { + context.missing(_createdMeta); + } + if (data.containsKey('cover_art')) { + context.handle( + _coverArtMeta, + coverArt.isAcceptableOrUnknown(data['cover_art']!, _coverArtMeta), + ); + } + if (data.containsKey('genre')) { + context.handle( + _genreMeta, + genre.isAcceptableOrUnknown(data['genre']!, _genreMeta), + ); + } + if (data.containsKey('year')) { + context.handle( + _yearMeta, + year.isAcceptableOrUnknown(data['year']!, _yearMeta), + ); + } + if (data.containsKey('starred')) { + context.handle( + _starredMeta, + starred.isAcceptableOrUnknown(data['starred']!, _starredMeta), + ); + } + if (data.containsKey('frequent_rank')) { + context.handle( + _frequentRankMeta, + frequentRank.isAcceptableOrUnknown( + data['frequent_rank']!, + _frequentRankMeta, + ), + ); + } + if (data.containsKey('recent_rank')) { + context.handle( + _recentRankMeta, + recentRank.isAcceptableOrUnknown(data['recent_rank']!, _recentRankMeta), + ); + } + return context; + } + + @override + Set get $primaryKey => {sourceId, id}; + @override + models.Album map(Map data, {String? tablePrefix}) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return models.Album( + id: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}id'], + )!, + artistId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}artist_id'], + ), + name: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}name'], + )!, + albumArtist: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}album_artist'], + ), + created: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}created'], + )!, + coverArt: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}cover_art'], + ), + year: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}year'], + ), + starred: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}starred'], + ), + genre: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}genre'], + ), + frequentRank: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}frequent_rank'], + ), + recentRank: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}recent_rank'], + ), + ); + } + + @override + Albums createAlias(String alias) { + return Albums(attachedDatabase, alias); + } + + @override + List get customConstraints => const [ + 'PRIMARY KEY(source_id, id)', + 'FOREIGN KEY(source_id)REFERENCES sources(id)ON DELETE CASCADE', + ]; + @override + bool get dontWriteConstraints => true; +} + +class AlbumsCompanion extends UpdateCompanion { + final Value sourceId; + final Value id; + final Value artistId; + final Value name; + final Value albumArtist; + final Value created; + final Value coverArt; + final Value genre; + final Value year; + final Value starred; + final Value frequentRank; + final Value recentRank; + final Value rowid; + const AlbumsCompanion({ + this.sourceId = const Value.absent(), + this.id = const Value.absent(), + this.artistId = const Value.absent(), + this.name = const Value.absent(), + this.albumArtist = const Value.absent(), + this.created = const Value.absent(), + this.coverArt = const Value.absent(), + this.genre = const Value.absent(), + this.year = const Value.absent(), + this.starred = const Value.absent(), + this.frequentRank = const Value.absent(), + this.recentRank = const Value.absent(), + this.rowid = const Value.absent(), + }); + AlbumsCompanion.insert({ + required int sourceId, + required String id, + this.artistId = const Value.absent(), + required String name, + this.albumArtist = const Value.absent(), + required DateTime created, + this.coverArt = const Value.absent(), + this.genre = const Value.absent(), + this.year = const Value.absent(), + this.starred = const Value.absent(), + this.frequentRank = const Value.absent(), + this.recentRank = const Value.absent(), + this.rowid = const Value.absent(), + }) : sourceId = Value(sourceId), + id = Value(id), + name = Value(name), + created = Value(created); + static Insertable custom({ + Expression? sourceId, + Expression? id, + Expression? artistId, + Expression? name, + Expression? albumArtist, + Expression? created, + Expression? coverArt, + Expression? genre, + Expression? year, + Expression? starred, + Expression? frequentRank, + Expression? recentRank, + Expression? rowid, + }) { + return RawValuesInsertable({ + if (sourceId != null) 'source_id': sourceId, + if (id != null) 'id': id, + if (artistId != null) 'artist_id': artistId, + if (name != null) 'name': name, + if (albumArtist != null) 'album_artist': albumArtist, + if (created != null) 'created': created, + if (coverArt != null) 'cover_art': coverArt, + if (genre != null) 'genre': genre, + if (year != null) 'year': year, + if (starred != null) 'starred': starred, + if (frequentRank != null) 'frequent_rank': frequentRank, + if (recentRank != null) 'recent_rank': recentRank, + if (rowid != null) 'rowid': rowid, + }); + } + + AlbumsCompanion copyWith({ + Value? sourceId, + Value? id, + Value? artistId, + Value? name, + Value? albumArtist, + Value? created, + Value? coverArt, + Value? genre, + Value? year, + Value? starred, + Value? frequentRank, + Value? recentRank, + Value? rowid, + }) { + return AlbumsCompanion( + sourceId: sourceId ?? this.sourceId, + id: id ?? this.id, + artistId: artistId ?? this.artistId, + name: name ?? this.name, + albumArtist: albumArtist ?? this.albumArtist, + created: created ?? this.created, + coverArt: coverArt ?? this.coverArt, + genre: genre ?? this.genre, + year: year ?? this.year, + starred: starred ?? this.starred, + frequentRank: frequentRank ?? this.frequentRank, + recentRank: recentRank ?? this.recentRank, + rowid: rowid ?? this.rowid, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (sourceId.present) { + map['source_id'] = Variable(sourceId.value); + } + if (id.present) { + map['id'] = Variable(id.value); + } + if (artistId.present) { + map['artist_id'] = Variable(artistId.value); + } + if (name.present) { + map['name'] = Variable(name.value); + } + if (albumArtist.present) { + map['album_artist'] = Variable(albumArtist.value); + } + if (created.present) { + map['created'] = Variable(created.value); + } + if (coverArt.present) { + map['cover_art'] = Variable(coverArt.value); + } + if (genre.present) { + map['genre'] = Variable(genre.value); + } + if (year.present) { + map['year'] = Variable(year.value); + } + if (starred.present) { + map['starred'] = Variable(starred.value); + } + if (frequentRank.present) { + map['frequent_rank'] = Variable(frequentRank.value); + } + if (recentRank.present) { + map['recent_rank'] = Variable(recentRank.value); + } + if (rowid.present) { + map['rowid'] = Variable(rowid.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('AlbumsCompanion(') + ..write('sourceId: $sourceId, ') + ..write('id: $id, ') + ..write('artistId: $artistId, ') + ..write('name: $name, ') + ..write('albumArtist: $albumArtist, ') + ..write('created: $created, ') + ..write('coverArt: $coverArt, ') + ..write('genre: $genre, ') + ..write('year: $year, ') + ..write('starred: $starred, ') + ..write('frequentRank: $frequentRank, ') + ..write('recentRank: $recentRank, ') + ..write('rowid: $rowid') + ..write(')')) + .toString(); + } +} + +class Playlists extends Table with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + Playlists(this.attachedDatabase, [this._alias]); + static const VerificationMeta _sourceIdMeta = const VerificationMeta( + 'sourceId', + ); + late final GeneratedColumn sourceId = GeneratedColumn( + 'source_id', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + $customConstraints: 'NOT NULL', + ); + static const VerificationMeta _idMeta = const VerificationMeta('id'); + late final GeneratedColumn id = GeneratedColumn( + 'id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + $customConstraints: 'NOT NULL', + ); + static const VerificationMeta _nameMeta = const VerificationMeta('name'); + late final GeneratedColumn name = GeneratedColumn( + 'name', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + $customConstraints: 'NOT NULL COLLATE NOCASE', + ); + static const VerificationMeta _commentMeta = const VerificationMeta( + 'comment', + ); + late final GeneratedColumn comment = GeneratedColumn( + 'comment', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + $customConstraints: 'COLLATE NOCASE', + ); + static const VerificationMeta _coverArtMeta = const VerificationMeta( + 'coverArt', + ); + late final GeneratedColumn coverArt = GeneratedColumn( + 'cover_art', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + $customConstraints: '', + ); + static const VerificationMeta _songCountMeta = const VerificationMeta( + 'songCount', + ); + late final GeneratedColumn songCount = GeneratedColumn( + 'song_count', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + $customConstraints: 'NOT NULL', + ); + static const VerificationMeta _createdMeta = const VerificationMeta( + 'created', + ); + late final GeneratedColumn created = GeneratedColumn( + 'created', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: true, + $customConstraints: 'NOT NULL', + ); + static const VerificationMeta _changedMeta = const VerificationMeta( + 'changed', + ); + late final GeneratedColumn changed = GeneratedColumn( + 'changed', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + $customConstraints: + 'NOT NULL DEFAULT (strftime(\'%s\', CURRENT_TIMESTAMP))', + defaultValue: const CustomExpression('strftime(\'%s\', CURRENT_TIMESTAMP)'), + ); + @override + List get $columns => [ + sourceId, + id, + name, + comment, + coverArt, + songCount, + created, + changed, + ]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'playlists'; + @override + VerificationContext validateIntegrity( + Insertable instance, { + bool isInserting = false, + }) { + final context = VerificationContext(); + final data = instance.toColumns(true); + if (data.containsKey('source_id')) { + context.handle( + _sourceIdMeta, + sourceId.isAcceptableOrUnknown(data['source_id']!, _sourceIdMeta), + ); + } else if (isInserting) { + context.missing(_sourceIdMeta); + } + if (data.containsKey('id')) { + context.handle(_idMeta, id.isAcceptableOrUnknown(data['id']!, _idMeta)); + } else if (isInserting) { + context.missing(_idMeta); + } + if (data.containsKey('name')) { + context.handle( + _nameMeta, + name.isAcceptableOrUnknown(data['name']!, _nameMeta), + ); + } else if (isInserting) { + context.missing(_nameMeta); + } + if (data.containsKey('comment')) { + context.handle( + _commentMeta, + comment.isAcceptableOrUnknown(data['comment']!, _commentMeta), + ); + } + if (data.containsKey('cover_art')) { + context.handle( + _coverArtMeta, + coverArt.isAcceptableOrUnknown(data['cover_art']!, _coverArtMeta), + ); + } + if (data.containsKey('song_count')) { + context.handle( + _songCountMeta, + songCount.isAcceptableOrUnknown(data['song_count']!, _songCountMeta), + ); + } else if (isInserting) { + context.missing(_songCountMeta); + } + if (data.containsKey('created')) { + context.handle( + _createdMeta, + created.isAcceptableOrUnknown(data['created']!, _createdMeta), + ); + } else if (isInserting) { + context.missing(_createdMeta); + } + if (data.containsKey('changed')) { + context.handle( + _changedMeta, + changed.isAcceptableOrUnknown(data['changed']!, _changedMeta), + ); + } + return context; + } + + @override + Set get $primaryKey => {sourceId, id}; + @override + models.Playlist map(Map data, {String? tablePrefix}) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return models.Playlist( + id: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}id'], + )!, + name: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}name'], + )!, + comment: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}comment'], + ), + created: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}created'], + )!, + changed: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}changed'], + )!, + coverArt: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}cover_art'], + ), + ); + } + + @override + Playlists createAlias(String alias) { + return Playlists(attachedDatabase, alias); + } + + @override + List get customConstraints => const [ + 'PRIMARY KEY(source_id, id)', + 'FOREIGN KEY(source_id)REFERENCES sources(id)ON DELETE CASCADE', + ]; + @override + bool get dontWriteConstraints => true; +} + +class PlaylistsCompanion extends UpdateCompanion { + final Value sourceId; + final Value id; + final Value name; + final Value comment; + final Value coverArt; + final Value songCount; + final Value created; + final Value changed; + final Value rowid; + const PlaylistsCompanion({ + this.sourceId = const Value.absent(), + this.id = const Value.absent(), + this.name = const Value.absent(), + this.comment = const Value.absent(), + this.coverArt = const Value.absent(), + this.songCount = const Value.absent(), + this.created = const Value.absent(), + this.changed = const Value.absent(), + this.rowid = const Value.absent(), + }); + PlaylistsCompanion.insert({ + required int sourceId, + required String id, + required String name, + this.comment = const Value.absent(), + this.coverArt = const Value.absent(), + required int songCount, + required DateTime created, + this.changed = const Value.absent(), + this.rowid = const Value.absent(), + }) : sourceId = Value(sourceId), + id = Value(id), + name = Value(name), + songCount = Value(songCount), + created = Value(created); + static Insertable custom({ + Expression? sourceId, + Expression? id, + Expression? name, + Expression? comment, + Expression? coverArt, + Expression? songCount, + Expression? created, + Expression? changed, + Expression? rowid, + }) { + return RawValuesInsertable({ + if (sourceId != null) 'source_id': sourceId, + if (id != null) 'id': id, + if (name != null) 'name': name, + if (comment != null) 'comment': comment, + if (coverArt != null) 'cover_art': coverArt, + if (songCount != null) 'song_count': songCount, + if (created != null) 'created': created, + if (changed != null) 'changed': changed, + if (rowid != null) 'rowid': rowid, + }); + } + + PlaylistsCompanion copyWith({ + Value? sourceId, + Value? id, + Value? name, + Value? comment, + Value? coverArt, + Value? songCount, + Value? created, + Value? changed, + Value? rowid, + }) { + return PlaylistsCompanion( + sourceId: sourceId ?? this.sourceId, + id: id ?? this.id, + name: name ?? this.name, + comment: comment ?? this.comment, + coverArt: coverArt ?? this.coverArt, + songCount: songCount ?? this.songCount, + created: created ?? this.created, + changed: changed ?? this.changed, + rowid: rowid ?? this.rowid, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (sourceId.present) { + map['source_id'] = Variable(sourceId.value); + } + if (id.present) { + map['id'] = Variable(id.value); + } + if (name.present) { + map['name'] = Variable(name.value); + } + if (comment.present) { + map['comment'] = Variable(comment.value); + } + if (coverArt.present) { + map['cover_art'] = Variable(coverArt.value); + } + if (songCount.present) { + map['song_count'] = Variable(songCount.value); + } + if (created.present) { + map['created'] = Variable(created.value); + } + if (changed.present) { + map['changed'] = Variable(changed.value); + } + if (rowid.present) { + map['rowid'] = Variable(rowid.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('PlaylistsCompanion(') + ..write('sourceId: $sourceId, ') + ..write('id: $id, ') + ..write('name: $name, ') + ..write('comment: $comment, ') + ..write('coverArt: $coverArt, ') + ..write('songCount: $songCount, ') + ..write('created: $created, ') + ..write('changed: $changed, ') + ..write('rowid: $rowid') + ..write(')')) + .toString(); + } +} + +class PlaylistSongs extends Table + with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + PlaylistSongs(this.attachedDatabase, [this._alias]); + static const VerificationMeta _sourceIdMeta = const VerificationMeta( + 'sourceId', + ); + late final GeneratedColumn sourceId = GeneratedColumn( + 'source_id', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + $customConstraints: 'NOT NULL', + ); + static const VerificationMeta _playlistIdMeta = const VerificationMeta( + 'playlistId', + ); + late final GeneratedColumn playlistId = GeneratedColumn( + 'playlist_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + $customConstraints: 'NOT NULL', + ); + static const VerificationMeta _songIdMeta = const VerificationMeta('songId'); + late final GeneratedColumn songId = GeneratedColumn( + 'song_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + $customConstraints: 'NOT NULL', + ); + static const VerificationMeta _positionMeta = const VerificationMeta( + 'position', + ); + late final GeneratedColumn position = GeneratedColumn( + 'position', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + $customConstraints: 'NOT NULL', + ); + @override + List get $columns => [ + sourceId, + playlistId, + songId, + position, + ]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'playlist_songs'; + @override + VerificationContext validateIntegrity( + Insertable instance, { + bool isInserting = false, + }) { + final context = VerificationContext(); + final data = instance.toColumns(true); + if (data.containsKey('source_id')) { + context.handle( + _sourceIdMeta, + sourceId.isAcceptableOrUnknown(data['source_id']!, _sourceIdMeta), + ); + } else if (isInserting) { + context.missing(_sourceIdMeta); + } + if (data.containsKey('playlist_id')) { + context.handle( + _playlistIdMeta, + playlistId.isAcceptableOrUnknown(data['playlist_id']!, _playlistIdMeta), + ); + } else if (isInserting) { + context.missing(_playlistIdMeta); + } + if (data.containsKey('song_id')) { + context.handle( + _songIdMeta, + songId.isAcceptableOrUnknown(data['song_id']!, _songIdMeta), + ); + } else if (isInserting) { + context.missing(_songIdMeta); + } + if (data.containsKey('position')) { + context.handle( + _positionMeta, + position.isAcceptableOrUnknown(data['position']!, _positionMeta), + ); + } else if (isInserting) { + context.missing(_positionMeta); + } + return context; + } + + @override + Set get $primaryKey => {sourceId, playlistId, position}; + @override + models.PlaylistSong map(Map data, {String? tablePrefix}) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return models.PlaylistSong( + playlistId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}playlist_id'], + )!, + songId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}song_id'], + )!, + position: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}position'], + )!, + ); + } + + @override + PlaylistSongs createAlias(String alias) { + return PlaylistSongs(attachedDatabase, alias); + } + + @override + List get customConstraints => const [ + 'PRIMARY KEY(source_id, playlist_id, position)', + 'FOREIGN KEY(source_id)REFERENCES sources(id)ON DELETE CASCADE', + ]; + @override + bool get dontWriteConstraints => true; +} + +class PlaylistSongsCompanion extends UpdateCompanion { + final Value sourceId; + final Value playlistId; + final Value songId; + final Value position; + final Value rowid; + const PlaylistSongsCompanion({ + this.sourceId = const Value.absent(), + this.playlistId = const Value.absent(), + this.songId = const Value.absent(), + this.position = const Value.absent(), + this.rowid = const Value.absent(), + }); + PlaylistSongsCompanion.insert({ + required int sourceId, + required String playlistId, + required String songId, + required int position, + this.rowid = const Value.absent(), + }) : sourceId = Value(sourceId), + playlistId = Value(playlistId), + songId = Value(songId), + position = Value(position); + static Insertable custom({ + Expression? sourceId, + Expression? playlistId, + Expression? songId, + Expression? position, + Expression? rowid, + }) { + return RawValuesInsertable({ + if (sourceId != null) 'source_id': sourceId, + if (playlistId != null) 'playlist_id': playlistId, + if (songId != null) 'song_id': songId, + if (position != null) 'position': position, + if (rowid != null) 'rowid': rowid, + }); + } + + PlaylistSongsCompanion copyWith({ + Value? sourceId, + Value? playlistId, + Value? songId, + Value? position, + Value? rowid, + }) { + return PlaylistSongsCompanion( + sourceId: sourceId ?? this.sourceId, + playlistId: playlistId ?? this.playlistId, + songId: songId ?? this.songId, + position: position ?? this.position, + rowid: rowid ?? this.rowid, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (sourceId.present) { + map['source_id'] = Variable(sourceId.value); + } + if (playlistId.present) { + map['playlist_id'] = Variable(playlistId.value); + } + if (songId.present) { + map['song_id'] = Variable(songId.value); + } + if (position.present) { + map['position'] = Variable(position.value); + } + if (rowid.present) { + map['rowid'] = Variable(rowid.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('PlaylistSongsCompanion(') + ..write('sourceId: $sourceId, ') + ..write('playlistId: $playlistId, ') + ..write('songId: $songId, ') + ..write('position: $position, ') + ..write('rowid: $rowid') + ..write(')')) + .toString(); + } +} + +class Songs extends Table with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + Songs(this.attachedDatabase, [this._alias]); + static const VerificationMeta _sourceIdMeta = const VerificationMeta( + 'sourceId', + ); + late final GeneratedColumn sourceId = GeneratedColumn( + 'source_id', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + $customConstraints: 'NOT NULL', + ); + static const VerificationMeta _idMeta = const VerificationMeta('id'); + late final GeneratedColumn id = GeneratedColumn( + 'id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + $customConstraints: 'NOT NULL', + ); + static const VerificationMeta _albumIdMeta = const VerificationMeta( + 'albumId', + ); + late final GeneratedColumn albumId = GeneratedColumn( + 'album_id', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + $customConstraints: '', + ); + static const VerificationMeta _artistIdMeta = const VerificationMeta( + 'artistId', + ); + late final GeneratedColumn artistId = GeneratedColumn( + 'artist_id', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + $customConstraints: '', + ); + static const VerificationMeta _titleMeta = const VerificationMeta('title'); + late final GeneratedColumn title = GeneratedColumn( + 'title', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + $customConstraints: 'NOT NULL COLLATE NOCASE', + ); + static const VerificationMeta _albumMeta = const VerificationMeta('album'); + late final GeneratedColumn album = GeneratedColumn( + 'album', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + $customConstraints: 'COLLATE NOCASE', + ); + static const VerificationMeta _artistMeta = const VerificationMeta('artist'); + late final GeneratedColumn artist = GeneratedColumn( + 'artist', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + $customConstraints: 'COLLATE NOCASE', + ); + late final GeneratedColumnWithTypeConverter duration = + GeneratedColumn( + 'duration', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + $customConstraints: '', + ).withConverter(Songs.$converterdurationn); + static const VerificationMeta _trackMeta = const VerificationMeta('track'); + late final GeneratedColumn track = GeneratedColumn( + 'track', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + $customConstraints: '', + ); + static const VerificationMeta _discMeta = const VerificationMeta('disc'); + late final GeneratedColumn disc = GeneratedColumn( + 'disc', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + $customConstraints: '', + ); + static const VerificationMeta _starredMeta = const VerificationMeta( + 'starred', + ); + late final GeneratedColumn starred = GeneratedColumn( + 'starred', + aliasedName, + true, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + $customConstraints: '', + ); + static const VerificationMeta _genreMeta = const VerificationMeta('genre'); + late final GeneratedColumn genre = GeneratedColumn( + 'genre', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + $customConstraints: '', + ); + static const VerificationMeta _updatedMeta = const VerificationMeta( + 'updated', + ); + late final GeneratedColumn updated = GeneratedColumn( + 'updated', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + $customConstraints: + 'NOT NULL DEFAULT (strftime(\'%s\', CURRENT_TIMESTAMP))', + defaultValue: const CustomExpression('strftime(\'%s\', CURRENT_TIMESTAMP)'), + ); + @override + List get $columns => [ + sourceId, + id, + albumId, + artistId, + title, + album, + artist, + duration, + track, + disc, + starred, + genre, + updated, + ]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'songs'; + @override + VerificationContext validateIntegrity( + Insertable instance, { + bool isInserting = false, + }) { + final context = VerificationContext(); + final data = instance.toColumns(true); + if (data.containsKey('source_id')) { + context.handle( + _sourceIdMeta, + sourceId.isAcceptableOrUnknown(data['source_id']!, _sourceIdMeta), + ); + } else if (isInserting) { + context.missing(_sourceIdMeta); + } + if (data.containsKey('id')) { + context.handle(_idMeta, id.isAcceptableOrUnknown(data['id']!, _idMeta)); + } else if (isInserting) { + context.missing(_idMeta); + } + if (data.containsKey('album_id')) { + context.handle( + _albumIdMeta, + albumId.isAcceptableOrUnknown(data['album_id']!, _albumIdMeta), + ); + } + if (data.containsKey('artist_id')) { + context.handle( + _artistIdMeta, + artistId.isAcceptableOrUnknown(data['artist_id']!, _artistIdMeta), + ); + } + if (data.containsKey('title')) { + context.handle( + _titleMeta, + title.isAcceptableOrUnknown(data['title']!, _titleMeta), + ); + } else if (isInserting) { + context.missing(_titleMeta); + } + if (data.containsKey('album')) { + context.handle( + _albumMeta, + album.isAcceptableOrUnknown(data['album']!, _albumMeta), + ); + } + if (data.containsKey('artist')) { + context.handle( + _artistMeta, + artist.isAcceptableOrUnknown(data['artist']!, _artistMeta), + ); + } + if (data.containsKey('track')) { + context.handle( + _trackMeta, + track.isAcceptableOrUnknown(data['track']!, _trackMeta), + ); + } + if (data.containsKey('disc')) { + context.handle( + _discMeta, + disc.isAcceptableOrUnknown(data['disc']!, _discMeta), + ); + } + if (data.containsKey('starred')) { + context.handle( + _starredMeta, + starred.isAcceptableOrUnknown(data['starred']!, _starredMeta), + ); + } + if (data.containsKey('genre')) { + context.handle( + _genreMeta, + genre.isAcceptableOrUnknown(data['genre']!, _genreMeta), + ); + } + if (data.containsKey('updated')) { + context.handle( + _updatedMeta, + updated.isAcceptableOrUnknown(data['updated']!, _updatedMeta), + ); + } + return context; + } + + @override + Set get $primaryKey => {sourceId, id}; + @override + models.Song map(Map data, {String? tablePrefix}) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return models.Song( + id: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}id'], + )!, + albumId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}album_id'], + ), + artistId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}artist_id'], + ), + title: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}title'], + )!, + artist: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}artist'], + ), + album: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}album'], + ), + duration: Songs.$converterdurationn.fromSql( + attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}duration'], + ), + ), + track: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}track'], + ), + disc: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}disc'], + ), + starred: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}starred'], + ), + genre: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}genre'], + ), + ); + } + + @override + Songs createAlias(String alias) { + return Songs(attachedDatabase, alias); + } + + static TypeConverter $converterduration = + const DurationSecondsConverter(); + static TypeConverter $converterdurationn = + NullAwareTypeConverter.wrap($converterduration); + @override + List get customConstraints => const [ + 'PRIMARY KEY(source_id, id)', + 'FOREIGN KEY(source_id)REFERENCES sources(id)ON DELETE CASCADE', + ]; + @override + bool get dontWriteConstraints => true; +} + +class SongsCompanion extends UpdateCompanion { + final Value sourceId; + final Value id; + final Value albumId; + final Value artistId; + final Value title; + final Value album; + final Value artist; + final Value duration; + final Value track; + final Value disc; + final Value starred; + final Value genre; + final Value updated; + final Value rowid; + const SongsCompanion({ + this.sourceId = const Value.absent(), + this.id = const Value.absent(), + this.albumId = const Value.absent(), + this.artistId = const Value.absent(), + this.title = const Value.absent(), + this.album = const Value.absent(), + this.artist = const Value.absent(), + this.duration = const Value.absent(), + this.track = const Value.absent(), + this.disc = const Value.absent(), + this.starred = const Value.absent(), + this.genre = const Value.absent(), + this.updated = const Value.absent(), + this.rowid = const Value.absent(), + }); + SongsCompanion.insert({ + required int sourceId, + required String id, + this.albumId = const Value.absent(), + this.artistId = const Value.absent(), + required String title, + this.album = const Value.absent(), + this.artist = const Value.absent(), + this.duration = const Value.absent(), + this.track = const Value.absent(), + this.disc = const Value.absent(), + this.starred = const Value.absent(), + this.genre = const Value.absent(), + this.updated = const Value.absent(), + this.rowid = const Value.absent(), + }) : sourceId = Value(sourceId), + id = Value(id), + title = Value(title); + static Insertable custom({ + Expression? sourceId, + Expression? id, + Expression? albumId, + Expression? artistId, + Expression? title, + Expression? album, + Expression? artist, + Expression? duration, + Expression? track, + Expression? disc, + Expression? starred, + Expression? genre, + Expression? updated, + Expression? rowid, + }) { + return RawValuesInsertable({ + if (sourceId != null) 'source_id': sourceId, + if (id != null) 'id': id, + if (albumId != null) 'album_id': albumId, + if (artistId != null) 'artist_id': artistId, + if (title != null) 'title': title, + if (album != null) 'album': album, + if (artist != null) 'artist': artist, + if (duration != null) 'duration': duration, + if (track != null) 'track': track, + if (disc != null) 'disc': disc, + if (starred != null) 'starred': starred, + if (genre != null) 'genre': genre, + if (updated != null) 'updated': updated, + if (rowid != null) 'rowid': rowid, + }); + } + + SongsCompanion copyWith({ + Value? sourceId, + Value? id, + Value? albumId, + Value? artistId, + Value? title, + Value? album, + Value? artist, + Value? duration, + Value? track, + Value? disc, + Value? starred, + Value? genre, + Value? updated, + Value? rowid, + }) { + return SongsCompanion( + sourceId: sourceId ?? this.sourceId, + id: id ?? this.id, + albumId: albumId ?? this.albumId, + artistId: artistId ?? this.artistId, + title: title ?? this.title, + album: album ?? this.album, + artist: artist ?? this.artist, + duration: duration ?? this.duration, + track: track ?? this.track, + disc: disc ?? this.disc, + starred: starred ?? this.starred, + genre: genre ?? this.genre, + updated: updated ?? this.updated, + rowid: rowid ?? this.rowid, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (sourceId.present) { + map['source_id'] = Variable(sourceId.value); + } + if (id.present) { + map['id'] = Variable(id.value); + } + if (albumId.present) { + map['album_id'] = Variable(albumId.value); + } + if (artistId.present) { + map['artist_id'] = Variable(artistId.value); + } + if (title.present) { + map['title'] = Variable(title.value); + } + if (album.present) { + map['album'] = Variable(album.value); + } + if (artist.present) { + map['artist'] = Variable(artist.value); + } + if (duration.present) { + map['duration'] = Variable( + Songs.$converterdurationn.toSql(duration.value), + ); + } + if (track.present) { + map['track'] = Variable(track.value); + } + if (disc.present) { + map['disc'] = Variable(disc.value); + } + if (starred.present) { + map['starred'] = Variable(starred.value); + } + if (genre.present) { + map['genre'] = Variable(genre.value); + } + if (updated.present) { + map['updated'] = Variable(updated.value); + } + if (rowid.present) { + map['rowid'] = Variable(rowid.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('SongsCompanion(') + ..write('sourceId: $sourceId, ') + ..write('id: $id, ') + ..write('albumId: $albumId, ') + ..write('artistId: $artistId, ') + ..write('title: $title, ') + ..write('album: $album, ') + ..write('artist: $artist, ') + ..write('duration: $duration, ') + ..write('track: $track, ') + ..write('disc: $disc, ') + ..write('starred: $starred, ') + ..write('genre: $genre, ') + ..write('updated: $updated, ') + ..write('rowid: $rowid') + ..write(')')) + .toString(); + } +} + +abstract class _$SubtracksDatabase extends GeneratedDatabase { + _$SubtracksDatabase(QueryExecutor e) : super(e); + $SubtracksDatabaseManager get managers => $SubtracksDatabaseManager(this); + late final Sources sources = Sources(this); + late final SubsonicSourceOptions subsonicSourceOptions = + SubsonicSourceOptions(this); + late final Artists artists = Artists(this); + late final Index artistsSourceId = Index( + 'artists_source_id', + 'CREATE INDEX artists_source_id ON artists (source_id)', + ); + late final Albums albums = Albums(this); + late final Index albumsSourceId = Index( + 'albums_source_id', + 'CREATE INDEX albums_source_id ON albums (source_id)', + ); + late final Index albumsSourceIdArtistIdIdx = Index( + 'albums_source_id_artist_id_idx', + 'CREATE INDEX albums_source_id_artist_id_idx ON albums (source_id, artist_id)', + ); + late final Playlists playlists = Playlists(this); + late final Index playlistsSourceId = Index( + 'playlists_source_id', + 'CREATE INDEX playlists_source_id ON playlists (source_id)', + ); + late final PlaylistSongs playlistSongs = PlaylistSongs(this); + late final Index playlistSongsSourceIdPlaylistIdIdx = Index( + 'playlist_songs_source_id_playlist_id_idx', + 'CREATE INDEX playlist_songs_source_id_playlist_id_idx ON playlist_songs (source_id, playlist_id)', + ); + late final Index playlistSongsSourceIdSongIdIdx = Index( + 'playlist_songs_source_id_song_id_idx', + 'CREATE INDEX playlist_songs_source_id_song_id_idx ON playlist_songs (source_id, song_id)', + ); + late final Songs songs = Songs(this); + late final Index songsSourceIdAlbumIdIdx = Index( + 'songs_source_id_album_id_idx', + 'CREATE INDEX songs_source_id_album_id_idx ON songs (source_id, album_id)', + ); + late final Index songsSourceIdArtistIdIdx = Index( + 'songs_source_id_artist_id_idx', + 'CREATE INDEX songs_source_id_artist_id_idx ON songs (source_id, artist_id)', + ); + @override + Iterable> get allTables => + allSchemaEntities.whereType>(); + @override + List get allSchemaEntities => [ + sources, + subsonicSourceOptions, + artists, + artistsSourceId, + albums, + albumsSourceId, + albumsSourceIdArtistIdIdx, + playlists, + playlistsSourceId, + playlistSongs, + playlistSongsSourceIdPlaylistIdIdx, + playlistSongsSourceIdSongIdIdx, + songs, + songsSourceIdAlbumIdIdx, + songsSourceIdArtistIdIdx, + ]; + @override + StreamQueryUpdateRules get streamUpdateRules => const StreamQueryUpdateRules([ + WritePropagation( + on: TableUpdateQuery.onTableName( + 'sources', + limitUpdateKind: UpdateKind.delete, + ), + result: [TableUpdate('subsonic_source_options', kind: UpdateKind.delete)], + ), + WritePropagation( + on: TableUpdateQuery.onTableName( + 'sources', + limitUpdateKind: UpdateKind.delete, + ), + result: [TableUpdate('artists', kind: UpdateKind.delete)], + ), + WritePropagation( + on: TableUpdateQuery.onTableName( + 'sources', + limitUpdateKind: UpdateKind.delete, + ), + result: [TableUpdate('albums', kind: UpdateKind.delete)], + ), + WritePropagation( + on: TableUpdateQuery.onTableName( + 'sources', + limitUpdateKind: UpdateKind.delete, + ), + result: [TableUpdate('playlists', kind: UpdateKind.delete)], + ), + WritePropagation( + on: TableUpdateQuery.onTableName( + 'sources', + limitUpdateKind: UpdateKind.delete, + ), + result: [TableUpdate('playlist_songs', kind: UpdateKind.delete)], + ), + WritePropagation( + on: TableUpdateQuery.onTableName( + 'sources', + limitUpdateKind: UpdateKind.delete, + ), + result: [TableUpdate('songs', kind: UpdateKind.delete)], + ), + ]); +} + +typedef $SourcesCreateCompanionBuilder = + SourcesCompanion Function({ + Value id, + required String name, + Value isActive, + Value createdAt, + }); +typedef $SourcesUpdateCompanionBuilder = + SourcesCompanion Function({ + Value id, + Value name, + Value isActive, + Value createdAt, + }); + +class $SourcesFilterComposer extends Composer<_$SubtracksDatabase, Sources> { + $SourcesFilterComposer({ + required super.$db, + required super.$table, + super.joinBuilder, + super.$addJoinBuilderToRootComposer, + super.$removeJoinBuilderFromRootComposer, + }); + ColumnFilters get id => $composableBuilder( + column: $table.id, + builder: (column) => ColumnFilters(column), + ); + + ColumnFilters get name => $composableBuilder( + column: $table.name, + builder: (column) => ColumnFilters(column), + ); + + ColumnFilters get isActive => $composableBuilder( + column: $table.isActive, + builder: (column) => ColumnFilters(column), + ); + + ColumnFilters get createdAt => $composableBuilder( + column: $table.createdAt, + builder: (column) => ColumnFilters(column), + ); +} + +class $SourcesOrderingComposer extends Composer<_$SubtracksDatabase, Sources> { + $SourcesOrderingComposer({ + required super.$db, + required super.$table, + super.joinBuilder, + super.$addJoinBuilderToRootComposer, + super.$removeJoinBuilderFromRootComposer, + }); + ColumnOrderings get id => $composableBuilder( + column: $table.id, + builder: (column) => ColumnOrderings(column), + ); + + ColumnOrderings get name => $composableBuilder( + column: $table.name, + builder: (column) => ColumnOrderings(column), + ); + + ColumnOrderings get isActive => $composableBuilder( + column: $table.isActive, + builder: (column) => ColumnOrderings(column), + ); + + ColumnOrderings get createdAt => $composableBuilder( + column: $table.createdAt, + builder: (column) => ColumnOrderings(column), + ); +} + +class $SourcesAnnotationComposer + extends Composer<_$SubtracksDatabase, Sources> { + $SourcesAnnotationComposer({ + required super.$db, + required super.$table, + super.joinBuilder, + super.$addJoinBuilderToRootComposer, + super.$removeJoinBuilderFromRootComposer, + }); + GeneratedColumn get id => + $composableBuilder(column: $table.id, builder: (column) => column); + + GeneratedColumn get name => + $composableBuilder(column: $table.name, builder: (column) => column); + + GeneratedColumn get isActive => + $composableBuilder(column: $table.isActive, builder: (column) => column); + + GeneratedColumn get createdAt => + $composableBuilder(column: $table.createdAt, builder: (column) => column); +} + +class $SourcesTableManager + extends + RootTableManager< + _$SubtracksDatabase, + Sources, + Source, + $SourcesFilterComposer, + $SourcesOrderingComposer, + $SourcesAnnotationComposer, + $SourcesCreateCompanionBuilder, + $SourcesUpdateCompanionBuilder, + (Source, BaseReferences<_$SubtracksDatabase, Sources, Source>), + Source, + PrefetchHooks Function() + > { + $SourcesTableManager(_$SubtracksDatabase db, Sources table) + : super( + TableManagerState( + db: db, + table: table, + createFilteringComposer: () => + $SourcesFilterComposer($db: db, $table: table), + createOrderingComposer: () => + $SourcesOrderingComposer($db: db, $table: table), + createComputedFieldComposer: () => + $SourcesAnnotationComposer($db: db, $table: table), + updateCompanionCallback: + ({ + Value id = const Value.absent(), + Value name = const Value.absent(), + Value isActive = const Value.absent(), + Value createdAt = const Value.absent(), + }) => SourcesCompanion( + id: id, + name: name, + isActive: isActive, + createdAt: createdAt, + ), + createCompanionCallback: + ({ + Value id = const Value.absent(), + required String name, + Value isActive = const Value.absent(), + Value createdAt = const Value.absent(), + }) => SourcesCompanion.insert( + id: id, + name: name, + isActive: isActive, + createdAt: createdAt, + ), + withReferenceMapper: (p0) => p0 + .map((e) => (e.readTable(table), BaseReferences(db, table, e))) + .toList(), + prefetchHooksCallback: null, + ), + ); +} + +typedef $SourcesProcessedTableManager = + ProcessedTableManager< + _$SubtracksDatabase, + Sources, + Source, + $SourcesFilterComposer, + $SourcesOrderingComposer, + $SourcesAnnotationComposer, + $SourcesCreateCompanionBuilder, + $SourcesUpdateCompanionBuilder, + (Source, BaseReferences<_$SubtracksDatabase, Sources, Source>), + Source, + PrefetchHooks Function() + >; +typedef $SubsonicSourceOptionsCreateCompanionBuilder = + SubsonicSourceOptionsCompanion Function({ + Value sourceId, + required Uri address, + required String username, + required String password, + Value useTokenAuth, + }); +typedef $SubsonicSourceOptionsUpdateCompanionBuilder = + SubsonicSourceOptionsCompanion Function({ + Value sourceId, + Value address, + Value username, + Value password, + Value useTokenAuth, + }); + +class $SubsonicSourceOptionsFilterComposer + extends Composer<_$SubtracksDatabase, SubsonicSourceOptions> { + $SubsonicSourceOptionsFilterComposer({ + required super.$db, + required super.$table, + super.joinBuilder, + super.$addJoinBuilderToRootComposer, + super.$removeJoinBuilderFromRootComposer, + }); + ColumnFilters get sourceId => $composableBuilder( + column: $table.sourceId, + builder: (column) => ColumnFilters(column), + ); + + ColumnWithTypeConverterFilters get address => + $composableBuilder( + column: $table.address, + builder: (column) => ColumnWithTypeConverterFilters(column), + ); + + ColumnFilters get username => $composableBuilder( + column: $table.username, + builder: (column) => ColumnFilters(column), + ); + + ColumnFilters get password => $composableBuilder( + column: $table.password, + builder: (column) => ColumnFilters(column), + ); + + ColumnFilters get useTokenAuth => $composableBuilder( + column: $table.useTokenAuth, + builder: (column) => ColumnFilters(column), + ); +} + +class $SubsonicSourceOptionsOrderingComposer + extends Composer<_$SubtracksDatabase, SubsonicSourceOptions> { + $SubsonicSourceOptionsOrderingComposer({ + required super.$db, + required super.$table, + super.joinBuilder, + super.$addJoinBuilderToRootComposer, + super.$removeJoinBuilderFromRootComposer, + }); + ColumnOrderings get sourceId => $composableBuilder( + column: $table.sourceId, + builder: (column) => ColumnOrderings(column), + ); + + ColumnOrderings get address => $composableBuilder( + column: $table.address, + builder: (column) => ColumnOrderings(column), + ); + + ColumnOrderings get username => $composableBuilder( + column: $table.username, + builder: (column) => ColumnOrderings(column), + ); + + ColumnOrderings get password => $composableBuilder( + column: $table.password, + builder: (column) => ColumnOrderings(column), + ); + + ColumnOrderings get useTokenAuth => $composableBuilder( + column: $table.useTokenAuth, + builder: (column) => ColumnOrderings(column), + ); +} + +class $SubsonicSourceOptionsAnnotationComposer + extends Composer<_$SubtracksDatabase, SubsonicSourceOptions> { + $SubsonicSourceOptionsAnnotationComposer({ + required super.$db, + required super.$table, + super.joinBuilder, + super.$addJoinBuilderToRootComposer, + super.$removeJoinBuilderFromRootComposer, + }); + GeneratedColumn get sourceId => + $composableBuilder(column: $table.sourceId, builder: (column) => column); + + GeneratedColumnWithTypeConverter get address => + $composableBuilder(column: $table.address, builder: (column) => column); + + GeneratedColumn get username => + $composableBuilder(column: $table.username, builder: (column) => column); + + GeneratedColumn get password => + $composableBuilder(column: $table.password, builder: (column) => column); + + GeneratedColumn get useTokenAuth => $composableBuilder( + column: $table.useTokenAuth, + builder: (column) => column, + ); +} + +class $SubsonicSourceOptionsTableManager + extends + RootTableManager< + _$SubtracksDatabase, + SubsonicSourceOptions, + SubsonicSourceOption, + $SubsonicSourceOptionsFilterComposer, + $SubsonicSourceOptionsOrderingComposer, + $SubsonicSourceOptionsAnnotationComposer, + $SubsonicSourceOptionsCreateCompanionBuilder, + $SubsonicSourceOptionsUpdateCompanionBuilder, + ( + SubsonicSourceOption, + BaseReferences< + _$SubtracksDatabase, + SubsonicSourceOptions, + SubsonicSourceOption + >, + ), + SubsonicSourceOption, + PrefetchHooks Function() + > { + $SubsonicSourceOptionsTableManager( + _$SubtracksDatabase db, + SubsonicSourceOptions table, + ) : super( + TableManagerState( + db: db, + table: table, + createFilteringComposer: () => + $SubsonicSourceOptionsFilterComposer($db: db, $table: table), + createOrderingComposer: () => + $SubsonicSourceOptionsOrderingComposer($db: db, $table: table), + createComputedFieldComposer: () => + $SubsonicSourceOptionsAnnotationComposer($db: db, $table: table), + updateCompanionCallback: + ({ + Value sourceId = const Value.absent(), + Value address = const Value.absent(), + Value username = const Value.absent(), + Value password = const Value.absent(), + Value useTokenAuth = const Value.absent(), + }) => SubsonicSourceOptionsCompanion( + sourceId: sourceId, + address: address, + username: username, + password: password, + useTokenAuth: useTokenAuth, + ), + createCompanionCallback: + ({ + Value sourceId = const Value.absent(), + required Uri address, + required String username, + required String password, + Value useTokenAuth = const Value.absent(), + }) => SubsonicSourceOptionsCompanion.insert( + sourceId: sourceId, + address: address, + username: username, + password: password, + useTokenAuth: useTokenAuth, + ), + withReferenceMapper: (p0) => p0 + .map((e) => (e.readTable(table), BaseReferences(db, table, e))) + .toList(), + prefetchHooksCallback: null, + ), + ); +} + +typedef $SubsonicSourceOptionsProcessedTableManager = + ProcessedTableManager< + _$SubtracksDatabase, + SubsonicSourceOptions, + SubsonicSourceOption, + $SubsonicSourceOptionsFilterComposer, + $SubsonicSourceOptionsOrderingComposer, + $SubsonicSourceOptionsAnnotationComposer, + $SubsonicSourceOptionsCreateCompanionBuilder, + $SubsonicSourceOptionsUpdateCompanionBuilder, + ( + SubsonicSourceOption, + BaseReferences< + _$SubtracksDatabase, + SubsonicSourceOptions, + SubsonicSourceOption + >, + ), + SubsonicSourceOption, + PrefetchHooks Function() + >; +typedef $ArtistsCreateCompanionBuilder = + ArtistsCompanion Function({ + required int sourceId, + required String id, + required String name, + Value starred, + Value rowid, + }); +typedef $ArtistsUpdateCompanionBuilder = + ArtistsCompanion Function({ + Value sourceId, + Value id, + Value name, + Value starred, + Value rowid, + }); + +class $ArtistsFilterComposer extends Composer<_$SubtracksDatabase, Artists> { + $ArtistsFilterComposer({ + required super.$db, + required super.$table, + super.joinBuilder, + super.$addJoinBuilderToRootComposer, + super.$removeJoinBuilderFromRootComposer, + }); + ColumnFilters get sourceId => $composableBuilder( + column: $table.sourceId, + builder: (column) => ColumnFilters(column), + ); + + ColumnFilters get id => $composableBuilder( + column: $table.id, + builder: (column) => ColumnFilters(column), + ); + + ColumnFilters get name => $composableBuilder( + column: $table.name, + builder: (column) => ColumnFilters(column), + ); + + ColumnFilters get starred => $composableBuilder( + column: $table.starred, + builder: (column) => ColumnFilters(column), + ); +} + +class $ArtistsOrderingComposer extends Composer<_$SubtracksDatabase, Artists> { + $ArtistsOrderingComposer({ + required super.$db, + required super.$table, + super.joinBuilder, + super.$addJoinBuilderToRootComposer, + super.$removeJoinBuilderFromRootComposer, + }); + ColumnOrderings get sourceId => $composableBuilder( + column: $table.sourceId, + builder: (column) => ColumnOrderings(column), + ); + + ColumnOrderings get id => $composableBuilder( + column: $table.id, + builder: (column) => ColumnOrderings(column), + ); + + ColumnOrderings get name => $composableBuilder( + column: $table.name, + builder: (column) => ColumnOrderings(column), + ); + + ColumnOrderings get starred => $composableBuilder( + column: $table.starred, + builder: (column) => ColumnOrderings(column), + ); +} + +class $ArtistsAnnotationComposer + extends Composer<_$SubtracksDatabase, Artists> { + $ArtistsAnnotationComposer({ + required super.$db, + required super.$table, + super.joinBuilder, + super.$addJoinBuilderToRootComposer, + super.$removeJoinBuilderFromRootComposer, + }); + GeneratedColumn get sourceId => + $composableBuilder(column: $table.sourceId, builder: (column) => column); + + GeneratedColumn get id => + $composableBuilder(column: $table.id, builder: (column) => column); + + GeneratedColumn get name => + $composableBuilder(column: $table.name, builder: (column) => column); + + GeneratedColumn get starred => + $composableBuilder(column: $table.starred, builder: (column) => column); +} + +class $ArtistsTableManager + extends + RootTableManager< + _$SubtracksDatabase, + Artists, + models.Artist, + $ArtistsFilterComposer, + $ArtistsOrderingComposer, + $ArtistsAnnotationComposer, + $ArtistsCreateCompanionBuilder, + $ArtistsUpdateCompanionBuilder, + ( + models.Artist, + BaseReferences<_$SubtracksDatabase, Artists, models.Artist>, + ), + models.Artist, + PrefetchHooks Function() + > { + $ArtistsTableManager(_$SubtracksDatabase db, Artists table) + : super( + TableManagerState( + db: db, + table: table, + createFilteringComposer: () => + $ArtistsFilterComposer($db: db, $table: table), + createOrderingComposer: () => + $ArtistsOrderingComposer($db: db, $table: table), + createComputedFieldComposer: () => + $ArtistsAnnotationComposer($db: db, $table: table), + updateCompanionCallback: + ({ + Value sourceId = const Value.absent(), + Value id = const Value.absent(), + Value name = const Value.absent(), + Value starred = const Value.absent(), + Value rowid = const Value.absent(), + }) => ArtistsCompanion( + sourceId: sourceId, + id: id, + name: name, + starred: starred, + rowid: rowid, + ), + createCompanionCallback: + ({ + required int sourceId, + required String id, + required String name, + Value starred = const Value.absent(), + Value rowid = const Value.absent(), + }) => ArtistsCompanion.insert( + sourceId: sourceId, + id: id, + name: name, + starred: starred, + rowid: rowid, + ), + withReferenceMapper: (p0) => p0 + .map((e) => (e.readTable(table), BaseReferences(db, table, e))) + .toList(), + prefetchHooksCallback: null, + ), + ); +} + +typedef $ArtistsProcessedTableManager = + ProcessedTableManager< + _$SubtracksDatabase, + Artists, + models.Artist, + $ArtistsFilterComposer, + $ArtistsOrderingComposer, + $ArtistsAnnotationComposer, + $ArtistsCreateCompanionBuilder, + $ArtistsUpdateCompanionBuilder, + ( + models.Artist, + BaseReferences<_$SubtracksDatabase, Artists, models.Artist>, + ), + models.Artist, + PrefetchHooks Function() + >; +typedef $AlbumsCreateCompanionBuilder = + AlbumsCompanion Function({ + required int sourceId, + required String id, + Value artistId, + required String name, + Value albumArtist, + required DateTime created, + Value coverArt, + Value genre, + Value year, + Value starred, + Value frequentRank, + Value recentRank, + Value rowid, + }); +typedef $AlbumsUpdateCompanionBuilder = + AlbumsCompanion Function({ + Value sourceId, + Value id, + Value artistId, + Value name, + Value albumArtist, + Value created, + Value coverArt, + Value genre, + Value year, + Value starred, + Value frequentRank, + Value recentRank, + Value rowid, + }); + +class $AlbumsFilterComposer extends Composer<_$SubtracksDatabase, Albums> { + $AlbumsFilterComposer({ + required super.$db, + required super.$table, + super.joinBuilder, + super.$addJoinBuilderToRootComposer, + super.$removeJoinBuilderFromRootComposer, + }); + ColumnFilters get sourceId => $composableBuilder( + column: $table.sourceId, + builder: (column) => ColumnFilters(column), + ); + + ColumnFilters get id => $composableBuilder( + column: $table.id, + builder: (column) => ColumnFilters(column), + ); + + ColumnFilters get artistId => $composableBuilder( + column: $table.artistId, + builder: (column) => ColumnFilters(column), + ); + + ColumnFilters get name => $composableBuilder( + column: $table.name, + builder: (column) => ColumnFilters(column), + ); + + ColumnFilters get albumArtist => $composableBuilder( + column: $table.albumArtist, + builder: (column) => ColumnFilters(column), + ); + + ColumnFilters get created => $composableBuilder( + column: $table.created, + builder: (column) => ColumnFilters(column), + ); + + ColumnFilters get coverArt => $composableBuilder( + column: $table.coverArt, + builder: (column) => ColumnFilters(column), + ); + + ColumnFilters get genre => $composableBuilder( + column: $table.genre, + builder: (column) => ColumnFilters(column), + ); + + ColumnFilters get year => $composableBuilder( + column: $table.year, + builder: (column) => ColumnFilters(column), + ); + + ColumnFilters get starred => $composableBuilder( + column: $table.starred, + builder: (column) => ColumnFilters(column), + ); + + ColumnFilters get frequentRank => $composableBuilder( + column: $table.frequentRank, + builder: (column) => ColumnFilters(column), + ); + + ColumnFilters get recentRank => $composableBuilder( + column: $table.recentRank, + builder: (column) => ColumnFilters(column), + ); +} + +class $AlbumsOrderingComposer extends Composer<_$SubtracksDatabase, Albums> { + $AlbumsOrderingComposer({ + required super.$db, + required super.$table, + super.joinBuilder, + super.$addJoinBuilderToRootComposer, + super.$removeJoinBuilderFromRootComposer, + }); + ColumnOrderings get sourceId => $composableBuilder( + column: $table.sourceId, + builder: (column) => ColumnOrderings(column), + ); + + ColumnOrderings get id => $composableBuilder( + column: $table.id, + builder: (column) => ColumnOrderings(column), + ); + + ColumnOrderings get artistId => $composableBuilder( + column: $table.artistId, + builder: (column) => ColumnOrderings(column), + ); + + ColumnOrderings get name => $composableBuilder( + column: $table.name, + builder: (column) => ColumnOrderings(column), + ); + + ColumnOrderings get albumArtist => $composableBuilder( + column: $table.albumArtist, + builder: (column) => ColumnOrderings(column), + ); + + ColumnOrderings get created => $composableBuilder( + column: $table.created, + builder: (column) => ColumnOrderings(column), + ); + + ColumnOrderings get coverArt => $composableBuilder( + column: $table.coverArt, + builder: (column) => ColumnOrderings(column), + ); + + ColumnOrderings get genre => $composableBuilder( + column: $table.genre, + builder: (column) => ColumnOrderings(column), + ); + + ColumnOrderings get year => $composableBuilder( + column: $table.year, + builder: (column) => ColumnOrderings(column), + ); + + ColumnOrderings get starred => $composableBuilder( + column: $table.starred, + builder: (column) => ColumnOrderings(column), + ); + + ColumnOrderings get frequentRank => $composableBuilder( + column: $table.frequentRank, + builder: (column) => ColumnOrderings(column), + ); + + ColumnOrderings get recentRank => $composableBuilder( + column: $table.recentRank, + builder: (column) => ColumnOrderings(column), + ); +} + +class $AlbumsAnnotationComposer extends Composer<_$SubtracksDatabase, Albums> { + $AlbumsAnnotationComposer({ + required super.$db, + required super.$table, + super.joinBuilder, + super.$addJoinBuilderToRootComposer, + super.$removeJoinBuilderFromRootComposer, + }); + GeneratedColumn get sourceId => + $composableBuilder(column: $table.sourceId, builder: (column) => column); + + GeneratedColumn get id => + $composableBuilder(column: $table.id, builder: (column) => column); + + GeneratedColumn get artistId => + $composableBuilder(column: $table.artistId, builder: (column) => column); + + GeneratedColumn get name => + $composableBuilder(column: $table.name, builder: (column) => column); + + GeneratedColumn get albumArtist => $composableBuilder( + column: $table.albumArtist, + builder: (column) => column, + ); + + GeneratedColumn get created => + $composableBuilder(column: $table.created, builder: (column) => column); + + GeneratedColumn get coverArt => + $composableBuilder(column: $table.coverArt, builder: (column) => column); + + GeneratedColumn get genre => + $composableBuilder(column: $table.genre, builder: (column) => column); + + GeneratedColumn get year => + $composableBuilder(column: $table.year, builder: (column) => column); + + GeneratedColumn get starred => + $composableBuilder(column: $table.starred, builder: (column) => column); + + GeneratedColumn get frequentRank => $composableBuilder( + column: $table.frequentRank, + builder: (column) => column, + ); + + GeneratedColumn get recentRank => $composableBuilder( + column: $table.recentRank, + builder: (column) => column, + ); +} + +class $AlbumsTableManager + extends + RootTableManager< + _$SubtracksDatabase, + Albums, + models.Album, + $AlbumsFilterComposer, + $AlbumsOrderingComposer, + $AlbumsAnnotationComposer, + $AlbumsCreateCompanionBuilder, + $AlbumsUpdateCompanionBuilder, + ( + models.Album, + BaseReferences<_$SubtracksDatabase, Albums, models.Album>, + ), + models.Album, + PrefetchHooks Function() + > { + $AlbumsTableManager(_$SubtracksDatabase db, Albums table) + : super( + TableManagerState( + db: db, + table: table, + createFilteringComposer: () => + $AlbumsFilterComposer($db: db, $table: table), + createOrderingComposer: () => + $AlbumsOrderingComposer($db: db, $table: table), + createComputedFieldComposer: () => + $AlbumsAnnotationComposer($db: db, $table: table), + updateCompanionCallback: + ({ + Value sourceId = const Value.absent(), + Value id = const Value.absent(), + Value artistId = const Value.absent(), + Value name = const Value.absent(), + Value albumArtist = const Value.absent(), + Value created = const Value.absent(), + Value coverArt = const Value.absent(), + Value genre = const Value.absent(), + Value year = const Value.absent(), + Value starred = const Value.absent(), + Value frequentRank = const Value.absent(), + Value recentRank = const Value.absent(), + Value rowid = const Value.absent(), + }) => AlbumsCompanion( + sourceId: sourceId, + id: id, + artistId: artistId, + name: name, + albumArtist: albumArtist, + created: created, + coverArt: coverArt, + genre: genre, + year: year, + starred: starred, + frequentRank: frequentRank, + recentRank: recentRank, + rowid: rowid, + ), + createCompanionCallback: + ({ + required int sourceId, + required String id, + Value artistId = const Value.absent(), + required String name, + Value albumArtist = const Value.absent(), + required DateTime created, + Value coverArt = const Value.absent(), + Value genre = const Value.absent(), + Value year = const Value.absent(), + Value starred = const Value.absent(), + Value frequentRank = const Value.absent(), + Value recentRank = const Value.absent(), + Value rowid = const Value.absent(), + }) => AlbumsCompanion.insert( + sourceId: sourceId, + id: id, + artistId: artistId, + name: name, + albumArtist: albumArtist, + created: created, + coverArt: coverArt, + genre: genre, + year: year, + starred: starred, + frequentRank: frequentRank, + recentRank: recentRank, + rowid: rowid, + ), + withReferenceMapper: (p0) => p0 + .map((e) => (e.readTable(table), BaseReferences(db, table, e))) + .toList(), + prefetchHooksCallback: null, + ), + ); +} + +typedef $AlbumsProcessedTableManager = + ProcessedTableManager< + _$SubtracksDatabase, + Albums, + models.Album, + $AlbumsFilterComposer, + $AlbumsOrderingComposer, + $AlbumsAnnotationComposer, + $AlbumsCreateCompanionBuilder, + $AlbumsUpdateCompanionBuilder, + (models.Album, BaseReferences<_$SubtracksDatabase, Albums, models.Album>), + models.Album, + PrefetchHooks Function() + >; +typedef $PlaylistsCreateCompanionBuilder = + PlaylistsCompanion Function({ + required int sourceId, + required String id, + required String name, + Value comment, + Value coverArt, + required int songCount, + required DateTime created, + Value changed, + Value rowid, + }); +typedef $PlaylistsUpdateCompanionBuilder = + PlaylistsCompanion Function({ + Value sourceId, + Value id, + Value name, + Value comment, + Value coverArt, + Value songCount, + Value created, + Value changed, + Value rowid, + }); + +class $PlaylistsFilterComposer + extends Composer<_$SubtracksDatabase, Playlists> { + $PlaylistsFilterComposer({ + required super.$db, + required super.$table, + super.joinBuilder, + super.$addJoinBuilderToRootComposer, + super.$removeJoinBuilderFromRootComposer, + }); + ColumnFilters get sourceId => $composableBuilder( + column: $table.sourceId, + builder: (column) => ColumnFilters(column), + ); + + ColumnFilters get id => $composableBuilder( + column: $table.id, + builder: (column) => ColumnFilters(column), + ); + + ColumnFilters get name => $composableBuilder( + column: $table.name, + builder: (column) => ColumnFilters(column), + ); + + ColumnFilters get comment => $composableBuilder( + column: $table.comment, + builder: (column) => ColumnFilters(column), + ); + + ColumnFilters get coverArt => $composableBuilder( + column: $table.coverArt, + builder: (column) => ColumnFilters(column), + ); + + ColumnFilters get songCount => $composableBuilder( + column: $table.songCount, + builder: (column) => ColumnFilters(column), + ); + + ColumnFilters get created => $composableBuilder( + column: $table.created, + builder: (column) => ColumnFilters(column), + ); + + ColumnFilters get changed => $composableBuilder( + column: $table.changed, + builder: (column) => ColumnFilters(column), + ); +} + +class $PlaylistsOrderingComposer + extends Composer<_$SubtracksDatabase, Playlists> { + $PlaylistsOrderingComposer({ + required super.$db, + required super.$table, + super.joinBuilder, + super.$addJoinBuilderToRootComposer, + super.$removeJoinBuilderFromRootComposer, + }); + ColumnOrderings get sourceId => $composableBuilder( + column: $table.sourceId, + builder: (column) => ColumnOrderings(column), + ); + + ColumnOrderings get id => $composableBuilder( + column: $table.id, + builder: (column) => ColumnOrderings(column), + ); + + ColumnOrderings get name => $composableBuilder( + column: $table.name, + builder: (column) => ColumnOrderings(column), + ); + + ColumnOrderings get comment => $composableBuilder( + column: $table.comment, + builder: (column) => ColumnOrderings(column), + ); + + ColumnOrderings get coverArt => $composableBuilder( + column: $table.coverArt, + builder: (column) => ColumnOrderings(column), + ); + + ColumnOrderings get songCount => $composableBuilder( + column: $table.songCount, + builder: (column) => ColumnOrderings(column), + ); + + ColumnOrderings get created => $composableBuilder( + column: $table.created, + builder: (column) => ColumnOrderings(column), + ); + + ColumnOrderings get changed => $composableBuilder( + column: $table.changed, + builder: (column) => ColumnOrderings(column), + ); +} + +class $PlaylistsAnnotationComposer + extends Composer<_$SubtracksDatabase, Playlists> { + $PlaylistsAnnotationComposer({ + required super.$db, + required super.$table, + super.joinBuilder, + super.$addJoinBuilderToRootComposer, + super.$removeJoinBuilderFromRootComposer, + }); + GeneratedColumn get sourceId => + $composableBuilder(column: $table.sourceId, builder: (column) => column); + + GeneratedColumn get id => + $composableBuilder(column: $table.id, builder: (column) => column); + + GeneratedColumn get name => + $composableBuilder(column: $table.name, builder: (column) => column); + + GeneratedColumn get comment => + $composableBuilder(column: $table.comment, builder: (column) => column); + + GeneratedColumn get coverArt => + $composableBuilder(column: $table.coverArt, builder: (column) => column); + + GeneratedColumn get songCount => + $composableBuilder(column: $table.songCount, builder: (column) => column); + + GeneratedColumn get created => + $composableBuilder(column: $table.created, builder: (column) => column); + + GeneratedColumn get changed => + $composableBuilder(column: $table.changed, builder: (column) => column); +} + +class $PlaylistsTableManager + extends + RootTableManager< + _$SubtracksDatabase, + Playlists, + models.Playlist, + $PlaylistsFilterComposer, + $PlaylistsOrderingComposer, + $PlaylistsAnnotationComposer, + $PlaylistsCreateCompanionBuilder, + $PlaylistsUpdateCompanionBuilder, + ( + models.Playlist, + BaseReferences<_$SubtracksDatabase, Playlists, models.Playlist>, + ), + models.Playlist, + PrefetchHooks Function() + > { + $PlaylistsTableManager(_$SubtracksDatabase db, Playlists table) + : super( + TableManagerState( + db: db, + table: table, + createFilteringComposer: () => + $PlaylistsFilterComposer($db: db, $table: table), + createOrderingComposer: () => + $PlaylistsOrderingComposer($db: db, $table: table), + createComputedFieldComposer: () => + $PlaylistsAnnotationComposer($db: db, $table: table), + updateCompanionCallback: + ({ + Value sourceId = const Value.absent(), + Value id = const Value.absent(), + Value name = const Value.absent(), + Value comment = const Value.absent(), + Value coverArt = const Value.absent(), + Value songCount = const Value.absent(), + Value created = const Value.absent(), + Value changed = const Value.absent(), + Value rowid = const Value.absent(), + }) => PlaylistsCompanion( + sourceId: sourceId, + id: id, + name: name, + comment: comment, + coverArt: coverArt, + songCount: songCount, + created: created, + changed: changed, + rowid: rowid, + ), + createCompanionCallback: + ({ + required int sourceId, + required String id, + required String name, + Value comment = const Value.absent(), + Value coverArt = const Value.absent(), + required int songCount, + required DateTime created, + Value changed = const Value.absent(), + Value rowid = const Value.absent(), + }) => PlaylistsCompanion.insert( + sourceId: sourceId, + id: id, + name: name, + comment: comment, + coverArt: coverArt, + songCount: songCount, + created: created, + changed: changed, + rowid: rowid, + ), + withReferenceMapper: (p0) => p0 + .map((e) => (e.readTable(table), BaseReferences(db, table, e))) + .toList(), + prefetchHooksCallback: null, + ), + ); +} + +typedef $PlaylistsProcessedTableManager = + ProcessedTableManager< + _$SubtracksDatabase, + Playlists, + models.Playlist, + $PlaylistsFilterComposer, + $PlaylistsOrderingComposer, + $PlaylistsAnnotationComposer, + $PlaylistsCreateCompanionBuilder, + $PlaylistsUpdateCompanionBuilder, + ( + models.Playlist, + BaseReferences<_$SubtracksDatabase, Playlists, models.Playlist>, + ), + models.Playlist, + PrefetchHooks Function() + >; +typedef $PlaylistSongsCreateCompanionBuilder = + PlaylistSongsCompanion Function({ + required int sourceId, + required String playlistId, + required String songId, + required int position, + Value rowid, + }); +typedef $PlaylistSongsUpdateCompanionBuilder = + PlaylistSongsCompanion Function({ + Value sourceId, + Value playlistId, + Value songId, + Value position, + Value rowid, + }); + +class $PlaylistSongsFilterComposer + extends Composer<_$SubtracksDatabase, PlaylistSongs> { + $PlaylistSongsFilterComposer({ + required super.$db, + required super.$table, + super.joinBuilder, + super.$addJoinBuilderToRootComposer, + super.$removeJoinBuilderFromRootComposer, + }); + ColumnFilters get sourceId => $composableBuilder( + column: $table.sourceId, + builder: (column) => ColumnFilters(column), + ); + + ColumnFilters get playlistId => $composableBuilder( + column: $table.playlistId, + builder: (column) => ColumnFilters(column), + ); + + ColumnFilters get songId => $composableBuilder( + column: $table.songId, + builder: (column) => ColumnFilters(column), + ); + + ColumnFilters get position => $composableBuilder( + column: $table.position, + builder: (column) => ColumnFilters(column), + ); +} + +class $PlaylistSongsOrderingComposer + extends Composer<_$SubtracksDatabase, PlaylistSongs> { + $PlaylistSongsOrderingComposer({ + required super.$db, + required super.$table, + super.joinBuilder, + super.$addJoinBuilderToRootComposer, + super.$removeJoinBuilderFromRootComposer, + }); + ColumnOrderings get sourceId => $composableBuilder( + column: $table.sourceId, + builder: (column) => ColumnOrderings(column), + ); + + ColumnOrderings get playlistId => $composableBuilder( + column: $table.playlistId, + builder: (column) => ColumnOrderings(column), + ); + + ColumnOrderings get songId => $composableBuilder( + column: $table.songId, + builder: (column) => ColumnOrderings(column), + ); + + ColumnOrderings get position => $composableBuilder( + column: $table.position, + builder: (column) => ColumnOrderings(column), + ); +} + +class $PlaylistSongsAnnotationComposer + extends Composer<_$SubtracksDatabase, PlaylistSongs> { + $PlaylistSongsAnnotationComposer({ + required super.$db, + required super.$table, + super.joinBuilder, + super.$addJoinBuilderToRootComposer, + super.$removeJoinBuilderFromRootComposer, + }); + GeneratedColumn get sourceId => + $composableBuilder(column: $table.sourceId, builder: (column) => column); + + GeneratedColumn get playlistId => $composableBuilder( + column: $table.playlistId, + builder: (column) => column, + ); + + GeneratedColumn get songId => + $composableBuilder(column: $table.songId, builder: (column) => column); + + GeneratedColumn get position => + $composableBuilder(column: $table.position, builder: (column) => column); +} + +class $PlaylistSongsTableManager + extends + RootTableManager< + _$SubtracksDatabase, + PlaylistSongs, + models.PlaylistSong, + $PlaylistSongsFilterComposer, + $PlaylistSongsOrderingComposer, + $PlaylistSongsAnnotationComposer, + $PlaylistSongsCreateCompanionBuilder, + $PlaylistSongsUpdateCompanionBuilder, + ( + models.PlaylistSong, + BaseReferences< + _$SubtracksDatabase, + PlaylistSongs, + models.PlaylistSong + >, + ), + models.PlaylistSong, + PrefetchHooks Function() + > { + $PlaylistSongsTableManager(_$SubtracksDatabase db, PlaylistSongs table) + : super( + TableManagerState( + db: db, + table: table, + createFilteringComposer: () => + $PlaylistSongsFilterComposer($db: db, $table: table), + createOrderingComposer: () => + $PlaylistSongsOrderingComposer($db: db, $table: table), + createComputedFieldComposer: () => + $PlaylistSongsAnnotationComposer($db: db, $table: table), + updateCompanionCallback: + ({ + Value sourceId = const Value.absent(), + Value playlistId = const Value.absent(), + Value songId = const Value.absent(), + Value position = const Value.absent(), + Value rowid = const Value.absent(), + }) => PlaylistSongsCompanion( + sourceId: sourceId, + playlistId: playlistId, + songId: songId, + position: position, + rowid: rowid, + ), + createCompanionCallback: + ({ + required int sourceId, + required String playlistId, + required String songId, + required int position, + Value rowid = const Value.absent(), + }) => PlaylistSongsCompanion.insert( + sourceId: sourceId, + playlistId: playlistId, + songId: songId, + position: position, + rowid: rowid, + ), + withReferenceMapper: (p0) => p0 + .map((e) => (e.readTable(table), BaseReferences(db, table, e))) + .toList(), + prefetchHooksCallback: null, + ), + ); +} + +typedef $PlaylistSongsProcessedTableManager = + ProcessedTableManager< + _$SubtracksDatabase, + PlaylistSongs, + models.PlaylistSong, + $PlaylistSongsFilterComposer, + $PlaylistSongsOrderingComposer, + $PlaylistSongsAnnotationComposer, + $PlaylistSongsCreateCompanionBuilder, + $PlaylistSongsUpdateCompanionBuilder, + ( + models.PlaylistSong, + BaseReferences<_$SubtracksDatabase, PlaylistSongs, models.PlaylistSong>, + ), + models.PlaylistSong, + PrefetchHooks Function() + >; +typedef $SongsCreateCompanionBuilder = + SongsCompanion Function({ + required int sourceId, + required String id, + Value albumId, + Value artistId, + required String title, + Value album, + Value artist, + Value duration, + Value track, + Value disc, + Value starred, + Value genre, + Value updated, + Value rowid, + }); +typedef $SongsUpdateCompanionBuilder = + SongsCompanion Function({ + Value sourceId, + Value id, + Value albumId, + Value artistId, + Value title, + Value album, + Value artist, + Value duration, + Value track, + Value disc, + Value starred, + Value genre, + Value updated, + Value rowid, + }); + +class $SongsFilterComposer extends Composer<_$SubtracksDatabase, Songs> { + $SongsFilterComposer({ + required super.$db, + required super.$table, + super.joinBuilder, + super.$addJoinBuilderToRootComposer, + super.$removeJoinBuilderFromRootComposer, + }); + ColumnFilters get sourceId => $composableBuilder( + column: $table.sourceId, + builder: (column) => ColumnFilters(column), + ); + + ColumnFilters get id => $composableBuilder( + column: $table.id, + builder: (column) => ColumnFilters(column), + ); + + ColumnFilters get albumId => $composableBuilder( + column: $table.albumId, + builder: (column) => ColumnFilters(column), + ); + + ColumnFilters get artistId => $composableBuilder( + column: $table.artistId, + builder: (column) => ColumnFilters(column), + ); + + ColumnFilters get title => $composableBuilder( + column: $table.title, + builder: (column) => ColumnFilters(column), + ); + + ColumnFilters get album => $composableBuilder( + column: $table.album, + builder: (column) => ColumnFilters(column), + ); + + ColumnFilters get artist => $composableBuilder( + column: $table.artist, + builder: (column) => ColumnFilters(column), + ); + + ColumnWithTypeConverterFilters get duration => + $composableBuilder( + column: $table.duration, + builder: (column) => ColumnWithTypeConverterFilters(column), + ); + + ColumnFilters get track => $composableBuilder( + column: $table.track, + builder: (column) => ColumnFilters(column), + ); + + ColumnFilters get disc => $composableBuilder( + column: $table.disc, + builder: (column) => ColumnFilters(column), + ); + + ColumnFilters get starred => $composableBuilder( + column: $table.starred, + builder: (column) => ColumnFilters(column), + ); + + ColumnFilters get genre => $composableBuilder( + column: $table.genre, + builder: (column) => ColumnFilters(column), + ); + + ColumnFilters get updated => $composableBuilder( + column: $table.updated, + builder: (column) => ColumnFilters(column), + ); +} + +class $SongsOrderingComposer extends Composer<_$SubtracksDatabase, Songs> { + $SongsOrderingComposer({ + required super.$db, + required super.$table, + super.joinBuilder, + super.$addJoinBuilderToRootComposer, + super.$removeJoinBuilderFromRootComposer, + }); + ColumnOrderings get sourceId => $composableBuilder( + column: $table.sourceId, + builder: (column) => ColumnOrderings(column), + ); + + ColumnOrderings get id => $composableBuilder( + column: $table.id, + builder: (column) => ColumnOrderings(column), + ); + + ColumnOrderings get albumId => $composableBuilder( + column: $table.albumId, + builder: (column) => ColumnOrderings(column), + ); + + ColumnOrderings get artistId => $composableBuilder( + column: $table.artistId, + builder: (column) => ColumnOrderings(column), + ); + + ColumnOrderings get title => $composableBuilder( + column: $table.title, + builder: (column) => ColumnOrderings(column), + ); + + ColumnOrderings get album => $composableBuilder( + column: $table.album, + builder: (column) => ColumnOrderings(column), + ); + + ColumnOrderings get artist => $composableBuilder( + column: $table.artist, + builder: (column) => ColumnOrderings(column), + ); + + ColumnOrderings get duration => $composableBuilder( + column: $table.duration, + builder: (column) => ColumnOrderings(column), + ); + + ColumnOrderings get track => $composableBuilder( + column: $table.track, + builder: (column) => ColumnOrderings(column), + ); + + ColumnOrderings get disc => $composableBuilder( + column: $table.disc, + builder: (column) => ColumnOrderings(column), + ); + + ColumnOrderings get starred => $composableBuilder( + column: $table.starred, + builder: (column) => ColumnOrderings(column), + ); + + ColumnOrderings get genre => $composableBuilder( + column: $table.genre, + builder: (column) => ColumnOrderings(column), + ); + + ColumnOrderings get updated => $composableBuilder( + column: $table.updated, + builder: (column) => ColumnOrderings(column), + ); +} + +class $SongsAnnotationComposer extends Composer<_$SubtracksDatabase, Songs> { + $SongsAnnotationComposer({ + required super.$db, + required super.$table, + super.joinBuilder, + super.$addJoinBuilderToRootComposer, + super.$removeJoinBuilderFromRootComposer, + }); + GeneratedColumn get sourceId => + $composableBuilder(column: $table.sourceId, builder: (column) => column); + + GeneratedColumn get id => + $composableBuilder(column: $table.id, builder: (column) => column); + + GeneratedColumn get albumId => + $composableBuilder(column: $table.albumId, builder: (column) => column); + + GeneratedColumn get artistId => + $composableBuilder(column: $table.artistId, builder: (column) => column); + + GeneratedColumn get title => + $composableBuilder(column: $table.title, builder: (column) => column); + + GeneratedColumn get album => + $composableBuilder(column: $table.album, builder: (column) => column); + + GeneratedColumn get artist => + $composableBuilder(column: $table.artist, builder: (column) => column); + + GeneratedColumnWithTypeConverter get duration => + $composableBuilder(column: $table.duration, builder: (column) => column); + + GeneratedColumn get track => + $composableBuilder(column: $table.track, builder: (column) => column); + + GeneratedColumn get disc => + $composableBuilder(column: $table.disc, builder: (column) => column); + + GeneratedColumn get starred => + $composableBuilder(column: $table.starred, builder: (column) => column); + + GeneratedColumn get genre => + $composableBuilder(column: $table.genre, builder: (column) => column); + + GeneratedColumn get updated => + $composableBuilder(column: $table.updated, builder: (column) => column); +} + +class $SongsTableManager + extends + RootTableManager< + _$SubtracksDatabase, + Songs, + models.Song, + $SongsFilterComposer, + $SongsOrderingComposer, + $SongsAnnotationComposer, + $SongsCreateCompanionBuilder, + $SongsUpdateCompanionBuilder, + ( + models.Song, + BaseReferences<_$SubtracksDatabase, Songs, models.Song>, + ), + models.Song, + PrefetchHooks Function() + > { + $SongsTableManager(_$SubtracksDatabase db, Songs table) + : super( + TableManagerState( + db: db, + table: table, + createFilteringComposer: () => + $SongsFilterComposer($db: db, $table: table), + createOrderingComposer: () => + $SongsOrderingComposer($db: db, $table: table), + createComputedFieldComposer: () => + $SongsAnnotationComposer($db: db, $table: table), + updateCompanionCallback: + ({ + Value sourceId = const Value.absent(), + Value id = const Value.absent(), + Value albumId = const Value.absent(), + Value artistId = const Value.absent(), + Value title = const Value.absent(), + Value album = const Value.absent(), + Value artist = const Value.absent(), + Value duration = const Value.absent(), + Value track = const Value.absent(), + Value disc = const Value.absent(), + Value starred = const Value.absent(), + Value genre = const Value.absent(), + Value updated = const Value.absent(), + Value rowid = const Value.absent(), + }) => SongsCompanion( + sourceId: sourceId, + id: id, + albumId: albumId, + artistId: artistId, + title: title, + album: album, + artist: artist, + duration: duration, + track: track, + disc: disc, + starred: starred, + genre: genre, + updated: updated, + rowid: rowid, + ), + createCompanionCallback: + ({ + required int sourceId, + required String id, + Value albumId = const Value.absent(), + Value artistId = const Value.absent(), + required String title, + Value album = const Value.absent(), + Value artist = const Value.absent(), + Value duration = const Value.absent(), + Value track = const Value.absent(), + Value disc = const Value.absent(), + Value starred = const Value.absent(), + Value genre = const Value.absent(), + Value updated = const Value.absent(), + Value rowid = const Value.absent(), + }) => SongsCompanion.insert( + sourceId: sourceId, + id: id, + albumId: albumId, + artistId: artistId, + title: title, + album: album, + artist: artist, + duration: duration, + track: track, + disc: disc, + starred: starred, + genre: genre, + updated: updated, + rowid: rowid, + ), + withReferenceMapper: (p0) => p0 + .map((e) => (e.readTable(table), BaseReferences(db, table, e))) + .toList(), + prefetchHooksCallback: null, + ), + ); +} + +typedef $SongsProcessedTableManager = + ProcessedTableManager< + _$SubtracksDatabase, + Songs, + models.Song, + $SongsFilterComposer, + $SongsOrderingComposer, + $SongsAnnotationComposer, + $SongsCreateCompanionBuilder, + $SongsUpdateCompanionBuilder, + (models.Song, BaseReferences<_$SubtracksDatabase, Songs, models.Song>), + models.Song, + PrefetchHooks Function() + >; + +class $SubtracksDatabaseManager { + final _$SubtracksDatabase _db; + $SubtracksDatabaseManager(this._db); + $SourcesTableManager get sources => $SourcesTableManager(_db, _db.sources); + $SubsonicSourceOptionsTableManager get subsonicSourceOptions => + $SubsonicSourceOptionsTableManager(_db, _db.subsonicSourceOptions); + $ArtistsTableManager get artists => $ArtistsTableManager(_db, _db.artists); + $AlbumsTableManager get albums => $AlbumsTableManager(_db, _db.albums); + $PlaylistsTableManager get playlists => + $PlaylistsTableManager(_db, _db.playlists); + $PlaylistSongsTableManager get playlistSongs => + $PlaylistSongsTableManager(_db, _db.playlistSongs); + $SongsTableManager get songs => $SongsTableManager(_db, _db.songs); +} diff --git a/lib/database/tables.drift b/lib/database/tables.drift new file mode 100644 index 0000000..3616db8 --- /dev/null +++ b/lib/database/tables.drift @@ -0,0 +1,555 @@ +import 'converters.dart'; +import '../sources/models.dart'; + +-- +-- SCHEMA +-- + +-- CREATE TABLE queue( +-- "index" INT NOT NULL PRIMARY KEY UNIQUE, +-- source_id INT NOT NULL, +-- id TEXT NOT NULL, +-- context ENUM(QueueContextType) NOT NULL, +-- context_id TEXT, +-- current_track BOOLEAN UNIQUE +-- ); +-- CREATE INDEX queue_index ON queue ("index"); +-- CREATE INDEX queue_current_track ON queue ("current_track"); + +-- CREATE TABLE last_audio_state( +-- id INT NOT NULL PRIMARY KEY, +-- queue_mode ENUM(QueueMode) NOT NULL, +-- shuffle_indicies TEXT MAPPED BY `const IListIntConverter()`, +-- repeat ENUM(RepeatMode) NOT NULL +-- ); + +-- CREATE TABLE last_bottom_nav_state( +-- id INT NOT NULL PRIMARY KEY, +-- tab TEXT NOT NULL +-- ); + +-- CREATE TABLE last_library_state( +-- id INT NOT NULL PRIMARY KEY, +-- tab TEXT NOT NULL, +-- albums_list TEXT NOT NULL MAPPED BY `const ListQueryConverter()`, +-- artists_list TEXT NOT NULL MAPPED BY `const ListQueryConverter()`, +-- playlists_list TEXT NOT NULL MAPPED BY `const ListQueryConverter()`, +-- songs_list TEXT NOT NULL MAPPED BY `const ListQueryConverter()` +-- ); + +-- CREATE TABLE app_settings( +-- id INT NOT NULL PRIMARY KEY, +-- max_bitrate_wifi INT NOT NULL, +-- max_bitrate_mobile INT NOT NULL, +-- stream_format TEXT +-- ) WITH AppSettings; + +CREATE TABLE sources( + id INT NOT NULL PRIMARY KEY AUTOINCREMENT, + name TEXT NOT NULL COLLATE NOCASE, + is_active BOOLEAN UNIQUE, + created_at DATETIME NOT NULL DEFAULT (strftime('%s', CURRENT_TIMESTAMP)) +); + +CREATE TABLE subsonic_source_options( + source_id INT NOT NULL PRIMARY KEY, + address TEXT NOT NULL MAPPED BY `const UriConverter()`, + username TEXT NOT NULL, + password TEXT NOT NULL, + use_token_auth BOOLEAN NOT NULL DEFAULT 1, + FOREIGN KEY (source_id) REFERENCES sources (id) ON DELETE CASCADE +); + +CREATE TABLE artists( + source_id INT NOT NULL, + id TEXT NOT NULL, + name TEXT NOT NULL COLLATE NOCASE, + starred DATETIME, + PRIMARY KEY (source_id, id), + FOREIGN KEY (source_id) REFERENCES sources (id) ON DELETE CASCADE +) WITH Artist; +CREATE INDEX artists_source_id ON artists (source_id); + +-- CREATE VIRTUAL TABLE artists_fts USING fts5(source_id, name, content=artists, content_rowid=rowid); + +-- CREATE TRIGGER artists_ai AFTER INSERT ON artists BEGIN +-- INSERT INTO artists_fts(rowid, source_id, name) +-- VALUES (new.rowid, new.source_id, new.name); +-- END; + +-- CREATE TRIGGER artists_ad AFTER DELETE ON artists BEGIN +-- INSERT INTO artists_fts(artists_fts, rowid, source_id, name) +-- VALUES('delete', old.rowid, old.source_id, old.name); +-- END; + +-- CREATE TRIGGER artists_au AFTER UPDATE ON artists BEGIN +-- INSERT INTO artists_fts(artists_fts, rowid, source_id, name) +-- VALUES('delete', old.rowid, old.source_id, old.name); +-- INSERT INTO artists_fts(rowid, source_id, name) +-- VALUES (new.rowid, new.source_id, new.name); +-- END; + +CREATE TABLE albums( + source_id INT NOT NULL, + id TEXT NOT NULL, + artist_id TEXT, + name TEXT NOT NULL COLLATE NOCASE, + album_artist TEXT COLLATE NOCASE, + created DATETIME NOT NULL, + cover_art TEXT, + genre TEXT, + year INT, + starred DATETIME, + frequent_rank INT, + recent_rank INT, + PRIMARY KEY (source_id, id), + FOREIGN KEY (source_id) REFERENCES sources (id) ON DELETE CASCADE +) WITH Album; +CREATE INDEX albums_source_id ON albums (source_id); +CREATE INDEX albums_source_id_artist_id_idx ON albums (source_id, artist_id); + +-- CREATE VIRTUAL TABLE albums_fts USING fts5(source_id, name, content=albums, content_rowid=rowid); + +-- CREATE TRIGGER albums_ai AFTER INSERT ON albums BEGIN +-- INSERT INTO albums_fts(rowid, source_id, name) +-- VALUES (new.rowid, new.source_id, new.name); +-- END; + +-- CREATE TRIGGER albums_ad AFTER DELETE ON albums BEGIN +-- INSERT INTO albums_fts(albums_fts, rowid, source_id, name) +-- VALUES('delete', old.rowid, old.source_id, old.name); +-- END; + +-- CREATE TRIGGER albums_au AFTER UPDATE ON albums BEGIN +-- INSERT INTO albums_fts(albums_fts, rowid, source_id, name) +-- VALUES('delete', old.rowid, old.source_id, old.name); +-- INSERT INTO albums_fts(rowid, source_id, name) +-- VALUES (new.rowid, new.source_id, new.name); +-- END; + +CREATE TABLE playlists( + source_id INT NOT NULL, + id TEXT NOT NULL, + name TEXT NOT NULL COLLATE NOCASE, + comment TEXT COLLATE NOCASE, + cover_art TEXT, + song_count INT NOT NULL, + created DATETIME NOT NULL, + changed DATETIME NOT NULL DEFAULT (strftime('%s', CURRENT_TIMESTAMP)), + PRIMARY KEY (source_id, id), + FOREIGN KEY (source_id) REFERENCES sources (id) ON DELETE CASCADE +) WITH Playlist; +CREATE INDEX playlists_source_id ON playlists (source_id); + +CREATE TABLE playlist_songs( + source_id INT NOT NULL, + playlist_id TEXT NOT NULL, + song_id TEXT NOT NULL, + position INT NOT NULL, + PRIMARY KEY (source_id, playlist_id, position), + FOREIGN KEY (source_id) REFERENCES sources (id) ON DELETE CASCADE +) WITH PlaylistSong; +CREATE INDEX playlist_songs_source_id_playlist_id_idx ON playlist_songs (source_id, playlist_id); +CREATE INDEX playlist_songs_source_id_song_id_idx ON playlist_songs (source_id, song_id); + +-- CREATE VIRTUAL TABLE playlists_fts USING fts5(source_id, name, content=playlists, content_rowid=rowid); + +-- CREATE TRIGGER playlists_ai AFTER INSERT ON playlists BEGIN +-- INSERT INTO playlists_fts(rowid, source_id, name) +-- VALUES (new.rowid, new.source_id, new.name); +-- END; + +-- CREATE TRIGGER playlists_ad AFTER DELETE ON playlists BEGIN +-- INSERT INTO playlists_fts(playlists_fts, rowid, source_id, name) +-- VALUES('delete', old.rowid, old.source_id, old.name); +-- END; + +-- CREATE TRIGGER playlists_au AFTER UPDATE ON playlists BEGIN +-- INSERT INTO playlists_fts(playlists_fts, rowid, source_id, name) +-- VALUES('delete', old.rowid, old.source_id, old.name); +-- INSERT INTO playlists_fts(rowid, source_id, name) +-- VALUES (new.rowid, new.source_id, new.name); +-- END; + +CREATE TABLE songs( + source_id INT NOT NULL, + id TEXT NOT NULL, + album_id TEXT, + artist_id TEXT, + title TEXT NOT NULL COLLATE NOCASE, + album TEXT COLLATE NOCASE, + artist TEXT COLLATE NOCASE, + duration INT MAPPED BY `const DurationSecondsConverter()`, + track INT, + disc INT, + starred DATETIME, + genre TEXT, + updated DATETIME NOT NULL DEFAULT (strftime('%s', CURRENT_TIMESTAMP)), + PRIMARY KEY (source_id, id), + FOREIGN KEY (source_id) REFERENCES sources (id) ON DELETE CASCADE +) WITH Song; +CREATE INDEX songs_source_id_album_id_idx ON songs (source_id, album_id); +CREATE INDEX songs_source_id_artist_id_idx ON songs (source_id, artist_id); + +-- CREATE VIRTUAL TABLE songs_fts USING fts5(source_id, title, content=songs, content_rowid=rowid); + +-- CREATE TRIGGER songs_ai AFTER INSERT ON songs BEGIN +-- INSERT INTO songs_fts(rowid, source_id, title) +-- VALUES (new.rowid, new.source_id, new.title); +-- END; + +-- CREATE TRIGGER songs_ad AFTER DELETE ON songs BEGIN +-- INSERT INTO songs_fts(songs_fts, rowid, source_id, title) +-- VALUES('delete', old.rowid, old.source_id, old.title); +-- END; + +-- CREATE TRIGGER songs_au AFTER UPDATE ON songs BEGIN +-- INSERT INTO songs_fts(songs_fts, rowid, source_id, title) +-- VALUES('delete', old.rowid, old.source_id, old.title); +-- INSERT INTO songs_fts(rowid, source_id, title) +-- VALUES (new.rowid, new.source_id, new.title); +-- END; + +-- +-- QUERIES +-- + +-- sourcesCount: +-- SELECT COUNT(*) +-- FROM sources; + +-- allSubsonicSources WITH SubsonicSettings: +-- SELECT +-- sources.id, +-- sources.name, +-- sources.address, +-- sources.is_active, +-- sources.created_at, +-- subsonic_sources.features, +-- subsonic_sources.username, +-- subsonic_sources.password, +-- subsonic_sources.use_token_auth +-- FROM sources +-- JOIN subsonic_sources ON subsonic_sources.source_id = sources.id; + +-- albumIdsWithDownloadStatus: +-- SELECT albums.id +-- FROM albums +-- JOIN songs on songs.source_id = albums.source_id AND songs.album_id = albums.id +-- WHERE +-- albums.source_id = :source_id +-- AND (songs.download_file_path IS NOT NULL OR songs.download_task_id IS NOT NULL) +-- GROUP BY albums.id; + +-- artistIdsWithDownloadStatus: +-- SELECT artists.id +-- FROM artists +-- LEFT JOIN albums ON artists.source_id = albums.source_id AND artists.id = albums.artist_id +-- LEFT JOIN songs ON albums.source_id = songs.source_id AND albums.id = songs.album_id +-- WHERE +-- artists.source_id = :source_id +-- AND (songs.download_file_path IS NOT NULL OR songs.download_task_id IS NOT NULL) +-- GROUP BY artists.id; + +-- playlistIdsWithDownloadStatus: +-- SELECT playlists.id +-- FROM playlists +-- LEFT JOIN playlist_songs ON playlist_songs.source_id = playlists.source_id AND playlist_songs.playlist_id = playlists.id +-- LEFT JOIN songs ON playlist_songs.source_id = songs.source_id AND playlist_songs.song_id = songs.id +-- WHERE +-- playlists.source_id = :source_id +-- AND (songs.download_file_path IS NOT NULL OR songs.download_task_id IS NOT NULL) +-- GROUP BY playlists.id; + +-- searchArtists: +-- SELECT rowid +-- FROM artists_fts +-- WHERE artists_fts MATCH :query +-- ORDER BY rank +-- LIMIT :limit OFFSET :offset; + +-- searchAlbums: +-- SELECT rowid +-- FROM albums_fts +-- WHERE albums_fts MATCH :query +-- ORDER BY rank +-- LIMIT :limit OFFSET :offset; + +-- searchPlaylists: +-- SELECT rowid +-- FROM playlists_fts +-- WHERE playlists_fts MATCH :query +-- ORDER BY rank +-- LIMIT :limit OFFSET :offset; + +-- searchSongs: +-- SELECT rowid +-- FROM songs_fts +-- WHERE songs_fts MATCH :query +-- ORDER BY rank +-- LIMIT :limit OFFSET :offset; + +-- artistById: +-- SELECT * FROM artists +-- WHERE source_id = :source_id AND id = :id; + +-- albumById: +-- SELECT * FROM albums +-- WHERE source_id = :source_id AND id = :id; + +-- albumsByArtistId: +-- SELECT * FROM albums +-- WHERE source_id = :source_id AND artist_id = :artist_id; + +-- albumsInIds: +-- SELECT * FROM albums +-- WHERE source_id = :source_id AND id IN :ids; + +-- playlistById: +-- SELECT * FROM playlists +-- WHERE source_id = :source_id AND id = :id; + +-- songById: +-- SELECT * FROM songs +-- WHERE source_id = :source_id AND id = :id; + +-- albumGenres: +-- SELECT +-- genre +-- FROM albums +-- WHERE genre IS NOT NULL AND source_id = :source_id +-- GROUP BY genre +-- ORDER BY COUNT(genre) DESC +-- LIMIT :limit OFFSET :offset; + +-- albumsByGenre: +-- SELECT +-- albums.* +-- FROM albums +-- JOIN songs ON albums.source_id = songs.source_id AND albums.id = songs.album_id +-- WHERE songs.source_id = :source_id AND songs.genre = :genre +-- GROUP BY albums.id +-- ORDER BY albums.created DESC, albums.name +-- LIMIT :limit OFFSET :offset; + +-- filterSongsByGenre: +-- SELECT +-- songs.* +-- FROM songs +-- JOIN albums ON albums.source_id = songs.source_id AND albums.id = songs.album_id +-- WHERE $predicate +-- ORDER BY $order +-- LIMIT $limit; + +-- songsByGenreCount: +-- SELECT +-- COUNT(*) +-- FROM songs +-- WHERE songs.source_id = :source_id AND songs.genre = :genre; + +-- songsWithDownloadTasks: +-- SELECT * FROM songs +-- WHERE download_task_id IS NOT NULL; + +-- songByDownloadTask: +-- SELECT * FROM songs +-- WHERE download_task_id = :task_id; + +-- clearSongDownloadTaskBySong: +-- UPDATE songs SET +-- download_task_id = NULL +-- WHERE source_id = :source_id AND id = :id; + +-- completeSongDownload: +-- UPDATE songs SET +-- download_task_id = NULL, +-- download_file_path = :file_path +-- WHERE download_task_id = :task_id; + +-- clearSongDownloadTask: +-- UPDATE songs SET +-- download_task_id = NULL, +-- download_file_path = NULL +-- WHERE download_task_id = :task_id; + +-- updateSongDownloadTask: +-- UPDATE songs SET +-- download_task_id = :task_id +-- WHERE source_id = :source_id AND id = :id; + +-- deleteSongDownloadFile: +-- UPDATE songs SET +-- download_task_id = NULL, +-- download_file_path = NULL +-- WHERE source_id = :source_id AND id = :id; + +-- albumDownloadStatus WITH ListDownloadStatus: +-- SELECT +-- COUNT(*) as total, +-- COUNT(CASE WHEN songs.download_file_path IS NOT NULL THEN songs.id ELSE NULL END) AS downloaded, +-- COUNT(CASE WHEN songs.download_task_id IS NOT NULL THEN songs.id ELSE NULL END) AS downloading +-- FROM albums +-- JOIN songs ON albums.source_id = songs.source_id AND albums.id = songs.album_id +-- WHERE albums.source_id = :source_id AND albums.id = :id; + +-- playlistDownloadStatus WITH ListDownloadStatus: +-- SELECT +-- COUNT(DISTINCT songs.id) as total, +-- COUNT(DISTINCT CASE WHEN songs.download_file_path IS NOT NULL THEN songs.id ELSE NULL END) AS downloaded, +-- COUNT(DISTINCT CASE WHEN songs.download_task_id IS NOT NULL THEN songs.id ELSE NULL END) AS downloading +-- FROM playlists +-- JOIN playlist_songs ON +-- playlist_songs.source_id = playlists.source_id +-- AND playlist_songs.playlist_id = playlists.id +-- JOIN songs ON +-- songs.source_id = playlist_songs.source_id +-- AND songs.id = playlist_songs.song_id +-- WHERE +-- playlists.source_id = :source_id AND playlists.id = :id; + +-- filterAlbums: +-- SELECT +-- albums.* +-- FROM albums +-- WHERE $predicate +-- ORDER BY $order +-- LIMIT $limit; + +-- filterAlbumsDownloaded: +-- SELECT +-- albums.* +-- FROM albums +-- LEFT JOIN songs ON albums.source_id = songs.source_id AND albums.id = songs.album_id +-- WHERE $predicate +-- GROUP BY albums.source_id, albums.id +-- HAVING SUM(CASE WHEN songs.download_file_path IS NOT NULL THEN 1 ELSE 0 END) > 0 +-- ORDER BY $order +-- LIMIT $limit; + +-- filterArtists: +-- SELECT +-- artists.* +-- FROM artists +-- WHERE $predicate +-- ORDER BY $order +-- LIMIT $limit; + +-- filterArtistsDownloaded WITH Artist: +-- SELECT +-- artists.*, +-- COUNT(DISTINCT CASE WHEN songs.download_file_path IS NOT NULL THEN songs.album_id ELSE NULL END) AS album_count +-- FROM artists +-- LEFT JOIN albums ON artists.source_id = albums.source_id AND artists.id = albums.artist_id +-- LEFT JOIN songs ON albums.source_id = songs.source_id AND albums.id = songs.album_id +-- WHERE $predicate +-- GROUP BY artists.source_id, artists.id +-- HAVING SUM(CASE WHEN songs.download_file_path IS NOT NULL THEN 1 ELSE 0 END) > 0 +-- ORDER BY $order +-- LIMIT $limit; + +-- filterPlaylists: +-- SELECT +-- playlists.* +-- FROM playlists +-- WHERE $predicate +-- ORDER BY $order +-- LIMIT $limit; + +-- filterPlaylistsDownloaded WITH Playlist: +-- SELECT +-- playlists.*, +-- COUNT(CASE WHEN songs.download_file_path IS NOT NULL THEN songs.id ELSE NULL END) AS song_count +-- FROM playlists +-- LEFT JOIN playlist_songs ON playlist_songs.source_id = playlists.source_id AND playlist_songs.playlist_id = playlists.id +-- LEFT JOIN songs ON playlist_songs.source_id = songs.source_id AND playlist_songs.song_id = songs.id +-- WHERE $predicate +-- GROUP BY playlists.source_id, playlists.id +-- HAVING SUM(CASE WHEN songs.download_file_path IS NOT NULL THEN 1 ELSE 0 END) > 0 +-- ORDER BY $order +-- LIMIT $limit; + +-- filterSongs: +-- SELECT +-- songs.* +-- FROM songs +-- WHERE $predicate +-- ORDER BY $order +-- LIMIT $limit; + +-- filterSongsDownloaded: +-- SELECT +-- songs.* +-- FROM songs +-- WHERE $predicate AND songs.download_file_path IS NOT NULL +-- ORDER BY $order +-- LIMIT $limit; + +-- playlistIsDownloaded: +-- SELECT +-- COUNT(*) = 0 +-- FROM playlists +-- JOIN playlist_songs ON +-- playlist_songs.source_id = playlists.source_id +-- AND playlist_songs.playlist_id = playlists.id +-- JOIN songs ON +-- songs.source_id = playlist_songs.source_id +-- AND songs.id = playlist_songs.song_id +-- WHERE +-- playlists.source_id = :source_id AND playlists.id = :id +-- AND songs.download_file_path IS NULL; + +-- playlistHasDownloadsInProgress: +-- SELECT +-- COUNT(*) > 0 +-- FROM playlists +-- JOIN playlist_songs ON +-- playlist_songs.source_id = playlists.source_id +-- AND playlist_songs.playlist_id = playlists.id +-- JOIN songs ON +-- songs.source_id = playlist_songs.source_id +-- AND songs.id = playlist_songs.song_id +-- WHERE playlists.source_id = :source_id AND playlists.id = :id +-- AND songs.download_task_id IS NOT NULL; + +-- songsInIds: +-- SELECT * +-- FROM songs +-- WHERE source_id = :source_id AND id IN :ids; + +-- songsInRowIds: +-- SELECT * +-- FROM songs +-- WHERE ROWID IN :row_ids; + +-- albumsInRowIds: +-- SELECT * +-- FROM albums +-- WHERE ROWID IN :row_ids; + +-- artistsInRowIds: +-- SELECT * +-- FROM artists +-- WHERE ROWID IN :row_ids; + +-- playlistsInRowIds: +-- SELECT * +-- FROM playlists +-- WHERE ROWID IN :row_ids; + +-- currentTrackIndex: +-- SELECT +-- queue."index" +-- FROM queue +-- WHERE queue.current_track = 1; + +-- queueLength: +-- SELECT COUNT(*) FROM queue; + +-- queueInIndicies: +-- SELECT * +-- FROM queue +-- WHERE queue."index" IN :indicies; + +-- getAppSettings: +-- SELECT * FROM app_settings +-- WHERE id = 1; diff --git a/lib/services/sync_services.dart b/lib/services/sync_services.dart new file mode 100644 index 0000000..f234826 --- /dev/null +++ b/lib/services/sync_services.dart @@ -0,0 +1,45 @@ +import 'package:async/async.dart'; +import 'package:collection/collection.dart'; +import 'package:drift/drift.dart'; + +import '../database/database.dart'; +import '../sources/music_source.dart'; + +class SyncService { + SyncService({ + required this.source, + required this.db, + required this.sourceId, + }); + + final MusicSource source; + final SubtracksDatabase db; + final int sourceId; + + Future sync() async { + await db.transaction(() async { + await syncArtists(); + }); + } + + Future syncArtists() async { + final sourceArtistIds = {}; + + await for (final artists in source.allArtists().slices(200)) { + sourceArtistIds.addAll(artists.map((e) => e.id)); + + await db.batch((batch) async { + batch.insertAllOnConflictUpdate( + db.artists, + artists.map((artist) => artist.toDb(sourceId)), + ); + }); + } + + for (var slice in sourceArtistIds.slices(kSqliteMaxVariableNumber - 1)) { + await db.artists.deleteWhere( + (tbl) => tbl.sourceId.equals(sourceId) & tbl.id.isNotIn(slice), + ); + } + } +} diff --git a/lib/sources/models.dart b/lib/sources/models.dart index ae68c99..0174b8c 100644 --- a/lib/sources/models.dart +++ b/lib/sources/models.dart @@ -1,32 +1,21 @@ -// ignore_for_file: annotate_overrides - import 'package:freezed_annotation/freezed_annotation.dart'; part 'models.freezed.dart'; -part 'models.g.dart'; - -mixin Starred { - DateTime? get starred; -} - -mixin CoverArt { - String? get coverArt; -} @freezed -abstract class SourceItem with _$SourceItem { - @With() - const factory SourceItem.artist({ +abstract class Artist with _$Artist { + const factory Artist({ required String id, required String name, DateTime? starred, Uri? smallImage, Uri? largeImage, - }) = SourceArtist; + }) = _Artist; +} - @With() - @With() - const factory SourceItem.album({ +@freezed +abstract class Album with _$Album { + const factory Album({ required String id, String? artistId, required String name, @@ -38,10 +27,12 @@ abstract class SourceItem with _$SourceItem { String? genre, int? frequentRank, int? recentRank, - }) = SourceAlbum; + }) = _Album; +} - @With() - const factory SourceItem.playlist({ +@freezed +abstract class Playlist with _$Playlist { + const factory Playlist({ required String id, required String name, String? comment, @@ -50,11 +41,12 @@ abstract class SourceItem with _$SourceItem { String? coverArt, String? owner, bool? public, - }) = SourcePlaylist; + }) = _Playlist; +} - @With() - @With() - const factory SourceItem.song({ +@freezed +abstract class Song with _$Song { + const factory Song({ required String id, String? albumId, String? artistId, @@ -67,20 +59,14 @@ abstract class SourceItem with _$SourceItem { DateTime? starred, String? genre, String? coverArt, - }) = SourceSong; - - factory SourceItem.fromJson(Map json) => - _$SourceItemFromJson(json); + }) = _Song; } @freezed -abstract class SourcePlaylistSong with _$SourcePlaylistSong { - const factory SourcePlaylistSong({ +abstract class PlaylistSong with _$PlaylistSong { + const factory PlaylistSong({ required String playlistId, required String songId, required int position, - }) = _SourcePlaylistSong; - - factory SourcePlaylistSong.fromJson(Map json) => - _$SourcePlaylistSongFromJson(json); + }) = _PlaylistSong; } diff --git a/lib/sources/models.freezed.dart b/lib/sources/models.freezed.dart index 38694f6..875c323 100644 --- a/lib/sources/models.freezed.dart +++ b/lib/sources/models.freezed.dart @@ -11,75 +11,41 @@ part of 'models.dart'; // dart format off T _$identity(T value) => value; -SourceItem _$SourceItemFromJson( - Map json -) { - switch (json['runtimeType']) { - case 'artist': - return SourceArtist.fromJson( - json - ); - case 'album': - return SourceAlbum.fromJson( - json - ); - case 'playlist': - return SourcePlaylist.fromJson( - json - ); - case 'song': - return SourceSong.fromJson( - json - ); - - default: - throw CheckedFromJsonException( - json, - 'runtimeType', - 'SourceItem', - 'Invalid union type "${json['runtimeType']}"!' -); - } - -} - /// @nodoc -mixin _$SourceItem { +mixin _$Artist { - String get id; -/// Create a copy of SourceItem + String get id; String get name; DateTime? get starred; Uri? get smallImage; Uri? get largeImage; +/// Create a copy of Artist /// with the given fields replaced by the non-null parameter values. @JsonKey(includeFromJson: false, includeToJson: false) @pragma('vm:prefer-inline') -$SourceItemCopyWith get copyWith => _$SourceItemCopyWithImpl(this as SourceItem, _$identity); +$ArtistCopyWith get copyWith => _$ArtistCopyWithImpl(this as Artist, _$identity); - /// Serializes this SourceItem to a JSON map. - Map toJson(); @override bool operator ==(Object other) { - return identical(this, other) || (other.runtimeType == runtimeType&&other is SourceItem&&(identical(other.id, id) || other.id == id)); + return identical(this, other) || (other.runtimeType == runtimeType&&other is Artist&&(identical(other.id, id) || other.id == id)&&(identical(other.name, name) || other.name == name)&&(identical(other.starred, starred) || other.starred == starred)&&(identical(other.smallImage, smallImage) || other.smallImage == smallImage)&&(identical(other.largeImage, largeImage) || other.largeImage == largeImage)); } -@JsonKey(includeFromJson: false, includeToJson: false) + @override -int get hashCode => Object.hash(runtimeType,id); +int get hashCode => Object.hash(runtimeType,id,name,starred,smallImage,largeImage); @override String toString() { - return 'SourceItem(id: $id)'; + return 'Artist(id: $id, name: $name, starred: $starred, smallImage: $smallImage, largeImage: $largeImage)'; } } /// @nodoc -abstract mixin class $SourceItemCopyWith<$Res> { - factory $SourceItemCopyWith(SourceItem value, $Res Function(SourceItem) _then) = _$SourceItemCopyWithImpl; +abstract mixin class $ArtistCopyWith<$Res> { + factory $ArtistCopyWith(Artist value, $Res Function(Artist) _then) = _$ArtistCopyWithImpl; @useResult $Res call({ - String id + String id, String name, DateTime? starred, Uri? smallImage, Uri? largeImage }); @@ -87,27 +53,31 @@ $Res call({ } /// @nodoc -class _$SourceItemCopyWithImpl<$Res> - implements $SourceItemCopyWith<$Res> { - _$SourceItemCopyWithImpl(this._self, this._then); +class _$ArtistCopyWithImpl<$Res> + implements $ArtistCopyWith<$Res> { + _$ArtistCopyWithImpl(this._self, this._then); - final SourceItem _self; - final $Res Function(SourceItem) _then; + final Artist _self; + final $Res Function(Artist) _then; -/// Create a copy of SourceItem +/// Create a copy of Artist /// with the given fields replaced by the non-null parameter values. -@pragma('vm:prefer-inline') @override $Res call({Object? id = null,}) { +@pragma('vm:prefer-inline') @override $Res call({Object? id = null,Object? name = null,Object? starred = freezed,Object? smallImage = freezed,Object? largeImage = freezed,}) { return _then(_self.copyWith( id: null == id ? _self.id : id // ignore: cast_nullable_to_non_nullable -as String, +as String,name: null == name ? _self.name : name // ignore: cast_nullable_to_non_nullable +as String,starred: freezed == starred ? _self.starred : starred // ignore: cast_nullable_to_non_nullable +as DateTime?,smallImage: freezed == smallImage ? _self.smallImage : smallImage // ignore: cast_nullable_to_non_nullable +as Uri?,largeImage: freezed == largeImage ? _self.largeImage : largeImage // ignore: cast_nullable_to_non_nullable +as Uri?, )); } } -/// Adds pattern-matching-related methods to [SourceItem]. -extension SourceItemPatterns on SourceItem { +/// Adds pattern-matching-related methods to [Artist]. +extension ArtistPatterns on Artist { /// A variant of `map` that fallback to returning `orElse`. /// /// It is equivalent to doing: @@ -120,14 +90,11 @@ extension SourceItemPatterns on SourceItem { /// } /// ``` -@optionalTypeArgs TResult maybeMap({TResult Function( SourceArtist value)? artist,TResult Function( SourceAlbum value)? album,TResult Function( SourcePlaylist value)? playlist,TResult Function( SourceSong value)? song,required TResult orElse(),}){ +@optionalTypeArgs TResult maybeMap(TResult Function( _Artist value)? $default,{required TResult orElse(),}){ final _that = this; switch (_that) { -case SourceArtist() when artist != null: -return artist(_that);case SourceAlbum() when album != null: -return album(_that);case SourcePlaylist() when playlist != null: -return playlist(_that);case SourceSong() when song != null: -return song(_that);case _: +case _Artist() when $default != null: +return $default(_that);case _: return orElse(); } @@ -145,14 +112,11 @@ return song(_that);case _: /// } /// ``` -@optionalTypeArgs TResult map({required TResult Function( SourceArtist value) artist,required TResult Function( SourceAlbum value) album,required TResult Function( SourcePlaylist value) playlist,required TResult Function( SourceSong value) song,}){ +@optionalTypeArgs TResult map(TResult Function( _Artist value) $default,){ final _that = this; switch (_that) { -case SourceArtist(): -return artist(_that);case SourceAlbum(): -return album(_that);case SourcePlaylist(): -return playlist(_that);case SourceSong(): -return song(_that);case _: +case _Artist(): +return $default(_that);case _: throw StateError('Unexpected subclass'); } @@ -169,14 +133,11 @@ return song(_that);case _: /// } /// ``` -@optionalTypeArgs TResult? mapOrNull({TResult? Function( SourceArtist value)? artist,TResult? Function( SourceAlbum value)? album,TResult? Function( SourcePlaylist value)? playlist,TResult? Function( SourceSong value)? song,}){ +@optionalTypeArgs TResult? mapOrNull(TResult? Function( _Artist value)? $default,){ final _that = this; switch (_that) { -case SourceArtist() when artist != null: -return artist(_that);case SourceAlbum() when album != null: -return album(_that);case SourcePlaylist() when playlist != null: -return playlist(_that);case SourceSong() when song != null: -return song(_that);case _: +case _Artist() when $default != null: +return $default(_that);case _: return null; } @@ -193,13 +154,10 @@ return song(_that);case _: /// } /// ``` -@optionalTypeArgs TResult maybeWhen({TResult Function( String id, String name, DateTime? starred, Uri? smallImage, Uri? largeImage)? artist,TResult Function( String id, String? artistId, String name, String? albumArtist, DateTime created, String? coverArt, int? year, DateTime? starred, String? genre, int? frequentRank, int? recentRank)? album,TResult Function( String id, String name, String? comment, DateTime created, DateTime changed, String? coverArt, String? owner, bool? public)? playlist,TResult Function( String id, String? albumId, String? artistId, String title, String? artist, String? album, Duration? duration, int? track, int? disc, DateTime? starred, String? genre, String? coverArt)? song,required TResult orElse(),}) {final _that = this; +@optionalTypeArgs TResult maybeWhen(TResult Function( String id, String name, DateTime? starred, Uri? smallImage, Uri? largeImage)? $default,{required TResult orElse(),}) {final _that = this; switch (_that) { -case SourceArtist() when artist != null: -return artist(_that.id,_that.name,_that.starred,_that.smallImage,_that.largeImage);case SourceAlbum() when album != null: -return album(_that.id,_that.artistId,_that.name,_that.albumArtist,_that.created,_that.coverArt,_that.year,_that.starred,_that.genre,_that.frequentRank,_that.recentRank);case SourcePlaylist() when playlist != null: -return playlist(_that.id,_that.name,_that.comment,_that.created,_that.changed,_that.coverArt,_that.owner,_that.public);case SourceSong() when song != null: -return song(_that.id,_that.albumId,_that.artistId,_that.title,_that.artist,_that.album,_that.duration,_that.track,_that.disc,_that.starred,_that.genre,_that.coverArt);case _: +case _Artist() when $default != null: +return $default(_that.id,_that.name,_that.starred,_that.smallImage,_that.largeImage);case _: return orElse(); } @@ -217,13 +175,10 @@ return song(_that.id,_that.albumId,_that.artistId,_that.title,_that.artist,_that /// } /// ``` -@optionalTypeArgs TResult when({required TResult Function( String id, String name, DateTime? starred, Uri? smallImage, Uri? largeImage) artist,required TResult Function( String id, String? artistId, String name, String? albumArtist, DateTime created, String? coverArt, int? year, DateTime? starred, String? genre, int? frequentRank, int? recentRank) album,required TResult Function( String id, String name, String? comment, DateTime created, DateTime changed, String? coverArt, String? owner, bool? public) playlist,required TResult Function( String id, String? albumId, String? artistId, String title, String? artist, String? album, Duration? duration, int? track, int? disc, DateTime? starred, String? genre, String? coverArt) song,}) {final _that = this; +@optionalTypeArgs TResult when(TResult Function( String id, String name, DateTime? starred, Uri? smallImage, Uri? largeImage) $default,) {final _that = this; switch (_that) { -case SourceArtist(): -return artist(_that.id,_that.name,_that.starred,_that.smallImage,_that.largeImage);case SourceAlbum(): -return album(_that.id,_that.artistId,_that.name,_that.albumArtist,_that.created,_that.coverArt,_that.year,_that.starred,_that.genre,_that.frequentRank,_that.recentRank);case SourcePlaylist(): -return playlist(_that.id,_that.name,_that.comment,_that.created,_that.changed,_that.coverArt,_that.owner,_that.public);case SourceSong(): -return song(_that.id,_that.albumId,_that.artistId,_that.title,_that.artist,_that.album,_that.duration,_that.track,_that.disc,_that.starred,_that.genre,_that.coverArt);case _: +case _Artist(): +return $default(_that.id,_that.name,_that.starred,_that.smallImage,_that.largeImage);case _: throw StateError('Unexpected subclass'); } @@ -240,13 +195,10 @@ return song(_that.id,_that.albumId,_that.artistId,_that.title,_that.artist,_that /// } /// ``` -@optionalTypeArgs TResult? whenOrNull({TResult? Function( String id, String name, DateTime? starred, Uri? smallImage, Uri? largeImage)? artist,TResult? Function( String id, String? artistId, String name, String? albumArtist, DateTime created, String? coverArt, int? year, DateTime? starred, String? genre, int? frequentRank, int? recentRank)? album,TResult? Function( String id, String name, String? comment, DateTime created, DateTime changed, String? coverArt, String? owner, bool? public)? playlist,TResult? Function( String id, String? albumId, String? artistId, String title, String? artist, String? album, Duration? duration, int? track, int? disc, DateTime? starred, String? genre, String? coverArt)? song,}) {final _that = this; +@optionalTypeArgs TResult? whenOrNull(TResult? Function( String id, String name, DateTime? starred, Uri? smallImage, Uri? largeImage)? $default,) {final _that = this; switch (_that) { -case SourceArtist() when artist != null: -return artist(_that.id,_that.name,_that.starred,_that.smallImage,_that.largeImage);case SourceAlbum() when album != null: -return album(_that.id,_that.artistId,_that.name,_that.albumArtist,_that.created,_that.coverArt,_that.year,_that.starred,_that.genre,_that.frequentRank,_that.recentRank);case SourcePlaylist() when playlist != null: -return playlist(_that.id,_that.name,_that.comment,_that.created,_that.changed,_that.coverArt,_that.owner,_that.public);case SourceSong() when song != null: -return song(_that.id,_that.albumId,_that.artistId,_that.title,_that.artist,_that.album,_that.duration,_that.track,_that.disc,_that.starred,_that.genre,_that.coverArt);case _: +case _Artist() when $default != null: +return $default(_that.id,_that.name,_that.starred,_that.smallImage,_that.largeImage);case _: return null; } @@ -255,53 +207,46 @@ return song(_that.id,_that.albumId,_that.artistId,_that.title,_that.artist,_that } /// @nodoc -@JsonSerializable() -class SourceArtist with Starred implements SourceItem { - const SourceArtist({required this.id, required this.name, this.starred, this.smallImage, this.largeImage, final String? $type}): $type = $type ?? 'artist'; - factory SourceArtist.fromJson(Map json) => _$SourceArtistFromJson(json); + +class _Artist implements Artist { + const _Artist({required this.id, required this.name, this.starred, this.smallImage, this.largeImage}); + @override final String id; - final String name; - final DateTime? starred; - final Uri? smallImage; - final Uri? largeImage; +@override final String name; +@override final DateTime? starred; +@override final Uri? smallImage; +@override final Uri? largeImage; -@JsonKey(name: 'runtimeType') -final String $type; - - -/// Create a copy of SourceItem +/// Create a copy of Artist /// with the given fields replaced by the non-null parameter values. @override @JsonKey(includeFromJson: false, includeToJson: false) @pragma('vm:prefer-inline') -$SourceArtistCopyWith get copyWith => _$SourceArtistCopyWithImpl(this, _$identity); +_$ArtistCopyWith<_Artist> get copyWith => __$ArtistCopyWithImpl<_Artist>(this, _$identity); + -@override -Map toJson() { - return _$SourceArtistToJson(this, ); -} @override bool operator ==(Object other) { - return identical(this, other) || (other.runtimeType == runtimeType&&other is SourceArtist&&(identical(other.id, id) || other.id == id)&&(identical(other.name, name) || other.name == name)&&(identical(other.starred, starred) || other.starred == starred)&&(identical(other.smallImage, smallImage) || other.smallImage == smallImage)&&(identical(other.largeImage, largeImage) || other.largeImage == largeImage)); + return identical(this, other) || (other.runtimeType == runtimeType&&other is _Artist&&(identical(other.id, id) || other.id == id)&&(identical(other.name, name) || other.name == name)&&(identical(other.starred, starred) || other.starred == starred)&&(identical(other.smallImage, smallImage) || other.smallImage == smallImage)&&(identical(other.largeImage, largeImage) || other.largeImage == largeImage)); } -@JsonKey(includeFromJson: false, includeToJson: false) + @override int get hashCode => Object.hash(runtimeType,id,name,starred,smallImage,largeImage); @override String toString() { - return 'SourceItem.artist(id: $id, name: $name, starred: $starred, smallImage: $smallImage, largeImage: $largeImage)'; + return 'Artist(id: $id, name: $name, starred: $starred, smallImage: $smallImage, largeImage: $largeImage)'; } } /// @nodoc -abstract mixin class $SourceArtistCopyWith<$Res> implements $SourceItemCopyWith<$Res> { - factory $SourceArtistCopyWith(SourceArtist value, $Res Function(SourceArtist) _then) = _$SourceArtistCopyWithImpl; +abstract mixin class _$ArtistCopyWith<$Res> implements $ArtistCopyWith<$Res> { + factory _$ArtistCopyWith(_Artist value, $Res Function(_Artist) _then) = __$ArtistCopyWithImpl; @override @useResult $Res call({ String id, String name, DateTime? starred, Uri? smallImage, Uri? largeImage @@ -312,17 +257,17 @@ $Res call({ } /// @nodoc -class _$SourceArtistCopyWithImpl<$Res> - implements $SourceArtistCopyWith<$Res> { - _$SourceArtistCopyWithImpl(this._self, this._then); +class __$ArtistCopyWithImpl<$Res> + implements _$ArtistCopyWith<$Res> { + __$ArtistCopyWithImpl(this._self, this._then); - final SourceArtist _self; - final $Res Function(SourceArtist) _then; + final _Artist _self; + final $Res Function(_Artist) _then; -/// Create a copy of SourceItem +/// Create a copy of Artist /// with the given fields replaced by the non-null parameter values. @override @pragma('vm:prefer-inline') $Res call({Object? id = null,Object? name = null,Object? starred = freezed,Object? smallImage = freezed,Object? largeImage = freezed,}) { - return _then(SourceArtist( + return _then(_Artist( id: null == id ? _self.id : id // ignore: cast_nullable_to_non_nullable as String,name: null == name ? _self.name : name // ignore: cast_nullable_to_non_nullable as String,starred: freezed == starred ? _self.starred : starred // ignore: cast_nullable_to_non_nullable @@ -336,59 +281,253 @@ as Uri?, } /// @nodoc -@JsonSerializable() +mixin _$Album { -class SourceAlbum with Starred, CoverArt implements SourceItem { - const SourceAlbum({required this.id, this.artistId, required this.name, this.albumArtist, required this.created, this.coverArt, this.year, this.starred, this.genre, this.frequentRank, this.recentRank, final String? $type}): $type = $type ?? 'album'; - factory SourceAlbum.fromJson(Map json) => _$SourceAlbumFromJson(json); - -@override final String id; - final String? artistId; - final String name; - final String? albumArtist; - final DateTime created; - final String? coverArt; - final int? year; - final DateTime? starred; - final String? genre; - final int? frequentRank; - final int? recentRank; - -@JsonKey(name: 'runtimeType') -final String $type; - - -/// Create a copy of SourceItem + String get id; String? get artistId; String get name; String? get albumArtist; DateTime get created; String? get coverArt; int? get year; DateTime? get starred; String? get genre; int? get frequentRank; int? get recentRank; +/// Create a copy of Album /// with the given fields replaced by the non-null parameter values. -@override @JsonKey(includeFromJson: false, includeToJson: false) +@JsonKey(includeFromJson: false, includeToJson: false) @pragma('vm:prefer-inline') -$SourceAlbumCopyWith get copyWith => _$SourceAlbumCopyWithImpl(this, _$identity); +$AlbumCopyWith get copyWith => _$AlbumCopyWithImpl(this as Album, _$identity); + -@override -Map toJson() { - return _$SourceAlbumToJson(this, ); -} @override bool operator ==(Object other) { - return identical(this, other) || (other.runtimeType == runtimeType&&other is SourceAlbum&&(identical(other.id, id) || other.id == id)&&(identical(other.artistId, artistId) || other.artistId == artistId)&&(identical(other.name, name) || other.name == name)&&(identical(other.albumArtist, albumArtist) || other.albumArtist == albumArtist)&&(identical(other.created, created) || other.created == created)&&(identical(other.coverArt, coverArt) || other.coverArt == coverArt)&&(identical(other.year, year) || other.year == year)&&(identical(other.starred, starred) || other.starred == starred)&&(identical(other.genre, genre) || other.genre == genre)&&(identical(other.frequentRank, frequentRank) || other.frequentRank == frequentRank)&&(identical(other.recentRank, recentRank) || other.recentRank == recentRank)); + return identical(this, other) || (other.runtimeType == runtimeType&&other is Album&&(identical(other.id, id) || other.id == id)&&(identical(other.artistId, artistId) || other.artistId == artistId)&&(identical(other.name, name) || other.name == name)&&(identical(other.albumArtist, albumArtist) || other.albumArtist == albumArtist)&&(identical(other.created, created) || other.created == created)&&(identical(other.coverArt, coverArt) || other.coverArt == coverArt)&&(identical(other.year, year) || other.year == year)&&(identical(other.starred, starred) || other.starred == starred)&&(identical(other.genre, genre) || other.genre == genre)&&(identical(other.frequentRank, frequentRank) || other.frequentRank == frequentRank)&&(identical(other.recentRank, recentRank) || other.recentRank == recentRank)); } -@JsonKey(includeFromJson: false, includeToJson: false) + @override int get hashCode => Object.hash(runtimeType,id,artistId,name,albumArtist,created,coverArt,year,starred,genre,frequentRank,recentRank); @override String toString() { - return 'SourceItem.album(id: $id, artistId: $artistId, name: $name, albumArtist: $albumArtist, created: $created, coverArt: $coverArt, year: $year, starred: $starred, genre: $genre, frequentRank: $frequentRank, recentRank: $recentRank)'; + return 'Album(id: $id, artistId: $artistId, name: $name, albumArtist: $albumArtist, created: $created, coverArt: $coverArt, year: $year, starred: $starred, genre: $genre, frequentRank: $frequentRank, recentRank: $recentRank)'; } } /// @nodoc -abstract mixin class $SourceAlbumCopyWith<$Res> implements $SourceItemCopyWith<$Res> { - factory $SourceAlbumCopyWith(SourceAlbum value, $Res Function(SourceAlbum) _then) = _$SourceAlbumCopyWithImpl; +abstract mixin class $AlbumCopyWith<$Res> { + factory $AlbumCopyWith(Album value, $Res Function(Album) _then) = _$AlbumCopyWithImpl; +@useResult +$Res call({ + String id, String? artistId, String name, String? albumArtist, DateTime created, String? coverArt, int? year, DateTime? starred, String? genre, int? frequentRank, int? recentRank +}); + + + + +} +/// @nodoc +class _$AlbumCopyWithImpl<$Res> + implements $AlbumCopyWith<$Res> { + _$AlbumCopyWithImpl(this._self, this._then); + + final Album _self; + final $Res Function(Album) _then; + +/// Create a copy of Album +/// with the given fields replaced by the non-null parameter values. +@pragma('vm:prefer-inline') @override $Res call({Object? id = null,Object? artistId = freezed,Object? name = null,Object? albumArtist = freezed,Object? created = null,Object? coverArt = freezed,Object? year = freezed,Object? starred = freezed,Object? genre = freezed,Object? frequentRank = freezed,Object? recentRank = freezed,}) { + return _then(_self.copyWith( +id: null == id ? _self.id : id // ignore: cast_nullable_to_non_nullable +as String,artistId: freezed == artistId ? _self.artistId : artistId // ignore: cast_nullable_to_non_nullable +as String?,name: null == name ? _self.name : name // ignore: cast_nullable_to_non_nullable +as String,albumArtist: freezed == albumArtist ? _self.albumArtist : albumArtist // ignore: cast_nullable_to_non_nullable +as String?,created: null == created ? _self.created : created // ignore: cast_nullable_to_non_nullable +as DateTime,coverArt: freezed == coverArt ? _self.coverArt : coverArt // ignore: cast_nullable_to_non_nullable +as String?,year: freezed == year ? _self.year : year // ignore: cast_nullable_to_non_nullable +as int?,starred: freezed == starred ? _self.starred : starred // ignore: cast_nullable_to_non_nullable +as DateTime?,genre: freezed == genre ? _self.genre : genre // ignore: cast_nullable_to_non_nullable +as String?,frequentRank: freezed == frequentRank ? _self.frequentRank : frequentRank // ignore: cast_nullable_to_non_nullable +as int?,recentRank: freezed == recentRank ? _self.recentRank : recentRank // ignore: cast_nullable_to_non_nullable +as int?, + )); +} + +} + + +/// Adds pattern-matching-related methods to [Album]. +extension AlbumPatterns on Album { +/// A variant of `map` that fallback to returning `orElse`. +/// +/// It is equivalent to doing: +/// ```dart +/// switch (sealedClass) { +/// case final Subclass value: +/// return ...; +/// case _: +/// return orElse(); +/// } +/// ``` + +@optionalTypeArgs TResult maybeMap(TResult Function( _Album value)? $default,{required TResult orElse(),}){ +final _that = this; +switch (_that) { +case _Album() when $default != null: +return $default(_that);case _: + return orElse(); + +} +} +/// A `switch`-like method, using callbacks. +/// +/// Callbacks receives the raw object, upcasted. +/// It is equivalent to doing: +/// ```dart +/// switch (sealedClass) { +/// case final Subclass value: +/// return ...; +/// case final Subclass2 value: +/// return ...; +/// } +/// ``` + +@optionalTypeArgs TResult map(TResult Function( _Album value) $default,){ +final _that = this; +switch (_that) { +case _Album(): +return $default(_that);case _: + throw StateError('Unexpected subclass'); + +} +} +/// A variant of `map` that fallback to returning `null`. +/// +/// It is equivalent to doing: +/// ```dart +/// switch (sealedClass) { +/// case final Subclass value: +/// return ...; +/// case _: +/// return null; +/// } +/// ``` + +@optionalTypeArgs TResult? mapOrNull(TResult? Function( _Album value)? $default,){ +final _that = this; +switch (_that) { +case _Album() when $default != null: +return $default(_that);case _: + return null; + +} +} +/// A variant of `when` that fallback to an `orElse` callback. +/// +/// It is equivalent to doing: +/// ```dart +/// switch (sealedClass) { +/// case Subclass(:final field): +/// return ...; +/// case _: +/// return orElse(); +/// } +/// ``` + +@optionalTypeArgs TResult maybeWhen(TResult Function( String id, String? artistId, String name, String? albumArtist, DateTime created, String? coverArt, int? year, DateTime? starred, String? genre, int? frequentRank, int? recentRank)? $default,{required TResult orElse(),}) {final _that = this; +switch (_that) { +case _Album() when $default != null: +return $default(_that.id,_that.artistId,_that.name,_that.albumArtist,_that.created,_that.coverArt,_that.year,_that.starred,_that.genre,_that.frequentRank,_that.recentRank);case _: + return orElse(); + +} +} +/// A `switch`-like method, using callbacks. +/// +/// As opposed to `map`, this offers destructuring. +/// It is equivalent to doing: +/// ```dart +/// switch (sealedClass) { +/// case Subclass(:final field): +/// return ...; +/// case Subclass2(:final field2): +/// return ...; +/// } +/// ``` + +@optionalTypeArgs TResult when(TResult Function( String id, String? artistId, String name, String? albumArtist, DateTime created, String? coverArt, int? year, DateTime? starred, String? genre, int? frequentRank, int? recentRank) $default,) {final _that = this; +switch (_that) { +case _Album(): +return $default(_that.id,_that.artistId,_that.name,_that.albumArtist,_that.created,_that.coverArt,_that.year,_that.starred,_that.genre,_that.frequentRank,_that.recentRank);case _: + throw StateError('Unexpected subclass'); + +} +} +/// A variant of `when` that fallback to returning `null` +/// +/// It is equivalent to doing: +/// ```dart +/// switch (sealedClass) { +/// case Subclass(:final field): +/// return ...; +/// case _: +/// return null; +/// } +/// ``` + +@optionalTypeArgs TResult? whenOrNull(TResult? Function( String id, String? artistId, String name, String? albumArtist, DateTime created, String? coverArt, int? year, DateTime? starred, String? genre, int? frequentRank, int? recentRank)? $default,) {final _that = this; +switch (_that) { +case _Album() when $default != null: +return $default(_that.id,_that.artistId,_that.name,_that.albumArtist,_that.created,_that.coverArt,_that.year,_that.starred,_that.genre,_that.frequentRank,_that.recentRank);case _: + return null; + +} +} + +} + +/// @nodoc + + +class _Album implements Album { + const _Album({required this.id, this.artistId, required this.name, this.albumArtist, required this.created, this.coverArt, this.year, this.starred, this.genre, this.frequentRank, this.recentRank}); + + +@override final String id; +@override final String? artistId; +@override final String name; +@override final String? albumArtist; +@override final DateTime created; +@override final String? coverArt; +@override final int? year; +@override final DateTime? starred; +@override final String? genre; +@override final int? frequentRank; +@override final int? recentRank; + +/// Create a copy of Album +/// with the given fields replaced by the non-null parameter values. +@override @JsonKey(includeFromJson: false, includeToJson: false) +@pragma('vm:prefer-inline') +_$AlbumCopyWith<_Album> get copyWith => __$AlbumCopyWithImpl<_Album>(this, _$identity); + + + +@override +bool operator ==(Object other) { + return identical(this, other) || (other.runtimeType == runtimeType&&other is _Album&&(identical(other.id, id) || other.id == id)&&(identical(other.artistId, artistId) || other.artistId == artistId)&&(identical(other.name, name) || other.name == name)&&(identical(other.albumArtist, albumArtist) || other.albumArtist == albumArtist)&&(identical(other.created, created) || other.created == created)&&(identical(other.coverArt, coverArt) || other.coverArt == coverArt)&&(identical(other.year, year) || other.year == year)&&(identical(other.starred, starred) || other.starred == starred)&&(identical(other.genre, genre) || other.genre == genre)&&(identical(other.frequentRank, frequentRank) || other.frequentRank == frequentRank)&&(identical(other.recentRank, recentRank) || other.recentRank == recentRank)); +} + + +@override +int get hashCode => Object.hash(runtimeType,id,artistId,name,albumArtist,created,coverArt,year,starred,genre,frequentRank,recentRank); + +@override +String toString() { + return 'Album(id: $id, artistId: $artistId, name: $name, albumArtist: $albumArtist, created: $created, coverArt: $coverArt, year: $year, starred: $starred, genre: $genre, frequentRank: $frequentRank, recentRank: $recentRank)'; +} + + +} + +/// @nodoc +abstract mixin class _$AlbumCopyWith<$Res> implements $AlbumCopyWith<$Res> { + factory _$AlbumCopyWith(_Album value, $Res Function(_Album) _then) = __$AlbumCopyWithImpl; @override @useResult $Res call({ String id, String? artistId, String name, String? albumArtist, DateTime created, String? coverArt, int? year, DateTime? starred, String? genre, int? frequentRank, int? recentRank @@ -399,17 +538,17 @@ $Res call({ } /// @nodoc -class _$SourceAlbumCopyWithImpl<$Res> - implements $SourceAlbumCopyWith<$Res> { - _$SourceAlbumCopyWithImpl(this._self, this._then); +class __$AlbumCopyWithImpl<$Res> + implements _$AlbumCopyWith<$Res> { + __$AlbumCopyWithImpl(this._self, this._then); - final SourceAlbum _self; - final $Res Function(SourceAlbum) _then; + final _Album _self; + final $Res Function(_Album) _then; -/// Create a copy of SourceItem +/// Create a copy of Album /// with the given fields replaced by the non-null parameter values. @override @pragma('vm:prefer-inline') $Res call({Object? id = null,Object? artistId = freezed,Object? name = null,Object? albumArtist = freezed,Object? created = null,Object? coverArt = freezed,Object? year = freezed,Object? starred = freezed,Object? genre = freezed,Object? frequentRank = freezed,Object? recentRank = freezed,}) { - return _then(SourceAlbum( + return _then(_Album( id: null == id ? _self.id : id // ignore: cast_nullable_to_non_nullable as String,artistId: freezed == artistId ? _self.artistId : artistId // ignore: cast_nullable_to_non_nullable as String?,name: null == name ? _self.name : name // ignore: cast_nullable_to_non_nullable @@ -429,56 +568,247 @@ as int?, } /// @nodoc -@JsonSerializable() +mixin _$Playlist { -class SourcePlaylist with CoverArt implements SourceItem { - const SourcePlaylist({required this.id, required this.name, this.comment, required this.created, required this.changed, this.coverArt, this.owner, this.public, final String? $type}): $type = $type ?? 'playlist'; - factory SourcePlaylist.fromJson(Map json) => _$SourcePlaylistFromJson(json); - -@override final String id; - final String name; - final String? comment; - final DateTime created; - final DateTime changed; - final String? coverArt; - final String? owner; - final bool? public; - -@JsonKey(name: 'runtimeType') -final String $type; - - -/// Create a copy of SourceItem + String get id; String get name; String? get comment; DateTime get created; DateTime get changed; String? get coverArt; String? get owner; bool? get public; +/// Create a copy of Playlist /// with the given fields replaced by the non-null parameter values. -@override @JsonKey(includeFromJson: false, includeToJson: false) +@JsonKey(includeFromJson: false, includeToJson: false) @pragma('vm:prefer-inline') -$SourcePlaylistCopyWith get copyWith => _$SourcePlaylistCopyWithImpl(this, _$identity); +$PlaylistCopyWith get copyWith => _$PlaylistCopyWithImpl(this as Playlist, _$identity); + -@override -Map toJson() { - return _$SourcePlaylistToJson(this, ); -} @override bool operator ==(Object other) { - return identical(this, other) || (other.runtimeType == runtimeType&&other is SourcePlaylist&&(identical(other.id, id) || other.id == id)&&(identical(other.name, name) || other.name == name)&&(identical(other.comment, comment) || other.comment == comment)&&(identical(other.created, created) || other.created == created)&&(identical(other.changed, changed) || other.changed == changed)&&(identical(other.coverArt, coverArt) || other.coverArt == coverArt)&&(identical(other.owner, owner) || other.owner == owner)&&(identical(other.public, public) || other.public == public)); + return identical(this, other) || (other.runtimeType == runtimeType&&other is Playlist&&(identical(other.id, id) || other.id == id)&&(identical(other.name, name) || other.name == name)&&(identical(other.comment, comment) || other.comment == comment)&&(identical(other.created, created) || other.created == created)&&(identical(other.changed, changed) || other.changed == changed)&&(identical(other.coverArt, coverArt) || other.coverArt == coverArt)&&(identical(other.owner, owner) || other.owner == owner)&&(identical(other.public, public) || other.public == public)); } -@JsonKey(includeFromJson: false, includeToJson: false) + @override int get hashCode => Object.hash(runtimeType,id,name,comment,created,changed,coverArt,owner,public); @override String toString() { - return 'SourceItem.playlist(id: $id, name: $name, comment: $comment, created: $created, changed: $changed, coverArt: $coverArt, owner: $owner, public: $public)'; + return 'Playlist(id: $id, name: $name, comment: $comment, created: $created, changed: $changed, coverArt: $coverArt, owner: $owner, public: $public)'; } } /// @nodoc -abstract mixin class $SourcePlaylistCopyWith<$Res> implements $SourceItemCopyWith<$Res> { - factory $SourcePlaylistCopyWith(SourcePlaylist value, $Res Function(SourcePlaylist) _then) = _$SourcePlaylistCopyWithImpl; +abstract mixin class $PlaylistCopyWith<$Res> { + factory $PlaylistCopyWith(Playlist value, $Res Function(Playlist) _then) = _$PlaylistCopyWithImpl; +@useResult +$Res call({ + String id, String name, String? comment, DateTime created, DateTime changed, String? coverArt, String? owner, bool? public +}); + + + + +} +/// @nodoc +class _$PlaylistCopyWithImpl<$Res> + implements $PlaylistCopyWith<$Res> { + _$PlaylistCopyWithImpl(this._self, this._then); + + final Playlist _self; + final $Res Function(Playlist) _then; + +/// Create a copy of Playlist +/// with the given fields replaced by the non-null parameter values. +@pragma('vm:prefer-inline') @override $Res call({Object? id = null,Object? name = null,Object? comment = freezed,Object? created = null,Object? changed = null,Object? coverArt = freezed,Object? owner = freezed,Object? public = freezed,}) { + return _then(_self.copyWith( +id: null == id ? _self.id : id // ignore: cast_nullable_to_non_nullable +as String,name: null == name ? _self.name : name // ignore: cast_nullable_to_non_nullable +as String,comment: freezed == comment ? _self.comment : comment // ignore: cast_nullable_to_non_nullable +as String?,created: null == created ? _self.created : created // ignore: cast_nullable_to_non_nullable +as DateTime,changed: null == changed ? _self.changed : changed // ignore: cast_nullable_to_non_nullable +as DateTime,coverArt: freezed == coverArt ? _self.coverArt : coverArt // ignore: cast_nullable_to_non_nullable +as String?,owner: freezed == owner ? _self.owner : owner // ignore: cast_nullable_to_non_nullable +as String?,public: freezed == public ? _self.public : public // ignore: cast_nullable_to_non_nullable +as bool?, + )); +} + +} + + +/// Adds pattern-matching-related methods to [Playlist]. +extension PlaylistPatterns on Playlist { +/// A variant of `map` that fallback to returning `orElse`. +/// +/// It is equivalent to doing: +/// ```dart +/// switch (sealedClass) { +/// case final Subclass value: +/// return ...; +/// case _: +/// return orElse(); +/// } +/// ``` + +@optionalTypeArgs TResult maybeMap(TResult Function( _Playlist value)? $default,{required TResult orElse(),}){ +final _that = this; +switch (_that) { +case _Playlist() when $default != null: +return $default(_that);case _: + return orElse(); + +} +} +/// A `switch`-like method, using callbacks. +/// +/// Callbacks receives the raw object, upcasted. +/// It is equivalent to doing: +/// ```dart +/// switch (sealedClass) { +/// case final Subclass value: +/// return ...; +/// case final Subclass2 value: +/// return ...; +/// } +/// ``` + +@optionalTypeArgs TResult map(TResult Function( _Playlist value) $default,){ +final _that = this; +switch (_that) { +case _Playlist(): +return $default(_that);case _: + throw StateError('Unexpected subclass'); + +} +} +/// A variant of `map` that fallback to returning `null`. +/// +/// It is equivalent to doing: +/// ```dart +/// switch (sealedClass) { +/// case final Subclass value: +/// return ...; +/// case _: +/// return null; +/// } +/// ``` + +@optionalTypeArgs TResult? mapOrNull(TResult? Function( _Playlist value)? $default,){ +final _that = this; +switch (_that) { +case _Playlist() when $default != null: +return $default(_that);case _: + return null; + +} +} +/// A variant of `when` that fallback to an `orElse` callback. +/// +/// It is equivalent to doing: +/// ```dart +/// switch (sealedClass) { +/// case Subclass(:final field): +/// return ...; +/// case _: +/// return orElse(); +/// } +/// ``` + +@optionalTypeArgs TResult maybeWhen(TResult Function( String id, String name, String? comment, DateTime created, DateTime changed, String? coverArt, String? owner, bool? public)? $default,{required TResult orElse(),}) {final _that = this; +switch (_that) { +case _Playlist() when $default != null: +return $default(_that.id,_that.name,_that.comment,_that.created,_that.changed,_that.coverArt,_that.owner,_that.public);case _: + return orElse(); + +} +} +/// A `switch`-like method, using callbacks. +/// +/// As opposed to `map`, this offers destructuring. +/// It is equivalent to doing: +/// ```dart +/// switch (sealedClass) { +/// case Subclass(:final field): +/// return ...; +/// case Subclass2(:final field2): +/// return ...; +/// } +/// ``` + +@optionalTypeArgs TResult when(TResult Function( String id, String name, String? comment, DateTime created, DateTime changed, String? coverArt, String? owner, bool? public) $default,) {final _that = this; +switch (_that) { +case _Playlist(): +return $default(_that.id,_that.name,_that.comment,_that.created,_that.changed,_that.coverArt,_that.owner,_that.public);case _: + throw StateError('Unexpected subclass'); + +} +} +/// A variant of `when` that fallback to returning `null` +/// +/// It is equivalent to doing: +/// ```dart +/// switch (sealedClass) { +/// case Subclass(:final field): +/// return ...; +/// case _: +/// return null; +/// } +/// ``` + +@optionalTypeArgs TResult? whenOrNull(TResult? Function( String id, String name, String? comment, DateTime created, DateTime changed, String? coverArt, String? owner, bool? public)? $default,) {final _that = this; +switch (_that) { +case _Playlist() when $default != null: +return $default(_that.id,_that.name,_that.comment,_that.created,_that.changed,_that.coverArt,_that.owner,_that.public);case _: + return null; + +} +} + +} + +/// @nodoc + + +class _Playlist implements Playlist { + const _Playlist({required this.id, required this.name, this.comment, required this.created, required this.changed, this.coverArt, this.owner, this.public}); + + +@override final String id; +@override final String name; +@override final String? comment; +@override final DateTime created; +@override final DateTime changed; +@override final String? coverArt; +@override final String? owner; +@override final bool? public; + +/// Create a copy of Playlist +/// with the given fields replaced by the non-null parameter values. +@override @JsonKey(includeFromJson: false, includeToJson: false) +@pragma('vm:prefer-inline') +_$PlaylistCopyWith<_Playlist> get copyWith => __$PlaylistCopyWithImpl<_Playlist>(this, _$identity); + + + +@override +bool operator ==(Object other) { + return identical(this, other) || (other.runtimeType == runtimeType&&other is _Playlist&&(identical(other.id, id) || other.id == id)&&(identical(other.name, name) || other.name == name)&&(identical(other.comment, comment) || other.comment == comment)&&(identical(other.created, created) || other.created == created)&&(identical(other.changed, changed) || other.changed == changed)&&(identical(other.coverArt, coverArt) || other.coverArt == coverArt)&&(identical(other.owner, owner) || other.owner == owner)&&(identical(other.public, public) || other.public == public)); +} + + +@override +int get hashCode => Object.hash(runtimeType,id,name,comment,created,changed,coverArt,owner,public); + +@override +String toString() { + return 'Playlist(id: $id, name: $name, comment: $comment, created: $created, changed: $changed, coverArt: $coverArt, owner: $owner, public: $public)'; +} + + +} + +/// @nodoc +abstract mixin class _$PlaylistCopyWith<$Res> implements $PlaylistCopyWith<$Res> { + factory _$PlaylistCopyWith(_Playlist value, $Res Function(_Playlist) _then) = __$PlaylistCopyWithImpl; @override @useResult $Res call({ String id, String name, String? comment, DateTime created, DateTime changed, String? coverArt, String? owner, bool? public @@ -489,17 +819,17 @@ $Res call({ } /// @nodoc -class _$SourcePlaylistCopyWithImpl<$Res> - implements $SourcePlaylistCopyWith<$Res> { - _$SourcePlaylistCopyWithImpl(this._self, this._then); +class __$PlaylistCopyWithImpl<$Res> + implements _$PlaylistCopyWith<$Res> { + __$PlaylistCopyWithImpl(this._self, this._then); - final SourcePlaylist _self; - final $Res Function(SourcePlaylist) _then; + final _Playlist _self; + final $Res Function(_Playlist) _then; -/// Create a copy of SourceItem +/// Create a copy of Playlist /// with the given fields replaced by the non-null parameter values. @override @pragma('vm:prefer-inline') $Res call({Object? id = null,Object? name = null,Object? comment = freezed,Object? created = null,Object? changed = null,Object? coverArt = freezed,Object? owner = freezed,Object? public = freezed,}) { - return _then(SourcePlaylist( + return _then(_Playlist( id: null == id ? _self.id : id // ignore: cast_nullable_to_non_nullable as String,name: null == name ? _self.name : name // ignore: cast_nullable_to_non_nullable as String,comment: freezed == comment ? _self.comment : comment // ignore: cast_nullable_to_non_nullable @@ -516,60 +846,255 @@ as bool?, } /// @nodoc -@JsonSerializable() +mixin _$Song { -class SourceSong with Starred, CoverArt implements SourceItem { - const SourceSong({required this.id, this.albumId, this.artistId, required this.title, this.artist, this.album, this.duration, this.track, this.disc, this.starred, this.genre, this.coverArt, final String? $type}): $type = $type ?? 'song'; - factory SourceSong.fromJson(Map json) => _$SourceSongFromJson(json); - -@override final String id; - final String? albumId; - final String? artistId; - final String title; - final String? artist; - final String? album; - final Duration? duration; - final int? track; - final int? disc; - final DateTime? starred; - final String? genre; - final String? coverArt; - -@JsonKey(name: 'runtimeType') -final String $type; - - -/// Create a copy of SourceItem + String get id; String? get albumId; String? get artistId; String get title; String? get artist; String? get album; Duration? get duration; int? get track; int? get disc; DateTime? get starred; String? get genre; String? get coverArt; +/// Create a copy of Song /// with the given fields replaced by the non-null parameter values. -@override @JsonKey(includeFromJson: false, includeToJson: false) +@JsonKey(includeFromJson: false, includeToJson: false) @pragma('vm:prefer-inline') -$SourceSongCopyWith get copyWith => _$SourceSongCopyWithImpl(this, _$identity); +$SongCopyWith get copyWith => _$SongCopyWithImpl(this as Song, _$identity); + -@override -Map toJson() { - return _$SourceSongToJson(this, ); -} @override bool operator ==(Object other) { - return identical(this, other) || (other.runtimeType == runtimeType&&other is SourceSong&&(identical(other.id, id) || other.id == id)&&(identical(other.albumId, albumId) || other.albumId == albumId)&&(identical(other.artistId, artistId) || other.artistId == artistId)&&(identical(other.title, title) || other.title == title)&&(identical(other.artist, artist) || other.artist == artist)&&(identical(other.album, album) || other.album == album)&&(identical(other.duration, duration) || other.duration == duration)&&(identical(other.track, track) || other.track == track)&&(identical(other.disc, disc) || other.disc == disc)&&(identical(other.starred, starred) || other.starred == starred)&&(identical(other.genre, genre) || other.genre == genre)&&(identical(other.coverArt, coverArt) || other.coverArt == coverArt)); + return identical(this, other) || (other.runtimeType == runtimeType&&other is Song&&(identical(other.id, id) || other.id == id)&&(identical(other.albumId, albumId) || other.albumId == albumId)&&(identical(other.artistId, artistId) || other.artistId == artistId)&&(identical(other.title, title) || other.title == title)&&(identical(other.artist, artist) || other.artist == artist)&&(identical(other.album, album) || other.album == album)&&(identical(other.duration, duration) || other.duration == duration)&&(identical(other.track, track) || other.track == track)&&(identical(other.disc, disc) || other.disc == disc)&&(identical(other.starred, starred) || other.starred == starred)&&(identical(other.genre, genre) || other.genre == genre)&&(identical(other.coverArt, coverArt) || other.coverArt == coverArt)); } -@JsonKey(includeFromJson: false, includeToJson: false) + @override int get hashCode => Object.hash(runtimeType,id,albumId,artistId,title,artist,album,duration,track,disc,starred,genre,coverArt); @override String toString() { - return 'SourceItem.song(id: $id, albumId: $albumId, artistId: $artistId, title: $title, artist: $artist, album: $album, duration: $duration, track: $track, disc: $disc, starred: $starred, genre: $genre, coverArt: $coverArt)'; + return 'Song(id: $id, albumId: $albumId, artistId: $artistId, title: $title, artist: $artist, album: $album, duration: $duration, track: $track, disc: $disc, starred: $starred, genre: $genre, coverArt: $coverArt)'; } } /// @nodoc -abstract mixin class $SourceSongCopyWith<$Res> implements $SourceItemCopyWith<$Res> { - factory $SourceSongCopyWith(SourceSong value, $Res Function(SourceSong) _then) = _$SourceSongCopyWithImpl; +abstract mixin class $SongCopyWith<$Res> { + factory $SongCopyWith(Song value, $Res Function(Song) _then) = _$SongCopyWithImpl; +@useResult +$Res call({ + String id, String? albumId, String? artistId, String title, String? artist, String? album, Duration? duration, int? track, int? disc, DateTime? starred, String? genre, String? coverArt +}); + + + + +} +/// @nodoc +class _$SongCopyWithImpl<$Res> + implements $SongCopyWith<$Res> { + _$SongCopyWithImpl(this._self, this._then); + + final Song _self; + final $Res Function(Song) _then; + +/// Create a copy of Song +/// with the given fields replaced by the non-null parameter values. +@pragma('vm:prefer-inline') @override $Res call({Object? id = null,Object? albumId = freezed,Object? artistId = freezed,Object? title = null,Object? artist = freezed,Object? album = freezed,Object? duration = freezed,Object? track = freezed,Object? disc = freezed,Object? starred = freezed,Object? genre = freezed,Object? coverArt = freezed,}) { + return _then(_self.copyWith( +id: null == id ? _self.id : id // ignore: cast_nullable_to_non_nullable +as String,albumId: freezed == albumId ? _self.albumId : albumId // ignore: cast_nullable_to_non_nullable +as String?,artistId: freezed == artistId ? _self.artistId : artistId // ignore: cast_nullable_to_non_nullable +as String?,title: null == title ? _self.title : title // ignore: cast_nullable_to_non_nullable +as String,artist: freezed == artist ? _self.artist : artist // ignore: cast_nullable_to_non_nullable +as String?,album: freezed == album ? _self.album : album // ignore: cast_nullable_to_non_nullable +as String?,duration: freezed == duration ? _self.duration : duration // ignore: cast_nullable_to_non_nullable +as Duration?,track: freezed == track ? _self.track : track // ignore: cast_nullable_to_non_nullable +as int?,disc: freezed == disc ? _self.disc : disc // ignore: cast_nullable_to_non_nullable +as int?,starred: freezed == starred ? _self.starred : starred // ignore: cast_nullable_to_non_nullable +as DateTime?,genre: freezed == genre ? _self.genre : genre // ignore: cast_nullable_to_non_nullable +as String?,coverArt: freezed == coverArt ? _self.coverArt : coverArt // ignore: cast_nullable_to_non_nullable +as String?, + )); +} + +} + + +/// Adds pattern-matching-related methods to [Song]. +extension SongPatterns on Song { +/// A variant of `map` that fallback to returning `orElse`. +/// +/// It is equivalent to doing: +/// ```dart +/// switch (sealedClass) { +/// case final Subclass value: +/// return ...; +/// case _: +/// return orElse(); +/// } +/// ``` + +@optionalTypeArgs TResult maybeMap(TResult Function( _Song value)? $default,{required TResult orElse(),}){ +final _that = this; +switch (_that) { +case _Song() when $default != null: +return $default(_that);case _: + return orElse(); + +} +} +/// A `switch`-like method, using callbacks. +/// +/// Callbacks receives the raw object, upcasted. +/// It is equivalent to doing: +/// ```dart +/// switch (sealedClass) { +/// case final Subclass value: +/// return ...; +/// case final Subclass2 value: +/// return ...; +/// } +/// ``` + +@optionalTypeArgs TResult map(TResult Function( _Song value) $default,){ +final _that = this; +switch (_that) { +case _Song(): +return $default(_that);case _: + throw StateError('Unexpected subclass'); + +} +} +/// A variant of `map` that fallback to returning `null`. +/// +/// It is equivalent to doing: +/// ```dart +/// switch (sealedClass) { +/// case final Subclass value: +/// return ...; +/// case _: +/// return null; +/// } +/// ``` + +@optionalTypeArgs TResult? mapOrNull(TResult? Function( _Song value)? $default,){ +final _that = this; +switch (_that) { +case _Song() when $default != null: +return $default(_that);case _: + return null; + +} +} +/// A variant of `when` that fallback to an `orElse` callback. +/// +/// It is equivalent to doing: +/// ```dart +/// switch (sealedClass) { +/// case Subclass(:final field): +/// return ...; +/// case _: +/// return orElse(); +/// } +/// ``` + +@optionalTypeArgs TResult maybeWhen(TResult Function( String id, String? albumId, String? artistId, String title, String? artist, String? album, Duration? duration, int? track, int? disc, DateTime? starred, String? genre, String? coverArt)? $default,{required TResult orElse(),}) {final _that = this; +switch (_that) { +case _Song() when $default != null: +return $default(_that.id,_that.albumId,_that.artistId,_that.title,_that.artist,_that.album,_that.duration,_that.track,_that.disc,_that.starred,_that.genre,_that.coverArt);case _: + return orElse(); + +} +} +/// A `switch`-like method, using callbacks. +/// +/// As opposed to `map`, this offers destructuring. +/// It is equivalent to doing: +/// ```dart +/// switch (sealedClass) { +/// case Subclass(:final field): +/// return ...; +/// case Subclass2(:final field2): +/// return ...; +/// } +/// ``` + +@optionalTypeArgs TResult when(TResult Function( String id, String? albumId, String? artistId, String title, String? artist, String? album, Duration? duration, int? track, int? disc, DateTime? starred, String? genre, String? coverArt) $default,) {final _that = this; +switch (_that) { +case _Song(): +return $default(_that.id,_that.albumId,_that.artistId,_that.title,_that.artist,_that.album,_that.duration,_that.track,_that.disc,_that.starred,_that.genre,_that.coverArt);case _: + throw StateError('Unexpected subclass'); + +} +} +/// A variant of `when` that fallback to returning `null` +/// +/// It is equivalent to doing: +/// ```dart +/// switch (sealedClass) { +/// case Subclass(:final field): +/// return ...; +/// case _: +/// return null; +/// } +/// ``` + +@optionalTypeArgs TResult? whenOrNull(TResult? Function( String id, String? albumId, String? artistId, String title, String? artist, String? album, Duration? duration, int? track, int? disc, DateTime? starred, String? genre, String? coverArt)? $default,) {final _that = this; +switch (_that) { +case _Song() when $default != null: +return $default(_that.id,_that.albumId,_that.artistId,_that.title,_that.artist,_that.album,_that.duration,_that.track,_that.disc,_that.starred,_that.genre,_that.coverArt);case _: + return null; + +} +} + +} + +/// @nodoc + + +class _Song implements Song { + const _Song({required this.id, this.albumId, this.artistId, required this.title, this.artist, this.album, this.duration, this.track, this.disc, this.starred, this.genre, this.coverArt}); + + +@override final String id; +@override final String? albumId; +@override final String? artistId; +@override final String title; +@override final String? artist; +@override final String? album; +@override final Duration? duration; +@override final int? track; +@override final int? disc; +@override final DateTime? starred; +@override final String? genre; +@override final String? coverArt; + +/// Create a copy of Song +/// with the given fields replaced by the non-null parameter values. +@override @JsonKey(includeFromJson: false, includeToJson: false) +@pragma('vm:prefer-inline') +_$SongCopyWith<_Song> get copyWith => __$SongCopyWithImpl<_Song>(this, _$identity); + + + +@override +bool operator ==(Object other) { + return identical(this, other) || (other.runtimeType == runtimeType&&other is _Song&&(identical(other.id, id) || other.id == id)&&(identical(other.albumId, albumId) || other.albumId == albumId)&&(identical(other.artistId, artistId) || other.artistId == artistId)&&(identical(other.title, title) || other.title == title)&&(identical(other.artist, artist) || other.artist == artist)&&(identical(other.album, album) || other.album == album)&&(identical(other.duration, duration) || other.duration == duration)&&(identical(other.track, track) || other.track == track)&&(identical(other.disc, disc) || other.disc == disc)&&(identical(other.starred, starred) || other.starred == starred)&&(identical(other.genre, genre) || other.genre == genre)&&(identical(other.coverArt, coverArt) || other.coverArt == coverArt)); +} + + +@override +int get hashCode => Object.hash(runtimeType,id,albumId,artistId,title,artist,album,duration,track,disc,starred,genre,coverArt); + +@override +String toString() { + return 'Song(id: $id, albumId: $albumId, artistId: $artistId, title: $title, artist: $artist, album: $album, duration: $duration, track: $track, disc: $disc, starred: $starred, genre: $genre, coverArt: $coverArt)'; +} + + +} + +/// @nodoc +abstract mixin class _$SongCopyWith<$Res> implements $SongCopyWith<$Res> { + factory _$SongCopyWith(_Song value, $Res Function(_Song) _then) = __$SongCopyWithImpl; @override @useResult $Res call({ String id, String? albumId, String? artistId, String title, String? artist, String? album, Duration? duration, int? track, int? disc, DateTime? starred, String? genre, String? coverArt @@ -580,17 +1105,17 @@ $Res call({ } /// @nodoc -class _$SourceSongCopyWithImpl<$Res> - implements $SourceSongCopyWith<$Res> { - _$SourceSongCopyWithImpl(this._self, this._then); +class __$SongCopyWithImpl<$Res> + implements _$SongCopyWith<$Res> { + __$SongCopyWithImpl(this._self, this._then); - final SourceSong _self; - final $Res Function(SourceSong) _then; + final _Song _self; + final $Res Function(_Song) _then; -/// Create a copy of SourceItem +/// Create a copy of Song /// with the given fields replaced by the non-null parameter values. @override @pragma('vm:prefer-inline') $Res call({Object? id = null,Object? albumId = freezed,Object? artistId = freezed,Object? title = null,Object? artist = freezed,Object? album = freezed,Object? duration = freezed,Object? track = freezed,Object? disc = freezed,Object? starred = freezed,Object? genre = freezed,Object? coverArt = freezed,}) { - return _then(SourceSong( + return _then(_Song( id: null == id ? _self.id : id // ignore: cast_nullable_to_non_nullable as String,albumId: freezed == albumId ? _self.albumId : albumId // ignore: cast_nullable_to_non_nullable as String?,artistId: freezed == artistId ? _self.artistId : artistId // ignore: cast_nullable_to_non_nullable @@ -610,41 +1135,38 @@ as String?, } - /// @nodoc -mixin _$SourcePlaylistSong { +mixin _$PlaylistSong { String get playlistId; String get songId; int get position; -/// Create a copy of SourcePlaylistSong +/// Create a copy of PlaylistSong /// with the given fields replaced by the non-null parameter values. @JsonKey(includeFromJson: false, includeToJson: false) @pragma('vm:prefer-inline') -$SourcePlaylistSongCopyWith get copyWith => _$SourcePlaylistSongCopyWithImpl(this as SourcePlaylistSong, _$identity); +$PlaylistSongCopyWith get copyWith => _$PlaylistSongCopyWithImpl(this as PlaylistSong, _$identity); - /// Serializes this SourcePlaylistSong to a JSON map. - Map toJson(); @override bool operator ==(Object other) { - return identical(this, other) || (other.runtimeType == runtimeType&&other is SourcePlaylistSong&&(identical(other.playlistId, playlistId) || other.playlistId == playlistId)&&(identical(other.songId, songId) || other.songId == songId)&&(identical(other.position, position) || other.position == position)); + return identical(this, other) || (other.runtimeType == runtimeType&&other is PlaylistSong&&(identical(other.playlistId, playlistId) || other.playlistId == playlistId)&&(identical(other.songId, songId) || other.songId == songId)&&(identical(other.position, position) || other.position == position)); } -@JsonKey(includeFromJson: false, includeToJson: false) + @override int get hashCode => Object.hash(runtimeType,playlistId,songId,position); @override String toString() { - return 'SourcePlaylistSong(playlistId: $playlistId, songId: $songId, position: $position)'; + return 'PlaylistSong(playlistId: $playlistId, songId: $songId, position: $position)'; } } /// @nodoc -abstract mixin class $SourcePlaylistSongCopyWith<$Res> { - factory $SourcePlaylistSongCopyWith(SourcePlaylistSong value, $Res Function(SourcePlaylistSong) _then) = _$SourcePlaylistSongCopyWithImpl; +abstract mixin class $PlaylistSongCopyWith<$Res> { + factory $PlaylistSongCopyWith(PlaylistSong value, $Res Function(PlaylistSong) _then) = _$PlaylistSongCopyWithImpl; @useResult $Res call({ String playlistId, String songId, int position @@ -655,14 +1177,14 @@ $Res call({ } /// @nodoc -class _$SourcePlaylistSongCopyWithImpl<$Res> - implements $SourcePlaylistSongCopyWith<$Res> { - _$SourcePlaylistSongCopyWithImpl(this._self, this._then); +class _$PlaylistSongCopyWithImpl<$Res> + implements $PlaylistSongCopyWith<$Res> { + _$PlaylistSongCopyWithImpl(this._self, this._then); - final SourcePlaylistSong _self; - final $Res Function(SourcePlaylistSong) _then; + final PlaylistSong _self; + final $Res Function(PlaylistSong) _then; -/// Create a copy of SourcePlaylistSong +/// Create a copy of PlaylistSong /// with the given fields replaced by the non-null parameter values. @pragma('vm:prefer-inline') @override $Res call({Object? playlistId = null,Object? songId = null,Object? position = null,}) { return _then(_self.copyWith( @@ -676,8 +1198,8 @@ as int, } -/// Adds pattern-matching-related methods to [SourcePlaylistSong]. -extension SourcePlaylistSongPatterns on SourcePlaylistSong { +/// Adds pattern-matching-related methods to [PlaylistSong]. +extension PlaylistSongPatterns on PlaylistSong { /// A variant of `map` that fallback to returning `orElse`. /// /// It is equivalent to doing: @@ -690,10 +1212,10 @@ extension SourcePlaylistSongPatterns on SourcePlaylistSong { /// } /// ``` -@optionalTypeArgs TResult maybeMap(TResult Function( _SourcePlaylistSong value)? $default,{required TResult orElse(),}){ +@optionalTypeArgs TResult maybeMap(TResult Function( _PlaylistSong value)? $default,{required TResult orElse(),}){ final _that = this; switch (_that) { -case _SourcePlaylistSong() when $default != null: +case _PlaylistSong() when $default != null: return $default(_that);case _: return orElse(); @@ -712,10 +1234,10 @@ return $default(_that);case _: /// } /// ``` -@optionalTypeArgs TResult map(TResult Function( _SourcePlaylistSong value) $default,){ +@optionalTypeArgs TResult map(TResult Function( _PlaylistSong value) $default,){ final _that = this; switch (_that) { -case _SourcePlaylistSong(): +case _PlaylistSong(): return $default(_that);case _: throw StateError('Unexpected subclass'); @@ -733,10 +1255,10 @@ return $default(_that);case _: /// } /// ``` -@optionalTypeArgs TResult? mapOrNull(TResult? Function( _SourcePlaylistSong value)? $default,){ +@optionalTypeArgs TResult? mapOrNull(TResult? Function( _PlaylistSong value)? $default,){ final _that = this; switch (_that) { -case _SourcePlaylistSong() when $default != null: +case _PlaylistSong() when $default != null: return $default(_that);case _: return null; @@ -756,7 +1278,7 @@ return $default(_that);case _: @optionalTypeArgs TResult maybeWhen(TResult Function( String playlistId, String songId, int position)? $default,{required TResult orElse(),}) {final _that = this; switch (_that) { -case _SourcePlaylistSong() when $default != null: +case _PlaylistSong() when $default != null: return $default(_that.playlistId,_that.songId,_that.position);case _: return orElse(); @@ -777,7 +1299,7 @@ return $default(_that.playlistId,_that.songId,_that.position);case _: @optionalTypeArgs TResult when(TResult Function( String playlistId, String songId, int position) $default,) {final _that = this; switch (_that) { -case _SourcePlaylistSong(): +case _PlaylistSong(): return $default(_that.playlistId,_that.songId,_that.position);case _: throw StateError('Unexpected subclass'); @@ -797,7 +1319,7 @@ return $default(_that.playlistId,_that.songId,_that.position);case _: @optionalTypeArgs TResult? whenOrNull(TResult? Function( String playlistId, String songId, int position)? $default,) {final _that = this; switch (_that) { -case _SourcePlaylistSong() when $default != null: +case _PlaylistSong() when $default != null: return $default(_that.playlistId,_that.songId,_that.position);case _: return null; @@ -807,47 +1329,44 @@ return $default(_that.playlistId,_that.songId,_that.position);case _: } /// @nodoc -@JsonSerializable() -class _SourcePlaylistSong implements SourcePlaylistSong { - const _SourcePlaylistSong({required this.playlistId, required this.songId, required this.position}); - factory _SourcePlaylistSong.fromJson(Map json) => _$SourcePlaylistSongFromJson(json); + +class _PlaylistSong implements PlaylistSong { + const _PlaylistSong({required this.playlistId, required this.songId, required this.position}); + @override final String playlistId; @override final String songId; @override final int position; -/// Create a copy of SourcePlaylistSong +/// Create a copy of PlaylistSong /// with the given fields replaced by the non-null parameter values. @override @JsonKey(includeFromJson: false, includeToJson: false) @pragma('vm:prefer-inline') -_$SourcePlaylistSongCopyWith<_SourcePlaylistSong> get copyWith => __$SourcePlaylistSongCopyWithImpl<_SourcePlaylistSong>(this, _$identity); +_$PlaylistSongCopyWith<_PlaylistSong> get copyWith => __$PlaylistSongCopyWithImpl<_PlaylistSong>(this, _$identity); + -@override -Map toJson() { - return _$SourcePlaylistSongToJson(this, ); -} @override bool operator ==(Object other) { - return identical(this, other) || (other.runtimeType == runtimeType&&other is _SourcePlaylistSong&&(identical(other.playlistId, playlistId) || other.playlistId == playlistId)&&(identical(other.songId, songId) || other.songId == songId)&&(identical(other.position, position) || other.position == position)); + return identical(this, other) || (other.runtimeType == runtimeType&&other is _PlaylistSong&&(identical(other.playlistId, playlistId) || other.playlistId == playlistId)&&(identical(other.songId, songId) || other.songId == songId)&&(identical(other.position, position) || other.position == position)); } -@JsonKey(includeFromJson: false, includeToJson: false) + @override int get hashCode => Object.hash(runtimeType,playlistId,songId,position); @override String toString() { - return 'SourcePlaylistSong(playlistId: $playlistId, songId: $songId, position: $position)'; + return 'PlaylistSong(playlistId: $playlistId, songId: $songId, position: $position)'; } } /// @nodoc -abstract mixin class _$SourcePlaylistSongCopyWith<$Res> implements $SourcePlaylistSongCopyWith<$Res> { - factory _$SourcePlaylistSongCopyWith(_SourcePlaylistSong value, $Res Function(_SourcePlaylistSong) _then) = __$SourcePlaylistSongCopyWithImpl; +abstract mixin class _$PlaylistSongCopyWith<$Res> implements $PlaylistSongCopyWith<$Res> { + factory _$PlaylistSongCopyWith(_PlaylistSong value, $Res Function(_PlaylistSong) _then) = __$PlaylistSongCopyWithImpl; @override @useResult $Res call({ String playlistId, String songId, int position @@ -858,17 +1377,17 @@ $Res call({ } /// @nodoc -class __$SourcePlaylistSongCopyWithImpl<$Res> - implements _$SourcePlaylistSongCopyWith<$Res> { - __$SourcePlaylistSongCopyWithImpl(this._self, this._then); +class __$PlaylistSongCopyWithImpl<$Res> + implements _$PlaylistSongCopyWith<$Res> { + __$PlaylistSongCopyWithImpl(this._self, this._then); - final _SourcePlaylistSong _self; - final $Res Function(_SourcePlaylistSong) _then; + final _PlaylistSong _self; + final $Res Function(_PlaylistSong) _then; -/// Create a copy of SourcePlaylistSong +/// Create a copy of PlaylistSong /// with the given fields replaced by the non-null parameter values. @override @pragma('vm:prefer-inline') $Res call({Object? playlistId = null,Object? songId = null,Object? position = null,}) { - return _then(_SourcePlaylistSong( + return _then(_PlaylistSong( playlistId: null == playlistId ? _self.playlistId : playlistId // ignore: cast_nullable_to_non_nullable as String,songId: null == songId ? _self.songId : songId // ignore: cast_nullable_to_non_nullable as String,position: null == position ? _self.position : position // ignore: cast_nullable_to_non_nullable diff --git a/lib/sources/models.g.dart b/lib/sources/models.g.dart deleted file mode 100644 index 2b32fbc..0000000 --- a/lib/sources/models.g.dart +++ /dev/null @@ -1,142 +0,0 @@ -// GENERATED CODE - DO NOT MODIFY BY HAND - -part of 'models.dart'; - -// ************************************************************************** -// JsonSerializableGenerator -// ************************************************************************** - -SourceArtist _$SourceArtistFromJson(Map json) => SourceArtist( - id: json['id'] as String, - name: json['name'] as String, - starred: json['starred'] == null - ? null - : DateTime.parse(json['starred'] as String), - smallImage: json['smallImage'] == null - ? null - : Uri.parse(json['smallImage'] as String), - largeImage: json['largeImage'] == null - ? null - : Uri.parse(json['largeImage'] as String), - $type: json['runtimeType'] as String?, -); - -Map _$SourceArtistToJson(SourceArtist instance) => - { - 'id': instance.id, - 'name': instance.name, - 'starred': instance.starred?.toIso8601String(), - 'smallImage': instance.smallImage?.toString(), - 'largeImage': instance.largeImage?.toString(), - 'runtimeType': instance.$type, - }; - -SourceAlbum _$SourceAlbumFromJson(Map json) => SourceAlbum( - id: json['id'] as String, - artistId: json['artistId'] as String?, - name: json['name'] as String, - albumArtist: json['albumArtist'] as String?, - created: DateTime.parse(json['created'] as String), - coverArt: json['coverArt'] as String?, - year: (json['year'] as num?)?.toInt(), - starred: json['starred'] == null - ? null - : DateTime.parse(json['starred'] as String), - genre: json['genre'] as String?, - frequentRank: (json['frequentRank'] as num?)?.toInt(), - recentRank: (json['recentRank'] as num?)?.toInt(), - $type: json['runtimeType'] as String?, -); - -Map _$SourceAlbumToJson(SourceAlbum instance) => - { - 'id': instance.id, - 'artistId': instance.artistId, - 'name': instance.name, - 'albumArtist': instance.albumArtist, - 'created': instance.created.toIso8601String(), - 'coverArt': instance.coverArt, - 'year': instance.year, - 'starred': instance.starred?.toIso8601String(), - 'genre': instance.genre, - 'frequentRank': instance.frequentRank, - 'recentRank': instance.recentRank, - 'runtimeType': instance.$type, - }; - -SourcePlaylist _$SourcePlaylistFromJson(Map json) => - SourcePlaylist( - id: json['id'] as String, - name: json['name'] as String, - comment: json['comment'] as String?, - created: DateTime.parse(json['created'] as String), - changed: DateTime.parse(json['changed'] as String), - coverArt: json['coverArt'] as String?, - owner: json['owner'] as String?, - public: json['public'] as bool?, - $type: json['runtimeType'] as String?, - ); - -Map _$SourcePlaylistToJson(SourcePlaylist instance) => - { - 'id': instance.id, - 'name': instance.name, - 'comment': instance.comment, - 'created': instance.created.toIso8601String(), - 'changed': instance.changed.toIso8601String(), - 'coverArt': instance.coverArt, - 'owner': instance.owner, - 'public': instance.public, - 'runtimeType': instance.$type, - }; - -SourceSong _$SourceSongFromJson(Map json) => SourceSong( - id: json['id'] as String, - albumId: json['albumId'] as String?, - artistId: json['artistId'] as String?, - title: json['title'] as String, - artist: json['artist'] as String?, - album: json['album'] as String?, - duration: json['duration'] == null - ? null - : Duration(microseconds: (json['duration'] as num).toInt()), - track: (json['track'] as num?)?.toInt(), - disc: (json['disc'] as num?)?.toInt(), - starred: json['starred'] == null - ? null - : DateTime.parse(json['starred'] as String), - genre: json['genre'] as String?, - coverArt: json['coverArt'] as String?, - $type: json['runtimeType'] as String?, -); - -Map _$SourceSongToJson(SourceSong instance) => - { - 'id': instance.id, - 'albumId': instance.albumId, - 'artistId': instance.artistId, - 'title': instance.title, - 'artist': instance.artist, - 'album': instance.album, - 'duration': instance.duration?.inMicroseconds, - 'track': instance.track, - 'disc': instance.disc, - 'starred': instance.starred?.toIso8601String(), - 'genre': instance.genre, - 'coverArt': instance.coverArt, - 'runtimeType': instance.$type, - }; - -_SourcePlaylistSong _$SourcePlaylistSongFromJson(Map json) => - _SourcePlaylistSong( - playlistId: json['playlistId'] as String, - songId: json['songId'] as String, - position: (json['position'] as num).toInt(), - ); - -Map _$SourcePlaylistSongToJson(_SourcePlaylistSong instance) => - { - 'playlistId': instance.playlistId, - 'songId': instance.songId, - 'position': instance.position, - }; diff --git a/lib/sources/music_source.dart b/lib/sources/music_source.dart index ab6ce0f..3c4e63c 100644 --- a/lib/sources/music_source.dart +++ b/lib/sources/music_source.dart @@ -3,11 +3,11 @@ import 'models.dart'; abstract class MusicSource { Future ping(); - Stream allAlbums(); - Stream allArtists(); - Stream allPlaylists(); - Stream allSongs(); - Stream allPlaylistSongs(); + Stream allAlbums(); + Stream allArtists(); + Stream allPlaylists(); + Stream allSongs(); + Stream allPlaylistSongs(); Uri streamUri(String songId); Uri downloadUri(String songId); diff --git a/lib/sources/subsonic/mapping.dart b/lib/sources/subsonic/mapping.dart index cfa366c..d816557 100644 --- a/lib/sources/subsonic/mapping.dart +++ b/lib/sources/subsonic/mapping.dart @@ -2,7 +2,7 @@ import 'package:xml/xml.dart'; import '../models.dart'; -SourceArtist mapArtist(XmlElement e, XmlElement? info) => SourceArtist( +Artist mapArtist(XmlElement e, XmlElement? info) => Artist( id: e.getAttribute('id')!, name: e.getAttribute('name')!, starred: DateTime.tryParse(e.getAttribute('starred').toString()), @@ -10,11 +10,11 @@ SourceArtist mapArtist(XmlElement e, XmlElement? info) => SourceArtist( largeImage: Uri.tryParse(info?.getElement('largeImageUrl')?.innerText ?? ''), ); -SourceAlbum mapAlbum( +Album mapAlbum( XmlElement e, { int? frequentRank, int? recentRank, -}) => SourceAlbum( +}) => Album( id: e.getAttribute('id')!, artistId: e.getAttribute('artistId'), name: e.getAttribute('name')!, @@ -28,7 +28,7 @@ SourceAlbum mapAlbum( recentRank: recentRank, ); -SourcePlaylist mapPlaylist(XmlElement e) => SourcePlaylist( +Playlist mapPlaylist(XmlElement e) => Playlist( id: e.getAttribute('id')!, name: e.getAttribute('name')!, comment: e.getAttribute('comment'), @@ -36,10 +36,9 @@ SourcePlaylist mapPlaylist(XmlElement e) => SourcePlaylist( created: DateTime.parse(e.getAttribute('created')!), changed: DateTime.parse(e.getAttribute('changed')!), owner: e.getAttribute('owner'), - public: bool.tryParse(e.getAttribute('public').toString()), ); -SourceSong mapSong(XmlElement e) => SourceSong( +Song mapSong(XmlElement e) => Song( id: e.getAttribute('id')!, albumId: e.getAttribute('albumId'), artistId: e.getAttribute('artistId'), @@ -57,10 +56,10 @@ SourceSong mapSong(XmlElement e) => SourceSong( genre: e.getAttribute('genre'), ); -SourcePlaylistSong mapPlaylistSong( +PlaylistSong mapPlaylistSong( int index, XmlElement e, -) => SourcePlaylistSong( +) => PlaylistSong( playlistId: e.parentElement!.getAttribute('id')!, songId: e.getAttribute('id')!, position: index, diff --git a/lib/sources/subsonic/source.dart b/lib/sources/subsonic/source.dart index a3b6960..0d57246 100644 --- a/lib/sources/subsonic/source.dart +++ b/lib/sources/subsonic/source.dart @@ -41,7 +41,7 @@ class SubsonicSource implements MusicSource { } @override - Stream allArtists() async* { + Stream allArtists() async* { final getArtistsRes = await _pool.withResource( () => client.get('getArtists'), ); @@ -58,7 +58,7 @@ class SubsonicSource implements MusicSource { } @override - Stream allAlbums() async* { + Stream allAlbums() async* { final extras = await Future.wait([ _albumList( 'frequent', @@ -89,7 +89,7 @@ class SubsonicSource implements MusicSource { } @override - Stream allPlaylists() async* { + Stream allPlaylists() async* { final res = await _pool.withResource(() => client.get('getPlaylists')); yield* Stream.fromIterable( @@ -98,7 +98,7 @@ class SubsonicSource implements MusicSource { } @override - Stream allPlaylistSongs() async* { + Stream allPlaylistSongs() async* { final allPlaylists = await _pool.withResource( () => client.get('getPlaylists'), ); @@ -116,7 +116,7 @@ class SubsonicSource implements MusicSource { } @override - Stream allSongs() async* { + Stream allSongs() async* { if (await supportsFastSongSync) { await for (var songs in _songSearch()) { yield* Stream.fromIterable(songs.map(mapSong)); diff --git a/pubspec.lock b/pubspec.lock index 7ba2831..d9d16a6 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -42,7 +42,7 @@ packages: source: hosted version: "2.7.0" async: - dependency: transitive + dependency: "direct main" description: name: async sha256: "758e6d74e971c3e5aceb4110bfd6698efc7f501675bcfe0c775459a8140750eb" @@ -161,6 +161,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.4.0" + charcode: + dependency: transitive + description: + name: charcode + sha256: fb0f1107cac15a5ea6ef0a6ef71a807b9e4267c713bb93e00e92d737cc8dbd8a + url: "https://pub.dev" + source: hosted + version: "1.4.0" checked_yaml: dependency: transitive description: @@ -281,6 +289,30 @@ packages: url: "https://pub.dev" source: hosted version: "3.1.1" + drift: + dependency: "direct main" + description: + name: drift + sha256: "83290a32ae006a7535c5ecf300722cb77177250d9df4ee2becc5fa8a36095114" + url: "https://pub.dev" + source: hosted + version: "2.29.0" + drift_dev: + dependency: "direct dev" + description: + name: drift_dev + sha256: "6019f827544e77524ffd5134ae0cb75dfd92ef5ef3e269872af92840c929cd43" + url: "https://pub.dev" + source: hosted + version: "2.29.0" + drift_flutter: + dependency: "direct main" + description: + name: drift_flutter + sha256: b7534bf320aac5213259aac120670ba67b63a1fd010505babc436ff86083818f + url: "https://pub.dev" + source: hosted + version: "0.2.7" fake_async: dependency: transitive description: @@ -289,6 +321,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.3.3" + fast_immutable_collections: + dependency: "direct main" + description: + name: fast_immutable_collections + sha256: "19f70498af299cbce5ff919dbbecd5abfd9d0c28139004f68d3810ce23dedfb3" + url: "https://pub.dev" + source: hosted + version: "11.1.0" ffi: dependency: transitive description: @@ -601,7 +641,7 @@ packages: source: hosted version: "2.2.0" path: - dependency: transitive + dependency: "direct main" description: name: path sha256: "75cca69d1490965be98c73ceaea117e8a04dd21217b37b292c9ddbec0d955bc5" @@ -609,7 +649,7 @@ packages: source: hosted version: "1.9.1" path_provider: - dependency: transitive + dependency: "direct main" description: name: path_provider sha256: "50c5dd5b6e1aaf6fb3a78b33f6aa3afca52bf903a8a5298f53101fdaee55bbcd" @@ -704,6 +744,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.5.0" + recase: + dependency: transitive + description: + name: recase + sha256: e4eb4ec2dcdee52dcf99cb4ceabaffc631d7424ee55e56f280bc039737f89213 + url: "https://pub.dev" + source: hosted + version: "4.1.0" riverpod: dependency: transitive description: @@ -869,6 +917,30 @@ packages: url: "https://pub.dev" source: hosted version: "2.4.0" + sqlite3: + dependency: transitive + description: + name: sqlite3 + sha256: "3145bd74dcdb4fd6f5c6dda4d4e4490a8087d7f286a14dee5d37087290f0f8a2" + url: "https://pub.dev" + source: hosted + version: "2.9.4" + sqlite3_flutter_libs: + dependency: transitive + description: + name: sqlite3_flutter_libs + sha256: "69c80d812ef2500202ebd22002cbfc1b6565e9ff56b2f971e757fac5d42294df" + url: "https://pub.dev" + source: hosted + version: "0.5.40" + sqlparser: + dependency: transitive + description: + name: sqlparser + sha256: "54eea43e36dd3769274c3108625f9ea1a382f8d2ac8b16f3e4589d9bd9b0e16c" + url: "https://pub.dev" + source: hosted + version: "0.42.0" stack_trace: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index 5581df5..fc64b1d 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -7,9 +7,13 @@ environment: sdk: ^3.9.2 dependencies: + async: ^2.13.0 cached_network_image: ^3.4.1 collection: ^1.19.1 crypto: ^3.0.6 + drift: ^2.29.0 + drift_flutter: ^0.2.7 + fast_immutable_collections: ^11.1.0 flutter: sdk: flutter flutter_hooks: ^0.21.3+1 @@ -20,6 +24,8 @@ dependencies: infinite_scroll_pagination: ^5.1.1 json_annotation: ^4.9.0 material_symbols_icons: ^4.2874.0 + path: ^1.9.1 + path_provider: ^2.1.5 pool: ^1.5.2 xml: ^6.6.1 @@ -33,6 +39,7 @@ dev_dependencies: freezed: ^3.2.3 json_serializable: ^6.11.1 test: ^1.26.2 + drift_dev: ^2.29.0 flutter: uses-material-design: true diff --git a/test/services/sync_service_test.dart b/test/services/sync_service_test.dart new file mode 100644 index 0000000..30cb6cc --- /dev/null +++ b/test/services/sync_service_test.dart @@ -0,0 +1,79 @@ +import 'package:subtracks/database/database.dart'; +import 'package:subtracks/services/sync_services.dart'; +import 'package:subtracks/sources/subsonic/source.dart'; +import 'package:test/test.dart'; + +import '../util/database.dart'; +import '../util/subsonic.dart'; + +void main() { + late SubtracksDatabase db; + late SubsonicSource source; + late int sourceId; + late int sourceIdOther; + + late SyncService sync; + + setUp(() async { + db = testDatabase(); + source = SubsonicSource(testServerClients()[Servers.navidrome]!); + sourceId = await db + .into(db.sources) + .insert(SourcesCompanion.insert(name: 'navidrome')); + sourceIdOther = await db + .into(db.sources) + .insert(SourcesCompanion.insert(name: 'other')); + + sync = SyncService( + db: db, + source: source, + sourceId: sourceId, + ); + }); + + tearDown(() async { + await db.close(); + }); + + test('syncArtists', () async { + await db + .into(db.artists) + .insert( + ArtistsCompanion.insert( + sourceId: sourceId, + id: 'shouldBeDeleted', + name: 'shouldBeDeleted', + ), + ); + await db + .into(db.artists) + .insert( + ArtistsCompanion.insert( + sourceId: sourceIdOther, + id: 'shouldBeKept', + name: 'shouldBeKept', + ), + ); + + await sync.syncArtists(); + + expect( + await db.managers.artists + .filter((f) => f.sourceId.equals(sourceId)) + .count(), + equals(2), + ); + expect( + await db.managers.artists + .filter((f) => f.id.equals('shouldBeDeleted')) + .getSingleOrNull(), + isNull, + ); + expect( + await db.managers.artists + .filter((f) => f.id.equals('shouldBeKept')) + .getSingleOrNull(), + isNotNull, + ); + }); +} diff --git a/test/sources/subsonic_test.dart b/test/sources/subsonic_test.dart index 26f64dc..bd511fd 100644 --- a/test/sources/subsonic_test.dart +++ b/test/sources/subsonic_test.dart @@ -1,147 +1,109 @@ import 'package:collection/collection.dart'; -import 'package:http/http.dart'; -import 'package:subtracks/sources/subsonic/client.dart'; import 'package:subtracks/sources/subsonic/source.dart'; import 'package:test/test.dart'; -class TestHttpClient extends BaseClient { - @override - Future send(BaseRequest request) => request.send(); -} - -class Server { - Server({ - required this.name, - required this.client, - }); - - final String name; - final SubsonicClient client; -} +import '../util/subsonic.dart'; void main() { - late SubsonicSource source; + groupByTestServer((client) { + late SubsonicSource source; - final clients = [ - Server( - name: 'navidrome', - client: SubsonicClient( - http: TestHttpClient(), - address: Uri.parse('http://localhost:4533/'), - username: 'admin', - password: 'password', - ), - ), - Server( - name: 'gonic', - client: SubsonicClient( - http: TestHttpClient(), - address: Uri.parse('http://localhost:4747/'), - username: 'admin', - password: 'admin', - ), - ), - ]; - - for (final Server(:name, :client) in clients) { - group(name, () { - setUp(() async { - source = SubsonicSource(client); - }); - - test('ping', () async { - await source.ping(); - }); - - test('allAlbums', () async { - final items = await source.allAlbums().toList(); - - expect(items.length, equals(3)); - - final kosmo = items.firstWhere((a) => a.name == 'Kosmonaut'); - - expect(kosmo.id.length, greaterThan(0)); - expect(kosmo.artistId?.length, greaterThan(0)); - expect(kosmo.albumArtist, equals('Ugress')); - expect(kosmo.created.compareTo(DateTime.now()), lessThan(0)); - expect(kosmo.coverArt?.length, greaterThan(0)); - expect(kosmo.year, equals(2006)); - expect(kosmo.starred, isNull); - expect(kosmo.genre, equals('Electronic')); - - final retro = items.firstWhere( - (a) => a.name == 'Retroconnaissance EP', - ); - final dunno = items.firstWhere( - (a) => a.name == "I Don't Know What I'm Doing", - ); - - expect(kosmo.recentRank, equals(0)); - expect(kosmo.frequentRank, equals(1)); - - expect(retro.recentRank, equals(1)); - expect(retro.frequentRank, equals(0)); - - expect(dunno.recentRank, isNull); - expect(dunno.frequentRank, isNull); - }); - - test('allArtists', () async { - final items = await source.allArtists().toList(); - - expect(items.length, equals(2)); - }); - - test('allSongs', () async { - final items = await source.allSongs().toList(); - - expect(items.length, equals(20)); - }); - - test('allPlaylists', () async { - final items = await source.allPlaylists().toList(); - - expect(items.length, equals(0)); - }); - - test('allPlaylistSongs', () async { - final items = await source.allPlaylistSongs().toList(); - - expect(items.length, equals(0)); - }); - - test('album-artist relation', () async { - final artists = await source.allArtists().toList(); - final albums = await source.allAlbums().toList(); - - final artistAlbums = artists - .map( - (artist) => [ - artist.name, - ...albums - .where((album) => album.artistId == artist.id) - .map((album) => album.name) - .sorted(), - ], - ) - .sorted((a, b) => (a[0]).compareTo(b[0])); - - expect(artistAlbums.length, equals(2)); - expect( - artistAlbums, - equals([ - [ - 'Brad Sucks', - "I Don't Know What I'm Doing", - ], - [ - 'Ugress', - 'Kosmonaut', - 'Retroconnaissance EP', - ], - ]), - ); - }); + setUp(() async { + source = SubsonicSource(client); }); - } + + test('ping', () async { + await source.ping(); + }); + + test('allAlbums', () async { + final items = await source.allAlbums().toList(); + + expect(items.length, equals(3)); + + final kosmo = items.firstWhere((a) => a.name == 'Kosmonaut'); + + expect(kosmo.id.length, greaterThan(0)); + expect(kosmo.artistId?.length, greaterThan(0)); + expect(kosmo.albumArtist, equals('Ugress')); + expect(kosmo.created.compareTo(DateTime.now()), lessThan(0)); + expect(kosmo.coverArt?.length, greaterThan(0)); + expect(kosmo.year, equals(2006)); + expect(kosmo.starred, isNull); + expect(kosmo.genre, equals('Electronic')); + + final retro = items.firstWhere( + (a) => a.name == 'Retroconnaissance EP', + ); + final dunno = items.firstWhere( + (a) => a.name == "I Don't Know What I'm Doing", + ); + + expect(kosmo.recentRank, equals(0)); + expect(kosmo.frequentRank, equals(1)); + + expect(retro.recentRank, equals(1)); + expect(retro.frequentRank, equals(0)); + + expect(dunno.recentRank, isNull); + expect(dunno.frequentRank, isNull); + }); + + test('allArtists', () async { + final items = await source.allArtists().toList(); + + expect(items.length, equals(2)); + }); + + test('allSongs', () async { + final items = await source.allSongs().toList(); + + expect(items.length, equals(20)); + }); + + test('allPlaylists', () async { + final items = await source.allPlaylists().toList(); + + expect(items.length, equals(0)); + }); + + test('allPlaylistSongs', () async { + final items = await source.allPlaylistSongs().toList(); + + expect(items.length, equals(0)); + }); + + test('album-artist relation', () async { + final artists = await source.allArtists().toList(); + final albums = await source.allAlbums().toList(); + + final artistAlbums = artists + .map( + (artist) => [ + artist.name, + ...albums + .where((album) => album.artistId == artist.id) + .map((album) => album.name) + .sorted(), + ], + ) + .sorted((a, b) => (a[0]).compareTo(b[0])); + + expect(artistAlbums.length, equals(2)); + expect( + artistAlbums, + equals([ + [ + 'Brad Sucks', + "I Don't Know What I'm Doing", + ], + [ + 'Ugress', + 'Kosmonaut', + 'Retroconnaissance EP', + ], + ]), + ); + }); + }); } diff --git a/test/util/database.dart b/test/util/database.dart new file mode 100644 index 0000000..cd7de34 --- /dev/null +++ b/test/util/database.dart @@ -0,0 +1,10 @@ +import 'package:drift/drift.dart'; +import 'package:drift/native.dart'; +import 'package:subtracks/database/database.dart'; + +SubtracksDatabase testDatabase() => SubtracksDatabase( + DatabaseConnection( + NativeDatabase.memory(), + closeStreamsSynchronously: true, + ), +); diff --git a/test/util/http.dart b/test/util/http.dart new file mode 100644 index 0000000..d1dec9e --- /dev/null +++ b/test/util/http.dart @@ -0,0 +1,6 @@ +import 'package:http/http.dart'; + +class TestHttpClient extends BaseClient { + @override + Future send(BaseRequest request) => request.send(); +} diff --git a/test/util/subsonic.dart b/test/util/subsonic.dart new file mode 100644 index 0000000..e6ee8cb --- /dev/null +++ b/test/util/subsonic.dart @@ -0,0 +1,34 @@ +import 'package:subtracks/sources/subsonic/client.dart'; +import 'package:test/test.dart'; + +import 'http.dart'; + +enum Servers { + navidrome, + gonic, +} + +Map testServerClients() => { + Servers.navidrome: SubsonicClient( + http: TestHttpClient(), + address: Uri.parse('http://localhost:4533/'), + username: 'admin', + password: 'password', + ), + Servers.gonic: SubsonicClient( + http: TestHttpClient(), + address: Uri.parse('http://localhost:4747/'), + username: 'admin', + password: 'admin', + ), +}; + +void groupByTestServer(void Function(SubsonicClient client) callback) { + final clients = testServerClients(); + + for (final MapEntry(key: server, value: client) in clients.entries) { + group(server.name, () { + callback(client); + }); + } +}