Browse Source

Merge pull request #1 from MediaBrowser/master

update
Stephen 11 năm trước cách đây
mục cha
commit
2a9e477812
100 tập tin đã thay đổi với 1242 bổ sung405 xóa
  1. 1 1
      MediaBrowser.Api/ApiEntryPoint.cs
  2. 37 1
      MediaBrowser.Api/ConfigurationService.cs
  3. 7 2
      MediaBrowser.Api/Dlna/DlnaServerService.cs
  4. 6 0
      MediaBrowser.Api/ItemUpdateService.cs
  5. 1 0
      MediaBrowser.Api/MediaBrowser.Api.csproj
  6. 13 3
      MediaBrowser.Api/Playback/BaseStreamingService.cs
  7. 186 0
      MediaBrowser.Api/Playback/BifService.cs
  8. 2 1
      MediaBrowser.Api/Playback/Progressive/VideoService.cs
  9. 1 0
      MediaBrowser.Common.Implementations/BaseApplicationHost.cs
  10. 82 9
      MediaBrowser.Common.Implementations/Configuration/BaseConfigurationManager.cs
  11. 17 7
      MediaBrowser.Common.Implementations/Updates/InstallationManager.cs
  12. 18 0
      MediaBrowser.Common/Configuration/ConfigurationUpdateEventArgs.cs
  13. 17 0
      MediaBrowser.Common/Configuration/IConfigurationFactory.cs
  14. 42 1
      MediaBrowser.Common/Configuration/IConfigurationManager.cs
  15. 2 0
      MediaBrowser.Common/MediaBrowser.Common.csproj
  16. 5 0
      MediaBrowser.Common/Net/MimeTypes.cs
  17. 7 0
      MediaBrowser.Controller/Chapters/IChapterManager.cs
  18. 18 0
      MediaBrowser.Controller/Entities/Audio/MusicAlbum.cs
  19. 0 5
      MediaBrowser.Controller/Entities/Folder.cs
  20. 21 0
      MediaBrowser.Controller/MediaEncoding/IMediaEncoder.cs
  21. 29 0
      MediaBrowser.Dlna/ConfigurationExtension.cs
  22. 4 2
      MediaBrowser.Dlna/ContentDirectory/ContentDirectory.cs
  23. 4 4
      MediaBrowser.Dlna/Didl/Filter.cs
  24. 1 1
      MediaBrowser.Dlna/Eventing/EventSubscription.cs
  25. 14 8
      MediaBrowser.Dlna/Main/DlnaEntryPoint.cs
  26. 3 0
      MediaBrowser.Dlna/MediaBrowser.Dlna.csproj
  27. 1 1
      MediaBrowser.Dlna/PlayTo/PlayToController.cs
  28. 3 3
      MediaBrowser.Dlna/PlayTo/PlayToManager.cs
  29. 3 3
      MediaBrowser.Dlna/PlayTo/SsdpHttpClient.cs
  30. 1 1
      MediaBrowser.Dlna/Service/BaseControlHandler.cs
  31. 17 13
      MediaBrowser.Dlna/Ssdp/SsdpHandler.cs
  32. 6 6
      MediaBrowser.LocalMetadata/BaseXmlProvider.cs
  33. 3 5
      MediaBrowser.LocalMetadata/Images/CollectionFolderImageProvider.cs
  34. 6 6
      MediaBrowser.LocalMetadata/Images/EpisodeLocalImageProvider.cs
  35. 4 5
      MediaBrowser.LocalMetadata/Images/ImagesByNameImageProvider.cs
  36. 4 4
      MediaBrowser.LocalMetadata/Images/InternalMetadataFolderImageProvider.cs
  37. 7 7
      MediaBrowser.LocalMetadata/Images/LocalImageProvider.cs
  38. 110 0
      MediaBrowser.LocalMetadata/MediaBrowser.LocalMetadata.csproj
  39. 5 5
      MediaBrowser.LocalMetadata/Parsers/BoxSetXmlParser.cs
  40. 6 6
      MediaBrowser.LocalMetadata/Parsers/EpisodeXmlParser.cs
  41. 5 5
      MediaBrowser.LocalMetadata/Parsers/GameSystemXmlParser.cs
  42. 6 6
      MediaBrowser.LocalMetadata/Parsers/GameXmlParser.cs
  43. 5 5
      MediaBrowser.LocalMetadata/Parsers/MovieXmlParser.cs
  44. 3 4
      MediaBrowser.LocalMetadata/Parsers/MusicVideoXmlParser.cs
  45. 3 3
      MediaBrowser.LocalMetadata/Parsers/SeasonXmlParser.cs
  46. 4 4
      MediaBrowser.LocalMetadata/Parsers/SeriesXmlParser.cs
  47. 36 0
      MediaBrowser.LocalMetadata/Properties/AssemblyInfo.cs
  48. 6 6
      MediaBrowser.LocalMetadata/Providers/AdultVideoXmlProvider.cs
  49. 5 5
      MediaBrowser.LocalMetadata/Providers/AlbumXmlProvider.cs
  50. 4 4
      MediaBrowser.LocalMetadata/Providers/ArtistXmlProvider.cs
  51. 5 4
      MediaBrowser.LocalMetadata/Providers/BoxSetXmlProvider.cs
  52. 4 4
      MediaBrowser.LocalMetadata/Providers/ChannelXmlProvider.cs
  53. 16 6
      MediaBrowser.LocalMetadata/Providers/EpisodeXmlProvider.cs
  54. 4 4
      MediaBrowser.LocalMetadata/Providers/FolderXmlProvider.cs
  55. 5 4
      MediaBrowser.LocalMetadata/Providers/GameSystemXmlProvider.cs
  56. 5 4
      MediaBrowser.LocalMetadata/Providers/GameXmlProvider.cs
  57. 6 5
      MediaBrowser.LocalMetadata/Providers/MovieXmlProvider.cs
  58. 5 6
      MediaBrowser.LocalMetadata/Providers/MusicVideoXmlProvider.cs
  59. 4 4
      MediaBrowser.LocalMetadata/Providers/PersonXmlProvider.cs
  60. 15 5
      MediaBrowser.LocalMetadata/Providers/SeasonXmlProvider.cs
  61. 15 5
      MediaBrowser.LocalMetadata/Providers/SeriesXmlProvider.cs
  62. 6 5
      MediaBrowser.LocalMetadata/Providers/TrailerXmlProvider.cs
  63. 6 6
      MediaBrowser.LocalMetadata/Providers/VideoXmlProvider.cs
  64. 5 5
      MediaBrowser.LocalMetadata/Savers/AlbumXmlSaver.cs
  65. 5 5
      MediaBrowser.LocalMetadata/Savers/ArtistXmlSaver.cs
  66. 5 6
      MediaBrowser.LocalMetadata/Savers/BoxSetXmlSaver.cs
  67. 5 6
      MediaBrowser.LocalMetadata/Savers/ChannelXmlSaver.cs
  68. 6 6
      MediaBrowser.LocalMetadata/Savers/EpisodeXmlSaver.cs
  69. 6 7
      MediaBrowser.LocalMetadata/Savers/FolderXmlSaver.cs
  70. 4 4
      MediaBrowser.LocalMetadata/Savers/GameSystemXmlSaver.cs
  71. 5 5
      MediaBrowser.LocalMetadata/Savers/GameXmlSaver.cs
  72. 7 8
      MediaBrowser.LocalMetadata/Savers/MovieXmlSaver.cs
  73. 4 6
      MediaBrowser.LocalMetadata/Savers/PersonXmlSaver.cs
  74. 5 5
      MediaBrowser.LocalMetadata/Savers/SeasonXmlSaver.cs
  75. 7 7
      MediaBrowser.LocalMetadata/Savers/SeriesXmlSaver.cs
  76. 7 7
      MediaBrowser.LocalMetadata/Savers/XmlSaverHelpers.cs
  77. 93 6
      MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs
  78. 5 6
      MediaBrowser.MediaEncoding/Subtitles/SubtitleEncoder.cs
  79. 15 6
      MediaBrowser.Model.Portable/MediaBrowser.Model.Portable.csproj
  80. 15 6
      MediaBrowser.Model.net35/MediaBrowser.Model.net35.csproj
  81. 18 0
      MediaBrowser.Model/Configuration/ChannelOptions.cs
  82. 27 0
      MediaBrowser.Model/Configuration/ChapterOptions.cs
  83. 10 67
      MediaBrowser.Model/Configuration/ServerConfiguration.cs
  84. 10 0
      MediaBrowser.Model/Configuration/SubtitlePlaybackMode.cs
  85. 0 8
      MediaBrowser.Model/Configuration/UserConfiguration.cs
  86. 21 0
      MediaBrowser.Model/Configuration/XbmcMetadataOptions.cs
  87. 29 0
      MediaBrowser.Model/Dlna/ConditionProcessor.cs
  88. 4 4
      MediaBrowser.Model/Dto/BaseItemDto.cs
  89. 2 1
      MediaBrowser.Model/Dto/BaseItemPerson.cs
  90. 2 1
      MediaBrowser.Model/Dto/ChapterInfoDto.cs
  91. 2 1
      MediaBrowser.Model/Dto/UserDto.cs
  92. 2 1
      MediaBrowser.Model/Dto/UserItemDataDto.cs
  93. 2 1
      MediaBrowser.Model/Entities/DisplayPreferences.cs
  94. 1 1
      MediaBrowser.Model/Entities/MediaStream.cs
  95. 8 0
      MediaBrowser.Model/Extensions/IHasPropertyChangedEvent.cs
  96. 10 1
      MediaBrowser.Model/Extensions/ListHelper.cs
  97. 2 1
      MediaBrowser.Model/LiveTv/BaseTimerInfoDto.cs
  98. 2 1
      MediaBrowser.Model/LiveTv/ChannelInfoDto.cs
  99. 2 1
      MediaBrowser.Model/LiveTv/ProgramInfoDto.cs
  100. 2 1
      MediaBrowser.Model/LiveTv/RecordingGroupDto.cs

+ 1 - 1
MediaBrowser.Api/ApiEntryPoint.cs

@@ -418,7 +418,7 @@ namespace MediaBrowser.Api
 
         private async void DeletePartialStreamFiles(string path, TranscodingJobType jobType, int retryCount, int delayMs)
         {
-            if (retryCount >= 8)
+            if (retryCount >= 10)
             {
                 return;
             }

+ 37 - 1
MediaBrowser.Api/ConfigurationService.cs

@@ -8,8 +8,11 @@ using MediaBrowser.Controller.Providers;
 using MediaBrowser.Model.Configuration;
 using MediaBrowser.Model.Serialization;
 using ServiceStack;
+using ServiceStack.Text.Controller;
+using ServiceStack.Web;
 using System;
 using System.Collections.Generic;
+using System.IO;
 using System.Linq;
 
 namespace MediaBrowser.Api
@@ -23,6 +26,13 @@ namespace MediaBrowser.Api
 
     }
 
+    [Route("/System/Configuration/{Key}", "GET", Summary = "Gets a named configuration")]
+    public class GetNamedConfiguration
+    {
+        [ApiMember(Name = "Key", Description = "Key", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "GET")]
+        public string Key { get; set; }
+    }
+    
     /// <summary>
     /// Class UpdateConfiguration
     /// </summary>
@@ -31,6 +41,15 @@ namespace MediaBrowser.Api
     {
     }
 
+    [Route("/System/Configuration/{Key}", "POST", Summary = "Updates named configuration")]
+    public class UpdateNamedConfiguration : IReturnVoid, IRequiresRequestStream
+    {
+        [ApiMember(Name = "Key", Description = "Key", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "GET")]
+        public string Key { get; set; }
+
+        public Stream RequestStream { get; set; }
+    }
+    
     [Route("/System/Configuration/MetadataOptions/Default", "GET", Summary = "Gets a default MetadataOptions object")]
     public class GetDefaultMetadataOptions : IReturn<MetadataOptions>
     {
@@ -88,6 +107,13 @@ namespace MediaBrowser.Api
             return ToOptimizedResultUsingCache(cacheKey, dateModified, null, () => _configurationManager.Configuration);
         }
 
+        public object Get(GetNamedConfiguration request)
+        {
+            var result = _configurationManager.GetConfiguration(request.Key);
+
+            return ToOptimizedResult(result);
+        }
+
         /// <summary>
         /// Posts the specified configuraiton.
         /// </summary>
@@ -95,7 +121,6 @@ namespace MediaBrowser.Api
         public void Post(UpdateConfiguration request)
         {
             // Silly, but we need to serialize and deserialize or the XmlSerializer will write the xml with an element name of UpdateConfiguration
-
             var json = _jsonSerializer.SerializeToString(request);
 
             var config = _jsonSerializer.DeserializeFromString<ServerConfiguration>(json);
@@ -103,6 +128,17 @@ namespace MediaBrowser.Api
             _configurationManager.ReplaceConfiguration(config);
         }
 
+        public void Post(UpdateNamedConfiguration request)
+        {
+            var pathInfo = PathInfo.Parse(Request.PathInfo);
+            var key = pathInfo.GetArgumentValue<string>(2);
+
+            var configurationType = _configurationManager.GetConfigurationType(key);
+            var configuration = _jsonSerializer.DeserializeFromStream(request.RequestStream, configurationType);
+            
+            _configurationManager.SaveConfiguration(key, configuration);
+        }
+
         public object Get(GetDefaultMetadataOptions request)
         {
             return ToOptimizedSerializedResultUsingCache(new MetadataOptions());

+ 7 - 2
MediaBrowser.Api/Dlna/DlnaServerService.cs

@@ -1,4 +1,6 @@
-using MediaBrowser.Controller.Dlna;
+using MediaBrowser.Common.Configuration;
+using MediaBrowser.Controller.Dlna;
+using MediaBrowser.Model.Configuration;
 using ServiceStack;
 using ServiceStack.Text.Controller;
 using ServiceStack.Web;
@@ -76,11 +78,14 @@ namespace MediaBrowser.Api.Dlna
         private readonly IContentDirectory _contentDirectory;
         private readonly IConnectionManager _connectionManager;
 
-        public DlnaServerService(IDlnaManager dlnaManager, IContentDirectory contentDirectory, IConnectionManager connectionManager)
+        private readonly IConfigurationManager _config;
+
+        public DlnaServerService(IDlnaManager dlnaManager, IContentDirectory contentDirectory, IConnectionManager connectionManager, IConfigurationManager config)
         {
             _dlnaManager = dlnaManager;
             _contentDirectory = contentDirectory;
             _connectionManager = connectionManager;
+            _config = config;
         }
 
         public object Get(GetDescriptionXml request)

+ 6 - 0
MediaBrowser.Api/ItemUpdateService.cs

@@ -108,6 +108,12 @@ namespace MediaBrowser.Api
                 hasTags.Tags = request.Tags;
             }
 
+            var hasTaglines = item as IHasTaglines;
+            if (hasTaglines != null)
+            {
+                hasTaglines.Taglines = request.Taglines;
+            }
+
             var hasShortOverview = item as IHasShortOverview;
             if (hasShortOverview != null)
             {

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

@@ -101,6 +101,7 @@
     <Compile Include="NotificationsService.cs" />
     <Compile Include="PackageReviewService.cs" />
     <Compile Include="PackageService.cs" />
+    <Compile Include="Playback\BifService.cs" />
     <Compile Include="Playback\EndlessStreamCopy.cs" />
     <Compile Include="Playback\Hls\BaseHlsService.cs" />
     <Compile Include="Playback\Hls\DynamicHlsService.cs" />

+ 13 - 3
MediaBrowser.Api/Playback/BaseStreamingService.cs

@@ -1447,6 +1447,16 @@ namespace MediaBrowser.Api.Playback
                     state.MediaPath = mediaUrl;
                     state.InputProtocol = MediaProtocol.Http;
                 }
+                else
+                {
+                    // No media info, so this is probably needed
+                    state.DeInterlace = true;
+                }
+
+                if (recording.RecordingInfo.Status == RecordingStatus.InProgress)
+                {
+                    state.ReadInputAtNativeFramerate = true;
+                }
 
                 state.RunTimeTicks = recording.RunTimeTicks;
 
@@ -1455,9 +1465,7 @@ namespace MediaBrowser.Api.Playback
                     await Task.Delay(1000, cancellationToken).ConfigureAwait(false);
                 }
 
-                state.ReadInputAtNativeFramerate = recording.RecordingInfo.Status == RecordingStatus.InProgress;
                 state.OutputAudioSync = "1000";
-                state.DeInterlace = true;
                 state.InputVideoSync = "-1";
                 state.InputAudioSync = "1";
                 state.InputContainer = recording.Container;
@@ -1524,7 +1532,9 @@ namespace MediaBrowser.Api.Playback
                 state.RunTimeTicks = mediaSource.RunTimeTicks;
             }
 
-            if (string.Equals(state.InputContainer, "wtv", StringComparison.OrdinalIgnoreCase))
+            // If it's a wtv and we don't have media info, we will probably need to deinterlace
+            if (string.Equals(state.InputContainer, "wtv", StringComparison.OrdinalIgnoreCase) &&
+                mediaStreams.Count == 0)
             {
                 state.DeInterlace = true;
             }

+ 186 - 0
MediaBrowser.Api/Playback/BifService.cs

@@ -0,0 +1,186 @@
+using MediaBrowser.Common.IO;
+using MediaBrowser.Controller;
+using MediaBrowser.Controller.Entities;
+using MediaBrowser.Controller.Library;
+using MediaBrowser.Controller.MediaEncoding;
+using ServiceStack;
+using System;
+using System.Collections.Concurrent;
+using System.Globalization;
+using System.IO;
+using System.Linq;
+using System.Threading;
+using System.Threading.Tasks;
+
+namespace MediaBrowser.Api.Playback
+{
+    [Route("/Videos/{Id}/index.bif", "GET")]
+    public class GetBifFile
+    {
+        [ApiMember(Name = "MediaSourceId", Description = "The media version id, if playing an alternate version", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "GET")]
+        public string MediaSourceId { get; set; }
+
+        [ApiMember(Name = "MaxWidth", Description = "Optional. The maximum horizontal resolution of the encoded video.", IsRequired = false, DataType = "int", ParameterType = "query", Verb = "GET")]
+        public int? MaxWidth { get; set; }
+
+        [ApiMember(Name = "Id", Description = "Item Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "GET")]
+        public string Id { get; set; }
+    }
+
+    public class BifService : BaseApiService
+    {
+        private readonly IServerApplicationPaths _appPaths;
+        private readonly ILibraryManager _libraryManager;
+        private readonly IMediaEncoder _mediaEncoder;
+        private readonly IFileSystem _fileSystem;
+
+        public BifService(IServerApplicationPaths appPaths, ILibraryManager libraryManager, IMediaEncoder mediaEncoder, IFileSystem fileSystem)
+        {
+            _appPaths = appPaths;
+            _libraryManager = libraryManager;
+            _mediaEncoder = mediaEncoder;
+            _fileSystem = fileSystem;
+        }
+
+        public object Get(GetBifFile request)
+        {
+            return ToStaticFileResult(GetBifFile(request).Result);
+        }
+
+        private async Task<string> GetBifFile(GetBifFile request)
+        {
+            var widthVal = request.MaxWidth.HasValue ? request.MaxWidth.Value.ToString(CultureInfo.InvariantCulture) : string.Empty;
+
+            var item = _libraryManager.GetItemById(request.Id);
+            var mediaSources = ((IHasMediaSources)item).GetMediaSources(false).ToList();
+            var mediaSource = mediaSources.FirstOrDefault(i => string.Equals(i.Id, request.MediaSourceId)) ?? mediaSources.First();
+
+            var path = Path.Combine(_appPaths.ImageCachePath, "bif", request.Id, request.MediaSourceId, widthVal, "index.bif");
+
+            if (File.Exists(path))
+            {
+                return path;
+            }
+
+            var protocol = mediaSource.Protocol;
+
+            var inputPath = MediaEncoderHelpers.GetInputArgument(mediaSource.Path, protocol, null, mediaSource.PlayableStreamFileNames);
+
+            var semaphore = GetLock(path);
+
+            await semaphore.WaitAsync().ConfigureAwait(false);
+
+            try
+            {
+                if (File.Exists(path))
+                {
+                    return path;
+                }
+                
+                await _mediaEncoder.ExtractVideoImagesOnInterval(inputPath, protocol, mediaSource.Video3DFormat,
+                        TimeSpan.FromSeconds(10), Path.GetDirectoryName(path), "img_", request.MaxWidth, CancellationToken.None)
+                        .ConfigureAwait(false);
+
+                var images = new DirectoryInfo(Path.GetDirectoryName(path))
+                    .EnumerateFiles()
+                    .Where(img => string.Equals(img.Extension, ".jpg", StringComparison.Ordinal))
+                    .OrderBy(i => i.FullName)
+                    .ToList();
+
+                using (var fs = _fileSystem.GetFileStream(path, FileMode.Create, FileAccess.Write, FileShare.Read, true))
+                {
+                    var magicNumber = new byte[] { 0x89, 0x42, 0x49, 0x46, 0x0d, 0x0a, 0x1a, 0x0a };
+                    await fs.WriteAsync(magicNumber, 0, magicNumber.Length);
+
+                    // version
+                    var bytes = GetBytes(0);
+                    await fs.WriteAsync(bytes, 0, bytes.Length);
+
+                    // image count
+                    bytes = GetBytes(images.Count);
+                    await fs.WriteAsync(bytes, 0, bytes.Length);
+
+                    // interval in ms
+                    bytes = GetBytes(10000);
+                    await fs.WriteAsync(bytes, 0, bytes.Length);
+
+                    // reserved
+                    for (var i = 20; i <= 63; i++)
+                    {
+                        bytes = new byte[] { 0x00 };
+                        await fs.WriteAsync(bytes, 0, bytes.Length);
+                    }
+
+                    // write the bif index
+                    var index = 0;
+                    long imageOffset = 64 + (8 * images.Count) + 8;
+
+                    foreach (var img in images)
+                    {
+                        bytes = GetBytes(index);
+                        await fs.WriteAsync(bytes, 0, bytes.Length);
+
+                        bytes = GetBytes(imageOffset);
+                        await fs.WriteAsync(bytes, 0, bytes.Length);
+
+                        imageOffset += img.Length;
+
+                        index++;
+                    }
+
+                    bytes = new byte[] { 0xff, 0xff, 0xff, 0xff };
+                    await fs.WriteAsync(bytes, 0, bytes.Length);
+
+                    bytes = GetBytes(imageOffset);
+                    await fs.WriteAsync(bytes, 0, bytes.Length);
+
+                    // write the images
+                    foreach (var img in images)
+                    {
+                        using (var imgStream = _fileSystem.GetFileStream(img.FullName, FileMode.Open, FileAccess.Read, FileShare.ReadWrite, true))
+                        {
+                            await imgStream.CopyToAsync(fs).ConfigureAwait(false);
+                        }
+                    }
+                }
+
+                return path;
+            }
+            finally
+            {
+                semaphore.Release();
+            }
+        }
+
+        private byte[] GetBytes(int value)
+        {
+            byte[] bytes = BitConverter.GetBytes(value);
+            if (BitConverter.IsLittleEndian)
+                Array.Reverse(bytes);
+            return bytes;
+        }
+
+        private byte[] GetBytes(long value)
+        {
+            var intVal = Convert.ToInt32(value);
+            return GetBytes(intVal);
+
+            //byte[] bytes = BitConverter.GetBytes(value);
+            //if (BitConverter.IsLittleEndian)
+            //    Array.Reverse(bytes);
+            //return bytes;
+        }
+
+        private static readonly ConcurrentDictionary<string, SemaphoreSlim> SemaphoreLocks = new ConcurrentDictionary<string, SemaphoreSlim>();
+
+        /// <summary>
+        /// Gets the lock.
+        /// </summary>
+        /// <param name="filename">The filename.</param>
+        /// <returns>System.Object.</returns>
+        private static SemaphoreSlim GetLock(string filename)
+        {
+            return SemaphoreLocks.GetOrAdd(filename, key => new SemaphoreSlim(1, 1));
+        }
+    }
+}

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

@@ -144,7 +144,8 @@ namespace MediaBrowser.Api.Playback.Progressive
                 return state.VideoStream != null && IsH264(state.VideoStream) ? args + " -bsf h264_mp4toannexb" : args;
             }
 
-            const string keyFrameArg = " -force_key_frames expr:if(isnan(prev_forced_t),gte(t,.1),gte(t,prev_forced_t+5))";
+            var keyFrameArg = string.Format(" -force_key_frames expr:gte(t,n_forced*{0})",
+                5.ToString(UsCulture));
 
             args += keyFrameArg;
 

+ 1 - 0
MediaBrowser.Common.Implementations/BaseApplicationHost.cs

@@ -342,6 +342,7 @@ namespace MediaBrowser.Common.Implementations
         /// </summary>
         protected virtual void FindParts()
         {
+            ConfigurationManager.AddParts(GetExports<IConfigurationFactory>());
             Plugins = GetExports<IPlugin>();
         }
 

+ 82 - 9
MediaBrowser.Common.Implementations/Configuration/BaseConfigurationManager.cs

@@ -1,10 +1,13 @@
-using System.IO;
-using MediaBrowser.Common.Configuration;
+using MediaBrowser.Common.Configuration;
 using MediaBrowser.Common.Events;
 using MediaBrowser.Model.Configuration;
 using MediaBrowser.Model.Logging;
 using MediaBrowser.Model.Serialization;
 using System;
+using System.Collections.Concurrent;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
 using System.Threading;
 
 namespace MediaBrowser.Common.Implementations.Configuration
@@ -25,6 +28,11 @@ namespace MediaBrowser.Common.Implementations.Configuration
         /// </summary>
         public event EventHandler<EventArgs> ConfigurationUpdated;
 
+        /// <summary>
+        /// Occurs when [named configuration updated].
+        /// </summary>
+        public event EventHandler<ConfigurationUpdateEventArgs> NamedConfigurationUpdated;
+
         /// <summary>
         /// Gets the logger.
         /// </summary>
@@ -74,6 +82,9 @@ namespace MediaBrowser.Common.Implementations.Configuration
             }
         }
 
+        private ConfigurationStore[] _configurationStores = {};
+        private IConfigurationFactory[] _configurationFactories;
+
         /// <summary>
         /// Initializes a new instance of the <see cref="BaseConfigurationManager" /> class.
         /// </summary>
@@ -89,10 +100,14 @@ namespace MediaBrowser.Common.Implementations.Configuration
             UpdateCachePath();
         }
 
-        /// <summary>
-        /// The _save lock
-        /// </summary>
-        private readonly object _configurationSaveLock = new object();
+        public void AddParts(IEnumerable<IConfigurationFactory> factories)
+        {
+            _configurationFactories = factories.ToArray();
+
+            _configurationStores = _configurationFactories
+                .SelectMany(i => i.GetConfigurations())
+                .ToArray();
+        }
 
         /// <summary>
         /// Saves the configuration.
@@ -103,7 +118,7 @@ namespace MediaBrowser.Common.Implementations.Configuration
 
             Directory.CreateDirectory(Path.GetDirectoryName(path));
 
-            lock (_configurationSaveLock)
+            lock (_configurationSyncLock)
             {
                 XmlSerializer.SerializeToFile(CommonConfiguration, path);
             }
@@ -144,8 +159,8 @@ namespace MediaBrowser.Common.Implementations.Configuration
         /// </summary>
         private void UpdateCachePath()
         {
-            ((BaseApplicationPaths)CommonApplicationPaths).CachePath = string.IsNullOrEmpty(CommonConfiguration.CachePath) ? 
-                null : 
+            ((BaseApplicationPaths)CommonApplicationPaths).CachePath = string.IsNullOrEmpty(CommonConfiguration.CachePath) ?
+                null :
                 CommonConfiguration.CachePath;
         }
 
@@ -168,5 +183,63 @@ namespace MediaBrowser.Common.Implementations.Configuration
                 }
             }
         }
+
+        private readonly ConcurrentDictionary<string, object> _configurations = new ConcurrentDictionary<string, object>();
+
+        private string GetConfigurationFile(string key)
+        {
+            return Path.Combine(CommonApplicationPaths.ConfigurationDirectoryPath, key.ToLower() + ".xml");
+        }
+
+        public object GetConfiguration(string key)
+        {
+            return _configurations.GetOrAdd(key, k =>
+            {
+                var file = GetConfigurationFile(key);
+
+                var configurationType = _configurationStores
+                    .First(i => string.Equals(i.Key, key, StringComparison.OrdinalIgnoreCase))
+                    .ConfigurationType;
+
+                lock (_configurationSyncLock)
+                {
+                    return ConfigurationHelper.GetXmlConfiguration(configurationType, file, XmlSerializer);
+                }
+            });
+        }
+
+        public void SaveConfiguration(string key, object configuration)
+        {
+            var configurationType = GetConfigurationType(key);
+
+            if (configuration.GetType() != configurationType)
+            {
+                throw new ArgumentException("Expected configuration type is " + configurationType.Name);
+            }
+
+            _configurations.AddOrUpdate(key, configuration, (k, v) => configuration);
+
+            var path = GetConfigurationFile(key);
+            Directory.CreateDirectory(Path.GetDirectoryName(path));
+
+            lock (_configurationSyncLock)
+            {
+                XmlSerializer.SerializeToFile(configuration, path);
+            }
+
+            EventHelper.FireEventIfNotNull(NamedConfigurationUpdated, this, new ConfigurationUpdateEventArgs
+            {
+                Key = key,
+                NewConfiguration = configuration
+
+            }, Logger);
+        }
+
+        public Type GetConfigurationType(string key)
+        {
+            return _configurationStores
+                .First(i => string.Equals(i.Key, key, StringComparison.OrdinalIgnoreCase))
+                .ConfigurationType;
+        }
     }
 }

+ 17 - 7
MediaBrowser.Common.Implementations/Updates/InstallationManager.cs

@@ -68,7 +68,7 @@ namespace MediaBrowser.Common.Implementations.Updates
         /// <param name="newVersion">The new version.</param>
         private void OnPluginUpdated(IPlugin plugin, PackageVersionInfo newVersion)
         {
-            _logger.Info("Plugin updated: {0} {1} {2}", newVersion.name, newVersion.version, newVersion.classification);
+            _logger.Info("Plugin updated: {0} {1} {2}", newVersion.name, newVersion.versionStr ?? string.Empty, newVersion.classification);
 
             EventHelper.FireEventIfNotNull(PluginUpdated, this, new GenericEventArgs<Tuple<IPlugin, PackageVersionInfo>> { Argument = new Tuple<IPlugin, PackageVersionInfo>(plugin, newVersion) }, _logger);
 
@@ -87,7 +87,7 @@ namespace MediaBrowser.Common.Implementations.Updates
         /// <param name="package">The package.</param>
         private void OnPluginInstalled(PackageVersionInfo package)
         {
-            _logger.Info("New plugin installed: {0} {1} {2}", package.name, package.version, package.classification);
+            _logger.Info("New plugin installed: {0} {1} {2}", package.name, package.versionStr ?? string.Empty, package.classification);
 
             EventHelper.FireEventIfNotNull(PluginInstalled, this, new GenericEventArgs<PackageVersionInfo> { Argument = package }, _logger);
 
@@ -133,6 +133,16 @@ namespace MediaBrowser.Common.Implementations.Updates
             _logger = logger;
         }
 
+        private Version GetPackageVersion(PackageVersionInfo version)
+        {
+            return new Version(ValueOrDefault(version.versionStr, "0.0.0.1"));
+        }
+
+        private static string ValueOrDefault(string str, string def)
+        {
+            return string.IsNullOrEmpty(str) ? def : str;
+        }
+
         /// <summary>
         /// Gets all available packages.
         /// </summary>
@@ -197,7 +207,7 @@ namespace MediaBrowser.Common.Implementations.Updates
             foreach (var package in packages)
             {
                 package.versions = package.versions.Where(v => !string.IsNullOrWhiteSpace(v.sourceUrl))
-                    .OrderByDescending(v => v.version).ToList();
+                    .OrderByDescending(GetPackageVersion).ToList();
             }
 
             // Remove packages with no versions
@@ -211,7 +221,7 @@ namespace MediaBrowser.Common.Implementations.Updates
             foreach (var package in packages)
             {
                 package.versions = package.versions.Where(v => !string.IsNullOrWhiteSpace(v.sourceUrl))
-                    .OrderByDescending(v => v.version).ToList();
+                    .OrderByDescending(GetPackageVersion).ToList();
             }
 
             if (packageType.HasValue)
@@ -272,7 +282,7 @@ namespace MediaBrowser.Common.Implementations.Updates
                 return null;
             }
 
-            return package.versions.FirstOrDefault(v => v.version.Equals(version) && v.classification == classification);
+            return package.versions.FirstOrDefault(v => GetPackageVersion(v).Equals(version) && v.classification == classification);
         }
 
         /// <summary>
@@ -309,7 +319,7 @@ namespace MediaBrowser.Common.Implementations.Updates
             }
 
             return package.versions
-                .OrderByDescending(v => v.version)
+                .OrderByDescending(GetPackageVersion)
                 .FirstOrDefault(v => v.classification <= classification && IsPackageVersionUpToDate(v, currentServerVersion));
         }
 
@@ -338,7 +348,7 @@ namespace MediaBrowser.Common.Implementations.Updates
             {
                 var latestPluginInfo = GetLatestCompatibleVersion(catalog, p.Name, p.Id.ToString(), applicationVersion, _config.CommonConfiguration.SystemUpdateLevel);
 
-                return latestPluginInfo != null && latestPluginInfo.version != null && latestPluginInfo.version > p.Version ? latestPluginInfo : null;
+                return latestPluginInfo != null && GetPackageVersion(latestPluginInfo) > p.Version ? latestPluginInfo : null;
 
             }).Where(i => i != null).ToList();
 

+ 18 - 0
MediaBrowser.Common/Configuration/ConfigurationUpdateEventArgs.cs

@@ -0,0 +1,18 @@
+using System;
+
+namespace MediaBrowser.Common.Configuration
+{
+    public class ConfigurationUpdateEventArgs : EventArgs
+    {
+        /// <summary>
+        /// Gets or sets the key.
+        /// </summary>
+        /// <value>The key.</value>
+        public string Key { get; set; }
+        /// <summary>
+        /// Gets or sets the new configuration.
+        /// </summary>
+        /// <value>The new configuration.</value>
+        public object NewConfiguration { get; set; }
+    }
+}

+ 17 - 0
MediaBrowser.Common/Configuration/IConfigurationFactory.cs

@@ -0,0 +1,17 @@
+using System;
+using System.Collections.Generic;
+
+namespace MediaBrowser.Common.Configuration
+{
+    public interface IConfigurationFactory
+    {
+        IEnumerable<ConfigurationStore> GetConfigurations();
+    }
+
+    public class ConfigurationStore
+    {
+        public string Key { get; set; }
+
+        public Type ConfigurationType { get; set; }
+    }
+}

+ 42 - 1
MediaBrowser.Common/Configuration/IConfigurationManager.cs

@@ -1,5 +1,6 @@
 using MediaBrowser.Model.Configuration;
 using System;
+using System.Collections.Generic;
 
 namespace MediaBrowser.Common.Configuration
 {
@@ -9,7 +10,12 @@ namespace MediaBrowser.Common.Configuration
         /// Occurs when [configuration updated].
         /// </summary>
         event EventHandler<EventArgs> ConfigurationUpdated;
-        
+
+        /// <summary>
+        /// Occurs when [named configuration updated].
+        /// </summary>
+        event EventHandler<ConfigurationUpdateEventArgs> NamedConfigurationUpdated;
+
         /// <summary>
         /// Gets or sets the application paths.
         /// </summary>
@@ -32,5 +38,40 @@ namespace MediaBrowser.Common.Configuration
         /// </summary>
         /// <param name="newConfiguration">The new configuration.</param>
         void ReplaceConfiguration(BaseApplicationConfiguration newConfiguration);
+
+        /// <summary>
+        /// Gets the configuration.
+        /// </summary>
+        /// <param name="key">The key.</param>
+        /// <returns>System.Object.</returns>
+        object GetConfiguration(string key);
+
+        /// <summary>
+        /// Gets the type of the configuration.
+        /// </summary>
+        /// <param name="key">The key.</param>
+        /// <returns>Type.</returns>
+        Type GetConfigurationType(string key);
+
+        /// <summary>
+        /// Saves the configuration.
+        /// </summary>
+        /// <param name="key">The key.</param>
+        /// <param name="configuration">The configuration.</param>
+        void SaveConfiguration(string key, object configuration);
+
+        /// <summary>
+        /// Adds the parts.
+        /// </summary>
+        /// <param name="factories">The factories.</param>
+        void AddParts(IEnumerable<IConfigurationFactory> factories);
+    }
+
+    public static class ConfigurationManagerExtensions
+    {
+        public static T GetConfiguration<T>(this IConfigurationManager manager, string key)
+        {
+            return (T)manager.GetConfiguration(key);
+        }
     }
 }

+ 2 - 0
MediaBrowser.Common/MediaBrowser.Common.csproj

@@ -55,7 +55,9 @@
       <Link>Properties\SharedVersion.cs</Link>
     </Compile>
     <Compile Include="Configuration\ConfigurationHelper.cs" />
+    <Compile Include="Configuration\ConfigurationUpdateEventArgs.cs" />
     <Compile Include="Configuration\IConfigurationManager.cs" />
+    <Compile Include="Configuration\IConfigurationFactory.cs" />
     <Compile Include="Constants\Constants.cs" />
     <Compile Include="Events\EventHelper.cs" />
     <Compile Include="Extensions\BaseExtensions.cs" />

+ 5 - 0
MediaBrowser.Common/Net/MimeTypes.cs

@@ -228,6 +228,11 @@ namespace MediaBrowser.Common.Net
                 return "text/vtt";
             }
 
+            if (ext.Equals(".bif", StringComparison.OrdinalIgnoreCase))
+            {
+                return "application/octet-stream";
+            }
+
             throw new ArgumentException("Argument not supported: " + path);
         }
     }

+ 7 - 0
MediaBrowser.Controller/Chapters/IChapterManager.cs

@@ -3,6 +3,7 @@ using MediaBrowser.Model.Chapters;
 using System.Collections.Generic;
 using System.Threading;
 using System.Threading.Tasks;
+using MediaBrowser.Model.Configuration;
 using MediaBrowser.Model.Entities;
 
 namespace MediaBrowser.Controller.Chapters
@@ -70,5 +71,11 @@ namespace MediaBrowser.Controller.Chapters
         /// </summary>
         /// <returns>IEnumerable{ChapterProviderInfo}.</returns>
         IEnumerable<ChapterProviderInfo> GetProviders();
+
+        /// <summary>
+        /// Gets the configuration.
+        /// </summary>
+        /// <returns>ChapterOptions.</returns>
+        ChapterOptions GetConfiguration();
     }
 }

+ 18 - 0
MediaBrowser.Controller/Entities/Audio/MusicAlbum.cs

@@ -71,6 +71,23 @@ namespace MediaBrowser.Controller.Entities.Audio
         /// <value>The tags.</value>
         public List<string> Tags { get; set; }
 
+        /// <summary>
+        /// Gets the tracks.
+        /// </summary>
+        /// <value>The tracks.</value>
+        public IEnumerable<Audio> Tracks
+        {
+            get
+            {
+                return RecursiveChildren.OfType<Audio>();
+            }
+        }
+
+        protected override IEnumerable<BaseItem> GetEligibleChildrenForRecursiveChildren(User user)
+        {
+            return Tracks;
+        }
+
         /// <summary>
         /// Songs will group into us so don't also include us in the index
         /// </summary>
@@ -177,6 +194,7 @@ namespace MediaBrowser.Controller.Entities.Audio
         }
     }
 
+    [Obsolete]
     public class MusicAlbumDisc : Folder
     {
 

+ 0 - 5
MediaBrowser.Controller/Entities/Folder.cs

@@ -445,11 +445,6 @@ namespace MediaBrowser.Controller.Entities
 
                 cancellationToken.ThrowIfCancellationRequested();
 
-                if (this is UserRootFolder)
-                {
-                    var b = true;
-                }
-
                 foreach (var child in nonCachedChildren)
                 {
                     BaseItem currentChild;

+ 21 - 0
MediaBrowser.Controller/MediaEncoding/IMediaEncoder.cs

@@ -43,6 +43,27 @@ namespace MediaBrowser.Controller.MediaEncoding
         /// <returns>Task{Stream}.</returns>
         Task<Stream> ExtractVideoImage(string[] inputFiles, MediaProtocol protocol, Video3DFormat? threedFormat, TimeSpan? offset, CancellationToken cancellationToken);
 
+        /// <summary>
+        /// Extracts the video images on interval.
+        /// </summary>
+        /// <param name="inputFiles">The input files.</param>
+        /// <param name="protocol">The protocol.</param>
+        /// <param name="threedFormat">The threed format.</param>
+        /// <param name="interval">The interval.</param>
+        /// <param name="targetDirectory">The target directory.</param>
+        /// <param name="filenamePrefix">The filename prefix.</param>
+        /// <param name="maxWidth">The maximum width.</param>
+        /// <param name="cancellationToken">The cancellation token.</param>
+        /// <returns>Task.</returns>
+        Task ExtractVideoImagesOnInterval(string[] inputFiles, 
+            MediaProtocol protocol, 
+            Video3DFormat? threedFormat, 
+            TimeSpan interval, 
+            string targetDirectory, 
+            string filenamePrefix, 
+            int? maxWidth,
+            CancellationToken cancellationToken);
+        
         /// <summary>
         /// Gets the media info.
         /// </summary>

+ 29 - 0
MediaBrowser.Dlna/ConfigurationExtension.cs

@@ -0,0 +1,29 @@
+using MediaBrowser.Common.Configuration;
+using MediaBrowser.Model.Configuration;
+using System.Collections.Generic;
+
+namespace MediaBrowser.Dlna
+{
+    public static class ConfigurationExtension
+    {
+        public static DlnaOptions GetDlnaConfiguration(this IConfigurationManager manager)
+        {
+            return manager.GetConfiguration<DlnaOptions>("dlna");
+        }
+    }
+
+    public class DlnaConfigurationFactory : IConfigurationFactory
+    {
+        public IEnumerable<ConfigurationStore> GetConfigurations()
+        {
+            return new List<ConfigurationStore>
+            {
+                new ConfigurationStore
+                {
+                    Key = "dlna",
+                    ConfigurationType = typeof (DlnaOptions)
+                }
+            };
+        }
+    }
+}

+ 4 - 2
MediaBrowser.Dlna/ContentDirectory/ContentDirectory.cs

@@ -89,9 +89,11 @@ namespace MediaBrowser.Dlna.ContentDirectory
                 }
             }
 
-            if (!string.IsNullOrEmpty(_config.Configuration.DlnaOptions.DefaultUserId))
+            var userId = _config.GetDlnaConfiguration().DefaultUserId;
+
+            if (!string.IsNullOrEmpty(userId))
             {
-                var user = _userManager.GetUserById(new Guid(_config.Configuration.DlnaOptions.DefaultUserId));
+                var user = _userManager.GetUserById(new Guid(userId));
 
                 if (user != null)
                 {

+ 4 - 4
MediaBrowser.Model/Dlna/Filter.cs → MediaBrowser.Dlna/Didl/Filter.cs

@@ -1,8 +1,9 @@
 using MediaBrowser.Model.Extensions;
 using System;
 using System.Collections.Generic;
+using System.Linq;
 
-namespace MediaBrowser.Model.Dlna
+namespace MediaBrowser.Dlna.Didl
 {
     public class Filter
     {
@@ -19,9 +20,8 @@ namespace MediaBrowser.Model.Dlna
         {
             _all = StringHelper.EqualsIgnoreCase(filter, "*");
 
-            List<string> list = new List<string>();
-            foreach (string s in (filter ?? string.Empty).Split(new[] {','}, StringSplitOptions.RemoveEmptyEntries))
-                list.Add(s);
+            var list = (filter ?? string.Empty).Split(new[] {','}, StringSplitOptions.RemoveEmptyEntries).ToList();
+
             _fields = list;
         }
 

+ 1 - 1
MediaBrowser.Model/Dlna/EventSubscription.cs → MediaBrowser.Dlna/Eventing/EventSubscription.cs

@@ -1,6 +1,6 @@
 using System;
 
-namespace MediaBrowser.Model.Dlna
+namespace MediaBrowser.Dlna.Eventing
 {
     public class EventSubscription
     {

+ 14 - 8
MediaBrowser.Dlna/Main/DlnaEntryPoint.cs

@@ -1,4 +1,5 @@
-using MediaBrowser.Common.Extensions;
+using MediaBrowser.Common.Configuration;
+using MediaBrowser.Common.Extensions;
 using MediaBrowser.Common.Net;
 using MediaBrowser.Controller;
 using MediaBrowser.Controller.Configuration;
@@ -58,34 +59,39 @@ namespace MediaBrowser.Dlna.Main
             StartSsdpHandler();
             ReloadComponents();
 
-            _config.ConfigurationUpdated += ConfigurationUpdated;
+            _config.NamedConfigurationUpdated += _config_NamedConfigurationUpdated;
         }
 
-        void ConfigurationUpdated(object sender, EventArgs e)
+        void _config_NamedConfigurationUpdated(object sender, ConfigurationUpdateEventArgs e)
         {
-            ReloadComponents();
+            if (string.Equals(e.Key, "dlna", StringComparison.OrdinalIgnoreCase))
+            {
+                ReloadComponents();
+            }
         }
 
         private void ReloadComponents()
         {
             var isServerStarted = _dlnaServerStarted;
 
-            if (_config.Configuration.DlnaOptions.EnableServer && !isServerStarted)
+            var options = _config.GetDlnaConfiguration();
+
+            if (options.EnableServer && !isServerStarted)
             {
                 StartDlnaServer();
             }
-            else if (!_config.Configuration.DlnaOptions.EnableServer && isServerStarted)
+            else if (!options.EnableServer && isServerStarted)
             {
                 DisposeDlnaServer();
             }
 
             var isPlayToStarted = _manager != null;
 
-            if (_config.Configuration.DlnaOptions.EnablePlayTo && !isPlayToStarted)
+            if (options.EnablePlayTo && !isPlayToStarted)
             {
                 StartPlayToManager();
             }
-            else if (!_config.Configuration.DlnaOptions.EnablePlayTo && isPlayToStarted)
+            else if (!options.EnablePlayTo && isPlayToStarted)
             {
                 DisposePlayToManager();
             }

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

@@ -51,13 +51,16 @@
     <Compile Include="..\SharedVersion.cs">
       <Link>Properties\SharedVersion.cs</Link>
     </Compile>
+    <Compile Include="ConfigurationExtension.cs" />
     <Compile Include="ConnectionManager\ConnectionManager.cs" />
     <Compile Include="ConnectionManager\ConnectionManagerXmlBuilder.cs" />
     <Compile Include="ConnectionManager\ControlHandler.cs" />
     <Compile Include="ConnectionManager\ServiceActionListBuilder.cs" />
+    <Compile Include="Didl\Filter.cs" />
     <Compile Include="DlnaManager.cs" />
     <Compile Include="Common\Argument.cs" />
     <Compile Include="Eventing\EventManager.cs" />
+    <Compile Include="Eventing\EventSubscription.cs" />
     <Compile Include="Main\DlnaEntryPoint.cs" />
     <Compile Include="PlayTo\CurrentIdEventArgs.cs" />
     <Compile Include="PlayTo\Device.cs">

+ 1 - 1
MediaBrowser.Dlna/PlayTo/PlayToController.cs

@@ -80,7 +80,7 @@ namespace MediaBrowser.Dlna.PlayTo
             _updateTimer = new Timer(updateTimer_Elapsed, null, 60000, 60000);
         }
 
-        private async void updateTimer_Elapsed(object state)
+        private void updateTimer_Elapsed(object state)
         {
             if (DateTime.UtcNow >= _device.DateLastActivity.AddSeconds(120))
             {

+ 3 - 3
MediaBrowser.Dlna/PlayTo/PlayToManager.cs

@@ -181,7 +181,7 @@ namespace MediaBrowser.Dlna.PlayTo
                 return;
             }
 
-            if (_config.Configuration.DlnaOptions.EnableDebugLogging)
+            if (_config.GetDlnaConfiguration().EnableDebugLogging)
             {
                 var headerTexts = args.Headers.Select(i => string.Format("{0}={1}", i.Key, i.Value));
                 var headerText = string.Join(",", headerTexts.ToArray());
@@ -220,7 +220,7 @@ namespace MediaBrowser.Dlna.PlayTo
                     {
                         _ssdpHandler.SendRendererSearchMessage(new IPEndPoint(localIp, 1900));
 
-                        var delay = _config.Configuration.DlnaOptions.ClientDiscoveryIntervalSeconds * 1000;
+                        var delay = _config.GetDlnaConfiguration().ClientDiscoveryIntervalSeconds * 1000;
 
                         await Task.Delay(delay, _tokenSource.Token).ConfigureAwait(false);
                     }
@@ -250,7 +250,7 @@ namespace MediaBrowser.Dlna.PlayTo
                     {
                         socket.SendTo(request, new IPEndPoint(IPAddress.Parse("239.255.255.250"), 1900));
 
-                        var delay = _config.Configuration.DlnaOptions.ClientDiscoveryIntervalSeconds * 1000;
+                        var delay = _config.GetDlnaConfiguration().ClientDiscoveryIntervalSeconds * 1000;
 
                         await Task.Delay(delay).ConfigureAwait(false);
                     }

+ 3 - 3
MediaBrowser.Dlna/PlayTo/SsdpHttpClient.cs

@@ -58,7 +58,7 @@ namespace MediaBrowser.Dlna.PlayTo
             {
                 Url = url,
                 UserAgent = USERAGENT,
-                LogRequest = _config.Configuration.DlnaOptions.EnableDebugLogging,
+                LogRequest = _config.GetDlnaConfiguration().EnableDebugLogging,
                 LogErrorResponseBody = true
             };
 
@@ -76,7 +76,7 @@ namespace MediaBrowser.Dlna.PlayTo
             {
                 Url = url,
                 UserAgent = USERAGENT,
-                LogRequest = _config.Configuration.DlnaOptions.EnableDebugLogging,
+                LogRequest = _config.GetDlnaConfiguration().EnableDebugLogging,
                 LogErrorResponseBody = true
             };
 
@@ -103,7 +103,7 @@ namespace MediaBrowser.Dlna.PlayTo
             {
                 Url = url,
                 UserAgent = USERAGENT,
-                LogRequest = _config.Configuration.DlnaOptions.EnableDebugLogging,
+                LogRequest = _config.GetDlnaConfiguration().EnableDebugLogging,
                 LogErrorResponseBody = true
             };
 

+ 1 - 1
MediaBrowser.Dlna/Service/BaseControlHandler.cs

@@ -28,7 +28,7 @@ namespace MediaBrowser.Dlna.Service
         {
             try
             {
-                if (Config.Configuration.DlnaOptions.EnableDebugLogging)
+                if (Config.GetDlnaConfiguration().EnableDebugLogging)
                 {
                     LogRequest(request);
                 }

+ 17 - 13
MediaBrowser.Dlna/Ssdp/SsdpHandler.cs

@@ -1,4 +1,4 @@
-using System.Text;
+using MediaBrowser.Common.Configuration;
 using MediaBrowser.Common.Events;
 using MediaBrowser.Controller.Configuration;
 using MediaBrowser.Dlna.Server;
@@ -9,6 +9,7 @@ using System.Collections.Generic;
 using System.Linq;
 using System.Net;
 using System.Net.Sockets;
+using System.Text;
 using System.Threading;
 
 namespace MediaBrowser.Dlna.Ssdp
@@ -42,12 +43,15 @@ namespace MediaBrowser.Dlna.Ssdp
             _config = config;
             _serverSignature = serverSignature;
 
-            _config.ConfigurationUpdated += _config_ConfigurationUpdated;
+            _config.NamedConfigurationUpdated += _config_ConfigurationUpdated;
         }
 
-        void _config_ConfigurationUpdated(object sender, EventArgs e)
+        void _config_ConfigurationUpdated(object sender, ConfigurationUpdateEventArgs e)
         {
-            ReloadAliveNotifier();
+            if (string.Equals(e.Key, "dlna", StringComparison.OrdinalIgnoreCase))
+            {
+                ReloadAliveNotifier();
+            }
         }
 
         public event EventHandler<SsdpMessageEventArgs> MessageReceived;
@@ -142,7 +146,7 @@ namespace MediaBrowser.Dlna.Ssdp
 
         private void RespondToSearch(IPEndPoint endpoint, string deviceType)
         {
-            if (_config.Configuration.DlnaOptions.EnableDebugLogging)
+            if (_config.GetDlnaConfiguration().EnableDebugLogging)
             {
                 _logger.Debug("RespondToSearch");
             }
@@ -166,7 +170,7 @@ namespace MediaBrowser.Dlna.Ssdp
 
                     SendDatagram(header, values, endpoint, null);
 
-                    if (_config.Configuration.DlnaOptions.EnableDebugLogging)
+                    if (_config.GetDlnaConfiguration().EnableDebugLogging)
                     {
                         _logger.Debug("{1} - Responded to a {0} request to {2}", d.Type, endpoint, d.Address.ToString());
                     }
@@ -255,14 +259,14 @@ namespace MediaBrowser.Dlna.Ssdp
 
                 var received = (byte[])result.AsyncState;
 
-                if (_config.Configuration.DlnaOptions.EnableDebugLogging)
+                if (_config.GetDlnaConfiguration().EnableDebugLogging)
                 {
                     _logger.Debug(Encoding.ASCII.GetString(received));
                 }
 
                 var args = SsdpHelper.ParseSsdpResponse(received, (IPEndPoint)endpoint);
 
-                if (_config.Configuration.DlnaOptions.EnableDebugLogging)
+                if (_config.GetDlnaConfiguration().EnableDebugLogging)
                 {
                     var headerTexts = args.Headers.Select(i => string.Format("{0}={1}", i.Key, i.Value));
                     var headerText = string.Join(",", headerTexts.ToArray());
@@ -285,7 +289,7 @@ namespace MediaBrowser.Dlna.Ssdp
 
         public void Dispose()
         {
-            _config.ConfigurationUpdated -= _config_ConfigurationUpdated;
+            _config.NamedConfigurationUpdated -= _config_ConfigurationUpdated;
 
             _isDisposed = true;
             while (_messageQueue.Count != 0)
@@ -337,7 +341,7 @@ namespace MediaBrowser.Dlna.Ssdp
 
         private void NotifyAll()
         {
-            if (_config.Configuration.DlnaOptions.EnableDebugLogging)
+            if (_config.GetDlnaConfiguration().EnableDebugLogging)
             {
                 _logger.Debug("Sending alive notifications");
             }
@@ -362,7 +366,7 @@ namespace MediaBrowser.Dlna.Ssdp
             values["NT"] = dev.Type;
             values["USN"] = dev.USN;
 
-            if (_config.Configuration.DlnaOptions.EnableDebugLogging)
+            if (_config.GetDlnaConfiguration().EnableDebugLogging)
             {
                 _logger.Debug("{0} said {1}", dev.USN, type);
             }
@@ -406,13 +410,13 @@ namespace MediaBrowser.Dlna.Ssdp
         private int _aliveNotifierIntervalMs;
         private void ReloadAliveNotifier()
         {
-            if (!_config.Configuration.DlnaOptions.BlastAliveMessages)
+            if (!_config.GetDlnaConfiguration().BlastAliveMessages)
             {
                 DisposeNotificationTimer();
                 return;
             }
 
-            var intervalMs = _config.Configuration.DlnaOptions.BlastAliveMessageIntervalSeconds * 1000;
+            var intervalMs = _config.GetDlnaConfiguration().BlastAliveMessageIntervalSeconds * 1000;
 
             if (_notificationTimer == null || _aliveNotifierIntervalMs != intervalMs)
             {

+ 6 - 6
MediaBrowser.Providers/BaseXmlProvider.cs → MediaBrowser.LocalMetadata/BaseXmlProvider.cs

@@ -1,13 +1,13 @@
-using MediaBrowser.Common.IO;
-using MediaBrowser.Controller.Entities;
-using MediaBrowser.Controller.Providers;
-using MediaBrowser.Model.Logging;
-using System;
+using System;
 using System.IO;
 using System.Threading;
 using System.Threading.Tasks;
+using MediaBrowser.Common.IO;
+using MediaBrowser.Controller.Entities;
+using MediaBrowser.Controller.Providers;
+using MediaBrowser.Model.Logging;
 
-namespace MediaBrowser.Providers
+namespace MediaBrowser.LocalMetadata
 {
     public abstract class BaseXmlProvider<T> : ILocalMetadataProvider<T>, IHasChangeMonitor
         where T : IHasMetadata, new()

+ 3 - 5
MediaBrowser.Providers/Folders/CollectionFolderImageProvider.cs → MediaBrowser.LocalMetadata/Images/CollectionFolderImageProvider.cs

@@ -1,10 +1,8 @@
-using MediaBrowser.Controller.Entities;
+using System.Collections.Generic;
+using MediaBrowser.Controller.Entities;
 using MediaBrowser.Controller.Providers;
-using MediaBrowser.Model.Entities;
-using MediaBrowser.Providers.All;
-using System.Collections.Generic;
 
-namespace MediaBrowser.Providers.Folders
+namespace MediaBrowser.LocalMetadata.Images
 {
     public class CollectionFolderLocalImageProvider : ILocalImageFileProvider, IHasOrder
     {

+ 6 - 6
MediaBrowser.Providers/TV/EpisodeLocalImageProvider.cs → MediaBrowser.LocalMetadata/Images/EpisodeLocalImageProvider.cs

@@ -1,13 +1,13 @@
-using MediaBrowser.Controller.Entities;
-using MediaBrowser.Controller.Entities.TV;
-using MediaBrowser.Controller.Providers;
-using MediaBrowser.Model.Entities;
-using System;
+using System;
 using System.Collections.Generic;
 using System.IO;
 using System.Linq;
+using MediaBrowser.Controller.Entities;
+using MediaBrowser.Controller.Entities.TV;
+using MediaBrowser.Controller.Providers;
+using MediaBrowser.Model.Entities;
 
-namespace MediaBrowser.Providers.TV
+namespace MediaBrowser.LocalMetadata.Images
 {
     public class EpisodeLocalLocalImageProvider : ILocalImageFileProvider
     {

+ 4 - 5
MediaBrowser.Providers/Folders/ImagesByNameImageProvider.cs → MediaBrowser.LocalMetadata/Images/ImagesByNameImageProvider.cs

@@ -1,12 +1,11 @@
-using MediaBrowser.Common.IO;
+using System.Collections.Generic;
+using System.IO;
+using MediaBrowser.Common.IO;
 using MediaBrowser.Controller.Configuration;
 using MediaBrowser.Controller.Entities;
 using MediaBrowser.Controller.Providers;
-using MediaBrowser.Providers.All;
-using System.Collections.Generic;
-using System.IO;
 
-namespace MediaBrowser.Providers.Folders
+namespace MediaBrowser.LocalMetadata.Images
 {
     public class ImagesByNameImageProvider : ILocalImageFileProvider, IHasOrder
     {

+ 4 - 4
MediaBrowser.Providers/All/InternalMetadataFolderImageProvider.cs → MediaBrowser.LocalMetadata/Images/InternalMetadataFolderImageProvider.cs

@@ -1,11 +1,11 @@
-using MediaBrowser.Controller.Configuration;
+using System.Collections.Generic;
+using System.IO;
+using MediaBrowser.Controller.Configuration;
 using MediaBrowser.Controller.Entities;
 using MediaBrowser.Controller.Entities.Audio;
 using MediaBrowser.Controller.Providers;
-using System.Collections.Generic;
-using System.IO;
 
-namespace MediaBrowser.Providers.All
+namespace MediaBrowser.LocalMetadata.Images
 {
     public class InternalMetadataFolderImageProvider : ILocalImageFileProvider, IHasOrder
     {

+ 7 - 7
MediaBrowser.Providers/All/LocalImageProvider.cs → MediaBrowser.LocalMetadata/Images/LocalImageProvider.cs

@@ -1,16 +1,16 @@
-using MediaBrowser.Controller.Entities;
+using System;
+using System.Collections.Generic;
+using System.Globalization;
+using System.IO;
+using System.Linq;
+using MediaBrowser.Controller.Entities;
 using MediaBrowser.Controller.Entities.Audio;
 using MediaBrowser.Controller.Entities.Movies;
 using MediaBrowser.Controller.Entities.TV;
 using MediaBrowser.Controller.Providers;
 using MediaBrowser.Model.Entities;
-using System;
-using System.Collections.Generic;
-using System.Globalization;
-using System.IO;
-using System.Linq;
 
-namespace MediaBrowser.Providers.All
+namespace MediaBrowser.LocalMetadata.Images
 {
     public class LocalImageProvider : ILocalImageFileProvider
     {

+ 110 - 0
MediaBrowser.LocalMetadata/MediaBrowser.LocalMetadata.csproj

@@ -0,0 +1,110 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
+  <PropertyGroup>
+    <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
+    <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
+    <ProjectGuid>{7EF9F3E0-697D-42F3-A08F-19DEB5F84392}</ProjectGuid>
+    <OutputType>Library</OutputType>
+    <AppDesignerFolder>Properties</AppDesignerFolder>
+    <RootNamespace>MediaBrowser.LocalMetadata</RootNamespace>
+    <AssemblyName>MediaBrowser.LocalMetadata</AssemblyName>
+    <TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
+    <FileAlignment>512</FileAlignment>
+  </PropertyGroup>
+  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
+    <DebugSymbols>true</DebugSymbols>
+    <DebugType>full</DebugType>
+    <Optimize>false</Optimize>
+    <OutputPath>bin\Debug\</OutputPath>
+    <DefineConstants>DEBUG;TRACE</DefineConstants>
+    <ErrorReport>prompt</ErrorReport>
+    <WarningLevel>4</WarningLevel>
+  </PropertyGroup>
+  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
+    <DebugType>pdbonly</DebugType>
+    <Optimize>true</Optimize>
+    <OutputPath>bin\Release\</OutputPath>
+    <DefineConstants>TRACE</DefineConstants>
+    <ErrorReport>prompt</ErrorReport>
+    <WarningLevel>4</WarningLevel>
+  </PropertyGroup>
+  <ItemGroup>
+    <Reference Include="System" />
+    <Reference Include="System.Core" />
+    <Reference Include="System.Xml.Linq" />
+    <Reference Include="System.Data.DataSetExtensions" />
+    <Reference Include="Microsoft.CSharp" />
+    <Reference Include="System.Data" />
+    <Reference Include="System.Xml" />
+  </ItemGroup>
+  <ItemGroup>
+    <Compile Include="BaseXmlProvider.cs" />
+    <Compile Include="Images\CollectionFolderImageProvider.cs" />
+    <Compile Include="Images\EpisodeLocalImageProvider.cs" />
+    <Compile Include="Images\ImagesByNameImageProvider.cs" />
+    <Compile Include="Images\InternalMetadataFolderImageProvider.cs" />
+    <Compile Include="Images\LocalImageProvider.cs" />
+    <Compile Include="Parsers\BoxSetXmlParser.cs" />
+    <Compile Include="Parsers\EpisodeXmlParser.cs" />
+    <Compile Include="Parsers\GameSystemXmlParser.cs" />
+    <Compile Include="Parsers\GameXmlParser.cs" />
+    <Compile Include="Parsers\MovieXmlParser.cs" />
+    <Compile Include="Parsers\MusicVideoXmlParser.cs" />
+    <Compile Include="Parsers\SeasonXmlParser.cs" />
+    <Compile Include="Parsers\SeriesXmlParser.cs" />
+    <Compile Include="Properties\AssemblyInfo.cs" />
+    <Compile Include="Providers\AdultVideoXmlProvider.cs" />
+    <Compile Include="Providers\AlbumXmlProvider.cs" />
+    <Compile Include="Providers\ArtistXmlProvider.cs" />
+    <Compile Include="Providers\BoxSetXmlProvider.cs" />
+    <Compile Include="Providers\ChannelXmlProvider.cs" />
+    <Compile Include="Providers\EpisodeXmlProvider.cs" />
+    <Compile Include="Providers\FolderXmlProvider.cs" />
+    <Compile Include="Providers\GameSystemXmlProvider.cs" />
+    <Compile Include="Providers\GameXmlProvider.cs" />
+    <Compile Include="Providers\MovieXmlProvider.cs" />
+    <Compile Include="Providers\MusicVideoXmlProvider.cs" />
+    <Compile Include="Providers\PersonXmlProvider.cs" />
+    <Compile Include="Providers\SeasonXmlProvider.cs" />
+    <Compile Include="Providers\SeriesXmlProvider.cs" />
+    <Compile Include="Providers\TrailerXmlProvider.cs" />
+    <Compile Include="Providers\VideoXmlProvider.cs" />
+    <Compile Include="Savers\AlbumXmlSaver.cs" />
+    <Compile Include="Savers\ArtistXmlSaver.cs" />
+    <Compile Include="Savers\BoxSetXmlSaver.cs" />
+    <Compile Include="Savers\ChannelXmlSaver.cs" />
+    <Compile Include="Savers\EpisodeXmlSaver.cs" />
+    <Compile Include="Savers\FolderXmlSaver.cs" />
+    <Compile Include="Savers\GameSystemXmlSaver.cs" />
+    <Compile Include="Savers\GameXmlSaver.cs" />
+    <Compile Include="Savers\MovieXmlSaver.cs" />
+    <Compile Include="Savers\PersonXmlSaver.cs" />
+    <Compile Include="Savers\SeasonXmlSaver.cs" />
+    <Compile Include="Savers\SeriesXmlSaver.cs" />
+    <Compile Include="Savers\XmlSaverHelpers.cs" />
+  </ItemGroup>
+  <ItemGroup>
+    <ProjectReference Include="..\MediaBrowser.Common\MediaBrowser.Common.csproj">
+      <Project>{9142eefa-7570-41e1-bfcc-468bb571af2f}</Project>
+      <Name>MediaBrowser.Common</Name>
+    </ProjectReference>
+    <ProjectReference Include="..\MediaBrowser.Controller\MediaBrowser.Controller.csproj">
+      <Project>{17e1f4e6-8abd-4fe5-9ecf-43d4b6087ba2}</Project>
+      <Name>MediaBrowser.Controller</Name>
+    </ProjectReference>
+    <ProjectReference Include="..\MediaBrowser.Model\MediaBrowser.Model.csproj">
+      <Project>{7eeeb4bb-f3e8-48fc-b4c5-70f0fff8329b}</Project>
+      <Name>MediaBrowser.Model</Name>
+    </ProjectReference>
+  </ItemGroup>
+  <ItemGroup />
+  <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
+  <!-- To modify your build process, add your task inside one of the targets below and uncomment it. 
+       Other similar extension points exist, see Microsoft.Common.targets.
+  <Target Name="BeforeBuild">
+  </Target>
+  <Target Name="AfterBuild">
+  </Target>
+  -->
+</Project>

+ 5 - 5
MediaBrowser.Providers/BoxSets/BoxSetXmlParser.cs → MediaBrowser.LocalMetadata/Parsers/BoxSetXmlParser.cs

@@ -1,12 +1,12 @@
-using MediaBrowser.Controller.Entities;
+using System.Collections.Generic;
+using System.Globalization;
+using System.Xml;
+using MediaBrowser.Controller.Entities;
 using MediaBrowser.Controller.Entities.Movies;
 using MediaBrowser.Controller.Providers;
 using MediaBrowser.Model.Logging;
-using System.Collections.Generic;
-using System.Globalization;
-using System.Xml;
 
-namespace MediaBrowser.Providers.BoxSets
+namespace MediaBrowser.LocalMetadata.Parsers
 {
     public class BoxSetXmlParser : BaseItemXmlParser<BoxSet>
     {

+ 6 - 6
MediaBrowser.Providers/TV/EpisodeXmlParser.cs → MediaBrowser.LocalMetadata/Parsers/EpisodeXmlParser.cs

@@ -1,15 +1,15 @@
-using MediaBrowser.Controller.Entities.TV;
-using MediaBrowser.Controller.Providers;
-using MediaBrowser.Model.Entities;
-using MediaBrowser.Model.Logging;
-using System;
+using System;
 using System.Collections.Generic;
 using System.Globalization;
 using System.IO;
 using System.Threading;
 using System.Xml;
+using MediaBrowser.Controller.Entities.TV;
+using MediaBrowser.Controller.Providers;
+using MediaBrowser.Model.Entities;
+using MediaBrowser.Model.Logging;
 
-namespace MediaBrowser.Providers.TV
+namespace MediaBrowser.LocalMetadata.Parsers
 {
     /// <summary>
     /// Class EpisodeXmlParser

+ 5 - 5
MediaBrowser.Providers/Games/GameSystemXmlParser.cs → MediaBrowser.LocalMetadata/Parsers/GameSystemXmlParser.cs

@@ -1,12 +1,12 @@
-using MediaBrowser.Controller.Entities;
+using System.Threading;
+using System.Threading.Tasks;
+using System.Xml;
+using MediaBrowser.Controller.Entities;
 using MediaBrowser.Controller.Providers;
 using MediaBrowser.Model.Entities;
 using MediaBrowser.Model.Logging;
-using System.Threading;
-using System.Threading.Tasks;
-using System.Xml;
 
-namespace MediaBrowser.Providers.Games
+namespace MediaBrowser.LocalMetadata.Parsers
 {
     public class GameSystemXmlParser : BaseItemXmlParser<GameSystem>
     {

+ 6 - 6
MediaBrowser.Providers/Games/GameXmlParser.cs → MediaBrowser.LocalMetadata/Parsers/GameXmlParser.cs

@@ -1,13 +1,13 @@
-using MediaBrowser.Controller.Entities;
-using MediaBrowser.Controller.Providers;
-using MediaBrowser.Model.Entities;
-using MediaBrowser.Model.Logging;
-using System.Globalization;
+using System.Globalization;
 using System.Threading;
 using System.Threading.Tasks;
 using System.Xml;
+using MediaBrowser.Controller.Entities;
+using MediaBrowser.Controller.Providers;
+using MediaBrowser.Model.Entities;
+using MediaBrowser.Model.Logging;
 
-namespace MediaBrowser.Providers.Games
+namespace MediaBrowser.LocalMetadata.Parsers
 {
     /// <summary>
     /// Class EpisodeXmlParser

+ 5 - 5
MediaBrowser.Providers/Movies/MovieXmlParser.cs → MediaBrowser.LocalMetadata/Parsers/MovieXmlParser.cs

@@ -1,13 +1,13 @@
-using MediaBrowser.Controller.Entities;
+using System.Collections.Generic;
+using System.Threading;
+using System.Xml;
+using MediaBrowser.Controller.Entities;
 using MediaBrowser.Controller.Entities.Movies;
 using MediaBrowser.Controller.Providers;
 using MediaBrowser.Model.Entities;
 using MediaBrowser.Model.Logging;
-using System.Collections.Generic;
-using System.Threading;
-using System.Xml;
 
-namespace MediaBrowser.Providers.Movies
+namespace MediaBrowser.LocalMetadata.Parsers
 {
     /// <summary>
     /// Class EpisodeXmlParser

+ 3 - 4
MediaBrowser.Providers/Music/MusicVideoXmlParser.cs → MediaBrowser.LocalMetadata/Parsers/MusicVideoXmlParser.cs

@@ -1,10 +1,9 @@
-using MediaBrowser.Controller.Entities;
-using MediaBrowser.Controller.Entities.Audio;
+using System.Xml;
+using MediaBrowser.Controller.Entities;
 using MediaBrowser.Controller.Providers;
 using MediaBrowser.Model.Logging;
-using System.Xml;
 
-namespace MediaBrowser.Providers.Music
+namespace MediaBrowser.LocalMetadata.Parsers
 {
     public class MusicVideoXmlParser : BaseItemXmlParser<MusicVideo>
     {

+ 3 - 3
MediaBrowser.Providers/TV/SeasonXmlParser.cs → MediaBrowser.LocalMetadata/Parsers/SeasonXmlParser.cs

@@ -1,9 +1,9 @@
-using MediaBrowser.Controller.Entities.TV;
+using System.Xml;
+using MediaBrowser.Controller.Entities.TV;
 using MediaBrowser.Controller.Providers;
 using MediaBrowser.Model.Logging;
-using System.Xml;
 
-namespace MediaBrowser.Providers.TV
+namespace MediaBrowser.LocalMetadata.Parsers
 {
     public class SeasonXmlParser : BaseItemXmlParser<Season>
     {

+ 4 - 4
MediaBrowser.Providers/TV/SeriesXmlParser.cs → MediaBrowser.LocalMetadata/Parsers/SeriesXmlParser.cs

@@ -1,12 +1,12 @@
-using MediaBrowser.Controller.Entities.TV;
+using System;
+using System.Xml;
+using MediaBrowser.Controller.Entities.TV;
 using MediaBrowser.Controller.Library;
 using MediaBrowser.Controller.Providers;
 using MediaBrowser.Model.Entities;
 using MediaBrowser.Model.Logging;
-using System;
-using System.Xml;
 
-namespace MediaBrowser.Providers.TV
+namespace MediaBrowser.LocalMetadata.Parsers
 {
     /// <summary>
     /// Class SeriesXmlParser

+ 36 - 0
MediaBrowser.LocalMetadata/Properties/AssemblyInfo.cs

@@ -0,0 +1,36 @@
+using System.Reflection;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+
+// General Information about an assembly is controlled through the following 
+// set of attributes. Change these attribute values to modify the information
+// associated with an assembly.
+[assembly: AssemblyTitle("MediaBrowser.LocalMetadata")]
+[assembly: AssemblyDescription("")]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCompany("")]
+[assembly: AssemblyProduct("MediaBrowser.LocalMetadata")]
+[assembly: AssemblyCopyright("Copyright ©  2014")]
+[assembly: AssemblyTrademark("")]
+[assembly: AssemblyCulture("")]
+
+// Setting ComVisible to false makes the types in this assembly not visible 
+// to COM components.  If you need to access a type in this assembly from 
+// COM, set the ComVisible attribute to true on that type.
+[assembly: ComVisible(false)]
+
+// The following GUID is for the ID of the typelib if this project is exposed to COM
+[assembly: Guid("1c669501-2113-493a-b0ed-f8fd26311941")]
+
+// Version information for an assembly consists of the following four values:
+//
+//      Major Version
+//      Minor Version 
+//      Build Number
+//      Revision
+//
+// You can specify all the values or you can default the Build and Revision Numbers 
+// by using the '*' as shown below:
+// [assembly: AssemblyVersion("1.0.*")]
+[assembly: AssemblyVersion("1.0.0.0")]
+[assembly: AssemblyFileVersion("1.0.0.0")]

+ 6 - 6
MediaBrowser.Providers/AdultVideos/AdultVideoXmlProvider.cs → MediaBrowser.LocalMetadata/Providers/AdultVideoXmlProvider.cs

@@ -1,14 +1,14 @@
-using MediaBrowser.Common.IO;
+using System.Collections.Generic;
+using System.IO;
+using System.Threading;
+using MediaBrowser.Common.IO;
 using MediaBrowser.Controller.Entities;
 using MediaBrowser.Controller.Providers;
+using MediaBrowser.LocalMetadata.Parsers;
 using MediaBrowser.Model.Entities;
 using MediaBrowser.Model.Logging;
-using MediaBrowser.Providers.Movies;
-using System.Collections.Generic;
-using System.IO;
-using System.Threading;
 
-namespace MediaBrowser.Providers.AdultVideos
+namespace MediaBrowser.LocalMetadata.Providers
 {
     class AdultVideoXmlProvider : BaseXmlProvider<AdultVideo>
     {

+ 5 - 5
MediaBrowser.Providers/Music/AlbumXmlProvider.cs → MediaBrowser.LocalMetadata/Providers/AlbumXmlProvider.cs

@@ -1,13 +1,13 @@
-using MediaBrowser.Common.IO;
+using System.IO;
+using System.Threading;
+using MediaBrowser.Common.IO;
 using MediaBrowser.Controller.Entities.Audio;
 using MediaBrowser.Controller.Providers;
 using MediaBrowser.Model.Logging;
-using System.IO;
-using System.Threading;
 
-namespace MediaBrowser.Providers.Music
+namespace MediaBrowser.LocalMetadata.Providers
 {
-    class AlbumXmlProvider : BaseXmlProvider<MusicAlbum>
+    public class AlbumXmlProvider : BaseXmlProvider<MusicAlbum>
     {
         private readonly ILogger _logger;
 

+ 4 - 4
MediaBrowser.Providers/Music/ArtistXmlProvider.cs → MediaBrowser.LocalMetadata/Providers/ArtistXmlProvider.cs

@@ -1,11 +1,11 @@
-using MediaBrowser.Common.IO;
+using System.IO;
+using System.Threading;
+using MediaBrowser.Common.IO;
 using MediaBrowser.Controller.Entities.Audio;
 using MediaBrowser.Controller.Providers;
 using MediaBrowser.Model.Logging;
-using System.IO;
-using System.Threading;
 
-namespace MediaBrowser.Providers.Music
+namespace MediaBrowser.LocalMetadata.Providers
 {
     class ArtistXmlProvider : BaseXmlProvider<MusicArtist>
     {

+ 5 - 4
MediaBrowser.Providers/BoxSets/BoxSetXmlProvider.cs → MediaBrowser.LocalMetadata/Providers/BoxSetXmlProvider.cs

@@ -1,11 +1,12 @@
-using MediaBrowser.Common.IO;
+using System.IO;
+using System.Threading;
+using MediaBrowser.Common.IO;
 using MediaBrowser.Controller.Entities.Movies;
 using MediaBrowser.Controller.Providers;
+using MediaBrowser.LocalMetadata.Parsers;
 using MediaBrowser.Model.Logging;
-using System.IO;
-using System.Threading;
 
-namespace MediaBrowser.Providers.BoxSets
+namespace MediaBrowser.LocalMetadata.Providers
 {
     /// <summary>
     /// Class BoxSetXmlProvider.

+ 4 - 4
MediaBrowser.Providers/LiveTv/ChannelXmlProvider.cs → MediaBrowser.LocalMetadata/Providers/ChannelXmlProvider.cs

@@ -1,11 +1,11 @@
-using MediaBrowser.Common.IO;
+using System.IO;
+using System.Threading;
+using MediaBrowser.Common.IO;
 using MediaBrowser.Controller.LiveTv;
 using MediaBrowser.Controller.Providers;
 using MediaBrowser.Model.Logging;
-using System.IO;
-using System.Threading;
 
-namespace MediaBrowser.Providers.LiveTv
+namespace MediaBrowser.LocalMetadata.Providers
 {
     public class ChannelXmlProvider : BaseXmlProvider<LiveTvChannel>
     {

+ 16 - 6
MediaBrowser.Providers/TV/EpisodeXmlProvider.cs → MediaBrowser.LocalMetadata/Providers/EpisodeXmlProvider.cs

@@ -1,15 +1,16 @@
-using MediaBrowser.Common.IO;
+using System.Collections.Generic;
+using System.IO;
+using System.Threading;
+using MediaBrowser.Common.IO;
 using MediaBrowser.Controller.Entities.TV;
 using MediaBrowser.Controller.Providers;
+using MediaBrowser.LocalMetadata.Parsers;
 using MediaBrowser.Model.Entities;
 using MediaBrowser.Model.Logging;
-using System.Collections.Generic;
-using System.IO;
-using System.Threading;
 
-namespace MediaBrowser.Providers.TV
+namespace MediaBrowser.LocalMetadata.Providers
 {
-    public class EpisodeXmlProvider : BaseXmlProvider<Episode>
+    public class EpisodeXmlProvider : BaseXmlProvider<Episode>, IHasOrder
     {
         private readonly ILogger _logger;
 
@@ -39,5 +40,14 @@ namespace MediaBrowser.Providers.TV
 
             return directoryService.GetFile(metadataFile);
         }
+
+        public int Order
+        {
+            get
+            {
+                // After Xbmc
+                return 1;
+            }
+        }
     }
 }

+ 4 - 4
MediaBrowser.Providers/Folders/FolderXmlProvider.cs → MediaBrowser.LocalMetadata/Providers/FolderXmlProvider.cs

@@ -1,11 +1,11 @@
-using MediaBrowser.Common.IO;
+using System.IO;
+using System.Threading;
+using MediaBrowser.Common.IO;
 using MediaBrowser.Controller.Entities;
 using MediaBrowser.Controller.Providers;
 using MediaBrowser.Model.Logging;
-using System.IO;
-using System.Threading;
 
-namespace MediaBrowser.Providers.Folders
+namespace MediaBrowser.LocalMetadata.Providers
 {
     /// <summary>
     /// Provides metadata for Folders and all subclasses by parsing folder.xml

+ 5 - 4
MediaBrowser.Providers/Games/GameSystemXmlProvider.cs → MediaBrowser.LocalMetadata/Providers/GameSystemXmlProvider.cs

@@ -1,11 +1,12 @@
-using MediaBrowser.Common.IO;
+using System.IO;
+using System.Threading;
+using MediaBrowser.Common.IO;
 using MediaBrowser.Controller.Entities;
 using MediaBrowser.Controller.Providers;
+using MediaBrowser.LocalMetadata.Parsers;
 using MediaBrowser.Model.Logging;
-using System.IO;
-using System.Threading;
 
-namespace MediaBrowser.Providers.Games
+namespace MediaBrowser.LocalMetadata.Providers
 {
     public class GameSystemXmlProvider : BaseXmlProvider<GameSystem>
     {

+ 5 - 4
MediaBrowser.Providers/Games/GameXmlProvider.cs → MediaBrowser.LocalMetadata/Providers/GameXmlProvider.cs

@@ -1,11 +1,12 @@
-using MediaBrowser.Common.IO;
+using System.IO;
+using System.Threading;
+using MediaBrowser.Common.IO;
 using MediaBrowser.Controller.Entities;
 using MediaBrowser.Controller.Providers;
+using MediaBrowser.LocalMetadata.Parsers;
 using MediaBrowser.Model.Logging;
-using System.IO;
-using System.Threading;
 
-namespace MediaBrowser.Providers.Games
+namespace MediaBrowser.LocalMetadata.Providers
 {
     public class GameXmlProvider : BaseXmlProvider<Game>
     {

+ 6 - 5
MediaBrowser.Providers/Movies/MovieXmlProvider.cs → MediaBrowser.LocalMetadata/Providers/MovieXmlProvider.cs

@@ -1,13 +1,14 @@
-using MediaBrowser.Common.IO;
+using System.Collections.Generic;
+using System.IO;
+using System.Threading;
+using MediaBrowser.Common.IO;
 using MediaBrowser.Controller.Entities.Movies;
 using MediaBrowser.Controller.Providers;
+using MediaBrowser.LocalMetadata.Parsers;
 using MediaBrowser.Model.Entities;
 using MediaBrowser.Model.Logging;
-using System.Collections.Generic;
-using System.IO;
-using System.Threading;
 
-namespace MediaBrowser.Providers.Movies
+namespace MediaBrowser.LocalMetadata.Providers
 {
     public class MovieXmlProvider : BaseXmlProvider<Movie>
     {

+ 5 - 6
MediaBrowser.Providers/Music/MusicVideoXmlProvider.cs → MediaBrowser.LocalMetadata/Providers/MusicVideoXmlProvider.cs

@@ -1,13 +1,12 @@
-using MediaBrowser.Common.IO;
+using System.IO;
+using System.Threading;
+using MediaBrowser.Common.IO;
 using MediaBrowser.Controller.Entities;
-using MediaBrowser.Controller.Entities.Audio;
 using MediaBrowser.Controller.Providers;
+using MediaBrowser.LocalMetadata.Parsers;
 using MediaBrowser.Model.Logging;
-using MediaBrowser.Providers.Movies;
-using System.IO;
-using System.Threading;
 
-namespace MediaBrowser.Providers.Music
+namespace MediaBrowser.LocalMetadata.Providers
 {
     class MusicVideoXmlProvider : BaseXmlProvider<MusicVideo>
     {

+ 4 - 4
MediaBrowser.Providers/People/PersonXmlProvider.cs → MediaBrowser.LocalMetadata/Providers/PersonXmlProvider.cs

@@ -1,11 +1,11 @@
-using MediaBrowser.Common.IO;
+using System.IO;
+using System.Threading;
+using MediaBrowser.Common.IO;
 using MediaBrowser.Controller.Entities;
 using MediaBrowser.Controller.Providers;
 using MediaBrowser.Model.Logging;
-using System.IO;
-using System.Threading;
 
-namespace MediaBrowser.Providers.People
+namespace MediaBrowser.LocalMetadata.Providers
 {
     public class PersonXmlProvider : BaseXmlProvider<Person>
     {

+ 15 - 5
MediaBrowser.Providers/TV/SeasonXmlProvider.cs → MediaBrowser.LocalMetadata/Providers/SeasonXmlProvider.cs

@@ -1,16 +1,17 @@
-using MediaBrowser.Common.IO;
+using System.IO;
+using System.Threading;
+using MediaBrowser.Common.IO;
 using MediaBrowser.Controller.Entities.TV;
 using MediaBrowser.Controller.Providers;
+using MediaBrowser.LocalMetadata.Parsers;
 using MediaBrowser.Model.Logging;
-using System.IO;
-using System.Threading;
 
-namespace MediaBrowser.Providers.TV
+namespace MediaBrowser.LocalMetadata.Providers
 {
     /// <summary>
     /// Class SeriesProviderFromXml
     /// </summary>
-    public class SeasonXmlProvider : BaseXmlProvider<Season>
+    public class SeasonXmlProvider : BaseXmlProvider<Season>, IHasOrder
     {
         private readonly ILogger _logger;
 
@@ -29,6 +30,15 @@ namespace MediaBrowser.Providers.TV
         {
             return directoryService.GetFile(Path.Combine(info.Path, "season.xml"));
         }
+
+        public int Order
+        {
+            get
+            {
+                // After Xbmc
+                return 1;
+            }
+        }
     }
 }
 

+ 15 - 5
MediaBrowser.Providers/TV/SeriesXmlProvider.cs → MediaBrowser.LocalMetadata/Providers/SeriesXmlProvider.cs

@@ -1,16 +1,17 @@
-using MediaBrowser.Common.IO;
+using System.IO;
+using System.Threading;
+using MediaBrowser.Common.IO;
 using MediaBrowser.Controller.Entities.TV;
 using MediaBrowser.Controller.Providers;
+using MediaBrowser.LocalMetadata.Parsers;
 using MediaBrowser.Model.Logging;
-using System.IO;
-using System.Threading;
 
-namespace MediaBrowser.Providers.TV
+namespace MediaBrowser.LocalMetadata.Providers
 {
     /// <summary>
     /// Class SeriesProviderFromXml
     /// </summary>
-    public class SeriesXmlProvider : BaseXmlProvider<Series>
+    public class SeriesXmlProvider : BaseXmlProvider<Series>, IHasOrder
     {
         private readonly ILogger _logger;
 
@@ -29,5 +30,14 @@ namespace MediaBrowser.Providers.TV
         {
             return directoryService.GetFile(Path.Combine(info.Path, "series.xml"));
         }
+
+        public int Order
+        {
+            get
+            {
+                // After Xbmc
+                return 1;
+            }
+        }
     }
 }

+ 6 - 5
MediaBrowser.Providers/Movies/TrailerXmlProvider.cs → MediaBrowser.LocalMetadata/Providers/TrailerXmlProvider.cs

@@ -1,13 +1,14 @@
-using MediaBrowser.Common.IO;
+using System.Collections.Generic;
+using System.IO;
+using System.Threading;
+using MediaBrowser.Common.IO;
 using MediaBrowser.Controller.Entities;
 using MediaBrowser.Controller.Providers;
+using MediaBrowser.LocalMetadata.Parsers;
 using MediaBrowser.Model.Entities;
 using MediaBrowser.Model.Logging;
-using System.Collections.Generic;
-using System.IO;
-using System.Threading;
 
-namespace MediaBrowser.Providers.Movies
+namespace MediaBrowser.LocalMetadata.Providers
 {
     public class TrailerXmlProvider : BaseXmlProvider<Trailer>
     {

+ 6 - 6
MediaBrowser.Providers/Videos/VideoXmlProvider.cs → MediaBrowser.LocalMetadata/Providers/VideoXmlProvider.cs

@@ -1,14 +1,14 @@
-using MediaBrowser.Common.IO;
+using System.Collections.Generic;
+using System.IO;
+using System.Threading;
+using MediaBrowser.Common.IO;
 using MediaBrowser.Controller.Entities;
 using MediaBrowser.Controller.Providers;
+using MediaBrowser.LocalMetadata.Parsers;
 using MediaBrowser.Model.Entities;
 using MediaBrowser.Model.Logging;
-using MediaBrowser.Providers.Movies;
-using System.Collections.Generic;
-using System.IO;
-using System.Threading;
 
-namespace MediaBrowser.Providers.Videos
+namespace MediaBrowser.LocalMetadata.Providers
 {
     class VideoXmlProvider : BaseXmlProvider<Video>
     {

+ 5 - 5
MediaBrowser.Providers/Savers/AlbumXmlSaver.cs → MediaBrowser.LocalMetadata/Savers/AlbumXmlSaver.cs

@@ -1,12 +1,12 @@
-using MediaBrowser.Controller.Entities;
-using MediaBrowser.Controller.Entities.Audio;
-using MediaBrowser.Controller.Library;
-using System.Collections.Generic;
+using System.Collections.Generic;
 using System.IO;
 using System.Text;
 using System.Threading;
+using MediaBrowser.Controller.Entities;
+using MediaBrowser.Controller.Entities.Audio;
+using MediaBrowser.Controller.Library;
 
-namespace MediaBrowser.Providers.Savers
+namespace MediaBrowser.LocalMetadata.Savers
 {
     class AlbumXmlSaver : IMetadataFileSaver
     {

+ 5 - 5
MediaBrowser.Providers/Savers/ArtistXmlSaver.cs → MediaBrowser.LocalMetadata/Savers/ArtistXmlSaver.cs

@@ -1,12 +1,12 @@
-using MediaBrowser.Controller.Entities;
-using MediaBrowser.Controller.Entities.Audio;
-using MediaBrowser.Controller.Library;
-using System.Collections.Generic;
+using System.Collections.Generic;
 using System.IO;
 using System.Text;
 using System.Threading;
+using MediaBrowser.Controller.Entities;
+using MediaBrowser.Controller.Entities.Audio;
+using MediaBrowser.Controller.Library;
 
-namespace MediaBrowser.Providers.Savers
+namespace MediaBrowser.LocalMetadata.Savers
 {
     class ArtistXmlSaver : IMetadataFileSaver
     {

+ 5 - 6
MediaBrowser.Providers/Savers/BoxSetXmlSaver.cs → MediaBrowser.LocalMetadata/Savers/BoxSetXmlSaver.cs

@@ -1,13 +1,12 @@
-using MediaBrowser.Controller.Entities;
-using MediaBrowser.Controller.Entities.Movies;
-using MediaBrowser.Controller.Library;
-using MediaBrowser.Model.Entities;
-using System.Collections.Generic;
+using System.Collections.Generic;
 using System.IO;
 using System.Text;
 using System.Threading;
+using MediaBrowser.Controller.Entities;
+using MediaBrowser.Controller.Entities.Movies;
+using MediaBrowser.Controller.Library;
 
-namespace MediaBrowser.Providers.Savers
+namespace MediaBrowser.LocalMetadata.Savers
 {
     public class BoxSetXmlSaver : IMetadataFileSaver
     {

+ 5 - 6
MediaBrowser.Providers/Savers/ChannelXmlSaver.cs → MediaBrowser.LocalMetadata/Savers/ChannelXmlSaver.cs

@@ -1,13 +1,12 @@
-using MediaBrowser.Controller.Entities;
-using MediaBrowser.Controller.Library;
-using MediaBrowser.Controller.LiveTv;
-using MediaBrowser.Model.Entities;
-using System.Collections.Generic;
+using System.Collections.Generic;
 using System.IO;
 using System.Text;
 using System.Threading;
+using MediaBrowser.Controller.Entities;
+using MediaBrowser.Controller.Library;
+using MediaBrowser.Controller.LiveTv;
 
-namespace MediaBrowser.Providers.Savers
+namespace MediaBrowser.LocalMetadata.Savers
 {
     /// <summary>
     /// Class PersonXmlSaver

+ 6 - 6
MediaBrowser.Providers/Savers/EpisodeXmlSaver.cs → MediaBrowser.LocalMetadata/Savers/EpisodeXmlSaver.cs

@@ -1,15 +1,15 @@
-using MediaBrowser.Controller.Entities;
-using MediaBrowser.Controller.Entities.TV;
-using MediaBrowser.Controller.Library;
-using MediaBrowser.Controller.Persistence;
-using System.Collections.Generic;
+using System.Collections.Generic;
 using System.Globalization;
 using System.IO;
 using System.Security;
 using System.Text;
 using System.Threading;
+using MediaBrowser.Controller.Entities;
+using MediaBrowser.Controller.Entities.TV;
+using MediaBrowser.Controller.Library;
+using MediaBrowser.Controller.Persistence;
 
-namespace MediaBrowser.Providers.Savers
+namespace MediaBrowser.LocalMetadata.Savers
 {
     public class EpisodeXmlSaver : IMetadataFileSaver
     {

+ 6 - 7
MediaBrowser.Providers/Savers/FolderXmlSaver.cs → MediaBrowser.LocalMetadata/Savers/FolderXmlSaver.cs

@@ -1,15 +1,14 @@
-using MediaBrowser.Controller.Entities;
+using System.Collections.Generic;
+using System.IO;
+using System.Text;
+using System.Threading;
+using MediaBrowser.Controller.Entities;
 using MediaBrowser.Controller.Entities.Audio;
 using MediaBrowser.Controller.Entities.Movies;
 using MediaBrowser.Controller.Entities.TV;
 using MediaBrowser.Controller.Library;
-using MediaBrowser.Model.Entities;
-using System.Collections.Generic;
-using System.IO;
-using System.Text;
-using System.Threading;
 
-namespace MediaBrowser.Providers.Savers
+namespace MediaBrowser.LocalMetadata.Savers
 {
     public class FolderXmlSaver : IMetadataFileSaver
     {

+ 4 - 4
MediaBrowser.Providers/Savers/GameSystemXmlSaver.cs → MediaBrowser.LocalMetadata/Savers/GameSystemXmlSaver.cs

@@ -1,12 +1,12 @@
-using MediaBrowser.Controller.Entities;
-using MediaBrowser.Controller.Library;
-using System.Collections.Generic;
+using System.Collections.Generic;
 using System.IO;
 using System.Security;
 using System.Text;
 using System.Threading;
+using MediaBrowser.Controller.Entities;
+using MediaBrowser.Controller.Library;
 
-namespace MediaBrowser.Providers.Savers
+namespace MediaBrowser.LocalMetadata.Savers
 {
     public class GameSystemXmlSaver : IMetadataFileSaver
     {

+ 5 - 5
MediaBrowser.Providers/Savers/GameXmlSaver.cs → MediaBrowser.LocalMetadata/Savers/GameXmlSaver.cs

@@ -1,14 +1,14 @@
-using MediaBrowser.Controller.Entities;
-using MediaBrowser.Controller.Library;
-using MediaBrowser.Model.Entities;
-using System.Collections.Generic;
+using System.Collections.Generic;
 using System.Globalization;
 using System.IO;
 using System.Security;
 using System.Text;
 using System.Threading;
+using MediaBrowser.Controller.Entities;
+using MediaBrowser.Controller.Library;
+using MediaBrowser.Model.Entities;
 
-namespace MediaBrowser.Providers.Savers
+namespace MediaBrowser.LocalMetadata.Savers
 {
     /// <summary>
     /// Saves game.xml for games

+ 7 - 8
MediaBrowser.Providers/Savers/MovieXmlSaver.cs → MediaBrowser.LocalMetadata/Savers/MovieXmlSaver.cs

@@ -1,16 +1,15 @@
-using MediaBrowser.Controller.Entities;
-using MediaBrowser.Controller.Entities.Movies;
-using MediaBrowser.Controller.Entities.TV;
-using MediaBrowser.Controller.Library;
-using MediaBrowser.Controller.Persistence;
-using System.Collections.Generic;
-using System.Globalization;
+using System.Collections.Generic;
 using System.IO;
 using System.Security;
 using System.Text;
 using System.Threading;
+using MediaBrowser.Controller.Entities;
+using MediaBrowser.Controller.Entities.Movies;
+using MediaBrowser.Controller.Entities.TV;
+using MediaBrowser.Controller.Library;
+using MediaBrowser.Controller.Persistence;
 
-namespace MediaBrowser.Providers.Savers
+namespace MediaBrowser.LocalMetadata.Savers
 {
     /// <summary>
     /// Saves movie.xml for movies, trailers and music videos

+ 4 - 6
MediaBrowser.Providers/Savers/PersonXmlSaver.cs → MediaBrowser.LocalMetadata/Savers/PersonXmlSaver.cs

@@ -1,14 +1,12 @@
-using MediaBrowser.Controller.Entities;
-using MediaBrowser.Controller.Library;
-using MediaBrowser.Controller.Providers;
-using System.Collections.Generic;
+using System.Collections.Generic;
 using System.IO;
 using System.Security;
 using System.Text;
 using System.Threading;
-using MediaBrowser.Model.Entities;
+using MediaBrowser.Controller.Entities;
+using MediaBrowser.Controller.Library;
 
-namespace MediaBrowser.Providers.Savers
+namespace MediaBrowser.LocalMetadata.Savers
 {
     /// <summary>
     /// Class PersonXmlSaver

+ 5 - 5
MediaBrowser.Providers/Savers/SeasonXmlSaver.cs → MediaBrowser.LocalMetadata/Savers/SeasonXmlSaver.cs

@@ -1,14 +1,14 @@
-using MediaBrowser.Controller.Entities;
-using MediaBrowser.Controller.Entities.TV;
-using MediaBrowser.Controller.Library;
-using System.Collections.Generic;
+using System.Collections.Generic;
 using System.Globalization;
 using System.IO;
 using System.Security;
 using System.Text;
 using System.Threading;
+using MediaBrowser.Controller.Entities;
+using MediaBrowser.Controller.Entities.TV;
+using MediaBrowser.Controller.Library;
 
-namespace MediaBrowser.Providers.Savers
+namespace MediaBrowser.LocalMetadata.Savers
 {
     public class SeasonXmlSaver : IMetadataFileSaver
     {

+ 7 - 7
MediaBrowser.Providers/Savers/SeriesXmlSaver.cs → MediaBrowser.LocalMetadata/Savers/SeriesXmlSaver.cs

@@ -1,15 +1,15 @@
-using System.Globalization;
-using MediaBrowser.Controller.Entities;
-using MediaBrowser.Controller.Entities.TV;
-using MediaBrowser.Controller.Library;
-using MediaBrowser.Model.Entities;
-using System.Collections.Generic;
+using System.Collections.Generic;
+using System.Globalization;
 using System.IO;
 using System.Security;
 using System.Text;
 using System.Threading;
+using MediaBrowser.Controller.Entities;
+using MediaBrowser.Controller.Entities.TV;
+using MediaBrowser.Controller.Library;
+using MediaBrowser.Model.Entities;
 
-namespace MediaBrowser.Providers.Savers
+namespace MediaBrowser.LocalMetadata.Savers
 {
     public class SeriesXmlSaver : IMetadataFileSaver
     {

+ 7 - 7
MediaBrowser.Providers/Savers/XmlSaverHelpers.cs → MediaBrowser.LocalMetadata/Savers/XmlSaverHelpers.cs

@@ -1,9 +1,4 @@
-using MediaBrowser.Controller.Entities;
-using MediaBrowser.Controller.Entities.Movies;
-using MediaBrowser.Controller.Entities.TV;
-using MediaBrowser.Controller.Persistence;
-using MediaBrowser.Model.Entities;
-using System;
+using System;
 using System.Collections.Generic;
 using System.Globalization;
 using System.IO;
@@ -11,8 +6,13 @@ using System.Linq;
 using System.Security;
 using System.Text;
 using System.Xml;
+using MediaBrowser.Controller.Entities;
+using MediaBrowser.Controller.Entities.Movies;
+using MediaBrowser.Controller.Entities.TV;
+using MediaBrowser.Controller.Persistence;
+using MediaBrowser.Model.Entities;
 
-namespace MediaBrowser.Providers.Savers
+namespace MediaBrowser.LocalMetadata.Savers
 {
     /// <summary>
     /// Class XmlHelpers

+ 93 - 6
MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs

@@ -152,7 +152,7 @@ namespace MediaBrowser.MediaEncoding.Encoder
                     RedirectStandardError = true,
                     FileName = FFProbePath,
                     Arguments = string.Format(args,
-                        probeSizeArgument, inputPath).Trim(),
+                    probeSizeArgument, inputPath).Trim(),
 
                     WindowStyle = ProcessWindowStyle.Hidden,
                     ErrorDialog = false
@@ -186,8 +186,7 @@ namespace MediaBrowser.MediaEncoding.Encoder
             {
                 process.BeginErrorReadLine();
 
-                result =
-                    _jsonSerializer.DeserializeFromStream<InternalMediaInfoResult>(process.StandardOutput.BaseStream);
+                result = _jsonSerializer.DeserializeFromStream<InternalMediaInfoResult>(process.StandardOutput.BaseStream);
             }
             catch
             {
@@ -292,7 +291,6 @@ namespace MediaBrowser.MediaEncoding.Encoder
             // apply some filters to thumbnail extracted below (below) crop any black lines that we made and get the correct ar then scale to width 600. 
             // This filter chain may have adverse effects on recorded tv thumbnails if ar changes during presentation ex. commercials @ diff ar
             var vf = "scale=600:trunc(600/dar/2)*2";
-            //crop=min(iw\,ih*dar):min(ih\,iw/dar):(iw-min(iw\,iw*sar))/2:(ih - min (ih\,ih/sar))/2,scale=600:(600/dar),thumbnail" -f image2
 
             if (threedFormat.HasValue)
             {
@@ -344,7 +342,8 @@ namespace MediaBrowser.MediaEncoding.Encoder
                     WindowStyle = ProcessWindowStyle.Hidden,
                     ErrorDialog = false,
                     RedirectStandardOutput = true,
-                    RedirectStandardError = true
+                    RedirectStandardError = true,
+                    RedirectStandardInput = true
                 }
             };
 
@@ -370,7 +369,7 @@ namespace MediaBrowser.MediaEncoding.Encoder
                 {
                     _logger.Info("Killing ffmpeg process");
 
-                    process.Kill();
+                    process.StandardInput.WriteLine("q");
 
                     process.WaitForExit(1000);
                 }
@@ -437,5 +436,93 @@ namespace MediaBrowser.MediaEncoding.Encoder
         {
             return time.ToString(@"hh\:mm\:ss\.fff", UsCulture);
         }
+
+        public async Task ExtractVideoImagesOnInterval(string[] inputFiles,
+            MediaProtocol protocol,
+            Video3DFormat? threedFormat,
+            TimeSpan interval,
+            string targetDirectory,
+            string filenamePrefix,
+            int? maxWidth,
+            CancellationToken cancellationToken)
+        {
+            var resourcePool = _videoImageResourcePool;
+
+            var inputArgument = GetInputArgument(inputFiles, protocol);
+
+            var vf = "fps=fps=1/" + interval.TotalSeconds.ToString(UsCulture);
+
+            if (maxWidth.HasValue)
+            {
+                var maxWidthParam = maxWidth.Value.ToString(UsCulture);
+
+                vf += string.Format(",scale=min(iw\\,{0}):trunc(ow/dar/2)*2", maxWidthParam);
+            }
+
+            Directory.CreateDirectory(targetDirectory);
+            var outputPath = Path.Combine(targetDirectory, filenamePrefix + "%05d.jpg");
+
+            var args = string.Format("-i {0} -threads 0 -v quiet -vf \"{2}\" -f image2 \"{1}\"", inputArgument, outputPath, vf);
+
+            var probeSize = GetProbeSizeArgument(new[] { inputArgument }, protocol);
+
+            if (!string.IsNullOrEmpty(probeSize))
+            {
+                args = probeSize + " " + args;
+            }
+
+            var process = new Process
+            {
+                StartInfo = new ProcessStartInfo
+                {
+                    CreateNoWindow = true,
+                    UseShellExecute = false,
+                    FileName = FFMpegPath,
+                    Arguments = args,
+                    WindowStyle = ProcessWindowStyle.Hidden,
+                    ErrorDialog = false,
+                    RedirectStandardInput = true
+                }
+            };
+
+            _logger.Info(process.StartInfo.FileName + " " + process.StartInfo.Arguments);
+            
+            await resourcePool.WaitAsync(cancellationToken).ConfigureAwait(false);
+
+            process.Start();
+
+            var ranToCompletion = process.WaitForExit(120000);
+
+            if (!ranToCompletion)
+            {
+                try
+                {
+                    _logger.Info("Killing ffmpeg process");
+
+                    process.StandardInput.WriteLine("q");
+
+                    process.WaitForExit(1000);
+                }
+                catch (Exception ex)
+                {
+                    _logger.ErrorException("Error killing process", ex);
+                }
+            }
+
+            resourcePool.Release();
+
+            var exitCode = ranToCompletion ? process.ExitCode : -1;
+
+            process.Dispose();
+
+            if (exitCode == -1)
+            {
+                var msg = string.Format("ffmpeg image extraction failed for {0}", inputArgument);
+
+                _logger.Error(msg);
+
+                throw new ApplicationException(msg);
+            }
+        }
     }
 }

+ 5 - 6
MediaBrowser.MediaEncoding/Subtitles/SubtitleEncoder.cs

@@ -342,12 +342,12 @@ namespace MediaBrowser.MediaEncoding.Subtitles
                 {
                     RedirectStandardOutput = false,
                     RedirectStandardError = true,
+                    RedirectStandardInput = true,
 
                     CreateNoWindow = true,
                     UseShellExecute = false,
                     FileName = _mediaEncoder.EncoderPath,
-                    Arguments =
-                        string.Format("{0} -i \"{1}\" -c:s ass \"{2}\"", encodingParam, inputPath, outputPath),
+                    Arguments = string.Format("{0} -i \"{1}\" -c:s ass \"{2}\"", encodingParam, inputPath, outputPath),
 
                     WindowStyle = ProcessWindowStyle.Hidden,
                     ErrorDialog = false
@@ -385,8 +385,7 @@ namespace MediaBrowser.MediaEncoding.Subtitles
                 {
                     _logger.Info("Killing ffmpeg subtitle conversion process");
 
-                    process.Kill();
-
+                    process.StandardInput.WriteLine("q");
                     process.WaitForExit(1000);
 
                     await logTask.ConfigureAwait(false);
@@ -520,6 +519,7 @@ namespace MediaBrowser.MediaEncoding.Subtitles
 
                     RedirectStandardOutput = false,
                     RedirectStandardError = true,
+                    RedirectStandardInput = true,
 
                     FileName = _mediaEncoder.EncoderPath,
                     Arguments = processArgs,
@@ -559,8 +559,7 @@ namespace MediaBrowser.MediaEncoding.Subtitles
                 {
                     _logger.Info("Killing ffmpeg subtitle extraction process");
 
-                    process.Kill();
-
+                    process.StandardInput.WriteLine("q");
                     process.WaitForExit(1000);
                 }
                 catch (Exception ex)

+ 15 - 6
MediaBrowser.Model.Portable/MediaBrowser.Model.Portable.csproj

@@ -104,6 +104,12 @@
     <Compile Include="..\MediaBrowser.Model\Configuration\BaseApplicationConfiguration.cs">
       <Link>Configuration\BaseApplicationConfiguration.cs</Link>
     </Compile>
+    <Compile Include="..\MediaBrowser.Model\Configuration\ChannelOptions.cs">
+      <Link>Configuration\ChannelOptions.cs</Link>
+    </Compile>
+    <Compile Include="..\MediaBrowser.Model\Configuration\ChapterOptions.cs">
+      <Link>Configuration\ChapterOptions.cs</Link>
+    </Compile>
     <Compile Include="..\MediaBrowser.Model\Configuration\DlnaOptions.cs">
       <Link>Configuration\DlnaOptions.cs</Link>
     </Compile>
@@ -152,6 +158,9 @@
     <Compile Include="..\MediaBrowser.Model\Configuration\SubtitleOptions.cs">
       <Link>Configuration\SubtitleOptions.cs</Link>
     </Compile>
+    <Compile Include="..\MediaBrowser.Model\Configuration\SubtitlePlaybackMode.cs">
+      <Link>Configuration\SubtitlePlaybackMode.cs</Link>
+    </Compile>
     <Compile Include="..\MediaBrowser.Model\Configuration\TvFileOrganizationOptions.cs">
       <Link>Configuration\TvFileOrganizationOptions.cs</Link>
     </Compile>
@@ -161,6 +170,9 @@
     <Compile Include="..\MediaBrowser.Model\Configuration\UserConfiguration.cs">
       <Link>Configuration\UserConfiguration.cs</Link>
     </Compile>
+    <Compile Include="..\MediaBrowser.Model\Configuration\XbmcMetadataOptions.cs">
+      <Link>Configuration\XbmcMetadataOptions.cs</Link>
+    </Compile>
     <Compile Include="..\MediaBrowser.Model\Dlna\AudioOptions.cs">
       <Link>Dlna\AudioOptions.cs</Link>
     </Compile>
@@ -203,12 +215,6 @@
     <Compile Include="..\MediaBrowser.Model\Dlna\DlnaProfileType.cs">
       <Link>Dlna\DlnaProfileType.cs</Link>
     </Compile>
-    <Compile Include="..\MediaBrowser.Model\Dlna\EventSubscription.cs">
-      <Link>Dlna\EventSubscription.cs</Link>
-    </Compile>
-    <Compile Include="..\MediaBrowser.Model\Dlna\Filter.cs">
-      <Link>Dlna\Filter.cs</Link>
-    </Compile>
     <Compile Include="..\MediaBrowser.Model\Dlna\HeaderMatchType.cs">
       <Link>Dlna\HeaderMatchType.cs</Link>
     </Compile>
@@ -434,6 +440,9 @@
     <Compile Include="..\MediaBrowser.Model\Extensions\DoubleHelper.cs">
       <Link>Extensions\DoubleHelper.cs</Link>
     </Compile>
+    <Compile Include="..\MediaBrowser.Model\Extensions\IHasPropertyChangedEvent.cs">
+      <Link>Extensions\IHasPropertyChangedEvent.cs</Link>
+    </Compile>
     <Compile Include="..\MediaBrowser.Model\Extensions\IntHelper.cs">
       <Link>Extensions\IntHelper.cs</Link>
     </Compile>

+ 15 - 6
MediaBrowser.Model.net35/MediaBrowser.Model.net35.csproj

@@ -91,6 +91,12 @@
     <Compile Include="..\MediaBrowser.Model\Configuration\BaseApplicationConfiguration.cs">
       <Link>Configuration\BaseApplicationConfiguration.cs</Link>
     </Compile>
+    <Compile Include="..\MediaBrowser.Model\Configuration\ChannelOptions.cs">
+      <Link>Configuration\ChannelOptions.cs</Link>
+    </Compile>
+    <Compile Include="..\MediaBrowser.Model\Configuration\ChapterOptions.cs">
+      <Link>Configuration\ChapterOptions.cs</Link>
+    </Compile>
     <Compile Include="..\MediaBrowser.Model\Configuration\DlnaOptions.cs">
       <Link>Configuration\DlnaOptions.cs</Link>
     </Compile>
@@ -139,6 +145,9 @@
     <Compile Include="..\MediaBrowser.Model\Configuration\SubtitleOptions.cs">
       <Link>Configuration\SubtitleOptions.cs</Link>
     </Compile>
+    <Compile Include="..\MediaBrowser.Model\Configuration\SubtitlePlaybackMode.cs">
+      <Link>Configuration\SubtitlePlaybackMode.cs</Link>
+    </Compile>
     <Compile Include="..\MediaBrowser.Model\Configuration\TvFileOrganizationOptions.cs">
       <Link>Configuration\TvFileOrganizationOptions.cs</Link>
     </Compile>
@@ -148,6 +157,9 @@
     <Compile Include="..\MediaBrowser.Model\Configuration\UserConfiguration.cs">
       <Link>Configuration\UserConfiguration.cs</Link>
     </Compile>
+    <Compile Include="..\MediaBrowser.Model\Configuration\XbmcMetadataOptions.cs">
+      <Link>Configuration\XbmcMetadataOptions.cs</Link>
+    </Compile>
     <Compile Include="..\MediaBrowser.Model\Dlna\AudioOptions.cs">
       <Link>Dlna\AudioOptions.cs</Link>
     </Compile>
@@ -190,12 +202,6 @@
     <Compile Include="..\MediaBrowser.Model\Dlna\DlnaProfileType.cs">
       <Link>Dlna\DlnaProfileType.cs</Link>
     </Compile>
-    <Compile Include="..\MediaBrowser.Model\Dlna\EventSubscription.cs">
-      <Link>Dlna\EventSubscription.cs</Link>
-    </Compile>
-    <Compile Include="..\MediaBrowser.Model\Dlna\Filter.cs">
-      <Link>Dlna\Filter.cs</Link>
-    </Compile>
     <Compile Include="..\MediaBrowser.Model\Dlna\HeaderMatchType.cs">
       <Link>Dlna\HeaderMatchType.cs</Link>
     </Compile>
@@ -421,6 +427,9 @@
     <Compile Include="..\MediaBrowser.Model\Extensions\DoubleHelper.cs">
       <Link>Extensions\DoubleHelper.cs</Link>
     </Compile>
+    <Compile Include="..\MediaBrowser.Model\Extensions\IHasPropertyChangedEvent.cs">
+      <Link>Extensions\IHasPropertyChangedEvent.cs</Link>
+    </Compile>
     <Compile Include="..\MediaBrowser.Model\Extensions\IntHelper.cs">
       <Link>Extensions\IntHelper.cs</Link>
     </Compile>

+ 18 - 0
MediaBrowser.Model/Configuration/ChannelOptions.cs

@@ -0,0 +1,18 @@
+namespace MediaBrowser.Model.Configuration
+{
+    public class ChannelOptions
+    {
+        public int? PreferredStreamingWidth { get; set; }
+
+        public string DownloadPath { get; set; }
+        public int? MaxDownloadAge { get; set; }
+
+        public string[] DownloadingChannels { get; set; }
+
+        public ChannelOptions()
+        {
+            DownloadingChannels = new string[] { };
+            MaxDownloadAge = 30;
+        }
+    }
+}

+ 27 - 0
MediaBrowser.Model/Configuration/ChapterOptions.cs

@@ -0,0 +1,27 @@
+namespace MediaBrowser.Model.Configuration
+{
+    public class ChapterOptions
+    {
+        public bool EnableMovieChapterImageExtraction { get; set; }
+        public bool EnableEpisodeChapterImageExtraction { get; set; }
+        public bool EnableOtherVideoChapterImageExtraction { get; set; }
+
+        public bool DownloadMovieChapters { get; set; }
+        public bool DownloadEpisodeChapters { get; set; }
+
+        public string[] FetcherOrder { get; set; }
+        public string[] DisabledFetchers { get; set; }
+
+        public ChapterOptions()
+        {
+            EnableMovieChapterImageExtraction = true;
+            EnableEpisodeChapterImageExtraction = false;
+            EnableOtherVideoChapterImageExtraction = false;
+
+            DownloadMovieChapters = true;
+
+            DisabledFetchers = new string[] { };
+            FetcherOrder = new string[] { };
+        }
+    }
+}

+ 10 - 67
MediaBrowser.Model/Configuration/ServerConfiguration.cs

@@ -1,6 +1,6 @@
-using MediaBrowser.Model.Weather;
+using System.Linq;
+using MediaBrowser.Model.Weather;
 using System;
-using System.Collections.Generic;
 
 namespace MediaBrowser.Model.Configuration
 {
@@ -69,24 +69,12 @@ namespace MediaBrowser.Model.Configuration
         /// <value>The display name of the season zero.</value>
         public string SeasonZeroDisplayName { get; set; }
 
-        /// <summary>
-        /// Gets or sets the metadata refresh days.
-        /// </summary>
-        /// <value>The metadata refresh days.</value>
-        public int MetadataRefreshDays { get; set; }
-
         /// <summary>
         /// Gets or sets a value indicating whether [save local meta].
         /// </summary>
         /// <value><c>true</c> if [save local meta]; otherwise, <c>false</c>.</value>
         public bool SaveLocalMeta { get; set; }
 
-        /// <summary>
-        /// Gets or sets a value indicating whether [refresh item images].
-        /// </summary>
-        /// <value><c>true</c> if [refresh item images]; otherwise, <c>false</c>.</value>
-        public bool RefreshItemImages { get; set; }
-
         /// <summary>
         /// Gets or sets the preferred metadata language.
         /// </summary>
@@ -223,8 +211,11 @@ namespace MediaBrowser.Model.Configuration
         public string[] ManualLoginClients { get; set; }
 
         public ChannelOptions ChannelOptions { get; set; }
+
         public ChapterOptions ChapterOptions { get; set; }
 
+        public bool DefaultMetadataSettingsApplied { get; set; }
+
         /// <summary>
         /// Initializes a new instance of the <see cref="ServerConfiguration" /> class.
         /// </summary>
@@ -256,7 +247,6 @@ namespace MediaBrowser.Model.Configuration
 
             PathSubstitutions = new PathSubstitution[] { };
 
-            MetadataRefreshDays = 30;
             PreferredMetadataLanguage = "en";
             MetadataCountryCode = "US";
 
@@ -268,13 +258,11 @@ namespace MediaBrowser.Model.Configuration
 
             SeasonZeroDisplayName = "Specials";
 
-            LiveTvOptions = new LiveTvOptions();
-
-            TvFileOrganizationOptions = new TvFileOrganizationOptions();
-
             EnableRealtimeMonitor = true;
 
-            List<MetadataOptions> options = new List<MetadataOptions>
+            UICulture = "en-us";
+
+            MetadataOptions = new[]
             {
                 new MetadataOptions(1, 1280) {ItemType = "Book"},
                 new MetadataOptions(1, 1280) {ItemType = "MusicAlbum"},
@@ -282,59 +270,14 @@ namespace MediaBrowser.Model.Configuration
                 new MetadataOptions(0, 1280) {ItemType = "Season"}
             };
 
-            MetadataOptions = options.ToArray();
-
-            DlnaOptions = new DlnaOptions();
-
-            UICulture = "en-us";
-
             NotificationOptions = new NotificationOptions();
 
             SubtitleOptions = new SubtitleOptions();
 
             ChannelOptions = new ChannelOptions();
-            ChapterOptions = new ChapterOptions();
-        }
-    }
-
-    public class ChannelOptions
-    {
-        public int? PreferredStreamingWidth { get; set; }
-
-        public string DownloadPath { get; set; }
-        public int? MaxDownloadAge { get; set; }
-
-        public string[] DownloadingChannels { get; set; }
-
-        public ChannelOptions()
-        {
-            DownloadingChannels = new string[] { };
-            MaxDownloadAge = 30;
-        }
-    }
 
-    public class ChapterOptions
-    {
-        public bool EnableMovieChapterImageExtraction { get; set; }
-        public bool EnableEpisodeChapterImageExtraction { get; set; }
-        public bool EnableOtherVideoChapterImageExtraction { get; set; }
-
-        public bool DownloadMovieChapters { get; set; }
-        public bool DownloadEpisodeChapters { get; set; }
-
-        public string[] FetcherOrder { get; set; }
-        public string[] DisabledFetchers { get; set; }
-
-        public ChapterOptions()
-        {
-            EnableMovieChapterImageExtraction = true;
-            EnableEpisodeChapterImageExtraction = false;
-            EnableOtherVideoChapterImageExtraction = false;
-
-            DownloadMovieChapters = true;
-
-            DisabledFetchers = new string[] { };
-            FetcherOrder = new string[] { };
+            LiveTvOptions = new LiveTvOptions();
+            TvFileOrganizationOptions = new TvFileOrganizationOptions();
         }
     }
 }

+ 10 - 0
MediaBrowser.Model/Configuration/SubtitlePlaybackMode.cs

@@ -0,0 +1,10 @@
+namespace MediaBrowser.Model.Configuration
+{
+    public enum SubtitlePlaybackMode
+    {
+        Default = 0,
+        Always = 1,
+        OnlyForced = 2,
+        None = 3
+    }
+}

+ 0 - 8
MediaBrowser.Model/Configuration/UserConfiguration.cs

@@ -91,12 +91,4 @@ namespace MediaBrowser.Model.Configuration
             ExcludeFoldersFromGrouping = new string[] { };
         }
     }
-
-    public enum SubtitlePlaybackMode
-    {
-        Default = 0,
-        Always = 1,
-        OnlyForced = 2,
-        None = 3
-    }
 }

+ 21 - 0
MediaBrowser.Model/Configuration/XbmcMetadataOptions.cs

@@ -0,0 +1,21 @@
+
+namespace MediaBrowser.Model.Configuration
+{
+    public class XbmcMetadataOptions
+    {
+        public string UserId { get; set; }
+
+        public string ReleaseDateFormat { get; set; }
+
+        public bool SaveImagePathsInNfo { get; set; }
+        public bool EnablePathSubstitution { get; set; }
+
+        public XbmcMetadataOptions()
+        {
+            ReleaseDateFormat = "yyyy-MM-dd";
+
+            SaveImagePathsInNfo = true;
+            EnablePathSubstitution = true;
+        }
+    }
+}

+ 29 - 0
MediaBrowser.Model/Dlna/ConditionProcessor.cs

@@ -175,6 +175,35 @@ namespace MediaBrowser.Model.Dlna
 
             return false;
         }
+
+        private bool IsConditionSatisfied(ProfileCondition condition, float? currentValue)
+        {
+            if (!currentValue.HasValue)
+            {
+                // If the value is unknown, it satisfies if not marked as required
+                return !condition.IsRequired;
+            }
+
+            float expected;
+            if (FloatHelper.TryParseCultureInvariant(condition.Value, out expected))
+            {
+                switch (condition.Condition)
+                {
+                    case ProfileConditionType.Equals:
+                        return currentValue.Value.Equals(expected);
+                    case ProfileConditionType.GreaterThanEqual:
+                        return currentValue.Value >= expected;
+                    case ProfileConditionType.LessThanEqual:
+                        return currentValue.Value <= expected;
+                    case ProfileConditionType.NotEquals:
+                        return !currentValue.Value.Equals(expected);
+                    default:
+                        throw new InvalidOperationException("Unexpected ProfileConditionType");
+                }
+            }
+
+            return false;
+        }
         
         private bool IsConditionSatisfied(ProfileCondition condition, double? currentValue)
         {

+ 4 - 4
MediaBrowser.Model/Dto/BaseItemDto.cs

@@ -15,7 +15,7 @@ namespace MediaBrowser.Model.Dto
     /// This holds information about a BaseItem in a format that is convenient for the client.
     /// </summary>
     [DebuggerDisplay("Name = {Name}, ID = {Id}, Type = {Type}")]
-    public class BaseItemDto : IHasProviderIds, INotifyPropertyChanged, IItemDto
+    public class BaseItemDto : IHasProviderIds, IHasPropertyChangedEvent, IItemDto
     {
         /// <summary>
         /// Gets or sets the name.
@@ -844,7 +844,7 @@ namespace MediaBrowser.Model.Dto
         [IgnoreDataMember]
         public bool IsVideo
         {
-            get { return StringHelper.EqualsIgnoreCase(MediaType, Entities.MediaType.Video); }
+            get { return StringHelper.EqualsIgnoreCase(MediaType, MediaBrowser.Model.Entities.MediaType.Video); }
         }
 
         /// <summary>
@@ -854,7 +854,7 @@ namespace MediaBrowser.Model.Dto
         [IgnoreDataMember]
         public bool IsAudio
         {
-            get { return StringHelper.EqualsIgnoreCase(MediaType, Entities.MediaType.Audio); }
+            get { return StringHelper.EqualsIgnoreCase(MediaType, MediaBrowser.Model.Entities.MediaType.Audio); }
         }
 
         /// <summary>
@@ -864,7 +864,7 @@ namespace MediaBrowser.Model.Dto
         [IgnoreDataMember]
         public bool IsGame
         {
-            get { return StringHelper.EqualsIgnoreCase(MediaType, Entities.MediaType.Game); }
+            get { return StringHelper.EqualsIgnoreCase(MediaType, MediaBrowser.Model.Entities.MediaType.Game); }
         }
 
         /// <summary>

+ 2 - 1
MediaBrowser.Model/Dto/BaseItemPerson.cs

@@ -1,6 +1,7 @@
 using System.ComponentModel;
 using System.Diagnostics;
 using System.Runtime.Serialization;
+using MediaBrowser.Model.Extensions;
 
 namespace MediaBrowser.Model.Dto
 {
@@ -8,7 +9,7 @@ namespace MediaBrowser.Model.Dto
     /// This is used by the api to get information about a Person within a BaseItem
     /// </summary>
     [DebuggerDisplay("Name = {Name}, Role = {Role}, Type = {Type}")]
-    public class BaseItemPerson : INotifyPropertyChanged
+    public class BaseItemPerson : IHasPropertyChangedEvent
     {
         /// <summary>
         /// Gets or sets the name.

+ 2 - 1
MediaBrowser.Model/Dto/ChapterInfoDto.cs

@@ -1,6 +1,7 @@
 using System.ComponentModel;
 using System.Diagnostics;
 using System.Runtime.Serialization;
+using MediaBrowser.Model.Extensions;
 
 namespace MediaBrowser.Model.Dto
 {
@@ -8,7 +9,7 @@ namespace MediaBrowser.Model.Dto
     /// Class ChapterInfo
     /// </summary>
     [DebuggerDisplay("Name = {Name}")]
-    public class ChapterInfoDto : INotifyPropertyChanged
+    public class ChapterInfoDto : IHasPropertyChangedEvent
     {
         /// <summary>
         /// Gets or sets the start position ticks.

+ 2 - 1
MediaBrowser.Model/Dto/UserDto.cs

@@ -3,6 +3,7 @@ using System;
 using System.ComponentModel;
 using System.Diagnostics;
 using System.Runtime.Serialization;
+using MediaBrowser.Model.Extensions;
 
 namespace MediaBrowser.Model.Dto
 {
@@ -10,7 +11,7 @@ namespace MediaBrowser.Model.Dto
     /// Class UserDto
     /// </summary>
     [DebuggerDisplay("Name = {Name}, ID = {Id}, HasPassword = {HasPassword}")]
-    public class UserDto : INotifyPropertyChanged, IItemDto
+    public class UserDto : IHasPropertyChangedEvent, IItemDto
     {
         /// <summary>
         /// Gets or sets the name.

+ 2 - 1
MediaBrowser.Model/Dto/UserItemDataDto.cs

@@ -1,12 +1,13 @@
 using System;
 using System.ComponentModel;
+using MediaBrowser.Model.Extensions;
 
 namespace MediaBrowser.Model.Dto
 {
     /// <summary>
     /// Class UserItemDataDto
     /// </summary>
-    public class UserItemDataDto : INotifyPropertyChanged
+    public class UserItemDataDto : IHasPropertyChangedEvent
     {
         /// <summary>
         /// Gets or sets the rating.

+ 2 - 1
MediaBrowser.Model/Entities/DisplayPreferences.cs

@@ -2,13 +2,14 @@
 using System;
 using System.Collections.Generic;
 using System.ComponentModel;
+using MediaBrowser.Model.Extensions;
 
 namespace MediaBrowser.Model.Entities
 {
     /// <summary>
     /// Defines the display preferences for any item that supports them (usually Folders)
     /// </summary>
-    public class DisplayPreferences : INotifyPropertyChanged
+    public class DisplayPreferences : IHasPropertyChangedEvent
     {
         /// <summary>
         /// Occurs when [property changed].

+ 1 - 1
MediaBrowser.Model/Entities/MediaStream.cs

@@ -136,7 +136,7 @@ namespace MediaBrowser.Model.Entities
             {
                 if (Type != MediaStreamType.Subtitle) return false;
 
-                var codec = Codec ?? string.Empty;
+                string codec = Codec ?? string.Empty;
 
                 return StringHelper.IndexOfIgnoreCase(codec, "pgs") == -1 &&
                        StringHelper.IndexOfIgnoreCase(codec, "dvd") == -1;

+ 8 - 0
MediaBrowser.Model/Extensions/IHasPropertyChangedEvent.cs

@@ -0,0 +1,8 @@
+using System.ComponentModel;
+
+namespace MediaBrowser.Model.Extensions
+{
+    public interface IHasPropertyChangedEvent : INotifyPropertyChanged
+    {
+    }
+}

+ 10 - 1
MediaBrowser.Model/Extensions/ListHelper.cs

@@ -6,7 +6,16 @@ namespace MediaBrowser.Model.Extensions
 {
     public static class ListHelper
     {
-        public static bool ContainsIgnoreCase(IEnumerable<string> list, string value)
+        public static bool ContainsIgnoreCase(List<string> list, string value)
+        {
+            if (value == null)
+            {
+                throw new ArgumentNullException("value");
+            }
+
+            return list.Contains(value, StringComparer.OrdinalIgnoreCase);
+        }
+        public static bool ContainsIgnoreCase(string[] list, string value)
         {
             if (value == null)
             {

+ 2 - 1
MediaBrowser.Model/LiveTv/BaseTimerInfoDto.cs

@@ -1,9 +1,10 @@
 using System;
 using System.ComponentModel;
+using MediaBrowser.Model.Extensions;
 
 namespace MediaBrowser.Model.LiveTv
 {
-    public class BaseTimerInfoDto : INotifyPropertyChanged
+    public class BaseTimerInfoDto : IHasPropertyChangedEvent
     {
         /// <summary>
         /// Occurs when a property value changes.

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

@@ -1,5 +1,6 @@
 using MediaBrowser.Model.Dto;
 using MediaBrowser.Model.Entities;
+using MediaBrowser.Model.Extensions;
 using MediaBrowser.Model.Library;
 using System.Collections.Generic;
 using System.ComponentModel;
@@ -12,7 +13,7 @@ namespace MediaBrowser.Model.LiveTv
     /// Class ChannelInfoDto
     /// </summary>
     [DebuggerDisplay("Name = {Name}, Number = {Number}")]
-    public class ChannelInfoDto : INotifyPropertyChanged, IItemDto
+    public class ChannelInfoDto : IHasPropertyChangedEvent, IItemDto
     {
         /// <summary>
         /// Gets or sets the name.

+ 2 - 1
MediaBrowser.Model/LiveTv/ProgramInfoDto.cs

@@ -5,12 +5,13 @@ using MediaBrowser.Model.Dto;
 using MediaBrowser.Model.Entities;
 using System;
 using System.Collections.Generic;
+using MediaBrowser.Model.Extensions;
 using MediaBrowser.Model.Library;
 
 namespace MediaBrowser.Model.LiveTv
 {
     [DebuggerDisplay("Name = {Name}, StartTime = {StartDate}, EndTime = {EndDate}")]
-    public class ProgramInfoDto : INotifyPropertyChanged, IItemDto
+    public class ProgramInfoDto : IHasPropertyChangedEvent, IItemDto
     {
         /// <summary>
         /// Id of the program.

+ 2 - 1
MediaBrowser.Model/LiveTv/RecordingGroupDto.cs

@@ -1,5 +1,6 @@
 using System.ComponentModel;
 using System.Diagnostics;
+using MediaBrowser.Model.Extensions;
 
 namespace MediaBrowser.Model.LiveTv
 {
@@ -7,7 +8,7 @@ namespace MediaBrowser.Model.LiveTv
     /// Class RecordingGroupDto.
     /// </summary>
     [DebuggerDisplay("Name = {Name}, Count = {RecordingCount}")]
-    public class RecordingGroupDto : INotifyPropertyChanged
+    public class RecordingGroupDto : IHasPropertyChangedEvent
     {
         /// <summary>
         /// Gets or sets the name.

Một số tệp đã không được hiển thị bởi vì quá nhiều tập tin thay đổi trong này khác