mirror of
https://github.com/Dr-Blank/Vaani.git
synced 2026-02-16 06:19:35 +00:00
aaa
This commit is contained in:
parent
a737365f26
commit
3c3c381f8a
18 changed files with 1266 additions and 1000 deletions
3
lib/features/player/core/abs_audio.dart
Normal file
3
lib/features/player/core/abs_audio.dart
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
import 'package:just_audio/just_audio.dart';
|
||||
|
||||
class AudiobookPlayer extends AudioPlayer {}
|
||||
|
|
@ -1,8 +1,4 @@
|
|||
import 'dart:io';
|
||||
|
||||
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';
|
||||
|
||||
// 对接audio_service
|
||||
|
|
|
|||
|
|
@ -1,34 +1,62 @@
|
|||
// import 'package:audio_service/audio_service.dart';
|
||||
// import 'package:audio_session/audio_session.dart';
|
||||
// import 'package:riverpod_annotation/riverpod_annotation.dart';
|
||||
// import 'package:vaani/features/player/core/abs_audio_handler.dart' as core;
|
||||
// import 'package:vaani/features/player/core/abs_audio_player.dart';
|
||||
// import 'package:vaani/globals.dart';
|
||||
// import 'package:just_audio_background/just_audio_background.dart'
|
||||
// show JustAudioBackground, NotificationConfig;
|
||||
// import 'package:just_audio_media_kit/just_audio_media_kit.dart'
|
||||
// show JustAudioMediaKit;
|
||||
// import 'package:vaani/features/settings/app_settings_provider.dart';
|
||||
// import 'package:vaani/features/settings/models/app_settings.dart';
|
||||
|
||||
// /// 音频播放器 配置
|
||||
// @Riverpod(keepAlive: true)
|
||||
// Future<void> configurePlayer(AbsAudioPlayer player) async {
|
||||
// Future<void> configurePlayer() async {
|
||||
// // for playing audio on windows, linux
|
||||
// JustAudioMediaKit.ensureInitialized();
|
||||
|
||||
// // for configuring how this app will interact with other audio apps
|
||||
// final session = await AudioSession.instance;
|
||||
// await session.configure(const AudioSessionConfiguration.speech());
|
||||
|
||||
// await AudioService.init(
|
||||
// builder: () => core.AbsAudioHandler(player),
|
||||
// config: const AudioServiceConfig(
|
||||
// androidNotificationChannelId: 'dr.blank.vaani.channel.audio',
|
||||
// androidNotificationChannelName: 'ABSPlayback',
|
||||
// androidNotificationChannelDescription:
|
||||
// 'Needed to control audio from lock screen',
|
||||
// androidNotificationOngoing: false,
|
||||
// androidStopForegroundOnPause: false,
|
||||
// androidNotificationIcon: 'drawable/ic_stat_logo',
|
||||
// preloadArtwork: true,
|
||||
// // fastForwardInterval: Duration(seconds: 20),
|
||||
// // rewindInterval: Duration(seconds: 20),
|
||||
// ),
|
||||
// );
|
||||
// final appSettings = loadOrCreateAppSettings();
|
||||
|
||||
// appLogger.finer('created simple player');
|
||||
// // for playing audio in the background
|
||||
// await JustAudioBackground.init(
|
||||
// androidNotificationChannelId: 'dr.blank.vaani.channel.audio',
|
||||
// androidNotificationChannelName: 'Audio playback',
|
||||
// androidNotificationOngoing: false,
|
||||
// androidStopForegroundOnPause: false,
|
||||
// androidNotificationChannelDescription: 'Audio playback in the background',
|
||||
// androidNotificationIcon: 'drawable/ic_stat_logo',
|
||||
// rewindInterval: appSettings.notificationSettings.rewindInterval,
|
||||
// fastForwardInterval: appSettings.notificationSettings.fastForwardInterval,
|
||||
// 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,
|
||||
// },
|
||||
// );
|
||||
// },
|
||||
// );
|
||||
// }
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ import 'package:audio_service/audio_service.dart';
|
|||
import 'package:audio_session/audio_session.dart';
|
||||
import 'package:collection/collection.dart';
|
||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||
import 'package:just_audio/just_audio.dart';
|
||||
import 'package:logging/logging.dart';
|
||||
import 'package:riverpod_annotation/riverpod_annotation.dart';
|
||||
import 'package:shelfsdk/audiobookshelf_api.dart' as api;
|
||||
|
|
@ -67,6 +68,123 @@ bool playerActive(Ref ref) {
|
|||
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(keepAlive: true)
|
||||
class AbsPlayer extends _$AbsPlayer {
|
||||
|
|
@ -166,7 +284,7 @@ class CurrentBook extends _$CurrentBook {
|
|||
@override
|
||||
api.BookExpanded? build() {
|
||||
listenSelf((previous, next) {
|
||||
if (next == null) {
|
||||
if (previous == null && next == null) {
|
||||
final activeLibraryItemId = AvailableHiveBoxes.basicBox
|
||||
.getAs<String>(CacheKey.activeLibraryItemId);
|
||||
if (activeLibraryItemId != null) {
|
||||
|
|
@ -226,20 +344,3 @@ class CurrentChapter extends _$CurrentChapter {
|
|||
Stream<Duration> positionChapter(Ref ref) {
|
||||
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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -57,6 +57,23 @@ final playerActiveProvider = AutoDisposeProvider<bool>.internal(
|
|||
@Deprecated('Will be removed in 3.0. Use Ref instead')
|
||||
// ignore: unused_element
|
||||
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';
|
||||
|
||||
/// Copied from Dart SDK
|
||||
|
|
@ -225,24 +242,22 @@ final positionChapterProvider = AutoDisposeStreamProvider<Duration>.internal(
|
|||
@Deprecated('Will be removed in 3.0. Use Ref instead')
|
||||
// ignore: unused_element
|
||||
typedef PositionChapterRef = AutoDisposeStreamProviderRef<Duration>;
|
||||
String _$currentChaptersHash() => r'2d694aaa17f7eed8f97859d83e5b61f22966c35a';
|
||||
String _$absAudioPlayerHash() => r'f595b5033eed9f4a4aa07c297c4a176955e6aab1';
|
||||
|
||||
/// See also [currentChapters].
|
||||
@ProviderFor(currentChapters)
|
||||
final currentChaptersProvider =
|
||||
AutoDisposeProvider<List<api.BookChapter>>.internal(
|
||||
currentChapters,
|
||||
name: r'currentChaptersProvider',
|
||||
/// See also [AbsAudioPlayer].
|
||||
@ProviderFor(AbsAudioPlayer)
|
||||
final absAudioPlayerProvider =
|
||||
NotifierProvider<AbsAudioPlayer, AudioPlayer>.internal(
|
||||
AbsAudioPlayer.new,
|
||||
name: r'absAudioPlayerProvider',
|
||||
debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product')
|
||||
? null
|
||||
: _$currentChaptersHash,
|
||||
: _$absAudioPlayerHash,
|
||||
dependencies: null,
|
||||
allTransitiveDependencies: null,
|
||||
);
|
||||
|
||||
@Deprecated('Will be removed in 3.0. Use Ref instead')
|
||||
// ignore: unused_element
|
||||
typedef CurrentChaptersRef = AutoDisposeProviderRef<List<api.BookChapter>>;
|
||||
typedef _$AbsAudioPlayer = Notifier<AudioPlayer>;
|
||||
String _$absPlayerHash() => r'e682fea03793a0370cb143602980d5c1e37396c7';
|
||||
|
||||
/// 音频播放器 riverpod状态
|
||||
|
|
@ -275,7 +290,7 @@ final playerStateProvider =
|
|||
);
|
||||
|
||||
typedef _$PlayerState = AutoDisposeNotifier<core.AbsPlayerState>;
|
||||
String _$currentBookHash() => r'790af1f9502b12879fc22c900ed5e3572381ab1e';
|
||||
String _$currentBookHash() => r'714d7701508b6186598e13bc38c57c3fe644ae90';
|
||||
|
||||
/// See also [CurrentBook].
|
||||
@ProviderFor(CurrentBook)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue