Selaa lähdekoodia

Merge branch 'master' of github.com:MediaBrowser/MediaBrowser

Luis Miguel Almánzar 11 vuotta sitten
vanhempi
sitoutus
ce8821c942
28 muutettua tiedostoa jossa 380 lisäystä ja 34 poistoa
  1. 60 10
      MediaBrowser.Api/LiveTv/LiveTvService.cs
  2. 6 0
      MediaBrowser.Controller/LiveTv/ChannelInfo.cs
  3. 2 1
      MediaBrowser.Controller/LiveTv/ILiveTvManager.cs
  4. 5 0
      MediaBrowser.Controller/LiveTv/ILiveTvService.cs
  5. 9 0
      MediaBrowser.Model.Portable/MediaBrowser.Model.Portable.csproj
  6. 9 0
      MediaBrowser.Model.net35/MediaBrowser.Model.net35.csproj
  7. 4 1
      MediaBrowser.Model/Configuration/ServerConfiguration.cs
  8. 12 0
      MediaBrowser.Model/Dto/RecordingInfoDto.cs
  9. 2 0
      MediaBrowser.Model/LiveTv/ChannelInfoDto.cs
  10. 17 0
      MediaBrowser.Model/LiveTv/EpgFullInfo.cs
  11. 32 0
      MediaBrowser.Model/LiveTv/EpgInfo.cs
  12. 78 0
      MediaBrowser.Model/LiveTv/RecordingInfo.cs
  13. 3 0
      MediaBrowser.Model/MediaBrowser.Model.csproj
  14. 1 1
      MediaBrowser.Providers/Movies/MovieDbProvider.cs
  15. 2 2
      MediaBrowser.Providers/Savers/XmlSaverHelpers.cs
  16. 1 1
      MediaBrowser.Providers/TV/RemoteSeasonProvider.cs
  17. 27 1
      MediaBrowser.Server.Implementations/IO/DirectoryWatchers.cs
  18. 2 1
      MediaBrowser.Server.Implementations/LiveTv/LiveTvManager.cs
  19. 1 1
      MediaBrowser.Server.Implementations/Persistence/SqliteDisplayPreferencesRepository.cs
  20. 4 1
      MediaBrowser.Server.Implementations/Persistence/SqliteExtensions.cs
  21. 2 2
      MediaBrowser.Server.Implementations/Persistence/SqliteItemRepository.cs
  22. 1 1
      MediaBrowser.Server.Implementations/Persistence/SqliteNotificationsRepository.cs
  23. 1 1
      MediaBrowser.Server.Implementations/Persistence/SqliteUserDataRepository.cs
  24. 1 1
      MediaBrowser.Server.Implementations/Persistence/SqliteUserRepository.cs
  25. 24 5
      MediaBrowser.Server.Implementations/Providers/ImageSaver.cs
  26. 8 1
      MediaBrowser.Server.Implementations/Sorting/PremiereDateComparer.cs
  27. 66 0
      MediaBrowser.ServerApplication/NextPvr/LiveTvService.cs
  28. 0 3
      MediaBrowser.sln

+ 60 - 10
MediaBrowser.Api/LiveTv/LiveTvService.cs

@@ -20,6 +20,20 @@ namespace MediaBrowser.Api.LiveTv
     {
         // Add filter by service if needed, and/or other filters
     }
+
+    [Route("/LiveTv/Recordings", "GET")]
+    [Api(Description = "Gets available live tv recordings.")]
+    public class GetRecordings : IReturn<List<RecordingInfo>>
+    {
+        // Add filter by service if needed, and/or other filters
+    }
+
+    [Route("/LiveTv/EPG", "GET")]
+    [Api(Description = "Gets available live tv epgs..")]
+    public class GetEpg : IReturn<List<EpgFullInfo>>
+    {
+        // Add filter by service if needed, and/or other filters
+    }
     
     public class LiveTvService : BaseApiService
     {
@@ -40,6 +54,14 @@ namespace MediaBrowser.Api.LiveTv
             return ToOptimizedResult(result);
         }
 
+        private LiveTvServiceInfo GetServiceInfo(ILiveTvService service)
+        {
+            return new LiveTvServiceInfo
+            {
+                Name = service.Name
+            };
+        }
+
         public object Get(GetChannels request)
         {
             var result = GetChannelsAsync(request).Result;
@@ -47,25 +69,53 @@ namespace MediaBrowser.Api.LiveTv
             return ToOptimizedResult(result);
         }
 
-        public async Task<IEnumerable<ChannelInfoDto>> GetChannelsAsync(GetChannels request)
+        private async Task<IEnumerable<ChannelInfoDto>> GetChannelsAsync(GetChannels request)
         {
             var services = _liveTvManager.Services;
 
-            var channelTasks = services.Select(i => i.GetChannelsAsync(CancellationToken.None));
+            var tasks = services.Select(i => i.GetChannelsAsync(CancellationToken.None));
 
-            var channelLists = await Task.WhenAll(channelTasks).ConfigureAwait(false);
+            var channelLists = await Task.WhenAll(tasks).ConfigureAwait(false);
 
             // Aggregate all channels from all services
             return channelLists.SelectMany(i => i)
                 .Select(_liveTvManager.GetChannelInfoDto);
         }
-        
-        private LiveTvServiceInfo GetServiceInfo(ILiveTvService service)
+
+        public object Get(GetRecordings request)
         {
-            return new LiveTvServiceInfo
-            {
-                Name = service.Name
-            };
+            var result = GetRecordingsAsync(request).Result;
+
+            return ToOptimizedResult(result);
+        }
+
+        private async Task<IEnumerable<RecordingInfo>> GetRecordingsAsync(GetRecordings request)
+        {
+            var services = _liveTvManager.Services;
+
+            var tasks = services.Select(i => i.GetRecordingsAsync(CancellationToken.None));
+
+            var recordings = await Task.WhenAll(tasks).ConfigureAwait(false);
+
+            return recordings.SelectMany(i => i);
+        }
+
+        public object Get(GetEpg request)
+        {
+            var result = GetEpgAsync(request).Result;
+
+            return ToOptimizedResult(result);
+        }
+
+        private async Task<IEnumerable<EpgFullInfo>> GetEpgAsync(GetEpg request)
+        {
+            var services = _liveTvManager.Services;
+
+            var tasks = services.Select(i => i.GetEpgAsync(CancellationToken.None));
+
+            var epg = await Task.WhenAll(tasks).ConfigureAwait(false);
+
+            return epg.SelectMany(i => i);
         }
     }
-}
+}

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

@@ -13,6 +13,12 @@ namespace MediaBrowser.Controller.LiveTv
         /// <value>The name.</value>
         public string Name { get; set; }
 
+        /// <summary>
+        /// Get or sets the Id.
+        /// </summary>
+        /// <value>The id of the channel.</value>
+        public string Id { get; set; }
+
         /// <summary>
         /// Gets or sets the name of the service.
         /// </summary>

+ 2 - 1
MediaBrowser.Controller/LiveTv/ILiveTvManager.cs

@@ -1,4 +1,5 @@
-using MediaBrowser.Model.LiveTv;
+using System.Threading.Tasks;
+using MediaBrowser.Model.LiveTv;
 using System.Collections.Generic;
 
 namespace MediaBrowser.Controller.LiveTv

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

@@ -1,6 +1,7 @@
 using System.Collections.Generic;
 using System.Threading;
 using System.Threading.Tasks;
+using MediaBrowser.Model.LiveTv;
 
 namespace MediaBrowser.Controller.LiveTv
 {
@@ -21,5 +22,9 @@ namespace MediaBrowser.Controller.LiveTv
         /// <param name="cancellationToken">The cancellation token.</param>
         /// <returns>Task{IEnumerable{ChannelInfo}}.</returns>
         Task<IEnumerable<ChannelInfo>> GetChannelsAsync(CancellationToken cancellationToken);
+
+        Task<IEnumerable<RecordingInfo>> GetRecordingsAsync(CancellationToken cancellationToken);
+
+        Task<IEnumerable<EpgFullInfo>> GetEpgAsync(CancellationToken cancellationToken);
     }
 }

+ 9 - 0
MediaBrowser.Model.Portable/MediaBrowser.Model.Portable.csproj

@@ -230,9 +230,18 @@
     <Compile Include="..\MediaBrowser.Model\LiveTv\ChannelType.cs">
       <Link>LiveTv\ChannelType.cs</Link>
     </Compile>
+    <Compile Include="..\MediaBrowser.Model\LiveTv\EpgFullInfo.cs">
+      <Link>LiveTv\EpgFullInfo.cs</Link>
+    </Compile>
+    <Compile Include="..\MediaBrowser.Model\LiveTv\EpgInfo.cs">
+      <Link>LiveTv\EpgInfo.cs</Link>
+    </Compile>
     <Compile Include="..\MediaBrowser.Model\LiveTv\LiveTvServiceInfo.cs">
       <Link>LiveTv\LiveTvServiceInfo.cs</Link>
     </Compile>
+    <Compile Include="..\MediaBrowser.Model\LiveTv\RecordingInfo.cs">
+      <Link>LiveTv\RecordingInfo.cs</Link>
+    </Compile>
     <Compile Include="..\MediaBrowser.Model\Logging\ILogger.cs">
       <Link>Logging\ILogger.cs</Link>
     </Compile>

+ 9 - 0
MediaBrowser.Model.net35/MediaBrowser.Model.net35.csproj

@@ -217,9 +217,18 @@
     <Compile Include="..\MediaBrowser.Model\LiveTv\ChannelType.cs">
       <Link>LiveTv\ChannelType.cs</Link>
     </Compile>
+    <Compile Include="..\MediaBrowser.Model\LiveTv\EpgFullInfo.cs">
+      <Link>LiveTv\EpgFullInfo.cs</Link>
+    </Compile>
+    <Compile Include="..\MediaBrowser.Model\LiveTv\EpgInfo.cs">
+      <Link>LiveTv\EpgInfo.cs</Link>
+    </Compile>
     <Compile Include="..\MediaBrowser.Model\LiveTv\LiveTvServiceInfo.cs">
       <Link>LiveTv\LiveTvServiceInfo.cs</Link>
     </Compile>
+    <Compile Include="..\MediaBrowser.Model\LiveTv\RecordingInfo.cs">
+      <Link>LiveTv\RecordingInfo.cs</Link>
+    </Compile>
     <Compile Include="..\MediaBrowser.Model\Logging\ILogger.cs">
       <Link>Logging\ILogger.cs</Link>
     </Compile>

+ 4 - 1
MediaBrowser.Model/Configuration/ServerConfiguration.cs

@@ -275,7 +275,10 @@ namespace MediaBrowser.Model.Configuration
             MetadataCountryCode = "US";
             DownloadMovieImages = new ImageDownloadOptions();
             DownloadSeriesImages = new ImageDownloadOptions();
-            DownloadSeasonImages = new ImageDownloadOptions();
+            DownloadSeasonImages = new ImageDownloadOptions
+            {
+                Backdrops = false
+            };
             DownloadMusicArtistImages = new ImageDownloadOptions();
             DownloadMusicAlbumImages = new ImageDownloadOptions();
             MaxBackdrops = 3;

+ 12 - 0
MediaBrowser.Model/Dto/RecordingInfoDto.cs

@@ -0,0 +1,12 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace MediaBrowser.Model.Dto
+{
+    public class RecordingInfoDto
+    {
+    }
+}

+ 2 - 0
MediaBrowser.Model/LiveTv/ChannelInfoDto.cs

@@ -12,6 +12,8 @@ namespace MediaBrowser.Model.LiveTv
         /// <value>The name.</value>
         public string Name { get; set; }
 
+        public string Id { get; set; }
+        
         /// <summary>
         /// Gets or sets the name of the service.
         /// </summary>

+ 17 - 0
MediaBrowser.Model/LiveTv/EpgFullInfo.cs

@@ -0,0 +1,17 @@
+using System.Collections.Generic;
+
+namespace MediaBrowser.Model.LiveTv
+{
+    public class EpgFullInfo
+    {
+        /// <summary>
+        /// ChannelId for the EPG.
+        /// </summary>
+        public string ChannelId { get; set; }
+
+        /// <summary>
+        /// List of all the programs for a specific channel
+        /// </summary>
+        public List<EpgInfo> EpgInfos { get; set; } 
+    }
+}

+ 32 - 0
MediaBrowser.Model/LiveTv/EpgInfo.cs

@@ -0,0 +1,32 @@
+using System;
+
+namespace MediaBrowser.Model.LiveTv
+{
+    public class EpgInfo
+    {
+        /// <summary>
+        /// Id of the program.
+        /// </summary>
+        public string Id { get; set; }
+
+        /// <summary>
+        /// Description of the progam.
+        /// </summary>
+        public string Description { get; set; }
+
+        /// <summary>
+        /// The start date of the program, in UTC.
+        /// </summary>
+        public DateTime StartDate { get; set; }
+
+        /// <summary>
+        /// The end date of the program, in UTC.
+        /// </summary>
+        public DateTime EndDate { get; set; }
+
+        /// <summary>
+        /// Genre of the program.
+        /// </summary>
+        public string Genre { get; set; }
+    }
+}

+ 78 - 0
MediaBrowser.Model/LiveTv/RecordingInfo.cs

@@ -0,0 +1,78 @@
+using System;
+using System.Collections.Generic;
+
+namespace MediaBrowser.Model.LiveTv
+{
+    public class RecordingInfo
+    {
+        /// <summary>
+        /// Id of the recording.
+        /// </summary>
+        public string Id { get; set; }
+
+        /// <summary>
+        /// ChannelId of the recording.
+        /// </summary>
+        public string ChannelId { get; set; }
+
+        /// <summary>
+        /// ChannelName of the recording.
+        /// </summary>
+        public string ChannelName { get; set; }
+
+        /// <summary>
+        /// Name of the recording.
+        /// </summary>
+        public string Name { get; set; }
+
+        /// <summary>
+        /// Description of the recording.
+        /// </summary>
+        public string Description { get; set; }
+
+        /// <summary>
+        /// The start date of the recording, in UTC.
+        /// </summary>
+        public DateTime StartDate { get; set; }
+
+        /// <summary>
+        /// The end date of the recording, in UTC.
+        /// </summary>
+        public DateTime EndDate { get; set; }
+
+        /// <summary>
+        /// Status of the recording.
+        /// </summary>
+        public string Status { get; set; } //TODO: Enum for status?? Difference NextPvr,Argus,...
+
+        /// <summary>
+        /// Quality of the Recording.
+        /// </summary>
+        public string Quality { get; set; } // TODO: Enum for quality?? Difference NextPvr,Argus,...
+
+        /// <summary>
+        /// Recurring recording?
+        /// </summary>
+        public bool Recurring { get; set; }
+
+        /// <summary>
+        /// Parent recurring.
+        /// </summary>
+        public string RecurringParent { get; set; }
+
+        /// <summary>
+        /// Start date for the recurring, in UTC.
+        /// </summary>
+        public DateTime RecurrringStartDate { get; set; }
+
+        /// <summary>
+        /// End date for the recurring, in UTC
+        /// </summary>
+        public DateTime RecurringEndDate { get; set; }
+
+        /// <summary>
+        /// When do we need the recording?
+        /// </summary>
+        public List<string> DayMask { get; set; }
+    }
+}

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

@@ -59,6 +59,8 @@
     <Compile Include="Dto\ItemByNameCounts.cs" />
     <Compile Include="Dto\ItemCounts.cs" />
     <Compile Include="Dto\ItemIndex.cs" />
+    <Compile Include="LiveTv\EpgFullInfo.cs" />
+    <Compile Include="LiveTv\EpgInfo.cs" />
     <Compile Include="Providers\RemoteImageInfo.cs" />
     <Compile Include="Dto\StudioDto.cs" />
     <Compile Include="Entities\CollectionType.cs" />
@@ -74,6 +76,7 @@
     <Compile Include="LiveTv\ChannelInfoDto.cs" />
     <Compile Include="LiveTv\ChannelType.cs" />
     <Compile Include="LiveTv\LiveTvServiceInfo.cs" />
+    <Compile Include="LiveTv\RecordingInfo.cs" />
     <Compile Include="Net\WebSocketMessage.cs" />
     <Compile Include="Net\WebSocketMessageType.cs" />
     <Compile Include="Net\WebSocketState.cs" />

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

@@ -221,7 +221,7 @@ namespace MediaBrowser.Providers.Movies
                     !imagesFileInfo.Exists || _fileSystem.GetLastWriteTimeUtc(imagesFileInfo) > providerInfo.LastRefreshed;
             }
 
-            return true;
+            return base.NeedsRefreshBasedOnCompareDate(item, providerInfo);
         }
 
         /// <summary>

+ 2 - 2
MediaBrowser.Providers/Savers/XmlSaverHelpers.cs

@@ -562,8 +562,8 @@ namespace MediaBrowser.Providers.Savers
                     {
                         var timespan = TimeSpan.FromTicks(item.RunTimeTicks.Value);
 
-                        builder.Append("<Duration>" + Convert.ToInt32(timespan.TotalMinutes).ToString(UsCulture) + "</Duration>");
-                        builder.Append("<DurationSeconds>" + Convert.ToInt32(timespan.TotalSeconds).ToString(UsCulture) + "</DurationSeconds>");
+                        builder.Append("<Duration>" + Convert.ToInt64(timespan.TotalMinutes).ToString(UsCulture) + "</Duration>");
+                        builder.Append("<DurationSeconds>" + Convert.ToInt64(timespan.TotalSeconds).ToString(UsCulture) + "</DurationSeconds>");
                     }
 
                     if (video != null && video.Video3DFormat.HasValue)

+ 1 - 1
MediaBrowser.Providers/TV/RemoteSeasonProvider.cs

@@ -158,7 +158,7 @@ namespace MediaBrowser.Providers.TV
                 try
                 {
                     var fanartData = FetchFanartXmlData(imagesXmlPath, seasonNumber.Value, cancellationToken);
-                    await DownloadImages(item, fanartData, 1, cancellationToken).ConfigureAwait(false);
+                    await DownloadImages(item, fanartData, ConfigurationManager.Configuration.MaxBackdrops, cancellationToken).ConfigureAwait(false);
                 }
                 catch (FileNotFoundException)
                 {

+ 27 - 1
MediaBrowser.Server.Implementations/IO/DirectoryWatchers.cs

@@ -361,7 +361,33 @@ namespace MediaBrowser.Server.Implementations.IO
             if (e.ChangeType == WatcherChangeTypes.Changed)
             {
                 // If the parent of an ignored path has a change event, ignore that too
-                if (tempIgnorePaths.Any(i => string.Equals(Path.GetDirectoryName(i), e.FullPath, StringComparison.OrdinalIgnoreCase) || string.Equals(i, e.FullPath, StringComparison.OrdinalIgnoreCase)))
+                if (tempIgnorePaths.Any(i =>
+                {
+                    if (string.Equals(i, e.FullPath, StringComparison.OrdinalIgnoreCase))
+                    {
+                        return true;
+                    }
+
+                    // Go up a level
+                    var parent = Path.GetDirectoryName(i);
+                    if (string.Equals(parent, e.FullPath, StringComparison.OrdinalIgnoreCase))
+                    {
+                        return true;
+                    }
+
+                    // Go up another level
+                    if (!string.IsNullOrEmpty(parent))
+                    {
+                        parent = Path.GetDirectoryName(i);
+                        if (string.Equals(parent, e.FullPath, StringComparison.OrdinalIgnoreCase))
+                        {
+                            return true;
+                        }
+                    }
+
+                    return false;
+
+                }))
                 {
                     return;
                 }

+ 2 - 1
MediaBrowser.Server.Implementations/LiveTv/LiveTvManager.cs

@@ -39,7 +39,8 @@ namespace MediaBrowser.Server.Implementations.LiveTv
             {
                 Name = info.Name,
                 ServiceName = info.ServiceName,
-                ChannelType = info.ChannelType
+                ChannelType = info.ChannelType,
+                Id = info.Id 
             };
         }
     }

+ 1 - 1
MediaBrowser.Server.Implementations/Persistence/SqliteDisplayPreferencesRepository.cs

@@ -80,7 +80,7 @@ namespace MediaBrowser.Server.Implementations.Persistence
         {
             var dbFile = Path.Combine(_appPaths.DataPath, "displaypreferences.db");
 
-            _connection = await SqliteExtensions.ConnectToDb(dbFile).ConfigureAwait(false);
+            _connection = await SqliteExtensions.ConnectToDb(dbFile, _logger).ConfigureAwait(false);
 
             string[] queries = {
 

+ 4 - 1
MediaBrowser.Server.Implementations/Persistence/SqliteExtensions.cs

@@ -128,15 +128,18 @@ namespace MediaBrowser.Server.Implementations.Persistence
         /// Connects to db.
         /// </summary>
         /// <param name="dbPath">The db path.</param>
+        /// <param name="logger">The logger.</param>
         /// <returns>Task{IDbConnection}.</returns>
         /// <exception cref="System.ArgumentNullException">dbPath</exception>
-        public static async Task<IDbConnection> ConnectToDb(string dbPath)
+        public static async Task<IDbConnection> ConnectToDb(string dbPath, ILogger logger)
         {
             if (string.IsNullOrEmpty(dbPath))
             {
                 throw new ArgumentNullException("dbPath");
             }
 
+            logger.Info("Opening {0}", dbPath);
+
 			#if __MonoCS__
 			var connectionstr = new SqliteConnectionStringBuilder
 			{

+ 2 - 2
MediaBrowser.Server.Implementations/Persistence/SqliteItemRepository.cs

@@ -91,7 +91,7 @@ namespace MediaBrowser.Server.Implementations.Persistence
 
             var chapterDbFile = Path.Combine(_appPaths.DataPath, "chapters.db");
 
-            var chapterConnection = SqliteExtensions.ConnectToDb(chapterDbFile).Result;
+            var chapterConnection = SqliteExtensions.ConnectToDb(chapterDbFile, _logger).Result;
 
             _chapterRepository = new SqliteChapterRepository(chapterConnection, logManager);
         }
@@ -104,7 +104,7 @@ namespace MediaBrowser.Server.Implementations.Persistence
         {
             var dbFile = Path.Combine(_appPaths.DataPath, "library.db");
 
-            _connection = await SqliteExtensions.ConnectToDb(dbFile).ConfigureAwait(false);
+            _connection = await SqliteExtensions.ConnectToDb(dbFile, _logger).ConfigureAwait(false);
 
             string[] queries = {
 

+ 1 - 1
MediaBrowser.Server.Implementations/Persistence/SqliteNotificationsRepository.cs

@@ -37,7 +37,7 @@ namespace MediaBrowser.Server.Implementations.Persistence
         {
             var dbFile = Path.Combine(_appPaths.DataPath, "notifications.db");
 
-            _connection = await SqliteExtensions.ConnectToDb(dbFile).ConfigureAwait(false);
+            _connection = await SqliteExtensions.ConnectToDb(dbFile, _logger).ConfigureAwait(false);
             
             string[] queries = {
 

+ 1 - 1
MediaBrowser.Server.Implementations/Persistence/SqliteUserDataRepository.cs

@@ -73,7 +73,7 @@ namespace MediaBrowser.Server.Implementations.Persistence
         {
             var dbFile = Path.Combine(_appPaths.DataPath, "userdata_v2.db");
 
-            _connection = await SqliteExtensions.ConnectToDb(dbFile).ConfigureAwait(false);
+            _connection = await SqliteExtensions.ConnectToDb(dbFile, _logger).ConfigureAwait(false);
 
             string[] queries = {
 

+ 1 - 1
MediaBrowser.Server.Implementations/Persistence/SqliteUserRepository.cs

@@ -70,7 +70,7 @@ namespace MediaBrowser.Server.Implementations.Persistence
         {
             var dbFile = Path.Combine(_appPaths.DataPath, "users.db");
 
-            _connection = await SqliteExtensions.ConnectToDb(dbFile).ConfigureAwait(false);
+            _connection = await SqliteExtensions.ConnectToDb(dbFile, _logger).ConfigureAwait(false);
             
             string[] queries = {
 

+ 24 - 5
MediaBrowser.Server.Implementations/Providers/ImageSaver.cs

@@ -1,4 +1,5 @@
-using MediaBrowser.Common.IO;
+using System.Collections.Generic;
+using MediaBrowser.Common.IO;
 using MediaBrowser.Controller.Configuration;
 using MediaBrowser.Controller.Entities;
 using MediaBrowser.Controller.Entities.Audio;
@@ -268,7 +269,7 @@ namespace MediaBrowser.Server.Implementations.Providers
                     {
                         item.ScreenshotImagePaths[imageIndex.Value] = path;
                     }
-                    else
+                    else if (!item.ScreenshotImagePaths.Contains(path, StringComparer.OrdinalIgnoreCase))
                     {
                         item.ScreenshotImagePaths.Add(path);
                     }
@@ -282,7 +283,7 @@ namespace MediaBrowser.Server.Implementations.Providers
                     {
                         item.BackdropImagePaths[imageIndex.Value] = path;
                     }
-                    else
+                    else if (!item.BackdropImagePaths.Contains(path, StringComparer.OrdinalIgnoreCase))
                     {
                         item.BackdropImagePaths.Add(path);
                     }
@@ -333,14 +334,14 @@ namespace MediaBrowser.Server.Implementations.Providers
                     {
                         throw new ArgumentNullException("imageIndex");
                     }
-                    filename = imageIndex.Value == 0 ? "backdrop" : "backdrop" + imageIndex.Value.ToString(UsCulture);
+                    filename = GetBackdropSaveFilename(item.BackdropImagePaths, "backdrop", "backdrop", imageIndex.Value);
                     break;
                 case ImageType.Screenshot:
                     if (!imageIndex.HasValue)
                     {
                         throw new ArgumentNullException("imageIndex");
                     }
-                    filename = imageIndex.Value == 0 ? "screenshot" : "screenshot" + imageIndex.Value.ToString(UsCulture);
+                    filename = GetBackdropSaveFilename(item.ScreenshotImagePaths, "screenshot", "screenshot", imageIndex.Value);
                     break;
                 default:
                     filename = type.ToString().ToLower();
@@ -380,6 +381,24 @@ namespace MediaBrowser.Server.Implementations.Providers
             return path;
         }
 
+        private string GetBackdropSaveFilename(List<string> images, string zeroIndexFilename, string numberedIndexPrefix, int index)
+        {
+            var filesnames = images.Select(Path.GetFileNameWithoutExtension).ToList();
+
+            if (index == 0)
+            {
+                return zeroIndexFilename;
+            }
+
+            var current = index;
+            while (filesnames.Contains(numberedIndexPrefix + current.ToString(UsCulture), StringComparer.OrdinalIgnoreCase))
+            {
+                current++;
+            }
+
+            return numberedIndexPrefix + current.ToString(UsCulture);
+        }
+
         /// <summary>
         /// Gets the compatible save paths.
         /// </summary>

+ 8 - 1
MediaBrowser.Server.Implementations/Sorting/PremiereDateComparer.cs

@@ -35,7 +35,14 @@ namespace MediaBrowser.Server.Implementations.Sorting
             
             if (x.ProductionYear.HasValue)
             {
-                return new DateTime(x.ProductionYear.Value, 1, 1, 0, 0, 0, DateTimeKind.Utc);
+                try
+                {
+                    return new DateTime(x.ProductionYear.Value, 1, 1, 0, 0, 0, DateTimeKind.Utc);
+                }
+                catch (ArgumentOutOfRangeException)
+                {
+                    // Don't blow up if the item has a bad ProductionYear, just return MinValue
+                }
             }
             return DateTime.MinValue;
         }

+ 66 - 0
MediaBrowser.ServerApplication/NextPvr/LiveTvService.cs

@@ -0,0 +1,66 @@
+using MediaBrowser.Common.Configuration;
+using MediaBrowser.Common.Net;
+using MediaBrowser.Controller.LiveTv;
+using System.Collections.Generic;
+using System.Threading;
+using System.Threading.Tasks;
+using MediaBrowser.Model.Logging;
+using MediaBrowser.Model.Serialization;
+
+namespace MediaBrowser.Plugins.NextPvr
+{
+    /// <summary>
+    /// Class LiveTvService
+    /// </summary>
+    public class LiveTvService : ILiveTvService
+    {
+        private readonly ILogger _logger;
+
+        private IApplicationPaths _appPaths;
+        private IJsonSerializer _json;
+        private IHttpClient _httpClient;
+
+        public LiveTvService(ILogger logger)
+        {
+            _logger = logger;
+        }
+
+        /// <summary>
+        /// Gets the channels async.
+        /// </summary>
+        /// <param name="cancellationToken">The cancellation token.</param>
+        /// <returns>Task{IEnumerable{ChannelInfo}}.</returns>
+        public Task<IEnumerable<ChannelInfo>> GetChannelsAsync(CancellationToken cancellationToken)
+        {
+            //using (var stream = await _httpClient.Get(new HttpRequestOptions()
+            //    {
+            //          Url = "",
+            //          CancellationToken = cancellationToken
+            //    }))
+            //{
+                
+            //}
+            _logger.Info("GetChannelsAsync");
+
+            var channels = new List<ChannelInfo>
+                {
+                    new ChannelInfo
+                        {
+                             Name = "NBC",
+                              ServiceName = Name
+                        }
+                };
+
+            return Task.FromResult<IEnumerable<ChannelInfo>>(channels);
+        }
+
+        /// <summary>
+        /// Gets the name.
+        /// </summary>
+        /// <value>The name.</value>
+        public string Name
+        {
+            get { return "Next Pvr"; }
+        }
+    }
+}

+ 0 - 3
MediaBrowser.sln

@@ -237,7 +237,4 @@ Global
 	GlobalSection(SolutionProperties) = preSolution
 		HideSolutionNode = FALSE
 	EndGlobalSection
-	GlobalSection(Performance) = preSolution
-		HasPerformanceSessions = true
-	EndGlobalSection
 EndGlobal