diff --git a/MediaBrowser.Controller/LiveTv/ProgramInfo.cs b/MediaBrowser.Controller/LiveTv/ProgramInfo.cs index 36f082b02c..3f8fc639f1 100644 --- a/MediaBrowser.Controller/LiveTv/ProgramInfo.cs +++ b/MediaBrowser.Controller/LiveTv/ProgramInfo.cs @@ -150,7 +150,22 @@ namespace MediaBrowser.Controller.LiveTv /// /// The production year. public int? ProductionYear { get; set; } - + /// + /// Gets or sets the home page URL. + /// + /// The home page URL. + public string HomePageUrl { get; set; } + /// + /// Gets or sets the series identifier. + /// + /// The series identifier. + public string SeriesId { get; set; } + /// + /// Gets or sets the show identifier. + /// + /// The show identifier. + public string ShowId { get; set; } + public ProgramInfo() { Genres = new List(); diff --git a/MediaBrowser.Controller/LiveTv/RecordingInfo.cs b/MediaBrowser.Controller/LiveTv/RecordingInfo.cs index e5817d390d..42064cf50d 100644 --- a/MediaBrowser.Controller/LiveTv/RecordingInfo.cs +++ b/MediaBrowser.Controller/LiveTv/RecordingInfo.cs @@ -185,7 +185,11 @@ namespace MediaBrowser.Controller.LiveTv /// /// null if [has image] contains no value, true if [has image]; otherwise, false. public bool? HasImage { get; set; } - + /// + /// Gets or sets the show identifier. + /// + /// The show identifier. + public string ShowId { get; set; } public RecordingInfo() { diff --git a/MediaBrowser.Controller/LiveTv/SeriesTimerInfo.cs b/MediaBrowser.Controller/LiveTv/SeriesTimerInfo.cs index 1be6549ff8..2d79473f00 100644 --- a/MediaBrowser.Controller/LiveTv/SeriesTimerInfo.cs +++ b/MediaBrowser.Controller/LiveTv/SeriesTimerInfo.cs @@ -94,6 +94,12 @@ namespace MediaBrowser.Controller.LiveTv /// /// true if this instance is post padding required; otherwise, false. public bool IsPostPaddingRequired { get; set; } + + /// + /// Gets or sets the series identifier. + /// + /// The series identifier. + public string SeriesId { get; set; } public SeriesTimerInfo() { diff --git a/MediaBrowser.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs b/MediaBrowser.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs index c90b9eacb8..cf294997b7 100644 --- a/MediaBrowser.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs +++ b/MediaBrowser.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs @@ -295,6 +295,13 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV RecordAnyTime = false, RecordNewOnly = false }; + + if (program != null) + { + defaults.SeriesId = program.SeriesId; + defaults.ProgramId = program.Id; + } + return Task.FromResult(defaults); } @@ -515,8 +522,9 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV OriginalAirDate = info.OriginalAirDate, Status = RecordingStatus.Scheduled, Overview = info.Overview, - SeriesTimerId = info.Id.Substring(0, 10), - TimerId = timer.Id + SeriesTimerId = timer.SeriesTimerId, + TimerId = timer.Id, + ShowId = info.ShowId }; _recordingProvider.Add(recording); } @@ -624,7 +632,9 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV { allPrograms = GetProgramsForSeries(seriesTimer, allPrograms); - allPrograms = allPrograms.Where(epg => currentRecordings.All(r => r.ProgramId.Substring(0, 14) != epg.Id.Substring(0, 14))); //filtered recordings already running + var recordingShowIds = currentRecordings.Select(i => i.ShowId).ToList(); + + allPrograms = allPrograms.Where(epg => !recordingShowIds.Contains(epg.ShowId, StringComparer.OrdinalIgnoreCase)); return allPrograms.Select(i => RecordingHelper.CreateTimer(i, seriesTimer)); } @@ -638,7 +648,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV if (seriesTimer.RecordNewOnly) { - allPrograms = allPrograms.Where(epg => !epg.IsRepeat); //Filtered by New only + allPrograms = allPrograms.Where(epg => !epg.IsRepeat); } if (!seriesTimer.RecordAnyChannel) @@ -648,8 +658,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV allPrograms = allPrograms.Where(epg => seriesTimer.Days.Contains(epg.StartDate.DayOfWeek)); - // TODO: This assumption will require review once additional listing providers are added - return allPrograms.Where(epg => epg.Id.StartsWith(seriesTimer.ProgramId, StringComparison.OrdinalIgnoreCase)); + return allPrograms.Where(epg => string.Equals(epg.SeriesId, seriesTimer.SeriesId, StringComparison.OrdinalIgnoreCase)); } private string GetChannelEpgCachePath(string channelId) diff --git a/MediaBrowser.Server.Implementations/LiveTv/Listings/Emby/EmbyListingsNorthAmerica.cs b/MediaBrowser.Server.Implementations/LiveTv/Listings/Emby/EmbyListingsNorthAmerica.cs index 641505e381..6c1cc5506f 100644 --- a/MediaBrowser.Server.Implementations/LiveTv/Listings/Emby/EmbyListingsNorthAmerica.cs +++ b/MediaBrowser.Server.Implementations/LiveTv/Listings/Emby/EmbyListingsNorthAmerica.cs @@ -26,6 +26,8 @@ namespace MediaBrowser.Server.Implementations.LiveTv.Listings.Emby public async Task> GetProgramsAsync(ListingsProviderInfo info, string channelNumber, DateTime startDateUtc, DateTime endDateUtc, CancellationToken cancellationToken) { + channelNumber = NormalizeNumber(channelNumber); + var url = "https://data.emby.media/service/listings?id=" + info.ListingsId; // Normalize @@ -36,8 +38,100 @@ namespace MediaBrowser.Server.Implementations.LiveTv.Listings.Emby url += "&end=" + endDateUtc.ToString("s", CultureInfo.InvariantCulture) + "Z"; var response = await GetResponse(url).ConfigureAwait(false); - - return new List(); + + return response.Where(i => IncludeInResults(i, channelNumber)).Select(GetProgramInfo); + } + + private ProgramInfo GetProgramInfo(ListingInfo info) + { + var showType = info.showType ?? string.Empty; + + var program = new ProgramInfo + { + Id = info.listingID.ToString(CultureInfo.InvariantCulture), + Name = GetStringValue(info.showName), + EpisodeTitle = GetStringValue(info.episodeTitle), + HomePageUrl = GetStringValue(info.webLink), + Overview = info.description, + IsHD = info.hd, + IsLive = info.live, + IsPremiere = info.seasonPremiere || info.seriesPremiere, + IsMovie = showType.IndexOf("Movie", StringComparison.OrdinalIgnoreCase) != -1, + IsKids = showType.IndexOf("Children", StringComparison.OrdinalIgnoreCase) != -1, + IsNews = showType.IndexOf("News", StringComparison.OrdinalIgnoreCase) != -1, + IsSports = showType.IndexOf("Sports", StringComparison.OrdinalIgnoreCase) != -1 + }; + + if (!string.IsNullOrWhiteSpace(info.listDateTime)) + { + program.StartDate = DateTime.ParseExact(info.listDateTime, "yyyy'-'MM'-'dd' 'HH':'mm':'ss", CultureInfo.InvariantCulture); + program.EndDate = program.StartDate.AddMinutes(info.duration); + } + + if (info.starRating > 0) + { + program.CommunityRating = info.starRating*2; + } + + if (!string.IsNullOrWhiteSpace(info.rating)) + { + // They don't have dashes so try to normalize + program.OfficialRating = info.rating.Replace("TV", "TV-").Replace("--", "-"); + } + + if (!string.IsNullOrWhiteSpace(info.year)) + { + program.ProductionYear = int.Parse(info.year, CultureInfo.InvariantCulture); + } + + if (info.showID > 0) + { + program.ShowId = info.showID.ToString(CultureInfo.InvariantCulture); + } + + if (info.seriesID > 0) + { + program.SeriesId = info.seriesID.ToString(CultureInfo.InvariantCulture); + program.IsSeries = true; + program.IsRepeat = info.repeat; + } + + if (info.starRating > 0) + { + program.CommunityRating = info.starRating * 2; + } + + if (string.Equals(info.showName, "Movie", StringComparison.OrdinalIgnoreCase)) + { + // Sometimes the movie title will be in here + if (!string.IsNullOrWhiteSpace(info.episodeTitle)) + { + program.Name = info.episodeTitle; + } + } + + return program; + } + + private string GetStringValue(string s) + { + return string.IsNullOrWhiteSpace(s) ? null : s; + } + + private bool IncludeInResults(ListingInfo info, string itemNumber) + { + if (string.Equals(itemNumber, NormalizeNumber(info.number), StringComparison.OrdinalIgnoreCase)) + { + return true; + } + + var channelNumber = info.channelNumber.ToString(CultureInfo.InvariantCulture); + if (info.subChannelNumber > 0) + { + channelNumber += "." + info.subChannelNumber.ToString(CultureInfo.InvariantCulture); + } + + return string.Equals(channelNumber, itemNumber, StringComparison.OrdinalIgnoreCase); } public async Task AddMetadata(ListingsProviderInfo info, List channels, CancellationToken cancellationToken) @@ -48,18 +142,25 @@ namespace MediaBrowser.Server.Implementations.LiveTv.Listings.Emby { var station = response.stations.FirstOrDefault(i => { + var itemNumber = NormalizeNumber(channel.Number); + + if (string.Equals(itemNumber, NormalizeNumber(i.number), StringComparison.OrdinalIgnoreCase)) + { + return true; + } + var channelNumber = i.channelNumber.ToString(CultureInfo.InvariantCulture); if (i.subChannelNumber > 0) { channelNumber += "." + i.subChannelNumber.ToString(CultureInfo.InvariantCulture); } - return string.Equals(channelNumber, channel.Number, StringComparison.OrdinalIgnoreCase); + return string.Equals(channelNumber, itemNumber, StringComparison.OrdinalIgnoreCase); }); if (station != null) { - channel.Name = station.name; + //channel.Name = station.name; if (!string.IsNullOrWhiteSpace(station.logoFilename)) { @@ -70,6 +171,11 @@ namespace MediaBrowser.Server.Implementations.LiveTv.Listings.Emby } } + private string NormalizeNumber(string number) + { + return number.Replace('-', '.'); + } + public Task Validate(ListingsProviderInfo info, bool validateLogin, bool validateListings) { return Task.FromResult(true); @@ -108,7 +214,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv.Listings.Emby return name; } - private async Task GetResponse(string url) + private async Task GetResponse(string url, Func filter = null) where T : class { using (var stream = await _httpClient.Get(new HttpRequestOptions @@ -131,12 +237,27 @@ namespace MediaBrowser.Server.Implementations.LiveTv.Listings.Emby }).ConfigureAwait(false)) { - return _jsonSerializer.DeserializeFromStream(secondStream); + return ParseResponse(secondStream, filter); } } } } + private T ParseResponse(Stream response, Func filter) + { + using (var reader = new StreamReader(response)) + { + var json = reader.ReadToEnd(); + + if (filter != null) + { + json = filter(json); + } + + return _jsonSerializer.DeserializeFromString(json); + } + } + private class LineupInfo { public string lineupID { get; set; } diff --git a/MediaBrowser.Server.Implementations/LiveTv/LiveTvManager.cs b/MediaBrowser.Server.Implementations/LiveTv/LiveTvManager.cs index 02ca7f0f6d..331f421851 100644 --- a/MediaBrowser.Server.Implementations/LiveTv/LiveTvManager.cs +++ b/MediaBrowser.Server.Implementations/LiveTv/LiveTvManager.cs @@ -622,6 +622,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv item.ProviderImageUrl = info.ImageUrl; item.RunTimeTicks = (info.EndDate - info.StartDate).Ticks; item.StartDate = info.StartDate; + item.HomePageUrl = info.HomePageUrl; item.ProductionYear = info.ProductionYear; item.PremiereDate = item.PremiereDate ?? info.OriginalAirDate;