Browse Source

live tv updates

Luke Pulverenti 11 years ago
parent
commit
4b2b36d1a3
35 changed files with 374 additions and 132 deletions
  1. 77 25
      MediaBrowser.Api/Playback/BaseStreamingService.cs
  2. 3 2
      MediaBrowser.Api/Playback/Hls/AudioHlsService.cs
  3. 5 3
      MediaBrowser.Api/Playback/Hls/BaseHlsService.cs
  4. 3 2
      MediaBrowser.Api/Playback/Hls/VideoHlsService.cs
  5. 3 2
      MediaBrowser.Api/Playback/Progressive/AudioService.cs
  6. 5 3
      MediaBrowser.Api/Playback/Progressive/BaseProgressiveStreamingService.cs
  7. 3 2
      MediaBrowser.Api/Playback/Progressive/VideoService.cs
  8. 6 0
      MediaBrowser.Api/Playback/StreamRequest.cs
  9. 2 0
      MediaBrowser.Api/Playback/StreamState.cs
  10. 1 1
      MediaBrowser.Controller/Entities/Audio/MusicArtist.cs
  11. 12 0
      MediaBrowser.Controller/Entities/IHasImages.cs
  12. 3 3
      MediaBrowser.Controller/Providers/IImageProvider.cs
  13. 15 3
      MediaBrowser.Model/LiveTv/ProgramInfoDto.cs
  14. 1 0
      MediaBrowser.Providers/MediaBrowser.Providers.csproj
  15. 5 0
      MediaBrowser.Providers/Movies/FanArtMovieProvider.cs
  16. 7 6
      MediaBrowser.Providers/Movies/ManualFanartMovieImageProvider.cs
  17. 8 8
      MediaBrowser.Providers/Movies/ManualMovieDbImageProvider.cs
  18. 7 6
      MediaBrowser.Providers/Movies/ManualMovieDbPersonImageProvider.cs
  19. 1 1
      MediaBrowser.Providers/Movies/MovieDbImagesProvider.cs
  20. 8 6
      MediaBrowser.Providers/Music/ManualFanartAlbumProvider.cs
  21. 6 4
      MediaBrowser.Providers/Music/ManualFanartArtistProvider.cs
  22. 3 3
      MediaBrowser.Providers/Music/ManualLastFmImageProvider.cs
  23. 7 6
      MediaBrowser.Providers/TV/ManualFanartSeasonProvider.cs
  24. 3 3
      MediaBrowser.Providers/TV/ManualFanartSeriesProvider.cs
  25. 3 3
      MediaBrowser.Providers/TV/ManualTvdbEpisodeImageProvider.cs
  26. 3 3
      MediaBrowser.Providers/TV/ManualTvdbPersonImageProvider.cs
  27. 3 3
      MediaBrowser.Providers/TV/ManualTvdbSeasonImageProvider.cs
  28. 5 4
      MediaBrowser.Providers/TV/ManualTvdbSeriesImageProvider.cs
  29. 48 0
      MediaBrowser.Providers/VirtualItemImageValidator.cs
  30. 7 5
      MediaBrowser.Server.Implementations/LiveTv/LiveTvDtoService.cs
  31. 103 20
      MediaBrowser.Server.Implementations/LiveTv/LiveTvManager.cs
  32. 3 0
      MediaBrowser.WebDashboard/MediaBrowser.WebDashboard.csproj
  33. 2 2
      Nuget/MediaBrowser.Common.Internal.nuspec
  34. 1 1
      Nuget/MediaBrowser.Common.nuspec
  35. 2 2
      Nuget/MediaBrowser.Server.Core.nuspec

+ 77 - 25
MediaBrowser.Api/Playback/BaseStreamingService.cs

@@ -5,6 +5,7 @@ using MediaBrowser.Controller.Configuration;
 using MediaBrowser.Controller.Dto;
 using MediaBrowser.Controller.Entities;
 using MediaBrowser.Controller.Library;
+using MediaBrowser.Controller.LiveTv;
 using MediaBrowser.Controller.MediaInfo;
 using MediaBrowser.Controller.Persistence;
 using MediaBrowser.Model.Configuration;
@@ -62,6 +63,7 @@ namespace MediaBrowser.Api.Playback
         protected IFileSystem FileSystem { get; private set; }
 
         protected IItemRepository ItemRepository { get; private set; }
+        protected ILiveTvManager LiveTvManager { get; private set; }
 
         /// <summary>
         /// Initializes a new instance of the <see cref="BaseStreamingService" /> class.
@@ -74,8 +76,9 @@ namespace MediaBrowser.Api.Playback
         /// <param name="dtoService">The dto service.</param>
         /// <param name="fileSystem">The file system.</param>
         /// <param name="itemRepository">The item repository.</param>
-        protected BaseStreamingService(IServerConfigurationManager serverConfig, IUserManager userManager, ILibraryManager libraryManager, IIsoManager isoManager, IMediaEncoder mediaEncoder, IDtoService dtoService, IFileSystem fileSystem, IItemRepository itemRepository)
+        protected BaseStreamingService(IServerConfigurationManager serverConfig, IUserManager userManager, ILibraryManager libraryManager, IIsoManager isoManager, IMediaEncoder mediaEncoder, IDtoService dtoService, IFileSystem fileSystem, IItemRepository itemRepository, ILiveTvManager liveTvManager)
         {
+            LiveTvManager = liveTvManager;
             ItemRepository = itemRepository;
             FileSystem = fileSystem;
             DtoService = dtoService;
@@ -194,6 +197,10 @@ namespace MediaBrowser.Api.Playback
             {
                 args += string.Format("-map 0:{0}", state.VideoStream.Index);
             }
+            else if (!state.HasMediaStreams)
+            {
+                args += string.Format("-map 0:{0}", 0);
+            }
             else
             {
                 args += "-map -0:v";
@@ -203,6 +210,10 @@ namespace MediaBrowser.Api.Playback
             {
                 args += string.Format(" -map 0:{0}", state.AudioStream.Index);
             }
+            else if (!state.HasMediaStreams)
+            {
+                args += string.Format(" -map 0:{0}", 1);
+            }
 
             else
             {
@@ -336,7 +347,10 @@ namespace MediaBrowser.Api.Playback
             // If fixed dimensions were supplied
             if (request.Width.HasValue && request.Height.HasValue)
             {
-                return string.Format(" -vf \"scale=trunc({0}/2)*2:trunc({1}/2)*2{2}\"", request.Width.Value, request.Height.Value, assSubtitleParam);
+                var widthParam = request.Width.Value.ToString(UsCulture);
+                var heightParam = request.Height.Value.ToString(UsCulture);
+
+                return string.Format(" -vf \"scale=trunc({0}/2)*2:trunc({1}/2)*2{2}\"", widthParam, heightParam, assSubtitleParam);
             }
 
             var isH264Output = outputVideoCodec.Equals("libx264", StringComparison.OrdinalIgnoreCase);
@@ -344,33 +358,41 @@ namespace MediaBrowser.Api.Playback
             // If a fixed width was requested
             if (request.Width.HasValue)
             {
+                var widthParam = request.Width.Value.ToString(UsCulture);
+                
                 return isH264Output ?
-                    string.Format(" -vf \"scale={0}:trunc(ow/a/2)*2{1}\"", request.Width.Value, assSubtitleParam) :
-                    string.Format(" -vf \"scale={0}:-1{1}\"", request.Width.Value, assSubtitleParam);
+                    string.Format(" -vf \"scale={0}:trunc(ow/a/2)*2{1}\"", widthParam, assSubtitleParam) :
+                    string.Format(" -vf \"scale={0}:-1{1}\"", widthParam, assSubtitleParam);
             }
 
             // If a fixed height was requested
             if (request.Height.HasValue)
             {
+                var heightParam = request.Height.Value.ToString(UsCulture);
+                
                 return isH264Output ?
-                    string.Format(" -vf \"scale=trunc(oh*a*2)/2:{0}{1}\"", request.Height.Value, assSubtitleParam) :
-                    string.Format(" -vf \"scale=-1:{0}{1}\"", request.Height.Value, assSubtitleParam);
+                    string.Format(" -vf \"scale=trunc(oh*a*2)/2:{0}{1}\"", heightParam, assSubtitleParam) :
+                    string.Format(" -vf \"scale=-1:{0}{1}\"", heightParam, assSubtitleParam);
             }
 
             // If a max width was requested
             if (request.MaxWidth.HasValue && (!request.MaxHeight.HasValue || state.VideoStream == null))
             {
+                var maxWidthParam = request.MaxWidth.Value.ToString(UsCulture);
+                
                 return isH264Output ?
-                    string.Format(" -vf \"scale=min(iw\\,{0}):trunc(ow/a/2)*2{1}\"", request.MaxWidth.Value, assSubtitleParam) :
-                    string.Format(" -vf \"scale=min(iw\\,{0}):-1{1}\"", request.MaxWidth.Value, assSubtitleParam);
+                    string.Format(" -vf \"scale=min(iw\\,{0}):trunc(ow/a/2)*2{1}\"", maxWidthParam, assSubtitleParam) :
+                    string.Format(" -vf \"scale=min(iw\\,{0}):-1{1}\"", maxWidthParam, assSubtitleParam);
             }
 
             // If a max height was requested
             if (request.MaxHeight.HasValue && (!request.MaxWidth.HasValue || state.VideoStream == null))
             {
+                var maxHeightParam = request.MaxHeight.Value.ToString(UsCulture);
+                
                 return isH264Output ?
-                    string.Format(" -vf \"scale=trunc(oh*a*2)/2:min(ih\\,{0}){1}\"", request.MaxHeight.Value, assSubtitleParam) :
-                    string.Format(" -vf \"scale=-1:min(ih\\,{0}){1}\"", request.MaxHeight.Value, assSubtitleParam);
+                    string.Format(" -vf \"scale=trunc(oh*a*2)/2:min(ih\\,{0}){1}\"", maxHeightParam, assSubtitleParam) :
+                    string.Format(" -vf \"scale=-1:min(ih\\,{0}){1}\"", maxHeightParam, assSubtitleParam);
             }
 
             if (state.VideoStream == null)
@@ -390,7 +412,10 @@ namespace MediaBrowser.Api.Playback
             // If we're encoding with libx264, it can't handle odd numbered widths or heights, so we'll have to fix that
             if (isH264Output)
             {
-                return string.Format(" -vf \"scale=trunc({0}/2)*2:trunc({1}/2)*2{2}\"", outputSize.Width, outputSize.Height, assSubtitleParam);
+                var widthParam = outputSize.Width.ToString(UsCulture);
+                var heightParam = outputSize.Height.ToString(UsCulture);
+
+                return string.Format(" -vf \"scale=trunc({0}/2)*2:trunc({1}/2)*2{2}\"", widthParam, heightParam, assSubtitleParam);
             }
 
             // Otherwise use -vf scale since ffmpeg will ensure internally that the aspect ratio is preserved
@@ -823,11 +848,10 @@ namespace MediaBrowser.Api.Playback
         /// Gets the state.
         /// </summary>
         /// <param name="request">The request.</param>
+        /// <param name="cancellationToken">The cancellation token.</param>
         /// <returns>StreamState.</returns>
-        protected StreamState GetState(StreamRequest request)
+        protected async Task<StreamState> GetState(StreamRequest request, CancellationToken cancellationToken)
         {
-            var item = DtoService.GetItemByDtoId(request.Id);
-
             var url = Request.PathInfo;
 
             if (!request.AudioCodec.HasValue)
@@ -838,22 +862,48 @@ namespace MediaBrowser.Api.Playback
             var state = new StreamState
             {
                 Request = request,
-                RequestedUrl = url,
-                MediaPath = item.Path,
-                IsRemote = item.LocationType == LocationType.Remote
+                RequestedUrl = url
             };
 
-            var video = item as Video;
+            BaseItem item;
+
+            if (string.Equals(request.Type, "Recording", StringComparison.OrdinalIgnoreCase))
+            {
+                var recording = await LiveTvManager.GetInternalRecording(request.Id, cancellationToken).ConfigureAwait(false);
+
+                state.VideoType = VideoType.VideoFile;
+                state.IsInputVideo = string.Equals(recording.MediaType, MediaType.Video, StringComparison.OrdinalIgnoreCase);
+                state.PlayableStreamFileNames = new List<string>();
+
+                if (!string.IsNullOrEmpty(recording.RecordingInfo.Path) && File.Exists(recording.RecordingInfo.Path))
+                {
+                    state.MediaPath = recording.RecordingInfo.Path;
+                    state.IsRemote = false;
+                }
+                else if (!string.IsNullOrEmpty(recording.RecordingInfo.Url))
+                {
+                    state.MediaPath = recording.RecordingInfo.Url;
+                    state.IsRemote = true;
+                }
 
-            if (video != null)
+                item = recording;
+            }
+            else
             {
-                state.IsInputVideo = true;
-                state.VideoType = video.VideoType;
-                state.IsoType = video.IsoType;
+                item = DtoService.GetItemByDtoId(request.Id);
 
-                state.PlayableStreamFileNames = video.PlayableStreamFileNames == null
-                    ? new List<string>()
-                    : video.PlayableStreamFileNames.ToList();
+                var video = item as Video;
+
+                if (video != null)
+                {
+                    state.IsInputVideo = true;
+                    state.VideoType = video.VideoType;
+                    state.IsoType = video.IsoType;
+
+                    state.PlayableStreamFileNames = video.PlayableStreamFileNames == null
+                        ? new List<string>()
+                        : video.PlayableStreamFileNames.ToList();
+                }
             }
 
             var videoRequest = request as VideoStreamRequest;
@@ -880,6 +930,8 @@ namespace MediaBrowser.Api.Playback
                 state.AudioStream = GetMediaStream(mediaStreams, null, MediaStreamType.Audio, true);
             }
 
+            state.HasMediaStreams = mediaStreams.Count > 0;
+
             return state;
         }
 

+ 3 - 2
MediaBrowser.Api/Playback/Hls/AudioHlsService.cs

@@ -3,6 +3,7 @@ using MediaBrowser.Common.MediaInfo;
 using MediaBrowser.Controller.Configuration;
 using MediaBrowser.Controller.Dto;
 using MediaBrowser.Controller.Library;
+using MediaBrowser.Controller.LiveTv;
 using MediaBrowser.Controller.Persistence;
 using MediaBrowser.Model.Dto;
 using MediaBrowser.Model.IO;
@@ -26,8 +27,8 @@ namespace MediaBrowser.Api.Playback.Hls
     /// </summary>
     public class AudioHlsService : BaseHlsService
     {
-        public AudioHlsService(IServerConfigurationManager serverConfig, IUserManager userManager, ILibraryManager libraryManager, IIsoManager isoManager, IMediaEncoder mediaEncoder, IDtoService dtoService, IFileSystem fileSystem, IItemRepository itemRepository)
-            : base(serverConfig, userManager, libraryManager, isoManager, mediaEncoder, dtoService, fileSystem, itemRepository)
+        public AudioHlsService(IServerConfigurationManager serverConfig, IUserManager userManager, ILibraryManager libraryManager, IIsoManager isoManager, IMediaEncoder mediaEncoder, IDtoService dtoService, IFileSystem fileSystem, IItemRepository itemRepository, ILiveTvManager liveTvManager)
+            : base(serverConfig, userManager, libraryManager, isoManager, mediaEncoder, dtoService, fileSystem, itemRepository, liveTvManager)
         {
         }
 

+ 5 - 3
MediaBrowser.Api/Playback/Hls/BaseHlsService.cs

@@ -5,6 +5,7 @@ using MediaBrowser.Common.Net;
 using MediaBrowser.Controller.Configuration;
 using MediaBrowser.Controller.Dto;
 using MediaBrowser.Controller.Library;
+using MediaBrowser.Controller.LiveTv;
 using MediaBrowser.Controller.Persistence;
 using MediaBrowser.Model.Dto;
 using MediaBrowser.Model.IO;
@@ -12,6 +13,7 @@ using System;
 using System.Collections.Generic;
 using System.IO;
 using System.Text;
+using System.Threading;
 using System.Threading.Tasks;
 
 namespace MediaBrowser.Api.Playback.Hls
@@ -21,8 +23,8 @@ namespace MediaBrowser.Api.Playback.Hls
     /// </summary>
     public abstract class BaseHlsService : BaseStreamingService
     {
-        protected BaseHlsService(IServerConfigurationManager serverConfig, IUserManager userManager, ILibraryManager libraryManager, IIsoManager isoManager, IMediaEncoder mediaEncoder, IDtoService dtoService, IFileSystem fileSystem, IItemRepository itemRepository)
-            : base(serverConfig, userManager, libraryManager, isoManager, mediaEncoder, dtoService, fileSystem, itemRepository)
+        protected BaseHlsService(IServerConfigurationManager serverConfig, IUserManager userManager, ILibraryManager libraryManager, IIsoManager isoManager, IMediaEncoder mediaEncoder, IDtoService dtoService, IFileSystem fileSystem, IItemRepository itemRepository, ILiveTvManager liveTvManager) 
+            : base(serverConfig, userManager, libraryManager, isoManager, mediaEncoder, dtoService, fileSystem, itemRepository, liveTvManager)
         {
         }
 
@@ -72,7 +74,7 @@ namespace MediaBrowser.Api.Playback.Hls
         /// <returns>System.Object.</returns>
         protected object ProcessRequest(StreamRequest request)
         {
-            var state = GetState(request);
+            var state = GetState(request, CancellationToken.None).Result;
 
             return ProcessRequestAsync(state).Result;
         }

+ 3 - 2
MediaBrowser.Api/Playback/Hls/VideoHlsService.cs

@@ -3,6 +3,7 @@ using MediaBrowser.Common.MediaInfo;
 using MediaBrowser.Controller.Configuration;
 using MediaBrowser.Controller.Dto;
 using MediaBrowser.Controller.Library;
+using MediaBrowser.Controller.LiveTv;
 using MediaBrowser.Controller.Persistence;
 using MediaBrowser.Model.IO;
 using ServiceStack;
@@ -32,8 +33,8 @@ namespace MediaBrowser.Api.Playback.Hls
     /// </summary>
     public class VideoHlsService : BaseHlsService
     {
-        public VideoHlsService(IServerConfigurationManager serverConfig, IUserManager userManager, ILibraryManager libraryManager, IIsoManager isoManager, IMediaEncoder mediaEncoder, IDtoService dtoService, IFileSystem fileSystem, IItemRepository itemRepository)
-            : base(serverConfig, userManager, libraryManager, isoManager, mediaEncoder, dtoService, fileSystem, itemRepository)
+        public VideoHlsService(IServerConfigurationManager serverConfig, IUserManager userManager, ILibraryManager libraryManager, IIsoManager isoManager, IMediaEncoder mediaEncoder, IDtoService dtoService, IFileSystem fileSystem, IItemRepository itemRepository, ILiveTvManager liveTvManager)
+            : base(serverConfig, userManager, libraryManager, isoManager, mediaEncoder, dtoService, fileSystem, itemRepository, liveTvManager)
         {
         }
 

+ 3 - 2
MediaBrowser.Api/Playback/Progressive/AudioService.cs

@@ -4,6 +4,7 @@ using MediaBrowser.Controller.Configuration;
 using MediaBrowser.Controller.Drawing;
 using MediaBrowser.Controller.Dto;
 using MediaBrowser.Controller.Library;
+using MediaBrowser.Controller.LiveTv;
 using MediaBrowser.Controller.Persistence;
 using MediaBrowser.Model.IO;
 using ServiceStack;
@@ -41,8 +42,8 @@ namespace MediaBrowser.Api.Playback.Progressive
     /// </summary>
     public class AudioService : BaseProgressiveStreamingService
     {
-        public AudioService(IServerConfigurationManager serverConfig, IUserManager userManager, ILibraryManager libraryManager, IIsoManager isoManager, IMediaEncoder mediaEncoder, IDtoService dtoService, IFileSystem fileSystem, IItemRepository itemRepository, IImageProcessor imageProcessor)
-            : base(serverConfig, userManager, libraryManager, isoManager, mediaEncoder, dtoService, fileSystem, itemRepository, imageProcessor)
+        public AudioService(IServerConfigurationManager serverConfig, IUserManager userManager, ILibraryManager libraryManager, IIsoManager isoManager, IMediaEncoder mediaEncoder, IDtoService dtoService, IFileSystem fileSystem, IItemRepository itemRepository, ILiveTvManager liveTvManager, IImageProcessor imageProcessor)
+            : base(serverConfig, userManager, libraryManager, isoManager, mediaEncoder, dtoService, fileSystem, itemRepository, liveTvManager, imageProcessor)
         {
         }
 

+ 5 - 3
MediaBrowser.Api/Playback/Progressive/BaseProgressiveStreamingService.cs

@@ -5,12 +5,14 @@ using MediaBrowser.Controller.Configuration;
 using MediaBrowser.Controller.Drawing;
 using MediaBrowser.Controller.Dto;
 using MediaBrowser.Controller.Library;
+using MediaBrowser.Controller.LiveTv;
 using MediaBrowser.Controller.Persistence;
 using MediaBrowser.Model.Dto;
 using MediaBrowser.Model.IO;
 using System.Collections.Generic;
 using System.IO;
 using System.Net.Http;
+using System.Threading;
 using System.Threading.Tasks;
 
 namespace MediaBrowser.Api.Playback.Progressive
@@ -22,8 +24,8 @@ namespace MediaBrowser.Api.Playback.Progressive
     {
         protected readonly IImageProcessor ImageProcessor;
 
-        protected BaseProgressiveStreamingService(IServerConfigurationManager serverConfig, IUserManager userManager, ILibraryManager libraryManager, IIsoManager isoManager, IMediaEncoder mediaEncoder, IDtoService dtoService, IFileSystem fileSystem, IItemRepository itemRepository, IImageProcessor imageProcessor)
-            : base(serverConfig, userManager, libraryManager, isoManager, mediaEncoder, dtoService, fileSystem, itemRepository)
+        protected BaseProgressiveStreamingService(IServerConfigurationManager serverConfig, IUserManager userManager, ILibraryManager libraryManager, IIsoManager isoManager, IMediaEncoder mediaEncoder, IDtoService dtoService, IFileSystem fileSystem, IItemRepository itemRepository, ILiveTvManager liveTvManager, IImageProcessor imageProcessor)
+            : base(serverConfig, userManager, libraryManager, isoManager, mediaEncoder, dtoService, fileSystem, itemRepository, liveTvManager)
         {
             ImageProcessor = imageProcessor;
         }
@@ -178,7 +180,7 @@ namespace MediaBrowser.Api.Playback.Progressive
         /// <returns>Task.</returns>
         protected object ProcessRequest(StreamRequest request, bool isHeadRequest)
         {
-            var state = GetState(request);
+            var state = GetState(request, CancellationToken.None).Result;
 
             var responseHeaders = new Dictionary<string, string>();
 

+ 3 - 2
MediaBrowser.Api/Playback/Progressive/VideoService.cs

@@ -4,6 +4,7 @@ using MediaBrowser.Controller.Configuration;
 using MediaBrowser.Controller.Drawing;
 using MediaBrowser.Controller.Dto;
 using MediaBrowser.Controller.Library;
+using MediaBrowser.Controller.LiveTv;
 using MediaBrowser.Controller.Persistence;
 using MediaBrowser.Model.IO;
 using ServiceStack;
@@ -54,8 +55,8 @@ namespace MediaBrowser.Api.Playback.Progressive
     /// </summary>
     public class VideoService : BaseProgressiveStreamingService
     {
-        public VideoService(IServerConfigurationManager serverConfig, IUserManager userManager, ILibraryManager libraryManager, IIsoManager isoManager, IMediaEncoder mediaEncoder, IDtoService dtoService, IFileSystem fileSystem, IItemRepository itemRepository, IImageProcessor imageProcessor)
-            : base(serverConfig, userManager, libraryManager, isoManager, mediaEncoder, dtoService, fileSystem, itemRepository, imageProcessor)
+        public VideoService(IServerConfigurationManager serverConfig, IUserManager userManager, ILibraryManager libraryManager, IIsoManager isoManager, IMediaEncoder mediaEncoder, IDtoService dtoService, IFileSystem fileSystem, IItemRepository itemRepository, ILiveTvManager liveTvManager, IImageProcessor imageProcessor)
+            : base(serverConfig, userManager, libraryManager, isoManager, mediaEncoder, dtoService, fileSystem, itemRepository, liveTvManager, imageProcessor)
         {
         }
 

+ 6 - 0
MediaBrowser.Api/Playback/StreamRequest.cs

@@ -65,6 +65,12 @@ namespace MediaBrowser.Api.Playback
         /// No need to put this in api docs since it's dlna only
         /// </summary>
         public bool AlbumArt { get; set; }
+
+        /// <summary>
+        /// Gets or sets the type.
+        /// </summary>
+        /// <value>The type.</value>
+        public string Type { get; set; }
     }
 
     public class VideoStreamRequest : StreamRequest

+ 2 - 0
MediaBrowser.Api/Playback/StreamState.cs

@@ -45,5 +45,7 @@ namespace MediaBrowser.Api.Playback
         public IsoType? IsoType { get; set; }
 
         public List<string> PlayableStreamFileNames { get; set; }
+
+        public bool HasMediaStreams { get; set; }
     }
 }

+ 1 - 1
MediaBrowser.Controller/Entities/Audio/MusicArtist.cs

@@ -96,7 +96,7 @@ namespace MediaBrowser.Controller.Entities.Audio
         /// </summary>
         /// <param name="item">The item.</param>
         /// <returns>System.String.</returns>
-        public static string GetUserDataKey(BaseItem item)
+        private static string GetUserDataKey(MusicArtist item)
         {
             var id = item.GetProviderId(MetadataProviders.Musicbrainz);
 

+ 12 - 0
MediaBrowser.Controller/Entities/IHasImages.cs

@@ -63,6 +63,18 @@ namespace MediaBrowser.Controller.Entities
         /// <param name="index2">The index2.</param>
         /// <returns>Task.</returns>
         Task SwapImages(ImageType type, int index1, int index2);
+
+        /// <summary>
+        /// Gets the display type of the media.
+        /// </summary>
+        /// <value>The display type of the media.</value>
+        string DisplayMediaType { get; set; }
+
+        /// <summary>
+        /// Gets or sets the primary image path.
+        /// </summary>
+        /// <value>The primary image path.</value>
+        string PrimaryImagePath { get; set; }
     }
 
     public static class HasImagesExtensions

+ 3 - 3
MediaBrowser.Controller/Providers/IImageProvider.cs

@@ -23,7 +23,7 @@ namespace MediaBrowser.Controller.Providers
         /// </summary>
         /// <param name="item">The item.</param>
         /// <returns><c>true</c> if XXXX, <c>false</c> otherwise</returns>
-        bool Supports(BaseItem item);
+        bool Supports(IHasImages item);
 
         /// <summary>
         /// Gets the images.
@@ -32,7 +32,7 @@ namespace MediaBrowser.Controller.Providers
         /// <param name="imageType">Type of the image.</param>
         /// <param name="cancellationToken">The cancellation token.</param>
         /// <returns>Task{IEnumerable{RemoteImageInfo}}.</returns>
-        Task<IEnumerable<RemoteImageInfo>> GetImages(BaseItem item, ImageType imageType, CancellationToken cancellationToken);
+        Task<IEnumerable<RemoteImageInfo>> GetImages(IHasImages item, ImageType imageType, CancellationToken cancellationToken);
 
         /// <summary>
         /// Gets the images.
@@ -40,7 +40,7 @@ namespace MediaBrowser.Controller.Providers
         /// <param name="item">The item.</param>
         /// <param name="cancellationToken">The cancellation token.</param>
         /// <returns>Task{IEnumerable{RemoteImageInfo}}.</returns>
-        Task<IEnumerable<RemoteImageInfo>> GetAllImages(BaseItem item, CancellationToken cancellationToken);
+        Task<IEnumerable<RemoteImageInfo>> GetAllImages(IHasImages item, CancellationToken cancellationToken);
 
         /// <summary>
         /// Gets the priority.

+ 15 - 3
MediaBrowser.Model/LiveTv/ProgramInfoDto.cs

@@ -1,7 +1,7 @@
-using System;
-using System.Collections.Generic;
-using MediaBrowser.Model.Dto;
+using MediaBrowser.Model.Dto;
 using MediaBrowser.Model.Entities;
+using System;
+using System.Collections.Generic;
 
 namespace MediaBrowser.Model.LiveTv
 {
@@ -12,6 +12,18 @@ namespace MediaBrowser.Model.LiveTv
         /// </summary>
         public string Id { get; set; }
 
+        /// <summary>
+        /// Gets or sets the timer identifier.
+        /// </summary>
+        /// <value>The timer identifier.</value>
+        public string TimerId { get; set; }
+
+        /// <summary>
+        /// Gets or sets the series timer identifier.
+        /// </summary>
+        /// <value>The series timer identifier.</value>
+        public string SeriesTimerId { get; set; }
+        
         /// <summary>
         /// Gets or sets the external identifier.
         /// </summary>

+ 1 - 0
MediaBrowser.Providers/MediaBrowser.Providers.csproj

@@ -146,6 +146,7 @@
     <Compile Include="TV\TvdbPrescanTask.cs" />
     <Compile Include="TV\TvdbSeriesImageProvider.cs" />
     <Compile Include="UserRootFolderNameProvider.cs" />
+    <Compile Include="VirtualItemImageValidator.cs" />
   </ItemGroup>
   <ItemGroup>
     <ProjectReference Include="..\MediaBrowser.Common\MediaBrowser.Common.csproj">

+ 5 - 0
MediaBrowser.Providers/Movies/FanArtMovieProvider.cs

@@ -111,6 +111,11 @@ namespace MediaBrowser.Providers.Movies
         /// <param name="item">The item.</param>
         /// <returns><c>true</c> if XXXX, <c>false</c> otherwise</returns>
         public override bool Supports(BaseItem item)
+        {
+            return SupportsItem(item);
+        }
+
+        internal static bool SupportsItem(IHasImages item)
         {
             var trailer = item as Trailer;
 

+ 7 - 6
MediaBrowser.Providers/Movies/ManualFanartMovieImageProvider.cs

@@ -36,23 +36,24 @@ namespace MediaBrowser.Providers.Movies
             get { return "FanArt"; }
         }
 
-        public bool Supports(BaseItem item)
+        public bool Supports(IHasImages item)
         {
-            return FanArtMovieProvider.Current.Supports(item);
+            return FanArtMovieProvider.SupportsItem(item);
         }
 
-        public async Task<IEnumerable<RemoteImageInfo>> GetImages(BaseItem item, ImageType imageType, CancellationToken cancellationToken)
+        public async Task<IEnumerable<RemoteImageInfo>> GetImages(IHasImages item, ImageType imageType, CancellationToken cancellationToken)
         {
             var images = await GetAllImages(item, cancellationToken).ConfigureAwait(false);
 
             return images.Where(i => i.Type == imageType);
         }
 
-        public Task<IEnumerable<RemoteImageInfo>> GetAllImages(BaseItem item, CancellationToken cancellationToken)
+        public Task<IEnumerable<RemoteImageInfo>> GetAllImages(IHasImages item, CancellationToken cancellationToken)
         {
+            var baseItem = (BaseItem)item;
             var list = new List<RemoteImageInfo>();
 
-            var movieId = item.GetProviderId(MetadataProviders.Tmdb);
+            var movieId = baseItem.GetProviderId(MetadataProviders.Tmdb);
 
             if (!string.IsNullOrEmpty(movieId))
             {
@@ -71,7 +72,7 @@ namespace MediaBrowser.Providers.Movies
             var language = _config.Configuration.PreferredMetadataLanguage;
 
             var isLanguageEn = string.Equals(language, "en", StringComparison.OrdinalIgnoreCase);
-            
+
             // Sort first by width to prioritize HD versions
             list = list.OrderByDescending(i => i.Width ?? 0)
                 .ThenByDescending(i =>

+ 8 - 8
MediaBrowser.Providers/Movies/ManualMovieDbImageProvider.cs

@@ -35,19 +35,19 @@ namespace MediaBrowser.Providers.Movies
             get { return "TheMovieDb"; }
         }
 
-        public bool Supports(BaseItem item)
+        public bool Supports(IHasImages item)
         {
             return MovieDbImagesProvider.SupportsItem(item);
         }
 
-        public async Task<IEnumerable<RemoteImageInfo>> GetImages(BaseItem item, ImageType imageType, CancellationToken cancellationToken)
+        public async Task<IEnumerable<RemoteImageInfo>> GetImages(IHasImages item, ImageType imageType, CancellationToken cancellationToken)
         {
             var images = await GetAllImages(item, cancellationToken).ConfigureAwait(false);
 
             return images.Where(i => i.Type == imageType);
         }
 
-        public async Task<IEnumerable<RemoteImageInfo>> GetAllImages(BaseItem item, CancellationToken cancellationToken)
+        public async Task<IEnumerable<RemoteImageInfo>> GetAllImages(IHasImages item, CancellationToken cancellationToken)
         {
             var list = new List<RemoteImageInfo>();
 
@@ -114,14 +114,14 @@ namespace MediaBrowser.Providers.Movies
                 .ThenByDescending(i => i.VoteCount ?? 0)
                 .ToList();
         }
-        
+
         /// <summary>
         /// Gets the posters.
         /// </summary>
         /// <param name="images">The images.</param>
         /// <param name="item">The item.</param>
         /// <returns>IEnumerable{MovieDbProvider.Poster}.</returns>
-        private IEnumerable<MovieDbProvider.Poster> GetPosters(MovieDbProvider.Images images, BaseItem item)
+        private IEnumerable<MovieDbProvider.Poster> GetPosters(MovieDbProvider.Images images, IHasImages item)
         {
             var language = _config.Configuration.PreferredMetadataLanguage;
 
@@ -134,7 +134,7 @@ namespace MediaBrowser.Providers.Movies
         /// <param name="images">The images.</param>
         /// <param name="item">The item.</param>
         /// <returns>IEnumerable{MovieDbProvider.Backdrop}.</returns>
-        private IEnumerable<MovieDbProvider.Backdrop> GetBackdrops(MovieDbProvider.Images images, BaseItem item)
+        private IEnumerable<MovieDbProvider.Backdrop> GetBackdrops(MovieDbProvider.Images images, IHasImages item)
         {
             var eligibleBackdrops = images.backdrops == null ? new List<MovieDbProvider.Backdrop>() :
                 images.backdrops
@@ -150,9 +150,9 @@ namespace MediaBrowser.Providers.Movies
         /// <param name="item">The item.</param>
         /// <param name="jsonSerializer">The json serializer.</param>
         /// <returns>Task{MovieImages}.</returns>
-        private MovieDbProvider.Images FetchImages(BaseItem item, IJsonSerializer jsonSerializer)
+        private MovieDbProvider.Images FetchImages(IHasImages item, IJsonSerializer jsonSerializer)
         {
-            var path = MovieDbProvider.Current.GetImagesDataFilePath(item);
+            var path = MovieDbProvider.Current.GetImagesDataFilePath((BaseItem)item);
 
             if (!string.IsNullOrEmpty(path))
             {

+ 7 - 6
MediaBrowser.Providers/Movies/ManualMovieDbPersonImageProvider.cs

@@ -34,26 +34,27 @@ namespace MediaBrowser.Providers.Movies
             get { return "TheMovieDb"; }
         }
 
-        public bool Supports(BaseItem item)
+        public bool Supports(IHasImages item)
         {
             return item is Person;
         }
 
-        public async Task<IEnumerable<RemoteImageInfo>> GetImages(BaseItem item, ImageType imageType, CancellationToken cancellationToken)
+        public async Task<IEnumerable<RemoteImageInfo>> GetImages(IHasImages item, ImageType imageType, CancellationToken cancellationToken)
         {
             var images = await GetAllImages(item, cancellationToken).ConfigureAwait(false);
 
             return images.Where(i => i.Type == imageType);
         }
 
-        public Task<IEnumerable<RemoteImageInfo>> GetAllImages(BaseItem item, CancellationToken cancellationToken)
+        public Task<IEnumerable<RemoteImageInfo>> GetAllImages(IHasImages item, CancellationToken cancellationToken)
         {
             return GetAllImagesInternal(item, true, cancellationToken);
         }
 
-        public async Task<IEnumerable<RemoteImageInfo>> GetAllImagesInternal(BaseItem item, bool retryOnMissingData, CancellationToken cancellationToken)
+        public async Task<IEnumerable<RemoteImageInfo>> GetAllImagesInternal(IHasImages item, bool retryOnMissingData, CancellationToken cancellationToken)
         {
-            var id = item.GetProviderId(MetadataProviders.Tmdb);
+            var person = (Person)item;
+            var id = person.GetProviderId(MetadataProviders.Tmdb);
 
             if (!string.IsNullOrEmpty(id))
             {
@@ -86,7 +87,7 @@ namespace MediaBrowser.Providers.Movies
 
             return new List<RemoteImageInfo>();
         }
-        
+
         private IEnumerable<RemoteImageInfo> GetImages(MovieDbPersonProvider.Images images, string baseImageUrl)
         {
             var list = new List<RemoteImageInfo>();

+ 1 - 1
MediaBrowser.Providers/Movies/MovieDbImagesProvider.cs

@@ -61,7 +61,7 @@ namespace MediaBrowser.Providers.Movies
             return SupportsItem(item);
         }
 
-        public static bool SupportsItem(BaseItem item)
+        internal static bool SupportsItem(IHasImages item)
         {
             var trailer = item as Trailer;
 

+ 8 - 6
MediaBrowser.Providers/Music/ManualFanartAlbumProvider.cs

@@ -37,32 +37,34 @@ namespace MediaBrowser.Providers.Music
             get { return "FanArt"; }
         }
 
-        public bool Supports(BaseItem item)
+        public bool Supports(IHasImages item)
         {
             return item is MusicAlbum;
         }
 
-        public async Task<IEnumerable<RemoteImageInfo>> GetImages(BaseItem item, ImageType imageType, CancellationToken cancellationToken)
+        public async Task<IEnumerable<RemoteImageInfo>> GetImages(IHasImages item, ImageType imageType, CancellationToken cancellationToken)
         {
             var images = await GetAllImages(item, cancellationToken).ConfigureAwait(false);
 
             return images.Where(i => i.Type == imageType);
         }
 
-        public Task<IEnumerable<RemoteImageInfo>> GetAllImages(BaseItem item, CancellationToken cancellationToken)
+        public Task<IEnumerable<RemoteImageInfo>> GetAllImages(IHasImages item, CancellationToken cancellationToken)
         {
+            var album = (MusicAlbum)item;
+
             var list = new List<RemoteImageInfo>();
 
-            var artistMusicBrainzId = item.Parent.GetProviderId(MetadataProviders.Musicbrainz);
+            var artistMusicBrainzId = album.Parent.GetProviderId(MetadataProviders.Musicbrainz);
 
             if (!string.IsNullOrEmpty(artistMusicBrainzId))
             {
                 var artistXmlPath = FanArtArtistProvider.GetArtistDataPath(_config.CommonApplicationPaths, artistMusicBrainzId);
                 artistXmlPath = Path.Combine(artistXmlPath, "fanart.xml");
 
-                var musicBrainzReleaseGroupId = item.GetProviderId(MetadataProviders.MusicBrainzReleaseGroup);
+                var musicBrainzReleaseGroupId = album.GetProviderId(MetadataProviders.MusicBrainzReleaseGroup);
 
-                var musicBrainzId = item.GetProviderId(MetadataProviders.Musicbrainz);
+                var musicBrainzId = album.GetProviderId(MetadataProviders.Musicbrainz);
 
                 try
                 {

+ 6 - 4
MediaBrowser.Providers/Music/ManualFanartArtistProvider.cs

@@ -37,23 +37,25 @@ namespace MediaBrowser.Providers.Music
             get { return "FanArt"; }
         }
 
-        public bool Supports(BaseItem item)
+        public bool Supports(IHasImages item)
         {
             return item is MusicArtist;
         }
 
-        public async Task<IEnumerable<RemoteImageInfo>> GetImages(BaseItem item, ImageType imageType, CancellationToken cancellationToken)
+        public async Task<IEnumerable<RemoteImageInfo>> GetImages(IHasImages item, ImageType imageType, CancellationToken cancellationToken)
         {
             var images = await GetAllImages(item, cancellationToken).ConfigureAwait(false);
 
             return images.Where(i => i.Type == imageType);
         }
 
-        public Task<IEnumerable<RemoteImageInfo>> GetAllImages(BaseItem item, CancellationToken cancellationToken)
+        public Task<IEnumerable<RemoteImageInfo>> GetAllImages(IHasImages item, CancellationToken cancellationToken)
         {
+            var artist = (MusicArtist)item;
+
             var list = new List<RemoteImageInfo>();
 
-            var artistMusicBrainzId = item.GetProviderId(MetadataProviders.Musicbrainz);
+            var artistMusicBrainzId = artist.GetProviderId(MetadataProviders.Musicbrainz);
 
             if (!string.IsNullOrEmpty(artistMusicBrainzId))
             {

+ 3 - 3
MediaBrowser.Providers/Music/ManualLastFmImageProvider.cs

@@ -23,19 +23,19 @@ namespace MediaBrowser.Providers.Music
             get { return "last.fm"; }
         }
 
-        public bool Supports(BaseItem item)
+        public bool Supports(IHasImages item)
         {
             return item is MusicAlbum || item is MusicArtist;
         }
 
-        public async Task<IEnumerable<RemoteImageInfo>> GetImages(BaseItem item, ImageType imageType, CancellationToken cancellationToken)
+        public async Task<IEnumerable<RemoteImageInfo>> GetImages(IHasImages item, ImageType imageType, CancellationToken cancellationToken)
         {
             var images = await GetAllImages(item, cancellationToken).ConfigureAwait(false);
 
             return images.Where(i => i.Type == imageType);
         }
 
-        public Task<IEnumerable<RemoteImageInfo>> GetAllImages(BaseItem item, CancellationToken cancellationToken)
+        public Task<IEnumerable<RemoteImageInfo>> GetAllImages(IHasImages item, CancellationToken cancellationToken)
         {
             var list = new List<RemoteImageInfo>();
 

+ 7 - 6
MediaBrowser.Providers/TV/ManualFanartSeasonProvider.cs

@@ -37,35 +37,36 @@ namespace MediaBrowser.Providers.TV
             get { return "FanArt"; }
         }
 
-        public bool Supports(BaseItem item)
+        public bool Supports(IHasImages item)
         {
             return item is Season;
         }
 
-        public async Task<IEnumerable<RemoteImageInfo>> GetImages(BaseItem item, ImageType imageType, CancellationToken cancellationToken)
+        public async Task<IEnumerable<RemoteImageInfo>> GetImages(IHasImages item, ImageType imageType, CancellationToken cancellationToken)
         {
             var images = await GetAllImages(item, cancellationToken).ConfigureAwait(false);
 
             return images.Where(i => i.Type == imageType);
         }
 
-        public Task<IEnumerable<RemoteImageInfo>> GetAllImages(BaseItem item, CancellationToken cancellationToken)
+        public Task<IEnumerable<RemoteImageInfo>> GetAllImages(IHasImages item, CancellationToken cancellationToken)
         {
             var list = new List<RemoteImageInfo>();
 
-            var series = ((Season)item).Series;
+            var season = (Season)item;
+            var series = season.Series;
 
             if (series != null)
             {
                 var id = series.GetProviderId(MetadataProviders.Tvdb);
 
-                if (!string.IsNullOrEmpty(id) && item.IndexNumber.HasValue)
+                if (!string.IsNullOrEmpty(id) && season.IndexNumber.HasValue)
                 {
                     var xmlPath = FanArtTvProvider.Current.GetFanartXmlPath(id);
 
                     try
                     {
-                        AddImages(list, item.IndexNumber.Value, xmlPath, cancellationToken);
+                        AddImages(list, season.IndexNumber.Value, xmlPath, cancellationToken);
                     }
                     catch (FileNotFoundException)
                     {

+ 3 - 3
MediaBrowser.Providers/TV/ManualFanartSeriesProvider.cs

@@ -37,19 +37,19 @@ namespace MediaBrowser.Providers.TV
             get { return "FanArt"; }
         }
 
-        public bool Supports(BaseItem item)
+        public bool Supports(IHasImages item)
         {
             return item is Series;
         }
 
-        public async Task<IEnumerable<RemoteImageInfo>> GetImages(BaseItem item, ImageType imageType, CancellationToken cancellationToken)
+        public async Task<IEnumerable<RemoteImageInfo>> GetImages(IHasImages item, ImageType imageType, CancellationToken cancellationToken)
         {
             var images = await GetAllImages(item, cancellationToken).ConfigureAwait(false);
 
             return images.Where(i => i.Type == imageType);
         }
 
-        public Task<IEnumerable<RemoteImageInfo>> GetAllImages(BaseItem item, CancellationToken cancellationToken)
+        public Task<IEnumerable<RemoteImageInfo>> GetAllImages(IHasImages item, CancellationToken cancellationToken)
         {
             var list = new List<RemoteImageInfo>();
 

+ 3 - 3
MediaBrowser.Providers/TV/ManualTvdbEpisodeImageProvider.cs

@@ -31,19 +31,19 @@ namespace MediaBrowser.Providers.TV
             get { return "TheTVDB"; }
         }
 
-        public bool Supports(BaseItem item)
+        public bool Supports(IHasImages item)
         {
             return item is Episode;
         }
 
-        public async Task<IEnumerable<RemoteImageInfo>> GetImages(BaseItem item, ImageType imageType, CancellationToken cancellationToken)
+        public async Task<IEnumerable<RemoteImageInfo>> GetImages(IHasImages item, ImageType imageType, CancellationToken cancellationToken)
         {
             var images = await GetAllImages(item, cancellationToken).ConfigureAwait(false);
 
             return images.Where(i => i.Type == imageType);
         }
 
-        public Task<IEnumerable<RemoteImageInfo>> GetAllImages(BaseItem item, CancellationToken cancellationToken)
+        public Task<IEnumerable<RemoteImageInfo>> GetAllImages(IHasImages item, CancellationToken cancellationToken)
         {
             var episode = (Episode)item;
 

+ 3 - 3
MediaBrowser.Providers/TV/ManualTvdbPersonImageProvider.cs

@@ -37,19 +37,19 @@ namespace MediaBrowser.Providers.TV
             get { return "TheTVDB"; }
         }
 
-        public bool Supports(BaseItem item)
+        public bool Supports(IHasImages item)
         {
             return item is Person;
         }
 
-        public async Task<IEnumerable<RemoteImageInfo>> GetImages(BaseItem item, ImageType imageType, CancellationToken cancellationToken)
+        public async Task<IEnumerable<RemoteImageInfo>> GetImages(IHasImages item, ImageType imageType, CancellationToken cancellationToken)
         {
             var images = await GetAllImages(item, cancellationToken).ConfigureAwait(false);
 
             return images.Where(i => i.Type == imageType);
         }
 
-        public Task<IEnumerable<RemoteImageInfo>> GetAllImages(BaseItem item, CancellationToken cancellationToken)
+        public Task<IEnumerable<RemoteImageInfo>> GetAllImages(IHasImages item, CancellationToken cancellationToken)
         {
             var seriesWithPerson = _library.RootFolder
                 .RecursiveChildren

+ 3 - 3
MediaBrowser.Providers/TV/ManualTvdbSeasonImageProvider.cs

@@ -38,19 +38,19 @@ namespace MediaBrowser.Providers.TV
             get { return "TheTVDB"; }
         }
 
-        public bool Supports(BaseItem item)
+        public bool Supports(IHasImages item)
         {
             return item is Season;
         }
 
-        public async Task<IEnumerable<RemoteImageInfo>> GetImages(BaseItem item, ImageType imageType, CancellationToken cancellationToken)
+        public async Task<IEnumerable<RemoteImageInfo>> GetImages(IHasImages item, ImageType imageType, CancellationToken cancellationToken)
         {
             var images = await GetAllImages(item, cancellationToken).ConfigureAwait(false);
 
             return images.Where(i => i.Type == imageType);
         }
 
-        public Task<IEnumerable<RemoteImageInfo>> GetAllImages(BaseItem item, CancellationToken cancellationToken)
+        public Task<IEnumerable<RemoteImageInfo>> GetAllImages(IHasImages item, CancellationToken cancellationToken)
         {
             var season = (Season)item;
 

+ 5 - 4
MediaBrowser.Providers/TV/ManualTvdbSeriesImageProvider.cs

@@ -38,21 +38,22 @@ namespace MediaBrowser.Providers.TV
             get { return "TheTVDB"; }
         }
 
-        public bool Supports(BaseItem item)
+        public bool Supports(IHasImages item)
         {
             return item is Series;
         }
 
-        public async Task<IEnumerable<RemoteImageInfo>> GetImages(BaseItem item, ImageType imageType, CancellationToken cancellationToken)
+        public async Task<IEnumerable<RemoteImageInfo>> GetImages(IHasImages item, ImageType imageType, CancellationToken cancellationToken)
         {
             var images = await GetAllImages(item, cancellationToken).ConfigureAwait(false);
 
             return images.Where(i => i.Type == imageType);
         }
 
-        public Task<IEnumerable<RemoteImageInfo>> GetAllImages(BaseItem item, CancellationToken cancellationToken)
+        public Task<IEnumerable<RemoteImageInfo>> GetAllImages(IHasImages item, CancellationToken cancellationToken)
         {
-            var seriesId = item.GetProviderId(MetadataProviders.Tvdb);
+            var series = (Series)item;
+            var seriesId = series.GetProviderId(MetadataProviders.Tvdb);
 
             if (!string.IsNullOrEmpty(seriesId))
             {

+ 48 - 0
MediaBrowser.Providers/VirtualItemImageValidator.cs

@@ -0,0 +1,48 @@
+using MediaBrowser.Controller.Configuration;
+using MediaBrowser.Controller.Entities;
+using MediaBrowser.Controller.Providers;
+using MediaBrowser.Model.Entities;
+using MediaBrowser.Model.Logging;
+using System;
+using System.Threading;
+using System.Threading.Tasks;
+
+namespace MediaBrowser.Providers
+{
+    public class VirtualItemImageValidator : BaseMetadataProvider
+    {
+        public VirtualItemImageValidator(ILogManager logManager, IServerConfigurationManager configurationManager) 
+            : base(logManager, configurationManager)
+        {
+        }
+
+        public override bool Supports(BaseItem item)
+        {
+            var locationType = item.LocationType;
+
+            return locationType == LocationType.Virtual ||
+                   locationType == LocationType.Remote;
+        }
+
+        public override Task<bool> FetchAsync(BaseItem item, bool force, BaseProviderInfo providerInfo, CancellationToken cancellationToken)
+        {
+            item.ValidateImages();
+            item.ValidateBackdrops();
+
+            var hasScreenshots = item as IHasScreenshots;
+
+            if (hasScreenshots != null)
+            {
+                hasScreenshots.ValidateScreenshots();
+            }
+
+            SetLastRefreshed(item, DateTime.UtcNow, providerInfo);
+            return TrueTaskResult;
+        }
+
+        public override MetadataProviderPriority Priority
+        {
+            get { return MetadataProviderPriority.First; }
+        }
+    }
+}

+ 7 - 5
MediaBrowser.Server.Implementations/LiveTv/LiveTvDtoService.cs

@@ -243,18 +243,20 @@ namespace MediaBrowser.Server.Implementations.LiveTv
             return dto;
         }
 
-        public ProgramInfoDto GetProgramInfoDto(ProgramInfo program, LiveTvChannel channel, User user = null)
+        public ProgramInfoDto GetProgramInfoDto(LiveTvProgram item, User user = null)
         {
+            var program = item.ProgramInfo;
+            
             var dto = new ProgramInfoDto
             {
-                Id = GetInternalProgramId(channel.ServiceName, program.Id).ToString("N"),
-                ChannelId = channel.Id.ToString("N"),
+                Id = GetInternalProgramId(item.ServiceName, program.Id).ToString("N"),
+                ChannelId = GetInternalChannelId(item.ServiceName, program.ChannelId, program.ChannelName).ToString("N"),
                 Overview = program.Overview,
                 EndDate = program.EndDate,
                 Genres = program.Genres,
                 ExternalId = program.Id,
                 Name = program.Name,
-                ServiceName = channel.ServiceName,
+                ServiceName = item.ServiceName,
                 StartDate = program.StartDate,
                 OfficialRating = program.OfficialRating,
                 IsHD = program.IsHD,
@@ -277,7 +279,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv
 
             if (user != null)
             {
-                //dto.UserData = _dtoService.GetUserItemDataDto(_userDataManager.GetUserData(user.Id, info.GetUserDataKey()));
+                dto.UserData = _dtoService.GetUserItemDataDto(_userDataManager.GetUserData(user.Id, item.GetUserDataKey()));
             }
 
             return dto;

+ 103 - 20
MediaBrowser.Server.Implementations/LiveTv/LiveTvManager.cs

@@ -37,7 +37,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv
         private readonly List<ILiveTvService> _services = new List<ILiveTvService>();
 
         private List<LiveTvChannel> _channels = new List<LiveTvChannel>();
-        private List<ProgramInfoDto> _programs = new List<ProgramInfoDto>();
+        private List<LiveTvProgram> _programs = new List<LiveTvProgram>();
 
         public LiveTvManager(IServerApplicationPaths appPaths, IFileSystem fileSystem, ILogger logger, IItemRepository itemRepo, IImageProcessor imageProcessor, ILocalizationManager localization, IUserDataManager userDataManager, IDtoService dtoService, IUserManager userManager)
         {
@@ -138,7 +138,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv
 
             return await GetRecording(recording, service.Name, cancellationToken).ConfigureAwait(false);
         }
-        
+
         private async Task<LiveTvChannel> GetChannel(ChannelInfo channelInfo, string serviceName, CancellationToken cancellationToken)
         {
             var path = Path.Combine(_appPaths.ItemsByNamePath, "channels", _fileSystem.GetValidFilename(serviceName), _fileSystem.GetValidFilename(channelInfo.Name));
@@ -189,6 +189,35 @@ namespace MediaBrowser.Server.Implementations.LiveTv
             return item;
         }
 
+        private async Task<LiveTvProgram> GetProgram(ProgramInfo info, string serviceName, CancellationToken cancellationToken)
+        {
+            var isNew = false;
+
+            var id = _tvDtoService.GetInternalProgramId(serviceName, info.Id);
+
+            var item = _itemRepo.RetrieveItem(id) as LiveTvProgram;
+
+            if (item == null)
+            {
+                item = new LiveTvProgram
+                {
+                    Name = info.Name,
+                    Id = id,
+                    DateCreated = DateTime.UtcNow,
+                    DateModified = DateTime.UtcNow
+                };
+
+                isNew = true;
+            }
+
+            item.ProgramInfo = info;
+            item.ServiceName = serviceName;
+
+            await item.RefreshMetadata(cancellationToken, forceSave: isNew, resetResolveArgs: false);
+
+            return item;
+        }
+
         private async Task<LiveTvRecording> GetRecording(RecordingInfo info, string serviceName, CancellationToken cancellationToken)
         {
             var isNew = false;
@@ -218,27 +247,50 @@ namespace MediaBrowser.Server.Implementations.LiveTv
             return item;
         }
 
-        public Task<ProgramInfoDto> GetProgram(string id, CancellationToken cancellationToken, User user = null)
+        public async Task<ProgramInfoDto> GetProgram(string id, CancellationToken cancellationToken, User user = null)
         {
-            var program = _programs.FirstOrDefault(i => string.Equals(i.Id, id, StringComparison.OrdinalIgnoreCase));
+            var guid = new Guid(id);
+            var program = _programs.FirstOrDefault(i => guid == i.Id);
+
+            var dto = _tvDtoService.GetProgramInfoDto(program, user);
+
+            await AddRecordingInfo(new[] { dto }, cancellationToken).ConfigureAwait(false);
 
-            return Task.FromResult(program);
+            return dto;
         }
 
-        public Task<QueryResult<ProgramInfoDto>> GetPrograms(ProgramQuery query, CancellationToken cancellationToken)
+        public async Task<QueryResult<ProgramInfoDto>> GetPrograms(ProgramQuery query, CancellationToken cancellationToken)
         {
-            IEnumerable<ProgramInfoDto> programs = _programs
-                .OrderBy(i => i.StartDate)
-                .ThenBy(i => i.EndDate);
+            IEnumerable<LiveTvProgram> programs = _programs;
 
             if (query.ChannelIdList.Length > 0)
             {
                 var guids = query.ChannelIdList.Select(i => new Guid(i)).ToList();
+                var serviceName = ActiveService.Name;
+
+                programs = programs.Where(i =>
+                {
+                    var programChannelId = i.ProgramInfo.ChannelId;
+
+                    var internalProgramChannelId = _tvDtoService.GetInternalChannelId(serviceName, programChannelId, i.ProgramInfo.ChannelName);
 
-                programs = programs.Where(i => guids.Contains(new Guid(i.ChannelId)));
+                    return guids.Contains(internalProgramChannelId);
+                });
             }
 
-            var returnArray = programs.ToArray();
+            var user = string.IsNullOrEmpty(query.UserId) ? null : _userManager.GetUserById(new Guid(query.UserId));
+
+            if (user != null)
+            {
+                programs = programs.Where(i => i.IsParentalAllowed(user, _localization));
+            }
+
+            var returnArray = programs
+                .OrderBy(i => i.ProgramInfo.StartDate)
+                .Select(i => _tvDtoService.GetProgramInfoDto(i, user))
+                .ToArray();
+
+            await AddRecordingInfo(returnArray, cancellationToken).ConfigureAwait(false);
 
             var result = new QueryResult<ProgramInfoDto>
             {
@@ -246,7 +298,31 @@ namespace MediaBrowser.Server.Implementations.LiveTv
                 TotalRecordCount = returnArray.Length
             };
 
-            return Task.FromResult(result);
+            return result;
+        }
+
+        private async Task AddRecordingInfo(IEnumerable<ProgramInfoDto> programs, CancellationToken cancellationToken)
+        {
+            var timers = await ActiveService.GetTimersAsync(cancellationToken).ConfigureAwait(false);
+            var timerList = timers.ToList();
+
+            foreach (var program in programs)
+            {
+                var timer = timerList.FirstOrDefault(i => string.Equals(i.ProgramId, program.ExternalId, StringComparison.OrdinalIgnoreCase));
+
+                if (timer != null)
+                {
+                    program.TimerId = _tvDtoService.GetInternalTimerId(program.ServiceName, timer.Id)
+                        .ToString("N");
+
+                    if (!string.IsNullOrEmpty(timer.SeriesTimerId))
+                    {
+                        program.SeriesTimerId = _tvDtoService.GetInternalSeriesTimerId(program.ServiceName, timer.SeriesTimerId)
+                            .ToString("N");
+                    }
+                }
+            }
+
         }
 
         internal async Task RefreshChannels(IProgress<double> progress, CancellationToken cancellationToken)
@@ -266,7 +342,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv
             var allChannelsList = allChannels.ToList();
 
             var list = new List<LiveTvChannel>();
-            var programs = new List<ProgramInfoDto>();
+            var programs = new List<LiveTvProgram>();
 
             var numComplete = 0;
 
@@ -278,7 +354,10 @@ namespace MediaBrowser.Server.Implementations.LiveTv
 
                     var channelPrograms = await service.GetProgramsAsync(channelInfo.Item2.Id, cancellationToken).ConfigureAwait(false);
 
-                    programs.AddRange(channelPrograms.Select(program => _tvDtoService.GetProgramInfoDto(program, item)));
+                    var programTasks = channelPrograms.Select(program => GetProgram(program, service.Name, cancellationToken));
+                    var programEntities = await Task.WhenAll(programTasks).ConfigureAwait(false);
+
+                    programs.AddRange(programEntities);
 
                     list.Add(item);
                 }
@@ -336,6 +415,11 @@ namespace MediaBrowser.Server.Implementations.LiveTv
 
             var entities = await GetEntities(list, service.Name, cancellationToken).ConfigureAwait(false);
 
+            if (user != null)
+            {
+                entities = entities.Where(i => i.IsParentalAllowed(user, _localization)).ToArray();
+            }
+
             var returnArray = entities
                 .Select(i => _tvDtoService.GetRecordingInfoDto(i, ActiveService, user))
                 .OrderByDescending(i => i.StartDate)
@@ -504,15 +588,14 @@ namespace MediaBrowser.Server.Implementations.LiveTv
             };
         }
 
-        public async Task<ChannelInfoDto> GetChannel(string id, CancellationToken cancellationToken, User user = null)
+        public Task<ChannelInfoDto> GetChannel(string id, CancellationToken cancellationToken, User user = null)
         {
-            var results = await GetChannels(new ChannelQuery
-            {
-                UserId = user == null ? null : user.Id.ToString("N")
+            var guid = new Guid(id);
+            var channel = _channels.FirstOrDefault(i => guid == i.Id);
 
-            }, cancellationToken).ConfigureAwait(false);
+            var dto = _tvDtoService.GetChannelInfoDto(channel, user);
 
-            return results.Items.FirstOrDefault(i => string.Equals(i.Id, id, StringComparison.CurrentCulture));
+            return Task.FromResult(dto);
         }
 
         public async Task<SeriesTimerInfoDto> GetNewTimerDefaults(CancellationToken cancellationToken)

+ 3 - 0
MediaBrowser.WebDashboard/MediaBrowser.WebDashboard.csproj

@@ -85,6 +85,9 @@
   </ItemGroup>
   <ItemGroup>
     <EmbeddedResource Include="ApiClient.js" />
+    <Content Include="dashboard-ui\css\images\clients\xbmc.png">
+      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
+    </Content>
     <Content Include="dashboard-ui\css\images\editor.png">
       <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
     </Content>

+ 2 - 2
Nuget/MediaBrowser.Common.Internal.nuspec

@@ -2,7 +2,7 @@
 <package xmlns="http://schemas.microsoft.com/packaging/2011/08/nuspec.xsd">
     <metadata>
         <id>MediaBrowser.Common.Internal</id>
-        <version>3.0.284</version>
+        <version>3.0.285</version>
         <title>MediaBrowser.Common.Internal</title>
         <authors>Luke</authors>
         <owners>ebr,Luke,scottisafool</owners>
@@ -12,7 +12,7 @@
         <description>Contains common components shared by Media Browser Theater and Media Browser Server. Not intended for plugin developer consumption.</description>
         <copyright>Copyright © Media Browser 2013</copyright>
         <dependencies>
-            <dependency id="MediaBrowser.Common" version="3.0.284" />
+            <dependency id="MediaBrowser.Common" version="3.0.285" />
             <dependency id="NLog" version="2.1.0" />
             <dependency id="SimpleInjector" version="2.4.0" />
             <dependency id="sharpcompress" version="0.10.2" />

+ 1 - 1
Nuget/MediaBrowser.Common.nuspec

@@ -2,7 +2,7 @@
 <package xmlns="http://schemas.microsoft.com/packaging/2011/08/nuspec.xsd">
     <metadata>
         <id>MediaBrowser.Common</id>
-        <version>3.0.284</version>
+        <version>3.0.285</version>
         <title>MediaBrowser.Common</title>
         <authors>Media Browser Team</authors>
         <owners>ebr,Luke,scottisafool</owners>

+ 2 - 2
Nuget/MediaBrowser.Server.Core.nuspec

@@ -2,7 +2,7 @@
 <package xmlns="http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd">
     <metadata>
         <id>MediaBrowser.Server.Core</id>
-        <version>3.0.284</version>
+        <version>3.0.285</version>
         <title>Media Browser.Server.Core</title>
         <authors>Media Browser Team</authors>
         <owners>ebr,Luke,scottisafool</owners>
@@ -12,7 +12,7 @@
         <description>Contains core components required to build plugins for Media Browser Server.</description>
         <copyright>Copyright © Media Browser 2013</copyright>
         <dependencies>
-            <dependency id="MediaBrowser.Common" version="3.0.284" />
+            <dependency id="MediaBrowser.Common" version="3.0.285" />
         </dependencies>
     </metadata>
     <files>