Add some analyzers to MediaBrowser.MediaEncoding

pull/3820/head
Bond-009 4 years ago
parent 18efa25a6f
commit 53f99d5d4b

@ -1,3 +1,5 @@
#pragma warning disable CS1591
using System; using System;
using System.Linq; using System.Linq;
using BDInfo.IO; using BDInfo.IO;
@ -5,7 +7,7 @@ using MediaBrowser.Model.IO;
namespace MediaBrowser.MediaEncoding.BdInfo namespace MediaBrowser.MediaEncoding.BdInfo
{ {
class BdInfoDirectoryInfo : IDirectoryInfo public class BdInfoDirectoryInfo : IDirectoryInfo
{ {
private readonly IFileSystem _fileSystem = null; private readonly IFileSystem _fileSystem = null;
@ -43,25 +45,32 @@ namespace MediaBrowser.MediaEncoding.BdInfo
public IDirectoryInfo[] GetDirectories() public IDirectoryInfo[] GetDirectories()
{ {
return Array.ConvertAll(_fileSystem.GetDirectories(_impl.FullName).ToArray(), return Array.ConvertAll(
_fileSystem.GetDirectories(_impl.FullName).ToArray(),
x => new BdInfoDirectoryInfo(_fileSystem, x)); x => new BdInfoDirectoryInfo(_fileSystem, x));
} }
public IFileInfo[] GetFiles() public IFileInfo[] GetFiles()
{ {
return Array.ConvertAll(_fileSystem.GetFiles(_impl.FullName).ToArray(), return Array.ConvertAll(
_fileSystem.GetFiles(_impl.FullName).ToArray(),
x => new BdInfoFileInfo(x)); x => new BdInfoFileInfo(x));
} }
public IFileInfo[] GetFiles(string searchPattern) public IFileInfo[] GetFiles(string searchPattern)
{ {
return Array.ConvertAll(_fileSystem.GetFiles(_impl.FullName, new[] { searchPattern }, false, false).ToArray(), return Array.ConvertAll(
_fileSystem.GetFiles(_impl.FullName, new[] { searchPattern }, false, false).ToArray(),
x => new BdInfoFileInfo(x)); x => new BdInfoFileInfo(x));
} }
public IFileInfo[] GetFiles(string searchPattern, System.IO.SearchOption searchOption) public IFileInfo[] GetFiles(string searchPattern, System.IO.SearchOption searchOption)
{ {
return Array.ConvertAll(_fileSystem.GetFiles(_impl.FullName, new[] { searchPattern }, false, return Array.ConvertAll(
_fileSystem.GetFiles(
_impl.FullName,
new[] { searchPattern },
false,
searchOption.HasFlag(System.IO.SearchOption.AllDirectories)).ToArray(), searchOption.HasFlag(System.IO.SearchOption.AllDirectories)).ToArray(),
x => new BdInfoFileInfo(x)); x => new BdInfoFileInfo(x));
} }

@ -1,11 +1,18 @@
#pragma warning disable CS1591
using System.IO; using System.IO;
using MediaBrowser.Model.IO; using MediaBrowser.Model.IO;
namespace MediaBrowser.MediaEncoding.BdInfo namespace MediaBrowser.MediaEncoding.BdInfo
{ {
class BdInfoFileInfo : BDInfo.IO.IFileInfo public class BdInfoFileInfo : BDInfo.IO.IFileInfo
{ {
FileSystemMetadata _impl = null; private FileSystemMetadata _impl = null;
public BdInfoFileInfo(FileSystemMetadata impl)
{
_impl = impl;
}
public string Name => _impl.Name; public string Name => _impl.Name;
@ -17,14 +24,10 @@ namespace MediaBrowser.MediaEncoding.BdInfo
public bool IsDir => _impl.IsDirectory; public bool IsDir => _impl.IsDirectory;
public BdInfoFileInfo(FileSystemMetadata impl)
{
_impl = impl;
}
public System.IO.Stream OpenRead() public System.IO.Stream OpenRead()
{ {
return new FileStream(FullName, return new FileStream(
FullName,
FileMode.Open, FileMode.Open,
FileAccess.Read, FileAccess.Read,
FileShare.Read); FileShare.Read);

@ -1,11 +1,7 @@
#pragma warning disable CS1591 #pragma warning disable CS1591
using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Globalization;
using System.IO;
using MediaBrowser.Common.Configuration; using MediaBrowser.Common.Configuration;
using MediaBrowser.Model.Configuration;
namespace MediaBrowser.MediaEncoding.Configuration namespace MediaBrowser.MediaEncoding.Configuration
{ {
@ -19,32 +15,4 @@ namespace MediaBrowser.MediaEncoding.Configuration
}; };
} }
} }
public class EncodingConfigurationStore : ConfigurationStore, IValidatingConfiguration
{
public EncodingConfigurationStore()
{
ConfigurationType = typeof(EncodingOptions);
Key = "encoding";
}
public void Validate(object oldConfig, object newConfig)
{
var newPath = ((EncodingOptions)newConfig).TranscodingTempPath;
if (!string.IsNullOrWhiteSpace(newPath)
&& !string.Equals(((EncodingOptions)oldConfig).TranscodingTempPath, newPath, StringComparison.Ordinal))
{
// Validate
if (!Directory.Exists(newPath))
{
throw new DirectoryNotFoundException(
string.Format(
CultureInfo.InvariantCulture,
"{0} does not exist.",
newPath));
}
}
}
}
} }

@ -0,0 +1,38 @@
#pragma warning disable CS1591
using System;
using System.Globalization;
using System.IO;
using MediaBrowser.Common.Configuration;
using MediaBrowser.Model.Configuration;
namespace MediaBrowser.MediaEncoding.Configuration
{
public class EncodingConfigurationStore : ConfigurationStore, IValidatingConfiguration
{
public EncodingConfigurationStore()
{
ConfigurationType = typeof(EncodingOptions);
Key = "encoding";
}
public void Validate(object oldConfig, object newConfig)
{
var newPath = ((EncodingOptions)newConfig).TranscodingTempPath;
if (!string.IsNullOrWhiteSpace(newPath)
&& !string.Equals(((EncodingOptions)oldConfig).TranscodingTempPath, newPath, StringComparison.Ordinal))
{
// Validate
if (!Directory.Exists(newPath))
{
throw new DirectoryNotFoundException(
string.Format(
CultureInfo.InvariantCulture,
"{0} does not exist.",
newPath));
}
}
}
}
}

@ -14,7 +14,7 @@ namespace MediaBrowser.MediaEncoding.Encoder
{ {
private const string DefaultEncoderPath = "ffmpeg"; private const string DefaultEncoderPath = "ffmpeg";
private static readonly string[] requiredDecoders = new[] private static readonly string[] _requiredDecoders = new[]
{ {
"h264", "h264",
"hevc", "hevc",
@ -57,7 +57,7 @@ namespace MediaBrowser.MediaEncoding.Encoder
"vc1_opencl" "vc1_opencl"
}; };
private static readonly string[] requiredEncoders = new[] private static readonly string[] _requiredEncoders = new[]
{ {
"libx264", "libx264",
"libx265", "libx265",
@ -112,6 +112,12 @@ namespace MediaBrowser.MediaEncoding.Encoder
_encoderPath = encoderPath; _encoderPath = encoderPath;
} }
private enum Codec
{
Encoder,
Decoder
}
public static Version MinVersion { get; } = new Version(4, 0); public static Version MinVersion { get; } = new Version(4, 0);
public static Version MaxVersion { get; } = null; public static Version MaxVersion { get; } = null;
@ -195,8 +201,8 @@ namespace MediaBrowser.MediaEncoding.Encoder
/// If that fails then we use one of the main libraries to determine if it's new/older than the latest /// If that fails then we use one of the main libraries to determine if it's new/older than the latest
/// we have stored. /// we have stored.
/// </summary> /// </summary>
/// <param name="output"></param> /// <param name="output">The output from "ffmpeg -version".</param>
/// <returns></returns> /// <returns>The FFmpeg version.</returns>
internal static Version GetFFmpegVersion(string output) internal static Version GetFFmpegVersion(string output)
{ {
// For pre-built binaries the FFmpeg version should be mentioned at the very start of the output // For pre-built binaries the FFmpeg version should be mentioned at the very start of the output
@ -218,10 +224,10 @@ namespace MediaBrowser.MediaEncoding.Encoder
/// <summary> /// <summary>
/// Grabs the library names and major.minor version numbers from the 'ffmpeg -version' output /// Grabs the library names and major.minor version numbers from the 'ffmpeg -version' output
/// and condenses them on to one line. Output format is "name1=major.minor,name2=major.minor,etc." /// and condenses them on to one line. Output format is "name1=major.minor,name2=major.minor,etc.".
/// </summary> /// </summary>
/// <param name="output"></param> /// <param name="output">The 'ffmpeg -version' output.</param>
/// <returns></returns> /// <returns>The library names and major.minor version numbers.</returns>
private static string GetLibrariesVersionString(string output) private static string GetLibrariesVersionString(string output)
{ {
var rc = new StringBuilder(144); var rc = new StringBuilder(144);
@ -241,12 +247,6 @@ namespace MediaBrowser.MediaEncoding.Encoder
return rc.Length == 0 ? null : rc.ToString(); return rc.Length == 0 ? null : rc.ToString();
} }
private enum Codec
{
Encoder,
Decoder
}
private IEnumerable<string> GetHwaccelTypes() private IEnumerable<string> GetHwaccelTypes()
{ {
string output = null; string output = null;
@ -264,7 +264,7 @@ namespace MediaBrowser.MediaEncoding.Encoder
return Enumerable.Empty<string>(); return Enumerable.Empty<string>();
} }
var found = output.Split(new char[] {'\r','\n'}, StringSplitOptions.RemoveEmptyEntries).Skip(1).Distinct().ToList(); var found = output.Split(new char[] { '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries).Skip(1).Distinct().ToList();
_logger.LogInformation("Available hwaccel types: {Types}", found); _logger.LogInformation("Available hwaccel types: {Types}", found);
return found; return found;
@ -288,7 +288,7 @@ namespace MediaBrowser.MediaEncoding.Encoder
return Enumerable.Empty<string>(); return Enumerable.Empty<string>();
} }
var required = codec == Codec.Encoder ? requiredEncoders : requiredDecoders; var required = codec == Codec.Encoder ? _requiredEncoders : _requiredDecoders;
var found = Regex var found = Regex
.Matches(output, @"^\s\S{6}\s(?<codec>[\w|-]+)\s+.+$", RegexOptions.Multiline) .Matches(output, @"^\s\S{6}\s(?<codec>[\w|-]+)\s+.+$", RegexOptions.Multiline)

@ -2,6 +2,7 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization; using System.Globalization;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
@ -21,9 +22,8 @@ using MediaBrowser.Model.Globalization;
using MediaBrowser.Model.IO; using MediaBrowser.Model.IO;
using MediaBrowser.Model.MediaInfo; using MediaBrowser.Model.MediaInfo;
using MediaBrowser.Model.System; using MediaBrowser.Model.System;
using Microsoft.Extensions.Logging;
using System.Diagnostics;
using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Logging;
namespace MediaBrowser.MediaEncoding.Encoder namespace MediaBrowser.MediaEncoding.Encoder
{ {
@ -37,6 +37,11 @@ namespace MediaBrowser.MediaEncoding.Encoder
/// </summary> /// </summary>
internal const int DefaultImageExtractionTimeout = 5000; internal const int DefaultImageExtractionTimeout = 5000;
/// <summary>
/// The us culture.
/// </summary>
private readonly CultureInfo _usCulture = new CultureInfo("en-US");
private readonly ILogger<MediaEncoder> _logger; private readonly ILogger<MediaEncoder> _logger;
private readonly IServerConfigurationManager _configurationManager; private readonly IServerConfigurationManager _configurationManager;
private readonly IFileSystem _fileSystem; private readonly IFileSystem _fileSystem;
@ -49,6 +54,10 @@ namespace MediaBrowser.MediaEncoding.Encoder
private readonly object _runningProcessesLock = new object(); private readonly object _runningProcessesLock = new object();
private readonly List<ProcessWrapper> _runningProcesses = new List<ProcessWrapper>(); private readonly List<ProcessWrapper> _runningProcesses = new List<ProcessWrapper>();
private List<string> _encoders = new List<string>();
private List<string> _decoders = new List<string>();
private List<string> _hwaccels = new List<string>();
private string _ffmpegPath = string.Empty; private string _ffmpegPath = string.Empty;
private string _ffprobePath; private string _ffprobePath;
@ -79,7 +88,7 @@ namespace MediaBrowser.MediaEncoding.Encoder
/// <summary> /// <summary>
/// Run at startup or if the user removes a Custom path from transcode page. /// Run at startup or if the user removes a Custom path from transcode page.
/// Sets global variables FFmpegPath. /// Sets global variables FFmpegPath.
/// Precedence is: Config > CLI > $PATH /// Precedence is: Config > CLI > $PATH.
/// </summary> /// </summary>
public void SetFFmpegPath() public void SetFFmpegPath()
{ {
@ -124,8 +133,8 @@ namespace MediaBrowser.MediaEncoding.Encoder
/// Triggered from the Settings > Transcoding UI page when users submits Custom FFmpeg path to use. /// Triggered from the Settings > Transcoding UI page when users submits Custom FFmpeg path to use.
/// Only write the new path to xml if it exists. Do not perform validation checks on ffmpeg here. /// Only write the new path to xml if it exists. Do not perform validation checks on ffmpeg here.
/// </summary> /// </summary>
/// <param name="path"></param> /// <param name="path">The path.</param>
/// <param name="pathType"></param> /// <param name="pathType">The path type.</param>
public void UpdateEncoderPath(string path, string pathType) public void UpdateEncoderPath(string path, string pathType)
{ {
string newPath; string newPath;
@ -170,8 +179,8 @@ namespace MediaBrowser.MediaEncoding.Encoder
/// If checks pass, global variable FFmpegPath and EncoderLocation are updated. /// If checks pass, global variable FFmpegPath and EncoderLocation are updated.
/// </summary> /// </summary>
/// <param name="path">FQPN to test.</param> /// <param name="path">FQPN to test.</param>
/// <param name="location">Location (External, Custom, System) of tool</param> /// <param name="location">Location (External, Custom, System) of tool.</param>
/// <returns></returns> /// <returns><c>true</c> if the version validation succeeded; otherwise, <c>false</c>.</returns>
private bool ValidatePath(string path, FFmpegLocation location) private bool ValidatePath(string path, FFmpegLocation location)
{ {
bool rc = false; bool rc = false;
@ -223,8 +232,8 @@ namespace MediaBrowser.MediaEncoding.Encoder
/// <summary> /// <summary>
/// Search the system $PATH environment variable looking for given filename. /// Search the system $PATH environment variable looking for given filename.
/// </summary> /// </summary>
/// <param name="fileName"></param> /// <param name="fileName">The filename.</param>
/// <returns></returns> /// <returns>The full path to the file.</returns>
private string ExistsOnSystemPath(string fileName) private string ExistsOnSystemPath(string fileName)
{ {
var inJellyfinPath = GetEncoderPathFromDirectory(AppContext.BaseDirectory, fileName, recursive: true); var inJellyfinPath = GetEncoderPathFromDirectory(AppContext.BaseDirectory, fileName, recursive: true);
@ -248,25 +257,19 @@ namespace MediaBrowser.MediaEncoding.Encoder
return null; return null;
} }
private List<string> _encoders = new List<string>();
public void SetAvailableEncoders(IEnumerable<string> list) public void SetAvailableEncoders(IEnumerable<string> list)
{ {
_encoders = list.ToList(); _encoders = list.ToList();
// _logger.Info("Supported encoders: {0}", string.Join(",", list.ToArray()));
} }
private List<string> _decoders = new List<string>();
public void SetAvailableDecoders(IEnumerable<string> list) public void SetAvailableDecoders(IEnumerable<string> list)
{ {
_decoders = list.ToList(); _decoders = list.ToList();
// _logger.Info("Supported decoders: {0}", string.Join(",", list.ToArray()));
} }
private List<string> _hwaccels = new List<string>();
public void SetAvailableHwaccels(IEnumerable<string> list) public void SetAvailableHwaccels(IEnumerable<string> list)
{ {
_hwaccels = list.ToList(); _hwaccels = list.ToList();
//_logger.Info("Supported hwaccels: {0}", string.Join(",", list.ToArray()));
} }
public bool SupportsEncoder(string encoder) public bool SupportsEncoder(string encoder)
@ -334,8 +337,16 @@ namespace MediaBrowser.MediaEncoding.Encoder
var forceEnableLogging = request.MediaSource.Protocol != MediaProtocol.File; var forceEnableLogging = request.MediaSource.Protocol != MediaProtocol.File;
return GetMediaInfoInternal(GetInputArgument(inputFiles, request.MediaSource.Protocol), request.MediaSource.Path, request.MediaSource.Protocol, extractChapters, return GetMediaInfoInternal(
probeSize, request.MediaType == DlnaProfileType.Audio, request.MediaSource.VideoType, forceEnableLogging, cancellationToken); GetInputArgument(inputFiles, request.MediaSource.Protocol),
request.MediaSource.Path,
request.MediaSource.Protocol,
extractChapters,
probeSize,
request.MediaType == DlnaProfileType.Audio,
request.MediaSource.VideoType,
forceEnableLogging,
cancellationToken);
} }
/// <summary> /// <summary>
@ -344,7 +355,7 @@ namespace MediaBrowser.MediaEncoding.Encoder
/// <param name="inputFiles">The input files.</param> /// <param name="inputFiles">The input files.</param>
/// <param name="protocol">The protocol.</param> /// <param name="protocol">The protocol.</param>
/// <returns>System.String.</returns> /// <returns>System.String.</returns>
/// <exception cref="ArgumentException">Unrecognized InputType</exception> /// <exception cref="ArgumentException">Unrecognized InputType.</exception>
public string GetInputArgument(IReadOnlyList<string> inputFiles, MediaProtocol protocol) public string GetInputArgument(IReadOnlyList<string> inputFiles, MediaProtocol protocol)
=> EncodingUtils.GetInputArgument(inputFiles, protocol); => EncodingUtils.GetInputArgument(inputFiles, protocol);
@ -352,7 +363,8 @@ namespace MediaBrowser.MediaEncoding.Encoder
/// Gets the media info internal. /// Gets the media info internal.
/// </summary> /// </summary>
/// <returns>Task{MediaInfoResult}.</returns> /// <returns>Task{MediaInfoResult}.</returns>
private async Task<MediaInfo> GetMediaInfoInternal(string inputPath, private async Task<MediaInfo> GetMediaInfoInternal(
string inputPath,
string primaryPath, string primaryPath,
MediaProtocol protocol, MediaProtocol protocol,
bool extractChapters, bool extractChapters,
@ -380,7 +392,6 @@ namespace MediaBrowser.MediaEncoding.Encoder
FileName = _ffprobePath, FileName = _ffprobePath,
Arguments = args, Arguments = args,
WindowStyle = ProcessWindowStyle.Hidden, WindowStyle = ProcessWindowStyle.Hidden,
ErrorDialog = false, ErrorDialog = false,
}, },
@ -441,11 +452,6 @@ namespace MediaBrowser.MediaEncoding.Encoder
} }
} }
/// <summary>
/// The us culture.
/// </summary>
protected readonly CultureInfo UsCulture = new CultureInfo("en-US");
public Task<string> ExtractAudioImage(string path, int? imageStreamIndex, CancellationToken cancellationToken) public Task<string> ExtractAudioImage(string path, int? imageStreamIndex, CancellationToken cancellationToken)
{ {
return ExtractImage(new[] { path }, null, null, imageStreamIndex, MediaProtocol.File, true, null, null, cancellationToken); return ExtractImage(new[] { path }, null, null, imageStreamIndex, MediaProtocol.File, true, null, null, cancellationToken);
@ -461,8 +467,16 @@ namespace MediaBrowser.MediaEncoding.Encoder
return ExtractImage(inputFiles, container, imageStream, imageStreamIndex, protocol, false, null, null, cancellationToken); return ExtractImage(inputFiles, container, imageStream, imageStreamIndex, protocol, false, null, null, cancellationToken);
} }
private async Task<string> ExtractImage(string[] inputFiles, string container, MediaStream videoStream, int? imageStreamIndex, MediaProtocol protocol, bool isAudio, private async Task<string> ExtractImage(
Video3DFormat? threedFormat, TimeSpan? offset, CancellationToken cancellationToken) string[] inputFiles,
string container,
MediaStream videoStream,
int? imageStreamIndex,
MediaProtocol protocol,
bool isAudio,
Video3DFormat? threedFormat,
TimeSpan? offset,
CancellationToken cancellationToken)
{ {
var inputArgument = GetInputArgument(inputFiles, protocol); var inputArgument = GetInputArgument(inputFiles, protocol);
@ -647,7 +661,7 @@ namespace MediaBrowser.MediaEncoding.Encoder
public string GetTimeParameter(TimeSpan time) public string GetTimeParameter(TimeSpan time)
{ {
return time.ToString(@"hh\:mm\:ss\.fff", UsCulture); return time.ToString(@"hh\:mm\:ss\.fff", _usCulture);
} }
public async Task ExtractVideoImagesOnInterval( public async Task ExtractVideoImagesOnInterval(
@ -664,11 +678,11 @@ namespace MediaBrowser.MediaEncoding.Encoder
{ {
var inputArgument = GetInputArgument(inputFiles, protocol); var inputArgument = GetInputArgument(inputFiles, protocol);
var vf = "fps=fps=1/" + interval.TotalSeconds.ToString(UsCulture); var vf = "fps=fps=1/" + interval.TotalSeconds.ToString(_usCulture);
if (maxWidth.HasValue) if (maxWidth.HasValue)
{ {
var maxWidthParam = maxWidth.Value.ToString(UsCulture); var maxWidthParam = maxWidth.Value.ToString(_usCulture);
vf += string.Format(",scale=min(iw\\,{0}):trunc(ow/dar/2)*2", maxWidthParam); vf += string.Format(",scale=min(iw\\,{0}):trunc(ow/dar/2)*2", maxWidthParam);
} }

@ -28,4 +28,16 @@
<PackageReference Include="UTF.Unknown" Version="2.3.0" /> <PackageReference Include="UTF.Unknown" Version="2.3.0" />
</ItemGroup> </ItemGroup>
<PropertyGroup Condition=" '$(Configuration)' == 'Debug' ">
<CodeAnalysisRuleSet>../jellyfin.ruleset</CodeAnalysisRuleSet>
</PropertyGroup>
<!-- Code Analyzers-->
<ItemGroup Condition=" '$(Configuration)' == 'Debug' ">
<!-- <PackageReference Include="Microsoft.CodeAnalysis.FxCopAnalyzers" Version="2.9.8" PrivateAssets="All" /> -->
<PackageReference Include="SerilogAnalyzer" Version="0.15.0" PrivateAssets="All" />
<PackageReference Include="StyleCop.Analyzers" Version="1.1.118" PrivateAssets="All" />
<PackageReference Include="SmartAnalyzers.MultithreadingAnalyzer" Version="1.1.31" PrivateAssets="All" />
</ItemGroup>
</Project> </Project>

@ -18,10 +18,19 @@ namespace MediaBrowser.MediaEncoding.Probing
{ {
public class ProbeResultNormalizer public class ProbeResultNormalizer
{ {
// When extracting subtitles, the maximum length to consider (to avoid invalid filenames)
private const int MaxSubtitleDescriptionExtractionLength = 100;
private const string ArtistReplaceValue = " | ";
private readonly char[] _nameDelimiters = { '/', '|', ';', '\\' };
private readonly CultureInfo _usCulture = new CultureInfo("en-US"); private readonly CultureInfo _usCulture = new CultureInfo("en-US");
private readonly ILogger _logger; private readonly ILogger _logger;
private readonly ILocalizationManager _localization; private readonly ILocalizationManager _localization;
private List<string> _splitWhiteList = null;
public ProbeResultNormalizer(ILogger logger, ILocalizationManager localization) public ProbeResultNormalizer(ILogger logger, ILocalizationManager localization)
{ {
_logger = logger; _logger = logger;
@ -370,7 +379,6 @@ namespace MediaBrowser.MediaEncoding.Probing
private List<NameValuePair> ReadValueArray(XmlReader reader) private List<NameValuePair> ReadValueArray(XmlReader reader)
{ {
var pairs = new List<NameValuePair>(); var pairs = new List<NameValuePair>();
reader.MoveToContent(); reader.MoveToContent();
@ -951,50 +959,46 @@ namespace MediaBrowser.MediaEncoding.Probing
private void SetAudioInfoFromTags(MediaInfo audio, Dictionary<string, string> tags) private void SetAudioInfoFromTags(MediaInfo audio, Dictionary<string, string> tags)
{ {
var peoples = new List<BaseItemPerson>();
var composer = FFProbeHelpers.GetDictionaryValue(tags, "composer"); var composer = FFProbeHelpers.GetDictionaryValue(tags, "composer");
if (!string.IsNullOrWhiteSpace(composer)) if (!string.IsNullOrWhiteSpace(composer))
{ {
var peoples = new List<BaseItemPerson>();
foreach (var person in Split(composer, false)) foreach (var person in Split(composer, false))
{ {
peoples.Add(new BaseItemPerson { Name = person, Type = PersonType.Composer }); peoples.Add(new BaseItemPerson { Name = person, Type = PersonType.Composer });
} }
audio.People = peoples.ToArray();
} }
// var conductor = FFProbeHelpers.GetDictionaryValue(tags, "conductor"); var conductor = FFProbeHelpers.GetDictionaryValue(tags, "conductor");
// if (!string.IsNullOrWhiteSpace(conductor)) if (!string.IsNullOrWhiteSpace(conductor))
//{ {
// foreach (var person in Split(conductor, false)) foreach (var person in Split(conductor, false))
// { {
// audio.People.Add(new BaseItemPerson { Name = person, Type = PersonType.Conductor }); peoples.Add(new BaseItemPerson { Name = person, Type = PersonType.Conductor });
// } }
//} }
// var lyricist = FFProbeHelpers.GetDictionaryValue(tags, "lyricist"); var lyricist = FFProbeHelpers.GetDictionaryValue(tags, "lyricist");
// if (!string.IsNullOrWhiteSpace(lyricist)) if (!string.IsNullOrWhiteSpace(lyricist))
//{ {
// foreach (var person in Split(lyricist, false)) foreach (var person in Split(lyricist, false))
// { {
// audio.People.Add(new BaseItemPerson { Name = person, Type = PersonType.Lyricist }); peoples.Add(new BaseItemPerson { Name = person, Type = PersonType.Lyricist });
// } }
//} }
// Check for writer some music is tagged that way as alternative to composer/lyricist // Check for writer some music is tagged that way as alternative to composer/lyricist
var writer = FFProbeHelpers.GetDictionaryValue(tags, "writer"); var writer = FFProbeHelpers.GetDictionaryValue(tags, "writer");
if (!string.IsNullOrWhiteSpace(writer)) if (!string.IsNullOrWhiteSpace(writer))
{ {
var peoples = new List<BaseItemPerson>();
foreach (var person in Split(writer, false)) foreach (var person in Split(writer, false))
{ {
peoples.Add(new BaseItemPerson { Name = person, Type = PersonType.Writer }); peoples.Add(new BaseItemPerson { Name = person, Type = PersonType.Writer });
} }
audio.People = peoples.ToArray();
} }
audio.People = peoples.ToArray();
audio.Album = FFProbeHelpers.GetDictionaryValue(tags, "album"); audio.Album = FFProbeHelpers.GetDictionaryValue(tags, "album");
var artists = FFProbeHelpers.GetDictionaryValue(tags, "artists"); var artists = FFProbeHelpers.GetDictionaryValue(tags, "artists");
@ -1119,8 +1123,6 @@ namespace MediaBrowser.MediaEncoding.Probing
.FirstOrDefault(i => !string.IsNullOrWhiteSpace(i)); .FirstOrDefault(i => !string.IsNullOrWhiteSpace(i));
} }
private readonly char[] _nameDelimiters = { '/', '|', ';', '\\' };
/// <summary> /// <summary>
/// Splits the specified val. /// Splits the specified val.
/// </summary> /// </summary>
@ -1140,8 +1142,6 @@ namespace MediaBrowser.MediaEncoding.Probing
.Select(i => i.Trim()); .Select(i => i.Trim());
} }
private const string ArtistReplaceValue = " | ";
private IEnumerable<string> SplitArtists(string val, char[] delimiters, bool splitFeaturing) private IEnumerable<string> SplitArtists(string val, char[] delimiters, bool splitFeaturing)
{ {
if (splitFeaturing) if (splitFeaturing)
@ -1171,9 +1171,6 @@ namespace MediaBrowser.MediaEncoding.Probing
return artistsFound; return artistsFound;
} }
private List<string> _splitWhiteList = null;
private IEnumerable<string> GetSplitWhitelist() private IEnumerable<string> GetSplitWhitelist()
{ {
if (_splitWhiteList == null) if (_splitWhiteList == null)
@ -1250,7 +1247,7 @@ namespace MediaBrowser.MediaEncoding.Probing
} }
/// <summary> /// <summary>
/// Gets the disc number, which is sometimes can be in the form of '1', or '1/3' /// Gets the disc number, which is sometimes can be in the form of '1', or '1/3'.
/// </summary> /// </summary>
/// <param name="tags">The tags.</param> /// <param name="tags">The tags.</param>
/// <param name="tagName">Name of the tag.</param> /// <param name="tagName">Name of the tag.</param>
@ -1296,8 +1293,6 @@ namespace MediaBrowser.MediaEncoding.Probing
return info; return info;
} }
private const int MaxSubtitleDescriptionExtractionLength = 100; // When extracting subtitles, the maximum length to consider (to avoid invalid filenames)
private void FetchWtvInfo(MediaInfo video, InternalMediaInfoResult data) private void FetchWtvInfo(MediaInfo video, InternalMediaInfoResult data)
{ {
if (data.Format == null || data.Format.Tags == null) if (data.Format == null || data.Format.Tags == null)
@ -1382,8 +1377,8 @@ namespace MediaBrowser.MediaEncoding.Probing
if (subtitle.Contains('/', StringComparison.Ordinal)) // It contains a episode number and season number if (subtitle.Contains('/', StringComparison.Ordinal)) // It contains a episode number and season number
{ {
string[] numbers = subtitle.Split(' '); string[] numbers = subtitle.Split(' ');
video.IndexNumber = int.Parse(numbers[0].Replace(".", "").Split('/')[0]); video.IndexNumber = int.Parse(numbers[0].Replace(".", string.Empty, StringComparison.Ordinal).Split('/')[0]);
int totalEpisodesInSeason = int.Parse(numbers[0].Replace(".", "").Split('/')[1]); int totalEpisodesInSeason = int.Parse(numbers[0].Replace(".", string.Empty, StringComparison.Ordinal).Split('/')[1]);
description = string.Join(" ", numbers, 1, numbers.Length - 1).Trim(); // Skip the first, concatenate the rest, clean up spaces and save it description = string.Join(" ", numbers, 1, numbers.Length - 1).Trim(); // Skip the first, concatenate the rest, clean up spaces and save it
} }

@ -25,7 +25,8 @@ namespace MediaBrowser.MediaEncoding.Subtitles
{ {
string line; string line;
while (reader.ReadLine() != "[Events]") while (reader.ReadLine() != "[Events]")
{ } {
}
var headers = ParseFieldHeaders(reader.ReadLine()); var headers = ParseFieldHeaders(reader.ReadLine());
@ -75,17 +76,14 @@ namespace MediaBrowser.MediaEncoding.Subtitles
{ {
var fields = line.Substring(8).Split(',').Select(x => x.Trim()).ToList(); var fields = line.Substring(8).Split(',').Select(x => x.Trim()).ToList();
var result = new Dictionary<string, int> { return new Dictionary<string, int>
{"Start", fields.IndexOf("Start")}, {
{"End", fields.IndexOf("End")}, { "Start", fields.IndexOf("Start") },
{"Text", fields.IndexOf("Text")} { "End", fields.IndexOf("End") },
}; { "Text", fields.IndexOf("Text") }
return result; };
} }
/// <summary>
/// Credit: https://github.com/SubtitleEdit/subtitleedit/blob/master/src/Logic/SubtitleFormats/AdvancedSubStationAlpha.cs
/// </summary>
private void RemoteNativeFormatting(SubtitleTrackEvent p) private void RemoteNativeFormatting(SubtitleTrackEvent p)
{ {
int indexOfBegin = p.Text.IndexOf('{'); int indexOfBegin = p.Text.IndexOf('{');

@ -8,7 +8,7 @@ using MediaBrowser.Model.MediaInfo;
namespace MediaBrowser.MediaEncoding.Subtitles namespace MediaBrowser.MediaEncoding.Subtitles
{ {
/// <summary> /// <summary>
/// Credit to https://github.com/SubtitleEdit/subtitleedit/blob/a299dc4407a31796364cc6ad83f0d3786194ba22/src/Logic/SubtitleFormats/SubStationAlpha.cs /// <see href="https://github.com/SubtitleEdit/subtitleedit/blob/a299dc4407a31796364cc6ad83f0d3786194ba22/src/Logic/SubtitleFormats/SubStationAlpha.cs">Credit</see>.
/// </summary> /// </summary>
public class SsaParser : ISubtitleParser public class SsaParser : ISubtitleParser
{ {
@ -179,10 +179,12 @@ namespace MediaBrowser.MediaEncoding.Subtitles
{ {
// h:mm:ss.cc // h:mm:ss.cc
string[] timeCode = time.Split(':', '.'); string[] timeCode = time.Split(':', '.');
return new TimeSpan(0, int.Parse(timeCode[0]), return new TimeSpan(
int.Parse(timeCode[1]), 0,
int.Parse(timeCode[2]), int.Parse(timeCode[0]),
int.Parse(timeCode[3]) * 10).Ticks; int.Parse(timeCode[1]),
int.Parse(timeCode[2]),
int.Parse(timeCode[3]) * 10).Ticks;
} }
private static string GetFormattedText(string text) private static string GetFormattedText(string text)
@ -282,6 +284,7 @@ namespace MediaBrowser.MediaEncoding.Subtitles
{ {
text = text.Insert(start, "<font color=\"" + color + "\"" + extraTags + ">"); text = text.Insert(start, "<font color=\"" + color + "\"" + extraTags + ">");
} }
int indexOfEndTag = text.IndexOf("{\\c}", start); int indexOfEndTag = text.IndexOf("{\\c}", start);
if (indexOfEndTag > 0) if (indexOfEndTag > 0)
{ {
@ -320,6 +323,7 @@ namespace MediaBrowser.MediaEncoding.Subtitles
{ {
text = text.Insert(start, "<font color=\"" + color + "\"" + extraTags + ">"); text = text.Insert(start, "<font color=\"" + color + "\"" + extraTags + ">");
} }
text += "</font>"; text += "</font>";
} }
} }

@ -34,6 +34,12 @@ namespace MediaBrowser.MediaEncoding.Subtitles
private readonly IHttpClient _httpClient; private readonly IHttpClient _httpClient;
private readonly IMediaSourceManager _mediaSourceManager; private readonly IMediaSourceManager _mediaSourceManager;
/// <summary>
/// The _semaphoreLocks.
/// </summary>
private readonly ConcurrentDictionary<string, SemaphoreSlim> _semaphoreLocks =
new ConcurrentDictionary<string, SemaphoreSlim>();
public SubtitleEncoder( public SubtitleEncoder(
ILibraryManager libraryManager, ILibraryManager libraryManager,
ILogger<SubtitleEncoder> logger, ILogger<SubtitleEncoder> logger,
@ -269,25 +275,6 @@ namespace MediaBrowser.MediaEncoding.Subtitles
return new SubtitleInfo(subtitleStream.Path, protocol, currentFormat, true); return new SubtitleInfo(subtitleStream.Path, protocol, currentFormat, true);
} }
private struct SubtitleInfo
{
public SubtitleInfo(string path, MediaProtocol protocol, string format, bool isExternal)
{
Path = path;
Protocol = protocol;
Format = format;
IsExternal = isExternal;
}
public string Path { get; set; }
public MediaProtocol Protocol { get; set; }
public string Format { get; set; }
public bool IsExternal { get; set; }
}
private ISubtitleParser GetReader(string format, bool throwIfMissing) private ISubtitleParser GetReader(string format, bool throwIfMissing)
{ {
if (string.IsNullOrEmpty(format)) if (string.IsNullOrEmpty(format))
@ -360,12 +347,6 @@ namespace MediaBrowser.MediaEncoding.Subtitles
throw new ArgumentException("Unsupported format: " + format); throw new ArgumentException("Unsupported format: " + format);
} }
/// <summary>
/// The _semaphoreLocks.
/// </summary>
private readonly ConcurrentDictionary<string, SemaphoreSlim> _semaphoreLocks =
new ConcurrentDictionary<string, SemaphoreSlim>();
/// <summary> /// <summary>
/// Gets the lock. /// Gets the lock.
/// </summary> /// </summary>
@ -414,7 +395,7 @@ namespace MediaBrowser.MediaEncoding.Subtitles
/// <param name="cancellationToken">The cancellation token.</param> /// <param name="cancellationToken">The cancellation token.</param>
/// <returns>Task.</returns> /// <returns>Task.</returns>
/// <exception cref="ArgumentNullException"> /// <exception cref="ArgumentNullException">
/// The <c>inputPath</c> or <c>outputPath</c> is <c>null</c> /// The <c>inputPath</c> or <c>outputPath</c> is <c>null</c>.
/// </exception> /// </exception>
private async Task ConvertTextSubtitleToSrtInternal(string inputPath, string language, MediaProtocol inputProtocol, string outputPath, CancellationToken cancellationToken) private async Task ConvertTextSubtitleToSrtInternal(string inputPath, string language, MediaProtocol inputProtocol, string outputPath, CancellationToken cancellationToken)
{ {
@ -438,7 +419,7 @@ namespace MediaBrowser.MediaEncoding.Subtitles
(encodingParam.Equals("UTF-16BE", StringComparison.OrdinalIgnoreCase) || (encodingParam.Equals("UTF-16BE", StringComparison.OrdinalIgnoreCase) ||
encodingParam.Equals("UTF-16LE", StringComparison.OrdinalIgnoreCase))) encodingParam.Equals("UTF-16LE", StringComparison.OrdinalIgnoreCase)))
{ {
encodingParam = ""; encodingParam = string.Empty;
} }
else if (!string.IsNullOrEmpty(encodingParam)) else if (!string.IsNullOrEmpty(encodingParam))
{ {
@ -540,7 +521,7 @@ namespace MediaBrowser.MediaEncoding.Subtitles
/// <param name="outputPath">The output path.</param> /// <param name="outputPath">The output path.</param>
/// <param name="cancellationToken">The cancellation token.</param> /// <param name="cancellationToken">The cancellation token.</param>
/// <returns>Task.</returns> /// <returns>Task.</returns>
/// <exception cref="ArgumentException">Must use inputPath list overload</exception> /// <exception cref="ArgumentException">Must use inputPath list overload.</exception>
private async Task ExtractTextSubtitle( private async Task ExtractTextSubtitle(
string[] inputFiles, string[] inputFiles,
MediaProtocol protocol, MediaProtocol protocol,
@ -759,7 +740,7 @@ namespace MediaBrowser.MediaEncoding.Subtitles
&& (string.Equals(charset, "utf-16le", StringComparison.OrdinalIgnoreCase) && (string.Equals(charset, "utf-16le", StringComparison.OrdinalIgnoreCase)
|| string.Equals(charset, "utf-16be", StringComparison.OrdinalIgnoreCase))) || string.Equals(charset, "utf-16be", StringComparison.OrdinalIgnoreCase)))
{ {
charset = ""; charset = string.Empty;
} }
_logger.LogDebug("charset {0} detected for {Path}", charset ?? "null", path); _logger.LogDebug("charset {0} detected for {Path}", charset ?? "null", path);
@ -790,5 +771,24 @@ namespace MediaBrowser.MediaEncoding.Subtitles
throw new ArgumentOutOfRangeException(nameof(protocol)); throw new ArgumentOutOfRangeException(nameof(protocol));
} }
} }
private struct SubtitleInfo
{
public SubtitleInfo(string path, MediaProtocol protocol, string format, bool isExternal)
{
Path = path;
Protocol = protocol;
Format = format;
IsExternal = isExternal;
}
public string Path { get; set; }
public MediaProtocol Protocol { get; set; }
public string Format { get; set; }
public bool IsExternal { get; set; }
}
} }
} }

Loading…
Cancel
Save