[PR #15263] [MERGED] Resolve symlinks for static media source infos #14267

Closed
opened 2026-02-07 07:27:06 +03:00 by OVERLORD · 0 comments
Owner

📋 Pull Request Information

Original PR: https://github.com/jellyfin/jellyfin/pull/15263
Author: @revam
Created: 10/28/2025
Status: Merged
Merged: 11/9/2025
Merged by: @crobibero

Base: release-10.11.zHead: bugfix/resolve-media-path-before-use


📝 Commits (9)

  • 4a9eb4e Resolve symlinks for static media source infos
  • a4147b0 Use video.Path instead of mediaSource.Path in trickplay manager when checking parent directory
  • 0c7cd95 Only attempt to resolve target if using file protocol
  • 5e8b7ff Catch IOExceptions when attempting to resolve path
  • 85f1c29 Add ResolveLinkTarget helpers to account for runtime issues with returnFinalTarget
  • 4fbfbbe Apply changes based on feedback
  • 251a9c7 Remove now unnecessary try…catch for IOExceptions
  • 587abd4 Refactor loop to use the visitor pattern to keep track of which nodes/paths have been visited.
  • 47344b3 Add fast path to avoid allocations if not a link

📊 Changes

5 files changed (+91 additions, -5 deletions)

View changed files

📝 Emby.Server.Implementations/IO/ManagedFileSystem.cs (+2 -1)
📝 Emby.Server.Implementations/Library/DotIgnoreIgnoreRule.cs (+2 -1)
📝 Jellyfin.Server.Implementations/Trickplay/TrickplayManager.cs (+2 -2)
📝 MediaBrowser.Controller/Entities/BaseItem.cs (+11 -1)
📝 MediaBrowser.Controller/IO/FileSystemHelper.cs (+74 -0)

📄 Description

Changes
This change will prevent issues like trying to Direct Play a symbolic link resulting in an out of range exception at the deepest possible level instead of in the API layer — in which it was handled before #15209 removed the symbolic link handler from the API — to ensure both the API and any internal processes using the media sources will always use a real file and never a symbolic link.

As part of this change, I swept through the code base to look for potential issues this may introduce, and found that the trickplay manager is checking the parent of the media source when checking if the video is in a backdrop folder, so i changed that to instead use the video path. I did not find any other issues in regards to this change.

Issues / PRs
Follow up for https://github.com/jellyfin/jellyfin/pull/15209

Player errs out when you try to play a symlinked file on 10.11.1;

[00:17:47] [ERR] [043] Jellyfin.Api.Middleware.ExceptionMiddleware: Error processing request. URL GET /Videos/1e295cae3782876cf0ac5cccb8ec1d85/stream.mkv.
System.ArgumentOutOfRangeException:  (Parameter 'count')
Actual value was 965864780.
   at Microsoft.AspNetCore.Http.SendFileFallback.SendFileAsync(Stream destination, String filePath, Int64 offset, Nullable`1 count, CancellationToken cancellationToken)
   at Microsoft.AspNetCore.Http.SendFileResponseExtensions.SendFileAsyncCore(HttpResponse response, String fileName, Int64 offset, Nullable`1 count, CancellationToken cancellationToken)
   at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeNextResultFilterAsync>g__Awaited|30_0[TFilter,TFilterAsync](ResourceInvoker invoker, Task lastTask, State next, Scope scope, Object state, Boolean isCompleted)
   at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.Rethrow(ResultExecutedContextSealed context)
   at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.ResultNext[TFilter,TFilterAsync](State& next, Scope& scope, Object& state, Boolean& isCompleted)
   at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.InvokeResultFilters()
--- End of stack trace from previous location ---
   at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeNextResourceFilter>g__Awaited|25_0(ResourceInvoker invoker, Task lastTask, State next, Scope scope, Object state, Boolean isCompleted)
   at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.Rethrow(ResourceExecutedContextSealed context)
   at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.Next(State& next, Scope& scope, Object& state, Boolean& isCompleted)
   at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.InvokeFilterPipelineAsync()
--- End of stack trace from previous location ---
   at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeAsync>g__Awaited|17_0(ResourceInvoker invoker, Task task, IDisposable scope)
   at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeAsync>g__Awaited|17_0(ResourceInvoker invoker, Task task, IDisposable scope)
   at Jellyfin.Api.Middleware.ServerStartupMessageMiddleware.Invoke(HttpContext httpContext, IServerApplicationHost serverApplicationHost, ILocalizationManager localizationManager) in /home/revam/Projects/Jellyfin/Jellyfin.Api/Middleware/ServerStartupMessageMiddleware.cs:line 41
   at Jellyfin.Api.Middleware.WebSocketHandlerMiddleware.Invoke(HttpContext httpContext, IWebSocketManager webSocketManager) in /home/revam/Projects/Jellyfin/Jellyfin.Api/Middleware/WebSocketHandlerMiddleware.cs:line 33
   at Jellyfin.Api.Middleware.IPBasedAccessValidationMiddleware.Invoke(HttpContext httpContext, INetworkManager networkManager) in /home/revam/Projects/Jellyfin/Jellyfin.Api/Middleware/IpBasedAccessValidationMiddleware.cs:line 41
   at Microsoft.AspNetCore.Authorization.AuthorizationMiddleware.Invoke(HttpContext context)
   at Jellyfin.Api.Middleware.QueryStringDecodingMiddleware.Invoke(HttpContext httpContext) in /home/revam/Projects/Jellyfin/Jellyfin.Api/Middleware/QueryStringDecodingMiddleware.cs:line 36
   at Swashbuckle.AspNetCore.ReDoc.ReDocMiddleware.Invoke(HttpContext httpContext)
   at Swashbuckle.AspNetCore.SwaggerUI.SwaggerUIMiddleware.Invoke(HttpContext httpContext)
   at Swashbuckle.AspNetCore.Swagger.SwaggerMiddleware.Invoke(HttpContext httpContext, ISwaggerProvider swaggerProvider)
   at Microsoft.AspNetCore.Authentication.AuthenticationMiddleware.Invoke(HttpContext context)
   at Jellyfin.Api.Middleware.RobotsRedirectionMiddleware.Invoke(HttpContext httpContext) in /home/revam/Projects/Jellyfin/Jellyfin.Api/Middleware/RobotsRedirectionMiddleware.cs:line 43
   at Jellyfin.Api.Middleware.LegacyEmbyRouteRewriteMiddleware.Invoke(HttpContext httpContext) in /home/revam/Projects/Jellyfin/Jellyfin.Api/Middleware/LegacyEmbyRouteRewriteMiddleware.cs:line 51
   at Jellyfin.Api.Middleware.ResponseTimeMiddleware.Invoke(HttpContext context, IServerConfigurationManager serverConfigurationManager) in /home/revam/Projects/Jellyfin/Jellyfin.Api/Middleware/ResponseTimeMiddleware.cs:line 66
   at Jellyfin.Api.Middleware.ExceptionMiddleware.Invoke(HttpContext context) in /home/revam/Projects/Jellyfin/Jellyfin.Api/Middleware/ExceptionMiddleware.cs:line 55
image

🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.

## 📋 Pull Request Information **Original PR:** https://github.com/jellyfin/jellyfin/pull/15263 **Author:** [@revam](https://github.com/revam) **Created:** 10/28/2025 **Status:** ✅ Merged **Merged:** 11/9/2025 **Merged by:** [@crobibero](https://github.com/crobibero) **Base:** `release-10.11.z` ← **Head:** `bugfix/resolve-media-path-before-use` --- ### 📝 Commits (9) - [`4a9eb4e`](https://github.com/jellyfin/jellyfin/commit/4a9eb4e185dc7adf600ba61d0257c4d51b095242) Resolve symlinks for static media source infos - [`a4147b0`](https://github.com/jellyfin/jellyfin/commit/a4147b0511d8df43972bd78b8ab283278ae88988) Use video.Path instead of mediaSource.Path in trickplay manager when checking parent directory - [`0c7cd95`](https://github.com/jellyfin/jellyfin/commit/0c7cd9575497aaf122f8dd5688fd5fe599bc023b) Only attempt to resolve target if using file protocol - [`5e8b7ff`](https://github.com/jellyfin/jellyfin/commit/5e8b7ffd2b09c84349f990b542a18de198eeffa7) Catch `IOException`s when attempting to resolve path - [`85f1c29`](https://github.com/jellyfin/jellyfin/commit/85f1c292a142be10f948542cc6bf0c6302cbbb01) Add `ResolveLinkTarget` helpers to account for runtime issues with `returnFinalTarget` - [`4fbfbbe`](https://github.com/jellyfin/jellyfin/commit/4fbfbbe6705b61d0b7486835b4e49a4b239e3c52) Apply changes based on feedback - [`251a9c7`](https://github.com/jellyfin/jellyfin/commit/251a9c707efdb9d5dc180c6057c93e71a7f881b7) Remove now unnecessary `try…catch` for `IOException`s - [`587abd4`](https://github.com/jellyfin/jellyfin/commit/587abd43702cad4efa32ce9fa0ec4dc9c9537aa5) Refactor loop to use the visitor pattern to keep track of which nodes/paths have been visited. - [`47344b3`](https://github.com/jellyfin/jellyfin/commit/47344b39b81280ede5bcf8c7ec4a384081c8d6b6) Add fast path to avoid allocations if not a link ### 📊 Changes **5 files changed** (+91 additions, -5 deletions) <details> <summary>View changed files</summary> 📝 `Emby.Server.Implementations/IO/ManagedFileSystem.cs` (+2 -1) 📝 `Emby.Server.Implementations/Library/DotIgnoreIgnoreRule.cs` (+2 -1) 📝 `Jellyfin.Server.Implementations/Trickplay/TrickplayManager.cs` (+2 -2) 📝 `MediaBrowser.Controller/Entities/BaseItem.cs` (+11 -1) 📝 `MediaBrowser.Controller/IO/FileSystemHelper.cs` (+74 -0) </details> ### 📄 Description **Changes** This change will prevent issues like _trying to Direct Play a symbolic link resulting in an out of range exception_ at the deepest possible level instead of in the API layer — in which it was handled before #15209 removed the symbolic link handler from the API — to ensure both the API and any internal processes using the media sources will always use a real file and never a symbolic link. As part of this change, I swept through the code base to look for potential issues this may introduce, and found that the trickplay manager is checking the parent of the media source when checking if the video is in a backdrop folder, so i changed that to instead use the video path. I did not find any other issues in regards to this change. **Issues / PRs** Follow up for https://github.com/jellyfin/jellyfin/pull/15209 Player errs out when you try to play a symlinked file on 10.11.1; ``` [00:17:47] [ERR] [043] Jellyfin.Api.Middleware.ExceptionMiddleware: Error processing request. URL GET /Videos/1e295cae3782876cf0ac5cccb8ec1d85/stream.mkv. System.ArgumentOutOfRangeException: (Parameter 'count') Actual value was 965864780. at Microsoft.AspNetCore.Http.SendFileFallback.SendFileAsync(Stream destination, String filePath, Int64 offset, Nullable`1 count, CancellationToken cancellationToken) at Microsoft.AspNetCore.Http.SendFileResponseExtensions.SendFileAsyncCore(HttpResponse response, String fileName, Int64 offset, Nullable`1 count, CancellationToken cancellationToken) at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeNextResultFilterAsync>g__Awaited|30_0[TFilter,TFilterAsync](ResourceInvoker invoker, Task lastTask, State next, Scope scope, Object state, Boolean isCompleted) at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.Rethrow(ResultExecutedContextSealed context) at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.ResultNext[TFilter,TFilterAsync](State& next, Scope& scope, Object& state, Boolean& isCompleted) at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.InvokeResultFilters() --- End of stack trace from previous location --- at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeNextResourceFilter>g__Awaited|25_0(ResourceInvoker invoker, Task lastTask, State next, Scope scope, Object state, Boolean isCompleted) at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.Rethrow(ResourceExecutedContextSealed context) at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.Next(State& next, Scope& scope, Object& state, Boolean& isCompleted) at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.InvokeFilterPipelineAsync() --- End of stack trace from previous location --- at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeAsync>g__Awaited|17_0(ResourceInvoker invoker, Task task, IDisposable scope) at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeAsync>g__Awaited|17_0(ResourceInvoker invoker, Task task, IDisposable scope) at Jellyfin.Api.Middleware.ServerStartupMessageMiddleware.Invoke(HttpContext httpContext, IServerApplicationHost serverApplicationHost, ILocalizationManager localizationManager) in /home/revam/Projects/Jellyfin/Jellyfin.Api/Middleware/ServerStartupMessageMiddleware.cs:line 41 at Jellyfin.Api.Middleware.WebSocketHandlerMiddleware.Invoke(HttpContext httpContext, IWebSocketManager webSocketManager) in /home/revam/Projects/Jellyfin/Jellyfin.Api/Middleware/WebSocketHandlerMiddleware.cs:line 33 at Jellyfin.Api.Middleware.IPBasedAccessValidationMiddleware.Invoke(HttpContext httpContext, INetworkManager networkManager) in /home/revam/Projects/Jellyfin/Jellyfin.Api/Middleware/IpBasedAccessValidationMiddleware.cs:line 41 at Microsoft.AspNetCore.Authorization.AuthorizationMiddleware.Invoke(HttpContext context) at Jellyfin.Api.Middleware.QueryStringDecodingMiddleware.Invoke(HttpContext httpContext) in /home/revam/Projects/Jellyfin/Jellyfin.Api/Middleware/QueryStringDecodingMiddleware.cs:line 36 at Swashbuckle.AspNetCore.ReDoc.ReDocMiddleware.Invoke(HttpContext httpContext) at Swashbuckle.AspNetCore.SwaggerUI.SwaggerUIMiddleware.Invoke(HttpContext httpContext) at Swashbuckle.AspNetCore.Swagger.SwaggerMiddleware.Invoke(HttpContext httpContext, ISwaggerProvider swaggerProvider) at Microsoft.AspNetCore.Authentication.AuthenticationMiddleware.Invoke(HttpContext context) at Jellyfin.Api.Middleware.RobotsRedirectionMiddleware.Invoke(HttpContext httpContext) in /home/revam/Projects/Jellyfin/Jellyfin.Api/Middleware/RobotsRedirectionMiddleware.cs:line 43 at Jellyfin.Api.Middleware.LegacyEmbyRouteRewriteMiddleware.Invoke(HttpContext httpContext) in /home/revam/Projects/Jellyfin/Jellyfin.Api/Middleware/LegacyEmbyRouteRewriteMiddleware.cs:line 51 at Jellyfin.Api.Middleware.ResponseTimeMiddleware.Invoke(HttpContext context, IServerConfigurationManager serverConfigurationManager) in /home/revam/Projects/Jellyfin/Jellyfin.Api/Middleware/ResponseTimeMiddleware.cs:line 66 at Jellyfin.Api.Middleware.ExceptionMiddleware.Invoke(HttpContext context) in /home/revam/Projects/Jellyfin/Jellyfin.Api/Middleware/ExceptionMiddleware.cs:line 55 ``` <img width="960" height="540" alt="image" src="https://github.com/user-attachments/assets/5ba50da7-f06a-4779-a2aa-a645f2599b58" /> <!-- Tag any issues that this PR solves here. ex. Fixes # --> --- <sub>🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.</sub>
OVERLORD added the pull-request label 2026-02-07 07:27:06 +03:00
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: starred/jellyfin#14267