diff --git a/Jellyfin.Api/Controllers/AudioController.cs b/Jellyfin.Api/Controllers/AudioController.cs index cd09d2bfab..72be555133 100644 --- a/Jellyfin.Api/Controllers/AudioController.cs +++ b/Jellyfin.Api/Controllers/AudioController.cs @@ -91,18 +91,18 @@ public class AudioController : BaseJellyfinApiController [ProducesAudioFile] public async Task GetAudioStream( [FromRoute, Required] Guid itemId, - [FromQuery] string? container, + [FromQuery] [RegularExpression(EncodingHelper.ValidationRegex)] string? container, [FromQuery] bool? @static, [FromQuery] string? @params, [FromQuery] string? tag, [FromQuery, ParameterObsolete] string? deviceProfileId, [FromQuery] string? playSessionId, - [FromQuery] string? segmentContainer, + [FromQuery] [RegularExpression(EncodingHelper.ValidationRegex)] string? segmentContainer, [FromQuery] int? segmentLength, [FromQuery] int? minSegments, [FromQuery] string? mediaSourceId, [FromQuery] string? deviceId, - [FromQuery] string? audioCodec, + [FromQuery] [RegularExpression(EncodingHelper.ValidationRegex)] string? audioCodec, [FromQuery] bool? enableAutoStreamCopy, [FromQuery] bool? allowVideoStreamCopy, [FromQuery] bool? allowAudioStreamCopy, @@ -132,8 +132,8 @@ public class AudioController : BaseJellyfinApiController [FromQuery] int? cpuCoreLimit, [FromQuery] string? liveStreamId, [FromQuery] bool? enableMpegtsM2TsMode, - [FromQuery] string? videoCodec, - [FromQuery] string? subtitleCodec, + [FromQuery] [RegularExpression(EncodingHelper.ValidationRegex)] string? videoCodec, + [FromQuery] [RegularExpression(EncodingHelper.ValidationRegex)] string? subtitleCodec, [FromQuery] string? transcodeReasons, [FromQuery] int? audioStreamIndex, [FromQuery] int? videoStreamIndex, @@ -261,12 +261,12 @@ public class AudioController : BaseJellyfinApiController [FromQuery] string? tag, [FromQuery, ParameterObsolete] string? deviceProfileId, [FromQuery] string? playSessionId, - [FromQuery] string? segmentContainer, + [FromQuery] [RegularExpression(EncodingHelper.ValidationRegex)] string? segmentContainer, [FromQuery] int? segmentLength, [FromQuery] int? minSegments, [FromQuery] string? mediaSourceId, [FromQuery] string? deviceId, - [FromQuery] string? audioCodec, + [FromQuery] [RegularExpression(EncodingHelper.ValidationRegex)] string? audioCodec, [FromQuery] bool? enableAutoStreamCopy, [FromQuery] bool? allowVideoStreamCopy, [FromQuery] bool? allowAudioStreamCopy, @@ -296,8 +296,8 @@ public class AudioController : BaseJellyfinApiController [FromQuery] int? cpuCoreLimit, [FromQuery] string? liveStreamId, [FromQuery] bool? enableMpegtsM2TsMode, - [FromQuery] string? videoCodec, - [FromQuery] string? subtitleCodec, + [FromQuery] [RegularExpression(EncodingHelper.ValidationRegex)] string? videoCodec, + [FromQuery] [RegularExpression(EncodingHelper.ValidationRegex)] string? subtitleCodec, [FromQuery] string? transcodeReasons, [FromQuery] int? audioStreamIndex, [FromQuery] int? videoStreamIndex, diff --git a/Jellyfin.Api/Controllers/DynamicHlsController.cs b/Jellyfin.Api/Controllers/DynamicHlsController.cs index e5be48b808..49fc2f3d78 100644 --- a/Jellyfin.Api/Controllers/DynamicHlsController.cs +++ b/Jellyfin.Api/Controllers/DynamicHlsController.cs @@ -163,18 +163,18 @@ public class DynamicHlsController : BaseJellyfinApiController [ProducesPlaylistFile] public async Task GetLiveHlsStream( [FromRoute, Required] Guid itemId, - [FromQuery] string? container, + [FromQuery] [RegularExpression(EncodingHelper.ValidationRegex)] string? container, [FromQuery] bool? @static, [FromQuery] string? @params, [FromQuery] string? tag, [FromQuery, ParameterObsolete] string? deviceProfileId, [FromQuery] string? playSessionId, - [FromQuery] string? segmentContainer, + [FromQuery] [RegularExpression(EncodingHelper.ValidationRegex)] string? segmentContainer, [FromQuery] int? segmentLength, [FromQuery] int? minSegments, [FromQuery] string? mediaSourceId, [FromQuery] string? deviceId, - [FromQuery] string? audioCodec, + [FromQuery] [RegularExpression(EncodingHelper.ValidationRegex)] string? audioCodec, [FromQuery] bool? enableAutoStreamCopy, [FromQuery] bool? allowVideoStreamCopy, [FromQuery] bool? allowAudioStreamCopy, @@ -204,8 +204,8 @@ public class DynamicHlsController : BaseJellyfinApiController [FromQuery] int? cpuCoreLimit, [FromQuery] string? liveStreamId, [FromQuery] bool? enableMpegtsM2TsMode, - [FromQuery] string? videoCodec, - [FromQuery] string? subtitleCodec, + [FromQuery] [RegularExpression(EncodingHelper.ValidationRegex)] string? videoCodec, + [FromQuery] [RegularExpression(EncodingHelper.ValidationRegex)] string? subtitleCodec, [FromQuery] string? transcodeReasons, [FromQuery] int? audioStreamIndex, [FromQuery] int? videoStreamIndex, @@ -406,12 +406,12 @@ public class DynamicHlsController : BaseJellyfinApiController [FromQuery] string? tag, [FromQuery, ParameterObsolete] string? deviceProfileId, [FromQuery] string? playSessionId, - [FromQuery] string? segmentContainer, + [FromQuery] [RegularExpression(EncodingHelper.ValidationRegex)] string? segmentContainer, [FromQuery] int? segmentLength, [FromQuery] int? minSegments, [FromQuery, Required] string mediaSourceId, [FromQuery] string? deviceId, - [FromQuery] string? audioCodec, + [FromQuery] [RegularExpression(EncodingHelper.ValidationRegex)] string? audioCodec, [FromQuery] bool? enableAutoStreamCopy, [FromQuery] bool? allowVideoStreamCopy, [FromQuery] bool? allowAudioStreamCopy, @@ -443,8 +443,8 @@ public class DynamicHlsController : BaseJellyfinApiController [FromQuery] int? cpuCoreLimit, [FromQuery] string? liveStreamId, [FromQuery] bool? enableMpegtsM2TsMode, - [FromQuery] string? videoCodec, - [FromQuery] string? subtitleCodec, + [FromQuery] [RegularExpression(EncodingHelper.ValidationRegex)] string? videoCodec, + [FromQuery] [RegularExpression(EncodingHelper.ValidationRegex)] string? subtitleCodec, [FromQuery] string? transcodeReasons, [FromQuery] int? audioStreamIndex, [FromQuery] int? videoStreamIndex, @@ -577,12 +577,12 @@ public class DynamicHlsController : BaseJellyfinApiController [FromQuery] string? tag, [FromQuery, ParameterObsolete] string? deviceProfileId, [FromQuery] string? playSessionId, - [FromQuery] string? segmentContainer, + [FromQuery] [RegularExpression(EncodingHelper.ValidationRegex)] string? segmentContainer, [FromQuery] int? segmentLength, [FromQuery] int? minSegments, [FromQuery, Required] string mediaSourceId, [FromQuery] string? deviceId, - [FromQuery] string? audioCodec, + [FromQuery] [RegularExpression(EncodingHelper.ValidationRegex)] string? audioCodec, [FromQuery] bool? enableAutoStreamCopy, [FromQuery] bool? allowVideoStreamCopy, [FromQuery] bool? allowAudioStreamCopy, @@ -613,8 +613,8 @@ public class DynamicHlsController : BaseJellyfinApiController [FromQuery] int? cpuCoreLimit, [FromQuery] string? liveStreamId, [FromQuery] bool? enableMpegtsM2TsMode, - [FromQuery] string? videoCodec, - [FromQuery] string? subtitleCodec, + [FromQuery] [RegularExpression(EncodingHelper.ValidationRegex)] string? videoCodec, + [FromQuery] [RegularExpression(EncodingHelper.ValidationRegex)] string? subtitleCodec, [FromQuery] string? transcodeReasons, [FromQuery] int? audioStreamIndex, [FromQuery] int? videoStreamIndex, @@ -742,12 +742,12 @@ public class DynamicHlsController : BaseJellyfinApiController [FromQuery] string? tag, [FromQuery, ParameterObsolete] string? deviceProfileId, [FromQuery] string? playSessionId, - [FromQuery] string? segmentContainer, + [FromQuery] [RegularExpression(EncodingHelper.ValidationRegex)] string? segmentContainer, [FromQuery] int? segmentLength, [FromQuery] int? minSegments, [FromQuery] string? mediaSourceId, [FromQuery] string? deviceId, - [FromQuery] string? audioCodec, + [FromQuery] [RegularExpression(EncodingHelper.ValidationRegex)] string? audioCodec, [FromQuery] bool? enableAutoStreamCopy, [FromQuery] bool? allowVideoStreamCopy, [FromQuery] bool? allowAudioStreamCopy, @@ -779,8 +779,8 @@ public class DynamicHlsController : BaseJellyfinApiController [FromQuery] int? cpuCoreLimit, [FromQuery] string? liveStreamId, [FromQuery] bool? enableMpegtsM2TsMode, - [FromQuery] string? videoCodec, - [FromQuery] string? subtitleCodec, + [FromQuery] [RegularExpression(EncodingHelper.ValidationRegex)] string? videoCodec, + [FromQuery] [RegularExpression(EncodingHelper.ValidationRegex)] string? subtitleCodec, [FromQuery] string? transcodeReasons, [FromQuery] int? audioStreamIndex, [FromQuery] int? videoStreamIndex, @@ -909,12 +909,12 @@ public class DynamicHlsController : BaseJellyfinApiController [FromQuery] string? tag, [FromQuery, ParameterObsolete] string? deviceProfileId, [FromQuery] string? playSessionId, - [FromQuery] string? segmentContainer, + [FromQuery] [RegularExpression(EncodingHelper.ValidationRegex)] string? segmentContainer, [FromQuery] int? segmentLength, [FromQuery] int? minSegments, [FromQuery] string? mediaSourceId, [FromQuery] string? deviceId, - [FromQuery] string? audioCodec, + [FromQuery] [RegularExpression(EncodingHelper.ValidationRegex)] string? audioCodec, [FromQuery] bool? enableAutoStreamCopy, [FromQuery] bool? allowVideoStreamCopy, [FromQuery] bool? allowAudioStreamCopy, @@ -945,8 +945,8 @@ public class DynamicHlsController : BaseJellyfinApiController [FromQuery] int? cpuCoreLimit, [FromQuery] string? liveStreamId, [FromQuery] bool? enableMpegtsM2TsMode, - [FromQuery] string? videoCodec, - [FromQuery] string? subtitleCodec, + [FromQuery] [RegularExpression(EncodingHelper.ValidationRegex)] string? videoCodec, + [FromQuery] [RegularExpression(EncodingHelper.ValidationRegex)] string? subtitleCodec, [FromQuery] string? transcodeReasons, [FromQuery] int? audioStreamIndex, [FromQuery] int? videoStreamIndex, @@ -1085,12 +1085,12 @@ public class DynamicHlsController : BaseJellyfinApiController [FromQuery] string? tag, [FromQuery, ParameterObsolete] string? deviceProfileId, [FromQuery] string? playSessionId, - [FromQuery] string? segmentContainer, + [FromQuery] [RegularExpression(EncodingHelper.ValidationRegex)] string? segmentContainer, [FromQuery] int? segmentLength, [FromQuery] int? minSegments, [FromQuery] string? mediaSourceId, [FromQuery] string? deviceId, - [FromQuery] string? audioCodec, + [FromQuery] [RegularExpression(EncodingHelper.ValidationRegex)] string? audioCodec, [FromQuery] bool? enableAutoStreamCopy, [FromQuery] bool? allowVideoStreamCopy, [FromQuery] bool? allowAudioStreamCopy, @@ -1122,8 +1122,8 @@ public class DynamicHlsController : BaseJellyfinApiController [FromQuery] int? cpuCoreLimit, [FromQuery] string? liveStreamId, [FromQuery] bool? enableMpegtsM2TsMode, - [FromQuery] string? videoCodec, - [FromQuery] string? subtitleCodec, + [FromQuery] [RegularExpression(EncodingHelper.ValidationRegex)] string? videoCodec, + [FromQuery] [RegularExpression(EncodingHelper.ValidationRegex)] string? subtitleCodec, [FromQuery] string? transcodeReasons, [FromQuery] int? audioStreamIndex, [FromQuery] int? videoStreamIndex, @@ -1265,12 +1265,12 @@ public class DynamicHlsController : BaseJellyfinApiController [FromQuery] string? tag, [FromQuery, ParameterObsolete] string? deviceProfileId, [FromQuery] string? playSessionId, - [FromQuery] string? segmentContainer, + [FromQuery] [RegularExpression(EncodingHelper.ValidationRegex)] string? segmentContainer, [FromQuery] int? segmentLength, [FromQuery] int? minSegments, [FromQuery] string? mediaSourceId, [FromQuery] string? deviceId, - [FromQuery] string? audioCodec, + [FromQuery] [RegularExpression(EncodingHelper.ValidationRegex)] string? audioCodec, [FromQuery] bool? enableAutoStreamCopy, [FromQuery] bool? allowVideoStreamCopy, [FromQuery] bool? allowAudioStreamCopy, @@ -1301,8 +1301,8 @@ public class DynamicHlsController : BaseJellyfinApiController [FromQuery] int? cpuCoreLimit, [FromQuery] string? liveStreamId, [FromQuery] bool? enableMpegtsM2TsMode, - [FromQuery] string? videoCodec, - [FromQuery] string? subtitleCodec, + [FromQuery] [RegularExpression(EncodingHelper.ValidationRegex)] string? videoCodec, + [FromQuery] [RegularExpression(EncodingHelper.ValidationRegex)] string? subtitleCodec, [FromQuery] string? transcodeReasons, [FromQuery] int? audioStreamIndex, [FromQuery] int? videoStreamIndex, diff --git a/Jellyfin.Api/Controllers/UniversalAudioController.cs b/Jellyfin.Api/Controllers/UniversalAudioController.cs index 634fca2eb1..db78e99464 100644 --- a/Jellyfin.Api/Controllers/UniversalAudioController.cs +++ b/Jellyfin.Api/Controllers/UniversalAudioController.cs @@ -92,13 +92,13 @@ public class UniversalAudioController : BaseJellyfinApiController [FromQuery] string? mediaSourceId, [FromQuery] string? deviceId, [FromQuery] Guid? userId, - [FromQuery] string? audioCodec, + [FromQuery] [RegularExpression(EncodingHelper.ValidationRegex)] string? audioCodec, [FromQuery] int? maxAudioChannels, [FromQuery] int? transcodingAudioChannels, [FromQuery] int? maxStreamingBitrate, [FromQuery] int? audioBitRate, [FromQuery] long? startTimeTicks, - [FromQuery] string? transcodingContainer, + [FromQuery] [RegularExpression(EncodingHelper.ValidationRegex)] string? transcodingContainer, [FromQuery] MediaStreamProtocol? transcodingProtocol, [FromQuery] int? maxAudioSampleRate, [FromQuery] int? maxAudioBitDepth, diff --git a/Jellyfin.Api/Controllers/VideosController.cs b/Jellyfin.Api/Controllers/VideosController.cs index b3029d6fa8..3801200320 100644 --- a/Jellyfin.Api/Controllers/VideosController.cs +++ b/Jellyfin.Api/Controllers/VideosController.cs @@ -311,18 +311,18 @@ public class VideosController : BaseJellyfinApiController [ProducesVideoFile] public async Task GetVideoStream( [FromRoute, Required] Guid itemId, - [FromQuery] string? container, + [FromQuery] [RegularExpression(EncodingHelper.ValidationRegex)] string? container, [FromQuery] bool? @static, [FromQuery] string? @params, [FromQuery] string? tag, [FromQuery, ParameterObsolete] string? deviceProfileId, [FromQuery] string? playSessionId, - [FromQuery] string? segmentContainer, + [FromQuery] [RegularExpression(EncodingHelper.ValidationRegex)] string? segmentContainer, [FromQuery] int? segmentLength, [FromQuery] int? minSegments, [FromQuery] string? mediaSourceId, [FromQuery] string? deviceId, - [FromQuery] string? audioCodec, + [FromQuery] [RegularExpression(EncodingHelper.ValidationRegex)] string? audioCodec, [FromQuery] bool? enableAutoStreamCopy, [FromQuery] bool? allowVideoStreamCopy, [FromQuery] bool? allowAudioStreamCopy, @@ -354,8 +354,8 @@ public class VideosController : BaseJellyfinApiController [FromQuery] int? cpuCoreLimit, [FromQuery] string? liveStreamId, [FromQuery] bool? enableMpegtsM2TsMode, - [FromQuery] string? videoCodec, - [FromQuery] string? subtitleCodec, + [FromQuery] [RegularExpression(EncodingHelper.ValidationRegex)] string? videoCodec, + [FromQuery] [RegularExpression(EncodingHelper.ValidationRegex)] string? subtitleCodec, [FromQuery] string? transcodeReasons, [FromQuery] int? audioStreamIndex, [FromQuery] int? videoStreamIndex, @@ -555,12 +555,12 @@ public class VideosController : BaseJellyfinApiController [FromQuery] string? tag, [FromQuery] string? deviceProfileId, [FromQuery] string? playSessionId, - [FromQuery] string? segmentContainer, + [FromQuery] [RegularExpression(EncodingHelper.ValidationRegex)] string? segmentContainer, [FromQuery] int? segmentLength, [FromQuery] int? minSegments, [FromQuery] string? mediaSourceId, [FromQuery] string? deviceId, - [FromQuery] string? audioCodec, + [FromQuery] [RegularExpression(EncodingHelper.ValidationRegex)] string? audioCodec, [FromQuery] bool? enableAutoStreamCopy, [FromQuery] bool? allowVideoStreamCopy, [FromQuery] bool? allowAudioStreamCopy, @@ -592,8 +592,8 @@ public class VideosController : BaseJellyfinApiController [FromQuery] int? cpuCoreLimit, [FromQuery] string? liveStreamId, [FromQuery] bool? enableMpegtsM2TsMode, - [FromQuery] string? videoCodec, - [FromQuery] string? subtitleCodec, + [FromQuery] [RegularExpression(EncodingHelper.ValidationRegex)] string? videoCodec, + [FromQuery] [RegularExpression(EncodingHelper.ValidationRegex)] string? subtitleCodec, [FromQuery] string? transcodeReasons, [FromQuery] int? audioStreamIndex, [FromQuery] int? videoStreamIndex, diff --git a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs index 178a9999c8..9a0e4a62cb 100644 --- a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs +++ b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs @@ -1,6 +1,8 @@ #nullable disable #pragma warning disable CS1591 +// We need lowercase normalized string for ffmpeg +#pragma warning disable CA1308 using System; using System.Collections.Generic; @@ -26,6 +28,14 @@ namespace MediaBrowser.Controller.MediaEncoding { public partial class EncodingHelper { + /// + /// The codec validation regex. + /// This regular expression matches strings that consist of alphanumeric characters, hyphens, + /// periods, underscores, commas, and vertical bars, with a length between 0 and 40 characters. + /// This should matches all common valid codecs. + /// + public const string ValidationRegex = @"^[a-zA-Z0-9\-\._,|]{0,40}$"; + private const string QsvAlias = "qs"; private const string VaapiAlias = "va"; private const string D3d11vaAlias = "dx11"; @@ -53,6 +63,8 @@ namespace MediaBrowser.Controller.MediaEncoding private readonly Version _minFFmpegVaapiH26xEncA53CcSei = new Version(6, 0); private readonly Version _minFFmpegReadrateOption = new Version(5, 0); + private static readonly Regex _validationRegex = new(ValidationRegex, RegexOptions.Compiled); + private static readonly string[] _videoProfilesH264 = new[] { "ConstrainedBaseline", @@ -391,7 +403,10 @@ namespace MediaBrowser.Controller.MediaEncoding return "libtheora"; } - return codec.ToLowerInvariant(); + if (_validationRegex.IsMatch(codec)) + { + return codec.ToLowerInvariant(); + } } return "copy"; @@ -429,7 +444,7 @@ namespace MediaBrowser.Controller.MediaEncoding public static string GetInputFormat(string container) { - if (string.IsNullOrEmpty(container)) + if (string.IsNullOrEmpty(container) || !_validationRegex.IsMatch(container)) { return null; } @@ -685,6 +700,11 @@ namespace MediaBrowser.Controller.MediaEncoding { var codec = state.OutputAudioCodec; + if (!_validationRegex.IsMatch(codec)) + { + codec = "aac"; + } + if (string.Equals(codec, "aac", StringComparison.OrdinalIgnoreCase)) { // Use Apple's aac encoder if available as it provides best audio quality