fix(runtime): harden application service boundaries

This commit is contained in:
edde746
2026-07-24 03:46:46 +02:00
parent 658da37b48
commit e0bf66eea8
309 changed files with 32574 additions and 4369 deletions
@@ -42,49 +42,44 @@ mixin _JellyfinPlaylistMethods on MediaServerCacheMixin {
final offset = start ?? 0;
final pageSize = size ?? _playlistsPageSize;
final requestedType = playlistType.toLowerCase();
final items = <MediaPlaylist>[];
var rawOffset = 0;
var filteredSeen = 0;
int? rawTotal;
var rawFinished = false;
while (items.length < pageSize && !rawFinished) {
final response = await _http.get(
'/Items',
queryParameters: {
'userId': connection.userId,
'IncludeItemTypes': 'Playlist',
'Recursive': 'true',
'StartIndex': rawOffset.toString(),
'Limit': pageSize.toString(),
'Fields': 'Overview,DateCreated,DateLastSaved,ChildCount,Tags',
...jellyfinImageQueryParameters,
},
abort: abort,
);
throwIfHttpError(response);
final rawItems = _itemsArray(response.data);
final rawTotalValue = response.data is Map<String, dynamic>
? (response.data as Map<String, dynamic>)['TotalRecordCount']
: null;
if (rawTotalValue is int) rawTotal = rawTotalValue;
for (final item in rawItems.map(_playlistFromJson)) {
if (!_matchesPlaylistFilters(item, requestedType: requestedType, smart: smart)) continue;
if (filteredSeen >= offset && items.length < pageSize) {
items.add(item);
}
filteredSeen++;
}
rawOffset += rawItems.length;
rawFinished = rawItems.isEmpty || rawItems.length < pageSize || (rawTotal != null && rawOffset >= rawTotal);
final mediaType = switch (requestedType) {
'' => null,
'video' => 'Video',
'audio' => 'Audio',
'photo' => 'Photo',
'book' => 'Book',
'unknown' => 'Unknown',
_ => '',
};
if (mediaType == '') {
return LibraryPage<MediaPlaylist>(items: const [], totalCount: 0, offset: offset);
}
final fallbackTotal = rawFinished
? filteredSeen
: fallbackPageTotal(offset: offset, itemCount: items.length, requestedSize: pageSize);
return LibraryPage<MediaPlaylist>(items: items, totalCount: fallbackTotal, offset: offset);
final response = await _http.get(
'/Items',
queryParameters: {
'userId': connection.userId,
'IncludeItemTypes': 'Playlist',
'Recursive': 'true',
'MediaTypes': ?mediaType,
'StartIndex': offset.toString(),
'Limit': pageSize.toString(),
'Fields': 'Overview,DateCreated,DateLastSaved,ChildCount,Tags',
...jellyfinImageQueryParameters,
},
abort: abort,
);
throwIfHttpError(response);
final items = _itemsArray(response.data).map(_playlistFromJson).toList();
final rawTotal = response.data is Map<String, dynamic>
? (response.data as Map<String, dynamic>)['TotalRecordCount']
: null;
final fallbackTotal = fallbackPageTotal(offset: offset, itemCount: items.length, requestedSize: pageSize);
return LibraryPage<MediaPlaylist>(
items: items,
totalCount: rawTotal is int ? rawTotal : fallbackTotal,
offset: offset,
);
}
@override
@@ -201,7 +196,7 @@ mixin _JellyfinPlaylistMethods on MediaServerCacheMixin {
return false;
}
if (item.playlistItemId == null) {
appLogger.e('movePlaylistItem: item ${item.id} ("${item.title}") has no playlistItemId');
appLogger.e('Jellyfin movePlaylistItem failed: missing playlist entry ID');
return false;
}
final response = await _http.post(
@@ -218,7 +213,7 @@ mixin _JellyfinPlaylistMethods on MediaServerCacheMixin {
return false;
}
if (item.playlistItemId == null) {
appLogger.e('removeFromPlaylist: item ${item.id} ("${item.title}") has no playlistItemId');
appLogger.e('Jellyfin removeFromPlaylist failed: missing playlist entry ID');
return false;
}
final response = await _http.delete(
@@ -253,12 +248,6 @@ mixin _JellyfinPlaylistMethods on MediaServerCacheMixin {
return 'video';
}
bool _matchesPlaylistFilters(MediaPlaylist playlist, {required String requestedType, required bool? smart}) {
if (requestedType.isNotEmpty && playlist.playlistType.toLowerCase() != requestedType) return false;
if (smart != null && playlist.smart != smart) return false;
return true;
}
String? _imageTagPath(String id, Object? tags) {
if (tags is! Map<String, dynamic>) return null;
final tag = tags['Primary'];