mirror of
https://github.com/austinried/subtracks.git
synced 2025-12-27 00:59:28 +01:00
Compare commits
3 Commits
51b9f3f1a8
...
914ec77ce0
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
914ec77ce0 | ||
|
|
de9bc98044 | ||
|
|
b5d52a034d |
@ -7,6 +7,10 @@
|
|||||||
"actionsDownloadDelete",
|
"actionsDownloadDelete",
|
||||||
"actionsOk",
|
"actionsOk",
|
||||||
"controlsShuffle",
|
"controlsShuffle",
|
||||||
|
"navigationTabsAlbums",
|
||||||
|
"navigationTabsArtists",
|
||||||
|
"navigationTabsPlaylists",
|
||||||
|
"navigationTabsSongs",
|
||||||
"resourcesAlbumCount",
|
"resourcesAlbumCount",
|
||||||
"resourcesArtistCount",
|
"resourcesArtistCount",
|
||||||
"resourcesFilterAlbum",
|
"resourcesFilterAlbum",
|
||||||
@ -40,6 +44,10 @@
|
|||||||
"actionsDownloadDelete",
|
"actionsDownloadDelete",
|
||||||
"actionsOk",
|
"actionsOk",
|
||||||
"controlsShuffle",
|
"controlsShuffle",
|
||||||
|
"navigationTabsAlbums",
|
||||||
|
"navigationTabsArtists",
|
||||||
|
"navigationTabsPlaylists",
|
||||||
|
"navigationTabsSongs",
|
||||||
"resourcesAlbumCount",
|
"resourcesAlbumCount",
|
||||||
"resourcesArtistCount",
|
"resourcesArtistCount",
|
||||||
"resourcesFilterAlbum",
|
"resourcesFilterAlbum",
|
||||||
@ -66,6 +74,10 @@
|
|||||||
],
|
],
|
||||||
|
|
||||||
"cs": [
|
"cs": [
|
||||||
|
"navigationTabsAlbums",
|
||||||
|
"navigationTabsArtists",
|
||||||
|
"navigationTabsPlaylists",
|
||||||
|
"navigationTabsSongs",
|
||||||
"resourcesAlbumCount",
|
"resourcesAlbumCount",
|
||||||
"resourcesArtistCount",
|
"resourcesArtistCount",
|
||||||
"resourcesPlaylistCount",
|
"resourcesPlaylistCount",
|
||||||
@ -93,6 +105,10 @@
|
|||||||
"actionsStar",
|
"actionsStar",
|
||||||
"actionsUnstar",
|
"actionsUnstar",
|
||||||
"controlsShuffle",
|
"controlsShuffle",
|
||||||
|
"navigationTabsAlbums",
|
||||||
|
"navigationTabsArtists",
|
||||||
|
"navigationTabsPlaylists",
|
||||||
|
"navigationTabsSongs",
|
||||||
"resourcesAlbumCount",
|
"resourcesAlbumCount",
|
||||||
"resourcesArtistCount",
|
"resourcesArtistCount",
|
||||||
"resourcesFilterAlbum",
|
"resourcesFilterAlbum",
|
||||||
@ -127,11 +143,19 @@
|
|||||||
],
|
],
|
||||||
|
|
||||||
"de": [
|
"de": [
|
||||||
|
"navigationTabsAlbums",
|
||||||
|
"navigationTabsArtists",
|
||||||
|
"navigationTabsPlaylists",
|
||||||
|
"navigationTabsSongs",
|
||||||
"settingsAboutShareLogs",
|
"settingsAboutShareLogs",
|
||||||
"settingsAboutChooseLog"
|
"settingsAboutChooseLog"
|
||||||
],
|
],
|
||||||
|
|
||||||
"es": [
|
"es": [
|
||||||
|
"navigationTabsAlbums",
|
||||||
|
"navigationTabsArtists",
|
||||||
|
"navigationTabsPlaylists",
|
||||||
|
"navigationTabsSongs",
|
||||||
"resourcesAlbumCount",
|
"resourcesAlbumCount",
|
||||||
"resourcesArtistCount",
|
"resourcesArtistCount",
|
||||||
"resourcesFilterAlbum",
|
"resourcesFilterAlbum",
|
||||||
@ -165,6 +189,10 @@
|
|||||||
"actionsDownloadDelete",
|
"actionsDownloadDelete",
|
||||||
"actionsOk",
|
"actionsOk",
|
||||||
"controlsShuffle",
|
"controlsShuffle",
|
||||||
|
"navigationTabsAlbums",
|
||||||
|
"navigationTabsArtists",
|
||||||
|
"navigationTabsPlaylists",
|
||||||
|
"navigationTabsSongs",
|
||||||
"resourcesAlbumCount",
|
"resourcesAlbumCount",
|
||||||
"resourcesArtistCount",
|
"resourcesArtistCount",
|
||||||
"resourcesFilterAlbum",
|
"resourcesFilterAlbum",
|
||||||
@ -190,6 +218,13 @@
|
|||||||
"settingsServersFieldsName"
|
"settingsServersFieldsName"
|
||||||
],
|
],
|
||||||
|
|
||||||
|
"gl": [
|
||||||
|
"navigationTabsAlbums",
|
||||||
|
"navigationTabsArtists",
|
||||||
|
"navigationTabsPlaylists",
|
||||||
|
"navigationTabsSongs"
|
||||||
|
],
|
||||||
|
|
||||||
"it": [
|
"it": [
|
||||||
"actionsCancel",
|
"actionsCancel",
|
||||||
"actionsDelete",
|
"actionsDelete",
|
||||||
@ -198,6 +233,10 @@
|
|||||||
"actionsDownloadDelete",
|
"actionsDownloadDelete",
|
||||||
"actionsOk",
|
"actionsOk",
|
||||||
"controlsShuffle",
|
"controlsShuffle",
|
||||||
|
"navigationTabsAlbums",
|
||||||
|
"navigationTabsArtists",
|
||||||
|
"navigationTabsPlaylists",
|
||||||
|
"navigationTabsSongs",
|
||||||
"resourcesAlbumCount",
|
"resourcesAlbumCount",
|
||||||
"resourcesArtistCount",
|
"resourcesArtistCount",
|
||||||
"resourcesFilterAlbum",
|
"resourcesFilterAlbum",
|
||||||
@ -234,6 +273,10 @@
|
|||||||
"actionsUnstar",
|
"actionsUnstar",
|
||||||
"controlsShuffle",
|
"controlsShuffle",
|
||||||
"messagesNothingHere",
|
"messagesNothingHere",
|
||||||
|
"navigationTabsAlbums",
|
||||||
|
"navigationTabsArtists",
|
||||||
|
"navigationTabsPlaylists",
|
||||||
|
"navigationTabsSongs",
|
||||||
"resourcesAlbumActionsPlay",
|
"resourcesAlbumActionsPlay",
|
||||||
"resourcesAlbumActionsView",
|
"resourcesAlbumActionsView",
|
||||||
"resourcesAlbumCount",
|
"resourcesAlbumCount",
|
||||||
@ -309,6 +352,10 @@
|
|||||||
"actionsDownloadDelete",
|
"actionsDownloadDelete",
|
||||||
"actionsOk",
|
"actionsOk",
|
||||||
"controlsShuffle",
|
"controlsShuffle",
|
||||||
|
"navigationTabsAlbums",
|
||||||
|
"navigationTabsArtists",
|
||||||
|
"navigationTabsPlaylists",
|
||||||
|
"navigationTabsSongs",
|
||||||
"resourcesAlbumCount",
|
"resourcesAlbumCount",
|
||||||
"resourcesArtistCount",
|
"resourcesArtistCount",
|
||||||
"resourcesFilterAlbum",
|
"resourcesFilterAlbum",
|
||||||
@ -342,6 +389,10 @@
|
|||||||
"actionsDownloadDelete",
|
"actionsDownloadDelete",
|
||||||
"actionsOk",
|
"actionsOk",
|
||||||
"controlsShuffle",
|
"controlsShuffle",
|
||||||
|
"navigationTabsAlbums",
|
||||||
|
"navigationTabsArtists",
|
||||||
|
"navigationTabsPlaylists",
|
||||||
|
"navigationTabsSongs",
|
||||||
"resourcesAlbumCount",
|
"resourcesAlbumCount",
|
||||||
"resourcesArtistCount",
|
"resourcesArtistCount",
|
||||||
"resourcesFilterAlbum",
|
"resourcesFilterAlbum",
|
||||||
@ -375,6 +426,10 @@
|
|||||||
"actionsDownloadDelete",
|
"actionsDownloadDelete",
|
||||||
"actionsOk",
|
"actionsOk",
|
||||||
"controlsShuffle",
|
"controlsShuffle",
|
||||||
|
"navigationTabsAlbums",
|
||||||
|
"navigationTabsArtists",
|
||||||
|
"navigationTabsPlaylists",
|
||||||
|
"navigationTabsSongs",
|
||||||
"resourcesAlbumCount",
|
"resourcesAlbumCount",
|
||||||
"resourcesArtistCount",
|
"resourcesArtistCount",
|
||||||
"resourcesFilterAlbum",
|
"resourcesFilterAlbum",
|
||||||
@ -401,6 +456,10 @@
|
|||||||
],
|
],
|
||||||
|
|
||||||
"pt": [
|
"pt": [
|
||||||
|
"navigationTabsAlbums",
|
||||||
|
"navigationTabsArtists",
|
||||||
|
"navigationTabsPlaylists",
|
||||||
|
"navigationTabsSongs",
|
||||||
"resourcesAlbumCount",
|
"resourcesAlbumCount",
|
||||||
"resourcesArtistCount",
|
"resourcesArtistCount",
|
||||||
"resourcesFilterOwner",
|
"resourcesFilterOwner",
|
||||||
@ -416,6 +475,13 @@
|
|||||||
"settingsServersFieldsName"
|
"settingsServersFieldsName"
|
||||||
],
|
],
|
||||||
|
|
||||||
|
"ru": [
|
||||||
|
"navigationTabsAlbums",
|
||||||
|
"navigationTabsArtists",
|
||||||
|
"navigationTabsPlaylists",
|
||||||
|
"navigationTabsSongs"
|
||||||
|
],
|
||||||
|
|
||||||
"tr": [
|
"tr": [
|
||||||
"actionsCancel",
|
"actionsCancel",
|
||||||
"actionsDelete",
|
"actionsDelete",
|
||||||
@ -424,6 +490,10 @@
|
|||||||
"actionsDownloadDelete",
|
"actionsDownloadDelete",
|
||||||
"actionsOk",
|
"actionsOk",
|
||||||
"controlsShuffle",
|
"controlsShuffle",
|
||||||
|
"navigationTabsAlbums",
|
||||||
|
"navigationTabsArtists",
|
||||||
|
"navigationTabsPlaylists",
|
||||||
|
"navigationTabsSongs",
|
||||||
"resourcesAlbumCount",
|
"resourcesAlbumCount",
|
||||||
"resourcesArtistCount",
|
"resourcesArtistCount",
|
||||||
"resourcesFilterAlbum",
|
"resourcesFilterAlbum",
|
||||||
@ -457,6 +527,10 @@
|
|||||||
"actionsDownloadDelete",
|
"actionsDownloadDelete",
|
||||||
"actionsOk",
|
"actionsOk",
|
||||||
"controlsShuffle",
|
"controlsShuffle",
|
||||||
|
"navigationTabsAlbums",
|
||||||
|
"navigationTabsArtists",
|
||||||
|
"navigationTabsPlaylists",
|
||||||
|
"navigationTabsSongs",
|
||||||
"resourcesAlbumCount",
|
"resourcesAlbumCount",
|
||||||
"resourcesArtistCount",
|
"resourcesArtistCount",
|
||||||
"resourcesFilterAlbum",
|
"resourcesFilterAlbum",
|
||||||
@ -484,6 +558,10 @@
|
|||||||
|
|
||||||
"zh": [
|
"zh": [
|
||||||
"controlsShuffle",
|
"controlsShuffle",
|
||||||
|
"navigationTabsAlbums",
|
||||||
|
"navigationTabsArtists",
|
||||||
|
"navigationTabsPlaylists",
|
||||||
|
"navigationTabsSongs",
|
||||||
"resourcesAlbumCount",
|
"resourcesAlbumCount",
|
||||||
"resourcesArtistCount",
|
"resourcesArtistCount",
|
||||||
"resourcesPlaylistCount",
|
"resourcesPlaylistCount",
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
import 'package:drift/drift.dart';
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_hooks/flutter_hooks.dart';
|
||||||
import 'package:go_router/go_router.dart';
|
import 'package:go_router/go_router.dart';
|
||||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||||
import 'package:infinite_scroll_pagination/infinite_scroll_pagination.dart';
|
import 'package:infinite_scroll_pagination/infinite_scroll_pagination.dart';
|
||||||
@ -7,7 +7,7 @@ import 'package:infinite_scroll_pagination/infinite_scroll_pagination.dart';
|
|||||||
import '../../sources/models.dart';
|
import '../../sources/models.dart';
|
||||||
import '../hooks/use_paging_controller.dart';
|
import '../hooks/use_paging_controller.dart';
|
||||||
import '../state/database.dart';
|
import '../state/database.dart';
|
||||||
import '../state/settings.dart';
|
import '../state/source.dart';
|
||||||
import 'list_items.dart';
|
import 'list_items.dart';
|
||||||
|
|
||||||
const kPageSize = 60;
|
const kPageSize = 60;
|
||||||
@ -23,15 +23,17 @@ class AlbumsGrid extends HookConsumerWidget {
|
|||||||
final controller = usePagingController<int, Album>(
|
final controller = usePagingController<int, Album>(
|
||||||
getNextPageKey: (state) =>
|
getNextPageKey: (state) =>
|
||||||
state.lastPageIsEmpty ? null : state.nextIntPageKey,
|
state.lastPageIsEmpty ? null : state.nextIntPageKey,
|
||||||
fetchPage: (pageKey) async {
|
fetchPage: (pageKey) => db.libraryDao.listAlbums(
|
||||||
final query = db.albums.select()
|
limit: kPageSize,
|
||||||
..where((f) => f.sourceId.equals(sourceId))
|
offset: (pageKey - 1) * kPageSize,
|
||||||
..limit(kPageSize, offset: (pageKey - 1) * kPageSize);
|
),
|
||||||
|
|
||||||
return await query.get();
|
|
||||||
},
|
|
||||||
);
|
);
|
||||||
|
|
||||||
|
useEffect(() {
|
||||||
|
controller.refresh();
|
||||||
|
return;
|
||||||
|
}, [sourceId]);
|
||||||
|
|
||||||
return PagingListener(
|
return PagingListener(
|
||||||
controller: controller,
|
controller: controller,
|
||||||
builder: (context, state, fetchNextPage) {
|
builder: (context, state, fetchNextPage) {
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
import 'package:drift/drift.dart';
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_hooks/flutter_hooks.dart';
|
||||||
import 'package:go_router/go_router.dart';
|
import 'package:go_router/go_router.dart';
|
||||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||||
import 'package:infinite_scroll_pagination/infinite_scroll_pagination.dart';
|
import 'package:infinite_scroll_pagination/infinite_scroll_pagination.dart';
|
||||||
@ -7,7 +7,7 @@ import 'package:infinite_scroll_pagination/infinite_scroll_pagination.dart';
|
|||||||
import '../../sources/models.dart';
|
import '../../sources/models.dart';
|
||||||
import '../hooks/use_paging_controller.dart';
|
import '../hooks/use_paging_controller.dart';
|
||||||
import '../state/database.dart';
|
import '../state/database.dart';
|
||||||
import '../state/settings.dart';
|
import '../state/source.dart';
|
||||||
import 'list_items.dart';
|
import 'list_items.dart';
|
||||||
|
|
||||||
const kPageSize = 30;
|
const kPageSize = 30;
|
||||||
@ -25,36 +25,17 @@ class ArtistsList extends HookConsumerWidget {
|
|||||||
final controller = usePagingController<int, _ArtistItem>(
|
final controller = usePagingController<int, _ArtistItem>(
|
||||||
getNextPageKey: (state) =>
|
getNextPageKey: (state) =>
|
||||||
state.lastPageIsEmpty ? null : state.nextIntPageKey,
|
state.lastPageIsEmpty ? null : state.nextIntPageKey,
|
||||||
fetchPage: (pageKey) async {
|
fetchPage: (pageKey) => db.libraryDao.listArtists(
|
||||||
final albumCount = db.albums.id.count();
|
limit: kPageSize,
|
||||||
|
offset: (pageKey - 1) * kPageSize,
|
||||||
final query =
|
|
||||||
db.artists.select().join([
|
|
||||||
leftOuterJoin(
|
|
||||||
db.albums,
|
|
||||||
db.albums.artistId.equalsExp(db.artists.id),
|
|
||||||
),
|
),
|
||||||
])
|
|
||||||
..addColumns([albumCount])
|
|
||||||
..where(
|
|
||||||
db.artists.sourceId.equals(sourceId) &
|
|
||||||
db.albums.sourceId.equals(sourceId),
|
|
||||||
)
|
|
||||||
..groupBy([db.artists.sourceId, db.artists.id])
|
|
||||||
..orderBy([OrderingTerm.asc(db.artists.name)])
|
|
||||||
..limit(kPageSize, offset: (pageKey - 1) * kPageSize);
|
|
||||||
|
|
||||||
return (await query.get())
|
|
||||||
.map(
|
|
||||||
(row) => (
|
|
||||||
artist: row.readTable(db.artists),
|
|
||||||
albumCount: row.read(albumCount),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
.toList();
|
|
||||||
},
|
|
||||||
);
|
);
|
||||||
|
|
||||||
|
useEffect(() {
|
||||||
|
controller.refresh();
|
||||||
|
return;
|
||||||
|
}, [sourceId]);
|
||||||
|
|
||||||
return PagingListener(
|
return PagingListener(
|
||||||
controller: controller,
|
controller: controller,
|
||||||
builder: (context, state, fetchNextPage) {
|
builder: (context, state, fetchNextPage) {
|
||||||
|
|||||||
@ -4,9 +4,11 @@ import 'package:go_router/go_router.dart';
|
|||||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||||
import 'package:material_symbols_icons/symbols.dart';
|
import 'package:material_symbols_icons/symbols.dart';
|
||||||
|
|
||||||
|
import '../../l10n/generated/app_localizations.dart';
|
||||||
import '../lists/albums_grid.dart';
|
import '../lists/albums_grid.dart';
|
||||||
import '../lists/artists_list.dart';
|
import '../lists/artists_list.dart';
|
||||||
import '../state/services.dart';
|
import '../state/services.dart';
|
||||||
|
import '../ui/text.dart';
|
||||||
import '../util/custom_scroll_fix.dart';
|
import '../util/custom_scroll_fix.dart';
|
||||||
|
|
||||||
const kIconSize = 26.0;
|
const kIconSize = 26.0;
|
||||||
@ -159,21 +161,26 @@ class TabTitleText extends HookConsumerWidget {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context, WidgetRef ref) {
|
Widget build(BuildContext context, WidgetRef ref) {
|
||||||
final theme = Theme.of(context);
|
final l = AppLocalizations.of(context);
|
||||||
final tabText = useState(LibraryTab.home.toString());
|
|
||||||
|
String tabLocalization(LibraryTab tab) => switch (tab) {
|
||||||
|
LibraryTab.albums => l.navigationTabsAlbums,
|
||||||
|
LibraryTab.home => l.navigationTabsHome,
|
||||||
|
LibraryTab.artists => l.navigationTabsArtists,
|
||||||
|
LibraryTab.songs => l.navigationTabsSongs,
|
||||||
|
LibraryTab.playlists => l.navigationTabsPlaylists,
|
||||||
|
};
|
||||||
|
|
||||||
|
final tabName = tabLocalization(LibraryTab.values[tabController.index]);
|
||||||
|
final tabText = useState(tabName);
|
||||||
|
|
||||||
useListenable(tabController);
|
useListenable(tabController);
|
||||||
useEffect(() {
|
useEffect(() {
|
||||||
tabText.value = LibraryTab.values[tabController.index].toString();
|
tabText.value = tabName;
|
||||||
return;
|
return;
|
||||||
}, [tabController.index]);
|
}, [tabName]);
|
||||||
|
|
||||||
return Text(
|
return TextH1(tabText.value);
|
||||||
tabText.value,
|
|
||||||
style: theme.textTheme.headlineLarge?.copyWith(
|
|
||||||
fontWeight: FontWeight.w800,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -13,7 +13,7 @@ class PreloadScreen extends HookConsumerWidget {
|
|||||||
Widget build(BuildContext context, WidgetRef ref) {
|
Widget build(BuildContext context, WidgetRef ref) {
|
||||||
final initializers = [
|
final initializers = [
|
||||||
ref.watch(databaseInitializer),
|
ref.watch(databaseInitializer),
|
||||||
ref.watch(sourceInitializer),
|
ref.watch(activeSourceInitializer),
|
||||||
ref.watch(packageInfoInitializer),
|
ref.watch(packageInfoInitializer),
|
||||||
];
|
];
|
||||||
|
|
||||||
|
|||||||
@ -1,9 +1,13 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_hooks/flutter_hooks.dart';
|
||||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||||
|
|
||||||
import '../../l10n/generated/app_localizations.dart';
|
import '../../l10n/generated/app_localizations.dart';
|
||||||
|
import '../state/database.dart';
|
||||||
|
import '../state/source.dart';
|
||||||
|
import '../ui/text.dart';
|
||||||
|
|
||||||
const kHorizontalPadding = 16.0;
|
const kHorizontalPadding = 18.0;
|
||||||
|
|
||||||
class SettingsScreen extends HookConsumerWidget {
|
class SettingsScreen extends HookConsumerWidget {
|
||||||
const SettingsScreen({super.key});
|
const SettingsScreen({super.key});
|
||||||
@ -13,11 +17,14 @@ class SettingsScreen extends HookConsumerWidget {
|
|||||||
final l = AppLocalizations.of(context);
|
final l = AppLocalizations.of(context);
|
||||||
|
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
|
appBar: AppBar(
|
||||||
|
title: TextH1(l.navigationTabsSettings),
|
||||||
|
),
|
||||||
body: ListView(
|
body: ListView(
|
||||||
children: [
|
children: [
|
||||||
const SizedBox(height: 96),
|
// const SizedBox(height: 96),
|
||||||
_SectionHeader(l.settingsServersName),
|
_SectionHeader(l.settingsServersName),
|
||||||
// const _Sources(),
|
const _Sources(),
|
||||||
// _SectionHeader(l.settingsNetworkName),
|
// _SectionHeader(l.settingsNetworkName),
|
||||||
// const _Network(),
|
// const _Network(),
|
||||||
// _SectionHeader(l.settingsAboutName),
|
// _SectionHeader(l.settingsAboutName),
|
||||||
@ -29,10 +36,10 @@ class SettingsScreen extends HookConsumerWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class _Section extends StatelessWidget {
|
class _Section extends StatelessWidget {
|
||||||
final List<Widget> children;
|
|
||||||
|
|
||||||
const _Section({required this.children});
|
const _Section({required this.children});
|
||||||
|
|
||||||
|
final List<Widget> children;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Column(
|
return Column(
|
||||||
@ -46,27 +53,22 @@ class _Section extends StatelessWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class _SectionHeader extends StatelessWidget {
|
class _SectionHeader extends StatelessWidget {
|
||||||
final String title;
|
|
||||||
|
|
||||||
const _SectionHeader(this.title);
|
const _SectionHeader(this.title);
|
||||||
|
|
||||||
|
final String title;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final theme = Theme.of(context);
|
|
||||||
|
|
||||||
return Column(
|
return Column(
|
||||||
children: [
|
children: [
|
||||||
|
const SizedBox(height: 16),
|
||||||
SizedBox(
|
SizedBox(
|
||||||
width: double.infinity,
|
width: double.infinity,
|
||||||
child: Padding(
|
child: Padding(
|
||||||
padding: const EdgeInsets.symmetric(horizontal: kHorizontalPadding),
|
padding: const EdgeInsets.symmetric(horizontal: kHorizontalPadding),
|
||||||
child: Text(
|
child: TextH2(title),
|
||||||
title,
|
|
||||||
style: theme.textTheme.displaySmall,
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
|
||||||
const SizedBox(height: 12),
|
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -346,79 +348,66 @@ class _SectionHeader extends StatelessWidget {
|
|||||||
// }
|
// }
|
||||||
// }
|
// }
|
||||||
|
|
||||||
// class _Sources extends HookConsumerWidget {
|
class _Sources extends HookConsumerWidget {
|
||||||
// const _Sources();
|
const _Sources();
|
||||||
|
|
||||||
// @override
|
@override
|
||||||
// Widget build(BuildContext context, WidgetRef ref) {
|
Widget build(BuildContext context, WidgetRef ref) {
|
||||||
// final sources = ref.watch(
|
final db = ref.watch(databaseProvider);
|
||||||
// settingsServiceProvider.select(
|
final activeSourceId = ref.watch(sourceIdProvider);
|
||||||
// (value) => value.sources,
|
final sources = useStream(db.sourcesDao.listSources()).data;
|
||||||
// ),
|
|
||||||
// );
|
|
||||||
// final activeSource = ref.watch(
|
|
||||||
// settingsServiceProvider.select(
|
|
||||||
// (value) => value.activeSource,
|
|
||||||
// ),
|
|
||||||
// );
|
|
||||||
|
|
||||||
// final l = AppLocalizations.of(context);
|
final l = AppLocalizations.of(context);
|
||||||
|
|
||||||
// return _Section(
|
if (sources == null) {
|
||||||
// children: [
|
return Container();
|
||||||
// for (var source in sources)
|
}
|
||||||
// RadioListTile<int>(
|
|
||||||
// value: source.id,
|
return _Section(
|
||||||
// groupValue: activeSource?.id,
|
children: [
|
||||||
// onChanged: (value) {
|
RadioGroup<int>(
|
||||||
// ref
|
groupValue: activeSourceId,
|
||||||
// .read(settingsServiceProvider.notifier)
|
onChanged: (value) {
|
||||||
// .setActiveSource(source.id);
|
if (value != null) {
|
||||||
// },
|
db.sourcesDao.setActiveSource(value);
|
||||||
// title: Text(source.name),
|
}
|
||||||
// subtitle: Text(
|
},
|
||||||
// source.address.toString(),
|
child: Column(
|
||||||
// maxLines: 1,
|
children: [
|
||||||
// softWrap: false,
|
for (final (source, settings) in sources)
|
||||||
// overflow: TextOverflow.fade,
|
RadioListTile<int>(
|
||||||
// ),
|
value: source.id,
|
||||||
// secondary: IconButton(
|
title: Text(source.name),
|
||||||
// icon: const Icon(Icons.edit_rounded),
|
subtitle: Text(
|
||||||
// onPressed: () {
|
settings.address.toString(),
|
||||||
|
maxLines: 1,
|
||||||
|
softWrap: false,
|
||||||
|
overflow: TextOverflow.fade,
|
||||||
|
),
|
||||||
|
secondary: IconButton(
|
||||||
|
icon: const Icon(Icons.edit_rounded),
|
||||||
|
onPressed: () {
|
||||||
// context.pushRoute(SourceRoute(id: source.id));
|
// context.pushRoute(SourceRoute(id: source.id));
|
||||||
// },
|
},
|
||||||
// ),
|
),
|
||||||
// ),
|
),
|
||||||
// const SizedBox(height: 8),
|
],
|
||||||
// Row(
|
),
|
||||||
// mainAxisAlignment: MainAxisAlignment.center,
|
),
|
||||||
// children: [
|
const SizedBox(height: 8),
|
||||||
// OutlinedButton.icon(
|
Row(
|
||||||
// icon: const Icon(Icons.add_rounded),
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
// label: Text(l.settingsServersActionsAdd),
|
children: [
|
||||||
// onPressed: () {
|
OutlinedButton.icon(
|
||||||
|
icon: const Icon(Icons.add_rounded),
|
||||||
|
label: Text(l.settingsServersActionsAdd),
|
||||||
|
onPressed: () {
|
||||||
// context.pushRoute(SourceRoute());
|
// context.pushRoute(SourceRoute());
|
||||||
// },
|
},
|
||||||
// ),
|
),
|
||||||
// ],
|
],
|
||||||
// ),
|
),
|
||||||
// // TODO: remove
|
],
|
||||||
// if (kDebugMode)
|
);
|
||||||
// Row(
|
}
|
||||||
// mainAxisAlignment: MainAxisAlignment.center,
|
}
|
||||||
// children: [
|
|
||||||
// OutlinedButton.icon(
|
|
||||||
// icon: const Icon(Icons.add_rounded),
|
|
||||||
// label: const Text('Add TEST'),
|
|
||||||
// onPressed: () {
|
|
||||||
// ref
|
|
||||||
// .read(settingsServiceProvider.notifier)
|
|
||||||
// .addTestSource('TEST');
|
|
||||||
// },
|
|
||||||
// ),
|
|
||||||
// ],
|
|
||||||
// ),
|
|
||||||
// ],
|
|
||||||
// );
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|||||||
@ -11,7 +11,8 @@ final databaseInitializer = FutureProvider<SubtracksDatabase>((ref) async {
|
|||||||
.insertOnConflictUpdate(
|
.insertOnConflictUpdate(
|
||||||
SourcesCompanion.insert(
|
SourcesCompanion.insert(
|
||||||
id: Value(1),
|
id: Value(1),
|
||||||
name: 'test navidrome',
|
name: 'test subsonic',
|
||||||
|
// isActive: Value(true),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
await db
|
await db
|
||||||
@ -22,9 +23,26 @@ final databaseInitializer = FutureProvider<SubtracksDatabase>((ref) async {
|
|||||||
address: Uri.parse('http://demo.subsonic.org'),
|
address: Uri.parse('http://demo.subsonic.org'),
|
||||||
username: 'guest1',
|
username: 'guest1',
|
||||||
password: 'guest',
|
password: 'guest',
|
||||||
// address: Uri.parse('http://10.0.2.2:4533'),
|
useTokenAuth: Value(true),
|
||||||
// username: 'admin',
|
),
|
||||||
// password: 'password',
|
);
|
||||||
|
await db
|
||||||
|
.into(db.sources)
|
||||||
|
.insertOnConflictUpdate(
|
||||||
|
SourcesCompanion.insert(
|
||||||
|
id: Value(2),
|
||||||
|
name: 'test navidrome',
|
||||||
|
// isActive: Value(null),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
await db
|
||||||
|
.into(db.subsonicSettings)
|
||||||
|
.insertOnConflictUpdate(
|
||||||
|
SubsonicSettingsCompanion.insert(
|
||||||
|
sourceId: Value(2),
|
||||||
|
address: Uri.parse('http://10.0.2.2:4533'),
|
||||||
|
username: 'admin',
|
||||||
|
password: 'password',
|
||||||
useTokenAuth: Value(true),
|
useTokenAuth: Value(true),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|||||||
@ -2,7 +2,6 @@ import 'package:hooks_riverpod/hooks_riverpod.dart';
|
|||||||
|
|
||||||
import '../../services/sync_service.dart';
|
import '../../services/sync_service.dart';
|
||||||
import 'database.dart';
|
import 'database.dart';
|
||||||
import 'settings.dart';
|
|
||||||
import 'source.dart';
|
import 'source.dart';
|
||||||
|
|
||||||
final syncServiceProvider = Provider<SyncService>((ref) {
|
final syncServiceProvider = Provider<SyncService>((ref) {
|
||||||
|
|||||||
@ -1,10 +1,6 @@
|
|||||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||||
import 'package:package_info_plus/package_info_plus.dart';
|
import 'package:package_info_plus/package_info_plus.dart';
|
||||||
|
|
||||||
final sourceIdProvider = Provider<int>((ref) {
|
|
||||||
return 1;
|
|
||||||
});
|
|
||||||
|
|
||||||
final packageInfoInitializer = FutureProvider<PackageInfo>((ref) {
|
final packageInfoInitializer = FutureProvider<PackageInfo>((ref) {
|
||||||
return PackageInfo.fromPlatform();
|
return PackageInfo.fromPlatform();
|
||||||
});
|
});
|
||||||
|
|||||||
@ -1,28 +1,27 @@
|
|||||||
import 'package:drift/drift.dart';
|
|
||||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||||
|
|
||||||
import '../../sources/music_source.dart';
|
|
||||||
import '../../sources/subsonic/client.dart';
|
import '../../sources/subsonic/client.dart';
|
||||||
import '../../sources/subsonic/source.dart';
|
import '../../sources/subsonic/source.dart';
|
||||||
import '../../util/http.dart';
|
import '../../util/http.dart';
|
||||||
import 'database.dart';
|
import 'database.dart';
|
||||||
import 'settings.dart';
|
|
||||||
|
|
||||||
final sourceInitializer = FutureProvider<MusicSource>((ref) async {
|
final activeSourceInitializer = StreamProvider<(int, SubsonicSource)>((
|
||||||
|
ref,
|
||||||
|
) async* {
|
||||||
final db = ref.watch(databaseProvider);
|
final db = ref.watch(databaseProvider);
|
||||||
final sourceId = ref.watch(sourceIdProvider);
|
|
||||||
|
|
||||||
final query = db.sources.select().join([
|
final activeSource = db.sourcesDao.activeSourceId().watchSingle();
|
||||||
leftOuterJoin(
|
|
||||||
db.subsonicSettings,
|
|
||||||
db.subsonicSettings.sourceId.equalsExp(db.sources.id),
|
|
||||||
),
|
|
||||||
])..where(db.sources.id.equals(sourceId));
|
|
||||||
|
|
||||||
final result = await query.getSingle();
|
await for (final source in activeSource) {
|
||||||
final subsonicSettings = result.readTable(db.subsonicSettings);
|
final sourceId = source.read(db.sources.id)!;
|
||||||
|
|
||||||
return SubsonicSource(
|
final subsonicSettings = await db.managers.subsonicSettings
|
||||||
|
.filter((f) => f.sourceId.equals(sourceId))
|
||||||
|
.getSingle();
|
||||||
|
|
||||||
|
yield (
|
||||||
|
sourceId,
|
||||||
|
SubsonicSource(
|
||||||
SubsonicClient(
|
SubsonicClient(
|
||||||
http: SubtracksHttpClient(),
|
http: SubtracksHttpClient(),
|
||||||
address: subsonicSettings.address,
|
address: subsonicSettings.address,
|
||||||
@ -30,9 +29,15 @@ final sourceInitializer = FutureProvider<MusicSource>((ref) async {
|
|||||||
password: subsonicSettings.password,
|
password: subsonicSettings.password,
|
||||||
useTokenAuth: subsonicSettings.useTokenAuth,
|
useTokenAuth: subsonicSettings.useTokenAuth,
|
||||||
),
|
),
|
||||||
|
),
|
||||||
);
|
);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
final sourceProvider = Provider<MusicSource>((ref) {
|
final sourceProvider = Provider<SubsonicSource>((ref) {
|
||||||
return ref.watch(sourceInitializer).value!;
|
return ref.watch(activeSourceInitializer).value!.$2;
|
||||||
|
});
|
||||||
|
|
||||||
|
final sourceIdProvider = Provider<int>((ref) {
|
||||||
|
return ref.watch(activeSourceInitializer).value!.$1;
|
||||||
});
|
});
|
||||||
|
|||||||
43
lib/app/ui/text.dart
Normal file
43
lib/app/ui/text.dart
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
|
class TextH1 extends StatelessWidget {
|
||||||
|
const TextH1(
|
||||||
|
this.data, {
|
||||||
|
super.key,
|
||||||
|
});
|
||||||
|
|
||||||
|
final String data;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
final theme = Theme.of(context);
|
||||||
|
|
||||||
|
return Text(
|
||||||
|
data,
|
||||||
|
style: theme.textTheme.headlineLarge?.copyWith(
|
||||||
|
fontWeight: FontWeight.w800,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class TextH2 extends StatelessWidget {
|
||||||
|
const TextH2(
|
||||||
|
this.data, {
|
||||||
|
super.key,
|
||||||
|
});
|
||||||
|
|
||||||
|
final String data;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
final theme = Theme.of(context);
|
||||||
|
|
||||||
|
return Text(
|
||||||
|
data,
|
||||||
|
style: theme.textTheme.headlineMedium?.copyWith(
|
||||||
|
fontWeight: FontWeight.w700,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
63
lib/database/dao/library_dao.dart
Normal file
63
lib/database/dao/library_dao.dart
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
import 'package:drift/drift.dart';
|
||||||
|
|
||||||
|
import '../../sources/models.dart' as models;
|
||||||
|
import '../database.dart';
|
||||||
|
|
||||||
|
part 'library_dao.g.dart';
|
||||||
|
|
||||||
|
@DriftAccessor(include: {'../tables.drift'})
|
||||||
|
class LibraryDao extends DatabaseAccessor<SubtracksDatabase>
|
||||||
|
with _$LibraryDaoMixin {
|
||||||
|
LibraryDao(super.db);
|
||||||
|
|
||||||
|
Future<List<models.Album>> listAlbums({
|
||||||
|
required int limit,
|
||||||
|
required int offset,
|
||||||
|
}) {
|
||||||
|
final query = albums.select()
|
||||||
|
..where(
|
||||||
|
(f) => f.sourceId.equalsExp(
|
||||||
|
subqueryExpression(db.sourcesDao.activeSourceId()),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
..limit(limit, offset: offset);
|
||||||
|
|
||||||
|
return query.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<List<({models.Artist artist, int albumCount})>> listArtists({
|
||||||
|
required int limit,
|
||||||
|
required int offset,
|
||||||
|
}) async {
|
||||||
|
final albumCount = albums.id.count();
|
||||||
|
|
||||||
|
final query =
|
||||||
|
artists.select().join([
|
||||||
|
leftOuterJoin(
|
||||||
|
albums,
|
||||||
|
albums.artistId.equalsExp(artists.id),
|
||||||
|
),
|
||||||
|
])
|
||||||
|
..addColumns([albumCount])
|
||||||
|
..where(
|
||||||
|
artists.sourceId.equalsExp(
|
||||||
|
subqueryExpression(db.sourcesDao.activeSourceId()),
|
||||||
|
) &
|
||||||
|
albums.sourceId.equalsExp(
|
||||||
|
subqueryExpression(db.sourcesDao.activeSourceId()),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
..groupBy([artists.sourceId, artists.id])
|
||||||
|
..orderBy([OrderingTerm.asc(artists.name)])
|
||||||
|
..limit(limit, offset: offset);
|
||||||
|
|
||||||
|
return (await query.get())
|
||||||
|
.map(
|
||||||
|
(row) => (
|
||||||
|
artist: row.readTable(artists),
|
||||||
|
albumCount: row.read(albumCount) ?? 0,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
.toList();
|
||||||
|
}
|
||||||
|
}
|
||||||
14
lib/database/dao/library_dao.g.dart
Normal file
14
lib/database/dao/library_dao.g.dart
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||||
|
|
||||||
|
part of 'library_dao.dart';
|
||||||
|
|
||||||
|
// ignore_for_file: type=lint
|
||||||
|
mixin _$LibraryDaoMixin on DatabaseAccessor<SubtracksDatabase> {
|
||||||
|
Sources get sources => attachedDatabase.sources;
|
||||||
|
SubsonicSettings get subsonicSettings => attachedDatabase.subsonicSettings;
|
||||||
|
Artists get artists => attachedDatabase.artists;
|
||||||
|
Albums get albums => attachedDatabase.albums;
|
||||||
|
Playlists get playlists => attachedDatabase.playlists;
|
||||||
|
PlaylistSongs get playlistSongs => attachedDatabase.playlistSongs;
|
||||||
|
Songs get songs => attachedDatabase.songs;
|
||||||
|
}
|
||||||
46
lib/database/dao/sources_dao.dart
Normal file
46
lib/database/dao/sources_dao.dart
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
import 'package:drift/drift.dart';
|
||||||
|
|
||||||
|
import '../database.dart';
|
||||||
|
|
||||||
|
part 'sources_dao.g.dart';
|
||||||
|
|
||||||
|
@DriftAccessor(include: {'../tables.drift'})
|
||||||
|
class SourcesDao extends DatabaseAccessor<SubtracksDatabase>
|
||||||
|
with _$SourcesDaoMixin {
|
||||||
|
SourcesDao(super.db);
|
||||||
|
|
||||||
|
JoinedSelectStatement<Sources, Source> activeSourceId() {
|
||||||
|
return selectOnly(sources)
|
||||||
|
..addColumns([sources.id])
|
||||||
|
..where(sources.isActive.equals(true));
|
||||||
|
}
|
||||||
|
|
||||||
|
Stream<List<(Source, SubsonicSetting)>> listSources() {
|
||||||
|
final query = select(sources).join([
|
||||||
|
innerJoin(
|
||||||
|
subsonicSettings,
|
||||||
|
sources.id.equalsExp(subsonicSettings.sourceId),
|
||||||
|
),
|
||||||
|
]);
|
||||||
|
|
||||||
|
return query.watch().map(
|
||||||
|
(rows) => rows
|
||||||
|
.map(
|
||||||
|
(row) => (
|
||||||
|
row.readTable(sources),
|
||||||
|
row.readTable(subsonicSettings),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
.toList(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> setActiveSource(int id) async {
|
||||||
|
await transaction(() async {
|
||||||
|
await db.managers.sources.update((o) => o(isActive: Value(null)));
|
||||||
|
await db.managers.sources
|
||||||
|
.filter((f) => f.id.equals(id))
|
||||||
|
.update((o) => o(isActive: Value(true)));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
14
lib/database/dao/sources_dao.g.dart
Normal file
14
lib/database/dao/sources_dao.g.dart
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||||
|
|
||||||
|
part of 'sources_dao.dart';
|
||||||
|
|
||||||
|
// ignore_for_file: type=lint
|
||||||
|
mixin _$SourcesDaoMixin on DatabaseAccessor<SubtracksDatabase> {
|
||||||
|
Sources get sources => attachedDatabase.sources;
|
||||||
|
SubsonicSettings get subsonicSettings => attachedDatabase.subsonicSettings;
|
||||||
|
Artists get artists => attachedDatabase.artists;
|
||||||
|
Albums get albums => attachedDatabase.albums;
|
||||||
|
Playlists get playlists => attachedDatabase.playlists;
|
||||||
|
PlaylistSongs get playlistSongs => attachedDatabase.playlistSongs;
|
||||||
|
Songs get songs => attachedDatabase.songs;
|
||||||
|
}
|
||||||
@ -5,6 +5,8 @@ import 'package:path_provider/path_provider.dart';
|
|||||||
|
|
||||||
import '../sources/models.dart' as models;
|
import '../sources/models.dart' as models;
|
||||||
import 'converters.dart';
|
import 'converters.dart';
|
||||||
|
import 'dao/library_dao.dart';
|
||||||
|
import 'dao/sources_dao.dart';
|
||||||
|
|
||||||
part 'database.g.dart';
|
part 'database.g.dart';
|
||||||
|
|
||||||
@ -12,7 +14,13 @@ part 'database.g.dart';
|
|||||||
// https://www.sqlite.org/limits.html
|
// https://www.sqlite.org/limits.html
|
||||||
const kSqliteMaxVariableNumber = 32766;
|
const kSqliteMaxVariableNumber = 32766;
|
||||||
|
|
||||||
@DriftDatabase(include: {'tables.drift'})
|
@DriftDatabase(
|
||||||
|
include: {'tables.drift'},
|
||||||
|
daos: [
|
||||||
|
SourcesDao,
|
||||||
|
LibraryDao,
|
||||||
|
],
|
||||||
|
)
|
||||||
class SubtracksDatabase extends _$SubtracksDatabase {
|
class SubtracksDatabase extends _$SubtracksDatabase {
|
||||||
SubtracksDatabase([QueryExecutor? executor])
|
SubtracksDatabase([QueryExecutor? executor])
|
||||||
: super(executor ?? _openConnection());
|
: super(executor ?? _openConnection());
|
||||||
@ -40,395 +48,6 @@ class SubtracksDatabase extends _$SubtracksDatabase {
|
|||||||
},
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// MultiSelectable<Album> albumsList(int sourceId, ListQuery opt) {
|
|
||||||
// return filterAlbums(
|
|
||||||
// (_) => _filterPredicate('albums', sourceId, opt),
|
|
||||||
// (_) => _filterOrderBy(opt),
|
|
||||||
// (_) => _filterLimit(opt),
|
|
||||||
// );
|
|
||||||
// }
|
|
||||||
|
|
||||||
// MultiSelectable<Album> albumsListDownloaded(int sourceId, ListQuery opt) {
|
|
||||||
// return filterAlbumsDownloaded(
|
|
||||||
// (_, __) => _filterPredicate('albums', sourceId, opt),
|
|
||||||
// (_, __) => _filterOrderBy(opt),
|
|
||||||
// (_, __) => _filterLimit(opt),
|
|
||||||
// );
|
|
||||||
// }
|
|
||||||
|
|
||||||
// MultiSelectable<Artist> artistsList(int sourceId, ListQuery opt) {
|
|
||||||
// return filterArtists(
|
|
||||||
// (_) => _filterPredicate('artists', sourceId, opt),
|
|
||||||
// (_) => _filterOrderBy(opt),
|
|
||||||
// (_) => _filterLimit(opt),
|
|
||||||
// );
|
|
||||||
// }
|
|
||||||
|
|
||||||
// MultiSelectable<Artist> artistsListDownloaded(int sourceId, ListQuery opt) {
|
|
||||||
// return filterArtistsDownloaded(
|
|
||||||
// (_, __, ___) => _filterPredicate('artists', sourceId, opt),
|
|
||||||
// (_, __, ___) => _filterOrderBy(opt),
|
|
||||||
// (_, __, ___) => _filterLimit(opt),
|
|
||||||
// );
|
|
||||||
// }
|
|
||||||
|
|
||||||
// MultiSelectable<Playlist> playlistsList(int sourceId, ListQuery opt) {
|
|
||||||
// return filterPlaylists(
|
|
||||||
// (_) => _filterPredicate('playlists', sourceId, opt),
|
|
||||||
// (_) => _filterOrderBy(opt),
|
|
||||||
// (_) => _filterLimit(opt),
|
|
||||||
// );
|
|
||||||
// }
|
|
||||||
|
|
||||||
// MultiSelectable<Playlist> playlistsListDownloaded(
|
|
||||||
// int sourceId,
|
|
||||||
// ListQuery opt,
|
|
||||||
// ) {
|
|
||||||
// return filterPlaylistsDownloaded(
|
|
||||||
// (_, __, ___) => _filterPredicate('playlists', sourceId, opt),
|
|
||||||
// (_, __, ___) => _filterOrderBy(opt),
|
|
||||||
// (_, __, ___) => _filterLimit(opt),
|
|
||||||
// );
|
|
||||||
// }
|
|
||||||
|
|
||||||
// MultiSelectable<Song> songsList(int sourceId, ListQuery opt) {
|
|
||||||
// return filterSongs(
|
|
||||||
// (_) => _filterPredicate('songs', sourceId, opt),
|
|
||||||
// (_) => _filterOrderBy(opt),
|
|
||||||
// (_) => _filterLimit(opt),
|
|
||||||
// );
|
|
||||||
// }
|
|
||||||
|
|
||||||
// MultiSelectable<Song> songsListDownloaded(int sourceId, ListQuery opt) {
|
|
||||||
// return filterSongsDownloaded(
|
|
||||||
// (_) => _filterPredicate('songs', sourceId, opt),
|
|
||||||
// (_) => _filterOrderBy(opt),
|
|
||||||
// (_) => _filterLimit(opt),
|
|
||||||
// );
|
|
||||||
// }
|
|
||||||
|
|
||||||
// Expression<bool> _filterPredicate(String table, int sourceId, ListQuery opt) {
|
|
||||||
// return opt.filters
|
|
||||||
// .map((filter) => buildFilter<bool>(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<Song> albumSongsList(SourceId sid, ListQuery opt) {
|
|
||||||
// return listQuery(
|
|
||||||
// select(songs)..where(
|
|
||||||
// (tbl) => tbl.sourceId.equals(sid.sourceId) & tbl.albumId.equals(sid.id),
|
|
||||||
// ),
|
|
||||||
// opt,
|
|
||||||
// );
|
|
||||||
// }
|
|
||||||
|
|
||||||
// MultiSelectable<Song> songsByAlbumList(int sourceId, ListQuery opt) {
|
|
||||||
// return filterSongsByGenre(
|
|
||||||
// (_, __) => _filterPredicate('songs', sourceId, opt),
|
|
||||||
// (_, __) => _filterOrderBy(opt),
|
|
||||||
// (_, __) => _filterLimit(opt),
|
|
||||||
// );
|
|
||||||
// }
|
|
||||||
|
|
||||||
// MultiSelectable<Song> 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<void> saveArtists(Iterable<ArtistsCompanion> artists) async {
|
|
||||||
// await batch((batch) {
|
|
||||||
// batch.insertAllOnConflictUpdate(this.artists, artists);
|
|
||||||
// });
|
|
||||||
// }
|
|
||||||
|
|
||||||
// Future<void> deleteArtistsNotIn(int sourceId, Set<String> 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<void> saveAlbums(Iterable<AlbumsCompanion> albums) async {
|
|
||||||
// await batch((batch) {
|
|
||||||
// batch.insertAllOnConflictUpdate(this.albums, albums);
|
|
||||||
// });
|
|
||||||
// }
|
|
||||||
|
|
||||||
// Future<void> deleteAlbumsNotIn(int sourceId, Set<String> 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<void> savePlaylists(
|
|
||||||
// Iterable<PlaylistWithSongsCompanion> 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<void> deletePlaylistsNotIn(int sourceId, Set<String> 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<void> savePlaylistSongs(
|
|
||||||
// int sourceId,
|
|
||||||
// List<String> ids,
|
|
||||||
// Iterable<PlaylistSongsCompanion> 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<void> saveSongs(Iterable<SongsCompanion> songs) async {
|
|
||||||
// await batch((batch) {
|
|
||||||
// batch.insertAllOnConflictUpdate(this.songs, songs);
|
|
||||||
// });
|
|
||||||
// }
|
|
||||||
|
|
||||||
// Future<void> deleteSongsNotIn(int sourceId, Set<String> 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<LastBottomNavStateData> getLastBottomNavState() {
|
|
||||||
// return select(lastBottomNavState)..where((tbl) => tbl.id.equals(1));
|
|
||||||
// }
|
|
||||||
|
|
||||||
// Future<void> saveLastBottomNavState(LastBottomNavStateData update) {
|
|
||||||
// return into(lastBottomNavState).insertOnConflictUpdate(update);
|
|
||||||
// }
|
|
||||||
|
|
||||||
// Selectable<LastLibraryStateData> getLastLibraryState() {
|
|
||||||
// return select(lastLibraryState)..where((tbl) => tbl.id.equals(1));
|
|
||||||
// }
|
|
||||||
|
|
||||||
// Future<void> saveLastLibraryState(LastLibraryStateData update) {
|
|
||||||
// return into(lastLibraryState).insertOnConflictUpdate(update);
|
|
||||||
// }
|
|
||||||
|
|
||||||
// Selectable<LastAudioStateData> getLastAudioState() {
|
|
||||||
// return select(lastAudioState)..where((tbl) => tbl.id.equals(1));
|
|
||||||
// }
|
|
||||||
|
|
||||||
// Future<void> saveLastAudioState(LastAudioStateCompanion update) {
|
|
||||||
// return into(lastAudioState).insertOnConflictUpdate(update);
|
|
||||||
// }
|
|
||||||
|
|
||||||
// Future<void> insertQueue(Iterable<QueueCompanion> songs) async {
|
|
||||||
// await batch((batch) {
|
|
||||||
// batch.insertAll(queue, songs);
|
|
||||||
// });
|
|
||||||
// }
|
|
||||||
|
|
||||||
// Future<void> clearQueue() async {
|
|
||||||
// await delete(queue).go();
|
|
||||||
// }
|
|
||||||
|
|
||||||
// Future<void> 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<void> 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<void> updateSource(SubsonicSettings source) async {
|
|
||||||
// await transaction(() async {
|
|
||||||
// await into(sources).insertOnConflictUpdate(source.toSourceInsertable());
|
|
||||||
// await into(
|
|
||||||
// subsonicSources,
|
|
||||||
// ).insertOnConflictUpdate(source.toSubsonicInsertable());
|
|
||||||
// });
|
|
||||||
// }
|
|
||||||
|
|
||||||
// Future<void> 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<void> 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<void> updateSettings(AppSettingsCompanion settings) async {
|
|
||||||
// await into(appSettings).insertOnConflictUpdate(settings);
|
|
||||||
// }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
extension ArtistToDb on models.Artist {
|
extension ArtistToDb on models.Artist {
|
||||||
@ -495,252 +114,3 @@ extension PlaylistSongToDb on models.PlaylistSong {
|
|||||||
position: position,
|
position: position,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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<T, R> listQuery<T extends HasResultSet, R>(
|
|
||||||
// SimpleSelectStatement<T, R> 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<T, R> listQueryJoined<T extends HasResultSet, R>(
|
|
||||||
// JoinedSelectStatement<T, R> 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<T> buildFilter<T extends Object>(
|
|
||||||
// FilterWith filter,
|
|
||||||
// ) {
|
|
||||||
// return filter.when(
|
|
||||||
// equals: (column, value, invert) => CustomExpression<T>(
|
|
||||||
// '$column ${invert ? '<>' : '='} \'$value\'',
|
|
||||||
// ),
|
|
||||||
// greaterThan: (column, value, orEquals) => CustomExpression<T>(
|
|
||||||
// '$column ${orEquals ? '>=' : '>'} $value',
|
|
||||||
// ),
|
|
||||||
// isNull: (column, invert) => CustomExpression<T>(
|
|
||||||
// '$column ${invert ? 'IS NOT' : 'IS'} NULL',
|
|
||||||
// ),
|
|
||||||
// betweenInt: (column, from, to) => CustomExpression<T>(
|
|
||||||
// '$column BETWEEN $from AND $to',
|
|
||||||
// ),
|
|
||||||
// isIn: (column, invert, values) => CustomExpression<T>(
|
|
||||||
// '$column ${invert ? 'NOT IN' : 'IN'} (${values.join(',')})',
|
|
||||||
// ),
|
|
||||||
// );
|
|
||||||
// }
|
|
||||||
|
|
||||||
// class AlbumSongsCompanion {
|
|
||||||
// final AlbumsCompanion album;
|
|
||||||
// final Iterable<SongsCompanion> songs;
|
|
||||||
|
|
||||||
// AlbumSongsCompanion(this.album, this.songs);
|
|
||||||
// }
|
|
||||||
|
|
||||||
// class ArtistAlbumsCompanion {
|
|
||||||
// final ArtistsCompanion artist;
|
|
||||||
// final Iterable<AlbumsCompanion> albums;
|
|
||||||
|
|
||||||
// ArtistAlbumsCompanion(this.artist, this.albums);
|
|
||||||
// }
|
|
||||||
|
|
||||||
// class PlaylistWithSongsCompanion {
|
|
||||||
// final PlaylistsCompanion playist;
|
|
||||||
// final Iterable<PlaylistSongsCompanion> songs;
|
|
||||||
|
|
||||||
// PlaylistWithSongsCompanion(this.playist, this.songs);
|
|
||||||
// }
|
|
||||||
|
|
||||||
// Future<void> 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<void> 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<void> 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),
|
|
||||||
// );
|
|
||||||
// });
|
|
||||||
// });
|
|
||||||
// }
|
|
||||||
|
|||||||
@ -2454,6 +2454,8 @@ abstract class _$SubtracksDatabase extends GeneratedDatabase {
|
|||||||
'songs_source_id_artist_id_idx',
|
'songs_source_id_artist_id_idx',
|
||||||
'CREATE INDEX songs_source_id_artist_id_idx ON songs (source_id, artist_id)',
|
'CREATE INDEX songs_source_id_artist_id_idx ON songs (source_id, artist_id)',
|
||||||
);
|
);
|
||||||
|
late final SourcesDao sourcesDao = SourcesDao(this as SubtracksDatabase);
|
||||||
|
late final LibraryDao libraryDao = LibraryDao(this as SubtracksDatabase);
|
||||||
@override
|
@override
|
||||||
Iterable<TableInfo<Table, Object?>> get allTables =>
|
Iterable<TableInfo<Table, Object?>> get allTables =>
|
||||||
allSchemaEntities.whereType<TableInfo<Table, Object?>>();
|
allSchemaEntities.whereType<TableInfo<Table, Object?>>();
|
||||||
|
|||||||
@ -1,4 +1,5 @@
|
|||||||
{
|
{
|
||||||
|
"@@locale": "en",
|
||||||
"actionsCancel": "Cancel",
|
"actionsCancel": "Cancel",
|
||||||
"@actionsCancel": {},
|
"@actionsCancel": {},
|
||||||
"actionsDelete": "Delete",
|
"actionsDelete": "Delete",
|
||||||
@ -21,6 +22,14 @@
|
|||||||
"@messagesNothingHere": {},
|
"@messagesNothingHere": {},
|
||||||
"navigationTabsHome": "Home",
|
"navigationTabsHome": "Home",
|
||||||
"@navigationTabsHome": {},
|
"@navigationTabsHome": {},
|
||||||
|
"navigationTabsAlbums": "Albums",
|
||||||
|
"@navigationTabsAlbums": {},
|
||||||
|
"navigationTabsArtists": "Artists",
|
||||||
|
"@navigationTabsArtists": {},
|
||||||
|
"navigationTabsPlaylists": "Playlists",
|
||||||
|
"@navigationTabsPlaylists": {},
|
||||||
|
"navigationTabsSongs": "Songs",
|
||||||
|
"@navigationTabsSongs": {},
|
||||||
"navigationTabsLibrary": "Library",
|
"navigationTabsLibrary": "Library",
|
||||||
"@navigationTabsLibrary": {},
|
"@navigationTabsLibrary": {},
|
||||||
"navigationTabsSearch": "Search",
|
"navigationTabsSearch": "Search",
|
||||||
|
|||||||
@ -198,6 +198,30 @@ abstract class AppLocalizations {
|
|||||||
/// **'Home'**
|
/// **'Home'**
|
||||||
String get navigationTabsHome;
|
String get navigationTabsHome;
|
||||||
|
|
||||||
|
/// No description provided for @navigationTabsAlbums.
|
||||||
|
///
|
||||||
|
/// In en, this message translates to:
|
||||||
|
/// **'Albums'**
|
||||||
|
String get navigationTabsAlbums;
|
||||||
|
|
||||||
|
/// No description provided for @navigationTabsArtists.
|
||||||
|
///
|
||||||
|
/// In en, this message translates to:
|
||||||
|
/// **'Artists'**
|
||||||
|
String get navigationTabsArtists;
|
||||||
|
|
||||||
|
/// No description provided for @navigationTabsPlaylists.
|
||||||
|
///
|
||||||
|
/// In en, this message translates to:
|
||||||
|
/// **'Playlists'**
|
||||||
|
String get navigationTabsPlaylists;
|
||||||
|
|
||||||
|
/// No description provided for @navigationTabsSongs.
|
||||||
|
///
|
||||||
|
/// In en, this message translates to:
|
||||||
|
/// **'Songs'**
|
||||||
|
String get navigationTabsSongs;
|
||||||
|
|
||||||
/// No description provided for @navigationTabsLibrary.
|
/// No description provided for @navigationTabsLibrary.
|
||||||
///
|
///
|
||||||
/// In en, this message translates to:
|
/// In en, this message translates to:
|
||||||
|
|||||||
@ -41,6 +41,18 @@ class AppLocalizationsAr extends AppLocalizations {
|
|||||||
@override
|
@override
|
||||||
String get navigationTabsHome => 'الرئيسية';
|
String get navigationTabsHome => 'الرئيسية';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get navigationTabsAlbums => 'Albums';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get navigationTabsArtists => 'Artists';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get navigationTabsPlaylists => 'Playlists';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get navigationTabsSongs => 'Songs';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get navigationTabsLibrary => 'المكتبة';
|
String get navigationTabsLibrary => 'المكتبة';
|
||||||
|
|
||||||
|
|||||||
@ -41,6 +41,18 @@ class AppLocalizationsCa extends AppLocalizations {
|
|||||||
@override
|
@override
|
||||||
String get navigationTabsHome => 'Inici';
|
String get navigationTabsHome => 'Inici';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get navigationTabsAlbums => 'Albums';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get navigationTabsArtists => 'Artists';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get navigationTabsPlaylists => 'Playlists';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get navigationTabsSongs => 'Songs';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get navigationTabsLibrary => 'Biblioteca';
|
String get navigationTabsLibrary => 'Biblioteca';
|
||||||
|
|
||||||
|
|||||||
@ -41,6 +41,18 @@ class AppLocalizationsCs extends AppLocalizations {
|
|||||||
@override
|
@override
|
||||||
String get navigationTabsHome => 'Domů';
|
String get navigationTabsHome => 'Domů';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get navigationTabsAlbums => 'Albums';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get navigationTabsArtists => 'Artists';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get navigationTabsPlaylists => 'Playlists';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get navigationTabsSongs => 'Songs';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get navigationTabsLibrary => 'Knihovna';
|
String get navigationTabsLibrary => 'Knihovna';
|
||||||
|
|
||||||
|
|||||||
@ -41,6 +41,18 @@ class AppLocalizationsDa extends AppLocalizations {
|
|||||||
@override
|
@override
|
||||||
String get navigationTabsHome => 'Hjem';
|
String get navigationTabsHome => 'Hjem';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get navigationTabsAlbums => 'Albums';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get navigationTabsArtists => 'Artists';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get navigationTabsPlaylists => 'Playlists';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get navigationTabsSongs => 'Songs';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get navigationTabsLibrary => 'Bibliotek';
|
String get navigationTabsLibrary => 'Bibliotek';
|
||||||
|
|
||||||
|
|||||||
@ -41,6 +41,18 @@ class AppLocalizationsDe extends AppLocalizations {
|
|||||||
@override
|
@override
|
||||||
String get navigationTabsHome => 'Startseite';
|
String get navigationTabsHome => 'Startseite';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get navigationTabsAlbums => 'Albums';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get navigationTabsArtists => 'Artists';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get navigationTabsPlaylists => 'Playlists';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get navigationTabsSongs => 'Songs';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get navigationTabsLibrary => 'Bibliothek';
|
String get navigationTabsLibrary => 'Bibliothek';
|
||||||
|
|
||||||
|
|||||||
@ -41,6 +41,18 @@ class AppLocalizationsEn extends AppLocalizations {
|
|||||||
@override
|
@override
|
||||||
String get navigationTabsHome => 'Home';
|
String get navigationTabsHome => 'Home';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get navigationTabsAlbums => 'Albums';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get navigationTabsArtists => 'Artists';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get navigationTabsPlaylists => 'Playlists';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get navigationTabsSongs => 'Songs';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get navigationTabsLibrary => 'Library';
|
String get navigationTabsLibrary => 'Library';
|
||||||
|
|
||||||
|
|||||||
@ -41,6 +41,18 @@ class AppLocalizationsEs extends AppLocalizations {
|
|||||||
@override
|
@override
|
||||||
String get navigationTabsHome => 'Casa';
|
String get navigationTabsHome => 'Casa';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get navigationTabsAlbums => 'Albums';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get navigationTabsArtists => 'Artists';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get navigationTabsPlaylists => 'Playlists';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get navigationTabsSongs => 'Songs';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get navigationTabsLibrary => 'Biblioteca';
|
String get navigationTabsLibrary => 'Biblioteca';
|
||||||
|
|
||||||
|
|||||||
@ -41,6 +41,18 @@ class AppLocalizationsFr extends AppLocalizations {
|
|||||||
@override
|
@override
|
||||||
String get navigationTabsHome => 'Accueil';
|
String get navigationTabsHome => 'Accueil';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get navigationTabsAlbums => 'Albums';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get navigationTabsArtists => 'Artists';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get navigationTabsPlaylists => 'Playlists';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get navigationTabsSongs => 'Songs';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get navigationTabsLibrary => 'Bibliothèque';
|
String get navigationTabsLibrary => 'Bibliothèque';
|
||||||
|
|
||||||
|
|||||||
@ -41,6 +41,18 @@ class AppLocalizationsGl extends AppLocalizations {
|
|||||||
@override
|
@override
|
||||||
String get navigationTabsHome => 'Inicio';
|
String get navigationTabsHome => 'Inicio';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get navigationTabsAlbums => 'Albums';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get navigationTabsArtists => 'Artists';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get navigationTabsPlaylists => 'Playlists';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get navigationTabsSongs => 'Songs';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get navigationTabsLibrary => 'Biblioteca';
|
String get navigationTabsLibrary => 'Biblioteca';
|
||||||
|
|
||||||
|
|||||||
@ -41,6 +41,18 @@ class AppLocalizationsIt extends AppLocalizations {
|
|||||||
@override
|
@override
|
||||||
String get navigationTabsHome => 'Home';
|
String get navigationTabsHome => 'Home';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get navigationTabsAlbums => 'Albums';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get navigationTabsArtists => 'Artists';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get navigationTabsPlaylists => 'Playlists';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get navigationTabsSongs => 'Songs';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get navigationTabsLibrary => 'Libreria';
|
String get navigationTabsLibrary => 'Libreria';
|
||||||
|
|
||||||
|
|||||||
@ -41,6 +41,18 @@ class AppLocalizationsJa extends AppLocalizations {
|
|||||||
@override
|
@override
|
||||||
String get navigationTabsHome => 'ホーム';
|
String get navigationTabsHome => 'ホーム';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get navigationTabsAlbums => 'Albums';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get navigationTabsArtists => 'Artists';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get navigationTabsPlaylists => 'Playlists';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get navigationTabsSongs => 'Songs';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get navigationTabsLibrary => 'ライブラリ';
|
String get navigationTabsLibrary => 'ライブラリ';
|
||||||
|
|
||||||
|
|||||||
@ -41,6 +41,18 @@ class AppLocalizationsNb extends AppLocalizations {
|
|||||||
@override
|
@override
|
||||||
String get navigationTabsHome => 'Hjem';
|
String get navigationTabsHome => 'Hjem';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get navigationTabsAlbums => 'Albums';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get navigationTabsArtists => 'Artists';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get navigationTabsPlaylists => 'Playlists';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get navigationTabsSongs => 'Songs';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get navigationTabsLibrary => 'Bibliotek';
|
String get navigationTabsLibrary => 'Bibliotek';
|
||||||
|
|
||||||
|
|||||||
@ -41,6 +41,18 @@ class AppLocalizationsPa extends AppLocalizations {
|
|||||||
@override
|
@override
|
||||||
String get navigationTabsHome => 'ਘਰ';
|
String get navigationTabsHome => 'ਘਰ';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get navigationTabsAlbums => 'Albums';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get navigationTabsArtists => 'Artists';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get navigationTabsPlaylists => 'Playlists';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get navigationTabsSongs => 'Songs';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get navigationTabsLibrary => 'ਲਾਇਬ੍ਰੇਰੀ';
|
String get navigationTabsLibrary => 'ਲਾਇਬ੍ਰੇਰੀ';
|
||||||
|
|
||||||
|
|||||||
@ -41,6 +41,18 @@ class AppLocalizationsPl extends AppLocalizations {
|
|||||||
@override
|
@override
|
||||||
String get navigationTabsHome => 'Strona główna';
|
String get navigationTabsHome => 'Strona główna';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get navigationTabsAlbums => 'Albums';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get navigationTabsArtists => 'Artists';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get navigationTabsPlaylists => 'Playlists';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get navigationTabsSongs => 'Songs';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get navigationTabsLibrary => 'Kolekcja';
|
String get navigationTabsLibrary => 'Kolekcja';
|
||||||
|
|
||||||
|
|||||||
@ -41,6 +41,18 @@ class AppLocalizationsPt extends AppLocalizations {
|
|||||||
@override
|
@override
|
||||||
String get navigationTabsHome => 'Início';
|
String get navigationTabsHome => 'Início';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get navigationTabsAlbums => 'Albums';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get navigationTabsArtists => 'Artists';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get navigationTabsPlaylists => 'Playlists';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get navigationTabsSongs => 'Songs';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get navigationTabsLibrary => 'Biblioteca';
|
String get navigationTabsLibrary => 'Biblioteca';
|
||||||
|
|
||||||
|
|||||||
@ -41,6 +41,18 @@ class AppLocalizationsRu extends AppLocalizations {
|
|||||||
@override
|
@override
|
||||||
String get navigationTabsHome => 'Главная';
|
String get navigationTabsHome => 'Главная';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get navigationTabsAlbums => 'Albums';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get navigationTabsArtists => 'Artists';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get navigationTabsPlaylists => 'Playlists';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get navigationTabsSongs => 'Songs';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get navigationTabsLibrary => 'Библиотека';
|
String get navigationTabsLibrary => 'Библиотека';
|
||||||
|
|
||||||
|
|||||||
@ -41,6 +41,18 @@ class AppLocalizationsTr extends AppLocalizations {
|
|||||||
@override
|
@override
|
||||||
String get navigationTabsHome => 'Giriş';
|
String get navigationTabsHome => 'Giriş';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get navigationTabsAlbums => 'Albums';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get navigationTabsArtists => 'Artists';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get navigationTabsPlaylists => 'Playlists';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get navigationTabsSongs => 'Songs';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get navigationTabsLibrary => 'Kütüphane';
|
String get navigationTabsLibrary => 'Kütüphane';
|
||||||
|
|
||||||
|
|||||||
@ -41,6 +41,18 @@ class AppLocalizationsVi extends AppLocalizations {
|
|||||||
@override
|
@override
|
||||||
String get navigationTabsHome => 'Trang chủ';
|
String get navigationTabsHome => 'Trang chủ';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get navigationTabsAlbums => 'Albums';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get navigationTabsArtists => 'Artists';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get navigationTabsPlaylists => 'Playlists';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get navigationTabsSongs => 'Songs';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get navigationTabsLibrary => 'Thư Viện';
|
String get navigationTabsLibrary => 'Thư Viện';
|
||||||
|
|
||||||
|
|||||||
@ -41,6 +41,18 @@ class AppLocalizationsZh extends AppLocalizations {
|
|||||||
@override
|
@override
|
||||||
String get navigationTabsHome => '首页';
|
String get navigationTabsHome => '首页';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get navigationTabsAlbums => 'Albums';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get navigationTabsArtists => 'Artists';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get navigationTabsPlaylists => 'Playlists';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get navigationTabsSongs => 'Songs';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get navigationTabsLibrary => '所有';
|
String get navigationTabsLibrary => '所有';
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user