This commit is contained in:
rang 2026-01-04 17:49:05 +08:00
parent a737365f26
commit 3c3c381f8a
18 changed files with 1266 additions and 1000 deletions

View file

@ -0,0 +1,3 @@
import 'package:just_audio/just_audio.dart';
class AudiobookPlayer extends AudioPlayer {}

View file

@ -1,8 +1,4 @@
import 'dart:io';
import 'package:audio_service/audio_service.dart'; import 'package:audio_service/audio_service.dart';
import 'package:flutter/foundation.dart';
import 'package:just_audio/just_audio.dart';
import 'package:vaani/features/player/core/abs_audio_player.dart'; import 'package:vaani/features/player/core/abs_audio_player.dart';
// audio_service // audio_service

View file

@ -1,34 +1,62 @@
// import 'package:audio_service/audio_service.dart'; // import 'package:audio_service/audio_service.dart';
// import 'package:audio_session/audio_session.dart'; // import 'package:audio_session/audio_session.dart';
// import 'package:riverpod_annotation/riverpod_annotation.dart'; // import 'package:just_audio_background/just_audio_background.dart'
// import 'package:vaani/features/player/core/abs_audio_handler.dart' as core; // show JustAudioBackground, NotificationConfig;
// import 'package:vaani/features/player/core/abs_audio_player.dart'; // import 'package:just_audio_media_kit/just_audio_media_kit.dart'
// import 'package:vaani/globals.dart'; // show JustAudioMediaKit;
// import 'package:vaani/features/settings/app_settings_provider.dart';
// import 'package:vaani/features/settings/models/app_settings.dart';
// /// // Future<void> configurePlayer() async {
// @Riverpod(keepAlive: true)
// Future<void> configurePlayer(AbsAudioPlayer player) async {
// // for playing audio on windows, linux // // for playing audio on windows, linux
// JustAudioMediaKit.ensureInitialized();
// // for configuring how this app will interact with other audio apps // // for configuring how this app will interact with other audio apps
// final session = await AudioSession.instance; // final session = await AudioSession.instance;
// await session.configure(const AudioSessionConfiguration.speech()); // await session.configure(const AudioSessionConfiguration.speech());
// await AudioService.init( // final appSettings = loadOrCreateAppSettings();
// builder: () => core.AbsAudioHandler(player),
// config: const AudioServiceConfig( // // for playing audio in the background
// await JustAudioBackground.init(
// androidNotificationChannelId: 'dr.blank.vaani.channel.audio', // androidNotificationChannelId: 'dr.blank.vaani.channel.audio',
// androidNotificationChannelName: 'ABSPlayback', // androidNotificationChannelName: 'Audio playback',
// androidNotificationChannelDescription:
// 'Needed to control audio from lock screen',
// androidNotificationOngoing: false, // androidNotificationOngoing: false,
// androidStopForegroundOnPause: false, // androidStopForegroundOnPause: false,
// androidNotificationChannelDescription: 'Audio playback in the background',
// androidNotificationIcon: 'drawable/ic_stat_logo', // androidNotificationIcon: 'drawable/ic_stat_logo',
// preloadArtwork: true, // rewindInterval: appSettings.notificationSettings.rewindInterval,
// // fastForwardInterval: Duration(seconds: 20), // fastForwardInterval: appSettings.notificationSettings.fastForwardInterval,
// // rewindInterval: Duration(seconds: 20), // androidShowNotificationBadge: false,
// ), // notificationConfigBuilder: (state) {
// final controls = [
// if (appSettings.notificationSettings.mediaControls
// .contains(NotificationMediaControl.skipToPreviousChapter) &&
// state.hasPrevious)
// MediaControl.skipToPrevious,
// if (appSettings.notificationSettings.mediaControls
// .contains(NotificationMediaControl.rewind))
// MediaControl.rewind,
// if (state.playing) MediaControl.pause else MediaControl.play,
// if (appSettings.notificationSettings.mediaControls
// .contains(NotificationMediaControl.fastForward))
// MediaControl.fastForward,
// if (appSettings.notificationSettings.mediaControls
// .contains(NotificationMediaControl.skipToNextChapter) &&
// state.hasNext)
// MediaControl.skipToNext,
// if (appSettings.notificationSettings.mediaControls
// .contains(NotificationMediaControl.stop))
// MediaControl.stop,
// ];
// return NotificationConfig(
// controls: controls,
// systemActions: const {
// MediaAction.seek,
// MediaAction.seekForward,
// MediaAction.seekBackward,
// },
// );
// },
// ); // );
// appLogger.finer('created simple player');
// } // }

View file

@ -2,6 +2,7 @@ import 'package:audio_service/audio_service.dart';
import 'package:audio_session/audio_session.dart'; import 'package:audio_session/audio_session.dart';
import 'package:collection/collection.dart'; import 'package:collection/collection.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:just_audio/just_audio.dart';
import 'package:logging/logging.dart'; import 'package:logging/logging.dart';
import 'package:riverpod_annotation/riverpod_annotation.dart'; import 'package:riverpod_annotation/riverpod_annotation.dart';
import 'package:shelfsdk/audiobookshelf_api.dart' as api; import 'package:shelfsdk/audiobookshelf_api.dart' as api;
@ -67,6 +68,123 @@ bool playerActive(Ref ref) {
return false; return false;
} }
@Riverpod(keepAlive: true)
AudioPlayer simpleAudioPlayer(Ref ref) {
final player = AudioPlayer();
ref.onDispose(player.dispose);
return player;
}
@Riverpod(keepAlive: true)
class AbsAudioPlayer extends _$AbsAudioPlayer {
@override
AudioPlayer build() {
final audioPlayer = ref.watch(simpleAudioPlayerProvider);
return audioPlayer;
}
Future<void> load(
api.BookExpanded book, {
Duration? initialPosition,
bool play = true,
}) async {
final currentTrack = book.findTrackAtTime(initialPosition ?? Duration.zero);
final indexTrack = book.tracks.indexOf(currentTrack);
final positionInTrack = initialPosition != null
? initialPosition - currentTrack.startOffset
: null;
final api = ref.read(authenticatedApiProvider);
final downloadManager = ref.read(simpleDownloadManagerProvider);
print(downloadManager.basePath);
final libItem =
await ref.read(libraryItemProvider(book.libraryItemId).future);
final downloadedUris = await downloadManager.getDownloadedFilesUri(libItem);
final bookSettings = ref.read(bookSettingsProvider(book.libraryItemId));
var bookPlayerSettings = bookSettings.playerSettings;
final start = bookSettings.playerSettings.skipChapterStart;
final end = bookSettings.playerSettings.skipChapterEnd;
final appPlayerSettings = ref.read(appSettingsProvider).playerSettings;
final configurePlayerForEveryBook =
appPlayerSettings.configurePlayerForEveryBook;
List<AudioSource> audioSources =
start > Duration.zero || end > Duration.zero
? book.tracks
.map(
(track) => ClippingAudioSource(
child: AudioSource.uri(
_getUri(
track,
downloadedUris,
baseUrl: api.baseUrl,
token: api.token!,
),
),
start: start,
end: end > Duration.zero ? null : track.duration - end,
),
)
.toList()
: book.tracks
.map(
(track) => AudioSource.uri(
_getUri(
track,
downloadedUris,
baseUrl: api.baseUrl,
token: api.token!,
),
),
)
.toList();
await state
.setAudioSources(
audioSources,
preload: true,
initialIndex: indexTrack,
initialPosition: positionInTrack,
)
.catchError((error) {
_logger.shout('Error in setting audio source: $error');
return null;
});
// set the volume
await state.setVolume(
configurePlayerForEveryBook
? bookPlayerSettings.preferredDefaultVolume ??
appPlayerSettings.preferredDefaultVolume
: appPlayerSettings.preferredDefaultVolume,
);
// set the speed
await state.setSpeed(
configurePlayerForEveryBook
? bookPlayerSettings.preferredDefaultSpeed ??
appPlayerSettings.preferredDefaultSpeed
: appPlayerSettings.preferredDefaultSpeed,
);
if (play) await state.play();
}
Uri _getUri(
api.AudioTrack track,
List<Uri>? downloadedUris, {
required Uri baseUrl,
required String token,
}) {
// check if the track is in the downloadedUris
final uri = downloadedUris?.firstWhereOrNull(
(element) {
return element.pathSegments.last == track.metadata?.filename;
},
);
return uri ??
Uri.parse('${baseUrl.toString()}${track.contentUrl}?token=$token');
}
}
/// riverpod状态 /// riverpod状态
@Riverpod(keepAlive: true) @Riverpod(keepAlive: true)
class AbsPlayer extends _$AbsPlayer { class AbsPlayer extends _$AbsPlayer {
@ -166,7 +284,7 @@ class CurrentBook extends _$CurrentBook {
@override @override
api.BookExpanded? build() { api.BookExpanded? build() {
listenSelf((previous, next) { listenSelf((previous, next) {
if (next == null) { if (previous == null && next == null) {
final activeLibraryItemId = AvailableHiveBoxes.basicBox final activeLibraryItemId = AvailableHiveBoxes.basicBox
.getAs<String>(CacheKey.activeLibraryItemId); .getAs<String>(CacheKey.activeLibraryItemId);
if (activeLibraryItemId != null) { if (activeLibraryItemId != null) {
@ -226,20 +344,3 @@ class CurrentChapter extends _$CurrentChapter {
Stream<Duration> positionChapter(Ref ref) { Stream<Duration> positionChapter(Ref ref) {
return ref.read(absPlayerProvider).positionInChapterStream; return ref.read(absPlayerProvider).positionInChapterStream;
} }
@riverpod
List<api.BookChapter> currentChapters(Ref ref) {
final book = ref.watch(currentBookProvider);
if (book == null) {
return [];
}
final currentChapter = ref.watch(currentChapterProvider);
if (currentChapter == null) {
return [];
}
final index = book.chapters.indexOf(currentChapter);
final total = book.chapters.length;
final start = index - 3 >= 0 ? index - 3 : 0;
final end = start + 20 <= total ? start + 20 : total;
return book.chapters.sublist(start, end);
}

View file

@ -57,6 +57,23 @@ final playerActiveProvider = AutoDisposeProvider<bool>.internal(
@Deprecated('Will be removed in 3.0. Use Ref instead') @Deprecated('Will be removed in 3.0. Use Ref instead')
// ignore: unused_element // ignore: unused_element
typedef PlayerActiveRef = AutoDisposeProviderRef<bool>; typedef PlayerActiveRef = AutoDisposeProviderRef<bool>;
String _$simpleAudioPlayerHash() => r'4da667e3b7047003edd594f8a76700afb963aceb';
/// See also [simpleAudioPlayer].
@ProviderFor(simpleAudioPlayer)
final simpleAudioPlayerProvider = Provider<AudioPlayer>.internal(
simpleAudioPlayer,
name: r'simpleAudioPlayerProvider',
debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product')
? null
: _$simpleAudioPlayerHash,
dependencies: null,
allTransitiveDependencies: null,
);
@Deprecated('Will be removed in 3.0. Use Ref instead')
// ignore: unused_element
typedef SimpleAudioPlayerRef = ProviderRef<AudioPlayer>;
String _$currentTimeHash() => r'3e7f99dbf48242a5fa0a4239a0f696535d0b4ac9'; String _$currentTimeHash() => r'3e7f99dbf48242a5fa0a4239a0f696535d0b4ac9';
/// Copied from Dart SDK /// Copied from Dart SDK
@ -225,24 +242,22 @@ final positionChapterProvider = AutoDisposeStreamProvider<Duration>.internal(
@Deprecated('Will be removed in 3.0. Use Ref instead') @Deprecated('Will be removed in 3.0. Use Ref instead')
// ignore: unused_element // ignore: unused_element
typedef PositionChapterRef = AutoDisposeStreamProviderRef<Duration>; typedef PositionChapterRef = AutoDisposeStreamProviderRef<Duration>;
String _$currentChaptersHash() => r'2d694aaa17f7eed8f97859d83e5b61f22966c35a'; String _$absAudioPlayerHash() => r'f595b5033eed9f4a4aa07c297c4a176955e6aab1';
/// See also [currentChapters]. /// See also [AbsAudioPlayer].
@ProviderFor(currentChapters) @ProviderFor(AbsAudioPlayer)
final currentChaptersProvider = final absAudioPlayerProvider =
AutoDisposeProvider<List<api.BookChapter>>.internal( NotifierProvider<AbsAudioPlayer, AudioPlayer>.internal(
currentChapters, AbsAudioPlayer.new,
name: r'currentChaptersProvider', name: r'absAudioPlayerProvider',
debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product') debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product')
? null ? null
: _$currentChaptersHash, : _$absAudioPlayerHash,
dependencies: null, dependencies: null,
allTransitiveDependencies: null, allTransitiveDependencies: null,
); );
@Deprecated('Will be removed in 3.0. Use Ref instead') typedef _$AbsAudioPlayer = Notifier<AudioPlayer>;
// ignore: unused_element
typedef CurrentChaptersRef = AutoDisposeProviderRef<List<api.BookChapter>>;
String _$absPlayerHash() => r'e682fea03793a0370cb143602980d5c1e37396c7'; String _$absPlayerHash() => r'e682fea03793a0370cb143602980d5c1e37396c7';
/// riverpod状态 /// riverpod状态
@ -275,7 +290,7 @@ final playerStateProvider =
); );
typedef _$PlayerState = AutoDisposeNotifier<core.AbsPlayerState>; typedef _$PlayerState = AutoDisposeNotifier<core.AbsPlayerState>;
String _$currentBookHash() => r'790af1f9502b12879fc22c900ed5e3572381ab1e'; String _$currentBookHash() => r'714d7701508b6186598e13bc38c57c3fe644ae90';
/// See also [CurrentBook]. /// See also [CurrentBook].
@ProviderFor(CurrentBook) @ProviderFor(CurrentBook)

View file

@ -51,7 +51,7 @@ class PlayerSettings with _$PlayerSettings {
ExpandedPlayerSettings expandedPlayerSettings, ExpandedPlayerSettings expandedPlayerSettings,
@Default(1) double preferredDefaultVolume, @Default(1) double preferredDefaultVolume,
@Default(1) double preferredDefaultSpeed, @Default(1) double preferredDefaultSpeed,
@Default([1, 1.25, 1.5, 1.75, 2]) List<double> speedOptions, @Default([0.5, 0.75, 1, 1.25, 1.5, 1.75, 2]) List<double> speedOptions,
@Default(0.05) double speedIncrement, @Default(0.05) double speedIncrement,
@Default(0.1) double minSpeed, @Default(0.1) double minSpeed,
@Default(4) double maxSpeed, @Default(4) double maxSpeed,

View file

@ -986,7 +986,15 @@ class _$PlayerSettingsImpl implements _PlayerSettings {
this.expandedPlayerSettings = const ExpandedPlayerSettings(), this.expandedPlayerSettings = const ExpandedPlayerSettings(),
this.preferredDefaultVolume = 1, this.preferredDefaultVolume = 1,
this.preferredDefaultSpeed = 1, this.preferredDefaultSpeed = 1,
final List<double> speedOptions = const [1, 1.25, 1.5, 1.75, 2], final List<double> speedOptions = const [
0.5,
0.75,
1,
1.25,
1.5,
1.75,
2
],
this.speedIncrement = 0.05, this.speedIncrement = 0.05,
this.minSpeed = 0.1, this.minSpeed = 0.1,
this.maxSpeed = 4, this.maxSpeed = 4,

View file

@ -99,7 +99,7 @@ _$PlayerSettingsImpl _$$PlayerSettingsImplFromJson(Map<String, dynamic> json) =>
speedOptions: (json['speedOptions'] as List<dynamic>?) speedOptions: (json['speedOptions'] as List<dynamic>?)
?.map((e) => (e as num).toDouble()) ?.map((e) => (e as num).toDouble())
.toList() ?? .toList() ??
const [1, 1.25, 1.5, 1.75, 2], const [0.5, 0.75, 1, 1.25, 1.5, 1.75, 2],
speedIncrement: (json['speedIncrement'] as num?)?.toDouble() ?? 0.05, speedIncrement: (json['speedIncrement'] as num?)?.toDouble() ?? 0.05,
minSpeed: (json['minSpeed'] as num?)?.toDouble() ?? 0.1, minSpeed: (json['minSpeed'] as num?)?.toDouble() ?? 0.1,
maxSpeed: (json['maxSpeed'] as num?)?.toDouble() ?? 4, maxSpeed: (json['maxSpeed'] as num?)?.toDouble() ?? 4,

View file

@ -6,13 +6,14 @@ import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:flutter_settings_ui/flutter_settings_ui.dart'; import 'package:flutter_settings_ui/flutter_settings_ui.dart';
import 'package:go_router/go_router.dart'; import 'package:go_router/go_router.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:vaani/generated/l10n.dart';
import 'package:vaani/router/router.dart';
import 'package:vaani/features/settings/app_settings_provider.dart'; import 'package:vaani/features/settings/app_settings_provider.dart';
import 'package:vaani/features/settings/models/app_settings.dart' as model; import 'package:vaani/features/settings/models/app_settings.dart' as model;
import 'package:vaani/features/settings/view/buttons.dart'; import 'package:vaani/features/settings/view/buttons.dart';
import 'package:vaani/features/settings/view/simple_settings_page.dart'; import 'package:vaani/features/settings/view/simple_settings_page.dart';
import 'package:vaani/features/settings/view/widgets/navigation_with_switch_tile.dart'; import 'package:vaani/features/settings/view/widgets/navigation_with_switch_tile.dart';
import 'package:vaani/generated/l10n.dart';
import 'package:vaani/router/router.dart';
import 'package:vaani/shared/widgets/custom_dropdown.dart';
class AppSettingsPage extends HookConsumerWidget { class AppSettingsPage extends HookConsumerWidget {
const AppSettingsPage({ const AppSettingsPage({
@ -40,22 +41,35 @@ class AppSettingsPage extends HookConsumerWidget {
SettingsTile( SettingsTile(
title: Text(S.of(context).language), title: Text(S.of(context).language),
leading: const Icon(Icons.language), leading: const Icon(Icons.language),
trailing: DropdownButton( trailing: CustomDropdown<String>(
value: appSettings.language, selected: appSettings.language,
items: S.delegate.supportedLocales.map((locale) { items: (f, cs) => S.delegate.supportedLocales.map((locale) {
return DropdownMenuItem( return locale.languageCode;
value: locale.languageCode,
child: Text(locales[locale.languageCode] ?? 'unknown'),
);
}).toList(), }).toList(),
onChanged: (value) { itemAsString: (item) => locales[item] ?? 'unknown',
onChanged: (value) async =>
ref.read(appSettingsProvider.notifier).update( ref.read(appSettingsProvider.notifier).update(
appSettings.copyWith( appSettings.copyWith(
language: value!, language: value!,
), ),
);
},
), ),
),
// trailing: DropdownButton(
// value: appSettings.language,
// items: S.delegate.supportedLocales.map((locale) {
// return DropdownMenuItem(
// value: locale.languageCode,
// child: Text(locales[locale.languageCode] ?? 'unknown'),
// );
// }).toList(),
// onChanged: (value) {
// ref.read(appSettingsProvider.notifier).update(
// appSettings.copyWith(
// language: value!,
// ),
// );
// },
// ),
description: Text(S.of(context).languageDescription), description: Text(S.of(context).languageDescription),
), ),
SettingsTile( SettingsTile(
@ -67,9 +81,9 @@ class AppSettingsPage extends HookConsumerWidget {
}, },
), ),
SettingsTile( SettingsTile(
title: Text('下载设置'), title: Text(S.of(context).downloadSettings),
leading: const Icon(Icons.download), leading: const Icon(Icons.download),
description: Text('自定义下载设置'), description: Text(S.of(context).downloadSettingsDescription),
onPressed: (context) { onPressed: (context) {
context.pushNamed(Routes.downloadSettings.name); context.pushNamed(Routes.downloadSettings.name);
}, },

View file

@ -3,11 +3,13 @@ import 'package:flutter/material.dart';
import 'package:flutter_hooks/flutter_hooks.dart'; import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:flutter_settings_ui/flutter_settings_ui.dart'; import 'package:flutter_settings_ui/flutter_settings_ui.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:vaani/constants/sizes.dart';
import 'package:vaani/generated/l10n.dart'; import 'package:vaani/generated/l10n.dart';
import 'package:vaani/features/settings/app_settings_provider.dart'; import 'package:vaani/features/settings/app_settings_provider.dart';
import 'package:vaani/features/settings/view/buttons.dart'; import 'package:vaani/features/settings/view/buttons.dart';
import 'package:vaani/features/settings/view/simple_settings_page.dart'; import 'package:vaani/features/settings/view/simple_settings_page.dart';
import 'package:vaani/shared/extensions/duration_format.dart'; import 'package:vaani/shared/extensions/duration_format.dart';
import 'package:vaani/shared/widgets/custom_dropdown.dart';
class PlayerSettingsPage extends HookConsumerWidget { class PlayerSettingsPage extends HookConsumerWidget {
const PlayerSettingsPage({ const PlayerSettingsPage({
@ -25,8 +27,8 @@ class PlayerSettingsPage extends HookConsumerWidget {
sections: [ sections: [
SettingsSection( SettingsSection(
margin: const EdgeInsetsDirectional.symmetric( margin: const EdgeInsetsDirectional.symmetric(
horizontal: 16.0, horizontal: AppElementSizes.paddingLarge,
vertical: 8.0, vertical: AppElementSizes.paddingRegular,
), ),
tiles: [ tiles: [
// preferred settings for every book // preferred settings for every book
@ -49,28 +51,27 @@ class PlayerSettingsPage extends HookConsumerWidget {
// preferred default speed // preferred default speed
SettingsTile( SettingsTile(
title: Text(S.of(context).playerSettingsSpeedDefault), title: Text(S.of(context).playerSettingsSpeedDefault),
trailing: Text( // trailing: Text(
'${playerSettings.preferredDefaultSpeed}x', // '${playerSettings.preferredDefaultSpeed}x',
style: // style:
TextStyle(color: primaryColor, fontWeight: FontWeight.bold), // TextStyle(color: primaryColor, fontWeight: FontWeight.bold),
), // ),
leading: const Icon(Icons.speed), trailing: CustomDropdown<double>(
onPressed: (context) async { selected: playerSettings.preferredDefaultSpeed,
final newSpeed = await showDialog( items: (f, cs) => playerSettings.speedOptions,
context: context, itemAsString: (item) => '${item}x',
builder: (context) => SpeedPicker( onChanged: (value) {
initialValue: playerSettings.preferredDefaultSpeed, if (value != null) {
),
);
if (newSpeed != null) {
ref.read(appSettingsProvider.notifier).update( ref.read(appSettingsProvider.notifier).update(
appSettings.copyWith.playerSettings( appSettings.copyWith.playerSettings(
preferredDefaultSpeed: newSpeed, preferredDefaultSpeed: value,
), ),
); );
} }
}, },
), ),
leading: const Icon(Icons.speed),
),
// preferred speed options // preferred speed options
SettingsTile( SettingsTile(
title: Text(S.of(context).playerSettingsSpeedOptions), title: Text(S.of(context).playerSettingsSpeedOptions),

View file

@ -54,10 +54,8 @@ class MessageLookup extends MessageLookupByLibrary {
"accountDeleteServer": MessageLookupByLibrary.simpleMessage( "accountDeleteServer": MessageLookupByLibrary.simpleMessage(
"Delete Server", "Delete Server",
), ),
"accountInvalidURL": "accountInvalidURL": MessageLookupByLibrary.simpleMessage("Invalid URL"),
MessageLookupByLibrary.simpleMessage("Invalid URL"), "accountManage": MessageLookupByLibrary.simpleMessage("Manage Accounts"),
"accountManage":
MessageLookupByLibrary.simpleMessage("Manage Accounts"),
"accountRegisteredServers": MessageLookupByLibrary.simpleMessage( "accountRegisteredServers": MessageLookupByLibrary.simpleMessage(
"Registered Servers", "Registered Servers",
), ),
@ -96,8 +94,7 @@ class MessageLookup extends MessageLookupByLibrary {
"autoTurnOnTimerAlways": MessageLookupByLibrary.simpleMessage( "autoTurnOnTimerAlways": MessageLookupByLibrary.simpleMessage(
"Always Auto Turn On Timer", "Always Auto Turn On Timer",
), ),
"autoTurnOnTimerAlwaysDescription": "autoTurnOnTimerAlwaysDescription": MessageLookupByLibrary.simpleMessage(
MessageLookupByLibrary.simpleMessage(
"Always turn on the sleep timer, no matter what", "Always turn on the sleep timer, no matter what",
), ),
"autoTurnOnTimerDescription": MessageLookupByLibrary.simpleMessage( "autoTurnOnTimerDescription": MessageLookupByLibrary.simpleMessage(
@ -125,11 +122,9 @@ class MessageLookup extends MessageLookupByLibrary {
"bookAuthors": MessageLookupByLibrary.simpleMessage("Authors"), "bookAuthors": MessageLookupByLibrary.simpleMessage("Authors"),
"bookDownloads": MessageLookupByLibrary.simpleMessage("Downloads"), "bookDownloads": MessageLookupByLibrary.simpleMessage("Downloads"),
"bookGenres": MessageLookupByLibrary.simpleMessage("Genres"), "bookGenres": MessageLookupByLibrary.simpleMessage("Genres"),
"bookMetadataAbridged": "bookMetadataAbridged": MessageLookupByLibrary.simpleMessage("Abridged"),
MessageLookupByLibrary.simpleMessage("Abridged"),
"bookMetadataLength": MessageLookupByLibrary.simpleMessage("Length"), "bookMetadataLength": MessageLookupByLibrary.simpleMessage("Length"),
"bookMetadataPublished": "bookMetadataPublished": MessageLookupByLibrary.simpleMessage("Published"),
MessageLookupByLibrary.simpleMessage("Published"),
"bookMetadataUnabridged": MessageLookupByLibrary.simpleMessage( "bookMetadataUnabridged": MessageLookupByLibrary.simpleMessage(
"Unabridged", "Unabridged",
), ),
@ -163,16 +158,20 @@ class MessageLookup extends MessageLookupByLibrary {
"delete": MessageLookupByLibrary.simpleMessage("Delete"), "delete": MessageLookupByLibrary.simpleMessage("Delete"),
"deleteDialog": m2, "deleteDialog": m2,
"deleted": m3, "deleted": m3,
"downloadSettings": MessageLookupByLibrary.simpleMessage(
"Download Settings",
),
"downloadSettingsDescription": MessageLookupByLibrary.simpleMessage(
"Customize download settings",
),
"erArmedText": MessageLookupByLibrary.simpleMessage("Release ready"), "erArmedText": MessageLookupByLibrary.simpleMessage("Release ready"),
"erDragText": MessageLookupByLibrary.simpleMessage("Pull to refresh"), "erDragText": MessageLookupByLibrary.simpleMessage("Pull to refresh"),
"erDragTextUp": MessageLookupByLibrary.simpleMessage("Pull to refresh"), "erDragTextUp": MessageLookupByLibrary.simpleMessage("Pull to refresh"),
"erFailedText": MessageLookupByLibrary.simpleMessage("Failed"), "erFailedText": MessageLookupByLibrary.simpleMessage("Failed"),
"erMessageText": "erMessageText": MessageLookupByLibrary.simpleMessage("Last updated at %T"),
MessageLookupByLibrary.simpleMessage("Last updated at %T"),
"erNoMoreText": MessageLookupByLibrary.simpleMessage("No more"), "erNoMoreText": MessageLookupByLibrary.simpleMessage("No more"),
"erProcessedText": MessageLookupByLibrary.simpleMessage("Succeeded"), "erProcessedText": MessageLookupByLibrary.simpleMessage("Succeeded"),
"erProcessingText": "erProcessingText": MessageLookupByLibrary.simpleMessage("Refreshing..."),
MessageLookupByLibrary.simpleMessage("Refreshing..."),
"erReadyText": MessageLookupByLibrary.simpleMessage("Refreshing..."), "erReadyText": MessageLookupByLibrary.simpleMessage("Refreshing..."),
"explore": MessageLookupByLibrary.simpleMessage("explore"), "explore": MessageLookupByLibrary.simpleMessage("explore"),
"exploreHint": MessageLookupByLibrary.simpleMessage( "exploreHint": MessageLookupByLibrary.simpleMessage(
@ -194,13 +193,11 @@ class MessageLookup extends MessageLookupByLibrary {
"homeBookContinueSeries": MessageLookupByLibrary.simpleMessage( "homeBookContinueSeries": MessageLookupByLibrary.simpleMessage(
"Continue Series", "Continue Series",
), ),
"homeBookContinueSeriesDescription": "homeBookContinueSeriesDescription": MessageLookupByLibrary.simpleMessage(
MessageLookupByLibrary.simpleMessage(
"Show play button for books in continue series shelf", "Show play button for books in continue series shelf",
), ),
"homeBookDiscover": MessageLookupByLibrary.simpleMessage("Discover"), "homeBookDiscover": MessageLookupByLibrary.simpleMessage("Discover"),
"homeBookListenAgain": "homeBookListenAgain": MessageLookupByLibrary.simpleMessage("Listen Again"),
MessageLookupByLibrary.simpleMessage("Listen Again"),
"homeBookListenAgainDescription": MessageLookupByLibrary.simpleMessage( "homeBookListenAgainDescription": MessageLookupByLibrary.simpleMessage(
"Show play button for all books in listen again shelf", "Show play button for all books in listen again shelf",
), ),
@ -210,8 +207,7 @@ class MessageLookup extends MessageLookupByLibrary {
"homeBookRecentlyAdded": MessageLookupByLibrary.simpleMessage( "homeBookRecentlyAdded": MessageLookupByLibrary.simpleMessage(
"Recently Added", "Recently Added",
), ),
"homeBookRecommended": "homeBookRecommended": MessageLookupByLibrary.simpleMessage("Recommended"),
MessageLookupByLibrary.simpleMessage("Recommended"),
"homeContinueListening": MessageLookupByLibrary.simpleMessage( "homeContinueListening": MessageLookupByLibrary.simpleMessage(
"Continue Listening", "Continue Listening",
), ),
@ -284,8 +280,7 @@ class MessageLookup extends MessageLookupByLibrary {
"nmpSettingsMediaControls": MessageLookupByLibrary.simpleMessage( "nmpSettingsMediaControls": MessageLookupByLibrary.simpleMessage(
"Media Controls", "Media Controls",
), ),
"nmpSettingsMediaControlsDescription": "nmpSettingsMediaControlsDescription": MessageLookupByLibrary.simpleMessage(
MessageLookupByLibrary.simpleMessage(
"Select the media controls to display", "Select the media controls to display",
), ),
"nmpSettingsSelectOne": MessageLookupByLibrary.simpleMessage( "nmpSettingsSelectOne": MessageLookupByLibrary.simpleMessage(
@ -304,32 +299,27 @@ class MessageLookup extends MessageLookupByLibrary {
"nmpSettingsSubTitleDescription": MessageLookupByLibrary.simpleMessage( "nmpSettingsSubTitleDescription": MessageLookupByLibrary.simpleMessage(
"The subtitle of the notification\n", "The subtitle of the notification\n",
), ),
"nmpSettingsTitle": "nmpSettingsTitle": MessageLookupByLibrary.simpleMessage("Primary Title"),
MessageLookupByLibrary.simpleMessage("Primary Title"),
"nmpSettingsTitleDescription": MessageLookupByLibrary.simpleMessage( "nmpSettingsTitleDescription": MessageLookupByLibrary.simpleMessage(
"The title of the notification\n", "The title of the notification\n",
), ),
"no": MessageLookupByLibrary.simpleMessage("No"), "no": MessageLookupByLibrary.simpleMessage("No"),
"notImplemented": "notImplemented": MessageLookupByLibrary.simpleMessage("Not implemented"),
MessageLookupByLibrary.simpleMessage("Not implemented"),
"notificationMediaPlayer": MessageLookupByLibrary.simpleMessage( "notificationMediaPlayer": MessageLookupByLibrary.simpleMessage(
"Notification Media Player", "Notification Media Player",
), ),
"notificationMediaPlayerDescription": "notificationMediaPlayerDescription": MessageLookupByLibrary.simpleMessage(
MessageLookupByLibrary.simpleMessage(
"Customize the media player in notifications", "Customize the media player in notifications",
), ),
"ok": MessageLookupByLibrary.simpleMessage("OK"), "ok": MessageLookupByLibrary.simpleMessage("OK"),
"pause": MessageLookupByLibrary.simpleMessage("Pause"), "pause": MessageLookupByLibrary.simpleMessage("Pause"),
"play": MessageLookupByLibrary.simpleMessage("Play"), "play": MessageLookupByLibrary.simpleMessage("Play"),
"playerSettings": "playerSettings": MessageLookupByLibrary.simpleMessage("Player Settings"),
MessageLookupByLibrary.simpleMessage("Player Settings"),
"playerSettingsCompleteTime": MessageLookupByLibrary.simpleMessage( "playerSettingsCompleteTime": MessageLookupByLibrary.simpleMessage(
"Mark Complete When Time Left", "Mark Complete When Time Left",
), ),
"playerSettingsCompleteTimeDescriptionHead": "playerSettingsCompleteTimeDescriptionHead":
MessageLookupByLibrary.simpleMessage( MessageLookupByLibrary.simpleMessage("Mark complete when less than "),
"Mark complete when less than "),
"playerSettingsCompleteTimeDescriptionTail": "playerSettingsCompleteTimeDescriptionTail":
MessageLookupByLibrary.simpleMessage(" left in the book"), MessageLookupByLibrary.simpleMessage(" left in the book"),
"playerSettingsDescription": MessageLookupByLibrary.simpleMessage( "playerSettingsDescription": MessageLookupByLibrary.simpleMessage(
@ -344,8 +334,7 @@ class MessageLookup extends MessageLookupByLibrary {
MessageLookupByLibrary.simpleMessage( MessageLookupByLibrary.simpleMessage(
"Show the progress of the current chapter in the player", "Show the progress of the current chapter in the player",
), ),
"playerSettingsDisplayTotalProgress": "playerSettingsDisplayTotalProgress": MessageLookupByLibrary.simpleMessage(
MessageLookupByLibrary.simpleMessage(
"Show Total Progress", "Show Total Progress",
), ),
"playerSettingsDisplayTotalProgressDescription": "playerSettingsDisplayTotalProgressDescription":
@ -374,8 +363,7 @@ class MessageLookup extends MessageLookupByLibrary {
), ),
"playerSettingsPlaybackReportingMinimumDescriptionTail": "playerSettingsPlaybackReportingMinimumDescriptionTail":
MessageLookupByLibrary.simpleMessage("of the book"), MessageLookupByLibrary.simpleMessage("of the book"),
"playerSettingsRememberForEveryBook": "playerSettingsRememberForEveryBook": MessageLookupByLibrary.simpleMessage(
MessageLookupByLibrary.simpleMessage(
"Remember Player Settings for Every Book", "Remember Player Settings for Every Book",
), ),
"playerSettingsRememberForEveryBookDescription": "playerSettingsRememberForEveryBookDescription":
@ -389,17 +377,14 @@ class MessageLookup extends MessageLookupByLibrary {
"playerSettingsSpeedOptions": MessageLookupByLibrary.simpleMessage( "playerSettingsSpeedOptions": MessageLookupByLibrary.simpleMessage(
"Speed Options", "Speed Options",
), ),
"playerSettingsSpeedOptionsSelect": "playerSettingsSpeedOptionsSelect": MessageLookupByLibrary.simpleMessage(
MessageLookupByLibrary.simpleMessage(
"Select Speed Options", "Select Speed Options",
), ),
"playerSettingsSpeedOptionsSelectAdd": "playerSettingsSpeedOptionsSelectAdd": MessageLookupByLibrary.simpleMessage(
MessageLookupByLibrary.simpleMessage(
"Add Speed Option", "Add Speed Option",
), ),
"playerSettingsSpeedOptionsSelectAddHelper": "playerSettingsSpeedOptionsSelectAddHelper":
MessageLookupByLibrary.simpleMessage( MessageLookupByLibrary.simpleMessage("Enter a new speed option to add"),
"Enter a new speed option to add"),
"playerSettingsSpeedSelect": MessageLookupByLibrary.simpleMessage( "playerSettingsSpeedSelect": MessageLookupByLibrary.simpleMessage(
"Select Speed", "Select Speed",
), ),
@ -447,8 +432,7 @@ class MessageLookup extends MessageLookupByLibrary {
"shakeActivationThreshold": MessageLookupByLibrary.simpleMessage( "shakeActivationThreshold": MessageLookupByLibrary.simpleMessage(
"Shake Activation Threshold", "Shake Activation Threshold",
), ),
"shakeActivationThresholdDescription": "shakeActivationThresholdDescription": MessageLookupByLibrary.simpleMessage(
MessageLookupByLibrary.simpleMessage(
"The higher the threshold, the harder you need to shake", "The higher the threshold, the harder you need to shake",
), ),
"shakeDetector": MessageLookupByLibrary.simpleMessage("Shake Detector"), "shakeDetector": MessageLookupByLibrary.simpleMessage("Shake Detector"),
@ -486,8 +470,7 @@ class MessageLookup extends MessageLookupByLibrary {
"themeModeHighContrast": MessageLookupByLibrary.simpleMessage( "themeModeHighContrast": MessageLookupByLibrary.simpleMessage(
"High Contrast Mode", "High Contrast Mode",
), ),
"themeModeHighContrastDescription": "themeModeHighContrastDescription": MessageLookupByLibrary.simpleMessage(
MessageLookupByLibrary.simpleMessage(
"Increase the contrast between the background and the text", "Increase the contrast between the background and the text",
), ),
"themeModeLight": MessageLookupByLibrary.simpleMessage("Light"), "themeModeLight": MessageLookupByLibrary.simpleMessage("Light"),
@ -502,8 +485,7 @@ class MessageLookup extends MessageLookupByLibrary {
"themeSettingsColorsBook": MessageLookupByLibrary.simpleMessage( "themeSettingsColorsBook": MessageLookupByLibrary.simpleMessage(
"Adaptive Theme on Item Page", "Adaptive Theme on Item Page",
), ),
"themeSettingsColorsBookDescription": "themeSettingsColorsBookDescription": MessageLookupByLibrary.simpleMessage(
MessageLookupByLibrary.simpleMessage(
"Get fancy with the colors on the item page at the cost of some performance", "Get fancy with the colors on the item page at the cost of some performance",
), ),
"themeSettingsColorsCurrent": MessageLookupByLibrary.simpleMessage( "themeSettingsColorsCurrent": MessageLookupByLibrary.simpleMessage(

View file

@ -50,8 +50,7 @@ class MessageLookup extends MessageLookupByLibrary {
"accountDeleteServer": MessageLookupByLibrary.simpleMessage("删除服务器"), "accountDeleteServer": MessageLookupByLibrary.simpleMessage("删除服务器"),
"accountInvalidURL": MessageLookupByLibrary.simpleMessage("无效网址"), "accountInvalidURL": MessageLookupByLibrary.simpleMessage("无效网址"),
"accountManage": MessageLookupByLibrary.simpleMessage("帐户管理"), "accountManage": MessageLookupByLibrary.simpleMessage("帐户管理"),
"accountRegisteredServers": "accountRegisteredServers": MessageLookupByLibrary.simpleMessage("已注册服务器"),
MessageLookupByLibrary.simpleMessage("已注册服务器"),
"accountRemoveServerAndUsers": MessageLookupByLibrary.simpleMessage( "accountRemoveServerAndUsers": MessageLookupByLibrary.simpleMessage(
"删除服务器和用户", "删除服务器和用户",
), ),
@ -61,8 +60,7 @@ class MessageLookup extends MessageLookupByLibrary {
"accountRemoveServerAndUsersTail": MessageLookupByLibrary.simpleMessage( "accountRemoveServerAndUsersTail": MessageLookupByLibrary.simpleMessage(
" 以及该应用程序中所有用户的登录信息。", " 以及该应用程序中所有用户的登录信息。",
), ),
"accountRemoveUserLogin": "accountRemoveUserLogin": MessageLookupByLibrary.simpleMessage("删除用户登录"),
MessageLookupByLibrary.simpleMessage("删除用户登录"),
"accountRemoveUserLoginHead": MessageLookupByLibrary.simpleMessage( "accountRemoveUserLoginHead": MessageLookupByLibrary.simpleMessage(
"这将删除用户 ", "这将删除用户 ",
), ),
@ -74,15 +72,11 @@ class MessageLookup extends MessageLookupByLibrary {
"accountUsersCount": m1, "accountUsersCount": m1,
"appSettings": MessageLookupByLibrary.simpleMessage("应用设置"), "appSettings": MessageLookupByLibrary.simpleMessage("应用设置"),
"appearance": MessageLookupByLibrary.simpleMessage("外观"), "appearance": MessageLookupByLibrary.simpleMessage("外观"),
"autoSleepTimerSettings": "autoSleepTimerSettings": MessageLookupByLibrary.simpleMessage("自动睡眠定时器设置"),
MessageLookupByLibrary.simpleMessage("自动睡眠定时器设置"), "autoTurnOnSleepTimer": MessageLookupByLibrary.simpleMessage("自动开启睡眠定时器"),
"autoTurnOnSleepTimer":
MessageLookupByLibrary.simpleMessage("自动开启睡眠定时器"),
"autoTurnOnTimer": MessageLookupByLibrary.simpleMessage("自动开启定时器"), "autoTurnOnTimer": MessageLookupByLibrary.simpleMessage("自动开启定时器"),
"autoTurnOnTimerAlways": "autoTurnOnTimerAlways": MessageLookupByLibrary.simpleMessage("始终自动开启定时器"),
MessageLookupByLibrary.simpleMessage("始终自动开启定时器"), "autoTurnOnTimerAlwaysDescription": MessageLookupByLibrary.simpleMessage(
"autoTurnOnTimerAlwaysDescription":
MessageLookupByLibrary.simpleMessage(
"总是打开睡眠定时器", "总是打开睡眠定时器",
), ),
"autoTurnOnTimerDescription": MessageLookupByLibrary.simpleMessage( "autoTurnOnTimerDescription": MessageLookupByLibrary.simpleMessage(
@ -124,11 +118,14 @@ class MessageLookup extends MessageLookupByLibrary {
"copyToClipboardDescription": MessageLookupByLibrary.simpleMessage( "copyToClipboardDescription": MessageLookupByLibrary.simpleMessage(
"将应用程序设置复制到剪贴板", "将应用程序设置复制到剪贴板",
), ),
"copyToClipboardToast": "copyToClipboardToast": MessageLookupByLibrary.simpleMessage("设置已复制到剪贴板"),
MessageLookupByLibrary.simpleMessage("设置已复制到剪贴板"),
"delete": MessageLookupByLibrary.simpleMessage("删除"), "delete": MessageLookupByLibrary.simpleMessage("删除"),
"deleteDialog": m2, "deleteDialog": m2,
"deleted": m3, "deleted": m3,
"downloadSettings": MessageLookupByLibrary.simpleMessage("下载设置"),
"downloadSettingsDescription": MessageLookupByLibrary.simpleMessage(
"自定义下载设置",
),
"erArmedText": MessageLookupByLibrary.simpleMessage("准备就绪"), "erArmedText": MessageLookupByLibrary.simpleMessage("准备就绪"),
"erDragText": MessageLookupByLibrary.simpleMessage("下拉刷新"), "erDragText": MessageLookupByLibrary.simpleMessage("下拉刷新"),
"erDragTextUp": MessageLookupByLibrary.simpleMessage("上拉加载"), "erDragTextUp": MessageLookupByLibrary.simpleMessage("上拉加载"),
@ -144,13 +141,11 @@ class MessageLookup extends MessageLookupByLibrary {
"general": MessageLookupByLibrary.simpleMessage("通用"), "general": MessageLookupByLibrary.simpleMessage("通用"),
"help": MessageLookupByLibrary.simpleMessage("Help"), "help": MessageLookupByLibrary.simpleMessage("Help"),
"home": MessageLookupByLibrary.simpleMessage("首页"), "home": MessageLookupByLibrary.simpleMessage("首页"),
"homeBookContinueListening": "homeBookContinueListening": MessageLookupByLibrary.simpleMessage("继续收听"),
MessageLookupByLibrary.simpleMessage("继续收听"),
"homeBookContinueListeningDescription": "homeBookContinueListeningDescription":
MessageLookupByLibrary.simpleMessage("继续收听书架上显示播放按钮"), MessageLookupByLibrary.simpleMessage("继续收听书架上显示播放按钮"),
"homeBookContinueSeries": MessageLookupByLibrary.simpleMessage("继续系列"), "homeBookContinueSeries": MessageLookupByLibrary.simpleMessage("继续系列"),
"homeBookContinueSeriesDescription": "homeBookContinueSeriesDescription": MessageLookupByLibrary.simpleMessage(
MessageLookupByLibrary.simpleMessage(
"继续系列书架上显示播放按钮", "继续系列书架上显示播放按钮",
), ),
"homeBookDiscover": MessageLookupByLibrary.simpleMessage("发现"), "homeBookDiscover": MessageLookupByLibrary.simpleMessage("发现"),
@ -172,8 +167,7 @@ class MessageLookup extends MessageLookupByLibrary {
), ),
"homePageSettingsOtherShelvesDescription": "homePageSettingsOtherShelvesDescription":
MessageLookupByLibrary.simpleMessage("显示所有剩余书架上所有书籍的播放按钮"), MessageLookupByLibrary.simpleMessage("显示所有剩余书架上所有书籍的播放按钮"),
"homePageSettingsQuickPlay": "homePageSettingsQuickPlay": MessageLookupByLibrary.simpleMessage("继续播放"),
MessageLookupByLibrary.simpleMessage("继续播放"),
"homeStartListening": MessageLookupByLibrary.simpleMessage("开始收听"), "homeStartListening": MessageLookupByLibrary.simpleMessage("开始收听"),
"language": MessageLookupByLibrary.simpleMessage("语言"), "language": MessageLookupByLibrary.simpleMessage("语言"),
"languageDescription": MessageLookupByLibrary.simpleMessage("语言切换"), "languageDescription": MessageLookupByLibrary.simpleMessage("语言切换"),
@ -190,8 +184,7 @@ class MessageLookup extends MessageLookupByLibrary {
"loginOpenID": MessageLookupByLibrary.simpleMessage("OpenID"), "loginOpenID": MessageLookupByLibrary.simpleMessage("OpenID"),
"loginPassword": MessageLookupByLibrary.simpleMessage("密码"), "loginPassword": MessageLookupByLibrary.simpleMessage("密码"),
"loginServerClick": MessageLookupByLibrary.simpleMessage("单击此处"), "loginServerClick": MessageLookupByLibrary.simpleMessage("单击此处"),
"loginServerConnected": "loginServerConnected": MessageLookupByLibrary.simpleMessage("服务器已连接,请登录"),
MessageLookupByLibrary.simpleMessage("服务器已连接,请登录"),
"loginServerNo": MessageLookupByLibrary.simpleMessage("没有服务器? "), "loginServerNo": MessageLookupByLibrary.simpleMessage("没有服务器? "),
"loginServerNoConnected": MessageLookupByLibrary.simpleMessage( "loginServerNoConnected": MessageLookupByLibrary.simpleMessage(
"请输入您的AudiobookShelf服务器的URL", "请输入您的AudiobookShelf服务器的URL",
@ -204,10 +197,8 @@ class MessageLookup extends MessageLookupByLibrary {
"logs": MessageLookupByLibrary.simpleMessage("日志"), "logs": MessageLookupByLibrary.simpleMessage("日志"),
"nmpSettingsBackward": MessageLookupByLibrary.simpleMessage("快退间隔"), "nmpSettingsBackward": MessageLookupByLibrary.simpleMessage("快退间隔"),
"nmpSettingsForward": MessageLookupByLibrary.simpleMessage("快进间隔"), "nmpSettingsForward": MessageLookupByLibrary.simpleMessage("快进间隔"),
"nmpSettingsMediaControls": "nmpSettingsMediaControls": MessageLookupByLibrary.simpleMessage("媒体控制"),
MessageLookupByLibrary.simpleMessage("媒体控制"), "nmpSettingsMediaControlsDescription": MessageLookupByLibrary.simpleMessage(
"nmpSettingsMediaControlsDescription":
MessageLookupByLibrary.simpleMessage(
"选择要显示的媒体控件", "选择要显示的媒体控件",
), ),
"nmpSettingsSelectOne": MessageLookupByLibrary.simpleMessage( "nmpSettingsSelectOne": MessageLookupByLibrary.simpleMessage(
@ -228,10 +219,8 @@ class MessageLookup extends MessageLookupByLibrary {
), ),
"no": MessageLookupByLibrary.simpleMessage(""), "no": MessageLookupByLibrary.simpleMessage(""),
"notImplemented": MessageLookupByLibrary.simpleMessage("未实现"), "notImplemented": MessageLookupByLibrary.simpleMessage("未实现"),
"notificationMediaPlayer": "notificationMediaPlayer": MessageLookupByLibrary.simpleMessage("通知媒体播放器"),
MessageLookupByLibrary.simpleMessage("通知媒体播放器"), "notificationMediaPlayerDescription": MessageLookupByLibrary.simpleMessage(
"notificationMediaPlayerDescription":
MessageLookupByLibrary.simpleMessage(
"在通知中自定义媒体播放器", "在通知中自定义媒体播放器",
), ),
"ok": MessageLookupByLibrary.simpleMessage("确定"), "ok": MessageLookupByLibrary.simpleMessage("确定"),
@ -253,8 +242,7 @@ class MessageLookup extends MessageLookupByLibrary {
MessageLookupByLibrary.simpleMessage("显示章节进度"), MessageLookupByLibrary.simpleMessage("显示章节进度"),
"playerSettingsDisplayChapterProgressDescription": "playerSettingsDisplayChapterProgressDescription":
MessageLookupByLibrary.simpleMessage("在播放器中显示当前章节的进度"), MessageLookupByLibrary.simpleMessage("在播放器中显示当前章节的进度"),
"playerSettingsDisplayTotalProgress": "playerSettingsDisplayTotalProgress": MessageLookupByLibrary.simpleMessage(
MessageLookupByLibrary.simpleMessage(
"显示总进度", "显示总进度",
), ),
"playerSettingsDisplayTotalProgressDescription": "playerSettingsDisplayTotalProgressDescription":
@ -277,8 +265,7 @@ class MessageLookup extends MessageLookupByLibrary {
MessageLookupByLibrary.simpleMessage("不要报告本书前 "), MessageLookupByLibrary.simpleMessage("不要报告本书前 "),
"playerSettingsPlaybackReportingMinimumDescriptionTail": "playerSettingsPlaybackReportingMinimumDescriptionTail":
MessageLookupByLibrary.simpleMessage(" 的播放"), MessageLookupByLibrary.simpleMessage(" 的播放"),
"playerSettingsRememberForEveryBook": "playerSettingsRememberForEveryBook": MessageLookupByLibrary.simpleMessage(
MessageLookupByLibrary.simpleMessage(
"记住每本书的播放器设置", "记住每本书的播放器设置",
), ),
"playerSettingsRememberForEveryBookDescription": "playerSettingsRememberForEveryBookDescription":
@ -290,18 +277,15 @@ class MessageLookup extends MessageLookupByLibrary {
"playerSettingsSpeedOptions": MessageLookupByLibrary.simpleMessage( "playerSettingsSpeedOptions": MessageLookupByLibrary.simpleMessage(
"播放速度选项", "播放速度选项",
), ),
"playerSettingsSpeedOptionsSelect": "playerSettingsSpeedOptionsSelect": MessageLookupByLibrary.simpleMessage(
MessageLookupByLibrary.simpleMessage(
"播放速度选项", "播放速度选项",
), ),
"playerSettingsSpeedOptionsSelectAdd": "playerSettingsSpeedOptionsSelectAdd": MessageLookupByLibrary.simpleMessage(
MessageLookupByLibrary.simpleMessage(
"添加一个速度选项", "添加一个速度选项",
), ),
"playerSettingsSpeedOptionsSelectAddHelper": "playerSettingsSpeedOptionsSelectAddHelper":
MessageLookupByLibrary.simpleMessage("输入一个新的速度选项"), MessageLookupByLibrary.simpleMessage("输入一个新的速度选项"),
"playerSettingsSpeedSelect": "playerSettingsSpeedSelect": MessageLookupByLibrary.simpleMessage("选择播放速度"),
MessageLookupByLibrary.simpleMessage("选择播放速度"),
"playerSettingsSpeedSelectHelper": MessageLookupByLibrary.simpleMessage( "playerSettingsSpeedSelectHelper": MessageLookupByLibrary.simpleMessage(
"输入默认的播放速度", "输入默认的播放速度",
), ),
@ -322,10 +306,8 @@ class MessageLookup extends MessageLookupByLibrary {
"restoreBackupHint": MessageLookupByLibrary.simpleMessage("将备份粘贴到此处"), "restoreBackupHint": MessageLookupByLibrary.simpleMessage("将备份粘贴到此处"),
"restoreBackupInvalid": MessageLookupByLibrary.simpleMessage("无效备份"), "restoreBackupInvalid": MessageLookupByLibrary.simpleMessage("无效备份"),
"restoreBackupSuccess": MessageLookupByLibrary.simpleMessage("设置已恢复"), "restoreBackupSuccess": MessageLookupByLibrary.simpleMessage("设置已恢复"),
"restoreBackupValidator": "restoreBackupValidator": MessageLookupByLibrary.simpleMessage("请将备份粘贴到此处"),
MessageLookupByLibrary.simpleMessage("请将备份粘贴到此处"), "restoreDescription": MessageLookupByLibrary.simpleMessage("从备份中还原应用程序设置"),
"restoreDescription":
MessageLookupByLibrary.simpleMessage("从备份中还原应用程序设置"),
"resume": MessageLookupByLibrary.simpleMessage("继续"), "resume": MessageLookupByLibrary.simpleMessage("继续"),
"retry": MessageLookupByLibrary.simpleMessage("重试"), "retry": MessageLookupByLibrary.simpleMessage("重试"),
"settings": MessageLookupByLibrary.simpleMessage("设置"), "settings": MessageLookupByLibrary.simpleMessage("设置"),
@ -333,10 +315,8 @@ class MessageLookup extends MessageLookupByLibrary {
"shakeActionDescription": MessageLookupByLibrary.simpleMessage( "shakeActionDescription": MessageLookupByLibrary.simpleMessage(
"检测到抖动时要执行的操作", "检测到抖动时要执行的操作",
), ),
"shakeActivationThreshold": "shakeActivationThreshold": MessageLookupByLibrary.simpleMessage("抖动激活阈值"),
MessageLookupByLibrary.simpleMessage("抖动激活阈值"), "shakeActivationThresholdDescription": MessageLookupByLibrary.simpleMessage(
"shakeActivationThresholdDescription":
MessageLookupByLibrary.simpleMessage(
"门槛越高,你就越难摇晃", "门槛越高,你就越难摇晃",
), ),
"shakeDetector": MessageLookupByLibrary.simpleMessage("抖动检测器"), "shakeDetector": MessageLookupByLibrary.simpleMessage("抖动检测器"),
@ -347,8 +327,7 @@ class MessageLookup extends MessageLookupByLibrary {
"shakeDetectorEnableDescription": MessageLookupByLibrary.simpleMessage( "shakeDetectorEnableDescription": MessageLookupByLibrary.simpleMessage(
"启用抖动检测以执行各种操作", "启用抖动检测以执行各种操作",
), ),
"shakeDetectorSettings": "shakeDetectorSettings": MessageLookupByLibrary.simpleMessage("抖动检测器设置"),
MessageLookupByLibrary.simpleMessage("抖动检测器设置"),
"shakeFeedback": MessageLookupByLibrary.simpleMessage("抖动反馈"), "shakeFeedback": MessageLookupByLibrary.simpleMessage("抖动反馈"),
"shakeFeedbackDescription": MessageLookupByLibrary.simpleMessage( "shakeFeedbackDescription": MessageLookupByLibrary.simpleMessage(
"检测到抖动时给出的反馈", "检测到抖动时给出的反馈",
@ -363,21 +342,18 @@ class MessageLookup extends MessageLookupByLibrary {
"themeMode": MessageLookupByLibrary.simpleMessage("主题模式"), "themeMode": MessageLookupByLibrary.simpleMessage("主题模式"),
"themeModeDark": MessageLookupByLibrary.simpleMessage("深色"), "themeModeDark": MessageLookupByLibrary.simpleMessage("深色"),
"themeModeHighContrast": MessageLookupByLibrary.simpleMessage("高对比度模式"), "themeModeHighContrast": MessageLookupByLibrary.simpleMessage("高对比度模式"),
"themeModeHighContrastDescription": "themeModeHighContrastDescription": MessageLookupByLibrary.simpleMessage(
MessageLookupByLibrary.simpleMessage(
"增加背景和文本之间的对比度", "增加背景和文本之间的对比度",
), ),
"themeModeLight": MessageLookupByLibrary.simpleMessage("浅色"), "themeModeLight": MessageLookupByLibrary.simpleMessage("浅色"),
"themeModeSystem": MessageLookupByLibrary.simpleMessage("跟随系统"), "themeModeSystem": MessageLookupByLibrary.simpleMessage("跟随系统"),
"themeSettings": MessageLookupByLibrary.simpleMessage("主题设置"), "themeSettings": MessageLookupByLibrary.simpleMessage("主题设置"),
"themeSettingsColors": MessageLookupByLibrary.simpleMessage("主题色"), "themeSettingsColors": MessageLookupByLibrary.simpleMessage("主题色"),
"themeSettingsColorsAndroid": "themeSettingsColorsAndroid": MessageLookupByLibrary.simpleMessage("主题色"),
MessageLookupByLibrary.simpleMessage("主题色"),
"themeSettingsColorsBook": MessageLookupByLibrary.simpleMessage( "themeSettingsColorsBook": MessageLookupByLibrary.simpleMessage(
"书籍详情页自适应主题", "书籍详情页自适应主题",
), ),
"themeSettingsColorsBookDescription": "themeSettingsColorsBookDescription": MessageLookupByLibrary.simpleMessage(
MessageLookupByLibrary.simpleMessage(
"以牺牲一些性能为代价,对书籍详情页的颜色进行美化", "以牺牲一些性能为代价,对书籍详情页的颜色进行美化",
), ),
"themeSettingsColorsCurrent": MessageLookupByLibrary.simpleMessage( "themeSettingsColorsCurrent": MessageLookupByLibrary.simpleMessage(
@ -388,8 +364,7 @@ class MessageLookup extends MessageLookupByLibrary {
"themeSettingsColorsDescription": MessageLookupByLibrary.simpleMessage( "themeSettingsColorsDescription": MessageLookupByLibrary.simpleMessage(
"使用应用程序的系统主题色", "使用应用程序的系统主题色",
), ),
"themeSettingsDescription": "themeSettingsDescription": MessageLookupByLibrary.simpleMessage("自定义应用主题"),
MessageLookupByLibrary.simpleMessage("自定义应用主题"),
"timeSecond": m7, "timeSecond": m7,
"unknown": MessageLookupByLibrary.simpleMessage("未知"), "unknown": MessageLookupByLibrary.simpleMessage("未知"),
"webVersion": MessageLookupByLibrary.simpleMessage("Web版本"), "webVersion": MessageLookupByLibrary.simpleMessage("Web版本"),

View file

@ -1894,6 +1894,26 @@ class S {
); );
} }
/// `Download Settings`
String get downloadSettings {
return Intl.message(
'Download Settings',
name: 'downloadSettings',
desc: '',
args: [],
);
}
/// `Customize download settings`
String get downloadSettingsDescription {
return Intl.message(
'Customize download settings',
name: 'downloadSettingsDescription',
desc: '',
args: [],
);
}
/// `Pull to refresh` /// `Pull to refresh`
String get erDragText { String get erDragText {
return Intl.message( return Intl.message(

View file

@ -272,6 +272,9 @@
"resetAppSettingsDescription": "Reset the app settings to the default values", "resetAppSettingsDescription": "Reset the app settings to the default values",
"resetAppSettingsDialog": "Are you sure you want to reset the app settings?", "resetAppSettingsDialog": "Are you sure you want to reset the app settings?",
"downloadSettings": "Download Settings",
"downloadSettingsDescription": "Customize download settings",
"erDragText": "Pull to refresh", "erDragText": "Pull to refresh",
"erDragTextUp": "Pull to refresh", "erDragTextUp": "Pull to refresh",
"erArmedText": "Release ready", "erArmedText": "Release ready",

View file

@ -272,6 +272,9 @@
"resetAppSettingsDescription": "将应用程序设置重置为默认值", "resetAppSettingsDescription": "将应用程序设置重置为默认值",
"resetAppSettingsDialog": "您确定要重置应用程序设置吗?", "resetAppSettingsDialog": "您确定要重置应用程序设置吗?",
"downloadSettings": "下载设置",
"downloadSettingsDescription": "自定义下载设置",
"erDragText": "下拉刷新", "erDragText": "下拉刷新",
"erDragTextUp": "上拉加载", "erDragTextUp": "上拉加载",
"erArmedText": "准备就绪", "erArmedText": "准备就绪",

View file

@ -0,0 +1,65 @@
// ignore_for_file: public_member_api_docs, sort_constructors_first
import 'dart:async';
import 'package:dropdown_search/dropdown_search.dart';
import 'package:flutter/material.dart';
import 'package:vaani/constants/sizes.dart';
//
class CustomDropdown<T> extends StatelessWidget {
final T? selected;
final FutureOr<List<T>> Function(String, LoadProps?)? items;
final String Function(T)? itemAsString;
final void Function(T?)? onChanged;
const CustomDropdown({
super.key,
this.selected,
this.items,
this.onChanged,
this.itemAsString,
});
@override
Widget build(BuildContext context) {
return DropdownSearch<T>(
selectedItem: selected,
mode: Mode.custom,
items: items,
dropdownBuilder: (ctx, selectedItem) {
return Row(
mainAxisAlignment: MainAxisAlignment.end,
children: [
Text(_itemAsString(selectedItem)),
const Icon(Icons.keyboard_arrow_down),
],
);
},
popupProps: PopupProps.menu(
menuProps: MenuProps(
borderRadius: const BorderRadius.all(
Radius.circular(AppElementSizes.borderRadiusRegular),
),
popUpAnimationStyle: AnimationStyle(duration: Duration.zero),
),
constraints: const BoxConstraints(minWidth: 180),
itemBuilder: (context, item, isDisabled, isSelected) => ListTile(
title: Text(_itemAsString(item)),
trailing: selected == item ? Icon(Icons.check) : null,
),
fit: FlexFit.loose,
),
onChanged: onChanged,
);
}
String _itemAsString(T? data) {
if (data == null) {
return "";
} else if (itemAsString != null) {
return itemAsString!(data);
}
return data.toString();
}
}

View file

@ -302,6 +302,14 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "1.0.2" version: "1.0.2"
cupertino_icons:
dependency: "direct main"
description:
name: cupertino_icons
sha256: ba631d1c7f7bef6b729a622b7b752645a2d076dba9976925b8f25725a30e1ee6
url: "https://pub.dev"
source: hosted
version: "1.0.8"
custom_lint: custom_lint:
dependency: "direct dev" dependency: "direct dev"
description: description:
@ -382,6 +390,14 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "2.1.1" version: "2.1.1"
dropdown_search:
dependency: "direct main"
description:
name: dropdown_search
sha256: c29b3e5147a82a06a4a08b3b574c51cb48cc17ad89893d53ee72a6f86643622e
url: "https://pub.dev"
source: hosted
version: "6.0.2"
duration_picker: duration_picker:
dependency: "direct main" dependency: "direct main"
description: description:
@ -460,7 +476,7 @@ packages:
source: hosted source: hosted
version: "4.5.2" version: "4.5.2"
flutter_cache_manager: flutter_cache_manager:
dependency: "direct main" dependency: transitive
description: description:
name: flutter_cache_manager name: flutter_cache_manager
sha256: "400b6592f16a4409a7f2bb929a9a7e38c72cceb8ffb99ee57bbf2cb2cecf8386" sha256: "400b6592f16a4409a7f2bb929a9a7e38c72cceb8ffb99ee57bbf2cb2cecf8386"
@ -1226,14 +1242,6 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "0.2.0" version: "0.2.0"
scroll_loop_auto_scroll:
dependency: "direct main"
description:
name: scroll_loop_auto_scroll
sha256: "83645b380c58c9dac2a9948b11a6b09149a2aebd18a7ca25bbf3be3c89dbbbff"
url: "https://pub.dev"
source: hosted
version: "0.0.5"
sensors_plus: sensors_plus:
dependency: "direct main" dependency: "direct main"
description: description:

View file

@ -35,39 +35,57 @@ dependencies:
flutter: flutter:
sdk: flutter sdk: flutter
animated_list_plus: ^0.5.2 # The following adds the Cupertino Icons font to your application.
animated_theme_switcher: ^2.0.10 # Use with the CupertinoIcons class for iOS style icons.
archive: ^4.0.5 # 用于编码和解码各种归档和压缩格式的Dart库 cupertino_icons: ^1.0.6
audio_video_progress_bar: ^2.0.2 # 播放进度条
# 图标
icons_plus: ^5.0.0
# hooks
flutter_hooks: ^0.21.2
# 路由
go_router: ^14.0.2
# 状态管理 hooks
hooks_riverpod: ^2.5.1 # riverpod hooks hooks_riverpod: ^2.5.1 # riverpod hooks
riverpod_annotation: ^2.3.5 riverpod_annotation: ^2.3.5
# auto_scroll_text: ^0.0.7
background_downloader: ^9.2.0
cached_network_image: ^3.3.1
# coast: ^2.0.2
# cupertino_icons: ^1.0.6
# flutter_platform_widgets: ^9.0.0
flutter_staggered_grid_view: ^0.7.0
super_sliver_list: ^0.4.1
easy_refresh: ^3.4.0
duration_picker: ^1.2.0
dynamic_color: ^1.7.0
# easy_stepper: ^0.8.4
file_picker: ^10.0.0
flutter_animate: ^4.5.0 # json
flutter_cache_manager: ^3.3.2 json_annotation: ^4.9.0
flutter_hooks: ^0.21.2
flutter_settings_ui: ^3.0.1
# font_awesome_flutter: ^10.7.0
freezed_annotation: ^2.4.1 freezed_annotation: ^2.4.1
go_router: ^14.0.2
# 设备信息
device_info_plus: ^11.3.3
# 包信息
package_info_plus: ^8.0.0
# 存储
path: ^1.9.0
path_provider: ^2.1.0
hive_ce: ^2.16.0 # 轻量级且极快的键值数据库 hive_ce: ^2.16.0 # 轻量级且极快的键值数据库
hive_ce_flutter: 2.3.3 hive_ce_flutter: 2.3.3
# hive: ^4.0.0-dev.2 file_picker: ^10.0.0 # 文件选择器
# isar: ^4.0.0-dev.14 archive: ^4.0.5 # 用于编码和解码各种归档和压缩格式的Dart库
# isar_flutter_libs: ^4.0.0-dev.14
json_annotation: ^4.9.0 # 图片缓存
cached_network_image: ^3.3.1
# flutter_cache_manager: ^3.3.2
# 日志
logging: ^1.2.0
logging_appenders: ^1.3.1
# 骨架屏
shimmer: ^3.0.0
# 滚动列表
super_sliver_list: ^0.4.1
# 上拉刷新 下拉加载
easy_refresh: ^3.4.0
# 音频播放
audio_service: ^0.18.15 audio_service: ^0.18.15
audio_session: ^0.1.23 audio_session: ^0.1.23
just_audio: ^0.10.5 just_audio: ^0.10.5
@ -79,58 +97,84 @@ dependencies:
# path: just_audio_background # path: just_audio_background
# just_audio_windows: ^0.2.2 # just_audio_windows: ^0.2.2
just_audio_media_kit: ^2.0.4 just_audio_media_kit: ^2.0.4
# just_audio_media_kit:
# path: ./just_audio_media_kit
media_kit_libs_linux: any media_kit_libs_linux: any
media_kit_libs_windows_audio: any media_kit_libs_windows_audio: any
# media_kit: ^1.2.3 # Primary package.
# media_kit_libs_audio: any # Native audio dependencies.
list_wheel_scroll_view_nls: ^0.0.3 # 下载
logging: ^1.2.0 background_downloader: ^9.2.0
logging_appenders: ^1.3.1
lottie: ^3.1.0 # 设置UI
material_color_utilities: ^0.11.1 flutter_settings_ui: ^3.0.1
# material_symbols_icons: ^4.2785.1
# miniplayer: # 权限申请
# git:
# url: https://github.com/Dr-Blank/miniplayer.git
# ref: feat-notifier-for-percent-dismissed
# numberpicker: ^2.1.2
device_info_plus: ^11.3.3
package_info_plus: ^8.0.0
path: ^1.9.0
path_provider: ^2.1.0
permission_handler: ^11.3.1 permission_handler: ^11.3.1
scroll_loop_auto_scroll: ^0.0.5 # html显示
sensors_plus: ^6.0.1 flutter_html: ^3.0.0
# 启动URL
url_launcher: ^6.2.6
# 播放进度条
audio_video_progress_bar: ^2.0.2
# 网格布局
flutter_staggered_grid_view: ^0.7.0
# 横向滚动
list_wheel_scroll_view_nls: ^0.0.3
# 滚动
# scroll_loop_auto_scroll: ^0.0.5
# 时长选择器
duration_picker: ^1.2.0
# 下拉选择框
dropdown_search: ^6.0.2
# 动画
flutter_animate: ^4.5.0
lottie: ^3.1.0
animated_list_plus: ^0.5.2
animated_theme_switcher: ^2.0.10
# Material Design 3色彩系统的算法和工具
material_color_utilities: ^0.11.1
# 动态色彩方案
dynamic_color: ^1.7.0
# 分享插件
share_plus: ^10.0.2 share_plus: ^10.0.2
# AudiobookShelf SDK
shelfsdk: shelfsdk:
path: ./shelfsdk path: ./shelfsdk
shimmer: ^3.0.0
url_launcher: ^6.2.6 # 移动端 Android IOS
vibration: ^3.1.3 vibration: ^3.1.3 # 震动
flutter_html: ^3.0.0 sensors_plus: ^6.0.1 # 访问加速度计、陀螺仪、磁力计和 气压计传感器
# 桌面
window_manager: ^0.5.1 # 窗口管理
tray_manager: ^0.5.2 # 系统托盘
flutter_localizations: flutter_localizations:
sdk: flutter sdk: flutter
window_manager: ^0.5.1
tray_manager: ^0.5.2
icons_plus: ^5.0.0
# http_cache_client: ^1.0.4
# http_cache_hive_store: ^5.0.1
dev_dependencies: dev_dependencies:
build_runner: ^2.4.9
custom_lint: ^0.7.0
flutter_lints: ^5.0.0
flutter_test: flutter_test:
sdk: flutter sdk: flutter
flutter_lints: ^5.0.0
custom_lint: ^0.7.0
riverpod_lint: ^2.3.10
# dart代码生成
build_runner: ^2.4.9
#Riverpod代码生成
riverpod_generator: ^2.4.2
freezed: ^2.5.2 freezed: ^2.5.2
json_serializable: ^6.8.0 json_serializable: ^6.8.0
riverpod_generator: ^2.4.2
riverpod_lint: ^2.3.10
hive_ce_generator: ^1.8.1 hive_ce_generator: ^1.8.1
# 启动图标
flutter_launcher_icons: "^0.14.4" flutter_launcher_icons: "^0.14.4"
# For information on the generic Dart part of this file, see the # For information on the generic Dart part of this file, see the