mirror of
https://github.com/gabehf/Fladder.git
synced 2026-03-08 15:08:18 -07:00
Init repo
This commit is contained in:
commit
764b6034e3
566 changed files with 212335 additions and 0 deletions
107
lib/models/account_model.dart
Normal file
107
lib/models/account_model.dart
Normal file
|
|
@ -0,0 +1,107 @@
|
|||
// ignore_for_file: public_member_api_docs, sort_constructors_first, invalid_annotation_target
|
||||
|
||||
import 'package:ficonsax/ficonsax.dart';
|
||||
import 'package:fladder/util/localization_helper.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/widgets.dart';
|
||||
|
||||
import 'package:fladder/jellyfin/jellyfin_open_api.swagger.dart';
|
||||
import 'package:fladder/models/credentials_model.dart';
|
||||
import 'package:fladder/util/adaptive_layout.dart';
|
||||
|
||||
import 'package:freezed_annotation/freezed_annotation.dart';
|
||||
|
||||
part 'account_model.freezed.dart';
|
||||
part 'account_model.g.dart';
|
||||
|
||||
@freezed
|
||||
class AccountModel with _$AccountModel {
|
||||
const AccountModel._();
|
||||
|
||||
const factory AccountModel({
|
||||
required String name,
|
||||
required String id,
|
||||
required String avatar,
|
||||
required DateTime lastUsed,
|
||||
@Default(Authentication.autoLogin) Authentication authMethod,
|
||||
@Default("") String localPin,
|
||||
required CredentialsModel credentials,
|
||||
@Default([]) List<String> latestItemsExcludes,
|
||||
@Default([]) List<String> searchQueryHistory,
|
||||
@Default(false) bool quickConnectState,
|
||||
@JsonKey(includeFromJson: false, includeToJson: false) UserPolicy? policy,
|
||||
@JsonKey(includeFromJson: false, includeToJson: false) ServerConfiguration? serverConfiguration,
|
||||
}) = _AccountModel;
|
||||
|
||||
factory AccountModel.fromJson(Map<String, dynamic> json) => _$AccountModelFromJson(json);
|
||||
|
||||
String get server {
|
||||
return credentials.server;
|
||||
}
|
||||
|
||||
bool get canDownload {
|
||||
return (policy?.enableContentDownloading ?? false) && !kIsWeb;
|
||||
}
|
||||
|
||||
//Check if it's the same account on the same server
|
||||
bool sameIdentity(AccountModel other) {
|
||||
if (identical(this, other)) return true;
|
||||
return other.id == id && other.credentials.serverId == credentials.serverId;
|
||||
}
|
||||
}
|
||||
|
||||
enum Authentication {
|
||||
autoLogin(0),
|
||||
biometrics(1),
|
||||
passcode(2),
|
||||
none(3);
|
||||
|
||||
const Authentication(this.value);
|
||||
final int value;
|
||||
|
||||
bool available(BuildContext context) {
|
||||
switch (this) {
|
||||
case Authentication.none:
|
||||
case Authentication.autoLogin:
|
||||
case Authentication.passcode:
|
||||
return true;
|
||||
case Authentication.biometrics:
|
||||
return !AdaptiveLayout.of(context).isDesktop;
|
||||
}
|
||||
}
|
||||
|
||||
String name(BuildContext context) {
|
||||
switch (this) {
|
||||
case Authentication.none:
|
||||
return context.localized.none;
|
||||
case Authentication.autoLogin:
|
||||
return context.localized.appLockAutoLogin;
|
||||
case Authentication.biometrics:
|
||||
return context.localized.appLockBiometrics;
|
||||
case Authentication.passcode:
|
||||
return context.localized.appLockPasscode;
|
||||
}
|
||||
}
|
||||
|
||||
IconData get icon {
|
||||
switch (this) {
|
||||
case Authentication.none:
|
||||
return IconsaxBold.arrow_bottom;
|
||||
case Authentication.autoLogin:
|
||||
return IconsaxOutline.login_1;
|
||||
case Authentication.biometrics:
|
||||
return IconsaxOutline.finger_scan;
|
||||
case Authentication.passcode:
|
||||
return IconsaxOutline.password_check;
|
||||
}
|
||||
}
|
||||
|
||||
static Authentication fromMap(int value) {
|
||||
return Authentication.values[value];
|
||||
}
|
||||
|
||||
int toMap() {
|
||||
return value;
|
||||
}
|
||||
}
|
||||
450
lib/models/account_model.freezed.dart
Normal file
450
lib/models/account_model.freezed.dart
Normal file
|
|
@ -0,0 +1,450 @@
|
|||
// 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 'account_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');
|
||||
|
||||
AccountModel _$AccountModelFromJson(Map<String, dynamic> json) {
|
||||
return _AccountModel.fromJson(json);
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
mixin _$AccountModel {
|
||||
String get name => throw _privateConstructorUsedError;
|
||||
String get id => throw _privateConstructorUsedError;
|
||||
String get avatar => throw _privateConstructorUsedError;
|
||||
DateTime get lastUsed => throw _privateConstructorUsedError;
|
||||
Authentication get authMethod => throw _privateConstructorUsedError;
|
||||
String get localPin => throw _privateConstructorUsedError;
|
||||
CredentialsModel get credentials => throw _privateConstructorUsedError;
|
||||
List<String> get latestItemsExcludes => throw _privateConstructorUsedError;
|
||||
List<String> get searchQueryHistory => throw _privateConstructorUsedError;
|
||||
bool get quickConnectState => throw _privateConstructorUsedError;
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
UserPolicy? get policy => throw _privateConstructorUsedError;
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
ServerConfiguration? get serverConfiguration =>
|
||||
throw _privateConstructorUsedError;
|
||||
|
||||
Map<String, dynamic> toJson() => throw _privateConstructorUsedError;
|
||||
@JsonKey(ignore: true)
|
||||
$AccountModelCopyWith<AccountModel> get copyWith =>
|
||||
throw _privateConstructorUsedError;
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
abstract class $AccountModelCopyWith<$Res> {
|
||||
factory $AccountModelCopyWith(
|
||||
AccountModel value, $Res Function(AccountModel) then) =
|
||||
_$AccountModelCopyWithImpl<$Res, AccountModel>;
|
||||
@useResult
|
||||
$Res call(
|
||||
{String name,
|
||||
String id,
|
||||
String avatar,
|
||||
DateTime lastUsed,
|
||||
Authentication authMethod,
|
||||
String localPin,
|
||||
CredentialsModel credentials,
|
||||
List<String> latestItemsExcludes,
|
||||
List<String> searchQueryHistory,
|
||||
bool quickConnectState,
|
||||
@JsonKey(includeFromJson: false, includeToJson: false) UserPolicy? policy,
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
ServerConfiguration? serverConfiguration});
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
class _$AccountModelCopyWithImpl<$Res, $Val extends AccountModel>
|
||||
implements $AccountModelCopyWith<$Res> {
|
||||
_$AccountModelCopyWithImpl(this._value, this._then);
|
||||
|
||||
// ignore: unused_field
|
||||
final $Val _value;
|
||||
// ignore: unused_field
|
||||
final $Res Function($Val) _then;
|
||||
|
||||
@pragma('vm:prefer-inline')
|
||||
@override
|
||||
$Res call({
|
||||
Object? name = null,
|
||||
Object? id = null,
|
||||
Object? avatar = null,
|
||||
Object? lastUsed = null,
|
||||
Object? authMethod = null,
|
||||
Object? localPin = null,
|
||||
Object? credentials = null,
|
||||
Object? latestItemsExcludes = null,
|
||||
Object? searchQueryHistory = null,
|
||||
Object? quickConnectState = null,
|
||||
Object? policy = freezed,
|
||||
Object? serverConfiguration = freezed,
|
||||
}) {
|
||||
return _then(_value.copyWith(
|
||||
name: null == name
|
||||
? _value.name
|
||||
: name // ignore: cast_nullable_to_non_nullable
|
||||
as String,
|
||||
id: null == id
|
||||
? _value.id
|
||||
: id // ignore: cast_nullable_to_non_nullable
|
||||
as String,
|
||||
avatar: null == avatar
|
||||
? _value.avatar
|
||||
: avatar // ignore: cast_nullable_to_non_nullable
|
||||
as String,
|
||||
lastUsed: null == lastUsed
|
||||
? _value.lastUsed
|
||||
: lastUsed // ignore: cast_nullable_to_non_nullable
|
||||
as DateTime,
|
||||
authMethod: null == authMethod
|
||||
? _value.authMethod
|
||||
: authMethod // ignore: cast_nullable_to_non_nullable
|
||||
as Authentication,
|
||||
localPin: null == localPin
|
||||
? _value.localPin
|
||||
: localPin // ignore: cast_nullable_to_non_nullable
|
||||
as String,
|
||||
credentials: null == credentials
|
||||
? _value.credentials
|
||||
: credentials // ignore: cast_nullable_to_non_nullable
|
||||
as CredentialsModel,
|
||||
latestItemsExcludes: null == latestItemsExcludes
|
||||
? _value.latestItemsExcludes
|
||||
: latestItemsExcludes // ignore: cast_nullable_to_non_nullable
|
||||
as List<String>,
|
||||
searchQueryHistory: null == searchQueryHistory
|
||||
? _value.searchQueryHistory
|
||||
: searchQueryHistory // ignore: cast_nullable_to_non_nullable
|
||||
as List<String>,
|
||||
quickConnectState: null == quickConnectState
|
||||
? _value.quickConnectState
|
||||
: quickConnectState // ignore: cast_nullable_to_non_nullable
|
||||
as bool,
|
||||
policy: freezed == policy
|
||||
? _value.policy
|
||||
: policy // ignore: cast_nullable_to_non_nullable
|
||||
as UserPolicy?,
|
||||
serverConfiguration: freezed == serverConfiguration
|
||||
? _value.serverConfiguration
|
||||
: serverConfiguration // ignore: cast_nullable_to_non_nullable
|
||||
as ServerConfiguration?,
|
||||
) as $Val);
|
||||
}
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
abstract class _$$AccountModelImplCopyWith<$Res>
|
||||
implements $AccountModelCopyWith<$Res> {
|
||||
factory _$$AccountModelImplCopyWith(
|
||||
_$AccountModelImpl value, $Res Function(_$AccountModelImpl) then) =
|
||||
__$$AccountModelImplCopyWithImpl<$Res>;
|
||||
@override
|
||||
@useResult
|
||||
$Res call(
|
||||
{String name,
|
||||
String id,
|
||||
String avatar,
|
||||
DateTime lastUsed,
|
||||
Authentication authMethod,
|
||||
String localPin,
|
||||
CredentialsModel credentials,
|
||||
List<String> latestItemsExcludes,
|
||||
List<String> searchQueryHistory,
|
||||
bool quickConnectState,
|
||||
@JsonKey(includeFromJson: false, includeToJson: false) UserPolicy? policy,
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
ServerConfiguration? serverConfiguration});
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
class __$$AccountModelImplCopyWithImpl<$Res>
|
||||
extends _$AccountModelCopyWithImpl<$Res, _$AccountModelImpl>
|
||||
implements _$$AccountModelImplCopyWith<$Res> {
|
||||
__$$AccountModelImplCopyWithImpl(
|
||||
_$AccountModelImpl _value, $Res Function(_$AccountModelImpl) _then)
|
||||
: super(_value, _then);
|
||||
|
||||
@pragma('vm:prefer-inline')
|
||||
@override
|
||||
$Res call({
|
||||
Object? name = null,
|
||||
Object? id = null,
|
||||
Object? avatar = null,
|
||||
Object? lastUsed = null,
|
||||
Object? authMethod = null,
|
||||
Object? localPin = null,
|
||||
Object? credentials = null,
|
||||
Object? latestItemsExcludes = null,
|
||||
Object? searchQueryHistory = null,
|
||||
Object? quickConnectState = null,
|
||||
Object? policy = freezed,
|
||||
Object? serverConfiguration = freezed,
|
||||
}) {
|
||||
return _then(_$AccountModelImpl(
|
||||
name: null == name
|
||||
? _value.name
|
||||
: name // ignore: cast_nullable_to_non_nullable
|
||||
as String,
|
||||
id: null == id
|
||||
? _value.id
|
||||
: id // ignore: cast_nullable_to_non_nullable
|
||||
as String,
|
||||
avatar: null == avatar
|
||||
? _value.avatar
|
||||
: avatar // ignore: cast_nullable_to_non_nullable
|
||||
as String,
|
||||
lastUsed: null == lastUsed
|
||||
? _value.lastUsed
|
||||
: lastUsed // ignore: cast_nullable_to_non_nullable
|
||||
as DateTime,
|
||||
authMethod: null == authMethod
|
||||
? _value.authMethod
|
||||
: authMethod // ignore: cast_nullable_to_non_nullable
|
||||
as Authentication,
|
||||
localPin: null == localPin
|
||||
? _value.localPin
|
||||
: localPin // ignore: cast_nullable_to_non_nullable
|
||||
as String,
|
||||
credentials: null == credentials
|
||||
? _value.credentials
|
||||
: credentials // ignore: cast_nullable_to_non_nullable
|
||||
as CredentialsModel,
|
||||
latestItemsExcludes: null == latestItemsExcludes
|
||||
? _value._latestItemsExcludes
|
||||
: latestItemsExcludes // ignore: cast_nullable_to_non_nullable
|
||||
as List<String>,
|
||||
searchQueryHistory: null == searchQueryHistory
|
||||
? _value._searchQueryHistory
|
||||
: searchQueryHistory // ignore: cast_nullable_to_non_nullable
|
||||
as List<String>,
|
||||
quickConnectState: null == quickConnectState
|
||||
? _value.quickConnectState
|
||||
: quickConnectState // ignore: cast_nullable_to_non_nullable
|
||||
as bool,
|
||||
policy: freezed == policy
|
||||
? _value.policy
|
||||
: policy // ignore: cast_nullable_to_non_nullable
|
||||
as UserPolicy?,
|
||||
serverConfiguration: freezed == serverConfiguration
|
||||
? _value.serverConfiguration
|
||||
: serverConfiguration // ignore: cast_nullable_to_non_nullable
|
||||
as ServerConfiguration?,
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
@JsonSerializable()
|
||||
class _$AccountModelImpl extends _AccountModel with DiagnosticableTreeMixin {
|
||||
const _$AccountModelImpl(
|
||||
{required this.name,
|
||||
required this.id,
|
||||
required this.avatar,
|
||||
required this.lastUsed,
|
||||
this.authMethod = Authentication.autoLogin,
|
||||
this.localPin = "",
|
||||
required this.credentials,
|
||||
final List<String> latestItemsExcludes = const [],
|
||||
final List<String> searchQueryHistory = const [],
|
||||
this.quickConnectState = false,
|
||||
@JsonKey(includeFromJson: false, includeToJson: false) this.policy,
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
this.serverConfiguration})
|
||||
: _latestItemsExcludes = latestItemsExcludes,
|
||||
_searchQueryHistory = searchQueryHistory,
|
||||
super._();
|
||||
|
||||
factory _$AccountModelImpl.fromJson(Map<String, dynamic> json) =>
|
||||
_$$AccountModelImplFromJson(json);
|
||||
|
||||
@override
|
||||
final String name;
|
||||
@override
|
||||
final String id;
|
||||
@override
|
||||
final String avatar;
|
||||
@override
|
||||
final DateTime lastUsed;
|
||||
@override
|
||||
@JsonKey()
|
||||
final Authentication authMethod;
|
||||
@override
|
||||
@JsonKey()
|
||||
final String localPin;
|
||||
@override
|
||||
final CredentialsModel credentials;
|
||||
final List<String> _latestItemsExcludes;
|
||||
@override
|
||||
@JsonKey()
|
||||
List<String> get latestItemsExcludes {
|
||||
if (_latestItemsExcludes is EqualUnmodifiableListView)
|
||||
return _latestItemsExcludes;
|
||||
// ignore: implicit_dynamic_type
|
||||
return EqualUnmodifiableListView(_latestItemsExcludes);
|
||||
}
|
||||
|
||||
final List<String> _searchQueryHistory;
|
||||
@override
|
||||
@JsonKey()
|
||||
List<String> get searchQueryHistory {
|
||||
if (_searchQueryHistory is EqualUnmodifiableListView)
|
||||
return _searchQueryHistory;
|
||||
// ignore: implicit_dynamic_type
|
||||
return EqualUnmodifiableListView(_searchQueryHistory);
|
||||
}
|
||||
|
||||
@override
|
||||
@JsonKey()
|
||||
final bool quickConnectState;
|
||||
@override
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
final UserPolicy? policy;
|
||||
@override
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
final ServerConfiguration? serverConfiguration;
|
||||
|
||||
@override
|
||||
String toString({DiagnosticLevel minLevel = DiagnosticLevel.info}) {
|
||||
return 'AccountModel(name: $name, id: $id, avatar: $avatar, lastUsed: $lastUsed, authMethod: $authMethod, localPin: $localPin, credentials: $credentials, latestItemsExcludes: $latestItemsExcludes, searchQueryHistory: $searchQueryHistory, quickConnectState: $quickConnectState, policy: $policy, serverConfiguration: $serverConfiguration)';
|
||||
}
|
||||
|
||||
@override
|
||||
void debugFillProperties(DiagnosticPropertiesBuilder properties) {
|
||||
super.debugFillProperties(properties);
|
||||
properties
|
||||
..add(DiagnosticsProperty('type', 'AccountModel'))
|
||||
..add(DiagnosticsProperty('name', name))
|
||||
..add(DiagnosticsProperty('id', id))
|
||||
..add(DiagnosticsProperty('avatar', avatar))
|
||||
..add(DiagnosticsProperty('lastUsed', lastUsed))
|
||||
..add(DiagnosticsProperty('authMethod', authMethod))
|
||||
..add(DiagnosticsProperty('localPin', localPin))
|
||||
..add(DiagnosticsProperty('credentials', credentials))
|
||||
..add(DiagnosticsProperty('latestItemsExcludes', latestItemsExcludes))
|
||||
..add(DiagnosticsProperty('searchQueryHistory', searchQueryHistory))
|
||||
..add(DiagnosticsProperty('quickConnectState', quickConnectState))
|
||||
..add(DiagnosticsProperty('policy', policy))
|
||||
..add(DiagnosticsProperty('serverConfiguration', serverConfiguration));
|
||||
}
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
return identical(this, other) ||
|
||||
(other.runtimeType == runtimeType &&
|
||||
other is _$AccountModelImpl &&
|
||||
(identical(other.name, name) || other.name == name) &&
|
||||
(identical(other.id, id) || other.id == id) &&
|
||||
(identical(other.avatar, avatar) || other.avatar == avatar) &&
|
||||
(identical(other.lastUsed, lastUsed) ||
|
||||
other.lastUsed == lastUsed) &&
|
||||
(identical(other.authMethod, authMethod) ||
|
||||
other.authMethod == authMethod) &&
|
||||
(identical(other.localPin, localPin) ||
|
||||
other.localPin == localPin) &&
|
||||
(identical(other.credentials, credentials) ||
|
||||
other.credentials == credentials) &&
|
||||
const DeepCollectionEquality()
|
||||
.equals(other._latestItemsExcludes, _latestItemsExcludes) &&
|
||||
const DeepCollectionEquality()
|
||||
.equals(other._searchQueryHistory, _searchQueryHistory) &&
|
||||
(identical(other.quickConnectState, quickConnectState) ||
|
||||
other.quickConnectState == quickConnectState) &&
|
||||
(identical(other.policy, policy) || other.policy == policy) &&
|
||||
(identical(other.serverConfiguration, serverConfiguration) ||
|
||||
other.serverConfiguration == serverConfiguration));
|
||||
}
|
||||
|
||||
@JsonKey(ignore: true)
|
||||
@override
|
||||
int get hashCode => Object.hash(
|
||||
runtimeType,
|
||||
name,
|
||||
id,
|
||||
avatar,
|
||||
lastUsed,
|
||||
authMethod,
|
||||
localPin,
|
||||
credentials,
|
||||
const DeepCollectionEquality().hash(_latestItemsExcludes),
|
||||
const DeepCollectionEquality().hash(_searchQueryHistory),
|
||||
quickConnectState,
|
||||
policy,
|
||||
serverConfiguration);
|
||||
|
||||
@JsonKey(ignore: true)
|
||||
@override
|
||||
@pragma('vm:prefer-inline')
|
||||
_$$AccountModelImplCopyWith<_$AccountModelImpl> get copyWith =>
|
||||
__$$AccountModelImplCopyWithImpl<_$AccountModelImpl>(this, _$identity);
|
||||
|
||||
@override
|
||||
Map<String, dynamic> toJson() {
|
||||
return _$$AccountModelImplToJson(
|
||||
this,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
abstract class _AccountModel extends AccountModel {
|
||||
const factory _AccountModel(
|
||||
{required final String name,
|
||||
required final String id,
|
||||
required final String avatar,
|
||||
required final DateTime lastUsed,
|
||||
final Authentication authMethod,
|
||||
final String localPin,
|
||||
required final CredentialsModel credentials,
|
||||
final List<String> latestItemsExcludes,
|
||||
final List<String> searchQueryHistory,
|
||||
final bool quickConnectState,
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
final UserPolicy? policy,
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
final ServerConfiguration? serverConfiguration}) = _$AccountModelImpl;
|
||||
const _AccountModel._() : super._();
|
||||
|
||||
factory _AccountModel.fromJson(Map<String, dynamic> json) =
|
||||
_$AccountModelImpl.fromJson;
|
||||
|
||||
@override
|
||||
String get name;
|
||||
@override
|
||||
String get id;
|
||||
@override
|
||||
String get avatar;
|
||||
@override
|
||||
DateTime get lastUsed;
|
||||
@override
|
||||
Authentication get authMethod;
|
||||
@override
|
||||
String get localPin;
|
||||
@override
|
||||
CredentialsModel get credentials;
|
||||
@override
|
||||
List<String> get latestItemsExcludes;
|
||||
@override
|
||||
List<String> get searchQueryHistory;
|
||||
@override
|
||||
bool get quickConnectState;
|
||||
@override
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
UserPolicy? get policy;
|
||||
@override
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
ServerConfiguration? get serverConfiguration;
|
||||
@override
|
||||
@JsonKey(ignore: true)
|
||||
_$$AccountModelImplCopyWith<_$AccountModelImpl> get copyWith =>
|
||||
throw _privateConstructorUsedError;
|
||||
}
|
||||
50
lib/models/account_model.g.dart
Normal file
50
lib/models/account_model.g.dart
Normal file
|
|
@ -0,0 +1,50 @@
|
|||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
|
||||
part of 'account_model.dart';
|
||||
|
||||
// **************************************************************************
|
||||
// JsonSerializableGenerator
|
||||
// **************************************************************************
|
||||
|
||||
_$AccountModelImpl _$$AccountModelImplFromJson(Map<String, dynamic> json) =>
|
||||
_$AccountModelImpl(
|
||||
name: json['name'] as String,
|
||||
id: json['id'] as String,
|
||||
avatar: json['avatar'] as String,
|
||||
lastUsed: DateTime.parse(json['lastUsed'] as String),
|
||||
authMethod:
|
||||
$enumDecodeNullable(_$AuthenticationEnumMap, json['authMethod']) ??
|
||||
Authentication.autoLogin,
|
||||
localPin: json['localPin'] as String? ?? "",
|
||||
credentials: CredentialsModel.fromJson(json['credentials'] as String),
|
||||
latestItemsExcludes: (json['latestItemsExcludes'] as List<dynamic>?)
|
||||
?.map((e) => e as String)
|
||||
.toList() ??
|
||||
const [],
|
||||
searchQueryHistory: (json['searchQueryHistory'] as List<dynamic>?)
|
||||
?.map((e) => e as String)
|
||||
.toList() ??
|
||||
const [],
|
||||
quickConnectState: json['quickConnectState'] as bool? ?? false,
|
||||
);
|
||||
|
||||
Map<String, dynamic> _$$AccountModelImplToJson(_$AccountModelImpl instance) =>
|
||||
<String, dynamic>{
|
||||
'name': instance.name,
|
||||
'id': instance.id,
|
||||
'avatar': instance.avatar,
|
||||
'lastUsed': instance.lastUsed.toIso8601String(),
|
||||
'authMethod': _$AuthenticationEnumMap[instance.authMethod]!,
|
||||
'localPin': instance.localPin,
|
||||
'credentials': instance.credentials,
|
||||
'latestItemsExcludes': instance.latestItemsExcludes,
|
||||
'searchQueryHistory': instance.searchQueryHistory,
|
||||
'quickConnectState': instance.quickConnectState,
|
||||
};
|
||||
|
||||
const _$AuthenticationEnumMap = {
|
||||
Authentication.autoLogin: 'autoLogin',
|
||||
Authentication.biometrics: 'biometrics',
|
||||
Authentication.passcode: 'passcode',
|
||||
Authentication.none: 'none',
|
||||
};
|
||||
72
lib/models/book_model.dart
Normal file
72
lib/models/book_model.dart
Normal file
|
|
@ -0,0 +1,72 @@
|
|||
import 'package:fladder/jellyfin/jellyfin_open_api.swagger.dart';
|
||||
import 'package:fladder/models/item_base_model.dart';
|
||||
import 'package:fladder/models/items/images_models.dart';
|
||||
import 'package:fladder/models/items/item_shared_models.dart';
|
||||
import 'package:fladder/models/items/overview_model.dart';
|
||||
import 'package:fladder/util/localization_helper.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
|
||||
class BookModel extends ItemBaseModel {
|
||||
final String? parentName;
|
||||
final List<ItemBaseModel> items;
|
||||
BookModel(
|
||||
{this.items = const [],
|
||||
required this.parentName,
|
||||
required super.name,
|
||||
required super.id,
|
||||
required super.overview,
|
||||
required super.parentId,
|
||||
required super.playlistId,
|
||||
required super.images,
|
||||
required super.childCount,
|
||||
required super.primaryRatio,
|
||||
required super.userData,
|
||||
super.jellyType,
|
||||
required super.canDownload,
|
||||
required super.canDelete});
|
||||
|
||||
@override
|
||||
String? get subText => parentName;
|
||||
|
||||
@override
|
||||
String? detailedName(BuildContext context) => "$name ${parentName != null ? "\n ($parentName)" : ""} ";
|
||||
|
||||
@override
|
||||
ItemBaseModel get parentBaseModel => copyWith(id: parentId);
|
||||
|
||||
@override
|
||||
bool get playAble => true;
|
||||
|
||||
int get currentPage => userData.playbackPositionTicks ~/ 10000;
|
||||
|
||||
@override
|
||||
String playText(BuildContext context) => context.localized.read(name);
|
||||
|
||||
@override
|
||||
double get progress => userData.progress != 0 ? 100 : 0;
|
||||
|
||||
@override
|
||||
String playButtonLabel(BuildContext context) => progress != 0
|
||||
? context.localized.continuePage(currentPage)
|
||||
: userData.played == true
|
||||
? "${context.localized.restart} $name"
|
||||
: context.localized.read(name);
|
||||
|
||||
factory BookModel.fromBaseDto(BaseItemDto item, Ref ref) {
|
||||
return BookModel(
|
||||
name: item.name ?? "",
|
||||
id: item.id ?? "",
|
||||
parentName: item.seriesName ?? item.seasonName,
|
||||
childCount: item.childCount,
|
||||
overview: OverviewModel.fromBaseItemDto(item, ref),
|
||||
userData: UserData.fromDto(item.userData),
|
||||
parentId: item.parentId,
|
||||
playlistId: item.playlistItemId,
|
||||
images: ImagesData.fromBaseItem(item, ref),
|
||||
canDelete: item.canDelete,
|
||||
canDownload: item.canDownload,
|
||||
primaryRatio: item.primaryImageAspectRatio,
|
||||
);
|
||||
}
|
||||
}
|
||||
47
lib/models/boxset_model.dart
Normal file
47
lib/models/boxset_model.dart
Normal file
|
|
@ -0,0 +1,47 @@
|
|||
import 'package:fladder/jellyfin/jellyfin_open_api.swagger.dart';
|
||||
import 'package:fladder/models/item_base_model.dart';
|
||||
import 'package:fladder/models/items/images_models.dart';
|
||||
import 'package:fladder/models/items/item_shared_models.dart';
|
||||
import 'package:fladder/models/items/overview_model.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
|
||||
import 'package:dart_mappable/dart_mappable.dart';
|
||||
|
||||
part 'boxset_model.mapper.dart';
|
||||
|
||||
@MappableClass()
|
||||
class BoxSetModel extends ItemBaseModel with BoxSetModelMappable {
|
||||
final List<ItemBaseModel> items;
|
||||
const BoxSetModel({
|
||||
this.items = const [],
|
||||
required super.name,
|
||||
required super.id,
|
||||
required super.overview,
|
||||
required super.parentId,
|
||||
required super.playlistId,
|
||||
required super.images,
|
||||
required super.childCount,
|
||||
required super.primaryRatio,
|
||||
required super.userData,
|
||||
required super.canDelete,
|
||||
required super.canDownload,
|
||||
super.jellyType,
|
||||
});
|
||||
|
||||
factory BoxSetModel.fromBaseDto(BaseItemDto item, Ref ref) {
|
||||
return BoxSetModel(
|
||||
name: item.name ?? "",
|
||||
id: item.id ?? "",
|
||||
childCount: item.childCount,
|
||||
overview: OverviewModel.fromBaseItemDto(item, ref),
|
||||
userData: UserData.fromDto(item.userData),
|
||||
parentId: item.parentId,
|
||||
playlistId: item.playlistItemId,
|
||||
images: ImagesData.fromBaseItem(item, ref),
|
||||
primaryRatio: item.primaryImageAspectRatio,
|
||||
canDelete: item.canDelete,
|
||||
canDownload: item.canDownload,
|
||||
jellyType: item.type,
|
||||
);
|
||||
}
|
||||
}
|
||||
242
lib/models/boxset_model.mapper.dart
Normal file
242
lib/models/boxset_model.mapper.dart
Normal file
|
|
@ -0,0 +1,242 @@
|
|||
// coverage:ignore-file
|
||||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
// ignore_for_file: type=lint
|
||||
// ignore_for_file: unused_element, unnecessary_cast, override_on_non_overriding_member
|
||||
// ignore_for_file: strict_raw_type, inference_failure_on_untyped_parameter
|
||||
|
||||
part of 'boxset_model.dart';
|
||||
|
||||
class BoxSetModelMapper extends SubClassMapperBase<BoxSetModel> {
|
||||
BoxSetModelMapper._();
|
||||
|
||||
static BoxSetModelMapper? _instance;
|
||||
static BoxSetModelMapper ensureInitialized() {
|
||||
if (_instance == null) {
|
||||
MapperContainer.globals.use(_instance = BoxSetModelMapper._());
|
||||
ItemBaseModelMapper.ensureInitialized().addSubMapper(_instance!);
|
||||
ItemBaseModelMapper.ensureInitialized();
|
||||
OverviewModelMapper.ensureInitialized();
|
||||
UserDataMapper.ensureInitialized();
|
||||
}
|
||||
return _instance!;
|
||||
}
|
||||
|
||||
@override
|
||||
final String id = 'BoxSetModel';
|
||||
|
||||
static List<ItemBaseModel> _$items(BoxSetModel v) => v.items;
|
||||
static const Field<BoxSetModel, List<ItemBaseModel>> _f$items =
|
||||
Field('items', _$items, opt: true, def: const []);
|
||||
static String _$name(BoxSetModel v) => v.name;
|
||||
static const Field<BoxSetModel, String> _f$name = Field('name', _$name);
|
||||
static String _$id(BoxSetModel v) => v.id;
|
||||
static const Field<BoxSetModel, String> _f$id = Field('id', _$id);
|
||||
static OverviewModel _$overview(BoxSetModel v) => v.overview;
|
||||
static const Field<BoxSetModel, OverviewModel> _f$overview =
|
||||
Field('overview', _$overview);
|
||||
static String? _$parentId(BoxSetModel v) => v.parentId;
|
||||
static const Field<BoxSetModel, String> _f$parentId =
|
||||
Field('parentId', _$parentId);
|
||||
static String? _$playlistId(BoxSetModel v) => v.playlistId;
|
||||
static const Field<BoxSetModel, String> _f$playlistId =
|
||||
Field('playlistId', _$playlistId);
|
||||
static ImagesData? _$images(BoxSetModel v) => v.images;
|
||||
static const Field<BoxSetModel, ImagesData> _f$images =
|
||||
Field('images', _$images);
|
||||
static int? _$childCount(BoxSetModel v) => v.childCount;
|
||||
static const Field<BoxSetModel, int> _f$childCount =
|
||||
Field('childCount', _$childCount);
|
||||
static double? _$primaryRatio(BoxSetModel v) => v.primaryRatio;
|
||||
static const Field<BoxSetModel, double> _f$primaryRatio =
|
||||
Field('primaryRatio', _$primaryRatio);
|
||||
static UserData _$userData(BoxSetModel v) => v.userData;
|
||||
static const Field<BoxSetModel, UserData> _f$userData =
|
||||
Field('userData', _$userData);
|
||||
static bool? _$canDelete(BoxSetModel v) => v.canDelete;
|
||||
static const Field<BoxSetModel, bool> _f$canDelete =
|
||||
Field('canDelete', _$canDelete);
|
||||
static bool? _$canDownload(BoxSetModel v) => v.canDownload;
|
||||
static const Field<BoxSetModel, bool> _f$canDownload =
|
||||
Field('canDownload', _$canDownload);
|
||||
static BaseItemKind? _$jellyType(BoxSetModel v) => v.jellyType;
|
||||
static const Field<BoxSetModel, BaseItemKind> _f$jellyType =
|
||||
Field('jellyType', _$jellyType, opt: true);
|
||||
|
||||
@override
|
||||
final MappableFields<BoxSetModel> fields = const {
|
||||
#items: _f$items,
|
||||
#name: _f$name,
|
||||
#id: _f$id,
|
||||
#overview: _f$overview,
|
||||
#parentId: _f$parentId,
|
||||
#playlistId: _f$playlistId,
|
||||
#images: _f$images,
|
||||
#childCount: _f$childCount,
|
||||
#primaryRatio: _f$primaryRatio,
|
||||
#userData: _f$userData,
|
||||
#canDelete: _f$canDelete,
|
||||
#canDownload: _f$canDownload,
|
||||
#jellyType: _f$jellyType,
|
||||
};
|
||||
@override
|
||||
final bool ignoreNull = true;
|
||||
|
||||
@override
|
||||
final String discriminatorKey = 'type';
|
||||
@override
|
||||
final dynamic discriminatorValue = 'BoxSetModel';
|
||||
@override
|
||||
late final ClassMapperBase superMapper =
|
||||
ItemBaseModelMapper.ensureInitialized();
|
||||
|
||||
static BoxSetModel _instantiate(DecodingData data) {
|
||||
return BoxSetModel(
|
||||
items: data.dec(_f$items),
|
||||
name: data.dec(_f$name),
|
||||
id: data.dec(_f$id),
|
||||
overview: data.dec(_f$overview),
|
||||
parentId: data.dec(_f$parentId),
|
||||
playlistId: data.dec(_f$playlistId),
|
||||
images: data.dec(_f$images),
|
||||
childCount: data.dec(_f$childCount),
|
||||
primaryRatio: data.dec(_f$primaryRatio),
|
||||
userData: data.dec(_f$userData),
|
||||
canDelete: data.dec(_f$canDelete),
|
||||
canDownload: data.dec(_f$canDownload),
|
||||
jellyType: data.dec(_f$jellyType));
|
||||
}
|
||||
|
||||
@override
|
||||
final Function instantiate = _instantiate;
|
||||
|
||||
static BoxSetModel fromMap(Map<String, dynamic> map) {
|
||||
return ensureInitialized().decodeMap<BoxSetModel>(map);
|
||||
}
|
||||
|
||||
static BoxSetModel fromJson(String json) {
|
||||
return ensureInitialized().decodeJson<BoxSetModel>(json);
|
||||
}
|
||||
}
|
||||
|
||||
mixin BoxSetModelMappable {
|
||||
String toJson() {
|
||||
return BoxSetModelMapper.ensureInitialized()
|
||||
.encodeJson<BoxSetModel>(this as BoxSetModel);
|
||||
}
|
||||
|
||||
Map<String, dynamic> toMap() {
|
||||
return BoxSetModelMapper.ensureInitialized()
|
||||
.encodeMap<BoxSetModel>(this as BoxSetModel);
|
||||
}
|
||||
|
||||
BoxSetModelCopyWith<BoxSetModel, BoxSetModel, BoxSetModel> get copyWith =>
|
||||
_BoxSetModelCopyWithImpl(this as BoxSetModel, $identity, $identity);
|
||||
@override
|
||||
String toString() {
|
||||
return BoxSetModelMapper.ensureInitialized()
|
||||
.stringifyValue(this as BoxSetModel);
|
||||
}
|
||||
}
|
||||
|
||||
extension BoxSetModelValueCopy<$R, $Out>
|
||||
on ObjectCopyWith<$R, BoxSetModel, $Out> {
|
||||
BoxSetModelCopyWith<$R, BoxSetModel, $Out> get $asBoxSetModel =>
|
||||
$base.as((v, t, t2) => _BoxSetModelCopyWithImpl(v, t, t2));
|
||||
}
|
||||
|
||||
abstract class BoxSetModelCopyWith<$R, $In extends BoxSetModel, $Out>
|
||||
implements ItemBaseModelCopyWith<$R, $In, $Out> {
|
||||
ListCopyWith<$R, ItemBaseModel,
|
||||
ItemBaseModelCopyWith<$R, ItemBaseModel, ItemBaseModel>> get items;
|
||||
@override
|
||||
OverviewModelCopyWith<$R, OverviewModel, OverviewModel> get overview;
|
||||
@override
|
||||
UserDataCopyWith<$R, UserData, UserData> get userData;
|
||||
@override
|
||||
$R call(
|
||||
{List<ItemBaseModel>? items,
|
||||
String? name,
|
||||
String? id,
|
||||
OverviewModel? overview,
|
||||
String? parentId,
|
||||
String? playlistId,
|
||||
ImagesData? images,
|
||||
int? childCount,
|
||||
double? primaryRatio,
|
||||
UserData? userData,
|
||||
bool? canDelete,
|
||||
bool? canDownload,
|
||||
BaseItemKind? jellyType});
|
||||
BoxSetModelCopyWith<$R2, $In, $Out2> $chain<$R2, $Out2>(Then<$Out2, $R2> t);
|
||||
}
|
||||
|
||||
class _BoxSetModelCopyWithImpl<$R, $Out>
|
||||
extends ClassCopyWithBase<$R, BoxSetModel, $Out>
|
||||
implements BoxSetModelCopyWith<$R, BoxSetModel, $Out> {
|
||||
_BoxSetModelCopyWithImpl(super.value, super.then, super.then2);
|
||||
|
||||
@override
|
||||
late final ClassMapperBase<BoxSetModel> $mapper =
|
||||
BoxSetModelMapper.ensureInitialized();
|
||||
@override
|
||||
ListCopyWith<$R, ItemBaseModel,
|
||||
ItemBaseModelCopyWith<$R, ItemBaseModel, ItemBaseModel>>
|
||||
get items => ListCopyWith(
|
||||
$value.items, (v, t) => v.copyWith.$chain(t), (v) => call(items: v));
|
||||
@override
|
||||
OverviewModelCopyWith<$R, OverviewModel, OverviewModel> get overview =>
|
||||
$value.overview.copyWith.$chain((v) => call(overview: v));
|
||||
@override
|
||||
UserDataCopyWith<$R, UserData, UserData> get userData =>
|
||||
$value.userData.copyWith.$chain((v) => call(userData: v));
|
||||
@override
|
||||
$R call(
|
||||
{List<ItemBaseModel>? items,
|
||||
String? name,
|
||||
String? id,
|
||||
OverviewModel? overview,
|
||||
Object? parentId = $none,
|
||||
Object? playlistId = $none,
|
||||
Object? images = $none,
|
||||
Object? childCount = $none,
|
||||
Object? primaryRatio = $none,
|
||||
UserData? userData,
|
||||
Object? canDelete = $none,
|
||||
Object? canDownload = $none,
|
||||
Object? jellyType = $none}) =>
|
||||
$apply(FieldCopyWithData({
|
||||
if (items != null) #items: items,
|
||||
if (name != null) #name: name,
|
||||
if (id != null) #id: id,
|
||||
if (overview != null) #overview: overview,
|
||||
if (parentId != $none) #parentId: parentId,
|
||||
if (playlistId != $none) #playlistId: playlistId,
|
||||
if (images != $none) #images: images,
|
||||
if (childCount != $none) #childCount: childCount,
|
||||
if (primaryRatio != $none) #primaryRatio: primaryRatio,
|
||||
if (userData != null) #userData: userData,
|
||||
if (canDelete != $none) #canDelete: canDelete,
|
||||
if (canDownload != $none) #canDownload: canDownload,
|
||||
if (jellyType != $none) #jellyType: jellyType
|
||||
}));
|
||||
@override
|
||||
BoxSetModel $make(CopyWithData data) => BoxSetModel(
|
||||
items: data.get(#items, or: $value.items),
|
||||
name: data.get(#name, or: $value.name),
|
||||
id: data.get(#id, or: $value.id),
|
||||
overview: data.get(#overview, or: $value.overview),
|
||||
parentId: data.get(#parentId, or: $value.parentId),
|
||||
playlistId: data.get(#playlistId, or: $value.playlistId),
|
||||
images: data.get(#images, or: $value.images),
|
||||
childCount: data.get(#childCount, or: $value.childCount),
|
||||
primaryRatio: data.get(#primaryRatio, or: $value.primaryRatio),
|
||||
userData: data.get(#userData, or: $value.userData),
|
||||
canDelete: data.get(#canDelete, or: $value.canDelete),
|
||||
canDownload: data.get(#canDownload, or: $value.canDownload),
|
||||
jellyType: data.get(#jellyType, or: $value.jellyType));
|
||||
|
||||
@override
|
||||
BoxSetModelCopyWith<$R2, BoxSetModel, $Out2> $chain<$R2, $Out2>(
|
||||
Then<$Out2, $R2> t) =>
|
||||
_BoxSetModelCopyWithImpl($value, $cast, t);
|
||||
}
|
||||
49
lib/models/collection_types.dart
Normal file
49
lib/models/collection_types.dart
Normal file
|
|
@ -0,0 +1,49 @@
|
|||
import 'package:ficonsax/ficonsax.dart';
|
||||
import 'package:fladder/jellyfin/jellyfin_open_api.enums.swagger.dart';
|
||||
import 'package:fladder/jellyfin/jellyfin_open_api.swagger.dart';
|
||||
import 'package:fladder/models/item_base_model.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
extension CollectionTypeExtension on CollectionType {
|
||||
IconData get iconOutlined {
|
||||
return getIconType(true);
|
||||
}
|
||||
|
||||
IconData get icon {
|
||||
return getIconType(false);
|
||||
}
|
||||
|
||||
Set<FladderItemType> get itemKinds {
|
||||
switch (this) {
|
||||
case CollectionType.movies:
|
||||
return {FladderItemType.movie};
|
||||
case CollectionType.tvshows:
|
||||
return {FladderItemType.series};
|
||||
case CollectionType.homevideos:
|
||||
return {FladderItemType.photoalbum, FladderItemType.folder, FladderItemType.photo, FladderItemType.video};
|
||||
case CollectionType.boxsets:
|
||||
case CollectionType.folders:
|
||||
case CollectionType.books:
|
||||
default:
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
IconData getIconType(bool outlined) {
|
||||
switch (this) {
|
||||
case CollectionType.movies:
|
||||
return outlined ? IconsaxOutline.video_horizontal : IconsaxBold.video_horizontal;
|
||||
case CollectionType.tvshows:
|
||||
return outlined ? IconsaxOutline.video_vertical : IconsaxBold.video_vertical;
|
||||
case CollectionType.boxsets:
|
||||
case CollectionType.folders:
|
||||
return outlined ? IconsaxOutline.folder : IconsaxBold.folder;
|
||||
case CollectionType.homevideos:
|
||||
return outlined ? IconsaxOutline.gallery : IconsaxBold.gallery;
|
||||
case CollectionType.books:
|
||||
return outlined ? IconsaxOutline.book : Icons.book_rounded;
|
||||
default:
|
||||
return IconsaxOutline.info_circle;
|
||||
}
|
||||
}
|
||||
}
|
||||
81
lib/models/credentials_model.dart
Normal file
81
lib/models/credentials_model.dart
Normal file
|
|
@ -0,0 +1,81 @@
|
|||
import 'dart:convert';
|
||||
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
|
||||
import 'package:fladder/util/application_info.dart';
|
||||
import 'package:xid/xid.dart';
|
||||
|
||||
class CredentialsModel {
|
||||
final String token;
|
||||
final String server;
|
||||
final String serverName;
|
||||
final String serverId;
|
||||
final String deviceId;
|
||||
CredentialsModel({
|
||||
this.token = "",
|
||||
this.server = "",
|
||||
this.serverName = "",
|
||||
this.serverId = "",
|
||||
required this.deviceId,
|
||||
});
|
||||
|
||||
factory CredentialsModel.createNewCredentials() {
|
||||
return CredentialsModel(deviceId: Xid().toString());
|
||||
}
|
||||
|
||||
Map<String, String> header(Ref ref) {
|
||||
final application = ref.read(applicationInfoProvider);
|
||||
final headers = {
|
||||
'content-type': 'application/json',
|
||||
'x-emby-token': token,
|
||||
'x-emby-authorization':
|
||||
'MediaBrowser Client="${application.name}", Device="${application.os}", DeviceId="$deviceId", Version="${application.version}"'
|
||||
};
|
||||
return headers;
|
||||
}
|
||||
|
||||
CredentialsModel copyWith({
|
||||
String? token,
|
||||
String? server,
|
||||
String? serverName,
|
||||
String? serverId,
|
||||
String? deviceId,
|
||||
}) {
|
||||
return CredentialsModel(
|
||||
token: token ?? this.token,
|
||||
server: server ?? this.server,
|
||||
serverName: serverName ?? this.serverName,
|
||||
serverId: serverId ?? this.serverId,
|
||||
deviceId: deviceId ?? this.deviceId,
|
||||
);
|
||||
}
|
||||
|
||||
Map<String, dynamic> toMap() {
|
||||
return {
|
||||
'token': token,
|
||||
'server': server,
|
||||
'serverName': serverName,
|
||||
'serverId': serverId,
|
||||
'deviceId': deviceId,
|
||||
};
|
||||
}
|
||||
|
||||
factory CredentialsModel.fromMap(Map<String, dynamic> map) {
|
||||
return CredentialsModel(
|
||||
token: map['token'] ?? '',
|
||||
server: map['server'] ?? '',
|
||||
serverName: map['serverName'] ?? '',
|
||||
serverId: map['serverId'] ?? '',
|
||||
deviceId: map['deviceId'] ?? '',
|
||||
);
|
||||
}
|
||||
|
||||
String toJson() => json.encode(toMap());
|
||||
|
||||
factory CredentialsModel.fromJson(String source) => CredentialsModel.fromMap(json.decode(source));
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'CredentialsModel(token: $token, server: $server, serverName: $serverName, serverId: $serverId, header: $header)';
|
||||
}
|
||||
}
|
||||
26
lib/models/favourites_model.dart
Normal file
26
lib/models/favourites_model.dart
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
import 'package:fladder/models/item_base_model.dart';
|
||||
|
||||
class FavouritesModel {
|
||||
final bool loading;
|
||||
final Map<FladderItemType, List<ItemBaseModel>> favourites;
|
||||
final List<ItemBaseModel> people;
|
||||
|
||||
FavouritesModel({
|
||||
this.loading = false,
|
||||
this.favourites = const {},
|
||||
this.people = const [],
|
||||
});
|
||||
|
||||
FavouritesModel copyWith({
|
||||
bool? loading,
|
||||
String? searchQuery,
|
||||
Map<FladderItemType, List<ItemBaseModel>>? favourites,
|
||||
List<ItemBaseModel>? people,
|
||||
}) {
|
||||
return FavouritesModel(
|
||||
loading: loading ?? this.loading,
|
||||
favourites: favourites ?? this.favourites,
|
||||
people: people ?? this.people,
|
||||
);
|
||||
}
|
||||
}
|
||||
34
lib/models/home_model.dart
Normal file
34
lib/models/home_model.dart
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
// ignore_for_file: public_member_api_docs, sort_constructors_first
|
||||
import 'package:fladder/models/item_base_model.dart';
|
||||
|
||||
class HomeModel {
|
||||
final bool loading;
|
||||
final List<ItemBaseModel> resumeVideo;
|
||||
final List<ItemBaseModel> resumeAudio;
|
||||
final List<ItemBaseModel> resumeBooks;
|
||||
final List<ItemBaseModel> nextUp;
|
||||
HomeModel({
|
||||
this.loading = false,
|
||||
this.resumeVideo = const [],
|
||||
this.resumeAudio = const [],
|
||||
this.resumeBooks = const [],
|
||||
this.nextUp = const [],
|
||||
});
|
||||
|
||||
HomeModel copyWith({
|
||||
bool? loading,
|
||||
List<ItemBaseModel>? resumeVideo,
|
||||
List<ItemBaseModel>? resumeAudio,
|
||||
List<ItemBaseModel>? resumeBooks,
|
||||
List<ItemBaseModel>? nextUp,
|
||||
List<ItemBaseModel>? nextUpBooks,
|
||||
}) {
|
||||
return HomeModel(
|
||||
loading: loading ?? this.loading,
|
||||
resumeVideo: resumeVideo ?? this.resumeVideo,
|
||||
resumeAudio: resumeAudio ?? this.resumeAudio,
|
||||
resumeBooks: resumeBooks ?? this.resumeBooks,
|
||||
nextUp: nextUp ?? this.nextUp,
|
||||
);
|
||||
}
|
||||
}
|
||||
107
lib/models/information_model.dart
Normal file
107
lib/models/information_model.dart
Normal file
|
|
@ -0,0 +1,107 @@
|
|||
// ignore_for_file: constant_identifier_names
|
||||
|
||||
import 'package:fladder/jellyfin/jellyfin_open_api.swagger.dart';
|
||||
import 'package:fladder/util/size_formatting.dart';
|
||||
|
||||
class InformationModel {
|
||||
final Map<String, dynamic> baseInformation;
|
||||
final List<Map<String, dynamic>> videoStreams;
|
||||
final List<Map<String, dynamic>> audioStreams;
|
||||
final List<Map<String, dynamic>> subStreams;
|
||||
InformationModel({
|
||||
required this.baseInformation,
|
||||
required this.videoStreams,
|
||||
required this.audioStreams,
|
||||
required this.subStreams,
|
||||
});
|
||||
|
||||
static InformationModel? fromResponse(BaseItemDto? item) {
|
||||
if (item == null) return null;
|
||||
var videoStreams = item.mediaStreams?.where((element) => element.type == MediaStreamType.video).toList() ?? [];
|
||||
var audioStreams = item.mediaStreams?.where((element) => element.type == MediaStreamType.audio).toList() ?? [];
|
||||
var subStreams = item.mediaStreams?.where((element) => element.type == MediaStreamType.subtitle).toList() ?? [];
|
||||
return InformationModel(
|
||||
baseInformation: {
|
||||
"Title": item.name,
|
||||
"Container": item.container,
|
||||
"Path": item.path,
|
||||
"Size": item.mediaSources?.firstOrNull?.size.byteFormat,
|
||||
},
|
||||
videoStreams: videoStreams
|
||||
.map(
|
||||
(e) => {
|
||||
"Title": e.displayTitle,
|
||||
"Codec": e.codec,
|
||||
"Profile": e.profile,
|
||||
"Level": e.level,
|
||||
"Resolution": "${e.width}x${e.height}",
|
||||
"Aspect Ration": e.aspectRatio,
|
||||
"Interlaced": e.isInterlaced,
|
||||
"FrameRate": e.realFrameRate,
|
||||
"Bitrate": "${e.bitRate} kbps",
|
||||
"Bit depth": e.bitDepth,
|
||||
"Video range": e.videoRange,
|
||||
"Video range type": e.videoRangeType,
|
||||
"Ref frames": e.refFrames,
|
||||
},
|
||||
)
|
||||
.toList(),
|
||||
audioStreams: audioStreams
|
||||
.map(
|
||||
(e) => {
|
||||
"Title": e.displayTitle,
|
||||
"Language": e.language,
|
||||
"Codec": e.codec,
|
||||
"Layout": e.channelLayout,
|
||||
"Bitrate": "${e.bitRate} kbps",
|
||||
"Sample Rate": "${e.sampleRate} Hz",
|
||||
"Default": e.isDefault,
|
||||
"Forced": e.isForced,
|
||||
"External": e.isExternal,
|
||||
},
|
||||
)
|
||||
.toList(),
|
||||
subStreams: subStreams
|
||||
.map(
|
||||
(e) => {
|
||||
"Title": e.displayTitle,
|
||||
"Language": e.language,
|
||||
"Codec": e.codec,
|
||||
"Profile": e.profile,
|
||||
"Default": e.isDefault,
|
||||
"Forced": e.isForced,
|
||||
"External": e.isExternal,
|
||||
},
|
||||
)
|
||||
.toList(),
|
||||
);
|
||||
}
|
||||
|
||||
InformationModel copyWith({
|
||||
Map<String, dynamic>? baseInformation,
|
||||
List<Map<String, dynamic>>? videoStreams,
|
||||
List<Map<String, dynamic>>? audioStreams,
|
||||
List<Map<String, dynamic>>? subStreams,
|
||||
}) {
|
||||
return InformationModel(
|
||||
baseInformation: baseInformation ?? this.baseInformation,
|
||||
videoStreams: videoStreams ?? this.videoStreams,
|
||||
audioStreams: audioStreams ?? this.audioStreams,
|
||||
subStreams: subStreams ?? this.subStreams,
|
||||
);
|
||||
}
|
||||
|
||||
static String mapToString(Map<String, dynamic> map) {
|
||||
return map.entries.map((e) => "${e.key}: ${e.value}").join("\n");
|
||||
}
|
||||
|
||||
static String streamsToString(List<Map<String, dynamic>> streams) {
|
||||
return streams.map((e) => mapToString(e)).join("\n");
|
||||
}
|
||||
|
||||
@override
|
||||
String toString() => "${mapToString(baseInformation)}\n\n"
|
||||
"${streamsToString(videoStreams)}\n\n"
|
||||
"${streamsToString(audioStreams)}\n\n"
|
||||
"${streamsToString(subStreams)}\n\n";
|
||||
}
|
||||
363
lib/models/item_base_model.dart
Normal file
363
lib/models/item_base_model.dart
Normal file
|
|
@ -0,0 +1,363 @@
|
|||
import 'package:dart_mappable/dart_mappable.dart';
|
||||
import 'package:ficonsax/ficonsax.dart';
|
||||
import 'package:fladder/models/book_model.dart';
|
||||
import 'package:fladder/models/boxset_model.dart';
|
||||
import 'package:fladder/models/items/media_streams_model.dart';
|
||||
import 'package:fladder/models/library_search/library_search_options.dart';
|
||||
import 'package:fladder/models/playlist_model.dart';
|
||||
import 'package:fladder/routes/build_routes/home_routes.dart';
|
||||
import 'package:fladder/routes/build_routes/route_builder.dart';
|
||||
import 'package:fladder/screens/details_screens/book_detail_screen.dart';
|
||||
import 'package:fladder/util/localization_helper.dart';
|
||||
import 'package:fladder/util/string_extensions.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
|
||||
import 'package:fladder/jellyfin/jellyfin_open_api.enums.swagger.dart';
|
||||
import 'package:fladder/jellyfin/jellyfin_open_api.swagger.dart' as dto;
|
||||
import 'package:fladder/models/items/episode_model.dart';
|
||||
import 'package:fladder/models/items/folder_model.dart';
|
||||
import 'package:fladder/models/items/images_models.dart';
|
||||
import 'package:fladder/models/items/item_shared_models.dart';
|
||||
import 'package:fladder/models/items/movie_model.dart';
|
||||
import 'package:fladder/models/items/overview_model.dart';
|
||||
import 'package:fladder/models/items/person_model.dart';
|
||||
import 'package:fladder/models/items/photos_model.dart';
|
||||
import 'package:fladder/models/items/season_model.dart';
|
||||
import 'package:fladder/models/items/series_model.dart';
|
||||
import 'package:fladder/screens/details_screens/details_screens.dart';
|
||||
import 'package:fladder/screens/details_screens/episode_detail_screen.dart';
|
||||
import 'package:fladder/screens/details_screens/season_detail_screen.dart';
|
||||
import 'package:fladder/screens/library_search/library_search_screen.dart';
|
||||
|
||||
part 'item_base_model.mapper.dart';
|
||||
|
||||
@MappableClass()
|
||||
class ItemBaseModel with ItemBaseModelMappable {
|
||||
final String name;
|
||||
final String id;
|
||||
final OverviewModel overview;
|
||||
final String? parentId;
|
||||
final String? playlistId;
|
||||
final ImagesData? images;
|
||||
final int? childCount;
|
||||
final double? primaryRatio;
|
||||
final UserData userData;
|
||||
final bool? canDownload;
|
||||
final bool? canDelete;
|
||||
final dto.BaseItemKind? jellyType;
|
||||
|
||||
const ItemBaseModel({
|
||||
required this.name,
|
||||
required this.id,
|
||||
required this.overview,
|
||||
required this.parentId,
|
||||
required this.playlistId,
|
||||
required this.images,
|
||||
required this.childCount,
|
||||
required this.primaryRatio,
|
||||
required this.userData,
|
||||
required this.canDownload,
|
||||
required this.canDelete,
|
||||
required this.jellyType,
|
||||
});
|
||||
|
||||
String get title => name;
|
||||
|
||||
ItemBaseModel? setProgress(double progress) {
|
||||
return copyWith(userData: userData.copyWith(progress: progress));
|
||||
}
|
||||
|
||||
Widget? subTitle(SortingOptions options) => switch (options) {
|
||||
SortingOptions.parentalRating => overview.parentalRating != null
|
||||
? Row(
|
||||
children: [
|
||||
Icon(
|
||||
IconsaxBold.star_1,
|
||||
size: 14,
|
||||
color: Colors.yellowAccent,
|
||||
),
|
||||
const SizedBox(width: 6),
|
||||
Text((overview.parentalRating ?? 0.0).toString())
|
||||
],
|
||||
)
|
||||
: null,
|
||||
SortingOptions.communityRating => overview.communityRating != null
|
||||
? Row(
|
||||
children: [
|
||||
Icon(
|
||||
IconsaxBold.star_1,
|
||||
size: 14,
|
||||
color: Colors.yellowAccent,
|
||||
),
|
||||
const SizedBox(width: 6),
|
||||
Text((overview.communityRating ?? 0.0).toString())
|
||||
],
|
||||
)
|
||||
: null,
|
||||
_ => null,
|
||||
};
|
||||
|
||||
///Used for retrieving the correct id when fetching queue
|
||||
String get streamId => id;
|
||||
|
||||
ItemBaseModel get parentBaseModel => copyWith(id: parentId);
|
||||
|
||||
bool get emptyShow => false;
|
||||
|
||||
bool get identifiable => false;
|
||||
|
||||
int? get unPlayedItemCount => userData.unPlayedItemCount;
|
||||
|
||||
bool get unWatched => !userData.played && userData.progress <= 0 && userData.unPlayedItemCount == 0;
|
||||
|
||||
String? detailedName(BuildContext context) => null;
|
||||
|
||||
String? get subText => null;
|
||||
String? subTextShort(BuildContext context) => null;
|
||||
String? label(BuildContext context) => null;
|
||||
|
||||
ImagesData? get getPosters => images;
|
||||
|
||||
ImageData? get bannerImage => images?.primary ?? getPosters?.randomBackDrop ?? getPosters?.primary;
|
||||
|
||||
bool get playAble => false;
|
||||
|
||||
bool get syncAble => false;
|
||||
|
||||
bool get galleryItem => false;
|
||||
|
||||
MediaStreamsModel? get streamModel => null;
|
||||
|
||||
String playText(BuildContext context) => context.localized.play(name);
|
||||
|
||||
double get progress => userData.progress;
|
||||
|
||||
String playButtonLabel(BuildContext context) =>
|
||||
progress != 0 ? context.localized.resume(name.maxLength()) : context.localized.play(name.maxLength());
|
||||
|
||||
Widget get detailScreenWidget {
|
||||
switch (this) {
|
||||
case PersonModel _:
|
||||
return PersonDetailScreen(person: Person(id: id, image: images?.primary));
|
||||
case SeasonModel _:
|
||||
return SeasonDetailScreen(item: this);
|
||||
case FolderModel _:
|
||||
case PhotoAlbumModel _:
|
||||
case BoxSetModel _:
|
||||
case PlaylistModel _:
|
||||
return LibrarySearchScreen(folderId: [id]);
|
||||
case PhotoModel _:
|
||||
final photo = this as PhotoModel;
|
||||
return LibrarySearchScreen(
|
||||
folderId: [photo.albumId ?? photo.parentId ?? ""],
|
||||
photoToView: photo,
|
||||
);
|
||||
case BookModel book:
|
||||
return BookDetailScreen(item: book);
|
||||
case MovieModel _:
|
||||
return MovieDetailScreen(item: this);
|
||||
case EpisodeModel _:
|
||||
return EpisodeDetailScreen(item: this);
|
||||
case SeriesModel series:
|
||||
return SeriesDetailScreen(item: series);
|
||||
default:
|
||||
return EmptyItem(item: this);
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> navigateTo(BuildContext context) async => context.routePush(DetailsRoute(id: id), extra: this);
|
||||
|
||||
factory ItemBaseModel.fromBaseDto(dto.BaseItemDto item, Ref ref) {
|
||||
return switch (item.type) {
|
||||
BaseItemKind.photo || BaseItemKind.video => PhotoModel.fromBaseDto(item, ref),
|
||||
BaseItemKind.photoalbum => PhotoAlbumModel.fromBaseDto(item, ref),
|
||||
BaseItemKind.folder ||
|
||||
BaseItemKind.collectionfolder ||
|
||||
BaseItemKind.aggregatefolder =>
|
||||
FolderModel.fromBaseDto(item, ref),
|
||||
BaseItemKind.episode => EpisodeModel.fromBaseDto(item, ref),
|
||||
BaseItemKind.movie => MovieModel.fromBaseDto(item, ref),
|
||||
BaseItemKind.series => SeriesModel.fromBaseDto(item, ref),
|
||||
BaseItemKind.person => PersonModel.fromBaseDto(item, ref),
|
||||
BaseItemKind.season => SeasonModel.fromBaseDto(item, ref),
|
||||
BaseItemKind.boxset => BoxSetModel.fromBaseDto(item, ref),
|
||||
BaseItemKind.book => BookModel.fromBaseDto(item, ref),
|
||||
BaseItemKind.playlist => PlaylistModel.fromBaseDto(item, ref),
|
||||
_ => ItemBaseModel._fromBaseDto(item, ref)
|
||||
};
|
||||
}
|
||||
|
||||
factory ItemBaseModel._fromBaseDto(dto.BaseItemDto item, Ref ref) {
|
||||
return ItemBaseModel(
|
||||
name: item.name ?? "",
|
||||
id: item.id ?? "",
|
||||
childCount: item.childCount,
|
||||
overview: OverviewModel.fromBaseItemDto(item, ref),
|
||||
userData: UserData.fromDto(item.userData),
|
||||
parentId: item.parentId,
|
||||
playlistId: item.playlistItemId,
|
||||
images: ImagesData.fromBaseItem(item, ref),
|
||||
primaryRatio: item.primaryImageAspectRatio,
|
||||
canDelete: item.canDelete,
|
||||
canDownload: item.canDownload,
|
||||
jellyType: item.type,
|
||||
);
|
||||
}
|
||||
|
||||
FladderItemType get type => switch (this) {
|
||||
MovieModel _ => FladderItemType.movie,
|
||||
SeriesModel _ => FladderItemType.series,
|
||||
SeasonModel _ => FladderItemType.season,
|
||||
PhotoAlbumModel _ => FladderItemType.photoalbum,
|
||||
PhotoModel model => model.internalType,
|
||||
EpisodeModel _ => FladderItemType.episode,
|
||||
BookModel _ => FladderItemType.book,
|
||||
PlaylistModel _ => FladderItemType.playlist,
|
||||
FolderModel _ => FladderItemType.folder,
|
||||
ItemBaseModel _ => FladderItemType.baseType,
|
||||
};
|
||||
|
||||
@override
|
||||
bool operator ==(covariant ItemBaseModel other) {
|
||||
if (identical(this, other)) return true;
|
||||
return other.id == id;
|
||||
}
|
||||
|
||||
@override
|
||||
int get hashCode {
|
||||
return id.hashCode ^ type.hashCode;
|
||||
}
|
||||
}
|
||||
|
||||
// Currently supported types
|
||||
enum FladderItemType {
|
||||
baseType(
|
||||
icon: IconsaxOutline.folder_2,
|
||||
selectedicon: IconsaxBold.folder_2,
|
||||
),
|
||||
audio(
|
||||
icon: IconsaxOutline.music,
|
||||
selectedicon: IconsaxBold.music,
|
||||
),
|
||||
musicAlbum(
|
||||
icon: IconsaxOutline.music,
|
||||
selectedicon: IconsaxBold.music,
|
||||
),
|
||||
musicVideo(
|
||||
icon: IconsaxOutline.music,
|
||||
selectedicon: IconsaxBold.music,
|
||||
),
|
||||
collectionFolder(
|
||||
icon: IconsaxOutline.music,
|
||||
selectedicon: IconsaxBold.music,
|
||||
),
|
||||
video(
|
||||
icon: IconsaxOutline.video,
|
||||
selectedicon: IconsaxBold.video,
|
||||
),
|
||||
movie(
|
||||
icon: IconsaxOutline.video_horizontal,
|
||||
selectedicon: IconsaxBold.video_horizontal,
|
||||
),
|
||||
series(
|
||||
icon: IconsaxOutline.video_vertical,
|
||||
selectedicon: IconsaxBold.video_vertical,
|
||||
),
|
||||
season(
|
||||
icon: IconsaxOutline.video_vertical,
|
||||
selectedicon: IconsaxBold.video_vertical,
|
||||
),
|
||||
episode(
|
||||
icon: IconsaxOutline.video_vertical,
|
||||
selectedicon: IconsaxBold.video_vertical,
|
||||
),
|
||||
photo(
|
||||
icon: IconsaxOutline.picture_frame,
|
||||
selectedicon: IconsaxBold.picture_frame,
|
||||
),
|
||||
person(
|
||||
icon: IconsaxOutline.user,
|
||||
selectedicon: IconsaxBold.user,
|
||||
),
|
||||
photoalbum(
|
||||
icon: IconsaxOutline.gallery,
|
||||
selectedicon: IconsaxBold.gallery,
|
||||
),
|
||||
folder(
|
||||
icon: IconsaxOutline.folder,
|
||||
selectedicon: IconsaxBold.folder,
|
||||
),
|
||||
boxset(
|
||||
icon: IconsaxOutline.bookmark,
|
||||
selectedicon: IconsaxBold.bookmark,
|
||||
),
|
||||
playlist(
|
||||
icon: IconsaxOutline.archive_book,
|
||||
selectedicon: IconsaxBold.archive_book,
|
||||
),
|
||||
book(
|
||||
icon: IconsaxOutline.book,
|
||||
selectedicon: IconsaxBold.book,
|
||||
);
|
||||
|
||||
const FladderItemType({required this.icon, required this.selectedicon});
|
||||
|
||||
static Set<FladderItemType> get playable => {
|
||||
FladderItemType.series,
|
||||
FladderItemType.episode,
|
||||
FladderItemType.season,
|
||||
FladderItemType.movie,
|
||||
FladderItemType.musicVideo,
|
||||
};
|
||||
|
||||
static Set<FladderItemType> get galleryItem => {
|
||||
FladderItemType.photo,
|
||||
FladderItemType.video,
|
||||
};
|
||||
|
||||
String label(BuildContext context) {
|
||||
return switch (this) {
|
||||
FladderItemType.baseType => context.localized.mediaTypeBase,
|
||||
FladderItemType.audio => context.localized.audio,
|
||||
FladderItemType.collectionFolder => context.localized.collectionFolder,
|
||||
FladderItemType.musicAlbum => context.localized.musicAlbum,
|
||||
FladderItemType.musicVideo => context.localized.video,
|
||||
FladderItemType.video => context.localized.video,
|
||||
FladderItemType.movie => context.localized.mediaTypeMovie,
|
||||
FladderItemType.series => context.localized.mediaTypeSeries,
|
||||
FladderItemType.season => context.localized.mediaTypeSeason,
|
||||
FladderItemType.episode => context.localized.mediaTypeEpisode,
|
||||
FladderItemType.photo => context.localized.mediaTypePhoto,
|
||||
FladderItemType.person => context.localized.mediaTypePerson,
|
||||
FladderItemType.photoalbum => context.localized.mediaTypePhotoAlbum,
|
||||
FladderItemType.folder => context.localized.mediaTypeFolder,
|
||||
FladderItemType.boxset => context.localized.mediaTypeBoxset,
|
||||
FladderItemType.playlist => context.localized.mediaTypePlaylist,
|
||||
FladderItemType.book => context.localized.mediaTypeBook,
|
||||
};
|
||||
}
|
||||
|
||||
BaseItemKind get dtoKind => switch (this) {
|
||||
FladderItemType.baseType => BaseItemKind.userrootfolder,
|
||||
FladderItemType.audio => BaseItemKind.audio,
|
||||
FladderItemType.collectionFolder => BaseItemKind.collectionfolder,
|
||||
FladderItemType.musicAlbum => BaseItemKind.musicalbum,
|
||||
FladderItemType.musicVideo => BaseItemKind.video,
|
||||
FladderItemType.video => BaseItemKind.video,
|
||||
FladderItemType.movie => BaseItemKind.movie,
|
||||
FladderItemType.series => BaseItemKind.series,
|
||||
FladderItemType.season => BaseItemKind.season,
|
||||
FladderItemType.episode => BaseItemKind.episode,
|
||||
FladderItemType.photo => BaseItemKind.photo,
|
||||
FladderItemType.person => BaseItemKind.person,
|
||||
FladderItemType.photoalbum => BaseItemKind.photoalbum,
|
||||
FladderItemType.folder => BaseItemKind.folder,
|
||||
FladderItemType.boxset => BaseItemKind.boxset,
|
||||
FladderItemType.playlist => BaseItemKind.playlist,
|
||||
FladderItemType.book => BaseItemKind.book,
|
||||
};
|
||||
|
||||
final IconData icon;
|
||||
final IconData selectedicon;
|
||||
}
|
||||
214
lib/models/item_base_model.mapper.dart
Normal file
214
lib/models/item_base_model.mapper.dart
Normal file
|
|
@ -0,0 +1,214 @@
|
|||
// coverage:ignore-file
|
||||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
// ignore_for_file: type=lint
|
||||
// ignore_for_file: unused_element, unnecessary_cast, override_on_non_overriding_member
|
||||
// ignore_for_file: strict_raw_type, inference_failure_on_untyped_parameter
|
||||
|
||||
part of 'item_base_model.dart';
|
||||
|
||||
class ItemBaseModelMapper extends ClassMapperBase<ItemBaseModel> {
|
||||
ItemBaseModelMapper._();
|
||||
|
||||
static ItemBaseModelMapper? _instance;
|
||||
static ItemBaseModelMapper ensureInitialized() {
|
||||
if (_instance == null) {
|
||||
MapperContainer.globals.use(_instance = ItemBaseModelMapper._());
|
||||
OverviewModelMapper.ensureInitialized();
|
||||
UserDataMapper.ensureInitialized();
|
||||
}
|
||||
return _instance!;
|
||||
}
|
||||
|
||||
@override
|
||||
final String id = 'ItemBaseModel';
|
||||
|
||||
static String _$name(ItemBaseModel v) => v.name;
|
||||
static const Field<ItemBaseModel, String> _f$name = Field('name', _$name);
|
||||
static String _$id(ItemBaseModel v) => v.id;
|
||||
static const Field<ItemBaseModel, String> _f$id = Field('id', _$id);
|
||||
static OverviewModel _$overview(ItemBaseModel v) => v.overview;
|
||||
static const Field<ItemBaseModel, OverviewModel> _f$overview =
|
||||
Field('overview', _$overview);
|
||||
static String? _$parentId(ItemBaseModel v) => v.parentId;
|
||||
static const Field<ItemBaseModel, String> _f$parentId =
|
||||
Field('parentId', _$parentId);
|
||||
static String? _$playlistId(ItemBaseModel v) => v.playlistId;
|
||||
static const Field<ItemBaseModel, String> _f$playlistId =
|
||||
Field('playlistId', _$playlistId);
|
||||
static ImagesData? _$images(ItemBaseModel v) => v.images;
|
||||
static const Field<ItemBaseModel, ImagesData> _f$images =
|
||||
Field('images', _$images);
|
||||
static int? _$childCount(ItemBaseModel v) => v.childCount;
|
||||
static const Field<ItemBaseModel, int> _f$childCount =
|
||||
Field('childCount', _$childCount);
|
||||
static double? _$primaryRatio(ItemBaseModel v) => v.primaryRatio;
|
||||
static const Field<ItemBaseModel, double> _f$primaryRatio =
|
||||
Field('primaryRatio', _$primaryRatio);
|
||||
static UserData _$userData(ItemBaseModel v) => v.userData;
|
||||
static const Field<ItemBaseModel, UserData> _f$userData =
|
||||
Field('userData', _$userData);
|
||||
static bool? _$canDownload(ItemBaseModel v) => v.canDownload;
|
||||
static const Field<ItemBaseModel, bool> _f$canDownload =
|
||||
Field('canDownload', _$canDownload);
|
||||
static bool? _$canDelete(ItemBaseModel v) => v.canDelete;
|
||||
static const Field<ItemBaseModel, bool> _f$canDelete =
|
||||
Field('canDelete', _$canDelete);
|
||||
static dto.BaseItemKind? _$jellyType(ItemBaseModel v) => v.jellyType;
|
||||
static const Field<ItemBaseModel, dto.BaseItemKind> _f$jellyType =
|
||||
Field('jellyType', _$jellyType);
|
||||
|
||||
@override
|
||||
final MappableFields<ItemBaseModel> fields = const {
|
||||
#name: _f$name,
|
||||
#id: _f$id,
|
||||
#overview: _f$overview,
|
||||
#parentId: _f$parentId,
|
||||
#playlistId: _f$playlistId,
|
||||
#images: _f$images,
|
||||
#childCount: _f$childCount,
|
||||
#primaryRatio: _f$primaryRatio,
|
||||
#userData: _f$userData,
|
||||
#canDownload: _f$canDownload,
|
||||
#canDelete: _f$canDelete,
|
||||
#jellyType: _f$jellyType,
|
||||
};
|
||||
@override
|
||||
final bool ignoreNull = true;
|
||||
|
||||
static ItemBaseModel _instantiate(DecodingData data) {
|
||||
return ItemBaseModel(
|
||||
name: data.dec(_f$name),
|
||||
id: data.dec(_f$id),
|
||||
overview: data.dec(_f$overview),
|
||||
parentId: data.dec(_f$parentId),
|
||||
playlistId: data.dec(_f$playlistId),
|
||||
images: data.dec(_f$images),
|
||||
childCount: data.dec(_f$childCount),
|
||||
primaryRatio: data.dec(_f$primaryRatio),
|
||||
userData: data.dec(_f$userData),
|
||||
canDownload: data.dec(_f$canDownload),
|
||||
canDelete: data.dec(_f$canDelete),
|
||||
jellyType: data.dec(_f$jellyType));
|
||||
}
|
||||
|
||||
@override
|
||||
final Function instantiate = _instantiate;
|
||||
|
||||
static ItemBaseModel fromMap(Map<String, dynamic> map) {
|
||||
return ensureInitialized().decodeMap<ItemBaseModel>(map);
|
||||
}
|
||||
|
||||
static ItemBaseModel fromJson(String json) {
|
||||
return ensureInitialized().decodeJson<ItemBaseModel>(json);
|
||||
}
|
||||
}
|
||||
|
||||
mixin ItemBaseModelMappable {
|
||||
String toJson() {
|
||||
return ItemBaseModelMapper.ensureInitialized()
|
||||
.encodeJson<ItemBaseModel>(this as ItemBaseModel);
|
||||
}
|
||||
|
||||
Map<String, dynamic> toMap() {
|
||||
return ItemBaseModelMapper.ensureInitialized()
|
||||
.encodeMap<ItemBaseModel>(this as ItemBaseModel);
|
||||
}
|
||||
|
||||
ItemBaseModelCopyWith<ItemBaseModel, ItemBaseModel, ItemBaseModel>
|
||||
get copyWith => _ItemBaseModelCopyWithImpl(
|
||||
this as ItemBaseModel, $identity, $identity);
|
||||
@override
|
||||
String toString() {
|
||||
return ItemBaseModelMapper.ensureInitialized()
|
||||
.stringifyValue(this as ItemBaseModel);
|
||||
}
|
||||
}
|
||||
|
||||
extension ItemBaseModelValueCopy<$R, $Out>
|
||||
on ObjectCopyWith<$R, ItemBaseModel, $Out> {
|
||||
ItemBaseModelCopyWith<$R, ItemBaseModel, $Out> get $asItemBaseModel =>
|
||||
$base.as((v, t, t2) => _ItemBaseModelCopyWithImpl(v, t, t2));
|
||||
}
|
||||
|
||||
abstract class ItemBaseModelCopyWith<$R, $In extends ItemBaseModel, $Out>
|
||||
implements ClassCopyWith<$R, $In, $Out> {
|
||||
OverviewModelCopyWith<$R, OverviewModel, OverviewModel> get overview;
|
||||
UserDataCopyWith<$R, UserData, UserData> get userData;
|
||||
$R call(
|
||||
{String? name,
|
||||
String? id,
|
||||
OverviewModel? overview,
|
||||
String? parentId,
|
||||
String? playlistId,
|
||||
ImagesData? images,
|
||||
int? childCount,
|
||||
double? primaryRatio,
|
||||
UserData? userData,
|
||||
bool? canDownload,
|
||||
bool? canDelete,
|
||||
dto.BaseItemKind? jellyType});
|
||||
ItemBaseModelCopyWith<$R2, $In, $Out2> $chain<$R2, $Out2>(Then<$Out2, $R2> t);
|
||||
}
|
||||
|
||||
class _ItemBaseModelCopyWithImpl<$R, $Out>
|
||||
extends ClassCopyWithBase<$R, ItemBaseModel, $Out>
|
||||
implements ItemBaseModelCopyWith<$R, ItemBaseModel, $Out> {
|
||||
_ItemBaseModelCopyWithImpl(super.value, super.then, super.then2);
|
||||
|
||||
@override
|
||||
late final ClassMapperBase<ItemBaseModel> $mapper =
|
||||
ItemBaseModelMapper.ensureInitialized();
|
||||
@override
|
||||
OverviewModelCopyWith<$R, OverviewModel, OverviewModel> get overview =>
|
||||
$value.overview.copyWith.$chain((v) => call(overview: v));
|
||||
@override
|
||||
UserDataCopyWith<$R, UserData, UserData> get userData =>
|
||||
$value.userData.copyWith.$chain((v) => call(userData: v));
|
||||
@override
|
||||
$R call(
|
||||
{String? name,
|
||||
String? id,
|
||||
OverviewModel? overview,
|
||||
Object? parentId = $none,
|
||||
Object? playlistId = $none,
|
||||
Object? images = $none,
|
||||
Object? childCount = $none,
|
||||
Object? primaryRatio = $none,
|
||||
UserData? userData,
|
||||
Object? canDownload = $none,
|
||||
Object? canDelete = $none,
|
||||
Object? jellyType = $none}) =>
|
||||
$apply(FieldCopyWithData({
|
||||
if (name != null) #name: name,
|
||||
if (id != null) #id: id,
|
||||
if (overview != null) #overview: overview,
|
||||
if (parentId != $none) #parentId: parentId,
|
||||
if (playlistId != $none) #playlistId: playlistId,
|
||||
if (images != $none) #images: images,
|
||||
if (childCount != $none) #childCount: childCount,
|
||||
if (primaryRatio != $none) #primaryRatio: primaryRatio,
|
||||
if (userData != null) #userData: userData,
|
||||
if (canDownload != $none) #canDownload: canDownload,
|
||||
if (canDelete != $none) #canDelete: canDelete,
|
||||
if (jellyType != $none) #jellyType: jellyType
|
||||
}));
|
||||
@override
|
||||
ItemBaseModel $make(CopyWithData data) => ItemBaseModel(
|
||||
name: data.get(#name, or: $value.name),
|
||||
id: data.get(#id, or: $value.id),
|
||||
overview: data.get(#overview, or: $value.overview),
|
||||
parentId: data.get(#parentId, or: $value.parentId),
|
||||
playlistId: data.get(#playlistId, or: $value.playlistId),
|
||||
images: data.get(#images, or: $value.images),
|
||||
childCount: data.get(#childCount, or: $value.childCount),
|
||||
primaryRatio: data.get(#primaryRatio, or: $value.primaryRatio),
|
||||
userData: data.get(#userData, or: $value.userData),
|
||||
canDownload: data.get(#canDownload, or: $value.canDownload),
|
||||
canDelete: data.get(#canDelete, or: $value.canDelete),
|
||||
jellyType: data.get(#jellyType, or: $value.jellyType));
|
||||
|
||||
@override
|
||||
ItemBaseModelCopyWith<$R2, ItemBaseModel, $Out2> $chain<$R2, $Out2>(
|
||||
Then<$Out2, $R2> t) =>
|
||||
_ItemBaseModelCopyWithImpl($value, $cast, t);
|
||||
}
|
||||
318
lib/models/item_editing_model.dart
Normal file
318
lib/models/item_editing_model.dart
Normal file
|
|
@ -0,0 +1,318 @@
|
|||
import 'dart:typed_data';
|
||||
|
||||
import 'package:collection/collection.dart';
|
||||
import 'package:fladder/models/items/series_model.dart';
|
||||
import 'package:flutter/widgets.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
|
||||
import 'package:fladder/jellyfin/jellyfin_open_api.enums.swagger.dart';
|
||||
import 'package:fladder/jellyfin/jellyfin_open_api.swagger.dart' as jelly;
|
||||
import 'package:fladder/models/item_base_model.dart';
|
||||
import 'package:fladder/models/items/item_shared_models.dart';
|
||||
import 'package:fladder/providers/image_provider.dart';
|
||||
|
||||
class EditItemsProvider {
|
||||
final List<EditingImageModel> serverImages;
|
||||
final List<EditingImageModel> images;
|
||||
final List<EditingImageModel> customImages;
|
||||
final EditingImageModel? selected;
|
||||
final List<EditingImageModel> selection;
|
||||
const EditItemsProvider({
|
||||
this.serverImages = const [],
|
||||
this.images = const [],
|
||||
this.customImages = const [],
|
||||
this.selected,
|
||||
this.selection = const [],
|
||||
});
|
||||
|
||||
Future<void> setImage(
|
||||
jelly.ImageType type, {
|
||||
required Function(EditingImageModel? imageModel) uploadData,
|
||||
required Function(EditingImageModel? imageModel) uploadUrl,
|
||||
}) async {
|
||||
switch (type) {
|
||||
case jelly.ImageType.primary:
|
||||
case jelly.ImageType.logo:
|
||||
{
|
||||
if (selected == null) return;
|
||||
if (selected?.imageData != null) {
|
||||
await uploadData(selected!.copyWith(type: type));
|
||||
} else if (selected?.url != null) {
|
||||
await uploadUrl(selected!.copyWith(type: type));
|
||||
}
|
||||
}
|
||||
case jelly.ImageType.backdrop:
|
||||
{
|
||||
for (var element in selection) {
|
||||
if (element.imageData != null) {
|
||||
await uploadData(element.copyWith(type: type));
|
||||
} else if (element.url != null) {
|
||||
await uploadUrl(element.copyWith(type: type));
|
||||
}
|
||||
}
|
||||
}
|
||||
default:
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
EditItemsProvider copyWith({
|
||||
List<EditingImageModel>? serverImages,
|
||||
List<EditingImageModel>? images,
|
||||
List<EditingImageModel>? customImages,
|
||||
ValueGetter<EditingImageModel?>? selected,
|
||||
List<EditingImageModel>? selection,
|
||||
}) {
|
||||
return EditItemsProvider(
|
||||
serverImages: serverImages ?? this.serverImages,
|
||||
images: images ?? this.images,
|
||||
customImages: customImages ?? this.customImages,
|
||||
selected: selected != null ? selected() : this.selected,
|
||||
selection: selection ?? this.selection,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class ItemEditingModel {
|
||||
final ItemBaseModel? item;
|
||||
final jelly.MetadataEditorInfo? editorInfo;
|
||||
final Map<String, dynamic>? json;
|
||||
final Map<String, dynamic>? editedJson;
|
||||
final bool includeAllImages;
|
||||
final EditItemsProvider primary;
|
||||
final EditItemsProvider logo;
|
||||
final EditItemsProvider backdrop;
|
||||
final bool saving;
|
||||
ItemEditingModel({
|
||||
this.item,
|
||||
this.editorInfo,
|
||||
this.json,
|
||||
this.editedJson,
|
||||
this.includeAllImages = false,
|
||||
this.primary = const EditItemsProvider(),
|
||||
this.logo = const EditItemsProvider(),
|
||||
this.backdrop = const EditItemsProvider(),
|
||||
this.saving = false,
|
||||
});
|
||||
|
||||
Map<String, dynamic>? editAbleFields() {
|
||||
return editedJson == null
|
||||
? {}
|
||||
: {
|
||||
"Name": editedJson?["Name"] as String?,
|
||||
"OriginalTitle": editedJson?["OriginalTitle"] as String?,
|
||||
"PremiereDate": editedJson?["PremiereDate"] != null ? DateTime.tryParse(editedJson!["PremiereDate"]) : null,
|
||||
"DateCreated": editedJson?["DateCreated"] != null ? DateTime.tryParse(editedJson!["DateCreated"]) : null,
|
||||
"ProductionYear": editedJson?["ProductionYear"] as int?,
|
||||
"Path": editedJson?["Path"] as String?,
|
||||
"Overview": editedJson?["Overview"] as String? ?? "",
|
||||
}
|
||||
..removeWhere((key, value) => value == null);
|
||||
}
|
||||
|
||||
Map<String, dynamic>? editAdvancedAbleFields(Ref ref) => editedJson == null
|
||||
? {}
|
||||
: {
|
||||
if (item is SeriesModel) "DisplayOrder": DisplayOrder.fromMap(editedJson?["DisplayOrder"]),
|
||||
if (item is SeriesModel) ...{
|
||||
"OfficialRating": {
|
||||
for (String element in (editorInfo?.parentalRatingOptions?.map((e) => e.name).toSet()
|
||||
?..add(json?["OfficialRating"] as String?))
|
||||
?.whereNotNull()
|
||||
.toList() ??
|
||||
[])
|
||||
element: (editedJson?["OfficialRating"] as String?) == element
|
||||
},
|
||||
"CustomRating": {
|
||||
for (String element in (editorInfo?.parentalRatingOptions?.map((e) => e.name).toSet()
|
||||
?..add(json?["CustomRating"] as String?))
|
||||
?.whereNotNull()
|
||||
.toList() ??
|
||||
[])
|
||||
element: (editedJson?["CustomRating"] as String?) == element
|
||||
},
|
||||
},
|
||||
"People": editedJson?["People"] != null
|
||||
? (editedJson!["People"] as List<dynamic>)
|
||||
.map((e) => Person.fromBasePerson(jelly.BaseItemPerson.fromJson(e), ref))
|
||||
.toList()
|
||||
: null,
|
||||
"ExternalUrls": editedJson?["ExternalUrls"] != null
|
||||
? (editedJson!["ExternalUrls"] as List<dynamic>).map((e) => ExternalUrls.fromMap(e)).toList()
|
||||
: null,
|
||||
"CommunityRating": double.tryParse((editedJson?["CommunityRating"] as num?).toString()),
|
||||
"SeriesName": editedJson?["SeriesName"] as String?,
|
||||
"IndexNumber": editedJson?["IndexNumber"] as int?,
|
||||
"RunTimeTicks": (editedJson?["RunTimeTicks"] == null)
|
||||
? null
|
||||
: Duration(milliseconds: editedJson?["RunTimeTicks"] ~/ 10000),
|
||||
"ParentIndexNumber": editedJson?["ParentIndexNumber"] as int?,
|
||||
if (item is SeriesModel) "Status": ShowStatus.fromMap(editedJson?["Status"] as String?),
|
||||
"Genres": editedJson?["Genres"] != null ? (List<String>.from(editedJson!["Genres"])) : null,
|
||||
"Tags": editedJson?["Tags"] != null ? (List<String>.from(editedJson?["Tags"])) : null,
|
||||
"Studios": editedJson?["Studios"] != null
|
||||
? (editedJson!["Studios"] as List<dynamic>).map((e) => Studio.fromMap(e)).toList()
|
||||
: null,
|
||||
"SeriesStudio": editedJson?["SeriesStudio"] as String?,
|
||||
"LockData": editedJson?["LockData"] as bool? ?? false,
|
||||
"LockedFields": ((editedJson?["LockData"] as bool?) == false)
|
||||
? EditorLockedFields.enabled(List<String>.from(editedJson?["LockedFields"]))
|
||||
: null,
|
||||
}
|
||||
..removeWhere((key, value) => value == null);
|
||||
|
||||
ItemEditingModel copyWith({
|
||||
ValueGetter<ItemBaseModel?>? item,
|
||||
ValueGetter<jelly.MetadataEditorInfo?>? editorInfo,
|
||||
ValueGetter<Map<String, dynamic>?>? json,
|
||||
ValueGetter<Map<String, dynamic>?>? editedJson,
|
||||
bool? includeAllImages,
|
||||
EditItemsProvider? primary,
|
||||
EditItemsProvider? logo,
|
||||
EditItemsProvider? backdrop,
|
||||
bool? saving,
|
||||
}) {
|
||||
return ItemEditingModel(
|
||||
item: item != null ? item() : this.item,
|
||||
editorInfo: editorInfo != null ? editorInfo() : this.editorInfo,
|
||||
json: json != null ? json() : this.json,
|
||||
editedJson: editedJson != null ? editedJson() : this.editedJson,
|
||||
includeAllImages: includeAllImages ?? this.includeAllImages,
|
||||
primary: primary ?? this.primary,
|
||||
logo: logo ?? this.logo,
|
||||
backdrop: backdrop ?? this.backdrop,
|
||||
saving: saving ?? this.saving,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class EditingImageModel {
|
||||
final String providerName;
|
||||
final String? url;
|
||||
final Uint8List? imageData;
|
||||
final int? index;
|
||||
final int height;
|
||||
final int width;
|
||||
final double communityRating;
|
||||
final int voteCount;
|
||||
final String language;
|
||||
final jelly.ImageType type;
|
||||
final jelly.RatingType ratingType;
|
||||
EditingImageModel({
|
||||
required this.providerName,
|
||||
this.url,
|
||||
this.imageData,
|
||||
this.index,
|
||||
this.height = 0,
|
||||
this.width = 0,
|
||||
this.communityRating = 0.0,
|
||||
this.voteCount = 0,
|
||||
this.language = "",
|
||||
this.type = jelly.ImageType.primary,
|
||||
this.ratingType = jelly.RatingType.likes,
|
||||
});
|
||||
|
||||
double get ratio {
|
||||
if (width == 0 && height == 0) return 1;
|
||||
final ratio = (width.toDouble() / height.toDouble()).clamp(0.1, 5).toDouble();
|
||||
if (ratio < 0) {
|
||||
return 1;
|
||||
} else {
|
||||
return ratio;
|
||||
}
|
||||
}
|
||||
|
||||
factory EditingImageModel.fromDto(jelly.RemoteImageInfo info) {
|
||||
return EditingImageModel(
|
||||
providerName: info.providerName ?? "",
|
||||
url: info.url ?? "",
|
||||
height: info.height ?? 0,
|
||||
width: info.width ?? 0,
|
||||
communityRating: info.communityRating ?? 0.0,
|
||||
voteCount: info.voteCount ?? 0,
|
||||
language: info.language ?? "",
|
||||
type: info.type ?? jelly.ImageType.primary,
|
||||
ratingType: info.ratingType ?? jelly.RatingType.likes,
|
||||
);
|
||||
}
|
||||
|
||||
factory EditingImageModel.fromImage(jelly.ImageInfo info, String itemId, Ref ref) {
|
||||
return EditingImageModel(
|
||||
providerName: "",
|
||||
url: switch (info.imageType ?? ImageType.primary) {
|
||||
ImageType.backdrop => ref.read(imageUtilityProvider).getBackdropOrigImage(
|
||||
itemId,
|
||||
info.imageIndex ?? 0,
|
||||
info.hashCode.toString(),
|
||||
),
|
||||
_ => ref.read(imageUtilityProvider).getItemsOrigImageUrl(
|
||||
itemId,
|
||||
type: info.imageType ?? ImageType.primary,
|
||||
),
|
||||
},
|
||||
index: info.imageIndex,
|
||||
height: info.height ?? 0,
|
||||
width: info.width ?? 0,
|
||||
type: info.imageType ?? ImageType.primary,
|
||||
);
|
||||
}
|
||||
|
||||
EditingImageModel copyWith({
|
||||
String? providerName,
|
||||
ValueGetter<String?>? url,
|
||||
ValueGetter<Uint8List?>? imageData,
|
||||
ValueGetter<int?>? index,
|
||||
int? height,
|
||||
int? width,
|
||||
double? communityRating,
|
||||
int? voteCount,
|
||||
String? language,
|
||||
jelly.ImageType? type,
|
||||
jelly.RatingType? ratingType,
|
||||
}) {
|
||||
return EditingImageModel(
|
||||
providerName: providerName ?? this.providerName,
|
||||
url: url != null ? url() : this.url,
|
||||
imageData: imageData != null ? imageData() : this.imageData,
|
||||
index: index != null ? index() : this.index,
|
||||
height: height ?? this.height,
|
||||
width: width ?? this.width,
|
||||
communityRating: communityRating ?? this.communityRating,
|
||||
voteCount: voteCount ?? this.voteCount,
|
||||
language: language ?? this.language,
|
||||
type: type ?? this.type,
|
||||
ratingType: ratingType ?? this.ratingType,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
if (identical(this, other)) return true;
|
||||
|
||||
return other is EditingImageModel &&
|
||||
other.providerName == providerName &&
|
||||
other.url == url &&
|
||||
other.imageData == imageData &&
|
||||
other.height == height &&
|
||||
other.width == width &&
|
||||
other.communityRating == communityRating &&
|
||||
other.voteCount == voteCount &&
|
||||
other.language == language &&
|
||||
other.type == type &&
|
||||
other.ratingType == ratingType;
|
||||
}
|
||||
|
||||
@override
|
||||
int get hashCode {
|
||||
return providerName.hashCode ^
|
||||
url.hashCode ^
|
||||
imageData.hashCode ^
|
||||
height.hashCode ^
|
||||
width.hashCode ^
|
||||
communityRating.hashCode ^
|
||||
voteCount.hashCode ^
|
||||
language.hashCode ^
|
||||
type.hashCode ^
|
||||
ratingType.hashCode;
|
||||
}
|
||||
}
|
||||
90
lib/models/items/chapters_model.dart
Normal file
90
lib/models/items/chapters_model.dart
Normal file
|
|
@ -0,0 +1,90 @@
|
|||
import 'dart:convert';
|
||||
import 'dart:io';
|
||||
import 'dart:typed_data';
|
||||
|
||||
import 'package:cached_network_image/cached_network_image.dart';
|
||||
import 'package:collection/collection.dart';
|
||||
import 'package:fladder/main.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
|
||||
import 'package:fladder/jellyfin/jellyfin_open_api.swagger.dart' as dto;
|
||||
import 'package:fladder/providers/image_provider.dart';
|
||||
|
||||
class Chapter {
|
||||
final String name;
|
||||
final String imageUrl;
|
||||
final Uint8List? imageData;
|
||||
final Duration startPosition;
|
||||
Chapter({
|
||||
required this.name,
|
||||
required this.imageUrl,
|
||||
this.imageData,
|
||||
required this.startPosition,
|
||||
});
|
||||
|
||||
ImageProvider get imageProvider {
|
||||
if (imageData != null) {
|
||||
return Image.memory(imageData!).image;
|
||||
}
|
||||
if (imageUrl.startsWith("http")) {
|
||||
return CachedNetworkImageProvider(
|
||||
cacheKey: name + imageUrl,
|
||||
cacheManager: CustomCacheManager.instance,
|
||||
imageUrl,
|
||||
);
|
||||
} else {
|
||||
return Image.file(
|
||||
key: Key(name + imageUrl),
|
||||
File(imageUrl),
|
||||
).image;
|
||||
}
|
||||
}
|
||||
|
||||
static List<Chapter> chaptersFromInfo(String itemId, List<dto.ChapterInfo> chapters, Ref ref) {
|
||||
return chapters
|
||||
.mapIndexed((index, element) => Chapter(
|
||||
name: element.name ?? "",
|
||||
imageUrl: ref.read(imageUtilityProvider).getChapterUrl(itemId, index),
|
||||
startPosition: Duration(milliseconds: (element.startPositionTicks ?? 0) ~/ 10000)))
|
||||
.toList();
|
||||
}
|
||||
|
||||
Chapter copyWith({
|
||||
String? name,
|
||||
String? imageUrl,
|
||||
Duration? startPosition,
|
||||
}) {
|
||||
return Chapter(
|
||||
name: name ?? this.name,
|
||||
imageUrl: imageUrl ?? this.imageUrl,
|
||||
startPosition: startPosition ?? this.startPosition,
|
||||
);
|
||||
}
|
||||
|
||||
Map<String, dynamic> toMap() {
|
||||
return {
|
||||
'name': name,
|
||||
'imageUrl': imageUrl,
|
||||
'startPosition': startPosition.inMilliseconds,
|
||||
};
|
||||
}
|
||||
|
||||
factory Chapter.fromMap(Map<String, dynamic> map) {
|
||||
return Chapter(
|
||||
name: map['name'] ?? '',
|
||||
imageUrl: map['imageUrl'] ?? '',
|
||||
startPosition: Duration(milliseconds: map['startPosition'] as int),
|
||||
);
|
||||
}
|
||||
|
||||
String toJson() => json.encode(toMap());
|
||||
|
||||
factory Chapter.fromJson(String source) => Chapter.fromMap(json.decode(source));
|
||||
}
|
||||
|
||||
extension ChapterExtension on List<Chapter> {
|
||||
Chapter? getChapterFromDuration(Duration duration) {
|
||||
return lastWhereOrNull((element) => element.startPosition < duration);
|
||||
}
|
||||
}
|
||||
201
lib/models/items/episode_model.dart
Normal file
201
lib/models/items/episode_model.dart
Normal file
|
|
@ -0,0 +1,201 @@
|
|||
import 'package:collection/collection.dart';
|
||||
import 'package:fladder/jellyfin/enum_models.dart';
|
||||
import 'package:fladder/models/items/series_model.dart';
|
||||
import 'package:fladder/util/localization_helper.dart';
|
||||
import 'package:fladder/util/string_extensions.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
|
||||
import 'package:fladder/jellyfin/jellyfin_open_api.swagger.dart' as dto;
|
||||
import 'package:fladder/models/items/chapters_model.dart';
|
||||
import 'package:fladder/models/items/images_models.dart';
|
||||
import 'package:fladder/models/items/item_shared_models.dart';
|
||||
import 'package:fladder/models/items/item_stream_model.dart';
|
||||
import 'package:fladder/models/items/media_streams_model.dart';
|
||||
import 'package:fladder/models/items/overview_model.dart';
|
||||
import 'package:dart_mappable/dart_mappable.dart';
|
||||
|
||||
part 'episode_model.mapper.dart';
|
||||
|
||||
enum EpisodeStatus { available, unaired, missing }
|
||||
|
||||
@MappableClass()
|
||||
class EpisodeModel extends ItemStreamModel with EpisodeModelMappable {
|
||||
final String? seriesName;
|
||||
final int season;
|
||||
final int episode;
|
||||
final List<Chapter> chapters;
|
||||
final ItemLocation? location;
|
||||
final DateTime? dateAired;
|
||||
const EpisodeModel({
|
||||
required this.seriesName,
|
||||
required this.season,
|
||||
required this.episode,
|
||||
this.chapters = const [],
|
||||
this.location,
|
||||
this.dateAired,
|
||||
required super.name,
|
||||
required super.id,
|
||||
required super.overview,
|
||||
required super.parentId,
|
||||
required super.playlistId,
|
||||
required super.images,
|
||||
required super.childCount,
|
||||
required super.primaryRatio,
|
||||
required super.userData,
|
||||
required super.parentImages,
|
||||
required super.mediaStreams,
|
||||
super.canDelete,
|
||||
super.canDownload,
|
||||
super.jellyType,
|
||||
});
|
||||
EpisodeStatus get status {
|
||||
return switch (location) {
|
||||
ItemLocation.filesystem => EpisodeStatus.available,
|
||||
ItemLocation.virtual =>
|
||||
(dateAired?.isBefore(DateTime.now()) == true) ? EpisodeStatus.missing : EpisodeStatus.unaired,
|
||||
_ => EpisodeStatus.missing
|
||||
};
|
||||
}
|
||||
|
||||
@override
|
||||
String? detailedName(BuildContext context) => "${subTextShort(context)} - $name";
|
||||
|
||||
@override
|
||||
SeriesModel get parentBaseModel => SeriesModel(
|
||||
originalTitle: '',
|
||||
sortName: '',
|
||||
status: "",
|
||||
name: seriesName ?? "",
|
||||
id: parentId ?? "",
|
||||
playlistId: playlistId,
|
||||
overview: overview,
|
||||
parentId: parentId,
|
||||
images: images,
|
||||
childCount: childCount,
|
||||
primaryRatio: primaryRatio,
|
||||
userData: UserData(),
|
||||
);
|
||||
|
||||
@override
|
||||
String get streamId => parentId ?? "";
|
||||
|
||||
@override
|
||||
String get title => seriesName ?? name;
|
||||
|
||||
@override
|
||||
MediaStreamsModel? get streamModel => mediaStreams;
|
||||
|
||||
@override
|
||||
ImagesData? get getPosters => parentImages;
|
||||
|
||||
@override
|
||||
String? get subText => name.isEmpty ? "TBA" : name;
|
||||
|
||||
@override
|
||||
String? subTextShort(BuildContext context) => seasonEpisodeLabel(context);
|
||||
|
||||
@override
|
||||
String? label(BuildContext context) => "${subTextShort(context)} - $name";
|
||||
|
||||
@override
|
||||
bool get playAble => switch (status) {
|
||||
EpisodeStatus.available => true,
|
||||
_ => false,
|
||||
};
|
||||
|
||||
@override
|
||||
String playButtonLabel(BuildContext context) {
|
||||
final string = seasonEpisodeLabel(context).maxLength();
|
||||
return progress != 0 ? context.localized.resume(string) : context.localized.play(string);
|
||||
}
|
||||
|
||||
String seasonAnnotation(BuildContext context) => context.localized.season(1)[0];
|
||||
String episodeAnnotation(BuildContext context) => context.localized.episode(1)[0];
|
||||
|
||||
String seasonEpisodeLabel(BuildContext context) {
|
||||
return "${seasonAnnotation(context)}$season - ${episodeAnnotation(context)}$episode";
|
||||
}
|
||||
|
||||
String seasonEpisodeLabelFull(BuildContext context) {
|
||||
return "${context.localized.season(1)} $season - ${context.localized.episode(1)} $episode";
|
||||
}
|
||||
|
||||
String episodeLabel(BuildContext context) {
|
||||
return "${seasonEpisodeLabel(context)} - $subText";
|
||||
}
|
||||
|
||||
String get fullName {
|
||||
return "$episode. $subText";
|
||||
}
|
||||
|
||||
@override
|
||||
bool get syncAble => playAble;
|
||||
|
||||
@override
|
||||
factory EpisodeModel.fromBaseDto(dto.BaseItemDto item, Ref ref) => EpisodeModel(
|
||||
seriesName: item.seriesName,
|
||||
name: item.name ?? "",
|
||||
id: item.id ?? "",
|
||||
childCount: item.childCount,
|
||||
overview: OverviewModel.fromBaseItemDto(item, ref),
|
||||
userData: UserData.fromDto(item.userData),
|
||||
parentId: item.seriesId,
|
||||
playlistId: item.playlistItemId,
|
||||
dateAired: item.premiereDate,
|
||||
chapters: Chapter.chaptersFromInfo(item.id ?? "", item.chapters ?? [], ref),
|
||||
images: ImagesData.fromBaseItem(item, ref, getOriginalSize: true),
|
||||
primaryRatio: item.primaryImageAspectRatio,
|
||||
season: item.parentIndexNumber ?? 0,
|
||||
episode: item.indexNumber ?? 0,
|
||||
location: ItemLocation.fromDto(item.locationType),
|
||||
parentImages: ImagesData.fromBaseItemParent(item, ref),
|
||||
canDelete: item.canDelete,
|
||||
canDownload: item.canDownload,
|
||||
mediaStreams:
|
||||
MediaStreamsModel.fromMediaStreamsList(item.mediaSources?.firstOrNull, item.mediaStreams ?? [], ref),
|
||||
jellyType: item.type,
|
||||
);
|
||||
|
||||
static List<EpisodeModel> episodesFromDto(List<dto.BaseItemDto>? dto, Ref ref) {
|
||||
return dto?.map((e) => EpisodeModel.fromBaseDto(e, ref)).toList() ?? [];
|
||||
}
|
||||
}
|
||||
|
||||
extension EpisodeListExtensions on List<EpisodeModel> {
|
||||
Map<int, List<EpisodeModel>> get episodesBySeason {
|
||||
Map<int, List<EpisodeModel>> groupedItems = {};
|
||||
for (int i = 0; i < length; i++) {
|
||||
int seasonIndex = this[i].season;
|
||||
if (!groupedItems.containsKey(seasonIndex)) {
|
||||
groupedItems[seasonIndex] = [this[i]];
|
||||
} else {
|
||||
groupedItems[seasonIndex]?.add(this[i]);
|
||||
}
|
||||
}
|
||||
return groupedItems;
|
||||
}
|
||||
|
||||
EpisodeModel? get nextUp {
|
||||
final lastProgress =
|
||||
lastIndexWhere((element) => element.userData.progress != 0 && element.status == EpisodeStatus.available);
|
||||
final lastPlayed =
|
||||
lastIndexWhere((element) => element.userData.played && element.status == EpisodeStatus.available);
|
||||
|
||||
if (lastProgress == -1 && lastPlayed == -1) {
|
||||
return firstWhereOrNull((element) => element.status == EpisodeStatus.available);
|
||||
} else {
|
||||
return getRange(lastProgress > lastPlayed ? lastProgress : lastPlayed + 1, length)
|
||||
.firstWhereOrNull((element) => element.status == EpisodeStatus.available);
|
||||
}
|
||||
}
|
||||
|
||||
bool get allPlayed {
|
||||
for (var element in this) {
|
||||
if (!element.userData.played) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
301
lib/models/items/episode_model.mapper.dart
Normal file
301
lib/models/items/episode_model.mapper.dart
Normal file
|
|
@ -0,0 +1,301 @@
|
|||
// coverage:ignore-file
|
||||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
// ignore_for_file: type=lint
|
||||
// ignore_for_file: unused_element, unnecessary_cast, override_on_non_overriding_member
|
||||
// ignore_for_file: strict_raw_type, inference_failure_on_untyped_parameter
|
||||
|
||||
part of 'episode_model.dart';
|
||||
|
||||
class EpisodeModelMapper extends SubClassMapperBase<EpisodeModel> {
|
||||
EpisodeModelMapper._();
|
||||
|
||||
static EpisodeModelMapper? _instance;
|
||||
static EpisodeModelMapper ensureInitialized() {
|
||||
if (_instance == null) {
|
||||
MapperContainer.globals.use(_instance = EpisodeModelMapper._());
|
||||
ItemStreamModelMapper.ensureInitialized().addSubMapper(_instance!);
|
||||
OverviewModelMapper.ensureInitialized();
|
||||
UserDataMapper.ensureInitialized();
|
||||
}
|
||||
return _instance!;
|
||||
}
|
||||
|
||||
@override
|
||||
final String id = 'EpisodeModel';
|
||||
|
||||
static String? _$seriesName(EpisodeModel v) => v.seriesName;
|
||||
static const Field<EpisodeModel, String> _f$seriesName =
|
||||
Field('seriesName', _$seriesName);
|
||||
static int _$season(EpisodeModel v) => v.season;
|
||||
static const Field<EpisodeModel, int> _f$season = Field('season', _$season);
|
||||
static int _$episode(EpisodeModel v) => v.episode;
|
||||
static const Field<EpisodeModel, int> _f$episode =
|
||||
Field('episode', _$episode);
|
||||
static List<Chapter> _$chapters(EpisodeModel v) => v.chapters;
|
||||
static const Field<EpisodeModel, List<Chapter>> _f$chapters =
|
||||
Field('chapters', _$chapters, opt: true, def: const []);
|
||||
static ItemLocation? _$location(EpisodeModel v) => v.location;
|
||||
static const Field<EpisodeModel, ItemLocation> _f$location =
|
||||
Field('location', _$location, opt: true);
|
||||
static DateTime? _$dateAired(EpisodeModel v) => v.dateAired;
|
||||
static const Field<EpisodeModel, DateTime> _f$dateAired =
|
||||
Field('dateAired', _$dateAired, opt: true);
|
||||
static String _$name(EpisodeModel v) => v.name;
|
||||
static const Field<EpisodeModel, String> _f$name = Field('name', _$name);
|
||||
static String _$id(EpisodeModel v) => v.id;
|
||||
static const Field<EpisodeModel, String> _f$id = Field('id', _$id);
|
||||
static OverviewModel _$overview(EpisodeModel v) => v.overview;
|
||||
static const Field<EpisodeModel, OverviewModel> _f$overview =
|
||||
Field('overview', _$overview);
|
||||
static String? _$parentId(EpisodeModel v) => v.parentId;
|
||||
static const Field<EpisodeModel, String> _f$parentId =
|
||||
Field('parentId', _$parentId);
|
||||
static String? _$playlistId(EpisodeModel v) => v.playlistId;
|
||||
static const Field<EpisodeModel, String> _f$playlistId =
|
||||
Field('playlistId', _$playlistId);
|
||||
static ImagesData? _$images(EpisodeModel v) => v.images;
|
||||
static const Field<EpisodeModel, ImagesData> _f$images =
|
||||
Field('images', _$images);
|
||||
static int? _$childCount(EpisodeModel v) => v.childCount;
|
||||
static const Field<EpisodeModel, int> _f$childCount =
|
||||
Field('childCount', _$childCount);
|
||||
static double? _$primaryRatio(EpisodeModel v) => v.primaryRatio;
|
||||
static const Field<EpisodeModel, double> _f$primaryRatio =
|
||||
Field('primaryRatio', _$primaryRatio);
|
||||
static UserData _$userData(EpisodeModel v) => v.userData;
|
||||
static const Field<EpisodeModel, UserData> _f$userData =
|
||||
Field('userData', _$userData);
|
||||
static ImagesData? _$parentImages(EpisodeModel v) => v.parentImages;
|
||||
static const Field<EpisodeModel, ImagesData> _f$parentImages =
|
||||
Field('parentImages', _$parentImages);
|
||||
static MediaStreamsModel _$mediaStreams(EpisodeModel v) => v.mediaStreams;
|
||||
static const Field<EpisodeModel, MediaStreamsModel> _f$mediaStreams =
|
||||
Field('mediaStreams', _$mediaStreams);
|
||||
static bool? _$canDelete(EpisodeModel v) => v.canDelete;
|
||||
static const Field<EpisodeModel, bool> _f$canDelete =
|
||||
Field('canDelete', _$canDelete, opt: true);
|
||||
static bool? _$canDownload(EpisodeModel v) => v.canDownload;
|
||||
static const Field<EpisodeModel, bool> _f$canDownload =
|
||||
Field('canDownload', _$canDownload, opt: true);
|
||||
static dto.BaseItemKind? _$jellyType(EpisodeModel v) => v.jellyType;
|
||||
static const Field<EpisodeModel, dto.BaseItemKind> _f$jellyType =
|
||||
Field('jellyType', _$jellyType, opt: true);
|
||||
|
||||
@override
|
||||
final MappableFields<EpisodeModel> fields = const {
|
||||
#seriesName: _f$seriesName,
|
||||
#season: _f$season,
|
||||
#episode: _f$episode,
|
||||
#chapters: _f$chapters,
|
||||
#location: _f$location,
|
||||
#dateAired: _f$dateAired,
|
||||
#name: _f$name,
|
||||
#id: _f$id,
|
||||
#overview: _f$overview,
|
||||
#parentId: _f$parentId,
|
||||
#playlistId: _f$playlistId,
|
||||
#images: _f$images,
|
||||
#childCount: _f$childCount,
|
||||
#primaryRatio: _f$primaryRatio,
|
||||
#userData: _f$userData,
|
||||
#parentImages: _f$parentImages,
|
||||
#mediaStreams: _f$mediaStreams,
|
||||
#canDelete: _f$canDelete,
|
||||
#canDownload: _f$canDownload,
|
||||
#jellyType: _f$jellyType,
|
||||
};
|
||||
@override
|
||||
final bool ignoreNull = true;
|
||||
|
||||
@override
|
||||
final String discriminatorKey = 'type';
|
||||
@override
|
||||
final dynamic discriminatorValue = 'EpisodeModel';
|
||||
@override
|
||||
late final ClassMapperBase superMapper =
|
||||
ItemStreamModelMapper.ensureInitialized();
|
||||
|
||||
static EpisodeModel _instantiate(DecodingData data) {
|
||||
return EpisodeModel(
|
||||
seriesName: data.dec(_f$seriesName),
|
||||
season: data.dec(_f$season),
|
||||
episode: data.dec(_f$episode),
|
||||
chapters: data.dec(_f$chapters),
|
||||
location: data.dec(_f$location),
|
||||
dateAired: data.dec(_f$dateAired),
|
||||
name: data.dec(_f$name),
|
||||
id: data.dec(_f$id),
|
||||
overview: data.dec(_f$overview),
|
||||
parentId: data.dec(_f$parentId),
|
||||
playlistId: data.dec(_f$playlistId),
|
||||
images: data.dec(_f$images),
|
||||
childCount: data.dec(_f$childCount),
|
||||
primaryRatio: data.dec(_f$primaryRatio),
|
||||
userData: data.dec(_f$userData),
|
||||
parentImages: data.dec(_f$parentImages),
|
||||
mediaStreams: data.dec(_f$mediaStreams),
|
||||
canDelete: data.dec(_f$canDelete),
|
||||
canDownload: data.dec(_f$canDownload),
|
||||
jellyType: data.dec(_f$jellyType));
|
||||
}
|
||||
|
||||
@override
|
||||
final Function instantiate = _instantiate;
|
||||
|
||||
static EpisodeModel fromMap(Map<String, dynamic> map) {
|
||||
return ensureInitialized().decodeMap<EpisodeModel>(map);
|
||||
}
|
||||
|
||||
static EpisodeModel fromJson(String json) {
|
||||
return ensureInitialized().decodeJson<EpisodeModel>(json);
|
||||
}
|
||||
}
|
||||
|
||||
mixin EpisodeModelMappable {
|
||||
String toJson() {
|
||||
return EpisodeModelMapper.ensureInitialized()
|
||||
.encodeJson<EpisodeModel>(this as EpisodeModel);
|
||||
}
|
||||
|
||||
Map<String, dynamic> toMap() {
|
||||
return EpisodeModelMapper.ensureInitialized()
|
||||
.encodeMap<EpisodeModel>(this as EpisodeModel);
|
||||
}
|
||||
|
||||
EpisodeModelCopyWith<EpisodeModel, EpisodeModel, EpisodeModel> get copyWith =>
|
||||
_EpisodeModelCopyWithImpl(this as EpisodeModel, $identity, $identity);
|
||||
@override
|
||||
String toString() {
|
||||
return EpisodeModelMapper.ensureInitialized()
|
||||
.stringifyValue(this as EpisodeModel);
|
||||
}
|
||||
}
|
||||
|
||||
extension EpisodeModelValueCopy<$R, $Out>
|
||||
on ObjectCopyWith<$R, EpisodeModel, $Out> {
|
||||
EpisodeModelCopyWith<$R, EpisodeModel, $Out> get $asEpisodeModel =>
|
||||
$base.as((v, t, t2) => _EpisodeModelCopyWithImpl(v, t, t2));
|
||||
}
|
||||
|
||||
abstract class EpisodeModelCopyWith<$R, $In extends EpisodeModel, $Out>
|
||||
implements ItemStreamModelCopyWith<$R, $In, $Out> {
|
||||
ListCopyWith<$R, Chapter, ObjectCopyWith<$R, Chapter, Chapter>> get chapters;
|
||||
@override
|
||||
OverviewModelCopyWith<$R, OverviewModel, OverviewModel> get overview;
|
||||
@override
|
||||
UserDataCopyWith<$R, UserData, UserData> get userData;
|
||||
@override
|
||||
$R call(
|
||||
{String? seriesName,
|
||||
int? season,
|
||||
int? episode,
|
||||
List<Chapter>? chapters,
|
||||
ItemLocation? location,
|
||||
DateTime? dateAired,
|
||||
String? name,
|
||||
String? id,
|
||||
OverviewModel? overview,
|
||||
String? parentId,
|
||||
String? playlistId,
|
||||
ImagesData? images,
|
||||
int? childCount,
|
||||
double? primaryRatio,
|
||||
UserData? userData,
|
||||
ImagesData? parentImages,
|
||||
MediaStreamsModel? mediaStreams,
|
||||
bool? canDelete,
|
||||
bool? canDownload,
|
||||
dto.BaseItemKind? jellyType});
|
||||
EpisodeModelCopyWith<$R2, $In, $Out2> $chain<$R2, $Out2>(Then<$Out2, $R2> t);
|
||||
}
|
||||
|
||||
class _EpisodeModelCopyWithImpl<$R, $Out>
|
||||
extends ClassCopyWithBase<$R, EpisodeModel, $Out>
|
||||
implements EpisodeModelCopyWith<$R, EpisodeModel, $Out> {
|
||||
_EpisodeModelCopyWithImpl(super.value, super.then, super.then2);
|
||||
|
||||
@override
|
||||
late final ClassMapperBase<EpisodeModel> $mapper =
|
||||
EpisodeModelMapper.ensureInitialized();
|
||||
@override
|
||||
ListCopyWith<$R, Chapter, ObjectCopyWith<$R, Chapter, Chapter>>
|
||||
get chapters => ListCopyWith($value.chapters,
|
||||
(v, t) => ObjectCopyWith(v, $identity, t), (v) => call(chapters: v));
|
||||
@override
|
||||
OverviewModelCopyWith<$R, OverviewModel, OverviewModel> get overview =>
|
||||
$value.overview.copyWith.$chain((v) => call(overview: v));
|
||||
@override
|
||||
UserDataCopyWith<$R, UserData, UserData> get userData =>
|
||||
$value.userData.copyWith.$chain((v) => call(userData: v));
|
||||
@override
|
||||
$R call(
|
||||
{Object? seriesName = $none,
|
||||
int? season,
|
||||
int? episode,
|
||||
List<Chapter>? chapters,
|
||||
Object? location = $none,
|
||||
Object? dateAired = $none,
|
||||
String? name,
|
||||
String? id,
|
||||
OverviewModel? overview,
|
||||
Object? parentId = $none,
|
||||
Object? playlistId = $none,
|
||||
Object? images = $none,
|
||||
Object? childCount = $none,
|
||||
Object? primaryRatio = $none,
|
||||
UserData? userData,
|
||||
Object? parentImages = $none,
|
||||
MediaStreamsModel? mediaStreams,
|
||||
Object? canDelete = $none,
|
||||
Object? canDownload = $none,
|
||||
Object? jellyType = $none}) =>
|
||||
$apply(FieldCopyWithData({
|
||||
if (seriesName != $none) #seriesName: seriesName,
|
||||
if (season != null) #season: season,
|
||||
if (episode != null) #episode: episode,
|
||||
if (chapters != null) #chapters: chapters,
|
||||
if (location != $none) #location: location,
|
||||
if (dateAired != $none) #dateAired: dateAired,
|
||||
if (name != null) #name: name,
|
||||
if (id != null) #id: id,
|
||||
if (overview != null) #overview: overview,
|
||||
if (parentId != $none) #parentId: parentId,
|
||||
if (playlistId != $none) #playlistId: playlistId,
|
||||
if (images != $none) #images: images,
|
||||
if (childCount != $none) #childCount: childCount,
|
||||
if (primaryRatio != $none) #primaryRatio: primaryRatio,
|
||||
if (userData != null) #userData: userData,
|
||||
if (parentImages != $none) #parentImages: parentImages,
|
||||
if (mediaStreams != null) #mediaStreams: mediaStreams,
|
||||
if (canDelete != $none) #canDelete: canDelete,
|
||||
if (canDownload != $none) #canDownload: canDownload,
|
||||
if (jellyType != $none) #jellyType: jellyType
|
||||
}));
|
||||
@override
|
||||
EpisodeModel $make(CopyWithData data) => EpisodeModel(
|
||||
seriesName: data.get(#seriesName, or: $value.seriesName),
|
||||
season: data.get(#season, or: $value.season),
|
||||
episode: data.get(#episode, or: $value.episode),
|
||||
chapters: data.get(#chapters, or: $value.chapters),
|
||||
location: data.get(#location, or: $value.location),
|
||||
dateAired: data.get(#dateAired, or: $value.dateAired),
|
||||
name: data.get(#name, or: $value.name),
|
||||
id: data.get(#id, or: $value.id),
|
||||
overview: data.get(#overview, or: $value.overview),
|
||||
parentId: data.get(#parentId, or: $value.parentId),
|
||||
playlistId: data.get(#playlistId, or: $value.playlistId),
|
||||
images: data.get(#images, or: $value.images),
|
||||
childCount: data.get(#childCount, or: $value.childCount),
|
||||
primaryRatio: data.get(#primaryRatio, or: $value.primaryRatio),
|
||||
userData: data.get(#userData, or: $value.userData),
|
||||
parentImages: data.get(#parentImages, or: $value.parentImages),
|
||||
mediaStreams: data.get(#mediaStreams, or: $value.mediaStreams),
|
||||
canDelete: data.get(#canDelete, or: $value.canDelete),
|
||||
canDownload: data.get(#canDownload, or: $value.canDownload),
|
||||
jellyType: data.get(#jellyType, or: $value.jellyType));
|
||||
|
||||
@override
|
||||
EpisodeModelCopyWith<$R2, EpisodeModel, $Out2> $chain<$R2, $Out2>(
|
||||
Then<$Out2, $R2> t) =>
|
||||
_EpisodeModelCopyWithImpl($value, $cast, t);
|
||||
}
|
||||
50
lib/models/items/folder_model.dart
Normal file
50
lib/models/items/folder_model.dart
Normal file
|
|
@ -0,0 +1,50 @@
|
|||
// ignore_for_file: public_member_api_docs, sort_constructors_first
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
|
||||
import 'package:fladder/jellyfin/jellyfin_open_api.swagger.dart';
|
||||
import 'package:fladder/models/item_base_model.dart';
|
||||
import 'package:fladder/models/items/images_models.dart';
|
||||
import 'package:fladder/models/items/item_shared_models.dart';
|
||||
import 'package:fladder/models/items/overview_model.dart';
|
||||
|
||||
import 'package:dart_mappable/dart_mappable.dart';
|
||||
|
||||
part 'folder_model.mapper.dart';
|
||||
|
||||
@MappableClass()
|
||||
class FolderModel extends ItemBaseModel with FolderModelMappable {
|
||||
final List<ItemBaseModel> items;
|
||||
|
||||
const FolderModel({
|
||||
required this.items,
|
||||
required super.overview,
|
||||
required super.parentId,
|
||||
required super.playlistId,
|
||||
required super.images,
|
||||
required super.childCount,
|
||||
required super.primaryRatio,
|
||||
required super.userData,
|
||||
required super.name,
|
||||
required super.id,
|
||||
super.canDownload,
|
||||
super.canDelete,
|
||||
super.jellyType,
|
||||
});
|
||||
|
||||
factory FolderModel.fromBaseDto(BaseItemDto item, Ref ref) {
|
||||
return FolderModel(
|
||||
name: item.name ?? "",
|
||||
id: item.id ?? "",
|
||||
childCount: item.childCount,
|
||||
overview: OverviewModel.fromBaseItemDto(item, ref),
|
||||
userData: UserData.fromDto(item.userData),
|
||||
parentId: item.parentId,
|
||||
playlistId: item.playlistItemId,
|
||||
images: ImagesData.fromBaseItem(item, ref),
|
||||
primaryRatio: item.primaryImageAspectRatio,
|
||||
items: [],
|
||||
canDelete: item.canDelete,
|
||||
canDownload: item.canDownload,
|
||||
);
|
||||
}
|
||||
}
|
||||
242
lib/models/items/folder_model.mapper.dart
Normal file
242
lib/models/items/folder_model.mapper.dart
Normal file
|
|
@ -0,0 +1,242 @@
|
|||
// coverage:ignore-file
|
||||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
// ignore_for_file: type=lint
|
||||
// ignore_for_file: unused_element, unnecessary_cast, override_on_non_overriding_member
|
||||
// ignore_for_file: strict_raw_type, inference_failure_on_untyped_parameter
|
||||
|
||||
part of 'folder_model.dart';
|
||||
|
||||
class FolderModelMapper extends SubClassMapperBase<FolderModel> {
|
||||
FolderModelMapper._();
|
||||
|
||||
static FolderModelMapper? _instance;
|
||||
static FolderModelMapper ensureInitialized() {
|
||||
if (_instance == null) {
|
||||
MapperContainer.globals.use(_instance = FolderModelMapper._());
|
||||
ItemBaseModelMapper.ensureInitialized().addSubMapper(_instance!);
|
||||
ItemBaseModelMapper.ensureInitialized();
|
||||
OverviewModelMapper.ensureInitialized();
|
||||
UserDataMapper.ensureInitialized();
|
||||
}
|
||||
return _instance!;
|
||||
}
|
||||
|
||||
@override
|
||||
final String id = 'FolderModel';
|
||||
|
||||
static List<ItemBaseModel> _$items(FolderModel v) => v.items;
|
||||
static const Field<FolderModel, List<ItemBaseModel>> _f$items =
|
||||
Field('items', _$items);
|
||||
static OverviewModel _$overview(FolderModel v) => v.overview;
|
||||
static const Field<FolderModel, OverviewModel> _f$overview =
|
||||
Field('overview', _$overview);
|
||||
static String? _$parentId(FolderModel v) => v.parentId;
|
||||
static const Field<FolderModel, String> _f$parentId =
|
||||
Field('parentId', _$parentId);
|
||||
static String? _$playlistId(FolderModel v) => v.playlistId;
|
||||
static const Field<FolderModel, String> _f$playlistId =
|
||||
Field('playlistId', _$playlistId);
|
||||
static ImagesData? _$images(FolderModel v) => v.images;
|
||||
static const Field<FolderModel, ImagesData> _f$images =
|
||||
Field('images', _$images);
|
||||
static int? _$childCount(FolderModel v) => v.childCount;
|
||||
static const Field<FolderModel, int> _f$childCount =
|
||||
Field('childCount', _$childCount);
|
||||
static double? _$primaryRatio(FolderModel v) => v.primaryRatio;
|
||||
static const Field<FolderModel, double> _f$primaryRatio =
|
||||
Field('primaryRatio', _$primaryRatio);
|
||||
static UserData _$userData(FolderModel v) => v.userData;
|
||||
static const Field<FolderModel, UserData> _f$userData =
|
||||
Field('userData', _$userData);
|
||||
static String _$name(FolderModel v) => v.name;
|
||||
static const Field<FolderModel, String> _f$name = Field('name', _$name);
|
||||
static String _$id(FolderModel v) => v.id;
|
||||
static const Field<FolderModel, String> _f$id = Field('id', _$id);
|
||||
static bool? _$canDownload(FolderModel v) => v.canDownload;
|
||||
static const Field<FolderModel, bool> _f$canDownload =
|
||||
Field('canDownload', _$canDownload, opt: true);
|
||||
static bool? _$canDelete(FolderModel v) => v.canDelete;
|
||||
static const Field<FolderModel, bool> _f$canDelete =
|
||||
Field('canDelete', _$canDelete, opt: true);
|
||||
static BaseItemKind? _$jellyType(FolderModel v) => v.jellyType;
|
||||
static const Field<FolderModel, BaseItemKind> _f$jellyType =
|
||||
Field('jellyType', _$jellyType, opt: true);
|
||||
|
||||
@override
|
||||
final MappableFields<FolderModel> fields = const {
|
||||
#items: _f$items,
|
||||
#overview: _f$overview,
|
||||
#parentId: _f$parentId,
|
||||
#playlistId: _f$playlistId,
|
||||
#images: _f$images,
|
||||
#childCount: _f$childCount,
|
||||
#primaryRatio: _f$primaryRatio,
|
||||
#userData: _f$userData,
|
||||
#name: _f$name,
|
||||
#id: _f$id,
|
||||
#canDownload: _f$canDownload,
|
||||
#canDelete: _f$canDelete,
|
||||
#jellyType: _f$jellyType,
|
||||
};
|
||||
@override
|
||||
final bool ignoreNull = true;
|
||||
|
||||
@override
|
||||
final String discriminatorKey = 'type';
|
||||
@override
|
||||
final dynamic discriminatorValue = 'FolderModel';
|
||||
@override
|
||||
late final ClassMapperBase superMapper =
|
||||
ItemBaseModelMapper.ensureInitialized();
|
||||
|
||||
static FolderModel _instantiate(DecodingData data) {
|
||||
return FolderModel(
|
||||
items: data.dec(_f$items),
|
||||
overview: data.dec(_f$overview),
|
||||
parentId: data.dec(_f$parentId),
|
||||
playlistId: data.dec(_f$playlistId),
|
||||
images: data.dec(_f$images),
|
||||
childCount: data.dec(_f$childCount),
|
||||
primaryRatio: data.dec(_f$primaryRatio),
|
||||
userData: data.dec(_f$userData),
|
||||
name: data.dec(_f$name),
|
||||
id: data.dec(_f$id),
|
||||
canDownload: data.dec(_f$canDownload),
|
||||
canDelete: data.dec(_f$canDelete),
|
||||
jellyType: data.dec(_f$jellyType));
|
||||
}
|
||||
|
||||
@override
|
||||
final Function instantiate = _instantiate;
|
||||
|
||||
static FolderModel fromMap(Map<String, dynamic> map) {
|
||||
return ensureInitialized().decodeMap<FolderModel>(map);
|
||||
}
|
||||
|
||||
static FolderModel fromJson(String json) {
|
||||
return ensureInitialized().decodeJson<FolderModel>(json);
|
||||
}
|
||||
}
|
||||
|
||||
mixin FolderModelMappable {
|
||||
String toJson() {
|
||||
return FolderModelMapper.ensureInitialized()
|
||||
.encodeJson<FolderModel>(this as FolderModel);
|
||||
}
|
||||
|
||||
Map<String, dynamic> toMap() {
|
||||
return FolderModelMapper.ensureInitialized()
|
||||
.encodeMap<FolderModel>(this as FolderModel);
|
||||
}
|
||||
|
||||
FolderModelCopyWith<FolderModel, FolderModel, FolderModel> get copyWith =>
|
||||
_FolderModelCopyWithImpl(this as FolderModel, $identity, $identity);
|
||||
@override
|
||||
String toString() {
|
||||
return FolderModelMapper.ensureInitialized()
|
||||
.stringifyValue(this as FolderModel);
|
||||
}
|
||||
}
|
||||
|
||||
extension FolderModelValueCopy<$R, $Out>
|
||||
on ObjectCopyWith<$R, FolderModel, $Out> {
|
||||
FolderModelCopyWith<$R, FolderModel, $Out> get $asFolderModel =>
|
||||
$base.as((v, t, t2) => _FolderModelCopyWithImpl(v, t, t2));
|
||||
}
|
||||
|
||||
abstract class FolderModelCopyWith<$R, $In extends FolderModel, $Out>
|
||||
implements ItemBaseModelCopyWith<$R, $In, $Out> {
|
||||
ListCopyWith<$R, ItemBaseModel,
|
||||
ItemBaseModelCopyWith<$R, ItemBaseModel, ItemBaseModel>> get items;
|
||||
@override
|
||||
OverviewModelCopyWith<$R, OverviewModel, OverviewModel> get overview;
|
||||
@override
|
||||
UserDataCopyWith<$R, UserData, UserData> get userData;
|
||||
@override
|
||||
$R call(
|
||||
{List<ItemBaseModel>? items,
|
||||
OverviewModel? overview,
|
||||
String? parentId,
|
||||
String? playlistId,
|
||||
ImagesData? images,
|
||||
int? childCount,
|
||||
double? primaryRatio,
|
||||
UserData? userData,
|
||||
String? name,
|
||||
String? id,
|
||||
bool? canDownload,
|
||||
bool? canDelete,
|
||||
BaseItemKind? jellyType});
|
||||
FolderModelCopyWith<$R2, $In, $Out2> $chain<$R2, $Out2>(Then<$Out2, $R2> t);
|
||||
}
|
||||
|
||||
class _FolderModelCopyWithImpl<$R, $Out>
|
||||
extends ClassCopyWithBase<$R, FolderModel, $Out>
|
||||
implements FolderModelCopyWith<$R, FolderModel, $Out> {
|
||||
_FolderModelCopyWithImpl(super.value, super.then, super.then2);
|
||||
|
||||
@override
|
||||
late final ClassMapperBase<FolderModel> $mapper =
|
||||
FolderModelMapper.ensureInitialized();
|
||||
@override
|
||||
ListCopyWith<$R, ItemBaseModel,
|
||||
ItemBaseModelCopyWith<$R, ItemBaseModel, ItemBaseModel>>
|
||||
get items => ListCopyWith(
|
||||
$value.items, (v, t) => v.copyWith.$chain(t), (v) => call(items: v));
|
||||
@override
|
||||
OverviewModelCopyWith<$R, OverviewModel, OverviewModel> get overview =>
|
||||
$value.overview.copyWith.$chain((v) => call(overview: v));
|
||||
@override
|
||||
UserDataCopyWith<$R, UserData, UserData> get userData =>
|
||||
$value.userData.copyWith.$chain((v) => call(userData: v));
|
||||
@override
|
||||
$R call(
|
||||
{List<ItemBaseModel>? items,
|
||||
OverviewModel? overview,
|
||||
Object? parentId = $none,
|
||||
Object? playlistId = $none,
|
||||
Object? images = $none,
|
||||
Object? childCount = $none,
|
||||
Object? primaryRatio = $none,
|
||||
UserData? userData,
|
||||
String? name,
|
||||
String? id,
|
||||
Object? canDownload = $none,
|
||||
Object? canDelete = $none,
|
||||
Object? jellyType = $none}) =>
|
||||
$apply(FieldCopyWithData({
|
||||
if (items != null) #items: items,
|
||||
if (overview != null) #overview: overview,
|
||||
if (parentId != $none) #parentId: parentId,
|
||||
if (playlistId != $none) #playlistId: playlistId,
|
||||
if (images != $none) #images: images,
|
||||
if (childCount != $none) #childCount: childCount,
|
||||
if (primaryRatio != $none) #primaryRatio: primaryRatio,
|
||||
if (userData != null) #userData: userData,
|
||||
if (name != null) #name: name,
|
||||
if (id != null) #id: id,
|
||||
if (canDownload != $none) #canDownload: canDownload,
|
||||
if (canDelete != $none) #canDelete: canDelete,
|
||||
if (jellyType != $none) #jellyType: jellyType
|
||||
}));
|
||||
@override
|
||||
FolderModel $make(CopyWithData data) => FolderModel(
|
||||
items: data.get(#items, or: $value.items),
|
||||
overview: data.get(#overview, or: $value.overview),
|
||||
parentId: data.get(#parentId, or: $value.parentId),
|
||||
playlistId: data.get(#playlistId, or: $value.playlistId),
|
||||
images: data.get(#images, or: $value.images),
|
||||
childCount: data.get(#childCount, or: $value.childCount),
|
||||
primaryRatio: data.get(#primaryRatio, or: $value.primaryRatio),
|
||||
userData: data.get(#userData, or: $value.userData),
|
||||
name: data.get(#name, or: $value.name),
|
||||
id: data.get(#id, or: $value.id),
|
||||
canDownload: data.get(#canDownload, or: $value.canDownload),
|
||||
canDelete: data.get(#canDelete, or: $value.canDelete),
|
||||
jellyType: data.get(#jellyType, or: $value.jellyType));
|
||||
|
||||
@override
|
||||
FolderModelCopyWith<$R2, FolderModel, $Out2> $chain<$R2, $Out2>(
|
||||
Then<$Out2, $R2> t) =>
|
||||
_FolderModelCopyWithImpl($value, $cast, t);
|
||||
}
|
||||
286
lib/models/items/images_models.dart
Normal file
286
lib/models/items/images_models.dart
Normal file
|
|
@ -0,0 +1,286 @@
|
|||
import 'dart:convert';
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:cached_network_image/cached_network_image.dart';
|
||||
import 'package:collection/collection.dart';
|
||||
import 'package:fladder/main.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/widgets.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
|
||||
import 'package:fladder/jellyfin/jellyfin_open_api.enums.swagger.dart' as enums;
|
||||
import 'package:fladder/jellyfin/jellyfin_open_api.swagger.dart' as dto;
|
||||
import 'package:fladder/providers/image_provider.dart';
|
||||
import 'package:fladder/util/jelly_id.dart';
|
||||
|
||||
class ImagesData {
|
||||
final ImageData? primary;
|
||||
final List<ImageData>? backDrop;
|
||||
final ImageData? logo;
|
||||
ImagesData({
|
||||
this.primary,
|
||||
this.backDrop,
|
||||
this.logo,
|
||||
});
|
||||
|
||||
bool get isEmpty {
|
||||
if (primary == null && backDrop == null) return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
ImageData? get firstOrNull {
|
||||
return primary ?? backDrop?[0];
|
||||
}
|
||||
|
||||
ImageData? get randomBackDrop => (backDrop?..shuffle())?.firstOrNull ?? primary;
|
||||
|
||||
factory ImagesData.fromBaseItem(
|
||||
dto.BaseItemDto item,
|
||||
Ref ref, {
|
||||
Size backDrop = const Size(2000, 2000),
|
||||
Size logo = const Size(1000, 1000),
|
||||
Size primary = const Size(600, 600),
|
||||
bool getOriginalSize = false,
|
||||
int quality = 90,
|
||||
}) {
|
||||
final newImgesData = ImagesData(
|
||||
primary: item.imageTags?['Primary'] != null
|
||||
? ImageData(
|
||||
path: getOriginalSize
|
||||
? ref.read(imageUtilityProvider).getItemsOrigImageUrl(
|
||||
item.id!,
|
||||
type: enums.ImageType.primary,
|
||||
)
|
||||
: ref.read(imageUtilityProvider).getItemsImageUrl(
|
||||
(item.id!),
|
||||
type: enums.ImageType.primary,
|
||||
maxHeight: primary.height.toInt(),
|
||||
maxWidth: primary.width.toInt(),
|
||||
quality: quality,
|
||||
),
|
||||
key: item.imageTags?['Primary'],
|
||||
hash: item.imageBlurHashes?.primary?[item.imageTags?['Primary']] as String? ?? "",
|
||||
)
|
||||
: null,
|
||||
logo: item.imageTags?['Logo'] != null
|
||||
? ImageData(
|
||||
path: getOriginalSize
|
||||
? ref.read(imageUtilityProvider).getItemsOrigImageUrl(
|
||||
item.id!,
|
||||
type: enums.ImageType.logo,
|
||||
)
|
||||
: ref.read(imageUtilityProvider).getItemsImageUrl(
|
||||
(item.id!),
|
||||
type: enums.ImageType.logo,
|
||||
maxHeight: logo.height.toInt(),
|
||||
maxWidth: logo.width.toInt(),
|
||||
quality: quality,
|
||||
),
|
||||
key: item.imageTags?['Logo'],
|
||||
hash: item.imageBlurHashes?.logo?[item.imageTags?['Logo']] as String? ?? "")
|
||||
: null,
|
||||
backDrop: (item.backdropImageTags ?? []).mapIndexed(
|
||||
(index, backdrop) {
|
||||
final image = ImageData(
|
||||
path: getOriginalSize
|
||||
? ref.read(imageUtilityProvider).getBackdropOrigImage(
|
||||
item.id!,
|
||||
index,
|
||||
backdrop,
|
||||
)
|
||||
: ref.read(imageUtilityProvider).getBackdropImage(
|
||||
(item.id!),
|
||||
index,
|
||||
backdrop,
|
||||
maxHeight: backDrop.height.toInt(),
|
||||
maxWidth: backDrop.width.toInt(),
|
||||
quality: quality,
|
||||
),
|
||||
key: backdrop,
|
||||
hash: item.imageBlurHashes?.backdrop?[backdrop] ?? jellyId,
|
||||
);
|
||||
return image;
|
||||
},
|
||||
).toList(),
|
||||
);
|
||||
return newImgesData;
|
||||
}
|
||||
|
||||
static ImagesData? fromBaseItemParent(
|
||||
dto.BaseItemDto item,
|
||||
Ref ref, {
|
||||
Size backDrop = const Size(2000, 2000),
|
||||
Size logo = const Size(1000, 1000),
|
||||
Size primary = const Size(600, 600),
|
||||
int quality = 90,
|
||||
}) {
|
||||
if (item.seriesId == null && item.parentId == null) return null;
|
||||
final newImgesData = ImagesData(
|
||||
primary: (item.seriesPrimaryImageTag != null)
|
||||
? ImageData(
|
||||
path: ref.read(imageUtilityProvider).getItemsImageUrl(
|
||||
(item.seriesId!),
|
||||
type: enums.ImageType.primary,
|
||||
maxHeight: primary.height.toInt(),
|
||||
maxWidth: primary.width.toInt(),
|
||||
quality: quality,
|
||||
),
|
||||
key: item.seriesPrimaryImageTag ?? "",
|
||||
hash: item.imageBlurHashes?.primary?[item.seriesPrimaryImageTag] as String? ?? "")
|
||||
: null,
|
||||
logo: (item.parentLogoImageTag != null)
|
||||
? ImageData(
|
||||
path: ref.read(imageUtilityProvider).getItemsImageUrl(
|
||||
(item.seriesId!),
|
||||
type: enums.ImageType.logo,
|
||||
maxHeight: logo.height.toInt(),
|
||||
maxWidth: logo.width.toInt(),
|
||||
quality: quality,
|
||||
),
|
||||
key: item.parentLogoImageTag ?? "",
|
||||
hash: item.imageBlurHashes?.logo?[item.parentLogoImageTag] as String? ?? "")
|
||||
: null,
|
||||
backDrop: (item.backdropImageTags ?? []).mapIndexed(
|
||||
(index, backdrop) {
|
||||
final image = ImageData(
|
||||
path: ref.read(imageUtilityProvider).getBackdropImage(
|
||||
((item.seriesId ?? item.parentId)!),
|
||||
index,
|
||||
backdrop,
|
||||
maxHeight: backDrop.height.toInt(),
|
||||
maxWidth: backDrop.width.toInt(),
|
||||
quality: quality,
|
||||
),
|
||||
key: backdrop,
|
||||
hash: item.imageBlurHashes?.backdrop?[backdrop],
|
||||
);
|
||||
return image;
|
||||
},
|
||||
).toList(),
|
||||
);
|
||||
return newImgesData;
|
||||
}
|
||||
|
||||
static ImagesData? fromPersonDto(
|
||||
dto.BaseItemPerson item,
|
||||
Ref ref, {
|
||||
Size backDrop = const Size(2000, 2000),
|
||||
Size logo = const Size(1000, 1000),
|
||||
Size primary = const Size(2000, 2000),
|
||||
int quality = 90,
|
||||
}) {
|
||||
return ImagesData(
|
||||
primary: (item.primaryImageTag != null && item.imageBlurHashes != null)
|
||||
? ImageData(
|
||||
path: ref.read(imageUtilityProvider).getItemsImageUrl(
|
||||
item.id ?? "",
|
||||
type: enums.ImageType.primary,
|
||||
maxHeight: primary.height.toInt(),
|
||||
maxWidth: primary.width.toInt(),
|
||||
quality: quality,
|
||||
),
|
||||
key: item.primaryImageTag ?? "",
|
||||
hash: item.imageBlurHashes?.primary?[item.primaryImageTag] as String? ?? jellyId)
|
||||
: null,
|
||||
logo: null,
|
||||
backDrop: null,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
String toString() => 'ImagesData(primary: $primary, backDrop: $backDrop, logo: $logo)';
|
||||
|
||||
ImagesData copyWith({
|
||||
ValueGetter<ImageData?>? primary,
|
||||
ValueGetter<List<ImageData>?>? backDrop,
|
||||
ValueGetter<ImageData?>? logo,
|
||||
}) {
|
||||
return ImagesData(
|
||||
primary: primary != null ? primary() : this.primary,
|
||||
backDrop: backDrop != null ? backDrop() : this.backDrop,
|
||||
logo: logo != null ? logo() : this.logo,
|
||||
);
|
||||
}
|
||||
|
||||
Map<String, dynamic> toMap() {
|
||||
return {
|
||||
'primary': primary?.toMap(),
|
||||
'backDrop': backDrop?.map((x) => x.toMap()).toList(),
|
||||
'logo': logo?.toMap(),
|
||||
};
|
||||
}
|
||||
|
||||
factory ImagesData.fromMap(Map<String, dynamic> map) {
|
||||
return ImagesData(
|
||||
primary: map['primary'] != null ? ImageData.fromMap(map['primary']) : null,
|
||||
backDrop:
|
||||
map['backDrop'] != null ? List<ImageData>.from(map['backDrop']?.map((x) => ImageData.fromMap(x))) : null,
|
||||
logo: map['logo'] != null ? ImageData.fromMap(map['logo']) : null,
|
||||
);
|
||||
}
|
||||
|
||||
String toJson() => json.encode(toMap());
|
||||
|
||||
factory ImagesData.fromJson(String source) => ImagesData.fromMap(json.decode(source));
|
||||
}
|
||||
|
||||
class ImageData {
|
||||
final String path;
|
||||
final String hash;
|
||||
final String key;
|
||||
ImageData({
|
||||
this.path = '',
|
||||
this.hash = '',
|
||||
this.key = '',
|
||||
});
|
||||
|
||||
ImageProvider get imageProvider {
|
||||
if (path.startsWith("http")) {
|
||||
return CachedNetworkImageProvider(
|
||||
cacheKey: key,
|
||||
cacheManager: CustomCacheManager.instance,
|
||||
path,
|
||||
);
|
||||
} else {
|
||||
return Image.file(
|
||||
key: Key(key),
|
||||
File(path),
|
||||
).image;
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
String toString() => 'ImageData(path: $path, hash: $hash, key: $key)';
|
||||
|
||||
ImageData copyWith({
|
||||
String? path,
|
||||
String? hash,
|
||||
String? key,
|
||||
}) {
|
||||
return ImageData(
|
||||
path: path ?? this.path,
|
||||
hash: hash ?? this.hash,
|
||||
key: key ?? this.key,
|
||||
);
|
||||
}
|
||||
|
||||
Map<String, dynamic> toMap() {
|
||||
return {
|
||||
'path': path,
|
||||
'hash': hash,
|
||||
'key': key,
|
||||
};
|
||||
}
|
||||
|
||||
factory ImageData.fromMap(Map<String, dynamic> map) {
|
||||
return ImageData(
|
||||
path: map['path'] ?? '',
|
||||
hash: map['hash'] ?? '',
|
||||
key: map['key'] ?? '',
|
||||
);
|
||||
}
|
||||
|
||||
String toJson() => json.encode(toMap());
|
||||
|
||||
factory ImageData.fromJson(String source) => ImageData.fromMap(json.decode(source));
|
||||
}
|
||||
47
lib/models/items/intro_skip_model.dart
Normal file
47
lib/models/items/intro_skip_model.dart
Normal file
|
|
@ -0,0 +1,47 @@
|
|||
// 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;
|
||||
565
lib/models/items/intro_skip_model.freezed.dart
Normal file
565
lib/models/items/intro_skip_model.freezed.dart
Normal file
|
|
@ -0,0 +1,565 @@
|
|||
// 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;
|
||||
|
||||
Map<String, dynamic> toJson() => throw _privateConstructorUsedError;
|
||||
@JsonKey(ignore: true)
|
||||
$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;
|
||||
|
||||
@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);
|
||||
}
|
||||
|
||||
@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);
|
||||
});
|
||||
}
|
||||
|
||||
@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);
|
||||
|
||||
@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(ignore: true)
|
||||
@override
|
||||
int get hashCode => Object.hash(runtimeType, intro, credits);
|
||||
|
||||
@JsonKey(ignore: true)
|
||||
@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;
|
||||
@override
|
||||
@JsonKey(ignore: true)
|
||||
_$$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;
|
||||
|
||||
Map<String, dynamic> toJson() => throw _privateConstructorUsedError;
|
||||
@JsonKey(ignore: true)
|
||||
$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;
|
||||
|
||||
@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);
|
||||
|
||||
@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(ignore: true)
|
||||
@override
|
||||
int get hashCode =>
|
||||
Object.hash(runtimeType, id, valid, start, end, showTime, hideTime);
|
||||
|
||||
@JsonKey(ignore: true)
|
||||
@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;
|
||||
@override
|
||||
@JsonKey(ignore: true)
|
||||
_$$IntroSkipModelImplCopyWith<_$IntroSkipModelImpl> get copyWith =>
|
||||
throw _privateConstructorUsedError;
|
||||
}
|
||||
46
lib/models/items/intro_skip_model.g.dart
Normal file
46
lib/models/items/intro_skip_model.g.dart
Normal file
|
|
@ -0,0 +1,46 @@
|
|||
// 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),
|
||||
};
|
||||
21
lib/models/items/item_properties_model.dart
Normal file
21
lib/models/items/item_properties_model.dart
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
import 'package:fladder/jellyfin/jellyfin_open_api.swagger.dart' as dto;
|
||||
import 'package:freezed_annotation/freezed_annotation.dart';
|
||||
|
||||
part 'item_properties_model.freezed.dart';
|
||||
|
||||
@Freezed(fromJson: false, toJson: false)
|
||||
class ItemPropertiesModel with _$ItemPropertiesModel {
|
||||
const ItemPropertiesModel._();
|
||||
|
||||
factory ItemPropertiesModel._internal({
|
||||
required bool canDelete,
|
||||
required bool canDownload,
|
||||
}) = _ItemPropertiesModel;
|
||||
|
||||
factory ItemPropertiesModel.fromBaseDto(dto.BaseItemDto dtoItem) {
|
||||
return ItemPropertiesModel._internal(
|
||||
canDelete: dtoItem.canDelete ?? false,
|
||||
canDownload: dtoItem.canDownload ?? false,
|
||||
);
|
||||
}
|
||||
}
|
||||
156
lib/models/items/item_properties_model.freezed.dart
Normal file
156
lib/models/items/item_properties_model.freezed.dart
Normal file
|
|
@ -0,0 +1,156 @@
|
|||
// 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 'item_properties_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');
|
||||
|
||||
/// @nodoc
|
||||
mixin _$ItemPropertiesModel {
|
||||
bool get canDelete => throw _privateConstructorUsedError;
|
||||
bool get canDownload => throw _privateConstructorUsedError;
|
||||
|
||||
@JsonKey(ignore: true)
|
||||
$ItemPropertiesModelCopyWith<ItemPropertiesModel> get copyWith =>
|
||||
throw _privateConstructorUsedError;
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
abstract class $ItemPropertiesModelCopyWith<$Res> {
|
||||
factory $ItemPropertiesModelCopyWith(
|
||||
ItemPropertiesModel value, $Res Function(ItemPropertiesModel) then) =
|
||||
_$ItemPropertiesModelCopyWithImpl<$Res, ItemPropertiesModel>;
|
||||
@useResult
|
||||
$Res call({bool canDelete, bool canDownload});
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
class _$ItemPropertiesModelCopyWithImpl<$Res, $Val extends ItemPropertiesModel>
|
||||
implements $ItemPropertiesModelCopyWith<$Res> {
|
||||
_$ItemPropertiesModelCopyWithImpl(this._value, this._then);
|
||||
|
||||
// ignore: unused_field
|
||||
final $Val _value;
|
||||
// ignore: unused_field
|
||||
final $Res Function($Val) _then;
|
||||
|
||||
@pragma('vm:prefer-inline')
|
||||
@override
|
||||
$Res call({
|
||||
Object? canDelete = null,
|
||||
Object? canDownload = null,
|
||||
}) {
|
||||
return _then(_value.copyWith(
|
||||
canDelete: null == canDelete
|
||||
? _value.canDelete
|
||||
: canDelete // ignore: cast_nullable_to_non_nullable
|
||||
as bool,
|
||||
canDownload: null == canDownload
|
||||
? _value.canDownload
|
||||
: canDownload // ignore: cast_nullable_to_non_nullable
|
||||
as bool,
|
||||
) as $Val);
|
||||
}
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
abstract class _$$ItemPropertiesModelImplCopyWith<$Res>
|
||||
implements $ItemPropertiesModelCopyWith<$Res> {
|
||||
factory _$$ItemPropertiesModelImplCopyWith(_$ItemPropertiesModelImpl value,
|
||||
$Res Function(_$ItemPropertiesModelImpl) then) =
|
||||
__$$ItemPropertiesModelImplCopyWithImpl<$Res>;
|
||||
@override
|
||||
@useResult
|
||||
$Res call({bool canDelete, bool canDownload});
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
class __$$ItemPropertiesModelImplCopyWithImpl<$Res>
|
||||
extends _$ItemPropertiesModelCopyWithImpl<$Res, _$ItemPropertiesModelImpl>
|
||||
implements _$$ItemPropertiesModelImplCopyWith<$Res> {
|
||||
__$$ItemPropertiesModelImplCopyWithImpl(_$ItemPropertiesModelImpl _value,
|
||||
$Res Function(_$ItemPropertiesModelImpl) _then)
|
||||
: super(_value, _then);
|
||||
|
||||
@pragma('vm:prefer-inline')
|
||||
@override
|
||||
$Res call({
|
||||
Object? canDelete = null,
|
||||
Object? canDownload = null,
|
||||
}) {
|
||||
return _then(_$ItemPropertiesModelImpl(
|
||||
canDelete: null == canDelete
|
||||
? _value.canDelete
|
||||
: canDelete // ignore: cast_nullable_to_non_nullable
|
||||
as bool,
|
||||
canDownload: null == canDownload
|
||||
? _value.canDownload
|
||||
: canDownload // ignore: cast_nullable_to_non_nullable
|
||||
as bool,
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
|
||||
class _$ItemPropertiesModelImpl extends _ItemPropertiesModel {
|
||||
_$ItemPropertiesModelImpl(
|
||||
{required this.canDelete, required this.canDownload})
|
||||
: super._();
|
||||
|
||||
@override
|
||||
final bool canDelete;
|
||||
@override
|
||||
final bool canDownload;
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'ItemPropertiesModel._internal(canDelete: $canDelete, canDownload: $canDownload)';
|
||||
}
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
return identical(this, other) ||
|
||||
(other.runtimeType == runtimeType &&
|
||||
other is _$ItemPropertiesModelImpl &&
|
||||
(identical(other.canDelete, canDelete) ||
|
||||
other.canDelete == canDelete) &&
|
||||
(identical(other.canDownload, canDownload) ||
|
||||
other.canDownload == canDownload));
|
||||
}
|
||||
|
||||
@override
|
||||
int get hashCode => Object.hash(runtimeType, canDelete, canDownload);
|
||||
|
||||
@JsonKey(ignore: true)
|
||||
@override
|
||||
@pragma('vm:prefer-inline')
|
||||
_$$ItemPropertiesModelImplCopyWith<_$ItemPropertiesModelImpl> get copyWith =>
|
||||
__$$ItemPropertiesModelImplCopyWithImpl<_$ItemPropertiesModelImpl>(
|
||||
this, _$identity);
|
||||
}
|
||||
|
||||
abstract class _ItemPropertiesModel extends ItemPropertiesModel {
|
||||
factory _ItemPropertiesModel(
|
||||
{required final bool canDelete,
|
||||
required final bool canDownload}) = _$ItemPropertiesModelImpl;
|
||||
_ItemPropertiesModel._() : super._();
|
||||
|
||||
@override
|
||||
bool get canDelete;
|
||||
@override
|
||||
bool get canDownload;
|
||||
@override
|
||||
@JsonKey(ignore: true)
|
||||
_$$ItemPropertiesModelImplCopyWith<_$ItemPropertiesModelImpl> get copyWith =>
|
||||
throw _privateConstructorUsedError;
|
||||
}
|
||||
380
lib/models/items/item_shared_models.dart
Normal file
380
lib/models/items/item_shared_models.dart
Normal file
|
|
@ -0,0 +1,380 @@
|
|||
// ignore_for_file: public_member_api_docs, sort_constructors_first
|
||||
import 'dart:convert';
|
||||
|
||||
import 'package:collection/collection.dart';
|
||||
import 'package:fladder/jellyfin/jellyfin_open_api.enums.swagger.dart';
|
||||
import 'package:flutter/widgets.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
|
||||
import 'package:fladder/jellyfin/jellyfin_open_api.swagger.dart' as dto;
|
||||
import 'package:fladder/models/items/images_models.dart';
|
||||
|
||||
import 'package:dart_mappable/dart_mappable.dart';
|
||||
import 'package:json_annotation/json_annotation.dart';
|
||||
|
||||
part 'item_shared_models.mapper.dart';
|
||||
|
||||
@MappableClass()
|
||||
class UserData with UserDataMappable {
|
||||
final bool isFavourite;
|
||||
final int playCount;
|
||||
final int? unPlayedItemCount;
|
||||
final int playbackPositionTicks;
|
||||
final double progress;
|
||||
final bool played;
|
||||
final DateTime? lastPlayed;
|
||||
const UserData({
|
||||
this.isFavourite = false,
|
||||
this.playCount = 0,
|
||||
this.unPlayedItemCount,
|
||||
this.playbackPositionTicks = 0,
|
||||
this.progress = 0,
|
||||
this.lastPlayed,
|
||||
this.played = false,
|
||||
});
|
||||
|
||||
factory UserData.fromDto(dto.UserItemDataDto? dto) {
|
||||
if (dto == null) {
|
||||
return UserData();
|
||||
}
|
||||
return UserData(
|
||||
isFavourite: dto.isFavorite ?? false,
|
||||
playCount: dto.playCount ?? 0,
|
||||
playbackPositionTicks: dto.playbackPositionTicks ?? 0,
|
||||
played: dto.played ?? false,
|
||||
unPlayedItemCount: dto.unplayedItemCount ?? 0,
|
||||
lastPlayed: dto.lastPlayedDate,
|
||||
progress: dto.playedPercentage ?? 0,
|
||||
);
|
||||
}
|
||||
|
||||
Duration get playBackPosition => Duration(milliseconds: playbackPositionTicks ~/ 10000);
|
||||
|
||||
factory UserData.fromMap(Map<String, dynamic> map) => UserDataMapper.fromMap(map);
|
||||
factory UserData.fromJson(String json) => UserDataMapper.fromJson(json);
|
||||
}
|
||||
|
||||
class UserDataJsonSerializer extends JsonConverter<UserData, String> {
|
||||
const UserDataJsonSerializer();
|
||||
|
||||
@override
|
||||
UserData fromJson(String json) {
|
||||
return UserData.fromJson(json);
|
||||
}
|
||||
|
||||
@override
|
||||
String toJson(UserData object) {
|
||||
return object.toJson();
|
||||
}
|
||||
}
|
||||
|
||||
enum EditorLockedFields {
|
||||
name("Name"),
|
||||
overView("Overview"),
|
||||
genres("Genres"),
|
||||
officialRating("OfficialRating"),
|
||||
cast("Cast"),
|
||||
productionLocations("ProductionLocations"),
|
||||
runTime("Runtime"),
|
||||
studios("Studios"),
|
||||
tags("Tags"),
|
||||
;
|
||||
|
||||
const EditorLockedFields(this.value);
|
||||
|
||||
static Map<EditorLockedFields, bool> enabled(List<String> fromStrings) => Map.fromEntries(
|
||||
EditorLockedFields.values.map(
|
||||
(e) => MapEntry(e, fromStrings.contains(e.value)),
|
||||
),
|
||||
);
|
||||
|
||||
final String value;
|
||||
}
|
||||
|
||||
enum DisplayOrder {
|
||||
empty(""),
|
||||
aired("aired"),
|
||||
originalAirDate("originalAirDate"),
|
||||
absolute("absolute"),
|
||||
dvd("dvd"),
|
||||
digital("digital"),
|
||||
storyArc("storyArc"),
|
||||
production("production"),
|
||||
tv("tv"),
|
||||
;
|
||||
|
||||
const DisplayOrder(this.value);
|
||||
|
||||
static DisplayOrder? fromMap(String? value) {
|
||||
return DisplayOrder.values.firstWhereOrNull((element) => element.value == value) ?? DisplayOrder.empty;
|
||||
}
|
||||
|
||||
final String value;
|
||||
}
|
||||
|
||||
enum ShowStatus {
|
||||
empty(""),
|
||||
ended("Ended"),
|
||||
continuing("Continuing");
|
||||
|
||||
const ShowStatus(this.value);
|
||||
|
||||
static ShowStatus? fromMap(String? value) {
|
||||
return ShowStatus.values.firstWhereOrNull((element) => element.value == value) ?? ShowStatus.empty;
|
||||
}
|
||||
|
||||
final String value;
|
||||
}
|
||||
|
||||
class ExternalUrls {
|
||||
final String name;
|
||||
final String url;
|
||||
ExternalUrls({
|
||||
required this.name,
|
||||
required this.url,
|
||||
});
|
||||
|
||||
static List<ExternalUrls> fromDto(List<dto.ExternalUrl> dto) {
|
||||
return dto.map((e) => ExternalUrls(name: e.name ?? "", url: e.url ?? "")).toList();
|
||||
}
|
||||
|
||||
Map<String, dynamic> toMap() {
|
||||
return {
|
||||
'Name': name,
|
||||
'Url': url,
|
||||
};
|
||||
}
|
||||
|
||||
factory ExternalUrls.fromMap(Map<String, dynamic> map) {
|
||||
return ExternalUrls(
|
||||
name: map['Name'] ?? '',
|
||||
url: map['Url'] ?? '',
|
||||
);
|
||||
}
|
||||
|
||||
String toJson() => json.encode(toMap());
|
||||
|
||||
factory ExternalUrls.fromJson(String source) => ExternalUrls.fromMap(json.decode(source));
|
||||
}
|
||||
|
||||
class GenreItems {
|
||||
final String id;
|
||||
final String name;
|
||||
GenreItems({
|
||||
required this.id,
|
||||
required this.name,
|
||||
});
|
||||
|
||||
@override
|
||||
String toString() => 'GenreItems(id: $id, name: $name)';
|
||||
}
|
||||
|
||||
class Person {
|
||||
final String id;
|
||||
final String name;
|
||||
final ImageData? image;
|
||||
final String role;
|
||||
final PersonKind? type;
|
||||
Person({
|
||||
required this.id,
|
||||
this.name = "",
|
||||
this.image,
|
||||
this.role = "",
|
||||
this.type,
|
||||
});
|
||||
|
||||
static Person fromBaseDto(dto.BaseItemDto item, Ref ref) {
|
||||
return Person(
|
||||
id: item.id ?? "",
|
||||
name: item.name ?? "",
|
||||
image: ImagesData.fromBaseItem(item, ref).primary,
|
||||
);
|
||||
}
|
||||
|
||||
static Person fromBasePerson(dto.BaseItemPerson person, Ref ref) {
|
||||
return Person(
|
||||
id: person.id ?? "",
|
||||
name: person.name ?? "",
|
||||
image: ImagesData.fromPersonDto(person, ref)?.primary,
|
||||
role: person.role ?? "",
|
||||
type: person.type);
|
||||
}
|
||||
|
||||
dto.BaseItemPerson toPerson() {
|
||||
return dto.BaseItemPerson(
|
||||
id: id,
|
||||
name: name,
|
||||
type: type,
|
||||
role: role,
|
||||
);
|
||||
}
|
||||
|
||||
static List<Person> peopleFromDto(List<dto.BaseItemPerson>? people, Ref ref) {
|
||||
return people
|
||||
?.mapIndexed(
|
||||
(index, person) => fromBasePerson(person, ref),
|
||||
)
|
||||
.toList() ??
|
||||
[];
|
||||
}
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'People(id: $id, name: $name, imageUrl: $image, role: $role, type: $type)';
|
||||
}
|
||||
}
|
||||
|
||||
class Studio {
|
||||
final String id;
|
||||
final String name;
|
||||
Studio({
|
||||
required this.id,
|
||||
required this.name,
|
||||
});
|
||||
|
||||
Studio copyWith({
|
||||
String? id,
|
||||
String? name,
|
||||
ValueGetter<String?>? image,
|
||||
}) {
|
||||
return Studio(
|
||||
id: id ?? this.id,
|
||||
name: name ?? this.name,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
String toString() => 'Studio(name: $name, id: $id)';
|
||||
|
||||
Map<String, dynamic> toMap() {
|
||||
return {
|
||||
'id': id,
|
||||
'name': name,
|
||||
};
|
||||
}
|
||||
|
||||
factory Studio.fromMap(Map<String, dynamic> map) {
|
||||
return Studio(
|
||||
id: map['id'] ?? map['Id'] ?? '',
|
||||
name: map['name'] ?? map['Name'] ?? '',
|
||||
);
|
||||
}
|
||||
|
||||
String toJson() => json.encode(toMap());
|
||||
|
||||
factory Studio.fromJson(String source) => Studio.fromMap(json.decode(source));
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
if (identical(this, other)) return true;
|
||||
|
||||
return other is Studio && other.id == id && other.name == name;
|
||||
}
|
||||
|
||||
@override
|
||||
int get hashCode => id.hashCode ^ name.hashCode;
|
||||
}
|
||||
|
||||
// class UserData {
|
||||
// final bool isFavourite;
|
||||
// final int playCount;
|
||||
// final int? unPlayedItemCount;
|
||||
// final int playbackPositionTicks;
|
||||
// final double progress;
|
||||
// final bool played;
|
||||
// UserData({
|
||||
// this.isFavourite = false,
|
||||
// this.playCount = 0,
|
||||
// this.unPlayedItemCount,
|
||||
// this.playbackPositionTicks = 0,
|
||||
// this.progress = 0,
|
||||
// this.played = false,
|
||||
// });
|
||||
|
||||
// factory UserData.fromDto(dto.UserItemDataDto? dto) {
|
||||
// if (dto == null) {
|
||||
// return UserData();
|
||||
// }
|
||||
// return UserData(
|
||||
// isFavourite: dto.isFavorite ?? false,
|
||||
// playCount: dto.playCount ?? 0,
|
||||
// playbackPositionTicks: dto.playbackPositionTicks ?? 0,
|
||||
// played: dto.played ?? false,
|
||||
// unPlayedItemCount: dto.unplayedItemCount ?? 0,
|
||||
// progress: dto.playedPercentage ?? 0,
|
||||
// );
|
||||
// }
|
||||
|
||||
// Duration get playBackPosition => Duration(milliseconds: playbackPositionTicks ~/ 10000);
|
||||
|
||||
// @override
|
||||
// String toString() {
|
||||
// return 'UserData(isFavourite: $isFavourite, playCount: $playCount, unPlayedItemCount: $unPlayedItemCount, playbackPositionTicks: $playbackPositionTicks, progress: $progress, played: $played)';
|
||||
// }
|
||||
|
||||
// UserData copyWith({
|
||||
// bool? isFavourite,
|
||||
// int? playCount,
|
||||
// int? unPlayedItemCount,
|
||||
// int? playbackPositionTicks,
|
||||
// double? progress,
|
||||
// bool? played,
|
||||
// }) {
|
||||
// return UserData(
|
||||
// isFavourite: isFavourite ?? this.isFavourite,
|
||||
// playCount: playCount ?? this.playCount,
|
||||
// unPlayedItemCount: unPlayedItemCount ?? this.unPlayedItemCount,
|
||||
// playbackPositionTicks: playbackPositionTicks ?? this.playbackPositionTicks,
|
||||
// progress: progress ?? this.progress,
|
||||
// played: played ?? this.played,
|
||||
// );
|
||||
// }
|
||||
|
||||
// Map<String, dynamic> toMap() {
|
||||
// return <String, dynamic>{
|
||||
// 'isFavourite': isFavourite,
|
||||
// 'playCount': playCount,
|
||||
// 'unPlayedItemCount': unPlayedItemCount,
|
||||
// 'playbackPositionTicks': playbackPositionTicks,
|
||||
// 'progress': progress,
|
||||
// 'played': played,
|
||||
// };
|
||||
// }
|
||||
|
||||
// factory UserData.fromMap(Map<String, dynamic> map) {
|
||||
// return UserData(
|
||||
// isFavourite: (map['isFavourite'] ?? false) as bool,
|
||||
// playCount: (map['playCount'] ?? 0) as int,
|
||||
// unPlayedItemCount: (map['unPlayedItemCount'] ?? 0) as int,
|
||||
// playbackPositionTicks: (map['playbackPositionTicks'] ?? 0) as int,
|
||||
// progress: (map['progress'] ?? 0.0) as double,
|
||||
// played: (map['played'] ?? false) as bool,
|
||||
// );
|
||||
// }
|
||||
|
||||
// String toJson() => json.encode(toMap());
|
||||
|
||||
// factory UserData.fromJson(String source) => UserData.fromMap(json.decode(source) as Map<String, dynamic>);
|
||||
|
||||
// @override
|
||||
// bool operator ==(covariant UserData other) {
|
||||
// if (identical(this, other)) return true;
|
||||
|
||||
// return other.isFavourite == isFavourite &&
|
||||
// other.playCount == playCount &&
|
||||
// other.unPlayedItemCount == unPlayedItemCount &&
|
||||
// other.playbackPositionTicks == playbackPositionTicks &&
|
||||
// other.progress == progress &&
|
||||
// other.played == played;
|
||||
// }
|
||||
|
||||
// @override
|
||||
// int get hashCode {
|
||||
// return isFavourite.hashCode ^
|
||||
// playCount.hashCode ^
|
||||
// unPlayedItemCount.hashCode ^
|
||||
// playbackPositionTicks.hashCode ^
|
||||
// progress.hashCode ^
|
||||
// played.hashCode;
|
||||
// }
|
||||
// }
|
||||
162
lib/models/items/item_shared_models.mapper.dart
Normal file
162
lib/models/items/item_shared_models.mapper.dart
Normal file
|
|
@ -0,0 +1,162 @@
|
|||
// coverage:ignore-file
|
||||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
// ignore_for_file: type=lint
|
||||
// ignore_for_file: unused_element, unnecessary_cast, override_on_non_overriding_member
|
||||
// ignore_for_file: strict_raw_type, inference_failure_on_untyped_parameter
|
||||
|
||||
part of 'item_shared_models.dart';
|
||||
|
||||
class UserDataMapper extends ClassMapperBase<UserData> {
|
||||
UserDataMapper._();
|
||||
|
||||
static UserDataMapper? _instance;
|
||||
static UserDataMapper ensureInitialized() {
|
||||
if (_instance == null) {
|
||||
MapperContainer.globals.use(_instance = UserDataMapper._());
|
||||
}
|
||||
return _instance!;
|
||||
}
|
||||
|
||||
@override
|
||||
final String id = 'UserData';
|
||||
|
||||
static bool _$isFavourite(UserData v) => v.isFavourite;
|
||||
static const Field<UserData, bool> _f$isFavourite =
|
||||
Field('isFavourite', _$isFavourite, opt: true, def: false);
|
||||
static int _$playCount(UserData v) => v.playCount;
|
||||
static const Field<UserData, int> _f$playCount =
|
||||
Field('playCount', _$playCount, opt: true, def: 0);
|
||||
static int? _$unPlayedItemCount(UserData v) => v.unPlayedItemCount;
|
||||
static const Field<UserData, int> _f$unPlayedItemCount =
|
||||
Field('unPlayedItemCount', _$unPlayedItemCount, opt: true);
|
||||
static int _$playbackPositionTicks(UserData v) => v.playbackPositionTicks;
|
||||
static const Field<UserData, int> _f$playbackPositionTicks = Field(
|
||||
'playbackPositionTicks', _$playbackPositionTicks,
|
||||
opt: true, def: 0);
|
||||
static double _$progress(UserData v) => v.progress;
|
||||
static const Field<UserData, double> _f$progress =
|
||||
Field('progress', _$progress, opt: true, def: 0);
|
||||
static DateTime? _$lastPlayed(UserData v) => v.lastPlayed;
|
||||
static const Field<UserData, DateTime> _f$lastPlayed =
|
||||
Field('lastPlayed', _$lastPlayed, opt: true);
|
||||
static bool _$played(UserData v) => v.played;
|
||||
static const Field<UserData, bool> _f$played =
|
||||
Field('played', _$played, opt: true, def: false);
|
||||
|
||||
@override
|
||||
final MappableFields<UserData> fields = const {
|
||||
#isFavourite: _f$isFavourite,
|
||||
#playCount: _f$playCount,
|
||||
#unPlayedItemCount: _f$unPlayedItemCount,
|
||||
#playbackPositionTicks: _f$playbackPositionTicks,
|
||||
#progress: _f$progress,
|
||||
#lastPlayed: _f$lastPlayed,
|
||||
#played: _f$played,
|
||||
};
|
||||
@override
|
||||
final bool ignoreNull = true;
|
||||
|
||||
static UserData _instantiate(DecodingData data) {
|
||||
return UserData(
|
||||
isFavourite: data.dec(_f$isFavourite),
|
||||
playCount: data.dec(_f$playCount),
|
||||
unPlayedItemCount: data.dec(_f$unPlayedItemCount),
|
||||
playbackPositionTicks: data.dec(_f$playbackPositionTicks),
|
||||
progress: data.dec(_f$progress),
|
||||
lastPlayed: data.dec(_f$lastPlayed),
|
||||
played: data.dec(_f$played));
|
||||
}
|
||||
|
||||
@override
|
||||
final Function instantiate = _instantiate;
|
||||
|
||||
static UserData fromMap(Map<String, dynamic> map) {
|
||||
return ensureInitialized().decodeMap<UserData>(map);
|
||||
}
|
||||
|
||||
static UserData fromJson(String json) {
|
||||
return ensureInitialized().decodeJson<UserData>(json);
|
||||
}
|
||||
}
|
||||
|
||||
mixin UserDataMappable {
|
||||
String toJson() {
|
||||
return UserDataMapper.ensureInitialized()
|
||||
.encodeJson<UserData>(this as UserData);
|
||||
}
|
||||
|
||||
Map<String, dynamic> toMap() {
|
||||
return UserDataMapper.ensureInitialized()
|
||||
.encodeMap<UserData>(this as UserData);
|
||||
}
|
||||
|
||||
UserDataCopyWith<UserData, UserData, UserData> get copyWith =>
|
||||
_UserDataCopyWithImpl(this as UserData, $identity, $identity);
|
||||
@override
|
||||
String toString() {
|
||||
return UserDataMapper.ensureInitialized().stringifyValue(this as UserData);
|
||||
}
|
||||
}
|
||||
|
||||
extension UserDataValueCopy<$R, $Out> on ObjectCopyWith<$R, UserData, $Out> {
|
||||
UserDataCopyWith<$R, UserData, $Out> get $asUserData =>
|
||||
$base.as((v, t, t2) => _UserDataCopyWithImpl(v, t, t2));
|
||||
}
|
||||
|
||||
abstract class UserDataCopyWith<$R, $In extends UserData, $Out>
|
||||
implements ClassCopyWith<$R, $In, $Out> {
|
||||
$R call(
|
||||
{bool? isFavourite,
|
||||
int? playCount,
|
||||
int? unPlayedItemCount,
|
||||
int? playbackPositionTicks,
|
||||
double? progress,
|
||||
DateTime? lastPlayed,
|
||||
bool? played});
|
||||
UserDataCopyWith<$R2, $In, $Out2> $chain<$R2, $Out2>(Then<$Out2, $R2> t);
|
||||
}
|
||||
|
||||
class _UserDataCopyWithImpl<$R, $Out>
|
||||
extends ClassCopyWithBase<$R, UserData, $Out>
|
||||
implements UserDataCopyWith<$R, UserData, $Out> {
|
||||
_UserDataCopyWithImpl(super.value, super.then, super.then2);
|
||||
|
||||
@override
|
||||
late final ClassMapperBase<UserData> $mapper =
|
||||
UserDataMapper.ensureInitialized();
|
||||
@override
|
||||
$R call(
|
||||
{bool? isFavourite,
|
||||
int? playCount,
|
||||
Object? unPlayedItemCount = $none,
|
||||
int? playbackPositionTicks,
|
||||
double? progress,
|
||||
Object? lastPlayed = $none,
|
||||
bool? played}) =>
|
||||
$apply(FieldCopyWithData({
|
||||
if (isFavourite != null) #isFavourite: isFavourite,
|
||||
if (playCount != null) #playCount: playCount,
|
||||
if (unPlayedItemCount != $none) #unPlayedItemCount: unPlayedItemCount,
|
||||
if (playbackPositionTicks != null)
|
||||
#playbackPositionTicks: playbackPositionTicks,
|
||||
if (progress != null) #progress: progress,
|
||||
if (lastPlayed != $none) #lastPlayed: lastPlayed,
|
||||
if (played != null) #played: played
|
||||
}));
|
||||
@override
|
||||
UserData $make(CopyWithData data) => UserData(
|
||||
isFavourite: data.get(#isFavourite, or: $value.isFavourite),
|
||||
playCount: data.get(#playCount, or: $value.playCount),
|
||||
unPlayedItemCount:
|
||||
data.get(#unPlayedItemCount, or: $value.unPlayedItemCount),
|
||||
playbackPositionTicks:
|
||||
data.get(#playbackPositionTicks, or: $value.playbackPositionTicks),
|
||||
progress: data.get(#progress, or: $value.progress),
|
||||
lastPlayed: data.get(#lastPlayed, or: $value.lastPlayed),
|
||||
played: data.get(#played, or: $value.played));
|
||||
|
||||
@override
|
||||
UserDataCopyWith<$R2, UserData, $Out2> $chain<$R2, $Out2>(
|
||||
Then<$Out2, $R2> t) =>
|
||||
_UserDataCopyWithImpl($value, $cast, t);
|
||||
}
|
||||
67
lib/models/items/item_stream_model.dart
Normal file
67
lib/models/items/item_stream_model.dart
Normal file
|
|
@ -0,0 +1,67 @@
|
|||
import 'package:fladder/jellyfin/jellyfin_open_api.swagger.dart';
|
||||
import 'package:fladder/jellyfin/jellyfin_open_api.swagger.dart' as dto;
|
||||
import 'package:fladder/models/item_base_model.dart';
|
||||
import 'package:fladder/models/items/episode_model.dart';
|
||||
import 'package:fladder/models/items/images_models.dart';
|
||||
import 'package:fladder/models/items/item_shared_models.dart';
|
||||
import 'package:fladder/models/items/media_streams_model.dart';
|
||||
import 'package:fladder/models/items/movie_model.dart';
|
||||
import 'package:fladder/models/items/overview_model.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
|
||||
import 'package:dart_mappable/dart_mappable.dart';
|
||||
|
||||
part 'item_stream_model.mapper.dart';
|
||||
|
||||
@MappableClass()
|
||||
class ItemStreamModel extends ItemBaseModel with ItemStreamModelMappable {
|
||||
final ImagesData? parentImages;
|
||||
final MediaStreamsModel mediaStreams;
|
||||
const ItemStreamModel({
|
||||
required this.parentImages,
|
||||
required this.mediaStreams,
|
||||
required super.name,
|
||||
required super.id,
|
||||
required super.overview,
|
||||
required super.parentId,
|
||||
required super.playlistId,
|
||||
required super.images,
|
||||
required super.childCount,
|
||||
required super.primaryRatio,
|
||||
required super.userData,
|
||||
required super.canDelete,
|
||||
required super.canDownload,
|
||||
super.jellyType,
|
||||
});
|
||||
factory ItemStreamModel.fromBaseDto(dto.BaseItemDto item, Ref ref) {
|
||||
return switch (item.type) {
|
||||
BaseItemKind.episode => EpisodeModel.fromBaseDto(item, ref),
|
||||
BaseItemKind.movie => MovieModel.fromBaseDto(item, ref),
|
||||
_ => ItemStreamModel._fromBaseDto(item, ref)
|
||||
};
|
||||
}
|
||||
|
||||
factory ItemStreamModel._fromBaseDto(dto.BaseItemDto item, Ref ref) {
|
||||
return ItemStreamModel(
|
||||
name: item.name ?? "",
|
||||
id: item.id ?? "",
|
||||
childCount: item.childCount,
|
||||
overview: OverviewModel.fromBaseItemDto(item, ref),
|
||||
userData: UserData.fromDto(item.userData),
|
||||
parentId: item.parentId,
|
||||
playlistId: item.playlistItemId,
|
||||
images: ImagesData.fromBaseItem(item, ref),
|
||||
primaryRatio: item.primaryImageAspectRatio,
|
||||
parentImages: ImagesData.fromBaseItemParent(item, ref),
|
||||
canDelete: item.canDelete,
|
||||
canDownload: item.canDownload,
|
||||
mediaStreams:
|
||||
MediaStreamsModel.fromMediaStreamsList(item.mediaSources?.firstOrNull, item.mediaStreams ?? [], ref),
|
||||
);
|
||||
}
|
||||
|
||||
String? get videoPropertiesLabel {
|
||||
if (mediaStreams.displayProfile == null && mediaStreams.resolution == null) return null;
|
||||
return "${mediaStreams.displayProfile?.value}";
|
||||
}
|
||||
}
|
||||
245
lib/models/items/item_stream_model.mapper.dart
Normal file
245
lib/models/items/item_stream_model.mapper.dart
Normal file
|
|
@ -0,0 +1,245 @@
|
|||
// coverage:ignore-file
|
||||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
// ignore_for_file: type=lint
|
||||
// ignore_for_file: unused_element, unnecessary_cast, override_on_non_overriding_member
|
||||
// ignore_for_file: strict_raw_type, inference_failure_on_untyped_parameter
|
||||
|
||||
part of 'item_stream_model.dart';
|
||||
|
||||
class ItemStreamModelMapper extends SubClassMapperBase<ItemStreamModel> {
|
||||
ItemStreamModelMapper._();
|
||||
|
||||
static ItemStreamModelMapper? _instance;
|
||||
static ItemStreamModelMapper ensureInitialized() {
|
||||
if (_instance == null) {
|
||||
MapperContainer.globals.use(_instance = ItemStreamModelMapper._());
|
||||
ItemBaseModelMapper.ensureInitialized().addSubMapper(_instance!);
|
||||
OverviewModelMapper.ensureInitialized();
|
||||
UserDataMapper.ensureInitialized();
|
||||
}
|
||||
return _instance!;
|
||||
}
|
||||
|
||||
@override
|
||||
final String id = 'ItemStreamModel';
|
||||
|
||||
static ImagesData? _$parentImages(ItemStreamModel v) => v.parentImages;
|
||||
static const Field<ItemStreamModel, ImagesData> _f$parentImages =
|
||||
Field('parentImages', _$parentImages);
|
||||
static MediaStreamsModel _$mediaStreams(ItemStreamModel v) => v.mediaStreams;
|
||||
static const Field<ItemStreamModel, MediaStreamsModel> _f$mediaStreams =
|
||||
Field('mediaStreams', _$mediaStreams);
|
||||
static String _$name(ItemStreamModel v) => v.name;
|
||||
static const Field<ItemStreamModel, String> _f$name = Field('name', _$name);
|
||||
static String _$id(ItemStreamModel v) => v.id;
|
||||
static const Field<ItemStreamModel, String> _f$id = Field('id', _$id);
|
||||
static OverviewModel _$overview(ItemStreamModel v) => v.overview;
|
||||
static const Field<ItemStreamModel, OverviewModel> _f$overview =
|
||||
Field('overview', _$overview);
|
||||
static String? _$parentId(ItemStreamModel v) => v.parentId;
|
||||
static const Field<ItemStreamModel, String> _f$parentId =
|
||||
Field('parentId', _$parentId);
|
||||
static String? _$playlistId(ItemStreamModel v) => v.playlistId;
|
||||
static const Field<ItemStreamModel, String> _f$playlistId =
|
||||
Field('playlistId', _$playlistId);
|
||||
static ImagesData? _$images(ItemStreamModel v) => v.images;
|
||||
static const Field<ItemStreamModel, ImagesData> _f$images =
|
||||
Field('images', _$images);
|
||||
static int? _$childCount(ItemStreamModel v) => v.childCount;
|
||||
static const Field<ItemStreamModel, int> _f$childCount =
|
||||
Field('childCount', _$childCount);
|
||||
static double? _$primaryRatio(ItemStreamModel v) => v.primaryRatio;
|
||||
static const Field<ItemStreamModel, double> _f$primaryRatio =
|
||||
Field('primaryRatio', _$primaryRatio);
|
||||
static UserData _$userData(ItemStreamModel v) => v.userData;
|
||||
static const Field<ItemStreamModel, UserData> _f$userData =
|
||||
Field('userData', _$userData);
|
||||
static bool? _$canDelete(ItemStreamModel v) => v.canDelete;
|
||||
static const Field<ItemStreamModel, bool> _f$canDelete =
|
||||
Field('canDelete', _$canDelete);
|
||||
static bool? _$canDownload(ItemStreamModel v) => v.canDownload;
|
||||
static const Field<ItemStreamModel, bool> _f$canDownload =
|
||||
Field('canDownload', _$canDownload);
|
||||
static dto.BaseItemKind? _$jellyType(ItemStreamModel v) => v.jellyType;
|
||||
static const Field<ItemStreamModel, dto.BaseItemKind> _f$jellyType =
|
||||
Field('jellyType', _$jellyType, opt: true);
|
||||
|
||||
@override
|
||||
final MappableFields<ItemStreamModel> fields = const {
|
||||
#parentImages: _f$parentImages,
|
||||
#mediaStreams: _f$mediaStreams,
|
||||
#name: _f$name,
|
||||
#id: _f$id,
|
||||
#overview: _f$overview,
|
||||
#parentId: _f$parentId,
|
||||
#playlistId: _f$playlistId,
|
||||
#images: _f$images,
|
||||
#childCount: _f$childCount,
|
||||
#primaryRatio: _f$primaryRatio,
|
||||
#userData: _f$userData,
|
||||
#canDelete: _f$canDelete,
|
||||
#canDownload: _f$canDownload,
|
||||
#jellyType: _f$jellyType,
|
||||
};
|
||||
@override
|
||||
final bool ignoreNull = true;
|
||||
|
||||
@override
|
||||
final String discriminatorKey = 'type';
|
||||
@override
|
||||
final dynamic discriminatorValue = 'ItemStreamModel';
|
||||
@override
|
||||
late final ClassMapperBase superMapper =
|
||||
ItemBaseModelMapper.ensureInitialized();
|
||||
|
||||
static ItemStreamModel _instantiate(DecodingData data) {
|
||||
return ItemStreamModel(
|
||||
parentImages: data.dec(_f$parentImages),
|
||||
mediaStreams: data.dec(_f$mediaStreams),
|
||||
name: data.dec(_f$name),
|
||||
id: data.dec(_f$id),
|
||||
overview: data.dec(_f$overview),
|
||||
parentId: data.dec(_f$parentId),
|
||||
playlistId: data.dec(_f$playlistId),
|
||||
images: data.dec(_f$images),
|
||||
childCount: data.dec(_f$childCount),
|
||||
primaryRatio: data.dec(_f$primaryRatio),
|
||||
userData: data.dec(_f$userData),
|
||||
canDelete: data.dec(_f$canDelete),
|
||||
canDownload: data.dec(_f$canDownload),
|
||||
jellyType: data.dec(_f$jellyType));
|
||||
}
|
||||
|
||||
@override
|
||||
final Function instantiate = _instantiate;
|
||||
|
||||
static ItemStreamModel fromMap(Map<String, dynamic> map) {
|
||||
return ensureInitialized().decodeMap<ItemStreamModel>(map);
|
||||
}
|
||||
|
||||
static ItemStreamModel fromJson(String json) {
|
||||
return ensureInitialized().decodeJson<ItemStreamModel>(json);
|
||||
}
|
||||
}
|
||||
|
||||
mixin ItemStreamModelMappable {
|
||||
String toJson() {
|
||||
return ItemStreamModelMapper.ensureInitialized()
|
||||
.encodeJson<ItemStreamModel>(this as ItemStreamModel);
|
||||
}
|
||||
|
||||
Map<String, dynamic> toMap() {
|
||||
return ItemStreamModelMapper.ensureInitialized()
|
||||
.encodeMap<ItemStreamModel>(this as ItemStreamModel);
|
||||
}
|
||||
|
||||
ItemStreamModelCopyWith<ItemStreamModel, ItemStreamModel, ItemStreamModel>
|
||||
get copyWith => _ItemStreamModelCopyWithImpl(
|
||||
this as ItemStreamModel, $identity, $identity);
|
||||
@override
|
||||
String toString() {
|
||||
return ItemStreamModelMapper.ensureInitialized()
|
||||
.stringifyValue(this as ItemStreamModel);
|
||||
}
|
||||
}
|
||||
|
||||
extension ItemStreamModelValueCopy<$R, $Out>
|
||||
on ObjectCopyWith<$R, ItemStreamModel, $Out> {
|
||||
ItemStreamModelCopyWith<$R, ItemStreamModel, $Out> get $asItemStreamModel =>
|
||||
$base.as((v, t, t2) => _ItemStreamModelCopyWithImpl(v, t, t2));
|
||||
}
|
||||
|
||||
abstract class ItemStreamModelCopyWith<$R, $In extends ItemStreamModel, $Out>
|
||||
implements ItemBaseModelCopyWith<$R, $In, $Out> {
|
||||
@override
|
||||
OverviewModelCopyWith<$R, OverviewModel, OverviewModel> get overview;
|
||||
@override
|
||||
UserDataCopyWith<$R, UserData, UserData> get userData;
|
||||
@override
|
||||
$R call(
|
||||
{ImagesData? parentImages,
|
||||
MediaStreamsModel? mediaStreams,
|
||||
String? name,
|
||||
String? id,
|
||||
OverviewModel? overview,
|
||||
String? parentId,
|
||||
String? playlistId,
|
||||
ImagesData? images,
|
||||
int? childCount,
|
||||
double? primaryRatio,
|
||||
UserData? userData,
|
||||
bool? canDelete,
|
||||
bool? canDownload,
|
||||
dto.BaseItemKind? jellyType});
|
||||
ItemStreamModelCopyWith<$R2, $In, $Out2> $chain<$R2, $Out2>(
|
||||
Then<$Out2, $R2> t);
|
||||
}
|
||||
|
||||
class _ItemStreamModelCopyWithImpl<$R, $Out>
|
||||
extends ClassCopyWithBase<$R, ItemStreamModel, $Out>
|
||||
implements ItemStreamModelCopyWith<$R, ItemStreamModel, $Out> {
|
||||
_ItemStreamModelCopyWithImpl(super.value, super.then, super.then2);
|
||||
|
||||
@override
|
||||
late final ClassMapperBase<ItemStreamModel> $mapper =
|
||||
ItemStreamModelMapper.ensureInitialized();
|
||||
@override
|
||||
OverviewModelCopyWith<$R, OverviewModel, OverviewModel> get overview =>
|
||||
$value.overview.copyWith.$chain((v) => call(overview: v));
|
||||
@override
|
||||
UserDataCopyWith<$R, UserData, UserData> get userData =>
|
||||
$value.userData.copyWith.$chain((v) => call(userData: v));
|
||||
@override
|
||||
$R call(
|
||||
{Object? parentImages = $none,
|
||||
MediaStreamsModel? mediaStreams,
|
||||
String? name,
|
||||
String? id,
|
||||
OverviewModel? overview,
|
||||
Object? parentId = $none,
|
||||
Object? playlistId = $none,
|
||||
Object? images = $none,
|
||||
Object? childCount = $none,
|
||||
Object? primaryRatio = $none,
|
||||
UserData? userData,
|
||||
Object? canDelete = $none,
|
||||
Object? canDownload = $none,
|
||||
Object? jellyType = $none}) =>
|
||||
$apply(FieldCopyWithData({
|
||||
if (parentImages != $none) #parentImages: parentImages,
|
||||
if (mediaStreams != null) #mediaStreams: mediaStreams,
|
||||
if (name != null) #name: name,
|
||||
if (id != null) #id: id,
|
||||
if (overview != null) #overview: overview,
|
||||
if (parentId != $none) #parentId: parentId,
|
||||
if (playlistId != $none) #playlistId: playlistId,
|
||||
if (images != $none) #images: images,
|
||||
if (childCount != $none) #childCount: childCount,
|
||||
if (primaryRatio != $none) #primaryRatio: primaryRatio,
|
||||
if (userData != null) #userData: userData,
|
||||
if (canDelete != $none) #canDelete: canDelete,
|
||||
if (canDownload != $none) #canDownload: canDownload,
|
||||
if (jellyType != $none) #jellyType: jellyType
|
||||
}));
|
||||
@override
|
||||
ItemStreamModel $make(CopyWithData data) => ItemStreamModel(
|
||||
parentImages: data.get(#parentImages, or: $value.parentImages),
|
||||
mediaStreams: data.get(#mediaStreams, or: $value.mediaStreams),
|
||||
name: data.get(#name, or: $value.name),
|
||||
id: data.get(#id, or: $value.id),
|
||||
overview: data.get(#overview, or: $value.overview),
|
||||
parentId: data.get(#parentId, or: $value.parentId),
|
||||
playlistId: data.get(#playlistId, or: $value.playlistId),
|
||||
images: data.get(#images, or: $value.images),
|
||||
childCount: data.get(#childCount, or: $value.childCount),
|
||||
primaryRatio: data.get(#primaryRatio, or: $value.primaryRatio),
|
||||
userData: data.get(#userData, or: $value.userData),
|
||||
canDelete: data.get(#canDelete, or: $value.canDelete),
|
||||
canDownload: data.get(#canDownload, or: $value.canDownload),
|
||||
jellyType: data.get(#jellyType, or: $value.jellyType));
|
||||
|
||||
@override
|
||||
ItemStreamModelCopyWith<$R2, ItemStreamModel, $Out2> $chain<$R2, $Out2>(
|
||||
Then<$Out2, $R2> t) =>
|
||||
_ItemStreamModelCopyWithImpl($value, $cast, t);
|
||||
}
|
||||
372
lib/models/items/media_streams_model.dart
Normal file
372
lib/models/items/media_streams_model.dart
Normal file
|
|
@ -0,0 +1,372 @@
|
|||
import 'dart:convert';
|
||||
|
||||
import 'package:fladder/jellyfin/jellyfin_open_api.enums.swagger.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
// ignore_for_file: public_member_api_docs, sort_constructors_first
|
||||
import 'package:collection/collection.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
|
||||
import 'package:fladder/jellyfin/jellyfin_open_api.swagger.dart' as dto;
|
||||
import 'package:fladder/providers/user_provider.dart';
|
||||
import 'package:fladder/util/video_properties.dart';
|
||||
|
||||
class MediaStreamsModel {
|
||||
final int? defaultAudioStreamIndex;
|
||||
final int? defaultSubStreamIndex;
|
||||
final List<VideoStreamModel> videoStreams;
|
||||
final List<AudioStreamModel> audioStreams;
|
||||
final List<SubStreamModel> subStreams;
|
||||
MediaStreamsModel({
|
||||
this.defaultAudioStreamIndex,
|
||||
this.defaultSubStreamIndex,
|
||||
required this.videoStreams,
|
||||
required this.audioStreams,
|
||||
required this.subStreams,
|
||||
});
|
||||
|
||||
bool get isNull {
|
||||
return defaultAudioStreamIndex == null ||
|
||||
defaultSubStreamIndex == null ||
|
||||
audioStreams.isEmpty ||
|
||||
subStreams.isEmpty;
|
||||
}
|
||||
|
||||
bool get isNotEmpty {
|
||||
return audioStreams.isNotEmpty && subStreams.isNotEmpty;
|
||||
}
|
||||
|
||||
AudioStreamModel? get currentAudioStream {
|
||||
if (defaultAudioStreamIndex == -1) {
|
||||
return AudioStreamModel.no();
|
||||
}
|
||||
return audioStreams.firstWhereOrNull((element) => element.index == defaultAudioStreamIndex) ??
|
||||
audioStreams.firstOrNull;
|
||||
}
|
||||
|
||||
SubStreamModel? get currentSubStream {
|
||||
if (defaultSubStreamIndex == -1) {
|
||||
return SubStreamModel.no();
|
||||
}
|
||||
return subStreams.firstWhereOrNull((element) => element.index == defaultSubStreamIndex) ?? subStreams.firstOrNull;
|
||||
}
|
||||
|
||||
DisplayProfile? get displayProfile {
|
||||
return DisplayProfile.fromVideoStreams(videoStreams);
|
||||
}
|
||||
|
||||
Resolution? get resolution {
|
||||
return Resolution.fromVideoStream(videoStreams.firstOrNull);
|
||||
}
|
||||
|
||||
String? get resolutionText {
|
||||
final stream = videoStreams.firstOrNull;
|
||||
if (stream == null) return null;
|
||||
return "${stream.width}x${stream.height}";
|
||||
}
|
||||
|
||||
Widget? audioIcon(
|
||||
BuildContext context,
|
||||
Function()? onTap,
|
||||
) {
|
||||
final audioStream = audioStreams.firstWhereOrNull((element) => element.isDefault) ?? audioStreams.firstOrNull;
|
||||
if (audioStream == null) return null;
|
||||
return DefaultVideoInformationBox(
|
||||
onTap: onTap,
|
||||
child: Text(
|
||||
audioStream.title,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget subtitleIcon(
|
||||
BuildContext context,
|
||||
Function()? onTap,
|
||||
) {
|
||||
return DefaultVideoInformationBox(
|
||||
onTap: onTap,
|
||||
child: Icon(
|
||||
subStreams.isNotEmpty ? Icons.subtitles_rounded : Icons.subtitles_off_outlined,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
static MediaStreamsModel fromMediaStreamsList(
|
||||
dto.MediaSourceInfo? mediaSource, List<dto.MediaStream> streams, Ref ref) {
|
||||
return MediaStreamsModel(
|
||||
defaultAudioStreamIndex: mediaSource?.defaultAudioStreamIndex,
|
||||
defaultSubStreamIndex: mediaSource?.defaultSubtitleStreamIndex,
|
||||
videoStreams: streams
|
||||
.where((element) => element.type == dto.MediaStreamType.video)
|
||||
.map(
|
||||
(e) => VideoStreamModel.fromMediaStream(e),
|
||||
)
|
||||
.sortByExternal(),
|
||||
audioStreams: streams
|
||||
.where((element) => element.type == dto.MediaStreamType.audio)
|
||||
.map(
|
||||
(e) => AudioStreamModel.fromMediaStream(e),
|
||||
)
|
||||
.sortByExternal(),
|
||||
subStreams: streams
|
||||
.where((element) => element.type == dto.MediaStreamType.subtitle)
|
||||
.map(
|
||||
(sub) => SubStreamModel.fromMediaStream(sub, ref),
|
||||
)
|
||||
.sortByExternal(),
|
||||
);
|
||||
}
|
||||
|
||||
MediaStreamsModel copyWith({
|
||||
int? defaultAudioStreamIndex,
|
||||
int? defaultSubStreamIndex,
|
||||
List<VideoStreamModel>? videoStreams,
|
||||
List<AudioStreamModel>? audioStreams,
|
||||
List<SubStreamModel>? subStreams,
|
||||
}) {
|
||||
return MediaStreamsModel(
|
||||
defaultAudioStreamIndex: defaultAudioStreamIndex ?? this.defaultAudioStreamIndex,
|
||||
defaultSubStreamIndex: defaultSubStreamIndex ?? this.defaultSubStreamIndex,
|
||||
videoStreams: videoStreams ?? this.videoStreams,
|
||||
audioStreams: audioStreams ?? this.audioStreams,
|
||||
subStreams: subStreams ?? this.subStreams,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'MediaStreamsModel(defaultAudioStreamIndex: $defaultAudioStreamIndex, defaultSubStreamIndex: $defaultSubStreamIndex, videoStreams: $videoStreams, audioStreams: $audioStreams, subStreams: $subStreams)';
|
||||
}
|
||||
}
|
||||
|
||||
class StreamModel {
|
||||
final String name;
|
||||
final String codec;
|
||||
final bool isDefault;
|
||||
final bool isExternal;
|
||||
final int index;
|
||||
StreamModel({
|
||||
required this.name,
|
||||
required this.codec,
|
||||
required this.isDefault,
|
||||
required this.isExternal,
|
||||
required this.index,
|
||||
});
|
||||
}
|
||||
|
||||
class VideoStreamModel extends StreamModel {
|
||||
final int width;
|
||||
final int height;
|
||||
final double frameRate;
|
||||
final String? videoDoViTitle;
|
||||
final VideoRangeType? videoRangeType;
|
||||
VideoStreamModel({
|
||||
required super.name,
|
||||
required super.codec,
|
||||
required super.isDefault,
|
||||
required super.isExternal,
|
||||
required super.index,
|
||||
required this.videoDoViTitle,
|
||||
required this.videoRangeType,
|
||||
required this.width,
|
||||
required this.height,
|
||||
required this.frameRate,
|
||||
});
|
||||
|
||||
factory VideoStreamModel.fromMediaStream(dto.MediaStream stream) {
|
||||
return VideoStreamModel(
|
||||
name: stream.title ?? "",
|
||||
isDefault: stream.isDefault ?? false,
|
||||
codec: stream.codec ?? "",
|
||||
videoDoViTitle: stream.videoDoViTitle,
|
||||
videoRangeType: stream.videoRangeType,
|
||||
width: stream.width ?? 0,
|
||||
height: stream.height ?? 0,
|
||||
frameRate: stream.realFrameRate ?? 24,
|
||||
isExternal: stream.isExternal ?? false,
|
||||
index: stream.index ?? -1,
|
||||
);
|
||||
}
|
||||
String get prettyName {
|
||||
return "${Resolution.fromVideoStream(this)?.value} - ${DisplayProfile.fromVideoStream(this).value} - (${codec.toUpperCase()})";
|
||||
}
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'VideoStreamModel(width: $width, height: $height, frameRate: $frameRate, videoDoViTitle: $videoDoViTitle, videoRangeType: $videoRangeType)';
|
||||
}
|
||||
}
|
||||
|
||||
//Instead of using sortBy(a.isExternal etc..) this one seems to be more consistent for some reason
|
||||
extension SortByExternalExtension<T extends StreamModel> on Iterable<T> {
|
||||
List<T> sortByExternal() {
|
||||
return [...where((element) => !element.isExternal), ...where((element) => element.isExternal)];
|
||||
}
|
||||
}
|
||||
|
||||
class AudioStreamModel extends StreamModel {
|
||||
final String displayTitle;
|
||||
final String language;
|
||||
final String channelLayout;
|
||||
|
||||
AudioStreamModel({
|
||||
required this.displayTitle,
|
||||
required super.name,
|
||||
required super.codec,
|
||||
required super.isDefault,
|
||||
required super.isExternal,
|
||||
required super.index,
|
||||
required this.language,
|
||||
required this.channelLayout,
|
||||
});
|
||||
|
||||
factory AudioStreamModel.fromMediaStream(dto.MediaStream stream) {
|
||||
return AudioStreamModel(
|
||||
displayTitle: stream.displayTitle ?? "",
|
||||
name: stream.title ?? "",
|
||||
isDefault: stream.isDefault ?? false,
|
||||
codec: stream.codec ?? "",
|
||||
language: stream.language ?? "Unknown",
|
||||
channelLayout: stream.channelLayout ?? "",
|
||||
isExternal: stream.isExternal ?? false,
|
||||
index: stream.index ?? -1,
|
||||
);
|
||||
}
|
||||
|
||||
String get title =>
|
||||
[name, language, codec, channelLayout].whereNotNull().where((element) => element.isNotEmpty).join(' - ');
|
||||
|
||||
AudioStreamModel.no({
|
||||
super.name = 'Off',
|
||||
this.displayTitle = 'Off',
|
||||
this.language = '',
|
||||
super.codec = '',
|
||||
this.channelLayout = '',
|
||||
super.isDefault = false,
|
||||
super.isExternal = false,
|
||||
super.index = -1,
|
||||
});
|
||||
}
|
||||
|
||||
class SubStreamModel extends StreamModel {
|
||||
String id;
|
||||
String title;
|
||||
String displayTitle;
|
||||
String language;
|
||||
String? url;
|
||||
bool supportsExternalStream;
|
||||
SubStreamModel({
|
||||
required super.name,
|
||||
required this.id,
|
||||
required this.title,
|
||||
required this.displayTitle,
|
||||
required this.language,
|
||||
this.url,
|
||||
required super.codec,
|
||||
required super.isDefault,
|
||||
required super.isExternal,
|
||||
required super.index,
|
||||
this.supportsExternalStream = false,
|
||||
});
|
||||
|
||||
SubStreamModel.no({
|
||||
super.name = 'Off',
|
||||
this.id = 'Off',
|
||||
this.title = 'Off',
|
||||
this.displayTitle = 'Off',
|
||||
this.language = '',
|
||||
this.url = '',
|
||||
super.codec = '',
|
||||
super.isDefault = false,
|
||||
super.isExternal = false,
|
||||
super.index = -1,
|
||||
this.supportsExternalStream = false,
|
||||
});
|
||||
|
||||
factory SubStreamModel.fromMediaStream(dto.MediaStream stream, Ref ref) {
|
||||
return SubStreamModel(
|
||||
name: stream.title ?? "",
|
||||
title: stream.title ?? "",
|
||||
displayTitle: stream.displayTitle ?? "",
|
||||
language: stream.language ?? "Unknown",
|
||||
isDefault: stream.isDefault ?? false,
|
||||
codec: stream.codec ?? "",
|
||||
id: stream.hashCode.toString(),
|
||||
supportsExternalStream: stream.supportsExternalStream ?? false,
|
||||
url: stream.deliveryUrl != null
|
||||
? "${ref.read(userProvider)?.server ?? ""}${stream.deliveryUrl}}".replaceAll(".vtt", ".srt")
|
||||
: null,
|
||||
isExternal: stream.isExternal ?? false,
|
||||
index: stream.index ?? -1,
|
||||
);
|
||||
}
|
||||
|
||||
SubStreamModel copyWith({
|
||||
String? name,
|
||||
String? id,
|
||||
String? title,
|
||||
String? displayTitle,
|
||||
String? language,
|
||||
ValueGetter<String?>? url,
|
||||
String? codec,
|
||||
bool? isDefault,
|
||||
bool? isExternal,
|
||||
int? index,
|
||||
bool? supportsExternalStream,
|
||||
}) {
|
||||
return SubStreamModel(
|
||||
name: name ?? this.name,
|
||||
id: id ?? this.id,
|
||||
title: title ?? this.title,
|
||||
displayTitle: displayTitle ?? this.displayTitle,
|
||||
language: language ?? this.language,
|
||||
url: url != null ? url() : this.url,
|
||||
supportsExternalStream: supportsExternalStream ?? this.supportsExternalStream,
|
||||
codec: codec ?? this.codec,
|
||||
isDefault: isDefault ?? this.isDefault,
|
||||
isExternal: isExternal ?? this.isExternal,
|
||||
index: index ?? this.index,
|
||||
);
|
||||
}
|
||||
|
||||
Map<String, dynamic> toMap() {
|
||||
return {
|
||||
'name': name,
|
||||
'id': id,
|
||||
'title': title,
|
||||
'displayTitle': displayTitle,
|
||||
'language': language,
|
||||
'url': url,
|
||||
'supportsExternalStream': supportsExternalStream,
|
||||
'codec': codec,
|
||||
'isExternal': isExternal,
|
||||
'isDefault': isDefault,
|
||||
'index': index,
|
||||
};
|
||||
}
|
||||
|
||||
factory SubStreamModel.fromMap(Map<String, dynamic> map) {
|
||||
return SubStreamModel(
|
||||
name: map['name'] ?? '',
|
||||
id: map['id'] ?? '',
|
||||
title: map['title'] ?? '',
|
||||
displayTitle: map['displayTitle'] ?? '',
|
||||
language: map['language'] ?? '',
|
||||
url: map['url'],
|
||||
supportsExternalStream: map['supportsExternalStream'] ?? false,
|
||||
codec: map['codec'] ?? '',
|
||||
isDefault: map['isDefault'] ?? false,
|
||||
isExternal: map['isExternal'] ?? false,
|
||||
index: map['index'] ?? -1,
|
||||
);
|
||||
}
|
||||
|
||||
String toJson() => json.encode(toMap());
|
||||
|
||||
factory SubStreamModel.fromJson(String source) => SubStreamModel.fromMap(json.decode(source));
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'SubFile(title: $title, displayTitle: $displayTitle, language: $language, url: $url, isExternal: $isExternal)';
|
||||
}
|
||||
}
|
||||
107
lib/models/items/movie_model.dart
Normal file
107
lib/models/items/movie_model.dart
Normal file
|
|
@ -0,0 +1,107 @@
|
|||
// ignore_for_file: public_member_api_docs, sort_constructors_first
|
||||
|
||||
import 'package:fladder/util/humanize_duration.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
|
||||
import 'package:fladder/jellyfin/jellyfin_open_api.swagger.dart' as dto;
|
||||
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/item_shared_models.dart';
|
||||
import 'package:fladder/models/items/item_stream_model.dart';
|
||||
import 'package:fladder/models/items/media_streams_model.dart';
|
||||
import 'package:fladder/models/items/overview_model.dart';
|
||||
import 'package:fladder/screens/details_screens/movie_detail_screen.dart';
|
||||
|
||||
import 'package:dart_mappable/dart_mappable.dart';
|
||||
|
||||
part 'movie_model.mapper.dart';
|
||||
|
||||
@MappableClass()
|
||||
class MovieModel extends ItemStreamModel with MovieModelMappable {
|
||||
final String originalTitle;
|
||||
final String? path;
|
||||
final DateTime premiereDate;
|
||||
final String sortName;
|
||||
final String status;
|
||||
final List<ItemBaseModel> related;
|
||||
final List<Chapter> chapters;
|
||||
const MovieModel({
|
||||
required this.originalTitle,
|
||||
this.path,
|
||||
this.chapters = const [],
|
||||
required this.premiereDate,
|
||||
required this.sortName,
|
||||
required this.status,
|
||||
this.related = const [],
|
||||
required super.name,
|
||||
required super.id,
|
||||
required super.overview,
|
||||
required super.parentId,
|
||||
required super.playlistId,
|
||||
required super.images,
|
||||
required super.childCount,
|
||||
required super.primaryRatio,
|
||||
required super.userData,
|
||||
required super.parentImages,
|
||||
required super.mediaStreams,
|
||||
required super.canDownload,
|
||||
required super.canDelete,
|
||||
super.jellyType,
|
||||
});
|
||||
@override
|
||||
String? detailedName(BuildContext context) => "$name${overview.yearAired != null ? " (${overview.yearAired})" : ""}";
|
||||
|
||||
@override
|
||||
Widget get detailScreenWidget => MovieDetailScreen(item: this);
|
||||
|
||||
@override
|
||||
ItemBaseModel get parentBaseModel => this;
|
||||
|
||||
@override
|
||||
String? get subText => overview.yearAired?.toString() ?? overview.runTime.humanize;
|
||||
|
||||
@override
|
||||
bool get playAble => true;
|
||||
|
||||
@override
|
||||
bool get identifiable => true;
|
||||
|
||||
@override
|
||||
String? label(BuildContext context) =>
|
||||
overview.yearAired == null ? overview.runTime.humanize : "$name (${overview.yearAired})";
|
||||
|
||||
@override
|
||||
ImageData? get bannerImage => images?.backDrop?.firstOrNull ?? images?.primary ?? getPosters?.primary;
|
||||
|
||||
@override
|
||||
MediaStreamsModel? get streamModel => mediaStreams;
|
||||
|
||||
@override
|
||||
bool get syncAble => true;
|
||||
|
||||
factory MovieModel.fromBaseDto(dto.BaseItemDto item, Ref ref) {
|
||||
return MovieModel(
|
||||
name: item.name ?? "",
|
||||
id: item.id ?? "",
|
||||
childCount: item.childCount,
|
||||
overview: OverviewModel.fromBaseItemDto(item, ref),
|
||||
userData: UserData.fromDto(item.userData),
|
||||
parentId: item.parentId,
|
||||
playlistId: item.playlistItemId,
|
||||
sortName: item.sortName ?? "",
|
||||
status: item.status ?? "",
|
||||
originalTitle: item.originalTitle ?? "",
|
||||
images: ImagesData.fromBaseItem(item, ref),
|
||||
primaryRatio: item.primaryImageAspectRatio,
|
||||
chapters: Chapter.chaptersFromInfo(item.id ?? "", item.chapters ?? [], ref),
|
||||
premiereDate: item.premiereDate ?? DateTime.now(),
|
||||
parentImages: ImagesData.fromBaseItemParent(item, ref),
|
||||
canDelete: item.canDelete,
|
||||
canDownload: item.canDownload,
|
||||
mediaStreams:
|
||||
MediaStreamsModel.fromMediaStreamsList(item.mediaSources?.firstOrNull, item.mediaStreams ?? [], ref),
|
||||
);
|
||||
}
|
||||
}
|
||||
318
lib/models/items/movie_model.mapper.dart
Normal file
318
lib/models/items/movie_model.mapper.dart
Normal file
|
|
@ -0,0 +1,318 @@
|
|||
// coverage:ignore-file
|
||||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
// ignore_for_file: type=lint
|
||||
// ignore_for_file: unused_element, unnecessary_cast, override_on_non_overriding_member
|
||||
// ignore_for_file: strict_raw_type, inference_failure_on_untyped_parameter
|
||||
|
||||
part of 'movie_model.dart';
|
||||
|
||||
class MovieModelMapper extends SubClassMapperBase<MovieModel> {
|
||||
MovieModelMapper._();
|
||||
|
||||
static MovieModelMapper? _instance;
|
||||
static MovieModelMapper ensureInitialized() {
|
||||
if (_instance == null) {
|
||||
MapperContainer.globals.use(_instance = MovieModelMapper._());
|
||||
ItemStreamModelMapper.ensureInitialized().addSubMapper(_instance!);
|
||||
ItemBaseModelMapper.ensureInitialized();
|
||||
OverviewModelMapper.ensureInitialized();
|
||||
UserDataMapper.ensureInitialized();
|
||||
}
|
||||
return _instance!;
|
||||
}
|
||||
|
||||
@override
|
||||
final String id = 'MovieModel';
|
||||
|
||||
static String _$originalTitle(MovieModel v) => v.originalTitle;
|
||||
static const Field<MovieModel, String> _f$originalTitle =
|
||||
Field('originalTitle', _$originalTitle);
|
||||
static String? _$path(MovieModel v) => v.path;
|
||||
static const Field<MovieModel, String> _f$path =
|
||||
Field('path', _$path, opt: true);
|
||||
static List<Chapter> _$chapters(MovieModel v) => v.chapters;
|
||||
static const Field<MovieModel, List<Chapter>> _f$chapters =
|
||||
Field('chapters', _$chapters, opt: true, def: const []);
|
||||
static DateTime _$premiereDate(MovieModel v) => v.premiereDate;
|
||||
static const Field<MovieModel, DateTime> _f$premiereDate =
|
||||
Field('premiereDate', _$premiereDate);
|
||||
static String _$sortName(MovieModel v) => v.sortName;
|
||||
static const Field<MovieModel, String> _f$sortName =
|
||||
Field('sortName', _$sortName);
|
||||
static String _$status(MovieModel v) => v.status;
|
||||
static const Field<MovieModel, String> _f$status = Field('status', _$status);
|
||||
static List<ItemBaseModel> _$related(MovieModel v) => v.related;
|
||||
static const Field<MovieModel, List<ItemBaseModel>> _f$related =
|
||||
Field('related', _$related, opt: true, def: const []);
|
||||
static String _$name(MovieModel v) => v.name;
|
||||
static const Field<MovieModel, String> _f$name = Field('name', _$name);
|
||||
static String _$id(MovieModel v) => v.id;
|
||||
static const Field<MovieModel, String> _f$id = Field('id', _$id);
|
||||
static OverviewModel _$overview(MovieModel v) => v.overview;
|
||||
static const Field<MovieModel, OverviewModel> _f$overview =
|
||||
Field('overview', _$overview);
|
||||
static String? _$parentId(MovieModel v) => v.parentId;
|
||||
static const Field<MovieModel, String> _f$parentId =
|
||||
Field('parentId', _$parentId);
|
||||
static String? _$playlistId(MovieModel v) => v.playlistId;
|
||||
static const Field<MovieModel, String> _f$playlistId =
|
||||
Field('playlistId', _$playlistId);
|
||||
static ImagesData? _$images(MovieModel v) => v.images;
|
||||
static const Field<MovieModel, ImagesData> _f$images =
|
||||
Field('images', _$images);
|
||||
static int? _$childCount(MovieModel v) => v.childCount;
|
||||
static const Field<MovieModel, int> _f$childCount =
|
||||
Field('childCount', _$childCount);
|
||||
static double? _$primaryRatio(MovieModel v) => v.primaryRatio;
|
||||
static const Field<MovieModel, double> _f$primaryRatio =
|
||||
Field('primaryRatio', _$primaryRatio);
|
||||
static UserData _$userData(MovieModel v) => v.userData;
|
||||
static const Field<MovieModel, UserData> _f$userData =
|
||||
Field('userData', _$userData);
|
||||
static ImagesData? _$parentImages(MovieModel v) => v.parentImages;
|
||||
static const Field<MovieModel, ImagesData> _f$parentImages =
|
||||
Field('parentImages', _$parentImages);
|
||||
static MediaStreamsModel _$mediaStreams(MovieModel v) => v.mediaStreams;
|
||||
static const Field<MovieModel, MediaStreamsModel> _f$mediaStreams =
|
||||
Field('mediaStreams', _$mediaStreams);
|
||||
static bool? _$canDownload(MovieModel v) => v.canDownload;
|
||||
static const Field<MovieModel, bool> _f$canDownload =
|
||||
Field('canDownload', _$canDownload);
|
||||
static bool? _$canDelete(MovieModel v) => v.canDelete;
|
||||
static const Field<MovieModel, bool> _f$canDelete =
|
||||
Field('canDelete', _$canDelete);
|
||||
static dto.BaseItemKind? _$jellyType(MovieModel v) => v.jellyType;
|
||||
static const Field<MovieModel, dto.BaseItemKind> _f$jellyType =
|
||||
Field('jellyType', _$jellyType, opt: true);
|
||||
|
||||
@override
|
||||
final MappableFields<MovieModel> fields = const {
|
||||
#originalTitle: _f$originalTitle,
|
||||
#path: _f$path,
|
||||
#chapters: _f$chapters,
|
||||
#premiereDate: _f$premiereDate,
|
||||
#sortName: _f$sortName,
|
||||
#status: _f$status,
|
||||
#related: _f$related,
|
||||
#name: _f$name,
|
||||
#id: _f$id,
|
||||
#overview: _f$overview,
|
||||
#parentId: _f$parentId,
|
||||
#playlistId: _f$playlistId,
|
||||
#images: _f$images,
|
||||
#childCount: _f$childCount,
|
||||
#primaryRatio: _f$primaryRatio,
|
||||
#userData: _f$userData,
|
||||
#parentImages: _f$parentImages,
|
||||
#mediaStreams: _f$mediaStreams,
|
||||
#canDownload: _f$canDownload,
|
||||
#canDelete: _f$canDelete,
|
||||
#jellyType: _f$jellyType,
|
||||
};
|
||||
@override
|
||||
final bool ignoreNull = true;
|
||||
|
||||
@override
|
||||
final String discriminatorKey = 'type';
|
||||
@override
|
||||
final dynamic discriminatorValue = 'MovieModel';
|
||||
@override
|
||||
late final ClassMapperBase superMapper =
|
||||
ItemStreamModelMapper.ensureInitialized();
|
||||
|
||||
static MovieModel _instantiate(DecodingData data) {
|
||||
return MovieModel(
|
||||
originalTitle: data.dec(_f$originalTitle),
|
||||
path: data.dec(_f$path),
|
||||
chapters: data.dec(_f$chapters),
|
||||
premiereDate: data.dec(_f$premiereDate),
|
||||
sortName: data.dec(_f$sortName),
|
||||
status: data.dec(_f$status),
|
||||
related: data.dec(_f$related),
|
||||
name: data.dec(_f$name),
|
||||
id: data.dec(_f$id),
|
||||
overview: data.dec(_f$overview),
|
||||
parentId: data.dec(_f$parentId),
|
||||
playlistId: data.dec(_f$playlistId),
|
||||
images: data.dec(_f$images),
|
||||
childCount: data.dec(_f$childCount),
|
||||
primaryRatio: data.dec(_f$primaryRatio),
|
||||
userData: data.dec(_f$userData),
|
||||
parentImages: data.dec(_f$parentImages),
|
||||
mediaStreams: data.dec(_f$mediaStreams),
|
||||
canDownload: data.dec(_f$canDownload),
|
||||
canDelete: data.dec(_f$canDelete),
|
||||
jellyType: data.dec(_f$jellyType));
|
||||
}
|
||||
|
||||
@override
|
||||
final Function instantiate = _instantiate;
|
||||
|
||||
static MovieModel fromMap(Map<String, dynamic> map) {
|
||||
return ensureInitialized().decodeMap<MovieModel>(map);
|
||||
}
|
||||
|
||||
static MovieModel fromJson(String json) {
|
||||
return ensureInitialized().decodeJson<MovieModel>(json);
|
||||
}
|
||||
}
|
||||
|
||||
mixin MovieModelMappable {
|
||||
String toJson() {
|
||||
return MovieModelMapper.ensureInitialized()
|
||||
.encodeJson<MovieModel>(this as MovieModel);
|
||||
}
|
||||
|
||||
Map<String, dynamic> toMap() {
|
||||
return MovieModelMapper.ensureInitialized()
|
||||
.encodeMap<MovieModel>(this as MovieModel);
|
||||
}
|
||||
|
||||
MovieModelCopyWith<MovieModel, MovieModel, MovieModel> get copyWith =>
|
||||
_MovieModelCopyWithImpl(this as MovieModel, $identity, $identity);
|
||||
@override
|
||||
String toString() {
|
||||
return MovieModelMapper.ensureInitialized()
|
||||
.stringifyValue(this as MovieModel);
|
||||
}
|
||||
}
|
||||
|
||||
extension MovieModelValueCopy<$R, $Out>
|
||||
on ObjectCopyWith<$R, MovieModel, $Out> {
|
||||
MovieModelCopyWith<$R, MovieModel, $Out> get $asMovieModel =>
|
||||
$base.as((v, t, t2) => _MovieModelCopyWithImpl(v, t, t2));
|
||||
}
|
||||
|
||||
abstract class MovieModelCopyWith<$R, $In extends MovieModel, $Out>
|
||||
implements ItemStreamModelCopyWith<$R, $In, $Out> {
|
||||
ListCopyWith<$R, Chapter, ObjectCopyWith<$R, Chapter, Chapter>> get chapters;
|
||||
ListCopyWith<$R, ItemBaseModel,
|
||||
ItemBaseModelCopyWith<$R, ItemBaseModel, ItemBaseModel>> get related;
|
||||
@override
|
||||
OverviewModelCopyWith<$R, OverviewModel, OverviewModel> get overview;
|
||||
@override
|
||||
UserDataCopyWith<$R, UserData, UserData> get userData;
|
||||
@override
|
||||
$R call(
|
||||
{String? originalTitle,
|
||||
String? path,
|
||||
List<Chapter>? chapters,
|
||||
DateTime? premiereDate,
|
||||
String? sortName,
|
||||
String? status,
|
||||
List<ItemBaseModel>? related,
|
||||
String? name,
|
||||
String? id,
|
||||
OverviewModel? overview,
|
||||
String? parentId,
|
||||
String? playlistId,
|
||||
ImagesData? images,
|
||||
int? childCount,
|
||||
double? primaryRatio,
|
||||
UserData? userData,
|
||||
ImagesData? parentImages,
|
||||
MediaStreamsModel? mediaStreams,
|
||||
bool? canDownload,
|
||||
bool? canDelete,
|
||||
dto.BaseItemKind? jellyType});
|
||||
MovieModelCopyWith<$R2, $In, $Out2> $chain<$R2, $Out2>(Then<$Out2, $R2> t);
|
||||
}
|
||||
|
||||
class _MovieModelCopyWithImpl<$R, $Out>
|
||||
extends ClassCopyWithBase<$R, MovieModel, $Out>
|
||||
implements MovieModelCopyWith<$R, MovieModel, $Out> {
|
||||
_MovieModelCopyWithImpl(super.value, super.then, super.then2);
|
||||
|
||||
@override
|
||||
late final ClassMapperBase<MovieModel> $mapper =
|
||||
MovieModelMapper.ensureInitialized();
|
||||
@override
|
||||
ListCopyWith<$R, Chapter, ObjectCopyWith<$R, Chapter, Chapter>>
|
||||
get chapters => ListCopyWith($value.chapters,
|
||||
(v, t) => ObjectCopyWith(v, $identity, t), (v) => call(chapters: v));
|
||||
@override
|
||||
ListCopyWith<$R, ItemBaseModel,
|
||||
ItemBaseModelCopyWith<$R, ItemBaseModel, ItemBaseModel>>
|
||||
get related => ListCopyWith($value.related,
|
||||
(v, t) => v.copyWith.$chain(t), (v) => call(related: v));
|
||||
@override
|
||||
OverviewModelCopyWith<$R, OverviewModel, OverviewModel> get overview =>
|
||||
$value.overview.copyWith.$chain((v) => call(overview: v));
|
||||
@override
|
||||
UserDataCopyWith<$R, UserData, UserData> get userData =>
|
||||
$value.userData.copyWith.$chain((v) => call(userData: v));
|
||||
@override
|
||||
$R call(
|
||||
{String? originalTitle,
|
||||
Object? path = $none,
|
||||
List<Chapter>? chapters,
|
||||
DateTime? premiereDate,
|
||||
String? sortName,
|
||||
String? status,
|
||||
List<ItemBaseModel>? related,
|
||||
String? name,
|
||||
String? id,
|
||||
OverviewModel? overview,
|
||||
Object? parentId = $none,
|
||||
Object? playlistId = $none,
|
||||
Object? images = $none,
|
||||
Object? childCount = $none,
|
||||
Object? primaryRatio = $none,
|
||||
UserData? userData,
|
||||
Object? parentImages = $none,
|
||||
MediaStreamsModel? mediaStreams,
|
||||
Object? canDownload = $none,
|
||||
Object? canDelete = $none,
|
||||
Object? jellyType = $none}) =>
|
||||
$apply(FieldCopyWithData({
|
||||
if (originalTitle != null) #originalTitle: originalTitle,
|
||||
if (path != $none) #path: path,
|
||||
if (chapters != null) #chapters: chapters,
|
||||
if (premiereDate != null) #premiereDate: premiereDate,
|
||||
if (sortName != null) #sortName: sortName,
|
||||
if (status != null) #status: status,
|
||||
if (related != null) #related: related,
|
||||
if (name != null) #name: name,
|
||||
if (id != null) #id: id,
|
||||
if (overview != null) #overview: overview,
|
||||
if (parentId != $none) #parentId: parentId,
|
||||
if (playlistId != $none) #playlistId: playlistId,
|
||||
if (images != $none) #images: images,
|
||||
if (childCount != $none) #childCount: childCount,
|
||||
if (primaryRatio != $none) #primaryRatio: primaryRatio,
|
||||
if (userData != null) #userData: userData,
|
||||
if (parentImages != $none) #parentImages: parentImages,
|
||||
if (mediaStreams != null) #mediaStreams: mediaStreams,
|
||||
if (canDownload != $none) #canDownload: canDownload,
|
||||
if (canDelete != $none) #canDelete: canDelete,
|
||||
if (jellyType != $none) #jellyType: jellyType
|
||||
}));
|
||||
@override
|
||||
MovieModel $make(CopyWithData data) => MovieModel(
|
||||
originalTitle: data.get(#originalTitle, or: $value.originalTitle),
|
||||
path: data.get(#path, or: $value.path),
|
||||
chapters: data.get(#chapters, or: $value.chapters),
|
||||
premiereDate: data.get(#premiereDate, or: $value.premiereDate),
|
||||
sortName: data.get(#sortName, or: $value.sortName),
|
||||
status: data.get(#status, or: $value.status),
|
||||
related: data.get(#related, or: $value.related),
|
||||
name: data.get(#name, or: $value.name),
|
||||
id: data.get(#id, or: $value.id),
|
||||
overview: data.get(#overview, or: $value.overview),
|
||||
parentId: data.get(#parentId, or: $value.parentId),
|
||||
playlistId: data.get(#playlistId, or: $value.playlistId),
|
||||
images: data.get(#images, or: $value.images),
|
||||
childCount: data.get(#childCount, or: $value.childCount),
|
||||
primaryRatio: data.get(#primaryRatio, or: $value.primaryRatio),
|
||||
userData: data.get(#userData, or: $value.userData),
|
||||
parentImages: data.get(#parentImages, or: $value.parentImages),
|
||||
mediaStreams: data.get(#mediaStreams, or: $value.mediaStreams),
|
||||
canDownload: data.get(#canDownload, or: $value.canDownload),
|
||||
canDelete: data.get(#canDelete, or: $value.canDelete),
|
||||
jellyType: data.get(#jellyType, or: $value.jellyType));
|
||||
|
||||
@override
|
||||
MovieModelCopyWith<$R2, MovieModel, $Out2> $chain<$R2, $Out2>(
|
||||
Then<$Out2, $R2> t) =>
|
||||
_MovieModelCopyWithImpl($value, $cast, t);
|
||||
}
|
||||
82
lib/models/items/overview_model.dart
Normal file
82
lib/models/items/overview_model.dart
Normal file
|
|
@ -0,0 +1,82 @@
|
|||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
|
||||
import 'package:fladder/jellyfin/jellyfin_open_api.swagger.dart';
|
||||
import 'package:fladder/models/items/chapters_model.dart';
|
||||
import 'package:fladder/models/items/item_shared_models.dart';
|
||||
import 'package:fladder/models/items/trick_play_model.dart';
|
||||
import 'package:fladder/util/duration_extensions.dart';
|
||||
|
||||
import 'package:dart_mappable/dart_mappable.dart';
|
||||
|
||||
part 'overview_model.mapper.dart';
|
||||
|
||||
@MappableClass()
|
||||
class OverviewModel with OverviewModelMappable {
|
||||
final Duration? runTime;
|
||||
final String summary;
|
||||
final int? yearAired;
|
||||
final DateTime? dateAdded;
|
||||
final String? parentalRating;
|
||||
final int? productionYear;
|
||||
final double? criticRating;
|
||||
final double? communityRating;
|
||||
final Map<String, TrickPlayModel>? trickPlayInfo;
|
||||
final List<Chapter>? chapters;
|
||||
final List<ExternalUrls>? externalUrls;
|
||||
final List<Studio> studios;
|
||||
final List<String> genres;
|
||||
final List<GenreItems> genreItems;
|
||||
final List<String> tags;
|
||||
final List<Person> people;
|
||||
const OverviewModel({
|
||||
this.runTime,
|
||||
this.summary = "",
|
||||
this.yearAired,
|
||||
this.dateAdded,
|
||||
this.parentalRating,
|
||||
this.productionYear,
|
||||
this.criticRating,
|
||||
this.communityRating,
|
||||
this.trickPlayInfo,
|
||||
this.chapters,
|
||||
this.externalUrls,
|
||||
this.studios = const [],
|
||||
this.genres = const [],
|
||||
this.genreItems = const [],
|
||||
this.tags = const [],
|
||||
this.people = const [],
|
||||
});
|
||||
|
||||
List<Person> get directors {
|
||||
return people.where((element) => element.type == PersonKind.director).toList();
|
||||
}
|
||||
|
||||
List<Person> get writers {
|
||||
return people.where((element) => element.type == PersonKind.writer).toList();
|
||||
}
|
||||
|
||||
factory OverviewModel.fromBaseItemDto(BaseItemDto item, Ref ref) {
|
||||
final trickPlayItem = item.trickplay;
|
||||
return OverviewModel(
|
||||
runTime: item.runTimeDuration,
|
||||
yearAired: item.productionYear,
|
||||
parentalRating: item.officialRating,
|
||||
summary: item.overview ?? "",
|
||||
genres: item.genres ?? [],
|
||||
criticRating: item.criticRating,
|
||||
communityRating: item.communityRating,
|
||||
tags: item.tags ?? [],
|
||||
dateAdded: item.dateCreated,
|
||||
trickPlayInfo:
|
||||
trickPlayItem != null && trickPlayItem.isNotEmpty ? TrickPlayModel.toTrickPlayMap(trickPlayItem) : null,
|
||||
chapters: item.id != null ? Chapter.chaptersFromInfo(item.id ?? "", item.chapters ?? [], ref) : null,
|
||||
studios: item.studios?.map((e) => Studio(id: e.id ?? "", name: e.name ?? "")).toList() ?? [],
|
||||
genreItems: item.genreItems?.map((e) => GenreItems(id: e.id ?? "", name: e.name ?? "")).toList() ?? [],
|
||||
externalUrls: ExternalUrls.fromDto(item.externalUrls ?? []),
|
||||
people: Person.peopleFromDto(item.people ?? [], ref),
|
||||
);
|
||||
}
|
||||
|
||||
factory OverviewModel.fromMap(Map<String, dynamic> map) => OverviewModelMapper.fromMap(map);
|
||||
factory OverviewModel.fromJson(String json) => OverviewModelMapper.fromJson(json);
|
||||
}
|
||||
302
lib/models/items/overview_model.mapper.dart
Normal file
302
lib/models/items/overview_model.mapper.dart
Normal file
|
|
@ -0,0 +1,302 @@
|
|||
// coverage:ignore-file
|
||||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
// ignore_for_file: type=lint
|
||||
// ignore_for_file: unused_element, unnecessary_cast, override_on_non_overriding_member
|
||||
// ignore_for_file: strict_raw_type, inference_failure_on_untyped_parameter
|
||||
|
||||
part of 'overview_model.dart';
|
||||
|
||||
class OverviewModelMapper extends ClassMapperBase<OverviewModel> {
|
||||
OverviewModelMapper._();
|
||||
|
||||
static OverviewModelMapper? _instance;
|
||||
static OverviewModelMapper ensureInitialized() {
|
||||
if (_instance == null) {
|
||||
MapperContainer.globals.use(_instance = OverviewModelMapper._());
|
||||
}
|
||||
return _instance!;
|
||||
}
|
||||
|
||||
@override
|
||||
final String id = 'OverviewModel';
|
||||
|
||||
static Duration? _$runTime(OverviewModel v) => v.runTime;
|
||||
static const Field<OverviewModel, Duration> _f$runTime =
|
||||
Field('runTime', _$runTime, opt: true);
|
||||
static String _$summary(OverviewModel v) => v.summary;
|
||||
static const Field<OverviewModel, String> _f$summary =
|
||||
Field('summary', _$summary, opt: true, def: "");
|
||||
static int? _$yearAired(OverviewModel v) => v.yearAired;
|
||||
static const Field<OverviewModel, int> _f$yearAired =
|
||||
Field('yearAired', _$yearAired, opt: true);
|
||||
static DateTime? _$dateAdded(OverviewModel v) => v.dateAdded;
|
||||
static const Field<OverviewModel, DateTime> _f$dateAdded =
|
||||
Field('dateAdded', _$dateAdded, opt: true);
|
||||
static String? _$parentalRating(OverviewModel v) => v.parentalRating;
|
||||
static const Field<OverviewModel, String> _f$parentalRating =
|
||||
Field('parentalRating', _$parentalRating, opt: true);
|
||||
static int? _$productionYear(OverviewModel v) => v.productionYear;
|
||||
static const Field<OverviewModel, int> _f$productionYear =
|
||||
Field('productionYear', _$productionYear, opt: true);
|
||||
static double? _$criticRating(OverviewModel v) => v.criticRating;
|
||||
static const Field<OverviewModel, double> _f$criticRating =
|
||||
Field('criticRating', _$criticRating, opt: true);
|
||||
static double? _$communityRating(OverviewModel v) => v.communityRating;
|
||||
static const Field<OverviewModel, double> _f$communityRating =
|
||||
Field('communityRating', _$communityRating, opt: true);
|
||||
static Map<String, TrickPlayModel>? _$trickPlayInfo(OverviewModel v) =>
|
||||
v.trickPlayInfo;
|
||||
static const Field<OverviewModel, Map<String, TrickPlayModel>>
|
||||
_f$trickPlayInfo = Field('trickPlayInfo', _$trickPlayInfo, opt: true);
|
||||
static List<Chapter>? _$chapters(OverviewModel v) => v.chapters;
|
||||
static const Field<OverviewModel, List<Chapter>> _f$chapters =
|
||||
Field('chapters', _$chapters, opt: true);
|
||||
static List<ExternalUrls>? _$externalUrls(OverviewModel v) => v.externalUrls;
|
||||
static const Field<OverviewModel, List<ExternalUrls>> _f$externalUrls =
|
||||
Field('externalUrls', _$externalUrls, opt: true);
|
||||
static List<Studio> _$studios(OverviewModel v) => v.studios;
|
||||
static const Field<OverviewModel, List<Studio>> _f$studios =
|
||||
Field('studios', _$studios, opt: true, def: const []);
|
||||
static List<String> _$genres(OverviewModel v) => v.genres;
|
||||
static const Field<OverviewModel, List<String>> _f$genres =
|
||||
Field('genres', _$genres, opt: true, def: const []);
|
||||
static List<GenreItems> _$genreItems(OverviewModel v) => v.genreItems;
|
||||
static const Field<OverviewModel, List<GenreItems>> _f$genreItems =
|
||||
Field('genreItems', _$genreItems, opt: true, def: const []);
|
||||
static List<String> _$tags(OverviewModel v) => v.tags;
|
||||
static const Field<OverviewModel, List<String>> _f$tags =
|
||||
Field('tags', _$tags, opt: true, def: const []);
|
||||
static List<Person> _$people(OverviewModel v) => v.people;
|
||||
static const Field<OverviewModel, List<Person>> _f$people =
|
||||
Field('people', _$people, opt: true, def: const []);
|
||||
|
||||
@override
|
||||
final MappableFields<OverviewModel> fields = const {
|
||||
#runTime: _f$runTime,
|
||||
#summary: _f$summary,
|
||||
#yearAired: _f$yearAired,
|
||||
#dateAdded: _f$dateAdded,
|
||||
#parentalRating: _f$parentalRating,
|
||||
#productionYear: _f$productionYear,
|
||||
#criticRating: _f$criticRating,
|
||||
#communityRating: _f$communityRating,
|
||||
#trickPlayInfo: _f$trickPlayInfo,
|
||||
#chapters: _f$chapters,
|
||||
#externalUrls: _f$externalUrls,
|
||||
#studios: _f$studios,
|
||||
#genres: _f$genres,
|
||||
#genreItems: _f$genreItems,
|
||||
#tags: _f$tags,
|
||||
#people: _f$people,
|
||||
};
|
||||
@override
|
||||
final bool ignoreNull = true;
|
||||
|
||||
static OverviewModel _instantiate(DecodingData data) {
|
||||
return OverviewModel(
|
||||
runTime: data.dec(_f$runTime),
|
||||
summary: data.dec(_f$summary),
|
||||
yearAired: data.dec(_f$yearAired),
|
||||
dateAdded: data.dec(_f$dateAdded),
|
||||
parentalRating: data.dec(_f$parentalRating),
|
||||
productionYear: data.dec(_f$productionYear),
|
||||
criticRating: data.dec(_f$criticRating),
|
||||
communityRating: data.dec(_f$communityRating),
|
||||
trickPlayInfo: data.dec(_f$trickPlayInfo),
|
||||
chapters: data.dec(_f$chapters),
|
||||
externalUrls: data.dec(_f$externalUrls),
|
||||
studios: data.dec(_f$studios),
|
||||
genres: data.dec(_f$genres),
|
||||
genreItems: data.dec(_f$genreItems),
|
||||
tags: data.dec(_f$tags),
|
||||
people: data.dec(_f$people));
|
||||
}
|
||||
|
||||
@override
|
||||
final Function instantiate = _instantiate;
|
||||
|
||||
static OverviewModel fromMap(Map<String, dynamic> map) {
|
||||
return ensureInitialized().decodeMap<OverviewModel>(map);
|
||||
}
|
||||
|
||||
static OverviewModel fromJson(String json) {
|
||||
return ensureInitialized().decodeJson<OverviewModel>(json);
|
||||
}
|
||||
}
|
||||
|
||||
mixin OverviewModelMappable {
|
||||
String toJson() {
|
||||
return OverviewModelMapper.ensureInitialized()
|
||||
.encodeJson<OverviewModel>(this as OverviewModel);
|
||||
}
|
||||
|
||||
Map<String, dynamic> toMap() {
|
||||
return OverviewModelMapper.ensureInitialized()
|
||||
.encodeMap<OverviewModel>(this as OverviewModel);
|
||||
}
|
||||
|
||||
OverviewModelCopyWith<OverviewModel, OverviewModel, OverviewModel>
|
||||
get copyWith => _OverviewModelCopyWithImpl(
|
||||
this as OverviewModel, $identity, $identity);
|
||||
@override
|
||||
String toString() {
|
||||
return OverviewModelMapper.ensureInitialized()
|
||||
.stringifyValue(this as OverviewModel);
|
||||
}
|
||||
}
|
||||
|
||||
extension OverviewModelValueCopy<$R, $Out>
|
||||
on ObjectCopyWith<$R, OverviewModel, $Out> {
|
||||
OverviewModelCopyWith<$R, OverviewModel, $Out> get $asOverviewModel =>
|
||||
$base.as((v, t, t2) => _OverviewModelCopyWithImpl(v, t, t2));
|
||||
}
|
||||
|
||||
abstract class OverviewModelCopyWith<$R, $In extends OverviewModel, $Out>
|
||||
implements ClassCopyWith<$R, $In, $Out> {
|
||||
MapCopyWith<$R, String, TrickPlayModel,
|
||||
ObjectCopyWith<$R, TrickPlayModel, TrickPlayModel>>? get trickPlayInfo;
|
||||
ListCopyWith<$R, Chapter, ObjectCopyWith<$R, Chapter, Chapter>>? get chapters;
|
||||
ListCopyWith<$R, ExternalUrls,
|
||||
ObjectCopyWith<$R, ExternalUrls, ExternalUrls>>? get externalUrls;
|
||||
ListCopyWith<$R, Studio, ObjectCopyWith<$R, Studio, Studio>> get studios;
|
||||
ListCopyWith<$R, String, ObjectCopyWith<$R, String, String>> get genres;
|
||||
ListCopyWith<$R, GenreItems, ObjectCopyWith<$R, GenreItems, GenreItems>>
|
||||
get genreItems;
|
||||
ListCopyWith<$R, String, ObjectCopyWith<$R, String, String>> get tags;
|
||||
ListCopyWith<$R, Person, ObjectCopyWith<$R, Person, Person>> get people;
|
||||
$R call(
|
||||
{Duration? runTime,
|
||||
String? summary,
|
||||
int? yearAired,
|
||||
DateTime? dateAdded,
|
||||
String? parentalRating,
|
||||
int? productionYear,
|
||||
double? criticRating,
|
||||
double? communityRating,
|
||||
Map<String, TrickPlayModel>? trickPlayInfo,
|
||||
List<Chapter>? chapters,
|
||||
List<ExternalUrls>? externalUrls,
|
||||
List<Studio>? studios,
|
||||
List<String>? genres,
|
||||
List<GenreItems>? genreItems,
|
||||
List<String>? tags,
|
||||
List<Person>? people});
|
||||
OverviewModelCopyWith<$R2, $In, $Out2> $chain<$R2, $Out2>(Then<$Out2, $R2> t);
|
||||
}
|
||||
|
||||
class _OverviewModelCopyWithImpl<$R, $Out>
|
||||
extends ClassCopyWithBase<$R, OverviewModel, $Out>
|
||||
implements OverviewModelCopyWith<$R, OverviewModel, $Out> {
|
||||
_OverviewModelCopyWithImpl(super.value, super.then, super.then2);
|
||||
|
||||
@override
|
||||
late final ClassMapperBase<OverviewModel> $mapper =
|
||||
OverviewModelMapper.ensureInitialized();
|
||||
@override
|
||||
MapCopyWith<$R, String, TrickPlayModel,
|
||||
ObjectCopyWith<$R, TrickPlayModel, TrickPlayModel>>?
|
||||
get trickPlayInfo => $value.trickPlayInfo != null
|
||||
? MapCopyWith(
|
||||
$value.trickPlayInfo!,
|
||||
(v, t) => ObjectCopyWith(v, $identity, t),
|
||||
(v) => call(trickPlayInfo: v))
|
||||
: null;
|
||||
@override
|
||||
ListCopyWith<$R, Chapter, ObjectCopyWith<$R, Chapter, Chapter>>?
|
||||
get chapters => $value.chapters != null
|
||||
? ListCopyWith(
|
||||
$value.chapters!,
|
||||
(v, t) => ObjectCopyWith(v, $identity, t),
|
||||
(v) => call(chapters: v))
|
||||
: null;
|
||||
@override
|
||||
ListCopyWith<$R, ExternalUrls,
|
||||
ObjectCopyWith<$R, ExternalUrls, ExternalUrls>>?
|
||||
get externalUrls => $value.externalUrls != null
|
||||
? ListCopyWith(
|
||||
$value.externalUrls!,
|
||||
(v, t) => ObjectCopyWith(v, $identity, t),
|
||||
(v) => call(externalUrls: v))
|
||||
: null;
|
||||
@override
|
||||
ListCopyWith<$R, Studio, ObjectCopyWith<$R, Studio, Studio>> get studios =>
|
||||
ListCopyWith($value.studios, (v, t) => ObjectCopyWith(v, $identity, t),
|
||||
(v) => call(studios: v));
|
||||
@override
|
||||
ListCopyWith<$R, String, ObjectCopyWith<$R, String, String>> get genres =>
|
||||
ListCopyWith($value.genres, (v, t) => ObjectCopyWith(v, $identity, t),
|
||||
(v) => call(genres: v));
|
||||
@override
|
||||
ListCopyWith<$R, GenreItems, ObjectCopyWith<$R, GenreItems, GenreItems>>
|
||||
get genreItems => ListCopyWith(
|
||||
$value.genreItems,
|
||||
(v, t) => ObjectCopyWith(v, $identity, t),
|
||||
(v) => call(genreItems: v));
|
||||
@override
|
||||
ListCopyWith<$R, String, ObjectCopyWith<$R, String, String>> get tags =>
|
||||
ListCopyWith($value.tags, (v, t) => ObjectCopyWith(v, $identity, t),
|
||||
(v) => call(tags: v));
|
||||
@override
|
||||
ListCopyWith<$R, Person, ObjectCopyWith<$R, Person, Person>> get people =>
|
||||
ListCopyWith($value.people, (v, t) => ObjectCopyWith(v, $identity, t),
|
||||
(v) => call(people: v));
|
||||
@override
|
||||
$R call(
|
||||
{Object? runTime = $none,
|
||||
String? summary,
|
||||
Object? yearAired = $none,
|
||||
Object? dateAdded = $none,
|
||||
Object? parentalRating = $none,
|
||||
Object? productionYear = $none,
|
||||
Object? criticRating = $none,
|
||||
Object? communityRating = $none,
|
||||
Object? trickPlayInfo = $none,
|
||||
Object? chapters = $none,
|
||||
Object? externalUrls = $none,
|
||||
List<Studio>? studios,
|
||||
List<String>? genres,
|
||||
List<GenreItems>? genreItems,
|
||||
List<String>? tags,
|
||||
List<Person>? people}) =>
|
||||
$apply(FieldCopyWithData({
|
||||
if (runTime != $none) #runTime: runTime,
|
||||
if (summary != null) #summary: summary,
|
||||
if (yearAired != $none) #yearAired: yearAired,
|
||||
if (dateAdded != $none) #dateAdded: dateAdded,
|
||||
if (parentalRating != $none) #parentalRating: parentalRating,
|
||||
if (productionYear != $none) #productionYear: productionYear,
|
||||
if (criticRating != $none) #criticRating: criticRating,
|
||||
if (communityRating != $none) #communityRating: communityRating,
|
||||
if (trickPlayInfo != $none) #trickPlayInfo: trickPlayInfo,
|
||||
if (chapters != $none) #chapters: chapters,
|
||||
if (externalUrls != $none) #externalUrls: externalUrls,
|
||||
if (studios != null) #studios: studios,
|
||||
if (genres != null) #genres: genres,
|
||||
if (genreItems != null) #genreItems: genreItems,
|
||||
if (tags != null) #tags: tags,
|
||||
if (people != null) #people: people
|
||||
}));
|
||||
@override
|
||||
OverviewModel $make(CopyWithData data) => OverviewModel(
|
||||
runTime: data.get(#runTime, or: $value.runTime),
|
||||
summary: data.get(#summary, or: $value.summary),
|
||||
yearAired: data.get(#yearAired, or: $value.yearAired),
|
||||
dateAdded: data.get(#dateAdded, or: $value.dateAdded),
|
||||
parentalRating: data.get(#parentalRating, or: $value.parentalRating),
|
||||
productionYear: data.get(#productionYear, or: $value.productionYear),
|
||||
criticRating: data.get(#criticRating, or: $value.criticRating),
|
||||
communityRating: data.get(#communityRating, or: $value.communityRating),
|
||||
trickPlayInfo: data.get(#trickPlayInfo, or: $value.trickPlayInfo),
|
||||
chapters: data.get(#chapters, or: $value.chapters),
|
||||
externalUrls: data.get(#externalUrls, or: $value.externalUrls),
|
||||
studios: data.get(#studios, or: $value.studios),
|
||||
genres: data.get(#genres, or: $value.genres),
|
||||
genreItems: data.get(#genreItems, or: $value.genreItems),
|
||||
tags: data.get(#tags, or: $value.tags),
|
||||
people: data.get(#people, or: $value.people));
|
||||
|
||||
@override
|
||||
OverviewModelCopyWith<$R2, OverviewModel, $Out2> $chain<$R2, $Out2>(
|
||||
Then<$Out2, $R2> t) =>
|
||||
_OverviewModelCopyWithImpl($value, $cast, t);
|
||||
}
|
||||
68
lib/models/items/person_model.dart
Normal file
68
lib/models/items/person_model.dart
Normal file
|
|
@ -0,0 +1,68 @@
|
|||
import 'package:fladder/models/items/images_models.dart';
|
||||
import 'package:fladder/models/items/overview_model.dart';
|
||||
|
||||
import 'package:fladder/jellyfin/jellyfin_open_api.swagger.dart';
|
||||
import 'package:fladder/models/item_base_model.dart';
|
||||
import 'package:fladder/models/items/item_shared_models.dart';
|
||||
import 'package:fladder/models/items/movie_model.dart';
|
||||
import 'package:fladder/models/items/series_model.dart';
|
||||
|
||||
import 'package:dart_mappable/dart_mappable.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
|
||||
part 'person_model.mapper.dart';
|
||||
|
||||
@MappableClass()
|
||||
class PersonModel extends ItemBaseModel with PersonModelMappable {
|
||||
final DateTime? dateOfBirth;
|
||||
final List<String> birthPlace;
|
||||
final List<MovieModel> movies;
|
||||
final List<SeriesModel> series;
|
||||
const PersonModel({
|
||||
this.dateOfBirth,
|
||||
required this.birthPlace,
|
||||
required this.movies,
|
||||
required this.series,
|
||||
required super.name,
|
||||
required super.id,
|
||||
required super.overview,
|
||||
required super.parentId,
|
||||
required super.playlistId,
|
||||
required super.images,
|
||||
required super.childCount,
|
||||
required super.primaryRatio,
|
||||
required super.userData,
|
||||
super.canDownload,
|
||||
super.canDelete,
|
||||
super.jellyType,
|
||||
});
|
||||
|
||||
static PersonModel fromBaseDto(BaseItemDto item, Ref ref) {
|
||||
return PersonModel(
|
||||
name: item.name ?? "",
|
||||
id: item.id ?? "",
|
||||
childCount: item.childCount,
|
||||
overview: OverviewModel.fromBaseItemDto(item, ref),
|
||||
userData: UserData.fromDto(item.userData),
|
||||
parentId: item.parentId,
|
||||
playlistId: item.playlistItemId,
|
||||
images: ImagesData.fromBaseItem(item, ref, getOriginalSize: true),
|
||||
primaryRatio: item.primaryImageAspectRatio,
|
||||
dateOfBirth: item.premiereDate,
|
||||
birthPlace: item.productionLocations ?? [],
|
||||
movies: [],
|
||||
series: [],
|
||||
);
|
||||
}
|
||||
|
||||
int? get age {
|
||||
if (dateOfBirth == null) return null;
|
||||
final today = DateTime.now();
|
||||
final months = today.month - dateOfBirth!.month;
|
||||
if (months < 0) {
|
||||
return (dateOfBirth!.year - (DateTime.now().year - 1)).abs();
|
||||
} else {
|
||||
return (dateOfBirth!.year - DateTime.now().year).abs();
|
||||
}
|
||||
}
|
||||
}
|
||||
281
lib/models/items/person_model.mapper.dart
Normal file
281
lib/models/items/person_model.mapper.dart
Normal file
|
|
@ -0,0 +1,281 @@
|
|||
// coverage:ignore-file
|
||||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
// ignore_for_file: type=lint
|
||||
// ignore_for_file: unused_element, unnecessary_cast, override_on_non_overriding_member
|
||||
// ignore_for_file: strict_raw_type, inference_failure_on_untyped_parameter
|
||||
|
||||
part of 'person_model.dart';
|
||||
|
||||
class PersonModelMapper extends SubClassMapperBase<PersonModel> {
|
||||
PersonModelMapper._();
|
||||
|
||||
static PersonModelMapper? _instance;
|
||||
static PersonModelMapper ensureInitialized() {
|
||||
if (_instance == null) {
|
||||
MapperContainer.globals.use(_instance = PersonModelMapper._());
|
||||
ItemBaseModelMapper.ensureInitialized().addSubMapper(_instance!);
|
||||
MovieModelMapper.ensureInitialized();
|
||||
SeriesModelMapper.ensureInitialized();
|
||||
OverviewModelMapper.ensureInitialized();
|
||||
UserDataMapper.ensureInitialized();
|
||||
}
|
||||
return _instance!;
|
||||
}
|
||||
|
||||
@override
|
||||
final String id = 'PersonModel';
|
||||
|
||||
static DateTime? _$dateOfBirth(PersonModel v) => v.dateOfBirth;
|
||||
static const Field<PersonModel, DateTime> _f$dateOfBirth =
|
||||
Field('dateOfBirth', _$dateOfBirth, opt: true);
|
||||
static List<String> _$birthPlace(PersonModel v) => v.birthPlace;
|
||||
static const Field<PersonModel, List<String>> _f$birthPlace =
|
||||
Field('birthPlace', _$birthPlace);
|
||||
static List<MovieModel> _$movies(PersonModel v) => v.movies;
|
||||
static const Field<PersonModel, List<MovieModel>> _f$movies =
|
||||
Field('movies', _$movies);
|
||||
static List<SeriesModel> _$series(PersonModel v) => v.series;
|
||||
static const Field<PersonModel, List<SeriesModel>> _f$series =
|
||||
Field('series', _$series);
|
||||
static String _$name(PersonModel v) => v.name;
|
||||
static const Field<PersonModel, String> _f$name = Field('name', _$name);
|
||||
static String _$id(PersonModel v) => v.id;
|
||||
static const Field<PersonModel, String> _f$id = Field('id', _$id);
|
||||
static OverviewModel _$overview(PersonModel v) => v.overview;
|
||||
static const Field<PersonModel, OverviewModel> _f$overview =
|
||||
Field('overview', _$overview);
|
||||
static String? _$parentId(PersonModel v) => v.parentId;
|
||||
static const Field<PersonModel, String> _f$parentId =
|
||||
Field('parentId', _$parentId);
|
||||
static String? _$playlistId(PersonModel v) => v.playlistId;
|
||||
static const Field<PersonModel, String> _f$playlistId =
|
||||
Field('playlistId', _$playlistId);
|
||||
static ImagesData? _$images(PersonModel v) => v.images;
|
||||
static const Field<PersonModel, ImagesData> _f$images =
|
||||
Field('images', _$images);
|
||||
static int? _$childCount(PersonModel v) => v.childCount;
|
||||
static const Field<PersonModel, int> _f$childCount =
|
||||
Field('childCount', _$childCount);
|
||||
static double? _$primaryRatio(PersonModel v) => v.primaryRatio;
|
||||
static const Field<PersonModel, double> _f$primaryRatio =
|
||||
Field('primaryRatio', _$primaryRatio);
|
||||
static UserData _$userData(PersonModel v) => v.userData;
|
||||
static const Field<PersonModel, UserData> _f$userData =
|
||||
Field('userData', _$userData);
|
||||
static bool? _$canDownload(PersonModel v) => v.canDownload;
|
||||
static const Field<PersonModel, bool> _f$canDownload =
|
||||
Field('canDownload', _$canDownload, opt: true);
|
||||
static bool? _$canDelete(PersonModel v) => v.canDelete;
|
||||
static const Field<PersonModel, bool> _f$canDelete =
|
||||
Field('canDelete', _$canDelete, opt: true);
|
||||
static BaseItemKind? _$jellyType(PersonModel v) => v.jellyType;
|
||||
static const Field<PersonModel, BaseItemKind> _f$jellyType =
|
||||
Field('jellyType', _$jellyType, opt: true);
|
||||
|
||||
@override
|
||||
final MappableFields<PersonModel> fields = const {
|
||||
#dateOfBirth: _f$dateOfBirth,
|
||||
#birthPlace: _f$birthPlace,
|
||||
#movies: _f$movies,
|
||||
#series: _f$series,
|
||||
#name: _f$name,
|
||||
#id: _f$id,
|
||||
#overview: _f$overview,
|
||||
#parentId: _f$parentId,
|
||||
#playlistId: _f$playlistId,
|
||||
#images: _f$images,
|
||||
#childCount: _f$childCount,
|
||||
#primaryRatio: _f$primaryRatio,
|
||||
#userData: _f$userData,
|
||||
#canDownload: _f$canDownload,
|
||||
#canDelete: _f$canDelete,
|
||||
#jellyType: _f$jellyType,
|
||||
};
|
||||
@override
|
||||
final bool ignoreNull = true;
|
||||
|
||||
@override
|
||||
final String discriminatorKey = 'type';
|
||||
@override
|
||||
final dynamic discriminatorValue = 'PersonModel';
|
||||
@override
|
||||
late final ClassMapperBase superMapper =
|
||||
ItemBaseModelMapper.ensureInitialized();
|
||||
|
||||
static PersonModel _instantiate(DecodingData data) {
|
||||
return PersonModel(
|
||||
dateOfBirth: data.dec(_f$dateOfBirth),
|
||||
birthPlace: data.dec(_f$birthPlace),
|
||||
movies: data.dec(_f$movies),
|
||||
series: data.dec(_f$series),
|
||||
name: data.dec(_f$name),
|
||||
id: data.dec(_f$id),
|
||||
overview: data.dec(_f$overview),
|
||||
parentId: data.dec(_f$parentId),
|
||||
playlistId: data.dec(_f$playlistId),
|
||||
images: data.dec(_f$images),
|
||||
childCount: data.dec(_f$childCount),
|
||||
primaryRatio: data.dec(_f$primaryRatio),
|
||||
userData: data.dec(_f$userData),
|
||||
canDownload: data.dec(_f$canDownload),
|
||||
canDelete: data.dec(_f$canDelete),
|
||||
jellyType: data.dec(_f$jellyType));
|
||||
}
|
||||
|
||||
@override
|
||||
final Function instantiate = _instantiate;
|
||||
|
||||
static PersonModel fromMap(Map<String, dynamic> map) {
|
||||
return ensureInitialized().decodeMap<PersonModel>(map);
|
||||
}
|
||||
|
||||
static PersonModel fromJson(String json) {
|
||||
return ensureInitialized().decodeJson<PersonModel>(json);
|
||||
}
|
||||
}
|
||||
|
||||
mixin PersonModelMappable {
|
||||
String toJson() {
|
||||
return PersonModelMapper.ensureInitialized()
|
||||
.encodeJson<PersonModel>(this as PersonModel);
|
||||
}
|
||||
|
||||
Map<String, dynamic> toMap() {
|
||||
return PersonModelMapper.ensureInitialized()
|
||||
.encodeMap<PersonModel>(this as PersonModel);
|
||||
}
|
||||
|
||||
PersonModelCopyWith<PersonModel, PersonModel, PersonModel> get copyWith =>
|
||||
_PersonModelCopyWithImpl(this as PersonModel, $identity, $identity);
|
||||
@override
|
||||
String toString() {
|
||||
return PersonModelMapper.ensureInitialized()
|
||||
.stringifyValue(this as PersonModel);
|
||||
}
|
||||
}
|
||||
|
||||
extension PersonModelValueCopy<$R, $Out>
|
||||
on ObjectCopyWith<$R, PersonModel, $Out> {
|
||||
PersonModelCopyWith<$R, PersonModel, $Out> get $asPersonModel =>
|
||||
$base.as((v, t, t2) => _PersonModelCopyWithImpl(v, t, t2));
|
||||
}
|
||||
|
||||
abstract class PersonModelCopyWith<$R, $In extends PersonModel, $Out>
|
||||
implements ItemBaseModelCopyWith<$R, $In, $Out> {
|
||||
ListCopyWith<$R, String, ObjectCopyWith<$R, String, String>> get birthPlace;
|
||||
ListCopyWith<$R, MovieModel, MovieModelCopyWith<$R, MovieModel, MovieModel>>
|
||||
get movies;
|
||||
ListCopyWith<$R, SeriesModel,
|
||||
SeriesModelCopyWith<$R, SeriesModel, SeriesModel>> get series;
|
||||
@override
|
||||
OverviewModelCopyWith<$R, OverviewModel, OverviewModel> get overview;
|
||||
@override
|
||||
UserDataCopyWith<$R, UserData, UserData> get userData;
|
||||
@override
|
||||
$R call(
|
||||
{DateTime? dateOfBirth,
|
||||
List<String>? birthPlace,
|
||||
List<MovieModel>? movies,
|
||||
List<SeriesModel>? series,
|
||||
String? name,
|
||||
String? id,
|
||||
OverviewModel? overview,
|
||||
String? parentId,
|
||||
String? playlistId,
|
||||
ImagesData? images,
|
||||
int? childCount,
|
||||
double? primaryRatio,
|
||||
UserData? userData,
|
||||
bool? canDownload,
|
||||
bool? canDelete,
|
||||
BaseItemKind? jellyType});
|
||||
PersonModelCopyWith<$R2, $In, $Out2> $chain<$R2, $Out2>(Then<$Out2, $R2> t);
|
||||
}
|
||||
|
||||
class _PersonModelCopyWithImpl<$R, $Out>
|
||||
extends ClassCopyWithBase<$R, PersonModel, $Out>
|
||||
implements PersonModelCopyWith<$R, PersonModel, $Out> {
|
||||
_PersonModelCopyWithImpl(super.value, super.then, super.then2);
|
||||
|
||||
@override
|
||||
late final ClassMapperBase<PersonModel> $mapper =
|
||||
PersonModelMapper.ensureInitialized();
|
||||
@override
|
||||
ListCopyWith<$R, String, ObjectCopyWith<$R, String, String>> get birthPlace =>
|
||||
ListCopyWith($value.birthPlace, (v, t) => ObjectCopyWith(v, $identity, t),
|
||||
(v) => call(birthPlace: v));
|
||||
@override
|
||||
ListCopyWith<$R, MovieModel, MovieModelCopyWith<$R, MovieModel, MovieModel>>
|
||||
get movies => ListCopyWith($value.movies, (v, t) => v.copyWith.$chain(t),
|
||||
(v) => call(movies: v));
|
||||
@override
|
||||
ListCopyWith<$R, SeriesModel,
|
||||
SeriesModelCopyWith<$R, SeriesModel, SeriesModel>>
|
||||
get series => ListCopyWith($value.series, (v, t) => v.copyWith.$chain(t),
|
||||
(v) => call(series: v));
|
||||
@override
|
||||
OverviewModelCopyWith<$R, OverviewModel, OverviewModel> get overview =>
|
||||
$value.overview.copyWith.$chain((v) => call(overview: v));
|
||||
@override
|
||||
UserDataCopyWith<$R, UserData, UserData> get userData =>
|
||||
$value.userData.copyWith.$chain((v) => call(userData: v));
|
||||
@override
|
||||
$R call(
|
||||
{Object? dateOfBirth = $none,
|
||||
List<String>? birthPlace,
|
||||
List<MovieModel>? movies,
|
||||
List<SeriesModel>? series,
|
||||
String? name,
|
||||
String? id,
|
||||
OverviewModel? overview,
|
||||
Object? parentId = $none,
|
||||
Object? playlistId = $none,
|
||||
Object? images = $none,
|
||||
Object? childCount = $none,
|
||||
Object? primaryRatio = $none,
|
||||
UserData? userData,
|
||||
Object? canDownload = $none,
|
||||
Object? canDelete = $none,
|
||||
Object? jellyType = $none}) =>
|
||||
$apply(FieldCopyWithData({
|
||||
if (dateOfBirth != $none) #dateOfBirth: dateOfBirth,
|
||||
if (birthPlace != null) #birthPlace: birthPlace,
|
||||
if (movies != null) #movies: movies,
|
||||
if (series != null) #series: series,
|
||||
if (name != null) #name: name,
|
||||
if (id != null) #id: id,
|
||||
if (overview != null) #overview: overview,
|
||||
if (parentId != $none) #parentId: parentId,
|
||||
if (playlistId != $none) #playlistId: playlistId,
|
||||
if (images != $none) #images: images,
|
||||
if (childCount != $none) #childCount: childCount,
|
||||
if (primaryRatio != $none) #primaryRatio: primaryRatio,
|
||||
if (userData != null) #userData: userData,
|
||||
if (canDownload != $none) #canDownload: canDownload,
|
||||
if (canDelete != $none) #canDelete: canDelete,
|
||||
if (jellyType != $none) #jellyType: jellyType
|
||||
}));
|
||||
@override
|
||||
PersonModel $make(CopyWithData data) => PersonModel(
|
||||
dateOfBirth: data.get(#dateOfBirth, or: $value.dateOfBirth),
|
||||
birthPlace: data.get(#birthPlace, or: $value.birthPlace),
|
||||
movies: data.get(#movies, or: $value.movies),
|
||||
series: data.get(#series, or: $value.series),
|
||||
name: data.get(#name, or: $value.name),
|
||||
id: data.get(#id, or: $value.id),
|
||||
overview: data.get(#overview, or: $value.overview),
|
||||
parentId: data.get(#parentId, or: $value.parentId),
|
||||
playlistId: data.get(#playlistId, or: $value.playlistId),
|
||||
images: data.get(#images, or: $value.images),
|
||||
childCount: data.get(#childCount, or: $value.childCount),
|
||||
primaryRatio: data.get(#primaryRatio, or: $value.primaryRatio),
|
||||
userData: data.get(#userData, or: $value.userData),
|
||||
canDownload: data.get(#canDownload, or: $value.canDownload),
|
||||
canDelete: data.get(#canDelete, or: $value.canDelete),
|
||||
jellyType: data.get(#jellyType, or: $value.jellyType));
|
||||
|
||||
@override
|
||||
PersonModelCopyWith<$R2, PersonModel, $Out2> $chain<$R2, $Out2>(
|
||||
Then<$Out2, $R2> t) =>
|
||||
_PersonModelCopyWithImpl($value, $cast, t);
|
||||
}
|
||||
154
lib/models/items/photos_model.dart
Normal file
154
lib/models/items/photos_model.dart
Normal file
|
|
@ -0,0 +1,154 @@
|
|||
// ignore_for_file: public_member_api_docs, sort_constructors_first
|
||||
import 'package:fladder/jellyfin/jellyfin_open_api.enums.swagger.dart';
|
||||
import 'package:fladder/util/localization_helper.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
|
||||
import 'package:fladder/jellyfin/jellyfin_open_api.swagger.dart' as dto;
|
||||
import 'package:fladder/models/item_base_model.dart';
|
||||
import 'package:fladder/models/items/images_models.dart';
|
||||
import 'package:fladder/models/items/item_shared_models.dart';
|
||||
import 'package:fladder/models/items/overview_model.dart';
|
||||
import 'package:fladder/providers/user_provider.dart';
|
||||
import 'package:fladder/screens/shared/fladder_snackbar.dart';
|
||||
import 'package:fladder/util/refresh_state.dart';
|
||||
|
||||
import 'package:dart_mappable/dart_mappable.dart';
|
||||
|
||||
part 'photos_model.mapper.dart';
|
||||
|
||||
@MappableClass()
|
||||
class PhotoAlbumModel extends ItemBaseModel with PhotoAlbumModelMappable {
|
||||
final List<ItemBaseModel> photos;
|
||||
|
||||
const PhotoAlbumModel({
|
||||
required this.photos,
|
||||
required super.name,
|
||||
required super.id,
|
||||
required super.overview,
|
||||
required super.parentId,
|
||||
required super.playlistId,
|
||||
required super.images,
|
||||
required super.childCount,
|
||||
required super.primaryRatio,
|
||||
required super.userData,
|
||||
super.canDelete,
|
||||
super.canDownload,
|
||||
super.jellyType,
|
||||
});
|
||||
@override
|
||||
bool get unWatched => userData.unPlayedItemCount != 0;
|
||||
|
||||
@override
|
||||
bool get playAble => true;
|
||||
|
||||
@override
|
||||
ItemBaseModel get parentBaseModel => copyWith(id: parentId);
|
||||
|
||||
factory PhotoAlbumModel.fromBaseDto(dto.BaseItemDto item, Ref ref) {
|
||||
return PhotoAlbumModel(
|
||||
name: item.name ?? "",
|
||||
id: item.id ?? "",
|
||||
childCount: item.childCount,
|
||||
overview: OverviewModel.fromBaseItemDto(item, ref),
|
||||
userData: UserData.fromDto(item.userData),
|
||||
parentId: item.parentId,
|
||||
playlistId: item.playlistItemId,
|
||||
images: ImagesData.fromBaseItem(item, ref),
|
||||
primaryRatio: item.primaryImageAspectRatio,
|
||||
canDelete: item.canDelete,
|
||||
canDownload: item.canDownload,
|
||||
photos: [],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@MappableClass()
|
||||
class PhotoModel extends ItemBaseModel with PhotoModelMappable {
|
||||
final String? albumId;
|
||||
final DateTime? dateTaken;
|
||||
final ImagesData? thumbnail;
|
||||
final FladderItemType internalType;
|
||||
|
||||
const PhotoModel({
|
||||
required this.albumId,
|
||||
required this.dateTaken,
|
||||
required this.thumbnail,
|
||||
required this.internalType,
|
||||
required super.name,
|
||||
required super.id,
|
||||
required super.overview,
|
||||
required super.parentId,
|
||||
required super.playlistId,
|
||||
required super.images,
|
||||
required super.childCount,
|
||||
required super.primaryRatio,
|
||||
required super.userData,
|
||||
required super.canDownload,
|
||||
required super.canDelete,
|
||||
super.jellyType,
|
||||
});
|
||||
|
||||
@override
|
||||
PhotoAlbumModel get parentBaseModel => PhotoAlbumModel(
|
||||
photos: [],
|
||||
name: "",
|
||||
id: parentId ?? "",
|
||||
overview: overview,
|
||||
parentId: parentId,
|
||||
playlistId: playlistId,
|
||||
images: images,
|
||||
childCount: childCount,
|
||||
primaryRatio: primaryRatio,
|
||||
userData: userData,
|
||||
);
|
||||
|
||||
@override
|
||||
ImagesData? get getPosters => thumbnail;
|
||||
|
||||
@override
|
||||
bool get galleryItem => switch (internalType) {
|
||||
FladderItemType.photo => albumId?.isNotEmpty == true,
|
||||
FladderItemType.video => parentId?.isNotEmpty == true,
|
||||
_ => false,
|
||||
};
|
||||
|
||||
@override
|
||||
bool get unWatched => false;
|
||||
|
||||
factory PhotoModel.fromBaseDto(dto.BaseItemDto item, Ref ref) {
|
||||
return PhotoModel(
|
||||
name: item.name ?? "",
|
||||
id: item.id ?? "",
|
||||
childCount: item.childCount,
|
||||
overview: OverviewModel.fromBaseItemDto(item, ref),
|
||||
userData: UserData.fromDto(item.userData),
|
||||
parentId: item.parentId,
|
||||
playlistId: item.playlistItemId,
|
||||
primaryRatio: double.tryParse(item.aspectRatio ?? "") ?? item.primaryImageAspectRatio ?? 1.0,
|
||||
dateTaken: item.dateCreated,
|
||||
albumId: item.albumId,
|
||||
thumbnail: ImagesData.fromBaseItem(item, ref),
|
||||
images: ImagesData.fromBaseItem(item, ref, getOriginalSize: true),
|
||||
canDelete: item.canDelete,
|
||||
canDownload: item.canDownload,
|
||||
internalType: switch (item.type) {
|
||||
BaseItemKind.video => FladderItemType.video,
|
||||
_ => FladderItemType.photo,
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
String downloadPath(WidgetRef ref) {
|
||||
return "${ref.read(userProvider)?.server}/Items/$id/Download?api_key=${ref.read(userProvider)?.credentials.token}";
|
||||
}
|
||||
|
||||
Future<void> navigateToAlbum(BuildContext context) async {
|
||||
if ((albumId ?? parentId) == null) {
|
||||
fladderSnackbar(context, title: context.localized.notPartOfAlbum);
|
||||
return;
|
||||
}
|
||||
await parentBaseModel.navigateTo(context);
|
||||
if (context.mounted) context.refreshData();
|
||||
}
|
||||
}
|
||||
498
lib/models/items/photos_model.mapper.dart
Normal file
498
lib/models/items/photos_model.mapper.dart
Normal file
|
|
@ -0,0 +1,498 @@
|
|||
// coverage:ignore-file
|
||||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
// ignore_for_file: type=lint
|
||||
// ignore_for_file: unused_element, unnecessary_cast, override_on_non_overriding_member
|
||||
// ignore_for_file: strict_raw_type, inference_failure_on_untyped_parameter
|
||||
|
||||
part of 'photos_model.dart';
|
||||
|
||||
class PhotoAlbumModelMapper extends SubClassMapperBase<PhotoAlbumModel> {
|
||||
PhotoAlbumModelMapper._();
|
||||
|
||||
static PhotoAlbumModelMapper? _instance;
|
||||
static PhotoAlbumModelMapper ensureInitialized() {
|
||||
if (_instance == null) {
|
||||
MapperContainer.globals.use(_instance = PhotoAlbumModelMapper._());
|
||||
ItemBaseModelMapper.ensureInitialized().addSubMapper(_instance!);
|
||||
ItemBaseModelMapper.ensureInitialized();
|
||||
OverviewModelMapper.ensureInitialized();
|
||||
UserDataMapper.ensureInitialized();
|
||||
}
|
||||
return _instance!;
|
||||
}
|
||||
|
||||
@override
|
||||
final String id = 'PhotoAlbumModel';
|
||||
|
||||
static List<ItemBaseModel> _$photos(PhotoAlbumModel v) => v.photos;
|
||||
static const Field<PhotoAlbumModel, List<ItemBaseModel>> _f$photos =
|
||||
Field('photos', _$photos);
|
||||
static String _$name(PhotoAlbumModel v) => v.name;
|
||||
static const Field<PhotoAlbumModel, String> _f$name = Field('name', _$name);
|
||||
static String _$id(PhotoAlbumModel v) => v.id;
|
||||
static const Field<PhotoAlbumModel, String> _f$id = Field('id', _$id);
|
||||
static OverviewModel _$overview(PhotoAlbumModel v) => v.overview;
|
||||
static const Field<PhotoAlbumModel, OverviewModel> _f$overview =
|
||||
Field('overview', _$overview);
|
||||
static String? _$parentId(PhotoAlbumModel v) => v.parentId;
|
||||
static const Field<PhotoAlbumModel, String> _f$parentId =
|
||||
Field('parentId', _$parentId);
|
||||
static String? _$playlistId(PhotoAlbumModel v) => v.playlistId;
|
||||
static const Field<PhotoAlbumModel, String> _f$playlistId =
|
||||
Field('playlistId', _$playlistId);
|
||||
static ImagesData? _$images(PhotoAlbumModel v) => v.images;
|
||||
static const Field<PhotoAlbumModel, ImagesData> _f$images =
|
||||
Field('images', _$images);
|
||||
static int? _$childCount(PhotoAlbumModel v) => v.childCount;
|
||||
static const Field<PhotoAlbumModel, int> _f$childCount =
|
||||
Field('childCount', _$childCount);
|
||||
static double? _$primaryRatio(PhotoAlbumModel v) => v.primaryRatio;
|
||||
static const Field<PhotoAlbumModel, double> _f$primaryRatio =
|
||||
Field('primaryRatio', _$primaryRatio);
|
||||
static UserData _$userData(PhotoAlbumModel v) => v.userData;
|
||||
static const Field<PhotoAlbumModel, UserData> _f$userData =
|
||||
Field('userData', _$userData);
|
||||
static bool? _$canDelete(PhotoAlbumModel v) => v.canDelete;
|
||||
static const Field<PhotoAlbumModel, bool> _f$canDelete =
|
||||
Field('canDelete', _$canDelete, opt: true);
|
||||
static bool? _$canDownload(PhotoAlbumModel v) => v.canDownload;
|
||||
static const Field<PhotoAlbumModel, bool> _f$canDownload =
|
||||
Field('canDownload', _$canDownload, opt: true);
|
||||
static dto.BaseItemKind? _$jellyType(PhotoAlbumModel v) => v.jellyType;
|
||||
static const Field<PhotoAlbumModel, dto.BaseItemKind> _f$jellyType =
|
||||
Field('jellyType', _$jellyType, opt: true);
|
||||
|
||||
@override
|
||||
final MappableFields<PhotoAlbumModel> fields = const {
|
||||
#photos: _f$photos,
|
||||
#name: _f$name,
|
||||
#id: _f$id,
|
||||
#overview: _f$overview,
|
||||
#parentId: _f$parentId,
|
||||
#playlistId: _f$playlistId,
|
||||
#images: _f$images,
|
||||
#childCount: _f$childCount,
|
||||
#primaryRatio: _f$primaryRatio,
|
||||
#userData: _f$userData,
|
||||
#canDelete: _f$canDelete,
|
||||
#canDownload: _f$canDownload,
|
||||
#jellyType: _f$jellyType,
|
||||
};
|
||||
@override
|
||||
final bool ignoreNull = true;
|
||||
|
||||
@override
|
||||
final String discriminatorKey = 'type';
|
||||
@override
|
||||
final dynamic discriminatorValue = 'PhotoAlbumModel';
|
||||
@override
|
||||
late final ClassMapperBase superMapper =
|
||||
ItemBaseModelMapper.ensureInitialized();
|
||||
|
||||
static PhotoAlbumModel _instantiate(DecodingData data) {
|
||||
return PhotoAlbumModel(
|
||||
photos: data.dec(_f$photos),
|
||||
name: data.dec(_f$name),
|
||||
id: data.dec(_f$id),
|
||||
overview: data.dec(_f$overview),
|
||||
parentId: data.dec(_f$parentId),
|
||||
playlistId: data.dec(_f$playlistId),
|
||||
images: data.dec(_f$images),
|
||||
childCount: data.dec(_f$childCount),
|
||||
primaryRatio: data.dec(_f$primaryRatio),
|
||||
userData: data.dec(_f$userData),
|
||||
canDelete: data.dec(_f$canDelete),
|
||||
canDownload: data.dec(_f$canDownload),
|
||||
jellyType: data.dec(_f$jellyType));
|
||||
}
|
||||
|
||||
@override
|
||||
final Function instantiate = _instantiate;
|
||||
|
||||
static PhotoAlbumModel fromMap(Map<String, dynamic> map) {
|
||||
return ensureInitialized().decodeMap<PhotoAlbumModel>(map);
|
||||
}
|
||||
|
||||
static PhotoAlbumModel fromJson(String json) {
|
||||
return ensureInitialized().decodeJson<PhotoAlbumModel>(json);
|
||||
}
|
||||
}
|
||||
|
||||
mixin PhotoAlbumModelMappable {
|
||||
String toJson() {
|
||||
return PhotoAlbumModelMapper.ensureInitialized()
|
||||
.encodeJson<PhotoAlbumModel>(this as PhotoAlbumModel);
|
||||
}
|
||||
|
||||
Map<String, dynamic> toMap() {
|
||||
return PhotoAlbumModelMapper.ensureInitialized()
|
||||
.encodeMap<PhotoAlbumModel>(this as PhotoAlbumModel);
|
||||
}
|
||||
|
||||
PhotoAlbumModelCopyWith<PhotoAlbumModel, PhotoAlbumModel, PhotoAlbumModel>
|
||||
get copyWith => _PhotoAlbumModelCopyWithImpl(
|
||||
this as PhotoAlbumModel, $identity, $identity);
|
||||
@override
|
||||
String toString() {
|
||||
return PhotoAlbumModelMapper.ensureInitialized()
|
||||
.stringifyValue(this as PhotoAlbumModel);
|
||||
}
|
||||
}
|
||||
|
||||
extension PhotoAlbumModelValueCopy<$R, $Out>
|
||||
on ObjectCopyWith<$R, PhotoAlbumModel, $Out> {
|
||||
PhotoAlbumModelCopyWith<$R, PhotoAlbumModel, $Out> get $asPhotoAlbumModel =>
|
||||
$base.as((v, t, t2) => _PhotoAlbumModelCopyWithImpl(v, t, t2));
|
||||
}
|
||||
|
||||
abstract class PhotoAlbumModelCopyWith<$R, $In extends PhotoAlbumModel, $Out>
|
||||
implements ItemBaseModelCopyWith<$R, $In, $Out> {
|
||||
ListCopyWith<$R, ItemBaseModel,
|
||||
ItemBaseModelCopyWith<$R, ItemBaseModel, ItemBaseModel>> get photos;
|
||||
@override
|
||||
OverviewModelCopyWith<$R, OverviewModel, OverviewModel> get overview;
|
||||
@override
|
||||
UserDataCopyWith<$R, UserData, UserData> get userData;
|
||||
@override
|
||||
$R call(
|
||||
{List<ItemBaseModel>? photos,
|
||||
String? name,
|
||||
String? id,
|
||||
OverviewModel? overview,
|
||||
String? parentId,
|
||||
String? playlistId,
|
||||
ImagesData? images,
|
||||
int? childCount,
|
||||
double? primaryRatio,
|
||||
UserData? userData,
|
||||
bool? canDelete,
|
||||
bool? canDownload,
|
||||
dto.BaseItemKind? jellyType});
|
||||
PhotoAlbumModelCopyWith<$R2, $In, $Out2> $chain<$R2, $Out2>(
|
||||
Then<$Out2, $R2> t);
|
||||
}
|
||||
|
||||
class _PhotoAlbumModelCopyWithImpl<$R, $Out>
|
||||
extends ClassCopyWithBase<$R, PhotoAlbumModel, $Out>
|
||||
implements PhotoAlbumModelCopyWith<$R, PhotoAlbumModel, $Out> {
|
||||
_PhotoAlbumModelCopyWithImpl(super.value, super.then, super.then2);
|
||||
|
||||
@override
|
||||
late final ClassMapperBase<PhotoAlbumModel> $mapper =
|
||||
PhotoAlbumModelMapper.ensureInitialized();
|
||||
@override
|
||||
ListCopyWith<$R, ItemBaseModel,
|
||||
ItemBaseModelCopyWith<$R, ItemBaseModel, ItemBaseModel>>
|
||||
get photos => ListCopyWith($value.photos, (v, t) => v.copyWith.$chain(t),
|
||||
(v) => call(photos: v));
|
||||
@override
|
||||
OverviewModelCopyWith<$R, OverviewModel, OverviewModel> get overview =>
|
||||
$value.overview.copyWith.$chain((v) => call(overview: v));
|
||||
@override
|
||||
UserDataCopyWith<$R, UserData, UserData> get userData =>
|
||||
$value.userData.copyWith.$chain((v) => call(userData: v));
|
||||
@override
|
||||
$R call(
|
||||
{List<ItemBaseModel>? photos,
|
||||
String? name,
|
||||
String? id,
|
||||
OverviewModel? overview,
|
||||
Object? parentId = $none,
|
||||
Object? playlistId = $none,
|
||||
Object? images = $none,
|
||||
Object? childCount = $none,
|
||||
Object? primaryRatio = $none,
|
||||
UserData? userData,
|
||||
Object? canDelete = $none,
|
||||
Object? canDownload = $none,
|
||||
Object? jellyType = $none}) =>
|
||||
$apply(FieldCopyWithData({
|
||||
if (photos != null) #photos: photos,
|
||||
if (name != null) #name: name,
|
||||
if (id != null) #id: id,
|
||||
if (overview != null) #overview: overview,
|
||||
if (parentId != $none) #parentId: parentId,
|
||||
if (playlistId != $none) #playlistId: playlistId,
|
||||
if (images != $none) #images: images,
|
||||
if (childCount != $none) #childCount: childCount,
|
||||
if (primaryRatio != $none) #primaryRatio: primaryRatio,
|
||||
if (userData != null) #userData: userData,
|
||||
if (canDelete != $none) #canDelete: canDelete,
|
||||
if (canDownload != $none) #canDownload: canDownload,
|
||||
if (jellyType != $none) #jellyType: jellyType
|
||||
}));
|
||||
@override
|
||||
PhotoAlbumModel $make(CopyWithData data) => PhotoAlbumModel(
|
||||
photos: data.get(#photos, or: $value.photos),
|
||||
name: data.get(#name, or: $value.name),
|
||||
id: data.get(#id, or: $value.id),
|
||||
overview: data.get(#overview, or: $value.overview),
|
||||
parentId: data.get(#parentId, or: $value.parentId),
|
||||
playlistId: data.get(#playlistId, or: $value.playlistId),
|
||||
images: data.get(#images, or: $value.images),
|
||||
childCount: data.get(#childCount, or: $value.childCount),
|
||||
primaryRatio: data.get(#primaryRatio, or: $value.primaryRatio),
|
||||
userData: data.get(#userData, or: $value.userData),
|
||||
canDelete: data.get(#canDelete, or: $value.canDelete),
|
||||
canDownload: data.get(#canDownload, or: $value.canDownload),
|
||||
jellyType: data.get(#jellyType, or: $value.jellyType));
|
||||
|
||||
@override
|
||||
PhotoAlbumModelCopyWith<$R2, PhotoAlbumModel, $Out2> $chain<$R2, $Out2>(
|
||||
Then<$Out2, $R2> t) =>
|
||||
_PhotoAlbumModelCopyWithImpl($value, $cast, t);
|
||||
}
|
||||
|
||||
class PhotoModelMapper extends SubClassMapperBase<PhotoModel> {
|
||||
PhotoModelMapper._();
|
||||
|
||||
static PhotoModelMapper? _instance;
|
||||
static PhotoModelMapper ensureInitialized() {
|
||||
if (_instance == null) {
|
||||
MapperContainer.globals.use(_instance = PhotoModelMapper._());
|
||||
ItemBaseModelMapper.ensureInitialized().addSubMapper(_instance!);
|
||||
OverviewModelMapper.ensureInitialized();
|
||||
UserDataMapper.ensureInitialized();
|
||||
}
|
||||
return _instance!;
|
||||
}
|
||||
|
||||
@override
|
||||
final String id = 'PhotoModel';
|
||||
|
||||
static String? _$albumId(PhotoModel v) => v.albumId;
|
||||
static const Field<PhotoModel, String> _f$albumId =
|
||||
Field('albumId', _$albumId);
|
||||
static DateTime? _$dateTaken(PhotoModel v) => v.dateTaken;
|
||||
static const Field<PhotoModel, DateTime> _f$dateTaken =
|
||||
Field('dateTaken', _$dateTaken);
|
||||
static ImagesData? _$thumbnail(PhotoModel v) => v.thumbnail;
|
||||
static const Field<PhotoModel, ImagesData> _f$thumbnail =
|
||||
Field('thumbnail', _$thumbnail);
|
||||
static FladderItemType _$internalType(PhotoModel v) => v.internalType;
|
||||
static const Field<PhotoModel, FladderItemType> _f$internalType =
|
||||
Field('internalType', _$internalType);
|
||||
static String _$name(PhotoModel v) => v.name;
|
||||
static const Field<PhotoModel, String> _f$name = Field('name', _$name);
|
||||
static String _$id(PhotoModel v) => v.id;
|
||||
static const Field<PhotoModel, String> _f$id = Field('id', _$id);
|
||||
static OverviewModel _$overview(PhotoModel v) => v.overview;
|
||||
static const Field<PhotoModel, OverviewModel> _f$overview =
|
||||
Field('overview', _$overview);
|
||||
static String? _$parentId(PhotoModel v) => v.parentId;
|
||||
static const Field<PhotoModel, String> _f$parentId =
|
||||
Field('parentId', _$parentId);
|
||||
static String? _$playlistId(PhotoModel v) => v.playlistId;
|
||||
static const Field<PhotoModel, String> _f$playlistId =
|
||||
Field('playlistId', _$playlistId);
|
||||
static ImagesData? _$images(PhotoModel v) => v.images;
|
||||
static const Field<PhotoModel, ImagesData> _f$images =
|
||||
Field('images', _$images);
|
||||
static int? _$childCount(PhotoModel v) => v.childCount;
|
||||
static const Field<PhotoModel, int> _f$childCount =
|
||||
Field('childCount', _$childCount);
|
||||
static double? _$primaryRatio(PhotoModel v) => v.primaryRatio;
|
||||
static const Field<PhotoModel, double> _f$primaryRatio =
|
||||
Field('primaryRatio', _$primaryRatio);
|
||||
static UserData _$userData(PhotoModel v) => v.userData;
|
||||
static const Field<PhotoModel, UserData> _f$userData =
|
||||
Field('userData', _$userData);
|
||||
static bool? _$canDownload(PhotoModel v) => v.canDownload;
|
||||
static const Field<PhotoModel, bool> _f$canDownload =
|
||||
Field('canDownload', _$canDownload);
|
||||
static bool? _$canDelete(PhotoModel v) => v.canDelete;
|
||||
static const Field<PhotoModel, bool> _f$canDelete =
|
||||
Field('canDelete', _$canDelete);
|
||||
static dto.BaseItemKind? _$jellyType(PhotoModel v) => v.jellyType;
|
||||
static const Field<PhotoModel, dto.BaseItemKind> _f$jellyType =
|
||||
Field('jellyType', _$jellyType, opt: true);
|
||||
|
||||
@override
|
||||
final MappableFields<PhotoModel> fields = const {
|
||||
#albumId: _f$albumId,
|
||||
#dateTaken: _f$dateTaken,
|
||||
#thumbnail: _f$thumbnail,
|
||||
#internalType: _f$internalType,
|
||||
#name: _f$name,
|
||||
#id: _f$id,
|
||||
#overview: _f$overview,
|
||||
#parentId: _f$parentId,
|
||||
#playlistId: _f$playlistId,
|
||||
#images: _f$images,
|
||||
#childCount: _f$childCount,
|
||||
#primaryRatio: _f$primaryRatio,
|
||||
#userData: _f$userData,
|
||||
#canDownload: _f$canDownload,
|
||||
#canDelete: _f$canDelete,
|
||||
#jellyType: _f$jellyType,
|
||||
};
|
||||
@override
|
||||
final bool ignoreNull = true;
|
||||
|
||||
@override
|
||||
final String discriminatorKey = 'type';
|
||||
@override
|
||||
final dynamic discriminatorValue = 'PhotoModel';
|
||||
@override
|
||||
late final ClassMapperBase superMapper =
|
||||
ItemBaseModelMapper.ensureInitialized();
|
||||
|
||||
static PhotoModel _instantiate(DecodingData data) {
|
||||
return PhotoModel(
|
||||
albumId: data.dec(_f$albumId),
|
||||
dateTaken: data.dec(_f$dateTaken),
|
||||
thumbnail: data.dec(_f$thumbnail),
|
||||
internalType: data.dec(_f$internalType),
|
||||
name: data.dec(_f$name),
|
||||
id: data.dec(_f$id),
|
||||
overview: data.dec(_f$overview),
|
||||
parentId: data.dec(_f$parentId),
|
||||
playlistId: data.dec(_f$playlistId),
|
||||
images: data.dec(_f$images),
|
||||
childCount: data.dec(_f$childCount),
|
||||
primaryRatio: data.dec(_f$primaryRatio),
|
||||
userData: data.dec(_f$userData),
|
||||
canDownload: data.dec(_f$canDownload),
|
||||
canDelete: data.dec(_f$canDelete),
|
||||
jellyType: data.dec(_f$jellyType));
|
||||
}
|
||||
|
||||
@override
|
||||
final Function instantiate = _instantiate;
|
||||
|
||||
static PhotoModel fromMap(Map<String, dynamic> map) {
|
||||
return ensureInitialized().decodeMap<PhotoModel>(map);
|
||||
}
|
||||
|
||||
static PhotoModel fromJson(String json) {
|
||||
return ensureInitialized().decodeJson<PhotoModel>(json);
|
||||
}
|
||||
}
|
||||
|
||||
mixin PhotoModelMappable {
|
||||
String toJson() {
|
||||
return PhotoModelMapper.ensureInitialized()
|
||||
.encodeJson<PhotoModel>(this as PhotoModel);
|
||||
}
|
||||
|
||||
Map<String, dynamic> toMap() {
|
||||
return PhotoModelMapper.ensureInitialized()
|
||||
.encodeMap<PhotoModel>(this as PhotoModel);
|
||||
}
|
||||
|
||||
PhotoModelCopyWith<PhotoModel, PhotoModel, PhotoModel> get copyWith =>
|
||||
_PhotoModelCopyWithImpl(this as PhotoModel, $identity, $identity);
|
||||
@override
|
||||
String toString() {
|
||||
return PhotoModelMapper.ensureInitialized()
|
||||
.stringifyValue(this as PhotoModel);
|
||||
}
|
||||
}
|
||||
|
||||
extension PhotoModelValueCopy<$R, $Out>
|
||||
on ObjectCopyWith<$R, PhotoModel, $Out> {
|
||||
PhotoModelCopyWith<$R, PhotoModel, $Out> get $asPhotoModel =>
|
||||
$base.as((v, t, t2) => _PhotoModelCopyWithImpl(v, t, t2));
|
||||
}
|
||||
|
||||
abstract class PhotoModelCopyWith<$R, $In extends PhotoModel, $Out>
|
||||
implements ItemBaseModelCopyWith<$R, $In, $Out> {
|
||||
@override
|
||||
OverviewModelCopyWith<$R, OverviewModel, OverviewModel> get overview;
|
||||
@override
|
||||
UserDataCopyWith<$R, UserData, UserData> get userData;
|
||||
@override
|
||||
$R call(
|
||||
{String? albumId,
|
||||
DateTime? dateTaken,
|
||||
ImagesData? thumbnail,
|
||||
FladderItemType? internalType,
|
||||
String? name,
|
||||
String? id,
|
||||
OverviewModel? overview,
|
||||
String? parentId,
|
||||
String? playlistId,
|
||||
ImagesData? images,
|
||||
int? childCount,
|
||||
double? primaryRatio,
|
||||
UserData? userData,
|
||||
bool? canDownload,
|
||||
bool? canDelete,
|
||||
dto.BaseItemKind? jellyType});
|
||||
PhotoModelCopyWith<$R2, $In, $Out2> $chain<$R2, $Out2>(Then<$Out2, $R2> t);
|
||||
}
|
||||
|
||||
class _PhotoModelCopyWithImpl<$R, $Out>
|
||||
extends ClassCopyWithBase<$R, PhotoModel, $Out>
|
||||
implements PhotoModelCopyWith<$R, PhotoModel, $Out> {
|
||||
_PhotoModelCopyWithImpl(super.value, super.then, super.then2);
|
||||
|
||||
@override
|
||||
late final ClassMapperBase<PhotoModel> $mapper =
|
||||
PhotoModelMapper.ensureInitialized();
|
||||
@override
|
||||
OverviewModelCopyWith<$R, OverviewModel, OverviewModel> get overview =>
|
||||
$value.overview.copyWith.$chain((v) => call(overview: v));
|
||||
@override
|
||||
UserDataCopyWith<$R, UserData, UserData> get userData =>
|
||||
$value.userData.copyWith.$chain((v) => call(userData: v));
|
||||
@override
|
||||
$R call(
|
||||
{Object? albumId = $none,
|
||||
Object? dateTaken = $none,
|
||||
Object? thumbnail = $none,
|
||||
FladderItemType? internalType,
|
||||
String? name,
|
||||
String? id,
|
||||
OverviewModel? overview,
|
||||
Object? parentId = $none,
|
||||
Object? playlistId = $none,
|
||||
Object? images = $none,
|
||||
Object? childCount = $none,
|
||||
Object? primaryRatio = $none,
|
||||
UserData? userData,
|
||||
Object? canDownload = $none,
|
||||
Object? canDelete = $none,
|
||||
Object? jellyType = $none}) =>
|
||||
$apply(FieldCopyWithData({
|
||||
if (albumId != $none) #albumId: albumId,
|
||||
if (dateTaken != $none) #dateTaken: dateTaken,
|
||||
if (thumbnail != $none) #thumbnail: thumbnail,
|
||||
if (internalType != null) #internalType: internalType,
|
||||
if (name != null) #name: name,
|
||||
if (id != null) #id: id,
|
||||
if (overview != null) #overview: overview,
|
||||
if (parentId != $none) #parentId: parentId,
|
||||
if (playlistId != $none) #playlistId: playlistId,
|
||||
if (images != $none) #images: images,
|
||||
if (childCount != $none) #childCount: childCount,
|
||||
if (primaryRatio != $none) #primaryRatio: primaryRatio,
|
||||
if (userData != null) #userData: userData,
|
||||
if (canDownload != $none) #canDownload: canDownload,
|
||||
if (canDelete != $none) #canDelete: canDelete,
|
||||
if (jellyType != $none) #jellyType: jellyType
|
||||
}));
|
||||
@override
|
||||
PhotoModel $make(CopyWithData data) => PhotoModel(
|
||||
albumId: data.get(#albumId, or: $value.albumId),
|
||||
dateTaken: data.get(#dateTaken, or: $value.dateTaken),
|
||||
thumbnail: data.get(#thumbnail, or: $value.thumbnail),
|
||||
internalType: data.get(#internalType, or: $value.internalType),
|
||||
name: data.get(#name, or: $value.name),
|
||||
id: data.get(#id, or: $value.id),
|
||||
overview: data.get(#overview, or: $value.overview),
|
||||
parentId: data.get(#parentId, or: $value.parentId),
|
||||
playlistId: data.get(#playlistId, or: $value.playlistId),
|
||||
images: data.get(#images, or: $value.images),
|
||||
childCount: data.get(#childCount, or: $value.childCount),
|
||||
primaryRatio: data.get(#primaryRatio, or: $value.primaryRatio),
|
||||
userData: data.get(#userData, or: $value.userData),
|
||||
canDownload: data.get(#canDownload, or: $value.canDownload),
|
||||
canDelete: data.get(#canDelete, or: $value.canDelete),
|
||||
jellyType: data.get(#jellyType, or: $value.jellyType));
|
||||
|
||||
@override
|
||||
PhotoModelCopyWith<$R2, PhotoModel, $Out2> $chain<$R2, $Out2>(
|
||||
Then<$Out2, $R2> t) =>
|
||||
_PhotoModelCopyWithImpl($value, $cast, t);
|
||||
}
|
||||
98
lib/models/items/season_model.dart
Normal file
98
lib/models/items/season_model.dart
Normal file
|
|
@ -0,0 +1,98 @@
|
|||
// ignore_for_file: public_member_api_docs, sort_constructors_first
|
||||
|
||||
import 'package:collection/collection.dart';
|
||||
import 'package:fladder/models/items/overview_model.dart';
|
||||
import 'package:fladder/models/items/series_model.dart';
|
||||
import 'package:fladder/util/localization_helper.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
|
||||
import 'package:fladder/jellyfin/jellyfin_open_api.swagger.dart' as dto;
|
||||
import 'package:fladder/models/item_base_model.dart';
|
||||
import 'package:fladder/models/items/episode_model.dart';
|
||||
import 'package:fladder/models/items/images_models.dart';
|
||||
import 'package:fladder/models/items/item_shared_models.dart';
|
||||
|
||||
import 'package:dart_mappable/dart_mappable.dart';
|
||||
|
||||
part 'season_model.mapper.dart';
|
||||
|
||||
@MappableClass()
|
||||
class SeasonModel extends ItemBaseModel with SeasonModelMappable {
|
||||
final ImagesData? parentImages;
|
||||
final String seasonName;
|
||||
final List<EpisodeModel> episodes;
|
||||
final int episodeCount;
|
||||
final String seriesId;
|
||||
final String seriesName;
|
||||
const SeasonModel({
|
||||
required this.parentImages,
|
||||
required this.seasonName,
|
||||
this.episodes = const [],
|
||||
required this.episodeCount,
|
||||
required this.seriesId,
|
||||
required this.seriesName,
|
||||
required super.name,
|
||||
required super.id,
|
||||
required super.overview,
|
||||
required super.parentId,
|
||||
required super.playlistId,
|
||||
required super.images,
|
||||
required super.childCount,
|
||||
required super.primaryRatio,
|
||||
required super.userData,
|
||||
required super.canDelete,
|
||||
required super.canDownload,
|
||||
super.jellyType,
|
||||
});
|
||||
factory SeasonModel.fromBaseDto(dto.BaseItemDto item, Ref ref) {
|
||||
return SeasonModel(
|
||||
name: item.name ?? "",
|
||||
id: item.id ?? "",
|
||||
childCount: item.childCount,
|
||||
overview: OverviewModel.fromBaseItemDto(item, ref),
|
||||
userData: UserData.fromDto(item.userData),
|
||||
parentId: item.seasonId ?? item.parentId,
|
||||
playlistId: item.playlistItemId,
|
||||
images: ImagesData.fromBaseItem(item, ref),
|
||||
primaryRatio: item.primaryImageAspectRatio,
|
||||
seasonName: item.seasonName ?? "",
|
||||
episodeCount: item.episodeCount ?? 0,
|
||||
parentImages: ImagesData.fromBaseItemParent(item, ref, primary: const Size(2000, 2000)),
|
||||
seriesId: item.seriesId ?? item.parentId ?? item.id ?? "",
|
||||
canDelete: item.canDelete,
|
||||
canDownload: item.canDownload,
|
||||
seriesName: item.seriesName ?? "",
|
||||
);
|
||||
}
|
||||
|
||||
EpisodeModel? get nextUp {
|
||||
return episodes.lastWhereOrNull((element) => element.userData.progress > 0) ??
|
||||
episodes.firstWhereOrNull((element) => element.userData.played == false);
|
||||
}
|
||||
|
||||
@override
|
||||
ImagesData? get getPosters => images ?? parentImages;
|
||||
|
||||
String localizedName(BuildContext context) => name.replaceFirst("Season", context.localized.season(1));
|
||||
|
||||
@override
|
||||
SeriesModel get parentBaseModel => SeriesModel(
|
||||
originalTitle: '',
|
||||
sortName: '',
|
||||
status: "",
|
||||
name: seriesName,
|
||||
id: parentId ?? "",
|
||||
playlistId: playlistId,
|
||||
overview: overview,
|
||||
parentId: parentId,
|
||||
images: images,
|
||||
childCount: childCount,
|
||||
primaryRatio: primaryRatio,
|
||||
userData: UserData(),
|
||||
);
|
||||
|
||||
static List<SeasonModel> seasonsFromDto(List<dto.BaseItemDto>? dto, Ref ref) {
|
||||
return dto?.map((e) => SeasonModel.fromBaseDto(e, ref)).toList() ?? [];
|
||||
}
|
||||
}
|
||||
287
lib/models/items/season_model.mapper.dart
Normal file
287
lib/models/items/season_model.mapper.dart
Normal file
|
|
@ -0,0 +1,287 @@
|
|||
// coverage:ignore-file
|
||||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
// ignore_for_file: type=lint
|
||||
// ignore_for_file: unused_element, unnecessary_cast, override_on_non_overriding_member
|
||||
// ignore_for_file: strict_raw_type, inference_failure_on_untyped_parameter
|
||||
|
||||
part of 'season_model.dart';
|
||||
|
||||
class SeasonModelMapper extends SubClassMapperBase<SeasonModel> {
|
||||
SeasonModelMapper._();
|
||||
|
||||
static SeasonModelMapper? _instance;
|
||||
static SeasonModelMapper ensureInitialized() {
|
||||
if (_instance == null) {
|
||||
MapperContainer.globals.use(_instance = SeasonModelMapper._());
|
||||
ItemBaseModelMapper.ensureInitialized().addSubMapper(_instance!);
|
||||
EpisodeModelMapper.ensureInitialized();
|
||||
OverviewModelMapper.ensureInitialized();
|
||||
UserDataMapper.ensureInitialized();
|
||||
}
|
||||
return _instance!;
|
||||
}
|
||||
|
||||
@override
|
||||
final String id = 'SeasonModel';
|
||||
|
||||
static ImagesData? _$parentImages(SeasonModel v) => v.parentImages;
|
||||
static const Field<SeasonModel, ImagesData> _f$parentImages =
|
||||
Field('parentImages', _$parentImages);
|
||||
static String _$seasonName(SeasonModel v) => v.seasonName;
|
||||
static const Field<SeasonModel, String> _f$seasonName =
|
||||
Field('seasonName', _$seasonName);
|
||||
static List<EpisodeModel> _$episodes(SeasonModel v) => v.episodes;
|
||||
static const Field<SeasonModel, List<EpisodeModel>> _f$episodes =
|
||||
Field('episodes', _$episodes, opt: true, def: const []);
|
||||
static int _$episodeCount(SeasonModel v) => v.episodeCount;
|
||||
static const Field<SeasonModel, int> _f$episodeCount =
|
||||
Field('episodeCount', _$episodeCount);
|
||||
static String _$seriesId(SeasonModel v) => v.seriesId;
|
||||
static const Field<SeasonModel, String> _f$seriesId =
|
||||
Field('seriesId', _$seriesId);
|
||||
static String _$seriesName(SeasonModel v) => v.seriesName;
|
||||
static const Field<SeasonModel, String> _f$seriesName =
|
||||
Field('seriesName', _$seriesName);
|
||||
static String _$name(SeasonModel v) => v.name;
|
||||
static const Field<SeasonModel, String> _f$name = Field('name', _$name);
|
||||
static String _$id(SeasonModel v) => v.id;
|
||||
static const Field<SeasonModel, String> _f$id = Field('id', _$id);
|
||||
static OverviewModel _$overview(SeasonModel v) => v.overview;
|
||||
static const Field<SeasonModel, OverviewModel> _f$overview =
|
||||
Field('overview', _$overview);
|
||||
static String? _$parentId(SeasonModel v) => v.parentId;
|
||||
static const Field<SeasonModel, String> _f$parentId =
|
||||
Field('parentId', _$parentId);
|
||||
static String? _$playlistId(SeasonModel v) => v.playlistId;
|
||||
static const Field<SeasonModel, String> _f$playlistId =
|
||||
Field('playlistId', _$playlistId);
|
||||
static ImagesData? _$images(SeasonModel v) => v.images;
|
||||
static const Field<SeasonModel, ImagesData> _f$images =
|
||||
Field('images', _$images);
|
||||
static int? _$childCount(SeasonModel v) => v.childCount;
|
||||
static const Field<SeasonModel, int> _f$childCount =
|
||||
Field('childCount', _$childCount);
|
||||
static double? _$primaryRatio(SeasonModel v) => v.primaryRatio;
|
||||
static const Field<SeasonModel, double> _f$primaryRatio =
|
||||
Field('primaryRatio', _$primaryRatio);
|
||||
static UserData _$userData(SeasonModel v) => v.userData;
|
||||
static const Field<SeasonModel, UserData> _f$userData =
|
||||
Field('userData', _$userData);
|
||||
static bool? _$canDelete(SeasonModel v) => v.canDelete;
|
||||
static const Field<SeasonModel, bool> _f$canDelete =
|
||||
Field('canDelete', _$canDelete);
|
||||
static bool? _$canDownload(SeasonModel v) => v.canDownload;
|
||||
static const Field<SeasonModel, bool> _f$canDownload =
|
||||
Field('canDownload', _$canDownload);
|
||||
static dto.BaseItemKind? _$jellyType(SeasonModel v) => v.jellyType;
|
||||
static const Field<SeasonModel, dto.BaseItemKind> _f$jellyType =
|
||||
Field('jellyType', _$jellyType, opt: true);
|
||||
|
||||
@override
|
||||
final MappableFields<SeasonModel> fields = const {
|
||||
#parentImages: _f$parentImages,
|
||||
#seasonName: _f$seasonName,
|
||||
#episodes: _f$episodes,
|
||||
#episodeCount: _f$episodeCount,
|
||||
#seriesId: _f$seriesId,
|
||||
#seriesName: _f$seriesName,
|
||||
#name: _f$name,
|
||||
#id: _f$id,
|
||||
#overview: _f$overview,
|
||||
#parentId: _f$parentId,
|
||||
#playlistId: _f$playlistId,
|
||||
#images: _f$images,
|
||||
#childCount: _f$childCount,
|
||||
#primaryRatio: _f$primaryRatio,
|
||||
#userData: _f$userData,
|
||||
#canDelete: _f$canDelete,
|
||||
#canDownload: _f$canDownload,
|
||||
#jellyType: _f$jellyType,
|
||||
};
|
||||
@override
|
||||
final bool ignoreNull = true;
|
||||
|
||||
@override
|
||||
final String discriminatorKey = 'type';
|
||||
@override
|
||||
final dynamic discriminatorValue = 'SeasonModel';
|
||||
@override
|
||||
late final ClassMapperBase superMapper =
|
||||
ItemBaseModelMapper.ensureInitialized();
|
||||
|
||||
static SeasonModel _instantiate(DecodingData data) {
|
||||
return SeasonModel(
|
||||
parentImages: data.dec(_f$parentImages),
|
||||
seasonName: data.dec(_f$seasonName),
|
||||
episodes: data.dec(_f$episodes),
|
||||
episodeCount: data.dec(_f$episodeCount),
|
||||
seriesId: data.dec(_f$seriesId),
|
||||
seriesName: data.dec(_f$seriesName),
|
||||
name: data.dec(_f$name),
|
||||
id: data.dec(_f$id),
|
||||
overview: data.dec(_f$overview),
|
||||
parentId: data.dec(_f$parentId),
|
||||
playlistId: data.dec(_f$playlistId),
|
||||
images: data.dec(_f$images),
|
||||
childCount: data.dec(_f$childCount),
|
||||
primaryRatio: data.dec(_f$primaryRatio),
|
||||
userData: data.dec(_f$userData),
|
||||
canDelete: data.dec(_f$canDelete),
|
||||
canDownload: data.dec(_f$canDownload),
|
||||
jellyType: data.dec(_f$jellyType));
|
||||
}
|
||||
|
||||
@override
|
||||
final Function instantiate = _instantiate;
|
||||
|
||||
static SeasonModel fromMap(Map<String, dynamic> map) {
|
||||
return ensureInitialized().decodeMap<SeasonModel>(map);
|
||||
}
|
||||
|
||||
static SeasonModel fromJson(String json) {
|
||||
return ensureInitialized().decodeJson<SeasonModel>(json);
|
||||
}
|
||||
}
|
||||
|
||||
mixin SeasonModelMappable {
|
||||
String toJson() {
|
||||
return SeasonModelMapper.ensureInitialized()
|
||||
.encodeJson<SeasonModel>(this as SeasonModel);
|
||||
}
|
||||
|
||||
Map<String, dynamic> toMap() {
|
||||
return SeasonModelMapper.ensureInitialized()
|
||||
.encodeMap<SeasonModel>(this as SeasonModel);
|
||||
}
|
||||
|
||||
SeasonModelCopyWith<SeasonModel, SeasonModel, SeasonModel> get copyWith =>
|
||||
_SeasonModelCopyWithImpl(this as SeasonModel, $identity, $identity);
|
||||
@override
|
||||
String toString() {
|
||||
return SeasonModelMapper.ensureInitialized()
|
||||
.stringifyValue(this as SeasonModel);
|
||||
}
|
||||
}
|
||||
|
||||
extension SeasonModelValueCopy<$R, $Out>
|
||||
on ObjectCopyWith<$R, SeasonModel, $Out> {
|
||||
SeasonModelCopyWith<$R, SeasonModel, $Out> get $asSeasonModel =>
|
||||
$base.as((v, t, t2) => _SeasonModelCopyWithImpl(v, t, t2));
|
||||
}
|
||||
|
||||
abstract class SeasonModelCopyWith<$R, $In extends SeasonModel, $Out>
|
||||
implements ItemBaseModelCopyWith<$R, $In, $Out> {
|
||||
ListCopyWith<$R, EpisodeModel,
|
||||
EpisodeModelCopyWith<$R, EpisodeModel, EpisodeModel>> get episodes;
|
||||
@override
|
||||
OverviewModelCopyWith<$R, OverviewModel, OverviewModel> get overview;
|
||||
@override
|
||||
UserDataCopyWith<$R, UserData, UserData> get userData;
|
||||
@override
|
||||
$R call(
|
||||
{ImagesData? parentImages,
|
||||
String? seasonName,
|
||||
List<EpisodeModel>? episodes,
|
||||
int? episodeCount,
|
||||
String? seriesId,
|
||||
String? seriesName,
|
||||
String? name,
|
||||
String? id,
|
||||
OverviewModel? overview,
|
||||
String? parentId,
|
||||
String? playlistId,
|
||||
ImagesData? images,
|
||||
int? childCount,
|
||||
double? primaryRatio,
|
||||
UserData? userData,
|
||||
bool? canDelete,
|
||||
bool? canDownload,
|
||||
dto.BaseItemKind? jellyType});
|
||||
SeasonModelCopyWith<$R2, $In, $Out2> $chain<$R2, $Out2>(Then<$Out2, $R2> t);
|
||||
}
|
||||
|
||||
class _SeasonModelCopyWithImpl<$R, $Out>
|
||||
extends ClassCopyWithBase<$R, SeasonModel, $Out>
|
||||
implements SeasonModelCopyWith<$R, SeasonModel, $Out> {
|
||||
_SeasonModelCopyWithImpl(super.value, super.then, super.then2);
|
||||
|
||||
@override
|
||||
late final ClassMapperBase<SeasonModel> $mapper =
|
||||
SeasonModelMapper.ensureInitialized();
|
||||
@override
|
||||
ListCopyWith<$R, EpisodeModel,
|
||||
EpisodeModelCopyWith<$R, EpisodeModel, EpisodeModel>>
|
||||
get episodes => ListCopyWith($value.episodes,
|
||||
(v, t) => v.copyWith.$chain(t), (v) => call(episodes: v));
|
||||
@override
|
||||
OverviewModelCopyWith<$R, OverviewModel, OverviewModel> get overview =>
|
||||
$value.overview.copyWith.$chain((v) => call(overview: v));
|
||||
@override
|
||||
UserDataCopyWith<$R, UserData, UserData> get userData =>
|
||||
$value.userData.copyWith.$chain((v) => call(userData: v));
|
||||
@override
|
||||
$R call(
|
||||
{Object? parentImages = $none,
|
||||
String? seasonName,
|
||||
List<EpisodeModel>? episodes,
|
||||
int? episodeCount,
|
||||
String? seriesId,
|
||||
String? seriesName,
|
||||
String? name,
|
||||
String? id,
|
||||
OverviewModel? overview,
|
||||
Object? parentId = $none,
|
||||
Object? playlistId = $none,
|
||||
Object? images = $none,
|
||||
Object? childCount = $none,
|
||||
Object? primaryRatio = $none,
|
||||
UserData? userData,
|
||||
Object? canDelete = $none,
|
||||
Object? canDownload = $none,
|
||||
Object? jellyType = $none}) =>
|
||||
$apply(FieldCopyWithData({
|
||||
if (parentImages != $none) #parentImages: parentImages,
|
||||
if (seasonName != null) #seasonName: seasonName,
|
||||
if (episodes != null) #episodes: episodes,
|
||||
if (episodeCount != null) #episodeCount: episodeCount,
|
||||
if (seriesId != null) #seriesId: seriesId,
|
||||
if (seriesName != null) #seriesName: seriesName,
|
||||
if (name != null) #name: name,
|
||||
if (id != null) #id: id,
|
||||
if (overview != null) #overview: overview,
|
||||
if (parentId != $none) #parentId: parentId,
|
||||
if (playlistId != $none) #playlistId: playlistId,
|
||||
if (images != $none) #images: images,
|
||||
if (childCount != $none) #childCount: childCount,
|
||||
if (primaryRatio != $none) #primaryRatio: primaryRatio,
|
||||
if (userData != null) #userData: userData,
|
||||
if (canDelete != $none) #canDelete: canDelete,
|
||||
if (canDownload != $none) #canDownload: canDownload,
|
||||
if (jellyType != $none) #jellyType: jellyType
|
||||
}));
|
||||
@override
|
||||
SeasonModel $make(CopyWithData data) => SeasonModel(
|
||||
parentImages: data.get(#parentImages, or: $value.parentImages),
|
||||
seasonName: data.get(#seasonName, or: $value.seasonName),
|
||||
episodes: data.get(#episodes, or: $value.episodes),
|
||||
episodeCount: data.get(#episodeCount, or: $value.episodeCount),
|
||||
seriesId: data.get(#seriesId, or: $value.seriesId),
|
||||
seriesName: data.get(#seriesName, or: $value.seriesName),
|
||||
name: data.get(#name, or: $value.name),
|
||||
id: data.get(#id, or: $value.id),
|
||||
overview: data.get(#overview, or: $value.overview),
|
||||
parentId: data.get(#parentId, or: $value.parentId),
|
||||
playlistId: data.get(#playlistId, or: $value.playlistId),
|
||||
images: data.get(#images, or: $value.images),
|
||||
childCount: data.get(#childCount, or: $value.childCount),
|
||||
primaryRatio: data.get(#primaryRatio, or: $value.primaryRatio),
|
||||
userData: data.get(#userData, or: $value.userData),
|
||||
canDelete: data.get(#canDelete, or: $value.canDelete),
|
||||
canDownload: data.get(#canDownload, or: $value.canDownload),
|
||||
jellyType: data.get(#jellyType, or: $value.jellyType));
|
||||
|
||||
@override
|
||||
SeasonModelCopyWith<$R2, SeasonModel, $Out2> $chain<$R2, $Out2>(
|
||||
Then<$Out2, $R2> t) =>
|
||||
_SeasonModelCopyWithImpl($value, $cast, t);
|
||||
}
|
||||
96
lib/models/items/series_model.dart
Normal file
96
lib/models/items/series_model.dart
Normal file
|
|
@ -0,0 +1,96 @@
|
|||
import 'package:fladder/screens/details_screens/series_detail_screen.dart';
|
||||
import 'package:flutter/widgets.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
|
||||
import 'package:fladder/jellyfin/jellyfin_open_api.swagger.dart' as dto;
|
||||
import 'package:fladder/models/item_base_model.dart';
|
||||
import 'package:fladder/models/items/episode_model.dart';
|
||||
import 'package:fladder/models/items/images_models.dart';
|
||||
import 'package:fladder/models/items/item_shared_models.dart';
|
||||
import 'package:fladder/models/items/overview_model.dart';
|
||||
import 'package:fladder/models/items/season_model.dart';
|
||||
|
||||
import 'package:dart_mappable/dart_mappable.dart';
|
||||
|
||||
part 'series_model.mapper.dart';
|
||||
|
||||
@MappableClass()
|
||||
class SeriesModel extends ItemBaseModel with SeriesModelMappable {
|
||||
final List<EpisodeModel>? availableEpisodes;
|
||||
final List<SeasonModel>? seasons;
|
||||
final String originalTitle;
|
||||
final String sortName;
|
||||
final String status;
|
||||
final List<ItemBaseModel> related;
|
||||
const SeriesModel({
|
||||
this.availableEpisodes,
|
||||
this.seasons,
|
||||
required this.originalTitle,
|
||||
required this.sortName,
|
||||
required this.status,
|
||||
this.related = const [],
|
||||
required super.name,
|
||||
required super.id,
|
||||
required super.overview,
|
||||
required super.parentId,
|
||||
required super.playlistId,
|
||||
required super.images,
|
||||
required super.childCount,
|
||||
required super.primaryRatio,
|
||||
required super.userData,
|
||||
super.canDownload,
|
||||
super.canDelete,
|
||||
super.jellyType,
|
||||
});
|
||||
|
||||
EpisodeModel? get nextUp => availableEpisodes?.nextUp ?? availableEpisodes?.firstOrNull;
|
||||
|
||||
@override
|
||||
String detailedName(BuildContext context) => name;
|
||||
|
||||
@override
|
||||
ItemBaseModel get parentBaseModel => this;
|
||||
|
||||
@override
|
||||
Widget get detailScreenWidget => SeriesDetailScreen(item: this);
|
||||
|
||||
@override
|
||||
bool get emptyShow => childCount == 0;
|
||||
|
||||
@override
|
||||
bool get playAble => userData.unPlayedItemCount != 0;
|
||||
|
||||
@override
|
||||
bool get identifiable => true;
|
||||
|
||||
@override
|
||||
bool get unWatched =>
|
||||
!userData.played && userData.progress <= 0 && userData.unPlayedItemCount == 0 && childCount != 0;
|
||||
|
||||
@override
|
||||
String get subText => overview.yearAired?.toString() ?? "";
|
||||
|
||||
List<ItemBaseModel> fetchAllShows() {
|
||||
return availableEpisodes?.map((e) => e).toList() ?? [];
|
||||
}
|
||||
|
||||
@override
|
||||
bool get syncAble => true;
|
||||
|
||||
factory SeriesModel.fromBaseDto(dto.BaseItemDto item, Ref ref) => SeriesModel(
|
||||
name: item.name ?? "",
|
||||
id: item.id ?? "",
|
||||
childCount: item.childCount,
|
||||
overview: OverviewModel.fromBaseItemDto(item, ref),
|
||||
userData: UserData.fromDto(item.userData),
|
||||
parentId: item.parentId,
|
||||
playlistId: item.playlistItemId,
|
||||
images: ImagesData.fromBaseItem(item, ref, getOriginalSize: true),
|
||||
primaryRatio: item.primaryImageAspectRatio,
|
||||
originalTitle: item.originalTitle ?? "",
|
||||
sortName: item.sortName ?? "",
|
||||
canDelete: item.canDelete,
|
||||
canDownload: item.canDownload,
|
||||
status: item.status ?? "Continuing",
|
||||
);
|
||||
}
|
||||
309
lib/models/items/series_model.mapper.dart
Normal file
309
lib/models/items/series_model.mapper.dart
Normal file
|
|
@ -0,0 +1,309 @@
|
|||
// coverage:ignore-file
|
||||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
// ignore_for_file: type=lint
|
||||
// ignore_for_file: unused_element, unnecessary_cast, override_on_non_overriding_member
|
||||
// ignore_for_file: strict_raw_type, inference_failure_on_untyped_parameter
|
||||
|
||||
part of 'series_model.dart';
|
||||
|
||||
class SeriesModelMapper extends SubClassMapperBase<SeriesModel> {
|
||||
SeriesModelMapper._();
|
||||
|
||||
static SeriesModelMapper? _instance;
|
||||
static SeriesModelMapper ensureInitialized() {
|
||||
if (_instance == null) {
|
||||
MapperContainer.globals.use(_instance = SeriesModelMapper._());
|
||||
ItemBaseModelMapper.ensureInitialized().addSubMapper(_instance!);
|
||||
EpisodeModelMapper.ensureInitialized();
|
||||
SeasonModelMapper.ensureInitialized();
|
||||
ItemBaseModelMapper.ensureInitialized();
|
||||
OverviewModelMapper.ensureInitialized();
|
||||
UserDataMapper.ensureInitialized();
|
||||
}
|
||||
return _instance!;
|
||||
}
|
||||
|
||||
@override
|
||||
final String id = 'SeriesModel';
|
||||
|
||||
static List<EpisodeModel>? _$availableEpisodes(SeriesModel v) =>
|
||||
v.availableEpisodes;
|
||||
static const Field<SeriesModel, List<EpisodeModel>> _f$availableEpisodes =
|
||||
Field('availableEpisodes', _$availableEpisodes, opt: true);
|
||||
static List<SeasonModel>? _$seasons(SeriesModel v) => v.seasons;
|
||||
static const Field<SeriesModel, List<SeasonModel>> _f$seasons =
|
||||
Field('seasons', _$seasons, opt: true);
|
||||
static String _$originalTitle(SeriesModel v) => v.originalTitle;
|
||||
static const Field<SeriesModel, String> _f$originalTitle =
|
||||
Field('originalTitle', _$originalTitle);
|
||||
static String _$sortName(SeriesModel v) => v.sortName;
|
||||
static const Field<SeriesModel, String> _f$sortName =
|
||||
Field('sortName', _$sortName);
|
||||
static String _$status(SeriesModel v) => v.status;
|
||||
static const Field<SeriesModel, String> _f$status = Field('status', _$status);
|
||||
static List<ItemBaseModel> _$related(SeriesModel v) => v.related;
|
||||
static const Field<SeriesModel, List<ItemBaseModel>> _f$related =
|
||||
Field('related', _$related, opt: true, def: const []);
|
||||
static String _$name(SeriesModel v) => v.name;
|
||||
static const Field<SeriesModel, String> _f$name = Field('name', _$name);
|
||||
static String _$id(SeriesModel v) => v.id;
|
||||
static const Field<SeriesModel, String> _f$id = Field('id', _$id);
|
||||
static OverviewModel _$overview(SeriesModel v) => v.overview;
|
||||
static const Field<SeriesModel, OverviewModel> _f$overview =
|
||||
Field('overview', _$overview);
|
||||
static String? _$parentId(SeriesModel v) => v.parentId;
|
||||
static const Field<SeriesModel, String> _f$parentId =
|
||||
Field('parentId', _$parentId);
|
||||
static String? _$playlistId(SeriesModel v) => v.playlistId;
|
||||
static const Field<SeriesModel, String> _f$playlistId =
|
||||
Field('playlistId', _$playlistId);
|
||||
static ImagesData? _$images(SeriesModel v) => v.images;
|
||||
static const Field<SeriesModel, ImagesData> _f$images =
|
||||
Field('images', _$images);
|
||||
static int? _$childCount(SeriesModel v) => v.childCount;
|
||||
static const Field<SeriesModel, int> _f$childCount =
|
||||
Field('childCount', _$childCount);
|
||||
static double? _$primaryRatio(SeriesModel v) => v.primaryRatio;
|
||||
static const Field<SeriesModel, double> _f$primaryRatio =
|
||||
Field('primaryRatio', _$primaryRatio);
|
||||
static UserData _$userData(SeriesModel v) => v.userData;
|
||||
static const Field<SeriesModel, UserData> _f$userData =
|
||||
Field('userData', _$userData);
|
||||
static bool? _$canDownload(SeriesModel v) => v.canDownload;
|
||||
static const Field<SeriesModel, bool> _f$canDownload =
|
||||
Field('canDownload', _$canDownload, opt: true);
|
||||
static bool? _$canDelete(SeriesModel v) => v.canDelete;
|
||||
static const Field<SeriesModel, bool> _f$canDelete =
|
||||
Field('canDelete', _$canDelete, opt: true);
|
||||
static dto.BaseItemKind? _$jellyType(SeriesModel v) => v.jellyType;
|
||||
static const Field<SeriesModel, dto.BaseItemKind> _f$jellyType =
|
||||
Field('jellyType', _$jellyType, opt: true);
|
||||
|
||||
@override
|
||||
final MappableFields<SeriesModel> fields = const {
|
||||
#availableEpisodes: _f$availableEpisodes,
|
||||
#seasons: _f$seasons,
|
||||
#originalTitle: _f$originalTitle,
|
||||
#sortName: _f$sortName,
|
||||
#status: _f$status,
|
||||
#related: _f$related,
|
||||
#name: _f$name,
|
||||
#id: _f$id,
|
||||
#overview: _f$overview,
|
||||
#parentId: _f$parentId,
|
||||
#playlistId: _f$playlistId,
|
||||
#images: _f$images,
|
||||
#childCount: _f$childCount,
|
||||
#primaryRatio: _f$primaryRatio,
|
||||
#userData: _f$userData,
|
||||
#canDownload: _f$canDownload,
|
||||
#canDelete: _f$canDelete,
|
||||
#jellyType: _f$jellyType,
|
||||
};
|
||||
@override
|
||||
final bool ignoreNull = true;
|
||||
|
||||
@override
|
||||
final String discriminatorKey = 'type';
|
||||
@override
|
||||
final dynamic discriminatorValue = 'SeriesModel';
|
||||
@override
|
||||
late final ClassMapperBase superMapper =
|
||||
ItemBaseModelMapper.ensureInitialized();
|
||||
|
||||
static SeriesModel _instantiate(DecodingData data) {
|
||||
return SeriesModel(
|
||||
availableEpisodes: data.dec(_f$availableEpisodes),
|
||||
seasons: data.dec(_f$seasons),
|
||||
originalTitle: data.dec(_f$originalTitle),
|
||||
sortName: data.dec(_f$sortName),
|
||||
status: data.dec(_f$status),
|
||||
related: data.dec(_f$related),
|
||||
name: data.dec(_f$name),
|
||||
id: data.dec(_f$id),
|
||||
overview: data.dec(_f$overview),
|
||||
parentId: data.dec(_f$parentId),
|
||||
playlistId: data.dec(_f$playlistId),
|
||||
images: data.dec(_f$images),
|
||||
childCount: data.dec(_f$childCount),
|
||||
primaryRatio: data.dec(_f$primaryRatio),
|
||||
userData: data.dec(_f$userData),
|
||||
canDownload: data.dec(_f$canDownload),
|
||||
canDelete: data.dec(_f$canDelete),
|
||||
jellyType: data.dec(_f$jellyType));
|
||||
}
|
||||
|
||||
@override
|
||||
final Function instantiate = _instantiate;
|
||||
|
||||
static SeriesModel fromMap(Map<String, dynamic> map) {
|
||||
return ensureInitialized().decodeMap<SeriesModel>(map);
|
||||
}
|
||||
|
||||
static SeriesModel fromJson(String json) {
|
||||
return ensureInitialized().decodeJson<SeriesModel>(json);
|
||||
}
|
||||
}
|
||||
|
||||
mixin SeriesModelMappable {
|
||||
String toJson() {
|
||||
return SeriesModelMapper.ensureInitialized()
|
||||
.encodeJson<SeriesModel>(this as SeriesModel);
|
||||
}
|
||||
|
||||
Map<String, dynamic> toMap() {
|
||||
return SeriesModelMapper.ensureInitialized()
|
||||
.encodeMap<SeriesModel>(this as SeriesModel);
|
||||
}
|
||||
|
||||
SeriesModelCopyWith<SeriesModel, SeriesModel, SeriesModel> get copyWith =>
|
||||
_SeriesModelCopyWithImpl(this as SeriesModel, $identity, $identity);
|
||||
@override
|
||||
String toString() {
|
||||
return SeriesModelMapper.ensureInitialized()
|
||||
.stringifyValue(this as SeriesModel);
|
||||
}
|
||||
}
|
||||
|
||||
extension SeriesModelValueCopy<$R, $Out>
|
||||
on ObjectCopyWith<$R, SeriesModel, $Out> {
|
||||
SeriesModelCopyWith<$R, SeriesModel, $Out> get $asSeriesModel =>
|
||||
$base.as((v, t, t2) => _SeriesModelCopyWithImpl(v, t, t2));
|
||||
}
|
||||
|
||||
abstract class SeriesModelCopyWith<$R, $In extends SeriesModel, $Out>
|
||||
implements ItemBaseModelCopyWith<$R, $In, $Out> {
|
||||
ListCopyWith<$R, EpisodeModel,
|
||||
EpisodeModelCopyWith<$R, EpisodeModel, EpisodeModel>>?
|
||||
get availableEpisodes;
|
||||
ListCopyWith<$R, SeasonModel,
|
||||
SeasonModelCopyWith<$R, SeasonModel, SeasonModel>>? get seasons;
|
||||
ListCopyWith<$R, ItemBaseModel,
|
||||
ItemBaseModelCopyWith<$R, ItemBaseModel, ItemBaseModel>> get related;
|
||||
@override
|
||||
OverviewModelCopyWith<$R, OverviewModel, OverviewModel> get overview;
|
||||
@override
|
||||
UserDataCopyWith<$R, UserData, UserData> get userData;
|
||||
@override
|
||||
$R call(
|
||||
{List<EpisodeModel>? availableEpisodes,
|
||||
List<SeasonModel>? seasons,
|
||||
String? originalTitle,
|
||||
String? sortName,
|
||||
String? status,
|
||||
List<ItemBaseModel>? related,
|
||||
String? name,
|
||||
String? id,
|
||||
OverviewModel? overview,
|
||||
String? parentId,
|
||||
String? playlistId,
|
||||
ImagesData? images,
|
||||
int? childCount,
|
||||
double? primaryRatio,
|
||||
UserData? userData,
|
||||
bool? canDownload,
|
||||
bool? canDelete,
|
||||
dto.BaseItemKind? jellyType});
|
||||
SeriesModelCopyWith<$R2, $In, $Out2> $chain<$R2, $Out2>(Then<$Out2, $R2> t);
|
||||
}
|
||||
|
||||
class _SeriesModelCopyWithImpl<$R, $Out>
|
||||
extends ClassCopyWithBase<$R, SeriesModel, $Out>
|
||||
implements SeriesModelCopyWith<$R, SeriesModel, $Out> {
|
||||
_SeriesModelCopyWithImpl(super.value, super.then, super.then2);
|
||||
|
||||
@override
|
||||
late final ClassMapperBase<SeriesModel> $mapper =
|
||||
SeriesModelMapper.ensureInitialized();
|
||||
@override
|
||||
ListCopyWith<$R, EpisodeModel,
|
||||
EpisodeModelCopyWith<$R, EpisodeModel, EpisodeModel>>?
|
||||
get availableEpisodes => $value.availableEpisodes != null
|
||||
? ListCopyWith($value.availableEpisodes!,
|
||||
(v, t) => v.copyWith.$chain(t), (v) => call(availableEpisodes: v))
|
||||
: null;
|
||||
@override
|
||||
ListCopyWith<$R, SeasonModel,
|
||||
SeasonModelCopyWith<$R, SeasonModel, SeasonModel>>?
|
||||
get seasons => $value.seasons != null
|
||||
? ListCopyWith($value.seasons!, (v, t) => v.copyWith.$chain(t),
|
||||
(v) => call(seasons: v))
|
||||
: null;
|
||||
@override
|
||||
ListCopyWith<$R, ItemBaseModel,
|
||||
ItemBaseModelCopyWith<$R, ItemBaseModel, ItemBaseModel>>
|
||||
get related => ListCopyWith($value.related,
|
||||
(v, t) => v.copyWith.$chain(t), (v) => call(related: v));
|
||||
@override
|
||||
OverviewModelCopyWith<$R, OverviewModel, OverviewModel> get overview =>
|
||||
$value.overview.copyWith.$chain((v) => call(overview: v));
|
||||
@override
|
||||
UserDataCopyWith<$R, UserData, UserData> get userData =>
|
||||
$value.userData.copyWith.$chain((v) => call(userData: v));
|
||||
@override
|
||||
$R call(
|
||||
{Object? availableEpisodes = $none,
|
||||
Object? seasons = $none,
|
||||
String? originalTitle,
|
||||
String? sortName,
|
||||
String? status,
|
||||
List<ItemBaseModel>? related,
|
||||
String? name,
|
||||
String? id,
|
||||
OverviewModel? overview,
|
||||
Object? parentId = $none,
|
||||
Object? playlistId = $none,
|
||||
Object? images = $none,
|
||||
Object? childCount = $none,
|
||||
Object? primaryRatio = $none,
|
||||
UserData? userData,
|
||||
Object? canDownload = $none,
|
||||
Object? canDelete = $none,
|
||||
Object? jellyType = $none}) =>
|
||||
$apply(FieldCopyWithData({
|
||||
if (availableEpisodes != $none) #availableEpisodes: availableEpisodes,
|
||||
if (seasons != $none) #seasons: seasons,
|
||||
if (originalTitle != null) #originalTitle: originalTitle,
|
||||
if (sortName != null) #sortName: sortName,
|
||||
if (status != null) #status: status,
|
||||
if (related != null) #related: related,
|
||||
if (name != null) #name: name,
|
||||
if (id != null) #id: id,
|
||||
if (overview != null) #overview: overview,
|
||||
if (parentId != $none) #parentId: parentId,
|
||||
if (playlistId != $none) #playlistId: playlistId,
|
||||
if (images != $none) #images: images,
|
||||
if (childCount != $none) #childCount: childCount,
|
||||
if (primaryRatio != $none) #primaryRatio: primaryRatio,
|
||||
if (userData != null) #userData: userData,
|
||||
if (canDownload != $none) #canDownload: canDownload,
|
||||
if (canDelete != $none) #canDelete: canDelete,
|
||||
if (jellyType != $none) #jellyType: jellyType
|
||||
}));
|
||||
@override
|
||||
SeriesModel $make(CopyWithData data) => SeriesModel(
|
||||
availableEpisodes:
|
||||
data.get(#availableEpisodes, or: $value.availableEpisodes),
|
||||
seasons: data.get(#seasons, or: $value.seasons),
|
||||
originalTitle: data.get(#originalTitle, or: $value.originalTitle),
|
||||
sortName: data.get(#sortName, or: $value.sortName),
|
||||
status: data.get(#status, or: $value.status),
|
||||
related: data.get(#related, or: $value.related),
|
||||
name: data.get(#name, or: $value.name),
|
||||
id: data.get(#id, or: $value.id),
|
||||
overview: data.get(#overview, or: $value.overview),
|
||||
parentId: data.get(#parentId, or: $value.parentId),
|
||||
playlistId: data.get(#playlistId, or: $value.playlistId),
|
||||
images: data.get(#images, or: $value.images),
|
||||
childCount: data.get(#childCount, or: $value.childCount),
|
||||
primaryRatio: data.get(#primaryRatio, or: $value.primaryRatio),
|
||||
userData: data.get(#userData, or: $value.userData),
|
||||
canDownload: data.get(#canDownload, or: $value.canDownload),
|
||||
canDelete: data.get(#canDelete, or: $value.canDelete),
|
||||
jellyType: data.get(#jellyType, or: $value.jellyType));
|
||||
|
||||
@override
|
||||
SeriesModelCopyWith<$R2, SeriesModel, $Out2> $chain<$R2, $Out2>(
|
||||
Then<$Out2, $R2> t) =>
|
||||
_SeriesModelCopyWithImpl($value, $cast, t);
|
||||
}
|
||||
61
lib/models/items/trick_play_model.dart
Normal file
61
lib/models/items/trick_play_model.dart
Normal file
|
|
@ -0,0 +1,61 @@
|
|||
import 'dart:ui';
|
||||
|
||||
import 'package:freezed_annotation/freezed_annotation.dart';
|
||||
|
||||
part 'trick_play_model.freezed.dart';
|
||||
part 'trick_play_model.g.dart';
|
||||
|
||||
@freezed
|
||||
class TrickPlayModel with _$TrickPlayModel {
|
||||
factory TrickPlayModel({
|
||||
required int width,
|
||||
required int height,
|
||||
required int tileWidth,
|
||||
required int tileHeight,
|
||||
required int thumbnailCount,
|
||||
required Duration interval,
|
||||
@Default([]) List<String> images,
|
||||
}) = _TrickPlayModel;
|
||||
|
||||
const TrickPlayModel._();
|
||||
|
||||
int get imagesPerTile => tileWidth * tileHeight;
|
||||
|
||||
String? getTile(Duration position) {
|
||||
final int currentIndex = (position.inMilliseconds ~/ interval.inMilliseconds).clamp(0, thumbnailCount);
|
||||
final int indexOfTile = (currentIndex ~/ imagesPerTile).clamp(0, images.length);
|
||||
return images.elementAtOrNull(indexOfTile);
|
||||
}
|
||||
|
||||
Offset offset(Duration position) {
|
||||
final int currentIndex = (position.inMilliseconds ~/ interval.inMilliseconds).clamp(0, thumbnailCount - 1);
|
||||
final int tileIndex = currentIndex % imagesPerTile;
|
||||
final int column = tileIndex % tileWidth;
|
||||
final int row = tileIndex ~/ tileWidth;
|
||||
return Offset((width * column).toDouble(), (height * row).toDouble());
|
||||
}
|
||||
|
||||
static Map<String, TrickPlayModel> toTrickPlayMap(Map<String, dynamic> map) {
|
||||
Map<String, TrickPlayModel> newMap = {};
|
||||
final firstMap = (((map.entries.first as MapEntry).value as Map<String, dynamic>));
|
||||
newMap.addEntries(firstMap.entries.map(
|
||||
(e) {
|
||||
final map = e.value as Map<String, dynamic>;
|
||||
return MapEntry(
|
||||
e.key,
|
||||
TrickPlayModel(
|
||||
width: map['Width'] as int? ?? 0,
|
||||
height: map['Height'] as int? ?? 0,
|
||||
tileWidth: map['TileWidth'] as int? ?? 0,
|
||||
tileHeight: map['TileHeight'] as int? ?? 0,
|
||||
thumbnailCount: map['ThumbnailCount'] as int? ?? 0,
|
||||
interval: Duration(milliseconds: map['Interval'] as int? ?? 0),
|
||||
),
|
||||
);
|
||||
},
|
||||
));
|
||||
return newMap;
|
||||
}
|
||||
|
||||
factory TrickPlayModel.fromJson(Map<String, dynamic> json) => _$TrickPlayModelFromJson(json);
|
||||
}
|
||||
297
lib/models/items/trick_play_model.freezed.dart
Normal file
297
lib/models/items/trick_play_model.freezed.dart
Normal file
|
|
@ -0,0 +1,297 @@
|
|||
// 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 'trick_play_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');
|
||||
|
||||
TrickPlayModel _$TrickPlayModelFromJson(Map<String, dynamic> json) {
|
||||
return _TrickPlayModel.fromJson(json);
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
mixin _$TrickPlayModel {
|
||||
int get width => throw _privateConstructorUsedError;
|
||||
int get height => throw _privateConstructorUsedError;
|
||||
int get tileWidth => throw _privateConstructorUsedError;
|
||||
int get tileHeight => throw _privateConstructorUsedError;
|
||||
int get thumbnailCount => throw _privateConstructorUsedError;
|
||||
Duration get interval => throw _privateConstructorUsedError;
|
||||
List<String> get images => throw _privateConstructorUsedError;
|
||||
|
||||
Map<String, dynamic> toJson() => throw _privateConstructorUsedError;
|
||||
@JsonKey(ignore: true)
|
||||
$TrickPlayModelCopyWith<TrickPlayModel> get copyWith =>
|
||||
throw _privateConstructorUsedError;
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
abstract class $TrickPlayModelCopyWith<$Res> {
|
||||
factory $TrickPlayModelCopyWith(
|
||||
TrickPlayModel value, $Res Function(TrickPlayModel) then) =
|
||||
_$TrickPlayModelCopyWithImpl<$Res, TrickPlayModel>;
|
||||
@useResult
|
||||
$Res call(
|
||||
{int width,
|
||||
int height,
|
||||
int tileWidth,
|
||||
int tileHeight,
|
||||
int thumbnailCount,
|
||||
Duration interval,
|
||||
List<String> images});
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
class _$TrickPlayModelCopyWithImpl<$Res, $Val extends TrickPlayModel>
|
||||
implements $TrickPlayModelCopyWith<$Res> {
|
||||
_$TrickPlayModelCopyWithImpl(this._value, this._then);
|
||||
|
||||
// ignore: unused_field
|
||||
final $Val _value;
|
||||
// ignore: unused_field
|
||||
final $Res Function($Val) _then;
|
||||
|
||||
@pragma('vm:prefer-inline')
|
||||
@override
|
||||
$Res call({
|
||||
Object? width = null,
|
||||
Object? height = null,
|
||||
Object? tileWidth = null,
|
||||
Object? tileHeight = null,
|
||||
Object? thumbnailCount = null,
|
||||
Object? interval = null,
|
||||
Object? images = null,
|
||||
}) {
|
||||
return _then(_value.copyWith(
|
||||
width: null == width
|
||||
? _value.width
|
||||
: width // ignore: cast_nullable_to_non_nullable
|
||||
as int,
|
||||
height: null == height
|
||||
? _value.height
|
||||
: height // ignore: cast_nullable_to_non_nullable
|
||||
as int,
|
||||
tileWidth: null == tileWidth
|
||||
? _value.tileWidth
|
||||
: tileWidth // ignore: cast_nullable_to_non_nullable
|
||||
as int,
|
||||
tileHeight: null == tileHeight
|
||||
? _value.tileHeight
|
||||
: tileHeight // ignore: cast_nullable_to_non_nullable
|
||||
as int,
|
||||
thumbnailCount: null == thumbnailCount
|
||||
? _value.thumbnailCount
|
||||
: thumbnailCount // ignore: cast_nullable_to_non_nullable
|
||||
as int,
|
||||
interval: null == interval
|
||||
? _value.interval
|
||||
: interval // ignore: cast_nullable_to_non_nullable
|
||||
as Duration,
|
||||
images: null == images
|
||||
? _value.images
|
||||
: images // ignore: cast_nullable_to_non_nullable
|
||||
as List<String>,
|
||||
) as $Val);
|
||||
}
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
abstract class _$$TrickPlayModelImplCopyWith<$Res>
|
||||
implements $TrickPlayModelCopyWith<$Res> {
|
||||
factory _$$TrickPlayModelImplCopyWith(_$TrickPlayModelImpl value,
|
||||
$Res Function(_$TrickPlayModelImpl) then) =
|
||||
__$$TrickPlayModelImplCopyWithImpl<$Res>;
|
||||
@override
|
||||
@useResult
|
||||
$Res call(
|
||||
{int width,
|
||||
int height,
|
||||
int tileWidth,
|
||||
int tileHeight,
|
||||
int thumbnailCount,
|
||||
Duration interval,
|
||||
List<String> images});
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
class __$$TrickPlayModelImplCopyWithImpl<$Res>
|
||||
extends _$TrickPlayModelCopyWithImpl<$Res, _$TrickPlayModelImpl>
|
||||
implements _$$TrickPlayModelImplCopyWith<$Res> {
|
||||
__$$TrickPlayModelImplCopyWithImpl(
|
||||
_$TrickPlayModelImpl _value, $Res Function(_$TrickPlayModelImpl) _then)
|
||||
: super(_value, _then);
|
||||
|
||||
@pragma('vm:prefer-inline')
|
||||
@override
|
||||
$Res call({
|
||||
Object? width = null,
|
||||
Object? height = null,
|
||||
Object? tileWidth = null,
|
||||
Object? tileHeight = null,
|
||||
Object? thumbnailCount = null,
|
||||
Object? interval = null,
|
||||
Object? images = null,
|
||||
}) {
|
||||
return _then(_$TrickPlayModelImpl(
|
||||
width: null == width
|
||||
? _value.width
|
||||
: width // ignore: cast_nullable_to_non_nullable
|
||||
as int,
|
||||
height: null == height
|
||||
? _value.height
|
||||
: height // ignore: cast_nullable_to_non_nullable
|
||||
as int,
|
||||
tileWidth: null == tileWidth
|
||||
? _value.tileWidth
|
||||
: tileWidth // ignore: cast_nullable_to_non_nullable
|
||||
as int,
|
||||
tileHeight: null == tileHeight
|
||||
? _value.tileHeight
|
||||
: tileHeight // ignore: cast_nullable_to_non_nullable
|
||||
as int,
|
||||
thumbnailCount: null == thumbnailCount
|
||||
? _value.thumbnailCount
|
||||
: thumbnailCount // ignore: cast_nullable_to_non_nullable
|
||||
as int,
|
||||
interval: null == interval
|
||||
? _value.interval
|
||||
: interval // ignore: cast_nullable_to_non_nullable
|
||||
as Duration,
|
||||
images: null == images
|
||||
? _value._images
|
||||
: images // ignore: cast_nullable_to_non_nullable
|
||||
as List<String>,
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
@JsonSerializable()
|
||||
class _$TrickPlayModelImpl extends _TrickPlayModel {
|
||||
_$TrickPlayModelImpl(
|
||||
{required this.width,
|
||||
required this.height,
|
||||
required this.tileWidth,
|
||||
required this.tileHeight,
|
||||
required this.thumbnailCount,
|
||||
required this.interval,
|
||||
final List<String> images = const []})
|
||||
: _images = images,
|
||||
super._();
|
||||
|
||||
factory _$TrickPlayModelImpl.fromJson(Map<String, dynamic> json) =>
|
||||
_$$TrickPlayModelImplFromJson(json);
|
||||
|
||||
@override
|
||||
final int width;
|
||||
@override
|
||||
final int height;
|
||||
@override
|
||||
final int tileWidth;
|
||||
@override
|
||||
final int tileHeight;
|
||||
@override
|
||||
final int thumbnailCount;
|
||||
@override
|
||||
final Duration interval;
|
||||
final List<String> _images;
|
||||
@override
|
||||
@JsonKey()
|
||||
List<String> get images {
|
||||
if (_images is EqualUnmodifiableListView) return _images;
|
||||
// ignore: implicit_dynamic_type
|
||||
return EqualUnmodifiableListView(_images);
|
||||
}
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'TrickPlayModel(width: $width, height: $height, tileWidth: $tileWidth, tileHeight: $tileHeight, thumbnailCount: $thumbnailCount, interval: $interval, images: $images)';
|
||||
}
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
return identical(this, other) ||
|
||||
(other.runtimeType == runtimeType &&
|
||||
other is _$TrickPlayModelImpl &&
|
||||
(identical(other.width, width) || other.width == width) &&
|
||||
(identical(other.height, height) || other.height == height) &&
|
||||
(identical(other.tileWidth, tileWidth) ||
|
||||
other.tileWidth == tileWidth) &&
|
||||
(identical(other.tileHeight, tileHeight) ||
|
||||
other.tileHeight == tileHeight) &&
|
||||
(identical(other.thumbnailCount, thumbnailCount) ||
|
||||
other.thumbnailCount == thumbnailCount) &&
|
||||
(identical(other.interval, interval) ||
|
||||
other.interval == interval) &&
|
||||
const DeepCollectionEquality().equals(other._images, _images));
|
||||
}
|
||||
|
||||
@JsonKey(ignore: true)
|
||||
@override
|
||||
int get hashCode => Object.hash(
|
||||
runtimeType,
|
||||
width,
|
||||
height,
|
||||
tileWidth,
|
||||
tileHeight,
|
||||
thumbnailCount,
|
||||
interval,
|
||||
const DeepCollectionEquality().hash(_images));
|
||||
|
||||
@JsonKey(ignore: true)
|
||||
@override
|
||||
@pragma('vm:prefer-inline')
|
||||
_$$TrickPlayModelImplCopyWith<_$TrickPlayModelImpl> get copyWith =>
|
||||
__$$TrickPlayModelImplCopyWithImpl<_$TrickPlayModelImpl>(
|
||||
this, _$identity);
|
||||
|
||||
@override
|
||||
Map<String, dynamic> toJson() {
|
||||
return _$$TrickPlayModelImplToJson(
|
||||
this,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
abstract class _TrickPlayModel extends TrickPlayModel {
|
||||
factory _TrickPlayModel(
|
||||
{required final int width,
|
||||
required final int height,
|
||||
required final int tileWidth,
|
||||
required final int tileHeight,
|
||||
required final int thumbnailCount,
|
||||
required final Duration interval,
|
||||
final List<String> images}) = _$TrickPlayModelImpl;
|
||||
_TrickPlayModel._() : super._();
|
||||
|
||||
factory _TrickPlayModel.fromJson(Map<String, dynamic> json) =
|
||||
_$TrickPlayModelImpl.fromJson;
|
||||
|
||||
@override
|
||||
int get width;
|
||||
@override
|
||||
int get height;
|
||||
@override
|
||||
int get tileWidth;
|
||||
@override
|
||||
int get tileHeight;
|
||||
@override
|
||||
int get thumbnailCount;
|
||||
@override
|
||||
Duration get interval;
|
||||
@override
|
||||
List<String> get images;
|
||||
@override
|
||||
@JsonKey(ignore: true)
|
||||
_$$TrickPlayModelImplCopyWith<_$TrickPlayModelImpl> get copyWith =>
|
||||
throw _privateConstructorUsedError;
|
||||
}
|
||||
33
lib/models/items/trick_play_model.g.dart
Normal file
33
lib/models/items/trick_play_model.g.dart
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
|
||||
part of 'trick_play_model.dart';
|
||||
|
||||
// **************************************************************************
|
||||
// JsonSerializableGenerator
|
||||
// **************************************************************************
|
||||
|
||||
_$TrickPlayModelImpl _$$TrickPlayModelImplFromJson(Map<String, dynamic> json) =>
|
||||
_$TrickPlayModelImpl(
|
||||
width: (json['width'] as num).toInt(),
|
||||
height: (json['height'] as num).toInt(),
|
||||
tileWidth: (json['tileWidth'] as num).toInt(),
|
||||
tileHeight: (json['tileHeight'] as num).toInt(),
|
||||
thumbnailCount: (json['thumbnailCount'] as num).toInt(),
|
||||
interval: Duration(microseconds: (json['interval'] as num).toInt()),
|
||||
images: (json['images'] as List<dynamic>?)
|
||||
?.map((e) => e as String)
|
||||
.toList() ??
|
||||
const [],
|
||||
);
|
||||
|
||||
Map<String, dynamic> _$$TrickPlayModelImplToJson(
|
||||
_$TrickPlayModelImpl instance) =>
|
||||
<String, dynamic>{
|
||||
'width': instance.width,
|
||||
'height': instance.height,
|
||||
'tileWidth': instance.tileWidth,
|
||||
'tileHeight': instance.tileHeight,
|
||||
'thumbnailCount': instance.thumbnailCount,
|
||||
'interval': instance.interval.inMicroseconds,
|
||||
'images': instance.images,
|
||||
};
|
||||
61
lib/models/library_model.dart
Normal file
61
lib/models/library_model.dart
Normal file
|
|
@ -0,0 +1,61 @@
|
|||
// ignore_for_file: public_member_api_docs, sort_constructors_first
|
||||
import 'package:fladder/jellyfin/jellyfin_open_api.enums.swagger.dart' as enums;
|
||||
import 'package:fladder/models/item_base_model.dart';
|
||||
import 'package:fladder/models/items/photos_model.dart';
|
||||
import 'package:fladder/models/recommended_model.dart';
|
||||
|
||||
class LibraryModel {
|
||||
final bool loading;
|
||||
final String id;
|
||||
final String name;
|
||||
final List<PhotoModel> timelinePhotos;
|
||||
final List<ItemBaseModel> posters;
|
||||
final List<RecommendedModel> recommendations;
|
||||
final List<String> genres;
|
||||
final List<ItemBaseModel> latest;
|
||||
final List<ItemBaseModel> nextUp;
|
||||
final List<ItemBaseModel> favourites;
|
||||
final enums.BaseItemKind type;
|
||||
|
||||
LibraryModel({
|
||||
this.loading = false,
|
||||
required this.id,
|
||||
required this.name,
|
||||
this.timelinePhotos = const [],
|
||||
this.posters = const [],
|
||||
this.recommendations = const [],
|
||||
this.genres = const [],
|
||||
this.latest = const [],
|
||||
this.nextUp = const [],
|
||||
this.favourites = const [],
|
||||
required this.type,
|
||||
});
|
||||
|
||||
LibraryModel copyWith({
|
||||
bool? loading,
|
||||
String? id,
|
||||
String? name,
|
||||
List<PhotoModel>? timelinePhotos,
|
||||
List<ItemBaseModel>? posters,
|
||||
List<RecommendedModel>? recommendations,
|
||||
List<String>? genres,
|
||||
List<ItemBaseModel>? latest,
|
||||
List<ItemBaseModel>? nextUp,
|
||||
List<ItemBaseModel>? favourites,
|
||||
enums.BaseItemKind? type,
|
||||
}) {
|
||||
return LibraryModel(
|
||||
loading: loading ?? this.loading,
|
||||
id: id ?? this.id,
|
||||
name: name ?? this.name,
|
||||
timelinePhotos: timelinePhotos ?? this.timelinePhotos,
|
||||
posters: posters ?? this.posters,
|
||||
recommendations: recommendations ?? this.recommendations,
|
||||
genres: genres ?? this.genres,
|
||||
latest: latest ?? this.latest,
|
||||
nextUp: nextUp ?? this.nextUp,
|
||||
favourites: favourites ?? this.favourites,
|
||||
type: type ?? this.type,
|
||||
);
|
||||
}
|
||||
}
|
||||
223
lib/models/library_search/library_search_model.dart
Normal file
223
lib/models/library_search/library_search_model.dart
Normal file
|
|
@ -0,0 +1,223 @@
|
|||
import 'package:collection/collection.dart';
|
||||
import 'package:dart_mappable/dart_mappable.dart';
|
||||
import 'package:fladder/util/list_extensions.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
import 'package:fladder/jellyfin/jellyfin_open_api.enums.swagger.dart';
|
||||
import 'package:fladder/jellyfin/jellyfin_open_api.swagger.dart';
|
||||
import 'package:fladder/models/item_base_model.dart';
|
||||
import 'package:fladder/models/items/item_shared_models.dart';
|
||||
import 'package:fladder/models/library_search/library_search_options.dart';
|
||||
import 'package:fladder/models/view_model.dart';
|
||||
import 'package:fladder/util/localization_helper.dart';
|
||||
import 'package:fladder/util/map_bool_helper.dart';
|
||||
|
||||
part 'library_search_model.mapper.dart';
|
||||
|
||||
@MappableClass()
|
||||
class LibrarySearchModel with LibrarySearchModelMappable {
|
||||
final bool loading;
|
||||
final bool selecteMode;
|
||||
final String searchQuery;
|
||||
final List<ItemBaseModel> folderOverwrite;
|
||||
final Map<ViewModel, bool> views;
|
||||
final List<ItemBaseModel> posters;
|
||||
final List<ItemBaseModel> selectedPosters;
|
||||
final Map<ItemFilter, bool> filters;
|
||||
final Map<String, bool> genres;
|
||||
final Map<Studio, bool> studios;
|
||||
final Map<String, bool> tags;
|
||||
final Map<int, bool> years;
|
||||
final Map<String, bool> officialRatings;
|
||||
final Map<FladderItemType, bool> types;
|
||||
final SortingOptions sortingOption;
|
||||
final SortingOrder sortOrder;
|
||||
final bool favourites;
|
||||
final bool hideEmtpyShows;
|
||||
final bool recursive;
|
||||
final GroupBy groupBy;
|
||||
final Map<String, int> lastIndices;
|
||||
final Map<String, int> libraryItemCounts;
|
||||
final bool fetchingItems;
|
||||
|
||||
const LibrarySearchModel({
|
||||
this.loading = false,
|
||||
this.selecteMode = false,
|
||||
this.folderOverwrite = const [],
|
||||
this.searchQuery = "",
|
||||
this.views = const {},
|
||||
this.posters = const [],
|
||||
this.selectedPosters = const [],
|
||||
this.filters = const {
|
||||
ItemFilter.isplayed: false,
|
||||
ItemFilter.isunplayed: false,
|
||||
ItemFilter.isresumable: false,
|
||||
},
|
||||
this.genres = const {},
|
||||
this.studios = const {},
|
||||
this.tags = const {},
|
||||
this.years = const {},
|
||||
this.officialRatings = const {},
|
||||
this.types = const {
|
||||
FladderItemType.audio: false,
|
||||
FladderItemType.boxset: false,
|
||||
FladderItemType.book: false,
|
||||
FladderItemType.collectionFolder: false,
|
||||
FladderItemType.episode: false,
|
||||
FladderItemType.folder: false,
|
||||
FladderItemType.movie: true,
|
||||
FladderItemType.musicAlbum: false,
|
||||
FladderItemType.musicVideo: false,
|
||||
FladderItemType.photo: false,
|
||||
FladderItemType.person: false,
|
||||
FladderItemType.photoalbum: false,
|
||||
FladderItemType.series: true,
|
||||
FladderItemType.video: true,
|
||||
},
|
||||
this.favourites = false,
|
||||
this.sortingOption = SortingOptions.name,
|
||||
this.sortOrder = SortingOrder.ascending,
|
||||
this.hideEmtpyShows = true,
|
||||
this.recursive = false,
|
||||
this.groupBy = GroupBy.none,
|
||||
this.lastIndices = const {},
|
||||
this.libraryItemCounts = const {},
|
||||
this.fetchingItems = false,
|
||||
});
|
||||
|
||||
bool get hasActiveFilters {
|
||||
return genres.hasEnabled ||
|
||||
studios.hasEnabled ||
|
||||
tags.hasEnabled ||
|
||||
years.hasEnabled ||
|
||||
officialRatings.hasEnabled ||
|
||||
hideEmtpyShows ||
|
||||
filters.hasEnabled ||
|
||||
favourites ||
|
||||
searchQuery.isNotEmpty;
|
||||
}
|
||||
|
||||
int get totalItemCount {
|
||||
if (libraryItemCounts.isEmpty) return posters.length;
|
||||
int totalCount = 0;
|
||||
for (var item in libraryItemCounts.values) {
|
||||
totalCount += item;
|
||||
}
|
||||
return totalCount;
|
||||
}
|
||||
|
||||
bool get allDoneFetching {
|
||||
if (libraryItemCounts.isEmpty) return false;
|
||||
if (libraryItemCounts.length != lastIndices.length) {
|
||||
return false;
|
||||
} else {
|
||||
for (var item in libraryItemCounts.entries) {
|
||||
if (lastIndices[item.key] != item.value) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
String searchBarTitle(BuildContext context) {
|
||||
if (folderOverwrite.isNotEmpty) {
|
||||
return "${context.localized.search} ${folderOverwrite.last.name}...";
|
||||
}
|
||||
return views.included.length == 1
|
||||
? "${context.localized.search} ${views.included.first.name}..."
|
||||
: "${context.localized.search} ${context.localized.library(2)}...";
|
||||
}
|
||||
|
||||
ItemBaseModel? get nestedCurrentItem => folderOverwrite.lastOrNull;
|
||||
|
||||
List<ItemBaseModel> get activePosters => selectedPosters.isNotEmpty ? selectedPosters : posters;
|
||||
|
||||
bool get showPlayButtons {
|
||||
if (totalItemCount == 0) return false;
|
||||
return types.included.isEmpty ||
|
||||
types.included.containsAny(
|
||||
{...FladderItemType.playable, FladderItemType.folder},
|
||||
);
|
||||
}
|
||||
|
||||
bool get showGalleryButtons {
|
||||
if (totalItemCount == 0) return false;
|
||||
return types.included.isEmpty ||
|
||||
types.included.containsAny(
|
||||
{...FladderItemType.galleryItem, FladderItemType.photoalbum, FladderItemType.folder},
|
||||
);
|
||||
}
|
||||
|
||||
LibrarySearchModel resetLazyLoad() {
|
||||
return copyWith(
|
||||
selectedPosters: [],
|
||||
lastIndices: const {},
|
||||
libraryItemCounts: const {},
|
||||
);
|
||||
}
|
||||
|
||||
LibrarySearchModel fullReset() {
|
||||
return copyWith(
|
||||
posters: [],
|
||||
selectedPosters: [],
|
||||
lastIndices: const {},
|
||||
libraryItemCounts: const {},
|
||||
);
|
||||
}
|
||||
|
||||
LibrarySearchModel setFiltersToDefault() {
|
||||
return copyWith(
|
||||
genres: const {},
|
||||
tags: const {},
|
||||
officialRatings: const {},
|
||||
years: const {},
|
||||
searchQuery: '',
|
||||
favourites: false,
|
||||
recursive: false,
|
||||
studios: const {},
|
||||
hideEmtpyShows: true,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
bool operator ==(covariant LibrarySearchModel other) {
|
||||
if (identical(this, other)) return true;
|
||||
|
||||
return other.searchQuery == searchQuery &&
|
||||
listEquals(other.folderOverwrite, folderOverwrite) &&
|
||||
mapEquals(other.views, views) &&
|
||||
mapEquals(other.filters, filters) &&
|
||||
mapEquals(other.genres, genres) &&
|
||||
mapEquals(other.studios, studios) &&
|
||||
mapEquals(other.tags, tags) &&
|
||||
mapEquals(other.years, years) &&
|
||||
mapEquals(other.officialRatings, officialRatings) &&
|
||||
mapEquals(other.types, types) &&
|
||||
other.sortingOption == sortingOption &&
|
||||
other.sortOrder == sortOrder &&
|
||||
other.favourites == favourites &&
|
||||
other.recursive == recursive;
|
||||
}
|
||||
|
||||
@override
|
||||
int get hashCode {
|
||||
return searchQuery.hashCode ^
|
||||
folderOverwrite.hashCode ^
|
||||
views.hashCode ^
|
||||
posters.hashCode ^
|
||||
selectedPosters.hashCode ^
|
||||
filters.hashCode ^
|
||||
genres.hashCode ^
|
||||
studios.hashCode ^
|
||||
tags.hashCode ^
|
||||
years.hashCode ^
|
||||
officialRatings.hashCode ^
|
||||
types.hashCode ^
|
||||
sortingOption.hashCode ^
|
||||
sortOrder.hashCode ^
|
||||
favourites.hashCode ^
|
||||
recursive.hashCode;
|
||||
}
|
||||
}
|
||||
418
lib/models/library_search/library_search_model.mapper.dart
Normal file
418
lib/models/library_search/library_search_model.mapper.dart
Normal file
|
|
@ -0,0 +1,418 @@
|
|||
// coverage:ignore-file
|
||||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
// ignore_for_file: type=lint
|
||||
// ignore_for_file: unused_element, unnecessary_cast, override_on_non_overriding_member
|
||||
// ignore_for_file: strict_raw_type, inference_failure_on_untyped_parameter
|
||||
|
||||
part of 'library_search_model.dart';
|
||||
|
||||
class LibrarySearchModelMapper extends ClassMapperBase<LibrarySearchModel> {
|
||||
LibrarySearchModelMapper._();
|
||||
|
||||
static LibrarySearchModelMapper? _instance;
|
||||
static LibrarySearchModelMapper ensureInitialized() {
|
||||
if (_instance == null) {
|
||||
MapperContainer.globals.use(_instance = LibrarySearchModelMapper._());
|
||||
ItemBaseModelMapper.ensureInitialized();
|
||||
}
|
||||
return _instance!;
|
||||
}
|
||||
|
||||
@override
|
||||
final String id = 'LibrarySearchModel';
|
||||
|
||||
static bool _$loading(LibrarySearchModel v) => v.loading;
|
||||
static const Field<LibrarySearchModel, bool> _f$loading =
|
||||
Field('loading', _$loading, opt: true, def: false);
|
||||
static bool _$selecteMode(LibrarySearchModel v) => v.selecteMode;
|
||||
static const Field<LibrarySearchModel, bool> _f$selecteMode =
|
||||
Field('selecteMode', _$selecteMode, opt: true, def: false);
|
||||
static List<ItemBaseModel> _$folderOverwrite(LibrarySearchModel v) =>
|
||||
v.folderOverwrite;
|
||||
static const Field<LibrarySearchModel, List<ItemBaseModel>>
|
||||
_f$folderOverwrite =
|
||||
Field('folderOverwrite', _$folderOverwrite, opt: true, def: const []);
|
||||
static String _$searchQuery(LibrarySearchModel v) => v.searchQuery;
|
||||
static const Field<LibrarySearchModel, String> _f$searchQuery =
|
||||
Field('searchQuery', _$searchQuery, opt: true, def: "");
|
||||
static Map<ViewModel, bool> _$views(LibrarySearchModel v) => v.views;
|
||||
static const Field<LibrarySearchModel, Map<ViewModel, bool>> _f$views =
|
||||
Field('views', _$views, opt: true, def: const {});
|
||||
static List<ItemBaseModel> _$posters(LibrarySearchModel v) => v.posters;
|
||||
static const Field<LibrarySearchModel, List<ItemBaseModel>> _f$posters =
|
||||
Field('posters', _$posters, opt: true, def: const []);
|
||||
static List<ItemBaseModel> _$selectedPosters(LibrarySearchModel v) =>
|
||||
v.selectedPosters;
|
||||
static const Field<LibrarySearchModel, List<ItemBaseModel>>
|
||||
_f$selectedPosters =
|
||||
Field('selectedPosters', _$selectedPosters, opt: true, def: const []);
|
||||
static Map<ItemFilter, bool> _$filters(LibrarySearchModel v) => v.filters;
|
||||
static const Field<LibrarySearchModel, Map<ItemFilter, bool>> _f$filters =
|
||||
Field('filters', _$filters, opt: true, def: const {
|
||||
ItemFilter.isplayed: false,
|
||||
ItemFilter.isunplayed: false,
|
||||
ItemFilter.isresumable: false
|
||||
});
|
||||
static Map<String, bool> _$genres(LibrarySearchModel v) => v.genres;
|
||||
static const Field<LibrarySearchModel, Map<String, bool>> _f$genres =
|
||||
Field('genres', _$genres, opt: true, def: const {});
|
||||
static Map<Studio, bool> _$studios(LibrarySearchModel v) => v.studios;
|
||||
static const Field<LibrarySearchModel, Map<Studio, bool>> _f$studios =
|
||||
Field('studios', _$studios, opt: true, def: const {});
|
||||
static Map<String, bool> _$tags(LibrarySearchModel v) => v.tags;
|
||||
static const Field<LibrarySearchModel, Map<String, bool>> _f$tags =
|
||||
Field('tags', _$tags, opt: true, def: const {});
|
||||
static Map<int, bool> _$years(LibrarySearchModel v) => v.years;
|
||||
static const Field<LibrarySearchModel, Map<int, bool>> _f$years =
|
||||
Field('years', _$years, opt: true, def: const {});
|
||||
static Map<String, bool> _$officialRatings(LibrarySearchModel v) =>
|
||||
v.officialRatings;
|
||||
static const Field<LibrarySearchModel, Map<String, bool>> _f$officialRatings =
|
||||
Field('officialRatings', _$officialRatings, opt: true, def: const {});
|
||||
static Map<FladderItemType, bool> _$types(LibrarySearchModel v) => v.types;
|
||||
static const Field<LibrarySearchModel, Map<FladderItemType, bool>> _f$types =
|
||||
Field('types', _$types, opt: true, def: const {
|
||||
FladderItemType.audio: false,
|
||||
FladderItemType.boxset: false,
|
||||
FladderItemType.book: false,
|
||||
FladderItemType.collectionFolder: false,
|
||||
FladderItemType.episode: false,
|
||||
FladderItemType.folder: false,
|
||||
FladderItemType.movie: true,
|
||||
FladderItemType.musicAlbum: false,
|
||||
FladderItemType.musicVideo: false,
|
||||
FladderItemType.photo: false,
|
||||
FladderItemType.person: false,
|
||||
FladderItemType.photoalbum: false,
|
||||
FladderItemType.series: true,
|
||||
FladderItemType.video: true
|
||||
});
|
||||
static bool _$favourites(LibrarySearchModel v) => v.favourites;
|
||||
static const Field<LibrarySearchModel, bool> _f$favourites =
|
||||
Field('favourites', _$favourites, opt: true, def: false);
|
||||
static SortingOptions _$sortingOption(LibrarySearchModel v) =>
|
||||
v.sortingOption;
|
||||
static const Field<LibrarySearchModel, SortingOptions> _f$sortingOption =
|
||||
Field('sortingOption', _$sortingOption,
|
||||
opt: true, def: SortingOptions.name);
|
||||
static SortingOrder _$sortOrder(LibrarySearchModel v) => v.sortOrder;
|
||||
static const Field<LibrarySearchModel, SortingOrder> _f$sortOrder =
|
||||
Field('sortOrder', _$sortOrder, opt: true, def: SortingOrder.ascending);
|
||||
static bool _$hideEmtpyShows(LibrarySearchModel v) => v.hideEmtpyShows;
|
||||
static const Field<LibrarySearchModel, bool> _f$hideEmtpyShows =
|
||||
Field('hideEmtpyShows', _$hideEmtpyShows, opt: true, def: true);
|
||||
static bool _$recursive(LibrarySearchModel v) => v.recursive;
|
||||
static const Field<LibrarySearchModel, bool> _f$recursive =
|
||||
Field('recursive', _$recursive, opt: true, def: false);
|
||||
static GroupBy _$groupBy(LibrarySearchModel v) => v.groupBy;
|
||||
static const Field<LibrarySearchModel, GroupBy> _f$groupBy =
|
||||
Field('groupBy', _$groupBy, opt: true, def: GroupBy.none);
|
||||
static Map<String, int> _$lastIndices(LibrarySearchModel v) => v.lastIndices;
|
||||
static const Field<LibrarySearchModel, Map<String, int>> _f$lastIndices =
|
||||
Field('lastIndices', _$lastIndices, opt: true, def: const {});
|
||||
static Map<String, int> _$libraryItemCounts(LibrarySearchModel v) =>
|
||||
v.libraryItemCounts;
|
||||
static const Field<LibrarySearchModel, Map<String, int>>
|
||||
_f$libraryItemCounts =
|
||||
Field('libraryItemCounts', _$libraryItemCounts, opt: true, def: const {});
|
||||
static bool _$fetchingItems(LibrarySearchModel v) => v.fetchingItems;
|
||||
static const Field<LibrarySearchModel, bool> _f$fetchingItems =
|
||||
Field('fetchingItems', _$fetchingItems, opt: true, def: false);
|
||||
|
||||
@override
|
||||
final MappableFields<LibrarySearchModel> fields = const {
|
||||
#loading: _f$loading,
|
||||
#selecteMode: _f$selecteMode,
|
||||
#folderOverwrite: _f$folderOverwrite,
|
||||
#searchQuery: _f$searchQuery,
|
||||
#views: _f$views,
|
||||
#posters: _f$posters,
|
||||
#selectedPosters: _f$selectedPosters,
|
||||
#filters: _f$filters,
|
||||
#genres: _f$genres,
|
||||
#studios: _f$studios,
|
||||
#tags: _f$tags,
|
||||
#years: _f$years,
|
||||
#officialRatings: _f$officialRatings,
|
||||
#types: _f$types,
|
||||
#favourites: _f$favourites,
|
||||
#sortingOption: _f$sortingOption,
|
||||
#sortOrder: _f$sortOrder,
|
||||
#hideEmtpyShows: _f$hideEmtpyShows,
|
||||
#recursive: _f$recursive,
|
||||
#groupBy: _f$groupBy,
|
||||
#lastIndices: _f$lastIndices,
|
||||
#libraryItemCounts: _f$libraryItemCounts,
|
||||
#fetchingItems: _f$fetchingItems,
|
||||
};
|
||||
@override
|
||||
final bool ignoreNull = true;
|
||||
|
||||
static LibrarySearchModel _instantiate(DecodingData data) {
|
||||
return LibrarySearchModel(
|
||||
loading: data.dec(_f$loading),
|
||||
selecteMode: data.dec(_f$selecteMode),
|
||||
folderOverwrite: data.dec(_f$folderOverwrite),
|
||||
searchQuery: data.dec(_f$searchQuery),
|
||||
views: data.dec(_f$views),
|
||||
posters: data.dec(_f$posters),
|
||||
selectedPosters: data.dec(_f$selectedPosters),
|
||||
filters: data.dec(_f$filters),
|
||||
genres: data.dec(_f$genres),
|
||||
studios: data.dec(_f$studios),
|
||||
tags: data.dec(_f$tags),
|
||||
years: data.dec(_f$years),
|
||||
officialRatings: data.dec(_f$officialRatings),
|
||||
types: data.dec(_f$types),
|
||||
favourites: data.dec(_f$favourites),
|
||||
sortingOption: data.dec(_f$sortingOption),
|
||||
sortOrder: data.dec(_f$sortOrder),
|
||||
hideEmtpyShows: data.dec(_f$hideEmtpyShows),
|
||||
recursive: data.dec(_f$recursive),
|
||||
groupBy: data.dec(_f$groupBy),
|
||||
lastIndices: data.dec(_f$lastIndices),
|
||||
libraryItemCounts: data.dec(_f$libraryItemCounts),
|
||||
fetchingItems: data.dec(_f$fetchingItems));
|
||||
}
|
||||
|
||||
@override
|
||||
final Function instantiate = _instantiate;
|
||||
|
||||
static LibrarySearchModel fromMap(Map<String, dynamic> map) {
|
||||
return ensureInitialized().decodeMap<LibrarySearchModel>(map);
|
||||
}
|
||||
|
||||
static LibrarySearchModel fromJson(String json) {
|
||||
return ensureInitialized().decodeJson<LibrarySearchModel>(json);
|
||||
}
|
||||
}
|
||||
|
||||
mixin LibrarySearchModelMappable {
|
||||
String toJson() {
|
||||
return LibrarySearchModelMapper.ensureInitialized()
|
||||
.encodeJson<LibrarySearchModel>(this as LibrarySearchModel);
|
||||
}
|
||||
|
||||
Map<String, dynamic> toMap() {
|
||||
return LibrarySearchModelMapper.ensureInitialized()
|
||||
.encodeMap<LibrarySearchModel>(this as LibrarySearchModel);
|
||||
}
|
||||
|
||||
LibrarySearchModelCopyWith<LibrarySearchModel, LibrarySearchModel,
|
||||
LibrarySearchModel>
|
||||
get copyWith => _LibrarySearchModelCopyWithImpl(
|
||||
this as LibrarySearchModel, $identity, $identity);
|
||||
@override
|
||||
String toString() {
|
||||
return LibrarySearchModelMapper.ensureInitialized()
|
||||
.stringifyValue(this as LibrarySearchModel);
|
||||
}
|
||||
}
|
||||
|
||||
extension LibrarySearchModelValueCopy<$R, $Out>
|
||||
on ObjectCopyWith<$R, LibrarySearchModel, $Out> {
|
||||
LibrarySearchModelCopyWith<$R, LibrarySearchModel, $Out>
|
||||
get $asLibrarySearchModel =>
|
||||
$base.as((v, t, t2) => _LibrarySearchModelCopyWithImpl(v, t, t2));
|
||||
}
|
||||
|
||||
abstract class LibrarySearchModelCopyWith<$R, $In extends LibrarySearchModel,
|
||||
$Out> implements ClassCopyWith<$R, $In, $Out> {
|
||||
ListCopyWith<$R, ItemBaseModel,
|
||||
ItemBaseModelCopyWith<$R, ItemBaseModel, ItemBaseModel>>
|
||||
get folderOverwrite;
|
||||
MapCopyWith<$R, ViewModel, bool, ObjectCopyWith<$R, bool, bool>> get views;
|
||||
ListCopyWith<$R, ItemBaseModel,
|
||||
ItemBaseModelCopyWith<$R, ItemBaseModel, ItemBaseModel>> get posters;
|
||||
ListCopyWith<$R, ItemBaseModel,
|
||||
ItemBaseModelCopyWith<$R, ItemBaseModel, ItemBaseModel>>
|
||||
get selectedPosters;
|
||||
MapCopyWith<$R, ItemFilter, bool, ObjectCopyWith<$R, bool, bool>> get filters;
|
||||
MapCopyWith<$R, String, bool, ObjectCopyWith<$R, bool, bool>> get genres;
|
||||
MapCopyWith<$R, Studio, bool, ObjectCopyWith<$R, bool, bool>> get studios;
|
||||
MapCopyWith<$R, String, bool, ObjectCopyWith<$R, bool, bool>> get tags;
|
||||
MapCopyWith<$R, int, bool, ObjectCopyWith<$R, bool, bool>> get years;
|
||||
MapCopyWith<$R, String, bool, ObjectCopyWith<$R, bool, bool>>
|
||||
get officialRatings;
|
||||
MapCopyWith<$R, FladderItemType, bool, ObjectCopyWith<$R, bool, bool>>
|
||||
get types;
|
||||
MapCopyWith<$R, String, int, ObjectCopyWith<$R, int, int>> get lastIndices;
|
||||
MapCopyWith<$R, String, int, ObjectCopyWith<$R, int, int>>
|
||||
get libraryItemCounts;
|
||||
$R call(
|
||||
{bool? loading,
|
||||
bool? selecteMode,
|
||||
List<ItemBaseModel>? folderOverwrite,
|
||||
String? searchQuery,
|
||||
Map<ViewModel, bool>? views,
|
||||
List<ItemBaseModel>? posters,
|
||||
List<ItemBaseModel>? selectedPosters,
|
||||
Map<ItemFilter, bool>? filters,
|
||||
Map<String, bool>? genres,
|
||||
Map<Studio, bool>? studios,
|
||||
Map<String, bool>? tags,
|
||||
Map<int, bool>? years,
|
||||
Map<String, bool>? officialRatings,
|
||||
Map<FladderItemType, bool>? types,
|
||||
bool? favourites,
|
||||
SortingOptions? sortingOption,
|
||||
SortingOrder? sortOrder,
|
||||
bool? hideEmtpyShows,
|
||||
bool? recursive,
|
||||
GroupBy? groupBy,
|
||||
Map<String, int>? lastIndices,
|
||||
Map<String, int>? libraryItemCounts,
|
||||
bool? fetchingItems});
|
||||
LibrarySearchModelCopyWith<$R2, $In, $Out2> $chain<$R2, $Out2>(
|
||||
Then<$Out2, $R2> t);
|
||||
}
|
||||
|
||||
class _LibrarySearchModelCopyWithImpl<$R, $Out>
|
||||
extends ClassCopyWithBase<$R, LibrarySearchModel, $Out>
|
||||
implements LibrarySearchModelCopyWith<$R, LibrarySearchModel, $Out> {
|
||||
_LibrarySearchModelCopyWithImpl(super.value, super.then, super.then2);
|
||||
|
||||
@override
|
||||
late final ClassMapperBase<LibrarySearchModel> $mapper =
|
||||
LibrarySearchModelMapper.ensureInitialized();
|
||||
@override
|
||||
ListCopyWith<$R, ItemBaseModel,
|
||||
ItemBaseModelCopyWith<$R, ItemBaseModel, ItemBaseModel>>
|
||||
get folderOverwrite => ListCopyWith($value.folderOverwrite,
|
||||
(v, t) => v.copyWith.$chain(t), (v) => call(folderOverwrite: v));
|
||||
@override
|
||||
MapCopyWith<$R, ViewModel, bool, ObjectCopyWith<$R, bool, bool>> get views =>
|
||||
MapCopyWith($value.views, (v, t) => ObjectCopyWith(v, $identity, t),
|
||||
(v) => call(views: v));
|
||||
@override
|
||||
ListCopyWith<$R, ItemBaseModel,
|
||||
ItemBaseModelCopyWith<$R, ItemBaseModel, ItemBaseModel>>
|
||||
get posters => ListCopyWith($value.posters,
|
||||
(v, t) => v.copyWith.$chain(t), (v) => call(posters: v));
|
||||
@override
|
||||
ListCopyWith<$R, ItemBaseModel,
|
||||
ItemBaseModelCopyWith<$R, ItemBaseModel, ItemBaseModel>>
|
||||
get selectedPosters => ListCopyWith($value.selectedPosters,
|
||||
(v, t) => v.copyWith.$chain(t), (v) => call(selectedPosters: v));
|
||||
@override
|
||||
MapCopyWith<$R, ItemFilter, bool, ObjectCopyWith<$R, bool, bool>>
|
||||
get filters => MapCopyWith($value.filters,
|
||||
(v, t) => ObjectCopyWith(v, $identity, t), (v) => call(filters: v));
|
||||
@override
|
||||
MapCopyWith<$R, String, bool, ObjectCopyWith<$R, bool, bool>> get genres =>
|
||||
MapCopyWith($value.genres, (v, t) => ObjectCopyWith(v, $identity, t),
|
||||
(v) => call(genres: v));
|
||||
@override
|
||||
MapCopyWith<$R, Studio, bool, ObjectCopyWith<$R, bool, bool>> get studios =>
|
||||
MapCopyWith($value.studios, (v, t) => ObjectCopyWith(v, $identity, t),
|
||||
(v) => call(studios: v));
|
||||
@override
|
||||
MapCopyWith<$R, String, bool, ObjectCopyWith<$R, bool, bool>> get tags =>
|
||||
MapCopyWith($value.tags, (v, t) => ObjectCopyWith(v, $identity, t),
|
||||
(v) => call(tags: v));
|
||||
@override
|
||||
MapCopyWith<$R, int, bool, ObjectCopyWith<$R, bool, bool>> get years =>
|
||||
MapCopyWith($value.years, (v, t) => ObjectCopyWith(v, $identity, t),
|
||||
(v) => call(years: v));
|
||||
@override
|
||||
MapCopyWith<$R, String, bool, ObjectCopyWith<$R, bool, bool>>
|
||||
get officialRatings => MapCopyWith(
|
||||
$value.officialRatings,
|
||||
(v, t) => ObjectCopyWith(v, $identity, t),
|
||||
(v) => call(officialRatings: v));
|
||||
@override
|
||||
MapCopyWith<$R, FladderItemType, bool, ObjectCopyWith<$R, bool, bool>>
|
||||
get types => MapCopyWith($value.types,
|
||||
(v, t) => ObjectCopyWith(v, $identity, t), (v) => call(types: v));
|
||||
@override
|
||||
MapCopyWith<$R, String, int, ObjectCopyWith<$R, int, int>> get lastIndices =>
|
||||
MapCopyWith($value.lastIndices, (v, t) => ObjectCopyWith(v, $identity, t),
|
||||
(v) => call(lastIndices: v));
|
||||
@override
|
||||
MapCopyWith<$R, String, int, ObjectCopyWith<$R, int, int>>
|
||||
get libraryItemCounts => MapCopyWith(
|
||||
$value.libraryItemCounts,
|
||||
(v, t) => ObjectCopyWith(v, $identity, t),
|
||||
(v) => call(libraryItemCounts: v));
|
||||
@override
|
||||
$R call(
|
||||
{bool? loading,
|
||||
bool? selecteMode,
|
||||
List<ItemBaseModel>? folderOverwrite,
|
||||
String? searchQuery,
|
||||
Map<ViewModel, bool>? views,
|
||||
List<ItemBaseModel>? posters,
|
||||
List<ItemBaseModel>? selectedPosters,
|
||||
Map<ItemFilter, bool>? filters,
|
||||
Map<String, bool>? genres,
|
||||
Map<Studio, bool>? studios,
|
||||
Map<String, bool>? tags,
|
||||
Map<int, bool>? years,
|
||||
Map<String, bool>? officialRatings,
|
||||
Map<FladderItemType, bool>? types,
|
||||
bool? favourites,
|
||||
SortingOptions? sortingOption,
|
||||
SortingOrder? sortOrder,
|
||||
bool? hideEmtpyShows,
|
||||
bool? recursive,
|
||||
GroupBy? groupBy,
|
||||
Map<String, int>? lastIndices,
|
||||
Map<String, int>? libraryItemCounts,
|
||||
bool? fetchingItems}) =>
|
||||
$apply(FieldCopyWithData({
|
||||
if (loading != null) #loading: loading,
|
||||
if (selecteMode != null) #selecteMode: selecteMode,
|
||||
if (folderOverwrite != null) #folderOverwrite: folderOverwrite,
|
||||
if (searchQuery != null) #searchQuery: searchQuery,
|
||||
if (views != null) #views: views,
|
||||
if (posters != null) #posters: posters,
|
||||
if (selectedPosters != null) #selectedPosters: selectedPosters,
|
||||
if (filters != null) #filters: filters,
|
||||
if (genres != null) #genres: genres,
|
||||
if (studios != null) #studios: studios,
|
||||
if (tags != null) #tags: tags,
|
||||
if (years != null) #years: years,
|
||||
if (officialRatings != null) #officialRatings: officialRatings,
|
||||
if (types != null) #types: types,
|
||||
if (favourites != null) #favourites: favourites,
|
||||
if (sortingOption != null) #sortingOption: sortingOption,
|
||||
if (sortOrder != null) #sortOrder: sortOrder,
|
||||
if (hideEmtpyShows != null) #hideEmtpyShows: hideEmtpyShows,
|
||||
if (recursive != null) #recursive: recursive,
|
||||
if (groupBy != null) #groupBy: groupBy,
|
||||
if (lastIndices != null) #lastIndices: lastIndices,
|
||||
if (libraryItemCounts != null) #libraryItemCounts: libraryItemCounts,
|
||||
if (fetchingItems != null) #fetchingItems: fetchingItems
|
||||
}));
|
||||
@override
|
||||
LibrarySearchModel $make(CopyWithData data) => LibrarySearchModel(
|
||||
loading: data.get(#loading, or: $value.loading),
|
||||
selecteMode: data.get(#selecteMode, or: $value.selecteMode),
|
||||
folderOverwrite: data.get(#folderOverwrite, or: $value.folderOverwrite),
|
||||
searchQuery: data.get(#searchQuery, or: $value.searchQuery),
|
||||
views: data.get(#views, or: $value.views),
|
||||
posters: data.get(#posters, or: $value.posters),
|
||||
selectedPosters: data.get(#selectedPosters, or: $value.selectedPosters),
|
||||
filters: data.get(#filters, or: $value.filters),
|
||||
genres: data.get(#genres, or: $value.genres),
|
||||
studios: data.get(#studios, or: $value.studios),
|
||||
tags: data.get(#tags, or: $value.tags),
|
||||
years: data.get(#years, or: $value.years),
|
||||
officialRatings: data.get(#officialRatings, or: $value.officialRatings),
|
||||
types: data.get(#types, or: $value.types),
|
||||
favourites: data.get(#favourites, or: $value.favourites),
|
||||
sortingOption: data.get(#sortingOption, or: $value.sortingOption),
|
||||
sortOrder: data.get(#sortOrder, or: $value.sortOrder),
|
||||
hideEmtpyShows: data.get(#hideEmtpyShows, or: $value.hideEmtpyShows),
|
||||
recursive: data.get(#recursive, or: $value.recursive),
|
||||
groupBy: data.get(#groupBy, or: $value.groupBy),
|
||||
lastIndices: data.get(#lastIndices, or: $value.lastIndices),
|
||||
libraryItemCounts:
|
||||
data.get(#libraryItemCounts, or: $value.libraryItemCounts),
|
||||
fetchingItems: data.get(#fetchingItems, or: $value.fetchingItems));
|
||||
|
||||
@override
|
||||
LibrarySearchModelCopyWith<$R2, LibrarySearchModel, $Out2> $chain<$R2, $Out2>(
|
||||
Then<$Out2, $R2> t) =>
|
||||
_LibrarySearchModelCopyWithImpl($value, $cast, t);
|
||||
}
|
||||
127
lib/models/library_search/library_search_options.dart
Normal file
127
lib/models/library_search/library_search_options.dart
Normal file
|
|
@ -0,0 +1,127 @@
|
|||
import 'package:fladder/models/item_base_model.dart';
|
||||
import 'package:fladder/util/localization_helper.dart';
|
||||
|
||||
import 'package:fladder/jellyfin/jellyfin_open_api.enums.swagger.dart';
|
||||
import 'package:fladder/jellyfin/jellyfin_open_api.swagger.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
enum SortingOptions {
|
||||
name([ItemSortBy.name]),
|
||||
communityRating([ItemSortBy.communityrating]),
|
||||
// criticsRating([ItemSortBy.criticrating]),
|
||||
parentalRating([ItemSortBy.officialrating]),
|
||||
dateAdded([ItemSortBy.datecreated]),
|
||||
dateLastContentAdded([ItemSortBy.datelastcontentadded]),
|
||||
favorite([ItemSortBy.isfavoriteorliked]),
|
||||
datePlayed([ItemSortBy.dateplayed]),
|
||||
folders([ItemSortBy.isfolder]),
|
||||
playCount([ItemSortBy.playcount]),
|
||||
releaseDate([ItemSortBy.productionyear, ItemSortBy.premieredate]),
|
||||
runTime([ItemSortBy.runtime]),
|
||||
random([ItemSortBy.random]);
|
||||
|
||||
const SortingOptions(this.value);
|
||||
final List<ItemSortBy> value;
|
||||
|
||||
List<ItemSortBy> get toSortBy => [...value, ItemSortBy.name];
|
||||
|
||||
String label(BuildContext context) => switch (this) {
|
||||
name => context.localized.name,
|
||||
communityRating => context.localized.communityRating,
|
||||
parentalRating => context.localized.parentalRating,
|
||||
dateAdded => context.localized.dateAdded,
|
||||
dateLastContentAdded => context.localized.dateLastContentAdded,
|
||||
favorite => context.localized.favorite,
|
||||
datePlayed => context.localized.datePlayed,
|
||||
folders => context.localized.folders,
|
||||
playCount => context.localized.playCount,
|
||||
releaseDate => context.localized.releaseDate,
|
||||
runTime => context.localized.runTime,
|
||||
random => context.localized.random,
|
||||
};
|
||||
}
|
||||
|
||||
enum GroupBy {
|
||||
none,
|
||||
name,
|
||||
genres,
|
||||
dateAdded,
|
||||
tags,
|
||||
releaseDate,
|
||||
rating,
|
||||
type;
|
||||
|
||||
String value(BuildContext context) => switch (this) {
|
||||
GroupBy.none => context.localized.none,
|
||||
GroupBy.name => context.localized.name,
|
||||
GroupBy.genres => context.localized.genre(1),
|
||||
GroupBy.dateAdded => context.localized.dateAdded,
|
||||
GroupBy.tags => context.localized.tag(1),
|
||||
GroupBy.releaseDate => context.localized.releaseDate,
|
||||
GroupBy.rating => context.localized.rating(1),
|
||||
GroupBy.type => context.localized.type(1),
|
||||
};
|
||||
}
|
||||
|
||||
enum SortingOrder {
|
||||
ascending,
|
||||
descending;
|
||||
|
||||
SortOrder get sortOrder => switch (this) {
|
||||
ascending => SortOrder.ascending,
|
||||
descending => SortOrder.descending,
|
||||
};
|
||||
|
||||
String label(BuildContext context) => switch (this) {
|
||||
ascending => context.localized.ascending,
|
||||
descending => context.localized.descending,
|
||||
};
|
||||
}
|
||||
|
||||
extension ItemFilterExtension on ItemFilter {
|
||||
String label(BuildContext context) {
|
||||
return switch (this) {
|
||||
ItemFilter.isplayed => context.localized.played,
|
||||
ItemFilter.isunplayed => context.localized.unPlayed,
|
||||
ItemFilter.isresumable => context.localized.resumable,
|
||||
_ => "",
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
int sortItems(ItemBaseModel a, ItemBaseModel b, SortingOptions sortingOption, SortingOrder sortingOrder) {
|
||||
for (var sortBy in sortingOption.toSortBy) {
|
||||
int comparison = 0;
|
||||
switch (sortBy) {
|
||||
case ItemSortBy.communityrating:
|
||||
comparison = (a.overview.communityRating ?? 0).compareTo(b.overview.communityRating ?? 0);
|
||||
break;
|
||||
case ItemSortBy.isfavoriteorliked:
|
||||
comparison = a.userData.isFavourite == b.userData.isFavourite
|
||||
? 0
|
||||
: a.userData.isFavourite
|
||||
? 1
|
||||
: -1;
|
||||
break;
|
||||
case ItemSortBy.dateplayed:
|
||||
comparison = (a.userData.lastPlayed ?? DateTime(0)).compareTo(b.userData.lastPlayed ?? DateTime(0));
|
||||
break;
|
||||
case ItemSortBy.playcount:
|
||||
comparison = a.userData.playCount.compareTo(b.userData.playCount);
|
||||
break;
|
||||
case ItemSortBy.premieredate:
|
||||
case ItemSortBy.productionyear:
|
||||
comparison = (a.overview.productionYear ?? 0).compareTo(b.overview.productionYear ?? 0);
|
||||
break;
|
||||
case ItemSortBy.runtime:
|
||||
comparison = (a.overview.runTime ?? Duration.zero).compareTo(b.overview.runTime ?? Duration.zero);
|
||||
break;
|
||||
default:
|
||||
comparison = a.name.compareTo(b.name);
|
||||
}
|
||||
if (comparison != 0) {
|
||||
return sortingOrder == SortingOrder.ascending ? comparison : -comparison;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
26
lib/models/login_screen_model.dart
Normal file
26
lib/models/login_screen_model.dart
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
// ignore_for_file: public_member_api_docs, sort_constructors_first
|
||||
import 'package:fladder/models/account_model.dart';
|
||||
import 'package:fladder/models/credentials_model.dart';
|
||||
|
||||
class LoginScreenModel {
|
||||
final List<AccountModel> accounts;
|
||||
final CredentialsModel tempCredentials;
|
||||
final bool loading;
|
||||
LoginScreenModel({
|
||||
required this.accounts,
|
||||
required this.tempCredentials,
|
||||
required this.loading,
|
||||
});
|
||||
|
||||
LoginScreenModel copyWith({
|
||||
List<AccountModel>? accounts,
|
||||
CredentialsModel? tempCredentials,
|
||||
bool? loading,
|
||||
}) {
|
||||
return LoginScreenModel(
|
||||
accounts: accounts ?? this.accounts,
|
||||
tempCredentials: tempCredentials ?? this.tempCredentials,
|
||||
loading: loading ?? this.loading,
|
||||
);
|
||||
}
|
||||
}
|
||||
52
lib/models/media_playback_model.dart
Normal file
52
lib/models/media_playback_model.dart
Normal file
|
|
@ -0,0 +1,52 @@
|
|||
enum VideoPlayerState {
|
||||
minimized,
|
||||
fullScreen,
|
||||
disposed,
|
||||
}
|
||||
|
||||
class MediaPlaybackModel {
|
||||
final VideoPlayerState state;
|
||||
final bool playing;
|
||||
final Duration position;
|
||||
final Duration lastPosition;
|
||||
final Duration duration;
|
||||
final Duration buffer;
|
||||
final bool completed;
|
||||
final bool errorPlaying;
|
||||
final bool buffering;
|
||||
MediaPlaybackModel({
|
||||
this.state = VideoPlayerState.disposed,
|
||||
this.playing = false,
|
||||
this.position = Duration.zero,
|
||||
this.lastPosition = Duration.zero,
|
||||
this.duration = Duration.zero,
|
||||
this.buffer = Duration.zero,
|
||||
this.completed = false,
|
||||
this.errorPlaying = false,
|
||||
this.buffering = false,
|
||||
});
|
||||
|
||||
MediaPlaybackModel copyWith({
|
||||
VideoPlayerState? state,
|
||||
bool? playing,
|
||||
Duration? position,
|
||||
Duration? lastPosition,
|
||||
Duration? duration,
|
||||
Duration? buffer,
|
||||
bool? completed,
|
||||
bool? errorPlaying,
|
||||
bool? buffering,
|
||||
}) {
|
||||
return MediaPlaybackModel(
|
||||
state: state ?? this.state,
|
||||
playing: playing ?? this.playing,
|
||||
position: position ?? this.position,
|
||||
lastPosition: lastPosition ?? this.lastPosition,
|
||||
duration: duration ?? this.duration,
|
||||
buffer: buffer ?? this.buffer,
|
||||
completed: completed ?? this.completed,
|
||||
errorPlaying: errorPlaying ?? this.errorPlaying,
|
||||
buffering: buffering ?? this.buffering,
|
||||
);
|
||||
}
|
||||
}
|
||||
209
lib/models/playback/direct_playback_model.dart
Normal file
209
lib/models/playback/direct_playback_model.dart
Normal file
|
|
@ -0,0 +1,209 @@
|
|||
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: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_streams_model.dart';
|
||||
import 'package:fladder/models/playback/playback_model.dart';
|
||||
import 'package:fladder/providers/api_provider.dart';
|
||||
import 'package:fladder/providers/video_player_provider.dart';
|
||||
import 'package:fladder/util/duration_extensions.dart';
|
||||
|
||||
class DirectPlaybackModel implements PlaybackModel {
|
||||
DirectPlaybackModel({
|
||||
required this.item,
|
||||
required this.media,
|
||||
required this.playbackInfo,
|
||||
this.mediaStreams,
|
||||
this.introSkipModel,
|
||||
this.chapters,
|
||||
this.trickPlay,
|
||||
this.queue = const [],
|
||||
});
|
||||
|
||||
@override
|
||||
final ItemBaseModel item;
|
||||
|
||||
@override
|
||||
final Media? media;
|
||||
|
||||
@override
|
||||
final PlaybackInfoResponse playbackInfo;
|
||||
|
||||
@override
|
||||
final MediaStreamsModel? mediaStreams;
|
||||
|
||||
@override
|
||||
final IntroOutSkipModel? introSkipModel;
|
||||
|
||||
@override
|
||||
final List<Chapter>? chapters;
|
||||
|
||||
@override
|
||||
final TrickPlayModel? trickPlay;
|
||||
|
||||
@override
|
||||
ItemBaseModel? get nextVideo => queue.nextOrNull(item);
|
||||
|
||||
@override
|
||||
ItemBaseModel? get previousVideo => queue.previousOrNull(item);
|
||||
|
||||
@override
|
||||
Future<Duration>? startDuration() async => item.userData.playBackPosition;
|
||||
|
||||
@override
|
||||
List<SubStreamModel> get subStreams => [SubStreamModel.no(), ...mediaStreams?.subStreams ?? []];
|
||||
|
||||
List<QueueItem> get itemsInQueue =>
|
||||
queue.mapIndexed((index, element) => QueueItem(id: element.id, playlistItemId: "playlistItem$index")).toList();
|
||||
|
||||
@override
|
||||
Future<DirectPlaybackModel> setSubtitle(SubStreamModel? model, MediaControlsWrapper player) async {
|
||||
final wantedSubtitle =
|
||||
model ?? subStreams.firstWhereOrNull((element) => element.index == mediaStreams?.defaultSubStreamIndex);
|
||||
if (wantedSubtitle == null) return this;
|
||||
if (wantedSubtitle.index == SubStreamModel.no().index) {
|
||||
await player.setSubtitleTrack(SubtitleTrack.no());
|
||||
} else {
|
||||
final subTracks = player.subTracks.getRange(2, player.subTracks.length).toList();
|
||||
final index = subStreams.sublist(1).indexWhere((element) => element.id == wantedSubtitle.id);
|
||||
final subTrack = subTracks.elementAtOrNull(index);
|
||||
if (wantedSubtitle.isExternal && wantedSubtitle.url != null && subTrack == null) {
|
||||
await player.setSubtitleTrack(SubtitleTrack.uri(wantedSubtitle.url!));
|
||||
} else if (subTrack != null) {
|
||||
await player.setSubtitleTrack(subTrack);
|
||||
}
|
||||
}
|
||||
return copyWith(mediaStreams: () => mediaStreams?.copyWith(defaultSubStreamIndex: wantedSubtitle.index));
|
||||
}
|
||||
|
||||
@override
|
||||
List<AudioStreamModel> get audioStreams => [AudioStreamModel.no(), ...mediaStreams?.audioStreams ?? []];
|
||||
|
||||
@override
|
||||
Future<DirectPlaybackModel>? setAudio(AudioStreamModel? model, MediaControlsWrapper player) async {
|
||||
final wantedAudioStream =
|
||||
model ?? audioStreams.firstWhereOrNull((element) => element.index == mediaStreams?.defaultAudioStreamIndex);
|
||||
if (wantedAudioStream == null) return this;
|
||||
if (wantedAudioStream.index == AudioStreamModel.no().index) {
|
||||
await player.setAudioTrack(AudioTrack.no());
|
||||
} else {
|
||||
final audioTracks = player.audioTracks.getRange(2, player.audioTracks.length).toList();
|
||||
final audioTrack = audioTracks.elementAtOrNull(audioStreams.indexOf(wantedAudioStream) - 1);
|
||||
if (audioTrack != null) {
|
||||
await player.setAudioTrack(audioTrack);
|
||||
}
|
||||
}
|
||||
return copyWith(mediaStreams: () => mediaStreams?.copyWith(defaultAudioStreamIndex: wantedAudioStream.index));
|
||||
}
|
||||
|
||||
@override
|
||||
Future<PlaybackModel?> playbackStarted(Duration position, Ref ref) async {
|
||||
await ref.read(jellyApiProvider).sessionsPlayingPost(
|
||||
body: PlaybackStartInfo(
|
||||
canSeek: true,
|
||||
itemId: item.id,
|
||||
mediaSourceId: item.id,
|
||||
playSessionId: playbackInfo.playSessionId,
|
||||
subtitleStreamIndex: item.streamModel?.defaultSubStreamIndex,
|
||||
audioStreamIndex: item.streamModel?.defaultAudioStreamIndex,
|
||||
volumeLevel: 100,
|
||||
playbackStartTimeTicks: position.toRuntimeTicks,
|
||||
playMethod: PlayMethod.directplay,
|
||||
isMuted: false,
|
||||
isPaused: false,
|
||||
repeatMode: RepeatMode.repeatall,
|
||||
nowPlayingQueue: itemsInQueue,
|
||||
),
|
||||
);
|
||||
return null;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<PlaybackModel?> playbackStopped(Duration position, Duration? totalDuration, Ref ref) async {
|
||||
ref.read(playBackModel.notifier).update((state) => null);
|
||||
|
||||
await ref.read(jellyApiProvider).sessionsPlayingStoppedPost(
|
||||
body: PlaybackStopInfo(
|
||||
itemId: item.id,
|
||||
mediaSourceId: item.id,
|
||||
playSessionId: playbackInfo.playSessionId,
|
||||
positionTicks: position.toRuntimeTicks,
|
||||
failed: false,
|
||||
nowPlayingQueue: itemsInQueue,
|
||||
),
|
||||
totalDuration: totalDuration,
|
||||
);
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<PlaybackModel?> updatePlaybackPosition(Duration position, bool isPlaying, Ref ref) async {
|
||||
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(
|
||||
body: PlaybackProgressInfo(
|
||||
canSeek: true,
|
||||
itemId: item.id,
|
||||
mediaSourceId: item.id,
|
||||
playSessionId: playbackInfo.playSessionId,
|
||||
subtitleStreamIndex: item.streamModel?.defaultSubStreamIndex,
|
||||
audioStreamIndex: item.streamModel?.defaultAudioStreamIndex,
|
||||
volumeLevel: 100,
|
||||
playMethod: PlayMethod.directplay,
|
||||
isPaused: !isPlaying,
|
||||
isMuted: false,
|
||||
positionTicks: position.toRuntimeTicks,
|
||||
repeatMode: RepeatMode.repeatall,
|
||||
nowPlayingQueue: itemsInQueue,
|
||||
),
|
||||
);
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@override
|
||||
String toString() => 'DirectPlaybackModel(item: $item, playbackInfo: $playbackInfo)';
|
||||
|
||||
@override
|
||||
final List<ItemBaseModel> queue;
|
||||
|
||||
@override
|
||||
DirectPlaybackModel copyWith({
|
||||
ItemBaseModel? item,
|
||||
ValueGetter<Media?>? media,
|
||||
ValueGetter<Duration>? lastPosition,
|
||||
PlaybackInfoResponse? playbackInfo,
|
||||
ValueGetter<MediaStreamsModel?>? mediaStreams,
|
||||
ValueGetter<IntroOutSkipModel?>? introSkipModel,
|
||||
ValueGetter<List<Chapter>?>? chapters,
|
||||
ValueGetter<TrickPlayModel?>? trickPlay,
|
||||
List<ItemBaseModel>? queue,
|
||||
}) {
|
||||
return DirectPlaybackModel(
|
||||
item: item ?? this.item,
|
||||
media: media != null ? media() : this.media,
|
||||
playbackInfo: playbackInfo ?? this.playbackInfo,
|
||||
mediaStreams: mediaStreams != null ? mediaStreams() : this.mediaStreams,
|
||||
introSkipModel: introSkipModel != null ? introSkipModel() : this.introSkipModel,
|
||||
chapters: chapters != null ? chapters() : this.chapters,
|
||||
trickPlay: trickPlay != null ? trickPlay() : this.trickPlay,
|
||||
queue: queue ?? this.queue,
|
||||
);
|
||||
}
|
||||
}
|
||||
175
lib/models/playback/offline_playback_model.dart
Normal file
175
lib/models/playback/offline_playback_model.dart
Normal file
|
|
@ -0,0 +1,175 @@
|
|||
import 'package:collection/collection.dart';
|
||||
import 'package:fladder/jellyfin/jellyfin_open_api.swagger.dart';
|
||||
import 'package:fladder/models/items/trick_play_model.dart';
|
||||
import 'package:fladder/models/syncing/sync_item.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({
|
||||
required this.item,
|
||||
required this.media,
|
||||
required this.syncedItem,
|
||||
this.mediaStreams,
|
||||
this.playbackInfo,
|
||||
this.introSkipModel,
|
||||
this.trickPlay,
|
||||
this.queue = const [],
|
||||
this.syncedQueue = const [],
|
||||
});
|
||||
|
||||
@override
|
||||
final ItemBaseModel item;
|
||||
|
||||
@override
|
||||
final PlaybackInfoResponse? playbackInfo;
|
||||
|
||||
@override
|
||||
final Media? media;
|
||||
|
||||
final SyncedItem syncedItem;
|
||||
|
||||
@override
|
||||
final MediaStreamsModel? mediaStreams;
|
||||
|
||||
@override
|
||||
final IntroOutSkipModel? introSkipModel;
|
||||
|
||||
@override
|
||||
List<Chapter>? get chapters => syncedItem.chapters;
|
||||
|
||||
@override
|
||||
final TrickPlayModel? trickPlay;
|
||||
|
||||
@override
|
||||
Future<Duration>? startDuration() async => item.userData.playBackPosition;
|
||||
|
||||
@override
|
||||
ItemBaseModel? get nextVideo => queue.nextOrNull(item);
|
||||
@override
|
||||
ItemBaseModel? get previousVideo => queue.previousOrNull(item);
|
||||
|
||||
@override
|
||||
List<SubStreamModel> get subStreams => [SubStreamModel.no(), ...syncedItem.subtitles];
|
||||
|
||||
@override
|
||||
Future<OfflinePlaybackModel> setSubtitle(SubStreamModel? model, MediaControlsWrapper player) async {
|
||||
final wantedSubtitle =
|
||||
model ?? subStreams.firstWhereOrNull((element) => element.index == mediaStreams?.defaultSubStreamIndex);
|
||||
if (wantedSubtitle == null) return this;
|
||||
if (wantedSubtitle.index == SubStreamModel.no().index) {
|
||||
await player.setSubtitleTrack(SubtitleTrack.no());
|
||||
} else {
|
||||
final subTracks = player.subTracks.getRange(2, player.subTracks.length).toList();
|
||||
final index = subStreams.sublist(1).indexWhere((element) => element.id == wantedSubtitle.id);
|
||||
final subTrack = subTracks.elementAtOrNull(index);
|
||||
if (wantedSubtitle.isExternal && wantedSubtitle.url != null && subTrack == null) {
|
||||
await player.setSubtitleTrack(SubtitleTrack.uri(wantedSubtitle.url!));
|
||||
} else if (subTrack != null) {
|
||||
await player.setSubtitleTrack(subTrack);
|
||||
}
|
||||
}
|
||||
return copyWith(mediaStreams: () => mediaStreams?.copyWith(defaultSubStreamIndex: wantedSubtitle.index));
|
||||
}
|
||||
|
||||
@override
|
||||
List<AudioStreamModel> get audioStreams => [AudioStreamModel.no(), ...mediaStreams?.audioStreams ?? []];
|
||||
|
||||
@override
|
||||
Future<OfflinePlaybackModel>? setAudio(AudioStreamModel? model, MediaControlsWrapper player) async {
|
||||
final wantedAudioStream =
|
||||
model ?? audioStreams.firstWhereOrNull((element) => element.index == mediaStreams?.defaultAudioStreamIndex);
|
||||
if (wantedAudioStream == null) return this;
|
||||
if (wantedAudioStream.index == AudioStreamModel.no().index) {
|
||||
await player.setAudioTrack(AudioTrack.no());
|
||||
} else {
|
||||
final audioTracks = player.audioTracks.getRange(2, player.audioTracks.length).toList();
|
||||
final audioTrack = audioTracks.elementAtOrNull(audioStreams.indexOf(wantedAudioStream) - 1);
|
||||
if (audioTrack != null) {
|
||||
await player.setAudioTrack(audioTrack);
|
||||
}
|
||||
}
|
||||
return copyWith(mediaStreams: () => mediaStreams?.copyWith(defaultAudioStreamIndex: wantedAudioStream.index));
|
||||
}
|
||||
|
||||
@override
|
||||
Future<PlaybackModel?> playbackStarted(Duration position, Ref ref) async {
|
||||
return null;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<PlaybackModel?> playbackStopped(Duration position, Duration? totalDuration, Ref ref) async {
|
||||
return null;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<PlaybackModel?> updatePlaybackPosition(Duration position, bool isPlaying, Ref ref) async {
|
||||
final progress = position.inMilliseconds / (item.overview.runTime?.inMilliseconds ?? 0) * 100;
|
||||
final newItem = syncedItem.copyWith(
|
||||
userData: syncedItem.userData?.copyWith(
|
||||
playbackPositionTicks: position.toRuntimeTicks,
|
||||
progress: progress,
|
||||
played: isPlayed(position, item.overview.runTime ?? Duration.zero),
|
||||
),
|
||||
);
|
||||
await ref.read(syncProvider.notifier).updateItem(newItem);
|
||||
return this;
|
||||
}
|
||||
|
||||
bool isPlayed(Duration position, Duration totalDuration) {
|
||||
Duration startBuffer = totalDuration * 0.05;
|
||||
Duration endBuffer = totalDuration * 0.90;
|
||||
|
||||
Duration validStart = startBuffer;
|
||||
Duration validEnd = endBuffer;
|
||||
|
||||
if (position >= validStart && position <= validEnd) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@override
|
||||
String toString() => 'OfflinePlaybackModel(item: $item, syncedItem: $syncedItem)';
|
||||
|
||||
@override
|
||||
final List<ItemBaseModel> queue;
|
||||
|
||||
final List<SyncedItem> syncedQueue;
|
||||
|
||||
@override
|
||||
OfflinePlaybackModel copyWith({
|
||||
ItemBaseModel? item,
|
||||
ValueGetter<Media?>? media,
|
||||
SyncedItem? syncedItem,
|
||||
ValueGetter<MediaStreamsModel?>? mediaStreams,
|
||||
ValueGetter<IntroOutSkipModel?>? introSkipModel,
|
||||
ValueGetter<TrickPlayModel?>? trickPlay,
|
||||
List<ItemBaseModel>? queue,
|
||||
List<SyncedItem>? syncedQueue,
|
||||
}) {
|
||||
return OfflinePlaybackModel(
|
||||
item: item ?? this.item,
|
||||
media: media != null ? media() : this.media,
|
||||
syncedItem: syncedItem ?? this.syncedItem,
|
||||
mediaStreams: mediaStreams != null ? mediaStreams() : this.mediaStreams,
|
||||
introSkipModel: introSkipModel != null ? introSkipModel() : this.introSkipModel,
|
||||
trickPlay: trickPlay != null ? trickPlay() : this.trickPlay,
|
||||
queue: queue ?? this.queue,
|
||||
syncedQueue: syncedQueue ?? this.syncedQueue,
|
||||
);
|
||||
}
|
||||
}
|
||||
370
lib/models/playback/playback_model.dart
Normal file
370
lib/models/playback/playback_model.dart
Normal file
|
|
@ -0,0 +1,370 @@
|
|||
import 'dart:developer';
|
||||
|
||||
import 'package:chopper/chopper.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: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/episode_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/direct_playback_model.dart';
|
||||
import 'package:fladder/models/playback/offline_playback_model.dart';
|
||||
import 'package:fladder/models/playback/transcode_playback_model.dart';
|
||||
import 'package:fladder/models/syncing/sync_item.dart';
|
||||
import 'package:fladder/models/video_stream_model.dart';
|
||||
import 'package:fladder/profiles/default_profile.dart';
|
||||
import 'package:fladder/providers/api_provider.dart';
|
||||
import 'package:fladder/providers/service_provider.dart';
|
||||
import 'package:fladder/providers/sync/sync_provider_helpers.dart';
|
||||
import 'package:fladder/providers/sync_provider.dart';
|
||||
import 'package:fladder/providers/user_provider.dart';
|
||||
import 'package:fladder/providers/video_player_provider.dart';
|
||||
import 'package:fladder/util/duration_extensions.dart';
|
||||
import 'package:fladder/wrappers/media_control_wrapper.dart'
|
||||
if (dart.library.html) 'package:fladder/wrappers/media_control_wrapper_web.dart';
|
||||
|
||||
extension PlaybackModelExtension on PlaybackModel? {
|
||||
String? get label => switch (this) {
|
||||
DirectPlaybackModel _ => PlaybackType.directStream.name,
|
||||
TranscodePlaybackModel _ => PlaybackType.transcode.name,
|
||||
OfflinePlaybackModel _ => PlaybackType.offline.name,
|
||||
_ => null
|
||||
};
|
||||
}
|
||||
|
||||
abstract class PlaybackModel {
|
||||
final ItemBaseModel item = throw UnimplementedError();
|
||||
final Media? media = throw UnimplementedError();
|
||||
final List<ItemBaseModel> queue = const [];
|
||||
final IntroOutSkipModel? introSkipModel = null;
|
||||
final PlaybackInfoResponse? playbackInfo = throw UnimplementedError();
|
||||
|
||||
List<Chapter>? get chapters;
|
||||
TrickPlayModel? get trickPlay;
|
||||
|
||||
Future<PlaybackModel?> updatePlaybackPosition(Duration position, bool isPlaying, Ref ref) =>
|
||||
throw UnimplementedError();
|
||||
Future<PlaybackModel?> playbackStarted(Duration position, Ref ref) => throw UnimplementedError();
|
||||
Future<PlaybackModel?> playbackStopped(Duration position, Duration? totalDuration, Ref ref) =>
|
||||
throw UnimplementedError();
|
||||
|
||||
final MediaStreamsModel? mediaStreams = throw UnimplementedError();
|
||||
List<SubStreamModel>? get subStreams;
|
||||
List<AudioStreamModel>? get audioStreams;
|
||||
|
||||
Future<Duration>? startDuration() async => item.userData.playBackPosition;
|
||||
Future<PlaybackModel>? setSubtitle(SubStreamModel? model, MediaControlsWrapper player) {
|
||||
return null;
|
||||
}
|
||||
|
||||
Future<PlaybackModel>? setAudio(AudioStreamModel? model, MediaControlsWrapper player) => null;
|
||||
|
||||
ItemBaseModel? get nextVideo => throw UnimplementedError();
|
||||
ItemBaseModel? get previousVideo => throw UnimplementedError();
|
||||
|
||||
PlaybackModel copyWith();
|
||||
}
|
||||
|
||||
final playbackModelHelper = Provider<PlaybackModelHelper>((ref) {
|
||||
return PlaybackModelHelper(ref: ref);
|
||||
});
|
||||
|
||||
class PlaybackModelHelper {
|
||||
const PlaybackModelHelper({required this.ref});
|
||||
|
||||
final Ref ref;
|
||||
|
||||
JellyService get api => ref.read(jellyApiProvider);
|
||||
|
||||
Future<PlaybackModel?> loadNewVideo(ItemBaseModel newItem) async {
|
||||
ref.read(videoPlayerProvider).pause();
|
||||
ref.read(mediaPlaybackProvider.notifier).update((state) => state.copyWith(buffering: true));
|
||||
final currentModel = ref.read(playBackModel);
|
||||
final newModel = (await createServerPlaybackModel(
|
||||
newItem,
|
||||
null,
|
||||
oldModel: currentModel,
|
||||
)) ??
|
||||
await createOfflinePlaybackModel(
|
||||
newItem,
|
||||
ref.read(syncProvider.notifier).getSyncedItem(newItem),
|
||||
oldModel: currentModel,
|
||||
);
|
||||
if (newModel == null) return null;
|
||||
ref.read(videoPlayerProvider.notifier).loadPlaybackItem(newModel, startPosition: Duration.zero);
|
||||
return newModel;
|
||||
}
|
||||
|
||||
Future<OfflinePlaybackModel?> createOfflinePlaybackModel(
|
||||
ItemBaseModel item,
|
||||
SyncedItem? syncedItem, {
|
||||
PlaybackModel? oldModel,
|
||||
}) async {
|
||||
final ItemBaseModel? syncedItemModel = ref.read(syncProvider.notifier).getItem(syncedItem);
|
||||
if (syncedItemModel == null || syncedItem == null || !syncedItem.dataFile.existsSync()) return null;
|
||||
|
||||
final children = ref.read(syncChildrenProvider(syncedItem));
|
||||
final syncedItems = children.where((element) => element.videoFile.existsSync()).toList();
|
||||
final itemQueue = syncedItems.map((e) => e.createItemModel(ref));
|
||||
|
||||
return OfflinePlaybackModel(
|
||||
item: syncedItemModel,
|
||||
syncedItem: syncedItem,
|
||||
trickPlay: syncedItem.trickPlayModel,
|
||||
introSkipModel: syncedItem.introOutSkipModel,
|
||||
media: Media(syncedItem.videoFile.path),
|
||||
queue: itemQueue.whereNotNull().toList(),
|
||||
syncedQueue: children,
|
||||
mediaStreams: item.streamModel ?? syncedItemModel.streamModel,
|
||||
);
|
||||
}
|
||||
|
||||
Future<EpisodeModel?> getNextUpEpisode(String itemId) async {
|
||||
final responnse = await api.showsNextUpGet(parentId: itemId, fields: [ItemFields.overview]);
|
||||
final episode = responnse.body?.items?.firstOrNull;
|
||||
if (episode == null) {
|
||||
return null;
|
||||
} else {
|
||||
return EpisodeModel.fromBaseDto(episode, ref);
|
||||
}
|
||||
}
|
||||
|
||||
Future<PlaybackModel?> createServerPlaybackModel(ItemBaseModel? item, PlaybackType? type,
|
||||
{PlaybackModel? oldModel, List<ItemBaseModel>? libraryQueue, Duration? startPosition}) async {
|
||||
try {
|
||||
if (item == null) return null;
|
||||
final userId = ref.read(userProvider)?.id;
|
||||
if (userId?.isEmpty == true) return null;
|
||||
|
||||
final queue = oldModel?.queue ?? libraryQueue ?? await collectQueue(item);
|
||||
|
||||
final firstItemToPlay = switch (item) {
|
||||
SeriesModel _ || SeasonModel _ => (await getNextUpEpisode(item.id) ?? queue.first),
|
||||
_ => item,
|
||||
};
|
||||
|
||||
final fullItem = await api.usersUserIdItemsItemIdGet(itemId: firstItemToPlay.id);
|
||||
|
||||
final streamModel = firstItemToPlay.streamModel;
|
||||
|
||||
Response<PlaybackInfoResponse> response = await api.itemsItemIdPlaybackInfoPost(
|
||||
itemId: firstItemToPlay.id,
|
||||
enableDirectPlay: type != PlaybackType.transcode,
|
||||
enableDirectStream: type != PlaybackType.transcode,
|
||||
enableTranscoding: true,
|
||||
autoOpenLiveStream: true,
|
||||
startTimeTicks: startPosition?.toRuntimeTicks,
|
||||
audioStreamIndex: streamModel?.defaultAudioStreamIndex,
|
||||
subtitleStreamIndex: streamModel?.defaultSubStreamIndex,
|
||||
mediaSourceId: firstItemToPlay.id,
|
||||
body: PlaybackInfoDto(
|
||||
startTimeTicks: startPosition?.toRuntimeTicks,
|
||||
audioStreamIndex: streamModel?.defaultAudioStreamIndex,
|
||||
subtitleStreamIndex: streamModel?.defaultSubStreamIndex,
|
||||
enableTranscoding: true,
|
||||
autoOpenLiveStream: true,
|
||||
deviceProfile: defaultProfile,
|
||||
userId: userId,
|
||||
mediaSourceId: firstItemToPlay.id,
|
||||
),
|
||||
);
|
||||
|
||||
PlaybackInfoResponse? playbackInfo = response.body;
|
||||
if (playbackInfo == null) return null;
|
||||
|
||||
final mediaSource = playbackInfo.mediaSources?.first;
|
||||
|
||||
final mediaStreamsWithUrls = MediaStreamsModel.fromMediaStreamsList(
|
||||
playbackInfo.mediaSources?.firstOrNull, playbackInfo.mediaSources?.firstOrNull?.mediaStreams ?? [], ref)
|
||||
.copyWith(
|
||||
defaultAudioStreamIndex: streamModel?.defaultAudioStreamIndex,
|
||||
defaultSubStreamIndex: streamModel?.defaultSubStreamIndex,
|
||||
);
|
||||
|
||||
final intro = await api.introSkipGet(id: item.id);
|
||||
final trickPlay = (await api.getTrickPlay(item: fullItem.body, ref: ref))?.body;
|
||||
final chapters = fullItem.body?.overview.chapters ?? [];
|
||||
|
||||
if (mediaSource == null) return null;
|
||||
|
||||
if ((mediaSource.supportsDirectStream ?? false) || (mediaSource.supportsDirectPlay ?? false)) {
|
||||
final Map<String, String?> directOptions = {
|
||||
'Static': 'true',
|
||||
'mediaSourceId': mediaSource.id,
|
||||
'api_key': ref.read(userProvider)?.credentials.token,
|
||||
};
|
||||
|
||||
if (mediaSource.eTag != null) {
|
||||
directOptions['Tag'] = mediaSource.eTag;
|
||||
}
|
||||
|
||||
if (mediaSource.liveStreamId != null) {
|
||||
directOptions['LiveStreamId'] = mediaSource.liveStreamId;
|
||||
}
|
||||
|
||||
final params = Uri(queryParameters: directOptions).query;
|
||||
|
||||
return DirectPlaybackModel(
|
||||
item: fullItem.body ?? item,
|
||||
queue: queue,
|
||||
introSkipModel: intro?.body,
|
||||
chapters: chapters,
|
||||
playbackInfo: playbackInfo,
|
||||
trickPlay: trickPlay,
|
||||
media: Media('${ref.read(userProvider)?.server ?? ""}/Videos/${mediaSource.id}/stream?$params'),
|
||||
mediaStreams: mediaStreamsWithUrls,
|
||||
);
|
||||
} else if ((mediaSource.supportsTranscoding ?? false) && mediaSource.transcodingUrl != null) {
|
||||
return TranscodePlaybackModel(
|
||||
item: fullItem.body ?? item,
|
||||
queue: queue,
|
||||
introSkipModel: intro?.body,
|
||||
chapters: chapters,
|
||||
trickPlay: trickPlay,
|
||||
playbackInfo: playbackInfo,
|
||||
media: Media("${ref.read(userProvider)?.server ?? ""}${mediaSource.transcodingUrl ?? ""}"),
|
||||
mediaStreams: mediaStreamsWithUrls,
|
||||
);
|
||||
}
|
||||
return null;
|
||||
} catch (e) {
|
||||
log(e.toString());
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
Future<List<ItemBaseModel>> collectQueue(ItemBaseModel model) async {
|
||||
switch (model) {
|
||||
case EpisodeModel _:
|
||||
case SeriesModel _:
|
||||
case SeasonModel _:
|
||||
List<EpisodeModel> episodeList = ((await fetchEpisodesFromSeries(model.streamId)).body ?? [])
|
||||
..removeWhere((element) => element.status != EpisodeStatus.available);
|
||||
return episodeList;
|
||||
default:
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
Future<Response<List<EpisodeModel>>> fetchEpisodesFromSeries(String seriesId) async {
|
||||
final response = await api.showsSeriesIdEpisodesGet(
|
||||
seriesId: seriesId,
|
||||
fields: [
|
||||
ItemFields.overview,
|
||||
ItemFields.originaltitle,
|
||||
ItemFields.mediastreams,
|
||||
ItemFields.mediasources,
|
||||
ItemFields.mediasourcecount,
|
||||
ItemFields.width,
|
||||
ItemFields.height,
|
||||
],
|
||||
);
|
||||
return Response(response.base, (response.body?.items?.map((e) => EpisodeModel.fromBaseDto(e, ref)).toList() ?? []));
|
||||
}
|
||||
|
||||
Future<void> shouldReload(PlaybackModel playbackModel) async {
|
||||
if (playbackModel is OfflinePlaybackModel) {
|
||||
return;
|
||||
}
|
||||
|
||||
final item = playbackModel.item;
|
||||
|
||||
final userId = ref.read(userProvider)?.id;
|
||||
if (userId?.isEmpty == true) return;
|
||||
|
||||
final currentPosition = ref.read(mediaPlaybackProvider.select((value) => value.position));
|
||||
|
||||
final audioIndex = playbackModel.mediaStreams?.defaultAudioStreamIndex;
|
||||
final subIndex = playbackModel.mediaStreams?.defaultSubStreamIndex;
|
||||
|
||||
Response<PlaybackInfoResponse> response = await api.itemsItemIdPlaybackInfoPost(
|
||||
itemId: item.id,
|
||||
enableDirectPlay: true,
|
||||
enableDirectStream: true,
|
||||
enableTranscoding: true,
|
||||
autoOpenLiveStream: true,
|
||||
startTimeTicks: currentPosition.toRuntimeTicks,
|
||||
audioStreamIndex: audioIndex,
|
||||
subtitleStreamIndex: subIndex,
|
||||
mediaSourceId: item.id,
|
||||
body: PlaybackInfoDto(
|
||||
startTimeTicks: currentPosition.toRuntimeTicks,
|
||||
audioStreamIndex: audioIndex,
|
||||
subtitleStreamIndex: subIndex,
|
||||
enableTranscoding: true,
|
||||
autoOpenLiveStream: true,
|
||||
deviceProfile: defaultProfile,
|
||||
userId: userId,
|
||||
mediaSourceId: item.id,
|
||||
),
|
||||
);
|
||||
|
||||
PlaybackInfoResponse playbackInfo = response.bodyOrThrow;
|
||||
|
||||
final mediaSource = playbackInfo.mediaSources?.first;
|
||||
|
||||
final mediaStreamsWithUrls = MediaStreamsModel.fromMediaStreamsList(
|
||||
playbackInfo.mediaSources?.firstOrNull, playbackInfo.mediaSources?.firstOrNull?.mediaStreams ?? [], ref)
|
||||
.copyWith(
|
||||
defaultAudioStreamIndex: audioIndex,
|
||||
defaultSubStreamIndex: subIndex,
|
||||
);
|
||||
|
||||
if (mediaSource == null) return;
|
||||
|
||||
PlaybackModel? newModel;
|
||||
|
||||
if ((mediaSource.supportsDirectStream ?? false) || (mediaSource.supportsDirectPlay ?? false)) {
|
||||
final Map<String, String?> directOptions = {
|
||||
'Static': 'true',
|
||||
'mediaSourceId': mediaSource.id,
|
||||
'api_key': ref.read(userProvider)?.credentials.token,
|
||||
};
|
||||
|
||||
if (mediaSource.eTag != null) {
|
||||
directOptions['Tag'] = mediaSource.eTag;
|
||||
}
|
||||
|
||||
if (mediaSource.liveStreamId != null) {
|
||||
directOptions['LiveStreamId'] = mediaSource.liveStreamId;
|
||||
}
|
||||
|
||||
final params = Uri(queryParameters: directOptions).query;
|
||||
|
||||
final directPlay = '${ref.read(userProvider)?.server ?? ""}/Videos/${mediaSource.id}/stream?$params';
|
||||
|
||||
newModel = DirectPlaybackModel(
|
||||
item: playbackModel.item,
|
||||
queue: playbackModel.queue,
|
||||
introSkipModel: playbackModel.introSkipModel,
|
||||
chapters: playbackModel.chapters,
|
||||
playbackInfo: playbackInfo,
|
||||
trickPlay: playbackModel.trickPlay,
|
||||
media: Media(directPlay),
|
||||
mediaStreams: mediaStreamsWithUrls,
|
||||
);
|
||||
} else if ((mediaSource.supportsTranscoding ?? false) && mediaSource.transcodingUrl != null) {
|
||||
newModel = TranscodePlaybackModel(
|
||||
item: playbackModel.item,
|
||||
queue: playbackModel.queue,
|
||||
introSkipModel: playbackModel.introSkipModel,
|
||||
chapters: playbackModel.chapters,
|
||||
playbackInfo: playbackInfo,
|
||||
trickPlay: playbackModel.trickPlay,
|
||||
media: Media("${ref.read(userProvider)?.server ?? ""}${mediaSource.transcodingUrl ?? ""}"),
|
||||
mediaStreams: mediaStreamsWithUrls,
|
||||
);
|
||||
}
|
||||
if (newModel == null) return;
|
||||
if (newModel.runtimeType != playbackModel.runtimeType || newModel is TranscodePlaybackModel) {
|
||||
ref.read(videoPlayerProvider.notifier).loadPlaybackItem(newModel, startPosition: currentPosition);
|
||||
}
|
||||
}
|
||||
}
|
||||
210
lib/models/playback/transcode_playback_model.dart
Normal file
210
lib/models/playback/transcode_playback_model.dart
Normal file
|
|
@ -0,0 +1,210 @@
|
|||
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: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_streams_model.dart';
|
||||
import 'package:fladder/models/playback/playback_model.dart';
|
||||
import 'package:fladder/providers/api_provider.dart';
|
||||
import 'package:fladder/providers/video_player_provider.dart';
|
||||
import 'package:fladder/util/duration_extensions.dart';
|
||||
|
||||
class TranscodePlaybackModel implements PlaybackModel {
|
||||
TranscodePlaybackModel({
|
||||
required this.item,
|
||||
required this.media,
|
||||
required this.playbackInfo,
|
||||
this.mediaStreams,
|
||||
this.introSkipModel,
|
||||
this.chapters,
|
||||
this.trickPlay,
|
||||
this.queue = const [],
|
||||
});
|
||||
|
||||
@override
|
||||
final ItemBaseModel item;
|
||||
|
||||
@override
|
||||
final Media? media;
|
||||
|
||||
@override
|
||||
final PlaybackInfoResponse playbackInfo;
|
||||
|
||||
@override
|
||||
final MediaStreamsModel? mediaStreams;
|
||||
|
||||
@override
|
||||
final IntroOutSkipModel? introSkipModel;
|
||||
|
||||
@override
|
||||
final List<Chapter>? chapters;
|
||||
|
||||
@override
|
||||
final TrickPlayModel? trickPlay;
|
||||
|
||||
@override
|
||||
ItemBaseModel? get nextVideo => queue.nextOrNull(item);
|
||||
|
||||
@override
|
||||
ItemBaseModel? get previousVideo => queue.previousOrNull(item);
|
||||
|
||||
@override
|
||||
Future<Duration>? startDuration() async => item.userData.playBackPosition;
|
||||
|
||||
@override
|
||||
List<SubStreamModel> get subStreams => [SubStreamModel.no(), ...mediaStreams?.subStreams ?? []];
|
||||
|
||||
List<QueueItem> get itemsInQueue =>
|
||||
queue.mapIndexed((index, element) => QueueItem(id: element.id, playlistItemId: "playlistItem$index")).toList();
|
||||
|
||||
@override
|
||||
Future<TranscodePlaybackModel> setSubtitle(SubStreamModel? model, MediaControlsWrapper player) async {
|
||||
final wantedSubtitle =
|
||||
model ?? subStreams.firstWhereOrNull((element) => element.index == mediaStreams?.defaultSubStreamIndex);
|
||||
if (wantedSubtitle == null) return this;
|
||||
if (wantedSubtitle.index == SubStreamModel.no().index) {
|
||||
await player.setSubtitleTrack(SubtitleTrack.no());
|
||||
} else {
|
||||
final subTracks = player.subTracks.getRange(2, player.subTracks.length).toList();
|
||||
final index = subStreams.sublist(1).indexWhere((element) => element.id == wantedSubtitle.id);
|
||||
final subTrack = subTracks.elementAtOrNull(index);
|
||||
if (wantedSubtitle.isExternal && wantedSubtitle.url != null && subTrack == null) {
|
||||
await player.setSubtitleTrack(SubtitleTrack.uri(wantedSubtitle.url!));
|
||||
} else if (subTrack != null) {
|
||||
await player.setSubtitleTrack(subTrack);
|
||||
}
|
||||
}
|
||||
return copyWith(mediaStreams: () => mediaStreams?.copyWith(defaultSubStreamIndex: wantedSubtitle.index));
|
||||
}
|
||||
|
||||
@override
|
||||
List<AudioStreamModel> get audioStreams => [AudioStreamModel.no(), ...mediaStreams?.audioStreams ?? []];
|
||||
|
||||
@override
|
||||
Future<TranscodePlaybackModel>? setAudio(AudioStreamModel? model, MediaControlsWrapper player) async {
|
||||
final wantedAudioStream =
|
||||
model ?? audioStreams.firstWhereOrNull((element) => element.index == mediaStreams?.defaultAudioStreamIndex);
|
||||
if (wantedAudioStream == null) return this;
|
||||
if (wantedAudioStream.index == AudioStreamModel.no().index) {
|
||||
await player.setAudioTrack(AudioTrack.no());
|
||||
} else {
|
||||
final audioTracks = player.audioTracks.getRange(2, player.audioTracks.length).toList();
|
||||
final audioTrack = audioTracks.elementAtOrNull(audioStreams.indexOf(wantedAudioStream) - 1);
|
||||
if (audioTrack != null) {
|
||||
await player.setAudioTrack(audioTrack);
|
||||
}
|
||||
}
|
||||
return copyWith(mediaStreams: () => mediaStreams?.copyWith(defaultAudioStreamIndex: wantedAudioStream.index));
|
||||
}
|
||||
|
||||
@override
|
||||
Future<PlaybackModel?> playbackStarted(Duration position, Ref ref) async {
|
||||
await ref.read(jellyApiProvider).sessionsPlayingPost(
|
||||
body: PlaybackStartInfo(
|
||||
canSeek: true,
|
||||
itemId: item.id,
|
||||
mediaSourceId: item.id,
|
||||
playSessionId: playbackInfo.playSessionId,
|
||||
sessionId: playbackInfo.playSessionId,
|
||||
subtitleStreamIndex: item.streamModel?.defaultSubStreamIndex,
|
||||
audioStreamIndex: item.streamModel?.defaultAudioStreamIndex,
|
||||
volumeLevel: 100,
|
||||
playbackStartTimeTicks: position.toRuntimeTicks,
|
||||
playMethod: PlayMethod.transcode,
|
||||
isMuted: false,
|
||||
isPaused: false,
|
||||
repeatMode: RepeatMode.repeatall,
|
||||
nowPlayingQueue: itemsInQueue,
|
||||
),
|
||||
);
|
||||
return null;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<PlaybackModel?> playbackStopped(Duration position, Duration? totalDuration, Ref ref) async {
|
||||
ref.read(playBackModel.notifier).update((state) => null);
|
||||
|
||||
await ref.read(jellyApiProvider).sessionsPlayingStoppedPost(
|
||||
body: PlaybackStopInfo(
|
||||
itemId: item.id,
|
||||
mediaSourceId: item.id,
|
||||
playSessionId: playbackInfo.playSessionId,
|
||||
positionTicks: position.toRuntimeTicks,
|
||||
failed: false,
|
||||
nowPlayingQueue: itemsInQueue,
|
||||
),
|
||||
totalDuration: totalDuration,
|
||||
);
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<PlaybackModel?> updatePlaybackPosition(Duration position, bool isPlaying, Ref ref) async {
|
||||
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(
|
||||
body: PlaybackProgressInfo(
|
||||
canSeek: true,
|
||||
itemId: item.id,
|
||||
mediaSourceId: item.id,
|
||||
playSessionId: playbackInfo.playSessionId,
|
||||
sessionId: playbackInfo.playSessionId,
|
||||
subtitleStreamIndex: item.streamModel?.defaultSubStreamIndex,
|
||||
audioStreamIndex: item.streamModel?.defaultAudioStreamIndex,
|
||||
volumeLevel: 100,
|
||||
positionTicks: position.toRuntimeTicks,
|
||||
playMethod: PlayMethod.transcode,
|
||||
isPaused: !isPlaying,
|
||||
isMuted: false,
|
||||
repeatMode: RepeatMode.repeatall,
|
||||
nowPlayingQueue: itemsInQueue,
|
||||
),
|
||||
);
|
||||
return this;
|
||||
}
|
||||
|
||||
@override
|
||||
String toString() => 'TranscodePlaybackModel(item: $item, playbackInfo: $playbackInfo)';
|
||||
|
||||
@override
|
||||
final List<ItemBaseModel> queue;
|
||||
|
||||
@override
|
||||
TranscodePlaybackModel copyWith({
|
||||
ItemBaseModel? item,
|
||||
ValueGetter<Media?>? media,
|
||||
ValueGetter<Duration>? lastPosition,
|
||||
PlaybackInfoResponse? playbackInfo,
|
||||
ValueGetter<MediaStreamsModel?>? mediaStreams,
|
||||
ValueGetter<IntroOutSkipModel?>? introSkipModel,
|
||||
ValueGetter<List<Chapter>?>? chapters,
|
||||
ValueGetter<TrickPlayModel?>? trickPlay,
|
||||
List<ItemBaseModel>? queue,
|
||||
}) {
|
||||
return TranscodePlaybackModel(
|
||||
item: item ?? this.item,
|
||||
media: media != null ? media() : this.media,
|
||||
playbackInfo: playbackInfo ?? this.playbackInfo,
|
||||
mediaStreams: mediaStreams != null ? mediaStreams() : this.mediaStreams,
|
||||
introSkipModel: introSkipModel != null ? introSkipModel() : this.introSkipModel,
|
||||
chapters: chapters != null ? chapters() : this.chapters,
|
||||
trickPlay: trickPlay != null ? trickPlay() : this.trickPlay,
|
||||
queue: queue ?? this.queue,
|
||||
);
|
||||
}
|
||||
}
|
||||
40
lib/models/playlist_model.dart
Normal file
40
lib/models/playlist_model.dart
Normal file
|
|
@ -0,0 +1,40 @@
|
|||
import 'package:fladder/jellyfin/jellyfin_open_api.swagger.dart';
|
||||
import 'package:fladder/models/item_base_model.dart';
|
||||
import 'package:fladder/models/items/images_models.dart';
|
||||
import 'package:fladder/models/items/item_shared_models.dart';
|
||||
import 'package:fladder/models/items/overview_model.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
|
||||
class PlaylistModel extends ItemBaseModel {
|
||||
PlaylistModel({
|
||||
required super.name,
|
||||
required super.id,
|
||||
required super.overview,
|
||||
required super.parentId,
|
||||
required super.playlistId,
|
||||
required super.images,
|
||||
required super.childCount,
|
||||
required super.primaryRatio,
|
||||
required super.userData,
|
||||
super.canDelete,
|
||||
super.canDownload,
|
||||
super.jellyType,
|
||||
});
|
||||
|
||||
factory PlaylistModel.fromBaseDto(BaseItemDto item, Ref ref) {
|
||||
return PlaylistModel(
|
||||
name: item.name ?? "",
|
||||
id: item.id ?? "",
|
||||
childCount: item.childCount,
|
||||
overview: OverviewModel.fromBaseItemDto(item, ref),
|
||||
userData: UserData.fromDto(item.userData),
|
||||
parentId: item.parentId,
|
||||
playlistId: item.playlistItemId,
|
||||
images: ImagesData.fromBaseItem(item, ref),
|
||||
primaryRatio: item.primaryImageAspectRatio,
|
||||
canDelete: item.canDelete,
|
||||
canDownload: item.canDownload,
|
||||
jellyType: item.type,
|
||||
);
|
||||
}
|
||||
}
|
||||
25
lib/models/recommended_model.dart
Normal file
25
lib/models/recommended_model.dart
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
// ignore_for_file: public_member_api_docs, sort_constructors_first
|
||||
import 'package:fladder/models/item_base_model.dart';
|
||||
|
||||
class RecommendedModel {
|
||||
final String name;
|
||||
final List<ItemBaseModel> posters;
|
||||
final String type;
|
||||
RecommendedModel({
|
||||
required this.name,
|
||||
required this.posters,
|
||||
required this.type,
|
||||
});
|
||||
|
||||
RecommendedModel copyWith({
|
||||
String? name,
|
||||
List<ItemBaseModel>? posters,
|
||||
String? type,
|
||||
}) {
|
||||
return RecommendedModel(
|
||||
name: name ?? this.name,
|
||||
posters: posters ?? this.posters,
|
||||
type: type ?? this.type,
|
||||
);
|
||||
}
|
||||
}
|
||||
28
lib/models/search_model.dart
Normal file
28
lib/models/search_model.dart
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
import 'package:fladder/models/item_base_model.dart';
|
||||
|
||||
class SearchModel {
|
||||
final bool loading;
|
||||
final String searchQuery;
|
||||
final int resultCount;
|
||||
final Map<FladderItemType, List<ItemBaseModel>> results;
|
||||
SearchModel({
|
||||
this.loading = false,
|
||||
this.searchQuery = "",
|
||||
this.resultCount = 0,
|
||||
this.results = const {},
|
||||
});
|
||||
|
||||
SearchModel copyWith({
|
||||
bool? loading,
|
||||
String? searchQuery,
|
||||
int? resultCount,
|
||||
Map<FladderItemType, List<ItemBaseModel>>? results,
|
||||
}) {
|
||||
return SearchModel(
|
||||
loading: loading ?? this.loading,
|
||||
searchQuery: searchQuery ?? this.searchQuery,
|
||||
resultCount: resultCount ?? this.resultCount,
|
||||
results: results ?? this.results,
|
||||
);
|
||||
}
|
||||
}
|
||||
117
lib/models/settings/client_settings_model.dart
Normal file
117
lib/models/settings/client_settings_model.dart
Normal file
|
|
@ -0,0 +1,117 @@
|
|||
import 'dart:convert';
|
||||
import 'dart:developer';
|
||||
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
import 'package:fladder/util/custom_color_themes.dart';
|
||||
|
||||
import 'package:freezed_annotation/freezed_annotation.dart';
|
||||
|
||||
part 'client_settings_model.freezed.dart';
|
||||
part 'client_settings_model.g.dart';
|
||||
|
||||
@freezed
|
||||
class ClientSettingsModel with _$ClientSettingsModel {
|
||||
factory ClientSettingsModel({
|
||||
String? syncPath,
|
||||
@Default(Vector2(x: 0, y: 0)) Vector2 position,
|
||||
@Default(Vector2(x: 1280, y: 720)) Vector2 size,
|
||||
@Default(Duration(seconds: 30)) Duration? timeOut,
|
||||
Duration? nextUpDateCutoff,
|
||||
@Default(ThemeMode.system) ThemeMode themeMode,
|
||||
ColorThemes? themeColor,
|
||||
@Default(false) bool amoledBlack,
|
||||
@Default(false) bool blurPlaceHolders,
|
||||
@Default(false) bool blurUpcomingEpisodes,
|
||||
@LocaleConvert() Locale? selectedLocale,
|
||||
@Default(true) bool enableMediaKeys,
|
||||
@Default(1.0) double posterSize,
|
||||
@Default(false) bool pinchPosterZoom,
|
||||
@Default(false) bool mouseDragSupport,
|
||||
int? libraryPageSize,
|
||||
}) = _ClientSettingsModel;
|
||||
|
||||
factory ClientSettingsModel.fromJson(Map<String, dynamic> json) => _$ClientSettingsModelFromJson(json);
|
||||
}
|
||||
|
||||
class LocaleConvert implements JsonConverter<Locale?, String?> {
|
||||
const LocaleConvert();
|
||||
|
||||
@override
|
||||
Locale? fromJson(String? json) {
|
||||
if (json == null) return null;
|
||||
final parts = json.split('_');
|
||||
if (parts.length == 1) {
|
||||
return Locale(parts[0]);
|
||||
} else if (parts.length == 2) {
|
||||
return Locale(parts[0], parts[1]);
|
||||
} else {
|
||||
log("Invalid Locale format");
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
String? toJson(Locale? object) {
|
||||
if (object == null) return null;
|
||||
if (object.countryCode == null || object.countryCode?.isEmpty == true) {
|
||||
return object.languageCode;
|
||||
}
|
||||
return '${object.languageCode}_${object.countryCode}';
|
||||
}
|
||||
}
|
||||
|
||||
class Vector2 {
|
||||
final double x;
|
||||
final double y;
|
||||
const Vector2({
|
||||
required this.x,
|
||||
required this.y,
|
||||
});
|
||||
|
||||
Vector2 copyWith({
|
||||
double? x,
|
||||
double? y,
|
||||
}) {
|
||||
return Vector2(
|
||||
x: x ?? this.x,
|
||||
y: y ?? this.y,
|
||||
);
|
||||
}
|
||||
|
||||
Map<String, dynamic> toMap() {
|
||||
return <String, dynamic>{
|
||||
'x': x,
|
||||
'y': y,
|
||||
};
|
||||
}
|
||||
|
||||
factory Vector2.fromMap(Map<String, dynamic> map) {
|
||||
return Vector2(
|
||||
x: map['x'] as double,
|
||||
y: map['y'] as double,
|
||||
);
|
||||
}
|
||||
|
||||
String toJson() => json.encode(toMap());
|
||||
|
||||
factory Vector2.fromJson(String source) => Vector2.fromMap(json.decode(source) as Map<String, dynamic>);
|
||||
|
||||
factory Vector2.fromSize(Size size) => Vector2(x: size.width, y: size.height);
|
||||
|
||||
@override
|
||||
String toString() => 'Vector2(x: $x, y: $y)';
|
||||
|
||||
@override
|
||||
bool operator ==(covariant Vector2 other) {
|
||||
if (identical(this, other)) return true;
|
||||
|
||||
return other.x == x && other.y == y;
|
||||
}
|
||||
|
||||
@override
|
||||
int get hashCode => x.hashCode ^ y.hashCode;
|
||||
|
||||
static fromPosition(Offset windowPosition) => Vector2(x: windowPosition.dx, y: windowPosition.dy);
|
||||
}
|
||||
526
lib/models/settings/client_settings_model.freezed.dart
Normal file
526
lib/models/settings/client_settings_model.freezed.dart
Normal file
|
|
@ -0,0 +1,526 @@
|
|||
// 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 'client_settings_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');
|
||||
|
||||
ClientSettingsModel _$ClientSettingsModelFromJson(Map<String, dynamic> json) {
|
||||
return _ClientSettingsModel.fromJson(json);
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
mixin _$ClientSettingsModel {
|
||||
String? get syncPath => throw _privateConstructorUsedError;
|
||||
Vector2 get position => throw _privateConstructorUsedError;
|
||||
Vector2 get size => throw _privateConstructorUsedError;
|
||||
Duration? get timeOut => throw _privateConstructorUsedError;
|
||||
Duration? get nextUpDateCutoff => throw _privateConstructorUsedError;
|
||||
ThemeMode get themeMode => throw _privateConstructorUsedError;
|
||||
ColorThemes? get themeColor => throw _privateConstructorUsedError;
|
||||
bool get amoledBlack => throw _privateConstructorUsedError;
|
||||
bool get blurPlaceHolders => throw _privateConstructorUsedError;
|
||||
bool get blurUpcomingEpisodes => throw _privateConstructorUsedError;
|
||||
@LocaleConvert()
|
||||
Locale? get selectedLocale => throw _privateConstructorUsedError;
|
||||
bool get enableMediaKeys => throw _privateConstructorUsedError;
|
||||
double get posterSize => throw _privateConstructorUsedError;
|
||||
bool get pinchPosterZoom => throw _privateConstructorUsedError;
|
||||
bool get mouseDragSupport => throw _privateConstructorUsedError;
|
||||
int? get libraryPageSize => throw _privateConstructorUsedError;
|
||||
|
||||
Map<String, dynamic> toJson() => throw _privateConstructorUsedError;
|
||||
@JsonKey(ignore: true)
|
||||
$ClientSettingsModelCopyWith<ClientSettingsModel> get copyWith =>
|
||||
throw _privateConstructorUsedError;
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
abstract class $ClientSettingsModelCopyWith<$Res> {
|
||||
factory $ClientSettingsModelCopyWith(
|
||||
ClientSettingsModel value, $Res Function(ClientSettingsModel) then) =
|
||||
_$ClientSettingsModelCopyWithImpl<$Res, ClientSettingsModel>;
|
||||
@useResult
|
||||
$Res call(
|
||||
{String? syncPath,
|
||||
Vector2 position,
|
||||
Vector2 size,
|
||||
Duration? timeOut,
|
||||
Duration? nextUpDateCutoff,
|
||||
ThemeMode themeMode,
|
||||
ColorThemes? themeColor,
|
||||
bool amoledBlack,
|
||||
bool blurPlaceHolders,
|
||||
bool blurUpcomingEpisodes,
|
||||
@LocaleConvert() Locale? selectedLocale,
|
||||
bool enableMediaKeys,
|
||||
double posterSize,
|
||||
bool pinchPosterZoom,
|
||||
bool mouseDragSupport,
|
||||
int? libraryPageSize});
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
class _$ClientSettingsModelCopyWithImpl<$Res, $Val extends ClientSettingsModel>
|
||||
implements $ClientSettingsModelCopyWith<$Res> {
|
||||
_$ClientSettingsModelCopyWithImpl(this._value, this._then);
|
||||
|
||||
// ignore: unused_field
|
||||
final $Val _value;
|
||||
// ignore: unused_field
|
||||
final $Res Function($Val) _then;
|
||||
|
||||
@pragma('vm:prefer-inline')
|
||||
@override
|
||||
$Res call({
|
||||
Object? syncPath = freezed,
|
||||
Object? position = null,
|
||||
Object? size = null,
|
||||
Object? timeOut = freezed,
|
||||
Object? nextUpDateCutoff = freezed,
|
||||
Object? themeMode = null,
|
||||
Object? themeColor = freezed,
|
||||
Object? amoledBlack = null,
|
||||
Object? blurPlaceHolders = null,
|
||||
Object? blurUpcomingEpisodes = null,
|
||||
Object? selectedLocale = freezed,
|
||||
Object? enableMediaKeys = null,
|
||||
Object? posterSize = null,
|
||||
Object? pinchPosterZoom = null,
|
||||
Object? mouseDragSupport = null,
|
||||
Object? libraryPageSize = freezed,
|
||||
}) {
|
||||
return _then(_value.copyWith(
|
||||
syncPath: freezed == syncPath
|
||||
? _value.syncPath
|
||||
: syncPath // ignore: cast_nullable_to_non_nullable
|
||||
as String?,
|
||||
position: null == position
|
||||
? _value.position
|
||||
: position // ignore: cast_nullable_to_non_nullable
|
||||
as Vector2,
|
||||
size: null == size
|
||||
? _value.size
|
||||
: size // ignore: cast_nullable_to_non_nullable
|
||||
as Vector2,
|
||||
timeOut: freezed == timeOut
|
||||
? _value.timeOut
|
||||
: timeOut // ignore: cast_nullable_to_non_nullable
|
||||
as Duration?,
|
||||
nextUpDateCutoff: freezed == nextUpDateCutoff
|
||||
? _value.nextUpDateCutoff
|
||||
: nextUpDateCutoff // ignore: cast_nullable_to_non_nullable
|
||||
as Duration?,
|
||||
themeMode: null == themeMode
|
||||
? _value.themeMode
|
||||
: themeMode // ignore: cast_nullable_to_non_nullable
|
||||
as ThemeMode,
|
||||
themeColor: freezed == themeColor
|
||||
? _value.themeColor
|
||||
: themeColor // ignore: cast_nullable_to_non_nullable
|
||||
as ColorThemes?,
|
||||
amoledBlack: null == amoledBlack
|
||||
? _value.amoledBlack
|
||||
: amoledBlack // ignore: cast_nullable_to_non_nullable
|
||||
as bool,
|
||||
blurPlaceHolders: null == blurPlaceHolders
|
||||
? _value.blurPlaceHolders
|
||||
: blurPlaceHolders // ignore: cast_nullable_to_non_nullable
|
||||
as bool,
|
||||
blurUpcomingEpisodes: null == blurUpcomingEpisodes
|
||||
? _value.blurUpcomingEpisodes
|
||||
: blurUpcomingEpisodes // ignore: cast_nullable_to_non_nullable
|
||||
as bool,
|
||||
selectedLocale: freezed == selectedLocale
|
||||
? _value.selectedLocale
|
||||
: selectedLocale // ignore: cast_nullable_to_non_nullable
|
||||
as Locale?,
|
||||
enableMediaKeys: null == enableMediaKeys
|
||||
? _value.enableMediaKeys
|
||||
: enableMediaKeys // ignore: cast_nullable_to_non_nullable
|
||||
as bool,
|
||||
posterSize: null == posterSize
|
||||
? _value.posterSize
|
||||
: posterSize // ignore: cast_nullable_to_non_nullable
|
||||
as double,
|
||||
pinchPosterZoom: null == pinchPosterZoom
|
||||
? _value.pinchPosterZoom
|
||||
: pinchPosterZoom // ignore: cast_nullable_to_non_nullable
|
||||
as bool,
|
||||
mouseDragSupport: null == mouseDragSupport
|
||||
? _value.mouseDragSupport
|
||||
: mouseDragSupport // ignore: cast_nullable_to_non_nullable
|
||||
as bool,
|
||||
libraryPageSize: freezed == libraryPageSize
|
||||
? _value.libraryPageSize
|
||||
: libraryPageSize // ignore: cast_nullable_to_non_nullable
|
||||
as int?,
|
||||
) as $Val);
|
||||
}
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
abstract class _$$ClientSettingsModelImplCopyWith<$Res>
|
||||
implements $ClientSettingsModelCopyWith<$Res> {
|
||||
factory _$$ClientSettingsModelImplCopyWith(_$ClientSettingsModelImpl value,
|
||||
$Res Function(_$ClientSettingsModelImpl) then) =
|
||||
__$$ClientSettingsModelImplCopyWithImpl<$Res>;
|
||||
@override
|
||||
@useResult
|
||||
$Res call(
|
||||
{String? syncPath,
|
||||
Vector2 position,
|
||||
Vector2 size,
|
||||
Duration? timeOut,
|
||||
Duration? nextUpDateCutoff,
|
||||
ThemeMode themeMode,
|
||||
ColorThemes? themeColor,
|
||||
bool amoledBlack,
|
||||
bool blurPlaceHolders,
|
||||
bool blurUpcomingEpisodes,
|
||||
@LocaleConvert() Locale? selectedLocale,
|
||||
bool enableMediaKeys,
|
||||
double posterSize,
|
||||
bool pinchPosterZoom,
|
||||
bool mouseDragSupport,
|
||||
int? libraryPageSize});
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
class __$$ClientSettingsModelImplCopyWithImpl<$Res>
|
||||
extends _$ClientSettingsModelCopyWithImpl<$Res, _$ClientSettingsModelImpl>
|
||||
implements _$$ClientSettingsModelImplCopyWith<$Res> {
|
||||
__$$ClientSettingsModelImplCopyWithImpl(_$ClientSettingsModelImpl _value,
|
||||
$Res Function(_$ClientSettingsModelImpl) _then)
|
||||
: super(_value, _then);
|
||||
|
||||
@pragma('vm:prefer-inline')
|
||||
@override
|
||||
$Res call({
|
||||
Object? syncPath = freezed,
|
||||
Object? position = null,
|
||||
Object? size = null,
|
||||
Object? timeOut = freezed,
|
||||
Object? nextUpDateCutoff = freezed,
|
||||
Object? themeMode = null,
|
||||
Object? themeColor = freezed,
|
||||
Object? amoledBlack = null,
|
||||
Object? blurPlaceHolders = null,
|
||||
Object? blurUpcomingEpisodes = null,
|
||||
Object? selectedLocale = freezed,
|
||||
Object? enableMediaKeys = null,
|
||||
Object? posterSize = null,
|
||||
Object? pinchPosterZoom = null,
|
||||
Object? mouseDragSupport = null,
|
||||
Object? libraryPageSize = freezed,
|
||||
}) {
|
||||
return _then(_$ClientSettingsModelImpl(
|
||||
syncPath: freezed == syncPath
|
||||
? _value.syncPath
|
||||
: syncPath // ignore: cast_nullable_to_non_nullable
|
||||
as String?,
|
||||
position: null == position
|
||||
? _value.position
|
||||
: position // ignore: cast_nullable_to_non_nullable
|
||||
as Vector2,
|
||||
size: null == size
|
||||
? _value.size
|
||||
: size // ignore: cast_nullable_to_non_nullable
|
||||
as Vector2,
|
||||
timeOut: freezed == timeOut
|
||||
? _value.timeOut
|
||||
: timeOut // ignore: cast_nullable_to_non_nullable
|
||||
as Duration?,
|
||||
nextUpDateCutoff: freezed == nextUpDateCutoff
|
||||
? _value.nextUpDateCutoff
|
||||
: nextUpDateCutoff // ignore: cast_nullable_to_non_nullable
|
||||
as Duration?,
|
||||
themeMode: null == themeMode
|
||||
? _value.themeMode
|
||||
: themeMode // ignore: cast_nullable_to_non_nullable
|
||||
as ThemeMode,
|
||||
themeColor: freezed == themeColor
|
||||
? _value.themeColor
|
||||
: themeColor // ignore: cast_nullable_to_non_nullable
|
||||
as ColorThemes?,
|
||||
amoledBlack: null == amoledBlack
|
||||
? _value.amoledBlack
|
||||
: amoledBlack // ignore: cast_nullable_to_non_nullable
|
||||
as bool,
|
||||
blurPlaceHolders: null == blurPlaceHolders
|
||||
? _value.blurPlaceHolders
|
||||
: blurPlaceHolders // ignore: cast_nullable_to_non_nullable
|
||||
as bool,
|
||||
blurUpcomingEpisodes: null == blurUpcomingEpisodes
|
||||
? _value.blurUpcomingEpisodes
|
||||
: blurUpcomingEpisodes // ignore: cast_nullable_to_non_nullable
|
||||
as bool,
|
||||
selectedLocale: freezed == selectedLocale
|
||||
? _value.selectedLocale
|
||||
: selectedLocale // ignore: cast_nullable_to_non_nullable
|
||||
as Locale?,
|
||||
enableMediaKeys: null == enableMediaKeys
|
||||
? _value.enableMediaKeys
|
||||
: enableMediaKeys // ignore: cast_nullable_to_non_nullable
|
||||
as bool,
|
||||
posterSize: null == posterSize
|
||||
? _value.posterSize
|
||||
: posterSize // ignore: cast_nullable_to_non_nullable
|
||||
as double,
|
||||
pinchPosterZoom: null == pinchPosterZoom
|
||||
? _value.pinchPosterZoom
|
||||
: pinchPosterZoom // ignore: cast_nullable_to_non_nullable
|
||||
as bool,
|
||||
mouseDragSupport: null == mouseDragSupport
|
||||
? _value.mouseDragSupport
|
||||
: mouseDragSupport // ignore: cast_nullable_to_non_nullable
|
||||
as bool,
|
||||
libraryPageSize: freezed == libraryPageSize
|
||||
? _value.libraryPageSize
|
||||
: libraryPageSize // ignore: cast_nullable_to_non_nullable
|
||||
as int?,
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
@JsonSerializable()
|
||||
class _$ClientSettingsModelImpl
|
||||
with DiagnosticableTreeMixin
|
||||
implements _ClientSettingsModel {
|
||||
_$ClientSettingsModelImpl(
|
||||
{this.syncPath,
|
||||
this.position = const Vector2(x: 0, y: 0),
|
||||
this.size = const Vector2(x: 1280, y: 720),
|
||||
this.timeOut = const Duration(seconds: 30),
|
||||
this.nextUpDateCutoff,
|
||||
this.themeMode = ThemeMode.system,
|
||||
this.themeColor,
|
||||
this.amoledBlack = false,
|
||||
this.blurPlaceHolders = false,
|
||||
this.blurUpcomingEpisodes = false,
|
||||
@LocaleConvert() this.selectedLocale,
|
||||
this.enableMediaKeys = true,
|
||||
this.posterSize = 1.0,
|
||||
this.pinchPosterZoom = false,
|
||||
this.mouseDragSupport = false,
|
||||
this.libraryPageSize});
|
||||
|
||||
factory _$ClientSettingsModelImpl.fromJson(Map<String, dynamic> json) =>
|
||||
_$$ClientSettingsModelImplFromJson(json);
|
||||
|
||||
@override
|
||||
final String? syncPath;
|
||||
@override
|
||||
@JsonKey()
|
||||
final Vector2 position;
|
||||
@override
|
||||
@JsonKey()
|
||||
final Vector2 size;
|
||||
@override
|
||||
@JsonKey()
|
||||
final Duration? timeOut;
|
||||
@override
|
||||
final Duration? nextUpDateCutoff;
|
||||
@override
|
||||
@JsonKey()
|
||||
final ThemeMode themeMode;
|
||||
@override
|
||||
final ColorThemes? themeColor;
|
||||
@override
|
||||
@JsonKey()
|
||||
final bool amoledBlack;
|
||||
@override
|
||||
@JsonKey()
|
||||
final bool blurPlaceHolders;
|
||||
@override
|
||||
@JsonKey()
|
||||
final bool blurUpcomingEpisodes;
|
||||
@override
|
||||
@LocaleConvert()
|
||||
final Locale? selectedLocale;
|
||||
@override
|
||||
@JsonKey()
|
||||
final bool enableMediaKeys;
|
||||
@override
|
||||
@JsonKey()
|
||||
final double posterSize;
|
||||
@override
|
||||
@JsonKey()
|
||||
final bool pinchPosterZoom;
|
||||
@override
|
||||
@JsonKey()
|
||||
final bool mouseDragSupport;
|
||||
@override
|
||||
final int? libraryPageSize;
|
||||
|
||||
@override
|
||||
String toString({DiagnosticLevel minLevel = DiagnosticLevel.info}) {
|
||||
return 'ClientSettingsModel(syncPath: $syncPath, position: $position, size: $size, timeOut: $timeOut, nextUpDateCutoff: $nextUpDateCutoff, themeMode: $themeMode, themeColor: $themeColor, amoledBlack: $amoledBlack, blurPlaceHolders: $blurPlaceHolders, blurUpcomingEpisodes: $blurUpcomingEpisodes, selectedLocale: $selectedLocale, enableMediaKeys: $enableMediaKeys, posterSize: $posterSize, pinchPosterZoom: $pinchPosterZoom, mouseDragSupport: $mouseDragSupport, libraryPageSize: $libraryPageSize)';
|
||||
}
|
||||
|
||||
@override
|
||||
void debugFillProperties(DiagnosticPropertiesBuilder properties) {
|
||||
super.debugFillProperties(properties);
|
||||
properties
|
||||
..add(DiagnosticsProperty('type', 'ClientSettingsModel'))
|
||||
..add(DiagnosticsProperty('syncPath', syncPath))
|
||||
..add(DiagnosticsProperty('position', position))
|
||||
..add(DiagnosticsProperty('size', size))
|
||||
..add(DiagnosticsProperty('timeOut', timeOut))
|
||||
..add(DiagnosticsProperty('nextUpDateCutoff', nextUpDateCutoff))
|
||||
..add(DiagnosticsProperty('themeMode', themeMode))
|
||||
..add(DiagnosticsProperty('themeColor', themeColor))
|
||||
..add(DiagnosticsProperty('amoledBlack', amoledBlack))
|
||||
..add(DiagnosticsProperty('blurPlaceHolders', blurPlaceHolders))
|
||||
..add(DiagnosticsProperty('blurUpcomingEpisodes', blurUpcomingEpisodes))
|
||||
..add(DiagnosticsProperty('selectedLocale', selectedLocale))
|
||||
..add(DiagnosticsProperty('enableMediaKeys', enableMediaKeys))
|
||||
..add(DiagnosticsProperty('posterSize', posterSize))
|
||||
..add(DiagnosticsProperty('pinchPosterZoom', pinchPosterZoom))
|
||||
..add(DiagnosticsProperty('mouseDragSupport', mouseDragSupport))
|
||||
..add(DiagnosticsProperty('libraryPageSize', libraryPageSize));
|
||||
}
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
return identical(this, other) ||
|
||||
(other.runtimeType == runtimeType &&
|
||||
other is _$ClientSettingsModelImpl &&
|
||||
(identical(other.syncPath, syncPath) ||
|
||||
other.syncPath == syncPath) &&
|
||||
(identical(other.position, position) ||
|
||||
other.position == position) &&
|
||||
(identical(other.size, size) || other.size == size) &&
|
||||
(identical(other.timeOut, timeOut) || other.timeOut == timeOut) &&
|
||||
(identical(other.nextUpDateCutoff, nextUpDateCutoff) ||
|
||||
other.nextUpDateCutoff == nextUpDateCutoff) &&
|
||||
(identical(other.themeMode, themeMode) ||
|
||||
other.themeMode == themeMode) &&
|
||||
(identical(other.themeColor, themeColor) ||
|
||||
other.themeColor == themeColor) &&
|
||||
(identical(other.amoledBlack, amoledBlack) ||
|
||||
other.amoledBlack == amoledBlack) &&
|
||||
(identical(other.blurPlaceHolders, blurPlaceHolders) ||
|
||||
other.blurPlaceHolders == blurPlaceHolders) &&
|
||||
(identical(other.blurUpcomingEpisodes, blurUpcomingEpisodes) ||
|
||||
other.blurUpcomingEpisodes == blurUpcomingEpisodes) &&
|
||||
(identical(other.selectedLocale, selectedLocale) ||
|
||||
other.selectedLocale == selectedLocale) &&
|
||||
(identical(other.enableMediaKeys, enableMediaKeys) ||
|
||||
other.enableMediaKeys == enableMediaKeys) &&
|
||||
(identical(other.posterSize, posterSize) ||
|
||||
other.posterSize == posterSize) &&
|
||||
(identical(other.pinchPosterZoom, pinchPosterZoom) ||
|
||||
other.pinchPosterZoom == pinchPosterZoom) &&
|
||||
(identical(other.mouseDragSupport, mouseDragSupport) ||
|
||||
other.mouseDragSupport == mouseDragSupport) &&
|
||||
(identical(other.libraryPageSize, libraryPageSize) ||
|
||||
other.libraryPageSize == libraryPageSize));
|
||||
}
|
||||
|
||||
@JsonKey(ignore: true)
|
||||
@override
|
||||
int get hashCode => Object.hash(
|
||||
runtimeType,
|
||||
syncPath,
|
||||
position,
|
||||
size,
|
||||
timeOut,
|
||||
nextUpDateCutoff,
|
||||
themeMode,
|
||||
themeColor,
|
||||
amoledBlack,
|
||||
blurPlaceHolders,
|
||||
blurUpcomingEpisodes,
|
||||
selectedLocale,
|
||||
enableMediaKeys,
|
||||
posterSize,
|
||||
pinchPosterZoom,
|
||||
mouseDragSupport,
|
||||
libraryPageSize);
|
||||
|
||||
@JsonKey(ignore: true)
|
||||
@override
|
||||
@pragma('vm:prefer-inline')
|
||||
_$$ClientSettingsModelImplCopyWith<_$ClientSettingsModelImpl> get copyWith =>
|
||||
__$$ClientSettingsModelImplCopyWithImpl<_$ClientSettingsModelImpl>(
|
||||
this, _$identity);
|
||||
|
||||
@override
|
||||
Map<String, dynamic> toJson() {
|
||||
return _$$ClientSettingsModelImplToJson(
|
||||
this,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
abstract class _ClientSettingsModel implements ClientSettingsModel {
|
||||
factory _ClientSettingsModel(
|
||||
{final String? syncPath,
|
||||
final Vector2 position,
|
||||
final Vector2 size,
|
||||
final Duration? timeOut,
|
||||
final Duration? nextUpDateCutoff,
|
||||
final ThemeMode themeMode,
|
||||
final ColorThemes? themeColor,
|
||||
final bool amoledBlack,
|
||||
final bool blurPlaceHolders,
|
||||
final bool blurUpcomingEpisodes,
|
||||
@LocaleConvert() final Locale? selectedLocale,
|
||||
final bool enableMediaKeys,
|
||||
final double posterSize,
|
||||
final bool pinchPosterZoom,
|
||||
final bool mouseDragSupport,
|
||||
final int? libraryPageSize}) = _$ClientSettingsModelImpl;
|
||||
|
||||
factory _ClientSettingsModel.fromJson(Map<String, dynamic> json) =
|
||||
_$ClientSettingsModelImpl.fromJson;
|
||||
|
||||
@override
|
||||
String? get syncPath;
|
||||
@override
|
||||
Vector2 get position;
|
||||
@override
|
||||
Vector2 get size;
|
||||
@override
|
||||
Duration? get timeOut;
|
||||
@override
|
||||
Duration? get nextUpDateCutoff;
|
||||
@override
|
||||
ThemeMode get themeMode;
|
||||
@override
|
||||
ColorThemes? get themeColor;
|
||||
@override
|
||||
bool get amoledBlack;
|
||||
@override
|
||||
bool get blurPlaceHolders;
|
||||
@override
|
||||
bool get blurUpcomingEpisodes;
|
||||
@override
|
||||
@LocaleConvert()
|
||||
Locale? get selectedLocale;
|
||||
@override
|
||||
bool get enableMediaKeys;
|
||||
@override
|
||||
double get posterSize;
|
||||
@override
|
||||
bool get pinchPosterZoom;
|
||||
@override
|
||||
bool get mouseDragSupport;
|
||||
@override
|
||||
int? get libraryPageSize;
|
||||
@override
|
||||
@JsonKey(ignore: true)
|
||||
_$$ClientSettingsModelImplCopyWith<_$ClientSettingsModelImpl> get copyWith =>
|
||||
throw _privateConstructorUsedError;
|
||||
}
|
||||
83
lib/models/settings/client_settings_model.g.dart
Normal file
83
lib/models/settings/client_settings_model.g.dart
Normal file
|
|
@ -0,0 +1,83 @@
|
|||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
|
||||
part of 'client_settings_model.dart';
|
||||
|
||||
// **************************************************************************
|
||||
// JsonSerializableGenerator
|
||||
// **************************************************************************
|
||||
|
||||
_$ClientSettingsModelImpl _$$ClientSettingsModelImplFromJson(
|
||||
Map<String, dynamic> json) =>
|
||||
_$ClientSettingsModelImpl(
|
||||
syncPath: json['syncPath'] as String?,
|
||||
position: json['position'] == null
|
||||
? const Vector2(x: 0, y: 0)
|
||||
: Vector2.fromJson(json['position'] as String),
|
||||
size: json['size'] == null
|
||||
? const Vector2(x: 1280, y: 720)
|
||||
: Vector2.fromJson(json['size'] as String),
|
||||
timeOut: json['timeOut'] == null
|
||||
? const Duration(seconds: 30)
|
||||
: Duration(microseconds: (json['timeOut'] as num).toInt()),
|
||||
nextUpDateCutoff: json['nextUpDateCutoff'] == null
|
||||
? null
|
||||
: Duration(microseconds: (json['nextUpDateCutoff'] as num).toInt()),
|
||||
themeMode: $enumDecodeNullable(_$ThemeModeEnumMap, json['themeMode']) ??
|
||||
ThemeMode.system,
|
||||
themeColor: $enumDecodeNullable(_$ColorThemesEnumMap, json['themeColor']),
|
||||
amoledBlack: json['amoledBlack'] as bool? ?? false,
|
||||
blurPlaceHolders: json['blurPlaceHolders'] as bool? ?? false,
|
||||
blurUpcomingEpisodes: json['blurUpcomingEpisodes'] as bool? ?? false,
|
||||
selectedLocale:
|
||||
const LocaleConvert().fromJson(json['selectedLocale'] as String?),
|
||||
enableMediaKeys: json['enableMediaKeys'] as bool? ?? true,
|
||||
posterSize: (json['posterSize'] as num?)?.toDouble() ?? 1.0,
|
||||
pinchPosterZoom: json['pinchPosterZoom'] as bool? ?? false,
|
||||
mouseDragSupport: json['mouseDragSupport'] as bool? ?? false,
|
||||
libraryPageSize: (json['libraryPageSize'] as num?)?.toInt(),
|
||||
);
|
||||
|
||||
Map<String, dynamic> _$$ClientSettingsModelImplToJson(
|
||||
_$ClientSettingsModelImpl instance) =>
|
||||
<String, dynamic>{
|
||||
'syncPath': instance.syncPath,
|
||||
'position': instance.position,
|
||||
'size': instance.size,
|
||||
'timeOut': instance.timeOut?.inMicroseconds,
|
||||
'nextUpDateCutoff': instance.nextUpDateCutoff?.inMicroseconds,
|
||||
'themeMode': _$ThemeModeEnumMap[instance.themeMode]!,
|
||||
'themeColor': _$ColorThemesEnumMap[instance.themeColor],
|
||||
'amoledBlack': instance.amoledBlack,
|
||||
'blurPlaceHolders': instance.blurPlaceHolders,
|
||||
'blurUpcomingEpisodes': instance.blurUpcomingEpisodes,
|
||||
'selectedLocale': const LocaleConvert().toJson(instance.selectedLocale),
|
||||
'enableMediaKeys': instance.enableMediaKeys,
|
||||
'posterSize': instance.posterSize,
|
||||
'pinchPosterZoom': instance.pinchPosterZoom,
|
||||
'mouseDragSupport': instance.mouseDragSupport,
|
||||
'libraryPageSize': instance.libraryPageSize,
|
||||
};
|
||||
|
||||
const _$ThemeModeEnumMap = {
|
||||
ThemeMode.system: 'system',
|
||||
ThemeMode.light: 'light',
|
||||
ThemeMode.dark: 'dark',
|
||||
};
|
||||
|
||||
const _$ColorThemesEnumMap = {
|
||||
ColorThemes.fladder: 'fladder',
|
||||
ColorThemes.deepOrange: 'deepOrange',
|
||||
ColorThemes.amber: 'amber',
|
||||
ColorThemes.green: 'green',
|
||||
ColorThemes.lightGreen: 'lightGreen',
|
||||
ColorThemes.lime: 'lime',
|
||||
ColorThemes.cyan: 'cyan',
|
||||
ColorThemes.blue: 'blue',
|
||||
ColorThemes.lightBlue: 'lightBlue',
|
||||
ColorThemes.indigo: 'indigo',
|
||||
ColorThemes.deepBlue: 'deepBlue',
|
||||
ColorThemes.brown: 'brown',
|
||||
ColorThemes.purple: 'purple',
|
||||
ColorThemes.deepPurple: 'deepPurple',
|
||||
ColorThemes.blueGrey: 'blueGrey',
|
||||
};
|
||||
108
lib/models/settings/home_settings_model.dart
Normal file
108
lib/models/settings/home_settings_model.dart
Normal file
|
|
@ -0,0 +1,108 @@
|
|||
import 'dart:convert';
|
||||
|
||||
import 'package:collection/collection.dart';
|
||||
import 'package:fladder/util/localization_helper.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
enum HomeCarouselSettings {
|
||||
off,
|
||||
nextUp,
|
||||
cont,
|
||||
combined,
|
||||
;
|
||||
|
||||
const HomeCarouselSettings();
|
||||
|
||||
String label(BuildContext context) => switch (this) {
|
||||
HomeCarouselSettings.off => context.localized.hide,
|
||||
HomeCarouselSettings.nextUp => context.localized.nextUp,
|
||||
HomeCarouselSettings.cont => context.localized.settingsContinue,
|
||||
HomeCarouselSettings.combined => context.localized.combined,
|
||||
};
|
||||
|
||||
String toMap() {
|
||||
return toString();
|
||||
}
|
||||
|
||||
static HomeCarouselSettings fromMap(String value) {
|
||||
return HomeCarouselSettings.values.firstWhereOrNull((element) => element.name == value) ??
|
||||
HomeCarouselSettings.combined;
|
||||
}
|
||||
}
|
||||
|
||||
enum HomeNextUp {
|
||||
off,
|
||||
nextUp,
|
||||
cont,
|
||||
combined,
|
||||
separate,
|
||||
;
|
||||
|
||||
const HomeNextUp();
|
||||
|
||||
String label(BuildContext context) => switch (this) {
|
||||
HomeNextUp.off => context.localized.hide,
|
||||
HomeNextUp.nextUp => context.localized.nextUp,
|
||||
HomeNextUp.cont => context.localized.settingsContinue,
|
||||
HomeNextUp.combined => context.localized.combined,
|
||||
HomeNextUp.separate => context.localized.separate,
|
||||
};
|
||||
|
||||
String toMap() {
|
||||
return toString();
|
||||
}
|
||||
|
||||
static HomeNextUp fromMap(String value) {
|
||||
return HomeNextUp.values.firstWhereOrNull((element) => element.name == value) ?? HomeNextUp.separate;
|
||||
}
|
||||
}
|
||||
|
||||
class HomeSettingsModel {
|
||||
final HomeCarouselSettings carouselSettings;
|
||||
final HomeNextUp nextUp;
|
||||
HomeSettingsModel({
|
||||
this.carouselSettings = HomeCarouselSettings.combined,
|
||||
this.nextUp = HomeNextUp.separate,
|
||||
});
|
||||
|
||||
HomeSettingsModel copyWith({
|
||||
HomeCarouselSettings? carouselSettings,
|
||||
HomeNextUp? nextUp,
|
||||
}) {
|
||||
return HomeSettingsModel(
|
||||
carouselSettings: carouselSettings ?? this.carouselSettings,
|
||||
nextUp: nextUp ?? this.nextUp,
|
||||
);
|
||||
}
|
||||
|
||||
Map<String, dynamic> toMap() {
|
||||
return {
|
||||
'carouselSettings': carouselSettings.toMap(),
|
||||
'nextUp': nextUp.toMap(),
|
||||
};
|
||||
}
|
||||
|
||||
factory HomeSettingsModel.fromMap(Map<String, dynamic> map) {
|
||||
return HomeSettingsModel(
|
||||
carouselSettings: HomeCarouselSettings.fromMap(map['carouselSettings']),
|
||||
nextUp: HomeNextUp.fromMap(map['nextUp']),
|
||||
);
|
||||
}
|
||||
|
||||
String toJson() => json.encode(toMap());
|
||||
|
||||
factory HomeSettingsModel.fromJson(String source) => HomeSettingsModel.fromMap(json.decode(source));
|
||||
|
||||
@override
|
||||
String toString() => 'HomeSettingsModel(carouselSettings: $carouselSettings, nextUp: $nextUp)';
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
if (identical(this, other)) return true;
|
||||
|
||||
return other is HomeSettingsModel && other.carouselSettings == carouselSettings && other.nextUp == nextUp;
|
||||
}
|
||||
|
||||
@override
|
||||
int get hashCode => carouselSettings.hashCode ^ nextUp.hashCode;
|
||||
}
|
||||
254
lib/models/settings/subtitle_settings_model.dart
Normal file
254
lib/models/settings/subtitle_settings_model.dart
Normal file
|
|
@ -0,0 +1,254 @@
|
|||
// ignore_for_file: public_member_api_docs, sort_constructors_first
|
||||
import 'dart:convert';
|
||||
import 'dart:math' as math;
|
||||
|
||||
import 'package:collection/collection.dart';
|
||||
import 'package:fladder/providers/settings/subtitle_settings_provider.dart';
|
||||
import 'package:fladder/providers/settings/video_player_settings_provider.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:google_fonts/google_fonts.dart';
|
||||
|
||||
class SubtitleSettingsModel {
|
||||
final double fontSize;
|
||||
final FontWeight fontWeight;
|
||||
final double verticalOffset;
|
||||
final Color color;
|
||||
final Color outlineColor;
|
||||
final double outlineSize;
|
||||
final Color backGroundColor;
|
||||
final double shadow;
|
||||
const SubtitleSettingsModel({
|
||||
this.fontSize = 60,
|
||||
this.fontWeight = FontWeight.normal,
|
||||
this.verticalOffset = 0.10,
|
||||
this.color = Colors.white,
|
||||
this.outlineColor = const Color.fromRGBO(0, 0, 0, 0.85),
|
||||
this.outlineSize = 4,
|
||||
this.backGroundColor = const Color.fromARGB(0, 0, 0, 0),
|
||||
this.shadow = 0.5,
|
||||
});
|
||||
|
||||
SubtitleSettingsModel copyWith({
|
||||
double? fontSize,
|
||||
FontWeight? fontWeight,
|
||||
double? verticalOffset,
|
||||
Color? color,
|
||||
Color? outlineColor,
|
||||
double? outlineSize,
|
||||
Color? backGroundColor,
|
||||
double? shadow,
|
||||
}) {
|
||||
return SubtitleSettingsModel(
|
||||
fontSize: fontSize ?? this.fontSize,
|
||||
fontWeight: fontWeight ?? this.fontWeight,
|
||||
verticalOffset: verticalOffset ?? this.verticalOffset,
|
||||
color: color ?? this.color,
|
||||
outlineColor: outlineColor ?? this.outlineColor,
|
||||
outlineSize: outlineSize ?? this.outlineSize,
|
||||
backGroundColor: backGroundColor ?? this.backGroundColor,
|
||||
shadow: shadow ?? this.shadow,
|
||||
);
|
||||
}
|
||||
|
||||
TextStyle get backGroundStyle {
|
||||
return style.copyWith(
|
||||
shadows: (shadow > 0.01)
|
||||
? [
|
||||
Shadow(
|
||||
blurRadius: 16,
|
||||
color: Colors.black.withOpacity(shadow),
|
||||
),
|
||||
Shadow(
|
||||
blurRadius: 8,
|
||||
color: Colors.black.withOpacity(shadow),
|
||||
),
|
||||
]
|
||||
: null,
|
||||
foreground: Paint()
|
||||
..style = PaintingStyle.stroke
|
||||
..strokeWidth = outlineSize * (fontSize / 30)
|
||||
..color = outlineColor
|
||||
..strokeCap = StrokeCap.round
|
||||
..strokeJoin = StrokeJoin.round,
|
||||
);
|
||||
}
|
||||
|
||||
TextStyle get style {
|
||||
return TextStyle(
|
||||
height: 1.4,
|
||||
fontSize: fontSize,
|
||||
fontWeight: fontWeight,
|
||||
fontFamily: GoogleFonts.openSans().fontFamily,
|
||||
letterSpacing: 0.0,
|
||||
wordSpacing: 0.0,
|
||||
color: color,
|
||||
);
|
||||
}
|
||||
|
||||
Map<String, dynamic> toMap() {
|
||||
return <String, dynamic>{
|
||||
'fontSize': fontSize,
|
||||
'fontWeight': fontWeight.value,
|
||||
'verticalOffset': verticalOffset,
|
||||
'color': color.value,
|
||||
'outlineColor': outlineColor.value,
|
||||
'outlineSize': outlineSize,
|
||||
'backGroundColor': backGroundColor.value,
|
||||
'shadow': shadow,
|
||||
};
|
||||
}
|
||||
|
||||
String toJson() => json.encode(toMap());
|
||||
|
||||
factory SubtitleSettingsModel.fromJson(String source) => SubtitleSettingsModel.fromMap(json.decode(source));
|
||||
|
||||
factory SubtitleSettingsModel.fromMap(Map<String, dynamic> map) {
|
||||
return const SubtitleSettingsModel().copyWith(
|
||||
fontSize: map['fontSize'] as double?,
|
||||
fontWeight: FontWeight.values.firstWhereOrNull((element) => element.index == map['fontWeight'] as int?),
|
||||
verticalOffset: map['verticalOffset'] as double?,
|
||||
color: map['color'] != null ? Color(map['color'] as int) : null,
|
||||
outlineColor: map['outlineColor'] != null ? Color(map['outlineColor'] as int) : null,
|
||||
outlineSize: map['outlineSize'] as double?,
|
||||
backGroundColor: map['backGroundColor'] != null ? Color(map['backGroundColor'] as int) : null,
|
||||
shadow: map['shadow'] as double?,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'SubtitleSettingsModel(fontSize: $fontSize, fontWeight: $fontWeight, verticalOffset: $verticalOffset, color: $color, outlineColor: $outlineColor, outlineSize: $outlineSize, backGroundColor: $backGroundColor, shadow: $shadow)';
|
||||
}
|
||||
|
||||
@override
|
||||
bool operator ==(covariant SubtitleSettingsModel other) {
|
||||
if (identical(this, other)) return true;
|
||||
|
||||
return other.fontSize == fontSize &&
|
||||
other.fontWeight == fontWeight &&
|
||||
other.verticalOffset == verticalOffset &&
|
||||
other.color == color &&
|
||||
other.outlineColor == outlineColor &&
|
||||
other.outlineSize == outlineSize &&
|
||||
other.backGroundColor == backGroundColor &&
|
||||
other.shadow == shadow;
|
||||
}
|
||||
|
||||
@override
|
||||
int get hashCode {
|
||||
return fontSize.hashCode ^
|
||||
fontWeight.hashCode ^
|
||||
verticalOffset.hashCode ^
|
||||
color.hashCode ^
|
||||
outlineColor.hashCode ^
|
||||
outlineSize.hashCode ^
|
||||
backGroundColor.hashCode ^
|
||||
shadow.hashCode;
|
||||
}
|
||||
}
|
||||
|
||||
class SubtitleText extends ConsumerWidget {
|
||||
final SubtitleSettingsModel subModel;
|
||||
final EdgeInsets padding;
|
||||
final String text;
|
||||
final double offset;
|
||||
const SubtitleText({
|
||||
required this.subModel,
|
||||
required this.padding,
|
||||
required this.offset,
|
||||
required this.text,
|
||||
super.key,
|
||||
});
|
||||
|
||||
// The reference width for calculating the visible text scale factor.
|
||||
static const kTextScaleFactorReferenceWidth = 1920.0;
|
||||
// The reference height for calculating the visible text scale factor.
|
||||
static const kTextScaleFactorReferenceHeight = 1080.0;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
final fillScreen = ref.watch(videoPlayerSettingsProvider.select((value) => value.fillScreen));
|
||||
return Padding(
|
||||
padding: (fillScreen ? EdgeInsets.zero : EdgeInsets.only(left: padding.left, right: padding.right))
|
||||
.add(const EdgeInsets.all(16)),
|
||||
child: LayoutBuilder(
|
||||
builder: (context, constraints) {
|
||||
final textScale = MediaQuery.textScalerOf(context)
|
||||
.scale((ref.read(subtitleSettingsProvider.select((value) => value.fontSize)) *
|
||||
math.sqrt(
|
||||
((constraints.maxWidth * constraints.maxHeight) /
|
||||
(kTextScaleFactorReferenceWidth * kTextScaleFactorReferenceHeight))
|
||||
.clamp(0.0, 1.0),
|
||||
)));
|
||||
|
||||
// Function to calculate the height of the text
|
||||
double getTextHeight(BuildContext context, String text, TextStyle style) {
|
||||
final TextPainter textPainter = TextPainter(
|
||||
text: TextSpan(text: text, style: style),
|
||||
textDirection: TextDirection.ltr,
|
||||
textScaler: MediaQuery.textScalerOf(context),
|
||||
)..layout(minWidth: 0, maxWidth: double.infinity);
|
||||
|
||||
return textPainter.height;
|
||||
}
|
||||
|
||||
// Calculate the available height for the text alignment
|
||||
double availableHeight = constraints.maxHeight;
|
||||
|
||||
// Calculate the desired position based on the percentage
|
||||
double desiredPosition = availableHeight * offset;
|
||||
|
||||
// Get the height of the Text widget with the current font style
|
||||
double textHeight = getTextHeight(context, text, subModel.style);
|
||||
|
||||
// Calculate the position to keep the text within visible bounds
|
||||
double position = desiredPosition - textHeight / 2;
|
||||
|
||||
// Ensure the text doesn't go off-screen
|
||||
position = position.clamp(0, availableHeight - textHeight);
|
||||
|
||||
return IgnorePointer(
|
||||
child: Stack(
|
||||
alignment: Alignment.bottomCenter,
|
||||
children: [
|
||||
Positioned(
|
||||
bottom: position,
|
||||
child: Container(
|
||||
constraints: BoxConstraints(maxWidth: constraints.maxWidth, maxHeight: constraints.maxHeight),
|
||||
decoration: BoxDecoration(
|
||||
color: subModel.backGroundColor,
|
||||
borderRadius: BorderRadius.circular(16),
|
||||
),
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 16),
|
||||
child: Text(
|
||||
text,
|
||||
style: subModel.backGroundStyle.copyWith(fontSize: textScale),
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
Positioned(
|
||||
bottom: position,
|
||||
child: Container(
|
||||
constraints: BoxConstraints(maxWidth: constraints.maxWidth, maxHeight: constraints.maxHeight),
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 16),
|
||||
child: Text(
|
||||
text,
|
||||
style: subModel.style.copyWith(fontSize: textScale),
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
),
|
||||
),
|
||||
)
|
||||
],
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
113
lib/models/settings/video_player_settings.dart
Normal file
113
lib/models/settings/video_player_settings.dart
Normal file
|
|
@ -0,0 +1,113 @@
|
|||
// ignore_for_file: public_member_api_docs, sort_constructors_first
|
||||
import 'dart:convert';
|
||||
|
||||
import 'package:collection/collection.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/widgets.dart';
|
||||
|
||||
class VideoPlayerSettingsModel {
|
||||
final double? screenBrightness;
|
||||
final BoxFit videoFit;
|
||||
final bool fillScreen;
|
||||
final bool hardwareAccel;
|
||||
final bool useLibass;
|
||||
final double internalVolume;
|
||||
final String? audioDevice;
|
||||
const VideoPlayerSettingsModel({
|
||||
this.screenBrightness,
|
||||
this.videoFit = BoxFit.contain,
|
||||
this.fillScreen = false,
|
||||
this.hardwareAccel = true,
|
||||
this.useLibass = false,
|
||||
this.internalVolume = 100,
|
||||
this.audioDevice,
|
||||
});
|
||||
|
||||
double get volume => switch (defaultTargetPlatform) {
|
||||
TargetPlatform.android || TargetPlatform.iOS => 100,
|
||||
_ => internalVolume,
|
||||
};
|
||||
|
||||
VideoPlayerSettingsModel copyWith({
|
||||
ValueGetter<double?>? screenBrightness,
|
||||
BoxFit? videoFit,
|
||||
bool? fillScreen,
|
||||
bool? hardwareAccel,
|
||||
bool? useLibass,
|
||||
double? internalVolume,
|
||||
ValueGetter<String?>? audioDevice,
|
||||
}) {
|
||||
return VideoPlayerSettingsModel(
|
||||
screenBrightness: screenBrightness != null ? screenBrightness() : this.screenBrightness,
|
||||
videoFit: videoFit ?? this.videoFit,
|
||||
fillScreen: fillScreen ?? this.fillScreen,
|
||||
hardwareAccel: hardwareAccel ?? this.hardwareAccel,
|
||||
useLibass: useLibass ?? this.useLibass,
|
||||
internalVolume: internalVolume ?? this.internalVolume,
|
||||
audioDevice: audioDevice != null ? audioDevice() : this.audioDevice,
|
||||
);
|
||||
}
|
||||
|
||||
Map<String, dynamic> toMap() {
|
||||
return {
|
||||
'screenBrightness': screenBrightness,
|
||||
'videoFit': videoFit.name,
|
||||
'fillScreen': fillScreen,
|
||||
'hardwareAccel': hardwareAccel,
|
||||
'useLibass': useLibass,
|
||||
'internalVolume': internalVolume,
|
||||
'audioDevice': audioDevice,
|
||||
};
|
||||
}
|
||||
|
||||
factory VideoPlayerSettingsModel.fromMap(Map<String, dynamic> map) {
|
||||
return VideoPlayerSettingsModel(
|
||||
screenBrightness: map['screenBrightness']?.toDouble(),
|
||||
videoFit: BoxFit.values.firstWhereOrNull((element) => element.name == map['videoFit']) ?? BoxFit.contain,
|
||||
fillScreen: map['fillScreen'] ?? false,
|
||||
hardwareAccel: map['hardwareAccel'] ?? false,
|
||||
useLibass: map['useLibass'] ?? false,
|
||||
internalVolume: map['internalVolume']?.toDouble() ?? 0.0,
|
||||
audioDevice: map['audioDevice'],
|
||||
);
|
||||
}
|
||||
|
||||
String toJson() => json.encode(toMap());
|
||||
|
||||
factory VideoPlayerSettingsModel.fromJson(String source) => VideoPlayerSettingsModel.fromMap(json.decode(source));
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'VideoPlayerSettingsModel(screenBrightness: $screenBrightness, videoFit: $videoFit, fillScreen: $fillScreen, hardwareAccel: $hardwareAccel, useLibass: $useLibass, internalVolume: $internalVolume, audioDevice: $audioDevice)';
|
||||
}
|
||||
|
||||
bool playerSame(VideoPlayerSettingsModel other) {
|
||||
return other.hardwareAccel == hardwareAccel && other.useLibass == useLibass;
|
||||
}
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
if (identical(this, other)) return true;
|
||||
|
||||
return other is VideoPlayerSettingsModel &&
|
||||
other.screenBrightness == screenBrightness &&
|
||||
other.videoFit == videoFit &&
|
||||
other.fillScreen == fillScreen &&
|
||||
other.hardwareAccel == hardwareAccel &&
|
||||
other.useLibass == useLibass &&
|
||||
other.internalVolume == internalVolume &&
|
||||
other.audioDevice == audioDevice;
|
||||
}
|
||||
|
||||
@override
|
||||
int get hashCode {
|
||||
return screenBrightness.hashCode ^
|
||||
videoFit.hashCode ^
|
||||
fillScreen.hashCode ^
|
||||
hardwareAccel.hashCode ^
|
||||
useLibass.hashCode ^
|
||||
internalVolume.hashCode ^
|
||||
audioDevice.hashCode;
|
||||
}
|
||||
}
|
||||
41
lib/models/syncing/download_stream.dart
Normal file
41
lib/models/syncing/download_stream.dart
Normal file
|
|
@ -0,0 +1,41 @@
|
|||
import 'package:background_downloader/background_downloader.dart' as dl;
|
||||
|
||||
class DownloadStream {
|
||||
final String id;
|
||||
final dl.DownloadTask? task;
|
||||
final double progress;
|
||||
final dl.TaskStatus status;
|
||||
DownloadStream({
|
||||
required this.id,
|
||||
this.task,
|
||||
required this.progress,
|
||||
required this.status,
|
||||
});
|
||||
|
||||
DownloadStream.empty()
|
||||
: id = '',
|
||||
task = null,
|
||||
progress = -1,
|
||||
status = dl.TaskStatus.notFound;
|
||||
|
||||
bool get hasDownload => progress != -1.0 && status != dl.TaskStatus.notFound && status != dl.TaskStatus.complete;
|
||||
|
||||
DownloadStream copyWith({
|
||||
String? id,
|
||||
dl.DownloadTask? task,
|
||||
double? progress,
|
||||
dl.TaskStatus? status,
|
||||
}) {
|
||||
return DownloadStream(
|
||||
id: id ?? this.id,
|
||||
task: task ?? this.task,
|
||||
progress: progress ?? this.progress,
|
||||
status: status ?? this.status,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'DownloadStream(id: $id, task: $task, progress: $progress, status: $status)';
|
||||
}
|
||||
}
|
||||
75
lib/models/syncing/i_synced_item.dart
Normal file
75
lib/models/syncing/i_synced_item.dart
Normal file
|
|
@ -0,0 +1,75 @@
|
|||
import 'dart:convert';
|
||||
|
||||
import 'package:fladder/models/syncing/sync_item.dart';
|
||||
import 'package:isar/isar.dart';
|
||||
|
||||
part 'i_synced_item.g.dart';
|
||||
|
||||
// extension IsarExtensions on String? {
|
||||
// int get fastHash {
|
||||
// if (this == null) return 0;
|
||||
// var hash = 0xcbf29ce484222325;
|
||||
|
||||
// var i = 0;
|
||||
// while (i < this!.length) {
|
||||
// final codeUnit = this!.codeUnitAt(i++);
|
||||
// hash ^= codeUnit >> 8;
|
||||
// hash *= 0x100000001b3;
|
||||
// hash ^= codeUnit & 0xFF;
|
||||
// hash *= 0x100000001b3;
|
||||
// }
|
||||
|
||||
// return hash;
|
||||
// }
|
||||
// }
|
||||
|
||||
@collection
|
||||
class ISyncedItem {
|
||||
String? userId;
|
||||
String id;
|
||||
int? sortKey;
|
||||
String? parentId;
|
||||
String? path;
|
||||
int? fileSize;
|
||||
String? videoFileName;
|
||||
String? trickPlayModel;
|
||||
String? introOutroSkipModel;
|
||||
String? images;
|
||||
List<String>? chapters;
|
||||
List<String>? subtitles;
|
||||
String? userData;
|
||||
ISyncedItem({
|
||||
this.userId,
|
||||
required this.id,
|
||||
this.sortKey,
|
||||
this.parentId,
|
||||
this.path,
|
||||
this.fileSize,
|
||||
this.videoFileName,
|
||||
this.trickPlayModel,
|
||||
this.introOutroSkipModel,
|
||||
this.images,
|
||||
this.chapters,
|
||||
this.subtitles,
|
||||
this.userData,
|
||||
});
|
||||
|
||||
factory ISyncedItem.fromSynced(SyncedItem syncedItem, String? path) {
|
||||
return ISyncedItem(
|
||||
id: syncedItem.id,
|
||||
parentId: syncedItem.parentId,
|
||||
userId: syncedItem.userId,
|
||||
path: syncedItem.path?.replaceAll(path ?? "", '').substring(1),
|
||||
fileSize: syncedItem.fileSize,
|
||||
sortKey: syncedItem.sortKey,
|
||||
videoFileName: syncedItem.videoFileName,
|
||||
trickPlayModel: syncedItem.fTrickPlayModel != null ? jsonEncode(syncedItem.fTrickPlayModel?.toJson()) : null,
|
||||
introOutroSkipModel:
|
||||
syncedItem.introOutSkipModel != null ? jsonEncode(syncedItem.introOutSkipModel?.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(),
|
||||
userData: syncedItem.userData != null ? jsonEncode(syncedItem.userData?.toJson()) : null,
|
||||
);
|
||||
}
|
||||
}
|
||||
3600
lib/models/syncing/i_synced_item.g.dart
Normal file
3600
lib/models/syncing/i_synced_item.g.dart
Normal file
File diff suppressed because it is too large
Load diff
197
lib/models/syncing/sync_item.dart
Normal file
197
lib/models/syncing/sync_item.dart
Normal file
|
|
@ -0,0 +1,197 @@
|
|||
import 'dart:convert';
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:background_downloader/background_downloader.dart';
|
||||
import 'package:ficonsax/ficonsax.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/images_models.dart';
|
||||
import 'package:fladder/models/items/intro_skip_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';
|
||||
import 'package:fladder/models/syncing/i_synced_item.dart';
|
||||
import 'package:fladder/providers/sync/sync_provider_helpers.dart';
|
||||
import 'package:fladder/providers/sync_provider.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:freezed_annotation/freezed_annotation.dart';
|
||||
import 'package:path/path.dart';
|
||||
|
||||
part 'sync_item.freezed.dart';
|
||||
part 'sync_item.g.dart';
|
||||
|
||||
@freezed
|
||||
class SyncedItem with _$SyncedItem {
|
||||
const SyncedItem._();
|
||||
|
||||
factory SyncedItem({
|
||||
required String id,
|
||||
String? parentId,
|
||||
required String userId,
|
||||
String? path,
|
||||
@Default(false) bool markedForDelete,
|
||||
int? sortKey,
|
||||
int? fileSize,
|
||||
String? videoFileName,
|
||||
IntroOutSkipModel? introOutSkipModel,
|
||||
TrickPlayModel? fTrickPlayModel,
|
||||
ImagesData? fImages,
|
||||
@Default([]) List<Chapter> fChapters,
|
||||
@Default([]) List<SubStreamModel> subtitles,
|
||||
@UserDataJsonSerializer() UserData? userData,
|
||||
}) = _SyncItem;
|
||||
|
||||
static String trickPlayPath = "TrickPlay";
|
||||
|
||||
List<Chapter> get chapters => fChapters.map((e) => e.copyWith(imageUrl: joinAll({"$path", e.imageUrl}))).toList();
|
||||
|
||||
ImagesData? get images => fImages?.copyWith(
|
||||
primary: () => fImages?.primary?.copyWith(path: joinAll(["$path", "${fImages?.primary?.path}"])),
|
||||
logo: () => fImages?.logo?.copyWith(path: joinAll(["$path", "${fImages?.logo?.path}"])),
|
||||
backDrop: () => fImages?.backDrop?.map((e) => e.copyWith(path: joinAll(["$path", (e.path)]))).toList(),
|
||||
);
|
||||
|
||||
TrickPlayModel? get trickPlayModel => fTrickPlayModel?.copyWith(
|
||||
images: fTrickPlayModel?.images
|
||||
.map(
|
||||
(trickPlayPath) => joinAll(["$path", trickPlayPath]),
|
||||
)
|
||||
.toList() ??
|
||||
[]);
|
||||
|
||||
File get dataFile => File(joinAll(["$path", "data.json"]));
|
||||
Directory get trickPlayDirectory => Directory(joinAll(["$path", trickPlayPath]));
|
||||
File get videoFile => File(joinAll(["$path", "$videoFileName"]));
|
||||
Directory get directory => Directory(path ?? "");
|
||||
|
||||
SyncStatus get status => switch (videoFile.existsSync()) {
|
||||
true => SyncStatus.complete,
|
||||
_ => SyncStatus.partially,
|
||||
};
|
||||
|
||||
String? get taskId => null;
|
||||
|
||||
bool get childHasTask => false;
|
||||
|
||||
double get totalProgress => 0.0;
|
||||
|
||||
bool get hasVideoFile => videoFileName?.isNotEmpty == true && (fileSize ?? 0) > 0;
|
||||
|
||||
TaskStatus get anyStatus {
|
||||
return TaskStatus.notFound;
|
||||
}
|
||||
|
||||
double get downloadProgress => 0.0;
|
||||
TaskStatus get downloadStatus => TaskStatus.notFound;
|
||||
DownloadTask? get task => null;
|
||||
|
||||
Future<bool> deleteDatFiles(Ref ref) async {
|
||||
try {
|
||||
await videoFile.delete();
|
||||
await Directory(joinAll([directory.path, trickPlayPath])).delete(recursive: true);
|
||||
} catch (e) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
List<SyncedItem> nestedChildren(WidgetRef ref) => ref.watch(syncChildrenProvider(this));
|
||||
|
||||
List<SyncedItem> getChildren(Ref ref) => ref.read(syncProvider.notifier).getChildren(this);
|
||||
List<SyncedItem> getNestedChildren(Ref ref) => ref.read(syncProvider.notifier).getNestedChildren(this);
|
||||
|
||||
Future<int> get getDirSize async {
|
||||
var files = await directory.list(recursive: true).toList();
|
||||
var dirSize = files.fold(0, (int sum, file) => sum + file.statSync().size);
|
||||
return dirSize;
|
||||
}
|
||||
|
||||
ItemBaseModel? createItemModel(Ref ref) {
|
||||
if (!dataFile.existsSync()) return null;
|
||||
final BaseItemDto itemDto = BaseItemDto.fromJson(jsonDecode(dataFile.readAsStringSync()));
|
||||
final itemModel = ItemBaseModel.fromBaseDto(itemDto, ref);
|
||||
return itemModel.copyWith(
|
||||
images: images,
|
||||
userData: userData,
|
||||
);
|
||||
}
|
||||
|
||||
factory SyncedItem.fromJson(Map<String, dynamic> json) => _$SyncedItemFromJson(json);
|
||||
|
||||
factory SyncedItem.fromIsar(ISyncedItem isarSyncedItem, String savePath) {
|
||||
return SyncedItem(
|
||||
id: isarSyncedItem.id,
|
||||
parentId: isarSyncedItem.parentId,
|
||||
userId: isarSyncedItem.userId ?? "",
|
||||
sortKey: isarSyncedItem.sortKey,
|
||||
path: joinAll([savePath, isarSyncedItem.path ?? ""]),
|
||||
fileSize: isarSyncedItem.fileSize,
|
||||
videoFileName: isarSyncedItem.videoFileName,
|
||||
introOutSkipModel: isarSyncedItem.introOutroSkipModel != null
|
||||
? IntroOutSkipModel.fromJson(jsonDecode(isarSyncedItem.introOutroSkipModel!))
|
||||
: null,
|
||||
fTrickPlayModel: isarSyncedItem.trickPlayModel != null
|
||||
? TrickPlayModel.fromJson(jsonDecode(isarSyncedItem.trickPlayModel!))
|
||||
: null,
|
||||
fImages: isarSyncedItem.images != null ? ImagesData.fromJson(jsonDecode(isarSyncedItem.images!)) : null,
|
||||
fChapters: isarSyncedItem.chapters
|
||||
?.map(
|
||||
(e) => Chapter.fromJson(jsonDecode(e)),
|
||||
)
|
||||
.toList() ??
|
||||
[],
|
||||
subtitles: isarSyncedItem.subtitles
|
||||
?.map(
|
||||
(e) => SubStreamModel.fromJson(jsonDecode(e)),
|
||||
)
|
||||
.toList() ??
|
||||
[],
|
||||
userData: isarSyncedItem.userData != null ? UserData.fromJson(jsonDecode(isarSyncedItem.userData!)) : null,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
enum SyncStatus {
|
||||
complete(
|
||||
"Synced",
|
||||
Color.fromARGB(255, 141, 214, 58),
|
||||
IconsaxOutline.tick_circle,
|
||||
),
|
||||
partially(
|
||||
"Partially",
|
||||
Color.fromARGB(255, 221, 135, 23),
|
||||
IconsaxOutline.more_circle,
|
||||
),
|
||||
;
|
||||
|
||||
const SyncStatus(this.label, this.color, this.icon);
|
||||
|
||||
final Color color;
|
||||
final String label;
|
||||
final IconData icon;
|
||||
}
|
||||
|
||||
extension StatusExtension on TaskStatus {
|
||||
Color color(BuildContext context) => switch (this) {
|
||||
TaskStatus.enqueued => Colors.blueAccent,
|
||||
TaskStatus.running => Colors.limeAccent,
|
||||
TaskStatus.complete => Colors.limeAccent,
|
||||
TaskStatus.canceled || TaskStatus.notFound || TaskStatus.failed => Theme.of(context).colorScheme.error,
|
||||
TaskStatus.waitingToRetry => Colors.yellowAccent,
|
||||
TaskStatus.paused => Colors.orangeAccent,
|
||||
};
|
||||
|
||||
String get name => switch (this) {
|
||||
TaskStatus.enqueued => 'Enqueued',
|
||||
TaskStatus.running => 'Running',
|
||||
TaskStatus.complete => 'Complete',
|
||||
TaskStatus.notFound => 'Not Found',
|
||||
TaskStatus.failed => 'Failed',
|
||||
TaskStatus.canceled => 'Canceled',
|
||||
TaskStatus.waitingToRetry => 'Waiting To Retry',
|
||||
TaskStatus.paused => 'Paused',
|
||||
};
|
||||
}
|
||||
494
lib/models/syncing/sync_item.freezed.dart
Normal file
494
lib/models/syncing/sync_item.freezed.dart
Normal file
|
|
@ -0,0 +1,494 @@
|
|||
// 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 'sync_item.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');
|
||||
|
||||
SyncedItem _$SyncedItemFromJson(Map<String, dynamic> json) {
|
||||
return _SyncItem.fromJson(json);
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
mixin _$SyncedItem {
|
||||
String get id => throw _privateConstructorUsedError;
|
||||
String? get parentId => throw _privateConstructorUsedError;
|
||||
String get userId => throw _privateConstructorUsedError;
|
||||
String? get path => throw _privateConstructorUsedError;
|
||||
bool get markedForDelete => throw _privateConstructorUsedError;
|
||||
int? get sortKey => throw _privateConstructorUsedError;
|
||||
int? get fileSize => throw _privateConstructorUsedError;
|
||||
String? get videoFileName => throw _privateConstructorUsedError;
|
||||
IntroOutSkipModel? get introOutSkipModel =>
|
||||
throw _privateConstructorUsedError;
|
||||
TrickPlayModel? get fTrickPlayModel => throw _privateConstructorUsedError;
|
||||
ImagesData? get fImages => throw _privateConstructorUsedError;
|
||||
List<Chapter> get fChapters => throw _privateConstructorUsedError;
|
||||
List<SubStreamModel> get subtitles => throw _privateConstructorUsedError;
|
||||
@UserDataJsonSerializer()
|
||||
UserData? get userData => throw _privateConstructorUsedError;
|
||||
|
||||
Map<String, dynamic> toJson() => throw _privateConstructorUsedError;
|
||||
@JsonKey(ignore: true)
|
||||
$SyncedItemCopyWith<SyncedItem> get copyWith =>
|
||||
throw _privateConstructorUsedError;
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
abstract class $SyncedItemCopyWith<$Res> {
|
||||
factory $SyncedItemCopyWith(
|
||||
SyncedItem value, $Res Function(SyncedItem) then) =
|
||||
_$SyncedItemCopyWithImpl<$Res, SyncedItem>;
|
||||
@useResult
|
||||
$Res call(
|
||||
{String id,
|
||||
String? parentId,
|
||||
String userId,
|
||||
String? path,
|
||||
bool markedForDelete,
|
||||
int? sortKey,
|
||||
int? fileSize,
|
||||
String? videoFileName,
|
||||
IntroOutSkipModel? introOutSkipModel,
|
||||
TrickPlayModel? fTrickPlayModel,
|
||||
ImagesData? fImages,
|
||||
List<Chapter> fChapters,
|
||||
List<SubStreamModel> subtitles,
|
||||
@UserDataJsonSerializer() UserData? userData});
|
||||
|
||||
$IntroOutSkipModelCopyWith<$Res>? get introOutSkipModel;
|
||||
$TrickPlayModelCopyWith<$Res>? get fTrickPlayModel;
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
class _$SyncedItemCopyWithImpl<$Res, $Val extends SyncedItem>
|
||||
implements $SyncedItemCopyWith<$Res> {
|
||||
_$SyncedItemCopyWithImpl(this._value, this._then);
|
||||
|
||||
// ignore: unused_field
|
||||
final $Val _value;
|
||||
// ignore: unused_field
|
||||
final $Res Function($Val) _then;
|
||||
|
||||
@pragma('vm:prefer-inline')
|
||||
@override
|
||||
$Res call({
|
||||
Object? id = null,
|
||||
Object? parentId = freezed,
|
||||
Object? userId = null,
|
||||
Object? path = freezed,
|
||||
Object? markedForDelete = null,
|
||||
Object? sortKey = freezed,
|
||||
Object? fileSize = freezed,
|
||||
Object? videoFileName = freezed,
|
||||
Object? introOutSkipModel = freezed,
|
||||
Object? fTrickPlayModel = freezed,
|
||||
Object? fImages = freezed,
|
||||
Object? fChapters = null,
|
||||
Object? subtitles = null,
|
||||
Object? userData = freezed,
|
||||
}) {
|
||||
return _then(_value.copyWith(
|
||||
id: null == id
|
||||
? _value.id
|
||||
: id // ignore: cast_nullable_to_non_nullable
|
||||
as String,
|
||||
parentId: freezed == parentId
|
||||
? _value.parentId
|
||||
: parentId // ignore: cast_nullable_to_non_nullable
|
||||
as String?,
|
||||
userId: null == userId
|
||||
? _value.userId
|
||||
: userId // ignore: cast_nullable_to_non_nullable
|
||||
as String,
|
||||
path: freezed == path
|
||||
? _value.path
|
||||
: path // ignore: cast_nullable_to_non_nullable
|
||||
as String?,
|
||||
markedForDelete: null == markedForDelete
|
||||
? _value.markedForDelete
|
||||
: markedForDelete // ignore: cast_nullable_to_non_nullable
|
||||
as bool,
|
||||
sortKey: freezed == sortKey
|
||||
? _value.sortKey
|
||||
: sortKey // ignore: cast_nullable_to_non_nullable
|
||||
as int?,
|
||||
fileSize: freezed == fileSize
|
||||
? _value.fileSize
|
||||
: fileSize // ignore: cast_nullable_to_non_nullable
|
||||
as int?,
|
||||
videoFileName: freezed == videoFileName
|
||||
? _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?,
|
||||
fTrickPlayModel: freezed == fTrickPlayModel
|
||||
? _value.fTrickPlayModel
|
||||
: fTrickPlayModel // ignore: cast_nullable_to_non_nullable
|
||||
as TrickPlayModel?,
|
||||
fImages: freezed == fImages
|
||||
? _value.fImages
|
||||
: fImages // ignore: cast_nullable_to_non_nullable
|
||||
as ImagesData?,
|
||||
fChapters: null == fChapters
|
||||
? _value.fChapters
|
||||
: fChapters // ignore: cast_nullable_to_non_nullable
|
||||
as List<Chapter>,
|
||||
subtitles: null == subtitles
|
||||
? _value.subtitles
|
||||
: subtitles // ignore: cast_nullable_to_non_nullable
|
||||
as List<SubStreamModel>,
|
||||
userData: freezed == userData
|
||||
? _value.userData
|
||||
: userData // ignore: cast_nullable_to_non_nullable
|
||||
as UserData?,
|
||||
) as $Val);
|
||||
}
|
||||
|
||||
@override
|
||||
@pragma('vm:prefer-inline')
|
||||
$IntroOutSkipModelCopyWith<$Res>? get introOutSkipModel {
|
||||
if (_value.introOutSkipModel == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return $IntroOutSkipModelCopyWith<$Res>(_value.introOutSkipModel!, (value) {
|
||||
return _then(_value.copyWith(introOutSkipModel: value) as $Val);
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
@pragma('vm:prefer-inline')
|
||||
$TrickPlayModelCopyWith<$Res>? get fTrickPlayModel {
|
||||
if (_value.fTrickPlayModel == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return $TrickPlayModelCopyWith<$Res>(_value.fTrickPlayModel!, (value) {
|
||||
return _then(_value.copyWith(fTrickPlayModel: value) as $Val);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
abstract class _$$SyncItemImplCopyWith<$Res>
|
||||
implements $SyncedItemCopyWith<$Res> {
|
||||
factory _$$SyncItemImplCopyWith(
|
||||
_$SyncItemImpl value, $Res Function(_$SyncItemImpl) then) =
|
||||
__$$SyncItemImplCopyWithImpl<$Res>;
|
||||
@override
|
||||
@useResult
|
||||
$Res call(
|
||||
{String id,
|
||||
String? parentId,
|
||||
String userId,
|
||||
String? path,
|
||||
bool markedForDelete,
|
||||
int? sortKey,
|
||||
int? fileSize,
|
||||
String? videoFileName,
|
||||
IntroOutSkipModel? introOutSkipModel,
|
||||
TrickPlayModel? fTrickPlayModel,
|
||||
ImagesData? fImages,
|
||||
List<Chapter> fChapters,
|
||||
List<SubStreamModel> subtitles,
|
||||
@UserDataJsonSerializer() UserData? userData});
|
||||
|
||||
@override
|
||||
$IntroOutSkipModelCopyWith<$Res>? get introOutSkipModel;
|
||||
@override
|
||||
$TrickPlayModelCopyWith<$Res>? get fTrickPlayModel;
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
class __$$SyncItemImplCopyWithImpl<$Res>
|
||||
extends _$SyncedItemCopyWithImpl<$Res, _$SyncItemImpl>
|
||||
implements _$$SyncItemImplCopyWith<$Res> {
|
||||
__$$SyncItemImplCopyWithImpl(
|
||||
_$SyncItemImpl _value, $Res Function(_$SyncItemImpl) _then)
|
||||
: super(_value, _then);
|
||||
|
||||
@pragma('vm:prefer-inline')
|
||||
@override
|
||||
$Res call({
|
||||
Object? id = null,
|
||||
Object? parentId = freezed,
|
||||
Object? userId = null,
|
||||
Object? path = freezed,
|
||||
Object? markedForDelete = null,
|
||||
Object? sortKey = freezed,
|
||||
Object? fileSize = freezed,
|
||||
Object? videoFileName = freezed,
|
||||
Object? introOutSkipModel = freezed,
|
||||
Object? fTrickPlayModel = freezed,
|
||||
Object? fImages = freezed,
|
||||
Object? fChapters = null,
|
||||
Object? subtitles = null,
|
||||
Object? userData = freezed,
|
||||
}) {
|
||||
return _then(_$SyncItemImpl(
|
||||
id: null == id
|
||||
? _value.id
|
||||
: id // ignore: cast_nullable_to_non_nullable
|
||||
as String,
|
||||
parentId: freezed == parentId
|
||||
? _value.parentId
|
||||
: parentId // ignore: cast_nullable_to_non_nullable
|
||||
as String?,
|
||||
userId: null == userId
|
||||
? _value.userId
|
||||
: userId // ignore: cast_nullable_to_non_nullable
|
||||
as String,
|
||||
path: freezed == path
|
||||
? _value.path
|
||||
: path // ignore: cast_nullable_to_non_nullable
|
||||
as String?,
|
||||
markedForDelete: null == markedForDelete
|
||||
? _value.markedForDelete
|
||||
: markedForDelete // ignore: cast_nullable_to_non_nullable
|
||||
as bool,
|
||||
sortKey: freezed == sortKey
|
||||
? _value.sortKey
|
||||
: sortKey // ignore: cast_nullable_to_non_nullable
|
||||
as int?,
|
||||
fileSize: freezed == fileSize
|
||||
? _value.fileSize
|
||||
: fileSize // ignore: cast_nullable_to_non_nullable
|
||||
as int?,
|
||||
videoFileName: freezed == videoFileName
|
||||
? _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?,
|
||||
fTrickPlayModel: freezed == fTrickPlayModel
|
||||
? _value.fTrickPlayModel
|
||||
: fTrickPlayModel // ignore: cast_nullable_to_non_nullable
|
||||
as TrickPlayModel?,
|
||||
fImages: freezed == fImages
|
||||
? _value.fImages
|
||||
: fImages // ignore: cast_nullable_to_non_nullable
|
||||
as ImagesData?,
|
||||
fChapters: null == fChapters
|
||||
? _value._fChapters
|
||||
: fChapters // ignore: cast_nullable_to_non_nullable
|
||||
as List<Chapter>,
|
||||
subtitles: null == subtitles
|
||||
? _value._subtitles
|
||||
: subtitles // ignore: cast_nullable_to_non_nullable
|
||||
as List<SubStreamModel>,
|
||||
userData: freezed == userData
|
||||
? _value.userData
|
||||
: userData // ignore: cast_nullable_to_non_nullable
|
||||
as UserData?,
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
@JsonSerializable()
|
||||
class _$SyncItemImpl extends _SyncItem {
|
||||
_$SyncItemImpl(
|
||||
{required this.id,
|
||||
this.parentId,
|
||||
required this.userId,
|
||||
this.path,
|
||||
this.markedForDelete = false,
|
||||
this.sortKey,
|
||||
this.fileSize,
|
||||
this.videoFileName,
|
||||
this.introOutSkipModel,
|
||||
this.fTrickPlayModel,
|
||||
this.fImages,
|
||||
final List<Chapter> fChapters = const [],
|
||||
final List<SubStreamModel> subtitles = const [],
|
||||
@UserDataJsonSerializer() this.userData})
|
||||
: _fChapters = fChapters,
|
||||
_subtitles = subtitles,
|
||||
super._();
|
||||
|
||||
factory _$SyncItemImpl.fromJson(Map<String, dynamic> json) =>
|
||||
_$$SyncItemImplFromJson(json);
|
||||
|
||||
@override
|
||||
final String id;
|
||||
@override
|
||||
final String? parentId;
|
||||
@override
|
||||
final String userId;
|
||||
@override
|
||||
final String? path;
|
||||
@override
|
||||
@JsonKey()
|
||||
final bool markedForDelete;
|
||||
@override
|
||||
final int? sortKey;
|
||||
@override
|
||||
final int? fileSize;
|
||||
@override
|
||||
final String? videoFileName;
|
||||
@override
|
||||
final IntroOutSkipModel? introOutSkipModel;
|
||||
@override
|
||||
final TrickPlayModel? fTrickPlayModel;
|
||||
@override
|
||||
final ImagesData? fImages;
|
||||
final List<Chapter> _fChapters;
|
||||
@override
|
||||
@JsonKey()
|
||||
List<Chapter> get fChapters {
|
||||
if (_fChapters is EqualUnmodifiableListView) return _fChapters;
|
||||
// ignore: implicit_dynamic_type
|
||||
return EqualUnmodifiableListView(_fChapters);
|
||||
}
|
||||
|
||||
final List<SubStreamModel> _subtitles;
|
||||
@override
|
||||
@JsonKey()
|
||||
List<SubStreamModel> get subtitles {
|
||||
if (_subtitles is EqualUnmodifiableListView) return _subtitles;
|
||||
// ignore: implicit_dynamic_type
|
||||
return EqualUnmodifiableListView(_subtitles);
|
||||
}
|
||||
|
||||
@override
|
||||
@UserDataJsonSerializer()
|
||||
final UserData? userData;
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'SyncedItem(id: $id, parentId: $parentId, userId: $userId, path: $path, markedForDelete: $markedForDelete, sortKey: $sortKey, fileSize: $fileSize, videoFileName: $videoFileName, introOutSkipModel: $introOutSkipModel, fTrickPlayModel: $fTrickPlayModel, fImages: $fImages, fChapters: $fChapters, subtitles: $subtitles, userData: $userData)';
|
||||
}
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
return identical(this, other) ||
|
||||
(other.runtimeType == runtimeType &&
|
||||
other is _$SyncItemImpl &&
|
||||
(identical(other.id, id) || other.id == id) &&
|
||||
(identical(other.parentId, parentId) ||
|
||||
other.parentId == parentId) &&
|
||||
(identical(other.userId, userId) || other.userId == userId) &&
|
||||
(identical(other.path, path) || other.path == path) &&
|
||||
(identical(other.markedForDelete, markedForDelete) ||
|
||||
other.markedForDelete == markedForDelete) &&
|
||||
(identical(other.sortKey, sortKey) || other.sortKey == sortKey) &&
|
||||
(identical(other.fileSize, fileSize) ||
|
||||
other.fileSize == fileSize) &&
|
||||
(identical(other.videoFileName, videoFileName) ||
|
||||
other.videoFileName == videoFileName) &&
|
||||
(identical(other.introOutSkipModel, introOutSkipModel) ||
|
||||
other.introOutSkipModel == introOutSkipModel) &&
|
||||
(identical(other.fTrickPlayModel, fTrickPlayModel) ||
|
||||
other.fTrickPlayModel == fTrickPlayModel) &&
|
||||
(identical(other.fImages, fImages) || other.fImages == fImages) &&
|
||||
const DeepCollectionEquality()
|
||||
.equals(other._fChapters, _fChapters) &&
|
||||
const DeepCollectionEquality()
|
||||
.equals(other._subtitles, _subtitles) &&
|
||||
(identical(other.userData, userData) ||
|
||||
other.userData == userData));
|
||||
}
|
||||
|
||||
@JsonKey(ignore: true)
|
||||
@override
|
||||
int get hashCode => Object.hash(
|
||||
runtimeType,
|
||||
id,
|
||||
parentId,
|
||||
userId,
|
||||
path,
|
||||
markedForDelete,
|
||||
sortKey,
|
||||
fileSize,
|
||||
videoFileName,
|
||||
introOutSkipModel,
|
||||
fTrickPlayModel,
|
||||
fImages,
|
||||
const DeepCollectionEquality().hash(_fChapters),
|
||||
const DeepCollectionEquality().hash(_subtitles),
|
||||
userData);
|
||||
|
||||
@JsonKey(ignore: true)
|
||||
@override
|
||||
@pragma('vm:prefer-inline')
|
||||
_$$SyncItemImplCopyWith<_$SyncItemImpl> get copyWith =>
|
||||
__$$SyncItemImplCopyWithImpl<_$SyncItemImpl>(this, _$identity);
|
||||
|
||||
@override
|
||||
Map<String, dynamic> toJson() {
|
||||
return _$$SyncItemImplToJson(
|
||||
this,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
abstract class _SyncItem extends SyncedItem {
|
||||
factory _SyncItem(
|
||||
{required final String id,
|
||||
final String? parentId,
|
||||
required final String userId,
|
||||
final String? path,
|
||||
final bool markedForDelete,
|
||||
final int? sortKey,
|
||||
final int? fileSize,
|
||||
final String? videoFileName,
|
||||
final IntroOutSkipModel? introOutSkipModel,
|
||||
final TrickPlayModel? fTrickPlayModel,
|
||||
final ImagesData? fImages,
|
||||
final List<Chapter> fChapters,
|
||||
final List<SubStreamModel> subtitles,
|
||||
@UserDataJsonSerializer() final UserData? userData}) = _$SyncItemImpl;
|
||||
_SyncItem._() : super._();
|
||||
|
||||
factory _SyncItem.fromJson(Map<String, dynamic> json) =
|
||||
_$SyncItemImpl.fromJson;
|
||||
|
||||
@override
|
||||
String get id;
|
||||
@override
|
||||
String? get parentId;
|
||||
@override
|
||||
String get userId;
|
||||
@override
|
||||
String? get path;
|
||||
@override
|
||||
bool get markedForDelete;
|
||||
@override
|
||||
int? get sortKey;
|
||||
@override
|
||||
int? get fileSize;
|
||||
@override
|
||||
String? get videoFileName;
|
||||
@override
|
||||
IntroOutSkipModel? get introOutSkipModel;
|
||||
@override
|
||||
TrickPlayModel? get fTrickPlayModel;
|
||||
@override
|
||||
ImagesData? get fImages;
|
||||
@override
|
||||
List<Chapter> get fChapters;
|
||||
@override
|
||||
List<SubStreamModel> get subtitles;
|
||||
@override
|
||||
@UserDataJsonSerializer()
|
||||
UserData? get userData;
|
||||
@override
|
||||
@JsonKey(ignore: true)
|
||||
_$$SyncItemImplCopyWith<_$SyncItemImpl> get copyWith =>
|
||||
throw _privateConstructorUsedError;
|
||||
}
|
||||
71
lib/models/syncing/sync_item.g.dart
Normal file
71
lib/models/syncing/sync_item.g.dart
Normal file
|
|
@ -0,0 +1,71 @@
|
|||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
|
||||
part of 'sync_item.dart';
|
||||
|
||||
// **************************************************************************
|
||||
// JsonSerializableGenerator
|
||||
// **************************************************************************
|
||||
|
||||
_$SyncItemImpl _$$SyncItemImplFromJson(Map<String, dynamic> json) =>
|
||||
_$SyncItemImpl(
|
||||
id: json['id'] as String,
|
||||
parentId: json['parentId'] as String?,
|
||||
userId: json['userId'] as String,
|
||||
path: json['path'] as String?,
|
||||
markedForDelete: json['markedForDelete'] as bool? ?? false,
|
||||
sortKey: (json['sortKey'] as num?)?.toInt(),
|
||||
fileSize: (json['fileSize'] as num?)?.toInt(),
|
||||
videoFileName: json['videoFileName'] as String?,
|
||||
introOutSkipModel: json['introOutSkipModel'] == null
|
||||
? null
|
||||
: IntroOutSkipModel.fromJson(
|
||||
json['introOutSkipModel'] as Map<String, dynamic>),
|
||||
fTrickPlayModel: json['fTrickPlayModel'] == null
|
||||
? null
|
||||
: TrickPlayModel.fromJson(
|
||||
json['fTrickPlayModel'] as Map<String, dynamic>),
|
||||
fImages: json['fImages'] == null
|
||||
? null
|
||||
: ImagesData.fromJson(json['fImages'] as String),
|
||||
fChapters: (json['fChapters'] as List<dynamic>?)
|
||||
?.map((e) => Chapter.fromJson(e as String))
|
||||
.toList() ??
|
||||
const [],
|
||||
subtitles: (json['subtitles'] as List<dynamic>?)
|
||||
?.map((e) => SubStreamModel.fromJson(e as String))
|
||||
.toList() ??
|
||||
const [],
|
||||
userData: _$JsonConverterFromJson<String, UserData>(
|
||||
json['userData'], const UserDataJsonSerializer().fromJson),
|
||||
);
|
||||
|
||||
Map<String, dynamic> _$$SyncItemImplToJson(_$SyncItemImpl instance) =>
|
||||
<String, dynamic>{
|
||||
'id': instance.id,
|
||||
'parentId': instance.parentId,
|
||||
'userId': instance.userId,
|
||||
'path': instance.path,
|
||||
'markedForDelete': instance.markedForDelete,
|
||||
'sortKey': instance.sortKey,
|
||||
'fileSize': instance.fileSize,
|
||||
'videoFileName': instance.videoFileName,
|
||||
'introOutSkipModel': instance.introOutSkipModel,
|
||||
'fTrickPlayModel': instance.fTrickPlayModel,
|
||||
'fImages': instance.fImages,
|
||||
'fChapters': instance.fChapters,
|
||||
'subtitles': instance.subtitles,
|
||||
'userData': _$JsonConverterToJson<String, UserData>(
|
||||
instance.userData, const UserDataJsonSerializer().toJson),
|
||||
};
|
||||
|
||||
Value? _$JsonConverterFromJson<Json, Value>(
|
||||
Object? json,
|
||||
Value? Function(Json json) fromJson,
|
||||
) =>
|
||||
json == null ? null : fromJson(json as Json);
|
||||
|
||||
Json? _$JsonConverterToJson<Json, Value>(
|
||||
Value? value,
|
||||
Json? Function(Value value) toJson,
|
||||
) =>
|
||||
value == null ? null : toJson(value);
|
||||
16
lib/models/syncing/sync_settings_model.dart
Normal file
16
lib/models/syncing/sync_settings_model.dart
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
// ignore_for_file: invalid_annotation_target
|
||||
|
||||
import 'package:freezed_annotation/freezed_annotation.dart';
|
||||
|
||||
import 'package:fladder/models/syncing/sync_item.dart';
|
||||
|
||||
part 'sync_settings_model.freezed.dart';
|
||||
|
||||
@Freezed(toJson: false, fromJson: false)
|
||||
class SyncSettingsModel with _$SyncSettingsModel {
|
||||
const SyncSettingsModel._();
|
||||
|
||||
factory SyncSettingsModel({
|
||||
@Default([]) List<SyncedItem> items,
|
||||
}) = _SyncSettignsModel;
|
||||
}
|
||||
144
lib/models/syncing/sync_settings_model.freezed.dart
Normal file
144
lib/models/syncing/sync_settings_model.freezed.dart
Normal file
|
|
@ -0,0 +1,144 @@
|
|||
// 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 'sync_settings_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');
|
||||
|
||||
/// @nodoc
|
||||
mixin _$SyncSettingsModel {
|
||||
List<SyncedItem> get items => throw _privateConstructorUsedError;
|
||||
|
||||
@JsonKey(ignore: true)
|
||||
$SyncSettingsModelCopyWith<SyncSettingsModel> get copyWith =>
|
||||
throw _privateConstructorUsedError;
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
abstract class $SyncSettingsModelCopyWith<$Res> {
|
||||
factory $SyncSettingsModelCopyWith(
|
||||
SyncSettingsModel value, $Res Function(SyncSettingsModel) then) =
|
||||
_$SyncSettingsModelCopyWithImpl<$Res, SyncSettingsModel>;
|
||||
@useResult
|
||||
$Res call({List<SyncedItem> items});
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
class _$SyncSettingsModelCopyWithImpl<$Res, $Val extends SyncSettingsModel>
|
||||
implements $SyncSettingsModelCopyWith<$Res> {
|
||||
_$SyncSettingsModelCopyWithImpl(this._value, this._then);
|
||||
|
||||
// ignore: unused_field
|
||||
final $Val _value;
|
||||
// ignore: unused_field
|
||||
final $Res Function($Val) _then;
|
||||
|
||||
@pragma('vm:prefer-inline')
|
||||
@override
|
||||
$Res call({
|
||||
Object? items = null,
|
||||
}) {
|
||||
return _then(_value.copyWith(
|
||||
items: null == items
|
||||
? _value.items
|
||||
: items // ignore: cast_nullable_to_non_nullable
|
||||
as List<SyncedItem>,
|
||||
) as $Val);
|
||||
}
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
abstract class _$$SyncSettignsModelImplCopyWith<$Res>
|
||||
implements $SyncSettingsModelCopyWith<$Res> {
|
||||
factory _$$SyncSettignsModelImplCopyWith(_$SyncSettignsModelImpl value,
|
||||
$Res Function(_$SyncSettignsModelImpl) then) =
|
||||
__$$SyncSettignsModelImplCopyWithImpl<$Res>;
|
||||
@override
|
||||
@useResult
|
||||
$Res call({List<SyncedItem> items});
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
class __$$SyncSettignsModelImplCopyWithImpl<$Res>
|
||||
extends _$SyncSettingsModelCopyWithImpl<$Res, _$SyncSettignsModelImpl>
|
||||
implements _$$SyncSettignsModelImplCopyWith<$Res> {
|
||||
__$$SyncSettignsModelImplCopyWithImpl(_$SyncSettignsModelImpl _value,
|
||||
$Res Function(_$SyncSettignsModelImpl) _then)
|
||||
: super(_value, _then);
|
||||
|
||||
@pragma('vm:prefer-inline')
|
||||
@override
|
||||
$Res call({
|
||||
Object? items = null,
|
||||
}) {
|
||||
return _then(_$SyncSettignsModelImpl(
|
||||
items: null == items
|
||||
? _value._items
|
||||
: items // ignore: cast_nullable_to_non_nullable
|
||||
as List<SyncedItem>,
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
|
||||
class _$SyncSettignsModelImpl extends _SyncSettignsModel {
|
||||
_$SyncSettignsModelImpl({final List<SyncedItem> items = const []})
|
||||
: _items = items,
|
||||
super._();
|
||||
|
||||
final List<SyncedItem> _items;
|
||||
@override
|
||||
@JsonKey()
|
||||
List<SyncedItem> get items {
|
||||
if (_items is EqualUnmodifiableListView) return _items;
|
||||
// ignore: implicit_dynamic_type
|
||||
return EqualUnmodifiableListView(_items);
|
||||
}
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'SyncSettingsModel(items: $items)';
|
||||
}
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
return identical(this, other) ||
|
||||
(other.runtimeType == runtimeType &&
|
||||
other is _$SyncSettignsModelImpl &&
|
||||
const DeepCollectionEquality().equals(other._items, _items));
|
||||
}
|
||||
|
||||
@override
|
||||
int get hashCode =>
|
||||
Object.hash(runtimeType, const DeepCollectionEquality().hash(_items));
|
||||
|
||||
@JsonKey(ignore: true)
|
||||
@override
|
||||
@pragma('vm:prefer-inline')
|
||||
_$$SyncSettignsModelImplCopyWith<_$SyncSettignsModelImpl> get copyWith =>
|
||||
__$$SyncSettignsModelImplCopyWithImpl<_$SyncSettignsModelImpl>(
|
||||
this, _$identity);
|
||||
}
|
||||
|
||||
abstract class _SyncSettignsModel extends SyncSettingsModel {
|
||||
factory _SyncSettignsModel({final List<SyncedItem> items}) =
|
||||
_$SyncSettignsModelImpl;
|
||||
_SyncSettignsModel._() : super._();
|
||||
|
||||
@override
|
||||
List<SyncedItem> get items;
|
||||
@override
|
||||
@JsonKey(ignore: true)
|
||||
_$$SyncSettignsModelImplCopyWith<_$SyncSettignsModelImpl> get copyWith =>
|
||||
throw _privateConstructorUsedError;
|
||||
}
|
||||
177
lib/models/trickplayer_model.dart
Normal file
177
lib/models/trickplayer_model.dart
Normal file
|
|
@ -0,0 +1,177 @@
|
|||
// ignore_for_file: public_member_api_docs, sort_constructors_first
|
||||
import 'dart:convert';
|
||||
|
||||
import 'package:flutter/foundation.dart';
|
||||
|
||||
import 'package:fladder/models/items/chapters_model.dart';
|
||||
|
||||
class Bif {
|
||||
final String version;
|
||||
final List<int> widthResolutions;
|
||||
Bif({
|
||||
required this.version,
|
||||
required this.widthResolutions,
|
||||
});
|
||||
|
||||
Bif copyWith({
|
||||
String? version,
|
||||
List<int>? widthResolutions,
|
||||
}) {
|
||||
return Bif(
|
||||
version: version ?? this.version,
|
||||
widthResolutions: widthResolutions ?? this.widthResolutions,
|
||||
);
|
||||
}
|
||||
|
||||
Map<String, dynamic> toMap() {
|
||||
return <String, dynamic>{
|
||||
'Version': version,
|
||||
'WidthResolutions': widthResolutions,
|
||||
};
|
||||
}
|
||||
|
||||
factory Bif.fromMap(Map<String, dynamic> map) {
|
||||
return Bif(
|
||||
version: (map['Version'] ?? '') as String,
|
||||
widthResolutions: List<int>.from((map['WidthResolutions'] ?? const <int>[]) as List<dynamic>),
|
||||
);
|
||||
}
|
||||
|
||||
String toJson() => json.encode(toMap());
|
||||
|
||||
factory Bif.fromJson(String source) => Bif.fromMap(json.decode(source) as Map<String, dynamic>);
|
||||
|
||||
@override
|
||||
String toString() => 'Bif(version: $version, widthResolutions: $widthResolutions)';
|
||||
|
||||
@override
|
||||
bool operator ==(covariant Bif other) {
|
||||
if (identical(this, other)) return true;
|
||||
|
||||
return other.version == version && listEquals(other.widthResolutions, widthResolutions);
|
||||
}
|
||||
|
||||
@override
|
||||
int get hashCode => version.hashCode ^ widthResolutions.hashCode;
|
||||
}
|
||||
|
||||
class BifUtil {
|
||||
static List<int> bifMagicNumbers = [0x89, 0x42, 0x49, 0x46, 0x0D, 0x0A, 0x1A, 0x0A];
|
||||
static const int supportedBifVersion = 0;
|
||||
|
||||
static Future<BifData?> trickPlayDecode(Uint8List array, int width) async {
|
||||
Indexed8Array data = Indexed8Array(array);
|
||||
|
||||
for (int b in bifMagicNumbers) {
|
||||
if (data.read() != b) {
|
||||
print('Attempted to read invalid bif file.');
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
int bifVersion = data.readInt32();
|
||||
if (bifVersion != supportedBifVersion) {
|
||||
print('Client only supports BIF v$supportedBifVersion but file is v$bifVersion');
|
||||
return null;
|
||||
}
|
||||
|
||||
print('BIF version: $bifVersion');
|
||||
|
||||
int bifImgCount = data.readInt32();
|
||||
if (bifImgCount <= 0) {
|
||||
print('BIF file contains no images.');
|
||||
return null;
|
||||
}
|
||||
|
||||
print('BIF image count: $bifImgCount');
|
||||
|
||||
int timestampMultiplier = data.readInt32();
|
||||
if (timestampMultiplier == 0) timestampMultiplier = 1000;
|
||||
|
||||
data.addPosition(44); // Reserved
|
||||
|
||||
List<BifIndexEntry> bifIndex = [];
|
||||
for (int i = 0; i < bifImgCount; i++) {
|
||||
bifIndex.add(BifIndexEntry(data.readInt32(), data.readInt32()));
|
||||
}
|
||||
|
||||
List<Chapter> bifImages = [];
|
||||
for (int i = 0; i < bifIndex.length; i++) {
|
||||
BifIndexEntry indexEntry = bifIndex[i];
|
||||
int timestamp = indexEntry.timestamp;
|
||||
int offset = indexEntry.offset;
|
||||
int nextOffset = i + 1 < bifIndex.length ? bifIndex[i + 1].offset : data.limit();
|
||||
|
||||
Uint8List imageBytes = Uint8List.sublistView(Uint8List.view(data.array.buffer, offset, nextOffset - offset));
|
||||
|
||||
bifImages.add(
|
||||
Chapter(
|
||||
name: "",
|
||||
imageUrl: "",
|
||||
imageData: imageBytes,
|
||||
startPosition: Duration(milliseconds: timestamp * timestampMultiplier),
|
||||
),
|
||||
);
|
||||
}
|
||||
return BifData(bifVersion, bifImgCount, bifImages, width);
|
||||
}
|
||||
}
|
||||
|
||||
class Indexed8Array {
|
||||
Uint8List array;
|
||||
int readIndex;
|
||||
|
||||
Indexed8Array(Uint8List byteArray)
|
||||
: array = byteArray,
|
||||
readIndex = 0;
|
||||
|
||||
int read() {
|
||||
return array[readIndex++];
|
||||
}
|
||||
|
||||
void addPosition(int amount) {
|
||||
readIndex += amount;
|
||||
}
|
||||
|
||||
int readInt32() {
|
||||
int b1 = read() & 0xFF;
|
||||
int b2 = read() & 0xFF;
|
||||
int b3 = read() & 0xFF;
|
||||
int b4 = read() & 0xFF;
|
||||
|
||||
return b1 | (b2 << 8) | (b3 << 16) | (b4 << 24);
|
||||
}
|
||||
|
||||
Uint8List getByteArray() {
|
||||
return array;
|
||||
}
|
||||
|
||||
int limit() {
|
||||
return array.length;
|
||||
}
|
||||
|
||||
Endian order() {
|
||||
return Endian.big;
|
||||
}
|
||||
}
|
||||
|
||||
class BifIndexEntry {
|
||||
int timestamp;
|
||||
int offset;
|
||||
|
||||
BifIndexEntry(this.timestamp, this.offset);
|
||||
}
|
||||
|
||||
class BifData {
|
||||
int bifVersion;
|
||||
int bifImgCount;
|
||||
List<Chapter> chapters;
|
||||
int width;
|
||||
|
||||
BifData(
|
||||
this.bifVersion,
|
||||
this.bifImgCount,
|
||||
this.chapters,
|
||||
this.width,
|
||||
);
|
||||
}
|
||||
208
lib/models/video_stream_model.dart
Normal file
208
lib/models/video_stream_model.dart
Normal file
|
|
@ -0,0 +1,208 @@
|
|||
// 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/media_streams_model.dart';
|
||||
import 'package:fladder/providers/user_provider.dart';
|
||||
|
||||
enum PlaybackType {
|
||||
directStream,
|
||||
offline,
|
||||
transcode;
|
||||
|
||||
IconData get icon => switch (this) {
|
||||
PlaybackType.offline => IconsaxOutline.cloud,
|
||||
PlaybackType.directStream => IconsaxOutline.arrow_right_1,
|
||||
PlaybackType.transcode => IconsaxOutline.convert,
|
||||
};
|
||||
|
||||
String get name {
|
||||
switch (this) {
|
||||
case PlaybackType.directStream:
|
||||
return "Direct";
|
||||
case PlaybackType.offline:
|
||||
return "Offline";
|
||||
case PlaybackType.transcode:
|
||||
return "Transcoding";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class VideoPlayback {
|
||||
final List<ItemStreamModel> queue;
|
||||
final ItemStreamModel? currentItem;
|
||||
final SyncedItem? currentSyncedItem;
|
||||
final VideoStream? currentStream;
|
||||
VideoPlayback({
|
||||
required this.queue,
|
||||
this.currentItem,
|
||||
this.currentSyncedItem,
|
||||
this.currentStream,
|
||||
});
|
||||
|
||||
List<SubStreamModel> get subStreams => currentStream?.mediaStreamsModel?.subStreams ?? [];
|
||||
List<AudioStreamModel> get audioStreams => currentStream?.mediaStreamsModel?.audioStreams ?? [];
|
||||
|
||||
ItemStreamModel? get previousVideo {
|
||||
final int currentIndex = queue.indexWhere((element) => element.id == currentItem?.id);
|
||||
if (currentIndex > 0) {
|
||||
return queue[currentIndex - 1];
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
ItemStreamModel? get nextVideo {
|
||||
final int currentIndex = queue.indexWhere((element) => element.id == currentItem?.id);
|
||||
if ((currentIndex + 1) < queue.length) {
|
||||
return queue[currentIndex + 1];
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
VideoPlayback copyWith({
|
||||
List<ItemStreamModel>? queue,
|
||||
ItemStreamModel? currentItem,
|
||||
SyncedItem? currentSyncedItem,
|
||||
VideoStream? currentStream,
|
||||
Map<AudioStreamModel, AudioTrack>? audioMappings,
|
||||
Map<SubStreamModel, SubtitleTrack>? subMappings,
|
||||
}) {
|
||||
return VideoPlayback(
|
||||
queue: queue ?? this.queue,
|
||||
currentItem: currentItem ?? this.currentItem,
|
||||
currentSyncedItem: currentSyncedItem ?? this.currentSyncedItem,
|
||||
currentStream: currentStream ?? this.currentStream,
|
||||
);
|
||||
}
|
||||
|
||||
VideoPlayback clear() {
|
||||
return VideoPlayback(
|
||||
queue: queue,
|
||||
currentItem: null,
|
||||
currentStream: null,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class VideoStream {
|
||||
final String id;
|
||||
final Duration? currentPosition;
|
||||
final PlaybackType playbackType;
|
||||
final String playbackUrl;
|
||||
final String playSessionId;
|
||||
final List<Chapter>? chapters;
|
||||
final List<Chapter>? trickPlay;
|
||||
final IntroSkipModel? introSkipModel;
|
||||
final int? audioStreamIndex;
|
||||
final int? subtitleStreamIndex;
|
||||
final MediaStreamsModel? mediaStreamsModel;
|
||||
|
||||
AudioStreamModel? get currentAudioStream {
|
||||
if (audioStreamIndex == -1) {
|
||||
return null;
|
||||
}
|
||||
return mediaStreamsModel?.audioStreams.firstWhereOrNull(
|
||||
(element) => element.index == (audioStreamIndex ?? mediaStreamsModel?.currentAudioStream ?? 0));
|
||||
}
|
||||
|
||||
SubStreamModel? get currentSubStream {
|
||||
if (subtitleStreamIndex == -1) {
|
||||
return null;
|
||||
}
|
||||
return mediaStreamsModel?.subStreams.firstWhereOrNull(
|
||||
(element) => element.index == (subtitleStreamIndex ?? mediaStreamsModel?.currentSubStream ?? 0));
|
||||
}
|
||||
|
||||
VideoStream({
|
||||
required this.id,
|
||||
this.currentPosition,
|
||||
required this.playbackType,
|
||||
required this.playbackUrl,
|
||||
required this.playSessionId,
|
||||
this.chapters,
|
||||
this.trickPlay,
|
||||
this.introSkipModel,
|
||||
this.audioStreamIndex,
|
||||
this.subtitleStreamIndex,
|
||||
this.mediaStreamsModel,
|
||||
});
|
||||
|
||||
VideoStream copyWith({
|
||||
String? id,
|
||||
Duration? currentPosition,
|
||||
PlaybackType? playbackType,
|
||||
String? playbackUrl,
|
||||
String? playSessionId,
|
||||
List<Chapter>? chapters,
|
||||
List<Chapter>? trickPlay,
|
||||
IntroSkipModel? introSkipModel,
|
||||
int? audioStreamIndex,
|
||||
int? subtitleStreamIndex,
|
||||
MediaStreamsModel? mediaStreamsModel,
|
||||
}) {
|
||||
return VideoStream(
|
||||
id: id ?? this.id,
|
||||
currentPosition: currentPosition ?? this.currentPosition,
|
||||
playbackType: playbackType ?? this.playbackType,
|
||||
playbackUrl: playbackUrl ?? this.playbackUrl,
|
||||
playSessionId: playSessionId ?? this.playSessionId,
|
||||
chapters: chapters ?? this.chapters,
|
||||
trickPlay: trickPlay ?? this.trickPlay,
|
||||
introSkipModel: introSkipModel ?? this.introSkipModel,
|
||||
audioStreamIndex: audioStreamIndex ?? this.audioStreamIndex,
|
||||
subtitleStreamIndex: subtitleStreamIndex ?? this.subtitleStreamIndex,
|
||||
mediaStreamsModel: mediaStreamsModel ?? this.mediaStreamsModel,
|
||||
);
|
||||
}
|
||||
|
||||
static VideoStream? fromPlayBackInfo(PlaybackInfoResponse info, Ref ref) {
|
||||
final mediaSource = info.mediaSources?.first;
|
||||
var playType = PlaybackType.directStream;
|
||||
|
||||
String? playbackUrl;
|
||||
|
||||
if (mediaSource == null) return null;
|
||||
|
||||
if (mediaSource.supportsDirectStream ?? false) {
|
||||
final Map<String, String?> directOptions = {
|
||||
'Static': 'true',
|
||||
'mediaSourceId': mediaSource.id,
|
||||
'api_key': ref.read(userProvider)?.credentials.token,
|
||||
};
|
||||
|
||||
if (mediaSource.eTag != null) {
|
||||
directOptions['Tag'] = mediaSource.eTag;
|
||||
}
|
||||
|
||||
if (mediaSource.liveStreamId != null) {
|
||||
directOptions['LiveStreamId'] = mediaSource.liveStreamId;
|
||||
}
|
||||
|
||||
final params = Uri(queryParameters: directOptions).query;
|
||||
|
||||
playbackUrl = '${ref.read(userProvider)?.server ?? ""}/Videos/${mediaSource.id}/stream?$params';
|
||||
} else if ((mediaSource.supportsTranscoding ?? false) && mediaSource.transcodingUrl != null) {
|
||||
playbackUrl = "${ref.read(userProvider)?.server ?? ""}${mediaSource.transcodingUrl ?? ""}";
|
||||
playType = PlaybackType.transcode;
|
||||
}
|
||||
|
||||
if (playbackUrl == null) return null;
|
||||
|
||||
return VideoStream(
|
||||
id: info.mediaSources?.first.id ?? "",
|
||||
playbackUrl: playbackUrl,
|
||||
playbackType: playType,
|
||||
playSessionId: info.playSessionId ?? "",
|
||||
mediaStreamsModel: MediaStreamsModel.fromMediaStreamsList(
|
||||
info.mediaSources?.firstOrNull, info.mediaSources?.firstOrNull?.mediaStreams ?? [], ref),
|
||||
);
|
||||
}
|
||||
}
|
||||
97
lib/models/view_model.dart
Normal file
97
lib/models/view_model.dart
Normal file
|
|
@ -0,0 +1,97 @@
|
|||
// ignore_for_file: public_member_api_docs, sort_constructors_first
|
||||
|
||||
import 'package:collection/collection.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
|
||||
import 'package:fladder/jellyfin/jellyfin_open_api.enums.swagger.dart';
|
||||
import 'package:fladder/jellyfin/jellyfin_open_api.swagger.dart' as dto;
|
||||
import 'package:fladder/models/item_base_model.dart';
|
||||
|
||||
class ViewModel {
|
||||
final String name;
|
||||
final String id;
|
||||
final String serverId;
|
||||
final DateTime dateCreated;
|
||||
final bool canDelete;
|
||||
final bool canDownload;
|
||||
final String parentId;
|
||||
final CollectionType collectionType;
|
||||
final dto.PlayAccess playAccess;
|
||||
final List<ItemBaseModel> recentlyAdded;
|
||||
final int childCount;
|
||||
ViewModel({
|
||||
required this.name,
|
||||
required this.id,
|
||||
required this.serverId,
|
||||
required this.dateCreated,
|
||||
required this.canDelete,
|
||||
required this.canDownload,
|
||||
required this.parentId,
|
||||
required this.collectionType,
|
||||
required this.playAccess,
|
||||
required this.recentlyAdded,
|
||||
required this.childCount,
|
||||
});
|
||||
|
||||
ViewModel copyWith({
|
||||
String? name,
|
||||
String? id,
|
||||
String? serverId,
|
||||
DateTime? dateCreated,
|
||||
bool? canDelete,
|
||||
bool? canDownload,
|
||||
String? parentId,
|
||||
CollectionType? collectionType,
|
||||
dto.PlayAccess? playAccess,
|
||||
List<ItemBaseModel>? recentlyAdded,
|
||||
int? childCount,
|
||||
}) {
|
||||
return ViewModel(
|
||||
name: name ?? this.name,
|
||||
id: id ?? this.id,
|
||||
serverId: serverId ?? this.serverId,
|
||||
dateCreated: dateCreated ?? this.dateCreated,
|
||||
canDelete: canDelete ?? this.canDelete,
|
||||
canDownload: canDownload ?? this.canDownload,
|
||||
parentId: parentId ?? this.parentId,
|
||||
collectionType: collectionType ?? this.collectionType,
|
||||
playAccess: playAccess ?? this.playAccess,
|
||||
recentlyAdded: recentlyAdded ?? this.recentlyAdded,
|
||||
childCount: childCount ?? this.childCount,
|
||||
);
|
||||
}
|
||||
|
||||
factory ViewModel.fromBodyDto(dto.BaseItemDto item, Ref ref) {
|
||||
return ViewModel(
|
||||
name: item.name ?? "",
|
||||
id: item.id ?? "",
|
||||
serverId: item.serverId ?? "",
|
||||
dateCreated: item.dateCreated ?? DateTime.now(),
|
||||
canDelete: item.canDelete ?? false,
|
||||
canDownload: item.canDownload ?? false,
|
||||
parentId: item.parentId ?? "",
|
||||
recentlyAdded: [],
|
||||
collectionType: CollectionType.values
|
||||
.firstWhereOrNull((element) => element.name.toLowerCase() == item.collectionType?.value?.toLowerCase()) ??
|
||||
CollectionType.movies,
|
||||
playAccess: item.playAccess ?? PlayAccess.none,
|
||||
childCount: item.childCount ?? 0,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
bool operator ==(covariant ViewModel other) {
|
||||
if (identical(this, other)) return true;
|
||||
return other.id == id && other.serverId == serverId;
|
||||
}
|
||||
|
||||
@override
|
||||
int get hashCode {
|
||||
return id.hashCode ^ serverId.hashCode;
|
||||
}
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'ViewModel(name: $name, id: $id, serverId: $serverId, dateCreated: $dateCreated, canDelete: $canDelete, canDownload: $canDownload, parentId: $parentId, collectionType: $collectionType, playAccess: $playAccess, recentlyAdded: $recentlyAdded, childCount: $childCount)';
|
||||
}
|
||||
}
|
||||
24
lib/models/views_model.dart
Normal file
24
lib/models/views_model.dart
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
// ignore_for_file: public_member_api_docs, sort_constructors_first
|
||||
import 'package:fladder/models/view_model.dart';
|
||||
|
||||
class ViewsModel {
|
||||
final bool loading;
|
||||
final List<ViewModel> views;
|
||||
final List<ViewModel> dashboardViews;
|
||||
ViewsModel({
|
||||
this.loading = false,
|
||||
this.views = const [],
|
||||
this.dashboardViews = const [],
|
||||
});
|
||||
|
||||
ViewsModel copyWith({
|
||||
bool? loading,
|
||||
List<ViewModel>? views,
|
||||
List<ViewModel>? dashboardViews,
|
||||
}) {
|
||||
return ViewsModel(
|
||||
loading: loading ?? this.loading,
|
||||
views: views ?? this.views,
|
||||
dashboardViews: dashboardViews ?? this.dashboardViews);
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue