All checks were successful
continuous-integration/drone/push Build is passing
158 lines
6.8 KiB
C#
158 lines
6.8 KiB
C#
using MediaBrowser.Controller;
|
|
using MediaBrowser.Controller.Dto;
|
|
using MediaBrowser.Controller.Library;
|
|
using MediaBrowser.Controller.Plugins;
|
|
using MediaBrowser.Controller.Providers;
|
|
using Microsoft.AspNetCore.Mvc.ApplicationModels;
|
|
using Microsoft.AspNetCore.Mvc.Filters;
|
|
using Microsoft.Extensions.DependencyInjection;
|
|
|
|
namespace Jellyfin.Plugin.Cinema;
|
|
|
|
/// <summary>
|
|
/// Register Cinema services.
|
|
/// </summary>
|
|
///
|
|
public class CinemaServiceRegistrator : IPluginServiceRegistrator
|
|
{
|
|
/// <inheritdoc />
|
|
public void RegisterServices(IServiceCollection serviceCollection, IServerApplicationHost applicationHost)
|
|
{
|
|
//serviceCollection.AddSingleton<IImageProvider, CinemaImageProvider>();
|
|
serviceCollection.AddHostedService<CinemaHost>();
|
|
|
|
// HACK: Replace IMediaSourceManager as CinemaMediaSourceProvider is not called soon enough while
|
|
// entering a media item page.
|
|
Type tMsm = typeof(IMediaSourceManager);
|
|
int count = serviceCollection.Count;
|
|
for (int i = 0; i < count; i++)
|
|
{
|
|
ServiceDescriptor a = serviceCollection[i];
|
|
if (a.ServiceType == tMsm && !a.IsKeyedService)
|
|
{
|
|
Type oldImplType = a.ImplementationType!;
|
|
serviceCollection.RemoveAt(i);
|
|
serviceCollection.AddSingleton(new CinemaInnerMediaSourceManager(oldImplType));
|
|
serviceCollection.AddSingleton(oldImplType, oldImplType);
|
|
// Replace IMediaSourceManager but also need direct access to the CinemaMediaSourceManager if it itself
|
|
// also gets replaced in a longer chain
|
|
serviceCollection.AddSingleton<CinemaMediaSourceManager>();
|
|
serviceCollection.AddSingleton<IMediaSourceManager>(x => x.GetRequiredService<CinemaMediaSourceManager>());
|
|
break;
|
|
}
|
|
}
|
|
|
|
// HACK: Replace ILibraryManager as Series.GetEpisodes is not virtual
|
|
Type tLm = typeof(ILibraryManager);
|
|
int count2 = serviceCollection.Count;
|
|
for (int i = 0; i < count2; i++)
|
|
{
|
|
ServiceDescriptor a = serviceCollection[i];
|
|
if (a.ServiceType == tLm && !a.IsKeyedService)
|
|
{
|
|
Type oldImplType = a.ImplementationType!;
|
|
serviceCollection.RemoveAt(i);
|
|
serviceCollection.AddSingleton(new CinemaInnerLibraryManager(oldImplType));
|
|
serviceCollection.AddSingleton(oldImplType, oldImplType);
|
|
// Replace IMediaSourceManager but also need direct access to the CinemaLibraryManager if it itself
|
|
// also gets replaced in a longer chain
|
|
serviceCollection.AddSingleton<CinemaLibraryManager>();
|
|
serviceCollection.AddSingleton<ILibraryManager>(x => x.GetRequiredService<CinemaLibraryManager>());
|
|
break;
|
|
}
|
|
}
|
|
|
|
// HACK: Extract values for some API calls that are otherwise inaccessible
|
|
serviceCollection.AddMvc(options =>
|
|
{
|
|
options.Conventions.Add(new MediaSourceIdExtractConvention());
|
|
});
|
|
}
|
|
|
|
sealed class MediaSourceIdExtractConvention : IApplicationModelConvention
|
|
{
|
|
public void Apply(ApplicationModel application)
|
|
{
|
|
ControllerModel? mediaInfo = application.Controllers.Where(x => x.ControllerName == "MediaInfo").FirstOrDefault();
|
|
ControllerModel? dynamicHls = application.Controllers.Where(x => x.ControllerName == "DynamicHls").FirstOrDefault();
|
|
ControllerModel? subtitle = application.Controllers.Where(x => x.ControllerName == "Subtitle").FirstOrDefault();
|
|
ActionModel? getPostedPlaybackInfo, getMasterHlsVideoPlaylist, getVariantHlsVideoPlaylist, getHlsVideoSegment, getSubtitleWithTicks;
|
|
if (mediaInfo == null
|
|
|| (getPostedPlaybackInfo = mediaInfo.Actions.Where(x => x.ActionName == "GetPostedPlaybackInfo").FirstOrDefault()) == null
|
|
|| dynamicHls == null
|
|
|| (getMasterHlsVideoPlaylist = dynamicHls.Actions.Where(x => x.ActionName == "GetMasterHlsVideoPlaylist").FirstOrDefault()) == null
|
|
|| (getVariantHlsVideoPlaylist = dynamicHls.Actions.Where(x => x.ActionName == "GetVariantHlsVideoPlaylist").FirstOrDefault()) == null
|
|
|| (getHlsVideoSegment = dynamicHls.Actions.Where(x => x.ActionName == "GetHlsVideoSegment").FirstOrDefault()) == null
|
|
|| subtitle == null
|
|
|| (getSubtitleWithTicks = subtitle.Actions.Where(x => x.ActionName == "GetSubtitleWithTicks").FirstOrDefault()) == null)
|
|
throw new InvalidOperationException("Failed to register for MediaSourceId extraction from the Jellyfin's MediaInfo controller.");
|
|
|
|
getPostedPlaybackInfo.Filters.Add(new GetPostedPlaybackInfoMediaSourceIdFilter());
|
|
// Note: All DynamicHls controller actions share the same parameter name so we can
|
|
// re-use the same filter for all
|
|
getMasterHlsVideoPlaylist.Filters.Add(new DynamicHlsMediaSourceIdFilter());
|
|
getVariantHlsVideoPlaylist.Filters.Add(new DynamicHlsMediaSourceIdFilter());
|
|
getHlsVideoSegment.Filters.Add(new DynamicHlsMediaSourceIdFilter());
|
|
// Slightly different parameter name
|
|
getSubtitleWithTicks.Filters.Add(new SubtitleMediaSourceIdFilter());
|
|
}
|
|
}
|
|
|
|
sealed class GetPostedPlaybackInfoMediaSourceIdFilter : IActionFilter
|
|
{
|
|
public void OnActionExecuting(ActionExecutingContext context)
|
|
{
|
|
string? mediaSourceId;
|
|
if (!context.ActionArguments.TryGetValue("playbackInfoDto", out object? playbackInfoO)
|
|
|| (mediaSourceId = playbackInfoO!.GetType().GetProperty("MediaSourceId")?.GetValue(playbackInfoO) as string) == null)
|
|
{
|
|
// Fallback to the obsolete query argument
|
|
if (!context.ActionArguments.TryGetValue("mediaSourceId", out object? mediaSourceIdO)
|
|
|| (mediaSourceId = mediaSourceIdO as string) == null)
|
|
// It is optional in certain situations
|
|
return;
|
|
//throw new InvalidOperationException("Cannot extract MediaSourceId from the Jellyfin's action MediaInfo.GetPostedPlaybackInfo.");
|
|
}
|
|
|
|
context.HttpContext.Items.Add(CinemaMediaSourceManager.ContextItemsMediaSourceIdKey, mediaSourceId);
|
|
}
|
|
|
|
public void OnActionExecuted(ActionExecutedContext context)
|
|
{
|
|
}
|
|
}
|
|
|
|
sealed class DynamicHlsMediaSourceIdFilter : IActionFilter
|
|
{
|
|
public void OnActionExecuting(ActionExecutingContext context)
|
|
{
|
|
string? mediaSourceId;
|
|
if (!context.ActionArguments.TryGetValue("mediaSourceId", out object? mediaSourceIdO)
|
|
|| (mediaSourceId = mediaSourceIdO as string) == null)
|
|
throw new InvalidOperationException("Cannot extract MediaSourceId from the Jellyfin's controller DynamicHls.");
|
|
|
|
context.HttpContext.Items.Add(CinemaMediaSourceManager.ContextItemsMediaSourceIdKey, mediaSourceId);
|
|
}
|
|
|
|
public void OnActionExecuted(ActionExecutedContext context)
|
|
{
|
|
}
|
|
}
|
|
|
|
sealed class SubtitleMediaSourceIdFilter : IActionFilter
|
|
{
|
|
public void OnActionExecuting(ActionExecutingContext context)
|
|
{
|
|
string? mediaSourceId;
|
|
if (!context.ActionArguments.TryGetValue("routeMediaSourceId", out object? mediaSourceIdO)
|
|
|| (mediaSourceId = mediaSourceIdO as string) == null)
|
|
throw new InvalidOperationException("Cannot extract MediaSourceId from the Jellyfin's controller Subtitle.");
|
|
|
|
context.HttpContext.Items.Add(CinemaMediaSourceManager.ContextItemsMediaSourceIdKey, mediaSourceId);
|
|
}
|
|
|
|
public void OnActionExecuted(ActionExecutedContext context)
|
|
{
|
|
}
|
|
}
|
|
} |