替换miniPlayer

This commit is contained in:
rang 2025-11-13 17:53:23 +08:00
parent eb9b8f3b94
commit e67d045da6
34 changed files with 1777 additions and 1078 deletions

View file

@ -2,7 +2,7 @@ import 'package:flutter/material.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:vaani/features/per_book_settings/providers/book_settings_provider.dart';
import 'package:vaani/features/player/providers/audiobook_player.dart';
import 'package:vaani/features/player/view/player_when_expanded.dart';
import 'package:vaani/features/player/view/player_expanded.dart';
import 'package:vaani/settings/view/notification_settings_page.dart';
class SkipChapterStartEndButton extends HookConsumerWidget {

View file

@ -1,18 +1,28 @@
import 'dart:async';
import 'package:shelfsdk/audiobookshelf_api.dart';
import 'package:vaani/features/player/core/audiobook_player.dart';
import 'package:vaani/shared/extensions/chapter.dart';
import 'package:vaani/shared/utils/throttler.dart';
class SkipStartEnd {
final Duration start;
final Duration end;
final AudiobookPlayer player;
// id
int? chapterId;
// int _index;
final List<StreamSubscription> _subscriptions = [];
final throttler = Throttler(delay: Duration(seconds: 3));
// final StreamController<PlaybackEvent> _playbackController =
// StreamController<PlaybackEvent>.broadcast();
SkipStartEnd({required this.start, required this.end, required this.player}) {
SkipStartEnd({
required this.start,
required this.end,
required this.player,
this.chapterId,
}) {
// if (start > Duration()) {
// _subscriptions.add(
// player.currentIndexStream.listen((index) {
@ -25,25 +35,81 @@ class SkipStartEnd {
// }),
// );
// }
if (end > Duration()) {
// if (end > Duration()) {
// _subscriptions.add(
// player.positionStream.distinct().listen((position) {
// if (player.duration != null &&
// player.duration!.inMilliseconds - player.position.inMilliseconds <
// end.inMilliseconds) {
// throttler.call(() {
// print('跳过片尾');
// Future.microtask(() async {
// await player.stop();
// player.seekToNext();
// });
// });
// }
// }),
// );
// }
if (start > Duration.zero || end > Duration.zero) {
_subscriptions.add(
player.positionStream.distinct().listen((position) {
if (player.duration != null &&
player.duration!.inMilliseconds - player.position.inMilliseconds <
end.inMilliseconds) {
throttler.call(() {
print('跳过片尾');
Future.microtask(() async {
await player.stop();
player.seekToNext();
player.positionStream.listen((position) {
final chapter = player.currentChapter;
if (chapter == null) {
return;
}
if (chapter.id == chapterId) {
if (end > Duration.zero &&
chapter.duration - (player.positionInBook - chapter.start) <
end) {
throttler.call(() {
Future.microtask(() => skipEnd(chapter));
});
});
}
}
if (chapter.id != chapterId) {
if (start > Duration.zero &&
player.positionInBook - chapter.start < Duration(seconds: 1)) {
throttler.call(() {
Future.microtask(() => skipStart(chapter));
});
}
chapterId = chapter.id;
}
}),
);
}
}
void skipStart(BookChapter chapter) {
print('跳过片头');
final globalPosition = player.positionInBook;
if (globalPosition - chapter.start < Duration(seconds: 1)) {
player.seekInBook(chapter.start + start);
}
}
void skipEnd(chapter) {
print('跳过片尾');
final book = player.book;
if (book == null) {
return;
}
if (start > Duration.zero) {
final currentIndex = book.chapters.indexOf(chapter);
if (currentIndex < book.chapters.length - 1) {
final nextChapter = book.chapters[currentIndex + 1];
// +
print('跳过片头+片尾');
player.skipToChapter(nextChapter.id, position: start);
}
} else {
player.seekToPrevious();
}
}
/// dispose the timer
void dispose() {
for (var sub in _subscriptions) {
@ -53,38 +119,3 @@ class SkipStartEnd {
// _playbackController.close();
}
}
class Throttler {
final Duration delay;
Timer? _timer;
DateTime? _lastRun;
Throttler({required this.delay});
void call(void Function() callback) {
//
if (_lastRun == null) {
callback();
_lastRun = DateTime.now();
return;
}
//
if (DateTime.now().difference(_lastRun!) > delay) {
callback();
_lastRun = DateTime.now();
}
//
else {
_timer?.cancel();
_timer = Timer(delay, () {
callback();
_lastRun = DateTime.now();
});
}
}
void dispose() {
_timer?.cancel();
}
}

View file

@ -5,12 +5,13 @@ import 'package:vaani/features/skip_start_end/skip_start_end.dart' as core;
part 'skip_start_end_provider.g.dart';
@Riverpod(keepAlive: true)
@riverpod
class SkipStartEnd extends _$SkipStartEnd {
@override
core.SkipStartEnd? build() {
final player = ref.watch(audiobookPlayerProvider);
final bookId = player.book?.libraryItemId ?? '_';
final player = ref.watch(simpleAudiobookPlayerProvider);
final book = ref.watch(audiobookPlayerProvider.select((v) => v.book));
final bookId = book?.libraryItemId ?? '_';
if (bookId == '_') {
return null;
}
@ -18,6 +19,13 @@ class SkipStartEnd extends _$SkipStartEnd {
final start = bookSettings.playerSettings.skipChapterStart;
final end = bookSettings.playerSettings.skipChapterEnd;
return core.SkipStartEnd(start: start, end: end, player: player);
final skipStartEnd = core.SkipStartEnd(
start: start,
end: end,
player: player,
chapterId: player.currentChapter?.id,
);
ref.onDispose(skipStartEnd.dispose);
return skipStartEnd;
}
}

View file

@ -6,12 +6,12 @@ part of 'skip_start_end_provider.dart';
// RiverpodGenerator
// **************************************************************************
String _$skipStartEndHash() => r'202cfb36fdb3d3fa12debfb188f87650473a88a9';
String _$skipStartEndHash() => r'857b448eac9bb9ab85cea9217775712e660bc990';
/// See also [SkipStartEnd].
@ProviderFor(SkipStartEnd)
final skipStartEndProvider =
NotifierProvider<SkipStartEnd, core.SkipStartEnd?>.internal(
AutoDisposeNotifierProvider<SkipStartEnd, core.SkipStartEnd?>.internal(
SkipStartEnd.new,
name: r'skipStartEndProvider',
debugGetCreateSourceHash:
@ -20,6 +20,6 @@ final skipStartEndProvider =
allTransitiveDependencies: null,
);
typedef _$SkipStartEnd = Notifier<core.SkipStartEnd?>;
typedef _$SkipStartEnd = AutoDisposeNotifier<core.SkipStartEnd?>;
// ignore_for_file: type=lint
// ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member, deprecated_member_use_from_same_package