feat: Android TV support (#503)

Co-authored-by: PartyDonut <PartyDonut@users.noreply.github.com>
This commit is contained in:
PartyDonut 2025-09-28 21:07:49 +02:00 committed by GitHub
parent 7ab8c015b9
commit c299492d6d
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
168 changed files with 12019 additions and 3073 deletions

View file

@ -164,7 +164,7 @@ class ItemBaseModel with ItemBaseModelMappable {
}
}
Future<void> navigateTo(BuildContext context, {WidgetRef? ref}) async {
Future<void> navigateTo(BuildContext context, {WidgetRef? ref, Object? tag}) async {
switch (this) {
case FolderModel _:
case BoxSetModel _:
@ -191,7 +191,7 @@ class ItemBaseModel with ItemBaseModelMappable {
case SeasonModel _:
case PersonModel _:
default:
context.router.push(DetailsRoute(id: id, item: this));
context.router.push(DetailsRoute(id: id, item: this, tag: tag));
break;
}
}

View file

@ -181,7 +181,7 @@ class EpisodeModel extends ItemStreamModel with EpisodeModelMappable {
playlistId: item.playlistItemId,
dateAired: item.premiereDate,
chapters: Chapter.chaptersFromInfo(item.id ?? "", item.chapters ?? [], ref),
images: ImagesData.fromBaseItem(item, ref, getOriginalSize: true),
images: ImagesData.fromBaseItem(item, ref),
primaryRatio: item.primaryImageAspectRatio,
season: item.parentIndexNumber ?? 0,
episode: item.indexNumber ?? 0,

View file

@ -38,10 +38,9 @@ class ImagesData {
dto.BaseItemDto item,
Ref ref, {
Size backDrop = const Size(2000, 2000),
Size logo = const Size(1000, 1000),
Size logo = const Size(500, 500),
Size primary = const Size(600, 600),
bool getOriginalSize = false,
int quality = 95,
}) {
final itemid = item.id;
if (itemid == null) return null;
@ -59,7 +58,6 @@ class ImagesData {
type: enums.ImageType.primary,
maxHeight: primary.height.toInt(),
maxWidth: primary.width.toInt(),
quality: quality,
),
key: "${itemid}_primary_${item.imageTags?['Primary']}",
hash: item.imageBlurHashes?.primary?[item.imageTags?['Primary']] ?? "",
@ -77,7 +75,6 @@ class ImagesData {
type: enums.ImageType.logo,
maxHeight: logo.height.toInt(),
maxWidth: logo.width.toInt(),
quality: quality,
),
key: "${itemid}_logo_${item.imageTags?['Logo']}",
hash: item.imageBlurHashes?.logo?[item.imageTags?['Logo']] ?? "")
@ -98,7 +95,6 @@ class ImagesData {
backdrop,
maxHeight: backDrop.height.toInt(),
maxWidth: backDrop.width.toInt(),
quality: quality,
),
key: "${itemid}_backdrop_${index}_$backdrop",
hash: item.imageBlurHashes?.backdrop?[backdrop] ?? "",
@ -116,9 +112,8 @@ class ImagesData {
dto.BaseItemDto item,
Ref ref, {
Size backDrop = const Size(2000, 2000),
Size logo = const Size(1000, 1000),
Size logo = const Size(500, 500),
Size primary = const Size(600, 600),
int quality = 95,
}) {
if (item.seriesId == null && item.parentId == null) return null;
@ -132,7 +127,6 @@ class ImagesData {
type: enums.ImageType.primary,
maxHeight: primary.height.toInt(),
maxWidth: primary.width.toInt(),
quality: quality,
),
key: "${item.seriesId}_primary_${item.seriesPrimaryImageTag ?? ""}",
hash: item.imageBlurHashes?.primary?[item.seriesPrimaryImageTag] ?? "")
@ -144,7 +138,6 @@ class ImagesData {
type: enums.ImageType.logo,
maxHeight: logo.height.toInt(),
maxWidth: logo.width.toInt(),
quality: quality,
),
key: "${item.seriesId}_logo_${item.parentLogoImageTag ?? ""}",
hash: item.imageBlurHashes?.logo?[item.parentLogoImageTag] ?? "")
@ -161,7 +154,6 @@ class ImagesData {
backdrop,
maxHeight: backDrop.height.toInt(),
maxWidth: backDrop.width.toInt(),
quality: quality,
),
key: "${itemId}_backdrop_${index}_$backdrop",
hash: item.imageBlurHashes?.backdrop?[backdrop] ?? "",
@ -180,8 +172,7 @@ class ImagesData {
Ref ref, {
Size backDrop = const Size(2000, 2000),
Size logo = const Size(1000, 1000),
Size primary = const Size(2000, 2000),
int quality = 95,
Size primary = const Size(500, 500),
}) {
return ImagesData(
primary: (item.primaryImageTag != null && item.imageBlurHashes != null)
@ -191,7 +182,6 @@ class ImagesData {
type: enums.ImageType.primary,
maxHeight: primary.height.toInt(),
maxWidth: primary.width.toInt(),
quality: quality,
),
key: "${item.id ?? ""}_primary_${item.primaryImageTag ?? ''}",
hash: item.imageBlurHashes?.primary?[item.primaryImageTag] ?? '')

View file

@ -74,6 +74,11 @@ class MovieModel extends ItemStreamModel with MovieModelMappable {
@override
MediaStreamsModel? get streamModel => mediaStreams;
@override
String? label(BuildContext context) {
return name;
}
@override
bool get syncAble => true;

View file

@ -1,15 +1,14 @@
import 'package:fladder/models/items/images_models.dart';
import 'package:fladder/models/items/overview_model.dart';
import 'package:dart_mappable/dart_mappable.dart';
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/movie_model.dart';
import 'package:fladder/models/items/overview_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()

View file

@ -85,7 +85,7 @@ class SeriesModel extends ItemBaseModel with SeriesModelMappable {
userData: UserData.fromDto(item.userData),
parentId: item.parentId,
playlistId: item.playlistItemId,
images: ImagesData.fromBaseItem(item, ref, getOriginalSize: true),
images: ImagesData.fromBaseItem(item, ref),
primaryRatio: item.primaryImageAspectRatio,
originalTitle: item.originalTitle ?? "",
sortName: item.sortName ?? "",

View file

@ -1,26 +1,34 @@
// ignore_for_file: public_member_api_docs, sort_constructors_first
import 'package:freezed_annotation/freezed_annotation.dart';
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,
});
part 'login_screen_model.freezed.dart';
LoginScreenModel copyWith({
List<AccountModel>? accounts,
CredentialsModel? tempCredentials,
bool? loading,
}) {
return LoginScreenModel(
accounts: accounts ?? this.accounts,
tempCredentials: tempCredentials ?? this.tempCredentials,
loading: loading ?? this.loading,
);
}
enum LoginScreenType {
users,
login,
code,
}
@Freezed(copyWith: true)
abstract class LoginScreenModel with _$LoginScreenModel {
factory LoginScreenModel({
@Default([]) List<AccountModel> accounts,
@Default(LoginScreenType.users) LoginScreenType screen,
ServerLoginModel? serverLoginModel,
String? errorMessage,
@Default(false) bool hasBaseUrl,
@Default(false) bool loading,
}) = _LoginScreenModel;
}
@Freezed(copyWith: true)
abstract class ServerLoginModel with _$ServerLoginModel {
factory ServerLoginModel({
required CredentialsModel tempCredentials,
@Default([]) List<AccountModel> accounts,
String? serverMessage,
@Default(false) bool hasQuickConnect,
}) = _ServerLoginModel;
}

View file

@ -0,0 +1,774 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
// coverage:ignore-file
// 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 'login_screen_model.dart';
// **************************************************************************
// FreezedGenerator
// **************************************************************************
// dart format off
T _$identity<T>(T value) => value;
/// @nodoc
mixin _$LoginScreenModel {
List<AccountModel> get accounts;
LoginScreenType get screen;
ServerLoginModel? get serverLoginModel;
String? get errorMessage;
bool get hasBaseUrl;
bool get loading;
/// Create a copy of LoginScreenModel
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
@pragma('vm:prefer-inline')
$LoginScreenModelCopyWith<LoginScreenModel> get copyWith =>
_$LoginScreenModelCopyWithImpl<LoginScreenModel>(
this as LoginScreenModel, _$identity);
@override
String toString() {
return 'LoginScreenModel(accounts: $accounts, screen: $screen, serverLoginModel: $serverLoginModel, errorMessage: $errorMessage, hasBaseUrl: $hasBaseUrl, loading: $loading)';
}
}
/// @nodoc
abstract mixin class $LoginScreenModelCopyWith<$Res> {
factory $LoginScreenModelCopyWith(
LoginScreenModel value, $Res Function(LoginScreenModel) _then) =
_$LoginScreenModelCopyWithImpl;
@useResult
$Res call(
{List<AccountModel> accounts,
LoginScreenType screen,
ServerLoginModel? serverLoginModel,
String? errorMessage,
bool hasBaseUrl,
bool loading});
$ServerLoginModelCopyWith<$Res>? get serverLoginModel;
}
/// @nodoc
class _$LoginScreenModelCopyWithImpl<$Res>
implements $LoginScreenModelCopyWith<$Res> {
_$LoginScreenModelCopyWithImpl(this._self, this._then);
final LoginScreenModel _self;
final $Res Function(LoginScreenModel) _then;
/// Create a copy of LoginScreenModel
/// with the given fields replaced by the non-null parameter values.
@pragma('vm:prefer-inline')
@override
$Res call({
Object? accounts = null,
Object? screen = null,
Object? serverLoginModel = freezed,
Object? errorMessage = freezed,
Object? hasBaseUrl = null,
Object? loading = null,
}) {
return _then(_self.copyWith(
accounts: null == accounts
? _self.accounts
: accounts // ignore: cast_nullable_to_non_nullable
as List<AccountModel>,
screen: null == screen
? _self.screen
: screen // ignore: cast_nullable_to_non_nullable
as LoginScreenType,
serverLoginModel: freezed == serverLoginModel
? _self.serverLoginModel
: serverLoginModel // ignore: cast_nullable_to_non_nullable
as ServerLoginModel?,
errorMessage: freezed == errorMessage
? _self.errorMessage
: errorMessage // ignore: cast_nullable_to_non_nullable
as String?,
hasBaseUrl: null == hasBaseUrl
? _self.hasBaseUrl
: hasBaseUrl // ignore: cast_nullable_to_non_nullable
as bool,
loading: null == loading
? _self.loading
: loading // ignore: cast_nullable_to_non_nullable
as bool,
));
}
/// Create a copy of LoginScreenModel
/// with the given fields replaced by the non-null parameter values.
@override
@pragma('vm:prefer-inline')
$ServerLoginModelCopyWith<$Res>? get serverLoginModel {
if (_self.serverLoginModel == null) {
return null;
}
return $ServerLoginModelCopyWith<$Res>(_self.serverLoginModel!, (value) {
return _then(_self.copyWith(serverLoginModel: value));
});
}
}
/// Adds pattern-matching-related methods to [LoginScreenModel].
extension LoginScreenModelPatterns on LoginScreenModel {
/// A variant of `map` that fallback to returning `orElse`.
///
/// It is equivalent to doing:
/// ```dart
/// switch (sealedClass) {
/// case final Subclass value:
/// return ...;
/// case _:
/// return orElse();
/// }
/// ```
@optionalTypeArgs
TResult maybeMap<TResult extends Object?>(
TResult Function(_LoginScreenModel value)? $default, {
required TResult orElse(),
}) {
final _that = this;
switch (_that) {
case _LoginScreenModel() when $default != null:
return $default(_that);
case _:
return orElse();
}
}
/// A `switch`-like method, using callbacks.
///
/// Callbacks receives the raw object, upcasted.
/// It is equivalent to doing:
/// ```dart
/// switch (sealedClass) {
/// case final Subclass value:
/// return ...;
/// case final Subclass2 value:
/// return ...;
/// }
/// ```
@optionalTypeArgs
TResult map<TResult extends Object?>(
TResult Function(_LoginScreenModel value) $default,
) {
final _that = this;
switch (_that) {
case _LoginScreenModel():
return $default(_that);
case _:
throw StateError('Unexpected subclass');
}
}
/// A variant of `map` that fallback to returning `null`.
///
/// It is equivalent to doing:
/// ```dart
/// switch (sealedClass) {
/// case final Subclass value:
/// return ...;
/// case _:
/// return null;
/// }
/// ```
@optionalTypeArgs
TResult? mapOrNull<TResult extends Object?>(
TResult? Function(_LoginScreenModel value)? $default,
) {
final _that = this;
switch (_that) {
case _LoginScreenModel() when $default != null:
return $default(_that);
case _:
return null;
}
}
/// A variant of `when` that fallback to an `orElse` callback.
///
/// It is equivalent to doing:
/// ```dart
/// switch (sealedClass) {
/// case Subclass(:final field):
/// return ...;
/// case _:
/// return orElse();
/// }
/// ```
@optionalTypeArgs
TResult maybeWhen<TResult extends Object?>(
TResult Function(
List<AccountModel> accounts,
LoginScreenType screen,
ServerLoginModel? serverLoginModel,
String? errorMessage,
bool hasBaseUrl,
bool loading)?
$default, {
required TResult orElse(),
}) {
final _that = this;
switch (_that) {
case _LoginScreenModel() when $default != null:
return $default(_that.accounts, _that.screen, _that.serverLoginModel,
_that.errorMessage, _that.hasBaseUrl, _that.loading);
case _:
return orElse();
}
}
/// A `switch`-like method, using callbacks.
///
/// As opposed to `map`, this offers destructuring.
/// It is equivalent to doing:
/// ```dart
/// switch (sealedClass) {
/// case Subclass(:final field):
/// return ...;
/// case Subclass2(:final field2):
/// return ...;
/// }
/// ```
@optionalTypeArgs
TResult when<TResult extends Object?>(
TResult Function(
List<AccountModel> accounts,
LoginScreenType screen,
ServerLoginModel? serverLoginModel,
String? errorMessage,
bool hasBaseUrl,
bool loading)
$default,
) {
final _that = this;
switch (_that) {
case _LoginScreenModel():
return $default(_that.accounts, _that.screen, _that.serverLoginModel,
_that.errorMessage, _that.hasBaseUrl, _that.loading);
case _:
throw StateError('Unexpected subclass');
}
}
/// A variant of `when` that fallback to returning `null`
///
/// It is equivalent to doing:
/// ```dart
/// switch (sealedClass) {
/// case Subclass(:final field):
/// return ...;
/// case _:
/// return null;
/// }
/// ```
@optionalTypeArgs
TResult? whenOrNull<TResult extends Object?>(
TResult? Function(
List<AccountModel> accounts,
LoginScreenType screen,
ServerLoginModel? serverLoginModel,
String? errorMessage,
bool hasBaseUrl,
bool loading)?
$default,
) {
final _that = this;
switch (_that) {
case _LoginScreenModel() when $default != null:
return $default(_that.accounts, _that.screen, _that.serverLoginModel,
_that.errorMessage, _that.hasBaseUrl, _that.loading);
case _:
return null;
}
}
}
/// @nodoc
class _LoginScreenModel implements LoginScreenModel {
_LoginScreenModel(
{final List<AccountModel> accounts = const [],
this.screen = LoginScreenType.users,
this.serverLoginModel,
this.errorMessage,
this.hasBaseUrl = false,
this.loading = false})
: _accounts = accounts;
final List<AccountModel> _accounts;
@override
@JsonKey()
List<AccountModel> get accounts {
if (_accounts is EqualUnmodifiableListView) return _accounts;
// ignore: implicit_dynamic_type
return EqualUnmodifiableListView(_accounts);
}
@override
@JsonKey()
final LoginScreenType screen;
@override
final ServerLoginModel? serverLoginModel;
@override
final String? errorMessage;
@override
@JsonKey()
final bool hasBaseUrl;
@override
@JsonKey()
final bool loading;
/// Create a copy of LoginScreenModel
/// with the given fields replaced by the non-null parameter values.
@override
@JsonKey(includeFromJson: false, includeToJson: false)
@pragma('vm:prefer-inline')
_$LoginScreenModelCopyWith<_LoginScreenModel> get copyWith =>
__$LoginScreenModelCopyWithImpl<_LoginScreenModel>(this, _$identity);
@override
String toString() {
return 'LoginScreenModel(accounts: $accounts, screen: $screen, serverLoginModel: $serverLoginModel, errorMessage: $errorMessage, hasBaseUrl: $hasBaseUrl, loading: $loading)';
}
}
/// @nodoc
abstract mixin class _$LoginScreenModelCopyWith<$Res>
implements $LoginScreenModelCopyWith<$Res> {
factory _$LoginScreenModelCopyWith(
_LoginScreenModel value, $Res Function(_LoginScreenModel) _then) =
__$LoginScreenModelCopyWithImpl;
@override
@useResult
$Res call(
{List<AccountModel> accounts,
LoginScreenType screen,
ServerLoginModel? serverLoginModel,
String? errorMessage,
bool hasBaseUrl,
bool loading});
@override
$ServerLoginModelCopyWith<$Res>? get serverLoginModel;
}
/// @nodoc
class __$LoginScreenModelCopyWithImpl<$Res>
implements _$LoginScreenModelCopyWith<$Res> {
__$LoginScreenModelCopyWithImpl(this._self, this._then);
final _LoginScreenModel _self;
final $Res Function(_LoginScreenModel) _then;
/// Create a copy of LoginScreenModel
/// with the given fields replaced by the non-null parameter values.
@override
@pragma('vm:prefer-inline')
$Res call({
Object? accounts = null,
Object? screen = null,
Object? serverLoginModel = freezed,
Object? errorMessage = freezed,
Object? hasBaseUrl = null,
Object? loading = null,
}) {
return _then(_LoginScreenModel(
accounts: null == accounts
? _self._accounts
: accounts // ignore: cast_nullable_to_non_nullable
as List<AccountModel>,
screen: null == screen
? _self.screen
: screen // ignore: cast_nullable_to_non_nullable
as LoginScreenType,
serverLoginModel: freezed == serverLoginModel
? _self.serverLoginModel
: serverLoginModel // ignore: cast_nullable_to_non_nullable
as ServerLoginModel?,
errorMessage: freezed == errorMessage
? _self.errorMessage
: errorMessage // ignore: cast_nullable_to_non_nullable
as String?,
hasBaseUrl: null == hasBaseUrl
? _self.hasBaseUrl
: hasBaseUrl // ignore: cast_nullable_to_non_nullable
as bool,
loading: null == loading
? _self.loading
: loading // ignore: cast_nullable_to_non_nullable
as bool,
));
}
/// Create a copy of LoginScreenModel
/// with the given fields replaced by the non-null parameter values.
@override
@pragma('vm:prefer-inline')
$ServerLoginModelCopyWith<$Res>? get serverLoginModel {
if (_self.serverLoginModel == null) {
return null;
}
return $ServerLoginModelCopyWith<$Res>(_self.serverLoginModel!, (value) {
return _then(_self.copyWith(serverLoginModel: value));
});
}
}
/// @nodoc
mixin _$ServerLoginModel {
CredentialsModel get tempCredentials;
List<AccountModel> get accounts;
String? get serverMessage;
bool get hasQuickConnect;
/// Create a copy of ServerLoginModel
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
@pragma('vm:prefer-inline')
$ServerLoginModelCopyWith<ServerLoginModel> get copyWith =>
_$ServerLoginModelCopyWithImpl<ServerLoginModel>(
this as ServerLoginModel, _$identity);
@override
String toString() {
return 'ServerLoginModel(tempCredentials: $tempCredentials, accounts: $accounts, serverMessage: $serverMessage, hasQuickConnect: $hasQuickConnect)';
}
}
/// @nodoc
abstract mixin class $ServerLoginModelCopyWith<$Res> {
factory $ServerLoginModelCopyWith(
ServerLoginModel value, $Res Function(ServerLoginModel) _then) =
_$ServerLoginModelCopyWithImpl;
@useResult
$Res call(
{CredentialsModel tempCredentials,
List<AccountModel> accounts,
String? serverMessage,
bool hasQuickConnect});
}
/// @nodoc
class _$ServerLoginModelCopyWithImpl<$Res>
implements $ServerLoginModelCopyWith<$Res> {
_$ServerLoginModelCopyWithImpl(this._self, this._then);
final ServerLoginModel _self;
final $Res Function(ServerLoginModel) _then;
/// Create a copy of ServerLoginModel
/// with the given fields replaced by the non-null parameter values.
@pragma('vm:prefer-inline')
@override
$Res call({
Object? tempCredentials = null,
Object? accounts = null,
Object? serverMessage = freezed,
Object? hasQuickConnect = null,
}) {
return _then(_self.copyWith(
tempCredentials: null == tempCredentials
? _self.tempCredentials
: tempCredentials // ignore: cast_nullable_to_non_nullable
as CredentialsModel,
accounts: null == accounts
? _self.accounts
: accounts // ignore: cast_nullable_to_non_nullable
as List<AccountModel>,
serverMessage: freezed == serverMessage
? _self.serverMessage
: serverMessage // ignore: cast_nullable_to_non_nullable
as String?,
hasQuickConnect: null == hasQuickConnect
? _self.hasQuickConnect
: hasQuickConnect // ignore: cast_nullable_to_non_nullable
as bool,
));
}
}
/// Adds pattern-matching-related methods to [ServerLoginModel].
extension ServerLoginModelPatterns on ServerLoginModel {
/// A variant of `map` that fallback to returning `orElse`.
///
/// It is equivalent to doing:
/// ```dart
/// switch (sealedClass) {
/// case final Subclass value:
/// return ...;
/// case _:
/// return orElse();
/// }
/// ```
@optionalTypeArgs
TResult maybeMap<TResult extends Object?>(
TResult Function(_ServerLoginModel value)? $default, {
required TResult orElse(),
}) {
final _that = this;
switch (_that) {
case _ServerLoginModel() when $default != null:
return $default(_that);
case _:
return orElse();
}
}
/// A `switch`-like method, using callbacks.
///
/// Callbacks receives the raw object, upcasted.
/// It is equivalent to doing:
/// ```dart
/// switch (sealedClass) {
/// case final Subclass value:
/// return ...;
/// case final Subclass2 value:
/// return ...;
/// }
/// ```
@optionalTypeArgs
TResult map<TResult extends Object?>(
TResult Function(_ServerLoginModel value) $default,
) {
final _that = this;
switch (_that) {
case _ServerLoginModel():
return $default(_that);
case _:
throw StateError('Unexpected subclass');
}
}
/// A variant of `map` that fallback to returning `null`.
///
/// It is equivalent to doing:
/// ```dart
/// switch (sealedClass) {
/// case final Subclass value:
/// return ...;
/// case _:
/// return null;
/// }
/// ```
@optionalTypeArgs
TResult? mapOrNull<TResult extends Object?>(
TResult? Function(_ServerLoginModel value)? $default,
) {
final _that = this;
switch (_that) {
case _ServerLoginModel() when $default != null:
return $default(_that);
case _:
return null;
}
}
/// A variant of `when` that fallback to an `orElse` callback.
///
/// It is equivalent to doing:
/// ```dart
/// switch (sealedClass) {
/// case Subclass(:final field):
/// return ...;
/// case _:
/// return orElse();
/// }
/// ```
@optionalTypeArgs
TResult maybeWhen<TResult extends Object?>(
TResult Function(
CredentialsModel tempCredentials,
List<AccountModel> accounts,
String? serverMessage,
bool hasQuickConnect)?
$default, {
required TResult orElse(),
}) {
final _that = this;
switch (_that) {
case _ServerLoginModel() when $default != null:
return $default(_that.tempCredentials, _that.accounts,
_that.serverMessage, _that.hasQuickConnect);
case _:
return orElse();
}
}
/// A `switch`-like method, using callbacks.
///
/// As opposed to `map`, this offers destructuring.
/// It is equivalent to doing:
/// ```dart
/// switch (sealedClass) {
/// case Subclass(:final field):
/// return ...;
/// case Subclass2(:final field2):
/// return ...;
/// }
/// ```
@optionalTypeArgs
TResult when<TResult extends Object?>(
TResult Function(
CredentialsModel tempCredentials,
List<AccountModel> accounts,
String? serverMessage,
bool hasQuickConnect)
$default,
) {
final _that = this;
switch (_that) {
case _ServerLoginModel():
return $default(_that.tempCredentials, _that.accounts,
_that.serverMessage, _that.hasQuickConnect);
case _:
throw StateError('Unexpected subclass');
}
}
/// A variant of `when` that fallback to returning `null`
///
/// It is equivalent to doing:
/// ```dart
/// switch (sealedClass) {
/// case Subclass(:final field):
/// return ...;
/// case _:
/// return null;
/// }
/// ```
@optionalTypeArgs
TResult? whenOrNull<TResult extends Object?>(
TResult? Function(
CredentialsModel tempCredentials,
List<AccountModel> accounts,
String? serverMessage,
bool hasQuickConnect)?
$default,
) {
final _that = this;
switch (_that) {
case _ServerLoginModel() when $default != null:
return $default(_that.tempCredentials, _that.accounts,
_that.serverMessage, _that.hasQuickConnect);
case _:
return null;
}
}
}
/// @nodoc
class _ServerLoginModel implements ServerLoginModel {
_ServerLoginModel(
{required this.tempCredentials,
final List<AccountModel> accounts = const [],
this.serverMessage,
this.hasQuickConnect = false})
: _accounts = accounts;
@override
final CredentialsModel tempCredentials;
final List<AccountModel> _accounts;
@override
@JsonKey()
List<AccountModel> get accounts {
if (_accounts is EqualUnmodifiableListView) return _accounts;
// ignore: implicit_dynamic_type
return EqualUnmodifiableListView(_accounts);
}
@override
final String? serverMessage;
@override
@JsonKey()
final bool hasQuickConnect;
/// Create a copy of ServerLoginModel
/// with the given fields replaced by the non-null parameter values.
@override
@JsonKey(includeFromJson: false, includeToJson: false)
@pragma('vm:prefer-inline')
_$ServerLoginModelCopyWith<_ServerLoginModel> get copyWith =>
__$ServerLoginModelCopyWithImpl<_ServerLoginModel>(this, _$identity);
@override
String toString() {
return 'ServerLoginModel(tempCredentials: $tempCredentials, accounts: $accounts, serverMessage: $serverMessage, hasQuickConnect: $hasQuickConnect)';
}
}
/// @nodoc
abstract mixin class _$ServerLoginModelCopyWith<$Res>
implements $ServerLoginModelCopyWith<$Res> {
factory _$ServerLoginModelCopyWith(
_ServerLoginModel value, $Res Function(_ServerLoginModel) _then) =
__$ServerLoginModelCopyWithImpl;
@override
@useResult
$Res call(
{CredentialsModel tempCredentials,
List<AccountModel> accounts,
String? serverMessage,
bool hasQuickConnect});
}
/// @nodoc
class __$ServerLoginModelCopyWithImpl<$Res>
implements _$ServerLoginModelCopyWith<$Res> {
__$ServerLoginModelCopyWithImpl(this._self, this._then);
final _ServerLoginModel _self;
final $Res Function(_ServerLoginModel) _then;
/// Create a copy of ServerLoginModel
/// with the given fields replaced by the non-null parameter values.
@override
@pragma('vm:prefer-inline')
$Res call({
Object? tempCredentials = null,
Object? accounts = null,
Object? serverMessage = freezed,
Object? hasQuickConnect = null,
}) {
return _then(_ServerLoginModel(
tempCredentials: null == tempCredentials
? _self.tempCredentials
: tempCredentials // ignore: cast_nullable_to_non_nullable
as CredentialsModel,
accounts: null == accounts
? _self._accounts
: accounts // ignore: cast_nullable_to_non_nullable
as List<AccountModel>,
serverMessage: freezed == serverMessage
? _self.serverMessage
: serverMessage // ignore: cast_nullable_to_non_nullable
as String?,
hasQuickConnect: null == hasQuickConnect
? _self.hasQuickConnect
: hasQuickConnect // ignore: cast_nullable_to_non_nullable
as bool,
));
}
}
// dart format on

View file

@ -137,7 +137,7 @@ class PlaybackModelHelper {
oldModel: currentModel,
);
if (newModel == null) return null;
ref.read(videoPlayerProvider.notifier).loadPlaybackItem(newModel, startPosition: Duration.zero);
ref.read(videoPlayerProvider.notifier).loadPlaybackItem(newModel, Duration.zero);
return newModel;
}
@ -502,7 +502,7 @@ class PlaybackModelHelper {
}
if (newModel == null) return;
if (newModel.runtimeType != playbackModel.runtimeType || newModel is TranscodePlaybackModel) {
ref.read(videoPlayerProvider.notifier).loadPlaybackItem(newModel, startPosition: currentPosition);
ref.read(videoPlayerProvider.notifier).loadPlaybackItem(newModel, currentPosition);
}
}
}

View file

@ -8,12 +8,14 @@ abstract class ArgumentsModel with _$ArgumentsModel {
factory ArgumentsModel({
@Default(false) bool htpcMode,
@Default(false) bool leanBackMode,
}) = _ArgumentsModel;
factory ArgumentsModel.fromArguments(List<String> arguments) {
factory ArgumentsModel.fromArguments(List<String> arguments, bool leanBackEnabled) {
arguments = arguments.map((e) => e.trim()).toList();
return ArgumentsModel(
htpcMode: arguments.contains('--htpc'),
htpcMode: arguments.contains('--htpc') || leanBackEnabled,
leanBackMode: leanBackEnabled,
);
}
}

View file

@ -15,10 +15,11 @@ T _$identity<T>(T value) => value;
/// @nodoc
mixin _$ArgumentsModel {
bool get htpcMode;
bool get leanBackMode;
@override
String toString() {
return 'ArgumentsModel(htpcMode: $htpcMode)';
return 'ArgumentsModel(htpcMode: $htpcMode, leanBackMode: $leanBackMode)';
}
}
@ -115,13 +116,13 @@ extension ArgumentsModelPatterns on ArgumentsModel {
@optionalTypeArgs
TResult maybeWhen<TResult extends Object?>(
TResult Function(bool htpcMode)? $default, {
TResult Function(bool htpcMode, bool leanBackMode)? $default, {
required TResult orElse(),
}) {
final _that = this;
switch (_that) {
case _ArgumentsModel() when $default != null:
return $default(_that.htpcMode);
return $default(_that.htpcMode, _that.leanBackMode);
case _:
return orElse();
}
@ -142,12 +143,12 @@ extension ArgumentsModelPatterns on ArgumentsModel {
@optionalTypeArgs
TResult when<TResult extends Object?>(
TResult Function(bool htpcMode) $default,
TResult Function(bool htpcMode, bool leanBackMode) $default,
) {
final _that = this;
switch (_that) {
case _ArgumentsModel():
return $default(_that.htpcMode);
return $default(_that.htpcMode, _that.leanBackMode);
case _:
throw StateError('Unexpected subclass');
}
@ -167,12 +168,12 @@ extension ArgumentsModelPatterns on ArgumentsModel {
@optionalTypeArgs
TResult? whenOrNull<TResult extends Object?>(
TResult? Function(bool htpcMode)? $default,
TResult? Function(bool htpcMode, bool leanBackMode)? $default,
) {
final _that = this;
switch (_that) {
case _ArgumentsModel() when $default != null:
return $default(_that.htpcMode);
return $default(_that.htpcMode, _that.leanBackMode);
case _:
return null;
}
@ -182,15 +183,19 @@ extension ArgumentsModelPatterns on ArgumentsModel {
/// @nodoc
class _ArgumentsModel extends ArgumentsModel {
_ArgumentsModel({this.htpcMode = false}) : super._();
_ArgumentsModel({this.htpcMode = false, this.leanBackMode = false})
: super._();
@override
@JsonKey()
final bool htpcMode;
@override
@JsonKey()
final bool leanBackMode;
@override
String toString() {
return 'ArgumentsModel(htpcMode: $htpcMode)';
return 'ArgumentsModel(htpcMode: $htpcMode, leanBackMode: $leanBackMode)';
}
}

View file

@ -40,7 +40,8 @@ T selectAvailableOrSmaller<T>(T value, Set<T> availableOptions, List<T> allOptio
enum HomeBanner {
hide,
carousel,
banner;
banner,
detailedBanner;
const HomeBanner();
@ -48,6 +49,7 @@ enum HomeBanner {
HomeBanner.hide => context.localized.hide,
HomeBanner.carousel => context.localized.homeBannerCarousel,
HomeBanner.banner => context.localized.homeBannerSlideshow,
HomeBanner.detailedBanner => 'Detailed banner'
};
}

View file

@ -47,12 +47,14 @@ const _$ViewSizeEnumMap = {
ViewSize.phone: 'phone',
ViewSize.tablet: 'tablet',
ViewSize.desktop: 'desktop',
ViewSize.television: 'television',
};
const _$HomeBannerEnumMap = {
HomeBanner.hide: 'hide',
HomeBanner.carousel: 'carousel',
HomeBanner.banner: 'banner',
HomeBanner.detailedBanner: 'detailedBanner',
};
const _$HomeCarouselSettingsEnumMap = {

View file

@ -127,11 +127,17 @@ abstract class VideoPlayerSettingsModel with _$VideoPlayerSettingsModel {
enum PlayerOptions {
libMDK,
libMPV;
libMPV,
nativePlayer;
const PlayerOptions();
static Iterable<PlayerOptions> get available => kIsWeb ? {PlayerOptions.libMPV} : PlayerOptions.values;
static Iterable<PlayerOptions> get available => kIsWeb
? {PlayerOptions.libMPV}
: switch (defaultTargetPlatform) {
TargetPlatform.android => PlayerOptions.values,
_ => {PlayerOptions.libMDK, PlayerOptions.libMPV},
};
static PlayerOptions get platformDefaults {
if (kIsWeb) return PlayerOptions.libMPV;
@ -143,6 +149,7 @@ enum PlayerOptions {
String label(BuildContext context) => switch (this) {
PlayerOptions.libMDK => "MDK",
PlayerOptions.libMPV => "MPV",
PlayerOptions.nativePlayer => "Native",
};
}

View file

@ -82,6 +82,7 @@ const _$BoxFitEnumMap = {
const _$PlayerOptionsEnumMap = {
PlayerOptions.libMDK: 'libMDK',
PlayerOptions.libMPV: 'libMPV',
PlayerOptions.nativePlayer: 'nativePlayer',
};
const _$DeviceOrientationEnumMap = {