diff options
23 files changed, 449 insertions, 219 deletions
diff --git a/Emby.Common.Implementations/BaseApplicationHost.cs b/Emby.Common.Implementations/BaseApplicationHost.cs index 2fe82e5a4..897557efd 100644 --- a/Emby.Common.Implementations/BaseApplicationHost.cs +++ b/Emby.Common.Implementations/BaseApplicationHost.cs @@ -27,11 +27,16 @@ using System.Threading; using System.Threading.Tasks; using MediaBrowser.Common.Extensions; using Emby.Common.Implementations.Cryptography; +using Emby.Common.Implementations.Diagnostics; +using Emby.Common.Implementations.Threading; using MediaBrowser.Common; using MediaBrowser.Common.IO; using MediaBrowser.Model.Cryptography; +using MediaBrowser.Model.Diagnostics; using MediaBrowser.Model.System; using MediaBrowser.Model.Tasks; +using MediaBrowser.Model.Threading; + #if NETSTANDARD1_6 using System.Runtime.Loader; #endif @@ -146,6 +151,9 @@ namespace Emby.Common.Implementations protected ISystemEvents SystemEvents { get; private set; } + protected IProcessFactory ProcessFactory { get; private set; } + protected ITimerFactory TimerFactory { get; private set; } + /// <summary> /// Gets the name. /// </summary> @@ -535,6 +543,12 @@ return null; IsoManager = new IsoManager(); RegisterSingleInstance(IsoManager); + ProcessFactory = new ProcessFactory(); + RegisterSingleInstance(ProcessFactory); + + TimerFactory = new TimerFactory(); + RegisterSingleInstance(TimerFactory); + return Task.FromResult(true); } diff --git a/Emby.Common.Implementations/Diagnostics/CommonProcess.cs b/Emby.Common.Implementations/Diagnostics/CommonProcess.cs new file mode 100644 index 000000000..06677bc29 --- /dev/null +++ b/Emby.Common.Implementations/Diagnostics/CommonProcess.cs @@ -0,0 +1,108 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.IO; +using System.Linq; +using System.Threading.Tasks; +using MediaBrowser.Model.Diagnostics; + +namespace Emby.Common.Implementations.Diagnostics +{ + public class CommonProcess : IProcess + { + public event EventHandler Exited; + + private readonly ProcessOptions _options; + private readonly Process _process; + + public CommonProcess(ProcessOptions options) + { + _options = options; + + var startInfo = new ProcessStartInfo + { + Arguments = options.Arguments, + FileName = options.FileName, + WorkingDirectory = options.WorkingDirectory, + UseShellExecute = options.UseShellExecute, + CreateNoWindow = options.CreateNoWindow, + RedirectStandardError = options.RedirectStandardError, + RedirectStandardInput = options.RedirectStandardInput, + RedirectStandardOutput = options.RedirectStandardOutput + }; + +#if NET46 + startInfo.ErrorDialog = options.ErrorDialog; + + if (options.IsHidden) + { + startInfo.WindowStyle = ProcessWindowStyle.Hidden; + } +#endif + + _process = new Process + { + StartInfo = startInfo + }; + + if (options.EnableRaisingEvents) + { + _process.EnableRaisingEvents = true; + _process.Exited += _process_Exited; + } + } + + private void _process_Exited(object sender, EventArgs e) + { + if (Exited != null) + { + Exited(_process, e); + } + } + + public ProcessOptions StartInfo + { + get { return _options; } + } + + public StreamWriter StandardInput + { + get { return _process.StandardInput; } + } + + public StreamReader StandardError + { + get { return _process.StandardError; } + } + + public StreamReader StandardOutput + { + get { return _process.StandardOutput; } + } + + public int ExitCode + { + get { return _process.ExitCode; } + } + + public void Start() + { + _process.Start(); + } + + public void Kill() + { + _process.Kill(); + } + + public bool WaitForExit(int timeMs) + { + return _process.WaitForExit(timeMs); + } + + public void Dispose() + { + _process.Dispose(); + } + } +} diff --git a/Emby.Common.Implementations/Diagnostics/ProcessFactory.cs b/Emby.Common.Implementations/Diagnostics/ProcessFactory.cs new file mode 100644 index 000000000..292da023c --- /dev/null +++ b/Emby.Common.Implementations/Diagnostics/ProcessFactory.cs @@ -0,0 +1,12 @@ +using MediaBrowser.Model.Diagnostics; + +namespace Emby.Common.Implementations.Diagnostics +{ + public class ProcessFactory : IProcessFactory + { + public IProcess Create(ProcessOptions options) + { + return new CommonProcess(options); + } + } +} diff --git a/Emby.Common.Implementations/Threading/CommonTimer.cs b/Emby.Common.Implementations/Threading/CommonTimer.cs new file mode 100644 index 000000000..8895f6798 --- /dev/null +++ b/Emby.Common.Implementations/Threading/CommonTimer.cs @@ -0,0 +1,39 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using MediaBrowser.Model.Threading; + +namespace Emby.Common.Implementations.Threading +{ + public class CommonTimer : ITimer + { + private readonly Timer _timer; + + public CommonTimer(Action<object> callback, object state, TimeSpan dueTime, TimeSpan period) + { + _timer = new Timer(new TimerCallback(callback), state, dueTime, period); + } + + public CommonTimer(Action<object> callback, object state, int dueTimeMs, int periodMs) + { + _timer = new Timer(new TimerCallback(callback), state, dueTimeMs, periodMs); + } + + public void Change(TimeSpan dueTime, TimeSpan period) + { + _timer.Change(dueTime, period); + } + + public void Change(int dueTimeMs, int periodMs) + { + _timer.Change(dueTimeMs, periodMs); + } + + public void Dispose() + { + _timer.Dispose(); + } + } +} diff --git a/Emby.Common.Implementations/Threading/TimerFactory.cs b/Emby.Common.Implementations/Threading/TimerFactory.cs new file mode 100644 index 000000000..028dd0963 --- /dev/null +++ b/Emby.Common.Implementations/Threading/TimerFactory.cs @@ -0,0 +1,21 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using MediaBrowser.Model.Threading; + +namespace Emby.Common.Implementations.Threading +{ + public class TimerFactory : ITimerFactory + { + public ITimer Create(Action<object> callback, object state, TimeSpan dueTime, TimeSpan period) + { + return new CommonTimer(callback, state, dueTime, period); + } + + public ITimer Create(Action<object> callback, object state, int dueTimeMs, int periodMs) + { + return new CommonTimer(callback, state, dueTimeMs, periodMs); + } + } +} diff --git a/Emby.Common.Implementations/project.json b/Emby.Common.Implementations/project.json index 444d0e13e..a65b86345 100644 --- a/Emby.Common.Implementations/project.json +++ b/Emby.Common.Implementations/project.json @@ -44,6 +44,8 @@ "target": "project" }, "System.IO.FileSystem.DriveInfo": "4.0.0", + "System.Diagnostics.Process": "4.1.0", + "System.Threading.Timer": "4.0.1", "System.Net.Requests": "4.0.11", "System.Xml.XmlSerializer": "4.0.11", "System.Net.Http": "4.1.0", diff --git a/Emby.Server.sln b/Emby.Server.sln index 2e2e093d5..e9073a8d0 100644 --- a/Emby.Server.sln +++ b/Emby.Server.sln @@ -42,6 +42,10 @@ Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Emby.Dlna", "Emby.Dlna\Emby EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Emby.Photos", "Emby.Photos\Emby.Photos.csproj", "{89AB4548-770D-41FD-A891-8DAFF44F452C}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MediaBrowser.Api", "MediaBrowser.Api\MediaBrowser.Api.csproj", "{4FD51AC5-2C16-4308-A993-C3A84F3B4582}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MediaBrowser.MediaEncoding", "MediaBrowser.MediaEncoding\MediaBrowser.MediaEncoding.csproj", "{0BD82FA6-EB8A-4452-8AF5-74F9C3849451}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -145,6 +149,18 @@ Global {89AB4548-770D-41FD-A891-8DAFF44F452C}.Release Mono|Any CPU.Build.0 = Release|Any CPU {89AB4548-770D-41FD-A891-8DAFF44F452C}.Release|Any CPU.ActiveCfg = Release|Any CPU {89AB4548-770D-41FD-A891-8DAFF44F452C}.Release|Any CPU.Build.0 = Release|Any CPU + {4FD51AC5-2C16-4308-A993-C3A84F3B4582}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {4FD51AC5-2C16-4308-A993-C3A84F3B4582}.Debug|Any CPU.Build.0 = Debug|Any CPU + {4FD51AC5-2C16-4308-A993-C3A84F3B4582}.Release Mono|Any CPU.ActiveCfg = Release Mono|Any CPU + {4FD51AC5-2C16-4308-A993-C3A84F3B4582}.Release Mono|Any CPU.Build.0 = Release Mono|Any CPU + {4FD51AC5-2C16-4308-A993-C3A84F3B4582}.Release|Any CPU.ActiveCfg = Release|Any CPU + {4FD51AC5-2C16-4308-A993-C3A84F3B4582}.Release|Any CPU.Build.0 = Release|Any CPU + {0BD82FA6-EB8A-4452-8AF5-74F9C3849451}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {0BD82FA6-EB8A-4452-8AF5-74F9C3849451}.Debug|Any CPU.Build.0 = Debug|Any CPU + {0BD82FA6-EB8A-4452-8AF5-74F9C3849451}.Release Mono|Any CPU.ActiveCfg = Release Mono|Any CPU + {0BD82FA6-EB8A-4452-8AF5-74F9C3849451}.Release Mono|Any CPU.Build.0 = Release Mono|Any CPU + {0BD82FA6-EB8A-4452-8AF5-74F9C3849451}.Release|Any CPU.ActiveCfg = Release|Any CPU + {0BD82FA6-EB8A-4452-8AF5-74F9C3849451}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -166,5 +182,7 @@ Global {C227ADB7-E256-4E70-A8B9-22B9E0CF4F55} = {8ADD772F-F0A4-4A53-9B2F-AF4A4C585839} {F40E364D-01D9-4BBF-B82C-5D6C55E0A1F5} = {8ADD772F-F0A4-4A53-9B2F-AF4A4C585839} {89AB4548-770D-41FD-A891-8DAFF44F452C} = {8ADD772F-F0A4-4A53-9B2F-AF4A4C585839} + {4FD51AC5-2C16-4308-A993-C3A84F3B4582} = {8ADD772F-F0A4-4A53-9B2F-AF4A4C585839} + {0BD82FA6-EB8A-4452-8AF5-74F9C3849451} = {8ADD772F-F0A4-4A53-9B2F-AF4A4C585839} EndGlobalSection EndGlobal diff --git a/MediaBrowser.MediaEncoding/Encoder/AudioEncoder.cs b/MediaBrowser.MediaEncoding/Encoder/AudioEncoder.cs index 06f992efd..5a554d26f 100644 --- a/MediaBrowser.MediaEncoding/Encoder/AudioEncoder.cs +++ b/MediaBrowser.MediaEncoding/Encoder/AudioEncoder.cs @@ -7,15 +7,13 @@ using MediaBrowser.Model.Logging; using System; using System.Collections.Generic; using System.Threading.Tasks; -using MediaBrowser.Common.IO; -using MediaBrowser.Controller.IO; -using MediaBrowser.Model.IO; +using MediaBrowser.Model.Diagnostics; namespace MediaBrowser.MediaEncoding.Encoder { public class AudioEncoder : BaseEncoder { - public AudioEncoder(MediaEncoder mediaEncoder, ILogger logger, IServerConfigurationManager configurationManager, IFileSystem fileSystem, IIsoManager isoManager, ILibraryManager libraryManager, ISessionManager sessionManager, ISubtitleEncoder subtitleEncoder, IMediaSourceManager mediaSourceManager) : base(mediaEncoder, logger, configurationManager, fileSystem, isoManager, libraryManager, sessionManager, subtitleEncoder, mediaSourceManager) + public AudioEncoder(MediaEncoder mediaEncoder, ILogger logger, IServerConfigurationManager configurationManager, IFileSystem fileSystem, IIsoManager isoManager, ILibraryManager libraryManager, ISessionManager sessionManager, ISubtitleEncoder subtitleEncoder, IMediaSourceManager mediaSourceManager, IProcessFactory processFactory) : base(mediaEncoder, logger, configurationManager, fileSystem, isoManager, libraryManager, sessionManager, subtitleEncoder, mediaSourceManager, processFactory) { } @@ -116,5 +114,6 @@ namespace MediaBrowser.MediaEncoding.Encoder { get { return false; } } + } } diff --git a/MediaBrowser.MediaEncoding/Encoder/BaseEncoder.cs b/MediaBrowser.MediaEncoding/Encoder/BaseEncoder.cs index f43f01871..b8087fded 100644 --- a/MediaBrowser.MediaEncoding/Encoder/BaseEncoder.cs +++ b/MediaBrowser.MediaEncoding/Encoder/BaseEncoder.cs @@ -11,15 +11,12 @@ using MediaBrowser.Model.Logging; using MediaBrowser.Model.MediaInfo; using System; using System.Collections.Generic; -using System.Diagnostics; using System.Globalization; using System.IO; using System.Text; using System.Threading; using System.Threading.Tasks; -using MediaBrowser.Common.IO; -using MediaBrowser.Controller.IO; -using MediaBrowser.Model.IO; +using MediaBrowser.Model.Diagnostics; using MediaBrowser.Model.Dlna; namespace MediaBrowser.MediaEncoding.Encoder @@ -35,6 +32,7 @@ namespace MediaBrowser.MediaEncoding.Encoder protected readonly ISessionManager SessionManager; protected readonly ISubtitleEncoder SubtitleEncoder; protected readonly IMediaSourceManager MediaSourceManager; + protected IProcessFactory ProcessFactory; protected readonly CultureInfo UsCulture = new CultureInfo("en-US"); @@ -46,7 +44,7 @@ namespace MediaBrowser.MediaEncoding.Encoder ILibraryManager libraryManager, ISessionManager sessionManager, ISubtitleEncoder subtitleEncoder, - IMediaSourceManager mediaSourceManager) + IMediaSourceManager mediaSourceManager, IProcessFactory processFactory) { MediaEncoder = mediaEncoder; Logger = logger; @@ -57,6 +55,7 @@ namespace MediaBrowser.MediaEncoding.Encoder SessionManager = sessionManager; SubtitleEncoder = subtitleEncoder; MediaSourceManager = mediaSourceManager; + ProcessFactory = processFactory; } public async Task<EncodingJob> Start(EncodingJobOptions options, @@ -75,27 +74,23 @@ namespace MediaBrowser.MediaEncoding.Encoder var commandLineArgs = await GetCommandLineArguments(encodingJob).ConfigureAwait(false); - var process = new Process + var process = ProcessFactory.Create(new ProcessOptions { - StartInfo = new ProcessStartInfo - { - CreateNoWindow = true, - UseShellExecute = false, - - // Must consume both stdout and stderr or deadlocks may occur - //RedirectStandardOutput = true, - RedirectStandardError = true, - RedirectStandardInput = true, + CreateNoWindow = true, + UseShellExecute = false, - FileName = MediaEncoder.EncoderPath, - Arguments = commandLineArgs, + // Must consume both stdout and stderr or deadlocks may occur + //RedirectStandardOutput = true, + RedirectStandardError = true, + RedirectStandardInput = true, - WindowStyle = ProcessWindowStyle.Hidden, - ErrorDialog = false - }, + FileName = MediaEncoder.EncoderPath, + Arguments = commandLineArgs, + IsHidden = true, + ErrorDialog = false, EnableRaisingEvents = true - }; + }); var workingDirectory = GetWorkingDirectory(options); if (!string.IsNullOrWhiteSpace(workingDirectory)) @@ -149,7 +144,7 @@ namespace MediaBrowser.MediaEncoding.Encoder return encodingJob; } - private void Cancel(Process process, EncodingJob job) + private void Cancel(IProcess process, EncodingJob job) { Logger.Info("Killing ffmpeg process for {0}", job.OutputFilePath); @@ -164,7 +159,7 @@ namespace MediaBrowser.MediaEncoding.Encoder /// </summary> /// <param name="process">The process.</param> /// <param name="job">The job.</param> - private void OnFfMpegProcessExited(Process process, EncodingJob job) + private void OnFfMpegProcessExited(IProcess process, EncodingJob job) { job.HasExited = true; diff --git a/MediaBrowser.MediaEncoding/Encoder/EncoderValidator.cs b/MediaBrowser.MediaEncoding/Encoder/EncoderValidator.cs index a78d6cfd6..20b5eb05d 100644 --- a/MediaBrowser.MediaEncoding/Encoder/EncoderValidator.cs +++ b/MediaBrowser.MediaEncoding/Encoder/EncoderValidator.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Diagnostics; +using MediaBrowser.Model.Diagnostics; using MediaBrowser.Model.Logging; namespace MediaBrowser.MediaEncoding.Encoder @@ -8,10 +9,12 @@ namespace MediaBrowser.MediaEncoding.Encoder public class EncoderValidator { private readonly ILogger _logger; + private readonly IProcessFactory _processFactory; - public EncoderValidator(ILogger logger) + public EncoderValidator(ILogger logger, IProcessFactory processFactory) { _logger = logger; + _processFactory = processFactory; } public Tuple<List<string>, List<string>> Validate(string encoderPath) @@ -145,19 +148,16 @@ namespace MediaBrowser.MediaEncoding.Encoder private string GetProcessOutput(string path, string arguments) { - var process = new Process + var process = _processFactory.Create(new ProcessOptions { - StartInfo = new ProcessStartInfo - { - CreateNoWindow = true, - UseShellExecute = false, - FileName = path, - Arguments = arguments, - WindowStyle = ProcessWindowStyle.Hidden, - ErrorDialog = false, - RedirectStandardOutput = true - } - }; + CreateNoWindow = true, + UseShellExecute = false, + FileName = path, + Arguments = arguments, + IsHidden = true, + ErrorDialog = false, + RedirectStandardOutput = true + }); _logger.Info("Running {0} {1}", path, arguments); diff --git a/MediaBrowser.MediaEncoding/Encoder/FontConfigLoader.cs b/MediaBrowser.MediaEncoding/Encoder/FontConfigLoader.cs index 7f4e7909a..42048ab9e 100644 --- a/MediaBrowser.MediaEncoding/Encoder/FontConfigLoader.cs +++ b/MediaBrowser.MediaEncoding/Encoder/FontConfigLoader.cs @@ -88,9 +88,9 @@ namespace MediaBrowser.MediaEncoding.Encoder /// <returns>Task.</returns> private async Task DownloadFontFile(string fontsDirectory, string fontFilename, IProgress<double> progress) { - var existingFile = Directory - .EnumerateFiles(_appPaths.ProgramDataPath, fontFilename, SearchOption.AllDirectories) - .FirstOrDefault(); + var existingFile = _fileSystem + .GetFilePaths(_appPaths.ProgramDataPath, true) + .FirstOrDefault(i => string.Equals(fontFilename, Path.GetFileName(i), StringComparison.OrdinalIgnoreCase)); if (existingFile != null) { diff --git a/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs b/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs index bf6ff7655..d9571f8e5 100644 --- a/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs +++ b/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs @@ -14,19 +14,16 @@ using MediaBrowser.Model.MediaInfo; using MediaBrowser.Model.Serialization; using System; using System.Collections.Generic; -using System.Diagnostics; using System.Globalization; using System.IO; using System.Linq; using System.Threading; using System.Threading.Tasks; -using MediaBrowser.Model.IO; using MediaBrowser.Model.Configuration; using MediaBrowser.Common.Configuration; using MediaBrowser.Common.Extensions; -using MediaBrowser.Common.IO; using MediaBrowser.Common.Net; -using MediaBrowser.Controller.IO; +using MediaBrowser.Model.Diagnostics; namespace MediaBrowser.MediaEncoding.Encoder { @@ -81,14 +78,19 @@ namespace MediaBrowser.MediaEncoding.Encoder protected readonly Func<IMediaSourceManager> MediaSourceManager; private readonly IHttpClient _httpClient; private readonly IZipClient _zipClient; + private readonly IProcessFactory _processFactory; private readonly IMemoryStreamProvider _memoryStreamProvider; private readonly List<ProcessWrapper> _runningProcesses = new List<ProcessWrapper>(); private readonly bool _hasExternalEncoder; - private string _originalFFMpegPath; - private string _originalFFProbePath; - - public MediaEncoder(ILogger logger, IJsonSerializer jsonSerializer, string ffMpegPath, string ffProbePath, bool hasExternalEncoder, IServerConfigurationManager configurationManager, IFileSystem fileSystem, ILiveTvManager liveTvManager, IIsoManager isoManager, ILibraryManager libraryManager, IChannelManager channelManager, ISessionManager sessionManager, Func<ISubtitleEncoder> subtitleEncoder, Func<IMediaSourceManager> mediaSourceManager, IHttpClient httpClient, IZipClient zipClient, IMemoryStreamProvider memoryStreamProvider) + private readonly string _originalFFMpegPath; + private readonly string _originalFFProbePath; + private readonly int DefaultImageExtractionTimeoutMs; + private readonly bool EnableEncoderFontFile; + + public MediaEncoder(ILogger logger, IJsonSerializer jsonSerializer, string ffMpegPath, string ffProbePath, bool hasExternalEncoder, IServerConfigurationManager configurationManager, IFileSystem fileSystem, ILiveTvManager liveTvManager, IIsoManager isoManager, ILibraryManager libraryManager, IChannelManager channelManager, ISessionManager sessionManager, Func<ISubtitleEncoder> subtitleEncoder, Func<IMediaSourceManager> mediaSourceManager, IHttpClient httpClient, IZipClient zipClient, IMemoryStreamProvider memoryStreamProvider, IProcessFactory processFactory, + int defaultImageExtractionTimeoutMs, + bool enableEncoderFontFile) { _logger = logger; _jsonSerializer = jsonSerializer; @@ -104,6 +106,9 @@ namespace MediaBrowser.MediaEncoding.Encoder _httpClient = httpClient; _zipClient = zipClient; _memoryStreamProvider = memoryStreamProvider; + _processFactory = processFactory; + DefaultImageExtractionTimeoutMs = defaultImageExtractionTimeoutMs; + EnableEncoderFontFile = enableEncoderFontFile; FFProbePath = ffProbePath; FFMpegPath = ffMpegPath; _originalFFProbePath = ffProbePath; @@ -158,12 +163,12 @@ namespace MediaBrowser.MediaEncoding.Encoder if (!string.IsNullOrWhiteSpace(FFMpegPath)) { - var result = new EncoderValidator(_logger).Validate(FFMpegPath); + var result = new EncoderValidator(_logger, _processFactory).Validate(FFMpegPath); SetAvailableDecoders(result.Item1); SetAvailableEncoders(result.Item2); - if (Environment.OSVersion.Platform == PlatformID.Win32NT) + if (EnableEncoderFontFile) { var directory = Path.GetDirectoryName(FFMpegPath); @@ -255,7 +260,7 @@ namespace MediaBrowser.MediaEncoding.Encoder private bool ValidateVersion(string path, bool logOutput) { - return new EncoderValidator(_logger).ValidateVersion(path, logOutput); + return new EncoderValidator(_logger, _processFactory).ValidateVersion(path, logOutput); } private void ConfigureEncoderPaths() @@ -509,27 +514,22 @@ namespace MediaBrowser.MediaEncoding.Encoder ? "{0} -i {1} -threads 0 -v info -print_format json -show_streams -show_chapters -show_format" : "{0} -i {1} -threads 0 -v info -print_format json -show_streams -show_format"; - var process = new Process + var process = _processFactory.Create(new ProcessOptions { - StartInfo = new ProcessStartInfo - { - CreateNoWindow = true, - UseShellExecute = false, - - // Must consume both or ffmpeg may hang due to deadlocks. See comments below. - RedirectStandardOutput = true, - //RedirectStandardError = true, - RedirectStandardInput = false, - FileName = FFProbePath, - Arguments = string.Format(args, - probeSizeArgument, inputPath).Trim(), - - WindowStyle = ProcessWindowStyle.Hidden, - ErrorDialog = false - }, - + CreateNoWindow = true, + UseShellExecute = false, + + // Must consume both or ffmpeg may hang due to deadlocks. See comments below. + RedirectStandardOutput = true, + //RedirectStandardError = true, + RedirectStandardInput = false, + FileName = FFProbePath, + Arguments = string.Format(args, probeSizeArgument, inputPath).Trim(), + + IsHidden = true, + ErrorDialog = false, EnableRaisingEvents = true - }; + }); _logger.Debug("{0} {1}", process.StartInfo.FileName, process.StartInfo.Arguments); @@ -644,26 +644,22 @@ namespace MediaBrowser.MediaEncoding.Encoder var args = "{0} -i {1} -map 0:v:{2} -an -filter:v idet -frames:v 500 -an -f null /dev/null"; - var process = new Process + var process = _processFactory.Create(new ProcessOptions { - StartInfo = new ProcessStartInfo - { - CreateNoWindow = true, - UseShellExecute = false, - - // Must consume both or ffmpeg may hang due to deadlocks. See comments below. - //RedirectStandardOutput = true, - RedirectStandardError = true, - RedirectStandardInput = false, - FileName = FFMpegPath, - Arguments = string.Format(args, probeSizeArgument, inputPath, videoStream.Index.ToString(CultureInfo.InvariantCulture)).Trim(), - - WindowStyle = ProcessWindowStyle.Hidden, - ErrorDialog = false - }, - + CreateNoWindow = true, + UseShellExecute = false, + + // Must consume both or ffmpeg may hang due to deadlocks. See comments below. + //RedirectStandardOutput = true, + RedirectStandardError = true, + RedirectStandardInput = false, + FileName = FFMpegPath, + Arguments = string.Format(args, probeSizeArgument, inputPath, videoStream.Index.ToString(CultureInfo.InvariantCulture)).Trim(), + + IsHidden = true, + ErrorDialog = false, EnableRaisingEvents = true - }; + }); _logger.Debug("{0} {1}", process.StartInfo.FileName, process.StartInfo.Arguments); var idetFoundInterlaced = false; @@ -916,18 +912,15 @@ namespace MediaBrowser.MediaEncoding.Encoder args = string.Format("-ss {0} ", GetTimeParameter(offset.Value)) + args; } - var process = new Process + var process = _processFactory.Create(new ProcessOptions { - StartInfo = new ProcessStartInfo - { - CreateNoWindow = true, - UseShellExecute = false, - FileName = FFMpegPath, - Arguments = args, - WindowStyle = ProcessWindowStyle.Hidden, - ErrorDialog = false - } - }; + CreateNoWindow = true, + UseShellExecute = false, + FileName = FFMpegPath, + Arguments = args, + IsHidden = true, + ErrorDialog = false + }); _logger.Debug("{0} {1}", process.StartInfo.FileName, process.StartInfo.Arguments); @@ -944,7 +937,7 @@ namespace MediaBrowser.MediaEncoding.Encoder var timeoutMs = ConfigurationManager.Configuration.ImageExtractionTimeoutMs; if (timeoutMs <= 0) { - timeoutMs = Environment.Is64BitOperatingSystem ? (Environment.ProcessorCount > 2 ? 14000 : 20000) : 40000; + timeoutMs = DefaultImageExtractionTimeoutMs; } ranToCompletion = process.WaitForExit(timeoutMs); @@ -1022,19 +1015,16 @@ namespace MediaBrowser.MediaEncoding.Encoder args = probeSize + " " + args; } - var process = new Process + var process = _processFactory.Create(new ProcessOptions { - StartInfo = new ProcessStartInfo - { - CreateNoWindow = true, - UseShellExecute = false, - FileName = FFMpegPath, - Arguments = args, - WindowStyle = ProcessWindowStyle.Hidden, - ErrorDialog = false, - RedirectStandardInput = true - } - }; + CreateNoWindow = true, + UseShellExecute = false, + FileName = FFMpegPath, + Arguments = args, + IsHidden = true, + ErrorDialog = false, + RedirectStandardInput = true + }); _logger.Info(process.StartInfo.FileName + " " + process.StartInfo.Arguments); @@ -1107,7 +1097,8 @@ namespace MediaBrowser.MediaEncoding.Encoder LibraryManager, SessionManager, SubtitleEncoder(), - MediaSourceManager()) + MediaSourceManager(), + _processFactory) .Start(options, progress, cancellationToken).ConfigureAwait(false); await job.TaskCompletionSource.Task.ConfigureAwait(false); @@ -1127,7 +1118,8 @@ namespace MediaBrowser.MediaEncoding.Encoder LibraryManager, SessionManager, SubtitleEncoder(), - MediaSourceManager()) + MediaSourceManager(), + _processFactory) .Start(options, progress, cancellationToken).ConfigureAwait(false); await job.TaskCompletionSource.Task.ConfigureAwait(false); @@ -1231,14 +1223,14 @@ namespace MediaBrowser.MediaEncoding.Encoder private class ProcessWrapper : IDisposable { - public readonly Process Process; + public readonly IProcess Process; public bool HasExited; public int? ExitCode; private readonly MediaEncoder _mediaEncoder; private readonly ILogger _logger; public bool IsRedirectingStdin { get; private set; } - public ProcessWrapper(Process process, MediaEncoder mediaEncoder, ILogger logger, bool isRedirectingStdin) + public ProcessWrapper(IProcess process, MediaEncoder mediaEncoder, ILogger logger, bool isRedirectingStdin) { Process = process; _mediaEncoder = mediaEncoder; @@ -1249,7 +1241,7 @@ namespace MediaBrowser.MediaEncoding.Encoder void Process_Exited(object sender, EventArgs e) { - var process = (Process)sender; + var process = (IProcess)sender; HasExited = true; @@ -1269,7 +1261,7 @@ namespace MediaBrowser.MediaEncoding.Encoder DisposeProcess(process); } - private void DisposeProcess(Process process) + private void DisposeProcess(IProcess process) { try { diff --git a/MediaBrowser.MediaEncoding/Encoder/VideoEncoder.cs b/MediaBrowser.MediaEncoding/Encoder/VideoEncoder.cs index 73f82b61c..cbbca479a 100644 --- a/MediaBrowser.MediaEncoding/Encoder/VideoEncoder.cs +++ b/MediaBrowser.MediaEncoding/Encoder/VideoEncoder.cs @@ -8,15 +8,13 @@ using MediaBrowser.Model.Logging; using System; using System.IO; using System.Threading.Tasks; -using MediaBrowser.Common.IO; -using MediaBrowser.Controller.IO; -using MediaBrowser.Model.IO; +using MediaBrowser.Model.Diagnostics; namespace MediaBrowser.MediaEncoding.Encoder { public class VideoEncoder : BaseEncoder { - public VideoEncoder(MediaEncoder mediaEncoder, ILogger logger, IServerConfigurationManager configurationManager, IFileSystem fileSystem, IIsoManager isoManager, ILibraryManager libraryManager, ISessionManager sessionManager, ISubtitleEncoder subtitleEncoder, IMediaSourceManager mediaSourceManager) : base(mediaEncoder, logger, configurationManager, fileSystem, isoManager, libraryManager, sessionManager, subtitleEncoder, mediaSourceManager) + public VideoEncoder(MediaEncoder mediaEncoder, ILogger logger, IServerConfigurationManager configurationManager, IFileSystem fileSystem, IIsoManager isoManager, ILibraryManager libraryManager, ISessionManager sessionManager, ISubtitleEncoder subtitleEncoder, IMediaSourceManager mediaSourceManager, IProcessFactory processFactory) : base(mediaEncoder, logger, configurationManager, fileSystem, isoManager, libraryManager, sessionManager, subtitleEncoder, mediaSourceManager, processFactory) { } @@ -193,5 +191,6 @@ namespace MediaBrowser.MediaEncoding.Encoder { get { return true; } } + } }
\ No newline at end of file diff --git a/MediaBrowser.MediaEncoding/MediaBrowser.MediaEncoding.csproj b/MediaBrowser.MediaEncoding/MediaBrowser.MediaEncoding.csproj index 89e0b61c1..63e789a59 100644 --- a/MediaBrowser.MediaEncoding/MediaBrowser.MediaEncoding.csproj +++ b/MediaBrowser.MediaEncoding/MediaBrowser.MediaEncoding.csproj @@ -9,10 +9,10 @@ <AppDesignerFolder>Properties</AppDesignerFolder> <RootNamespace>MediaBrowser.MediaEncoding</RootNamespace> <AssemblyName>MediaBrowser.MediaEncoding</AssemblyName> - <TargetFrameworkVersion>v4.6</TargetFrameworkVersion> <FileAlignment>512</FileAlignment> - <SolutionDir Condition="$(SolutionDir) == '' Or $(SolutionDir) == '*Undefined*'">..\</SolutionDir> - <TargetFrameworkProfile /> + <ProjectTypeGuids>{786C830F-07A1-408B-BD7F-6EE04809D6DB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids> + <TargetFrameworkProfile>Profile7</TargetFrameworkProfile> + <TargetFrameworkVersion>v4.5</TargetFrameworkVersion> </PropertyGroup> <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' "> <DebugSymbols>true</DebugSymbols> @@ -37,13 +37,6 @@ <WarningLevel>4</WarningLevel> </PropertyGroup> <ItemGroup> - <Reference Include="System" /> - <Reference Include="System.Core" /> - <Reference Include="System.Xml.Linq" /> - <Reference Include="System.Data.DataSetExtensions" /> - <Reference Include="Microsoft.CSharp" /> - <Reference Include="System.Data" /> - <Reference Include="System.Xml" /> <Reference Include="UniversalDetector, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL"> <HintPath>..\packages\UniversalDetector.1.0.1\lib\portable-net45+sl4+wp71+win8+wpa81\UniversalDetector.dll</HintPath> <Private>True</Private> @@ -108,7 +101,7 @@ <ItemGroup> <None Include="packages.config" /> </ItemGroup> - <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" /> + <Import Project="$(MSBuildExtensionsPath32)\Microsoft\Portable\$(TargetFrameworkVersion)\Microsoft.Portable.CSharp.targets" /> <!-- To modify your build process, add your task inside one of the targets below and uncomment it. Other similar extension points exist, see Microsoft.Common.targets. <Target Name="BeforeBuild"> diff --git a/MediaBrowser.MediaEncoding/MediaBrowser.MediaEncoding.nuget.targets b/MediaBrowser.MediaEncoding/MediaBrowser.MediaEncoding.nuget.targets new file mode 100644 index 000000000..e69ce0e64 --- /dev/null +++ b/MediaBrowser.MediaEncoding/MediaBrowser.MediaEncoding.nuget.targets @@ -0,0 +1,6 @@ +<?xml version="1.0" encoding="utf-8" standalone="no"?> +<Project ToolsVersion="14.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <Target Name="EmitMSBuildWarning" BeforeTargets="Build"> + <Warning Text="Packages containing MSBuild targets and props files cannot be fully installed in projects targeting multiple frameworks. The MSBuild targets and props files have been ignored." /> + </Target> +</Project>
\ No newline at end of file diff --git a/MediaBrowser.MediaEncoding/Subtitles/OpenSubtitleDownloader.cs b/MediaBrowser.MediaEncoding/Subtitles/OpenSubtitleDownloader.cs index d8f36de9a..e26a6392c 100644 --- a/MediaBrowser.MediaEncoding/Subtitles/OpenSubtitleDownloader.cs +++ b/MediaBrowser.MediaEncoding/Subtitles/OpenSubtitleDownloader.cs @@ -14,6 +14,7 @@ using MediaBrowser.Controller.Security; using MediaBrowser.Controller.Subtitles; using MediaBrowser.Model.Dto; using MediaBrowser.Model.Entities; +using MediaBrowser.Model.IO; using MediaBrowser.Model.Logging; using MediaBrowser.Model.Providers; using MediaBrowser.Model.Serialization; @@ -31,14 +32,16 @@ namespace MediaBrowser.MediaEncoding.Subtitles private readonly IEncryptionManager _encryption; private readonly IJsonSerializer _json; + private readonly IFileSystem _fileSystem; - public OpenSubtitleDownloader(ILogManager logManager, IHttpClient httpClient, IServerConfigurationManager config, IEncryptionManager encryption, IJsonSerializer json) + public OpenSubtitleDownloader(ILogManager logManager, IHttpClient httpClient, IServerConfigurationManager config, IEncryptionManager encryption, IJsonSerializer json, IFileSystem fileSystem) { _logger = logManager.GetLogger(GetType().Name); _httpClient = httpClient; _config = config; _encryption = encryption; _json = json; + _fileSystem = fileSystem; _config.NamedConfigurationUpdating += _config_NamedConfigurationUpdating; @@ -133,7 +136,7 @@ namespace MediaBrowser.MediaEncoding.Subtitles if ((DateTime.UtcNow - _lastRateLimitException).TotalHours < 1) { - throw new ApplicationException("OpenSubtitles rate limit reached"); + throw new Exception("OpenSubtitles rate limit reached"); } var resultDownLoad = await OpenSubtitles.DownloadSubtitlesAsync(downloadsList, cancellationToken).ConfigureAwait(false); @@ -141,12 +144,12 @@ namespace MediaBrowser.MediaEncoding.Subtitles if ((resultDownLoad.Status ?? string.Empty).IndexOf("407", StringComparison.OrdinalIgnoreCase) != -1) { _lastRateLimitException = DateTime.UtcNow; - throw new ApplicationException("OpenSubtitles rate limit reached"); + throw new Exception("OpenSubtitles rate limit reached"); } if (!(resultDownLoad is MethodResponseSubtitleDownload)) { - throw new ApplicationException("Invalid response type"); + throw new Exception("Invalid response type"); } var results = ((MethodResponseSubtitleDownload)resultDownLoad).Results; @@ -269,11 +272,11 @@ namespace MediaBrowser.MediaEncoding.Subtitles var subLanguageId = NormalizeLanguage(request.Language); string hash; - using (var fileStream = File.OpenRead(request.MediaPath)) + using (var fileStream = _fileSystem.OpenRead(request.MediaPath)) { hash = Utilities.ComputeHash(fileStream); } - var fileInfo = new FileInfo(request.MediaPath); + var fileInfo = _fileSystem.GetFileInfo(request.MediaPath); var movieByteSize = fileInfo.Length; var searchImdbId = request.ContentType == VideoContentType.Movie ? imdbId.ToString(_usCulture) : ""; var subtitleSearchParameters = request.ContentType == VideoContentType.Episode diff --git a/MediaBrowser.MediaEncoding/Subtitles/SubtitleEncoder.cs b/MediaBrowser.MediaEncoding/Subtitles/SubtitleEncoder.cs index 416184f49..1248d138d 100644 --- a/MediaBrowser.MediaEncoding/Subtitles/SubtitleEncoder.cs +++ b/MediaBrowser.MediaEncoding/Subtitles/SubtitleEncoder.cs @@ -18,9 +18,8 @@ using System.Text; using System.Threading; using System.Threading.Tasks; using MediaBrowser.Model.IO; -using MediaBrowser.Common.IO; -using MediaBrowser.Controller.IO; -using MediaBrowser.Model.IO; +using MediaBrowser.Model.Diagnostics; +using MediaBrowser.Model.TextEncoding; using UniversalDetector; namespace MediaBrowser.MediaEncoding.Subtitles @@ -36,8 +35,10 @@ namespace MediaBrowser.MediaEncoding.Subtitles private readonly IHttpClient _httpClient; private readonly IMediaSourceManager _mediaSourceManager; private readonly IMemoryStreamProvider _memoryStreamProvider; + private readonly IProcessFactory _processFactory; + private readonly IEncoding _textEncoding; - public SubtitleEncoder(ILibraryManager libraryManager, ILogger logger, IApplicationPaths appPaths, IFileSystem fileSystem, IMediaEncoder mediaEncoder, IJsonSerializer json, IHttpClient httpClient, IMediaSourceManager mediaSourceManager, IMemoryStreamProvider memoryStreamProvider) + public SubtitleEncoder(ILibraryManager libraryManager, ILogger logger, IApplicationPaths appPaths, IFileSystem fileSystem, IMediaEncoder mediaEncoder, IJsonSerializer json, IHttpClient httpClient, IMediaSourceManager mediaSourceManager, IMemoryStreamProvider memoryStreamProvider, IProcessFactory processFactory, IEncoding textEncoding) { _libraryManager = libraryManager; _logger = logger; @@ -48,6 +49,8 @@ namespace MediaBrowser.MediaEncoding.Subtitles _httpClient = httpClient; _mediaSourceManager = mediaSourceManager; _memoryStreamProvider = memoryStreamProvider; + _processFactory = processFactory; + _textEncoding = textEncoding; } private string SubtitleCachePath @@ -418,7 +421,6 @@ namespace MediaBrowser.MediaEncoding.Subtitles /// or /// outputPath /// </exception> - /// <exception cref="System.ApplicationException"></exception> private async Task ConvertTextSubtitleToSrtInternal(string inputPath, string language, MediaProtocol inputProtocol, string outputPath, CancellationToken cancellationToken) { if (string.IsNullOrEmpty(inputPath)) @@ -440,23 +442,20 @@ namespace MediaBrowser.MediaEncoding.Subtitles encodingParam = " -sub_charenc " + encodingParam; } - var process = new Process + var process = _processFactory.Create(new ProcessOptions { - StartInfo = new ProcessStartInfo - { - RedirectStandardOutput = false, - RedirectStandardError = true, - RedirectStandardInput = true, + RedirectStandardOutput = false, + RedirectStandardError = true, + RedirectStandardInput = true, - CreateNoWindow = true, - UseShellExecute = false, - FileName = _mediaEncoder.EncoderPath, - Arguments = string.Format("{0} -i \"{1}\" -c:s srt \"{2}\"", encodingParam, inputPath, outputPath), + CreateNoWindow = true, + UseShellExecute = false, + FileName = _mediaEncoder.EncoderPath, + Arguments = string.Format("{0} -i \"{1}\" -c:s srt \"{2}\"", encodingParam, inputPath, outputPath), - WindowStyle = ProcessWindowStyle.Hidden, - ErrorDialog = false - } - }; + IsHidden = true, + ErrorDialog = false + }); _logger.Info("{0} {1}", process.StartInfo.FileName, process.StartInfo.Arguments); @@ -538,7 +537,7 @@ namespace MediaBrowser.MediaEncoding.Subtitles _logger.Error(msg); - throw new ApplicationException(msg); + throw new Exception(msg); } await SetAssFont(outputPath).ConfigureAwait(false); } @@ -593,23 +592,20 @@ namespace MediaBrowser.MediaEncoding.Subtitles var processArgs = string.Format("-i {0} -map 0:{1} -an -vn -c:s {2} \"{3}\"", inputPath, subtitleStreamIndex, outputCodec, outputPath); - var process = new Process + var process = _processFactory.Create(new ProcessOptions { - StartInfo = new ProcessStartInfo - { - CreateNoWindow = true, - UseShellExecute = false, + CreateNoWindow = true, + UseShellExecute = false, - RedirectStandardOutput = false, - RedirectStandardError = true, - RedirectStandardInput = true, + RedirectStandardOutput = false, + RedirectStandardError = true, + RedirectStandardInput = true, - FileName = _mediaEncoder.EncoderPath, - Arguments = processArgs, - WindowStyle = ProcessWindowStyle.Hidden, - ErrorDialog = false - } - }; + FileName = _mediaEncoder.EncoderPath, + Arguments = processArgs, + IsHidden = true, + ErrorDialog = false + }); _logger.Info("{0} {1}", process.StartInfo.FileName, process.StartInfo.Arguments); @@ -675,10 +671,6 @@ namespace MediaBrowser.MediaEncoding.Subtitles { } - catch (DirectoryNotFoundException) - { - - } catch (IOException ex) { _logger.ErrorException("Error deleting extracted subtitle {0}", ex, outputPath); @@ -695,7 +687,7 @@ namespace MediaBrowser.MediaEncoding.Subtitles _logger.Error(msg); - throw new ApplicationException(msg); + throw new Exception(msg); } else { @@ -749,20 +741,26 @@ namespace MediaBrowser.MediaEncoding.Subtitles string text; Encoding encoding; - using (var reader = new StreamReader(file, true)) + using (var fileStream = _fileSystem.OpenRead(file)) { - encoding = reader.CurrentEncoding; + using (var reader = new StreamReader(fileStream, true)) + { + encoding = reader.CurrentEncoding; - text = await reader.ReadToEndAsync().ConfigureAwait(false); + text = await reader.ReadToEndAsync().ConfigureAwait(false); + } } var newText = text.Replace(",Arial,", ",Arial Unicode MS,"); if (!string.Equals(text, newText)) { - using (var writer = new StreamWriter(file, false, encoding)) + using (var fileStream = _fileSystem.GetFileStream(file, FileOpenMode.Create, FileAccessMode.Write, FileShareMode.Read)) { - writer.Write(newText); + using (var writer = new StreamWriter(fileStream, encoding)) + { + writer.Write(newText); + } } } } @@ -795,7 +793,7 @@ namespace MediaBrowser.MediaEncoding.Subtitles { if (protocol == MediaProtocol.File) { - if (GetFileEncoding(path).Equals(Encoding.UTF8)) + if (_textEncoding.GetFileEncoding(path).Equals(Encoding.UTF8)) { return string.Empty; } @@ -902,29 +900,6 @@ namespace MediaBrowser.MediaEncoding.Subtitles return null; } - private Encoding GetFileEncoding(string srcFile) - { - // *** Detect byte order mark if any - otherwise assume default - var buffer = new byte[5]; - - using (var file = _fileSystem.GetFileStream(srcFile, FileOpenMode.Open, FileAccessMode.Read, FileShareMode.ReadWrite)) - { - file.Read(buffer, 0, 5); - } - - if (buffer[0] == 0xef && buffer[1] == 0xbb && buffer[2] == 0xbf) - return Encoding.UTF8; - if (buffer[0] == 0xfe && buffer[1] == 0xff) - return Encoding.Unicode; - if (buffer[0] == 0 && buffer[1] == 0 && buffer[2] == 0xfe && buffer[3] == 0xff) - return Encoding.UTF32; - if (buffer[0] == 0x2b && buffer[1] == 0x2f && buffer[2] == 0x76) - return Encoding.UTF7; - - // It's ok - anything aside from utf is ok since that's what we're looking for - return Encoding.Default; - } - private async Task<Stream> GetStream(string path, MediaProtocol protocol, CancellationToken cancellationToken) { if (protocol == MediaProtocol.Http) diff --git a/MediaBrowser.MediaEncoding/project.json b/MediaBrowser.MediaEncoding/project.json new file mode 100644 index 000000000..fbbe9eaf3 --- /dev/null +++ b/MediaBrowser.MediaEncoding/project.json @@ -0,0 +1,17 @@ +{ + "frameworks":{ + "netstandard1.6":{ + "dependencies":{ + "NETStandard.Library":"1.6.0", + } + }, + ".NETPortable,Version=v4.5,Profile=Profile7":{ + "buildOptions": { + "define": [ ] + }, + "frameworkAssemblies":{ + + } + } + } +}
\ No newline at end of file diff --git a/MediaBrowser.Model/Diagnostics/IProcessFactory.cs b/MediaBrowser.Model/Diagnostics/IProcessFactory.cs index 998bbcd28..a60c4b4fb 100644 --- a/MediaBrowser.Model/Diagnostics/IProcessFactory.cs +++ b/MediaBrowser.Model/Diagnostics/IProcessFactory.cs @@ -1,8 +1,4 @@ using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; namespace MediaBrowser.Model.Diagnostics { @@ -22,6 +18,7 @@ namespace MediaBrowser.Model.Diagnostics public bool ErrorDialog { get; set; } public bool RedirectStandardError { get; set; } public bool RedirectStandardInput { get; set; } + public bool RedirectStandardOutput { get; set; } public bool IsHidden { get; set; } } } diff --git a/MediaBrowser.Model/TextEncoding/IEncoding.cs b/MediaBrowser.Model/TextEncoding/IEncoding.cs index 3b9652e3a..3d884c9d2 100644 --- a/MediaBrowser.Model/TextEncoding/IEncoding.cs +++ b/MediaBrowser.Model/TextEncoding/IEncoding.cs @@ -1,9 +1,12 @@ - +using System.Text; + namespace MediaBrowser.Model.TextEncoding { public interface IEncoding { byte[] GetASCIIBytes(string text); string GetASCIIString(byte[] bytes, int startIndex, int length); + + Encoding GetFileEncoding(string path); } } diff --git a/MediaBrowser.Server.Implementations/MediaBrowser.Server.Implementations.csproj b/MediaBrowser.Server.Implementations/MediaBrowser.Server.Implementations.csproj index e6f203120..24fe077c2 100644 --- a/MediaBrowser.Server.Implementations/MediaBrowser.Server.Implementations.csproj +++ b/MediaBrowser.Server.Implementations/MediaBrowser.Server.Implementations.csproj @@ -70,6 +70,9 @@ <Reference Include="ServiceStack.Api.Swagger"> <HintPath>..\ThirdParty\ServiceStack\ServiceStack.Api.Swagger.dll</HintPath> </Reference> + <Reference Include="ServiceStack.Common"> + <HintPath>..\ThirdParty\ServiceStack\ServiceStack.Common.dll</HintPath> + </Reference> <Reference Include="SharpCompress, Version=0.10.3.0, Culture=neutral, processorArchitecture=MSIL"> <SpecificVersion>False</SpecificVersion> <HintPath>..\ThirdParty\SharpCompress\SharpCompress.dll</HintPath> diff --git a/MediaBrowser.Server.Implementations/TextEncoding/TextEncoding.cs b/MediaBrowser.Server.Implementations/TextEncoding/TextEncoding.cs index c1029dfb5..4c047b7d5 100644 --- a/MediaBrowser.Server.Implementations/TextEncoding/TextEncoding.cs +++ b/MediaBrowser.Server.Implementations/TextEncoding/TextEncoding.cs @@ -1,10 +1,18 @@ using System.Text; +using MediaBrowser.Model.IO; using MediaBrowser.Model.TextEncoding; namespace MediaBrowser.Server.Implementations.TextEncoding { - public class TextEncoding : IEncoding + public class TextEncoding : IEncoding { + private readonly IFileSystem _fileSystem; + + public TextEncoding(IFileSystem fileSystem) + { + _fileSystem = fileSystem; + } + public byte[] GetASCIIBytes(string text) { return Encoding.ASCII.GetBytes(text); @@ -14,5 +22,28 @@ namespace MediaBrowser.Server.Implementations.TextEncoding { return Encoding.ASCII.GetString(bytes, 0, bytes.Length); } + + public Encoding GetFileEncoding(string srcFile) + { + // *** Detect byte order mark if any - otherwise assume default + var buffer = new byte[5]; + + using (var file = _fileSystem.OpenRead(srcFile)) + { + file.Read(buffer, 0, 5); + } + + if (buffer[0] == 0xef && buffer[1] == 0xbb && buffer[2] == 0xbf) + return Encoding.UTF8; + if (buffer[0] == 0xfe && buffer[1] == 0xff) + return Encoding.Unicode; + if (buffer[0] == 0 && buffer[1] == 0 && buffer[2] == 0xfe && buffer[3] == 0xff) + return Encoding.UTF32; + if (buffer[0] == 0x2b && buffer[1] == 0x2f && buffer[2] == 0x76) + return Encoding.UTF7; + + // It's ok - anything aside from utf is ok since that's what we're looking for + return Encoding.Default; + } } } diff --git a/MediaBrowser.Server.Startup.Common/ApplicationHost.cs b/MediaBrowser.Server.Startup.Common/ApplicationHost.cs index 7ff50df63..2e86b6c3e 100644 --- a/MediaBrowser.Server.Startup.Common/ApplicationHost.cs +++ b/MediaBrowser.Server.Startup.Common/ApplicationHost.cs @@ -547,7 +547,7 @@ namespace MediaBrowser.Server.Startup.Common StringExtensions.LocalizationManager = LocalizationManager; RegisterSingleInstance(LocalizationManager); - IEncoding textEncoding = new TextEncoding(); + IEncoding textEncoding = new TextEncoding(FileSystemManager); RegisterSingleInstance(textEncoding); Utilities.EncodingHelper = textEncoding; RegisterSingleInstance<IBlurayExaminer>(() => new BdInfoExaminer(FileSystemManager, textEncoding)); @@ -697,7 +697,7 @@ namespace MediaBrowser.Server.Startup.Common RegisterSingleInstance<ISessionContext>(new SessionContext(UserManager, authContext, SessionManager)); RegisterSingleInstance<IAuthService>(new AuthService(UserManager, authContext, ServerConfigurationManager, ConnectManager, SessionManager, DeviceManager)); - SubtitleEncoder = new SubtitleEncoder(LibraryManager, LogManager.GetLogger("SubtitleEncoder"), ApplicationPaths, FileSystemManager, MediaEncoder, JsonSerializer, HttpClient, MediaSourceManager, MemoryStreamProvider); + SubtitleEncoder = new SubtitleEncoder(LibraryManager, LogManager.GetLogger("SubtitleEncoder"), ApplicationPaths, FileSystemManager, MediaEncoder, JsonSerializer, HttpClient, MediaSourceManager, MemoryStreamProvider, ProcessFactory, textEncoding); RegisterSingleInstance(SubtitleEncoder); await displayPreferencesRepo.Initialize().ConfigureAwait(false); @@ -789,7 +789,10 @@ namespace MediaBrowser.Server.Startup.Common () => SubtitleEncoder, () => MediaSourceManager, HttpClient, - ZipClient, MemoryStreamProvider); + ZipClient, MemoryStreamProvider, + ProcessFactory, + Environment.Is64BitOperatingSystem ? (Environment.ProcessorCount > 2 ? 14000 : 20000) : 40000, + Environment.OSVersion.Platform == PlatformID.Win32NT); MediaEncoder = mediaEncoder; RegisterSingleInstance(MediaEncoder); |
