diff options
| author | Niels van Velzen <nielsvanvelzen@users.noreply.github.com> | 2026-05-03 21:56:34 +0200 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2026-05-03 21:56:34 +0200 |
| commit | 6e22075a63432aae48859cf9c67fde158dc80d2e (patch) | |
| tree | c3a33238cc56857d8e3daa56db01f290118c9215 /Jellyfin.Api/Controllers/ImageController.cs | |
| parent | d9ced0d6399c82ddad9e983605bb0d828a608e63 (diff) | |
| parent | d68d0fa96267ad96eaa5a0ba37e072f59a71442a (diff) | |
Merge pull request #16062 from Shadowghost/perf-rebased
Query Performance Improvements
Diffstat (limited to 'Jellyfin.Api/Controllers/ImageController.cs')
| -rw-r--r-- | Jellyfin.Api/Controllers/ImageController.cs | 43 |
1 files changed, 29 insertions, 14 deletions
diff --git a/Jellyfin.Api/Controllers/ImageController.cs b/Jellyfin.Api/Controllers/ImageController.cs index cd8132d215..ae792142b4 100644 --- a/Jellyfin.Api/Controllers/ImageController.cs +++ b/Jellyfin.Api/Controllers/ImageController.cs @@ -1698,7 +1698,8 @@ public class ImageController : BaseJellyfinApiController return await GetImageResult( options, cacheDuration, - ImmutableDictionary<string, string>.Empty) + ImmutableDictionary<string, string>.Empty, + tag) .ConfigureAwait(false); } @@ -1913,7 +1914,8 @@ public class ImageController : BaseJellyfinApiController return await GetImageResult( options, cacheDuration, - responseHeaders).ConfigureAwait(false); + responseHeaders, + tag).ConfigureAwait(false); } private ImageFormat[] GetOutputFormats(ImageFormat? format) @@ -1992,18 +1994,13 @@ public class ImageController : BaseJellyfinApiController private async Task<ActionResult> GetImageResult( ImageProcessingOptions imageProcessingOptions, TimeSpan? cacheDuration, - IDictionary<string, string> headers) + IDictionary<string, string> headers, + string? tag) { var (imagePath, imageContentType, dateImageModified) = await _imageProcessor.ProcessImage(imageProcessingOptions).ConfigureAwait(false); var disableCaching = Request.Headers[HeaderNames.CacheControl].Contains("no-cache"); - var parsingSuccessful = DateTime.TryParse(Request.Headers[HeaderNames.IfModifiedSince], out var ifModifiedSinceHeader); - - // if the parsing of the IfModifiedSince header was not successful, disable caching - if (!parsingSuccessful) - { - // disableCaching = true; - } + var hasTag = !string.IsNullOrEmpty(tag); foreach (var (key, value) in headers) { @@ -2025,7 +2022,8 @@ public class ImageController : BaseJellyfinApiController { if (cacheDuration.HasValue) { - Response.Headers.Append(HeaderNames.CacheControl, "public, max-age=" + cacheDuration.Value.TotalSeconds); + // When tag is provided, the URL is effectively immutable - the tag changes when the image changes + Response.Headers.Append(HeaderNames.CacheControl, "public, max-age=" + cacheDuration.Value.TotalSeconds + ", immutable"); } else { @@ -2034,10 +2032,27 @@ public class ImageController : BaseJellyfinApiController Response.Headers.Append(HeaderNames.LastModified, dateImageModified.ToUniversalTime().ToString("ddd, dd MMM yyyy HH:mm:ss \"GMT\"", CultureInfo.InvariantCulture)); - // if the image was not modified since "ifModifiedSinceHeader"-header, return a HTTP status code 304 not modified - if (!(dateImageModified > ifModifiedSinceHeader) && cacheDuration.HasValue) + // Add ETag header for stronger cache validation when tag is provided + if (hasTag) + { + Response.Headers.Append(HeaderNames.ETag, $"\"{tag}\""); + + // Check If-None-Match header for ETag-based validation (preferred over If-Modified-Since) + var ifNoneMatch = Request.Headers[HeaderNames.IfNoneMatch].ToString(); + if (!string.IsNullOrEmpty(ifNoneMatch) + && (string.Equals(ifNoneMatch, $"\"{tag}\"", StringComparison.Ordinal) + || string.Equals(ifNoneMatch, tag, StringComparison.Ordinal))) + { + Response.StatusCode = StatusCodes.Status304NotModified; + return new ContentResult(); + } + } + + // Check If-Modified-Since header for time-based validation + if (DateTime.TryParse(Request.Headers[HeaderNames.IfModifiedSince], out var ifModifiedSinceHeader)) { - if (ifModifiedSinceHeader.Add(cacheDuration.Value) < DateTime.UtcNow) + // Return 304 if the image has not been modified since the client's cached version + if (dateImageModified <= ifModifiedSinceHeader) { Response.StatusCode = StatusCodes.Status304NotModified; return new ContentResult(); |
