فهرست منبع

live tv updates

Luke Pulverenti 11 سال پیش
والد
کامیت
f20f3b4b68

+ 7 - 0
MediaBrowser.Api/LiveTv/LiveTvImageService.cs

@@ -152,6 +152,13 @@ namespace MediaBrowser.Api.LiveTv
             return GetImageService().GetImage(request, item);
             return GetImageService().GetImage(request, item);
         }
         }
 
 
+        public object Get(GetProgramImage request)
+        {
+            var item = _liveTv.GetInternalProgram(request.Id);
+
+            return GetImageService().GetImage(request, item);
+        }
+
         public void Post(PostChannelImage request)
         public void Post(PostChannelImage request)
         {
         {
             var pathInfo = PathInfo.Parse(Request.PathInfo);
             var pathInfo = PathInfo.Parse(Request.PathInfo);

+ 7 - 0
MediaBrowser.Controller/LiveTv/ChannelInfo.cs

@@ -42,5 +42,12 @@ namespace MediaBrowser.Controller.LiveTv
         /// </summary>
         /// </summary>
         /// <value>The image URL.</value>
         /// <value>The image URL.</value>
         public string ImageUrl { get; set; }
         public string ImageUrl { get; set; }
+
+        /// <summary>
+        /// Gets or sets a value indicating whether this instance has image.
+        /// </summary>
+        /// <value><c>null</c> if [has image] contains no value, <c>true</c> if [has image]; otherwise, <c>false</c>.</value>
+        public bool? HasImage { get; set; }
+
     }
     }
 }
 }

+ 7 - 7
MediaBrowser.Controller/LiveTv/ILiveTvManager.cs

@@ -24,13 +24,6 @@ namespace MediaBrowser.Controller.LiveTv
         /// <value>The services.</value>
         /// <value>The services.</value>
         IReadOnlyList<ILiveTvService> Services { get; }
         IReadOnlyList<ILiveTvService> Services { get; }
 
 
-        /// <summary>
-        /// Schedules the recording.
-        /// </summary>
-        /// <param name="programId">The program identifier.</param>
-        /// <returns>Task.</returns>
-        Task ScheduleRecording(string programId);
-
         /// <summary>
         /// <summary>
         /// Gets the new timer defaults asynchronous.
         /// Gets the new timer defaults asynchronous.
         /// </summary>
         /// </summary>
@@ -146,6 +139,13 @@ namespace MediaBrowser.Controller.LiveTv
         /// <returns>Channel.</returns>
         /// <returns>Channel.</returns>
         LiveTvChannel GetInternalChannel(string id);
         LiveTvChannel GetInternalChannel(string id);
 
 
+        /// <summary>
+        /// Gets the internal program.
+        /// </summary>
+        /// <param name="id">The identifier.</param>
+        /// <returns>LiveTvProgram.</returns>
+        LiveTvProgram GetInternalProgram(string id);
+        
         /// <summary>
         /// <summary>
         /// Gets the recording.
         /// Gets the recording.
         /// </summary>
         /// </summary>

+ 17 - 0
MediaBrowser.Controller/LiveTv/ILiveTvService.cs

@@ -1,4 +1,5 @@
 using System.Collections.Generic;
 using System.Collections.Generic;
+using System.IO;
 using System.Threading;
 using System.Threading;
 using System.Threading.Tasks;
 using System.Threading.Tasks;
 
 
@@ -138,5 +139,21 @@ namespace MediaBrowser.Controller.LiveTv
         /// <param name="cancellationToken">The cancellation token.</param>
         /// <param name="cancellationToken">The cancellation token.</param>
         /// <returns>Task{IEnumerable{ProgramInfo}}.</returns>
         /// <returns>Task{IEnumerable{ProgramInfo}}.</returns>
         Task<IEnumerable<ProgramInfo>> GetProgramsAsync(string channelId, CancellationToken cancellationToken);
         Task<IEnumerable<ProgramInfo>> GetProgramsAsync(string channelId, CancellationToken cancellationToken);
+
+        /// <summary>
+        /// Gets the recording stream.
+        /// </summary>
+        /// <param name="recordingId">The recording identifier.</param>
+        /// <param name="cancellationToken">The cancellation token.</param>
+        /// <returns>Task{Stream}.</returns>
+        Task<Stream> GetRecordingStream(string recordingId, CancellationToken cancellationToken);
+
+        /// <summary>
+        /// Gets the channel stream.
+        /// </summary>
+        /// <param name="recordingId">The recording identifier.</param>
+        /// <param name="cancellationToken">The cancellation token.</param>
+        /// <returns>Task{Stream}.</returns>
+        Task<Stream> GetChannelStream(string recordingId, CancellationToken cancellationToken);
     }
     }
 }
 }

+ 6 - 0
MediaBrowser.Controller/LiveTv/ProgramInfo.cs

@@ -157,6 +157,12 @@ namespace MediaBrowser.Controller.LiveTv
         /// <value><c>true</c> if this instance is premiere; otherwise, <c>false</c>.</value>
         /// <value><c>true</c> if this instance is premiere; otherwise, <c>false</c>.</value>
         public bool IsPremiere { get; set;  }
         public bool IsPremiere { get; set;  }
 
 
+        /// <summary>
+        /// Gets or sets a value indicating whether this instance has image.
+        /// </summary>
+        /// <value><c>null</c> if [has image] contains no value, <c>true</c> if [has image]; otherwise, <c>false</c>.</value>
+        public bool? HasImage { get; set; }
+
         public ProgramInfo()
         public ProgramInfo()
         {
         {
             Genres = new List<string>();
             Genres = new List<string>();

+ 13 - 0
MediaBrowser.Controller/LiveTv/RecordingInfo.cs

@@ -11,6 +11,12 @@ namespace MediaBrowser.Controller.LiveTv
         /// </summary>
         /// </summary>
         public string Id { get; set; }
         public string Id { get; set; }
 
 
+        /// <summary>
+        /// Gets or sets the series timer identifier.
+        /// </summary>
+        /// <value>The series timer identifier.</value>
+        public string SeriesTimerId { get; set; }
+        
         /// <summary>
         /// <summary>
         /// ChannelId of the recording.
         /// ChannelId of the recording.
         /// </summary>
         /// </summary>
@@ -167,6 +173,13 @@ namespace MediaBrowser.Controller.LiveTv
         /// <value>The image URL.</value>
         /// <value>The image URL.</value>
         public string ImageUrl { get; set; }
         public string ImageUrl { get; set; }
 
 
+        /// <summary>
+        /// Gets or sets a value indicating whether this instance has image.
+        /// </summary>
+        /// <value><c>null</c> if [has image] contains no value, <c>true</c> if [has image]; otherwise, <c>false</c>.</value>
+        public bool? HasImage { get; set; }
+
+
         public RecordingInfo()
         public RecordingInfo()
         {
         {
             Genres = new List<string>();
             Genres = new List<string>();

+ 6 - 0
MediaBrowser.Model/LiveTv/RecordingInfoDto.cs

@@ -12,6 +12,12 @@ namespace MediaBrowser.Model.LiveTv
         /// </summary>
         /// </summary>
         public string Id { get; set; }
         public string Id { get; set; }
 
 
+        /// <summary>
+        /// Gets or sets the series timer identifier.
+        /// </summary>
+        /// <value>The series timer identifier.</value>
+        public string SeriesTimerId { get; set; }
+        
         /// <summary>
         /// <summary>
         /// Gets or sets the external identifier.
         /// Gets or sets the external identifier.
         /// </summary>
         /// </summary>

+ 6 - 0
MediaBrowser.Model/LiveTv/TimerInfoDto.cs

@@ -122,5 +122,11 @@ namespace MediaBrowser.Model.LiveTv
         /// </summary>
         /// </summary>
         /// <value>The priority.</value>
         /// <value>The priority.</value>
         public int Priority { get; set; }
         public int Priority { get; set; }
+
+        /// <summary>
+        /// Gets or sets the program information.
+        /// </summary>
+        /// <value>The program information.</value>
+        public ProgramInfoDto ProgramInfo { get; set; }
     }
     }
 }
 }

+ 1 - 1
MediaBrowser.Server.Implementations/Library/Resolvers/Movies/MovieResolver.cs

@@ -179,7 +179,7 @@ namespace MediaBrowser.Server.Implementations.Library.Resolvers.Movies
         private void SetProviderIdFromPath(Video item)
         private void SetProviderIdFromPath(Video item)
         {
         {
             //we need to only look at the name of this actual item (not parents)
             //we need to only look at the name of this actual item (not parents)
-            var justName = item.IsInMixedFolder ? Path.GetFileName(item.Path) : Path.GetFileName(Path.GetDirectoryName(item.Path));
+            var justName = item.IsInMixedFolder ? Path.GetFileName(item.Path) : Path.GetFileName(item.MetaLocation);
 
 
             var id = justName.GetAttributeValue("tmdbid");
             var id = justName.GetAttributeValue("tmdbid");
 
 

+ 30 - 10
MediaBrowser.Server.Implementations/LiveTv/ChannelImageProvider.cs

@@ -51,9 +51,11 @@ namespace MediaBrowser.Server.Implementations.LiveTv
                 return true;
                 return true;
             }
             }
 
 
+            var changed = true;
+
             try
             try
             {
             {
-                await DownloadImage((LiveTvChannel)item, cancellationToken).ConfigureAwait(false);
+                changed = await DownloadImage((LiveTvChannel)item, cancellationToken).ConfigureAwait(false);
             }
             }
             catch (HttpException ex)
             catch (HttpException ex)
             {
             {
@@ -64,11 +66,15 @@ namespace MediaBrowser.Server.Implementations.LiveTv
                 }
                 }
             }
             }
 
 
-            SetLastRefreshed(item, DateTime.UtcNow, providerInfo);
-            return true;
+            if (changed)
+            {
+                SetLastRefreshed(item, DateTime.UtcNow, providerInfo);
+            }
+
+            return changed;
         }
         }
 
 
-        private async Task DownloadImage(LiveTvChannel item, CancellationToken cancellationToken)
+        private async Task<bool> DownloadImage(LiveTvChannel item, CancellationToken cancellationToken)
         {
         {
             var channelInfo = item.ChannelInfo;
             var channelInfo = item.ChannelInfo;
 
 
@@ -92,22 +98,33 @@ namespace MediaBrowser.Server.Implementations.LiveTv
 
 
                 if (!response.ContentType.StartsWith("image/", StringComparison.OrdinalIgnoreCase))
                 if (!response.ContentType.StartsWith("image/", StringComparison.OrdinalIgnoreCase))
                 {
                 {
-                    throw new InvalidOperationException("Provider did not return an image content type.");
+                    Logger.Error("Provider did not return an image content type.");
+                    return false;
                 }
                 }
 
 
                 imageStream = response.Content;
                 imageStream = response.Content;
                 contentType = response.ContentType;
                 contentType = response.ContentType;
             }
             }
-            else
+            else if (channelInfo.HasImage ?? true)
             {
             {
                 var service = _liveTvManager.Services.FirstOrDefault(i => string.Equals(i.Name, item.ServiceName, StringComparison.OrdinalIgnoreCase));
                 var service = _liveTvManager.Services.FirstOrDefault(i => string.Equals(i.Name, item.ServiceName, StringComparison.OrdinalIgnoreCase));
 
 
                 if (service != null)
                 if (service != null)
                 {
                 {
-                    var response = await service.GetChannelImageAsync(channelInfo.Id, cancellationToken).ConfigureAwait(false);
-
-                    imageStream = response.Stream;
-                    contentType = response.MimeType;
+                    try
+                    {
+                        var response = await service.GetChannelImageAsync(channelInfo.Id, cancellationToken).ConfigureAwait(false);
+
+                        if (response != null)
+                        {
+                            imageStream = response.Stream;
+                            contentType = response.MimeType;
+                        }
+                    }
+                    catch (NotImplementedException)
+                    {
+                        return false;
+                    }
                 }
                 }
             }
             }
 
 
@@ -117,7 +134,10 @@ namespace MediaBrowser.Server.Implementations.LiveTv
                 var url = item.ServiceName + channelInfo.Id;
                 var url = item.ServiceName + channelInfo.Id;
 
 
                 await _providerManager.SaveImage(item, imageStream, contentType, ImageType.Primary, null, url, cancellationToken).ConfigureAwait(false);
                 await _providerManager.SaveImage(item, imageStream, contentType, ImageType.Primary, null, url, cancellationToken).ConfigureAwait(false);
+                return true;
             }
             }
+
+            return false;
         }
         }
 
 
         public override MetadataProviderPriority Priority
         public override MetadataProviderPriority Priority

+ 10 - 1
MediaBrowser.Server.Implementations/LiveTv/LiveTvDtoService.cs

@@ -30,7 +30,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv
             _logger = logger;
             _logger = logger;
         }
         }
 
 
-        public TimerInfoDto GetTimerInfoDto(TimerInfo info, ILiveTvService service)
+        public TimerInfoDto GetTimerInfoDto(TimerInfo info, ILiveTvService service, LiveTvProgram program)
         {
         {
             var dto = new TimerInfoDto
             var dto = new TimerInfoDto
             {
             {
@@ -61,6 +61,14 @@ namespace MediaBrowser.Server.Implementations.LiveTv
                 dto.ProgramId = GetInternalProgramId(service.Name, info.ProgramId).ToString("N");
                 dto.ProgramId = GetInternalProgramId(service.Name, info.ProgramId).ToString("N");
             }
             }
 
 
+            if (program != null)
+            {
+                dto.ProgramInfo = GetProgramInfoDto(program);
+
+                dto.ProgramInfo.TimerId = dto.Id;
+                dto.ProgramInfo.SeriesTimerId = dto.SeriesTimerId;
+            }
+
             return dto;
             return dto;
         }
         }
 
 
@@ -155,6 +163,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv
             var dto = new RecordingInfoDto
             var dto = new RecordingInfoDto
             {
             {
                 Id = GetInternalRecordingId(service.Name, info.Id).ToString("N"),
                 Id = GetInternalRecordingId(service.Name, info.Id).ToString("N"),
+                SeriesTimerId = string.IsNullOrEmpty(info.SeriesTimerId) ? null : GetInternalSeriesTimerId(service.Name, info.SeriesTimerId).ToString("N"),
                 Type = recording.GetClientTypeName(),
                 Type = recording.GetClientTypeName(),
                 ChannelName = info.ChannelName,
                 ChannelName = info.ChannelName,
                 Overview = info.Overview,
                 Overview = info.Overview,

+ 39 - 33
MediaBrowser.Server.Implementations/LiveTv/LiveTvManager.cs

@@ -36,8 +36,8 @@ namespace MediaBrowser.Server.Implementations.LiveTv
 
 
         private readonly List<ILiveTvService> _services = new List<ILiveTvService>();
         private readonly List<ILiveTvService> _services = new List<ILiveTvService>();
 
 
-        private List<LiveTvChannel> _channels = new List<LiveTvChannel>();
-        private List<LiveTvProgram> _programs = new List<LiveTvProgram>();
+        private Dictionary<Guid, LiveTvChannel> _channels = new Dictionary<Guid, LiveTvChannel>();
+        private Dictionary<Guid, LiveTvProgram> _programs = new Dictionary<Guid, LiveTvProgram>();
 
 
         public LiveTvManager(IServerApplicationPaths appPaths, IFileSystem fileSystem, ILogger logger, IItemRepository itemRepo, IImageProcessor imageProcessor, ILocalizationManager localization, IUserDataManager userDataManager, IDtoService dtoService, IUserManager userManager)
         public LiveTvManager(IServerApplicationPaths appPaths, IFileSystem fileSystem, ILogger logger, IItemRepository itemRepo, IImageProcessor imageProcessor, ILocalizationManager localization, IUserDataManager userDataManager, IDtoService dtoService, IUserManager userManager)
         {
         {
@@ -77,7 +77,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv
         {
         {
             var user = string.IsNullOrEmpty(query.UserId) ? null : _userManager.GetUserById(new Guid(query.UserId));
             var user = string.IsNullOrEmpty(query.UserId) ? null : _userManager.GetUserById(new Guid(query.UserId));
 
 
-            IEnumerable<LiveTvChannel> channels = _channels;
+            IEnumerable<LiveTvChannel> channels = _channels.Values;
 
 
             if (user != null)
             if (user != null)
             {
             {
@@ -125,7 +125,20 @@ namespace MediaBrowser.Server.Implementations.LiveTv
         {
         {
             var guid = new Guid(id);
             var guid = new Guid(id);
 
 
-            return _channels.FirstOrDefault(i => i.Id == guid);
+            LiveTvChannel channel = null;
+
+            _channels.TryGetValue(guid, out channel);
+            return channel;
+        }
+
+        public LiveTvProgram GetInternalProgram(string id)
+        {
+            var guid = new Guid(id);
+
+            LiveTvProgram obj = null;
+
+            _programs.TryGetValue(guid, out obj);
+            return obj;
         }
         }
 
 
         public async Task<LiveTvRecording> GetInternalRecording(string id, CancellationToken cancellationToken)
         public async Task<LiveTvRecording> GetInternalRecording(string id, CancellationToken cancellationToken)
@@ -249,8 +262,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv
 
 
         public async Task<ProgramInfoDto> GetProgram(string id, CancellationToken cancellationToken, User user = null)
         public async Task<ProgramInfoDto> GetProgram(string id, CancellationToken cancellationToken, User user = null)
         {
         {
-            var guid = new Guid(id);
-            var program = _programs.FirstOrDefault(i => guid == i.Id);
+            var program = GetInternalProgram(id);
 
 
             var dto = _tvDtoService.GetProgramInfoDto(program, user);
             var dto = _tvDtoService.GetProgramInfoDto(program, user);
 
 
@@ -261,7 +273,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv
 
 
         public async Task<QueryResult<ProgramInfoDto>> GetPrograms(ProgramQuery query, CancellationToken cancellationToken)
         public async Task<QueryResult<ProgramInfoDto>> GetPrograms(ProgramQuery query, CancellationToken cancellationToken)
         {
         {
-            IEnumerable<LiveTvProgram> programs = _programs;
+            IEnumerable<LiveTvProgram> programs = _programs.Values;
 
 
             if (query.ChannelIdList.Length > 0)
             if (query.ChannelIdList.Length > 0)
             {
             {
@@ -377,8 +389,8 @@ namespace MediaBrowser.Server.Implementations.LiveTv
                 progress.Report(90 * percent + 10);
                 progress.Report(90 * percent + 10);
             }
             }
 
 
-            _programs = programs;
-            _channels = list;
+            _programs = programs.ToDictionary(i => i.Id);
+            _channels = list.ToDictionary(i => i.Id);
         }
         }
 
 
         private async Task<IEnumerable<Tuple<string, ChannelInfo>>> GetChannels(ILiveTvService service, CancellationToken cancellationToken)
         private async Task<IEnumerable<Tuple<string, ChannelInfo>>> GetChannels(ILiveTvService service, CancellationToken cancellationToken)
@@ -445,11 +457,12 @@ namespace MediaBrowser.Server.Implementations.LiveTv
 
 
             if (string.IsNullOrEmpty(serviceName) && !string.IsNullOrEmpty(channelId))
             if (string.IsNullOrEmpty(serviceName) && !string.IsNullOrEmpty(channelId))
             {
             {
-                var channelIdGuid = new Guid(channelId);
+                var channel = GetInternalChannel(channelId);
 
 
-                serviceName = _channels.Where(i => i.Id == channelIdGuid)
-                    .Select(i => i.ServiceName)
-                    .FirstOrDefault();
+                if (channel != null)
+                {
+                    serviceName = channel.ServiceName;
+                }
             }
             }
 
 
             if (!string.IsNullOrEmpty(serviceName))
             if (!string.IsNullOrEmpty(serviceName))
@@ -460,31 +473,25 @@ namespace MediaBrowser.Server.Implementations.LiveTv
             return services;
             return services;
         }
         }
 
 
-        public Task ScheduleRecording(string programId)
-        {
-            throw new NotImplementedException();
-        }
-
         public async Task<QueryResult<TimerInfoDto>> GetTimers(TimerQuery query, CancellationToken cancellationToken)
         public async Task<QueryResult<TimerInfoDto>> GetTimers(TimerQuery query, CancellationToken cancellationToken)
         {
         {
-            var list = new List<TimerInfoDto>();
-
-            if (ActiveService != null)
-            {
-                var timers = await ActiveService.GetTimersAsync(cancellationToken).ConfigureAwait(false);
-
-                var dtos = timers.Select(i => _tvDtoService.GetTimerInfoDto(i, ActiveService));
-
-                list.AddRange(dtos);
-            }
+            var service = ActiveService;
+            var timers = await service.GetTimersAsync(cancellationToken).ConfigureAwait(false);
 
 
             if (!string.IsNullOrEmpty(query.ChannelId))
             if (!string.IsNullOrEmpty(query.ChannelId))
             {
             {
-                list = list.Where(i => string.Equals(i.ChannelId, query.ChannelId))
-                    .ToList();
+                var guid = new Guid(query.ChannelId);
+                timers = timers.Where(i => guid == _tvDtoService.GetInternalChannelId(service.Name, i.ChannelId, i.ChannelName));
             }
             }
 
 
-            var returnArray = list.OrderBy(i => i.StartDate)
+            var returnArray = timers
+                .Select(i =>
+                {
+                    var program = string.IsNullOrEmpty(i.ProgramId) ? null : GetInternalProgram(_tvDtoService.GetInternalProgramId(service.Name, i.ProgramId).ToString("N"));
+
+                    return _tvDtoService.GetTimerInfoDto(i, ActiveService, program);
+                })
+                .OrderBy(i => i.StartDate)
                 .ToArray();
                 .ToArray();
 
 
             return new QueryResult<TimerInfoDto>
             return new QueryResult<TimerInfoDto>
@@ -590,8 +597,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv
 
 
         public Task<ChannelInfoDto> GetChannel(string id, CancellationToken cancellationToken, User user = null)
         public Task<ChannelInfoDto> GetChannel(string id, CancellationToken cancellationToken, User user = null)
         {
         {
-            var guid = new Guid(id);
-            var channel = _channels.FirstOrDefault(i => guid == i.Id);
+            var channel = GetInternalChannel(id);
 
 
             var dto = _tvDtoService.GetChannelInfoDto(channel, user);
             var dto = _tvDtoService.GetChannelInfoDto(channel, user);
 
 

+ 30 - 10
MediaBrowser.Server.Implementations/LiveTv/ProgramImageProvider.cs

@@ -51,9 +51,11 @@ namespace MediaBrowser.Server.Implementations.LiveTv
                 return true;
                 return true;
             }
             }
 
 
+            var changed = true;
+
             try
             try
             {
             {
-                await DownloadImage((LiveTvProgram)item, cancellationToken).ConfigureAwait(false);
+                changed = await DownloadImage((LiveTvProgram)item, cancellationToken).ConfigureAwait(false);
             }
             }
             catch (HttpException ex)
             catch (HttpException ex)
             {
             {
@@ -64,11 +66,15 @@ namespace MediaBrowser.Server.Implementations.LiveTv
                 }
                 }
             }
             }
 
 
-            SetLastRefreshed(item, DateTime.UtcNow, providerInfo);
-            return true;
+            if (changed)
+            {
+                SetLastRefreshed(item, DateTime.UtcNow, providerInfo);
+            }
+
+            return changed;
         }
         }
 
 
-        private async Task DownloadImage(LiveTvProgram item, CancellationToken cancellationToken)
+        private async Task<bool> DownloadImage(LiveTvProgram item, CancellationToken cancellationToken)
         {
         {
             var programInfo = item.ProgramInfo;
             var programInfo = item.ProgramInfo;
 
 
@@ -92,22 +98,33 @@ namespace MediaBrowser.Server.Implementations.LiveTv
 
 
                 if (!response.ContentType.StartsWith("image/", StringComparison.OrdinalIgnoreCase))
                 if (!response.ContentType.StartsWith("image/", StringComparison.OrdinalIgnoreCase))
                 {
                 {
-                    throw new InvalidOperationException("Provider did not return an image content type.");
+                    Logger.Error("Provider did not return an image content type.");
+                    return false;
                 }
                 }
 
 
                 imageStream = response.Content;
                 imageStream = response.Content;
                 contentType = response.ContentType;
                 contentType = response.ContentType;
             }
             }
-            else
+            else if (programInfo.HasImage ?? true)
             {
             {
                 var service = _liveTvManager.Services.FirstOrDefault(i => string.Equals(i.Name, item.ServiceName, StringComparison.OrdinalIgnoreCase));
                 var service = _liveTvManager.Services.FirstOrDefault(i => string.Equals(i.Name, item.ServiceName, StringComparison.OrdinalIgnoreCase));
 
 
                 if (service != null)
                 if (service != null)
                 {
                 {
-                    var response = await service.GetProgramImageAsync(programInfo.Id, programInfo.ChannelId, cancellationToken).ConfigureAwait(false);
-
-                    imageStream = response.Stream;
-                    contentType = response.MimeType;
+                    try
+                    {
+                        var response = await service.GetProgramImageAsync(programInfo.Id, programInfo.ChannelId, cancellationToken).ConfigureAwait(false);
+
+                        if (response != null)
+                        {
+                            imageStream = response.Stream;
+                            contentType = response.MimeType;
+                        }
+                    }
+                    catch (NotImplementedException)
+                    {
+                        return false;
+                    }
                 }
                 }
             }
             }
 
 
@@ -117,7 +134,10 @@ namespace MediaBrowser.Server.Implementations.LiveTv
                 var url = item.ServiceName + programInfo.Id;
                 var url = item.ServiceName + programInfo.Id;
 
 
                 await _providerManager.SaveImage(item, imageStream, contentType, ImageType.Primary, null, url, cancellationToken).ConfigureAwait(false);
                 await _providerManager.SaveImage(item, imageStream, contentType, ImageType.Primary, null, url, cancellationToken).ConfigureAwait(false);
+                return true;
             }
             }
+
+            return false;
         }
         }
 
 
         public override MetadataProviderPriority Priority
         public override MetadataProviderPriority Priority

+ 30 - 10
MediaBrowser.Server.Implementations/LiveTv/RecordingImageProvider.cs

@@ -51,9 +51,11 @@ namespace MediaBrowser.Server.Implementations.LiveTv
                 return true;
                 return true;
             }
             }
 
 
+            var changed = true;
+
             try
             try
             {
             {
-                await DownloadImage((LiveTvRecording)item, cancellationToken).ConfigureAwait(false);
+                changed = await DownloadImage((LiveTvRecording)item, cancellationToken).ConfigureAwait(false);
             }
             }
             catch (HttpException ex)
             catch (HttpException ex)
             {
             {
@@ -64,11 +66,15 @@ namespace MediaBrowser.Server.Implementations.LiveTv
                 }
                 }
             }
             }
 
 
-            SetLastRefreshed(item, DateTime.UtcNow, providerInfo);
-            return true;
+            if (changed)
+            {
+                SetLastRefreshed(item, DateTime.UtcNow, providerInfo);
+            }
+
+            return changed;
         }
         }
 
 
-        private async Task DownloadImage(LiveTvRecording item, CancellationToken cancellationToken)
+        private async Task<bool> DownloadImage(LiveTvRecording item, CancellationToken cancellationToken)
         {
         {
             var recordingInfo = item.RecordingInfo;
             var recordingInfo = item.RecordingInfo;
 
 
@@ -92,22 +98,33 @@ namespace MediaBrowser.Server.Implementations.LiveTv
 
 
                 if (!response.ContentType.StartsWith("image/", StringComparison.OrdinalIgnoreCase))
                 if (!response.ContentType.StartsWith("image/", StringComparison.OrdinalIgnoreCase))
                 {
                 {
-                    throw new InvalidOperationException("Provider did not return an image content type.");
+                    Logger.Error("Provider did not return an image content type.");
+                    return false;
                 }
                 }
 
 
                 imageStream = response.Content;
                 imageStream = response.Content;
                 contentType = response.ContentType;
                 contentType = response.ContentType;
             }
             }
-            else
+            else if (recordingInfo.HasImage ?? true)
             {
             {
                 var service = _liveTvManager.Services.FirstOrDefault(i => string.Equals(i.Name, item.ServiceName, StringComparison.OrdinalIgnoreCase));
                 var service = _liveTvManager.Services.FirstOrDefault(i => string.Equals(i.Name, item.ServiceName, StringComparison.OrdinalIgnoreCase));
 
 
                 if (service != null)
                 if (service != null)
                 {
                 {
-                    var response = await service.GetRecordingImageAsync(recordingInfo.Id, cancellationToken).ConfigureAwait(false);
-
-                    imageStream = response.Stream;
-                    contentType = response.MimeType;
+                    try
+                    {
+                        var response = await service.GetRecordingImageAsync(recordingInfo.Id, cancellationToken).ConfigureAwait(false);
+
+                        if (response != null)
+                        {
+                            imageStream = response.Stream;
+                            contentType = response.MimeType;
+                        }
+                    }
+                    catch (NotImplementedException)
+                    {
+                        return false;
+                    }
                 }
                 }
             }
             }
 
 
@@ -117,7 +134,10 @@ namespace MediaBrowser.Server.Implementations.LiveTv
                 var url = item.ServiceName + recordingInfo.Id;
                 var url = item.ServiceName + recordingInfo.Id;
 
 
                 await _providerManager.SaveImage(item, imageStream, contentType, ImageType.Primary, null, url, cancellationToken).ConfigureAwait(false);
                 await _providerManager.SaveImage(item, imageStream, contentType, ImageType.Primary, null, url, cancellationToken).ConfigureAwait(false);
+                return true;
             }
             }
+
+            return false;
         }
         }
 
 
         public override MetadataProviderPriority Priority
         public override MetadataProviderPriority Priority

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

@@ -2,7 +2,7 @@
 <package xmlns="http://schemas.microsoft.com/packaging/2011/08/nuspec.xsd">
 <package xmlns="http://schemas.microsoft.com/packaging/2011/08/nuspec.xsd">
     <metadata>
     <metadata>
         <id>MediaBrowser.Common.Internal</id>
         <id>MediaBrowser.Common.Internal</id>
-        <version>3.0.285</version>
+        <version>3.0.288</version>
         <title>MediaBrowser.Common.Internal</title>
         <title>MediaBrowser.Common.Internal</title>
         <authors>Luke</authors>
         <authors>Luke</authors>
         <owners>ebr,Luke,scottisafool</owners>
         <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>
         <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>
         <copyright>Copyright © Media Browser 2013</copyright>
         <dependencies>
         <dependencies>
-            <dependency id="MediaBrowser.Common" version="3.0.285" />
+            <dependency id="MediaBrowser.Common" version="3.0.288" />
             <dependency id="NLog" version="2.1.0" />
             <dependency id="NLog" version="2.1.0" />
             <dependency id="SimpleInjector" version="2.4.0" />
             <dependency id="SimpleInjector" version="2.4.0" />
             <dependency id="sharpcompress" version="0.10.2" />
             <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">
 <package xmlns="http://schemas.microsoft.com/packaging/2011/08/nuspec.xsd">
     <metadata>
     <metadata>
         <id>MediaBrowser.Common</id>
         <id>MediaBrowser.Common</id>
-        <version>3.0.285</version>
+        <version>3.0.288</version>
         <title>MediaBrowser.Common</title>
         <title>MediaBrowser.Common</title>
         <authors>Media Browser Team</authors>
         <authors>Media Browser Team</authors>
         <owners>ebr,Luke,scottisafool</owners>
         <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">
 <package xmlns="http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd">
     <metadata>
     <metadata>
         <id>MediaBrowser.Server.Core</id>
         <id>MediaBrowser.Server.Core</id>
-        <version>3.0.285</version>
+        <version>3.0.288</version>
         <title>Media Browser.Server.Core</title>
         <title>Media Browser.Server.Core</title>
         <authors>Media Browser Team</authors>
         <authors>Media Browser Team</authors>
         <owners>ebr,Luke,scottisafool</owners>
         <owners>ebr,Luke,scottisafool</owners>
@@ -12,7 +12,7 @@
         <description>Contains core components required to build plugins for Media Browser Server.</description>
         <description>Contains core components required to build plugins for Media Browser Server.</description>
         <copyright>Copyright © Media Browser 2013</copyright>
         <copyright>Copyright © Media Browser 2013</copyright>
         <dependencies>
         <dependencies>
-            <dependency id="MediaBrowser.Common" version="3.0.285" />
+            <dependency id="MediaBrowser.Common" version="3.0.288" />
         </dependencies>
         </dependencies>
     </metadata>
     </metadata>
     <files>
     <files>