From e26f2a2ab1a181ae9aa8d0860a930640e72e2265 Mon Sep 17 00:00:00 2001 From: Duncan Tourolle Date: Sun, 16 Nov 2025 20:53:23 +0100 Subject: [PATCH] passthrough not transcode --- .../Channels/SRFPlayChannel.cs | 13 ++++++++++-- .../Providers/SRFMediaProvider.cs | 15 ++++++++++--- .../Services/StreamUrlResolver.cs | 21 ++++++++++++++++--- 3 files changed, 41 insertions(+), 8 deletions(-) diff --git a/Jellyfin.Plugin.SRFPlay/Channels/SRFPlayChannel.cs b/Jellyfin.Plugin.SRFPlay/Channels/SRFPlayChannel.cs index 3d2fd0d..c70cbb1 100644 --- a/Jellyfin.Plugin.SRFPlay/Channels/SRFPlayChannel.cs +++ b/Jellyfin.Plugin.SRFPlay/Channels/SRFPlayChannel.cs @@ -7,6 +7,7 @@ using System.Text; using System.Threading; using System.Threading.Tasks; using Jellyfin.Plugin.SRFPlay.Services; +using MediaBrowser.Controller; using MediaBrowser.Controller.Channels; using MediaBrowser.Controller.Providers; using MediaBrowser.Model.Channels; @@ -27,6 +28,7 @@ public class SRFPlayChannel : IChannel, IHasCacheKey private readonly StreamUrlResolver _streamResolver; private readonly StreamProxyService _proxyService; private readonly CategoryService? _categoryService; + private readonly IServerApplicationHost _appHost; /// /// Initializes a new instance of the class. @@ -35,12 +37,14 @@ public class SRFPlayChannel : IChannel, IHasCacheKey /// The content refresh service. /// The stream resolver. /// The stream proxy service. + /// The server application host. /// The category service (optional). public SRFPlayChannel( ILoggerFactory loggerFactory, ContentRefreshService contentRefreshService, StreamUrlResolver streamResolver, StreamProxyService proxyService, + IServerApplicationHost appHost, CategoryService? categoryService = null) { _loggerFactory = loggerFactory; @@ -48,6 +52,7 @@ public class SRFPlayChannel : IChannel, IHasCacheKey _contentRefreshService = contentRefreshService; _streamResolver = streamResolver; _proxyService = proxyService; + _appHost = appHost; _categoryService = categoryService; if (_categoryService == null) @@ -471,9 +476,13 @@ public class SRFPlayChannel : IChannel, IHasCacheKey // Register stream with proxy service _proxyService.RegisterStream(itemId, streamUrl); + // Get the server's local API URL so remote clients can access the proxy + // Use the published server address configured in Jellyfin's network settings + var serverUrl = _appHost.GetSmartApiUrl(string.Empty); + // Create proxy URL as absolute HTTP URL (required for ffmpeg) - // Use localhost as Jellyfin should be able to access its own endpoints - var proxyUrl = $"http://localhost:8096/Plugins/SRFPlay/Proxy/{itemId}/master.m3u8"; + // Use the actual server URL so remote clients can access it + var proxyUrl = $"{serverUrl}/Plugins/SRFPlay/Proxy/{itemId}/master.m3u8"; // Build overview var overview = chapter.Description ?? chapter.Lead; diff --git a/Jellyfin.Plugin.SRFPlay/Providers/SRFMediaProvider.cs b/Jellyfin.Plugin.SRFPlay/Providers/SRFMediaProvider.cs index 4bf93e2..ba498fd 100644 --- a/Jellyfin.Plugin.SRFPlay/Providers/SRFMediaProvider.cs +++ b/Jellyfin.Plugin.SRFPlay/Providers/SRFMediaProvider.cs @@ -5,6 +5,7 @@ using System.Threading; using System.Threading.Tasks; using Jellyfin.Plugin.SRFPlay.Api; using Jellyfin.Plugin.SRFPlay.Services; +using MediaBrowser.Controller; using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Library; using MediaBrowser.Model.Dto; @@ -24,6 +25,7 @@ public class SRFMediaProvider : IMediaSourceProvider private readonly MetadataCache _metadataCache; private readonly StreamUrlResolver _streamResolver; private readonly StreamProxyService _proxyService; + private readonly IServerApplicationHost _appHost; /// /// Initializes a new instance of the class. @@ -32,17 +34,20 @@ public class SRFMediaProvider : IMediaSourceProvider /// The metadata cache. /// The stream URL resolver. /// The stream proxy service. + /// The server application host. public SRFMediaProvider( ILoggerFactory loggerFactory, MetadataCache metadataCache, StreamUrlResolver streamResolver, - StreamProxyService proxyService) + StreamProxyService proxyService, + IServerApplicationHost appHost) { _loggerFactory = loggerFactory; _logger = loggerFactory.CreateLogger(); _metadataCache = metadataCache; _streamResolver = streamResolver; _proxyService = proxyService; + _appHost = appHost; } /// @@ -174,10 +179,14 @@ public class SRFMediaProvider : IMediaSourceProvider var itemIdStr = item.Id.ToString("N"); // Use hex format without dashes _proxyService.RegisterStream(itemIdStr, streamUrl); + // Get the server's local API URL so remote clients can access the proxy + // Use the published server address configured in Jellyfin's network settings + var serverUrl = _appHost.GetSmartApiUrl(string.Empty); + // Create proxy URL as absolute HTTP URL (required for ffmpeg) - // Use localhost as Jellyfin should be able to access its own endpoints + // Use the actual server URL so remote clients can access it // Include item ID as query parameter to preserve it during transcoding - var proxyUrl = $"http://localhost:8096/Plugins/SRFPlay/Proxy/{itemIdStr}/master.m3u8?itemId={itemIdStr}"; + var proxyUrl = $"{serverUrl}/Plugins/SRFPlay/Proxy/{itemIdStr}/master.m3u8?itemId={itemIdStr}"; _logger.LogInformation("Using proxy URL for item {ItemId}: {ProxyUrl}", itemIdStr, proxyUrl); diff --git a/Jellyfin.Plugin.SRFPlay/Services/StreamUrlResolver.cs b/Jellyfin.Plugin.SRFPlay/Services/StreamUrlResolver.cs index a29e5a5..f46c1cc 100644 --- a/Jellyfin.Plugin.SRFPlay/Services/StreamUrlResolver.cs +++ b/Jellyfin.Plugin.SRFPlay/Services/StreamUrlResolver.cs @@ -87,6 +87,17 @@ public class StreamUrlResolver : IDisposable chapter.Id, hlsResources.Count); + // Log all HLS resources with their quality info to help debug quality selection + foreach (var resource in hlsResources) + { + _logger.LogInformation( + "Available HLS resource - Quality={Quality}, Protocol={Protocol}, Streaming={Streaming}, URL={Url}", + resource.Quality ?? "NULL", + resource.Protocol ?? "NULL", + resource.Streaming ?? "NULL", + resource.Url); + } + if (hlsResources.Count == 0) { _logger.LogWarning("No HLS resources found for chapter: {ChapterId}", chapter.Id); @@ -114,6 +125,10 @@ public class StreamUrlResolver : IDisposable } // Select based on quality preference + _logger.LogInformation( + "Selecting stream with quality preference: {QualityPreference}", + qualityPreference); + Resource? selectedResource = qualityPreference switch { QualityPreference.HD => SelectHDResource(hlsResources) ?? SelectBestAvailableResource(hlsResources), @@ -124,10 +139,10 @@ public class StreamUrlResolver : IDisposable if (selectedResource != null) { - _logger.LogDebug( - "Selected stream for chapter {ChapterId}: Quality={Quality}, Protocol={Protocol}, URL={Url}", + _logger.LogInformation( + "✅ Selected stream for chapter {ChapterId}: Quality={Quality}, Protocol={Protocol}, URL={Url}", chapter.Id, - selectedResource.Quality, + selectedResource.Quality ?? "NULL", selectedResource.Protocol, selectedResource.Url); return selectedResource.Url;