Normalize fix, apply in more places
This commit is contained in:
@@ -174,7 +174,7 @@ public partial class AudioNormalizationTask : IScheduledTask
|
||||
if (!t.NormalizationGain.HasValue && !t.LUFS.HasValue && t.IsFileProtocol)
|
||||
{
|
||||
t.LUFS = await CalculateLUFSAsync(
|
||||
string.Format(CultureInfo.InvariantCulture, "-i \"{0}\"", t.Path.Replace("\"", "\\\"", StringComparison.Ordinal)),
|
||||
string.Format(CultureInfo.InvariantCulture, "-i \"{0}\"", t.Path.EscapeProcessArgument()),
|
||||
false,
|
||||
cancellationToken).ConfigureAwait(false);
|
||||
toSaveDbItems.Add(t);
|
||||
|
||||
@@ -20,7 +20,6 @@ using MediaBrowser.Controller.Configuration;
|
||||
using MediaBrowser.Controller.Library;
|
||||
using MediaBrowser.Controller.MediaEncoding;
|
||||
using MediaBrowser.Controller.Streaming;
|
||||
using MediaBrowser.MediaEncoding.Encoder;
|
||||
using MediaBrowser.Model.Configuration;
|
||||
using MediaBrowser.Model.Dlna;
|
||||
using MediaBrowser.Model.Entities;
|
||||
@@ -1652,9 +1651,9 @@ public class DynamicHlsController : BaseJellyfinApiController
|
||||
segmentFormat,
|
||||
startNumber.ToString(CultureInfo.InvariantCulture),
|
||||
baseUrlParam,
|
||||
EncodingUtils.NormalizePath(outputTsArg),
|
||||
outputTsArg.EscapeProcessArgument(),
|
||||
hlsArguments,
|
||||
EncodingUtils.NormalizePath(outputPath)).Trim();
|
||||
outputPath.EscapeProcessArgument()).Trim();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -1318,7 +1318,7 @@ namespace MediaBrowser.Controller.MediaEncoding
|
||||
arg.Append(canvasArgs);
|
||||
}
|
||||
|
||||
arg.Append(" -i file:\"").Append(subtitlePath.Replace("\"", "\\\"", StringComparison.Ordinal)).Append('\"');
|
||||
arg.Append(" -i file:\"").Append(subtitlePath.EscapeProcessArgument()).Append('\"');
|
||||
}
|
||||
|
||||
if (state.AudioStream is not null && state.AudioStream.IsExternal)
|
||||
@@ -1330,7 +1330,7 @@ namespace MediaBrowser.Controller.MediaEncoding
|
||||
arg.Append(' ').Append(seekAudioParam);
|
||||
}
|
||||
|
||||
arg.Append(" -i \"").Append(state.AudioStream.Path.Replace("\"", "\\\"", StringComparison.Ordinal)).Append('"');
|
||||
arg.Append(" -i \"").Append(state.AudioStream.Path.EscapeProcessArgument()).Append('"');
|
||||
}
|
||||
|
||||
// Disable auto inserted SW scaler for HW decoders in case of changed resolution.
|
||||
|
||||
@@ -14,7 +14,6 @@ using MediaBrowser.Controller.Entities;
|
||||
using MediaBrowser.Controller.IO;
|
||||
using MediaBrowser.Controller.Library;
|
||||
using MediaBrowser.Controller.MediaEncoding;
|
||||
using MediaBrowser.MediaEncoding.Encoder;
|
||||
using MediaBrowser.Model.Dto;
|
||||
using MediaBrowser.Model.Entities;
|
||||
using MediaBrowser.Model.IO;
|
||||
@@ -160,7 +159,7 @@ namespace MediaBrowser.MediaEncoding.Attachments
|
||||
CultureInfo.InvariantCulture,
|
||||
"-dump_attachment:{0} \"{1}\" ",
|
||||
attachment.Index,
|
||||
EncodingUtils.NormalizePath(attachmentPath));
|
||||
attachmentPath.EscapeProcessArgument());
|
||||
missingPaths.Add(attachmentPath);
|
||||
}
|
||||
|
||||
@@ -425,7 +424,7 @@ namespace MediaBrowser.MediaEncoding.Attachments
|
||||
"-dump_attachment:{1} \"{2}\" -i {0} {3}",
|
||||
inputPath,
|
||||
attachmentStreamIndex,
|
||||
EncodingUtils.NormalizePath(outputPath),
|
||||
outputPath.EscapeProcessArgument(),
|
||||
hasVideoOrAudioStream ? "-t 0 -f null null" : string.Empty);
|
||||
|
||||
int exitCode;
|
||||
|
||||
@@ -4,6 +4,7 @@ using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using Jellyfin.Extensions;
|
||||
using MediaBrowser.Model.MediaInfo;
|
||||
|
||||
namespace MediaBrowser.MediaEncoding.Encoder
|
||||
@@ -42,7 +43,7 @@ namespace MediaBrowser.MediaEncoding.Encoder
|
||||
// If there's more than one we'll need to use the concat command
|
||||
if (inputFiles.Count > 1)
|
||||
{
|
||||
var files = string.Join('|', inputFiles.Select(NormalizePath));
|
||||
var files = string.Join('|', inputFiles.Select(f => f.EscapeProcessArgument()));
|
||||
|
||||
return string.Format(CultureInfo.InvariantCulture, "concat:\"{0}\"", files);
|
||||
}
|
||||
@@ -64,21 +65,9 @@ namespace MediaBrowser.MediaEncoding.Encoder
|
||||
return string.Format(CultureInfo.InvariantCulture, "\"{0}\"", path);
|
||||
}
|
||||
|
||||
// Quotes are valid path characters in linux and they need to be escaped here with a leading \
|
||||
path = NormalizePath(path);
|
||||
path = path.EscapeProcessArgument();
|
||||
|
||||
return string.Format(CultureInfo.InvariantCulture, "{1}:\"{0}\"", path, inputPrefix);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Normalizes the path.
|
||||
/// </summary>
|
||||
/// <param name="path">The path.</param>
|
||||
/// <returns>System.String.</returns>
|
||||
public static string NormalizePath(string path)
|
||||
{
|
||||
// Quotes are valid path characters in linux and they need to be escaped here with a leading \
|
||||
return path.Replace("\"", "\\\"", StringComparison.Ordinal);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,6 +12,7 @@ using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using AsyncKeyedLock;
|
||||
using Jellyfin.Extensions;
|
||||
using MediaBrowser.Common;
|
||||
using MediaBrowser.Common.Configuration;
|
||||
using MediaBrowser.Common.Extensions;
|
||||
@@ -21,7 +22,6 @@ using MediaBrowser.Controller.Entities;
|
||||
using MediaBrowser.Controller.IO;
|
||||
using MediaBrowser.Controller.Library;
|
||||
using MediaBrowser.Controller.MediaEncoding;
|
||||
using MediaBrowser.MediaEncoding.Encoder;
|
||||
using MediaBrowser.Model.Dto;
|
||||
using MediaBrowser.Model.Entities;
|
||||
using MediaBrowser.Model.IO;
|
||||
@@ -454,7 +454,7 @@ namespace MediaBrowser.MediaEncoding.Subtitles
|
||||
encodingParam = " -sub_charenc " + encodingParam;
|
||||
}
|
||||
|
||||
var args = string.Format(CultureInfo.InvariantCulture, "-y {0} -i \"{1}\" -c:s srt \"{2}\"", encodingParam, EncodingUtils.NormalizePath(inputPath), EncodingUtils.NormalizePath(outputPath));
|
||||
var args = string.Format(CultureInfo.InvariantCulture, "-y {0} -i \"{1}\" -c:s srt \"{2}\"", encodingParam, inputPath.EscapeProcessArgument(), outputPath.EscapeProcessArgument());
|
||||
|
||||
await ExtractSubtitlesForFile(
|
||||
inputPath,
|
||||
@@ -632,7 +632,7 @@ namespace MediaBrowser.MediaEncoding.Subtitles
|
||||
streamIndex,
|
||||
outputCodec,
|
||||
outputFormatOption,
|
||||
EncodingUtils.NormalizePath(outputPath));
|
||||
outputPath.EscapeProcessArgument());
|
||||
}
|
||||
|
||||
await ExtractSubtitlesForFile(inputPath, args, outputPaths, cancellationToken).ConfigureAwait(false);
|
||||
@@ -690,7 +690,7 @@ namespace MediaBrowser.MediaEncoding.Subtitles
|
||||
streamIndex,
|
||||
outputCodec,
|
||||
outputFormatOption,
|
||||
EncodingUtils.NormalizePath(outputPath));
|
||||
outputPath.EscapeProcessArgument());
|
||||
}
|
||||
|
||||
if (outputPaths.Count > 0)
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Text.RegularExpressions;
|
||||
using ICU4N.Text;
|
||||
|
||||
@@ -173,5 +174,41 @@ namespace Jellyfin.Extensions
|
||||
|
||||
return cleaned;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Escapes an argument so that it survives command line parsing as a single argument when it is wrapped in double quotes by the caller.
|
||||
/// </summary>
|
||||
/// <param name="value">The argument to escape.</param>
|
||||
/// <returns>The escaped argument.</returns>
|
||||
public static string EscapeProcessArgument(this string value)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(value);
|
||||
|
||||
var span = value.AsSpan();
|
||||
if (!span.Contains('"'))
|
||||
{
|
||||
var trailing = span.Length - span.TrimEnd('\\').Length;
|
||||
return trailing == 0 ? value : string.Concat(value, new string('\\', trailing));
|
||||
}
|
||||
|
||||
var escaped = new StringBuilder(value.Length + 8);
|
||||
var backslashes = 0;
|
||||
|
||||
foreach (var character in span)
|
||||
{
|
||||
if (character == '\\')
|
||||
{
|
||||
backslashes++;
|
||||
continue;
|
||||
}
|
||||
|
||||
escaped
|
||||
.Append('\\', character == '"' ? (backslashes * 2) + 1 : backslashes)
|
||||
.Append(character);
|
||||
backslashes = 0;
|
||||
}
|
||||
|
||||
return escaped.Append('\\', backslashes * 2).ToString();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -188,8 +188,8 @@ namespace Jellyfin.LiveTv.IO
|
||||
var commandLineArgs = string.Format(
|
||||
CultureInfo.InvariantCulture,
|
||||
"-i \"{0}\" {2} -map_metadata -1 -threads {6} {3}{4}{5} -y \"{1}\"",
|
||||
inputTempFile,
|
||||
targetFile.Replace("\"", "\\\"", StringComparison.Ordinal), // Escape quotes in filename
|
||||
inputTempFile.EscapeProcessArgument(),
|
||||
targetFile.EscapeProcessArgument(),
|
||||
videoArgs,
|
||||
GetAudioArgs(mediaSource),
|
||||
subtitleArgs,
|
||||
|
||||
@@ -75,5 +75,28 @@ namespace Jellyfin.Extensions.Tests
|
||||
var result = str.AsSpan().RightPart(needle).ToString();
|
||||
Assert.Equal(expectedResult, result);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData("", "")]
|
||||
[InlineData("/media/movies/Film.mkv", "/media/movies/Film.mkv")]
|
||||
[InlineData(@"C:\media\movies\Film.mkv", @"C:\media\movies\Film.mkv")]
|
||||
[InlineData(@"/media/a""b.mkv", @"/media/a\""b.mkv")]
|
||||
[InlineData(@"/media/a\""b.mkv", @"/media/a\\\""b.mkv")]
|
||||
[InlineData(@"/media/a\\""b.mkv", @"/media/a\\\\\""b.mkv")]
|
||||
[InlineData(@"/media/a\b""c.mkv", @"/media/a\b\""c.mkv")]
|
||||
[InlineData(@"/media/trailing\", @"/media/trailing\\")]
|
||||
[InlineData(@"/media/evil\"" -f lavfi -i sine .mkv", @"/media/evil\\\"" -f lavfi -i sine .mkv")]
|
||||
public void EscapeProcessArgument_ValidInput_Corrects(string input, string expectedResult)
|
||||
{
|
||||
Assert.Equal(expectedResult, input.EscapeProcessArgument());
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData("/media/movies/Film with spaces.mkv")]
|
||||
[InlineData(@"C:\media\movies\Film.mkv")]
|
||||
public void EscapeProcessArgument_NothingToEscape_ReturnsSameInstance(string input)
|
||||
{
|
||||
Assert.Same(input, input.EscapeProcessArgument());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user