diff --git a/CHANGELOG.md b/CHANGELOG.md index 21b403f16..174c30889 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). ## [Unreleased] ### Added - Add artists directly to schedules +- Include MPAA and VCHIP content ratings in XMLTV guide data ### Fixed - Ignore unsupported plex guids (this prevented some libraries from scanning correctly) diff --git a/ErsatzTV.Core/Iptv/ChannelGuide.cs b/ErsatzTV.Core/Iptv/ChannelGuide.cs index cfb053d4d..f0a737a1f 100644 --- a/ErsatzTV.Core/Iptv/ChannelGuide.cs +++ b/ErsatzTV.Core/Iptv/ChannelGuide.cs @@ -1,4 +1,5 @@ -using System.Collections.Generic; +using System; +using System.Collections.Generic; using System.IO; using System.Linq; using System.Text; @@ -8,6 +9,7 @@ using ErsatzTV.Core.Emby; using ErsatzTV.Core.Jellyfin; using LanguageExt; using LanguageExt.UnsafeValueAccess; +using Serilog; using static LanguageExt.Prelude; namespace ErsatzTV.Core.Iptv @@ -94,7 +96,7 @@ namespace ErsatzTV.Core.Iptv string title = GetTitle(startItem); string subtitle = GetSubtitle(startItem); string description = GetDescription(startItem); - string contentRating = string.Empty; + Option contentRating = GetContentRating(startItem); xml.WriteStartElement("programme"); xml.WriteAttributeString("start", start); @@ -210,12 +212,12 @@ namespace ErsatzTV.Core.Iptv } } - if (!string.IsNullOrWhiteSpace(contentRating)) + foreach (ContentRating rating in contentRating) { xml.WriteStartElement("rating"); - xml.WriteAttributeString("system", "MPAA"); + xml.WriteAttributeString("system", rating.System); xml.WriteStartElement("value"); - xml.WriteString(contentRating); + xml.WriteString(rating.Value); xml.WriteEndElement(); // value xml.WriteEndElement(); // rating } @@ -322,5 +324,45 @@ namespace ErsatzTV.Core.Iptv _ => string.Empty }; } + + private static Option GetContentRating(PlayoutItem playoutItem) + { + try + { + return playoutItem.MediaItem switch + { + Movie m => m.MovieMetadata + .HeadOrNone() + .Match(mm => ParseContentRating(mm.ContentRating, "MPAA"), () => None), + Episode e => e.Season.Show.ShowMetadata + .HeadOrNone() + .Match(sm => ParseContentRating(sm.ContentRating, "VCHIP"), () => None), + _ => None + }; + } + catch (Exception ex) + { + Log.Logger.Warning(ex, "Failed to get content rating for playout item {Item}", GetTitle(playoutItem)); + return None; + } + } + + private static Option ParseContentRating(string contentRating, string system) + { + Option maybeFirst = contentRating.Split('/').HeadOrNone(); + return maybeFirst.Map>( + first => + { + string[] split = first.Split(':'); + if (split.Length == 2 && split[0].ToLowerInvariant() == "us") + { + return new ContentRating(system, split[1].ToUpperInvariant()); + } + + return None; + }).Flatten(); + } + + private record ContentRating(string System, string Value); } }