Files
jellyfin-ha-src/Jellyfin.Server/Extensions/TranscodeStoreServiceCollectionExtensions.cs
T
unkin-agent 95220e2ed6
ci/woodpecker/push/ci Pipeline was successful
ci/woodpecker/pr/ci Pipeline was successful
fix(ha): read transcode store config from the variable form deployments set
The startup configuration only reads JELLYFIN_ prefixed environment
variables, so the bare Jellyfin__TranscodeStore__* form used by the chart,
the manifests and the README is dropped and the Redis store is never
registered. Nothing logs the selected store, so the fallback is invisible.

- Read bare Jellyfin__* variables into the Jellyfin:* configuration root
- Keep an explicit JELLYFIN_ variable winning over the bare form
- Log the selected transcode session store at startup
- Ping Redis once at startup and log an unreachable store at Error
- Log endpoints only, never the connection string
2026-09-13 13:10:52 +10:00

88 lines
3.4 KiB
C#

using System;
using System.Linq;
using Emby.Server.Implementations.MediaEncoding;
using MediaBrowser.Controller.MediaEncoding;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using StackExchange.Redis;
namespace Jellyfin.Server.Extensions;
/// <summary>
/// Extensions for registering the transcode session store.
/// </summary>
public static class TranscodeStoreServiceCollectionExtensions
{
/// <summary>
/// Registers the transcode session store, Redis-backed when a connection string is configured and
/// no-op otherwise, and reports the selected store at <see cref="LogLevel.Information"/>.
/// </summary>
/// <param name="serviceCollection">The service collection.</param>
/// <param name="configuration">The configuration to read <c>Jellyfin:TranscodeStore</c> from.</param>
/// <param name="logger">The logger to report the selected store on.</param>
/// <returns>The updated service collection.</returns>
public static IServiceCollection AddTranscodeSessionStore(
this IServiceCollection serviceCollection,
IConfiguration configuration,
ILogger logger)
{
ArgumentNullException.ThrowIfNull(configuration);
ArgumentNullException.ThrowIfNull(logger);
serviceCollection.Configure<TranscodeStoreOptions>(configuration.GetSection(TranscodeStoreOptions.ConfigurationSection));
var redisConnectionString = configuration[TranscodeStoreOptions.RedisConnectionStringKey];
if (string.IsNullOrEmpty(redisConnectionString))
{
logger.LogInformation(
"Transcode session store: {Store}. Cross-pod transcode takeover is off; set {Key} to enable it.",
nameof(NullTranscodeSessionStore),
TranscodeStoreOptions.RedisConnectionStringKey);
return serviceCollection.AddSingleton<ITranscodeSessionStore, NullTranscodeSessionStore>();
}
logger.LogInformation(
"Transcode session store: {Store} on {Endpoints}.",
nameof(RedisTranscodeSessionStore),
DescribeEndpoints(redisConnectionString));
serviceCollection.AddSingleton<IConnectionMultiplexer>(sp =>
{
try
{
return ConnectionMultiplexer.Connect(redisConnectionString);
}
catch (Exception ex)
{
sp.GetRequiredService<ILogger<CoreAppHost>>()
.LogError(ex, "Failed to connect to Redis. Check the {Key} configuration.", TranscodeStoreOptions.RedisConnectionStringKey);
throw;
}
});
serviceCollection.AddSingleton<ITranscodeSessionStore, RedisTranscodeSessionStore>();
serviceCollection.AddHostedService<TranscodeStoreConnectivityProbe>();
return serviceCollection;
}
/// <summary>
/// Renders the endpoints of a connection string for logging. The connection string itself is never
/// logged because it can carry a password.
/// </summary>
private static string DescribeEndpoints(string redisConnectionString)
{
try
{
return string.Join(
',',
ConfigurationOptions.Parse(redisConnectionString).EndPoints.Select(endpoint => endpoint.ToString()));
}
catch (ArgumentException)
{
return "(unparsable connection string)";
}
}
}