36 Commits

Author SHA1 Message Date
Bart Ribbers
dae166ecb9 Merge eaffffac4d into b0bb26f84b 2024-12-10 22:47:14 +01:00
Bart Ribbers
eaffffac4d fix: move all .of(context) out of the widget tree and into a variable
At some places <something>.of(context) was used multiple times in the
same widget. This, although small, can have an impact on performance
that's just plain unnecessary. It's better to just get things you need
out of the context first before you do anything else.
2024-12-10 22:47:06 +01:00
Bart Ribbers
e11250182d chore: upgrade to Flutter 3.24.5
At the time of writing the latest Flutter version.
2024-12-10 22:45:36 +01:00
Bart Ribbers
fc0daacfc0 chore: ignore VSCode's settings.json in git
Seettings in there can be different per user and should be, it should
not be committed
2024-12-10 20:42:50 +01:00
Bart Ribbers
7e5885e5c8 chore: cleanup fvm configuration
I guess fvm changed the way it does it's configuration at some point,
but rather than having .fvm/fmv_config.json checked in .fvmrc should be
checked in. The .fvm directory is used to store files and symlinks
related to the installed Flutter version and as such should not be
commited at all.
2024-12-10 20:42:49 +01:00
austinried
b0bb26f84b Fix initial server ping/feature tests always using token auth 2023-05-18 06:42:29 +09:00
austinried
e94fcf3128 bump version 2023-05-16 18:59:16 +09:00
josé m
bd6e818f36 Translated using Weblate (Galician)
Currently translated at 100.0% (94 of 94 strings)

Co-authored-by: josé m <correoxm@disroot.org>
Translate-URL: https://hosted.weblate.org/projects/subtracks/subtracks/gl/
Translation: Subtracks/subtracks
2023-05-16 18:57:04 +09:00
Max Smith
96d0c35c31 Translated using Weblate (Russian)
Currently translated at 100.0% (94 of 94 strings)

Co-authored-by: Max Smith <sevinfolds@gmail.com>
Translate-URL: https://hosted.weblate.org/projects/subtracks/subtracks/ru/
Translation: Subtracks/subtracks
2023-05-16 18:57:04 +09:00
Tim Schneeberger
4ef3281a0b Translated using Weblate (German)
Currently translated at 100.0% (92 of 92 strings)

Co-authored-by: Tim Schneeberger <thebone.main@gmail.com>
Translate-URL: https://hosted.weblate.org/projects/subtracks/subtracks/de/
Translation: Subtracks/subtracks
2023-05-16 18:57:04 +09:00
austinried
c56e3dba0f remove todo 2023-05-16 09:34:39 +09:00
austinried
53d284ace4 redact error too
create log file if it doesn't exist first
2023-05-16 09:34:39 +09:00
austinried
c2733482e5 show snackbar error for sync
log http errors
log sync errors
2023-05-16 09:34:39 +09:00
austinried
67f0c926c4 add snackbar method for errors
test (ping) server before saving source
display error message when saving source
2023-05-16 09:34:39 +09:00
Joel Calado
889be2ff2c return null 2023-05-15 07:11:58 +09:00
Joel Calado
52b51954aa improve url validation in settings 2023-05-15 07:11:58 +09:00
austinried
1c76293559 default force plaintext password off 2023-05-14 14:35:19 +09:00
austinried
250d6793a2 wording 2023-05-14 14:29:04 +09:00
austinried
121af2bca3 audio playback error logging
subsonic error logging
source save error logging
2023-05-14 14:29:04 +09:00
austinried
e410dcb2eb log sql exceptions 2023-05-14 14:29:04 +09:00
austinried
63ff9772e5 initial console/file logging framework 2023-05-14 14:29:04 +09:00
Vojtěch Fošnár
1ae29c5ade Translated using Weblate (Czech)
Currently translated at 85.8% (79 of 92 strings)

Co-authored-by: Vojtěch Fošnár <vfosnar@fosny.eu>
Translate-URL: https://hosted.weblate.org/projects/subtracks/subtracks/cs/
Translation: Subtracks/subtracks
2023-05-11 10:07:23 +09:00
Joel Calado
fedd6a71bb Translated using Weblate (Portuguese)
Currently translated at 88.0% (81 of 92 strings)

Co-authored-by: Joel Calado <joelcalado@gmail.com>
Translate-URL: https://hosted.weblate.org/projects/subtracks/subtracks/pt/
Translation: Subtracks/subtracks
2023-05-11 10:07:23 +09:00
austinried
8f64cfcbca bump version 2023-05-08 06:39:17 +09:00
austinried
1edb2c13da update todo 2023-05-08 06:37:10 +09:00
austinried
7f83204b24 don't pass all ids as params
instead, only pass ids to delete and chunk those by the param limit
2023-05-07 13:56:05 +09:00
austinried
0fe52494d0 update todo 2023-05-07 13:54:56 +09:00
Daniel Playfair Cal
56dbcde3b4 add autofill hints for source page 2023-05-07 13:54:26 +09:00
austinried
8fbc5e6ce4 add artist radio 2023-05-07 13:28:15 +09:00
austinried
979a4b7c73 add plaintext password option
fixes #161
2023-05-06 17:56:03 +09:00
josé m
7b1da24748 Translated using Weblate (Galician)
Currently translated at 100.0% (92 of 92 strings)

Co-authored-by: josé m <correoxm@disroot.org>
Translate-URL: https://hosted.weblate.org/projects/subtracks/subtracks/gl/
Translation: Subtracks/subtracks
2023-05-06 09:11:25 +09:00
Sacelo
7014aa85d1 Translated using Weblate (Spanish)
Currently translated at 77.1% (71 of 92 strings)

Co-authored-by: Sacelo <rion020806@gmail.com>
Translate-URL: https://hosted.weblate.org/projects/subtracks/subtracks/es/
Translation: Subtracks/subtracks
2023-05-06 09:11:25 +09:00
雨杉叶
abab674322 Translated using Weblate (Chinese (Simplified))
Currently translated at 88.0% (81 of 92 strings)

Co-authored-by: 雨杉叶 <yushaye@outlook.com>
Translate-URL: https://hosted.weblate.org/projects/subtracks/subtracks/zh_Hans/
Translation: Subtracks/subtracks
2023-05-06 09:11:25 +09:00
daniel-225
498bb22a69 Translated using Weblate (German)
Currently translated at 75.0% (69 of 92 strings)

Co-authored-by: daniel-225 <dsoukup@outlook.de>
Translate-URL: https://hosted.weblate.org/projects/subtracks/subtracks/de/
Translation: Subtracks/subtracks
2023-05-06 09:11:25 +09:00
austinried
11fe43a750 add server info fields 2023-04-28 21:59:33 +09:00
austinried
2a60a7306c use english as last fallback locale
fixes #160
2023-04-28 21:45:59 +09:00
83 changed files with 5126 additions and 3078 deletions

View File

@@ -1,4 +0,0 @@
{
"flutterSdkVersion": "3.7.11",
"flavors": {}
}

4
.fvmrc Normal file
View File

@@ -0,0 +1,4 @@
{
"flutter": "3.24.5",
"flavors": {}
}

View File

@@ -23,10 +23,14 @@ A clear and concise description of what you expected to happen.
**Screenshots**
If applicable, add screenshots to help explain your problem.
**Smartphone (please complete the following information):**
- Device: [e.g. Pixel 4]
**Device**
- Model: [e.g. Pixel 4]
- OS: [e.g. Android 12]
- Subtracks version [e.g. 1.2.0]
**Server**
- Software: [e.g. Navidrome]
- Version: [e.g. 0.49.3]
**Additional context**
Add any other context about the problem here.

8
.gitignore vendored
View File

@@ -16,10 +16,8 @@ migrate_working_dir/
*.iws
.idea/
# The .vscode folder contains launch configuration and tasks you configure in
# VS Code which you may wish to be included in version control, so this line
# is commented out by default.
#.vscode/
# VSCode related
.vscode/settings.json
# Flutter/Dart/Pub related
**/doc/api/
@@ -45,5 +43,5 @@ app.*.map.json
/.env
*.sqlite*
/.fvm/flutter_sdk
.fvm/
*.keystore

View File

@@ -22,6 +22,8 @@
"resourcesSortByTitle",
"resourcesSortByUpdated",
"settingsAboutActionsSupport",
"settingsAboutShareLogs",
"settingsAboutChooseLog",
"settingsNetworkOptionsOfflineMode",
"settingsNetworkOptionsOfflineModeOff",
"settingsNetworkOptionsOfflineModeOn",
@@ -53,6 +55,8 @@
"resourcesSortByTitle",
"resourcesSortByUpdated",
"settingsAboutActionsSupport",
"settingsAboutShareLogs",
"settingsAboutChooseLog",
"settingsNetworkOptionsOfflineMode",
"settingsNetworkOptionsOfflineModeOff",
"settingsNetworkOptionsOfflineModeOn",
@@ -62,32 +66,12 @@
],
"cs": [
"actionsCancel",
"actionsDelete",
"actionsDownload",
"actionsDownloadCancel",
"actionsDownloadDelete",
"actionsOk",
"controlsShuffle",
"resourcesAlbumCount",
"resourcesArtistCount",
"resourcesFilterAlbum",
"resourcesFilterArtist",
"resourcesFilterOwner",
"resourcesFilterYear",
"resourcesPlaylistCount",
"resourcesSongCount",
"resourcesSongListDeleteAllContent",
"resourcesSongListDeleteAllTitle",
"resourcesSortByAlbum",
"resourcesSortByAlbumCount",
"resourcesSortByTitle",
"resourcesSortByUpdated",
"settingsAboutActionsLicenses",
"settingsAboutActionsProjectHomepage",
"settingsAboutActionsSupport",
"settingsAboutName",
"settingsAboutVersion",
"settingsAboutShareLogs",
"settingsAboutChooseLog",
"settingsMusicName",
"settingsMusicOptionsScrobbleDescriptionOff",
"settingsMusicOptionsScrobbleDescriptionOn",
@@ -96,12 +80,7 @@
"settingsNetworkOptionsMinBufferTitle",
"settingsNetworkOptionsOfflineMode",
"settingsNetworkOptionsOfflineModeOff",
"settingsNetworkOptionsOfflineModeOn",
"settingsNetworkOptionsStreamFormat",
"settingsNetworkOptionsStreamFormatServerDefault",
"settingsResetActionsClearImageCache",
"settingsResetName",
"settingsServersFieldsName"
"settingsNetworkOptionsOfflineModeOn"
],
"da": [
@@ -133,6 +112,8 @@
"resourcesSortByTitle",
"resourcesSortByUpdated",
"settingsAboutActionsSupport",
"settingsAboutShareLogs",
"settingsAboutChooseLog",
"settingsMusicOptionsScrobbleDescriptionOff",
"settingsNetworkOptionsOfflineMode",
"settingsNetworkOptionsOfflineModeOff",
@@ -146,44 +127,11 @@
],
"de": [
"actionsCancel",
"actionsDelete",
"actionsDownload",
"actionsDownloadCancel",
"actionsDownloadDelete",
"actionsOk",
"controlsShuffle",
"resourcesAlbumCount",
"resourcesArtistCount",
"resourcesFilterAlbum",
"resourcesFilterArtist",
"resourcesFilterOwner",
"resourcesFilterYear",
"resourcesPlaylistCount",
"resourcesSongCount",
"resourcesSongListDeleteAllContent",
"resourcesSongListDeleteAllTitle",
"resourcesSortByAlbum",
"resourcesSortByAlbumCount",
"resourcesSortByTitle",
"resourcesSortByUpdated",
"settingsAboutActionsSupport",
"settingsNetworkOptionsOfflineMode",
"settingsNetworkOptionsOfflineModeOff",
"settingsNetworkOptionsOfflineModeOn",
"settingsNetworkOptionsStreamFormat",
"settingsNetworkOptionsStreamFormatServerDefault",
"settingsServersFieldsName"
"settingsAboutShareLogs",
"settingsAboutChooseLog"
],
"es": [
"actionsCancel",
"actionsDelete",
"actionsDownload",
"actionsDownloadCancel",
"actionsDownloadDelete",
"actionsOk",
"controlsShuffle",
"resourcesAlbumCount",
"resourcesArtistCount",
"resourcesFilterAlbum",
@@ -199,6 +147,8 @@
"resourcesSortByTitle",
"resourcesSortByUpdated",
"settingsAboutActionsSupport",
"settingsAboutShareLogs",
"settingsAboutChooseLog",
"settingsNetworkOptionsOfflineMode",
"settingsNetworkOptionsOfflineModeOff",
"settingsNetworkOptionsOfflineModeOn",
@@ -230,37 +180,8 @@
"resourcesSortByTitle",
"resourcesSortByUpdated",
"settingsAboutActionsSupport",
"settingsNetworkOptionsOfflineMode",
"settingsNetworkOptionsOfflineModeOff",
"settingsNetworkOptionsOfflineModeOn",
"settingsNetworkOptionsStreamFormat",
"settingsNetworkOptionsStreamFormatServerDefault",
"settingsServersFieldsName"
],
"gl": [
"actionsCancel",
"actionsDelete",
"actionsDownload",
"actionsDownloadCancel",
"actionsDownloadDelete",
"actionsOk",
"controlsShuffle",
"resourcesAlbumCount",
"resourcesArtistCount",
"resourcesFilterAlbum",
"resourcesFilterArtist",
"resourcesFilterOwner",
"resourcesFilterYear",
"resourcesPlaylistCount",
"resourcesSongCount",
"resourcesSongListDeleteAllContent",
"resourcesSongListDeleteAllTitle",
"resourcesSortByAlbum",
"resourcesSortByAlbumCount",
"resourcesSortByTitle",
"resourcesSortByUpdated",
"settingsAboutActionsSupport",
"settingsAboutShareLogs",
"settingsAboutChooseLog",
"settingsNetworkOptionsOfflineMode",
"settingsNetworkOptionsOfflineModeOff",
"settingsNetworkOptionsOfflineModeOn",
@@ -292,6 +213,8 @@
"resourcesSortByTitle",
"resourcesSortByUpdated",
"settingsAboutActionsSupport",
"settingsAboutShareLogs",
"settingsAboutChooseLog",
"settingsNetworkOptionsOfflineMode",
"settingsNetworkOptionsOfflineModeOff",
"settingsNetworkOptionsOfflineModeOn",
@@ -343,6 +266,8 @@
"settingsAboutActionsLicenses",
"settingsAboutActionsSupport",
"settingsAboutName",
"settingsAboutShareLogs",
"settingsAboutChooseLog",
"settingsAboutVersion",
"settingsMusicOptionsScrobbleDescriptionOff",
"settingsMusicOptionsScrobbleDescriptionOn",
@@ -399,6 +324,8 @@
"resourcesSortByTitle",
"resourcesSortByUpdated",
"settingsAboutActionsSupport",
"settingsAboutShareLogs",
"settingsAboutChooseLog",
"settingsNetworkOptionsOfflineMode",
"settingsNetworkOptionsOfflineModeOff",
"settingsNetworkOptionsOfflineModeOn",
@@ -430,6 +357,8 @@
"resourcesSortByTitle",
"resourcesSortByUpdated",
"settingsAboutActionsSupport",
"settingsAboutShareLogs",
"settingsAboutChooseLog",
"settingsNetworkOptionsOfflineMode",
"settingsNetworkOptionsOfflineModeOff",
"settingsNetworkOptionsOfflineModeOn",
@@ -461,6 +390,8 @@
"resourcesSortByTitle",
"resourcesSortByUpdated",
"settingsAboutActionsSupport",
"settingsAboutShareLogs",
"settingsAboutChooseLog",
"settingsNetworkOptionsOfflineMode",
"settingsNetworkOptionsOfflineModeOff",
"settingsNetworkOptionsOfflineModeOn",
@@ -470,63 +401,17 @@
],
"pt": [
"actionsCancel",
"actionsDelete",
"actionsDownload",
"actionsDownloadCancel",
"actionsDownloadDelete",
"actionsOk",
"controlsShuffle",
"resourcesAlbumCount",
"resourcesArtistCount",
"resourcesFilterAlbum",
"resourcesFilterArtist",
"resourcesFilterOwner",
"resourcesFilterYear",
"resourcesPlaylistCount",
"resourcesSongCount",
"resourcesSongListDeleteAllContent",
"resourcesSongListDeleteAllTitle",
"resourcesSortByAlbum",
"resourcesSortByAlbumCount",
"resourcesSortByTitle",
"resourcesSortByUpdated",
"settingsAboutActionsSupport",
"settingsNetworkOptionsOfflineMode",
"settingsNetworkOptionsOfflineModeOff",
"settingsNetworkOptionsOfflineModeOn",
"settingsNetworkOptionsStreamFormat",
"settingsNetworkOptionsStreamFormatServerDefault",
"settingsServersFieldsName"
],
"ru": [
"actionsCancel",
"actionsDelete",
"actionsDownload",
"actionsDownloadCancel",
"actionsDownloadDelete",
"actionsOk",
"controlsShuffle",
"resourcesAlbumCount",
"resourcesArtistCount",
"resourcesFilterAlbum",
"resourcesFilterArtist",
"resourcesFilterOwner",
"resourcesFilterYear",
"resourcesPlaylistCount",
"resourcesSongCount",
"resourcesSongListDeleteAllContent",
"resourcesSongListDeleteAllTitle",
"resourcesSortByAlbum",
"resourcesSortByAlbumCount",
"resourcesSortByTitle",
"resourcesSortByUpdated",
"settingsAboutActionsSupport",
"settingsNetworkOptionsOfflineMode",
"settingsNetworkOptionsOfflineModeOff",
"settingsNetworkOptionsOfflineModeOn",
"settingsNetworkOptionsStreamFormat",
"settingsAboutShareLogs",
"settingsAboutChooseLog",
"settingsNetworkOptionsStreamFormatServerDefault",
"settingsServersFieldsName"
],
@@ -554,6 +439,8 @@
"resourcesSortByTitle",
"resourcesSortByUpdated",
"settingsAboutActionsSupport",
"settingsAboutShareLogs",
"settingsAboutChooseLog",
"settingsNetworkOptionsOfflineMode",
"settingsNetworkOptionsOfflineModeOff",
"settingsNetworkOptionsOfflineModeOn",
@@ -585,6 +472,8 @@
"resourcesSortByTitle",
"resourcesSortByUpdated",
"settingsAboutActionsSupport",
"settingsAboutShareLogs",
"settingsAboutChooseLog",
"settingsNetworkOptionsOfflineMode",
"settingsNetworkOptionsOfflineModeOff",
"settingsNetworkOptionsOfflineModeOn",
@@ -594,28 +483,13 @@
],
"zh": [
"actionsCancel",
"actionsDelete",
"actionsDownload",
"actionsDownloadCancel",
"actionsDownloadDelete",
"actionsOk",
"controlsShuffle",
"resourcesAlbumCount",
"resourcesArtistCount",
"resourcesFilterAlbum",
"resourcesFilterArtist",
"resourcesFilterOwner",
"resourcesFilterYear",
"resourcesPlaylistCount",
"resourcesSongCount",
"resourcesSongListDeleteAllContent",
"resourcesSongListDeleteAllTitle",
"resourcesSortByAlbum",
"resourcesSortByAlbumCount",
"resourcesSortByTitle",
"resourcesSortByUpdated",
"settingsAboutActionsSupport",
"settingsAboutShareLogs",
"settingsAboutChooseLog",
"settingsNetworkOptionsOfflineMode",
"settingsNetworkOptionsOfflineModeOff",
"settingsNetworkOptionsOfflineModeOn",

62
TODO.md
View File

@@ -1,34 +1,30 @@
## To-do
- [ ] Star/unstar
- [ ] Context menus
- [ ] Download actions for song
- [ ] Playlist management
- [ ] Add to playlist (from context)
- [ ] Queue management
- [ ] View playing queue
- [ ] Re-order queue
- [ ] Add to queue (from context)
- [ ] Remove from queue
- [ ] Scrobbling
- [ ] Library filters (year/genre/etc)
- [ ] Library list display modes
- [ ] Search
- [ ] Individual "more" results pages
- [ ] Radio modes
- [ ] Artist
- [ ] Now playing gestures
- [ ] Swipe bar/album to skip
- [ ] Double-tap to seek forward/back (bar only)
- [ ] Settings
- [ ] Sources
- [ ] Use plaintext password
- [ ] Music
- [ ] Scrobble
- [ ] Downloads
- [ ] Used/available space
- [ ] Clear downloads
- [ ] Clear images
- [ ] About
- [ ] Licenses
- [ ] Welcome/setup flow
- [ ] Proper loading screen/animation
- Star/unstar
- Context menus
- Download actions for song
- Playlist management
- Add to playlist (from context)
- Queue management
- View playing queue
- Re-order queue
- Add to queue (from context)
- Remove from queue
- Scrobbling
- Library filters (year/genre/etc)
- Library list display modes
- Search
- Individual "more" results pages
- Now playing gestures
- Swipe bar/album to skip
- Double-tap to seek forward/back (bar only)
- Settings
- Music
- Scrobble
- Downloads
- Used/available space
- Clear downloads
- Clear images
- About
- Licenses
- Welcome/setup flow
- Proper loading screen/animation

View File

@@ -1,3 +1,9 @@
plugins {
id "com.android.application"
id "kotlin-android"
id "dev.flutter.flutter-gradle-plugin"
}
def localProperties = new Properties()
def localPropertiesFile = rootProject.file('local.properties')
if (localPropertiesFile.exists()) {
@@ -6,11 +12,6 @@ if (localPropertiesFile.exists()) {
}
}
def flutterRoot = localProperties.getProperty('flutter.sdk')
if (flutterRoot == null) {
throw new GradleException("Flutter SDK not found. Define location with flutter.sdk in the local.properties file.")
}
def flutterVersionCode = localProperties.getProperty('flutter.versionCode')
if (flutterVersionCode == null) {
flutterVersionCode = '1'
@@ -21,10 +22,6 @@ if (flutterVersionName == null) {
flutterVersionName = '1.0'
}
apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle"
def keystoreProperties = new Properties()
def keystorePropertiesFile = rootProject.file('key.properties')
if (keystorePropertiesFile.exists()) {
@@ -53,7 +50,7 @@ android {
applicationId "com.subtracks2"
// You can update the following values to match your application needs.
// For more information, see: https://docs.flutter.dev/deployment/android#reviewing-the-gradle-build-configuration.
minSdkVersion 19
minSdkVersion flutter.minSdkVersion
targetSdkVersion flutter.targetSdkVersion
versionCode flutterVersionCode.toInteger()
versionName flutterVersionName
@@ -81,8 +78,4 @@ android {
flutter {
source '../..'
}
dependencies {
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
}
}

View File

@@ -5,4 +5,9 @@
to allow setting breakpoints, to provide hot reload, etc.
-->
<uses-permission android:name="android.permission.INTERNET"/>
<!-- audio_service -->
<uses-permission android:name="android.permission.WAKE_LOCK" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_MEDIA_PLAYBACK" />
</manifest>

View File

@@ -69,4 +69,5 @@
<!-- audio_service -->
<uses-permission android:name="android.permission.WAKE_LOCK" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_MEDIA_PLAYBACK" />
</manifest>

View File

@@ -1,25 +0,0 @@
// Generated file.
//
// If you wish to remove Flutter's multidex support, delete this entire file.
//
// Modifications to this file should be done in a copy under a different name
// as this file may be regenerated.
package io.flutter.app;
import android.app.Application;
import android.content.Context;
import androidx.annotation.CallSuper;
import androidx.multidex.MultiDex;
/**
* Extension of {@link android.app.Application}, adding multidex support.
*/
public class FlutterMultiDexApplication extends Application {
@Override
@CallSuper
protected void attachBaseContext(Context base) {
super.attachBaseContext(base);
MultiDex.install(this);
}
}

View File

@@ -5,4 +5,9 @@
to allow setting breakpoints, to provide hot reload, etc.
-->
<uses-permission android:name="android.permission.INTERNET"/>
<!-- audio_service -->
<uses-permission android:name="android.permission.WAKE_LOCK" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_MEDIA_PLAYBACK" />
</manifest>

View File

@@ -1,16 +1,3 @@
buildscript {
ext.kotlin_version = '1.7.10'
repositories {
google()
mavenCentral()
}
dependencies {
classpath 'com.android.tools.build:gradle:7.2.0'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
}
}
allprojects {
repositories {
google()
@@ -26,6 +13,6 @@ subprojects {
project.evaluationDependsOn(':app')
}
task clean(type: Delete) {
tasks.register("clean", Delete) {
delete rootProject.buildDir
}

View File

@@ -1,11 +1,25 @@
include ':app'
pluginManagement {
def flutterSdkPath = {
def properties = new Properties()
file("local.properties").withInputStream { properties.load(it) }
def flutterSdkPath = properties.getProperty("flutter.sdk")
assert flutterSdkPath != null, "flutter.sdk not set in local.properties"
return flutterSdkPath
}()
def localPropertiesFile = new File(rootProject.projectDir, "local.properties")
def properties = new Properties()
includeBuild("$flutterSdkPath/packages/flutter_tools/gradle")
assert localPropertiesFile.exists()
localPropertiesFile.withReader("UTF-8") { reader -> properties.load(reader) }
repositories {
google()
mavenCentral()
gradlePluginPortal()
}
}
def flutterSdkPath = properties.getProperty("flutter.sdk")
assert flutterSdkPath != null, "flutter.sdk not set in local.properties"
apply from: "$flutterSdkPath/packages/flutter_tools/gradle/app_plugin_loader.gradle"
plugins {
id "dev.flutter.flutter-plugin-loader" version "1.0.0"
id "com.android.application" version "7.2.0" apply false
id "org.jetbrains.kotlin.android" version "2.0.21" apply false
}
include ":app"

View File

@@ -1,3 +1,4 @@
import 'package:fast_immutable_collections/fast_immutable_collections.dart';
import 'package:flutter/material.dart';
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
@@ -77,7 +78,8 @@ class App extends HookConsumerWidget {
),
routeInformationParser: appRouter.defaultRouteParser(),
localizationsDelegates: AppLocalizations.localizationsDelegates,
supportedLocales: AppLocalizations.supportedLocales,
supportedLocales: [...AppLocalizations.supportedLocales]
..moveToTheFront(const Locale('en')),
);
}
}

View File

@@ -20,4 +20,5 @@ final lastPathProvider = NotifierProvider<LastPath, String>.internal(
);
typedef _$LastPath = Notifier<String>;
// ignore_for_file: unnecessary_raw_strings, subtype_of_sealed_class, invalid_use_of_internal_member, do_not_use_environment, prefer_const_constructors, public_member_api_docs, avoid_private_typedef_functions
// ignore_for_file: type=lint
// ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member

View File

@@ -32,6 +32,8 @@ class RadioPlayFab extends StatelessWidget {
@override
Widget build(BuildContext context) {
final theme = Theme.of(context);
return FloatingActionButton(
heroTag: null,
onPressed: onPressed,
@@ -44,7 +46,7 @@ class RadioPlayFab extends StatelessWidget {
right: -10,
child: Icon(
Icons.play_arrow_rounded,
color: Theme.of(context).colorScheme.primaryContainer,
color: theme.colorScheme.primaryContainer,
size: 26,
),
),

View File

@@ -25,7 +25,7 @@ Future<T?> showContextMenu<T>({
required WidgetBuilder builder,
}) {
return showModalBottomSheet<T>(
backgroundColor: ref.read(baseThemeProvider).theme.colorScheme.background,
backgroundColor: ref.read(baseThemeProvider).theme.colorScheme.surface,
useRootNavigator: true,
isScrollControlled: true,
context: context,
@@ -327,8 +327,9 @@ class _DownloadAction extends HookConsumerWidget {
@override
Widget build(BuildContext context, WidgetRef ref) {
final l = AppLocalizations.of(context);
return _MenuItem(
title: _actionText(AppLocalizations.of(context)),
title: _actionText(l),
icon: downloadAction.iconBuilder(context),
onTap: downloadAction.action,
);

View File

@@ -54,11 +54,12 @@ class BackgroundGradient extends HookConsumerWidget {
@override
Widget build(BuildContext context, WidgetRef ref) {
final mediaQuery = MediaQuery.of(context);
final base = ref.watch(baseThemeProvider);
return SizedBox(
width: double.infinity,
height: MediaQuery.of(context).size.height,
height: mediaQuery.size.height,
child: Container(
decoration: BoxDecoration(
gradient: LinearGradient(

View File

@@ -112,9 +112,9 @@ List<DownloadAction> useListDownloadActions({
DownloadAction cancel() {
return DownloadAction(
type: DownloadActionType.cancel,
iconBuilder: (context) => Stack(
iconBuilder: (context) => const Stack(
alignment: Alignment.center,
children: const [
children: [
Icon(Icons.cancel_rounded),
SizedBox(
height: 32,

View File

@@ -103,6 +103,8 @@ class ArtistArtImage extends HookConsumerWidget {
@override
Widget build(BuildContext context, WidgetRef ref) {
final theme = Theme.of(context);
final cache = ref.watch(_artistArtUriCacheInfoProvider(
artistId: artistId,
thumbnail: thumbnail,
@@ -123,7 +125,7 @@ class ArtistArtImage extends HookConsumerWidget {
width: width,
),
loading: () => Container(
color: Theme.of(context).colorScheme.secondaryContainer,
color: theme.colorScheme.secondaryContainer,
height: height,
width: width,
),
@@ -211,9 +213,11 @@ class CardClip extends StatelessWidget {
@override
Widget build(BuildContext context) {
final cardShape = Theme.of(context).cardTheme.shape;
return ClipRRect(
borderRadius:
cardShape is RoundedRectangleBorder ? cardShape.borderRadius : null,
borderRadius: cardShape is RoundedRectangleBorder
? cardShape.borderRadius
: BorderRadius.zero,
child: !square
? child
: AspectRatio(
@@ -247,6 +251,8 @@ class UriCacheInfoImage extends StatelessWidget {
@override
Widget build(BuildContext context) {
final theme = Theme.of(context);
return CachedNetworkImage(
imageUrl: cache.uri.toString(),
cacheKey: cache.cacheKey,
@@ -260,7 +266,7 @@ class UriCacheInfoImage extends StatelessWidget {
placeholderStyle == PlaceholderStyle.spinner
? Container()
: Container(
color: Theme.of(context).colorScheme.secondaryContainer,
color: theme.colorScheme.secondaryContainer,
),
errorWidget: (context, url, error) => PlaceholderImage(
fit: fit,

View File

@@ -30,8 +30,6 @@ class _SystemHash {
}
}
typedef _ArtistArtCacheInfoRef = AutoDisposeProviderRef<CacheInfo>;
/// See also [_artistArtCacheInfo].
@ProviderFor(_artistArtCacheInfo)
const _artistArtCacheInfoProvider = _ArtistArtCacheInfoFamily();
@@ -81,11 +79,11 @@ class _ArtistArtCacheInfoFamily extends Family<CacheInfo> {
class _ArtistArtCacheInfoProvider extends AutoDisposeProvider<CacheInfo> {
/// See also [_artistArtCacheInfo].
_ArtistArtCacheInfoProvider({
required this.artistId,
this.thumbnail = true,
}) : super.internal(
required String artistId,
bool thumbnail = true,
}) : this._internal(
(ref) => _artistArtCacheInfo(
ref,
ref as _ArtistArtCacheInfoRef,
artistId: artistId,
thumbnail: thumbnail,
),
@@ -98,11 +96,48 @@ class _ArtistArtCacheInfoProvider extends AutoDisposeProvider<CacheInfo> {
dependencies: _ArtistArtCacheInfoFamily._dependencies,
allTransitiveDependencies:
_ArtistArtCacheInfoFamily._allTransitiveDependencies,
artistId: artistId,
thumbnail: thumbnail,
);
_ArtistArtCacheInfoProvider._internal(
super._createNotifier, {
required super.name,
required super.dependencies,
required super.allTransitiveDependencies,
required super.debugGetCreateSourceHash,
required super.from,
required this.artistId,
required this.thumbnail,
}) : super.internal();
final String artistId;
final bool thumbnail;
@override
Override overrideWith(
CacheInfo Function(_ArtistArtCacheInfoRef provider) create,
) {
return ProviderOverride(
origin: this,
override: _ArtistArtCacheInfoProvider._internal(
(ref) => create(ref as _ArtistArtCacheInfoRef),
from: from,
name: null,
dependencies: null,
allTransitiveDependencies: null,
debugGetCreateSourceHash: null,
artistId: artistId,
thumbnail: thumbnail,
),
);
}
@override
AutoDisposeProviderElement<CacheInfo> createElement() {
return _ArtistArtCacheInfoProviderElement(this);
}
@override
bool operator ==(Object other) {
return other is _ArtistArtCacheInfoProvider &&
@@ -120,9 +155,26 @@ class _ArtistArtCacheInfoProvider extends AutoDisposeProvider<CacheInfo> {
}
}
mixin _ArtistArtCacheInfoRef on AutoDisposeProviderRef<CacheInfo> {
/// The parameter `artistId` of this provider.
String get artistId;
/// The parameter `thumbnail` of this provider.
bool get thumbnail;
}
class _ArtistArtCacheInfoProviderElement
extends AutoDisposeProviderElement<CacheInfo> with _ArtistArtCacheInfoRef {
_ArtistArtCacheInfoProviderElement(super.provider);
@override
String get artistId => (origin as _ArtistArtCacheInfoProvider).artistId;
@override
bool get thumbnail => (origin as _ArtistArtCacheInfoProvider).thumbnail;
}
String _$artistArtCachedUrlHash() =>
r'2a5e0fea614ff12a1d562faccec6cfe98394af42';
typedef _ArtistArtCachedUrlRef = AutoDisposeFutureProviderRef<String?>;
/// See also [_artistArtCachedUrl].
@ProviderFor(_artistArtCachedUrl)
@@ -173,11 +225,11 @@ class _ArtistArtCachedUrlFamily extends Family<AsyncValue<String?>> {
class _ArtistArtCachedUrlProvider extends AutoDisposeFutureProvider<String?> {
/// See also [_artistArtCachedUrl].
_ArtistArtCachedUrlProvider({
required this.artistId,
this.thumbnail = true,
}) : super.internal(
required String artistId,
bool thumbnail = true,
}) : this._internal(
(ref) => _artistArtCachedUrl(
ref,
ref as _ArtistArtCachedUrlRef,
artistId: artistId,
thumbnail: thumbnail,
),
@@ -190,11 +242,48 @@ class _ArtistArtCachedUrlProvider extends AutoDisposeFutureProvider<String?> {
dependencies: _ArtistArtCachedUrlFamily._dependencies,
allTransitiveDependencies:
_ArtistArtCachedUrlFamily._allTransitiveDependencies,
artistId: artistId,
thumbnail: thumbnail,
);
_ArtistArtCachedUrlProvider._internal(
super._createNotifier, {
required super.name,
required super.dependencies,
required super.allTransitiveDependencies,
required super.debugGetCreateSourceHash,
required super.from,
required this.artistId,
required this.thumbnail,
}) : super.internal();
final String artistId;
final bool thumbnail;
@override
Override overrideWith(
FutureOr<String?> Function(_ArtistArtCachedUrlRef provider) create,
) {
return ProviderOverride(
origin: this,
override: _ArtistArtCachedUrlProvider._internal(
(ref) => create(ref as _ArtistArtCachedUrlRef),
from: from,
name: null,
dependencies: null,
allTransitiveDependencies: null,
debugGetCreateSourceHash: null,
artistId: artistId,
thumbnail: thumbnail,
),
);
}
@override
AutoDisposeFutureProviderElement<String?> createElement() {
return _ArtistArtCachedUrlProviderElement(this);
}
@override
bool operator ==(Object other) {
return other is _ArtistArtCachedUrlProvider &&
@@ -212,9 +301,27 @@ class _ArtistArtCachedUrlProvider extends AutoDisposeFutureProvider<String?> {
}
}
mixin _ArtistArtCachedUrlRef on AutoDisposeFutureProviderRef<String?> {
/// The parameter `artistId` of this provider.
String get artistId;
/// The parameter `thumbnail` of this provider.
bool get thumbnail;
}
class _ArtistArtCachedUrlProviderElement
extends AutoDisposeFutureProviderElement<String?>
with _ArtistArtCachedUrlRef {
_ArtistArtCachedUrlProviderElement(super.provider);
@override
String get artistId => (origin as _ArtistArtCachedUrlProvider).artistId;
@override
bool get thumbnail => (origin as _ArtistArtCachedUrlProvider).thumbnail;
}
String _$artistArtUriCacheInfoHash() =>
r'9bdc0f5654882265236ef746ea697a6d107a4b6f';
typedef _ArtistArtUriCacheInfoRef = AutoDisposeFutureProviderRef<UriCacheInfo>;
/// See also [_artistArtUriCacheInfo].
@ProviderFor(_artistArtUriCacheInfo)
@@ -266,11 +373,11 @@ class _ArtistArtUriCacheInfoProvider
extends AutoDisposeFutureProvider<UriCacheInfo> {
/// See also [_artistArtUriCacheInfo].
_ArtistArtUriCacheInfoProvider({
required this.artistId,
this.thumbnail = true,
}) : super.internal(
required String artistId,
bool thumbnail = true,
}) : this._internal(
(ref) => _artistArtUriCacheInfo(
ref,
ref as _ArtistArtUriCacheInfoRef,
artistId: artistId,
thumbnail: thumbnail,
),
@@ -283,11 +390,48 @@ class _ArtistArtUriCacheInfoProvider
dependencies: _ArtistArtUriCacheInfoFamily._dependencies,
allTransitiveDependencies:
_ArtistArtUriCacheInfoFamily._allTransitiveDependencies,
artistId: artistId,
thumbnail: thumbnail,
);
_ArtistArtUriCacheInfoProvider._internal(
super._createNotifier, {
required super.name,
required super.dependencies,
required super.allTransitiveDependencies,
required super.debugGetCreateSourceHash,
required super.from,
required this.artistId,
required this.thumbnail,
}) : super.internal();
final String artistId;
final bool thumbnail;
@override
Override overrideWith(
FutureOr<UriCacheInfo> Function(_ArtistArtUriCacheInfoRef provider) create,
) {
return ProviderOverride(
origin: this,
override: _ArtistArtUriCacheInfoProvider._internal(
(ref) => create(ref as _ArtistArtUriCacheInfoRef),
from: from,
name: null,
dependencies: null,
allTransitiveDependencies: null,
debugGetCreateSourceHash: null,
artistId: artistId,
thumbnail: thumbnail,
),
);
}
@override
AutoDisposeFutureProviderElement<UriCacheInfo> createElement() {
return _ArtistArtUriCacheInfoProviderElement(this);
}
@override
bool operator ==(Object other) {
return other is _ArtistArtUriCacheInfoProvider &&
@@ -304,4 +448,24 @@ class _ArtistArtUriCacheInfoProvider
return _SystemHash.finish(hash);
}
}
// ignore_for_file: unnecessary_raw_strings, subtype_of_sealed_class, invalid_use_of_internal_member, do_not_use_environment, prefer_const_constructors, public_member_api_docs, avoid_private_typedef_functions
mixin _ArtistArtUriCacheInfoRef on AutoDisposeFutureProviderRef<UriCacheInfo> {
/// The parameter `artistId` of this provider.
String get artistId;
/// The parameter `thumbnail` of this provider.
bool get thumbnail;
}
class _ArtistArtUriCacheInfoProviderElement
extends AutoDisposeFutureProviderElement<UriCacheInfo>
with _ArtistArtUriCacheInfoRef {
_ArtistArtUriCacheInfoProviderElement(super.provider);
@override
String get artistId => (origin as _ArtistArtUriCacheInfoProvider).artistId;
@override
bool get thumbnail => (origin as _ArtistArtUriCacheInfoProvider).thumbnail;
}
// ignore_for_file: type=lint
// ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member

View File

@@ -204,12 +204,14 @@ class ArtistListTile extends HookConsumerWidget {
@override
Widget build(BuildContext context, WidgetRef ref) {
final l = AppLocalizations.of(context);
return ListTile(
leading: CircleClip(
child: ArtistArtImage(artistId: artist.id),
),
title: Text(artist.name),
subtitle: Text(AppLocalizations.of(context).resourcesAlbumCount(
subtitle: Text(l.resourcesAlbumCount(
artist.albumCount,
)),
onTap: onTap,
@@ -239,6 +241,8 @@ class PlaylistListTile extends HookConsumerWidget {
@override
Widget build(BuildContext context, WidgetRef ref) {
final l = AppLocalizations.of(context);
// generate the palette used in other views ahead of time
ref.watch(playlistArtPaletteProvider(playlist.id));
final cache = ref.watch(cacheServiceProvider).playlistArt(playlist);
@@ -248,7 +252,7 @@ class PlaylistListTile extends HookConsumerWidget {
child: UriCacheInfoImage(cache: cache),
),
title: Text(playlist.name),
subtitle: Text(AppLocalizations.of(context).resourcesSongCount(
subtitle: Text(l.resourcesSongCount(
playlist.songCount,
)),
onTap: onTap,

View File

@@ -4,6 +4,7 @@ import 'package:infinite_scroll_pagination/infinite_scroll_pagination.dart';
import '../services/sync_service.dart';
import 'items.dart';
import 'snackbars.dart';
class PagedListQueryView<T> extends HookConsumerWidget {
final PagingController<int, T> pagingController;
@@ -71,6 +72,8 @@ class PagedGridQueryView<T> extends HookConsumerWidget {
@override
Widget build(BuildContext context, WidgetRef ref) {
final mediaQuery = MediaQuery.of(context);
SliverGridDelegate gridDelegate;
double spacing;
@@ -91,7 +94,7 @@ class PagedGridQueryView<T> extends HookConsumerWidget {
}
final listView = PagedGridView<int, T>(
padding: MediaQuery.of(context).padding + EdgeInsets.all(spacing),
padding: mediaQuery.padding + EdgeInsets.all(spacing),
pagingController: pagingController,
builderDelegate: PagedChildBuilderDelegate(
itemBuilder: (context, item, index) =>
@@ -122,7 +125,14 @@ class SyncAllRefresh extends HookConsumerWidget {
@override
Widget build(BuildContext context, WidgetRef ref) {
return RefreshIndicator(
onRefresh: () => ref.read(syncServiceProvider.notifier).syncAll(),
onRefresh: () async {
try {
await ref.read(syncServiceProvider.notifier).syncAll();
} catch (e) {
if (!context.mounted) return;
showErrorSnackbar(context, e.toString());
}
},
child: child,
);
}

View File

@@ -30,13 +30,13 @@ class NowPlayingBar extends HookConsumerWidget {
elevation: 3,
color: colors?.darkBackground,
// surfaceTintColor: theme?.colorScheme.background,
child: Column(
child: const Column(
children: [
SizedBox(
height: 70,
child: Row(
mainAxisSize: MainAxisSize.max,
children: const [
children: [
Padding(
padding: EdgeInsets.all(10),
child: _ArtImage(),
@@ -54,7 +54,7 @@ class NowPlayingBar extends HookConsumerWidget {
],
),
),
const _ProgressBar(),
_ProgressBar(),
],
),
),
@@ -112,6 +112,8 @@ class _TrackInfo extends HookConsumerWidget {
@override
Widget build(BuildContext context, WidgetRef ref) {
final theme = Theme.of(context);
final item = ref.watch(mediaItemProvider);
return Column(
@@ -125,11 +127,11 @@ class _TrackInfo extends HookConsumerWidget {
// maxLines: 1,
// softWrap: false,
// overflow: TextOverflow.fade,
// style: Theme.of(context).textTheme.labelLarge,
// style: theme.textTheme.labelLarge,
// ),
ScrollableText(
data?.title ?? 'Nothing!!!',
style: Theme.of(context).textTheme.labelLarge,
style: theme.textTheme.labelLarge,
),
const SizedBox(height: 2),
Text(
@@ -137,7 +139,7 @@ class _TrackInfo extends HookConsumerWidget {
maxLines: 1,
softWrap: false,
overflow: TextOverflow.fade,
style: Theme.of(context).textTheme.labelMedium,
style: theme.textTheme.labelMedium,
),
],
error: (_, __) => const [Text('Error!')],
@@ -158,6 +160,8 @@ class PlayPauseButton extends HookConsumerWidget {
@override
Widget build(BuildContext context, WidgetRef ref) {
final theme = Theme.of(context);
final playing = ref.watch(playingProvider);
final state = ref.watch(processingStateProvider);
@@ -173,7 +177,7 @@ class PlayPauseButton extends HookConsumerWidget {
width: size / 3,
child: CircularProgressIndicator(
strokeWidth: size / 16,
color: Theme.of(context).colorScheme.background,
color: theme.colorScheme.surface,
),
),
],
@@ -195,7 +199,7 @@ class PlayPauseButton extends HookConsumerWidget {
}
},
icon: icon,
color: Theme.of(context).colorScheme.onBackground,
color: theme.colorScheme.surface,
);
}
}

View File

@@ -1,13 +1,19 @@
import 'dart:math';
import 'package:auto_route/auto_route.dart';
import 'package:fast_immutable_collections/fast_immutable_collections.dart';
import 'package:flutter/material.dart';
import 'package:flutter_staggered_grid_view/flutter_staggered_grid_view.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import '../../database/database.dart';
import '../../models/query.dart';
import '../../models/support.dart';
import '../../services/audio_service.dart';
import '../../state/music.dart';
import '../../state/settings.dart';
import '../app_router.dart';
import '../buttons.dart';
import '../images.dart';
import '../items.dart';
@@ -27,6 +33,26 @@ class ArtistPage extends HookConsumerWidget {
final albums = ref.watch(albumsByArtistIdProvider(id));
return Scaffold(
floatingActionButton: RadioPlayFab(
onPressed: () => artist.hasValue
? ref.read(audioControlProvider).playRadio(
context: QueueContextType.artist,
contextId: artist.valueOrNull!.id,
query: ListQuery(
filters: IList([
FilterWith.equals(
column: 'artist_id',
value: artist.valueOrNull!.id,
)
]),
),
getSongs: (query) => ref
.read(databaseProvider)
.songsList(ref.read(sourceIdProvider), query)
.get(),
)
: null,
),
body: CustomScrollView(
slivers: [
SliverToBoxAdapter(
@@ -92,10 +118,11 @@ class _Title extends StatelessWidget {
@override
Widget build(BuildContext context) {
final theme = Theme.of(context);
return Text(
text,
textAlign: TextAlign.center,
style: Theme.of(context).textTheme.displayMedium!.copyWith(
style: theme.textTheme.displayMedium!.copyWith(
fontWeight: FontWeight.bold,
color: Colors.white,
shadows: [

View File

@@ -46,6 +46,8 @@ class BottomNavTabsPage extends HookConsumerWidget {
@override
Widget build(BuildContext context, WidgetRef ref) {
final theme = Theme.of(context);
final observer = ref.watch(bottomTabObserverProvider);
const navElevation = 3.0;
@@ -63,8 +65,8 @@ class BottomNavTabsPage extends HookConsumerWidget {
return AnnotatedRegion<SystemUiOverlayStyle>(
value: SystemUiOverlayStyle.light.copyWith(
systemNavigationBarColor: ElevationOverlay.applySurfaceTint(
Theme.of(context).colorScheme.surface,
Theme.of(context).colorScheme.surfaceTint,
theme.colorScheme.surface,
theme.colorScheme.surfaceTint,
navElevation,
),
statusBarColor: Colors.transparent,
@@ -111,13 +113,13 @@ class OfflineIndicator extends HookConsumerWidget {
),
child: FilledButton.tonal(
style: const ButtonStyle(
padding: MaterialStatePropertyAll<EdgeInsetsGeometry>(
padding: WidgetStatePropertyAll<EdgeInsetsGeometry>(
EdgeInsets.zero,
),
fixedSize: MaterialStatePropertyAll<Size>(
fixedSize: WidgetStatePropertyAll<Size>(
Size(42, 42),
),
minimumSize: MaterialStatePropertyAll<Size>(
minimumSize: WidgetStatePropertyAll<Size>(
Size(42, 42),
),
),
@@ -136,7 +138,6 @@ class OfflineIndicator extends HookConsumerWidget {
padding: EdgeInsets.only(left: 2, bottom: 2),
child: Icon(
Icons.cloud_off_rounded,
// color: Theme.of(context).colorScheme.secondary,
size: 20,
),
),
@@ -154,6 +155,7 @@ class _BottomNavBar extends HookConsumerWidget {
@override
Widget build(BuildContext context, WidgetRef ref) {
final iconTheme = IconTheme.of(context);
final tabsRouter = AutoTabsRouter.of(context);
useListenableSelector(tabsRouter, () => tabsRouter.activeIndex);
@@ -190,9 +192,7 @@ class _BottomNavBar extends HookConsumerWidget {
return SvgPicture.asset(
'assets/tag_FILL0_wght400_GRAD0_opsz24.svg',
colorFilter: ColorFilter.mode(
IconTheme.of(context).color!.withOpacity(
IconTheme.of(context).opacity ?? 1,
),
iconTheme.color!.withOpacity(iconTheme.opacity ?? 1),
BlendMode.srcIn,
),
height: 28,

View File

@@ -53,4 +53,5 @@ final lastBottomNavStateServiceProvider =
);
typedef _$LastBottomNavStateService = AsyncNotifier<void>;
// ignore_for_file: unnecessary_raw_strings, subtype_of_sealed_class, invalid_use_of_internal_member, do_not_use_environment, prefer_const_constructors, public_member_api_docs, avoid_private_typedef_functions
// ignore_for_file: type=lint
// ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member

View File

@@ -249,6 +249,8 @@ class _Category extends HookConsumerWidget {
@override
Widget build(BuildContext context, WidgetRef ref) {
final theme = Theme.of(context);
return MultiSliver(
children: [
SliverToBoxAdapter(
@@ -256,7 +258,7 @@ class _Category extends HookConsumerWidget {
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 16),
child: Text(
title,
style: Theme.of(context).textTheme.headlineMedium,
style: theme.textTheme.headlineMedium,
),
),
),

View File

@@ -30,8 +30,6 @@ class _SystemHash {
}
}
typedef AlbumsCategoryListRef = AutoDisposeStreamProviderRef<List<Album>>;
/// See also [albumsCategoryList].
@ProviderFor(albumsCategoryList)
const albumsCategoryListProvider = AlbumsCategoryListFamily();
@@ -79,10 +77,10 @@ class AlbumsCategoryListProvider
extends AutoDisposeStreamProvider<List<Album>> {
/// See also [albumsCategoryList].
AlbumsCategoryListProvider(
this.opt,
) : super.internal(
ListQuery opt,
) : this._internal(
(ref) => albumsCategoryList(
ref,
ref as AlbumsCategoryListRef,
opt,
),
from: albumsCategoryListProvider,
@@ -94,10 +92,44 @@ class AlbumsCategoryListProvider
dependencies: AlbumsCategoryListFamily._dependencies,
allTransitiveDependencies:
AlbumsCategoryListFamily._allTransitiveDependencies,
opt: opt,
);
AlbumsCategoryListProvider._internal(
super._createNotifier, {
required super.name,
required super.dependencies,
required super.allTransitiveDependencies,
required super.debugGetCreateSourceHash,
required super.from,
required this.opt,
}) : super.internal();
final ListQuery opt;
@override
Override overrideWith(
Stream<List<Album>> Function(AlbumsCategoryListRef provider) create,
) {
return ProviderOverride(
origin: this,
override: AlbumsCategoryListProvider._internal(
(ref) => create(ref as AlbumsCategoryListRef),
from: from,
name: null,
dependencies: null,
allTransitiveDependencies: null,
debugGetCreateSourceHash: null,
opt: opt,
),
);
}
@override
AutoDisposeStreamProviderElement<List<Album>> createElement() {
return _AlbumsCategoryListProviderElement(this);
}
@override
bool operator ==(Object other) {
return other is AlbumsCategoryListProvider && other.opt == opt;
@@ -111,4 +143,19 @@ class AlbumsCategoryListProvider
return _SystemHash.finish(hash);
}
}
// ignore_for_file: unnecessary_raw_strings, subtype_of_sealed_class, invalid_use_of_internal_member, do_not_use_environment, prefer_const_constructors, public_member_api_docs, avoid_private_typedef_functions
mixin AlbumsCategoryListRef on AutoDisposeStreamProviderRef<List<Album>> {
/// The parameter `opt` of this provider.
ListQuery get opt;
}
class _AlbumsCategoryListProviderElement
extends AutoDisposeStreamProviderElement<List<Album>>
with AlbumsCategoryListRef {
_AlbumsCategoryListProviderElement(super.provider);
@override
ListQuery get opt => (origin as AlbumsCategoryListProvider).opt;
}
// ignore_for_file: type=lint
// ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member

View File

@@ -228,6 +228,8 @@ class _LibraryFilterFab extends HookConsumerWidget {
@override
Widget build(BuildContext context, WidgetRef ref) {
final theme = Theme.of(context);
final tabsRouter = AutoTabsRouter.of(context);
final activeIndex =
useListenableSelector(tabsRouter, () => tabsRouter.activeIndex);
@@ -242,7 +244,7 @@ class _LibraryFilterFab extends HookConsumerWidget {
end: 0,
child: Icon(
Icons.circle,
color: Theme.of(context).colorScheme.primaryContainer,
color: theme.colorScheme.primaryContainer,
size: 11,
),
),
@@ -449,6 +451,8 @@ class ListSortFilterOptions extends HookConsumerWidget {
@override
Widget build(BuildContext context, WidgetRef ref) {
final theme = Theme.of(context);
final list = ref.watch(libraryListQueryProvider(index));
return SliverList(
@@ -457,7 +461,7 @@ class ListSortFilterOptions extends HookConsumerWidget {
padding: const EdgeInsets.symmetric(horizontal: 16),
child: Text(
'Sort by',
style: Theme.of(context).textTheme.titleLarge,
style: theme.textTheme.titleLarge,
),
),
const SizedBox(height: 8),
@@ -480,7 +484,7 @@ class ListSortFilterOptions extends HookConsumerWidget {
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8),
child: Text(
'Filter',
style: Theme.of(context).textTheme.titleLarge,
style: theme.textTheme.titleLarge,
),
),
for (var column in list.options.filterColumns)

View File

@@ -60,8 +60,6 @@ class _SystemHash {
}
}
typedef LibraryListQueryRef = ProviderRef<LibraryListQuery>;
/// See also [libraryListQuery].
@ProviderFor(libraryListQuery)
const libraryListQueryProvider = LibraryListQueryFamily();
@@ -108,10 +106,10 @@ class LibraryListQueryFamily extends Family<LibraryListQuery> {
class LibraryListQueryProvider extends Provider<LibraryListQuery> {
/// See also [libraryListQuery].
LibraryListQueryProvider(
this.index,
) : super.internal(
int index,
) : this._internal(
(ref) => libraryListQuery(
ref,
ref as LibraryListQueryRef,
index,
),
from: libraryListQueryProvider,
@@ -123,10 +121,44 @@ class LibraryListQueryProvider extends Provider<LibraryListQuery> {
dependencies: LibraryListQueryFamily._dependencies,
allTransitiveDependencies:
LibraryListQueryFamily._allTransitiveDependencies,
index: index,
);
LibraryListQueryProvider._internal(
super._createNotifier, {
required super.name,
required super.dependencies,
required super.allTransitiveDependencies,
required super.debugGetCreateSourceHash,
required super.from,
required this.index,
}) : super.internal();
final int index;
@override
Override overrideWith(
LibraryListQuery Function(LibraryListQueryRef provider) create,
) {
return ProviderOverride(
origin: this,
override: LibraryListQueryProvider._internal(
(ref) => create(ref as LibraryListQueryRef),
from: from,
name: null,
dependencies: null,
allTransitiveDependencies: null,
debugGetCreateSourceHash: null,
index: index,
),
);
}
@override
ProviderElement<LibraryListQuery> createElement() {
return _LibraryListQueryProviderElement(this);
}
@override
bool operator ==(Object other) {
return other is LibraryListQueryProvider && other.index == index;
@@ -141,6 +173,19 @@ class LibraryListQueryProvider extends Provider<LibraryListQuery> {
}
}
mixin LibraryListQueryRef on ProviderRef<LibraryListQuery> {
/// The parameter `index` of this provider.
int get index;
}
class _LibraryListQueryProviderElement extends ProviderElement<LibraryListQuery>
with LibraryListQueryRef {
_LibraryListQueryProviderElement(super.provider);
@override
int get index => (origin as LibraryListQueryProvider).index;
}
String _$lastLibraryStateServiceHash() =>
r'a49e26b5dc0fcb0f697ec2def08e7336f64c4cb3';
@@ -173,4 +218,5 @@ final libraryListsProvider =
);
typedef _$LibraryLists = Notifier<IList<LibraryListQuery>>;
// ignore_for_file: unnecessary_raw_strings, subtype_of_sealed_class, invalid_use_of_internal_member, do_not_use_environment, prefer_const_constructors, public_member_api_docs, avoid_private_typedef_functions
// ignore_for_file: type=lint
// ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member

View File

@@ -29,8 +29,6 @@ class _SystemHash {
}
}
typedef SongsListRef = AutoDisposeFutureProviderRef<List<Song>>;
/// See also [songsList].
@ProviderFor(songsList)
const songsListProvider = SongsListFamily();
@@ -77,10 +75,10 @@ class SongsListFamily extends Family<AsyncValue<List<Song>>> {
class SongsListProvider extends AutoDisposeFutureProvider<List<Song>> {
/// See also [songsList].
SongsListProvider(
this.opt,
) : super.internal(
ListQuery opt,
) : this._internal(
(ref) => songsList(
ref,
ref as SongsListRef,
opt,
),
from: songsListProvider,
@@ -91,10 +89,44 @@ class SongsListProvider extends AutoDisposeFutureProvider<List<Song>> {
: _$songsListHash,
dependencies: SongsListFamily._dependencies,
allTransitiveDependencies: SongsListFamily._allTransitiveDependencies,
opt: opt,
);
SongsListProvider._internal(
super._createNotifier, {
required super.name,
required super.dependencies,
required super.allTransitiveDependencies,
required super.debugGetCreateSourceHash,
required super.from,
required this.opt,
}) : super.internal();
final ListQuery opt;
@override
Override overrideWith(
FutureOr<List<Song>> Function(SongsListRef provider) create,
) {
return ProviderOverride(
origin: this,
override: SongsListProvider._internal(
(ref) => create(ref as SongsListRef),
from: from,
name: null,
dependencies: null,
allTransitiveDependencies: null,
debugGetCreateSourceHash: null,
opt: opt,
),
);
}
@override
AutoDisposeFutureProviderElement<List<Song>> createElement() {
return _SongsListProviderElement(this);
}
@override
bool operator ==(Object other) {
return other is SongsListProvider && other.opt == opt;
@@ -108,4 +140,18 @@ class SongsListProvider extends AutoDisposeFutureProvider<List<Song>> {
return _SystemHash.finish(hash);
}
}
// ignore_for_file: unnecessary_raw_strings, subtype_of_sealed_class, invalid_use_of_internal_member, do_not_use_environment, prefer_const_constructors, public_member_api_docs, avoid_private_typedef_functions
mixin SongsListRef on AutoDisposeFutureProviderRef<List<Song>> {
/// The parameter `opt` of this provider.
ListQuery get opt;
}
class _SongsListProviderElement
extends AutoDisposeFutureProviderElement<List<Song>> with SongsListRef {
_SongsListProviderElement(super.provider);
@override
ListQuery get opt => (origin as SongsListProvider).opt;
}
// ignore_for_file: type=lint
// ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member

View File

@@ -55,13 +55,13 @@ class NowPlayingPage extends HookConsumerWidget {
],
),
),
body: Stack(
body: const Stack(
children: [
const MediaItemGradient(),
MediaItemGradient(),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 8),
padding: EdgeInsets.symmetric(horizontal: 8, vertical: 8),
child: Column(
children: const [
children: [
Expanded(
child: Padding(
padding: EdgeInsets.symmetric(horizontal: 16),
@@ -215,6 +215,8 @@ class _Progress extends HookConsumerWidget {
@override
Widget build(BuildContext context, WidgetRef ref) {
final theme = Theme.of(context);
final colors = ref.watch(mediaItemThemeProvider).valueOrNull;
final position = ref.watch(positionProvider);
final duration = ref.watch(durationProvider);
@@ -229,8 +231,8 @@ class _Progress extends HookConsumerWidget {
value: changing.value ? changeValue.value : position.toDouble(),
min: 0,
max: max(duration.toDouble(), position.toDouble()),
thumbColor: colors?.theme.colorScheme.onBackground,
activeColor: colors?.theme.colorScheme.onBackground,
thumbColor: colors?.theme.colorScheme.surface,
activeColor: colors?.theme.colorScheme.surface,
inactiveColor: colors?.theme.colorScheme.surface,
onChanged: (value) {
changeValue.value = value;
@@ -246,7 +248,7 @@ class _Progress extends HookConsumerWidget {
Padding(
padding: const EdgeInsets.symmetric(horizontal: 24),
child: DefaultTextStyle(
style: Theme.of(context).textTheme.titleMedium!,
style: theme.textTheme.titleMedium!,
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
@@ -354,7 +356,7 @@ class _Controls extends HookConsumerWidget {
final audio = ref.watch(audioControlProvider);
return IconTheme(
data: IconThemeData(color: base.theme.colorScheme.onBackground),
data: IconThemeData(color: base.theme.colorScheme.surface),
child: Column(
children: [
SizedBox(

View File

@@ -131,11 +131,11 @@ class _SectionHeader extends HookConsumerWidget {
@override
Widget build(BuildContext context, WidgetRef ref) {
final theme = Theme.of(context).textTheme;
final theme = Theme.of(context);
return Padding(
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8),
child: Text(title, style: theme.headlineMedium),
child: Text(title, style: theme.textTheme.headlineMedium),
);
}
}

View File

@@ -35,4 +35,5 @@ final searchQueryProvider =
);
typedef _$SearchQuery = AutoDisposeNotifier<String?>;
// ignore_for_file: unnecessary_raw_strings, subtype_of_sealed_class, invalid_use_of_internal_member, do_not_use_environment, prefer_const_constructors, public_member_api_docs, avoid_private_typedef_functions
// ignore_for_file: type=lint
// ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member

View File

@@ -1,11 +1,16 @@
import 'dart:math';
import 'package:auto_route/auto_route.dart';
import 'package:fast_immutable_collections/fast_immutable_collections.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:path/path.dart' as p;
import 'package:share_plus/share_plus.dart';
import 'package:url_launcher/url_launcher.dart';
import '../../log.dart';
import '../../models/support.dart';
import '../../services/settings_service.dart';
import '../../state/init.dart';
@@ -162,6 +167,54 @@ class _About extends HookConsumerWidget {
mode: LaunchMode.externalApplication,
),
),
const SizedBox(height: 12),
const _ShareLogsButton(),
],
);
}
}
class _ShareLogsButton extends StatelessWidget {
const _ShareLogsButton();
@override
Widget build(BuildContext context) {
final l = AppLocalizations.of(context);
return Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
OutlinedButton.icon(
icon: const Icon(Icons.share),
label: Text(l.settingsAboutShareLogs),
onPressed: () async {
final files = await logFiles();
if (files.isEmpty) return;
if (!context.mounted) return;
final value = await showDialog<String>(
context: context,
builder: (context) => MultipleChoiceDialog<String>(
title: l.settingsAboutChooseLog,
current: files.first.path,
options: files
.map((e) => MultiChoiceOption.string(
title: p.basename(e.path),
option: e.path,
))
.toIList(),
),
);
if (value == null) return;
Share.shareXFiles(
[XFile(value, mimeType: 'text/plain')],
subject: 'Logs from subtracks: ${String.fromCharCodes(
List.generate(8, (_) => Random().nextInt(26) + 65),
)}',
);
},
),
],
);
}

View File

@@ -8,9 +8,11 @@ import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import '../../database/database.dart';
import '../../log.dart';
import '../../models/settings.dart';
import '../../services/settings_service.dart';
import '../items.dart';
import '../snackbars.dart';
class SourcePage extends HookConsumerWidget {
final int? id;
@@ -41,9 +43,10 @@ class SourcePage extends HookConsumerWidget {
label: l.settingsServersFieldsAddress,
initialValue: source?.address.toString(),
keyboardType: TextInputType.url,
autofillHints: const [AutofillHints.url],
required: true,
validator: (value, label) {
if (Uri.tryParse(value!) == null) {
if (!value!.contains(RegExp(r'https?:\/\/'))) {
return '$label must be a valid URL';
}
return null;
@@ -52,17 +55,29 @@ class SourcePage extends HookConsumerWidget {
final username = LabeledTextField(
label: l.settingsServersFieldsUsername,
initialValue: source?.username,
autofillHints: const [AutofillHints.username],
required: true,
);
final password = LabeledTextField(
label: l.settingsServersFieldsPassword,
initialValue: source?.password,
obscureText: true,
autofillHints: const [AutofillHints.password],
required: true,
);
return WillPopScope(
onWillPop: () async => !isSaving.value && !isDeleting.value,
final forcePlaintextPassword = useState(!(source?.useTokenAuth ?? true));
final forcePlaintextSwitch = SwitchListTile(
value: forcePlaintextPassword.value,
title: Text(l.settingsServersOptionsForcePlaintextPasswordTitle),
subtitle: forcePlaintextPassword.value
? Text(l.settingsServersOptionsForcePlaintextPasswordDescriptionOn)
: Text(l.settingsServersOptionsForcePlaintextPasswordDescriptionOff),
onChanged: (value) => forcePlaintextPassword.value = value,
);
return PopScope(
canPop: !isSaving.value && !isDeleting.value,
child: Scaffold(
appBar: AppBar(),
floatingActionButton: Row(
@@ -128,6 +143,7 @@ class SourcePage extends HookConsumerWidget {
address: Uri.parse(address.value),
username: username.value,
password: password.value,
useTokenAuth: !forcePlaintextPassword.value,
),
);
} else {
@@ -142,12 +158,15 @@ class SourcePage extends HookConsumerWidget {
features: IList(),
username: username.value,
password: password.value,
useTokenAuth: const Value(true),
useTokenAuth:
Value(!forcePlaintextPassword.value),
),
);
}
} catch (err) {
// TOOD: toast the error or whatever
} catch (e, st) {
if (!context.mounted) return;
showErrorSnackbar(context, e.toString());
log.severe('Saving source', e, st);
error = true;
} finally {
isSaving.value = false;
@@ -163,21 +182,25 @@ class SourcePage extends HookConsumerWidget {
),
body: Form(
key: form,
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 24),
child: AutofillGroup(
child: ListView(
children: [
const SizedBox(height: 96 - kToolbarHeight),
Text(
source == null
? l.settingsServersActionsAdd
: l.settingsServersActionsEdit,
style: theme.textTheme.displaySmall,
Padding(
padding: const EdgeInsets.symmetric(horizontal: 16),
child: Text(
source == null
? l.settingsServersActionsAdd
: l.settingsServersActionsEdit,
style: theme.textTheme.displaySmall,
),
),
name,
address,
username,
password,
const SizedBox(height: 24),
forcePlaintextSwitch,
const FabPadding(),
],
),
@@ -194,6 +217,7 @@ class LabeledTextField extends HookConsumerWidget {
final bool obscureText;
final bool required;
final TextInputType? keyboardType;
final Iterable<String>? autofillHints;
final String? Function(String? value, String label)? validator;
// ignore: prefer_const_constructors_in_immutables
@@ -204,6 +228,7 @@ class LabeledTextField extends HookConsumerWidget {
this.obscureText = false,
this.keyboardType,
this.validator,
this.autofillHints,
this.required = false,
});
@@ -224,35 +249,36 @@ class LabeledTextField extends HookConsumerWidget {
final theme = Theme.of(context);
_controller = useTextEditingController(text: initialValue);
return Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
const SizedBox(height: 24),
Text(
label,
style: theme.textTheme.titleMedium,
),
TextFormField(
controller: _controller,
obscureText: obscureText,
keyboardType: keyboardType,
validator: (value) {
String? error;
return Padding(
padding: const EdgeInsets.symmetric(horizontal: 16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
const SizedBox(height: 24),
Text(label, style: theme.textTheme.titleMedium),
TextFormField(
controller: _controller,
obscureText: obscureText,
keyboardType: keyboardType,
autofillHints: autofillHints,
validator: (value) {
String? error;
if (required) {
error = _requiredValidator(value);
if (error != null) {
return error;
if (required) {
error = _requiredValidator(value);
if (error != null) {
return error;
}
}
}
if (validator != null) {
return validator!(value, label);
}
return null;
},
),
],
if (validator != null) {
return validator!(value, label);
}
return null;
},
),
],
),
);
}
}

14
lib/app/snackbars.dart Normal file
View File

@@ -0,0 +1,14 @@
import 'package:flutter/material.dart';
void showErrorSnackbar(BuildContext context, String message) {
final colors = Theme.of(context).colorScheme;
ScaffoldMessenger.of(context).showSnackBar(SnackBar(
content: Text(message, style: TextStyle(color: colors.onErrorContainer)),
backgroundColor: colors.errorContainer,
showCloseIcon: true,
closeIconColor: colors.onErrorContainer,
behavior: SnackBarBehavior.floating,
duration: const Duration(seconds: 10),
));
}

View File

@@ -20,4 +20,5 @@ final imageCacheProvider = Provider<CacheManager>.internal(
);
typedef ImageCacheRef = ProviderRef<CacheManager>;
// ignore_for_file: unnecessary_raw_strings, subtype_of_sealed_class, invalid_use_of_internal_member, do_not_use_environment, prefer_const_constructors, public_member_api_docs, avoid_private_typedef_functions
// ignore_for_file: type=lint
// ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member

View File

@@ -9,14 +9,20 @@ import 'package:path/path.dart' as p;
import 'package:path_provider/path_provider.dart';
import 'package:riverpod_annotation/riverpod_annotation.dart';
import '../log.dart';
import '../models/music.dart';
import '../models/query.dart';
import '../models/settings.dart';
import '../models/support.dart';
import 'converters.dart';
import 'error_logging_database.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() : super(_openConnection());
@@ -169,12 +175,27 @@ class SubtracksDatabase extends _$SubtracksDatabase {
});
}
Future<void> deleteArtistsNotIn(int sourceId, Iterable<String> ids) async {
await (delete(artists)
..where(
(tbl) => tbl.sourceId.equals(sourceId) & tbl.id.isNotIn(ids),
))
.go();
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 {
@@ -183,15 +204,27 @@ class SubtracksDatabase extends _$SubtracksDatabase {
});
}
Future<void> deleteAlbumsNotIn(int sourceId, Iterable<String> ids) async {
final alsoKeep = (await albumIdsWithDownloaded(sourceId).get()).toSet();
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();
ids = ids.toList()..addAll(alsoKeep);
await (delete(albums)
..where(
(tbl) => tbl.sourceId.equals(sourceId) & tbl.id.isNotIn(ids),
))
.go();
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(
@@ -215,18 +248,31 @@ class SubtracksDatabase extends _$SubtracksDatabase {
});
}
Future<void> deletePlaylistsNotIn(int sourceId, Iterable<String> ids) async {
await (delete(playlists)
..where(
(tbl) => tbl.sourceId.equals(sourceId) & tbl.id.isNotIn(ids),
))
.go();
await (delete(playlistSongs)
..where(
(tbl) =>
tbl.sourceId.equals(sourceId) & tbl.playlistId.isNotIn(ids),
))
.go();
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(
@@ -250,29 +296,33 @@ class SubtracksDatabase extends _$SubtracksDatabase {
});
}
Future<void> deleteSongsNotIn(int sourceId, Iterable<String> ids) async {
await (delete(songs)
..where(
(tbl) =>
tbl.sourceId.equals(sourceId) &
tbl.id.isNotIn(ids) &
tbl.downloadFilePath.isNull() &
tbl.downloadTaskId.isNull(),
))
.go();
final remainingIds = (await (selectOnly(songs)
..addColumns([songs.id])
..where(songs.sourceId.equals(sourceId)))
.map((row) => row.read(songs.id))
.get())
.whereNotNull();
await (delete(playlistSongs)
..where(
(tbl) =>
tbl.sourceId.equals(sourceId) &
tbl.songId.isNotIn(remainingIds),
))
.go();
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() {
@@ -387,7 +437,11 @@ LazyDatabase _openConnection() {
final dbFolder = await getApplicationDocumentsDirectory();
final file = File(p.join(dbFolder.path, 'subtracks.sqlite'));
// return NativeDatabase.createInBackground(file, logStatements: true);
return NativeDatabase.createInBackground(file);
return ErrorLoggingDatabase(
NativeDatabase.createInBackground(file),
(e, s) => log.severe('SQL error', e, s),
);
});
}

View File

@@ -53,9 +53,10 @@ class Queue extends Table with TableInfo<Queue, QueueData> {
List<GeneratedColumn> get $columns =>
[index, sourceId, id, context, contextId, currentTrack];
@override
String get aliasedName => _alias ?? 'queue';
String get aliasedName => _alias ?? actualTableName;
@override
String get actualTableName => 'queue';
String get actualTableName => $name;
static const String $name = 'queue';
@override
VerificationContext validateIntegrity(Insertable<QueueData> instance,
{bool isInserting = false}) {
@@ -144,8 +145,7 @@ class QueueData extends DataClass implements Insertable<QueueData> {
map['source_id'] = Variable<int>(sourceId);
map['id'] = Variable<String>(id);
{
final converter = Queue.$convertercontext;
map['context'] = Variable<String>(converter.toSql(context));
map['context'] = Variable<String>(Queue.$convertercontext.toSql(context));
}
if (!nullToAbsent || contextId != null) {
map['context_id'] = Variable<String>(contextId);
@@ -315,8 +315,8 @@ class QueueCompanion extends UpdateCompanion<QueueData> {
map['id'] = Variable<String>(id.value);
}
if (context.present) {
final converter = Queue.$convertercontext;
map['context'] = Variable<String>(converter.toSql(context.value));
map['context'] =
Variable<String>(Queue.$convertercontext.toSql(context.value));
}
if (contextId.present) {
map['context_id'] = Variable<String>(contextId.value);
@@ -382,9 +382,10 @@ class LastAudioState extends Table
List<GeneratedColumn> get $columns =>
[id, queueMode, shuffleIndicies, repeat];
@override
String get aliasedName => _alias ?? 'last_audio_state';
String get aliasedName => _alias ?? actualTableName;
@override
String get actualTableName => 'last_audio_state';
String get actualTableName => $name;
static const String $name = 'last_audio_state';
@override
VerificationContext validateIntegrity(Insertable<LastAudioStateData> instance,
{bool isInserting = false}) {
@@ -452,17 +453,16 @@ class LastAudioStateData extends DataClass
final map = <String, Expression>{};
map['id'] = Variable<int>(id);
{
final converter = LastAudioState.$converterqueueMode;
map['queue_mode'] = Variable<int>(converter.toSql(queueMode));
map['queue_mode'] =
Variable<int>(LastAudioState.$converterqueueMode.toSql(queueMode));
}
if (!nullToAbsent || shuffleIndicies != null) {
final converter = LastAudioState.$convertershuffleIndiciesn;
map['shuffle_indicies'] =
Variable<String>(converter.toSql(shuffleIndicies));
map['shuffle_indicies'] = Variable<String>(
LastAudioState.$convertershuffleIndiciesn.toSql(shuffleIndicies));
}
{
final converter = LastAudioState.$converterrepeat;
map['repeat'] = Variable<int>(converter.toSql(repeat));
map['repeat'] =
Variable<int>(LastAudioState.$converterrepeat.toSql(repeat));
}
return map;
}
@@ -592,17 +592,17 @@ class LastAudioStateCompanion extends UpdateCompanion<LastAudioStateData> {
map['id'] = Variable<int>(id.value);
}
if (queueMode.present) {
final converter = LastAudioState.$converterqueueMode;
map['queue_mode'] = Variable<int>(converter.toSql(queueMode.value));
map['queue_mode'] = Variable<int>(
LastAudioState.$converterqueueMode.toSql(queueMode.value));
}
if (shuffleIndicies.present) {
final converter = LastAudioState.$convertershuffleIndiciesn;
map['shuffle_indicies'] =
Variable<String>(converter.toSql(shuffleIndicies.value));
map['shuffle_indicies'] = Variable<String>(LastAudioState
.$convertershuffleIndiciesn
.toSql(shuffleIndicies.value));
}
if (repeat.present) {
final converter = LastAudioState.$converterrepeat;
map['repeat'] = Variable<int>(converter.toSql(repeat.value));
map['repeat'] =
Variable<int>(LastAudioState.$converterrepeat.toSql(repeat.value));
}
return map;
}
@@ -640,9 +640,10 @@ class LastBottomNavState extends Table
@override
List<GeneratedColumn> get $columns => [id, tab];
@override
String get aliasedName => _alias ?? 'last_bottom_nav_state';
String get aliasedName => _alias ?? actualTableName;
@override
String get actualTableName => 'last_bottom_nav_state';
String get actualTableName => $name;
static const String $name = 'last_bottom_nav_state';
@override
VerificationContext validateIntegrity(
Insertable<LastBottomNavStateData> instance,
@@ -849,9 +850,10 @@ class LastLibraryState extends Table
List<GeneratedColumn> get $columns =>
[id, tab, albumsList, artistsList, playlistsList, songsList];
@override
String get aliasedName => _alias ?? 'last_library_state';
String get aliasedName => _alias ?? actualTableName;
@override
String get actualTableName => 'last_library_state';
String get actualTableName => $name;
static const String $name = 'last_library_state';
@override
VerificationContext validateIntegrity(
Insertable<LastLibraryStateData> instance,
@@ -937,20 +939,20 @@ class LastLibraryStateData extends DataClass
map['id'] = Variable<int>(id);
map['tab'] = Variable<String>(tab);
{
final converter = LastLibraryState.$converteralbumsList;
map['albums_list'] = Variable<String>(converter.toSql(albumsList));
map['albums_list'] = Variable<String>(
LastLibraryState.$converteralbumsList.toSql(albumsList));
}
{
final converter = LastLibraryState.$converterartistsList;
map['artists_list'] = Variable<String>(converter.toSql(artistsList));
map['artists_list'] = Variable<String>(
LastLibraryState.$converterartistsList.toSql(artistsList));
}
{
final converter = LastLibraryState.$converterplaylistsList;
map['playlists_list'] = Variable<String>(converter.toSql(playlistsList));
map['playlists_list'] = Variable<String>(
LastLibraryState.$converterplaylistsList.toSql(playlistsList));
}
{
final converter = LastLibraryState.$convertersongsList;
map['songs_list'] = Variable<String>(converter.toSql(songsList));
map['songs_list'] = Variable<String>(
LastLibraryState.$convertersongsList.toSql(songsList));
}
return map;
}
@@ -1106,22 +1108,20 @@ class LastLibraryStateCompanion extends UpdateCompanion<LastLibraryStateData> {
map['tab'] = Variable<String>(tab.value);
}
if (albumsList.present) {
final converter = LastLibraryState.$converteralbumsList;
map['albums_list'] = Variable<String>(converter.toSql(albumsList.value));
map['albums_list'] = Variable<String>(
LastLibraryState.$converteralbumsList.toSql(albumsList.value));
}
if (artistsList.present) {
final converter = LastLibraryState.$converterartistsList;
map['artists_list'] =
Variable<String>(converter.toSql(artistsList.value));
map['artists_list'] = Variable<String>(
LastLibraryState.$converterartistsList.toSql(artistsList.value));
}
if (playlistsList.present) {
final converter = LastLibraryState.$converterplaylistsList;
map['playlists_list'] =
Variable<String>(converter.toSql(playlistsList.value));
map['playlists_list'] = Variable<String>(
LastLibraryState.$converterplaylistsList.toSql(playlistsList.value));
}
if (songsList.present) {
final converter = LastLibraryState.$convertersongsList;
map['songs_list'] = Variable<String>(converter.toSql(songsList.value));
map['songs_list'] = Variable<String>(
LastLibraryState.$convertersongsList.toSql(songsList.value));
}
return map;
}
@@ -1177,9 +1177,10 @@ class AppSettingsTable extends Table
List<GeneratedColumn> get $columns =>
[id, maxBitrateWifi, maxBitrateMobile, streamFormat];
@override
String get aliasedName => _alias ?? 'app_settings';
String get aliasedName => _alias ?? actualTableName;
@override
String get actualTableName => 'app_settings';
String get actualTableName => $name;
static const String $name = 'app_settings';
@override
VerificationContext validateIntegrity(Insertable<AppSettings> instance,
{bool isInserting = false}) {
@@ -1359,9 +1360,10 @@ class Sources extends Table with TableInfo<Sources, Source> {
List<GeneratedColumn> get $columns =>
[id, name, address, isActive, createdAt];
@override
String get aliasedName => _alias ?? 'sources';
String get aliasedName => _alias ?? actualTableName;
@override
String get actualTableName => 'sources';
String get actualTableName => $name;
static const String $name = 'sources';
@override
VerificationContext validateIntegrity(Insertable<Source> instance,
{bool isInserting = false}) {
@@ -1435,8 +1437,8 @@ class Source extends DataClass implements Insertable<Source> {
map['id'] = Variable<int>(id);
map['name'] = Variable<String>(name);
{
final converter = Sources.$converteraddress;
map['address'] = Variable<String>(converter.toSql(address));
map['address'] =
Variable<String>(Sources.$converteraddress.toSql(address));
}
if (!nullToAbsent || isActive != null) {
map['is_active'] = Variable<bool>(isActive);
@@ -1580,8 +1582,8 @@ class SourcesCompanion extends UpdateCompanion<Source> {
map['name'] = Variable<String>(name.value);
}
if (address.present) {
final converter = Sources.$converteraddress;
map['address'] = Variable<String>(converter.toSql(address.value));
map['address'] =
Variable<String>(Sources.$converteraddress.toSql(address.value));
}
if (isActive.present) {
map['is_active'] = Variable<bool>(isActive.value);
@@ -1653,9 +1655,10 @@ class SubsonicSources extends Table
List<GeneratedColumn> get $columns =>
[sourceId, features, username, password, useTokenAuth];
@override
String get aliasedName => _alias ?? 'subsonic_sources';
String get aliasedName => _alias ?? actualTableName;
@override
String get actualTableName => 'subsonic_sources';
String get actualTableName => $name;
static const String $name = 'subsonic_sources';
@override
VerificationContext validateIntegrity(Insertable<SubsonicSource> instance,
{bool isInserting = false}) {
@@ -1738,8 +1741,8 @@ class SubsonicSource extends DataClass implements Insertable<SubsonicSource> {
final map = <String, Expression>{};
map['source_id'] = Variable<int>(sourceId);
{
final converter = SubsonicSources.$converterfeatures;
map['features'] = Variable<String>(converter.toSql(features));
map['features'] =
Variable<String>(SubsonicSources.$converterfeatures.toSql(features));
}
map['username'] = Variable<String>(username);
map['password'] = Variable<String>(password);
@@ -1879,8 +1882,8 @@ class SubsonicSourcesCompanion extends UpdateCompanion<SubsonicSource> {
map['source_id'] = Variable<int>(sourceId.value);
}
if (features.present) {
final converter = SubsonicSources.$converterfeatures;
map['features'] = Variable<String>(converter.toSql(features.value));
map['features'] = Variable<String>(
SubsonicSources.$converterfeatures.toSql(features.value));
}
if (username.present) {
map['username'] = Variable<String>(username.value);
@@ -1959,9 +1962,10 @@ class Artists extends Table with TableInfo<Artists, Artist> {
List<GeneratedColumn> get $columns =>
[sourceId, id, name, albumCount, starred, updated];
@override
String get aliasedName => _alias ?? 'artists';
String get aliasedName => _alias ?? actualTableName;
@override
String get actualTableName => 'artists';
String get actualTableName => $name;
static const String $name = 'artists';
@override
VerificationContext validateIntegrity(Insertable<Artist> instance,
{bool isInserting = false}) {
@@ -2170,9 +2174,10 @@ class ArtistsFts extends Table
@override
List<GeneratedColumn> get $columns => [sourceId, name];
@override
String get aliasedName => _alias ?? 'artists_fts';
String get aliasedName => _alias ?? actualTableName;
@override
String get actualTableName => 'artists_fts';
String get actualTableName => $name;
static const String $name = 'artists_fts';
@override
VerificationContext validateIntegrity(Insertable<ArtistsFt> instance,
{bool isInserting = false}) {
@@ -2468,9 +2473,10 @@ class Albums extends Table with TableInfo<Albums, Album> {
updated
];
@override
String get aliasedName => _alias ?? 'albums';
String get aliasedName => _alias ?? actualTableName;
@override
String get actualTableName => 'albums';
String get actualTableName => $name;
static const String $name = 'albums';
@override
VerificationContext validateIntegrity(Insertable<Album> instance,
{bool isInserting = false}) {
@@ -2837,9 +2843,10 @@ class AlbumsFts extends Table
@override
List<GeneratedColumn> get $columns => [sourceId, name];
@override
String get aliasedName => _alias ?? 'albums_fts';
String get aliasedName => _alias ?? actualTableName;
@override
String get actualTableName => 'albums_fts';
String get actualTableName => $name;
static const String $name = 'albums_fts';
@override
VerificationContext validateIntegrity(Insertable<AlbumsFt> instance,
{bool isInserting = false}) {
@@ -3072,9 +3079,10 @@ class Playlists extends Table with TableInfo<Playlists, Playlist> {
List<GeneratedColumn> get $columns =>
[sourceId, id, name, comment, coverArt, songCount, created, updated];
@override
String get aliasedName => _alias ?? 'playlists';
String get aliasedName => _alias ?? actualTableName;
@override
String get actualTableName => 'playlists';
String get actualTableName => $name;
static const String $name = 'playlists';
@override
VerificationContext validateIntegrity(Insertable<Playlist> instance,
{bool isInserting = false}) {
@@ -3340,9 +3348,10 @@ class PlaylistSongs extends Table with TableInfo<PlaylistSongs, PlaylistSong> {
List<GeneratedColumn> get $columns =>
[sourceId, playlistId, songId, position, updated];
@override
String get aliasedName => _alias ?? 'playlist_songs';
String get aliasedName => _alias ?? actualTableName;
@override
String get actualTableName => 'playlist_songs';
String get actualTableName => $name;
static const String $name = 'playlist_songs';
@override
VerificationContext validateIntegrity(Insertable<PlaylistSong> instance,
{bool isInserting = false}) {
@@ -3632,9 +3641,10 @@ class PlaylistsFts extends Table
@override
List<GeneratedColumn> get $columns => [sourceId, name];
@override
String get aliasedName => _alias ?? 'playlists_fts';
String get aliasedName => _alias ?? actualTableName;
@override
String get actualTableName => 'playlists_fts';
String get actualTableName => $name;
static const String $name = 'playlists_fts';
@override
VerificationContext validateIntegrity(Insertable<PlaylistsFt> instance,
{bool isInserting = false}) {
@@ -3936,9 +3946,10 @@ class Songs extends Table with TableInfo<Songs, Song> {
updated
];
@override
String get aliasedName => _alias ?? 'songs';
String get aliasedName => _alias ?? actualTableName;
@override
String get actualTableName => 'songs';
String get actualTableName => $name;
static const String $name = 'songs';
@override
VerificationContext validateIntegrity(Insertable<Song> instance,
{bool isInserting = false}) {
@@ -4236,8 +4247,8 @@ class SongsCompanion extends UpdateCompanion<Song> {
map['artist'] = Variable<String>(artist.value);
}
if (duration.present) {
final converter = Songs.$converterdurationn;
map['duration'] = Variable<int>(converter.toSql(duration.value));
map['duration'] =
Variable<int>(Songs.$converterdurationn.toSql(duration.value));
}
if (track.present) {
map['track'] = Variable<int>(track.value);
@@ -4316,9 +4327,10 @@ class SongsFts extends Table
@override
List<GeneratedColumn> get $columns => [sourceId, title];
@override
String get aliasedName => _alias ?? 'songs_fts';
String get aliasedName => _alias ?? actualTableName;
@override
String get actualTableName => 'songs_fts';
String get actualTableName => $name;
static const String $name = 'songs_fts';
@override
VerificationContext validateIntegrity(Insertable<SongsFt> instance,
{bool isInserting = false}) {
@@ -4596,7 +4608,7 @@ abstract class _$SubtracksDatabase extends GeneratedDatabase {
));
}
Selectable<String> albumIdsWithDownloaded(int sourceId) {
Selectable<String> albumIdsWithDownloadStatus(int sourceId) {
return customSelect(
'SELECT albums.id FROM albums JOIN songs ON songs.source_id = albums.source_id AND songs.album_id = albums.id WHERE albums.source_id = ?1 AND(songs.download_file_path IS NOT NULL OR songs.download_task_id IS NOT NULL)GROUP BY albums.id',
variables: [
@@ -4608,6 +4620,32 @@ abstract class _$SubtracksDatabase extends GeneratedDatabase {
}).map((QueryRow row) => row.read<String>('id'));
}
Selectable<String> artistIdsWithDownloadStatus(int sourceId) {
return customSelect(
'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 = ?1 AND(songs.download_file_path IS NOT NULL OR songs.download_task_id IS NOT NULL)GROUP BY artists.id',
variables: [
Variable<int>(sourceId)
],
readsFrom: {
artists,
albums,
songs,
}).map((QueryRow row) => row.read<String>('id'));
}
Selectable<String> playlistIdsWithDownloadStatus(int sourceId) {
return customSelect(
'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 = ?1 AND(songs.download_file_path IS NOT NULL OR songs.download_task_id IS NOT NULL)GROUP BY playlists.id',
variables: [
Variable<int>(sourceId)
],
readsFrom: {
playlists,
playlistSongs,
songs,
}).map((QueryRow row) => row.read<String>('id'));
}
Selectable<int> searchArtists(String query, int limit, int offset) {
return customSelect(
'SELECT "rowid" FROM artists_fts WHERE artists_fts MATCH ?1 ORDER BY rank LIMIT ?2 OFFSET ?3',
@@ -5544,4 +5582,5 @@ final databaseProvider = Provider<SubtracksDatabase>.internal(
);
typedef DatabaseRef = ProviderRef<SubtracksDatabase>;
// ignore_for_file: unnecessary_raw_strings, subtype_of_sealed_class, invalid_use_of_internal_member, do_not_use_environment, prefer_const_constructors, public_member_api_docs, avoid_private_typedef_functions
// ignore_for_file: type=lint
// ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member

View File

@@ -0,0 +1,94 @@
import 'dart:async';
import 'package:drift/drift.dart';
import 'package:drift/isolate.dart';
/// https://github.com/simolus3/drift/issues/2326#issuecomment-1445138730
class ErrorLoggingDatabase implements QueryExecutor {
final QueryExecutor inner;
final void Function(Object, StackTrace) onError;
ErrorLoggingDatabase(this.inner, this.onError);
Future<T> _handleErrors<T>(Future<T> Function() body) {
return Future.sync(body)
.onError<DriftWrappedException>((error, stackTrace) {
onError(error, error.trace ?? stackTrace);
throw error;
}).onError<DriftRemoteException>((error, stackTrace) {
onError(error, error.remoteStackTrace ?? stackTrace);
throw error;
});
}
@override
TransactionExecutor beginTransaction() {
return _ErrorLoggingTransactionExecutor(inner.beginTransaction(), onError);
}
@override
Future<void> close() {
return _handleErrors(inner.close);
}
@override
SqlDialect get dialect => inner.dialect;
@override
Future<bool> ensureOpen(QueryExecutorUser user) {
return _handleErrors(() => inner.ensureOpen(user));
}
@override
Future<void> runBatched(BatchedStatements statements) {
return _handleErrors(() => inner.runBatched(statements));
}
@override
Future<void> runCustom(String statement, [List<Object?>? args]) {
return _handleErrors(() => inner.runCustom(statement, args));
}
@override
Future<int> runDelete(String statement, List<Object?> args) {
return _handleErrors(() => inner.runDelete(statement, args));
}
@override
Future<int> runInsert(String statement, List<Object?> args) {
return _handleErrors(() => inner.runInsert(statement, args));
}
@override
Future<List<Map<String, Object?>>> runSelect(
String statement, List<Object?> args) {
return _handleErrors(() => inner.runSelect(statement, args));
}
@override
Future<int> runUpdate(String statement, List<Object?> args) {
return _handleErrors(() => inner.runUpdate(statement, args));
}
}
class _ErrorLoggingTransactionExecutor extends ErrorLoggingDatabase
implements TransactionExecutor {
final TransactionExecutor transaction;
_ErrorLoggingTransactionExecutor(
this.transaction, void Function(Object, StackTrace) onError)
: super(transaction, onError);
@override
Future<void> rollback() {
return _handleErrors(transaction.rollback);
}
@override
Future<void> send() {
return _handleErrors(transaction.send);
}
@override
bool get supportsNestedTransactions => transaction.supportsNestedTransactions;
}

View File

@@ -244,7 +244,7 @@ allSubsonicSources WITH SubsonicSettings:
FROM sources
JOIN subsonic_sources ON subsonic_sources.source_id = sources.id;
albumIdsWithDownloaded:
albumIdsWithDownloadStatus:
SELECT albums.id
FROM albums
JOIN songs on songs.source_id = albums.source_id AND songs.album_id = albums.id
@@ -253,6 +253,26 @@ albumIdsWithDownloaded:
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

View File

@@ -1,7 +1,8 @@
import 'package:flutter/foundation.dart';
import 'package:http/http.dart';
import 'package:riverpod_annotation/riverpod_annotation.dart';
import '../log.dart';
part 'client.g.dart';
const Map<String, String> subtracksHeaders = {
@@ -14,8 +15,14 @@ class SubtracksHttpClient extends BaseClient {
@override
Future<StreamedResponse> send(BaseRequest request) {
request.headers.addAll(subtracksHeaders);
if (kDebugMode) print('${request.method} ${request.url}');
return request.send();
log.info('${request.method} ${request.url}');
try {
return request.send();
} catch (e, st) {
log.severe('HTTP client: ${request.method} ${request.url}', e, st);
rethrow;
}
}
}

View File

@@ -20,4 +20,5 @@ final httpClientProvider = Provider<BaseClient>.internal(
);
typedef HttpClientRef = ProviderRef<BaseClient>;
// ignore_for_file: unnecessary_raw_strings, subtype_of_sealed_class, invalid_use_of_internal_member, do_not_use_environment, prefer_const_constructors, public_member_api_docs, avoid_private_typedef_functions
// ignore_for_file: type=lint
// ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member

View File

@@ -1,166 +1,226 @@
{
"actionsStar": "Ohodnotit",
"@actionsStar": {},
"actionsUnstar": "Zrušit hodnocení",
"@actionsUnstar": {},
"messagesNothingHere": "Zde nic není…",
"@messagesNothingHere": {},
"navigationTabsHome": "Domů",
"@navigationTabsHome": {},
"navigationTabsLibrary": "Knihovna",
"@navigationTabsLibrary": {},
"navigationTabsSearch": "Hledat",
"@navigationTabsSearch": {},
"navigationTabsSettings": "Nastavení",
"@navigationTabsSettings": {},
"resourcesAlbumActionsPlay": "Přehrát album",
"@resourcesAlbumActionsPlay": {},
"resourcesAlbumActionsView": "Zobrazit album",
"@resourcesAlbumActionsView": {},
"resourcesAlbumListsSort": "Seřadit alba",
"@resourcesAlbumListsSort": {},
"resourcesAlbumName": "{count,plural, =1{Album} few{Alba} many{Alba} other{Alba}}",
"@resourcesAlbumName": {
"placeholders": {
"count": {
"type": "int"
}
}
},
"resourcesArtistActionsView": "Zobrazit umělce",
"@resourcesArtistActionsView": {},
"resourcesArtistListsSort": "Seřadit umělce",
"@resourcesArtistListsSort": {},
"resourcesArtistName": "{count,plural, =1{Umělec} few{Umělci} many{Umělci} other{Umělci}}",
"@resourcesArtistName": {
"placeholders": {
"count": {
"type": "int"
}
}
},
"resourcesFilterGenre": "Podle žánru",
"@resourcesFilterGenre": {},
"resourcesFilterStarred": "Ohodnocené",
"@resourcesFilterStarred": {},
"resourcesPlaylistActionsPlay": "Přehrát seznam skladeb",
"@resourcesPlaylistActionsPlay": {},
"resourcesPlaylistName": "{count,plural, =1{Seznam skladeb} few{Seznamy skladeb} many{Seznamy skladeb} other{Seznamy skladeb}}",
"@resourcesPlaylistName": {
"placeholders": {
"count": {
"type": "int"
}
}
},
"resourcesQueueName": "{count,plural, =1{Fronta} few{Fronty} many{Fronty} other{Fronty}}",
"@resourcesQueueName": {
"placeholders": {
"count": {
"type": "int"
}
}
},
"resourcesSongListsArtistTopSongs": "Top skladby",
"@resourcesSongListsArtistTopSongs": {},
"resourcesSongName": "{count,plural, =1{Skladba} few{Skladby} many{Skladby} other{Skladby}}",
"@resourcesSongName": {
"placeholders": {
"count": {
"type": "int"
}
}
},
"resourcesSortByAdded": "Nedávno přidané",
"@resourcesSortByAdded": {},
"resourcesSortByArtist": "Podle umělce",
"@resourcesSortByArtist": {},
"resourcesSortByFrequentlyPlayed": "Často přehrávané",
"@resourcesSortByFrequentlyPlayed": {},
"resourcesSortByName": "Podle názvu",
"@resourcesSortByName": {},
"resourcesSortByRandom": "Náhodně",
"@resourcesSortByRandom": {},
"resourcesSortByRecentlyPlayed": "Často přehrávané",
"@resourcesSortByRecentlyPlayed": {},
"resourcesSortByYear": "Podle roku",
"@resourcesSortByYear": {},
"searchHeaderTitle": "Hledat: {query}",
"@searchHeaderTitle": {
"placeholders": {
"query": {
"type": "String"
}
}
},
"searchInputPlaceholder": "Hledat",
"@searchInputPlaceholder": {},
"searchMoreResults": "Více…",
"@searchMoreResults": {},
"searchNowPlayingContext": "Výsledky hledání",
"@searchNowPlayingContext": {},
"settingsNetworkName": "Síť",
"@settingsNetworkName": {},
"settingsNetworkOptionsMaxBitrateMobileTitle": "Maximální datový tok (mobil)",
"@settingsNetworkOptionsMaxBitrateMobileTitle": {},
"settingsNetworkOptionsMaxBitrateWifiTitle": "Maximální datový tok (Wi-Fi)",
"@settingsNetworkOptionsMaxBitrateWifiTitle": {},
"settingsNetworkValuesKbps": "{value}kbps",
"@settingsNetworkValuesKbps": {
"placeholders": {
"value": {
"type": "String"
}
}
},
"settingsNetworkValuesSeconds": "{value} sekund",
"@settingsNetworkValuesSeconds": {
"placeholders": {
"value": {
"type": "String"
}
}
},
"settingsNetworkValuesUnlimitedKbps": "Neomezeno",
"@settingsNetworkValuesUnlimitedKbps": {},
"settingsServersActionsAdd": "Přidat server",
"@settingsServersActionsAdd": {},
"settingsServersActionsDelete": "Odstranit",
"@settingsServersActionsDelete": {},
"settingsServersActionsEdit": "Upravit server",
"@settingsServersActionsEdit": {},
"settingsServersActionsSave": "Uložit",
"@settingsServersActionsSave": {},
"settingsServersActionsTestConnection": "Otestovat spojení",
"@settingsServersActionsTestConnection": {},
"settingsServersFieldsAddress": "Adresa",
"@settingsServersFieldsAddress": {},
"settingsServersFieldsPassword": "Heslo",
"@settingsServersFieldsPassword": {},
"settingsServersFieldsUsername": "Uživ. jméno",
"@settingsServersFieldsUsername": {},
"settingsServersMessagesConnectionFailed": "Připojení k {address} selhalo, zkontrolujte nastavení nebo server",
"@settingsServersMessagesConnectionFailed": {
"placeholders": {
"address": {
"type": "String"
}
}
},
"settingsServersMessagesConnectionOk": "Připojení k {address} je OK!",
"@settingsServersMessagesConnectionOk": {
"placeholders": {
"address": {
"type": "String"
}
}
},
"settingsServersName": "Servery",
"@settingsServersName": {},
"settingsServersOptionsForcePlaintextPasswordDescriptionOff": "Posílat heslo jako token + salt",
"@settingsServersOptionsForcePlaintextPasswordDescriptionOff": {},
"settingsServersOptionsForcePlaintextPasswordDescriptionOn": "Posílat heslo v prostém textu (zastaralé, ujistěte se, že je vaše připojení zabezpečené!)",
"@settingsServersOptionsForcePlaintextPasswordDescriptionOn": {},
"settingsServersOptionsForcePlaintextPasswordTitle": "Vynutit heslo ve formátu prostého textu",
"@settingsServersOptionsForcePlaintextPasswordTitle": {}
}
"actionsStar": "Ohodnotit",
"@actionsStar": {},
"actionsUnstar": "Zrušit hodnocení",
"@actionsUnstar": {},
"messagesNothingHere": "Zde nic není…",
"@messagesNothingHere": {},
"navigationTabsHome": "Domů",
"@navigationTabsHome": {},
"navigationTabsLibrary": "Knihovna",
"@navigationTabsLibrary": {},
"navigationTabsSearch": "Hledat",
"@navigationTabsSearch": {},
"navigationTabsSettings": "Nastavení",
"@navigationTabsSettings": {},
"resourcesAlbumActionsPlay": "Přehrát album",
"@resourcesAlbumActionsPlay": {},
"resourcesAlbumActionsView": "Zobrazit album",
"@resourcesAlbumActionsView": {},
"resourcesAlbumListsSort": "Seřadit alba",
"@resourcesAlbumListsSort": {},
"resourcesAlbumName": "{count,plural, =1{Album} few{Alba} many{Alba} other{Alba}}",
"@resourcesAlbumName": {
"placeholders": {
"count": {
"type": "int"
}
}
},
"resourcesArtistActionsView": "Zobrazit umělce",
"@resourcesArtistActionsView": {},
"resourcesArtistListsSort": "Seřadit umělce",
"@resourcesArtistListsSort": {},
"resourcesArtistName": "{count,plural, =1{Umělec} few{Umělci} many{Umělci} other{Umělci}}",
"@resourcesArtistName": {
"placeholders": {
"count": {
"type": "int"
}
}
},
"resourcesFilterGenre": "Podle žánru",
"@resourcesFilterGenre": {},
"resourcesFilterStarred": "Ohodnocené",
"@resourcesFilterStarred": {},
"resourcesPlaylistActionsPlay": "Přehrát seznam skladeb",
"@resourcesPlaylistActionsPlay": {},
"resourcesPlaylistName": "{count,plural, =1{Seznam skladeb} few{Seznamy skladeb} many{Seznamy skladeb} other{Seznamy skladeb}}",
"@resourcesPlaylistName": {
"placeholders": {
"count": {
"type": "int"
}
}
},
"resourcesQueueName": "{count,plural, =1{Fronta} few{Fronty} many{Fronty} other{Fronty}}",
"@resourcesQueueName": {
"placeholders": {
"count": {
"type": "int"
}
}
},
"resourcesSongListsArtistTopSongs": "Top skladby",
"@resourcesSongListsArtistTopSongs": {},
"resourcesSongName": "{count,plural, =1{Skladba} few{Skladby} many{Skladby} other{Skladby}}",
"@resourcesSongName": {
"placeholders": {
"count": {
"type": "int"
}
}
},
"resourcesSortByAdded": "Nedávno přidané",
"@resourcesSortByAdded": {},
"resourcesSortByArtist": "Umělce",
"@resourcesSortByArtist": {},
"resourcesSortByFrequentlyPlayed": "Často přehrávané",
"@resourcesSortByFrequentlyPlayed": {},
"resourcesSortByName": "Názvu",
"@resourcesSortByName": {},
"resourcesSortByRandom": "Náhodně",
"@resourcesSortByRandom": {},
"resourcesSortByRecentlyPlayed": "Často přehrávané",
"@resourcesSortByRecentlyPlayed": {},
"resourcesSortByYear": "Roku",
"@resourcesSortByYear": {},
"searchHeaderTitle": "Hledat: {query}",
"@searchHeaderTitle": {
"placeholders": {
"query": {
"type": "String"
}
}
},
"searchInputPlaceholder": "Hledat",
"@searchInputPlaceholder": {},
"searchMoreResults": "Více…",
"@searchMoreResults": {},
"searchNowPlayingContext": "Výsledky hledání",
"@searchNowPlayingContext": {},
"settingsNetworkName": "Síť",
"@settingsNetworkName": {},
"settingsNetworkOptionsMaxBitrateMobileTitle": "Maximální datový tok (mobil)",
"@settingsNetworkOptionsMaxBitrateMobileTitle": {},
"settingsNetworkOptionsMaxBitrateWifiTitle": "Maximální datový tok (Wi-Fi)",
"@settingsNetworkOptionsMaxBitrateWifiTitle": {},
"settingsNetworkValuesKbps": "{value}kbps",
"@settingsNetworkValuesKbps": {
"placeholders": {
"value": {
"type": "String"
}
}
},
"settingsNetworkValuesSeconds": "{value} sekund",
"@settingsNetworkValuesSeconds": {
"placeholders": {
"value": {
"type": "String"
}
}
},
"settingsNetworkValuesUnlimitedKbps": "Neomezeno",
"@settingsNetworkValuesUnlimitedKbps": {},
"settingsServersActionsAdd": "Přidat server",
"@settingsServersActionsAdd": {},
"settingsServersActionsDelete": "Odstranit",
"@settingsServersActionsDelete": {},
"settingsServersActionsEdit": "Upravit server",
"@settingsServersActionsEdit": {},
"settingsServersActionsSave": "Uložit",
"@settingsServersActionsSave": {},
"settingsServersActionsTestConnection": "Otestovat spojení",
"@settingsServersActionsTestConnection": {},
"settingsServersFieldsAddress": "Adresa",
"@settingsServersFieldsAddress": {},
"settingsServersFieldsPassword": "Heslo",
"@settingsServersFieldsPassword": {},
"settingsServersFieldsUsername": "Uživ. jméno",
"@settingsServersFieldsUsername": {},
"settingsServersMessagesConnectionFailed": "Připojení k {address} selhalo, zkontrolujte nastavení nebo server",
"@settingsServersMessagesConnectionFailed": {
"placeholders": {
"address": {
"type": "String"
}
}
},
"settingsServersMessagesConnectionOk": "Připojení k {address} je OK!",
"@settingsServersMessagesConnectionOk": {
"placeholders": {
"address": {
"type": "String"
}
}
},
"settingsServersName": "Servery",
"@settingsServersName": {},
"settingsServersOptionsForcePlaintextPasswordDescriptionOff": "Posílat heslo jako token + salt",
"@settingsServersOptionsForcePlaintextPasswordDescriptionOff": {},
"settingsServersOptionsForcePlaintextPasswordDescriptionOn": "Posílat heslo v prostém textu (zastaralé, ujistěte se, že je vaše připojení zabezpečené!)",
"@settingsServersOptionsForcePlaintextPasswordDescriptionOn": {},
"settingsServersOptionsForcePlaintextPasswordTitle": "Vynutit heslo ve formátu prostého textu",
"@settingsServersOptionsForcePlaintextPasswordTitle": {},
"actionsDownloadDelete": "Smazat stažené",
"@actionsDownloadDelete": {},
"actionsOk": "OK",
"@actionsOk": {},
"actionsCancel": "Zrušit",
"@actionsCancel": {},
"actionsDownload": "Stáhnout",
"@actionsDownload": {},
"controlsShuffle": "Náhodně",
"@controlsShuffle": {},
"resourcesFilterAlbum": "Album",
"@resourcesFilterAlbum": {},
"resourcesFilterArtist": "Umělec",
"@resourcesFilterArtist": {},
"resourcesFilterYear": "Rok",
"@resourcesFilterYear": {},
"resourcesFilterOwner": "Majitele",
"@resourcesFilterOwner": {},
"resourcesSongListDeleteAllTitle": "Smazat stažené?",
"@resourcesSongListDeleteAllTitle": {},
"resourcesSongListDeleteAllContent": "Toto odstraní všechny stažené soubory s hudbou.",
"@resourcesSongListDeleteAllContent": {},
"resourcesSortByUpdated": "Naposledy upravené",
"@resourcesSortByUpdated": {},
"resourcesSortByAlbum": "Alba",
"@resourcesSortByAlbum": {},
"resourcesSortByAlbumCount": "Počtu alb",
"@resourcesSortByAlbumCount": {},
"resourcesSortByTitle": "Názvu",
"@resourcesSortByTitle": {},
"settingsAboutActionsLicenses": "Licence",
"@settingsAboutActionsLicenses": {},
"settingsAboutActionsProjectHomepage": "Stránka projektu",
"@settingsAboutActionsProjectHomepage": {},
"settingsAboutActionsSupport": "Podpořit vývojáře 💜",
"@settingsAboutActionsSupport": {},
"settingsAboutVersion": "verze {version}",
"@settingsAboutVersion": {
"placeholders": {
"version": {
"type": "String"
}
}
},
"settingsNetworkOptionsStreamFormat": "Preferovaný formát pro streamování",
"@settingsNetworkOptionsStreamFormat": {},
"settingsNetworkOptionsStreamFormatServerDefault": "Použít nastavení serveru",
"@settingsNetworkOptionsStreamFormatServerDefault": {},
"settingsResetActionsClearImageCache": "Smazat mezipaměť obrázků",
"@settingsResetActionsClearImageCache": {},
"settingsResetName": "Resetovat",
"@settingsResetName": {},
"settingsServersFieldsName": "Jméno",
"@settingsServersFieldsName": {},
"settingsAboutName": "O aplikaci",
"@settingsAboutName": {},
"actionsDownloadCancel": "Zrušit stahování",
"@actionsDownloadCancel": {},
"actionsDelete": "Smazat",
"@actionsDelete": {}
}

View File

@@ -1,196 +1,276 @@
{
"actionsStar": "Markieren",
"@actionsStar": {},
"actionsUnstar": "Markierung entfernen",
"@actionsUnstar": {},
"messagesNothingHere": "Hier ist nichts…",
"@messagesNothingHere": {},
"navigationTabsHome": "Startseite",
"@navigationTabsHome": {},
"navigationTabsLibrary": "Bibliothek",
"@navigationTabsLibrary": {},
"navigationTabsSearch": "Suche",
"@navigationTabsSearch": {},
"navigationTabsSettings": "Einstellungen",
"@navigationTabsSettings": {},
"resourcesAlbumActionsPlay": "Album abspielen",
"@resourcesAlbumActionsPlay": {},
"resourcesAlbumActionsView": "Album anzeigen",
"@resourcesAlbumActionsView": {},
"resourcesAlbumListsSort": "Alben sortieren",
"@resourcesAlbumListsSort": {},
"resourcesAlbumName": "{count,plural, =1{Album} other{Alben}}",
"@resourcesAlbumName": {
"placeholders": {
"count": {
"type": "int"
}
}
},
"resourcesArtistActionsView": "Interpret anzeigen",
"@resourcesArtistActionsView": {},
"resourcesArtistListsSort": "Interpreten sortieren",
"@resourcesArtistListsSort": {},
"resourcesArtistName": "{count,plural, =1{Interpret} other{Interpreten}}",
"@resourcesArtistName": {
"placeholders": {
"count": {
"type": "int"
}
}
},
"resourcesFilterGenre": "Nach Genre",
"@resourcesFilterGenre": {},
"resourcesFilterStarred": "Favoriten",
"@resourcesFilterStarred": {},
"resourcesPlaylistActionsPlay": "Wiedergabeliste abspielen",
"@resourcesPlaylistActionsPlay": {},
"resourcesPlaylistName": "{count,plural, =1{Wiedergabeliste} other{Wiedergabelisten}}",
"@resourcesPlaylistName": {
"placeholders": {
"count": {
"type": "int"
}
}
},
"resourcesQueueName": "{count,plural, =1{Warteschlange} other{Warteschlangen}}",
"@resourcesQueueName": {
"placeholders": {
"count": {
"type": "int"
}
}
},
"resourcesSongListsArtistTopSongs": "Top Lieder",
"@resourcesSongListsArtistTopSongs": {},
"resourcesSongName": "{count,plural, =1{Lied} other{Lieder}}",
"@resourcesSongName": {
"placeholders": {
"count": {
"type": "int"
}
}
},
"resourcesSortByAdded": "Kürzlich hinzugefügt",
"@resourcesSortByAdded": {},
"resourcesSortByArtist": "Nach Interpreten",
"@resourcesSortByArtist": {},
"resourcesSortByFrequentlyPlayed": "Häufig abgespielt",
"@resourcesSortByFrequentlyPlayed": {},
"resourcesSortByName": "Nach Name",
"@resourcesSortByName": {},
"resourcesSortByRandom": "Zufällig",
"@resourcesSortByRandom": {},
"resourcesSortByRecentlyPlayed": "Kürzlich abgespielt",
"@resourcesSortByRecentlyPlayed": {},
"resourcesSortByYear": "Nach Jahr",
"@resourcesSortByYear": {},
"searchHeaderTitle": "Suche: {query}",
"@searchHeaderTitle": {
"placeholders": {
"query": {
"type": "String"
}
}
},
"searchInputPlaceholder": "Suche",
"@searchInputPlaceholder": {},
"searchMoreResults": "Mehr…",
"@searchMoreResults": {},
"searchNowPlayingContext": "Suchergebnis",
"@searchNowPlayingContext": {},
"settingsAboutActionsLicenses": "Lizenzen",
"@settingsAboutActionsLicenses": {},
"settingsAboutActionsProjectHomepage": "Projektseite",
"@settingsAboutActionsProjectHomepage": {},
"settingsAboutName": "Über",
"@settingsAboutName": {},
"settingsAboutVersion": "Version {version}",
"@settingsAboutVersion": {
"placeholders": {
"version": {
"type": "String"
}
}
},
"settingsMusicName": "Musik",
"@settingsMusicName": {},
"settingsMusicOptionsScrobbleDescriptionOff": "Kein Scrobble für Wiedergabeverlauf",
"@settingsMusicOptionsScrobbleDescriptionOff": {},
"settingsMusicOptionsScrobbleDescriptionOn": "Scrobble Wiedergabeverlauf",
"@settingsMusicOptionsScrobbleDescriptionOn": {},
"settingsMusicOptionsScrobbleTitle": "Scrobble Wiedergabe",
"@settingsMusicOptionsScrobbleTitle": {},
"settingsNetworkName": "Netzwerk",
"@settingsNetworkName": {},
"settingsNetworkOptionsMaxBitrateMobileTitle": "Maximale Bitrate (Mobil)",
"@settingsNetworkOptionsMaxBitrateMobileTitle": {},
"settingsNetworkOptionsMaxBitrateWifiTitle": "Maximale Bitrate (WLAN)",
"@settingsNetworkOptionsMaxBitrateWifiTitle": {},
"settingsNetworkOptionsMaxBufferTitle": "Maximale Pufferzeit",
"@settingsNetworkOptionsMaxBufferTitle": {},
"settingsNetworkOptionsMinBufferTitle": "Minimale Pufferzeit",
"@settingsNetworkOptionsMinBufferTitle": {},
"settingsNetworkValuesKbps": "{value}kbps",
"@settingsNetworkValuesKbps": {
"placeholders": {
"value": {
"type": "String"
}
}
},
"settingsNetworkValuesSeconds": "{value} Sekunden",
"@settingsNetworkValuesSeconds": {
"placeholders": {
"value": {
"type": "String"
}
}
},
"settingsNetworkValuesUnlimitedKbps": "Unbegrenzt",
"@settingsNetworkValuesUnlimitedKbps": {},
"settingsResetActionsClearImageCache": "Bildzwischenspeicher löschen",
"@settingsResetActionsClearImageCache": {},
"settingsResetName": "Zurücksetzen",
"@settingsResetName": {},
"settingsServersActionsAdd": "Server hinzufügen",
"@settingsServersActionsAdd": {},
"settingsServersActionsDelete": "Löschen",
"@settingsServersActionsDelete": {},
"settingsServersActionsEdit": "Server bearbeiten",
"@settingsServersActionsEdit": {},
"settingsServersActionsSave": "Speichern",
"@settingsServersActionsSave": {},
"settingsServersActionsTestConnection": "Verbindung testen",
"@settingsServersActionsTestConnection": {},
"settingsServersFieldsAddress": "Adresse",
"@settingsServersFieldsAddress": {},
"settingsServersFieldsPassword": "Passwort",
"@settingsServersFieldsPassword": {},
"settingsServersFieldsUsername": "Nutzername",
"@settingsServersFieldsUsername": {},
"settingsServersMessagesConnectionFailed": "Verbindung zu {address} fehlgeschlagen, überprüfe Einstellungen oder Server",
"@settingsServersMessagesConnectionFailed": {
"placeholders": {
"address": {
"type": "String"
}
}
},
"settingsServersMessagesConnectionOk": "Verbindung zu {address} ist OK!",
"@settingsServersMessagesConnectionOk": {
"placeholders": {
"address": {
"type": "String"
}
}
},
"settingsServersName": "Server",
"@settingsServersName": {},
"settingsServersOptionsForcePlaintextPasswordDescriptionOff": "Sende Passwort als Token + Salt",
"@settingsServersOptionsForcePlaintextPasswordDescriptionOff": {},
"settingsServersOptionsForcePlaintextPasswordDescriptionOn": "Passwort als Klartext senden (Veraltet, stellen Sie sicher, dass Ihre Verbindung sicher ist!)",
"@settingsServersOptionsForcePlaintextPasswordDescriptionOn": {},
"settingsServersOptionsForcePlaintextPasswordTitle": "Erzwinge Klartextpasswort",
"@settingsServersOptionsForcePlaintextPasswordTitle": {}
}
"actionsStar": "Markieren",
"@actionsStar": {},
"actionsUnstar": "Markierung entfernen",
"@actionsUnstar": {},
"messagesNothingHere": "Hier ist nichts…",
"@messagesNothingHere": {},
"navigationTabsHome": "Startseite",
"@navigationTabsHome": {},
"navigationTabsLibrary": "Bibliothek",
"@navigationTabsLibrary": {},
"navigationTabsSearch": "Suche",
"@navigationTabsSearch": {},
"navigationTabsSettings": "Einstellungen",
"@navigationTabsSettings": {},
"resourcesAlbumActionsPlay": "Album abspielen",
"@resourcesAlbumActionsPlay": {},
"resourcesAlbumActionsView": "Album anzeigen",
"@resourcesAlbumActionsView": {},
"resourcesAlbumListsSort": "Alben sortieren",
"@resourcesAlbumListsSort": {},
"resourcesAlbumName": "{count,plural, =1{Album} other{Alben}}",
"@resourcesAlbumName": {
"placeholders": {
"count": {
"type": "int"
}
}
},
"resourcesArtistActionsView": "Interpret anzeigen",
"@resourcesArtistActionsView": {},
"resourcesArtistListsSort": "Interpreten sortieren",
"@resourcesArtistListsSort": {},
"resourcesArtistName": "{count,plural, =1{Interpret} other{Interpreten}}",
"@resourcesArtistName": {
"placeholders": {
"count": {
"type": "int"
}
}
},
"resourcesFilterGenre": "Nach Genre",
"@resourcesFilterGenre": {},
"resourcesFilterStarred": "Favoriten",
"@resourcesFilterStarred": {},
"resourcesPlaylistActionsPlay": "Wiedergabeliste abspielen",
"@resourcesPlaylistActionsPlay": {},
"resourcesPlaylistName": "{count,plural, =1{Wiedergabeliste} other{Wiedergabelisten}}",
"@resourcesPlaylistName": {
"placeholders": {
"count": {
"type": "int"
}
}
},
"resourcesQueueName": "{count,plural, =1{Warteschlange} other{Warteschlangen}}",
"@resourcesQueueName": {
"placeholders": {
"count": {
"type": "int"
}
}
},
"resourcesSongListsArtistTopSongs": "Top Lieder",
"@resourcesSongListsArtistTopSongs": {},
"resourcesSongName": "{count,plural, =1{Lied} other{Lieder}}",
"@resourcesSongName": {
"placeholders": {
"count": {
"type": "int"
}
}
},
"resourcesSortByAdded": "Kürzlich hinzugefügt",
"@resourcesSortByAdded": {},
"resourcesSortByArtist": "Nach Interpreten",
"@resourcesSortByArtist": {},
"resourcesSortByFrequentlyPlayed": "Häufig abgespielt",
"@resourcesSortByFrequentlyPlayed": {},
"resourcesSortByName": "Nach Name",
"@resourcesSortByName": {},
"resourcesSortByRandom": "Zufällig",
"@resourcesSortByRandom": {},
"resourcesSortByRecentlyPlayed": "Kürzlich abgespielt",
"@resourcesSortByRecentlyPlayed": {},
"resourcesSortByYear": "Nach Jahr",
"@resourcesSortByYear": {},
"searchHeaderTitle": "Suche: {query}",
"@searchHeaderTitle": {
"placeholders": {
"query": {
"type": "String"
}
}
},
"searchInputPlaceholder": "Suche",
"@searchInputPlaceholder": {},
"searchMoreResults": "Mehr…",
"@searchMoreResults": {},
"searchNowPlayingContext": "Suchergebnis",
"@searchNowPlayingContext": {},
"settingsAboutActionsLicenses": "Lizenzen",
"@settingsAboutActionsLicenses": {},
"settingsAboutActionsProjectHomepage": "Projektseite",
"@settingsAboutActionsProjectHomepage": {},
"settingsAboutName": "Über",
"@settingsAboutName": {},
"settingsAboutVersion": "Version {version}",
"@settingsAboutVersion": {
"placeholders": {
"version": {
"type": "String"
}
}
},
"settingsMusicName": "Musik",
"@settingsMusicName": {},
"settingsMusicOptionsScrobbleDescriptionOff": "Kein Scrobble für Wiedergabeverlauf",
"@settingsMusicOptionsScrobbleDescriptionOff": {},
"settingsMusicOptionsScrobbleDescriptionOn": "Scrobble Wiedergabeverlauf",
"@settingsMusicOptionsScrobbleDescriptionOn": {},
"settingsMusicOptionsScrobbleTitle": "Scrobble Wiedergabe",
"@settingsMusicOptionsScrobbleTitle": {},
"settingsNetworkName": "Netzwerk",
"@settingsNetworkName": {},
"settingsNetworkOptionsMaxBitrateMobileTitle": "Maximale Bitrate (Mobil)",
"@settingsNetworkOptionsMaxBitrateMobileTitle": {},
"settingsNetworkOptionsMaxBitrateWifiTitle": "Maximale Bitrate (WLAN)",
"@settingsNetworkOptionsMaxBitrateWifiTitle": {},
"settingsNetworkOptionsMaxBufferTitle": "Maximale Pufferzeit",
"@settingsNetworkOptionsMaxBufferTitle": {},
"settingsNetworkOptionsMinBufferTitle": "Minimale Pufferzeit",
"@settingsNetworkOptionsMinBufferTitle": {},
"settingsNetworkValuesKbps": "{value}kbps",
"@settingsNetworkValuesKbps": {
"placeholders": {
"value": {
"type": "String"
}
}
},
"settingsNetworkValuesSeconds": "{value} Sekunden",
"@settingsNetworkValuesSeconds": {
"placeholders": {
"value": {
"type": "String"
}
}
},
"settingsNetworkValuesUnlimitedKbps": "Unbegrenzt",
"@settingsNetworkValuesUnlimitedKbps": {},
"settingsResetActionsClearImageCache": "Bildzwischenspeicher löschen",
"@settingsResetActionsClearImageCache": {},
"settingsResetName": "Zurücksetzen",
"@settingsResetName": {},
"settingsServersActionsAdd": "Server hinzufügen",
"@settingsServersActionsAdd": {},
"settingsServersActionsDelete": "Löschen",
"@settingsServersActionsDelete": {},
"settingsServersActionsEdit": "Server bearbeiten",
"@settingsServersActionsEdit": {},
"settingsServersActionsSave": "Speichern",
"@settingsServersActionsSave": {},
"settingsServersActionsTestConnection": "Verbindung testen",
"@settingsServersActionsTestConnection": {},
"settingsServersFieldsAddress": "Adresse",
"@settingsServersFieldsAddress": {},
"settingsServersFieldsPassword": "Passwort",
"@settingsServersFieldsPassword": {},
"settingsServersFieldsUsername": "Nutzername",
"@settingsServersFieldsUsername": {},
"settingsServersMessagesConnectionFailed": "Verbindung zu {address} fehlgeschlagen, überprüfe Einstellungen oder Server",
"@settingsServersMessagesConnectionFailed": {
"placeholders": {
"address": {
"type": "String"
}
}
},
"settingsServersMessagesConnectionOk": "Verbindung zu {address} ist OK!",
"@settingsServersMessagesConnectionOk": {
"placeholders": {
"address": {
"type": "String"
}
}
},
"settingsServersName": "Server",
"@settingsServersName": {},
"settingsServersOptionsForcePlaintextPasswordDescriptionOff": "Sende Passwort als Token + Salt",
"@settingsServersOptionsForcePlaintextPasswordDescriptionOff": {},
"settingsServersOptionsForcePlaintextPasswordDescriptionOn": "Passwort als Klartext senden (Veraltet, stellen Sie sicher, dass Ihre Verbindung sicher ist!)",
"@settingsServersOptionsForcePlaintextPasswordDescriptionOn": {},
"settingsServersOptionsForcePlaintextPasswordTitle": "Erzwinge Klartextpasswort",
"@settingsServersOptionsForcePlaintextPasswordTitle": {},
"actionsDelete": "Löschen",
"@actionsDelete": {},
"actionsDownload": "Herunterladen",
"@actionsDownload": {},
"actionsDownloadCancel": "Download abbrechen",
"@actionsDownloadCancel": {},
"controlsShuffle": "Zufall",
"@controlsShuffle": {},
"actionsCancel": "Abbrechen",
"@actionsCancel": {},
"actionsDownloadDelete": "Heruntergeladene Inhalte löschen",
"@actionsDownloadDelete": {},
"actionsOk": "OK",
"@actionsOk": {},
"resourcesAlbumCount": "{count,plural, =1{{count} Album} other{{count} Alben}}",
"@resourcesAlbumCount": {
"placeholders": {
"count": {
"type": "int"
}
}
},
"resourcesFilterAlbum": "Album",
"@resourcesFilterAlbum": {},
"resourcesArtistCount": "{count,plural, =1{{count} Künstler} other{{count} Künstler}}",
"@resourcesArtistCount": {
"placeholders": {
"count": {
"type": "int"
}
}
},
"resourcesFilterArtist": "Künstler",
"@resourcesFilterArtist": {},
"resourcesFilterOwner": "Besitzer",
"@resourcesFilterOwner": {},
"resourcesFilterYear": "Jahr",
"@resourcesFilterYear": {},
"resourcesPlaylistCount": "{count,plural, =1{{count} Playlist} other{{count} Playlists}}",
"@resourcesPlaylistCount": {
"placeholders": {
"count": {
"type": "int"
}
}
},
"resourcesSongCount": "{count,plural, =1{{count} Song} other{{count} Songs}}",
"@resourcesSongCount": {
"placeholders": {
"count": {
"type": "int"
}
}
},
"resourcesSongListDeleteAllContent": "Hierdurch werden alle heruntergeladenen Inhalte entfernt.",
"@resourcesSongListDeleteAllContent": {},
"resourcesSortByAlbum": "Album",
"@resourcesSortByAlbum": {},
"resourcesSortByAlbumCount": "Albenanzahl",
"@resourcesSortByAlbumCount": {},
"resourcesSortByTitle": "Titel",
"@resourcesSortByTitle": {},
"resourcesSortByUpdated": "Kürzlich hinzugefügt",
"@resourcesSortByUpdated": {},
"settingsAboutActionsSupport": "Den Entwickler unterstützen",
"@settingsAboutActionsSupport": {},
"settingsNetworkOptionsOfflineMode": "Offline Modus",
"@settingsNetworkOptionsOfflineMode": {},
"settingsNetworkOptionsOfflineModeOff": "Nutze das Internet um Musik zu synchronisieren.",
"@settingsNetworkOptionsOfflineModeOff": {},
"settingsNetworkOptionsOfflineModeOn": "Nutze nicht das Internet um Musik zu synchronisieren.",
"@settingsNetworkOptionsOfflineModeOn": {},
"settingsNetworkOptionsStreamFormat": "Bevorzugtes Streaming-Format",
"@settingsNetworkOptionsStreamFormat": {},
"settingsServersFieldsName": "Name",
"@settingsServersFieldsName": {},
"resourcesSongListDeleteAllTitle": "Downloads löschen?",
"@resourcesSongListDeleteAllTitle": {},
"settingsNetworkOptionsStreamFormatServerDefault": "Server-Standard verwenden",
"@settingsNetworkOptionsStreamFormatServerDefault": {}
}

View File

@@ -173,6 +173,10 @@
"@settingsAboutActionsSupport": {},
"settingsAboutName": "About",
"@settingsAboutName": {},
"settingsAboutShareLogs": "Share logs",
"@settingsAboutShareLogs": {},
"settingsAboutChooseLog": "Choose a log file",
"@settingsAboutChooseLog": {},
"settingsAboutVersion": "version {version}",
"@settingsAboutVersion": {
"placeholders": {

View File

@@ -1,196 +1,210 @@
{
"actionsStar": "Estrella",
"@actionsStar": {},
"actionsUnstar": "Retirar estrella",
"@actionsUnstar": {},
"messagesNothingHere": "Nada aquí…",
"@messagesNothingHere": {},
"navigationTabsHome": "Casa",
"@navigationTabsHome": {},
"navigationTabsLibrary": "Biblioteca",
"@navigationTabsLibrary": {},
"navigationTabsSearch": "Buscar",
"@navigationTabsSearch": {},
"navigationTabsSettings": "Entorno",
"@navigationTabsSettings": {},
"resourcesAlbumActionsPlay": "Reproducir Álbum",
"@resourcesAlbumActionsPlay": {},
"resourcesAlbumActionsView": "Ver Álbum",
"@resourcesAlbumActionsView": {},
"resourcesAlbumListsSort": "Ordenar Álbumes",
"@resourcesAlbumListsSort": {},
"resourcesAlbumName": "{count,plural, =1{Álbum} other{Álbumes}}",
"@resourcesAlbumName": {
"placeholders": {
"count": {
"type": "int"
}
}
},
"resourcesArtistActionsView": "Ver Artista",
"@resourcesArtistActionsView": {},
"resourcesArtistListsSort": "Oredenar Artistas",
"@resourcesArtistListsSort": {},
"resourcesArtistName": "{count,plural, =1{Artista} other{Artistas}}",
"@resourcesArtistName": {
"placeholders": {
"count": {
"type": "int"
}
}
},
"resourcesFilterGenre": "Por Género",
"@resourcesFilterGenre": {},
"resourcesFilterStarred": "Estrellas",
"@resourcesFilterStarred": {},
"resourcesPlaylistActionsPlay": "Reproducir Lista de reproducción",
"@resourcesPlaylistActionsPlay": {},
"resourcesPlaylistName": "{count,plural, =1{Lista de reproducción} other{Listas de reproducción}}",
"@resourcesPlaylistName": {
"placeholders": {
"count": {
"type": "int"
}
}
},
"resourcesQueueName": "{count,plural, =1{Cola} other{Colas}}",
"@resourcesQueueName": {
"placeholders": {
"count": {
"type": "int"
}
}
},
"resourcesSongListsArtistTopSongs": "Mejores Canciones",
"@resourcesSongListsArtistTopSongs": {},
"resourcesSongName": "{count,plural, =1{Cancion} other{Canciones}}",
"@resourcesSongName": {
"placeholders": {
"count": {
"type": "int"
}
}
},
"resourcesSortByAdded": "Recientemente Añadido",
"@resourcesSortByAdded": {},
"resourcesSortByArtist": "Por Artista",
"@resourcesSortByArtist": {},
"resourcesSortByFrequentlyPlayed": "Jugado Frecuentemente",
"@resourcesSortByFrequentlyPlayed": {},
"resourcesSortByName": "Por Nombre",
"@resourcesSortByName": {},
"resourcesSortByRandom": "Aleatorio",
"@resourcesSortByRandom": {},
"resourcesSortByRecentlyPlayed": "Recientemente Jugado",
"@resourcesSortByRecentlyPlayed": {},
"resourcesSortByYear": "Por Año",
"@resourcesSortByYear": {},
"searchHeaderTitle": "Buscar: {query}",
"@searchHeaderTitle": {
"placeholders": {
"query": {
"type": "String"
}
}
},
"searchInputPlaceholder": "Buscar",
"@searchInputPlaceholder": {},
"searchMoreResults": "Más…",
"@searchMoreResults": {},
"searchNowPlayingContext": "Resultados de la búsqueda",
"@searchNowPlayingContext": {},
"settingsAboutActionsLicenses": "Licencias",
"@settingsAboutActionsLicenses": {},
"settingsAboutActionsProjectHomepage": "Página de inicio del proyecto",
"@settingsAboutActionsProjectHomepage": {},
"settingsAboutName": "Información",
"@settingsAboutName": {},
"settingsAboutVersion": "versión {version}",
"@settingsAboutVersion": {
"placeholders": {
"version": {
"type": "String"
}
}
},
"settingsMusicName": "Música",
"@settingsMusicName": {},
"settingsMusicOptionsScrobbleDescriptionOff": "No hagas historial de reproducción de scrobble",
"@settingsMusicOptionsScrobbleDescriptionOff": {},
"settingsMusicOptionsScrobbleDescriptionOn": "Historial de reproducción de scrobble",
"@settingsMusicOptionsScrobbleDescriptionOn": {},
"settingsMusicOptionsScrobbleTitle": "Obras de teatro de Scrobble",
"@settingsMusicOptionsScrobbleTitle": {},
"settingsNetworkName": "Sitio",
"@settingsNetworkName": {},
"settingsNetworkOptionsMaxBitrateMobileTitle": "Tasa de bits máxima (mobile)",
"@settingsNetworkOptionsMaxBitrateMobileTitle": {},
"settingsNetworkOptionsMaxBitrateWifiTitle": "Tasa de bits máxima (Wi-Fi)",
"@settingsNetworkOptionsMaxBitrateWifiTitle": {},
"settingsNetworkOptionsMaxBufferTitle": "Máxima de buffer tiempo",
"@settingsNetworkOptionsMaxBufferTitle": {},
"settingsNetworkOptionsMinBufferTitle": "Mínimo de buffer tiempo",
"@settingsNetworkOptionsMinBufferTitle": {},
"settingsNetworkValuesKbps": "{value} kilobytes por segundo",
"@settingsNetworkValuesKbps": {
"placeholders": {
"value": {
"type": "String"
}
}
},
"settingsNetworkValuesSeconds": "{value} segundos",
"@settingsNetworkValuesSeconds": {
"placeholders": {
"value": {
"type": "String"
}
}
},
"settingsNetworkValuesUnlimitedKbps": "Ilimitados",
"@settingsNetworkValuesUnlimitedKbps": {},
"settingsResetActionsClearImageCache": "Caché de imágenes claras",
"@settingsResetActionsClearImageCache": {},
"settingsResetName": "Reinicializar",
"@settingsResetName": {},
"settingsServersActionsAdd": "Agregar Servidor",
"@settingsServersActionsAdd": {},
"settingsServersActionsDelete": "Supr",
"@settingsServersActionsDelete": {},
"settingsServersActionsEdit": "Editar Servidor",
"@settingsServersActionsEdit": {},
"settingsServersActionsSave": "Enviar",
"@settingsServersActionsSave": {},
"settingsServersActionsTestConnection": "Conexión de prueba",
"@settingsServersActionsTestConnection": {},
"settingsServersFieldsAddress": "Alocución",
"@settingsServersFieldsAddress": {},
"settingsServersFieldsPassword": "La contraseña",
"@settingsServersFieldsPassword": {},
"settingsServersFieldsUsername": "Nombre de usuario",
"@settingsServersFieldsUsername": {},
"settingsServersMessagesConnectionFailed": "La conexión a {address} falló, verifique la configuracón o el servidor",
"@settingsServersMessagesConnectionFailed": {
"placeholders": {
"address": {
"type": "String"
}
}
},
"settingsServersMessagesConnectionOk": "¡Conexión con {address} Ok!",
"@settingsServersMessagesConnectionOk": {
"placeholders": {
"address": {
"type": "String"
}
}
},
"settingsServersName": "Servidores",
"@settingsServersName": {},
"settingsServersOptionsForcePlaintextPasswordDescriptionOff": "Enviar contraseña como token + sal",
"@settingsServersOptionsForcePlaintextPasswordDescriptionOff": {},
"settingsServersOptionsForcePlaintextPasswordDescriptionOn": "Enviar contraseña en texto plano (¡legado, asegúrese de que su conexión sea segura!)",
"@settingsServersOptionsForcePlaintextPasswordDescriptionOn": {},
"settingsServersOptionsForcePlaintextPasswordTitle": "Forzar contraseña de texto sin formato",
"@settingsServersOptionsForcePlaintextPasswordTitle": {}
}
"actionsStar": "Favorito",
"@actionsStar": {},
"actionsUnstar": "Retirar favorito",
"@actionsUnstar": {},
"messagesNothingHere": "Nada aquí…",
"@messagesNothingHere": {},
"navigationTabsHome": "Casa",
"@navigationTabsHome": {},
"navigationTabsLibrary": "Biblioteca",
"@navigationTabsLibrary": {},
"navigationTabsSearch": "Buscar",
"@navigationTabsSearch": {},
"navigationTabsSettings": "Entorno",
"@navigationTabsSettings": {},
"resourcesAlbumActionsPlay": "Reproducir Álbum",
"@resourcesAlbumActionsPlay": {},
"resourcesAlbumActionsView": "Ver Álbum",
"@resourcesAlbumActionsView": {},
"resourcesAlbumListsSort": "Ordenar Álbumes",
"@resourcesAlbumListsSort": {},
"resourcesAlbumName": "{count,plural, =1{Álbum} other{Álbumes}}",
"@resourcesAlbumName": {
"placeholders": {
"count": {
"type": "int"
}
}
},
"resourcesArtistActionsView": "Ver Artista",
"@resourcesArtistActionsView": {},
"resourcesArtistListsSort": "Oredenar Artistas",
"@resourcesArtistListsSort": {},
"resourcesArtistName": "{count,plural, =1{Artista} other{Artistas}}",
"@resourcesArtistName": {
"placeholders": {
"count": {
"type": "int"
}
}
},
"resourcesFilterGenre": "Por Género",
"@resourcesFilterGenre": {},
"resourcesFilterStarred": "Estrellas",
"@resourcesFilterStarred": {},
"resourcesPlaylistActionsPlay": "Reproducir Lista de reproducción",
"@resourcesPlaylistActionsPlay": {},
"resourcesPlaylistName": "{count,plural, =1{Lista de reproducción} other{Listas de reproducción}}",
"@resourcesPlaylistName": {
"placeholders": {
"count": {
"type": "int"
}
}
},
"resourcesQueueName": "{count,plural, =1{Cola} other{Colas}}",
"@resourcesQueueName": {
"placeholders": {
"count": {
"type": "int"
}
}
},
"resourcesSongListsArtistTopSongs": "Mejores Canciones",
"@resourcesSongListsArtistTopSongs": {},
"resourcesSongName": "{count,plural, =1{Cancion} other{Canciones}}",
"@resourcesSongName": {
"placeholders": {
"count": {
"type": "int"
}
}
},
"resourcesSortByAdded": "Recientemente Añadido",
"@resourcesSortByAdded": {},
"resourcesSortByArtist": "Por Artista",
"@resourcesSortByArtist": {},
"resourcesSortByFrequentlyPlayed": "Jugado Frecuentemente",
"@resourcesSortByFrequentlyPlayed": {},
"resourcesSortByName": "Por Nombre",
"@resourcesSortByName": {},
"resourcesSortByRandom": "Aleatorio",
"@resourcesSortByRandom": {},
"resourcesSortByRecentlyPlayed": "Recientemente Jugado",
"@resourcesSortByRecentlyPlayed": {},
"resourcesSortByYear": "Por Año",
"@resourcesSortByYear": {},
"searchHeaderTitle": "Buscar: {query}",
"@searchHeaderTitle": {
"placeholders": {
"query": {
"type": "String"
}
}
},
"searchInputPlaceholder": "Buscar",
"@searchInputPlaceholder": {},
"searchMoreResults": "Más…",
"@searchMoreResults": {},
"searchNowPlayingContext": "Resultados de la búsqueda",
"@searchNowPlayingContext": {},
"settingsAboutActionsLicenses": "Licencias",
"@settingsAboutActionsLicenses": {},
"settingsAboutActionsProjectHomepage": "Página de inicio del proyecto",
"@settingsAboutActionsProjectHomepage": {},
"settingsAboutName": "Información",
"@settingsAboutName": {},
"settingsAboutVersion": "versión {version}",
"@settingsAboutVersion": {
"placeholders": {
"version": {
"type": "String"
}
}
},
"settingsMusicName": "Música",
"@settingsMusicName": {},
"settingsMusicOptionsScrobbleDescriptionOff": "No hagas historial de reproducción de scrobble",
"@settingsMusicOptionsScrobbleDescriptionOff": {},
"settingsMusicOptionsScrobbleDescriptionOn": "Historial de reproducción de scrobble",
"@settingsMusicOptionsScrobbleDescriptionOn": {},
"settingsMusicOptionsScrobbleTitle": "Obras de teatro de Scrobble",
"@settingsMusicOptionsScrobbleTitle": {},
"settingsNetworkName": "Sitio",
"@settingsNetworkName": {},
"settingsNetworkOptionsMaxBitrateMobileTitle": "Tasa de bits máxima (mobile)",
"@settingsNetworkOptionsMaxBitrateMobileTitle": {},
"settingsNetworkOptionsMaxBitrateWifiTitle": "Tasa de bits máxima (Wi-Fi)",
"@settingsNetworkOptionsMaxBitrateWifiTitle": {},
"settingsNetworkOptionsMaxBufferTitle": "Máxima de buffer tiempo",
"@settingsNetworkOptionsMaxBufferTitle": {},
"settingsNetworkOptionsMinBufferTitle": "Mínimo de buffer tiempo",
"@settingsNetworkOptionsMinBufferTitle": {},
"settingsNetworkValuesKbps": "{value} kilobytes por segundo",
"@settingsNetworkValuesKbps": {
"placeholders": {
"value": {
"type": "String"
}
}
},
"settingsNetworkValuesSeconds": "{value} segundos",
"@settingsNetworkValuesSeconds": {
"placeholders": {
"value": {
"type": "String"
}
}
},
"settingsNetworkValuesUnlimitedKbps": "Ilimitados",
"@settingsNetworkValuesUnlimitedKbps": {},
"settingsResetActionsClearImageCache": "Caché de imágenes claras",
"@settingsResetActionsClearImageCache": {},
"settingsResetName": "Reinicializar",
"@settingsResetName": {},
"settingsServersActionsAdd": "Agregar Servidor",
"@settingsServersActionsAdd": {},
"settingsServersActionsDelete": "Supr",
"@settingsServersActionsDelete": {},
"settingsServersActionsEdit": "Editar Servidor",
"@settingsServersActionsEdit": {},
"settingsServersActionsSave": "Enviar",
"@settingsServersActionsSave": {},
"settingsServersActionsTestConnection": "Conexión de prueba",
"@settingsServersActionsTestConnection": {},
"settingsServersFieldsAddress": "Alocución",
"@settingsServersFieldsAddress": {},
"settingsServersFieldsPassword": "La contraseña",
"@settingsServersFieldsPassword": {},
"settingsServersFieldsUsername": "Nombre de usuario",
"@settingsServersFieldsUsername": {},
"settingsServersMessagesConnectionFailed": "La conexión a {address} falló, verifique la configuracón o el servidor",
"@settingsServersMessagesConnectionFailed": {
"placeholders": {
"address": {
"type": "String"
}
}
},
"settingsServersMessagesConnectionOk": "¡Conexión con {address} Ok!",
"@settingsServersMessagesConnectionOk": {
"placeholders": {
"address": {
"type": "String"
}
}
},
"settingsServersName": "Servidores",
"@settingsServersName": {},
"settingsServersOptionsForcePlaintextPasswordDescriptionOff": "Enviar contraseña como token + sal",
"@settingsServersOptionsForcePlaintextPasswordDescriptionOff": {},
"settingsServersOptionsForcePlaintextPasswordDescriptionOn": "Enviar contraseña en texto plano (¡legado, asegúrese de que su conexión sea segura!)",
"@settingsServersOptionsForcePlaintextPasswordDescriptionOn": {},
"settingsServersOptionsForcePlaintextPasswordTitle": "Forzar contraseña de texto sin formato",
"@settingsServersOptionsForcePlaintextPasswordTitle": {},
"actionsDelete": "Borrar",
"@actionsDelete": {},
"actionsOk": "Ok",
"@actionsOk": {},
"actionsDownload": "Descargar",
"@actionsDownload": {},
"actionsDownloadCancel": "Anular descargar",
"@actionsDownloadCancel": {},
"controlsShuffle": "Reproducir aleatoriamente",
"@controlsShuffle": {},
"actionsCancel": "Cancelar",
"@actionsCancel": {},
"actionsDownloadDelete": "Eliminar descargado",
"@actionsDownloadDelete": {}
}

View File

@@ -1,196 +1,280 @@
{
"actionsStar": "Estrela",
"@actionsStar": {},
"actionsUnstar": "Retirar",
"@actionsUnstar": {},
"messagesNothingHere": "Nada por aquí…",
"@messagesNothingHere": {},
"navigationTabsHome": "Inicio",
"@navigationTabsHome": {},
"navigationTabsLibrary": "Biblioteca",
"@navigationTabsLibrary": {},
"navigationTabsSearch": "Buscar",
"@navigationTabsSearch": {},
"navigationTabsSettings": "Axustes",
"@navigationTabsSettings": {},
"resourcesAlbumActionsPlay": "Reproducir",
"@resourcesAlbumActionsPlay": {},
"resourcesAlbumActionsView": "Ver Álbum",
"@resourcesAlbumActionsView": {},
"resourcesAlbumListsSort": "Ordenar Álbums",
"@resourcesAlbumListsSort": {},
"resourcesAlbumName": "{count,plural, =1{Álbum} other{Álbums}}",
"@resourcesAlbumName": {
"placeholders": {
"count": {
"type": "int"
}
}
},
"resourcesArtistActionsView": "Ver Artista",
"@resourcesArtistActionsView": {},
"resourcesArtistListsSort": "Ordenar Artistas",
"@resourcesArtistListsSort": {},
"resourcesArtistName": "{count,plural, =1{Artista} other{Artistas}}",
"@resourcesArtistName": {
"placeholders": {
"count": {
"type": "int"
}
}
},
"resourcesFilterGenre": "Por xénero",
"@resourcesFilterGenre": {},
"resourcesFilterStarred": "Favoritas",
"@resourcesFilterStarred": {},
"resourcesPlaylistActionsPlay": "Reproducir lista",
"@resourcesPlaylistActionsPlay": {},
"resourcesPlaylistName": "{count,plural, =1{Listaxe} other{Listaxes}}",
"@resourcesPlaylistName": {
"placeholders": {
"count": {
"type": "int"
}
}
},
"resourcesQueueName": "{count,plural, =1{Fila} other{Filas}}",
"@resourcesQueueName": {
"placeholders": {
"count": {
"type": "int"
}
}
},
"resourcesSongListsArtistTopSongs": "Máis reproducidas",
"@resourcesSongListsArtistTopSongs": {},
"resourcesSongName": "{count,plural, =1{Canción} other{Cancións}}",
"@resourcesSongName": {
"placeholders": {
"count": {
"type": "int"
}
}
},
"resourcesSortByAdded": "Últimas engadidas",
"@resourcesSortByAdded": {},
"resourcesSortByArtist": "Por artista",
"@resourcesSortByArtist": {},
"resourcesSortByFrequentlyPlayed": "Reproducidas a miúdo",
"@resourcesSortByFrequentlyPlayed": {},
"resourcesSortByName": "Por nome",
"@resourcesSortByName": {},
"resourcesSortByRandom": "Ao chou",
"@resourcesSortByRandom": {},
"resourcesSortByRecentlyPlayed": "Reproducidas a miúdo",
"@resourcesSortByRecentlyPlayed": {},
"resourcesSortByYear": "Por ano",
"@resourcesSortByYear": {},
"searchHeaderTitle": "Buscar: {query}",
"@searchHeaderTitle": {
"placeholders": {
"query": {
"type": "String"
}
}
},
"searchInputPlaceholder": "Buscar",
"@searchInputPlaceholder": {},
"searchMoreResults": "Máis…",
"@searchMoreResults": {},
"searchNowPlayingContext": "Resultados",
"@searchNowPlayingContext": {},
"settingsAboutActionsLicenses": "Licenzas",
"@settingsAboutActionsLicenses": {},
"settingsAboutActionsProjectHomepage": "Web do Proxecto",
"@settingsAboutActionsProjectHomepage": {},
"settingsAboutName": "Acerca de",
"@settingsAboutName": {},
"settingsAboutVersion": "versión {version}",
"@settingsAboutVersion": {
"placeholders": {
"version": {
"type": "String"
}
}
},
"settingsMusicName": "Música",
"@settingsMusicName": {},
"settingsMusicOptionsScrobbleDescriptionOff": "Non rexistrar o historial de reprodución",
"@settingsMusicOptionsScrobbleDescriptionOff": {},
"settingsMusicOptionsScrobbleDescriptionOn": "Rexistrar o historial de reprodución",
"@settingsMusicOptionsScrobbleDescriptionOn": {},
"settingsMusicOptionsScrobbleTitle": "Rexistrar reprodución",
"@settingsMusicOptionsScrobbleTitle": {},
"settingsNetworkName": "Rede",
"@settingsNetworkName": {},
"settingsNetworkOptionsMaxBitrateMobileTitle": "Bitrate máx. (móbil)",
"@settingsNetworkOptionsMaxBitrateMobileTitle": {},
"settingsNetworkOptionsMaxBitrateWifiTitle": "Bitrate máx. (Wi-Fi)",
"@settingsNetworkOptionsMaxBitrateWifiTitle": {},
"settingsNetworkOptionsMaxBufferTitle": "Tempo máximo na memoria",
"@settingsNetworkOptionsMaxBufferTitle": {},
"settingsNetworkOptionsMinBufferTitle": "Tempo mínimo na memoria",
"@settingsNetworkOptionsMinBufferTitle": {},
"settingsNetworkValuesKbps": "{value}kbps",
"@settingsNetworkValuesKbps": {
"placeholders": {
"value": {
"type": "String"
}
}
},
"settingsNetworkValuesSeconds": "{value} segundos",
"@settingsNetworkValuesSeconds": {
"placeholders": {
"value": {
"type": "String"
}
}
},
"settingsNetworkValuesUnlimitedKbps": "Sen límite",
"@settingsNetworkValuesUnlimitedKbps": {},
"settingsResetActionsClearImageCache": "Limpar a caché de imaxes",
"@settingsResetActionsClearImageCache": {},
"settingsResetName": "Restablecer",
"@settingsResetName": {},
"settingsServersActionsAdd": "Engadir Servidor",
"@settingsServersActionsAdd": {},
"settingsServersActionsDelete": "Eliminar",
"@settingsServersActionsDelete": {},
"settingsServersActionsEdit": "Editar Servidor",
"@settingsServersActionsEdit": {},
"settingsServersActionsSave": "Gardar",
"@settingsServersActionsSave": {},
"settingsServersActionsTestConnection": "Comprobar Conexión",
"@settingsServersActionsTestConnection": {},
"settingsServersFieldsAddress": "Enderezo",
"@settingsServersFieldsAddress": {},
"settingsServersFieldsPassword": "Contrasinal",
"@settingsServersFieldsPassword": {},
"settingsServersFieldsUsername": "Identificador",
"@settingsServersFieldsUsername": {},
"settingsServersMessagesConnectionFailed": "Fallou a conexión a {address}, comproba os axustes",
"@settingsServersMessagesConnectionFailed": {
"placeholders": {
"address": {
"type": "String"
}
}
},
"settingsServersMessagesConnectionOk": "Conexión con {address} OK!",
"@settingsServersMessagesConnectionOk": {
"placeholders": {
"address": {
"type": "String"
}
}
},
"settingsServersName": "Servidores",
"@settingsServersName": {},
"settingsServersOptionsForcePlaintextPasswordDescriptionOff": "Enviar contrasinal como token + salt",
"@settingsServersOptionsForcePlaintextPasswordDescriptionOff": {},
"settingsServersOptionsForcePlaintextPasswordDescriptionOn": "Enviar contrasinal en texto plano (herdado, pon coidado en que a conexión sexa segura!)",
"@settingsServersOptionsForcePlaintextPasswordDescriptionOn": {},
"settingsServersOptionsForcePlaintextPasswordTitle": "Forzar contrasinal en texto plano",
"@settingsServersOptionsForcePlaintextPasswordTitle": {}
}
"actionsStar": "Estrela",
"@actionsStar": {},
"actionsUnstar": "Retirar",
"@actionsUnstar": {},
"messagesNothingHere": "Nada por aquí…",
"@messagesNothingHere": {},
"navigationTabsHome": "Inicio",
"@navigationTabsHome": {},
"navigationTabsLibrary": "Biblioteca",
"@navigationTabsLibrary": {},
"navigationTabsSearch": "Buscar",
"@navigationTabsSearch": {},
"navigationTabsSettings": "Axustes",
"@navigationTabsSettings": {},
"resourcesAlbumActionsPlay": "Reproducir",
"@resourcesAlbumActionsPlay": {},
"resourcesAlbumActionsView": "Ver Álbum",
"@resourcesAlbumActionsView": {},
"resourcesAlbumListsSort": "Ordenar Álbums",
"@resourcesAlbumListsSort": {},
"resourcesAlbumName": "{count,plural, =1{Álbum} other{Álbums}}",
"@resourcesAlbumName": {
"placeholders": {
"count": {
"type": "int"
}
}
},
"resourcesArtistActionsView": "Ver Artista",
"@resourcesArtistActionsView": {},
"resourcesArtistListsSort": "Ordenar Artistas",
"@resourcesArtistListsSort": {},
"resourcesArtistName": "{count,plural, =1{Artista} other{Artistas}}",
"@resourcesArtistName": {
"placeholders": {
"count": {
"type": "int"
}
}
},
"resourcesFilterGenre": "Por xénero",
"@resourcesFilterGenre": {},
"resourcesFilterStarred": "Favoritas",
"@resourcesFilterStarred": {},
"resourcesPlaylistActionsPlay": "Reproducir lista",
"@resourcesPlaylistActionsPlay": {},
"resourcesPlaylistName": "{count,plural, =1{Listaxe} other{Listaxes}}",
"@resourcesPlaylistName": {
"placeholders": {
"count": {
"type": "int"
}
}
},
"resourcesQueueName": "{count,plural, =1{Fila} other{Filas}}",
"@resourcesQueueName": {
"placeholders": {
"count": {
"type": "int"
}
}
},
"resourcesSongListsArtistTopSongs": "Máis reproducidas",
"@resourcesSongListsArtistTopSongs": {},
"resourcesSongName": "{count,plural, =1{Canción} other{Cancións}}",
"@resourcesSongName": {
"placeholders": {
"count": {
"type": "int"
}
}
},
"resourcesSortByAdded": "Últimas engadidas",
"@resourcesSortByAdded": {},
"resourcesSortByArtist": "Por artista",
"@resourcesSortByArtist": {},
"resourcesSortByFrequentlyPlayed": "Reproducidas a miúdo",
"@resourcesSortByFrequentlyPlayed": {},
"resourcesSortByName": "Por nome",
"@resourcesSortByName": {},
"resourcesSortByRandom": "Ao chou",
"@resourcesSortByRandom": {},
"resourcesSortByRecentlyPlayed": "Reproducidas a miúdo",
"@resourcesSortByRecentlyPlayed": {},
"resourcesSortByYear": "Por ano",
"@resourcesSortByYear": {},
"searchHeaderTitle": "Buscar: {query}",
"@searchHeaderTitle": {
"placeholders": {
"query": {
"type": "String"
}
}
},
"searchInputPlaceholder": "Buscar",
"@searchInputPlaceholder": {},
"searchMoreResults": "Máis…",
"@searchMoreResults": {},
"searchNowPlayingContext": "Resultados",
"@searchNowPlayingContext": {},
"settingsAboutActionsLicenses": "Licenzas",
"@settingsAboutActionsLicenses": {},
"settingsAboutActionsProjectHomepage": "Web do Proxecto",
"@settingsAboutActionsProjectHomepage": {},
"settingsAboutName": "Acerca de",
"@settingsAboutName": {},
"settingsAboutVersion": "versión {version}",
"@settingsAboutVersion": {
"placeholders": {
"version": {
"type": "String"
}
}
},
"settingsMusicName": "Música",
"@settingsMusicName": {},
"settingsMusicOptionsScrobbleDescriptionOff": "Non rexistrar o historial de reprodución",
"@settingsMusicOptionsScrobbleDescriptionOff": {},
"settingsMusicOptionsScrobbleDescriptionOn": "Rexistrar o historial de reprodución",
"@settingsMusicOptionsScrobbleDescriptionOn": {},
"settingsMusicOptionsScrobbleTitle": "Rexistrar reprodución",
"@settingsMusicOptionsScrobbleTitle": {},
"settingsNetworkName": "Rede",
"@settingsNetworkName": {},
"settingsNetworkOptionsMaxBitrateMobileTitle": "Bitrate máx. (móbil)",
"@settingsNetworkOptionsMaxBitrateMobileTitle": {},
"settingsNetworkOptionsMaxBitrateWifiTitle": "Bitrate máx. (Wi-Fi)",
"@settingsNetworkOptionsMaxBitrateWifiTitle": {},
"settingsNetworkOptionsMaxBufferTitle": "Tempo máximo na memoria",
"@settingsNetworkOptionsMaxBufferTitle": {},
"settingsNetworkOptionsMinBufferTitle": "Tempo mínimo na memoria",
"@settingsNetworkOptionsMinBufferTitle": {},
"settingsNetworkValuesKbps": "{value}kbps",
"@settingsNetworkValuesKbps": {
"placeholders": {
"value": {
"type": "String"
}
}
},
"settingsNetworkValuesSeconds": "{value} segundos",
"@settingsNetworkValuesSeconds": {
"placeholders": {
"value": {
"type": "String"
}
}
},
"settingsNetworkValuesUnlimitedKbps": "Sen límite",
"@settingsNetworkValuesUnlimitedKbps": {},
"settingsResetActionsClearImageCache": "Limpar a caché de imaxes",
"@settingsResetActionsClearImageCache": {},
"settingsResetName": "Restablecer",
"@settingsResetName": {},
"settingsServersActionsAdd": "Engadir Servidor",
"@settingsServersActionsAdd": {},
"settingsServersActionsDelete": "Eliminar",
"@settingsServersActionsDelete": {},
"settingsServersActionsEdit": "Editar Servidor",
"@settingsServersActionsEdit": {},
"settingsServersActionsSave": "Gardar",
"@settingsServersActionsSave": {},
"settingsServersActionsTestConnection": "Comprobar Conexión",
"@settingsServersActionsTestConnection": {},
"settingsServersFieldsAddress": "Enderezo",
"@settingsServersFieldsAddress": {},
"settingsServersFieldsPassword": "Contrasinal",
"@settingsServersFieldsPassword": {},
"settingsServersFieldsUsername": "Identificador",
"@settingsServersFieldsUsername": {},
"settingsServersMessagesConnectionFailed": "Fallou a conexión a {address}, comproba os axustes",
"@settingsServersMessagesConnectionFailed": {
"placeholders": {
"address": {
"type": "String"
}
}
},
"settingsServersMessagesConnectionOk": "Conexión con {address} OK!",
"@settingsServersMessagesConnectionOk": {
"placeholders": {
"address": {
"type": "String"
}
}
},
"settingsServersName": "Servidores",
"@settingsServersName": {},
"settingsServersOptionsForcePlaintextPasswordDescriptionOff": "Enviar contrasinal como token + salt",
"@settingsServersOptionsForcePlaintextPasswordDescriptionOff": {},
"settingsServersOptionsForcePlaintextPasswordDescriptionOn": "Enviar contrasinal en texto plano (herdado, pon coidado en que a conexión sexa segura!)",
"@settingsServersOptionsForcePlaintextPasswordDescriptionOn": {},
"settingsServersOptionsForcePlaintextPasswordTitle": "Forzar contrasinal en texto plano",
"@settingsServersOptionsForcePlaintextPasswordTitle": {},
"actionsCancel": "Cancelar",
"@actionsCancel": {},
"actionsDelete": "Eliminar",
"@actionsDelete": {},
"actionsDownload": "Descargar",
"@actionsDownload": {},
"actionsDownloadCancel": "Cancelar a descarga",
"@actionsDownloadCancel": {},
"actionsDownloadDelete": "Eliminar o descargado",
"@actionsDownloadDelete": {},
"actionsOk": "OK",
"@actionsOk": {},
"controlsShuffle": "Barallar",
"@controlsShuffle": {},
"resourcesAlbumCount": "{count,plural, =1{{count} álbum} other{{count} álbums}}",
"@resourcesAlbumCount": {
"placeholders": {
"count": {
"type": "int"
}
}
},
"resourcesArtistCount": "{count,plural, =1{{count} artista} other{{count} artistas}}",
"@resourcesArtistCount": {
"placeholders": {
"count": {
"type": "int"
}
}
},
"resourcesFilterAlbum": "Álbum",
"@resourcesFilterAlbum": {},
"resourcesFilterArtist": "Artista",
"@resourcesFilterArtist": {},
"resourcesFilterOwner": "Dono",
"@resourcesFilterOwner": {},
"resourcesFilterYear": "Ano",
"@resourcesFilterYear": {},
"resourcesPlaylistCount": "{count,plural, =1{{count} lista} other{{count} listas}}",
"@resourcesPlaylistCount": {
"placeholders": {
"count": {
"type": "int"
}
}
},
"resourcesSongCount": "{count,plural, =1{{count} canción} other{{count} cancións}}",
"@resourcesSongCount": {
"placeholders": {
"count": {
"type": "int"
}
}
},
"resourcesSongListDeleteAllContent": "Vas eliminar todas as cancións descargadas.",
"@resourcesSongListDeleteAllContent": {},
"resourcesSongListDeleteAllTitle": "Eliminar descargas?",
"@resourcesSongListDeleteAllTitle": {},
"resourcesSortByAlbum": "Álbum",
"@resourcesSortByAlbum": {},
"resourcesSortByAlbumCount": "Número de álbums",
"@resourcesSortByAlbumCount": {},
"settingsAboutActionsSupport": "Axuda ao desenvolvemento 💜",
"@settingsAboutActionsSupport": {},
"settingsNetworkOptionsOfflineMode": "Modo sen conexión",
"@settingsNetworkOptionsOfflineMode": {},
"settingsNetworkOptionsOfflineModeOff": "Usa internet para sincr. música.",
"@settingsNetworkOptionsOfflineModeOff": {},
"settingsNetworkOptionsOfflineModeOn": "Non usar internet para sincr. ou reproducir música.",
"@settingsNetworkOptionsOfflineModeOn": {},
"settingsNetworkOptionsStreamFormat": "Modo de reprodución preferido",
"@settingsNetworkOptionsStreamFormat": {},
"settingsNetworkOptionsStreamFormatServerDefault": "Usar por defecto do servidor",
"@settingsNetworkOptionsStreamFormatServerDefault": {},
"settingsServersFieldsName": "Nome",
"@settingsServersFieldsName": {},
"resourcesSortByTitle": "Título",
"@resourcesSortByTitle": {},
"resourcesSortByUpdated": "Actualizado recentemente",
"@resourcesSortByUpdated": {},
"settingsAboutShareLogs": "Compartir rexistros",
"@settingsAboutShareLogs": {},
"settingsAboutChooseLog": "Escolle un ficheiro de rexistro",
"@settingsAboutChooseLog": {}
}

View File

@@ -1,196 +1,230 @@
{
"actionsStar": "Favorito",
"@actionsStar": {},
"actionsUnstar": "Remover favorito",
"@actionsUnstar": {},
"messagesNothingHere": "Não existe nada…",
"@messagesNothingHere": {},
"navigationTabsHome": "Início",
"@navigationTabsHome": {},
"navigationTabsLibrary": "Biblioteca",
"@navigationTabsLibrary": {},
"navigationTabsSearch": "Procurar",
"@navigationTabsSearch": {},
"navigationTabsSettings": "Definições",
"@navigationTabsSettings": {},
"resourcesAlbumActionsPlay": "Tocar Álbum",
"@resourcesAlbumActionsPlay": {},
"resourcesAlbumActionsView": "Ver Álbum",
"@resourcesAlbumActionsView": {},
"resourcesAlbumListsSort": "Ordenar Álbuns",
"@resourcesAlbumListsSort": {},
"resourcesAlbumName": "{count,plural, =1{Álbum} other{Álbuns}}",
"@resourcesAlbumName": {
"placeholders": {
"count": {
"type": "int"
}
}
},
"resourcesArtistActionsView": "Ver Artista",
"@resourcesArtistActionsView": {},
"resourcesArtistListsSort": "Ordenar Artistas",
"@resourcesArtistListsSort": {},
"resourcesArtistName": "{count,plural, =1{Artista} other{Artistas}}",
"@resourcesArtistName": {
"placeholders": {
"count": {
"type": "int"
}
}
},
"resourcesFilterGenre": "Por Género",
"@resourcesFilterGenre": {},
"resourcesFilterStarred": "Favoritos",
"@resourcesFilterStarred": {},
"resourcesPlaylistActionsPlay": "Tocar Playlist",
"@resourcesPlaylistActionsPlay": {},
"resourcesPlaylistName": "{count,plural, =1{Lista} other{Listas}}",
"@resourcesPlaylistName": {
"placeholders": {
"count": {
"type": "int"
}
}
},
"resourcesQueueName": "{count,plural, =1{Fila} other{Filas}}",
"@resourcesQueueName": {
"placeholders": {
"count": {
"type": "int"
}
}
},
"resourcesSongListsArtistTopSongs": "Top Músicas",
"@resourcesSongListsArtistTopSongs": {},
"resourcesSongName": "{count,plural, =1{Música} other{Músicas}}",
"@resourcesSongName": {
"placeholders": {
"count": {
"type": "int"
}
}
},
"resourcesSortByAdded": "Adicionado recentemente",
"@resourcesSortByAdded": {},
"resourcesSortByArtist": "Por Artista",
"@resourcesSortByArtist": {},
"resourcesSortByFrequentlyPlayed": "Mais Tocado",
"@resourcesSortByFrequentlyPlayed": {},
"resourcesSortByName": "Por Nome",
"@resourcesSortByName": {},
"resourcesSortByRandom": "Aleatório",
"@resourcesSortByRandom": {},
"resourcesSortByRecentlyPlayed": "Ouviu recentemente",
"@resourcesSortByRecentlyPlayed": {},
"resourcesSortByYear": "Por Ano",
"@resourcesSortByYear": {},
"searchHeaderTitle": "Procurar: {query}",
"@searchHeaderTitle": {
"placeholders": {
"query": {
"type": "String"
}
}
},
"searchInputPlaceholder": "Procurar",
"@searchInputPlaceholder": {},
"searchMoreResults": "Mais…",
"@searchMoreResults": {},
"searchNowPlayingContext": "Resultados da Pesquisa",
"@searchNowPlayingContext": {},
"settingsAboutActionsLicenses": "Licenças",
"@settingsAboutActionsLicenses": {},
"settingsAboutActionsProjectHomepage": "Página do Projeto",
"@settingsAboutActionsProjectHomepage": {},
"settingsAboutName": "Acerca",
"@settingsAboutName": {},
"settingsAboutVersion": "versão {version}",
"@settingsAboutVersion": {
"placeholders": {
"version": {
"type": "String"
}
}
},
"settingsMusicName": "Música",
"@settingsMusicName": {},
"settingsMusicOptionsScrobbleDescriptionOff": "Não enviar histórico de reproduções por scrobble",
"@settingsMusicOptionsScrobbleDescriptionOff": {},
"settingsMusicOptionsScrobbleDescriptionOn": "Enviar histórico de reproduções por scrobble",
"@settingsMusicOptionsScrobbleDescriptionOn": {},
"settingsMusicOptionsScrobbleTitle": "Enviar reproduções por scrobble",
"@settingsMusicOptionsScrobbleTitle": {},
"settingsNetworkName": "Rede",
"@settingsNetworkName": {},
"settingsNetworkOptionsMaxBitrateMobileTitle": "Bitrate Máximo (móvel)",
"@settingsNetworkOptionsMaxBitrateMobileTitle": {},
"settingsNetworkOptionsMaxBitrateWifiTitle": "Bitrate Máximo (Wi-Fi)",
"@settingsNetworkOptionsMaxBitrateWifiTitle": {},
"settingsNetworkOptionsMaxBufferTitle": "Tempo de buffer máximo",
"@settingsNetworkOptionsMaxBufferTitle": {},
"settingsNetworkOptionsMinBufferTitle": "Tempo de buffer mínimo",
"@settingsNetworkOptionsMinBufferTitle": {},
"settingsNetworkValuesKbps": "{value}kbps",
"@settingsNetworkValuesKbps": {
"placeholders": {
"value": {
"type": "String"
}
}
},
"settingsNetworkValuesSeconds": "{value} segundos",
"@settingsNetworkValuesSeconds": {
"placeholders": {
"value": {
"type": "String"
}
}
},
"settingsNetworkValuesUnlimitedKbps": "Ilimitado",
"@settingsNetworkValuesUnlimitedKbps": {},
"settingsResetActionsClearImageCache": "Limpar cache de Imagens",
"@settingsResetActionsClearImageCache": {},
"settingsResetName": "Redefinir",
"@settingsResetName": {},
"settingsServersActionsAdd": "Adicionar Servidor",
"@settingsServersActionsAdd": {},
"settingsServersActionsDelete": "Apagar",
"@settingsServersActionsDelete": {},
"settingsServersActionsEdit": "Editar Servidor",
"@settingsServersActionsEdit": {},
"settingsServersActionsSave": "Guardar",
"@settingsServersActionsSave": {},
"settingsServersActionsTestConnection": "Testar Ligação",
"@settingsServersActionsTestConnection": {},
"settingsServersFieldsAddress": "Endereço",
"@settingsServersFieldsAddress": {},
"settingsServersFieldsPassword": "Senha",
"@settingsServersFieldsPassword": {},
"settingsServersFieldsUsername": "Nome de utilizador",
"@settingsServersFieldsUsername": {},
"settingsServersMessagesConnectionFailed": "Ligação a {address} falhou, verifique definições ou servidor",
"@settingsServersMessagesConnectionFailed": {
"placeholders": {
"address": {
"type": "String"
}
}
},
"settingsServersMessagesConnectionOk": "Ligação a {address} OK!",
"@settingsServersMessagesConnectionOk": {
"placeholders": {
"address": {
"type": "String"
}
}
},
"settingsServersName": "Servidores",
"@settingsServersName": {},
"settingsServersOptionsForcePlaintextPasswordDescriptionOff": "Enviar senha como token + sal",
"@settingsServersOptionsForcePlaintextPasswordDescriptionOff": {},
"settingsServersOptionsForcePlaintextPasswordDescriptionOn": "Enviar senha em texto simples (antigo, certifique-se que a sua ligação é segura!)",
"@settingsServersOptionsForcePlaintextPasswordDescriptionOn": {},
"settingsServersOptionsForcePlaintextPasswordTitle": "Forçar password em texto simples",
"@settingsServersOptionsForcePlaintextPasswordTitle": {}
}
"actionsStar": "Favorito",
"@actionsStar": {},
"actionsUnstar": "Remover favorito",
"@actionsUnstar": {},
"messagesNothingHere": "Não existe nada…",
"@messagesNothingHere": {},
"navigationTabsHome": "Início",
"@navigationTabsHome": {},
"navigationTabsLibrary": "Biblioteca",
"@navigationTabsLibrary": {},
"navigationTabsSearch": "Procurar",
"@navigationTabsSearch": {},
"navigationTabsSettings": "Definições",
"@navigationTabsSettings": {},
"resourcesAlbumActionsPlay": "Tocar Álbum",
"@resourcesAlbumActionsPlay": {},
"resourcesAlbumActionsView": "Ver Álbum",
"@resourcesAlbumActionsView": {},
"resourcesAlbumListsSort": "Ordenar Álbuns",
"@resourcesAlbumListsSort": {},
"resourcesAlbumName": "{count,plural, =1{Álbum} other{Álbuns}}",
"@resourcesAlbumName": {
"placeholders": {
"count": {
"type": "int"
}
}
},
"resourcesArtistActionsView": "Ver Artista",
"@resourcesArtistActionsView": {},
"resourcesArtistListsSort": "Ordenar Artistas",
"@resourcesArtistListsSort": {},
"resourcesArtistName": "{count,plural, =1{Artista} other{Artistas}}",
"@resourcesArtistName": {
"placeholders": {
"count": {
"type": "int"
}
}
},
"resourcesFilterGenre": "Por Género",
"@resourcesFilterGenre": {},
"resourcesFilterStarred": "Favoritos",
"@resourcesFilterStarred": {},
"resourcesPlaylistActionsPlay": "Tocar Playlist",
"@resourcesPlaylistActionsPlay": {},
"resourcesPlaylistName": "{count,plural, =1{Lista} other{Listas}}",
"@resourcesPlaylistName": {
"placeholders": {
"count": {
"type": "int"
}
}
},
"resourcesQueueName": "{count,plural, =1{Fila} other{Filas}}",
"@resourcesQueueName": {
"placeholders": {
"count": {
"type": "int"
}
}
},
"resourcesSongListsArtistTopSongs": "Top Músicas",
"@resourcesSongListsArtistTopSongs": {},
"resourcesSongName": "{count,plural, =1{Música} other{Músicas}}",
"@resourcesSongName": {
"placeholders": {
"count": {
"type": "int"
}
}
},
"resourcesSortByAdded": "Adicionado recentemente",
"@resourcesSortByAdded": {},
"resourcesSortByArtist": "Por Artista",
"@resourcesSortByArtist": {},
"resourcesSortByFrequentlyPlayed": "Mais Tocado",
"@resourcesSortByFrequentlyPlayed": {},
"resourcesSortByName": "Por Nome",
"@resourcesSortByName": {},
"resourcesSortByRandom": "Aleatório",
"@resourcesSortByRandom": {},
"resourcesSortByRecentlyPlayed": "Ouviu recentemente",
"@resourcesSortByRecentlyPlayed": {},
"resourcesSortByYear": "Por Ano",
"@resourcesSortByYear": {},
"searchHeaderTitle": "Procurar: {query}",
"@searchHeaderTitle": {
"placeholders": {
"query": {
"type": "String"
}
}
},
"searchInputPlaceholder": "Procurar",
"@searchInputPlaceholder": {},
"searchMoreResults": "Mais…",
"@searchMoreResults": {},
"searchNowPlayingContext": "Resultados da Pesquisa",
"@searchNowPlayingContext": {},
"settingsAboutActionsLicenses": "Licenças",
"@settingsAboutActionsLicenses": {},
"settingsAboutActionsProjectHomepage": "Página do Projeto",
"@settingsAboutActionsProjectHomepage": {},
"settingsAboutName": "Acerca",
"@settingsAboutName": {},
"settingsAboutVersion": "versão {version}",
"@settingsAboutVersion": {
"placeholders": {
"version": {
"type": "String"
}
}
},
"settingsMusicName": "Música",
"@settingsMusicName": {},
"settingsMusicOptionsScrobbleDescriptionOff": "Não enviar histórico de reproduções por scrobble",
"@settingsMusicOptionsScrobbleDescriptionOff": {},
"settingsMusicOptionsScrobbleDescriptionOn": "Enviar histórico de reproduções por scrobble",
"@settingsMusicOptionsScrobbleDescriptionOn": {},
"settingsMusicOptionsScrobbleTitle": "Enviar reproduções por scrobble",
"@settingsMusicOptionsScrobbleTitle": {},
"settingsNetworkName": "Rede",
"@settingsNetworkName": {},
"settingsNetworkOptionsMaxBitrateMobileTitle": "Bitrate Máximo (móvel)",
"@settingsNetworkOptionsMaxBitrateMobileTitle": {},
"settingsNetworkOptionsMaxBitrateWifiTitle": "Bitrate Máximo (Wi-Fi)",
"@settingsNetworkOptionsMaxBitrateWifiTitle": {},
"settingsNetworkOptionsMaxBufferTitle": "Tempo de buffer máximo",
"@settingsNetworkOptionsMaxBufferTitle": {},
"settingsNetworkOptionsMinBufferTitle": "Tempo de buffer mínimo",
"@settingsNetworkOptionsMinBufferTitle": {},
"settingsNetworkValuesKbps": "{value}kbps",
"@settingsNetworkValuesKbps": {
"placeholders": {
"value": {
"type": "String"
}
}
},
"settingsNetworkValuesSeconds": "{value} segundos",
"@settingsNetworkValuesSeconds": {
"placeholders": {
"value": {
"type": "String"
}
}
},
"settingsNetworkValuesUnlimitedKbps": "Ilimitado",
"@settingsNetworkValuesUnlimitedKbps": {},
"settingsResetActionsClearImageCache": "Limpar cache de Imagens",
"@settingsResetActionsClearImageCache": {},
"settingsResetName": "Redefinir",
"@settingsResetName": {},
"settingsServersActionsAdd": "Adicionar Servidor",
"@settingsServersActionsAdd": {},
"settingsServersActionsDelete": "Apagar",
"@settingsServersActionsDelete": {},
"settingsServersActionsEdit": "Editar Servidor",
"@settingsServersActionsEdit": {},
"settingsServersActionsSave": "Guardar",
"@settingsServersActionsSave": {},
"settingsServersActionsTestConnection": "Testar Ligação",
"@settingsServersActionsTestConnection": {},
"settingsServersFieldsAddress": "Endereço",
"@settingsServersFieldsAddress": {},
"settingsServersFieldsPassword": "Senha",
"@settingsServersFieldsPassword": {},
"settingsServersFieldsUsername": "Nome de utilizador",
"@settingsServersFieldsUsername": {},
"settingsServersMessagesConnectionFailed": "Ligação a {address} falhou, verifique definições ou servidor",
"@settingsServersMessagesConnectionFailed": {
"placeholders": {
"address": {
"type": "String"
}
}
},
"settingsServersMessagesConnectionOk": "Ligação a {address} OK!",
"@settingsServersMessagesConnectionOk": {
"placeholders": {
"address": {
"type": "String"
}
}
},
"settingsServersName": "Servidores",
"@settingsServersName": {},
"settingsServersOptionsForcePlaintextPasswordDescriptionOff": "Enviar senha como token + sal",
"@settingsServersOptionsForcePlaintextPasswordDescriptionOff": {},
"settingsServersOptionsForcePlaintextPasswordDescriptionOn": "Enviar senha em texto simples (antigo, certifique-se que a sua ligação é segura!)",
"@settingsServersOptionsForcePlaintextPasswordDescriptionOn": {},
"settingsServersOptionsForcePlaintextPasswordTitle": "Forçar password em texto simples",
"@settingsServersOptionsForcePlaintextPasswordTitle": {},
"actionsCancel": "Cancelar",
"@actionsCancel": {},
"actionsDelete": "Apagar",
"@actionsDelete": {},
"actionsDownload": "Descarregar",
"@actionsDownload": {},
"actionsDownloadCancel": "Cancelar descarga",
"@actionsDownloadCancel": {},
"actionsDownloadDelete": "Apagar descarga",
"@actionsDownloadDelete": {},
"resourcesFilterAlbum": "Álbum",
"@resourcesFilterAlbum": {},
"resourcesFilterArtist": "Artista",
"@resourcesFilterArtist": {},
"resourcesFilterYear": "Ano",
"@resourcesFilterYear": {},
"resourcesSortByAlbum": "Álbum",
"@resourcesSortByAlbum": {},
"settingsAboutActionsSupport": "Apoie o programador 💜",
"@settingsAboutActionsSupport": {},
"settingsNetworkOptionsOfflineMode": "Modo offline",
"@settingsNetworkOptionsOfflineMode": {},
"settingsNetworkOptionsOfflineModeOff": "Usar a internet para sincronizar música.",
"@settingsNetworkOptionsOfflineModeOff": {},
"settingsNetworkOptionsOfflineModeOn": "Não usar a internet para sincronizar ou tocar música.",
"@settingsNetworkOptionsOfflineModeOn": {},
"settingsNetworkOptionsStreamFormat": "Formato preferido de streaming",
"@settingsNetworkOptionsStreamFormat": {},
"resourcesSortByTitle": "Título",
"@resourcesSortByTitle": {},
"actionsOk": "OK",
"@actionsOk": {},
"controlsShuffle": "Aleatório",
"@controlsShuffle": {}
}

View File

@@ -1,196 +1,280 @@
{
"actionsStar": "Избранное",
"@actionsStar": {},
"actionsUnstar": "Убрать из избранного",
"@actionsUnstar": {},
"messagesNothingHere": "Здесь ничего нет…",
"@messagesNothingHere": {},
"navigationTabsHome": "Главная",
"@navigationTabsHome": {},
"navigationTabsLibrary": "Библиотека",
"@navigationTabsLibrary": {},
"navigationTabsSearch": "Поиск",
"@navigationTabsSearch": {},
"navigationTabsSettings": "Настройки",
"@navigationTabsSettings": {},
"resourcesAlbumActionsPlay": "Воспроизвести альбом",
"@resourcesAlbumActionsPlay": {},
"resourcesAlbumActionsView": "Посмотреть альбом",
"@resourcesAlbumActionsView": {},
"resourcesAlbumListsSort": "Сортировка альбомов",
"@resourcesAlbumListsSort": {},
"resourcesAlbumName": "{count,plural, =1{Альбом} few{Альбомы} many{Альбомов} other{Альбомов}}",
"@resourcesAlbumName": {
"placeholders": {
"count": {
"type": "int"
}
}
},
"resourcesArtistActionsView": "Посмотреть исполнителя",
"@resourcesArtistActionsView": {},
"resourcesArtistListsSort": "Сортировать исполнителей",
"@resourcesArtistListsSort": {},
"resourcesArtistName": "{count,plural, =1{Исполнитель} few{Исполнители} many{Исполнителей} other{Исполнителей}}",
"@resourcesArtistName": {
"placeholders": {
"count": {
"type": "int"
}
}
},
"resourcesFilterGenre": "По жанру",
"@resourcesFilterGenre": {},
"resourcesFilterStarred": "Избранные",
"@resourcesFilterStarred": {},
"resourcesPlaylistActionsPlay": "Воспроизвести плейлист",
"@resourcesPlaylistActionsPlay": {},
"resourcesPlaylistName": "{count,plural, =1{Плейлист} few{Плейлисты} many{Плейлистов} other{Плейлистов}}",
"@resourcesPlaylistName": {
"placeholders": {
"count": {
"type": "int"
}
}
},
"resourcesQueueName": "{count,plural, =1{Очередь} few{Очереди} many{Очередей} other{Очередей}}",
"@resourcesQueueName": {
"placeholders": {
"count": {
"type": "int"
}
}
},
"resourcesSongListsArtistTopSongs": "Лучшие треки",
"@resourcesSongListsArtistTopSongs": {},
"resourcesSongName": "{count,plural, =1{Трек} few{Трека} many{Треков} other{Треков}}",
"@resourcesSongName": {
"placeholders": {
"count": {
"type": "int"
}
}
},
"resourcesSortByAdded": "Недавно добавленные",
"@resourcesSortByAdded": {},
"resourcesSortByArtist": "По исполнителю",
"@resourcesSortByArtist": {},
"resourcesSortByFrequentlyPlayed": "Часто проигрываемые",
"@resourcesSortByFrequentlyPlayed": {},
"resourcesSortByName": "По имени",
"@resourcesSortByName": {},
"resourcesSortByRandom": "Случайно",
"@resourcesSortByRandom": {},
"resourcesSortByRecentlyPlayed": "Недавно проигранные",
"@resourcesSortByRecentlyPlayed": {},
"resourcesSortByYear": "По году",
"@resourcesSortByYear": {},
"searchHeaderTitle": "Поиск: {query}",
"@searchHeaderTitle": {
"placeholders": {
"query": {
"type": "String"
}
}
},
"searchInputPlaceholder": "Поиск",
"@searchInputPlaceholder": {},
"searchMoreResults": "Больше…",
"@searchMoreResults": {},
"searchNowPlayingContext": "Результаты поиска",
"@searchNowPlayingContext": {},
"settingsAboutActionsLicenses": "Лицензии",
"@settingsAboutActionsLicenses": {},
"settingsAboutActionsProjectHomepage": "Сайт проекта",
"@settingsAboutActionsProjectHomepage": {},
"settingsAboutName": "О Subtracks",
"@settingsAboutName": {},
"settingsAboutVersion": "версия {version}",
"@settingsAboutVersion": {
"placeholders": {
"version": {
"type": "String"
}
}
},
"settingsMusicName": "Музыка",
"@settingsMusicName": {},
"settingsMusicOptionsScrobbleDescriptionOff": "Не отправлять историю воспроизведений",
"@settingsMusicOptionsScrobbleDescriptionOff": {},
"settingsMusicOptionsScrobbleDescriptionOn": "Скробблинг истории воспроизведения",
"@settingsMusicOptionsScrobbleDescriptionOn": {},
"settingsMusicOptionsScrobbleTitle": "Скробблинг",
"@settingsMusicOptionsScrobbleTitle": {},
"settingsNetworkName": "Сеть",
"@settingsNetworkName": {},
"settingsNetworkOptionsMaxBitrateMobileTitle": "Максимальный битрейт (мобильный интернет)",
"@settingsNetworkOptionsMaxBitrateMobileTitle": {},
"settingsNetworkOptionsMaxBitrateWifiTitle": "Максимальный битрейт (Wi-Fi)",
"@settingsNetworkOptionsMaxBitrateWifiTitle": {},
"settingsNetworkOptionsMaxBufferTitle": "Максимальное время буферизации",
"@settingsNetworkOptionsMaxBufferTitle": {},
"settingsNetworkOptionsMinBufferTitle": "Минимальное время буферизации",
"@settingsNetworkOptionsMinBufferTitle": {},
"settingsNetworkValuesKbps": "{value} кбит/с",
"@settingsNetworkValuesKbps": {
"placeholders": {
"value": {
"type": "String"
}
}
},
"settingsNetworkValuesSeconds": "{value} секунд",
"@settingsNetworkValuesSeconds": {
"placeholders": {
"value": {
"type": "String"
}
}
},
"settingsNetworkValuesUnlimitedKbps": "Без ограничений",
"@settingsNetworkValuesUnlimitedKbps": {},
"settingsResetActionsClearImageCache": "Очистить кэш изображения",
"@settingsResetActionsClearImageCache": {},
"settingsResetName": "Сброс",
"@settingsResetName": {},
"settingsServersActionsAdd": "Добавить сервер",
"@settingsServersActionsAdd": {},
"settingsServersActionsDelete": "Удалить",
"@settingsServersActionsDelete": {},
"settingsServersActionsEdit": "Редактировать сервер",
"@settingsServersActionsEdit": {},
"settingsServersActionsSave": "Сохранить",
"@settingsServersActionsSave": {},
"settingsServersActionsTestConnection": "Проверить подключение",
"@settingsServersActionsTestConnection": {},
"settingsServersFieldsAddress": "Адрес",
"@settingsServersFieldsAddress": {},
"settingsServersFieldsPassword": "Пароль",
"@settingsServersFieldsPassword": {},
"settingsServersFieldsUsername": "Имя пользователя",
"@settingsServersFieldsUsername": {},
"settingsServersMessagesConnectionFailed": "Не удалось подключиться к {address}, проверьте настройки или сервер",
"@settingsServersMessagesConnectionFailed": {
"placeholders": {
"address": {
"type": "String"
}
}
},
"settingsServersMessagesConnectionOk": "Подключение к {address} установлено!",
"@settingsServersMessagesConnectionOk": {
"placeholders": {
"address": {
"type": "String"
}
}
},
"settingsServersName": "Серверы",
"@settingsServersName": {},
"settingsServersOptionsForcePlaintextPasswordDescriptionOff": "Отправить пароль в виде токена",
"@settingsServersOptionsForcePlaintextPasswordDescriptionOff": {},
"settingsServersOptionsForcePlaintextPasswordDescriptionOn": "Отправить пароль в виде текста (устарело, убедитесь, что ваше соединение безопасно!)",
"@settingsServersOptionsForcePlaintextPasswordDescriptionOn": {},
"settingsServersOptionsForcePlaintextPasswordTitle": "Принудительно использовать текстовой пароль",
"@settingsServersOptionsForcePlaintextPasswordTitle": {}
}
"actionsStar": "Избранное",
"@actionsStar": {},
"actionsUnstar": "Убрать из избранного",
"@actionsUnstar": {},
"messagesNothingHere": "Здесь ничего нет…",
"@messagesNothingHere": {},
"navigationTabsHome": "Главная",
"@navigationTabsHome": {},
"navigationTabsLibrary": "Библиотека",
"@navigationTabsLibrary": {},
"navigationTabsSearch": "Поиск",
"@navigationTabsSearch": {},
"navigationTabsSettings": "Настройки",
"@navigationTabsSettings": {},
"resourcesAlbumActionsPlay": "Воспроизвести альбом",
"@resourcesAlbumActionsPlay": {},
"resourcesAlbumActionsView": "Посмотреть альбом",
"@resourcesAlbumActionsView": {},
"resourcesAlbumListsSort": "Сортировка альбомов",
"@resourcesAlbumListsSort": {},
"resourcesAlbumName": "{count,plural, =1{Альбом} few{Альбомы} many{Альбомов} other{Альбомов}}",
"@resourcesAlbumName": {
"placeholders": {
"count": {
"type": "int"
}
}
},
"resourcesArtistActionsView": "Посмотреть исполнителя",
"@resourcesArtistActionsView": {},
"resourcesArtistListsSort": "Сортировать исполнителей",
"@resourcesArtistListsSort": {},
"resourcesArtistName": "{count,plural, =1{Исполнитель} few{Исполнители} many{Исполнителей} other{Исполнителей}}",
"@resourcesArtistName": {
"placeholders": {
"count": {
"type": "int"
}
}
},
"resourcesFilterGenre": "По жанру",
"@resourcesFilterGenre": {},
"resourcesFilterStarred": "Избранные",
"@resourcesFilterStarred": {},
"resourcesPlaylistActionsPlay": "Воспроизвести плейлист",
"@resourcesPlaylistActionsPlay": {},
"resourcesPlaylistName": "{count,plural, =1{Плейлист} few{Плейлисты} many{Плейлистов} other{Плейлистов}}",
"@resourcesPlaylistName": {
"placeholders": {
"count": {
"type": "int"
}
}
},
"resourcesQueueName": "{count,plural, =1{Очередь} few{Очереди} many{Очередей} other{Очередей}}",
"@resourcesQueueName": {
"placeholders": {
"count": {
"type": "int"
}
}
},
"resourcesSongListsArtistTopSongs": "Лучшие треки",
"@resourcesSongListsArtistTopSongs": {},
"resourcesSongName": "{count,plural, =1{Трек} few{Трека} many{Треков} other{Треков}}",
"@resourcesSongName": {
"placeholders": {
"count": {
"type": "int"
}
}
},
"resourcesSortByAdded": "Недавно добавленные",
"@resourcesSortByAdded": {},
"resourcesSortByArtist": "По исполнителям",
"@resourcesSortByArtist": {},
"resourcesSortByFrequentlyPlayed": "Часто проигрываемые",
"@resourcesSortByFrequentlyPlayed": {},
"resourcesSortByName": "По имени",
"@resourcesSortByName": {},
"resourcesSortByRandom": "Случайно",
"@resourcesSortByRandom": {},
"resourcesSortByRecentlyPlayed": "Недавно проигранные",
"@resourcesSortByRecentlyPlayed": {},
"resourcesSortByYear": "По году",
"@resourcesSortByYear": {},
"searchHeaderTitle": "Поиск: {query}",
"@searchHeaderTitle": {
"placeholders": {
"query": {
"type": "String"
}
}
},
"searchInputPlaceholder": "Поиск",
"@searchInputPlaceholder": {},
"searchMoreResults": "Больше…",
"@searchMoreResults": {},
"searchNowPlayingContext": "Результаты поиска",
"@searchNowPlayingContext": {},
"settingsAboutActionsLicenses": "Лицензии",
"@settingsAboutActionsLicenses": {},
"settingsAboutActionsProjectHomepage": "Сайт проекта",
"@settingsAboutActionsProjectHomepage": {},
"settingsAboutName": "О Subtracks",
"@settingsAboutName": {},
"settingsAboutVersion": "версия {version}",
"@settingsAboutVersion": {
"placeholders": {
"version": {
"type": "String"
}
}
},
"settingsMusicName": "Музыка",
"@settingsMusicName": {},
"settingsMusicOptionsScrobbleDescriptionOff": "Не отправлять историю воспроизведений",
"@settingsMusicOptionsScrobbleDescriptionOff": {},
"settingsMusicOptionsScrobbleDescriptionOn": "Скробблинг истории воспроизведения",
"@settingsMusicOptionsScrobbleDescriptionOn": {},
"settingsMusicOptionsScrobbleTitle": "Скробблинг",
"@settingsMusicOptionsScrobbleTitle": {},
"settingsNetworkName": "Сеть",
"@settingsNetworkName": {},
"settingsNetworkOptionsMaxBitrateMobileTitle": "Максимальный битрейт (мобильный интернет)",
"@settingsNetworkOptionsMaxBitrateMobileTitle": {},
"settingsNetworkOptionsMaxBitrateWifiTitle": "Максимальный битрейт (Wi-Fi)",
"@settingsNetworkOptionsMaxBitrateWifiTitle": {},
"settingsNetworkOptionsMaxBufferTitle": "Максимальное время буферизации",
"@settingsNetworkOptionsMaxBufferTitle": {},
"settingsNetworkOptionsMinBufferTitle": "Минимальное время буферизации",
"@settingsNetworkOptionsMinBufferTitle": {},
"settingsNetworkValuesKbps": "{value} кбит/с",
"@settingsNetworkValuesKbps": {
"placeholders": {
"value": {
"type": "String"
}
}
},
"settingsNetworkValuesSeconds": "{value} секунд",
"@settingsNetworkValuesSeconds": {
"placeholders": {
"value": {
"type": "String"
}
}
},
"settingsNetworkValuesUnlimitedKbps": "Без ограничений",
"@settingsNetworkValuesUnlimitedKbps": {},
"settingsResetActionsClearImageCache": "Очистить кэш изображения",
"@settingsResetActionsClearImageCache": {},
"settingsResetName": "Сброс",
"@settingsResetName": {},
"settingsServersActionsAdd": "Добавить сервер",
"@settingsServersActionsAdd": {},
"settingsServersActionsDelete": "Удалить",
"@settingsServersActionsDelete": {},
"settingsServersActionsEdit": "Редактировать сервер",
"@settingsServersActionsEdit": {},
"settingsServersActionsSave": "Сохранить",
"@settingsServersActionsSave": {},
"settingsServersActionsTestConnection": "Проверить подключение",
"@settingsServersActionsTestConnection": {},
"settingsServersFieldsAddress": "Адрес",
"@settingsServersFieldsAddress": {},
"settingsServersFieldsPassword": "Пароль",
"@settingsServersFieldsPassword": {},
"settingsServersFieldsUsername": "Имя пользователя",
"@settingsServersFieldsUsername": {},
"settingsServersMessagesConnectionFailed": "Не удалось подключиться к {address}, проверьте настройки или сервер",
"@settingsServersMessagesConnectionFailed": {
"placeholders": {
"address": {
"type": "String"
}
}
},
"settingsServersMessagesConnectionOk": "Подключение к {address} установлено!",
"@settingsServersMessagesConnectionOk": {
"placeholders": {
"address": {
"type": "String"
}
}
},
"settingsServersName": "Серверы",
"@settingsServersName": {},
"settingsServersOptionsForcePlaintextPasswordDescriptionOff": "Отправить пароль в виде токена",
"@settingsServersOptionsForcePlaintextPasswordDescriptionOff": {},
"settingsServersOptionsForcePlaintextPasswordDescriptionOn": "Отправить пароль в виде текста (устарело, убедитесь, что ваше соединение безопасно!)",
"@settingsServersOptionsForcePlaintextPasswordDescriptionOn": {},
"settingsServersOptionsForcePlaintextPasswordTitle": "Принудительно использовать текстовой пароль",
"@settingsServersOptionsForcePlaintextPasswordTitle": {},
"settingsAboutShareLogs": "Поделиться журналами",
"@settingsAboutShareLogs": {},
"settingsAboutChooseLog": "Выбрать файл журнала",
"@settingsAboutChooseLog": {},
"settingsNetworkOptionsStreamFormatServerDefault": "Использовать сервер по умолчанию",
"@settingsNetworkOptionsStreamFormatServerDefault": {},
"actionsDownload": "Скачать",
"@actionsDownload": {},
"actionsDownloadCancel": "Отменить загрузку",
"@actionsDownloadCancel": {},
"actionsCancel": "Отменить",
"@actionsCancel": {},
"resourcesSongCount": "{count,plural, =1{{count} трек} other{{count} треки}}",
"@resourcesSongCount": {
"placeholders": {
"count": {
"type": "int"
}
}
},
"resourcesSortByAlbum": "По альбомам",
"@resourcesSortByAlbum": {},
"resourcesSortByTitle": "По заголовку",
"@resourcesSortByTitle": {},
"resourcesSortByUpdated": "По недавно обновленному",
"@resourcesSortByUpdated": {},
"resourcesSortByAlbumCount": "По количеству альбомов",
"@resourcesSortByAlbumCount": {},
"settingsNetworkOptionsOfflineMode": "Автономный режим",
"@settingsNetworkOptionsOfflineMode": {},
"settingsNetworkOptionsOfflineModeOff": "Использовать интернет для синхронизации музыки.",
"@settingsNetworkOptionsOfflineModeOff": {},
"settingsServersFieldsName": "Имя",
"@settingsServersFieldsName": {},
"actionsDelete": "Удалить",
"@actionsDelete": {},
"actionsDownloadDelete": "Удалить загруженное",
"@actionsDownloadDelete": {},
"actionsOk": "ОК",
"@actionsOk": {},
"controlsShuffle": "Перемешать",
"@controlsShuffle": {},
"resourcesFilterArtist": "По исполнителю",
"@resourcesFilterArtist": {},
"resourcesFilterAlbum": "По альбомам",
"@resourcesFilterAlbum": {},
"resourcesFilterYear": "По годам",
"@resourcesFilterYear": {},
"resourcesFilterOwner": "По владельцу",
"@resourcesFilterOwner": {},
"resourcesSongListDeleteAllContent": "Это удалит все загруженные файлы песен.",
"@resourcesSongListDeleteAllContent": {},
"settingsNetworkOptionsStreamFormat": "Предпочтительный формат потока",
"@settingsNetworkOptionsStreamFormat": {},
"resourcesSongListDeleteAllTitle": "Удалить загрузки?",
"@resourcesSongListDeleteAllTitle": {},
"settingsNetworkOptionsOfflineModeOn": "Не использовать интернет для синхронизации или воспроизведения музыки.",
"@settingsNetworkOptionsOfflineModeOn": {},
"settingsAboutActionsSupport": "Поддержать разработчика",
"@settingsAboutActionsSupport": {},
"resourcesArtistCount": "{count,plural, =1{{count} исполнитель} other{{count} исполнители}}",
"@resourcesArtistCount": {
"placeholders": {
"count": {
"type": "int"
}
}
},
"resourcesPlaylistCount": "{count,plural, =1{{count} плейлист} other{{count} плейлисты}}",
"@resourcesPlaylistCount": {
"placeholders": {
"count": {
"type": "int"
}
}
},
"resourcesAlbumCount": "{count,plural, =1{{count} альбом} other{{count} альбомы}}",
"@resourcesAlbumCount": {
"placeholders": {
"count": {
"type": "int"
}
}
}
}

View File

@@ -1,196 +1,230 @@
{
"actionsStar": "收藏",
"@actionsStar": {},
"actionsUnstar": "移除收藏",
"@actionsUnstar": {},
"messagesNothingHere": "什么都没有…",
"@messagesNothingHere": {},
"navigationTabsHome": "首页",
"@navigationTabsHome": {},
"navigationTabsLibrary": "所有",
"@navigationTabsLibrary": {},
"navigationTabsSearch": "搜索",
"@navigationTabsSearch": {},
"navigationTabsSettings": "设置",
"@navigationTabsSettings": {},
"resourcesAlbumActionsPlay": "播放专辑",
"@resourcesAlbumActionsPlay": {},
"resourcesAlbumActionsView": "查看专辑",
"@resourcesAlbumActionsView": {},
"resourcesAlbumListsSort": "专辑排序",
"@resourcesAlbumListsSort": {},
"resourcesAlbumName": "{count,plural, other{专辑}}",
"@resourcesAlbumName": {
"placeholders": {
"count": {
"type": "int"
}
}
},
"resourcesArtistActionsView": "查看歌手",
"@resourcesArtistActionsView": {},
"resourcesArtistListsSort": "歌手排序",
"@resourcesArtistListsSort": {},
"resourcesArtistName": "{count,plural, other{歌手}}",
"@resourcesArtistName": {
"placeholders": {
"count": {
"type": "int"
}
}
},
"resourcesFilterGenre": "根据类型",
"@resourcesFilterGenre": {},
"resourcesFilterStarred": "已收藏",
"@resourcesFilterStarred": {},
"resourcesPlaylistActionsPlay": "全部播放",
"@resourcesPlaylistActionsPlay": {},
"resourcesPlaylistName": "{count,plural, other{播放列表}}",
"@resourcesPlaylistName": {
"placeholders": {
"count": {
"type": "int"
}
}
},
"resourcesQueueName": "{count,plural, other{队列}}",
"@resourcesQueueName": {
"placeholders": {
"count": {
"type": "int"
}
}
},
"resourcesSongListsArtistTopSongs": "热门歌曲",
"@resourcesSongListsArtistTopSongs": {},
"resourcesSongName": "{count,plural, other{歌曲}}",
"@resourcesSongName": {
"placeholders": {
"count": {
"type": "int"
}
}
},
"resourcesSortByAdded": "最近添加",
"@resourcesSortByAdded": {},
"resourcesSortByArtist": "根据歌手",
"@resourcesSortByArtist": {},
"resourcesSortByFrequentlyPlayed": "播放最多",
"@resourcesSortByFrequentlyPlayed": {},
"resourcesSortByName": "根据名称",
"@resourcesSortByName": {},
"resourcesSortByRandom": "随机",
"@resourcesSortByRandom": {},
"resourcesSortByRecentlyPlayed": "最近播放",
"@resourcesSortByRecentlyPlayed": {},
"resourcesSortByYear": "根据年份",
"@resourcesSortByYear": {},
"searchHeaderTitle": "搜索: {query}",
"@searchHeaderTitle": {
"placeholders": {
"query": {
"type": "String"
}
}
},
"searchInputPlaceholder": "搜索",
"@searchInputPlaceholder": {},
"searchMoreResults": "更多…",
"@searchMoreResults": {},
"searchNowPlayingContext": "搜索结果",
"@searchNowPlayingContext": {},
"settingsAboutActionsLicenses": "许可",
"@settingsAboutActionsLicenses": {},
"settingsAboutActionsProjectHomepage": "项目地址",
"@settingsAboutActionsProjectHomepage": {},
"settingsAboutName": "关于",
"@settingsAboutName": {},
"settingsAboutVersion": "版本 {version}",
"@settingsAboutVersion": {
"placeholders": {
"version": {
"type": "String"
}
}
},
"settingsMusicName": "音乐",
"@settingsMusicName": {},
"settingsMusicOptionsScrobbleDescriptionOff": "不记录scrobble历史",
"@settingsMusicOptionsScrobbleDescriptionOff": {},
"settingsMusicOptionsScrobbleDescriptionOn": "Scrobble播放历史",
"@settingsMusicOptionsScrobbleDescriptionOn": {},
"settingsMusicOptionsScrobbleTitle": "Scrobble模式",
"@settingsMusicOptionsScrobbleTitle": {},
"settingsNetworkName": "网络",
"@settingsNetworkName": {},
"settingsNetworkOptionsMaxBitrateMobileTitle": "最大比特率 (3G/4G/5G)",
"@settingsNetworkOptionsMaxBitrateMobileTitle": {},
"settingsNetworkOptionsMaxBitrateWifiTitle": "最大比特率 (Wi-Fi)",
"@settingsNetworkOptionsMaxBitrateWifiTitle": {},
"settingsNetworkOptionsMaxBufferTitle": "最大缓冲时间",
"@settingsNetworkOptionsMaxBufferTitle": {},
"settingsNetworkOptionsMinBufferTitle": "最小缓冲时间",
"@settingsNetworkOptionsMinBufferTitle": {},
"settingsNetworkValuesKbps": "{value}kbps",
"@settingsNetworkValuesKbps": {
"placeholders": {
"value": {
"type": "String"
}
}
},
"settingsNetworkValuesSeconds": "{value} 秒",
"@settingsNetworkValuesSeconds": {
"placeholders": {
"value": {
"type": "String"
}
}
},
"settingsNetworkValuesUnlimitedKbps": "不限制",
"@settingsNetworkValuesUnlimitedKbps": {},
"settingsResetActionsClearImageCache": "清除图片缓存",
"@settingsResetActionsClearImageCache": {},
"settingsResetName": "重置",
"@settingsResetName": {},
"settingsServersActionsAdd": "添加服务器",
"@settingsServersActionsAdd": {},
"settingsServersActionsDelete": "删除",
"@settingsServersActionsDelete": {},
"settingsServersActionsEdit": "编辑服务器",
"@settingsServersActionsEdit": {},
"settingsServersActionsSave": "保存",
"@settingsServersActionsSave": {},
"settingsServersActionsTestConnection": "测试连接",
"@settingsServersActionsTestConnection": {},
"settingsServersFieldsAddress": "地址",
"@settingsServersFieldsAddress": {},
"settingsServersFieldsPassword": "密码",
"@settingsServersFieldsPassword": {},
"settingsServersFieldsUsername": "用户名",
"@settingsServersFieldsUsername": {},
"settingsServersMessagesConnectionFailed": "连接到 {address} 失败,检查设置或服务器",
"@settingsServersMessagesConnectionFailed": {
"placeholders": {
"address": {
"type": "String"
}
}
},
"settingsServersMessagesConnectionOk": "连接到 {address} 正常!",
"@settingsServersMessagesConnectionOk": {
"placeholders": {
"address": {
"type": "String"
}
}
},
"settingsServersName": "服务器",
"@settingsServersName": {},
"settingsServersOptionsForcePlaintextPasswordDescriptionOff": "密码以 token + salt 加密发送",
"@settingsServersOptionsForcePlaintextPasswordDescriptionOff": {},
"settingsServersOptionsForcePlaintextPasswordDescriptionOn": "密码以明文发送(不推荐,注意链接安全!)",
"@settingsServersOptionsForcePlaintextPasswordDescriptionOn": {},
"settingsServersOptionsForcePlaintextPasswordTitle": "强制使用明文密码",
"@settingsServersOptionsForcePlaintextPasswordTitle": {}
}
"actionsStar": "收藏",
"@actionsStar": {},
"actionsUnstar": "移除收藏",
"@actionsUnstar": {},
"messagesNothingHere": "什么都没有…",
"@messagesNothingHere": {},
"navigationTabsHome": "首页",
"@navigationTabsHome": {},
"navigationTabsLibrary": "所有",
"@navigationTabsLibrary": {},
"navigationTabsSearch": "搜索",
"@navigationTabsSearch": {},
"navigationTabsSettings": "设置",
"@navigationTabsSettings": {},
"resourcesAlbumActionsPlay": "播放专辑",
"@resourcesAlbumActionsPlay": {},
"resourcesAlbumActionsView": "查看专辑",
"@resourcesAlbumActionsView": {},
"resourcesAlbumListsSort": "专辑排序",
"@resourcesAlbumListsSort": {},
"resourcesAlbumName": "{count,plural, other{专辑}}",
"@resourcesAlbumName": {
"placeholders": {
"count": {
"type": "int"
}
}
},
"resourcesArtistActionsView": "查看歌手",
"@resourcesArtistActionsView": {},
"resourcesArtistListsSort": "歌手排序",
"@resourcesArtistListsSort": {},
"resourcesArtistName": "{count,plural, other{歌手}}",
"@resourcesArtistName": {
"placeholders": {
"count": {
"type": "int"
}
}
},
"resourcesFilterGenre": "根据类型",
"@resourcesFilterGenre": {},
"resourcesFilterStarred": "已收藏",
"@resourcesFilterStarred": {},
"resourcesPlaylistActionsPlay": "全部播放",
"@resourcesPlaylistActionsPlay": {},
"resourcesPlaylistName": "{count,plural, other{播放列表}}",
"@resourcesPlaylistName": {
"placeholders": {
"count": {
"type": "int"
}
}
},
"resourcesQueueName": "{count,plural, other{队列}}",
"@resourcesQueueName": {
"placeholders": {
"count": {
"type": "int"
}
}
},
"resourcesSongListsArtistTopSongs": "热门歌曲",
"@resourcesSongListsArtistTopSongs": {},
"resourcesSongName": "{count,plural, other{歌曲}}",
"@resourcesSongName": {
"placeholders": {
"count": {
"type": "int"
}
}
},
"resourcesSortByAdded": "最近添加",
"@resourcesSortByAdded": {},
"resourcesSortByArtist": "根据歌手",
"@resourcesSortByArtist": {},
"resourcesSortByFrequentlyPlayed": "播放最多",
"@resourcesSortByFrequentlyPlayed": {},
"resourcesSortByName": "根据名称",
"@resourcesSortByName": {},
"resourcesSortByRandom": "随机",
"@resourcesSortByRandom": {},
"resourcesSortByRecentlyPlayed": "最近播放",
"@resourcesSortByRecentlyPlayed": {},
"resourcesSortByYear": "根据年份",
"@resourcesSortByYear": {},
"searchHeaderTitle": "搜索: {query}",
"@searchHeaderTitle": {
"placeholders": {
"query": {
"type": "String"
}
}
},
"searchInputPlaceholder": "搜索",
"@searchInputPlaceholder": {},
"searchMoreResults": "更多…",
"@searchMoreResults": {},
"searchNowPlayingContext": "搜索结果",
"@searchNowPlayingContext": {},
"settingsAboutActionsLicenses": "许可",
"@settingsAboutActionsLicenses": {},
"settingsAboutActionsProjectHomepage": "项目地址",
"@settingsAboutActionsProjectHomepage": {},
"settingsAboutName": "关于",
"@settingsAboutName": {},
"settingsAboutVersion": "版本 {version}",
"@settingsAboutVersion": {
"placeholders": {
"version": {
"type": "String"
}
}
},
"settingsMusicName": "音乐",
"@settingsMusicName": {},
"settingsMusicOptionsScrobbleDescriptionOff": "不记录scrobble历史",
"@settingsMusicOptionsScrobbleDescriptionOff": {},
"settingsMusicOptionsScrobbleDescriptionOn": "Scrobble播放历史",
"@settingsMusicOptionsScrobbleDescriptionOn": {},
"settingsMusicOptionsScrobbleTitle": "Scrobble模式",
"@settingsMusicOptionsScrobbleTitle": {},
"settingsNetworkName": "网络",
"@settingsNetworkName": {},
"settingsNetworkOptionsMaxBitrateMobileTitle": "最大比特率 (3G/4G/5G)",
"@settingsNetworkOptionsMaxBitrateMobileTitle": {},
"settingsNetworkOptionsMaxBitrateWifiTitle": "最大比特率 (Wi-Fi)",
"@settingsNetworkOptionsMaxBitrateWifiTitle": {},
"settingsNetworkOptionsMaxBufferTitle": "最大缓冲时间",
"@settingsNetworkOptionsMaxBufferTitle": {},
"settingsNetworkOptionsMinBufferTitle": "最小缓冲时间",
"@settingsNetworkOptionsMinBufferTitle": {},
"settingsNetworkValuesKbps": "{value}kbps",
"@settingsNetworkValuesKbps": {
"placeholders": {
"value": {
"type": "String"
}
}
},
"settingsNetworkValuesSeconds": "{value} 秒",
"@settingsNetworkValuesSeconds": {
"placeholders": {
"value": {
"type": "String"
}
}
},
"settingsNetworkValuesUnlimitedKbps": "不限制",
"@settingsNetworkValuesUnlimitedKbps": {},
"settingsResetActionsClearImageCache": "清除图片缓存",
"@settingsResetActionsClearImageCache": {},
"settingsResetName": "重置",
"@settingsResetName": {},
"settingsServersActionsAdd": "添加服务器",
"@settingsServersActionsAdd": {},
"settingsServersActionsDelete": "删除",
"@settingsServersActionsDelete": {},
"settingsServersActionsEdit": "编辑服务器",
"@settingsServersActionsEdit": {},
"settingsServersActionsSave": "保存",
"@settingsServersActionsSave": {},
"settingsServersActionsTestConnection": "测试连接",
"@settingsServersActionsTestConnection": {},
"settingsServersFieldsAddress": "地址",
"@settingsServersFieldsAddress": {},
"settingsServersFieldsPassword": "密码",
"@settingsServersFieldsPassword": {},
"settingsServersFieldsUsername": "用户名",
"@settingsServersFieldsUsername": {},
"settingsServersMessagesConnectionFailed": "连接到 {address} 失败,检查设置或服务器",
"@settingsServersMessagesConnectionFailed": {
"placeholders": {
"address": {
"type": "String"
}
}
},
"settingsServersMessagesConnectionOk": "连接到 {address} 正常!",
"@settingsServersMessagesConnectionOk": {
"placeholders": {
"address": {
"type": "String"
}
}
},
"settingsServersName": "服务器",
"@settingsServersName": {},
"settingsServersOptionsForcePlaintextPasswordDescriptionOff": "密码以 token + salt 加密发送",
"@settingsServersOptionsForcePlaintextPasswordDescriptionOff": {},
"settingsServersOptionsForcePlaintextPasswordDescriptionOn": "密码以明文发送(不推荐,注意链接安全!)",
"@settingsServersOptionsForcePlaintextPasswordDescriptionOn": {},
"settingsServersOptionsForcePlaintextPasswordTitle": "强制使用明文密码",
"@settingsServersOptionsForcePlaintextPasswordTitle": {},
"actionsDownload": "下载",
"@actionsDownload": {},
"actionsDownloadCancel": "取消下载",
"@actionsDownloadCancel": {},
"actionsDownloadDelete": "删除已下载",
"@actionsDownloadDelete": {},
"actionsOk": "确定",
"@actionsOk": {},
"resourcesFilterArtist": "歌手",
"@resourcesFilterArtist": {},
"resourcesFilterOwner": "所有者",
"@resourcesFilterOwner": {},
"resourcesSongListDeleteAllTitle": "删除下载?",
"@resourcesSongListDeleteAllTitle": {},
"resourcesSortByAlbum": "专辑",
"@resourcesSortByAlbum": {},
"resourcesSortByAlbumCount": "专辑数量",
"@resourcesSortByAlbumCount": {},
"resourcesSortByUpdated": "最近添加",
"@resourcesSortByUpdated": {},
"settingsAboutActionsSupport": "支持开发者",
"@settingsAboutActionsSupport": {},
"resourcesFilterAlbum": "专辑",
"@resourcesFilterAlbum": {},
"resourcesSortByTitle": "标题",
"@resourcesSortByTitle": {},
"actionsCancel": "取消",
"@actionsCancel": {},
"actionsDelete": "删除",
"@actionsDelete": {},
"resourcesFilterYear": "年份",
"@resourcesFilterYear": {},
"resourcesSongListDeleteAllContent": "该操作会删除所有已下载的歌曲文件。",
"@resourcesSongListDeleteAllContent": {}
}

209
lib/log.dart Normal file
View File

@@ -0,0 +1,209 @@
// import 'dart:convert';
import 'dart:io';
import 'package:collection/collection.dart';
import 'package:flutter/foundation.dart';
import 'package:logging/logging.dart';
import 'package:path/path.dart' as p;
import 'package:path_provider/path_provider.dart';
class AnsiColor {
/// ANSI Control Sequence Introducer, signals the terminal for new settings.
static const ansiEsc = '\x1B[';
/// Reset all colors and options for current SGRs to terminal defaults.
static const ansiDefault = '${ansiEsc}0m';
final int? fg;
final int? bg;
final bool color;
AnsiColor.none()
: fg = null,
bg = null,
color = false;
AnsiColor.fg(this.fg)
: bg = null,
color = true;
AnsiColor.bg(this.bg)
: fg = null,
color = true;
@override
String toString() {
if (fg != null) {
return '${ansiEsc}38;5;${fg}m';
} else if (bg != null) {
return '${ansiEsc}48;5;${bg}m';
} else {
return '';
}
}
String call(String msg) {
if (color) {
// ignore: unnecessary_brace_in_string_interps
return '${this}$msg$ansiDefault';
} else {
return msg;
}
}
AnsiColor toFg() => AnsiColor.fg(bg);
AnsiColor toBg() => AnsiColor.bg(fg);
/// Defaults the terminal's foreground color without altering the background.
String get resetForeground => color ? '${ansiEsc}39m' : '';
/// Defaults the terminal's background color without altering the foreground.
String get resetBackground => color ? '${ansiEsc}49m' : '';
static int grey(double level) => 232 + (level.clamp(0.0, 1.0) * 23).round();
}
final levelColors = {
Level.FINEST: AnsiColor.fg(AnsiColor.grey(0.5)),
Level.FINER: AnsiColor.fg(AnsiColor.grey(0.5)),
Level.FINE: AnsiColor.fg(AnsiColor.grey(0.5)),
Level.CONFIG: AnsiColor.fg(81),
Level.INFO: AnsiColor.fg(12),
Level.WARNING: AnsiColor.fg(208),
Level.SEVERE: AnsiColor.fg(196),
Level.SHOUT: AnsiColor.fg(199),
};
class LogData {
final String? message;
final Object? data;
const LogData(this.message, this.data);
}
String _format(
LogRecord event, {
bool color = false,
bool time = true,
bool level = true,
bool redact = true,
}) {
var message = '';
if (time) message += '${event.time.toIso8601String()} ';
if (level) message += '${event.level.name} ';
final object = event.object;
if (object is LogData) {
message += '${object.message}';
message += '\n${object.data}';
} else if (object != null) {
message += 'Object';
message += '\n$object';
} else {
message += event.message;
}
if (event.error != null) {
message += '\n${event.error}';
}
if (redact) {
message = _redactUrl(message);
}
if (event.stackTrace != null) {
message += '\n${event.stackTrace}';
}
return color
? message.split('\n').map((e) => levelColors[event.level]!(e)).join('\n')
: message;
}
String _redactUrl(String message) {
if (!_queryReplace('u').hasMatch(message)) {
return message;
}
message = _redactParam(message, 'u');
message = _redactParam(message, 'p');
message = _redactParam(message, 's');
message = _redactParam(message, 't');
return message;
}
RegExp _queryReplace(String key) => RegExp('$key=([^&|\\n|\\t\\s]+)');
String _redactParam(String url, String key) =>
url.replaceAll(_queryReplace(key), '$key=REDACTED');
Future<Directory> logDirectory() async {
return Directory(
p.join((await getApplicationDocumentsDirectory()).path, 'logs'),
);
}
Future<List<File>> logFiles() async {
final dir = await logDirectory();
return dir.listSync().whereType<File>().toList()
..sort(
(a, b) => b.statSync().modified.compareTo(a.statSync().modified),
);
}
File _currentLogFile(String logDir) {
final now = DateTime.now();
return File(p.join(logDir, '${now.year}-${now.month}-${now.day}.txt'));
}
Future<void> _printFile(String event, String logDir) async {
final file = _currentLogFile(logDir);
if (!event.endsWith('\n')) {
event += '\n';
}
await file.writeAsString(event, mode: FileMode.writeOnlyAppend, flush: true);
}
void _printDebug(LogRecord event) {
// ignore: avoid_print
print(_format(event, color: true, time: false, level: false, redact: false));
}
Future<void> _printRelease(LogRecord event, String logDir) async {
await _printFile(
_format(event, color: false, time: true, level: true, redact: true),
logDir,
);
}
final log = Logger('default');
Future<void> initLogging() async {
final dir = (await logDirectory())..create();
final file = _currentLogFile(dir.path);
if (!(await file.exists())) {
await file.create();
}
final files = await logFiles();
if (files.length > 7) {
for (var file in files.slice(7)) {
await file.delete();
}
}
Logger.root.level = kDebugMode ? Level.ALL : Level.INFO;
Logger.root.onRecord.asyncMap((event) async {
if (kDebugMode) {
_printDebug(event);
} else {
await _printRelease(event, dir.path);
}
}).listen((_) {}, cancelOnError: false);
}

View File

@@ -4,6 +4,7 @@ import 'package:stack_trace/stack_trace.dart' as stack_trace;
import 'package:worker_manager/worker_manager.dart';
import 'app/app.dart';
import 'log.dart';
void main() async {
// TOOD: probably remove before live
@@ -18,5 +19,8 @@ void main() async {
await Executor().warmUp();
WidgetsFlutterBinding.ensureInitialized();
await initLogging();
runApp(const ProviderScope(child: MyApp()));
}

View File

@@ -12,7 +12,7 @@ part of 'music.dart';
T _$identity<T>(T value) => value;
final _privateConstructorUsedError = UnsupportedError(
'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\nPlease check the documentation here for more information: https://github.com/rrousselGit/freezed#custom-getters-and-methods');
'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\nPlease check the documentation here for more information: https://github.com/rrousselGit/freezed#adding-getters-and-methods-to-our-models');
/// @nodoc
mixin _$SourceId {
@@ -62,21 +62,22 @@ class _$SourceIdCopyWithImpl<$Res, $Val extends SourceId>
}
/// @nodoc
abstract class _$$_SourceIdCopyWith<$Res> implements $SourceIdCopyWith<$Res> {
factory _$$_SourceIdCopyWith(
_$_SourceId value, $Res Function(_$_SourceId) then) =
__$$_SourceIdCopyWithImpl<$Res>;
abstract class _$$SourceIdImplCopyWith<$Res>
implements $SourceIdCopyWith<$Res> {
factory _$$SourceIdImplCopyWith(
_$SourceIdImpl value, $Res Function(_$SourceIdImpl) then) =
__$$SourceIdImplCopyWithImpl<$Res>;
@override
@useResult
$Res call({int sourceId, String id});
}
/// @nodoc
class __$$_SourceIdCopyWithImpl<$Res>
extends _$SourceIdCopyWithImpl<$Res, _$_SourceId>
implements _$$_SourceIdCopyWith<$Res> {
__$$_SourceIdCopyWithImpl(
_$_SourceId _value, $Res Function(_$_SourceId) _then)
class __$$SourceIdImplCopyWithImpl<$Res>
extends _$SourceIdCopyWithImpl<$Res, _$SourceIdImpl>
implements _$$SourceIdImplCopyWith<$Res> {
__$$SourceIdImplCopyWithImpl(
_$SourceIdImpl _value, $Res Function(_$SourceIdImpl) _then)
: super(_value, _then);
@pragma('vm:prefer-inline')
@@ -85,7 +86,7 @@ class __$$_SourceIdCopyWithImpl<$Res>
Object? sourceId = null,
Object? id = null,
}) {
return _then(_$_SourceId(
return _then(_$SourceIdImpl(
sourceId: null == sourceId
? _value.sourceId
: sourceId // ignore: cast_nullable_to_non_nullable
@@ -100,8 +101,8 @@ class __$$_SourceIdCopyWithImpl<$Res>
/// @nodoc
class _$_SourceId with DiagnosticableTreeMixin implements _SourceId {
const _$_SourceId({required this.sourceId, required this.id});
class _$SourceIdImpl with DiagnosticableTreeMixin implements _SourceId {
const _$SourceIdImpl({required this.sourceId, required this.id});
@override
final int sourceId;
@@ -123,10 +124,10 @@ class _$_SourceId with DiagnosticableTreeMixin implements _SourceId {
}
@override
bool operator ==(dynamic other) {
bool operator ==(Object other) {
return identical(this, other) ||
(other.runtimeType == runtimeType &&
other is _$_SourceId &&
other is _$SourceIdImpl &&
(identical(other.sourceId, sourceId) ||
other.sourceId == sourceId) &&
(identical(other.id, id) || other.id == id));
@@ -138,13 +139,13 @@ class _$_SourceId with DiagnosticableTreeMixin implements _SourceId {
@JsonKey(ignore: true)
@override
@pragma('vm:prefer-inline')
_$$_SourceIdCopyWith<_$_SourceId> get copyWith =>
__$$_SourceIdCopyWithImpl<_$_SourceId>(this, _$identity);
_$$SourceIdImplCopyWith<_$SourceIdImpl> get copyWith =>
__$$SourceIdImplCopyWithImpl<_$SourceIdImpl>(this, _$identity);
}
abstract class _SourceId implements SourceId {
const factory _SourceId(
{required final int sourceId, required final String id}) = _$_SourceId;
{required final int sourceId, required final String id}) = _$SourceIdImpl;
@override
int get sourceId;
@@ -152,7 +153,7 @@ abstract class _SourceId implements SourceId {
String get id;
@override
@JsonKey(ignore: true)
_$$_SourceIdCopyWith<_$_SourceId> get copyWith =>
_$$SourceIdImplCopyWith<_$SourceIdImpl> get copyWith =>
throw _privateConstructorUsedError;
}
@@ -205,22 +206,22 @@ class _$SourceIdSetCopyWithImpl<$Res, $Val extends SourceIdSet>
}
/// @nodoc
abstract class _$$_SourceIdSetCopyWith<$Res>
abstract class _$$SourceIdSetImplCopyWith<$Res>
implements $SourceIdSetCopyWith<$Res> {
factory _$$_SourceIdSetCopyWith(
_$_SourceIdSet value, $Res Function(_$_SourceIdSet) then) =
__$$_SourceIdSetCopyWithImpl<$Res>;
factory _$$SourceIdSetImplCopyWith(
_$SourceIdSetImpl value, $Res Function(_$SourceIdSetImpl) then) =
__$$SourceIdSetImplCopyWithImpl<$Res>;
@override
@useResult
$Res call({int sourceId, ISet<String> ids});
}
/// @nodoc
class __$$_SourceIdSetCopyWithImpl<$Res>
extends _$SourceIdSetCopyWithImpl<$Res, _$_SourceIdSet>
implements _$$_SourceIdSetCopyWith<$Res> {
__$$_SourceIdSetCopyWithImpl(
_$_SourceIdSet _value, $Res Function(_$_SourceIdSet) _then)
class __$$SourceIdSetImplCopyWithImpl<$Res>
extends _$SourceIdSetCopyWithImpl<$Res, _$SourceIdSetImpl>
implements _$$SourceIdSetImplCopyWith<$Res> {
__$$SourceIdSetImplCopyWithImpl(
_$SourceIdSetImpl _value, $Res Function(_$SourceIdSetImpl) _then)
: super(_value, _then);
@pragma('vm:prefer-inline')
@@ -229,7 +230,7 @@ class __$$_SourceIdSetCopyWithImpl<$Res>
Object? sourceId = null,
Object? ids = null,
}) {
return _then(_$_SourceIdSet(
return _then(_$SourceIdSetImpl(
sourceId: null == sourceId
? _value.sourceId
: sourceId // ignore: cast_nullable_to_non_nullable
@@ -244,8 +245,8 @@ class __$$_SourceIdSetCopyWithImpl<$Res>
/// @nodoc
class _$_SourceIdSet with DiagnosticableTreeMixin implements _SourceIdSet {
const _$_SourceIdSet({required this.sourceId, required this.ids});
class _$SourceIdSetImpl with DiagnosticableTreeMixin implements _SourceIdSet {
const _$SourceIdSetImpl({required this.sourceId, required this.ids});
@override
final int sourceId;
@@ -267,10 +268,10 @@ class _$_SourceIdSet with DiagnosticableTreeMixin implements _SourceIdSet {
}
@override
bool operator ==(dynamic other) {
bool operator ==(Object other) {
return identical(this, other) ||
(other.runtimeType == runtimeType &&
other is _$_SourceIdSet &&
other is _$SourceIdSetImpl &&
(identical(other.sourceId, sourceId) ||
other.sourceId == sourceId) &&
const DeepCollectionEquality().equals(other.ids, ids));
@@ -283,14 +284,14 @@ class _$_SourceIdSet with DiagnosticableTreeMixin implements _SourceIdSet {
@JsonKey(ignore: true)
@override
@pragma('vm:prefer-inline')
_$$_SourceIdSetCopyWith<_$_SourceIdSet> get copyWith =>
__$$_SourceIdSetCopyWithImpl<_$_SourceIdSet>(this, _$identity);
_$$SourceIdSetImplCopyWith<_$SourceIdSetImpl> get copyWith =>
__$$SourceIdSetImplCopyWithImpl<_$SourceIdSetImpl>(this, _$identity);
}
abstract class _SourceIdSet implements SourceIdSet {
const factory _SourceIdSet(
{required final int sourceId,
required final ISet<String> ids}) = _$_SourceIdSet;
required final ISet<String> ids}) = _$SourceIdSetImpl;
@override
int get sourceId;
@@ -298,7 +299,7 @@ abstract class _SourceIdSet implements SourceIdSet {
ISet<String> get ids;
@override
@JsonKey(ignore: true)
_$$_SourceIdSetCopyWith<_$_SourceIdSet> get copyWith =>
_$$SourceIdSetImplCopyWith<_$SourceIdSetImpl> get copyWith =>
throw _privateConstructorUsedError;
}
@@ -372,9 +373,10 @@ class _$ArtistCopyWithImpl<$Res, $Val extends Artist>
}
/// @nodoc
abstract class _$$_ArtistCopyWith<$Res> implements $ArtistCopyWith<$Res> {
factory _$$_ArtistCopyWith(_$_Artist value, $Res Function(_$_Artist) then) =
__$$_ArtistCopyWithImpl<$Res>;
abstract class _$$ArtistImplCopyWith<$Res> implements $ArtistCopyWith<$Res> {
factory _$$ArtistImplCopyWith(
_$ArtistImpl value, $Res Function(_$ArtistImpl) then) =
__$$ArtistImplCopyWithImpl<$Res>;
@override
@useResult
$Res call(
@@ -386,10 +388,11 @@ abstract class _$$_ArtistCopyWith<$Res> implements $ArtistCopyWith<$Res> {
}
/// @nodoc
class __$$_ArtistCopyWithImpl<$Res>
extends _$ArtistCopyWithImpl<$Res, _$_Artist>
implements _$$_ArtistCopyWith<$Res> {
__$$_ArtistCopyWithImpl(_$_Artist _value, $Res Function(_$_Artist) _then)
class __$$ArtistImplCopyWithImpl<$Res>
extends _$ArtistCopyWithImpl<$Res, _$ArtistImpl>
implements _$$ArtistImplCopyWith<$Res> {
__$$ArtistImplCopyWithImpl(
_$ArtistImpl _value, $Res Function(_$ArtistImpl) _then)
: super(_value, _then);
@pragma('vm:prefer-inline')
@@ -401,7 +404,7 @@ class __$$_ArtistCopyWithImpl<$Res>
Object? albumCount = null,
Object? starred = freezed,
}) {
return _then(_$_Artist(
return _then(_$ArtistImpl(
sourceId: null == sourceId
? _value.sourceId
: sourceId // ignore: cast_nullable_to_non_nullable
@@ -428,8 +431,8 @@ class __$$_ArtistCopyWithImpl<$Res>
/// @nodoc
class _$_Artist with DiagnosticableTreeMixin implements _Artist {
const _$_Artist(
class _$ArtistImpl with DiagnosticableTreeMixin implements _Artist {
const _$ArtistImpl(
{required this.sourceId,
required this.id,
required this.name,
@@ -465,10 +468,10 @@ class _$_Artist with DiagnosticableTreeMixin implements _Artist {
}
@override
bool operator ==(dynamic other) {
bool operator ==(Object other) {
return identical(this, other) ||
(other.runtimeType == runtimeType &&
other is _$_Artist &&
other is _$ArtistImpl &&
(identical(other.sourceId, sourceId) ||
other.sourceId == sourceId) &&
(identical(other.id, id) || other.id == id) &&
@@ -485,8 +488,8 @@ class _$_Artist with DiagnosticableTreeMixin implements _Artist {
@JsonKey(ignore: true)
@override
@pragma('vm:prefer-inline')
_$$_ArtistCopyWith<_$_Artist> get copyWith =>
__$$_ArtistCopyWithImpl<_$_Artist>(this, _$identity);
_$$ArtistImplCopyWith<_$ArtistImpl> get copyWith =>
__$$ArtistImplCopyWithImpl<_$ArtistImpl>(this, _$identity);
}
abstract class _Artist implements Artist {
@@ -495,7 +498,7 @@ abstract class _Artist implements Artist {
required final String id,
required final String name,
required final int albumCount,
final DateTime? starred}) = _$_Artist;
final DateTime? starred}) = _$ArtistImpl;
@override
int get sourceId;
@@ -509,7 +512,7 @@ abstract class _Artist implements Artist {
DateTime? get starred;
@override
@JsonKey(ignore: true)
_$$_ArtistCopyWith<_$_Artist> get copyWith =>
_$$ArtistImplCopyWith<_$ArtistImpl> get copyWith =>
throw _privateConstructorUsedError;
}
@@ -648,9 +651,10 @@ class _$AlbumCopyWithImpl<$Res, $Val extends Album>
}
/// @nodoc
abstract class _$$_AlbumCopyWith<$Res> implements $AlbumCopyWith<$Res> {
factory _$$_AlbumCopyWith(_$_Album value, $Res Function(_$_Album) then) =
__$$_AlbumCopyWithImpl<$Res>;
abstract class _$$AlbumImplCopyWith<$Res> implements $AlbumCopyWith<$Res> {
factory _$$AlbumImplCopyWith(
_$AlbumImpl value, $Res Function(_$AlbumImpl) then) =
__$$AlbumImplCopyWithImpl<$Res>;
@override
@useResult
$Res call(
@@ -671,9 +675,11 @@ abstract class _$$_AlbumCopyWith<$Res> implements $AlbumCopyWith<$Res> {
}
/// @nodoc
class __$$_AlbumCopyWithImpl<$Res> extends _$AlbumCopyWithImpl<$Res, _$_Album>
implements _$$_AlbumCopyWith<$Res> {
__$$_AlbumCopyWithImpl(_$_Album _value, $Res Function(_$_Album) _then)
class __$$AlbumImplCopyWithImpl<$Res>
extends _$AlbumCopyWithImpl<$Res, _$AlbumImpl>
implements _$$AlbumImplCopyWith<$Res> {
__$$AlbumImplCopyWithImpl(
_$AlbumImpl _value, $Res Function(_$AlbumImpl) _then)
: super(_value, _then);
@pragma('vm:prefer-inline')
@@ -694,7 +700,7 @@ class __$$_AlbumCopyWithImpl<$Res> extends _$AlbumCopyWithImpl<$Res, _$_Album>
Object? frequentRank = freezed,
Object? recentRank = freezed,
}) {
return _then(_$_Album(
return _then(_$AlbumImpl(
sourceId: null == sourceId
? _value.sourceId
: sourceId // ignore: cast_nullable_to_non_nullable
@@ -757,8 +763,8 @@ class __$$_AlbumCopyWithImpl<$Res> extends _$AlbumCopyWithImpl<$Res, _$_Album>
/// @nodoc
class _$_Album with DiagnosticableTreeMixin implements _Album {
const _$_Album(
class _$AlbumImpl with DiagnosticableTreeMixin implements _Album {
const _$AlbumImpl(
{required this.sourceId,
required this.id,
required this.name,
@@ -833,10 +839,10 @@ class _$_Album with DiagnosticableTreeMixin implements _Album {
}
@override
bool operator ==(dynamic other) {
bool operator ==(Object other) {
return identical(this, other) ||
(other.runtimeType == runtimeType &&
other is _$_Album &&
other is _$AlbumImpl &&
(identical(other.sourceId, sourceId) ||
other.sourceId == sourceId) &&
(identical(other.id, id) || other.id == id) &&
@@ -882,8 +888,8 @@ class _$_Album with DiagnosticableTreeMixin implements _Album {
@JsonKey(ignore: true)
@override
@pragma('vm:prefer-inline')
_$$_AlbumCopyWith<_$_Album> get copyWith =>
__$$_AlbumCopyWithImpl<_$_Album>(this, _$identity);
_$$AlbumImplCopyWith<_$AlbumImpl> get copyWith =>
__$$AlbumImplCopyWithImpl<_$AlbumImpl>(this, _$identity);
}
abstract class _Album implements Album {
@@ -901,7 +907,7 @@ abstract class _Album implements Album {
required final int songCount,
final bool isDeleted,
final int? frequentRank,
final int? recentRank}) = _$_Album;
final int? recentRank}) = _$AlbumImpl;
@override
int get sourceId;
@@ -933,7 +939,7 @@ abstract class _Album implements Album {
int? get recentRank;
@override
@JsonKey(ignore: true)
_$$_AlbumCopyWith<_$_Album> get copyWith =>
_$$AlbumImplCopyWith<_$AlbumImpl> get copyWith =>
throw _privateConstructorUsedError;
}
@@ -1022,10 +1028,11 @@ class _$PlaylistCopyWithImpl<$Res, $Val extends Playlist>
}
/// @nodoc
abstract class _$$_PlaylistCopyWith<$Res> implements $PlaylistCopyWith<$Res> {
factory _$$_PlaylistCopyWith(
_$_Playlist value, $Res Function(_$_Playlist) then) =
__$$_PlaylistCopyWithImpl<$Res>;
abstract class _$$PlaylistImplCopyWith<$Res>
implements $PlaylistCopyWith<$Res> {
factory _$$PlaylistImplCopyWith(
_$PlaylistImpl value, $Res Function(_$PlaylistImpl) then) =
__$$PlaylistImplCopyWithImpl<$Res>;
@override
@useResult
$Res call(
@@ -1039,11 +1046,11 @@ abstract class _$$_PlaylistCopyWith<$Res> implements $PlaylistCopyWith<$Res> {
}
/// @nodoc
class __$$_PlaylistCopyWithImpl<$Res>
extends _$PlaylistCopyWithImpl<$Res, _$_Playlist>
implements _$$_PlaylistCopyWith<$Res> {
__$$_PlaylistCopyWithImpl(
_$_Playlist _value, $Res Function(_$_Playlist) _then)
class __$$PlaylistImplCopyWithImpl<$Res>
extends _$PlaylistCopyWithImpl<$Res, _$PlaylistImpl>
implements _$$PlaylistImplCopyWith<$Res> {
__$$PlaylistImplCopyWithImpl(
_$PlaylistImpl _value, $Res Function(_$PlaylistImpl) _then)
: super(_value, _then);
@pragma('vm:prefer-inline')
@@ -1057,7 +1064,7 @@ class __$$_PlaylistCopyWithImpl<$Res>
Object? songCount = null,
Object? created = null,
}) {
return _then(_$_Playlist(
return _then(_$PlaylistImpl(
sourceId: null == sourceId
? _value.sourceId
: sourceId // ignore: cast_nullable_to_non_nullable
@@ -1092,8 +1099,8 @@ class __$$_PlaylistCopyWithImpl<$Res>
/// @nodoc
class _$_Playlist with DiagnosticableTreeMixin implements _Playlist {
const _$_Playlist(
class _$PlaylistImpl with DiagnosticableTreeMixin implements _Playlist {
const _$PlaylistImpl(
{required this.sourceId,
required this.id,
required this.name,
@@ -1137,10 +1144,10 @@ class _$_Playlist with DiagnosticableTreeMixin implements _Playlist {
}
@override
bool operator ==(dynamic other) {
bool operator ==(Object other) {
return identical(this, other) ||
(other.runtimeType == runtimeType &&
other is _$_Playlist &&
other is _$PlaylistImpl &&
(identical(other.sourceId, sourceId) ||
other.sourceId == sourceId) &&
(identical(other.id, id) || other.id == id) &&
@@ -1160,8 +1167,8 @@ class _$_Playlist with DiagnosticableTreeMixin implements _Playlist {
@JsonKey(ignore: true)
@override
@pragma('vm:prefer-inline')
_$$_PlaylistCopyWith<_$_Playlist> get copyWith =>
__$$_PlaylistCopyWithImpl<_$_Playlist>(this, _$identity);
_$$PlaylistImplCopyWith<_$PlaylistImpl> get copyWith =>
__$$PlaylistImplCopyWithImpl<_$PlaylistImpl>(this, _$identity);
}
abstract class _Playlist implements Playlist {
@@ -1172,7 +1179,7 @@ abstract class _Playlist implements Playlist {
final String? comment,
final String? coverArt,
required final int songCount,
required final DateTime created}) = _$_Playlist;
required final DateTime created}) = _$PlaylistImpl;
@override
int get sourceId;
@@ -1190,7 +1197,7 @@ abstract class _Playlist implements Playlist {
DateTime get created;
@override
@JsonKey(ignore: true)
_$$_PlaylistCopyWith<_$_Playlist> get copyWith =>
_$$PlaylistImplCopyWith<_$PlaylistImpl> get copyWith =>
throw _privateConstructorUsedError;
}
@@ -1334,9 +1341,10 @@ class _$SongCopyWithImpl<$Res, $Val extends Song>
}
/// @nodoc
abstract class _$$_SongCopyWith<$Res> implements $SongCopyWith<$Res> {
factory _$$_SongCopyWith(_$_Song value, $Res Function(_$_Song) then) =
__$$_SongCopyWithImpl<$Res>;
abstract class _$$SongImplCopyWith<$Res> implements $SongCopyWith<$Res> {
factory _$$SongImplCopyWith(
_$SongImpl value, $Res Function(_$SongImpl) then) =
__$$SongImplCopyWithImpl<$Res>;
@override
@useResult
$Res call(
@@ -1358,9 +1366,10 @@ abstract class _$$_SongCopyWith<$Res> implements $SongCopyWith<$Res> {
}
/// @nodoc
class __$$_SongCopyWithImpl<$Res> extends _$SongCopyWithImpl<$Res, _$_Song>
implements _$$_SongCopyWith<$Res> {
__$$_SongCopyWithImpl(_$_Song _value, $Res Function(_$_Song) _then)
class __$$SongImplCopyWithImpl<$Res>
extends _$SongCopyWithImpl<$Res, _$SongImpl>
implements _$$SongImplCopyWith<$Res> {
__$$SongImplCopyWithImpl(_$SongImpl _value, $Res Function(_$SongImpl) _then)
: super(_value, _then);
@pragma('vm:prefer-inline')
@@ -1382,7 +1391,7 @@ class __$$_SongCopyWithImpl<$Res> extends _$SongCopyWithImpl<$Res, _$_Song>
Object? downloadFilePath = freezed,
Object? isDeleted = null,
}) {
return _then(_$_Song(
return _then(_$SongImpl(
sourceId: null == sourceId
? _value.sourceId
: sourceId // ignore: cast_nullable_to_non_nullable
@@ -1449,8 +1458,8 @@ class __$$_SongCopyWithImpl<$Res> extends _$SongCopyWithImpl<$Res, _$_Song>
/// @nodoc
class _$_Song with DiagnosticableTreeMixin implements _Song {
const _$_Song(
class _$SongImpl with DiagnosticableTreeMixin implements _Song {
const _$SongImpl(
{required this.sourceId,
required this.id,
this.albumId,
@@ -1527,10 +1536,10 @@ class _$_Song with DiagnosticableTreeMixin implements _Song {
}
@override
bool operator ==(dynamic other) {
bool operator ==(Object other) {
return identical(this, other) ||
(other.runtimeType == runtimeType &&
other is _$_Song &&
other is _$SongImpl &&
(identical(other.sourceId, sourceId) ||
other.sourceId == sourceId) &&
(identical(other.id, id) || other.id == id) &&
@@ -1576,8 +1585,8 @@ class _$_Song with DiagnosticableTreeMixin implements _Song {
@JsonKey(ignore: true)
@override
@pragma('vm:prefer-inline')
_$$_SongCopyWith<_$_Song> get copyWith =>
__$$_SongCopyWithImpl<_$_Song>(this, _$identity);
_$$SongImplCopyWith<_$SongImpl> get copyWith =>
__$$SongImplCopyWithImpl<_$SongImpl>(this, _$identity);
}
abstract class _Song implements Song {
@@ -1596,7 +1605,7 @@ abstract class _Song implements Song {
final String? genre,
final String? downloadTaskId,
final String? downloadFilePath,
final bool isDeleted}) = _$_Song;
final bool isDeleted}) = _$SongImpl;
@override
int get sourceId;
@@ -1630,7 +1639,8 @@ abstract class _Song implements Song {
bool get isDeleted;
@override
@JsonKey(ignore: true)
_$$_SongCopyWith<_$_Song> get copyWith => throw _privateConstructorUsedError;
_$$SongImplCopyWith<_$SongImpl> get copyWith =>
throw _privateConstructorUsedError;
}
/// @nodoc
@@ -1698,11 +1708,11 @@ class _$SearchResultsCopyWithImpl<$Res, $Val extends SearchResults>
}
/// @nodoc
abstract class _$$_SearchResultsCopyWith<$Res>
abstract class _$$SearchResultsImplCopyWith<$Res>
implements $SearchResultsCopyWith<$Res> {
factory _$$_SearchResultsCopyWith(
_$_SearchResults value, $Res Function(_$_SearchResults) then) =
__$$_SearchResultsCopyWithImpl<$Res>;
factory _$$SearchResultsImplCopyWith(
_$SearchResultsImpl value, $Res Function(_$SearchResultsImpl) then) =
__$$SearchResultsImplCopyWithImpl<$Res>;
@override
@useResult
$Res call(
@@ -1713,11 +1723,11 @@ abstract class _$$_SearchResultsCopyWith<$Res>
}
/// @nodoc
class __$$_SearchResultsCopyWithImpl<$Res>
extends _$SearchResultsCopyWithImpl<$Res, _$_SearchResults>
implements _$$_SearchResultsCopyWith<$Res> {
__$$_SearchResultsCopyWithImpl(
_$_SearchResults _value, $Res Function(_$_SearchResults) _then)
class __$$SearchResultsImplCopyWithImpl<$Res>
extends _$SearchResultsCopyWithImpl<$Res, _$SearchResultsImpl>
implements _$$SearchResultsImplCopyWith<$Res> {
__$$SearchResultsImplCopyWithImpl(
_$SearchResultsImpl _value, $Res Function(_$SearchResultsImpl) _then)
: super(_value, _then);
@pragma('vm:prefer-inline')
@@ -1728,7 +1738,7 @@ class __$$_SearchResultsCopyWithImpl<$Res>
Object? albums = null,
Object? artists = null,
}) {
return _then(_$_SearchResults(
return _then(_$SearchResultsImpl(
query: freezed == query
? _value.query
: query // ignore: cast_nullable_to_non_nullable
@@ -1751,8 +1761,10 @@ class __$$_SearchResultsCopyWithImpl<$Res>
/// @nodoc
class _$_SearchResults with DiagnosticableTreeMixin implements _SearchResults {
const _$_SearchResults(
class _$SearchResultsImpl
with DiagnosticableTreeMixin
implements _SearchResults {
const _$SearchResultsImpl(
{this.query,
this.songs = const IListConst([]),
this.albums = const IListConst([]),
@@ -1787,10 +1799,10 @@ class _$_SearchResults with DiagnosticableTreeMixin implements _SearchResults {
}
@override
bool operator ==(dynamic other) {
bool operator ==(Object other) {
return identical(this, other) ||
(other.runtimeType == runtimeType &&
other is _$_SearchResults &&
other is _$SearchResultsImpl &&
(identical(other.query, query) || other.query == query) &&
const DeepCollectionEquality().equals(other.songs, songs) &&
const DeepCollectionEquality().equals(other.albums, albums) &&
@@ -1808,8 +1820,8 @@ class _$_SearchResults with DiagnosticableTreeMixin implements _SearchResults {
@JsonKey(ignore: true)
@override
@pragma('vm:prefer-inline')
_$$_SearchResultsCopyWith<_$_SearchResults> get copyWith =>
__$$_SearchResultsCopyWithImpl<_$_SearchResults>(this, _$identity);
_$$SearchResultsImplCopyWith<_$SearchResultsImpl> get copyWith =>
__$$SearchResultsImplCopyWithImpl<_$SearchResultsImpl>(this, _$identity);
}
abstract class _SearchResults implements SearchResults {
@@ -1817,7 +1829,7 @@ abstract class _SearchResults implements SearchResults {
{final String? query,
final IList<Song> songs,
final IList<Album> albums,
final IList<Artist> artists}) = _$_SearchResults;
final IList<Artist> artists}) = _$SearchResultsImpl;
@override
String? get query;
@@ -1829,6 +1841,6 @@ abstract class _SearchResults implements SearchResults {
IList<Artist> get artists;
@override
@JsonKey(ignore: true)
_$$_SearchResultsCopyWith<_$_SearchResults> get copyWith =>
_$$SearchResultsImplCopyWith<_$SearchResultsImpl> get copyWith =>
throw _privateConstructorUsedError;
}

File diff suppressed because it is too large Load Diff

View File

@@ -6,25 +6,26 @@ part of 'query.dart';
// JsonSerializableGenerator
// **************************************************************************
_$_Pagination _$$_PaginationFromJson(Map<String, dynamic> json) =>
_$_Pagination(
limit: json['limit'] as int,
offset: json['offset'] as int? ?? 0,
_$PaginationImpl _$$PaginationImplFromJson(Map<String, dynamic> json) =>
_$PaginationImpl(
limit: (json['limit'] as num).toInt(),
offset: (json['offset'] as num?)?.toInt() ?? 0,
);
Map<String, dynamic> _$$_PaginationToJson(_$_Pagination instance) =>
Map<String, dynamic> _$$PaginationImplToJson(_$PaginationImpl instance) =>
<String, dynamic>{
'limit': instance.limit,
'offset': instance.offset,
};
_$_SortBy _$$_SortByFromJson(Map<String, dynamic> json) => _$_SortBy(
_$SortByImpl _$$SortByImplFromJson(Map<String, dynamic> json) => _$SortByImpl(
column: json['column'] as String,
dir: $enumDecodeNullable(_$SortDirectionEnumMap, json['dir']) ??
SortDirection.asc,
);
Map<String, dynamic> _$$_SortByToJson(_$_SortBy instance) => <String, dynamic>{
Map<String, dynamic> _$$SortByImplToJson(_$SortByImpl instance) =>
<String, dynamic>{
'column': instance.column,
'dir': _$SortDirectionEnumMap[instance.dir]!,
};
@@ -34,15 +35,17 @@ const _$SortDirectionEnumMap = {
SortDirection.desc: 'desc',
};
_$_FilterWithEquals _$$_FilterWithEqualsFromJson(Map<String, dynamic> json) =>
_$_FilterWithEquals(
_$FilterWithEqualsImpl _$$FilterWithEqualsImplFromJson(
Map<String, dynamic> json) =>
_$FilterWithEqualsImpl(
column: json['column'] as String,
value: json['value'] as String,
invert: json['invert'] as bool? ?? false,
$type: json['runtimeType'] as String?,
);
Map<String, dynamic> _$$_FilterWithEqualsToJson(_$_FilterWithEquals instance) =>
Map<String, dynamic> _$$FilterWithEqualsImplToJson(
_$FilterWithEqualsImpl instance) =>
<String, dynamic>{
'column': instance.column,
'value': instance.value,
@@ -50,17 +53,17 @@ Map<String, dynamic> _$$_FilterWithEqualsToJson(_$_FilterWithEquals instance) =>
'runtimeType': instance.$type,
};
_$_FilterWithGreaterThan _$$_FilterWithGreaterThanFromJson(
_$FilterWithGreaterThanImpl _$$FilterWithGreaterThanImplFromJson(
Map<String, dynamic> json) =>
_$_FilterWithGreaterThan(
_$FilterWithGreaterThanImpl(
column: json['column'] as String,
value: json['value'] as String,
orEquals: json['orEquals'] as bool? ?? false,
$type: json['runtimeType'] as String?,
);
Map<String, dynamic> _$$_FilterWithGreaterThanToJson(
_$_FilterWithGreaterThan instance) =>
Map<String, dynamic> _$$FilterWithGreaterThanImplToJson(
_$FilterWithGreaterThanImpl instance) =>
<String, dynamic>{
'column': instance.column,
'value': instance.value,
@@ -68,31 +71,33 @@ Map<String, dynamic> _$$_FilterWithGreaterThanToJson(
'runtimeType': instance.$type,
};
_$_FilterWithIsNull _$$_FilterWithIsNullFromJson(Map<String, dynamic> json) =>
_$_FilterWithIsNull(
_$FilterWithIsNullImpl _$$FilterWithIsNullImplFromJson(
Map<String, dynamic> json) =>
_$FilterWithIsNullImpl(
column: json['column'] as String,
invert: json['invert'] as bool? ?? false,
$type: json['runtimeType'] as String?,
);
Map<String, dynamic> _$$_FilterWithIsNullToJson(_$_FilterWithIsNull instance) =>
Map<String, dynamic> _$$FilterWithIsNullImplToJson(
_$FilterWithIsNullImpl instance) =>
<String, dynamic>{
'column': instance.column,
'invert': instance.invert,
'runtimeType': instance.$type,
};
_$_FilterWithBetweenInt _$$_FilterWithBetweenIntFromJson(
_$FilterWithBetweenIntImpl _$$FilterWithBetweenIntImplFromJson(
Map<String, dynamic> json) =>
_$_FilterWithBetweenInt(
_$FilterWithBetweenIntImpl(
column: json['column'] as String,
from: json['from'] as int,
to: json['to'] as int,
from: (json['from'] as num).toInt(),
to: (json['to'] as num).toInt(),
$type: json['runtimeType'] as String?,
);
Map<String, dynamic> _$$_FilterWithBetweenIntToJson(
_$_FilterWithBetweenInt instance) =>
Map<String, dynamic> _$$FilterWithBetweenIntImplToJson(
_$FilterWithBetweenIntImpl instance) =>
<String, dynamic>{
'column': instance.column,
'from': instance.from,
@@ -100,8 +105,8 @@ Map<String, dynamic> _$$_FilterWithBetweenIntToJson(
'runtimeType': instance.$type,
};
_$_FilterWithIsIn _$$_FilterWithIsInFromJson(Map<String, dynamic> json) =>
_$_FilterWithIsIn(
_$FilterWithIsInImpl _$$FilterWithIsInImplFromJson(Map<String, dynamic> json) =>
_$FilterWithIsInImpl(
column: json['column'] as String,
invert: json['invert'] as bool? ?? false,
values: json['values'] == null
@@ -110,7 +115,8 @@ _$_FilterWithIsIn _$$_FilterWithIsInFromJson(Map<String, dynamic> json) =>
$type: json['runtimeType'] as String?,
);
Map<String, dynamic> _$$_FilterWithIsInToJson(_$_FilterWithIsIn instance) =>
Map<String, dynamic> _$$FilterWithIsInImplToJson(
_$FilterWithIsInImpl instance) =>
<String, dynamic>{
'column': instance.column,
'invert': instance.invert,
@@ -120,7 +126,8 @@ Map<String, dynamic> _$$_FilterWithIsInToJson(_$_FilterWithIsIn instance) =>
'runtimeType': instance.$type,
};
_$_ListQuery _$$_ListQueryFromJson(Map<String, dynamic> json) => _$_ListQuery(
_$ListQueryImpl _$$ListQueryImplFromJson(Map<String, dynamic> json) =>
_$ListQueryImpl(
page: json['page'] == null
? const Pagination(limit: -1, offset: 0)
: Pagination.fromJson(json['page'] as Map<String, dynamic>),
@@ -133,7 +140,7 @@ _$_ListQuery _$$_ListQueryFromJson(Map<String, dynamic> json) => _$_ListQuery(
(value) => FilterWith.fromJson(value as Map<String, dynamic>)),
);
Map<String, dynamic> _$$_ListQueryToJson(_$_ListQuery instance) =>
Map<String, dynamic> _$$ListQueryImplToJson(_$ListQueryImpl instance) =>
<String, dynamic>{
'page': instance.page,
'sort': instance.sort,

View File

@@ -12,7 +12,7 @@ part of 'settings.dart';
T _$identity<T>(T value) => value;
final _privateConstructorUsedError = UnsupportedError(
'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\nPlease check the documentation here for more information: https://github.com/rrousselGit/freezed#custom-getters-and-methods');
'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\nPlease check the documentation here for more information: https://github.com/rrousselGit/freezed#adding-getters-and-methods-to-our-models');
/// @nodoc
mixin _$Settings {
@@ -81,10 +81,11 @@ class _$SettingsCopyWithImpl<$Res, $Val extends Settings>
}
/// @nodoc
abstract class _$$_SettingsCopyWith<$Res> implements $SettingsCopyWith<$Res> {
factory _$$_SettingsCopyWith(
_$_Settings value, $Res Function(_$_Settings) then) =
__$$_SettingsCopyWithImpl<$Res>;
abstract class _$$SettingsImplCopyWith<$Res>
implements $SettingsCopyWith<$Res> {
factory _$$SettingsImplCopyWith(
_$SettingsImpl value, $Res Function(_$SettingsImpl) then) =
__$$SettingsImplCopyWithImpl<$Res>;
@override
@useResult
$Res call(
@@ -97,11 +98,11 @@ abstract class _$$_SettingsCopyWith<$Res> implements $SettingsCopyWith<$Res> {
}
/// @nodoc
class __$$_SettingsCopyWithImpl<$Res>
extends _$SettingsCopyWithImpl<$Res, _$_Settings>
implements _$$_SettingsCopyWith<$Res> {
__$$_SettingsCopyWithImpl(
_$_Settings _value, $Res Function(_$_Settings) _then)
class __$$SettingsImplCopyWithImpl<$Res>
extends _$SettingsCopyWithImpl<$Res, _$SettingsImpl>
implements _$$SettingsImplCopyWith<$Res> {
__$$SettingsImplCopyWithImpl(
_$SettingsImpl _value, $Res Function(_$SettingsImpl) _then)
: super(_value, _then);
@pragma('vm:prefer-inline')
@@ -111,7 +112,7 @@ class __$$_SettingsCopyWithImpl<$Res>
Object? activeSource = freezed,
Object? app = null,
}) {
return _then(_$_Settings(
return _then(_$SettingsImpl(
sources: null == sources
? _value.sources
: sources // ignore: cast_nullable_to_non_nullable
@@ -130,8 +131,8 @@ class __$$_SettingsCopyWithImpl<$Res>
/// @nodoc
class _$_Settings implements _Settings {
const _$_Settings(
class _$SettingsImpl implements _Settings {
const _$SettingsImpl(
{this.sources = const IListConst([]),
this.activeSource,
this.app = const AppSettings()});
@@ -151,10 +152,10 @@ class _$_Settings implements _Settings {
}
@override
bool operator ==(dynamic other) {
bool operator ==(Object other) {
return identical(this, other) ||
(other.runtimeType == runtimeType &&
other is _$_Settings &&
other is _$SettingsImpl &&
const DeepCollectionEquality().equals(other.sources, sources) &&
(identical(other.activeSource, activeSource) ||
other.activeSource == activeSource) &&
@@ -168,15 +169,15 @@ class _$_Settings implements _Settings {
@JsonKey(ignore: true)
@override
@pragma('vm:prefer-inline')
_$$_SettingsCopyWith<_$_Settings> get copyWith =>
__$$_SettingsCopyWithImpl<_$_Settings>(this, _$identity);
_$$SettingsImplCopyWith<_$SettingsImpl> get copyWith =>
__$$SettingsImplCopyWithImpl<_$SettingsImpl>(this, _$identity);
}
abstract class _Settings implements Settings {
const factory _Settings(
{final IList<SourceSettings> sources,
final SourceSettings? activeSource,
final AppSettings app}) = _$_Settings;
final AppSettings app}) = _$SettingsImpl;
@override
IList<SourceSettings> get sources;
@@ -186,7 +187,7 @@ abstract class _Settings implements Settings {
AppSettings get app;
@override
@JsonKey(ignore: true)
_$$_SettingsCopyWith<_$_Settings> get copyWith =>
_$$SettingsImplCopyWith<_$SettingsImpl> get copyWith =>
throw _privateConstructorUsedError;
}
@@ -245,22 +246,22 @@ class _$AppSettingsCopyWithImpl<$Res, $Val extends AppSettings>
}
/// @nodoc
abstract class _$$_AppSettingsCopyWith<$Res>
abstract class _$$AppSettingsImplCopyWith<$Res>
implements $AppSettingsCopyWith<$Res> {
factory _$$_AppSettingsCopyWith(
_$_AppSettings value, $Res Function(_$_AppSettings) then) =
__$$_AppSettingsCopyWithImpl<$Res>;
factory _$$AppSettingsImplCopyWith(
_$AppSettingsImpl value, $Res Function(_$AppSettingsImpl) then) =
__$$AppSettingsImplCopyWithImpl<$Res>;
@override
@useResult
$Res call({int maxBitrateWifi, int maxBitrateMobile, String? streamFormat});
}
/// @nodoc
class __$$_AppSettingsCopyWithImpl<$Res>
extends _$AppSettingsCopyWithImpl<$Res, _$_AppSettings>
implements _$$_AppSettingsCopyWith<$Res> {
__$$_AppSettingsCopyWithImpl(
_$_AppSettings _value, $Res Function(_$_AppSettings) _then)
class __$$AppSettingsImplCopyWithImpl<$Res>
extends _$AppSettingsCopyWithImpl<$Res, _$AppSettingsImpl>
implements _$$AppSettingsImplCopyWith<$Res> {
__$$AppSettingsImplCopyWithImpl(
_$AppSettingsImpl _value, $Res Function(_$AppSettingsImpl) _then)
: super(_value, _then);
@pragma('vm:prefer-inline')
@@ -270,7 +271,7 @@ class __$$_AppSettingsCopyWithImpl<$Res>
Object? maxBitrateMobile = null,
Object? streamFormat = freezed,
}) {
return _then(_$_AppSettings(
return _then(_$AppSettingsImpl(
maxBitrateWifi: null == maxBitrateWifi
? _value.maxBitrateWifi
: maxBitrateWifi // ignore: cast_nullable_to_non_nullable
@@ -289,8 +290,8 @@ class __$$_AppSettingsCopyWithImpl<$Res>
/// @nodoc
class _$_AppSettings extends _AppSettings {
const _$_AppSettings(
class _$AppSettingsImpl extends _AppSettings {
const _$AppSettingsImpl(
{this.maxBitrateWifi = 0,
this.maxBitrateMobile = 192,
this.streamFormat = 'mp3'})
@@ -312,10 +313,10 @@ class _$_AppSettings extends _AppSettings {
}
@override
bool operator ==(dynamic other) {
bool operator ==(Object other) {
return identical(this, other) ||
(other.runtimeType == runtimeType &&
other is _$_AppSettings &&
other is _$AppSettingsImpl &&
(identical(other.maxBitrateWifi, maxBitrateWifi) ||
other.maxBitrateWifi == maxBitrateWifi) &&
(identical(other.maxBitrateMobile, maxBitrateMobile) ||
@@ -331,15 +332,15 @@ class _$_AppSettings extends _AppSettings {
@JsonKey(ignore: true)
@override
@pragma('vm:prefer-inline')
_$$_AppSettingsCopyWith<_$_AppSettings> get copyWith =>
__$$_AppSettingsCopyWithImpl<_$_AppSettings>(this, _$identity);
_$$AppSettingsImplCopyWith<_$AppSettingsImpl> get copyWith =>
__$$AppSettingsImplCopyWithImpl<_$AppSettingsImpl>(this, _$identity);
}
abstract class _AppSettings extends AppSettings {
const factory _AppSettings(
{final int maxBitrateWifi,
final int maxBitrateMobile,
final String? streamFormat}) = _$_AppSettings;
final String? streamFormat}) = _$AppSettingsImpl;
const _AppSettings._() : super._();
@override
@@ -350,7 +351,7 @@ abstract class _AppSettings extends AppSettings {
String? get streamFormat;
@override
@JsonKey(ignore: true)
_$$_AppSettingsCopyWith<_$_AppSettings> get copyWith =>
_$$AppSettingsImplCopyWith<_$AppSettingsImpl> get copyWith =>
throw _privateConstructorUsedError;
}
@@ -454,11 +455,11 @@ class _$SubsonicSettingsCopyWithImpl<$Res, $Val extends SubsonicSettings>
}
/// @nodoc
abstract class _$$_SubsonicSettingsCopyWith<$Res>
abstract class _$$SubsonicSettingsImplCopyWith<$Res>
implements $SubsonicSettingsCopyWith<$Res> {
factory _$$_SubsonicSettingsCopyWith(
_$_SubsonicSettings value, $Res Function(_$_SubsonicSettings) then) =
__$$_SubsonicSettingsCopyWithImpl<$Res>;
factory _$$SubsonicSettingsImplCopyWith(_$SubsonicSettingsImpl value,
$Res Function(_$SubsonicSettingsImpl) then) =
__$$SubsonicSettingsImplCopyWithImpl<$Res>;
@override
@useResult
$Res call(
@@ -474,11 +475,11 @@ abstract class _$$_SubsonicSettingsCopyWith<$Res>
}
/// @nodoc
class __$$_SubsonicSettingsCopyWithImpl<$Res>
extends _$SubsonicSettingsCopyWithImpl<$Res, _$_SubsonicSettings>
implements _$$_SubsonicSettingsCopyWith<$Res> {
__$$_SubsonicSettingsCopyWithImpl(
_$_SubsonicSettings _value, $Res Function(_$_SubsonicSettings) _then)
class __$$SubsonicSettingsImplCopyWithImpl<$Res>
extends _$SubsonicSettingsCopyWithImpl<$Res, _$SubsonicSettingsImpl>
implements _$$SubsonicSettingsImplCopyWith<$Res> {
__$$SubsonicSettingsImplCopyWithImpl(_$SubsonicSettingsImpl _value,
$Res Function(_$SubsonicSettingsImpl) _then)
: super(_value, _then);
@pragma('vm:prefer-inline')
@@ -494,7 +495,7 @@ class __$$_SubsonicSettingsCopyWithImpl<$Res>
Object? password = null,
Object? useTokenAuth = null,
}) {
return _then(_$_SubsonicSettings(
return _then(_$SubsonicSettingsImpl(
id: null == id
? _value.id
: id // ignore: cast_nullable_to_non_nullable
@@ -537,8 +538,8 @@ class __$$_SubsonicSettingsCopyWithImpl<$Res>
/// @nodoc
class _$_SubsonicSettings extends _SubsonicSettings {
const _$_SubsonicSettings(
class _$SubsonicSettingsImpl extends _SubsonicSettings {
const _$SubsonicSettingsImpl(
{required this.id,
this.features = const IListConst([]),
required this.name,
@@ -577,10 +578,10 @@ class _$_SubsonicSettings extends _SubsonicSettings {
}
@override
bool operator ==(dynamic other) {
bool operator ==(Object other) {
return identical(this, other) ||
(other.runtimeType == runtimeType &&
other is _$_SubsonicSettings &&
other is _$SubsonicSettingsImpl &&
(identical(other.id, id) || other.id == id) &&
const DeepCollectionEquality().equals(other.features, features) &&
(identical(other.name, name) || other.name == name) &&
@@ -613,8 +614,9 @@ class _$_SubsonicSettings extends _SubsonicSettings {
@JsonKey(ignore: true)
@override
@pragma('vm:prefer-inline')
_$$_SubsonicSettingsCopyWith<_$_SubsonicSettings> get copyWith =>
__$$_SubsonicSettingsCopyWithImpl<_$_SubsonicSettings>(this, _$identity);
_$$SubsonicSettingsImplCopyWith<_$SubsonicSettingsImpl> get copyWith =>
__$$SubsonicSettingsImplCopyWithImpl<_$SubsonicSettingsImpl>(
this, _$identity);
}
abstract class _SubsonicSettings extends SubsonicSettings {
@@ -627,7 +629,7 @@ abstract class _SubsonicSettings extends SubsonicSettings {
required final DateTime createdAt,
required final String username,
required final String password,
final bool useTokenAuth}) = _$_SubsonicSettings;
final bool useTokenAuth}) = _$SubsonicSettingsImpl;
const _SubsonicSettings._() : super._();
@override
@@ -650,7 +652,7 @@ abstract class _SubsonicSettings extends SubsonicSettings {
bool get useTokenAuth;
@override
@JsonKey(ignore: true)
_$$_SubsonicSettingsCopyWith<_$_SubsonicSettings> get copyWith =>
_$$SubsonicSettingsImplCopyWith<_$SubsonicSettingsImpl> get copyWith =>
throw _privateConstructorUsedError;
}
@@ -714,11 +716,12 @@ class _$SubsonicSourceSettingsCopyWithImpl<$Res,
}
/// @nodoc
abstract class _$$_SubsonicSourceSettingsCopyWith<$Res>
abstract class _$$SubsonicSourceSettingsImplCopyWith<$Res>
implements $SubsonicSourceSettingsCopyWith<$Res> {
factory _$$_SubsonicSourceSettingsCopyWith(_$_SubsonicSourceSettings value,
$Res Function(_$_SubsonicSourceSettings) then) =
__$$_SubsonicSourceSettingsCopyWithImpl<$Res>;
factory _$$SubsonicSourceSettingsImplCopyWith(
_$SubsonicSourceSettingsImpl value,
$Res Function(_$SubsonicSourceSettingsImpl) then) =
__$$SubsonicSourceSettingsImplCopyWithImpl<$Res>;
@override
@useResult
$Res call({SourceSettings source, SubsonicSettings subsonic});
@@ -728,12 +731,13 @@ abstract class _$$_SubsonicSourceSettingsCopyWith<$Res>
}
/// @nodoc
class __$$_SubsonicSourceSettingsCopyWithImpl<$Res>
class __$$SubsonicSourceSettingsImplCopyWithImpl<$Res>
extends _$SubsonicSourceSettingsCopyWithImpl<$Res,
_$_SubsonicSourceSettings>
implements _$$_SubsonicSourceSettingsCopyWith<$Res> {
__$$_SubsonicSourceSettingsCopyWithImpl(_$_SubsonicSourceSettings _value,
$Res Function(_$_SubsonicSourceSettings) _then)
_$SubsonicSourceSettingsImpl>
implements _$$SubsonicSourceSettingsImplCopyWith<$Res> {
__$$SubsonicSourceSettingsImplCopyWithImpl(
_$SubsonicSourceSettingsImpl _value,
$Res Function(_$SubsonicSourceSettingsImpl) _then)
: super(_value, _then);
@pragma('vm:prefer-inline')
@@ -742,7 +746,7 @@ class __$$_SubsonicSourceSettingsCopyWithImpl<$Res>
Object? source = null,
Object? subsonic = null,
}) {
return _then(_$_SubsonicSourceSettings(
return _then(_$SubsonicSourceSettingsImpl(
source: null == source
? _value.source
: source // ignore: cast_nullable_to_non_nullable
@@ -757,8 +761,8 @@ class __$$_SubsonicSourceSettingsCopyWithImpl<$Res>
/// @nodoc
class _$_SubsonicSourceSettings extends _SubsonicSourceSettings {
const _$_SubsonicSourceSettings(
class _$SubsonicSourceSettingsImpl extends _SubsonicSourceSettings {
const _$SubsonicSourceSettingsImpl(
{required this.source, required this.subsonic})
: super._();
@@ -773,10 +777,10 @@ class _$_SubsonicSourceSettings extends _SubsonicSourceSettings {
}
@override
bool operator ==(dynamic other) {
bool operator ==(Object other) {
return identical(this, other) ||
(other.runtimeType == runtimeType &&
other is _$_SubsonicSourceSettings &&
other is _$SubsonicSourceSettingsImpl &&
(identical(other.source, source) || other.source == source) &&
(identical(other.subsonic, subsonic) ||
other.subsonic == subsonic));
@@ -788,15 +792,15 @@ class _$_SubsonicSourceSettings extends _SubsonicSourceSettings {
@JsonKey(ignore: true)
@override
@pragma('vm:prefer-inline')
_$$_SubsonicSourceSettingsCopyWith<_$_SubsonicSourceSettings> get copyWith =>
__$$_SubsonicSourceSettingsCopyWithImpl<_$_SubsonicSourceSettings>(
this, _$identity);
_$$SubsonicSourceSettingsImplCopyWith<_$SubsonicSourceSettingsImpl>
get copyWith => __$$SubsonicSourceSettingsImplCopyWithImpl<
_$SubsonicSourceSettingsImpl>(this, _$identity);
}
abstract class _SubsonicSourceSettings extends SubsonicSourceSettings {
const factory _SubsonicSourceSettings(
{required final SourceSettings source,
required final SubsonicSettings subsonic}) = _$_SubsonicSourceSettings;
required final SubsonicSettings subsonic}) = _$SubsonicSourceSettingsImpl;
const _SubsonicSourceSettings._() : super._();
@override
@@ -805,6 +809,6 @@ abstract class _SubsonicSourceSettings extends SubsonicSourceSettings {
SubsonicSettings get subsonic;
@override
@JsonKey(ignore: true)
_$$_SubsonicSourceSettingsCopyWith<_$_SubsonicSourceSettings> get copyWith =>
throw _privateConstructorUsedError;
_$$SubsonicSourceSettingsImplCopyWith<_$SubsonicSourceSettingsImpl>
get copyWith => throw _privateConstructorUsedError;
}

View File

@@ -68,7 +68,8 @@ enum QueueContextType {
album('album'),
playlist('playlist'),
library('library'),
genre('genre');
genre('genre'),
artist('artist');
const QueueContextType(this.value);
final String value;

File diff suppressed because it is too large Load Diff

View File

@@ -6,15 +6,16 @@ part of 'support.dart';
// JsonSerializableGenerator
// **************************************************************************
_$_QueueItemState _$$_QueueItemStateFromJson(Map<String, dynamic> json) =>
_$_QueueItemState(
_$QueueItemStateImpl _$$QueueItemStateImplFromJson(Map<String, dynamic> json) =>
_$QueueItemStateImpl(
id: json['id'] as String,
contextType: $enumDecode(_$QueueContextTypeEnumMap, json['contextType']),
contextId: json['contextId'] as String?,
contextTitle: json['contextTitle'] as String?,
);
Map<String, dynamic> _$$_QueueItemStateToJson(_$_QueueItemState instance) =>
Map<String, dynamic> _$$QueueItemStateImplToJson(
_$QueueItemStateImpl instance) =>
<String, dynamic>{
'id': instance.id,
'contextType': _$QueueContextTypeEnumMap[instance.contextType]!,
@@ -28,11 +29,12 @@ const _$QueueContextTypeEnumMap = {
QueueContextType.playlist: 'playlist',
QueueContextType.library: 'library',
QueueContextType.genre: 'genre',
QueueContextType.artist: 'artist',
};
_$_MediaItemData _$$_MediaItemDataFromJson(Map<String, dynamic> json) =>
_$_MediaItemData(
sourceId: json['sourceId'] as int,
_$MediaItemDataImpl _$$MediaItemDataImplFromJson(Map<String, dynamic> json) =>
_$MediaItemDataImpl(
sourceId: (json['sourceId'] as num).toInt(),
albumId: json['albumId'] as String?,
artCache:
_$JsonConverterFromJson<Map<String, dynamic>, MediaItemArtCache>(
@@ -41,7 +43,7 @@ _$_MediaItemData _$$_MediaItemDataFromJson(Map<String, dynamic> json) =>
contextId: json['contextId'] as String?,
);
Map<String, dynamic> _$$_MediaItemDataToJson(_$_MediaItemData instance) =>
Map<String, dynamic> _$$MediaItemDataImplToJson(_$MediaItemDataImpl instance) =>
<String, dynamic>{
'sourceId': instance.sourceId,
'albumId': instance.albumId,
@@ -64,16 +66,17 @@ Json? _$JsonConverterToJson<Json, Value>(
) =>
value == null ? null : toJson(value);
_$_MediaItemArtCache _$$_MediaItemArtCacheFromJson(Map<String, dynamic> json) =>
_$_MediaItemArtCache(
_$MediaItemArtCacheImpl _$$MediaItemArtCacheImplFromJson(
Map<String, dynamic> json) =>
_$MediaItemArtCacheImpl(
fullArtUri: Uri.parse(json['fullArtUri'] as String),
fullArtCacheKey: json['fullArtCacheKey'] as String,
thumbnailArtUri: Uri.parse(json['thumbnailArtUri'] as String),
thumbnailArtCacheKey: json['thumbnailArtCacheKey'] as String,
);
Map<String, dynamic> _$$_MediaItemArtCacheToJson(
_$_MediaItemArtCache instance) =>
Map<String, dynamic> _$$MediaItemArtCacheImplToJson(
_$MediaItemArtCacheImpl instance) =>
<String, dynamic>{
'fullArtUri': instance.fullArtUri.toString(),
'fullArtCacheKey': instance.fullArtCacheKey,

View File

@@ -4,7 +4,6 @@ import 'dart:math';
import 'package:audio_service/audio_service.dart';
import 'package:collection/collection.dart';
import 'package:drift/drift.dart' show Value;
import 'package:flutter/foundation.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:just_audio/just_audio.dart';
import 'package:pool/pool.dart';
@@ -14,6 +13,7 @@ import 'package:synchronized/synchronized.dart';
import '../cache/image_cache.dart';
import '../database/database.dart';
import '../log.dart';
import '../models/music.dart';
import '../models/query.dart';
import '../models/support.dart';
@@ -122,6 +122,10 @@ class AudioControl extends BaseAudioHandler with QueueHandler, SeekHandler {
));
});
_player.playbackEventStream.doOnError((e, st) async {
log.warning('playbackEventStream', e, st);
});
shuffleIndicies.listen((value) {
playbackState.add(playbackState.value.copyWith(
shuffleMode: value != null
@@ -137,7 +141,7 @@ class AudioControl extends BaseAudioHandler with QueueHandler, SeekHandler {
_player.processingStateStream.listen((event) async {
if (event == ProcessingState.completed) {
if (_audioSource.length > 0) {
yell('completed');
log.fine('completed');
await stop();
await seek(Duration.zero);
}
@@ -386,7 +390,7 @@ class AudioControl extends BaseAudioHandler with QueueHandler, SeekHandler {
mediaItem.add(slice.current!.mediaItem);
queue.add(list.map((e) => e.mediaItem).toList());
yell('addAll');
log.fine('addAll');
await _audioSource.addAll(list.map((e) => e.audioSource).toList());
await _player.seek(Duration.zero, index: list.indexOf(slice.current!));
}
@@ -410,7 +414,7 @@ class AudioControl extends BaseAudioHandler with QueueHandler, SeekHandler {
final sourceNeedsPrev = sourceIndex == 0;
if (sourceNeedsNext && slice.next != null) {
yell('add');
log.fine('add');
await _audioSource.add(slice.next!.audioSource);
}
if (sourceNeedsPrev && slice.prev != null) {
@@ -497,7 +501,7 @@ class AudioControl extends BaseAudioHandler with QueueHandler, SeekHandler {
}
Future<void> _insertFirstAudioSource(AudioSource source) {
yell('insert');
log.fine('insert');
final wait = _audioSource.insert(0, source);
_currentIndexIgnore.add(1);
return wait;
@@ -505,20 +509,20 @@ class AudioControl extends BaseAudioHandler with QueueHandler, SeekHandler {
Future<void> _pruneAudioSources(int keepIndex) async {
if (keepIndex > 0) {
yell('removeRange 0');
log.fine('removeRange 0');
final wait = _audioSource.removeRange(0, keepIndex);
_currentIndexIgnore.add(0);
await wait;
}
if (_audioSource.length > 1) {
yell('removeRange 1');
log.fine('removeRange 1');
await _audioSource.removeRange(1, _audioSource.length);
}
}
Future<void> _clearAudioSource([bool clearMetadata = false]) async {
// await _player.stop();
yell('_clearAudioSource');
log.fine('_clearAudioSource');
await _audioSource.clear();
if (clearMetadata) {
@@ -697,11 +701,3 @@ class AudioControl extends BaseAudioHandler with QueueHandler, SeekHandler {
}
}
}
void yell(String msg) {
if (kDebugMode) {
print('=================================================================<');
print(msg);
print('=================================================================>');
}
}

View File

@@ -35,4 +35,5 @@ final audioControlProvider = Provider<AudioControl>.internal(
);
typedef AudioControlRef = ProviderRef<AudioControl>;
// ignore_for_file: unnecessary_raw_strings, subtype_of_sealed_class, invalid_use_of_internal_member, do_not_use_environment, prefer_const_constructors, public_member_api_docs, avoid_private_typedef_functions
// ignore_for_file: type=lint
// ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member

View File

@@ -20,4 +20,5 @@ final cacheServiceProvider = Provider<CacheService>.internal(
);
typedef CacheServiceRef = ProviderRef<CacheService>;
// ignore_for_file: unnecessary_raw_strings, subtype_of_sealed_class, invalid_use_of_internal_member, do_not_use_environment, prefer_const_constructors, public_member_api_docs, avoid_private_typedef_functions
// ignore_for_file: type=lint
// ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member

View File

@@ -214,7 +214,7 @@ class DownloadService extends _$DownloadService {
Future<void> deleteAll(int sourceId) async {
final db = ref.read(databaseProvider);
final albumIds = await db.albumIdsWithDownloaded(sourceId).get();
final albumIds = await db.albumIdsWithDownloadStatus(sourceId).get();
for (var id in albumIds) {
await deleteAlbum(await (db.albumById(sourceId, id)).getSingle());
}
@@ -534,7 +534,7 @@ class DownloadService extends _$DownloadService {
_port.asyncMap((dynamic data) async {
final taskId = (data as List<dynamic>)[0] as String;
final status = DownloadTaskStatus(data[1] as int);
final status = DownloadTaskStatus.fromInt(data[1] as int);
final progress = data[2] as int;
var download = state.downloads.firstWhereOrNull(
@@ -579,11 +579,11 @@ class DownloadService extends _$DownloadService {
@pragma('vm:entry-point')
static void downloadCallback(
String id,
DownloadTaskStatus status,
int status,
int progress,
) {
IsolateNameServer.lookupPortByName('downloader_send_port')?.send(
[id, status.value, progress],
[id, status, progress],
);
}
}

View File

@@ -12,7 +12,7 @@ part of 'download_service.dart';
T _$identity<T>(T value) => value;
final _privateConstructorUsedError = UnsupportedError(
'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\nPlease check the documentation here for more information: https://github.com/rrousselGit/freezed#custom-getters-and-methods');
'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\nPlease check the documentation here for more information: https://github.com/rrousselGit/freezed#adding-getters-and-methods-to-our-models');
/// @nodoc
mixin _$DownloadState {
@@ -86,11 +86,11 @@ class _$DownloadStateCopyWithImpl<$Res, $Val extends DownloadState>
}
/// @nodoc
abstract class _$$_DownloadStateCopyWith<$Res>
abstract class _$$DownloadStateImplCopyWith<$Res>
implements $DownloadStateCopyWith<$Res> {
factory _$$_DownloadStateCopyWith(
_$_DownloadState value, $Res Function(_$_DownloadState) then) =
__$$_DownloadStateCopyWithImpl<$Res>;
factory _$$DownloadStateImplCopyWith(
_$DownloadStateImpl value, $Res Function(_$DownloadStateImpl) then) =
__$$DownloadStateImplCopyWithImpl<$Res>;
@override
@useResult
$Res call(
@@ -102,11 +102,11 @@ abstract class _$$_DownloadStateCopyWith<$Res>
}
/// @nodoc
class __$$_DownloadStateCopyWithImpl<$Res>
extends _$DownloadStateCopyWithImpl<$Res, _$_DownloadState>
implements _$$_DownloadStateCopyWith<$Res> {
__$$_DownloadStateCopyWithImpl(
_$_DownloadState _value, $Res Function(_$_DownloadState) _then)
class __$$DownloadStateImplCopyWithImpl<$Res>
extends _$DownloadStateCopyWithImpl<$Res, _$DownloadStateImpl>
implements _$$DownloadStateImplCopyWith<$Res> {
__$$DownloadStateImplCopyWithImpl(
_$DownloadStateImpl _value, $Res Function(_$DownloadStateImpl) _then)
: super(_value, _then);
@pragma('vm:prefer-inline')
@@ -118,7 +118,7 @@ class __$$_DownloadStateCopyWithImpl<$Res>
Object? listCancels = null,
Object? saveDir = null,
}) {
return _then(_$_DownloadState(
return _then(_$DownloadStateImpl(
downloads: null == downloads
? _value.downloads
: downloads // ignore: cast_nullable_to_non_nullable
@@ -145,8 +145,8 @@ class __$$_DownloadStateCopyWithImpl<$Res>
/// @nodoc
class _$_DownloadState implements _DownloadState {
const _$_DownloadState(
class _$DownloadStateImpl implements _DownloadState {
const _$DownloadStateImpl(
{this.downloads = const IListConst([]),
this.deletes = const IListConst([]),
this.listDownloads = const IListConst([]),
@@ -174,10 +174,10 @@ class _$_DownloadState implements _DownloadState {
}
@override
bool operator ==(dynamic other) {
bool operator ==(Object other) {
return identical(this, other) ||
(other.runtimeType == runtimeType &&
other is _$_DownloadState &&
other is _$DownloadStateImpl &&
const DeepCollectionEquality().equals(other.downloads, downloads) &&
const DeepCollectionEquality().equals(other.deletes, deletes) &&
const DeepCollectionEquality()
@@ -199,8 +199,8 @@ class _$_DownloadState implements _DownloadState {
@JsonKey(ignore: true)
@override
@pragma('vm:prefer-inline')
_$$_DownloadStateCopyWith<_$_DownloadState> get copyWith =>
__$$_DownloadStateCopyWithImpl<_$_DownloadState>(this, _$identity);
_$$DownloadStateImplCopyWith<_$DownloadStateImpl> get copyWith =>
__$$DownloadStateImplCopyWithImpl<_$DownloadStateImpl>(this, _$identity);
}
abstract class _DownloadState implements DownloadState {
@@ -209,7 +209,7 @@ abstract class _DownloadState implements DownloadState {
final IList<SourceId> deletes,
final IList<SourceId> listDownloads,
final IList<SourceId> listCancels,
required final String saveDir}) = _$_DownloadState;
required final String saveDir}) = _$DownloadStateImpl;
@override
IList<Download> get downloads;
@@ -223,7 +223,7 @@ abstract class _DownloadState implements DownloadState {
String get saveDir;
@override
@JsonKey(ignore: true)
_$$_DownloadStateCopyWith<_$_DownloadState> get copyWith =>
_$$DownloadStateImplCopyWith<_$DownloadStateImpl> get copyWith =>
throw _privateConstructorUsedError;
}
@@ -319,10 +319,11 @@ class _$DownloadCopyWithImpl<$Res, $Val extends Download>
}
/// @nodoc
abstract class _$$_DownloadCopyWith<$Res> implements $DownloadCopyWith<$Res> {
factory _$$_DownloadCopyWith(
_$_Download value, $Res Function(_$_Download) then) =
__$$_DownloadCopyWithImpl<$Res>;
abstract class _$$DownloadImplCopyWith<$Res>
implements $DownloadCopyWith<$Res> {
factory _$$DownloadImplCopyWith(
_$DownloadImpl value, $Res Function(_$DownloadImpl) then) =
__$$DownloadImplCopyWithImpl<$Res>;
@override
@useResult
$Res call(
@@ -337,11 +338,11 @@ abstract class _$$_DownloadCopyWith<$Res> implements $DownloadCopyWith<$Res> {
}
/// @nodoc
class __$$_DownloadCopyWithImpl<$Res>
extends _$DownloadCopyWithImpl<$Res, _$_Download>
implements _$$_DownloadCopyWith<$Res> {
__$$_DownloadCopyWithImpl(
_$_Download _value, $Res Function(_$_Download) _then)
class __$$DownloadImplCopyWithImpl<$Res>
extends _$DownloadCopyWithImpl<$Res, _$DownloadImpl>
implements _$$DownloadImplCopyWith<$Res> {
__$$DownloadImplCopyWithImpl(
_$DownloadImpl _value, $Res Function(_$DownloadImpl) _then)
: super(_value, _then);
@pragma('vm:prefer-inline')
@@ -356,7 +357,7 @@ class __$$_DownloadCopyWithImpl<$Res>
Object? timeCreated = null,
Object? allowCellular = null,
}) {
return _then(_$_Download(
return _then(_$DownloadImpl(
taskId: null == taskId
? _value.taskId
: taskId // ignore: cast_nullable_to_non_nullable
@@ -395,8 +396,8 @@ class __$$_DownloadCopyWithImpl<$Res>
/// @nodoc
class _$_Download extends _Download {
const _$_Download(
class _$DownloadImpl extends _Download {
const _$DownloadImpl(
{required this.taskId,
required this.status,
required this.progress,
@@ -430,10 +431,10 @@ class _$_Download extends _Download {
}
@override
bool operator ==(dynamic other) {
bool operator ==(Object other) {
return identical(this, other) ||
(other.runtimeType == runtimeType &&
other is _$_Download &&
other is _$DownloadImpl &&
(identical(other.taskId, taskId) || other.taskId == taskId) &&
(identical(other.status, status) || other.status == status) &&
(identical(other.progress, progress) ||
@@ -456,8 +457,8 @@ class _$_Download extends _Download {
@JsonKey(ignore: true)
@override
@pragma('vm:prefer-inline')
_$$_DownloadCopyWith<_$_Download> get copyWith =>
__$$_DownloadCopyWithImpl<_$_Download>(this, _$identity);
_$$DownloadImplCopyWith<_$DownloadImpl> get copyWith =>
__$$DownloadImplCopyWithImpl<_$DownloadImpl>(this, _$identity);
}
abstract class _Download extends Download {
@@ -469,7 +470,7 @@ abstract class _Download extends Download {
required final String? filename,
required final String savedDir,
required final int timeCreated,
required final bool allowCellular}) = _$_Download;
required final bool allowCellular}) = _$DownloadImpl;
const _Download._() : super._();
@override
@@ -490,6 +491,6 @@ abstract class _Download extends Download {
bool get allowCellular;
@override
@JsonKey(ignore: true)
_$$_DownloadCopyWith<_$_Download> get copyWith =>
_$$DownloadImplCopyWith<_$DownloadImpl> get copyWith =>
throw _privateConstructorUsedError;
}

View File

@@ -6,7 +6,7 @@ part of 'download_service.dart';
// RiverpodGenerator
// **************************************************************************
String _$downloadServiceHash() => r'92e963b5c070f4d1edb0cd81899b16393c2b9a70';
String _$downloadServiceHash() => r'd2aeed2c026de4dc7fa26b2331244b49e7e9f003';
/// See also [DownloadService].
@ProviderFor(DownloadService)
@@ -22,4 +22,5 @@ final downloadServiceProvider =
);
typedef _$DownloadService = Notifier<DownloadState>;
// ignore_for_file: unnecessary_raw_strings, subtype_of_sealed_class, invalid_use_of_internal_member, do_not_use_environment, prefer_const_constructors, public_member_api_docs, avoid_private_typedef_functions
// ignore_for_file: type=lint
// ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member

View File

@@ -46,13 +46,15 @@ class SettingsService extends _$SettingsService {
features: IList(),
username: subsonic.username.value,
password: subsonic.password.value,
useTokenAuth: true,
useTokenAuth: subsonic.useTokenAuth.value,
isActive: true,
createdAt: DateTime.now(),
),
ref.read(httpClientProvider),
);
await client.test();
final features = IList([
if (await client.testFeature(SubsonicFeature.emptyQuerySearch))
SubsonicFeature.emptyQuerySearch,
@@ -66,6 +68,10 @@ class SettingsService extends _$SettingsService {
}
Future<void> updateSource(SubsonicSettings source) async {
final client = SubsonicClient(source, ref.read(httpClientProvider));
await client.test();
await _db.updateSource(source);
await init();
}

View File

@@ -6,7 +6,7 @@ part of 'settings_service.dart';
// RiverpodGenerator
// **************************************************************************
String _$settingsServiceHash() => r'85f2bd5eedc3f791fe03a6707748bc95277c6aaf';
String _$settingsServiceHash() => r'b53814d7d06c5d0a7ac82f447ac510e59a9598f0';
/// See also [SettingsService].
@ProviderFor(SettingsService)
@@ -22,4 +22,5 @@ final settingsServiceProvider =
);
typedef _$SettingsService = Notifier<Settings>;
// ignore_for_file: unnecessary_raw_strings, subtype_of_sealed_class, invalid_use_of_internal_member, do_not_use_environment, prefer_const_constructors, public_member_api_docs, avoid_private_typedef_functions
// ignore_for_file: type=lint
// ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member

View File

@@ -31,7 +31,7 @@ class SyncService extends _$SyncService {
final source = ref.read(musicSourceProvider);
final db = ref.read(databaseProvider);
final ids = <String>[];
final ids = <String>{};
await for (var artists in source.allArtists()) {
ids.addAll(artists.map((e) => e.id.value));
await db.saveArtists(artists);
@@ -44,7 +44,7 @@ class SyncService extends _$SyncService {
final source = ref.read(musicSourceProvider);
final db = ref.read(databaseProvider);
final ids = <String>[];
final ids = <String>{};
await for (var albums in source.allAlbums()) {
ids.addAll(albums.map((e) => e.id.value));
await db.saveAlbums(albums);
@@ -57,7 +57,7 @@ class SyncService extends _$SyncService {
final source = ref.read(musicSourceProvider);
final db = ref.read(databaseProvider);
final ids = <String>[];
final ids = <String>{};
await for (var playlists in source.allPlaylists()) {
ids.addAll(playlists.map((e) => e.playist.id.value));
await db.savePlaylists(playlists);
@@ -70,7 +70,7 @@ class SyncService extends _$SyncService {
final source = ref.read(musicSourceProvider);
final db = ref.read(databaseProvider);
final ids = <String>[];
final ids = <String>{};
await for (var songs in source.allSongs()) {
ids.addAll(songs.map((e) => e.id.value));
await db.saveSongs(songs);

View File

@@ -6,7 +6,7 @@ part of 'sync_service.dart';
// RiverpodGenerator
// **************************************************************************
String _$syncServiceHash() => r'2b8da374c3143bc56f17115440d57bc70468a17e';
String _$syncServiceHash() => r'58ebee4e6f055b64ee6789ae43d63c0e15c679e0';
/// See also [SyncService].
@ProviderFor(SyncService)
@@ -20,4 +20,5 @@ final syncServiceProvider = NotifierProvider<SyncService, DateTime>.internal(
);
typedef _$SyncService = Notifier<DateTime>;
// ignore_for_file: unnecessary_raw_strings, subtype_of_sealed_class, invalid_use_of_internal_member, do_not_use_environment, prefer_const_constructors, public_member_api_docs, avoid_private_typedef_functions
// ignore_for_file: type=lint
// ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member

View File

@@ -1,6 +1,8 @@
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:rxdart/rxdart.dart';
import '../database/database.dart';
import '../log.dart';
import '../state/settings.dart';
abstract class BaseMusicSource {
@@ -40,25 +42,33 @@ class MusicSource implements BaseMusicSource {
@override
Stream<Iterable<AlbumsCompanion>> allAlbums() {
_testOnline();
return _source.allAlbums();
return _source
.allAlbums()
.doOnError((e, st) => log.severe('allAlbums', e, st));
}
@override
Stream<Iterable<ArtistsCompanion>> allArtists() {
_testOnline();
return _source.allArtists();
return _source
.allArtists()
.doOnError((e, st) => log.severe('allArtists', e, st));
}
@override
Stream<Iterable<PlaylistWithSongsCompanion>> allPlaylists() {
_testOnline();
return _source.allPlaylists();
return _source
.allPlaylists()
.doOnError((e, st) => log.severe('allPlaylists', e, st));
}
@override
Stream<Iterable<SongsCompanion>> allSongs() {
_testOnline();
return _source.allSongs();
return _source
.allSongs()
.doOnError((e, st) => log.severe('allSongs', e, st));
}
@override
@@ -82,10 +92,4 @@ class MusicSource implements BaseMusicSource {
@override
Uri streamUri(String songId) => _source.streamUri(songId);
@override
bool operator ==(other) => other is BaseMusicSource && (other.id == id);
@override
int get hashCode => id;
}

View File

@@ -6,6 +6,7 @@ import 'package:crypto/crypto.dart';
import 'package:http/http.dart';
import 'package:xml/xml.dart';
import '../../log.dart';
import '../../models/settings.dart';
import 'xml.dart';
@@ -89,12 +90,16 @@ class SubsonicClient {
final subsonicResponse =
SubsonicResponse(XmlDocument.parse(utf8.decode(res.bodyBytes)));
if (subsonicResponse.status == Status.failed) {
throw SubsonicException(subsonicResponse.xml);
final error = SubsonicException(subsonicResponse.xml);
log.severe('Subsonic error', error);
throw error;
}
return subsonicResponse;
}
Future<void> test() => get('ping');
Future<bool> testFeature(SubsonicFeature feature) async {
switch (feature) {
case SubsonicFeature.emptyQuerySearch:

View File

@@ -144,7 +144,7 @@ class SubsonicSource implements MusicSource {
return Uri.tryParse(res.xml
.getElement('artistInfo2')
?.getElement(thumbnail ? 'smallImageUrl' : 'largeImageUrl')
?.text ??
?.value ??
'');
}

View File

@@ -226,4 +226,5 @@ final lastAudioStateServiceProvider =
);
typedef _$LastAudioStateService = AsyncNotifier<void>;
// ignore_for_file: unnecessary_raw_strings, subtype_of_sealed_class, invalid_use_of_internal_member, do_not_use_environment, prefer_const_constructors, public_member_api_docs, avoid_private_typedef_functions
// ignore_for_file: type=lint
// ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member

View File

@@ -94,4 +94,5 @@ final initProvider = FutureProvider<void>.internal(
);
typedef InitRef = FutureProviderRef<void>;
// ignore_for_file: unnecessary_raw_strings, subtype_of_sealed_class, invalid_use_of_internal_member, do_not_use_environment, prefer_const_constructors, public_member_api_docs, avoid_private_typedef_functions
// ignore_for_file: type=lint
// ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member

File diff suppressed because it is too large Load Diff

View File

@@ -76,4 +76,5 @@ final offlineModeProvider = NotifierProvider<OfflineMode, bool>.internal(
);
typedef _$OfflineMode = Notifier<bool>;
// ignore_for_file: unnecessary_raw_strings, subtype_of_sealed_class, invalid_use_of_internal_member, do_not_use_environment, prefer_const_constructors, public_member_api_docs, avoid_private_typedef_functions
// ignore_for_file: type=lint
// ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member

View File

@@ -87,7 +87,6 @@ ColorTheme _colorTheme(_ColorThemeRef ref, Palette palette) {
final colorScheme = ColorScheme.fromSeed(
brightness: Brightness.dark,
seedColor: background?.color ?? Colors.purple[800]!,
background: background?.color,
primaryContainer: primary?.color,
onPrimaryContainer: primary?.bodyTextColor,
secondaryContainer: secondary?.color,
@@ -96,8 +95,8 @@ ColorTheme _colorTheme(_ColorThemeRef ref, Palette palette) {
surfaceTint: vibrant?.color,
);
final hsv = HSVColor.fromColor(colorScheme.background);
final hsl = HSLColor.fromColor(colorScheme.background);
final hsv = HSVColor.fromColor(colorScheme.surface);
final hsl = HSLColor.fromColor(colorScheme.surface);
return base.copyWith(
theme: ThemeData(
@@ -106,7 +105,7 @@ ColorTheme _colorTheme(_ColorThemeRef ref, Palette palette) {
brightness: base.theme.brightness,
cardTheme: base.theme.cardTheme,
),
gradientHigh: colorScheme.background,
gradientHigh: colorScheme.surface,
darkBackground: hsv.withValue(kDarkBackgroundValue).toColor(),
darkerBackground: hsl.withLightness(kDarkerBackgroundLightness).toColor(),
onDarkerBackground:
@@ -128,13 +127,13 @@ ColorTheme baseTheme(BaseThemeRef ref) {
),
);
final hsv = HSVColor.fromColor(theme.colorScheme.background);
final hsl = HSLColor.fromColor(theme.colorScheme.background);
final hsv = HSVColor.fromColor(theme.colorScheme.surface);
final hsl = HSLColor.fromColor(theme.colorScheme.surface);
return ColorTheme(
theme: theme,
gradientHigh: theme.colorScheme.background,
gradientLow: HSLColor.fromColor(theme.colorScheme.background)
gradientHigh: theme.colorScheme.surface,
gradientLow: HSLColor.fromColor(theme.colorScheme.surface)
.withLightness(0.06)
.toColor(),
darkBackground: hsv.withValue(kDarkBackgroundValue).toColor(),

View File

@@ -6,7 +6,7 @@ part of 'theme.dart';
// RiverpodGenerator
// **************************************************************************
String _$colorThemeHash() => r'f5cc23cb5e2af379c02ae4b9756df72f9f6da5e6';
String _$colorThemeHash() => r'37d4eb17395341800b67f4f257c2cc49bf3dc91b';
/// Copied from Dart SDK
class _SystemHash {
@@ -29,8 +29,6 @@ class _SystemHash {
}
}
typedef _ColorThemeRef = AutoDisposeProviderRef<ColorTheme>;
/// See also [_colorTheme].
@ProviderFor(_colorTheme)
const _colorThemeProvider = _ColorThemeFamily();
@@ -77,10 +75,10 @@ class _ColorThemeFamily extends Family<ColorTheme> {
class _ColorThemeProvider extends AutoDisposeProvider<ColorTheme> {
/// See also [_colorTheme].
_ColorThemeProvider(
this.palette,
) : super.internal(
Palette palette,
) : this._internal(
(ref) => _colorTheme(
ref,
ref as _ColorThemeRef,
palette,
),
from: _colorThemeProvider,
@@ -92,10 +90,44 @@ class _ColorThemeProvider extends AutoDisposeProvider<ColorTheme> {
dependencies: _ColorThemeFamily._dependencies,
allTransitiveDependencies:
_ColorThemeFamily._allTransitiveDependencies,
palette: palette,
);
_ColorThemeProvider._internal(
super._createNotifier, {
required super.name,
required super.dependencies,
required super.allTransitiveDependencies,
required super.debugGetCreateSourceHash,
required super.from,
required this.palette,
}) : super.internal();
final Palette palette;
@override
Override overrideWith(
ColorTheme Function(_ColorThemeRef provider) create,
) {
return ProviderOverride(
origin: this,
override: _ColorThemeProvider._internal(
(ref) => create(ref as _ColorThemeRef),
from: from,
name: null,
dependencies: null,
allTransitiveDependencies: null,
debugGetCreateSourceHash: null,
palette: palette,
),
);
}
@override
AutoDisposeProviderElement<ColorTheme> createElement() {
return _ColorThemeProviderElement(this);
}
@override
bool operator ==(Object other) {
return other is _ColorThemeProvider && other.palette == palette;
@@ -110,7 +142,20 @@ class _ColorThemeProvider extends AutoDisposeProvider<ColorTheme> {
}
}
String _$baseThemeHash() => r'317a5ef77def208357a54b7938ef3d91666fce70';
mixin _ColorThemeRef on AutoDisposeProviderRef<ColorTheme> {
/// The parameter `palette` of this provider.
Palette get palette;
}
class _ColorThemeProviderElement extends AutoDisposeProviderElement<ColorTheme>
with _ColorThemeRef {
_ColorThemeProviderElement(super.provider);
@override
Palette get palette => (origin as _ColorThemeProvider).palette;
}
String _$baseThemeHash() => r'489ea7dcf66a57e6eda300c75d8af5fcaef85e8e';
/// See also [baseTheme].
@ProviderFor(baseTheme)
@@ -125,7 +170,6 @@ final baseThemeProvider = AutoDisposeProvider<ColorTheme>.internal(
typedef BaseThemeRef = AutoDisposeProviderRef<ColorTheme>;
String _$albumArtPaletteHash() => r'8130b954ee3c67f53207593d4ed3dfbffb00c95d';
typedef AlbumArtPaletteRef = AutoDisposeFutureProviderRef<Palette>;
/// See also [albumArtPalette].
@ProviderFor(albumArtPalette)
@@ -173,10 +217,10 @@ class AlbumArtPaletteFamily extends Family<AsyncValue<Palette>> {
class AlbumArtPaletteProvider extends AutoDisposeFutureProvider<Palette> {
/// See also [albumArtPalette].
AlbumArtPaletteProvider(
this.id,
) : super.internal(
String id,
) : this._internal(
(ref) => albumArtPalette(
ref,
ref as AlbumArtPaletteRef,
id,
),
from: albumArtPaletteProvider,
@@ -188,10 +232,44 @@ class AlbumArtPaletteProvider extends AutoDisposeFutureProvider<Palette> {
dependencies: AlbumArtPaletteFamily._dependencies,
allTransitiveDependencies:
AlbumArtPaletteFamily._allTransitiveDependencies,
id: id,
);
AlbumArtPaletteProvider._internal(
super._createNotifier, {
required super.name,
required super.dependencies,
required super.allTransitiveDependencies,
required super.debugGetCreateSourceHash,
required super.from,
required this.id,
}) : super.internal();
final String id;
@override
Override overrideWith(
FutureOr<Palette> Function(AlbumArtPaletteRef provider) create,
) {
return ProviderOverride(
origin: this,
override: AlbumArtPaletteProvider._internal(
(ref) => create(ref as AlbumArtPaletteRef),
from: from,
name: null,
dependencies: null,
allTransitiveDependencies: null,
debugGetCreateSourceHash: null,
id: id,
),
);
}
@override
AutoDisposeFutureProviderElement<Palette> createElement() {
return _AlbumArtPaletteProviderElement(this);
}
@override
bool operator ==(Object other) {
return other is AlbumArtPaletteProvider && other.id == id;
@@ -206,9 +284,21 @@ class AlbumArtPaletteProvider extends AutoDisposeFutureProvider<Palette> {
}
}
mixin AlbumArtPaletteRef on AutoDisposeFutureProviderRef<Palette> {
/// The parameter `id` of this provider.
String get id;
}
class _AlbumArtPaletteProviderElement
extends AutoDisposeFutureProviderElement<Palette> with AlbumArtPaletteRef {
_AlbumArtPaletteProviderElement(super.provider);
@override
String get id => (origin as AlbumArtPaletteProvider).id;
}
String _$playlistArtPaletteHash() =>
r'6bc015688f354ea8d91dde86e2a7191ef1ef6496';
typedef PlaylistArtPaletteRef = AutoDisposeFutureProviderRef<Palette>;
/// See also [playlistArtPalette].
@ProviderFor(playlistArtPalette)
@@ -256,10 +346,10 @@ class PlaylistArtPaletteFamily extends Family<AsyncValue<Palette>> {
class PlaylistArtPaletteProvider extends AutoDisposeFutureProvider<Palette> {
/// See also [playlistArtPalette].
PlaylistArtPaletteProvider(
this.id,
) : super.internal(
String id,
) : this._internal(
(ref) => playlistArtPalette(
ref,
ref as PlaylistArtPaletteRef,
id,
),
from: playlistArtPaletteProvider,
@@ -271,10 +361,44 @@ class PlaylistArtPaletteProvider extends AutoDisposeFutureProvider<Palette> {
dependencies: PlaylistArtPaletteFamily._dependencies,
allTransitiveDependencies:
PlaylistArtPaletteFamily._allTransitiveDependencies,
id: id,
);
PlaylistArtPaletteProvider._internal(
super._createNotifier, {
required super.name,
required super.dependencies,
required super.allTransitiveDependencies,
required super.debugGetCreateSourceHash,
required super.from,
required this.id,
}) : super.internal();
final String id;
@override
Override overrideWith(
FutureOr<Palette> Function(PlaylistArtPaletteRef provider) create,
) {
return ProviderOverride(
origin: this,
override: PlaylistArtPaletteProvider._internal(
(ref) => create(ref as PlaylistArtPaletteRef),
from: from,
name: null,
dependencies: null,
allTransitiveDependencies: null,
debugGetCreateSourceHash: null,
id: id,
),
);
}
@override
AutoDisposeFutureProviderElement<Palette> createElement() {
return _PlaylistArtPaletteProviderElement(this);
}
@override
bool operator ==(Object other) {
return other is PlaylistArtPaletteProvider && other.id == id;
@@ -289,6 +413,20 @@ class PlaylistArtPaletteProvider extends AutoDisposeFutureProvider<Palette> {
}
}
mixin PlaylistArtPaletteRef on AutoDisposeFutureProviderRef<Palette> {
/// The parameter `id` of this provider.
String get id;
}
class _PlaylistArtPaletteProviderElement
extends AutoDisposeFutureProviderElement<Palette>
with PlaylistArtPaletteRef {
_PlaylistArtPaletteProviderElement(super.provider);
@override
String get id => (origin as PlaylistArtPaletteProvider).id;
}
String _$mediaItemPaletteHash() => r'2f2744aa735c6056919197c283a367714d7e04e4';
/// See also [mediaItemPalette].
@@ -320,7 +458,6 @@ final mediaItemThemeProvider = AutoDisposeFutureProvider<ColorTheme>.internal(
typedef MediaItemThemeRef = AutoDisposeFutureProviderRef<ColorTheme>;
String _$albumArtThemeHash() => r'd3ee71b2df856f1763ec925e158ae2e0f613b9e0';
typedef AlbumArtThemeRef = AutoDisposeFutureProviderRef<ColorTheme>;
/// See also [albumArtTheme].
@ProviderFor(albumArtTheme)
@@ -368,10 +505,10 @@ class AlbumArtThemeFamily extends Family<AsyncValue<ColorTheme>> {
class AlbumArtThemeProvider extends AutoDisposeFutureProvider<ColorTheme> {
/// See also [albumArtTheme].
AlbumArtThemeProvider(
this.id,
) : super.internal(
String id,
) : this._internal(
(ref) => albumArtTheme(
ref,
ref as AlbumArtThemeRef,
id,
),
from: albumArtThemeProvider,
@@ -383,10 +520,44 @@ class AlbumArtThemeProvider extends AutoDisposeFutureProvider<ColorTheme> {
dependencies: AlbumArtThemeFamily._dependencies,
allTransitiveDependencies:
AlbumArtThemeFamily._allTransitiveDependencies,
id: id,
);
AlbumArtThemeProvider._internal(
super._createNotifier, {
required super.name,
required super.dependencies,
required super.allTransitiveDependencies,
required super.debugGetCreateSourceHash,
required super.from,
required this.id,
}) : super.internal();
final String id;
@override
Override overrideWith(
FutureOr<ColorTheme> Function(AlbumArtThemeRef provider) create,
) {
return ProviderOverride(
origin: this,
override: AlbumArtThemeProvider._internal(
(ref) => create(ref as AlbumArtThemeRef),
from: from,
name: null,
dependencies: null,
allTransitiveDependencies: null,
debugGetCreateSourceHash: null,
id: id,
),
);
}
@override
AutoDisposeFutureProviderElement<ColorTheme> createElement() {
return _AlbumArtThemeProviderElement(this);
}
@override
bool operator ==(Object other) {
return other is AlbumArtThemeProvider && other.id == id;
@@ -401,8 +572,20 @@ class AlbumArtThemeProvider extends AutoDisposeFutureProvider<ColorTheme> {
}
}
mixin AlbumArtThemeRef on AutoDisposeFutureProviderRef<ColorTheme> {
/// The parameter `id` of this provider.
String get id;
}
class _AlbumArtThemeProviderElement
extends AutoDisposeFutureProviderElement<ColorTheme> with AlbumArtThemeRef {
_AlbumArtThemeProviderElement(super.provider);
@override
String get id => (origin as AlbumArtThemeProvider).id;
}
String _$playlistArtThemeHash() => r'1629552e1f3aa2a1e7d223ac1e078893042e5e3b';
typedef PlaylistArtThemeRef = AutoDisposeFutureProviderRef<ColorTheme>;
/// See also [playlistArtTheme].
@ProviderFor(playlistArtTheme)
@@ -450,10 +633,10 @@ class PlaylistArtThemeFamily extends Family<AsyncValue<ColorTheme>> {
class PlaylistArtThemeProvider extends AutoDisposeFutureProvider<ColorTheme> {
/// See also [playlistArtTheme].
PlaylistArtThemeProvider(
this.id,
) : super.internal(
String id,
) : this._internal(
(ref) => playlistArtTheme(
ref,
ref as PlaylistArtThemeRef,
id,
),
from: playlistArtThemeProvider,
@@ -465,10 +648,44 @@ class PlaylistArtThemeProvider extends AutoDisposeFutureProvider<ColorTheme> {
dependencies: PlaylistArtThemeFamily._dependencies,
allTransitiveDependencies:
PlaylistArtThemeFamily._allTransitiveDependencies,
id: id,
);
PlaylistArtThemeProvider._internal(
super._createNotifier, {
required super.name,
required super.dependencies,
required super.allTransitiveDependencies,
required super.debugGetCreateSourceHash,
required super.from,
required this.id,
}) : super.internal();
final String id;
@override
Override overrideWith(
FutureOr<ColorTheme> Function(PlaylistArtThemeRef provider) create,
) {
return ProviderOverride(
origin: this,
override: PlaylistArtThemeProvider._internal(
(ref) => create(ref as PlaylistArtThemeRef),
from: from,
name: null,
dependencies: null,
allTransitiveDependencies: null,
debugGetCreateSourceHash: null,
id: id,
),
);
}
@override
AutoDisposeFutureProviderElement<ColorTheme> createElement() {
return _PlaylistArtThemeProviderElement(this);
}
@override
bool operator ==(Object other) {
return other is PlaylistArtThemeProvider && other.id == id;
@@ -482,4 +699,19 @@ class PlaylistArtThemeProvider extends AutoDisposeFutureProvider<ColorTheme> {
return _SystemHash.finish(hash);
}
}
// ignore_for_file: unnecessary_raw_strings, subtype_of_sealed_class, invalid_use_of_internal_member, do_not_use_environment, prefer_const_constructors, public_member_api_docs, avoid_private_typedef_functions
mixin PlaylistArtThemeRef on AutoDisposeFutureProviderRef<ColorTheme> {
/// The parameter `id` of this provider.
String get id;
}
class _PlaylistArtThemeProviderElement
extends AutoDisposeFutureProviderElement<ColorTheme>
with PlaylistArtThemeRef {
_PlaylistArtThemeProviderElement(super.provider);
@override
String get id => (origin as PlaylistArtThemeProvider).id;
}
// ignore_for_file: type=lint
// ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member

File diff suppressed because it is too large Load Diff

View File

@@ -4,10 +4,10 @@ homepage: https://github.com/austinried/subtracks
repository: https://github.com/austinried/subtracks
issue_tracker: https://github.com/austinried/subtracks/issues
publish_to: 'none'
version: 2.0.0-alpha.1+10
version: 2.0.0-alpha.3+12
environment:
sdk: '>=2.19.2 <3.0.0'
sdk: '>=3.5.0 <4.0.0'
dependencies:
flutter:
@@ -22,7 +22,7 @@ dependencies:
path: ^1.8.2
path_provider: ^2.0.12
flutter_dotenv: ^5.0.2 # TODO: remove before release
collection: ^1.17.0
collection: ^1.17.1
intl: any
flutter_hooks: ^0.18.5+1
hooks_riverpod: ^2.3.2
@@ -55,18 +55,17 @@ dependencies:
synchronized: ^3.1.0
flutter_keyboard_visibility: ^5.4.0
connectivity_plus: ^3.0.4
package_info_plus: ^3.1.1
package_info_plus: ^8.1.1
url_launcher: ^6.1.10
logging: ^1.1.1
share_plus: ^7.0.0
# https://github.com/dart-lang/intl/issues/522#issuecomment-1469961807
dependency_overrides:
intl: ^0.18.0
dev_dependencies:
flutter_test:
sdk: flutter
flutter_lints: ^2.0.0
test: ^1.22.0
build_runner:
riverpod_generator: ^2.1.4
freezed: ^2.3.2