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

@ -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);
}

View file

@ -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)