feature: Add support for mediaSegments (#138)

Co-authored-by: PartyDonut <PartyDonut@users.noreply.github.com>
This commit is contained in:
PartyDonut 2024-11-09 19:40:12 +01:00 committed by GitHub
parent 36758bd508
commit 5c560e54b5
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
28 changed files with 6823 additions and 8312 deletions

View file

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

View file

@ -1,20 +1,6 @@
import 'package:json_annotation/json_annotation.dart';
import 'package:collection/collection.dart';
enum AnalysisMode {
@JsonValue(null)
swaggerGeneratedUnknown(null),
@JsonValue('Introduction')
introduction('Introduction'),
@JsonValue('Credits')
credits('Credits');
final String? value;
const AnalysisMode(this.value);
}
enum AudioSpatialFormat {
@JsonValue(null)
swaggerGeneratedUnknown(null),
@ -313,6 +299,20 @@ enum DayPattern {
const DayPattern(this.value);
}
enum DeinterlaceMethod {
@JsonValue(null)
swaggerGeneratedUnknown(null),
@JsonValue('yadif')
yadif('yadif'),
@JsonValue('bwdif')
bwdif('bwdif');
final String? value;
const DeinterlaceMethod(this.value);
}
enum DlnaProfileType {
@JsonValue(null)
swaggerGeneratedUnknown(null),
@ -342,7 +342,11 @@ enum DownMixStereoAlgorithms {
@JsonValue('Dave750')
dave750('Dave750'),
@JsonValue('NightmodeDialogue')
nightmodedialogue('NightmodeDialogue');
nightmodedialogue('NightmodeDialogue'),
@JsonValue('Rfc7845')
rfc7845('Rfc7845'),
@JsonValue('Ac4')
ac4('Ac4');
final String? value;
@ -397,6 +401,38 @@ enum EmbeddedSubtitleOptions {
const EmbeddedSubtitleOptions(this.value);
}
enum EncoderPreset {
@JsonValue(null)
swaggerGeneratedUnknown(null),
@JsonValue('auto')
auto('auto'),
@JsonValue('placebo')
placebo('placebo'),
@JsonValue('veryslow')
veryslow('veryslow'),
@JsonValue('slower')
slower('slower'),
@JsonValue('slow')
slow('slow'),
@JsonValue('medium')
medium('medium'),
@JsonValue('fast')
fast('fast'),
@JsonValue('faster')
faster('faster'),
@JsonValue('veryfast')
veryfast('veryfast'),
@JsonValue('superfast')
superfast('superfast'),
@JsonValue('ultrafast')
ultrafast('ultrafast');
final String? value;
const EncoderPreset(this.value);
}
enum EncodingContext {
@JsonValue(null)
swaggerGeneratedUnknown(null),
@ -705,28 +741,30 @@ enum GroupUpdateType {
const GroupUpdateType(this.value);
}
enum HardwareEncodingType {
enum HardwareAccelerationType {
@JsonValue(null)
swaggerGeneratedUnknown(null),
@JsonValue('AMF')
amf('AMF'),
@JsonValue('QSV')
qsv('QSV'),
@JsonValue('NVENC')
nvenc('NVENC'),
@JsonValue('V4L2M2M')
v4l2m2m('V4L2M2M'),
@JsonValue('VAAPI')
vaapi('VAAPI'),
@JsonValue('VideoToolBox')
videotoolbox('VideoToolBox'),
@JsonValue('RKMPP')
rkmpp('RKMPP');
@JsonValue('none')
none('none'),
@JsonValue('amf')
amf('amf'),
@JsonValue('qsv')
qsv('qsv'),
@JsonValue('nvenc')
nvenc('nvenc'),
@JsonValue('v4l2m2m')
v4l2m2m('v4l2m2m'),
@JsonValue('vaapi')
vaapi('vaapi'),
@JsonValue('videotoolbox')
videotoolbox('videotoolbox'),
@JsonValue('rkmpp')
rkmpp('rkmpp');
final String? value;
const HardwareEncodingType(this.value);
const HardwareAccelerationType(this.value);
}
enum ImageFormat {
@ -1199,6 +1237,28 @@ enum MediaProtocol {
const MediaProtocol(this.value);
}
enum MediaSegmentType {
@JsonValue(null)
swaggerGeneratedUnknown(null),
@JsonValue('Unknown')
unknown('Unknown'),
@JsonValue('Commercial')
commercial('Commercial'),
@JsonValue('Preview')
preview('Preview'),
@JsonValue('Recap')
recap('Recap'),
@JsonValue('Outro')
outro('Outro'),
@JsonValue('Intro')
intro('Intro');
final String? value;
const MediaSegmentType(this.value);
}
enum MediaSourceType {
@JsonValue(null)
swaggerGeneratedUnknown(null),
@ -2011,6 +2071,68 @@ enum TaskState {
const TaskState(this.value);
}
enum TonemappingAlgorithm {
@JsonValue(null)
swaggerGeneratedUnknown(null),
@JsonValue('none')
none('none'),
@JsonValue('clip')
clip('clip'),
@JsonValue('linear')
linear('linear'),
@JsonValue('gamma')
gamma('gamma'),
@JsonValue('reinhard')
reinhard('reinhard'),
@JsonValue('hable')
hable('hable'),
@JsonValue('mobius')
mobius('mobius'),
@JsonValue('bt2390')
bt2390('bt2390');
final String? value;
const TonemappingAlgorithm(this.value);
}
enum TonemappingMode {
@JsonValue(null)
swaggerGeneratedUnknown(null),
@JsonValue('auto')
auto('auto'),
@JsonValue('max')
max('max'),
@JsonValue('rgb')
rgb('rgb'),
@JsonValue('lum')
lum('lum'),
@JsonValue('itp')
itp('itp');
final String? value;
const TonemappingMode(this.value);
}
enum TonemappingRange {
@JsonValue(null)
swaggerGeneratedUnknown(null),
@JsonValue('auto')
auto('auto'),
@JsonValue('tv')
tv('tv'),
@JsonValue('pc')
pc('pc');
final String? value;
const TonemappingRange(this.value);
}
enum TranscodeReason {
@JsonValue(null)
swaggerGeneratedUnknown(null),
@ -2064,7 +2186,9 @@ enum TranscodeReason {
@JsonValue('DirectPlayError')
directplayerror('DirectPlayError'),
@JsonValue('VideoRangeTypeNotSupported')
videorangetypenotsupported('VideoRangeTypeNotSupported');
videorangetypenotsupported('VideoRangeTypeNotSupported'),
@JsonValue('VideoCodecTagNotSupported')
videocodectagnotsupported('VideoCodecTagNotSupported');
final String? value;
@ -2138,7 +2262,9 @@ enum TranscodingInfoTranscodeReasons {
@JsonValue('DirectPlayError')
directplayerror('DirectPlayError'),
@JsonValue('VideoRangeTypeNotSupported')
videorangetypenotsupported('VideoRangeTypeNotSupported');
videorangetypenotsupported('VideoRangeTypeNotSupported'),
@JsonValue('VideoCodecTagNotSupported')
videocodectagnotsupported('VideoCodecTagNotSupported');
final String? value;
@ -5023,62 +5149,6 @@ enum SessionsSessionIdViewingPostItemType {
const SessionsSessionIdViewingPostItemType(this.value);
}
enum EpisodeIdIntroTimestampsGetMode {
@JsonValue(null)
swaggerGeneratedUnknown(null),
@JsonValue('Introduction')
introduction('Introduction'),
@JsonValue('Credits')
credits('Credits');
final String? value;
const EpisodeIdIntroTimestampsGetMode(this.value);
}
enum EpisodeIdIntroTimestampsV1GetMode {
@JsonValue(null)
swaggerGeneratedUnknown(null),
@JsonValue('Introduction')
introduction('Introduction'),
@JsonValue('Credits')
credits('Credits');
final String? value;
const EpisodeIdIntroTimestampsV1GetMode(this.value);
}
enum IntrosAllGetMode {
@JsonValue(null)
swaggerGeneratedUnknown(null),
@JsonValue('Introduction')
introduction('Introduction'),
@JsonValue('Credits')
credits('Credits');
final String? value;
const IntrosAllGetMode(this.value);
}
enum IntrosEraseTimestampsPostMode {
@JsonValue(null)
swaggerGeneratedUnknown(null),
@JsonValue('Introduction')
introduction('Introduction'),
@JsonValue('Credits')
credits('Credits');
final String? value;
const IntrosEraseTimestampsPostMode(this.value);
}
enum ShowsSeriesIdEpisodesGetSortBy {
@JsonValue(null)
swaggerGeneratedUnknown(null),

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -1,47 +0,0 @@
// ignore_for_file: public_member_api_docs, sort_constructors_first, invalid_annotation_target
import 'package:freezed_annotation/freezed_annotation.dart';
part 'intro_skip_model.freezed.dart';
part 'intro_skip_model.g.dart';
@freezed
class IntroOutSkipModel with _$IntroOutSkipModel {
const IntroOutSkipModel._();
factory IntroOutSkipModel({
IntroSkipModel? intro,
IntroSkipModel? credits,
}) = _IntroOutSkipModel;
factory IntroOutSkipModel.fromJson(Map<String, dynamic> json) => _$IntroOutSkipModelFromJson(json);
bool introInRange(Duration position) {
if (intro == null) return false;
return (position.compareTo(intro!.showTime) >= 0 && position.compareTo(intro!.hideTime) <= 0);
}
bool creditsInRange(Duration position) {
if (credits == null) return false;
return (position.compareTo(credits!.showTime) >= 0 && position.compareTo(credits!.hideTime) <= 0);
}
}
@freezed
class IntroSkipModel with _$IntroSkipModel {
factory IntroSkipModel({
@JsonKey(name: "EpisodeId") required String id,
@JsonKey(name: "Valid") required bool valid,
@JsonKey(name: "IntroStart", fromJson: _durationFromMilliseconds, toJson: _durationToMilliseconds)
required Duration start,
@JsonKey(name: "IntroEnd", fromJson: _durationFromMilliseconds, toJson: _durationToMilliseconds)
required Duration end,
@JsonKey(name: "ShowSkipPromptAt", fromJson: _durationFromMilliseconds, toJson: _durationToMilliseconds)
required Duration showTime,
@JsonKey(name: "HideSkipPromptAt", fromJson: _durationFromMilliseconds, toJson: _durationToMilliseconds)
required Duration hideTime,
}) = _IntroSkipModel;
factory IntroSkipModel.fromJson(Map<String, dynamic> json) => _$IntroSkipModelFromJson(json);
}
Duration _durationFromMilliseconds(num milliseconds) => Duration(milliseconds: (milliseconds * 1000).toInt());
num _durationToMilliseconds(Duration duration) => duration.inMilliseconds.toDouble() / 1000.0;

View file

@ -1,595 +0,0 @@
// coverage:ignore-file
// GENERATED CODE - DO NOT MODIFY BY HAND
// ignore_for_file: type=lint
// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark
part of 'intro_skip_model.dart';
// **************************************************************************
// FreezedGenerator
// **************************************************************************
T _$identity<T>(T value) => value;
final _privateConstructorUsedError = UnsupportedError(
'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\nPlease check the documentation here for more information: https://github.com/rrousselGit/freezed#adding-getters-and-methods-to-our-models');
IntroOutSkipModel _$IntroOutSkipModelFromJson(Map<String, dynamic> json) {
return _IntroOutSkipModel.fromJson(json);
}
/// @nodoc
mixin _$IntroOutSkipModel {
IntroSkipModel? get intro => throw _privateConstructorUsedError;
IntroSkipModel? get credits => throw _privateConstructorUsedError;
/// Serializes this IntroOutSkipModel to a JSON map.
Map<String, dynamic> toJson() => throw _privateConstructorUsedError;
/// Create a copy of IntroOutSkipModel
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
$IntroOutSkipModelCopyWith<IntroOutSkipModel> get copyWith =>
throw _privateConstructorUsedError;
}
/// @nodoc
abstract class $IntroOutSkipModelCopyWith<$Res> {
factory $IntroOutSkipModelCopyWith(
IntroOutSkipModel value, $Res Function(IntroOutSkipModel) then) =
_$IntroOutSkipModelCopyWithImpl<$Res, IntroOutSkipModel>;
@useResult
$Res call({IntroSkipModel? intro, IntroSkipModel? credits});
$IntroSkipModelCopyWith<$Res>? get intro;
$IntroSkipModelCopyWith<$Res>? get credits;
}
/// @nodoc
class _$IntroOutSkipModelCopyWithImpl<$Res, $Val extends IntroOutSkipModel>
implements $IntroOutSkipModelCopyWith<$Res> {
_$IntroOutSkipModelCopyWithImpl(this._value, this._then);
// ignore: unused_field
final $Val _value;
// ignore: unused_field
final $Res Function($Val) _then;
/// Create a copy of IntroOutSkipModel
/// with the given fields replaced by the non-null parameter values.
@pragma('vm:prefer-inline')
@override
$Res call({
Object? intro = freezed,
Object? credits = freezed,
}) {
return _then(_value.copyWith(
intro: freezed == intro
? _value.intro
: intro // ignore: cast_nullable_to_non_nullable
as IntroSkipModel?,
credits: freezed == credits
? _value.credits
: credits // ignore: cast_nullable_to_non_nullable
as IntroSkipModel?,
) as $Val);
}
/// Create a copy of IntroOutSkipModel
/// with the given fields replaced by the non-null parameter values.
@override
@pragma('vm:prefer-inline')
$IntroSkipModelCopyWith<$Res>? get intro {
if (_value.intro == null) {
return null;
}
return $IntroSkipModelCopyWith<$Res>(_value.intro!, (value) {
return _then(_value.copyWith(intro: value) as $Val);
});
}
/// Create a copy of IntroOutSkipModel
/// with the given fields replaced by the non-null parameter values.
@override
@pragma('vm:prefer-inline')
$IntroSkipModelCopyWith<$Res>? get credits {
if (_value.credits == null) {
return null;
}
return $IntroSkipModelCopyWith<$Res>(_value.credits!, (value) {
return _then(_value.copyWith(credits: value) as $Val);
});
}
}
/// @nodoc
abstract class _$$IntroOutSkipModelImplCopyWith<$Res>
implements $IntroOutSkipModelCopyWith<$Res> {
factory _$$IntroOutSkipModelImplCopyWith(_$IntroOutSkipModelImpl value,
$Res Function(_$IntroOutSkipModelImpl) then) =
__$$IntroOutSkipModelImplCopyWithImpl<$Res>;
@override
@useResult
$Res call({IntroSkipModel? intro, IntroSkipModel? credits});
@override
$IntroSkipModelCopyWith<$Res>? get intro;
@override
$IntroSkipModelCopyWith<$Res>? get credits;
}
/// @nodoc
class __$$IntroOutSkipModelImplCopyWithImpl<$Res>
extends _$IntroOutSkipModelCopyWithImpl<$Res, _$IntroOutSkipModelImpl>
implements _$$IntroOutSkipModelImplCopyWith<$Res> {
__$$IntroOutSkipModelImplCopyWithImpl(_$IntroOutSkipModelImpl _value,
$Res Function(_$IntroOutSkipModelImpl) _then)
: super(_value, _then);
/// Create a copy of IntroOutSkipModel
/// with the given fields replaced by the non-null parameter values.
@pragma('vm:prefer-inline')
@override
$Res call({
Object? intro = freezed,
Object? credits = freezed,
}) {
return _then(_$IntroOutSkipModelImpl(
intro: freezed == intro
? _value.intro
: intro // ignore: cast_nullable_to_non_nullable
as IntroSkipModel?,
credits: freezed == credits
? _value.credits
: credits // ignore: cast_nullable_to_non_nullable
as IntroSkipModel?,
));
}
}
/// @nodoc
@JsonSerializable()
class _$IntroOutSkipModelImpl extends _IntroOutSkipModel {
_$IntroOutSkipModelImpl({this.intro, this.credits}) : super._();
factory _$IntroOutSkipModelImpl.fromJson(Map<String, dynamic> json) =>
_$$IntroOutSkipModelImplFromJson(json);
@override
final IntroSkipModel? intro;
@override
final IntroSkipModel? credits;
@override
String toString() {
return 'IntroOutSkipModel(intro: $intro, credits: $credits)';
}
@override
bool operator ==(Object other) {
return identical(this, other) ||
(other.runtimeType == runtimeType &&
other is _$IntroOutSkipModelImpl &&
(identical(other.intro, intro) || other.intro == intro) &&
(identical(other.credits, credits) || other.credits == credits));
}
@JsonKey(includeFromJson: false, includeToJson: false)
@override
int get hashCode => Object.hash(runtimeType, intro, credits);
/// Create a copy of IntroOutSkipModel
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
@override
@pragma('vm:prefer-inline')
_$$IntroOutSkipModelImplCopyWith<_$IntroOutSkipModelImpl> get copyWith =>
__$$IntroOutSkipModelImplCopyWithImpl<_$IntroOutSkipModelImpl>(
this, _$identity);
@override
Map<String, dynamic> toJson() {
return _$$IntroOutSkipModelImplToJson(
this,
);
}
}
abstract class _IntroOutSkipModel extends IntroOutSkipModel {
factory _IntroOutSkipModel(
{final IntroSkipModel? intro,
final IntroSkipModel? credits}) = _$IntroOutSkipModelImpl;
_IntroOutSkipModel._() : super._();
factory _IntroOutSkipModel.fromJson(Map<String, dynamic> json) =
_$IntroOutSkipModelImpl.fromJson;
@override
IntroSkipModel? get intro;
@override
IntroSkipModel? get credits;
/// Create a copy of IntroOutSkipModel
/// with the given fields replaced by the non-null parameter values.
@override
@JsonKey(includeFromJson: false, includeToJson: false)
_$$IntroOutSkipModelImplCopyWith<_$IntroOutSkipModelImpl> get copyWith =>
throw _privateConstructorUsedError;
}
IntroSkipModel _$IntroSkipModelFromJson(Map<String, dynamic> json) {
return _IntroSkipModel.fromJson(json);
}
/// @nodoc
mixin _$IntroSkipModel {
@JsonKey(name: "EpisodeId")
String get id => throw _privateConstructorUsedError;
@JsonKey(name: "Valid")
bool get valid => throw _privateConstructorUsedError;
@JsonKey(
name: "IntroStart",
fromJson: _durationFromMilliseconds,
toJson: _durationToMilliseconds)
Duration get start => throw _privateConstructorUsedError;
@JsonKey(
name: "IntroEnd",
fromJson: _durationFromMilliseconds,
toJson: _durationToMilliseconds)
Duration get end => throw _privateConstructorUsedError;
@JsonKey(
name: "ShowSkipPromptAt",
fromJson: _durationFromMilliseconds,
toJson: _durationToMilliseconds)
Duration get showTime => throw _privateConstructorUsedError;
@JsonKey(
name: "HideSkipPromptAt",
fromJson: _durationFromMilliseconds,
toJson: _durationToMilliseconds)
Duration get hideTime => throw _privateConstructorUsedError;
/// Serializes this IntroSkipModel to a JSON map.
Map<String, dynamic> toJson() => throw _privateConstructorUsedError;
/// Create a copy of IntroSkipModel
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
$IntroSkipModelCopyWith<IntroSkipModel> get copyWith =>
throw _privateConstructorUsedError;
}
/// @nodoc
abstract class $IntroSkipModelCopyWith<$Res> {
factory $IntroSkipModelCopyWith(
IntroSkipModel value, $Res Function(IntroSkipModel) then) =
_$IntroSkipModelCopyWithImpl<$Res, IntroSkipModel>;
@useResult
$Res call(
{@JsonKey(name: "EpisodeId") String id,
@JsonKey(name: "Valid") bool valid,
@JsonKey(
name: "IntroStart",
fromJson: _durationFromMilliseconds,
toJson: _durationToMilliseconds)
Duration start,
@JsonKey(
name: "IntroEnd",
fromJson: _durationFromMilliseconds,
toJson: _durationToMilliseconds)
Duration end,
@JsonKey(
name: "ShowSkipPromptAt",
fromJson: _durationFromMilliseconds,
toJson: _durationToMilliseconds)
Duration showTime,
@JsonKey(
name: "HideSkipPromptAt",
fromJson: _durationFromMilliseconds,
toJson: _durationToMilliseconds)
Duration hideTime});
}
/// @nodoc
class _$IntroSkipModelCopyWithImpl<$Res, $Val extends IntroSkipModel>
implements $IntroSkipModelCopyWith<$Res> {
_$IntroSkipModelCopyWithImpl(this._value, this._then);
// ignore: unused_field
final $Val _value;
// ignore: unused_field
final $Res Function($Val) _then;
/// Create a copy of IntroSkipModel
/// with the given fields replaced by the non-null parameter values.
@pragma('vm:prefer-inline')
@override
$Res call({
Object? id = null,
Object? valid = null,
Object? start = null,
Object? end = null,
Object? showTime = null,
Object? hideTime = null,
}) {
return _then(_value.copyWith(
id: null == id
? _value.id
: id // ignore: cast_nullable_to_non_nullable
as String,
valid: null == valid
? _value.valid
: valid // ignore: cast_nullable_to_non_nullable
as bool,
start: null == start
? _value.start
: start // ignore: cast_nullable_to_non_nullable
as Duration,
end: null == end
? _value.end
: end // ignore: cast_nullable_to_non_nullable
as Duration,
showTime: null == showTime
? _value.showTime
: showTime // ignore: cast_nullable_to_non_nullable
as Duration,
hideTime: null == hideTime
? _value.hideTime
: hideTime // ignore: cast_nullable_to_non_nullable
as Duration,
) as $Val);
}
}
/// @nodoc
abstract class _$$IntroSkipModelImplCopyWith<$Res>
implements $IntroSkipModelCopyWith<$Res> {
factory _$$IntroSkipModelImplCopyWith(_$IntroSkipModelImpl value,
$Res Function(_$IntroSkipModelImpl) then) =
__$$IntroSkipModelImplCopyWithImpl<$Res>;
@override
@useResult
$Res call(
{@JsonKey(name: "EpisodeId") String id,
@JsonKey(name: "Valid") bool valid,
@JsonKey(
name: "IntroStart",
fromJson: _durationFromMilliseconds,
toJson: _durationToMilliseconds)
Duration start,
@JsonKey(
name: "IntroEnd",
fromJson: _durationFromMilliseconds,
toJson: _durationToMilliseconds)
Duration end,
@JsonKey(
name: "ShowSkipPromptAt",
fromJson: _durationFromMilliseconds,
toJson: _durationToMilliseconds)
Duration showTime,
@JsonKey(
name: "HideSkipPromptAt",
fromJson: _durationFromMilliseconds,
toJson: _durationToMilliseconds)
Duration hideTime});
}
/// @nodoc
class __$$IntroSkipModelImplCopyWithImpl<$Res>
extends _$IntroSkipModelCopyWithImpl<$Res, _$IntroSkipModelImpl>
implements _$$IntroSkipModelImplCopyWith<$Res> {
__$$IntroSkipModelImplCopyWithImpl(
_$IntroSkipModelImpl _value, $Res Function(_$IntroSkipModelImpl) _then)
: super(_value, _then);
/// Create a copy of IntroSkipModel
/// with the given fields replaced by the non-null parameter values.
@pragma('vm:prefer-inline')
@override
$Res call({
Object? id = null,
Object? valid = null,
Object? start = null,
Object? end = null,
Object? showTime = null,
Object? hideTime = null,
}) {
return _then(_$IntroSkipModelImpl(
id: null == id
? _value.id
: id // ignore: cast_nullable_to_non_nullable
as String,
valid: null == valid
? _value.valid
: valid // ignore: cast_nullable_to_non_nullable
as bool,
start: null == start
? _value.start
: start // ignore: cast_nullable_to_non_nullable
as Duration,
end: null == end
? _value.end
: end // ignore: cast_nullable_to_non_nullable
as Duration,
showTime: null == showTime
? _value.showTime
: showTime // ignore: cast_nullable_to_non_nullable
as Duration,
hideTime: null == hideTime
? _value.hideTime
: hideTime // ignore: cast_nullable_to_non_nullable
as Duration,
));
}
}
/// @nodoc
@JsonSerializable()
class _$IntroSkipModelImpl implements _IntroSkipModel {
_$IntroSkipModelImpl(
{@JsonKey(name: "EpisodeId") required this.id,
@JsonKey(name: "Valid") required this.valid,
@JsonKey(
name: "IntroStart",
fromJson: _durationFromMilliseconds,
toJson: _durationToMilliseconds)
required this.start,
@JsonKey(
name: "IntroEnd",
fromJson: _durationFromMilliseconds,
toJson: _durationToMilliseconds)
required this.end,
@JsonKey(
name: "ShowSkipPromptAt",
fromJson: _durationFromMilliseconds,
toJson: _durationToMilliseconds)
required this.showTime,
@JsonKey(
name: "HideSkipPromptAt",
fromJson: _durationFromMilliseconds,
toJson: _durationToMilliseconds)
required this.hideTime});
factory _$IntroSkipModelImpl.fromJson(Map<String, dynamic> json) =>
_$$IntroSkipModelImplFromJson(json);
@override
@JsonKey(name: "EpisodeId")
final String id;
@override
@JsonKey(name: "Valid")
final bool valid;
@override
@JsonKey(
name: "IntroStart",
fromJson: _durationFromMilliseconds,
toJson: _durationToMilliseconds)
final Duration start;
@override
@JsonKey(
name: "IntroEnd",
fromJson: _durationFromMilliseconds,
toJson: _durationToMilliseconds)
final Duration end;
@override
@JsonKey(
name: "ShowSkipPromptAt",
fromJson: _durationFromMilliseconds,
toJson: _durationToMilliseconds)
final Duration showTime;
@override
@JsonKey(
name: "HideSkipPromptAt",
fromJson: _durationFromMilliseconds,
toJson: _durationToMilliseconds)
final Duration hideTime;
@override
String toString() {
return 'IntroSkipModel(id: $id, valid: $valid, start: $start, end: $end, showTime: $showTime, hideTime: $hideTime)';
}
@override
bool operator ==(Object other) {
return identical(this, other) ||
(other.runtimeType == runtimeType &&
other is _$IntroSkipModelImpl &&
(identical(other.id, id) || other.id == id) &&
(identical(other.valid, valid) || other.valid == valid) &&
(identical(other.start, start) || other.start == start) &&
(identical(other.end, end) || other.end == end) &&
(identical(other.showTime, showTime) ||
other.showTime == showTime) &&
(identical(other.hideTime, hideTime) ||
other.hideTime == hideTime));
}
@JsonKey(includeFromJson: false, includeToJson: false)
@override
int get hashCode =>
Object.hash(runtimeType, id, valid, start, end, showTime, hideTime);
/// Create a copy of IntroSkipModel
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
@override
@pragma('vm:prefer-inline')
_$$IntroSkipModelImplCopyWith<_$IntroSkipModelImpl> get copyWith =>
__$$IntroSkipModelImplCopyWithImpl<_$IntroSkipModelImpl>(
this, _$identity);
@override
Map<String, dynamic> toJson() {
return _$$IntroSkipModelImplToJson(
this,
);
}
}
abstract class _IntroSkipModel implements IntroSkipModel {
factory _IntroSkipModel(
{@JsonKey(name: "EpisodeId") required final String id,
@JsonKey(name: "Valid") required final bool valid,
@JsonKey(
name: "IntroStart",
fromJson: _durationFromMilliseconds,
toJson: _durationToMilliseconds)
required final Duration start,
@JsonKey(
name: "IntroEnd",
fromJson: _durationFromMilliseconds,
toJson: _durationToMilliseconds)
required final Duration end,
@JsonKey(
name: "ShowSkipPromptAt",
fromJson: _durationFromMilliseconds,
toJson: _durationToMilliseconds)
required final Duration showTime,
@JsonKey(
name: "HideSkipPromptAt",
fromJson: _durationFromMilliseconds,
toJson: _durationToMilliseconds)
required final Duration hideTime}) = _$IntroSkipModelImpl;
factory _IntroSkipModel.fromJson(Map<String, dynamic> json) =
_$IntroSkipModelImpl.fromJson;
@override
@JsonKey(name: "EpisodeId")
String get id;
@override
@JsonKey(name: "Valid")
bool get valid;
@override
@JsonKey(
name: "IntroStart",
fromJson: _durationFromMilliseconds,
toJson: _durationToMilliseconds)
Duration get start;
@override
@JsonKey(
name: "IntroEnd",
fromJson: _durationFromMilliseconds,
toJson: _durationToMilliseconds)
Duration get end;
@override
@JsonKey(
name: "ShowSkipPromptAt",
fromJson: _durationFromMilliseconds,
toJson: _durationToMilliseconds)
Duration get showTime;
@override
@JsonKey(
name: "HideSkipPromptAt",
fromJson: _durationFromMilliseconds,
toJson: _durationToMilliseconds)
Duration get hideTime;
/// Create a copy of IntroSkipModel
/// with the given fields replaced by the non-null parameter values.
@override
@JsonKey(includeFromJson: false, includeToJson: false)
_$$IntroSkipModelImplCopyWith<_$IntroSkipModelImpl> get copyWith =>
throw _privateConstructorUsedError;
}

View file

@ -1,46 +0,0 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'intro_skip_model.dart';
// **************************************************************************
// JsonSerializableGenerator
// **************************************************************************
_$IntroOutSkipModelImpl _$$IntroOutSkipModelImplFromJson(
Map<String, dynamic> json) =>
_$IntroOutSkipModelImpl(
intro: json['intro'] == null
? null
: IntroSkipModel.fromJson(json['intro'] as Map<String, dynamic>),
credits: json['credits'] == null
? null
: IntroSkipModel.fromJson(json['credits'] as Map<String, dynamic>),
);
Map<String, dynamic> _$$IntroOutSkipModelImplToJson(
_$IntroOutSkipModelImpl instance) =>
<String, dynamic>{
'intro': instance.intro,
'credits': instance.credits,
};
_$IntroSkipModelImpl _$$IntroSkipModelImplFromJson(Map<String, dynamic> json) =>
_$IntroSkipModelImpl(
id: json['EpisodeId'] as String,
valid: json['Valid'] as bool,
start: _durationFromMilliseconds(json['IntroStart'] as num),
end: _durationFromMilliseconds(json['IntroEnd'] as num),
showTime: _durationFromMilliseconds(json['ShowSkipPromptAt'] as num),
hideTime: _durationFromMilliseconds(json['HideSkipPromptAt'] as num),
);
Map<String, dynamic> _$$IntroSkipModelImplToJson(
_$IntroSkipModelImpl instance) =>
<String, dynamic>{
'EpisodeId': instance.id,
'Valid': instance.valid,
'IntroStart': _durationToMilliseconds(instance.start),
'IntroEnd': _durationToMilliseconds(instance.end),
'ShowSkipPromptAt': _durationToMilliseconds(instance.showTime),
'HideSkipPromptAt': _durationToMilliseconds(instance.hideTime),
};

View file

@ -0,0 +1,79 @@
import 'package:flutter/material.dart';
import 'package:collection/collection.dart';
import 'package:freezed_annotation/freezed_annotation.dart';
import 'package:fladder/jellyfin/jellyfin_open_api.swagger.dart' as dto;
part 'media_segments_model.freezed.dart';
part 'media_segments_model.g.dart';
@freezed
class MediaSegmentsModel with _$MediaSegmentsModel {
const MediaSegmentsModel._();
factory MediaSegmentsModel({
@Default([]) List<MediaSegment> segments,
}) = _MediaSegmentsModel;
factory MediaSegmentsModel.fromJson(Map<String, dynamic> json) => _$MediaSegmentsModelFromJson(json);
MediaSegment? get intro => segments.firstWhereOrNull((element) => element.type == MediaSegmentType.intro);
MediaSegment? get outro => segments.firstWhereOrNull((element) => element.type == MediaSegmentType.outro);
}
@freezed
class MediaSegment with _$MediaSegment {
const MediaSegment._();
factory MediaSegment({
required MediaSegmentType type,
required Duration start,
required Duration end,
}) = _MediaSegment;
factory MediaSegment.fromJson(Map<String, dynamic> json) => _$MediaSegmentFromJson(json);
bool inRange(Duration position) => (position.compareTo(start) >= 0 && position.compareTo(end) <= 0);
}
enum MediaSegmentType {
unknown,
commercial,
preview,
recap,
outro,
intro;
Color get color => switch (this) {
MediaSegmentType.unknown => Colors.black,
MediaSegmentType.commercial => Colors.purpleAccent,
MediaSegmentType.preview => Colors.deepOrangeAccent,
MediaSegmentType.recap => Colors.lightBlueAccent,
MediaSegmentType.outro => Colors.yellowAccent,
MediaSegmentType.intro => Colors.greenAccent,
};
static MediaSegmentType fromDto(dto.MediaSegmentType? value) {
return switch (value) {
dto.MediaSegmentType.swaggerGeneratedUnknown => MediaSegmentType.unknown,
dto.MediaSegmentType.unknown => MediaSegmentType.unknown,
dto.MediaSegmentType.commercial => MediaSegmentType.commercial,
dto.MediaSegmentType.preview => MediaSegmentType.preview,
dto.MediaSegmentType.recap => MediaSegmentType.recap,
dto.MediaSegmentType.outro => MediaSegmentType.outro,
dto.MediaSegmentType.intro => MediaSegmentType.intro,
null => MediaSegmentType.unknown,
};
}
}
extension MediaSegmentExtension on dto.MediaSegmentDto {
MediaSegment get toSegment => MediaSegment(
type: MediaSegmentType.fromDto(type),
start: _durationToMilliseconds(startTicks ?? 0),
end: _durationToMilliseconds(endTicks ?? 0),
);
}
Duration _durationToMilliseconds(num milliseconds) => Duration(milliseconds: (milliseconds ~/ 10000));

View file

@ -0,0 +1,363 @@
// coverage:ignore-file
// GENERATED CODE - DO NOT MODIFY BY HAND
// ignore_for_file: type=lint
// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark
part of 'media_segments_model.dart';
// **************************************************************************
// FreezedGenerator
// **************************************************************************
T _$identity<T>(T value) => value;
final _privateConstructorUsedError = UnsupportedError(
'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\nPlease check the documentation here for more information: https://github.com/rrousselGit/freezed#adding-getters-and-methods-to-our-models');
MediaSegmentsModel _$MediaSegmentsModelFromJson(Map<String, dynamic> json) {
return _MediaSegmentsModel.fromJson(json);
}
/// @nodoc
mixin _$MediaSegmentsModel {
List<MediaSegment> get segments => throw _privateConstructorUsedError;
/// Serializes this MediaSegmentsModel to a JSON map.
Map<String, dynamic> toJson() => throw _privateConstructorUsedError;
/// Create a copy of MediaSegmentsModel
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
$MediaSegmentsModelCopyWith<MediaSegmentsModel> get copyWith =>
throw _privateConstructorUsedError;
}
/// @nodoc
abstract class $MediaSegmentsModelCopyWith<$Res> {
factory $MediaSegmentsModelCopyWith(
MediaSegmentsModel value, $Res Function(MediaSegmentsModel) then) =
_$MediaSegmentsModelCopyWithImpl<$Res, MediaSegmentsModel>;
@useResult
$Res call({List<MediaSegment> segments});
}
/// @nodoc
class _$MediaSegmentsModelCopyWithImpl<$Res, $Val extends MediaSegmentsModel>
implements $MediaSegmentsModelCopyWith<$Res> {
_$MediaSegmentsModelCopyWithImpl(this._value, this._then);
// ignore: unused_field
final $Val _value;
// ignore: unused_field
final $Res Function($Val) _then;
/// Create a copy of MediaSegmentsModel
/// with the given fields replaced by the non-null parameter values.
@pragma('vm:prefer-inline')
@override
$Res call({
Object? segments = null,
}) {
return _then(_value.copyWith(
segments: null == segments
? _value.segments
: segments // ignore: cast_nullable_to_non_nullable
as List<MediaSegment>,
) as $Val);
}
}
/// @nodoc
abstract class _$$MediaSegmentsModelImplCopyWith<$Res>
implements $MediaSegmentsModelCopyWith<$Res> {
factory _$$MediaSegmentsModelImplCopyWith(_$MediaSegmentsModelImpl value,
$Res Function(_$MediaSegmentsModelImpl) then) =
__$$MediaSegmentsModelImplCopyWithImpl<$Res>;
@override
@useResult
$Res call({List<MediaSegment> segments});
}
/// @nodoc
class __$$MediaSegmentsModelImplCopyWithImpl<$Res>
extends _$MediaSegmentsModelCopyWithImpl<$Res, _$MediaSegmentsModelImpl>
implements _$$MediaSegmentsModelImplCopyWith<$Res> {
__$$MediaSegmentsModelImplCopyWithImpl(_$MediaSegmentsModelImpl _value,
$Res Function(_$MediaSegmentsModelImpl) _then)
: super(_value, _then);
/// Create a copy of MediaSegmentsModel
/// with the given fields replaced by the non-null parameter values.
@pragma('vm:prefer-inline')
@override
$Res call({
Object? segments = null,
}) {
return _then(_$MediaSegmentsModelImpl(
segments: null == segments
? _value._segments
: segments // ignore: cast_nullable_to_non_nullable
as List<MediaSegment>,
));
}
}
/// @nodoc
@JsonSerializable()
class _$MediaSegmentsModelImpl extends _MediaSegmentsModel {
_$MediaSegmentsModelImpl({final List<MediaSegment> segments = const []})
: _segments = segments,
super._();
factory _$MediaSegmentsModelImpl.fromJson(Map<String, dynamic> json) =>
_$$MediaSegmentsModelImplFromJson(json);
final List<MediaSegment> _segments;
@override
@JsonKey()
List<MediaSegment> get segments {
if (_segments is EqualUnmodifiableListView) return _segments;
// ignore: implicit_dynamic_type
return EqualUnmodifiableListView(_segments);
}
@override
String toString() {
return 'MediaSegmentsModel(segments: $segments)';
}
@override
bool operator ==(Object other) {
return identical(this, other) ||
(other.runtimeType == runtimeType &&
other is _$MediaSegmentsModelImpl &&
const DeepCollectionEquality().equals(other._segments, _segments));
}
@JsonKey(includeFromJson: false, includeToJson: false)
@override
int get hashCode =>
Object.hash(runtimeType, const DeepCollectionEquality().hash(_segments));
/// Create a copy of MediaSegmentsModel
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
@override
@pragma('vm:prefer-inline')
_$$MediaSegmentsModelImplCopyWith<_$MediaSegmentsModelImpl> get copyWith =>
__$$MediaSegmentsModelImplCopyWithImpl<_$MediaSegmentsModelImpl>(
this, _$identity);
@override
Map<String, dynamic> toJson() {
return _$$MediaSegmentsModelImplToJson(
this,
);
}
}
abstract class _MediaSegmentsModel extends MediaSegmentsModel {
factory _MediaSegmentsModel({final List<MediaSegment> segments}) =
_$MediaSegmentsModelImpl;
_MediaSegmentsModel._() : super._();
factory _MediaSegmentsModel.fromJson(Map<String, dynamic> json) =
_$MediaSegmentsModelImpl.fromJson;
@override
List<MediaSegment> get segments;
/// Create a copy of MediaSegmentsModel
/// with the given fields replaced by the non-null parameter values.
@override
@JsonKey(includeFromJson: false, includeToJson: false)
_$$MediaSegmentsModelImplCopyWith<_$MediaSegmentsModelImpl> get copyWith =>
throw _privateConstructorUsedError;
}
MediaSegment _$MediaSegmentFromJson(Map<String, dynamic> json) {
return _MediaSegment.fromJson(json);
}
/// @nodoc
mixin _$MediaSegment {
MediaSegmentType get type => throw _privateConstructorUsedError;
Duration get start => throw _privateConstructorUsedError;
Duration get end => throw _privateConstructorUsedError;
/// Serializes this MediaSegment to a JSON map.
Map<String, dynamic> toJson() => throw _privateConstructorUsedError;
/// Create a copy of MediaSegment
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
$MediaSegmentCopyWith<MediaSegment> get copyWith =>
throw _privateConstructorUsedError;
}
/// @nodoc
abstract class $MediaSegmentCopyWith<$Res> {
factory $MediaSegmentCopyWith(
MediaSegment value, $Res Function(MediaSegment) then) =
_$MediaSegmentCopyWithImpl<$Res, MediaSegment>;
@useResult
$Res call({MediaSegmentType type, Duration start, Duration end});
}
/// @nodoc
class _$MediaSegmentCopyWithImpl<$Res, $Val extends MediaSegment>
implements $MediaSegmentCopyWith<$Res> {
_$MediaSegmentCopyWithImpl(this._value, this._then);
// ignore: unused_field
final $Val _value;
// ignore: unused_field
final $Res Function($Val) _then;
/// Create a copy of MediaSegment
/// with the given fields replaced by the non-null parameter values.
@pragma('vm:prefer-inline')
@override
$Res call({
Object? type = null,
Object? start = null,
Object? end = null,
}) {
return _then(_value.copyWith(
type: null == type
? _value.type
: type // ignore: cast_nullable_to_non_nullable
as MediaSegmentType,
start: null == start
? _value.start
: start // ignore: cast_nullable_to_non_nullable
as Duration,
end: null == end
? _value.end
: end // ignore: cast_nullable_to_non_nullable
as Duration,
) as $Val);
}
}
/// @nodoc
abstract class _$$MediaSegmentImplCopyWith<$Res>
implements $MediaSegmentCopyWith<$Res> {
factory _$$MediaSegmentImplCopyWith(
_$MediaSegmentImpl value, $Res Function(_$MediaSegmentImpl) then) =
__$$MediaSegmentImplCopyWithImpl<$Res>;
@override
@useResult
$Res call({MediaSegmentType type, Duration start, Duration end});
}
/// @nodoc
class __$$MediaSegmentImplCopyWithImpl<$Res>
extends _$MediaSegmentCopyWithImpl<$Res, _$MediaSegmentImpl>
implements _$$MediaSegmentImplCopyWith<$Res> {
__$$MediaSegmentImplCopyWithImpl(
_$MediaSegmentImpl _value, $Res Function(_$MediaSegmentImpl) _then)
: super(_value, _then);
/// Create a copy of MediaSegment
/// with the given fields replaced by the non-null parameter values.
@pragma('vm:prefer-inline')
@override
$Res call({
Object? type = null,
Object? start = null,
Object? end = null,
}) {
return _then(_$MediaSegmentImpl(
type: null == type
? _value.type
: type // ignore: cast_nullable_to_non_nullable
as MediaSegmentType,
start: null == start
? _value.start
: start // ignore: cast_nullable_to_non_nullable
as Duration,
end: null == end
? _value.end
: end // ignore: cast_nullable_to_non_nullable
as Duration,
));
}
}
/// @nodoc
@JsonSerializable()
class _$MediaSegmentImpl extends _MediaSegment {
_$MediaSegmentImpl(
{required this.type, required this.start, required this.end})
: super._();
factory _$MediaSegmentImpl.fromJson(Map<String, dynamic> json) =>
_$$MediaSegmentImplFromJson(json);
@override
final MediaSegmentType type;
@override
final Duration start;
@override
final Duration end;
@override
String toString() {
return 'MediaSegment(type: $type, start: $start, end: $end)';
}
@override
bool operator ==(Object other) {
return identical(this, other) ||
(other.runtimeType == runtimeType &&
other is _$MediaSegmentImpl &&
(identical(other.type, type) || other.type == type) &&
(identical(other.start, start) || other.start == start) &&
(identical(other.end, end) || other.end == end));
}
@JsonKey(includeFromJson: false, includeToJson: false)
@override
int get hashCode => Object.hash(runtimeType, type, start, end);
/// Create a copy of MediaSegment
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
@override
@pragma('vm:prefer-inline')
_$$MediaSegmentImplCopyWith<_$MediaSegmentImpl> get copyWith =>
__$$MediaSegmentImplCopyWithImpl<_$MediaSegmentImpl>(this, _$identity);
@override
Map<String, dynamic> toJson() {
return _$$MediaSegmentImplToJson(
this,
);
}
}
abstract class _MediaSegment extends MediaSegment {
factory _MediaSegment(
{required final MediaSegmentType type,
required final Duration start,
required final Duration end}) = _$MediaSegmentImpl;
_MediaSegment._() : super._();
factory _MediaSegment.fromJson(Map<String, dynamic> json) =
_$MediaSegmentImpl.fromJson;
@override
MediaSegmentType get type;
@override
Duration get start;
@override
Duration get end;
/// Create a copy of MediaSegment
/// with the given fields replaced by the non-null parameter values.
@override
@JsonKey(includeFromJson: false, includeToJson: false)
_$$MediaSegmentImplCopyWith<_$MediaSegmentImpl> get copyWith =>
throw _privateConstructorUsedError;
}

View file

@ -0,0 +1,45 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'media_segments_model.dart';
// **************************************************************************
// JsonSerializableGenerator
// **************************************************************************
_$MediaSegmentsModelImpl _$$MediaSegmentsModelImplFromJson(
Map<String, dynamic> json) =>
_$MediaSegmentsModelImpl(
segments: (json['segments'] as List<dynamic>?)
?.map((e) => MediaSegment.fromJson(e as Map<String, dynamic>))
.toList() ??
const [],
);
Map<String, dynamic> _$$MediaSegmentsModelImplToJson(
_$MediaSegmentsModelImpl instance) =>
<String, dynamic>{
'segments': instance.segments,
};
_$MediaSegmentImpl _$$MediaSegmentImplFromJson(Map<String, dynamic> json) =>
_$MediaSegmentImpl(
type: $enumDecode(_$MediaSegmentTypeEnumMap, json['type']),
start: Duration(microseconds: (json['start'] as num).toInt()),
end: Duration(microseconds: (json['end'] as num).toInt()),
);
Map<String, dynamic> _$$MediaSegmentImplToJson(_$MediaSegmentImpl instance) =>
<String, dynamic>{
'type': _$MediaSegmentTypeEnumMap[instance.type]!,
'start': instance.start.inMicroseconds,
'end': instance.end.inMicroseconds,
};
const _$MediaSegmentTypeEnumMap = {
MediaSegmentType.unknown: 'unknown',
MediaSegmentType.commercial: 'commercial',
MediaSegmentType.preview: 'preview',
MediaSegmentType.recap: 'recap',
MediaSegmentType.outro: 'outro',
MediaSegmentType.intro: 'intro',
};

View file

@ -5,7 +5,7 @@ import 'package:media_kit/media_kit.dart';
import 'package:fladder/jellyfin/jellyfin_open_api.swagger.dart';
import 'package:fladder/models/item_base_model.dart';
import 'package:fladder/models/items/chapters_model.dart';
import 'package:fladder/models/items/intro_skip_model.dart';
import 'package:fladder/models/items/media_segments_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';
@ -23,7 +23,7 @@ class DirectPlaybackModel implements PlaybackModel {
required this.media,
required this.playbackInfo,
this.mediaStreams,
this.introSkipModel,
this.mediaSegments,
this.chapters,
this.trickPlay,
this.queue = const [],
@ -42,7 +42,7 @@ class DirectPlaybackModel implements PlaybackModel {
final MediaStreamsModel? mediaStreams;
@override
final IntroOutSkipModel? introSkipModel;
final MediaSegmentsModel? mediaSegments;
@override
final List<Chapter>? chapters;
@ -180,7 +180,7 @@ class DirectPlaybackModel implements PlaybackModel {
ValueGetter<Duration>? lastPosition,
PlaybackInfoResponse? playbackInfo,
ValueGetter<MediaStreamsModel?>? mediaStreams,
ValueGetter<IntroOutSkipModel?>? introSkipModel,
ValueGetter<MediaSegmentsModel?>? mediaSegments,
ValueGetter<List<Chapter>?>? chapters,
ValueGetter<TrickPlayModel?>? trickPlay,
List<ItemBaseModel>? queue,
@ -190,7 +190,7 @@ class DirectPlaybackModel implements PlaybackModel {
media: media != null ? media() : this.media,
playbackInfo: playbackInfo ?? this.playbackInfo,
mediaStreams: mediaStreams != null ? mediaStreams() : this.mediaStreams,
introSkipModel: introSkipModel != null ? introSkipModel() : this.introSkipModel,
mediaSegments: mediaSegments != null ? mediaSegments() : this.mediaSegments,
chapters: chapters != null ? chapters() : this.chapters,
trickPlay: trickPlay != null ? trickPlay() : this.trickPlay,
queue: queue ?? this.queue,

View file

@ -1,21 +1,21 @@
import 'package:collection/collection.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:media_kit/media_kit.dart';
import 'package:fladder/jellyfin/jellyfin_open_api.swagger.dart';
import 'package:fladder/models/item_base_model.dart';
import 'package:fladder/models/items/chapters_model.dart';
import 'package:fladder/models/items/media_segments_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/syncing/sync_item.dart';
import 'package:fladder/providers/sync_provider.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';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:media_kit/media_kit.dart';
import 'package:fladder/models/item_base_model.dart';
import 'package:fladder/models/items/chapters_model.dart';
import 'package:fladder/models/items/intro_skip_model.dart';
import 'package:fladder/models/items/media_streams_model.dart';
import 'package:fladder/models/playback/playback_model.dart';
import 'package:fladder/providers/sync_provider.dart';
import 'package:fladder/util/duration_extensions.dart';
class OfflinePlaybackModel implements PlaybackModel {
OfflinePlaybackModel({
@ -24,7 +24,7 @@ class OfflinePlaybackModel implements PlaybackModel {
required this.syncedItem,
this.mediaStreams,
this.playbackInfo,
this.introSkipModel,
this.mediaSegments,
this.trickPlay,
this.queue = const [],
this.syncedQueue = const [],
@ -45,7 +45,7 @@ class OfflinePlaybackModel implements PlaybackModel {
final MediaStreamsModel? mediaStreams;
@override
final IntroOutSkipModel? introSkipModel;
final MediaSegmentsModel? mediaSegments;
@override
List<Chapter>? get chapters => syncedItem.chapters;
@ -156,7 +156,7 @@ class OfflinePlaybackModel implements PlaybackModel {
ValueGetter<Media?>? media,
SyncedItem? syncedItem,
ValueGetter<MediaStreamsModel?>? mediaStreams,
ValueGetter<IntroOutSkipModel?>? introSkipModel,
ValueGetter<MediaSegmentsModel?>? mediaSegments,
ValueGetter<TrickPlayModel?>? trickPlay,
List<ItemBaseModel>? queue,
List<SyncedItem>? syncedQueue,
@ -166,7 +166,7 @@ class OfflinePlaybackModel implements PlaybackModel {
media: media != null ? media() : this.media,
syncedItem: syncedItem ?? this.syncedItem,
mediaStreams: mediaStreams != null ? mediaStreams() : this.mediaStreams,
introSkipModel: introSkipModel != null ? introSkipModel() : this.introSkipModel,
mediaSegments: mediaSegments != null ? mediaSegments() : this.mediaSegments,
trickPlay: trickPlay != null ? trickPlay() : this.trickPlay,
queue: queue ?? this.queue,
syncedQueue: syncedQueue ?? this.syncedQueue,

View file

@ -9,7 +9,7 @@ import 'package:fladder/jellyfin/jellyfin_open_api.swagger.dart';
import 'package:fladder/models/item_base_model.dart';
import 'package:fladder/models/items/chapters_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_segments_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';
@ -43,7 +43,7 @@ abstract class PlaybackModel {
final ItemBaseModel item = throw UnimplementedError();
final Media? media = throw UnimplementedError();
final List<ItemBaseModel> queue = const [];
final IntroOutSkipModel? introSkipModel = null;
final MediaSegmentsModel? mediaSegments = null;
final PlaybackInfoResponse? playbackInfo = throw UnimplementedError();
List<Chapter>? get chapters;
@ -118,7 +118,7 @@ class PlaybackModelHelper {
item: syncedItemModel,
syncedItem: syncedItem,
trickPlay: syncedItem.trickPlayModel,
introSkipModel: syncedItem.introOutSkipModel,
mediaSegments: syncedItem.mediaSegments,
media: Media(syncedItem.videoFile.path),
queue: itemQueue.whereNotNull().toList(),
syncedQueue: children,
@ -188,7 +188,7 @@ class PlaybackModelHelper {
defaultSubStreamIndex: streamModel?.defaultSubStreamIndex,
);
final intro = await api.introSkipGet(id: item.id);
final mediaSegments = await api.mediaSegmentsGet(id: item.id);
final trickPlay = (await api.getTrickPlay(item: fullItem.body, ref: ref))?.body;
final chapters = fullItem.body?.overview.chapters ?? [];
@ -214,7 +214,7 @@ class PlaybackModelHelper {
return DirectPlaybackModel(
item: fullItem.body ?? item,
queue: queue,
introSkipModel: intro?.body,
mediaSegments: mediaSegments?.body,
chapters: chapters,
playbackInfo: playbackInfo,
trickPlay: trickPlay,
@ -225,7 +225,7 @@ class PlaybackModelHelper {
return TranscodePlaybackModel(
item: fullItem.body ?? item,
queue: queue,
introSkipModel: intro?.body,
mediaSegments: mediaSegments?.body,
chapters: chapters,
trickPlay: trickPlay,
playbackInfo: playbackInfo,
@ -343,7 +343,7 @@ class PlaybackModelHelper {
newModel = DirectPlaybackModel(
item: playbackModel.item,
queue: playbackModel.queue,
introSkipModel: playbackModel.introSkipModel,
mediaSegments: playbackModel.mediaSegments,
chapters: playbackModel.chapters,
playbackInfo: playbackInfo,
trickPlay: playbackModel.trickPlay,
@ -354,7 +354,7 @@ class PlaybackModelHelper {
newModel = TranscodePlaybackModel(
item: playbackModel.item,
queue: playbackModel.queue,
introSkipModel: playbackModel.introSkipModel,
mediaSegments: playbackModel.mediaSegments,
chapters: playbackModel.chapters,
playbackInfo: playbackInfo,
trickPlay: playbackModel.trickPlay,

View file

@ -5,7 +5,7 @@ import 'package:media_kit/media_kit.dart';
import 'package:fladder/jellyfin/jellyfin_open_api.swagger.dart';
import 'package:fladder/models/item_base_model.dart';
import 'package:fladder/models/items/chapters_model.dart';
import 'package:fladder/models/items/intro_skip_model.dart';
import 'package:fladder/models/items/media_segments_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';
@ -23,7 +23,7 @@ class TranscodePlaybackModel implements PlaybackModel {
required this.media,
required this.playbackInfo,
this.mediaStreams,
this.introSkipModel,
this.mediaSegments,
this.chapters,
this.trickPlay,
this.queue = const [],
@ -42,7 +42,7 @@ class TranscodePlaybackModel implements PlaybackModel {
final MediaStreamsModel? mediaStreams;
@override
final IntroOutSkipModel? introSkipModel;
final MediaSegmentsModel? mediaSegments;
@override
final List<Chapter>? chapters;
@ -181,7 +181,7 @@ class TranscodePlaybackModel implements PlaybackModel {
ValueGetter<Duration>? lastPosition,
PlaybackInfoResponse? playbackInfo,
ValueGetter<MediaStreamsModel?>? mediaStreams,
ValueGetter<IntroOutSkipModel?>? introSkipModel,
ValueGetter<MediaSegmentsModel?>? mediaSegments,
ValueGetter<List<Chapter>?>? chapters,
ValueGetter<TrickPlayModel?>? trickPlay,
List<ItemBaseModel>? queue,
@ -191,7 +191,7 @@ class TranscodePlaybackModel implements PlaybackModel {
media: media != null ? media() : this.media,
playbackInfo: playbackInfo ?? this.playbackInfo,
mediaStreams: mediaStreams != null ? mediaStreams() : this.mediaStreams,
introSkipModel: introSkipModel != null ? introSkipModel() : this.introSkipModel,
mediaSegments: mediaSegments != null ? mediaSegments() : this.mediaSegments,
chapters: chapters != null ? chapters() : this.chapters,
trickPlay: trickPlay != null ? trickPlay() : this.trickPlay,
queue: queue ?? this.queue,

View file

@ -35,7 +35,7 @@ class ISyncedItem {
int? fileSize;
String? videoFileName;
String? trickPlayModel;
String? introOutroSkipModel;
String? mediaSegments;
String? images;
List<String>? chapters;
List<String>? subtitles;
@ -50,7 +50,7 @@ class ISyncedItem {
this.fileSize,
this.videoFileName,
this.trickPlayModel,
this.introOutroSkipModel,
this.mediaSegments,
this.images,
this.chapters,
this.subtitles,
@ -68,8 +68,7 @@ class ISyncedItem {
sortName: syncedItem.sortName,
videoFileName: syncedItem.videoFileName,
trickPlayModel: syncedItem.fTrickPlayModel != null ? jsonEncode(syncedItem.fTrickPlayModel?.toJson()) : null,
introOutroSkipModel:
syncedItem.introOutSkipModel != null ? jsonEncode(syncedItem.introOutSkipModel?.toJson()) : null,
mediaSegments: syncedItem.mediaSegments != null ? jsonEncode(syncedItem.mediaSegments?.toJson()) : null,
images: syncedItem.fImages != null ? jsonEncode(syncedItem.fImages?.toJson()) : null,
chapters: syncedItem.fChapters.map((e) => jsonEncode(e.toJson())).toList(),
subtitles: syncedItem.subtitles.map((e) => jsonEncode(e.toJson())).toList(),

View file

@ -57,7 +57,7 @@ const ISyncedItemSchema = IsarGeneratedSchema(
type: IsarType.string,
),
IsarPropertySchema(
name: 'introOutroSkipModel',
name: 'mediaSegments',
type: IsarType.string,
),
IsarPropertySchema(
@ -141,7 +141,7 @@ int serializeISyncedItem(IsarWriter writer, ISyncedItem object) {
}
}
{
final value = object.introOutroSkipModel;
final value = object.mediaSegments;
if (value == null) {
IsarCore.writeNull(writer, 10);
} else {
@ -218,8 +218,8 @@ ISyncedItem deserializeISyncedItem(IsarReader reader) {
_videoFileName = IsarCore.readString(reader, 8);
final String? _trickPlayModel;
_trickPlayModel = IsarCore.readString(reader, 9);
final String? _introOutroSkipModel;
_introOutroSkipModel = IsarCore.readString(reader, 10);
final String? _mediaSegments;
_mediaSegments = IsarCore.readString(reader, 10);
final String? _images;
_images = IsarCore.readString(reader, 11);
final List<String>? _chapters;
@ -268,7 +268,7 @@ ISyncedItem deserializeISyncedItem(IsarReader reader) {
fileSize: _fileSize,
videoFileName: _videoFileName,
trickPlayModel: _trickPlayModel,
introOutroSkipModel: _introOutroSkipModel,
mediaSegments: _mediaSegments,
images: _images,
chapters: _chapters,
subtitles: _subtitles,
@ -361,7 +361,7 @@ sealed class _ISyncedItemUpdate {
int? fileSize,
String? videoFileName,
String? trickPlayModel,
String? introOutroSkipModel,
String? mediaSegments,
String? images,
String? userData,
});
@ -383,7 +383,7 @@ class _ISyncedItemUpdateImpl implements _ISyncedItemUpdate {
Object? fileSize = ignore,
Object? videoFileName = ignore,
Object? trickPlayModel = ignore,
Object? introOutroSkipModel = ignore,
Object? mediaSegments = ignore,
Object? images = ignore,
Object? userData = ignore,
}) {
@ -398,7 +398,7 @@ class _ISyncedItemUpdateImpl implements _ISyncedItemUpdate {
if (fileSize != ignore) 7: fileSize as int?,
if (videoFileName != ignore) 8: videoFileName as String?,
if (trickPlayModel != ignore) 9: trickPlayModel as String?,
if (introOutroSkipModel != ignore) 10: introOutroSkipModel as String?,
if (mediaSegments != ignore) 10: mediaSegments as String?,
if (images != ignore) 11: images as String?,
if (userData != ignore) 14: userData as String?,
}) >
@ -417,7 +417,7 @@ sealed class _ISyncedItemUpdateAll {
int? fileSize,
String? videoFileName,
String? trickPlayModel,
String? introOutroSkipModel,
String? mediaSegments,
String? images,
String? userData,
});
@ -439,7 +439,7 @@ class _ISyncedItemUpdateAllImpl implements _ISyncedItemUpdateAll {
Object? fileSize = ignore,
Object? videoFileName = ignore,
Object? trickPlayModel = ignore,
Object? introOutroSkipModel = ignore,
Object? mediaSegments = ignore,
Object? images = ignore,
Object? userData = ignore,
}) {
@ -452,7 +452,7 @@ class _ISyncedItemUpdateAllImpl implements _ISyncedItemUpdateAll {
if (fileSize != ignore) 7: fileSize as int?,
if (videoFileName != ignore) 8: videoFileName as String?,
if (trickPlayModel != ignore) 9: trickPlayModel as String?,
if (introOutroSkipModel != ignore) 10: introOutroSkipModel as String?,
if (mediaSegments != ignore) 10: mediaSegments as String?,
if (images != ignore) 11: images as String?,
if (userData != ignore) 14: userData as String?,
});
@ -475,7 +475,7 @@ sealed class _ISyncedItemQueryUpdate {
int? fileSize,
String? videoFileName,
String? trickPlayModel,
String? introOutroSkipModel,
String? mediaSegments,
String? images,
String? userData,
});
@ -497,7 +497,7 @@ class _ISyncedItemQueryUpdateImpl implements _ISyncedItemQueryUpdate {
Object? fileSize = ignore,
Object? videoFileName = ignore,
Object? trickPlayModel = ignore,
Object? introOutroSkipModel = ignore,
Object? mediaSegments = ignore,
Object? images = ignore,
Object? userData = ignore,
}) {
@ -510,7 +510,7 @@ class _ISyncedItemQueryUpdateImpl implements _ISyncedItemQueryUpdate {
if (fileSize != ignore) 7: fileSize as int?,
if (videoFileName != ignore) 8: videoFileName as String?,
if (trickPlayModel != ignore) 9: trickPlayModel as String?,
if (introOutroSkipModel != ignore) 10: introOutroSkipModel as String?,
if (mediaSegments != ignore) 10: mediaSegments as String?,
if (images != ignore) 11: images as String?,
if (userData != ignore) 14: userData as String?,
});
@ -540,7 +540,7 @@ class _ISyncedItemQueryBuilderUpdateImpl implements _ISyncedItemQueryUpdate {
Object? fileSize = ignore,
Object? videoFileName = ignore,
Object? trickPlayModel = ignore,
Object? introOutroSkipModel = ignore,
Object? mediaSegments = ignore,
Object? images = ignore,
Object? userData = ignore,
}) {
@ -555,7 +555,7 @@ class _ISyncedItemQueryBuilderUpdateImpl implements _ISyncedItemQueryUpdate {
if (fileSize != ignore) 7: fileSize as int?,
if (videoFileName != ignore) 8: videoFileName as String?,
if (trickPlayModel != ignore) 9: trickPlayModel as String?,
if (introOutroSkipModel != ignore) 10: introOutroSkipModel as String?,
if (mediaSegments != ignore) 10: mediaSegments as String?,
if (images != ignore) 11: images as String?,
if (userData != ignore) 14: userData as String?,
});
@ -2015,21 +2015,21 @@ extension ISyncedItemQueryFilter
}
QueryBuilder<ISyncedItem, ISyncedItem, QAfterFilterCondition>
introOutroSkipModelIsNull() {
mediaSegmentsIsNull() {
return QueryBuilder.apply(this, (query) {
return query.addFilterCondition(const IsNullCondition(property: 10));
});
}
QueryBuilder<ISyncedItem, ISyncedItem, QAfterFilterCondition>
introOutroSkipModelIsNotNull() {
mediaSegmentsIsNotNull() {
return QueryBuilder.apply(not(), (query) {
return query.addFilterCondition(const IsNullCondition(property: 10));
});
}
QueryBuilder<ISyncedItem, ISyncedItem, QAfterFilterCondition>
introOutroSkipModelEqualTo(
mediaSegmentsEqualTo(
String? value, {
bool caseSensitive = true,
}) {
@ -2045,7 +2045,7 @@ extension ISyncedItemQueryFilter
}
QueryBuilder<ISyncedItem, ISyncedItem, QAfterFilterCondition>
introOutroSkipModelGreaterThan(
mediaSegmentsGreaterThan(
String? value, {
bool caseSensitive = true,
}) {
@ -2061,7 +2061,7 @@ extension ISyncedItemQueryFilter
}
QueryBuilder<ISyncedItem, ISyncedItem, QAfterFilterCondition>
introOutroSkipModelGreaterThanOrEqualTo(
mediaSegmentsGreaterThanOrEqualTo(
String? value, {
bool caseSensitive = true,
}) {
@ -2077,7 +2077,7 @@ extension ISyncedItemQueryFilter
}
QueryBuilder<ISyncedItem, ISyncedItem, QAfterFilterCondition>
introOutroSkipModelLessThan(
mediaSegmentsLessThan(
String? value, {
bool caseSensitive = true,
}) {
@ -2093,7 +2093,7 @@ extension ISyncedItemQueryFilter
}
QueryBuilder<ISyncedItem, ISyncedItem, QAfterFilterCondition>
introOutroSkipModelLessThanOrEqualTo(
mediaSegmentsLessThanOrEqualTo(
String? value, {
bool caseSensitive = true,
}) {
@ -2109,7 +2109,7 @@ extension ISyncedItemQueryFilter
}
QueryBuilder<ISyncedItem, ISyncedItem, QAfterFilterCondition>
introOutroSkipModelBetween(
mediaSegmentsBetween(
String? lower,
String? upper, {
bool caseSensitive = true,
@ -2127,7 +2127,7 @@ extension ISyncedItemQueryFilter
}
QueryBuilder<ISyncedItem, ISyncedItem, QAfterFilterCondition>
introOutroSkipModelStartsWith(
mediaSegmentsStartsWith(
String value, {
bool caseSensitive = true,
}) {
@ -2143,7 +2143,7 @@ extension ISyncedItemQueryFilter
}
QueryBuilder<ISyncedItem, ISyncedItem, QAfterFilterCondition>
introOutroSkipModelEndsWith(
mediaSegmentsEndsWith(
String value, {
bool caseSensitive = true,
}) {
@ -2159,7 +2159,7 @@ extension ISyncedItemQueryFilter
}
QueryBuilder<ISyncedItem, ISyncedItem, QAfterFilterCondition>
introOutroSkipModelContains(String value, {bool caseSensitive = true}) {
mediaSegmentsContains(String value, {bool caseSensitive = true}) {
return QueryBuilder.apply(this, (query) {
return query.addFilterCondition(
ContainsCondition(
@ -2172,7 +2172,7 @@ extension ISyncedItemQueryFilter
}
QueryBuilder<ISyncedItem, ISyncedItem, QAfterFilterCondition>
introOutroSkipModelMatches(String pattern, {bool caseSensitive = true}) {
mediaSegmentsMatches(String pattern, {bool caseSensitive = true}) {
return QueryBuilder.apply(this, (query) {
return query.addFilterCondition(
MatchesCondition(
@ -2185,7 +2185,7 @@ extension ISyncedItemQueryFilter
}
QueryBuilder<ISyncedItem, ISyncedItem, QAfterFilterCondition>
introOutroSkipModelIsEmpty() {
mediaSegmentsIsEmpty() {
return QueryBuilder.apply(this, (query) {
return query.addFilterCondition(
const EqualCondition(
@ -2197,7 +2197,7 @@ extension ISyncedItemQueryFilter
}
QueryBuilder<ISyncedItem, ISyncedItem, QAfterFilterCondition>
introOutroSkipModelIsNotEmpty() {
mediaSegmentsIsNotEmpty() {
return QueryBuilder.apply(this, (query) {
return query.addFilterCondition(
const GreaterCondition(
@ -3189,8 +3189,8 @@ extension ISyncedItemQuerySortBy
});
}
QueryBuilder<ISyncedItem, ISyncedItem, QAfterSortBy>
sortByIntroOutroSkipModel({bool caseSensitive = true}) {
QueryBuilder<ISyncedItem, ISyncedItem, QAfterSortBy> sortByMediaSegments(
{bool caseSensitive = true}) {
return QueryBuilder.apply(this, (query) {
return query.addSortBy(
10,
@ -3199,8 +3199,8 @@ extension ISyncedItemQuerySortBy
});
}
QueryBuilder<ISyncedItem, ISyncedItem, QAfterSortBy>
sortByIntroOutroSkipModelDesc({bool caseSensitive = true}) {
QueryBuilder<ISyncedItem, ISyncedItem, QAfterSortBy> sortByMediaSegmentsDesc(
{bool caseSensitive = true}) {
return QueryBuilder.apply(this, (query) {
return query.addSortBy(
10,
@ -3377,15 +3377,15 @@ extension ISyncedItemQuerySortThenBy
});
}
QueryBuilder<ISyncedItem, ISyncedItem, QAfterSortBy>
thenByIntroOutroSkipModel({bool caseSensitive = true}) {
QueryBuilder<ISyncedItem, ISyncedItem, QAfterSortBy> thenByMediaSegments(
{bool caseSensitive = true}) {
return QueryBuilder.apply(this, (query) {
return query.addSortBy(10, caseSensitive: caseSensitive);
});
}
QueryBuilder<ISyncedItem, ISyncedItem, QAfterSortBy>
thenByIntroOutroSkipModelDesc({bool caseSensitive = true}) {
QueryBuilder<ISyncedItem, ISyncedItem, QAfterSortBy> thenByMediaSegmentsDesc(
{bool caseSensitive = true}) {
return QueryBuilder.apply(this, (query) {
return query.addSortBy(10, sort: Sort.desc, caseSensitive: caseSensitive);
});
@ -3477,7 +3477,7 @@ extension ISyncedItemQueryWhereDistinct
}
QueryBuilder<ISyncedItem, ISyncedItem, QAfterDistinct>
distinctByIntroOutroSkipModel({bool caseSensitive = true}) {
distinctByMediaSegments({bool caseSensitive = true}) {
return QueryBuilder.apply(this, (query) {
return query.addDistinctBy(10, caseSensitive: caseSensitive);
});
@ -3566,8 +3566,7 @@ extension ISyncedItemQueryProperty1
});
}
QueryBuilder<ISyncedItem, String?, QAfterProperty>
introOutroSkipModelProperty() {
QueryBuilder<ISyncedItem, String?, QAfterProperty> mediaSegmentsProperty() {
return QueryBuilder.apply(this, (query) {
return query.addProperty(10);
});
@ -3657,7 +3656,7 @@ extension ISyncedItemQueryProperty2<R>
}
QueryBuilder<ISyncedItem, (R, String?), QAfterProperty>
introOutroSkipModelProperty() {
mediaSegmentsProperty() {
return QueryBuilder.apply(this, (query) {
return query.addProperty(10);
});
@ -3749,7 +3748,7 @@ extension ISyncedItemQueryProperty3<R1, R2>
}
QueryBuilder<ISyncedItem, (R1, R2, String?), QOperations>
introOutroSkipModelProperty() {
mediaSegmentsProperty() {
return QueryBuilder.apply(this, (query) {
return query.addProperty(10);
});

View file

@ -13,7 +13,7 @@ import 'package:fladder/jellyfin/jellyfin_open_api.swagger.dart';
import 'package:fladder/models/item_base_model.dart';
import 'package:fladder/models/items/chapters_model.dart';
import 'package:fladder/models/items/images_models.dart';
import 'package:fladder/models/items/intro_skip_model.dart';
import 'package:fladder/models/items/media_segments_model.dart';
import 'package:fladder/models/items/item_shared_models.dart';
import 'package:fladder/models/items/media_streams_model.dart';
import 'package:fladder/models/items/trick_play_model.dart';
@ -38,7 +38,7 @@ class SyncedItem with _$SyncedItem {
String? sortName,
int? fileSize,
String? videoFileName,
IntroOutSkipModel? introOutSkipModel,
MediaSegmentsModel? mediaSegments,
TrickPlayModel? fTrickPlayModel,
ImagesData? fImages,
@Default([]) List<Chapter> fChapters,
@ -132,8 +132,8 @@ class SyncedItem with _$SyncedItem {
path: joinAll([savePath, isarSyncedItem.path ?? ""]),
fileSize: isarSyncedItem.fileSize,
videoFileName: isarSyncedItem.videoFileName,
introOutSkipModel: isarSyncedItem.introOutroSkipModel != null
? IntroOutSkipModel.fromJson(jsonDecode(isarSyncedItem.introOutroSkipModel!))
mediaSegments: isarSyncedItem.mediaSegments != null
? MediaSegmentsModel.fromJson(jsonDecode(isarSyncedItem.mediaSegments!))
: null,
fTrickPlayModel: isarSyncedItem.trickPlayModel != null
? TrickPlayModel.fromJson(jsonDecode(isarSyncedItem.trickPlayModel!))

View file

@ -25,8 +25,7 @@ mixin _$SyncedItem {
String? get sortName => throw _privateConstructorUsedError;
int? get fileSize => throw _privateConstructorUsedError;
String? get videoFileName => throw _privateConstructorUsedError;
IntroOutSkipModel? get introOutSkipModel =>
throw _privateConstructorUsedError;
MediaSegmentsModel? get mediaSegments => throw _privateConstructorUsedError;
TrickPlayModel? get fTrickPlayModel => throw _privateConstructorUsedError;
ImagesData? get fImages => throw _privateConstructorUsedError;
List<Chapter> get fChapters => throw _privateConstructorUsedError;
@ -57,14 +56,14 @@ abstract class $SyncedItemCopyWith<$Res> {
String? sortName,
int? fileSize,
String? videoFileName,
IntroOutSkipModel? introOutSkipModel,
MediaSegmentsModel? mediaSegments,
TrickPlayModel? fTrickPlayModel,
ImagesData? fImages,
List<Chapter> fChapters,
List<SubStreamModel> subtitles,
@UserDataJsonSerializer() UserData? userData});
$IntroOutSkipModelCopyWith<$Res>? get introOutSkipModel;
$MediaSegmentsModelCopyWith<$Res>? get mediaSegments;
$TrickPlayModelCopyWith<$Res>? get fTrickPlayModel;
}
@ -92,7 +91,7 @@ class _$SyncedItemCopyWithImpl<$Res, $Val extends SyncedItem>
Object? sortName = freezed,
Object? fileSize = freezed,
Object? videoFileName = freezed,
Object? introOutSkipModel = freezed,
Object? mediaSegments = freezed,
Object? fTrickPlayModel = freezed,
Object? fImages = freezed,
Object? fChapters = null,
@ -136,10 +135,10 @@ class _$SyncedItemCopyWithImpl<$Res, $Val extends SyncedItem>
? _value.videoFileName
: videoFileName // ignore: cast_nullable_to_non_nullable
as String?,
introOutSkipModel: freezed == introOutSkipModel
? _value.introOutSkipModel
: introOutSkipModel // ignore: cast_nullable_to_non_nullable
as IntroOutSkipModel?,
mediaSegments: freezed == mediaSegments
? _value.mediaSegments
: mediaSegments // ignore: cast_nullable_to_non_nullable
as MediaSegmentsModel?,
fTrickPlayModel: freezed == fTrickPlayModel
? _value.fTrickPlayModel
: fTrickPlayModel // ignore: cast_nullable_to_non_nullable
@ -167,13 +166,13 @@ class _$SyncedItemCopyWithImpl<$Res, $Val extends SyncedItem>
/// with the given fields replaced by the non-null parameter values.
@override
@pragma('vm:prefer-inline')
$IntroOutSkipModelCopyWith<$Res>? get introOutSkipModel {
if (_value.introOutSkipModel == null) {
$MediaSegmentsModelCopyWith<$Res>? get mediaSegments {
if (_value.mediaSegments == null) {
return null;
}
return $IntroOutSkipModelCopyWith<$Res>(_value.introOutSkipModel!, (value) {
return _then(_value.copyWith(introOutSkipModel: value) as $Val);
return $MediaSegmentsModelCopyWith<$Res>(_value.mediaSegments!, (value) {
return _then(_value.copyWith(mediaSegments: value) as $Val);
});
}
@ -210,7 +209,7 @@ abstract class _$$SyncItemImplCopyWith<$Res>
String? sortName,
int? fileSize,
String? videoFileName,
IntroOutSkipModel? introOutSkipModel,
MediaSegmentsModel? mediaSegments,
TrickPlayModel? fTrickPlayModel,
ImagesData? fImages,
List<Chapter> fChapters,
@ -218,7 +217,7 @@ abstract class _$$SyncItemImplCopyWith<$Res>
@UserDataJsonSerializer() UserData? userData});
@override
$IntroOutSkipModelCopyWith<$Res>? get introOutSkipModel;
$MediaSegmentsModelCopyWith<$Res>? get mediaSegments;
@override
$TrickPlayModelCopyWith<$Res>? get fTrickPlayModel;
}
@ -245,7 +244,7 @@ class __$$SyncItemImplCopyWithImpl<$Res>
Object? sortName = freezed,
Object? fileSize = freezed,
Object? videoFileName = freezed,
Object? introOutSkipModel = freezed,
Object? mediaSegments = freezed,
Object? fTrickPlayModel = freezed,
Object? fImages = freezed,
Object? fChapters = null,
@ -289,10 +288,10 @@ class __$$SyncItemImplCopyWithImpl<$Res>
? _value.videoFileName
: videoFileName // ignore: cast_nullable_to_non_nullable
as String?,
introOutSkipModel: freezed == introOutSkipModel
? _value.introOutSkipModel
: introOutSkipModel // ignore: cast_nullable_to_non_nullable
as IntroOutSkipModel?,
mediaSegments: freezed == mediaSegments
? _value.mediaSegments
: mediaSegments // ignore: cast_nullable_to_non_nullable
as MediaSegmentsModel?,
fTrickPlayModel: freezed == fTrickPlayModel
? _value.fTrickPlayModel
: fTrickPlayModel // ignore: cast_nullable_to_non_nullable
@ -330,7 +329,7 @@ class _$SyncItemImpl extends _SyncItem {
this.sortName,
this.fileSize,
this.videoFileName,
this.introOutSkipModel,
this.mediaSegments,
this.fTrickPlayModel,
this.fImages,
final List<Chapter> fChapters = const [],
@ -361,7 +360,7 @@ class _$SyncItemImpl extends _SyncItem {
@override
final String? videoFileName;
@override
final IntroOutSkipModel? introOutSkipModel;
final MediaSegmentsModel? mediaSegments;
@override
final TrickPlayModel? fTrickPlayModel;
@override
@ -390,7 +389,7 @@ class _$SyncItemImpl extends _SyncItem {
@override
String toString() {
return 'SyncedItem(id: $id, syncing: $syncing, parentId: $parentId, userId: $userId, path: $path, markedForDelete: $markedForDelete, sortName: $sortName, fileSize: $fileSize, videoFileName: $videoFileName, introOutSkipModel: $introOutSkipModel, fTrickPlayModel: $fTrickPlayModel, fImages: $fImages, fChapters: $fChapters, subtitles: $subtitles, userData: $userData)';
return 'SyncedItem(id: $id, syncing: $syncing, parentId: $parentId, userId: $userId, path: $path, markedForDelete: $markedForDelete, sortName: $sortName, fileSize: $fileSize, videoFileName: $videoFileName, mediaSegments: $mediaSegments, fTrickPlayModel: $fTrickPlayModel, fImages: $fImages, fChapters: $fChapters, subtitles: $subtitles, userData: $userData)';
}
@override
@ -412,8 +411,8 @@ class _$SyncItemImpl extends _SyncItem {
other.fileSize == fileSize) &&
(identical(other.videoFileName, videoFileName) ||
other.videoFileName == videoFileName) &&
(identical(other.introOutSkipModel, introOutSkipModel) ||
other.introOutSkipModel == introOutSkipModel) &&
(identical(other.mediaSegments, mediaSegments) ||
other.mediaSegments == mediaSegments) &&
(identical(other.fTrickPlayModel, fTrickPlayModel) ||
other.fTrickPlayModel == fTrickPlayModel) &&
(identical(other.fImages, fImages) || other.fImages == fImages) &&
@ -437,7 +436,7 @@ class _$SyncItemImpl extends _SyncItem {
sortName,
fileSize,
videoFileName,
introOutSkipModel,
mediaSegments,
fTrickPlayModel,
fImages,
const DeepCollectionEquality().hash(_fChapters),
@ -464,7 +463,7 @@ abstract class _SyncItem extends SyncedItem {
final String? sortName,
final int? fileSize,
final String? videoFileName,
final IntroOutSkipModel? introOutSkipModel,
final MediaSegmentsModel? mediaSegments,
final TrickPlayModel? fTrickPlayModel,
final ImagesData? fImages,
final List<Chapter> fChapters,
@ -491,7 +490,7 @@ abstract class _SyncItem extends SyncedItem {
@override
String? get videoFileName;
@override
IntroOutSkipModel? get introOutSkipModel;
MediaSegmentsModel? get mediaSegments;
@override
TrickPlayModel? get fTrickPlayModel;
@override

View file

@ -1,16 +1,17 @@
import 'package:flutter/material.dart';
// ignore_for_file: public_member_api_docs, sort_constructors_first
import 'package:collection/collection.dart';
import 'package:ficonsax/ficonsax.dart';
import 'package:fladder/models/items/item_stream_model.dart';
import 'package:fladder/models/syncing/sync_item.dart';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:media_kit/media_kit.dart';
import 'package:fladder/jellyfin/jellyfin_open_api.swagger.dart';
import 'package:fladder/models/items/chapters_model.dart';
import 'package:fladder/models/items/intro_skip_model.dart';
import 'package:fladder/models/items/item_stream_model.dart';
import 'package:fladder/models/items/media_segments_model.dart';
import 'package:fladder/models/items/media_streams_model.dart';
import 'package:fladder/models/syncing/sync_item.dart';
import 'package:fladder/providers/user_provider.dart';
enum PlaybackType {
@ -100,7 +101,7 @@ class VideoStream {
final String playSessionId;
final List<Chapter>? chapters;
final List<Chapter>? trickPlay;
final IntroSkipModel? introSkipModel;
final MediaSegmentsModel? mediaSegments;
final int? audioStreamIndex;
final int? subtitleStreamIndex;
final MediaStreamsModel? mediaStreamsModel;
@ -129,7 +130,7 @@ class VideoStream {
required this.playSessionId,
this.chapters,
this.trickPlay,
this.introSkipModel,
this.mediaSegments,
this.audioStreamIndex,
this.subtitleStreamIndex,
this.mediaStreamsModel,
@ -143,7 +144,7 @@ class VideoStream {
String? playSessionId,
List<Chapter>? chapters,
List<Chapter>? trickPlay,
IntroSkipModel? introSkipModel,
MediaSegmentsModel? mediaSegments,
int? audioStreamIndex,
int? subtitleStreamIndex,
MediaStreamsModel? mediaStreamsModel,
@ -156,7 +157,7 @@ class VideoStream {
playSessionId: playSessionId ?? this.playSessionId,
chapters: chapters ?? this.chapters,
trickPlay: trickPlay ?? this.trickPlay,
introSkipModel: introSkipModel ?? this.introSkipModel,
mediaSegments: mediaSegments ?? this.mediaSegments,
audioStreamIndex: audioStreamIndex ?? this.audioStreamIndex,
subtitleStreamIndex: subtitleStreamIndex ?? this.subtitleStreamIndex,
mediaStreamsModel: mediaStreamsModel ?? this.mediaStreamsModel,

View file

@ -1,4 +1,3 @@
import 'dart:convert';
import 'dart:developer';
import 'dart:typed_data';
@ -12,7 +11,7 @@ import 'package:fladder/jellyfin/jellyfin_open_api.swagger.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/items/intro_skip_model.dart';
import 'package:fladder/models/items/media_segments_model.dart';
import 'package:fladder/models/items/trick_play_model.dart';
import 'package:fladder/providers/api_provider.dart';
import 'package:fladder/providers/image_provider.dart';
@ -871,20 +870,14 @@ class JellyService {
userId: account?.id,
);
Future<Response<IntroOutSkipModel>?> introSkipGet({
Future<Response<MediaSegmentsModel>?> mediaSegmentsGet({
required String id,
}) async {
try {
final response = await api.episodeIdIntroTimestampsGet(id: id);
final outro = await api.episodeIdIntroSkipperSegmentsGet(id: id);
final map = jsonDecode(outro.bodyString) as Map<String, dynamic>;
final newModel = IntroOutSkipModel(
intro:
map["Introduction"] != null ? IntroSkipModel.fromJson(map["Introduction"] as Map<String, dynamic>) : null,
credits: map["Credits"] != null ? IntroSkipModel.fromJson(map["Credits"] as Map<String, dynamic>) : null,
);
final response = await api.mediaSegmentsItemIdGet(itemId: id);
final newSegments = response.body?.items?.map((e) => e.toSegment).toList() ?? [];
return response.copyWith(
body: newModel,
body: MediaSegmentsModel(segments: newSegments),
);
} catch (e) {
log(e.toString());
@ -927,7 +920,7 @@ class JellyService {
}
}
Future<Response<List<SessionInfo>>> sessionsInfo(String deviceId) async => api.sessionsGet(deviceId: deviceId);
Future<Response<List<SessionInfoDto>>> sessionsInfo(String deviceId) async => api.sessionsGet(deviceId: deviceId);
Future<Response<bool>> quickConnect(String code) async => api.quickConnectAuthorizePost(code: code);

View file

@ -436,12 +436,12 @@ class SyncNotifier extends StateNotifier<SyncSettingsModel> {
final subtitles = await saveExternalSubtitles(newState?.mediaStreamsModel?.subStreams, syncItem);
final trickPlayFile = await saveTrickPlayData(item, directory);
final introOutroSkip = (await api.introSkipGet(id: syncItem.id))?.body;
final mediaSegments = (await api.mediaSegmentsGet(id: syncItem.id))?.body;
syncItem = syncItem.copyWith(
subtitles: subtitles,
fTrickPlayModel: trickPlayFile,
introOutSkipModel: introOutroSkip,
mediaSegments: mediaSegments,
);
await updateItem(syncItem);

View file

@ -1,9 +1,9 @@
import 'package:fladder/providers/video_player_provider.dart';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:media_kit/media_kit.dart';
import 'package:fladder/providers/video_player_provider.dart';
import 'package:fladder/screens/video_player/components/video_player_chapters.dart';
import 'package:fladder/screens/video_player/components/video_player_queue.dart';
@ -84,10 +84,10 @@ class IntroSkipButton extends ConsumerWidget {
}
}
class CreditsSkipButton extends ConsumerWidget {
class OutroSkipButton extends ConsumerWidget {
final bool isOverlayVisible;
final Function()? skipCredits;
const CreditsSkipButton({this.skipCredits, required this.isOverlayVisible, super.key});
final Function()? skipOutro;
const OutroSkipButton({this.skipOutro, required this.isOverlayVisible, super.key});
@override
Widget build(BuildContext context, WidgetRef ref) {
@ -96,7 +96,7 @@ class CreditsSkipButton extends ConsumerWidget {
opacity: 1,
duration: const Duration(milliseconds: 250),
child: ElevatedButton(
onPressed: () => skipCredits?.call(),
onPressed: () => skipOutro?.call(),
style: ElevatedButton.styleFrom(shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(5))),
child: const Padding(
padding: EdgeInsets.all(8),

View file

@ -81,7 +81,7 @@ class _VideoPlayerNextWrapperState extends ConsumerState<VideoPlayerNextWrapper>
return;
}
final credits = ref.read(playBackModel)?.introSkipModel?.credits;
final credits = ref.read(playBackModel)?.mediaSegments?.outro;
if (nextType == AutoNextType.static || credits == null) {
if ((model.duration - model.position).abs() < const Duration(seconds: 32)) {

View file

@ -6,7 +6,7 @@ import 'package:collection/collection.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:fladder/models/items/chapters_model.dart';
import 'package:fladder/models/items/intro_skip_model.dart';
import 'package:fladder/models/items/media_segments_model.dart';
import 'package:fladder/providers/video_player_provider.dart';
import 'package:fladder/util/duration_extensions.dart';
import 'package:fladder/util/list_padding.dart';
@ -54,7 +54,7 @@ class _ChapterProgressSliderState extends ConsumerState<VideoProgressBar> {
final isVisible = (onDragStart ? true : onHoverStart);
final player = ref.watch(videoPlayerProvider);
final position = onDragStart ? currentDuration : widget.position;
final IntroOutSkipModel? introCreditsModel = ref.read(playBackModel.select((value) => value?.introSkipModel));
final MediaSegmentsModel? mediaSegments = ref.read(playBackModel.select((value) => value?.mediaSegments));
final relativeFraction = position.inMilliseconds / widget.duration.inMilliseconds;
return LayoutBuilder(
builder: (context, constraints) {
@ -138,35 +138,21 @@ class _ChapterProgressSliderState extends ConsumerState<VideoProgressBar> {
child: Stack(
alignment: Alignment.center,
children: [
if (introCreditsModel?.intro?.start != null && introCreditsModel?.intro?.end != null)
Positioned(
left: calculateStartOffset(constraints, introCreditsModel!.intro!.start),
right: calculateRightOffset(constraints, introCreditsModel.intro!.end),
...?mediaSegments?.segments.map(
(segment) => Positioned(
left: calculateStartOffset(constraints, segment.start),
right: calculateRightOffset(constraints, segment.end),
bottom: 0,
child: Container(
height: 6,
decoration: BoxDecoration(
color: Colors.greenAccent.withOpacity(0.8),
color: segment.type.color,
borderRadius: BorderRadius.circular(
100,
),
),
),
),
if (introCreditsModel?.credits?.start != null && introCreditsModel?.credits?.end != null)
Positioned(
left: calculateStartOffset(constraints, introCreditsModel!.credits!.start),
right: calculateRightOffset(constraints, introCreditsModel.credits!.end),
bottom: 0,
child: Container(
height: 6,
decoration: BoxDecoration(
color: Colors.orangeAccent.withOpacity(0.8),
borderRadius: BorderRadius.circular(
100,
),
),
),
),
if (!widget.buffering) ...{
//VideoBufferBar

View file

@ -13,7 +13,7 @@ import 'package:screen_brightness/screen_brightness.dart';
import 'package:universal_html/html.dart' as html;
import 'package:window_manager/window_manager.dart';
import 'package:fladder/models/items/intro_skip_model.dart';
import 'package:fladder/models/items/media_segments_model.dart';
import 'package:fladder/models/media_playback_model.dart';
import 'package:fladder/models/playback/playback_model.dart';
import 'package:fladder/providers/settings/client_settings_provider.dart';
@ -57,10 +57,10 @@ class _DesktopControlsState extends ConsumerState<DesktopControls> {
late final double bottomPadding = MediaQuery.of(context).viewPadding.bottom;
bool _onKey(KeyEvent value) {
final introSkipModel = ref.read(playBackModel.select((value) => value?.introSkipModel));
final mediaSegments = ref.read(playBackModel.select((value) => value?.mediaSegments));
final position = ref.read(mediaPlaybackProvider).position;
bool showIntroSkipButton = introSkipModel?.introInRange(position) ?? false;
bool showCreditSkipButton = introSkipModel?.creditsInRange(position) ?? false;
bool showIntroSkipButton = mediaSegments?.intro?.inRange(position) ?? false;
bool showOutroSkipButton = mediaSegments?.outro?.inRange(position) ?? false;
if (value is KeyRepeatEvent) {
if (value.logicalKey == LogicalKeyboardKey.arrowUp) {
resetTimer();
@ -76,9 +76,9 @@ class _DesktopControlsState extends ConsumerState<DesktopControls> {
if (value is KeyDownEvent) {
if (value.logicalKey == LogicalKeyboardKey.keyS) {
if (showIntroSkipButton) {
skipIntro(introSkipModel);
} else if (showCreditSkipButton) {
skipCredits(introSkipModel);
skipIntro(mediaSegments);
} else if (showOutroSkipButton) {
skipOutro(mediaSegments);
}
return true;
}
@ -116,7 +116,7 @@ class _DesktopControlsState extends ConsumerState<DesktopControls> {
@override
Widget build(BuildContext context) {
final introSkipModel = ref.watch(playBackModel.select((value) => value?.introSkipModel));
final mediaSegments = ref.watch(playBackModel.select((value) => value?.mediaSegments));
final player = ref.watch(videoPlayerProvider.select((value) => value.controller));
return InputHandler(
autoFocus: false,
@ -167,8 +167,8 @@ class _DesktopControlsState extends ConsumerState<DesktopControls> {
Consumer(
builder: (context, ref, child) {
final position = ref.watch(mediaPlaybackProvider.select((value) => value.position));
bool showIntroSkipButton = introSkipModel?.introInRange(position) ?? false;
bool showCreditSkipButton = introSkipModel?.creditsInRange(position) ?? false;
bool showIntroSkipButton = mediaSegments?.intro?.inRange(position) ?? false;
bool showOutroSkipButton = mediaSegments?.outro?.inRange(position) ?? false;
return Stack(
children: [
if (showIntroSkipButton)
@ -178,18 +178,18 @@ class _DesktopControlsState extends ConsumerState<DesktopControls> {
padding: const EdgeInsets.all(32),
child: IntroSkipButton(
isOverlayVisible: showOverlay,
skipIntro: () => skipIntro(introSkipModel),
skipIntro: () => skipIntro(mediaSegments),
),
),
),
if (showCreditSkipButton)
if (showOutroSkipButton)
Align(
alignment: Alignment.centerRight,
child: Padding(
padding: const EdgeInsets.all(32),
child: CreditsSkipButton(
child: OutroSkipButton(
isOverlayVisible: showOverlay,
skipCredits: () => skipCredits(introSkipModel),
skipOutro: () => skipOutro(mediaSegments),
),
),
)
@ -582,17 +582,17 @@ class _DesktopControlsState extends ConsumerState<DesktopControls> {
);
}
void skipIntro(IntroOutSkipModel? introSkipModel) {
void skipIntro(MediaSegmentsModel? mediaSegments) {
resetTimer();
final end = introSkipModel?.intro?.end;
final end = mediaSegments?.intro?.end;
if (end != null) {
ref.read(videoPlayerProvider).seek(end);
}
}
void skipCredits(IntroOutSkipModel? introSkipModel) {
void skipOutro(MediaSegmentsModel? mediaSegments) {
resetTimer();
final end = introSkipModel?.credits?.end;
final end = mediaSegments?.outro?.end;
if (end != null) {
ref.read(videoPlayerProvider).seek(end);
}

View file

@ -6,7 +6,6 @@ import 'package:transparent_image/transparent_image.dart';
import 'package:fladder/models/items/images_models.dart';
import 'package:fladder/providers/settings/client_settings_provider.dart';
import 'package:fladder/util/adaptive_layout.dart';
class FladderImage extends ConsumerWidget {
final ImageData? image;
@ -29,7 +28,6 @@ class FladderImage extends ConsumerWidget {
Widget build(BuildContext context, WidgetRef ref) {
final useBluredPlaceHolder = ref.watch(clientSettingsProvider.select((value) => value.blurPlaceHolders));
final newImage = image;
final blurSize = AdaptiveLayout.of(context).isDesktop ? 32 : 16;
if (newImage == null) {
return placeHolder ?? Container();
} else {
@ -44,8 +42,6 @@ class FladderImage extends ConsumerWidget {
filterQuality: FilterQuality.low,
image: BlurHashImage(
newImage.hash,
decodingWidth: blurSize,
decodingHeight: blurSize,
),
),
if (!blurOnly)

File diff suppressed because it is too large Load diff