feat: Added Skip forward time #84

This commit is contained in:
kodjomoustapha 2023-12-29 16:46:31 +01:00
parent 710c498bb1
commit a33536d680
18 changed files with 235 additions and 135 deletions

View file

@ -275,5 +275,6 @@
"default_playback_speed_length": "طول سرعة التشغيل الافتراضية",
"updateProgressAfterReading": "تحديث التقدم بعد القراءة",
"no_sources_installed": "لم يتم تثبيت مصادر!",
"show_extensions": "عرض الإضافات"
"show_extensions": "عرض الإضافات",
"default_skip_forward_skip_length": "طول التخطي الافتراضي للأمام"
}

View file

@ -275,5 +275,6 @@
"default_playback_speed_length": "Standardlänge für Wiedergabegeschwindigkeit",
"updateProgressAfterReading": "Fortschritt nach dem Lesen aktualisieren",
"no_sources_installed": "Keine Quellen installiert!",
"show_extensions": "Erweiterungen anzeigen"
"show_extensions": "Erweiterungen anzeigen",
"default_skip_forward_skip_length": "Standardmäßige Länge des Vorwärtsspringens"
}

View file

@ -275,5 +275,6 @@
"default_playback_speed_length": "Default Playback speed length",
"updateProgressAfterReading": "Update progress after reading",
"no_sources_installed": "No sources installed!",
"show_extensions": "Show extensions"
"show_extensions": "Show extensions",
"default_skip_forward_skip_length": "Default skip forward skip length"
}

View file

@ -275,5 +275,6 @@
"default_playback_speed_length": "Duración predeterminada de la velocidad de reproducción",
"updateProgressAfterReading": "Actualizar el progreso después de leer",
"no_sources_installed": "¡No hay fuentes instaladas!",
"show_extensions": "Mostrar extensiones"
"show_extensions": "Mostrar extensiones",
"default_skip_forward_skip_length": "Longitud de salto hacia adelante predeterminada"
}

View file

@ -275,5 +275,6 @@
"default_playback_speed_length": "Duración predeterminada de la velocidad de reproducción",
"updateProgressAfterReading": "Actualizar el progreso después de leer",
"no_sources_installed": "¡No hay fuentes instaladas!",
"show_extensions": "Mostrar extensiones"
"show_extensions": "Mostrar extensiones",
"default_skip_forward_skip_length": "Longitud de salto hacia adelante predeterminada"
}

View file

@ -275,5 +275,6 @@
"default_playback_speed_length": "Longueur par défaut de la vitesse de lecture",
"updateProgressAfterReading": "Synchroniser la progression après lecture",
"no_sources_installed": "Aucune source installée !",
"show_extensions": "Afficher les extensions"
"show_extensions": "Afficher les extensions",
"default_skip_forward_skip_length": "Longueur de saut par défaut"
}

View file

@ -275,5 +275,6 @@
"default_playback_speed_length": "Panjang kecepatan pemutaran default",
"updateProgressAfterReading": "Perbarui kemajuan setelah membaca",
"no_sources_installed": "Tidak ada sumber yang terpasang!",
"show_extensions": "Tampilkan ekstensi"
"show_extensions": "Tampilkan ekstensi",
"default_skip_forward_skip_length": "Panjang lompatan maju default"
}

View file

@ -275,5 +275,6 @@
"default_playback_speed_length": "Durata predefinita per la velocità di riproduzione",
"updateProgressAfterReading": "Aggiorna il progresso dopo aver letto",
"no_sources_installed": "Nessuna fonte installata!",
"show_extensions": "Mostra estensioni"
"show_extensions": "Mostra estensioni",
"default_skip_forward_skip_length": "Lunghezza predefinita del salto in avanti"
}

View file

@ -275,5 +275,6 @@
"default_playback_speed_length": "Duração padrão da velocidade de reprodução",
"updateProgressAfterReading": "Atualizar progresso após a leitura",
"no_sources_installed": "Nenhuma fonte instalada!",
"show_extensions": "mostrar extensões"
"show_extensions": "mostrar extensões",
"default_skip_forward_skip_length": "Comprimento padrão do salto para frente"
}

View file

@ -275,5 +275,6 @@
"default_playback_speed_length": "Duração padrão da velocidade de reprodução",
"updateProgressAfterReading": "Atualize o progresso após a leitura",
"no_sources_installed": "Nenhuma fonte instalada!",
"show_extensions": "Mostrar extensões"
"show_extensions": "Mostrar extensões",
"default_skip_forward_skip_length": "Comprimento padrão do salto para frente"
}

View file

@ -275,5 +275,6 @@
"default_playback_speed_length": "Стандартная длина скорости воспроизведения",
"updateProgressAfterReading": "Обновить прогресс после чтения",
"no_sources_installed": "Источники не установлены!",
"show_extensions": "Показать расширения"
"show_extensions": "Показать расширения",
"default_skip_forward_skip_length": "Длина пропуска вперед по умолчанию"
}

View file

@ -275,5 +275,6 @@
"default_playback_speed_length": "Varsayılan Oynatma hızı süresi",
"updateProgressAfterReading": "Okuduktan Sonra İlerlemeyi Güncelle",
"no_sources_installed": "Hiçbir kaynak yüklü değil!",
"show_extensions": "uzantıları göster"
"show_extensions": "uzantıları göster",
"default_skip_forward_skip_length": "Varsayılan ileri atlama atlama uzunluğu"
}

View file

@ -275,5 +275,6 @@
"default_playback_speed_length": "默认播放速度长度",
"updateProgressAfterReading": "阅读后更新进度",
"no_sources_installed": "未安装任何来源!",
"show_extensions": "显示扩展"
"show_extensions": "显示扩展",
"default_skip_forward_skip_length": "默认向前跳过长度"
}

View file

@ -47,7 +47,10 @@ void main(List<String> args) async {
// Ensure widget and media kits are initialized.
WidgetsFlutterBinding.ensureInitialized();
MediaKit.ensureInitialized();
await windowManager.ensureInitialized();
if (!(Platform.isAndroid || Platform.isIOS)) {
await windowManager.ensureInitialized();
}
// Initialize the Isar database.
isar = await StorageProvider().initDB(null);
await StorageProvider().requestPermission();

View file

@ -691,7 +691,6 @@ class _AnimeStreamPageState extends riv.ConsumerState<AnimeStreamPage> {
onPressed: () async {
_tempPosition.value = Duration(
seconds: defaultSkipIntroLength +
_currentPosition.value.inSeconds -
_currentPosition.value.inSeconds);
await _player.seek(Duration(
seconds: _currentPosition.value.inSeconds +
@ -779,6 +778,7 @@ class _AnimeStreamPageState extends riv.ConsumerState<AnimeStreamPage> {
_streamController
.getEpisodesLength(_streamController.getEpisodeIndex().$2);
bool hasNextEpisode = _streamController.getEpisodeIndex().$1 != 0;
final skipDuration = ref.watch(defaultDoubleTapToSkipLengthStateProvider);
return Column(
mainAxisAlignment: MainAxisAlignment.end,
children: [
@ -810,6 +810,78 @@ class _AnimeStreamPageState extends riv.ConsumerState<AnimeStreamPage> {
CustomeMaterialDesktopPlayOrPauseButton(
controller: _controller,
),
SizedBox(
height: 50,
width: 50,
child: IconButton(
onPressed: () async {
_tempPosition.value = Duration(
seconds:
skipDuration - _currentPosition.value.inSeconds);
await _player.seek(Duration(
seconds:
_currentPosition.value.inSeconds - skipDuration));
_tempPosition.value = null;
},
icon: Stack(
children: [
const Positioned.fill(
child: Icon(
Icons.rotate_left_outlined,
color: Colors.white,
size: 30,
),
),
Positioned.fill(
child: Center(
child: Padding(
padding: const EdgeInsets.only(top: 2),
child: Text(
skipDuration.toString(),
style: const TextStyle(fontSize: 9),
),
)),
),
],
),
),
),
SizedBox(
height: 50,
width: 50,
child: IconButton(
onPressed: () async {
_tempPosition.value = Duration(
seconds:
skipDuration + _currentPosition.value.inSeconds);
await _player.seek(Duration(
seconds:
_currentPosition.value.inSeconds + skipDuration));
_tempPosition.value = null;
},
icon: Stack(
children: [
const Positioned.fill(
child: Icon(
Icons.rotate_right_outlined,
color: Colors.white,
size: 30,
),
),
Positioned.fill(
child: Center(
child: Padding(
padding: const EdgeInsets.only(top: 2),
child: Text(
skipDuration.toString(),
style: const TextStyle(fontSize: 9),
),
)),
),
],
),
),
),
if (hasNextEpisode)
IconButton(
onPressed: () async {
@ -958,7 +1030,7 @@ class _AnimeStreamPageState extends riv.ConsumerState<AnimeStreamPage> {
fit: fit,
key: _key,
controls: (state) => _isDesktop
? DestopControllerWidget(
? DesktopControllerWidget(
videoController: _controller,
topButtonBarWidget: _topButtonBar(context),
videoStatekey: _key,

View file

@ -10,7 +10,7 @@ import 'package:media_kit_video/media_kit_video.dart';
import 'package:media_kit_video/media_kit_video_controls/src/controls/extensions/duration.dart';
import 'package:window_manager/window_manager.dart';
class DestopControllerWidget extends StatefulWidget {
class DesktopControllerWidget extends StatefulWidget {
final Function(Duration?) tempDuration;
final AnimeStreamController streamController;
final VideoController videoController;
@ -18,7 +18,7 @@ class DestopControllerWidget extends StatefulWidget {
final GlobalKey<VideoState> videoStatekey;
final Widget bottomButtonBarWidget;
final Widget seekToWidget;
const DestopControllerWidget(
const DesktopControllerWidget(
{super.key,
required this.videoController,
required this.topButtonBarWidget,
@ -29,10 +29,10 @@ class DestopControllerWidget extends StatefulWidget {
required this.tempDuration});
@override
State<DestopControllerWidget> createState() => _DestopControllerWidgetState();
State<DesktopControllerWidget> createState() => _DesktopControllerWidgetState();
}
class _DestopControllerWidgetState extends State<DestopControllerWidget> {
class _DesktopControllerWidgetState extends State<DesktopControllerWidget> {
bool mount = true;
bool visible = true;
Duration controlsTransitionDuration = const Duration(milliseconds: 300);

View file

@ -1,17 +1,19 @@
// ignore_for_file: depend_on_referenced_packages
import 'dart:async';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:mangayomi/modules/anime/anime_player_view.dart';
import 'package:mangayomi/modules/anime/providers/anime_player_controller_provider.dart';
import 'package:mangayomi/modules/anime/widgets/custom_seekbar.dart';
import 'package:mangayomi/modules/anime/widgets/indicator_builder.dart';
import 'package:mangayomi/modules/manga/reader/providers/push_router.dart';
import 'package:mangayomi/modules/more/settings/player/providers/player_state_provider.dart';
import 'package:volume_controller/volume_controller.dart';
import 'package:screen_brightness/screen_brightness.dart';
import 'package:flutter/material.dart';
import 'package:media_kit_video/media_kit_video.dart';
import 'package:media_kit_video/media_kit_video_controls/src/controls/extensions/duration.dart';
class MobileControllerWidget extends StatefulWidget {
class MobileControllerWidget extends ConsumerStatefulWidget {
final AnimeStreamController streamController;
final VideoController videoController;
final Widget topButtonBarWidget;
@ -26,16 +28,19 @@ class MobileControllerWidget extends StatefulWidget {
required this.videoStatekey});
@override
State<MobileControllerWidget> createState() => _MobileControllerWidgetState();
ConsumerState<MobileControllerWidget> createState() =>
_MobileControllerWidgetState();
}
class _MobileControllerWidgetState extends State<MobileControllerWidget> {
class _MobileControllerWidgetState
extends ConsumerState<MobileControllerWidget> {
bool mount = true;
bool visible = true;
Duration controlsTransitionDuration = const Duration(milliseconds: 300);
Color backdropColor = const Color(0x66000000);
Timer? _timer;
late final skipDuration =
ref.watch(defaultDoubleTapToSkipLengthStateProvider);
final ValueNotifier<double> _brightnessValue = ValueNotifier(0.0);
final ValueNotifier<bool> _brightnessIndicator = ValueNotifier(false);
Timer? _brightnessTimer;
@ -78,7 +83,7 @@ class _MobileControllerWidgetState extends State<MobileControllerWidget> {
}
}
final horizontalGestureSensitivity = 5000;
final horizontalGestureSensitivity = 7500;
final verticalGestureSensitivity = 500;
@override
void didChangeDependencies() {
@ -556,30 +561,31 @@ class _MobileControllerWidgetState extends State<MobileControllerWidget> {
}
},
child: _BackwardSeekIndicator(
onChanged: (value) {
setState(() {
_seekBarDeltaValueNotifier = widget
.videoController
.player
.state
.position -
onChanged: (value) {
setState(() {
_seekBarDeltaValueNotifier = widget
.videoController
.player
.state
.position -
value;
});
},
onSubmitted: (value) {
setState(() {
_hideSeekBackwardButton = true;
});
var result = widget.videoController.player
.state.position -
value;
});
},
onSubmitted: (value) {
setState(() {
_hideSeekBackwardButton = true;
});
var result = widget
.videoController.player.state.position -
value;
result = result.clamp(
Duration.zero,
widget.videoController.player.state.duration,
);
widget.videoController.player.seek(result);
},
),
result = result.clamp(
Duration.zero,
widget
.videoController.player.state.duration,
);
widget.videoController.player.seek(result);
},
skipDuration: skipDuration),
)
: const SizedBox(),
),
@ -604,30 +610,31 @@ class _MobileControllerWidgetState extends State<MobileControllerWidget> {
}
},
child: _ForwardSeekIndicator(
onChanged: (value) {
setState(() {
_seekBarDeltaValueNotifier = widget
.videoController
.player
.state
.position +
onChanged: (value) {
setState(() {
_seekBarDeltaValueNotifier = widget
.videoController
.player
.state
.position +
value;
});
},
onSubmitted: (value) {
setState(() {
_hideSeekForwardButton = true;
});
var result = widget.videoController.player
.state.position +
value;
});
},
onSubmitted: (value) {
setState(() {
_hideSeekForwardButton = true;
});
var result = widget
.videoController.player.state.position +
value;
result = result.clamp(
Duration.zero,
widget.videoController.player.state.duration,
);
widget.videoController.player.seek(result);
},
),
result = result.clamp(
Duration.zero,
widget
.videoController.player.state.duration,
);
widget.videoController.player.seek(result);
},
skipDuration: skipDuration),
)
: const SizedBox(),
),
@ -643,9 +650,11 @@ class _MobileControllerWidgetState extends State<MobileControllerWidget> {
class _BackwardSeekIndicator extends StatefulWidget {
final void Function(Duration) onChanged;
final void Function(Duration) onSubmitted;
final int skipDuration;
const _BackwardSeekIndicator({
required this.onChanged,
required this.onSubmitted,
required this.skipDuration,
});
@override
@ -653,7 +662,7 @@ class _BackwardSeekIndicator extends StatefulWidget {
}
class _BackwardSeekIndicatorState extends State<_BackwardSeekIndicator> {
Duration value = const Duration(seconds: 10);
late Duration value = Duration(seconds: widget.skipDuration);
Timer? timer;
@ -679,7 +688,7 @@ class _BackwardSeekIndicatorState extends State<_BackwardSeekIndicator> {
});
widget.onChanged.call(value);
setState(() {
value += const Duration(seconds: 10);
value += Duration(seconds: widget.skipDuration);
});
}
@ -729,9 +738,11 @@ class _BackwardSeekIndicatorState extends State<_BackwardSeekIndicator> {
class _ForwardSeekIndicator extends StatefulWidget {
final void Function(Duration) onChanged;
final void Function(Duration) onSubmitted;
final int skipDuration;
const _ForwardSeekIndicator({
required this.onChanged,
required this.onSubmitted,
required this.skipDuration,
});
@override
@ -739,7 +750,7 @@ class _ForwardSeekIndicator extends StatefulWidget {
}
class _ForwardSeekIndicatorState extends State<_ForwardSeekIndicator> {
Duration value = const Duration(seconds: 10);
late Duration value = Duration(seconds: widget.skipDuration);
Timer? timer;
@ -765,7 +776,7 @@ class _ForwardSeekIndicatorState extends State<_ForwardSeekIndicator> {
});
widget.onChanged.call(value);
setState(() {
value += const Duration(seconds: 10);
value += Duration(seconds: widget.skipDuration);
});
}

View file

@ -15,8 +15,8 @@ class PlayerScreen extends ConsumerWidget {
final defaultSkipIntroLength =
ref.watch(defaultSkipIntroLengthStateProvider);
// final defaultDoubleTapToSkipLength =
// ref.watch(defaultDoubleTapToSkipLengthStateProvider);
final defaultDoubleTapToSkipLength =
ref.watch(defaultDoubleTapToSkipLengthStateProvider);
final defaultPlayBackSpeed = ref.watch(defaultPlayBackSpeedStateProvider);
return Scaffold(
@ -150,64 +150,65 @@ class PlayerScreen extends ConsumerWidget {
style: TextStyle(fontSize: 11, color: secondaryColor(context)),
),
),
// ListTile(
// onTap: () {
// final values = [30, 20, 10, 5, 3, 0];
// showDialog(
// context: context,
// builder: (context) {
// return AlertDialog(
// title: Text("Default Double tap to skip length"),
// content: SizedBox(
// width: mediaWidth(context, 0.8),
// child: ListView.builder(
// shrinkWrap: true,
// itemCount: values.length,
// itemBuilder: (context, index) {
// return RadioListTile(
// dense: true,
// contentPadding: const EdgeInsets.all(0),
// value: values[index],
// groupValue: defaultDoubleTapToSkipLength,
// onChanged: (value) {
// ref
// .read(
// defaultDoubleTapToSkipLengthStateProvider
// .notifier)
// .set(value!);
// Navigator.pop(context);
// },
// title: Row(
// children: [Text("${values[index]}s")],
// ),
// );
// },
// )),
// actions: [
// Row(
// mainAxisAlignment: MainAxisAlignment.end,
// children: [
// TextButton(
// onPressed: () async {
// Navigator.pop(context);
// },
// child: Text(
// context.l10n.cancel,
// style:
// TextStyle(color: primaryColor(context)),
// )),
// ],
// )
// ],
// );
// });
// },
// title: Text("Default Double tap to skip length"),
// subtitle: Text(
// "${defaultDoubleTapToSkipLength}s",
// style: TextStyle(fontSize: 11, color: secondaryColor(context)),
// ),
// ),
ListTile(
onTap: () {
final values = [30, 20, 10, 5, 3, 1];
showDialog(
context: context,
builder: (context) {
return AlertDialog(
title:
Text(context.l10n.default_skip_forward_skip_length),
content: SizedBox(
width: mediaWidth(context, 0.8),
child: ListView.builder(
shrinkWrap: true,
itemCount: values.length,
itemBuilder: (context, index) {
return RadioListTile(
dense: true,
contentPadding: const EdgeInsets.all(0),
value: values[index],
groupValue: defaultDoubleTapToSkipLength,
onChanged: (value) {
ref
.read(
defaultDoubleTapToSkipLengthStateProvider
.notifier)
.set(value!);
Navigator.pop(context);
},
title: Row(
children: [Text("${values[index]}s")],
),
);
},
)),
actions: [
Row(
mainAxisAlignment: MainAxisAlignment.end,
children: [
TextButton(
onPressed: () async {
Navigator.pop(context);
},
child: Text(
context.l10n.cancel,
style:
TextStyle(color: primaryColor(context)),
)),
],
)
],
);
});
},
title: Text(context.l10n.default_skip_forward_skip_length),
subtitle: Text(
"${defaultDoubleTapToSkipLength}s",
style: TextStyle(fontSize: 11, color: secondaryColor(context)),
),
),
ListTile(
onTap: () {
final values = [0.25, 0.5, 0.75, 1.0, 1.25, 1.50, 1.75, 2.0];