passthrough not transcode
All checks were successful
🚀 Release Plugin / build-and-release (push) Successful in 3m19s
🏗️ Build Plugin / build (push) Successful in 3m8s
🧪 Test Plugin / test (push) Successful in 1m43s

This commit is contained in:
Duncan Tourolle 2025-11-16 20:53:23 +01:00
parent cd0f680981
commit e26f2a2ab1
3 changed files with 41 additions and 8 deletions

View File

@ -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;
/// <summary>
/// Initializes a new instance of the <see cref="SRFPlayChannel"/> class.
@ -35,12 +37,14 @@ public class SRFPlayChannel : IChannel, IHasCacheKey
/// <param name="contentRefreshService">The content refresh service.</param>
/// <param name="streamResolver">The stream resolver.</param>
/// <param name="proxyService">The stream proxy service.</param>
/// <param name="appHost">The server application host.</param>
/// <param name="categoryService">The category service (optional).</param>
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;

View File

@ -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;
/// <summary>
/// Initializes a new instance of the <see cref="SRFMediaProvider"/> class.
@ -32,17 +34,20 @@ public class SRFMediaProvider : IMediaSourceProvider
/// <param name="metadataCache">The metadata cache.</param>
/// <param name="streamResolver">The stream URL resolver.</param>
/// <param name="proxyService">The stream proxy service.</param>
/// <param name="appHost">The server application host.</param>
public SRFMediaProvider(
ILoggerFactory loggerFactory,
MetadataCache metadataCache,
StreamUrlResolver streamResolver,
StreamProxyService proxyService)
StreamProxyService proxyService,
IServerApplicationHost appHost)
{
_loggerFactory = loggerFactory;
_logger = loggerFactory.CreateLogger<SRFMediaProvider>();
_metadataCache = metadataCache;
_streamResolver = streamResolver;
_proxyService = proxyService;
_appHost = appHost;
}
/// <summary>
@ -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);

View File

@ -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;