Browse Source

update file system methods

Luke 9 years ago
parent
commit
14de062681
100 changed files with 397 additions and 552 deletions
  1. 1 1
      Emby.Drawing/Common/ImageHeader.cs
  2. 2 2
      Emby.Drawing/GDI/GDIImageEncoder.cs
  3. 7 4
      Emby.Drawing/ImageMagick/ImageMagickEncoder.cs
  4. 15 12
      Emby.Drawing/ImageMagick/PlayedIndicatorDrawer.cs
  5. 5 2
      Emby.Drawing/ImageMagick/StripCollageBuilder.cs
  6. 1 1
      Emby.Drawing/ImageMagick/UnplayedCountIndicator.cs
  7. 8 8
      Emby.Drawing/ImageProcessor.cs
  8. 0 100
      MediaBrowser.Api/AppThemeService.cs
  9. 5 3
      MediaBrowser.Api/EnvironmentService.cs
  10. 10 11
      MediaBrowser.Api/Images/ImageByNameService.cs
  11. 3 3
      MediaBrowser.Api/Images/RemoteImageService.cs
  12. 4 4
      MediaBrowser.Api/ItemLookupService.cs
  13. 1 1
      MediaBrowser.Api/ItemRefreshService.cs
  14. 3 3
      MediaBrowser.Api/Library/LibraryHelpers.cs
  15. 1 1
      MediaBrowser.Api/Library/LibraryService.cs
  16. 8 8
      MediaBrowser.Api/Library/LibraryStructureService.cs
  17. 5 10
      MediaBrowser.Api/MediaBrowser.Api.csproj
  18. 3 3
      MediaBrowser.Api/Playback/BaseStreamingService.cs
  19. 5 6
      MediaBrowser.Api/Playback/Dash/MpegDashService.cs
  20. 2 2
      MediaBrowser.Api/Playback/Hls/BaseHlsService.cs
  21. 5 6
      MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs
  22. 2 2
      MediaBrowser.Api/Playback/Progressive/BaseProgressiveStreamingService.cs
  23. 1 1
      MediaBrowser.Api/Subtitles/SubtitleService.cs
  24. 2 4
      MediaBrowser.Api/System/SystemService.cs
  25. 13 5
      MediaBrowser.Common.Implementations/Archiving/ZipClient.cs
  26. 5 4
      MediaBrowser.Common.Implementations/BaseApplicationHost.cs
  27. 2 2
      MediaBrowser.Common.Implementations/BaseApplicationPaths.cs
  28. 2 2
      MediaBrowser.Common.Implementations/Configuration/BaseConfigurationManager.cs
  29. 5 4
      MediaBrowser.Common.Implementations/Configuration/ConfigurationHelper.cs
  30. 12 5
      MediaBrowser.Common.Implementations/Devices/DeviceId.cs
  31. 2 2
      MediaBrowser.Common.Implementations/HttpClientManager/HttpClientManager.cs
  32. 40 20
      MediaBrowser.Common.Implementations/IO/CommonFileSystem.cs
  33. 1 1
      MediaBrowser.Common.Implementations/Logging/NlogManager.cs
  34. 2 2
      MediaBrowser.Common.Implementations/ScheduledTasks/ScheduledTaskWorker.cs
  35. 3 3
      MediaBrowser.Common.Implementations/ScheduledTasks/Tasks/DeleteCacheFileTask.cs
  36. 1 1
      MediaBrowser.Common.Implementations/ScheduledTasks/Tasks/DeleteLogFileTask.cs
  37. 5 5
      MediaBrowser.Common.Implementations/Security/MBLicenseFile.cs
  38. 1 1
      MediaBrowser.Common.Implementations/Serialization/JsonSerializer.cs
  39. 9 1
      MediaBrowser.Common.Implementations/Serialization/XmlSerializer.cs
  40. 4 4
      MediaBrowser.Common.Implementations/Updates/InstallationManager.cs
  41. 41 16
      MediaBrowser.Common/IO/IFileSystem.cs
  42. 5 5
      MediaBrowser.Controller/Entities/BaseItem.cs
  43. 3 3
      MediaBrowser.Controller/Entities/Folder.cs
  44. 5 5
      MediaBrowser.Controller/Entities/User.cs
  45. 5 11
      MediaBrowser.Controller/MediaBrowser.Controller.csproj
  46. 7 5
      MediaBrowser.Controller/Providers/DirectoryService.cs
  47. 3 2
      MediaBrowser.Controller/Providers/MetadataRefreshOptions.cs
  48. 0 38
      MediaBrowser.Controller/Themes/IAppThemeManager.cs
  49. 0 31
      MediaBrowser.Controller/Themes/InternalThemeImage.cs
  50. 4 6
      MediaBrowser.Dlna/DlnaManager.cs
  51. 4 3
      MediaBrowser.LocalMetadata/Savers/XmlSaverHelpers.cs
  52. 1 1
      MediaBrowser.MediaEncoding/Configuration/EncodingConfigurationFactory.cs
  53. 3 3
      MediaBrowser.MediaEncoding/Encoder/BaseEncoder.cs
  54. 1 1
      MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs
  55. 10 10
      MediaBrowser.MediaEncoding/Subtitles/SubtitleEncoder.cs
  56. 10 5
      MediaBrowser.Model/Dlna/StreamBuilder.cs
  57. 3 12
      MediaBrowser.Model/MediaBrowser.Model.csproj
  58. 0 22
      MediaBrowser.Model/Themes/AppTheme.cs
  59. 0 9
      MediaBrowser.Model/Themes/AppThemeInfo.cs
  60. 0 18
      MediaBrowser.Model/Themes/ThemeImage.cs
  61. 1 1
      MediaBrowser.Providers/BoxSets/MovieDbBoxSetProvider.cs
  62. 2 2
      MediaBrowser.Providers/ImagesByName/ImageUtils.cs
  63. 1 1
      MediaBrowser.Providers/Manager/ImageSaver.cs
  64. 2 2
      MediaBrowser.Providers/Manager/ItemImageProvider.cs
  65. 1 1
      MediaBrowser.Providers/Manager/ProviderManager.cs
  66. 3 3
      MediaBrowser.Providers/MediaInfo/AudioImageProvider.cs
  67. 1 1
      MediaBrowser.Providers/MediaInfo/FFProbeProvider.cs
  68. 1 1
      MediaBrowser.Providers/MediaInfo/FFProbeVideoInfo.cs
  69. 3 3
      MediaBrowser.Providers/Movies/FanArtMovieUpdatesPostScanTask.cs
  70. 1 1
      MediaBrowser.Providers/Movies/FanartMovieImageProvider.cs
  71. 5 2
      MediaBrowser.Providers/Movies/GenericMovieDbInfo.cs
  72. 2 2
      MediaBrowser.Providers/Movies/MovieDbProvider.cs
  73. 3 3
      MediaBrowser.Providers/Movies/MovieUpdatesPrescanTask.cs
  74. 1 1
      MediaBrowser.Providers/Music/AudioDbAlbumProvider.cs
  75. 1 1
      MediaBrowser.Providers/Music/AudioDbArtistProvider.cs
  76. 1 1
      MediaBrowser.Providers/Music/FanArtArtistProvider.cs
  77. 3 3
      MediaBrowser.Providers/Music/FanArtUpdatesPostScanTask.cs
  78. 1 1
      MediaBrowser.Providers/People/MovieDbPersonProvider.cs
  79. 1 1
      MediaBrowser.Providers/Subtitles/SubtitleManager.cs
  80. 2 2
      MediaBrowser.Providers/TV/DummySeasonProvider.cs
  81. 3 3
      MediaBrowser.Providers/TV/FanArtTvUpdatesPostScanTask.cs
  82. 1 1
      MediaBrowser.Providers/TV/FanartSeriesProvider.cs
  83. 2 2
      MediaBrowser.Providers/TV/MissingEpisodeProvider.cs
  84. 1 1
      MediaBrowser.Providers/TV/MovieDbEpisodeImageProvider.cs
  85. 1 1
      MediaBrowser.Providers/TV/MovieDbSeasonProvider.cs
  86. 2 2
      MediaBrowser.Providers/TV/MovieDbSeriesProvider.cs
  87. 4 4
      MediaBrowser.Providers/TV/TvdbPrescanTask.cs
  88. 6 7
      MediaBrowser.Providers/TV/TvdbSeriesProvider.cs
  89. 5 5
      MediaBrowser.Server.Implementations/Channels/ChannelManager.cs
  90. 1 1
      MediaBrowser.Server.Implementations/Collections/CollectionManager.cs
  91. 1 1
      MediaBrowser.Server.Implementations/Collections/CollectionsDynamicFolder.cs
  92. 2 2
      MediaBrowser.Server.Implementations/Configuration/ServerConfigurationManager.cs
  93. 3 3
      MediaBrowser.Server.Implementations/Connect/ConnectEntryPoint.cs
  94. 3 3
      MediaBrowser.Server.Implementations/Connect/ConnectManager.cs
  95. 1 1
      MediaBrowser.Server.Implementations/Devices/CameraUploadsFolder.cs
  96. 1 1
      MediaBrowser.Server.Implementations/Devices/DeviceManager.cs
  97. 2 2
      MediaBrowser.Server.Implementations/Devices/DeviceRepository.cs
  98. 2 2
      MediaBrowser.Server.Implementations/EntryPoints/Notifications/RemoteNotifications.cs
  99. 6 6
      MediaBrowser.Server.Implementations/FileOrganization/EpisodeFileOrganizer.cs
  100. 3 5
      MediaBrowser.Server.Implementations/FileOrganization/TvFolderOrganizer.cs

+ 1 - 1
Emby.Drawing/Common/ImageHeader.cs

@@ -46,7 +46,7 @@ namespace Emby.Drawing.Common
         /// <exception cref="ArgumentException">The image was of an unrecognised format.</exception>
         public static ImageSize GetDimensions(string path, ILogger logger, IFileSystem fileSystem)
         {
-            using (var fs = File.OpenRead(path))
+			using (var fs = fileSystem.OpenRead(path))
             {
                 using (var binaryReader = new BinaryReader(fs))
                 {

+ 2 - 2
Emby.Drawing/GDI/GDIImageEncoder.cs

@@ -66,7 +66,7 @@ namespace Emby.Drawing.GDI
             {
                 using (var croppedImage = image.CropWhitespace())
                 {
-                    Directory.CreateDirectory(Path.GetDirectoryName(outputPath));
+					_fileSystem.CreateDirectory(Path.GetDirectoryName(outputPath));
 
                     using (var outputStream = _fileSystem.GetFileStream(outputPath, FileMode.Create, FileAccess.Write, FileShare.Read, false))
                     {
@@ -120,7 +120,7 @@ namespace Emby.Drawing.GDI
 
                         var outputFormat = GetOutputFormat(originalImage, selectedOutputFormat);
 
-                        Directory.CreateDirectory(Path.GetDirectoryName(cacheFilePath));
+						_fileSystem.CreateDirectory(Path.GetDirectoryName(cacheFilePath));
 
                         // Save to the cache location
                         using (var cacheFileStream = _fileSystem.GetFileStream(cacheFilePath, FileMode.Create, FileAccess.Write, FileShare.Read, false))

+ 7 - 4
Emby.Drawing/ImageMagick/ImageMagickEncoder.cs

@@ -8,6 +8,7 @@ using MediaBrowser.Model.Logging;
 using System;
 using System.IO;
 using System.Linq;
+using MediaBrowser.Common.IO;
 
 namespace Emby.Drawing.ImageMagick
 {
@@ -15,13 +16,15 @@ namespace Emby.Drawing.ImageMagick
     {
         private readonly ILogger _logger;
         private readonly IApplicationPaths _appPaths;
-        private readonly IHttpClient _httpClient;
+		private readonly IHttpClient _httpClient;
+		private readonly IFileSystem _fileSystem;
 
-        public ImageMagickEncoder(ILogger logger, IApplicationPaths appPaths, IHttpClient httpClient)
+        public ImageMagickEncoder(ILogger logger, IApplicationPaths appPaths, IHttpClient httpClient, IFileSystem fileSystem)
         {
             _logger = logger;
             _appPaths = appPaths;
             _httpClient = httpClient;
+			_fileSystem = fileSystem;
 
             LogImageMagickVersion();
         }
@@ -77,7 +80,7 @@ namespace Emby.Drawing.ImageMagick
             try
             {
                 var tmpPath = Path.Combine(_appPaths.TempDirectory, Guid.NewGuid() + ".webp");
-                Directory.CreateDirectory(Path.GetDirectoryName(tmpPath));
+				_fileSystem.CreateDirectory(Path.GetDirectoryName(tmpPath));
 
                 using (var wand = new MagickWand(1, 1, new PixelWand("none", 1)))
                 {
@@ -181,7 +184,7 @@ namespace Emby.Drawing.ImageMagick
                 {
                     var currentImageSize = new ImageSize(imageWidth, imageHeight);
 
-                    var task = new PlayedIndicatorDrawer(_appPaths, _httpClient).DrawPlayedIndicator(wand, currentImageSize);
+					var task = new PlayedIndicatorDrawer(_appPaths, _httpClient, _fileSystem).DrawPlayedIndicator(wand, currentImageSize);
                     Task.WaitAll(task);
                 }
                 else if (options.UnplayedCount.HasValue)

+ 15 - 12
Emby.Drawing/ImageMagick/PlayedIndicatorDrawer.cs

@@ -5,6 +5,7 @@ using MediaBrowser.Model.Drawing;
 using System;
 using System.IO;
 using System.Threading.Tasks;
+using MediaBrowser.Common.IO;
 
 namespace Emby.Drawing.ImageMagick
 {
@@ -14,12 +15,14 @@ namespace Emby.Drawing.ImageMagick
         private const int OffsetFromTopRightCorner = 38;
 
         private readonly IApplicationPaths _appPaths;
-        private readonly IHttpClient _iHttpClient;
+		private readonly IHttpClient _iHttpClient;
+		private readonly IFileSystem _fileSystem;
 
-        public PlayedIndicatorDrawer(IApplicationPaths appPaths, IHttpClient iHttpClient)
+		public PlayedIndicatorDrawer(IApplicationPaths appPaths, IHttpClient iHttpClient, IFileSystem fileSystem)
         {
             _appPaths = appPaths;
             _iHttpClient = iHttpClient;
+			_fileSystem = fileSystem;
         }
 
         public async Task DrawPlayedIndicator(MagickWand wand, ImageSize imageSize)
@@ -38,7 +41,7 @@ namespace Emby.Drawing.ImageMagick
                     pixel.Opacity = 0;
                     pixel.Color = "white";
                     draw.FillColor = pixel;
-                    draw.Font = await DownloadFont("webdings.ttf", "https://github.com/MediaBrowser/Emby.Resources/raw/master/fonts/webdings.ttf", _appPaths, _iHttpClient).ConfigureAwait(false);
+                    draw.Font = await DownloadFont("webdings.ttf", "https://github.com/MediaBrowser/Emby.Resources/raw/master/fonts/webdings.ttf", _appPaths, _iHttpClient, _fileSystem).ConfigureAwait(false);
                     draw.FontSize = FontSize;
                     draw.FontStyle = FontStyleType.NormalStyle;
                     draw.TextAlignment = TextAlignType.CenterAlign;
@@ -52,18 +55,18 @@ namespace Emby.Drawing.ImageMagick
             }
         }
 
-        internal static string ExtractFont(string name, IApplicationPaths paths)
+		internal static string ExtractFont(string name, IApplicationPaths paths, IFileSystem fileSystem)
         {
             var filePath = Path.Combine(paths.ProgramDataPath, "fonts", name);
 
-            if (File.Exists(filePath))
+			if (fileSystem.FileExists(filePath))
             {
                 return filePath;
             }
 
             var namespacePath = typeof(PlayedIndicatorDrawer).Namespace + ".fonts." + name;
             var tempPath = Path.Combine(paths.TempDirectory, Guid.NewGuid().ToString("N") + ".ttf");
-            Directory.CreateDirectory(Path.GetDirectoryName(tempPath));
+			fileSystem.CreateDirectory(Path.GetDirectoryName(tempPath));
 
             using (var stream = typeof(PlayedIndicatorDrawer).Assembly.GetManifestResourceStream(namespacePath))
             {
@@ -73,11 +76,11 @@ namespace Emby.Drawing.ImageMagick
                 }
             }
 
-            Directory.CreateDirectory(Path.GetDirectoryName(filePath));
+			fileSystem.CreateDirectory(Path.GetDirectoryName(filePath));
 
             try
             {
-                File.Copy(tempPath, filePath, false);
+				fileSystem.CopyFile(tempPath, filePath, false);
             }
             catch (IOException)
             {
@@ -87,11 +90,11 @@ namespace Emby.Drawing.ImageMagick
             return tempPath;
         }
 
-        internal static async Task<string> DownloadFont(string name, string url, IApplicationPaths paths, IHttpClient httpClient)
+		internal static async Task<string> DownloadFont(string name, string url, IApplicationPaths paths, IHttpClient httpClient, IFileSystem fileSystem)
         {
             var filePath = Path.Combine(paths.ProgramDataPath, "fonts", name);
 
-            if (File.Exists(filePath))
+			if (fileSystem.FileExists(filePath))
             {
                 return filePath;
             }
@@ -103,11 +106,11 @@ namespace Emby.Drawing.ImageMagick
 
             }).ConfigureAwait(false);
 
-            Directory.CreateDirectory(Path.GetDirectoryName(filePath));
+			fileSystem.CreateDirectory(Path.GetDirectoryName(filePath));
 
             try
             {
-                File.Copy(tempPath, filePath, false);
+				fileSystem.CopyFile(tempPath, filePath, false);
             }
             catch (IOException)
             {

+ 5 - 2
Emby.Drawing/ImageMagick/StripCollageBuilder.cs

@@ -2,16 +2,19 @@
 using MediaBrowser.Common.Configuration;
 using System;
 using System.Collections.Generic;
+using MediaBrowser.Common.IO;
 
 namespace Emby.Drawing.ImageMagick
 {
     public class StripCollageBuilder
     {
         private readonly IApplicationPaths _appPaths;
+		private readonly IFileSystem _fileSystem;
 
-        public StripCollageBuilder(IApplicationPaths appPaths)
+		public StripCollageBuilder(IApplicationPaths appPaths, IFileSystem fileSystem)
         {
             _appPaths = appPaths;
+			_fileSystem = fileSystem;
         }
 
         public void BuildPosterCollage(List<string> paths, string outputPath, int width, int height, string text)
@@ -490,7 +493,7 @@ namespace Emby.Drawing.ImageMagick
 
         private string MontserratLightFont
         {
-            get { return PlayedIndicatorDrawer.ExtractFont("MontserratLight.otf", _appPaths); }
+			get { return PlayedIndicatorDrawer.ExtractFont("MontserratLight.otf", _appPaths, _fileSystem); }
         }
     }
 }

+ 1 - 1
Emby.Drawing/ImageMagick/UnplayedCountIndicator.cs

@@ -33,7 +33,7 @@ namespace Emby.Drawing.ImageMagick
                     pixel.Opacity = 0;
                     pixel.Color = "white";
                     draw.FillColor = pixel;
-                    draw.Font = PlayedIndicatorDrawer.ExtractFont("robotoregular.ttf", _appPaths);
+                    draw.Font = PlayedIndicatorDrawer.ExtractFont("robotoregular.ttf", _appPaths, _fileSystem);
                     draw.FontStyle = FontStyleType.NormalStyle;
                     draw.TextAlignment = TextAlignType.CenterAlign;
                     draw.FontWeight = FontWeightType.RegularStyle;

+ 8 - 8
Emby.Drawing/ImageProcessor.cs

@@ -215,12 +215,12 @@ namespace Emby.Drawing
             {
                 CheckDisposed();
 
-                if (!File.Exists(cacheFilePath))
+				if (!_fileSystem.FileExists(cacheFilePath))
                 {
                     var newWidth = Convert.ToInt32(newSize.Width);
                     var newHeight = Convert.ToInt32(newSize.Height);
 
-                    Directory.CreateDirectory(Path.GetDirectoryName(cacheFilePath));
+					_fileSystem.CreateDirectory(Path.GetDirectoryName(cacheFilePath));
 
                     await _imageProcessingSemaphore.WaitAsync().ConfigureAwait(false);
 
@@ -270,7 +270,7 @@ namespace Emby.Drawing
             await semaphore.WaitAsync().ConfigureAwait(false);
 
             // Check again in case of contention
-            if (File.Exists(croppedImagePath))
+			if (_fileSystem.FileExists(croppedImagePath))
             {
                 semaphore.Release();
                 return GetResult(croppedImagePath);
@@ -280,7 +280,7 @@ namespace Emby.Drawing
 
             try
             {
-                Directory.CreateDirectory(Path.GetDirectoryName(croppedImagePath));
+				_fileSystem.CreateDirectory(Path.GetDirectoryName(croppedImagePath));
 
                 await _imageProcessingSemaphore.WaitAsync().ConfigureAwait(false);
                 imageProcessingLockTaken = true;
@@ -366,7 +366,7 @@ namespace Emby.Drawing
         /// <returns>ImageSize.</returns>
         public ImageSize GetImageSize(string path)
         {
-            return GetImageSize(path, File.GetLastWriteTimeUtc(path), false);
+            return GetImageSize(path, _fileSystem.GetLastWriteTimeUtc(path), false);
         }
 
         public ImageSize GetImageSize(ItemImageInfo info)
@@ -452,7 +452,7 @@ namespace Emby.Drawing
                 try
                 {
                     var path = ImageSizeFile;
-                    Directory.CreateDirectory(Path.GetDirectoryName(path));
+					_fileSystem.CreateDirectory(Path.GetDirectoryName(path));
                     _jsonSerializer.SerializeToFile(_cachedImagedSizes, path);
                 }
                 catch (Exception ex)
@@ -624,7 +624,7 @@ namespace Emby.Drawing
             await semaphore.WaitAsync().ConfigureAwait(false);
 
             // Check again in case of contention
-            if (File.Exists(enhancedImagePath))
+			if (_fileSystem.FileExists(enhancedImagePath))
             {
                 semaphore.Release();
                 return enhancedImagePath;
@@ -634,7 +634,7 @@ namespace Emby.Drawing
 
             try
             {
-                Directory.CreateDirectory(Path.GetDirectoryName(enhancedImagePath));
+				_fileSystem.CreateDirectory(Path.GetDirectoryName(enhancedImagePath));
 
                 await _imageProcessingSemaphore.WaitAsync().ConfigureAwait(false);
 

+ 0 - 100
MediaBrowser.Api/AppThemeService.cs

@@ -1,100 +0,0 @@
-using MediaBrowser.Common.IO;
-using MediaBrowser.Controller.Net;
-using MediaBrowser.Controller.Themes;
-using MediaBrowser.Model.Themes;
-using ServiceStack;
-using System;
-using System.Collections.Generic;
-using System.IO;
-using System.Linq;
-
-namespace MediaBrowser.Api
-{
-    [Route("/Themes", "GET", Summary = "Gets a list of available themes for an app")]
-    public class GetAppThemes : IReturn<List<AppThemeInfo>>
-    {
-        [ApiMember(Name = "App", IsRequired = true, DataType = "string", ParameterType = "query", Verb = "GET")]
-        public string App { get; set; }
-    }
-
-    [Route("/Themes/Info", "GET", Summary = "Gets an app theme")]
-    public class GetAppTheme : IReturn<AppTheme>
-    {
-        [ApiMember(Name = "App", IsRequired = true, DataType = "string", ParameterType = "query", Verb = "GET")]
-        public string App { get; set; }
-
-        [ApiMember(Name = "Name", IsRequired = true, DataType = "string", ParameterType = "query", Verb = "GET")]
-        public string Name { get; set; }
-    }
-
-    [Route("/Themes/Images", "GET", Summary = "Gets an app theme")]
-    public class GetAppThemeImage
-    {
-        [ApiMember(Name = "App", IsRequired = true, DataType = "string", ParameterType = "query", Verb = "GET")]
-        public string App { get; set; }
-
-        [ApiMember(Name = "Theme", IsRequired = true, DataType = "string", ParameterType = "query", Verb = "GET")]
-        public string Theme { get; set; }
-
-        [ApiMember(Name = "Name", IsRequired = true, DataType = "string", ParameterType = "query", Verb = "GET")]
-        public string Name { get; set; }
-
-        [ApiMember(Name = "CacheTag", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")]
-        public string CacheTag { get; set; }
-    }
-
-    [Route("/Themes", "POST", Summary = "Saves a theme")]
-    public class SaveTheme : AppTheme, IReturnVoid
-    {
-    }
-
-    [Authenticated]
-    public class AppThemeService : BaseApiService
-    {
-        private readonly IAppThemeManager _themeManager;
-        private readonly IFileSystem _fileSystem;
-
-        public AppThemeService(IAppThemeManager themeManager, IFileSystem fileSystem)
-        {
-            _themeManager = themeManager;
-            _fileSystem = fileSystem;
-        }
-
-        public object Get(GetAppThemes request)
-        {
-            var result = _themeManager.GetThemes(request.App).ToList();
-
-            return ToOptimizedResult(result);
-        }
-
-        public object Get(GetAppTheme request)
-        {
-            var result = _themeManager.GetTheme(request.App, request.Name);
-
-            return ToOptimizedResult(result);
-        }
-
-        public void Post(SaveTheme request)
-        {
-            _themeManager.SaveTheme(request);
-        }
-
-        public object Get(GetAppThemeImage request)
-        {
-            var info = _themeManager.GetImageImageInfo(request.App, request.Theme, request.Name);
-
-            var cacheGuid = new Guid(info.CacheTag);
-
-            TimeSpan? cacheDuration = null;
-
-            if (!string.IsNullOrEmpty(request.CacheTag) && cacheGuid == new Guid(request.CacheTag))
-            {
-                cacheDuration = TimeSpan.FromDays(365);
-            }
-
-            var contentType = MimeTypes.GetMimeType(info.Path);
-
-            return ResultFactory.GetCachedResult(Request, cacheGuid, null, cacheDuration, () => _fileSystem.GetFileStream(info.Path, FileMode.Open, FileAccess.Read, FileShare.Read), contentType);
-        }
-    }
-}

+ 5 - 3
MediaBrowser.Api/EnvironmentService.cs

@@ -8,6 +8,7 @@ using System.Collections.Generic;
 using System.Globalization;
 using System.IO;
 using System.Linq;
+using MediaBrowser.Common.IO;
 
 namespace MediaBrowser.Api
 {
@@ -96,13 +97,14 @@ namespace MediaBrowser.Api
         /// The _network manager
         /// </summary>
         private readonly INetworkManager _networkManager;
+		private IFileSystem _fileSystem;
 
         /// <summary>
         /// Initializes a new instance of the <see cref="EnvironmentService" /> class.
         /// </summary>
         /// <param name="networkManager">The network manager.</param>
         /// <exception cref="System.ArgumentNullException">networkManager</exception>
-        public EnvironmentService(INetworkManager networkManager)
+		public EnvironmentService(INetworkManager networkManager, IFileSystem fileSystem)
         {
             if (networkManager == null)
             {
@@ -110,6 +112,7 @@ namespace MediaBrowser.Api
             }
 
             _networkManager = networkManager;
+			_fileSystem = fileSystem;
         }
 
         /// <summary>
@@ -222,8 +225,7 @@ namespace MediaBrowser.Api
         private IEnumerable<FileSystemEntryInfo> GetFileSystemEntries(GetDirectoryContents request)
         {
             // using EnumerateFileSystemInfos doesn't handle reparse points (symlinks)
-            var entries = new DirectoryInfo(request.Path).EnumerateDirectories("*", SearchOption.TopDirectoryOnly)
-                .Concat<FileSystemInfo>(new DirectoryInfo(request.Path).EnumerateFiles("*", SearchOption.TopDirectoryOnly)).Where(i =>
+			var entries = _fileSystem.GetFileSystemEntries(request.Path).Where(i =>
             {
                 if (!request.IncludeHidden && i.Attributes.HasFlag(FileAttributes.Hidden))
                 {

+ 10 - 11
MediaBrowser.Api/Images/ImageByNameService.cs

@@ -130,8 +130,7 @@ namespace MediaBrowser.Api.Images
         {
             try
             {
-                return new DirectoryInfo(path)
-                    .GetFiles("*", SearchOption.AllDirectories)
+				return _fileSystem.GetFiles(path)
                     .Where(i => BaseItem.SupportedImageExtensions.Contains(i.Extension, StringComparer.Ordinal))
                     .Select(i => new ImageByNameInfo
                     {
@@ -184,7 +183,7 @@ namespace MediaBrowser.Api.Images
 
             var paths = BaseItem.SupportedImageExtensions.Select(i => Path.Combine(_appPaths.GeneralPath, request.Name, filename + i)).ToList();
 
-            var path = paths.FirstOrDefault(File.Exists) ?? paths.FirstOrDefault();
+			var path = paths.FirstOrDefault(_fileSystem.FileExists) ?? paths.FirstOrDefault();
 
             return ToStaticFileResult(path);
         }
@@ -198,11 +197,11 @@ namespace MediaBrowser.Api.Images
         {
             var themeFolder = Path.Combine(_appPaths.RatingsPath, request.Theme);
 
-            if (Directory.Exists(themeFolder))
+			if (_fileSystem.DirectoryExists(themeFolder))
             {
                 var path = BaseItem.SupportedImageExtensions
                     .Select(i => Path.Combine(themeFolder, request.Name + i))
-                    .FirstOrDefault(File.Exists);
+					.FirstOrDefault(_fileSystem.FileExists);
 
                 if (!string.IsNullOrEmpty(path))
                 {
@@ -212,14 +211,14 @@ namespace MediaBrowser.Api.Images
 
             var allFolder = Path.Combine(_appPaths.RatingsPath, "all");
 
-            if (Directory.Exists(allFolder))
+			if (_fileSystem.DirectoryExists(allFolder))
             {
                 // Avoid implicitly captured closure
                 var currentRequest = request;
 
                 var path = BaseItem.SupportedImageExtensions
                     .Select(i => Path.Combine(allFolder, currentRequest.Name + i))
-                    .FirstOrDefault(File.Exists);
+					.FirstOrDefault(_fileSystem.FileExists);
 
                 if (!string.IsNullOrEmpty(path))
                 {
@@ -239,10 +238,10 @@ namespace MediaBrowser.Api.Images
         {
             var themeFolder = Path.Combine(_appPaths.MediaInfoImagesPath, request.Theme);
 
-            if (Directory.Exists(themeFolder))
+			if (_fileSystem.DirectoryExists(themeFolder))
             {
                 var path = BaseItem.SupportedImageExtensions.Select(i => Path.Combine(themeFolder, request.Name + i))
-                    .FirstOrDefault(File.Exists);
+					.FirstOrDefault(_fileSystem.FileExists);
 
                 if (!string.IsNullOrEmpty(path))
                 {
@@ -252,13 +251,13 @@ namespace MediaBrowser.Api.Images
 
             var allFolder = Path.Combine(_appPaths.MediaInfoImagesPath, "all");
 
-            if (Directory.Exists(allFolder))
+			if (_fileSystem.DirectoryExists(allFolder))
             {
                 // Avoid implicitly captured closure
                 var currentRequest = request;
 
                 var path = BaseItem.SupportedImageExtensions.Select(i => Path.Combine(allFolder, currentRequest.Name + i))
-                    .FirstOrDefault(File.Exists);
+					.FirstOrDefault(_fileSystem.FileExists);
 
                 if (!string.IsNullOrEmpty(path))
                 {

+ 3 - 3
MediaBrowser.Api/Images/RemoteImageService.cs

@@ -237,7 +237,7 @@ namespace MediaBrowser.Api.Images
                     contentPath = await reader.ReadToEndAsync().ConfigureAwait(false);
                 }
 
-                if (File.Exists(contentPath))
+				if (_fileSystem.FileExists(contentPath))
                 {
                     return ToStaticFileResult(contentPath);
                 }
@@ -281,7 +281,7 @@ namespace MediaBrowser.Api.Images
 
             var fullCachePath = GetFullCachePath(urlHash + "." + ext);
 
-            Directory.CreateDirectory(Path.GetDirectoryName(fullCachePath));
+			_fileSystem.CreateDirectory(Path.GetDirectoryName(fullCachePath));
             using (var stream = result.Content)
             {
                 using (var filestream = _fileSystem.GetFileStream(fullCachePath, FileMode.Create, FileAccess.Write, FileShare.Read, true))
@@ -290,7 +290,7 @@ namespace MediaBrowser.Api.Images
                 }
             }
 
-            Directory.CreateDirectory(Path.GetDirectoryName(pointerCachePath));
+			_fileSystem.CreateDirectory(Path.GetDirectoryName(pointerCachePath));
             using (var writer = new StreamWriter(pointerCachePath))
             {
                 await writer.WriteAsync(fullCachePath).ConfigureAwait(false);

+ 4 - 4
MediaBrowser.Api/ItemLookupService.cs

@@ -200,7 +200,7 @@ namespace MediaBrowser.Api
             //}
             item.ProviderIds = request.ProviderIds;
 
-            var task = _providerManager.RefreshFullItem(item, new MetadataRefreshOptions
+			var task = _providerManager.RefreshFullItem(item, new MetadataRefreshOptions(_fileSystem)
             {
                 MetadataRefreshMode = MetadataRefreshMode.FullRefresh,
                 ImageRefreshMode = ImageRefreshMode.FullRefresh,
@@ -230,7 +230,7 @@ namespace MediaBrowser.Api
                     contentPath = await reader.ReadToEndAsync().ConfigureAwait(false);
                 }
 
-                if (File.Exists(contentPath))
+				if (_fileSystem.FileExists(contentPath))
                 {
                     return ToStaticFileResult(contentPath);
                 }
@@ -271,7 +271,7 @@ namespace MediaBrowser.Api
 
             var fullCachePath = GetFullCachePath(urlHash + "." + ext);
 
-            Directory.CreateDirectory(Path.GetDirectoryName(fullCachePath));
+			_fileSystem.CreateDirectory(Path.GetDirectoryName(fullCachePath));
             using (var stream = result.Content)
             {
                 using (var filestream = _fileSystem.GetFileStream(fullCachePath, FileMode.Create, FileAccess.Write, FileShare.Read, true))
@@ -280,7 +280,7 @@ namespace MediaBrowser.Api
                 }
             }
 
-            Directory.CreateDirectory(Path.GetDirectoryName(pointerCachePath));
+			_fileSystem.CreateDirectory(Path.GetDirectoryName(pointerCachePath));
             using (var writer = new StreamWriter(pointerCachePath))
             {
                 await writer.WriteAsync(fullCachePath).ConfigureAwait(false);

+ 1 - 1
MediaBrowser.Api/ItemRefreshService.cs

@@ -66,7 +66,7 @@ namespace MediaBrowser.Api
 
         private MetadataRefreshOptions GetRefreshOptions(BaseRefreshRequest request)
         {
-            return new MetadataRefreshOptions(new DirectoryService())
+            return new MetadataRefreshOptions(new DirectoryService(_fileSystem))
             {
                 MetadataRefreshMode = request.MetadataRefreshMode,
                 ImageRefreshMode = request.ImageRefreshMode,

+ 3 - 3
MediaBrowser.Api/Library/LibraryHelpers.cs

@@ -33,7 +33,7 @@ namespace MediaBrowser.Api.Library
             var rootFolderPath = appPaths.DefaultUserViewsPath;
             var path = Path.Combine(rootFolderPath, virtualFolderName);
 
-            if (!Directory.Exists(path))
+			if (!fileSystem.DirectoryExists(path))
             {
                 throw new DirectoryNotFoundException(string.Format("The media collection {0} does not exist", virtualFolderName));
             }
@@ -57,7 +57,7 @@ namespace MediaBrowser.Api.Library
         /// <exception cref="System.ArgumentException">The path is not valid.</exception>
         public static void AddMediaPath(IFileSystem fileSystem, string virtualFolderName, string path, IServerApplicationPaths appPaths)
         {
-            if (!Directory.Exists(path))
+			if (!fileSystem.DirectoryExists(path))
             {
                 throw new DirectoryNotFoundException("The path does not exist.");
             }
@@ -69,7 +69,7 @@ namespace MediaBrowser.Api.Library
 
             var lnk = Path.Combine(virtualFolderPath, shortcutFilename + ShortcutFileExtension);
 
-            while (File.Exists(lnk))
+			while (fileSystem.FileExists(lnk))
             {
                 shortcutFilename += "1";
                 lnk = Path.Combine(virtualFolderPath, shortcutFilename + ShortcutFileExtension);

+ 1 - 1
MediaBrowser.Api/Library/LibraryService.cs

@@ -557,7 +557,7 @@ namespace MediaBrowser.Api.Library
             {
                 throw new ArgumentException("This command cannot be used for remote or virtual items.");
             }
-            if (Directory.Exists(item.Path))
+			if (_fileSystem.DirectoryExists(item.Path))
             {
                 throw new ArgumentException("This command cannot be used for directories.");
             }

+ 8 - 8
MediaBrowser.Api/Library/LibraryStructureService.cs

@@ -195,7 +195,7 @@ namespace MediaBrowser.Api.Library
 
             var virtualFolderPath = Path.Combine(rootFolderPath, name);
 
-            if (Directory.Exists(virtualFolderPath))
+            if (_fileSystem.DirectoryExists(virtualFolderPath))
             {
                 throw new ArgumentException("There is already a media collection with the name " + name + ".");
             }
@@ -204,13 +204,13 @@ namespace MediaBrowser.Api.Library
 
             try
             {
-                Directory.CreateDirectory(virtualFolderPath);
+				_fileSystem.CreateDirectory(virtualFolderPath);
 
                 if (!string.IsNullOrEmpty(request.CollectionType))
                 {
                     var path = Path.Combine(virtualFolderPath, request.CollectionType + ".collection");
 
-                    File.Create(path);
+                    _fileSystem.CreateFile(path);
                 }
             }
             finally
@@ -256,12 +256,12 @@ namespace MediaBrowser.Api.Library
             var currentPath = Path.Combine(rootFolderPath, request.Name);
             var newPath = Path.Combine(rootFolderPath, request.NewName);
 
-            if (!Directory.Exists(currentPath))
+			if (!_fileSystem.DirectoryExists(currentPath))
             {
                 throw new DirectoryNotFoundException("The media collection does not exist");
             }
 
-            if (!string.Equals(currentPath, newPath, StringComparison.OrdinalIgnoreCase) && Directory.Exists(newPath))
+			if (!string.Equals(currentPath, newPath, StringComparison.OrdinalIgnoreCase) && _fileSystem.DirectoryExists(newPath))
             {
                 throw new ArgumentException("There is already a media collection with the name " + newPath + ".");
             }
@@ -276,11 +276,11 @@ namespace MediaBrowser.Api.Library
                     //Create an unique name
                     var temporaryName = Guid.NewGuid().ToString();
                     var temporaryPath = Path.Combine(rootFolderPath, temporaryName);
-                    Directory.Move(currentPath, temporaryPath);
+					_fileSystem.MoveDirectory(currentPath, temporaryPath);
                     currentPath = temporaryPath;
                 }
 
-                Directory.Move(currentPath, newPath);
+				_fileSystem.MoveDirectory(currentPath, newPath);
             }
             finally
             {
@@ -319,7 +319,7 @@ namespace MediaBrowser.Api.Library
 
             var path = Path.Combine(rootFolderPath, request.Name);
 
-            if (!Directory.Exists(path))
+			if (!_fileSystem.DirectoryExists(path))
             {
                 throw new DirectoryNotFoundException("The media folder does not exist");
             }

+ 5 - 10
MediaBrowser.Api/MediaBrowser.Api.csproj

@@ -11,10 +11,10 @@
     <AssemblyName>MediaBrowser.Api</AssemblyName>
     <FileAlignment>512</FileAlignment>
     <SolutionDir Condition="$(SolutionDir) == '' Or $(SolutionDir) == '*Undefined*'">..\</SolutionDir>
-    <ProductVersion>10.0.0</ProductVersion>
-    <SchemaVersion>2.0</SchemaVersion>
     <TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
     <RestorePackages>true</RestorePackages>
+    <ReleaseVersion>
+    </ReleaseVersion>
   </PropertyGroup>
   <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
     <DebugSymbols>true</DebugSymbols>
@@ -25,7 +25,6 @@
     <ErrorReport>prompt</ErrorReport>
     <WarningLevel>4</WarningLevel>
     <PlatformTarget>AnyCPU</PlatformTarget>
-    <TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
   </PropertyGroup>
   <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
     <DebugType>none</DebugType>
@@ -34,7 +33,6 @@
     <DefineConstants>TRACE</DefineConstants>
     <ErrorReport>prompt</ErrorReport>
     <WarningLevel>4</WarningLevel>
-    <TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
   </PropertyGroup>
   <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release Mono|AnyCPU' ">
     <DebugType>none</DebugType>
@@ -43,16 +41,11 @@
     <DefineConstants>TRACE</DefineConstants>
     <ErrorReport>prompt</ErrorReport>
     <WarningLevel>4</WarningLevel>
-    <TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
   </PropertyGroup>
   <PropertyGroup>
     <RunPostBuildEvent>Always</RunPostBuildEvent>
   </PropertyGroup>
   <ItemGroup>
-    <Reference Include="MoreLinq, Version=1.1.18418.0, Culture=neutral, PublicKeyToken=384d532d7e88985d, processorArchitecture=MSIL">
-      <SpecificVersion>False</SpecificVersion>
-      <HintPath>..\packages\morelinq.1.1.1\lib\net35\MoreLinq.dll</HintPath>
-    </Reference>
     <Reference Include="System" />
     <Reference Include="System.Core" />
     <Reference Include="Microsoft.CSharp" />
@@ -64,12 +57,14 @@
     <Reference Include="ServiceStack.Text">
       <HintPath>..\ThirdParty\ServiceStack.Text\ServiceStack.Text.dll</HintPath>
     </Reference>
+    <Reference Include="MoreLinq">
+      <HintPath>..\packages\morelinq.1.1.1\lib\net35\MoreLinq.dll</HintPath>
+    </Reference>
   </ItemGroup>
   <ItemGroup>
     <Compile Include="..\SharedVersion.cs">
       <Link>Properties\SharedVersion.cs</Link>
     </Compile>
-    <Compile Include="AppThemeService.cs" />
     <Compile Include="BrandingService.cs" />
     <Compile Include="ChannelService.cs" />
     <Compile Include="ConnectService.cs" />

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

@@ -889,7 +889,7 @@ namespace MediaBrowser.Api.Playback
             CancellationTokenSource cancellationTokenSource,
             string workingDirectory = null)
         {
-            Directory.CreateDirectory(Path.GetDirectoryName(outputPath));
+            FileSystem.CreateDirectory(Path.GetDirectoryName(outputPath));
 
             await AcquireResources(state, cancellationTokenSource).ConfigureAwait(false);
 
@@ -942,7 +942,7 @@ namespace MediaBrowser.Api.Playback
             Logger.Info(commandLineLogMessage);
 
             var logFilePath = Path.Combine(ServerConfigurationManager.ApplicationPaths.LogDirectoryPath, "transcode-" + Guid.NewGuid() + ".txt");
-            Directory.CreateDirectory(Path.GetDirectoryName(logFilePath));
+            FileSystem.CreateDirectory(Path.GetDirectoryName(logFilePath));
 
             // FFMpeg writes debug/error info to stderr. This is useful when debugging so let's put it in the log directory.
             state.LogFileStream = FileSystem.GetFileStream(logFilePath, FileMode.Create, FileAccess.Write, FileShare.Read, true);
@@ -972,7 +972,7 @@ namespace MediaBrowser.Api.Playback
             StartStreamingLog(transcodingJob, state, process.StandardError.BaseStream, state.LogFileStream);
 
             // Wait for the file to exist before proceeeding
-            while (!File.Exists(state.WaitForPath ?? outputPath) && !transcodingJob.HasExited)
+			while (!FileSystem.FileExists(state.WaitForPath ?? outputPath) && !transcodingJob.HasExited)
             {
                 await Task.Delay(100, cancellationTokenSource.Token).ConfigureAwait(false);
             }

+ 5 - 6
MediaBrowser.Api/Playback/Dash/MpegDashService.cs

@@ -174,7 +174,7 @@ namespace MediaBrowser.Api.Playback.Dash
 
                                 var workingDirectory = Path.Combine(Path.GetDirectoryName(playlistPath), (startNumber == -1 ? 0 : startNumber).ToString(CultureInfo.InvariantCulture));
                                 state.WaitForPath = Path.Combine(workingDirectory, Path.GetFileName(playlistPath));
-                                Directory.CreateDirectory(workingDirectory);
+                                FileSystem.CreateDirectory(workingDirectory);
                                 job = await StartFfMpeg(state, playlistPath, cancellationTokenSource, workingDirectory).ConfigureAwait(false);
                                 await WaitForMinimumDashSegmentCount(Path.Combine(workingDirectory, Path.GetFileName(playlistPath)), 1, cancellationTokenSource.Token).ConfigureAwait(false);
                             }
@@ -328,8 +328,7 @@ namespace MediaBrowser.Api.Playback.Dash
 
             try
             {
-                return new DirectoryInfo(folder)
-                    .EnumerateFiles("*", SearchOption.AllDirectories)
+				return fileSystem.GetFiles(folder)
                     .Where(i => string.Equals(i.Extension, segmentExtension, StringComparison.OrdinalIgnoreCase))
                     .OrderByDescending(fileSystem.GetLastWriteTimeUtc)
                     .Take(count)
@@ -348,12 +347,12 @@ namespace MediaBrowser.Api.Playback.Dash
             if (requestedIndex == -1)
             {
                 var path = Path.Combine(folder, "0", "stream" + representationId + "-" + "init" + segmentExtension);
-                return File.Exists(path) ? path : null;
+				return FileSystem.FileExists(path) ? path : null;
             }
 
             try
             {
-                foreach (var subfolder in new DirectoryInfo(folder).EnumerateDirectories().ToList())
+				foreach (var subfolder in FileSystem.GetDirectories(folder).ToList())
                 {
                     var subfolderName = Path.GetFileNameWithoutExtension(subfolder.FullName);
                     int startNumber;
@@ -361,7 +360,7 @@ namespace MediaBrowser.Api.Playback.Dash
                     {
                         var segmentIndex = requestedIndex - startNumber + 1;
                         var path = Path.Combine(folder, subfolderName, "stream" + representationId + "-" + segmentIndex.ToString("00000", CultureInfo.InvariantCulture) + segmentExtension);
-                        if (File.Exists(path))
+						if (FileSystem.FileExists(path))
                         {
                             return path;
                         }

+ 2 - 2
MediaBrowser.Api/Playback/Hls/BaseHlsService.cs

@@ -90,12 +90,12 @@ namespace MediaBrowser.Api.Playback.Hls
             TranscodingJob job = null;
             var playlist = state.OutputFilePath;
 
-            if (!File.Exists(playlist))
+			if (!FileSystem.FileExists(playlist))
             {
                 await ApiEntryPoint.Instance.TranscodingStartLock.WaitAsync(cancellationTokenSource.Token).ConfigureAwait(false);
                 try
                 {
-                    if (!File.Exists(playlist))
+					if (!FileSystem.FileExists(playlist))
                     {
                         // If the playlist doesn't already exist, startup ffmpeg
                         try

+ 5 - 6
MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs

@@ -165,7 +165,7 @@ namespace MediaBrowser.Api.Playback.Hls
 
             TranscodingJob job = null;
 
-            if (File.Exists(segmentPath))
+			if (FileSystem.FileExists(segmentPath))
             {
                 job = ApiEntryPoint.Instance.OnTranscodeBeginRequest(playlistPath, TranscodingJobType);
                 return await GetSegmentResult(state, playlistPath, segmentPath, requestedIndex, job, cancellationToken).ConfigureAwait(false);
@@ -174,7 +174,7 @@ namespace MediaBrowser.Api.Playback.Hls
             await ApiEntryPoint.Instance.TranscodingStartLock.WaitAsync(cancellationTokenSource.Token).ConfigureAwait(false);
             try
             {
-                if (File.Exists(segmentPath))
+				if (FileSystem.FileExists(segmentPath))
                 {
                     job = ApiEntryPoint.Instance.OnTranscodeBeginRequest(playlistPath, TranscodingJobType);
                     return await GetSegmentResult(state, playlistPath, segmentPath, requestedIndex, job, cancellationToken).ConfigureAwait(false);
@@ -386,8 +386,7 @@ namespace MediaBrowser.Api.Playback.Hls
 
             try
             {
-                return new DirectoryInfo(folder)
-                    .EnumerateFiles("*", SearchOption.TopDirectoryOnly)
+				return fileSystem.GetFiles(folder)
                     .Where(i => string.Equals(i.Extension, segmentExtension, StringComparison.OrdinalIgnoreCase) && Path.GetFileNameWithoutExtension(i.Name).StartsWith(filePrefix, StringComparison.OrdinalIgnoreCase))
                     .OrderByDescending(fileSystem.GetLastWriteTimeUtc)
                     .FirstOrDefault();
@@ -432,7 +431,7 @@ namespace MediaBrowser.Api.Playback.Hls
             CancellationToken cancellationToken)
         {
             // If all transcoding has completed, just return immediately
-            if (transcodingJob != null && transcodingJob.HasExited && File.Exists(segmentPath))
+			if (transcodingJob != null && transcodingJob.HasExited && FileSystem.FileExists(segmentPath))
             {
                 return GetSegmentResult(state, segmentPath, segmentIndex, transcodingJob);
             }
@@ -452,7 +451,7 @@ namespace MediaBrowser.Api.Playback.Hls
                             // If it appears in the playlist, it's done
                             if (text.IndexOf(segmentFilename, StringComparison.OrdinalIgnoreCase) != -1)
                             {
-                                if (File.Exists(segmentPath))
+								if (FileSystem.FileExists(segmentPath))
                                 {
                                     return GetSegmentResult(state, segmentPath, segmentIndex, transcodingJob);
                                 }

+ 2 - 2
MediaBrowser.Api/Playback/Progressive/BaseProgressiveStreamingService.cs

@@ -139,7 +139,7 @@ namespace MediaBrowser.Api.Playback.Progressive
             }
 
             var outputPath = state.OutputFilePath;
-            var outputPathExists = File.Exists(outputPath);
+			var outputPathExists = FileSystem.FileExists(outputPath);
 
             var isTranscodeCached = outputPathExists && !ApiEntryPoint.Instance.HasActiveTranscodingJob(outputPath, TranscodingJobType.Progressive);
 
@@ -325,7 +325,7 @@ namespace MediaBrowser.Api.Playback.Progressive
             {
                 TranscodingJob job;
 
-                if (!File.Exists(outputPath))
+				if (!FileSystem.FileExists(outputPath))
                 {
                     job = await StartFfMpeg(state, outputPath, cancellationTokenSource).ConfigureAwait(false);
                 }

+ 1 - 1
MediaBrowser.Api/Subtitles/SubtitleService.cs

@@ -259,7 +259,7 @@ namespace MediaBrowser.Api.Subtitles
                     await _subtitleManager.DownloadSubtitles(video, request.SubtitleId, CancellationToken.None)
                         .ConfigureAwait(false);
 
-                    _providerManager.QueueRefresh(video.Id, new MetadataRefreshOptions());
+                    _providerManager.QueueRefresh(video.Id, new MetadataRefreshOptions(_fileSystem));
                 }
                 catch (Exception ex)
                 {

+ 2 - 4
MediaBrowser.Api/System/SystemService.cs

@@ -122,8 +122,7 @@ namespace MediaBrowser.Api.System
 
             try
             {
-                files = new DirectoryInfo(_appPaths.LogDirectoryPath)
-                    .EnumerateFiles("*", SearchOption.AllDirectories)
+				files = _fileSystem.GetFiles(_appPaths.LogDirectoryPath)
                     .Where(i => string.Equals(i.Extension, ".txt", StringComparison.OrdinalIgnoreCase))
                     .ToList();
             }
@@ -149,8 +148,7 @@ namespace MediaBrowser.Api.System
 
         public object Get(GetLogFile request)
         {
-            var file = new DirectoryInfo(_appPaths.LogDirectoryPath)
-                .EnumerateFiles("*", SearchOption.AllDirectories)
+			var file = _fileSystem.GetFiles(_appPaths.LogDirectoryPath)
                 .First(i => string.Equals(i.Name, request.Name, StringComparison.OrdinalIgnoreCase));
 
             return ResultFactory.GetStaticFileResult(Request, file.FullName, FileShare.ReadWrite);

+ 13 - 5
MediaBrowser.Common.Implementations/Archiving/ZipClient.cs

@@ -7,6 +7,7 @@ using SharpCompress.Reader;
 using SharpCompress.Reader.Zip;
 using System;
 using System.IO;
+using MediaBrowser.Common.IO;
 
 namespace MediaBrowser.Common.Implementations.Archiving
 {
@@ -15,7 +16,14 @@ namespace MediaBrowser.Common.Implementations.Archiving
     /// </summary>
     public class ZipClient : IZipClient
     {
-        /// <summary>
+		private IFileSystem _fileSystem;
+
+		public ZipClient(IFileSystem fileSystem) 
+		{
+			_fileSystem = fileSystem;
+		}
+
+		/// <summary>
         /// Extracts all.
         /// </summary>
         /// <param name="sourceFile">The source file.</param>
@@ -23,7 +31,7 @@ namespace MediaBrowser.Common.Implementations.Archiving
         /// <param name="overwriteExistingFiles">if set to <c>true</c> [overwrite existing files].</param>
         public void ExtractAll(string sourceFile, string targetPath, bool overwriteExistingFiles)
         {
-            using (var fileStream = File.OpenRead(sourceFile))
+			using (var fileStream = _fileSystem.OpenRead(sourceFile))
             {
                 ExtractAll(fileStream, targetPath, overwriteExistingFiles);
             }
@@ -73,7 +81,7 @@ namespace MediaBrowser.Common.Implementations.Archiving
         /// <param name="overwriteExistingFiles">if set to <c>true</c> [overwrite existing files].</param>
         public void ExtractAllFrom7z(string sourceFile, string targetPath, bool overwriteExistingFiles)
         {
-            using (var fileStream = File.OpenRead(sourceFile))
+			using (var fileStream = _fileSystem.OpenRead(sourceFile))
             {
                 ExtractAllFrom7z(fileStream, targetPath, overwriteExistingFiles);
             }
@@ -112,7 +120,7 @@ namespace MediaBrowser.Common.Implementations.Archiving
         /// <param name="overwriteExistingFiles">if set to <c>true</c> [overwrite existing files].</param>
         public void ExtractAllFromTar(string sourceFile, string targetPath, bool overwriteExistingFiles)
         {
-            using (var fileStream = File.OpenRead(sourceFile))
+			using (var fileStream = _fileSystem.OpenRead(sourceFile))
             {
                 ExtractAllFromTar(fileStream, targetPath, overwriteExistingFiles);
             }
@@ -150,7 +158,7 @@ namespace MediaBrowser.Common.Implementations.Archiving
         /// <param name="overwriteExistingFiles">if set to <c>true</c> [overwrite existing files].</param>
         public void ExtractAllFromRar(string sourceFile, string targetPath, bool overwriteExistingFiles)
         {
-            using (var fileStream = File.OpenRead(sourceFile))
+			using (var fileStream = _fileSystem.OpenRead(sourceFile))
             {
                 ExtractAllFromRar(fileStream, targetPath, overwriteExistingFiles);
             }

+ 5 - 4
MediaBrowser.Common.Implementations/BaseApplicationHost.cs

@@ -93,7 +93,7 @@ namespace MediaBrowser.Common.Implementations
         /// <summary>
         /// The _XML serializer
         /// </summary>
-        protected readonly IXmlSerializer XmlSerializer = new XmlSerializer();
+        protected readonly IXmlSerializer XmlSerializer;
 
         /// <summary>
         /// Gets assemblies that failed to load
@@ -180,7 +180,7 @@ namespace MediaBrowser.Common.Implementations
             {
                 if (_deviceId == null)
                 {
-                    _deviceId = new DeviceId(ApplicationPaths, LogManager.GetLogger("SystemId"));
+                    _deviceId = new DeviceId(ApplicationPaths, LogManager.GetLogger("SystemId"), FileSystemManager);
                 }
 
                 return _deviceId.Value;
@@ -199,6 +199,7 @@ namespace MediaBrowser.Common.Implementations
             ILogManager logManager, 
             IFileSystem fileSystem)
         {
+			XmlSerializer = new MediaBrowser.Common.Implementations.Serialization.XmlSerializer (fileSystem);
             FailedAssemblies = new List<string>();
 
             ApplicationPaths = applicationPaths;
@@ -473,7 +474,7 @@ namespace MediaBrowser.Common.Implementations
 			InstallationManager = new InstallationManager(Logger, this, ApplicationPaths, HttpClient, JsonSerializer, SecurityManager, ConfigurationManager, FileSystemManager);
 			RegisterSingleInstance(InstallationManager);
 
-			ZipClient = new ZipClient();
+			ZipClient = new ZipClient(FileSystemManager);
 			RegisterSingleInstance(ZipClient);
 
 			IsoManager = new IsoManager();
@@ -650,7 +651,7 @@ namespace MediaBrowser.Common.Implementations
         {
             try
             {
-                return Assembly.Load(File.ReadAllBytes((file)));
+                return Assembly.Load(FileSystemManager.ReadAllBytes((file)));
             }
             catch (Exception ex)
             {

+ 2 - 2
MediaBrowser.Common.Implementations/BaseApplicationPaths.cs

@@ -45,7 +45,7 @@ namespace MediaBrowser.Common.Implementations
                 {
                     _dataDirectory = Path.Combine(ProgramDataPath, "data");
 
-                    Directory.CreateDirectory(_dataDirectory);
+                    FileSystem.CreateDirectory(_dataDirectory);
                 }
 
                 return _dataDirectory;
@@ -152,7 +152,7 @@ namespace MediaBrowser.Common.Implementations
                 {
                     _cachePath = Path.Combine(ProgramDataPath, "cache");
 
-                    Directory.CreateDirectory(_cachePath);
+                    FileSystem.CreateDirectory(_cachePath);
                 }
 
                 return _cachePath;

+ 2 - 2
MediaBrowser.Common.Implementations/Configuration/BaseConfigurationManager.cs

@@ -121,7 +121,7 @@ namespace MediaBrowser.Common.Implementations.Configuration
         {
             var path = CommonApplicationPaths.SystemConfigurationFilePath;
 
-            Directory.CreateDirectory(Path.GetDirectoryName(path));
+            FileSystem.CreateDirectory(Path.GetDirectoryName(path));
 
             lock (_configurationSyncLock)
             {
@@ -276,7 +276,7 @@ namespace MediaBrowser.Common.Implementations.Configuration
             _configurations.AddOrUpdate(key, configuration, (k, v) => configuration);
 
             var path = GetConfigurationFile(key);
-            Directory.CreateDirectory(Path.GetDirectoryName(path));
+            FileSystem.CreateDirectory(Path.GetDirectoryName(path));
 
             lock (_configurationSyncLock)
             {

+ 5 - 4
MediaBrowser.Common.Implementations/Configuration/ConfigurationHelper.cs

@@ -2,6 +2,7 @@
 using System;
 using System.IO;
 using System.Linq;
+using MediaBrowser.Common.IO;
 
 namespace MediaBrowser.Common.Implementations.Configuration
 {
@@ -18,7 +19,7 @@ namespace MediaBrowser.Common.Implementations.Configuration
         /// <param name="path">The path.</param>
         /// <param name="xmlSerializer">The XML serializer.</param>
         /// <returns>System.Object.</returns>
-        public static object GetXmlConfiguration(Type type, string path, IXmlSerializer xmlSerializer)
+        public static object GetXmlConfiguration(Type type, string path, IXmlSerializer xmlSerializer, IFileSystem fileSystem)
         {
             object configuration;
 
@@ -27,7 +28,7 @@ namespace MediaBrowser.Common.Implementations.Configuration
             // Use try/catch to avoid the extra file system lookup using File.Exists
             try
             {
-                buffer = File.ReadAllBytes(path);
+				buffer = fileSystem.ReadAllBytes(path);
 
                 configuration = xmlSerializer.DeserializeFromBytes(type, buffer);
             }
@@ -46,10 +47,10 @@ namespace MediaBrowser.Common.Implementations.Configuration
                 // If the file didn't exist before, or if something has changed, re-save
                 if (buffer == null || !buffer.SequenceEqual(newBytes))
                 {
-                    Directory.CreateDirectory(Path.GetDirectoryName(path));
+					fileSystem.CreateDirectory(Path.GetDirectoryName(path));
 
                     // Save it after load in case we got new items
-                    File.WriteAllBytes(path, newBytes);
+					fileSystem.WriteAllBytes(path, newBytes);
                 }
 
                 return configuration;

+ 12 - 5
MediaBrowser.Common.Implementations/Devices/DeviceId.cs

@@ -3,13 +3,15 @@ using MediaBrowser.Model.Logging;
 using System;
 using System.IO;
 using System.Text;
+using MediaBrowser.Common.IO;
 
 namespace MediaBrowser.Common.Implementations.Devices
 {
     public class DeviceId
     {
         private readonly IApplicationPaths _appPaths;
-        private readonly ILogger _logger;
+		private readonly ILogger _logger;
+		private readonly IFileSystem _fileSystem;
 
         private readonly object _syncLock = new object();
 
@@ -24,7 +26,7 @@ namespace MediaBrowser.Common.Implementations.Devices
             {
                 lock (_syncLock)
                 {
-                    var value = File.ReadAllText(CachePath, Encoding.UTF8);
+					var value = _fileSystem.ReadAllText(CachePath, Encoding.UTF8);
 
                     Guid guid;
                     if (Guid.TryParse(value, out guid))
@@ -55,11 +57,11 @@ namespace MediaBrowser.Common.Implementations.Devices
             {
                 var path = CachePath;
 
-                Directory.CreateDirectory(Path.GetDirectoryName(path));
+				_fileSystem.CreateDirectory(Path.GetDirectoryName(path));
 
                 lock (_syncLock)
                 {
-                    File.WriteAllText(path, id, Encoding.UTF8);
+                    _fileSystem.WriteAllText(path, id, Encoding.UTF8);
                 }
             }
             catch (Exception ex)
@@ -88,10 +90,15 @@ namespace MediaBrowser.Common.Implementations.Devices
 
         private string _id;
 
-        public DeviceId(IApplicationPaths appPaths, ILogger logger)
+        public DeviceId(IApplicationPaths appPaths, ILogger logger, IFileSystem fileSystem)
         {
+			if (fileSystem == null) {
+				throw new ArgumentNullException ("fileSystem");
+			}
+
             _appPaths = appPaths;
             _logger = logger;
+			_fileSystem = fileSystem;
         }
 
         public string Value

+ 2 - 2
MediaBrowser.Common.Implementations/HttpClientManager/HttpClientManager.cs

@@ -355,7 +355,7 @@ namespace MediaBrowser.Common.Implementations.HttpClientManager
 
         private async Task CacheResponse(HttpResponseInfo response, string responseCachePath)
         {
-            Directory.CreateDirectory(Path.GetDirectoryName(responseCachePath));
+			_fileSystem.CreateDirectory(Path.GetDirectoryName(responseCachePath));
 
             using (var responseStream = response.Content)
             {
@@ -599,7 +599,7 @@ namespace MediaBrowser.Common.Implementations.HttpClientManager
         {
             ValidateParams(options);
 
-            Directory.CreateDirectory(_appPaths.TempDirectory);
+			_fileSystem.CreateDirectory(_appPaths.TempDirectory);
 
             var tempFile = Path.Combine(_appPaths.TempDirectory, Guid.NewGuid() + ".tmp");
 

+ 40 - 20
MediaBrowser.Common.Implementations/IO/CommonFileSystem.cs

@@ -4,6 +4,8 @@ using MediaBrowser.Model.Logging;
 using System;
 using System.IO;
 using System.Text;
+using System.Collections.Generic;
+using System.Linq;
 
 namespace MediaBrowser.Common.Implementations.IO
 {
@@ -75,7 +77,7 @@ namespace MediaBrowser.Common.Implementations.IO
 
             if (string.Equals(Path.GetExtension(filename), ".mblink", StringComparison.OrdinalIgnoreCase))
             {
-                var path = File.ReadAllText(filename);
+                var path = ReadAllText(filename);
 
                 return NormalizePath(path);
             }
@@ -105,7 +107,7 @@ namespace MediaBrowser.Common.Implementations.IO
                 throw new ArgumentNullException("target");
             }
 
-            File.WriteAllText(shortcutPath, target);
+			_fileSystem.WriteAllText(shortcutPath, target);
         }
 
         /// <summary>
@@ -230,7 +232,7 @@ namespace MediaBrowser.Common.Implementations.IO
         /// <param name="share">The share.</param>
         /// <param name="isAsync">if set to <c>true</c> [is asynchronous].</param>
         /// <returns>FileStream.</returns>
-        public FileStream GetFileStream(string path, FileMode mode, FileAccess access, FileShare share, bool isAsync = false)
+        public Stream GetFileStream(string path, FileMode mode, FileAccess access, FileShare share, bool isAsync = false)
         {
             if (_supportsAsyncFileStreams && isAsync)
             {
@@ -264,11 +266,11 @@ namespace MediaBrowser.Common.Implementations.IO
             RemoveHiddenAttribute(file1);
             RemoveHiddenAttribute(file2);
 
-            File.Copy(file1, temp1, true);
-            File.Copy(file2, temp2, true);
+			CopyFile(file1, temp1, true);
+			CopyFile(file2, temp2, true);
 
-            File.Copy(temp1, file2, true);
-            File.Copy(temp2, file1, true);
+			CopyFile(temp1, file2, true);
+            CopyFile(temp2, file1, true);
 
             DeleteFile(temp1);
             DeleteFile(temp2);
@@ -410,24 +412,42 @@ namespace MediaBrowser.Common.Implementations.IO
             //return Path.IsPathRooted(path);
         }
 
-        public void DeleteFile(string path, bool sendToRecycleBin)
-        {
-            File.Delete(path);
-        }
-
-        public void DeleteDirectory(string path, bool recursive, bool sendToRecycleBin)
-        {
-            Directory.Delete(path, recursive);
-        }
-
         public void DeleteFile(string path)
         {
-            DeleteFile(path, false);
+            File.Delete(path);
         }
 
         public void DeleteDirectory(string path, bool recursive)
         {
-            DeleteDirectory(path, recursive, false);
-        }
+            Directory.Delete(path, recursive);
+		}
+
+		public void CreateDirectory(string path)
+		{
+			Directory.CreateDirectory(path);
+		}
+			
+		public IEnumerable<DirectoryInfo> GetDirectories(string path, bool recursive = false) 
+		{
+			var searchOption = recursive ? SearchOption.AllDirectories : SearchOption.TopDirectoryOnly;
+
+			return new DirectoryInfo (path).EnumerateDirectories("*", searchOption);
+		}
+
+		public IEnumerable<FileInfo> GetFiles(string path, bool recursive = false) 
+		{
+			var searchOption = recursive ? SearchOption.AllDirectories : SearchOption.TopDirectoryOnly;
+
+			return new DirectoryInfo (path).EnumerateFiles("*", searchOption);
+		}
+
+		public IEnumerable<FileSystemInfo> GetFileSystemEntries(string path, bool recursive = false) 
+		{
+			var directoryInfo = new DirectoryInfo (path);
+			var searchOption = recursive ? SearchOption.AllDirectories : SearchOption.TopDirectoryOnly;
+
+			return directoryInfo.EnumerateDirectories("*", searchOption)
+							.Concat<FileSystemInfo>(directoryInfo.EnumerateFiles("*", searchOption));
+		}
     }
 }

+ 1 - 1
MediaBrowser.Common.Implementations/Logging/NlogManager.cs

@@ -208,7 +208,7 @@ namespace MediaBrowser.Common.Implementations.Logging
         {
             LogFilePath = Path.Combine(LogDirectory, LogFilePrefix + "-" + decimal.Round(DateTime.Now.Ticks / 10000000) + ".txt");
 
-            Directory.CreateDirectory(Path.GetDirectoryName(LogFilePath));
+			_fileSystem.CreateDirectory(Path.GetDirectoryName(LogFilePath));
 
             AddFileTarget(LogFilePath, level);
 

+ 2 - 2
MediaBrowser.Common.Implementations/ScheduledTasks/ScheduledTaskWorker.cs

@@ -154,7 +154,7 @@ namespace MediaBrowser.Common.Implementations.ScheduledTasks
                 _lastExecutionResult = value;
 
                 var path = GetHistoryFilePath();
-                Directory.CreateDirectory(Path.GetDirectoryName(path));
+				_fileSystem.CreateDirectory(Path.GetDirectoryName(path));
 
                 lock (_lastExecutionResultSyncLock)
                 {
@@ -552,7 +552,7 @@ namespace MediaBrowser.Common.Implementations.ScheduledTasks
         {
             var path = GetConfigurationFilePath();
 
-            Directory.CreateDirectory(Path.GetDirectoryName(path));
+			_fileSystem.CreateDirectory(Path.GetDirectoryName(path));
 
             JsonSerializer.SerializeToFile(triggers.Select(ScheduledTaskHelpers.GetTriggerInfo), path);
         }

+ 3 - 3
MediaBrowser.Common.Implementations/ScheduledTasks/Tasks/DeleteCacheFileTask.cs

@@ -95,7 +95,7 @@ namespace MediaBrowser.Common.Implementations.ScheduledTasks.Tasks
         /// <param name="progress">The progress.</param>
         private void DeleteCacheFilesFromDirectory(CancellationToken cancellationToken, string directory, DateTime minDateModified, IProgress<double> progress)
         {
-            var filesToDelete = new DirectoryInfo(directory).EnumerateFiles("*", SearchOption.AllDirectories)
+			var filesToDelete = _fileSystem.GetFiles(directory, true)
                 .Where(f => _fileSystem.GetLastWriteTimeUtc(f) < minDateModified)
                 .ToList();
 
@@ -120,14 +120,14 @@ namespace MediaBrowser.Common.Implementations.ScheduledTasks.Tasks
             progress.Report(100);
         }
 
-        private static void DeleteEmptyFolders(string parent)
+        private void DeleteEmptyFolders(string parent)
         {
             foreach (var directory in Directory.GetDirectories(parent))
             {
                 DeleteEmptyFolders(directory);
                 if (!Directory.EnumerateFileSystemEntries(directory).Any())
                 {
-                    Directory.Delete(directory, false);
+					_fileSystem.DeleteDirectory(directory, false);
                 }
             }
         }

+ 1 - 1
MediaBrowser.Common.Implementations/ScheduledTasks/Tasks/DeleteLogFileTask.cs

@@ -58,7 +58,7 @@ namespace MediaBrowser.Common.Implementations.ScheduledTasks.Tasks
             // Delete log files more than n days old
             var minDateModified = DateTime.UtcNow.AddDays(-(ConfigurationManager.CommonConfiguration.LogFileRetentionDays));
 
-            var filesToDelete = new DirectoryInfo(ConfigurationManager.CommonApplicationPaths.LogDirectoryPath).EnumerateFileSystemInfos("*", SearchOption.AllDirectories)
+			var filesToDelete = _fileSystem.GetFiles(ConfigurationManager.CommonApplicationPaths.LogDirectoryPath, true)
                           .Where(f => _fileSystem.GetLastWriteTimeUtc(f) < minDateModified)
                           .ToList();
 

+ 5 - 5
MediaBrowser.Common.Implementations/Security/MBLicenseFile.cs

@@ -99,15 +99,15 @@ namespace MediaBrowser.Common.Implementations.Security
             {
                 try
                 {
-                    contents = File.ReadAllLines(licenseFile);
+					contents = _fileSystem.ReadAllLines(licenseFile);
                 }
                 catch (DirectoryNotFoundException)
                 {
-                    (File.Create(licenseFile)).Close();
+					(_fileSystem.CreateFile(licenseFile)).Close();
                 }
                 catch (FileNotFoundException)
                 {
-                    (File.Create(licenseFile)).Close();
+					(_fileSystem.CreateFile(licenseFile)).Close();
                 }
             }
             if (contents != null && contents.Length > 0)
@@ -150,8 +150,8 @@ namespace MediaBrowser.Common.Implementations.Security
             }
 
             var licenseFile = Filename;
-            Directory.CreateDirectory(Path.GetDirectoryName(licenseFile));
-            lock (_fileLock) File.WriteAllLines(licenseFile, lines);
+			_fileSystem.CreateDirectory(Path.GetDirectoryName(licenseFile));
+			lock (_fileLock) _fileSystem.WriteAllLines(licenseFile, lines);
         }
     }
 }

+ 1 - 1
MediaBrowser.Common.Implementations/Serialization/JsonSerializer.cs

@@ -53,7 +53,7 @@ namespace MediaBrowser.Common.Implementations.Serialization
                 throw new ArgumentNullException("file");
             }
 
-            using (Stream stream = File.Open(file, FileMode.Create))
+			using (Stream stream = _fileSystem.GetFileStream(file, FileMode.Create, FileAccess.Write, FileShare.Read))
             {
                 SerializeToStream(obj, stream);
             }

+ 9 - 1
MediaBrowser.Common.Implementations/Serialization/XmlSerializer.cs

@@ -3,6 +3,7 @@ using System;
 using System.Collections.Concurrent;
 using System.IO;
 using System.Xml;
+using MediaBrowser.Common.IO;
 
 namespace MediaBrowser.Common.Implementations.Serialization
 {
@@ -11,6 +12,13 @@ namespace MediaBrowser.Common.Implementations.Serialization
     /// </summary>
     public class XmlSerializer : IXmlSerializer
     {
+		private IFileSystem _fileSystem;
+
+		public XmlSerializer(IFileSystem fileSystem) 
+		{
+			_fileSystem = fileSystem;
+		}
+
         // Need to cache these
         // http://dotnetcodebox.blogspot.com/2013/01/xmlserializer-class-may-result-in.html
         private readonly ConcurrentDictionary<string, System.Xml.Serialization.XmlSerializer> _serializers =
@@ -83,7 +91,7 @@ namespace MediaBrowser.Common.Implementations.Serialization
         /// <returns>System.Object.</returns>
         public object DeserializeFromFile(Type type, string file)
         {
-            using (var stream = File.OpenRead(file))
+            using (var stream = _fileSystem.OpenRead(file))
             {
                 return DeserializeFromStream(type, stream);
             }

+ 4 - 4
MediaBrowser.Common.Implementations/Updates/InstallationManager.cs

@@ -553,7 +553,7 @@ namespace MediaBrowser.Common.Implementations.Updates
             if (packageChecksum != Guid.Empty) // support for legacy uploads for now
             {
                 using (var crypto = new MD5CryptoServiceProvider())
-                using (var stream = new BufferedStream(File.OpenRead(tempFile), 100000))
+				using (var stream = new BufferedStream(_fileSystem.OpenRead(tempFile), 100000))
                 {
                     var check = Guid.Parse(BitConverter.ToString(crypto.ComputeHash(stream)).Replace("-", String.Empty));
                     if (check != packageChecksum)
@@ -568,12 +568,12 @@ namespace MediaBrowser.Common.Implementations.Updates
             // Success - move it to the real target 
             try
             {
-                Directory.CreateDirectory(Path.GetDirectoryName(target));
-                File.Copy(tempFile, target, true);
+				_fileSystem.CreateDirectory(Path.GetDirectoryName(target));
+				_fileSystem.CopyFile(tempFile, target, true);
                 //If it is an archive - write out a version file so we know what it is
                 if (isArchive)
                 {
-                    File.WriteAllText(target + ".ver", package.versionStr);
+					_fileSystem.WriteAllText(target + ".ver", package.versionStr);
                 }
             }
             catch (IOException e)

+ 41 - 16
MediaBrowser.Common/IO/IFileSystem.cs

@@ -1,5 +1,7 @@
 using System;
 using System.IO;
+using System.Collections.Generic;
+using System.Text;
 
 namespace MediaBrowser.Common.IO
 {
@@ -73,7 +75,9 @@ namespace MediaBrowser.Common.IO
         /// <param name="share">The share.</param>
         /// <param name="isAsync">if set to <c>true</c> [is asynchronous].</param>
         /// <returns>FileStream.</returns>
-        FileStream GetFileStream(string path, FileMode mode, FileAccess access, FileShare share, bool isAsync = false);
+        Stream GetFileStream(string path, FileMode mode, FileAccess access, FileShare share, bool isAsync = false);
+
+		Stream OpenRead(String path);
 
         /// <summary>
         /// Swaps the files.
@@ -134,21 +138,6 @@ namespace MediaBrowser.Common.IO
         /// <returns><c>true</c> if [is path file] [the specified path]; otherwise, <c>false</c>.</returns>
         bool IsPathFile(string path);
 
-        /// <summary>
-        /// Deletes the file.
-        /// </summary>
-        /// <param name="path">The path.</param>
-        /// <param name="sendToRecycleBin">if set to <c>true</c> [send to recycle bin].</param>
-        void DeleteFile(string path, bool sendToRecycleBin);
-
-        /// <summary>
-        /// Deletes the directory.
-        /// </summary>
-        /// <param name="path">The path.</param>
-        /// <param name="recursive">if set to <c>true</c> [recursive].</param>
-        /// <param name="sendToRecycleBin">if set to <c>true</c> [send to recycle bin].</param>
-        void DeleteDirectory(string path, bool recursive, bool sendToRecycleBin);
-
         /// <summary>
         /// Deletes the file.
         /// </summary>
@@ -161,5 +150,41 @@ namespace MediaBrowser.Common.IO
         /// <param name="path">The path.</param>
         /// <param name="recursive">if set to <c>true</c> [recursive].</param>
         void DeleteDirectory(string path, bool recursive);
+
+		IEnumerable<DirectoryInfo> GetDirectories(string path, bool recursive = false);
+
+		IEnumerable<FileInfo> GetFiles(string path, bool recursive = false);
+
+		IEnumerable<FileSystemInfo> GetFileSystemEntries(string path, bool recursive = false);
+
+		void CreateDirectory(string path);
+
+		void CopyFile(string source, string target, bool overwrite);
+
+		void MoveFile(string source, string target);
+
+		void MoveDirectory(string source, string target);
+
+		bool DirectoryExists(string path);
+
+		bool FileExists(string path);
+
+		string ReadAllText(string path, Encoding encoding);
+
+		string ReadAllText(string path);
+
+		byte[] ReadAllBytes(string path);
+
+		void WriteAllBytes(string path, byte[] bytes);
+
+		void WriteAllText(string path, string text, Encoding encoding);
+
+		void WriteAllText(string path, string text);
+
+		void CreateFile(string path);
+
+		void WriteAllLines(string path, string[] lines);
+
+		string[] ReadAllLines(string path);
     }
 }

+ 5 - 5
MediaBrowser.Controller/Entities/BaseItem.cs

@@ -683,7 +683,7 @@ namespace MediaBrowser.Controller.Entities
         {
             var files = fileSystemChildren.OfType<DirectoryInfo>()
                 .Where(i => string.Equals(i.Name, ThemeSongsFolderName, StringComparison.OrdinalIgnoreCase))
-                .SelectMany(i => i.EnumerateFiles("*", SearchOption.TopDirectoryOnly))
+				.SelectMany(i => directoryService.GetFiles(i.FullName))
                 .ToList();
 
             // Support plex/xbmc convention
@@ -719,7 +719,7 @@ namespace MediaBrowser.Controller.Entities
         {
             var files = fileSystemChildren.OfType<DirectoryInfo>()
                 .Where(i => string.Equals(i.Name, ThemeVideosFolderName, StringComparison.OrdinalIgnoreCase))
-                .SelectMany(i => i.EnumerateFiles("*", SearchOption.TopDirectoryOnly));
+				.SelectMany(i => directoryService.GetFiles(i.FullName));
 
             return LibraryManager.ResolvePaths(files, directoryService, null)
                 .OfType<Video>()
@@ -743,7 +743,7 @@ namespace MediaBrowser.Controller.Entities
 
         public Task RefreshMetadata(CancellationToken cancellationToken)
         {
-            return RefreshMetadata(new MetadataRefreshOptions(new DirectoryService()), cancellationToken);
+            return RefreshMetadata(new MetadataRefreshOptions(new DirectoryService(FileSystem)), cancellationToken);
         }
 
         /// <summary>
@@ -1396,7 +1396,7 @@ namespace MediaBrowser.Controller.Entities
         /// <returns>Task.</returns>
         public virtual Task ChangedExternally()
         {
-            ProviderManager.QueueRefresh(Id, new MetadataRefreshOptions());
+			ProviderManager.QueueRefresh(Id, new MetadataRefreshOptions(FileSystem));
             return Task.FromResult(true);
         }
 
@@ -1613,7 +1613,7 @@ namespace MediaBrowser.Controller.Entities
                 var newImagePaths = images.Select(i => i.FullName).ToList();
 
                 var deleted = existingImages
-                    .Where(i => !newImagePaths.Contains(i.Path, StringComparer.OrdinalIgnoreCase) && !File.Exists(i.Path))
+					.Where(i => !newImagePaths.Contains(i.Path, StringComparer.OrdinalIgnoreCase) && !FileSystem.FileExists(i.Path))
                     .ToList();
 
                 ImageInfos = ImageInfos.Except(deleted).ToList();

+ 3 - 3
MediaBrowser.Controller/Entities/Folder.cs

@@ -371,7 +371,7 @@ namespace MediaBrowser.Controller.Entities
 
         public Task ValidateChildren(IProgress<double> progress, CancellationToken cancellationToken)
         {
-            return ValidateChildren(progress, cancellationToken, new MetadataRefreshOptions(new DirectoryService()));
+			return ValidateChildren(progress, cancellationToken, new MetadataRefreshOptions(new DirectoryService(FileSystem)));
         }
 
         /// <summary>
@@ -693,7 +693,7 @@ namespace MediaBrowser.Controller.Entities
         /// <returns><c>true</c> if the specified path is offline; otherwise, <c>false</c>.</returns>
         private bool IsPathOffline(string path)
         {
-            if (File.Exists(path))
+			if (FileSystem.FileExists(path))
             {
                 return false;
             }
@@ -703,7 +703,7 @@ namespace MediaBrowser.Controller.Entities
             // Depending on whether the path is local or unc, it may return either null or '\' at the top
             while (!string.IsNullOrEmpty(path) && path.Length > 1)
             {
-                if (Directory.Exists(path))
+				if (FileSystem.DirectoryExists(path))
                 {
                     return false;
                 }

+ 5 - 5
MediaBrowser.Controller/Entities/User.cs

@@ -177,24 +177,24 @@ namespace MediaBrowser.Controller.Entities
                 var oldConfigurationDirectory = ConfigurationDirectoryPath;
 
                 // Exceptions will be thrown if these paths already exist
-                if (Directory.Exists(newConfigDirectory))
+				if (FileSystem.DirectoryExists(newConfigDirectory))
                 {
                     FileSystem.DeleteDirectory(newConfigDirectory, true);
                 }
 
-                if (Directory.Exists(oldConfigurationDirectory))
+				if (FileSystem.DirectoryExists(oldConfigurationDirectory))
                 {
-                    Directory.Move(oldConfigurationDirectory, newConfigDirectory);
+					FileSystem.MoveDirectory(oldConfigurationDirectory, newConfigDirectory);
                 }
                 else
                 {
-                    Directory.CreateDirectory(newConfigDirectory);
+					FileSystem.CreateDirectory(newConfigDirectory);
                 }
             }
 
             Name = newName;
 
-            return RefreshMetadata(new MetadataRefreshOptions(new DirectoryService())
+			return RefreshMetadata(new MetadataRefreshOptions(new DirectoryService(FileSystem))
             {
                 ReplaceAllMetadata = true,
                 ImageRefreshMode = ImageRefreshMode.FullRefresh,

+ 5 - 11
MediaBrowser.Controller/MediaBrowser.Controller.csproj

@@ -11,10 +11,10 @@
     <AssemblyName>MediaBrowser.Controller</AssemblyName>
     <FileAlignment>512</FileAlignment>
     <SolutionDir Condition="$(SolutionDir) == '' Or $(SolutionDir) == '*Undefined*'">..\</SolutionDir>
-    <ProductVersion>10.0.0</ProductVersion>
-    <SchemaVersion>2.0</SchemaVersion>
     <TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
     <RestorePackages>true</RestorePackages>
+    <ReleaseVersion>
+    </ReleaseVersion>
   </PropertyGroup>
   <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
     <DebugSymbols>true</DebugSymbols>
@@ -24,7 +24,6 @@
     <DefineConstants>DEBUG;TRACE</DefineConstants>
     <ErrorReport>prompt</ErrorReport>
     <WarningLevel>4</WarningLevel>
-    <TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
   </PropertyGroup>
   <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
     <DebugType>none</DebugType>
@@ -33,7 +32,6 @@
     <DefineConstants>TRACE</DefineConstants>
     <ErrorReport>prompt</ErrorReport>
     <WarningLevel>4</WarningLevel>
-    <TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
   </PropertyGroup>
   <PropertyGroup>
     <RunPostBuildEvent>Always</RunPostBuildEvent>
@@ -45,16 +43,11 @@
     <DefineConstants>TRACE</DefineConstants>
     <ErrorReport>prompt</ErrorReport>
     <WarningLevel>4</WarningLevel>
-    <TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
   </PropertyGroup>
   <ItemGroup>
     <Reference Include="Interfaces.IO">
       <HintPath>..\packages\Interfaces.IO.1.0.0.5\lib\portable-net45+sl4+wp71+win8+wpa81\Interfaces.IO.dll</HintPath>
     </Reference>
-    <Reference Include="MoreLinq, Version=1.1.18418.0, Culture=neutral, PublicKeyToken=384d532d7e88985d, processorArchitecture=MSIL">
-      <SpecificVersion>False</SpecificVersion>
-      <HintPath>..\packages\morelinq.1.1.1\lib\net35\MoreLinq.dll</HintPath>
-    </Reference>
     <Reference Include="System" />
     <Reference Include="System.Core" />
     <Reference Include="System.Data" />
@@ -66,6 +59,9 @@
     <Reference Include="ServiceStack.Interfaces">
       <HintPath>..\ThirdParty\ServiceStack\ServiceStack.Interfaces.dll</HintPath>
     </Reference>
+    <Reference Include="MoreLinq">
+      <HintPath>..\packages\morelinq.1.1.1\lib\net35\MoreLinq.dll</HintPath>
+    </Reference>
   </ItemGroup>
   <ItemGroup>
     <Compile Include="..\SharedVersion.cs">
@@ -418,8 +414,6 @@
     <Compile Include="Sync\ISyncRepository.cs" />
     <Compile Include="Sync\SyncedFileInfo.cs" />
     <Compile Include="Sync\SyncedItemProgress.cs" />
-    <Compile Include="Themes\IAppThemeManager.cs" />
-    <Compile Include="Themes\InternalThemeImage.cs" />
     <Compile Include="TV\ITVSeriesManager.cs" />
   </ItemGroup>
   <ItemGroup>

+ 7 - 5
MediaBrowser.Controller/Providers/DirectoryService.cs

@@ -4,23 +4,26 @@ using System.Collections.Concurrent;
 using System.Collections.Generic;
 using System.IO;
 using System.Linq;
+using MediaBrowser.Common.IO;
 
 namespace MediaBrowser.Controller.Providers
 {
     public class DirectoryService : IDirectoryService
     {
         private readonly ILogger _logger;
+		private readonly IFileSystem _fileSystem;
 
         private readonly ConcurrentDictionary<string, Dictionary<string,FileSystemInfo>> _cache =
             new ConcurrentDictionary<string, Dictionary<string, FileSystemInfo>>(StringComparer.OrdinalIgnoreCase);
 
-        public DirectoryService(ILogger logger)
+		public DirectoryService(ILogger logger, IFileSystem fileSystem)
         {
             _logger = logger;
+			_fileSystem = fileSystem;
         }
 
-        public DirectoryService()
-            : this(new NullLogger())
+		public DirectoryService(IFileSystem fileSystem)
+            : this(new NullLogger(), fileSystem)
         {
         }
 
@@ -59,8 +62,7 @@ namespace MediaBrowser.Controller.Providers
                 try
                 {
                     // using EnumerateFileSystemInfos doesn't handle reparse points (symlinks)
-                    var list = new DirectoryInfo(path).EnumerateDirectories("*", SearchOption.TopDirectoryOnly)
-                        .Concat<FileSystemInfo>(new DirectoryInfo(path).EnumerateFiles("*", SearchOption.TopDirectoryOnly));
+					var list = _fileSystem.GetFileSystemEntries(path);
 
                     // Seeing dupes on some users file system for some reason
                     foreach (var item in list)

+ 3 - 2
MediaBrowser.Controller/Providers/MetadataRefreshOptions.cs

@@ -1,4 +1,5 @@
 using System.Linq;
+using MediaBrowser.Common.IO;
 
 namespace MediaBrowser.Controller.Providers
 {
@@ -15,8 +16,8 @@ namespace MediaBrowser.Controller.Providers
 
         public bool ForceSave { get; set; }
 
-        public MetadataRefreshOptions()
-            : this(new DirectoryService())
+        public MetadataRefreshOptions(IFileSystem fileSystem)
+			: this(new DirectoryService(fileSystem))
         {
         }
 

+ 0 - 38
MediaBrowser.Controller/Themes/IAppThemeManager.cs

@@ -1,38 +0,0 @@
-using MediaBrowser.Model.Themes;
-using System.Collections.Generic;
-
-namespace MediaBrowser.Controller.Themes
-{
-    public interface IAppThemeManager
-    {
-        /// <summary>
-        /// Gets the themes.
-        /// </summary>
-        /// <param name="applicationName">Name of the application.</param>
-        /// <returns>IEnumerable{AppThemeInfo}.</returns>
-        IEnumerable<AppThemeInfo> GetThemes(string applicationName);
-
-        /// <summary>
-        /// Gets the theme.
-        /// </summary>
-        /// <param name="applicationName">Name of the application.</param>
-        /// <param name="name">The name.</param>
-        /// <returns>AppTheme.</returns>
-        AppTheme GetTheme(string applicationName, string name);
-
-        /// <summary>
-        /// Saves the theme.
-        /// </summary>
-        /// <param name="theme">The theme.</param>
-        void SaveTheme(AppTheme theme);
-
-        /// <summary>
-        /// Gets the image image information.
-        /// </summary>
-        /// <param name="applicationName">Name of the application.</param>
-        /// <param name="themeName">Name of the theme.</param>
-        /// <param name="imageName">Name of the image.</param>
-        /// <returns>InternalThemeImage.</returns>
-        InternalThemeImage GetImageImageInfo(string applicationName, string themeName, string imageName);
-    }
-}

+ 0 - 31
MediaBrowser.Controller/Themes/InternalThemeImage.cs

@@ -1,31 +0,0 @@
-using System;
-
-namespace MediaBrowser.Controller.Themes
-{
-    public class InternalThemeImage
-    {
-        /// <summary>
-        /// Gets or sets the name.
-        /// </summary>
-        /// <value>The name.</value>
-        public string Name { get; set; }
-
-        /// <summary>
-        /// Gets or sets the cache tag.
-        /// </summary>
-        /// <value>The cache tag.</value>
-        public string CacheTag { get; set; }
-
-        /// <summary>
-        /// Gets or sets the path.
-        /// </summary>
-        /// <value>The path.</value>
-        public string Path { get; set; }
-
-        /// <summary>
-        /// Gets or sets the date modified.
-        /// </summary>
-        /// <value>The date modified.</value>
-        public DateTime DateModified { get; set; }
-    }
-}

+ 4 - 6
MediaBrowser.Dlna/DlnaManager.cs

@@ -279,8 +279,7 @@ namespace MediaBrowser.Dlna
         {
             try
             {
-                return new DirectoryInfo(path)
-                    .EnumerateFiles("*", SearchOption.TopDirectoryOnly)
+				return _fileSystem.GetFiles(path)
                     .Where(i => string.Equals(i.Extension, ".xml", StringComparison.OrdinalIgnoreCase))
                     .Select(i => ParseProfileXmlFile(i.FullName, type))
                     .Where(i => i != null)
@@ -342,8 +341,7 @@ namespace MediaBrowser.Dlna
         {
             try
             {
-                return new DirectoryInfo(path)
-                    .EnumerateFiles("*", SearchOption.TopDirectoryOnly)
+				return _fileSystem.GetFiles(path)
                     .Where(i => string.Equals(i.Extension, ".xml", StringComparison.OrdinalIgnoreCase))
                     .Select(i => new InternalProfileInfo
                     {
@@ -385,7 +383,7 @@ namespace MediaBrowser.Dlna
 
                     if (!fileInfo.Exists || fileInfo.Length != stream.Length)
                     {
-                        Directory.CreateDirectory(systemProfilesPath);
+						_fileSystem.CreateDirectory(systemProfilesPath);
 
                         using (var fileStream = _fileSystem.GetFileStream(path, FileMode.Create, FileAccess.Write, FileShare.Read))
                         {
@@ -396,7 +394,7 @@ namespace MediaBrowser.Dlna
             }
 
             // Not necessary, but just to make it easy to find
-            Directory.CreateDirectory(UserProfilesPath);
+			_fileSystem.CreateDirectory(UserProfilesPath);
         }
 
         public void DeleteProfile(string id)

+ 4 - 3
MediaBrowser.LocalMetadata/Savers/XmlSaverHelpers.cs

@@ -14,6 +14,7 @@ using System.Linq;
 using System.Security;
 using System.Text;
 using System.Xml;
+using MediaBrowser.Common.IO;
 
 namespace MediaBrowser.LocalMetadata.Savers
 {
@@ -130,9 +131,9 @@ namespace MediaBrowser.LocalMetadata.Savers
         /// <param name="xml">The XML.</param>
         /// <param name="path">The path.</param>
         /// <param name="xmlTagsUsed">The XML tags used.</param>
-        public static void Save(StringBuilder xml, string path, List<string> xmlTagsUsed, IServerConfigurationManager config)
+        public static void Save(StringBuilder xml, string path, List<string> xmlTagsUsed, IServerConfigurationManager config, IFileSystem fileSystem)
         {
-            if (File.Exists(path))
+			if (fileSystem.FileExists(path))
             {
                 var position = xml.ToString().LastIndexOf("</", StringComparison.OrdinalIgnoreCase);
                 xml.Insert(position, GetCustomTags(path, xmlTagsUsed));
@@ -144,7 +145,7 @@ namespace MediaBrowser.LocalMetadata.Savers
             //Add the new node to the document.
             xmlDocument.InsertBefore(xmlDocument.CreateXmlDeclaration("1.0", "UTF-8", "yes"), xmlDocument.DocumentElement);
 
-            Directory.CreateDirectory(Path.GetDirectoryName(path));
+			fileSystem.CreateDirectory(Path.GetDirectoryName(path));
 
             var wasHidden = false;
 

+ 1 - 1
MediaBrowser.MediaEncoding/Configuration/EncodingConfigurationFactory.cs

@@ -35,7 +35,7 @@ namespace MediaBrowser.MediaEncoding.Configuration
                 && !string.Equals(oldEncodingConfig.TranscodingTempPath ?? string.Empty, newPath))
             {
                 // Validate
-                if (!Directory.Exists(newPath))
+				if (!_fileSystem.DirectoryExists(newPath))
                 {
                     throw new DirectoryNotFoundException(string.Format("{0} does not exist.", newPath));
                 }

+ 3 - 3
MediaBrowser.MediaEncoding/Encoder/BaseEncoder.cs

@@ -65,7 +65,7 @@ namespace MediaBrowser.MediaEncoding.Encoder
                 .CreateJob(options, IsVideoEncoder, progress, cancellationToken).ConfigureAwait(false);
 
             encodingJob.OutputFilePath = GetOutputFilePath(encodingJob);
-            Directory.CreateDirectory(Path.GetDirectoryName(encodingJob.OutputFilePath));
+            FileSystem.CreateDirectory(Path.GetDirectoryName(encodingJob.OutputFilePath));
 
             encodingJob.ReadInputAtNativeFramerate = options.ReadInputAtNativeFramerate;
 
@@ -112,7 +112,7 @@ namespace MediaBrowser.MediaEncoding.Encoder
             Logger.Info(commandLineLogMessage);
 
             var logFilePath = Path.Combine(ConfigurationManager.CommonApplicationPaths.LogDirectoryPath, "transcode-" + Guid.NewGuid() + ".txt");
-            Directory.CreateDirectory(Path.GetDirectoryName(logFilePath));
+            FileSystem.CreateDirectory(Path.GetDirectoryName(logFilePath));
 
             // FFMpeg writes debug/error info to stderr. This is useful when debugging so let's put it in the log directory.
             encodingJob.LogFileStream = FileSystem.GetFileStream(logFilePath, FileMode.Create, FileAccess.Write, FileShare.Read, true);
@@ -144,7 +144,7 @@ namespace MediaBrowser.MediaEncoding.Encoder
             new JobLogger(Logger).StartStreamingLog(encodingJob, process.StandardError.BaseStream, encodingJob.LogFileStream);
 
             // Wait for the file to exist before proceeeding
-            while (!File.Exists(encodingJob.OutputFilePath) && !encodingJob.HasExited)
+			while (!FileSystem.FileExists(encodingJob.OutputFilePath) && !encodingJob.HasExited)
             {
                 await Task.Delay(100, cancellationToken).ConfigureAwait(false);
             }

+ 1 - 1
MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs

@@ -605,7 +605,7 @@ namespace MediaBrowser.MediaEncoding.Encoder
                 vf += string.Format(",scale=min(iw\\,{0}):trunc(ow/dar/2)*2", maxWidthParam);
             }
 
-            Directory.CreateDirectory(targetDirectory);
+			FileSystem.CreateDirectory(targetDirectory);
             var outputPath = Path.Combine(targetDirectory, filenamePrefix + "%05d.jpg");
 
             var args = string.Format("-i {0} -threads 1 -v quiet -vf \"{2}\" -f image2 \"{1}\"", inputArgument, outputPath, vf);

+ 10 - 10
MediaBrowser.MediaEncoding/Subtitles/SubtitleEncoder.cs

@@ -183,7 +183,7 @@ namespace MediaBrowser.MediaEncoding.Subtitles
                 }
             }
 
-            return File.OpenRead(path);
+            return _fileSystem.OpenRead(path);
         }
 
         private Encoding GetEncoding(string charset)
@@ -346,7 +346,7 @@ namespace MediaBrowser.MediaEncoding.Subtitles
 
             try
             {
-                if (!File.Exists(outputPath))
+				if (!_fileSystem.FileExists(outputPath))
                 {
                     await ConvertTextSubtitleToSrtInternal(inputPath, inputProtocol, outputPath, cancellationToken).ConfigureAwait(false);
                 }
@@ -383,7 +383,7 @@ namespace MediaBrowser.MediaEncoding.Subtitles
                 throw new ArgumentNullException("outputPath");
             }
 
-            Directory.CreateDirectory(Path.GetDirectoryName(outputPath));
+			_fileSystem.CreateDirectory(Path.GetDirectoryName(outputPath));
 
             var encodingParam = await GetSubtitleFileCharacterSet(inputPath, inputProtocol, cancellationToken).ConfigureAwait(false);
 
@@ -413,7 +413,7 @@ namespace MediaBrowser.MediaEncoding.Subtitles
             _logger.Debug("{0} {1}", process.StartInfo.FileName, process.StartInfo.Arguments);
 
             var logFilePath = Path.Combine(_appPaths.LogDirectoryPath, "ffmpeg-sub-convert-" + Guid.NewGuid() + ".txt");
-            Directory.CreateDirectory(Path.GetDirectoryName(logFilePath));
+			_fileSystem.CreateDirectory(Path.GetDirectoryName(logFilePath));
 
             var logFileStream = _fileSystem.GetFileStream(logFilePath, FileMode.Create, FileAccess.Write, FileShare.Read,
                 true);
@@ -466,7 +466,7 @@ namespace MediaBrowser.MediaEncoding.Subtitles
             {
                 failed = true;
 
-                if (File.Exists(outputPath))
+				if (_fileSystem.FileExists(outputPath))
                 {
                     try
                     {
@@ -479,7 +479,7 @@ namespace MediaBrowser.MediaEncoding.Subtitles
                     }
                 }
             }
-            else if (!File.Exists(outputPath))
+			else if (!_fileSystem.FileExists(outputPath))
             {
                 failed = true;
             }
@@ -515,7 +515,7 @@ namespace MediaBrowser.MediaEncoding.Subtitles
 
             try
             {
-                if (!File.Exists(outputPath))
+				if (!_fileSystem.FileExists(outputPath))
                 {
                     await ExtractTextSubtitleInternal(_mediaEncoder.GetInputArgument(inputFiles, protocol), subtitleStreamIndex,
                             outputCodec, outputPath, cancellationToken).ConfigureAwait(false);
@@ -540,7 +540,7 @@ namespace MediaBrowser.MediaEncoding.Subtitles
                 throw new ArgumentNullException("outputPath");
             }
 
-            Directory.CreateDirectory(Path.GetDirectoryName(outputPath));
+			_fileSystem.CreateDirectory(Path.GetDirectoryName(outputPath));
 
             var processArgs = string.Format("-i {0} -map 0:{1} -an -vn -c:s {2} \"{3}\"", inputPath,
                 subtitleStreamIndex, outputCodec, outputPath);
@@ -566,7 +566,7 @@ namespace MediaBrowser.MediaEncoding.Subtitles
             _logger.Info("{0} {1}", process.StartInfo.FileName, process.StartInfo.Arguments);
 
             var logFilePath = Path.Combine(_appPaths.LogDirectoryPath, "ffmpeg-sub-extract-" + Guid.NewGuid() + ".txt");
-            Directory.CreateDirectory(Path.GetDirectoryName(logFilePath));
+			_fileSystem.CreateDirectory(Path.GetDirectoryName(logFilePath));
 
             var logFileStream = _fileSystem.GetFileStream(logFilePath, FileMode.Create, FileAccess.Write, FileShare.Read,
                 true);
@@ -635,7 +635,7 @@ namespace MediaBrowser.MediaEncoding.Subtitles
                     _logger.ErrorException("Error deleting extracted subtitle {0}", ex, outputPath);
                 }
             }
-            else if (!File.Exists(outputPath))
+			else if (!_fileSystem.FileExists(outputPath))
             {
                 failed = true;
             }

+ 10 - 5
MediaBrowser.Model/Dlna/StreamBuilder.cs

@@ -725,7 +725,7 @@ namespace MediaBrowser.Model.Dlna
 
         public static SubtitleProfile GetSubtitleProfile(MediaStream subtitleStream, SubtitleProfile[] subtitleProfiles, EncodingContext context, PlayMethod playMethod)
         {
-            if (playMethod != PlayMethod.Transcode)
+			if (playMethod != PlayMethod.Transcode && !subtitleStream.IsExternal)
             {
                 // Look for supported embedded subs
                 foreach (SubtitleProfile profile in subtitleProfiles)
@@ -749,17 +749,22 @@ namespace MediaBrowser.Model.Dlna
 
             // Look for an external profile that matches the stream type (text/graphical)
             foreach (SubtitleProfile profile in subtitleProfiles)
-            {
-                bool requiresConversion = !StringHelper.EqualsIgnoreCase(subtitleStream.Codec, profile.Format);
+			{
+				if (profile.Method != SubtitleDeliveryMethod.External)
+				{
+					continue;
+				}
 
                 if (!profile.SupportsLanguage(subtitleStream.Language))
                 {
                     continue;
                 }
 
-                if (profile.Method == SubtitleDeliveryMethod.External && subtitleStream.IsTextSubtitleStream == MediaStream.IsTextFormat(profile.Format))
+                if (subtitleStream.IsTextSubtitleStream == MediaStream.IsTextFormat(profile.Format))
                 {
-                    if (subtitleStream.IsTextSubtitleStream || !requiresConversion)
+					bool requiresConversion = !StringHelper.EqualsIgnoreCase(subtitleStream.Codec, profile.Format);
+
+					if (subtitleStream.IsTextSubtitleStream || !requiresConversion)
                     {
                         if (subtitleStream.SupportsExternalStream)
                         {

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

@@ -12,10 +12,10 @@
     <FileAlignment>512</FileAlignment>
     <SolutionDir Condition="$(SolutionDir) == '' Or $(SolutionDir) == '*Undefined*'">..\</SolutionDir>
     <FodyPath>..\packages\Fody.1.19.1.0</FodyPath>
-    <ProductVersion>10.0.0</ProductVersion>
-    <SchemaVersion>2.0</SchemaVersion>
     <TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
     <RestorePackages>true</RestorePackages>
+    <ReleaseVersion>
+    </ReleaseVersion>
   </PropertyGroup>
   <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
     <DebugSymbols>true</DebugSymbols>
@@ -26,7 +26,6 @@
     <ErrorReport>prompt</ErrorReport>
     <WarningLevel>4</WarningLevel>
     <PlatformTarget>AnyCPU</PlatformTarget>
-    <TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
   </PropertyGroup>
   <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
     <DebugType>pdbonly</DebugType>
@@ -35,7 +34,6 @@
     <DefineConstants>TRACE</DefineConstants>
     <ErrorReport>prompt</ErrorReport>
     <WarningLevel>4</WarningLevel>
-    <TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
   </PropertyGroup>
   <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release Mono|AnyCPU' ">
     <DebugType>pdbonly</DebugType>
@@ -44,14 +42,10 @@
     <DefineConstants>TRACE</DefineConstants>
     <ErrorReport>prompt</ErrorReport>
     <WarningLevel>4</WarningLevel>
-    <TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
   </PropertyGroup>
   <PropertyGroup>
     <RunPostBuildEvent>Always</RunPostBuildEvent>
   </PropertyGroup>
-  <PropertyGroup>
-    <SignAssembly>false</SignAssembly>
-  </PropertyGroup>
   <PropertyGroup>
     <AssemblyOriginatorKeyFile>MediaBrowser.Model.snk</AssemblyOriginatorKeyFile>
   </PropertyGroup>
@@ -408,9 +402,6 @@
     <Compile Include="Sync\SyncTarget.cs" />
     <Compile Include="System\LogFile.cs" />
     <Compile Include="System\PublicSystemInfo.cs" />
-    <Compile Include="Themes\AppTheme.cs" />
-    <Compile Include="Themes\AppThemeInfo.cs" />
-    <Compile Include="Themes\ThemeImage.cs" />
     <Compile Include="Updates\CheckForUpdateResult.cs" />
     <Compile Include="Updates\PackageTargetSystem.cs" />
     <Compile Include="Updates\InstallationInfo.cs" />
@@ -465,7 +456,7 @@
       <HintPath>..\packages\PropertyChanged.Fody.1.41.0.0\Lib\NET35\PropertyChanged.dll</HintPath>
       <Private>False</Private>
     </Reference>
-    <Reference Include="System.XML" />
+    <Reference Include="System.Xml" />
   </ItemGroup>
   <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
   <PropertyGroup>

+ 0 - 22
MediaBrowser.Model/Themes/AppTheme.cs

@@ -1,22 +0,0 @@
-using System.Collections.Generic;
-
-namespace MediaBrowser.Model.Themes
-{
-    public class AppTheme
-    {
-        public string AppName { get; set; }
-
-        public string Name { get; set; }
-
-        public Dictionary<string, string> Options { get; set; }
-
-        public List<ThemeImage> Images { get; set; }
-
-        public AppTheme()
-        {
-            Options = new Dictionary<string, string>();
-
-            Images = new List<ThemeImage>();
-        }
-    }
-}

+ 0 - 9
MediaBrowser.Model/Themes/AppThemeInfo.cs

@@ -1,9 +0,0 @@
-namespace MediaBrowser.Model.Themes
-{
-    public class AppThemeInfo
-    {
-        public string AppName { get; set; }
-        
-        public string Name { get; set; }
-    }
-}

+ 0 - 18
MediaBrowser.Model/Themes/ThemeImage.cs

@@ -1,18 +0,0 @@
-
-namespace MediaBrowser.Model.Themes
-{
-    public class ThemeImage
-    {
-        /// <summary>
-        /// Gets or sets the name.
-        /// </summary>
-        /// <value>The name.</value>
-        public string Name { get; set; }
-
-        /// <summary>
-        /// Gets or sets the cache tag.
-        /// </summary>
-        /// <value>The cache tag.</value>
-        public string CacheTag { get; set; }
-    }
-}

+ 1 - 1
MediaBrowser.Providers/BoxSets/MovieDbBoxSetProvider.cs

@@ -156,7 +156,7 @@ namespace MediaBrowser.Providers.BoxSets
 
             var dataFilePath = GetDataFilePath(_config.ApplicationPaths, tmdbId, preferredMetadataLanguage);
 
-            Directory.CreateDirectory(Path.GetDirectoryName(dataFilePath));
+			_fileSystem.CreateDirectory(Path.GetDirectoryName(dataFilePath));
 
             _json.SerializeToFile(mainResult, dataFilePath);
         }

+ 2 - 2
MediaBrowser.Providers/ImagesByName/ImageUtils.cs

@@ -40,9 +40,9 @@ namespace MediaBrowser.Providers.ImagesByName
 
                     }).ConfigureAwait(false);
 
-                    Directory.CreateDirectory(Path.GetDirectoryName(file));
+					fileSystem.CreateDirectory(Path.GetDirectoryName(file));
 
-                    File.Copy(temp, file, true);
+					fileSystem.CopyFile(temp, file, true);
                 }
                 finally
                 {

+ 1 - 1
MediaBrowser.Providers/Manager/ImageSaver.cs

@@ -208,7 +208,7 @@ namespace MediaBrowser.Providers.Manager
 
             try
             {
-                Directory.CreateDirectory(Path.GetDirectoryName(path));
+				_fileSystem.CreateDirectory(Path.GetDirectoryName(path));
 
                 // If the file is currently hidden we'll have to remove that or the save will fail
                 var file = new FileInfo(path);

+ 2 - 2
MediaBrowser.Providers/Manager/ItemImageProvider.cs

@@ -358,7 +358,7 @@ namespace MediaBrowser.Providers.Manager
 
             if (deleted)
             {
-                item.ValidateImages(new DirectoryService(_logger));
+                item.ValidateImages(new DirectoryService(_logger, _fileSystem));
             }
         }
 
@@ -392,7 +392,7 @@ namespace MediaBrowser.Providers.Manager
                 else
                 {
                     var existing = item.GetImageInfo(type, 0);
-                    if (existing != null && !File.Exists(existing.Path))
+					if (existing != null && !_fileSystem.FileExists(existing.Path))
                     {
                         item.RemoveImage(existing);
                         changed = true;

+ 1 - 1
MediaBrowser.Providers/Manager/ProviderManager.cs

@@ -702,7 +702,7 @@ namespace MediaBrowser.Providers.Manager
 
                             // Manual edit occurred
                             // Even if save local is off, save locally anyway if the metadata file already exists
-                            if (fileSaver == null || !isEnabledFor || !File.Exists(fileSaver.GetSavePath(item)))
+							if (fileSaver == null || !isEnabledFor || !_fileSystem.FileExists(fileSaver.GetSavePath(item)))
                             {
                                 return false;
                             }

+ 3 - 3
MediaBrowser.Providers/MediaInfo/AudioImageProvider.cs

@@ -55,7 +55,7 @@ namespace MediaBrowser.Providers.MediaInfo
         {
             var path = GetAudioImagePath(item);
 
-            if (!File.Exists(path))
+			if (!_fileSystem.FileExists(path))
             {
                 var semaphore = GetLock(path);
 
@@ -65,9 +65,9 @@ namespace MediaBrowser.Providers.MediaInfo
                 try
                 {
                     // Check again in case it was saved while waiting for the lock
-                    if (!File.Exists(path))
+					if (!_fileSystem.FileExists(path))
                     {
-                        Directory.CreateDirectory(Path.GetDirectoryName(path));
+						_fileSystem.CreateDirectory(Path.GetDirectoryName(path));
 
                         using (var stream = await _mediaEncoder.ExtractAudioImage(item.Path, cancellationToken).ConfigureAwait(false))
                         {

+ 1 - 1
MediaBrowser.Providers/MediaInfo/FFProbeProvider.cs

@@ -148,7 +148,7 @@ namespace MediaBrowser.Providers.MediaInfo
 
         private void FetchShortcutInfo(Video video)
         {
-            video.ShortcutPath = File.ReadAllText(video.Path);
+			video.ShortcutPath = _fileSystem.ReadAllText(video.Path);
         }
 
         public Task<ItemUpdateType> FetchAudioInfo<T>(T item, CancellationToken cancellationToken)

+ 1 - 1
MediaBrowser.Providers/MediaInfo/FFProbeVideoInfo.cs

@@ -706,7 +706,7 @@ namespace MediaBrowser.Providers.MediaInfo
 
             // Try to eliminate menus and intros by skipping all files at the front of the list that are less than the minimum size
             // Once we reach a file that is at least the minimum, return all subsequent ones
-            var allVobs = new DirectoryInfo(root).EnumerateFiles("*", SearchOption.AllDirectories)
+			var allVobs = _fileSystem.GetFiles(root)
                 .Where(file => string.Equals(file.Extension, ".vob", StringComparison.OrdinalIgnoreCase))
                 .OrderBy(i => i.FullName)
                 .ToList();

+ 3 - 3
MediaBrowser.Providers/Movies/FanArtMovieUpdatesPostScanTask.cs

@@ -66,7 +66,7 @@ namespace MediaBrowser.Providers.Movies
 
             var path = FanartMovieImageProvider.GetMoviesDataPath(_config.CommonApplicationPaths);
 
-            Directory.CreateDirectory(path);
+			_fileSystem.CreateDirectory(path);
             
             var timestampFile = Path.Combine(path, "time.txt");
 
@@ -79,7 +79,7 @@ namespace MediaBrowser.Providers.Movies
             }
 
             // Find out the last time we queried for updates
-            var lastUpdateTime = timestampFileInfo.Exists ? File.ReadAllText(timestampFile, Encoding.UTF8) : string.Empty;
+			var lastUpdateTime = timestampFileInfo.Exists ? _fileSystem.ReadAllText(timestampFile, Encoding.UTF8) : string.Empty;
 
             var existingDirectories = Directory.EnumerateDirectories(path).Select(Path.GetFileName).ToList();
 
@@ -95,7 +95,7 @@ namespace MediaBrowser.Providers.Movies
 
             var newUpdateTime = Convert.ToInt64(DateTimeToUnixTimestamp(DateTime.UtcNow)).ToString(UsCulture);
             
-            File.WriteAllText(timestampFile, newUpdateTime, Encoding.UTF8);
+			_fileSystem.WriteAllText(timestampFile, newUpdateTime, Encoding.UTF8);
 
             progress.Report(100);
         }

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

@@ -293,7 +293,7 @@ namespace MediaBrowser.Providers.Movies
 
             var path = GetFanartJsonPath(id);
 
-            Directory.CreateDirectory(Path.GetDirectoryName(path));
+			_fileSystem.CreateDirectory(Path.GetDirectoryName(path));
 
             try
             {

+ 5 - 2
MediaBrowser.Providers/Movies/GenericMovieDbInfo.cs

@@ -13,6 +13,7 @@ using System.Linq;
 using System.Net;
 using System.Threading;
 using System.Threading.Tasks;
+using MediaBrowser.Common.IO;
 
 namespace MediaBrowser.Providers.Movies
 {
@@ -22,14 +23,16 @@ namespace MediaBrowser.Providers.Movies
         private readonly ILogger _logger;
         private readonly IJsonSerializer _jsonSerializer;
         private readonly ILibraryManager _libraryManager;
+		private readonly IFileSystem _fileSystem;
 
         private readonly CultureInfo _usCulture = new CultureInfo("en-US");
 
-        public GenericMovieDbInfo(ILogger logger, IJsonSerializer jsonSerializer, ILibraryManager libraryManager)
+		public GenericMovieDbInfo(ILogger logger, IJsonSerializer jsonSerializer, ILibraryManager libraryManager, IFileSystem fileSystem)
         {
             _logger = logger;
             _jsonSerializer = jsonSerializer;
             _libraryManager = libraryManager;
+			_fileSystem = fileSystem;
         }
 
         public async Task<MetadataResult<T>> GetMetadata(ItemLookupInfo itemId, CancellationToken cancellationToken)
@@ -88,7 +91,7 @@ namespace MediaBrowser.Providers.Movies
                 tmdbId = movieInfo.id.ToString(_usCulture);
 
                 dataFilePath = MovieDbProvider.Current.GetDataFilePath(tmdbId, language);
-                Directory.CreateDirectory(Path.GetDirectoryName(dataFilePath));
+				_fileSystem.CreateDirectory(Path.GetDirectoryName(dataFilePath));
                 _jsonSerializer.SerializeToFile(movieInfo, dataFilePath);
             }
 

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

@@ -115,7 +115,7 @@ namespace MediaBrowser.Providers.Movies
         public Task<MetadataResult<T>> GetItemMetadata<T>(ItemLookupInfo id, CancellationToken cancellationToken)
             where T : BaseItem, new()
         {
-            var movieDb = new GenericMovieDbInfo<T>(_logger, _jsonSerializer, _libraryManager);
+			var movieDb = new GenericMovieDbInfo<T>(_logger, _jsonSerializer, _libraryManager, _fileSystem);
 
             return movieDb.GetMetadata(id, cancellationToken);
         }
@@ -210,7 +210,7 @@ namespace MediaBrowser.Providers.Movies
 
             var dataFilePath = GetDataFilePath(id, preferredMetadataLanguage);
 
-            Directory.CreateDirectory(Path.GetDirectoryName(dataFilePath));
+			_fileSystem.CreateDirectory(Path.GetDirectoryName(dataFilePath));
 
             _jsonSerializer.SerializeToFile(mainResult, dataFilePath);
         }

+ 3 - 3
MediaBrowser.Providers/Movies/MovieUpdatesPrescanTask.cs

@@ -76,7 +76,7 @@ namespace MediaBrowser.Providers.Movies
 
             var path = MovieDbProvider.GetMoviesDataPath(_config.CommonApplicationPaths);
 
-            Directory.CreateDirectory(path);
+			_fileSystem.CreateDirectory(path);
 
             var timestampFile = Path.Combine(path, "time.txt");
 
@@ -89,7 +89,7 @@ namespace MediaBrowser.Providers.Movies
             }
 
             // Find out the last time we queried tvdb for updates
-            var lastUpdateTime = timestampFileInfo.Exists ? File.ReadAllText(timestampFile, Encoding.UTF8) : string.Empty;
+			var lastUpdateTime = timestampFileInfo.Exists ? _fileSystem.ReadAllText(timestampFile, Encoding.UTF8) : string.Empty;
 
             var existingDirectories = Directory.EnumerateDirectories(path).Select(Path.GetFileName).ToList();
 
@@ -117,7 +117,7 @@ namespace MediaBrowser.Providers.Movies
                 }
             }
 
-            File.WriteAllText(timestampFile, DateTime.UtcNow.Ticks.ToString(UsCulture), Encoding.UTF8);
+			_fileSystem.WriteAllText(timestampFile, DateTime.UtcNow.Ticks.ToString(UsCulture), Encoding.UTF8);
             progress.Report(100);
         }
 

+ 1 - 1
MediaBrowser.Providers/Music/AudioDbAlbumProvider.cs

@@ -125,7 +125,7 @@ namespace MediaBrowser.Providers.Music
 
             var path = GetAlbumInfoPath(_config.ApplicationPaths, musicBrainzReleaseGroupId);
 
-            Directory.CreateDirectory(Path.GetDirectoryName(path));
+			_fileSystem.CreateDirectory(Path.GetDirectoryName(path));
 
             using (var response = await _httpClient.Get(new HttpRequestOptions
             {

+ 1 - 1
MediaBrowser.Providers/Music/AudioDbArtistProvider.cs

@@ -121,7 +121,7 @@ namespace MediaBrowser.Providers.Music
 
             }).ConfigureAwait(false))
             {
-                Directory.CreateDirectory(Path.GetDirectoryName(path));
+				_fileSystem.CreateDirectory(Path.GetDirectoryName(path));
 
                 using (var xmlFileStream = _fileSystem.GetFileStream(path, FileMode.Create, FileAccess.Write, FileShare.Read, true))
                 {

+ 1 - 1
MediaBrowser.Providers/Music/FanArtArtistProvider.cs

@@ -433,7 +433,7 @@ namespace MediaBrowser.Providers.Music
 
             var xmlPath = GetArtistXmlPath(_config.ApplicationPaths, musicBrainzId);
 
-            Directory.CreateDirectory(Path.GetDirectoryName(xmlPath));
+			_fileSystem.CreateDirectory(Path.GetDirectoryName(xmlPath));
 
             using (var response = await _httpClient.Get(new HttpRequestOptions
             {

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

@@ -65,7 +65,7 @@ namespace MediaBrowser.Providers.Music
 
             var path = FanartArtistProvider.GetArtistDataPath(_config.CommonApplicationPaths);
 
-            Directory.CreateDirectory(path);
+			_fileSystem.CreateDirectory(path);
 
             var timestampFile = Path.Combine(path, "time.txt");
 
@@ -78,7 +78,7 @@ namespace MediaBrowser.Providers.Music
             }
 
             // Find out the last time we queried for updates
-            var lastUpdateTime = timestampFileInfo.Exists ? File.ReadAllText(timestampFile, Encoding.UTF8) : string.Empty;
+			var lastUpdateTime = timestampFileInfo.Exists ? _fileSystem.ReadAllText(timestampFile, Encoding.UTF8) : string.Empty;
 
             var existingDirectories = Directory.EnumerateDirectories(path).Select(Path.GetFileName).ToList();
 
@@ -94,7 +94,7 @@ namespace MediaBrowser.Providers.Music
 
             var newUpdateTime = Convert.ToInt64(DateTimeToUnixTimestamp(DateTime.UtcNow)).ToString(UsCulture);
             
-            File.WriteAllText(timestampFile, newUpdateTime, Encoding.UTF8);
+			_fileSystem.WriteAllText(timestampFile, newUpdateTime, Encoding.UTF8);
 
             progress.Report(100);
         }

+ 1 - 1
MediaBrowser.Providers/People/MovieDbPersonProvider.cs

@@ -200,7 +200,7 @@ namespace MediaBrowser.Providers.People
 
             }).ConfigureAwait(false))
             {
-                Directory.CreateDirectory(Path.GetDirectoryName(dataFilePath));
+				_fileSystem.CreateDirectory(Path.GetDirectoryName(dataFilePath));
 
                 using (var fs = _fileSystem.GetFileStream(dataFilePath, FileMode.Create, FileAccess.Write, FileShare.Read, true))
                 {

+ 1 - 1
MediaBrowser.Providers/Subtitles/SubtitleManager.cs

@@ -252,7 +252,7 @@ namespace MediaBrowser.Providers.Subtitles
                 _monitor.ReportFileSystemChangeComplete(path, false);
             }
 
-            return _libraryManager.GetItemById(itemId).RefreshMetadata(new MetadataRefreshOptions(new DirectoryService())
+            return _libraryManager.GetItemById(itemId).RefreshMetadata(new MetadataRefreshOptions(new DirectoryService(_fileSystem))
             {
                 ImageRefreshMode = ImageRefreshMode.ValidationOnly,
                 MetadataRefreshMode = MetadataRefreshMode.ValidationOnly

+ 2 - 2
MediaBrowser.Providers/TV/DummySeasonProvider.cs

@@ -38,7 +38,7 @@ namespace MediaBrowser.Providers.TV
 
             if (hasNewSeasons)
             {
-                var directoryService = new DirectoryService();
+                var directoryService = new DirectoryService(_fileSystem);
 
                 //await series.RefreshMetadata(new MetadataRefreshOptions(directoryService), cancellationToken).ConfigureAwait(false);
 
@@ -118,7 +118,7 @@ namespace MediaBrowser.Providers.TV
             
             await series.AddChild(season, cancellationToken).ConfigureAwait(false);
 
-            await season.RefreshMetadata(new MetadataRefreshOptions(), cancellationToken).ConfigureAwait(false);
+            await season.RefreshMetadata(new MetadataRefreshOptions(_fileSystem), cancellationToken).ConfigureAwait(false);
 
             return season;
         }

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

@@ -65,7 +65,7 @@ namespace MediaBrowser.Providers.TV
 
             var path = FanartSeriesProvider.GetSeriesDataPath(_config.CommonApplicationPaths);
 
-            Directory.CreateDirectory(path);
+			_fileSystem.CreateDirectory(path);
             
             var timestampFile = Path.Combine(path, "time.txt");
 
@@ -78,7 +78,7 @@ namespace MediaBrowser.Providers.TV
             }
 
             // Find out the last time we queried for updates
-            var lastUpdateTime = timestampFileInfo.Exists ? File.ReadAllText(timestampFile, Encoding.UTF8) : string.Empty;
+			var lastUpdateTime = timestampFileInfo.Exists ? _fileSystem.ReadAllText(timestampFile, Encoding.UTF8) : string.Empty;
 
             var existingDirectories = Directory.EnumerateDirectories(path).Select(Path.GetFileName).ToList();
 
@@ -94,7 +94,7 @@ namespace MediaBrowser.Providers.TV
 
             var newUpdateTime = Convert.ToInt64(DateTimeToUnixTimestamp(DateTime.UtcNow)).ToString(UsCulture);
             
-            File.WriteAllText(timestampFile, newUpdateTime, Encoding.UTF8);
+			_fileSystem.WriteAllText(timestampFile, newUpdateTime, Encoding.UTF8);
 
             progress.Report(100);
         }

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

@@ -309,7 +309,7 @@ namespace MediaBrowser.Providers.TV
 
             var path = GetFanartJsonPath(tvdbId);
 
-            Directory.CreateDirectory(Path.GetDirectoryName(path));
+			_fileSystem.CreateDirectory(Path.GetDirectoryName(path));
 
             try
             {

+ 2 - 2
MediaBrowser.Providers/TV/MissingEpisodeProvider.cs

@@ -119,7 +119,7 @@ namespace MediaBrowser.Providers.TV
             {
                 foreach (var series in group)
                 {
-                    var directoryService = new DirectoryService();
+                    var directoryService = new DirectoryService(_fileSystem);
 
                     await series.RefreshMetadata(new MetadataRefreshOptions(directoryService)
                     {
@@ -413,7 +413,7 @@ namespace MediaBrowser.Providers.TV
 
             await season.AddChild(episode, cancellationToken).ConfigureAwait(false);
 
-            await episode.RefreshMetadata(new MetadataRefreshOptions
+			await episode.RefreshMetadata(new MetadataRefreshOptions(_fileSystem)
             {
             }, cancellationToken).ConfigureAwait(false);
         }

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

@@ -206,7 +206,7 @@ namespace MediaBrowser.Providers.TV
 
             var dataFilePath = GetDataFilePath(id, seasonNumber, episodeNumber, preferredMetadataLanguage);
 
-            Directory.CreateDirectory(Path.GetDirectoryName(dataFilePath));
+			_fileSystem.CreateDirectory(Path.GetDirectoryName(dataFilePath));
             _jsonSerializer.SerializeToFile(mainResult, dataFilePath);
         }
 

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

@@ -190,7 +190,7 @@ namespace MediaBrowser.Providers.TV
 
             var dataFilePath = GetDataFilePath(id, seasonNumber, preferredMetadataLanguage);
 
-            Directory.CreateDirectory(Path.GetDirectoryName(dataFilePath));
+			_fileSystem.CreateDirectory(Path.GetDirectoryName(dataFilePath));
             _jsonSerializer.SerializeToFile(mainResult, dataFilePath);
         }
 

+ 2 - 2
MediaBrowser.Providers/TV/MovieDbSeriesProvider.cs

@@ -194,7 +194,7 @@ namespace MediaBrowser.Providers.TV
             tmdbId = seriesInfo.id.ToString(_usCulture);
 
             dataFilePath = GetDataFilePath(tmdbId, language);
-            Directory.CreateDirectory(Path.GetDirectoryName(dataFilePath));
+			_fileSystem.CreateDirectory(Path.GetDirectoryName(dataFilePath));
             _jsonSerializer.SerializeToFile(seriesInfo, dataFilePath);
 
             await EnsureSeriesInfo(tmdbId, language, cancellationToken).ConfigureAwait(false);
@@ -289,7 +289,7 @@ namespace MediaBrowser.Providers.TV
 
             var dataFilePath = GetDataFilePath(id, preferredMetadataLanguage);
 
-            Directory.CreateDirectory(Path.GetDirectoryName(dataFilePath));
+			_fileSystem.CreateDirectory(Path.GetDirectoryName(dataFilePath));
 
             _jsonSerializer.SerializeToFile(mainResult, dataFilePath);
         }

+ 4 - 4
MediaBrowser.Providers/TV/TvdbPrescanTask.cs

@@ -89,7 +89,7 @@ namespace MediaBrowser.Providers.TV
 
             var path = TvdbSeriesProvider.GetSeriesDataPath(_config.CommonApplicationPaths);
 
-            Directory.CreateDirectory(path);
+			_fileSystem.CreateDirectory(path);
 
             var timestampFile = Path.Combine(path, "time.txt");
 
@@ -102,7 +102,7 @@ namespace MediaBrowser.Providers.TV
             }
 
             // Find out the last time we queried tvdb for updates
-            var lastUpdateTime = timestampFileInfo.Exists ? File.ReadAllText(timestampFile, Encoding.UTF8) : string.Empty;
+			var lastUpdateTime = timestampFileInfo.Exists ? _fileSystem.ReadAllText(timestampFile, Encoding.UTF8) : string.Empty;
 
             string newUpdateTime;
 
@@ -157,7 +157,7 @@ namespace MediaBrowser.Providers.TV
                 await UpdateSeries(listToUpdate, path, nullableUpdateValue, progress, cancellationToken).ConfigureAwait(false);
             }
 
-            File.WriteAllText(timestampFile, newUpdateTime, Encoding.UTF8);
+			_fileSystem.WriteAllText(timestampFile, newUpdateTime, Encoding.UTF8);
             progress.Report(100);
         }
 
@@ -357,7 +357,7 @@ namespace MediaBrowser.Providers.TV
 
             seriesDataPath = Path.Combine(seriesDataPath, id);
 
-            Directory.CreateDirectory(seriesDataPath);
+			_fileSystem.CreateDirectory(seriesDataPath);
 
             return TvdbSeriesProvider.Current.DownloadSeriesZip(id, seriesDataPath, lastTvDbUpdateTime, preferredMetadataLanguage, cancellationToken);
         }

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

@@ -252,7 +252,7 @@ namespace MediaBrowser.Providers.TV
 
             if (!string.Equals(downloadLangaugeXmlFile, saveAsLanguageXmlFile, StringComparison.OrdinalIgnoreCase))
             {
-                File.Copy(downloadLangaugeXmlFile, saveAsLanguageXmlFile, true);
+				_fileSystem.CopyFile(downloadLangaugeXmlFile, saveAsLanguageXmlFile, true);
             }
 
             await ExtractEpisodes(seriesDataPath, downloadLangaugeXmlFile, lastTvDbUpdateTime).ConfigureAwait(false);
@@ -268,9 +268,9 @@ namespace MediaBrowser.Providers.TV
         {
             var seriesDataPath = GetSeriesDataPath(_config.ApplicationPaths, seriesId);
 
-            Directory.CreateDirectory(seriesDataPath);
+			_fileSystem.CreateDirectory(seriesDataPath);
 
-            var files = new DirectoryInfo(seriesDataPath).EnumerateFiles("*.xml", SearchOption.TopDirectoryOnly)
+			var files = _fileSystem.GetFiles(seriesDataPath)
                 .ToList();
 
             var seriesXmlFilename = preferredMetadataLanguage + ".xml";
@@ -1107,7 +1107,7 @@ namespace MediaBrowser.Providers.TV
             var file = Path.Combine(seriesDataPath, string.Format("episode-{0}-{1}.xml", seasonNumber, episodeNumber));
 
             // Only save the file if not already there, or if the episode has changed
-            if (hasEpisodeChanged || !File.Exists(file))
+			if (hasEpisodeChanged || !_fileSystem.FileExists(file))
             {
                 using (var writer = XmlWriter.Create(file, new XmlWriterSettings
                 {
@@ -1124,7 +1124,7 @@ namespace MediaBrowser.Providers.TV
                 file = Path.Combine(seriesDataPath, string.Format("episode-abs-{0}.xml", absoluteNumber));
 
                 // Only save the file if not already there, or if the episode has changed
-                if (hasEpisodeChanged || !File.Exists(file))
+				if (hasEpisodeChanged || !_fileSystem.FileExists(file))
                 {
                     using (var writer = XmlWriter.Create(file, new XmlWriterSettings
                     {
@@ -1167,8 +1167,7 @@ namespace MediaBrowser.Providers.TV
         {
             try
             {
-                foreach (var file in new DirectoryInfo(path)
-                    .EnumerateFiles("*.xml", SearchOption.AllDirectories)
+				foreach (var file in _fileSystem.GetFiles(path, true)
                     .ToList())
                 {
                     _fileSystem.DeleteFile(file.FullName);

+ 5 - 5
MediaBrowser.Server.Implementations/Channels/ChannelManager.cs

@@ -318,7 +318,7 @@ namespace MediaBrowser.Server.Implementations.Channels
 
             try
             {
-                var files = new DirectoryInfo(parentPath).EnumerateFiles("*", SearchOption.TopDirectoryOnly);
+				var files = _fileSystem.GetFiles(parentPath);
 
                 if (string.Equals(item.MediaType, MediaType.Video, StringComparison.OrdinalIgnoreCase))
                 {
@@ -411,7 +411,7 @@ namespace MediaBrowser.Server.Implementations.Channels
             {
                 _logger.Debug("Creating directory {0}", path);
 
-                Directory.CreateDirectory(path);
+				_fileSystem.CreateDirectory(path);
                 fileInfo = new DirectoryInfo(path);
 
                 if (!fileInfo.Exists)
@@ -1082,7 +1082,7 @@ namespace MediaBrowser.Server.Implementations.Channels
         {
             try
             {
-                Directory.CreateDirectory(Path.GetDirectoryName(path));
+				_fileSystem.CreateDirectory(Path.GetDirectoryName(path));
 
                 _jsonSerializer.SerializeToFile(result, path);
             }
@@ -1462,7 +1462,7 @@ namespace MediaBrowser.Server.Implementations.Channels
                 options.RequestHeaders[header.Key] = header.Value;
             }
 
-            Directory.CreateDirectory(Path.GetDirectoryName(destination));
+			_fileSystem.CreateDirectory(Path.GetDirectoryName(destination));
 
             // Determine output extension
             var response = await _httpClient.GetTempFileResponse(options).ConfigureAwait(false);
@@ -1500,7 +1500,7 @@ namespace MediaBrowser.Server.Implementations.Channels
                 throw new ApplicationException("Unexpected response type encountered: " + response.ContentType);
             }
 
-            File.Copy(response.TempFilePath, destination, true);
+			_fileSystem.CopyFile(response.TempFilePath, destination, true);
 
             try
             {

+ 1 - 1
MediaBrowser.Server.Implementations/Collections/CollectionManager.cs

@@ -70,7 +70,7 @@ namespace MediaBrowser.Server.Implementations.Collections
 
             try
             {
-                Directory.CreateDirectory(path);
+				_fileSystem.CreateDirectory(path);
 
                 var collection = new BoxSet
                 {

+ 1 - 1
MediaBrowser.Server.Implementations/Collections/CollectionsDynamicFolder.cs

@@ -17,7 +17,7 @@ namespace MediaBrowser.Server.Implementations.Collections
         {
             var path = Path.Combine(_appPaths.DataPath, "collections");
 
-            Directory.CreateDirectory(path);
+			_fileSystem.CreateDirectory(path);
 
             return new ManualCollectionsFolder
             {

+ 2 - 2
MediaBrowser.Server.Implementations/Configuration/ServerConfigurationManager.cs

@@ -198,7 +198,7 @@ namespace MediaBrowser.Server.Implementations.Configuration
                 && !string.Equals(Configuration.ItemsByNamePath ?? string.Empty, newPath))
             {
                 // Validate
-                if (!Directory.Exists(newPath))
+				if (!_fileSystem.DirectoryExists(newPath))
                 {
                     throw new DirectoryNotFoundException(string.Format("{0} does not exist.", newPath));
                 }
@@ -218,7 +218,7 @@ namespace MediaBrowser.Server.Implementations.Configuration
                 && !string.Equals(Configuration.MetadataPath ?? string.Empty, newPath))
             {
                 // Validate
-                if (!Directory.Exists(newPath))
+				if (!_fileSystem.DirectoryExists(newPath))
                 {
                     throw new DirectoryNotFoundException(string.Format("{0} does not exist.", newPath));
                 }

+ 3 - 3
MediaBrowser.Server.Implementations/Connect/ConnectEntryPoint.cs

@@ -90,8 +90,8 @@ namespace MediaBrowser.Server.Implementations.Connect
 
             try
             {
-                Directory.CreateDirectory(Path.GetDirectoryName(path));
-                File.WriteAllText(path, address, Encoding.UTF8);
+				_fileSystem.CreateDirectory(Path.GetDirectoryName(path));
+				_fileSystem.WriteAllText(path, address, Encoding.UTF8);
             }
             catch (Exception ex)
             {
@@ -105,7 +105,7 @@ namespace MediaBrowser.Server.Implementations.Connect
 
             try
             {
-                var endpoint = File.ReadAllText(path, Encoding.UTF8);
+				var endpoint = _fileSystem.ReadAllText(path, Encoding.UTF8);
 
                 if (IsValid(endpoint))
                 {

+ 3 - 3
MediaBrowser.Server.Implementations/Connect/ConnectManager.cs

@@ -315,7 +315,7 @@ namespace MediaBrowser.Server.Implementations.Connect
 
             try
             {
-                Directory.CreateDirectory(Path.GetDirectoryName(path));
+				_fileSystem.CreateDirectory(Path.GetDirectoryName(path));
 
                 var json = _json.SerializeToString(_data);
 
@@ -323,7 +323,7 @@ namespace MediaBrowser.Server.Implementations.Connect
 
                 lock (_dataFileLock)
                 {
-                    File.WriteAllText(path, encrypted, Encoding.UTF8);
+					_fileSystem.WriteAllText(path, encrypted, Encoding.UTF8);
                 }
             }
             catch (Exception ex)
@@ -340,7 +340,7 @@ namespace MediaBrowser.Server.Implementations.Connect
             {
                 lock (_dataFileLock)
                 {
-                    var encrypted = File.ReadAllText(path, Encoding.UTF8);
+					var encrypted = _fileSystem.ReadAllText(path, Encoding.UTF8);
 
                     var json = _encryption.DecryptString(encrypted);
 

+ 1 - 1
MediaBrowser.Server.Implementations/Devices/CameraUploadsFolder.cs

@@ -61,7 +61,7 @@ namespace MediaBrowser.Server.Implementations.Devices
         {
             var path = Path.Combine(_appPaths.DataPath, "camerauploads");
 
-            Directory.CreateDirectory(path);
+			_fileSystem.CreateDirectory(path);
 
             return new CameraUploadsFolder
             {

+ 1 - 1
MediaBrowser.Server.Implementations/Devices/DeviceManager.cs

@@ -157,7 +157,7 @@ namespace MediaBrowser.Server.Implementations.Devices
 
             _libraryMonitor.ReportFileSystemChangeBeginning(path);
 
-            Directory.CreateDirectory(Path.GetDirectoryName(path));
+			_fileSystem.CreateDirectory(Path.GetDirectoryName(path));
 
             try
             {

+ 2 - 2
MediaBrowser.Server.Implementations/Devices/DeviceRepository.cs

@@ -47,7 +47,7 @@ namespace MediaBrowser.Server.Implementations.Devices
         public Task SaveDevice(DeviceInfo device)
         {
             var path = Path.Combine(GetDevicePath(device.Id), "device.json");
-            Directory.CreateDirectory(Path.GetDirectoryName(path));
+			_fileSystem.CreateDirectory(Path.GetDirectoryName(path));
 
             lock (_syncLock)
             {
@@ -178,7 +178,7 @@ namespace MediaBrowser.Server.Implementations.Devices
         public void AddCameraUpload(string deviceId, LocalFileInfo file)
         {
             var path = Path.Combine(GetDevicePath(deviceId), "camerauploads.json");
-            Directory.CreateDirectory(Path.GetDirectoryName(path));
+			_fileSystem.CreateDirectory(Path.GetDirectoryName(path));
 
             lock (_syncLock)
             {

+ 2 - 2
MediaBrowser.Server.Implementations/EntryPoints/Notifications/RemoteNotifications.cs

@@ -60,7 +60,7 @@ namespace MediaBrowser.Server.Implementations.EntryPoints.Notifications
         {
             var dataPath = Path.Combine(_appPaths.DataPath, "remotenotifications.json");
 
-            var lastRunTime = File.Exists(dataPath) ? _fileSystem.GetLastWriteTimeUtc(dataPath) : DateTime.MinValue;
+			var lastRunTime = _fileSystem.FileExists(dataPath) ? _fileSystem.GetLastWriteTimeUtc(dataPath) : DateTime.MinValue;
 
             try
             {
@@ -88,7 +88,7 @@ namespace MediaBrowser.Server.Implementations.EntryPoints.Notifications
             {
                 var notifications = _json.DeserializeFromStream<RemoteNotification[]>(stream);
 
-                File.WriteAllText(dataPath, string.Empty);
+				_fileSystem.WriteAllText(dataPath, string.Empty);
 
                 await CreateNotifications(notifications, lastRunTime).ConfigureAwait(false);
             }

+ 6 - 6
MediaBrowser.Server.Implementations/FileOrganization/EpisodeFileOrganizer.cs

@@ -182,7 +182,7 @@ namespace MediaBrowser.Server.Implementations.FileOrganization
             _logger.Info("Sorting file {0} to new path {1}", sourcePath, newPath);
             result.TargetPath = newPath;
 
-            var fileExists = File.Exists(result.TargetPath);
+			var fileExists = _fileSystem.FileExists(result.TargetPath);
             var otherDuplicatePaths = GetOtherDuplicatePaths(result.TargetPath, series, seasonNumber, episodeNumber, endingEpiosdeNumber);
 
             if (!overwriteExisting)
@@ -272,7 +272,7 @@ namespace MediaBrowser.Server.Implementations.FileOrganization
 
                     var destination = Path.Combine(directory, filename);
 
-                    File.Move(file, destination);
+					_fileSystem.MoveFile(file, destination);
                 }
             }
         }
@@ -332,19 +332,19 @@ namespace MediaBrowser.Server.Implementations.FileOrganization
         {
             _libraryMonitor.ReportFileSystemChangeBeginning(result.TargetPath);
 
-            Directory.CreateDirectory(Path.GetDirectoryName(result.TargetPath));
+			_fileSystem.CreateDirectory(Path.GetDirectoryName(result.TargetPath));
 
-            var targetAlreadyExists = File.Exists(result.TargetPath);
+			var targetAlreadyExists = _fileSystem.FileExists(result.TargetPath);
 
             try
             {
                 if (targetAlreadyExists || options.CopyOriginalFile)
                 {
-                    File.Copy(result.OriginalPath, result.TargetPath, true);
+					_fileSystem.CopyFile(result.OriginalPath, result.TargetPath, true);
                 }
                 else
                 {
-                    File.Move(result.OriginalPath, result.TargetPath);
+					_fileSystem.MoveFile(result.OriginalPath, result.TargetPath);
                 }
 
                 result.Status = FileSortingStatus.Success;

+ 3 - 5
MediaBrowser.Server.Implementations/FileOrganization/TvFolderOrganizer.cs

@@ -132,8 +132,7 @@ namespace MediaBrowser.Server.Implementations.FileOrganization
         {
             try
             {
-                return new DirectoryInfo(path)
-                    .EnumerateFiles("*", SearchOption.AllDirectories)
+				return _fileSystem.GetFiles(path, true)
                     .ToList();
             }
             catch (IOException ex)
@@ -151,8 +150,7 @@ namespace MediaBrowser.Server.Implementations.FileOrganization
         /// <param name="extensions">The extensions.</param>
         private void DeleteLeftOverFiles(string path, IEnumerable<string> extensions)
         {
-            var eligibleFiles = new DirectoryInfo(path)
-                .EnumerateFiles("*", SearchOption.AllDirectories)
+			var eligibleFiles = _fileSystem.GetFiles(path, true)
                 .Where(i => extensions.Contains(i.Extension, StringComparer.OrdinalIgnoreCase))
                 .ToList();
 
@@ -189,7 +187,7 @@ namespace MediaBrowser.Server.Implementations.FileOrganization
                     try
                     {
                         _logger.Debug("Deleting empty directory {0}", path);
-                        Directory.Delete(path);
+                        _fileSystem.DeleteDirectory(path);
                     }
                     catch (UnauthorizedAccessException) { }
                     catch (DirectoryNotFoundException) { }

Some files were not shown because too many files changed in this diff