mirror of
https://github.com/austinried/subtracks.git
synced 2026-03-28 07:12:43 +01:00
Compare commits
22 Commits
v2.0.0-alp
...
10280a9903
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
10280a9903 | ||
|
|
686e0a2dba | ||
|
|
fc0daacfc0 | ||
|
|
7e5885e5c8 | ||
|
|
b0bb26f84b | ||
|
|
e94fcf3128 | ||
|
|
bd6e818f36 | ||
|
|
96d0c35c31 | ||
|
|
4ef3281a0b | ||
|
|
c56e3dba0f | ||
|
|
53d284ace4 | ||
|
|
c2733482e5 | ||
|
|
67f0c926c4 | ||
|
|
889be2ff2c | ||
|
|
52b51954aa | ||
|
|
1c76293559 | ||
|
|
250d6793a2 | ||
|
|
121af2bca3 | ||
|
|
e410dcb2eb | ||
|
|
63ff9772e5 | ||
|
|
1ae29c5ade | ||
|
|
fedd6a71bb |
@@ -1,4 +0,0 @@
|
||||
{
|
||||
"flutterSdkVersion": "3.7.11",
|
||||
"flavors": {}
|
||||
}
|
||||
8
.gitignore
vendored
8
.gitignore
vendored
@@ -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
|
||||
|
||||
@@ -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,29 +127,8 @@
|
||||
],
|
||||
|
||||
"de": [
|
||||
"actionsDownloadDelete",
|
||||
"actionsOk",
|
||||
"resourcesAlbumCount",
|
||||
"resourcesArtistCount",
|
||||
"resourcesFilterAlbum",
|
||||
"resourcesFilterArtist",
|
||||
"resourcesFilterOwner",
|
||||
"resourcesFilterYear",
|
||||
"resourcesPlaylistCount",
|
||||
"resourcesSongCount",
|
||||
"resourcesSongListDeleteAllContent",
|
||||
"resourcesSongListDeleteAllTitle",
|
||||
"resourcesSortByAlbum",
|
||||
"resourcesSortByAlbumCount",
|
||||
"resourcesSortByTitle",
|
||||
"resourcesSortByUpdated",
|
||||
"settingsAboutActionsSupport",
|
||||
"settingsNetworkOptionsOfflineMode",
|
||||
"settingsNetworkOptionsOfflineModeOff",
|
||||
"settingsNetworkOptionsOfflineModeOn",
|
||||
"settingsNetworkOptionsStreamFormat",
|
||||
"settingsNetworkOptionsStreamFormatServerDefault",
|
||||
"settingsServersFieldsName"
|
||||
"settingsAboutShareLogs",
|
||||
"settingsAboutChooseLog"
|
||||
],
|
||||
|
||||
"es": [
|
||||
@@ -187,6 +147,8 @@
|
||||
"resourcesSortByTitle",
|
||||
"resourcesSortByUpdated",
|
||||
"settingsAboutActionsSupport",
|
||||
"settingsAboutShareLogs",
|
||||
"settingsAboutChooseLog",
|
||||
"settingsNetworkOptionsOfflineMode",
|
||||
"settingsNetworkOptionsOfflineModeOff",
|
||||
"settingsNetworkOptionsOfflineModeOn",
|
||||
@@ -218,6 +180,8 @@
|
||||
"resourcesSortByTitle",
|
||||
"resourcesSortByUpdated",
|
||||
"settingsAboutActionsSupport",
|
||||
"settingsAboutShareLogs",
|
||||
"settingsAboutChooseLog",
|
||||
"settingsNetworkOptionsOfflineMode",
|
||||
"settingsNetworkOptionsOfflineModeOff",
|
||||
"settingsNetworkOptionsOfflineModeOn",
|
||||
@@ -249,6 +213,8 @@
|
||||
"resourcesSortByTitle",
|
||||
"resourcesSortByUpdated",
|
||||
"settingsAboutActionsSupport",
|
||||
"settingsAboutShareLogs",
|
||||
"settingsAboutChooseLog",
|
||||
"settingsNetworkOptionsOfflineMode",
|
||||
"settingsNetworkOptionsOfflineModeOff",
|
||||
"settingsNetworkOptionsOfflineModeOn",
|
||||
@@ -300,6 +266,8 @@
|
||||
"settingsAboutActionsLicenses",
|
||||
"settingsAboutActionsSupport",
|
||||
"settingsAboutName",
|
||||
"settingsAboutShareLogs",
|
||||
"settingsAboutChooseLog",
|
||||
"settingsAboutVersion",
|
||||
"settingsMusicOptionsScrobbleDescriptionOff",
|
||||
"settingsMusicOptionsScrobbleDescriptionOn",
|
||||
@@ -356,6 +324,8 @@
|
||||
"resourcesSortByTitle",
|
||||
"resourcesSortByUpdated",
|
||||
"settingsAboutActionsSupport",
|
||||
"settingsAboutShareLogs",
|
||||
"settingsAboutChooseLog",
|
||||
"settingsNetworkOptionsOfflineMode",
|
||||
"settingsNetworkOptionsOfflineModeOff",
|
||||
"settingsNetworkOptionsOfflineModeOn",
|
||||
@@ -387,6 +357,8 @@
|
||||
"resourcesSortByTitle",
|
||||
"resourcesSortByUpdated",
|
||||
"settingsAboutActionsSupport",
|
||||
"settingsAboutShareLogs",
|
||||
"settingsAboutChooseLog",
|
||||
"settingsNetworkOptionsOfflineMode",
|
||||
"settingsNetworkOptionsOfflineModeOff",
|
||||
"settingsNetworkOptionsOfflineModeOn",
|
||||
@@ -418,6 +390,8 @@
|
||||
"resourcesSortByTitle",
|
||||
"resourcesSortByUpdated",
|
||||
"settingsAboutActionsSupport",
|
||||
"settingsAboutShareLogs",
|
||||
"settingsAboutChooseLog",
|
||||
"settingsNetworkOptionsOfflineMode",
|
||||
"settingsNetworkOptionsOfflineModeOff",
|
||||
"settingsNetworkOptionsOfflineModeOn",
|
||||
@@ -427,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"
|
||||
],
|
||||
@@ -511,6 +439,8 @@
|
||||
"resourcesSortByTitle",
|
||||
"resourcesSortByUpdated",
|
||||
"settingsAboutActionsSupport",
|
||||
"settingsAboutShareLogs",
|
||||
"settingsAboutChooseLog",
|
||||
"settingsNetworkOptionsOfflineMode",
|
||||
"settingsNetworkOptionsOfflineModeOff",
|
||||
"settingsNetworkOptionsOfflineModeOn",
|
||||
@@ -542,6 +472,8 @@
|
||||
"resourcesSortByTitle",
|
||||
"resourcesSortByUpdated",
|
||||
"settingsAboutActionsSupport",
|
||||
"settingsAboutShareLogs",
|
||||
"settingsAboutChooseLog",
|
||||
"settingsNetworkOptionsOfflineMode",
|
||||
"settingsNetworkOptionsOfflineModeOff",
|
||||
"settingsNetworkOptionsOfflineModeOn",
|
||||
@@ -556,6 +488,8 @@
|
||||
"resourcesArtistCount",
|
||||
"resourcesPlaylistCount",
|
||||
"resourcesSongCount",
|
||||
"settingsAboutShareLogs",
|
||||
"settingsAboutChooseLog",
|
||||
"settingsNetworkOptionsOfflineMode",
|
||||
"settingsNetworkOptionsOfflineModeOff",
|
||||
"settingsNetworkOptionsOfflineModeOn",
|
||||
|
||||
@@ -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"
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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"
|
||||
@@ -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,
|
||||
),
|
||||
),
|
||||
|
||||
@@ -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,
|
||||
);
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -118,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: [
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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,
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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),
|
||||
)}',
|
||||
);
|
||||
},
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
@@ -44,7 +46,7 @@ class SourcePage extends HookConsumerWidget {
|
||||
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;
|
||||
@@ -64,7 +66,7 @@ class SourcePage extends HookConsumerWidget {
|
||||
required: true,
|
||||
);
|
||||
|
||||
final forcePlaintextPassword = useState(!(source?.useTokenAuth ?? false));
|
||||
final forcePlaintextPassword = useState(!(source?.useTokenAuth ?? true));
|
||||
final forcePlaintextSwitch = SwitchListTile(
|
||||
value: forcePlaintextPassword.value,
|
||||
title: Text(l.settingsServersOptionsForcePlaintextPasswordTitle),
|
||||
@@ -74,8 +76,8 @@ class SourcePage extends HookConsumerWidget {
|
||||
onChanged: (value) => forcePlaintextPassword.value = value,
|
||||
);
|
||||
|
||||
return WillPopScope(
|
||||
onWillPop: () async => !isSaving.value && !isDeleting.value,
|
||||
return PopScope(
|
||||
canPop: !isSaving.value && !isDeleting.value,
|
||||
child: Scaffold(
|
||||
appBar: AppBar(),
|
||||
floatingActionButton: Row(
|
||||
@@ -161,8 +163,10 @@ class SourcePage extends HookConsumerWidget {
|
||||
),
|
||||
);
|
||||
}
|
||||
} 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;
|
||||
|
||||
14
lib/app/snackbars.dart
Normal file
14
lib/app/snackbars.dart
Normal 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),
|
||||
));
|
||||
}
|
||||
@@ -9,11 +9,13 @@ 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';
|
||||
|
||||
@@ -435,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),
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
94
lib/database/error_logging_database.dart
Normal file
94
lib/database/error_logging_database.dart
Normal 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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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": {}
|
||||
}
|
||||
|
||||
@@ -202,5 +202,75 @@
|
||||
"controlsShuffle": "Zufall",
|
||||
"@controlsShuffle": {},
|
||||
"actionsCancel": "Abbrechen",
|
||||
"@actionsCancel": {}
|
||||
"@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": {}
|
||||
}
|
||||
|
||||
@@ -173,6 +173,10 @@
|
||||
"@settingsAboutActionsSupport": {},
|
||||
"settingsAboutName": "About",
|
||||
"@settingsAboutName": {},
|
||||
"settingsAboutShareLogs": "Share logs",
|
||||
"@settingsAboutShareLogs": {},
|
||||
"settingsAboutChooseLog": "Choose a log file",
|
||||
"@settingsAboutChooseLog": {},
|
||||
"settingsAboutVersion": "version {version}",
|
||||
"@settingsAboutVersion": {
|
||||
"placeholders": {
|
||||
|
||||
@@ -272,5 +272,9 @@
|
||||
"resourcesSortByTitle": "Título",
|
||||
"@resourcesSortByTitle": {},
|
||||
"resourcesSortByUpdated": "Actualizado recentemente",
|
||||
"@resourcesSortByUpdated": {}
|
||||
"@resourcesSortByUpdated": {},
|
||||
"settingsAboutShareLogs": "Compartir rexistros",
|
||||
"@settingsAboutShareLogs": {},
|
||||
"settingsAboutChooseLog": "Escolle un ficheiro de rexistro",
|
||||
"@settingsAboutChooseLog": {}
|
||||
}
|
||||
|
||||
@@ -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": {}
|
||||
}
|
||||
|
||||
@@ -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"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
209
lib/log.dart
Normal file
209
lib/log.dart
Normal 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);
|
||||
}
|
||||
@@ -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()));
|
||||
}
|
||||
|
||||
@@ -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('=================================================================>');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -144,7 +144,7 @@ class SubsonicSource implements MusicSource {
|
||||
return Uri.tryParse(res.xml
|
||||
.getElement('artistInfo2')
|
||||
?.getElement(thumbnail ? 'smallImageUrl' : 'largeImageUrl')
|
||||
?.text ??
|
||||
?.value ??
|
||||
'');
|
||||
}
|
||||
|
||||
|
||||
@@ -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(),
|
||||
|
||||
646
pubspec.lock
646
pubspec.lock
File diff suppressed because it is too large
Load Diff
@@ -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.2+11
|
||||
version: 2.0.0-alpha.3+12
|
||||
|
||||
environment:
|
||||
sdk: '>=2.19.2 <3.0.0'
|
||||
sdk: '>=3.0.0 <4.0.0'
|
||||
|
||||
dependencies:
|
||||
flutter:
|
||||
@@ -55,8 +55,10 @@ 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:
|
||||
|
||||
Reference in New Issue
Block a user