fix: make PlexMetadata type/key/title nullable for folder items

Plex API returns Directory entries without type field, causing
TypeError crash in fromJson. Made fields nullable and migrated
callers to use mediaType enum and displayTitle getter instead
of raw string access.
This commit is contained in:
edde746
2026-03-14 05:40:14 +01:00
parent 642c49b1ea
commit 4f736c555b
27 changed files with 134 additions and 140 deletions
+6 -6
View File
@@ -351,7 +351,7 @@ class DiscordRPCService {
timestamps: _buildTimestamps(),
statusDisplayType: DiscordStatusDisplayType.details,
largeAsset: _cachedThumbnailUrl != null
? DiscordAsset(url: _cachedThumbnailUrl!, text: metadata.grandparentTitle ?? metadata.title)
? DiscordAsset(url: _cachedThumbnailUrl!, text: metadata.grandparentTitle ?? metadata.title!)
: null,
),
);
@@ -392,14 +392,14 @@ class DiscordRPCService {
switch (metadata.mediaType) {
case PlexMediaType.movie:
final year = metadata.year != null ? ' (${metadata.year})' : '';
return metadata.title + year;
return metadata.title! + year;
case PlexMediaType.episode:
// Show: "Show Name" or just episode title if no show name
return metadata.grandparentTitle ?? metadata.title;
return metadata.grandparentTitle ?? metadata.title!;
default:
return metadata.title;
return metadata.title!;
}
}
@@ -411,9 +411,9 @@ class DiscordRPCService {
final season = metadata.parentIndex;
final episode = metadata.index;
if (season != null && episode != null) {
return 'S$season E$episode - ${metadata.title}';
return 'S$season E$episode - ${metadata.title!}';
}
return metadata.title;
return metadata.title!;
case PlexMediaType.movie:
return metadata.studio;
+18 -21
View File
@@ -341,7 +341,7 @@ class DownloadManagerService {
serverId: metadata.serverId!,
ratingKey: metadata.ratingKey,
globalKey: globalKey,
type: metadata.type,
type: metadata.type ?? '',
parentRatingKey: metadata.parentRatingKey,
grandparentRatingKey: metadata.grandparentRatingKey,
status: DownloadStatus.queued.index,
@@ -446,8 +446,8 @@ class DownloadManagerService {
// Build display name for notifications
final displayName = metadata.type == 'episode'
? '${metadata.grandparentTitle ?? metadata.title} - ${metadata.title}'
: metadata.title;
? '${metadata.grandparentTitle ?? metadata.displayTitle} - ${metadata.displayTitle}'
: metadata.displayTitle;
// Get WiFi-only setting for native enforcement
final settings = await SettingsService.getInstance();
@@ -1234,7 +1234,7 @@ class DownloadManagerService {
// Emit initial progress
_emitDeletionProgress(
DeletionProgress(globalKey: globalKey, itemTitle: metadata.title, currentItem: 0, totalItems: totalItems),
DeletionProgress(globalKey: globalKey, itemTitle: metadata.displayTitle, currentItem: 0, totalItems: totalItems),
);
// Delete files from storage (with progress updates)
@@ -1250,7 +1250,7 @@ class DownloadManagerService {
_emitDeletionProgress(
DeletionProgress(
globalKey: globalKey,
itemTitle: metadata.title,
itemTitle: metadata.displayTitle,
currentItem: totalItems,
totalItems: totalItems,
),
@@ -1264,17 +1264,14 @@ class DownloadManagerService {
/// Calculate total items to delete (for progress tracking)
Future<int> _getTotalItemsToDelete(PlexMetadata metadata, String _) async {
switch (metadata.type.toLowerCase()) {
case 'episode':
return 1; // Single episode
case 'movie':
return 1; // Single movie
case 'season':
// Count episodes in season
switch (metadata.mediaType) {
case PlexMediaType.episode:
case PlexMediaType.movie:
return 1;
case PlexMediaType.season:
final episodes = await _database.getEpisodesBySeason(metadata.ratingKey);
return episodes.length;
case 'show':
// Count all episodes in show
case PlexMediaType.show:
final episodes = await _database.getEpisodesByShow(metadata.ratingKey);
return episodes.length;
default:
@@ -1301,17 +1298,17 @@ class DownloadManagerService {
}
// Delete based on type
switch (metadata.type.toLowerCase()) {
case 'episode':
switch (metadata.mediaType) {
case PlexMediaType.episode:
await _deleteEpisodeFiles(metadata, serverId);
break;
case 'season':
case PlexMediaType.season:
await _deleteSeasonFiles(metadata, serverId);
break;
case 'show':
case PlexMediaType.show:
await _deleteShowFiles(metadata, serverId);
break;
case 'movie':
case PlexMediaType.movie:
await _deleteMovieFiles(metadata, serverId);
break;
default:
@@ -1447,7 +1444,7 @@ class DownloadManagerService {
episodes: episodesInSeason,
serverId: serverId,
parentKey: season.ratingKey,
parentTitle: season.title,
parentTitle: season.displayTitle,
);
final seasonDir = await _storageService.getSeasonDirectory(season, showYear: showYear);
@@ -1510,7 +1507,7 @@ class DownloadManagerService {
episodes: episodesInShow,
serverId: serverId,
parentKey: show.ratingKey,
parentTitle: show.title,
parentTitle: show.displayTitle,
);
final showDir = await _storageService.getShowDirectory(show);
+3 -3
View File
@@ -65,7 +65,7 @@ class DownloadStorageService {
String _formatEpisodeFileName(PlexMetadata episode) {
final season = padNumber(episode.parentIndex ?? 0, 2);
final ep = padNumber(episode.index ?? 0, 2);
final episodeName = _sanitizeFileName(episode.title);
final episodeName = _sanitizeFileName(episode.title!);
return 'S${season}E$ep - $episodeName';
}
@@ -246,13 +246,13 @@ class DownloadStorageService {
/// Get the folder name for a movie: "Movie Name (YYYY)"
String _getMovieFolderName(PlexMetadata movie) {
return _formatTitleWithYear(movie.title, movie.year);
return _formatTitleWithYear(movie.title!, movie.year);
}
/// Get the folder name for a TV show: "Show Name (YYYY)"
/// [showYear]: Pass explicitly for episodes (episode.year may differ from show's year)
String _getShowFolderName(PlexMetadata metadata, {int? showYear}) {
final title = metadata.grandparentTitle ?? metadata.title;
final title = metadata.grandparentTitle ?? metadata.title!;
final year = showYear ?? metadata.year;
return _formatTitleWithYear(title, year);
}
+1 -1
View File
@@ -53,7 +53,7 @@ class MediaControlsManager {
// Update OS media controls
await OsMediaControls.setMetadata(
MediaMetadata(
title: metadata.title,
title: metadata.title!,
artist: _buildArtist(metadata),
artworkUrl: artworkUrl,
duration: duration,
+2 -2
View File
@@ -134,7 +134,7 @@ class WatchNextService {
title = item.grandparentTitle!;
episodeTitle = item.title;
} else {
title = item.title;
title = item.title!;
episodeTitle = null;
}
@@ -148,7 +148,7 @@ class WatchNextService {
'episodeTitle': episodeTitle,
'description': item.summary,
'posterUri': posterUri,
'type': item.type.toLowerCase(),
'type': item.mediaType.name,
'duration': item.duration ?? 0,
'lastPlaybackPosition': item.viewOffset ?? 0,
'lastEngagementTime': lastEngagementTime,