chore: Renaming of Trickplay -> TrickPlay and other cleanups (#130)

Co-authored-by: PartyDonut <PartyDonut@users.noreply.github.com>
This commit is contained in:
PartyDonut 2024-11-08 08:52:34 +01:00 committed by GitHub
parent 556a33d613
commit 0b7b73134f
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
9 changed files with 58 additions and 71 deletions

View file

@ -3,7 +3,8 @@
"ficonsax", "ficonsax",
"jellyfin", "jellyfin",
"Jellyfin", "Jellyfin",
"LTRB" "LTRB",
"LTWH"
], ],
"dart.flutterSdkPath": ".fvm/versions/3.24.3", "dart.flutterSdkPath": ".fvm/versions/3.24.3",
"search.exclude": { "search.exclude": {

View file

@ -22,8 +22,8 @@ class TrickPlayModel with _$TrickPlayModel {
int get imagesPerTile => tileWidth * tileHeight; int get imagesPerTile => tileWidth * tileHeight;
String? getTile(Duration position) { String? getTile(Duration position) {
final int currentIndex = (position.inMilliseconds ~/ interval.inMilliseconds).clamp(0, thumbnailCount); final int currentIndex = (position.inMilliseconds ~/ interval.inMilliseconds).clamp(0, thumbnailCount - 1);
final int indexOfTile = (currentIndex ~/ imagesPerTile).clamp(0, images.length); final int indexOfTile = (currentIndex ~/ imagesPerTile).clamp(0, (images.length - 1));
return images.elementAtOrNull(indexOfTile); return images.elementAtOrNull(indexOfTile);
} }
@ -32,7 +32,10 @@ class TrickPlayModel with _$TrickPlayModel {
final int tileIndex = currentIndex % imagesPerTile; final int tileIndex = currentIndex % imagesPerTile;
final int column = tileIndex % tileWidth; final int column = tileIndex % tileWidth;
final int row = tileIndex ~/ tileWidth; final int row = tileIndex ~/ tileWidth;
return Offset((width * column).toDouble(), (height * row).toDouble()); return Offset(
(width * column).toDouble(),
(height * row).toDouble(),
);
} }
static Map<String, TrickPlayModel> toTrickPlayMap(Map<String, dynamic> map) { static Map<String, TrickPlayModel> toTrickPlayMap(Map<String, dynamic> map) {

View file

@ -1,9 +1,4 @@
import 'package:collection/collection.dart'; import 'package:collection/collection.dart';
import 'package:fladder/models/items/trick_play_model.dart';
import 'package:fladder/util/list_extensions.dart';
import 'package:fladder/wrappers/media_control_wrapper.dart'
if (dart.library.html) 'package:fladder/wrappers/media_control_wrapper_web.dart';
import 'package:flutter/widgets.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:media_kit/media_kit.dart'; import 'package:media_kit/media_kit.dart';
@ -12,10 +7,15 @@ import 'package:fladder/models/item_base_model.dart';
import 'package:fladder/models/items/chapters_model.dart'; import 'package:fladder/models/items/chapters_model.dart';
import 'package:fladder/models/items/intro_skip_model.dart'; import 'package:fladder/models/items/intro_skip_model.dart';
import 'package:fladder/models/items/media_streams_model.dart'; import 'package:fladder/models/items/media_streams_model.dart';
import 'package:fladder/models/items/trick_play_model.dart';
import 'package:fladder/models/playback/playback_model.dart'; import 'package:fladder/models/playback/playback_model.dart';
import 'package:fladder/providers/api_provider.dart'; import 'package:fladder/providers/api_provider.dart';
import 'package:fladder/providers/video_player_provider.dart'; import 'package:fladder/providers/video_player_provider.dart';
import 'package:fladder/util/duration_extensions.dart'; import 'package:fladder/util/duration_extensions.dart';
import 'package:fladder/util/list_extensions.dart';
import 'package:fladder/wrappers/media_control_wrapper.dart'
if (dart.library.html) 'package:fladder/wrappers/media_control_wrapper_web.dart';
import 'package:flutter/widgets.dart';
class DirectPlaybackModel implements PlaybackModel { class DirectPlaybackModel implements PlaybackModel {
DirectPlaybackModel({ DirectPlaybackModel({
@ -147,13 +147,6 @@ class DirectPlaybackModel implements PlaybackModel {
@override @override
Future<PlaybackModel?> updatePlaybackPosition(Duration position, bool isPlaying, Ref ref) async { Future<PlaybackModel?> updatePlaybackPosition(Duration position, bool isPlaying, Ref ref) async {
final api = ref.read(jellyApiProvider); final api = ref.read(jellyApiProvider);
//Check for newly generated scrubImages
if (trickPlay == null) {
final trickplay = await api.getTrickPlay(item: item, ref: ref);
ref.read(playBackModel.notifier).update((state) => copyWith(trickPlay: () => trickplay?.body));
}
await api.sessionsPlayingProgressPost( await api.sessionsPlayingProgressPost(
body: PlaybackProgressInfo( body: PlaybackProgressInfo(
canSeek: true, canSeek: true,

View file

@ -2,9 +2,6 @@ import 'dart:developer';
import 'package:chopper/chopper.dart'; import 'package:chopper/chopper.dart';
import 'package:collection/collection.dart'; import 'package:collection/collection.dart';
import 'package:fladder/models/items/intro_skip_model.dart';
import 'package:fladder/models/items/season_model.dart';
import 'package:fladder/models/items/series_model.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:media_kit/media_kit.dart'; import 'package:media_kit/media_kit.dart';
@ -12,7 +9,10 @@ import 'package:fladder/jellyfin/jellyfin_open_api.swagger.dart';
import 'package:fladder/models/item_base_model.dart'; import 'package:fladder/models/item_base_model.dart';
import 'package:fladder/models/items/chapters_model.dart'; import 'package:fladder/models/items/chapters_model.dart';
import 'package:fladder/models/items/episode_model.dart'; import 'package:fladder/models/items/episode_model.dart';
import 'package:fladder/models/items/intro_skip_model.dart';
import 'package:fladder/models/items/media_streams_model.dart'; import 'package:fladder/models/items/media_streams_model.dart';
import 'package:fladder/models/items/season_model.dart';
import 'package:fladder/models/items/series_model.dart';
import 'package:fladder/models/items/trick_play_model.dart'; import 'package:fladder/models/items/trick_play_model.dart';
import 'package:fladder/models/playback/direct_playback_model.dart'; import 'package:fladder/models/playback/direct_playback_model.dart';
import 'package:fladder/models/playback/offline_playback_model.dart'; import 'package:fladder/models/playback/offline_playback_model.dart';
@ -127,8 +127,8 @@ class PlaybackModelHelper {
} }
Future<EpisodeModel?> getNextUpEpisode(String itemId) async { Future<EpisodeModel?> getNextUpEpisode(String itemId) async {
final responnse = await api.showsNextUpGet(parentId: itemId, fields: [ItemFields.overview]); final response = await api.showsNextUpGet(parentId: itemId, fields: [ItemFields.overview]);
final episode = responnse.body?.items?.firstOrNull; final episode = response.body?.items?.firstOrNull;
if (episode == null) { if (episode == null) {
return null; return null;
} else { } else {

View file

@ -1,9 +1,4 @@
import 'package:collection/collection.dart'; import 'package:collection/collection.dart';
import 'package:fladder/models/items/trick_play_model.dart';
import 'package:fladder/util/list_extensions.dart';
import 'package:fladder/wrappers/media_control_wrapper.dart'
if (dart.library.html) 'package:fladder/wrappers/media_control_wrapper_web.dart';
import 'package:flutter/widgets.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:media_kit/media_kit.dart'; import 'package:media_kit/media_kit.dart';
@ -12,10 +7,15 @@ import 'package:fladder/models/item_base_model.dart';
import 'package:fladder/models/items/chapters_model.dart'; import 'package:fladder/models/items/chapters_model.dart';
import 'package:fladder/models/items/intro_skip_model.dart'; import 'package:fladder/models/items/intro_skip_model.dart';
import 'package:fladder/models/items/media_streams_model.dart'; import 'package:fladder/models/items/media_streams_model.dart';
import 'package:fladder/models/items/trick_play_model.dart';
import 'package:fladder/models/playback/playback_model.dart'; import 'package:fladder/models/playback/playback_model.dart';
import 'package:fladder/providers/api_provider.dart'; import 'package:fladder/providers/api_provider.dart';
import 'package:fladder/providers/video_player_provider.dart'; import 'package:fladder/providers/video_player_provider.dart';
import 'package:fladder/util/duration_extensions.dart'; import 'package:fladder/util/duration_extensions.dart';
import 'package:fladder/util/list_extensions.dart';
import 'package:fladder/wrappers/media_control_wrapper.dart'
if (dart.library.html) 'package:fladder/wrappers/media_control_wrapper_web.dart';
import 'package:flutter/widgets.dart';
class TranscodePlaybackModel implements PlaybackModel { class TranscodePlaybackModel implements PlaybackModel {
TranscodePlaybackModel({ TranscodePlaybackModel({
@ -148,13 +148,6 @@ class TranscodePlaybackModel implements PlaybackModel {
@override @override
Future<PlaybackModel?> updatePlaybackPosition(Duration position, bool isPlaying, Ref ref) async { Future<PlaybackModel?> updatePlaybackPosition(Duration position, bool isPlaying, Ref ref) async {
final api = ref.read(jellyApiProvider); final api = ref.read(jellyApiProvider);
//Check for newly generated scrubImages
if (trickPlay == null) {
final trickplay = await api.getTrickPlay(item: item, ref: ref);
ref.read(playBackModel.notifier).update((state) => copyWith(trickPlay: () => trickplay?.bodyOrThrow));
}
await api.sessionsPlayingProgressPost( await api.sessionsPlayingProgressPost(
body: PlaybackProgressInfo( body: PlaybackProgressInfo(
canSeek: true, canSeek: true,

View file

@ -4,21 +4,21 @@ import 'dart:typed_data';
import 'package:chopper/chopper.dart'; import 'package:chopper/chopper.dart';
import 'package:collection/collection.dart'; import 'package:collection/collection.dart';
import 'package:fladder/jellyfin/enum_models.dart';
import 'package:fladder/models/credentials_model.dart';
import 'package:fladder/models/items/intro_skip_model.dart';
import 'package:fladder/models/items/trick_play_model.dart';
import 'package:fladder/providers/image_provider.dart';
import 'package:fladder/util/duration_extensions.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:path/path.dart';
import 'package:fladder/jellyfin/enum_models.dart';
import 'package:fladder/jellyfin/jellyfin_open_api.swagger.dart'; import 'package:fladder/jellyfin/jellyfin_open_api.swagger.dart';
import 'package:fladder/models/account_model.dart'; import 'package:fladder/models/account_model.dart';
import 'package:fladder/models/credentials_model.dart';
import 'package:fladder/models/item_base_model.dart'; import 'package:fladder/models/item_base_model.dart';
import 'package:fladder/models/items/intro_skip_model.dart';
import 'package:fladder/models/items/trick_play_model.dart';
import 'package:fladder/providers/api_provider.dart'; import 'package:fladder/providers/api_provider.dart';
import 'package:fladder/providers/image_provider.dart';
import 'package:fladder/providers/user_provider.dart'; import 'package:fladder/providers/user_provider.dart';
import 'package:fladder/util/duration_extensions.dart';
import 'package:fladder/util/jellyfin_extension.dart'; import 'package:fladder/util/jellyfin_extension.dart';
import 'package:path/path.dart';
final jellyServiceProvider = StateProvider<JellyService>( final jellyServiceProvider = StateProvider<JellyService>(
(ref) => JellyService( (ref) => JellyService(
@ -913,8 +913,7 @@ class JellyService {
if (server == null) return null; if (server == null) return null;
final lines = response.bodyString.split('\n') final lines = response.bodyString.split('\n')..removeWhere((element) => element.startsWith('#'));
..removeWhere((element) => element.startsWith('#') || !element.contains('.jpg'));
return response.copyWith( return response.copyWith(
body: trickPlayModel.copyWith( body: trickPlayModel.copyWith(
images: lines images: lines

View file

@ -13,9 +13,9 @@ import 'package:fladder/util/list_padding.dart';
import 'package:fladder/util/string_extensions.dart'; import 'package:fladder/util/string_extensions.dart';
import 'package:fladder/widgets/gapped_container_shape.dart'; import 'package:fladder/widgets/gapped_container_shape.dart';
import 'package:fladder/widgets/shared/fladder_slider.dart'; import 'package:fladder/widgets/shared/fladder_slider.dart';
import 'package:fladder/widgets/shared/trickplay_image.dart'; import 'package:fladder/widgets/shared/trick_play_image.dart';
class ChapterProgressSlider extends ConsumerStatefulWidget { class VideoProgressBar extends ConsumerStatefulWidget {
final Function(bool value) wasPlayingChanged; final Function(bool value) wasPlayingChanged;
final bool wasPlaying; final bool wasPlaying;
final VoidCallback timerReset; final VoidCallback timerReset;
@ -24,7 +24,7 @@ class ChapterProgressSlider extends ConsumerStatefulWidget {
final bool buffering; final bool buffering;
final Duration buffer; final Duration buffer;
final Function(Duration duration) onPositionChanged; final Function(Duration duration) onPositionChanged;
const ChapterProgressSlider({ const VideoProgressBar({
required this.wasPlayingChanged, required this.wasPlayingChanged,
required this.wasPlaying, required this.wasPlaying,
required this.timerReset, required this.timerReset,
@ -40,7 +40,7 @@ class ChapterProgressSlider extends ConsumerStatefulWidget {
ConsumerState<ConsumerStatefulWidget> createState() => _ChapterProgressSliderState(); ConsumerState<ConsumerStatefulWidget> createState() => _ChapterProgressSliderState();
} }
class _ChapterProgressSliderState extends ConsumerState<ChapterProgressSlider> { class _ChapterProgressSliderState extends ConsumerState<VideoProgressBar> {
bool onHoverStart = false; bool onHoverStart = false;
bool onDragStart = false; bool onDragStart = false;
double _chapterPosition = 0.0; double _chapterPosition = 0.0;
@ -54,12 +54,12 @@ class _ChapterProgressSliderState extends ConsumerState<ChapterProgressSlider> {
final isVisible = (onDragStart ? true : onHoverStart); final isVisible = (onDragStart ? true : onHoverStart);
final player = ref.watch(videoPlayerProvider); final player = ref.watch(videoPlayerProvider);
final position = onDragStart ? currentDuration : widget.position; final position = onDragStart ? currentDuration : widget.position;
final IntroOutSkipModel? introOutro = ref.read(playBackModel.select((value) => value?.introSkipModel)); final IntroOutSkipModel? introCreditsModel = ref.read(playBackModel.select((value) => value?.introSkipModel));
final relativeFraction = position.inMilliseconds / widget.duration.inMilliseconds; final relativeFraction = position.inMilliseconds / widget.duration.inMilliseconds;
return LayoutBuilder( return LayoutBuilder(
builder: (context, constraints) { builder: (context, constraints) {
final sliderHeight = SliderTheme.of(context).trackHeight ?? (constraints.maxHeight / 3); final sliderHeight = SliderTheme.of(context).trackHeight ?? (constraints.maxHeight / 3);
final bufferWidth = calculateFracionWidth(constraints, widget.buffer); final bufferWidth = calculateFractionWidth(constraints, widget.buffer);
final bufferFraction = relativeFraction / (bufferWidth / constraints.maxWidth); final bufferFraction = relativeFraction / (bufferWidth / constraints.maxWidth);
return Stack( return Stack(
clipBehavior: Clip.none, clipBehavior: Clip.none,
@ -138,10 +138,10 @@ class _ChapterProgressSliderState extends ConsumerState<ChapterProgressSlider> {
child: Stack( child: Stack(
alignment: Alignment.center, alignment: Alignment.center,
children: [ children: [
if (introOutro?.intro?.start != null && introOutro?.intro?.end != null) if (introCreditsModel?.intro?.start != null && introCreditsModel?.intro?.end != null)
Positioned( Positioned(
left: calculateStartOffset(constraints, introOutro!.intro!.start), left: calculateStartOffset(constraints, introCreditsModel!.intro!.start),
right: calculateRightOffset(constraints, introOutro.intro!.end), right: calculateRightOffset(constraints, introCreditsModel.intro!.end),
bottom: 0, bottom: 0,
child: Container( child: Container(
height: 6, height: 6,
@ -153,10 +153,10 @@ class _ChapterProgressSliderState extends ConsumerState<ChapterProgressSlider> {
), ),
), ),
), ),
if (introOutro?.credits?.start != null && introOutro?.credits?.end != null) if (introCreditsModel?.credits?.start != null && introCreditsModel?.credits?.end != null)
Positioned( Positioned(
left: calculateStartOffset(constraints, introOutro!.credits!.start), left: calculateStartOffset(constraints, introCreditsModel!.credits!.start),
right: calculateRightOffset(constraints, introOutro.credits!.end), right: calculateRightOffset(constraints, introCreditsModel.credits!.end),
bottom: 0, bottom: 0,
child: Container( child: Container(
height: 6, height: 6,
@ -259,7 +259,7 @@ class _ChapterProgressSliderState extends ConsumerState<ChapterProgressSlider> {
); );
} }
double calculateFracionWidth(BoxConstraints constraints, Duration incoming) { double calculateFractionWidth(BoxConstraints constraints, Duration incoming) {
return (constraints.maxWidth * (incoming.inSeconds / widget.duration.inSeconds)).clamp(0, constraints.maxWidth); return (constraints.maxWidth * (incoming.inSeconds / widget.duration.inSeconds)).clamp(0, constraints.maxWidth);
} }
@ -320,7 +320,7 @@ class _ChapterProgressSliderState extends ConsumerState<ChapterProgressSlider> {
: const SizedBox.shrink() : const SizedBox.shrink()
: AspectRatio( : AspectRatio(
aspectRatio: trickPlay.width.toDouble() / trickPlay.height.toDouble(), aspectRatio: trickPlay.width.toDouble() / trickPlay.height.toDouble(),
child: TrickplayImage( child: TrickPlayImage(
trickPlay, trickPlay,
position: currentDuration, position: currentDuration,
), ),

View file

@ -475,7 +475,7 @@ class _DesktopControlsState extends ConsumerState<DesktopControls> {
const SizedBox(height: 4), const SizedBox(height: 4),
SizedBox( SizedBox(
height: 25, height: 25,
child: ChapterProgressSlider( child: VideoProgressBar(
wasPlayingChanged: (value) => wasPlaying = value, wasPlayingChanged: (value) => wasPlaying = value,
wasPlaying: wasPlaying, wasPlaying: wasPlaying,
duration: mediaPlayback.duration, duration: mediaPlayback.duration,

View file

@ -10,19 +10,19 @@ import 'package:http/http.dart' as http;
import 'package:fladder/models/items/trick_play_model.dart'; import 'package:fladder/models/items/trick_play_model.dart';
class TrickplayImage extends ConsumerStatefulWidget { class TrickPlayImage extends ConsumerStatefulWidget {
final TrickPlayModel trickplay; final TrickPlayModel trickPlay;
final Duration? position; final Duration? position;
const TrickplayImage(this.trickplay, {this.position, super.key}); const TrickPlayImage(this.trickPlay, {this.position, super.key});
@override @override
ConsumerState<ConsumerStatefulWidget> createState() => _TrickplayImageState(); ConsumerState<ConsumerStatefulWidget> createState() => _TrickPlayImageState();
} }
class _TrickplayImageState extends ConsumerState<TrickplayImage> { class _TrickPlayImageState extends ConsumerState<TrickPlayImage> {
ui.Image? image; ui.Image? image;
late TrickPlayModel model = widget.trickplay; late TrickPlayModel model = widget.trickPlay;
late Duration time = widget.position ?? Duration.zero; late Duration time = widget.position ?? Duration.zero;
late Offset currentOffset = const Offset(0, 0); late Offset currentOffset = const Offset(0, 0);
String? currentUrl; String? currentUrl;
@ -34,11 +34,11 @@ class _TrickplayImageState extends ConsumerState<TrickplayImage> {
} }
@override @override
void didUpdateWidget(covariant TrickplayImage oldWidget) { void didUpdateWidget(covariant TrickPlayImage oldWidget) {
super.didUpdateWidget(oldWidget); super.didUpdateWidget(oldWidget);
if (oldWidget.position?.inMilliseconds != widget.position?.inMilliseconds) { if (oldWidget.position?.inMilliseconds != widget.position?.inMilliseconds) {
time = widget.position ?? Duration.zero; time = widget.position ?? Duration.zero;
model = widget.trickplay; model = widget.trickPlay;
loadImage(); loadImage();
} }
} }
@ -48,11 +48,9 @@ class _TrickplayImageState extends ConsumerState<TrickplayImage> {
return Container( return Container(
child: image != null child: image != null
? CustomPaint( ? CustomPaint(
painter: TilledPainter(image!, currentOffset, widget.trickplay), painter: _TrickPlayPainter(image!, currentOffset, widget.trickPlay),
) )
: Container( : const SizedBox.shrink(),
color: Colors.purple,
),
); );
} }
@ -96,12 +94,12 @@ class _TrickplayImageState extends ConsumerState<TrickplayImage> {
} }
} }
class TilledPainter extends CustomPainter { class _TrickPlayPainter extends CustomPainter {
final ui.Image image; final ui.Image image;
final Offset offset; final Offset offset;
final TrickPlayModel model; final TrickPlayModel model;
TilledPainter(this.image, this.offset, this.model); _TrickPlayPainter(this.image, this.offset, this.model);
@override @override
void paint(Canvas canvas, Size size) { void paint(Canvas canvas, Size size) {