aboutsummaryrefslogtreecommitdiff
path: root/MediaBrowser.MediaEncoding
diff options
context:
space:
mode:
Diffstat (limited to 'MediaBrowser.MediaEncoding')
-rw-r--r--MediaBrowser.MediaEncoding/Attachments/AttachmentExtractor.cs1
-rw-r--r--MediaBrowser.MediaEncoding/BdInfo/BdInfoExaminer.cs4
-rw-r--r--MediaBrowser.MediaEncoding/Encoder/EncoderValidator.cs82
-rw-r--r--MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs36
-rw-r--r--MediaBrowser.MediaEncoding/Probing/FFProbeHelpers.cs8
-rw-r--r--MediaBrowser.MediaEncoding/Probing/ProbeResultNormalizer.cs76
-rw-r--r--MediaBrowser.MediaEncoding/Subtitles/AssParser.cs4
-rw-r--r--MediaBrowser.MediaEncoding/Subtitles/SrtParser.cs8
-rw-r--r--MediaBrowser.MediaEncoding/Subtitles/SsaParser.cs85
-rw-r--r--MediaBrowser.MediaEncoding/Subtitles/SubtitleEncoder.cs14
10 files changed, 256 insertions, 62 deletions
diff --git a/MediaBrowser.MediaEncoding/Attachments/AttachmentExtractor.cs b/MediaBrowser.MediaEncoding/Attachments/AttachmentExtractor.cs
index a82f2108f..f02999370 100644
--- a/MediaBrowser.MediaEncoding/Attachments/AttachmentExtractor.cs
+++ b/MediaBrowser.MediaEncoding/Attachments/AttachmentExtractor.cs
@@ -269,7 +269,6 @@ namespace MediaBrowser.MediaEncoding.Attachments
if (disposing)
{
-
}
_disposed = true;
diff --git a/MediaBrowser.MediaEncoding/BdInfo/BdInfoExaminer.cs b/MediaBrowser.MediaEncoding/BdInfo/BdInfoExaminer.cs
index 3260f3051..ccfae2fa5 100644
--- a/MediaBrowser.MediaEncoding/BdInfo/BdInfoExaminer.cs
+++ b/MediaBrowser.MediaEncoding/BdInfo/BdInfoExaminer.cs
@@ -9,7 +9,7 @@ using MediaBrowser.Model.MediaInfo;
namespace MediaBrowser.MediaEncoding.BdInfo
{
/// <summary>
- /// Class BdInfoExaminer
+ /// Class BdInfoExaminer.
/// </summary>
public class BdInfoExaminer : IBlurayExaminer
{
@@ -41,7 +41,7 @@ namespace MediaBrowser.MediaEncoding.BdInfo
var outputStream = new BlurayDiscInfo
{
- MediaStreams = new MediaStream[] { }
+ MediaStreams = Array.Empty<MediaStream>()
};
if (playlist == null)
diff --git a/MediaBrowser.MediaEncoding/Encoder/EncoderValidator.cs b/MediaBrowser.MediaEncoding/Encoder/EncoderValidator.cs
index 6e036d24c..0fd0239b4 100644
--- a/MediaBrowser.MediaEncoding/Encoder/EncoderValidator.cs
+++ b/MediaBrowser.MediaEncoding/Encoder/EncoderValidator.cs
@@ -14,23 +14,45 @@ namespace MediaBrowser.MediaEncoding.Encoder
private static readonly string[] requiredDecoders = new[]
{
+ "h264",
+ "hevc",
"mpeg2video",
+ "mpeg4",
+ "msmpeg4",
+ "dts",
+ "ac3",
+ "aac",
+ "mp3",
"h264_qsv",
"hevc_qsv",
"mpeg2_qsv",
- "mpeg2_mmal",
- "mpeg4_mmal",
"vc1_qsv",
- "vc1_mmal",
+ "vp8_qsv",
+ "vp9_qsv",
"h264_cuvid",
"hevc_cuvid",
- "dts",
- "ac3",
- "aac",
- "mp3",
- "h264",
+ "mpeg2_cuvid",
+ "vc1_cuvid",
+ "mpeg4_cuvid",
+ "vp8_cuvid",
+ "vp9_cuvid",
"h264_mmal",
- "hevc"
+ "mpeg2_mmal",
+ "mpeg4_mmal",
+ "vc1_mmal",
+ "h264_mediacodec",
+ "hevc_mediacodec",
+ "mpeg2_mediacodec",
+ "mpeg4_mediacodec",
+ "vp8_mediacodec",
+ "vp9_mediacodec",
+ "h264_opencl",
+ "hevc_opencl",
+ "mpeg2_opencl",
+ "mpeg4_opencl",
+ "vp8_opencl",
+ "vp9_opencl",
+ "vc1_opencl"
};
private static readonly string[] requiredEncoders = new[]
@@ -43,22 +65,24 @@ namespace MediaBrowser.MediaEncoding.Encoder
"libvpx-vp9",
"aac",
"libfdk_aac",
+ "ac3",
"libmp3lame",
"libopus",
"libvorbis",
"srt",
- "h264_nvenc",
- "hevc_nvenc",
+ "h264_amf",
+ "hevc_amf",
"h264_qsv",
"hevc_qsv",
- "h264_omx",
- "hevc_omx",
+ "h264_nvenc",
+ "hevc_nvenc",
"h264_vaapi",
"hevc_vaapi",
+ "h264_omx",
+ "hevc_omx",
"h264_v4l2m2m",
- "ac3",
- "h264_amf",
- "hevc_amf"
+ "h264_videotoolbox",
+ "hevc_videotoolbox"
};
// Try and use the individual library versions to determine a FFmpeg version
@@ -66,6 +90,7 @@ namespace MediaBrowser.MediaEncoding.Encoder
// $ ffmpeg -version | perl -ne ' print "$1=$2.$3," if /^(lib\w+)\s+(\d+)\.\s*(\d+)/'
private static readonly IReadOnlyDictionary<string, Version> _ffmpegVersionMap = new Dictionary<string, Version>
{
+ { "libavutil=56.51,libavcodec=58.91,libavformat=58.45,libavdevice=58.10,libavfilter=7.85,libswscale=5.7,libswresample=3.7,libpostproc=55.7,", new Version(4, 3) },
{ "libavutil=56.31,libavcodec=58.54,libavformat=58.29,libavdevice=58.8,libavfilter=7.57,libswscale=5.5,libswresample=3.5,libpostproc=55.5,", new Version(4, 2) },
{ "libavutil=56.22,libavcodec=58.35,libavformat=58.20,libavdevice=58.5,libavfilter=7.40,libswscale=5.3,libswresample=3.3,libpostproc=55.3,", new Version(4, 1) },
{ "libavutil=56.14,libavcodec=58.18,libavformat=58.12,libavdevice=58.3,libavfilter=7.16,libswscale=5.1,libswresample=3.1,libpostproc=55.1,", new Version(4, 0) },
@@ -159,6 +184,8 @@ namespace MediaBrowser.MediaEncoding.Encoder
public IEnumerable<string> GetEncoders() => GetCodecs(Codec.Encoder);
+ public IEnumerable<string> GetHwaccels() => GetHwaccelTypes();
+
/// <summary>
/// Using the output from "ffmpeg -version" work out the FFmpeg version.
/// For pre-built binaries the first line should contain a string like "ffmpeg version x.y", which is easy
@@ -218,6 +245,29 @@ namespace MediaBrowser.MediaEncoding.Encoder
Decoder
}
+ private IEnumerable<string> GetHwaccelTypes()
+ {
+ string output = null;
+ try
+ {
+ output = GetProcessOutput(_encoderPath, "-hwaccels");
+ }
+ catch (Exception ex)
+ {
+ _logger.LogError(ex, "Error detecting available hwaccel types");
+ }
+
+ if (string.IsNullOrWhiteSpace(output))
+ {
+ return Enumerable.Empty<string>();
+ }
+
+ var found = output.Split(new char[] {'\r','\n'}, StringSplitOptions.RemoveEmptyEntries).Skip(1).Distinct().ToList();
+ _logger.LogInformation("Available hwaccel types: {Types}", found);
+
+ return found;
+ }
+
private IEnumerable<string> GetCodecs(Codec codec)
{
string codecstr = codec == Codec.Encoder ? "encoders" : "decoders";
diff --git a/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs b/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs
index 26bd202ba..62fdbc618 100644
--- a/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs
+++ b/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs
@@ -21,11 +21,12 @@ using MediaBrowser.Model.MediaInfo;
using MediaBrowser.Model.System;
using Microsoft.Extensions.Logging;
using System.Diagnostics;
+using Microsoft.Extensions.Configuration;
namespace MediaBrowser.MediaEncoding.Encoder
{
/// <summary>
- /// Class MediaEncoder
+ /// Class MediaEncoder.
/// </summary>
public class MediaEncoder : IMediaEncoder, IDisposable
{
@@ -46,7 +47,7 @@ namespace MediaBrowser.MediaEncoding.Encoder
private readonly object _runningProcessesLock = new object();
private readonly List<ProcessWrapper> _runningProcesses = new List<ProcessWrapper>();
- private string _ffmpegPath;
+ private string _ffmpegPath = string.Empty;
private string _ffprobePath;
public MediaEncoder(
@@ -55,14 +56,14 @@ namespace MediaBrowser.MediaEncoding.Encoder
IFileSystem fileSystem,
ILocalizationManager localization,
Lazy<EncodingHelper> encodingHelperFactory,
- string startupOptionsFFmpegPath)
+ IConfiguration config)
{
_logger = logger;
_configurationManager = configurationManager;
_fileSystem = fileSystem;
_localization = localization;
_encodingHelperFactory = encodingHelperFactory;
- _startupOptionFFmpegPath = startupOptionsFFmpegPath;
+ _startupOptionFFmpegPath = config.GetValue<string>(Controller.Extensions.ConfigurationExtensions.FfmpegPathKey) ?? string.Empty;
}
private EncodingHelper EncodingHelper => _encodingHelperFactory.Value;
@@ -111,6 +112,7 @@ namespace MediaBrowser.MediaEncoding.Encoder
SetAvailableDecoders(validator.GetDecoders());
SetAvailableEncoders(validator.GetEncoders());
+ SetAvailableHwaccels(validator.GetHwaccels());
}
_logger.LogInformation("FFmpeg: {EncoderLocation}: {FfmpegPath}", EncoderLocation, _ffmpegPath ?? string.Empty);
@@ -165,7 +167,7 @@ namespace MediaBrowser.MediaEncoding.Encoder
/// Validates the supplied FQPN to ensure it is a ffmpeg utility.
/// If checks pass, global variable FFmpegPath and EncoderLocation are updated.
/// </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>
/// <returns></returns>
private bool ValidatePath(string path, FFmpegLocation location)
@@ -228,6 +230,7 @@ namespace MediaBrowser.MediaEncoding.Encoder
{
return inJellyfinPath;
}
+
var values = Environment.GetEnvironmentVariable("PATH");
foreach (var path in values.Split(Path.PathSeparator))
@@ -247,14 +250,21 @@ namespace MediaBrowser.MediaEncoding.Encoder
public void SetAvailableEncoders(IEnumerable<string> list)
{
_encoders = list.ToList();
- //_logger.Info("Supported encoders: {0}", string.Join(",", list.ToArray()));
+ // _logger.Info("Supported encoders: {0}", string.Join(",", list.ToArray()));
}
private List<string> _decoders = new List<string>();
public void SetAvailableDecoders(IEnumerable<string> list)
{
_decoders = list.ToList();
- //_logger.Info("Supported decoders: {0}", string.Join(",", list.ToArray()));
+ // _logger.Info("Supported decoders: {0}", string.Join(",", list.ToArray()));
+ }
+
+ private List<string> _hwaccels = new List<string>();
+ public void SetAvailableHwaccels(IEnumerable<string> list)
+ {
+ _hwaccels = list.ToList();
+ //_logger.Info("Supported hwaccels: {0}", string.Join(",", list.ToArray()));
}
public bool SupportsEncoder(string encoder)
@@ -267,6 +277,11 @@ namespace MediaBrowser.MediaEncoding.Encoder
return _decoders.Contains(decoder, StringComparer.OrdinalIgnoreCase);
}
+ public bool SupportsHwaccel(string hwaccel)
+ {
+ return _hwaccels.Contains(hwaccel, StringComparer.OrdinalIgnoreCase);
+ }
+
public bool CanEncodeToAudioCodec(string codec)
{
if (string.Equals(codec, "opus", StringComparison.OrdinalIgnoreCase))
@@ -425,7 +440,7 @@ namespace MediaBrowser.MediaEncoding.Encoder
}
/// <summary>
- /// The us culture
+ /// The us culture.
/// </summary>
protected readonly CultureInfo UsCulture = new CultureInfo("en-US");
@@ -500,11 +515,11 @@ namespace MediaBrowser.MediaEncoding.Encoder
break;
case Video3DFormat.FullSideBySide:
vf = "crop=iw/2:ih:0:0,setdar=dar=a,crop=min(iw\\,ih*dar):min(ih\\,iw/dar):(iw-min(iw\\,iw*sar))/2:(ih - min (ih\\,ih/sar))/2,setsar=sar=1,scale=600:trunc(600/dar/2)*2";
- //fsbs crop width in half,set the display aspect,crop out any black bars we may have made the scale width to 600.
+ // fsbs crop width in half,set the display aspect,crop out any black bars we may have made the scale width to 600.
break;
case Video3DFormat.HalfTopAndBottom:
vf = "crop=iw:ih/2:0:0,scale=(iw*2):ih),setdar=dar=a,crop=min(iw\\,ih*dar):min(ih\\,iw/dar):(iw-min(iw\\,iw*sar))/2:(ih - min (ih\\,ih/sar))/2,setsar=sar=1,scale=600:trunc(600/dar/2)*2";
- //htab crop heigh in half,scale to correct size, set the display aspect,crop out any black bars we may have made the scale width to 600
+ // htab crop heigh in half,scale to correct size, set the display aspect,crop out any black bars we may have made the scale width to 600
break;
case Video3DFormat.FullTopAndBottom:
vf = "crop=iw:ih/2:0:0,setdar=dar=a,crop=min(iw\\,ih*dar):min(ih\\,iw/dar):(iw-min(iw\\,iw*sar))/2:(ih - min (ih\\,ih/sar))/2,setsar=sar=1,scale=600:trunc(600/dar/2)*2";
@@ -920,7 +935,6 @@ namespace MediaBrowser.MediaEncoding.Encoder
var fileParts = _fileSystem.GetFileNameWithoutExtension(f).Split('_');
return fileParts.Length == 3 && string.Equals(title, fileParts[1], StringComparison.OrdinalIgnoreCase);
-
}).ToList();
// If this resulted in not getting any vobs, just take them all
diff --git a/MediaBrowser.MediaEncoding/Probing/FFProbeHelpers.cs b/MediaBrowser.MediaEncoding/Probing/FFProbeHelpers.cs
index 78dc7b607..3aa296f7f 100644
--- a/MediaBrowser.MediaEncoding/Probing/FFProbeHelpers.cs
+++ b/MediaBrowser.MediaEncoding/Probing/FFProbeHelpers.cs
@@ -35,7 +35,7 @@ namespace MediaBrowser.MediaEncoding.Probing
}
/// <summary>
- /// Gets a string from an FFProbeResult tags dictionary
+ /// Gets a string from an FFProbeResult tags dictionary.
/// </summary>
/// <param name="tags">The tags.</param>
/// <param name="key">The key.</param>
@@ -52,7 +52,7 @@ namespace MediaBrowser.MediaEncoding.Probing
}
/// <summary>
- /// Gets an int from an FFProbeResult tags dictionary
+ /// Gets an int from an FFProbeResult tags dictionary.
/// </summary>
/// <param name="tags">The tags.</param>
/// <param name="key">The key.</param>
@@ -73,7 +73,7 @@ namespace MediaBrowser.MediaEncoding.Probing
}
/// <summary>
- /// Gets a DateTime from an FFProbeResult tags dictionary
+ /// Gets a DateTime from an FFProbeResult tags dictionary.
/// </summary>
/// <param name="tags">The tags.</param>
/// <param name="key">The key.</param>
@@ -94,7 +94,7 @@ namespace MediaBrowser.MediaEncoding.Probing
}
/// <summary>
- /// Converts a dictionary to case insensitive
+ /// Converts a dictionary to case insensitive.
/// </summary>
/// <param name="dict">The dict.</param>
/// <returns>Dictionary{System.StringSystem.String}.</returns>
diff --git a/MediaBrowser.MediaEncoding/Probing/ProbeResultNormalizer.cs b/MediaBrowser.MediaEncoding/Probing/ProbeResultNormalizer.cs
index 9386d6524..c85d963ed 100644
--- a/MediaBrowser.MediaEncoding/Probing/ProbeResultNormalizer.cs
+++ b/MediaBrowser.MediaEncoding/Probing/ProbeResultNormalizer.cs
@@ -93,6 +93,7 @@ namespace MediaBrowser.MediaEncoding.Probing
{
overview = FFProbeHelpers.GetDictionaryValue(tags, "description");
}
+
if (string.IsNullOrWhiteSpace(overview))
{
overview = FFProbeHelpers.GetDictionaryValue(tags, "desc");
@@ -274,10 +275,12 @@ namespace MediaBrowser.MediaEncoding.Probing
reader.Read();
continue;
}
+
using (var subtree = reader.ReadSubtree())
{
ReadFromDictNode(subtree, info);
}
+
break;
default:
reader.Skip();
@@ -319,6 +322,7 @@ namespace MediaBrowser.MediaEncoding.Probing
{
ProcessPairs(currentKey, pairs, info);
}
+
currentKey = reader.ReadElementContentAsString();
pairs = new List<NameValuePair>();
break;
@@ -332,6 +336,7 @@ namespace MediaBrowser.MediaEncoding.Probing
Value = value
});
}
+
break;
case "array":
if (reader.IsEmptyElement)
@@ -339,6 +344,7 @@ namespace MediaBrowser.MediaEncoding.Probing
reader.Read();
continue;
}
+
using (var subtree = reader.ReadSubtree())
{
if (!string.IsNullOrWhiteSpace(currentKey))
@@ -346,6 +352,7 @@ namespace MediaBrowser.MediaEncoding.Probing
pairs.AddRange(ReadValueArray(subtree));
}
}
+
break;
default:
reader.Skip();
@@ -381,6 +388,7 @@ namespace MediaBrowser.MediaEncoding.Probing
reader.Read();
continue;
}
+
using (var subtree = reader.ReadSubtree())
{
var dict = GetNameValuePair(subtree);
@@ -389,6 +397,7 @@ namespace MediaBrowser.MediaEncoding.Probing
pairs.Add(dict);
}
}
+
break;
default:
reader.Skip();
@@ -413,7 +422,6 @@ namespace MediaBrowser.MediaEncoding.Probing
.Where(i => !string.IsNullOrWhiteSpace(i))
.Distinct(StringComparer.OrdinalIgnoreCase)
.ToArray();
-
}
else if (string.Equals(key, "screenwriters", StringComparison.OrdinalIgnoreCase))
{
@@ -425,7 +433,6 @@ namespace MediaBrowser.MediaEncoding.Probing
Type = PersonType.Writer
});
}
-
}
else if (string.Equals(key, "producers", StringComparison.OrdinalIgnoreCase))
{
@@ -517,7 +524,7 @@ namespace MediaBrowser.MediaEncoding.Probing
}
/// <summary>
- /// Converts ffprobe stream info to our MediaAttachment class
+ /// Converts ffprobe stream info to our MediaAttachment class.
/// </summary>
/// <param name="streamInfo">The stream info.</param>
/// <returns>MediaAttachments.</returns>
@@ -550,7 +557,7 @@ namespace MediaBrowser.MediaEncoding.Probing
}
/// <summary>
- /// Converts ffprobe stream info to our MediaStream class
+ /// Converts ffprobe stream info to our MediaStream class.
/// </summary>
/// <param name="isAudio">if set to <c>true</c> [is info].</param>
/// <param name="streamInfo">The stream info.</param>
@@ -562,7 +569,7 @@ namespace MediaBrowser.MediaEncoding.Probing
if (string.Equals(streamInfo.CodecName, "mov_text", StringComparison.OrdinalIgnoreCase))
{
// Edit: but these are also sometimes subtitles?
- //return null;
+ // return null;
}
var stream = new MediaStream
@@ -684,7 +691,7 @@ namespace MediaBrowser.MediaEncoding.Probing
stream.BitDepth = streamInfo.BitsPerRawSample;
}
- //stream.IsAnamorphic = string.Equals(streamInfo.sample_aspect_ratio, "0:1", StringComparison.OrdinalIgnoreCase) ||
+ // stream.IsAnamorphic = string.Equals(streamInfo.sample_aspect_ratio, "0:1", StringComparison.OrdinalIgnoreCase) ||
// string.Equals(stream.AspectRatio, "2.35:1", StringComparison.OrdinalIgnoreCase) ||
// string.Equals(stream.AspectRatio, "2.40:1", StringComparison.OrdinalIgnoreCase);
@@ -769,7 +776,7 @@ namespace MediaBrowser.MediaEncoding.Probing
}
/// <summary>
- /// Gets a string from an FFProbeResult tags dictionary
+ /// Gets a string from an FFProbeResult tags dictionary.
/// </summary>
/// <param name="tags">The tags.</param>
/// <param name="key">The key.</param>
@@ -950,11 +957,12 @@ namespace MediaBrowser.MediaEncoding.Probing
{
peoples.Add(new BaseItemPerson { Name = person, Type = PersonType.Composer });
}
+
audio.People = peoples.ToArray();
}
- //var conductor = FFProbeHelpers.GetDictionaryValue(tags, "conductor");
- //if (!string.IsNullOrWhiteSpace(conductor))
+ // var conductor = FFProbeHelpers.GetDictionaryValue(tags, "conductor");
+ // if (!string.IsNullOrWhiteSpace(conductor))
//{
// foreach (var person in Split(conductor, false))
// {
@@ -962,8 +970,8 @@ namespace MediaBrowser.MediaEncoding.Probing
// }
//}
- //var lyricist = FFProbeHelpers.GetDictionaryValue(tags, "lyricist");
- //if (!string.IsNullOrWhiteSpace(lyricist))
+ // var lyricist = FFProbeHelpers.GetDictionaryValue(tags, "lyricist");
+ // if (!string.IsNullOrWhiteSpace(lyricist))
//{
// foreach (var person in Split(lyricist, false))
// {
@@ -981,6 +989,7 @@ namespace MediaBrowser.MediaEncoding.Probing
{
peoples.Add(new BaseItemPerson { Name = person, Type = PersonType.Writer });
}
+
audio.People = peoples.ToArray();
}
@@ -999,7 +1008,7 @@ namespace MediaBrowser.MediaEncoding.Probing
var artist = FFProbeHelpers.GetDictionaryValue(tags, "artist");
if (string.IsNullOrWhiteSpace(artist))
{
- audio.Artists = new string[] { };
+ audio.Artists = Array.Empty<string>();
}
else
{
@@ -1014,6 +1023,7 @@ namespace MediaBrowser.MediaEncoding.Probing
{
albumArtist = FFProbeHelpers.GetDictionaryValue(tags, "album artist");
}
+
if (string.IsNullOrWhiteSpace(albumArtist))
{
albumArtist = FFProbeHelpers.GetDictionaryValue(tags, "album_artist");
@@ -1021,14 +1031,13 @@ namespace MediaBrowser.MediaEncoding.Probing
if (string.IsNullOrWhiteSpace(albumArtist))
{
- audio.AlbumArtists = new string[] { };
+ audio.AlbumArtists = Array.Empty<string>();
}
else
{
audio.AlbumArtists = SplitArtists(albumArtist, _nameDelimiters, true)
.DistinctNames()
.ToArray();
-
}
if (audio.AlbumArtists.Length == 0)
@@ -1056,23 +1065,43 @@ namespace MediaBrowser.MediaEncoding.Probing
// These support mulitple values, but for now we only store the first.
var mb = GetMultipleMusicBrainzId(FFProbeHelpers.GetDictionaryValue(tags, "MusicBrainz Album Artist Id"));
- if (mb == null) mb = GetMultipleMusicBrainzId(FFProbeHelpers.GetDictionaryValue(tags, "MUSICBRAINZ_ALBUMARTISTID"));
+ if (mb == null)
+ {
+ mb = GetMultipleMusicBrainzId(FFProbeHelpers.GetDictionaryValue(tags, "MUSICBRAINZ_ALBUMARTISTID"));
+ }
+
audio.SetProviderId(MetadataProvider.MusicBrainzAlbumArtist, mb);
mb = GetMultipleMusicBrainzId(FFProbeHelpers.GetDictionaryValue(tags, "MusicBrainz Artist Id"));
- if (mb == null) mb = GetMultipleMusicBrainzId(FFProbeHelpers.GetDictionaryValue(tags, "MUSICBRAINZ_ARTISTID"));
+ if (mb == null)
+ {
+ mb = GetMultipleMusicBrainzId(FFProbeHelpers.GetDictionaryValue(tags, "MUSICBRAINZ_ARTISTID"));
+ }
+
audio.SetProviderId(MetadataProvider.MusicBrainzArtist, mb);
mb = GetMultipleMusicBrainzId(FFProbeHelpers.GetDictionaryValue(tags, "MusicBrainz Album Id"));
- if (mb == null) mb = GetMultipleMusicBrainzId(FFProbeHelpers.GetDictionaryValue(tags, "MUSICBRAINZ_ALBUMID"));
+ if (mb == null)
+ {
+ mb = GetMultipleMusicBrainzId(FFProbeHelpers.GetDictionaryValue(tags, "MUSICBRAINZ_ALBUMID"));
+ }
+
audio.SetProviderId(MetadataProvider.MusicBrainzAlbum, mb);
mb = GetMultipleMusicBrainzId(FFProbeHelpers.GetDictionaryValue(tags, "MusicBrainz Release Group Id"));
- if (mb == null) mb = GetMultipleMusicBrainzId(FFProbeHelpers.GetDictionaryValue(tags, "MUSICBRAINZ_RELEASEGROUPID"));
+ if (mb == null)
+ {
+ mb = GetMultipleMusicBrainzId(FFProbeHelpers.GetDictionaryValue(tags, "MUSICBRAINZ_RELEASEGROUPID"));
+ }
+
audio.SetProviderId(MetadataProvider.MusicBrainzReleaseGroup, mb);
mb = GetMultipleMusicBrainzId(FFProbeHelpers.GetDictionaryValue(tags, "MusicBrainz Release Track Id"));
- if (mb == null) mb = GetMultipleMusicBrainzId(FFProbeHelpers.GetDictionaryValue(tags, "MUSICBRAINZ_RELEASETRACKID"));
+ if (mb == null)
+ {
+ mb = GetMultipleMusicBrainzId(FFProbeHelpers.GetDictionaryValue(tags, "MUSICBRAINZ_RELEASETRACKID"));
+ }
+
audio.SetProviderId(MetadataProvider.MusicBrainzTrack, mb);
}
@@ -1157,7 +1186,7 @@ namespace MediaBrowser.MediaEncoding.Probing
}
/// <summary>
- /// Gets the studios from the tags collection
+ /// Gets the studios from the tags collection.
/// </summary>
/// <param name="info">The info.</param>
/// <param name="tags">The tags.</param>
@@ -1178,6 +1207,7 @@ namespace MediaBrowser.MediaEncoding.Probing
{
continue;
}
+
if (info.AlbumArtists.Contains(studio, StringComparer.OrdinalIgnoreCase))
{
continue;
@@ -1194,7 +1224,7 @@ namespace MediaBrowser.MediaEncoding.Probing
}
/// <summary>
- /// Gets the genres from the tags collection
+ /// Gets the genres from the tags collection.
/// </summary>
/// <param name="info">The information.</param>
/// <param name="tags">The tags.</param>
@@ -1354,14 +1384,18 @@ namespace MediaBrowser.MediaEncoding.Probing
description = string.Join(" ", numbers, 1, numbers.Length - 1).Trim(); // Skip the first, concatenate the rest, clean up spaces and save it
}
else
+ {
throw new Exception(); // Switch to default parsing
+ }
}
catch // Default parsing
{
if (subtitle.Contains(".")) // skip the comment, keep the subtitle
description = string.Join(".", subtitle.Split('.'), 1, subtitle.Split('.').Length - 1).Trim(); // skip the first
else
+ {
description = subtitle.Trim(); // Clean up whitespaces and save it
+ }
}
}
}
diff --git a/MediaBrowser.MediaEncoding/Subtitles/AssParser.cs b/MediaBrowser.MediaEncoding/Subtitles/AssParser.cs
index f44cf1452..0e2d70017 100644
--- a/MediaBrowser.MediaEncoding/Subtitles/AssParser.cs
+++ b/MediaBrowser.MediaEncoding/Subtitles/AssParser.cs
@@ -23,6 +23,7 @@ namespace MediaBrowser.MediaEncoding.Subtitles
string line;
while (reader.ReadLine() != "[Events]")
{ }
+
var headers = ParseFieldHeaders(reader.ReadLine());
while ((line = reader.ReadLine()) != null)
@@ -56,6 +57,7 @@ namespace MediaBrowser.MediaEncoding.Subtitles
trackEvents.Add(subEvent);
}
}
+
trackInfo.TrackEvents = trackEvents.ToArray();
return trackInfo;
}
@@ -112,11 +114,13 @@ namespace MediaBrowser.MediaEncoding.Subtitles
{
pre = s.Substring(0, 5) + "}";
}
+
int indexOfEnd = p.Text.IndexOf('}');
p.Text = p.Text.Remove(indexOfBegin, (indexOfEnd - indexOfBegin) + 1);
indexOfBegin = p.Text.IndexOf('{');
}
+
p.Text = pre + p.Text;
}
}
diff --git a/MediaBrowser.MediaEncoding/Subtitles/SrtParser.cs b/MediaBrowser.MediaEncoding/Subtitles/SrtParser.cs
index c98dd1502..728efa788 100644
--- a/MediaBrowser.MediaEncoding/Subtitles/SrtParser.cs
+++ b/MediaBrowser.MediaEncoding/Subtitles/SrtParser.cs
@@ -35,6 +35,7 @@ namespace MediaBrowser.MediaEncoding.Subtitles
{
continue;
}
+
var subEvent = new SubtitleTrackEvent { Id = line };
line = reader.ReadLine();
@@ -52,11 +53,15 @@ namespace MediaBrowser.MediaEncoding.Subtitles
_logger.LogWarning("Unrecognized line in srt: {0}", line);
continue;
}
+
subEvent.StartPositionTicks = GetTicks(time[0]);
var endTime = time[1];
var idx = endTime.IndexOf(" ", StringComparison.Ordinal);
if (idx > 0)
+ {
endTime = endTime.Substring(0, idx);
+ }
+
subEvent.EndPositionTicks = GetTicks(endTime);
var multiline = new List<string>();
while ((line = reader.ReadLine()) != null)
@@ -65,8 +70,10 @@ namespace MediaBrowser.MediaEncoding.Subtitles
{
break;
}
+
multiline.Add(line);
}
+
subEvent.Text = string.Join(ParserValues.NewLine, multiline);
subEvent.Text = subEvent.Text.Replace(@"\N", ParserValues.NewLine, StringComparison.OrdinalIgnoreCase);
subEvent.Text = Regex.Replace(subEvent.Text, @"\{(?:\\\d?[\w.-]+(?:\([^\)]*\)|&H?[0-9A-Fa-f]+&|))+\}", string.Empty, RegexOptions.IgnoreCase);
@@ -76,6 +83,7 @@ namespace MediaBrowser.MediaEncoding.Subtitles
trackEvents.Add(subEvent);
}
}
+
trackInfo.TrackEvents = trackEvents.ToArray();
return trackInfo;
}
diff --git a/MediaBrowser.MediaEncoding/Subtitles/SsaParser.cs b/MediaBrowser.MediaEncoding/Subtitles/SsaParser.cs
index b94d45165..77033c6b4 100644
--- a/MediaBrowser.MediaEncoding/Subtitles/SsaParser.cs
+++ b/MediaBrowser.MediaEncoding/Subtitles/SsaParser.cs
@@ -41,7 +41,9 @@ namespace MediaBrowser.MediaEncoding.Subtitles
lineNumber++;
if (!eventsStarted)
+ {
header.AppendLine(line);
+ }
if (line.Trim().ToLowerInvariant() == "[events]")
{
@@ -62,17 +64,29 @@ namespace MediaBrowser.MediaEncoding.Subtitles
for (int i = 0; i < format.Length; i++)
{
if (format[i].Trim().ToLowerInvariant() == "layer")
+ {
indexLayer = i;
+ }
else if (format[i].Trim().ToLowerInvariant() == "start")
+ {
indexStart = i;
+ }
else if (format[i].Trim().ToLowerInvariant() == "end")
+ {
indexEnd = i;
+ }
else if (format[i].Trim().ToLowerInvariant() == "text")
+ {
indexText = i;
+ }
else if (format[i].Trim().ToLowerInvariant() == "effect")
+ {
indexEffect = i;
+ }
else if (format[i].Trim().ToLowerInvariant() == "style")
+ {
indexStyle = i;
+ }
}
}
}
@@ -89,28 +103,48 @@ namespace MediaBrowser.MediaEncoding.Subtitles
string[] splittedLine;
if (s.StartsWith("dialogue:"))
+ {
splittedLine = line.Substring(10).Split(',');
+ }
else
+ {
splittedLine = line.Split(',');
+ }
for (int i = 0; i < splittedLine.Length; i++)
{
if (i == indexStart)
+ {
start = splittedLine[i].Trim();
+ }
else if (i == indexEnd)
+ {
end = splittedLine[i].Trim();
+ }
else if (i == indexLayer)
+ {
layer = splittedLine[i];
+ }
else if (i == indexEffect)
+ {
effect = splittedLine[i];
+ }
else if (i == indexText)
+ {
text = splittedLine[i];
+ }
else if (i == indexStyle)
+ {
style = splittedLine[i];
+ }
else if (i == indexName)
+ {
name = splittedLine[i];
+ }
else if (i > indexText)
+ {
text += "," + splittedLine[i];
+ }
}
try
@@ -130,11 +164,12 @@ namespace MediaBrowser.MediaEncoding.Subtitles
}
}
- //if (header.Length > 0)
- //subtitle.Header = header.ToString();
+ // if (header.Length > 0)
+ // subtitle.Header = header.ToString();
- //subtitle.Renumber(1);
+ // subtitle.Renumber(1);
}
+
trackInfo.TrackEvents = trackEvents.ToArray();
return trackInfo;
}
@@ -168,15 +203,23 @@ namespace MediaBrowser.MediaEncoding.Subtitles
CheckAndAddSubTags(ref fontName, ref extraTags, out italic);
text = text.Remove(start, end - start + 1);
if (italic)
+ {
text = text.Insert(start, "<font face=\"" + fontName + "\"" + extraTags + "><i>");
+ }
else
+ {
text = text.Insert(start, "<font face=\"" + fontName + "\"" + extraTags + ">");
+ }
int indexOfEndTag = text.IndexOf("{\\fn}", start);
if (indexOfEndTag > 0)
+ {
text = text.Remove(indexOfEndTag, "{\\fn}".Length).Insert(indexOfEndTag, "</font>");
+ }
else
+ {
text += "</font>";
+ }
}
}
@@ -193,15 +236,23 @@ namespace MediaBrowser.MediaEncoding.Subtitles
{
text = text.Remove(start, end - start + 1);
if (italic)
+ {
text = text.Insert(start, "<font size=\"" + fontSize + "\"" + extraTags + "><i>");
+ }
else
+ {
text = text.Insert(start, "<font size=\"" + fontSize + "\"" + extraTags + ">");
+ }
int indexOfEndTag = text.IndexOf("{\\fs}", start);
if (indexOfEndTag > 0)
+ {
text = text.Remove(indexOfEndTag, "{\\fs}".Length).Insert(indexOfEndTag, "</font>");
+ }
else
+ {
text += "</font>";
+ }
}
}
}
@@ -225,14 +276,22 @@ namespace MediaBrowser.MediaEncoding.Subtitles
text = text.Remove(start, end - start + 1);
if (italic)
+ {
text = text.Insert(start, "<font color=\"" + color + "\"" + extraTags + "><i>");
+ }
else
+ {
text = text.Insert(start, "<font color=\"" + color + "\"" + extraTags + ">");
+ }
int indexOfEndTag = text.IndexOf("{\\c}", start);
if (indexOfEndTag > 0)
+ {
text = text.Remove(indexOfEndTag, "{\\c}".Length).Insert(indexOfEndTag, "</font>");
+ }
else
+ {
text += "</font>";
+ }
}
}
@@ -255,32 +314,41 @@ namespace MediaBrowser.MediaEncoding.Subtitles
text = text.Remove(start, end - start + 1);
if (italic)
+ {
text = text.Insert(start, "<font color=\"" + color + "\"" + extraTags + "><i>");
+ }
else
+ {
text = text.Insert(start, "<font color=\"" + color + "\"" + extraTags + ">");
+ }
text += "</font>";
}
}
-
}
text = text.Replace(@"{\i1}", "<i>");
text = text.Replace(@"{\i0}", "</i>");
text = text.Replace(@"{\i}", "</i>");
if (CountTagInText(text, "<i>") > CountTagInText(text, "</i>"))
+ {
text += "</i>";
+ }
text = text.Replace(@"{\u1}", "<u>");
text = text.Replace(@"{\u0}", "</u>");
text = text.Replace(@"{\u}", "</u>");
if (CountTagInText(text, "<u>") > CountTagInText(text, "</u>"))
+ {
text += "</u>";
+ }
text = text.Replace(@"{\b1}", "<b>");
text = text.Replace(@"{\b0}", "</b>");
text = text.Replace(@"{\b}", "</b>");
if (CountTagInText(text, "<b>") > CountTagInText(text, "</b>"))
+ {
text += "</b>";
+ }
return text;
}
@@ -288,7 +356,10 @@ namespace MediaBrowser.MediaEncoding.Subtitles
private static bool IsInteger(string s)
{
if (int.TryParse(s, out var i))
+ {
return true;
+ }
+
return false;
}
@@ -300,9 +371,13 @@ namespace MediaBrowser.MediaEncoding.Subtitles
{
count++;
if (index == text.Length)
+ {
return count;
+ }
+
index = text.IndexOf(tag, index + 1);
}
+
return count;
}
@@ -330,6 +405,7 @@ namespace MediaBrowser.MediaEncoding.Subtitles
{
rest = string.Empty;
}
+
extraTags += " size=\"" + fontSize.Substring(2) + "\"";
}
else if (rest.StartsWith("fn") && rest.Length > 2)
@@ -345,6 +421,7 @@ namespace MediaBrowser.MediaEncoding.Subtitles
{
rest = string.Empty;
}
+
extraTags += " face=\"" + fontName.Substring(2) + "\"";
}
else if (rest.StartsWith("c") && rest.Length > 2)
diff --git a/MediaBrowser.MediaEncoding/Subtitles/SubtitleEncoder.cs b/MediaBrowser.MediaEncoding/Subtitles/SubtitleEncoder.cs
index f08af6045..f1aa8ea5f 100644
--- a/MediaBrowser.MediaEncoding/Subtitles/SubtitleEncoder.cs
+++ b/MediaBrowser.MediaEncoding/Subtitles/SubtitleEncoder.cs
@@ -115,6 +115,7 @@ namespace MediaBrowser.MediaEncoding.Subtitles
{
throw new ArgumentNullException(nameof(item));
}
+
if (string.IsNullOrWhiteSpace(mediaSourceId))
{
throw new ArgumentNullException(nameof(mediaSourceId));
@@ -271,8 +272,11 @@ namespace MediaBrowser.MediaEncoding.Subtitles
}
public string Path { get; set; }
+
public MediaProtocol Protocol { get; set; }
+
public string Format { get; set; }
+
public bool IsExternal { get; set; }
}
@@ -287,10 +291,12 @@ namespace MediaBrowser.MediaEncoding.Subtitles
{
return new SrtParser(_logger);
}
+
if (string.Equals(format, SubtitleFormat.SSA, StringComparison.OrdinalIgnoreCase))
{
return new SsaParser();
}
+
if (string.Equals(format, SubtitleFormat.ASS, StringComparison.OrdinalIgnoreCase))
{
return new AssParser();
@@ -315,14 +321,17 @@ namespace MediaBrowser.MediaEncoding.Subtitles
{
return new JsonWriter();
}
+
if (string.Equals(format, SubtitleFormat.SRT, StringComparison.OrdinalIgnoreCase))
{
return new SrtWriter();
}
+
if (string.Equals(format, SubtitleFormat.VTT, StringComparison.OrdinalIgnoreCase))
{
return new VttWriter();
}
+
if (string.Equals(format, SubtitleFormat.TTML, StringComparison.OrdinalIgnoreCase))
{
return new TtmlWriter();
@@ -344,7 +353,7 @@ namespace MediaBrowser.MediaEncoding.Subtitles
}
/// <summary>
- /// The _semaphoreLocks
+ /// The _semaphoreLocks.
/// </summary>
private readonly ConcurrentDictionary<string, SemaphoreSlim> _semaphoreLocks =
new ConcurrentDictionary<string, SemaphoreSlim>();
@@ -640,7 +649,6 @@ namespace MediaBrowser.MediaEncoding.Subtitles
}
catch (FileNotFoundException)
{
-
}
catch (IOException ex)
{
@@ -737,7 +745,7 @@ namespace MediaBrowser.MediaEncoding.Subtitles
var charset = CharsetDetector.DetectFromStream(stream).Detected?.EncodingName;
// UTF16 is automatically converted to UTF8 by FFmpeg, do not specify a character encoding
- if ((path.EndsWith(".ass") || path.EndsWith(".ssa"))
+ if ((path.EndsWith(".ass") || path.EndsWith(".ssa") || path.EndsWith(".srt"))
&& (string.Equals(charset, "utf-16le", StringComparison.OrdinalIgnoreCase)
|| string.Equals(charset, "utf-16be", StringComparison.OrdinalIgnoreCase)))
{