aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Emby.Dlna/PlayTo/DlnaHttpClient.cs10
-rw-r--r--Emby.Naming/Audio/AlbumParser.cs13
-rw-r--r--Emby.Naming/TV/SeriesResolver.cs7
-rw-r--r--Emby.Naming/Video/VideoListResolver.cs12
-rw-r--r--Emby.Server.Implementations/Library/Resolvers/Movies/MovieResolver.cs10
-rw-r--r--Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/LegacyHdHomerunChannelCommands.cs7
-rw-r--r--Emby.Server.Implementations/LiveTv/TunerHosts/M3uParser.cs9
-rw-r--r--Jellyfin.Server.Implementations/Users/UserManager.cs20
-rw-r--r--MediaBrowser.Common/Extensions/BaseExtensions.cs13
-rw-r--r--MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs7
-rw-r--r--MediaBrowser.MediaEncoding/Encoder/EncoderValidator.cs15
-rw-r--r--MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs11
-rw-r--r--MediaBrowser.MediaEncoding/Subtitles/AssWriter.cs7
-rw-r--r--MediaBrowser.MediaEncoding/Subtitles/SrtWriter.cs7
-rw-r--r--MediaBrowser.MediaEncoding/Subtitles/SsaWriter.cs7
-rw-r--r--MediaBrowser.MediaEncoding/Subtitles/TtmlWriter.cs7
-rw-r--r--MediaBrowser.MediaEncoding/Subtitles/VttWriter.cs7
-rw-r--r--MediaBrowser.Model/Dlna/SearchCriteria.cs31
-rw-r--r--MediaBrowser.Providers/MediaInfo/AudioFileProber.cs7
-rw-r--r--MediaBrowser.Providers/Plugins/Tmdb/TmdbUtils.cs9
-rw-r--r--MediaBrowser.XbmcMetadata/Savers/BaseNfoSaver.cs15
-rw-r--r--jellyfin.ruleset2
-rw-r--r--src/Jellyfin.Drawing.Skia/StripCollageBuilder.cs6
-rw-r--r--src/Jellyfin.Extensions/StringExtensions.cs10
-rw-r--r--tests/Jellyfin.Providers.Tests/Manager/ItemImageProviderTests.cs7
25 files changed, 140 insertions, 116 deletions
diff --git a/Emby.Dlna/PlayTo/DlnaHttpClient.cs b/Emby.Dlna/PlayTo/DlnaHttpClient.cs
index 8b983e9e3..8454c1afd 100644
--- a/Emby.Dlna/PlayTo/DlnaHttpClient.cs
+++ b/Emby.Dlna/PlayTo/DlnaHttpClient.cs
@@ -31,6 +31,9 @@ namespace Emby.Dlna.PlayTo
_httpClientFactory = httpClientFactory;
}
+ [GeneratedRegex("(&(?![a-z]*;))")]
+ private static partial Regex EscapeAmpersandRegex();
+
private static string NormalizeServiceUrl(string baseUrl, string serviceUrl)
{
// If it's already a complete url, don't stick anything onto the front of it
@@ -128,12 +131,5 @@ namespace Emby.Dlna.PlayTo
// Have to await here instead of returning the Task directly, otherwise request would be disposed too soon
return await SendRequestAsync(request, cancellationToken).ConfigureAwait(false);
}
-
- /// <summary>
- /// Compile-time generated regular expression for escaping ampersands.
- /// </summary>
- /// <returns>Compiled regular expression.</returns>
- [GeneratedRegex("(&(?![a-z]*;))")]
- private static partial Regex EscapeAmpersandRegex();
}
}
diff --git a/Emby.Naming/Audio/AlbumParser.cs b/Emby.Naming/Audio/AlbumParser.cs
index 86a564153..97961778f 100644
--- a/Emby.Naming/Audio/AlbumParser.cs
+++ b/Emby.Naming/Audio/AlbumParser.cs
@@ -10,7 +10,7 @@ namespace Emby.Naming.Audio
/// <summary>
/// Helper class to determine if Album is multipart.
/// </summary>
- public class AlbumParser
+ public partial class AlbumParser
{
private readonly NamingOptions _options;
@@ -23,6 +23,9 @@ namespace Emby.Naming.Audio
_options = options;
}
+ [GeneratedRegex(@"[-\.\(\)\s]+")]
+ private static partial Regex CleanRegex();
+
/// <summary>
/// Function that determines if album is multipart.
/// </summary>
@@ -42,13 +45,9 @@ namespace Emby.Naming.Audio
// Normalize
// Remove whitespace
- filename = filename.Replace('-', ' ');
- filename = filename.Replace('.', ' ');
- filename = filename.Replace('(', ' ');
- filename = filename.Replace(')', ' ');
- filename = Regex.Replace(filename, @"\s+", " ");
+ filename = CleanRegex().Replace(filename, " ");
- ReadOnlySpan<char> trimmedFilename = filename.TrimStart();
+ ReadOnlySpan<char> trimmedFilename = filename.AsSpan().TrimStart();
foreach (var prefix in _options.AlbumStackingPrefixes)
{
diff --git a/Emby.Naming/TV/SeriesResolver.cs b/Emby.Naming/TV/SeriesResolver.cs
index 307a84096..d8fa41743 100644
--- a/Emby.Naming/TV/SeriesResolver.cs
+++ b/Emby.Naming/TV/SeriesResolver.cs
@@ -7,14 +7,15 @@ namespace Emby.Naming.TV
/// <summary>
/// Used to resolve information about series from path.
/// </summary>
- public static class SeriesResolver
+ public static partial class SeriesResolver
{
/// <summary>
/// Regex that matches strings of at least 2 characters separated by a dot or underscore.
/// Used for removing separators between words, i.e turns "The_show" into "The show" while
/// preserving namings like "S.H.O.W".
/// </summary>
- private static readonly Regex _seriesNameRegex = new Regex(@"((?<a>[^\._]{2,})[\._]*)|([\._](?<b>[^\._]{2,}))", RegexOptions.Compiled);
+ [GeneratedRegex(@"((?<a>[^\._]{2,})[\._]*)|([\._](?<b>[^\._]{2,}))")]
+ private static partial Regex SeriesNameRegex();
/// <summary>
/// Resolve information about series from path.
@@ -37,7 +38,7 @@ namespace Emby.Naming.TV
if (!string.IsNullOrEmpty(seriesName))
{
- seriesName = _seriesNameRegex.Replace(seriesName, "${a} ${b}").Trim();
+ seriesName = SeriesNameRegex().Replace(seriesName, "${a} ${b}").Trim();
}
return new SeriesInfo(path)
diff --git a/Emby.Naming/Video/VideoListResolver.cs b/Emby.Naming/Video/VideoListResolver.cs
index 6209cd46f..51f29cf08 100644
--- a/Emby.Naming/Video/VideoListResolver.cs
+++ b/Emby.Naming/Video/VideoListResolver.cs
@@ -12,9 +12,13 @@ namespace Emby.Naming.Video
/// <summary>
/// Resolves alternative versions and extras from list of video files.
/// </summary>
- public static class VideoListResolver
+ public static partial class VideoListResolver
{
- private static readonly Regex _resolutionRegex = new Regex("[0-9]{2}[0-9]+[ip]", RegexOptions.IgnoreCase | RegexOptions.Compiled);
+ [GeneratedRegex("[0-9]{2}[0-9]+[ip]", RegexOptions.IgnoreCase)]
+ private static partial Regex ResolutionRegex();
+
+ [GeneratedRegex(@"^\[([^]]*)\]")]
+ private static partial Regex CheckMultiVersionRegex();
/// <summary>
/// Resolves alternative versions and extras from list of video files.
@@ -131,7 +135,7 @@ namespace Emby.Naming.Video
if (videos.Count > 1)
{
- var groups = videos.GroupBy(x => _resolutionRegex.IsMatch(x.Files[0].FileNameWithoutExtension)).ToList();
+ var groups = videos.GroupBy(x => ResolutionRegex().IsMatch(x.Files[0].FileNameWithoutExtension)).ToList();
videos.Clear();
foreach (var group in groups)
{
@@ -201,7 +205,7 @@ namespace Emby.Naming.Video
// The CleanStringParser should have removed common keywords etc.
return testFilename.IsEmpty
|| testFilename[0] == '-'
- || Regex.IsMatch(testFilename, @"^\[([^]]*)\]", RegexOptions.Compiled);
+ || CheckMultiVersionRegex().IsMatch(testFilename);
}
}
}
diff --git a/Emby.Server.Implementations/Library/Resolvers/Movies/MovieResolver.cs b/Emby.Server.Implementations/Library/Resolvers/Movies/MovieResolver.cs
index ea980b992..0b65bf921 100644
--- a/Emby.Server.Implementations/Library/Resolvers/Movies/MovieResolver.cs
+++ b/Emby.Server.Implementations/Library/Resolvers/Movies/MovieResolver.cs
@@ -24,7 +24,7 @@ namespace Emby.Server.Implementations.Library.Resolvers.Movies
/// <summary>
/// Class MovieResolver.
/// </summary>
- public class MovieResolver : BaseVideoResolver<Video>, IMultiItemResolver
+ public partial class MovieResolver : BaseVideoResolver<Video>, IMultiItemResolver
{
private readonly IImageProcessor _imageProcessor;
@@ -56,6 +56,9 @@ namespace Emby.Server.Implementations.Library.Resolvers.Movies
/// <value>The priority.</value>
public override ResolverPriority Priority => ResolverPriority.Fourth;
+ [GeneratedRegex(@"\bsample\b", RegexOptions.IgnoreCase)]
+ private static partial Regex IsIgnoredRegex();
+
/// <inheritdoc />
public MultiItemResolverResult ResolveMultiple(
Folder parent,
@@ -261,7 +264,7 @@ namespace Emby.Server.Implementations.Library.Resolvers.Movies
{
leftOver.Add(child);
}
- else if (!IsIgnored(child.Name))
+ else if (!IsIgnoredRegex().IsMatch(child.Name))
{
files.Add(child);
}
@@ -314,9 +317,6 @@ namespace Emby.Server.Implementations.Library.Resolvers.Movies
return result;
}
- private static bool IsIgnored(ReadOnlySpan<char> filename)
- => Regex.IsMatch(filename, @"\bsample\b", RegexOptions.IgnoreCase | RegexOptions.Compiled);
-
private static bool ContainsFile(IReadOnlyList<VideoInfo> result, FileSystemMetadata file)
{
for (var i = 0; i < result.Count; i++)
diff --git a/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/LegacyHdHomerunChannelCommands.cs b/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/LegacyHdHomerunChannelCommands.cs
index 3450f971f..654474e97 100644
--- a/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/LegacyHdHomerunChannelCommands.cs
+++ b/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/LegacyHdHomerunChannelCommands.cs
@@ -5,7 +5,7 @@ using System.Text.RegularExpressions;
namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
{
- public class LegacyHdHomerunChannelCommands : IHdHomerunChannelCommands
+ public partial class LegacyHdHomerunChannelCommands : IHdHomerunChannelCommands
{
private string? _channel;
private string? _program;
@@ -13,7 +13,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
public LegacyHdHomerunChannelCommands(string url)
{
// parse url for channel and program
- var match = Regex.Match(url, @"\/ch([0-9]+)-?([0-9]*)");
+ var match = ChannelAndProgramRegex().Match(url);
if (match.Success)
{
_channel = match.Groups[1].Value;
@@ -21,6 +21,9 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
}
}
+ [GeneratedRegex(@"\/ch([0-9]+)-?([0-9]*)")]
+ private static partial Regex ChannelAndProgramRegex();
+
public IEnumerable<(string CommandName, string CommandValue)> GetCommands()
{
if (!string.IsNullOrEmpty(_channel))
diff --git a/Emby.Server.Implementations/LiveTv/TunerHosts/M3uParser.cs b/Emby.Server.Implementations/LiveTv/TunerHosts/M3uParser.cs
index b41816230..df9101f48 100644
--- a/Emby.Server.Implementations/LiveTv/TunerHosts/M3uParser.cs
+++ b/Emby.Server.Implementations/LiveTv/TunerHosts/M3uParser.cs
@@ -20,7 +20,7 @@ using Microsoft.Extensions.Logging;
namespace Emby.Server.Implementations.LiveTv.TunerHosts
{
- public class M3uParser
+ public partial class M3uParser
{
private const string ExtInfPrefix = "#EXTINF:";
@@ -33,6 +33,9 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts
_httpClientFactory = httpClientFactory;
}
+ [GeneratedRegex(@"([a-z0-9\-_]+)=\""([^""]+)\""", RegexOptions.IgnoreCase, "en-US")]
+ private static partial Regex KeyValueRegex();
+
public async Task<List<ChannelInfo>> Parse(TunerHostInfo info, string channelIdPrefix, CancellationToken cancellationToken)
{
// Read the file and display it line by line.
@@ -311,7 +314,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts
{
var dict = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
- var matches = Regex.Matches(line, @"([a-z0-9\-_]+)=\""([^""]+)\""", RegexOptions.IgnoreCase);
+ var matches = KeyValueRegex().Matches(line);
remaining = line;
@@ -320,7 +323,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts
var key = match.Groups[1].Value;
var value = match.Groups[2].Value;
- dict[match.Groups[1].Value] = match.Groups[2].Value;
+ dict[key] = value;
remaining = remaining.Replace(key + "=\"" + value + "\"", string.Empty, StringComparison.OrdinalIgnoreCase);
}
diff --git a/Jellyfin.Server.Implementations/Users/UserManager.cs b/Jellyfin.Server.Implementations/Users/UserManager.cs
index 1d03baa4c..ec0c64cd7 100644
--- a/Jellyfin.Server.Implementations/Users/UserManager.cs
+++ b/Jellyfin.Server.Implementations/Users/UserManager.cs
@@ -31,7 +31,7 @@ namespace Jellyfin.Server.Implementations.Users
/// <summary>
/// Manages the creation and retrieval of <see cref="User"/> instances.
/// </summary>
- public class UserManager : IUserManager
+ public partial class UserManager : IUserManager
{
private readonly IDbContextFactory<JellyfinDbContext> _dbProvider;
private readonly IEventManager _eventManager;
@@ -105,6 +105,12 @@ namespace Jellyfin.Server.Implementations.Users
/// <inheritdoc/>
public IEnumerable<Guid> UsersIds => _users.Keys;
+ // This is some regex that matches only on unicode "word" characters, as well as -, _ and @
+ // In theory this will cut out most if not all 'control' characters which should help minimize any weirdness
+ // Usernames can contain letters (a-z + whatever else unicode is cool with), numbers (0-9), at-signs (@), dashes (-), underscores (_), apostrophes ('), periods (.) and spaces ( )
+ [GeneratedRegex("^[\\w\\ \\-'._@]+$")]
+ private static partial Regex ValidUsernameRegex();
+
/// <inheritdoc/>
public User? GetUserById(Guid id)
{
@@ -527,7 +533,7 @@ namespace Jellyfin.Server.Implementations.Users
}
var defaultName = Environment.UserName;
- if (string.IsNullOrWhiteSpace(defaultName) || !IsValidUsername(defaultName))
+ if (string.IsNullOrWhiteSpace(defaultName) || !ValidUsernameRegex().IsMatch(defaultName))
{
defaultName = "MyJellyfinUser";
}
@@ -710,7 +716,7 @@ namespace Jellyfin.Server.Implementations.Users
internal static void ThrowIfInvalidUsername(string name)
{
- if (!string.IsNullOrWhiteSpace(name) && IsValidUsername(name))
+ if (!string.IsNullOrWhiteSpace(name) && ValidUsernameRegex().IsMatch(name))
{
return;
}
@@ -718,14 +724,6 @@ namespace Jellyfin.Server.Implementations.Users
throw new ArgumentException("Usernames can contain unicode symbols, numbers (0-9), dashes (-), underscores (_), apostrophes ('), and periods (.)", nameof(name));
}
- private static bool IsValidUsername(ReadOnlySpan<char> name)
- {
- // This is some regex that matches only on unicode "word" characters, as well as -, _ and @
- // In theory this will cut out most if not all 'control' characters which should help minimize any weirdness
- // Usernames can contain letters (a-z + whatever else unicode is cool with), numbers (0-9), at-signs (@), dashes (-), underscores (_), apostrophes ('), periods (.) and spaces ( )
- return Regex.IsMatch(name, @"^[\w\ \-'._@]+$");
- }
-
private IAuthenticationProvider GetAuthenticationProvider(User user)
{
return GetAuthenticationProviders(user)[0];
diff --git a/MediaBrowser.Common/Extensions/BaseExtensions.cs b/MediaBrowser.Common/Extensions/BaseExtensions.cs
index e3775021e..3615b662b 100644
--- a/MediaBrowser.Common/Extensions/BaseExtensions.cs
+++ b/MediaBrowser.Common/Extensions/BaseExtensions.cs
@@ -8,20 +8,19 @@ namespace MediaBrowser.Common.Extensions
/// <summary>
/// Class BaseExtensions.
/// </summary>
- public static class BaseExtensions
+ public static partial class BaseExtensions
{
+ // http://stackoverflow.com/questions/1349023/how-can-i-strip-html-from-text-in-net
+ [GeneratedRegex(@"<(.|\n)*?>")]
+ private static partial Regex StripHtmlRegex();
+
/// <summary>
/// Strips the HTML.
/// </summary>
/// <param name="htmlString">The HTML string.</param>
/// <returns><see cref="string" />.</returns>
public static string StripHtml(this string htmlString)
- {
- // http://stackoverflow.com/questions/1349023/how-can-i-strip-html-from-text-in-net
- const string Pattern = @"<(.|\n)*?>";
-
- return Regex.Replace(htmlString, Pattern, string.Empty).Trim();
- }
+ => StripHtmlRegex().Replace(htmlString, string.Empty).Trim();
/// <summary>
/// Gets the Md5.
diff --git a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs
index e18c1733e..750713694 100644
--- a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs
+++ b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs
@@ -23,7 +23,7 @@ using Microsoft.Extensions.Configuration;
namespace MediaBrowser.Controller.MediaEncoding
{
- public class EncodingHelper
+ public partial class EncodingHelper
{
private const string QsvAlias = "qs";
private const string VaapiAlias = "va";
@@ -120,6 +120,9 @@ namespace MediaBrowser.Controller.MediaEncoding
_config = config;
}
+ [GeneratedRegex(@"\s+")]
+ private static partial Regex WhiteSpaceRegex();
+
public string GetH264Encoder(EncodingJobInfo state, EncodingOptions encodingOptions)
=> GetH26xOrAv1Encoder("libx264", "h264", state, encodingOptions);
@@ -1831,7 +1834,7 @@ namespace MediaBrowser.Controller.MediaEncoding
}
var profile = state.GetRequestedProfiles(targetVideoCodec).FirstOrDefault() ?? string.Empty;
- profile = Regex.Replace(profile, @"\s+", string.Empty);
+ profile = WhiteSpaceRegex().Replace(profile, string.Empty);
// We only transcode to HEVC 8-bit for now, force Main Profile.
if (profile.Contains("main10", StringComparison.OrdinalIgnoreCase)
diff --git a/MediaBrowser.MediaEncoding/Encoder/EncoderValidator.cs b/MediaBrowser.MediaEncoding/Encoder/EncoderValidator.cs
index e1a0e8d67..38118ed0e 100644
--- a/MediaBrowser.MediaEncoding/Encoder/EncoderValidator.cs
+++ b/MediaBrowser.MediaEncoding/Encoder/EncoderValidator.cs
@@ -10,7 +10,7 @@ using Microsoft.Extensions.Logging;
namespace MediaBrowser.MediaEncoding.Encoder
{
- public class EncoderValidator
+ public partial class EncoderValidator
{
private static readonly string[] _requiredDecoders = new[]
{
@@ -165,6 +165,12 @@ namespace MediaBrowser.MediaEncoding.Encoder
public static Version? MaxVersion { get; } = null;
+ [GeneratedRegex(@"^ffmpeg version n?((?:[0-9]+\.?)+)")]
+ private static partial Regex FfmpegVersionRegex();
+
+ [GeneratedRegex(@"((?<name>lib\w+)\s+(?<major>[0-9]+)\.\s*(?<minor>[0-9]+))", RegexOptions.Multiline)]
+ private static partial Regex LibraryRegex();
+
public bool ValidateVersion()
{
string output;
@@ -283,7 +289,7 @@ namespace MediaBrowser.MediaEncoding.Encoder
internal Version? GetFFmpegVersionInternal(string output)
{
// For pre-built binaries the FFmpeg version should be mentioned at the very start of the output
- var match = Regex.Match(output, @"^ffmpeg version n?((?:[0-9]+\.?)+)");
+ var match = FfmpegVersionRegex().Match(output);
if (match.Success)
{
@@ -331,10 +337,7 @@ namespace MediaBrowser.MediaEncoding.Encoder
{
var map = new Dictionary<string, Version>();
- foreach (Match match in Regex.Matches(
- output,
- @"((?<name>lib\w+)\s+(?<major>[0-9]+)\.\s*(?<minor>[0-9]+))",
- RegexOptions.Multiline))
+ foreach (Match match in LibraryRegex().Matches(output))
{
var version = new Version(
int.Parse(match.Groups["major"].ValueSpan, CultureInfo.InvariantCulture),
diff --git a/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs b/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs
index 4e63d205c..0885fbe92 100644
--- a/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs
+++ b/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs
@@ -36,7 +36,7 @@ namespace MediaBrowser.MediaEncoding.Encoder
/// <summary>
/// Class MediaEncoder.
/// </summary>
- public class MediaEncoder : IMediaEncoder, IDisposable
+ public partial class MediaEncoder : IMediaEncoder, IDisposable
{
/// <summary>
/// The default SDR image extraction timeout in milliseconds.
@@ -142,6 +142,9 @@ namespace MediaBrowser.MediaEncoding.Encoder
/// <inheritdoc />
public bool IsVaapiDeviceSupportVulkanFmtModifier => _isVaapiDeviceSupportVulkanFmtModifier;
+ [GeneratedRegex(@"[^\/\\]+?(\.[^\/\\\n.]+)?$")]
+ private static partial Regex FfprobePathRegex();
+
/// <summary>
/// Run at startup or if the user removes a Custom path from transcode page.
/// Sets global variables FFmpegPath.
@@ -176,7 +179,7 @@ namespace MediaBrowser.MediaEncoding.Encoder
if (_ffmpegPath is not null)
{
// Determine a probe path from the mpeg path
- _ffprobePath = Regex.Replace(_ffmpegPath, @"[^\/\\]+?(\.[^\/\\\n.]+)?$", @"ffprobe$1");
+ _ffprobePath = FfprobePathRegex().Replace(_ffmpegPath, @"ffprobe$1");
// Interrogate to understand what coders are supported
var validator = new EncoderValidator(_logger, _ffmpegPath);
@@ -416,8 +419,8 @@ namespace MediaBrowser.MediaEncoding.Encoder
public Task<MediaInfo> GetMediaInfo(MediaInfoRequest request, CancellationToken cancellationToken)
{
var extractChapters = request.MediaType == DlnaProfileType.Video && request.ExtractChapters;
- string analyzeDuration = string.Empty;
- string ffmpegAnalyzeDuration = _config.GetFFmpegAnalyzeDuration() ?? string.Empty;
+ var analyzeDuration = string.Empty;
+ var ffmpegAnalyzeDuration = _config.GetFFmpegAnalyzeDuration() ?? string.Empty;
if (request.MediaSource.AnalyzeDurationMs > 0)
{
diff --git a/MediaBrowser.MediaEncoding/Subtitles/AssWriter.cs b/MediaBrowser.MediaEncoding/Subtitles/AssWriter.cs
index 0d1cf6e25..7d7b80e99 100644
--- a/MediaBrowser.MediaEncoding/Subtitles/AssWriter.cs
+++ b/MediaBrowser.MediaEncoding/Subtitles/AssWriter.cs
@@ -11,8 +11,11 @@ namespace MediaBrowser.MediaEncoding.Subtitles
/// <summary>
/// ASS subtitle writer.
/// </summary>
- public class AssWriter : ISubtitleWriter
+ public partial class AssWriter : ISubtitleWriter
{
+ [GeneratedRegex(@"\n", RegexOptions.IgnoreCase)]
+ private static partial Regex NewLineRegex();
+
/// <inheritdoc />
public void Write(SubtitleTrackInfo info, Stream stream, CancellationToken cancellationToken)
{
@@ -40,7 +43,7 @@ namespace MediaBrowser.MediaEncoding.Subtitles
var trackEvent = trackEvents[i];
var startTime = TimeSpan.FromTicks(trackEvent.StartPositionTicks).ToString(timeFormat, CultureInfo.InvariantCulture);
var endTime = TimeSpan.FromTicks(trackEvent.EndPositionTicks).ToString(timeFormat, CultureInfo.InvariantCulture);
- var text = Regex.Replace(trackEvent.Text, @"\n", "\\n", RegexOptions.IgnoreCase);
+ var text = NewLineRegex().Replace(trackEvent.Text, "\\n");
writer.WriteLine(
"Dialogue: 0,{0},{1},Default,{2}",
diff --git a/MediaBrowser.MediaEncoding/Subtitles/SrtWriter.cs b/MediaBrowser.MediaEncoding/Subtitles/SrtWriter.cs
index 143c010b7..86f77aa06 100644
--- a/MediaBrowser.MediaEncoding/Subtitles/SrtWriter.cs
+++ b/MediaBrowser.MediaEncoding/Subtitles/SrtWriter.cs
@@ -11,8 +11,11 @@ namespace MediaBrowser.MediaEncoding.Subtitles
/// <summary>
/// SRT subtitle writer.
/// </summary>
- public class SrtWriter : ISubtitleWriter
+ public partial class SrtWriter : ISubtitleWriter
{
+ [GeneratedRegex(@"\\n", RegexOptions.IgnoreCase)]
+ private static partial Regex NewLineEscapedRegex();
+
/// <inheritdoc />
public void Write(SubtitleTrackInfo info, Stream stream, CancellationToken cancellationToken)
{
@@ -35,7 +38,7 @@ namespace MediaBrowser.MediaEncoding.Subtitles
var text = trackEvent.Text;
// TODO: Not sure how to handle these
- text = Regex.Replace(text, @"\\n", " ", RegexOptions.IgnoreCase);
+ text = NewLineEscapedRegex().Replace(text, " ");
writer.WriteLine(text);
writer.WriteLine();
diff --git a/MediaBrowser.MediaEncoding/Subtitles/SsaWriter.cs b/MediaBrowser.MediaEncoding/Subtitles/SsaWriter.cs
index 6761cd309..b5fd1ed93 100644
--- a/MediaBrowser.MediaEncoding/Subtitles/SsaWriter.cs
+++ b/MediaBrowser.MediaEncoding/Subtitles/SsaWriter.cs
@@ -11,8 +11,11 @@ namespace MediaBrowser.MediaEncoding.Subtitles
/// <summary>
/// SSA subtitle writer.
/// </summary>
- public class SsaWriter : ISubtitleWriter
+ public partial class SsaWriter : ISubtitleWriter
{
+ [GeneratedRegex(@"\n", RegexOptions.IgnoreCase)]
+ private static partial Regex NewLineRegex();
+
/// <inheritdoc />
public void Write(SubtitleTrackInfo info, Stream stream, CancellationToken cancellationToken)
{
@@ -40,7 +43,7 @@ namespace MediaBrowser.MediaEncoding.Subtitles
var trackEvent = trackEvents[i];
var startTime = TimeSpan.FromTicks(trackEvent.StartPositionTicks).ToString(timeFormat, CultureInfo.InvariantCulture);
var endTime = TimeSpan.FromTicks(trackEvent.EndPositionTicks).ToString(timeFormat, CultureInfo.InvariantCulture);
- var text = Regex.Replace(trackEvent.Text, @"\n", "\\n", RegexOptions.IgnoreCase);
+ var text = NewLineRegex().Replace(trackEvent.Text, "\\n");
writer.WriteLine(
"Dialogue: 0,{0},{1},Default,{2}",
diff --git a/MediaBrowser.MediaEncoding/Subtitles/TtmlWriter.cs b/MediaBrowser.MediaEncoding/Subtitles/TtmlWriter.cs
index e5c785bc5..ea45f2070 100644
--- a/MediaBrowser.MediaEncoding/Subtitles/TtmlWriter.cs
+++ b/MediaBrowser.MediaEncoding/Subtitles/TtmlWriter.cs
@@ -9,8 +9,11 @@ namespace MediaBrowser.MediaEncoding.Subtitles
/// <summary>
/// TTML subtitle writer.
/// </summary>
- public class TtmlWriter : ISubtitleWriter
+ public partial class TtmlWriter : ISubtitleWriter
{
+ [GeneratedRegex(@"\\n", RegexOptions.IgnoreCase)]
+ private static partial Regex NewLineEscapeRegex();
+
/// <inheritdoc />
public void Write(SubtitleTrackInfo info, Stream stream, CancellationToken cancellationToken)
{
@@ -38,7 +41,7 @@ namespace MediaBrowser.MediaEncoding.Subtitles
{
var text = trackEvent.Text;
- text = Regex.Replace(text, @"\\n", "<br/>", RegexOptions.IgnoreCase);
+ text = NewLineEscapeRegex().Replace(text, "<br/>");
writer.WriteLine(
"<p begin=\"{0}\" dur=\"{1}\">{2}</p>",
diff --git a/MediaBrowser.MediaEncoding/Subtitles/VttWriter.cs b/MediaBrowser.MediaEncoding/Subtitles/VttWriter.cs
index 38ef57dee..3e0f47b5a 100644
--- a/MediaBrowser.MediaEncoding/Subtitles/VttWriter.cs
+++ b/MediaBrowser.MediaEncoding/Subtitles/VttWriter.cs
@@ -10,8 +10,11 @@ namespace MediaBrowser.MediaEncoding.Subtitles
/// <summary>
/// Subtitle writer for the WebVTT format.
/// </summary>
- public class VttWriter : ISubtitleWriter
+ public partial class VttWriter : ISubtitleWriter
{
+ [GeneratedRegex(@"\\n", RegexOptions.IgnoreCase)]
+ private static partial Regex NewlineEscapeRegex();
+
/// <inheritdoc />
public void Write(SubtitleTrackInfo info, Stream stream, CancellationToken cancellationToken)
{
@@ -39,7 +42,7 @@ namespace MediaBrowser.MediaEncoding.Subtitles
var text = trackEvent.Text;
// TODO: Not sure how to handle these
- text = Regex.Replace(text, @"\\n", " ", RegexOptions.IgnoreCase);
+ text = NewlineEscapeRegex().Replace(text, " ");
writer.WriteLine(text);
writer.WriteLine();
diff --git a/MediaBrowser.Model/Dlna/SearchCriteria.cs b/MediaBrowser.Model/Dlna/SearchCriteria.cs
index 77d6a55ea..6f4a692c8 100644
--- a/MediaBrowser.Model/Dlna/SearchCriteria.cs
+++ b/MediaBrowser.Model/Dlna/SearchCriteria.cs
@@ -5,7 +5,7 @@ using System.Text.RegularExpressions;
namespace MediaBrowser.Model.Dlna
{
- public class SearchCriteria
+ public partial class SearchCriteria
{
public SearchCriteria(string search)
{
@@ -13,10 +13,10 @@ namespace MediaBrowser.Model.Dlna
SearchType = SearchType.Unknown;
- string[] factors = RegexSplit(search, "(and|or)");
+ string[] factors = AndOrRegex().Split(search);
foreach (string factor in factors)
{
- string[] subFactors = RegexSplit(factor.Trim().Trim('(').Trim(')').Trim(), "\\s", 3);
+ string[] subFactors = WhiteSpaceRegex().Split(factor.Trim().Trim('(').Trim(')').Trim(), 3);
if (subFactors.Length == 3)
{
@@ -46,27 +46,10 @@ namespace MediaBrowser.Model.Dlna
public SearchType SearchType { get; set; }
- /// <summary>
- /// Splits the specified string.
- /// </summary>
- /// <param name="str">The string.</param>
- /// <param name="term">The term.</param>
- /// <param name="limit">The limit.</param>
- /// <returns>System.String[].</returns>
- private static string[] RegexSplit(string str, string term, int limit)
- {
- return new Regex(term).Split(str, limit);
- }
+ [GeneratedRegex("\\s")]
+ private static partial Regex WhiteSpaceRegex();
- /// <summary>
- /// Splits the specified string.
- /// </summary>
- /// <param name="str">The string.</param>
- /// <param name="term">The term.</param>
- /// <returns>System.String[].</returns>
- private static string[] RegexSplit(string str, string term)
- {
- return Regex.Split(str, term, RegexOptions.IgnoreCase);
- }
+ [GeneratedRegex("(and|or)", RegexOptions.IgnoreCase)]
+ private static partial Regex AndOrRegex();
}
}
diff --git a/MediaBrowser.Providers/MediaInfo/AudioFileProber.cs b/MediaBrowser.Providers/MediaInfo/AudioFileProber.cs
index e1dcbc993..a7e8f774c 100644
--- a/MediaBrowser.Providers/MediaInfo/AudioFileProber.cs
+++ b/MediaBrowser.Providers/MediaInfo/AudioFileProber.cs
@@ -25,7 +25,7 @@ namespace MediaBrowser.Providers.MediaInfo
/// <summary>
/// Probes audio files for metadata.
/// </summary>
- public class AudioFileProber
+ public partial class AudioFileProber
{
// Default LUFS value for use with the web interface, at -18db gain will be 1(no db gain).
private const float DefaultLUFSValue = -18;
@@ -58,6 +58,9 @@ namespace MediaBrowser.Providers.MediaInfo
_mediaSourceManager = mediaSourceManager;
}
+ [GeneratedRegex("I:\\s+(.*?)\\s+LUFS")]
+ private static partial Regex LUFSRegex();
+
/// <summary>
/// Probes the specified item for metadata.
/// </summary>
@@ -129,7 +132,7 @@ namespace MediaBrowser.Providers.MediaInfo
output = await process.StandardError.ReadToEndAsync(cancellationToken).ConfigureAwait(false);
cancellationToken.ThrowIfCancellationRequested();
- MatchCollection split = Regex.Matches(output, @"I:\s+(.*?)\s+LUFS");
+ MatchCollection split = LUFSRegex().Matches(output);
if (split.Count != 0)
{
diff --git a/MediaBrowser.Providers/Plugins/Tmdb/TmdbUtils.cs b/MediaBrowser.Providers/Plugins/Tmdb/TmdbUtils.cs
index 516eee758..a7c93ac4c 100644
--- a/MediaBrowser.Providers/Plugins/Tmdb/TmdbUtils.cs
+++ b/MediaBrowser.Providers/Plugins/Tmdb/TmdbUtils.cs
@@ -11,10 +11,8 @@ namespace MediaBrowser.Providers.Plugins.Tmdb
/// <summary>
/// Utilities for the TMDb provider.
/// </summary>
- public static class TmdbUtils
+ public static partial class TmdbUtils
{
- private static readonly Regex _nonWords = new(@"[\W_]+", RegexOptions.Compiled);
-
/// <summary>
/// URL of the TMDb instance to use.
/// </summary>
@@ -50,6 +48,9 @@ namespace MediaBrowser.Providers.Plugins.Tmdb
PersonKind.Producer
};
+ [GeneratedRegex(@"[\W_]+")]
+ private static partial Regex NonWordRegex();
+
/// <summary>
/// Cleans the name according to TMDb requirements.
/// </summary>
@@ -58,7 +59,7 @@ namespace MediaBrowser.Providers.Plugins.Tmdb
public static string CleanName(string name)
{
// TMDb expects a space separated list of words make sure that is the case
- return _nonWords.Replace(name, " ");
+ return NonWordRegex().Replace(name, " ");
}
/// <summary>
diff --git a/MediaBrowser.XbmcMetadata/Savers/BaseNfoSaver.cs b/MediaBrowser.XbmcMetadata/Savers/BaseNfoSaver.cs
index 4f8f869ac..bf66a3145 100644
--- a/MediaBrowser.XbmcMetadata/Savers/BaseNfoSaver.cs
+++ b/MediaBrowser.XbmcMetadata/Savers/BaseNfoSaver.cs
@@ -27,15 +27,12 @@ using Microsoft.Extensions.Logging;
namespace MediaBrowser.XbmcMetadata.Savers
{
- public abstract class BaseNfoSaver : IMetadataFileSaver
+ public abstract partial class BaseNfoSaver : IMetadataFileSaver
{
public const string DateAddedFormat = "yyyy-MM-dd HH:mm:ss";
public const string YouTubeWatchUrl = "https://www.youtube.com/watch?v=";
- // filters control characters but allows only properly-formed surrogate sequences
- private const string _invalidXMLCharsRegex = @"(?<![\uD800-\uDBFF])[\uDC00-\uDFFF]|[\uD800-\uDBFF](?![\uDC00-\uDFFF])|[\x00-\x08\x0B\x0C\x0E-\x1F\x7F-\x9F\uFEFF\uFFFE\uFFFF]";
-
private static readonly HashSet<string> _commonTags = new HashSet<string>(StringComparer.OrdinalIgnoreCase)
{
"plot",
@@ -148,6 +145,12 @@ namespace MediaBrowser.XbmcMetadata.Savers
public static string SaverName => "Nfo";
+ // filters control characters but allows only properly-formed surrogate sequences
+ // http://web.archive.org/web/20181230211547/https://emby.media/community/index.php?/topic/49071-nfo-not-generated-on-actualize-or-rescan-or-identify
+ // Web Archive version of link since it's not really explained in the thread.
+ [GeneratedRegex(@"(?<![\uD800-\uDBFF])[\uDC00-\uDFFF]|[\uD800-\uDBFF](?![\uDC00-\uDFFF])|[\x00-\x08\x0B\x0C\x0E-\x1F\x7F-\x9F\uFEFF\uFFFE\uFFFF]")]
+ private static partial Regex InvalidXMLCharsRegexRegex();
+
/// <inheritdoc />
public string GetSavePath(BaseItem item)
=> GetLocalSavePath(item);
@@ -354,9 +357,7 @@ namespace MediaBrowser.XbmcMetadata.Savers
if (!string.IsNullOrEmpty(stream.Language))
{
- // http://web.archive.org/web/20181230211547/https://emby.media/community/index.php?/topic/49071-nfo-not-generated-on-actualize-or-rescan-or-identify
- // Web Archive version of link since it's not really explained in the thread.
- writer.WriteElementString("language", Regex.Replace(stream.Language, _invalidXMLCharsRegex, string.Empty));
+ writer.WriteElementString("language", InvalidXMLCharsRegexRegex().Replace(stream.Language, string.Empty));
}
var scanType = stream.IsInterlaced ? "interlaced" : "progressive";
diff --git a/jellyfin.ruleset b/jellyfin.ruleset
index b611caa11..c846e2cd4 100644
--- a/jellyfin.ruleset
+++ b/jellyfin.ruleset
@@ -60,6 +60,8 @@
<Rule Id="SA1515" Action="None" />
<!-- disable warning SA1600: Elements should be documented -->
<Rule Id="SA1600" Action="None" />
+ <!-- disable warning SA1601: Partial elements should be documented -->
+ <Rule Id="SA1601" Action="None" />
<!-- disable warning SA1602: Enumeration items should be documented -->
<Rule Id="SA1602" Action="None" />
<!-- disable warning SA1633: The file header is missing or not located at the top of the file -->
diff --git a/src/Jellyfin.Drawing.Skia/StripCollageBuilder.cs b/src/Jellyfin.Drawing.Skia/StripCollageBuilder.cs
index a7a3338df..a8f80f7e2 100644
--- a/src/Jellyfin.Drawing.Skia/StripCollageBuilder.cs
+++ b/src/Jellyfin.Drawing.Skia/StripCollageBuilder.cs
@@ -23,6 +23,9 @@ public partial class StripCollageBuilder
_skiaEncoder = skiaEncoder;
}
+ [GeneratedRegex(@"[^\p{IsCJKUnifiedIdeographs}\p{IsCJKUnifiedIdeographsExtensionA}\p{IsKatakana}\p{IsHiragana}\p{IsHangulSyllables}\p{IsHangulJamo}]")]
+ private static partial Regex NonCjkPatternRegex();
+
[GeneratedRegex(@"\p{IsArabic}|\p{IsArmenian}|\p{IsHebrew}|\p{IsSyriac}|\p{IsThaana}")]
private static partial Regex IsRtlTextRegex();
@@ -123,8 +126,7 @@ public partial class StripCollageBuilder
var typeFace = SKTypeface.FromFamilyName("sans-serif", SKFontStyleWeight.Bold, SKFontStyleWidth.Normal, SKFontStyleSlant.Upright);
// use the system fallback to find a typeface for the given CJK character
- var nonCjkPattern = @"[^\p{IsCJKUnifiedIdeographs}\p{IsCJKUnifiedIdeographsExtensionA}\p{IsKatakana}\p{IsHiragana}\p{IsHangulSyllables}\p{IsHangulJamo}]";
- var filteredName = Regex.Replace(libraryName ?? string.Empty, nonCjkPattern, string.Empty);
+ var filteredName = NonCjkPatternRegex().Replace(libraryName ?? string.Empty, string.Empty);
if (!string.IsNullOrEmpty(filteredName))
{
typeFace = SKFontManager.Default.MatchCharacter(null, SKFontStyleWeight.Bold, SKFontStyleWidth.Normal, SKFontStyleSlant.Upright, null, filteredName[0]);
diff --git a/src/Jellyfin.Extensions/StringExtensions.cs b/src/Jellyfin.Extensions/StringExtensions.cs
index b22eb7c4e..fd8f7e59a 100644
--- a/src/Jellyfin.Extensions/StringExtensions.cs
+++ b/src/Jellyfin.Extensions/StringExtensions.cs
@@ -6,11 +6,13 @@ namespace Jellyfin.Extensions
/// <summary>
/// Provides extensions methods for <see cref="string" />.
/// </summary>
- public static class StringExtensions
+ public static partial class StringExtensions
{
// Matches non-conforming unicode chars
// https://mnaoumov.wordpress.com/2014/06/14/stripping-invalid-characters-from-utf-16-strings/
- private static readonly Regex _nonConformingUnicode = new Regex("([\ud800-\udbff](?![\udc00-\udfff]))|((?<![\ud800-\udbff])[\udc00-\udfff])|(\ufffd)", RegexOptions.Compiled);
+
+ [GeneratedRegex("([\ud800-\udbff](?![\udc00-\udfff]))|((?<![\ud800-\udbff])[\udc00-\udfff])|(�)")]
+ private static partial Regex NonConformingUnicodeRegex();
/// <summary>
/// Removes the diacritics character from the strings.
@@ -19,7 +21,7 @@ namespace Jellyfin.Extensions
/// <returns>The string without diacritics character.</returns>
public static string RemoveDiacritics(this string text)
=> Diacritics.Extensions.StringExtensions.RemoveDiacritics(
- _nonConformingUnicode.Replace(text, string.Empty));
+ NonConformingUnicodeRegex().Replace(text, string.Empty));
/// <summary>
/// Checks whether or not the specified string has diacritics in it.
@@ -28,7 +30,7 @@ namespace Jellyfin.Extensions
/// <returns>True if the string has diacritics, false otherwise.</returns>
public static bool HasDiacritics(this string text)
=> Diacritics.Extensions.StringExtensions.HasDiacritics(text)
- || _nonConformingUnicode.IsMatch(text);
+ || NonConformingUnicodeRegex().IsMatch(text);
/// <summary>
/// Counts the number of occurrences of [needle] in the string.
diff --git a/tests/Jellyfin.Providers.Tests/Manager/ItemImageProviderTests.cs b/tests/Jellyfin.Providers.Tests/Manager/ItemImageProviderTests.cs
index 925e8fa19..f157f01e5 100644
--- a/tests/Jellyfin.Providers.Tests/Manager/ItemImageProviderTests.cs
+++ b/tests/Jellyfin.Providers.Tests/Manager/ItemImageProviderTests.cs
@@ -25,10 +25,13 @@ using Xunit;
namespace Jellyfin.Providers.Tests.Manager
{
- public class ItemImageProviderTests
+ public partial class ItemImageProviderTests
{
private const string TestDataImagePath = "Test Data/Images/blank{0}.jpg";
+ [GeneratedRegex("[0-9]+")]
+ private static partial Regex NumbersRegex();
+
[Fact]
public void ValidateImages_PhotoEmptyProviders_NoChange()
{
@@ -463,7 +466,7 @@ namespace Jellyfin.Providers.Tests.Manager
// images from the provider manager are sorted by preference (earlier images are higher priority) so we can verify that low url numbers are chosen
foreach (var image in actualImages)
{
- var index = int.Parse(Regex.Match(image.Path, @"[0-9]+").Value, NumberStyles.Integer, CultureInfo.InvariantCulture);
+ var index = int.Parse(NumbersRegex().Match(image.Path).ValueSpan, NumberStyles.Integer, CultureInfo.InvariantCulture);
Assert.True(index < imageCount);
}
}