mirror of
https://github.com/jellyfin/jellyfin.git
synced 2024-07-21 13:10:45 +02:00
commit
35778ebc02
|
@ -5,6 +5,7 @@ using System;
|
|||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using CommonIO;
|
||||
|
||||
namespace Emby.Drawing.Common
|
||||
{
|
||||
|
@ -220,4 +221,4 @@ namespace Emby.Drawing.Common
|
|||
throw new ArgumentException(ErrorMessage);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -12,7 +12,6 @@
|
|||
<TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
|
||||
<FileAlignment>512</FileAlignment>
|
||||
<SolutionDir Condition="$(SolutionDir) == '' Or $(SolutionDir) == '*Undefined*'">..\</SolutionDir>
|
||||
<RestorePackages>true</RestorePackages>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
|
@ -32,10 +31,20 @@
|
|||
<WarningLevel>4</WarningLevel>
|
||||
</PropertyGroup>
|
||||
<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">
|
||||
<SpecificVersion>False</SpecificVersion>
|
||||
<HintPath>..\packages\ImageMagickSharp.1.0.0.16\lib\net45\ImageMagickSharp.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 Include="policy.2.0.taglib-sharp">
|
||||
<HintPath>..\packages\taglib.2.1.0.0\lib\policy.2.0.taglib-sharp.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="System" />
|
||||
<Reference Include="System.Core" />
|
||||
<Reference Include="System.Drawing" />
|
||||
|
@ -44,11 +53,15 @@
|
|||
<Reference Include="Microsoft.CSharp" />
|
||||
<Reference Include="System.Data" />
|
||||
<Reference Include="System.Xml" />
|
||||
<Reference Include="taglib-sharp">
|
||||
<HintPath>..\packages\taglib.2.1.0.0\lib\taglib-sharp.dll</HintPath>
|
||||
</Reference>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="..\SharedVersion.cs">
|
||||
<Link>Properties\SharedVersion.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="Common\ImageHeader.cs" />
|
||||
<Compile Include="GDI\DynamicImageHelpers.cs" />
|
||||
<Compile Include="GDI\GDIImageEncoder.cs" />
|
||||
<Compile Include="GDI\ImageExtensions.cs" />
|
||||
|
@ -56,13 +69,13 @@
|
|||
<Compile Include="GDI\PlayedIndicatorDrawer.cs" />
|
||||
<Compile Include="GDI\UnplayedCountIndicator.cs" />
|
||||
<Compile Include="IImageEncoder.cs" />
|
||||
<Compile Include="Common\ImageHeader.cs" />
|
||||
<Compile Include="ImageHelpers.cs" />
|
||||
<Compile Include="ImageMagick\ImageMagickEncoder.cs" />
|
||||
<Compile Include="ImageMagick\StripCollageBuilder.cs" />
|
||||
<Compile Include="ImageProcessor.cs" />
|
||||
<Compile Include="ImageMagick\PercentPlayedDrawer.cs" />
|
||||
<Compile Include="ImageMagick\PlayedIndicatorDrawer.cs" />
|
||||
<Compile Include="NullImageEncoder.cs" />
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
<Compile Include="ImageMagick\UnplayedCountIndicator.cs" />
|
||||
</ItemGroup>
|
||||
|
@ -87,6 +100,9 @@
|
|||
<ItemGroup>
|
||||
<None Include="packages.config" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<EmbeddedResource Include="GDI\empty.png" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
|
||||
Other similar extension points exist, see Microsoft.Common.targets.
|
||||
|
|
|
@ -4,6 +4,7 @@ using System.Drawing;
|
|||
using System.Drawing.Drawing2D;
|
||||
using System.Drawing.Imaging;
|
||||
using System.IO;
|
||||
using CommonIO;
|
||||
|
||||
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.Logging;
|
||||
using System;
|
||||
|
@ -8,6 +7,7 @@ using System.Drawing.Drawing2D;
|
|||
using System.Drawing.Imaging;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using CommonIO;
|
||||
using ImageFormat = MediaBrowser.Model.Drawing.ImageFormat;
|
||||
|
||||
namespace Emby.Drawing.GDI
|
||||
|
@ -22,7 +22,20 @@ namespace Emby.Drawing.GDI
|
|||
_fileSystem = fileSystem;
|
||||
_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
|
||||
|
@ -66,7 +79,7 @@ namespace Emby.Drawing.GDI
|
|||
{
|
||||
using (var croppedImage = image.CropWhitespace())
|
||||
{
|
||||
Directory.CreateDirectory(Path.GetDirectoryName(outputPath));
|
||||
_fileSystem.CreateDirectory(Path.GetDirectoryName(outputPath));
|
||||
|
||||
using (var outputStream = _fileSystem.GetFileStream(outputPath, FileMode.Create, FileAccess.Write, FileShare.Read, false))
|
||||
{
|
||||
|
@ -120,7 +133,7 @@ namespace Emby.Drawing.GDI
|
|||
|
||||
var outputFormat = GetOutputFormat(originalImage, selectedOutputFormat);
|
||||
|
||||
Directory.CreateDirectory(Path.GetDirectoryName(cacheFilePath));
|
||||
_fileSystem.CreateDirectory(Path.GetDirectoryName(cacheFilePath));
|
||||
|
||||
// Save to the cache location
|
||||
using (var cacheFileStream = _fileSystem.GetFileStream(cacheFilePath, FileMode.Create, FileAccess.Write, FileShare.Read, false))
|
||||
|
@ -252,5 +265,15 @@ namespace Emby.Drawing.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>
|
||||
ImageFormat[] SupportedOutputFormats { get; }
|
||||
/// <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.
|
||||
/// </summary>
|
||||
/// <param name="inputPath">The input path.</param>
|
||||
|
@ -49,5 +43,17 @@ namespace Emby.Drawing
|
|||
/// </summary>
|
||||
/// <value>The name.</value>
|
||||
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.IO;
|
||||
using System.Linq;
|
||||
using CommonIO;
|
||||
|
||||
namespace Emby.Drawing.ImageMagick
|
||||
{
|
||||
|
@ -16,14 +17,16 @@ namespace Emby.Drawing.ImageMagick
|
|||
private readonly ILogger _logger;
|
||||
private readonly IApplicationPaths _appPaths;
|
||||
private readonly IHttpClient _httpClient;
|
||||
private readonly IFileSystem _fileSystem;
|
||||
|
||||
public ImageMagickEncoder(ILogger logger, IApplicationPaths appPaths, IHttpClient httpClient)
|
||||
public ImageMagickEncoder(ILogger logger, IApplicationPaths appPaths, IHttpClient httpClient, IFileSystem fileSystem)
|
||||
{
|
||||
_logger = logger;
|
||||
_appPaths = appPaths;
|
||||
_httpClient = httpClient;
|
||||
_fileSystem = fileSystem;
|
||||
|
||||
LogImageMagickVersion();
|
||||
LogVersion();
|
||||
}
|
||||
|
||||
public string[] SupportedInputFormats
|
||||
|
@ -64,7 +67,7 @@ namespace Emby.Drawing.ImageMagick
|
|||
}
|
||||
}
|
||||
|
||||
private void LogImageMagickVersion()
|
||||
private void LogVersion()
|
||||
{
|
||||
_logger.Info("ImageMagick version: " + Wand.VersionString);
|
||||
TestWebp();
|
||||
|
@ -77,16 +80,16 @@ namespace Emby.Drawing.ImageMagick
|
|||
try
|
||||
{
|
||||
var tmpPath = Path.Combine(_appPaths.TempDirectory, Guid.NewGuid() + ".webp");
|
||||
Directory.CreateDirectory(Path.GetDirectoryName(tmpPath));
|
||||
_fileSystem.CreateDirectory(Path.GetDirectoryName(tmpPath));
|
||||
|
||||
using (var wand = new MagickWand(1, 1, new PixelWand("none", 1)))
|
||||
{
|
||||
wand.SaveImage(tmpPath);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
catch
|
||||
{
|
||||
_logger.ErrorException("Error loading webp: ", ex);
|
||||
//_logger.ErrorException("Error loading webp: ", ex);
|
||||
_webpAvailable = false;
|
||||
}
|
||||
}
|
||||
|
@ -100,6 +103,7 @@ namespace Emby.Drawing.ImageMagick
|
|||
wand.CurrentImage.TrimImage(10);
|
||||
wand.SaveImage(outputPath);
|
||||
}
|
||||
SaveDelay();
|
||||
}
|
||||
|
||||
public ImageSize GetImageSize(string path)
|
||||
|
@ -159,6 +163,7 @@ namespace Emby.Drawing.ImageMagick
|
|||
}
|
||||
}
|
||||
}
|
||||
SaveDelay();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -181,14 +186,14 @@ namespace Emby.Drawing.ImageMagick
|
|||
{
|
||||
var currentImageSize = new ImageSize(imageWidth, imageHeight);
|
||||
|
||||
var task = new PlayedIndicatorDrawer(_appPaths, _httpClient).DrawPlayedIndicator(wand, currentImageSize);
|
||||
var task = new PlayedIndicatorDrawer(_appPaths, _httpClient, _fileSystem).DrawPlayedIndicator(wand, currentImageSize);
|
||||
Task.WaitAll(task);
|
||||
}
|
||||
else if (options.UnplayedCount.HasValue)
|
||||
{
|
||||
var currentImageSize = new ImageSize(imageWidth, imageHeight);
|
||||
|
||||
new UnplayedCountIndicator(_appPaths).DrawUnplayedCountIndicator(wand, currentImageSize, options.UnplayedCount.Value);
|
||||
new UnplayedCountIndicator(_appPaths, _fileSystem).DrawUnplayedCountIndicator(wand, currentImageSize, options.UnplayedCount.Value);
|
||||
}
|
||||
|
||||
if (options.PercentPlayed > 0)
|
||||
|
@ -209,16 +214,25 @@ namespace Emby.Drawing.ImageMagick
|
|||
|
||||
if (ratio >= 1.4)
|
||||
{
|
||||
new StripCollageBuilder(_appPaths).BuildThumbCollage(options.InputPaths.ToList(), options.OutputPath, options.Width, options.Height, options.Text);
|
||||
new StripCollageBuilder(_appPaths, _fileSystem).BuildThumbCollage(options.InputPaths.ToList(), options.OutputPath, options.Width, options.Height, options.Text);
|
||||
}
|
||||
else if (ratio >= .9)
|
||||
{
|
||||
new StripCollageBuilder(_appPaths).BuildSquareCollage(options.InputPaths.ToList(), options.OutputPath, options.Width, options.Height, options.Text);
|
||||
new StripCollageBuilder(_appPaths, _fileSystem).BuildSquareCollage(options.InputPaths.ToList(), options.OutputPath, options.Width, options.Height, options.Text);
|
||||
}
|
||||
else
|
||||
{
|
||||
new StripCollageBuilder(_appPaths).BuildPosterCollage(options.InputPaths.ToList(), options.OutputPath, options.Width, options.Height, options.Text);
|
||||
new StripCollageBuilder(_appPaths, _fileSystem).BuildPosterCollage(options.InputPaths.ToList(), options.OutputPath, options.Width, options.Height, options.Text);
|
||||
}
|
||||
|
||||
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
|
||||
|
@ -240,5 +254,15 @@ namespace Emby.Drawing.ImageMagick
|
|||
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.IO;
|
||||
using System.Threading.Tasks;
|
||||
using CommonIO;
|
||||
using MediaBrowser.Common.IO;
|
||||
|
||||
namespace Emby.Drawing.ImageMagick
|
||||
{
|
||||
|
@ -14,12 +16,14 @@ namespace Emby.Drawing.ImageMagick
|
|||
private const int OffsetFromTopRightCorner = 38;
|
||||
|
||||
private readonly IApplicationPaths _appPaths;
|
||||
private readonly IHttpClient _iHttpClient;
|
||||
private readonly IHttpClient _iHttpClient;
|
||||
private readonly IFileSystem _fileSystem;
|
||||
|
||||
public PlayedIndicatorDrawer(IApplicationPaths appPaths, IHttpClient iHttpClient)
|
||||
public PlayedIndicatorDrawer(IApplicationPaths appPaths, IHttpClient iHttpClient, IFileSystem fileSystem)
|
||||
{
|
||||
_appPaths = appPaths;
|
||||
_iHttpClient = iHttpClient;
|
||||
_fileSystem = fileSystem;
|
||||
}
|
||||
|
||||
public async Task DrawPlayedIndicator(MagickWand wand, ImageSize imageSize)
|
||||
|
@ -38,7 +42,7 @@ namespace Emby.Drawing.ImageMagick
|
|||
pixel.Opacity = 0;
|
||||
pixel.Color = "white";
|
||||
draw.FillColor = pixel;
|
||||
draw.Font = await DownloadFont("webdings.ttf", "https://github.com/MediaBrowser/Emby.Resources/raw/master/fonts/webdings.ttf", _appPaths, _iHttpClient).ConfigureAwait(false);
|
||||
draw.Font = await DownloadFont("webdings.ttf", "https://github.com/MediaBrowser/Emby.Resources/raw/master/fonts/webdings.ttf", _appPaths, _iHttpClient, _fileSystem).ConfigureAwait(false);
|
||||
draw.FontSize = FontSize;
|
||||
draw.FontStyle = FontStyleType.NormalStyle;
|
||||
draw.TextAlignment = TextAlignType.CenterAlign;
|
||||
|
@ -52,18 +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);
|
||||
|
||||
if (File.Exists(filePath))
|
||||
if (fileSystem.FileExists(filePath))
|
||||
{
|
||||
return filePath;
|
||||
}
|
||||
|
||||
var namespacePath = typeof(PlayedIndicatorDrawer).Namespace + ".fonts." + name;
|
||||
var tempPath = Path.Combine(paths.TempDirectory, Guid.NewGuid().ToString("N") + ".ttf");
|
||||
Directory.CreateDirectory(Path.GetDirectoryName(tempPath));
|
||||
fileSystem.CreateDirectory(Path.GetDirectoryName(tempPath));
|
||||
|
||||
using (var stream = typeof(PlayedIndicatorDrawer).Assembly.GetManifestResourceStream(namespacePath))
|
||||
{
|
||||
|
@ -73,11 +77,11 @@ namespace Emby.Drawing.ImageMagick
|
|||
}
|
||||
}
|
||||
|
||||
Directory.CreateDirectory(Path.GetDirectoryName(filePath));
|
||||
fileSystem.CreateDirectory(Path.GetDirectoryName(filePath));
|
||||
|
||||
try
|
||||
{
|
||||
File.Copy(tempPath, filePath, false);
|
||||
fileSystem.CopyFile(tempPath, filePath, false);
|
||||
}
|
||||
catch (IOException)
|
||||
{
|
||||
|
@ -87,11 +91,11 @@ namespace Emby.Drawing.ImageMagick
|
|||
return tempPath;
|
||||
}
|
||||
|
||||
internal static async Task<string> DownloadFont(string name, string url, IApplicationPaths paths, IHttpClient httpClient)
|
||||
internal static async Task<string> DownloadFont(string name, string url, IApplicationPaths paths, IHttpClient httpClient, IFileSystem fileSystem)
|
||||
{
|
||||
var filePath = Path.Combine(paths.ProgramDataPath, "fonts", name);
|
||||
|
||||
if (File.Exists(filePath))
|
||||
if (fileSystem.FileExists(filePath))
|
||||
{
|
||||
return filePath;
|
||||
}
|
||||
|
@ -103,11 +107,11 @@ namespace Emby.Drawing.ImageMagick
|
|||
|
||||
}).ConfigureAwait(false);
|
||||
|
||||
Directory.CreateDirectory(Path.GetDirectoryName(filePath));
|
||||
fileSystem.CreateDirectory(Path.GetDirectoryName(filePath));
|
||||
|
||||
try
|
||||
{
|
||||
File.Copy(tempPath, filePath, false);
|
||||
fileSystem.CopyFile(tempPath, filePath, false);
|
||||
}
|
||||
catch (IOException)
|
||||
{
|
||||
|
|
|
@ -2,16 +2,20 @@
|
|||
using MediaBrowser.Common.Configuration;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using CommonIO;
|
||||
using MediaBrowser.Common.IO;
|
||||
|
||||
namespace Emby.Drawing.ImageMagick
|
||||
{
|
||||
public class StripCollageBuilder
|
||||
{
|
||||
private readonly IApplicationPaths _appPaths;
|
||||
private readonly IFileSystem _fileSystem;
|
||||
|
||||
public StripCollageBuilder(IApplicationPaths appPaths)
|
||||
public StripCollageBuilder(IApplicationPaths appPaths, IFileSystem fileSystem)
|
||||
{
|
||||
_appPaths = appPaths;
|
||||
_fileSystem = fileSystem;
|
||||
}
|
||||
|
||||
public void BuildPosterCollage(List<string> paths, string outputPath, int width, int height, string text)
|
||||
|
@ -145,17 +149,17 @@ namespace Emby.Drawing.ImageMagick
|
|||
|
||||
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()))
|
||||
{
|
||||
var wand = new MagickWand(width, height);
|
||||
wand.OpenImage("gradient:#111111-#111111");
|
||||
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 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)
|
||||
{
|
||||
|
@ -350,14 +354,14 @@ namespace Emby.Drawing.ImageMagick
|
|||
|
||||
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()))
|
||||
{
|
||||
var wand = new MagickWand(width, height);
|
||||
wand.OpenImage("gradient:#111111-#111111");
|
||||
using (var draw = new DrawingWand())
|
||||
{
|
||||
var iSlice = Convert.ToInt32(width * .225);
|
||||
var iSlice = Convert.ToInt32(width * .3);
|
||||
int iTrans = Convert.ToInt32(height * .25);
|
||||
int iHeight = Convert.ToInt32(height * .63);
|
||||
var horizontalImagePadding = Convert.ToInt32(width * 0.02);
|
||||
|
@ -490,7 +494,7 @@ namespace Emby.Drawing.ImageMagick
|
|||
|
||||
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 MediaBrowser.Common.Configuration;
|
||||
using MediaBrowser.Common.IO;
|
||||
using MediaBrowser.Model.Drawing;
|
||||
using System.Globalization;
|
||||
using CommonIO;
|
||||
|
||||
namespace Emby.Drawing.ImageMagick
|
||||
{
|
||||
|
@ -10,10 +12,12 @@ namespace Emby.Drawing.ImageMagick
|
|||
private const int OffsetFromTopRightCorner = 38;
|
||||
|
||||
private readonly IApplicationPaths _appPaths;
|
||||
private readonly IFileSystem _fileSystem;
|
||||
|
||||
public UnplayedCountIndicator(IApplicationPaths appPaths)
|
||||
public UnplayedCountIndicator(IApplicationPaths appPaths, IFileSystem fileSystem)
|
||||
{
|
||||
_appPaths = appPaths;
|
||||
_fileSystem = fileSystem;
|
||||
}
|
||||
|
||||
public void DrawUnplayedCountIndicator(MagickWand wand, ImageSize imageSize, int count)
|
||||
|
@ -33,7 +37,7 @@ namespace Emby.Drawing.ImageMagick
|
|||
pixel.Opacity = 0;
|
||||
pixel.Color = "white";
|
||||
draw.FillColor = pixel;
|
||||
draw.Font = PlayedIndicatorDrawer.ExtractFont("robotoregular.ttf", _appPaths);
|
||||
draw.Font = PlayedIndicatorDrawer.ExtractFont("robotoregular.ttf", _appPaths, _fileSystem);
|
||||
draw.FontStyle = FontStyleType.NormalStyle;
|
||||
draw.TextAlignment = TextAlignType.CenterAlign;
|
||||
draw.FontWeight = FontWeightType.RegularStyle;
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
using Emby.Drawing.Common;
|
||||
using MediaBrowser.Common.Extensions;
|
||||
using MediaBrowser.Common.Extensions;
|
||||
using MediaBrowser.Common.IO;
|
||||
using MediaBrowser.Controller;
|
||||
using MediaBrowser.Controller.Drawing;
|
||||
|
@ -17,6 +16,9 @@ using System.IO;
|
|||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using CommonIO;
|
||||
using Emby.Drawing.Common;
|
||||
using MediaBrowser.Controller.Library;
|
||||
|
||||
namespace Emby.Drawing
|
||||
{
|
||||
|
@ -52,18 +54,20 @@ namespace Emby.Drawing
|
|||
private readonly IServerApplicationPaths _appPaths;
|
||||
private readonly IImageEncoder _imageEncoder;
|
||||
private readonly SemaphoreSlim _imageProcessingSemaphore;
|
||||
private readonly Func<ILibraryManager> _libraryManager;
|
||||
|
||||
public ImageProcessor(ILogger logger,
|
||||
IServerApplicationPaths appPaths,
|
||||
IFileSystem fileSystem,
|
||||
IJsonSerializer jsonSerializer,
|
||||
IImageEncoder imageEncoder,
|
||||
int maxConcurrentImageProcesses)
|
||||
int maxConcurrentImageProcesses, Func<ILibraryManager> libraryManager)
|
||||
{
|
||||
_logger = logger;
|
||||
_fileSystem = fileSystem;
|
||||
_jsonSerializer = jsonSerializer;
|
||||
_imageEncoder = imageEncoder;
|
||||
_libraryManager = libraryManager;
|
||||
_appPaths = appPaths;
|
||||
|
||||
ImageEnhancers = new List<IImageEnhancer>();
|
||||
|
@ -106,6 +110,15 @@ namespace Emby.Drawing
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
public bool SupportsImageCollageCreation
|
||||
{
|
||||
get
|
||||
{
|
||||
return _imageEncoder.SupportsImageCollageCreation;
|
||||
}
|
||||
}
|
||||
|
||||
private string ResizedImageCachePath
|
||||
{
|
||||
get
|
||||
|
@ -157,7 +170,19 @@ namespace Emby.Drawing
|
|||
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)
|
||||
{
|
||||
|
@ -165,9 +190,9 @@ namespace Emby.Drawing
|
|||
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);
|
||||
|
||||
|
@ -180,7 +205,7 @@ namespace Emby.Drawing
|
|||
var tuple = await GetEnhancedImage(new ItemImageInfo
|
||||
{
|
||||
DateModified = dateModified,
|
||||
Type = options.Image.Type,
|
||||
Type = originalImage.Type,
|
||||
Path = originalImagePath
|
||||
|
||||
}, options.Item, options.ImageIndex, options.Enhancers).ConfigureAwait(false);
|
||||
|
@ -215,21 +240,18 @@ namespace Emby.Drawing
|
|||
{
|
||||
CheckDisposed();
|
||||
|
||||
if (!File.Exists(cacheFilePath))
|
||||
if (!_fileSystem.FileExists(cacheFilePath))
|
||||
{
|
||||
var newWidth = Convert.ToInt32(newSize.Width);
|
||||
var newHeight = Convert.ToInt32(newSize.Height);
|
||||
|
||||
Directory.CreateDirectory(Path.GetDirectoryName(cacheFilePath));
|
||||
_fileSystem.CreateDirectory(Path.GetDirectoryName(cacheFilePath));
|
||||
|
||||
await _imageProcessingSemaphore.WaitAsync().ConfigureAwait(false);
|
||||
|
||||
imageProcessingLockTaken = true;
|
||||
|
||||
_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
|
||||
|
@ -270,7 +292,7 @@ namespace Emby.Drawing
|
|||
await semaphore.WaitAsync().ConfigureAwait(false);
|
||||
|
||||
// Check again in case of contention
|
||||
if (File.Exists(croppedImagePath))
|
||||
if (_fileSystem.FileExists(croppedImagePath))
|
||||
{
|
||||
semaphore.Release();
|
||||
return GetResult(croppedImagePath);
|
||||
|
@ -280,13 +302,18 @@ namespace Emby.Drawing
|
|||
|
||||
try
|
||||
{
|
||||
Directory.CreateDirectory(Path.GetDirectoryName(croppedImagePath));
|
||||
_fileSystem.CreateDirectory(Path.GetDirectoryName(croppedImagePath));
|
||||
|
||||
await _imageProcessingSemaphore.WaitAsync().ConfigureAwait(false);
|
||||
imageProcessingLockTaken = true;
|
||||
|
||||
_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)
|
||||
{
|
||||
// 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());
|
||||
}
|
||||
|
||||
/// <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)
|
||||
{
|
||||
return GetImageSize(info.Path, info.DateModified, false);
|
||||
}
|
||||
|
||||
public ImageSize GetImageSize(string path)
|
||||
{
|
||||
return GetImageSize(path, _fileSystem.GetLastWriteTimeUtc(path), false);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the size of the image.
|
||||
/// </summary>
|
||||
|
@ -399,7 +421,11 @@ namespace Emby.Drawing
|
|||
{
|
||||
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;
|
||||
|
@ -413,28 +439,26 @@ namespace Emby.Drawing
|
|||
/// <returns>ImageSize.</returns>
|
||||
private ImageSize GetImageSizeInternal(string path, bool allowSlowMethod)
|
||||
{
|
||||
ImageSize size;
|
||||
|
||||
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
|
||||
{
|
||||
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 size;
|
||||
return ImageHeader.GetDimensions(path, _logger, _fileSystem);
|
||||
}
|
||||
|
||||
private readonly Timer _saveImageSizeTimer;
|
||||
|
@ -452,7 +476,7 @@ namespace Emby.Drawing
|
|||
try
|
||||
{
|
||||
var path = ImageSizeFile;
|
||||
Directory.CreateDirectory(Path.GetDirectoryName(path));
|
||||
_fileSystem.CreateDirectory(Path.GetDirectoryName(path));
|
||||
_jsonSerializer.SerializeToFile(_cachedImagedSizes, path);
|
||||
}
|
||||
catch (Exception ex)
|
||||
|
@ -624,7 +648,7 @@ namespace Emby.Drawing
|
|||
await semaphore.WaitAsync().ConfigureAwait(false);
|
||||
|
||||
// Check again in case of contention
|
||||
if (File.Exists(enhancedImagePath))
|
||||
if (_fileSystem.FileExists(enhancedImagePath))
|
||||
{
|
||||
semaphore.Release();
|
||||
return enhancedImagePath;
|
||||
|
@ -634,7 +658,7 @@ namespace Emby.Drawing
|
|||
|
||||
try
|
||||
{
|
||||
Directory.CreateDirectory(Path.GetDirectoryName(enhancedImagePath));
|
||||
_fileSystem.CreateDirectory(Path.GetDirectoryName(enhancedImagePath));
|
||||
|
||||
await _imageProcessingSemaphore.WaitAsync().ConfigureAwait(false);
|
||||
|
||||
|
@ -773,11 +797,11 @@ namespace Emby.Drawing
|
|||
|
||||
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);
|
||||
|
||||
_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
|
||||
{
|
||||
|
@ -799,7 +823,6 @@ namespace Emby.Drawing
|
|||
|
||||
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"?>
|
||||
<packages>
|
||||
<package id="CommonIO" version="1.0.0.5" targetFramework="net45" />
|
||||
<package id="ImageMagickSharp" version="1.0.0.16" targetFramework="net45" />
|
||||
<package id="Patterns.Logging" version="1.0.0.2" targetFramework="net45" />
|
||||
</packages>
|
|
@ -15,6 +15,7 @@ using System.IO;
|
|||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using CommonIO;
|
||||
|
||||
namespace MediaBrowser.Api
|
||||
{
|
||||
|
@ -95,7 +96,7 @@ namespace MediaBrowser.Api
|
|||
{
|
||||
var path = _config.ApplicationPaths.TranscodingTempPath;
|
||||
|
||||
foreach (var file in Directory.EnumerateFiles(path, "*", SearchOption.AllDirectories)
|
||||
foreach (var file in _fileSystem.GetFilePaths(path, true)
|
||||
.ToList())
|
||||
{
|
||||
_fileSystem.DeleteFile(file);
|
||||
|
@ -567,7 +568,7 @@ namespace MediaBrowser.Api
|
|||
var directory = Path.GetDirectoryName(outputFilePath);
|
||||
var name = Path.GetFileNameWithoutExtension(outputFilePath);
|
||||
|
||||
var filesToDelete = Directory.EnumerateFiles(directory)
|
||||
var filesToDelete = _fileSystem.GetFilePaths(directory)
|
||||
.Where(f => f.IndexOf(name, StringComparison.OrdinalIgnoreCase) != -1)
|
||||
.ToList();
|
||||
|
||||
|
@ -577,7 +578,7 @@ namespace MediaBrowser.Api
|
|||
{
|
||||
try
|
||||
{
|
||||
Logger.Info("Deleting HLS file {0}", file);
|
||||
Logger.Debug("Deleting HLS file {0}", file);
|
||||
_fileSystem.DeleteFile(file);
|
||||
}
|
||||
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.IO;
|
||||
using System.Linq;
|
||||
using CommonIO;
|
||||
|
||||
namespace MediaBrowser.Api
|
||||
{
|
||||
|
|
|
@ -108,7 +108,6 @@ namespace MediaBrowser.Api.Dlna
|
|||
private readonly IConnectionManager _connectionManager;
|
||||
private readonly IMediaReceiverRegistrar _mediaReceiverRegistrar;
|
||||
|
||||
// TODO: Add utf-8
|
||||
private const string XMLContentType = "text/xml; charset=UTF-8";
|
||||
|
||||
public DlnaServerService(IDlnaManager dlnaManager, IContentDirectory contentDirectory, IConnectionManager connectionManager, IMediaReceiverRegistrar mediaReceiverRegistrar)
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
using MediaBrowser.Common.Net;
|
||||
using MediaBrowser.Common.IO;
|
||||
using MediaBrowser.Common.Net;
|
||||
using MediaBrowser.Controller.Net;
|
||||
using MediaBrowser.Model.IO;
|
||||
using MediaBrowser.Model.Net;
|
||||
|
@ -8,6 +9,7 @@ using System.Collections.Generic;
|
|||
using System.Globalization;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using CommonIO;
|
||||
|
||||
namespace MediaBrowser.Api
|
||||
{
|
||||
|
@ -96,13 +98,14 @@ namespace MediaBrowser.Api
|
|||
/// The _network manager
|
||||
/// </summary>
|
||||
private readonly INetworkManager _networkManager;
|
||||
private IFileSystem _fileSystem;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="EnvironmentService" /> class.
|
||||
/// </summary>
|
||||
/// <param name="networkManager">The network manager.</param>
|
||||
/// <exception cref="System.ArgumentNullException">networkManager</exception>
|
||||
public EnvironmentService(INetworkManager networkManager)
|
||||
public EnvironmentService(INetworkManager networkManager, IFileSystem fileSystem)
|
||||
{
|
||||
if (networkManager == null)
|
||||
{
|
||||
|
@ -110,6 +113,7 @@ namespace MediaBrowser.Api
|
|||
}
|
||||
|
||||
_networkManager = networkManager;
|
||||
_fileSystem = fileSystem;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -117,8 +121,6 @@ namespace MediaBrowser.Api
|
|||
/// </summary>
|
||||
/// <param name="request">The request.</param>
|
||||
/// <returns>System.Object.</returns>
|
||||
/// <exception cref="System.ArgumentNullException">Path</exception>
|
||||
/// <exception cref="System.ArgumentException"></exception>
|
||||
public object Get(GetDirectoryContents request)
|
||||
{
|
||||
var path = request.Path;
|
||||
|
@ -222,8 +224,7 @@ namespace MediaBrowser.Api
|
|||
private IEnumerable<FileSystemEntryInfo> GetFileSystemEntries(GetDirectoryContents request)
|
||||
{
|
||||
// using EnumerateFileSystemInfos doesn't handle reparse points (symlinks)
|
||||
var entries = new DirectoryInfo(request.Path).EnumerateDirectories("*", SearchOption.TopDirectoryOnly)
|
||||
.Concat<FileSystemInfo>(new DirectoryInfo(request.Path).EnumerateFiles("*", SearchOption.TopDirectoryOnly)).Where(i =>
|
||||
var entries = _fileSystem.GetFileSystemEntries(request.Path).Where(i =>
|
||||
{
|
||||
if (!request.IncludeHidden && i.Attributes.HasFlag(FileAttributes.Hidden))
|
||||
{
|
||||
|
|
|
@ -9,6 +9,7 @@ using System;
|
|||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using CommonIO;
|
||||
|
||||
namespace MediaBrowser.Api.Images
|
||||
{
|
||||
|
@ -130,8 +131,7 @@ namespace MediaBrowser.Api.Images
|
|||
{
|
||||
try
|
||||
{
|
||||
return new DirectoryInfo(path)
|
||||
.GetFiles("*", SearchOption.AllDirectories)
|
||||
return _fileSystem.GetFiles(path)
|
||||
.Where(i => BaseItem.SupportedImageExtensions.Contains(i.Extension, StringComparer.Ordinal))
|
||||
.Select(i => new ImageByNameInfo
|
||||
{
|
||||
|
@ -184,7 +184,7 @@ namespace MediaBrowser.Api.Images
|
|||
|
||||
var paths = BaseItem.SupportedImageExtensions.Select(i => Path.Combine(_appPaths.GeneralPath, request.Name, filename + i)).ToList();
|
||||
|
||||
var path = paths.FirstOrDefault(File.Exists) ?? paths.FirstOrDefault();
|
||||
var path = paths.FirstOrDefault(_fileSystem.FileExists) ?? paths.FirstOrDefault();
|
||||
|
||||
return ToStaticFileResult(path);
|
||||
}
|
||||
|
@ -198,11 +198,11 @@ namespace MediaBrowser.Api.Images
|
|||
{
|
||||
var themeFolder = Path.Combine(_appPaths.RatingsPath, request.Theme);
|
||||
|
||||
if (Directory.Exists(themeFolder))
|
||||
if (_fileSystem.DirectoryExists(themeFolder))
|
||||
{
|
||||
var path = BaseItem.SupportedImageExtensions
|
||||
.Select(i => Path.Combine(themeFolder, request.Name + i))
|
||||
.FirstOrDefault(File.Exists);
|
||||
.FirstOrDefault(_fileSystem.FileExists);
|
||||
|
||||
if (!string.IsNullOrEmpty(path))
|
||||
{
|
||||
|
@ -212,14 +212,14 @@ namespace MediaBrowser.Api.Images
|
|||
|
||||
var allFolder = Path.Combine(_appPaths.RatingsPath, "all");
|
||||
|
||||
if (Directory.Exists(allFolder))
|
||||
if (_fileSystem.DirectoryExists(allFolder))
|
||||
{
|
||||
// Avoid implicitly captured closure
|
||||
var currentRequest = request;
|
||||
|
||||
var path = BaseItem.SupportedImageExtensions
|
||||
.Select(i => Path.Combine(allFolder, currentRequest.Name + i))
|
||||
.FirstOrDefault(File.Exists);
|
||||
.FirstOrDefault(_fileSystem.FileExists);
|
||||
|
||||
if (!string.IsNullOrEmpty(path))
|
||||
{
|
||||
|
@ -239,10 +239,10 @@ namespace MediaBrowser.Api.Images
|
|||
{
|
||||
var themeFolder = Path.Combine(_appPaths.MediaInfoImagesPath, request.Theme);
|
||||
|
||||
if (Directory.Exists(themeFolder))
|
||||
if (_fileSystem.DirectoryExists(themeFolder))
|
||||
{
|
||||
var path = BaseItem.SupportedImageExtensions.Select(i => Path.Combine(themeFolder, request.Name + i))
|
||||
.FirstOrDefault(File.Exists);
|
||||
.FirstOrDefault(_fileSystem.FileExists);
|
||||
|
||||
if (!string.IsNullOrEmpty(path))
|
||||
{
|
||||
|
@ -252,13 +252,13 @@ namespace MediaBrowser.Api.Images
|
|||
|
||||
var allFolder = Path.Combine(_appPaths.MediaInfoImagesPath, "all");
|
||||
|
||||
if (Directory.Exists(allFolder))
|
||||
if (_fileSystem.DirectoryExists(allFolder))
|
||||
{
|
||||
// Avoid implicitly captured closure
|
||||
var currentRequest = request;
|
||||
|
||||
var path = BaseItem.SupportedImageExtensions.Select(i => Path.Combine(allFolder, currentRequest.Name + i))
|
||||
.FirstOrDefault(File.Exists);
|
||||
.FirstOrDefault(_fileSystem.FileExists);
|
||||
|
||||
if (!string.IsNullOrEmpty(path))
|
||||
{
|
||||
|
|
|
@ -17,6 +17,7 @@ using System.IO;
|
|||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using CommonIO;
|
||||
using MimeTypes = MediaBrowser.Model.Net.MimeTypes;
|
||||
|
||||
namespace MediaBrowser.Api.Images
|
||||
|
@ -311,17 +312,23 @@ namespace MediaBrowser.Api.Images
|
|||
{
|
||||
try
|
||||
{
|
||||
var fileInfo = new FileInfo(info.Path);
|
||||
|
||||
int? width = null;
|
||||
int? height = null;
|
||||
long length = 0;
|
||||
|
||||
try
|
||||
{
|
||||
var size = _imageProcessor.GetImageSize(info);
|
||||
if (info.IsLocalFile)
|
||||
{
|
||||
var fileInfo = new FileInfo(info.Path);
|
||||
length = fileInfo.Length;
|
||||
|
||||
width = Convert.ToInt32(size.Width);
|
||||
height = Convert.ToInt32(size.Height);
|
||||
var size = _imageProcessor.GetImageSize(info);
|
||||
|
||||
width = Convert.ToInt32(size.Width);
|
||||
height = Convert.ToInt32(size.Height);
|
||||
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
|
@ -333,7 +340,7 @@ namespace MediaBrowser.Api.Images
|
|||
ImageIndex = imageIndex,
|
||||
ImageType = info.Type,
|
||||
ImageTag = _imageProcessor.GetImageCacheTag(item, info),
|
||||
Size = fileInfo.Length,
|
||||
Size = length,
|
||||
Width = width,
|
||||
Height = height
|
||||
};
|
||||
|
@ -557,7 +564,14 @@ namespace MediaBrowser.Api.Images
|
|||
|
||||
}).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 cacheGuid = new Guid(_imageProcessor.GetImageCacheTag(item, imageInfo, supportedImageEnhancers));
|
||||
|
@ -578,6 +592,7 @@ namespace MediaBrowser.Api.Images
|
|||
return GetImageResult(item,
|
||||
request,
|
||||
imageInfo,
|
||||
cropwhitespace,
|
||||
format,
|
||||
supportedImageEnhancers,
|
||||
contentType,
|
||||
|
@ -590,6 +605,7 @@ namespace MediaBrowser.Api.Images
|
|||
private async Task<object> GetImageResult(IHasImages item,
|
||||
ImageRequest request,
|
||||
ItemImageInfo image,
|
||||
bool cropwhitespace,
|
||||
ImageFormat format,
|
||||
List<IImageEnhancer> enhancers,
|
||||
string contentType,
|
||||
|
@ -597,13 +613,6 @@ namespace MediaBrowser.Api.Images
|
|||
IDictionary<string, string> headers,
|
||||
bool isHeadRequest)
|
||||
{
|
||||
var cropwhitespace = request.Type == ImageType.Logo || request.Type == ImageType.Art;
|
||||
|
||||
if (request.CropWhitespace.HasValue)
|
||||
{
|
||||
cropwhitespace = request.CropWhitespace.Value;
|
||||
}
|
||||
|
||||
var options = new ImageProcessingOptions
|
||||
{
|
||||
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))
|
||||
{
|
||||
|
@ -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();
|
||||
|
||||
if (serverFormats.Contains(ImageFormat.Webp) &&
|
||||
GetClientSupportedFormats().Contains(ImageFormat.Webp))
|
||||
// Client doesn't care about format, so start with webp if supported
|
||||
if (serverFormats.Contains(ImageFormat.Webp) && clientSupportedFormats.Contains(ImageFormat.Webp))
|
||||
{
|
||||
return ImageFormat.Webp;
|
||||
}
|
||||
|
@ -661,10 +697,7 @@ namespace MediaBrowser.Api.Images
|
|||
return ImageFormat.Png;
|
||||
}
|
||||
|
||||
var extension = Path.GetExtension(image.Path);
|
||||
|
||||
if (string.Equals(extension, ".jpg", StringComparison.OrdinalIgnoreCase) ||
|
||||
string.Equals(extension, ".jpeg", StringComparison.OrdinalIgnoreCase))
|
||||
if (inputFormat.HasValue && inputFormat.Value == ImageFormat.Jpg)
|
||||
{
|
||||
return ImageFormat.Jpg;
|
||||
}
|
||||
|
@ -675,7 +708,7 @@ namespace MediaBrowser.Api.Images
|
|||
|
||||
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;
|
||||
|
||||
|
|
|
@ -16,6 +16,7 @@ using System.IO;
|
|||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using CommonIO;
|
||||
|
||||
namespace MediaBrowser.Api.Images
|
||||
{
|
||||
|
@ -237,7 +238,7 @@ namespace MediaBrowser.Api.Images
|
|||
contentPath = await reader.ReadToEndAsync().ConfigureAwait(false);
|
||||
}
|
||||
|
||||
if (File.Exists(contentPath))
|
||||
if (_fileSystem.FileExists(contentPath))
|
||||
{
|
||||
return ToStaticFileResult(contentPath);
|
||||
}
|
||||
|
@ -281,7 +282,7 @@ namespace MediaBrowser.Api.Images
|
|||
|
||||
var fullCachePath = GetFullCachePath(urlHash + "." + ext);
|
||||
|
||||
Directory.CreateDirectory(Path.GetDirectoryName(fullCachePath));
|
||||
_fileSystem.CreateDirectory(Path.GetDirectoryName(fullCachePath));
|
||||
using (var stream = result.Content)
|
||||
{
|
||||
using (var filestream = _fileSystem.GetFileStream(fullCachePath, FileMode.Create, FileAccess.Write, FileShare.Read, true))
|
||||
|
@ -290,7 +291,7 @@ namespace MediaBrowser.Api.Images
|
|||
}
|
||||
}
|
||||
|
||||
Directory.CreateDirectory(Path.GetDirectoryName(pointerCachePath));
|
||||
_fileSystem.CreateDirectory(Path.GetDirectoryName(pointerCachePath));
|
||||
using (var writer = new StreamWriter(pointerCachePath))
|
||||
{
|
||||
await writer.WriteAsync(fullCachePath).ConfigureAwait(false);
|
||||
|
|
|
@ -16,6 +16,7 @@ using System.IO;
|
|||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using CommonIO;
|
||||
|
||||
namespace MediaBrowser.Api
|
||||
{
|
||||
|
@ -200,7 +201,7 @@ namespace MediaBrowser.Api
|
|||
//}
|
||||
item.ProviderIds = request.ProviderIds;
|
||||
|
||||
var task = _providerManager.RefreshFullItem(item, new MetadataRefreshOptions
|
||||
var task = _providerManager.RefreshFullItem(item, new MetadataRefreshOptions(_fileSystem)
|
||||
{
|
||||
MetadataRefreshMode = MetadataRefreshMode.FullRefresh,
|
||||
ImageRefreshMode = ImageRefreshMode.FullRefresh,
|
||||
|
@ -230,7 +231,7 @@ namespace MediaBrowser.Api
|
|||
contentPath = await reader.ReadToEndAsync().ConfigureAwait(false);
|
||||
}
|
||||
|
||||
if (File.Exists(contentPath))
|
||||
if (_fileSystem.FileExists(contentPath))
|
||||
{
|
||||
return ToStaticFileResult(contentPath);
|
||||
}
|
||||
|
@ -271,7 +272,7 @@ namespace MediaBrowser.Api
|
|||
|
||||
var fullCachePath = GetFullCachePath(urlHash + "." + ext);
|
||||
|
||||
Directory.CreateDirectory(Path.GetDirectoryName(fullCachePath));
|
||||
_fileSystem.CreateDirectory(Path.GetDirectoryName(fullCachePath));
|
||||
using (var stream = result.Content)
|
||||
{
|
||||
using (var filestream = _fileSystem.GetFileStream(fullCachePath, FileMode.Create, FileAccess.Write, FileShare.Read, true))
|
||||
|
@ -280,7 +281,7 @@ namespace MediaBrowser.Api
|
|||
}
|
||||
}
|
||||
|
||||
Directory.CreateDirectory(Path.GetDirectoryName(pointerCachePath));
|
||||
_fileSystem.CreateDirectory(Path.GetDirectoryName(pointerCachePath));
|
||||
using (var writer = new StreamWriter(pointerCachePath))
|
||||
{
|
||||
await writer.WriteAsync(fullCachePath).ConfigureAwait(false);
|
||||
|
|
|
@ -4,6 +4,8 @@ using MediaBrowser.Controller.Net;
|
|||
using MediaBrowser.Controller.Providers;
|
||||
using ServiceStack;
|
||||
using System.Threading;
|
||||
using CommonIO;
|
||||
using MediaBrowser.Common.IO;
|
||||
|
||||
namespace MediaBrowser.Api
|
||||
{
|
||||
|
@ -37,11 +39,13 @@ namespace MediaBrowser.Api
|
|||
{
|
||||
private readonly ILibraryManager _libraryManager;
|
||||
private readonly IProviderManager _providerManager;
|
||||
private readonly IFileSystem _fileSystem;
|
||||
|
||||
public ItemRefreshService(ILibraryManager libraryManager, IProviderManager providerManager)
|
||||
public ItemRefreshService(ILibraryManager libraryManager, IProviderManager providerManager, IFileSystem fileSystem)
|
||||
{
|
||||
_libraryManager = libraryManager;
|
||||
_providerManager = providerManager;
|
||||
_fileSystem = fileSystem;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -66,7 +70,7 @@ namespace MediaBrowser.Api
|
|||
|
||||
private MetadataRefreshOptions GetRefreshOptions(BaseRefreshRequest request)
|
||||
{
|
||||
return new MetadataRefreshOptions(new DirectoryService())
|
||||
return new MetadataRefreshOptions(new DirectoryService(_fileSystem))
|
||||
{
|
||||
MetadataRefreshMode = request.MetadataRefreshMode,
|
||||
ImageRefreshMode = request.ImageRefreshMode,
|
||||
|
|
|
@ -211,11 +211,6 @@ namespace MediaBrowser.Api
|
|||
|
||||
UpdateItem(request, item);
|
||||
|
||||
if (isLockedChanged && item.IsLocked)
|
||||
{
|
||||
item.IsUnidentified = false;
|
||||
}
|
||||
|
||||
await item.UpdateToRepository(ItemUpdateType.MetadataEdit, CancellationToken.None).ConfigureAwait(false);
|
||||
|
||||
if (request.People != null)
|
||||
|
@ -321,13 +316,8 @@ namespace MediaBrowser.Api
|
|||
|
||||
SetProductionLocations(item, request);
|
||||
|
||||
var hasLang = item as IHasPreferredMetadataLanguage;
|
||||
|
||||
if (hasLang != null)
|
||||
{
|
||||
hasLang.PreferredMetadataCountryCode = request.PreferredMetadataCountryCode;
|
||||
hasLang.PreferredMetadataLanguage = request.PreferredMetadataLanguage;
|
||||
}
|
||||
item.PreferredMetadataCountryCode = request.PreferredMetadataCountryCode;
|
||||
item.PreferredMetadataLanguage = request.PreferredMetadataLanguage;
|
||||
|
||||
var hasDisplayOrder = item as IHasDisplayOrder;
|
||||
if (hasDisplayOrder != null)
|
||||
|
|
|
@ -3,6 +3,7 @@ using MediaBrowser.Controller;
|
|||
using System;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using CommonIO;
|
||||
|
||||
namespace MediaBrowser.Api.Library
|
||||
{
|
||||
|
@ -33,11 +34,11 @@ namespace MediaBrowser.Api.Library
|
|||
var rootFolderPath = appPaths.DefaultUserViewsPath;
|
||||
var path = Path.Combine(rootFolderPath, virtualFolderName);
|
||||
|
||||
if (!Directory.Exists(path))
|
||||
if (!fileSystem.DirectoryExists(path))
|
||||
{
|
||||
throw new DirectoryNotFoundException(string.Format("The media collection {0} does not exist", virtualFolderName));
|
||||
}
|
||||
|
||||
|
||||
var shortcut = Directory.EnumerateFiles(path, ShortcutFileSearch, SearchOption.AllDirectories).FirstOrDefault(f => fileSystem.ResolveShortcut(f).Equals(mediaPath, StringComparison.OrdinalIgnoreCase));
|
||||
|
||||
if (!string.IsNullOrEmpty(shortcut))
|
||||
|
@ -53,11 +54,9 @@ namespace MediaBrowser.Api.Library
|
|||
/// <param name="virtualFolderName">Name of the virtual folder.</param>
|
||||
/// <param name="path">The path.</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)
|
||||
{
|
||||
if (!Directory.Exists(path))
|
||||
if (!fileSystem.DirectoryExists(path))
|
||||
{
|
||||
throw new DirectoryNotFoundException("The path does not exist.");
|
||||
}
|
||||
|
@ -69,7 +68,7 @@ namespace MediaBrowser.Api.Library
|
|||
|
||||
var lnk = Path.Combine(virtualFolderPath, shortcutFilename + ShortcutFileExtension);
|
||||
|
||||
while (File.Exists(lnk))
|
||||
while (fileSystem.FileExists(lnk))
|
||||
{
|
||||
shortcutFilename += "1";
|
||||
lnk = Path.Combine(virtualFolderPath, shortcutFilename + ShortcutFileExtension);
|
||||
|
|
|
@ -27,6 +27,8 @@ using System.IO;
|
|||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using CommonIO;
|
||||
using MediaBrowser.Common.IO;
|
||||
|
||||
namespace MediaBrowser.Api.Library
|
||||
{
|
||||
|
@ -282,12 +284,13 @@ namespace MediaBrowser.Api.Library
|
|||
private readonly IChannelManager _channelManager;
|
||||
private readonly ITVSeriesManager _tvManager;
|
||||
private readonly ILibraryMonitor _libraryMonitor;
|
||||
private readonly IFileSystem _fileSystem;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="LibraryService" /> class.
|
||||
/// </summary>
|
||||
public LibraryService(IItemRepository itemRepo, ILibraryManager libraryManager, IUserManager userManager,
|
||||
IDtoService dtoService, IUserDataManager userDataManager, IAuthorizationContext authContext, IActivityManager activityManager, ILocalizationManager localization, ILiveTvManager liveTv, IChannelManager channelManager, ITVSeriesManager tvManager, ILibraryMonitor libraryMonitor)
|
||||
IDtoService dtoService, IUserDataManager userDataManager, IAuthorizationContext authContext, IActivityManager activityManager, ILocalizationManager localization, ILiveTvManager liveTv, IChannelManager channelManager, ITVSeriesManager tvManager, ILibraryMonitor libraryMonitor, IFileSystem fileSystem)
|
||||
{
|
||||
_itemRepo = itemRepo;
|
||||
_libraryManager = libraryManager;
|
||||
|
@ -301,6 +304,7 @@ namespace MediaBrowser.Api.Library
|
|||
_channelManager = channelManager;
|
||||
_tvManager = tvManager;
|
||||
_libraryMonitor = libraryMonitor;
|
||||
_fileSystem = fileSystem;
|
||||
}
|
||||
|
||||
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.");
|
||||
}
|
||||
if (Directory.Exists(item.Path))
|
||||
if (_fileSystem.DirectoryExists(item.Path))
|
||||
{
|
||||
throw new ArgumentException("This command cannot be used for directories.");
|
||||
}
|
||||
|
@ -719,7 +723,7 @@ namespace MediaBrowser.Api.Library
|
|||
|
||||
if (!item.CanDelete(user))
|
||||
{
|
||||
throw new UnauthorizedAccessException();
|
||||
throw new SecurityException("Unauthorized access");
|
||||
}
|
||||
|
||||
if (item is ILiveTvRecording)
|
||||
|
|
|
@ -10,6 +10,7 @@ using System.IO;
|
|||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using CommonIO;
|
||||
|
||||
namespace MediaBrowser.Api.Library
|
||||
{
|
||||
|
@ -46,6 +47,12 @@ namespace MediaBrowser.Api.Library
|
|||
/// </summary>
|
||||
/// <value><c>true</c> if [refresh library]; otherwise, <c>false</c>.</value>
|
||||
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")]
|
||||
|
@ -195,22 +202,42 @@ namespace MediaBrowser.Api.Library
|
|||
|
||||
var virtualFolderPath = Path.Combine(rootFolderPath, name);
|
||||
|
||||
if (Directory.Exists(virtualFolderPath))
|
||||
if (_fileSystem.DirectoryExists(virtualFolderPath))
|
||||
{
|
||||
throw new ArgumentException("There is already a media collection with the name " + name + ".");
|
||||
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();
|
||||
|
||||
try
|
||||
{
|
||||
Directory.CreateDirectory(virtualFolderPath);
|
||||
_fileSystem.CreateDirectory(virtualFolderPath);
|
||||
|
||||
if (!string.IsNullOrEmpty(request.CollectionType))
|
||||
{
|
||||
var path = Path.Combine(virtualFolderPath, request.CollectionType + ".collection");
|
||||
|
||||
File.Create(path);
|
||||
using (File.Create(path))
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
if (request.Paths != null)
|
||||
{
|
||||
foreach (var path in request.Paths)
|
||||
{
|
||||
LibraryHelpers.AddMediaPath(_fileSystem, request.Name, path, _appPaths);
|
||||
}
|
||||
}
|
||||
}
|
||||
finally
|
||||
|
@ -256,12 +283,12 @@ namespace MediaBrowser.Api.Library
|
|||
var currentPath = Path.Combine(rootFolderPath, request.Name);
|
||||
var newPath = Path.Combine(rootFolderPath, request.NewName);
|
||||
|
||||
if (!Directory.Exists(currentPath))
|
||||
if (!_fileSystem.DirectoryExists(currentPath))
|
||||
{
|
||||
throw new DirectoryNotFoundException("The media collection does not exist");
|
||||
}
|
||||
|
||||
if (!string.Equals(currentPath, newPath, StringComparison.OrdinalIgnoreCase) && Directory.Exists(newPath))
|
||||
if (!string.Equals(currentPath, newPath, StringComparison.OrdinalIgnoreCase) && _fileSystem.DirectoryExists(newPath))
|
||||
{
|
||||
throw new ArgumentException("There is already a media collection with the name " + newPath + ".");
|
||||
}
|
||||
|
@ -276,11 +303,11 @@ namespace MediaBrowser.Api.Library
|
|||
//Create an unique name
|
||||
var temporaryName = Guid.NewGuid().ToString();
|
||||
var temporaryPath = Path.Combine(rootFolderPath, temporaryName);
|
||||
Directory.Move(currentPath, temporaryPath);
|
||||
_fileSystem.MoveDirectory(currentPath, temporaryPath);
|
||||
currentPath = temporaryPath;
|
||||
}
|
||||
|
||||
Directory.Move(currentPath, newPath);
|
||||
_fileSystem.MoveDirectory(currentPath, newPath);
|
||||
}
|
||||
finally
|
||||
{
|
||||
|
@ -319,7 +346,7 @@ namespace MediaBrowser.Api.Library
|
|||
|
||||
var path = Path.Combine(rootFolderPath, request.Name);
|
||||
|
||||
if (!Directory.Exists(path))
|
||||
if (!_fileSystem.DirectoryExists(path))
|
||||
{
|
||||
throw new DirectoryNotFoundException("The media folder does not exist");
|
||||
}
|
||||
|
|
|
@ -521,12 +521,12 @@ namespace MediaBrowser.Api.LiveTv
|
|||
|
||||
if (user == null)
|
||||
{
|
||||
throw new UnauthorizedAccessException("Anonymous live tv management is not allowed.");
|
||||
throw new SecurityException("Anonymous live tv management is not allowed.");
|
||||
}
|
||||
|
||||
if (!user.Policy.EnableLiveTvManagement)
|
||||
{
|
||||
throw new UnauthorizedAccessException("The current user does not have permission to manage live tv.");
|
||||
throw new SecurityException("The current user does not have permission to manage live tv.");
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -11,10 +11,9 @@
|
|||
<AssemblyName>MediaBrowser.Api</AssemblyName>
|
||||
<FileAlignment>512</FileAlignment>
|
||||
<SolutionDir Condition="$(SolutionDir) == '' Or $(SolutionDir) == '*Undefined*'">..\</SolutionDir>
|
||||
<ProductVersion>10.0.0</ProductVersion>
|
||||
<SchemaVersion>2.0</SchemaVersion>
|
||||
<TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
|
||||
<RestorePackages>true</RestorePackages>
|
||||
<ReleaseVersion>
|
||||
</ReleaseVersion>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
|
@ -25,7 +24,6 @@
|
|||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
<PlatformTarget>AnyCPU</PlatformTarget>
|
||||
<TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
|
||||
<DebugType>none</DebugType>
|
||||
|
@ -34,7 +32,6 @@
|
|||
<DefineConstants>TRACE</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
<TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release Mono|AnyCPU' ">
|
||||
<DebugType>none</DebugType>
|
||||
|
@ -43,15 +40,17 @@
|
|||
<DefineConstants>TRACE</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
<TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup>
|
||||
<RunPostBuildEvent>Always</RunPostBuildEvent>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="MoreLinq, Version=1.1.17511.0, Culture=neutral, PublicKeyToken=384d532d7e88985d, processorArchitecture=MSIL">
|
||||
<Reference Include="CommonIO, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL">
|
||||
<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 Include="System" />
|
||||
<Reference Include="System.Core" />
|
||||
|
@ -64,12 +63,14 @@
|
|||
<Reference Include="ServiceStack.Text">
|
||||
<HintPath>..\ThirdParty\ServiceStack.Text\ServiceStack.Text.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="MoreLinq">
|
||||
<HintPath>..\packages\morelinq.1.1.1\lib\net35\MoreLinq.dll</HintPath>
|
||||
</Reference>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="..\SharedVersion.cs">
|
||||
<Link>Properties\SharedVersion.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="AppThemeService.cs" />
|
||||
<Compile Include="BrandingService.cs" />
|
||||
<Compile Include="ChannelService.cs" />
|
||||
<Compile Include="ConnectService.cs" />
|
||||
|
|
|
@ -23,6 +23,7 @@ using System.Linq;
|
|||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using CommonIO;
|
||||
|
||||
namespace MediaBrowser.Api.Playback
|
||||
{
|
||||
|
@ -290,13 +291,6 @@ namespace MediaBrowser.Api.Playback
|
|||
{
|
||||
get
|
||||
{
|
||||
var lib = ApiEntryPoint.Instance.GetEncodingOptions().H264Encoder;
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(lib))
|
||||
{
|
||||
return lib;
|
||||
}
|
||||
|
||||
return "libx264";
|
||||
}
|
||||
}
|
||||
|
@ -809,6 +803,46 @@ namespace MediaBrowser.Api.Playback
|
|||
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>
|
||||
/// Gets the input argument.
|
||||
/// </summary>
|
||||
|
@ -816,7 +850,7 @@ namespace MediaBrowser.Api.Playback
|
|||
/// <returns>System.String.</returns>
|
||||
protected string GetInputArgument(StreamState state)
|
||||
{
|
||||
var arg = "-i " + GetInputPathArgument(state);
|
||||
var arg = string.Format("-i {0}", GetInputPathArgument(state));
|
||||
|
||||
if (state.SubtitleStream != null)
|
||||
{
|
||||
|
@ -826,7 +860,7 @@ namespace MediaBrowser.Api.Playback
|
|||
}
|
||||
}
|
||||
|
||||
return arg;
|
||||
return arg.Trim();
|
||||
}
|
||||
|
||||
private string GetInputPathArgument(StreamState state)
|
||||
|
@ -840,7 +874,7 @@ namespace MediaBrowser.Api.Playback
|
|||
{
|
||||
if (!(state.VideoType == VideoType.Iso && state.IsoMount == null))
|
||||
{
|
||||
inputPath = MediaEncoderHelpers.GetInputArgument(mediaPath, state.InputProtocol, state.IsoMount, state.PlayableStreamFileNames);
|
||||
inputPath = MediaEncoderHelpers.GetInputArgument(FileSystem, mediaPath, state.InputProtocol, state.IsoMount, state.PlayableStreamFileNames);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -889,7 +923,7 @@ namespace MediaBrowser.Api.Playback
|
|||
CancellationTokenSource cancellationTokenSource,
|
||||
string workingDirectory = null)
|
||||
{
|
||||
Directory.CreateDirectory(Path.GetDirectoryName(outputPath));
|
||||
FileSystem.CreateDirectory(Path.GetDirectoryName(outputPath));
|
||||
|
||||
await AcquireResources(state, cancellationTokenSource).ConfigureAwait(false);
|
||||
|
||||
|
@ -942,7 +976,7 @@ namespace MediaBrowser.Api.Playback
|
|||
Logger.Info(commandLineLogMessage);
|
||||
|
||||
var logFilePath = Path.Combine(ServerConfigurationManager.ApplicationPaths.LogDirectoryPath, "transcode-" + Guid.NewGuid() + ".txt");
|
||||
Directory.CreateDirectory(Path.GetDirectoryName(logFilePath));
|
||||
FileSystem.CreateDirectory(Path.GetDirectoryName(logFilePath));
|
||||
|
||||
// FFMpeg writes debug/error info to stderr. This is useful when debugging so let's put it in the log directory.
|
||||
state.LogFileStream = FileSystem.GetFileStream(logFilePath, FileMode.Create, FileAccess.Write, FileShare.Read, true);
|
||||
|
@ -972,7 +1006,7 @@ namespace MediaBrowser.Api.Playback
|
|||
StartStreamingLog(transcodingJob, state, process.StandardError.BaseStream, state.LogFileStream);
|
||||
|
||||
// Wait for the file to exist before proceeeding
|
||||
while (!File.Exists(state.WaitForPath ?? outputPath) && !transcodingJob.HasExited)
|
||||
while (!FileSystem.FileExists(state.WaitForPath ?? outputPath) && !transcodingJob.HasExited)
|
||||
{
|
||||
await Task.Delay(100, cancellationTokenSource.Token).ConfigureAwait(false);
|
||||
}
|
||||
|
@ -1027,6 +1061,7 @@ namespace MediaBrowser.Api.Playback
|
|||
var bytes = Encoding.UTF8.GetBytes(Environment.NewLine + line);
|
||||
|
||||
await target.WriteAsync(bytes, 0, bytes.Length).ConfigureAwait(false);
|
||||
await target.FlushAsync().ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1151,15 +1186,8 @@ namespace MediaBrowser.Api.Playback
|
|||
|
||||
if (bitrate.HasValue)
|
||||
{
|
||||
var hasFixedResolution = state.VideoRequest.HasFixedResolution;
|
||||
|
||||
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
|
||||
// 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));
|
||||
|
@ -1170,36 +1198,15 @@ namespace MediaBrowser.Api.Playback
|
|||
return string.Format(" -b:v {0}", bitrate.Value.ToString(UsCulture));
|
||||
}
|
||||
|
||||
// h264_qsv
|
||||
if (string.Equals(videoCodec, "h264_qsv", StringComparison.OrdinalIgnoreCase) || string.Equals(videoCodec, "libnvenc", StringComparison.OrdinalIgnoreCase))
|
||||
// h264
|
||||
if (isHls)
|
||||
{
|
||||
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(" -b:v {0} -maxrate ({0}*1.2) -bufsize ({0}*2)", bitrate.Value.ToString(UsCulture));
|
||||
return string.Format(" -b:v {0} -maxrate {0} -bufsize {1}",
|
||||
bitrate.Value.ToString(UsCulture),
|
||||
(bitrate.Value * 2).ToString(UsCulture));
|
||||
}
|
||||
|
||||
// H264
|
||||
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.Format(" -b:v {0}", bitrate.Value.ToString(UsCulture));
|
||||
}
|
||||
|
||||
return string.Empty;
|
||||
|
@ -1986,7 +1993,8 @@ namespace MediaBrowser.Api.Playback
|
|||
state.IsTargetCabac,
|
||||
state.TargetRefFrames,
|
||||
state.TargetVideoStreamCount,
|
||||
state.TargetAudioStreamCount);
|
||||
state.TargetAudioStreamCount,
|
||||
state.TargetVideoCodecTag);
|
||||
|
||||
if (mediaProfile != null)
|
||||
{
|
||||
|
@ -2083,7 +2091,8 @@ namespace MediaBrowser.Api.Playback
|
|||
state.IsTargetCabac,
|
||||
state.TargetRefFrames,
|
||||
state.TargetVideoStreamCount,
|
||||
state.TargetAudioStreamCount
|
||||
state.TargetAudioStreamCount,
|
||||
state.TargetVideoCodecTag
|
||||
|
||||
).FirstOrDefault() ?? string.Empty;
|
||||
}
|
||||
|
@ -2158,6 +2167,12 @@ namespace MediaBrowser.Api.Playback
|
|||
inputModifier += " -re";
|
||||
}
|
||||
|
||||
var videoDecoder = GetVideoDecoder(state);
|
||||
if (!string.IsNullOrWhiteSpace(videoDecoder))
|
||||
{
|
||||
inputModifier += " " + videoDecoder;
|
||||
}
|
||||
|
||||
return inputModifier;
|
||||
}
|
||||
|
||||
|
|
|
@ -17,6 +17,7 @@ using System.IO;
|
|||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using CommonIO;
|
||||
using MimeTypes = MediaBrowser.Model.Net.MimeTypes;
|
||||
|
||||
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));
|
||||
state.WaitForPath = Path.Combine(workingDirectory, Path.GetFileName(playlistPath));
|
||||
Directory.CreateDirectory(workingDirectory);
|
||||
FileSystem.CreateDirectory(workingDirectory);
|
||||
job = await StartFfMpeg(state, playlistPath, cancellationTokenSource, workingDirectory).ConfigureAwait(false);
|
||||
await WaitForMinimumDashSegmentCount(Path.Combine(workingDirectory, Path.GetFileName(playlistPath)), 1, cancellationTokenSource.Token).ConfigureAwait(false);
|
||||
}
|
||||
|
@ -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);
|
||||
|
||||
try
|
||||
{
|
||||
return new DirectoryInfo(folder)
|
||||
.EnumerateFiles("*", SearchOption.AllDirectories)
|
||||
return fileSystem.GetFiles(folder)
|
||||
.Where(i => string.Equals(i.Extension, segmentExtension, StringComparison.OrdinalIgnoreCase))
|
||||
.OrderByDescending(fileSystem.GetLastWriteTimeUtc)
|
||||
.Take(count)
|
||||
|
@ -337,7 +337,7 @@ namespace MediaBrowser.Api.Playback.Dash
|
|||
}
|
||||
catch (DirectoryNotFoundException)
|
||||
{
|
||||
return new List<FileInfo>();
|
||||
return new List<FileSystemMetadata>();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -348,20 +348,20 @@ namespace MediaBrowser.Api.Playback.Dash
|
|||
if (requestedIndex == -1)
|
||||
{
|
||||
var path = Path.Combine(folder, "0", "stream" + representationId + "-" + "init" + segmentExtension);
|
||||
return File.Exists(path) ? path : null;
|
||||
return FileSystem.FileExists(path) ? path : null;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
foreach (var subfolder in new DirectoryInfo(folder).EnumerateDirectories().ToList())
|
||||
foreach (var subfolder in FileSystem.GetDirectoryPaths(folder).ToList())
|
||||
{
|
||||
var subfolderName = Path.GetFileNameWithoutExtension(subfolder.FullName);
|
||||
var subfolderName = Path.GetFileNameWithoutExtension(subfolder);
|
||||
int startNumber;
|
||||
if (int.TryParse(subfolderName, NumberStyles.Any, UsCulture, out startNumber))
|
||||
{
|
||||
var segmentIndex = requestedIndex - startNumber + 1;
|
||||
var path = Path.Combine(folder, subfolderName, "stream" + representationId + "-" + segmentIndex.ToString("00000", CultureInfo.InvariantCulture) + segmentExtension);
|
||||
if (File.Exists(path))
|
||||
if (FileSystem.FileExists(path))
|
||||
{
|
||||
return path;
|
||||
}
|
||||
|
|
|
@ -15,6 +15,7 @@ using System.IO;
|
|||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using CommonIO;
|
||||
|
||||
namespace MediaBrowser.Api.Playback.Hls
|
||||
{
|
||||
|
@ -23,7 +24,8 @@ namespace MediaBrowser.Api.Playback.Hls
|
|||
/// </summary>
|
||||
public abstract class BaseHlsService : BaseStreamingService
|
||||
{
|
||||
protected BaseHlsService(IServerConfigurationManager serverConfig, IUserManager userManager, ILibraryManager libraryManager, IIsoManager isoManager, IMediaEncoder mediaEncoder, IFileSystem fileSystem, IDlnaManager dlnaManager, ISubtitleEncoder subtitleEncoder, IDeviceManager deviceManager, IMediaSourceManager mediaSourceManager, IZipClient zipClient, IJsonSerializer jsonSerializer) : base(serverConfig, userManager, libraryManager, isoManager, mediaEncoder, fileSystem, dlnaManager, subtitleEncoder, deviceManager, mediaSourceManager, zipClient, jsonSerializer)
|
||||
protected BaseHlsService(IServerConfigurationManager serverConfig, IUserManager userManager, ILibraryManager libraryManager, IIsoManager isoManager, IMediaEncoder mediaEncoder, IFileSystem fileSystem, IDlnaManager dlnaManager, ISubtitleEncoder subtitleEncoder, IDeviceManager deviceManager, IMediaSourceManager mediaSourceManager, IZipClient zipClient, IJsonSerializer jsonSerializer)
|
||||
: base(serverConfig, userManager, libraryManager, isoManager, mediaEncoder, fileSystem, dlnaManager, subtitleEncoder, deviceManager, mediaSourceManager, zipClient, jsonSerializer)
|
||||
{
|
||||
}
|
||||
|
||||
|
@ -90,12 +92,12 @@ namespace MediaBrowser.Api.Playback.Hls
|
|||
TranscodingJob job = null;
|
||||
var playlist = state.OutputFilePath;
|
||||
|
||||
if (!File.Exists(playlist))
|
||||
if (!FileSystem.FileExists(playlist))
|
||||
{
|
||||
await ApiEntryPoint.Instance.TranscodingStartLock.WaitAsync(cancellationTokenSource.Token).ConfigureAwait(false);
|
||||
try
|
||||
{
|
||||
if (!File.Exists(playlist))
|
||||
if (!FileSystem.FileExists(playlist))
|
||||
{
|
||||
// If the playlist doesn't already exist, startup ffmpeg
|
||||
try
|
||||
|
@ -150,7 +152,7 @@ namespace MediaBrowser.Api.Playback.Hls
|
|||
{
|
||||
ApiEntryPoint.Instance.OnTranscodeEndRequest(job);
|
||||
}
|
||||
|
||||
|
||||
return ResultFactory.GetResult(playlistText, MimeTypes.GetMimeType("playlist.m3u8"), new Dictionary<string, string>());
|
||||
}
|
||||
|
||||
|
@ -317,4 +319,4 @@ namespace MediaBrowser.Api.Playback.Hls
|
|||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -20,6 +20,7 @@ using System.Linq;
|
|||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using CommonIO;
|
||||
using MimeTypes = MediaBrowser.Model.Net.MimeTypes;
|
||||
|
||||
namespace MediaBrowser.Api.Playback.Hls
|
||||
|
@ -165,7 +166,7 @@ namespace MediaBrowser.Api.Playback.Hls
|
|||
|
||||
TranscodingJob job = null;
|
||||
|
||||
if (File.Exists(segmentPath))
|
||||
if (FileSystem.FileExists(segmentPath))
|
||||
{
|
||||
job = ApiEntryPoint.Instance.OnTranscodeBeginRequest(playlistPath, TranscodingJobType);
|
||||
return await GetSegmentResult(state, playlistPath, segmentPath, requestedIndex, job, cancellationToken).ConfigureAwait(false);
|
||||
|
@ -174,7 +175,7 @@ namespace MediaBrowser.Api.Playback.Hls
|
|||
await ApiEntryPoint.Instance.TranscodingStartLock.WaitAsync(cancellationTokenSource.Token).ConfigureAwait(false);
|
||||
try
|
||||
{
|
||||
if (File.Exists(segmentPath))
|
||||
if (FileSystem.FileExists(segmentPath))
|
||||
{
|
||||
job = ApiEntryPoint.Instance.OnTranscodeBeginRequest(playlistPath, TranscodingJobType);
|
||||
return await GetSegmentResult(state, playlistPath, segmentPath, requestedIndex, job, cancellationToken).ConfigureAwait(false);
|
||||
|
@ -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)
|
||||
{
|
||||
|
@ -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);
|
||||
|
||||
|
@ -386,8 +387,7 @@ namespace MediaBrowser.Api.Playback.Hls
|
|||
|
||||
try
|
||||
{
|
||||
return new DirectoryInfo(folder)
|
||||
.EnumerateFiles("*", SearchOption.TopDirectoryOnly)
|
||||
return fileSystem.GetFiles(folder)
|
||||
.Where(i => string.Equals(i.Extension, segmentExtension, StringComparison.OrdinalIgnoreCase) && Path.GetFileNameWithoutExtension(i.Name).StartsWith(filePrefix, StringComparison.OrdinalIgnoreCase))
|
||||
.OrderByDescending(fileSystem.GetLastWriteTimeUtc)
|
||||
.FirstOrDefault();
|
||||
|
@ -432,7 +432,7 @@ namespace MediaBrowser.Api.Playback.Hls
|
|||
CancellationToken cancellationToken)
|
||||
{
|
||||
// If all transcoding has completed, just return immediately
|
||||
if (transcodingJob != null && transcodingJob.HasExited && File.Exists(segmentPath))
|
||||
if (transcodingJob != null && transcodingJob.HasExited && FileSystem.FileExists(segmentPath))
|
||||
{
|
||||
return GetSegmentResult(state, segmentPath, segmentIndex, transcodingJob);
|
||||
}
|
||||
|
@ -452,7 +452,7 @@ namespace MediaBrowser.Api.Playback.Hls
|
|||
// If it appears in the playlist, it's done
|
||||
if (text.IndexOf(segmentFilename, StringComparison.OrdinalIgnoreCase) != -1)
|
||||
{
|
||||
if (File.Exists(segmentPath))
|
||||
if (FileSystem.FileExists(segmentPath))
|
||||
{
|
||||
return GetSegmentResult(state, segmentPath, segmentIndex, transcodingJob);
|
||||
}
|
||||
|
@ -989,4 +989,4 @@ namespace MediaBrowser.Api.Playback.Hls
|
|||
return base.CanStreamCopyVideo(request, videoStream);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -8,6 +8,7 @@ using MediaBrowser.Model.IO;
|
|||
using MediaBrowser.Model.Serialization;
|
||||
using ServiceStack;
|
||||
using System;
|
||||
using CommonIO;
|
||||
|
||||
namespace MediaBrowser.Api.Playback.Hls
|
||||
{
|
||||
|
|
|
@ -57,7 +57,7 @@ namespace MediaBrowser.Api.Playback
|
|||
Size = 102400;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
[Authenticated]
|
||||
public class MediaInfoService : BaseApiService
|
||||
{
|
||||
|
@ -289,7 +289,7 @@ namespace MediaBrowser.Api.Playback
|
|||
if (mediaSource.SupportsDirectStream)
|
||||
{
|
||||
options.MaxBitrate = GetMaxBitrate(maxBitrate);
|
||||
|
||||
|
||||
// The MediaSource supports direct stream, now test to see if the client supports it
|
||||
var streamInfo = string.Equals(item.MediaType, MediaType.Audio, StringComparison.OrdinalIgnoreCase) ?
|
||||
streamBuilder.BuildAudioItem(options) :
|
||||
|
@ -309,7 +309,7 @@ namespace MediaBrowser.Api.Playback
|
|||
if (mediaSource.SupportsTranscoding)
|
||||
{
|
||||
options.MaxBitrate = GetMaxBitrate(maxBitrate);
|
||||
|
||||
|
||||
// The MediaSource supports direct stream, now test to see if the client supports it
|
||||
var streamInfo = string.Equals(item.MediaType, MediaType.Audio, StringComparison.OrdinalIgnoreCase) ?
|
||||
streamBuilder.BuildAudioItem(options) :
|
||||
|
@ -336,9 +336,15 @@ namespace MediaBrowser.Api.Playback
|
|||
var maxBitrate = clientMaxBitrate;
|
||||
var remoteClientMaxBitrate = _config.Configuration.RemoteClientBitrateLimit;
|
||||
|
||||
if (remoteClientMaxBitrate > 0 && !_networkManager.IsInLocalNetwork(Request.RemoteIp))
|
||||
if (remoteClientMaxBitrate > 0)
|
||||
{
|
||||
maxBitrate = Math.Min(maxBitrate ?? remoteClientMaxBitrate, remoteClientMaxBitrate);
|
||||
var isInLocalNetwork = _networkManager.IsInLocalNetwork(Request.RemoteIp);
|
||||
|
||||
Logger.Info("RemoteClientBitrateLimit: {0}, RemoteIp: {1}, IsInLocalNetwork: {2}", remoteClientMaxBitrate, Request.RemoteIp, isInLocalNetwork);
|
||||
if (!isInLocalNetwork)
|
||||
{
|
||||
maxBitrate = Math.Min(maxBitrate ?? remoteClientMaxBitrate, remoteClientMaxBitrate);
|
||||
}
|
||||
}
|
||||
|
||||
return maxBitrate;
|
||||
|
|
|
@ -11,6 +11,7 @@ using MediaBrowser.Model.IO;
|
|||
using MediaBrowser.Model.Serialization;
|
||||
using ServiceStack;
|
||||
using System.Collections.Generic;
|
||||
using CommonIO;
|
||||
|
||||
namespace MediaBrowser.Api.Playback.Progressive
|
||||
{
|
||||
|
|
|
@ -17,6 +17,7 @@ using System.Globalization;
|
|||
using System.IO;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using CommonIO;
|
||||
|
||||
namespace MediaBrowser.Api.Playback.Progressive
|
||||
{
|
||||
|
@ -139,7 +140,7 @@ namespace MediaBrowser.Api.Playback.Progressive
|
|||
}
|
||||
|
||||
var outputPath = state.OutputFilePath;
|
||||
var outputPathExists = File.Exists(outputPath);
|
||||
var outputPathExists = FileSystem.FileExists(outputPath);
|
||||
|
||||
var isTranscodeCached = outputPathExists && !ApiEntryPoint.Instance.HasActiveTranscodingJob(outputPath, TranscodingJobType.Progressive);
|
||||
|
||||
|
@ -325,7 +326,7 @@ namespace MediaBrowser.Api.Playback.Progressive
|
|||
{
|
||||
TranscodingJob job;
|
||||
|
||||
if (!File.Exists(outputPath))
|
||||
if (!FileSystem.FileExists(outputPath))
|
||||
{
|
||||
job = await StartFfMpeg(state, outputPath, cancellationTokenSource).ConfigureAwait(false);
|
||||
}
|
||||
|
|
|
@ -5,6 +5,7 @@ using System;
|
|||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Threading.Tasks;
|
||||
using CommonIO;
|
||||
|
||||
namespace MediaBrowser.Api.Playback.Progressive
|
||||
{
|
||||
|
|
|
@ -11,12 +11,14 @@ using MediaBrowser.Model.Serialization;
|
|||
using ServiceStack;
|
||||
using System;
|
||||
using System.IO;
|
||||
using CommonIO;
|
||||
|
||||
namespace MediaBrowser.Api.Playback.Progressive
|
||||
{
|
||||
/// <summary>
|
||||
/// Class GetVideoStream
|
||||
/// </summary>
|
||||
[Route("/Videos/{Id}/stream.mpegts", "GET")]
|
||||
[Route("/Videos/{Id}/stream.ts", "GET")]
|
||||
[Route("/Videos/{Id}/stream.webm", "GET")]
|
||||
[Route("/Videos/{Id}/stream.asf", "GET")]
|
||||
|
|
|
@ -74,7 +74,7 @@ namespace MediaBrowser.Api.Playback
|
|||
{
|
||||
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
|
||||
{
|
||||
get
|
||||
|
|
|
@ -40,10 +40,27 @@ namespace MediaBrowser.Api
|
|||
/// Gets or sets the user id.
|
||||
/// </summary>
|
||||
/// <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; }
|
||||
}
|
||||
|
||||
[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")]
|
||||
public class RemoveFromPlaylist : IReturnVoid
|
||||
{
|
||||
|
@ -105,6 +122,13 @@ namespace MediaBrowser.Api
|
|||
_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)
|
||||
{
|
||||
var result = await _playlistManager.CreatePlaylist(new PlaylistCreationRequest
|
||||
|
|
|
@ -118,6 +118,14 @@ namespace MediaBrowser.Api
|
|||
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>
|
||||
/// Class PluginsService
|
||||
/// </summary>
|
||||
|
@ -265,6 +273,16 @@ namespace MediaBrowser.Api
|
|||
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>
|
||||
/// Posts the specified request.
|
||||
/// </summary>
|
||||
|
|
|
@ -43,7 +43,6 @@ namespace MediaBrowser.Api.Reports
|
|||
MusicArtist,
|
||||
AudioAlbum,
|
||||
Locked,
|
||||
Unidentified,
|
||||
ImagePrimary,
|
||||
ImageBackdrop,
|
||||
ImageLogo,
|
||||
|
|
|
@ -17,7 +17,6 @@ namespace MediaBrowser.Api.Reports
|
|||
TrailersImage,
|
||||
SpecialsImage,
|
||||
LockDataImage,
|
||||
UnidentifiedImage,
|
||||
TagsPrimaryImage,
|
||||
TagsBackdropImage,
|
||||
TagsLogoImage,
|
||||
|
|
|
@ -105,7 +105,6 @@ namespace MediaBrowser.Api.Reports
|
|||
{
|
||||
HeaderMetadata.Status,
|
||||
HeaderMetadata.Locked,
|
||||
HeaderMetadata.Unidentified,
|
||||
HeaderMetadata.ImagePrimary,
|
||||
HeaderMetadata.ImageBackdrop,
|
||||
HeaderMetadata.ImageLogo,
|
||||
|
@ -122,7 +121,6 @@ namespace MediaBrowser.Api.Reports
|
|||
{
|
||||
HeaderMetadata.Status,
|
||||
HeaderMetadata.Locked,
|
||||
HeaderMetadata.Unidentified,
|
||||
HeaderMetadata.ImagePrimary,
|
||||
HeaderMetadata.ImageBackdrop,
|
||||
HeaderMetadata.ImageLogo,
|
||||
|
@ -143,7 +141,6 @@ namespace MediaBrowser.Api.Reports
|
|||
{
|
||||
HeaderMetadata.Status,
|
||||
HeaderMetadata.Locked,
|
||||
HeaderMetadata.Unidentified,
|
||||
HeaderMetadata.ImagePrimary,
|
||||
HeaderMetadata.ImageBackdrop,
|
||||
HeaderMetadata.ImageLogo,
|
||||
|
@ -161,7 +158,6 @@ namespace MediaBrowser.Api.Reports
|
|||
{
|
||||
HeaderMetadata.Status,
|
||||
HeaderMetadata.Locked,
|
||||
HeaderMetadata.Unidentified,
|
||||
HeaderMetadata.ImagePrimary,
|
||||
HeaderMetadata.ImageBackdrop,
|
||||
HeaderMetadata.ImageLogo,
|
||||
|
@ -177,7 +173,6 @@ namespace MediaBrowser.Api.Reports
|
|||
{
|
||||
HeaderMetadata.Status,
|
||||
HeaderMetadata.Locked,
|
||||
HeaderMetadata.Unidentified,
|
||||
HeaderMetadata.ImagePrimary,
|
||||
HeaderMetadata.ImageBackdrop,
|
||||
HeaderMetadata.ImageLogo,
|
||||
|
@ -198,7 +193,6 @@ namespace MediaBrowser.Api.Reports
|
|||
{
|
||||
HeaderMetadata.Status,
|
||||
HeaderMetadata.Locked,
|
||||
HeaderMetadata.Unidentified,
|
||||
HeaderMetadata.ImagePrimary,
|
||||
HeaderMetadata.ImageBackdrop,
|
||||
HeaderMetadata.ImageLogo,
|
||||
|
@ -223,7 +217,6 @@ namespace MediaBrowser.Api.Reports
|
|||
{
|
||||
HeaderMetadata.Status,
|
||||
HeaderMetadata.Locked,
|
||||
HeaderMetadata.Unidentified,
|
||||
HeaderMetadata.ImagePrimary,
|
||||
HeaderMetadata.ImageBackdrop,
|
||||
HeaderMetadata.ImageLogo,
|
||||
|
@ -241,7 +234,6 @@ namespace MediaBrowser.Api.Reports
|
|||
{
|
||||
HeaderMetadata.Status,
|
||||
HeaderMetadata.Locked,
|
||||
HeaderMetadata.Unidentified,
|
||||
HeaderMetadata.ImagePrimary,
|
||||
HeaderMetadata.ImageBackdrop,
|
||||
HeaderMetadata.ImageLogo,
|
||||
|
@ -260,7 +252,6 @@ namespace MediaBrowser.Api.Reports
|
|||
{
|
||||
HeaderMetadata.Status,
|
||||
HeaderMetadata.Locked,
|
||||
HeaderMetadata.Unidentified,
|
||||
HeaderMetadata.ImagePrimary,
|
||||
HeaderMetadata.ImageBackdrop,
|
||||
HeaderMetadata.ImageLogo,
|
||||
|
@ -284,7 +275,6 @@ namespace MediaBrowser.Api.Reports
|
|||
{
|
||||
HeaderMetadata.Status,
|
||||
HeaderMetadata.Locked,
|
||||
HeaderMetadata.Unidentified,
|
||||
HeaderMetadata.ImagePrimary,
|
||||
HeaderMetadata.ImageBackdrop,
|
||||
HeaderMetadata.ImageLogo,
|
||||
|
@ -315,11 +305,9 @@ namespace MediaBrowser.Api.Reports
|
|||
{
|
||||
HeaderMetadata.Status,
|
||||
HeaderMetadata.Locked,
|
||||
HeaderMetadata.Unidentified,
|
||||
HeaderMetadata.ImagePrimary,
|
||||
HeaderMetadata.ImageBackdrop,
|
||||
HeaderMetadata.ImageLogo,
|
||||
HeaderMetadata.Unidentified,
|
||||
HeaderMetadata.ImagePrimary,
|
||||
HeaderMetadata.ImageBackdrop,
|
||||
HeaderMetadata.ImageLogo,
|
||||
|
@ -376,12 +364,6 @@ namespace MediaBrowser.Api.Reports
|
|||
option.Header.CanGroup = false;
|
||||
option.Header.DisplayType = ReportDisplayType.Export;
|
||||
break;
|
||||
case HeaderMetadata.Unidentified:
|
||||
option.Column = (i, r) => this.GetBoolString(r.IsUnidentified);
|
||||
option.Header.ItemViewType = ItemViewType.UnidentifiedImage;
|
||||
option.Header.CanGroup = false;
|
||||
option.Header.DisplayType = ReportDisplayType.Export;
|
||||
break;
|
||||
case HeaderMetadata.ImagePrimary:
|
||||
option.Column = (i, r) => this.GetBoolString(r.HasImageTagsPrimary);
|
||||
option.Header.ItemViewType = ItemViewType.TagsPrimaryImage;
|
||||
|
@ -633,7 +615,6 @@ namespace MediaBrowser.Api.Reports
|
|||
{
|
||||
Id = item.Id.ToString("N"),
|
||||
HasLockData = item.IsLocked,
|
||||
IsUnidentified = item.IsUnidentified,
|
||||
HasLocalTrailer = hasTrailers != null ? hasTrailers.GetTrailerIds().Count() > 0 : false,
|
||||
HasImageTagsPrimary = (item.ImageInfos != null && item.ImageInfos.Count(n => n.Type == ImageType.Primary) > 0),
|
||||
HasImageTagsBackdrop = (item.ImageInfos != null && item.ImageInfos.Count(n => n.Type == ImageType.Backdrop) > 0),
|
||||
|
|
|
@ -56,10 +56,6 @@ namespace MediaBrowser.Api.Reports
|
|||
/// <value> true if this object has specials, false if not. </value>
|
||||
public bool HasSpecials { get; set; }
|
||||
|
||||
/// <summary> Gets or sets a value indicating whether this object is unidentified. </summary>
|
||||
/// <value> true if this object is unidentified, false if not. </value>
|
||||
public bool IsUnidentified { get; set; }
|
||||
|
||||
/// <summary> Gets or sets the columns. </summary>
|
||||
/// <value> The columns. </value>
|
||||
public List<ReportItem> Columns { get; set; }
|
||||
|
|
|
@ -226,7 +226,6 @@ namespace MediaBrowser.Api.Reports
|
|||
NameStartsWithOrGreater = request.NameStartsWithOrGreater,
|
||||
HasImdbId = request.HasImdbId,
|
||||
IsYearMismatched = request.IsYearMismatched,
|
||||
IsUnidentified = request.IsUnidentified,
|
||||
IsPlaceHolder = request.IsPlaceHolder,
|
||||
IsLocked = request.IsLocked,
|
||||
IsInBoxSet = request.IsInBoxSet,
|
||||
|
|
|
@ -213,7 +213,7 @@ namespace MediaBrowser.Api.Reports
|
|||
};
|
||||
foreach (var item in t)
|
||||
{
|
||||
var ps = items.Where(x => x.People != null && x.SupportsPeople).SelectMany(x => x.People)
|
||||
var ps = items.SelectMany(x => _libraryManager.GetPeople(x))
|
||||
.Where(n => n.Type == item.ToString())
|
||||
.GroupBy(x => x.Name)
|
||||
.OrderByDescending(x => x.Count())
|
||||
|
|
|
@ -124,7 +124,7 @@ namespace MediaBrowser.Api.Social
|
|||
Task.WaitAll(task);
|
||||
}
|
||||
|
||||
public object Get(GetShareImage request)
|
||||
public async Task<object> Get(GetShareImage request)
|
||||
{
|
||||
var share = _sharingManager.GetShareInfo(request.Id);
|
||||
|
||||
|
@ -143,7 +143,21 @@ namespace MediaBrowser.Api.Social
|
|||
|
||||
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
|
||||
|
|
|
@ -69,10 +69,11 @@ namespace MediaBrowser.Api
|
|||
_config.Configuration.MergeMetadataAndImagesByName = true;
|
||||
_config.Configuration.EnableStandaloneMetadata = true;
|
||||
_config.Configuration.EnableLibraryMetadataSubFolder = true;
|
||||
_config.Configuration.EnableUserSpecificUserViews = true;
|
||||
_config.Configuration.EnableCustomPathSubFolders = true;
|
||||
_config.Configuration.DisableXmlSavers = true;
|
||||
_config.Configuration.DisableStartupScan = true;
|
||||
_config.Configuration.EnableUserViews = true;
|
||||
_config.Configuration.EnableDateLastRefresh = true;
|
||||
_config.SaveConfiguration();
|
||||
}
|
||||
|
||||
|
|
|
@ -15,7 +15,9 @@ using System.Linq;
|
|||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using CommonIO;
|
||||
using MimeTypes = MediaBrowser.Model.Net.MimeTypes;
|
||||
using MediaBrowser.Common.IO;
|
||||
|
||||
namespace MediaBrowser.Api.Subtitles
|
||||
{
|
||||
|
@ -127,14 +129,16 @@ namespace MediaBrowser.Api.Subtitles
|
|||
private readonly ISubtitleEncoder _subtitleEncoder;
|
||||
private readonly IMediaSourceManager _mediaSourceManager;
|
||||
private readonly IProviderManager _providerManager;
|
||||
private readonly IFileSystem _fileSystem;
|
||||
|
||||
public SubtitleService(ILibraryManager libraryManager, ISubtitleManager subtitleManager, ISubtitleEncoder subtitleEncoder, IMediaSourceManager mediaSourceManager, IProviderManager providerManager)
|
||||
public SubtitleService(ILibraryManager libraryManager, ISubtitleManager subtitleManager, ISubtitleEncoder subtitleEncoder, IMediaSourceManager mediaSourceManager, IProviderManager providerManager, IFileSystem fileSystem)
|
||||
{
|
||||
_libraryManager = libraryManager;
|
||||
_subtitleManager = subtitleManager;
|
||||
_subtitleEncoder = subtitleEncoder;
|
||||
_mediaSourceManager = mediaSourceManager;
|
||||
_providerManager = providerManager;
|
||||
_fileSystem = fileSystem;
|
||||
}
|
||||
|
||||
public async Task<object> Get(GetSubtitlePlaylist request)
|
||||
|
@ -259,7 +263,7 @@ namespace MediaBrowser.Api.Subtitles
|
|||
await _subtitleManager.DownloadSubtitles(video, request.SubtitleId, CancellationToken.None)
|
||||
.ConfigureAwait(false);
|
||||
|
||||
_providerManager.QueueRefresh(video.Id, new MetadataRefreshOptions());
|
||||
_providerManager.QueueRefresh(video.Id, new MetadataRefreshOptions(_fileSystem));
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
|
|
|
@ -244,7 +244,15 @@ namespace MediaBrowser.Api.Sync
|
|||
var task = _syncManager.ReportSyncJobItemTransferBeginning(request.Id);
|
||||
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)
|
||||
|
|
|
@ -12,6 +12,7 @@ using System.Collections.Generic;
|
|||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using CommonIO;
|
||||
|
||||
namespace MediaBrowser.Api.System
|
||||
{
|
||||
|
@ -118,18 +119,17 @@ namespace MediaBrowser.Api.System
|
|||
|
||||
public object Get(GetServerLogs request)
|
||||
{
|
||||
List<FileInfo> files;
|
||||
List<FileSystemMetadata> files;
|
||||
|
||||
try
|
||||
{
|
||||
files = new DirectoryInfo(_appPaths.LogDirectoryPath)
|
||||
.EnumerateFiles("*", SearchOption.AllDirectories)
|
||||
files = _fileSystem.GetFiles(_appPaths.LogDirectoryPath)
|
||||
.Where(i => string.Equals(i.Extension, ".txt", StringComparison.OrdinalIgnoreCase))
|
||||
.ToList();
|
||||
}
|
||||
catch (DirectoryNotFoundException)
|
||||
{
|
||||
files = new List<FileInfo>();
|
||||
files = new List<FileSystemMetadata>();
|
||||
}
|
||||
|
||||
var result = files.Select(i => new LogFile
|
||||
|
@ -149,8 +149,7 @@ namespace MediaBrowser.Api.System
|
|||
|
||||
public object Get(GetLogFile request)
|
||||
{
|
||||
var file = new DirectoryInfo(_appPaths.LogDirectoryPath)
|
||||
.EnumerateFiles("*", SearchOption.AllDirectories)
|
||||
var file = _fileSystem.GetFiles(_appPaths.LogDirectoryPath)
|
||||
.First(i => string.Equals(i.Name, request.Name, StringComparison.OrdinalIgnoreCase));
|
||||
|
||||
return ResultFactory.GetStaticFileResult(Request, file.FullName, FileShare.ReadWrite);
|
||||
|
|
|
@ -299,9 +299,6 @@ namespace MediaBrowser.Api.UserLibrary
|
|||
[ApiMember(Name = "IsLocked", Description = "Optional filter by items that are locked.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")]
|
||||
public bool? IsLocked { get; set; }
|
||||
|
||||
[ApiMember(Name = "IsUnidentified", Description = "Optional filter by items that are unidentified by internet metadata providers.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")]
|
||||
public bool? IsUnidentified { get; set; }
|
||||
|
||||
[ApiMember(Name = "IsPlaceHolder", Description = "Optional filter by items that are placeholders", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")]
|
||||
public bool? IsPlaceHolder { get; set; }
|
||||
|
||||
|
|
|
@ -192,7 +192,6 @@ namespace MediaBrowser.Api.UserLibrary
|
|||
NameStartsWithOrGreater = request.NameStartsWithOrGreater,
|
||||
HasImdbId = request.HasImdbId,
|
||||
IsYearMismatched = request.IsYearMismatched,
|
||||
IsUnidentified = request.IsUnidentified,
|
||||
IsPlaceHolder = request.IsPlaceHolder,
|
||||
IsLocked = request.IsLocked,
|
||||
IsInBoxSet = request.IsInBoxSet,
|
||||
|
|
|
@ -39,6 +39,17 @@ namespace MediaBrowser.Api.UserLibrary
|
|||
public string UserId { get; set; }
|
||||
}
|
||||
|
||||
[Route("/Users/{UserId}/GroupingOptions", "GET")]
|
||||
public class GetGroupingOptions : IReturn<List<SpecialViewOption>>
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the user id.
|
||||
/// </summary>
|
||||
/// <value>The user id.</value>
|
||||
[ApiMember(Name = "UserId", Description = "User Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "GET")]
|
||||
public string UserId { get; set; }
|
||||
}
|
||||
|
||||
public class UserViewsService : BaseApiService
|
||||
{
|
||||
private readonly IUserManager _userManager;
|
||||
|
@ -105,6 +116,29 @@ namespace MediaBrowser.Api.UserLibrary
|
|||
return ToOptimizedResult(list);
|
||||
}
|
||||
|
||||
public async Task<object> Get(GetGroupingOptions request)
|
||||
{
|
||||
var user = _userManager.GetUserById(request.UserId);
|
||||
|
||||
var views = user.RootFolder
|
||||
.GetChildren(user, true)
|
||||
.OfType<Folder>()
|
||||
.Where(i => !UserView.IsExcludedFromGrouping(i))
|
||||
.ToList();
|
||||
|
||||
var list = views
|
||||
.Select(i => new SpecialViewOption
|
||||
{
|
||||
Name = i.Name,
|
||||
Id = i.Id.ToString("N")
|
||||
|
||||
})
|
||||
.OrderBy(i => i.Name)
|
||||
.ToList();
|
||||
|
||||
return ToOptimizedResult(list);
|
||||
}
|
||||
|
||||
private bool IsEligibleForSpecialView(ICollectionFolder view)
|
||||
{
|
||||
var types = new[] { CollectionType.Movies, CollectionType.TvShows, CollectionType.Games, CollectionType.Music, CollectionType.Photos };
|
||||
|
|
|
@ -11,6 +11,7 @@ using System;
|
|||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using CommonIO;
|
||||
|
||||
namespace MediaBrowser.Api
|
||||
{
|
||||
|
|
|
@ -1,4 +1,6 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<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>
|
|
@ -7,6 +7,8 @@ using SharpCompress.Reader;
|
|||
using SharpCompress.Reader.Zip;
|
||||
using System;
|
||||
using System.IO;
|
||||
using CommonIO;
|
||||
using MediaBrowser.Common.IO;
|
||||
|
||||
namespace MediaBrowser.Common.Implementations.Archiving
|
||||
{
|
||||
|
@ -15,7 +17,14 @@ namespace MediaBrowser.Common.Implementations.Archiving
|
|||
/// </summary>
|
||||
public class ZipClient : IZipClient
|
||||
{
|
||||
/// <summary>
|
||||
private IFileSystem _fileSystem;
|
||||
|
||||
public ZipClient(IFileSystem fileSystem)
|
||||
{
|
||||
_fileSystem = fileSystem;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Extracts all.
|
||||
/// </summary>
|
||||
/// <param name="sourceFile">The source file.</param>
|
||||
|
@ -23,7 +32,7 @@ namespace MediaBrowser.Common.Implementations.Archiving
|
|||
/// <param name="overwriteExistingFiles">if set to <c>true</c> [overwrite existing files].</param>
|
||||
public void ExtractAll(string sourceFile, string targetPath, bool overwriteExistingFiles)
|
||||
{
|
||||
using (var fileStream = File.OpenRead(sourceFile))
|
||||
using (var fileStream = _fileSystem.OpenRead(sourceFile))
|
||||
{
|
||||
ExtractAll(fileStream, targetPath, overwriteExistingFiles);
|
||||
}
|
||||
|
@ -73,7 +82,7 @@ namespace MediaBrowser.Common.Implementations.Archiving
|
|||
/// <param name="overwriteExistingFiles">if set to <c>true</c> [overwrite existing files].</param>
|
||||
public void ExtractAllFrom7z(string sourceFile, string targetPath, bool overwriteExistingFiles)
|
||||
{
|
||||
using (var fileStream = File.OpenRead(sourceFile))
|
||||
using (var fileStream = _fileSystem.OpenRead(sourceFile))
|
||||
{
|
||||
ExtractAllFrom7z(fileStream, targetPath, overwriteExistingFiles);
|
||||
}
|
||||
|
@ -112,7 +121,7 @@ namespace MediaBrowser.Common.Implementations.Archiving
|
|||
/// <param name="overwriteExistingFiles">if set to <c>true</c> [overwrite existing files].</param>
|
||||
public void ExtractAllFromTar(string sourceFile, string targetPath, bool overwriteExistingFiles)
|
||||
{
|
||||
using (var fileStream = File.OpenRead(sourceFile))
|
||||
using (var fileStream = _fileSystem.OpenRead(sourceFile))
|
||||
{
|
||||
ExtractAllFromTar(fileStream, targetPath, overwriteExistingFiles);
|
||||
}
|
||||
|
@ -150,7 +159,7 @@ namespace MediaBrowser.Common.Implementations.Archiving
|
|||
/// <param name="overwriteExistingFiles">if set to <c>true</c> [overwrite existing files].</param>
|
||||
public void ExtractAllFromRar(string sourceFile, string targetPath, bool overwriteExistingFiles)
|
||||
{
|
||||
using (var fileStream = File.OpenRead(sourceFile))
|
||||
using (var fileStream = _fileSystem.OpenRead(sourceFile))
|
||||
{
|
||||
ExtractAllFromRar(fileStream, targetPath, overwriteExistingFiles);
|
||||
}
|
||||
|
|
|
@ -30,6 +30,7 @@ using System.Reflection;
|
|||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using CommonIO;
|
||||
|
||||
namespace MediaBrowser.Common.Implementations
|
||||
{
|
||||
|
@ -93,7 +94,7 @@ namespace MediaBrowser.Common.Implementations
|
|||
/// <summary>
|
||||
/// The _XML serializer
|
||||
/// </summary>
|
||||
protected readonly IXmlSerializer XmlSerializer = new XmlSerializer();
|
||||
protected readonly IXmlSerializer XmlSerializer;
|
||||
|
||||
/// <summary>
|
||||
/// Gets assemblies that failed to load
|
||||
|
@ -180,7 +181,7 @@ namespace MediaBrowser.Common.Implementations
|
|||
{
|
||||
if (_deviceId == null)
|
||||
{
|
||||
_deviceId = new DeviceId(ApplicationPaths, LogManager.GetLogger("SystemId"));
|
||||
_deviceId = new DeviceId(ApplicationPaths, LogManager.GetLogger("SystemId"), FileSystemManager);
|
||||
}
|
||||
|
||||
return _deviceId.Value;
|
||||
|
@ -199,6 +200,7 @@ namespace MediaBrowser.Common.Implementations
|
|||
ILogManager logManager,
|
||||
IFileSystem fileSystem)
|
||||
{
|
||||
XmlSerializer = new MediaBrowser.Common.Implementations.Serialization.XmlSerializer (fileSystem);
|
||||
FailedAssemblies = new List<string>();
|
||||
|
||||
ApplicationPaths = applicationPaths;
|
||||
|
@ -320,7 +322,7 @@ namespace MediaBrowser.Common.Implementations
|
|||
|
||||
protected virtual IJsonSerializer CreateJsonSerializer()
|
||||
{
|
||||
return new JsonSerializer();
|
||||
return new JsonSerializer(FileSystemManager);
|
||||
}
|
||||
|
||||
private void SetHttpLimit()
|
||||
|
@ -414,6 +416,8 @@ namespace MediaBrowser.Common.Implementations
|
|||
/// </summary>
|
||||
protected virtual void FindParts()
|
||||
{
|
||||
RegisterModules();
|
||||
|
||||
ConfigurationManager.AddParts(GetExports<IConfigurationFactory>());
|
||||
Plugins = GetExports<IPlugin>();
|
||||
}
|
||||
|
@ -449,7 +453,7 @@ namespace MediaBrowser.Common.Implementations
|
|||
|
||||
RegisterSingleInstance<IApplicationPaths>(ApplicationPaths);
|
||||
|
||||
TaskManager = new TaskManager(ApplicationPaths, JsonSerializer, Logger);
|
||||
TaskManager = new TaskManager(ApplicationPaths, JsonSerializer, Logger, FileSystemManager);
|
||||
|
||||
RegisterSingleInstance(JsonSerializer);
|
||||
RegisterSingleInstance(XmlSerializer);
|
||||
|
@ -473,13 +477,12 @@ namespace MediaBrowser.Common.Implementations
|
|||
InstallationManager = new InstallationManager(Logger, this, ApplicationPaths, HttpClient, JsonSerializer, SecurityManager, ConfigurationManager, FileSystemManager);
|
||||
RegisterSingleInstance(InstallationManager);
|
||||
|
||||
ZipClient = new ZipClient();
|
||||
ZipClient = new ZipClient(FileSystemManager);
|
||||
RegisterSingleInstance(ZipClient);
|
||||
|
||||
IsoManager = new IsoManager();
|
||||
RegisterSingleInstance(IsoManager);
|
||||
|
||||
RegisterModules();
|
||||
return Task.FromResult (true);
|
||||
}
|
||||
|
||||
|
@ -522,6 +525,14 @@ namespace MediaBrowser.Common.Implementations
|
|||
}
|
||||
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
|
||||
return ex.Types.Where(t => t != null);
|
||||
}
|
||||
|
@ -581,7 +592,7 @@ namespace MediaBrowser.Common.Implementations
|
|||
protected void RegisterSingleInstance<T>(T obj, bool manageLifetime = true)
|
||||
where T : class
|
||||
{
|
||||
Container.RegisterSingle(obj);
|
||||
Container.RegisterSingleton(obj);
|
||||
|
||||
if (manageLifetime)
|
||||
{
|
||||
|
@ -607,7 +618,7 @@ namespace MediaBrowser.Common.Implementations
|
|||
protected void RegisterSingleInstance<T>(Func<T> func)
|
||||
where T : class
|
||||
{
|
||||
Container.RegisterSingle(func);
|
||||
Container.RegisterSingleton(func);
|
||||
}
|
||||
|
||||
void IDependencyContainer.Register(Type typeInterface, Type typeImplementation)
|
||||
|
|
|
@ -9,6 +9,8 @@ using System.Collections.Generic;
|
|||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using CommonIO;
|
||||
using MediaBrowser.Common.Extensions;
|
||||
|
||||
namespace MediaBrowser.Common.Implementations.Configuration
|
||||
{
|
||||
|
@ -32,7 +34,7 @@ namespace MediaBrowser.Common.Implementations.Configuration
|
|||
/// Occurs when [configuration updating].
|
||||
/// </summary>
|
||||
public event EventHandler<ConfigurationUpdateEventArgs> NamedConfigurationUpdating;
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Occurs when [named configuration updated].
|
||||
/// </summary>
|
||||
|
@ -54,6 +56,7 @@ namespace MediaBrowser.Common.Implementations.Configuration
|
|||
/// </summary>
|
||||
/// <value>The application paths.</value>
|
||||
public IApplicationPaths CommonApplicationPaths { get; private set; }
|
||||
public readonly IFileSystem FileSystem;
|
||||
|
||||
/// <summary>
|
||||
/// The _configuration loaded
|
||||
|
@ -87,8 +90,8 @@ namespace MediaBrowser.Common.Implementations.Configuration
|
|||
}
|
||||
}
|
||||
|
||||
private ConfigurationStore[] _configurationStores = {};
|
||||
private IConfigurationFactory[] _configurationFactories = {};
|
||||
private ConfigurationStore[] _configurationStores = { };
|
||||
private IConfigurationFactory[] _configurationFactories = { };
|
||||
|
||||
/// <summary>
|
||||
/// 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="logManager">The log manager.</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;
|
||||
XmlSerializer = xmlSerializer;
|
||||
FileSystem = fileSystem;
|
||||
Logger = logManager.GetLogger(GetType().Name);
|
||||
|
||||
UpdateCachePath();
|
||||
|
@ -199,9 +203,19 @@ namespace MediaBrowser.Common.Implementations.Configuration
|
|||
{
|
||||
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 string GetConfigurationFile(string key)
|
||||
|
@ -215,9 +229,15 @@ namespace MediaBrowser.Common.Implementations.Configuration
|
|||
{
|
||||
var file = GetConfigurationFile(key);
|
||||
|
||||
var configurationType = _configurationStores
|
||||
.First(i => string.Equals(i.Key, key, StringComparison.OrdinalIgnoreCase))
|
||||
.ConfigurationType;
|
||||
var configurationInfo = _configurationStores
|
||||
.FirstOrDefault(i => string.Equals(i.Key, key, StringComparison.OrdinalIgnoreCase));
|
||||
|
||||
if (configurationInfo == null)
|
||||
{
|
||||
throw new ResourceNotFoundException("Configuration with key " + key + " not found.");
|
||||
}
|
||||
|
||||
var configurationType = configurationInfo.ConfigurationType;
|
||||
|
||||
lock (_configurationSyncLock)
|
||||
{
|
||||
|
@ -272,7 +292,7 @@ namespace MediaBrowser.Common.Implementations.Configuration
|
|||
NewConfiguration = configuration
|
||||
|
||||
}, Logger);
|
||||
|
||||
|
||||
_configurations.AddOrUpdate(key, configuration, (k, v) => configuration);
|
||||
|
||||
var path = GetConfigurationFile(key);
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
using System;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using MediaBrowser.Common.IO;
|
||||
|
||||
namespace MediaBrowser.Common.Implementations.Configuration
|
||||
{
|
||||
|
@ -27,7 +28,7 @@ namespace MediaBrowser.Common.Implementations.Configuration
|
|||
// Use try/catch to avoid the extra file system lookup using File.Exists
|
||||
try
|
||||
{
|
||||
buffer = File.ReadAllBytes(path);
|
||||
buffer = File.ReadAllBytes(path);
|
||||
|
||||
configuration = xmlSerializer.DeserializeFromBytes(type, buffer);
|
||||
}
|
||||
|
@ -46,10 +47,10 @@ namespace MediaBrowser.Common.Implementations.Configuration
|
|||
// If the file didn't exist before, or if something has changed, re-save
|
||||
if (buffer == null || !buffer.SequenceEqual(newBytes))
|
||||
{
|
||||
Directory.CreateDirectory(Path.GetDirectoryName(path));
|
||||
Directory.CreateDirectory(Path.GetDirectoryName(path));
|
||||
|
||||
// Save it after load in case we got new items
|
||||
File.WriteAllBytes(path, newBytes);
|
||||
File.WriteAllBytes(path, newBytes);
|
||||
}
|
||||
|
||||
return configuration;
|
||||
|
|
|
@ -3,13 +3,16 @@ using MediaBrowser.Model.Logging;
|
|||
using System;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using CommonIO;
|
||||
using MediaBrowser.Common.IO;
|
||||
|
||||
namespace MediaBrowser.Common.Implementations.Devices
|
||||
{
|
||||
public class DeviceId
|
||||
{
|
||||
private readonly IApplicationPaths _appPaths;
|
||||
private readonly ILogger _logger;
|
||||
private readonly ILogger _logger;
|
||||
private readonly IFileSystem _fileSystem;
|
||||
|
||||
private readonly object _syncLock = new object();
|
||||
|
||||
|
@ -24,7 +27,7 @@ namespace MediaBrowser.Common.Implementations.Devices
|
|||
{
|
||||
lock (_syncLock)
|
||||
{
|
||||
var value = File.ReadAllText(CachePath, Encoding.UTF8);
|
||||
var value = File.ReadAllText(CachePath, Encoding.UTF8);
|
||||
|
||||
Guid guid;
|
||||
if (Guid.TryParse(value, out guid))
|
||||
|
@ -55,11 +58,11 @@ namespace MediaBrowser.Common.Implementations.Devices
|
|||
{
|
||||
var path = CachePath;
|
||||
|
||||
Directory.CreateDirectory(Path.GetDirectoryName(path));
|
||||
_fileSystem.CreateDirectory(Path.GetDirectoryName(path));
|
||||
|
||||
lock (_syncLock)
|
||||
{
|
||||
File.WriteAllText(path, id, Encoding.UTF8);
|
||||
_fileSystem.WriteAllText(path, id, Encoding.UTF8);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
|
@ -88,10 +91,15 @@ namespace MediaBrowser.Common.Implementations.Devices
|
|||
|
||||
private string _id;
|
||||
|
||||
public DeviceId(IApplicationPaths appPaths, ILogger logger)
|
||||
public DeviceId(IApplicationPaths appPaths, ILogger logger, IFileSystem fileSystem)
|
||||
{
|
||||
if (fileSystem == null) {
|
||||
throw new ArgumentNullException ("fileSystem");
|
||||
}
|
||||
|
||||
_appPaths = appPaths;
|
||||
_logger = logger;
|
||||
_fileSystem = fileSystem;
|
||||
}
|
||||
|
||||
public string Value
|
||||
|
|
|
@ -17,6 +17,7 @@ using System.Net.Cache;
|
|||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using CommonIO;
|
||||
|
||||
namespace MediaBrowser.Common.Implementations.HttpClientManager
|
||||
{
|
||||
|
@ -282,8 +283,7 @@ namespace MediaBrowser.Common.Implementations.HttpClientManager
|
|||
|
||||
var url = options.Url;
|
||||
var urlHash = url.ToLower().GetMD5().ToString("N");
|
||||
var semaphore = GetLock(url);
|
||||
|
||||
|
||||
var responseCachePath = Path.Combine(_appPaths.CachePath, "httpclient", urlHash);
|
||||
|
||||
response = await GetCachedResponse(responseCachePath, options.CacheLength, url).ConfigureAwait(false);
|
||||
|
@ -292,6 +292,8 @@ namespace MediaBrowser.Common.Implementations.HttpClientManager
|
|||
return response;
|
||||
}
|
||||
|
||||
var semaphore = GetLock(url);
|
||||
|
||||
await semaphore.WaitAsync(options.CancellationToken).ConfigureAwait(false);
|
||||
|
||||
try
|
||||
|
@ -355,7 +357,7 @@ namespace MediaBrowser.Common.Implementations.HttpClientManager
|
|||
|
||||
private async Task CacheResponse(HttpResponseInfo response, string responseCachePath)
|
||||
{
|
||||
Directory.CreateDirectory(Path.GetDirectoryName(responseCachePath));
|
||||
_fileSystem.CreateDirectory(Path.GetDirectoryName(responseCachePath));
|
||||
|
||||
using (var responseStream = response.Content)
|
||||
{
|
||||
|
@ -464,20 +466,11 @@ namespace MediaBrowser.Common.Implementations.HttpClientManager
|
|||
}
|
||||
catch (OperationCanceledException ex)
|
||||
{
|
||||
var exception = GetCancellationException(options.Url, options.CancellationToken, ex);
|
||||
|
||||
var httpException = exception as HttpException;
|
||||
|
||||
if (httpException != null && httpException.IsTimedOut)
|
||||
{
|
||||
client.LastTimeout = DateTime.UtcNow;
|
||||
}
|
||||
|
||||
throw exception;
|
||||
throw GetCancellationException(options, client, options.CancellationToken, ex);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
throw GetException(ex, options);
|
||||
throw GetException(ex, options, client);
|
||||
}
|
||||
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)
|
||||
{
|
||||
return new HttpResponseInfo(disposable)
|
||||
|
@ -599,7 +571,7 @@ namespace MediaBrowser.Common.Implementations.HttpClientManager
|
|||
{
|
||||
ValidateParams(options);
|
||||
|
||||
Directory.CreateDirectory(_appPaths.TempDirectory);
|
||||
_fileSystem.CreateDirectory(_appPaths.TempDirectory);
|
||||
|
||||
var tempFile = Path.Combine(_appPaths.TempDirectory, Guid.NewGuid() + ".tmp");
|
||||
|
||||
|
@ -624,6 +596,8 @@ namespace MediaBrowser.Common.Implementations.HttpClientManager
|
|||
_logger.Info("HttpClientManager.GetTempFileResponse url: {0}", options.Url);
|
||||
}
|
||||
|
||||
var client = GetHttpClient(GetHostFromUrl(options.Url), options.EnableHttpCompression);
|
||||
|
||||
try
|
||||
{
|
||||
options.CancellationToken.ThrowIfCancellationRequested();
|
||||
|
@ -632,7 +606,6 @@ namespace MediaBrowser.Common.Implementations.HttpClientManager
|
|||
{
|
||||
var httpResponse = (HttpWebResponse)response;
|
||||
|
||||
var client = GetHttpClient(GetHostFromUrl(options.Url), options.EnableHttpCompression);
|
||||
EnsureSuccessStatusCode(client, httpResponse, options);
|
||||
|
||||
options.CancellationToken.ThrowIfCancellationRequested();
|
||||
|
@ -669,7 +642,7 @@ namespace MediaBrowser.Common.Implementations.HttpClientManager
|
|||
catch (Exception ex)
|
||||
{
|
||||
DeleteTempFile(tempFile);
|
||||
throw GetException(ex, options);
|
||||
throw GetException(ex, options, client);
|
||||
}
|
||||
finally
|
||||
{
|
||||
|
@ -694,14 +667,37 @@ namespace MediaBrowser.Common.Implementations.HttpClientManager
|
|||
|
||||
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
|
||||
?? ex.InnerException as WebException;
|
||||
|
||||
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
|
||||
|
@ -709,10 +705,13 @@ namespace MediaBrowser.Common.Implementations.HttpClientManager
|
|||
|
||||
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;
|
||||
}
|
||||
|
@ -784,18 +783,24 @@ namespace MediaBrowser.Common.Implementations.HttpClientManager
|
|||
/// <summary>
|
||||
/// Throws the cancellation exception.
|
||||
/// </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="exception">The exception.</param>
|
||||
/// <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 (!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
|
||||
return new HttpException(msg, exception)
|
||||
|
@ -815,12 +820,6 @@ namespace MediaBrowser.Common.Implementations.HttpClientManager
|
|||
|
||||
if (!isSuccessful)
|
||||
{
|
||||
if ((int) statusCode == 429)
|
||||
{
|
||||
client.LastTimeout = DateTime.UtcNow;
|
||||
}
|
||||
|
||||
if (statusCode == HttpStatusCode.RequestEntityTooLarge)
|
||||
if (options.LogErrorResponseBody)
|
||||
{
|
||||
try
|
||||
|
@ -869,25 +868,11 @@ namespace MediaBrowser.Common.Implementations.HttpClientManager
|
|||
Task<WebResponse> asyncTask = Task.Factory.FromAsync<WebResponse>(request.BeginGetResponse, request.EndGetResponse, null);
|
||||
|
||||
ThreadPool.RegisterWaitForSingleObject((asyncTask as IAsyncResult).AsyncWaitHandle, TimeoutCallback, request, timeout, true);
|
||||
asyncTask.ContinueWith(task =>
|
||||
{
|
||||
taskCompletion.TrySetResult(task.Result);
|
||||
|
||||
}, TaskContinuationOptions.NotOnFaulted);
|
||||
var callback = new TaskCallback { taskCompletion = taskCompletion };
|
||||
asyncTask.ContinueWith(callback.OnSuccess, TaskContinuationOptions.NotOnFaulted);
|
||||
|
||||
// Handle errors
|
||||
asyncTask.ContinueWith(task =>
|
||||
{
|
||||
if (task.Exception != null)
|
||||
{
|
||||
taskCompletion.TrySetException(task.Exception);
|
||||
}
|
||||
else
|
||||
{
|
||||
taskCompletion.TrySetException(new List<Exception>());
|
||||
}
|
||||
|
||||
}, TaskContinuationOptions.OnlyOnFaulted);
|
||||
asyncTask.ContinueWith(callback.OnError, TaskContinuationOptions.OnlyOnFaulted);
|
||||
|
||||
return taskCompletion.Task;
|
||||
}
|
||||
|
@ -903,5 +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>
|
||||
/// <param name="name">The name.</param>
|
||||
/// <returns>ILogger.</returns>
|
||||
public ILogger GetLogger(string name)
|
||||
public Model.Logging.ILogger GetLogger(string name)
|
||||
{
|
||||
return new NLogger(name, this);
|
||||
}
|
||||
|
@ -208,7 +208,7 @@ namespace MediaBrowser.Common.Implementations.Logging
|
|||
{
|
||||
LogFilePath = Path.Combine(LogDirectory, LogFilePrefix + "-" + decimal.Round(DateTime.Now.Ticks / 10000000) + ".txt");
|
||||
|
||||
Directory.CreateDirectory(Path.GetDirectoryName(LogFilePath));
|
||||
Directory.CreateDirectory(Path.GetDirectoryName(LogFilePath));
|
||||
|
||||
AddFileTarget(LogFilePath, level);
|
||||
|
||||
|
|
|
@ -14,7 +14,6 @@
|
|||
<ProductVersion>10.0.0</ProductVersion>
|
||||
<SchemaVersion>2.0</SchemaVersion>
|
||||
<TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
|
||||
<RestorePackages>true</RestorePackages>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
|
@ -48,18 +47,24 @@
|
|||
<RunPostBuildEvent>Always</RunPostBuildEvent>
|
||||
</PropertyGroup>
|
||||
<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>
|
||||
<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 Include="SharpCompress, Version=0.10.2.0, Culture=neutral, PublicKeyToken=beaf6f427e128133, processorArchitecture=MSIL">
|
||||
<SpecificVersion>False</SpecificVersion>
|
||||
<HintPath>..\ThirdParty\SharpCompress\SharpCompress.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="SimpleInjector, Version=2.7.0.0, Culture=neutral, PublicKeyToken=984cb50dea722e99, processorArchitecture=MSIL">
|
||||
<Reference Include="SimpleInjector, Version=2.8.0.0, Culture=neutral, PublicKeyToken=984cb50dea722e99, processorArchitecture=MSIL">
|
||||
<SpecificVersion>False</SpecificVersion>
|
||||
<HintPath>..\packages\SimpleInjector.2.8.0\lib\net45\SimpleInjector.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
<HintPath>..\packages\SimpleInjector.3.0.5\lib\net45\SimpleInjector.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="System" />
|
||||
<Reference Include="System.Core" />
|
||||
|
@ -82,7 +87,6 @@
|
|||
<Compile Include="Devices\DeviceId.cs" />
|
||||
<Compile Include="HttpClientManager\HttpClientInfo.cs" />
|
||||
<Compile Include="HttpClientManager\HttpClientManager.cs" />
|
||||
<Compile Include="IO\CommonFileSystem.cs" />
|
||||
<Compile Include="IO\IsoManager.cs" />
|
||||
<Compile Include="Logging\LogHelper.cs" />
|
||||
<Compile Include="Logging\NLogger.cs" />
|
||||
|
|
|
@ -12,6 +12,7 @@ using System.IO;
|
|||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using CommonIO;
|
||||
|
||||
namespace MediaBrowser.Common.Implementations.ScheduledTasks
|
||||
{
|
||||
|
@ -51,6 +52,7 @@ namespace MediaBrowser.Common.Implementations.ScheduledTasks
|
|||
/// </summary>
|
||||
/// <value>The task manager.</value>
|
||||
private ITaskManager TaskManager { get; set; }
|
||||
private readonly IFileSystem _fileSystem;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ScheduledTaskWorker" /> class.
|
||||
|
@ -71,7 +73,7 @@ namespace MediaBrowser.Common.Implementations.ScheduledTasks
|
|||
/// or
|
||||
/// logger
|
||||
/// </exception>
|
||||
public ScheduledTaskWorker(IScheduledTask scheduledTask, IApplicationPaths applicationPaths, ITaskManager taskManager, IJsonSerializer jsonSerializer, ILogger logger)
|
||||
public ScheduledTaskWorker(IScheduledTask scheduledTask, IApplicationPaths applicationPaths, ITaskManager taskManager, IJsonSerializer jsonSerializer, ILogger logger, IFileSystem fileSystem)
|
||||
{
|
||||
if (scheduledTask == null)
|
||||
{
|
||||
|
@ -99,6 +101,7 @@ namespace MediaBrowser.Common.Implementations.ScheduledTasks
|
|||
TaskManager = taskManager;
|
||||
JsonSerializer = jsonSerializer;
|
||||
Logger = logger;
|
||||
_fileSystem = fileSystem;
|
||||
|
||||
ReloadTriggerEvents(true);
|
||||
}
|
||||
|
@ -154,7 +157,7 @@ namespace MediaBrowser.Common.Implementations.ScheduledTasks
|
|||
_lastExecutionResult = value;
|
||||
|
||||
var path = GetHistoryFilePath();
|
||||
Directory.CreateDirectory(Path.GetDirectoryName(path));
|
||||
_fileSystem.CreateDirectory(Path.GetDirectoryName(path));
|
||||
|
||||
lock (_lastExecutionResultSyncLock)
|
||||
{
|
||||
|
@ -300,6 +303,11 @@ namespace MediaBrowser.Common.Implementations.ScheduledTasks
|
|||
}
|
||||
}
|
||||
|
||||
public void ReloadTriggerEvents()
|
||||
{
|
||||
ReloadTriggerEvents(false);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reloads the trigger events.
|
||||
/// </summary>
|
||||
|
@ -552,7 +560,7 @@ namespace MediaBrowser.Common.Implementations.ScheduledTasks
|
|||
{
|
||||
var path = GetConfigurationFilePath();
|
||||
|
||||
Directory.CreateDirectory(Path.GetDirectoryName(path));
|
||||
_fileSystem.CreateDirectory(Path.GetDirectoryName(path));
|
||||
|
||||
JsonSerializer.SerializeToFile(triggers.Select(ScheduledTaskHelpers.GetTriggerInfo), path);
|
||||
}
|
||||
|
@ -622,7 +630,7 @@ namespace MediaBrowser.Common.Implementations.ScheduledTasks
|
|||
{
|
||||
try
|
||||
{
|
||||
Logger.Debug(Name + ": Cancelling");
|
||||
Logger.Info(Name + ": Cancelling");
|
||||
token.Cancel();
|
||||
}
|
||||
catch (Exception ex)
|
||||
|
@ -635,16 +643,16 @@ namespace MediaBrowser.Common.Implementations.ScheduledTasks
|
|||
{
|
||||
try
|
||||
{
|
||||
Logger.Debug(Name + ": Waiting on Task");
|
||||
Logger.Info(Name + ": Waiting on Task");
|
||||
var exited = Task.WaitAll(new[] { task }, 2000);
|
||||
|
||||
if (exited)
|
||||
{
|
||||
Logger.Debug(Name + ": Task exited");
|
||||
Logger.Info(Name + ": Task exited");
|
||||
}
|
||||
else
|
||||
{
|
||||
Logger.Debug(Name + ": Timed out waiting for task to stop");
|
||||
Logger.Info(Name + ": Timed out waiting for task to stop");
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
|
|
|
@ -10,6 +10,9 @@ using System.Collections.Concurrent;
|
|||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using CommonIO;
|
||||
using MediaBrowser.Common.IO;
|
||||
using Microsoft.Win32;
|
||||
|
||||
namespace MediaBrowser.Common.Implementations.ScheduledTasks
|
||||
{
|
||||
|
@ -50,6 +53,7 @@ namespace MediaBrowser.Common.Implementations.ScheduledTasks
|
|||
/// </summary>
|
||||
/// <value>The logger.</value>
|
||||
private ILogger Logger { get; set; }
|
||||
private readonly IFileSystem _fileSystem;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="TaskManager" /> class.
|
||||
|
@ -58,13 +62,36 @@ namespace MediaBrowser.Common.Implementations.ScheduledTasks
|
|||
/// <param name="jsonSerializer">The json serializer.</param>
|
||||
/// <param name="logger">The logger.</param>
|
||||
/// <exception cref="System.ArgumentException">kernel</exception>
|
||||
public TaskManager(IApplicationPaths applicationPaths, IJsonSerializer jsonSerializer, ILogger logger)
|
||||
public TaskManager(IApplicationPaths applicationPaths, IJsonSerializer jsonSerializer, ILogger logger, IFileSystem fileSystem)
|
||||
{
|
||||
ApplicationPaths = applicationPaths;
|
||||
JsonSerializer = jsonSerializer;
|
||||
Logger = logger;
|
||||
_fileSystem = fileSystem;
|
||||
|
||||
ScheduledTasks = new IScheduledTaskWorker[] { };
|
||||
|
||||
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>
|
||||
|
@ -106,9 +133,16 @@ namespace MediaBrowser.Common.Implementations.ScheduledTasks
|
|||
public void QueueScheduledTask<T>(TaskExecutionOptions options)
|
||||
where T : IScheduledTask
|
||||
{
|
||||
var scheduledTask = ScheduledTasks.First(t => t.ScheduledTask.GetType() == typeof(T));
|
||||
var scheduledTask = ScheduledTasks.FirstOrDefault(t => t.ScheduledTask.GetType() == typeof(T));
|
||||
|
||||
QueueScheduledTask(scheduledTask, options);
|
||||
if (scheduledTask == null)
|
||||
{
|
||||
Logger.Error("Unable to find scheduled task of type {0} in QueueScheduledTask.", typeof(T).Name);
|
||||
}
|
||||
else
|
||||
{
|
||||
QueueScheduledTask(scheduledTask, options);
|
||||
}
|
||||
}
|
||||
|
||||
public void QueueScheduledTask<T>()
|
||||
|
@ -116,7 +150,7 @@ namespace MediaBrowser.Common.Implementations.ScheduledTasks
|
|||
{
|
||||
QueueScheduledTask<T>(new TaskExecutionOptions());
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Queues the scheduled task.
|
||||
/// </summary>
|
||||
|
@ -124,9 +158,16 @@ namespace MediaBrowser.Common.Implementations.ScheduledTasks
|
|||
/// <param name="options">The task options.</param>
|
||||
public void QueueScheduledTask(IScheduledTask task, TaskExecutionOptions options)
|
||||
{
|
||||
var scheduledTask = ScheduledTasks.First(t => t.ScheduledTask.GetType() == task.GetType());
|
||||
var scheduledTask = ScheduledTasks.FirstOrDefault(t => t.ScheduledTask.GetType() == task.GetType());
|
||||
|
||||
QueueScheduledTask(scheduledTask, options);
|
||||
if (scheduledTask == null)
|
||||
{
|
||||
Logger.Error("Unable to find scheduled task of type {0} in QueueScheduledTask.", task.GetType().Name);
|
||||
}
|
||||
else
|
||||
{
|
||||
QueueScheduledTask(scheduledTask, options);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -161,7 +202,7 @@ namespace MediaBrowser.Common.Implementations.ScheduledTasks
|
|||
var myTasks = ScheduledTasks.ToList();
|
||||
|
||||
var list = tasks.ToList();
|
||||
myTasks.AddRange(list.Select(t => new ScheduledTaskWorker(t, ApplicationPaths, this, JsonSerializer, Logger)));
|
||||
myTasks.AddRange(list.Select(t => new ScheduledTaskWorker(t, ApplicationPaths, this, JsonSerializer, Logger, _fileSystem)));
|
||||
|
||||
ScheduledTasks = myTasks.ToArray();
|
||||
}
|
||||
|
|
|
@ -8,6 +8,7 @@ using System.IO;
|
|||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using CommonIO;
|
||||
|
||||
namespace MediaBrowser.Common.Implementations.ScheduledTasks.Tasks
|
||||
{
|
||||
|
@ -95,7 +96,7 @@ namespace MediaBrowser.Common.Implementations.ScheduledTasks.Tasks
|
|||
/// <param name="progress">The progress.</param>
|
||||
private void DeleteCacheFilesFromDirectory(CancellationToken cancellationToken, string directory, DateTime minDateModified, IProgress<double> progress)
|
||||
{
|
||||
var filesToDelete = new DirectoryInfo(directory).EnumerateFiles("*", SearchOption.AllDirectories)
|
||||
var filesToDelete = _fileSystem.GetFiles(directory, true)
|
||||
.Where(f => _fileSystem.GetLastWriteTimeUtc(f) < minDateModified)
|
||||
.ToList();
|
||||
|
||||
|
@ -120,14 +121,14 @@ namespace MediaBrowser.Common.Implementations.ScheduledTasks.Tasks
|
|||
progress.Report(100);
|
||||
}
|
||||
|
||||
private static void DeleteEmptyFolders(string parent)
|
||||
private void DeleteEmptyFolders(string parent)
|
||||
{
|
||||
foreach (var directory in Directory.GetDirectories(parent))
|
||||
foreach (var directory in _fileSystem.GetDirectoryPaths(parent))
|
||||
{
|
||||
DeleteEmptyFolders(directory);
|
||||
if (!Directory.EnumerateFileSystemEntries(directory).Any())
|
||||
if (!_fileSystem.GetFileSystemEntryPaths(directory).Any())
|
||||
{
|
||||
Directory.Delete(directory, false);
|
||||
_fileSystem.DeleteDirectory(directory, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,6 +7,7 @@ using System.IO;
|
|||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using CommonIO;
|
||||
|
||||
namespace MediaBrowser.Common.Implementations.ScheduledTasks.Tasks
|
||||
{
|
||||
|
@ -58,7 +59,7 @@ namespace MediaBrowser.Common.Implementations.ScheduledTasks.Tasks
|
|||
// Delete log files more than n days old
|
||||
var minDateModified = DateTime.UtcNow.AddDays(-(ConfigurationManager.CommonConfiguration.LogFileRetentionDays));
|
||||
|
||||
var filesToDelete = new DirectoryInfo(ConfigurationManager.CommonApplicationPaths.LogDirectoryPath).EnumerateFileSystemInfos("*", SearchOption.AllDirectories)
|
||||
var filesToDelete = _fileSystem.GetFiles(ConfigurationManager.CommonApplicationPaths.LogDirectoryPath, true)
|
||||
.Where(f => _fileSystem.GetLastWriteTimeUtc(f) < minDateModified)
|
||||
.ToList();
|
||||
|
||||
|
|
|
@ -99,7 +99,7 @@ namespace MediaBrowser.Common.Implementations.Security
|
|||
{
|
||||
try
|
||||
{
|
||||
contents = File.ReadAllLines(licenseFile);
|
||||
contents = File.ReadAllLines(licenseFile);
|
||||
}
|
||||
catch (DirectoryNotFoundException)
|
||||
{
|
||||
|
@ -107,7 +107,7 @@ namespace MediaBrowser.Common.Implementations.Security
|
|||
}
|
||||
catch (FileNotFoundException)
|
||||
{
|
||||
(File.Create(licenseFile)).Close();
|
||||
(File.Create(licenseFile)).Close();
|
||||
}
|
||||
}
|
||||
if (contents != null && contents.Length > 0)
|
||||
|
@ -150,8 +150,8 @@ namespace MediaBrowser.Common.Implementations.Security
|
|||
}
|
||||
|
||||
var licenseFile = Filename;
|
||||
Directory.CreateDirectory(Path.GetDirectoryName(licenseFile));
|
||||
lock (_fileLock) File.WriteAllLines(licenseFile, lines);
|
||||
Directory.CreateDirectory(Path.GetDirectoryName(licenseFile));
|
||||
lock (_fileLock) File.WriteAllLines(licenseFile, lines);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
using MediaBrowser.Common.Configuration;
|
||||
using System.IO;
|
||||
using MediaBrowser.Common.Configuration;
|
||||
using MediaBrowser.Common.Net;
|
||||
using MediaBrowser.Common.Security;
|
||||
using MediaBrowser.Model.Entities;
|
||||
|
@ -18,6 +19,7 @@ namespace MediaBrowser.Common.Implementations.Security
|
|||
public class PluginSecurityManager : ISecurityManager
|
||||
{
|
||||
private const string MBValidateUrl = MbAdmin.HttpsUrl + "service/registration/validate";
|
||||
private const string AppstoreRegUrl = /*MbAdmin.HttpsUrl*/ "http://mb3admin.com/admin/" + "service/appstore/register";
|
||||
|
||||
/// <summary>
|
||||
/// 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,
|
||||
string mb2Equivalent = null,
|
||||
string version = null)
|
||||
|
|
|
@ -7,5 +7,6 @@ namespace MediaBrowser.Common.Implementations.Security
|
|||
public string featId { get; set; }
|
||||
public bool registered { 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.IO;
|
||||
using CommonIO;
|
||||
|
||||
namespace MediaBrowser.Common.Implementations.Serialization
|
||||
{
|
||||
|
@ -9,8 +11,11 @@ namespace MediaBrowser.Common.Implementations.Serialization
|
|||
/// </summary>
|
||||
public class JsonSerializer : IJsonSerializer
|
||||
{
|
||||
public JsonSerializer()
|
||||
private readonly IFileSystem _fileSystem;
|
||||
|
||||
public JsonSerializer(IFileSystem fileSystem)
|
||||
{
|
||||
_fileSystem = fileSystem;
|
||||
Configure();
|
||||
}
|
||||
|
||||
|
@ -53,7 +58,7 @@ namespace MediaBrowser.Common.Implementations.Serialization
|
|||
throw new ArgumentNullException("file");
|
||||
}
|
||||
|
||||
using (Stream stream = File.Open(file, FileMode.Create))
|
||||
using (Stream stream = _fileSystem.GetFileStream(file, FileMode.Create, FileAccess.Write, FileShare.Read))
|
||||
{
|
||||
SerializeToStream(obj, stream);
|
||||
}
|
||||
|
|
|
@ -3,6 +3,8 @@ using System;
|
|||
using System.Collections.Concurrent;
|
||||
using System.IO;
|
||||
using System.Xml;
|
||||
using CommonIO;
|
||||
using MediaBrowser.Common.IO;
|
||||
|
||||
namespace MediaBrowser.Common.Implementations.Serialization
|
||||
{
|
||||
|
@ -11,6 +13,13 @@ namespace MediaBrowser.Common.Implementations.Serialization
|
|||
/// </summary>
|
||||
public class XmlSerializer : IXmlSerializer
|
||||
{
|
||||
private IFileSystem _fileSystem;
|
||||
|
||||
public XmlSerializer(IFileSystem fileSystem)
|
||||
{
|
||||
_fileSystem = fileSystem;
|
||||
}
|
||||
|
||||
// Need to cache these
|
||||
// http://dotnetcodebox.blogspot.com/2013/01/xmlserializer-class-may-result-in.html
|
||||
private readonly ConcurrentDictionary<string, System.Xml.Serialization.XmlSerializer> _serializers =
|
||||
|
@ -83,7 +92,7 @@ namespace MediaBrowser.Common.Implementations.Serialization
|
|||
/// <returns>System.Object.</returns>
|
||||
public object DeserializeFromFile(Type type, string file)
|
||||
{
|
||||
using (var stream = File.OpenRead(file))
|
||||
using (var stream = _fileSystem.OpenRead(file))
|
||||
{
|
||||
return DeserializeFromStream(type, stream);
|
||||
}
|
||||
|
|
|
@ -19,6 +19,7 @@ using System.Linq;
|
|||
using System.Security.Cryptography;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using CommonIO;
|
||||
|
||||
namespace MediaBrowser.Common.Implementations.Updates
|
||||
{
|
||||
|
@ -553,7 +554,7 @@ namespace MediaBrowser.Common.Implementations.Updates
|
|||
if (packageChecksum != Guid.Empty) // support for legacy uploads for now
|
||||
{
|
||||
using (var crypto = new MD5CryptoServiceProvider())
|
||||
using (var stream = new BufferedStream(File.OpenRead(tempFile), 100000))
|
||||
using (var stream = new BufferedStream(_fileSystem.OpenRead(tempFile), 100000))
|
||||
{
|
||||
var check = Guid.Parse(BitConverter.ToString(crypto.ComputeHash(stream)).Replace("-", String.Empty));
|
||||
if (check != packageChecksum)
|
||||
|
@ -568,12 +569,12 @@ namespace MediaBrowser.Common.Implementations.Updates
|
|||
// Success - move it to the real target
|
||||
try
|
||||
{
|
||||
Directory.CreateDirectory(Path.GetDirectoryName(target));
|
||||
File.Copy(tempFile, target, true);
|
||||
_fileSystem.CreateDirectory(Path.GetDirectoryName(target));
|
||||
_fileSystem.CopyFile(tempFile, target, true);
|
||||
//If it is an archive - write out a version file so we know what it is
|
||||
if (isArchive)
|
||||
{
|
||||
File.WriteAllText(target + ".ver", package.versionStr);
|
||||
File.WriteAllText(target + ".ver", package.versionStr);
|
||||
}
|
||||
}
|
||||
catch (IOException e)
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<packages>
|
||||
<package id="NLog" version="3.2.1" targetFramework="net45" />
|
||||
<package id="SimpleInjector" version="2.8.0" targetFramework="net45" />
|
||||
<package id="CommonIO" version="1.0.0.5" 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>
|
||||
|
|
|
@ -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\ResourceNotFoundException.cs" />
|
||||
<Compile Include="IDependencyContainer.cs" />
|
||||
<Compile Include="IO\IFileSystem.cs" />
|
||||
<Compile Include="IO\ProgressStream.cs" />
|
||||
<Compile Include="IO\StreamDefaults.cs" />
|
||||
<Compile Include="Configuration\IApplicationPaths.cs" />
|
||||
|
|
|
@ -87,6 +87,7 @@ namespace MediaBrowser.Common.Net
|
|||
public bool BufferContent { get; set; }
|
||||
|
||||
public bool LogRequest { get; set; }
|
||||
public bool LogErrors { get; set; }
|
||||
|
||||
public bool LogErrorResponseBody { get; set; }
|
||||
public bool EnableKeepAlive { get; set; }
|
||||
|
@ -116,6 +117,7 @@ namespace MediaBrowser.Common.Net
|
|||
RequestHeaders = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
|
||||
|
||||
LogRequest = true;
|
||||
LogErrors = true;
|
||||
CacheMode = CacheMode.None;
|
||||
|
||||
TimeoutMs = 20000;
|
||||
|
|
|
@ -69,5 +69,10 @@ namespace MediaBrowser.Common.ScheduledTasks
|
|||
/// </summary>
|
||||
/// <value>The unique id.</value>
|
||||
string Id { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Reloads the trigger events.
|
||||
/// </summary>
|
||||
void ReloadTriggerEvents();
|
||||
}
|
||||
}
|
|
@ -51,8 +51,8 @@ namespace MediaBrowser.Common.ScheduledTasks
|
|||
DisposeTimer();
|
||||
|
||||
var triggerDate = lastResult != null ?
|
||||
lastResult.EndTimeUtc.Add(Interval) :
|
||||
DateTime.UtcNow.Add(FirstRunDelay);
|
||||
lastResult.EndTimeUtc.Add(Interval) :
|
||||
DateTime.UtcNow.Add(FirstRunDelay);
|
||||
|
||||
if (DateTime.UtcNow > triggerDate)
|
||||
{
|
||||
|
@ -62,7 +62,7 @@ namespace MediaBrowser.Common.ScheduledTasks
|
|||
}
|
||||
else
|
||||
{
|
||||
triggerDate = DateTime.UtcNow.Add(Interval);
|
||||
triggerDate = DateTime.UtcNow.AddMinutes(1);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
using System;
|
||||
using MediaBrowser.Model.Entities;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
|
@ -45,5 +46,11 @@ namespace MediaBrowser.Common.Security
|
|||
/// </summary>
|
||||
/// <returns>Task<SupporterInfo>.</returns>
|
||||
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 string OriginalChannelName { get; set; }
|
||||
|
||||
public override bool IsVisible(User user)
|
||||
{
|
||||
if (user.Policy.BlockedChannels != null)
|
||||
|
|
|
@ -7,24 +7,15 @@ using MediaBrowser.Model.Entities;
|
|||
using MediaBrowser.Model.Users;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Runtime.Serialization;
|
||||
using System.Threading;
|
||||
|
||||
namespace MediaBrowser.Controller.Channels
|
||||
{
|
||||
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 string OriginalImageUrl { get; set; }
|
||||
|
||||
public List<ChannelMediaInfo> ChannelMediaSources { get; set; }
|
||||
|
||||
protected override bool GetBlockUnratedValue(UserPolicy config)
|
||||
|
@ -37,6 +28,7 @@ namespace MediaBrowser.Controller.Channels
|
|||
return ExternalId;
|
||||
}
|
||||
|
||||
[IgnoreDataMember]
|
||||
public override bool SupportsLocalMetadata
|
||||
{
|
||||
get
|
||||
|
@ -55,6 +47,7 @@ namespace MediaBrowser.Controller.Channels
|
|||
ChannelMediaSources = new List<ChannelMediaInfo>();
|
||||
}
|
||||
|
||||
[IgnoreDataMember]
|
||||
public override LocationType LocationType
|
||||
{
|
||||
get
|
||||
|
|
|
@ -3,28 +3,24 @@ using MediaBrowser.Model.Channels;
|
|||
using MediaBrowser.Model.Querying;
|
||||
using MediaBrowser.Model.Users;
|
||||
using System;
|
||||
using System.Runtime.Serialization;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using MediaBrowser.Model.Entities;
|
||||
|
||||
namespace MediaBrowser.Controller.Channels
|
||||
{
|
||||
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 string OriginalImageUrl { get; set; }
|
||||
|
||||
protected override bool GetBlockUnratedValue(UserPolicy config)
|
||||
{
|
||||
// Don't block.
|
||||
return false;
|
||||
}
|
||||
|
||||
[IgnoreDataMember]
|
||||
public override bool SupportsLocalMetadata
|
||||
{
|
||||
get
|
||||
|
|
|
@ -8,24 +8,15 @@ using MediaBrowser.Model.Users;
|
|||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Runtime.Serialization;
|
||||
using System.Threading;
|
||||
|
||||
namespace MediaBrowser.Controller.Channels
|
||||
{
|
||||
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 string OriginalImageUrl { get; set; }
|
||||
|
||||
public List<ChannelMediaInfo> ChannelMediaSources { get; set; }
|
||||
|
||||
protected override string CreateUserDataKey()
|
||||
|
@ -56,6 +47,7 @@ namespace MediaBrowser.Controller.Channels
|
|||
return config.BlockUnratedItems.Contains(UnratedItem.ChannelContent);
|
||||
}
|
||||
|
||||
[IgnoreDataMember]
|
||||
public override bool SupportsLocalMetadata
|
||||
{
|
||||
get
|
||||
|
@ -74,6 +66,7 @@ namespace MediaBrowser.Controller.Channels
|
|||
ChannelMediaSources = new List<ChannelMediaInfo>();
|
||||
}
|
||||
|
||||
[IgnoreDataMember]
|
||||
public override LocationType LocationType
|
||||
{
|
||||
get
|
||||
|
@ -115,7 +108,11 @@ namespace MediaBrowser.Controller.Channels
|
|||
var info = GetItemLookupInfo<ChannelItemLookupInfo>();
|
||||
|
||||
info.ContentType = ContentType;
|
||||
info.ExtraType = ExtraType;
|
||||
|
||||
if (ExtraType.HasValue)
|
||||
{
|
||||
info.ExtraType = ExtraType.Value;
|
||||
}
|
||||
|
||||
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 ExternalId { get; set; }
|
||||
|
||||
ChannelItemType ChannelItemType { get; set; }
|
||||
|
||||
string OriginalImageUrl { get; set; }
|
||||
|
||||
string DataVersion { get; set; }
|
||||
}
|
||||
}
|
||||
|
|
|
@ -16,7 +16,7 @@ namespace MediaBrowser.Controller.Channels
|
|||
/// </summary>
|
||||
/// <param name="channels">The channels.</param>
|
||||
/// <param name="factories">The factories.</param>
|
||||
void AddParts(IEnumerable<IChannel> channels, IEnumerable<IChannelFactory> factories);
|
||||
void AddParts(IEnumerable<IChannel> channels);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the channel download path.
|
||||
|
|
|
@ -11,7 +11,7 @@ namespace MediaBrowser.Controller.Channels
|
|||
|
||||
ChannelMediaContentType ContentType { get; set; }
|
||||
|
||||
ExtraType ExtraType { get; set; }
|
||||
ExtraType? ExtraType { get; set; }
|
||||
|
||||
List<ChannelMediaInfo> ChannelMediaSources { get; set; }
|
||||
}
|
||||
|
|
|
@ -25,13 +25,6 @@ namespace MediaBrowser.Controller.Drawing
|
|||
/// <value>The image enhancers.</value>
|
||||
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>
|
||||
/// Gets the size of the image.
|
||||
/// </summary>
|
||||
|
@ -39,6 +32,13 @@ namespace MediaBrowser.Controller.Drawing
|
|||
/// <returns>ImageSize.</returns>
|
||||
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>
|
||||
/// Adds the parts.
|
||||
/// </summary>
|
||||
|
@ -105,5 +105,11 @@ namespace MediaBrowser.Controller.Drawing
|
|||
/// </summary>
|
||||
/// <param name="options">The options.</param>
|
||||
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.Linq;
|
||||
using System.Runtime.Serialization;
|
||||
using CommonIO;
|
||||
using MediaBrowser.Common.IO;
|
||||
using MediaBrowser.Controller.Providers;
|
||||
|
||||
namespace MediaBrowser.Controller.Entities
|
||||
|
@ -62,7 +64,7 @@ namespace MediaBrowser.Controller.Entities
|
|||
|
||||
public List<string> PhysicalLocationsList { get; set; }
|
||||
|
||||
protected override IEnumerable<FileSystemInfo> GetFileSystemChildren(IDirectoryService directoryService)
|
||||
protected override IEnumerable<FileSystemMetadata> GetFileSystemChildren(IDirectoryService directoryService)
|
||||
{
|
||||
return CreateResolveArgs(directoryService).FileSystemChildren;
|
||||
}
|
||||
|
@ -73,7 +75,7 @@ namespace MediaBrowser.Controller.Entities
|
|||
|
||||
var args = new ItemResolveArgs(ConfigurationManager.ApplicationPaths , directoryService)
|
||||
{
|
||||
FileInfo = new DirectoryInfo(path),
|
||||
FileInfo = FileSystem.GetDirectoryInfo(path),
|
||||
Path = path,
|
||||
Parent = Parent
|
||||
};
|
||||
|
@ -94,7 +96,7 @@ namespace MediaBrowser.Controller.Entities
|
|||
{
|
||||
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;
|
||||
|
|
|
@ -24,14 +24,20 @@ namespace MediaBrowser.Controller.Entities.Audio
|
|||
IThemeMedia,
|
||||
IArchivable
|
||||
{
|
||||
public string FormatName { get; set; }
|
||||
public long? Size { get; set; }
|
||||
public string Container { get; set; }
|
||||
public int? TotalBitrate { get; set; }
|
||||
public List<string> Tags { get; set; }
|
||||
public ExtraType ExtraType { get; set; }
|
||||
public ExtraType? ExtraType { get; set; }
|
||||
|
||||
public bool IsThemeMedia { get; set; }
|
||||
[IgnoreDataMember]
|
||||
public bool IsThemeMedia
|
||||
{
|
||||
get
|
||||
{
|
||||
return ExtraType.HasValue && ExtraType.Value == Model.Entities.ExtraType.ThemeSong;
|
||||
}
|
||||
}
|
||||
|
||||
public Audio()
|
||||
{
|
||||
|
@ -46,12 +52,6 @@ namespace MediaBrowser.Controller.Entities.Audio
|
|||
get { return LocationType == LocationType.FileSystem && RunTimeTicks.HasValue; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether this instance has embedded image.
|
||||
/// </summary>
|
||||
/// <value><c>true</c> if this instance has embedded image; otherwise, <c>false</c>.</value>
|
||||
public bool HasEmbeddedImage { get; set; }
|
||||
|
||||
[IgnoreDataMember]
|
||||
protected override bool SupportsOwnedItems
|
||||
{
|
||||
|
@ -212,8 +212,7 @@ namespace MediaBrowser.Controller.Entities.Audio
|
|||
Path = enablePathSubstituion ? GetMappedPath(i.Path, locationType) : i.Path,
|
||||
RunTimeTicks = i.RunTimeTicks,
|
||||
Container = i.Container,
|
||||
Size = i.Size,
|
||||
Formats = (i.FormatName ?? string.Empty).Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries).ToList()
|
||||
Size = i.Size
|
||||
};
|
||||
|
||||
if (string.IsNullOrEmpty(info.Container))
|
||||
|
|
|
@ -23,6 +23,7 @@ using System.Linq;
|
|||
using System.Runtime.Serialization;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using CommonIO;
|
||||
|
||||
namespace MediaBrowser.Controller.Entities
|
||||
{
|
||||
|
@ -38,7 +39,6 @@ namespace MediaBrowser.Controller.Entities
|
|||
ProviderIds = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
|
||||
LockedFields = new List<MetadataFields>();
|
||||
ImageInfos = new List<ItemImageInfo>();
|
||||
Identities = new List<IItemIdentity>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -56,12 +56,16 @@ namespace MediaBrowser.Controller.Entities
|
|||
public static string ThemeSongFilename = "theme";
|
||||
public static string ThemeVideosFolderName = "backdrops";
|
||||
|
||||
public string PreferredMetadataCountryCode { get; set; }
|
||||
public string PreferredMetadataLanguage { get; set; }
|
||||
|
||||
public List<ItemImageInfo> ImageInfos { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the channel identifier.
|
||||
/// </summary>
|
||||
/// <value>The channel identifier.</value>
|
||||
[IgnoreDataMember]
|
||||
public string ChannelId { get; set; }
|
||||
|
||||
[IgnoreDataMember]
|
||||
|
@ -120,6 +124,12 @@ namespace MediaBrowser.Controller.Entities
|
|||
/// <value>The id.</value>
|
||||
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>
|
||||
/// 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.
|
||||
|
@ -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]
|
||||
public virtual bool IsHidden
|
||||
{
|
||||
|
@ -183,7 +213,7 @@ namespace MediaBrowser.Controller.Entities
|
|||
{
|
||||
// Local trailer, special feature, theme video, etc.
|
||||
// An item that belongs to another item but is not part of the Parent-Child tree
|
||||
return !IsFolder && Parent == null && LocationType == LocationType.FileSystem;
|
||||
return !IsFolder && ParentId == Guid.Empty && LocationType == LocationType.FileSystem;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -305,6 +335,9 @@ namespace MediaBrowser.Controller.Entities
|
|||
|
||||
public DateTime DateLastSaved { get; set; }
|
||||
|
||||
[IgnoreDataMember]
|
||||
public DateTime DateLastRefreshed { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The logger
|
||||
/// </summary>
|
||||
|
@ -331,30 +364,8 @@ namespace MediaBrowser.Controller.Entities
|
|||
return Name;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns true if this item should not attempt to fetch metadata
|
||||
/// </summary>
|
||||
/// <value><c>true</c> if [dont fetch meta]; otherwise, <c>false</c>.</value>
|
||||
[Obsolete("Please use IsLocked instead of DontFetchMeta")]
|
||||
public bool DontFetchMeta { get; set; }
|
||||
|
||||
[IgnoreDataMember]
|
||||
public bool IsLocked
|
||||
{
|
||||
get
|
||||
{
|
||||
return DontFetchMeta;
|
||||
}
|
||||
set
|
||||
{
|
||||
DontFetchMeta = value;
|
||||
}
|
||||
}
|
||||
|
||||
public bool IsUnidentified { get; set; }
|
||||
|
||||
[IgnoreDataMember]
|
||||
public List<IItemIdentity> Identities { get; set; }
|
||||
public bool IsLocked { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the locked fields.
|
||||
|
@ -484,7 +495,6 @@ namespace MediaBrowser.Controller.Entities
|
|||
|
||||
public Guid ParentId { get; set; }
|
||||
|
||||
private Folder _parent;
|
||||
/// <summary>
|
||||
/// Gets or sets the parent.
|
||||
/// </summary>
|
||||
|
@ -494,11 +504,6 @@ namespace MediaBrowser.Controller.Entities
|
|||
{
|
||||
get
|
||||
{
|
||||
if (_parent != null)
|
||||
{
|
||||
return _parent;
|
||||
}
|
||||
|
||||
if (ParentId != Guid.Empty)
|
||||
{
|
||||
return LibraryManager.GetItemById(ParentId) as Folder;
|
||||
|
@ -506,12 +511,14 @@ namespace MediaBrowser.Controller.Entities
|
|||
|
||||
return null;
|
||||
}
|
||||
set { _parent = value; }
|
||||
set
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
public void SetParent(Folder parent)
|
||||
{
|
||||
Parent = parent;
|
||||
ParentId = parent == null ? Guid.Empty : parent.Id;
|
||||
}
|
||||
|
||||
|
@ -558,6 +565,7 @@ namespace MediaBrowser.Controller.Entities
|
|||
/// Gets or sets the end date.
|
||||
/// </summary>
|
||||
/// <value>The end date.</value>
|
||||
[IgnoreDataMember]
|
||||
public DateTime? EndDate { get; set; }
|
||||
|
||||
/// <summary>
|
||||
|
@ -582,6 +590,7 @@ namespace MediaBrowser.Controller.Entities
|
|||
/// Gets or sets the custom rating.
|
||||
/// </summary>
|
||||
/// <value>The custom rating.</value>
|
||||
//[IgnoreDataMember]
|
||||
public string CustomRating { get; set; }
|
||||
|
||||
/// <summary>
|
||||
|
@ -590,12 +599,6 @@ namespace MediaBrowser.Controller.Entities
|
|||
/// <value>The overview.</value>
|
||||
public string Overview { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the people.
|
||||
/// </summary>
|
||||
/// <value>The people.</value>
|
||||
public List<PersonInfo> People { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the studios.
|
||||
/// </summary>
|
||||
|
@ -618,6 +621,7 @@ namespace MediaBrowser.Controller.Entities
|
|||
/// Gets or sets the community rating.
|
||||
/// </summary>
|
||||
/// <value>The community rating.</value>
|
||||
//[IgnoreDataMember]
|
||||
public float? CommunityRating { get; set; }
|
||||
|
||||
/// <summary>
|
||||
|
@ -643,6 +647,7 @@ namespace MediaBrowser.Controller.Entities
|
|||
/// This could be episode number, album track number, etc.
|
||||
/// </summary>
|
||||
/// <value>The index number.</value>
|
||||
//[IgnoreDataMember]
|
||||
public int? IndexNumber { get; set; }
|
||||
|
||||
/// <summary>
|
||||
|
@ -662,7 +667,7 @@ namespace MediaBrowser.Controller.Entities
|
|||
{
|
||||
get
|
||||
{
|
||||
if (!string.IsNullOrEmpty(CustomRating))
|
||||
if (!string.IsNullOrWhiteSpace(CustomRating))
|
||||
{
|
||||
return CustomRating;
|
||||
}
|
||||
|
@ -701,16 +706,16 @@ namespace MediaBrowser.Controller.Entities
|
|||
/// Loads the theme songs.
|
||||
/// </summary>
|
||||
/// <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))
|
||||
.SelectMany(i => i.EnumerateFiles("*", SearchOption.TopDirectoryOnly))
|
||||
.SelectMany(i => directoryService.GetFiles(i.FullName))
|
||||
.ToList();
|
||||
|
||||
// Support plex/xbmc convention
|
||||
files.AddRange(fileSystemChildren.OfType<FileInfo>()
|
||||
.Where(i => string.Equals(FileSystem.GetFileNameWithoutExtension(i), ThemeSongFilename, StringComparison.OrdinalIgnoreCase))
|
||||
files.AddRange(fileSystemChildren
|
||||
.Where(i => !i.IsDirectory && string.Equals(FileSystem.GetFileNameWithoutExtension(i), ThemeSongFilename, StringComparison.OrdinalIgnoreCase))
|
||||
);
|
||||
|
||||
return LibraryManager.ResolvePaths(files, directoryService, null)
|
||||
|
@ -737,11 +742,11 @@ namespace MediaBrowser.Controller.Entities
|
|||
/// Loads the video backdrops.
|
||||
/// </summary>
|
||||
/// <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))
|
||||
.SelectMany(i => i.EnumerateFiles("*", SearchOption.TopDirectoryOnly));
|
||||
.SelectMany(i => directoryService.GetFiles(i.FullName));
|
||||
|
||||
return LibraryManager.ResolvePaths(files, directoryService, null)
|
||||
.OfType<Video>()
|
||||
|
@ -765,7 +770,7 @@ namespace MediaBrowser.Controller.Entities
|
|||
|
||||
public Task RefreshMetadata(CancellationToken cancellationToken)
|
||||
{
|
||||
return RefreshMetadata(new MetadataRefreshOptions(new DirectoryService()), cancellationToken);
|
||||
return RefreshMetadata(new MetadataRefreshOptions(new DirectoryService(FileSystem)), cancellationToken);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -786,7 +791,7 @@ namespace MediaBrowser.Controller.Entities
|
|||
{
|
||||
var files = locationType != LocationType.Remote && locationType != LocationType.Virtual ?
|
||||
GetFileSystemChildren(options.DirectoryService).ToList() :
|
||||
new List<FileSystemInfo>();
|
||||
new List<FileSystemMetadata>();
|
||||
|
||||
var ownedItemsChanged = await RefreshedOwnedItems(options, files, cancellationToken).ConfigureAwait(false);
|
||||
|
||||
|
@ -833,7 +838,7 @@ namespace MediaBrowser.Controller.Entities
|
|||
/// <param name="fileSystemChildren"></param>
|
||||
/// <param name="cancellationToken"></param>
|
||||
/// <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;
|
||||
|
||||
|
@ -864,14 +869,14 @@ namespace MediaBrowser.Controller.Entities
|
|||
return themeSongsChanged || themeVideosChanged || localTrailersChanged;
|
||||
}
|
||||
|
||||
protected virtual IEnumerable<FileSystemInfo> GetFileSystemChildren(IDirectoryService directoryService)
|
||||
protected virtual IEnumerable<FileSystemMetadata> GetFileSystemChildren(IDirectoryService directoryService)
|
||||
{
|
||||
var path = ContainingFolderPath;
|
||||
|
||||
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();
|
||||
|
||||
|
@ -888,7 +893,7 @@ namespace MediaBrowser.Controller.Entities
|
|||
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();
|
||||
|
||||
|
@ -902,7 +907,7 @@ namespace MediaBrowser.Controller.Entities
|
|||
|
||||
if (!i.IsThemeMedia)
|
||||
{
|
||||
i.IsThemeMedia = true;
|
||||
i.ExtraType = ExtraType.ThemeVideo;
|
||||
subOptions.ForceSave = true;
|
||||
}
|
||||
|
||||
|
@ -919,7 +924,7 @@ namespace MediaBrowser.Controller.Entities
|
|||
/// <summary>
|
||||
/// Refreshes the theme songs.
|
||||
/// </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 newThemeSongIds = newThemeSongs.Select(i => i.Id).ToList();
|
||||
|
@ -932,7 +937,7 @@ namespace MediaBrowser.Controller.Entities
|
|||
|
||||
if (!i.IsThemeMedia)
|
||||
{
|
||||
i.IsThemeMedia = true;
|
||||
i.ExtraType = ExtraType.ThemeSong;
|
||||
subOptions.ForceSave = true;
|
||||
}
|
||||
|
||||
|
@ -999,18 +1004,11 @@ namespace MediaBrowser.Controller.Entities
|
|||
/// <returns>System.String.</returns>
|
||||
public string GetPreferredMetadataLanguage()
|
||||
{
|
||||
string lang = null;
|
||||
|
||||
var hasLang = this as IHasPreferredMetadataLanguage;
|
||||
|
||||
if (hasLang != null)
|
||||
{
|
||||
lang = hasLang.PreferredMetadataLanguage;
|
||||
}
|
||||
string lang = PreferredMetadataLanguage;
|
||||
|
||||
if (string.IsNullOrWhiteSpace(lang))
|
||||
{
|
||||
lang = Parents.OfType<IHasPreferredMetadataLanguage>()
|
||||
lang = Parents
|
||||
.Select(i => i.PreferredMetadataLanguage)
|
||||
.FirstOrDefault(i => !string.IsNullOrWhiteSpace(i));
|
||||
}
|
||||
|
@ -1036,18 +1034,11 @@ namespace MediaBrowser.Controller.Entities
|
|||
/// <returns>System.String.</returns>
|
||||
public string GetPreferredMetadataCountryCode()
|
||||
{
|
||||
string lang = null;
|
||||
|
||||
var hasLang = this as IHasPreferredMetadataLanguage;
|
||||
|
||||
if (hasLang != null)
|
||||
{
|
||||
lang = hasLang.PreferredMetadataCountryCode;
|
||||
}
|
||||
string lang = PreferredMetadataCountryCode;
|
||||
|
||||
if (string.IsNullOrWhiteSpace(lang))
|
||||
{
|
||||
lang = Parents.OfType<IHasPreferredMetadataLanguage>()
|
||||
lang = Parents
|
||||
.Select(i => i.PreferredMetadataCountryCode)
|
||||
.FirstOrDefault(i => !string.IsNullOrWhiteSpace(i));
|
||||
}
|
||||
|
@ -1114,7 +1105,14 @@ namespace MediaBrowser.Controller.Entities
|
|||
// Could not determine the integer value
|
||||
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;
|
||||
|
@ -1165,6 +1163,17 @@ namespace MediaBrowser.Controller.Entities
|
|||
/// <returns><c>true</c> if XXXX, <c>false</c> otherwise.</returns>
|
||||
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);
|
||||
}
|
||||
|
||||
|
@ -1418,7 +1427,7 @@ namespace MediaBrowser.Controller.Entities
|
|||
/// <returns>Task.</returns>
|
||||
public virtual Task ChangedExternally()
|
||||
{
|
||||
ProviderManager.QueueRefresh(Id, new MetadataRefreshOptions());
|
||||
ProviderManager.QueueRefresh(Id, new MetadataRefreshOptions(FileSystem));
|
||||
return Task.FromResult(true);
|
||||
}
|
||||
|
||||
|
@ -1434,7 +1443,24 @@ namespace MediaBrowser.Controller.Entities
|
|||
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)
|
||||
{
|
||||
|
@ -1475,18 +1501,21 @@ namespace MediaBrowser.Controller.Entities
|
|||
// Remove it from the item
|
||||
RemoveImage(info);
|
||||
|
||||
// Delete the source file
|
||||
var currentFile = new FileInfo(info.Path);
|
||||
|
||||
// Deletion will fail if the file is hidden so remove the attribute first
|
||||
if (currentFile.Exists)
|
||||
if (info.IsLocalFile)
|
||||
{
|
||||
if ((currentFile.Attributes & FileAttributes.Hidden) == FileAttributes.Hidden)
|
||||
{
|
||||
currentFile.Attributes &= ~FileAttributes.Hidden;
|
||||
}
|
||||
// Delete the source file
|
||||
var currentFile = new FileInfo(info.Path);
|
||||
|
||||
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);
|
||||
|
@ -1507,11 +1536,16 @@ namespace MediaBrowser.Controller.Entities
|
|||
/// </summary>
|
||||
public bool ValidateImages(IDirectoryService directoryService)
|
||||
{
|
||||
var allDirectories = ImageInfos.Select(i => System.IO.Path.GetDirectoryName(i.Path)).Distinct(StringComparer.OrdinalIgnoreCase).ToList();
|
||||
var allFiles = allDirectories.SelectMany(directoryService.GetFiles).Select(i => i.FullName).ToList();
|
||||
var allFiles = ImageInfos
|
||||
.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
|
||||
.Where(image => !allFiles.Contains(image.Path, StringComparer.OrdinalIgnoreCase))
|
||||
.Where(image => image.IsLocalFile && !allFiles.Contains(image.Path, StringComparer.OrdinalIgnoreCase))
|
||||
.ToList();
|
||||
|
||||
if (deletedImages.Count > 0)
|
||||
|
@ -1584,11 +1618,6 @@ namespace MediaBrowser.Controller.Entities
|
|||
return ImageInfos.Where(i => i.Type == imageType);
|
||||
}
|
||||
|
||||
public bool AddImages(ImageType imageType, IEnumerable<FileInfo> images)
|
||||
{
|
||||
return AddImages(imageType, images.Cast<FileSystemInfo>().ToList());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds the images.
|
||||
/// </summary>
|
||||
|
@ -1596,7 +1625,7 @@ namespace MediaBrowser.Controller.Entities
|
|||
/// <param name="images">The images.</param>
|
||||
/// <returns><c>true</c> if XXXX, <c>false</c> otherwise.</returns>
|
||||
/// <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)
|
||||
{
|
||||
|
@ -1606,7 +1635,7 @@ namespace MediaBrowser.Controller.Entities
|
|||
var existingImages = GetImages(imageType)
|
||||
.ToList();
|
||||
|
||||
var newImageList = new List<FileSystemInfo>();
|
||||
var newImageList = new List<FileSystemMetadata>();
|
||||
var imageAdded = false;
|
||||
|
||||
foreach (var newImage in images)
|
||||
|
@ -1626,7 +1655,10 @@ namespace MediaBrowser.Controller.Entities
|
|||
}
|
||||
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 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();
|
||||
|
||||
ImageInfos = ImageInfos.Except(deleted).ToList();
|
||||
|
@ -1646,7 +1678,7 @@ namespace MediaBrowser.Controller.Entities
|
|||
return newImageList.Count > 0;
|
||||
}
|
||||
|
||||
private ItemImageInfo GetImageInfo(FileSystemInfo file, ImageType type)
|
||||
private ItemImageInfo GetImageInfo(FileSystemMetadata file, ImageType type)
|
||||
{
|
||||
return new ItemImageInfo
|
||||
{
|
||||
|
@ -1686,6 +1718,12 @@ namespace MediaBrowser.Controller.Entities
|
|||
return Task.FromResult(true);
|
||||
}
|
||||
|
||||
if (!info1.IsLocalFile || !info2.IsLocalFile)
|
||||
{
|
||||
// TODO: Not supported yet
|
||||
return Task.FromResult(true);
|
||||
}
|
||||
|
||||
var path1 = info1.Path;
|
||||
var path2 = info2.Path;
|
||||
|
||||
|
@ -1769,7 +1807,7 @@ namespace MediaBrowser.Controller.Entities
|
|||
{
|
||||
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)
|
||||
{
|
||||
video = LibraryManager.ResolvePath(new FileInfo(path)) as Video;
|
||||
video = LibraryManager.ResolvePath(FileSystem.GetFileSystemInfo(path)) as Video;
|
||||
|
||||
newOptions.ForceSave = true;
|
||||
}
|
||||
|
|
|
@ -7,7 +7,7 @@ using MediaBrowser.Model.Users;
|
|||
|
||||
namespace MediaBrowser.Controller.Entities
|
||||
{
|
||||
public class Book : BaseItem, IHasTags, IHasPreferredMetadataLanguage, IHasLookupInfo<BookInfo>, IHasSeries
|
||||
public class Book : BaseItem, IHasTags, IHasLookupInfo<BookInfo>, IHasSeries
|
||||
{
|
||||
public override string MediaType
|
||||
{
|
||||
|
@ -25,14 +25,6 @@ namespace MediaBrowser.Controller.Entities
|
|||
|
||||
public string SeriesName { get; set; }
|
||||
|
||||
public string PreferredMetadataLanguage { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the preferred metadata country code.
|
||||
/// </summary>
|
||||
/// <value>The preferred metadata country code.</value>
|
||||
public string PreferredMetadataCountryCode { get; set; }
|
||||
|
||||
public Book()
|
||||
{
|
||||
Tags = new List<string>();
|
||||
|
|
|
@ -8,6 +8,8 @@ using System.Linq;
|
|||
using System.Runtime.Serialization;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using CommonIO;
|
||||
using MediaBrowser.Common.IO;
|
||||
|
||||
namespace MediaBrowser.Controller.Entities
|
||||
{
|
||||
|
@ -80,7 +82,7 @@ namespace MediaBrowser.Controller.Entities
|
|||
|
||||
public List<string> PhysicalLocationsList { get; set; }
|
||||
|
||||
protected override IEnumerable<FileSystemInfo> GetFileSystemChildren(IDirectoryService directoryService)
|
||||
protected override IEnumerable<FileSystemMetadata> GetFileSystemChildren(IDirectoryService directoryService)
|
||||
{
|
||||
return CreateResolveArgs(directoryService).FileSystemChildren;
|
||||
}
|
||||
|
@ -107,7 +109,7 @@ namespace MediaBrowser.Controller.Entities
|
|||
|
||||
var args = new ItemResolveArgs(ConfigurationManager.ApplicationPaths, directoryService)
|
||||
{
|
||||
FileInfo = new DirectoryInfo(path),
|
||||
FileInfo = FileSystem.GetDirectoryInfo(path),
|
||||
Path = path,
|
||||
Parent = Parent,
|
||||
CollectionType = CollectionType
|
||||
|
@ -129,7 +131,7 @@ namespace MediaBrowser.Controller.Entities
|
|||
{
|
||||
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;
|
||||
|
|
|
@ -13,13 +13,15 @@ using System.Linq;
|
|||
using System.Runtime.Serialization;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using CommonIO;
|
||||
using MediaBrowser.Common.IO;
|
||||
|
||||
namespace MediaBrowser.Controller.Entities
|
||||
{
|
||||
/// <summary>
|
||||
/// Class Folder
|
||||
/// </summary>
|
||||
public class Folder : BaseItem, IHasThemeMedia, IHasTags, IHasPreferredMetadataLanguage
|
||||
public class Folder : BaseItem, IHasThemeMedia, IHasTags
|
||||
{
|
||||
public static IUserManager UserManager { get; set; }
|
||||
public static IUserViewManager UserViewManager { get; set; }
|
||||
|
@ -28,14 +30,6 @@ namespace MediaBrowser.Controller.Entities
|
|||
public List<Guid> ThemeVideoIds { get; set; }
|
||||
public List<string> Tags { get; set; }
|
||||
|
||||
public string PreferredMetadataLanguage { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the preferred metadata country code.
|
||||
/// </summary>
|
||||
/// <value>The preferred metadata country code.</value>
|
||||
public string PreferredMetadataCountryCode { get; set; }
|
||||
|
||||
public Folder()
|
||||
{
|
||||
LinkedChildren = new List<LinkedChild>();
|
||||
|
@ -48,7 +42,7 @@ namespace MediaBrowser.Controller.Entities
|
|||
[IgnoreDataMember]
|
||||
public virtual bool IsPreSorted
|
||||
{
|
||||
get { return ConfigurationManager.Configuration.EnableWindowsShortcuts; }
|
||||
get { return false; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -120,7 +114,7 @@ namespace MediaBrowser.Controller.Entities
|
|||
[IgnoreDataMember]
|
||||
protected virtual bool SupportsShortcutChildren
|
||||
{
|
||||
get { return false; }
|
||||
get { return ConfigurationManager.Configuration.EnableWindowsShortcuts; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -213,7 +207,7 @@ namespace MediaBrowser.Controller.Entities
|
|||
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]
|
||||
public IEnumerable<BaseItem> Children
|
||||
{
|
||||
get { return ActualChildren; }
|
||||
get { return ActualChildren.ToList(); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -371,7 +365,7 @@ namespace MediaBrowser.Controller.Entities
|
|||
|
||||
public Task ValidateChildren(IProgress<double> progress, CancellationToken cancellationToken)
|
||||
{
|
||||
return ValidateChildren(progress, cancellationToken, new MetadataRefreshOptions(new DirectoryService()));
|
||||
return ValidateChildren(progress, cancellationToken, new MetadataRefreshOptions(new DirectoryService(FileSystem)));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -474,7 +468,7 @@ namespace MediaBrowser.Controller.Entities
|
|||
currentChild.DateModified = child.DateModified;
|
||||
}
|
||||
|
||||
currentChild.IsOffline = false;
|
||||
await UpdateIsOffline(currentChild, false).ConfigureAwait(false);
|
||||
validChildren.Add(currentChild);
|
||||
}
|
||||
else
|
||||
|
@ -509,12 +503,12 @@ namespace MediaBrowser.Controller.Entities
|
|||
|
||||
else if (!string.IsNullOrEmpty(item.Path) && IsPathOffline(item.Path))
|
||||
{
|
||||
item.IsOffline = true;
|
||||
await UpdateIsOffline(item, true).ConfigureAwait(false);
|
||||
validChildren.Add(item);
|
||||
}
|
||||
else
|
||||
{
|
||||
item.IsOffline = false;
|
||||
await UpdateIsOffline(item, false).ConfigureAwait(false);
|
||||
actualRemovals.Add(item);
|
||||
}
|
||||
}
|
||||
|
@ -569,6 +563,17 @@ namespace MediaBrowser.Controller.Entities
|
|||
progress.Report(100);
|
||||
}
|
||||
|
||||
private Task UpdateIsOffline(BaseItem item, bool newValue)
|
||||
{
|
||||
if (item.IsOffline != newValue)
|
||||
{
|
||||
item.IsOffline = newValue;
|
||||
return item.UpdateToRepository(ItemUpdateType.None, CancellationToken.None);
|
||||
}
|
||||
|
||||
return Task.FromResult(true);
|
||||
}
|
||||
|
||||
private async Task RefreshMetadataRecursive(MetadataRefreshOptions refreshOptions, bool recursive, IProgress<double> progress, CancellationToken cancellationToken)
|
||||
{
|
||||
var children = ActualChildren.ToList();
|
||||
|
@ -691,9 +696,9 @@ namespace MediaBrowser.Controller.Entities
|
|||
/// </summary>
|
||||
/// <param name="path">The path.</param>
|
||||
/// <returns><c>true</c> if the specified path is offline; otherwise, <c>false</c>.</returns>
|
||||
private bool IsPathOffline(string path)
|
||||
public static bool IsPathOffline(string path)
|
||||
{
|
||||
if (File.Exists(path))
|
||||
if (FileSystem.FileExists(path))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
@ -703,7 +708,7 @@ namespace MediaBrowser.Controller.Entities
|
|||
// Depending on whether the path is local or unc, it may return either null or '\' at the top
|
||||
while (!string.IsNullOrEmpty(path) && path.Length > 1)
|
||||
{
|
||||
if (Directory.Exists(path))
|
||||
if (FileSystem.DirectoryExists(path))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
@ -725,12 +730,12 @@ namespace MediaBrowser.Controller.Entities
|
|||
/// <param name="folders">The folders.</param>
|
||||
/// <param name="path">The path.</param>
|
||||
/// <returns><c>true</c> if the specified folders contains path; otherwise, <c>false</c>.</returns>
|
||||
private bool ContainsPath(IEnumerable<VirtualFolderInfo> folders, string path)
|
||||
private static bool ContainsPath(IEnumerable<VirtualFolderInfo> folders, string path)
|
||||
{
|
||||
return folders.SelectMany(i => i.Locations).Any(i => ContainsPath(i, path));
|
||||
}
|
||||
|
||||
private bool ContainsPath(string parent, string path)
|
||||
private static bool ContainsPath(string parent, string path)
|
||||
{
|
||||
return string.Equals(parent, path, StringComparison.OrdinalIgnoreCase) || FileSystem.ContainsSubPath(parent, path);
|
||||
}
|
||||
|
@ -752,21 +757,24 @@ namespace MediaBrowser.Controller.Entities
|
|||
/// <returns>IEnumerable{BaseItem}.</returns>
|
||||
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();
|
||||
|
||||
//if (children.Count != childrenItems.Count)
|
||||
//{
|
||||
// var b = this;
|
||||
//}
|
||||
|
||||
return childrenItems;
|
||||
//}).Items.Select(RetrieveChild).Where(i => i != null);
|
||||
}
|
||||
else
|
||||
{
|
||||
return ItemRepository.GetChildrenItems(Id).Select(RetrieveChild).Where(i => i != null);
|
||||
}
|
||||
}
|
||||
|
||||
private BaseItem RetrieveChild(BaseItem child)
|
||||
{
|
||||
if (child.Id == Guid.Empty)
|
||||
if (child == null || child.Id == Guid.Empty)
|
||||
{
|
||||
Logger.Error("Item found with empty Id: " + (child.Path ?? child.Name));
|
||||
return null;
|
||||
|
@ -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;
|
||||
|
||||
|
@ -1085,7 +1093,7 @@ namespace MediaBrowser.Controller.Entities
|
|||
/// Refreshes the linked children.
|
||||
/// </summary>
|
||||
/// <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 currentShortcutLinks = LinkedChildren.Where(i => i.Type == LinkedChildType.Shortcut).ToList();
|
||||
|
@ -1170,9 +1178,16 @@ namespace MediaBrowser.Controller.Entities
|
|||
DateTime? datePlayed,
|
||||
bool resetPosition)
|
||||
{
|
||||
var itemsResult = await GetItems(new InternalItemsQuery
|
||||
{
|
||||
User = user,
|
||||
Recursive = true,
|
||||
IsFolder = false
|
||||
|
||||
}).ConfigureAwait(false);
|
||||
|
||||
// Sweep through recursively and update status
|
||||
var tasks = GetRecursiveChildren(user, i => !i.IsFolder && i.LocationType != LocationType.Virtual)
|
||||
.Select(c => c.MarkPlayed(user, datePlayed, resetPosition));
|
||||
var tasks = itemsResult.Items.Select(c => c.MarkPlayed(user, datePlayed, resetPosition));
|
||||
|
||||
await Task.WhenAll(tasks).ConfigureAwait(false);
|
||||
}
|
||||
|
@ -1184,9 +1199,16 @@ namespace MediaBrowser.Controller.Entities
|
|||
/// <returns>Task.</returns>
|
||||
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
|
||||
var tasks = GetRecursiveChildren(user, i => !i.IsFolder && i.LocationType != LocationType.Virtual)
|
||||
.Select(c => c.MarkUnplayed(user));
|
||||
var tasks = itemsResult.Items.Select(c => c.MarkUnplayed(user));
|
||||
|
||||
await Task.WhenAll(tasks).ConfigureAwait(false);
|
||||
}
|
||||
|
|
|
@ -8,19 +8,11 @@ using System.Linq;
|
|||
|
||||
namespace MediaBrowser.Controller.Entities
|
||||
{
|
||||
public class Game : BaseItem, IHasTrailers, IHasThemeMedia, IHasTags, IHasScreenshots, ISupportsPlaceHolders, IHasPreferredMetadataLanguage, IHasLookupInfo<GameInfo>
|
||||
public class Game : BaseItem, IHasTrailers, IHasThemeMedia, IHasTags, IHasScreenshots, ISupportsPlaceHolders, IHasLookupInfo<GameInfo>
|
||||
{
|
||||
public List<Guid> ThemeSongIds { get; set; }
|
||||
public List<Guid> ThemeVideoIds { get; set; }
|
||||
|
||||
public string PreferredMetadataLanguage { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the preferred metadata country code.
|
||||
/// </summary>
|
||||
/// <value>The preferred metadata country code.</value>
|
||||
public string PreferredMetadataCountryCode { get; set; }
|
||||
|
||||
public Game()
|
||||
{
|
||||
MultiPartGameFiles = new List<string>();
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue