From 93902fc610a9d8b52780d88f7bb986e668567c9d Mon Sep 17 00:00:00 2001 From: john janzen Date: Sat, 20 Dec 2025 19:42:51 +0100 Subject: fix crashes on devices that don't support ipv6 --- src/Jellyfin.Networking/Manager/NetworkManager.cs | 3 +++ 1 file changed, 3 insertions(+) (limited to 'src') diff --git a/src/Jellyfin.Networking/Manager/NetworkManager.cs b/src/Jellyfin.Networking/Manager/NetworkManager.cs index 126d9f15cf..e82e854417 100644 --- a/src/Jellyfin.Networking/Manager/NetworkManager.cs +++ b/src/Jellyfin.Networking/Manager/NetworkManager.cs @@ -779,6 +779,9 @@ public class NetworkManager : INetworkManager, IDisposable return knownInterfaces; } + // TODO: remove when upgrade to dotnet 11 is done + readIpv6 &= Socket.OSSupportsIPv6; + // No bind address and no exclusions, so listen on all interfaces. var result = new List(); if (readIpv4 && readIpv6) -- cgit v1.2.3 From 146681f0ba927b6c2d1e392a2b157a28c36e1a6b Mon Sep 17 00:00:00 2001 From: john janzen Date: Sun, 21 Dec 2025 15:37:22 +0100 Subject: Warn server administrator when IPv6 is enabled but unsupported by OS --- Jellyfin.Server/ServerSetupApp/SetupServer.cs | 2 +- src/Jellyfin.Networking/Manager/NetworkManager.cs | 10 ++++++++-- 2 files changed, 9 insertions(+), 3 deletions(-) (limited to 'src') diff --git a/Jellyfin.Server/ServerSetupApp/SetupServer.cs b/Jellyfin.Server/ServerSetupApp/SetupServer.cs index 4340969a30..1aa39f97b6 100644 --- a/Jellyfin.Server/ServerSetupApp/SetupServer.cs +++ b/Jellyfin.Server/ServerSetupApp/SetupServer.cs @@ -162,7 +162,7 @@ public sealed class SetupServer : IDisposable { var knownBindInterfaces = NetworkManager.GetInterfacesCore(_loggerFactory.CreateLogger(), config.EnableIPv4, config.EnableIPv6); knownBindInterfaces = NetworkManager.FilterBindSettings(config, knownBindInterfaces.ToList(), config.EnableIPv4, config.EnableIPv6); - var bindInterfaces = NetworkManager.GetAllBindInterfaces(false, _configurationManager, knownBindInterfaces, config.EnableIPv4, config.EnableIPv6); + var bindInterfaces = NetworkManager.GetAllBindInterfaces(_loggerFactory.CreateLogger(), false, _configurationManager, knownBindInterfaces, config.EnableIPv4, config.EnableIPv6); Extensions.WebHostBuilderExtensions.SetupJellyfinWebServer( bindInterfaces, config.InternalHttpPort, diff --git a/src/Jellyfin.Networking/Manager/NetworkManager.cs b/src/Jellyfin.Networking/Manager/NetworkManager.cs index e82e854417..88f16d8c50 100644 --- a/src/Jellyfin.Networking/Manager/NetworkManager.cs +++ b/src/Jellyfin.Networking/Manager/NetworkManager.cs @@ -753,12 +753,13 @@ public class NetworkManager : INetworkManager, IDisposable /// public IReadOnlyList GetAllBindInterfaces(bool individualInterfaces = false) { - return NetworkManager.GetAllBindInterfaces(individualInterfaces, _configurationManager, _interfaces, IsIPv4Enabled, IsIPv6Enabled); + return NetworkManager.GetAllBindInterfaces(_logger, individualInterfaces, _configurationManager, _interfaces, IsIPv4Enabled, IsIPv6Enabled); } /// /// Reads the jellyfin configuration of the configuration manager and produces a list of interfaces that should be bound. /// + /// Logger to use for messages. /// Defines that only known interfaces should be used. /// The ConfigurationManager. /// The known interfaces that gets returned if possible or instructed. @@ -766,6 +767,7 @@ public class NetworkManager : INetworkManager, IDisposable /// Include IPV6 type interfaces. /// A list of ip address of which jellyfin should bind to. public static IReadOnlyList GetAllBindInterfaces( + ILogger logger, bool individualInterfaces, IConfigurationManager configurationManager, IReadOnlyList knownInterfaces, @@ -780,7 +782,11 @@ public class NetworkManager : INetworkManager, IDisposable } // TODO: remove when upgrade to dotnet 11 is done - readIpv6 &= Socket.OSSupportsIPv6; + if (readIpv6 && !Socket.OSSupportsIPv6) + { + logger.LogWarning("IPv6 Unsupported by OS, not listening on IPv6"); + readIpv6 = false; + } // No bind address and no exclusions, so listen on all interfaces. var result = new List(); -- cgit v1.2.3 From 72b4faa00b743dc5bbd2e25c54b216510e978a5a Mon Sep 17 00:00:00 2001 From: ZeusCraft10 Date: Tue, 30 Dec 2025 17:31:40 -0500 Subject: Fix UDP Auto-Discovery returning IPv6 for cross-subnet IPv4 requests Fixes #15898 When a UDP discovery request is relayed from a different IPv4 subnet, GetBindAddress() now correctly returns an IPv4 address instead of incorrectly falling back to ::1. Changes: - Loopback fallback now prefers address family matching the source IP - Interface fallback now prefers interfaces matching source address family - Added test case for cross-subnet IPv4 request scenario --- CONTRIBUTORS.md | 1 + src/Jellyfin.Networking/Manager/NetworkManager.cs | 29 +++++++++++++++++++--- .../Jellyfin.Networking.Tests/NetworkParseTests.cs | 2 ++ 3 files changed, 29 insertions(+), 3 deletions(-) (limited to 'src') diff --git a/CONTRIBUTORS.md b/CONTRIBUTORS.md index 0fd509f842..9b716a3862 100644 --- a/CONTRIBUTORS.md +++ b/CONTRIBUTORS.md @@ -164,6 +164,7 @@ - [XVicarious](https://github.com/XVicarious) - [YouKnowBlom](https://github.com/YouKnowBlom) - [ZachPhelan](https://github.com/ZachPhelan) + - [ZeusCraft10](https://github.com/ZeusCraft10) - [KristupasSavickas](https://github.com/KristupasSavickas) - [Pusta](https://github.com/pusta) - [nielsvanvelzen](https://github.com/nielsvanvelzen) diff --git a/src/Jellyfin.Networking/Manager/NetworkManager.cs b/src/Jellyfin.Networking/Manager/NetworkManager.cs index 126d9f15cf..10986b358f 100644 --- a/src/Jellyfin.Networking/Manager/NetworkManager.cs +++ b/src/Jellyfin.Networking/Manager/NetworkManager.cs @@ -875,7 +875,20 @@ public class NetworkManager : INetworkManager, IDisposable if (availableInterfaces.Count == 0) { // There isn't any others, so we'll use the loopback. - result = IsIPv4Enabled && !IsIPv6Enabled ? "127.0.0.1" : "::1"; + // Prefer loopback address matching the source's address family + if (source is not null && source.AddressFamily == AddressFamily.InterNetwork && IsIPv4Enabled) + { + result = "127.0.0.1"; + } + else if (source is not null && source.AddressFamily == AddressFamily.InterNetworkV6 && IsIPv6Enabled) + { + result = "::1"; + } + else + { + result = IsIPv4Enabled ? "127.0.0.1" : "::1"; + } + _logger.LogWarning("{Source}: Only loopback {Result} returned, using that as bind address.", source, result); return result; } @@ -900,9 +913,19 @@ public class NetworkManager : INetworkManager, IDisposable } } - // Fallback to first available interface + // Fallback to an interface matching the source's address family, or first available + var preferredInterface = availableInterfaces + .FirstOrDefault(x => x.Address.AddressFamily == source.AddressFamily); + + if (preferredInterface is not null) + { + result = NetworkUtils.FormatIPString(preferredInterface.Address); + _logger.LogDebug("{Source}: No matching subnet found, using interface with matching address family: {Result}", source, result); + return result; + } + result = NetworkUtils.FormatIPString(availableInterfaces[0].Address); - _logger.LogDebug("{Source}: No matching interfaces found, using preferred interface as bind address: {Result}", source, result); + _logger.LogDebug("{Source}: No matching interfaces found, using first available interface as bind address: {Result}", source, result); return result; } diff --git a/tests/Jellyfin.Networking.Tests/NetworkParseTests.cs b/tests/Jellyfin.Networking.Tests/NetworkParseTests.cs index 38208476f8..d8748aadac 100644 --- a/tests/Jellyfin.Networking.Tests/NetworkParseTests.cs +++ b/tests/Jellyfin.Networking.Tests/NetworkParseTests.cs @@ -377,6 +377,8 @@ namespace Jellyfin.Networking.Tests [InlineData("192.168.1.208/24,-16,eth16|10.0.0.1/24,10,eth7", "192.168.1.0/24", "10.0.0.1", "192.168.1.209", "10.0.0.1")] // LAN not bound, so return external. [InlineData("192.168.1.208/24,-16,eth16|10.0.0.1/24,10,eth7", "192.168.1.0/24", "192.168.1.208,10.0.0.1", "8.8.8.8", "10.0.0.1")] // return external bind address [InlineData("192.168.1.208/24,-16,eth16|10.0.0.1/24,10,eth7", "192.168.1.0/24", "192.168.1.208,10.0.0.1", "192.168.1.210", "192.168.1.208")] // return LAN bind address + // Cross-subnet IPv4 request should return IPv4, not IPv6 (Issue #15898) + [InlineData("192.168.1.208/24,-16,eth16|fd00::1/64,10,eth7", "192.168.1.0/24", "", "192.168.2.100", "192.168.1.208")] public void GetBindInterface_ValidSourceGiven_Success(string interfaces, string lan, string bind, string source, string result) { var conf = new NetworkConfiguration -- cgit v1.2.3 From b82a2ced75484c4805e927178bac0702d2713f71 Mon Sep 17 00:00:00 2001 From: nyanmisaka Date: Mon, 23 Mar 2026 17:06:22 -0400 Subject: Backport pull request #16423 from jellyfin/release-10.11.z Fix readrate options in FFmpeg 8.1 Original-merge: 29b236185701091f6719862b05bd7bda58d88475 Merged-by: Bond-009 Backported-by: Bond_009 --- MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs | 13 ++++++++++++- src/Jellyfin.LiveTv/IO/EncodedRecorder.cs | 7 +++++++ 2 files changed, 19 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs index c7b11f47d1..21d4c36f68 100644 --- a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs +++ b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs @@ -85,6 +85,7 @@ namespace MediaBrowser.Controller.MediaEncoding private readonly Version _minFFmpegVaapiDeviceVendorId = new Version(7, 0, 1); private readonly Version _minFFmpegQsvVppScaleModeOption = new Version(6, 0); private readonly Version _minFFmpegRkmppHevcDecDoviRpu = new Version(7, 1, 1); + private readonly Version _minFFmpegReadrateCatchupOption = new Version(8, 0); private static readonly Regex _containerValidationRegex = new(ContainerValidationRegex, RegexOptions.Compiled); @@ -7226,8 +7227,10 @@ namespace MediaBrowser.Controller.MediaEncoding inputModifier += GetVideoSyncOption(state.InputVideoSync, _mediaEncoder.EncoderVersion); } + int readrate = 0; if (state.ReadInputAtNativeFramerate && state.InputProtocol != MediaProtocol.Rtsp) { + readrate = 1; inputModifier += " -re"; } else if (encodingOptions.EnableSegmentDeletion @@ -7238,7 +7241,15 @@ namespace MediaBrowser.Controller.MediaEncoding { // Set an input read rate limit 10x for using SegmentDeletion with stream-copy // to prevent ffmpeg from exiting prematurely (due to fast drive) - inputModifier += " -readrate 10"; + readrate = 10; + inputModifier += $" -readrate {readrate}"; + } + + // Set a larger catchup value to revert to the old behavior, + // otherwise, remuxing might stall due to this new option + if (readrate > 0 && _mediaEncoder.EncoderVersion >= _minFFmpegReadrateCatchupOption) + { + inputModifier += $" -readrate_catchup {readrate * 100}"; } var flags = new List(); diff --git a/src/Jellyfin.LiveTv/IO/EncodedRecorder.cs b/src/Jellyfin.LiveTv/IO/EncodedRecorder.cs index be7ff52977..d877a0d124 100644 --- a/src/Jellyfin.LiveTv/IO/EncodedRecorder.cs +++ b/src/Jellyfin.LiveTv/IO/EncodedRecorder.cs @@ -156,6 +156,13 @@ namespace Jellyfin.LiveTv.IO if (mediaSource.ReadAtNativeFramerate) { inputModifier += " -re"; + + // Set a larger catchup value to revert to the old behavior, + // otherwise, remuxing might stall due to this new option + if (_mediaEncoder.EncoderVersion >= new Version(8, 0)) + { + inputModifier += " -readrate_catchup 100"; + } } if (mediaSource.RequiresLooping) -- cgit v1.2.3 From a3960b30c04d5313b0bfb3d57a7da35af5e8af3b Mon Sep 17 00:00:00 2001 From: Bond-009 Date: Sun, 29 Mar 2026 14:28:41 -0400 Subject: Backport pull request #16369 from jellyfin/release-10.11.z Fix nullref ex in font handling Original-merge: 41c2d51d8cb9b4f9bdf81be6e73f7ae2d447a8d7 Merged-by: Bond-009 Backported-by: Bond_009 --- src/Jellyfin.Drawing.Skia/SkiaEncoder.cs | 57 +++++++++++++++++++++----------- 1 file changed, 37 insertions(+), 20 deletions(-) (limited to 'src') diff --git a/src/Jellyfin.Drawing.Skia/SkiaEncoder.cs b/src/Jellyfin.Drawing.Skia/SkiaEncoder.cs index c6eab92ead..ade993d927 100644 --- a/src/Jellyfin.Drawing.Skia/SkiaEncoder.cs +++ b/src/Jellyfin.Drawing.Skia/SkiaEncoder.cs @@ -25,7 +25,7 @@ public class SkiaEncoder : IImageEncoder private readonly ILogger _logger; private readonly IApplicationPaths _appPaths; private static readonly SKImageFilter _imageFilter; - private static readonly SKTypeface[] _typefaces; + private static readonly SKTypeface?[] _typefaces = InitializeTypefaces(); /// /// The default sampling options, equivalent to old high quality filter settings when upscaling. @@ -37,9 +37,7 @@ public class SkiaEncoder : IImageEncoder /// public static readonly SKSamplingOptions DefaultSamplingOptions; -#pragma warning disable CA1810 static SkiaEncoder() -#pragma warning restore CA1810 { var kernel = new[] { @@ -59,21 +57,6 @@ public class SkiaEncoder : IImageEncoder SKShaderTileMode.Clamp, true); - // Initialize the list of typefaces - // We have to statically build a list of typefaces because MatchCharacter only accepts a single character or code point - // But in reality a human-readable character (grapheme cluster) could be multiple code points. For example, πŸš΅πŸ»β€β™€οΈ is a single emoji but 5 code points (U+1F6B5 + U+1F3FB + U+200D + U+2640 + U+FE0F) - _typefaces = - [ - SKFontManager.Default.MatchCharacter(null, SKFontStyleWeight.Bold, SKFontStyleWidth.Normal, SKFontStyleSlant.Upright, null, 'ιΈ‘'), // CJK Simplified Chinese - SKFontManager.Default.MatchCharacter(null, SKFontStyleWeight.Bold, SKFontStyleWidth.Normal, SKFontStyleSlant.Upright, null, 'ι›ž'), // CJK Traditional Chinese - SKFontManager.Default.MatchCharacter(null, SKFontStyleWeight.Bold, SKFontStyleWidth.Normal, SKFontStyleSlant.Upright, null, 'γƒŽ'), // CJK Japanese - SKFontManager.Default.MatchCharacter(null, SKFontStyleWeight.Bold, SKFontStyleWidth.Normal, SKFontStyleSlant.Upright, null, '각'), // CJK Korean - SKFontManager.Default.MatchCharacter(null, SKFontStyleWeight.Bold, SKFontStyleWidth.Normal, SKFontStyleSlant.Upright, null, 128169), // Emojis, 128169 is the πŸ’©emoji - SKFontManager.Default.MatchCharacter(null, SKFontStyleWeight.Bold, SKFontStyleWidth.Normal, SKFontStyleSlant.Upright, null, 'Χ–'), // Hebrew - SKFontManager.Default.MatchCharacter(null, SKFontStyleWeight.Bold, SKFontStyleWidth.Normal, SKFontStyleSlant.Upright, null, 'ي'), // Arabic - SKTypeface.FromFamilyName("sans-serif", SKFontStyleWeight.Bold, SKFontStyleWidth.Normal, SKFontStyleSlant.Upright) // Default font - ]; - // use cubic for upscaling UpscaleSamplingOptions = new SKSamplingOptions(SKCubicResampler.Mitchell); // use bilinear for everything else @@ -132,7 +115,7 @@ public class SkiaEncoder : IImageEncoder /// /// Gets the default typeface to use. /// - public static SKTypeface DefaultTypeFace => _typefaces.Last(); + public static SKTypeface? DefaultTypeFace => _typefaces.Last(); /// /// Check if the native lib is available. @@ -152,6 +135,40 @@ public class SkiaEncoder : IImageEncoder } } + /// + /// Initialize the list of typefaces + /// We have to statically build a list of typefaces because MatchCharacter only accepts a single character or code point + /// But in reality a human-readable character (grapheme cluster) could be multiple code points. For example, πŸš΅πŸ»β€β™€οΈ is a single emoji but 5 code points (U+1F6B5 + U+1F3FB + U+200D + U+2640 + U+FE0F) + /// + /// The list of typefaces. + private static SKTypeface?[] InitializeTypefaces() + { + int[] chars = [ + 'ιΈ‘', // CJK Simplified Chinese + 'ι›ž', // CJK Traditional Chinese + 'γƒŽ', // CJK Japanese + '각', // CJK Korean + 128169, // Emojis, 128169 is the Pile of Poo (πŸ’©) emoji + 'Χ–', // Hebrew + 'ي' // Arabic + ]; + var fonts = new List(chars.Length + 1); + foreach (var ch in chars) + { + var font = SKFontManager.Default.MatchCharacter(null, SKFontStyleWeight.Bold, SKFontStyleWidth.Normal, SKFontStyleSlant.Upright, null, ch); + if (font is not null) + { + fonts.Add(font); + } + } + + // Default font + fonts.Add(SKTypeface.FromFamilyName("sans-serif", SKFontStyleWeight.Bold, SKFontStyleWidth.Normal, SKFontStyleSlant.Upright) + ?? SKFontManager.Default.MatchCharacter(null, SKFontStyleWeight.Bold, SKFontStyleWidth.Normal, SKFontStyleSlant.Upright, null, 'a')); + + return fonts.ToArray(); + } + /// /// Convert a to a . /// @@ -809,7 +826,7 @@ public class SkiaEncoder : IImageEncoder { foreach (var typeface in _typefaces) { - if (typeface.ContainsGlyphs(c)) + if (typeface is not null && typeface.ContainsGlyphs(c)) { return typeface; } -- cgit v1.2.3 From 2134ea3f7fd12ef9bd7b5881cedb84484c6f56d2 Mon Sep 17 00:00:00 2001 From: theguymadmax <171496228+theguymadmax@users.noreply.github.com> Date: Mon, 30 Mar 2026 14:08:06 -0400 Subject: Backport pull request #16514 from jellyfin/release-10.11.z Fix lint issue Original-merge: e1691e649e8431d080e1d1bc0aacbc2e6198f371 Merged-by: joshuaboniface Backported-by: Bond_009 --- src/Jellyfin.Drawing.Skia/SkiaEncoder.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/Jellyfin.Drawing.Skia/SkiaEncoder.cs b/src/Jellyfin.Drawing.Skia/SkiaEncoder.cs index ade993d927..babab57d52 100644 --- a/src/Jellyfin.Drawing.Skia/SkiaEncoder.cs +++ b/src/Jellyfin.Drawing.Skia/SkiaEncoder.cs @@ -138,7 +138,7 @@ public class SkiaEncoder : IImageEncoder /// /// Initialize the list of typefaces /// We have to statically build a list of typefaces because MatchCharacter only accepts a single character or code point - /// But in reality a human-readable character (grapheme cluster) could be multiple code points. For example, πŸš΅πŸ»β€β™€οΈ is a single emoji but 5 code points (U+1F6B5 + U+1F3FB + U+200D + U+2640 + U+FE0F) + /// But in reality a human-readable character (grapheme cluster) could be multiple code points. For example, πŸš΅πŸ»β€β™€οΈ is a single emoji but 5 code points (U+1F6B5 + U+1F3FB + U+200D + U+2640 + U+FE0F). /// /// The list of typefaces. private static SKTypeface?[] InitializeTypefaces() -- cgit v1.2.3 From d5d4309417ff6d39015db57c729cf55111df8ac0 Mon Sep 17 00:00:00 2001 From: Bond-009 Date: Mon, 30 Mar 2026 14:08:09 -0400 Subject: Backport pull request #16522 from jellyfin/release-10.11.z Fix CA1810 build error Original-merge: 7e88b18192762dcbf82b2182bacd486b4d828e04 Merged-by: nielsvanvelzen Backported-by: Bond_009 --- src/Jellyfin.Drawing.Skia/SkiaEncoder.cs | 43 +++++++++++--------------------- 1 file changed, 14 insertions(+), 29 deletions(-) (limited to 'src') diff --git a/src/Jellyfin.Drawing.Skia/SkiaEncoder.cs b/src/Jellyfin.Drawing.Skia/SkiaEncoder.cs index babab57d52..3f7ae4d2cd 100644 --- a/src/Jellyfin.Drawing.Skia/SkiaEncoder.cs +++ b/src/Jellyfin.Drawing.Skia/SkiaEncoder.cs @@ -24,44 +24,29 @@ public class SkiaEncoder : IImageEncoder private static readonly HashSet _transparentImageTypes = new(StringComparer.OrdinalIgnoreCase) { ".png", ".gif", ".webp" }; private readonly ILogger _logger; private readonly IApplicationPaths _appPaths; - private static readonly SKImageFilter _imageFilter; private static readonly SKTypeface?[] _typefaces = InitializeTypefaces(); + private static readonly SKImageFilter _imageFilter = SKImageFilter.CreateMatrixConvolution( + new SKSizeI(3, 3), + [ + 0, -.1f, 0, + -.1f, 1.4f, -.1f, + 0, -.1f, 0 + ], + 1f, + 0f, + new SKPointI(1, 1), + SKShaderTileMode.Clamp, + true); /// /// The default sampling options, equivalent to old high quality filter settings when upscaling. /// - public static readonly SKSamplingOptions UpscaleSamplingOptions; + public static readonly SKSamplingOptions UpscaleSamplingOptions = new SKSamplingOptions(SKCubicResampler.Mitchell); /// /// The sampling options, used for downscaling images, equivalent to old high quality filter settings when not upscaling. /// - public static readonly SKSamplingOptions DefaultSamplingOptions; - - static SkiaEncoder() - { - var kernel = new[] - { - 0, -.1f, 0, - -.1f, 1.4f, -.1f, - 0, -.1f, 0, - }; - - var kernelSize = new SKSizeI(3, 3); - var kernelOffset = new SKPointI(1, 1); - _imageFilter = SKImageFilter.CreateMatrixConvolution( - kernelSize, - kernel, - 1f, - 0f, - kernelOffset, - SKShaderTileMode.Clamp, - true); - - // use cubic for upscaling - UpscaleSamplingOptions = new SKSamplingOptions(SKCubicResampler.Mitchell); - // use bilinear for everything else - DefaultSamplingOptions = new SKSamplingOptions(SKFilterMode.Linear, SKMipmapMode.Linear); - } + public static readonly SKSamplingOptions DefaultSamplingOptions = new SKSamplingOptions(SKFilterMode.Linear, SKMipmapMode.Linear); /// /// Initializes a new instance of the class. -- cgit v1.2.3 From 3c9b71e1241237107c260bb84b9221f532ef8105 Mon Sep 17 00:00:00 2001 From: "Joshua M. Boniface" Date: Sun, 29 Mar 2026 17:30:09 -0400 Subject: Fix GHSA-8fw7-f233-ffr8 with improved sanitization Co-Authored-By: Shadowghost --- Jellyfin.Data/UserEntityExtensions.cs | 2 +- src/Jellyfin.LiveTv/TunerHosts/M3uParser.cs | 17 +++++++++++++++++ 2 files changed, 18 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/Jellyfin.Data/UserEntityExtensions.cs b/Jellyfin.Data/UserEntityExtensions.cs index 149fc9042d..0fc8d3cd25 100644 --- a/Jellyfin.Data/UserEntityExtensions.cs +++ b/Jellyfin.Data/UserEntityExtensions.cs @@ -185,7 +185,7 @@ public static class UserEntityExtensions entity.Permissions.Add(new Permission(PermissionKind.EnableSyncTranscoding, true)); entity.Permissions.Add(new Permission(PermissionKind.EnableAudioPlaybackTranscoding, true)); entity.Permissions.Add(new Permission(PermissionKind.EnableLiveTvAccess, true)); - entity.Permissions.Add(new Permission(PermissionKind.EnableLiveTvManagement, true)); + entity.Permissions.Add(new Permission(PermissionKind.EnableLiveTvManagement, false)); entity.Permissions.Add(new Permission(PermissionKind.EnableSharedDeviceControl, true)); entity.Permissions.Add(new Permission(PermissionKind.EnableVideoPlaybackTranscoding, true)); entity.Permissions.Add(new Permission(PermissionKind.ForceRemoteSourceTranscoding, false)); diff --git a/src/Jellyfin.LiveTv/TunerHosts/M3uParser.cs b/src/Jellyfin.LiveTv/TunerHosts/M3uParser.cs index 2270758454..5da7762f6f 100644 --- a/src/Jellyfin.LiveTv/TunerHosts/M3uParser.cs +++ b/src/Jellyfin.LiveTv/TunerHosts/M3uParser.cs @@ -93,6 +93,13 @@ namespace Jellyfin.LiveTv.TunerHosts } else if (!string.IsNullOrWhiteSpace(extInf) && !trimmedLine.StartsWith('#')) { + if (!IsValidChannelUrl(trimmedLine)) + { + _logger.LogWarning("Skipping M3U channel entry with non-HTTP path: {Path}", trimmedLine); + extInf = string.Empty; + continue; + } + var channel = GetChannelInfo(extInf, tunerHostId, trimmedLine); channel.Id = channelIdPrefix + trimmedLine.GetMD5().ToString("N", CultureInfo.InvariantCulture); @@ -247,6 +254,16 @@ namespace Jellyfin.LiveTv.TunerHosts return numberString; } + private static bool IsValidChannelUrl(string url) + { + return Uri.TryCreate(url, UriKind.Absolute, out var uri) + && (string.Equals(uri.Scheme, "http", StringComparison.OrdinalIgnoreCase) + || string.Equals(uri.Scheme, "https", StringComparison.OrdinalIgnoreCase) + || string.Equals(uri.Scheme, "rtsp", StringComparison.OrdinalIgnoreCase) + || string.Equals(uri.Scheme, "rtp", StringComparison.OrdinalIgnoreCase) + || string.Equals(uri.Scheme, "udp", StringComparison.OrdinalIgnoreCase)); + } + private static bool IsValidChannelNumber(string numberString) { if (string.IsNullOrWhiteSpace(numberString) -- cgit v1.2.3