aboutsummaryrefslogtreecommitdiff
path: root/MediaBrowser.Controller
diff options
context:
space:
mode:
Diffstat (limited to 'MediaBrowser.Controller')
-rw-r--r--MediaBrowser.Controller/Entities/BaseItem.cs12
-rw-r--r--MediaBrowser.Controller/IO/FileSystemHelper.cs74
2 files changed, 85 insertions, 1 deletions
diff --git a/MediaBrowser.Controller/Entities/BaseItem.cs b/MediaBrowser.Controller/Entities/BaseItem.cs
index 4989f0f3f..3c46d53e5 100644
--- a/MediaBrowser.Controller/Entities/BaseItem.cs
+++ b/MediaBrowser.Controller/Entities/BaseItem.cs
@@ -24,6 +24,7 @@ using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Dto;
using MediaBrowser.Controller.Entities.Audio;
using MediaBrowser.Controller.Entities.TV;
+using MediaBrowser.Controller.IO;
using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.MediaSegments;
using MediaBrowser.Controller.Persistence;
@@ -1127,6 +1128,15 @@ namespace MediaBrowser.Controller.Entities
var protocol = item.PathProtocol;
+ // Resolve the item path so everywhere we use the media source it will always point to
+ // the correct path even if symlinks are in use. Calling ResolveLinkTarget on a non-link
+ // path will return null, so it's safe to check for all paths.
+ var itemPath = item.Path;
+ if (protocol is MediaProtocol.File && FileSystemHelper.ResolveLinkTarget(itemPath, returnFinalTarget: true) is { Exists: true } linkInfo)
+ {
+ itemPath = linkInfo.FullName;
+ }
+
var info = new MediaSourceInfo
{
Id = item.Id.ToString("N", CultureInfo.InvariantCulture),
@@ -1134,7 +1144,7 @@ namespace MediaBrowser.Controller.Entities
MediaStreams = MediaSourceManager.GetMediaStreams(item.Id),
MediaAttachments = MediaSourceManager.GetMediaAttachments(item.Id),
Name = GetMediaSourceName(item),
- Path = enablePathSubstitution ? GetMappedPath(item, item.Path, protocol) : item.Path,
+ Path = enablePathSubstitution ? GetMappedPath(item, itemPath, protocol) : itemPath,
RunTimeTicks = item.RunTimeTicks,
Container = item.Container,
Size = item.Size,
diff --git a/MediaBrowser.Controller/IO/FileSystemHelper.cs b/MediaBrowser.Controller/IO/FileSystemHelper.cs
index 1a33c3aa8..324aea7e3 100644
--- a/MediaBrowser.Controller/IO/FileSystemHelper.cs
+++ b/MediaBrowser.Controller/IO/FileSystemHelper.cs
@@ -1,4 +1,5 @@
using System;
+using System.Collections.Generic;
using System.IO;
using System.Linq;
using MediaBrowser.Model.IO;
@@ -61,4 +62,77 @@ public static class FileSystemHelper
}
}
}
+
+ /// <summary>
+ /// Gets the target of the specified file link.
+ /// </summary>
+ /// <remarks>
+ /// This helper exists because of this upstream runtime issue; https://github.com/dotnet/runtime/issues/92128.
+ /// </remarks>
+ /// <param name="linkPath">The path of the file link.</param>
+ /// <param name="returnFinalTarget">true to follow links to the final target; false to return the immediate next link.</param>
+ /// <returns>
+ /// A <see cref="FileInfo"/> if the <paramref name="linkPath"/> is a link, regardless of if the target exists; otherwise, <c>null</c>.
+ /// </returns>
+ public static FileInfo? ResolveLinkTarget(string linkPath, bool returnFinalTarget = false)
+ {
+ // Check if the file exists so the native resolve handler won't throw at us.
+ if (!File.Exists(linkPath))
+ {
+ return null;
+ }
+
+ if (!returnFinalTarget)
+ {
+ return File.ResolveLinkTarget(linkPath, returnFinalTarget: false) as FileInfo;
+ }
+
+ if (File.ResolveLinkTarget(linkPath, returnFinalTarget: false) is not FileInfo targetInfo)
+ {
+ return null;
+ }
+
+ var currentPath = targetInfo.FullName;
+ var visited = new HashSet<string>(StringComparer.Ordinal) { linkPath, currentPath };
+ while (File.ResolveLinkTarget(currentPath, returnFinalTarget: false) is FileInfo linkInfo)
+ {
+ var targetPath = linkInfo.FullName;
+
+ // If an infinite loop is detected, return the file info for the
+ // first link in the loop we encountered.
+ if (!visited.Add(targetPath))
+ {
+ return new FileInfo(targetPath);
+ }
+
+ targetInfo = linkInfo;
+ currentPath = targetPath;
+
+ // Exit if the target doesn't exist, so the native resolve handler won't throw at us.
+ if (!targetInfo.Exists)
+ {
+ break;
+ }
+ }
+
+ return targetInfo;
+ }
+
+ /// <summary>
+ /// Gets the target of the specified file link.
+ /// </summary>
+ /// <remarks>
+ /// This helper exists because of this upstream runtime issue; https://github.com/dotnet/runtime/issues/92128.
+ /// </remarks>
+ /// <param name="fileInfo">The file info of the file link.</param>
+ /// <param name="returnFinalTarget">true to follow links to the final target; false to return the immediate next link.</param>
+ /// <returns>
+ /// A <see cref="FileInfo"/> if the <paramref name="fileInfo"/> is a link, regardless of if the target exists; otherwise, <c>null</c>.
+ /// </returns>
+ public static FileInfo? ResolveLinkTarget(FileInfo fileInfo, bool returnFinalTarget = false)
+ {
+ ArgumentNullException.ThrowIfNull(fileInfo);
+
+ return ResolveLinkTarget(fileInfo.FullName, returnFinalTarget);
+ }
}