diff --git a/MediaBrowser.Plugins.NextPvr/LiveTvService.cs b/MediaBrowser.Plugins.NextPvr/LiveTvService.cs index fbc1751..a36919e 100644 --- a/MediaBrowser.Plugins.NextPvr/LiveTvService.cs +++ b/MediaBrowser.Plugins.NextPvr/LiveTvService.cs @@ -39,6 +39,8 @@ namespace MediaBrowser.Plugins.NextPvr private ICryptoProvider _cryptoProvider; private IFileSystem _fileSystem; + public DateTime LastRecordingChange = DateTime.MinValue; + public LiveTvService(IHttpClient httpClient, IJsonSerializer jsonSerializer, ILogger logger, ICryptoProvider cryptoProvider, IFileSystem fileSystem) { _httpClient = httpClient; @@ -246,6 +248,8 @@ namespace MediaBrowser.Plugins.NextPvr using (var stream = await _httpClient.Get(options).ConfigureAwait(false)) { + LastRecordingChange = DateTime.UtcNow; + bool? error = new CancelDeleteRecordingResponse().RecordingError(stream, _jsonSerializer, _logger); if (error == null || error == true) @@ -287,6 +291,7 @@ namespace MediaBrowser.Plugins.NextPvr using (var stream = await _httpClient.Get(options).ConfigureAwait(false)) { + LastRecordingChange = DateTime.UtcNow; bool? error = new CancelDeleteRecordingResponse().RecordingError(stream, _jsonSerializer, _logger); if (error == null || error == true) @@ -606,9 +611,11 @@ namespace MediaBrowser.Plugins.NextPvr try { await _httpClient.Post(options).ConfigureAwait((false)); + LastRecordingChange = DateTime.UtcNow; } catch (HttpException ex) { + LastRecordingChange = DateTime.UtcNow; _logger.Error(string.Format("[NextPvr] UpdateTimer Async with exception: {0}", ex.Message)); throw new LiveTvConflictException(); } @@ -728,7 +735,8 @@ namespace MediaBrowser.Plugins.NextPvr } }, - Container = "mpegts" + Container = "mpegts", + SupportsProbing = false }; } } @@ -757,7 +765,8 @@ namespace MediaBrowser.Plugins.NextPvr } }, - Container = "mpegts" + Container = "mpegts", + SupportsProbing = false }; } diff --git a/MediaBrowser.Plugins.NextPvr/MediaBrowser.Plugins.NextPvr.csproj b/MediaBrowser.Plugins.NextPvr/MediaBrowser.Plugins.NextPvr.csproj index f8b8d23..4eb2fd0 100644 --- a/MediaBrowser.Plugins.NextPvr/MediaBrowser.Plugins.NextPvr.csproj +++ b/MediaBrowser.Plugins.NextPvr/MediaBrowser.Plugins.NextPvr.csproj @@ -2,8 +2,8 @@ netstandard1.3; - 3.1.8.0 - 3.1.8.0 + 3.1.9.0 + 3.1.9.0 @@ -15,7 +15,7 @@ - + diff --git a/MediaBrowser.Plugins.NextPvr/RecordingsChannel.cs b/MediaBrowser.Plugins.NextPvr/RecordingsChannel.cs new file mode 100644 index 0000000..9f28e69 --- /dev/null +++ b/MediaBrowser.Plugins.NextPvr/RecordingsChannel.cs @@ -0,0 +1,342 @@ +using System; +using System.Collections.Generic; +using System.Text; +using System.Threading; +using System.Threading.Tasks; +using MediaBrowser.Controller.Channels; +using MediaBrowser.Controller.Providers; +using MediaBrowser.Model.Entities; +using MediaBrowser.Model.Channels; +using MediaBrowser.Model.MediaInfo; +using MediaBrowser.Controller.LiveTv; +using System.Linq; +using MediaBrowser.Common.Extensions; +using MediaBrowser.Model.Dto; +using System.Globalization; +using MediaBrowser.Controller.Entities; + +namespace MediaBrowser.Plugins.NextPvr +{ + public class RecordingsChannel : IChannel, IIndexableChannel, IHasCacheKey, ISupportsDelete + { + public ILiveTvManager _liveTvManager; + + public RecordingsChannel(ILiveTvManager liveTvManager) + { + _liveTvManager = liveTvManager; + } + + public string Name + { + get + { + return "Next Pvr Recordings"; + } + } + + public string Description + { + get + { + return "Next Pvr Recordings"; + } + } + + public string DataVersion + { + get + { + return "1"; + } + } + + public string HomePageUrl + { + get { return "http://www.nextpvr.com/"; } + } + + public ChannelParentalRating ParentalRating + { + get { return ChannelParentalRating.GeneralAudience; } + } + + public string GetCacheKey(string userId) + { + var now = DateTime.UtcNow; + + var values = new List(); + + values.Add(now.DayOfYear.ToString(CultureInfo.InvariantCulture)); + values.Add(now.Hour.ToString(CultureInfo.InvariantCulture)); + + double minute = now.Minute; + minute /= 5; + + values.Add(Math.Floor(minute).ToString(CultureInfo.InvariantCulture)); + + values.Add(GetService().LastRecordingChange.Ticks.ToString(CultureInfo.InvariantCulture)); + + return string.Join("-", values.ToArray()); + } + + public InternalChannelFeatures GetChannelFeatures() + { + return new InternalChannelFeatures + { + ContentTypes = new List + { + ChannelMediaContentType.Movie, + ChannelMediaContentType.Episode, + ChannelMediaContentType.Clip + }, + MediaTypes = new List + { + ChannelMediaType.Audio, + ChannelMediaType.Video + }, + SupportsContentDownloading = true + }; + } + + public Task GetChannelImage(ImageType type, CancellationToken cancellationToken) + { + if (type == ImageType.Primary) + { + return Task.FromResult(new DynamicImageResponse + { + Path = "https://raw.githubusercontent.com/MediaBrowser/MediaBrowser.Resources/master/images/catalog/nextpvr.png", + Protocol = MediaProtocol.Http, + HasImage = true + }); + } + + return Task.FromResult(new DynamicImageResponse + { + HasImage = false + }); + } + + public IEnumerable GetSupportedChannelImages() + { + return new List + { + ImageType.Primary + }; + } + + public bool IsEnabledFor(string userId) + { + return true; + } + + private LiveTvService GetService() + { + return _liveTvManager.Services.OfType().First(); + } + + public bool CanDelete(BaseItem item) + { + return !item.IsFolder; + } + + public Task DeleteItem(string id, CancellationToken cancellationToken) + { + return GetService().DeleteRecordingAsync(id, cancellationToken); + } + + public Task GetAllMedia(InternalAllChannelMediaQuery query, CancellationToken cancellationToken) + { + return GetChannelItems(new InternalChannelItemQuery(), i => true, cancellationToken); + } + + public Task GetChannelItems(InternalChannelItemQuery query, CancellationToken cancellationToken) + { + if (string.IsNullOrWhiteSpace(query.FolderId)) + { + return GetRecordingGroups(query, cancellationToken); + } + + if (query.FolderId.StartsWith("series_", StringComparison.OrdinalIgnoreCase)) + { + var hash = query.FolderId.Split('_')[1]; + return GetChannelItems(query, i => i.IsSeries && string.Equals(i.Name.GetMD5().ToString("N"), hash, StringComparison.Ordinal), cancellationToken); + } + + if (string.Equals(query.FolderId, "kids", StringComparison.OrdinalIgnoreCase)) + { + return GetChannelItems(query, i => i.IsKids, cancellationToken); + } + + if (string.Equals(query.FolderId, "movies", StringComparison.OrdinalIgnoreCase)) + { + return GetChannelItems(query, i => i.IsMovie, cancellationToken); + } + + if (string.Equals(query.FolderId, "news", StringComparison.OrdinalIgnoreCase)) + { + return GetChannelItems(query, i => i.IsNews, cancellationToken); + } + + if (string.Equals(query.FolderId, "sports", StringComparison.OrdinalIgnoreCase)) + { + return GetChannelItems(query, i => i.IsSports, cancellationToken); + } + + if (string.Equals(query.FolderId, "others", StringComparison.OrdinalIgnoreCase)) + { + return GetChannelItems(query, i => !i.IsSports && !i.IsNews && !i.IsMovie && !i.IsKids && !i.IsSeries, cancellationToken); + } + + var result = new ChannelItemResult() + { + Items = new List() + }; + + return Task.FromResult(result); + } + + public async Task GetChannelItems(InternalChannelItemQuery query, Func filter, CancellationToken cancellationToken) + { + var service = GetService(); + var allRecordings = await service.GetRecordingsAsync(cancellationToken).ConfigureAwait(false); + + var result = new ChannelItemResult() + { + Items = new List() + }; + + result.Items.AddRange(allRecordings.Where(filter).Select(ConvertToChannelItem)); + + return result; + } + + private ChannelItemInfo ConvertToChannelItem(RecordingInfo item) + { + var channelItem = new ChannelItemInfo + { + Name = string.IsNullOrEmpty(item.EpisodeTitle) ? item.Name : item.EpisodeTitle, + SeriesName = !string.IsNullOrEmpty(item.EpisodeTitle) || item.IsSeries ? item.Name : null, + OfficialRating = item.OfficialRating, + CommunityRating = item.CommunityRating, + ContentType = item.IsMovie ? ChannelMediaContentType.Movie : (item.IsSeries ? ChannelMediaContentType.Episode : ChannelMediaContentType.Clip), + Genres = item.Genres, + ImageUrl = item.ImageUrl, + //HomePageUrl = item.HomePageUrl + Id = item.Id, + //IndexNumber = item.IndexNumber, + MediaType = item.ChannelType == Model.LiveTv.ChannelType.TV ? ChannelMediaType.Video : ChannelMediaType.Audio, + MediaSources = new List + { + new MediaSourceInfo + { + Path = item.Path, + Protocol = item.Path.StartsWith("http", StringComparison.OrdinalIgnoreCase) ? MediaProtocol.Http : MediaProtocol.File, + Id = item.Id + } + }, + //ParentIndexNumber = item.ParentIndexNumber, + PremiereDate = item.OriginalAirDate, + //ProductionYear = item.ProductionYear, + //Studios = item.Studios, + Type = ChannelItemType.Media, + DateModified = item.DateLastUpdated, + Overview = item.Overview, + //People = item.People + }; + + return channelItem; + } + + private async Task GetRecordingGroups(InternalChannelItemQuery query, CancellationToken cancellationToken) + { + var service = GetService(); + + var allRecordings = await service.GetRecordingsAsync(cancellationToken).ConfigureAwait(false); + var result = new ChannelItemResult() + { + Items = new List() + }; + + var series = allRecordings + .Where(i => i.IsSeries) + .ToLookup(i => i.Name, StringComparer.OrdinalIgnoreCase); + + result.Items.AddRange(series.OrderBy(i => i.Key).Select(i => new ChannelItemInfo + { + Name = i.Key, + FolderType = ChannelFolderType.Container, + Id = "series_" + i.Key.GetMD5().ToString("N"), + Type = ChannelItemType.Folder, + ImageUrl = i.First().ImageUrl + })); + + var kids = allRecordings.FirstOrDefault(i => i.IsKids); + + if (kids != null) + { + result.Items.Add(new ChannelItemInfo + { + Name = "Kids", + FolderType = ChannelFolderType.Container, + Id = "kids", + Type = ChannelItemType.Folder, + ImageUrl = kids.ImageUrl + }); + } + + var movies = allRecordings.FirstOrDefault(i => i.IsMovie); + if (movies != null) + { + result.Items.Add(new ChannelItemInfo + { + Name = "Movies", + FolderType = ChannelFolderType.Container, + Id = "movies", + Type = ChannelItemType.Folder, + ImageUrl = movies.ImageUrl + }); + } + + var news = allRecordings.FirstOrDefault(i => i.IsNews); + if (news != null) + { + result.Items.Add(new ChannelItemInfo + { + Name = "News", + FolderType = ChannelFolderType.Container, + Id = "news", + Type = ChannelItemType.Folder, + ImageUrl = news.ImageUrl + }); + } + + var sports = allRecordings.FirstOrDefault(i => i.IsSports); + if (sports != null) + { + result.Items.Add(new ChannelItemInfo + { + Name = "Sports", + FolderType = ChannelFolderType.Container, + Id = "sports", + Type = ChannelItemType.Folder, + ImageUrl = sports.ImageUrl + }); + } + + var other = allRecordings.FirstOrDefault(i => !i.IsSports && !i.IsNews && !i.IsMovie && !i.IsKids && !i.IsSeries); + if (other != null) + { + result.Items.Add(new ChannelItemInfo + { + Name = "Others", + FolderType = ChannelFolderType.Container, + Id = "others", + Type = ChannelItemType.Folder, + ImageUrl = other.ImageUrl + }); + } + + return result; + } + } +}