From 82b0de922396dcaf4cc44c77bbf8176355960d07 Mon Sep 17 00:00:00 2001 From: edde746 <86283021+edde746@users.noreply.github.com> Date: Mon, 9 Mar 2026 16:16:37 +0100 Subject: [PATCH] feat: display movie edition titles across all views --- lib/models/plex_metadata.dart | 4 ++++ lib/models/plex_metadata.g.dart | 2 ++ lib/screens/media_detail_screen.dart | 1 + lib/screens/playlist/playlist_item_card.dart | 8 ++++++-- lib/widgets/media_card.dart | 9 ++++++++- lib/widgets/video_controls/sheets/queue_sheet.dart | 2 +- lib/widgets/video_controls/widgets/content_strip.dart | 2 +- 7 files changed, 23 insertions(+), 5 deletions(-) diff --git a/lib/models/plex_metadata.dart b/lib/models/plex_metadata.dart index e929b1e7..872193ec 100644 --- a/lib/models/plex_metadata.dart +++ b/lib/models/plex_metadata.dart @@ -99,6 +99,7 @@ class PlexMetadata with MultiServerFields { final String? audienceRatingImage; // Audience rating source URI final String? tagline; final String? originalTitle; + final String? editionTitle; // Edition name for movies (e.g., "Director's Cut", "Extended") final String? subtype; // Clip subtype: "trailer", "behindTheScenes", "deleted", etc. final int? extraType; // Numeric extra type identifier final String? primaryExtraKey; // Points to main trailer (e.g., "/library/metadata/52601") @@ -186,6 +187,7 @@ class PlexMetadata with MultiServerFields { this.audienceRatingImage, this.tagline, this.originalTitle, + this.editionTitle, this.subtype, this.extraType, this.primaryExtraKey, @@ -244,6 +246,7 @@ class PlexMetadata with MultiServerFields { String? audienceRatingImage, String? tagline, String? originalTitle, + String? editionTitle, String? subtype, int? extraType, String? primaryExtraKey, @@ -300,6 +303,7 @@ class PlexMetadata with MultiServerFields { audienceRatingImage: audienceRatingImage ?? this.audienceRatingImage, tagline: tagline ?? this.tagline, originalTitle: originalTitle ?? this.originalTitle, + editionTitle: editionTitle ?? this.editionTitle, subtype: subtype ?? this.subtype, extraType: extraType ?? this.extraType, primaryExtraKey: primaryExtraKey ?? this.primaryExtraKey, diff --git a/lib/models/plex_metadata.g.dart b/lib/models/plex_metadata.g.dart index 097c8f7e..d6d1406f 100644 --- a/lib/models/plex_metadata.g.dart +++ b/lib/models/plex_metadata.g.dart @@ -54,6 +54,7 @@ PlexMetadata _$PlexMetadataFromJson(Map json) => PlexMetadata( audienceRatingImage: json['audienceRatingImage'] as String?, tagline: json['tagline'] as String?, originalTitle: json['originalTitle'] as String?, + editionTitle: json['editionTitle'] as String?, subtype: json['subtype'] as String?, extraType: (json['extraType'] as num?)?.toInt(), primaryExtraKey: json['primaryExtraKey'] as String?, @@ -109,6 +110,7 @@ Map _$PlexMetadataToJson(PlexMetadata instance) => runSpacing: 8, children: [ if (metadata.year != null) _buildMetadataChip('${metadata.year}'), + if (metadata.editionTitle != null) _buildMetadataChip(metadata.editionTitle!), if (metadata.contentRating != null) _buildMetadataChip(formatContentRating(metadata.contentRating!)), if (metadata.duration != null) diff --git a/lib/screens/playlist/playlist_item_card.dart b/lib/screens/playlist/playlist_item_card.dart index fdadd599..c10d61c0 100644 --- a/lib/screens/playlist/playlist_item_card.dart +++ b/lib/screens/playlist/playlist_item_card.dart @@ -248,8 +248,12 @@ class _PlaylistItemCardState extends State { } return widget.item.displaySubtitle ?? t.discover.tvShow; } else if (itemType == 'movie') { - // For movies, show year - return widget.item.year?.toString() ?? t.discover.movie; + // For movies, show year and edition + final year = widget.item.year?.toString(); + if (year != null && widget.item.editionTitle != null) { + return '$year · ${widget.item.editionTitle}'; + } + return year ?? t.discover.movie; } // Default to type diff --git a/lib/widgets/media_card.dart b/lib/widgets/media_card.dart index e7a92fe8..16c45437 100644 --- a/lib/widgets/media_card.dart +++ b/lib/widgets/media_card.dart @@ -455,6 +455,11 @@ class _MediaCardList extends StatelessWidget { parts.add('${metadata.year}'); } + // Add edition title + if (metadata.editionTitle != null) { + parts.add(metadata.editionTitle!); + } + // Add duration if (metadata.duration != null) { parts.add(formatDurationTextual(metadata.duration!)); @@ -749,7 +754,9 @@ class _MediaCardHelpers { ); } else if (metadata.year != null) { return Text( - '${metadata.year}', + metadata.editionTitle != null ? '${metadata.year} · ${metadata.editionTitle}' : '${metadata.year}', + maxLines: 1, + overflow: TextOverflow.ellipsis, style: Theme.of( context, ).textTheme.bodySmall?.copyWith(color: tokens(context).textMuted, fontSize: 11, height: 1.1), diff --git a/lib/widgets/video_controls/sheets/queue_sheet.dart b/lib/widgets/video_controls/sheets/queue_sheet.dart index 4bcbaabe..972726b2 100644 --- a/lib/widgets/video_controls/sheets/queue_sheet.dart +++ b/lib/widgets/video_controls/sheets/queue_sheet.dart @@ -137,7 +137,7 @@ class QueueSheet extends StatelessWidget { return item.grandparentTitle!; } if (item.year != null) { - return '${item.year}'; + return item.editionTitle != null ? '${item.year} · ${item.editionTitle}' : '${item.year}'; } return item.type; } diff --git a/lib/widgets/video_controls/widgets/content_strip.dart b/lib/widgets/video_controls/widgets/content_strip.dart index 8a433234..830eb296 100644 --- a/lib/widgets/video_controls/widgets/content_strip.dart +++ b/lib/widgets/video_controls/widgets/content_strip.dart @@ -510,7 +510,7 @@ class ContentStripState extends State { return '${item.grandparentTitle} \u00b7 S${item.parentIndex}E${item.index}'; } if (item.grandparentTitle != null) return item.grandparentTitle!; - if (item.year != null) return '${item.year}'; + if (item.year != null) return item.editionTitle != null ? '${item.year} · ${item.editionTitle}' : '${item.year}'; return item.type; }