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; using MediaBrowser.Model.LiveTv; namespace MediaBrowser.Plugins.VuPlus { public class RecordingsChannel : IChannel, IHasCacheKey, ISupportsDelete, ISupportsLatestMedia, ISupportsMediaProbe, IHasFolderAttributes, IHasChangeEvent { public ILiveTvManager _liveTvManager; public event EventHandler ContentChanged; private Timer _updateTimer; public void OnContentChanged() { if (ContentChanged != null) { ContentChanged(this, EventArgs.Empty); } } private void OnUpdateTimerCallback(object state) { OnContentChanged(); } public RecordingsChannel(ILiveTvManager liveTvManager) { _liveTvManager = liveTvManager; var interval = TimeSpan.FromMinutes(15); _updateTimer = new Timer(OnUpdateTimerCallback, null, interval, interval); } public void Dispose() { if (_updateTimer != null) { _updateTimer.Dispose(); _updateTimer = null; } } public string Name { get { return "VuPlus Recordings"; } } public string[] Attributes { get { return new[] { "Recordings" }; } } public string Description { get { return "VuPlus Recordings"; } } public string DataVersion { get { return "1"; } } public string HomePageUrl { get { return null; } } public ChannelParentalRating ParentalRating { get { return ChannelParentalRating.GeneralAudience; } } public string GetCacheKey(string userId) { var now = DateTimeOffset.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://github.com/MediaBrowser/MediaBrowser.Plugins.VuPlus/raw/master/MediaBrowser.Plugins.VuPlus/Images/thumb.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 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.GetAllRecordingsAsync(cancellationToken).ConfigureAwait(false); var result = new ChannelItemResult() { Items = new List() }; result.Items.AddRange(allRecordings.Where(filter).Select(ConvertToChannelItem)); return result; } private ChannelItemInfo ConvertToChannelItem(MyRecordingInfo item) { var path = string.IsNullOrEmpty(item.Path) ? item.Url : item.Path; 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 = path, Protocol = 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 IsLiveStream = item.Status == Model.LiveTv.RecordingStatus.InProgress, Etag = item.Status.ToString() }; return channelItem; } private async Task GetRecordingGroups(InternalChannelItemQuery query, CancellationToken cancellationToken) { var service = GetService(); var allRecordings = await service.GetAllRecordingsAsync(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; } } public class MyRecordingInfo { /// /// Id of the recording. /// public string Id { get; set; } /// /// Gets or sets the series timer identifier. /// /// The series timer identifier. public string SeriesTimerId { get; set; } /// /// Gets or sets the timer identifier. /// /// The timer identifier. public string TimerId { get; set; } /// /// ChannelId of the recording. /// public string ChannelId { get; set; } /// /// Gets or sets the type of the channel. /// /// The type of the channel. public ChannelType ChannelType { get; set; } /// /// Name of the recording. /// public string Name { get; set; } /// /// Gets or sets the path. /// /// The path. public string Path { get; set; } /// /// Gets or sets the URL. /// /// The URL. public string Url { get; set; } /// /// Gets or sets the overview. /// /// The overview. public string Overview { get; set; } /// /// The start date of the recording, in UTC. /// public DateTimeOffset StartDate { get; set; } /// /// The end date of the recording, in UTC. /// public DateTimeOffset EndDate { get; set; } /// /// Gets or sets the program identifier. /// /// The program identifier. public string ProgramId { get; set; } /// /// Gets or sets the status. /// /// The status. public RecordingStatus Status { get; set; } /// /// Genre of the program. /// public List Genres { get; set; } /// /// Gets or sets a value indicating whether this instance is repeat. /// /// true if this instance is repeat; otherwise, false. public bool IsRepeat { get; set; } /// /// Gets or sets the episode title. /// /// The episode title. public string EpisodeTitle { get; set; } /// /// Gets or sets a value indicating whether this instance is hd. /// /// true if this instance is hd; otherwise, false. public bool? IsHD { get; set; } /// /// Gets or sets the audio. /// /// The audio. public ProgramAudio? Audio { get; set; } /// /// Gets or sets the original air date. /// /// The original air date. public DateTimeOffset? OriginalAirDate { get; set; } /// /// Gets or sets a value indicating whether this instance is movie. /// /// true if this instance is movie; otherwise, false. public bool IsMovie { get; set; } /// /// Gets or sets a value indicating whether this instance is sports. /// /// true if this instance is sports; otherwise, false. public bool IsSports { get; set; } /// /// Gets or sets a value indicating whether this instance is series. /// /// true if this instance is series; otherwise, false. public bool IsSeries { get; set; } /// /// Gets or sets a value indicating whether this instance is live. /// /// true if this instance is live; otherwise, false. public bool IsLive { get; set; } /// /// Gets or sets a value indicating whether this instance is news. /// /// true if this instance is news; otherwise, false. public bool IsNews { get; set; } /// /// Gets or sets a value indicating whether this instance is kids. /// /// true if this instance is kids; otherwise, false. public bool IsKids { get; set; } /// /// Gets or sets a value indicating whether this instance is premiere. /// /// true if this instance is premiere; otherwise, false. public bool IsPremiere { get; set; } /// /// Gets or sets the official rating. /// /// The official rating. public string OfficialRating { get; set; } /// /// Gets or sets the community rating. /// /// The community rating. public float? CommunityRating { get; set; } /// /// Supply the image path if it can be accessed directly from the file system /// /// The image path. public string ImagePath { get; set; } /// /// Supply the image url if it can be downloaded /// /// The image URL. public string ImageUrl { get; set; } /// /// Gets or sets a value indicating whether this instance has image. /// /// 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; } /// /// Gets or sets the date last updated. /// /// The date last updated. public DateTimeOffset DateLastUpdated { get; set; } public MyRecordingInfo() { Genres = new List(); } } }