mirror of
https://github.com/jellyfin/jellyfin.git
synced 2025-07-09 03:04:24 -04:00
commit
35778ebc02
@ -5,6 +5,7 @@ using System;
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using CommonIO;
|
||||||
|
|
||||||
namespace Emby.Drawing.Common
|
namespace Emby.Drawing.Common
|
||||||
{
|
{
|
||||||
@ -220,4 +221,4 @@ namespace Emby.Drawing.Common
|
|||||||
throw new ArgumentException(ErrorMessage);
|
throw new ArgumentException(ErrorMessage);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -12,7 +12,6 @@
|
|||||||
<TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
|
<TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
|
||||||
<FileAlignment>512</FileAlignment>
|
<FileAlignment>512</FileAlignment>
|
||||||
<SolutionDir Condition="$(SolutionDir) == '' Or $(SolutionDir) == '*Undefined*'">..\</SolutionDir>
|
<SolutionDir Condition="$(SolutionDir) == '' Or $(SolutionDir) == '*Undefined*'">..\</SolutionDir>
|
||||||
<RestorePackages>true</RestorePackages>
|
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
|
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
|
||||||
<DebugSymbols>true</DebugSymbols>
|
<DebugSymbols>true</DebugSymbols>
|
||||||
@ -32,10 +31,20 @@
|
|||||||
<WarningLevel>4</WarningLevel>
|
<WarningLevel>4</WarningLevel>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
<Reference Include="CommonIO, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL">
|
||||||
|
<SpecificVersion>False</SpecificVersion>
|
||||||
|
<HintPath>..\packages\CommonIO.1.0.0.5\lib\net45\CommonIO.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
<Reference Include="ImageMagickSharp, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL">
|
<Reference Include="ImageMagickSharp, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL">
|
||||||
<SpecificVersion>False</SpecificVersion>
|
<SpecificVersion>False</SpecificVersion>
|
||||||
<HintPath>..\packages\ImageMagickSharp.1.0.0.16\lib\net45\ImageMagickSharp.dll</HintPath>
|
<HintPath>..\packages\ImageMagickSharp.1.0.0.16\lib\net45\ImageMagickSharp.dll</HintPath>
|
||||||
</Reference>
|
</Reference>
|
||||||
|
<Reference Include="Patterns.Logging">
|
||||||
|
<HintPath>..\packages\Patterns.Logging.1.0.0.2\lib\portable-net45+sl4+wp71+win8+wpa81\Patterns.Logging.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" />
|
||||||
<Reference Include="System.Core" />
|
<Reference Include="System.Core" />
|
||||||
<Reference Include="System.Drawing" />
|
<Reference Include="System.Drawing" />
|
||||||
@ -44,11 +53,15 @@
|
|||||||
<Reference Include="Microsoft.CSharp" />
|
<Reference Include="Microsoft.CSharp" />
|
||||||
<Reference Include="System.Data" />
|
<Reference Include="System.Data" />
|
||||||
<Reference Include="System.Xml" />
|
<Reference Include="System.Xml" />
|
||||||
|
<Reference Include="taglib-sharp">
|
||||||
|
<HintPath>..\packages\taglib.2.1.0.0\lib\taglib-sharp.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Compile Include="..\SharedVersion.cs">
|
<Compile Include="..\SharedVersion.cs">
|
||||||
<Link>Properties\SharedVersion.cs</Link>
|
<Link>Properties\SharedVersion.cs</Link>
|
||||||
</Compile>
|
</Compile>
|
||||||
|
<Compile Include="Common\ImageHeader.cs" />
|
||||||
<Compile Include="GDI\DynamicImageHelpers.cs" />
|
<Compile Include="GDI\DynamicImageHelpers.cs" />
|
||||||
<Compile Include="GDI\GDIImageEncoder.cs" />
|
<Compile Include="GDI\GDIImageEncoder.cs" />
|
||||||
<Compile Include="GDI\ImageExtensions.cs" />
|
<Compile Include="GDI\ImageExtensions.cs" />
|
||||||
@ -56,13 +69,13 @@
|
|||||||
<Compile Include="GDI\PlayedIndicatorDrawer.cs" />
|
<Compile Include="GDI\PlayedIndicatorDrawer.cs" />
|
||||||
<Compile Include="GDI\UnplayedCountIndicator.cs" />
|
<Compile Include="GDI\UnplayedCountIndicator.cs" />
|
||||||
<Compile Include="IImageEncoder.cs" />
|
<Compile Include="IImageEncoder.cs" />
|
||||||
<Compile Include="Common\ImageHeader.cs" />
|
|
||||||
<Compile Include="ImageHelpers.cs" />
|
<Compile Include="ImageHelpers.cs" />
|
||||||
<Compile Include="ImageMagick\ImageMagickEncoder.cs" />
|
<Compile Include="ImageMagick\ImageMagickEncoder.cs" />
|
||||||
<Compile Include="ImageMagick\StripCollageBuilder.cs" />
|
<Compile Include="ImageMagick\StripCollageBuilder.cs" />
|
||||||
<Compile Include="ImageProcessor.cs" />
|
<Compile Include="ImageProcessor.cs" />
|
||||||
<Compile Include="ImageMagick\PercentPlayedDrawer.cs" />
|
<Compile Include="ImageMagick\PercentPlayedDrawer.cs" />
|
||||||
<Compile Include="ImageMagick\PlayedIndicatorDrawer.cs" />
|
<Compile Include="ImageMagick\PlayedIndicatorDrawer.cs" />
|
||||||
|
<Compile Include="NullImageEncoder.cs" />
|
||||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||||
<Compile Include="ImageMagick\UnplayedCountIndicator.cs" />
|
<Compile Include="ImageMagick\UnplayedCountIndicator.cs" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
@ -87,6 +100,9 @@
|
|||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<None Include="packages.config" />
|
<None Include="packages.config" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<EmbeddedResource Include="GDI\empty.png" />
|
||||||
|
</ItemGroup>
|
||||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||||
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
|
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
|
||||||
Other similar extension points exist, see Microsoft.Common.targets.
|
Other similar extension points exist, see Microsoft.Common.targets.
|
||||||
|
@ -4,6 +4,7 @@ using System.Drawing;
|
|||||||
using System.Drawing.Drawing2D;
|
using System.Drawing.Drawing2D;
|
||||||
using System.Drawing.Imaging;
|
using System.Drawing.Imaging;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
|
using CommonIO;
|
||||||
|
|
||||||
namespace Emby.Drawing.GDI
|
namespace Emby.Drawing.GDI
|
||||||
{
|
{
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
using MediaBrowser.Common.IO;
|
using MediaBrowser.Controller.Drawing;
|
||||||
using MediaBrowser.Controller.Drawing;
|
|
||||||
using MediaBrowser.Model.Drawing;
|
using MediaBrowser.Model.Drawing;
|
||||||
using MediaBrowser.Model.Logging;
|
using MediaBrowser.Model.Logging;
|
||||||
using System;
|
using System;
|
||||||
@ -8,6 +7,7 @@ using System.Drawing.Drawing2D;
|
|||||||
using System.Drawing.Imaging;
|
using System.Drawing.Imaging;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using CommonIO;
|
||||||
using ImageFormat = MediaBrowser.Model.Drawing.ImageFormat;
|
using ImageFormat = MediaBrowser.Model.Drawing.ImageFormat;
|
||||||
|
|
||||||
namespace Emby.Drawing.GDI
|
namespace Emby.Drawing.GDI
|
||||||
@ -22,7 +22,20 @@ namespace Emby.Drawing.GDI
|
|||||||
_fileSystem = fileSystem;
|
_fileSystem = fileSystem;
|
||||||
_logger = logger;
|
_logger = logger;
|
||||||
|
|
||||||
_logger.Info("GDI image processor initialized");
|
LogInfo();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void LogInfo()
|
||||||
|
{
|
||||||
|
_logger.Info("GDIImageEncoder starting");
|
||||||
|
using (var stream = GetType().Assembly.GetManifestResourceStream(GetType().Namespace + ".empty.png"))
|
||||||
|
{
|
||||||
|
using (var img = Image.FromStream(stream))
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_logger.Info("GDIImageEncoder started");
|
||||||
}
|
}
|
||||||
|
|
||||||
public string[] SupportedInputFormats
|
public string[] SupportedInputFormats
|
||||||
@ -66,7 +79,7 @@ namespace Emby.Drawing.GDI
|
|||||||
{
|
{
|
||||||
using (var croppedImage = image.CropWhitespace())
|
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))
|
using (var outputStream = _fileSystem.GetFileStream(outputPath, FileMode.Create, FileAccess.Write, FileShare.Read, false))
|
||||||
{
|
{
|
||||||
@ -120,7 +133,7 @@ namespace Emby.Drawing.GDI
|
|||||||
|
|
||||||
var outputFormat = GetOutputFormat(originalImage, selectedOutputFormat);
|
var outputFormat = GetOutputFormat(originalImage, selectedOutputFormat);
|
||||||
|
|
||||||
Directory.CreateDirectory(Path.GetDirectoryName(cacheFilePath));
|
_fileSystem.CreateDirectory(Path.GetDirectoryName(cacheFilePath));
|
||||||
|
|
||||||
// Save to the cache location
|
// Save to the cache location
|
||||||
using (var cacheFileStream = _fileSystem.GetFileStream(cacheFilePath, FileMode.Create, FileAccess.Write, FileShare.Read, false))
|
using (var cacheFileStream = _fileSystem.GetFileStream(cacheFilePath, FileMode.Create, FileAccess.Write, FileShare.Read, false))
|
||||||
@ -252,5 +265,15 @@ namespace Emby.Drawing.GDI
|
|||||||
{
|
{
|
||||||
get { return "GDI"; }
|
get { return "GDI"; }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public bool SupportsImageCollageCreation
|
||||||
|
{
|
||||||
|
get { return true; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool SupportsImageEncoding
|
||||||
|
{
|
||||||
|
get { return true; }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
BIN
Emby.Drawing/GDI/empty.png
Normal file
BIN
Emby.Drawing/GDI/empty.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 158 B |
@ -17,12 +17,6 @@ namespace Emby.Drawing
|
|||||||
/// <value>The supported output formats.</value>
|
/// <value>The supported output formats.</value>
|
||||||
ImageFormat[] SupportedOutputFormats { get; }
|
ImageFormat[] SupportedOutputFormats { get; }
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the size of the image.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="path">The path.</param>
|
|
||||||
/// <returns>ImageSize.</returns>
|
|
||||||
ImageSize GetImageSize(string path);
|
|
||||||
/// <summary>
|
|
||||||
/// Crops the white space.
|
/// Crops the white space.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="inputPath">The input path.</param>
|
/// <param name="inputPath">The input path.</param>
|
||||||
@ -49,5 +43,17 @@ namespace Emby.Drawing
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <value>The name.</value>
|
/// <value>The name.</value>
|
||||||
string Name { get; }
|
string Name { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets a value indicating whether [supports image collage creation].
|
||||||
|
/// </summary>
|
||||||
|
/// <value><c>true</c> if [supports image collage creation]; otherwise, <c>false</c>.</value>
|
||||||
|
bool SupportsImageCollageCreation { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets a value indicating whether [supports image encoding].
|
||||||
|
/// </summary>
|
||||||
|
/// <value><c>true</c> if [supports image encoding]; otherwise, <c>false</c>.</value>
|
||||||
|
bool SupportsImageEncoding { get; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -8,6 +8,7 @@ using MediaBrowser.Model.Logging;
|
|||||||
using System;
|
using System;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using CommonIO;
|
||||||
|
|
||||||
namespace Emby.Drawing.ImageMagick
|
namespace Emby.Drawing.ImageMagick
|
||||||
{
|
{
|
||||||
@ -16,14 +17,16 @@ namespace Emby.Drawing.ImageMagick
|
|||||||
private readonly ILogger _logger;
|
private readonly ILogger _logger;
|
||||||
private readonly IApplicationPaths _appPaths;
|
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;
|
_logger = logger;
|
||||||
_appPaths = appPaths;
|
_appPaths = appPaths;
|
||||||
_httpClient = httpClient;
|
_httpClient = httpClient;
|
||||||
|
_fileSystem = fileSystem;
|
||||||
|
|
||||||
LogImageMagickVersion();
|
LogVersion();
|
||||||
}
|
}
|
||||||
|
|
||||||
public string[] SupportedInputFormats
|
public string[] SupportedInputFormats
|
||||||
@ -64,7 +67,7 @@ namespace Emby.Drawing.ImageMagick
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void LogImageMagickVersion()
|
private void LogVersion()
|
||||||
{
|
{
|
||||||
_logger.Info("ImageMagick version: " + Wand.VersionString);
|
_logger.Info("ImageMagick version: " + Wand.VersionString);
|
||||||
TestWebp();
|
TestWebp();
|
||||||
@ -77,16 +80,16 @@ namespace Emby.Drawing.ImageMagick
|
|||||||
try
|
try
|
||||||
{
|
{
|
||||||
var tmpPath = Path.Combine(_appPaths.TempDirectory, Guid.NewGuid() + ".webp");
|
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)))
|
using (var wand = new MagickWand(1, 1, new PixelWand("none", 1)))
|
||||||
{
|
{
|
||||||
wand.SaveImage(tmpPath);
|
wand.SaveImage(tmpPath);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch
|
||||||
{
|
{
|
||||||
_logger.ErrorException("Error loading webp: ", ex);
|
//_logger.ErrorException("Error loading webp: ", ex);
|
||||||
_webpAvailable = false;
|
_webpAvailable = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -100,6 +103,7 @@ namespace Emby.Drawing.ImageMagick
|
|||||||
wand.CurrentImage.TrimImage(10);
|
wand.CurrentImage.TrimImage(10);
|
||||||
wand.SaveImage(outputPath);
|
wand.SaveImage(outputPath);
|
||||||
}
|
}
|
||||||
|
SaveDelay();
|
||||||
}
|
}
|
||||||
|
|
||||||
public ImageSize GetImageSize(string path)
|
public ImageSize GetImageSize(string path)
|
||||||
@ -159,6 +163,7 @@ namespace Emby.Drawing.ImageMagick
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
SaveDelay();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -181,14 +186,14 @@ namespace Emby.Drawing.ImageMagick
|
|||||||
{
|
{
|
||||||
var currentImageSize = new ImageSize(imageWidth, imageHeight);
|
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);
|
Task.WaitAll(task);
|
||||||
}
|
}
|
||||||
else if (options.UnplayedCount.HasValue)
|
else if (options.UnplayedCount.HasValue)
|
||||||
{
|
{
|
||||||
var currentImageSize = new ImageSize(imageWidth, imageHeight);
|
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)
|
if (options.PercentPlayed > 0)
|
||||||
@ -209,16 +214,25 @@ namespace Emby.Drawing.ImageMagick
|
|||||||
|
|
||||||
if (ratio >= 1.4)
|
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)
|
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
|
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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
SaveDelay();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void SaveDelay()
|
||||||
|
{
|
||||||
|
// For some reason the images are not always getting released right away
|
||||||
|
var task = Task.Delay(300);
|
||||||
|
Task.WaitAll(task);
|
||||||
}
|
}
|
||||||
|
|
||||||
public string Name
|
public string Name
|
||||||
@ -240,5 +254,15 @@ namespace Emby.Drawing.ImageMagick
|
|||||||
throw new ObjectDisposedException(GetType().Name);
|
throw new ObjectDisposedException(GetType().Name);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public bool SupportsImageCollageCreation
|
||||||
|
{
|
||||||
|
get { return true; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool SupportsImageEncoding
|
||||||
|
{
|
||||||
|
get { return true; }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -5,6 +5,8 @@ using MediaBrowser.Model.Drawing;
|
|||||||
using System;
|
using System;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
using CommonIO;
|
||||||
|
using MediaBrowser.Common.IO;
|
||||||
|
|
||||||
namespace Emby.Drawing.ImageMagick
|
namespace Emby.Drawing.ImageMagick
|
||||||
{
|
{
|
||||||
@ -14,12 +16,14 @@ namespace Emby.Drawing.ImageMagick
|
|||||||
private const int OffsetFromTopRightCorner = 38;
|
private const int OffsetFromTopRightCorner = 38;
|
||||||
|
|
||||||
private readonly IApplicationPaths _appPaths;
|
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;
|
_appPaths = appPaths;
|
||||||
_iHttpClient = iHttpClient;
|
_iHttpClient = iHttpClient;
|
||||||
|
_fileSystem = fileSystem;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task DrawPlayedIndicator(MagickWand wand, ImageSize imageSize)
|
public async Task DrawPlayedIndicator(MagickWand wand, ImageSize imageSize)
|
||||||
@ -38,7 +42,7 @@ namespace Emby.Drawing.ImageMagick
|
|||||||
pixel.Opacity = 0;
|
pixel.Opacity = 0;
|
||||||
pixel.Color = "white";
|
pixel.Color = "white";
|
||||||
draw.FillColor = pixel;
|
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.FontSize = FontSize;
|
||||||
draw.FontStyle = FontStyleType.NormalStyle;
|
draw.FontStyle = FontStyleType.NormalStyle;
|
||||||
draw.TextAlignment = TextAlignType.CenterAlign;
|
draw.TextAlignment = TextAlignType.CenterAlign;
|
||||||
@ -52,18 +56,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);
|
var filePath = Path.Combine(paths.ProgramDataPath, "fonts", name);
|
||||||
|
|
||||||
if (File.Exists(filePath))
|
if (fileSystem.FileExists(filePath))
|
||||||
{
|
{
|
||||||
return filePath;
|
return filePath;
|
||||||
}
|
}
|
||||||
|
|
||||||
var namespacePath = typeof(PlayedIndicatorDrawer).Namespace + ".fonts." + name;
|
var namespacePath = typeof(PlayedIndicatorDrawer).Namespace + ".fonts." + name;
|
||||||
var tempPath = Path.Combine(paths.TempDirectory, Guid.NewGuid().ToString("N") + ".ttf");
|
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))
|
using (var stream = typeof(PlayedIndicatorDrawer).Assembly.GetManifestResourceStream(namespacePath))
|
||||||
{
|
{
|
||||||
@ -73,11 +77,11 @@ namespace Emby.Drawing.ImageMagick
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Directory.CreateDirectory(Path.GetDirectoryName(filePath));
|
fileSystem.CreateDirectory(Path.GetDirectoryName(filePath));
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
File.Copy(tempPath, filePath, false);
|
fileSystem.CopyFile(tempPath, filePath, false);
|
||||||
}
|
}
|
||||||
catch (IOException)
|
catch (IOException)
|
||||||
{
|
{
|
||||||
@ -87,11 +91,11 @@ namespace Emby.Drawing.ImageMagick
|
|||||||
return tempPath;
|
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);
|
var filePath = Path.Combine(paths.ProgramDataPath, "fonts", name);
|
||||||
|
|
||||||
if (File.Exists(filePath))
|
if (fileSystem.FileExists(filePath))
|
||||||
{
|
{
|
||||||
return filePath;
|
return filePath;
|
||||||
}
|
}
|
||||||
@ -103,11 +107,11 @@ namespace Emby.Drawing.ImageMagick
|
|||||||
|
|
||||||
}).ConfigureAwait(false);
|
}).ConfigureAwait(false);
|
||||||
|
|
||||||
Directory.CreateDirectory(Path.GetDirectoryName(filePath));
|
fileSystem.CreateDirectory(Path.GetDirectoryName(filePath));
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
File.Copy(tempPath, filePath, false);
|
fileSystem.CopyFile(tempPath, filePath, false);
|
||||||
}
|
}
|
||||||
catch (IOException)
|
catch (IOException)
|
||||||
{
|
{
|
||||||
|
@ -2,16 +2,20 @@
|
|||||||
using MediaBrowser.Common.Configuration;
|
using MediaBrowser.Common.Configuration;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using CommonIO;
|
||||||
|
using MediaBrowser.Common.IO;
|
||||||
|
|
||||||
namespace Emby.Drawing.ImageMagick
|
namespace Emby.Drawing.ImageMagick
|
||||||
{
|
{
|
||||||
public class StripCollageBuilder
|
public class StripCollageBuilder
|
||||||
{
|
{
|
||||||
private readonly IApplicationPaths _appPaths;
|
private readonly IApplicationPaths _appPaths;
|
||||||
|
private readonly IFileSystem _fileSystem;
|
||||||
|
|
||||||
public StripCollageBuilder(IApplicationPaths appPaths)
|
public StripCollageBuilder(IApplicationPaths appPaths, IFileSystem fileSystem)
|
||||||
{
|
{
|
||||||
_appPaths = appPaths;
|
_appPaths = appPaths;
|
||||||
|
_fileSystem = fileSystem;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void BuildPosterCollage(List<string> paths, string outputPath, int width, int height, string text)
|
public void BuildPosterCollage(List<string> paths, string outputPath, int width, int height, string text)
|
||||||
@ -145,17 +149,17 @@ namespace Emby.Drawing.ImageMagick
|
|||||||
|
|
||||||
private MagickWand BuildPosterCollageWand(List<string> paths, int width, int height)
|
private MagickWand BuildPosterCollageWand(List<string> paths, int width, int height)
|
||||||
{
|
{
|
||||||
var inputPaths = ImageHelpers.ProjectPaths(paths, 4);
|
var inputPaths = ImageHelpers.ProjectPaths(paths, 3);
|
||||||
using (var wandImages = new MagickWand(inputPaths.ToArray()))
|
using (var wandImages = new MagickWand(inputPaths.ToArray()))
|
||||||
{
|
{
|
||||||
var wand = new MagickWand(width, height);
|
var wand = new MagickWand(width, height);
|
||||||
wand.OpenImage("gradient:#111111-#111111");
|
wand.OpenImage("gradient:#111111-#111111");
|
||||||
using (var draw = new DrawingWand())
|
using (var draw = new DrawingWand())
|
||||||
{
|
{
|
||||||
var iSlice = Convert.ToInt32(width * 0.225);
|
var iSlice = Convert.ToInt32(width * 0.3);
|
||||||
int iTrans = Convert.ToInt32(height * .25);
|
int iTrans = Convert.ToInt32(height * .25);
|
||||||
int iHeight = Convert.ToInt32(height * .65);
|
int iHeight = Convert.ToInt32(height * .65);
|
||||||
var horizontalImagePadding = Convert.ToInt32(width * 0.0275);
|
var horizontalImagePadding = Convert.ToInt32(width * 0.0366);
|
||||||
|
|
||||||
foreach (var element in wandImages.ImageList)
|
foreach (var element in wandImages.ImageList)
|
||||||
{
|
{
|
||||||
@ -350,14 +354,14 @@ namespace Emby.Drawing.ImageMagick
|
|||||||
|
|
||||||
private MagickWand BuildSquareCollageWand(List<string> paths, int width, int height)
|
private MagickWand BuildSquareCollageWand(List<string> paths, int width, int height)
|
||||||
{
|
{
|
||||||
var inputPaths = ImageHelpers.ProjectPaths(paths, 4);
|
var inputPaths = ImageHelpers.ProjectPaths(paths, 3);
|
||||||
using (var wandImages = new MagickWand(inputPaths.ToArray()))
|
using (var wandImages = new MagickWand(inputPaths.ToArray()))
|
||||||
{
|
{
|
||||||
var wand = new MagickWand(width, height);
|
var wand = new MagickWand(width, height);
|
||||||
wand.OpenImage("gradient:#111111-#111111");
|
wand.OpenImage("gradient:#111111-#111111");
|
||||||
using (var draw = new DrawingWand())
|
using (var draw = new DrawingWand())
|
||||||
{
|
{
|
||||||
var iSlice = Convert.ToInt32(width * .225);
|
var iSlice = Convert.ToInt32(width * .3);
|
||||||
int iTrans = Convert.ToInt32(height * .25);
|
int iTrans = Convert.ToInt32(height * .25);
|
||||||
int iHeight = Convert.ToInt32(height * .63);
|
int iHeight = Convert.ToInt32(height * .63);
|
||||||
var horizontalImagePadding = Convert.ToInt32(width * 0.02);
|
var horizontalImagePadding = Convert.ToInt32(width * 0.02);
|
||||||
@ -490,7 +494,7 @@ namespace Emby.Drawing.ImageMagick
|
|||||||
|
|
||||||
private string MontserratLightFont
|
private string MontserratLightFont
|
||||||
{
|
{
|
||||||
get { return PlayedIndicatorDrawer.ExtractFont("MontserratLight.otf", _appPaths); }
|
get { return PlayedIndicatorDrawer.ExtractFont("MontserratLight.otf", _appPaths, _fileSystem); }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,9 @@
|
|||||||
using ImageMagickSharp;
|
using ImageMagickSharp;
|
||||||
using MediaBrowser.Common.Configuration;
|
using MediaBrowser.Common.Configuration;
|
||||||
|
using MediaBrowser.Common.IO;
|
||||||
using MediaBrowser.Model.Drawing;
|
using MediaBrowser.Model.Drawing;
|
||||||
using System.Globalization;
|
using System.Globalization;
|
||||||
|
using CommonIO;
|
||||||
|
|
||||||
namespace Emby.Drawing.ImageMagick
|
namespace Emby.Drawing.ImageMagick
|
||||||
{
|
{
|
||||||
@ -10,10 +12,12 @@ namespace Emby.Drawing.ImageMagick
|
|||||||
private const int OffsetFromTopRightCorner = 38;
|
private const int OffsetFromTopRightCorner = 38;
|
||||||
|
|
||||||
private readonly IApplicationPaths _appPaths;
|
private readonly IApplicationPaths _appPaths;
|
||||||
|
private readonly IFileSystem _fileSystem;
|
||||||
|
|
||||||
public UnplayedCountIndicator(IApplicationPaths appPaths)
|
public UnplayedCountIndicator(IApplicationPaths appPaths, IFileSystem fileSystem)
|
||||||
{
|
{
|
||||||
_appPaths = appPaths;
|
_appPaths = appPaths;
|
||||||
|
_fileSystem = fileSystem;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void DrawUnplayedCountIndicator(MagickWand wand, ImageSize imageSize, int count)
|
public void DrawUnplayedCountIndicator(MagickWand wand, ImageSize imageSize, int count)
|
||||||
@ -33,7 +37,7 @@ namespace Emby.Drawing.ImageMagick
|
|||||||
pixel.Opacity = 0;
|
pixel.Opacity = 0;
|
||||||
pixel.Color = "white";
|
pixel.Color = "white";
|
||||||
draw.FillColor = pixel;
|
draw.FillColor = pixel;
|
||||||
draw.Font = PlayedIndicatorDrawer.ExtractFont("robotoregular.ttf", _appPaths);
|
draw.Font = PlayedIndicatorDrawer.ExtractFont("robotoregular.ttf", _appPaths, _fileSystem);
|
||||||
draw.FontStyle = FontStyleType.NormalStyle;
|
draw.FontStyle = FontStyleType.NormalStyle;
|
||||||
draw.TextAlignment = TextAlignType.CenterAlign;
|
draw.TextAlignment = TextAlignType.CenterAlign;
|
||||||
draw.FontWeight = FontWeightType.RegularStyle;
|
draw.FontWeight = FontWeightType.RegularStyle;
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
using Emby.Drawing.Common;
|
using MediaBrowser.Common.Extensions;
|
||||||
using MediaBrowser.Common.Extensions;
|
|
||||||
using MediaBrowser.Common.IO;
|
using MediaBrowser.Common.IO;
|
||||||
using MediaBrowser.Controller;
|
using MediaBrowser.Controller;
|
||||||
using MediaBrowser.Controller.Drawing;
|
using MediaBrowser.Controller.Drawing;
|
||||||
@ -17,6 +16,9 @@ using System.IO;
|
|||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
using CommonIO;
|
||||||
|
using Emby.Drawing.Common;
|
||||||
|
using MediaBrowser.Controller.Library;
|
||||||
|
|
||||||
namespace Emby.Drawing
|
namespace Emby.Drawing
|
||||||
{
|
{
|
||||||
@ -52,18 +54,20 @@ namespace Emby.Drawing
|
|||||||
private readonly IServerApplicationPaths _appPaths;
|
private readonly IServerApplicationPaths _appPaths;
|
||||||
private readonly IImageEncoder _imageEncoder;
|
private readonly IImageEncoder _imageEncoder;
|
||||||
private readonly SemaphoreSlim _imageProcessingSemaphore;
|
private readonly SemaphoreSlim _imageProcessingSemaphore;
|
||||||
|
private readonly Func<ILibraryManager> _libraryManager;
|
||||||
|
|
||||||
public ImageProcessor(ILogger logger,
|
public ImageProcessor(ILogger logger,
|
||||||
IServerApplicationPaths appPaths,
|
IServerApplicationPaths appPaths,
|
||||||
IFileSystem fileSystem,
|
IFileSystem fileSystem,
|
||||||
IJsonSerializer jsonSerializer,
|
IJsonSerializer jsonSerializer,
|
||||||
IImageEncoder imageEncoder,
|
IImageEncoder imageEncoder,
|
||||||
int maxConcurrentImageProcesses)
|
int maxConcurrentImageProcesses, Func<ILibraryManager> libraryManager)
|
||||||
{
|
{
|
||||||
_logger = logger;
|
_logger = logger;
|
||||||
_fileSystem = fileSystem;
|
_fileSystem = fileSystem;
|
||||||
_jsonSerializer = jsonSerializer;
|
_jsonSerializer = jsonSerializer;
|
||||||
_imageEncoder = imageEncoder;
|
_imageEncoder = imageEncoder;
|
||||||
|
_libraryManager = libraryManager;
|
||||||
_appPaths = appPaths;
|
_appPaths = appPaths;
|
||||||
|
|
||||||
ImageEnhancers = new List<IImageEnhancer>();
|
ImageEnhancers = new List<IImageEnhancer>();
|
||||||
@ -106,6 +110,15 @@ namespace Emby.Drawing
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public bool SupportsImageCollageCreation
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
return _imageEncoder.SupportsImageCollageCreation;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private string ResizedImageCachePath
|
private string ResizedImageCachePath
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
@ -157,7 +170,19 @@ namespace Emby.Drawing
|
|||||||
throw new ArgumentNullException("options");
|
throw new ArgumentNullException("options");
|
||||||
}
|
}
|
||||||
|
|
||||||
var originalImagePath = options.Image.Path;
|
var originalImage = options.Image;
|
||||||
|
|
||||||
|
if (!originalImage.IsLocalFile)
|
||||||
|
{
|
||||||
|
originalImage = await _libraryManager().ConvertImageToLocal(options.Item, originalImage, options.ImageIndex).ConfigureAwait(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
var originalImagePath = originalImage.Path;
|
||||||
|
|
||||||
|
if (!_imageEncoder.SupportsImageEncoding)
|
||||||
|
{
|
||||||
|
return originalImagePath;
|
||||||
|
}
|
||||||
|
|
||||||
if (options.HasDefaultOptions(originalImagePath) && options.Enhancers.Count == 0 && !options.CropWhiteSpace)
|
if (options.HasDefaultOptions(originalImagePath) && options.Enhancers.Count == 0 && !options.CropWhiteSpace)
|
||||||
{
|
{
|
||||||
@ -165,9 +190,9 @@ namespace Emby.Drawing
|
|||||||
return originalImagePath;
|
return originalImagePath;
|
||||||
}
|
}
|
||||||
|
|
||||||
var dateModified = options.Image.DateModified;
|
var dateModified = originalImage.DateModified;
|
||||||
|
|
||||||
if (options.CropWhiteSpace)
|
if (options.CropWhiteSpace && _imageEncoder.SupportsImageEncoding)
|
||||||
{
|
{
|
||||||
var tuple = await GetWhitespaceCroppedImage(originalImagePath, dateModified).ConfigureAwait(false);
|
var tuple = await GetWhitespaceCroppedImage(originalImagePath, dateModified).ConfigureAwait(false);
|
||||||
|
|
||||||
@ -180,7 +205,7 @@ namespace Emby.Drawing
|
|||||||
var tuple = await GetEnhancedImage(new ItemImageInfo
|
var tuple = await GetEnhancedImage(new ItemImageInfo
|
||||||
{
|
{
|
||||||
DateModified = dateModified,
|
DateModified = dateModified,
|
||||||
Type = options.Image.Type,
|
Type = originalImage.Type,
|
||||||
Path = originalImagePath
|
Path = originalImagePath
|
||||||
|
|
||||||
}, options.Item, options.ImageIndex, options.Enhancers).ConfigureAwait(false);
|
}, options.Item, options.ImageIndex, options.Enhancers).ConfigureAwait(false);
|
||||||
@ -215,21 +240,18 @@ namespace Emby.Drawing
|
|||||||
{
|
{
|
||||||
CheckDisposed();
|
CheckDisposed();
|
||||||
|
|
||||||
if (!File.Exists(cacheFilePath))
|
if (!_fileSystem.FileExists(cacheFilePath))
|
||||||
{
|
{
|
||||||
var newWidth = Convert.ToInt32(newSize.Width);
|
var newWidth = Convert.ToInt32(newSize.Width);
|
||||||
var newHeight = Convert.ToInt32(newSize.Height);
|
var newHeight = Convert.ToInt32(newSize.Height);
|
||||||
|
|
||||||
Directory.CreateDirectory(Path.GetDirectoryName(cacheFilePath));
|
_fileSystem.CreateDirectory(Path.GetDirectoryName(cacheFilePath));
|
||||||
|
|
||||||
await _imageProcessingSemaphore.WaitAsync().ConfigureAwait(false);
|
await _imageProcessingSemaphore.WaitAsync().ConfigureAwait(false);
|
||||||
|
|
||||||
imageProcessingLockTaken = true;
|
imageProcessingLockTaken = true;
|
||||||
|
|
||||||
_imageEncoder.EncodeImage(originalImagePath, cacheFilePath, newWidth, newHeight, quality, options);
|
_imageEncoder.EncodeImage(originalImagePath, cacheFilePath, newWidth, newHeight, quality, options);
|
||||||
|
|
||||||
// ImageMagick doesn't seem to always release it right away
|
|
||||||
await Task.Delay(100).ConfigureAwait(false);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
@ -270,7 +292,7 @@ namespace Emby.Drawing
|
|||||||
await semaphore.WaitAsync().ConfigureAwait(false);
|
await semaphore.WaitAsync().ConfigureAwait(false);
|
||||||
|
|
||||||
// Check again in case of contention
|
// Check again in case of contention
|
||||||
if (File.Exists(croppedImagePath))
|
if (_fileSystem.FileExists(croppedImagePath))
|
||||||
{
|
{
|
||||||
semaphore.Release();
|
semaphore.Release();
|
||||||
return GetResult(croppedImagePath);
|
return GetResult(croppedImagePath);
|
||||||
@ -280,13 +302,18 @@ namespace Emby.Drawing
|
|||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
Directory.CreateDirectory(Path.GetDirectoryName(croppedImagePath));
|
_fileSystem.CreateDirectory(Path.GetDirectoryName(croppedImagePath));
|
||||||
|
|
||||||
await _imageProcessingSemaphore.WaitAsync().ConfigureAwait(false);
|
await _imageProcessingSemaphore.WaitAsync().ConfigureAwait(false);
|
||||||
imageProcessingLockTaken = true;
|
imageProcessingLockTaken = true;
|
||||||
|
|
||||||
_imageEncoder.CropWhiteSpace(originalImagePath, croppedImagePath);
|
_imageEncoder.CropWhiteSpace(originalImagePath, croppedImagePath);
|
||||||
}
|
}
|
||||||
|
catch (NotImplementedException)
|
||||||
|
{
|
||||||
|
// No need to spam the log with an error message
|
||||||
|
return new Tuple<string, DateTime>(originalImagePath, dateModified);
|
||||||
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
// We have to have a catch-all here because some of the .net image methods throw a plain old Exception
|
// We have to have a catch-all here because some of the .net image methods throw a plain old Exception
|
||||||
@ -359,21 +386,16 @@ namespace Emby.Drawing
|
|||||||
return GetCachePath(ResizedImageCachePath, filename, "." + format.ToString().ToLower());
|
return GetCachePath(ResizedImageCachePath, filename, "." + format.ToString().ToLower());
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the size of the image.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="path">The path.</param>
|
|
||||||
/// <returns>ImageSize.</returns>
|
|
||||||
public ImageSize GetImageSize(string path)
|
|
||||||
{
|
|
||||||
return GetImageSize(path, File.GetLastWriteTimeUtc(path), false);
|
|
||||||
}
|
|
||||||
|
|
||||||
public ImageSize GetImageSize(ItemImageInfo info)
|
public ImageSize GetImageSize(ItemImageInfo info)
|
||||||
{
|
{
|
||||||
return GetImageSize(info.Path, info.DateModified, false);
|
return GetImageSize(info.Path, info.DateModified, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public ImageSize GetImageSize(string path)
|
||||||
|
{
|
||||||
|
return GetImageSize(path, _fileSystem.GetLastWriteTimeUtc(path), false);
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the size of the image.
|
/// Gets the size of the image.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -399,7 +421,11 @@ namespace Emby.Drawing
|
|||||||
{
|
{
|
||||||
size = GetImageSizeInternal(path, allowSlowMethod);
|
size = GetImageSizeInternal(path, allowSlowMethod);
|
||||||
|
|
||||||
_cachedImagedSizes.AddOrUpdate(cacheHash, size, (keyName, oldValue) => size);
|
if (size.Width > 0 && size.Height > 0)
|
||||||
|
{
|
||||||
|
StartSaveImageSizeTimer();
|
||||||
|
_cachedImagedSizes.AddOrUpdate(cacheHash, size, (keyName, oldValue) => size);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return size;
|
return size;
|
||||||
@ -413,28 +439,26 @@ namespace Emby.Drawing
|
|||||||
/// <returns>ImageSize.</returns>
|
/// <returns>ImageSize.</returns>
|
||||||
private ImageSize GetImageSizeInternal(string path, bool allowSlowMethod)
|
private ImageSize GetImageSizeInternal(string path, bool allowSlowMethod)
|
||||||
{
|
{
|
||||||
ImageSize size;
|
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
size = ImageHeader.GetDimensions(path, _logger, _fileSystem);
|
using (var file = TagLib.File.Create(path))
|
||||||
|
{
|
||||||
|
var image = file as TagLib.Image.File;
|
||||||
|
|
||||||
|
var properties = image.Properties;
|
||||||
|
|
||||||
|
return new ImageSize
|
||||||
|
{
|
||||||
|
Height = properties.PhotoHeight,
|
||||||
|
Width = properties.PhotoWidth
|
||||||
|
};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
catch
|
catch
|
||||||
{
|
{
|
||||||
if (!allowSlowMethod)
|
|
||||||
{
|
|
||||||
throw;
|
|
||||||
}
|
|
||||||
//_logger.Info("Failed to read image header for {0}. Doing it the slow way.", path);
|
|
||||||
|
|
||||||
CheckDisposed();
|
|
||||||
|
|
||||||
size = _imageEncoder.GetImageSize(path);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
StartSaveImageSizeTimer();
|
return ImageHeader.GetDimensions(path, _logger, _fileSystem);
|
||||||
|
|
||||||
return size;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private readonly Timer _saveImageSizeTimer;
|
private readonly Timer _saveImageSizeTimer;
|
||||||
@ -452,7 +476,7 @@ namespace Emby.Drawing
|
|||||||
try
|
try
|
||||||
{
|
{
|
||||||
var path = ImageSizeFile;
|
var path = ImageSizeFile;
|
||||||
Directory.CreateDirectory(Path.GetDirectoryName(path));
|
_fileSystem.CreateDirectory(Path.GetDirectoryName(path));
|
||||||
_jsonSerializer.SerializeToFile(_cachedImagedSizes, path);
|
_jsonSerializer.SerializeToFile(_cachedImagedSizes, path);
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
@ -624,7 +648,7 @@ namespace Emby.Drawing
|
|||||||
await semaphore.WaitAsync().ConfigureAwait(false);
|
await semaphore.WaitAsync().ConfigureAwait(false);
|
||||||
|
|
||||||
// Check again in case of contention
|
// Check again in case of contention
|
||||||
if (File.Exists(enhancedImagePath))
|
if (_fileSystem.FileExists(enhancedImagePath))
|
||||||
{
|
{
|
||||||
semaphore.Release();
|
semaphore.Release();
|
||||||
return enhancedImagePath;
|
return enhancedImagePath;
|
||||||
@ -634,7 +658,7 @@ namespace Emby.Drawing
|
|||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
Directory.CreateDirectory(Path.GetDirectoryName(enhancedImagePath));
|
_fileSystem.CreateDirectory(Path.GetDirectoryName(enhancedImagePath));
|
||||||
|
|
||||||
await _imageProcessingSemaphore.WaitAsync().ConfigureAwait(false);
|
await _imageProcessingSemaphore.WaitAsync().ConfigureAwait(false);
|
||||||
|
|
||||||
@ -773,11 +797,11 @@ namespace Emby.Drawing
|
|||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
_logger.Debug("Creating image collage and saving to {0}", options.OutputPath);
|
_logger.Info("Creating image collage and saving to {0}", options.OutputPath);
|
||||||
|
|
||||||
_imageEncoder.CreateImageCollage(options);
|
_imageEncoder.CreateImageCollage(options);
|
||||||
|
|
||||||
_logger.Debug("Completed creation of image collage and saved to {0}", options.OutputPath);
|
_logger.Info("Completed creation of image collage and saved to {0}", options.OutputPath);
|
||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
{
|
{
|
||||||
@ -799,7 +823,6 @@ namespace Emby.Drawing
|
|||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -819,4 +842,4 @@ namespace Emby.Drawing
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
64
Emby.Drawing/NullImageEncoder.cs
Normal file
64
Emby.Drawing/NullImageEncoder.cs
Normal file
@ -0,0 +1,64 @@
|
|||||||
|
using System;
|
||||||
|
using MediaBrowser.Controller.Drawing;
|
||||||
|
using MediaBrowser.Model.Drawing;
|
||||||
|
|
||||||
|
namespace Emby.Drawing
|
||||||
|
{
|
||||||
|
public class NullImageEncoder : IImageEncoder
|
||||||
|
{
|
||||||
|
public string[] SupportedInputFormats
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
return new[]
|
||||||
|
{
|
||||||
|
"png",
|
||||||
|
"jpeg",
|
||||||
|
"jpg"
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public ImageFormat[] SupportedOutputFormats
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
return new[] { ImageFormat.Jpg, ImageFormat.Png };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void CropWhiteSpace(string inputPath, string outputPath)
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void EncodeImage(string inputPath, string outputPath, int width, int height, int quality, ImageProcessingOptions options)
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void CreateImageCollage(ImageCollageOptions options)
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
public string Name
|
||||||
|
{
|
||||||
|
get { return "Null Image Encoder"; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool SupportsImageCollageCreation
|
||||||
|
{
|
||||||
|
get { return false; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool SupportsImageEncoding
|
||||||
|
{
|
||||||
|
get { return false; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,4 +1,6 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<packages>
|
<packages>
|
||||||
|
<package id="CommonIO" version="1.0.0.5" targetFramework="net45" />
|
||||||
<package id="ImageMagickSharp" version="1.0.0.16" targetFramework="net45" />
|
<package id="ImageMagickSharp" version="1.0.0.16" targetFramework="net45" />
|
||||||
|
<package id="Patterns.Logging" version="1.0.0.2" targetFramework="net45" />
|
||||||
</packages>
|
</packages>
|
@ -15,6 +15,7 @@ using System.IO;
|
|||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
using CommonIO;
|
||||||
|
|
||||||
namespace MediaBrowser.Api
|
namespace MediaBrowser.Api
|
||||||
{
|
{
|
||||||
@ -95,7 +96,7 @@ namespace MediaBrowser.Api
|
|||||||
{
|
{
|
||||||
var path = _config.ApplicationPaths.TranscodingTempPath;
|
var path = _config.ApplicationPaths.TranscodingTempPath;
|
||||||
|
|
||||||
foreach (var file in Directory.EnumerateFiles(path, "*", SearchOption.AllDirectories)
|
foreach (var file in _fileSystem.GetFilePaths(path, true)
|
||||||
.ToList())
|
.ToList())
|
||||||
{
|
{
|
||||||
_fileSystem.DeleteFile(file);
|
_fileSystem.DeleteFile(file);
|
||||||
@ -567,7 +568,7 @@ namespace MediaBrowser.Api
|
|||||||
var directory = Path.GetDirectoryName(outputFilePath);
|
var directory = Path.GetDirectoryName(outputFilePath);
|
||||||
var name = Path.GetFileNameWithoutExtension(outputFilePath);
|
var name = Path.GetFileNameWithoutExtension(outputFilePath);
|
||||||
|
|
||||||
var filesToDelete = Directory.EnumerateFiles(directory)
|
var filesToDelete = _fileSystem.GetFilePaths(directory)
|
||||||
.Where(f => f.IndexOf(name, StringComparison.OrdinalIgnoreCase) != -1)
|
.Where(f => f.IndexOf(name, StringComparison.OrdinalIgnoreCase) != -1)
|
||||||
.ToList();
|
.ToList();
|
||||||
|
|
||||||
@ -577,7 +578,7 @@ namespace MediaBrowser.Api
|
|||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
Logger.Info("Deleting HLS file {0}", file);
|
Logger.Debug("Deleting HLS file {0}", file);
|
||||||
_fileSystem.DeleteFile(file);
|
_fileSystem.DeleteFile(file);
|
||||||
}
|
}
|
||||||
catch (DirectoryNotFoundException)
|
catch (DirectoryNotFoundException)
|
||||||
|
@ -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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -11,6 +11,7 @@ using ServiceStack.Web;
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using CommonIO;
|
||||||
|
|
||||||
namespace MediaBrowser.Api
|
namespace MediaBrowser.Api
|
||||||
{
|
{
|
||||||
|
@ -108,7 +108,6 @@ namespace MediaBrowser.Api.Dlna
|
|||||||
private readonly IConnectionManager _connectionManager;
|
private readonly IConnectionManager _connectionManager;
|
||||||
private readonly IMediaReceiverRegistrar _mediaReceiverRegistrar;
|
private readonly IMediaReceiverRegistrar _mediaReceiverRegistrar;
|
||||||
|
|
||||||
// TODO: Add utf-8
|
|
||||||
private const string XMLContentType = "text/xml; charset=UTF-8";
|
private const string XMLContentType = "text/xml; charset=UTF-8";
|
||||||
|
|
||||||
public DlnaServerService(IDlnaManager dlnaManager, IContentDirectory contentDirectory, IConnectionManager connectionManager, IMediaReceiverRegistrar mediaReceiverRegistrar)
|
public DlnaServerService(IDlnaManager dlnaManager, IContentDirectory contentDirectory, IConnectionManager connectionManager, IMediaReceiverRegistrar mediaReceiverRegistrar)
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
using MediaBrowser.Common.Net;
|
using MediaBrowser.Common.IO;
|
||||||
|
using MediaBrowser.Common.Net;
|
||||||
using MediaBrowser.Controller.Net;
|
using MediaBrowser.Controller.Net;
|
||||||
using MediaBrowser.Model.IO;
|
using MediaBrowser.Model.IO;
|
||||||
using MediaBrowser.Model.Net;
|
using MediaBrowser.Model.Net;
|
||||||
@ -8,6 +9,7 @@ using System.Collections.Generic;
|
|||||||
using System.Globalization;
|
using System.Globalization;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using CommonIO;
|
||||||
|
|
||||||
namespace MediaBrowser.Api
|
namespace MediaBrowser.Api
|
||||||
{
|
{
|
||||||
@ -96,13 +98,14 @@ namespace MediaBrowser.Api
|
|||||||
/// The _network manager
|
/// The _network manager
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private readonly INetworkManager _networkManager;
|
private readonly INetworkManager _networkManager;
|
||||||
|
private IFileSystem _fileSystem;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Initializes a new instance of the <see cref="EnvironmentService" /> class.
|
/// Initializes a new instance of the <see cref="EnvironmentService" /> class.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="networkManager">The network manager.</param>
|
/// <param name="networkManager">The network manager.</param>
|
||||||
/// <exception cref="System.ArgumentNullException">networkManager</exception>
|
/// <exception cref="System.ArgumentNullException">networkManager</exception>
|
||||||
public EnvironmentService(INetworkManager networkManager)
|
public EnvironmentService(INetworkManager networkManager, IFileSystem fileSystem)
|
||||||
{
|
{
|
||||||
if (networkManager == null)
|
if (networkManager == null)
|
||||||
{
|
{
|
||||||
@ -110,6 +113,7 @@ namespace MediaBrowser.Api
|
|||||||
}
|
}
|
||||||
|
|
||||||
_networkManager = networkManager;
|
_networkManager = networkManager;
|
||||||
|
_fileSystem = fileSystem;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -117,8 +121,6 @@ namespace MediaBrowser.Api
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="request">The request.</param>
|
/// <param name="request">The request.</param>
|
||||||
/// <returns>System.Object.</returns>
|
/// <returns>System.Object.</returns>
|
||||||
/// <exception cref="System.ArgumentNullException">Path</exception>
|
|
||||||
/// <exception cref="System.ArgumentException"></exception>
|
|
||||||
public object Get(GetDirectoryContents request)
|
public object Get(GetDirectoryContents request)
|
||||||
{
|
{
|
||||||
var path = request.Path;
|
var path = request.Path;
|
||||||
@ -222,8 +224,7 @@ namespace MediaBrowser.Api
|
|||||||
private IEnumerable<FileSystemEntryInfo> GetFileSystemEntries(GetDirectoryContents request)
|
private IEnumerable<FileSystemEntryInfo> GetFileSystemEntries(GetDirectoryContents request)
|
||||||
{
|
{
|
||||||
// using EnumerateFileSystemInfos doesn't handle reparse points (symlinks)
|
// using EnumerateFileSystemInfos doesn't handle reparse points (symlinks)
|
||||||
var entries = new DirectoryInfo(request.Path).EnumerateDirectories("*", SearchOption.TopDirectoryOnly)
|
var entries = _fileSystem.GetFileSystemEntries(request.Path).Where(i =>
|
||||||
.Concat<FileSystemInfo>(new DirectoryInfo(request.Path).EnumerateFiles("*", SearchOption.TopDirectoryOnly)).Where(i =>
|
|
||||||
{
|
{
|
||||||
if (!request.IncludeHidden && i.Attributes.HasFlag(FileAttributes.Hidden))
|
if (!request.IncludeHidden && i.Attributes.HasFlag(FileAttributes.Hidden))
|
||||||
{
|
{
|
||||||
|
@ -9,6 +9,7 @@ using System;
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using CommonIO;
|
||||||
|
|
||||||
namespace MediaBrowser.Api.Images
|
namespace MediaBrowser.Api.Images
|
||||||
{
|
{
|
||||||
@ -130,8 +131,7 @@ namespace MediaBrowser.Api.Images
|
|||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
return new DirectoryInfo(path)
|
return _fileSystem.GetFiles(path)
|
||||||
.GetFiles("*", SearchOption.AllDirectories)
|
|
||||||
.Where(i => BaseItem.SupportedImageExtensions.Contains(i.Extension, StringComparer.Ordinal))
|
.Where(i => BaseItem.SupportedImageExtensions.Contains(i.Extension, StringComparer.Ordinal))
|
||||||
.Select(i => new ImageByNameInfo
|
.Select(i => new ImageByNameInfo
|
||||||
{
|
{
|
||||||
@ -184,7 +184,7 @@ namespace MediaBrowser.Api.Images
|
|||||||
|
|
||||||
var paths = BaseItem.SupportedImageExtensions.Select(i => Path.Combine(_appPaths.GeneralPath, request.Name, filename + i)).ToList();
|
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);
|
return ToStaticFileResult(path);
|
||||||
}
|
}
|
||||||
@ -198,11 +198,11 @@ namespace MediaBrowser.Api.Images
|
|||||||
{
|
{
|
||||||
var themeFolder = Path.Combine(_appPaths.RatingsPath, request.Theme);
|
var themeFolder = Path.Combine(_appPaths.RatingsPath, request.Theme);
|
||||||
|
|
||||||
if (Directory.Exists(themeFolder))
|
if (_fileSystem.DirectoryExists(themeFolder))
|
||||||
{
|
{
|
||||||
var path = BaseItem.SupportedImageExtensions
|
var path = BaseItem.SupportedImageExtensions
|
||||||
.Select(i => Path.Combine(themeFolder, request.Name + i))
|
.Select(i => Path.Combine(themeFolder, request.Name + i))
|
||||||
.FirstOrDefault(File.Exists);
|
.FirstOrDefault(_fileSystem.FileExists);
|
||||||
|
|
||||||
if (!string.IsNullOrEmpty(path))
|
if (!string.IsNullOrEmpty(path))
|
||||||
{
|
{
|
||||||
@ -212,14 +212,14 @@ namespace MediaBrowser.Api.Images
|
|||||||
|
|
||||||
var allFolder = Path.Combine(_appPaths.RatingsPath, "all");
|
var allFolder = Path.Combine(_appPaths.RatingsPath, "all");
|
||||||
|
|
||||||
if (Directory.Exists(allFolder))
|
if (_fileSystem.DirectoryExists(allFolder))
|
||||||
{
|
{
|
||||||
// Avoid implicitly captured closure
|
// Avoid implicitly captured closure
|
||||||
var currentRequest = request;
|
var currentRequest = request;
|
||||||
|
|
||||||
var path = BaseItem.SupportedImageExtensions
|
var path = BaseItem.SupportedImageExtensions
|
||||||
.Select(i => Path.Combine(allFolder, currentRequest.Name + i))
|
.Select(i => Path.Combine(allFolder, currentRequest.Name + i))
|
||||||
.FirstOrDefault(File.Exists);
|
.FirstOrDefault(_fileSystem.FileExists);
|
||||||
|
|
||||||
if (!string.IsNullOrEmpty(path))
|
if (!string.IsNullOrEmpty(path))
|
||||||
{
|
{
|
||||||
@ -239,10 +239,10 @@ namespace MediaBrowser.Api.Images
|
|||||||
{
|
{
|
||||||
var themeFolder = Path.Combine(_appPaths.MediaInfoImagesPath, request.Theme);
|
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))
|
var path = BaseItem.SupportedImageExtensions.Select(i => Path.Combine(themeFolder, request.Name + i))
|
||||||
.FirstOrDefault(File.Exists);
|
.FirstOrDefault(_fileSystem.FileExists);
|
||||||
|
|
||||||
if (!string.IsNullOrEmpty(path))
|
if (!string.IsNullOrEmpty(path))
|
||||||
{
|
{
|
||||||
@ -252,13 +252,13 @@ namespace MediaBrowser.Api.Images
|
|||||||
|
|
||||||
var allFolder = Path.Combine(_appPaths.MediaInfoImagesPath, "all");
|
var allFolder = Path.Combine(_appPaths.MediaInfoImagesPath, "all");
|
||||||
|
|
||||||
if (Directory.Exists(allFolder))
|
if (_fileSystem.DirectoryExists(allFolder))
|
||||||
{
|
{
|
||||||
// Avoid implicitly captured closure
|
// Avoid implicitly captured closure
|
||||||
var currentRequest = request;
|
var currentRequest = request;
|
||||||
|
|
||||||
var path = BaseItem.SupportedImageExtensions.Select(i => Path.Combine(allFolder, currentRequest.Name + i))
|
var path = BaseItem.SupportedImageExtensions.Select(i => Path.Combine(allFolder, currentRequest.Name + i))
|
||||||
.FirstOrDefault(File.Exists);
|
.FirstOrDefault(_fileSystem.FileExists);
|
||||||
|
|
||||||
if (!string.IsNullOrEmpty(path))
|
if (!string.IsNullOrEmpty(path))
|
||||||
{
|
{
|
||||||
|
@ -17,6 +17,7 @@ using System.IO;
|
|||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
using CommonIO;
|
||||||
using MimeTypes = MediaBrowser.Model.Net.MimeTypes;
|
using MimeTypes = MediaBrowser.Model.Net.MimeTypes;
|
||||||
|
|
||||||
namespace MediaBrowser.Api.Images
|
namespace MediaBrowser.Api.Images
|
||||||
@ -311,17 +312,23 @@ namespace MediaBrowser.Api.Images
|
|||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var fileInfo = new FileInfo(info.Path);
|
|
||||||
|
|
||||||
int? width = null;
|
int? width = null;
|
||||||
int? height = null;
|
int? height = null;
|
||||||
|
long length = 0;
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var size = _imageProcessor.GetImageSize(info);
|
if (info.IsLocalFile)
|
||||||
|
{
|
||||||
|
var fileInfo = new FileInfo(info.Path);
|
||||||
|
length = fileInfo.Length;
|
||||||
|
|
||||||
width = Convert.ToInt32(size.Width);
|
var size = _imageProcessor.GetImageSize(info);
|
||||||
height = Convert.ToInt32(size.Height);
|
|
||||||
|
width = Convert.ToInt32(size.Width);
|
||||||
|
height = Convert.ToInt32(size.Height);
|
||||||
|
|
||||||
|
}
|
||||||
}
|
}
|
||||||
catch
|
catch
|
||||||
{
|
{
|
||||||
@ -333,7 +340,7 @@ namespace MediaBrowser.Api.Images
|
|||||||
ImageIndex = imageIndex,
|
ImageIndex = imageIndex,
|
||||||
ImageType = info.Type,
|
ImageType = info.Type,
|
||||||
ImageTag = _imageProcessor.GetImageCacheTag(item, info),
|
ImageTag = _imageProcessor.GetImageCacheTag(item, info),
|
||||||
Size = fileInfo.Length,
|
Size = length,
|
||||||
Width = width,
|
Width = width,
|
||||||
Height = height
|
Height = height
|
||||||
};
|
};
|
||||||
@ -557,7 +564,14 @@ namespace MediaBrowser.Api.Images
|
|||||||
|
|
||||||
}).ToList() : new List<IImageEnhancer>();
|
}).ToList() : new List<IImageEnhancer>();
|
||||||
|
|
||||||
var format = GetOutputFormat(request, imageInfo, supportedImageEnhancers);
|
var cropwhitespace = request.Type == ImageType.Logo || request.Type == ImageType.Art;
|
||||||
|
|
||||||
|
if (request.CropWhitespace.HasValue)
|
||||||
|
{
|
||||||
|
cropwhitespace = request.CropWhitespace.Value;
|
||||||
|
}
|
||||||
|
|
||||||
|
var format = GetOutputFormat(request, imageInfo, cropwhitespace, supportedImageEnhancers);
|
||||||
var contentType = GetMimeType(format, imageInfo.Path);
|
var contentType = GetMimeType(format, imageInfo.Path);
|
||||||
|
|
||||||
var cacheGuid = new Guid(_imageProcessor.GetImageCacheTag(item, imageInfo, supportedImageEnhancers));
|
var cacheGuid = new Guid(_imageProcessor.GetImageCacheTag(item, imageInfo, supportedImageEnhancers));
|
||||||
@ -578,6 +592,7 @@ namespace MediaBrowser.Api.Images
|
|||||||
return GetImageResult(item,
|
return GetImageResult(item,
|
||||||
request,
|
request,
|
||||||
imageInfo,
|
imageInfo,
|
||||||
|
cropwhitespace,
|
||||||
format,
|
format,
|
||||||
supportedImageEnhancers,
|
supportedImageEnhancers,
|
||||||
contentType,
|
contentType,
|
||||||
@ -590,6 +605,7 @@ namespace MediaBrowser.Api.Images
|
|||||||
private async Task<object> GetImageResult(IHasImages item,
|
private async Task<object> GetImageResult(IHasImages item,
|
||||||
ImageRequest request,
|
ImageRequest request,
|
||||||
ItemImageInfo image,
|
ItemImageInfo image,
|
||||||
|
bool cropwhitespace,
|
||||||
ImageFormat format,
|
ImageFormat format,
|
||||||
List<IImageEnhancer> enhancers,
|
List<IImageEnhancer> enhancers,
|
||||||
string contentType,
|
string contentType,
|
||||||
@ -597,13 +613,6 @@ namespace MediaBrowser.Api.Images
|
|||||||
IDictionary<string, string> headers,
|
IDictionary<string, string> headers,
|
||||||
bool isHeadRequest)
|
bool isHeadRequest)
|
||||||
{
|
{
|
||||||
var cropwhitespace = request.Type == ImageType.Logo || request.Type == ImageType.Art;
|
|
||||||
|
|
||||||
if (request.CropWhitespace.HasValue)
|
|
||||||
{
|
|
||||||
cropwhitespace = request.CropWhitespace.Value;
|
|
||||||
}
|
|
||||||
|
|
||||||
var options = new ImageProcessingOptions
|
var options = new ImageProcessingOptions
|
||||||
{
|
{
|
||||||
CropWhiteSpace = cropwhitespace,
|
CropWhiteSpace = cropwhitespace,
|
||||||
@ -637,7 +646,7 @@ namespace MediaBrowser.Api.Images
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private ImageFormat GetOutputFormat(ImageRequest request, ItemImageInfo image, List<IImageEnhancer> enhancers)
|
private ImageFormat GetOutputFormat(ImageRequest request, ItemImageInfo image, bool cropwhitespace, List<IImageEnhancer> enhancers)
|
||||||
{
|
{
|
||||||
if (!string.IsNullOrWhiteSpace(request.Format))
|
if (!string.IsNullOrWhiteSpace(request.Format))
|
||||||
{
|
{
|
||||||
@ -648,10 +657,37 @@ namespace MediaBrowser.Api.Images
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var extension = Path.GetExtension(image.Path);
|
||||||
|
ImageFormat? inputFormat = null;
|
||||||
|
|
||||||
|
if (string.Equals(extension, ".jpg", StringComparison.OrdinalIgnoreCase) ||
|
||||||
|
string.Equals(extension, ".jpeg", StringComparison.OrdinalIgnoreCase))
|
||||||
|
{
|
||||||
|
inputFormat = ImageFormat.Jpg;
|
||||||
|
}
|
||||||
|
else if (string.Equals(extension, ".png", StringComparison.OrdinalIgnoreCase))
|
||||||
|
{
|
||||||
|
inputFormat = ImageFormat.Png;
|
||||||
|
}
|
||||||
|
|
||||||
|
var clientSupportedFormats = GetClientSupportedFormats();
|
||||||
|
if (inputFormat.HasValue && clientSupportedFormats.Contains(inputFormat.Value) && enhancers.Count == 0)
|
||||||
|
{
|
||||||
|
if ((request.Quality ?? 100) == 100 && !request.Height.HasValue && !request.Width.HasValue &&
|
||||||
|
!request.AddPlayedIndicator && !request.PercentPlayed.HasValue && !request.UnplayedCount.HasValue && string.IsNullOrWhiteSpace(request.BackgroundColor))
|
||||||
|
{
|
||||||
|
// TODO: Allow this when specfying max width/height if the value is in range
|
||||||
|
if (!cropwhitespace && !request.MaxHeight.HasValue && !request.MaxWidth.HasValue)
|
||||||
|
{
|
||||||
|
return inputFormat.Value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
var serverFormats = _imageProcessor.GetSupportedImageOutputFormats();
|
var serverFormats = _imageProcessor.GetSupportedImageOutputFormats();
|
||||||
|
|
||||||
if (serverFormats.Contains(ImageFormat.Webp) &&
|
// Client doesn't care about format, so start with webp if supported
|
||||||
GetClientSupportedFormats().Contains(ImageFormat.Webp))
|
if (serverFormats.Contains(ImageFormat.Webp) && clientSupportedFormats.Contains(ImageFormat.Webp))
|
||||||
{
|
{
|
||||||
return ImageFormat.Webp;
|
return ImageFormat.Webp;
|
||||||
}
|
}
|
||||||
@ -661,10 +697,7 @@ namespace MediaBrowser.Api.Images
|
|||||||
return ImageFormat.Png;
|
return ImageFormat.Png;
|
||||||
}
|
}
|
||||||
|
|
||||||
var extension = Path.GetExtension(image.Path);
|
if (inputFormat.HasValue && inputFormat.Value == ImageFormat.Jpg)
|
||||||
|
|
||||||
if (string.Equals(extension, ".jpg", StringComparison.OrdinalIgnoreCase) ||
|
|
||||||
string.Equals(extension, ".jpeg", StringComparison.OrdinalIgnoreCase))
|
|
||||||
{
|
{
|
||||||
return ImageFormat.Jpg;
|
return ImageFormat.Jpg;
|
||||||
}
|
}
|
||||||
@ -675,7 +708,7 @@ namespace MediaBrowser.Api.Images
|
|||||||
|
|
||||||
private ImageFormat[] GetClientSupportedFormats()
|
private ImageFormat[] GetClientSupportedFormats()
|
||||||
{
|
{
|
||||||
var supportsWebP = (Request.AcceptTypes ?? new string[] {}).Contains("image/webp", StringComparer.OrdinalIgnoreCase);
|
var supportsWebP = (Request.AcceptTypes ?? new string[] { }).Contains("image/webp", StringComparer.OrdinalIgnoreCase);
|
||||||
|
|
||||||
var userAgent = Request.UserAgent ?? string.Empty;
|
var userAgent = Request.UserAgent ?? string.Empty;
|
||||||
|
|
||||||
|
@ -16,6 +16,7 @@ using System.IO;
|
|||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
using CommonIO;
|
||||||
|
|
||||||
namespace MediaBrowser.Api.Images
|
namespace MediaBrowser.Api.Images
|
||||||
{
|
{
|
||||||
@ -237,7 +238,7 @@ namespace MediaBrowser.Api.Images
|
|||||||
contentPath = await reader.ReadToEndAsync().ConfigureAwait(false);
|
contentPath = await reader.ReadToEndAsync().ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (File.Exists(contentPath))
|
if (_fileSystem.FileExists(contentPath))
|
||||||
{
|
{
|
||||||
return ToStaticFileResult(contentPath);
|
return ToStaticFileResult(contentPath);
|
||||||
}
|
}
|
||||||
@ -281,7 +282,7 @@ namespace MediaBrowser.Api.Images
|
|||||||
|
|
||||||
var fullCachePath = GetFullCachePath(urlHash + "." + ext);
|
var fullCachePath = GetFullCachePath(urlHash + "." + ext);
|
||||||
|
|
||||||
Directory.CreateDirectory(Path.GetDirectoryName(fullCachePath));
|
_fileSystem.CreateDirectory(Path.GetDirectoryName(fullCachePath));
|
||||||
using (var stream = result.Content)
|
using (var stream = result.Content)
|
||||||
{
|
{
|
||||||
using (var filestream = _fileSystem.GetFileStream(fullCachePath, FileMode.Create, FileAccess.Write, FileShare.Read, true))
|
using (var filestream = _fileSystem.GetFileStream(fullCachePath, FileMode.Create, FileAccess.Write, FileShare.Read, true))
|
||||||
@ -290,7 +291,7 @@ namespace MediaBrowser.Api.Images
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Directory.CreateDirectory(Path.GetDirectoryName(pointerCachePath));
|
_fileSystem.CreateDirectory(Path.GetDirectoryName(pointerCachePath));
|
||||||
using (var writer = new StreamWriter(pointerCachePath))
|
using (var writer = new StreamWriter(pointerCachePath))
|
||||||
{
|
{
|
||||||
await writer.WriteAsync(fullCachePath).ConfigureAwait(false);
|
await writer.WriteAsync(fullCachePath).ConfigureAwait(false);
|
||||||
|
@ -16,6 +16,7 @@ using System.IO;
|
|||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
using CommonIO;
|
||||||
|
|
||||||
namespace MediaBrowser.Api
|
namespace MediaBrowser.Api
|
||||||
{
|
{
|
||||||
@ -200,7 +201,7 @@ namespace MediaBrowser.Api
|
|||||||
//}
|
//}
|
||||||
item.ProviderIds = request.ProviderIds;
|
item.ProviderIds = request.ProviderIds;
|
||||||
|
|
||||||
var task = _providerManager.RefreshFullItem(item, new MetadataRefreshOptions
|
var task = _providerManager.RefreshFullItem(item, new MetadataRefreshOptions(_fileSystem)
|
||||||
{
|
{
|
||||||
MetadataRefreshMode = MetadataRefreshMode.FullRefresh,
|
MetadataRefreshMode = MetadataRefreshMode.FullRefresh,
|
||||||
ImageRefreshMode = ImageRefreshMode.FullRefresh,
|
ImageRefreshMode = ImageRefreshMode.FullRefresh,
|
||||||
@ -230,7 +231,7 @@ namespace MediaBrowser.Api
|
|||||||
contentPath = await reader.ReadToEndAsync().ConfigureAwait(false);
|
contentPath = await reader.ReadToEndAsync().ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (File.Exists(contentPath))
|
if (_fileSystem.FileExists(contentPath))
|
||||||
{
|
{
|
||||||
return ToStaticFileResult(contentPath);
|
return ToStaticFileResult(contentPath);
|
||||||
}
|
}
|
||||||
@ -271,7 +272,7 @@ namespace MediaBrowser.Api
|
|||||||
|
|
||||||
var fullCachePath = GetFullCachePath(urlHash + "." + ext);
|
var fullCachePath = GetFullCachePath(urlHash + "." + ext);
|
||||||
|
|
||||||
Directory.CreateDirectory(Path.GetDirectoryName(fullCachePath));
|
_fileSystem.CreateDirectory(Path.GetDirectoryName(fullCachePath));
|
||||||
using (var stream = result.Content)
|
using (var stream = result.Content)
|
||||||
{
|
{
|
||||||
using (var filestream = _fileSystem.GetFileStream(fullCachePath, FileMode.Create, FileAccess.Write, FileShare.Read, true))
|
using (var filestream = _fileSystem.GetFileStream(fullCachePath, FileMode.Create, FileAccess.Write, FileShare.Read, true))
|
||||||
@ -280,7 +281,7 @@ namespace MediaBrowser.Api
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Directory.CreateDirectory(Path.GetDirectoryName(pointerCachePath));
|
_fileSystem.CreateDirectory(Path.GetDirectoryName(pointerCachePath));
|
||||||
using (var writer = new StreamWriter(pointerCachePath))
|
using (var writer = new StreamWriter(pointerCachePath))
|
||||||
{
|
{
|
||||||
await writer.WriteAsync(fullCachePath).ConfigureAwait(false);
|
await writer.WriteAsync(fullCachePath).ConfigureAwait(false);
|
||||||
|
@ -4,6 +4,8 @@ using MediaBrowser.Controller.Net;
|
|||||||
using MediaBrowser.Controller.Providers;
|
using MediaBrowser.Controller.Providers;
|
||||||
using ServiceStack;
|
using ServiceStack;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
|
using CommonIO;
|
||||||
|
using MediaBrowser.Common.IO;
|
||||||
|
|
||||||
namespace MediaBrowser.Api
|
namespace MediaBrowser.Api
|
||||||
{
|
{
|
||||||
@ -37,11 +39,13 @@ namespace MediaBrowser.Api
|
|||||||
{
|
{
|
||||||
private readonly ILibraryManager _libraryManager;
|
private readonly ILibraryManager _libraryManager;
|
||||||
private readonly IProviderManager _providerManager;
|
private readonly IProviderManager _providerManager;
|
||||||
|
private readonly IFileSystem _fileSystem;
|
||||||
|
|
||||||
public ItemRefreshService(ILibraryManager libraryManager, IProviderManager providerManager)
|
public ItemRefreshService(ILibraryManager libraryManager, IProviderManager providerManager, IFileSystem fileSystem)
|
||||||
{
|
{
|
||||||
_libraryManager = libraryManager;
|
_libraryManager = libraryManager;
|
||||||
_providerManager = providerManager;
|
_providerManager = providerManager;
|
||||||
|
_fileSystem = fileSystem;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -66,7 +70,7 @@ namespace MediaBrowser.Api
|
|||||||
|
|
||||||
private MetadataRefreshOptions GetRefreshOptions(BaseRefreshRequest request)
|
private MetadataRefreshOptions GetRefreshOptions(BaseRefreshRequest request)
|
||||||
{
|
{
|
||||||
return new MetadataRefreshOptions(new DirectoryService())
|
return new MetadataRefreshOptions(new DirectoryService(_fileSystem))
|
||||||
{
|
{
|
||||||
MetadataRefreshMode = request.MetadataRefreshMode,
|
MetadataRefreshMode = request.MetadataRefreshMode,
|
||||||
ImageRefreshMode = request.ImageRefreshMode,
|
ImageRefreshMode = request.ImageRefreshMode,
|
||||||
|
@ -211,11 +211,6 @@ namespace MediaBrowser.Api
|
|||||||
|
|
||||||
UpdateItem(request, item);
|
UpdateItem(request, item);
|
||||||
|
|
||||||
if (isLockedChanged && item.IsLocked)
|
|
||||||
{
|
|
||||||
item.IsUnidentified = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
await item.UpdateToRepository(ItemUpdateType.MetadataEdit, CancellationToken.None).ConfigureAwait(false);
|
await item.UpdateToRepository(ItemUpdateType.MetadataEdit, CancellationToken.None).ConfigureAwait(false);
|
||||||
|
|
||||||
if (request.People != null)
|
if (request.People != null)
|
||||||
@ -321,13 +316,8 @@ namespace MediaBrowser.Api
|
|||||||
|
|
||||||
SetProductionLocations(item, request);
|
SetProductionLocations(item, request);
|
||||||
|
|
||||||
var hasLang = item as IHasPreferredMetadataLanguage;
|
item.PreferredMetadataCountryCode = request.PreferredMetadataCountryCode;
|
||||||
|
item.PreferredMetadataLanguage = request.PreferredMetadataLanguage;
|
||||||
if (hasLang != null)
|
|
||||||
{
|
|
||||||
hasLang.PreferredMetadataCountryCode = request.PreferredMetadataCountryCode;
|
|
||||||
hasLang.PreferredMetadataLanguage = request.PreferredMetadataLanguage;
|
|
||||||
}
|
|
||||||
|
|
||||||
var hasDisplayOrder = item as IHasDisplayOrder;
|
var hasDisplayOrder = item as IHasDisplayOrder;
|
||||||
if (hasDisplayOrder != null)
|
if (hasDisplayOrder != null)
|
||||||
|
@ -3,6 +3,7 @@ using MediaBrowser.Controller;
|
|||||||
using System;
|
using System;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using CommonIO;
|
||||||
|
|
||||||
namespace MediaBrowser.Api.Library
|
namespace MediaBrowser.Api.Library
|
||||||
{
|
{
|
||||||
@ -33,11 +34,11 @@ namespace MediaBrowser.Api.Library
|
|||||||
var rootFolderPath = appPaths.DefaultUserViewsPath;
|
var rootFolderPath = appPaths.DefaultUserViewsPath;
|
||||||
var path = Path.Combine(rootFolderPath, virtualFolderName);
|
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));
|
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));
|
var shortcut = Directory.EnumerateFiles(path, ShortcutFileSearch, SearchOption.AllDirectories).FirstOrDefault(f => fileSystem.ResolveShortcut(f).Equals(mediaPath, StringComparison.OrdinalIgnoreCase));
|
||||||
|
|
||||||
if (!string.IsNullOrEmpty(shortcut))
|
if (!string.IsNullOrEmpty(shortcut))
|
||||||
@ -53,11 +54,9 @@ namespace MediaBrowser.Api.Library
|
|||||||
/// <param name="virtualFolderName">Name of the virtual folder.</param>
|
/// <param name="virtualFolderName">Name of the virtual folder.</param>
|
||||||
/// <param name="path">The path.</param>
|
/// <param name="path">The path.</param>
|
||||||
/// <param name="appPaths">The app paths.</param>
|
/// <param name="appPaths">The app paths.</param>
|
||||||
/// <exception cref="System.IO.DirectoryNotFoundException">The path does not exist.</exception>
|
|
||||||
/// <exception cref="System.ArgumentException">The path is not valid.</exception>
|
|
||||||
public static void AddMediaPath(IFileSystem fileSystem, string virtualFolderName, string path, IServerApplicationPaths appPaths)
|
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.");
|
throw new DirectoryNotFoundException("The path does not exist.");
|
||||||
}
|
}
|
||||||
@ -69,7 +68,7 @@ namespace MediaBrowser.Api.Library
|
|||||||
|
|
||||||
var lnk = Path.Combine(virtualFolderPath, shortcutFilename + ShortcutFileExtension);
|
var lnk = Path.Combine(virtualFolderPath, shortcutFilename + ShortcutFileExtension);
|
||||||
|
|
||||||
while (File.Exists(lnk))
|
while (fileSystem.FileExists(lnk))
|
||||||
{
|
{
|
||||||
shortcutFilename += "1";
|
shortcutFilename += "1";
|
||||||
lnk = Path.Combine(virtualFolderPath, shortcutFilename + ShortcutFileExtension);
|
lnk = Path.Combine(virtualFolderPath, shortcutFilename + ShortcutFileExtension);
|
||||||
|
@ -27,6 +27,8 @@ using System.IO;
|
|||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
using CommonIO;
|
||||||
|
using MediaBrowser.Common.IO;
|
||||||
|
|
||||||
namespace MediaBrowser.Api.Library
|
namespace MediaBrowser.Api.Library
|
||||||
{
|
{
|
||||||
@ -282,12 +284,13 @@ namespace MediaBrowser.Api.Library
|
|||||||
private readonly IChannelManager _channelManager;
|
private readonly IChannelManager _channelManager;
|
||||||
private readonly ITVSeriesManager _tvManager;
|
private readonly ITVSeriesManager _tvManager;
|
||||||
private readonly ILibraryMonitor _libraryMonitor;
|
private readonly ILibraryMonitor _libraryMonitor;
|
||||||
|
private readonly IFileSystem _fileSystem;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Initializes a new instance of the <see cref="LibraryService" /> class.
|
/// Initializes a new instance of the <see cref="LibraryService" /> class.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public LibraryService(IItemRepository itemRepo, ILibraryManager libraryManager, IUserManager userManager,
|
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;
|
_itemRepo = itemRepo;
|
||||||
_libraryManager = libraryManager;
|
_libraryManager = libraryManager;
|
||||||
@ -301,6 +304,7 @@ namespace MediaBrowser.Api.Library
|
|||||||
_channelManager = channelManager;
|
_channelManager = channelManager;
|
||||||
_tvManager = tvManager;
|
_tvManager = tvManager;
|
||||||
_libraryMonitor = libraryMonitor;
|
_libraryMonitor = libraryMonitor;
|
||||||
|
_fileSystem = fileSystem;
|
||||||
}
|
}
|
||||||
|
|
||||||
public object Get(GetSimilarItems request)
|
public object Get(GetSimilarItems request)
|
||||||
@ -557,7 +561,7 @@ namespace MediaBrowser.Api.Library
|
|||||||
{
|
{
|
||||||
throw new ArgumentException("This command cannot be used for remote or virtual items.");
|
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.");
|
throw new ArgumentException("This command cannot be used for directories.");
|
||||||
}
|
}
|
||||||
@ -719,7 +723,7 @@ namespace MediaBrowser.Api.Library
|
|||||||
|
|
||||||
if (!item.CanDelete(user))
|
if (!item.CanDelete(user))
|
||||||
{
|
{
|
||||||
throw new UnauthorizedAccessException();
|
throw new SecurityException("Unauthorized access");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (item is ILiveTvRecording)
|
if (item is ILiveTvRecording)
|
||||||
|
@ -10,6 +10,7 @@ using System.IO;
|
|||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
using CommonIO;
|
||||||
|
|
||||||
namespace MediaBrowser.Api.Library
|
namespace MediaBrowser.Api.Library
|
||||||
{
|
{
|
||||||
@ -46,6 +47,12 @@ namespace MediaBrowser.Api.Library
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <value><c>true</c> if [refresh library]; otherwise, <c>false</c>.</value>
|
/// <value><c>true</c> if [refresh library]; otherwise, <c>false</c>.</value>
|
||||||
public bool RefreshLibrary { get; set; }
|
public bool RefreshLibrary { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the path.
|
||||||
|
/// </summary>
|
||||||
|
/// <value>The path.</value>
|
||||||
|
public string[] Paths { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
[Route("/Library/VirtualFolders", "DELETE")]
|
[Route("/Library/VirtualFolders", "DELETE")]
|
||||||
@ -195,22 +202,42 @@ namespace MediaBrowser.Api.Library
|
|||||||
|
|
||||||
var virtualFolderPath = Path.Combine(rootFolderPath, name);
|
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 + ".");
|
throw new ArgumentException("There is already a media library with the name " + name + ".");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (request.Paths != null)
|
||||||
|
{
|
||||||
|
var invalidpath = request.Paths.FirstOrDefault(i => !_fileSystem.DirectoryExists(i));
|
||||||
|
if (invalidpath != null)
|
||||||
|
{
|
||||||
|
throw new ArgumentException("The specified path does not exist: " + invalidpath + ".");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
_libraryMonitor.Stop();
|
_libraryMonitor.Stop();
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
Directory.CreateDirectory(virtualFolderPath);
|
_fileSystem.CreateDirectory(virtualFolderPath);
|
||||||
|
|
||||||
if (!string.IsNullOrEmpty(request.CollectionType))
|
if (!string.IsNullOrEmpty(request.CollectionType))
|
||||||
{
|
{
|
||||||
var path = Path.Combine(virtualFolderPath, request.CollectionType + ".collection");
|
var path = Path.Combine(virtualFolderPath, request.CollectionType + ".collection");
|
||||||
|
|
||||||
File.Create(path);
|
using (File.Create(path))
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (request.Paths != null)
|
||||||
|
{
|
||||||
|
foreach (var path in request.Paths)
|
||||||
|
{
|
||||||
|
LibraryHelpers.AddMediaPath(_fileSystem, request.Name, path, _appPaths);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
@ -256,12 +283,12 @@ namespace MediaBrowser.Api.Library
|
|||||||
var currentPath = Path.Combine(rootFolderPath, request.Name);
|
var currentPath = Path.Combine(rootFolderPath, request.Name);
|
||||||
var newPath = Path.Combine(rootFolderPath, request.NewName);
|
var newPath = Path.Combine(rootFolderPath, request.NewName);
|
||||||
|
|
||||||
if (!Directory.Exists(currentPath))
|
if (!_fileSystem.DirectoryExists(currentPath))
|
||||||
{
|
{
|
||||||
throw new DirectoryNotFoundException("The media collection does not exist");
|
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 + ".");
|
throw new ArgumentException("There is already a media collection with the name " + newPath + ".");
|
||||||
}
|
}
|
||||||
@ -276,11 +303,11 @@ namespace MediaBrowser.Api.Library
|
|||||||
//Create an unique name
|
//Create an unique name
|
||||||
var temporaryName = Guid.NewGuid().ToString();
|
var temporaryName = Guid.NewGuid().ToString();
|
||||||
var temporaryPath = Path.Combine(rootFolderPath, temporaryName);
|
var temporaryPath = Path.Combine(rootFolderPath, temporaryName);
|
||||||
Directory.Move(currentPath, temporaryPath);
|
_fileSystem.MoveDirectory(currentPath, temporaryPath);
|
||||||
currentPath = temporaryPath;
|
currentPath = temporaryPath;
|
||||||
}
|
}
|
||||||
|
|
||||||
Directory.Move(currentPath, newPath);
|
_fileSystem.MoveDirectory(currentPath, newPath);
|
||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
{
|
{
|
||||||
@ -319,7 +346,7 @@ namespace MediaBrowser.Api.Library
|
|||||||
|
|
||||||
var path = Path.Combine(rootFolderPath, request.Name);
|
var path = Path.Combine(rootFolderPath, request.Name);
|
||||||
|
|
||||||
if (!Directory.Exists(path))
|
if (!_fileSystem.DirectoryExists(path))
|
||||||
{
|
{
|
||||||
throw new DirectoryNotFoundException("The media folder does not exist");
|
throw new DirectoryNotFoundException("The media folder does not exist");
|
||||||
}
|
}
|
||||||
|
@ -521,12 +521,12 @@ namespace MediaBrowser.Api.LiveTv
|
|||||||
|
|
||||||
if (user == null)
|
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)
|
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.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -11,10 +11,9 @@
|
|||||||
<AssemblyName>MediaBrowser.Api</AssemblyName>
|
<AssemblyName>MediaBrowser.Api</AssemblyName>
|
||||||
<FileAlignment>512</FileAlignment>
|
<FileAlignment>512</FileAlignment>
|
||||||
<SolutionDir Condition="$(SolutionDir) == '' Or $(SolutionDir) == '*Undefined*'">..\</SolutionDir>
|
<SolutionDir Condition="$(SolutionDir) == '' Or $(SolutionDir) == '*Undefined*'">..\</SolutionDir>
|
||||||
<ProductVersion>10.0.0</ProductVersion>
|
|
||||||
<SchemaVersion>2.0</SchemaVersion>
|
|
||||||
<TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
|
<TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
|
||||||
<RestorePackages>true</RestorePackages>
|
<ReleaseVersion>
|
||||||
|
</ReleaseVersion>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
|
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
|
||||||
<DebugSymbols>true</DebugSymbols>
|
<DebugSymbols>true</DebugSymbols>
|
||||||
@ -25,7 +24,6 @@
|
|||||||
<ErrorReport>prompt</ErrorReport>
|
<ErrorReport>prompt</ErrorReport>
|
||||||
<WarningLevel>4</WarningLevel>
|
<WarningLevel>4</WarningLevel>
|
||||||
<PlatformTarget>AnyCPU</PlatformTarget>
|
<PlatformTarget>AnyCPU</PlatformTarget>
|
||||||
<TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
|
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
|
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
|
||||||
<DebugType>none</DebugType>
|
<DebugType>none</DebugType>
|
||||||
@ -34,7 +32,6 @@
|
|||||||
<DefineConstants>TRACE</DefineConstants>
|
<DefineConstants>TRACE</DefineConstants>
|
||||||
<ErrorReport>prompt</ErrorReport>
|
<ErrorReport>prompt</ErrorReport>
|
||||||
<WarningLevel>4</WarningLevel>
|
<WarningLevel>4</WarningLevel>
|
||||||
<TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
|
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release Mono|AnyCPU' ">
|
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release Mono|AnyCPU' ">
|
||||||
<DebugType>none</DebugType>
|
<DebugType>none</DebugType>
|
||||||
@ -43,15 +40,17 @@
|
|||||||
<DefineConstants>TRACE</DefineConstants>
|
<DefineConstants>TRACE</DefineConstants>
|
||||||
<ErrorReport>prompt</ErrorReport>
|
<ErrorReport>prompt</ErrorReport>
|
||||||
<WarningLevel>4</WarningLevel>
|
<WarningLevel>4</WarningLevel>
|
||||||
<TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
|
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<RunPostBuildEvent>Always</RunPostBuildEvent>
|
<RunPostBuildEvent>Always</RunPostBuildEvent>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Reference Include="MoreLinq, Version=1.1.17511.0, Culture=neutral, PublicKeyToken=384d532d7e88985d, processorArchitecture=MSIL">
|
<Reference Include="CommonIO, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL">
|
||||||
<SpecificVersion>False</SpecificVersion>
|
<SpecificVersion>False</SpecificVersion>
|
||||||
<HintPath>..\packages\morelinq.1.1.0\lib\net35\MoreLinq.dll</HintPath>
|
<HintPath>..\packages\CommonIO.1.0.0.5\lib\net45\CommonIO.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>
|
||||||
</Reference>
|
</Reference>
|
||||||
<Reference Include="System" />
|
<Reference Include="System" />
|
||||||
<Reference Include="System.Core" />
|
<Reference Include="System.Core" />
|
||||||
@ -64,12 +63,14 @@
|
|||||||
<Reference Include="ServiceStack.Text">
|
<Reference Include="ServiceStack.Text">
|
||||||
<HintPath>..\ThirdParty\ServiceStack.Text\ServiceStack.Text.dll</HintPath>
|
<HintPath>..\ThirdParty\ServiceStack.Text\ServiceStack.Text.dll</HintPath>
|
||||||
</Reference>
|
</Reference>
|
||||||
|
<Reference Include="MoreLinq">
|
||||||
|
<HintPath>..\packages\morelinq.1.1.1\lib\net35\MoreLinq.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Compile Include="..\SharedVersion.cs">
|
<Compile Include="..\SharedVersion.cs">
|
||||||
<Link>Properties\SharedVersion.cs</Link>
|
<Link>Properties\SharedVersion.cs</Link>
|
||||||
</Compile>
|
</Compile>
|
||||||
<Compile Include="AppThemeService.cs" />
|
|
||||||
<Compile Include="BrandingService.cs" />
|
<Compile Include="BrandingService.cs" />
|
||||||
<Compile Include="ChannelService.cs" />
|
<Compile Include="ChannelService.cs" />
|
||||||
<Compile Include="ConnectService.cs" />
|
<Compile Include="ConnectService.cs" />
|
||||||
|
@ -23,6 +23,7 @@ using System.Linq;
|
|||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
using CommonIO;
|
||||||
|
|
||||||
namespace MediaBrowser.Api.Playback
|
namespace MediaBrowser.Api.Playback
|
||||||
{
|
{
|
||||||
@ -290,13 +291,6 @@ namespace MediaBrowser.Api.Playback
|
|||||||
{
|
{
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
var lib = ApiEntryPoint.Instance.GetEncodingOptions().H264Encoder;
|
|
||||||
|
|
||||||
if (!string.IsNullOrWhiteSpace(lib))
|
|
||||||
{
|
|
||||||
return lib;
|
|
||||||
}
|
|
||||||
|
|
||||||
return "libx264";
|
return "libx264";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -809,6 +803,46 @@ namespace MediaBrowser.Api.Playback
|
|||||||
return "copy";
|
return "copy";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <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>
|
/// <summary>
|
||||||
/// Gets the input argument.
|
/// Gets the input argument.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -816,7 +850,7 @@ namespace MediaBrowser.Api.Playback
|
|||||||
/// <returns>System.String.</returns>
|
/// <returns>System.String.</returns>
|
||||||
protected string GetInputArgument(StreamState state)
|
protected string GetInputArgument(StreamState state)
|
||||||
{
|
{
|
||||||
var arg = "-i " + GetInputPathArgument(state);
|
var arg = string.Format("-i {0}", GetInputPathArgument(state));
|
||||||
|
|
||||||
if (state.SubtitleStream != null)
|
if (state.SubtitleStream != null)
|
||||||
{
|
{
|
||||||
@ -826,7 +860,7 @@ namespace MediaBrowser.Api.Playback
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return arg;
|
return arg.Trim();
|
||||||
}
|
}
|
||||||
|
|
||||||
private string GetInputPathArgument(StreamState state)
|
private string GetInputPathArgument(StreamState state)
|
||||||
@ -840,7 +874,7 @@ namespace MediaBrowser.Api.Playback
|
|||||||
{
|
{
|
||||||
if (!(state.VideoType == VideoType.Iso && state.IsoMount == null))
|
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 +923,7 @@ namespace MediaBrowser.Api.Playback
|
|||||||
CancellationTokenSource cancellationTokenSource,
|
CancellationTokenSource cancellationTokenSource,
|
||||||
string workingDirectory = null)
|
string workingDirectory = null)
|
||||||
{
|
{
|
||||||
Directory.CreateDirectory(Path.GetDirectoryName(outputPath));
|
FileSystem.CreateDirectory(Path.GetDirectoryName(outputPath));
|
||||||
|
|
||||||
await AcquireResources(state, cancellationTokenSource).ConfigureAwait(false);
|
await AcquireResources(state, cancellationTokenSource).ConfigureAwait(false);
|
||||||
|
|
||||||
@ -942,7 +976,7 @@ namespace MediaBrowser.Api.Playback
|
|||||||
Logger.Info(commandLineLogMessage);
|
Logger.Info(commandLineLogMessage);
|
||||||
|
|
||||||
var logFilePath = Path.Combine(ServerConfigurationManager.ApplicationPaths.LogDirectoryPath, "transcode-" + Guid.NewGuid() + ".txt");
|
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.
|
// 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);
|
state.LogFileStream = FileSystem.GetFileStream(logFilePath, FileMode.Create, FileAccess.Write, FileShare.Read, true);
|
||||||
@ -972,7 +1006,7 @@ namespace MediaBrowser.Api.Playback
|
|||||||
StartStreamingLog(transcodingJob, state, process.StandardError.BaseStream, state.LogFileStream);
|
StartStreamingLog(transcodingJob, state, process.StandardError.BaseStream, state.LogFileStream);
|
||||||
|
|
||||||
// Wait for the file to exist before proceeeding
|
// 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);
|
await Task.Delay(100, cancellationTokenSource.Token).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
@ -1027,6 +1061,7 @@ namespace MediaBrowser.Api.Playback
|
|||||||
var bytes = Encoding.UTF8.GetBytes(Environment.NewLine + line);
|
var bytes = Encoding.UTF8.GetBytes(Environment.NewLine + line);
|
||||||
|
|
||||||
await target.WriteAsync(bytes, 0, bytes.Length).ConfigureAwait(false);
|
await target.WriteAsync(bytes, 0, bytes.Length).ConfigureAwait(false);
|
||||||
|
await target.FlushAsync().ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1151,15 +1186,8 @@ namespace MediaBrowser.Api.Playback
|
|||||||
|
|
||||||
if (bitrate.HasValue)
|
if (bitrate.HasValue)
|
||||||
{
|
{
|
||||||
var hasFixedResolution = state.VideoRequest.HasFixedResolution;
|
|
||||||
|
|
||||||
if (string.Equals(videoCodec, "libvpx", StringComparison.OrdinalIgnoreCase))
|
if (string.Equals(videoCodec, "libvpx", StringComparison.OrdinalIgnoreCase))
|
||||||
{
|
{
|
||||||
if (hasFixedResolution)
|
|
||||||
{
|
|
||||||
return string.Format(" -minrate:v ({0}*.90) -maxrate:v ({0}*1.10) -bufsize:v {0} -b:v {0}", bitrate.Value.ToString(UsCulture));
|
|
||||||
}
|
|
||||||
|
|
||||||
// With vpx when crf is used, b:v becomes a max rate
|
// With vpx when crf is used, b:v becomes a max rate
|
||||||
// https://trac.ffmpeg.org/wiki/vpxEncodingGuide. But higher bitrate source files -b:v causes judder so limite the bitrate but dont allow it to "saturate" the bitrate. So dont contrain it down just up.
|
// https://trac.ffmpeg.org/wiki/vpxEncodingGuide. But higher bitrate source files -b:v causes judder so limite the bitrate but dont allow it to "saturate" the bitrate. So dont contrain it down just up.
|
||||||
return string.Format(" -maxrate:v {0} -bufsize:v ({0}*2) -b:v {0}", bitrate.Value.ToString(UsCulture));
|
return string.Format(" -maxrate:v {0} -bufsize:v ({0}*2) -b:v {0}", bitrate.Value.ToString(UsCulture));
|
||||||
@ -1170,36 +1198,15 @@ namespace MediaBrowser.Api.Playback
|
|||||||
return string.Format(" -b:v {0}", bitrate.Value.ToString(UsCulture));
|
return string.Format(" -b:v {0}", bitrate.Value.ToString(UsCulture));
|
||||||
}
|
}
|
||||||
|
|
||||||
// h264_qsv
|
// h264
|
||||||
if (string.Equals(videoCodec, "h264_qsv", StringComparison.OrdinalIgnoreCase) || string.Equals(videoCodec, "libnvenc", StringComparison.OrdinalIgnoreCase))
|
if (isHls)
|
||||||
{
|
{
|
||||||
if (hasFixedResolution)
|
return string.Format(" -b:v {0} -maxrate {0} -bufsize {1}",
|
||||||
{
|
bitrate.Value.ToString(UsCulture),
|
||||||
if (isHls)
|
(bitrate.Value * 2).ToString(UsCulture));
|
||||||
{
|
|
||||||
return string.Format(" -b:v {0} -maxrate ({0}*.80) -bufsize {0}", bitrate.Value.ToString(UsCulture));
|
|
||||||
}
|
|
||||||
|
|
||||||
return string.Format(" -b:v {0}", bitrate.Value.ToString(UsCulture));
|
|
||||||
}
|
|
||||||
|
|
||||||
return string.Format(" -b:v {0} -maxrate ({0}*1.2) -bufsize ({0}*2)", bitrate.Value.ToString(UsCulture));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// H264
|
return string.Format(" -b:v {0}", bitrate.Value.ToString(UsCulture));
|
||||||
if (hasFixedResolution)
|
|
||||||
{
|
|
||||||
if (isHls)
|
|
||||||
{
|
|
||||||
return string.Format(" -b:v {0} -maxrate ({0}*.80) -bufsize {0}", bitrate.Value.ToString(UsCulture));
|
|
||||||
}
|
|
||||||
|
|
||||||
return string.Format(" -b:v {0}", bitrate.Value.ToString(UsCulture));
|
|
||||||
}
|
|
||||||
|
|
||||||
return string.Format(" -maxrate {0} -bufsize {1}",
|
|
||||||
bitrate.Value.ToString(UsCulture),
|
|
||||||
(bitrate.Value * 2).ToString(UsCulture));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return string.Empty;
|
return string.Empty;
|
||||||
@ -1986,7 +1993,8 @@ namespace MediaBrowser.Api.Playback
|
|||||||
state.IsTargetCabac,
|
state.IsTargetCabac,
|
||||||
state.TargetRefFrames,
|
state.TargetRefFrames,
|
||||||
state.TargetVideoStreamCount,
|
state.TargetVideoStreamCount,
|
||||||
state.TargetAudioStreamCount);
|
state.TargetAudioStreamCount,
|
||||||
|
state.TargetVideoCodecTag);
|
||||||
|
|
||||||
if (mediaProfile != null)
|
if (mediaProfile != null)
|
||||||
{
|
{
|
||||||
@ -2083,7 +2091,8 @@ namespace MediaBrowser.Api.Playback
|
|||||||
state.IsTargetCabac,
|
state.IsTargetCabac,
|
||||||
state.TargetRefFrames,
|
state.TargetRefFrames,
|
||||||
state.TargetVideoStreamCount,
|
state.TargetVideoStreamCount,
|
||||||
state.TargetAudioStreamCount
|
state.TargetAudioStreamCount,
|
||||||
|
state.TargetVideoCodecTag
|
||||||
|
|
||||||
).FirstOrDefault() ?? string.Empty;
|
).FirstOrDefault() ?? string.Empty;
|
||||||
}
|
}
|
||||||
@ -2158,6 +2167,12 @@ namespace MediaBrowser.Api.Playback
|
|||||||
inputModifier += " -re";
|
inputModifier += " -re";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var videoDecoder = GetVideoDecoder(state);
|
||||||
|
if (!string.IsNullOrWhiteSpace(videoDecoder))
|
||||||
|
{
|
||||||
|
inputModifier += " " + videoDecoder;
|
||||||
|
}
|
||||||
|
|
||||||
return inputModifier;
|
return inputModifier;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -17,6 +17,7 @@ using System.IO;
|
|||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
using CommonIO;
|
||||||
using MimeTypes = MediaBrowser.Model.Net.MimeTypes;
|
using MimeTypes = MediaBrowser.Model.Net.MimeTypes;
|
||||||
|
|
||||||
namespace MediaBrowser.Api.Playback.Dash
|
namespace MediaBrowser.Api.Playback.Dash
|
||||||
@ -174,7 +175,7 @@ namespace MediaBrowser.Api.Playback.Dash
|
|||||||
|
|
||||||
var workingDirectory = Path.Combine(Path.GetDirectoryName(playlistPath), (startNumber == -1 ? 0 : startNumber).ToString(CultureInfo.InvariantCulture));
|
var workingDirectory = Path.Combine(Path.GetDirectoryName(playlistPath), (startNumber == -1 ? 0 : startNumber).ToString(CultureInfo.InvariantCulture));
|
||||||
state.WaitForPath = Path.Combine(workingDirectory, Path.GetFileName(playlistPath));
|
state.WaitForPath = Path.Combine(workingDirectory, Path.GetFileName(playlistPath));
|
||||||
Directory.CreateDirectory(workingDirectory);
|
FileSystem.CreateDirectory(workingDirectory);
|
||||||
job = await StartFfMpeg(state, playlistPath, cancellationTokenSource, workingDirectory).ConfigureAwait(false);
|
job = await StartFfMpeg(state, playlistPath, cancellationTokenSource, workingDirectory).ConfigureAwait(false);
|
||||||
await WaitForMinimumDashSegmentCount(Path.Combine(workingDirectory, Path.GetFileName(playlistPath)), 1, cancellationTokenSource.Token).ConfigureAwait(false);
|
await WaitForMinimumDashSegmentCount(Path.Combine(workingDirectory, Path.GetFileName(playlistPath)), 1, cancellationTokenSource.Token).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
@ -322,14 +323,13 @@ namespace MediaBrowser.Api.Playback.Dash
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static List<FileInfo> GetLastTranscodingFiles(string playlist, string segmentExtension, IFileSystem fileSystem, int count)
|
private static List<FileSystemMetadata> GetLastTranscodingFiles(string playlist, string segmentExtension, IFileSystem fileSystem, int count)
|
||||||
{
|
{
|
||||||
var folder = Path.GetDirectoryName(playlist);
|
var folder = Path.GetDirectoryName(playlist);
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
return new DirectoryInfo(folder)
|
return fileSystem.GetFiles(folder)
|
||||||
.EnumerateFiles("*", SearchOption.AllDirectories)
|
|
||||||
.Where(i => string.Equals(i.Extension, segmentExtension, StringComparison.OrdinalIgnoreCase))
|
.Where(i => string.Equals(i.Extension, segmentExtension, StringComparison.OrdinalIgnoreCase))
|
||||||
.OrderByDescending(fileSystem.GetLastWriteTimeUtc)
|
.OrderByDescending(fileSystem.GetLastWriteTimeUtc)
|
||||||
.Take(count)
|
.Take(count)
|
||||||
@ -337,7 +337,7 @@ namespace MediaBrowser.Api.Playback.Dash
|
|||||||
}
|
}
|
||||||
catch (DirectoryNotFoundException)
|
catch (DirectoryNotFoundException)
|
||||||
{
|
{
|
||||||
return new List<FileInfo>();
|
return new List<FileSystemMetadata>();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -348,20 +348,20 @@ namespace MediaBrowser.Api.Playback.Dash
|
|||||||
if (requestedIndex == -1)
|
if (requestedIndex == -1)
|
||||||
{
|
{
|
||||||
var path = Path.Combine(folder, "0", "stream" + representationId + "-" + "init" + segmentExtension);
|
var path = Path.Combine(folder, "0", "stream" + representationId + "-" + "init" + segmentExtension);
|
||||||
return File.Exists(path) ? path : null;
|
return FileSystem.FileExists(path) ? path : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
try
|
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;
|
int startNumber;
|
||||||
if (int.TryParse(subfolderName, NumberStyles.Any, UsCulture, out startNumber))
|
if (int.TryParse(subfolderName, NumberStyles.Any, UsCulture, out startNumber))
|
||||||
{
|
{
|
||||||
var segmentIndex = requestedIndex - startNumber + 1;
|
var segmentIndex = requestedIndex - startNumber + 1;
|
||||||
var path = Path.Combine(folder, subfolderName, "stream" + representationId + "-" + segmentIndex.ToString("00000", CultureInfo.InvariantCulture) + segmentExtension);
|
var path = Path.Combine(folder, subfolderName, "stream" + representationId + "-" + segmentIndex.ToString("00000", CultureInfo.InvariantCulture) + segmentExtension);
|
||||||
if (File.Exists(path))
|
if (FileSystem.FileExists(path))
|
||||||
{
|
{
|
||||||
return path;
|
return path;
|
||||||
}
|
}
|
||||||
|
@ -15,6 +15,7 @@ using System.IO;
|
|||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
using CommonIO;
|
||||||
|
|
||||||
namespace MediaBrowser.Api.Playback.Hls
|
namespace MediaBrowser.Api.Playback.Hls
|
||||||
{
|
{
|
||||||
@ -23,7 +24,8 @@ namespace MediaBrowser.Api.Playback.Hls
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public abstract class BaseHlsService : BaseStreamingService
|
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 +92,12 @@ namespace MediaBrowser.Api.Playback.Hls
|
|||||||
TranscodingJob job = null;
|
TranscodingJob job = null;
|
||||||
var playlist = state.OutputFilePath;
|
var playlist = state.OutputFilePath;
|
||||||
|
|
||||||
if (!File.Exists(playlist))
|
if (!FileSystem.FileExists(playlist))
|
||||||
{
|
{
|
||||||
await ApiEntryPoint.Instance.TranscodingStartLock.WaitAsync(cancellationTokenSource.Token).ConfigureAwait(false);
|
await ApiEntryPoint.Instance.TranscodingStartLock.WaitAsync(cancellationTokenSource.Token).ConfigureAwait(false);
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
if (!File.Exists(playlist))
|
if (!FileSystem.FileExists(playlist))
|
||||||
{
|
{
|
||||||
// If the playlist doesn't already exist, startup ffmpeg
|
// If the playlist doesn't already exist, startup ffmpeg
|
||||||
try
|
try
|
||||||
@ -150,7 +152,7 @@ namespace MediaBrowser.Api.Playback.Hls
|
|||||||
{
|
{
|
||||||
ApiEntryPoint.Instance.OnTranscodeEndRequest(job);
|
ApiEntryPoint.Instance.OnTranscodeEndRequest(job);
|
||||||
}
|
}
|
||||||
|
|
||||||
return ResultFactory.GetResult(playlistText, MimeTypes.GetMimeType("playlist.m3u8"), new Dictionary<string, string>());
|
return ResultFactory.GetResult(playlistText, MimeTypes.GetMimeType("playlist.m3u8"), new Dictionary<string, string>());
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -317,4 +319,4 @@ namespace MediaBrowser.Api.Playback.Hls
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -20,6 +20,7 @@ using System.Linq;
|
|||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
using CommonIO;
|
||||||
using MimeTypes = MediaBrowser.Model.Net.MimeTypes;
|
using MimeTypes = MediaBrowser.Model.Net.MimeTypes;
|
||||||
|
|
||||||
namespace MediaBrowser.Api.Playback.Hls
|
namespace MediaBrowser.Api.Playback.Hls
|
||||||
@ -165,7 +166,7 @@ namespace MediaBrowser.Api.Playback.Hls
|
|||||||
|
|
||||||
TranscodingJob job = null;
|
TranscodingJob job = null;
|
||||||
|
|
||||||
if (File.Exists(segmentPath))
|
if (FileSystem.FileExists(segmentPath))
|
||||||
{
|
{
|
||||||
job = ApiEntryPoint.Instance.OnTranscodeBeginRequest(playlistPath, TranscodingJobType);
|
job = ApiEntryPoint.Instance.OnTranscodeBeginRequest(playlistPath, TranscodingJobType);
|
||||||
return await GetSegmentResult(state, playlistPath, segmentPath, requestedIndex, job, cancellationToken).ConfigureAwait(false);
|
return await GetSegmentResult(state, playlistPath, segmentPath, requestedIndex, job, cancellationToken).ConfigureAwait(false);
|
||||||
@ -174,7 +175,7 @@ namespace MediaBrowser.Api.Playback.Hls
|
|||||||
await ApiEntryPoint.Instance.TranscodingStartLock.WaitAsync(cancellationTokenSource.Token).ConfigureAwait(false);
|
await ApiEntryPoint.Instance.TranscodingStartLock.WaitAsync(cancellationTokenSource.Token).ConfigureAwait(false);
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
if (File.Exists(segmentPath))
|
if (FileSystem.FileExists(segmentPath))
|
||||||
{
|
{
|
||||||
job = ApiEntryPoint.Instance.OnTranscodeBeginRequest(playlistPath, TranscodingJobType);
|
job = ApiEntryPoint.Instance.OnTranscodeBeginRequest(playlistPath, TranscodingJobType);
|
||||||
return await GetSegmentResult(state, playlistPath, segmentPath, requestedIndex, job, cancellationToken).ConfigureAwait(false);
|
return await GetSegmentResult(state, playlistPath, segmentPath, requestedIndex, job, cancellationToken).ConfigureAwait(false);
|
||||||
@ -354,7 +355,7 @@ namespace MediaBrowser.Api.Playback.Hls
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void DeleteFile(FileInfo file, int retryCount)
|
private void DeleteFile(FileSystemMetadata file, int retryCount)
|
||||||
{
|
{
|
||||||
if (retryCount >= 5)
|
if (retryCount >= 5)
|
||||||
{
|
{
|
||||||
@ -378,7 +379,7 @@ namespace MediaBrowser.Api.Playback.Hls
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static FileInfo GetLastTranscodingFile(string playlist, string segmentExtension, IFileSystem fileSystem)
|
private static FileSystemMetadata GetLastTranscodingFile(string playlist, string segmentExtension, IFileSystem fileSystem)
|
||||||
{
|
{
|
||||||
var folder = Path.GetDirectoryName(playlist);
|
var folder = Path.GetDirectoryName(playlist);
|
||||||
|
|
||||||
@ -386,8 +387,7 @@ namespace MediaBrowser.Api.Playback.Hls
|
|||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
return new DirectoryInfo(folder)
|
return fileSystem.GetFiles(folder)
|
||||||
.EnumerateFiles("*", SearchOption.TopDirectoryOnly)
|
|
||||||
.Where(i => string.Equals(i.Extension, segmentExtension, StringComparison.OrdinalIgnoreCase) && Path.GetFileNameWithoutExtension(i.Name).StartsWith(filePrefix, StringComparison.OrdinalIgnoreCase))
|
.Where(i => string.Equals(i.Extension, segmentExtension, StringComparison.OrdinalIgnoreCase) && Path.GetFileNameWithoutExtension(i.Name).StartsWith(filePrefix, StringComparison.OrdinalIgnoreCase))
|
||||||
.OrderByDescending(fileSystem.GetLastWriteTimeUtc)
|
.OrderByDescending(fileSystem.GetLastWriteTimeUtc)
|
||||||
.FirstOrDefault();
|
.FirstOrDefault();
|
||||||
@ -432,7 +432,7 @@ namespace MediaBrowser.Api.Playback.Hls
|
|||||||
CancellationToken cancellationToken)
|
CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
// If all transcoding has completed, just return immediately
|
// 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);
|
return GetSegmentResult(state, segmentPath, segmentIndex, transcodingJob);
|
||||||
}
|
}
|
||||||
@ -452,7 +452,7 @@ namespace MediaBrowser.Api.Playback.Hls
|
|||||||
// If it appears in the playlist, it's done
|
// If it appears in the playlist, it's done
|
||||||
if (text.IndexOf(segmentFilename, StringComparison.OrdinalIgnoreCase) != -1)
|
if (text.IndexOf(segmentFilename, StringComparison.OrdinalIgnoreCase) != -1)
|
||||||
{
|
{
|
||||||
if (File.Exists(segmentPath))
|
if (FileSystem.FileExists(segmentPath))
|
||||||
{
|
{
|
||||||
return GetSegmentResult(state, segmentPath, segmentIndex, transcodingJob);
|
return GetSegmentResult(state, segmentPath, segmentIndex, transcodingJob);
|
||||||
}
|
}
|
||||||
@ -989,4 +989,4 @@ namespace MediaBrowser.Api.Playback.Hls
|
|||||||
return base.CanStreamCopyVideo(request, videoStream);
|
return base.CanStreamCopyVideo(request, videoStream);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -8,6 +8,7 @@ using MediaBrowser.Model.IO;
|
|||||||
using MediaBrowser.Model.Serialization;
|
using MediaBrowser.Model.Serialization;
|
||||||
using ServiceStack;
|
using ServiceStack;
|
||||||
using System;
|
using System;
|
||||||
|
using CommonIO;
|
||||||
|
|
||||||
namespace MediaBrowser.Api.Playback.Hls
|
namespace MediaBrowser.Api.Playback.Hls
|
||||||
{
|
{
|
||||||
|
@ -57,7 +57,7 @@ namespace MediaBrowser.Api.Playback
|
|||||||
Size = 102400;
|
Size = 102400;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
[Authenticated]
|
[Authenticated]
|
||||||
public class MediaInfoService : BaseApiService
|
public class MediaInfoService : BaseApiService
|
||||||
{
|
{
|
||||||
@ -289,7 +289,7 @@ namespace MediaBrowser.Api.Playback
|
|||||||
if (mediaSource.SupportsDirectStream)
|
if (mediaSource.SupportsDirectStream)
|
||||||
{
|
{
|
||||||
options.MaxBitrate = GetMaxBitrate(maxBitrate);
|
options.MaxBitrate = GetMaxBitrate(maxBitrate);
|
||||||
|
|
||||||
// The MediaSource supports direct stream, now test to see if the client supports it
|
// The MediaSource supports direct stream, now test to see if the client supports it
|
||||||
var streamInfo = string.Equals(item.MediaType, MediaType.Audio, StringComparison.OrdinalIgnoreCase) ?
|
var streamInfo = string.Equals(item.MediaType, MediaType.Audio, StringComparison.OrdinalIgnoreCase) ?
|
||||||
streamBuilder.BuildAudioItem(options) :
|
streamBuilder.BuildAudioItem(options) :
|
||||||
@ -309,7 +309,7 @@ namespace MediaBrowser.Api.Playback
|
|||||||
if (mediaSource.SupportsTranscoding)
|
if (mediaSource.SupportsTranscoding)
|
||||||
{
|
{
|
||||||
options.MaxBitrate = GetMaxBitrate(maxBitrate);
|
options.MaxBitrate = GetMaxBitrate(maxBitrate);
|
||||||
|
|
||||||
// The MediaSource supports direct stream, now test to see if the client supports it
|
// The MediaSource supports direct stream, now test to see if the client supports it
|
||||||
var streamInfo = string.Equals(item.MediaType, MediaType.Audio, StringComparison.OrdinalIgnoreCase) ?
|
var streamInfo = string.Equals(item.MediaType, MediaType.Audio, StringComparison.OrdinalIgnoreCase) ?
|
||||||
streamBuilder.BuildAudioItem(options) :
|
streamBuilder.BuildAudioItem(options) :
|
||||||
@ -336,9 +336,15 @@ namespace MediaBrowser.Api.Playback
|
|||||||
var maxBitrate = clientMaxBitrate;
|
var maxBitrate = clientMaxBitrate;
|
||||||
var remoteClientMaxBitrate = _config.Configuration.RemoteClientBitrateLimit;
|
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;
|
return maxBitrate;
|
||||||
|
@ -11,6 +11,7 @@ using MediaBrowser.Model.IO;
|
|||||||
using MediaBrowser.Model.Serialization;
|
using MediaBrowser.Model.Serialization;
|
||||||
using ServiceStack;
|
using ServiceStack;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using CommonIO;
|
||||||
|
|
||||||
namespace MediaBrowser.Api.Playback.Progressive
|
namespace MediaBrowser.Api.Playback.Progressive
|
||||||
{
|
{
|
||||||
|
@ -17,6 +17,7 @@ using System.Globalization;
|
|||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
using CommonIO;
|
||||||
|
|
||||||
namespace MediaBrowser.Api.Playback.Progressive
|
namespace MediaBrowser.Api.Playback.Progressive
|
||||||
{
|
{
|
||||||
@ -139,7 +140,7 @@ namespace MediaBrowser.Api.Playback.Progressive
|
|||||||
}
|
}
|
||||||
|
|
||||||
var outputPath = state.OutputFilePath;
|
var outputPath = state.OutputFilePath;
|
||||||
var outputPathExists = File.Exists(outputPath);
|
var outputPathExists = FileSystem.FileExists(outputPath);
|
||||||
|
|
||||||
var isTranscodeCached = outputPathExists && !ApiEntryPoint.Instance.HasActiveTranscodingJob(outputPath, TranscodingJobType.Progressive);
|
var isTranscodeCached = outputPathExists && !ApiEntryPoint.Instance.HasActiveTranscodingJob(outputPath, TranscodingJobType.Progressive);
|
||||||
|
|
||||||
@ -325,7 +326,7 @@ namespace MediaBrowser.Api.Playback.Progressive
|
|||||||
{
|
{
|
||||||
TranscodingJob job;
|
TranscodingJob job;
|
||||||
|
|
||||||
if (!File.Exists(outputPath))
|
if (!FileSystem.FileExists(outputPath))
|
||||||
{
|
{
|
||||||
job = await StartFfMpeg(state, outputPath, cancellationTokenSource).ConfigureAwait(false);
|
job = await StartFfMpeg(state, outputPath, cancellationTokenSource).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
|
@ -5,6 +5,7 @@ using System;
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
using CommonIO;
|
||||||
|
|
||||||
namespace MediaBrowser.Api.Playback.Progressive
|
namespace MediaBrowser.Api.Playback.Progressive
|
||||||
{
|
{
|
||||||
|
@ -11,12 +11,14 @@ using MediaBrowser.Model.Serialization;
|
|||||||
using ServiceStack;
|
using ServiceStack;
|
||||||
using System;
|
using System;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
|
using CommonIO;
|
||||||
|
|
||||||
namespace MediaBrowser.Api.Playback.Progressive
|
namespace MediaBrowser.Api.Playback.Progressive
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Class GetVideoStream
|
/// Class GetVideoStream
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
[Route("/Videos/{Id}/stream.mpegts", "GET")]
|
||||||
[Route("/Videos/{Id}/stream.ts", "GET")]
|
[Route("/Videos/{Id}/stream.ts", "GET")]
|
||||||
[Route("/Videos/{Id}/stream.webm", "GET")]
|
[Route("/Videos/{Id}/stream.webm", "GET")]
|
||||||
[Route("/Videos/{Id}/stream.asf", "GET")]
|
[Route("/Videos/{Id}/stream.asf", "GET")]
|
||||||
|
@ -74,7 +74,7 @@ namespace MediaBrowser.Api.Playback
|
|||||||
{
|
{
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
return ReadInputAtNativeFramerate ? 1000 : 0;
|
return 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -457,6 +457,17 @@ namespace MediaBrowser.Api.Playback
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public string TargetVideoCodecTag
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
var stream = VideoStream;
|
||||||
|
return !Request.Static
|
||||||
|
? null
|
||||||
|
: stream == null ? null : stream.CodecTag;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public bool? IsTargetAnamorphic
|
public bool? IsTargetAnamorphic
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
|
@ -40,10 +40,27 @@ namespace MediaBrowser.Api
|
|||||||
/// Gets or sets the user id.
|
/// Gets or sets the user id.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <value>The user id.</value>
|
/// <value>The user id.</value>
|
||||||
[ApiMember(Name = "UserId", Description = "User Id", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")]
|
[ApiMember(Name = "UserId", Description = "User Id", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "POST")]
|
||||||
public string UserId { get; set; }
|
public string UserId { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Route("/Playlists/{Id}/Items/{ItemId}/Move/{NewIndex}", "POST", Summary = "Moves a playlist item")]
|
||||||
|
public class MoveItem : IReturnVoid
|
||||||
|
{
|
||||||
|
[ApiMember(Name = "ItemId", Description = "ItemId", IsRequired = true, DataType = "string", ParameterType = "query", Verb = "POST")]
|
||||||
|
public string ItemId { get; set; }
|
||||||
|
|
||||||
|
[ApiMember(Name = "Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "POST")]
|
||||||
|
public string Id { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the user id.
|
||||||
|
/// </summary>
|
||||||
|
/// <value>The user id.</value>
|
||||||
|
[ApiMember(Name = "NewIndex", Description = "NewIndex", IsRequired = true, DataType = "string", ParameterType = "query", Verb = "POST")]
|
||||||
|
public int NewIndex { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
[Route("/Playlists/{Id}/Items", "DELETE", Summary = "Removes items from a playlist")]
|
[Route("/Playlists/{Id}/Items", "DELETE", Summary = "Removes items from a playlist")]
|
||||||
public class RemoveFromPlaylist : IReturnVoid
|
public class RemoveFromPlaylist : IReturnVoid
|
||||||
{
|
{
|
||||||
@ -105,6 +122,13 @@ namespace MediaBrowser.Api
|
|||||||
_libraryManager = libraryManager;
|
_libraryManager = libraryManager;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void Post(MoveItem request)
|
||||||
|
{
|
||||||
|
var task = _playlistManager.MoveItem(request.Id, request.ItemId, request.NewIndex);
|
||||||
|
|
||||||
|
Task.WaitAll(task);
|
||||||
|
}
|
||||||
|
|
||||||
public async Task<object> Post(CreatePlaylist request)
|
public async Task<object> Post(CreatePlaylist request)
|
||||||
{
|
{
|
||||||
var result = await _playlistManager.CreatePlaylist(new PlaylistCreationRequest
|
var result = await _playlistManager.CreatePlaylist(new PlaylistCreationRequest
|
||||||
|
@ -118,6 +118,14 @@ namespace MediaBrowser.Api
|
|||||||
public string Name { get; set; }
|
public string Name { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Route("/Appstore/Register", "POST", Summary = "Registers an appstore sale")]
|
||||||
|
[Authenticated]
|
||||||
|
public class RegisterAppstoreSale
|
||||||
|
{
|
||||||
|
[ApiMember(Name = "Parameters", Description = "Java representation of parameters to pass through to admin server", IsRequired = true, DataType = "string", ParameterType = "query", Verb = "POST")]
|
||||||
|
public string Parameters { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Class PluginsService
|
/// Class PluginsService
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -265,6 +273,16 @@ namespace MediaBrowser.Api
|
|||||||
return ToOptimizedSerializedResultUsingCache(result);
|
return ToOptimizedSerializedResultUsingCache(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Post app store sale
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="request"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public async Task Post(RegisterAppstoreSale request)
|
||||||
|
{
|
||||||
|
await _securityManager.RegisterAppStoreSale(request.Parameters);
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Posts the specified request.
|
/// Posts the specified request.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -43,7 +43,6 @@ namespace MediaBrowser.Api.Reports
|
|||||||
MusicArtist,
|
MusicArtist,
|
||||||
AudioAlbum,
|
AudioAlbum,
|
||||||
Locked,
|
Locked,
|
||||||
Unidentified,
|
|
||||||
ImagePrimary,
|
ImagePrimary,
|
||||||
ImageBackdrop,
|
ImageBackdrop,
|
||||||
ImageLogo,
|
ImageLogo,
|
||||||
|
@ -17,7 +17,6 @@ namespace MediaBrowser.Api.Reports
|
|||||||
TrailersImage,
|
TrailersImage,
|
||||||
SpecialsImage,
|
SpecialsImage,
|
||||||
LockDataImage,
|
LockDataImage,
|
||||||
UnidentifiedImage,
|
|
||||||
TagsPrimaryImage,
|
TagsPrimaryImage,
|
||||||
TagsBackdropImage,
|
TagsBackdropImage,
|
||||||
TagsLogoImage,
|
TagsLogoImage,
|
||||||
|
@ -105,7 +105,6 @@ namespace MediaBrowser.Api.Reports
|
|||||||
{
|
{
|
||||||
HeaderMetadata.Status,
|
HeaderMetadata.Status,
|
||||||
HeaderMetadata.Locked,
|
HeaderMetadata.Locked,
|
||||||
HeaderMetadata.Unidentified,
|
|
||||||
HeaderMetadata.ImagePrimary,
|
HeaderMetadata.ImagePrimary,
|
||||||
HeaderMetadata.ImageBackdrop,
|
HeaderMetadata.ImageBackdrop,
|
||||||
HeaderMetadata.ImageLogo,
|
HeaderMetadata.ImageLogo,
|
||||||
@ -122,7 +121,6 @@ namespace MediaBrowser.Api.Reports
|
|||||||
{
|
{
|
||||||
HeaderMetadata.Status,
|
HeaderMetadata.Status,
|
||||||
HeaderMetadata.Locked,
|
HeaderMetadata.Locked,
|
||||||
HeaderMetadata.Unidentified,
|
|
||||||
HeaderMetadata.ImagePrimary,
|
HeaderMetadata.ImagePrimary,
|
||||||
HeaderMetadata.ImageBackdrop,
|
HeaderMetadata.ImageBackdrop,
|
||||||
HeaderMetadata.ImageLogo,
|
HeaderMetadata.ImageLogo,
|
||||||
@ -143,7 +141,6 @@ namespace MediaBrowser.Api.Reports
|
|||||||
{
|
{
|
||||||
HeaderMetadata.Status,
|
HeaderMetadata.Status,
|
||||||
HeaderMetadata.Locked,
|
HeaderMetadata.Locked,
|
||||||
HeaderMetadata.Unidentified,
|
|
||||||
HeaderMetadata.ImagePrimary,
|
HeaderMetadata.ImagePrimary,
|
||||||
HeaderMetadata.ImageBackdrop,
|
HeaderMetadata.ImageBackdrop,
|
||||||
HeaderMetadata.ImageLogo,
|
HeaderMetadata.ImageLogo,
|
||||||
@ -161,7 +158,6 @@ namespace MediaBrowser.Api.Reports
|
|||||||
{
|
{
|
||||||
HeaderMetadata.Status,
|
HeaderMetadata.Status,
|
||||||
HeaderMetadata.Locked,
|
HeaderMetadata.Locked,
|
||||||
HeaderMetadata.Unidentified,
|
|
||||||
HeaderMetadata.ImagePrimary,
|
HeaderMetadata.ImagePrimary,
|
||||||
HeaderMetadata.ImageBackdrop,
|
HeaderMetadata.ImageBackdrop,
|
||||||
HeaderMetadata.ImageLogo,
|
HeaderMetadata.ImageLogo,
|
||||||
@ -177,7 +173,6 @@ namespace MediaBrowser.Api.Reports
|
|||||||
{
|
{
|
||||||
HeaderMetadata.Status,
|
HeaderMetadata.Status,
|
||||||
HeaderMetadata.Locked,
|
HeaderMetadata.Locked,
|
||||||
HeaderMetadata.Unidentified,
|
|
||||||
HeaderMetadata.ImagePrimary,
|
HeaderMetadata.ImagePrimary,
|
||||||
HeaderMetadata.ImageBackdrop,
|
HeaderMetadata.ImageBackdrop,
|
||||||
HeaderMetadata.ImageLogo,
|
HeaderMetadata.ImageLogo,
|
||||||
@ -198,7 +193,6 @@ namespace MediaBrowser.Api.Reports
|
|||||||
{
|
{
|
||||||
HeaderMetadata.Status,
|
HeaderMetadata.Status,
|
||||||
HeaderMetadata.Locked,
|
HeaderMetadata.Locked,
|
||||||
HeaderMetadata.Unidentified,
|
|
||||||
HeaderMetadata.ImagePrimary,
|
HeaderMetadata.ImagePrimary,
|
||||||
HeaderMetadata.ImageBackdrop,
|
HeaderMetadata.ImageBackdrop,
|
||||||
HeaderMetadata.ImageLogo,
|
HeaderMetadata.ImageLogo,
|
||||||
@ -223,7 +217,6 @@ namespace MediaBrowser.Api.Reports
|
|||||||
{
|
{
|
||||||
HeaderMetadata.Status,
|
HeaderMetadata.Status,
|
||||||
HeaderMetadata.Locked,
|
HeaderMetadata.Locked,
|
||||||
HeaderMetadata.Unidentified,
|
|
||||||
HeaderMetadata.ImagePrimary,
|
HeaderMetadata.ImagePrimary,
|
||||||
HeaderMetadata.ImageBackdrop,
|
HeaderMetadata.ImageBackdrop,
|
||||||
HeaderMetadata.ImageLogo,
|
HeaderMetadata.ImageLogo,
|
||||||
@ -241,7 +234,6 @@ namespace MediaBrowser.Api.Reports
|
|||||||
{
|
{
|
||||||
HeaderMetadata.Status,
|
HeaderMetadata.Status,
|
||||||
HeaderMetadata.Locked,
|
HeaderMetadata.Locked,
|
||||||
HeaderMetadata.Unidentified,
|
|
||||||
HeaderMetadata.ImagePrimary,
|
HeaderMetadata.ImagePrimary,
|
||||||
HeaderMetadata.ImageBackdrop,
|
HeaderMetadata.ImageBackdrop,
|
||||||
HeaderMetadata.ImageLogo,
|
HeaderMetadata.ImageLogo,
|
||||||
@ -260,7 +252,6 @@ namespace MediaBrowser.Api.Reports
|
|||||||
{
|
{
|
||||||
HeaderMetadata.Status,
|
HeaderMetadata.Status,
|
||||||
HeaderMetadata.Locked,
|
HeaderMetadata.Locked,
|
||||||
HeaderMetadata.Unidentified,
|
|
||||||
HeaderMetadata.ImagePrimary,
|
HeaderMetadata.ImagePrimary,
|
||||||
HeaderMetadata.ImageBackdrop,
|
HeaderMetadata.ImageBackdrop,
|
||||||
HeaderMetadata.ImageLogo,
|
HeaderMetadata.ImageLogo,
|
||||||
@ -284,7 +275,6 @@ namespace MediaBrowser.Api.Reports
|
|||||||
{
|
{
|
||||||
HeaderMetadata.Status,
|
HeaderMetadata.Status,
|
||||||
HeaderMetadata.Locked,
|
HeaderMetadata.Locked,
|
||||||
HeaderMetadata.Unidentified,
|
|
||||||
HeaderMetadata.ImagePrimary,
|
HeaderMetadata.ImagePrimary,
|
||||||
HeaderMetadata.ImageBackdrop,
|
HeaderMetadata.ImageBackdrop,
|
||||||
HeaderMetadata.ImageLogo,
|
HeaderMetadata.ImageLogo,
|
||||||
@ -315,11 +305,9 @@ namespace MediaBrowser.Api.Reports
|
|||||||
{
|
{
|
||||||
HeaderMetadata.Status,
|
HeaderMetadata.Status,
|
||||||
HeaderMetadata.Locked,
|
HeaderMetadata.Locked,
|
||||||
HeaderMetadata.Unidentified,
|
|
||||||
HeaderMetadata.ImagePrimary,
|
HeaderMetadata.ImagePrimary,
|
||||||
HeaderMetadata.ImageBackdrop,
|
HeaderMetadata.ImageBackdrop,
|
||||||
HeaderMetadata.ImageLogo,
|
HeaderMetadata.ImageLogo,
|
||||||
HeaderMetadata.Unidentified,
|
|
||||||
HeaderMetadata.ImagePrimary,
|
HeaderMetadata.ImagePrimary,
|
||||||
HeaderMetadata.ImageBackdrop,
|
HeaderMetadata.ImageBackdrop,
|
||||||
HeaderMetadata.ImageLogo,
|
HeaderMetadata.ImageLogo,
|
||||||
@ -376,12 +364,6 @@ namespace MediaBrowser.Api.Reports
|
|||||||
option.Header.CanGroup = false;
|
option.Header.CanGroup = false;
|
||||||
option.Header.DisplayType = ReportDisplayType.Export;
|
option.Header.DisplayType = ReportDisplayType.Export;
|
||||||
break;
|
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:
|
case HeaderMetadata.ImagePrimary:
|
||||||
option.Column = (i, r) => this.GetBoolString(r.HasImageTagsPrimary);
|
option.Column = (i, r) => this.GetBoolString(r.HasImageTagsPrimary);
|
||||||
option.Header.ItemViewType = ItemViewType.TagsPrimaryImage;
|
option.Header.ItemViewType = ItemViewType.TagsPrimaryImage;
|
||||||
@ -633,7 +615,6 @@ namespace MediaBrowser.Api.Reports
|
|||||||
{
|
{
|
||||||
Id = item.Id.ToString("N"),
|
Id = item.Id.ToString("N"),
|
||||||
HasLockData = item.IsLocked,
|
HasLockData = item.IsLocked,
|
||||||
IsUnidentified = item.IsUnidentified,
|
|
||||||
HasLocalTrailer = hasTrailers != null ? hasTrailers.GetTrailerIds().Count() > 0 : false,
|
HasLocalTrailer = hasTrailers != null ? hasTrailers.GetTrailerIds().Count() > 0 : false,
|
||||||
HasImageTagsPrimary = (item.ImageInfos != null && item.ImageInfos.Count(n => n.Type == ImageType.Primary) > 0),
|
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),
|
HasImageTagsBackdrop = (item.ImageInfos != null && item.ImageInfos.Count(n => n.Type == ImageType.Backdrop) > 0),
|
||||||
|
@ -56,10 +56,6 @@ namespace MediaBrowser.Api.Reports
|
|||||||
/// <value> true if this object has specials, false if not. </value>
|
/// <value> true if this object has specials, false if not. </value>
|
||||||
public bool HasSpecials { get; set; }
|
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>
|
/// <summary> Gets or sets the columns. </summary>
|
||||||
/// <value> The columns. </value>
|
/// <value> The columns. </value>
|
||||||
public List<ReportItem> Columns { get; set; }
|
public List<ReportItem> Columns { get; set; }
|
||||||
|
@ -226,7 +226,6 @@ namespace MediaBrowser.Api.Reports
|
|||||||
NameStartsWithOrGreater = request.NameStartsWithOrGreater,
|
NameStartsWithOrGreater = request.NameStartsWithOrGreater,
|
||||||
HasImdbId = request.HasImdbId,
|
HasImdbId = request.HasImdbId,
|
||||||
IsYearMismatched = request.IsYearMismatched,
|
IsYearMismatched = request.IsYearMismatched,
|
||||||
IsUnidentified = request.IsUnidentified,
|
|
||||||
IsPlaceHolder = request.IsPlaceHolder,
|
IsPlaceHolder = request.IsPlaceHolder,
|
||||||
IsLocked = request.IsLocked,
|
IsLocked = request.IsLocked,
|
||||||
IsInBoxSet = request.IsInBoxSet,
|
IsInBoxSet = request.IsInBoxSet,
|
||||||
|
@ -213,7 +213,7 @@ namespace MediaBrowser.Api.Reports
|
|||||||
};
|
};
|
||||||
foreach (var item in t)
|
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())
|
.Where(n => n.Type == item.ToString())
|
||||||
.GroupBy(x => x.Name)
|
.GroupBy(x => x.Name)
|
||||||
.OrderByDescending(x => x.Count())
|
.OrderByDescending(x => x.Count())
|
||||||
|
@ -124,7 +124,7 @@ namespace MediaBrowser.Api.Social
|
|||||||
Task.WaitAll(task);
|
Task.WaitAll(task);
|
||||||
}
|
}
|
||||||
|
|
||||||
public object Get(GetShareImage request)
|
public async Task<object> Get(GetShareImage request)
|
||||||
{
|
{
|
||||||
var share = _sharingManager.GetShareInfo(request.Id);
|
var share = _sharingManager.GetShareInfo(request.Id);
|
||||||
|
|
||||||
@ -143,7 +143,21 @@ namespace MediaBrowser.Api.Social
|
|||||||
|
|
||||||
if (image != null)
|
if (image != null)
|
||||||
{
|
{
|
||||||
return ToStaticFileResult(image.Path);
|
if (image.IsLocalFile)
|
||||||
|
{
|
||||||
|
return ToStaticFileResult(image.Path);
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
// Don't fail the request over this
|
||||||
|
var updatedImage = await _libraryManager.ConvertImageToLocal(item, image, 0).ConfigureAwait(false);
|
||||||
|
return ToStaticFileResult(updatedImage.Path);
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Grab a dlna icon if nothing else is available
|
// Grab a dlna icon if nothing else is available
|
||||||
|
@ -69,10 +69,11 @@ namespace MediaBrowser.Api
|
|||||||
_config.Configuration.MergeMetadataAndImagesByName = true;
|
_config.Configuration.MergeMetadataAndImagesByName = true;
|
||||||
_config.Configuration.EnableStandaloneMetadata = true;
|
_config.Configuration.EnableStandaloneMetadata = true;
|
||||||
_config.Configuration.EnableLibraryMetadataSubFolder = true;
|
_config.Configuration.EnableLibraryMetadataSubFolder = true;
|
||||||
_config.Configuration.EnableUserSpecificUserViews = true;
|
|
||||||
_config.Configuration.EnableCustomPathSubFolders = true;
|
_config.Configuration.EnableCustomPathSubFolders = true;
|
||||||
_config.Configuration.DisableXmlSavers = true;
|
_config.Configuration.DisableXmlSavers = true;
|
||||||
_config.Configuration.DisableStartupScan = true;
|
_config.Configuration.DisableStartupScan = true;
|
||||||
|
_config.Configuration.EnableUserViews = true;
|
||||||
|
_config.Configuration.EnableDateLastRefresh = true;
|
||||||
_config.SaveConfiguration();
|
_config.SaveConfiguration();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -15,7 +15,9 @@ using System.Linq;
|
|||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
using CommonIO;
|
||||||
using MimeTypes = MediaBrowser.Model.Net.MimeTypes;
|
using MimeTypes = MediaBrowser.Model.Net.MimeTypes;
|
||||||
|
using MediaBrowser.Common.IO;
|
||||||
|
|
||||||
namespace MediaBrowser.Api.Subtitles
|
namespace MediaBrowser.Api.Subtitles
|
||||||
{
|
{
|
||||||
@ -127,14 +129,16 @@ namespace MediaBrowser.Api.Subtitles
|
|||||||
private readonly ISubtitleEncoder _subtitleEncoder;
|
private readonly ISubtitleEncoder _subtitleEncoder;
|
||||||
private readonly IMediaSourceManager _mediaSourceManager;
|
private readonly IMediaSourceManager _mediaSourceManager;
|
||||||
private readonly IProviderManager _providerManager;
|
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;
|
_libraryManager = libraryManager;
|
||||||
_subtitleManager = subtitleManager;
|
_subtitleManager = subtitleManager;
|
||||||
_subtitleEncoder = subtitleEncoder;
|
_subtitleEncoder = subtitleEncoder;
|
||||||
_mediaSourceManager = mediaSourceManager;
|
_mediaSourceManager = mediaSourceManager;
|
||||||
_providerManager = providerManager;
|
_providerManager = providerManager;
|
||||||
|
_fileSystem = fileSystem;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<object> Get(GetSubtitlePlaylist request)
|
public async Task<object> Get(GetSubtitlePlaylist request)
|
||||||
@ -259,7 +263,7 @@ namespace MediaBrowser.Api.Subtitles
|
|||||||
await _subtitleManager.DownloadSubtitles(video, request.SubtitleId, CancellationToken.None)
|
await _subtitleManager.DownloadSubtitles(video, request.SubtitleId, CancellationToken.None)
|
||||||
.ConfigureAwait(false);
|
.ConfigureAwait(false);
|
||||||
|
|
||||||
_providerManager.QueueRefresh(video.Id, new MetadataRefreshOptions());
|
_providerManager.QueueRefresh(video.Id, new MetadataRefreshOptions(_fileSystem));
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
|
@ -244,7 +244,15 @@ namespace MediaBrowser.Api.Sync
|
|||||||
var task = _syncManager.ReportSyncJobItemTransferBeginning(request.Id);
|
var task = _syncManager.ReportSyncJobItemTransferBeginning(request.Id);
|
||||||
Task.WaitAll(task);
|
Task.WaitAll(task);
|
||||||
|
|
||||||
return ToStaticFileResult(jobItem.OutputPath);
|
return ResultFactory.GetStaticFileResult(Request, new StaticFileResultOptions
|
||||||
|
{
|
||||||
|
Path = jobItem.OutputPath,
|
||||||
|
OnError = () =>
|
||||||
|
{
|
||||||
|
var failedTask = _syncManager.ReportSyncJobItemTransferFailed(request.Id);
|
||||||
|
Task.WaitAll(failedTask);
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public object Get(GetSyncDialogOptions request)
|
public object Get(GetSyncDialogOptions request)
|
||||||
|
@ -12,6 +12,7 @@ using System.Collections.Generic;
|
|||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
using CommonIO;
|
||||||
|
|
||||||
namespace MediaBrowser.Api.System
|
namespace MediaBrowser.Api.System
|
||||||
{
|
{
|
||||||
@ -118,18 +119,17 @@ namespace MediaBrowser.Api.System
|
|||||||
|
|
||||||
public object Get(GetServerLogs request)
|
public object Get(GetServerLogs request)
|
||||||
{
|
{
|
||||||
List<FileInfo> files;
|
List<FileSystemMetadata> files;
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
files = new DirectoryInfo(_appPaths.LogDirectoryPath)
|
files = _fileSystem.GetFiles(_appPaths.LogDirectoryPath)
|
||||||
.EnumerateFiles("*", SearchOption.AllDirectories)
|
|
||||||
.Where(i => string.Equals(i.Extension, ".txt", StringComparison.OrdinalIgnoreCase))
|
.Where(i => string.Equals(i.Extension, ".txt", StringComparison.OrdinalIgnoreCase))
|
||||||
.ToList();
|
.ToList();
|
||||||
}
|
}
|
||||||
catch (DirectoryNotFoundException)
|
catch (DirectoryNotFoundException)
|
||||||
{
|
{
|
||||||
files = new List<FileInfo>();
|
files = new List<FileSystemMetadata>();
|
||||||
}
|
}
|
||||||
|
|
||||||
var result = files.Select(i => new LogFile
|
var result = files.Select(i => new LogFile
|
||||||
@ -149,8 +149,7 @@ namespace MediaBrowser.Api.System
|
|||||||
|
|
||||||
public object Get(GetLogFile request)
|
public object Get(GetLogFile request)
|
||||||
{
|
{
|
||||||
var file = new DirectoryInfo(_appPaths.LogDirectoryPath)
|
var file = _fileSystem.GetFiles(_appPaths.LogDirectoryPath)
|
||||||
.EnumerateFiles("*", SearchOption.AllDirectories)
|
|
||||||
.First(i => string.Equals(i.Name, request.Name, StringComparison.OrdinalIgnoreCase));
|
.First(i => string.Equals(i.Name, request.Name, StringComparison.OrdinalIgnoreCase));
|
||||||
|
|
||||||
return ResultFactory.GetStaticFileResult(Request, file.FullName, FileShare.ReadWrite);
|
return ResultFactory.GetStaticFileResult(Request, file.FullName, FileShare.ReadWrite);
|
||||||
|
@ -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")]
|
[ApiMember(Name = "IsLocked", Description = "Optional filter by items that are locked.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")]
|
||||||
public bool? IsLocked { get; set; }
|
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")]
|
[ApiMember(Name = "IsPlaceHolder", Description = "Optional filter by items that are placeholders", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")]
|
||||||
public bool? IsPlaceHolder { get; set; }
|
public bool? IsPlaceHolder { get; set; }
|
||||||
|
|
||||||
|
@ -192,7 +192,6 @@ namespace MediaBrowser.Api.UserLibrary
|
|||||||
NameStartsWithOrGreater = request.NameStartsWithOrGreater,
|
NameStartsWithOrGreater = request.NameStartsWithOrGreater,
|
||||||
HasImdbId = request.HasImdbId,
|
HasImdbId = request.HasImdbId,
|
||||||
IsYearMismatched = request.IsYearMismatched,
|
IsYearMismatched = request.IsYearMismatched,
|
||||||
IsUnidentified = request.IsUnidentified,
|
|
||||||
IsPlaceHolder = request.IsPlaceHolder,
|
IsPlaceHolder = request.IsPlaceHolder,
|
||||||
IsLocked = request.IsLocked,
|
IsLocked = request.IsLocked,
|
||||||
IsInBoxSet = request.IsInBoxSet,
|
IsInBoxSet = request.IsInBoxSet,
|
||||||
|
@ -39,6 +39,17 @@ namespace MediaBrowser.Api.UserLibrary
|
|||||||
public string UserId { get; set; }
|
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
|
public class UserViewsService : BaseApiService
|
||||||
{
|
{
|
||||||
private readonly IUserManager _userManager;
|
private readonly IUserManager _userManager;
|
||||||
@ -105,6 +116,29 @@ namespace MediaBrowser.Api.UserLibrary
|
|||||||
return ToOptimizedResult(list);
|
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)
|
private bool IsEligibleForSpecialView(ICollectionFolder view)
|
||||||
{
|
{
|
||||||
var types = new[] { CollectionType.Movies, CollectionType.TvShows, CollectionType.Games, CollectionType.Music, CollectionType.Photos };
|
var types = new[] { CollectionType.Movies, CollectionType.TvShows, CollectionType.Games, CollectionType.Music, CollectionType.Photos };
|
||||||
|
@ -11,6 +11,7 @@ using System;
|
|||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
using CommonIO;
|
||||||
|
|
||||||
namespace MediaBrowser.Api
|
namespace MediaBrowser.Api
|
||||||
{
|
{
|
||||||
|
@ -1,4 +1,6 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<packages>
|
<packages>
|
||||||
<package id="morelinq" version="1.1.0" targetFramework="net45" />
|
<package id="CommonIO" version="1.0.0.5" targetFramework="net45" />
|
||||||
|
<package id="morelinq" version="1.1.1" targetFramework="net45" />
|
||||||
|
<package id="Patterns.Logging" version="1.0.0.2" targetFramework="net45" />
|
||||||
</packages>
|
</packages>
|
@ -7,6 +7,8 @@ using SharpCompress.Reader;
|
|||||||
using SharpCompress.Reader.Zip;
|
using SharpCompress.Reader.Zip;
|
||||||
using System;
|
using System;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
|
using CommonIO;
|
||||||
|
using MediaBrowser.Common.IO;
|
||||||
|
|
||||||
namespace MediaBrowser.Common.Implementations.Archiving
|
namespace MediaBrowser.Common.Implementations.Archiving
|
||||||
{
|
{
|
||||||
@ -15,7 +17,14 @@ namespace MediaBrowser.Common.Implementations.Archiving
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public class ZipClient : IZipClient
|
public class ZipClient : IZipClient
|
||||||
{
|
{
|
||||||
/// <summary>
|
private IFileSystem _fileSystem;
|
||||||
|
|
||||||
|
public ZipClient(IFileSystem fileSystem)
|
||||||
|
{
|
||||||
|
_fileSystem = fileSystem;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
/// Extracts all.
|
/// Extracts all.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="sourceFile">The source file.</param>
|
/// <param name="sourceFile">The source file.</param>
|
||||||
@ -23,7 +32,7 @@ namespace MediaBrowser.Common.Implementations.Archiving
|
|||||||
/// <param name="overwriteExistingFiles">if set to <c>true</c> [overwrite existing files].</param>
|
/// <param name="overwriteExistingFiles">if set to <c>true</c> [overwrite existing files].</param>
|
||||||
public void ExtractAll(string sourceFile, string targetPath, bool overwriteExistingFiles)
|
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);
|
ExtractAll(fileStream, targetPath, overwriteExistingFiles);
|
||||||
}
|
}
|
||||||
@ -73,7 +82,7 @@ namespace MediaBrowser.Common.Implementations.Archiving
|
|||||||
/// <param name="overwriteExistingFiles">if set to <c>true</c> [overwrite existing files].</param>
|
/// <param name="overwriteExistingFiles">if set to <c>true</c> [overwrite existing files].</param>
|
||||||
public void ExtractAllFrom7z(string sourceFile, string targetPath, bool overwriteExistingFiles)
|
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);
|
ExtractAllFrom7z(fileStream, targetPath, overwriteExistingFiles);
|
||||||
}
|
}
|
||||||
@ -112,7 +121,7 @@ namespace MediaBrowser.Common.Implementations.Archiving
|
|||||||
/// <param name="overwriteExistingFiles">if set to <c>true</c> [overwrite existing files].</param>
|
/// <param name="overwriteExistingFiles">if set to <c>true</c> [overwrite existing files].</param>
|
||||||
public void ExtractAllFromTar(string sourceFile, string targetPath, bool overwriteExistingFiles)
|
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);
|
ExtractAllFromTar(fileStream, targetPath, overwriteExistingFiles);
|
||||||
}
|
}
|
||||||
@ -150,7 +159,7 @@ namespace MediaBrowser.Common.Implementations.Archiving
|
|||||||
/// <param name="overwriteExistingFiles">if set to <c>true</c> [overwrite existing files].</param>
|
/// <param name="overwriteExistingFiles">if set to <c>true</c> [overwrite existing files].</param>
|
||||||
public void ExtractAllFromRar(string sourceFile, string targetPath, bool overwriteExistingFiles)
|
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);
|
ExtractAllFromRar(fileStream, targetPath, overwriteExistingFiles);
|
||||||
}
|
}
|
||||||
|
@ -30,6 +30,7 @@ using System.Reflection;
|
|||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
using CommonIO;
|
||||||
|
|
||||||
namespace MediaBrowser.Common.Implementations
|
namespace MediaBrowser.Common.Implementations
|
||||||
{
|
{
|
||||||
@ -93,7 +94,7 @@ namespace MediaBrowser.Common.Implementations
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// The _XML serializer
|
/// The _XML serializer
|
||||||
/// </summary>
|
/// </summary>
|
||||||
protected readonly IXmlSerializer XmlSerializer = new XmlSerializer();
|
protected readonly IXmlSerializer XmlSerializer;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets assemblies that failed to load
|
/// Gets assemblies that failed to load
|
||||||
@ -180,7 +181,7 @@ namespace MediaBrowser.Common.Implementations
|
|||||||
{
|
{
|
||||||
if (_deviceId == null)
|
if (_deviceId == null)
|
||||||
{
|
{
|
||||||
_deviceId = new DeviceId(ApplicationPaths, LogManager.GetLogger("SystemId"));
|
_deviceId = new DeviceId(ApplicationPaths, LogManager.GetLogger("SystemId"), FileSystemManager);
|
||||||
}
|
}
|
||||||
|
|
||||||
return _deviceId.Value;
|
return _deviceId.Value;
|
||||||
@ -199,6 +200,7 @@ namespace MediaBrowser.Common.Implementations
|
|||||||
ILogManager logManager,
|
ILogManager logManager,
|
||||||
IFileSystem fileSystem)
|
IFileSystem fileSystem)
|
||||||
{
|
{
|
||||||
|
XmlSerializer = new MediaBrowser.Common.Implementations.Serialization.XmlSerializer (fileSystem);
|
||||||
FailedAssemblies = new List<string>();
|
FailedAssemblies = new List<string>();
|
||||||
|
|
||||||
ApplicationPaths = applicationPaths;
|
ApplicationPaths = applicationPaths;
|
||||||
@ -320,7 +322,7 @@ namespace MediaBrowser.Common.Implementations
|
|||||||
|
|
||||||
protected virtual IJsonSerializer CreateJsonSerializer()
|
protected virtual IJsonSerializer CreateJsonSerializer()
|
||||||
{
|
{
|
||||||
return new JsonSerializer();
|
return new JsonSerializer(FileSystemManager);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void SetHttpLimit()
|
private void SetHttpLimit()
|
||||||
@ -414,6 +416,8 @@ namespace MediaBrowser.Common.Implementations
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
protected virtual void FindParts()
|
protected virtual void FindParts()
|
||||||
{
|
{
|
||||||
|
RegisterModules();
|
||||||
|
|
||||||
ConfigurationManager.AddParts(GetExports<IConfigurationFactory>());
|
ConfigurationManager.AddParts(GetExports<IConfigurationFactory>());
|
||||||
Plugins = GetExports<IPlugin>();
|
Plugins = GetExports<IPlugin>();
|
||||||
}
|
}
|
||||||
@ -449,7 +453,7 @@ namespace MediaBrowser.Common.Implementations
|
|||||||
|
|
||||||
RegisterSingleInstance<IApplicationPaths>(ApplicationPaths);
|
RegisterSingleInstance<IApplicationPaths>(ApplicationPaths);
|
||||||
|
|
||||||
TaskManager = new TaskManager(ApplicationPaths, JsonSerializer, Logger);
|
TaskManager = new TaskManager(ApplicationPaths, JsonSerializer, Logger, FileSystemManager);
|
||||||
|
|
||||||
RegisterSingleInstance(JsonSerializer);
|
RegisterSingleInstance(JsonSerializer);
|
||||||
RegisterSingleInstance(XmlSerializer);
|
RegisterSingleInstance(XmlSerializer);
|
||||||
@ -473,13 +477,12 @@ namespace MediaBrowser.Common.Implementations
|
|||||||
InstallationManager = new InstallationManager(Logger, this, ApplicationPaths, HttpClient, JsonSerializer, SecurityManager, ConfigurationManager, FileSystemManager);
|
InstallationManager = new InstallationManager(Logger, this, ApplicationPaths, HttpClient, JsonSerializer, SecurityManager, ConfigurationManager, FileSystemManager);
|
||||||
RegisterSingleInstance(InstallationManager);
|
RegisterSingleInstance(InstallationManager);
|
||||||
|
|
||||||
ZipClient = new ZipClient();
|
ZipClient = new ZipClient(FileSystemManager);
|
||||||
RegisterSingleInstance(ZipClient);
|
RegisterSingleInstance(ZipClient);
|
||||||
|
|
||||||
IsoManager = new IsoManager();
|
IsoManager = new IsoManager();
|
||||||
RegisterSingleInstance(IsoManager);
|
RegisterSingleInstance(IsoManager);
|
||||||
|
|
||||||
RegisterModules();
|
|
||||||
return Task.FromResult (true);
|
return Task.FromResult (true);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -522,6 +525,14 @@ namespace MediaBrowser.Common.Implementations
|
|||||||
}
|
}
|
||||||
catch (ReflectionTypeLoadException ex)
|
catch (ReflectionTypeLoadException ex)
|
||||||
{
|
{
|
||||||
|
if (ex.LoaderExceptions != null)
|
||||||
|
{
|
||||||
|
foreach (var loaderException in ex.LoaderExceptions)
|
||||||
|
{
|
||||||
|
Logger.Error("LoaderException: " + loaderException.Message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// If it fails we can still get a list of the Types it was able to resolve
|
// If it fails we can still get a list of the Types it was able to resolve
|
||||||
return ex.Types.Where(t => t != null);
|
return ex.Types.Where(t => t != null);
|
||||||
}
|
}
|
||||||
@ -581,7 +592,7 @@ namespace MediaBrowser.Common.Implementations
|
|||||||
protected void RegisterSingleInstance<T>(T obj, bool manageLifetime = true)
|
protected void RegisterSingleInstance<T>(T obj, bool manageLifetime = true)
|
||||||
where T : class
|
where T : class
|
||||||
{
|
{
|
||||||
Container.RegisterSingle(obj);
|
Container.RegisterSingleton(obj);
|
||||||
|
|
||||||
if (manageLifetime)
|
if (manageLifetime)
|
||||||
{
|
{
|
||||||
@ -607,7 +618,7 @@ namespace MediaBrowser.Common.Implementations
|
|||||||
protected void RegisterSingleInstance<T>(Func<T> func)
|
protected void RegisterSingleInstance<T>(Func<T> func)
|
||||||
where T : class
|
where T : class
|
||||||
{
|
{
|
||||||
Container.RegisterSingle(func);
|
Container.RegisterSingleton(func);
|
||||||
}
|
}
|
||||||
|
|
||||||
void IDependencyContainer.Register(Type typeInterface, Type typeImplementation)
|
void IDependencyContainer.Register(Type typeInterface, Type typeImplementation)
|
||||||
|
@ -9,6 +9,8 @@ using System.Collections.Generic;
|
|||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
|
using CommonIO;
|
||||||
|
using MediaBrowser.Common.Extensions;
|
||||||
|
|
||||||
namespace MediaBrowser.Common.Implementations.Configuration
|
namespace MediaBrowser.Common.Implementations.Configuration
|
||||||
{
|
{
|
||||||
@ -32,7 +34,7 @@ namespace MediaBrowser.Common.Implementations.Configuration
|
|||||||
/// Occurs when [configuration updating].
|
/// Occurs when [configuration updating].
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public event EventHandler<ConfigurationUpdateEventArgs> NamedConfigurationUpdating;
|
public event EventHandler<ConfigurationUpdateEventArgs> NamedConfigurationUpdating;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Occurs when [named configuration updated].
|
/// Occurs when [named configuration updated].
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -54,6 +56,7 @@ namespace MediaBrowser.Common.Implementations.Configuration
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <value>The application paths.</value>
|
/// <value>The application paths.</value>
|
||||||
public IApplicationPaths CommonApplicationPaths { get; private set; }
|
public IApplicationPaths CommonApplicationPaths { get; private set; }
|
||||||
|
public readonly IFileSystem FileSystem;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The _configuration loaded
|
/// The _configuration loaded
|
||||||
@ -87,8 +90,8 @@ namespace MediaBrowser.Common.Implementations.Configuration
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private ConfigurationStore[] _configurationStores = {};
|
private ConfigurationStore[] _configurationStores = { };
|
||||||
private IConfigurationFactory[] _configurationFactories = {};
|
private IConfigurationFactory[] _configurationFactories = { };
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Initializes a new instance of the <see cref="BaseConfigurationManager" /> class.
|
/// Initializes a new instance of the <see cref="BaseConfigurationManager" /> class.
|
||||||
@ -96,10 +99,11 @@ namespace MediaBrowser.Common.Implementations.Configuration
|
|||||||
/// <param name="applicationPaths">The application paths.</param>
|
/// <param name="applicationPaths">The application paths.</param>
|
||||||
/// <param name="logManager">The log manager.</param>
|
/// <param name="logManager">The log manager.</param>
|
||||||
/// <param name="xmlSerializer">The XML serializer.</param>
|
/// <param name="xmlSerializer">The XML serializer.</param>
|
||||||
protected BaseConfigurationManager(IApplicationPaths applicationPaths, ILogManager logManager, IXmlSerializer xmlSerializer)
|
protected BaseConfigurationManager(IApplicationPaths applicationPaths, ILogManager logManager, IXmlSerializer xmlSerializer, IFileSystem fileSystem)
|
||||||
{
|
{
|
||||||
CommonApplicationPaths = applicationPaths;
|
CommonApplicationPaths = applicationPaths;
|
||||||
XmlSerializer = xmlSerializer;
|
XmlSerializer = xmlSerializer;
|
||||||
|
FileSystem = fileSystem;
|
||||||
Logger = logManager.GetLogger(GetType().Name);
|
Logger = logManager.GetLogger(GetType().Name);
|
||||||
|
|
||||||
UpdateCachePath();
|
UpdateCachePath();
|
||||||
@ -199,9 +203,19 @@ namespace MediaBrowser.Common.Implementations.Configuration
|
|||||||
{
|
{
|
||||||
throw new DirectoryNotFoundException(string.Format("{0} does not exist.", newPath));
|
throw new DirectoryNotFoundException(string.Format("{0} does not exist.", newPath));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
EnsureWriteAccess(newPath);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected void EnsureWriteAccess(string path)
|
||||||
|
{
|
||||||
|
var file = Path.Combine(path, Guid.NewGuid().ToString());
|
||||||
|
|
||||||
|
FileSystem.WriteAllText(file, string.Empty);
|
||||||
|
FileSystem.DeleteFile(file);
|
||||||
|
}
|
||||||
|
|
||||||
private readonly ConcurrentDictionary<string, object> _configurations = new ConcurrentDictionary<string, object>();
|
private readonly ConcurrentDictionary<string, object> _configurations = new ConcurrentDictionary<string, object>();
|
||||||
|
|
||||||
private string GetConfigurationFile(string key)
|
private string GetConfigurationFile(string key)
|
||||||
@ -215,9 +229,15 @@ namespace MediaBrowser.Common.Implementations.Configuration
|
|||||||
{
|
{
|
||||||
var file = GetConfigurationFile(key);
|
var file = GetConfigurationFile(key);
|
||||||
|
|
||||||
var configurationType = _configurationStores
|
var configurationInfo = _configurationStores
|
||||||
.First(i => string.Equals(i.Key, key, StringComparison.OrdinalIgnoreCase))
|
.FirstOrDefault(i => string.Equals(i.Key, key, StringComparison.OrdinalIgnoreCase));
|
||||||
.ConfigurationType;
|
|
||||||
|
if (configurationInfo == null)
|
||||||
|
{
|
||||||
|
throw new ResourceNotFoundException("Configuration with key " + key + " not found.");
|
||||||
|
}
|
||||||
|
|
||||||
|
var configurationType = configurationInfo.ConfigurationType;
|
||||||
|
|
||||||
lock (_configurationSyncLock)
|
lock (_configurationSyncLock)
|
||||||
{
|
{
|
||||||
@ -272,7 +292,7 @@ namespace MediaBrowser.Common.Implementations.Configuration
|
|||||||
NewConfiguration = configuration
|
NewConfiguration = configuration
|
||||||
|
|
||||||
}, Logger);
|
}, Logger);
|
||||||
|
|
||||||
_configurations.AddOrUpdate(key, configuration, (k, v) => configuration);
|
_configurations.AddOrUpdate(key, configuration, (k, v) => configuration);
|
||||||
|
|
||||||
var path = GetConfigurationFile(key);
|
var path = GetConfigurationFile(key);
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using MediaBrowser.Common.IO;
|
||||||
|
|
||||||
namespace MediaBrowser.Common.Implementations.Configuration
|
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
|
// Use try/catch to avoid the extra file system lookup using File.Exists
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
buffer = File.ReadAllBytes(path);
|
buffer = File.ReadAllBytes(path);
|
||||||
|
|
||||||
configuration = xmlSerializer.DeserializeFromBytes(type, buffer);
|
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 the file didn't exist before, or if something has changed, re-save
|
||||||
if (buffer == null || !buffer.SequenceEqual(newBytes))
|
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
|
// Save it after load in case we got new items
|
||||||
File.WriteAllBytes(path, newBytes);
|
File.WriteAllBytes(path, newBytes);
|
||||||
}
|
}
|
||||||
|
|
||||||
return configuration;
|
return configuration;
|
||||||
|
@ -3,13 +3,16 @@ using MediaBrowser.Model.Logging;
|
|||||||
using System;
|
using System;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
|
using CommonIO;
|
||||||
|
using MediaBrowser.Common.IO;
|
||||||
|
|
||||||
namespace MediaBrowser.Common.Implementations.Devices
|
namespace MediaBrowser.Common.Implementations.Devices
|
||||||
{
|
{
|
||||||
public class DeviceId
|
public class DeviceId
|
||||||
{
|
{
|
||||||
private readonly IApplicationPaths _appPaths;
|
private readonly IApplicationPaths _appPaths;
|
||||||
private readonly ILogger _logger;
|
private readonly ILogger _logger;
|
||||||
|
private readonly IFileSystem _fileSystem;
|
||||||
|
|
||||||
private readonly object _syncLock = new object();
|
private readonly object _syncLock = new object();
|
||||||
|
|
||||||
@ -24,7 +27,7 @@ namespace MediaBrowser.Common.Implementations.Devices
|
|||||||
{
|
{
|
||||||
lock (_syncLock)
|
lock (_syncLock)
|
||||||
{
|
{
|
||||||
var value = File.ReadAllText(CachePath, Encoding.UTF8);
|
var value = File.ReadAllText(CachePath, Encoding.UTF8);
|
||||||
|
|
||||||
Guid guid;
|
Guid guid;
|
||||||
if (Guid.TryParse(value, out guid))
|
if (Guid.TryParse(value, out guid))
|
||||||
@ -55,11 +58,11 @@ namespace MediaBrowser.Common.Implementations.Devices
|
|||||||
{
|
{
|
||||||
var path = CachePath;
|
var path = CachePath;
|
||||||
|
|
||||||
Directory.CreateDirectory(Path.GetDirectoryName(path));
|
_fileSystem.CreateDirectory(Path.GetDirectoryName(path));
|
||||||
|
|
||||||
lock (_syncLock)
|
lock (_syncLock)
|
||||||
{
|
{
|
||||||
File.WriteAllText(path, id, Encoding.UTF8);
|
_fileSystem.WriteAllText(path, id, Encoding.UTF8);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
@ -88,10 +91,15 @@ namespace MediaBrowser.Common.Implementations.Devices
|
|||||||
|
|
||||||
private string _id;
|
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;
|
_appPaths = appPaths;
|
||||||
_logger = logger;
|
_logger = logger;
|
||||||
|
_fileSystem = fileSystem;
|
||||||
}
|
}
|
||||||
|
|
||||||
public string Value
|
public string Value
|
||||||
|
@ -17,6 +17,7 @@ using System.Net.Cache;
|
|||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
using CommonIO;
|
||||||
|
|
||||||
namespace MediaBrowser.Common.Implementations.HttpClientManager
|
namespace MediaBrowser.Common.Implementations.HttpClientManager
|
||||||
{
|
{
|
||||||
@ -282,8 +283,7 @@ namespace MediaBrowser.Common.Implementations.HttpClientManager
|
|||||||
|
|
||||||
var url = options.Url;
|
var url = options.Url;
|
||||||
var urlHash = url.ToLower().GetMD5().ToString("N");
|
var urlHash = url.ToLower().GetMD5().ToString("N");
|
||||||
var semaphore = GetLock(url);
|
|
||||||
|
|
||||||
var responseCachePath = Path.Combine(_appPaths.CachePath, "httpclient", urlHash);
|
var responseCachePath = Path.Combine(_appPaths.CachePath, "httpclient", urlHash);
|
||||||
|
|
||||||
response = await GetCachedResponse(responseCachePath, options.CacheLength, url).ConfigureAwait(false);
|
response = await GetCachedResponse(responseCachePath, options.CacheLength, url).ConfigureAwait(false);
|
||||||
@ -292,6 +292,8 @@ namespace MediaBrowser.Common.Implementations.HttpClientManager
|
|||||||
return response;
|
return response;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var semaphore = GetLock(url);
|
||||||
|
|
||||||
await semaphore.WaitAsync(options.CancellationToken).ConfigureAwait(false);
|
await semaphore.WaitAsync(options.CancellationToken).ConfigureAwait(false);
|
||||||
|
|
||||||
try
|
try
|
||||||
@ -355,7 +357,7 @@ namespace MediaBrowser.Common.Implementations.HttpClientManager
|
|||||||
|
|
||||||
private async Task CacheResponse(HttpResponseInfo response, string responseCachePath)
|
private async Task CacheResponse(HttpResponseInfo response, string responseCachePath)
|
||||||
{
|
{
|
||||||
Directory.CreateDirectory(Path.GetDirectoryName(responseCachePath));
|
_fileSystem.CreateDirectory(Path.GetDirectoryName(responseCachePath));
|
||||||
|
|
||||||
using (var responseStream = response.Content)
|
using (var responseStream = response.Content)
|
||||||
{
|
{
|
||||||
@ -464,20 +466,11 @@ namespace MediaBrowser.Common.Implementations.HttpClientManager
|
|||||||
}
|
}
|
||||||
catch (OperationCanceledException ex)
|
catch (OperationCanceledException ex)
|
||||||
{
|
{
|
||||||
var exception = GetCancellationException(options.Url, options.CancellationToken, ex);
|
throw GetCancellationException(options, client, options.CancellationToken, ex);
|
||||||
|
|
||||||
var httpException = exception as HttpException;
|
|
||||||
|
|
||||||
if (httpException != null && httpException.IsTimedOut)
|
|
||||||
{
|
|
||||||
client.LastTimeout = DateTime.UtcNow;
|
|
||||||
}
|
|
||||||
|
|
||||||
throw exception;
|
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
throw GetException(ex, options);
|
throw GetException(ex, options, client);
|
||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
{
|
{
|
||||||
@ -488,27 +481,6 @@ namespace MediaBrowser.Common.Implementations.HttpClientManager
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the exception.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="ex">The ex.</param>
|
|
||||||
/// <param name="options">The options.</param>
|
|
||||||
/// <returns>HttpException.</returns>
|
|
||||||
private HttpException GetException(WebException ex, HttpRequestOptions options)
|
|
||||||
{
|
|
||||||
_logger.ErrorException("Error getting response from " + options.Url, ex);
|
|
||||||
|
|
||||||
var exception = new HttpException(ex.Message, ex);
|
|
||||||
|
|
||||||
var response = ex.Response as HttpWebResponse;
|
|
||||||
if (response != null)
|
|
||||||
{
|
|
||||||
exception.StatusCode = response.StatusCode;
|
|
||||||
}
|
|
||||||
|
|
||||||
return exception;
|
|
||||||
}
|
|
||||||
|
|
||||||
private HttpResponseInfo GetResponseInfo(HttpWebResponse httpResponse, Stream content, long? contentLength, IDisposable disposable)
|
private HttpResponseInfo GetResponseInfo(HttpWebResponse httpResponse, Stream content, long? contentLength, IDisposable disposable)
|
||||||
{
|
{
|
||||||
return new HttpResponseInfo(disposable)
|
return new HttpResponseInfo(disposable)
|
||||||
@ -599,7 +571,7 @@ namespace MediaBrowser.Common.Implementations.HttpClientManager
|
|||||||
{
|
{
|
||||||
ValidateParams(options);
|
ValidateParams(options);
|
||||||
|
|
||||||
Directory.CreateDirectory(_appPaths.TempDirectory);
|
_fileSystem.CreateDirectory(_appPaths.TempDirectory);
|
||||||
|
|
||||||
var tempFile = Path.Combine(_appPaths.TempDirectory, Guid.NewGuid() + ".tmp");
|
var tempFile = Path.Combine(_appPaths.TempDirectory, Guid.NewGuid() + ".tmp");
|
||||||
|
|
||||||
@ -624,6 +596,8 @@ namespace MediaBrowser.Common.Implementations.HttpClientManager
|
|||||||
_logger.Info("HttpClientManager.GetTempFileResponse url: {0}", options.Url);
|
_logger.Info("HttpClientManager.GetTempFileResponse url: {0}", options.Url);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var client = GetHttpClient(GetHostFromUrl(options.Url), options.EnableHttpCompression);
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
options.CancellationToken.ThrowIfCancellationRequested();
|
options.CancellationToken.ThrowIfCancellationRequested();
|
||||||
@ -632,7 +606,6 @@ namespace MediaBrowser.Common.Implementations.HttpClientManager
|
|||||||
{
|
{
|
||||||
var httpResponse = (HttpWebResponse)response;
|
var httpResponse = (HttpWebResponse)response;
|
||||||
|
|
||||||
var client = GetHttpClient(GetHostFromUrl(options.Url), options.EnableHttpCompression);
|
|
||||||
EnsureSuccessStatusCode(client, httpResponse, options);
|
EnsureSuccessStatusCode(client, httpResponse, options);
|
||||||
|
|
||||||
options.CancellationToken.ThrowIfCancellationRequested();
|
options.CancellationToken.ThrowIfCancellationRequested();
|
||||||
@ -669,7 +642,7 @@ namespace MediaBrowser.Common.Implementations.HttpClientManager
|
|||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
DeleteTempFile(tempFile);
|
DeleteTempFile(tempFile);
|
||||||
throw GetException(ex, options);
|
throw GetException(ex, options, client);
|
||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
{
|
{
|
||||||
@ -694,14 +667,37 @@ namespace MediaBrowser.Common.Implementations.HttpClientManager
|
|||||||
|
|
||||||
protected static readonly CultureInfo UsCulture = new CultureInfo("en-US");
|
protected static readonly CultureInfo UsCulture = new CultureInfo("en-US");
|
||||||
|
|
||||||
private Exception GetException(Exception ex, HttpRequestOptions options)
|
private Exception GetException(Exception ex, HttpRequestOptions options, HttpClientInfo client)
|
||||||
{
|
{
|
||||||
|
if (ex is HttpException)
|
||||||
|
{
|
||||||
|
return ex;
|
||||||
|
}
|
||||||
|
|
||||||
var webException = ex as WebException
|
var webException = ex as WebException
|
||||||
?? ex.InnerException as WebException;
|
?? ex.InnerException as WebException;
|
||||||
|
|
||||||
if (webException != null)
|
if (webException != null)
|
||||||
{
|
{
|
||||||
return GetException(webException, options);
|
if (options.LogErrors)
|
||||||
|
{
|
||||||
|
_logger.ErrorException("Error getting response from " + options.Url, ex);
|
||||||
|
}
|
||||||
|
|
||||||
|
var exception = new HttpException(ex.Message, ex);
|
||||||
|
|
||||||
|
var response = webException.Response as HttpWebResponse;
|
||||||
|
if (response != null)
|
||||||
|
{
|
||||||
|
exception.StatusCode = response.StatusCode;
|
||||||
|
|
||||||
|
if ((int)response.StatusCode == 429)
|
||||||
|
{
|
||||||
|
client.LastTimeout = DateTime.UtcNow;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return exception;
|
||||||
}
|
}
|
||||||
|
|
||||||
var operationCanceledException = ex as OperationCanceledException
|
var operationCanceledException = ex as OperationCanceledException
|
||||||
@ -709,10 +705,13 @@ namespace MediaBrowser.Common.Implementations.HttpClientManager
|
|||||||
|
|
||||||
if (operationCanceledException != null)
|
if (operationCanceledException != null)
|
||||||
{
|
{
|
||||||
return GetCancellationException(options.Url, options.CancellationToken, operationCanceledException);
|
return GetCancellationException(options, client, options.CancellationToken, operationCanceledException);
|
||||||
}
|
}
|
||||||
|
|
||||||
_logger.ErrorException("Error getting response from " + options.Url, ex);
|
if (options.LogErrors)
|
||||||
|
{
|
||||||
|
_logger.ErrorException("Error getting response from " + options.Url, ex);
|
||||||
|
}
|
||||||
|
|
||||||
return ex;
|
return ex;
|
||||||
}
|
}
|
||||||
@ -784,18 +783,24 @@ namespace MediaBrowser.Common.Implementations.HttpClientManager
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Throws the cancellation exception.
|
/// Throws the cancellation exception.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="url">The URL.</param>
|
/// <param name="options">The options.</param>
|
||||||
|
/// <param name="client">The client.</param>
|
||||||
/// <param name="cancellationToken">The cancellation token.</param>
|
/// <param name="cancellationToken">The cancellation token.</param>
|
||||||
/// <param name="exception">The exception.</param>
|
/// <param name="exception">The exception.</param>
|
||||||
/// <returns>Exception.</returns>
|
/// <returns>Exception.</returns>
|
||||||
private Exception GetCancellationException(string url, CancellationToken cancellationToken, OperationCanceledException exception)
|
private Exception GetCancellationException(HttpRequestOptions options, HttpClientInfo client, CancellationToken cancellationToken, OperationCanceledException exception)
|
||||||
{
|
{
|
||||||
// If the HttpClient's timeout is reached, it will cancel the Task internally
|
// If the HttpClient's timeout is reached, it will cancel the Task internally
|
||||||
if (!cancellationToken.IsCancellationRequested)
|
if (!cancellationToken.IsCancellationRequested)
|
||||||
{
|
{
|
||||||
var msg = string.Format("Connection to {0} timed out", url);
|
var msg = string.Format("Connection to {0} timed out", options.Url);
|
||||||
|
|
||||||
_logger.Error(msg);
|
if (options.LogErrors)
|
||||||
|
{
|
||||||
|
_logger.Error(msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
client.LastTimeout = DateTime.UtcNow;
|
||||||
|
|
||||||
// Throw an HttpException so that the caller doesn't think it was cancelled by user code
|
// Throw an HttpException so that the caller doesn't think it was cancelled by user code
|
||||||
return new HttpException(msg, exception)
|
return new HttpException(msg, exception)
|
||||||
@ -815,12 +820,6 @@ namespace MediaBrowser.Common.Implementations.HttpClientManager
|
|||||||
|
|
||||||
if (!isSuccessful)
|
if (!isSuccessful)
|
||||||
{
|
{
|
||||||
if ((int) statusCode == 429)
|
|
||||||
{
|
|
||||||
client.LastTimeout = DateTime.UtcNow;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (statusCode == HttpStatusCode.RequestEntityTooLarge)
|
|
||||||
if (options.LogErrorResponseBody)
|
if (options.LogErrorResponseBody)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
@ -869,25 +868,11 @@ namespace MediaBrowser.Common.Implementations.HttpClientManager
|
|||||||
Task<WebResponse> asyncTask = Task.Factory.FromAsync<WebResponse>(request.BeginGetResponse, request.EndGetResponse, null);
|
Task<WebResponse> asyncTask = Task.Factory.FromAsync<WebResponse>(request.BeginGetResponse, request.EndGetResponse, null);
|
||||||
|
|
||||||
ThreadPool.RegisterWaitForSingleObject((asyncTask as IAsyncResult).AsyncWaitHandle, TimeoutCallback, request, timeout, true);
|
ThreadPool.RegisterWaitForSingleObject((asyncTask as IAsyncResult).AsyncWaitHandle, TimeoutCallback, request, timeout, true);
|
||||||
asyncTask.ContinueWith(task =>
|
var callback = new TaskCallback { taskCompletion = taskCompletion };
|
||||||
{
|
asyncTask.ContinueWith(callback.OnSuccess, TaskContinuationOptions.NotOnFaulted);
|
||||||
taskCompletion.TrySetResult(task.Result);
|
|
||||||
|
|
||||||
}, TaskContinuationOptions.NotOnFaulted);
|
|
||||||
|
|
||||||
// Handle errors
|
// Handle errors
|
||||||
asyncTask.ContinueWith(task =>
|
asyncTask.ContinueWith(callback.OnError, TaskContinuationOptions.OnlyOnFaulted);
|
||||||
{
|
|
||||||
if (task.Exception != null)
|
|
||||||
{
|
|
||||||
taskCompletion.TrySetException(task.Exception);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
taskCompletion.TrySetException(new List<Exception>());
|
|
||||||
}
|
|
||||||
|
|
||||||
}, TaskContinuationOptions.OnlyOnFaulted);
|
|
||||||
|
|
||||||
return taskCompletion.Task;
|
return taskCompletion.Task;
|
||||||
}
|
}
|
||||||
@ -903,5 +888,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>());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,433 +0,0 @@
|
|||||||
using MediaBrowser.Model.Extensions;
|
|
||||||
using MediaBrowser.Common.IO;
|
|
||||||
using MediaBrowser.Model.Logging;
|
|
||||||
using System;
|
|
||||||
using System.IO;
|
|
||||||
using System.Text;
|
|
||||||
|
|
||||||
namespace MediaBrowser.Common.Implementations.IO
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Class CommonFileSystem
|
|
||||||
/// </summary>
|
|
||||||
public class CommonFileSystem : IFileSystem
|
|
||||||
{
|
|
||||||
protected ILogger Logger;
|
|
||||||
|
|
||||||
private readonly bool _supportsAsyncFileStreams;
|
|
||||||
private char[] _invalidFileNameChars;
|
|
||||||
|
|
||||||
public CommonFileSystem(ILogger logger, bool supportsAsyncFileStreams, bool usePresetInvalidFileNameChars)
|
|
||||||
{
|
|
||||||
Logger = logger;
|
|
||||||
_supportsAsyncFileStreams = supportsAsyncFileStreams;
|
|
||||||
|
|
||||||
SetInvalidFileNameChars(usePresetInvalidFileNameChars);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void SetInvalidFileNameChars(bool usePresetInvalidFileNameChars)
|
|
||||||
{
|
|
||||||
// GetInvalidFileNameChars is less restrictive in Linux/Mac than Windows, this mimic Windows behavior for mono under Linux/Mac.
|
|
||||||
|
|
||||||
if (usePresetInvalidFileNameChars)
|
|
||||||
{
|
|
||||||
_invalidFileNameChars = new char[41] { '\x00', '\x01', '\x02', '\x03', '\x04', '\x05', '\x06', '\x07',
|
|
||||||
'\x08', '\x09', '\x0A', '\x0B', '\x0C', '\x0D', '\x0E', '\x0F', '\x10', '\x11', '\x12',
|
|
||||||
'\x13', '\x14', '\x15', '\x16', '\x17', '\x18', '\x19', '\x1A', '\x1B', '\x1C', '\x1D',
|
|
||||||
'\x1E', '\x1F', '\x22', '\x3C', '\x3E', '\x7C', ':', '*', '?', '\\', '/' };
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
_invalidFileNameChars = Path.GetInvalidFileNameChars();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Determines whether the specified filename is shortcut.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="filename">The filename.</param>
|
|
||||||
/// <returns><c>true</c> if the specified filename is shortcut; otherwise, <c>false</c>.</returns>
|
|
||||||
/// <exception cref="System.ArgumentNullException">filename</exception>
|
|
||||||
public virtual bool IsShortcut(string filename)
|
|
||||||
{
|
|
||||||
if (string.IsNullOrEmpty(filename))
|
|
||||||
{
|
|
||||||
throw new ArgumentNullException("filename");
|
|
||||||
}
|
|
||||||
|
|
||||||
var extension = Path.GetExtension(filename);
|
|
||||||
|
|
||||||
return string.Equals(extension, ".mblink", StringComparison.OrdinalIgnoreCase);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Resolves the shortcut.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="filename">The filename.</param>
|
|
||||||
/// <returns>System.String.</returns>
|
|
||||||
/// <exception cref="System.ArgumentNullException">filename</exception>
|
|
||||||
public virtual string ResolveShortcut(string filename)
|
|
||||||
{
|
|
||||||
if (string.IsNullOrEmpty(filename))
|
|
||||||
{
|
|
||||||
throw new ArgumentNullException("filename");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (string.Equals(Path.GetExtension(filename), ".mblink", StringComparison.OrdinalIgnoreCase))
|
|
||||||
{
|
|
||||||
var path = File.ReadAllText(filename);
|
|
||||||
|
|
||||||
return NormalizePath(path);
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Creates the shortcut.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="shortcutPath">The shortcut path.</param>
|
|
||||||
/// <param name="target">The target.</param>
|
|
||||||
/// <exception cref="System.ArgumentNullException">
|
|
||||||
/// shortcutPath
|
|
||||||
/// or
|
|
||||||
/// target
|
|
||||||
/// </exception>
|
|
||||||
public void CreateShortcut(string shortcutPath, string target)
|
|
||||||
{
|
|
||||||
if (string.IsNullOrEmpty(shortcutPath))
|
|
||||||
{
|
|
||||||
throw new ArgumentNullException("shortcutPath");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (string.IsNullOrEmpty(target))
|
|
||||||
{
|
|
||||||
throw new ArgumentNullException("target");
|
|
||||||
}
|
|
||||||
|
|
||||||
File.WriteAllText(shortcutPath, target);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the file system info.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="path">The path.</param>
|
|
||||||
/// <returns>FileSystemInfo.</returns>
|
|
||||||
public FileSystemInfo GetFileSystemInfo(string path)
|
|
||||||
{
|
|
||||||
if (string.IsNullOrEmpty(path))
|
|
||||||
{
|
|
||||||
throw new ArgumentNullException("path");
|
|
||||||
}
|
|
||||||
|
|
||||||
// Take a guess to try and avoid two file system hits, but we'll double-check by calling Exists
|
|
||||||
if (Path.HasExtension(path))
|
|
||||||
{
|
|
||||||
var fileInfo = new FileInfo(path);
|
|
||||||
|
|
||||||
if (fileInfo.Exists)
|
|
||||||
{
|
|
||||||
return fileInfo;
|
|
||||||
}
|
|
||||||
|
|
||||||
return new DirectoryInfo(path);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
var fileInfo = new DirectoryInfo(path);
|
|
||||||
|
|
||||||
if (fileInfo.Exists)
|
|
||||||
{
|
|
||||||
return fileInfo;
|
|
||||||
}
|
|
||||||
|
|
||||||
return new FileInfo(path);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The space char
|
|
||||||
/// </summary>
|
|
||||||
private const char SpaceChar = ' ';
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Takes a filename and removes invalid characters
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="filename">The filename.</param>
|
|
||||||
/// <returns>System.String.</returns>
|
|
||||||
/// <exception cref="System.ArgumentNullException">filename</exception>
|
|
||||||
public string GetValidFilename(string filename)
|
|
||||||
{
|
|
||||||
if (string.IsNullOrEmpty(filename))
|
|
||||||
{
|
|
||||||
throw new ArgumentNullException("filename");
|
|
||||||
}
|
|
||||||
|
|
||||||
var builder = new StringBuilder(filename);
|
|
||||||
|
|
||||||
foreach (var c in _invalidFileNameChars)
|
|
||||||
{
|
|
||||||
builder = builder.Replace(c, SpaceChar);
|
|
||||||
}
|
|
||||||
|
|
||||||
return builder.ToString();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the creation time UTC.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="info">The info.</param>
|
|
||||||
/// <returns>DateTime.</returns>
|
|
||||||
public DateTime GetCreationTimeUtc(FileSystemInfo info)
|
|
||||||
{
|
|
||||||
// This could throw an error on some file systems that have dates out of range
|
|
||||||
try
|
|
||||||
{
|
|
||||||
return info.CreationTimeUtc;
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
Logger.ErrorException("Error determining CreationTimeUtc for {0}", ex, info.FullName);
|
|
||||||
return DateTime.MinValue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the creation time UTC.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="info">The info.</param>
|
|
||||||
/// <returns>DateTime.</returns>
|
|
||||||
public DateTime GetLastWriteTimeUtc(FileSystemInfo info)
|
|
||||||
{
|
|
||||||
// This could throw an error on some file systems that have dates out of range
|
|
||||||
try
|
|
||||||
{
|
|
||||||
return info.LastWriteTimeUtc;
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
Logger.ErrorException("Error determining LastAccessTimeUtc for {0}", ex, info.FullName);
|
|
||||||
return DateTime.MinValue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the last write time UTC.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="path">The path.</param>
|
|
||||||
/// <returns>DateTime.</returns>
|
|
||||||
public DateTime GetLastWriteTimeUtc(string path)
|
|
||||||
{
|
|
||||||
return GetLastWriteTimeUtc(GetFileSystemInfo(path));
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the file stream.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="path">The path.</param>
|
|
||||||
/// <param name="mode">The mode.</param>
|
|
||||||
/// <param name="access">The access.</param>
|
|
||||||
/// <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)
|
|
||||||
{
|
|
||||||
if (_supportsAsyncFileStreams && isAsync)
|
|
||||||
{
|
|
||||||
return new FileStream(path, mode, access, share, StreamDefaults.DefaultFileStreamBufferSize, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
return new FileStream(path, mode, access, share, StreamDefaults.DefaultFileStreamBufferSize);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Swaps the files.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="file1">The file1.</param>
|
|
||||||
/// <param name="file2">The file2.</param>
|
|
||||||
public void SwapFiles(string file1, string file2)
|
|
||||||
{
|
|
||||||
if (string.IsNullOrEmpty(file1))
|
|
||||||
{
|
|
||||||
throw new ArgumentNullException("file1");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (string.IsNullOrEmpty(file2))
|
|
||||||
{
|
|
||||||
throw new ArgumentNullException("file2");
|
|
||||||
}
|
|
||||||
|
|
||||||
var temp1 = Path.GetTempFileName();
|
|
||||||
var temp2 = Path.GetTempFileName();
|
|
||||||
|
|
||||||
// Copying over will fail against hidden files
|
|
||||||
RemoveHiddenAttribute(file1);
|
|
||||||
RemoveHiddenAttribute(file2);
|
|
||||||
|
|
||||||
File.Copy(file1, temp1, true);
|
|
||||||
File.Copy(file2, temp2, true);
|
|
||||||
|
|
||||||
File.Copy(temp1, file2, true);
|
|
||||||
File.Copy(temp2, file1, true);
|
|
||||||
|
|
||||||
DeleteFile(temp1);
|
|
||||||
DeleteFile(temp2);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Removes the hidden attribute.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="path">The path.</param>
|
|
||||||
private void RemoveHiddenAttribute(string path)
|
|
||||||
{
|
|
||||||
if (string.IsNullOrEmpty(path))
|
|
||||||
{
|
|
||||||
throw new ArgumentNullException("path");
|
|
||||||
}
|
|
||||||
|
|
||||||
var currentFile = new FileInfo(path);
|
|
||||||
|
|
||||||
// This will fail if the file is hidden
|
|
||||||
if (currentFile.Exists)
|
|
||||||
{
|
|
||||||
if ((currentFile.Attributes & FileAttributes.Hidden) == FileAttributes.Hidden)
|
|
||||||
{
|
|
||||||
currentFile.Attributes &= ~FileAttributes.Hidden;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool ContainsSubPath(string parentPath, string path)
|
|
||||||
{
|
|
||||||
if (string.IsNullOrEmpty(parentPath))
|
|
||||||
{
|
|
||||||
throw new ArgumentNullException("parentPath");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (string.IsNullOrEmpty(path))
|
|
||||||
{
|
|
||||||
throw new ArgumentNullException("path");
|
|
||||||
}
|
|
||||||
|
|
||||||
return path.IndexOf(parentPath.TrimEnd(Path.DirectorySeparatorChar) + Path.DirectorySeparatorChar, StringComparison.OrdinalIgnoreCase) != -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool IsRootPath(string path)
|
|
||||||
{
|
|
||||||
if (string.IsNullOrEmpty(path))
|
|
||||||
{
|
|
||||||
throw new ArgumentNullException("path");
|
|
||||||
}
|
|
||||||
|
|
||||||
var parent = Path.GetDirectoryName(path);
|
|
||||||
|
|
||||||
if (!string.IsNullOrEmpty(parent))
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
public string NormalizePath(string path)
|
|
||||||
{
|
|
||||||
if (string.IsNullOrEmpty(path))
|
|
||||||
{
|
|
||||||
throw new ArgumentNullException("path");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (path.EndsWith(":\\", StringComparison.OrdinalIgnoreCase))
|
|
||||||
{
|
|
||||||
return path;
|
|
||||||
}
|
|
||||||
|
|
||||||
return path.TrimEnd(Path.DirectorySeparatorChar);
|
|
||||||
}
|
|
||||||
|
|
||||||
public string SubstitutePath(string path, string from, string to)
|
|
||||||
{
|
|
||||||
if (string.IsNullOrWhiteSpace(path))
|
|
||||||
{
|
|
||||||
throw new ArgumentNullException("path");
|
|
||||||
}
|
|
||||||
if (string.IsNullOrWhiteSpace(from))
|
|
||||||
{
|
|
||||||
throw new ArgumentNullException("from");
|
|
||||||
}
|
|
||||||
if (string.IsNullOrWhiteSpace(to))
|
|
||||||
{
|
|
||||||
throw new ArgumentNullException("to");
|
|
||||||
}
|
|
||||||
|
|
||||||
var newPath = path.Replace(from, to, StringComparison.OrdinalIgnoreCase);
|
|
||||||
|
|
||||||
if (!string.Equals(newPath, path))
|
|
||||||
{
|
|
||||||
if (to.IndexOf('/') != -1)
|
|
||||||
{
|
|
||||||
newPath = newPath.Replace('\\', '/');
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
newPath = newPath.Replace('/', '\\');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return newPath;
|
|
||||||
}
|
|
||||||
|
|
||||||
public string GetFileNameWithoutExtension(FileSystemInfo info)
|
|
||||||
{
|
|
||||||
if (info is DirectoryInfo)
|
|
||||||
{
|
|
||||||
return info.Name;
|
|
||||||
}
|
|
||||||
|
|
||||||
return Path.GetFileNameWithoutExtension(info.FullName);
|
|
||||||
}
|
|
||||||
|
|
||||||
public string GetFileNameWithoutExtension(string path)
|
|
||||||
{
|
|
||||||
return Path.GetFileNameWithoutExtension(path);
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool IsPathFile(string path)
|
|
||||||
{
|
|
||||||
if (string.IsNullOrWhiteSpace(path))
|
|
||||||
{
|
|
||||||
throw new ArgumentNullException("path");
|
|
||||||
}
|
|
||||||
|
|
||||||
// Cannot use Path.IsPathRooted because it returns false under mono when using windows-based paths, e.g. C:\\
|
|
||||||
|
|
||||||
if (path.IndexOf("://", StringComparison.OrdinalIgnoreCase) != -1 &&
|
|
||||||
!path.StartsWith("file://", StringComparison.OrdinalIgnoreCase))
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
|
|
||||||
//return Path.IsPathRooted(path);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void DeleteFile(string path, bool sendToRecycleBin)
|
|
||||||
{
|
|
||||||
File.Delete(path);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void DeleteDirectory(string path, bool recursive, bool sendToRecycleBin)
|
|
||||||
{
|
|
||||||
Directory.Delete(path, recursive);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void DeleteFile(string path)
|
|
||||||
{
|
|
||||||
DeleteFile(path, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void DeleteDirectory(string path, bool recursive)
|
|
||||||
{
|
|
||||||
DeleteDirectory(path, recursive, false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -170,7 +170,7 @@ namespace MediaBrowser.Common.Implementations.Logging
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="name">The name.</param>
|
/// <param name="name">The name.</param>
|
||||||
/// <returns>ILogger.</returns>
|
/// <returns>ILogger.</returns>
|
||||||
public ILogger GetLogger(string name)
|
public Model.Logging.ILogger GetLogger(string name)
|
||||||
{
|
{
|
||||||
return new NLogger(name, this);
|
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");
|
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);
|
AddFileTarget(LogFilePath, level);
|
||||||
|
|
||||||
|
@ -14,7 +14,6 @@
|
|||||||
<ProductVersion>10.0.0</ProductVersion>
|
<ProductVersion>10.0.0</ProductVersion>
|
||||||
<SchemaVersion>2.0</SchemaVersion>
|
<SchemaVersion>2.0</SchemaVersion>
|
||||||
<TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
|
<TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
|
||||||
<RestorePackages>true</RestorePackages>
|
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
|
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
|
||||||
<DebugSymbols>true</DebugSymbols>
|
<DebugSymbols>true</DebugSymbols>
|
||||||
@ -48,18 +47,24 @@
|
|||||||
<RunPostBuildEvent>Always</RunPostBuildEvent>
|
<RunPostBuildEvent>Always</RunPostBuildEvent>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Reference Include="NLog, Version=3.2.1.0, Culture=neutral, PublicKeyToken=5120e14c03d0593c, processorArchitecture=MSIL">
|
<Reference Include="CommonIO, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL">
|
||||||
<SpecificVersion>False</SpecificVersion>
|
<SpecificVersion>False</SpecificVersion>
|
||||||
<HintPath>..\packages\NLog.3.2.1\lib\net45\NLog.dll</HintPath>
|
<HintPath>..\packages\CommonIO.1.0.0.5\lib\net45\CommonIO.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="NLog, Version=4.0.0.0, Culture=neutral, PublicKeyToken=5120e14c03d0593c, processorArchitecture=MSIL">
|
||||||
|
<SpecificVersion>False</SpecificVersion>
|
||||||
|
<HintPath>..\packages\NLog.4.1.1\lib\net45\NLog.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>
|
||||||
</Reference>
|
</Reference>
|
||||||
<Reference Include="SharpCompress, Version=0.10.2.0, Culture=neutral, PublicKeyToken=beaf6f427e128133, processorArchitecture=MSIL">
|
<Reference Include="SharpCompress, Version=0.10.2.0, Culture=neutral, PublicKeyToken=beaf6f427e128133, processorArchitecture=MSIL">
|
||||||
<SpecificVersion>False</SpecificVersion>
|
<SpecificVersion>False</SpecificVersion>
|
||||||
<HintPath>..\ThirdParty\SharpCompress\SharpCompress.dll</HintPath>
|
<HintPath>..\ThirdParty\SharpCompress\SharpCompress.dll</HintPath>
|
||||||
</Reference>
|
</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>
|
<SpecificVersion>False</SpecificVersion>
|
||||||
<HintPath>..\packages\SimpleInjector.2.8.0\lib\net45\SimpleInjector.dll</HintPath>
|
<HintPath>..\packages\SimpleInjector.3.0.5\lib\net45\SimpleInjector.dll</HintPath>
|
||||||
<Private>True</Private>
|
|
||||||
</Reference>
|
</Reference>
|
||||||
<Reference Include="System" />
|
<Reference Include="System" />
|
||||||
<Reference Include="System.Core" />
|
<Reference Include="System.Core" />
|
||||||
@ -82,7 +87,6 @@
|
|||||||
<Compile Include="Devices\DeviceId.cs" />
|
<Compile Include="Devices\DeviceId.cs" />
|
||||||
<Compile Include="HttpClientManager\HttpClientInfo.cs" />
|
<Compile Include="HttpClientManager\HttpClientInfo.cs" />
|
||||||
<Compile Include="HttpClientManager\HttpClientManager.cs" />
|
<Compile Include="HttpClientManager\HttpClientManager.cs" />
|
||||||
<Compile Include="IO\CommonFileSystem.cs" />
|
|
||||||
<Compile Include="IO\IsoManager.cs" />
|
<Compile Include="IO\IsoManager.cs" />
|
||||||
<Compile Include="Logging\LogHelper.cs" />
|
<Compile Include="Logging\LogHelper.cs" />
|
||||||
<Compile Include="Logging\NLogger.cs" />
|
<Compile Include="Logging\NLogger.cs" />
|
||||||
|
@ -12,6 +12,7 @@ using System.IO;
|
|||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
using CommonIO;
|
||||||
|
|
||||||
namespace MediaBrowser.Common.Implementations.ScheduledTasks
|
namespace MediaBrowser.Common.Implementations.ScheduledTasks
|
||||||
{
|
{
|
||||||
@ -51,6 +52,7 @@ namespace MediaBrowser.Common.Implementations.ScheduledTasks
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <value>The task manager.</value>
|
/// <value>The task manager.</value>
|
||||||
private ITaskManager TaskManager { get; set; }
|
private ITaskManager TaskManager { get; set; }
|
||||||
|
private readonly IFileSystem _fileSystem;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Initializes a new instance of the <see cref="ScheduledTaskWorker" /> class.
|
/// Initializes a new instance of the <see cref="ScheduledTaskWorker" /> class.
|
||||||
@ -71,7 +73,7 @@ namespace MediaBrowser.Common.Implementations.ScheduledTasks
|
|||||||
/// or
|
/// or
|
||||||
/// logger
|
/// logger
|
||||||
/// </exception>
|
/// </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)
|
if (scheduledTask == null)
|
||||||
{
|
{
|
||||||
@ -99,6 +101,7 @@ namespace MediaBrowser.Common.Implementations.ScheduledTasks
|
|||||||
TaskManager = taskManager;
|
TaskManager = taskManager;
|
||||||
JsonSerializer = jsonSerializer;
|
JsonSerializer = jsonSerializer;
|
||||||
Logger = logger;
|
Logger = logger;
|
||||||
|
_fileSystem = fileSystem;
|
||||||
|
|
||||||
ReloadTriggerEvents(true);
|
ReloadTriggerEvents(true);
|
||||||
}
|
}
|
||||||
@ -154,7 +157,7 @@ namespace MediaBrowser.Common.Implementations.ScheduledTasks
|
|||||||
_lastExecutionResult = value;
|
_lastExecutionResult = value;
|
||||||
|
|
||||||
var path = GetHistoryFilePath();
|
var path = GetHistoryFilePath();
|
||||||
Directory.CreateDirectory(Path.GetDirectoryName(path));
|
_fileSystem.CreateDirectory(Path.GetDirectoryName(path));
|
||||||
|
|
||||||
lock (_lastExecutionResultSyncLock)
|
lock (_lastExecutionResultSyncLock)
|
||||||
{
|
{
|
||||||
@ -300,6 +303,11 @@ namespace MediaBrowser.Common.Implementations.ScheduledTasks
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void ReloadTriggerEvents()
|
||||||
|
{
|
||||||
|
ReloadTriggerEvents(false);
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Reloads the trigger events.
|
/// Reloads the trigger events.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -552,7 +560,7 @@ namespace MediaBrowser.Common.Implementations.ScheduledTasks
|
|||||||
{
|
{
|
||||||
var path = GetConfigurationFilePath();
|
var path = GetConfigurationFilePath();
|
||||||
|
|
||||||
Directory.CreateDirectory(Path.GetDirectoryName(path));
|
_fileSystem.CreateDirectory(Path.GetDirectoryName(path));
|
||||||
|
|
||||||
JsonSerializer.SerializeToFile(triggers.Select(ScheduledTaskHelpers.GetTriggerInfo), path);
|
JsonSerializer.SerializeToFile(triggers.Select(ScheduledTaskHelpers.GetTriggerInfo), path);
|
||||||
}
|
}
|
||||||
@ -622,7 +630,7 @@ namespace MediaBrowser.Common.Implementations.ScheduledTasks
|
|||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
Logger.Debug(Name + ": Cancelling");
|
Logger.Info(Name + ": Cancelling");
|
||||||
token.Cancel();
|
token.Cancel();
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
@ -635,16 +643,16 @@ namespace MediaBrowser.Common.Implementations.ScheduledTasks
|
|||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
Logger.Debug(Name + ": Waiting on Task");
|
Logger.Info(Name + ": Waiting on Task");
|
||||||
var exited = Task.WaitAll(new[] { task }, 2000);
|
var exited = Task.WaitAll(new[] { task }, 2000);
|
||||||
|
|
||||||
if (exited)
|
if (exited)
|
||||||
{
|
{
|
||||||
Logger.Debug(Name + ": Task exited");
|
Logger.Info(Name + ": Task exited");
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
Logger.Debug(Name + ": Timed out waiting for task to stop");
|
Logger.Info(Name + ": Timed out waiting for task to stop");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
|
@ -10,6 +10,9 @@ using System.Collections.Concurrent;
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
using CommonIO;
|
||||||
|
using MediaBrowser.Common.IO;
|
||||||
|
using Microsoft.Win32;
|
||||||
|
|
||||||
namespace MediaBrowser.Common.Implementations.ScheduledTasks
|
namespace MediaBrowser.Common.Implementations.ScheduledTasks
|
||||||
{
|
{
|
||||||
@ -50,6 +53,7 @@ namespace MediaBrowser.Common.Implementations.ScheduledTasks
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <value>The logger.</value>
|
/// <value>The logger.</value>
|
||||||
private ILogger Logger { get; set; }
|
private ILogger Logger { get; set; }
|
||||||
|
private readonly IFileSystem _fileSystem;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Initializes a new instance of the <see cref="TaskManager" /> class.
|
/// Initializes a new instance of the <see cref="TaskManager" /> class.
|
||||||
@ -58,13 +62,36 @@ namespace MediaBrowser.Common.Implementations.ScheduledTasks
|
|||||||
/// <param name="jsonSerializer">The json serializer.</param>
|
/// <param name="jsonSerializer">The json serializer.</param>
|
||||||
/// <param name="logger">The logger.</param>
|
/// <param name="logger">The logger.</param>
|
||||||
/// <exception cref="System.ArgumentException">kernel</exception>
|
/// <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;
|
ApplicationPaths = applicationPaths;
|
||||||
JsonSerializer = jsonSerializer;
|
JsonSerializer = jsonSerializer;
|
||||||
Logger = logger;
|
Logger = logger;
|
||||||
|
_fileSystem = fileSystem;
|
||||||
|
|
||||||
ScheduledTasks = new IScheduledTaskWorker[] { };
|
ScheduledTasks = new IScheduledTaskWorker[] { };
|
||||||
|
|
||||||
|
BindToSystemEvent();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void BindToSystemEvent()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
SystemEvents.PowerModeChanged += SystemEvents_PowerModeChanged;
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void SystemEvents_PowerModeChanged(object sender, PowerModeChangedEventArgs e)
|
||||||
|
{
|
||||||
|
foreach (var task in ScheduledTasks)
|
||||||
|
{
|
||||||
|
task.ReloadTriggerEvents();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -106,9 +133,16 @@ namespace MediaBrowser.Common.Implementations.ScheduledTasks
|
|||||||
public void QueueScheduledTask<T>(TaskExecutionOptions options)
|
public void QueueScheduledTask<T>(TaskExecutionOptions options)
|
||||||
where T : IScheduledTask
|
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>()
|
public void QueueScheduledTask<T>()
|
||||||
@ -116,7 +150,7 @@ namespace MediaBrowser.Common.Implementations.ScheduledTasks
|
|||||||
{
|
{
|
||||||
QueueScheduledTask<T>(new TaskExecutionOptions());
|
QueueScheduledTask<T>(new TaskExecutionOptions());
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Queues the scheduled task.
|
/// Queues the scheduled task.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -124,9 +158,16 @@ namespace MediaBrowser.Common.Implementations.ScheduledTasks
|
|||||||
/// <param name="options">The task options.</param>
|
/// <param name="options">The task options.</param>
|
||||||
public void QueueScheduledTask(IScheduledTask task, TaskExecutionOptions options)
|
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>
|
/// <summary>
|
||||||
@ -161,7 +202,7 @@ namespace MediaBrowser.Common.Implementations.ScheduledTasks
|
|||||||
var myTasks = ScheduledTasks.ToList();
|
var myTasks = ScheduledTasks.ToList();
|
||||||
|
|
||||||
var list = tasks.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();
|
ScheduledTasks = myTasks.ToArray();
|
||||||
}
|
}
|
||||||
|
@ -8,6 +8,7 @@ using System.IO;
|
|||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
using CommonIO;
|
||||||
|
|
||||||
namespace MediaBrowser.Common.Implementations.ScheduledTasks.Tasks
|
namespace MediaBrowser.Common.Implementations.ScheduledTasks.Tasks
|
||||||
{
|
{
|
||||||
@ -95,7 +96,7 @@ namespace MediaBrowser.Common.Implementations.ScheduledTasks.Tasks
|
|||||||
/// <param name="progress">The progress.</param>
|
/// <param name="progress">The progress.</param>
|
||||||
private void DeleteCacheFilesFromDirectory(CancellationToken cancellationToken, string directory, DateTime minDateModified, IProgress<double> progress)
|
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)
|
.Where(f => _fileSystem.GetLastWriteTimeUtc(f) < minDateModified)
|
||||||
.ToList();
|
.ToList();
|
||||||
|
|
||||||
@ -120,14 +121,14 @@ namespace MediaBrowser.Common.Implementations.ScheduledTasks.Tasks
|
|||||||
progress.Report(100);
|
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);
|
DeleteEmptyFolders(directory);
|
||||||
if (!Directory.EnumerateFileSystemEntries(directory).Any())
|
if (!_fileSystem.GetFileSystemEntryPaths(directory).Any())
|
||||||
{
|
{
|
||||||
Directory.Delete(directory, false);
|
_fileSystem.DeleteDirectory(directory, false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -7,6 +7,7 @@ using System.IO;
|
|||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
using CommonIO;
|
||||||
|
|
||||||
namespace MediaBrowser.Common.Implementations.ScheduledTasks.Tasks
|
namespace MediaBrowser.Common.Implementations.ScheduledTasks.Tasks
|
||||||
{
|
{
|
||||||
@ -58,7 +59,7 @@ namespace MediaBrowser.Common.Implementations.ScheduledTasks.Tasks
|
|||||||
// Delete log files more than n days old
|
// Delete log files more than n days old
|
||||||
var minDateModified = DateTime.UtcNow.AddDays(-(ConfigurationManager.CommonConfiguration.LogFileRetentionDays));
|
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)
|
.Where(f => _fileSystem.GetLastWriteTimeUtc(f) < minDateModified)
|
||||||
.ToList();
|
.ToList();
|
||||||
|
|
||||||
|
@ -99,7 +99,7 @@ namespace MediaBrowser.Common.Implementations.Security
|
|||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
contents = File.ReadAllLines(licenseFile);
|
contents = File.ReadAllLines(licenseFile);
|
||||||
}
|
}
|
||||||
catch (DirectoryNotFoundException)
|
catch (DirectoryNotFoundException)
|
||||||
{
|
{
|
||||||
@ -107,7 +107,7 @@ namespace MediaBrowser.Common.Implementations.Security
|
|||||||
}
|
}
|
||||||
catch (FileNotFoundException)
|
catch (FileNotFoundException)
|
||||||
{
|
{
|
||||||
(File.Create(licenseFile)).Close();
|
(File.Create(licenseFile)).Close();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (contents != null && contents.Length > 0)
|
if (contents != null && contents.Length > 0)
|
||||||
@ -150,8 +150,8 @@ namespace MediaBrowser.Common.Implementations.Security
|
|||||||
}
|
}
|
||||||
|
|
||||||
var licenseFile = Filename;
|
var licenseFile = Filename;
|
||||||
Directory.CreateDirectory(Path.GetDirectoryName(licenseFile));
|
Directory.CreateDirectory(Path.GetDirectoryName(licenseFile));
|
||||||
lock (_fileLock) File.WriteAllLines(licenseFile, lines);
|
lock (_fileLock) File.WriteAllLines(licenseFile, lines);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
using MediaBrowser.Common.Configuration;
|
using System.IO;
|
||||||
|
using MediaBrowser.Common.Configuration;
|
||||||
using MediaBrowser.Common.Net;
|
using MediaBrowser.Common.Net;
|
||||||
using MediaBrowser.Common.Security;
|
using MediaBrowser.Common.Security;
|
||||||
using MediaBrowser.Model.Entities;
|
using MediaBrowser.Model.Entities;
|
||||||
@ -18,6 +19,7 @@ namespace MediaBrowser.Common.Implementations.Security
|
|||||||
public class PluginSecurityManager : ISecurityManager
|
public class PluginSecurityManager : ISecurityManager
|
||||||
{
|
{
|
||||||
private const string MBValidateUrl = MbAdmin.HttpsUrl + "service/registration/validate";
|
private const string MBValidateUrl = MbAdmin.HttpsUrl + "service/registration/validate";
|
||||||
|
private const string AppstoreRegUrl = /*MbAdmin.HttpsUrl*/ "http://mb3admin.com/admin/" + "service/appstore/register";
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The _is MB supporter
|
/// The _is MB supporter
|
||||||
@ -185,6 +187,70 @@ namespace MediaBrowser.Common.Implementations.Security
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Register an app store sale with our back-end. It will validate the transaction with the store
|
||||||
|
/// and then register the proper feature and then fill in the supporter key on success.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="parameters">Json parameters to send to admin server</param>
|
||||||
|
public async Task RegisterAppStoreSale(string parameters)
|
||||||
|
{
|
||||||
|
var options = new HttpRequestOptions()
|
||||||
|
{
|
||||||
|
Url = AppstoreRegUrl,
|
||||||
|
CancellationToken = CancellationToken.None
|
||||||
|
};
|
||||||
|
options.RequestHeaders.Add("X-Emby-Token", _appHost.SystemId);
|
||||||
|
options.RequestContent = parameters;
|
||||||
|
options.RequestContentType = "application/json";
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
using (var response = await _httpClient.Post(options).ConfigureAwait(false))
|
||||||
|
{
|
||||||
|
var reg = _jsonSerializer.DeserializeFromStream<RegRecord>(response.Content);
|
||||||
|
|
||||||
|
if (reg == null)
|
||||||
|
{
|
||||||
|
var msg = "Result from appstore registration was null.";
|
||||||
|
_logger.Error(msg);
|
||||||
|
throw new ApplicationException(msg);
|
||||||
|
}
|
||||||
|
if (!String.IsNullOrEmpty(reg.key))
|
||||||
|
{
|
||||||
|
SupporterKey = reg.key;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
catch (ApplicationException)
|
||||||
|
{
|
||||||
|
SaveAppStoreInfo(parameters);
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
_logger.ErrorException("Error registering appstore purchase {0}", e, parameters ?? "NO PARMS SENT");
|
||||||
|
SaveAppStoreInfo(parameters);
|
||||||
|
//TODO - could create a re-try routine on start-up if this file is there. For now we can handle manually.
|
||||||
|
throw new ApplicationException("Error registering store sale");
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private void SaveAppStoreInfo(string info)
|
||||||
|
{
|
||||||
|
// Save all transaction information to a file
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
File.WriteAllText(Path.Combine(_appPaths.ProgramDataPath, "apptrans-error.txt"), info);
|
||||||
|
}
|
||||||
|
catch (IOException)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private async Task<MBRegistrationRecord> GetRegistrationStatusInternal(string feature,
|
private async Task<MBRegistrationRecord> GetRegistrationStatusInternal(string feature,
|
||||||
string mb2Equivalent = null,
|
string mb2Equivalent = null,
|
||||||
string version = null)
|
string version = null)
|
||||||
|
@ -7,5 +7,6 @@ namespace MediaBrowser.Common.Implementations.Security
|
|||||||
public string featId { get; set; }
|
public string featId { get; set; }
|
||||||
public bool registered { get; set; }
|
public bool registered { get; set; }
|
||||||
public DateTime expDate { get; set; }
|
public DateTime expDate { get; set; }
|
||||||
|
public string key { get; set; }
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -1,6 +1,8 @@
|
|||||||
using MediaBrowser.Model.Serialization;
|
using MediaBrowser.Common.IO;
|
||||||
|
using MediaBrowser.Model.Serialization;
|
||||||
using System;
|
using System;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
|
using CommonIO;
|
||||||
|
|
||||||
namespace MediaBrowser.Common.Implementations.Serialization
|
namespace MediaBrowser.Common.Implementations.Serialization
|
||||||
{
|
{
|
||||||
@ -9,8 +11,11 @@ namespace MediaBrowser.Common.Implementations.Serialization
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public class JsonSerializer : IJsonSerializer
|
public class JsonSerializer : IJsonSerializer
|
||||||
{
|
{
|
||||||
public JsonSerializer()
|
private readonly IFileSystem _fileSystem;
|
||||||
|
|
||||||
|
public JsonSerializer(IFileSystem fileSystem)
|
||||||
{
|
{
|
||||||
|
_fileSystem = fileSystem;
|
||||||
Configure();
|
Configure();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -53,7 +58,7 @@ namespace MediaBrowser.Common.Implementations.Serialization
|
|||||||
throw new ArgumentNullException("file");
|
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);
|
SerializeToStream(obj, stream);
|
||||||
}
|
}
|
||||||
|
@ -3,6 +3,8 @@ using System;
|
|||||||
using System.Collections.Concurrent;
|
using System.Collections.Concurrent;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Xml;
|
using System.Xml;
|
||||||
|
using CommonIO;
|
||||||
|
using MediaBrowser.Common.IO;
|
||||||
|
|
||||||
namespace MediaBrowser.Common.Implementations.Serialization
|
namespace MediaBrowser.Common.Implementations.Serialization
|
||||||
{
|
{
|
||||||
@ -11,6 +13,13 @@ namespace MediaBrowser.Common.Implementations.Serialization
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public class XmlSerializer : IXmlSerializer
|
public class XmlSerializer : IXmlSerializer
|
||||||
{
|
{
|
||||||
|
private IFileSystem _fileSystem;
|
||||||
|
|
||||||
|
public XmlSerializer(IFileSystem fileSystem)
|
||||||
|
{
|
||||||
|
_fileSystem = fileSystem;
|
||||||
|
}
|
||||||
|
|
||||||
// Need to cache these
|
// Need to cache these
|
||||||
// http://dotnetcodebox.blogspot.com/2013/01/xmlserializer-class-may-result-in.html
|
// http://dotnetcodebox.blogspot.com/2013/01/xmlserializer-class-may-result-in.html
|
||||||
private readonly ConcurrentDictionary<string, System.Xml.Serialization.XmlSerializer> _serializers =
|
private readonly ConcurrentDictionary<string, System.Xml.Serialization.XmlSerializer> _serializers =
|
||||||
@ -83,7 +92,7 @@ namespace MediaBrowser.Common.Implementations.Serialization
|
|||||||
/// <returns>System.Object.</returns>
|
/// <returns>System.Object.</returns>
|
||||||
public object DeserializeFromFile(Type type, string file)
|
public object DeserializeFromFile(Type type, string file)
|
||||||
{
|
{
|
||||||
using (var stream = File.OpenRead(file))
|
using (var stream = _fileSystem.OpenRead(file))
|
||||||
{
|
{
|
||||||
return DeserializeFromStream(type, stream);
|
return DeserializeFromStream(type, stream);
|
||||||
}
|
}
|
||||||
|
@ -19,6 +19,7 @@ using System.Linq;
|
|||||||
using System.Security.Cryptography;
|
using System.Security.Cryptography;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
using CommonIO;
|
||||||
|
|
||||||
namespace MediaBrowser.Common.Implementations.Updates
|
namespace MediaBrowser.Common.Implementations.Updates
|
||||||
{
|
{
|
||||||
@ -553,7 +554,7 @@ namespace MediaBrowser.Common.Implementations.Updates
|
|||||||
if (packageChecksum != Guid.Empty) // support for legacy uploads for now
|
if (packageChecksum != Guid.Empty) // support for legacy uploads for now
|
||||||
{
|
{
|
||||||
using (var crypto = new MD5CryptoServiceProvider())
|
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));
|
var check = Guid.Parse(BitConverter.ToString(crypto.ComputeHash(stream)).Replace("-", String.Empty));
|
||||||
if (check != packageChecksum)
|
if (check != packageChecksum)
|
||||||
@ -568,12 +569,12 @@ namespace MediaBrowser.Common.Implementations.Updates
|
|||||||
// Success - move it to the real target
|
// Success - move it to the real target
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
Directory.CreateDirectory(Path.GetDirectoryName(target));
|
_fileSystem.CreateDirectory(Path.GetDirectoryName(target));
|
||||||
File.Copy(tempFile, target, true);
|
_fileSystem.CopyFile(tempFile, target, true);
|
||||||
//If it is an archive - write out a version file so we know what it is
|
//If it is an archive - write out a version file so we know what it is
|
||||||
if (isArchive)
|
if (isArchive)
|
||||||
{
|
{
|
||||||
File.WriteAllText(target + ".ver", package.versionStr);
|
File.WriteAllText(target + ".ver", package.versionStr);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (IOException e)
|
catch (IOException e)
|
||||||
|
@ -1,5 +1,7 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<packages>
|
<packages>
|
||||||
<package id="NLog" version="3.2.1" targetFramework="net45" />
|
<package id="CommonIO" version="1.0.0.5" targetFramework="net45" />
|
||||||
<package id="SimpleInjector" version="2.8.0" targetFramework="net45" />
|
<package id="NLog" version="4.1.0" targetFramework="net45" />
|
||||||
|
<package id="Patterns.Logging" version="1.0.0.2" targetFramework="net45" />
|
||||||
|
<package id="SimpleInjector" version="3.0.5" targetFramework="net45" />
|
||||||
</packages>
|
</packages>
|
||||||
|
@ -1,165 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.IO;
|
|
||||||
|
|
||||||
namespace MediaBrowser.Common.IO
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Interface IFileSystem
|
|
||||||
/// </summary>
|
|
||||||
public interface IFileSystem
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Determines whether the specified filename is shortcut.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="filename">The filename.</param>
|
|
||||||
/// <returns><c>true</c> if the specified filename is shortcut; otherwise, <c>false</c>.</returns>
|
|
||||||
bool IsShortcut(string filename);
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Resolves the shortcut.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="filename">The filename.</param>
|
|
||||||
/// <returns>System.String.</returns>
|
|
||||||
string ResolveShortcut(string filename);
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Creates the shortcut.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="shortcutPath">The shortcut path.</param>
|
|
||||||
/// <param name="target">The target.</param>
|
|
||||||
void CreateShortcut(string shortcutPath, string target);
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the file system info.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="path">The path.</param>
|
|
||||||
/// <returns>FileSystemInfo.</returns>
|
|
||||||
FileSystemInfo GetFileSystemInfo(string path);
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the valid filename.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="filename">The filename.</param>
|
|
||||||
/// <returns>System.String.</returns>
|
|
||||||
string GetValidFilename(string filename);
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the creation time UTC.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="info">The info.</param>
|
|
||||||
/// <returns>DateTime.</returns>
|
|
||||||
DateTime GetCreationTimeUtc(FileSystemInfo info);
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the last write time UTC.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="info">The information.</param>
|
|
||||||
/// <returns>DateTime.</returns>
|
|
||||||
DateTime GetLastWriteTimeUtc(FileSystemInfo info);
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the last write time UTC.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="path">The path.</param>
|
|
||||||
/// <returns>DateTime.</returns>
|
|
||||||
DateTime GetLastWriteTimeUtc(string path);
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the file stream.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="path">The path.</param>
|
|
||||||
/// <param name="mode">The mode.</param>
|
|
||||||
/// <param name="access">The access.</param>
|
|
||||||
/// <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);
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Swaps the files.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="file1">The file1.</param>
|
|
||||||
/// <param name="file2">The file2.</param>
|
|
||||||
void SwapFiles(string file1, string file2);
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Determines whether [contains sub path] [the specified parent path].
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="parentPath">The parent path.</param>
|
|
||||||
/// <param name="path">The path.</param>
|
|
||||||
/// <returns><c>true</c> if [contains sub path] [the specified parent path]; otherwise, <c>false</c>.</returns>
|
|
||||||
bool ContainsSubPath(string parentPath, string path);
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Determines whether [is root path] [the specified path].
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="path">The path.</param>
|
|
||||||
/// <returns><c>true</c> if [is root path] [the specified path]; otherwise, <c>false</c>.</returns>
|
|
||||||
bool IsRootPath(string path);
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Normalizes the path.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="path">The path.</param>
|
|
||||||
/// <returns>System.String.</returns>
|
|
||||||
string NormalizePath(string path);
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Substitutes the path.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="path">The path.</param>
|
|
||||||
/// <param name="from">From.</param>
|
|
||||||
/// <param name="to">To.</param>
|
|
||||||
/// <returns>System.String.</returns>
|
|
||||||
string SubstitutePath(string path, string from, string to);
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the file name without extension.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="info">The information.</param>
|
|
||||||
/// <returns>System.String.</returns>
|
|
||||||
string GetFileNameWithoutExtension(FileSystemInfo info);
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the file name without extension.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="path">The path.</param>
|
|
||||||
/// <returns>System.String.</returns>
|
|
||||||
string GetFileNameWithoutExtension(string path);
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Determines whether [is path file] [the specified path].
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="path">The path.</param>
|
|
||||||
/// <returns><c>true</c> if [is path file] [the specified path]; otherwise, <c>false</c>.</returns>
|
|
||||||
bool IsPathFile(string path);
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Deletes the file.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="path">The path.</param>
|
|
||||||
/// <param name="sendToRecycleBin">if set to <c>true</c> [send to recycle bin].</param>
|
|
||||||
void DeleteFile(string path, bool sendToRecycleBin);
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Deletes the directory.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="path">The path.</param>
|
|
||||||
/// <param name="recursive">if set to <c>true</c> [recursive].</param>
|
|
||||||
/// <param name="sendToRecycleBin">if set to <c>true</c> [send to recycle bin].</param>
|
|
||||||
void DeleteDirectory(string path, bool recursive, bool sendToRecycleBin);
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Deletes the file.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="path">The path.</param>
|
|
||||||
void DeleteFile(string path);
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Deletes the directory.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="path">The path.</param>
|
|
||||||
/// <param name="recursive">if set to <c>true</c> [recursive].</param>
|
|
||||||
void DeleteDirectory(string path, bool recursive);
|
|
||||||
}
|
|
||||||
}
|
|
@ -60,7 +60,6 @@
|
|||||||
<Compile Include="Extensions\BaseExtensions.cs" />
|
<Compile Include="Extensions\BaseExtensions.cs" />
|
||||||
<Compile Include="Extensions\ResourceNotFoundException.cs" />
|
<Compile Include="Extensions\ResourceNotFoundException.cs" />
|
||||||
<Compile Include="IDependencyContainer.cs" />
|
<Compile Include="IDependencyContainer.cs" />
|
||||||
<Compile Include="IO\IFileSystem.cs" />
|
|
||||||
<Compile Include="IO\ProgressStream.cs" />
|
<Compile Include="IO\ProgressStream.cs" />
|
||||||
<Compile Include="IO\StreamDefaults.cs" />
|
<Compile Include="IO\StreamDefaults.cs" />
|
||||||
<Compile Include="Configuration\IApplicationPaths.cs" />
|
<Compile Include="Configuration\IApplicationPaths.cs" />
|
||||||
|
@ -87,6 +87,7 @@ namespace MediaBrowser.Common.Net
|
|||||||
public bool BufferContent { get; set; }
|
public bool BufferContent { get; set; }
|
||||||
|
|
||||||
public bool LogRequest { get; set; }
|
public bool LogRequest { get; set; }
|
||||||
|
public bool LogErrors { get; set; }
|
||||||
|
|
||||||
public bool LogErrorResponseBody { get; set; }
|
public bool LogErrorResponseBody { get; set; }
|
||||||
public bool EnableKeepAlive { get; set; }
|
public bool EnableKeepAlive { get; set; }
|
||||||
@ -116,6 +117,7 @@ namespace MediaBrowser.Common.Net
|
|||||||
RequestHeaders = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
|
RequestHeaders = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
|
||||||
|
|
||||||
LogRequest = true;
|
LogRequest = true;
|
||||||
|
LogErrors = true;
|
||||||
CacheMode = CacheMode.None;
|
CacheMode = CacheMode.None;
|
||||||
|
|
||||||
TimeoutMs = 20000;
|
TimeoutMs = 20000;
|
||||||
|
@ -69,5 +69,10 @@ namespace MediaBrowser.Common.ScheduledTasks
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <value>The unique id.</value>
|
/// <value>The unique id.</value>
|
||||||
string Id { get; }
|
string Id { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Reloads the trigger events.
|
||||||
|
/// </summary>
|
||||||
|
void ReloadTriggerEvents();
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -51,8 +51,8 @@ namespace MediaBrowser.Common.ScheduledTasks
|
|||||||
DisposeTimer();
|
DisposeTimer();
|
||||||
|
|
||||||
var triggerDate = lastResult != null ?
|
var triggerDate = lastResult != null ?
|
||||||
lastResult.EndTimeUtc.Add(Interval) :
|
lastResult.EndTimeUtc.Add(Interval) :
|
||||||
DateTime.UtcNow.Add(FirstRunDelay);
|
DateTime.UtcNow.Add(FirstRunDelay);
|
||||||
|
|
||||||
if (DateTime.UtcNow > triggerDate)
|
if (DateTime.UtcNow > triggerDate)
|
||||||
{
|
{
|
||||||
@ -62,7 +62,7 @@ namespace MediaBrowser.Common.ScheduledTasks
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
triggerDate = DateTime.UtcNow.Add(Interval);
|
triggerDate = DateTime.UtcNow.AddMinutes(1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
using System;
|
||||||
using MediaBrowser.Model.Entities;
|
using MediaBrowser.Model.Entities;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
@ -45,5 +46,11 @@ namespace MediaBrowser.Common.Security
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <returns>Task<SupporterInfo>.</returns>
|
/// <returns>Task<SupporterInfo>.</returns>
|
||||||
Task<SupporterInfo> GetSupporterInfo();
|
Task<SupporterInfo> GetSupporterInfo();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Register and app store sale with our back-end
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="parameters">Json parameters to pass to admin server</param>
|
||||||
|
Task RegisterAppStoreSale(string parameters);
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -10,8 +10,6 @@ namespace MediaBrowser.Controller.Channels
|
|||||||
{
|
{
|
||||||
public class Channel : Folder
|
public class Channel : Folder
|
||||||
{
|
{
|
||||||
public string OriginalChannelName { get; set; }
|
|
||||||
|
|
||||||
public override bool IsVisible(User user)
|
public override bool IsVisible(User user)
|
||||||
{
|
{
|
||||||
if (user.Policy.BlockedChannels != null)
|
if (user.Policy.BlockedChannels != null)
|
||||||
|
@ -7,24 +7,15 @@ using MediaBrowser.Model.Entities;
|
|||||||
using MediaBrowser.Model.Users;
|
using MediaBrowser.Model.Users;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using System.Runtime.Serialization;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
|
|
||||||
namespace MediaBrowser.Controller.Channels
|
namespace MediaBrowser.Controller.Channels
|
||||||
{
|
{
|
||||||
public class ChannelAudioItem : Audio, IChannelMediaItem
|
public class ChannelAudioItem : Audio, IChannelMediaItem
|
||||||
{
|
{
|
||||||
public string ExternalId { get; set; }
|
|
||||||
|
|
||||||
public string DataVersion { get; set; }
|
|
||||||
|
|
||||||
public ChannelItemType ChannelItemType { get; set; }
|
|
||||||
|
|
||||||
public bool IsInfiniteStream { get; set; }
|
|
||||||
|
|
||||||
public ChannelMediaContentType ContentType { get; set; }
|
public ChannelMediaContentType ContentType { get; set; }
|
||||||
|
|
||||||
public string OriginalImageUrl { get; set; }
|
|
||||||
|
|
||||||
public List<ChannelMediaInfo> ChannelMediaSources { get; set; }
|
public List<ChannelMediaInfo> ChannelMediaSources { get; set; }
|
||||||
|
|
||||||
protected override bool GetBlockUnratedValue(UserPolicy config)
|
protected override bool GetBlockUnratedValue(UserPolicy config)
|
||||||
@ -37,6 +28,7 @@ namespace MediaBrowser.Controller.Channels
|
|||||||
return ExternalId;
|
return ExternalId;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[IgnoreDataMember]
|
||||||
public override bool SupportsLocalMetadata
|
public override bool SupportsLocalMetadata
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
@ -55,6 +47,7 @@ namespace MediaBrowser.Controller.Channels
|
|||||||
ChannelMediaSources = new List<ChannelMediaInfo>();
|
ChannelMediaSources = new List<ChannelMediaInfo>();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[IgnoreDataMember]
|
||||||
public override LocationType LocationType
|
public override LocationType LocationType
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
|
@ -3,28 +3,24 @@ using MediaBrowser.Model.Channels;
|
|||||||
using MediaBrowser.Model.Querying;
|
using MediaBrowser.Model.Querying;
|
||||||
using MediaBrowser.Model.Users;
|
using MediaBrowser.Model.Users;
|
||||||
using System;
|
using System;
|
||||||
|
using System.Runtime.Serialization;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
using MediaBrowser.Model.Entities;
|
||||||
|
|
||||||
namespace MediaBrowser.Controller.Channels
|
namespace MediaBrowser.Controller.Channels
|
||||||
{
|
{
|
||||||
public class ChannelFolderItem : Folder, IChannelItem
|
public class ChannelFolderItem : Folder, IChannelItem
|
||||||
{
|
{
|
||||||
public string ExternalId { get; set; }
|
|
||||||
|
|
||||||
public string DataVersion { get; set; }
|
|
||||||
|
|
||||||
public ChannelItemType ChannelItemType { get; set; }
|
|
||||||
public ChannelFolderType ChannelFolderType { get; set; }
|
public ChannelFolderType ChannelFolderType { get; set; }
|
||||||
|
|
||||||
public string OriginalImageUrl { get; set; }
|
|
||||||
|
|
||||||
protected override bool GetBlockUnratedValue(UserPolicy config)
|
protected override bool GetBlockUnratedValue(UserPolicy config)
|
||||||
{
|
{
|
||||||
// Don't block.
|
// Don't block.
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[IgnoreDataMember]
|
||||||
public override bool SupportsLocalMetadata
|
public override bool SupportsLocalMetadata
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
|
@ -8,24 +8,15 @@ using MediaBrowser.Model.Users;
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Globalization;
|
using System.Globalization;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using System.Runtime.Serialization;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
|
|
||||||
namespace MediaBrowser.Controller.Channels
|
namespace MediaBrowser.Controller.Channels
|
||||||
{
|
{
|
||||||
public class ChannelVideoItem : Video, IChannelMediaItem, IHasLookupInfo<ChannelItemLookupInfo>
|
public class ChannelVideoItem : Video, IChannelMediaItem, IHasLookupInfo<ChannelItemLookupInfo>
|
||||||
{
|
{
|
||||||
public string ExternalId { get; set; }
|
|
||||||
|
|
||||||
public string DataVersion { get; set; }
|
|
||||||
|
|
||||||
public ChannelItemType ChannelItemType { get; set; }
|
|
||||||
|
|
||||||
public bool IsInfiniteStream { get; set; }
|
|
||||||
|
|
||||||
public ChannelMediaContentType ContentType { get; set; }
|
public ChannelMediaContentType ContentType { get; set; }
|
||||||
|
|
||||||
public string OriginalImageUrl { get; set; }
|
|
||||||
|
|
||||||
public List<ChannelMediaInfo> ChannelMediaSources { get; set; }
|
public List<ChannelMediaInfo> ChannelMediaSources { get; set; }
|
||||||
|
|
||||||
protected override string CreateUserDataKey()
|
protected override string CreateUserDataKey()
|
||||||
@ -56,6 +47,7 @@ namespace MediaBrowser.Controller.Channels
|
|||||||
return config.BlockUnratedItems.Contains(UnratedItem.ChannelContent);
|
return config.BlockUnratedItems.Contains(UnratedItem.ChannelContent);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[IgnoreDataMember]
|
||||||
public override bool SupportsLocalMetadata
|
public override bool SupportsLocalMetadata
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
@ -74,6 +66,7 @@ namespace MediaBrowser.Controller.Channels
|
|||||||
ChannelMediaSources = new List<ChannelMediaInfo>();
|
ChannelMediaSources = new List<ChannelMediaInfo>();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[IgnoreDataMember]
|
||||||
public override LocationType LocationType
|
public override LocationType LocationType
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
@ -115,7 +108,11 @@ namespace MediaBrowser.Controller.Channels
|
|||||||
var info = GetItemLookupInfo<ChannelItemLookupInfo>();
|
var info = GetItemLookupInfo<ChannelItemLookupInfo>();
|
||||||
|
|
||||||
info.ContentType = ContentType;
|
info.ContentType = ContentType;
|
||||||
info.ExtraType = ExtraType;
|
|
||||||
|
if (ExtraType.HasValue)
|
||||||
|
{
|
||||||
|
info.ExtraType = ExtraType.Value;
|
||||||
|
}
|
||||||
|
|
||||||
return info;
|
return info;
|
||||||
}
|
}
|
||||||
|
@ -1,14 +0,0 @@
|
|||||||
using System.Collections.Generic;
|
|
||||||
|
|
||||||
namespace MediaBrowser.Controller.Channels
|
|
||||||
{
|
|
||||||
public interface IChannelFactory
|
|
||||||
{
|
|
||||||
IEnumerable<IChannel> GetChannels();
|
|
||||||
}
|
|
||||||
|
|
||||||
public interface IFactoryChannel
|
|
||||||
{
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
@ -7,11 +7,5 @@ namespace MediaBrowser.Controller.Channels
|
|||||||
string ChannelId { get; set; }
|
string ChannelId { get; set; }
|
||||||
|
|
||||||
string ExternalId { get; set; }
|
string ExternalId { get; set; }
|
||||||
|
|
||||||
ChannelItemType ChannelItemType { get; set; }
|
|
||||||
|
|
||||||
string OriginalImageUrl { get; set; }
|
|
||||||
|
|
||||||
string DataVersion { get; set; }
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -16,7 +16,7 @@ namespace MediaBrowser.Controller.Channels
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="channels">The channels.</param>
|
/// <param name="channels">The channels.</param>
|
||||||
/// <param name="factories">The factories.</param>
|
/// <param name="factories">The factories.</param>
|
||||||
void AddParts(IEnumerable<IChannel> channels, IEnumerable<IChannelFactory> factories);
|
void AddParts(IEnumerable<IChannel> channels);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the channel download path.
|
/// Gets the channel download path.
|
||||||
|
@ -11,7 +11,7 @@ namespace MediaBrowser.Controller.Channels
|
|||||||
|
|
||||||
ChannelMediaContentType ContentType { get; set; }
|
ChannelMediaContentType ContentType { get; set; }
|
||||||
|
|
||||||
ExtraType ExtraType { get; set; }
|
ExtraType? ExtraType { get; set; }
|
||||||
|
|
||||||
List<ChannelMediaInfo> ChannelMediaSources { get; set; }
|
List<ChannelMediaInfo> ChannelMediaSources { get; set; }
|
||||||
}
|
}
|
||||||
|
@ -25,13 +25,6 @@ namespace MediaBrowser.Controller.Drawing
|
|||||||
/// <value>The image enhancers.</value>
|
/// <value>The image enhancers.</value>
|
||||||
IEnumerable<IImageEnhancer> ImageEnhancers { get; }
|
IEnumerable<IImageEnhancer> ImageEnhancers { get; }
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the size of the image.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="path">The path.</param>
|
|
||||||
/// <returns>ImageSize.</returns>
|
|
||||||
ImageSize GetImageSize(string path);
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the size of the image.
|
/// Gets the size of the image.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -39,6 +32,13 @@ namespace MediaBrowser.Controller.Drawing
|
|||||||
/// <returns>ImageSize.</returns>
|
/// <returns>ImageSize.</returns>
|
||||||
ImageSize GetImageSize(ItemImageInfo info);
|
ImageSize GetImageSize(ItemImageInfo info);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the size of the image.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="path">The path.</param>
|
||||||
|
/// <returns>ImageSize.</returns>
|
||||||
|
ImageSize GetImageSize(string path);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Adds the parts.
|
/// Adds the parts.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -105,5 +105,11 @@ namespace MediaBrowser.Controller.Drawing
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="options">The options.</param>
|
/// <param name="options">The options.</param>
|
||||||
Task CreateImageCollage(ImageCollageOptions options);
|
Task CreateImageCollage(ImageCollageOptions options);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets a value indicating whether [supports image collage creation].
|
||||||
|
/// </summary>
|
||||||
|
/// <value><c>true</c> if [supports image collage creation]; otherwise, <c>false</c>.</value>
|
||||||
|
bool SupportsImageCollageCreation { get; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -6,6 +6,8 @@ using System.Collections.Generic;
|
|||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Runtime.Serialization;
|
using System.Runtime.Serialization;
|
||||||
|
using CommonIO;
|
||||||
|
using MediaBrowser.Common.IO;
|
||||||
using MediaBrowser.Controller.Providers;
|
using MediaBrowser.Controller.Providers;
|
||||||
|
|
||||||
namespace MediaBrowser.Controller.Entities
|
namespace MediaBrowser.Controller.Entities
|
||||||
@ -62,7 +64,7 @@ namespace MediaBrowser.Controller.Entities
|
|||||||
|
|
||||||
public List<string> PhysicalLocationsList { get; set; }
|
public List<string> PhysicalLocationsList { get; set; }
|
||||||
|
|
||||||
protected override IEnumerable<FileSystemInfo> GetFileSystemChildren(IDirectoryService directoryService)
|
protected override IEnumerable<FileSystemMetadata> GetFileSystemChildren(IDirectoryService directoryService)
|
||||||
{
|
{
|
||||||
return CreateResolveArgs(directoryService).FileSystemChildren;
|
return CreateResolveArgs(directoryService).FileSystemChildren;
|
||||||
}
|
}
|
||||||
@ -73,7 +75,7 @@ namespace MediaBrowser.Controller.Entities
|
|||||||
|
|
||||||
var args = new ItemResolveArgs(ConfigurationManager.ApplicationPaths , directoryService)
|
var args = new ItemResolveArgs(ConfigurationManager.ApplicationPaths , directoryService)
|
||||||
{
|
{
|
||||||
FileInfo = new DirectoryInfo(path),
|
FileInfo = FileSystem.GetDirectoryInfo(path),
|
||||||
Path = path,
|
Path = path,
|
||||||
Parent = Parent
|
Parent = Parent
|
||||||
};
|
};
|
||||||
@ -94,7 +96,7 @@ namespace MediaBrowser.Controller.Entities
|
|||||||
{
|
{
|
||||||
var paths = LibraryManager.NormalizeRootPathList(fileSystemDictionary.Keys);
|
var paths = LibraryManager.NormalizeRootPathList(fileSystemDictionary.Keys);
|
||||||
|
|
||||||
fileSystemDictionary = paths.Select(i => (FileSystemInfo)new DirectoryInfo(i)).ToDictionary(i => i.FullName);
|
fileSystemDictionary = paths.Select(FileSystem.GetDirectoryInfo).ToDictionary(i => i.FullName);
|
||||||
}
|
}
|
||||||
|
|
||||||
args.FileSystemDictionary = fileSystemDictionary;
|
args.FileSystemDictionary = fileSystemDictionary;
|
||||||
|
@ -24,14 +24,20 @@ namespace MediaBrowser.Controller.Entities.Audio
|
|||||||
IThemeMedia,
|
IThemeMedia,
|
||||||
IArchivable
|
IArchivable
|
||||||
{
|
{
|
||||||
public string FormatName { get; set; }
|
|
||||||
public long? Size { get; set; }
|
public long? Size { get; set; }
|
||||||
public string Container { get; set; }
|
public string Container { get; set; }
|
||||||
public int? TotalBitrate { get; set; }
|
public int? TotalBitrate { get; set; }
|
||||||
public List<string> Tags { 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()
|
public Audio()
|
||||||
{
|
{
|
||||||
@ -46,12 +52,6 @@ namespace MediaBrowser.Controller.Entities.Audio
|
|||||||
get { return LocationType == LocationType.FileSystem && RunTimeTicks.HasValue; }
|
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]
|
[IgnoreDataMember]
|
||||||
protected override bool SupportsOwnedItems
|
protected override bool SupportsOwnedItems
|
||||||
{
|
{
|
||||||
@ -212,8 +212,7 @@ namespace MediaBrowser.Controller.Entities.Audio
|
|||||||
Path = enablePathSubstituion ? GetMappedPath(i.Path, locationType) : i.Path,
|
Path = enablePathSubstituion ? GetMappedPath(i.Path, locationType) : i.Path,
|
||||||
RunTimeTicks = i.RunTimeTicks,
|
RunTimeTicks = i.RunTimeTicks,
|
||||||
Container = i.Container,
|
Container = i.Container,
|
||||||
Size = i.Size,
|
Size = i.Size
|
||||||
Formats = (i.FormatName ?? string.Empty).Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries).ToList()
|
|
||||||
};
|
};
|
||||||
|
|
||||||
if (string.IsNullOrEmpty(info.Container))
|
if (string.IsNullOrEmpty(info.Container))
|
||||||
|
@ -23,6 +23,7 @@ using System.Linq;
|
|||||||
using System.Runtime.Serialization;
|
using System.Runtime.Serialization;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
using CommonIO;
|
||||||
|
|
||||||
namespace MediaBrowser.Controller.Entities
|
namespace MediaBrowser.Controller.Entities
|
||||||
{
|
{
|
||||||
@ -38,7 +39,6 @@ namespace MediaBrowser.Controller.Entities
|
|||||||
ProviderIds = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
|
ProviderIds = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
|
||||||
LockedFields = new List<MetadataFields>();
|
LockedFields = new List<MetadataFields>();
|
||||||
ImageInfos = new List<ItemImageInfo>();
|
ImageInfos = new List<ItemImageInfo>();
|
||||||
Identities = new List<IItemIdentity>();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -56,12 +56,16 @@ namespace MediaBrowser.Controller.Entities
|
|||||||
public static string ThemeSongFilename = "theme";
|
public static string ThemeSongFilename = "theme";
|
||||||
public static string ThemeVideosFolderName = "backdrops";
|
public static string ThemeVideosFolderName = "backdrops";
|
||||||
|
|
||||||
|
public string PreferredMetadataCountryCode { get; set; }
|
||||||
|
public string PreferredMetadataLanguage { get; set; }
|
||||||
|
|
||||||
public List<ItemImageInfo> ImageInfos { get; set; }
|
public List<ItemImageInfo> ImageInfos { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets the channel identifier.
|
/// Gets or sets the channel identifier.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <value>The channel identifier.</value>
|
/// <value>The channel identifier.</value>
|
||||||
|
[IgnoreDataMember]
|
||||||
public string ChannelId { get; set; }
|
public string ChannelId { get; set; }
|
||||||
|
|
||||||
[IgnoreDataMember]
|
[IgnoreDataMember]
|
||||||
@ -120,6 +124,12 @@ namespace MediaBrowser.Controller.Entities
|
|||||||
/// <value>The id.</value>
|
/// <value>The id.</value>
|
||||||
public Guid Id { get; set; }
|
public Guid Id { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets a value indicating whether this instance is hd.
|
||||||
|
/// </summary>
|
||||||
|
/// <value><c>true</c> if this instance is hd; otherwise, <c>false</c>.</value>
|
||||||
|
public bool? IsHD { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Return the id that should be used to key display prefs for this item.
|
/// Return the id that should be used to key display prefs for this item.
|
||||||
/// Default is based on the type for everything except actual generic folders.
|
/// Default is based on the type for everything except actual generic folders.
|
||||||
@ -162,6 +172,26 @@ namespace MediaBrowser.Controller.Entities
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Id of the program.
|
||||||
|
/// </summary>
|
||||||
|
[IgnoreDataMember]
|
||||||
|
public string ExternalId
|
||||||
|
{
|
||||||
|
get { return this.GetProviderId("ProviderExternalId"); }
|
||||||
|
set
|
||||||
|
{
|
||||||
|
this.SetProviderId("ProviderExternalId", value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the etag.
|
||||||
|
/// </summary>
|
||||||
|
/// <value>The etag.</value>
|
||||||
|
[IgnoreDataMember]
|
||||||
|
public string ExternalEtag { get; set; }
|
||||||
|
|
||||||
[IgnoreDataMember]
|
[IgnoreDataMember]
|
||||||
public virtual bool IsHidden
|
public virtual bool IsHidden
|
||||||
{
|
{
|
||||||
@ -183,7 +213,7 @@ namespace MediaBrowser.Controller.Entities
|
|||||||
{
|
{
|
||||||
// Local trailer, special feature, theme video, etc.
|
// Local trailer, special feature, theme video, etc.
|
||||||
// An item that belongs to another item but is not part of the Parent-Child tree
|
// 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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -305,6 +335,9 @@ namespace MediaBrowser.Controller.Entities
|
|||||||
|
|
||||||
public DateTime DateLastSaved { get; set; }
|
public DateTime DateLastSaved { get; set; }
|
||||||
|
|
||||||
|
[IgnoreDataMember]
|
||||||
|
public DateTime DateLastRefreshed { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The logger
|
/// The logger
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -331,30 +364,8 @@ namespace MediaBrowser.Controller.Entities
|
|||||||
return Name;
|
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]
|
[IgnoreDataMember]
|
||||||
public bool IsLocked
|
public bool IsLocked { get; set; }
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
return DontFetchMeta;
|
|
||||||
}
|
|
||||||
set
|
|
||||||
{
|
|
||||||
DontFetchMeta = value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool IsUnidentified { get; set; }
|
|
||||||
|
|
||||||
[IgnoreDataMember]
|
|
||||||
public List<IItemIdentity> Identities { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets the locked fields.
|
/// Gets or sets the locked fields.
|
||||||
@ -484,7 +495,6 @@ namespace MediaBrowser.Controller.Entities
|
|||||||
|
|
||||||
public Guid ParentId { get; set; }
|
public Guid ParentId { get; set; }
|
||||||
|
|
||||||
private Folder _parent;
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets the parent.
|
/// Gets or sets the parent.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -494,11 +504,6 @@ namespace MediaBrowser.Controller.Entities
|
|||||||
{
|
{
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
if (_parent != null)
|
|
||||||
{
|
|
||||||
return _parent;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ParentId != Guid.Empty)
|
if (ParentId != Guid.Empty)
|
||||||
{
|
{
|
||||||
return LibraryManager.GetItemById(ParentId) as Folder;
|
return LibraryManager.GetItemById(ParentId) as Folder;
|
||||||
@ -506,12 +511,14 @@ namespace MediaBrowser.Controller.Entities
|
|||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
set { _parent = value; }
|
set
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void SetParent(Folder parent)
|
public void SetParent(Folder parent)
|
||||||
{
|
{
|
||||||
Parent = parent;
|
|
||||||
ParentId = parent == null ? Guid.Empty : parent.Id;
|
ParentId = parent == null ? Guid.Empty : parent.Id;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -558,6 +565,7 @@ namespace MediaBrowser.Controller.Entities
|
|||||||
/// Gets or sets the end date.
|
/// Gets or sets the end date.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <value>The end date.</value>
|
/// <value>The end date.</value>
|
||||||
|
[IgnoreDataMember]
|
||||||
public DateTime? EndDate { get; set; }
|
public DateTime? EndDate { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -582,6 +590,7 @@ namespace MediaBrowser.Controller.Entities
|
|||||||
/// Gets or sets the custom rating.
|
/// Gets or sets the custom rating.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <value>The custom rating.</value>
|
/// <value>The custom rating.</value>
|
||||||
|
//[IgnoreDataMember]
|
||||||
public string CustomRating { get; set; }
|
public string CustomRating { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -590,12 +599,6 @@ namespace MediaBrowser.Controller.Entities
|
|||||||
/// <value>The overview.</value>
|
/// <value>The overview.</value>
|
||||||
public string Overview { get; set; }
|
public string Overview { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets or sets the people.
|
|
||||||
/// </summary>
|
|
||||||
/// <value>The people.</value>
|
|
||||||
public List<PersonInfo> People { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets the studios.
|
/// Gets or sets the studios.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -618,6 +621,7 @@ namespace MediaBrowser.Controller.Entities
|
|||||||
/// Gets or sets the community rating.
|
/// Gets or sets the community rating.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <value>The community rating.</value>
|
/// <value>The community rating.</value>
|
||||||
|
//[IgnoreDataMember]
|
||||||
public float? CommunityRating { get; set; }
|
public float? CommunityRating { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -643,6 +647,7 @@ namespace MediaBrowser.Controller.Entities
|
|||||||
/// This could be episode number, album track number, etc.
|
/// This could be episode number, album track number, etc.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <value>The index number.</value>
|
/// <value>The index number.</value>
|
||||||
|
//[IgnoreDataMember]
|
||||||
public int? IndexNumber { get; set; }
|
public int? IndexNumber { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -662,7 +667,7 @@ namespace MediaBrowser.Controller.Entities
|
|||||||
{
|
{
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
if (!string.IsNullOrEmpty(CustomRating))
|
if (!string.IsNullOrWhiteSpace(CustomRating))
|
||||||
{
|
{
|
||||||
return CustomRating;
|
return CustomRating;
|
||||||
}
|
}
|
||||||
@ -701,16 +706,16 @@ namespace MediaBrowser.Controller.Entities
|
|||||||
/// Loads the theme songs.
|
/// Loads the theme songs.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <returns>List{Audio.Audio}.</returns>
|
/// <returns>List{Audio.Audio}.</returns>
|
||||||
private IEnumerable<Audio.Audio> LoadThemeSongs(List<FileSystemInfo> fileSystemChildren, IDirectoryService directoryService)
|
private IEnumerable<Audio.Audio> LoadThemeSongs(List<FileSystemMetadata> fileSystemChildren, IDirectoryService directoryService)
|
||||||
{
|
{
|
||||||
var files = fileSystemChildren.OfType<DirectoryInfo>()
|
var files = fileSystemChildren.Where(i => i.IsDirectory)
|
||||||
.Where(i => string.Equals(i.Name, ThemeSongsFolderName, StringComparison.OrdinalIgnoreCase))
|
.Where(i => string.Equals(i.Name, ThemeSongsFolderName, StringComparison.OrdinalIgnoreCase))
|
||||||
.SelectMany(i => i.EnumerateFiles("*", SearchOption.TopDirectoryOnly))
|
.SelectMany(i => directoryService.GetFiles(i.FullName))
|
||||||
.ToList();
|
.ToList();
|
||||||
|
|
||||||
// Support plex/xbmc convention
|
// Support plex/xbmc convention
|
||||||
files.AddRange(fileSystemChildren.OfType<FileInfo>()
|
files.AddRange(fileSystemChildren
|
||||||
.Where(i => string.Equals(FileSystem.GetFileNameWithoutExtension(i), ThemeSongFilename, StringComparison.OrdinalIgnoreCase))
|
.Where(i => !i.IsDirectory && string.Equals(FileSystem.GetFileNameWithoutExtension(i), ThemeSongFilename, StringComparison.OrdinalIgnoreCase))
|
||||||
);
|
);
|
||||||
|
|
||||||
return LibraryManager.ResolvePaths(files, directoryService, null)
|
return LibraryManager.ResolvePaths(files, directoryService, null)
|
||||||
@ -737,11 +742,11 @@ namespace MediaBrowser.Controller.Entities
|
|||||||
/// Loads the video backdrops.
|
/// Loads the video backdrops.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <returns>List{Video}.</returns>
|
/// <returns>List{Video}.</returns>
|
||||||
private IEnumerable<Video> LoadThemeVideos(IEnumerable<FileSystemInfo> fileSystemChildren, IDirectoryService directoryService)
|
private IEnumerable<Video> LoadThemeVideos(IEnumerable<FileSystemMetadata> fileSystemChildren, IDirectoryService directoryService)
|
||||||
{
|
{
|
||||||
var files = fileSystemChildren.OfType<DirectoryInfo>()
|
var files = fileSystemChildren.Where(i => i.IsDirectory)
|
||||||
.Where(i => string.Equals(i.Name, ThemeVideosFolderName, StringComparison.OrdinalIgnoreCase))
|
.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)
|
return LibraryManager.ResolvePaths(files, directoryService, null)
|
||||||
.OfType<Video>()
|
.OfType<Video>()
|
||||||
@ -765,7 +770,7 @@ namespace MediaBrowser.Controller.Entities
|
|||||||
|
|
||||||
public Task RefreshMetadata(CancellationToken cancellationToken)
|
public Task RefreshMetadata(CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
return RefreshMetadata(new MetadataRefreshOptions(new DirectoryService()), cancellationToken);
|
return RefreshMetadata(new MetadataRefreshOptions(new DirectoryService(FileSystem)), cancellationToken);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -786,7 +791,7 @@ namespace MediaBrowser.Controller.Entities
|
|||||||
{
|
{
|
||||||
var files = locationType != LocationType.Remote && locationType != LocationType.Virtual ?
|
var files = locationType != LocationType.Remote && locationType != LocationType.Virtual ?
|
||||||
GetFileSystemChildren(options.DirectoryService).ToList() :
|
GetFileSystemChildren(options.DirectoryService).ToList() :
|
||||||
new List<FileSystemInfo>();
|
new List<FileSystemMetadata>();
|
||||||
|
|
||||||
var ownedItemsChanged = await RefreshedOwnedItems(options, files, cancellationToken).ConfigureAwait(false);
|
var ownedItemsChanged = await RefreshedOwnedItems(options, files, cancellationToken).ConfigureAwait(false);
|
||||||
|
|
||||||
@ -833,7 +838,7 @@ namespace MediaBrowser.Controller.Entities
|
|||||||
/// <param name="fileSystemChildren"></param>
|
/// <param name="fileSystemChildren"></param>
|
||||||
/// <param name="cancellationToken"></param>
|
/// <param name="cancellationToken"></param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
protected virtual async Task<bool> RefreshedOwnedItems(MetadataRefreshOptions options, List<FileSystemInfo> fileSystemChildren, CancellationToken cancellationToken)
|
protected virtual async Task<bool> RefreshedOwnedItems(MetadataRefreshOptions options, List<FileSystemMetadata> fileSystemChildren, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
var themeSongsChanged = false;
|
var themeSongsChanged = false;
|
||||||
|
|
||||||
@ -864,14 +869,14 @@ namespace MediaBrowser.Controller.Entities
|
|||||||
return themeSongsChanged || themeVideosChanged || localTrailersChanged;
|
return themeSongsChanged || themeVideosChanged || localTrailersChanged;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected virtual IEnumerable<FileSystemInfo> GetFileSystemChildren(IDirectoryService directoryService)
|
protected virtual IEnumerable<FileSystemMetadata> GetFileSystemChildren(IDirectoryService directoryService)
|
||||||
{
|
{
|
||||||
var path = ContainingFolderPath;
|
var path = ContainingFolderPath;
|
||||||
|
|
||||||
return directoryService.GetFileSystemEntries(path);
|
return directoryService.GetFileSystemEntries(path);
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task<bool> RefreshLocalTrailers(IHasTrailers item, MetadataRefreshOptions options, List<FileSystemInfo> fileSystemChildren, CancellationToken cancellationToken)
|
private async Task<bool> RefreshLocalTrailers(IHasTrailers item, MetadataRefreshOptions options, List<FileSystemMetadata> fileSystemChildren, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
var newItems = LibraryManager.FindTrailers(this, fileSystemChildren, options.DirectoryService).ToList();
|
var newItems = LibraryManager.FindTrailers(this, fileSystemChildren, options.DirectoryService).ToList();
|
||||||
|
|
||||||
@ -888,7 +893,7 @@ namespace MediaBrowser.Controller.Entities
|
|||||||
return itemsChanged;
|
return itemsChanged;
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task<bool> RefreshThemeVideos(IHasThemeMedia item, MetadataRefreshOptions options, IEnumerable<FileSystemInfo> fileSystemChildren, CancellationToken cancellationToken)
|
private async Task<bool> RefreshThemeVideos(IHasThemeMedia item, MetadataRefreshOptions options, IEnumerable<FileSystemMetadata> fileSystemChildren, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
var newThemeVideos = LoadThemeVideos(fileSystemChildren, options.DirectoryService).ToList();
|
var newThemeVideos = LoadThemeVideos(fileSystemChildren, options.DirectoryService).ToList();
|
||||||
|
|
||||||
@ -902,7 +907,7 @@ namespace MediaBrowser.Controller.Entities
|
|||||||
|
|
||||||
if (!i.IsThemeMedia)
|
if (!i.IsThemeMedia)
|
||||||
{
|
{
|
||||||
i.IsThemeMedia = true;
|
i.ExtraType = ExtraType.ThemeVideo;
|
||||||
subOptions.ForceSave = true;
|
subOptions.ForceSave = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -919,7 +924,7 @@ namespace MediaBrowser.Controller.Entities
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Refreshes the theme songs.
|
/// Refreshes the theme songs.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private async Task<bool> RefreshThemeSongs(IHasThemeMedia item, MetadataRefreshOptions options, List<FileSystemInfo> fileSystemChildren, CancellationToken cancellationToken)
|
private async Task<bool> RefreshThemeSongs(IHasThemeMedia item, MetadataRefreshOptions options, List<FileSystemMetadata> fileSystemChildren, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
var newThemeSongs = LoadThemeSongs(fileSystemChildren, options.DirectoryService).ToList();
|
var newThemeSongs = LoadThemeSongs(fileSystemChildren, options.DirectoryService).ToList();
|
||||||
var newThemeSongIds = newThemeSongs.Select(i => i.Id).ToList();
|
var newThemeSongIds = newThemeSongs.Select(i => i.Id).ToList();
|
||||||
@ -932,7 +937,7 @@ namespace MediaBrowser.Controller.Entities
|
|||||||
|
|
||||||
if (!i.IsThemeMedia)
|
if (!i.IsThemeMedia)
|
||||||
{
|
{
|
||||||
i.IsThemeMedia = true;
|
i.ExtraType = ExtraType.ThemeSong;
|
||||||
subOptions.ForceSave = true;
|
subOptions.ForceSave = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -999,18 +1004,11 @@ namespace MediaBrowser.Controller.Entities
|
|||||||
/// <returns>System.String.</returns>
|
/// <returns>System.String.</returns>
|
||||||
public string GetPreferredMetadataLanguage()
|
public string GetPreferredMetadataLanguage()
|
||||||
{
|
{
|
||||||
string lang = null;
|
string lang = PreferredMetadataLanguage;
|
||||||
|
|
||||||
var hasLang = this as IHasPreferredMetadataLanguage;
|
|
||||||
|
|
||||||
if (hasLang != null)
|
|
||||||
{
|
|
||||||
lang = hasLang.PreferredMetadataLanguage;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (string.IsNullOrWhiteSpace(lang))
|
if (string.IsNullOrWhiteSpace(lang))
|
||||||
{
|
{
|
||||||
lang = Parents.OfType<IHasPreferredMetadataLanguage>()
|
lang = Parents
|
||||||
.Select(i => i.PreferredMetadataLanguage)
|
.Select(i => i.PreferredMetadataLanguage)
|
||||||
.FirstOrDefault(i => !string.IsNullOrWhiteSpace(i));
|
.FirstOrDefault(i => !string.IsNullOrWhiteSpace(i));
|
||||||
}
|
}
|
||||||
@ -1036,18 +1034,11 @@ namespace MediaBrowser.Controller.Entities
|
|||||||
/// <returns>System.String.</returns>
|
/// <returns>System.String.</returns>
|
||||||
public string GetPreferredMetadataCountryCode()
|
public string GetPreferredMetadataCountryCode()
|
||||||
{
|
{
|
||||||
string lang = null;
|
string lang = PreferredMetadataCountryCode;
|
||||||
|
|
||||||
var hasLang = this as IHasPreferredMetadataLanguage;
|
|
||||||
|
|
||||||
if (hasLang != null)
|
|
||||||
{
|
|
||||||
lang = hasLang.PreferredMetadataCountryCode;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (string.IsNullOrWhiteSpace(lang))
|
if (string.IsNullOrWhiteSpace(lang))
|
||||||
{
|
{
|
||||||
lang = Parents.OfType<IHasPreferredMetadataLanguage>()
|
lang = Parents
|
||||||
.Select(i => i.PreferredMetadataCountryCode)
|
.Select(i => i.PreferredMetadataCountryCode)
|
||||||
.FirstOrDefault(i => !string.IsNullOrWhiteSpace(i));
|
.FirstOrDefault(i => !string.IsNullOrWhiteSpace(i));
|
||||||
}
|
}
|
||||||
@ -1114,7 +1105,14 @@ namespace MediaBrowser.Controller.Entities
|
|||||||
// Could not determine the integer value
|
// Could not determine the integer value
|
||||||
if (!value.HasValue)
|
if (!value.HasValue)
|
||||||
{
|
{
|
||||||
return true;
|
var isAllowed = !GetBlockUnratedValue(user.Policy);
|
||||||
|
|
||||||
|
if (!isAllowed)
|
||||||
|
{
|
||||||
|
Logger.Debug("{0} has an unrecognized parental rating of {1}.", Name, rating);
|
||||||
|
}
|
||||||
|
|
||||||
|
return isAllowed;
|
||||||
}
|
}
|
||||||
|
|
||||||
return value.Value <= maxAllowedRating.Value;
|
return value.Value <= maxAllowedRating.Value;
|
||||||
@ -1165,6 +1163,17 @@ namespace MediaBrowser.Controller.Entities
|
|||||||
/// <returns><c>true</c> if XXXX, <c>false</c> otherwise.</returns>
|
/// <returns><c>true</c> if XXXX, <c>false</c> otherwise.</returns>
|
||||||
protected virtual bool GetBlockUnratedValue(UserPolicy config)
|
protected virtual bool GetBlockUnratedValue(UserPolicy config)
|
||||||
{
|
{
|
||||||
|
// Don't block plain folders that are unrated. Let the media underneath get blocked
|
||||||
|
// Special folders like series and albums will override this method.
|
||||||
|
if (IsFolder)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (this is IItemByName)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
return config.BlockUnratedItems.Contains(UnratedItem.Other);
|
return config.BlockUnratedItems.Contains(UnratedItem.Other);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1418,7 +1427,7 @@ namespace MediaBrowser.Controller.Entities
|
|||||||
/// <returns>Task.</returns>
|
/// <returns>Task.</returns>
|
||||||
public virtual Task ChangedExternally()
|
public virtual Task ChangedExternally()
|
||||||
{
|
{
|
||||||
ProviderManager.QueueRefresh(Id, new MetadataRefreshOptions());
|
ProviderManager.QueueRefresh(Id, new MetadataRefreshOptions(FileSystem));
|
||||||
return Task.FromResult(true);
|
return Task.FromResult(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1434,7 +1443,24 @@ namespace MediaBrowser.Controller.Entities
|
|||||||
return GetImageInfo(type, imageIndex) != null;
|
return GetImageInfo(type, imageIndex) != null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void SetImagePath(ImageType type, int index, FileSystemInfo file)
|
public void SetImage(ItemImageInfo image, int index)
|
||||||
|
{
|
||||||
|
if (image.Type == ImageType.Chapter)
|
||||||
|
{
|
||||||
|
throw new ArgumentException("Cannot set chapter images using SetImagePath");
|
||||||
|
}
|
||||||
|
|
||||||
|
var existingImage = GetImageInfo(image.Type, index);
|
||||||
|
|
||||||
|
if (existingImage != null)
|
||||||
|
{
|
||||||
|
ImageInfos.Remove(existingImage);
|
||||||
|
}
|
||||||
|
|
||||||
|
ImageInfos.Add(image);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SetImagePath(ImageType type, int index, FileSystemMetadata file)
|
||||||
{
|
{
|
||||||
if (type == ImageType.Chapter)
|
if (type == ImageType.Chapter)
|
||||||
{
|
{
|
||||||
@ -1475,18 +1501,21 @@ namespace MediaBrowser.Controller.Entities
|
|||||||
// Remove it from the item
|
// Remove it from the item
|
||||||
RemoveImage(info);
|
RemoveImage(info);
|
||||||
|
|
||||||
// Delete the source file
|
if (info.IsLocalFile)
|
||||||
var currentFile = new FileInfo(info.Path);
|
|
||||||
|
|
||||||
// Deletion will fail if the file is hidden so remove the attribute first
|
|
||||||
if (currentFile.Exists)
|
|
||||||
{
|
{
|
||||||
if ((currentFile.Attributes & FileAttributes.Hidden) == FileAttributes.Hidden)
|
// Delete the source file
|
||||||
{
|
var currentFile = new FileInfo(info.Path);
|
||||||
currentFile.Attributes &= ~FileAttributes.Hidden;
|
|
||||||
}
|
|
||||||
|
|
||||||
FileSystem.DeleteFile(currentFile.FullName);
|
// Deletion will fail if the file is hidden so remove the attribute first
|
||||||
|
if (currentFile.Exists)
|
||||||
|
{
|
||||||
|
if ((currentFile.Attributes & FileAttributes.Hidden) == FileAttributes.Hidden)
|
||||||
|
{
|
||||||
|
currentFile.Attributes &= ~FileAttributes.Hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
FileSystem.DeleteFile(currentFile.FullName);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return UpdateToRepository(ItemUpdateType.ImageUpdate, CancellationToken.None);
|
return UpdateToRepository(ItemUpdateType.ImageUpdate, CancellationToken.None);
|
||||||
@ -1507,11 +1536,16 @@ namespace MediaBrowser.Controller.Entities
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public bool ValidateImages(IDirectoryService directoryService)
|
public bool ValidateImages(IDirectoryService directoryService)
|
||||||
{
|
{
|
||||||
var allDirectories = ImageInfos.Select(i => System.IO.Path.GetDirectoryName(i.Path)).Distinct(StringComparer.OrdinalIgnoreCase).ToList();
|
var allFiles = ImageInfos
|
||||||
var allFiles = allDirectories.SelectMany(directoryService.GetFiles).Select(i => i.FullName).ToList();
|
.Where(i => i.IsLocalFile)
|
||||||
|
.Select(i => System.IO.Path.GetDirectoryName(i.Path))
|
||||||
|
.Distinct(StringComparer.OrdinalIgnoreCase)
|
||||||
|
.SelectMany(directoryService.GetFiles)
|
||||||
|
.Select(i => i.FullName)
|
||||||
|
.ToList();
|
||||||
|
|
||||||
var deletedImages = ImageInfos
|
var deletedImages = ImageInfos
|
||||||
.Where(image => !allFiles.Contains(image.Path, StringComparer.OrdinalIgnoreCase))
|
.Where(image => image.IsLocalFile && !allFiles.Contains(image.Path, StringComparer.OrdinalIgnoreCase))
|
||||||
.ToList();
|
.ToList();
|
||||||
|
|
||||||
if (deletedImages.Count > 0)
|
if (deletedImages.Count > 0)
|
||||||
@ -1584,11 +1618,6 @@ namespace MediaBrowser.Controller.Entities
|
|||||||
return ImageInfos.Where(i => i.Type == imageType);
|
return ImageInfos.Where(i => i.Type == imageType);
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool AddImages(ImageType imageType, IEnumerable<FileInfo> images)
|
|
||||||
{
|
|
||||||
return AddImages(imageType, images.Cast<FileSystemInfo>().ToList());
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Adds the images.
|
/// Adds the images.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -1596,7 +1625,7 @@ namespace MediaBrowser.Controller.Entities
|
|||||||
/// <param name="images">The images.</param>
|
/// <param name="images">The images.</param>
|
||||||
/// <returns><c>true</c> if XXXX, <c>false</c> otherwise.</returns>
|
/// <returns><c>true</c> if XXXX, <c>false</c> otherwise.</returns>
|
||||||
/// <exception cref="System.ArgumentException">Cannot call AddImages with chapter images</exception>
|
/// <exception cref="System.ArgumentException">Cannot call AddImages with chapter images</exception>
|
||||||
public bool AddImages(ImageType imageType, List<FileSystemInfo> images)
|
public bool AddImages(ImageType imageType, List<FileSystemMetadata> images)
|
||||||
{
|
{
|
||||||
if (imageType == ImageType.Chapter)
|
if (imageType == ImageType.Chapter)
|
||||||
{
|
{
|
||||||
@ -1606,7 +1635,7 @@ namespace MediaBrowser.Controller.Entities
|
|||||||
var existingImages = GetImages(imageType)
|
var existingImages = GetImages(imageType)
|
||||||
.ToList();
|
.ToList();
|
||||||
|
|
||||||
var newImageList = new List<FileSystemInfo>();
|
var newImageList = new List<FileSystemMetadata>();
|
||||||
var imageAdded = false;
|
var imageAdded = false;
|
||||||
|
|
||||||
foreach (var newImage in images)
|
foreach (var newImage in images)
|
||||||
@ -1626,7 +1655,10 @@ namespace MediaBrowser.Controller.Entities
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
existing.DateModified = FileSystem.GetLastWriteTimeUtc(newImage);
|
if (existing.IsLocalFile)
|
||||||
|
{
|
||||||
|
existing.DateModified = FileSystem.GetLastWriteTimeUtc(newImage);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1635,7 +1667,7 @@ namespace MediaBrowser.Controller.Entities
|
|||||||
var newImagePaths = images.Select(i => i.FullName).ToList();
|
var newImagePaths = images.Select(i => i.FullName).ToList();
|
||||||
|
|
||||||
var deleted = existingImages
|
var deleted = existingImages
|
||||||
.Where(i => !newImagePaths.Contains(i.Path, StringComparer.OrdinalIgnoreCase) && !File.Exists(i.Path))
|
.Where(i => i.IsLocalFile && !newImagePaths.Contains(i.Path, StringComparer.OrdinalIgnoreCase) && !FileSystem.FileExists(i.Path))
|
||||||
.ToList();
|
.ToList();
|
||||||
|
|
||||||
ImageInfos = ImageInfos.Except(deleted).ToList();
|
ImageInfos = ImageInfos.Except(deleted).ToList();
|
||||||
@ -1646,7 +1678,7 @@ namespace MediaBrowser.Controller.Entities
|
|||||||
return newImageList.Count > 0;
|
return newImageList.Count > 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
private ItemImageInfo GetImageInfo(FileSystemInfo file, ImageType type)
|
private ItemImageInfo GetImageInfo(FileSystemMetadata file, ImageType type)
|
||||||
{
|
{
|
||||||
return new ItemImageInfo
|
return new ItemImageInfo
|
||||||
{
|
{
|
||||||
@ -1686,6 +1718,12 @@ namespace MediaBrowser.Controller.Entities
|
|||||||
return Task.FromResult(true);
|
return Task.FromResult(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!info1.IsLocalFile || !info2.IsLocalFile)
|
||||||
|
{
|
||||||
|
// TODO: Not supported yet
|
||||||
|
return Task.FromResult(true);
|
||||||
|
}
|
||||||
|
|
||||||
var path1 = info1.Path;
|
var path1 = info1.Path;
|
||||||
var path2 = info2.Path;
|
var path2 = info2.Path;
|
||||||
|
|
||||||
@ -1769,7 +1807,7 @@ namespace MediaBrowser.Controller.Entities
|
|||||||
{
|
{
|
||||||
foreach (var map in ConfigurationManager.Configuration.PathSubstitutions)
|
foreach (var map in ConfigurationManager.Configuration.PathSubstitutions)
|
||||||
{
|
{
|
||||||
path = FileSystem.SubstitutePath(path, map.From, map.To);
|
path = LibraryManager.SubstitutePath(path, map.From, map.To);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1810,7 +1848,7 @@ namespace MediaBrowser.Controller.Entities
|
|||||||
|
|
||||||
if (video == null)
|
if (video == null)
|
||||||
{
|
{
|
||||||
video = LibraryManager.ResolvePath(new FileInfo(path)) as Video;
|
video = LibraryManager.ResolvePath(FileSystem.GetFileSystemInfo(path)) as Video;
|
||||||
|
|
||||||
newOptions.ForceSave = true;
|
newOptions.ForceSave = true;
|
||||||
}
|
}
|
||||||
|
@ -7,7 +7,7 @@ using MediaBrowser.Model.Users;
|
|||||||
|
|
||||||
namespace MediaBrowser.Controller.Entities
|
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
|
public override string MediaType
|
||||||
{
|
{
|
||||||
@ -25,14 +25,6 @@ namespace MediaBrowser.Controller.Entities
|
|||||||
|
|
||||||
public string SeriesName { get; set; }
|
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()
|
public Book()
|
||||||
{
|
{
|
||||||
Tags = new List<string>();
|
Tags = new List<string>();
|
||||||
|
@ -8,6 +8,8 @@ using System.Linq;
|
|||||||
using System.Runtime.Serialization;
|
using System.Runtime.Serialization;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
using CommonIO;
|
||||||
|
using MediaBrowser.Common.IO;
|
||||||
|
|
||||||
namespace MediaBrowser.Controller.Entities
|
namespace MediaBrowser.Controller.Entities
|
||||||
{
|
{
|
||||||
@ -80,7 +82,7 @@ namespace MediaBrowser.Controller.Entities
|
|||||||
|
|
||||||
public List<string> PhysicalLocationsList { get; set; }
|
public List<string> PhysicalLocationsList { get; set; }
|
||||||
|
|
||||||
protected override IEnumerable<FileSystemInfo> GetFileSystemChildren(IDirectoryService directoryService)
|
protected override IEnumerable<FileSystemMetadata> GetFileSystemChildren(IDirectoryService directoryService)
|
||||||
{
|
{
|
||||||
return CreateResolveArgs(directoryService).FileSystemChildren;
|
return CreateResolveArgs(directoryService).FileSystemChildren;
|
||||||
}
|
}
|
||||||
@ -107,7 +109,7 @@ namespace MediaBrowser.Controller.Entities
|
|||||||
|
|
||||||
var args = new ItemResolveArgs(ConfigurationManager.ApplicationPaths, directoryService)
|
var args = new ItemResolveArgs(ConfigurationManager.ApplicationPaths, directoryService)
|
||||||
{
|
{
|
||||||
FileInfo = new DirectoryInfo(path),
|
FileInfo = FileSystem.GetDirectoryInfo(path),
|
||||||
Path = path,
|
Path = path,
|
||||||
Parent = Parent,
|
Parent = Parent,
|
||||||
CollectionType = CollectionType
|
CollectionType = CollectionType
|
||||||
@ -129,7 +131,7 @@ namespace MediaBrowser.Controller.Entities
|
|||||||
{
|
{
|
||||||
var paths = LibraryManager.NormalizeRootPathList(fileSystemDictionary.Keys);
|
var paths = LibraryManager.NormalizeRootPathList(fileSystemDictionary.Keys);
|
||||||
|
|
||||||
fileSystemDictionary = paths.Select(i => (FileSystemInfo)new DirectoryInfo(i)).ToDictionary(i => i.FullName);
|
fileSystemDictionary = paths.Select(FileSystem.GetDirectoryInfo).ToDictionary(i => i.FullName);
|
||||||
}
|
}
|
||||||
|
|
||||||
args.FileSystemDictionary = fileSystemDictionary;
|
args.FileSystemDictionary = fileSystemDictionary;
|
||||||
|
@ -13,13 +13,15 @@ using System.Linq;
|
|||||||
using System.Runtime.Serialization;
|
using System.Runtime.Serialization;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
using CommonIO;
|
||||||
|
using MediaBrowser.Common.IO;
|
||||||
|
|
||||||
namespace MediaBrowser.Controller.Entities
|
namespace MediaBrowser.Controller.Entities
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Class Folder
|
/// Class Folder
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class Folder : BaseItem, IHasThemeMedia, IHasTags, IHasPreferredMetadataLanguage
|
public class Folder : BaseItem, IHasThemeMedia, IHasTags
|
||||||
{
|
{
|
||||||
public static IUserManager UserManager { get; set; }
|
public static IUserManager UserManager { get; set; }
|
||||||
public static IUserViewManager UserViewManager { get; set; }
|
public static IUserViewManager UserViewManager { get; set; }
|
||||||
@ -28,14 +30,6 @@ namespace MediaBrowser.Controller.Entities
|
|||||||
public List<Guid> ThemeVideoIds { get; set; }
|
public List<Guid> ThemeVideoIds { get; set; }
|
||||||
public List<string> Tags { 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()
|
public Folder()
|
||||||
{
|
{
|
||||||
LinkedChildren = new List<LinkedChild>();
|
LinkedChildren = new List<LinkedChild>();
|
||||||
@ -48,7 +42,7 @@ namespace MediaBrowser.Controller.Entities
|
|||||||
[IgnoreDataMember]
|
[IgnoreDataMember]
|
||||||
public virtual bool IsPreSorted
|
public virtual bool IsPreSorted
|
||||||
{
|
{
|
||||||
get { return ConfigurationManager.Configuration.EnableWindowsShortcuts; }
|
get { return false; }
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -120,7 +114,7 @@ namespace MediaBrowser.Controller.Entities
|
|||||||
[IgnoreDataMember]
|
[IgnoreDataMember]
|
||||||
protected virtual bool SupportsShortcutChildren
|
protected virtual bool SupportsShortcutChildren
|
||||||
{
|
{
|
||||||
get { return false; }
|
get { return ConfigurationManager.Configuration.EnableWindowsShortcuts; }
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -213,7 +207,7 @@ namespace MediaBrowser.Controller.Entities
|
|||||||
return base.OfficialRatingForComparison;
|
return base.OfficialRatingForComparison;
|
||||||
}
|
}
|
||||||
|
|
||||||
return !string.IsNullOrEmpty(base.OfficialRatingForComparison) ? base.OfficialRatingForComparison : "None";
|
return !string.IsNullOrWhiteSpace(base.OfficialRatingForComparison) ? base.OfficialRatingForComparison : "None";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -320,7 +314,7 @@ namespace MediaBrowser.Controller.Entities
|
|||||||
[IgnoreDataMember]
|
[IgnoreDataMember]
|
||||||
public IEnumerable<BaseItem> Children
|
public IEnumerable<BaseItem> Children
|
||||||
{
|
{
|
||||||
get { return ActualChildren; }
|
get { return ActualChildren.ToList(); }
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -371,7 +365,7 @@ namespace MediaBrowser.Controller.Entities
|
|||||||
|
|
||||||
public Task ValidateChildren(IProgress<double> progress, CancellationToken cancellationToken)
|
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>
|
/// <summary>
|
||||||
@ -474,7 +468,7 @@ namespace MediaBrowser.Controller.Entities
|
|||||||
currentChild.DateModified = child.DateModified;
|
currentChild.DateModified = child.DateModified;
|
||||||
}
|
}
|
||||||
|
|
||||||
currentChild.IsOffline = false;
|
await UpdateIsOffline(currentChild, false).ConfigureAwait(false);
|
||||||
validChildren.Add(currentChild);
|
validChildren.Add(currentChild);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@ -509,12 +503,12 @@ namespace MediaBrowser.Controller.Entities
|
|||||||
|
|
||||||
else if (!string.IsNullOrEmpty(item.Path) && IsPathOffline(item.Path))
|
else if (!string.IsNullOrEmpty(item.Path) && IsPathOffline(item.Path))
|
||||||
{
|
{
|
||||||
item.IsOffline = true;
|
await UpdateIsOffline(item, true).ConfigureAwait(false);
|
||||||
validChildren.Add(item);
|
validChildren.Add(item);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
item.IsOffline = false;
|
await UpdateIsOffline(item, false).ConfigureAwait(false);
|
||||||
actualRemovals.Add(item);
|
actualRemovals.Add(item);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -569,6 +563,17 @@ namespace MediaBrowser.Controller.Entities
|
|||||||
progress.Report(100);
|
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)
|
private async Task RefreshMetadataRecursive(MetadataRefreshOptions refreshOptions, bool recursive, IProgress<double> progress, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
var children = ActualChildren.ToList();
|
var children = ActualChildren.ToList();
|
||||||
@ -691,9 +696,9 @@ namespace MediaBrowser.Controller.Entities
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="path">The path.</param>
|
/// <param name="path">The path.</param>
|
||||||
/// <returns><c>true</c> if the specified path is offline; otherwise, <c>false</c>.</returns>
|
/// <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;
|
return false;
|
||||||
}
|
}
|
||||||
@ -703,7 +708,7 @@ namespace MediaBrowser.Controller.Entities
|
|||||||
// Depending on whether the path is local or unc, it may return either null or '\' at the top
|
// 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)
|
while (!string.IsNullOrEmpty(path) && path.Length > 1)
|
||||||
{
|
{
|
||||||
if (Directory.Exists(path))
|
if (FileSystem.DirectoryExists(path))
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -725,12 +730,12 @@ namespace MediaBrowser.Controller.Entities
|
|||||||
/// <param name="folders">The folders.</param>
|
/// <param name="folders">The folders.</param>
|
||||||
/// <param name="path">The path.</param>
|
/// <param name="path">The path.</param>
|
||||||
/// <returns><c>true</c> if the specified folders contains path; otherwise, <c>false</c>.</returns>
|
/// <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));
|
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);
|
return string.Equals(parent, path, StringComparison.OrdinalIgnoreCase) || FileSystem.ContainsSubPath(parent, path);
|
||||||
}
|
}
|
||||||
@ -752,21 +757,24 @@ namespace MediaBrowser.Controller.Entities
|
|||||||
/// <returns>IEnumerable{BaseItem}.</returns>
|
/// <returns>IEnumerable{BaseItem}.</returns>
|
||||||
protected IEnumerable<BaseItem> GetCachedChildren()
|
protected IEnumerable<BaseItem> GetCachedChildren()
|
||||||
{
|
{
|
||||||
var childrenItems = ItemRepository.GetChildrenItems(Id).Select(RetrieveChild).Where(i => i != null);
|
if (ConfigurationManager.Configuration.DisableStartupScan)
|
||||||
|
{
|
||||||
|
return ItemRepository.GetChildrenItems(Id).Select(RetrieveChild).Where(i => i != null);
|
||||||
|
//return ItemRepository.GetItems(new InternalItemsQuery
|
||||||
|
//{
|
||||||
|
// ParentId = Id
|
||||||
|
|
||||||
//var children = ItemRepository.GetChildren(Id).Select(RetrieveChild).Where(i => i != null).ToList();
|
//}).Items.Select(RetrieveChild).Where(i => i != null);
|
||||||
|
}
|
||||||
//if (children.Count != childrenItems.Count)
|
else
|
||||||
//{
|
{
|
||||||
// var b = this;
|
return ItemRepository.GetChildrenItems(Id).Select(RetrieveChild).Where(i => i != null);
|
||||||
//}
|
}
|
||||||
|
|
||||||
return childrenItems;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private BaseItem RetrieveChild(BaseItem child)
|
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));
|
Logger.Error("Item found with empty Id: " + (child.Path ?? child.Name));
|
||||||
return null;
|
return null;
|
||||||
@ -1064,7 +1072,7 @@ namespace MediaBrowser.Controller.Entities
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override async Task<bool> RefreshedOwnedItems(MetadataRefreshOptions options, List<FileSystemInfo> fileSystemChildren, CancellationToken cancellationToken)
|
protected override async Task<bool> RefreshedOwnedItems(MetadataRefreshOptions options, List<FileSystemMetadata> fileSystemChildren, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
var changesFound = false;
|
var changesFound = false;
|
||||||
|
|
||||||
@ -1085,7 +1093,7 @@ namespace MediaBrowser.Controller.Entities
|
|||||||
/// Refreshes the linked children.
|
/// Refreshes the linked children.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <returns><c>true</c> if XXXX, <c>false</c> otherwise</returns>
|
/// <returns><c>true</c> if XXXX, <c>false</c> otherwise</returns>
|
||||||
private bool RefreshLinkedChildren(IEnumerable<FileSystemInfo> fileSystemChildren)
|
private bool RefreshLinkedChildren(IEnumerable<FileSystemMetadata> fileSystemChildren)
|
||||||
{
|
{
|
||||||
var currentManualLinks = LinkedChildren.Where(i => i.Type == LinkedChildType.Manual).ToList();
|
var currentManualLinks = LinkedChildren.Where(i => i.Type == LinkedChildType.Manual).ToList();
|
||||||
var currentShortcutLinks = LinkedChildren.Where(i => i.Type == LinkedChildType.Shortcut).ToList();
|
var currentShortcutLinks = LinkedChildren.Where(i => i.Type == LinkedChildType.Shortcut).ToList();
|
||||||
@ -1170,9 +1178,16 @@ namespace MediaBrowser.Controller.Entities
|
|||||||
DateTime? datePlayed,
|
DateTime? datePlayed,
|
||||||
bool resetPosition)
|
bool resetPosition)
|
||||||
{
|
{
|
||||||
|
var itemsResult = await GetItems(new InternalItemsQuery
|
||||||
|
{
|
||||||
|
User = user,
|
||||||
|
Recursive = true,
|
||||||
|
IsFolder = false
|
||||||
|
|
||||||
|
}).ConfigureAwait(false);
|
||||||
|
|
||||||
// Sweep through recursively and update status
|
// Sweep through recursively and update status
|
||||||
var tasks = GetRecursiveChildren(user, i => !i.IsFolder && i.LocationType != LocationType.Virtual)
|
var tasks = itemsResult.Items.Select(c => c.MarkPlayed(user, datePlayed, resetPosition));
|
||||||
.Select(c => c.MarkPlayed(user, datePlayed, resetPosition));
|
|
||||||
|
|
||||||
await Task.WhenAll(tasks).ConfigureAwait(false);
|
await Task.WhenAll(tasks).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
@ -1184,9 +1199,16 @@ namespace MediaBrowser.Controller.Entities
|
|||||||
/// <returns>Task.</returns>
|
/// <returns>Task.</returns>
|
||||||
public override async Task MarkUnplayed(User user)
|
public override async Task MarkUnplayed(User user)
|
||||||
{
|
{
|
||||||
|
var itemsResult = await GetItems(new InternalItemsQuery
|
||||||
|
{
|
||||||
|
User = user,
|
||||||
|
Recursive = true,
|
||||||
|
IsFolder = false
|
||||||
|
|
||||||
|
}).ConfigureAwait(false);
|
||||||
|
|
||||||
// Sweep through recursively and update status
|
// Sweep through recursively and update status
|
||||||
var tasks = GetRecursiveChildren(user, i => !i.IsFolder && i.LocationType != LocationType.Virtual)
|
var tasks = itemsResult.Items.Select(c => c.MarkUnplayed(user));
|
||||||
.Select(c => c.MarkUnplayed(user));
|
|
||||||
|
|
||||||
await Task.WhenAll(tasks).ConfigureAwait(false);
|
await Task.WhenAll(tasks).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
|
@ -8,19 +8,11 @@ using System.Linq;
|
|||||||
|
|
||||||
namespace MediaBrowser.Controller.Entities
|
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> ThemeSongIds { get; set; }
|
||||||
public List<Guid> ThemeVideoIds { 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()
|
public Game()
|
||||||
{
|
{
|
||||||
MultiPartGameFiles = new List<string>();
|
MultiPartGameFiles = new List<string>();
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user