@@ -35,6 +35,10 @@ sealed class LibraryQuery with _$LibraryQuery {
|
|||||||
/// Restrict to a single kind (e.g. `MediaKind.movie`). Null = library default.
|
/// Restrict to a single kind (e.g. `MediaKind.movie`). Null = library default.
|
||||||
MediaKind? kind,
|
MediaKind? kind,
|
||||||
|
|
||||||
|
/// Restrict to multiple kinds when no single [kind] represents the browse
|
||||||
|
/// surface. When non-empty, translators prefer this over [kind].
|
||||||
|
@Default(<MediaKind>[]) List<MediaKind> includeKinds,
|
||||||
|
|
||||||
/// Pagination — zero-based offset.
|
/// Pagination — zero-based offset.
|
||||||
@Default(0) int offset,
|
@Default(0) int offset,
|
||||||
@Default(50) int limit,
|
@Default(50) int limit,
|
||||||
|
|||||||
@@ -532,7 +532,9 @@ as List<String>,
|
|||||||
mixin _$LibraryQuery {
|
mixin _$LibraryQuery {
|
||||||
|
|
||||||
/// Restrict to a single kind (e.g. `MediaKind.movie`). Null = library default.
|
/// Restrict to a single kind (e.g. `MediaKind.movie`). Null = library default.
|
||||||
MediaKind? get kind;/// Pagination — zero-based offset.
|
MediaKind? get kind;/// Restrict to multiple kinds when no single [kind] represents the browse
|
||||||
|
/// surface. When non-empty, translators prefer this over [kind].
|
||||||
|
List<MediaKind> get includeKinds;/// Pagination — zero-based offset.
|
||||||
int get offset; int get limit; LibrarySort? get sort; List<LibraryFilter> get filters;/// Free-text search restricted to this library. Distinct from the global
|
int get offset; int get limit; LibrarySort? get sort; List<LibraryFilter> get filters;/// Free-text search restricted to this library. Distinct from the global
|
||||||
/// search endpoint.
|
/// search endpoint.
|
||||||
String? get search;/// Whether to include items the active user has already watched.
|
String? get search;/// Whether to include items the active user has already watched.
|
||||||
@@ -556,16 +558,16 @@ $LibraryQueryCopyWith<LibraryQuery> get copyWith => _$LibraryQueryCopyWithImpl<L
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
bool operator ==(Object other) {
|
bool operator ==(Object other) {
|
||||||
return identical(this, other) || (other.runtimeType == runtimeType&&other is LibraryQuery&&(identical(other.kind, kind) || other.kind == kind)&&(identical(other.offset, offset) || other.offset == offset)&&(identical(other.limit, limit) || other.limit == limit)&&(identical(other.sort, sort) || other.sort == sort)&&const DeepCollectionEquality().equals(other.filters, filters)&&(identical(other.search, search) || other.search == search)&&(identical(other.includeWatched, includeWatched) || other.includeWatched == includeWatched)&&(identical(other.favoritesOnly, favoritesOnly) || other.favoritesOnly == favoritesOnly)&&(identical(other.nameStartsWith, nameStartsWith) || other.nameStartsWith == nameStartsWith)&&const DeepCollectionEquality().equals(other.genres, genres)&&const DeepCollectionEquality().equals(other.officialRatings, officialRatings)&&const DeepCollectionEquality().equals(other.years, years)&&const DeepCollectionEquality().equals(other.tags, tags));
|
return identical(this, other) || (other.runtimeType == runtimeType&&other is LibraryQuery&&(identical(other.kind, kind) || other.kind == kind)&&const DeepCollectionEquality().equals(other.includeKinds, includeKinds)&&(identical(other.offset, offset) || other.offset == offset)&&(identical(other.limit, limit) || other.limit == limit)&&(identical(other.sort, sort) || other.sort == sort)&&const DeepCollectionEquality().equals(other.filters, filters)&&(identical(other.search, search) || other.search == search)&&(identical(other.includeWatched, includeWatched) || other.includeWatched == includeWatched)&&(identical(other.favoritesOnly, favoritesOnly) || other.favoritesOnly == favoritesOnly)&&(identical(other.nameStartsWith, nameStartsWith) || other.nameStartsWith == nameStartsWith)&&const DeepCollectionEquality().equals(other.genres, genres)&&const DeepCollectionEquality().equals(other.officialRatings, officialRatings)&&const DeepCollectionEquality().equals(other.years, years)&&const DeepCollectionEquality().equals(other.tags, tags));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
int get hashCode => Object.hash(runtimeType,kind,offset,limit,sort,const DeepCollectionEquality().hash(filters),search,includeWatched,favoritesOnly,nameStartsWith,const DeepCollectionEquality().hash(genres),const DeepCollectionEquality().hash(officialRatings),const DeepCollectionEquality().hash(years),const DeepCollectionEquality().hash(tags));
|
int get hashCode => Object.hash(runtimeType,kind,const DeepCollectionEquality().hash(includeKinds),offset,limit,sort,const DeepCollectionEquality().hash(filters),search,includeWatched,favoritesOnly,nameStartsWith,const DeepCollectionEquality().hash(genres),const DeepCollectionEquality().hash(officialRatings),const DeepCollectionEquality().hash(years),const DeepCollectionEquality().hash(tags));
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String toString() {
|
String toString() {
|
||||||
return 'LibraryQuery(kind: $kind, offset: $offset, limit: $limit, sort: $sort, filters: $filters, search: $search, includeWatched: $includeWatched, favoritesOnly: $favoritesOnly, nameStartsWith: $nameStartsWith, genres: $genres, officialRatings: $officialRatings, years: $years, tags: $tags)';
|
return 'LibraryQuery(kind: $kind, includeKinds: $includeKinds, offset: $offset, limit: $limit, sort: $sort, filters: $filters, search: $search, includeWatched: $includeWatched, favoritesOnly: $favoritesOnly, nameStartsWith: $nameStartsWith, genres: $genres, officialRatings: $officialRatings, years: $years, tags: $tags)';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -576,7 +578,7 @@ abstract mixin class $LibraryQueryCopyWith<$Res> {
|
|||||||
factory $LibraryQueryCopyWith(LibraryQuery value, $Res Function(LibraryQuery) _then) = _$LibraryQueryCopyWithImpl;
|
factory $LibraryQueryCopyWith(LibraryQuery value, $Res Function(LibraryQuery) _then) = _$LibraryQueryCopyWithImpl;
|
||||||
@useResult
|
@useResult
|
||||||
$Res call({
|
$Res call({
|
||||||
MediaKind? kind, int offset, int limit, LibrarySort? sort, List<LibraryFilter> filters, String? search, bool includeWatched, bool favoritesOnly, String? nameStartsWith, List<String>? genres, List<String>? officialRatings, List<int>? years, List<String>? tags
|
MediaKind? kind, List<MediaKind> includeKinds, int offset, int limit, LibrarySort? sort, List<LibraryFilter> filters, String? search, bool includeWatched, bool favoritesOnly, String? nameStartsWith, List<String>? genres, List<String>? officialRatings, List<int>? years, List<String>? tags
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
@@ -593,10 +595,11 @@ class _$LibraryQueryCopyWithImpl<$Res>
|
|||||||
|
|
||||||
/// Create a copy of LibraryQuery
|
/// Create a copy of LibraryQuery
|
||||||
/// with the given fields replaced by the non-null parameter values.
|
/// with the given fields replaced by the non-null parameter values.
|
||||||
@pragma('vm:prefer-inline') @override $Res call({Object? kind = freezed,Object? offset = null,Object? limit = null,Object? sort = freezed,Object? filters = null,Object? search = freezed,Object? includeWatched = null,Object? favoritesOnly = null,Object? nameStartsWith = freezed,Object? genres = freezed,Object? officialRatings = freezed,Object? years = freezed,Object? tags = freezed,}) {
|
@pragma('vm:prefer-inline') @override $Res call({Object? kind = freezed,Object? includeKinds = null,Object? offset = null,Object? limit = null,Object? sort = freezed,Object? filters = null,Object? search = freezed,Object? includeWatched = null,Object? favoritesOnly = null,Object? nameStartsWith = freezed,Object? genres = freezed,Object? officialRatings = freezed,Object? years = freezed,Object? tags = freezed,}) {
|
||||||
return _then(_self.copyWith(
|
return _then(_self.copyWith(
|
||||||
kind: freezed == kind ? _self.kind : kind // ignore: cast_nullable_to_non_nullable
|
kind: freezed == kind ? _self.kind : kind // ignore: cast_nullable_to_non_nullable
|
||||||
as MediaKind?,offset: null == offset ? _self.offset : offset // ignore: cast_nullable_to_non_nullable
|
as MediaKind?,includeKinds: null == includeKinds ? _self.includeKinds : includeKinds // ignore: cast_nullable_to_non_nullable
|
||||||
|
as List<MediaKind>,offset: null == offset ? _self.offset : offset // ignore: cast_nullable_to_non_nullable
|
||||||
as int,limit: null == limit ? _self.limit : limit // ignore: cast_nullable_to_non_nullable
|
as int,limit: null == limit ? _self.limit : limit // ignore: cast_nullable_to_non_nullable
|
||||||
as int,sort: freezed == sort ? _self.sort : sort // ignore: cast_nullable_to_non_nullable
|
as int,sort: freezed == sort ? _self.sort : sort // ignore: cast_nullable_to_non_nullable
|
||||||
as LibrarySort?,filters: null == filters ? _self.filters : filters // ignore: cast_nullable_to_non_nullable
|
as LibrarySort?,filters: null == filters ? _self.filters : filters // ignore: cast_nullable_to_non_nullable
|
||||||
@@ -702,10 +705,10 @@ return $default(_that);case _:
|
|||||||
/// }
|
/// }
|
||||||
/// ```
|
/// ```
|
||||||
|
|
||||||
@optionalTypeArgs TResult maybeWhen<TResult extends Object?>(TResult Function( MediaKind? kind, int offset, int limit, LibrarySort? sort, List<LibraryFilter> filters, String? search, bool includeWatched, bool favoritesOnly, String? nameStartsWith, List<String>? genres, List<String>? officialRatings, List<int>? years, List<String>? tags)? $default,{required TResult orElse(),}) {final _that = this;
|
@optionalTypeArgs TResult maybeWhen<TResult extends Object?>(TResult Function( MediaKind? kind, List<MediaKind> includeKinds, int offset, int limit, LibrarySort? sort, List<LibraryFilter> filters, String? search, bool includeWatched, bool favoritesOnly, String? nameStartsWith, List<String>? genres, List<String>? officialRatings, List<int>? years, List<String>? tags)? $default,{required TResult orElse(),}) {final _that = this;
|
||||||
switch (_that) {
|
switch (_that) {
|
||||||
case _LibraryQuery() when $default != null:
|
case _LibraryQuery() when $default != null:
|
||||||
return $default(_that.kind,_that.offset,_that.limit,_that.sort,_that.filters,_that.search,_that.includeWatched,_that.favoritesOnly,_that.nameStartsWith,_that.genres,_that.officialRatings,_that.years,_that.tags);case _:
|
return $default(_that.kind,_that.includeKinds,_that.offset,_that.limit,_that.sort,_that.filters,_that.search,_that.includeWatched,_that.favoritesOnly,_that.nameStartsWith,_that.genres,_that.officialRatings,_that.years,_that.tags);case _:
|
||||||
return orElse();
|
return orElse();
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -723,10 +726,10 @@ return $default(_that.kind,_that.offset,_that.limit,_that.sort,_that.filters,_th
|
|||||||
/// }
|
/// }
|
||||||
/// ```
|
/// ```
|
||||||
|
|
||||||
@optionalTypeArgs TResult when<TResult extends Object?>(TResult Function( MediaKind? kind, int offset, int limit, LibrarySort? sort, List<LibraryFilter> filters, String? search, bool includeWatched, bool favoritesOnly, String? nameStartsWith, List<String>? genres, List<String>? officialRatings, List<int>? years, List<String>? tags) $default,) {final _that = this;
|
@optionalTypeArgs TResult when<TResult extends Object?>(TResult Function( MediaKind? kind, List<MediaKind> includeKinds, int offset, int limit, LibrarySort? sort, List<LibraryFilter> filters, String? search, bool includeWatched, bool favoritesOnly, String? nameStartsWith, List<String>? genres, List<String>? officialRatings, List<int>? years, List<String>? tags) $default,) {final _that = this;
|
||||||
switch (_that) {
|
switch (_that) {
|
||||||
case _LibraryQuery():
|
case _LibraryQuery():
|
||||||
return $default(_that.kind,_that.offset,_that.limit,_that.sort,_that.filters,_that.search,_that.includeWatched,_that.favoritesOnly,_that.nameStartsWith,_that.genres,_that.officialRatings,_that.years,_that.tags);}
|
return $default(_that.kind,_that.includeKinds,_that.offset,_that.limit,_that.sort,_that.filters,_that.search,_that.includeWatched,_that.favoritesOnly,_that.nameStartsWith,_that.genres,_that.officialRatings,_that.years,_that.tags);}
|
||||||
}
|
}
|
||||||
/// A variant of `when` that fallback to returning `null`
|
/// A variant of `when` that fallback to returning `null`
|
||||||
///
|
///
|
||||||
@@ -740,10 +743,10 @@ return $default(_that.kind,_that.offset,_that.limit,_that.sort,_that.filters,_th
|
|||||||
/// }
|
/// }
|
||||||
/// ```
|
/// ```
|
||||||
|
|
||||||
@optionalTypeArgs TResult? whenOrNull<TResult extends Object?>(TResult? Function( MediaKind? kind, int offset, int limit, LibrarySort? sort, List<LibraryFilter> filters, String? search, bool includeWatched, bool favoritesOnly, String? nameStartsWith, List<String>? genres, List<String>? officialRatings, List<int>? years, List<String>? tags)? $default,) {final _that = this;
|
@optionalTypeArgs TResult? whenOrNull<TResult extends Object?>(TResult? Function( MediaKind? kind, List<MediaKind> includeKinds, int offset, int limit, LibrarySort? sort, List<LibraryFilter> filters, String? search, bool includeWatched, bool favoritesOnly, String? nameStartsWith, List<String>? genres, List<String>? officialRatings, List<int>? years, List<String>? tags)? $default,) {final _that = this;
|
||||||
switch (_that) {
|
switch (_that) {
|
||||||
case _LibraryQuery() when $default != null:
|
case _LibraryQuery() when $default != null:
|
||||||
return $default(_that.kind,_that.offset,_that.limit,_that.sort,_that.filters,_that.search,_that.includeWatched,_that.favoritesOnly,_that.nameStartsWith,_that.genres,_that.officialRatings,_that.years,_that.tags);case _:
|
return $default(_that.kind,_that.includeKinds,_that.offset,_that.limit,_that.sort,_that.filters,_that.search,_that.includeWatched,_that.favoritesOnly,_that.nameStartsWith,_that.genres,_that.officialRatings,_that.years,_that.tags);case _:
|
||||||
return null;
|
return null;
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -755,11 +758,22 @@ return $default(_that.kind,_that.offset,_that.limit,_that.sort,_that.filters,_th
|
|||||||
|
|
||||||
|
|
||||||
class _LibraryQuery implements LibraryQuery {
|
class _LibraryQuery implements LibraryQuery {
|
||||||
const _LibraryQuery({this.kind, this.offset = 0, this.limit = 50, this.sort, final List<LibraryFilter> filters = const <LibraryFilter>[], this.search, this.includeWatched = true, this.favoritesOnly = false, this.nameStartsWith, final List<String>? genres, final List<String>? officialRatings, final List<int>? years, final List<String>? tags}): _filters = filters,_genres = genres,_officialRatings = officialRatings,_years = years,_tags = tags;
|
const _LibraryQuery({this.kind, final List<MediaKind> includeKinds = const <MediaKind>[], this.offset = 0, this.limit = 50, this.sort, final List<LibraryFilter> filters = const <LibraryFilter>[], this.search, this.includeWatched = true, this.favoritesOnly = false, this.nameStartsWith, final List<String>? genres, final List<String>? officialRatings, final List<int>? years, final List<String>? tags}): _includeKinds = includeKinds,_filters = filters,_genres = genres,_officialRatings = officialRatings,_years = years,_tags = tags;
|
||||||
|
|
||||||
|
|
||||||
/// Restrict to a single kind (e.g. `MediaKind.movie`). Null = library default.
|
/// Restrict to a single kind (e.g. `MediaKind.movie`). Null = library default.
|
||||||
@override final MediaKind? kind;
|
@override final MediaKind? kind;
|
||||||
|
/// Restrict to multiple kinds when no single [kind] represents the browse
|
||||||
|
/// surface. When non-empty, translators prefer this over [kind].
|
||||||
|
final List<MediaKind> _includeKinds;
|
||||||
|
/// Restrict to multiple kinds when no single [kind] represents the browse
|
||||||
|
/// surface. When non-empty, translators prefer this over [kind].
|
||||||
|
@override@JsonKey() List<MediaKind> get includeKinds {
|
||||||
|
if (_includeKinds is EqualUnmodifiableListView) return _includeKinds;
|
||||||
|
// ignore: implicit_dynamic_type
|
||||||
|
return EqualUnmodifiableListView(_includeKinds);
|
||||||
|
}
|
||||||
|
|
||||||
/// Pagination — zero-based offset.
|
/// Pagination — zero-based offset.
|
||||||
@override@JsonKey() final int offset;
|
@override@JsonKey() final int offset;
|
||||||
@override@JsonKey() final int limit;
|
@override@JsonKey() final int limit;
|
||||||
@@ -837,16 +851,16 @@ _$LibraryQueryCopyWith<_LibraryQuery> get copyWith => __$LibraryQueryCopyWithImp
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
bool operator ==(Object other) {
|
bool operator ==(Object other) {
|
||||||
return identical(this, other) || (other.runtimeType == runtimeType&&other is _LibraryQuery&&(identical(other.kind, kind) || other.kind == kind)&&(identical(other.offset, offset) || other.offset == offset)&&(identical(other.limit, limit) || other.limit == limit)&&(identical(other.sort, sort) || other.sort == sort)&&const DeepCollectionEquality().equals(other._filters, _filters)&&(identical(other.search, search) || other.search == search)&&(identical(other.includeWatched, includeWatched) || other.includeWatched == includeWatched)&&(identical(other.favoritesOnly, favoritesOnly) || other.favoritesOnly == favoritesOnly)&&(identical(other.nameStartsWith, nameStartsWith) || other.nameStartsWith == nameStartsWith)&&const DeepCollectionEquality().equals(other._genres, _genres)&&const DeepCollectionEquality().equals(other._officialRatings, _officialRatings)&&const DeepCollectionEquality().equals(other._years, _years)&&const DeepCollectionEquality().equals(other._tags, _tags));
|
return identical(this, other) || (other.runtimeType == runtimeType&&other is _LibraryQuery&&(identical(other.kind, kind) || other.kind == kind)&&const DeepCollectionEquality().equals(other._includeKinds, _includeKinds)&&(identical(other.offset, offset) || other.offset == offset)&&(identical(other.limit, limit) || other.limit == limit)&&(identical(other.sort, sort) || other.sort == sort)&&const DeepCollectionEquality().equals(other._filters, _filters)&&(identical(other.search, search) || other.search == search)&&(identical(other.includeWatched, includeWatched) || other.includeWatched == includeWatched)&&(identical(other.favoritesOnly, favoritesOnly) || other.favoritesOnly == favoritesOnly)&&(identical(other.nameStartsWith, nameStartsWith) || other.nameStartsWith == nameStartsWith)&&const DeepCollectionEquality().equals(other._genres, _genres)&&const DeepCollectionEquality().equals(other._officialRatings, _officialRatings)&&const DeepCollectionEquality().equals(other._years, _years)&&const DeepCollectionEquality().equals(other._tags, _tags));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
int get hashCode => Object.hash(runtimeType,kind,offset,limit,sort,const DeepCollectionEquality().hash(_filters),search,includeWatched,favoritesOnly,nameStartsWith,const DeepCollectionEquality().hash(_genres),const DeepCollectionEquality().hash(_officialRatings),const DeepCollectionEquality().hash(_years),const DeepCollectionEquality().hash(_tags));
|
int get hashCode => Object.hash(runtimeType,kind,const DeepCollectionEquality().hash(_includeKinds),offset,limit,sort,const DeepCollectionEquality().hash(_filters),search,includeWatched,favoritesOnly,nameStartsWith,const DeepCollectionEquality().hash(_genres),const DeepCollectionEquality().hash(_officialRatings),const DeepCollectionEquality().hash(_years),const DeepCollectionEquality().hash(_tags));
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String toString() {
|
String toString() {
|
||||||
return 'LibraryQuery(kind: $kind, offset: $offset, limit: $limit, sort: $sort, filters: $filters, search: $search, includeWatched: $includeWatched, favoritesOnly: $favoritesOnly, nameStartsWith: $nameStartsWith, genres: $genres, officialRatings: $officialRatings, years: $years, tags: $tags)';
|
return 'LibraryQuery(kind: $kind, includeKinds: $includeKinds, offset: $offset, limit: $limit, sort: $sort, filters: $filters, search: $search, includeWatched: $includeWatched, favoritesOnly: $favoritesOnly, nameStartsWith: $nameStartsWith, genres: $genres, officialRatings: $officialRatings, years: $years, tags: $tags)';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -857,7 +871,7 @@ abstract mixin class _$LibraryQueryCopyWith<$Res> implements $LibraryQueryCopyWi
|
|||||||
factory _$LibraryQueryCopyWith(_LibraryQuery value, $Res Function(_LibraryQuery) _then) = __$LibraryQueryCopyWithImpl;
|
factory _$LibraryQueryCopyWith(_LibraryQuery value, $Res Function(_LibraryQuery) _then) = __$LibraryQueryCopyWithImpl;
|
||||||
@override @useResult
|
@override @useResult
|
||||||
$Res call({
|
$Res call({
|
||||||
MediaKind? kind, int offset, int limit, LibrarySort? sort, List<LibraryFilter> filters, String? search, bool includeWatched, bool favoritesOnly, String? nameStartsWith, List<String>? genres, List<String>? officialRatings, List<int>? years, List<String>? tags
|
MediaKind? kind, List<MediaKind> includeKinds, int offset, int limit, LibrarySort? sort, List<LibraryFilter> filters, String? search, bool includeWatched, bool favoritesOnly, String? nameStartsWith, List<String>? genres, List<String>? officialRatings, List<int>? years, List<String>? tags
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
@@ -874,10 +888,11 @@ class __$LibraryQueryCopyWithImpl<$Res>
|
|||||||
|
|
||||||
/// Create a copy of LibraryQuery
|
/// Create a copy of LibraryQuery
|
||||||
/// with the given fields replaced by the non-null parameter values.
|
/// with the given fields replaced by the non-null parameter values.
|
||||||
@override @pragma('vm:prefer-inline') $Res call({Object? kind = freezed,Object? offset = null,Object? limit = null,Object? sort = freezed,Object? filters = null,Object? search = freezed,Object? includeWatched = null,Object? favoritesOnly = null,Object? nameStartsWith = freezed,Object? genres = freezed,Object? officialRatings = freezed,Object? years = freezed,Object? tags = freezed,}) {
|
@override @pragma('vm:prefer-inline') $Res call({Object? kind = freezed,Object? includeKinds = null,Object? offset = null,Object? limit = null,Object? sort = freezed,Object? filters = null,Object? search = freezed,Object? includeWatched = null,Object? favoritesOnly = null,Object? nameStartsWith = freezed,Object? genres = freezed,Object? officialRatings = freezed,Object? years = freezed,Object? tags = freezed,}) {
|
||||||
return _then(_LibraryQuery(
|
return _then(_LibraryQuery(
|
||||||
kind: freezed == kind ? _self.kind : kind // ignore: cast_nullable_to_non_nullable
|
kind: freezed == kind ? _self.kind : kind // ignore: cast_nullable_to_non_nullable
|
||||||
as MediaKind?,offset: null == offset ? _self.offset : offset // ignore: cast_nullable_to_non_nullable
|
as MediaKind?,includeKinds: null == includeKinds ? _self._includeKinds : includeKinds // ignore: cast_nullable_to_non_nullable
|
||||||
|
as List<MediaKind>,offset: null == offset ? _self.offset : offset // ignore: cast_nullable_to_non_nullable
|
||||||
as int,limit: null == limit ? _self.limit : limit // ignore: cast_nullable_to_non_nullable
|
as int,limit: null == limit ? _self.limit : limit // ignore: cast_nullable_to_non_nullable
|
||||||
as int,sort: freezed == sort ? _self.sort : sort // ignore: cast_nullable_to_non_nullable
|
as int,sort: freezed == sort ? _self.sort : sort // ignore: cast_nullable_to_non_nullable
|
||||||
as LibrarySort?,filters: null == filters ? _self._filters : filters // ignore: cast_nullable_to_non_nullable
|
as LibrarySort?,filters: null == filters ? _self._filters : filters // ignore: cast_nullable_to_non_nullable
|
||||||
|
|||||||
@@ -15,6 +15,10 @@ class MediaLibrary {
|
|||||||
/// (poster shape, sort options). For mixed libraries this is [MediaKind.unknown].
|
/// (poster shape, sort options). For mixed libraries this is [MediaKind.unknown].
|
||||||
final MediaKind kind;
|
final MediaKind kind;
|
||||||
|
|
||||||
|
/// Explicit item kinds for this library's flat root browse. Mixed libraries
|
||||||
|
/// use this when no single [kind] can describe the server's root view.
|
||||||
|
final List<MediaKind> defaultBrowseKinds;
|
||||||
|
|
||||||
/// Optional ISO language code of the library's metadata locale.
|
/// Optional ISO language code of the library's metadata locale.
|
||||||
final String? language;
|
final String? language;
|
||||||
|
|
||||||
@@ -37,6 +41,7 @@ class MediaLibrary {
|
|||||||
required this.backend,
|
required this.backend,
|
||||||
required this.title,
|
required this.title,
|
||||||
this.kind = MediaKind.unknown,
|
this.kind = MediaKind.unknown,
|
||||||
|
this.defaultBrowseKinds = const [],
|
||||||
this.language,
|
this.language,
|
||||||
this.updatedAt,
|
this.updatedAt,
|
||||||
this.createdAt,
|
this.createdAt,
|
||||||
|
|||||||
@@ -760,12 +760,19 @@ class _LibraryBrowseTabState extends BaseLibraryTabState<MediaItem, LibraryBrows
|
|||||||
Future<LibraryPage<MediaItem>> fetchPage(int start, int size, AbortController? abort) async {
|
Future<LibraryPage<MediaItem>> fetchPage(int start, int size, AbortController? abort) async {
|
||||||
final client = context.getMediaClientForLibrary(widget.library);
|
final client = context.getMediaClientForLibrary(widget.library);
|
||||||
final filterParams = _buildFilterParams();
|
final filterParams = _buildFilterParams();
|
||||||
final query = libraryQueryFromPlexMap(
|
final baseQuery = libraryQueryFromPlexMap(
|
||||||
map: filterParams,
|
map: filterParams,
|
||||||
libraryKind: filterParams.containsKey('type') ? null : widget.library.kind,
|
libraryKind: filterParams.containsKey('type') ? null : widget.library.kind,
|
||||||
offset: start,
|
offset: start,
|
||||||
limit: size,
|
limit: size,
|
||||||
);
|
);
|
||||||
|
final query =
|
||||||
|
baseQuery.kind == null &&
|
||||||
|
baseQuery.includeKinds.isEmpty &&
|
||||||
|
_selectedGrouping == browseGroupingAll &&
|
||||||
|
widget.library.defaultBrowseKinds.isNotEmpty
|
||||||
|
? baseQuery.copyWith(includeKinds: widget.library.defaultBrowseKinds)
|
||||||
|
: baseQuery;
|
||||||
return client.fetchLibraryPagedContent(
|
return client.fetchLibraryPagedContent(
|
||||||
widget.library.id,
|
widget.library.id,
|
||||||
query: query,
|
query: query,
|
||||||
|
|||||||
@@ -428,7 +428,8 @@ mixin _JellyfinBrowseMethods on MediaServerCacheMixin {
|
|||||||
}) async {
|
}) async {
|
||||||
// [libraryKind] is only a fallback for library-default browsing. Explicit
|
// [libraryKind] is only a fallback for library-default browsing. Explicit
|
||||||
// grouping types on [query] (seasons/episodes) must keep priority.
|
// grouping types on [query] (seasons/episodes) must keep priority.
|
||||||
final effective = (query.kind == null && libraryKind != null && libraryKind != MediaKind.unknown)
|
final effective =
|
||||||
|
(query.kind == null && query.includeKinds.isEmpty && libraryKind != null && libraryKind != MediaKind.unknown)
|
||||||
? query.copyWith(kind: libraryKind)
|
? query.copyWith(kind: libraryKind)
|
||||||
: query;
|
: query;
|
||||||
return fetchLibraryContent(libraryId, effective, abort: abort);
|
return fetchLibraryContent(libraryId, effective, abort: abort);
|
||||||
|
|||||||
@@ -273,6 +273,7 @@ class JellyfinMappers {
|
|||||||
backend: MediaBackend.jellyfin,
|
backend: MediaBackend.jellyfin,
|
||||||
title: view['Name'] as String? ?? t.libraries.fallbackTitle,
|
title: view['Name'] as String? ?? t.libraries.fallbackTitle,
|
||||||
kind: _libraryKindFromCollectionType(collectionType, view['Type'] as String?),
|
kind: _libraryKindFromCollectionType(collectionType, view['Type'] as String?),
|
||||||
|
defaultBrowseKinds: _defaultBrowseKindsFromCollectionType(collectionType),
|
||||||
updatedAt: jellyfinIsoToEpochSeconds(view['DateLastSaved'] as String? ?? view['DateModified'] as String?),
|
updatedAt: jellyfinIsoToEpochSeconds(view['DateLastSaved'] as String? ?? view['DateModified'] as String?),
|
||||||
createdAt: jellyfinIsoToEpochSeconds(view['DateCreated'] as String?),
|
createdAt: jellyfinIsoToEpochSeconds(view['DateCreated'] as String?),
|
||||||
hidden: false,
|
hidden: false,
|
||||||
@@ -334,6 +335,10 @@ class JellyfinMappers {
|
|||||||
return MediaKind.fromString(type);
|
return MediaKind.fromString(type);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static List<MediaKind> _defaultBrowseKindsFromCollectionType(String? collectionType) {
|
||||||
|
return collectionType?.toLowerCase() == 'mixed' ? const [MediaKind.movie, MediaKind.show] : const <MediaKind>[];
|
||||||
|
}
|
||||||
|
|
||||||
static Map<String, dynamic>? _userData(Map<String, dynamic> item) {
|
static Map<String, dynamic>? _userData(Map<String, dynamic> item) {
|
||||||
final ud = item['UserData'];
|
final ud = item['UserData'];
|
||||||
return ud is Map<String, dynamic> ? ud : null;
|
return ud is Map<String, dynamic> ? ud : null;
|
||||||
|
|||||||
@@ -52,9 +52,16 @@ class PlexLibraryQueryTranslator implements LibraryQueryTranslator {
|
|||||||
@override
|
@override
|
||||||
Map<String, String> toQueryParameters(LibraryQuery query) {
|
Map<String, String> toQueryParameters(LibraryQuery query) {
|
||||||
final filters = <String, String>{};
|
final filters = <String, String>{};
|
||||||
final kindNumber = _plexTypeNumberFor(query.kind);
|
if (query.includeKinds.isNotEmpty) {
|
||||||
if (kindNumber != null) {
|
final kindNumbers = query.includeKinds.map(_plexTypeNumberFor).whereType<int>().join(',');
|
||||||
filters['type'] = kindNumber.toString();
|
if (kindNumbers.isNotEmpty) {
|
||||||
|
filters['type'] = kindNumbers;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
final kindNumber = _plexTypeNumberFor(query.kind);
|
||||||
|
if (kindNumber != null) {
|
||||||
|
filters['type'] = kindNumber.toString();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
final sort = query.sort;
|
final sort = query.sort;
|
||||||
if (sort != null) {
|
if (sort != null) {
|
||||||
@@ -221,7 +228,7 @@ class JellyfinLibraryQueryTranslator implements LibraryQueryTranslator {
|
|||||||
'StartIndex': query.offset.toString(),
|
'StartIndex': query.offset.toString(),
|
||||||
'Limit': query.limit.toString(),
|
'Limit': query.limit.toString(),
|
||||||
'EnableTotalRecordCount': 'true',
|
'EnableTotalRecordCount': 'true',
|
||||||
'IncludeItemTypes': _includeTypesFor(query.kind),
|
'IncludeItemTypes': _includeTypesFor(query),
|
||||||
'Fields': fields,
|
'Fields': fields,
|
||||||
...jellyfinImageQueryParameters,
|
...jellyfinImageQueryParameters,
|
||||||
};
|
};
|
||||||
@@ -263,7 +270,14 @@ class JellyfinLibraryQueryTranslator implements LibraryQueryTranslator {
|
|||||||
return params;
|
return params;
|
||||||
}
|
}
|
||||||
|
|
||||||
static String _includeTypesFor(MediaKind? kind) {
|
static String _includeTypesFor(LibraryQuery query) {
|
||||||
|
if (query.includeKinds.isNotEmpty) {
|
||||||
|
return query.includeKinds.map(_includeTypesForKind).join(',');
|
||||||
|
}
|
||||||
|
return _includeTypesForKind(query.kind);
|
||||||
|
}
|
||||||
|
|
||||||
|
static String _includeTypesForKind(MediaKind? kind) {
|
||||||
return switch (kind) {
|
return switch (kind) {
|
||||||
MediaKind.movie => 'Movie',
|
MediaKind.movie => 'Movie',
|
||||||
MediaKind.show => 'Series',
|
MediaKind.show => 'Series',
|
||||||
|
|||||||
@@ -132,6 +132,26 @@ void main() {
|
|||||||
expect(harness.loadedLibraries, [harness.libraryA.globalKey]);
|
expect(harness.loadedLibraries, [harness.libraryA.globalKey]);
|
||||||
expect(groupingChip.focusNode!.hasFocus, isTrue);
|
expect(groupingChip.focusNode!.hasFocus, isTrue);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
testWidgets('mixed library all grouping applies its explicit root kinds', (tester) async {
|
||||||
|
final client = _BrowseClient('server-a', 'Mixed');
|
||||||
|
final harness = _BrowseHarness(clientA: client);
|
||||||
|
addTearDown(harness.dispose);
|
||||||
|
harness.selectedLibrary.value = MediaLibrary(
|
||||||
|
id: 'mixed-library',
|
||||||
|
backend: MediaBackend.jellyfin,
|
||||||
|
title: 'Mixed',
|
||||||
|
defaultBrowseKinds: const [MediaKind.movie, MediaKind.show],
|
||||||
|
serverId: client.serverId,
|
||||||
|
);
|
||||||
|
|
||||||
|
await _pumpHarness(tester, harness);
|
||||||
|
|
||||||
|
expect(client.pageQueries, hasLength(1));
|
||||||
|
expect(client.pageQueries.single.kind, isNull);
|
||||||
|
expect(client.pageQueries.single.includeKinds, const [MediaKind.movie, MediaKind.show]);
|
||||||
|
expect(client.pageLibraryKinds.single, MediaKind.unknown);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> _pumpHarness(WidgetTester tester, _BrowseHarness harness, {bool settle = true}) async {
|
Future<void> _pumpHarness(WidgetTester tester, _BrowseHarness harness, {bool settle = true}) async {
|
||||||
@@ -257,6 +277,8 @@ class _BrowseClient implements MediaServerClient {
|
|||||||
final Queue<Future<LibraryPage<MediaItem>> Function()> pageResponses = Queue();
|
final Queue<Future<LibraryPage<MediaItem>> Function()> pageResponses = Queue();
|
||||||
var sortRequestCount = 0;
|
var sortRequestCount = 0;
|
||||||
var pageRequestCount = 0;
|
var pageRequestCount = 0;
|
||||||
|
final List<LibraryQuery> pageQueries = [];
|
||||||
|
final List<MediaKind?> pageLibraryKinds = [];
|
||||||
|
|
||||||
_BrowseClient(String serverId, this.itemTitle, {this.sortResponse}) : serverId = ServerId(serverId);
|
_BrowseClient(String serverId, this.itemTitle, {this.sortResponse}) : serverId = ServerId(serverId);
|
||||||
|
|
||||||
@@ -288,6 +310,8 @@ class _BrowseClient implements MediaServerClient {
|
|||||||
AbortController? abort,
|
AbortController? abort,
|
||||||
}) {
|
}) {
|
||||||
pageRequestCount++;
|
pageRequestCount++;
|
||||||
|
pageQueries.add(query);
|
||||||
|
pageLibraryKinds.add(libraryKind);
|
||||||
if (pageResponses.isNotEmpty) return pageResponses.removeFirst()();
|
if (pageResponses.isNotEmpty) return pageResponses.removeFirst()();
|
||||||
return Future.value(_pageFor(this, itemTitle));
|
return Future.value(_pageFor(this, itemTitle));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2342,6 +2342,8 @@ void main() {
|
|||||||
expect(albumBrowse['Fields'], 'PremiereDate,OriginalTitle,SortName');
|
expect(albumBrowse['Fields'], 'PremiereDate,OriginalTitle,SortName');
|
||||||
expect(albumBrowse['EnableUserData'], 'false');
|
expect(albumBrowse['EnableUserData'], 'false');
|
||||||
expect(trackBrowse['Fields'], 'UserData,PremiereDate,OriginalTitle,SortName');
|
expect(trackBrowse['Fields'], 'UserData,PremiereDate,OriginalTitle,SortName');
|
||||||
|
expect(albumBrowse['IncludeItemTypes'], 'MusicAlbum');
|
||||||
|
expect(trackBrowse['IncludeItemTypes'], 'Audio');
|
||||||
expect(trackBrowse.containsKey('EnableUserData'), isFalse);
|
expect(trackBrowse.containsKey('EnableUserData'), isFalse);
|
||||||
expect(artistAlbums['Fields'], 'PremiereDate,OriginalTitle,SortName');
|
expect(artistAlbums['Fields'], 'PremiereDate,OriginalTitle,SortName');
|
||||||
expect(artistAlbums['EnableUserData'], 'false');
|
expect(artistAlbums['EnableUserData'], 'false');
|
||||||
|
|||||||
@@ -474,13 +474,22 @@ void main() {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
test('falls back to MediaKind.unknown for unrecognised collections', () {
|
test('maps mixed collections to a movie and show root browse', () {
|
||||||
final lib = JellyfinMappers.library({
|
final mixed = JellyfinMappers.library({
|
||||||
'Id': 'view-x',
|
'Id': 'view-mixed',
|
||||||
'Name': 'Mixed',
|
'Name': 'Mixed',
|
||||||
'CollectionType': 'mixed',
|
'CollectionType': 'mixed',
|
||||||
}, serverId: ServerId(_serverId))!;
|
}, serverId: ServerId(_serverId))!;
|
||||||
expect(lib.kind, MediaKind.unknown);
|
expect(mixed.kind, MediaKind.unknown);
|
||||||
|
expect(mixed.defaultBrowseKinds, const [MediaKind.movie, MediaKind.show]);
|
||||||
|
|
||||||
|
final unrecognised = JellyfinMappers.library({
|
||||||
|
'Id': 'view-books',
|
||||||
|
'Name': 'Books',
|
||||||
|
'CollectionType': 'books',
|
||||||
|
}, serverId: ServerId(_serverId))!;
|
||||||
|
expect(unrecognised.kind, MediaKind.unknown);
|
||||||
|
expect(unrecognised.defaultBrowseKinds, isEmpty);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -21,6 +21,11 @@ void main() {
|
|||||||
expect(params['type'], '2');
|
expect(params['type'], '2');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('multiple kinds map to a comma-delimited type filter', () {
|
||||||
|
final params = translator.toQueryParameters(const LibraryQuery(includeKinds: [MediaKind.movie, MediaKind.show]));
|
||||||
|
expect(params['type'], '1,2');
|
||||||
|
});
|
||||||
|
|
||||||
test('collection kind has no Plex type number (filtered separately)', () {
|
test('collection kind has no Plex type number (filtered separately)', () {
|
||||||
final params = translator.toQueryParameters(const LibraryQuery(kind: MediaKind.collection));
|
final params = translator.toQueryParameters(const LibraryQuery(kind: MediaKind.collection));
|
||||||
expect(params, isNot(contains('type')));
|
expect(params, isNot(contains('type')));
|
||||||
@@ -98,6 +103,13 @@ void main() {
|
|||||||
expect(params['IncludeItemTypes'], 'Series');
|
expect(params['IncludeItemTypes'], 'Series');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('multiple kinds map to combined IncludeItemTypes', () {
|
||||||
|
final params = translator.toQueryParameters(
|
||||||
|
const LibraryQuery(kind: MediaKind.episode, includeKinds: [MediaKind.movie, MediaKind.show]),
|
||||||
|
);
|
||||||
|
expect(params['IncludeItemTypes'], 'Movie,Series');
|
||||||
|
});
|
||||||
|
|
||||||
test('collection kind maps to IncludeItemTypes=BoxSet', () {
|
test('collection kind maps to IncludeItemTypes=BoxSet', () {
|
||||||
final params = translator.toQueryParameters(const LibraryQuery(kind: MediaKind.collection));
|
final params = translator.toQueryParameters(const LibraryQuery(kind: MediaKind.collection));
|
||||||
expect(params['IncludeItemTypes'], 'BoxSet');
|
expect(params['IncludeItemTypes'], 'BoxSet');
|
||||||
|
|||||||
Reference in New Issue
Block a user