diff options
297 files changed, 3240 insertions, 2817 deletions
diff --git a/Emby.Drawing/Common/ImageHeader.cs b/Emby.Drawing/Common/ImageHeader.cs index b66bd71ea..59df5c04f 100644 --- a/Emby.Drawing/Common/ImageHeader.cs +++ b/Emby.Drawing/Common/ImageHeader.cs @@ -220,4 +220,4 @@ namespace Emby.Drawing.Common throw new ArgumentException(ErrorMessage); } } -} +}
\ No newline at end of file diff --git a/Emby.Drawing/Emby.Drawing.csproj b/Emby.Drawing/Emby.Drawing.csproj index ab53f7550..800756ee9 100644 --- a/Emby.Drawing/Emby.Drawing.csproj +++ b/Emby.Drawing/Emby.Drawing.csproj @@ -36,6 +36,9 @@ <SpecificVersion>False</SpecificVersion> <HintPath>..\packages\ImageMagickSharp.1.0.0.16\lib\net45\ImageMagickSharp.dll</HintPath> </Reference> + <Reference Include="policy.2.0.taglib-sharp"> + <HintPath>..\packages\taglib.2.1.0.0\lib\policy.2.0.taglib-sharp.dll</HintPath> + </Reference> <Reference Include="System" /> <Reference Include="System.Core" /> <Reference Include="System.Drawing" /> @@ -44,11 +47,15 @@ <Reference Include="Microsoft.CSharp" /> <Reference Include="System.Data" /> <Reference Include="System.Xml" /> + <Reference Include="taglib-sharp"> + <HintPath>..\packages\taglib.2.1.0.0\lib\taglib-sharp.dll</HintPath> + </Reference> </ItemGroup> <ItemGroup> <Compile Include="..\SharedVersion.cs"> <Link>Properties\SharedVersion.cs</Link> </Compile> + <Compile Include="Common\ImageHeader.cs" /> <Compile Include="GDI\DynamicImageHelpers.cs" /> <Compile Include="GDI\GDIImageEncoder.cs" /> <Compile Include="GDI\ImageExtensions.cs" /> @@ -56,7 +63,6 @@ <Compile Include="GDI\PlayedIndicatorDrawer.cs" /> <Compile Include="GDI\UnplayedCountIndicator.cs" /> <Compile Include="IImageEncoder.cs" /> - <Compile Include="Common\ImageHeader.cs" /> <Compile Include="ImageHelpers.cs" /> <Compile Include="ImageMagick\ImageMagickEncoder.cs" /> <Compile Include="ImageMagick\StripCollageBuilder.cs" /> diff --git a/Emby.Drawing/GDI/GDIImageEncoder.cs b/Emby.Drawing/GDI/GDIImageEncoder.cs index 7e3ca530b..1eabce74e 100644 --- a/Emby.Drawing/GDI/GDIImageEncoder.cs +++ b/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)) diff --git a/Emby.Drawing/ImageMagick/ImageMagickEncoder.cs b/Emby.Drawing/ImageMagick/ImageMagickEncoder.cs index 6ff40d1cf..2f8577acc 100644 --- a/Emby.Drawing/ImageMagick/ImageMagickEncoder.cs +++ b/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,14 +184,14 @@ 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) { var currentImageSize = new ImageSize(imageWidth, imageHeight); - new UnplayedCountIndicator(_appPaths).DrawUnplayedCountIndicator(wand, currentImageSize, options.UnplayedCount.Value); + new UnplayedCountIndicator(_appPaths, _fileSystem).DrawUnplayedCountIndicator(wand, currentImageSize, options.UnplayedCount.Value); } if (options.PercentPlayed > 0) @@ -209,15 +212,15 @@ namespace Emby.Drawing.ImageMagick if (ratio >= 1.4) { - new StripCollageBuilder(_appPaths).BuildThumbCollage(options.InputPaths.ToList(), options.OutputPath, options.Width, options.Height, options.Text); + new StripCollageBuilder(_appPaths, _fileSystem).BuildThumbCollage(options.InputPaths.ToList(), options.OutputPath, options.Width, options.Height, options.Text); } else if (ratio >= .9) { - new StripCollageBuilder(_appPaths).BuildSquareCollage(options.InputPaths.ToList(), options.OutputPath, options.Width, options.Height, options.Text); + new StripCollageBuilder(_appPaths, _fileSystem).BuildSquareCollage(options.InputPaths.ToList(), options.OutputPath, options.Width, options.Height, options.Text); } else { - new StripCollageBuilder(_appPaths).BuildPosterCollage(options.InputPaths.ToList(), options.OutputPath, options.Width, options.Height, options.Text); + new StripCollageBuilder(_appPaths, _fileSystem).BuildPosterCollage(options.InputPaths.ToList(), options.OutputPath, options.Width, options.Height, options.Text); } } diff --git a/Emby.Drawing/ImageMagick/PlayedIndicatorDrawer.cs b/Emby.Drawing/ImageMagick/PlayedIndicatorDrawer.cs index 1c751de1f..b5912788f 100644 --- a/Emby.Drawing/ImageMagick/PlayedIndicatorDrawer.cs +++ b/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) { diff --git a/Emby.Drawing/ImageMagick/StripCollageBuilder.cs b/Emby.Drawing/ImageMagick/StripCollageBuilder.cs index 7ed0f36e2..92eb1cd59 100644 --- a/Emby.Drawing/ImageMagick/StripCollageBuilder.cs +++ b/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); } } } } diff --git a/Emby.Drawing/ImageMagick/UnplayedCountIndicator.cs b/Emby.Drawing/ImageMagick/UnplayedCountIndicator.cs index dd25004d6..92601313a 100644 --- a/Emby.Drawing/ImageMagick/UnplayedCountIndicator.cs +++ b/Emby.Drawing/ImageMagick/UnplayedCountIndicator.cs @@ -1,5 +1,6 @@ using ImageMagickSharp; using MediaBrowser.Common.Configuration; +using MediaBrowser.Common.IO; using MediaBrowser.Model.Drawing; using System.Globalization; @@ -10,10 +11,12 @@ namespace Emby.Drawing.ImageMagick private const int OffsetFromTopRightCorner = 38; private readonly IApplicationPaths _appPaths; + private readonly IFileSystem _fileSystem; - public UnplayedCountIndicator(IApplicationPaths appPaths) + public UnplayedCountIndicator(IApplicationPaths appPaths, IFileSystem fileSystem) { _appPaths = appPaths; + _fileSystem = fileSystem; } public void DrawUnplayedCountIndicator(MagickWand wand, ImageSize imageSize, int count) @@ -33,7 +36,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; diff --git a/Emby.Drawing/ImageProcessor.cs b/Emby.Drawing/ImageProcessor.cs index 231bfa2b4..3e5dca9b7 100644 --- a/Emby.Drawing/ImageProcessor.cs +++ b/Emby.Drawing/ImageProcessor.cs @@ -1,5 +1,4 @@ -using Emby.Drawing.Common; -using MediaBrowser.Common.Extensions; +using MediaBrowser.Common.Extensions; using MediaBrowser.Common.IO; using MediaBrowser.Controller; using MediaBrowser.Controller.Drawing; @@ -17,6 +16,7 @@ using System.IO; using System.Linq; using System.Threading; using System.Threading.Tasks; +using Emby.Drawing.Common; namespace Emby.Drawing { @@ -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) @@ -399,6 +399,8 @@ namespace Emby.Drawing { size = GetImageSizeInternal(path, allowSlowMethod); + StartSaveImageSizeTimer(); + _cachedImagedSizes.AddOrUpdate(cacheHash, size, (keyName, oldValue) => size); } @@ -413,28 +415,26 @@ namespace Emby.Drawing /// <returns>ImageSize.</returns> private ImageSize GetImageSizeInternal(string path, bool allowSlowMethod) { - ImageSize size; - try { - size = ImageHeader.GetDimensions(path, _logger, _fileSystem); - } - catch - { - if (!allowSlowMethod) + using (var file = TagLib.File.Create(path)) { - throw; - } - //_logger.Info("Failed to read image header for {0}. Doing it the slow way.", path); + var image = file as TagLib.Image.File; - CheckDisposed(); + var properties = image.Properties; - size = _imageEncoder.GetImageSize(path); + return new ImageSize + { + Height = properties.PhotoHeight, + Width = properties.PhotoWidth + }; + } + } + catch + { } - StartSaveImageSizeTimer(); - - return size; + return ImageHeader.GetDimensions(path, _logger, _fileSystem); } private readonly Timer _saveImageSizeTimer; @@ -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); @@ -819,4 +819,4 @@ namespace Emby.Drawing } } } -} +}
\ No newline at end of file diff --git a/MediaBrowser.Api/ApiEntryPoint.cs b/MediaBrowser.Api/ApiEntryPoint.cs index c2b406190..aa5cd8d5f 100644 --- a/MediaBrowser.Api/ApiEntryPoint.cs +++ b/MediaBrowser.Api/ApiEntryPoint.cs @@ -95,7 +95,7 @@ namespace MediaBrowser.Api { var path = _config.ApplicationPaths.TranscodingTempPath; - foreach (var file in Directory.EnumerateFiles(path, "*", SearchOption.AllDirectories) + foreach (var file in _fileSystem.GetFilePaths(path, true) .ToList()) { _fileSystem.DeleteFile(file); @@ -567,7 +567,7 @@ namespace MediaBrowser.Api var directory = Path.GetDirectoryName(outputFilePath); var name = Path.GetFileNameWithoutExtension(outputFilePath); - var filesToDelete = Directory.EnumerateFiles(directory) + var filesToDelete = _fileSystem.GetFilePaths(directory) .Where(f => f.IndexOf(name, StringComparison.OrdinalIgnoreCase) != -1) .ToList(); diff --git a/MediaBrowser.Api/AppThemeService.cs b/MediaBrowser.Api/AppThemeService.cs deleted file mode 100644 index 87084e415..000000000 --- a/MediaBrowser.Api/AppThemeService.cs +++ /dev/null @@ -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); - } - } -} diff --git a/MediaBrowser.Api/Dlna/DlnaServerService.cs b/MediaBrowser.Api/Dlna/DlnaServerService.cs index 4f5e2ab25..4e7b1a7d5 100644 --- a/MediaBrowser.Api/Dlna/DlnaServerService.cs +++ b/MediaBrowser.Api/Dlna/DlnaServerService.cs @@ -108,7 +108,6 @@ namespace MediaBrowser.Api.Dlna private readonly IConnectionManager _connectionManager; private readonly IMediaReceiverRegistrar _mediaReceiverRegistrar; - // TODO: Add utf-8 private const string XMLContentType = "text/xml; charset=UTF-8"; public DlnaServerService(IDlnaManager dlnaManager, IContentDirectory contentDirectory, IConnectionManager connectionManager, IMediaReceiverRegistrar mediaReceiverRegistrar) diff --git a/MediaBrowser.Api/EnvironmentService.cs b/MediaBrowser.Api/EnvironmentService.cs index 457b4709b..81b326da8 100644 --- a/MediaBrowser.Api/EnvironmentService.cs +++ b/MediaBrowser.Api/EnvironmentService.cs @@ -1,4 +1,5 @@ -using MediaBrowser.Common.Net; +using MediaBrowser.Common.IO; +using MediaBrowser.Common.Net; using MediaBrowser.Controller.Net; using MediaBrowser.Model.IO; using MediaBrowser.Model.Net; @@ -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> @@ -117,8 +120,6 @@ namespace MediaBrowser.Api /// </summary> /// <param name="request">The request.</param> /// <returns>System.Object.</returns> - /// <exception cref="System.ArgumentNullException">Path</exception> - /// <exception cref="System.ArgumentException"></exception> public object Get(GetDirectoryContents request) { var path = request.Path; @@ -222,8 +223,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)) { diff --git a/MediaBrowser.Api/Images/ImageByNameService.cs b/MediaBrowser.Api/Images/ImageByNameService.cs index b4d5a9949..a6e1af516 100644 --- a/MediaBrowser.Api/Images/ImageByNameService.cs +++ b/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)) { diff --git a/MediaBrowser.Api/Images/RemoteImageService.cs b/MediaBrowser.Api/Images/RemoteImageService.cs index 6d2579ea9..c67ef96c9 100644 --- a/MediaBrowser.Api/Images/RemoteImageService.cs +++ b/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); diff --git a/MediaBrowser.Api/ItemLookupService.cs b/MediaBrowser.Api/ItemLookupService.cs index a0ba6e61f..9f7f57155 100644 --- a/MediaBrowser.Api/ItemLookupService.cs +++ b/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); diff --git a/MediaBrowser.Api/ItemRefreshService.cs b/MediaBrowser.Api/ItemRefreshService.cs index f56fd3282..1e74b3692 100644 --- a/MediaBrowser.Api/ItemRefreshService.cs +++ b/MediaBrowser.Api/ItemRefreshService.cs @@ -4,6 +4,7 @@ using MediaBrowser.Controller.Net; using MediaBrowser.Controller.Providers; using ServiceStack; using System.Threading; +using MediaBrowser.Common.IO; namespace MediaBrowser.Api { @@ -37,11 +38,13 @@ namespace MediaBrowser.Api { private readonly ILibraryManager _libraryManager; private readonly IProviderManager _providerManager; + private readonly IFileSystem _fileSystem; - public ItemRefreshService(ILibraryManager libraryManager, IProviderManager providerManager) + public ItemRefreshService(ILibraryManager libraryManager, IProviderManager providerManager, IFileSystem fileSystem) { _libraryManager = libraryManager; _providerManager = providerManager; + _fileSystem = fileSystem; } /// <summary> @@ -66,7 +69,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, diff --git a/MediaBrowser.Api/ItemUpdateService.cs b/MediaBrowser.Api/ItemUpdateService.cs index b8ae9392a..23ff74495 100644 --- a/MediaBrowser.Api/ItemUpdateService.cs +++ b/MediaBrowser.Api/ItemUpdateService.cs @@ -211,11 +211,6 @@ namespace MediaBrowser.Api UpdateItem(request, item); - if (isLockedChanged && item.IsLocked) - { - item.IsUnidentified = false; - } - await item.UpdateToRepository(ItemUpdateType.MetadataEdit, CancellationToken.None).ConfigureAwait(false); if (request.People != null) @@ -321,13 +316,8 @@ namespace MediaBrowser.Api SetProductionLocations(item, request); - var hasLang = item as IHasPreferredMetadataLanguage; - - if (hasLang != null) - { - hasLang.PreferredMetadataCountryCode = request.PreferredMetadataCountryCode; - hasLang.PreferredMetadataLanguage = request.PreferredMetadataLanguage; - } + item.PreferredMetadataCountryCode = request.PreferredMetadataCountryCode; + item.PreferredMetadataLanguage = request.PreferredMetadataLanguage; var hasDisplayOrder = item as IHasDisplayOrder; if (hasDisplayOrder != null) diff --git a/MediaBrowser.Api/Library/LibraryHelpers.cs b/MediaBrowser.Api/Library/LibraryHelpers.cs index 0ee28d6fe..877321b77 100644 --- a/MediaBrowser.Api/Library/LibraryHelpers.cs +++ b/MediaBrowser.Api/Library/LibraryHelpers.cs @@ -33,11 +33,11 @@ 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)); } - + var shortcut = Directory.EnumerateFiles(path, ShortcutFileSearch, SearchOption.AllDirectories).FirstOrDefault(f => fileSystem.ResolveShortcut(f).Equals(mediaPath, StringComparison.OrdinalIgnoreCase)); if (!string.IsNullOrEmpty(shortcut)) @@ -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); diff --git a/MediaBrowser.Api/Library/LibraryService.cs b/MediaBrowser.Api/Library/LibraryService.cs index 49dd121ba..20fd1ef40 100644 --- a/MediaBrowser.Api/Library/LibraryService.cs +++ b/MediaBrowser.Api/Library/LibraryService.cs @@ -27,6 +27,7 @@ using System.IO; using System.Linq; using System.Threading; using System.Threading.Tasks; +using MediaBrowser.Common.IO; namespace MediaBrowser.Api.Library { @@ -282,12 +283,13 @@ namespace MediaBrowser.Api.Library private readonly IChannelManager _channelManager; private readonly ITVSeriesManager _tvManager; private readonly ILibraryMonitor _libraryMonitor; + private readonly IFileSystem _fileSystem; /// <summary> /// Initializes a new instance of the <see cref="LibraryService" /> class. /// </summary> public LibraryService(IItemRepository itemRepo, ILibraryManager libraryManager, IUserManager userManager, - IDtoService dtoService, IUserDataManager userDataManager, IAuthorizationContext authContext, IActivityManager activityManager, ILocalizationManager localization, ILiveTvManager liveTv, IChannelManager channelManager, ITVSeriesManager tvManager, ILibraryMonitor libraryMonitor) + IDtoService dtoService, IUserDataManager userDataManager, IAuthorizationContext authContext, IActivityManager activityManager, ILocalizationManager localization, ILiveTvManager liveTv, IChannelManager channelManager, ITVSeriesManager tvManager, ILibraryMonitor libraryMonitor, IFileSystem fileSystem) { _itemRepo = itemRepo; _libraryManager = libraryManager; @@ -301,6 +303,7 @@ namespace MediaBrowser.Api.Library _channelManager = channelManager; _tvManager = tvManager; _libraryMonitor = libraryMonitor; + _fileSystem = fileSystem; } public object Get(GetSimilarItems request) @@ -557,7 +560,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."); } @@ -719,7 +722,7 @@ namespace MediaBrowser.Api.Library if (!item.CanDelete(user)) { - throw new UnauthorizedAccessException(); + throw new SecurityException("Unauthorized access"); } if (item is ILiveTvRecording) diff --git a/MediaBrowser.Api/Library/LibraryStructureService.cs b/MediaBrowser.Api/Library/LibraryStructureService.cs index f5fe921ce..c8731637c 100644 --- a/MediaBrowser.Api/Library/LibraryStructureService.cs +++ b/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,16 @@ 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); + using (File.Create(path)) + { + + } } } finally @@ -256,12 +259,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 +279,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 +322,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"); } diff --git a/MediaBrowser.Api/LiveTv/LiveTvService.cs b/MediaBrowser.Api/LiveTv/LiveTvService.cs index 07e64c98d..344d6ab3d 100644 --- a/MediaBrowser.Api/LiveTv/LiveTvService.cs +++ b/MediaBrowser.Api/LiveTv/LiveTvService.cs @@ -521,12 +521,12 @@ namespace MediaBrowser.Api.LiveTv if (user == null) { - throw new UnauthorizedAccessException("Anonymous live tv management is not allowed."); + throw new SecurityException("Anonymous live tv management is not allowed."); } if (!user.Policy.EnableLiveTvManagement) { - throw new UnauthorizedAccessException("The current user does not have permission to manage live tv."); + throw new SecurityException("The current user does not have permission to manage live tv."); } } diff --git a/MediaBrowser.Api/MediaBrowser.Api.csproj b/MediaBrowser.Api/MediaBrowser.Api.csproj index 295cc78e9..901dd11e2 100644 --- a/MediaBrowser.Api/MediaBrowser.Api.csproj +++ b/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.17511.0, Culture=neutral, PublicKeyToken=384d532d7e88985d, processorArchitecture=MSIL"> - <SpecificVersion>False</SpecificVersion> - <HintPath>..\packages\morelinq.1.1.0\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" /> diff --git a/MediaBrowser.Api/Playback/BaseStreamingService.cs b/MediaBrowser.Api/Playback/BaseStreamingService.cs index 531d67eed..8e5061d62 100644 --- a/MediaBrowser.Api/Playback/BaseStreamingService.cs +++ b/MediaBrowser.Api/Playback/BaseStreamingService.cs @@ -290,13 +290,6 @@ namespace MediaBrowser.Api.Playback { get { - var lib = ApiEntryPoint.Instance.GetEncodingOptions().H264Encoder; - - if (!string.IsNullOrWhiteSpace(lib)) - { - return lib; - } - return "libx264"; } } @@ -810,13 +803,53 @@ namespace MediaBrowser.Api.Playback } /// <summary> + /// Gets the name of the output video codec + /// </summary> + /// <param name="state">The state.</param> + /// <returns>System.String.</returns> + protected string GetVideoDecoder(StreamState state) + { + if (string.Equals(ApiEntryPoint.Instance.GetEncodingOptions().HardwareVideoDecoder, "qsv", StringComparison.OrdinalIgnoreCase)) + { + if (state.VideoStream != null && !string.IsNullOrWhiteSpace(state.VideoStream.Codec)) + { + switch (state.MediaSource.VideoStream.Codec.ToLower()) + { + case "avc": + case "h264": + if (MediaEncoder.SupportsDecoder("h264_qsv")) + { + return "-c:v h264_qsv "; + } + break; + case "mpeg2video": + if (MediaEncoder.SupportsDecoder("mpeg2_qsv")) + { + return "-c:v mpeg2_qsv "; + } + break; + case "vc1": + if (MediaEncoder.SupportsDecoder("vc1_qsv")) + { + return "-c:v vc1_qsv "; + } + break; + } + } + } + + // leave blank so ffmpeg will decide + return null; + } + + /// <summary> /// Gets the input argument. /// </summary> /// <param name="state">The state.</param> /// <returns>System.String.</returns> protected string GetInputArgument(StreamState state) { - var arg = "-i " + GetInputPathArgument(state); + var arg = string.Format("-i {0}", GetInputPathArgument(state)); if (state.SubtitleStream != null) { @@ -826,7 +859,7 @@ namespace MediaBrowser.Api.Playback } } - return arg; + return arg.Trim(); } private string GetInputPathArgument(StreamState state) @@ -840,7 +873,7 @@ namespace MediaBrowser.Api.Playback { if (!(state.VideoType == VideoType.Iso && state.IsoMount == null)) { - inputPath = MediaEncoderHelpers.GetInputArgument(mediaPath, state.InputProtocol, state.IsoMount, state.PlayableStreamFileNames); + inputPath = MediaEncoderHelpers.GetInputArgument(FileSystem, mediaPath, state.InputProtocol, state.IsoMount, state.PlayableStreamFileNames); } } @@ -889,7 +922,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 +975,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 +1005,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); } @@ -1027,6 +1060,7 @@ namespace MediaBrowser.Api.Playback var bytes = Encoding.UTF8.GetBytes(Environment.NewLine + line); await target.WriteAsync(bytes, 0, bytes.Length).ConfigureAwait(false); + await target.FlushAsync().ConfigureAwait(false); } } } @@ -2158,6 +2192,12 @@ namespace MediaBrowser.Api.Playback inputModifier += " -re"; } + var videoDecoder = GetVideoDecoder(state); + if (!string.IsNullOrWhiteSpace(videoDecoder)) + { + inputModifier += " " + videoDecoder; + } + return inputModifier; } diff --git a/MediaBrowser.Api/Playback/Dash/MpegDashService.cs b/MediaBrowser.Api/Playback/Dash/MpegDashService.cs index c201ffd58..cbee821d2 100644 --- a/MediaBrowser.Api/Playback/Dash/MpegDashService.cs +++ b/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,20 +347,20 @@ 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.GetDirectoryPaths(folder).ToList()) { - var subfolderName = Path.GetFileNameWithoutExtension(subfolder.FullName); + var subfolderName = Path.GetFileNameWithoutExtension(subfolder); int startNumber; if (int.TryParse(subfolderName, NumberStyles.Any, UsCulture, out startNumber)) { 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; } diff --git a/MediaBrowser.Api/Playback/Hls/BaseHlsService.cs b/MediaBrowser.Api/Playback/Hls/BaseHlsService.cs index e4f00e2a0..a9a9610a9 100644 --- a/MediaBrowser.Api/Playback/Hls/BaseHlsService.cs +++ b/MediaBrowser.Api/Playback/Hls/BaseHlsService.cs @@ -23,7 +23,8 @@ namespace MediaBrowser.Api.Playback.Hls /// </summary> public abstract class BaseHlsService : BaseStreamingService { - protected BaseHlsService(IServerConfigurationManager serverConfig, IUserManager userManager, ILibraryManager libraryManager, IIsoManager isoManager, IMediaEncoder mediaEncoder, IFileSystem fileSystem, IDlnaManager dlnaManager, ISubtitleEncoder subtitleEncoder, IDeviceManager deviceManager, IMediaSourceManager mediaSourceManager, IZipClient zipClient, IJsonSerializer jsonSerializer) : base(serverConfig, userManager, libraryManager, isoManager, mediaEncoder, fileSystem, dlnaManager, subtitleEncoder, deviceManager, mediaSourceManager, zipClient, jsonSerializer) + protected BaseHlsService(IServerConfigurationManager serverConfig, IUserManager userManager, ILibraryManager libraryManager, IIsoManager isoManager, IMediaEncoder mediaEncoder, IFileSystem fileSystem, IDlnaManager dlnaManager, ISubtitleEncoder subtitleEncoder, IDeviceManager deviceManager, IMediaSourceManager mediaSourceManager, IZipClient zipClient, IJsonSerializer jsonSerializer) + : base(serverConfig, userManager, libraryManager, isoManager, mediaEncoder, fileSystem, dlnaManager, subtitleEncoder, deviceManager, mediaSourceManager, zipClient, jsonSerializer) { } @@ -90,12 +91,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 @@ -150,7 +151,7 @@ namespace MediaBrowser.Api.Playback.Hls { ApiEntryPoint.Instance.OnTranscodeEndRequest(job); } - + return ResultFactory.GetResult(playlistText, MimeTypes.GetMimeType("playlist.m3u8"), new Dictionary<string, string>()); } @@ -317,4 +318,4 @@ namespace MediaBrowser.Api.Playback.Hls return false; } } -} +}
\ No newline at end of file diff --git a/MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs b/MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs index 7b2844cd4..cb49e65c7 100644 --- a/MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs +++ b/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); } @@ -989,4 +988,4 @@ namespace MediaBrowser.Api.Playback.Hls return base.CanStreamCopyVideo(request, videoStream); } } -} +}
\ No newline at end of file diff --git a/MediaBrowser.Api/Playback/MediaInfoService.cs b/MediaBrowser.Api/Playback/MediaInfoService.cs index 0b7b50134..2ab2d11f6 100644 --- a/MediaBrowser.Api/Playback/MediaInfoService.cs +++ b/MediaBrowser.Api/Playback/MediaInfoService.cs @@ -57,7 +57,7 @@ namespace MediaBrowser.Api.Playback Size = 102400; } } - + [Authenticated] public class MediaInfoService : BaseApiService { @@ -289,7 +289,7 @@ namespace MediaBrowser.Api.Playback if (mediaSource.SupportsDirectStream) { options.MaxBitrate = GetMaxBitrate(maxBitrate); - + // The MediaSource supports direct stream, now test to see if the client supports it var streamInfo = string.Equals(item.MediaType, MediaType.Audio, StringComparison.OrdinalIgnoreCase) ? streamBuilder.BuildAudioItem(options) : @@ -309,7 +309,7 @@ namespace MediaBrowser.Api.Playback if (mediaSource.SupportsTranscoding) { options.MaxBitrate = GetMaxBitrate(maxBitrate); - + // The MediaSource supports direct stream, now test to see if the client supports it var streamInfo = string.Equals(item.MediaType, MediaType.Audio, StringComparison.OrdinalIgnoreCase) ? streamBuilder.BuildAudioItem(options) : @@ -336,9 +336,15 @@ namespace MediaBrowser.Api.Playback var maxBitrate = clientMaxBitrate; var remoteClientMaxBitrate = _config.Configuration.RemoteClientBitrateLimit; - if (remoteClientMaxBitrate > 0 && !_networkManager.IsInLocalNetwork(Request.RemoteIp)) + if (remoteClientMaxBitrate > 0) { - maxBitrate = Math.Min(maxBitrate ?? remoteClientMaxBitrate, remoteClientMaxBitrate); + var isInLocalNetwork = _networkManager.IsInLocalNetwork(Request.RemoteIp); + + Logger.Info("RemoteClientBitrateLimit: {0}, RemoteIp: {1}, IsInLocalNetwork: {2}", remoteClientMaxBitrate, Request.RemoteIp, isInLocalNetwork); + if (!isInLocalNetwork) + { + maxBitrate = Math.Min(maxBitrate ?? remoteClientMaxBitrate, remoteClientMaxBitrate); + } } return maxBitrate; diff --git a/MediaBrowser.Api/Playback/Progressive/BaseProgressiveStreamingService.cs b/MediaBrowser.Api/Playback/Progressive/BaseProgressiveStreamingService.cs index 910ac18e7..aa0cda133 100644 --- a/MediaBrowser.Api/Playback/Progressive/BaseProgressiveStreamingService.cs +++ b/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); } diff --git a/MediaBrowser.Api/Playback/Progressive/VideoService.cs b/MediaBrowser.Api/Playback/Progressive/VideoService.cs index 84ae26248..1dfb43387 100644 --- a/MediaBrowser.Api/Playback/Progressive/VideoService.cs +++ b/MediaBrowser.Api/Playback/Progressive/VideoService.cs @@ -17,6 +17,7 @@ namespace MediaBrowser.Api.Playback.Progressive /// <summary> /// Class GetVideoStream /// </summary> + [Route("/Videos/{Id}/stream.mpegts", "GET")] [Route("/Videos/{Id}/stream.ts", "GET")] [Route("/Videos/{Id}/stream.webm", "GET")] [Route("/Videos/{Id}/stream.asf", "GET")] diff --git a/MediaBrowser.Api/Playback/StreamState.cs b/MediaBrowser.Api/Playback/StreamState.cs index 34dc5ea12..fdbe5835e 100644 --- a/MediaBrowser.Api/Playback/StreamState.cs +++ b/MediaBrowser.Api/Playback/StreamState.cs @@ -74,7 +74,7 @@ namespace MediaBrowser.Api.Playback { get { - return ReadInputAtNativeFramerate ? 1000 : 0; + return 0; } } diff --git a/MediaBrowser.Api/Reports/Common/HeaderMetadata.cs b/MediaBrowser.Api/Reports/Common/HeaderMetadata.cs index af5abf46a..62f941a7a 100644 --- a/MediaBrowser.Api/Reports/Common/HeaderMetadata.cs +++ b/MediaBrowser.Api/Reports/Common/HeaderMetadata.cs @@ -43,7 +43,6 @@ namespace MediaBrowser.Api.Reports MusicArtist, AudioAlbum, Locked, - Unidentified, ImagePrimary, ImageBackdrop, ImageLogo, diff --git a/MediaBrowser.Api/Reports/Common/ItemViewType.cs b/MediaBrowser.Api/Reports/Common/ItemViewType.cs index 5126457cf..098442115 100644 --- a/MediaBrowser.Api/Reports/Common/ItemViewType.cs +++ b/MediaBrowser.Api/Reports/Common/ItemViewType.cs @@ -17,7 +17,6 @@ namespace MediaBrowser.Api.Reports TrailersImage, SpecialsImage, LockDataImage, - UnidentifiedImage, TagsPrimaryImage, TagsBackdropImage, TagsLogoImage, diff --git a/MediaBrowser.Api/Reports/Data/ReportBuilder.cs b/MediaBrowser.Api/Reports/Data/ReportBuilder.cs index 512441bd7..5e339dc73 100644 --- a/MediaBrowser.Api/Reports/Data/ReportBuilder.cs +++ b/MediaBrowser.Api/Reports/Data/ReportBuilder.cs @@ -105,7 +105,6 @@ namespace MediaBrowser.Api.Reports { HeaderMetadata.Status, HeaderMetadata.Locked, - HeaderMetadata.Unidentified, HeaderMetadata.ImagePrimary, HeaderMetadata.ImageBackdrop, HeaderMetadata.ImageLogo, @@ -122,7 +121,6 @@ namespace MediaBrowser.Api.Reports { HeaderMetadata.Status, HeaderMetadata.Locked, - HeaderMetadata.Unidentified, HeaderMetadata.ImagePrimary, HeaderMetadata.ImageBackdrop, HeaderMetadata.ImageLogo, @@ -143,7 +141,6 @@ namespace MediaBrowser.Api.Reports { HeaderMetadata.Status, HeaderMetadata.Locked, - HeaderMetadata.Unidentified, HeaderMetadata.ImagePrimary, HeaderMetadata.ImageBackdrop, HeaderMetadata.ImageLogo, @@ -161,7 +158,6 @@ namespace MediaBrowser.Api.Reports { HeaderMetadata.Status, HeaderMetadata.Locked, - HeaderMetadata.Unidentified, HeaderMetadata.ImagePrimary, HeaderMetadata.ImageBackdrop, HeaderMetadata.ImageLogo, @@ -177,7 +173,6 @@ namespace MediaBrowser.Api.Reports { HeaderMetadata.Status, HeaderMetadata.Locked, - HeaderMetadata.Unidentified, HeaderMetadata.ImagePrimary, HeaderMetadata.ImageBackdrop, HeaderMetadata.ImageLogo, @@ -198,7 +193,6 @@ namespace MediaBrowser.Api.Reports { HeaderMetadata.Status, HeaderMetadata.Locked, - HeaderMetadata.Unidentified, HeaderMetadata.ImagePrimary, HeaderMetadata.ImageBackdrop, HeaderMetadata.ImageLogo, @@ -223,7 +217,6 @@ namespace MediaBrowser.Api.Reports { HeaderMetadata.Status, HeaderMetadata.Locked, - HeaderMetadata.Unidentified, HeaderMetadata.ImagePrimary, HeaderMetadata.ImageBackdrop, HeaderMetadata.ImageLogo, @@ -241,7 +234,6 @@ namespace MediaBrowser.Api.Reports { HeaderMetadata.Status, HeaderMetadata.Locked, - HeaderMetadata.Unidentified, HeaderMetadata.ImagePrimary, HeaderMetadata.ImageBackdrop, HeaderMetadata.ImageLogo, @@ -260,7 +252,6 @@ namespace MediaBrowser.Api.Reports { HeaderMetadata.Status, HeaderMetadata.Locked, - HeaderMetadata.Unidentified, HeaderMetadata.ImagePrimary, HeaderMetadata.ImageBackdrop, HeaderMetadata.ImageLogo, @@ -284,7 +275,6 @@ namespace MediaBrowser.Api.Reports { HeaderMetadata.Status, HeaderMetadata.Locked, - HeaderMetadata.Unidentified, HeaderMetadata.ImagePrimary, HeaderMetadata.ImageBackdrop, HeaderMetadata.ImageLogo, @@ -315,11 +305,9 @@ namespace MediaBrowser.Api.Reports { HeaderMetadata.Status, HeaderMetadata.Locked, - HeaderMetadata.Unidentified, HeaderMetadata.ImagePrimary, HeaderMetadata.ImageBackdrop, HeaderMetadata.ImageLogo, - HeaderMetadata.Unidentified, HeaderMetadata.ImagePrimary, HeaderMetadata.ImageBackdrop, HeaderMetadata.ImageLogo, @@ -376,12 +364,6 @@ namespace MediaBrowser.Api.Reports option.Header.CanGroup = false; option.Header.DisplayType = ReportDisplayType.Export; break; - case HeaderMetadata.Unidentified: - option.Column = (i, r) => this.GetBoolString(r.IsUnidentified); - option.Header.ItemViewType = ItemViewType.UnidentifiedImage; - option.Header.CanGroup = false; - option.Header.DisplayType = ReportDisplayType.Export; - break; case HeaderMetadata.ImagePrimary: option.Column = (i, r) => this.GetBoolString(r.HasImageTagsPrimary); option.Header.ItemViewType = ItemViewType.TagsPrimaryImage; @@ -633,7 +615,6 @@ namespace MediaBrowser.Api.Reports { Id = item.Id.ToString("N"), HasLockData = item.IsLocked, - IsUnidentified = item.IsUnidentified, HasLocalTrailer = hasTrailers != null ? hasTrailers.GetTrailerIds().Count() > 0 : false, HasImageTagsPrimary = (item.ImageInfos != null && item.ImageInfos.Count(n => n.Type == ImageType.Primary) > 0), HasImageTagsBackdrop = (item.ImageInfos != null && item.ImageInfos.Count(n => n.Type == ImageType.Backdrop) > 0), diff --git a/MediaBrowser.Api/Reports/Model/ReportRow.cs b/MediaBrowser.Api/Reports/Model/ReportRow.cs index 3f66b8479..1a46b72b7 100644 --- a/MediaBrowser.Api/Reports/Model/ReportRow.cs +++ b/MediaBrowser.Api/Reports/Model/ReportRow.cs @@ -56,10 +56,6 @@ namespace MediaBrowser.Api.Reports /// <value> true if this object has specials, false if not. </value> public bool HasSpecials { get; set; } - /// <summary> Gets or sets a value indicating whether this object is unidentified. </summary> - /// <value> true if this object is unidentified, false if not. </value> - public bool IsUnidentified { get; set; } - /// <summary> Gets or sets the columns. </summary> /// <value> The columns. </value> public List<ReportItem> Columns { get; set; } diff --git a/MediaBrowser.Api/Reports/ReportsService.cs b/MediaBrowser.Api/Reports/ReportsService.cs index 82e7264f1..6c2886f5b 100644 --- a/MediaBrowser.Api/Reports/ReportsService.cs +++ b/MediaBrowser.Api/Reports/ReportsService.cs @@ -226,7 +226,6 @@ namespace MediaBrowser.Api.Reports NameStartsWithOrGreater = request.NameStartsWithOrGreater, HasImdbId = request.HasImdbId, IsYearMismatched = request.IsYearMismatched, - IsUnidentified = request.IsUnidentified, IsPlaceHolder = request.IsPlaceHolder, IsLocked = request.IsLocked, IsInBoxSet = request.IsInBoxSet, diff --git a/MediaBrowser.Api/Reports/Stat/ReportStatBuilder.cs b/MediaBrowser.Api/Reports/Stat/ReportStatBuilder.cs index c9ee6337f..fb694d6e1 100644 --- a/MediaBrowser.Api/Reports/Stat/ReportStatBuilder.cs +++ b/MediaBrowser.Api/Reports/Stat/ReportStatBuilder.cs @@ -213,7 +213,7 @@ namespace MediaBrowser.Api.Reports }; foreach (var item in t) { - var ps = items.Where(x => x.People != null && x.SupportsPeople).SelectMany(x => x.People) + var ps = items.SelectMany(x => _libraryManager.GetPeople(x)) .Where(n => n.Type == item.ToString()) .GroupBy(x => x.Name) .OrderByDescending(x => x.Count()) diff --git a/MediaBrowser.Api/StartupWizardService.cs b/MediaBrowser.Api/StartupWizardService.cs index e9ac45fa2..962334cdc 100644 --- a/MediaBrowser.Api/StartupWizardService.cs +++ b/MediaBrowser.Api/StartupWizardService.cs @@ -69,10 +69,10 @@ namespace MediaBrowser.Api _config.Configuration.MergeMetadataAndImagesByName = true; _config.Configuration.EnableStandaloneMetadata = true; _config.Configuration.EnableLibraryMetadataSubFolder = true; - _config.Configuration.EnableUserSpecificUserViews = true; _config.Configuration.EnableCustomPathSubFolders = true; _config.Configuration.DisableXmlSavers = true; _config.Configuration.DisableStartupScan = true; + _config.Configuration.EnableUserViews = true; _config.SaveConfiguration(); } diff --git a/MediaBrowser.Api/Subtitles/SubtitleService.cs b/MediaBrowser.Api/Subtitles/SubtitleService.cs index c9280b6f6..0bfe7354d 100644 --- a/MediaBrowser.Api/Subtitles/SubtitleService.cs +++ b/MediaBrowser.Api/Subtitles/SubtitleService.cs @@ -16,6 +16,7 @@ using System.Text; using System.Threading; using System.Threading.Tasks; using MimeTypes = MediaBrowser.Model.Net.MimeTypes; +using MediaBrowser.Common.IO; namespace MediaBrowser.Api.Subtitles { @@ -127,14 +128,16 @@ namespace MediaBrowser.Api.Subtitles private readonly ISubtitleEncoder _subtitleEncoder; private readonly IMediaSourceManager _mediaSourceManager; private readonly IProviderManager _providerManager; + private readonly IFileSystem _fileSystem; - public SubtitleService(ILibraryManager libraryManager, ISubtitleManager subtitleManager, ISubtitleEncoder subtitleEncoder, IMediaSourceManager mediaSourceManager, IProviderManager providerManager) + public SubtitleService(ILibraryManager libraryManager, ISubtitleManager subtitleManager, ISubtitleEncoder subtitleEncoder, IMediaSourceManager mediaSourceManager, IProviderManager providerManager, IFileSystem fileSystem) { _libraryManager = libraryManager; _subtitleManager = subtitleManager; _subtitleEncoder = subtitleEncoder; _mediaSourceManager = mediaSourceManager; _providerManager = providerManager; + _fileSystem = fileSystem; } public async Task<object> Get(GetSubtitlePlaylist request) @@ -259,7 +262,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) { diff --git a/MediaBrowser.Api/System/SystemService.cs b/MediaBrowser.Api/System/SystemService.cs index aa1226901..00935dd1e 100644 --- a/MediaBrowser.Api/System/SystemService.cs +++ b/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); diff --git a/MediaBrowser.Api/UserLibrary/BaseItemsRequest.cs b/MediaBrowser.Api/UserLibrary/BaseItemsRequest.cs index 7db8e4dca..4d844e6cb 100644 --- a/MediaBrowser.Api/UserLibrary/BaseItemsRequest.cs +++ b/MediaBrowser.Api/UserLibrary/BaseItemsRequest.cs @@ -299,9 +299,6 @@ namespace MediaBrowser.Api.UserLibrary [ApiMember(Name = "IsLocked", Description = "Optional filter by items that are locked.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")] public bool? IsLocked { get; set; } - [ApiMember(Name = "IsUnidentified", Description = "Optional filter by items that are unidentified by internet metadata providers.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")] - public bool? IsUnidentified { get; set; } - [ApiMember(Name = "IsPlaceHolder", Description = "Optional filter by items that are placeholders", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")] public bool? IsPlaceHolder { get; set; } diff --git a/MediaBrowser.Api/UserLibrary/ItemsService.cs b/MediaBrowser.Api/UserLibrary/ItemsService.cs index 7d3290c2f..f1168ab7f 100644 --- a/MediaBrowser.Api/UserLibrary/ItemsService.cs +++ b/MediaBrowser.Api/UserLibrary/ItemsService.cs @@ -192,7 +192,6 @@ namespace MediaBrowser.Api.UserLibrary NameStartsWithOrGreater = request.NameStartsWithOrGreater, HasImdbId = request.HasImdbId, IsYearMismatched = request.IsYearMismatched, - IsUnidentified = request.IsUnidentified, IsPlaceHolder = request.IsPlaceHolder, IsLocked = request.IsLocked, IsInBoxSet = request.IsInBoxSet, diff --git a/MediaBrowser.Api/UserLibrary/UserViewsService.cs b/MediaBrowser.Api/UserLibrary/UserViewsService.cs index a49ab8556..9d7c38d6f 100644 --- a/MediaBrowser.Api/UserLibrary/UserViewsService.cs +++ b/MediaBrowser.Api/UserLibrary/UserViewsService.cs @@ -39,6 +39,17 @@ namespace MediaBrowser.Api.UserLibrary public string UserId { get; set; } } + [Route("/Users/{UserId}/GroupingOptions", "GET")] + public class GetGroupingOptions : IReturn<List<SpecialViewOption>> + { + /// <summary> + /// Gets or sets the user id. + /// </summary> + /// <value>The user id.</value> + [ApiMember(Name = "UserId", Description = "User Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "GET")] + public string UserId { get; set; } + } + public class UserViewsService : BaseApiService { private readonly IUserManager _userManager; @@ -105,6 +116,29 @@ namespace MediaBrowser.Api.UserLibrary return ToOptimizedResult(list); } + public async Task<object> Get(GetGroupingOptions request) + { + var user = _userManager.GetUserById(request.UserId); + + var views = user.RootFolder + .GetChildren(user, true) + .OfType<Folder>() + .Where(i => !UserView.IsExcludedFromGrouping(i)) + .ToList(); + + var list = views + .Select(i => new SpecialViewOption + { + Name = i.Name, + Id = i.Id.ToString("N") + + }) + .OrderBy(i => i.Name) + .ToList(); + + return ToOptimizedResult(list); + } + private bool IsEligibleForSpecialView(ICollectionFolder view) { var types = new[] { CollectionType.Movies, CollectionType.TvShows, CollectionType.Games, CollectionType.Music, CollectionType.Photos }; diff --git a/MediaBrowser.Api/packages.config b/MediaBrowser.Api/packages.config index 6df166204..0cdb26bd5 100644 --- a/MediaBrowser.Api/packages.config +++ b/MediaBrowser.Api/packages.config @@ -1,4 +1,4 @@ <?xml version="1.0" encoding="utf-8"?> <packages> - <package id="morelinq" version="1.1.0" targetFramework="net45" /> + <package id="morelinq" version="1.1.1" targetFramework="net45" /> </packages>
\ No newline at end of file diff --git a/MediaBrowser.Common.Implementations/Archiving/ZipClient.cs b/MediaBrowser.Common.Implementations/Archiving/ZipClient.cs index cdcbc311a..1377e9d55 100644 --- a/MediaBrowser.Common.Implementations/Archiving/ZipClient.cs +++ b/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); } diff --git a/MediaBrowser.Common.Implementations/BaseApplicationHost.cs b/MediaBrowser.Common.Implementations/BaseApplicationHost.cs index 70ed5c319..af41635f3 100644 --- a/MediaBrowser.Common.Implementations/BaseApplicationHost.cs +++ b/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; @@ -320,7 +321,7 @@ namespace MediaBrowser.Common.Implementations protected virtual IJsonSerializer CreateJsonSerializer() { - return new JsonSerializer(); + return new JsonSerializer(FileSystemManager); } private void SetHttpLimit() @@ -449,7 +450,7 @@ namespace MediaBrowser.Common.Implementations RegisterSingleInstance<IApplicationPaths>(ApplicationPaths); - TaskManager = new TaskManager(ApplicationPaths, JsonSerializer, Logger); + TaskManager = new TaskManager(ApplicationPaths, JsonSerializer, Logger, FileSystemManager); RegisterSingleInstance(JsonSerializer); RegisterSingleInstance(XmlSerializer); @@ -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(); @@ -581,7 +582,7 @@ namespace MediaBrowser.Common.Implementations protected void RegisterSingleInstance<T>(T obj, bool manageLifetime = true) where T : class { - Container.RegisterSingle(obj); + Container.RegisterSingleton(obj); if (manageLifetime) { @@ -607,7 +608,7 @@ namespace MediaBrowser.Common.Implementations protected void RegisterSingleInstance<T>(Func<T> func) where T : class { - Container.RegisterSingle(func); + Container.RegisterSingleton(func); } void IDependencyContainer.Register(Type typeInterface, Type typeImplementation) diff --git a/MediaBrowser.Common.Implementations/Configuration/ConfigurationHelper.cs b/MediaBrowser.Common.Implementations/Configuration/ConfigurationHelper.cs index ff5b8bd59..276da58d4 100644 --- a/MediaBrowser.Common.Implementations/Configuration/ConfigurationHelper.cs +++ b/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 { @@ -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 = File.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)); + Directory.CreateDirectory(Path.GetDirectoryName(path)); // Save it after load in case we got new items - File.WriteAllBytes(path, newBytes); + File.WriteAllBytes(path, newBytes); } return configuration; diff --git a/MediaBrowser.Common.Implementations/Devices/DeviceId.cs b/MediaBrowser.Common.Implementations/Devices/DeviceId.cs index 2a1c8877d..39f11fabf 100644 --- a/MediaBrowser.Common.Implementations/Devices/DeviceId.cs +++ b/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 = File.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 diff --git a/MediaBrowser.Common.Implementations/HttpClientManager/HttpClientManager.cs b/MediaBrowser.Common.Implementations/HttpClientManager/HttpClientManager.cs index 89f405e8a..7157f6325 100644 --- a/MediaBrowser.Common.Implementations/HttpClientManager/HttpClientManager.cs +++ b/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"); @@ -869,25 +869,11 @@ namespace MediaBrowser.Common.Implementations.HttpClientManager Task<WebResponse> asyncTask = Task.Factory.FromAsync<WebResponse>(request.BeginGetResponse, request.EndGetResponse, null); ThreadPool.RegisterWaitForSingleObject((asyncTask as IAsyncResult).AsyncWaitHandle, TimeoutCallback, request, timeout, true); - asyncTask.ContinueWith(task => - { - taskCompletion.TrySetResult(task.Result); - - }, TaskContinuationOptions.NotOnFaulted); + var callback = new TaskCallback { taskCompletion = taskCompletion }; + asyncTask.ContinueWith(callback.OnSuccess, TaskContinuationOptions.NotOnFaulted); // Handle errors - asyncTask.ContinueWith(task => - { - if (task.Exception != null) - { - taskCompletion.TrySetException(task.Exception); - } - else - { - taskCompletion.TrySetException(new List<Exception>()); - } - - }, TaskContinuationOptions.OnlyOnFaulted); + asyncTask.ContinueWith(callback.OnError, TaskContinuationOptions.OnlyOnFaulted); return taskCompletion.Task; } @@ -903,5 +889,27 @@ namespace MediaBrowser.Common.Implementations.HttpClientManager } } } + + private class TaskCallback + { + public TaskCompletionSource<WebResponse> taskCompletion; + + public void OnSuccess(Task<WebResponse> task) + { + taskCompletion.TrySetResult(task.Result); + } + + public void OnError(Task<WebResponse> task) + { + if (task.Exception != null) + { + taskCompletion.TrySetException(task.Exception); + } + else + { + taskCompletion.TrySetException(new List<Exception>()); + } + } + } } } diff --git a/MediaBrowser.Common.Implementations/IO/CommonFileSystem.cs b/MediaBrowser.Common.Implementations/IO/CommonFileSystem.cs index e9ef84663..b899e88c2 100644 --- a/MediaBrowser.Common.Implementations/IO/CommonFileSystem.cs +++ b/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); + File.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,110 @@ namespace MediaBrowser.Common.Implementations.IO //return Path.IsPathRooted(path); } - public void DeleteFile(string path, bool sendToRecycleBin) + public void DeleteFile(string path) { File.Delete(path); } - public void DeleteDirectory(string path, bool recursive, bool sendToRecycleBin) + public void DeleteDirectory(string path, bool recursive) { 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)); + } + + public Stream OpenRead(string path) + { + return File.OpenRead(path); } - public void DeleteFile(string path) + public void CopyFile(string source, string target, bool overwrite) { - DeleteFile(path, false); + File.Copy(source, target, overwrite); } - public void DeleteDirectory(string path, bool recursive) + public void MoveFile(string source, string target) + { + File.Move(source, target); + } + + public void MoveDirectory(string source, string target) + { + Directory.Move(source, target); + } + + public bool DirectoryExists(string path) + { + return Directory.Exists(path); + } + + public bool FileExists(string path) + { + return File.Exists(path); + } + + public string ReadAllText(string path) + { + return File.ReadAllText(path); + } + + public void WriteAllText(string path, string text, Encoding encoding) + { + File.WriteAllText(path, text, encoding); + } + + public void WriteAllText(string path, string text) + { + File.WriteAllText(path, text); + } + + public string ReadAllText(string path, Encoding encoding) + { + return File.ReadAllText(path, encoding); + } + + public IEnumerable<string> GetDirectoryPaths(string path, bool recursive = false) + { + var searchOption = recursive ? SearchOption.AllDirectories : SearchOption.TopDirectoryOnly; + return Directory.EnumerateDirectories(path, "*", searchOption); + } + + public IEnumerable<string> GetFilePaths(string path, bool recursive = false) + { + var searchOption = recursive ? SearchOption.AllDirectories : SearchOption.TopDirectoryOnly; + return Directory.EnumerateFiles(path, "*", searchOption); + } + + public IEnumerable<string> GetFileSystemEntryPaths(string path, bool recursive = false) { - DeleteDirectory(path, recursive, false); + var searchOption = recursive ? SearchOption.AllDirectories : SearchOption.TopDirectoryOnly; + return Directory.EnumerateFileSystemEntries(path, "*", searchOption); } } } diff --git a/MediaBrowser.Common.Implementations/Logging/NlogManager.cs b/MediaBrowser.Common.Implementations/Logging/NlogManager.cs index 698792802..391e7c212 100644 --- a/MediaBrowser.Common.Implementations/Logging/NlogManager.cs +++ b/MediaBrowser.Common.Implementations/Logging/NlogManager.cs @@ -170,7 +170,7 @@ namespace MediaBrowser.Common.Implementations.Logging /// </summary> /// <param name="name">The name.</param> /// <returns>ILogger.</returns> - public ILogger GetLogger(string name) + public Model.Logging.ILogger GetLogger(string name) { return new NLogger(name, this); } @@ -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)); + Directory.CreateDirectory(Path.GetDirectoryName(LogFilePath)); AddFileTarget(LogFilePath, level); diff --git a/MediaBrowser.Common.Implementations/MediaBrowser.Common.Implementations.csproj b/MediaBrowser.Common.Implementations/MediaBrowser.Common.Implementations.csproj index e603ea373..eb1122902 100644 --- a/MediaBrowser.Common.Implementations/MediaBrowser.Common.Implementations.csproj +++ b/MediaBrowser.Common.Implementations/MediaBrowser.Common.Implementations.csproj @@ -48,18 +48,17 @@ <RunPostBuildEvent>Always</RunPostBuildEvent> </PropertyGroup> <ItemGroup> - <Reference Include="NLog, Version=3.2.1.0, Culture=neutral, PublicKeyToken=5120e14c03d0593c, processorArchitecture=MSIL"> + <Reference Include="NLog, Version=4.0.0.0, Culture=neutral, PublicKeyToken=5120e14c03d0593c, processorArchitecture=MSIL"> <SpecificVersion>False</SpecificVersion> - <HintPath>..\packages\NLog.3.2.1\lib\net45\NLog.dll</HintPath> + <HintPath>..\packages\NLog.4.1.1\lib\net45\NLog.dll</HintPath> </Reference> <Reference Include="SharpCompress, Version=0.10.2.0, Culture=neutral, PublicKeyToken=beaf6f427e128133, processorArchitecture=MSIL"> <SpecificVersion>False</SpecificVersion> <HintPath>..\ThirdParty\SharpCompress\SharpCompress.dll</HintPath> </Reference> - <Reference Include="SimpleInjector, Version=2.7.0.0, Culture=neutral, PublicKeyToken=984cb50dea722e99, processorArchitecture=MSIL"> + <Reference Include="SimpleInjector, Version=2.8.0.0, Culture=neutral, PublicKeyToken=984cb50dea722e99, processorArchitecture=MSIL"> <SpecificVersion>False</SpecificVersion> - <HintPath>..\packages\SimpleInjector.2.8.0\lib\net45\SimpleInjector.dll</HintPath> - <Private>True</Private> + <HintPath>..\packages\SimpleInjector.3.0.5\lib\net45\SimpleInjector.dll</HintPath> </Reference> <Reference Include="System" /> <Reference Include="System.Core" /> diff --git a/MediaBrowser.Common.Implementations/ScheduledTasks/ScheduledTaskWorker.cs b/MediaBrowser.Common.Implementations/ScheduledTasks/ScheduledTaskWorker.cs index cbd1c1ac5..906184c75 100644 --- a/MediaBrowser.Common.Implementations/ScheduledTasks/ScheduledTaskWorker.cs +++ b/MediaBrowser.Common.Implementations/ScheduledTasks/ScheduledTaskWorker.cs @@ -12,6 +12,7 @@ using System.IO; using System.Linq; using System.Threading; using System.Threading.Tasks; +using MediaBrowser.Common.IO; namespace MediaBrowser.Common.Implementations.ScheduledTasks { @@ -51,6 +52,7 @@ namespace MediaBrowser.Common.Implementations.ScheduledTasks /// </summary> /// <value>The task manager.</value> private ITaskManager TaskManager { get; set; } + private readonly IFileSystem _fileSystem; /// <summary> /// Initializes a new instance of the <see cref="ScheduledTaskWorker" /> class. @@ -71,7 +73,7 @@ namespace MediaBrowser.Common.Implementations.ScheduledTasks /// or /// logger /// </exception> - public ScheduledTaskWorker(IScheduledTask scheduledTask, IApplicationPaths applicationPaths, ITaskManager taskManager, IJsonSerializer jsonSerializer, ILogger logger) + public ScheduledTaskWorker(IScheduledTask scheduledTask, IApplicationPaths applicationPaths, ITaskManager taskManager, IJsonSerializer jsonSerializer, ILogger logger, IFileSystem fileSystem) { if (scheduledTask == null) { @@ -99,6 +101,7 @@ namespace MediaBrowser.Common.Implementations.ScheduledTasks TaskManager = taskManager; JsonSerializer = jsonSerializer; Logger = logger; + _fileSystem = fileSystem; ReloadTriggerEvents(true); } @@ -154,7 +157,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 +555,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); } diff --git a/MediaBrowser.Common.Implementations/ScheduledTasks/TaskManager.cs b/MediaBrowser.Common.Implementations/ScheduledTasks/TaskManager.cs index de7987bd2..9419bdf22 100644 --- a/MediaBrowser.Common.Implementations/ScheduledTasks/TaskManager.cs +++ b/MediaBrowser.Common.Implementations/ScheduledTasks/TaskManager.cs @@ -10,6 +10,7 @@ using System.Collections.Concurrent; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; +using MediaBrowser.Common.IO; namespace MediaBrowser.Common.Implementations.ScheduledTasks { @@ -50,6 +51,7 @@ namespace MediaBrowser.Common.Implementations.ScheduledTasks /// </summary> /// <value>The logger.</value> private ILogger Logger { get; set; } + private readonly IFileSystem _fileSystem; /// <summary> /// Initializes a new instance of the <see cref="TaskManager" /> class. @@ -58,11 +60,12 @@ namespace MediaBrowser.Common.Implementations.ScheduledTasks /// <param name="jsonSerializer">The json serializer.</param> /// <param name="logger">The logger.</param> /// <exception cref="System.ArgumentException">kernel</exception> - public TaskManager(IApplicationPaths applicationPaths, IJsonSerializer jsonSerializer, ILogger logger) + public TaskManager(IApplicationPaths applicationPaths, IJsonSerializer jsonSerializer, ILogger logger, IFileSystem fileSystem) { ApplicationPaths = applicationPaths; JsonSerializer = jsonSerializer; Logger = logger; + _fileSystem = fileSystem; ScheduledTasks = new IScheduledTaskWorker[] { }; } @@ -106,9 +109,16 @@ namespace MediaBrowser.Common.Implementations.ScheduledTasks public void QueueScheduledTask<T>(TaskExecutionOptions options) where T : IScheduledTask { - var scheduledTask = ScheduledTasks.First(t => t.ScheduledTask.GetType() == typeof(T)); + var scheduledTask = ScheduledTasks.FirstOrDefault(t => t.ScheduledTask.GetType() == typeof(T)); - QueueScheduledTask(scheduledTask, options); + if (scheduledTask == null) + { + Logger.Error("Unable to find scheduled task of type {0} in QueueScheduledTask.", typeof(T).Name); + } + else + { + QueueScheduledTask(scheduledTask, options); + } } public void QueueScheduledTask<T>() @@ -124,9 +134,16 @@ namespace MediaBrowser.Common.Implementations.ScheduledTasks /// <param name="options">The task options.</param> public void QueueScheduledTask(IScheduledTask task, TaskExecutionOptions options) { - var scheduledTask = ScheduledTasks.First(t => t.ScheduledTask.GetType() == task.GetType()); + var scheduledTask = ScheduledTasks.FirstOrDefault(t => t.ScheduledTask.GetType() == task.GetType()); - QueueScheduledTask(scheduledTask, options); + if (scheduledTask == null) + { + Logger.Error("Unable to find scheduled task of type {0} in QueueScheduledTask.", task.GetType().Name); + } + else + { + QueueScheduledTask(scheduledTask, options); + } } /// <summary> @@ -161,7 +178,7 @@ namespace MediaBrowser.Common.Implementations.ScheduledTasks var myTasks = ScheduledTasks.ToList(); var list = tasks.ToList(); - myTasks.AddRange(list.Select(t => new ScheduledTaskWorker(t, ApplicationPaths, this, JsonSerializer, Logger))); + myTasks.AddRange(list.Select(t => new ScheduledTaskWorker(t, ApplicationPaths, this, JsonSerializer, Logger, _fileSystem))); ScheduledTasks = myTasks.ToArray(); } diff --git a/MediaBrowser.Common.Implementations/ScheduledTasks/Tasks/DeleteCacheFileTask.cs b/MediaBrowser.Common.Implementations/ScheduledTasks/Tasks/DeleteCacheFileTask.cs index d9c178d8b..ec48bb5dc 100644 --- a/MediaBrowser.Common.Implementations/ScheduledTasks/Tasks/DeleteCacheFileTask.cs +++ b/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)) + foreach (var directory in _fileSystem.GetDirectoryPaths(parent)) { DeleteEmptyFolders(directory); - if (!Directory.EnumerateFileSystemEntries(directory).Any()) + if (!_fileSystem.GetFileSystemEntryPaths(directory).Any()) { - Directory.Delete(directory, false); + _fileSystem.DeleteDirectory(directory, false); } } } diff --git a/MediaBrowser.Common.Implementations/ScheduledTasks/Tasks/DeleteLogFileTask.cs b/MediaBrowser.Common.Implementations/ScheduledTasks/Tasks/DeleteLogFileTask.cs index b2759c52a..ffba3d9da 100644 --- a/MediaBrowser.Common.Implementations/ScheduledTasks/Tasks/DeleteLogFileTask.cs +++ b/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(); diff --git a/MediaBrowser.Common.Implementations/Security/MBLicenseFile.cs b/MediaBrowser.Common.Implementations/Security/MBLicenseFile.cs index 63381efcd..79e558794 100644 --- a/MediaBrowser.Common.Implementations/Security/MBLicenseFile.cs +++ b/MediaBrowser.Common.Implementations/Security/MBLicenseFile.cs @@ -99,7 +99,7 @@ namespace MediaBrowser.Common.Implementations.Security { try { - contents = File.ReadAllLines(licenseFile); + contents = File.ReadAllLines(licenseFile); } catch (DirectoryNotFoundException) { @@ -107,7 +107,7 @@ namespace MediaBrowser.Common.Implementations.Security } catch (FileNotFoundException) { - (File.Create(licenseFile)).Close(); + (File.Create(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); + Directory.CreateDirectory(Path.GetDirectoryName(licenseFile)); + lock (_fileLock) File.WriteAllLines(licenseFile, lines); } } } diff --git a/MediaBrowser.Common.Implementations/Serialization/JsonSerializer.cs b/MediaBrowser.Common.Implementations/Serialization/JsonSerializer.cs index f194b334a..a758a0c1e 100644 --- a/MediaBrowser.Common.Implementations/Serialization/JsonSerializer.cs +++ b/MediaBrowser.Common.Implementations/Serialization/JsonSerializer.cs @@ -1,4 +1,5 @@ -using MediaBrowser.Model.Serialization; +using MediaBrowser.Common.IO; +using MediaBrowser.Model.Serialization; using System; using System.IO; @@ -9,8 +10,11 @@ namespace MediaBrowser.Common.Implementations.Serialization /// </summary> public class JsonSerializer : IJsonSerializer { - public JsonSerializer() + private readonly IFileSystem _fileSystem; + + public JsonSerializer(IFileSystem fileSystem) { + _fileSystem = fileSystem; Configure(); } @@ -53,7 +57,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); } diff --git a/MediaBrowser.Common.Implementations/Serialization/XmlSerializer.cs b/MediaBrowser.Common.Implementations/Serialization/XmlSerializer.cs index 04030522f..41a59fb2b 100644 --- a/MediaBrowser.Common.Implementations/Serialization/XmlSerializer.cs +++ b/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); } diff --git a/MediaBrowser.Common.Implementations/Updates/InstallationManager.cs b/MediaBrowser.Common.Implementations/Updates/InstallationManager.cs index 5f205d69e..12afdd8d7 100644 --- a/MediaBrowser.Common.Implementations/Updates/InstallationManager.cs +++ b/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); + File.WriteAllText(target + ".ver", package.versionStr); } } catch (IOException e) diff --git a/MediaBrowser.Common.Implementations/packages.config b/MediaBrowser.Common.Implementations/packages.config index e57c874f2..151a3a36d 100644 --- a/MediaBrowser.Common.Implementations/packages.config +++ b/MediaBrowser.Common.Implementations/packages.config @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="utf-8"?> <packages> - <package id="NLog" version="3.2.1" targetFramework="net45" /> - <package id="SimpleInjector" version="2.8.0" targetFramework="net45" /> + <package id="NLog" version="4.1.0" targetFramework="net45" /> + <package id="SimpleInjector" version="3.0.5" targetFramework="net45" /> </packages> diff --git a/MediaBrowser.Common/IO/IFileSystem.cs b/MediaBrowser.Common/IO/IFileSystem.cs index 5ce84f436..ce15ad999 100644 --- a/MediaBrowser.Common/IO/IFileSystem.cs +++ b/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. @@ -138,21 +142,6 @@ namespace MediaBrowser.Common.IO /// 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> - /// <param name="path">The path.</param> void DeleteFile(string path); /// <summary> @@ -161,5 +150,37 @@ 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); + + void WriteAllText(string path, string text); + + void WriteAllText(string path, string text, Encoding encoding); + + string ReadAllText(string path, Encoding encoding); + + IEnumerable<string> GetDirectoryPaths(string path, bool recursive = false); + + IEnumerable<string> GetFilePaths(string path, bool recursive = false); + + IEnumerable<string> GetFileSystemEntryPaths(string path, bool recursive = false); } } diff --git a/MediaBrowser.Common/ScheduledTasks/IntervalTrigger.cs b/MediaBrowser.Common/ScheduledTasks/IntervalTrigger.cs index 864131eef..8bcefed58 100644 --- a/MediaBrowser.Common/ScheduledTasks/IntervalTrigger.cs +++ b/MediaBrowser.Common/ScheduledTasks/IntervalTrigger.cs @@ -51,8 +51,8 @@ namespace MediaBrowser.Common.ScheduledTasks DisposeTimer(); var triggerDate = lastResult != null ? - lastResult.EndTimeUtc.Add(Interval) : - DateTime.UtcNow.Add(FirstRunDelay); + lastResult.EndTimeUtc.Add(Interval) : + DateTime.UtcNow.Add(FirstRunDelay); if (DateTime.UtcNow > triggerDate) { @@ -62,7 +62,7 @@ namespace MediaBrowser.Common.ScheduledTasks } else { - triggerDate = DateTime.UtcNow.Add(Interval); + triggerDate = DateTime.UtcNow.AddSeconds(10); } } diff --git a/MediaBrowser.Controller/Channels/Channel.cs b/MediaBrowser.Controller/Channels/Channel.cs index f0328a1d8..839b7d68f 100644 --- a/MediaBrowser.Controller/Channels/Channel.cs +++ b/MediaBrowser.Controller/Channels/Channel.cs @@ -10,8 +10,6 @@ namespace MediaBrowser.Controller.Channels { public class Channel : Folder { - public string OriginalChannelName { get; set; } - public override bool IsVisible(User user) { if (user.Policy.BlockedChannels != null) diff --git a/MediaBrowser.Controller/Channels/ChannelVideoItem.cs b/MediaBrowser.Controller/Channels/ChannelVideoItem.cs index ca5e343f8..3a9c1f9d0 100644 --- a/MediaBrowser.Controller/Channels/ChannelVideoItem.cs +++ b/MediaBrowser.Controller/Channels/ChannelVideoItem.cs @@ -115,7 +115,11 @@ namespace MediaBrowser.Controller.Channels var info = GetItemLookupInfo<ChannelItemLookupInfo>(); info.ContentType = ContentType; - info.ExtraType = ExtraType; + + if (ExtraType.HasValue) + { + info.ExtraType = ExtraType.Value; + } return info; } diff --git a/MediaBrowser.Controller/Channels/IChannelFactory.cs b/MediaBrowser.Controller/Channels/IChannelFactory.cs deleted file mode 100644 index c7ed92586..000000000 --- a/MediaBrowser.Controller/Channels/IChannelFactory.cs +++ /dev/null @@ -1,14 +0,0 @@ -using System.Collections.Generic; - -namespace MediaBrowser.Controller.Channels -{ - public interface IChannelFactory - { - IEnumerable<IChannel> GetChannels(); - } - - public interface IFactoryChannel - { - - } -}
\ No newline at end of file diff --git a/MediaBrowser.Controller/Channels/IChannelManager.cs b/MediaBrowser.Controller/Channels/IChannelManager.cs index 8d3e0f596..fec550df8 100644 --- a/MediaBrowser.Controller/Channels/IChannelManager.cs +++ b/MediaBrowser.Controller/Channels/IChannelManager.cs @@ -16,7 +16,7 @@ namespace MediaBrowser.Controller.Channels /// </summary> /// <param name="channels">The channels.</param> /// <param name="factories">The factories.</param> - void AddParts(IEnumerable<IChannel> channels, IEnumerable<IChannelFactory> factories); + void AddParts(IEnumerable<IChannel> channels); /// <summary> /// Gets the channel download path. diff --git a/MediaBrowser.Controller/Channels/IChannelMediaItem.cs b/MediaBrowser.Controller/Channels/IChannelMediaItem.cs index 50df07e72..60a29da90 100644 --- a/MediaBrowser.Controller/Channels/IChannelMediaItem.cs +++ b/MediaBrowser.Controller/Channels/IChannelMediaItem.cs @@ -11,7 +11,7 @@ namespace MediaBrowser.Controller.Channels ChannelMediaContentType ContentType { get; set; } - ExtraType ExtraType { get; set; } + ExtraType? ExtraType { get; set; } List<ChannelMediaInfo> ChannelMediaSources { get; set; } } diff --git a/MediaBrowser.Controller/Entities/Audio/Audio.cs b/MediaBrowser.Controller/Entities/Audio/Audio.cs index 623329ca6..43b980c20 100644 --- a/MediaBrowser.Controller/Entities/Audio/Audio.cs +++ b/MediaBrowser.Controller/Entities/Audio/Audio.cs @@ -24,14 +24,20 @@ namespace MediaBrowser.Controller.Entities.Audio IThemeMedia, IArchivable { - public string FormatName { get; set; } public long? Size { get; set; } public string Container { get; set; } public int? TotalBitrate { get; set; } public List<string> Tags { get; set; } - public ExtraType ExtraType { get; set; } + public ExtraType? ExtraType { get; set; } - public bool IsThemeMedia { get; set; } + [IgnoreDataMember] + public bool IsThemeMedia + { + get + { + return ExtraType.HasValue && ExtraType.Value == Model.Entities.ExtraType.ThemeSong; + } + } public Audio() { @@ -46,12 +52,6 @@ namespace MediaBrowser.Controller.Entities.Audio get { return LocationType == LocationType.FileSystem && RunTimeTicks.HasValue; } } - /// <summary> - /// Gets or sets a value indicating whether this instance has embedded image. - /// </summary> - /// <value><c>true</c> if this instance has embedded image; otherwise, <c>false</c>.</value> - public bool HasEmbeddedImage { get; set; } - [IgnoreDataMember] protected override bool SupportsOwnedItems { @@ -212,8 +212,7 @@ namespace MediaBrowser.Controller.Entities.Audio Path = enablePathSubstituion ? GetMappedPath(i.Path, locationType) : i.Path, RunTimeTicks = i.RunTimeTicks, Container = i.Container, - Size = i.Size, - Formats = (i.FormatName ?? string.Empty).Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries).ToList() + Size = i.Size }; if (string.IsNullOrEmpty(info.Container)) diff --git a/MediaBrowser.Controller/Entities/BaseItem.cs b/MediaBrowser.Controller/Entities/BaseItem.cs index 594b5ca93..5403c16dd 100644 --- a/MediaBrowser.Controller/Entities/BaseItem.cs +++ b/MediaBrowser.Controller/Entities/BaseItem.cs @@ -38,7 +38,6 @@ namespace MediaBrowser.Controller.Entities ProviderIds = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase); LockedFields = new List<MetadataFields>(); ImageInfos = new List<ItemImageInfo>(); - Identities = new List<IItemIdentity>(); } /// <summary> @@ -56,12 +55,16 @@ namespace MediaBrowser.Controller.Entities public static string ThemeSongFilename = "theme"; public static string ThemeVideosFolderName = "backdrops"; + public string PreferredMetadataCountryCode { get; set; } + public string PreferredMetadataLanguage { get; set; } + public List<ItemImageInfo> ImageInfos { get; set; } /// <summary> /// Gets or sets the channel identifier. /// </summary> /// <value>The channel identifier.</value> + [IgnoreDataMember] public string ChannelId { get; set; } [IgnoreDataMember] @@ -183,7 +186,7 @@ namespace MediaBrowser.Controller.Entities { // Local trailer, special feature, theme video, etc. // An item that belongs to another item but is not part of the Parent-Child tree - return !IsFolder && Parent == null && LocationType == LocationType.FileSystem; + return !IsFolder && ParentId == Guid.Empty && LocationType == LocationType.FileSystem; } } @@ -331,31 +334,9 @@ namespace MediaBrowser.Controller.Entities return Name; } - /// <summary> - /// Returns true if this item should not attempt to fetch metadata - /// </summary> - /// <value><c>true</c> if [dont fetch meta]; otherwise, <c>false</c>.</value> - [Obsolete("Please use IsLocked instead of DontFetchMeta")] - public bool DontFetchMeta { get; set; } - - [IgnoreDataMember] - public bool IsLocked - { - get - { - return DontFetchMeta; - } - set - { - DontFetchMeta = value; - } - } - - public bool IsUnidentified { get; set; } - [IgnoreDataMember] - public List<IItemIdentity> Identities { get; set; } - + public bool IsLocked { get; set; } + /// <summary> /// Gets or sets the locked fields. /// </summary> @@ -484,7 +465,6 @@ namespace MediaBrowser.Controller.Entities public Guid ParentId { get; set; } - private Folder _parent; /// <summary> /// Gets or sets the parent. /// </summary> @@ -494,11 +474,6 @@ namespace MediaBrowser.Controller.Entities { get { - if (_parent != null) - { - return _parent; - } - if (ParentId != Guid.Empty) { return LibraryManager.GetItemById(ParentId) as Folder; @@ -506,12 +481,14 @@ namespace MediaBrowser.Controller.Entities return null; } - set { _parent = value; } + set + { + + } } public void SetParent(Folder parent) { - Parent = parent; ParentId = parent == null ? Guid.Empty : parent.Id; } @@ -558,6 +535,7 @@ namespace MediaBrowser.Controller.Entities /// Gets or sets the end date. /// </summary> /// <value>The end date.</value> + [IgnoreDataMember] public DateTime? EndDate { get; set; } /// <summary> @@ -582,6 +560,7 @@ namespace MediaBrowser.Controller.Entities /// Gets or sets the custom rating. /// </summary> /// <value>The custom rating.</value> + [IgnoreDataMember] public string CustomRating { get; set; } /// <summary> @@ -591,12 +570,6 @@ namespace MediaBrowser.Controller.Entities public string Overview { get; set; } /// <summary> - /// Gets or sets the people. - /// </summary> - /// <value>The people.</value> - public List<PersonInfo> People { get; set; } - - /// <summary> /// Gets or sets the studios. /// </summary> /// <value>The studios.</value> @@ -618,6 +591,7 @@ namespace MediaBrowser.Controller.Entities /// Gets or sets the community rating. /// </summary> /// <value>The community rating.</value> + [IgnoreDataMember] public float? CommunityRating { get; set; } /// <summary> @@ -643,6 +617,7 @@ namespace MediaBrowser.Controller.Entities /// This could be episode number, album track number, etc. /// </summary> /// <value>The index number.</value> + [IgnoreDataMember] public int? IndexNumber { get; set; } /// <summary> @@ -705,7 +680,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 @@ -741,7 +716,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>() @@ -765,7 +740,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> @@ -902,7 +877,7 @@ namespace MediaBrowser.Controller.Entities if (!i.IsThemeMedia) { - i.IsThemeMedia = true; + i.ExtraType = ExtraType.ThemeVideo; subOptions.ForceSave = true; } @@ -932,7 +907,7 @@ namespace MediaBrowser.Controller.Entities if (!i.IsThemeMedia) { - i.IsThemeMedia = true; + i.ExtraType = ExtraType.ThemeSong; subOptions.ForceSave = true; } @@ -999,18 +974,11 @@ namespace MediaBrowser.Controller.Entities /// <returns>System.String.</returns> public string GetPreferredMetadataLanguage() { - string lang = null; - - var hasLang = this as IHasPreferredMetadataLanguage; - - if (hasLang != null) - { - lang = hasLang.PreferredMetadataLanguage; - } + string lang = PreferredMetadataLanguage; if (string.IsNullOrWhiteSpace(lang)) { - lang = Parents.OfType<IHasPreferredMetadataLanguage>() + lang = Parents .Select(i => i.PreferredMetadataLanguage) .FirstOrDefault(i => !string.IsNullOrWhiteSpace(i)); } @@ -1036,18 +1004,11 @@ namespace MediaBrowser.Controller.Entities /// <returns>System.String.</returns> public string GetPreferredMetadataCountryCode() { - string lang = null; - - var hasLang = this as IHasPreferredMetadataLanguage; - - if (hasLang != null) - { - lang = hasLang.PreferredMetadataCountryCode; - } + string lang = PreferredMetadataCountryCode; if (string.IsNullOrWhiteSpace(lang)) { - lang = Parents.OfType<IHasPreferredMetadataLanguage>() + lang = Parents .Select(i => i.PreferredMetadataCountryCode) .FirstOrDefault(i => !string.IsNullOrWhiteSpace(i)); } @@ -1114,7 +1075,7 @@ namespace MediaBrowser.Controller.Entities // Could not determine the integer value if (!value.HasValue) { - return true; + return !GetBlockUnratedValue(user.Policy); } return value.Value <= maxAllowedRating.Value; @@ -1418,7 +1379,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); } @@ -1635,7 +1596,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(); diff --git a/MediaBrowser.Controller/Entities/Book.cs b/MediaBrowser.Controller/Entities/Book.cs index e59db67a6..d31675baf 100644 --- a/MediaBrowser.Controller/Entities/Book.cs +++ b/MediaBrowser.Controller/Entities/Book.cs @@ -7,7 +7,7 @@ using MediaBrowser.Model.Users; namespace MediaBrowser.Controller.Entities { - public class Book : BaseItem, IHasTags, IHasPreferredMetadataLanguage, IHasLookupInfo<BookInfo>, IHasSeries + public class Book : BaseItem, IHasTags, IHasLookupInfo<BookInfo>, IHasSeries { public override string MediaType { @@ -25,14 +25,6 @@ namespace MediaBrowser.Controller.Entities public string SeriesName { get; set; } - public string PreferredMetadataLanguage { get; set; } - - /// <summary> - /// Gets or sets the preferred metadata country code. - /// </summary> - /// <value>The preferred metadata country code.</value> - public string PreferredMetadataCountryCode { get; set; } - public Book() { Tags = new List<string>(); diff --git a/MediaBrowser.Controller/Entities/Folder.cs b/MediaBrowser.Controller/Entities/Folder.cs index c3ac77328..401ca66de 100644 --- a/MediaBrowser.Controller/Entities/Folder.cs +++ b/MediaBrowser.Controller/Entities/Folder.cs @@ -19,7 +19,7 @@ namespace MediaBrowser.Controller.Entities /// <summary> /// Class Folder /// </summary> - public class Folder : BaseItem, IHasThemeMedia, IHasTags, IHasPreferredMetadataLanguage + public class Folder : BaseItem, IHasThemeMedia, IHasTags { public static IUserManager UserManager { get; set; } public static IUserViewManager UserViewManager { get; set; } @@ -28,14 +28,6 @@ namespace MediaBrowser.Controller.Entities public List<Guid> ThemeVideoIds { get; set; } public List<string> Tags { get; set; } - public string PreferredMetadataLanguage { get; set; } - - /// <summary> - /// Gets or sets the preferred metadata country code. - /// </summary> - /// <value>The preferred metadata country code.</value> - public string PreferredMetadataCountryCode { get; set; } - public Folder() { LinkedChildren = new List<LinkedChild>(); @@ -48,7 +40,7 @@ namespace MediaBrowser.Controller.Entities [IgnoreDataMember] public virtual bool IsPreSorted { - get { return ConfigurationManager.Configuration.EnableWindowsShortcuts; } + get { return false; } } /// <summary> @@ -120,7 +112,7 @@ namespace MediaBrowser.Controller.Entities [IgnoreDataMember] protected virtual bool SupportsShortcutChildren { - get { return false; } + get { return ConfigurationManager.Configuration.EnableWindowsShortcuts; } } /// <summary> @@ -371,7 +363,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> @@ -474,7 +466,7 @@ namespace MediaBrowser.Controller.Entities currentChild.DateModified = child.DateModified; } - currentChild.IsOffline = false; + await UpdateIsOffline(currentChild, false).ConfigureAwait(false); validChildren.Add(currentChild); } else @@ -509,12 +501,12 @@ namespace MediaBrowser.Controller.Entities else if (!string.IsNullOrEmpty(item.Path) && IsPathOffline(item.Path)) { - item.IsOffline = true; + await UpdateIsOffline(item, true).ConfigureAwait(false); validChildren.Add(item); } else { - item.IsOffline = false; + await UpdateIsOffline(item, false).ConfigureAwait(false); actualRemovals.Add(item); } } @@ -569,6 +561,17 @@ namespace MediaBrowser.Controller.Entities progress.Report(100); } + private Task UpdateIsOffline(BaseItem item, bool newValue) + { + if (item.IsOffline != newValue) + { + item.IsOffline = newValue; + return item.UpdateToRepository(ItemUpdateType.None, CancellationToken.None); + } + + return Task.FromResult(true); + } + private async Task RefreshMetadataRecursive(MetadataRefreshOptions refreshOptions, bool recursive, IProgress<double> progress, CancellationToken cancellationToken) { var children = ActualChildren.ToList(); @@ -691,9 +694,9 @@ namespace MediaBrowser.Controller.Entities /// </summary> /// <param name="path">The path.</param> /// <returns><c>true</c> if the specified path is offline; otherwise, <c>false</c>.</returns> - private bool IsPathOffline(string path) + public static bool IsPathOffline(string path) { - if (File.Exists(path)) + if (FileSystem.FileExists(path)) { return false; } @@ -703,7 +706,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; } @@ -725,12 +728,12 @@ namespace MediaBrowser.Controller.Entities /// <param name="folders">The folders.</param> /// <param name="path">The path.</param> /// <returns><c>true</c> if the specified folders contains path; otherwise, <c>false</c>.</returns> - private bool ContainsPath(IEnumerable<VirtualFolderInfo> folders, string path) + private static bool ContainsPath(IEnumerable<VirtualFolderInfo> folders, string path) { return folders.SelectMany(i => i.Locations).Any(i => ContainsPath(i, path)); } - private bool ContainsPath(string parent, string path) + private static bool ContainsPath(string parent, string path) { return string.Equals(parent, path, StringComparison.OrdinalIgnoreCase) || FileSystem.ContainsSubPath(parent, path); } @@ -752,21 +755,24 @@ namespace MediaBrowser.Controller.Entities /// <returns>IEnumerable{BaseItem}.</returns> protected IEnumerable<BaseItem> GetCachedChildren() { - var childrenItems = ItemRepository.GetChildrenItems(Id).Select(RetrieveChild).Where(i => i != null); - - //var children = ItemRepository.GetChildren(Id).Select(RetrieveChild).Where(i => i != null).ToList(); - - //if (children.Count != childrenItems.Count) - //{ - // var b = this; - //} + if (ConfigurationManager.Configuration.DisableStartupScan) + { + return ItemRepository.GetChildrenItems(Id).Select(RetrieveChild).Where(i => i != null); + //return ItemRepository.GetItems(new InternalItemsQuery + //{ + // ParentId = Id - return childrenItems; + //}).Items.Select(RetrieveChild).Where(i => i != null); + } + else + { + return ItemRepository.GetChildrenItems(Id).Select(RetrieveChild).Where(i => i != null); + } } private BaseItem RetrieveChild(BaseItem child) { - if (child.Id == Guid.Empty) + if (child == null || child.Id == Guid.Empty) { Logger.Error("Item found with empty Id: " + (child.Path ?? child.Name)); return null; diff --git a/MediaBrowser.Controller/Entities/Game.cs b/MediaBrowser.Controller/Entities/Game.cs index 15d2d755a..ed3e85d58 100644 --- a/MediaBrowser.Controller/Entities/Game.cs +++ b/MediaBrowser.Controller/Entities/Game.cs @@ -8,19 +8,11 @@ using System.Linq; namespace MediaBrowser.Controller.Entities { - public class Game : BaseItem, IHasTrailers, IHasThemeMedia, IHasTags, IHasScreenshots, ISupportsPlaceHolders, IHasPreferredMetadataLanguage, IHasLookupInfo<GameInfo> + public class Game : BaseItem, IHasTrailers, IHasThemeMedia, IHasTags, IHasScreenshots, ISupportsPlaceHolders, IHasLookupInfo<GameInfo> { public List<Guid> ThemeSongIds { get; set; } public List<Guid> ThemeVideoIds { get; set; } - public string PreferredMetadataLanguage { get; set; } - - /// <summary> - /// Gets or sets the preferred metadata country code. - /// </summary> - /// <value>The preferred metadata country code.</value> - public string PreferredMetadataCountryCode { get; set; } - public Game() { MultiPartGameFiles = new List<string>(); diff --git a/MediaBrowser.Controller/Entities/IHasMetadata.cs b/MediaBrowser.Controller/Entities/IHasMetadata.cs index 158bcb6d1..b8c3e2823 100644 --- a/MediaBrowser.Controller/Entities/IHasMetadata.cs +++ b/MediaBrowser.Controller/Entities/IHasMetadata.cs @@ -45,17 +45,6 @@ namespace MediaBrowser.Controller.Entities bool BeforeMetadataRefresh(); /// <summary> - /// Gets or sets a value indicating whether this instance is unidentified. - /// </summary> - /// <value><c>true</c> if this instance is unidentified; otherwise, <c>false</c>.</value> - bool IsUnidentified { get; set; } - - /// <summary> - /// Gets the item identities. - /// </summary> - List<IItemIdentity> Identities { get; set; } - - /// <summary> /// Afters the metadata refresh. /// </summary> void AfterMetadataRefresh(); diff --git a/MediaBrowser.Controller/Entities/IHasPreferredMetadataLanguage.cs b/MediaBrowser.Controller/Entities/IHasPreferredMetadataLanguage.cs deleted file mode 100644 index e3a233e49..000000000 --- a/MediaBrowser.Controller/Entities/IHasPreferredMetadataLanguage.cs +++ /dev/null @@ -1,21 +0,0 @@ - -namespace MediaBrowser.Controller.Entities -{ - /// <summary> - /// Interface IHasPreferredMetadataLanguage - /// </summary> - public interface IHasPreferredMetadataLanguage - { - /// <summary> - /// Gets or sets the preferred metadata language. - /// </summary> - /// <value>The preferred metadata language.</value> - string PreferredMetadataLanguage { get; set; } - - /// <summary> - /// Gets or sets the preferred metadata country code. - /// </summary> - /// <value>The preferred metadata country code.</value> - string PreferredMetadataCountryCode { get; set; } - } -} diff --git a/MediaBrowser.Controller/Entities/IHasProgramAttributes.cs b/MediaBrowser.Controller/Entities/IHasProgramAttributes.cs index 9938a4489..1c3270d72 100644 --- a/MediaBrowser.Controller/Entities/IHasProgramAttributes.cs +++ b/MediaBrowser.Controller/Entities/IHasProgramAttributes.cs @@ -15,6 +15,6 @@ namespace MediaBrowser.Controller.Entities bool IsLive { get; set; } bool IsPremiere { get; set; } ProgramAudio? Audio { get; set; } - DateTime? OriginalAirDate { get; set; } + string EpisodeTitle { get; set; } } } diff --git a/MediaBrowser.Controller/Entities/InternalItemsQuery.cs b/MediaBrowser.Controller/Entities/InternalItemsQuery.cs index 0af4972f7..785e2fd2b 100644 --- a/MediaBrowser.Controller/Entities/InternalItemsQuery.cs +++ b/MediaBrowser.Controller/Entities/InternalItemsQuery.cs @@ -52,7 +52,6 @@ namespace MediaBrowser.Controller.Entities public bool? IsHD { get; set; } public bool? IsInBoxSet { get; set; } public bool? IsLocked { get; set; } - public bool? IsUnidentified { get; set; } public bool? IsPlaceHolder { get; set; } public bool? IsYearMismatched { get; set; } @@ -98,7 +97,11 @@ namespace MediaBrowser.Controller.Entities public bool? IsCurrentSchema { get; set; } public bool? HasDeadParentId { get; set; } - + public bool? IsOffline { get; set; } + public LocationType? LocationType { get; set; } + + public Guid? ParentId { get; set; } + public InternalItemsQuery() { Tags = new string[] { }; diff --git a/MediaBrowser.Controller/Entities/TV/Season.cs b/MediaBrowser.Controller/Entities/TV/Season.cs index cfd6b46e0..21b89d7a9 100644 --- a/MediaBrowser.Controller/Entities/TV/Season.cs +++ b/MediaBrowser.Controller/Entities/TV/Season.cs @@ -62,17 +62,13 @@ namespace MediaBrowser.Controller.Entities.TV } /// <summary> - /// The _series - /// </summary> - private Series _series; - /// <summary> /// This Episode's Series Instance /// </summary> /// <value>The series.</value> [IgnoreDataMember] public Series Series { - get { return _series ?? (_series = FindParent<Series>()); } + get { return FindParent<Series>(); } } [IgnoreDataMember] diff --git a/MediaBrowser.Controller/Entities/TV/Series.cs b/MediaBrowser.Controller/Entities/TV/Series.cs index 2663d19e8..b23833845 100644 --- a/MediaBrowser.Controller/Entities/TV/Series.cs +++ b/MediaBrowser.Controller/Entities/TV/Series.cs @@ -20,7 +20,6 @@ namespace MediaBrowser.Controller.Entities.TV public List<Guid> SpecialFeatureIds { get; set; } public string OriginalTitle { get; set; } - public int SeasonCount { get; set; } public int? AnimeSeriesIndex { get; set; } diff --git a/MediaBrowser.Controller/Entities/User.cs b/MediaBrowser.Controller/Entities/User.cs index 71e3d1ce0..bf0f7a2a8 100644 --- a/MediaBrowser.Controller/Entities/User.cs +++ b/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, diff --git a/MediaBrowser.Controller/Entities/UserView.cs b/MediaBrowser.Controller/Entities/UserView.cs index 488e54cc3..f4577435f 100644 --- a/MediaBrowser.Controller/Entities/UserView.cs +++ b/MediaBrowser.Controller/Entities/UserView.cs @@ -82,7 +82,27 @@ namespace MediaBrowser.Controller.Entities { CollectionType.Books, CollectionType.HomeVideos, - CollectionType.Photos + CollectionType.Photos, + CollectionType.Playlists, + CollectionType.BoxSets + }; + + var collectionFolder = folder as ICollectionFolder; + + if (collectionFolder == null) + { + return false; + } + + return standaloneTypes.Contains(collectionFolder.CollectionType ?? string.Empty); + } + + public static bool IsUserSpecific(Folder folder) + { + var standaloneTypes = new List<string> + { + CollectionType.Playlists, + CollectionType.BoxSets }; var collectionFolder = folder as ICollectionFolder; diff --git a/MediaBrowser.Controller/Entities/UserViewBuilder.cs b/MediaBrowser.Controller/Entities/UserViewBuilder.cs index cee5dadd2..ce9a8903a 100644 --- a/MediaBrowser.Controller/Entities/UserViewBuilder.cs +++ b/MediaBrowser.Controller/Entities/UserViewBuilder.cs @@ -577,19 +577,9 @@ namespace MediaBrowser.Controller.Entities private async Task<QueryResult<BaseItem>> GetBoxsetView(Folder parent, User user, InternalItemsQuery query) { - return GetResult(GetMediaFolders(user).SelectMany(i => - { - var hasCollectionType = i as ICollectionFolder; - Func<BaseItem, bool> filter = b => b is BoxSet; - - if (hasCollectionType != null && string.Equals(hasCollectionType.CollectionType, CollectionType.BoxSets, StringComparison.OrdinalIgnoreCase)) - { - return i.GetChildren(user, true).Where(filter); - } - - return i.GetRecursiveChildren(user, filter); + var collections = _collectionManager.GetCollections(user); - }), parent, query); + return GetResult(collections, parent, query); } private async Task<QueryResult<BaseItem>> GetPhotosView(Folder queryParent, User user, InternalItemsQuery query) @@ -1041,11 +1031,6 @@ namespace MediaBrowser.Controller.Entities return false; } - if (request.IsUnidentified.HasValue) - { - return false; - } - if (request.IsYearMismatched.HasValue) { return false; @@ -1418,15 +1403,6 @@ namespace MediaBrowser.Controller.Entities } } - if (query.IsUnidentified.HasValue) - { - var val = query.IsUnidentified.Value; - if (item.IsUnidentified != val) - { - return false; - } - } - if (query.IsLocked.HasValue) { var val = query.IsLocked.Value; @@ -1808,6 +1784,13 @@ namespace MediaBrowser.Controller.Entities private IEnumerable<Folder> GetMediaFolders(User user) { + if (user == null) + { + return _libraryManager.RootFolder + .Children + .OfType<Folder>() + .Where(i => !UserView.IsExcludedFromGrouping(i)); + } return user.RootFolder .GetChildren(user, true, true) .OfType<Folder>() @@ -1816,6 +1799,16 @@ namespace MediaBrowser.Controller.Entities private IEnumerable<Folder> GetMediaFolders(User user, IEnumerable<string> viewTypes) { + if (user == null) + { + return GetMediaFolders(null) + .Where(i => + { + var folder = i as ICollectionFolder; + + return folder != null && viewTypes.Contains(folder.CollectionType ?? string.Empty, StringComparer.OrdinalIgnoreCase); + }); + } return GetMediaFolders(user) .Where(i => { @@ -1839,9 +1832,19 @@ namespace MediaBrowser.Controller.Entities { if (parent == null || parent is UserView) { + if (user == null) + { + return GetMediaFolders(null, viewTypes).SelectMany(i => i.GetRecursiveChildren()); + } + return GetMediaFolders(user, viewTypes).SelectMany(i => i.GetRecursiveChildren(user)); } + if (user == null) + { + return parent.GetRecursiveChildren(); + } + return parent.GetRecursiveChildren(user); } @@ -1849,9 +1852,19 @@ namespace MediaBrowser.Controller.Entities { if (parent == null || parent is UserView) { + if (user == null) + { + return GetMediaFolders(null, viewTypes).SelectMany(i => i.GetRecursiveChildren(filter)); + } + return GetMediaFolders(user, viewTypes).SelectMany(i => i.GetRecursiveChildren(user, filter)); } + if (user == null) + { + return parent.GetRecursiveChildren(filter); + } + return parent.GetRecursiveChildren(user, filter); } diff --git a/MediaBrowser.Controller/Entities/Video.cs b/MediaBrowser.Controller/Entities/Video.cs index 00dc5dc67..21405faf1 100644 --- a/MediaBrowser.Controller/Entities/Video.cs +++ b/MediaBrowser.Controller/Entities/Video.cs @@ -23,7 +23,6 @@ namespace MediaBrowser.Controller.Entities ISupportsPlaceHolders, IHasMediaSources, IHasShortOverview, - IHasPreferredMetadataLanguage, IThemeMedia, IArchivable { @@ -33,21 +32,20 @@ namespace MediaBrowser.Controller.Entities public List<string> LocalAlternateVersions { get; set; } public List<LinkedChild> LinkedAlternateVersions { get; set; } - public bool IsThemeMedia { get; set; } + [IgnoreDataMember] + public bool IsThemeMedia + { + get + { + return ExtraType.HasValue && ExtraType.Value == Model.Entities.ExtraType.ThemeVideo; + } + } - public string FormatName { get; set; } public long? Size { get; set; } public string Container { get; set; } public int? TotalBitrate { get; set; } public string ShortOverview { get; set; } - public ExtraType ExtraType { get; set; } - - /// <summary> - /// Gets or sets the preferred metadata country code. - /// </summary> - /// <value>The preferred metadata country code.</value> - public string PreferredMetadataCountryCode { get; set; } - public string PreferredMetadataLanguage { get; set; } + public ExtraType? ExtraType { get; set; } /// <summary> /// Gets or sets the timestamp. @@ -313,7 +311,7 @@ namespace MediaBrowser.Controller.Entities /// <returns>List{System.String}.</returns> public List<string> GetPlayableStreamFiles(string rootPath) { - var allFiles = Directory.EnumerateFiles(rootPath, "*", SearchOption.AllDirectories).ToList(); + var allFiles = FileSystem.GetFilePaths(rootPath, true).ToList(); return PlayableStreamFileNames.Select(name => allFiles.FirstOrDefault(f => string.Equals(System.IO.Path.GetFileName(f), name, StringComparison.OrdinalIgnoreCase))) .Where(f => !string.IsNullOrEmpty(f)) @@ -498,7 +496,6 @@ namespace MediaBrowser.Controller.Entities VideoType = i.VideoType, Container = i.Container, Size = i.Size, - Formats = (i.FormatName ?? string.Empty).Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries).ToList(), Timestamp = i.Timestamp, Type = type, PlayableStreamFileNames = i.PlayableStreamFileNames.ToList(), diff --git a/MediaBrowser.Controller/LiveTv/LiveTvAudioRecording.cs b/MediaBrowser.Controller/LiveTv/LiveTvAudioRecording.cs index 2179c5ecd..a34860c52 100644 --- a/MediaBrowser.Controller/LiveTv/LiveTvAudioRecording.cs +++ b/MediaBrowser.Controller/LiveTv/LiveTvAudioRecording.cs @@ -17,23 +17,32 @@ namespace MediaBrowser.Controller.LiveTv public string ExternalId { get; set; } public string ProviderImagePath { get; set; } public string ProviderImageUrl { get; set; } + [IgnoreDataMember] public string EpisodeTitle { get; set; } + [IgnoreDataMember] public bool IsSeries { get; set; } public string SeriesTimerId { get; set; } + [IgnoreDataMember] public DateTime StartDate { get; set; } public RecordingStatus Status { get; set; } + [IgnoreDataMember] public bool IsSports { get; set; } + [IgnoreDataMember] public bool IsNews { get; set; } + [IgnoreDataMember] public bool IsKids { get; set; } + [IgnoreDataMember] public bool IsRepeat { get; set; } + [IgnoreDataMember] public bool IsMovie { get; set; } public bool? IsHD { get; set; } + [IgnoreDataMember] public bool IsLive { get; set; } + [IgnoreDataMember] public bool IsPremiere { get; set; } public ChannelType ChannelType { get; set; } public string ProgramId { get; set; } public ProgramAudio? Audio { get; set; } - public DateTime? OriginalAirDate { get; set; } /// <summary> /// Gets the user data key. diff --git a/MediaBrowser.Controller/LiveTv/LiveTvProgram.cs b/MediaBrowser.Controller/LiveTv/LiveTvProgram.cs index 12052905f..8f032c522 100644 --- a/MediaBrowser.Controller/LiveTv/LiveTvProgram.cs +++ b/MediaBrowser.Controller/LiveTv/LiveTvProgram.cs @@ -42,12 +42,6 @@ namespace MediaBrowser.Controller.LiveTv public string ExternalId { get; set; } /// <summary> - /// Gets or sets the original air date. - /// </summary> - /// <value>The original air date.</value> - public DateTime? OriginalAirDate { get; set; } - - /// <summary> /// Gets or sets the type of the channel. /// </summary> /// <value>The type of the channel.</value> @@ -56,6 +50,7 @@ namespace MediaBrowser.Controller.LiveTv /// <summary> /// The start date of the program, in UTC. /// </summary> + [IgnoreDataMember] public DateTime StartDate { get; set; } /// <summary> @@ -74,12 +69,14 @@ namespace MediaBrowser.Controller.LiveTv /// Gets or sets a value indicating whether this instance is repeat. /// </summary> /// <value><c>true</c> if this instance is repeat; otherwise, <c>false</c>.</value> + [IgnoreDataMember] public bool IsRepeat { get; set; } /// <summary> /// Gets or sets the episode title. /// </summary> /// <value>The episode title.</value> + [IgnoreDataMember] public string EpisodeTitle { get; set; } /// <summary> @@ -110,42 +107,49 @@ namespace MediaBrowser.Controller.LiveTv /// Gets or sets a value indicating whether this instance is movie. /// </summary> /// <value><c>true</c> if this instance is movie; otherwise, <c>false</c>.</value> + [IgnoreDataMember] public bool IsMovie { get; set; } /// <summary> /// Gets or sets a value indicating whether this instance is sports. /// </summary> /// <value><c>true</c> if this instance is sports; otherwise, <c>false</c>.</value> + [IgnoreDataMember] public bool IsSports { get; set; } /// <summary> /// Gets or sets a value indicating whether this instance is series. /// </summary> /// <value><c>true</c> if this instance is series; otherwise, <c>false</c>.</value> + [IgnoreDataMember] public bool IsSeries { get; set; } /// <summary> /// Gets or sets a value indicating whether this instance is live. /// </summary> /// <value><c>true</c> if this instance is live; otherwise, <c>false</c>.</value> + [IgnoreDataMember] public bool IsLive { get; set; } /// <summary> /// Gets or sets a value indicating whether this instance is news. /// </summary> /// <value><c>true</c> if this instance is news; otherwise, <c>false</c>.</value> + [IgnoreDataMember] public bool IsNews { get; set; } /// <summary> /// Gets or sets a value indicating whether this instance is kids. /// </summary> /// <value><c>true</c> if this instance is kids; otherwise, <c>false</c>.</value> + [IgnoreDataMember] public bool IsKids { get; set; } /// <summary> /// Gets or sets a value indicating whether this instance is premiere. /// </summary> /// <value><c>true</c> if this instance is premiere; otherwise, <c>false</c>.</value> + [IgnoreDataMember] public bool IsPremiere { get; set; } /// <summary> @@ -244,6 +248,7 @@ namespace MediaBrowser.Controller.LiveTv return info; } + [IgnoreDataMember] public override bool SupportsPeople { get diff --git a/MediaBrowser.Controller/LiveTv/LiveTvVideoRecording.cs b/MediaBrowser.Controller/LiveTv/LiveTvVideoRecording.cs index 960f8054a..4218e4911 100644 --- a/MediaBrowser.Controller/LiveTv/LiveTvVideoRecording.cs +++ b/MediaBrowser.Controller/LiveTv/LiveTvVideoRecording.cs @@ -17,23 +17,32 @@ namespace MediaBrowser.Controller.LiveTv public string ExternalId { get; set; } public string ProviderImagePath { get; set; } public string ProviderImageUrl { get; set; } + [IgnoreDataMember] public string EpisodeTitle { get; set; } + [IgnoreDataMember] public bool IsSeries { get; set; } public string SeriesTimerId { get; set; } + [IgnoreDataMember] public DateTime StartDate { get; set; } public RecordingStatus Status { get; set; } + [IgnoreDataMember] public bool IsSports { get; set; } + [IgnoreDataMember] public bool IsNews { get; set; } + [IgnoreDataMember] public bool IsKids { get; set; } + [IgnoreDataMember] public bool IsRepeat { get; set; } + [IgnoreDataMember] public bool IsMovie { get; set; } public bool? IsHD { get; set; } + [IgnoreDataMember] public bool IsLive { get; set; } + [IgnoreDataMember] public bool IsPremiere { get; set; } public ChannelType ChannelType { get; set; } public string ProgramId { get; set; } public ProgramAudio? Audio { get; set; } - public DateTime? OriginalAirDate { get; set; } /// <summary> /// Gets the user data key. diff --git a/MediaBrowser.Controller/MediaBrowser.Controller.csproj b/MediaBrowser.Controller/MediaBrowser.Controller.csproj index 24309734f..b20f15544 100644 --- a/MediaBrowser.Controller/MediaBrowser.Controller.csproj +++ b/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.17511.0, Culture=neutral, PublicKeyToken=384d532d7e88985d, processorArchitecture=MSIL"> - <SpecificVersion>False</SpecificVersion> - <HintPath>..\packages\morelinq.1.1.0\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"> @@ -81,7 +77,6 @@ <Compile Include="Channels\ChannelParentalRating.cs" /> <Compile Include="Channels\ChannelSearchInfo.cs" /> <Compile Include="Channels\IChannel.cs" /> - <Compile Include="Channels\IChannelFactory.cs" /> <Compile Include="Channels\IChannelManager.cs" /> <Compile Include="Channels\IChannelItem.cs" /> <Compile Include="Channels\ChannelAudioItem.cs" /> @@ -149,7 +144,6 @@ <Compile Include="Entities\IHasMediaSources.cs" /> <Compile Include="Entities\IHasMetascore.cs" /> <Compile Include="Entities\IHasOriginalTitle.cs" /> - <Compile Include="Entities\IHasPreferredMetadataLanguage.cs" /> <Compile Include="Entities\IHasProductionLocations.cs" /> <Compile Include="Entities\IHasProgramAttributes.cs" /> <Compile Include="Entities\IHasScreenshots.cs" /> @@ -273,7 +267,6 @@ <Compile Include="Providers\DirectoryService.cs" /> <Compile Include="Providers\DynamicImageInfo.cs" /> <Compile Include="Providers\DynamicImageResponse.cs" /> - <Compile Include="Providers\EpisodeIdentity.cs" /> <Compile Include="Providers\EpisodeInfo.cs" /> <Compile Include="Providers\ExtraInfo.cs" /> <Compile Include="Providers\ExtraSource.cs" /> @@ -287,14 +280,12 @@ <Compile Include="Providers\IForcedProvider.cs" /> <Compile Include="Providers\IHasChangeMonitor.cs" /> <Compile Include="Entities\IHasMetadata.cs" /> - <Compile Include="Providers\IHasIdentities.cs" /> <Compile Include="Providers\IHasItemChangeMonitor.cs" /> <Compile Include="Providers\IHasLookupInfo.cs" /> <Compile Include="Providers\IHasOrder.cs" /> <Compile Include="Providers\IImageFileSaver.cs" /> <Compile Include="Providers\IImageProvider.cs" /> <Compile Include="Providers\IImageSaver.cs" /> - <Compile Include="Providers\IItemIdentity.cs" /> <Compile Include="Providers\IItemIdentityConverter.cs" /> <Compile Include="Providers\IItemIdentityProvider.cs" /> <Compile Include="Providers\ILocalImageFileProvider.cs" /> @@ -319,9 +310,7 @@ <Compile Include="Providers\MusicVideoInfo.cs" /> <Compile Include="Providers\PersonLookupInfo.cs" /> <Compile Include="Providers\RemoteSearchQuery.cs" /> - <Compile Include="Providers\SeasonIdentity.cs" /> <Compile Include="Providers\SeasonInfo.cs" /> - <Compile Include="Providers\SeriesIdentity.cs" /> <Compile Include="Providers\SeriesInfo.cs" /> <Compile Include="Providers\SeriesOrderTypes.cs" /> <Compile Include="Providers\SongInfo.cs" /> @@ -418,8 +407,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> diff --git a/MediaBrowser.Controller/MediaEncoding/IMediaEncoder.cs b/MediaBrowser.Controller/MediaEncoding/IMediaEncoder.cs index 23285b612..427af6f6d 100644 --- a/MediaBrowser.Controller/MediaEncoding/IMediaEncoder.cs +++ b/MediaBrowser.Controller/MediaEncoding/IMediaEncoder.cs @@ -25,6 +25,13 @@ namespace MediaBrowser.Controller.MediaEncoding string Version { get; } /// <summary> + /// Supportses the decoder. + /// </summary> + /// <param name="decoder">The decoder.</param> + /// <returns><c>true</c> if XXXX, <c>false</c> otherwise.</returns> + bool SupportsDecoder(string decoder); + + /// <summary> /// Extracts the audio image. /// </summary> /// <param name="path">The path.</param> diff --git a/MediaBrowser.Controller/MediaEncoding/MediaEncoderHelpers.cs b/MediaBrowser.Controller/MediaEncoding/MediaEncoderHelpers.cs index da9dd4dfd..6b7bddcfa 100644 --- a/MediaBrowser.Controller/MediaEncoding/MediaEncoderHelpers.cs +++ b/MediaBrowser.Controller/MediaEncoding/MediaEncoderHelpers.cs @@ -4,6 +4,7 @@ using System; using System.Collections.Generic; using System.IO; using System.Linq; +using MediaBrowser.Common.IO; namespace MediaBrowser.Controller.MediaEncoding { @@ -15,29 +16,30 @@ namespace MediaBrowser.Controller.MediaEncoding /// <summary> /// Gets the input argument. /// </summary> + /// <param name="fileSystem">The file system.</param> /// <param name="videoPath">The video path.</param> /// <param name="protocol">The protocol.</param> /// <param name="isoMount">The iso mount.</param> /// <param name="playableStreamFileNames">The playable stream file names.</param> /// <returns>System.String[][].</returns> - public static string[] GetInputArgument(string videoPath, MediaProtocol protocol, IIsoMount isoMount, List<string> playableStreamFileNames) + public static string[] GetInputArgument(IFileSystem fileSystem, string videoPath, MediaProtocol protocol, IIsoMount isoMount, List<string> playableStreamFileNames) { if (playableStreamFileNames.Count > 0) { if (isoMount == null) { - return GetPlayableStreamFiles(videoPath, playableStreamFileNames).ToArray(); + return GetPlayableStreamFiles(fileSystem, videoPath, playableStreamFileNames).ToArray(); } - return GetPlayableStreamFiles(isoMount.MountedPath, playableStreamFileNames).ToArray(); + return GetPlayableStreamFiles(fileSystem, isoMount.MountedPath, playableStreamFileNames).ToArray(); } return new[] {videoPath}; } - public static List<string> GetPlayableStreamFiles(string rootPath, IEnumerable<string> filenames) + public static List<string> GetPlayableStreamFiles(IFileSystem fileSystem, string rootPath, IEnumerable<string> filenames) { - var allFiles = Directory - .EnumerateFiles(rootPath, "*", SearchOption.AllDirectories) + var allFiles = fileSystem + .GetFilePaths(rootPath, true) .ToList(); return filenames.Select(name => allFiles.FirstOrDefault(f => string.Equals(Path.GetFileName(f), name, StringComparison.OrdinalIgnoreCase))) diff --git a/MediaBrowser.Controller/Net/IHttpServer.cs b/MediaBrowser.Controller/Net/IHttpServer.cs index 37142af19..91da5fab2 100644 --- a/MediaBrowser.Controller/Net/IHttpServer.cs +++ b/MediaBrowser.Controller/Net/IHttpServer.cs @@ -53,5 +53,10 @@ namespace MediaBrowser.Controller.Net /// Inits this instance. /// </summary> void Init(IEnumerable<IRestfulService> services); + + /// <summary> + /// If set, all requests will respond with this message + /// </summary> + string GlobalResponse { get; set; } } }
\ No newline at end of file diff --git a/MediaBrowser.Controller/Persistence/IItemRepository.cs b/MediaBrowser.Controller/Persistence/IItemRepository.cs index a4b9bf120..8fc0aedd3 100644 --- a/MediaBrowser.Controller/Persistence/IItemRepository.cs +++ b/MediaBrowser.Controller/Persistence/IItemRepository.cs @@ -169,6 +169,13 @@ namespace MediaBrowser.Controller.Persistence /// <param name="query">The query.</param> /// <returns>List<System.String>.</returns> List<string> GetPeopleNames(InternalPeopleQuery query); + + /// <summary> + /// Gets the item ids with path. + /// </summary> + /// <param name="query">The query.</param> + /// <returns>QueryResult<Tuple<Guid, System.String>>.</returns> + QueryResult<Tuple<Guid, string>> GetItemIdsWithPath(InternalItemsQuery query); } } diff --git a/MediaBrowser.Controller/Providers/BaseItemXmlParser.cs b/MediaBrowser.Controller/Providers/BaseItemXmlParser.cs index 383d0881e..2efd6e5c6 100644 --- a/MediaBrowser.Controller/Providers/BaseItemXmlParser.cs +++ b/MediaBrowser.Controller/Providers/BaseItemXmlParser.cs @@ -304,11 +304,7 @@ namespace MediaBrowser.Controller.Providers { var val = reader.ReadElementContentAsString(); - var hasLanguage = item as IHasPreferredMetadataLanguage; - if (hasLanguage != null) - { - hasLanguage.PreferredMetadataLanguage = val; - } + item.PreferredMetadataLanguage = val; break; } @@ -317,11 +313,7 @@ namespace MediaBrowser.Controller.Providers { var val = reader.ReadElementContentAsString(); - var hasLanguage = item as IHasPreferredMetadataLanguage; - if (hasLanguage != null) - { - hasLanguage.PreferredMetadataCountryCode = val; - } + item.PreferredMetadataCountryCode = val; break; } diff --git a/MediaBrowser.Controller/Providers/DirectoryService.cs b/MediaBrowser.Controller/Providers/DirectoryService.cs index 79ffb0d01..d320409f4 100644 --- a/MediaBrowser.Controller/Providers/DirectoryService.cs +++ b/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) diff --git a/MediaBrowser.Controller/Providers/EpisodeIdentity.cs b/MediaBrowser.Controller/Providers/EpisodeIdentity.cs deleted file mode 100644 index 53f469e95..000000000 --- a/MediaBrowser.Controller/Providers/EpisodeIdentity.cs +++ /dev/null @@ -1,12 +0,0 @@ -namespace MediaBrowser.Controller.Providers -{ - public class EpisodeIdentity : IItemIdentity - { - public string Type { get; set; } - - public string SeriesId { get; set; } - public int? SeasonIndex { get; set; } - public int IndexNumber { get; set; } - public int? IndexNumberEnd { get; set; } - } -}
\ No newline at end of file diff --git a/MediaBrowser.Controller/Providers/EpisodeInfo.cs b/MediaBrowser.Controller/Providers/EpisodeInfo.cs index 88a7cbab7..28abd636a 100644 --- a/MediaBrowser.Controller/Providers/EpisodeInfo.cs +++ b/MediaBrowser.Controller/Providers/EpisodeInfo.cs @@ -1,15 +1,10 @@ using System; using System.Collections.Generic; -using System.Linq; -using System.Threading; -using System.Threading.Tasks; namespace MediaBrowser.Controller.Providers { - public class EpisodeInfo : ItemLookupInfo, IHasIdentities<EpisodeIdentity> + public class EpisodeInfo : ItemLookupInfo { - private List<EpisodeIdentity> _identities = new List<EpisodeIdentity>(); - public Dictionary<string, string> SeriesProviderIds { get; set; } public int? IndexNumberEnd { get; set; } @@ -19,16 +14,5 @@ namespace MediaBrowser.Controller.Providers { SeriesProviderIds = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase); } - - public IEnumerable<EpisodeIdentity> Identities - { - get { return _identities; } - } - - public async Task FindIdentities(IProviderManager providerManager, CancellationToken cancellationToken) - { - var identifier = new ItemIdentifier<EpisodeInfo, EpisodeIdentity>(); - _identities = (await identifier.FindIdentities(this, providerManager, cancellationToken)).ToList(); - } } }
\ No newline at end of file diff --git a/MediaBrowser.Controller/Providers/IHasIdentities.cs b/MediaBrowser.Controller/Providers/IHasIdentities.cs deleted file mode 100644 index 36f940dd3..000000000 --- a/MediaBrowser.Controller/Providers/IHasIdentities.cs +++ /dev/null @@ -1,14 +0,0 @@ -using System.Collections.Generic; -using System.Threading; -using System.Threading.Tasks; - -namespace MediaBrowser.Controller.Providers -{ - public interface IHasIdentities<out TIdentity> - where TIdentity : IItemIdentity - { - IEnumerable<TIdentity> Identities { get; } - - Task FindIdentities(IProviderManager providerManager, CancellationToken cancellationToken); - } -}
\ No newline at end of file diff --git a/MediaBrowser.Controller/Providers/IItemIdentity.cs b/MediaBrowser.Controller/Providers/IItemIdentity.cs deleted file mode 100644 index cab189c84..000000000 --- a/MediaBrowser.Controller/Providers/IItemIdentity.cs +++ /dev/null @@ -1,7 +0,0 @@ -namespace MediaBrowser.Controller.Providers -{ - public interface IItemIdentity - { - string Type { get; } - } -}
\ No newline at end of file diff --git a/MediaBrowser.Controller/Providers/IItemIdentityConverter.cs b/MediaBrowser.Controller/Providers/IItemIdentityConverter.cs index 30e96b9e5..bfdd1dbf3 100644 --- a/MediaBrowser.Controller/Providers/IItemIdentityConverter.cs +++ b/MediaBrowser.Controller/Providers/IItemIdentityConverter.cs @@ -1,4 +1,4 @@ namespace MediaBrowser.Controller.Providers { - public interface IItemIdentityConverter : IHasOrder { } + public interface IItemIdentityConverter { } }
\ No newline at end of file diff --git a/MediaBrowser.Controller/Providers/IItemIdentityProvider.cs b/MediaBrowser.Controller/Providers/IItemIdentityProvider.cs index 9d437c208..6b403bb55 100644 --- a/MediaBrowser.Controller/Providers/IItemIdentityProvider.cs +++ b/MediaBrowser.Controller/Providers/IItemIdentityProvider.cs @@ -1,4 +1,4 @@ namespace MediaBrowser.Controller.Providers { - public interface IItemIdentityProvider : IHasOrder { } + public interface IItemIdentityProvider { } }
\ No newline at end of file diff --git a/MediaBrowser.Controller/Providers/IProviderManager.cs b/MediaBrowser.Controller/Providers/IProviderManager.cs index d6fc39c5f..57e4ff320 100644 --- a/MediaBrowser.Controller/Providers/IProviderManager.cs +++ b/MediaBrowser.Controller/Providers/IProviderManager.cs @@ -195,18 +195,16 @@ namespace MediaBrowser.Controller.Providers /// Gets the item identity providers. /// </summary> /// <typeparam name="TLookupInfo">The type of the t lookup information.</typeparam> - /// <typeparam name="TIdentity">The type of the t identity.</typeparam> /// <returns>IEnumerable<IItemIdentityProvider<TLookupInfo, TIdentity>>.</returns> - IEnumerable<IItemIdentityProvider<TLookupInfo, TIdentity>> GetItemIdentityProviders<TLookupInfo, TIdentity>() - where TLookupInfo : ItemLookupInfo - where TIdentity : IItemIdentity; + IEnumerable<IItemIdentityProvider<TLookupInfo>> GetItemIdentityProviders<TLookupInfo>() + where TLookupInfo : ItemLookupInfo; /// <summary> /// Gets the item identity converters. /// </summary> - /// <typeparam name="TIdentity">The type of the t identity.</typeparam> + /// <typeparam name="TLookupInfo">The type of the t lookup information.</typeparam> /// <returns>IEnumerable<IItemIdentityConverter<TIdentity>>.</returns> - IEnumerable<IItemIdentityConverter<TIdentity>> GetItemIdentityConverters<TIdentity>() - where TIdentity : IItemIdentity; + IEnumerable<IItemIdentityConverter<TLookupInfo>> GetItemIdentityConverters<TLookupInfo>() + where TLookupInfo : ItemLookupInfo; } }
\ No newline at end of file diff --git a/MediaBrowser.Controller/Providers/ItemIdentifier.cs b/MediaBrowser.Controller/Providers/ItemIdentifier.cs index 13e4f137f..bbc6dd76c 100644 --- a/MediaBrowser.Controller/Providers/ItemIdentifier.cs +++ b/MediaBrowser.Controller/Providers/ItemIdentifier.cs @@ -1,73 +1,36 @@ -using System.Collections.Generic; -using System.Linq; +using System.Linq; using System.Threading; using System.Threading.Tasks; namespace MediaBrowser.Controller.Providers { - public class ItemIdentifier<TLookupInfo, TIdentity> + public static class ItemIdentifier<TLookupInfo> where TLookupInfo : ItemLookupInfo - where TIdentity : IItemIdentity { - public async Task<IEnumerable<TIdentity>> FindIdentities(TLookupInfo item, IProviderManager providerManager, CancellationToken cancellationToken) + public static async Task FindIdentities(TLookupInfo item, IProviderManager providerManager, CancellationToken cancellationToken) { - var providers = providerManager.GetItemIdentityProviders<TLookupInfo, TIdentity>(); - var converters = providerManager.GetItemIdentityConverters<TIdentity>(); - - var identities = new List<IdentityPair>(); - + var providers = providerManager.GetItemIdentityProviders<TLookupInfo>(); + var converters = providerManager.GetItemIdentityConverters<TLookupInfo>().ToList(); + foreach (var provider in providers) { - var result = new IdentityPair - { - Identity = await provider.FindIdentity(item), - Order = provider.Order - }; - - if (!Equals(result.Identity, default(TIdentity))) - { - identities.Add(result); - } + await provider.Identify(item); } - var convertersAvailable = new List<IItemIdentityConverter<TIdentity>>(converters); - bool changesMade; + bool changesMade = true; - do + while (changesMade) { changesMade = false; - for (int i = convertersAvailable.Count - 1; i >= 0; i--) + foreach (var converter in converters) { - var converter = convertersAvailable[i]; - var input = identities.FirstOrDefault(id => id.Identity.Type == converter.SourceType); - var existing = identities.Where(id => id.Identity.Type == converter.ResultType); - - if (input != null && !existing.Any(id => id.Order <= converter.Order)) + if (await converter.Convert(item)) { - var result = new IdentityPair - { - Identity = await converter.Convert(input.Identity).ConfigureAwait(false), - Order = converter.Order - }; - - if (!Equals(result.Identity, default(TIdentity))) - { - identities.Add(result); - convertersAvailable.RemoveAt(i); - changesMade = true; - } + changesMade = true; } } - } while (changesMade); - - return identities.OrderBy(id => id.Order).Select(id => id.Identity); - } - - private class IdentityPair - { - public TIdentity Identity; - public int Order; + } } } }
\ No newline at end of file diff --git a/MediaBrowser.Controller/Providers/ItemIdentities.cs b/MediaBrowser.Controller/Providers/ItemIdentities.cs index 939fd3b8f..48316d0f4 100644 --- a/MediaBrowser.Controller/Providers/ItemIdentities.cs +++ b/MediaBrowser.Controller/Providers/ItemIdentities.cs @@ -2,20 +2,15 @@ namespace MediaBrowser.Controller.Providers { - public interface IItemIdentityProvider<in TLookupInfo, TIdentity> : IItemIdentityProvider + public interface IItemIdentityProvider<in TLookupInfo> : IItemIdentityProvider where TLookupInfo : ItemLookupInfo - where TIdentity : IItemIdentity { - Task<TIdentity> FindIdentity(TLookupInfo info); + Task Identify(TLookupInfo info); } - public interface IItemIdentityConverter<TIdentity> : IItemIdentityConverter - where TIdentity : IItemIdentity + public interface IItemIdentityConverter<in TLookupInfo> : IItemIdentityConverter + where TLookupInfo : ItemLookupInfo { - Task<TIdentity> Convert(TIdentity identity); - - string SourceType { get; } - - string ResultType { get; } + Task<bool> Convert(TLookupInfo info); } }
\ No newline at end of file diff --git a/MediaBrowser.Controller/Providers/MetadataRefreshOptions.cs b/MediaBrowser.Controller/Providers/MetadataRefreshOptions.cs index dbb7fbfcd..097c613cb 100644 --- a/MediaBrowser.Controller/Providers/MetadataRefreshOptions.cs +++ b/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)) { } diff --git a/MediaBrowser.Controller/Providers/MetadataStatus.cs b/MediaBrowser.Controller/Providers/MetadataStatus.cs index 283b9edbc..f395dabf1 100644 --- a/MediaBrowser.Controller/Providers/MetadataStatus.cs +++ b/MediaBrowser.Controller/Providers/MetadataStatus.cs @@ -78,9 +78,9 @@ namespace MediaBrowser.Controller.Providers public bool IsDirty { get; private set; } - public void SetDateLastMetadataRefresh(DateTime date) + public void SetDateLastMetadataRefresh(DateTime? date) { - if (date != (DateLastMetadataRefresh ?? DateTime.MinValue)) + if (date != DateLastMetadataRefresh) { IsDirty = true; } @@ -88,9 +88,9 @@ namespace MediaBrowser.Controller.Providers DateLastMetadataRefresh = date; } - public void SetDateLastImagesRefresh(DateTime date) + public void SetDateLastImagesRefresh(DateTime? date) { - if (date != (DateLastImagesRefresh ?? DateTime.MinValue)) + if (date != DateLastImagesRefresh) { IsDirty = true; } diff --git a/MediaBrowser.Controller/Providers/SeasonIdentity.cs b/MediaBrowser.Controller/Providers/SeasonIdentity.cs deleted file mode 100644 index 1e6b9b65a..000000000 --- a/MediaBrowser.Controller/Providers/SeasonIdentity.cs +++ /dev/null @@ -1,11 +0,0 @@ -namespace MediaBrowser.Controller.Providers -{ - public class SeasonIdentity : IItemIdentity - { - public string Type { get; set; } - - public string SeriesId { get; set; } - - public int SeasonIndex { get; set; } - } -}
\ No newline at end of file diff --git a/MediaBrowser.Controller/Providers/SeasonInfo.cs b/MediaBrowser.Controller/Providers/SeasonInfo.cs index 17bcd3f77..2c785d7d7 100644 --- a/MediaBrowser.Controller/Providers/SeasonInfo.cs +++ b/MediaBrowser.Controller/Providers/SeasonInfo.cs @@ -1,15 +1,10 @@ using System; using System.Collections.Generic; -using System.Linq; -using System.Threading; -using System.Threading.Tasks; namespace MediaBrowser.Controller.Providers { - public class SeasonInfo : ItemLookupInfo, IHasIdentities<SeasonIdentity> + public class SeasonInfo : ItemLookupInfo { - private List<SeasonIdentity> _identities = new List<SeasonIdentity>(); - public Dictionary<string, string> SeriesProviderIds { get; set; } public int? AnimeSeriesIndex { get; set; } @@ -17,16 +12,5 @@ namespace MediaBrowser.Controller.Providers { SeriesProviderIds = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase); } - - public IEnumerable<SeasonIdentity> Identities - { - get { return _identities; } - } - - public async Task FindIdentities(IProviderManager providerManager, CancellationToken cancellationToken) - { - var identifier = new ItemIdentifier<SeasonInfo, SeasonIdentity>(); - _identities = (await identifier.FindIdentities(this, providerManager, cancellationToken)).ToList(); - } } }
\ No newline at end of file diff --git a/MediaBrowser.Controller/Providers/SeriesIdentity.cs b/MediaBrowser.Controller/Providers/SeriesIdentity.cs deleted file mode 100644 index 326d34027..000000000 --- a/MediaBrowser.Controller/Providers/SeriesIdentity.cs +++ /dev/null @@ -1,9 +0,0 @@ -namespace MediaBrowser.Controller.Providers -{ - public class SeriesIdentity : IItemIdentity - { - public string Type { get; set; } - - public string Id { get; set; } - } -}
\ No newline at end of file diff --git a/MediaBrowser.Controller/Providers/SeriesInfo.cs b/MediaBrowser.Controller/Providers/SeriesInfo.cs index fc1119cd2..387865de2 100644 --- a/MediaBrowser.Controller/Providers/SeriesInfo.cs +++ b/MediaBrowser.Controller/Providers/SeriesInfo.cs @@ -1,25 +1,7 @@ -using System.Collections.Generic; -using System.Linq; -using System.Threading; -using System.Threading.Tasks; - namespace MediaBrowser.Controller.Providers { - public class SeriesInfo : ItemLookupInfo, IHasIdentities<SeriesIdentity> + public class SeriesInfo : ItemLookupInfo { - private List<SeriesIdentity> _identities = new List<SeriesIdentity>(); - public int? AnimeSeriesIndex { get; set; } - - public IEnumerable<SeriesIdentity> Identities - { - get { return _identities; } - } - - public async Task FindIdentities(IProviderManager providerManager, CancellationToken cancellationToken) - { - var identifier = new ItemIdentifier<SeriesInfo, SeriesIdentity>(); - _identities = (await identifier.FindIdentities(this, providerManager, cancellationToken)).ToList(); - } } }
\ No newline at end of file diff --git a/MediaBrowser.Controller/Themes/IAppThemeManager.cs b/MediaBrowser.Controller/Themes/IAppThemeManager.cs deleted file mode 100644 index 1a7c2aaab..000000000 --- a/MediaBrowser.Controller/Themes/IAppThemeManager.cs +++ /dev/null @@ -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); - } -} diff --git a/MediaBrowser.Controller/Themes/InternalThemeImage.cs b/MediaBrowser.Controller/Themes/InternalThemeImage.cs deleted file mode 100644 index 2b676c25b..000000000 --- a/MediaBrowser.Controller/Themes/InternalThemeImage.cs +++ /dev/null @@ -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; } - } -} diff --git a/MediaBrowser.Controller/packages.config b/MediaBrowser.Controller/packages.config index 8846b5a06..c320ed9d9 100644 --- a/MediaBrowser.Controller/packages.config +++ b/MediaBrowser.Controller/packages.config @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="utf-8"?> <packages> <package id="Interfaces.IO" version="1.0.0.5" targetFramework="net45" /> - <package id="morelinq" version="1.1.0" targetFramework="net45" /> + <package id="morelinq" version="1.1.1" targetFramework="net45" /> </packages>
\ No newline at end of file diff --git a/MediaBrowser.Dlna/Channels/DlnaChannelFactory.cs b/MediaBrowser.Dlna/Channels/DlnaChannelFactory.cs index f26ceff90..315313c04 100644 --- a/MediaBrowser.Dlna/Channels/DlnaChannelFactory.cs +++ b/MediaBrowser.Dlna/Channels/DlnaChannelFactory.cs @@ -18,325 +18,325 @@ using System.Threading.Tasks; namespace MediaBrowser.Dlna.Channels { - public class DlnaChannelFactory : IChannelFactory, IDisposable - { - private readonly IServerConfigurationManager _config; - private readonly ILogger _logger; - private readonly IHttpClient _httpClient; - - private readonly IDeviceDiscovery _deviceDiscovery; - - private readonly SemaphoreSlim _syncLock = new SemaphoreSlim(1, 1); - private List<Device> _servers = new List<Device>(); - - public static DlnaChannelFactory Instance; - - private Func<List<string>> _localServersLookup; - - public DlnaChannelFactory(IServerConfigurationManager config, IHttpClient httpClient, ILogger logger, IDeviceDiscovery deviceDiscovery) - { - _config = config; - _httpClient = httpClient; - _logger = logger; - _deviceDiscovery = deviceDiscovery; - Instance = this; - } - - internal void Start(Func<List<string>> localServersLookup) - { - _localServersLookup = localServersLookup; - - //deviceDiscovery.DeviceDiscovered += deviceDiscovery_DeviceDiscovered; - _deviceDiscovery.DeviceLeft += deviceDiscovery_DeviceLeft; - } - - async void deviceDiscovery_DeviceDiscovered(object sender, SsdpMessageEventArgs e) - { - string usn; - if (!e.Headers.TryGetValue("USN", out usn)) usn = string.Empty; - - string nt; - if (!e.Headers.TryGetValue("NT", out nt)) nt = string.Empty; - - string location; - if (!e.Headers.TryGetValue("Location", out location)) location = string.Empty; - - if (!IsValid(nt, usn)) - { - return; - } - - if (_localServersLookup != null) - { - if (_localServersLookup().Any(i => usn.IndexOf(i, StringComparison.OrdinalIgnoreCase) != -1)) - { - // Don't add the local Dlna server to this - return; - } - } - - if (GetExistingServers(usn).Any()) - { - return; - } - - await _syncLock.WaitAsync().ConfigureAwait(false); - - try - { - if (GetExistingServers(usn).Any()) - { - return; - } - - var device = await Device.CreateuPnpDeviceAsync(new Uri(location), _httpClient, _config, _logger) - .ConfigureAwait(false); - - if (!_servers.Any(i => string.Equals(i.Properties.UUID, device.Properties.UUID, StringComparison.OrdinalIgnoreCase))) - { - _servers.Add(device); - } - } - catch (Exception ex) - { - - } - finally - { - _syncLock.Release(); - } - } - - async void deviceDiscovery_DeviceLeft(object sender, SsdpMessageEventArgs e) - { - string usn; - if (!e.Headers.TryGetValue("USN", out usn)) usn = String.Empty; - - string nt; - if (!e.Headers.TryGetValue("NT", out nt)) nt = String.Empty; - - if (!IsValid(nt, usn)) - { - return; - } - - if (!GetExistingServers(usn).Any()) - { - return; - } - - await _syncLock.WaitAsync().ConfigureAwait(false); - - try - { - var list = _servers.ToList(); - - foreach (var device in GetExistingServers(usn).ToList()) - { - list.Remove(device); - } - - _servers = list; - } - finally - { - _syncLock.Release(); - } - } - - private bool IsValid(string nt, string usn) - { - // It has to report that it's a media renderer - if (usn.IndexOf("ContentDirectory:", StringComparison.OrdinalIgnoreCase) == -1 && - nt.IndexOf("ContentDirectory:", StringComparison.OrdinalIgnoreCase) == -1 && - usn.IndexOf("MediaServer:", StringComparison.OrdinalIgnoreCase) == -1 && - nt.IndexOf("MediaServer:", StringComparison.OrdinalIgnoreCase) == -1) - { - return false; - } - - return true; - } - - private IEnumerable<Device> GetExistingServers(string usn) - { - return _servers - .Where(i => usn.IndexOf(i.Properties.UUID, StringComparison.OrdinalIgnoreCase) != -1); - } - - public IEnumerable<IChannel> GetChannels() - { - //if (_servers.Count > 0) - //{ - // var service = _servers[0].Properties.Services - // .FirstOrDefault(i => string.Equals(i.ServiceType, "urn:schemas-upnp-org:service:ContentDirectory:1", StringComparison.OrdinalIgnoreCase)); - - // var controlUrl = service == null ? null : (_servers[0].Properties.BaseUrl.TrimEnd('/') + "/" + service.ControlUrl.TrimStart('/')); - - // if (!string.IsNullOrEmpty(controlUrl)) - // { - // return new List<IChannel> - // { - // new ServerChannel(_servers.ToList(), _httpClient, _logger, controlUrl) - // }; - // } - //} - - return new List<IChannel>(); - } - - public void Dispose() - { - if (_deviceDiscovery != null) - { - _deviceDiscovery.DeviceDiscovered -= deviceDiscovery_DeviceDiscovered; - _deviceDiscovery.DeviceLeft -= deviceDiscovery_DeviceLeft; - } - } - } - - public class ServerChannel : IChannel, IFactoryChannel - { - private readonly IHttpClient _httpClient; - private readonly ILogger _logger; - public string ControlUrl { get; set; } - public List<Device> Servers { get; set; } - - public ServerChannel(IHttpClient httpClient, ILogger logger) - { - _httpClient = httpClient; - _logger = logger; - Servers = new List<Device>(); - } - - public string Name - { - get { return "Devices"; } - } - - public string Description - { - get { return string.Empty; } - } - - public string DataVersion - { - get { return DateTime.UtcNow.Ticks.ToString(); } - } - - public string HomePageUrl - { - get { return string.Empty; } - } - - public ChannelParentalRating ParentalRating - { - get { return ChannelParentalRating.GeneralAudience; } - } - - public InternalChannelFeatures GetChannelFeatures() - { - return new InternalChannelFeatures - { - ContentTypes = new List<ChannelMediaContentType> - { - ChannelMediaContentType.Song, - ChannelMediaContentType.Clip - }, - - MediaTypes = new List<ChannelMediaType> - { - ChannelMediaType.Audio, - ChannelMediaType.Video, - ChannelMediaType.Photo - } - }; - } - - public bool IsEnabledFor(string userId) - { - return true; - } - - public async Task<ChannelItemResult> GetChannelItems(InternalChannelItemQuery query, CancellationToken cancellationToken) - { - IEnumerable<ChannelItemInfo> items; - - if (string.IsNullOrWhiteSpace(query.FolderId)) - { - items = Servers.Select(i => new ChannelItemInfo - { - FolderType = ChannelFolderType.Container, - Id = GetServerId(i), - Name = i.Properties.Name, - Overview = i.Properties.ModelDescription, - Type = ChannelItemType.Folder - }); - } - else - { - var idParts = query.FolderId.Split('|'); - var folderId = idParts.Length == 2 ? idParts[1] : null; - - var result = await new ContentDirectoryBrowser(_httpClient, _logger).Browse(new ContentDirectoryBrowseRequest - { - Limit = query.Limit, - StartIndex = query.StartIndex, - ParentId = folderId, - ContentDirectoryUrl = ControlUrl - - }, cancellationToken).ConfigureAwait(false); - - items = result.Items.ToList(); - } - - var list = items.ToList(); - var count = list.Count; - - list = ApplyPaging(list, query).ToList(); - - return new ChannelItemResult - { - Items = list, - TotalRecordCount = count - }; - } - - private string GetServerId(Device device) - { - return device.Properties.UUID.GetMD5().ToString("N"); - } - - private IEnumerable<T> ApplyPaging<T>(IEnumerable<T> items, InternalChannelItemQuery query) - { - if (query.StartIndex.HasValue) - { - items = items.Skip(query.StartIndex.Value); - } - - if (query.Limit.HasValue) - { - items = items.Take(query.Limit.Value); - } - - return items; - } - - public Task<DynamicImageResponse> GetChannelImage(ImageType type, CancellationToken cancellationToken) - { - // TODO: Implement - return Task.FromResult(new DynamicImageResponse - { - HasImage = false - }); - } - - public IEnumerable<ImageType> GetSupportedChannelImages() - { - return new List<ImageType> - { - ImageType.Primary - }; - } - } + //public class DlnaChannelFactory : IChannelFactory, IDisposable + //{ + // private readonly IServerConfigurationManager _config; + // private readonly ILogger _logger; + // private readonly IHttpClient _httpClient; + + // private readonly IDeviceDiscovery _deviceDiscovery; + + // private readonly SemaphoreSlim _syncLock = new SemaphoreSlim(1, 1); + // private List<Device> _servers = new List<Device>(); + + // public static DlnaChannelFactory Instance; + + // private Func<List<string>> _localServersLookup; + + // public DlnaChannelFactory(IServerConfigurationManager config, IHttpClient httpClient, ILogger logger, IDeviceDiscovery deviceDiscovery) + // { + // _config = config; + // _httpClient = httpClient; + // _logger = logger; + // _deviceDiscovery = deviceDiscovery; + // Instance = this; + // } + + // internal void Start(Func<List<string>> localServersLookup) + // { + // _localServersLookup = localServersLookup; + + // //deviceDiscovery.DeviceDiscovered += deviceDiscovery_DeviceDiscovered; + // _deviceDiscovery.DeviceLeft += deviceDiscovery_DeviceLeft; + // } + + // async void deviceDiscovery_DeviceDiscovered(object sender, SsdpMessageEventArgs e) + // { + // string usn; + // if (!e.Headers.TryGetValue("USN", out usn)) usn = string.Empty; + + // string nt; + // if (!e.Headers.TryGetValue("NT", out nt)) nt = string.Empty; + + // string location; + // if (!e.Headers.TryGetValue("Location", out location)) location = string.Empty; + + // if (!IsValid(nt, usn)) + // { + // return; + // } + + // if (_localServersLookup != null) + // { + // if (_localServersLookup().Any(i => usn.IndexOf(i, StringComparison.OrdinalIgnoreCase) != -1)) + // { + // // Don't add the local Dlna server to this + // return; + // } + // } + + // if (GetExistingServers(usn).Any()) + // { + // return; + // } + + // await _syncLock.WaitAsync().ConfigureAwait(false); + + // try + // { + // if (GetExistingServers(usn).Any()) + // { + // return; + // } + + // var device = await Device.CreateuPnpDeviceAsync(new Uri(location), _httpClient, _config, _logger) + // .ConfigureAwait(false); + + // if (!_servers.Any(i => string.Equals(i.Properties.UUID, device.Properties.UUID, StringComparison.OrdinalIgnoreCase))) + // { + // _servers.Add(device); + // } + // } + // catch (Exception ex) + // { + + // } + // finally + // { + // _syncLock.Release(); + // } + // } + + // async void deviceDiscovery_DeviceLeft(object sender, SsdpMessageEventArgs e) + // { + // string usn; + // if (!e.Headers.TryGetValue("USN", out usn)) usn = String.Empty; + + // string nt; + // if (!e.Headers.TryGetValue("NT", out nt)) nt = String.Empty; + + // if (!IsValid(nt, usn)) + // { + // return; + // } + + // if (!GetExistingServers(usn).Any()) + // { + // return; + // } + + // await _syncLock.WaitAsync().ConfigureAwait(false); + + // try + // { + // var list = _servers.ToList(); + + // foreach (var device in GetExistingServers(usn).ToList()) + // { + // list.Remove(device); + // } + + // _servers = list; + // } + // finally + // { + // _syncLock.Release(); + // } + // } + + // private bool IsValid(string nt, string usn) + // { + // // It has to report that it's a media renderer + // if (usn.IndexOf("ContentDirectory:", StringComparison.OrdinalIgnoreCase) == -1 && + // nt.IndexOf("ContentDirectory:", StringComparison.OrdinalIgnoreCase) == -1 && + // usn.IndexOf("MediaServer:", StringComparison.OrdinalIgnoreCase) == -1 && + // nt.IndexOf("MediaServer:", StringComparison.OrdinalIgnoreCase) == -1) + // { + // return false; + // } + + // return true; + // } + + // private IEnumerable<Device> GetExistingServers(string usn) + // { + // return _servers + // .Where(i => usn.IndexOf(i.Properties.UUID, StringComparison.OrdinalIgnoreCase) != -1); + // } + + // public IEnumerable<IChannel> GetChannels() + // { + // //if (_servers.Count > 0) + // //{ + // // var service = _servers[0].Properties.Services + // // .FirstOrDefault(i => string.Equals(i.ServiceType, "urn:schemas-upnp-org:service:ContentDirectory:1", StringComparison.OrdinalIgnoreCase)); + + // // var controlUrl = service == null ? null : (_servers[0].Properties.BaseUrl.TrimEnd('/') + "/" + service.ControlUrl.TrimStart('/')); + + // // if (!string.IsNullOrEmpty(controlUrl)) + // // { + // // return new List<IChannel> + // // { + // // new ServerChannel(_servers.ToList(), _httpClient, _logger, controlUrl) + // // }; + // // } + // //} + + // return new List<IChannel>(); + // } + + // public void Dispose() + // { + // if (_deviceDiscovery != null) + // { + // _deviceDiscovery.DeviceDiscovered -= deviceDiscovery_DeviceDiscovered; + // _deviceDiscovery.DeviceLeft -= deviceDiscovery_DeviceLeft; + // } + // } + //} + + //public class ServerChannel : IChannel, IFactoryChannel + //{ + // private readonly IHttpClient _httpClient; + // private readonly ILogger _logger; + // public string ControlUrl { get; set; } + // public List<Device> Servers { get; set; } + + // public ServerChannel(IHttpClient httpClient, ILogger logger) + // { + // _httpClient = httpClient; + // _logger = logger; + // Servers = new List<Device>(); + // } + + // public string Name + // { + // get { return "Devices"; } + // } + + // public string Description + // { + // get { return string.Empty; } + // } + + // public string DataVersion + // { + // get { return DateTime.UtcNow.Ticks.ToString(); } + // } + + // public string HomePageUrl + // { + // get { return string.Empty; } + // } + + // public ChannelParentalRating ParentalRating + // { + // get { return ChannelParentalRating.GeneralAudience; } + // } + + // public InternalChannelFeatures GetChannelFeatures() + // { + // return new InternalChannelFeatures + // { + // ContentTypes = new List<ChannelMediaContentType> + // { + // ChannelMediaContentType.Song, + // ChannelMediaContentType.Clip + // }, + + // MediaTypes = new List<ChannelMediaType> + // { + // ChannelMediaType.Audio, + // ChannelMediaType.Video, + // ChannelMediaType.Photo + // } + // }; + // } + + // public bool IsEnabledFor(string userId) + // { + // return true; + // } + + // public async Task<ChannelItemResult> GetChannelItems(InternalChannelItemQuery query, CancellationToken cancellationToken) + // { + // IEnumerable<ChannelItemInfo> items; + + // if (string.IsNullOrWhiteSpace(query.FolderId)) + // { + // items = Servers.Select(i => new ChannelItemInfo + // { + // FolderType = ChannelFolderType.Container, + // Id = GetServerId(i), + // Name = i.Properties.Name, + // Overview = i.Properties.ModelDescription, + // Type = ChannelItemType.Folder + // }); + // } + // else + // { + // var idParts = query.FolderId.Split('|'); + // var folderId = idParts.Length == 2 ? idParts[1] : null; + + // var result = await new ContentDirectoryBrowser(_httpClient, _logger).Browse(new ContentDirectoryBrowseRequest + // { + // Limit = query.Limit, + // StartIndex = query.StartIndex, + // ParentId = folderId, + // ContentDirectoryUrl = ControlUrl + + // }, cancellationToken).ConfigureAwait(false); + + // items = result.Items.ToList(); + // } + + // var list = items.ToList(); + // var count = list.Count; + + // list = ApplyPaging(list, query).ToList(); + + // return new ChannelItemResult + // { + // Items = list, + // TotalRecordCount = count + // }; + // } + + // private string GetServerId(Device device) + // { + // return device.Properties.UUID.GetMD5().ToString("N"); + // } + + // private IEnumerable<T> ApplyPaging<T>(IEnumerable<T> items, InternalChannelItemQuery query) + // { + // if (query.StartIndex.HasValue) + // { + // items = items.Skip(query.StartIndex.Value); + // } + + // if (query.Limit.HasValue) + // { + // items = items.Take(query.Limit.Value); + // } + + // return items; + // } + + // public Task<DynamicImageResponse> GetChannelImage(ImageType type, CancellationToken cancellationToken) + // { + // // TODO: Implement + // return Task.FromResult(new DynamicImageResponse + // { + // HasImage = false + // }); + // } + + // public IEnumerable<ImageType> GetSupportedChannelImages() + // { + // return new List<ImageType> + // { + // ImageType.Primary + // }; + // } + //} } diff --git a/MediaBrowser.Dlna/DlnaManager.cs b/MediaBrowser.Dlna/DlnaManager.cs index 95e2aaa77..0fd3cec89 100644 --- a/MediaBrowser.Dlna/DlnaManager.cs +++ b/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) diff --git a/MediaBrowser.Dlna/Main/DlnaEntryPoint.cs b/MediaBrowser.Dlna/Main/DlnaEntryPoint.cs index bdb778cab..8c45757e7 100644 --- a/MediaBrowser.Dlna/Main/DlnaEntryPoint.cs +++ b/MediaBrowser.Dlna/Main/DlnaEntryPoint.cs @@ -81,8 +81,6 @@ namespace MediaBrowser.Dlna.Main ReloadComponents(); _config.NamedConfigurationUpdated += _config_NamedConfigurationUpdated; - - DlnaChannelFactory.Instance.Start(() => _registeredServerIds); } void _config_NamedConfigurationUpdated(object sender, ConfigurationUpdateEventArgs e) diff --git a/MediaBrowser.Dlna/Ssdp/Datagram.cs b/MediaBrowser.Dlna/Ssdp/Datagram.cs index 0a6d27303..791297b0c 100644 --- a/MediaBrowser.Dlna/Ssdp/Datagram.cs +++ b/MediaBrowser.Dlna/Ssdp/Datagram.cs @@ -30,7 +30,7 @@ namespace MediaBrowser.Dlna.Ssdp { var msg = Encoding.ASCII.GetBytes(Message); - var socket = CreateSocket(!IgnoreBindFailure); + var socket = CreateSocket(); if (socket == null) { @@ -69,7 +69,7 @@ namespace MediaBrowser.Dlna.Ssdp } catch (Exception ex) { - if (!IgnoreBindFailure || EnableDebugLogging) + if (EnableDebugLogging) { _logger.ErrorException("Error sending Datagram to {0} from {1}: " + Message, ex, ToEndPoint, FromEndPoint == null ? "" : FromEndPoint.ToString()); } @@ -102,7 +102,7 @@ namespace MediaBrowser.Dlna.Ssdp } } - private Socket CreateSocket(bool isBroadcast) + private Socket CreateSocket() { try { @@ -110,11 +110,8 @@ namespace MediaBrowser.Dlna.Ssdp socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true); - if (isBroadcast) - { - socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.Broadcast, true); - socket.SetSocketOption(SocketOptionLevel.IP, SocketOptionName.MulticastTimeToLive, 4); - } + socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.Broadcast, true); + socket.SetSocketOption(SocketOptionLevel.IP, SocketOptionName.MulticastTimeToLive, 4); return socket; } diff --git a/MediaBrowser.Dlna/Ssdp/SsdpHandler.cs b/MediaBrowser.Dlna/Ssdp/SsdpHandler.cs index e6e9624d5..93d81427b 100644 --- a/MediaBrowser.Dlna/Ssdp/SsdpHandler.cs +++ b/MediaBrowser.Dlna/Ssdp/SsdpHandler.cs @@ -230,8 +230,8 @@ namespace MediaBrowser.Dlna.Ssdp values["ST"] = d.Type; values["USN"] = d.USN; - SendDatagram(header, values, endpoint, null, true, 1); - SendDatagram(header, values, endpoint, new IPEndPoint(d.Address, 0), true, 1); + SendDatagram(header, values, endpoint, null, false, 1); + SendDatagram(header, values, endpoint, new IPEndPoint(d.Address, 0), false, 1); //SendDatagram(header, values, endpoint, null, true); if (enableDebugLogging) diff --git a/MediaBrowser.LocalMetadata/Savers/BoxSetXmlSaver.cs b/MediaBrowser.LocalMetadata/Savers/BoxSetXmlSaver.cs index e6ae84169..a2180439e 100644 --- a/MediaBrowser.LocalMetadata/Savers/BoxSetXmlSaver.cs +++ b/MediaBrowser.LocalMetadata/Savers/BoxSetXmlSaver.cs @@ -6,6 +6,7 @@ using System.Collections.Generic; using System.IO; using System.Text; using System.Threading; +using MediaBrowser.Common.IO; namespace MediaBrowser.LocalMetadata.Savers { @@ -21,11 +22,13 @@ namespace MediaBrowser.LocalMetadata.Savers private readonly IServerConfigurationManager _config; private readonly ILibraryManager _libraryManager; + private readonly IFileSystem _fileSystem; - public BoxSetXmlSaver(IServerConfigurationManager config, ILibraryManager libraryManager) + public BoxSetXmlSaver(IServerConfigurationManager config, ILibraryManager libraryManager, IFileSystem fileSystem) { _config = config; _libraryManager = libraryManager; + _fileSystem = fileSystem; } /// <summary> @@ -62,7 +65,7 @@ namespace MediaBrowser.LocalMetadata.Savers var xmlFilePath = GetSavePath(item); - XmlSaverHelpers.Save(builder, xmlFilePath, new List<string> { }, _config); + XmlSaverHelpers.Save(builder, xmlFilePath, new List<string> { }, _config, _fileSystem); } /// <summary> diff --git a/MediaBrowser.LocalMetadata/Savers/EpisodeXmlSaver.cs b/MediaBrowser.LocalMetadata/Savers/EpisodeXmlSaver.cs index 96d95d40b..284f33de9 100644 --- a/MediaBrowser.LocalMetadata/Savers/EpisodeXmlSaver.cs +++ b/MediaBrowser.LocalMetadata/Savers/EpisodeXmlSaver.cs @@ -9,6 +9,7 @@ using System.IO; using System.Security; using System.Text; using System.Threading; +using MediaBrowser.Common.IO; namespace MediaBrowser.LocalMetadata.Savers { @@ -19,12 +20,14 @@ namespace MediaBrowser.LocalMetadata.Savers private readonly CultureInfo _usCulture = new CultureInfo("en-US"); private readonly IServerConfigurationManager _config; private readonly ILibraryManager _libraryManager; + private IFileSystem _fileSystem; - public EpisodeXmlProvider(IItemRepository itemRepository, IServerConfigurationManager config, ILibraryManager libraryManager) + public EpisodeXmlProvider(IItemRepository itemRepository, IServerConfigurationManager config, ILibraryManager libraryManager, IFileSystem fileSystem) { _itemRepository = itemRepository; _config = config; _libraryManager = libraryManager; + _fileSystem = fileSystem; } /// <summary> @@ -143,7 +146,8 @@ namespace MediaBrowser.LocalMetadata.Savers "DVD_episodenumber", "DVD_season", "absolute_number" - }, _config); + + }, _config, _fileSystem); } /// <summary> diff --git a/MediaBrowser.LocalMetadata/Savers/FolderXmlSaver.cs b/MediaBrowser.LocalMetadata/Savers/FolderXmlSaver.cs index ac56f0864..655d41255 100644 --- a/MediaBrowser.LocalMetadata/Savers/FolderXmlSaver.cs +++ b/MediaBrowser.LocalMetadata/Savers/FolderXmlSaver.cs @@ -9,6 +9,7 @@ using System.Collections.Generic; using System.IO; using System.Text; using System.Threading; +using MediaBrowser.Common.IO; namespace MediaBrowser.LocalMetadata.Savers { @@ -24,11 +25,13 @@ namespace MediaBrowser.LocalMetadata.Savers private readonly IServerConfigurationManager _config; private readonly ILibraryManager _libraryManager; + private readonly IFileSystem _fileSystem; - public FolderXmlSaver(IServerConfigurationManager config, ILibraryManager libraryManager) + public FolderXmlSaver(IServerConfigurationManager config, ILibraryManager libraryManager, IFileSystem fileSystem) { _config = config; _libraryManager = libraryManager; + _fileSystem = fileSystem; } /// <summary> @@ -76,7 +79,7 @@ namespace MediaBrowser.LocalMetadata.Savers var xmlFilePath = GetSavePath(item); - XmlSaverHelpers.Save(builder, xmlFilePath, new List<string> { }, _config); + XmlSaverHelpers.Save(builder, xmlFilePath, new List<string> { }, _config, _fileSystem); } /// <summary> diff --git a/MediaBrowser.LocalMetadata/Savers/GameSystemXmlSaver.cs b/MediaBrowser.LocalMetadata/Savers/GameSystemXmlSaver.cs index 770f1d7f9..c6d21655f 100644 --- a/MediaBrowser.LocalMetadata/Savers/GameSystemXmlSaver.cs +++ b/MediaBrowser.LocalMetadata/Savers/GameSystemXmlSaver.cs @@ -6,6 +6,7 @@ using System.IO; using System.Security; using System.Text; using System.Threading; +using MediaBrowser.Common.IO; namespace MediaBrowser.LocalMetadata.Savers { @@ -21,11 +22,13 @@ namespace MediaBrowser.LocalMetadata.Savers private readonly IServerConfigurationManager _config; private readonly ILibraryManager _libraryManager; + private readonly IFileSystem _fileSystem; - public GameSystemXmlSaver(IServerConfigurationManager config, ILibraryManager libraryManager) + public GameSystemXmlSaver(IServerConfigurationManager config, ILibraryManager libraryManager, IFileSystem fileSystem) { _config = config; _libraryManager = libraryManager; + _fileSystem = fileSystem; } /// <summary> @@ -69,7 +72,7 @@ namespace MediaBrowser.LocalMetadata.Savers var xmlFilePath = GetSavePath(item); - XmlSaverHelpers.Save(builder, xmlFilePath, new List<string> { }, _config); + XmlSaverHelpers.Save(builder, xmlFilePath, new List<string> { }, _config, _fileSystem); } /// <summary> diff --git a/MediaBrowser.LocalMetadata/Savers/GameXmlSaver.cs b/MediaBrowser.LocalMetadata/Savers/GameXmlSaver.cs index 26c4ff395..fe5d6b27a 100644 --- a/MediaBrowser.LocalMetadata/Savers/GameXmlSaver.cs +++ b/MediaBrowser.LocalMetadata/Savers/GameXmlSaver.cs @@ -8,6 +8,7 @@ using System.IO; using System.Security; using System.Text; using System.Threading; +using MediaBrowser.Common.IO; namespace MediaBrowser.LocalMetadata.Savers { @@ -26,11 +27,13 @@ namespace MediaBrowser.LocalMetadata.Savers private readonly IServerConfigurationManager _config; private readonly ILibraryManager _libraryManager; + private readonly IFileSystem _fileSystem; - public GameXmlSaver(IServerConfigurationManager config, ILibraryManager libraryManager) + public GameXmlSaver(IServerConfigurationManager config, ILibraryManager libraryManager, IFileSystem fileSystem) { _config = config; _libraryManager = libraryManager; + _fileSystem = fileSystem; } /// <summary> @@ -101,7 +104,7 @@ namespace MediaBrowser.LocalMetadata.Savers "GameSystem", "NesBox", "NesBoxRom" - }, _config); + }, _config, _fileSystem); } public string GetSavePath(IHasMetadata item) diff --git a/MediaBrowser.LocalMetadata/Savers/MovieXmlSaver.cs b/MediaBrowser.LocalMetadata/Savers/MovieXmlSaver.cs index a6fba3e9b..46d549a3e 100644 --- a/MediaBrowser.LocalMetadata/Savers/MovieXmlSaver.cs +++ b/MediaBrowser.LocalMetadata/Savers/MovieXmlSaver.cs @@ -9,6 +9,7 @@ using System.IO; using System.Security; using System.Text; using System.Threading; +using MediaBrowser.Common.IO; namespace MediaBrowser.LocalMetadata.Savers { @@ -20,12 +21,14 @@ namespace MediaBrowser.LocalMetadata.Savers private readonly IItemRepository _itemRepository; private readonly IServerConfigurationManager _config; private readonly ILibraryManager _libraryManager; + private IFileSystem _fileSystem; - public MovieXmlProvider(IItemRepository itemRepository, IServerConfigurationManager config, ILibraryManager libraryManager) + public MovieXmlProvider(IItemRepository itemRepository, IServerConfigurationManager config, ILibraryManager libraryManager, IFileSystem fileSystem) { _itemRepository = itemRepository; _config = config; _libraryManager = libraryManager; + _fileSystem = fileSystem; } public string Name @@ -122,7 +125,7 @@ namespace MediaBrowser.LocalMetadata.Savers "Artist", "Album", "TmdbCollectionName" - }, _config); + }, _config, _fileSystem); } public string GetSavePath(IHasMetadata item) diff --git a/MediaBrowser.LocalMetadata/Savers/PersonXmlSaver.cs b/MediaBrowser.LocalMetadata/Savers/PersonXmlSaver.cs index 9d943bfa4..d199cb3b8 100644 --- a/MediaBrowser.LocalMetadata/Savers/PersonXmlSaver.cs +++ b/MediaBrowser.LocalMetadata/Savers/PersonXmlSaver.cs @@ -6,6 +6,7 @@ using System.IO; using System.Security; using System.Text; using System.Threading; +using MediaBrowser.Common.IO; namespace MediaBrowser.LocalMetadata.Savers { @@ -24,11 +25,13 @@ namespace MediaBrowser.LocalMetadata.Savers private readonly IServerConfigurationManager _config; private readonly ILibraryManager _libraryManager; + private readonly IFileSystem _fileSystem; - public PersonXmlSaver(IServerConfigurationManager config, ILibraryManager libraryManager) + public PersonXmlSaver(IServerConfigurationManager config, ILibraryManager libraryManager, IFileSystem fileSystem) { _config = config; _libraryManager = libraryManager; + _fileSystem = fileSystem; } /// <summary> @@ -75,7 +78,7 @@ namespace MediaBrowser.LocalMetadata.Savers XmlSaverHelpers.Save(builder, xmlFilePath, new List<string> { "PlaceOfBirth" - }, _config); + }, _config, _fileSystem); } /// <summary> diff --git a/MediaBrowser.LocalMetadata/Savers/PlaylistXmlSaver.cs b/MediaBrowser.LocalMetadata/Savers/PlaylistXmlSaver.cs index 7dfe59b4b..4e047252f 100644 --- a/MediaBrowser.LocalMetadata/Savers/PlaylistXmlSaver.cs +++ b/MediaBrowser.LocalMetadata/Savers/PlaylistXmlSaver.cs @@ -7,6 +7,7 @@ using System.IO; using System.Security; using System.Text; using System.Threading; +using MediaBrowser.Common.IO; namespace MediaBrowser.LocalMetadata.Savers { @@ -22,11 +23,13 @@ namespace MediaBrowser.LocalMetadata.Savers private readonly IServerConfigurationManager _config; private readonly ILibraryManager _libraryManager; + private readonly IFileSystem _fileSystem; - public PlaylistXmlSaver(IServerConfigurationManager config, ILibraryManager libraryManager) + public PlaylistXmlSaver(IServerConfigurationManager config, ILibraryManager libraryManager, IFileSystem fileSystem) { _config = config; _libraryManager = libraryManager; + _fileSystem = fileSystem; } /// <summary> @@ -75,7 +78,7 @@ namespace MediaBrowser.LocalMetadata.Savers "OwnerUserId", "PlaylistMediaType" - }, _config); + }, _config, _fileSystem); } /// <summary> diff --git a/MediaBrowser.LocalMetadata/Savers/SeriesXmlSaver.cs b/MediaBrowser.LocalMetadata/Savers/SeriesXmlSaver.cs index 44b1cd8d3..8150695e7 100644 --- a/MediaBrowser.LocalMetadata/Savers/SeriesXmlSaver.cs +++ b/MediaBrowser.LocalMetadata/Savers/SeriesXmlSaver.cs @@ -9,6 +9,7 @@ using System.IO; using System.Security; using System.Text; using System.Threading; +using MediaBrowser.Common.IO; namespace MediaBrowser.LocalMetadata.Savers { @@ -16,11 +17,13 @@ namespace MediaBrowser.LocalMetadata.Savers { private readonly IServerConfigurationManager _config; private readonly ILibraryManager _libraryManager; + private IFileSystem _fileSystem; - public SeriesXmlProvider(IServerConfigurationManager config, ILibraryManager libraryManager) + public SeriesXmlProvider(IServerConfigurationManager config, ILibraryManager libraryManager, IFileSystem fileSystem) { _config = config; _libraryManager = libraryManager; + _fileSystem = fileSystem; } public string Name @@ -134,7 +137,7 @@ namespace MediaBrowser.LocalMetadata.Savers // Deprecated. No longer saving in this field. "AnimeSeriesIndex" - }, _config); + }, _config, _fileSystem); } /// <summary> diff --git a/MediaBrowser.LocalMetadata/Savers/XmlSaverHelpers.cs b/MediaBrowser.LocalMetadata/Savers/XmlSaverHelpers.cs index 091d59469..b56694a92 100644 --- a/MediaBrowser.LocalMetadata/Savers/XmlSaverHelpers.cs +++ b/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; @@ -425,17 +426,13 @@ namespace MediaBrowser.LocalMetadata.Savers } } - var hasLanguage = item as IHasPreferredMetadataLanguage; - if (hasLanguage != null) + if (!string.IsNullOrEmpty(item.PreferredMetadataLanguage)) { - if (!string.IsNullOrEmpty(hasLanguage.PreferredMetadataLanguage)) - { - builder.Append("<Language>" + SecurityElement.Escape(hasLanguage.PreferredMetadataLanguage) + "</Language>"); - } - if (!string.IsNullOrEmpty(hasLanguage.PreferredMetadataCountryCode)) - { - builder.Append("<CountryCode>" + SecurityElement.Escape(hasLanguage.PreferredMetadataCountryCode) + "</CountryCode>"); - } + builder.Append("<Language>" + SecurityElement.Escape(item.PreferredMetadataLanguage) + "</Language>"); + } + if (!string.IsNullOrEmpty(item.PreferredMetadataCountryCode)) + { + builder.Append("<CountryCode>" + SecurityElement.Escape(item.PreferredMetadataCountryCode) + "</CountryCode>"); } // Use original runtime here, actual file runtime later in MediaInfo diff --git a/MediaBrowser.MediaEncoding/Configuration/EncodingConfigurationFactory.cs b/MediaBrowser.MediaEncoding/Configuration/EncodingConfigurationFactory.cs index 17470d206..81f2a75ff 100644 --- a/MediaBrowser.MediaEncoding/Configuration/EncodingConfigurationFactory.cs +++ b/MediaBrowser.MediaEncoding/Configuration/EncodingConfigurationFactory.cs @@ -2,26 +2,37 @@ using MediaBrowser.Model.Configuration; using System.Collections.Generic; using System.IO; +using MediaBrowser.Common.IO; namespace MediaBrowser.MediaEncoding.Configuration { public class EncodingConfigurationFactory : IConfigurationFactory { + private readonly IFileSystem _fileSystem; + + public EncodingConfigurationFactory(IFileSystem fileSystem) + { + _fileSystem = fileSystem; + } + public IEnumerable<ConfigurationStore> GetConfigurations() { return new[] { - new EncodingConfigurationStore() + new EncodingConfigurationStore(_fileSystem) }; } } public class EncodingConfigurationStore : ConfigurationStore, IValidatingConfiguration { - public EncodingConfigurationStore() + private readonly IFileSystem _fileSystem; + + public EncodingConfigurationStore(IFileSystem fileSystem) { ConfigurationType = typeof(EncodingOptions); Key = "encoding"; + _fileSystem = fileSystem; } public void Validate(object oldConfig, object newConfig) @@ -35,7 +46,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)); } diff --git a/MediaBrowser.MediaEncoding/Encoder/BaseEncoder.cs b/MediaBrowser.MediaEncoding/Encoder/BaseEncoder.cs index 181f147b4..a8ea283ae 100644 --- a/MediaBrowser.MediaEncoding/Encoder/BaseEncoder.cs +++ b/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); } @@ -342,9 +342,55 @@ namespace MediaBrowser.MediaEncoding.Encoder inputModifier += " -re"; } + var videoDecoder = GetVideoDecoder(job); + if (!string.IsNullOrWhiteSpace(videoDecoder)) + { + inputModifier += " " + videoDecoder; + } + return inputModifier; } + /// <summary> + /// Gets the name of the output video codec + /// </summary> + /// <param name="state">The state.</param> + /// <returns>System.String.</returns> + protected string GetVideoDecoder(EncodingJob state) + { + if (string.Equals(GetEncodingOptions().HardwareVideoDecoder, "qsv", StringComparison.OrdinalIgnoreCase)) + { + if (state.VideoStream != null && !string.IsNullOrWhiteSpace(state.VideoStream.Codec)) + { + switch (state.MediaSource.VideoStream.Codec.ToLower()) + { + case "avc": + case "h264": + if (MediaEncoder.SupportsDecoder("h264_qsv")) + { + return "-c:v h264_qsv "; + } + break; + case "mpeg2video": + if (MediaEncoder.SupportsDecoder("mpeg2_qsv")) + { + return "-c:v mpeg2_qsv "; + } + break; + case "vc1": + if (MediaEncoder.SupportsDecoder("vc1_qsv")) + { + return "-c:v vc1_qsv "; + } + break; + } + } + } + + // leave blank so ffmpeg will decide + return null; + } + private string GetUserAgentParam(EncodingJob job) { string useragent = null; @@ -422,7 +468,7 @@ namespace MediaBrowser.MediaEncoding.Encoder { if (!(job.VideoType == VideoType.Iso && job.IsoMount == null)) { - inputPath = MediaEncoderHelpers.GetInputArgument(job.MediaPath, job.InputProtocol, job.IsoMount, job.PlayableStreamFileNames); + inputPath = MediaEncoderHelpers.GetInputArgument(FileSystem, job.MediaPath, job.InputProtocol, job.IsoMount, job.PlayableStreamFileNames); } } @@ -436,7 +482,7 @@ namespace MediaBrowser.MediaEncoding.Encoder state.IsoMount = await IsoManager.Mount(state.MediaPath, cancellationToken).ConfigureAwait(false); } - if (state.MediaSource.RequiresOpening) + if (state.MediaSource.RequiresOpening && string.IsNullOrWhiteSpace(state.LiveStreamId)) { var liveStreamResponse = await MediaSourceManager.OpenLiveStream(new LiveStreamRequest { diff --git a/MediaBrowser.MediaEncoding/Encoder/EncodingJob.cs b/MediaBrowser.MediaEncoding/Encoder/EncodingJob.cs index 806910d89..47babfd13 100644 --- a/MediaBrowser.MediaEncoding/Encoder/EncodingJob.cs +++ b/MediaBrowser.MediaEncoding/Encoder/EncodingJob.cs @@ -56,7 +56,7 @@ namespace MediaBrowser.MediaEncoding.Encoder public bool EnableMpegtsM2TsMode { get; set; } public TranscodeSeekInfo TranscodeSeekInfo { get; set; } public long? EncodingDurationTicks { get; set; } - public string LiveTvStreamId { get; set; } + public string LiveStreamId { get; set; } public long? RunTimeTicks; public string ItemType { get; set; } diff --git a/MediaBrowser.MediaEncoding/Encoder/EncodingJobFactory.cs b/MediaBrowser.MediaEncoding/Encoder/EncodingJobFactory.cs index 476d9166b..03dbd07f0 100644 --- a/MediaBrowser.MediaEncoding/Encoder/EncodingJobFactory.cs +++ b/MediaBrowser.MediaEncoding/Encoder/EncodingJobFactory.cs @@ -326,26 +326,36 @@ namespace MediaBrowser.MediaEncoding.Encoder /// <returns>System.Nullable{System.Int32}.</returns> private int? GetNumAudioChannelsParam(EncodingJobOptions request, MediaStream audioStream, string outputAudioCodec) { - if (audioStream != null) + var inputChannels = audioStream == null + ? null + : audioStream.Channels; + + if (inputChannels <= 0) { - var codec = outputAudioCodec ?? string.Empty; + inputChannels = null; + } - if (audioStream.Channels > 2 && codec.IndexOf("wma", StringComparison.OrdinalIgnoreCase) != -1) - { - // wmav2 currently only supports two channel output - return 2; - } + var codec = outputAudioCodec ?? string.Empty; + + if (codec.IndexOf("wma", StringComparison.OrdinalIgnoreCase) != -1) + { + // wmav2 currently only supports two channel output + return Math.Min(2, inputChannels ?? 2); } if (request.MaxAudioChannels.HasValue) { - if (audioStream != null && audioStream.Channels.HasValue) + if (inputChannels.HasValue) { - return Math.Min(request.MaxAudioChannels.Value, audioStream.Channels.Value); + return Math.Min(request.MaxAudioChannels.Value, inputChannels.Value); } + var channelLimit = codec.IndexOf("mp3", StringComparison.OrdinalIgnoreCase) != -1 + ? 2 + : 6; + // If we don't have any media info then limit it to 5 to prevent encoding errors due to asking for too many channels - return Math.Min(request.MaxAudioChannels.Value, 5); + return Math.Min(request.MaxAudioChannels.Value, channelLimit); } return request.AudioChannels; @@ -519,6 +529,11 @@ namespace MediaBrowser.MediaEncoding.Encoder return false; } + if (videoStream.IsAnamorphic ?? false) + { + return false; + } + // Can't stream copy if we're burning in subtitles if (request.SubtitleStreamIndex.HasValue) { diff --git a/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs b/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs index 4fcc71f97..32c93454d 100644 --- a/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs +++ b/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs @@ -97,6 +97,22 @@ namespace MediaBrowser.MediaEncoding.Encoder FFMpegPath = ffMpegPath; } + public void SetAvailableEncoders(List<string> list) + { + + } + + private List<string> _decoders = new List<string>(); + public void SetAvailableDecoders(List<string> list) + { + _decoders = list.ToList(); + } + + public bool SupportsDecoder(string decoder) + { + return _decoders.Contains(decoder, StringComparer.OrdinalIgnoreCase); + } + /// <summary> /// Gets the encoder path. /// </summary> @@ -116,7 +132,7 @@ namespace MediaBrowser.MediaEncoding.Encoder { var extractChapters = request.MediaType == DlnaProfileType.Video && request.ExtractChapters; - var inputFiles = MediaEncoderHelpers.GetInputArgument(request.InputPath, request.Protocol, request.MountedIso, request.PlayableStreamFileNames); + var inputFiles = MediaEncoderHelpers.GetInputArgument(FileSystem, request.InputPath, request.Protocol, request.MountedIso, request.PlayableStreamFileNames); var extractKeyFrameInterval = request.ExtractKeyFrameInterval && request.Protocol == MediaProtocol.File && request.VideoType == VideoType.VideoFile; @@ -330,7 +346,7 @@ namespace MediaBrowser.MediaEncoding.Encoder EnableRaisingEvents = true }; - _logger.Debug("{0} {1}", process.StartInfo.FileName, process.StartInfo.Arguments); + _logger.Info("{0} {1}", process.StartInfo.FileName, process.StartInfo.Arguments); using (process) { @@ -356,7 +372,7 @@ namespace MediaBrowser.MediaEncoding.Encoder process.WaitForExit(); - _logger.Debug("Keyframe extraction took {0} seconds", (DateTime.UtcNow - start).TotalSeconds); + _logger.Info("Keyframe extraction took {0} seconds", (DateTime.UtcNow - start).TotalSeconds); //_logger.Debug("Found keyframes {0}", string.Join(",", lines.ToArray())); return lines; } @@ -483,9 +499,6 @@ namespace MediaBrowser.MediaEncoding.Encoder } } - // TODO: Output in webp for smaller sizes - // -f image2 -f webp - // Use ffmpeg to sample 100 (we can drop this if required using thumbnail=50 for 50 frames) frames and pick the best thumbnail. Have a fall back just in case. var args = useIFrame ? string.Format("-i {0} -threads 1 -v quiet -vframes 1 -vf \"{2},thumbnail=30\" -f image2 \"{1}\"", inputPath, "-", vf) : string.Format("-i {0} -threads 1 -v quiet -vframes 1 -vf \"{2}\" -f image2 \"{1}\"", inputPath, "-", vf); @@ -605,7 +618,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); @@ -879,4 +892,4 @@ namespace MediaBrowser.MediaEncoding.Encoder } } } -} +}
\ No newline at end of file diff --git a/MediaBrowser.MediaEncoding/Probing/ProbeResultNormalizer.cs b/MediaBrowser.MediaEncoding/Probing/ProbeResultNormalizer.cs index 0226dbc5a..7cf2425a9 100644 --- a/MediaBrowser.MediaEncoding/Probing/ProbeResultNormalizer.cs +++ b/MediaBrowser.MediaEncoding/Probing/ProbeResultNormalizer.cs @@ -516,12 +516,25 @@ namespace MediaBrowser.MediaEncoding.Probing FetchStudios(audio, tags, "label"); // These support mulitple values, but for now we only store the first. - audio.SetProviderId(MetadataProviders.MusicBrainzAlbumArtist, GetMultipleMusicBrainzId(FFProbeHelpers.GetDictionaryValue(tags, "MusicBrainz Album Artist Id"))); - audio.SetProviderId(MetadataProviders.MusicBrainzArtist, GetMultipleMusicBrainzId(FFProbeHelpers.GetDictionaryValue(tags, "MusicBrainz Artist Id"))); - - audio.SetProviderId(MetadataProviders.MusicBrainzAlbum, GetMultipleMusicBrainzId(FFProbeHelpers.GetDictionaryValue(tags, "MusicBrainz Album Id"))); - audio.SetProviderId(MetadataProviders.MusicBrainzReleaseGroup, GetMultipleMusicBrainzId(FFProbeHelpers.GetDictionaryValue(tags, "MusicBrainz Release Group Id"))); - audio.SetProviderId(MetadataProviders.MusicBrainzTrack, GetMultipleMusicBrainzId(FFProbeHelpers.GetDictionaryValue(tags, "MusicBrainz Release Track Id"))); + var mb = GetMultipleMusicBrainzId(FFProbeHelpers.GetDictionaryValue(tags, "MusicBrainz Album Artist Id")); + if(mb == null) mb = GetMultipleMusicBrainzId(FFProbeHelpers.GetDictionaryValue(tags, "MUSICBRAINZ_ALBUMARTISTID")); + audio.SetProviderId(MetadataProviders.MusicBrainzAlbumArtist, mb); + + mb = GetMultipleMusicBrainzId(FFProbeHelpers.GetDictionaryValue(tags, "MusicBrainz Artist Id")); + if(mb == null) mb = GetMultipleMusicBrainzId(FFProbeHelpers.GetDictionaryValue(tags, "MUSICBRAINZ_ARTISTID")); + audio.SetProviderId(MetadataProviders.MusicBrainzArtist, mb); + + mb = GetMultipleMusicBrainzId(FFProbeHelpers.GetDictionaryValue(tags, "MusicBrainz Album Id")); + if(mb == null) mb = GetMultipleMusicBrainzId(FFProbeHelpers.GetDictionaryValue(tags, "MUSICBRAINZ_ALBUMID")); + audio.SetProviderId(MetadataProviders.MusicBrainzAlbum, mb); + + mb = GetMultipleMusicBrainzId(FFProbeHelpers.GetDictionaryValue(tags, "MusicBrainz Release Group Id")); + if(mb == null) mb = GetMultipleMusicBrainzId(FFProbeHelpers.GetDictionaryValue(tags, "MUSICBRAINZ_RELEASEGROUPID")); + audio.SetProviderId(MetadataProviders.MusicBrainzReleaseGroup, mb); + + mb = GetMultipleMusicBrainzId(FFProbeHelpers.GetDictionaryValue(tags, "MusicBrainz Release Track Id")); + if(mb == null) mb = GetMultipleMusicBrainzId(FFProbeHelpers.GetDictionaryValue(tags, "MUSICBRAINZ_RELEASETRACKID")); + audio.SetProviderId(MetadataProviders.MusicBrainzTrack, mb); } private string GetMultipleMusicBrainzId(string value) diff --git a/MediaBrowser.MediaEncoding/Subtitles/SrtParser.cs b/MediaBrowser.MediaEncoding/Subtitles/SrtParser.cs index d40d4afa7..2a6aa993c 100644 --- a/MediaBrowser.MediaEncoding/Subtitles/SrtParser.cs +++ b/MediaBrowser.MediaEncoding/Subtitles/SrtParser.cs @@ -69,7 +69,7 @@ namespace MediaBrowser.MediaEncoding.Subtitles } subEvent.Text = string.Join(ParserValues.NewLine, multiline); subEvent.Text = subEvent.Text.Replace(@"\N", ParserValues.NewLine, StringComparison.OrdinalIgnoreCase); - subEvent.Text = Regex.Replace(subEvent.Text, @"\{(\\[\w]+\(?([\w\d]+,?)+\)?)+\}", string.Empty, RegexOptions.IgnoreCase); + subEvent.Text = Regex.Replace(subEvent.Text, @"\{(?:\\\d?[\w.-]+(?:\([^\)]*\)|&H?[0-9A-Fa-f]+&|))+\}", string.Empty, RegexOptions.IgnoreCase); subEvent.Text = Regex.Replace(subEvent.Text, "<", "<", RegexOptions.IgnoreCase); subEvent.Text = Regex.Replace(subEvent.Text, ">", ">", RegexOptions.IgnoreCase); subEvent.Text = Regex.Replace(subEvent.Text, "<(\\/?(font|b|u|i|s))((\\s+(\\w|\\w[\\w\\-]*\\w)(\\s*=\\s*(?:\\\".*?\\\"|'.*?'|[^'\\\">\\s]+))?)+\\s*|\\s*)(\\/?)>", "<$1$3$7>", RegexOptions.IgnoreCase); diff --git a/MediaBrowser.MediaEncoding/Subtitles/SubtitleEncoder.cs b/MediaBrowser.MediaEncoding/Subtitles/SubtitleEncoder.cs index 9d43cafb8..ea4c72b2f 100644 --- a/MediaBrowser.MediaEncoding/Subtitles/SubtitleEncoder.cs +++ b/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; } diff --git a/MediaBrowser.Model.Portable/MediaBrowser.Model.Portable.csproj b/MediaBrowser.Model.Portable/MediaBrowser.Model.Portable.csproj index 7d813e903..40e532b79 100644 --- a/MediaBrowser.Model.Portable/MediaBrowser.Model.Portable.csproj +++ b/MediaBrowser.Model.Portable/MediaBrowser.Model.Portable.csproj @@ -1184,15 +1184,6 @@ <Compile Include="..\MediaBrowser.Model\Tasks\TaskTriggerInfo.cs"> <Link>Tasks\TaskTriggerInfo.cs</Link> </Compile> - <Compile Include="..\MediaBrowser.Model\Themes\AppTheme.cs"> - <Link>Themes\AppTheme.cs</Link> - </Compile> - <Compile Include="..\MediaBrowser.Model\Themes\AppThemeInfo.cs"> - <Link>Themes\AppThemeInfo.cs</Link> - </Compile> - <Compile Include="..\MediaBrowser.Model\Themes\ThemeImage.cs"> - <Link>Themes\ThemeImage.cs</Link> - </Compile> <Compile Include="..\MediaBrowser.Model\Updates\CheckForUpdateResult.cs"> <Link>Updates\CheckForUpdateResult.cs</Link> </Compile> diff --git a/MediaBrowser.Model.net35/MediaBrowser.Model.net35.csproj b/MediaBrowser.Model.net35/MediaBrowser.Model.net35.csproj index 8605a0ab3..09a7cde9d 100644 --- a/MediaBrowser.Model.net35/MediaBrowser.Model.net35.csproj +++ b/MediaBrowser.Model.net35/MediaBrowser.Model.net35.csproj @@ -1140,15 +1140,6 @@ <Compile Include="..\MediaBrowser.Model\Tasks\TaskTriggerInfo.cs"> <Link>Tasks\TaskTriggerInfo.cs</Link> </Compile> - <Compile Include="..\MediaBrowser.Model\Themes\AppTheme.cs"> - <Link>Themes\AppTheme.cs</Link> - </Compile> - <Compile Include="..\MediaBrowser.Model\Themes\AppThemeInfo.cs"> - <Link>Themes\AppThemeInfo.cs</Link> - </Compile> - <Compile Include="..\MediaBrowser.Model\Themes\ThemeImage.cs"> - <Link>Themes\ThemeImage.cs</Link> - </Compile> <Compile Include="..\MediaBrowser.Model\Updates\CheckForUpdateResult.cs"> <Link>Updates\CheckForUpdateResult.cs</Link> </Compile> diff --git a/MediaBrowser.Model/Configuration/EncodingOptions.cs b/MediaBrowser.Model/Configuration/EncodingOptions.cs index 6d3894f02..77b894eb8 100644 --- a/MediaBrowser.Model/Configuration/EncodingOptions.cs +++ b/MediaBrowser.Model/Configuration/EncodingOptions.cs @@ -6,14 +6,13 @@ namespace MediaBrowser.Model.Configuration public int EncodingThreadCount { get; set; } public string TranscodingTempPath { get; set; } public double DownMixAudioBoost { get; set; } - public string H264Encoder { get; set; } public bool EnableDebugLogging { get; set; } public bool EnableThrottling { get; set; } public int ThrottleThresholdInSeconds { get; set; } + public string HardwareVideoDecoder { get; set; } public EncodingOptions() { - H264Encoder = "libx264"; DownMixAudioBoost = 2; EnableThrottling = true; ThrottleThresholdInSeconds = 120; diff --git a/MediaBrowser.Model/Configuration/ServerConfiguration.cs b/MediaBrowser.Model/Configuration/ServerConfiguration.cs index 87b5e4fd7..5121fcd6f 100644 --- a/MediaBrowser.Model/Configuration/ServerConfiguration.cs +++ b/MediaBrowser.Model/Configuration/ServerConfiguration.cs @@ -45,12 +45,6 @@ namespace MediaBrowser.Model.Configuration public bool EnableHttps { get; set; } /// <summary> - /// Gets or sets a value indicating whether [enable user specific user views]. - /// </summary> - /// <value><c>true</c> if [enable user specific user views]; otherwise, <c>false</c>.</value> - public bool EnableUserSpecificUserViews { get; set; } - - /// <summary> /// Gets or sets the value pointing to the file system where the ssl certiifcate is located.. /// </summary> /// <value>The value pointing to the file system where the ssl certiifcate is located..</value> @@ -103,7 +97,13 @@ namespace MediaBrowser.Model.Configuration /// </summary> /// <value><c>true</c> if [disable startup scan]; otherwise, <c>false</c>.</value> public bool DisableStartupScan { get; set; } - + + /// <summary> + /// Gets or sets a value indicating whether [enable user views]. + /// </summary> + /// <value><c>true</c> if [enable user views]; otherwise, <c>false</c>.</value> + public bool EnableUserViews { get; set; } + /// <summary> /// Gets or sets a value indicating whether [enable library metadata sub folder]. /// </summary> @@ -223,7 +223,7 @@ namespace MediaBrowser.Model.Configuration public bool EnableWindowsShortcuts { get; set; } public bool EnableVideoFrameByFrameAnalysis { get; set; } - + /// <summary> /// Initializes a new instance of the <see cref="ServerConfiguration" /> class. /// </summary> @@ -274,11 +274,6 @@ namespace MediaBrowser.Model.Configuration InsecureApps9 = new[] { - "Chromecast", - "iOS", - "Unknown app", - "iPad", - "iPhone", "Windows Phone" }; @@ -581,4 +576,4 @@ namespace MediaBrowser.Model.Configuration }; } } -} +}
\ No newline at end of file diff --git a/MediaBrowser.Model/Dlna/ConditionProcessor.cs b/MediaBrowser.Model/Dlna/ConditionProcessor.cs index 793036f40..fd3df9c76 100644 --- a/MediaBrowser.Model/Dlna/ConditionProcessor.cs +++ b/MediaBrowser.Model/Dlna/ConditionProcessor.cs @@ -28,7 +28,6 @@ namespace MediaBrowser.Model.Dlna // TODO: Implement return true; case ProfileConditionValue.Has64BitOffsets: - // TODO: Implement return true; case ProfileConditionValue.IsAnamorphic: return IsConditionSatisfied(condition, isAnamorphic); diff --git a/MediaBrowser.Model/Dto/BaseItemDto.cs b/MediaBrowser.Model/Dto/BaseItemDto.cs index a1c075563..57fc711c2 100644 --- a/MediaBrowser.Model/Dto/BaseItemDto.cs +++ b/MediaBrowser.Model/Dto/BaseItemDto.cs @@ -74,8 +74,6 @@ namespace MediaBrowser.Model.Dto public string ShareUrl { get; set; } public float? Metascore { get; set; } - - public bool? IsUnidentified { get; set; } public bool? HasDynamicCategories { get; set; } public int? AnimeSeriesIndex { get; set; } @@ -278,12 +276,6 @@ namespace MediaBrowser.Model.Dto public int? ProductionYear { get; set; } /// <summary> - /// Gets or sets the season count. - /// </summary> - /// <value>The season count.</value> - public int? SeasonCount { get; set; } - - /// <summary> /// Gets or sets the players supported by a game. /// </summary> /// <value>The players.</value> @@ -1113,12 +1105,6 @@ namespace MediaBrowser.Model.Dto public DateTime? StartDate { get; set; } /// <summary> - /// Gets or sets the original air date. - /// </summary> - /// <value>The original air date.</value> - public DateTime? OriginalAirDate { get; set; } - - /// <summary> /// Gets or sets the completion percentage. /// </summary> /// <value>The completion percentage.</value> diff --git a/MediaBrowser.Model/MediaBrowser.Model.csproj b/MediaBrowser.Model/MediaBrowser.Model.csproj index 19c5c833a..2a86b96d6 100644 --- a/MediaBrowser.Model/MediaBrowser.Model.csproj +++ b/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,15 +42,11 @@ <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> <ItemGroup> @@ -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> diff --git a/MediaBrowser.Model/Themes/AppTheme.cs b/MediaBrowser.Model/Themes/AppTheme.cs deleted file mode 100644 index 527f1de72..000000000 --- a/MediaBrowser.Model/Themes/AppTheme.cs +++ /dev/null @@ -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>(); - } - } -} diff --git a/MediaBrowser.Model/Themes/AppThemeInfo.cs b/MediaBrowser.Model/Themes/AppThemeInfo.cs deleted file mode 100644 index bc359530a..000000000 --- a/MediaBrowser.Model/Themes/AppThemeInfo.cs +++ /dev/null @@ -1,9 +0,0 @@ -namespace MediaBrowser.Model.Themes -{ - public class AppThemeInfo - { - public string AppName { get; set; } - - public string Name { get; set; } - } -}
\ No newline at end of file diff --git a/MediaBrowser.Model/Themes/ThemeImage.cs b/MediaBrowser.Model/Themes/ThemeImage.cs deleted file mode 100644 index 2fe0820ae..000000000 --- a/MediaBrowser.Model/Themes/ThemeImage.cs +++ /dev/null @@ -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; } - } -} diff --git a/MediaBrowser.Providers/BoxSets/MovieDbBoxSetProvider.cs b/MediaBrowser.Providers/BoxSets/MovieDbBoxSetProvider.cs index 0e7050741..0fbb2d990 100644 --- a/MediaBrowser.Providers/BoxSets/MovieDbBoxSetProvider.cs +++ b/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); } diff --git a/MediaBrowser.Providers/Folders/DefaultImageProvider.cs b/MediaBrowser.Providers/Folders/DefaultImageProvider.cs index 13e486ae9..114346191 100644 --- a/MediaBrowser.Providers/Folders/DefaultImageProvider.cs +++ b/MediaBrowser.Providers/Folders/DefaultImageProvider.cs @@ -77,11 +77,11 @@ namespace MediaBrowser.Providers.Folders if (string.Equals(viewType, CollectionType.Books, StringComparison.OrdinalIgnoreCase)) { - return urlPrefix + "books.png"; + //return urlPrefix + "books.png"; } if (string.Equals(viewType, CollectionType.Games, StringComparison.OrdinalIgnoreCase)) { - return urlPrefix + "games.png"; + //return urlPrefix + "games.png"; } if (string.Equals(viewType, CollectionType.Music, StringComparison.OrdinalIgnoreCase)) { @@ -109,23 +109,23 @@ namespace MediaBrowser.Providers.Folders } if (string.Equals(viewType, CollectionType.Playlists, StringComparison.OrdinalIgnoreCase)) { - return urlPrefix + "playlists.png"; + //return urlPrefix + "playlists.png"; } if (string.Equals(viewType, CollectionType.HomeVideos, StringComparison.OrdinalIgnoreCase)) { - return urlPrefix + "homevideos.png"; + //return urlPrefix + "homevideos.png"; } if (string.Equals(viewType, CollectionType.MusicVideos, StringComparison.OrdinalIgnoreCase)) { - return urlPrefix + "musicvideos.png"; + //return urlPrefix + "musicvideos.png"; } if (string.Equals(viewType, CollectionType.BoxSets, StringComparison.OrdinalIgnoreCase)) { - return urlPrefix + "generic.png"; + //return urlPrefix + "generic.png"; } if (string.IsNullOrWhiteSpace(viewType)) { - return urlPrefix + "generic.png"; + //return urlPrefix + "generic.png"; } return null; diff --git a/MediaBrowser.Providers/ImagesByName/ImageUtils.cs b/MediaBrowser.Providers/ImagesByName/ImageUtils.cs index 72df3697a..ae2421f65 100644 --- a/MediaBrowser.Providers/ImagesByName/ImageUtils.cs +++ b/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 { diff --git a/MediaBrowser.Providers/Manager/ImageSaver.cs b/MediaBrowser.Providers/Manager/ImageSaver.cs index 22178434f..dec688635 100644 --- a/MediaBrowser.Providers/Manager/ImageSaver.cs +++ b/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); @@ -236,11 +236,6 @@ namespace MediaBrowser.Providers.Manager file.Attributes |= FileAttributes.Hidden; } } - catch (UnauthorizedAccessException ex) - { - _logger.Error("Error saving image to {0}", ex, path); - throw new Exception(string.Format("Error saving image to {0}", path), ex); - } finally { _libraryMonitor.ReportFileSystemChangeComplete(path, false); diff --git a/MediaBrowser.Providers/Manager/ItemImageProvider.cs b/MediaBrowser.Providers/Manager/ItemImageProvider.cs index 92fc1c2a8..3c412b788 100644 --- a/MediaBrowser.Providers/Manager/ItemImageProvider.cs +++ b/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; diff --git a/MediaBrowser.Providers/Manager/MetadataService.cs b/MediaBrowser.Providers/Manager/MetadataService.cs index cddc6f894..daf0a5822 100644 --- a/MediaBrowser.Providers/Manager/MetadataService.cs +++ b/MediaBrowser.Providers/Manager/MetadataService.cs @@ -141,15 +141,21 @@ namespace MediaBrowser.Providers.Manager if (providers.Count > 0) { - var id = await CreateInitialLookupInfo(itemOfType, cancellationToken).ConfigureAwait(false); + var id = itemOfType.GetLookupInfo(); + await ItemIdentifier<TIdType>.FindIdentities(id, ProviderManager, cancellationToken); var result = await RefreshWithProviders(metadataResult, id, refreshOptions, providers, itemImageProvider, cancellationToken).ConfigureAwait(false); updateType = updateType | result.UpdateType; refreshResult.AddStatus(result.Status, result.ErrorMessage); - refreshResult.SetDateLastMetadataRefresh(DateTime.UtcNow); - - MergeIdentities(itemOfType, id); + if (result.Failures == 0) + { + refreshResult.SetDateLastMetadataRefresh(DateTime.UtcNow); + } + else + { + refreshResult.SetDateLastMetadataRefresh(null); + } } } @@ -164,7 +170,14 @@ namespace MediaBrowser.Providers.Manager updateType = updateType | result.UpdateType; refreshResult.AddStatus(result.Status, result.ErrorMessage); - refreshResult.SetDateLastImagesRefresh(DateTime.UtcNow); + if (result.Failures == 0) + { + refreshResult.SetDateLastImagesRefresh(DateTime.UtcNow); + } + else + { + refreshResult.SetDateLastImagesRefresh(null); + } } } @@ -209,16 +222,7 @@ namespace MediaBrowser.Providers.Manager item.AfterMetadataRefresh(); return _cachedTask; } - - private void MergeIdentities(TItemType item, TIdType id) - { - var hasIdentity = id as IHasIdentities<IItemIdentity>; - if (hasIdentity != null) - { - item.Identities = hasIdentity.Identities.ToList(); - } - } - + private readonly Task<ItemUpdateType> _cachedResult = Task.FromResult(ItemUpdateType.None); /// <summary> /// Befores the save. @@ -479,13 +483,7 @@ namespace MediaBrowser.Providers.Manager } } - var isUnidentified = failedProviderCount > 0 && successfulProviderCount == 0; - - if (item.IsUnidentified != isUnidentified) - { - item.IsUnidentified = isUnidentified; - refreshResult.UpdateType = refreshResult.UpdateType | ItemUpdateType.MetadataImport; - } + //var isUnidentified = failedProviderCount > 0 && successfulProviderCount == 0; foreach (var provider in customProviders.Where(i => !(i is IPreRefreshProvider))) { @@ -503,7 +501,7 @@ namespace MediaBrowser.Providers.Manager { return false; } - + return true; } @@ -609,26 +607,6 @@ namespace MediaBrowser.Providers.Manager } } - private async Task<TIdType> CreateInitialLookupInfo(TItemType item, CancellationToken cancellationToken) - { - var info = item.GetLookupInfo(); - - var hasIdentity = info as IHasIdentities<IItemIdentity>; - if (hasIdentity != null) - { - try - { - await hasIdentity.FindIdentities(ProviderManager, cancellationToken).ConfigureAwait(false); - } - catch (Exception ex) - { - Logger.ErrorException("Error in identity providers", ex); - } - } - - return info; - } - private void MergeNewData(TItemType source, TIdType lookupInfo) { // Copy new provider id's that may have been obtained diff --git a/MediaBrowser.Providers/Manager/ProviderManager.cs b/MediaBrowser.Providers/Manager/ProviderManager.cs index fe0e4890c..b2a51377c 100644 --- a/MediaBrowser.Providers/Manager/ProviderManager.cs +++ b/MediaBrowser.Providers/Manager/ProviderManager.cs @@ -283,32 +283,31 @@ namespace MediaBrowser.Providers.Manager { var options = GetMetadataOptions(item); - return GetMetadataProvidersInternal<T>(item, options, false); + return GetMetadataProvidersInternal<T>(item, options, false, true); } - private IEnumerable<IMetadataProvider<T>> GetMetadataProvidersInternal<T>(IHasMetadata item, MetadataOptions options, bool includeDisabled) + private IEnumerable<IMetadataProvider<T>> GetMetadataProvidersInternal<T>(IHasMetadata item, MetadataOptions options, bool includeDisabled, bool checkIsOwnedItem) where T : IHasMetadata { // Avoid implicitly captured closure var currentOptions = options; return _metadataProviders.OfType<IMetadataProvider<T>>() - .Where(i => CanRefresh(i, item, currentOptions, includeDisabled)) + .Where(i => CanRefresh(i, item, currentOptions, includeDisabled, checkIsOwnedItem)) .OrderBy(i => GetConfiguredOrder(i, options)) .ThenBy(GetDefaultOrder); } - public IEnumerable<IItemIdentityProvider<TLookupInfo, TIdentity>> GetItemIdentityProviders<TLookupInfo, TIdentity>() + public IEnumerable<IItemIdentityProvider<TLookupInfo>> GetItemIdentityProviders<TLookupInfo>() where TLookupInfo : ItemLookupInfo - where TIdentity : IItemIdentity { - return _identityProviders.OfType<IItemIdentityProvider<TLookupInfo, TIdentity>>(); + return _identityProviders.OfType<IItemIdentityProvider<TLookupInfo>>(); } - public IEnumerable<IItemIdentityConverter<TIdentity>> GetItemIdentityConverters<TIdentity>() - where TIdentity : IItemIdentity + public IEnumerable<IItemIdentityConverter<TLookupInfo>> GetItemIdentityConverters<TLookupInfo>() + where TLookupInfo : ItemLookupInfo { - return _identityConverters.OfType<IItemIdentityConverter<TIdentity>>(); + return _identityConverters.OfType<IItemIdentityConverter<TLookupInfo>>(); } private IEnumerable<IRemoteImageProvider> GetRemoteImageProviders(IHasImages item, bool includeDisabled) @@ -318,7 +317,7 @@ namespace MediaBrowser.Providers.Manager return GetImageProviders(item, options, includeDisabled).OfType<IRemoteImageProvider>(); } - private bool CanRefresh(IMetadataProvider provider, IHasMetadata item, MetadataOptions options, bool includeDisabled) + private bool CanRefresh(IMetadataProvider provider, IHasMetadata item, MetadataOptions options, bool includeDisabled, bool checkIsOwnedItem) { if (!includeDisabled) { @@ -348,7 +347,7 @@ namespace MediaBrowser.Providers.Manager } // If this restriction is ever lifted, movie xml providers will have to be updated to prevent owned items like trailers from reading those files - if (item.IsOwnedItem) + if (checkIsOwnedItem && item.IsOwnedItem) { if (provider is ILocalMetadataProvider || provider is IRemoteMetadataProvider) { @@ -491,11 +490,10 @@ namespace MediaBrowser.Providers.Manager // Give it a dummy path just so that it looks like a file system item var dummy = new T() { - Path = Path.Combine(_appPaths.InternalMetadataPath, "dummy") + Path = Path.Combine(_appPaths.InternalMetadataPath, "dummy"), + ParentId = Guid.NewGuid() }; - dummy.SetParent(new Folder()); - var options = GetMetadataOptions(dummy); var summary = new MetadataPluginSummary @@ -523,7 +521,7 @@ namespace MediaBrowser.Providers.Manager private void AddMetadataPlugins<T>(List<MetadataPlugin> list, T item, MetadataOptions options) where T : IHasMetadata { - var providers = GetMetadataProvidersInternal<T>(item, options, true).ToList(); + var providers = GetMetadataProvidersInternal<T>(item, options, true, false).ToList(); // Locals list.AddRange(providers.Where(i => (i is ILocalMetadataProvider)).Select(i => new MetadataPlugin @@ -701,7 +699,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; } @@ -732,14 +730,15 @@ namespace MediaBrowser.Providers.Manager // Give it a dummy path just so that it looks like a file system item var dummy = new TItemType { - Path = Path.Combine(_appPaths.InternalMetadataPath, "dummy") + Path = Path.Combine(_appPaths.InternalMetadataPath, "dummy"), + ParentId = Guid.NewGuid() }; dummy.SetParent(new Folder()); var options = GetMetadataOptions(dummy); - var providers = GetMetadataProvidersInternal<TItemType>(dummy, options, searchInfo.IncludeDisabledProviders) + var providers = GetMetadataProvidersInternal<TItemType>(dummy, options, searchInfo.IncludeDisabledProviders, false) .OfType<IRemoteSearchProvider<TLookupType>>(); if (!string.IsNullOrEmpty(searchInfo.SearchProviderName)) @@ -880,6 +879,11 @@ namespace MediaBrowser.Providers.Manager private void StartRefreshTimer() { + if (_disposed) + { + return; + } + lock (_refreshTimerLock) { if (_refreshTimer == null) @@ -1014,6 +1018,7 @@ namespace MediaBrowser.Providers.Manager public void Dispose() { _disposed = true; + StopRefreshTimer(); } } }
\ No newline at end of file diff --git a/MediaBrowser.Providers/Manager/ProviderUtils.cs b/MediaBrowser.Providers/Manager/ProviderUtils.cs index fa4840f10..20b2ac6cd 100644 --- a/MediaBrowser.Providers/Manager/ProviderUtils.cs +++ b/MediaBrowser.Providers/Manager/ProviderUtils.cs @@ -227,14 +227,8 @@ namespace MediaBrowser.Providers.Manager target.DateCreated = source.DateCreated; } - var sourceHasLanguageSettings = source as IHasPreferredMetadataLanguage; - var targetHasLanguageSettings = target as IHasPreferredMetadataLanguage; - - if (sourceHasLanguageSettings != null && targetHasLanguageSettings != null) - { - targetHasLanguageSettings.PreferredMetadataCountryCode = sourceHasLanguageSettings.PreferredMetadataCountryCode; - targetHasLanguageSettings.PreferredMetadataLanguage = sourceHasLanguageSettings.PreferredMetadataLanguage; - } + target.PreferredMetadataCountryCode = source.PreferredMetadataCountryCode; + target.PreferredMetadataLanguage = source.PreferredMetadataLanguage; var sourceHasDisplayOrder = source as IHasDisplayOrder; var targetHasDisplayOrder = target as IHasDisplayOrder; diff --git a/MediaBrowser.Providers/MediaBrowser.Providers.csproj b/MediaBrowser.Providers/MediaBrowser.Providers.csproj index 1d323e567..1d12426b9 100644 --- a/MediaBrowser.Providers/MediaBrowser.Providers.csproj +++ b/MediaBrowser.Providers/MediaBrowser.Providers.csproj @@ -53,9 +53,9 @@ <SpecificVersion>False</SpecificVersion> <HintPath>..\packages\MediaBrowser.BdInfo.1.0.0.10\lib\net35\DvdLib.dll</HintPath> </Reference> - <Reference Include="MoreLinq, Version=1.1.17511.0, Culture=neutral, PublicKeyToken=384d532d7e88985d, processorArchitecture=MSIL"> + <Reference Include="MoreLinq, Version=1.1.18418.0, Culture=neutral, PublicKeyToken=384d532d7e88985d, processorArchitecture=MSIL"> <SpecificVersion>False</SpecificVersion> - <HintPath>..\packages\morelinq.1.1.0\lib\net35\MoreLinq.dll</HintPath> + <HintPath>..\packages\morelinq.1.1.1\lib\net35\MoreLinq.dll</HintPath> </Reference> <Reference Include="policy.2.0.taglib-sharp"> <HintPath>..\packages\taglib.2.1.0.0\lib\policy.2.0.taglib-sharp.dll</HintPath> diff --git a/MediaBrowser.Providers/MediaInfo/AudioImageProvider.cs b/MediaBrowser.Providers/MediaInfo/AudioImageProvider.cs index bd83862a8..e1775d259 100644 --- a/MediaBrowser.Providers/MediaInfo/AudioImageProvider.cs +++ b/MediaBrowser.Providers/MediaInfo/AudioImageProvider.cs @@ -43,7 +43,7 @@ namespace MediaBrowser.Providers.MediaInfo var audio = (Audio)item; // Can't extract if we didn't find a video stream in the file - if (!audio.HasEmbeddedImage) + if (!audio.GetMediaSources(false).Take(1).SelectMany(i => i.MediaStreams).Any(i => i.Type == MediaStreamType.EmbeddedImage)) { return Task.FromResult(new DynamicImageResponse { HasImage = false }); } @@ -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)) { diff --git a/MediaBrowser.Providers/MediaInfo/FFProbeAudioInfo.cs b/MediaBrowser.Providers/MediaInfo/FFProbeAudioInfo.cs index 0d4fc6720..4cf507d15 100644 --- a/MediaBrowser.Providers/MediaInfo/FFProbeAudioInfo.cs +++ b/MediaBrowser.Providers/MediaInfo/FFProbeAudioInfo.cs @@ -103,9 +103,8 @@ namespace MediaBrowser.Providers.MediaInfo { var mediaStreams = mediaInfo.MediaStreams; - audio.FormatName = mediaInfo.Container; + //audio.FormatName = mediaInfo.Container; audio.TotalBitrate = mediaInfo.Bitrate; - audio.HasEmbeddedImage = mediaStreams.Any(i => i.Type == MediaStreamType.EmbeddedImage); audio.RunTimeTicks = mediaInfo.RunTimeTicks; audio.Size = mediaInfo.Size; diff --git a/MediaBrowser.Providers/MediaInfo/FFProbeProvider.cs b/MediaBrowser.Providers/MediaInfo/FFProbeProvider.cs index c05f1b64b..2033107af 100644 --- a/MediaBrowser.Providers/MediaInfo/FFProbeProvider.cs +++ b/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) diff --git a/MediaBrowser.Providers/MediaInfo/VideoImageProvider.cs b/MediaBrowser.Providers/MediaInfo/VideoImageProvider.cs index bcea66662..46359731e 100644 --- a/MediaBrowser.Providers/MediaInfo/VideoImageProvider.cs +++ b/MediaBrowser.Providers/MediaInfo/VideoImageProvider.cs @@ -12,6 +12,7 @@ using System; using System.Collections.Generic; using System.Threading; using System.Threading.Tasks; +using MediaBrowser.Common.IO; namespace MediaBrowser.Providers.MediaInfo { @@ -22,14 +23,16 @@ namespace MediaBrowser.Providers.MediaInfo private readonly IServerConfigurationManager _config; private readonly ILibraryManager _libraryManager; private readonly ILogger _logger; + private readonly IFileSystem _fileSystem; - public VideoImageProvider(IIsoManager isoManager, IMediaEncoder mediaEncoder, IServerConfigurationManager config, ILibraryManager libraryManager, ILogger logger) + public VideoImageProvider(IIsoManager isoManager, IMediaEncoder mediaEncoder, IServerConfigurationManager config, ILibraryManager libraryManager, ILogger logger, IFileSystem fileSystem) { _isoManager = isoManager; _mediaEncoder = mediaEncoder; _config = config; _libraryManager = libraryManager; _logger = logger; + _fileSystem = fileSystem; } /// <summary> @@ -101,7 +104,7 @@ namespace MediaBrowser.Providers.MediaInfo ? MediaProtocol.Http : MediaProtocol.File; - var inputPath = MediaEncoderHelpers.GetInputArgument(item.Path, protocol, isoMount, item.PlayableStreamFileNames); + var inputPath = MediaEncoderHelpers.GetInputArgument(_fileSystem, item.Path, protocol, isoMount, item.PlayableStreamFileNames); var stream = await _mediaEncoder.ExtractVideoImage(inputPath, protocol, item.Video3DFormat, imageOffset, cancellationToken).ConfigureAwait(false); diff --git a/MediaBrowser.Providers/Movies/FanArtMovieUpdatesPostScanTask.cs b/MediaBrowser.Providers/Movies/FanArtMovieUpdatesPostScanTask.cs index f133f74db..8d0437f05 100644 --- a/MediaBrowser.Providers/Movies/FanArtMovieUpdatesPostScanTask.cs +++ b/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); } diff --git a/MediaBrowser.Providers/Movies/FanartMovieImageProvider.cs b/MediaBrowser.Providers/Movies/FanartMovieImageProvider.cs index 8d0c5cd54..3cf0c42b6 100644 --- a/MediaBrowser.Providers/Movies/FanartMovieImageProvider.cs +++ b/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 { diff --git a/MediaBrowser.Providers/Movies/GenericMovieDbInfo.cs b/MediaBrowser.Providers/Movies/GenericMovieDbInfo.cs index f5ee33d6b..504df02d5 100644 --- a/MediaBrowser.Providers/Movies/GenericMovieDbInfo.cs +++ b/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); } @@ -255,7 +258,14 @@ namespace MediaBrowser.Providers.Movies { foreach (var person in movieData.casts.crew) { - resultItem.AddPerson(new PersonInfo { Name = person.name.Trim(), Role = person.job, Type = person.department }); + // Normalize this + var type = person.department; + if (string.Equals(type, "writing", StringComparison.OrdinalIgnoreCase)) + { + type = PersonType.Writer; + } + + resultItem.AddPerson(new PersonInfo { Name = person.name.Trim(), Role = person.job, Type = type }); } } diff --git a/MediaBrowser.Providers/Movies/MovieDbProvider.cs b/MediaBrowser.Providers/Movies/MovieDbProvider.cs index 48b7140f8..4136d3601 100644 --- a/MediaBrowser.Providers/Movies/MovieDbProvider.cs +++ b/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); } diff --git a/MediaBrowser.Providers/Movies/MovieUpdatesPrescanTask.cs b/MediaBrowser.Providers/Movies/MovieUpdatesPrescanTask.cs index cc2142292..586e9ad40 100644 --- a/MediaBrowser.Providers/Movies/MovieUpdatesPrescanTask.cs +++ b/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); } diff --git a/MediaBrowser.Providers/Music/AudioDbAlbumProvider.cs b/MediaBrowser.Providers/Music/AudioDbAlbumProvider.cs index 3667d70cf..6437d2471 100644 --- a/MediaBrowser.Providers/Music/AudioDbAlbumProvider.cs +++ b/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 { diff --git a/MediaBrowser.Providers/Music/AudioDbArtistProvider.cs b/MediaBrowser.Providers/Music/AudioDbArtistProvider.cs index eefa1a2f4..5164c7df4 100644 --- a/MediaBrowser.Providers/Music/AudioDbArtistProvider.cs +++ b/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)) { diff --git a/MediaBrowser.Providers/Music/FanArtArtistProvider.cs b/MediaBrowser.Providers/Music/FanArtArtistProvider.cs index 597c5c0bc..7076a0016 100644 --- a/MediaBrowser.Providers/Music/FanArtArtistProvider.cs +++ b/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 { diff --git a/MediaBrowser.Providers/Music/FanArtUpdatesPostScanTask.cs b/MediaBrowser.Providers/Music/FanArtUpdatesPostScanTask.cs index a9b05c99f..86fb0899f 100644 --- a/MediaBrowser.Providers/Music/FanArtUpdatesPostScanTask.cs +++ b/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); } diff --git a/MediaBrowser.Providers/Omdb/OmdbImageProvider.cs b/MediaBrowser.Providers/Omdb/OmdbImageProvider.cs index 7f804f9df..3f25f0f93 100644 --- a/MediaBrowser.Providers/Omdb/OmdbImageProvider.cs +++ b/MediaBrowser.Providers/Omdb/OmdbImageProvider.cs @@ -79,8 +79,7 @@ namespace MediaBrowser.Providers.Omdb public bool Supports(IHasImages item) { - // Save the http requests since we know it's not currently supported - // TODO: Check again periodically + // We'll hammer Omdb if we enable this if (item is Person) { return false; diff --git a/MediaBrowser.Providers/People/MovieDbPersonProvider.cs b/MediaBrowser.Providers/People/MovieDbPersonProvider.cs index dd4231f3f..829296401 100644 --- a/MediaBrowser.Providers/People/MovieDbPersonProvider.cs +++ b/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)) { diff --git a/MediaBrowser.Providers/Photos/PhotoProvider.cs b/MediaBrowser.Providers/Photos/PhotoProvider.cs index b635d4ead..ef3144958 100644 --- a/MediaBrowser.Providers/Photos/PhotoProvider.cs +++ b/MediaBrowser.Providers/Photos/PhotoProvider.cs @@ -31,110 +31,111 @@ namespace MediaBrowser.Providers.Photos try { - var file = File.Create(item.Path); - - var image = file as TagLib.Image.File; - - var tag = file.GetTag(TagTypes.TiffIFD) as IFDTag; - - if (tag != null) + using (var file = TagLib.File.Create(item.Path)) { - var structure = tag.Structure; + var image = file as TagLib.Image.File; - if (structure != null) + var tag = file.GetTag(TagTypes.TiffIFD) as IFDTag; + + if (tag != null) { - var exif = structure.GetEntry(0, (ushort)IFDEntryTag.ExifIFD) as SubIFDEntry; + var structure = tag.Structure; - if (exif != null) + if (structure != null) { - var exifStructure = exif.Structure; + var exif = structure.GetEntry(0, (ushort)IFDEntryTag.ExifIFD) as SubIFDEntry; - if (exifStructure != null) + if (exif != null) { - var entry = exifStructure.GetEntry(0, (ushort)ExifEntryTag.ApertureValue) as RationalIFDEntry; + var exifStructure = exif.Structure; - if (entry != null) + if (exifStructure != null) { - double val = entry.Value.Numerator; - val /= entry.Value.Denominator; - item.Aperture = val; - } - - entry = exifStructure.GetEntry(0, (ushort)ExifEntryTag.ShutterSpeedValue) as RationalIFDEntry; - - if (entry != null) - { - double val = entry.Value.Numerator; - val /= entry.Value.Denominator; - item.ShutterSpeed = val; + var entry = exifStructure.GetEntry(0, (ushort)ExifEntryTag.ApertureValue) as RationalIFDEntry; + + if (entry != null) + { + double val = entry.Value.Numerator; + val /= entry.Value.Denominator; + item.Aperture = val; + } + + entry = exifStructure.GetEntry(0, (ushort)ExifEntryTag.ShutterSpeedValue) as RationalIFDEntry; + + if (entry != null) + { + double val = entry.Value.Numerator; + val /= entry.Value.Denominator; + item.ShutterSpeed = val; + } } } } } - } - - item.CameraMake = image.ImageTag.Make; - item.CameraModel = image.ImageTag.Model; - item.Width = image.Properties.PhotoWidth; - item.Height = image.Properties.PhotoHeight; + item.CameraMake = image.ImageTag.Make; + item.CameraModel = image.ImageTag.Model; - var rating = image.ImageTag.Rating; - if (rating.HasValue) - { - item.CommunityRating = rating; - } - else - { - item.CommunityRating = null; - } + item.Width = image.Properties.PhotoWidth; + item.Height = image.Properties.PhotoHeight; - item.Overview = image.ImageTag.Comment; + var rating = image.ImageTag.Rating; + if (rating.HasValue) + { + item.CommunityRating = rating; + } + else + { + item.CommunityRating = null; + } - if (!string.IsNullOrWhiteSpace(image.ImageTag.Title)) - { - item.Name = image.ImageTag.Title; - } + item.Overview = image.ImageTag.Comment; - var dateTaken = image.ImageTag.DateTime; - if (dateTaken.HasValue) - { - item.DateCreated = dateTaken.Value; - item.PremiereDate = dateTaken.Value; - item.ProductionYear = dateTaken.Value.Year; - } + if (!string.IsNullOrWhiteSpace(image.ImageTag.Title)) + { + item.Name = image.ImageTag.Title; + } - item.Genres = image.ImageTag.Genres.ToList(); - item.Tags = image.ImageTag.Keywords.ToList(); - item.Software = image.ImageTag.Software; + var dateTaken = image.ImageTag.DateTime; + if (dateTaken.HasValue) + { + item.DateCreated = dateTaken.Value; + item.PremiereDate = dateTaken.Value; + item.ProductionYear = dateTaken.Value.Year; + } - if (image.ImageTag.Orientation == TagLib.Image.ImageOrientation.None) - { - item.Orientation = null; - } - else - { - Model.Drawing.ImageOrientation orientation; - if (Enum.TryParse(image.ImageTag.Orientation.ToString(), true, out orientation)) + item.Genres = image.ImageTag.Genres.ToList(); + item.Tags = image.ImageTag.Keywords.ToList(); + item.Software = image.ImageTag.Software; + + if (image.ImageTag.Orientation == TagLib.Image.ImageOrientation.None) { - item.Orientation = orientation; + item.Orientation = null; + } + else + { + Model.Drawing.ImageOrientation orientation; + if (Enum.TryParse(image.ImageTag.Orientation.ToString(), true, out orientation)) + { + item.Orientation = orientation; + } } - } - item.ExposureTime = image.ImageTag.ExposureTime; - item.FocalLength = image.ImageTag.FocalLength; + item.ExposureTime = image.ImageTag.ExposureTime; + item.FocalLength = image.ImageTag.FocalLength; - item.Latitude = image.ImageTag.Latitude; - item.Longitude = image.ImageTag.Longitude; - item.Altitude = image.ImageTag.Altitude; + item.Latitude = image.ImageTag.Latitude; + item.Longitude = image.ImageTag.Longitude; + item.Altitude = image.ImageTag.Altitude; - if (image.ImageTag.ISOSpeedRatings.HasValue) - { - item.IsoSpeedRating = Convert.ToInt32(image.ImageTag.ISOSpeedRatings.Value); - } - else - { - item.IsoSpeedRating = null; + if (image.ImageTag.ISOSpeedRatings.HasValue) + { + item.IsoSpeedRating = Convert.ToInt32(image.ImageTag.ISOSpeedRatings.Value); + } + else + { + item.IsoSpeedRating = null; + } } } catch (Exception e) diff --git a/MediaBrowser.Providers/Subtitles/OpenSubtitleDownloader.cs b/MediaBrowser.Providers/Subtitles/OpenSubtitleDownloader.cs index 54db0d5fa..5e48b79e3 100644 --- a/MediaBrowser.Providers/Subtitles/OpenSubtitleDownloader.cs +++ b/MediaBrowser.Providers/Subtitles/OpenSubtitleDownloader.cs @@ -196,7 +196,7 @@ namespace MediaBrowser.Providers.Subtitles if (!(loginResponse is MethodResponseLogIn)) { - throw new UnauthorizedAccessException("Authentication to OpenSubtitles failed."); + throw new Exception("Authentication to OpenSubtitles failed."); } _lastLogin = DateTime.UtcNow; diff --git a/MediaBrowser.Providers/Subtitles/SubtitleManager.cs b/MediaBrowser.Providers/Subtitles/SubtitleManager.cs index c449edd07..b07f5b00a 100644 --- a/MediaBrowser.Providers/Subtitles/SubtitleManager.cs +++ b/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 diff --git a/MediaBrowser.Providers/TV/DummySeasonProvider.cs b/MediaBrowser.Providers/TV/DummySeasonProvider.cs index 426ff4318..c61b4f279 100644 --- a/MediaBrowser.Providers/TV/DummySeasonProvider.cs +++ b/MediaBrowser.Providers/TV/DummySeasonProvider.cs @@ -10,6 +10,7 @@ using System.Globalization; using System.Linq; using System.Threading; using System.Threading.Tasks; +using MediaBrowser.Common.IO; namespace MediaBrowser.Providers.TV { @@ -19,15 +20,17 @@ namespace MediaBrowser.Providers.TV private readonly ILogger _logger; private readonly ILocalizationManager _localization; private readonly ILibraryManager _libraryManager; + private readonly IFileSystem _fileSystem; private readonly CultureInfo _usCulture = new CultureInfo("en-US"); - public DummySeasonProvider(IServerConfigurationManager config, ILogger logger, ILocalizationManager localization, ILibraryManager libraryManager) + public DummySeasonProvider(IServerConfigurationManager config, ILogger logger, ILocalizationManager localization, ILibraryManager libraryManager, IFileSystem fileSystem) { _config = config; _logger = logger; _localization = localization; _libraryManager = libraryManager; + _fileSystem = fileSystem; } public async Task Run(Series series, CancellationToken cancellationToken) @@ -38,7 +41,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 +121,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; } diff --git a/MediaBrowser.Providers/TV/FanArtTvUpdatesPostScanTask.cs b/MediaBrowser.Providers/TV/FanArtTvUpdatesPostScanTask.cs index 64c6488fb..32fd0b119 100644 --- a/MediaBrowser.Providers/TV/FanArtTvUpdatesPostScanTask.cs +++ b/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); } diff --git a/MediaBrowser.Providers/TV/FanartSeriesProvider.cs b/MediaBrowser.Providers/TV/FanartSeriesProvider.cs index 6af2aa38d..a353ef4bd 100644 --- a/MediaBrowser.Providers/TV/FanartSeriesProvider.cs +++ b/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 { diff --git a/MediaBrowser.Providers/TV/MissingEpisodeProvider.cs b/MediaBrowser.Providers/TV/MissingEpisodeProvider.cs index 957345607..1443c524f 100644 --- a/MediaBrowser.Providers/TV/MissingEpisodeProvider.cs +++ b/MediaBrowser.Providers/TV/MissingEpisodeProvider.cs @@ -15,6 +15,7 @@ using System.Text; using System.Threading; using System.Threading.Tasks; using System.Xml; +using MediaBrowser.Common.IO; namespace MediaBrowser.Providers.TV { @@ -24,15 +25,17 @@ namespace MediaBrowser.Providers.TV private readonly ILogger _logger; private readonly ILibraryManager _libraryManager; private readonly ILocalizationManager _localization; + private readonly IFileSystem _fileSystem; private readonly CultureInfo _usCulture = new CultureInfo("en-US"); - public MissingEpisodeProvider(ILogger logger, IServerConfigurationManager config, ILibraryManager libraryManager, ILocalizationManager localization) + public MissingEpisodeProvider(ILogger logger, IServerConfigurationManager config, ILibraryManager libraryManager, ILocalizationManager localization, IFileSystem fileSystem) { _logger = logger; _config = config; _libraryManager = libraryManager; _localization = localization; + _fileSystem = fileSystem; } public async Task Run(IEnumerable<IGrouping<string, Series>> series, CancellationToken cancellationToken) @@ -119,7 +122,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) { @@ -395,7 +398,7 @@ namespace MediaBrowser.Providers.TV if (season == null) { - var provider = new DummySeasonProvider(_config, _logger, _localization, _libraryManager); + var provider = new DummySeasonProvider(_config, _logger, _localization, _libraryManager, _fileSystem); season = await provider.AddSeason(series, seasonNumber, cancellationToken).ConfigureAwait(false); } @@ -413,7 +416,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); } diff --git a/MediaBrowser.Providers/TV/MovieDbEpisodeImageProvider.cs b/MediaBrowser.Providers/TV/MovieDbEpisodeImageProvider.cs index ff5e6025d..aaa9bbacf 100644 --- a/MediaBrowser.Providers/TV/MovieDbEpisodeImageProvider.cs +++ b/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); } diff --git a/MediaBrowser.Providers/TV/MovieDbSeasonProvider.cs b/MediaBrowser.Providers/TV/MovieDbSeasonProvider.cs index 44c36af8f..fb4ce2c37 100644 --- a/MediaBrowser.Providers/TV/MovieDbSeasonProvider.cs +++ b/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); } diff --git a/MediaBrowser.Providers/TV/MovieDbSeriesProvider.cs b/MediaBrowser.Providers/TV/MovieDbSeriesProvider.cs index f861ad553..78595718d 100644 --- a/MediaBrowser.Providers/TV/MovieDbSeriesProvider.cs +++ b/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); } diff --git a/MediaBrowser.Providers/TV/SeriesMetadataService.cs b/MediaBrowser.Providers/TV/SeriesMetadataService.cs index 0b2aaa5a0..6a038b0ac 100644 --- a/MediaBrowser.Providers/TV/SeriesMetadataService.cs +++ b/MediaBrowser.Providers/TV/SeriesMetadataService.cs @@ -29,7 +29,7 @@ namespace MediaBrowser.Providers.TV if (refreshOptions.IsPostRecursiveRefresh) { - var provider = new DummySeasonProvider(ServerConfigurationManager, Logger, _localization, LibraryManager); + var provider = new DummySeasonProvider(ServerConfigurationManager, Logger, _localization, LibraryManager, FileSystem); try { @@ -62,11 +62,6 @@ namespace MediaBrowser.Providers.TV var sourceItem = source.Item; var targetItem = target.Item; - if (replaceData || targetItem.SeasonCount == 0) - { - targetItem.SeasonCount = sourceItem.SeasonCount; - } - if (replaceData || string.IsNullOrEmpty(targetItem.AirTime)) { targetItem.AirTime = sourceItem.AirTime; diff --git a/MediaBrowser.Providers/TV/SeriesPostScanTask.cs b/MediaBrowser.Providers/TV/SeriesPostScanTask.cs index 874b5c92d..991aba6de 100644 --- a/MediaBrowser.Providers/TV/SeriesPostScanTask.cs +++ b/MediaBrowser.Providers/TV/SeriesPostScanTask.cs @@ -10,6 +10,7 @@ using System.Collections.Generic; using System.Linq; using System.Threading; using System.Threading.Tasks; +using MediaBrowser.Common.IO; namespace MediaBrowser.Providers.TV { @@ -27,13 +28,15 @@ namespace MediaBrowser.Providers.TV private readonly IServerConfigurationManager _config; private readonly ILogger _logger; private readonly ILocalizationManager _localization; + private readonly IFileSystem _fileSystem; - public SeriesPostScanTask(ILibraryManager libraryManager, ILogger logger, IServerConfigurationManager config, ILocalizationManager localization) + public SeriesPostScanTask(ILibraryManager libraryManager, ILogger logger, IServerConfigurationManager config, ILocalizationManager localization, IFileSystem fileSystem) { _libraryManager = libraryManager; _logger = logger; _config = config; _localization = localization; + _fileSystem = fileSystem; } public Task Run(IProgress<double> progress, CancellationToken cancellationToken) @@ -50,7 +53,7 @@ namespace MediaBrowser.Providers.TV var seriesGroups = FindSeriesGroups(seriesList).Where(g => !string.IsNullOrEmpty(g.Key)).ToList(); - await new MissingEpisodeProvider(_logger, _config, _libraryManager, _localization).Run(seriesGroups, cancellationToken).ConfigureAwait(false); + await new MissingEpisodeProvider(_logger, _config, _libraryManager, _localization, _fileSystem).Run(seriesGroups, cancellationToken).ConfigureAwait(false); var numComplete = 0; @@ -65,12 +68,6 @@ namespace MediaBrowser.Providers.TV var physicalEpisodes = episodes.Where(i => i.LocationType != LocationType.Virtual) .ToList(); - series.SeasonCount = episodes - .Select(i => i.ParentIndexNumber ?? 0) - .Where(i => i != 0) - .Distinct() - .Count(); - series.SpecialFeatureIds = physicalEpisodes .Where(i => i.ParentIndexNumber.HasValue && i.ParentIndexNumber.Value == 0) .Select(i => i.Id) diff --git a/MediaBrowser.Providers/TV/TvdbEpisodeProvider.cs b/MediaBrowser.Providers/TV/TvdbEpisodeProvider.cs index 1702a5044..ec713064e 100644 --- a/MediaBrowser.Providers/TV/TvdbEpisodeProvider.cs +++ b/MediaBrowser.Providers/TV/TvdbEpisodeProvider.cs @@ -24,8 +24,11 @@ namespace MediaBrowser.Providers.TV /// <summary> /// Class RemoteEpisodeProvider /// </summary> - class TvdbEpisodeProvider : IRemoteMetadataProvider<Episode, EpisodeInfo>, IItemIdentityProvider<EpisodeInfo, EpisodeIdentity>, IHasChangeMonitor + class TvdbEpisodeProvider : IRemoteMetadataProvider<Episode, EpisodeInfo>, IItemIdentityProvider<EpisodeInfo>, IHasChangeMonitor { + private const string FullIdFormat = "{0}:{1}:{2}"; // seriesId:seasonIndex:episodeNumbers + private static readonly string FullIdKey = MetadataProviders.Tvdb + "-Full"; + internal static TvdbEpisodeProvider Current; private readonly IFileSystem _fileSystem; private readonly IServerConfigurationManager _config; @@ -45,18 +48,24 @@ namespace MediaBrowser.Providers.TV { var list = new List<RemoteSearchResult>(); - var identity = searchInfo.Identities.FirstOrDefault(id => id.Type == MetadataProviders.Tvdb.ToString()) ?? await FindIdentity(searchInfo).ConfigureAwait(false); + var identity = Identity.ParseIdentity(searchInfo.GetProviderId(FullIdKey)); + + if (identity == null) + { + await Identify(searchInfo).ConfigureAwait(false); + identity = Identity.ParseIdentity(searchInfo.GetProviderId(FullIdKey)); + } if (identity != null) { - await TvdbSeriesProvider.Current.EnsureSeriesInfo(identity.SeriesId, searchInfo.MetadataLanguage, + await TvdbSeriesProvider.Current.EnsureSeriesInfo(identity.Value.SeriesId, searchInfo.MetadataLanguage, cancellationToken).ConfigureAwait(false); - var seriesDataPath = TvdbSeriesProvider.GetSeriesDataPath(_config.ApplicationPaths, identity.SeriesId); + var seriesDataPath = TvdbSeriesProvider.GetSeriesDataPath(_config.ApplicationPaths, identity.Value.SeriesId); try { - var metadataResult = FetchEpisodeData(searchInfo, identity, seriesDataPath, searchInfo.SeriesProviderIds, cancellationToken); + var metadataResult = FetchEpisodeData(searchInfo, identity.Value, seriesDataPath, searchInfo.SeriesProviderIds, cancellationToken); if (metadataResult.HasMetadata) { @@ -95,17 +104,23 @@ namespace MediaBrowser.Providers.TV public async Task<MetadataResult<Episode>> GetMetadata(EpisodeInfo searchInfo, CancellationToken cancellationToken) { - var identity = searchInfo.Identities.FirstOrDefault(id => id.Type == MetadataProviders.Tvdb.ToString()) ?? await FindIdentity(searchInfo).ConfigureAwait(false); + var identity = Identity.ParseIdentity(searchInfo.GetProviderId(FullIdKey)); + + if (identity == null) + { + await Identify(searchInfo).ConfigureAwait(false); + identity = Identity.ParseIdentity(searchInfo.GetProviderId(FullIdKey)); + } var result = new MetadataResult<Episode>(); if (identity != null) { - var seriesDataPath = TvdbSeriesProvider.GetSeriesDataPath(_config.ApplicationPaths, identity.SeriesId); + var seriesDataPath = TvdbSeriesProvider.GetSeriesDataPath(_config.ApplicationPaths, identity.Value.SeriesId); try { - result = FetchEpisodeData(searchInfo, identity, seriesDataPath, searchInfo.SeriesProviderIds, cancellationToken); + result = FetchEpisodeData(searchInfo, identity.Value, seriesDataPath, searchInfo.SeriesProviderIds, cancellationToken); } catch (FileNotFoundException) { @@ -231,9 +246,9 @@ namespace MediaBrowser.Providers.TV /// <param name="seriesProviderIds">The series provider ids.</param> /// <param name="cancellationToken">The cancellation token.</param> /// <returns>Task{System.Boolean}.</returns> - private MetadataResult<Episode> FetchEpisodeData(EpisodeInfo id, EpisodeIdentity identity, string seriesDataPath, Dictionary<string, string> seriesProviderIds, CancellationToken cancellationToken) + private MetadataResult<Episode> FetchEpisodeData(EpisodeInfo id, Identity identity, string seriesDataPath, Dictionary<string, string> seriesProviderIds, CancellationToken cancellationToken) { - var episodeNumber = identity.IndexNumber; + var episodeNumber = identity.EpisodeNumber; var seasonOffset = TvdbSeriesProvider.GetSeriesOffset(seriesProviderIds) ?? 0; var seasonNumber = identity.SeasonIndex + seasonOffset; @@ -278,7 +293,7 @@ namespace MediaBrowser.Providers.TV usingAbsoluteData = true; } - var end = identity.IndexNumberEnd ?? episodeNumber; + var end = identity.EpisodeNumberEnd ?? episodeNumber; episodeNumber++; while (episodeNumber <= end) @@ -753,28 +768,86 @@ namespace MediaBrowser.Providers.TV }); } - public Task<EpisodeIdentity> FindIdentity(EpisodeInfo info) + public Task Identify(EpisodeInfo info) { + if (info.ProviderIds.ContainsKey(FullIdKey)) + { + return Task.FromResult<object>(null); + } + string seriesTvdbId; info.SeriesProviderIds.TryGetValue(MetadataProviders.Tvdb.ToString(), out seriesTvdbId); if (string.IsNullOrEmpty(seriesTvdbId) || info.IndexNumber == null) { - return Task.FromResult<EpisodeIdentity>(null); + return Task.FromResult<object>(null); } - - var id = new EpisodeIdentity - { - Type = MetadataProviders.Tvdb.ToString(), - SeriesId = seriesTvdbId, - SeasonIndex = info.ParentIndexNumber, - IndexNumber = info.IndexNumber.Value, - IndexNumberEnd = info.IndexNumberEnd - }; + + var id = new Identity(seriesTvdbId, info.ParentIndexNumber, info.IndexNumber.Value, info.IndexNumberEnd); + info.SetProviderId(FullIdKey, id.ToString()); return Task.FromResult(id); } public int Order { get { return 0; } } + + public struct Identity + { + public string SeriesId { get; private set; } + public int? SeasonIndex { get; private set; } + public int EpisodeNumber { get; private set; } + public int? EpisodeNumberEnd { get; private set; } + + public Identity(string id) + : this() + { + this = ParseIdentity(id).Value; + } + + public Identity(string seriesId, int? seasonIndex, int episodeNumber, int? episodeNumberEnd) + : this() + { + SeriesId = seriesId; + SeasonIndex = seasonIndex; + EpisodeNumber = episodeNumber; + EpisodeNumberEnd = episodeNumberEnd; + } + + public override string ToString() + { + return string.Format("{0}:{1}:{2}", + SeriesId, + SeasonIndex != null ? SeasonIndex.Value.ToString() : "A", + EpisodeNumber + (EpisodeNumberEnd != null ? "-" + EpisodeNumberEnd.Value.ToString() : "")); + } + + public static Identity? ParseIdentity(string id) + { + if (string.IsNullOrEmpty(id)) + return null; + + try { + var parts = id.Split(':'); + var series = parts[0]; + var season = parts[1] != "A" ? (int?)int.Parse(parts[1]) : null; + + int index; + int? indexEnd; + + if (parts[2].Contains("-")) { + var split = parts[2].IndexOf("-", StringComparison.OrdinalIgnoreCase); + index = int.Parse(parts[2].Substring(0, split)); + indexEnd = int.Parse(parts[2].Substring(split + 1)); + } else { + index = int.Parse(parts[2]); + indexEnd = null; + } + + return new Identity(series, season, index, indexEnd); + } catch { + return null; + } + } + } } } diff --git a/MediaBrowser.Providers/TV/TvdbPrescanTask.cs b/MediaBrowser.Providers/TV/TvdbPrescanTask.cs index 4bc4a2c8d..041f91355 100644 --- a/MediaBrowser.Providers/TV/TvdbPrescanTask.cs +++ b/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); } diff --git a/MediaBrowser.Providers/TV/TvdbSeasonIdentityProvider.cs b/MediaBrowser.Providers/TV/TvdbSeasonIdentityProvider.cs index 5876c9998..edeea36e4 100644 --- a/MediaBrowser.Providers/TV/TvdbSeasonIdentityProvider.cs +++ b/MediaBrowser.Providers/TV/TvdbSeasonIdentityProvider.cs @@ -4,26 +4,57 @@ using MediaBrowser.Model.Entities; namespace MediaBrowser.Providers.TV { - public class TvdbSeasonIdentityProvider : IItemIdentityProvider<SeasonInfo, SeasonIdentity> + public class TvdbSeasonIdentityProvider : IItemIdentityProvider<SeasonInfo> { - public Task<SeasonIdentity> FindIdentity(SeasonInfo info) + public static readonly string FullIdKey = MetadataProviders.Tvdb + "-Full"; + + public Task Identify(SeasonInfo info) { string tvdbSeriesId; if (!info.SeriesProviderIds.TryGetValue(MetadataProviders.Tvdb.ToString(), out tvdbSeriesId) || string.IsNullOrEmpty(tvdbSeriesId) || info.IndexNumber == null) { - return Task.FromResult<SeasonIdentity>(null); + return Task.FromResult<object>(null); + } + + if (string.IsNullOrEmpty(info.GetProviderId(FullIdKey))) + { + var id = string.Format("{0}:{1}", tvdbSeriesId, info.IndexNumber.Value); + info.SetProviderId(FullIdKey, id); } + + return Task.FromResult<object>(null); + } - var result = new SeasonIdentity + public static TvdbSeasonIdentity? ParseIdentity(string id) + { + try + { + var parts = id.Split(':'); + return new TvdbSeasonIdentity(parts[0], int.Parse(parts[1])); + } + catch { - Type = MetadataProviders.Tvdb.ToString(), - SeriesId = tvdbSeriesId, - SeasonIndex = info.IndexNumber.Value - }; + return null; + } + } + } + + public struct TvdbSeasonIdentity + { + public string SeriesId { get; private set; } + public int Index { get; private set; } - return Task.FromResult(result); + public TvdbSeasonIdentity(string id) + : this() + { + this = TvdbSeasonIdentityProvider.ParseIdentity(id).Value; } - public int Order { get { return 0; } } + public TvdbSeasonIdentity(string seriesId, int index) + : this() + { + SeriesId = seriesId; + Index = index; + } } }
\ No newline at end of file diff --git a/MediaBrowser.Providers/TV/TvdbSeasonImageProvider.cs b/MediaBrowser.Providers/TV/TvdbSeasonImageProvider.cs index 4dbf05d53..7f6224e5b 100644 --- a/MediaBrowser.Providers/TV/TvdbSeasonImageProvider.cs +++ b/MediaBrowser.Providers/TV/TvdbSeasonImageProvider.cs @@ -65,26 +65,23 @@ namespace MediaBrowser.Providers.TV var season = (Season)item; var series = season.Series; - var seriesId = series != null ? series.GetProviderId(MetadataProviders.Tvdb) : null; + var identity = TvdbSeasonIdentityProvider.ParseIdentity(season.GetProviderId(TvdbSeasonIdentityProvider.FullIdKey)); + if (identity == null && series != null && season.IndexNumber.HasValue) + { + identity = new TvdbSeasonIdentity(series.GetProviderId(MetadataProviders.Tvdb), season.IndexNumber.Value); + } - if (!string.IsNullOrEmpty(seriesId) && season.IndexNumber.HasValue) + if (identity != null && series != null) { - await TvdbSeriesProvider.Current.EnsureSeriesInfo(seriesId, series.GetPreferredMetadataLanguage(), cancellationToken).ConfigureAwait(false); + var id = identity.Value; + await TvdbSeriesProvider.Current.EnsureSeriesInfo(id.SeriesId, series.GetPreferredMetadataLanguage(), cancellationToken).ConfigureAwait(false); // Process images - var seriesDataPath = TvdbSeriesProvider.GetSeriesDataPath(_config.ApplicationPaths, seriesId); + var seriesDataPath = TvdbSeriesProvider.GetSeriesDataPath(_config.ApplicationPaths, id.SeriesId); var path = Path.Combine(seriesDataPath, "banners.xml"); - var identity = season.Identities.OfType<SeasonIdentity>() - .FirstOrDefault(id => id.Type == MetadataProviders.Tvdb.ToString()); - - var seasonNumber = season.IndexNumber.Value; - - if (identity != null) - { - seasonNumber = AdjustForSeriesOffset(series, identity.SeasonIndex); - } + var seasonNumber = AdjustForSeriesOffset(series, id.Index); try { diff --git a/MediaBrowser.Providers/TV/TvdbSeriesProvider.cs b/MediaBrowser.Providers/TV/TvdbSeriesProvider.cs index 3298fbc76..827e8bc53 100644 --- a/MediaBrowser.Providers/TV/TvdbSeriesProvider.cs +++ b/MediaBrowser.Providers/TV/TvdbSeriesProvider.cs @@ -25,7 +25,7 @@ using System.Xml; namespace MediaBrowser.Providers.TV { - public class TvdbSeriesProvider : IRemoteMetadataProvider<Series, SeriesInfo>, IItemIdentityProvider<SeriesInfo, SeriesIdentity>, IHasOrder + public class TvdbSeriesProvider : IRemoteMetadataProvider<Series, SeriesInfo>, IItemIdentityProvider<SeriesInfo>, IHasOrder { private const string TvdbSeriesOffset = "TvdbSeriesOffset"; private const string TvdbSeriesOffsetFormat = "{0}-{1}"; @@ -94,24 +94,10 @@ namespace MediaBrowser.Providers.TV if (string.IsNullOrWhiteSpace(seriesId)) { - seriesId = itemId.Identities - .Where(id => id.Type == MetadataProviders.Tvdb.ToString()) - .Select(id => id.Id) - .FirstOrDefault(); - - if (string.IsNullOrWhiteSpace(seriesId)) - { - var srch = await GetSearchResults(itemId, cancellationToken).ConfigureAwait(false); - - var entry = srch.FirstOrDefault(); - - if (entry != null) - { - seriesId = entry.GetProviderId(MetadataProviders.Tvdb); - } - } + await Identify(itemId).ConfigureAwait(false); + seriesId = itemId.GetProviderId(MetadataProviders.Tvdb); } - + cancellationToken.ThrowIfCancellationRequested(); if (!string.IsNullOrWhiteSpace(seriesId)) @@ -252,7 +238,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 +254,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 +1093,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 +1110,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,11 +1153,10 @@ namespace MediaBrowser.Providers.TV { try { - foreach (var file in new DirectoryInfo(path) - .EnumerateFiles("*.xml", SearchOption.AllDirectories) + foreach (var file in _fileSystem.GetFilePaths(path, true) .ToList()) { - _fileSystem.DeleteFile(file.FullName); + _fileSystem.DeleteFile(file); } } catch (DirectoryNotFoundException) @@ -1240,27 +1225,20 @@ namespace MediaBrowser.Providers.TV get { return "TheTVDB"; } } - public async Task<SeriesIdentity> FindIdentity(SeriesInfo info) + public async Task Identify(SeriesInfo info) { - string tvdbId; - if (!info.ProviderIds.TryGetValue(MetadataProviders.Tvdb.ToString(), out tvdbId)) + if (string.IsNullOrEmpty(info.GetProviderId(MetadataProviders.Tvdb))) { - var srch = await GetSearchResults(info, CancellationToken.None).ConfigureAwait(false); + var srch = await FindSeries(info.Name, info.MetadataLanguage, CancellationToken.None).ConfigureAwait(false); var entry = srch.FirstOrDefault(); if (entry != null) { - tvdbId = entry.GetProviderId(MetadataProviders.Tvdb); + var id = entry.GetProviderId(MetadataProviders.Tvdb); + info.SetProviderId(MetadataProviders.Tvdb, id); } } - - if (!string.IsNullOrWhiteSpace(tvdbId)) - { - return new SeriesIdentity { Type = MetadataProviders.Tvdb.ToString(), Id = tvdbId }; - } - - return null; } public int Order diff --git a/MediaBrowser.Providers/packages.config b/MediaBrowser.Providers/packages.config index be21e92dc..fe2b571a4 100644 --- a/MediaBrowser.Providers/packages.config +++ b/MediaBrowser.Providers/packages.config @@ -1,6 +1,6 @@ <?xml version="1.0" encoding="utf-8"?> <packages> <package id="MediaBrowser.BdInfo" version="1.0.0.10" targetFramework="net45" /> - <package id="morelinq" version="1.1.0" targetFramework="net45" /> + <package id="morelinq" version="1.1.1" targetFramework="net45" /> <package id="taglib" version="2.1.0.0" targetFramework="net45" /> </packages>
\ No newline at end of file diff --git a/MediaBrowser.Server.Implementations/Channels/ChannelManager.cs b/MediaBrowser.Server.Implementations/Channels/ChannelManager.cs index f670176e6..9b2f81217 100644 --- a/MediaBrowser.Server.Implementations/Channels/ChannelManager.cs +++ b/MediaBrowser.Server.Implementations/Channels/ChannelManager.cs @@ -31,7 +31,6 @@ namespace MediaBrowser.Server.Implementations.Channels public class ChannelManager : IChannelManager, IDisposable { private IChannel[] _channels; - private IChannelFactory[] _factories; private readonly IUserManager _userManager; private readonly IUserDataManager _userDataManager; @@ -76,10 +75,9 @@ namespace MediaBrowser.Server.Implementations.Channels } } - public void AddParts(IEnumerable<IChannel> channels, IEnumerable<IChannelFactory> factories) + public void AddParts(IEnumerable<IChannel> channels) { - _channels = channels.Where(i => !(i is IFactoryChannel)).ToArray(); - _factories = factories.ToArray(); + _channels = channels.ToArray(); } public string ChannelDownloadPath @@ -99,20 +97,7 @@ namespace MediaBrowser.Server.Implementations.Channels private IEnumerable<IChannel> GetAllChannels() { - return _factories - .SelectMany(i => - { - try - { - return i.GetChannels().ToList(); - } - catch (Exception ex) - { - _logger.ErrorException("Error getting channel list", ex); - return new List<IChannel>(); - } - }) - .Concat(_channels) + return _channels .OrderBy(i => i.Name); } @@ -318,7 +303,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 +396,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) @@ -432,23 +417,29 @@ namespace MediaBrowser.Server.Implementations.Channels Id = id, DateCreated = _fileSystem.GetCreationTimeUtc(fileInfo), DateModified = _fileSystem.GetLastWriteTimeUtc(fileInfo), - Path = path + Path = path, + ChannelId = channelInfo.Name.GetMD5().ToString("N") }; isNew = true; } + var channelId = channelInfo.Name.GetMD5().ToString("N"); + if (!string.Equals(item.ChannelId, channelId, StringComparison.OrdinalIgnoreCase)) + { + isNew = true; + } + item.OfficialRating = GetOfficialRating(channelInfo.ParentalRating); item.Overview = channelInfo.Description; item.HomePageUrl = channelInfo.HomePageUrl; - item.OriginalChannelName = channelInfo.Name; if (string.IsNullOrEmpty(item.Name)) { item.Name = channelInfo.Name; } - await item.RefreshMetadata(new MetadataRefreshOptions + await item.RefreshMetadata(new MetadataRefreshOptions(_fileSystem) { ForceSave = isNew @@ -1082,7 +1073,7 @@ namespace MediaBrowser.Server.Implementations.Channels { try { - Directory.CreateDirectory(Path.GetDirectoryName(path)); + _fileSystem.CreateDirectory(Path.GetDirectoryName(path)); _jsonSerializer.SerializeToFile(result, path); } @@ -1311,7 +1302,7 @@ namespace MediaBrowser.Server.Implementations.Channels internal IChannel GetChannelProvider(Channel channel) { - return GetAllChannels().First(i => string.Equals(i.Name, channel.OriginalChannelName, StringComparison.OrdinalIgnoreCase)); + return GetAllChannels().First(i => string.Equals(i.Name.GetMD5().ToString("N"), channel.ChannelId, StringComparison.OrdinalIgnoreCase) || string.Equals(i.Name, channel.Name, StringComparison.OrdinalIgnoreCase)); } private IEnumerable<BaseItem> ApplyFilters(IEnumerable<BaseItem> items, IEnumerable<ItemFilter> filters, User user) @@ -1462,7 +1453,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 +1491,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 { diff --git a/MediaBrowser.Server.Implementations/Collections/CollectionManager.cs b/MediaBrowser.Server.Implementations/Collections/CollectionManager.cs index 04f82db6f..430bae38e 100644 --- a/MediaBrowser.Server.Implementations/Collections/CollectionManager.cs +++ b/MediaBrowser.Server.Implementations/Collections/CollectionManager.cs @@ -21,17 +21,19 @@ namespace MediaBrowser.Server.Implementations.Collections private readonly IFileSystem _fileSystem; private readonly ILibraryMonitor _iLibraryMonitor; private readonly ILogger _logger; + private readonly IProviderManager _providerManager; public event EventHandler<CollectionCreatedEventArgs> CollectionCreated; public event EventHandler<CollectionModifiedEventArgs> ItemsAddedToCollection; public event EventHandler<CollectionModifiedEventArgs> ItemsRemovedFromCollection; - public CollectionManager(ILibraryManager libraryManager, IFileSystem fileSystem, ILibraryMonitor iLibraryMonitor, ILogger logger) + public CollectionManager(ILibraryManager libraryManager, IFileSystem fileSystem, ILibraryMonitor iLibraryMonitor, ILogger logger, IProviderManager providerManager) { _libraryManager = libraryManager; _fileSystem = fileSystem; _iLibraryMonitor = iLibraryMonitor; _logger = logger; + _providerManager = providerManager; } public Folder GetCollectionsFolder(string userId) @@ -70,7 +72,7 @@ namespace MediaBrowser.Server.Implementations.Collections try { - Directory.CreateDirectory(path); + _fileSystem.CreateDirectory(path); var collection = new BoxSet { @@ -88,13 +90,14 @@ namespace MediaBrowser.Server.Implementations.Collections await parentFolder.AddChild(collection, CancellationToken.None).ConfigureAwait(false); - await collection.RefreshMetadata(new MetadataRefreshOptions(new DirectoryService()), CancellationToken.None) - .ConfigureAwait(false); - if (options.ItemIdList.Count > 0) { await AddToCollection(collection.Id, options.ItemIdList, false); } + else + { + _providerManager.QueueRefresh(collection.Id, new MetadataRefreshOptions(_fileSystem)); + } EventHelper.FireEventIfNotNull(CollectionCreated, this, new CollectionCreatedEventArgs { @@ -181,7 +184,8 @@ namespace MediaBrowser.Server.Implementations.Collections collection.UpdateRatingToContent(); await collection.UpdateToRepository(ItemUpdateType.MetadataEdit, CancellationToken.None).ConfigureAwait(false); - await collection.RefreshMetadata(CancellationToken.None).ConfigureAwait(false); + + _providerManager.QueueRefresh(collection.Id, new MetadataRefreshOptions(_fileSystem)); if (fireEvent) { @@ -225,8 +229,8 @@ namespace MediaBrowser.Server.Implementations.Collections } } - var shortcutFiles = Directory - .EnumerateFiles(collection.Path, "*", SearchOption.TopDirectoryOnly) + var shortcutFiles = _fileSystem + .GetFilePaths(collection.Path) .Where(i => _fileSystem.IsShortcut(i)) .ToList(); @@ -263,7 +267,7 @@ namespace MediaBrowser.Server.Implementations.Collections collection.UpdateRatingToContent(); await collection.UpdateToRepository(ItemUpdateType.MetadataEdit, CancellationToken.None).ConfigureAwait(false); - await collection.RefreshMetadata(CancellationToken.None).ConfigureAwait(false); + _providerManager.QueueRefresh(collection.Id, new MetadataRefreshOptions(_fileSystem)); EventHelper.FireEventIfNotNull(ItemsRemovedFromCollection, this, new CollectionModifiedEventArgs { diff --git a/MediaBrowser.Server.Implementations/Collections/CollectionsDynamicFolder.cs b/MediaBrowser.Server.Implementations/Collections/CollectionsDynamicFolder.cs index 915a27c11..0fc7fb3f6 100644 --- a/MediaBrowser.Server.Implementations/Collections/CollectionsDynamicFolder.cs +++ b/MediaBrowser.Server.Implementations/Collections/CollectionsDynamicFolder.cs @@ -1,23 +1,26 @@ using MediaBrowser.Common.Configuration; using MediaBrowser.Controller.Entities; using System.IO; +using MediaBrowser.Common.IO; namespace MediaBrowser.Server.Implementations.Collections { public class CollectionsDynamicFolder : IVirtualFolderCreator { private readonly IApplicationPaths _appPaths; + private IFileSystem _fileSystem; - public CollectionsDynamicFolder(IApplicationPaths appPaths) + public CollectionsDynamicFolder(IApplicationPaths appPaths, IFileSystem fileSystem) { _appPaths = appPaths; + _fileSystem = fileSystem; } public BasePluginFolder GetFolder() { var path = Path.Combine(_appPaths.DataPath, "collections"); - Directory.CreateDirectory(path); + _fileSystem.CreateDirectory(path); return new ManualCollectionsFolder { diff --git a/MediaBrowser.Server.Implementations/Configuration/ServerConfigurationManager.cs b/MediaBrowser.Server.Implementations/Configuration/ServerConfigurationManager.cs index bf199503b..050b79db7 100644 --- a/MediaBrowser.Server.Implementations/Configuration/ServerConfigurationManager.cs +++ b/MediaBrowser.Server.Implementations/Configuration/ServerConfigurationManager.cs @@ -15,6 +15,7 @@ using MediaBrowser.Model.Serialization; using System; using System.IO; using System.Linq; +using MediaBrowser.Common.IO; namespace MediaBrowser.Server.Implementations.Configuration { @@ -23,15 +24,18 @@ namespace MediaBrowser.Server.Implementations.Configuration /// </summary> public class ServerConfigurationManager : BaseConfigurationManager, IServerConfigurationManager { + private readonly IFileSystem _fileSystem; + /// <summary> /// Initializes a new instance of the <see cref="ServerConfigurationManager" /> class. /// </summary> /// <param name="applicationPaths">The application paths.</param> /// <param name="logManager">The log manager.</param> /// <param name="xmlSerializer">The XML serializer.</param> - public ServerConfigurationManager(IApplicationPaths applicationPaths, ILogManager logManager, IXmlSerializer xmlSerializer) + public ServerConfigurationManager(IApplicationPaths applicationPaths, ILogManager logManager, IXmlSerializer xmlSerializer, IFileSystem fileSystem) : base(applicationPaths, logManager, xmlSerializer) { + _fileSystem = fileSystem; UpdateItemsByNamePath(); UpdateMetadataPath(); } @@ -198,7 +202,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 +222,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)); } diff --git a/MediaBrowser.Server.Implementations/Connect/ConnectEntryPoint.cs b/MediaBrowser.Server.Implementations/Connect/ConnectEntryPoint.cs index 8a659fb65..a8900fa5b 100644 --- a/MediaBrowser.Server.Implementations/Connect/ConnectEntryPoint.cs +++ b/MediaBrowser.Server.Implementations/Connect/ConnectEntryPoint.cs @@ -10,6 +10,7 @@ using System.IO; using System.Net; using System.Text; using System.Threading; +using MediaBrowser.Common.IO; namespace MediaBrowser.Server.Implementations.Connect { @@ -23,8 +24,9 @@ namespace MediaBrowser.Server.Implementations.Connect private readonly INetworkManager _networkManager; private readonly IApplicationHost _appHost; + private readonly IFileSystem _fileSystem; - public ConnectEntryPoint(IHttpClient httpClient, IApplicationPaths appPaths, ILogger logger, INetworkManager networkManager, IConnectManager connectManager, IApplicationHost appHost) + public ConnectEntryPoint(IHttpClient httpClient, IApplicationPaths appPaths, ILogger logger, INetworkManager networkManager, IConnectManager connectManager, IApplicationHost appHost, IFileSystem fileSystem) { _httpClient = httpClient; _appPaths = appPaths; @@ -32,6 +34,7 @@ namespace MediaBrowser.Server.Implementations.Connect _networkManager = networkManager; _connectManager = connectManager; _appHost = appHost; + _fileSystem = fileSystem; } public void Run() @@ -90,8 +93,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 +108,7 @@ namespace MediaBrowser.Server.Implementations.Connect try { - var endpoint = File.ReadAllText(path, Encoding.UTF8); + var endpoint = _fileSystem.ReadAllText(path, Encoding.UTF8); if (IsValid(endpoint)) { diff --git a/MediaBrowser.Server.Implementations/Connect/ConnectManager.cs b/MediaBrowser.Server.Implementations/Connect/ConnectManager.cs index 7cd96c5f3..f6e91528d 100644 --- a/MediaBrowser.Server.Implementations/Connect/ConnectManager.cs +++ b/MediaBrowser.Server.Implementations/Connect/ConnectManager.cs @@ -23,6 +23,7 @@ using System.Net; using System.Text; using System.Threading; using System.Threading.Tasks; +using MediaBrowser.Common.IO; namespace MediaBrowser.Server.Implementations.Connect { @@ -40,6 +41,7 @@ namespace MediaBrowser.Server.Implementations.Connect private readonly IUserManager _userManager; private readonly IProviderManager _providerManager; private readonly ISecurityManager _securityManager; + private readonly IFileSystem _fileSystem; private ConnectData _data = new ConnectData(); @@ -104,7 +106,7 @@ namespace MediaBrowser.Server.Implementations.Connect IEncryptionManager encryption, IHttpClient httpClient, IServerApplicationHost appHost, - IServerConfigurationManager config, IUserManager userManager, IProviderManager providerManager, ISecurityManager securityManager) + IServerConfigurationManager config, IUserManager userManager, IProviderManager providerManager, ISecurityManager securityManager, IFileSystem fileSystem) { _logger = logger; _appPaths = appPaths; @@ -116,6 +118,7 @@ namespace MediaBrowser.Server.Implementations.Connect _userManager = userManager; _providerManager = providerManager; _securityManager = securityManager; + _fileSystem = fileSystem; _userManager.UserConfigurationUpdated += _userManager_UserConfigurationUpdated; _config.ConfigurationUpdated += _config_ConfigurationUpdated; @@ -315,7 +318,7 @@ namespace MediaBrowser.Server.Implementations.Connect try { - Directory.CreateDirectory(Path.GetDirectoryName(path)); + _fileSystem.CreateDirectory(Path.GetDirectoryName(path)); var json = _json.SerializeToString(_data); @@ -323,7 +326,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 +343,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); @@ -943,7 +946,7 @@ namespace MediaBrowser.Server.Implementations.Connect { await _providerManager.SaveImage(user, imageUrl, _connectImageSemaphore, ImageType.Primary, null, CancellationToken.None).ConfigureAwait(false); - await user.RefreshMetadata(new MetadataRefreshOptions + await user.RefreshMetadata(new MetadataRefreshOptions(_fileSystem) { ForceSave = true, @@ -1076,20 +1079,25 @@ namespace MediaBrowser.Server.Implementations.Connect var url = GetConnectUrl("keyAssociation"); - url += "?serverId=" + ConnectServerId; - url += "&supporterKey=" + _securityManager.SupporterKey; - var options = new HttpRequestOptions { Url = url, CancellationToken = CancellationToken.None }; + var postData = new Dictionary<string, string> + { + {"serverId", ConnectServerId}, + {"supporterKey", _securityManager.SupporterKey} + }; + + options.SetPostData(postData); + SetServerAccessToken(options); SetApplicationHeader(options); // No need to examine the response - using (var stream = (await _httpClient.SendAsync(options, "GET").ConfigureAwait(false)).Content) + using (var stream = (await _httpClient.SendAsync(options, "POST").ConfigureAwait(false)).Content) { return _json.DeserializeFromStream<ConnectSupporterSummary>(stream); } @@ -1104,16 +1112,21 @@ namespace MediaBrowser.Server.Implementations.Connect var url = GetConnectUrl("keyAssociation"); - url += "?serverId=" + ConnectServerId; - url += "&supporterKey=" + _securityManager.SupporterKey; - url += "&userId=" + id; - var options = new HttpRequestOptions { Url = url, CancellationToken = CancellationToken.None }; + var postData = new Dictionary<string, string> + { + {"serverId", ConnectServerId}, + {"supporterKey", _securityManager.SupporterKey}, + {"userId", id} + }; + + options.SetPostData(postData); + SetServerAccessToken(options); SetApplicationHeader(options); @@ -1132,16 +1145,21 @@ namespace MediaBrowser.Server.Implementations.Connect var url = GetConnectUrl("keyAssociation"); - url += "?serverId=" + ConnectServerId; - url += "&supporterKey=" + _securityManager.SupporterKey; - url += "&userId=" + id; - var options = new HttpRequestOptions { Url = url, CancellationToken = CancellationToken.None }; + var postData = new Dictionary<string, string> + { + {"serverId", ConnectServerId}, + {"supporterKey", _securityManager.SupporterKey}, + {"userId", id} + }; + + options.SetPostData(postData); + SetServerAccessToken(options); SetApplicationHeader(options); diff --git a/MediaBrowser.Server.Implementations/Devices/CameraUploadsFolder.cs b/MediaBrowser.Server.Implementations/Devices/CameraUploadsFolder.cs index 566f4c5f4..15567703d 100644 --- a/MediaBrowser.Server.Implementations/Devices/CameraUploadsFolder.cs +++ b/MediaBrowser.Server.Implementations/Devices/CameraUploadsFolder.cs @@ -3,6 +3,7 @@ using MediaBrowser.Controller.Entities; using System; using System.IO; using System.Linq; +using MediaBrowser.Common.IO; namespace MediaBrowser.Server.Implementations.Devices { @@ -51,17 +52,19 @@ namespace MediaBrowser.Server.Implementations.Devices public class CameraUploadsDynamicFolder : IVirtualFolderCreator { private readonly IApplicationPaths _appPaths; + private readonly IFileSystem _fileSystem; - public CameraUploadsDynamicFolder(IApplicationPaths appPaths) + public CameraUploadsDynamicFolder(IApplicationPaths appPaths, IFileSystem fileSystem) { _appPaths = appPaths; + _fileSystem = fileSystem; } public BasePluginFolder GetFolder() { var path = Path.Combine(_appPaths.DataPath, "camerauploads"); - Directory.CreateDirectory(path); + _fileSystem.CreateDirectory(path); return new CameraUploadsFolder { diff --git a/MediaBrowser.Server.Implementations/Devices/DeviceManager.cs b/MediaBrowser.Server.Implementations/Devices/DeviceManager.cs index 0173f2784..548a2222a 100644 --- a/MediaBrowser.Server.Implementations/Devices/DeviceManager.cs +++ b/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 { @@ -290,4 +290,4 @@ namespace MediaBrowser.Server.Implementations.Devices return config.GetConfiguration<DevicesOptions>("devices"); } } -} +}
\ No newline at end of file diff --git a/MediaBrowser.Server.Implementations/Devices/DeviceRepository.cs b/MediaBrowser.Server.Implementations/Devices/DeviceRepository.cs index 6d324b1ab..853217826 100644 --- a/MediaBrowser.Server.Implementations/Devices/DeviceRepository.cs +++ b/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) { @@ -111,8 +111,8 @@ namespace MediaBrowser.Server.Implementations.Devices try { - return Directory - .EnumerateFiles(path, "*", SearchOption.AllDirectories) + return _fileSystem + .GetFilePaths(path, true) .Where(i => string.Equals(Path.GetFileName(i), "device.json", StringComparison.OrdinalIgnoreCase)) .ToList() .Select(i => @@ -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) { diff --git a/MediaBrowser.Server.Implementations/Dto/DtoService.cs b/MediaBrowser.Server.Implementations/Dto/DtoService.cs index edfef38fd..07374a6c3 100644 --- a/MediaBrowser.Server.Implementations/Dto/DtoService.cs +++ b/MediaBrowser.Server.Implementations/Dto/DtoService.cs @@ -908,12 +908,6 @@ namespace MediaBrowser.Server.Implementations.Dto dto.DisplayMediaType = item.DisplayMediaType; } - // Leave null if false - if (item.IsUnidentified) - { - dto.IsUnidentified = item.IsUnidentified; - } - if (fields.Contains(ItemFields.Settings)) { dto.LockedFields = item.LockedFields; @@ -1048,13 +1042,8 @@ namespace MediaBrowser.Server.Implementations.Dto dto.MediaType = item.MediaType; dto.LocationType = item.LocationType; - var hasLang = item as IHasPreferredMetadataLanguage; - - if (hasLang != null) - { - dto.PreferredMetadataCountryCode = hasLang.PreferredMetadataCountryCode; - dto.PreferredMetadataLanguage = hasLang.PreferredMetadataLanguage; - } + dto.PreferredMetadataCountryCode = item.PreferredMetadataCountryCode; + dto.PreferredMetadataLanguage = item.PreferredMetadataLanguage; var hasCriticRating = item as IHasCriticRating; if (hasCriticRating != null) @@ -1433,8 +1422,6 @@ namespace MediaBrowser.Server.Implementations.Dto dto.AirTime = series.AirTime; dto.SeriesStatus = series.Status; - dto.SeasonCount = series.SeasonCount; - if (fields.Contains(ItemFields.Settings)) { dto.DisplaySpecialsWithSeasons = series.DisplaySpecialsWithSeasons; diff --git a/MediaBrowser.Server.Implementations/EntryPoints/ExternalPortForwarding.cs b/MediaBrowser.Server.Implementations/EntryPoints/ExternalPortForwarding.cs index fa5841bb8..a2ffa9aff 100644 --- a/MediaBrowser.Server.Implementations/EntryPoints/ExternalPortForwarding.cs +++ b/MediaBrowser.Server.Implementations/EntryPoints/ExternalPortForwarding.cs @@ -147,7 +147,8 @@ namespace MediaBrowser.Server.Implementations.EntryPoints // It also can fail with others like 727-ExternalPortOnlySupportsWildcard, 728-NoPortMapsAvailable // and those errors (upnp errors) could be useful for diagnosting. - _logger.ErrorException("Error creating port forwarding rules", ex); + // Commenting out because users are reporting problems out of our control + //_logger.ErrorException("Error creating port forwarding rules", ex); } } diff --git a/MediaBrowser.Server.Implementations/EntryPoints/Notifications/RemoteNotifications.cs b/MediaBrowser.Server.Implementations/EntryPoints/Notifications/RemoteNotifications.cs index d5b7f5b36..932e94b2a 100644 --- a/MediaBrowser.Server.Implementations/EntryPoints/Notifications/RemoteNotifications.cs +++ b/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); } diff --git a/MediaBrowser.Server.Implementations/FileOrganization/EpisodeFileOrganizer.cs b/MediaBrowser.Server.Implementations/FileOrganization/EpisodeFileOrganizer.cs index 06b72e4ef..aa5a52512 100644 --- a/MediaBrowser.Server.Implementations/FileOrganization/EpisodeFileOrganizer.cs +++ b/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) @@ -256,7 +256,7 @@ namespace MediaBrowser.Server.Implementations.FileOrganization if (!string.IsNullOrWhiteSpace(originalFilenameWithoutExtension) && !string.IsNullOrWhiteSpace(directory)) { // Get all related files, e.g. metadata, images, etc - var files = Directory.EnumerateFiles(directory, "*", SearchOption.TopDirectoryOnly) + var files = _fileSystem.GetFilePaths(directory) .Where(i => (Path.GetFileNameWithoutExtension(i) ?? string.Empty).StartsWith(originalFilenameWithoutExtension, StringComparison.OrdinalIgnoreCase)) .ToList(); @@ -272,7 +272,7 @@ namespace MediaBrowser.Server.Implementations.FileOrganization var destination = Path.Combine(directory, filename); - File.Move(file, destination); + _fileSystem.MoveFile(file, destination); } } } @@ -313,7 +313,7 @@ namespace MediaBrowser.Server.Implementations.FileOrganization try { - var filesOfOtherExtensions = Directory.EnumerateFiles(folder, "*", SearchOption.TopDirectoryOnly) + var filesOfOtherExtensions = _fileSystem.GetFilePaths(folder) .Where(i => _libraryManager.IsVideoFile(i) && string.Equals(_fileSystem.GetFileNameWithoutExtension(i), targetFileNameWithoutExtension, StringComparison.OrdinalIgnoreCase)); episodePaths.AddRange(filesOfOtherExtensions); @@ -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; diff --git a/MediaBrowser.Server.Implementations/FileOrganization/TvFolderOrganizer.cs b/MediaBrowser.Server.Implementations/FileOrganization/TvFolderOrganizer.cs index 0caa8c26e..12cf86c17 100644 --- a/MediaBrowser.Server.Implementations/FileOrganization/TvFolderOrganizer.cs +++ b/MediaBrowser.Server.Implementations/FileOrganization/TvFolderOrganizer.cs @@ -46,6 +46,8 @@ namespace MediaBrowser.Server.Implementations.FileOrganization .Where(i => _libraryManager.IsVideoFile(i.FullName) && i.Length >= minFileBytes) .ToList(); + var processedFolders = new HashSet<string>(); + progress.Report(10); if (eligibleFiles.Count > 0) @@ -59,7 +61,11 @@ namespace MediaBrowser.Server.Implementations.FileOrganization try { - await organizer.OrganizeEpisodeFile(file.FullName, options, options.OverwriteExistingEpisodes, cancellationToken).ConfigureAwait(false); + var result = await organizer.OrganizeEpisodeFile(file.FullName, options, options.OverwriteExistingEpisodes, cancellationToken).ConfigureAwait(false); + if (result.Status == FileSortingStatus.Success && !processedFolders.Contains(file.DirectoryName, StringComparer.OrdinalIgnoreCase)) + { + processedFolders.Add(file.DirectoryName); + } } catch (Exception ex) { @@ -77,7 +83,7 @@ namespace MediaBrowser.Server.Implementations.FileOrganization cancellationToken.ThrowIfCancellationRequested(); progress.Report(99); - foreach (var path in watchLocations) + foreach (var path in processedFolders) { var deleteExtensions = options.LeftOverFileExtensionsToDelete .Select(i => i.Trim().TrimStart('.')) @@ -92,9 +98,9 @@ namespace MediaBrowser.Server.Implementations.FileOrganization if (options.DeleteEmptyFolders) { - foreach (var subfolder in GetDirectories(path).ToList()) + if (!IsWatchFolder(path, watchLocations)) { - DeleteEmptyFolders(subfolder); + DeleteEmptyFolders(path); } } } @@ -103,27 +109,6 @@ namespace MediaBrowser.Server.Implementations.FileOrganization } /// <summary> - /// Gets the directories. - /// </summary> - /// <param name="path">The path.</param> - /// <returns>IEnumerable{System.String}.</returns> - private IEnumerable<string> GetDirectories(string path) - { - try - { - return Directory - .EnumerateDirectories(path, "*", SearchOption.TopDirectoryOnly) - .ToList(); - } - catch (IOException ex) - { - _logger.ErrorException("Error getting files from {0}", ex, path); - - return new List<string>(); - } - } - - /// <summary> /// Gets the files to organize. /// </summary> /// <param name="path">The path.</param> @@ -132,8 +117,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 +135,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(); @@ -177,19 +160,19 @@ namespace MediaBrowser.Server.Implementations.FileOrganization { try { - foreach (var d in Directory.EnumerateDirectories(path)) + foreach (var d in _fileSystem.GetDirectoryPaths(path)) { DeleteEmptyFolders(d); } - var entries = Directory.EnumerateFileSystemEntries(path); + var entries = _fileSystem.GetFileSystemEntryPaths(path); if (!entries.Any()) { try { _logger.Debug("Deleting empty directory {0}", path); - Directory.Delete(path); + _fileSystem.DeleteDirectory(path, false); } catch (UnauthorizedAccessException) { } catch (DirectoryNotFoundException) { } @@ -197,5 +180,15 @@ namespace MediaBrowser.Server.Implementations.FileOrganization } catch (UnauthorizedAccessException) { } } + + /// <summary> + /// Determines if a given folder path is contained in a folder list + /// </summary> + /// <param name="path">The folder path to check.</param> + /// <param name="watchLocations">A list of folders.</param> + private bool IsWatchFolder(string path, IEnumerable<string> watchLocations) + { + return watchLocations.Contains(path, StringComparer.OrdinalIgnoreCase); + } } } diff --git a/MediaBrowser.Server.Implementations/HttpServer/HttpListenerHost.cs b/MediaBrowser.Server.Implementations/HttpServer/HttpListenerHost.cs index 3795f4b15..e8bb40ba1 100644 --- a/MediaBrowser.Server.Implementations/HttpServer/HttpListenerHost.cs +++ b/MediaBrowser.Server.Implementations/HttpServer/HttpListenerHost.cs @@ -79,6 +79,8 @@ namespace MediaBrowser.Server.Implementations.HttpServer _containerAdapter = new ContainerAdapter(applicationHost); } + public string GlobalResponse { get; set; } + public override void Configure(Container container) { HostConfig.Instance.DefaultRedirectPath = DefaultRedirectPath; @@ -90,7 +92,7 @@ namespace MediaBrowser.Server.Implementations.HttpServer {typeof (FileNotFoundException), 404}, {typeof (DirectoryNotFoundException), 404}, {typeof (SecurityException), 401}, - {typeof (UnauthorizedAccessException), 401} + {typeof (UnauthorizedAccessException), 500} }; HostConfig.Instance.DebugMode = true; @@ -336,6 +338,13 @@ namespace MediaBrowser.Server.Implementations.HttpServer return Task.FromResult(true); } + if (!string.IsNullOrWhiteSpace(GlobalResponse)) + { + httpRes.Write(GlobalResponse); + httpRes.ContentType = "text/plain"; + return Task.FromResult(true); + } + var handler = HttpHandlerFactory.GetHandler(httpReq); var remoteIp = httpReq.RemoteIp; diff --git a/MediaBrowser.Server.Implementations/HttpServer/Security/AuthorizationContext.cs b/MediaBrowser.Server.Implementations/HttpServer/Security/AuthorizationContext.cs index 80892b96c..ae5ce796e 100644 --- a/MediaBrowser.Server.Implementations/HttpServer/Security/AuthorizationContext.cs +++ b/MediaBrowser.Server.Implementations/HttpServer/Security/AuthorizationContext.cs @@ -69,47 +69,6 @@ namespace MediaBrowser.Server.Implementations.HttpServer.Security token = httpReq.QueryString["api_key"]; } - // Hack until iOS is updated - // TODO: Remove - if (string.IsNullOrWhiteSpace(client)) - { - var userAgent = httpReq.Headers["User-Agent"] ?? string.Empty; - - if (userAgent.IndexOf("mediabrowserios", StringComparison.OrdinalIgnoreCase) != -1 || - userAgent.IndexOf("iphone", StringComparison.OrdinalIgnoreCase) != -1 || - userAgent.IndexOf("ipad", StringComparison.OrdinalIgnoreCase) != -1) - { - client = "iOS"; - } - - else if (userAgent.IndexOf("crKey", StringComparison.OrdinalIgnoreCase) != -1) - { - client = "Chromecast"; - } - } - - // Hack until iOS is updated - // TODO: Remove - if (string.IsNullOrWhiteSpace(device)) - { - var userAgent = httpReq.Headers["User-Agent"] ?? string.Empty; - - if (userAgent.IndexOf("iPhone", StringComparison.OrdinalIgnoreCase) != -1) - { - device = "iPhone"; - } - - else if (userAgent.IndexOf("iPad", StringComparison.OrdinalIgnoreCase) != -1) - { - device = "iPad"; - } - - else if (userAgent.IndexOf("crKey", StringComparison.OrdinalIgnoreCase) != -1) - { - device = "Chromecast"; - } - } - var info = new AuthorizationInfo { Client = client, diff --git a/MediaBrowser.Server.Implementations/IO/LibraryMonitor.cs b/MediaBrowser.Server.Implementations/IO/LibraryMonitor.cs index 5bd26ce18..cb9d5a09f 100644 --- a/MediaBrowser.Server.Implementations/IO/LibraryMonitor.cs +++ b/MediaBrowser.Server.Implementations/IO/LibraryMonitor.cs @@ -645,7 +645,7 @@ namespace MediaBrowser.Server.Implementations.IO if (item != null) { // If the item has been deleted find the first valid parent that still exists - while (!Directory.Exists(item.Path) && !File.Exists(item.Path)) + while (!_fileSystem.DirectoryExists(item.Path) && !_fileSystem.FileExists(item.Path)) { item = item.Parent; diff --git a/MediaBrowser.Server.Implementations/Intros/DefaultIntroProvider.cs b/MediaBrowser.Server.Implementations/Intros/DefaultIntroProvider.cs index db9841f9d..ce3f0c66c 100644 --- a/MediaBrowser.Server.Implementations/Intros/DefaultIntroProvider.cs +++ b/MediaBrowser.Server.Implementations/Intros/DefaultIntroProvider.cs @@ -15,6 +15,7 @@ using System.IO; using System.Linq; using System.Threading; using System.Threading.Tasks; +using MediaBrowser.Common.IO; namespace MediaBrowser.Server.Implementations.Intros { @@ -25,14 +26,16 @@ namespace MediaBrowser.Server.Implementations.Intros private readonly ILocalizationManager _localization; private readonly IConfigurationManager _serverConfig; private readonly ILibraryManager _libraryManager; + private readonly IFileSystem _fileSystem; - public DefaultIntroProvider(ISecurityManager security, IChannelManager channelManager, ILocalizationManager localization, IConfigurationManager serverConfig, ILibraryManager libraryManager) + public DefaultIntroProvider(ISecurityManager security, IChannelManager channelManager, ILocalizationManager localization, IConfigurationManager serverConfig, ILibraryManager libraryManager, IFileSystem fileSystem) { _security = security; _channelManager = channelManager; _localization = localization; _serverConfig = serverConfig; _libraryManager = libraryManager; + _fileSystem = fileSystem; } public async Task<IEnumerable<IntroInfo>> GetIntros(BaseItem item, User user) @@ -232,7 +235,7 @@ namespace MediaBrowser.Server.Implementations.Intros return new List<string>(); } - return Directory.EnumerateFiles(options.CustomIntroPath, "*", SearchOption.AllDirectories) + return _fileSystem.GetFilePaths(options.CustomIntroPath, true) .Where(_libraryManager.IsVideoFile); } diff --git a/MediaBrowser.Server.Implementations/Library/LibraryManager.cs b/MediaBrowser.Server.Implementations/Library/LibraryManager.cs index cc9d9551c..0be871b0e 100644 --- a/MediaBrowser.Server.Implementations/Library/LibraryManager.cs +++ b/MediaBrowser.Server.Implementations/Library/LibraryManager.cs @@ -354,6 +354,10 @@ namespace MediaBrowser.Server.Implementations.Library return; } } + //if (!(item is Folder)) + //{ + // return; + //} LibraryItemsCache.AddOrUpdate(id, item, delegate { return item; }); } @@ -395,12 +399,12 @@ namespace MediaBrowser.Server.Implementations.Library { foreach (var path in item.GetDeletePaths().ToList()) { - if (Directory.Exists(path)) + if (_fileSystem.DirectoryExists(path)) { _logger.Debug("Deleting path {0}", path); _fileSystem.DeleteDirectory(path, true); } - else if (File.Exists(path)) + else if (_fileSystem.FileExists(path)) { _logger.Debug("Deleting path {0}", path); _fileSystem.DeleteFile(path); @@ -548,7 +552,7 @@ namespace MediaBrowser.Server.Implementations.Library public BaseItem ResolvePath(FileSystemInfo fileInfo, Folder parent = null) { - return ResolvePath(fileInfo, new DirectoryService(_logger), parent); + return ResolvePath(fileInfo, new DirectoryService(_logger, _fileSystem), parent); } private BaseItem ResolvePath(FileSystemInfo fileInfo, IDirectoryService directoryService, Folder parent = null, string collectionType = null) @@ -691,7 +695,7 @@ namespace MediaBrowser.Server.Implementations.Library { var rootFolderPath = ConfigurationManager.ApplicationPaths.RootFolderPath; - Directory.CreateDirectory(rootFolderPath); + _fileSystem.CreateDirectory(rootFolderPath); var rootFolder = GetItemById(GetNewItemId(rootFolderPath, typeof(AggregateFolder))) as AggregateFolder ?? (AggregateFolder)ResolvePath(new DirectoryInfo(rootFolderPath)); @@ -742,7 +746,7 @@ namespace MediaBrowser.Server.Implementations.Library { var userRootPath = ConfigurationManager.ApplicationPaths.DefaultUserViewsPath; - Directory.CreateDirectory(userRootPath); + _fileSystem.CreateDirectory(userRootPath); var tmpItem = GetItemById(GetNewItemId(userRootPath, typeof(UserRootFolder))) as UserRootFolder; @@ -1007,9 +1011,9 @@ namespace MediaBrowser.Server.Implementations.Library public Task ValidatePeople(CancellationToken cancellationToken, IProgress<double> progress) { // Ensure the location is available. - Directory.CreateDirectory(ConfigurationManager.ApplicationPaths.PeoplePath); + _fileSystem.CreateDirectory(ConfigurationManager.ApplicationPaths.PeoplePath); - return new PeopleValidator(this, _logger, ConfigurationManager).ValidatePeople(cancellationToken, progress); + return new PeopleValidator(this, _logger, ConfigurationManager, _fileSystem).ValidatePeople(cancellationToken, progress); } /// <summary> @@ -1064,7 +1068,7 @@ namespace MediaBrowser.Server.Implementations.Library progress.Report(.5); // Start by just validating the children of the root, but go no further - await RootFolder.ValidateChildren(new Progress<double>(), cancellationToken, new MetadataRefreshOptions(), recursive: false); + await RootFolder.ValidateChildren(new Progress<double>(), cancellationToken, new MetadataRefreshOptions(_fileSystem), recursive: false); progress.Report(1); @@ -1072,7 +1076,7 @@ namespace MediaBrowser.Server.Implementations.Library await userRoot.RefreshMetadata(cancellationToken).ConfigureAwait(false); - await userRoot.ValidateChildren(new Progress<double>(), cancellationToken, new MetadataRefreshOptions(), recursive: false).ConfigureAwait(false); + await userRoot.ValidateChildren(new Progress<double>(), cancellationToken, new MetadataRefreshOptions(_fileSystem), recursive: false).ConfigureAwait(false); progress.Report(2); var innerProgress = new ActionableProgress<double>(); @@ -1080,7 +1084,7 @@ namespace MediaBrowser.Server.Implementations.Library innerProgress.RegisterAction(pct => progress.Report(2 + pct * .73)); // Now validate the entire media library - await RootFolder.ValidateChildren(innerProgress, cancellationToken, new MetadataRefreshOptions(), recursive: true).ConfigureAwait(false); + await RootFolder.ValidateChildren(innerProgress, cancellationToken, new MetadataRefreshOptions(_fileSystem), recursive: true).ConfigureAwait(false); progress.Report(75); @@ -1165,7 +1169,7 @@ namespace MediaBrowser.Server.Implementations.Library /// <returns>IEnumerable{VirtualFolderInfo}.</returns> private IEnumerable<VirtualFolderInfo> GetView(string path) { - return Directory.EnumerateDirectories(path, "*", SearchOption.TopDirectoryOnly) + return _fileSystem.GetDirectoryPaths(path) .Select(dir => new VirtualFolderInfo { Name = Path.GetFileName(dir), @@ -1638,6 +1642,7 @@ namespace MediaBrowser.Server.Implementations.Library } private readonly TimeSpan _viewRefreshInterval = TimeSpan.FromHours(24); + //private readonly TimeSpan _viewRefreshInterval = TimeSpan.FromMinutes(1); public Task<UserView> GetNamedView(User user, string name, @@ -1645,12 +1650,7 @@ namespace MediaBrowser.Server.Implementations.Library string sortName, CancellationToken cancellationToken) { - if (ConfigurationManager.Configuration.EnableUserSpecificUserViews) - { - return GetNamedViewInternal(user, name, null, viewType, sortName, null, cancellationToken); - } - - return GetNamedView(name, viewType, sortName, cancellationToken); + return GetNamedViewInternal(user, name, null, viewType, sortName, null, cancellationToken); } public async Task<UserView> GetNamedView(string name, @@ -1671,7 +1671,7 @@ namespace MediaBrowser.Server.Implementations.Library if (item == null || !string.Equals(item.Path, path, StringComparison.OrdinalIgnoreCase)) { - Directory.CreateDirectory(path); + _fileSystem.CreateDirectory(path); item = new UserView { @@ -1702,7 +1702,7 @@ namespace MediaBrowser.Server.Implementations.Library if (refresh) { await item.UpdateToRepository(ItemUpdateType.MetadataImport, CancellationToken.None).ConfigureAwait(false); - _providerManagerFactory().QueueRefresh(item.Id, new MetadataRefreshOptions + _providerManagerFactory().QueueRefresh(item.Id, new MetadataRefreshOptions(_fileSystem) { // Not sure why this is necessary but need to figure it out // View images are not getting utilized without this @@ -1758,7 +1758,7 @@ namespace MediaBrowser.Server.Implementations.Library if (item == null) { - Directory.CreateDirectory(path); + _fileSystem.CreateDirectory(path); item = new UserView { @@ -1767,7 +1767,8 @@ namespace MediaBrowser.Server.Implementations.Library DateCreated = DateTime.UtcNow, Name = name, ViewType = viewType, - ForcedSortName = sortName + ForcedSortName = sortName, + UserId = user.Id }; if (!string.IsNullOrWhiteSpace(parentId)) @@ -1780,6 +1781,12 @@ namespace MediaBrowser.Server.Implementations.Library isNew = true; } + if (!item.UserId.HasValue) + { + item.UserId = user.Id; + await item.UpdateToRepository(ItemUpdateType.MetadataEdit, cancellationToken).ConfigureAwait(false); + } + if (!string.Equals(viewType, item.ViewType, StringComparison.OrdinalIgnoreCase)) { item.ViewType = viewType; @@ -1790,7 +1797,7 @@ namespace MediaBrowser.Server.Implementations.Library if (refresh) { - _providerManagerFactory().QueueRefresh(item.Id, new MetadataRefreshOptions + _providerManagerFactory().QueueRefresh(item.Id, new MetadataRefreshOptions(_fileSystem) { // Need to force save to increment DateLastSaved ForceSave = true @@ -1828,7 +1835,7 @@ namespace MediaBrowser.Server.Implementations.Library if (item == null) { - Directory.CreateDirectory(path); + _fileSystem.CreateDirectory(path); item = new UserView { @@ -1860,7 +1867,7 @@ namespace MediaBrowser.Server.Implementations.Library if (refresh) { - _providerManagerFactory().QueueRefresh(item.Id, new MetadataRefreshOptions + _providerManagerFactory().QueueRefresh(item.Id, new MetadataRefreshOptions(_fileSystem) { // Need to force save to increment DateLastSaved ForceSave = true @@ -2181,7 +2188,7 @@ namespace MediaBrowser.Server.Implementations.Library } } - return item.People ?? new List<PersonInfo>(); + return new List<PersonInfo>(); } public List<Person> GetPeopleItems(InternalPeopleQuery query) diff --git a/MediaBrowser.Server.Implementations/Library/MediaSourceManager.cs b/MediaBrowser.Server.Implementations/Library/MediaSourceManager.cs index 63067bf5a..b72da4d10 100644 --- a/MediaBrowser.Server.Implementations/Library/MediaSourceManager.cs +++ b/MediaBrowser.Server.Implementations/Library/MediaSourceManager.cs @@ -15,6 +15,7 @@ using System.IO; using System.Linq; using System.Threading; using System.Threading.Tasks; +using MediaBrowser.Common.IO; namespace MediaBrowser.Server.Implementations.Library { @@ -24,17 +25,19 @@ namespace MediaBrowser.Server.Implementations.Library private readonly IUserManager _userManager; private readonly ILibraryManager _libraryManager; private readonly IJsonSerializer _jsonSerializer; + private readonly IFileSystem _fileSystem; private IMediaSourceProvider[] _providers; private readonly ILogger _logger; - public MediaSourceManager(IItemRepository itemRepo, IUserManager userManager, ILibraryManager libraryManager, ILogger logger, IJsonSerializer jsonSerializer) + public MediaSourceManager(IItemRepository itemRepo, IUserManager userManager, ILibraryManager libraryManager, ILogger logger, IJsonSerializer jsonSerializer, IFileSystem fileSystem) { _itemRepo = itemRepo; _userManager = userManager; _libraryManager = libraryManager; _logger = logger; _jsonSerializer = jsonSerializer; + _fileSystem = fileSystem; } public void AddParts(IEnumerable<IMediaSourceProvider> providers) @@ -77,10 +80,6 @@ namespace MediaBrowser.Server.Implementations.Library { return false; } - if (string.Equals(stream.Codec, "ssa", StringComparison.OrdinalIgnoreCase)) - { - return false; - } return true; } @@ -170,7 +169,7 @@ namespace MediaBrowser.Server.Implementations.Library if (source.Protocol == MediaProtocol.File) { // TODO: Path substitution - if (!File.Exists(source.Path)) + if (!_fileSystem.FileExists(source.Path)) { source.SupportsDirectStream = false; } @@ -582,4 +581,4 @@ namespace MediaBrowser.Server.Implementations.Library public MediaSourceInfo MediaSource; } } -} +}
\ No newline at end of file diff --git a/MediaBrowser.Server.Implementations/Library/UserManager.cs b/MediaBrowser.Server.Implementations/Library/UserManager.cs index 5012f2479..a8fc7b4aa 100644 --- a/MediaBrowser.Server.Implementations/Library/UserManager.cs +++ b/MediaBrowser.Server.Implementations/Library/UserManager.cs @@ -454,7 +454,7 @@ namespace MediaBrowser.Server.Implementations.Library /// <returns>Task.</returns> public Task RefreshUsersMetadata(CancellationToken cancellationToken) { - var tasks = Users.Select(user => user.RefreshMetadata(new MetadataRefreshOptions(), cancellationToken)).ToList(); + var tasks = Users.Select(user => user.RefreshMetadata(new MetadataRefreshOptions(_fileSystem), cancellationToken)).ToList(); return Task.WhenAll(tasks); } @@ -745,7 +745,7 @@ namespace MediaBrowser.Server.Implementations.Library text.AppendLine(string.Empty); text.AppendLine("The pin code will expire at " + expiration.ToLocalTime().ToShortDateString() + " " + expiration.ToLocalTime().ToShortTimeString()); - File.WriteAllText(path, text.ToString(), Encoding.UTF8); + _fileSystem.WriteAllText(path, text.ToString(), Encoding.UTF8); var result = new PasswordPinCreationResult { @@ -919,7 +919,7 @@ namespace MediaBrowser.Server.Implementations.Library var path = GetPolifyFilePath(user); - Directory.CreateDirectory(Path.GetDirectoryName(path)); + _fileSystem.CreateDirectory(Path.GetDirectoryName(path)); lock (_policySyncLock) { @@ -1006,7 +1006,7 @@ namespace MediaBrowser.Server.Implementations.Library config = _jsonSerializer.DeserializeFromString<UserConfiguration>(json); } - Directory.CreateDirectory(Path.GetDirectoryName(path)); + _fileSystem.CreateDirectory(Path.GetDirectoryName(path)); lock (_configSyncLock) { diff --git a/MediaBrowser.Server.Implementations/Library/UserViewManager.cs b/MediaBrowser.Server.Implementations/Library/UserViewManager.cs index 43f77ec49..fe9e09318 100644 --- a/MediaBrowser.Server.Implementations/Library/UserViewManager.cs +++ b/MediaBrowser.Server.Implementations/Library/UserViewManager.cs @@ -65,20 +65,24 @@ namespace MediaBrowser.Server.Implementations.Library var list = new List<Folder>(); - if (_config.Configuration.EnableUserSpecificUserViews) + if (_config.Configuration.EnableUserViews) { foreach (var folder in standaloneFolders) { var collectionFolder = folder as ICollectionFolder; var folderViewType = collectionFolder == null ? null : collectionFolder.CollectionType; - if (plainFolderIds.Contains(folder.Id)) + if (UserView.IsUserSpecific(folder)) { - list.Add(await GetUserView(folder.Id, folder.Name, folderViewType, false, string.Empty, user, cancellationToken).ConfigureAwait(false)); + list.Add(await GetUserView(folder.Id, folder.Name, folderViewType, true, string.Empty, user, cancellationToken).ConfigureAwait(false)); + } + else if (plainFolderIds.Contains(folder.Id)) + { + list.Add(await GetUserView(folder.Id, folder.Name, folderViewType, false, string.Empty, cancellationToken).ConfigureAwait(false)); } else if (!string.IsNullOrWhiteSpace(folderViewType)) { - list.Add(await GetUserView(folder.Id, folder.Name, folderViewType, true, string.Empty, user, cancellationToken).ConfigureAwait(false)); + list.Add(await GetUserView(folder.Id, folder.Name, folderViewType, true, string.Empty, cancellationToken).ConfigureAwait(false)); } else { @@ -88,7 +92,29 @@ namespace MediaBrowser.Server.Implementations.Library } else { - list.AddRange(standaloneFolders); + // TODO: Deprecate this whole block + foreach (var folder in standaloneFolders) + { + var collectionFolder = folder as ICollectionFolder; + var folderViewType = collectionFolder == null ? null : collectionFolder.CollectionType; + + if (UserView.IsUserSpecific(folder)) + { + list.Add(await GetUserView(folder.Id, folder.Name, folderViewType, true, string.Empty, user, cancellationToken).ConfigureAwait(false)); + } + else if (plainFolderIds.Contains(folder.Id)) + { + list.Add(await GetUserView(folder.Id, folder.Name, folderViewType, false, string.Empty, user, cancellationToken).ConfigureAwait(false)); + } + else if (!string.IsNullOrWhiteSpace(folderViewType)) + { + list.Add(await GetUserView(folder.Id, folder.Name, folderViewType, true, string.Empty, user, cancellationToken).ConfigureAwait(false)); + } + else + { + list.Add(folder); + } + } } var parents = foldersWithViewTypes.Where(i => string.Equals(i.GetViewType(user), CollectionType.TvShows, StringComparison.OrdinalIgnoreCase) || string.IsNullOrWhiteSpace(i.GetViewType(user))) @@ -123,22 +149,6 @@ namespace MediaBrowser.Server.Implementations.Library list.Add(await GetUserView(parents, list, CollectionType.Games, string.Empty, user, cancellationToken).ConfigureAwait(false)); } - parents = foldersWithViewTypes.Where(i => string.Equals(i.GetViewType(user), CollectionType.BoxSets, StringComparison.OrdinalIgnoreCase)) - .ToList(); - - if (parents.Count > 0) - { - list.Add(await GetUserView(parents, list, CollectionType.BoxSets, string.Empty, user, cancellationToken).ConfigureAwait(false)); - } - - parents = foldersWithViewTypes.Where(i => string.Equals(i.GetViewType(user), CollectionType.Playlists, StringComparison.OrdinalIgnoreCase)) - .ToList(); - - if (parents.Count > 0) - { - list.Add(await GetUserView(parents, list, CollectionType.Playlists, string.Empty, user, cancellationToken).ConfigureAwait(false)); - } - if (user.Configuration.DisplayFoldersView) { var name = _localizationManager.GetLocalizedString("ViewType" + CollectionType.Folders); @@ -204,9 +214,9 @@ namespace MediaBrowser.Server.Implementations.Library public async Task<UserView> GetUserView(List<ICollectionFolder> parents, List<Folder> currentViews, string viewType, string sortName, User user, CancellationToken cancellationToken) { var name = _localizationManager.GetLocalizedString("ViewType" + viewType); - var enableUserSpecificViews = _config.Configuration.EnableUserSpecificUserViews; + var enableUserViews = _config.Configuration.EnableUserViews; - if (parents.Count == 1 && parents.All(i => string.Equals((enableUserSpecificViews ? i.CollectionType : i.GetViewType(user)), viewType, StringComparison.OrdinalIgnoreCase))) + if (parents.Count == 1 && parents.All(i => string.Equals((enableUserViews ? i.GetViewType(user) : i.CollectionType), viewType, StringComparison.OrdinalIgnoreCase))) { if (!string.IsNullOrWhiteSpace(parents[0].Name)) { @@ -222,7 +232,7 @@ namespace MediaBrowser.Server.Implementations.Library return await GetUserView(parentId, name, viewType, enableRichView, sortName, user, cancellationToken).ConfigureAwait(false); } - if (enableUserSpecificViews) + if (!enableUserViews) { var view = await _libraryManager.GetNamedView(user, name, viewType, sortName, cancellationToken).ConfigureAwait(false); @@ -244,6 +254,12 @@ namespace MediaBrowser.Server.Implementations.Library return _libraryManager.GetNamedView(user, name, parentId.ToString("N"), viewType, sortName, null, cancellationToken); } + public Task<UserView> GetUserView(Guid parentId, string name, string viewType, bool enableRichView, string sortName, CancellationToken cancellationToken) + { + viewType = enableRichView ? viewType : null; + return _libraryManager.GetNamedView(name, parentId.ToString("N"), viewType, sortName, null, cancellationToken); + } + public List<Tuple<BaseItem, List<BaseItem>>> GetLatestItems(LatestItemsQuery request) { var user = _userManager.GetUserById(request.UserId); diff --git a/MediaBrowser.Server.Implementations/Library/Validators/PeopleValidator.cs b/MediaBrowser.Server.Implementations/Library/Validators/PeopleValidator.cs index a4c43af5d..c6b294e83 100644 --- a/MediaBrowser.Server.Implementations/Library/Validators/PeopleValidator.cs +++ b/MediaBrowser.Server.Implementations/Library/Validators/PeopleValidator.cs @@ -11,6 +11,7 @@ using System.Collections.Generic; using System.Linq; using System.Threading; using System.Threading.Tasks; +using MediaBrowser.Common.IO; namespace MediaBrowser.Server.Implementations.Library.Validators { @@ -29,17 +30,19 @@ namespace MediaBrowser.Server.Implementations.Library.Validators private readonly ILogger _logger; private readonly IServerConfigurationManager _config; + private readonly IFileSystem _fileSystem; /// <summary> /// Initializes a new instance of the <see cref="PeopleValidator" /> class. /// </summary> /// <param name="libraryManager">The library manager.</param> /// <param name="logger">The logger.</param> - public PeopleValidator(ILibraryManager libraryManager, ILogger logger, IServerConfigurationManager config) + public PeopleValidator(ILibraryManager libraryManager, ILogger logger, IServerConfigurationManager config, IFileSystem fileSystem) { _libraryManager = libraryManager; _logger = logger; _config = config; + _fileSystem = fileSystem; } private bool DownloadMetadata(PersonInfo i, PeopleMetadataOptions options) @@ -121,7 +124,7 @@ namespace MediaBrowser.Server.Implementations.Library.Validators validIds.Add(item.Id); - var options = new MetadataRefreshOptions + var options = new MetadataRefreshOptions(_fileSystem) { MetadataRefreshMode = person.Value ? MetadataRefreshMode.Default : MetadataRefreshMode.ValidationOnly, ImageRefreshMode = person.Value ? ImageRefreshMode.Default : ImageRefreshMode.ValidationOnly diff --git a/MediaBrowser.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs b/MediaBrowser.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs index b89d8a8d1..2ff48cd85 100644 --- a/MediaBrowser.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs +++ b/MediaBrowser.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs @@ -8,7 +8,9 @@ using MediaBrowser.Controller.Drawing; using MediaBrowser.Controller.FileOrganization; using MediaBrowser.Controller.Library; using MediaBrowser.Controller.LiveTv; +using MediaBrowser.Controller.MediaEncoding; using MediaBrowser.Controller.Providers; +using MediaBrowser.Model.Dlna; using MediaBrowser.Model.Dto; using MediaBrowser.Model.Entities; using MediaBrowser.Model.Events; @@ -47,10 +49,11 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV private readonly ILibraryManager _libraryManager; private readonly IProviderManager _providerManager; private readonly IFileOrganizationService _organizationService; + private readonly IMediaEncoder _mediaEncoder; public static EmbyTV Current; - public EmbyTV(IApplicationHost appHost, ILogger logger, IJsonSerializer jsonSerializer, IHttpClient httpClient, IServerConfigurationManager config, ILiveTvManager liveTvManager, IFileSystem fileSystem, ISecurityManager security, ILibraryManager libraryManager, ILibraryMonitor libraryMonitor, IProviderManager providerManager, IFileOrganizationService organizationService) + public EmbyTV(IApplicationHost appHost, ILogger logger, IJsonSerializer jsonSerializer, IHttpClient httpClient, IServerConfigurationManager config, ILiveTvManager liveTvManager, IFileSystem fileSystem, ISecurityManager security, ILibraryManager libraryManager, ILibraryMonitor libraryMonitor, IProviderManager providerManager, IFileOrganizationService organizationService, IMediaEncoder mediaEncoder) { Current = this; @@ -64,12 +67,13 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV _libraryMonitor = libraryMonitor; _providerManager = providerManager; _organizationService = organizationService; + _mediaEncoder = mediaEncoder; _liveTvManager = (LiveTvManager)liveTvManager; _jsonSerializer = jsonSerializer; - _recordingProvider = new ItemDataProvider<RecordingInfo>(jsonSerializer, _logger, Path.Combine(DataPath, "recordings"), (r1, r2) => string.Equals(r1.Id, r2.Id, StringComparison.OrdinalIgnoreCase)); - _seriesTimerProvider = new SeriesTimerManager(jsonSerializer, _logger, Path.Combine(DataPath, "seriestimers")); - _timerProvider = new TimerManager(jsonSerializer, _logger, Path.Combine(DataPath, "timers")); + _recordingProvider = new ItemDataProvider<RecordingInfo>(fileSystem, jsonSerializer, _logger, Path.Combine(DataPath, "recordings"), (r1, r2) => string.Equals(r1.Id, r2.Id, StringComparison.OrdinalIgnoreCase)); + _seriesTimerProvider = new SeriesTimerManager(fileSystem, jsonSerializer, _logger, Path.Combine(DataPath, "seriestimers")); + _timerProvider = new TimerManager(fileSystem, jsonSerializer, _logger, Path.Combine(DataPath, "timers")); _timerProvider.TimerFired += _timerProvider_TimerFired; } @@ -126,6 +130,33 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV return status; } + public async Task RefreshSeriesTimers(CancellationToken cancellationToken, IProgress<double> progress) + { + var timers = await GetSeriesTimersAsync(cancellationToken).ConfigureAwait(false); + + List<ChannelInfo> channels = null; + + foreach (var timer in timers) + { + List<ProgramInfo> epgData; + + if (timer.RecordAnyChannel) + { + if (channels == null) + { + channels = (await GetChannelsAsync(true, CancellationToken.None).ConfigureAwait(false)).ToList(); + } + var channelIds = channels.Select(i => i.Id).ToList(); + epgData = GetEpgDataForChannels(channelIds); + } + else + { + epgData = GetEpgDataForChannel(timer.ChannelId); + } + await UpdateTimersForSeriesTimer(epgData, timer).ConfigureAwait(false); + } + } + private List<ChannelInfo> _channelCache = null; private async Task<IEnumerable<ChannelInfo>> GetChannelsAsync(bool enableCache, CancellationToken cancellationToken) { @@ -235,7 +266,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV try { - File.Delete(remove.Path); + _fileSystem.DeleteFile(remove.Path); } catch (DirectoryNotFoundException) { @@ -364,33 +395,6 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV return Task.FromResult((IEnumerable<SeriesTimerInfo>)_seriesTimerProvider.GetAll()); } - public async Task RefreshSeriesTimers(CancellationToken cancellationToken, IProgress<double> progress) - { - var timers = await GetSeriesTimersAsync(cancellationToken).ConfigureAwait(false); - - List<ChannelInfo> channels = null; - - foreach (var timer in timers) - { - List<ProgramInfo> epgData; - - if (timer.RecordAnyChannel) - { - if (channels == null) - { - channels = (await GetChannelsAsync(true, CancellationToken.None).ConfigureAwait(false)).ToList(); - } - var channelIds = channels.Select(i => i.Id).ToList(); - epgData = GetEpgDataForChannels(channelIds); - } - else - { - epgData = GetEpgDataForChannel(timer.ChannelId); - } - await UpdateTimersForSeriesTimer(epgData, timer).ConfigureAwait(false); - } - } - public async Task<IEnumerable<ProgramInfo>> GetProgramsAsync(string channelId, DateTime startDateUtc, DateTime endDateUtc, CancellationToken cancellationToken) { try @@ -469,6 +473,8 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV if (mediaSourceInfo != null) { + await AddMediaInfo(mediaSourceInfo, false, cancellationToken).ConfigureAwait(false); + mediaSourceInfo.Id = Guid.NewGuid().ToString("N"); return mediaSourceInfo; } @@ -499,6 +505,84 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV throw new NotImplementedException(); } + private async Task AddMediaInfo(MediaSourceInfo mediaSource, bool isAudio, CancellationToken cancellationToken) + { + var originalRuntime = mediaSource.RunTimeTicks; + + var info = await _mediaEncoder.GetMediaInfo(new MediaInfoRequest + { + InputPath = mediaSource.Path, + Protocol = mediaSource.Protocol, + MediaType = isAudio ? DlnaProfileType.Audio : DlnaProfileType.Video, + ExtractChapters = false + + }, cancellationToken).ConfigureAwait(false); + + mediaSource.Bitrate = info.Bitrate; + mediaSource.Container = info.Container; + mediaSource.Formats = info.Formats; + mediaSource.MediaStreams = info.MediaStreams; + mediaSource.RunTimeTicks = info.RunTimeTicks; + mediaSource.Size = info.Size; + mediaSource.Timestamp = info.Timestamp; + mediaSource.Video3DFormat = info.Video3DFormat; + mediaSource.VideoType = info.VideoType; + + mediaSource.DefaultSubtitleStreamIndex = null; + + // Null this out so that it will be treated like a live stream + if (!originalRuntime.HasValue) + { + mediaSource.RunTimeTicks = null; + } + + var audioStream = mediaSource.MediaStreams.FirstOrDefault(i => i.Type == Model.Entities.MediaStreamType.Audio); + + if (audioStream == null || audioStream.Index == -1) + { + mediaSource.DefaultAudioStreamIndex = null; + } + else + { + mediaSource.DefaultAudioStreamIndex = audioStream.Index; + } + + var videoStream = mediaSource.MediaStreams.FirstOrDefault(i => i.Type == Model.Entities.MediaStreamType.Video); + if (videoStream != null) + { + if (!videoStream.BitRate.HasValue) + { + var width = videoStream.Width ?? 1920; + + if (width >= 1900) + { + videoStream.BitRate = 8000000; + } + + else if (width >= 1260) + { + videoStream.BitRate = 3000000; + } + + else if (width >= 700) + { + videoStream.BitRate = 1000000; + } + } + } + + // Try to estimate this + if (!mediaSource.Bitrate.HasValue) + { + var total = mediaSource.MediaStreams.Select(i => i.BitRate ?? 0).Sum(); + + if (total > 0) + { + mediaSource.Bitrate = total; + } + } + } + public Task<List<MediaSourceInfo>> GetRecordingStreamMediaSources(string recordingId, CancellationToken cancellationToken) { throw new NotImplementedException(); @@ -527,11 +611,19 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV try { + var recordingEndDate = timer.EndDate.AddSeconds(timer.PostPaddingSeconds); + + if (recordingEndDate <= DateTime.UtcNow) + { + _logger.Warn("Recording timer fired for timer {0}, Id: {1}, but the program has already ended.", timer.Name, timer.Id); + return; + } + var cancellationTokenSource = new CancellationTokenSource(); if (_activeRecordings.TryAdd(timer.Id, cancellationTokenSource)) { - await RecordStream(timer, cancellationTokenSource.Token).ConfigureAwait(false); + await RecordStream(timer, recordingEndDate, cancellationTokenSource.Token).ConfigureAwait(false); } } catch (OperationCanceledException) @@ -544,49 +636,42 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV } } - private async Task RecordStream(TimerInfo timer, CancellationToken cancellationToken) + private async Task RecordStream(TimerInfo timer, DateTime recordingEndDate, CancellationToken cancellationToken) { if (timer == null) { throw new ArgumentNullException("timer"); } - var mediaStreamInfo = await GetChannelStream(timer.ChannelId, null, CancellationToken.None); - var duration = (timer.EndDate - DateTime.UtcNow).Add(TimeSpan.FromSeconds(timer.PostPaddingSeconds)); - - HttpRequestOptions httpRequestOptions = new HttpRequestOptions() - { - Url = mediaStreamInfo.Path - }; - var info = GetProgramInfoFromCache(timer.ChannelId, timer.ProgramId); + var recordPath = RecordingPath; if (info.IsMovie) { - recordPath = Path.Combine(recordPath, "Movies", _fileSystem.GetValidFilename(info.Name)); + recordPath = Path.Combine(recordPath, "Movies", _fileSystem.GetValidFilename(info.Name).Trim()); } else if (info.IsSeries) { - recordPath = Path.Combine(recordPath, "Series", _fileSystem.GetValidFilename(info.Name)); + recordPath = Path.Combine(recordPath, "Series", _fileSystem.GetValidFilename(info.Name).Trim()); } else if (info.IsKids) { - recordPath = Path.Combine(recordPath, "Kids", _fileSystem.GetValidFilename(info.Name)); + recordPath = Path.Combine(recordPath, "Kids", _fileSystem.GetValidFilename(info.Name).Trim()); } else if (info.IsSports) { - recordPath = Path.Combine(recordPath, "Sports", _fileSystem.GetValidFilename(info.Name)); + recordPath = Path.Combine(recordPath, "Sports", _fileSystem.GetValidFilename(info.Name).Trim()); } else { - recordPath = Path.Combine(recordPath, "Other", _fileSystem.GetValidFilename(info.Name)); + recordPath = Path.Combine(recordPath, "Other", _fileSystem.GetValidFilename(info.Name).Trim()); } - var recordingFileName = _fileSystem.GetValidFilename(RecordingHelper.GetRecordingName(timer, info)) + ".ts"; + var recordingFileName = _fileSystem.GetValidFilename(RecordingHelper.GetRecordingName(timer, info)).Trim() + ".ts"; recordPath = Path.Combine(recordPath, recordingFileName); - Directory.CreateDirectory(Path.GetDirectoryName(recordPath)); + _fileSystem.CreateDirectory(Path.GetDirectoryName(recordPath)); var recording = _recordingProvider.GetAll().FirstOrDefault(x => string.Equals(x.ProgramId, info.Id, StringComparison.OrdinalIgnoreCase)); @@ -624,15 +709,27 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV _recordingProvider.Add(recording); } - recording.Path = recordPath; - recording.Status = RecordingStatus.InProgress; - recording.DateLastUpdated = DateTime.UtcNow; - _recordingProvider.Update(recording); - - _logger.Info("Beginning recording."); - try { + var mediaStreamInfo = await GetChannelStream(timer.ChannelId, null, CancellationToken.None); + + // HDHR doesn't seem to release the tuner right away after first probing with ffmpeg + await Task.Delay(3000, cancellationToken).ConfigureAwait(false); + + var duration = recordingEndDate - DateTime.UtcNow; + + HttpRequestOptions httpRequestOptions = new HttpRequestOptions() + { + Url = mediaStreamInfo.Path + }; + + recording.Path = recordPath; + recording.Status = RecordingStatus.InProgress; + recording.DateLastUpdated = DateTime.UtcNow; + _recordingProvider.Update(recording); + + _logger.Info("Beginning recording."); + httpRequestOptions.BufferContent = false; var durationToken = new CancellationTokenSource(duration); var linkedToken = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken, durationToken.Token).Token; @@ -640,7 +737,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV _logger.Info("Writing file to path: " + recordPath); using (var response = await _httpClient.SendAsync(httpRequestOptions, "GET")) { - using (var output = File.Open(recordPath, FileMode.Create, FileAccess.Write, FileShare.Read)) + using (var output = _fileSystem.GetFileStream(recordPath, FileMode.Create, FileAccess.Write, FileShare.Read)) { await response.Content.CopyToAsync(output, StreamDefaults.DefaultCopyToBufferSize, linkedToken); } @@ -801,7 +898,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV private void SaveEpgDataForChannel(string channelId, List<ProgramInfo> epgData) { var path = GetChannelEpgCachePath(channelId); - Directory.CreateDirectory(Path.GetDirectoryName(path)); + _fileSystem.CreateDirectory(Path.GetDirectoryName(path)); lock (_epgLock) { _jsonSerializer.SerializeToFile(epgData, path); @@ -848,4 +945,4 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV }); } } -} +}
\ No newline at end of file diff --git a/MediaBrowser.Server.Implementations/LiveTv/EmbyTV/ItemDataProvider.cs b/MediaBrowser.Server.Implementations/LiveTv/EmbyTV/ItemDataProvider.cs index 75dec5f97..2c8917ab4 100644 --- a/MediaBrowser.Server.Implementations/LiveTv/EmbyTV/ItemDataProvider.cs +++ b/MediaBrowser.Server.Implementations/LiveTv/EmbyTV/ItemDataProvider.cs @@ -4,6 +4,7 @@ using System; using System.Collections.Generic; using System.IO; using System.Linq; +using MediaBrowser.Common.IO; namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV { @@ -16,13 +17,15 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV protected readonly ILogger Logger; private readonly string _dataPath; protected readonly Func<T, T, bool> EqualityComparer; + private readonly IFileSystem _fileSystem; - public ItemDataProvider(IJsonSerializer jsonSerializer, ILogger logger, string dataPath, Func<T, T, bool> equalityComparer) + public ItemDataProvider(IFileSystem fileSystem, IJsonSerializer jsonSerializer, ILogger logger, string dataPath, Func<T, T, bool> equalityComparer) { Logger = logger; _dataPath = dataPath; EqualityComparer = equalityComparer; _jsonSerializer = jsonSerializer; + _fileSystem = fileSystem; } public IReadOnlyList<T> GetAll() @@ -69,7 +72,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV private void UpdateList(List<T> newList) { var file = _dataPath + ".json"; - Directory.CreateDirectory(Path.GetDirectoryName(file)); + _fileSystem.CreateDirectory(Path.GetDirectoryName(file)); lock (_fileDataLock) { diff --git a/MediaBrowser.Server.Implementations/LiveTv/EmbyTV/RecordingHelper.cs b/MediaBrowser.Server.Implementations/LiveTv/EmbyTV/RecordingHelper.cs index 5b83d63b1..3ee808bb5 100644 --- a/MediaBrowser.Server.Implementations/LiveTv/EmbyTV/RecordingHelper.cs +++ b/MediaBrowser.Server.Implementations/LiveTv/EmbyTV/RecordingHelper.cs @@ -67,10 +67,14 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV } } - else if (info.ProductionYear != null) + else if (info.IsMovie && info.ProductionYear != null) { name += " (" + info.ProductionYear + ")"; } + else + { + name += " " + info.StartDate.ToString("yyyy-MM-dd"); + } return name; } diff --git a/MediaBrowser.Server.Implementations/LiveTv/EmbyTV/SeriesTimerManager.cs b/MediaBrowser.Server.Implementations/LiveTv/EmbyTV/SeriesTimerManager.cs index eab278eb4..563658885 100644 --- a/MediaBrowser.Server.Implementations/LiveTv/EmbyTV/SeriesTimerManager.cs +++ b/MediaBrowser.Server.Implementations/LiveTv/EmbyTV/SeriesTimerManager.cs @@ -2,13 +2,14 @@ using MediaBrowser.Model.Logging; using MediaBrowser.Model.Serialization; using System; +using MediaBrowser.Common.IO; namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV { public class SeriesTimerManager : ItemDataProvider<SeriesTimerInfo> { - public SeriesTimerManager(IJsonSerializer jsonSerializer, ILogger logger, string dataPath) - : base(jsonSerializer, logger, dataPath, (r1, r2) => string.Equals(r1.Id, r2.Id, StringComparison.OrdinalIgnoreCase)) + public SeriesTimerManager(IFileSystem fileSystem, IJsonSerializer jsonSerializer, ILogger logger, string dataPath) + : base(fileSystem, jsonSerializer, logger, dataPath, (r1, r2) => string.Equals(r1.Id, r2.Id, StringComparison.OrdinalIgnoreCase)) { } diff --git a/MediaBrowser.Server.Implementations/LiveTv/EmbyTV/TimerManager.cs b/MediaBrowser.Server.Implementations/LiveTv/EmbyTV/TimerManager.cs index 3ae38f382..64a5b7339 100644 --- a/MediaBrowser.Server.Implementations/LiveTv/EmbyTV/TimerManager.cs +++ b/MediaBrowser.Server.Implementations/LiveTv/EmbyTV/TimerManager.cs @@ -7,6 +7,7 @@ using System; using System.Collections.Concurrent; using System.Linq; using System.Threading; +using MediaBrowser.Common.IO; namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV { @@ -16,8 +17,8 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV public event EventHandler<GenericEventArgs<TimerInfo>> TimerFired; - public TimerManager(IJsonSerializer jsonSerializer, ILogger logger, string dataPath) - : base(jsonSerializer, logger, dataPath, (r1, r2) => string.Equals(r1.Id, r2.Id, StringComparison.OrdinalIgnoreCase)) + public TimerManager(IFileSystem fileSystem, IJsonSerializer jsonSerializer, ILogger logger, string dataPath) + : base(fileSystem, jsonSerializer, logger, dataPath, (r1, r2) => string.Equals(r1.Id, r2.Id, StringComparison.OrdinalIgnoreCase)) { } diff --git a/MediaBrowser.Server.Implementations/LiveTv/Listings/SchedulesDirect.cs b/MediaBrowser.Server.Implementations/LiveTv/Listings/SchedulesDirect.cs index 2efa91137..161bcbf48 100644 --- a/MediaBrowser.Server.Implementations/LiveTv/Listings/SchedulesDirect.cs +++ b/MediaBrowser.Server.Implementations/LiveTv/Listings/SchedulesDirect.cs @@ -362,8 +362,13 @@ namespace MediaBrowser.Server.Implementations.LiveTv.Listings private DateTime GetDate(string value) { - return DateTime.ParseExact(value, "yyyy'-'MM'-'dd'T'HH':'mm':'ss'Z'", - CultureInfo.InvariantCulture); + var date = DateTime.ParseExact(value, "yyyy'-'MM'-'dd'T'HH':'mm':'ss'Z'", CultureInfo.InvariantCulture); + + if (date.Kind != DateTimeKind.Utc) + { + date = DateTime.SpecifyKind(date, DateTimeKind.Utc); + } + return date; } private string GetProgramLogo(string apiUrl, ScheduleDirect.ShowImages images) diff --git a/MediaBrowser.Server.Implementations/LiveTv/LiveTvManager.cs b/MediaBrowser.Server.Implementations/LiveTv/LiveTvManager.cs index 7a26bf87c..8eb70a638 100644 --- a/MediaBrowser.Server.Implementations/LiveTv/LiveTvManager.cs +++ b/MediaBrowser.Server.Implementations/LiveTv/LiveTvManager.cs @@ -580,7 +580,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv item.Name = channelInfo.Name; } - await item.RefreshMetadata(new MetadataRefreshOptions + await item.RefreshMetadata(new MetadataRefreshOptions(_fileSystem) { ForceSave = isNew, ReplaceImages = replaceImages.Distinct().ToList() @@ -633,7 +633,6 @@ namespace MediaBrowser.Server.Implementations.LiveTv item.Name = info.Name; item.OfficialRating = item.OfficialRating ?? info.OfficialRating; item.Overview = item.Overview ?? info.Overview; - item.OriginalAirDate = info.OriginalAirDate; item.ProviderImagePath = info.ImagePath; item.ProviderImageUrl = info.ImageUrl; item.RunTimeTicks = (info.EndDate - info.StartDate).Ticks; @@ -641,7 +640,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv item.HomePageUrl = info.HomePageUrl; item.ProductionYear = info.ProductionYear; - item.PremiereDate = item.PremiereDate ?? info.OriginalAirDate; + item.PremiereDate = info.OriginalAirDate; item.IndexNumber = info.EpisodeNumber; item.ParentIndexNumber = info.SeasonNumber; @@ -650,16 +649,23 @@ namespace MediaBrowser.Server.Implementations.LiveTv { await _libraryManager.CreateItem(item, cancellationToken).ConfigureAwait(false); } + else if (string.IsNullOrWhiteSpace(info.Etag)) + { + await _libraryManager.UpdateItem(item, ItemUpdateType.MetadataImport, cancellationToken).ConfigureAwait(false); + } else { - if (string.IsNullOrWhiteSpace(info.Etag) || !string.Equals(info.Etag, item.Etag, StringComparison.OrdinalIgnoreCase)) + // Increment this whenver some internal change deems it necessary + var etag = info.Etag + "2"; + + if (!string.Equals(etag, item.Etag, StringComparison.OrdinalIgnoreCase)) { - item.Etag = info.Etag; + item.Etag = etag; await _libraryManager.UpdateItem(item, ItemUpdateType.MetadataImport, cancellationToken).ConfigureAwait(false); } } - _providerManager.QueueRefresh(item.Id, new MetadataRefreshOptions()); + _providerManager.QueueRefresh(item.Id, new MetadataRefreshOptions(_fileSystem)); return item; } @@ -705,6 +711,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv item.Overview = info.Overview; item.EndDate = info.EndDate; item.Genres = info.Genres; + item.PremiereDate = info.OriginalAirDate; var recording = (ILiveTvRecording)item; @@ -726,7 +733,6 @@ namespace MediaBrowser.Server.Implementations.LiveTv recording.IsRepeat = info.IsRepeat; recording.IsSeries = info.IsSeries; recording.IsSports = info.IsSports; - recording.OriginalAirDate = info.OriginalAirDate; recording.SeriesTimerId = info.SeriesTimerId; recording.StartDate = info.StartDate; recording.Status = info.Status; @@ -759,7 +765,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv await _libraryManager.UpdateItem(item, ItemUpdateType.MetadataImport, cancellationToken).ConfigureAwait(false); } - _providerManager.QueueRefresh(item.Id, new MetadataRefreshOptions()); + _providerManager.QueueRefresh(item.Id, new MetadataRefreshOptions(_fileSystem)); return item.Id; } @@ -1163,6 +1169,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv foreach (var program in channelPrograms) { var programItem = await GetProgram(program, channelId, currentChannel.ChannelType, service.Name, cancellationToken).ConfigureAwait(false); + programs.Add(programItem.Id); } } @@ -1430,8 +1437,6 @@ namespace MediaBrowser.Server.Implementations.LiveTv dto.IsPremiere = program.IsPremiere; } - dto.OriginalAirDate = program.OriginalAirDate; - if (channel != null) { dto.ChannelName = channel.Name; @@ -1471,7 +1476,6 @@ namespace MediaBrowser.Server.Implementations.LiveTv dto.IsNews = info.IsNews; dto.IsKids = info.IsKids; dto.IsPremiere = info.IsPremiere; - dto.OriginalAirDate = info.OriginalAirDate; dto.CanDelete = user == null ? recording.CanDelete() @@ -2352,4 +2356,4 @@ namespace MediaBrowser.Server.Implementations.LiveTv }); } } -} +}
\ No newline at end of file diff --git a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/BaseTunerHost.cs b/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/BaseTunerHost.cs index 909e2bba5..5afe8f54a 100644 --- a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/BaseTunerHost.cs +++ b/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/BaseTunerHost.cs @@ -9,6 +9,7 @@ using System.Collections.Generic; using System.Linq; using System.Threading; using System.Threading.Tasks; +using MediaBrowser.Model.Serialization; namespace MediaBrowser.Server.Implementations.LiveTv.TunerHosts { @@ -16,14 +17,16 @@ namespace MediaBrowser.Server.Implementations.LiveTv.TunerHosts { protected readonly IConfigurationManager Config; protected readonly ILogger Logger; + protected IJsonSerializer JsonSerializer; private readonly ConcurrentDictionary<string, ChannelCache> _channelCache = new ConcurrentDictionary<string, ChannelCache>(StringComparer.OrdinalIgnoreCase); - public BaseTunerHost(IConfigurationManager config, ILogger logger) + public BaseTunerHost(IConfigurationManager config, ILogger logger, IJsonSerializer jsonSerializer) { Config = config; Logger = logger; + JsonSerializer = jsonSerializer; } protected abstract Task<IEnumerable<ChannelInfo>> GetChannelsInternal(TunerHostInfo tuner, CancellationToken cancellationToken); @@ -44,6 +47,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv.TunerHosts var result = await GetChannelsInternal(tuner, cancellationToken).ConfigureAwait(false); var list = result.ToList(); + Logger.Debug("Channels from {0}: {1}", tuner.Url, JsonSerializer.SerializeToString(list)); if (!string.IsNullOrWhiteSpace(key)) { @@ -109,23 +113,22 @@ namespace MediaBrowser.Server.Implementations.LiveTv.TunerHosts foreach (var host in hostsWithChannel) { - // Check to make sure the tuner is available - // If there's only one tuner, don't bother with the check and just let the tuner be the one to throw an error - if (hostsWithChannel.Count > 1 && !await IsAvailable(host, channelId, cancellationToken).ConfigureAwait(false)) + try { - Logger.Error("Tuner is not currently available"); - continue; - } + var mediaSources = await GetChannelStreamMediaSources(host, channelId, cancellationToken).ConfigureAwait(false); - var mediaSources = await GetChannelStreamMediaSources(host, channelId, cancellationToken).ConfigureAwait(false); + // Prefix the id with the host Id so that we can easily find it + foreach (var mediaSource in mediaSources) + { + mediaSource.Id = host.Id + mediaSource.Id; + } - // Prefix the id with the host Id so that we can easily find it - foreach (var mediaSource in mediaSources) + return mediaSources; + } + catch (Exception ex) { - mediaSource.Id = host.Id + mediaSource.Id; + Logger.Error("Error opening tuner", ex); } - - return mediaSources; } } @@ -163,23 +166,18 @@ namespace MediaBrowser.Server.Implementations.LiveTv.TunerHosts foreach (var host in hostsWithChannel) { - // Check to make sure the tuner is available - // If there's only one tuner, don't bother with the check and just let the tuner be the one to throw an error - // If a streamId is specified then availibility has already been checked in GetChannelStreamMediaSources - if (string.IsNullOrWhiteSpace(streamId) && hostsWithChannel.Count > 1) + try { - if (!await IsAvailable(host, channelId, cancellationToken).ConfigureAwait(false)) + var stream = await GetChannelStream(host, channelId, streamId, cancellationToken).ConfigureAwait(false); + + if (stream != null) { - Logger.Error("Tuner is not currently available"); - continue; + return stream; } } - - var stream = await GetChannelStream(host, channelId, streamId, cancellationToken).ConfigureAwait(false); - - if (stream != null) + catch (Exception ex) { - return stream; + Logger.Error("Error opening tuner", ex); } } } @@ -187,21 +185,6 @@ namespace MediaBrowser.Server.Implementations.LiveTv.TunerHosts throw new LiveTvConflictException(); } - protected async Task<bool> IsAvailable(TunerHostInfo tuner, string channelId, CancellationToken cancellationToken) - { - try - { - return await IsAvailableInternal(tuner, channelId, cancellationToken).ConfigureAwait(false); - } - catch (Exception ex) - { - Logger.ErrorException("Error checking tuner availability", ex); - return false; - } - } - - protected abstract Task<bool> IsAvailableInternal(TunerHostInfo tuner, string channelId, CancellationToken cancellationToken); - protected abstract bool IsValidChannelId(string channelId); protected LiveTvOptions GetConfiguration() diff --git a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs b/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs index bccb0db0a..9cd706b53 100644 --- a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs +++ b/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs @@ -20,13 +20,11 @@ namespace MediaBrowser.Server.Implementations.LiveTv.TunerHosts.HdHomerun public class HdHomerunHost : BaseTunerHost, ITunerHost { private readonly IHttpClient _httpClient; - private readonly IJsonSerializer _jsonSerializer; public HdHomerunHost(IConfigurationManager config, ILogger logger, IHttpClient httpClient, IJsonSerializer jsonSerializer) - : base(config, logger) + : base(config, logger, jsonSerializer) { _httpClient = httpClient; - _jsonSerializer = jsonSerializer; } public string Name @@ -55,7 +53,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv.TunerHosts.HdHomerun }; using (var stream = await _httpClient.Get(options)) { - var root = _jsonSerializer.DeserializeFromStream<List<Channels>>(stream); + var root = JsonSerializer.DeserializeFromStream<List<Channels>>(stream); if (root != null) { @@ -325,11 +323,14 @@ namespace MediaBrowser.Server.Implementations.LiveTv.TunerHosts.HdHomerun BitRate = 128000 } }, - RequiresOpening = false, - RequiresClosing = false, + RequiresOpening = true, + RequiresClosing = true, BufferMs = 1000, Container = "ts", - Id = profile + Id = profile, + SupportsDirectPlay = true, + SupportsDirectStream = true, + SupportsTranscoding = true }; return mediaSource; @@ -377,7 +378,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv.TunerHosts.HdHomerun protected override async Task<MediaSourceInfo> GetChannelStream(TunerHostInfo info, string channelId, string streamId, CancellationToken cancellationToken) { - Logger.Debug("GetChannelStream: channel id: {0}. stream id: {1}", channelId, streamId ?? string.Empty); + Logger.Info("GetChannelStream: channel id: {0}. stream id: {1}", channelId, streamId ?? string.Empty); if (!channelId.StartsWith(ChannelIdPrefix, StringComparison.OrdinalIgnoreCase)) { @@ -395,12 +396,5 @@ namespace MediaBrowser.Server.Implementations.LiveTv.TunerHosts.HdHomerun await GetChannels(info, false, CancellationToken.None).ConfigureAwait(false); } } - - protected override async Task<bool> IsAvailableInternal(TunerHostInfo tuner, string channelId, CancellationToken cancellationToken) - { - var info = await GetTunerInfos(tuner, cancellationToken).ConfigureAwait(false); - - return info.Any(i => i.Status == LiveTvTunerStatus.Available || string.Equals(i.ChannelId, channelId, StringComparison.OrdinalIgnoreCase)); - } } } diff --git a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/M3UTunerHost.cs b/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/M3UTunerHost.cs index 3783e4b08..a26ed7a2a 100644 --- a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/M3UTunerHost.cs +++ b/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/M3UTunerHost.cs @@ -12,14 +12,22 @@ using System.IO; using System.Linq; using System.Threading; using System.Threading.Tasks; +using MediaBrowser.Common.IO; +using MediaBrowser.Common.Net; +using MediaBrowser.Model.Serialization; namespace MediaBrowser.Server.Implementations.LiveTv.TunerHosts { public class M3UTunerHost : BaseTunerHost, ITunerHost { - public M3UTunerHost(IConfigurationManager config, ILogger logger) - : base(config, logger) + private readonly IFileSystem _fileSystem; + private IHttpClient _httpClient; + + public M3UTunerHost(IConfigurationManager config, ILogger logger, IFileSystem fileSystem, IHttpClient httpClient, IJsonSerializer jsonSerializer) + : base(config, logger, jsonSerializer) { + _fileSystem = fileSystem; + _httpClient = httpClient; } public override string Type @@ -41,47 +49,48 @@ namespace MediaBrowser.Server.Implementations.LiveTv.TunerHosts string line; // Read the file and display it line by line. - var file = new StreamReader(url); - var channels = new List<M3UChannel>(); + using (var file = new StreamReader(await GetListingsStream(info, cancellationToken).ConfigureAwait(false))) + { + var channels = new List<M3UChannel>(); - string channnelName = null; - string channelNumber = null; + string channnelName = null; + string channelNumber = null; - while ((line = file.ReadLine()) != null) - { - line = line.Trim(); - if (string.IsNullOrWhiteSpace(line)) + while ((line = file.ReadLine()) != null) { - continue; - } + line = line.Trim(); + if (string.IsNullOrWhiteSpace(line)) + { + continue; + } - if (line.StartsWith("#EXTM3U", StringComparison.OrdinalIgnoreCase)) - { - continue; - } + if (line.StartsWith("#EXTM3U", StringComparison.OrdinalIgnoreCase)) + { + continue; + } - if (line.StartsWith("#EXTINF:", StringComparison.OrdinalIgnoreCase)) - { - var parts = line.Split(new[] { ':' }, 2).Last().Split(new[] { ',' }, 2); - channelNumber = parts[0]; - channnelName = parts[1]; - } - else if (!string.IsNullOrWhiteSpace(channelNumber)) - { - channels.Add(new M3UChannel + if (line.StartsWith("#EXTINF:", StringComparison.OrdinalIgnoreCase)) { - Name = channnelName, - Number = channelNumber, - Id = ChannelIdPrefix + urlHash + channelNumber, - Path = line - }); - - channelNumber = null; - channnelName = null; + var parts = line.Split(new[] { ':' }, 2).Last().Split(new[] { ',' }, 2); + channelNumber = parts[0]; + channnelName = parts[1]; + } + else if (!string.IsNullOrWhiteSpace(channelNumber)) + { + channels.Add(new M3UChannel + { + Name = channnelName, + Number = channelNumber, + Id = ChannelIdPrefix + urlHash + channelNumber, + Path = line + }); + + channelNumber = null; + channnelName = null; + } } + return channels; } - file.Close(); - return channels; } public Task<List<LiveTvTunerInfo>> GetTunerInfos(CancellationToken cancellationToken) @@ -119,9 +128,9 @@ namespace MediaBrowser.Server.Implementations.LiveTv.TunerHosts public async Task Validate(TunerHostInfo info) { - if (!File.Exists(info.Url)) + using (var stream = await GetListingsStream(info, CancellationToken.None).ConfigureAwait(false)) { - throw new FileNotFoundException(); + } } @@ -130,6 +139,15 @@ namespace MediaBrowser.Server.Implementations.LiveTv.TunerHosts return channelId.StartsWith(ChannelIdPrefix, StringComparison.OrdinalIgnoreCase); } + private Task<Stream> GetListingsStream(TunerHostInfo info, CancellationToken cancellationToken) + { + if (info.Url.StartsWith("http", StringComparison.OrdinalIgnoreCase)) + { + return _httpClient.Get(info.Url, cancellationToken); + } + return Task.FromResult(_fileSystem.OpenRead(info.Url)); + } + protected override async Task<List<MediaSourceInfo>> GetChannelStreamMediaSources(TunerHostInfo info, string channelId, CancellationToken cancellationToken) { var urlHash = info.Url.GetMD5().ToString("N"); @@ -190,10 +208,5 @@ namespace MediaBrowser.Server.Implementations.LiveTv.TunerHosts } return new List<MediaSourceInfo> { }; } - - protected override Task<bool> IsAvailableInternal(TunerHostInfo tuner, string channelId, CancellationToken cancellationToken) - { - return Task.FromResult(true); - } } } diff --git a/MediaBrowser.Server.Implementations/Localization/Core/ar.json b/MediaBrowser.Server.Implementations/Localization/Core/ar.json index 3d15d7b2e..fd47027c2 100644 --- a/MediaBrowser.Server.Implementations/Localization/Core/ar.json +++ b/MediaBrowser.Server.Implementations/Localization/Core/ar.json @@ -172,5 +172,6 @@ "HeaderProducer": "Producers", "HeaderWriter": "Writers", "HeaderParentalRatings": "Parental Ratings", - "HeaderCommunityRatings": "Community ratings" + "HeaderCommunityRatings": "Community ratings", + "StartupEmbyServerIsLoading": "Emby Server is loading. Please try again shortly." }
\ No newline at end of file diff --git a/MediaBrowser.Server.Implementations/Localization/Core/bg-BG.json b/MediaBrowser.Server.Implementations/Localization/Core/bg-BG.json index 8854611a0..aeebd2038 100644 --- a/MediaBrowser.Server.Implementations/Localization/Core/bg-BG.json +++ b/MediaBrowser.Server.Implementations/Localization/Core/bg-BG.json @@ -17,7 +17,7 @@ "ValueSpecialEpisodeName": "Special - {0}", "LabelChapterName": "Chapter {0}", "NameSeasonNumber": "Season {0}", - "LabelExit": "\u0418\u0437\u043b\u0435\u0437", + "LabelExit": "\u0418\u0437\u0445\u043e\u0434", "LabelVisitCommunity": "\u041f\u043e\u0441\u0435\u0442\u0438 \u043e\u0431\u0449\u0435\u0441\u0442\u0432\u043e\u0442\u043e", "LabelGithub": "Github", "LabelApiDocumentation": "API \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u0430\u0446\u0438\u044f", @@ -25,7 +25,7 @@ "LabelBrowseLibrary": "\u0420\u0430\u0437\u0433\u043b\u0435\u0434\u0430\u0439 \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0430\u0442\u0430", "LabelConfigureServer": "\u041a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0438\u0440\u0430\u0439 Emby", "LabelRestartServer": "\u0420\u0435\u0441\u0442\u0430\u0440\u0442\u0438\u0440\u0430\u0439 \u0441\u044a\u0440\u0432\u044a\u0440\u0430", - "CategorySync": "Sync", + "CategorySync": "\u0421\u0438\u043d\u0445\u0440\u043e\u043d\u0438\u0437.", "CategoryUser": "User", "CategorySystem": "System", "CategoryApplication": "Application", @@ -172,5 +172,6 @@ "HeaderProducer": "Producers", "HeaderWriter": "Writers", "HeaderParentalRatings": "Parental Ratings", - "HeaderCommunityRatings": "Community ratings" + "HeaderCommunityRatings": "Community ratings", + "StartupEmbyServerIsLoading": "Emby Server is loading. Please try again shortly." }
\ No newline at end of file diff --git a/MediaBrowser.Server.Implementations/Localization/Core/ca.json b/MediaBrowser.Server.Implementations/Localization/Core/ca.json index 35f2bafb8..46004f52c 100644 --- a/MediaBrowser.Server.Implementations/Localization/Core/ca.json +++ b/MediaBrowser.Server.Implementations/Localization/Core/ca.json @@ -172,5 +172,6 @@ "HeaderProducer": "Producers", "HeaderWriter": "Writers", "HeaderParentalRatings": "Parental Ratings", - "HeaderCommunityRatings": "Community ratings" + "HeaderCommunityRatings": "Community ratings", + "StartupEmbyServerIsLoading": "Emby Server is loading. Please try again shortly." }
\ No newline at end of file diff --git a/MediaBrowser.Server.Implementations/Localization/Core/core.json b/MediaBrowser.Server.Implementations/Localization/Core/core.json index 4eb66929d..5f11b9436 100644 --- a/MediaBrowser.Server.Implementations/Localization/Core/core.json +++ b/MediaBrowser.Server.Implementations/Localization/Core/core.json @@ -173,5 +173,6 @@ "HeaderProducer": "Producers", "HeaderWriter": "Writers", "HeaderParentalRatings": "Parental Ratings", - "HeaderCommunityRatings": "Community ratings" + "HeaderCommunityRatings": "Community ratings", + "StartupEmbyServerIsLoading": "Emby Server is loading. Please try again shortly." } diff --git a/MediaBrowser.Server.Implementations/Localization/Core/cs.json b/MediaBrowser.Server.Implementations/Localization/Core/cs.json index ee30a6028..771e735e9 100644 --- a/MediaBrowser.Server.Implementations/Localization/Core/cs.json +++ b/MediaBrowser.Server.Implementations/Localization/Core/cs.json @@ -172,5 +172,6 @@ "HeaderProducer": "Producers", "HeaderWriter": "Writers", "HeaderParentalRatings": "Parental Ratings", - "HeaderCommunityRatings": "Community ratings" + "HeaderCommunityRatings": "Community ratings", + "StartupEmbyServerIsLoading": "Emby Server is loading. Please try again shortly." }
\ No newline at end of file diff --git a/MediaBrowser.Server.Implementations/Localization/Core/da.json b/MediaBrowser.Server.Implementations/Localization/Core/da.json index 21d7e9e14..e6e4d9835 100644 --- a/MediaBrowser.Server.Implementations/Localization/Core/da.json +++ b/MediaBrowser.Server.Implementations/Localization/Core/da.json @@ -172,5 +172,6 @@ "HeaderProducer": "Producers", "HeaderWriter": "Writers", "HeaderParentalRatings": "Aldersgr\u00e6nser", - "HeaderCommunityRatings": "Community ratings" + "HeaderCommunityRatings": "Community ratings", + "StartupEmbyServerIsLoading": "Emby Server is loading. Please try again shortly." }
\ No newline at end of file diff --git a/MediaBrowser.Server.Implementations/Localization/Core/de.json b/MediaBrowser.Server.Implementations/Localization/Core/de.json index 277a224ce..0464b65ac 100644 --- a/MediaBrowser.Server.Implementations/Localization/Core/de.json +++ b/MediaBrowser.Server.Implementations/Localization/Core/de.json @@ -172,5 +172,6 @@ "HeaderProducer": "Produzenten", "HeaderWriter": "Autoren", "HeaderParentalRatings": "Altersbeschr\u00e4nkung", - "HeaderCommunityRatings": "Community Bewertungen" + "HeaderCommunityRatings": "Community Bewertungen", + "StartupEmbyServerIsLoading": "Emby Server startet, bitte versuchen Sie es gleich noch einmal." }
\ No newline at end of file diff --git a/MediaBrowser.Server.Implementations/Localization/Core/el.json b/MediaBrowser.Server.Implementations/Localization/Core/el.json index 213868042..7e94f7987 100644 --- a/MediaBrowser.Server.Implementations/Localization/Core/el.json +++ b/MediaBrowser.Server.Implementations/Localization/Core/el.json @@ -172,5 +172,6 @@ "HeaderProducer": "Producers", "HeaderWriter": "Writers", "HeaderParentalRatings": "Parental Ratings", - "HeaderCommunityRatings": "Community ratings" + "HeaderCommunityRatings": "Community ratings", + "StartupEmbyServerIsLoading": "Emby Server is loading. Please try again shortly." }
\ No newline at end of file diff --git a/MediaBrowser.Server.Implementations/Localization/Core/en-GB.json b/MediaBrowser.Server.Implementations/Localization/Core/en-GB.json index 566d2cf09..73534b08d 100644 --- a/MediaBrowser.Server.Implementations/Localization/Core/en-GB.json +++ b/MediaBrowser.Server.Implementations/Localization/Core/en-GB.json @@ -172,5 +172,6 @@ "HeaderProducer": "Producers", "HeaderWriter": "Writers", "HeaderParentalRatings": "Parental Ratings", - "HeaderCommunityRatings": "Community ratings" + "HeaderCommunityRatings": "Community ratings", + "StartupEmbyServerIsLoading": "Emby Server is loading. Please try again shortly." }
\ No newline at end of file diff --git a/MediaBrowser.Server.Implementations/Localization/Core/en-US.json b/MediaBrowser.Server.Implementations/Localization/Core/en-US.json index abe203804..e444c0e93 100644 --- a/MediaBrowser.Server.Implementations/Localization/Core/en-US.json +++ b/MediaBrowser.Server.Implementations/Localization/Core/en-US.json @@ -172,5 +172,6 @@ "HeaderProducer": "Producers", "HeaderWriter": "Writers", "HeaderParentalRatings": "Parental Ratings", - "HeaderCommunityRatings": "Community ratings" + "HeaderCommunityRatings": "Community ratings", + "StartupEmbyServerIsLoading": "Emby Server is loading. Please try again shortly." }
\ No newline at end of file diff --git a/MediaBrowser.Server.Implementations/Localization/Core/es-AR.json b/MediaBrowser.Server.Implementations/Localization/Core/es-AR.json index 9c1ea6cb9..8068e32a9 100644 --- a/MediaBrowser.Server.Implementations/Localization/Core/es-AR.json +++ b/MediaBrowser.Server.Implementations/Localization/Core/es-AR.json @@ -172,5 +172,6 @@ "HeaderProducer": "Producers", "HeaderWriter": "Writers", "HeaderParentalRatings": "Parental Ratings", - "HeaderCommunityRatings": "Community ratings" + "HeaderCommunityRatings": "Community ratings", + "StartupEmbyServerIsLoading": "Emby Server is loading. Please try again shortly." }
\ No newline at end of file diff --git a/MediaBrowser.Server.Implementations/Localization/Core/es-MX.json b/MediaBrowser.Server.Implementations/Localization/Core/es-MX.json index 9e1030dbd..972f94100 100644 --- a/MediaBrowser.Server.Implementations/Localization/Core/es-MX.json +++ b/MediaBrowser.Server.Implementations/Localization/Core/es-MX.json @@ -172,5 +172,6 @@ "HeaderProducer": "Productores", "HeaderWriter": "Guionistas", "HeaderParentalRatings": "Clasificaci\u00f3n Parental", - "HeaderCommunityRatings": "Clasificaciones de la comunidad" + "HeaderCommunityRatings": "Clasificaciones de la comunidad", + "StartupEmbyServerIsLoading": "El servidor Emby esta cargando. Por favor intente de nuevo dentro de poco." }
\ No newline at end of file diff --git a/MediaBrowser.Server.Implementations/Localization/Core/es.json b/MediaBrowser.Server.Implementations/Localization/Core/es.json index f93fd7219..9bf56e4af 100644 --- a/MediaBrowser.Server.Implementations/Localization/Core/es.json +++ b/MediaBrowser.Server.Implementations/Localization/Core/es.json @@ -172,5 +172,6 @@ "HeaderProducer": "Producers", "HeaderWriter": "Writers", "HeaderParentalRatings": "Parental Ratings", - "HeaderCommunityRatings": "Community ratings" + "HeaderCommunityRatings": "Community ratings", + "StartupEmbyServerIsLoading": "Emby Server is loading. Please try again shortly." }
\ No newline at end of file diff --git a/MediaBrowser.Server.Implementations/Localization/Core/fi.json b/MediaBrowser.Server.Implementations/Localization/Core/fi.json index 5df95146c..f0ab5b0d0 100644 --- a/MediaBrowser.Server.Implementations/Localization/Core/fi.json +++ b/MediaBrowser.Server.Implementations/Localization/Core/fi.json @@ -172,5 +172,6 @@ "HeaderProducer": "Producers", "HeaderWriter": "Writers", "HeaderParentalRatings": "Parental Ratings", - "HeaderCommunityRatings": "Community ratings" + "HeaderCommunityRatings": "Community ratings", + "StartupEmbyServerIsLoading": "Emby Server is loading. Please try again shortly." }
\ No newline at end of file diff --git a/MediaBrowser.Server.Implementations/Localization/Core/fr.json b/MediaBrowser.Server.Implementations/Localization/Core/fr.json index fe76ac64e..b32d4cb0a 100644 --- a/MediaBrowser.Server.Implementations/Localization/Core/fr.json +++ b/MediaBrowser.Server.Implementations/Localization/Core/fr.json @@ -172,5 +172,6 @@ "HeaderProducer": "Producteurs", "HeaderWriter": "Auteur(e)s", "HeaderParentalRatings": "Note parentale", - "HeaderCommunityRatings": "Classification de la communaut\u00e9" + "HeaderCommunityRatings": "Classification de la communaut\u00e9", + "StartupEmbyServerIsLoading": "Le serveur Emby est en cours de chargement. Veuillez r\u00e9essayer dans quelques instant." }
\ No newline at end of file diff --git a/MediaBrowser.Server.Implementations/Localization/Core/gsw.json b/MediaBrowser.Server.Implementations/Localization/Core/gsw.json index 7334b24ce..736c3d18f 100644 --- a/MediaBrowser.Server.Implementations/Localization/Core/gsw.json +++ b/MediaBrowser.Server.Implementations/Localization/Core/gsw.json @@ -172,5 +172,6 @@ "HeaderProducer": "Producers", "HeaderWriter": "Writers", "HeaderParentalRatings": "Parental Ratings", - "HeaderCommunityRatings": "Community ratings" + "HeaderCommunityRatings": "Community ratings", + "StartupEmbyServerIsLoading": "Emby Server is loading. Please try again shortly." }
\ No newline at end of file diff --git a/MediaBrowser.Server.Implementations/Localization/Core/he.json b/MediaBrowser.Server.Implementations/Localization/Core/he.json index b3bec3f02..a66438482 100644 --- a/MediaBrowser.Server.Implementations/Localization/Core/he.json +++ b/MediaBrowser.Server.Implementations/Localization/Core/he.json @@ -172,5 +172,6 @@ "HeaderProducer": "Producers", "HeaderWriter": "Writers", "HeaderParentalRatings": "Parental Ratings", - "HeaderCommunityRatings": "Community ratings" + "HeaderCommunityRatings": "Community ratings", + "StartupEmbyServerIsLoading": "Emby Server is loading. Please try again shortly." }
\ No newline at end of file diff --git a/MediaBrowser.Server.Implementations/Localization/Core/hr.json b/MediaBrowser.Server.Implementations/Localization/Core/hr.json index 451ff4f36..2b9d56566 100644 --- a/MediaBrowser.Server.Implementations/Localization/Core/hr.json +++ b/MediaBrowser.Server.Implementations/Localization/Core/hr.json @@ -172,5 +172,6 @@ "HeaderProducer": "Producers", "HeaderWriter": "Writers", "HeaderParentalRatings": "Parental Ratings", - "HeaderCommunityRatings": "Community ratings" + "HeaderCommunityRatings": "Community ratings", + "StartupEmbyServerIsLoading": "Emby Server is loading. Please try again shortly." }
\ No newline at end of file diff --git a/MediaBrowser.Server.Implementations/Localization/Core/it.json b/MediaBrowser.Server.Implementations/Localization/Core/it.json index 90f800634..197b9e2bb 100644 --- a/MediaBrowser.Server.Implementations/Localization/Core/it.json +++ b/MediaBrowser.Server.Implementations/Localization/Core/it.json @@ -20,8 +20,8 @@ "LabelExit": "Esci", "LabelVisitCommunity": "Visita la Community", "LabelGithub": "Github", - "LabelApiDocumentation": "Documentazione sulle Api", - "LabelDeveloperResources": "Risorse per i programmatori", + "LabelApiDocumentation": "Documentazione Api", + "LabelDeveloperResources": "Risorse programmatori", "LabelBrowseLibrary": "Esplora la libreria", "LabelConfigureServer": "Configura Emby", "LabelRestartServer": "Riavvia Server", @@ -120,36 +120,36 @@ "UserStartedPlayingItemWithValues": "{0} \u00e8 partito da {1}", "UserStoppedPlayingItemWithValues": "{0} stoppato {1}", "SubtitleDownloadFailureForItem": "Sottotitoli non scaricati per {0}", - "HeaderUnidentified": "Unidentified", - "HeaderImagePrimary": "Primary", - "HeaderImageBackdrop": "Backdrop", + "HeaderUnidentified": "Non identificata", + "HeaderImagePrimary": "Primaria", + "HeaderImageBackdrop": "Sfondo", "HeaderImageLogo": "Logo", - "HeaderUserPrimaryImage": "User Image", - "HeaderOverview": "Overview", - "HeaderShortOverview": "Short Overview", - "HeaderType": "Type", - "HeaderSeverity": "Severity", + "HeaderUserPrimaryImage": "Immagine utente", + "HeaderOverview": "Panoramica", + "HeaderShortOverview": "breve panoramica", + "HeaderType": "Tipo", + "HeaderSeverity": "gravit\u00e0", "HeaderUser": "Utente", "HeaderName": "Nome", "HeaderDate": "Data", "HeaderPremiereDate": "Premiere Date", - "HeaderDateAdded": "Date Added", + "HeaderDateAdded": "Aggiunto il", "HeaderReleaseDate": "Data Rilascio", "HeaderRuntime": "Durata", - "HeaderPlayCount": "Play Count", + "HeaderPlayCount": "Visto N\u00b0", "HeaderSeason": "Stagione", "HeaderSeasonNumber": "Stagione Numero", - "HeaderSeries": "Series:", + "HeaderSeries": "Serie:", "HeaderNetwork": "Rete", - "HeaderYear": "Year:", - "HeaderYears": "Years:", - "HeaderParentalRating": "Parental Rating", + "HeaderYear": "Anno:", + "HeaderYears": "Anni", + "HeaderParentalRating": "Valutazione parentale", "HeaderCommunityRating": "Voto Comunit\u00e0", "HeaderTrailers": "Trailers", "HeaderSpecials": "Speciali", - "HeaderGameSystems": "Game Systems", - "HeaderPlayers": "Players:", - "HeaderAlbumArtists": "Album Artists", + "HeaderGameSystems": "Sistemi di gioco", + "HeaderPlayers": "Giocatori", + "HeaderAlbumArtists": "Album Artisti", "HeaderAlbums": "Album", "HeaderDisc": "Disco", "HeaderTrack": "Traccia", @@ -163,14 +163,15 @@ "HeaderStatus": "Stato", "HeaderTracks": "Traccia", "HeaderMusicArtist": "Music artist", - "HeaderLocked": "Locked", + "HeaderLocked": "Bloccato", "HeaderStudios": "Studios", - "HeaderActor": "Actors", - "HeaderComposer": "Composers", - "HeaderDirector": "Directors", - "HeaderGuestStar": "Guest star", - "HeaderProducer": "Producers", - "HeaderWriter": "Writers", + "HeaderActor": "Attori", + "HeaderComposer": "Compositori", + "HeaderDirector": "Registi", + "HeaderGuestStar": "Personaggi famosi", + "HeaderProducer": "Produttori", + "HeaderWriter": "Sceneggiatori", "HeaderParentalRatings": "Valutazioni genitori", - "HeaderCommunityRatings": "Community ratings" + "HeaderCommunityRatings": "Community ratings", + "StartupEmbyServerIsLoading": "Emby Server is loading. Please try again shortly." }
\ No newline at end of file diff --git a/MediaBrowser.Server.Implementations/Localization/Core/kk.json b/MediaBrowser.Server.Implementations/Localization/Core/kk.json index 7e17dea6b..863523606 100644 --- a/MediaBrowser.Server.Implementations/Localization/Core/kk.json +++ b/MediaBrowser.Server.Implementations/Localization/Core/kk.json @@ -104,7 +104,7 @@ "DeviceOnlineWithName": "{0} \u049b\u043e\u0441\u044b\u043b\u0493\u0430\u043d", "UserOnlineFromDevice": "{0} - {1} \u0430\u0440\u049b\u044b\u043b\u044b \u049b\u043e\u0441\u044b\u043b\u0493\u0430\u043d", "ProviderValue": "\u0416\u0435\u0442\u043a\u0456\u0437\u0443\u0448\u0456: {0}", - "SubtitlesDownloadedForItem": "\u0421\u0443\u0431\u0442\u0438\u0442\u0440\u043b\u0435\u0440 {0} \u04af\u0448\u0456\u043d \u0436\u04af\u043a\u0442\u0435\u043f \u0430\u043b\u044b\u043d\u0434\u044b", + "SubtitlesDownloadedForItem": "\u0421\u0443\u0431\u0442\u0438\u0442\u0440\u043b\u0435\u0440 {0} \u04af\u0448\u0456\u043d \u0436\u04af\u043a\u0442\u0435\u043b\u0456\u043f \u0430\u043b\u044b\u043d\u0434\u044b", "UserConfigurationUpdatedWithName": "\u041f\u0430\u0439\u0434\u0430\u043b\u0430\u043d\u0443\u0448\u044b {0} \u04af\u0448\u0456\u043d \u0442\u0435\u04a3\u0448\u0435\u043b\u0456\u043c \u0436\u0430\u04a3\u0430\u0440\u0442\u044b\u043b\u0434\u044b", "UserCreatedWithName": "\u041f\u0430\u0439\u0434\u0430\u043b\u0430\u043d\u0443\u0448\u044b {0} \u0436\u0430\u0441\u0430\u043b\u0493\u0430\u043d", "UserPasswordChangedWithName": "\u041f\u0430\u0439\u0434\u0430\u043b\u0430\u043d\u0443\u0448\u044b {0} \u04af\u0448\u0456\u043d \u049b\u04b1\u043f\u0438\u044f \u0441\u04e9\u0437 \u04e9\u0437\u0433\u0435\u0440\u0442\u0456\u043b\u0434\u0456", @@ -117,9 +117,9 @@ "DeviceOfflineWithName": "{0} \u0430\u0436\u044b\u0440\u0430\u0442\u044b\u043b\u0493\u0430\u043d", "UserLockedOutWithName": "\u041f\u0430\u0439\u0434\u0430\u043b\u0430\u043d\u0443\u0448\u044b {0} \u049b\u04b1\u0440\u0441\u0430\u0443\u043b\u044b", "UserOfflineFromDevice": "{0} - {1} \u0430\u0440\u049b\u044b\u043b\u044b \u0430\u0436\u044b\u0440\u0430\u0442\u044b\u043b\u0493\u0430\u043d", - "UserStartedPlayingItemWithValues": "{0} - {1} \u043e\u0439\u043d\u0430\u0442\u0443\u044b \u0431\u0430\u0441\u0442\u0430\u043b\u0434\u044b", - "UserStoppedPlayingItemWithValues": "{0} - {1} \u043e\u0439\u043d\u0430\u0442\u0443\u044b \u0442\u043e\u049b\u0442\u0430\u043b\u0434\u044b", - "SubtitleDownloadFailureForItem": "\u0421\u0443\u0431\u0442\u0438\u0442\u0440\u043b\u0435\u0440 {0} \u04af\u0448\u0456\u043d \u0436\u04af\u043a\u0442\u0435\u043f \u0430\u043b\u044b\u043d\u0443\u044b \u0441\u04d9\u0442\u0441\u0456\u0437", + "UserStartedPlayingItemWithValues": "{0} - {1} \u043e\u0439\u043d\u0430\u0442\u0443\u044b\u043d \u0431\u0430\u0441\u0442\u0430\u0434\u044b", + "UserStoppedPlayingItemWithValues": "{0} - {1} \u043e\u0439\u043d\u0430\u0442\u0443\u044b\u043d \u0442\u043e\u049b\u0442\u0430\u0442\u0442\u044b", + "SubtitleDownloadFailureForItem": "\u0421\u0443\u0431\u0442\u0438\u0442\u0440\u043b\u0435\u0440 {0} \u04af\u0448\u0456\u043d \u0436\u04af\u043a\u0442\u0435\u043b\u0456\u043f \u0430\u043b\u044b\u043d\u0443\u044b \u0441\u04d9\u0442\u0441\u0456\u0437", "HeaderUnidentified": "\u0410\u043d\u044b\u049b\u0442\u0430\u043b\u043c\u0430\u0493\u0430\u043d", "HeaderImagePrimary": "\u041d\u0435\u0433\u0456\u0437\u0433\u0456", "HeaderImageBackdrop": "\u0410\u0440\u0442\u049b\u044b \u0441\u0443\u0440\u0435\u0442", @@ -172,5 +172,6 @@ "HeaderProducer": "\u041f\u0440\u043e\u0434\u044e\u0441\u0435\u0440\u043b\u0435\u0440", "HeaderWriter": "\u0421\u0446\u0435\u043d\u0430\u0440\u0438\u0439\u0448\u0456\u043b\u0435\u0440", "HeaderParentalRatings": "\u0416\u0430\u0441\u0442\u0430\u0441 \u0441\u0430\u043d\u0430\u0442\u0442\u0430\u0440", - "HeaderCommunityRatings": "\u049a\u0430\u0443\u044b\u043c \u0431\u0430\u0493\u0430\u043b\u0430\u0443\u043b\u0430\u0440\u044b" + "HeaderCommunityRatings": "\u049a\u0430\u0443\u044b\u043c \u0431\u0430\u0493\u0430\u043b\u0430\u0443\u043b\u0430\u0440\u044b", + "StartupEmbyServerIsLoading": "Emby Server \u0436\u04af\u043a\u0442\u0435\u043b\u0443\u0434\u0435. \u04d8\u0440\u0435\u043a\u0435\u0442\u0442\u0456 \u043a\u04e9\u043f \u04b1\u0437\u0430\u043c\u0430\u0439 \u049b\u0430\u0439\u0442\u0430\u043b\u0430\u04a3\u044b\u0437." }
\ No newline at end of file diff --git a/MediaBrowser.Server.Implementations/Localization/Core/ko.json b/MediaBrowser.Server.Implementations/Localization/Core/ko.json index 8bc5b5672..6044efb62 100644 --- a/MediaBrowser.Server.Implementations/Localization/Core/ko.json +++ b/MediaBrowser.Server.Implementations/Localization/Core/ko.json @@ -172,5 +172,6 @@ "HeaderProducer": "Producers", "HeaderWriter": "Writers", "HeaderParentalRatings": "Parental Ratings", - "HeaderCommunityRatings": "Community ratings" + "HeaderCommunityRatings": "Community ratings", + "StartupEmbyServerIsLoading": "Emby Server is loading. Please try again shortly." }
\ No newline at end of file diff --git a/MediaBrowser.Server.Implementations/Localization/Core/ms.json b/MediaBrowser.Server.Implementations/Localization/Core/ms.json index a85f00132..6b5c4393f 100644 --- a/MediaBrowser.Server.Implementations/Localization/Core/ms.json +++ b/MediaBrowser.Server.Implementations/Localization/Core/ms.json @@ -172,5 +172,6 @@ "HeaderProducer": "Producers", "HeaderWriter": "Writers", "HeaderParentalRatings": "Parental Ratings", - "HeaderCommunityRatings": "Community ratings" + "HeaderCommunityRatings": "Community ratings", + "StartupEmbyServerIsLoading": "Emby Server is loading. Please try again shortly." }
\ No newline at end of file diff --git a/MediaBrowser.Server.Implementations/Localization/Core/nb.json b/MediaBrowser.Server.Implementations/Localization/Core/nb.json index 2ab601701..f4ebe89b6 100644 --- a/MediaBrowser.Server.Implementations/Localization/Core/nb.json +++ b/MediaBrowser.Server.Implementations/Localization/Core/nb.json @@ -172,5 +172,6 @@ "HeaderProducer": "Producers", "HeaderWriter": "Writers", "HeaderParentalRatings": "Foreldresensur", - "HeaderCommunityRatings": "Community ratings" + "HeaderCommunityRatings": "Community ratings", + "StartupEmbyServerIsLoading": "Emby Server is loading. Please try again shortly." }
\ No newline at end of file diff --git a/MediaBrowser.Server.Implementations/Localization/Core/nl.json b/MediaBrowser.Server.Implementations/Localization/Core/nl.json index 8658518a4..b32ebefc3 100644 --- a/MediaBrowser.Server.Implementations/Localization/Core/nl.json +++ b/MediaBrowser.Server.Implementations/Localization/Core/nl.json @@ -157,7 +157,7 @@ "HeaderVideo": "Video", "HeaderEmbeddedImage": "Ingesloten afbeelding", "HeaderResolution": "Resolutie", - "HeaderSubtitles": "Ondertitels", + "HeaderSubtitles": "Ondertiteling", "HeaderGenres": "Genres", "HeaderCountries": "Landen", "HeaderStatus": "Status", @@ -172,5 +172,6 @@ "HeaderProducer": "Producenten", "HeaderWriter": "Schrijvers", "HeaderParentalRatings": "Ouderlijke toezicht", - "HeaderCommunityRatings": "Gemeenschapswaardering" + "HeaderCommunityRatings": "Gemeenschapswaardering", + "StartupEmbyServerIsLoading": "Emby Server is aan het laden, probeer het later opnieuw." }
\ No newline at end of file diff --git a/MediaBrowser.Server.Implementations/Localization/Core/pl.json b/MediaBrowser.Server.Implementations/Localization/Core/pl.json index b7b0e228e..13dca8889 100644 --- a/MediaBrowser.Server.Implementations/Localization/Core/pl.json +++ b/MediaBrowser.Server.Implementations/Localization/Core/pl.json @@ -172,5 +172,6 @@ "HeaderProducer": "Producers", "HeaderWriter": "Writers", "HeaderParentalRatings": "Parental Ratings", - "HeaderCommunityRatings": "Community ratings" + "HeaderCommunityRatings": "Community ratings", + "StartupEmbyServerIsLoading": "Emby Server is loading. Please try again shortly." }
\ No newline at end of file diff --git a/MediaBrowser.Server.Implementations/Localization/Core/pt-BR.json b/MediaBrowser.Server.Implementations/Localization/Core/pt-BR.json index e5e8afaa5..9548262c9 100644 --- a/MediaBrowser.Server.Implementations/Localization/Core/pt-BR.json +++ b/MediaBrowser.Server.Implementations/Localization/Core/pt-BR.json @@ -172,5 +172,6 @@ "HeaderProducer": "Produtores", "HeaderWriter": "Escritores", "HeaderParentalRatings": "Classifica\u00e7\u00f5es Parentais", - "HeaderCommunityRatings": "Avalia\u00e7\u00f5es da comunidade" + "HeaderCommunityRatings": "Avalia\u00e7\u00f5es da comunidade", + "StartupEmbyServerIsLoading": "O Servidor Emby est\u00e1 carregando. Por favor, tente novamente em breve." }
\ No newline at end of file diff --git a/MediaBrowser.Server.Implementations/Localization/Core/pt-PT.json b/MediaBrowser.Server.Implementations/Localization/Core/pt-PT.json index 5aa832e16..c3d514c8a 100644 --- a/MediaBrowser.Server.Implementations/Localization/Core/pt-PT.json +++ b/MediaBrowser.Server.Implementations/Localization/Core/pt-PT.json @@ -46,7 +46,7 @@ "NotificationOptionInstallationFailed": "Falha na instala\u00e7\u00e3o", "NotificationOptionNewLibraryContent": "Adicionado novo conte\u00fado", "NotificationOptionNewLibraryContentMultiple": "Novo conte\u00fado adicionado (m\u00faltiplo)", - "NotificationOptionCameraImageUploaded": "Camera image uploaded", + "NotificationOptionCameraImageUploaded": "Imagem da c\u00e2mara carregada", "NotificationOptionUserLockedOut": "User locked out", "NotificationOptionServerRestartRequired": "\u00c9 necess\u00e1rio reiniciar o servidor", "ViewTypePlaylists": "Playlists", @@ -172,5 +172,6 @@ "HeaderProducer": "Producers", "HeaderWriter": "Writers", "HeaderParentalRatings": "Parental Ratings", - "HeaderCommunityRatings": "Community ratings" + "HeaderCommunityRatings": "Community ratings", + "StartupEmbyServerIsLoading": "Emby Server is loading. Please try again shortly." }
\ No newline at end of file diff --git a/MediaBrowser.Server.Implementations/Localization/Core/ro.json b/MediaBrowser.Server.Implementations/Localization/Core/ro.json index 6c945006a..a6aa9577b 100644 --- a/MediaBrowser.Server.Implementations/Localization/Core/ro.json +++ b/MediaBrowser.Server.Implementations/Localization/Core/ro.json @@ -172,5 +172,6 @@ "HeaderProducer": "Producers", "HeaderWriter": "Writers", "HeaderParentalRatings": "Parental Ratings", - "HeaderCommunityRatings": "Community ratings" + "HeaderCommunityRatings": "Community ratings", + "StartupEmbyServerIsLoading": "Emby Server is loading. Please try again shortly." }
\ No newline at end of file diff --git a/MediaBrowser.Server.Implementations/Localization/Core/ru.json b/MediaBrowser.Server.Implementations/Localization/Core/ru.json index c705ddef4..fa1cbfc16 100644 --- a/MediaBrowser.Server.Implementations/Localization/Core/ru.json +++ b/MediaBrowser.Server.Implementations/Localization/Core/ru.json @@ -46,10 +46,10 @@ "NotificationOptionInstallationFailed": "\u0421\u0431\u043e\u0439 \u0443\u0441\u0442\u0430\u043d\u043e\u0432\u043a\u0438", "NotificationOptionNewLibraryContent": "\u041d\u043e\u0432\u043e\u0435 \u0441\u043e\u0434\u0435\u0440\u0436\u0430\u043d\u0438\u0435 \u0434\u043e\u0431\u0430\u0432\u043b\u0435\u043d\u043e", "NotificationOptionNewLibraryContentMultiple": "\u041d\u043e\u0432\u043e\u0435 \u0441\u043e\u0434\u0435\u0440\u0436\u0430\u043d\u0438\u0435 \u0434\u043e\u0431\u0430\u0432\u043b\u0435\u043d\u043e (\u043c\u043d\u043e\u0433\u043e\u043a\u0440\u0430\u0442\u043d\u043e)", - "NotificationOptionCameraImageUploaded": "\u0424\u043e\u0442\u043e\u0433\u0440\u0430\u0444\u0438\u044f \u0441 \u043a\u0430\u043c\u0435\u0440\u044b \u0432\u044b\u043b\u043e\u0436\u0435\u043d\u0430", + "NotificationOptionCameraImageUploaded": "\u041f\u0440\u043e\u0438\u0437\u0432\u0435\u0434\u0435\u043d\u0430 \u0432\u044b\u043a\u043b\u0430\u0434\u043a\u0430 \u043e\u0442\u0441\u043d\u044f\u0442\u043e\u0433\u043e \u0441 \u043a\u0430\u043c\u0435\u0440\u044b", "NotificationOptionUserLockedOut": "\u041f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044c \u0437\u0430\u0431\u043b\u043e\u043a\u0438\u0440\u043e\u0432\u0430\u043d", "NotificationOptionServerRestartRequired": "\u0422\u0440\u0435\u0431\u0443\u0435\u0442\u0441\u044f \u043f\u0435\u0440\u0435\u0437\u0430\u043f\u0443\u0441\u043a \u0441\u0435\u0440\u0432\u0435\u0440\u0430", - "ViewTypePlaylists": "\u0421\u043f\u0438\u0441\u043a\u0438 \u0432\u043e\u0441\u043f\u0440\u043e\u0438\u0437\u0432\u0435\u0434\u0435\u043d\u0438\u044f", + "ViewTypePlaylists": "\u041f\u043b\u0435\u0439-\u043b\u0438\u0441\u0442\u044b", "ViewTypeMovies": "\u041a\u0438\u043d\u043e", "ViewTypeTvShows": "\u0422\u0412", "ViewTypeGames": "\u0418\u0433\u0440\u044b", @@ -79,9 +79,9 @@ "ViewTypeMovieFavorites": "\u0418\u0437\u0431\u0440\u0430\u043d\u043d\u043e\u0435", "ViewTypeMovieGenres": "\u0416\u0430\u043d\u0440\u044b", "ViewTypeMusicLatest": "\u041f\u043e\u0441\u043b\u0435\u0434\u043d\u0435\u0435", - "ViewTypeMusicPlaylists": "\u0421\u043f\u0438\u0441\u043a\u0438 \u0432\u043e\u0441\u043f\u0440\u043e\u0438\u0437\u0432\u0435\u0434\u0435\u043d\u0438\u044f", + "ViewTypeMusicPlaylists": "\u041f\u043b\u0435\u0439-\u043b\u0438\u0441\u0442\u044b", "ViewTypeMusicAlbums": "\u0410\u043b\u044c\u0431\u043e\u043c\u044b", - "ViewTypeMusicAlbumArtists": "\u0410\u043b\u044c\u0431\u043e\u043c\u043d\u044b\u0435 \u0438\u0441\u043f\u043e\u043b\u043d\u0438\u0442\u0435\u043b\u0438", + "ViewTypeMusicAlbumArtists": "\u0418\u0441\u043f\u043e\u043b\u043d\u0438\u0442\u0435\u043b\u0438 \u0430\u043b\u044c\u0431\u043e\u043c\u0430", "HeaderOtherDisplaySettings": "\u041f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u044b \u043e\u0442\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u044f", "ViewTypeMusicSongs": "\u041a\u043e\u043c\u043f\u043e\u0437\u0438\u0446\u0438\u0438", "ViewTypeMusicFavorites": "\u0418\u0437\u0431\u0440\u0430\u043d\u043d\u043e\u0435", @@ -101,8 +101,8 @@ "ItemAddedWithName": "{0} (\u0434\u043e\u0431\u0430\u0432\u043b\u0435\u043d\u043e \u0432 \u043c\u0435\u0434\u0438\u0430\u0442\u0435\u043a\u0443)", "ItemRemovedWithName": "{0} (\u0438\u0437\u044a\u044f\u0442\u043e \u0438\u0437 \u043c\u0435\u0434\u0438\u0430\u0442\u0435\u043a\u0438)", "LabelIpAddressValue": "IP-\u0430\u0434\u0440\u0435\u0441: {0}", - "DeviceOnlineWithName": "{0} - \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u0435 \u0443\u0441\u0442\u0430\u043d\u043e\u0432\u043b\u0435\u043d\u043e", - "UserOnlineFromDevice": "{0} - \u043f\u043e\u0434\u043a\u043b. \u0441 {1} \u0443\u0441\u0442\u0430\u043d\u043e\u0432\u043b\u0435\u043d\u043e", + "DeviceOnlineWithName": "{0} - \u043f\u043e\u0434\u043a\u043b. \u0443\u0441\u0442-\u043d\u043e", + "UserOnlineFromDevice": "{0} - \u043f\u043e\u0434\u043a\u043b. \u0441 {1} \u0443\u0441\u0442-\u043d\u043e", "ProviderValue": "\u041f\u043e\u0441\u0442\u0430\u0432\u0449\u0438\u043a: {0}", "SubtitlesDownloadedForItem": "\u0421\u0443\u0431\u0442\u0438\u0442\u0440\u044b \u0434\u043b\u044f {0} \u0437\u0430\u0433\u0440\u0443\u0437\u0438\u043b\u0438\u0441\u044c", "UserConfigurationUpdatedWithName": "\u041a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u044f \u043f\u043e\u043b\u044c\u0437-\u043b\u044f {0} \u0431\u044b\u043b\u0430 \u043e\u0431\u043d\u043e\u0432\u043b\u0435\u043d\u0430", @@ -117,11 +117,11 @@ "DeviceOfflineWithName": "{0} - \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u0435 \u043f\u0440\u0435\u0440\u0432\u0430\u043d\u043e", "UserLockedOutWithName": "\u041f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044c {0} \u0431\u044b\u043b \u0437\u0430\u0431\u043b\u043e\u043a\u0438\u0440\u043e\u0432\u0430\u043d", "UserOfflineFromDevice": "{0} - \u043f\u043e\u0434\u043a\u043b. \u0441 {1} \u043f\u0440\u0435\u0440\u0432\u0430\u043d\u043e", - "UserStartedPlayingItemWithValues": "{0} - \u0432\u043e\u0441\u043f\u0440-\u0438\u0435 \u00ab{1}\u00bb \u0437\u0430\u043f-\u043d\u043e", - "UserStoppedPlayingItemWithValues": "{0} - \u0432\u043e\u0441\u043f\u0440-\u0438\u0435 \u00ab{1}\u00bb \u043e\u0441\u0442-\u043d\u043e", + "UserStartedPlayingItemWithValues": "{0} - \u0432\u043e\u0441\u043f\u0440. \u00ab{1}\u00bb \u0437\u0430\u043f-\u043d\u043e", + "UserStoppedPlayingItemWithValues": "{0} - \u0432\u043e\u0441\u043f\u0440. \u00ab{1}\u00bb \u043e\u0441\u0442-\u043d\u043e", "SubtitleDownloadFailureForItem": "\u0421\u0443\u0431\u0442\u0438\u0442\u0440\u044b \u043a {0} \u043d\u0435 \u0443\u0434\u0430\u043b\u043e\u0441\u044c \u0437\u0430\u0433\u0440\u0443\u0437\u0438\u0442\u044c", "HeaderUnidentified": "\u041d\u0435 \u0440\u0430\u0441\u043f\u043e\u0437\u043d\u0430\u043d\u043e", - "HeaderImagePrimary": "\u041e\u0441\u043d\u043e\u0432\u043d\u043e\u0439", + "HeaderImagePrimary": "\u0413\u043e\u043b\u043e\u0432\u043d\u043e\u0439", "HeaderImageBackdrop": "\u0417\u0430\u0434\u043d\u0438\u043a", "HeaderImageLogo": "\u041b\u043e\u0433\u043e\u0442\u0438\u043f", "HeaderUserPrimaryImage": "\u0420\u0438\u0441\u0443\u043d\u043e\u043a \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f", @@ -133,23 +133,23 @@ "HeaderName": "\u0418\u043c\u044f (\u043d\u0430\u0437\u0432\u0430\u043d\u0438\u0435)", "HeaderDate": "\u0414\u0430\u0442\u0430", "HeaderPremiereDate": "\u0414\u0430\u0442\u0430 \u043f\u0440\u0435\u043c\u044c\u0435\u0440\u044b", - "HeaderDateAdded": "\u0414\u0430\u0442\u0430 \u0434\u043e\u0431-\u0438\u044f", + "HeaderDateAdded": "\u0414\u0430\u0442\u0430 \u0434\u043e\u0431.", "HeaderReleaseDate": "\u0414\u0430\u0442\u0430 \u0432\u044b\u043f.", "HeaderRuntime": "\u0414\u043b\u0438\u0442.", - "HeaderPlayCount": "\u0427\u0438\u0441\u043b\u043e \u0432\u043e\u0441\u043f\u0440-\u0438\u0439", + "HeaderPlayCount": "\u041a\u043e\u043b-\u0432\u043e \u0432\u043e\u0441\u043f\u0440.", "HeaderSeason": "\u0421\u0435\u0437\u043e\u043d", - "HeaderSeasonNumber": "\u041d\u043e\u043c\u0435\u0440 \u0441\u0435\u0437\u043e\u043d\u0430", + "HeaderSeasonNumber": "\u2116 \u0441\u0435\u0437\u043e\u043d\u0430", "HeaderSeries": "\u0421\u0435\u0440\u0438\u0430\u043b:", "HeaderNetwork": "\u0422\u0435\u043b\u0435\u0441\u0435\u0442\u044c", "HeaderYear": "\u0413\u043e\u0434:", "HeaderYears": "\u0413\u043e\u0434\u044b:", - "HeaderParentalRating": "\u0412\u043e\u0437\u0440. \u043a\u0430\u0442-\u0438\u044f", + "HeaderParentalRating": "\u0412\u043e\u0437\u0440. \u043a\u0430\u0442.", "HeaderCommunityRating": "\u041e\u0431\u0449. \u043e\u0446\u0435\u043d\u043a\u0430", "HeaderTrailers": "\u0422\u0440\u0435\u0439\u043b.", "HeaderSpecials": "\u0421\u043f\u0435\u0446.", "HeaderGameSystems": "\u0418\u0433\u0440. \u0441\u0438\u0441\u0442\u0435\u043c\u044b", "HeaderPlayers": "\u0418\u0433\u0440\u043e\u043a\u0438:", - "HeaderAlbumArtists": "\u0410\u043b\u044c\u0431\u043e\u043c. \u0438\u0441\u043f\u043e\u043b\u043d\u0438\u0442\u0435\u043b\u0438", + "HeaderAlbumArtists": "\u0418\u0441\u043f-\u043b\u0438 \u0430\u043b\u044c\u0431\u043e\u043c\u0430", "HeaderAlbums": "\u0410\u043b\u044c\u0431\u043e\u043c\u044b", "HeaderDisc": "\u0414\u0438\u0441\u043a", "HeaderTrack": "\u0414\u043e\u0440-\u043a\u0430", @@ -172,5 +172,6 @@ "HeaderProducer": "\u041f\u0440\u043e\u0434\u044e\u0441\u0435\u0440\u044b", "HeaderWriter": "\u0421\u0446\u0435\u043d\u0430\u0440\u0438\u0441\u0442\u044b", "HeaderParentalRatings": "\u0412\u043e\u0437\u0440\u0430\u0441\u0442\u043d\u0430\u044f \u043a\u0430\u0442\u0435\u0433\u043e\u0440\u0438\u044f", - "HeaderCommunityRatings": "\u041e\u0431\u0449. \u043e\u0446\u0435\u043d\u043a\u0438" + "HeaderCommunityRatings": "\u041e\u0431\u0449. \u043e\u0446\u0435\u043d\u043a\u0438", + "StartupEmbyServerIsLoading": "Emby Server \u0437\u0430\u0433\u0440\u0443\u0436\u0430\u0435\u0442\u0441\u044f. \u041f\u043e\u0432\u0442\u043e\u0440\u0438\u0442\u0435 \u043f\u043e\u043f\u044b\u0442\u043a\u0443 \u0432\u0441\u043a\u043e\u0440\u0435." }
\ No newline at end of file diff --git a/MediaBrowser.Server.Implementations/Localization/Core/sl-SI.json b/MediaBrowser.Server.Implementations/Localization/Core/sl-SI.json index abe203804..e444c0e93 100644 --- a/MediaBrowser.Server.Implementations/Localization/Core/sl-SI.json +++ b/MediaBrowser.Server.Implementations/Localization/Core/sl-SI.json @@ -172,5 +172,6 @@ "HeaderProducer": "Producers", "HeaderWriter": "Writers", "HeaderParentalRatings": "Parental Ratings", - "HeaderCommunityRatings": "Community ratings" + "HeaderCommunityRatings": "Community ratings", + "StartupEmbyServerIsLoading": "Emby Server is loading. Please try again shortly." }
\ No newline at end of file diff --git a/MediaBrowser.Server.Implementations/Localization/Core/sv.json b/MediaBrowser.Server.Implementations/Localization/Core/sv.json index 3a5a322c6..667b8e4b4 100644 --- a/MediaBrowser.Server.Implementations/Localization/Core/sv.json +++ b/MediaBrowser.Server.Implementations/Localization/Core/sv.json @@ -172,5 +172,6 @@ "HeaderProducer": "Producers", "HeaderWriter": "Writers", "HeaderParentalRatings": "Parental Ratings", - "HeaderCommunityRatings": "Community ratings" + "HeaderCommunityRatings": "Community ratings", + "StartupEmbyServerIsLoading": "Emby Server is loading. Please try again shortly." }
\ No newline at end of file diff --git a/MediaBrowser.Server.Implementations/Localization/Core/tr.json b/MediaBrowser.Server.Implementations/Localization/Core/tr.json index f127cc816..810df2665 100644 --- a/MediaBrowser.Server.Implementations/Localization/Core/tr.json +++ b/MediaBrowser.Server.Implementations/Localization/Core/tr.json @@ -172,5 +172,6 @@ "HeaderProducer": "Producers", "HeaderWriter": "Writers", "HeaderParentalRatings": "Parental Ratings", - "HeaderCommunityRatings": "Community ratings" + "HeaderCommunityRatings": "Community ratings", + "StartupEmbyServerIsLoading": "Emby Server is loading. Please try again shortly." }
\ No newline at end of file diff --git a/MediaBrowser.Server.Implementations/Localization/Core/uk.json b/MediaBrowser.Server.Implementations/Localization/Core/uk.json index ec9ca5a39..73c9b26f9 100644 --- a/MediaBrowser.Server.Implementations/Localization/Core/uk.json +++ b/MediaBrowser.Server.Implementations/Localization/Core/uk.json @@ -172,5 +172,6 @@ "HeaderProducer": "Producers", "HeaderWriter": "Writers", "HeaderParentalRatings": "Parental Ratings", - "HeaderCommunityRatings": "Community ratings" + "HeaderCommunityRatings": "Community ratings", + "StartupEmbyServerIsLoading": "Emby Server is loading. Please try again shortly." }
\ No newline at end of file diff --git a/MediaBrowser.Server.Implementations/Localization/Core/vi.json b/MediaBrowser.Server.Implementations/Localization/Core/vi.json index 6e0c3ea20..75a5dbea7 100644 --- a/MediaBrowser.Server.Implementations/Localization/Core/vi.json +++ b/MediaBrowser.Server.Implementations/Localization/Core/vi.json @@ -172,5 +172,6 @@ "HeaderProducer": "Producers", "HeaderWriter": "Writers", "HeaderParentalRatings": "Parental Ratings", - "HeaderCommunityRatings": "Community ratings" + "HeaderCommunityRatings": "Community ratings", + "StartupEmbyServerIsLoading": "Emby Server is loading. Please try again shortly." }
\ No newline at end of file diff --git a/MediaBrowser.Server.Implementations/Localization/Core/zh-CN.json b/MediaBrowser.Server.Implementations/Localization/Core/zh-CN.json index 54cd7e59b..3368e5ccd 100644 --- a/MediaBrowser.Server.Implementations/Localization/Core/zh-CN.json +++ b/MediaBrowser.Server.Implementations/Localization/Core/zh-CN.json @@ -172,5 +172,6 @@ "HeaderProducer": "Producers", "HeaderWriter": "Writers", "HeaderParentalRatings": "\u5bb6\u957f\u5206\u7ea7", - "HeaderCommunityRatings": "Community ratings" + "HeaderCommunityRatings": "Community ratings", + "StartupEmbyServerIsLoading": "Emby Server is loading. Please try again shortly." }
\ No newline at end of file diff --git a/MediaBrowser.Server.Implementations/Localization/Core/zh-HK.json b/MediaBrowser.Server.Implementations/Localization/Core/zh-HK.json new file mode 100644 index 000000000..3abc8d75a --- /dev/null +++ b/MediaBrowser.Server.Implementations/Localization/Core/zh-HK.json @@ -0,0 +1,177 @@ +{ + "AppDeviceValues": "App: {0}, Device: {1}", + "UserDownloadingItemWithValues": "{0} is downloading {1}", + "FolderTypeMixed": "\u6df7\u5408\u5167\u5bb9", + "FolderTypeMovies": "\u96fb\u5f71", + "FolderTypeMusic": "\u97f3\u6a02", + "FolderTypeAdultVideos": "\u6210\u4eba\u5f71\u7247", + "FolderTypePhotos": "\u76f8\u7247", + "FolderTypeMusicVideos": "\u97f3\u6a02\u5f71\u7247", + "FolderTypeHomeVideos": "\u9996\u9801\u5f71\u7247", + "FolderTypeGames": "\u904a\u6232", + "FolderTypeBooks": "\u66f8\u85c9", + "FolderTypeTvShows": "\u96fb\u8996\u7bc0\u76ee", + "FolderTypeInherit": "\u7e7c\u627f", + "HeaderCastCrew": "\u6f14\u54e1\u9663\u5bb9", + "HeaderPeople": "People", + "ValueSpecialEpisodeName": "Special - {0}", + "LabelChapterName": "Chapter {0}", + "NameSeasonNumber": "Season {0}", + "LabelExit": "\u96e2\u958b", + "LabelVisitCommunity": "\u8a2a\u554f\u8a0e\u8ad6\u5340", + "LabelGithub": "Github", + "LabelApiDocumentation": "Api \u6587\u4ef6", + "LabelDeveloperResources": "\u958b\u767c\u8005\u8cc7\u6e90", + "LabelBrowseLibrary": "\u700f\u89bd\u8cc7\u6599\u5eab", + "LabelConfigureServer": "\u8a2d\u7f6e Emby", + "LabelRestartServer": "\u91cd\u555f\u4f3a\u670d\u5668", + "CategorySync": "\u540c\u6b65", + "CategoryUser": "User", + "CategorySystem": "System", + "CategoryApplication": "Application", + "CategoryPlugin": "Plugin", + "NotificationOptionPluginError": "Plugin failure", + "NotificationOptionApplicationUpdateAvailable": "Application update available", + "NotificationOptionApplicationUpdateInstalled": "Application update installed", + "NotificationOptionPluginUpdateInstalled": "Plugin update installed", + "NotificationOptionPluginInstalled": "Plugin installed", + "NotificationOptionPluginUninstalled": "Plugin uninstalled", + "NotificationOptionVideoPlayback": "Video playback started", + "NotificationOptionAudioPlayback": "Audio playback started", + "NotificationOptionGamePlayback": "Game playback started", + "NotificationOptionVideoPlaybackStopped": "Video playback stopped", + "NotificationOptionAudioPlaybackStopped": "Audio playback stopped", + "NotificationOptionGamePlaybackStopped": "Game playback stopped", + "NotificationOptionTaskFailed": "Scheduled task failure", + "NotificationOptionInstallationFailed": "Installation failure", + "NotificationOptionNewLibraryContent": "New content added", + "NotificationOptionNewLibraryContentMultiple": "New content added (multiple)", + "NotificationOptionCameraImageUploaded": "Camera image uploaded", + "NotificationOptionUserLockedOut": "User locked out", + "NotificationOptionServerRestartRequired": "Server restart required", + "ViewTypePlaylists": "Playlists", + "ViewTypeMovies": "Movies", + "ViewTypeTvShows": "TV", + "ViewTypeGames": "Games", + "ViewTypeMusic": "Music", + "ViewTypeMusicGenres": "Genres", + "ViewTypeMusicArtists": "Artists", + "ViewTypeBoxSets": "Collections", + "ViewTypeChannels": "Channels", + "ViewTypeLiveTV": "Live TV", + "ViewTypeLiveTvNowPlaying": "Now Airing", + "ViewTypeLatestGames": "Latest Games", + "ViewTypeRecentlyPlayedGames": "Recently Played", + "ViewTypeGameFavorites": "Favorites", + "ViewTypeGameSystems": "Game Systems", + "ViewTypeGameGenres": "Genres", + "ViewTypeTvResume": "Resume", + "ViewTypeTvNextUp": "Next Up", + "ViewTypeTvLatest": "Latest", + "ViewTypeTvShowSeries": "Series", + "ViewTypeTvGenres": "Genres", + "ViewTypeTvFavoriteSeries": "Favorite Series", + "ViewTypeTvFavoriteEpisodes": "Favorite Episodes", + "ViewTypeMovieResume": "Resume", + "ViewTypeMovieLatest": "Latest", + "ViewTypeMovieMovies": "Movies", + "ViewTypeMovieCollections": "Collections", + "ViewTypeMovieFavorites": "Favorites", + "ViewTypeMovieGenres": "Genres", + "ViewTypeMusicLatest": "Latest", + "ViewTypeMusicPlaylists": "Playlists", + "ViewTypeMusicAlbums": "Albums", + "ViewTypeMusicAlbumArtists": "Album Artists", + "HeaderOtherDisplaySettings": "Display Settings", + "ViewTypeMusicSongs": "Songs", + "ViewTypeMusicFavorites": "Favorites", + "ViewTypeMusicFavoriteAlbums": "Favorite Albums", + "ViewTypeMusicFavoriteArtists": "Favorite Artists", + "ViewTypeMusicFavoriteSongs": "Favorite Songs", + "ViewTypeFolders": "Folders", + "ViewTypeLiveTvRecordingGroups": "Recordings", + "ViewTypeLiveTvChannels": "Channels", + "ScheduledTaskFailedWithName": "{0} failed", + "LabelRunningTimeValue": "Running time: {0}", + "ScheduledTaskStartedWithName": "{0} started", + "VersionNumber": "\u7248\u672c {0}", + "PluginInstalledWithName": "{0} was installed", + "PluginUpdatedWithName": "{0} was updated", + "PluginUninstalledWithName": "{0} was uninstalled", + "ItemAddedWithName": "{0} was added to the library", + "ItemRemovedWithName": "{0} was removed from the library", + "LabelIpAddressValue": "Ip address: {0}", + "DeviceOnlineWithName": "{0} is connected", + "UserOnlineFromDevice": "{0} is online from {1}", + "ProviderValue": "Provider: {0}", + "SubtitlesDownloadedForItem": "Subtitles downloaded for {0}", + "UserConfigurationUpdatedWithName": "User configuration has been updated for {0}", + "UserCreatedWithName": "User {0} has been created", + "UserPasswordChangedWithName": "Password has been changed for user {0}", + "UserDeletedWithName": "User {0} has been deleted", + "MessageServerConfigurationUpdated": "Server configuration has been updated", + "MessageNamedServerConfigurationUpdatedWithValue": "Server configuration section {0} has been updated", + "MessageApplicationUpdated": "Emby Server has been updated", + "FailedLoginAttemptWithUserName": "Failed login attempt from {0}", + "AuthenticationSucceededWithUserName": "{0} successfully authenticated", + "DeviceOfflineWithName": "{0} has disconnected", + "UserLockedOutWithName": "User {0} has been locked out", + "UserOfflineFromDevice": "{0} has disconnected from {1}", + "UserStartedPlayingItemWithValues": "{0} has started playing {1}", + "UserStoppedPlayingItemWithValues": "{0} has stopped playing {1}", + "SubtitleDownloadFailureForItem": "Subtitles failed to download for {0}", + "HeaderUnidentified": "Unidentified", + "HeaderImagePrimary": "Primary", + "HeaderImageBackdrop": "Backdrop", + "HeaderImageLogo": "Logo", + "HeaderUserPrimaryImage": "User Image", + "HeaderOverview": "Overview", + "HeaderShortOverview": "Short Overview", + "HeaderType": "Type", + "HeaderSeverity": "Severity", + "HeaderUser": "User", + "HeaderName": "\u540d\u7a31", + "HeaderDate": "\u65e5\u671f", + "HeaderPremiereDate": "Premiere Date", + "HeaderDateAdded": "Date Added", + "HeaderReleaseDate": "Release date", + "HeaderRuntime": "Runtime", + "HeaderPlayCount": "Play Count", + "HeaderSeason": "Season", + "HeaderSeasonNumber": "Season number", + "HeaderSeries": "Series:", + "HeaderNetwork": "Network", + "HeaderYear": "Year:", + "HeaderYears": "Years:", + "HeaderParentalRating": "Parental Rating", + "HeaderCommunityRating": "Community rating", + "HeaderTrailers": "Trailers", + "HeaderSpecials": "Specials", + "HeaderGameSystems": "Game Systems", + "HeaderPlayers": "Players:", + "HeaderAlbumArtists": "Album Artists", + "HeaderAlbums": "Albums", + "HeaderDisc": "Disc", + "HeaderTrack": "Track", + "HeaderAudio": "\u97f3\u8a0a", + "HeaderVideo": "\u5f71\u7247", + "HeaderEmbeddedImage": "Embedded image", + "HeaderResolution": "Resolution", + "HeaderSubtitles": "Subtitles", + "HeaderGenres": "Genres", + "HeaderCountries": "Countries", + "HeaderStatus": "\u72c0\u614b", + "HeaderTracks": "Tracks", + "HeaderMusicArtist": "Music artist", + "HeaderLocked": "Locked", + "HeaderStudios": "Studios", + "HeaderActor": "Actors", + "HeaderComposer": "Composers", + "HeaderDirector": "Directors", + "HeaderGuestStar": "Guest star", + "HeaderProducer": "Producers", + "HeaderWriter": "Writers", + "HeaderParentalRatings": "Parental Ratings", + "HeaderCommunityRatings": "Community ratings", + "StartupEmbyServerIsLoading": "Emby Server is loading. Please try again shortly." +}
\ No newline at end of file diff --git a/MediaBrowser.Server.Implementations/Localization/Core/zh-TW.json b/MediaBrowser.Server.Implementations/Localization/Core/zh-TW.json index 787508176..2bcb5c132 100644 --- a/MediaBrowser.Server.Implementations/Localization/Core/zh-TW.json +++ b/MediaBrowser.Server.Implementations/Localization/Core/zh-TW.json @@ -172,5 +172,6 @@ "HeaderProducer": "Producers", "HeaderWriter": "Writers", "HeaderParentalRatings": "Parental Ratings", - "HeaderCommunityRatings": "Community ratings" + "HeaderCommunityRatings": "Community ratings", + "StartupEmbyServerIsLoading": "Emby Server is loading. Please try again shortly." }
\ No newline at end of file diff --git a/MediaBrowser.Server.Implementations/Localization/LocalizationManager.cs b/MediaBrowser.Server.Implementations/Localization/LocalizationManager.cs index 8c893c8d1..e776f4311 100644 --- a/MediaBrowser.Server.Implementations/Localization/LocalizationManager.cs +++ b/MediaBrowser.Server.Implementations/Localization/LocalizationManager.cs @@ -58,7 +58,7 @@ namespace MediaBrowser.Server.Implementations.Localization var localizationPath = LocalizationPath; - Directory.CreateDirectory(localizationPath); + _fileSystem.CreateDirectory(localizationPath); var existingFiles = Directory.EnumerateFiles(localizationPath, "ratings-*.txt", SearchOption.TopDirectoryOnly) .Select(Path.GetFileName) @@ -212,7 +212,7 @@ namespace MediaBrowser.Server.Implementations.Localization /// <returns>Dictionary{System.StringParentalRating}.</returns> private void LoadRatings(string file) { - var dict = File.ReadAllLines(file).Select(i => + var dict = File.ReadAllLines(file).Select(i => { if (!string.IsNullOrWhiteSpace(i)) { diff --git a/MediaBrowser.Server.Implementations/MediaBrowser.Server.Implementations.csproj b/MediaBrowser.Server.Implementations/MediaBrowser.Server.Implementations.csproj index f0312cef6..186db6792 100644 --- a/MediaBrowser.Server.Implementations/MediaBrowser.Server.Implementations.csproj +++ b/MediaBrowser.Server.Implementations/MediaBrowser.Server.Implementations.csproj @@ -11,10 +11,10 @@ <AssemblyName>MediaBrowser.Server.Implementations</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 Condition=" '$(Configuration)|$(Platform)' == 'Release Mono|AnyCPU' "> <DebugType>none</DebugType> @@ -42,34 +40,24 @@ <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="MediaBrowser.Naming, Version=1.0.5614.25103, Culture=neutral, processorArchitecture=MSIL"> + <Reference Include="MediaBrowser.Naming, Version=1.0.5748.36038, Culture=neutral, processorArchitecture=MSIL"> <SpecificVersion>False</SpecificVersion> - <HintPath>..\packages\MediaBrowser.Naming.1.0.0.37\lib\portable-net45+sl4+wp71+win8+wpa81\MediaBrowser.Naming.dll</HintPath> - </Reference> - <Reference Include="Mono.Nat, Version=1.2.24.0, Culture=neutral, processorArchitecture=MSIL"> - <SpecificVersion>False</SpecificVersion> - <HintPath>..\packages\Mono.Nat.1.2.24.0\lib\net40\Mono.Nat.dll</HintPath> - </Reference> - <Reference Include="MoreLinq, Version=1.1.17511.0, Culture=neutral, PublicKeyToken=384d532d7e88985d, processorArchitecture=MSIL"> - <SpecificVersion>False</SpecificVersion> - <HintPath>..\packages\morelinq.1.1.0\lib\net35\MoreLinq.dll</HintPath> + <HintPath>..\packages\MediaBrowser.Naming.1.0.0.38\lib\portable-net45+sl4+wp71+win8+wpa81\MediaBrowser.Naming.dll</HintPath> </Reference> <Reference Include="Patterns.Logging"> <HintPath>..\packages\Patterns.Logging.1.0.0.2\lib\portable-net45+sl4+wp71+win8+wpa81\Patterns.Logging.dll</HintPath> - <Private>True</Private> </Reference> <Reference Include="ServiceStack.Api.Swagger"> <HintPath>..\ThirdParty\ServiceStack\ServiceStack.Api.Swagger.dll</HintPath> </Reference> - <Reference Include="SocketHttpListener, Version=1.0.5634.16042, Culture=neutral, processorArchitecture=MSIL"> + <Reference Include="SocketHttpListener, Version=1.0.5749.18620, Culture=neutral, processorArchitecture=MSIL"> <SpecificVersion>False</SpecificVersion> - <HintPath>..\packages\SocketHttpListener.1.0.0.7\lib\net45\SocketHttpListener.dll</HintPath> + <HintPath>..\packages\SocketHttpListener.1.0.0.9\lib\net45\SocketHttpListener.dll</HintPath> </Reference> <Reference Include="System" /> <Reference Include="System.Core" /> @@ -100,6 +88,12 @@ <Reference Include="UniversalDetector"> <HintPath>..\ThirdParty\UniversalDetector\UniversalDetector.dll</HintPath> </Reference> + <Reference Include="Mono.Nat"> + <HintPath>..\packages\Mono.Nat.1.2.24.0\lib\net40\Mono.Nat.dll</HintPath> + </Reference> + <Reference Include="MoreLinq"> + <HintPath>..\packages\morelinq.1.1.1\lib\net35\MoreLinq.dll</HintPath> + </Reference> </ItemGroup> <ItemGroup> <Compile Include="..\SharedVersion.cs"> @@ -240,6 +234,7 @@ <Compile Include="Localization\LocalizationManager.cs" /> <Compile Include="Logging\PatternsLogger.cs" /> <Compile Include="MediaEncoder\EncodingManager.cs" /> + <Compile Include="Notifications\IConfigurableNotificationService.cs" /> <Compile Include="Persistence\BaseSqliteRepository.cs" /> <Compile Include="Persistence\CleanDatabaseScheduledTask.cs" /> <Compile Include="Social\SharingManager.cs" /> @@ -334,7 +329,6 @@ <Compile Include="Sync\SyncRepository.cs" /> <Compile Include="Sync\SyncConvertScheduledTask.cs" /> <Compile Include="Sync\TargetDataProvider.cs" /> - <Compile Include="Themes\AppThemeManager.cs" /> <Compile Include="TV\TVSeriesManager.cs" /> <Compile Include="Udp\UdpMessageReceivedEventArgs.cs" /> <Compile Include="Udp\UdpServer.cs" /> @@ -413,6 +407,7 @@ <EmbeddedResource Include="Localization\Core\vi.json" /> <EmbeddedResource Include="Localization\Core\zh-CN.json" /> <EmbeddedResource Include="Localization\Core\zh-TW.json" /> + <EmbeddedResource Include="Localization\Core\zh-HK.json" /> <None Include="packages.config" /> </ItemGroup> <ItemGroup> diff --git a/MediaBrowser.Server.Implementations/MediaEncoder/EncodingManager.cs b/MediaBrowser.Server.Implementations/MediaEncoder/EncodingManager.cs index 6b99883a5..550d76f44 100644 --- a/MediaBrowser.Server.Implementations/MediaEncoder/EncodingManager.cs +++ b/MediaBrowser.Server.Implementations/MediaEncoder/EncodingManager.cs @@ -134,11 +134,11 @@ namespace MediaBrowser.Server.Implementations.MediaEncoder var protocol = MediaProtocol.File; - var inputPath = MediaEncoderHelpers.GetInputArgument(video.Path, protocol, null, video.PlayableStreamFileNames); + var inputPath = MediaEncoderHelpers.GetInputArgument(_fileSystem, video.Path, protocol, null, video.PlayableStreamFileNames); try { - Directory.CreateDirectory(Path.GetDirectoryName(path)); + _fileSystem.CreateDirectory(Path.GetDirectoryName(path)); using (var stream = await _encoder.ExtractVideoImage(inputPath, protocol, video.Video3DFormat, time, cancellationToken).ConfigureAwait(false)) { @@ -194,7 +194,7 @@ namespace MediaBrowser.Server.Implementations.MediaEncoder try { - return Directory.EnumerateFiles(path) + return _fileSystem.GetFilePaths(path) .ToList(); } catch (DirectoryNotFoundException) diff --git a/MediaBrowser.Server.Implementations/News/NewsEntryPoint.cs b/MediaBrowser.Server.Implementations/News/NewsEntryPoint.cs index 619384b6f..0266756c3 100644 --- a/MediaBrowser.Server.Implementations/News/NewsEntryPoint.cs +++ b/MediaBrowser.Server.Implementations/News/NewsEntryPoint.cs @@ -71,7 +71,7 @@ namespace MediaBrowser.Server.Implementations.News { DateTime? lastUpdate = null; - if (File.Exists(path)) + if (_fileSystem.FileExists(path)) { lastUpdate = _fileSystem.GetLastWriteTimeUtc(path); } diff --git a/MediaBrowser.Server.Implementations/Notifications/IConfigurableNotificationService.cs b/MediaBrowser.Server.Implementations/Notifications/IConfigurableNotificationService.cs new file mode 100644 index 000000000..5c4f400b0 --- /dev/null +++ b/MediaBrowser.Server.Implementations/Notifications/IConfigurableNotificationService.cs @@ -0,0 +1,14 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace MediaBrowser.Server.Implementations.Notifications +{ + public interface IConfigurableNotificationService + { + bool IsHidden { get; } + bool IsEnabled(string notificationType); + } +} diff --git a/MediaBrowser.Server.Implementations/Notifications/InternalNotificationService.cs b/MediaBrowser.Server.Implementations/Notifications/InternalNotificationService.cs index 56cb52f10..4a625f0fb 100644 --- a/MediaBrowser.Server.Implementations/Notifications/InternalNotificationService.cs +++ b/MediaBrowser.Server.Implementations/Notifications/InternalNotificationService.cs @@ -3,10 +3,11 @@ using MediaBrowser.Controller.Notifications; using MediaBrowser.Model.Notifications; using System.Threading; using System.Threading.Tasks; +using System; namespace MediaBrowser.Server.Implementations.Notifications { - public class InternalNotificationService : INotificationService + public class InternalNotificationService : INotificationService, IConfigurableNotificationService { private readonly INotificationsRepository _repo; @@ -36,6 +37,24 @@ namespace MediaBrowser.Server.Implementations.Notifications public bool IsEnabledForUser(User user) { + return user.Policy.IsAdministrator; + } + + public bool IsHidden + { + get { return true; } + } + + public bool IsEnabled(string notificationType) + { + if (notificationType.IndexOf("playback", StringComparison.OrdinalIgnoreCase) != -1) + { + return false; + } + if (notificationType.IndexOf("newlibrarycontent", StringComparison.OrdinalIgnoreCase) != -1) + { + return false; + } return true; } } diff --git a/MediaBrowser.Server.Implementations/Notifications/NotificationManager.cs b/MediaBrowser.Server.Implementations/Notifications/NotificationManager.cs index 1ff928cd5..f19ff8a5f 100644 --- a/MediaBrowser.Server.Implementations/Notifications/NotificationManager.cs +++ b/MediaBrowser.Server.Implementations/Notifications/NotificationManager.cs @@ -230,8 +230,19 @@ namespace MediaBrowser.Server.Implementations.Notifications private bool IsEnabled(INotificationService service, string notificationType) { - return string.IsNullOrEmpty(notificationType) || - GetConfiguration().IsServiceEnabled(service.Name, notificationType); + if (string.IsNullOrEmpty(notificationType)) + { + return true; + } + + var configurable = service as IConfigurableNotificationService; + + if (configurable != null) + { + return configurable.IsEnabled(notificationType); + } + + return GetConfiguration().IsServiceEnabled(service.Name, notificationType); } public void AddParts(IEnumerable<INotificationService> services, IEnumerable<INotificationTypeFactory> notificationTypeFactories) @@ -268,7 +279,13 @@ namespace MediaBrowser.Server.Implementations.Notifications public IEnumerable<NotificationServiceInfo> GetNotificationServices() { - return _services.Select(i => new NotificationServiceInfo + return _services.Where(i => + { + var configurable = i as IConfigurableNotificationService; + + return configurable == null || !configurable.IsHidden; + + }).Select(i => new NotificationServiceInfo { Name = i.Name, Id = i.Name.GetMD5().ToString("N") diff --git a/MediaBrowser.Server.Implementations/Persistence/CleanDatabaseScheduledTask.cs b/MediaBrowser.Server.Implementations/Persistence/CleanDatabaseScheduledTask.cs index 9f87483ba..709ed5ec9 100644 --- a/MediaBrowser.Server.Implementations/Persistence/CleanDatabaseScheduledTask.cs +++ b/MediaBrowser.Server.Implementations/Persistence/CleanDatabaseScheduledTask.cs @@ -1,15 +1,18 @@ -using MediaBrowser.Common.Progress; +using MediaBrowser.Common.IO; +using MediaBrowser.Common.Progress; using MediaBrowser.Common.ScheduledTasks; using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Library; using MediaBrowser.Controller.LiveTv; using MediaBrowser.Controller.Persistence; +using MediaBrowser.Model.Entities; using MediaBrowser.Model.Logging; using System; using System.Collections.Generic; using System.Threading; using System.Threading.Tasks; +using MediaBrowser.Controller.Entities.Audio; namespace MediaBrowser.Server.Implementations.Persistence { @@ -19,13 +22,15 @@ namespace MediaBrowser.Server.Implementations.Persistence private readonly IItemRepository _itemRepo; private readonly ILogger _logger; private readonly IServerConfigurationManager _config; + private readonly IFileSystem _fileSystem; - public CleanDatabaseScheduledTask(ILibraryManager libraryManager, IItemRepository itemRepo, ILogger logger, IServerConfigurationManager config) + public CleanDatabaseScheduledTask(ILibraryManager libraryManager, IItemRepository itemRepo, ILogger logger, IServerConfigurationManager config, IFileSystem fileSystem) { _libraryManager = libraryManager; _itemRepo = itemRepo; _logger = logger; _config = config; + _fileSystem = fileSystem; } public string Name @@ -46,15 +51,18 @@ namespace MediaBrowser.Server.Implementations.Persistence public async Task Execute(CancellationToken cancellationToken, IProgress<double> progress) { var innerProgress = new ActionableProgress<double>(); - innerProgress.RegisterAction(p => progress.Report(.95 * p)); + innerProgress.RegisterAction(p => progress.Report(.4 * p)); await UpdateToLatestSchema(cancellationToken, innerProgress).ConfigureAwait(false); innerProgress = new ActionableProgress<double>(); - innerProgress.RegisterAction(p => progress.Report(95 + (.05 * p))); - - //await CleanDeadItems(cancellationToken, innerProgress).ConfigureAwait(false); + innerProgress.RegisterAction(p => progress.Report(40 + (.05 * p))); + await CleanDeadItems(cancellationToken, innerProgress).ConfigureAwait(false); + progress.Report(45); + innerProgress = new ActionableProgress<double>(); + innerProgress.RegisterAction(p => progress.Report(45 + (.55 * p))); + await CleanDeletedItems(cancellationToken, innerProgress).ConfigureAwait(false); progress.Report(100); } @@ -77,6 +85,12 @@ namespace MediaBrowser.Server.Implementations.Persistence { cancellationToken.ThrowIfCancellationRequested(); + if (itemId == Guid.Empty) + { + // Somehow some invalid data got into the db. It probably predates the boundary checking + continue; + } + var item = _libraryManager.GetItemById(itemId); if (item != null) @@ -147,11 +161,69 @@ namespace MediaBrowser.Server.Implementations.Persistence progress.Report(100); } + private async Task CleanDeletedItems(CancellationToken cancellationToken, IProgress<double> progress) + { + var result = _itemRepo.GetItemIdsWithPath(new InternalItemsQuery + { + IsOffline = false, + LocationType = LocationType.FileSystem, + //Limit = limit, + + // These have their own cleanup routines + ExcludeItemTypes = new[] { typeof(Person).Name, typeof(Genre).Name, typeof(MusicGenre).Name, typeof(GameGenre).Name, typeof(Studio).Name, typeof(Year).Name } + }); + + var numComplete = 0; + var numItems = result.Items.Length; + + foreach (var item in result.Items) + { + cancellationToken.ThrowIfCancellationRequested(); + + var path = item.Item2; + + try + { + if (_fileSystem.FileExists(path) || _fileSystem.DirectoryExists(path)) + { + continue; + } + + var libraryItem = _libraryManager.GetItemById(item.Item1); + + if (Folder.IsPathOffline(path)) + { + libraryItem.IsOffline = true; + await libraryItem.UpdateToRepository(ItemUpdateType.None, cancellationToken).ConfigureAwait(false); + continue; + } + + await _libraryManager.DeleteItem(libraryItem, new DeleteOptions + { + DeleteFileLocation = false + }); + } + catch (OperationCanceledException) + { + throw; + } + catch (Exception ex) + { + _logger.ErrorException("Error in CleanDeletedItems. File {0}", ex, path); + } + + numComplete++; + double percent = numComplete; + percent /= numItems; + progress.Report(percent * 100); + } + } + public IEnumerable<ITaskTrigger> GetDefaultTriggers() { return new ITaskTrigger[] { - new IntervalTrigger{ Interval = TimeSpan.FromDays(1)} + new IntervalTrigger{ Interval = TimeSpan.FromHours(6)} }; } } diff --git a/MediaBrowser.Server.Implementations/Persistence/SqliteItemRepository.cs b/MediaBrowser.Server.Implementations/Persistence/SqliteItemRepository.cs index 00ebf7ea6..8ce121db3 100644 --- a/MediaBrowser.Server.Implementations/Persistence/SqliteItemRepository.cs +++ b/MediaBrowser.Server.Implementations/Persistence/SqliteItemRepository.cs @@ -72,7 +72,7 @@ namespace MediaBrowser.Server.Implementations.Persistence private IDbCommand _deletePeopleCommand; private IDbCommand _savePersonCommand; - private const int LatestSchemaVersion = 6; + private const int LatestSchemaVersion = 11; /// <summary> /// Initializes a new instance of the <see cref="SqliteItemRepository"/> class. @@ -175,7 +175,19 @@ namespace MediaBrowser.Server.Implementations.Persistence _connection.AddColumn(_logger, "TypedBaseItems", "ForcedSortName", "Text"); _connection.AddColumn(_logger, "TypedBaseItems", "IsOffline", "BIT"); + _connection.AddColumn(_logger, "TypedBaseItems", "LocationType", "Text"); + _connection.AddColumn(_logger, "TypedBaseItems", "IsSeries", "BIT"); + _connection.AddColumn(_logger, "TypedBaseItems", "IsLive", "BIT"); + _connection.AddColumn(_logger, "TypedBaseItems", "IsNews", "BIT"); + _connection.AddColumn(_logger, "TypedBaseItems", "IsPremiere", "BIT"); + + _connection.AddColumn(_logger, "TypedBaseItems", "EpisodeTitle", "Text"); + _connection.AddColumn(_logger, "TypedBaseItems", "IsRepeat", "BIT"); + + _connection.AddColumn(_logger, "TypedBaseItems", "PreferredMetadataLanguage", "Text"); + _connection.AddColumn(_logger, "TypedBaseItems", "PreferredMetadataCountryCode", "Text"); + PrepareStatements(); _mediaStreamsRepository.Initialize(); @@ -187,11 +199,29 @@ namespace MediaBrowser.Server.Implementations.Persistence /// </summary> private readonly SemaphoreSlim _writeLock = new SemaphoreSlim(1, 1); - private string[] _retriveItemColumns = + private readonly string[] _retriveItemColumns = { "type", "data", - "IsOffline" + "StartDate", + "EndDate", + "IsOffline", + "ChannelId", + "IsMovie", + "IsSports", + "IsKids", + "IsSeries", + "IsLive", + "IsNews", + "IsPremiere", + "EpisodeTitle", + "IsRepeat", + "CommunityRating", + "CustomRating", + "IndexNumber", + "IsLocked", + "PreferredMetadataLanguage", + "PreferredMetadataCountryCode" }; /// <summary> @@ -211,6 +241,12 @@ namespace MediaBrowser.Server.Implementations.Persistence "IsKids", "IsMovie", "IsSports", + "IsSeries", + "IsLive", + "IsNews", + "IsPremiere", + "EpisodeTitle", + "IsRepeat", "CommunityRating", "CustomRating", "IndexNumber", @@ -235,7 +271,10 @@ namespace MediaBrowser.Server.Implementations.Persistence "DateCreated", "DateModified", "ForcedSortName", - "IsOffline" + "IsOffline", + "LocationType", + "PreferredMetadataLanguage", + "PreferredMetadataCountryCode" }; _saveItemCommand = _connection.CreateCommand(); _saveItemCommand.CommandText = "replace into TypedBaseItems (" + string.Join(",", saveColumns.ToArray()) + ") values ("; @@ -357,12 +396,24 @@ namespace MediaBrowser.Server.Implementations.Persistence _saveItemCommand.GetParameter(index++).Value = hasProgramAttributes.IsKids; _saveItemCommand.GetParameter(index++).Value = hasProgramAttributes.IsMovie; _saveItemCommand.GetParameter(index++).Value = hasProgramAttributes.IsSports; + _saveItemCommand.GetParameter(index++).Value = hasProgramAttributes.IsSeries; + _saveItemCommand.GetParameter(index++).Value = hasProgramAttributes.IsLive; + _saveItemCommand.GetParameter(index++).Value = hasProgramAttributes.IsNews; + _saveItemCommand.GetParameter(index++).Value = hasProgramAttributes.IsPremiere; + _saveItemCommand.GetParameter(index++).Value = hasProgramAttributes.EpisodeTitle; + _saveItemCommand.GetParameter(index++).Value = hasProgramAttributes.IsRepeat; } else { _saveItemCommand.GetParameter(index++).Value = null; _saveItemCommand.GetParameter(index++).Value = null; _saveItemCommand.GetParameter(index++).Value = null; + _saveItemCommand.GetParameter(index++).Value = null; + _saveItemCommand.GetParameter(index++).Value = null; + _saveItemCommand.GetParameter(index++).Value = null; + _saveItemCommand.GetParameter(index++).Value = null; + _saveItemCommand.GetParameter(index++).Value = null; + _saveItemCommand.GetParameter(index++).Value = null; } _saveItemCommand.GetParameter(index++).Value = item.CommunityRating; @@ -405,7 +456,11 @@ namespace MediaBrowser.Server.Implementations.Persistence _saveItemCommand.GetParameter(index++).Value = item.ForcedSortName; _saveItemCommand.GetParameter(index++).Value = item.IsOffline; + _saveItemCommand.GetParameter(index++).Value = item.LocationType.ToString(); + _saveItemCommand.GetParameter(index++).Value = item.PreferredMetadataLanguage; + _saveItemCommand.GetParameter(index++).Value = item.PreferredMetadataCountryCode; + _saveItemCommand.Transaction = transaction; _saveItemCommand.ExecuteNonQuery(); @@ -511,7 +566,105 @@ namespace MediaBrowser.Server.Implementations.Persistence if (!reader.IsDBNull(2)) { - item.IsOffline = reader.GetBoolean(2); + var hasStartDate = item as IHasStartDate; + if (hasStartDate != null) + { + hasStartDate.StartDate = reader.GetDateTime(2).ToUniversalTime(); + } + } + + if (!reader.IsDBNull(3)) + { + item.EndDate = reader.GetDateTime(3).ToUniversalTime(); + } + + if (!reader.IsDBNull(4)) + { + item.IsOffline = reader.GetBoolean(4); + } + + if (!reader.IsDBNull(5)) + { + item.ChannelId = reader.GetString(5); + } + + var hasProgramAttributes = item as IHasProgramAttributes; + if (hasProgramAttributes != null) + { + if (!reader.IsDBNull(6)) + { + hasProgramAttributes.IsMovie = reader.GetBoolean(6); + } + + if (!reader.IsDBNull(7)) + { + hasProgramAttributes.IsSports = reader.GetBoolean(7); + } + + if (!reader.IsDBNull(8)) + { + hasProgramAttributes.IsKids = reader.GetBoolean(8); + } + + if (!reader.IsDBNull(9)) + { + hasProgramAttributes.IsSeries = reader.GetBoolean(9); + } + + if (!reader.IsDBNull(10)) + { + hasProgramAttributes.IsLive = reader.GetBoolean(10); + } + + if (!reader.IsDBNull(11)) + { + hasProgramAttributes.IsNews = reader.GetBoolean(11); + } + + if (!reader.IsDBNull(12)) + { + hasProgramAttributes.IsPremiere = reader.GetBoolean(12); + } + + if (!reader.IsDBNull(13)) + { + hasProgramAttributes.EpisodeTitle = reader.GetString(13); + } + + if (!reader.IsDBNull(14)) + { + hasProgramAttributes.IsRepeat = reader.GetBoolean(14); + } + } + + if (!reader.IsDBNull(15)) + { + item.CommunityRating = reader.GetFloat(15); + } + + if (!reader.IsDBNull(16)) + { + item.CustomRating = reader.GetString(16); + } + + if (!reader.IsDBNull(17)) + { + item.IndexNumber = reader.GetInt32(17); + } + + if (!reader.IsDBNull(18)) + { + item.IsLocked = reader.GetBoolean(18); + } + + if (!reader.IsDBNull(19)) + { + item.PreferredMetadataLanguage = reader.GetString(19); + } + + if (!reader.IsDBNull(20)) + { + item.PreferredMetadataCountryCode = reader.GetString(20); } return item; @@ -882,6 +1035,75 @@ namespace MediaBrowser.Server.Implementations.Persistence } } + public QueryResult<Tuple<Guid, string>> GetItemIdsWithPath(InternalItemsQuery query) + { + if (query == null) + { + throw new ArgumentNullException("query"); + } + + CheckDisposed(); + + using (var cmd = _connection.CreateCommand()) + { + cmd.CommandText = "select guid,path from TypedBaseItems"; + + var whereClauses = GetWhereClauses(query, cmd, false); + + var whereTextWithoutPaging = whereClauses.Count == 0 ? + string.Empty : + " where " + string.Join(" AND ", whereClauses.ToArray()); + + whereClauses = GetWhereClauses(query, cmd, true); + + var whereText = whereClauses.Count == 0 ? + string.Empty : + " where " + string.Join(" AND ", whereClauses.ToArray()); + + cmd.CommandText += whereText; + + cmd.CommandText += GetOrderByText(query); + + if (query.Limit.HasValue) + { + cmd.CommandText += " LIMIT " + query.Limit.Value.ToString(CultureInfo.InvariantCulture); + } + + cmd.CommandText += "; select count (guid) from TypedBaseItems" + whereTextWithoutPaging; + + var list = new List<Tuple<Guid, string>>(); + var count = 0; + + _logger.Debug(cmd.CommandText); + + using (var reader = cmd.ExecuteReader(CommandBehavior.SequentialAccess)) + { + while (reader.Read()) + { + var id = reader.GetGuid(0); + string path = null; + + if (!reader.IsDBNull(1)) + { + path = reader.GetString(1); + } + list.Add(new Tuple<Guid, string>(id, path)); + } + + if (reader.NextResult() && reader.Read()) + { + count = reader.GetInt32(0); + } + } + + return new QueryResult<Tuple<Guid, string>>() + { + Items = list.ToArray(), + TotalRecordCount = count + }; + } + } + public QueryResult<Guid> GetItemIds(InternalItemsQuery query) { if (query == null) @@ -960,6 +1182,16 @@ namespace MediaBrowser.Server.Implementations.Persistence } cmd.Parameters.Add(cmd, "@SchemaVersion", DbType.Int32).Value = LatestSchemaVersion; } + if (query.IsOffline.HasValue) + { + whereClauses.Add("IsOffline=@IsOffline"); + cmd.Parameters.Add(cmd, "@IsOffline", DbType.Boolean).Value = query.IsOffline; + } + if (query.LocationType.HasValue) + { + whereClauses.Add("LocationType=@LocationType"); + cmd.Parameters.Add(cmd, "@LocationType", DbType.String).Value = query.LocationType.Value; + } if (query.IsMovie.HasValue) { whereClauses.Add("IsMovie=@IsMovie"); @@ -1011,6 +1243,12 @@ namespace MediaBrowser.Server.Implementations.Persistence whereClauses.Add(string.Format("ChannelId in ({0})", inClause)); } + if (query.ParentId.HasValue) + { + whereClauses.Add("ParentId=@ParentId"); + cmd.Parameters.Add(cmd, "@ParentId", DbType.Guid).Value = query.ParentId.Value; + } + if (query.MinEndDate.HasValue) { whereClauses.Add("EndDate>=@MinEndDate"); diff --git a/MediaBrowser.Server.Implementations/Photos/BaseDynamicImageProvider.cs b/MediaBrowser.Server.Implementations/Photos/BaseDynamicImageProvider.cs index 4b7e1c3a7..bd5c571fe 100644 --- a/MediaBrowser.Server.Implementations/Photos/BaseDynamicImageProvider.cs +++ b/MediaBrowser.Server.Implementations/Photos/BaseDynamicImageProvider.cs @@ -92,7 +92,7 @@ namespace MediaBrowser.Server.Implementations.Photos CancellationToken cancellationToken) { var outputPath = Path.Combine(ApplicationPaths.TempDirectory, Guid.NewGuid() + ".png"); - Directory.CreateDirectory(Path.GetDirectoryName(outputPath)); + FileSystem.CreateDirectory(Path.GetDirectoryName(outputPath)); var imageCreated = await CreateImage(item, itemsWithImages, outputPath, imageType, 0).ConfigureAwait(false); if (!imageCreated) @@ -145,7 +145,7 @@ namespace MediaBrowser.Server.Implementations.Photos private Task<bool> CreateCollage(IHasImages primaryItem, List<BaseItem> items, string outputPath, int width, int height) { - Directory.CreateDirectory(Path.GetDirectoryName(outputPath)); + FileSystem.CreateDirectory(Path.GetDirectoryName(outputPath)); var options = new ImageCollageOptions { diff --git a/MediaBrowser.Server.Implementations/Playlists/ManualPlaylistsFolder.cs b/MediaBrowser.Server.Implementations/Playlists/ManualPlaylistsFolder.cs index 3ec41b6dc..0daef052d 100644 --- a/MediaBrowser.Server.Implementations/Playlists/ManualPlaylistsFolder.cs +++ b/MediaBrowser.Server.Implementations/Playlists/ManualPlaylistsFolder.cs @@ -4,6 +4,7 @@ using MediaBrowser.Controller.Playlists; using System.Collections.Generic; using System.IO; using System.Linq; +using MediaBrowser.Common.IO; namespace MediaBrowser.Server.Implementations.Playlists { @@ -46,17 +47,19 @@ namespace MediaBrowser.Server.Implementations.Playlists public class PlaylistsDynamicFolder : IVirtualFolderCreator { private readonly IApplicationPaths _appPaths; + private readonly IFileSystem _fileSystem; - public PlaylistsDynamicFolder(IApplicationPaths appPaths) + public PlaylistsDynamicFolder(IApplicationPaths appPaths, IFileSystem fileSystem) { _appPaths = appPaths; + _fileSystem = fileSystem; } public BasePluginFolder GetFolder() { var path = Path.Combine(_appPaths.DataPath, "playlists"); - Directory.CreateDirectory(path); + _fileSystem.CreateDirectory(path); return new PlaylistsFolder { diff --git a/MediaBrowser.Server.Implementations/Playlists/PlaylistManager.cs b/MediaBrowser.Server.Implementations/Playlists/PlaylistManager.cs index 857cf743f..53f2211d3 100644 --- a/MediaBrowser.Server.Implementations/Playlists/PlaylistManager.cs +++ b/MediaBrowser.Server.Implementations/Playlists/PlaylistManager.cs @@ -110,7 +110,7 @@ namespace MediaBrowser.Server.Implementations.Playlists try { - Directory.CreateDirectory(path); + _fileSystem.CreateDirectory(path); var playlist = new Playlist { @@ -128,7 +128,7 @@ namespace MediaBrowser.Server.Implementations.Playlists await parentFolder.AddChild(playlist, CancellationToken.None).ConfigureAwait(false); - await playlist.RefreshMetadata(new MetadataRefreshOptions { ForceSave = true }, CancellationToken.None) + await playlist.RefreshMetadata(new MetadataRefreshOptions(_fileSystem) { ForceSave = true }, CancellationToken.None) .ConfigureAwait(false); if (options.ItemIdList.Count > 0) @@ -150,7 +150,7 @@ namespace MediaBrowser.Server.Implementations.Playlists private string GetTargetPath(string path) { - while (Directory.Exists(path)) + while (_fileSystem.DirectoryExists(path)) { path += "1"; } @@ -196,7 +196,7 @@ namespace MediaBrowser.Server.Implementations.Playlists await playlist.UpdateToRepository(ItemUpdateType.MetadataEdit, CancellationToken.None).ConfigureAwait(false); - _providerManager.QueueRefresh(playlist.Id, new MetadataRefreshOptions + _providerManager.QueueRefresh(playlist.Id, new MetadataRefreshOptions(_fileSystem) { ForceSave = true }); @@ -223,7 +223,7 @@ namespace MediaBrowser.Server.Implementations.Playlists await playlist.UpdateToRepository(ItemUpdateType.MetadataEdit, CancellationToken.None).ConfigureAwait(false); - _providerManager.QueueRefresh(playlist.Id, new MetadataRefreshOptions + _providerManager.QueueRefresh(playlist.Id, new MetadataRefreshOptions(_fileSystem) { ForceSave = true }); diff --git a/MediaBrowser.Server.Implementations/ScheduledTasks/ChapterImagesTask.cs b/MediaBrowser.Server.Implementations/ScheduledTasks/ChapterImagesTask.cs index b23aaeeff..f8dc08e26 100644 --- a/MediaBrowser.Server.Implementations/ScheduledTasks/ChapterImagesTask.cs +++ b/MediaBrowser.Server.Implementations/ScheduledTasks/ChapterImagesTask.cs @@ -11,6 +11,7 @@ using System.IO; using System.Linq; using System.Threading; using System.Threading.Tasks; +using MediaBrowser.Common.IO; namespace MediaBrowser.Server.Implementations.ScheduledTasks { @@ -39,6 +40,7 @@ namespace MediaBrowser.Server.Implementations.ScheduledTasks private readonly IApplicationPaths _appPaths; private readonly IEncodingManager _encodingManager; + private readonly IFileSystem _fileSystem; /// <summary> /// Initializes a new instance of the <see cref="ChapterImagesTask" /> class. @@ -46,13 +48,14 @@ namespace MediaBrowser.Server.Implementations.ScheduledTasks /// <param name="logManager">The log manager.</param> /// <param name="libraryManager">The library manager.</param> /// <param name="itemRepo">The item repo.</param> - public ChapterImagesTask(ILogManager logManager, ILibraryManager libraryManager, IItemRepository itemRepo, IApplicationPaths appPaths, IEncodingManager encodingManager) + public ChapterImagesTask(ILogManager logManager, ILibraryManager libraryManager, IItemRepository itemRepo, IApplicationPaths appPaths, IEncodingManager encodingManager, IFileSystem fileSystem) { _logger = logManager.GetLogger(GetType().Name); _libraryManager = libraryManager; _itemRepo = itemRepo; _appPaths = appPaths; _encodingManager = encodingManager; + _fileSystem = fileSystem; } /// <summary> @@ -94,7 +97,7 @@ namespace MediaBrowser.Server.Implementations.ScheduledTasks try { - previouslyFailedImages = File.ReadAllText(failHistoryPath) + previouslyFailedImages = _fileSystem.ReadAllText(failHistoryPath) .Split(new[] { '|' }, StringSplitOptions.RemoveEmptyEntries) .ToList(); } @@ -132,9 +135,9 @@ namespace MediaBrowser.Server.Implementations.ScheduledTasks var parentPath = Path.GetDirectoryName(failHistoryPath); - Directory.CreateDirectory(parentPath); + _fileSystem.CreateDirectory(parentPath); - File.WriteAllText(failHistoryPath, string.Join("|", previouslyFailedImages.ToArray())); + _fileSystem.WriteAllText(failHistoryPath, string.Join("|", previouslyFailedImages.ToArray())); } numComplete++; diff --git a/MediaBrowser.Server.Implementations/Session/SessionManager.cs b/MediaBrowser.Server.Implementations/Session/SessionManager.cs index 70a4cb439..d164874dc 100644 --- a/MediaBrowser.Server.Implementations/Session/SessionManager.cs +++ b/MediaBrowser.Server.Implementations/Session/SessionManager.cs @@ -29,6 +29,7 @@ using System.Globalization; using System.Linq; using System.Threading; using System.Threading.Tasks; +using MediaBrowser.Controller.Net; namespace MediaBrowser.Server.Implementations.Session { @@ -1276,7 +1277,7 @@ namespace MediaBrowser.Server.Implementations.Session { if (!_deviceManager.CanAccessDevice(user.Id.ToString("N"), request.DeviceId)) { - throw new UnauthorizedAccessException("User is not allowed access from this device."); + throw new SecurityException("User is not allowed access from this device."); } } @@ -1286,7 +1287,7 @@ namespace MediaBrowser.Server.Implementations.Session { EventHelper.FireEventIfNotNull(AuthenticationFailed, this, new GenericEventArgs<AuthenticationRequest>(request), _logger); - throw new UnauthorizedAccessException("Invalid user or password entered."); + throw new SecurityException("Invalid user or password entered."); } var token = await GetAuthorizationToken(user.Id.ToString("N"), request.DeviceId, request.App, request.AppVersion, request.DeviceName).ConfigureAwait(false); @@ -1322,8 +1323,9 @@ namespace MediaBrowser.Server.Implementations.Session if (existing.Items.Length > 0) { - _logger.Debug("Reissuing access token"); - return existing.Items[0].AccessToken; + var token = existing.Items[0].AccessToken; + _logger.Debug("Reissuing access token: " + token); + return token; } var newToken = new AuthenticationInfo diff --git a/MediaBrowser.Server.Implementations/Sync/MediaSync.cs b/MediaBrowser.Server.Implementations/Sync/MediaSync.cs index 86ef58e42..83dd13c05 100644 --- a/MediaBrowser.Server.Implementations/Sync/MediaSync.cs +++ b/MediaBrowser.Server.Implementations/Sync/MediaSync.cs @@ -147,7 +147,14 @@ namespace MediaBrowser.Server.Implementations.Sync progress.Report(totalProgress); }); - await GetItem(provider, dataProvider, target, serverId, serverName, jobItem, innerProgress, cancellationToken).ConfigureAwait(false); + try + { + await GetItem(provider, dataProvider, target, serverId, serverName, jobItem, innerProgress, cancellationToken).ConfigureAwait(false); + } + catch (Exception ex) + { + _logger.ErrorException("Error syncing item", ex); + } numComplete++; startingPercent = numComplete; diff --git a/MediaBrowser.Server.Implementations/Sync/SyncJobProcessor.cs b/MediaBrowser.Server.Implementations/Sync/SyncJobProcessor.cs index 1061a373e..c9099dbe7 100644 --- a/MediaBrowser.Server.Implementations/Sync/SyncJobProcessor.cs +++ b/MediaBrowser.Server.Implementations/Sync/SyncJobProcessor.cs @@ -702,7 +702,7 @@ namespace MediaBrowser.Server.Implementations.Sync var path = Path.Combine(temporaryPath, filename); - Directory.CreateDirectory(Path.GetDirectoryName(path)); + _fileSystem.CreateDirectory(Path.GetDirectoryName(path)); using (var stream = await _subtitleEncoder.GetSubtitles(streamInfo.ItemId, streamInfo.MediaSourceId, subtitleStreamIndex, subtitleStreamInfo.Format, 0, null, cancellationToken).ConfigureAwait(false)) { diff --git a/MediaBrowser.Server.Implementations/Sync/SyncManager.cs b/MediaBrowser.Server.Implementations/Sync/SyncManager.cs index 18fcb4e79..15d196877 100644 --- a/MediaBrowser.Server.Implementations/Sync/SyncManager.cs +++ b/MediaBrowser.Server.Implementations/Sync/SyncManager.cs @@ -516,60 +516,25 @@ namespace MediaBrowser.Server.Implementations.Sync return false; } - if (!item.RunTimeTicks.HasValue) - { - return false; - } - var video = item as Video; if (video != null) { - if (video.VideoType == VideoType.Iso || video.VideoType == VideoType.HdDvd) - { - return false; - } - if (video.IsPlaceHolder) { return false; } - if (video.IsArchive) - { - return false; - } - - if (video.IsStacked) - { - return false; - } - if (video.IsShortcut) { return false; } } - var game = item as Game; - if (game != null) - { - if (game.IsMultiPart) - { - return false; - } - } - if (item is LiveTvChannel || item is IChannelItem) { return false; } - // It would be nice to support these later - if (item is Game || item is Book) - { - return false; - } - return true; } @@ -997,8 +962,6 @@ namespace MediaBrowser.Server.Implementations.Sync return false; } - // TODO: Make sure it hasn't been deleted - return true; } diff --git a/MediaBrowser.Server.Implementations/Themes/AppThemeManager.cs b/MediaBrowser.Server.Implementations/Themes/AppThemeManager.cs deleted file mode 100644 index 2711c08aa..000000000 --- a/MediaBrowser.Server.Implementations/Themes/AppThemeManager.cs +++ /dev/null @@ -1,168 +0,0 @@ -using MediaBrowser.Common.Extensions; -using MediaBrowser.Common.IO; -using MediaBrowser.Controller; -using MediaBrowser.Controller.Themes; -using MediaBrowser.Model.Logging; -using MediaBrowser.Model.Serialization; -using MediaBrowser.Model.Themes; -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; - -namespace MediaBrowser.Server.Implementations.Themes -{ - public class AppThemeManager : IAppThemeManager - { - private readonly IServerApplicationPaths _appPaths; - private readonly IFileSystem _fileSystem; - private readonly IJsonSerializer _json; - private readonly ILogger _logger; - - private readonly string[] _supportedImageExtensions = { ".png", ".jpg", ".jpeg" }; - - public AppThemeManager(IServerApplicationPaths appPaths, IFileSystem fileSystem, IJsonSerializer json, ILogger logger) - { - _appPaths = appPaths; - _fileSystem = fileSystem; - _json = json; - _logger = logger; - } - - private string ThemePath - { - get - { - return Path.Combine(_appPaths.ProgramDataPath, "appthemes"); - } - } - - private string GetThemesPath(string applicationName) - { - if (string.IsNullOrWhiteSpace(applicationName)) - { - throw new ArgumentNullException("applicationName"); - } - - // Force everything lowercase for consistency and maximum compatibility with case-sensitive file systems - var name = _fileSystem.GetValidFilename(applicationName.ToLower()); - - return Path.Combine(ThemePath, name); - } - - private string GetThemePath(string applicationName, string name) - { - if (string.IsNullOrWhiteSpace(name)) - { - throw new ArgumentNullException("name"); - } - - // Force everything lowercase for consistency and maximum compatibility with case-sensitive file systems - name = _fileSystem.GetValidFilename(name.ToLower()); - - return Path.Combine(GetThemesPath(applicationName), name); - } - - private string GetImagesPath(string applicationName, string themeName) - { - return Path.Combine(GetThemePath(applicationName, themeName), "images"); - } - - public IEnumerable<AppThemeInfo> GetThemes(string applicationName) - { - var path = GetThemesPath(applicationName); - - try - { - return Directory - .EnumerateFiles(path, "*", SearchOption.AllDirectories) - .Where(i => string.Equals(Path.GetExtension(i), ".json", StringComparison.OrdinalIgnoreCase)) - .Select(i => - { - try - { - return _json.DeserializeFromFile<AppThemeInfo>(i); - } - catch (Exception ex) - { - _logger.ErrorException("Error deserializing {0}", ex, i); - return null; - } - - }).Where(i => i != null); - } - catch (DirectoryNotFoundException) - { - return new List<AppThemeInfo>(); - } - } - - public AppTheme GetTheme(string applicationName, string name) - { - var themePath = GetThemePath(applicationName, name); - var file = Path.Combine(themePath, "theme.json"); - - var imagesPath = GetImagesPath(applicationName, name); - - var theme = _json.DeserializeFromFile<AppTheme>(file); - - theme.Images = new DirectoryInfo(imagesPath) - .EnumerateFiles("*", SearchOption.TopDirectoryOnly) - .Where(i => _supportedImageExtensions.Contains(i.Extension, StringComparer.OrdinalIgnoreCase)) - .Select(GetThemeImage) - .ToList(); - - return theme; - } - - private ThemeImage GetThemeImage(FileInfo file) - { - var dateModified = _fileSystem.GetLastWriteTimeUtc(file); - - var cacheTag = (file.FullName + dateModified.Ticks).GetMD5().ToString("N"); - - return new ThemeImage - { - CacheTag = cacheTag, - Name = file.Name - }; - } - - public void SaveTheme(AppTheme theme) - { - var themePath = GetThemePath(theme.AppName, theme.Name); - var file = Path.Combine(themePath, "theme.json"); - - Directory.CreateDirectory(themePath); - - // Clone it so that we don't serialize all the images - they're always dynamic - var clone = new AppTheme - { - AppName = theme.AppName, - Name = theme.Name, - Options = theme.Options, - Images = null - }; - - _json.SerializeToFile(clone, file); - } - - public InternalThemeImage GetImageImageInfo(string applicationName, string themeName, string imageName) - { - var imagesPath = GetImagesPath(applicationName, themeName); - - var file = new DirectoryInfo(imagesPath).EnumerateFiles("*", SearchOption.TopDirectoryOnly) - .First(i => string.Equals(i.Name, imageName, StringComparison.OrdinalIgnoreCase)); - - var themeImage = GetThemeImage(file); - - return new InternalThemeImage - { - CacheTag = themeImage.CacheTag, - Name = themeImage.Name, - Path = file.FullName, - DateModified = _fileSystem.GetLastWriteTimeUtc(file) - }; - } - } -} diff --git a/MediaBrowser.Server.Implementations/UserViews/DynamicImageProvider.cs b/MediaBrowser.Server.Implementations/UserViews/DynamicImageProvider.cs index 8321ab952..2f5702983 100644 --- a/MediaBrowser.Server.Implementations/UserViews/DynamicImageProvider.cs +++ b/MediaBrowser.Server.Implementations/UserViews/DynamicImageProvider.cs @@ -66,14 +66,14 @@ namespace MediaBrowser.Server.Implementations.UserViews } var isUsingCollectionStrip = IsUsingCollectionStrip(view); - var recursive = isUsingCollectionStrip && !new[] { CollectionType.Playlists, CollectionType.Channels }.Contains(view.ViewType ?? string.Empty, StringComparer.OrdinalIgnoreCase); + var recursive = isUsingCollectionStrip && !new[] { CollectionType.Channels, CollectionType.BoxSets, CollectionType.Playlists }.Contains(view.ViewType ?? string.Empty, StringComparer.OrdinalIgnoreCase); var result = await view.GetItems(new InternalItemsQuery { User = (view.UserId.HasValue ? _userManager.GetUserById(view.UserId.Value) : null), CollapseBoxSetItems = false, Recursive = recursive, - ExcludeItemTypes = new[] { "UserView", "CollectionFolder", "Playlist" } + ExcludeItemTypes = new[] { "UserView", "CollectionFolder" } }).ConfigureAwait(false); @@ -147,7 +147,14 @@ namespace MediaBrowser.Server.Implementations.UserViews { CollectionType.Movies, CollectionType.TvShows, - CollectionType.Music + CollectionType.Music, + CollectionType.Games, + CollectionType.Books, + CollectionType.MusicVideos, + CollectionType.HomeVideos, + CollectionType.BoxSets, + CollectionType.Playlists, + CollectionType.Photos }; return collectionStripViewTypes.Contains(view.ViewType ?? string.Empty); diff --git a/MediaBrowser.Server.Implementations/packages.config b/MediaBrowser.Server.Implementations/packages.config index 92388c99e..ac32c1f7c 100644 --- a/MediaBrowser.Server.Implementations/packages.config +++ b/MediaBrowser.Server.Implementations/packages.config @@ -1,9 +1,9 @@ <?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="Interfaces.IO" version="1.0.0.5" targetFramework="net45" />
- <package id="MediaBrowser.Naming" version="1.0.0.37" targetFramework="net45" />
+ <package id="MediaBrowser.Naming" version="1.0.0.38" targetFramework="net45" />
<package id="Mono.Nat" version="1.2.24.0" targetFramework="net45" />
- <package id="morelinq" version="1.1.0" targetFramework="net45" />
+ <package id="morelinq" version="1.1.1" targetFramework="net45" />
<package id="Patterns.Logging" version="1.0.0.2" targetFramework="net45" />
- <package id="SocketHttpListener" version="1.0.0.7" targetFramework="net45" />
+ <package id="SocketHttpListener" version="1.0.0.9" targetFramework="net45" />
</packages>
\ No newline at end of file diff --git a/MediaBrowser.Server.Mac.userprefs b/MediaBrowser.Server.Mac.userprefs index b5f227685..cd108e550 100644 --- a/MediaBrowser.Server.Mac.userprefs +++ b/MediaBrowser.Server.Mac.userprefs @@ -1,25 +1,6 @@ <Properties> <MonoDevelop.Ide.Workspace ActiveConfiguration="Release" /> - <MonoDevelop.Ide.Workbench ActiveDocument="MediaBrowser.Common/Properties/AssemblyInfo.cs"> - <Files> - <File FileName="MediaBrowser.Server.Mac/ImageMagickSharp.dll.config" Line="1" Column="1" /> - <File FileName="MediaBrowser.Server.Startup.Common/ApplicationHost.cs" Line="1" Column="18" /> - <File FileName="MediaBrowser.Api/ConnectService.cs" Line="7" Column="22" /> - <File FileName="MediaBrowser.Server.Mac/AppController.cs" Line="22" Column="1" /> - <File FileName="MediaBrowser.Server.Mac/AppDelegate.cs" Line="24" Column="1" /> - <File FileName="MediaBrowser.Server.Mac/Main.cs" Line="60" Column="4" /> - <File FileName="SharedVersion.cs" Line="1" Column="1" /> - <File FileName="MediaBrowser.Common/Properties/AssemblyInfo.cs" Line="9" Column="12" /> - </Files> - <Pads> - <Pad Id="ProjectPad"> - <State expanded="True"> - <Node name="Emby.Server.Mac" selected="True" /> - <Node name="OpenSubtitlesHandler" expanded="True" /> - </State> - </Pad> - </Pads> - </MonoDevelop.Ide.Workbench> + <MonoDevelop.Ide.Workbench /> <MonoDevelop.Ide.DebuggingService.Breakpoints> <BreakpointStore> <Breakpoint file="/Users/luke/MediaBrowser/MediaBrowser.Common.Implementations/BaseApplicationPaths.cs" line="233" column="1" /> diff --git a/MediaBrowser.Server.Startup.Common/ApplicationHost.cs b/MediaBrowser.Server.Startup.Common/ApplicationHost.cs index 9ecd0764a..e63adaebd 100644 --- a/MediaBrowser.Server.Startup.Common/ApplicationHost.cs +++ b/MediaBrowser.Server.Startup.Common/ApplicationHost.cs @@ -42,7 +42,6 @@ using MediaBrowser.Controller.Social; using MediaBrowser.Controller.Sorting; using MediaBrowser.Controller.Subtitles; using MediaBrowser.Controller.Sync; -using MediaBrowser.Controller.Themes; using MediaBrowser.Controller.TV; using MediaBrowser.Dlna; using MediaBrowser.Dlna.ConnectionManager; @@ -87,7 +86,6 @@ using MediaBrowser.Server.Implementations.ServerManager; using MediaBrowser.Server.Implementations.Session; using MediaBrowser.Server.Implementations.Social; using MediaBrowser.Server.Implementations.Sync; -using MediaBrowser.Server.Implementations.Themes; using MediaBrowser.Server.Implementations.TV; using MediaBrowser.Server.Startup.Common.FFMpeg; using MediaBrowser.Server.Startup.Common.Migrations; @@ -124,7 +122,7 @@ namespace MediaBrowser.Server.Startup.Common /// <returns>IConfigurationManager.</returns> protected override IConfigurationManager GetConfigurationManager() { - return new ServerConfigurationManager(ApplicationPaths, LogManager, XmlSerializer); + return new ServerConfigurationManager(ApplicationPaths, LogManager, XmlSerializer, FileSystemManager); } /// <summary> @@ -314,6 +312,7 @@ namespace MediaBrowser.Server.Startup.Common await base.RunStartupTasks().ConfigureAwait(false); Logger.Info("Core startup complete"); + HttpServer.GlobalResponse = null; Parallel.ForEach(GetExports<IServerEntryPoint>(), entryPoint => { @@ -434,6 +433,7 @@ namespace MediaBrowser.Server.Startup.Common RegisterSingleInstance<ISearchEngine>(() => new SearchEngine(LogManager, LibraryManager, UserManager)); HttpServer = ServerFactory.CreateServer(this, LogManager, ServerConfigurationManager, "Emby", "web/index.html"); + HttpServer.GlobalResponse = LocalizationManager.GetLocalizedString("StartupEmbyServerIsLoading"); RegisterSingleInstance(HttpServer, false); progress.Report(10); @@ -458,7 +458,7 @@ namespace MediaBrowser.Server.Startup.Common var encryptionManager = new EncryptionManager(); RegisterSingleInstance<IEncryptionManager>(encryptionManager); - ConnectManager = new ConnectManager(LogManager.GetLogger("Connect"), ApplicationPaths, JsonSerializer, encryptionManager, HttpClient, this, ServerConfigurationManager, UserManager, ProviderManager, SecurityManager); + ConnectManager = new ConnectManager(LogManager.GetLogger("Connect"), ApplicationPaths, JsonSerializer, encryptionManager, HttpClient, this, ServerConfigurationManager, UserManager, ProviderManager, SecurityManager, FileSystemManager); RegisterSingleInstance(ConnectManager); DeviceManager = new DeviceManager(new DeviceRepository(ApplicationPaths, JsonSerializer, LogManager.GetLogger("DeviceManager"), FileSystemManager), UserManager, FileSystemManager, LibraryMonitor, ConfigurationManager, LogManager.GetLogger("DeviceManager"), NetworkManager); @@ -475,22 +475,19 @@ namespace MediaBrowser.Server.Startup.Common ChannelManager = new ChannelManager(UserManager, DtoService, LibraryManager, LogManager.GetLogger("ChannelManager"), ServerConfigurationManager, FileSystemManager, UserDataManager, JsonSerializer, LocalizationManager, HttpClient); RegisterSingleInstance(ChannelManager); - MediaSourceManager = new MediaSourceManager(ItemRepository, UserManager, LibraryManager, LogManager.GetLogger("MediaSourceManager"), JsonSerializer); + MediaSourceManager = new MediaSourceManager(ItemRepository, UserManager, LibraryManager, LogManager.GetLogger("MediaSourceManager"), JsonSerializer, FileSystemManager); RegisterSingleInstance(MediaSourceManager); SessionManager = new SessionManager(UserDataManager, LogManager.GetLogger("SessionManager"), UserRepository, LibraryManager, UserManager, musicManager, DtoService, ImageProcessor, JsonSerializer, this, HttpClient, AuthenticationRepository, DeviceManager, MediaSourceManager); RegisterSingleInstance(SessionManager); - var appThemeManager = new AppThemeManager(ApplicationPaths, FileSystemManager, JsonSerializer, Logger); - RegisterSingleInstance<IAppThemeManager>(appThemeManager); - var dlnaManager = new DlnaManager(XmlSerializer, FileSystemManager, ApplicationPaths, LogManager.GetLogger("Dlna"), JsonSerializer, this); RegisterSingleInstance<IDlnaManager>(dlnaManager); var connectionManager = new ConnectionManager(dlnaManager, ServerConfigurationManager, LogManager.GetLogger("UpnpConnectionManager"), HttpClient); RegisterSingleInstance<IConnectionManager>(connectionManager); - CollectionManager = new CollectionManager(LibraryManager, FileSystemManager, LibraryMonitor, LogManager.GetLogger("CollectionManager")); + CollectionManager = new CollectionManager(LibraryManager, FileSystemManager, LibraryMonitor, LogManager.GetLogger("CollectionManager"), ProviderManager); RegisterSingleInstance(CollectionManager); PlaylistManager = new PlaylistManager(LibraryManager, FileSystemManager, LibraryMonitor, LogManager.GetLogger("PlaylistManager"), UserManager, ProviderManager); @@ -573,7 +570,7 @@ namespace MediaBrowser.Server.Startup.Common { try { - return new ImageMagickEncoder(LogManager.GetLogger("ImageMagick"), ApplicationPaths, HttpClient); + return new ImageMagickEncoder(LogManager.GetLogger("ImageMagick"), ApplicationPaths, HttpClient, FileSystemManager); } catch (Exception ex) { @@ -598,9 +595,7 @@ namespace MediaBrowser.Server.Startup.Common var info = await new FFMpegDownloader(Logger, ApplicationPaths, HttpClient, ZipClient, FileSystemManager, NativeApp.Environment) .GetFFMpegInfo(NativeApp.Environment, _startupOptions, progress).ConfigureAwait(false); - new FFmpegValidator(Logger, ApplicationPaths).Validate(info); - - MediaEncoder = new MediaEncoder(LogManager.GetLogger("MediaEncoder"), + var mediaEncoder = new MediaEncoder(LogManager.GetLogger("MediaEncoder"), JsonSerializer, info.EncoderPath, info.ProbePath, @@ -614,7 +609,17 @@ namespace MediaBrowser.Server.Startup.Common SessionManager, () => SubtitleEncoder, () => MediaSourceManager); + + MediaEncoder = mediaEncoder; RegisterSingleInstance(MediaEncoder); + + Task.Run(() => + { + var result = new FFmpegValidator(Logger, ApplicationPaths, FileSystemManager).Validate(info); + + mediaEncoder.SetAvailableDecoders(result.Item1); + mediaEncoder.SetAvailableEncoders(result.Item2); + }); } /// <summary> @@ -793,7 +798,7 @@ namespace MediaBrowser.Server.Startup.Common SessionManager.AddParts(GetExports<ISessionControllerFactory>()); - ChannelManager.AddParts(GetExports<IChannel>(), GetExports<IChannelFactory>()); + ChannelManager.AddParts(GetExports<IChannel>()); MediaSourceManager.AddParts(GetExports<IMediaSourceProvider>()); @@ -851,9 +856,9 @@ namespace MediaBrowser.Server.Startup.Common if (generateCertificate) { - if (!File.Exists(certPath)) + if (!FileSystemManager.FileExists(certPath)) { - Directory.CreateDirectory(Path.GetDirectoryName(certPath)); + FileSystemManager.CreateDirectory(Path.GetDirectoryName(certPath)); try { diff --git a/MediaBrowser.Server.Startup.Common/Browser/BrowserLauncher.cs b/MediaBrowser.Server.Startup.Common/Browser/BrowserLauncher.cs index 3eeb072a8..a4504f25a 100644 --- a/MediaBrowser.Server.Startup.Common/Browser/BrowserLauncher.cs +++ b/MediaBrowser.Server.Startup.Common/Browser/BrowserLauncher.cs @@ -24,15 +24,6 @@ namespace MediaBrowser.Server.Startup.Common.Browser } /// <summary> - /// Opens the github. - /// </summary> - /// <param name="logger">The logger.</param> - public static void OpenGithub(ILogger logger) - { - OpenUrl("https://github.com/MediaBrowser/MediaBrowser", logger); - } - - /// <summary> /// Opens the community. /// </summary> /// <param name="logger">The logger.</param> @@ -62,18 +53,6 @@ namespace MediaBrowser.Server.Startup.Common.Browser } /// <summary> - /// Opens the swagger. - /// </summary> - /// <param name="appHost">The app host.</param> - /// <param name="logger">The logger.</param> - public static void OpenSwagger(IServerApplicationHost appHost, ILogger logger) - { - var url = appHost.GetLocalApiUrl("localhost") + "/swagger-ui/index.html"; - - OpenUrl(url, logger); - } - - /// <summary> /// Opens the URL. /// </summary> /// <param name="url">The URL.</param> diff --git a/MediaBrowser.Server.Startup.Common/FFMpeg/FFMpegDownloadInfo.cs b/MediaBrowser.Server.Startup.Common/FFMpeg/FFMpegDownloadInfo.cs index 2910479ef..42fc73488 100644 --- a/MediaBrowser.Server.Startup.Common/FFMpeg/FFMpegDownloadInfo.cs +++ b/MediaBrowser.Server.Startup.Common/FFMpeg/FFMpegDownloadInfo.cs @@ -33,7 +33,7 @@ namespace MediaBrowser.Server.Startup.Common.FFMpeg case OperatingSystem.Linux: info.ArchiveType = "7z"; - info.Version = "20150717"; + info.Version = "20150917"; break; case OperatingSystem.Osx: @@ -42,7 +42,7 @@ namespace MediaBrowser.Server.Startup.Common.FFMpeg switch (environment.SystemArchitecture) { case Architecture.X86_X64: - info.Version = "20150827"; + info.Version = "20150917"; break; case Architecture.X86: info.Version = "20150110"; @@ -54,7 +54,7 @@ namespace MediaBrowser.Server.Startup.Common.FFMpeg info.FFMpegFilename = "ffmpeg.exe"; info.FFProbeFilename = "ffprobe.exe"; - info.Version = "20150717"; + info.Version = "20150918"; info.ArchiveType = "7z"; switch (environment.SystemArchitecture) @@ -83,14 +83,14 @@ namespace MediaBrowser.Server.Startup.Common.FFMpeg case Architecture.X86_X64: return new[] { - "http://ffmpeg.zeranoe.com/builds/win64/static/ffmpeg-20150717-git-8250943-win64-static.7z", - "https://github.com/MediaBrowser/Emby.Resources/raw/master/ffmpeg/windows/ffmpeg-20150901-git-b54e03c-win64-static.7z" + "https://github.com/MediaBrowser/Emby.Resources/raw/master/ffmpeg/windows/ffmpeg-20150918-win64.7z", + "http://ffmpeg.zeranoe.com/builds/win64/static/ffmpeg-20150916-git-cbbd906-win64-static.7z" }; case Architecture.X86: return new[] { - "http://ffmpeg.zeranoe.com/builds/win32/static/ffmpeg-20150717-git-8250943-win32-static.7z", - "https://github.com/MediaBrowser/Emby.Resources/raw/master/ffmpeg/windows/ffmpeg-20150901-git-b54e03c-win32-static.7z" + "https://github.com/MediaBrowser/Emby.Resources/raw/master/ffmpeg/windows/ffmpeg-20150918-win32.7z", + "http://ffmpeg.zeranoe.com/builds/win32/static/ffmpeg-20150916-git-cbbd906-win32-static.7z" }; } break; @@ -102,7 +102,7 @@ namespace MediaBrowser.Server.Startup.Common.FFMpeg case Architecture.X86_X64: return new[] { - "https://github.com/MediaBrowser/Emby.Resources/raw/master/ffmpeg/osx/ffmpeg-x64-2.7.2.7z" + "https://github.com/MediaBrowser/Emby.Resources/raw/master/ffmpeg/osx/ffmpeg-x64-2.8.0.7z" }; case Architecture.X86: return new[] @@ -119,12 +119,12 @@ namespace MediaBrowser.Server.Startup.Common.FFMpeg case Architecture.X86_X64: return new[] { - "https://github.com/MediaBrowser/Emby.Resources/raw/master/ffmpeg/linux/ffmpeg-2.7.1-64bit-static.7z" + "https://github.com/MediaBrowser/Emby.Resources/raw/master/ffmpeg/linux/ffmpeg-2.8.0-64bit-static.7z" }; case Architecture.X86: return new[] { - "https://github.com/MediaBrowser/Emby.Resources/raw/master/ffmpeg/linux/ffmpeg-2.7.1-32bit-static.7z" + "https://github.com/MediaBrowser/Emby.Resources/raw/master/ffmpeg/linux/ffmpeg-2.8.0-32bit-static.7z" }; } break; diff --git a/MediaBrowser.Server.Startup.Common/FFMpeg/FFMpegDownloader.cs b/MediaBrowser.Server.Startup.Common/FFMpeg/FFMpegDownloader.cs index fe7cd943a..16864898a 100644 --- a/MediaBrowser.Server.Startup.Common/FFMpeg/FFMpegDownloader.cs +++ b/MediaBrowser.Server.Startup.Common/FFMpeg/FFMpegDownloader.cs @@ -78,11 +78,11 @@ namespace MediaBrowser.Server.Startup.Common.FFMpeg Version = version }; - Directory.CreateDirectory(versionedDirectoryPath); + _fileSystem.CreateDirectory(versionedDirectoryPath); var excludeFromDeletions = new List<string> { versionedDirectoryPath }; - if (!File.Exists(info.ProbePath) || !File.Exists(info.EncoderPath)) + if (!_fileSystem.FileExists(info.ProbePath) || !_fileSystem.FileExists(info.EncoderPath)) { // ffmpeg not present. See if there's an older version we can start with var existingVersion = GetExistingVersion(info, rootEncoderPath); @@ -218,7 +218,7 @@ namespace MediaBrowser.Server.Startup.Common.FFMpeg var tempFolder = Path.Combine(_appPaths.TempDirectory, Guid.NewGuid().ToString()); - Directory.CreateDirectory(tempFolder); + _fileSystem.CreateDirectory(tempFolder); try { @@ -237,7 +237,7 @@ namespace MediaBrowser.Server.Startup.Common.FFMpeg })) { var targetFile = Path.Combine(targetFolder, Path.GetFileName(file)); - File.Copy(file, targetFile, true); + _fileSystem.CopyFile(file, targetFile, true); SetFilePermissions(targetFile); } } @@ -301,13 +301,13 @@ namespace MediaBrowser.Server.Startup.Common.FFMpeg { var fontsDirectory = Path.Combine(targetPath, "fonts"); - Directory.CreateDirectory(fontsDirectory); + _fileSystem.CreateDirectory(fontsDirectory); const string fontFilename = "ARIALUNI.TTF"; var fontFile = Path.Combine(fontsDirectory, fontFilename); - if (File.Exists(fontFile)) + if (_fileSystem.FileExists(fontFile)) { await WriteFontConfigFile(fontsDirectory).ConfigureAwait(false); } @@ -350,7 +350,7 @@ namespace MediaBrowser.Server.Startup.Common.FFMpeg { try { - File.Copy(existingFile, Path.Combine(fontsDirectory, fontFilename), true); + _fileSystem.CopyFile(existingFile, Path.Combine(fontsDirectory, fontFilename), true); return; } catch (IOException ex) @@ -412,7 +412,7 @@ namespace MediaBrowser.Server.Startup.Common.FFMpeg const string fontConfigFilename = "fonts.conf"; var fontConfigFile = Path.Combine(fontsDirectory, fontConfigFilename); - if (!File.Exists(fontConfigFile)) + if (!_fileSystem.FileExists(fontConfigFile)) { var contents = string.Format("<?xml version=\"1.0\"?><fontconfig><dir>{0}</dir><alias><family>Arial</family><prefer>Arial Unicode MS</prefer></alias></fontconfig>", fontsDirectory); diff --git a/MediaBrowser.Server.Startup.Common/FFMpeg/FFmpegValidator.cs b/MediaBrowser.Server.Startup.Common/FFMpeg/FFmpegValidator.cs index 124a7f74b..d3388f47e 100644 --- a/MediaBrowser.Server.Startup.Common/FFMpeg/FFmpegValidator.cs +++ b/MediaBrowser.Server.Startup.Common/FFMpeg/FFmpegValidator.cs @@ -6,6 +6,8 @@ using System.Diagnostics; using System.Globalization; using System.IO; using System.Text; +using MediaBrowser.Common.IO; +using System.Collections.Generic; namespace MediaBrowser.Server.Startup.Common.FFMpeg { @@ -13,78 +15,74 @@ namespace MediaBrowser.Server.Startup.Common.FFMpeg { private readonly ILogger _logger; private readonly IApplicationPaths _appPaths; + private readonly IFileSystem _fileSystem; - public FFmpegValidator(ILogger logger, IApplicationPaths appPaths) + public FFmpegValidator(ILogger logger, IApplicationPaths appPaths, IFileSystem fileSystem) { _logger = logger; _appPaths = appPaths; + _fileSystem = fileSystem; } - public void Validate(FFMpegInfo info) + public Tuple<List<string>,List<string>> Validate(FFMpegInfo info) { _logger.Info("FFMpeg: {0}", info.EncoderPath); _logger.Info("FFProbe: {0}", info.ProbePath); - string cacheKey; + var decoders = GetDecoders(info.EncoderPath); + var encoders = GetEncoders(info.EncoderPath); - try - { - cacheKey = new FileInfo(info.EncoderPath).Length.ToString(CultureInfo.InvariantCulture).GetMD5().ToString("N"); - } - catch (IOException) - { - // This could happen if ffmpeg is coming from a Path variable and we don't have the full path - cacheKey = Guid.NewGuid().ToString("N"); - } - catch - { - cacheKey = Guid.NewGuid().ToString("N"); - } - - var cachePath = Path.Combine(_appPaths.CachePath, "1" + cacheKey); - - ValidateCodecs(info.EncoderPath, cachePath); + return new Tuple<List<string>, List<string>>(decoders, encoders); } - private void ValidateCodecs(string ffmpegPath, string cachePath) + private List<string> GetDecoders(string ffmpegPath) { - string output = null; + string output = string.Empty; try { - output = File.ReadAllText(cachePath, Encoding.UTF8); + output = GetFFMpegOutput(ffmpegPath, "-decoders"); } catch { - } - if (string.IsNullOrWhiteSpace(output)) + var found = new List<string>(); + var required = new[] { - try - { - output = GetFFMpegOutput(ffmpegPath, "-encoders"); - } - catch - { - return; - } + "h264_qsv", + "mpeg2_qsv", + "vc1_qsv" + }; - try + foreach (var codec in required) + { + var srch = " " + codec + " "; + + if (output.IndexOf(srch, StringComparison.OrdinalIgnoreCase) == -1) { - Directory.CreateDirectory(Path.GetDirectoryName(cachePath)); - File.WriteAllText(cachePath, output, Encoding.UTF8); + _logger.Warn("ffmpeg is missing decoder " + codec); } - catch + else { - + found.Add(codec); } } - ValidateCodecsFromOutput(output); + return found; } - private void ValidateCodecsFromOutput(string output) + private List<string> GetEncoders(string ffmpegPath) { + string output = null; + try + { + output = GetFFMpegOutput(ffmpegPath, "-encoders"); + } + catch + { + } + + var found = new List<string>(); var required = new[] { "libx264", @@ -100,16 +98,21 @@ namespace MediaBrowser.Server.Startup.Common.FFMpeg "srt" }; - foreach (var encoder in required) + foreach (var codec in required) { - var srch = " " + encoder + " "; + var srch = " " + codec + " "; if (output.IndexOf(srch, StringComparison.OrdinalIgnoreCase) == -1) { - _logger.Error("ffmpeg is missing encoder " + encoder); - //throw new ArgumentException("ffmpeg is missing encoder " + encoder); + _logger.Warn("ffmpeg is missing encoder " + codec); + } + else + { + found.Add(codec); } } + + return found; } private string GetFFMpegOutput(string path, string arguments) diff --git a/MediaBrowser.Server.Startup.Common/Migrations/MigrateUserFolders.cs b/MediaBrowser.Server.Startup.Common/Migrations/MigrateUserFolders.cs index cb566d6cf..1dfe4c0b5 100644 --- a/MediaBrowser.Server.Startup.Common/Migrations/MigrateUserFolders.cs +++ b/MediaBrowser.Server.Startup.Common/Migrations/MigrateUserFolders.cs @@ -23,7 +23,8 @@ namespace MediaBrowser.Server.Startup.Common.Migrations { var rootPath = _appPaths.RootFolderPath; - var folders = new DirectoryInfo(rootPath).EnumerateDirectories("*", SearchOption.TopDirectoryOnly).Where(i => !string.Equals(i.Name, "default", StringComparison.OrdinalIgnoreCase)) + var folders = _fileSystem.GetDirectories(rootPath) + .Where(i => !string.Equals(i.Name, "default", StringComparison.OrdinalIgnoreCase)) .ToList(); foreach (var folder in folders) diff --git a/MediaBrowser.Server.Startup.Common/UnhandledExceptionWriter.cs b/MediaBrowser.Server.Startup.Common/UnhandledExceptionWriter.cs index 96c24eaab..804533b9d 100644 --- a/MediaBrowser.Server.Startup.Common/UnhandledExceptionWriter.cs +++ b/MediaBrowser.Server.Startup.Common/UnhandledExceptionWriter.cs @@ -25,7 +25,7 @@ namespace MediaBrowser.Server.Startup.Common _logManager.Flush(); var path = Path.Combine(_appPaths.LogDirectoryPath, "unhandled_" + Guid.NewGuid() + ".txt"); - Directory.CreateDirectory(Path.GetDirectoryName(path)); + Directory.CreateDirectory(Path.GetDirectoryName(path)); var builder = LogHelper.GetLogMessage(ex); @@ -33,7 +33,7 @@ namespace MediaBrowser.Server.Startup.Common Console.WriteLine("UnhandledException"); Console.WriteLine(builder.ToString()); - File.WriteAllText(path, builder.ToString()); + File.WriteAllText(path, builder.ToString()); } } } diff --git a/MediaBrowser.Tests/MediaEncoding/Subtitles/TestSubtitles/unit.srt b/MediaBrowser.Tests/MediaEncoding/Subtitles/TestSubtitles/unit.srt index 5f6e5636e..1ce811bcb 100644 --- a/MediaBrowser.Tests/MediaEncoding/Subtitles/TestSubtitles/unit.srt +++ b/MediaBrowser.Tests/MediaEncoding/Subtitles/TestSubtitles/unit.srt @@ -35,7 +35,7 @@ Unclosed but <b>supported HTML tags are left in, {\i1} SSA italics aren't 9 00:00:36,000 --> 00:00:36,999 -Multiple {\pos(142,120)\b1}SSA tags are stripped +Multiple {\bord-3.7\clip(1,m 50 0 b 100 0 100 100 50 100 b 0 100 0 0 50 0)\pos(142,120)\t(0,500,\fscx100\fscy100)\b1\c&H000000&}SSA tags are stripped 10 00:00:37,000 --> 00:00:37,999 diff --git a/MediaBrowser.WebDashboard/Api/DashboardService.cs b/MediaBrowser.WebDashboard/Api/DashboardService.cs index 69c4417cb..c06f845f1 100644 --- a/MediaBrowser.WebDashboard/Api/DashboardService.cs +++ b/MediaBrowser.WebDashboard/Api/DashboardService.cs @@ -274,8 +274,8 @@ namespace MediaBrowser.WebDashboard.Api private void CopyFile(string src, string dst) { - Directory.CreateDirectory(Path.GetDirectoryName(dst)); - File.Copy(src, dst, true); + _fileSystem.CreateDirectory(Path.GetDirectoryName(dst)); + _fileSystem.CopyFile(src, dst, true); } public async Task<object> Get(GetDashboardPackage request) @@ -306,17 +306,17 @@ namespace MediaBrowser.WebDashboard.Api { // Overwrite certain files with cordova specific versions var cordovaVersion = Path.Combine(path, "cordova", "registrationservices.js"); - File.Copy(cordovaVersion, Path.Combine(path, "scripts", "registrationservices.js"), true); - File.Delete(cordovaVersion); + _fileSystem.CopyFile(cordovaVersion, Path.Combine(path, "scripts", "registrationservices.js"), true); + _fileSystem.DeleteFile(cordovaVersion); // Delete things that are unneeded in an attempt to keep the output as trim as possible - Directory.Delete(Path.Combine(path, "css", "images", "tour"), true); - Directory.Delete(Path.Combine(path, "apiclient", "alt"), true); + _fileSystem.DeleteDirectory(Path.Combine(path, "css", "images", "tour"), true); + _fileSystem.DeleteDirectory(Path.Combine(path, "apiclient", "alt"), true); - File.Delete(Path.Combine(path, "thirdparty", "jquerymobile-1.4.5", "jquery.mobile-1.4.5.min.map")); + _fileSystem.DeleteFile(Path.Combine(path, "thirdparty", "jquerymobile-1.4.5", "jquery.mobile-1.4.5.min.map")); - Directory.Delete(Path.Combine(path, "bower_components"), true); - Directory.Delete(Path.Combine(path, "thirdparty", "viblast"), true); + _fileSystem.DeleteDirectory(Path.Combine(path, "bower_components"), true); + _fileSystem.DeleteDirectory(Path.Combine(path, "thirdparty", "viblast"), true); // But we do need this CopyFile(Path.Combine(creator.DashboardUIPath, "bower_components", "webcomponentsjs", "webcomponents-lite.js"), Path.Combine(path, "bower_components", "webcomponentsjs", "webcomponents-lite.js")); @@ -355,7 +355,7 @@ namespace MediaBrowser.WebDashboard.Api { try { - var text = File.ReadAllText(file, Encoding.UTF8); + var text = _fileSystem.ReadAllText(file, Encoding.UTF8); var result = new KristensenCssMinifier().Minify(text, false, Encoding.UTF8); @@ -366,7 +366,7 @@ namespace MediaBrowser.WebDashboard.Api else { text = result.MinifiedContent; - File.WriteAllText(file, text, Encoding.UTF8); + _fileSystem.WriteAllText(file, text, Encoding.UTF8); } } catch (Exception ex) @@ -382,7 +382,7 @@ namespace MediaBrowser.WebDashboard.Api { try { - var text = File.ReadAllText(file, Encoding.UTF8); + var text = _fileSystem.ReadAllText(file, Encoding.UTF8); var result = new CrockfordJsMinifier().Minify(text, false, Encoding.UTF8); @@ -393,7 +393,7 @@ namespace MediaBrowser.WebDashboard.Api else { text = result.MinifiedContent; - File.WriteAllText(file, text, Encoding.UTF8); + _fileSystem.WriteAllText(file, text, Encoding.UTF8); } } catch (Exception ex) @@ -422,7 +422,7 @@ namespace MediaBrowser.WebDashboard.Api foreach (var file in excludeFiles) { - File.Delete(Path.Combine(destination, file)); + _fileSystem.DeleteFile(Path.Combine(destination, file)); } } @@ -449,17 +449,17 @@ namespace MediaBrowser.WebDashboard.Api private void CopyDirectory(string source, string destination) { - Directory.CreateDirectory(destination); + _fileSystem.CreateDirectory(destination); //Now Create all of the directories foreach (string dirPath in Directory.GetDirectories(source, "*", SearchOption.AllDirectories)) - Directory.CreateDirectory(dirPath.Replace(source, destination)); + _fileSystem.CreateDirectory(dirPath.Replace(source, destination)); //Copy all the files & Replaces any files with the same name foreach (string newPath in Directory.GetFiles(source, "*.*", SearchOption.AllDirectories)) - File.Copy(newPath, newPath.Replace(source, destination), true); + _fileSystem.CopyFile(newPath, newPath.Replace(source, destination), true); } } diff --git a/MediaBrowser.WebDashboard/Api/PackageCreator.cs b/MediaBrowser.WebDashboard/Api/PackageCreator.cs index 48ad62bd4..9e9c1f1b9 100644 --- a/MediaBrowser.WebDashboard/Api/PackageCreator.cs +++ b/MediaBrowser.WebDashboard/Api/PackageCreator.cs @@ -9,6 +9,7 @@ using System.IO; using System.Linq; using System.Text; using System.Threading.Tasks; +using MediaBrowser.Controller.Net; using WebMarkupMin.Core; using WebMarkupMin.Core.Minifiers; using WebMarkupMin.Core.Settings; @@ -136,7 +137,7 @@ namespace MediaBrowser.WebDashboard.Api // Don't allow file system access outside of the source folder if (!_fileSystem.ContainsSubPath(rootPath, fullPath)) { - throw new UnauthorizedAccessException(); + throw new SecurityException("Access denied"); } return fullPath; @@ -521,7 +522,6 @@ namespace MediaBrowser.WebDashboard.Api "thirdparty/jquery.unveil-custom.js", "apiclient/logger.js", "apiclient/md5.js", - "apiclient/sha1.js", "apiclient/store.js", "apiclient/device.js", "apiclient/credentials.js", @@ -607,7 +607,6 @@ namespace MediaBrowser.WebDashboard.Api "appsettings.js", "mediaplayer.js", "mediaplayer-video.js", - "nowplayingbar.js", "alphapicker.js", "directorybrowser.js", "collectioneditor.js", @@ -657,7 +656,6 @@ namespace MediaBrowser.WebDashboard.Api "thirdparty/jquerymobile-1.4.5/jquery.mobile.custom.structure.css", "css/site.css", "css/chromecast.css", - "css/nowplayingbar.css", "css/mediaplayer.css", "css/mediaplayer-video.css", "css/librarymenu.css", diff --git a/MediaBrowser.WebDashboard/MediaBrowser.WebDashboard.csproj b/MediaBrowser.WebDashboard/MediaBrowser.WebDashboard.csproj index 8abd591e5..131c7e657 100644 --- a/MediaBrowser.WebDashboard/MediaBrowser.WebDashboard.csproj +++ b/MediaBrowser.WebDashboard/MediaBrowser.WebDashboard.csproj @@ -57,9 +57,8 @@ <Reference Include="ServiceStack.Interfaces">
<HintPath>..\ThirdParty\ServiceStack\ServiceStack.Interfaces.dll</HintPath>
</Reference>
- <Reference Include="WebMarkupMin.Core, Version=0.9.12.0, Culture=neutral, PublicKeyToken=99472178d266584b, processorArchitecture=MSIL">
- <SpecificVersion>False</SpecificVersion>
- <HintPath>..\packages\WebMarkupMin.Core.0.9.12\lib\net40\WebMarkupMin.Core.dll</HintPath>
+ <Reference Include="WebMarkupMin.Core">
+ <HintPath>..\packages\WebMarkupMin.Core.1.0.0\lib\net40\WebMarkupMin.Core.dll</HintPath>
</Reference>
</ItemGroup>
<ItemGroup>
@@ -87,6 +86,24 @@ </ProjectReference>
</ItemGroup>
<ItemGroup>
+ <Content Include="dashboard-ui\apiclient\sync\contentuploader.js">
+ <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
+ </Content>
+ <Content Include="dashboard-ui\apiclient\fileupload.js">
+ <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
+ </Content>
+ <Content Include="dashboard-ui\apiclient\sync\mediasync.js">
+ <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
+ </Content>
+ <Content Include="dashboard-ui\apiclient\sync\multiserversync.js">
+ <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
+ </Content>
+ <Content Include="dashboard-ui\apiclient\sync\offlineusersync.js">
+ <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
+ </Content>
+ <Content Include="dashboard-ui\apiclient\sync\serversync.js">
+ <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
+ </Content>
<Content Include="dashboard-ui\bower_components\fastclick\lib\fastclick.js">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
@@ -144,18 +161,61 @@ <Content Include="dashboard-ui\bower_components\webcomponentsjs\webcomponents.min.js">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
+ <Content Include="dashboard-ui\components\imagedownloader\imagedownloader.js">
+ <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
+ </Content>
+ <Content Include="dashboard-ui\components\imagedownloader\imagedownloader.template.html">
+ <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
+ </Content>
+ <Content Include="dashboard-ui\components\imageuploader\imageuploader.js">
+ <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
+ </Content>
+ <Content Include="dashboard-ui\components\imageuploader\imageuploader.template.html">
+ <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
+ </Content>
+ <Content Include="dashboard-ui\components\metadataeditor\metadataeditor.js">
+ <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
+ </Content>
+ <Content Include="dashboard-ui\components\metadataeditor\metadataeditor.template.html">
+ <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
+ </Content>
+ <Content Include="dashboard-ui\components\paperdialoghelper.js">
+ <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
+ </Content>
<Content Include="dashboard-ui\cordova\android\localsync.js">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="dashboard-ui\cordova\android\logging.js">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
+ <Content Include="dashboard-ui\cordova\android\newapp.js">
+ <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
+ </Content>
+ <Content Include="dashboard-ui\cordova\fileupload.js">
+ <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
+ </Content>
+ <Content Include="dashboard-ui\cordova\ios\backgroundfetch.js">
+ <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
+ </Content>
+ <Content Include="dashboard-ui\cordova\ios\tabbar.js" />
+ <Content Include="dashboard-ui\cordova\ios\vlcplayer.js">
+ <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
+ </Content>
+ <Content Include="dashboard-ui\cordova\localassetmanager.js">
+ <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
+ </Content>
<Content Include="dashboard-ui\cordova\searchmenu.js">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="dashboard-ui\cordova\sharingwidget.js">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
+ <Content Include="dashboard-ui\css\images\ani_equalizer_black.gif">
+ <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
+ </Content>
+ <Content Include="dashboard-ui\css\images\ani_equalizer_white.gif">
+ <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
+ </Content>
<Content Include="dashboard-ui\css\images\clients\androidtv-tile.png">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
@@ -192,6 +252,12 @@ <Content Include="dashboard-ui\css\nowplayingbar.css">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
+ <Content Include="dashboard-ui\components\imageeditor\imageeditor.template.html">
+ <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
+ </Content>
+ <Content Include="dashboard-ui\favorites.html">
+ <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
+ </Content>
<Content Include="dashboard-ui\livetvguideprovider.html">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
@@ -249,6 +315,9 @@ <Content Include="dashboard-ui\scripts\searchmenu.js">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
+ <Content Include="dashboard-ui\scripts\searchpage.js">
+ <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
+ </Content>
<Content Include="dashboard-ui\scripts\secondaryitems.js">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
@@ -270,12 +339,18 @@ <Content Include="dashboard-ui\scripts\wizardlivetvtuner.js">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
+ <Content Include="dashboard-ui\search.html">
+ <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
+ </Content>
<Content Include="dashboard-ui\secondaryitems.html">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="dashboard-ui\shared.html">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
+ <Content Include="dashboard-ui\components\subtitleeditor\subtitleeditor.template.html">
+ <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
+ </Content>
<Content Include="dashboard-ui\themes\android.css">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
@@ -300,9 +375,6 @@ <Content Include="dashboard-ui\cordova\android\appstorage.js">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
- <Content Include="dashboard-ui\cordova\android\localassetmanager.js">
- <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
- </Content>
<Content Include="dashboard-ui\cordova\android\mediasession.js">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
@@ -330,6 +402,33 @@ <Content Include="dashboard-ui\thirdparty\emby-icons.html">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
+ <Content Include="dashboard-ui\thirdparty\jquerymobile-1.4.5\jqm.checkbox.css">
+ <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
+ </Content>
+ <Content Include="dashboard-ui\thirdparty\jquerymobile-1.4.5\jqm.collapsible.css">
+ <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
+ </Content>
+ <Content Include="dashboard-ui\thirdparty\jquerymobile-1.4.5\jqm.collapsible.js">
+ <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
+ </Content>
+ <Content Include="dashboard-ui\thirdparty\jquerymobile-1.4.5\jqm.controlgroup.css">
+ <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
+ </Content>
+ <Content Include="dashboard-ui\thirdparty\jquerymobile-1.4.5\jqm.controlgroup.js">
+ <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
+ </Content>
+ <Content Include="dashboard-ui\thirdparty\jquerymobile-1.4.5\jqm.listview.css">
+ <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
+ </Content>
+ <Content Include="dashboard-ui\thirdparty\jquerymobile-1.4.5\jqm.listview.js">
+ <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
+ </Content>
+ <Content Include="dashboard-ui\thirdparty\jquerymobile-1.4.5\jqm.panel.css">
+ <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
+ </Content>
+ <Content Include="dashboard-ui\thirdparty\jquerymobile-1.4.5\jqm.panel.js">
+ <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
+ </Content>
<Content Include="dashboard-ui\thirdparty\jquerymobile-1.4.5\jqm.popup.css">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
@@ -342,33 +441,24 @@ <Content Include="dashboard-ui\thirdparty\jquerymobile-1.4.5\jqm.slider.js">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
- <Content Include="dashboard-ui\thirdparty\jquerymobile-1.4.5\jquery.mobile.custom.icons.css">
+ <Content Include="dashboard-ui\thirdparty\jquerymobile-1.4.5\jqm.checkbox.js">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
- <Content Include="dashboard-ui\thirdparty\jquerymobile-1.4.5\jquery.mobile.custom.js">
+ <Content Include="dashboard-ui\thirdparty\jquerymobile-1.4.5\jquery.mobile.custom.icons.css">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
- <Content Include="dashboard-ui\thirdparty\jquerymobile-1.4.5\jquery.mobile.custom.min.js">
+ <Content Include="dashboard-ui\thirdparty\jquerymobile-1.4.5\jquery.mobile.custom.js">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="dashboard-ui\thirdparty\jquerymobile-1.4.5\jquery.mobile.custom.structure.css">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
- <Content Include="dashboard-ui\thirdparty\jquerymobile-1.4.5\jquery.mobile.custom.structure.min.css">
- <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
- </Content>
<Content Include="dashboard-ui\thirdparty\jquerymobile-1.4.5\jquery.mobile.custom.theme.css">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
- <Content Include="dashboard-ui\thirdparty\jquerymobile-1.4.5\jquery.mobile.custom.theme.min.css">
- <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
- </Content>
<Content Include="dashboard-ui\thirdparty\paper-button-style.css">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
- <Content Include="dashboard-ui\thirdparty\paper-ie10.css">
- <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
- </Content>
<Content Include="dashboard-ui\thirdparty\social-share-kit-1.0.4\.gitignore" />
<Content Include="dashboard-ui\thirdparty\social-share-kit-1.0.4\dist\css\social-share-kit.css">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
@@ -398,6 +488,9 @@ <Content Include="dashboard-ui\tvproviders\schedulesdirect.template.html">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
+ <Content Include="dashboard-ui\voice\textprocessor-en-us.js">
+ <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
+ </Content>
<Content Include="dashboard-ui\voice\voice.css">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
@@ -506,9 +599,6 @@ <Content Include="dashboard-ui\apiclient\ajax.js">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
- <Content Include="dashboard-ui\apiclient\alt\ajax.js">
- <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
- </Content>
<Content Include="dashboard-ui\apiclient\alt\bean.js">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
@@ -521,9 +611,6 @@ <Content Include="dashboard-ui\cordova\android\androidcredentials.js">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
- <Content Include="dashboard-ui\cordova\android\filesystem.js">
- <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
- </Content>
<Content Include="dashboard-ui\cordova\android\iap.js">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
@@ -537,9 +624,6 @@ <Content Include="dashboard-ui\cordova\externalplayer.js">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
- <Content Include="dashboard-ui\cordova\filesystem.js">
- <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
- </Content>
<Content Include="dashboard-ui\cordova\generaldevice.js" />
<Content Include="dashboard-ui\cordova\iap.js">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
@@ -784,9 +868,6 @@ <Content Include="dashboard-ui\metadatasubtitles.html">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
- <Content Include="dashboard-ui\musicalbumartists.html">
- <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
- </Content>
<Content Include="dashboard-ui\collections.html">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
@@ -880,9 +961,6 @@ <Content Include="dashboard-ui\edititemmetadata.html">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
- <Content Include="dashboard-ui\episodes.html">
- <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
- </Content>
<Content Include="dashboard-ui\gamegenres.html">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
@@ -958,10 +1036,7 @@ <Content Include="dashboard-ui\scripts\dlnasettings.js">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
- <Content Include="dashboard-ui\scripts\editcollectionitems.js">
- <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
- </Content>
- <Content Include="dashboard-ui\scripts\edititemsubtitles.js">
+ <Content Include="dashboard-ui\components\subtitleeditor\subtitleeditor.js">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="dashboard-ui\scripts\encodingsettings.js">
@@ -1087,9 +1162,6 @@ <Content Include="dashboard-ui\scripts\livetvsuggested.js">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
- <Content Include="dashboard-ui\scripts\serversecurity.js">
- <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
- </Content>
<Content Include="dashboard-ui\scripts\sync.js">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
@@ -1111,9 +1183,6 @@ <Content Include="dashboard-ui\scripts\wizardsettings.js">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
- <Content Include="dashboard-ui\serversecurity.html">
- <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
- </Content>
<Content Include="dashboard-ui\apiclient\connectionmanager.js">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
@@ -1135,9 +1204,6 @@ <Content Include="dashboard-ui\thirdparty\cast_sender.js">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
- <Content Include="dashboard-ui\thirdparty\filesystem.js">
- <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
- </Content>
<Content Include="dashboard-ui\thirdparty\fontawesome\css\font-awesome.css">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
@@ -1756,9 +1822,6 @@ <Content Include="dashboard-ui\thirdparty\jquerymobile-1.4.5\images\icons-svg\video-white.svg">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
- <Content Include="dashboard-ui\thirdparty\jquerymobile-1.4.5\jquery.mobile-1.4.5.min.css">
- <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
- </Content>
<Content Include="dashboard-ui\livetv.html">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
@@ -1780,9 +1843,6 @@ <Content Include="dashboard-ui\thirdparty\jstree\themes\default\throbber.gif">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
- <Content Include="dashboard-ui\tvupcoming.html">
- <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
- </Content>
<Content Include="dashboard-ui\userlibraryaccess.html">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
@@ -1840,19 +1900,7 @@ <Content Include="dashboard-ui\movies.html">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
- <Content Include="dashboard-ui\musicalbums.html">
- <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
- </Content>
- <Content Include="dashboard-ui\musicartists.html">
- <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
- </Content>
- <Content Include="dashboard-ui\musicgenres.html">
- <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
- </Content>
- <Content Include="dashboard-ui\musicrecommended.html">
- <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
- </Content>
- <Content Include="dashboard-ui\musicvideos.html">
+ <Content Include="dashboard-ui\music.html">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="dashboard-ui\scripts\alphapicker.js">
@@ -1861,7 +1909,7 @@ <Content Include="dashboard-ui\scripts\directorybrowser.js">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
- <Content Include="dashboard-ui\scripts\edititemimages.js">
+ <Content Include="dashboard-ui\components\imageeditor\imageeditor.js">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="dashboard-ui\scripts\edititemmetadata.js">
@@ -1873,9 +1921,6 @@ <Content Include="dashboard-ui\scripts\musicrecommended.js">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
- <Content Include="dashboard-ui\scripts\musicvideos.js">
- <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
- </Content>
<Content Include="dashboard-ui\scripts\notifications.js">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
@@ -1888,9 +1933,6 @@ <Content Include="dashboard-ui\scripts\tvlatest.js">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
- <Content Include="dashboard-ui\songs.html">
- <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
- </Content>
<Content Include="dashboard-ui\scripts\moviecollections.js">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
@@ -1980,22 +2022,7 @@ <Content Include="dashboard-ui\scripts\tvstudios.js">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
- <Content Include="dashboard-ui\tvgenres.html">
- <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
- </Content>
- <Content Include="dashboard-ui\tvlatest.html">
- <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
- </Content>
- <Content Include="dashboard-ui\tvpeople.html">
- <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
- </Content>
- <Content Include="dashboard-ui\tvrecommended.html">
- <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
- </Content>
- <Content Include="dashboard-ui\tvshows.html">
- <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
- </Content>
- <Content Include="dashboard-ui\tvstudios.html">
+ <Content Include="dashboard-ui\tv.html">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="dashboard-ui\userprofiles.html">
@@ -2652,6 +2679,12 @@ <Content Include="dashboard-ui\strings\javascript\zh-TW.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
+ <None Include="dashboard-ui\strings\html\zh-HK.json">
+ <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
+ </None>
+ <None Include="dashboard-ui\strings\javascript\zh-HK.json">
+ <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
+ </None>
<None Include="dashboard-ui\thirdparty\fontawesome\css\font-awesome.css.map">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
diff --git a/MediaBrowser.WebDashboard/packages.config b/MediaBrowser.WebDashboard/packages.config index 48f68789b..cb5a32347 100644 --- a/MediaBrowser.WebDashboard/packages.config +++ b/MediaBrowser.WebDashboard/packages.config @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="utf-8"?> <packages> <package id="MediaBrowser.ApiClient.Javascript" version="3.0.249" targetFramework="net45" /> - <package id="WebMarkupMin.Core" version="0.9.12" targetFramework="net45" /> + <package id="WebMarkupMin.Core" version="1.0.0" targetFramework="net45" /> </packages>
\ No newline at end of file diff --git a/MediaBrowser.XbmcMetadata/Parsers/BaseNfoParser.cs b/MediaBrowser.XbmcMetadata/Parsers/BaseNfoParser.cs index ac39d9d98..c5184ec3d 100644 --- a/MediaBrowser.XbmcMetadata/Parsers/BaseNfoParser.cs +++ b/MediaBrowser.XbmcMetadata/Parsers/BaseNfoParser.cs @@ -394,11 +394,7 @@ namespace MediaBrowser.XbmcMetadata.Parsers { var val = reader.ReadElementContentAsString(); - var hasLanguage = item as IHasPreferredMetadataLanguage; - if (hasLanguage != null) - { - hasLanguage.PreferredMetadataLanguage = val; - } + item.PreferredMetadataLanguage = val; break; } @@ -407,11 +403,7 @@ namespace MediaBrowser.XbmcMetadata.Parsers { var val = reader.ReadElementContentAsString(); - var hasLanguage = item as IHasPreferredMetadataLanguage; - if (hasLanguage != null) - { - hasLanguage.PreferredMetadataCountryCode = val; - } + item.PreferredMetadataCountryCode = val; break; } diff --git a/MediaBrowser.XbmcMetadata/Savers/BaseNfoSaver.cs b/MediaBrowser.XbmcMetadata/Savers/BaseNfoSaver.cs index b6f497a76..8876e1956 100644 --- a/MediaBrowser.XbmcMetadata/Savers/BaseNfoSaver.cs +++ b/MediaBrowser.XbmcMetadata/Savers/BaseNfoSaver.cs @@ -199,6 +199,8 @@ namespace MediaBrowser.XbmcMetadata.Savers private void SaveToFile(Stream stream, string path) { + FileSystem.CreateDirectory(Path.GetDirectoryName(path)); + var file = new FileInfo(path); var wasHidden = false; @@ -597,17 +599,13 @@ namespace MediaBrowser.XbmcMetadata.Savers writer.WriteElementString("tvcomid", tvcom); } - var hasLanguage = item as IHasPreferredMetadataLanguage; - if (hasLanguage != null) + if (!string.IsNullOrEmpty(item.PreferredMetadataLanguage)) { - if (!string.IsNullOrEmpty(hasLanguage.PreferredMetadataLanguage)) - { - writer.WriteElementString("language", hasLanguage.PreferredMetadataLanguage); - } - if (!string.IsNullOrEmpty(hasLanguage.PreferredMetadataCountryCode)) - { - writer.WriteElementString("countrycode", hasLanguage.PreferredMetadataCountryCode); - } + writer.WriteElementString("language", item.PreferredMetadataLanguage); + } + if (!string.IsNullOrEmpty(item.PreferredMetadataCountryCode)) + { + writer.WriteElementString("countrycode", item.PreferredMetadataCountryCode); } if (item.PremiereDate.HasValue && !(item is Episode)) diff --git a/MediaBrowser.XbmcMetadata/Savers/SeasonNfoSaver.cs b/MediaBrowser.XbmcMetadata/Savers/SeasonNfoSaver.cs index 49ace7702..ccac720fc 100644 --- a/MediaBrowser.XbmcMetadata/Savers/SeasonNfoSaver.cs +++ b/MediaBrowser.XbmcMetadata/Savers/SeasonNfoSaver.cs @@ -39,7 +39,7 @@ namespace MediaBrowser.XbmcMetadata.Savers return false; } - return updateType >= MinimumUpdateType || (updateType >= ItemUpdateType.MetadataImport && File.Exists(GetSavePath(item))); + return updateType >= MinimumUpdateType || (updateType >= ItemUpdateType.MetadataImport && FileSystem.FileExists(GetSavePath(item))); } protected override void WriteCustomElements(IHasMetadata item, XmlWriter writer) diff --git a/Nuget/MediaBrowser.Common.Internal.nuspec b/Nuget/MediaBrowser.Common.Internal.nuspec index f92e05492..37fc18d93 100644 --- a/Nuget/MediaBrowser.Common.Internal.nuspec +++ b/Nuget/MediaBrowser.Common.Internal.nuspec @@ -2,7 +2,7 @@ <package xmlns="http://schemas.microsoft.com/packaging/2011/08/nuspec.xsd"> <metadata> <id>MediaBrowser.Common.Internal</id> - <version>3.0.633</version> + <version>3.0.636</version> <title>MediaBrowser.Common.Internal</title> <authors>Luke</authors> <owners>ebr,Luke,scottisafool</owners> @@ -12,9 +12,9 @@ <description>Contains common components shared by Emby Theater and Emby Server. Not intended for plugin developer consumption.</description> <copyright>Copyright © Emby 2013</copyright> <dependencies> - <dependency id="MediaBrowser.Common" version="3.0.633" /> + <dependency id="MediaBrowser.Common" version="3.0.636" /> <dependency id="NLog" version="3.2.1" /> - <dependency id="SimpleInjector" version="2.8.0" /> + <dependency id="SimpleInjector" version="3.0.5" /> </dependencies> </metadata> <files> diff --git a/Nuget/MediaBrowser.Common.nuspec b/Nuget/MediaBrowser.Common.nuspec index 04d4deb7c..854e315ed 100644 --- a/Nuget/MediaBrowser.Common.nuspec +++ b/Nuget/MediaBrowser.Common.nuspec @@ -2,7 +2,7 @@ <package xmlns="http://schemas.microsoft.com/packaging/2011/08/nuspec.xsd"> <metadata> <id>MediaBrowser.Common</id> - <version>3.0.633</version> + <version>3.0.636</version> <title>MediaBrowser.Common</title> <authors>Emby Team</authors> <owners>ebr,Luke,scottisafool</owners> diff --git a/Nuget/MediaBrowser.Model.Signed.nuspec b/Nuget/MediaBrowser.Model.Signed.nuspec index 2a75ac3a8..f0024d238 100644 --- a/Nuget/MediaBrowser.Model.Signed.nuspec +++ b/Nuget/MediaBrowser.Model.Signed.nuspec @@ -2,7 +2,7 @@ <package xmlns="http://schemas.microsoft.com/packaging/2011/08/nuspec.xsd"> <metadata> <id>MediaBrowser.Model.Signed</id> - <version>3.0.633</version> + <version>3.0.636</version> <title>MediaBrowser.Model - Signed Edition</title> <authors>Emby Team</authors> <owners>ebr,Luke,scottisafool</owners> diff --git a/Nuget/MediaBrowser.Server.Core.nuspec b/Nuget/MediaBrowser.Server.Core.nuspec index da8309410..b84dded47 100644 --- a/Nuget/MediaBrowser.Server.Core.nuspec +++ b/Nuget/MediaBrowser.Server.Core.nuspec @@ -2,7 +2,7 @@ <package xmlns="http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd"> <metadata> <id>MediaBrowser.Server.Core</id> - <version>3.0.633</version> + <version>3.0.636</version> <title>Media Browser.Server.Core</title> <authors>Emby Team</authors> <owners>ebr,Luke,scottisafool</owners> @@ -12,7 +12,7 @@ <description>Contains core components required to build plugins for Emby Server.</description> <copyright>Copyright © Emby 2013</copyright> <dependencies> - <dependency id="MediaBrowser.Common" version="3.0.633" /> + <dependency id="MediaBrowser.Common" version="3.0.636" /> <dependency id="Interfaces.IO" version="1.0.0.5" /> </dependencies> </metadata> diff --git a/OpenSubtitlesHandler/MovieHasher.cs b/OpenSubtitlesHandler/MovieHasher.cs index a14bd20f8..8d11d8267 100644 --- a/OpenSubtitlesHandler/MovieHasher.cs +++ b/OpenSubtitlesHandler/MovieHasher.cs @@ -9,17 +9,7 @@ namespace OpenSubtitlesHandler { public class MovieHasher { - public static byte[] ComputeMovieHash(string filename) - { - byte[] result; - using (Stream input = File.OpenRead(filename)) - { - result = ComputeMovieHash(input); - } - return result; - } - - private static byte[] ComputeMovieHash(Stream input) + public static byte[] ComputeMovieHash(Stream input) { long lhash, streamsize; streamsize = input.Length; diff --git a/OpenSubtitlesHandler/Utilities.cs b/OpenSubtitlesHandler/Utilities.cs index 7f0f93009..4340d4c3f 100644 --- a/OpenSubtitlesHandler/Utilities.cs +++ b/OpenSubtitlesHandler/Utilities.cs @@ -44,7 +44,7 @@ namespace OpenSubtitlesHandler /// <returns>The hash as Hexadecimal string</returns> public static string ComputeHash(string fileName) { - byte[] hash = MovieHasher.ComputeMovieHash(fileName); + byte[] hash = MovieHasher.ComputeMovieHash(File.OpenRead(fileName)); return MovieHasher.ToHexadecimal(hash); } /// <summary> |
