From 19b783d7e996c406d1e1bc1366b5f340685391f4 Mon Sep 17 00:00:00 2001 From: Jason Dove <1695733+jasongdove@users.noreply.github.com> Date: Thu, 3 Sep 2026 10:06:27 -0500 Subject: [PATCH] fix: authorize artwork when requested from ui (#2998) --- ErsatzTV/Controllers/ArtworkController.cs | 46 +++++++++++++++++++++-- 1 file changed, 43 insertions(+), 3 deletions(-) diff --git a/ErsatzTV/Controllers/ArtworkController.cs b/ErsatzTV/Controllers/ArtworkController.cs index 413bb819d..2108a1167 100644 --- a/ErsatzTV/Controllers/ArtworkController.cs +++ b/ErsatzTV/Controllers/ArtworkController.cs @@ -10,6 +10,7 @@ using ErsatzTV.Core.Images; using ErsatzTV.Core.Interfaces.Images; using ErsatzTV.Core.Jellyfin; using ErsatzTV.Extensions; +using ErsatzTV.Filters; using Flurl; using MediatR; using Microsoft.AspNetCore.Mvc; @@ -27,6 +28,7 @@ public class ArtworkController( { [HttpHead("/artwork/{id:int}")] [HttpGet("/artwork/{id:int}")] + [ServiceFilter(typeof(ConditionalUiAuthorizeFilter))] // This route redirect to the proper artwork from its Id public async Task RedirectArtwork(int id, CancellationToken cancellationToken) { @@ -59,7 +61,19 @@ public class ArtworkController( [HttpGet("/iptv/artwork/posters/{fileName:hex}")] [HttpHead("/iptv/artwork/posters/{fileName:hex}.jpg")] [HttpGet("/iptv/artwork/posters/{fileName:hex}.jpg")] + public async Task IptvGetPoster(string fileName, CancellationToken cancellationToken) + { + Either cachedImagePath = + await mediator.Send( + new GetCachedImagePath(fileName, ArtworkKind.Poster, string.Empty, 440), + cancellationToken); + return cachedImagePath.Match( + Left: _ => new NotFoundResult(), + Right: r => new PhysicalFileResult(r.FileName, r.MimeType)); + } + [HttpGet("/artwork/posters/{fileName:hex}")] + [ServiceFilter(typeof(ConditionalUiAuthorizeFilter))] public async Task GetPoster(string fileName, CancellationToken cancellationToken) { Either cachedImagePath = @@ -72,6 +86,7 @@ public class ArtworkController( } [HttpGet("/artwork/watermarks/{fileName:hex}")] + [ServiceFilter(typeof(ConditionalUiAuthorizeFilter))] public async Task GetWatermark( string fileName, [FromQuery] @@ -88,6 +103,7 @@ public class ArtworkController( } [HttpGet("/artwork/fanart/{fileName:hex}")] + [ServiceFilter(typeof(ConditionalUiAuthorizeFilter))] public async Task GetFanArt(string fileName, CancellationToken cancellationToken) { Either cachedImagePath = @@ -100,31 +116,43 @@ public class ArtworkController( [HttpHead("/iptv/artwork/posters/plex/{id:int}")] [HttpGet("/iptv/artwork/posters/plex/{id:int}")] - [HttpGet("/artwork/posters/plex/{id:int}")] [HttpHead("/iptv/artwork/thumbnails/plex/{id:int}")] [HttpGet("/iptv/artwork/thumbnails/plex/{id:int}")] + public Task IptvGetPlex(int id, CancellationToken cancellationToken) => + GetPlexArtwork(id, cancellationToken); + + [HttpGet("/artwork/posters/plex/{id:int}")] [HttpGet("/artwork/thumbnails/plex/{id:int}")] [HttpGet("/artwork/fanart/plex/{id:int}")] + [ServiceFilter(typeof(ConditionalUiAuthorizeFilter))] public Task GetPlex(int id, CancellationToken cancellationToken) => GetPlexArtwork(id, cancellationToken); [HttpHead("/iptv/artwork/posters/jellyfin/{id:int}")] [HttpGet("/iptv/artwork/posters/jellyfin/{id:int}")] - [HttpGet("/artwork/posters/jellyfin/{id:int}")] [HttpHead("/iptv/artwork/thumbnails/jellyfin/{id:int}")] [HttpGet("/iptv/artwork/thumbnails/jellyfin/{id:int}")] + public Task IptvGetJellyfin(int id, CancellationToken cancellationToken) => + GetJellyfinArtwork(id, cancellationToken); + + [HttpGet("/artwork/posters/jellyfin/{id:int}")] [HttpGet("/artwork/thumbnails/jellyfin/{id:int}")] [HttpGet("/artwork/fanart/jellyfin/{id:int}")] + [ServiceFilter(typeof(ConditionalUiAuthorizeFilter))] public Task GetJellyfin(int id, CancellationToken cancellationToken) => GetJellyfinArtwork(id, cancellationToken); [HttpHead("/iptv/artwork/posters/emby/{id:int}")] [HttpGet("/iptv/artwork/posters/emby/{id:int}")] - [HttpGet("/artwork/posters/emby/{id:int}")] [HttpHead("/iptv/artwork/thumbnails/emby/{id:int}")] [HttpGet("/iptv/artwork/thumbnails/emby/{id:int}")] + public Task IptvetEmby(int id, CancellationToken cancellationToken) => + GetEmbyArtwork(id, cancellationToken); + + [HttpGet("/artwork/posters/emby/{id:int}")] [HttpGet("/artwork/thumbnails/emby/{id:int}")] [HttpGet("/artwork/fanart/emby/{id:int}")] + [ServiceFilter(typeof(ConditionalUiAuthorizeFilter))] public Task GetEmby(int id, CancellationToken cancellationToken) => GetEmbyArtwork(id, cancellationToken); @@ -132,7 +160,19 @@ public class ArtworkController( [HttpGet("/iptv/artwork/thumbnails/{fileName:hex}")] [HttpHead("/iptv/artwork/thumbnails/{fileName:hex}.jpg")] [HttpGet("/iptv/artwork/thumbnails/{fileName:hex}.jpg")] + public async Task IptvGetThumbnail(string fileName, CancellationToken cancellationToken) + { + Either cachedImagePath = + await mediator.Send( + new GetCachedImagePath(fileName, ArtworkKind.Thumbnail, string.Empty, 220), + cancellationToken); + return cachedImagePath.Match( + Left: _ => new NotFoundResult(), + Right: r => new PhysicalFileResult(r.FileName, r.MimeType)); + } + [HttpGet("/artwork/thumbnails/{fileName:hex}")] + [ServiceFilter(typeof(ConditionalUiAuthorizeFilter))] public async Task GetThumbnail(string fileName, CancellationToken cancellationToken) { Either cachedImagePath =