diff options
Diffstat (limited to 'Emby.Server.Implementations/Library')
4 files changed, 110 insertions, 37 deletions
diff --git a/Emby.Server.Implementations/Library/ExternalDataManager.cs b/Emby.Server.Implementations/Library/ExternalDataManager.cs index 4ad0f999bf..2c18e56df7 100644 --- a/Emby.Server.Implementations/Library/ExternalDataManager.cs +++ b/Emby.Server.Implementations/Library/ExternalDataManager.cs @@ -1,6 +1,5 @@ using System; using System.IO; -using System.Linq; using System.Threading; using System.Threading.Tasks; using MediaBrowser.Controller.Chapters; @@ -52,26 +51,33 @@ public class ExternalDataManager : IExternalDataManager /// <inheritdoc/> public async Task DeleteExternalItemDataAsync(BaseItem item, CancellationToken cancellationToken) { - var validPaths = _pathManager.GetExtractedDataPaths(item).Where(Directory.Exists).ToList(); - var itemId = item.Id; - if (validPaths.Count > 0) - { - foreach (var path in validPaths) - { - try - { - Directory.Delete(path, true); - } - catch (Exception ex) - { - _logger.LogWarning("Unable to prune external item data at {Path}: {Exception}", path, ex); - } - } - } + DeleteExternalItemFiles(item); + var itemId = item.Id; await _keyframeManager.DeleteKeyframeDataAsync(itemId, cancellationToken).ConfigureAwait(false); await _mediaSegmentManager.DeleteSegmentsAsync(itemId, cancellationToken).ConfigureAwait(false); await _trickplayManager.DeleteTrickplayDataAsync(itemId, cancellationToken).ConfigureAwait(false); await _chapterManager.DeleteChapterDataAsync(itemId, cancellationToken).ConfigureAwait(false); } + + /// <inheritdoc/> + public void DeleteExternalItemFiles(BaseItem item) + { + foreach (var path in _pathManager.GetExtractedDataPaths(item)) + { + if (!Directory.Exists(path)) + { + continue; + } + + try + { + Directory.Delete(path, true); + } + catch (Exception ex) + { + _logger.LogWarning("Unable to prune external item data at {Path}: {Exception}", path, ex); + } + } + } } diff --git a/Emby.Server.Implementations/Library/LibraryManager.cs b/Emby.Server.Implementations/Library/LibraryManager.cs index a826db090f..3691f4e19d 100644 --- a/Emby.Server.Implementations/Library/LibraryManager.cs +++ b/Emby.Server.Implementations/Library/LibraryManager.cs @@ -89,6 +89,7 @@ namespace Emby.Server.Implementations.Library private readonly FastConcurrentLru<Guid, BaseItem> _cache; private readonly DotIgnoreIgnoreRule _dotIgnoreIgnoreRule; private readonly IMediaStreamRepository _mediaStreamRepository; + private readonly Lazy<IExternalDataManager> _externalDataManagerFactory; /// <summary> /// The _root folder sync lock. @@ -132,6 +133,7 @@ namespace Emby.Server.Implementations.Library /// <param name="pathManager">The path manager.</param> /// <param name="dotIgnoreIgnoreRule">The .ignore rule handler.</param> /// <param name="mediaStreamRepository">The media stream repository.</param> + /// <param name="externalDataManagerFactory">The external data manager (lazy, to break the DI cycle through ChapterManager).</param> public LibraryManager( IServerApplicationHost appHost, ILoggerFactory loggerFactory, @@ -155,7 +157,8 @@ namespace Emby.Server.Implementations.Library IPeopleRepository peopleRepository, IPathManager pathManager, DotIgnoreIgnoreRule dotIgnoreIgnoreRule, - IMediaStreamRepository mediaStreamRepository) + IMediaStreamRepository mediaStreamRepository, + Lazy<IExternalDataManager> externalDataManagerFactory) { _appHost = appHost; _logger = loggerFactory.CreateLogger<LibraryManager>(); @@ -186,6 +189,7 @@ namespace Emby.Server.Implementations.Library _configurationManager.ConfigurationUpdated += ConfigurationUpdated; _mediaStreamRepository = mediaStreamRepository; + _externalDataManagerFactory = externalDataManagerFactory; RecordConfigurationValues(_configurationManager.Configuration); } @@ -396,6 +400,12 @@ namespace Emby.Server.Implementations.Library } } + var externalDataManager = _externalDataManagerFactory.Value; + foreach (var (item, _, _) in pathMaps) + { + externalDataManager.DeleteExternalItemFiles(item); + } + _persistenceService.DeleteItem([.. pathMaps.Select(f => f.Item.Id)]); } @@ -576,6 +586,13 @@ namespace Emby.Server.Implementations.Library item.SetParent(null); + var externalDataManager = _externalDataManagerFactory.Value; + externalDataManager.DeleteExternalItemFiles(item); + foreach (var child in children) + { + externalDataManager.DeleteExternalItemFiles(child); + } + _persistenceService.DeleteItem([item.Id, .. children.Select(f => f.Id)]); _cache.TryRemove(item.Id, out _); foreach (var child in children) @@ -1987,7 +2004,8 @@ namespace Emby.Server.Implementations.Library query.TopParentIds.Length == 0 && string.IsNullOrEmpty(query.AncestorWithPresentationUniqueKey) && string.IsNullOrEmpty(query.SeriesPresentationUniqueKey) && - query.ItemIds.Length == 0) + query.ItemIds.Length == 0 && + query.OwnerIds.Length == 0) { var userViews = UserViewManager.GetUserViews(new UserViewQuery { @@ -2432,8 +2450,14 @@ namespace Emby.Server.Implementations.Library var outdated = forceUpdate ? item.ImageInfos.Where(i => i.Path is not null).ToArray() : item.ImageInfos.Where(ImageNeedsRefresh).ToArray(); - // Skip image processing if current or live tv source - if (outdated.Length == 0 || item.SourceType != SourceType.Library) + + var parentItem = item.GetParent(); + var isLiveTvShow = item.SourceType != SourceType.Library && + parentItem is not null && + parentItem.SourceType != SourceType.Library; // not a channel + + // Skip image processing if current or live tv show + if (outdated.Length == 0 || isLiveTvShow) { RegisterItem(item); return; diff --git a/Emby.Server.Implementations/Library/MediaSourceManager.cs b/Emby.Server.Implementations/Library/MediaSourceManager.cs index 0caf66555a..c369fb0957 100644 --- a/Emby.Server.Implementations/Library/MediaSourceManager.cs +++ b/Emby.Server.Implementations/Library/MediaSourceManager.cs @@ -24,6 +24,7 @@ using MediaBrowser.Common.Extensions; using MediaBrowser.Controller; using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Entities.TV; +using MediaBrowser.Controller.IO; using MediaBrowser.Controller.Library; using MediaBrowser.Controller.LiveTv; using MediaBrowser.Controller.MediaEncoding; @@ -176,6 +177,7 @@ namespace Emby.Server.Implementations.Library public async Task<IReadOnlyList<MediaSourceInfo>> GetPlaybackMediaSources(BaseItem item, User user, bool allowMediaProbe, bool enablePathSubstitution, CancellationToken cancellationToken) { var mediaSources = GetStaticMediaSources(item, enablePathSubstitution, user); + ResolveSymlinkPaths(mediaSources, enablePathSubstitution); // If file is strm or main media stream is missing, force a metadata refresh with remote probing if (allowMediaProbe && mediaSources[0].Type != MediaSourceType.Placeholder @@ -192,6 +194,7 @@ namespace Emby.Server.Implementations.Library cancellationToken).ConfigureAwait(false); mediaSources = GetStaticMediaSources(item, enablePathSubstitution, user); + ResolveSymlinkPaths(mediaSources, enablePathSubstitution); } var dynamicMediaSources = await GetDynamicMediaSources(item, cancellationToken).ConfigureAwait(false); @@ -226,7 +229,7 @@ namespace Emby.Server.Implementations.Library list.Add(source); } - return SortMediaSources(list).ToArray(); + return SortMediaSources(list, item.Id).ToArray(); } /// <inheritdoc />> @@ -324,6 +327,28 @@ namespace Emby.Server.Implementations.Library } } + /// <summary> + /// Resolves symlinked file paths on the supplied sources to the real on-disk target. + /// Skipped when <paramref name="enablePathSubstitution"/> is set because the path may + /// already have been rewritten to a UNC/URL meant for the client to consume directly. + /// </summary> + private static void ResolveSymlinkPaths(IReadOnlyList<MediaSourceInfo> sources, bool enablePathSubstitution) + { + if (enablePathSubstitution) + { + return; + } + + foreach (var source in sources) + { + if (source.Protocol == MediaProtocol.File + && FileSystemHelper.ResolveLinkTarget(source.Path, returnFinalTarget: true) is { Exists: true } target) + { + source.Path = target.FullName; + } + } + } + private static void SetKeyProperties(IMediaSourceProvider provider, MediaSourceInfo mediaSource) { var prefix = provider.GetType().FullName.GetMD5().ToString("N", CultureInfo.InvariantCulture) + LiveStreamIdDelimiter; @@ -361,6 +386,12 @@ namespace Emby.Server.Implementations.Library if (user is not null) { + sources = sources + .Where(source => !Guid.TryParse(source.Id, out var sourceId) + || sourceId.Equals(item.Id) + || _libraryManager.GetItemById<BaseItem>(sourceId, user) is not null) + .ToArray(); + foreach (var source in sources) { SetDefaultAudioAndSubtitleStreamIndices(item, source, user); @@ -515,24 +546,32 @@ namespace Emby.Server.Implementations.Library } } - private static IEnumerable<MediaSourceInfo> SortMediaSources(IEnumerable<MediaSourceInfo> sources) + private static IEnumerable<MediaSourceInfo> SortMediaSources(IEnumerable<MediaSourceInfo> sources, Guid preferredItemId = default) { - return sources.OrderBy(i => - { - if (i.VideoType.HasValue && i.VideoType.Value == VideoType.VideoFile) + // The source belonging to the queried item sorts first so it stays the default that gets played. + var preferredId = preferredItemId.IsEmpty() + ? null + : preferredItemId.ToString("N", CultureInfo.InvariantCulture); + + return sources + .OrderByDescending(i => preferredId is not null && string.Equals(i.Id, preferredId, StringComparison.OrdinalIgnoreCase)) + .ThenBy(i => { - return 0; - } + if (i.VideoType.HasValue && i.VideoType.Value == VideoType.VideoFile) + { + return 0; + } - return 1; - }).ThenBy(i => i.Video3DFormat.HasValue ? 1 : 0) - .ThenByDescending(i => - { - var stream = i.VideoStream; + return 1; + }) + .ThenBy(i => i.Video3DFormat.HasValue ? 1 : 0) + .ThenByDescending(i => + { + var stream = i.VideoStream; - return stream?.Width ?? 0; - }) - .Where(i => i.Type != MediaSourceType.Placeholder); + return stream?.Width ?? 0; + }) + .Where(i => i.Type != MediaSourceType.Placeholder); } public async Task<Tuple<LiveStreamResponse, IDirectStreamProvider>> OpenLiveStreamInternal(LiveStreamRequest request, CancellationToken cancellationToken) diff --git a/Emby.Server.Implementations/Library/PathManager.cs b/Emby.Server.Implementations/Library/PathManager.cs index ef5edb9afa..fad948ad97 100644 --- a/Emby.Server.Implementations/Library/PathManager.cs +++ b/Emby.Server.Implementations/Library/PathManager.cs @@ -121,7 +121,11 @@ public class PathManager : IPathManager } paths.Add(GetTrickplayDirectory(item, false)); - paths.Add(GetTrickplayDirectory(item, true)); + if (!string.IsNullOrEmpty(item.Path)) + { + paths.Add(GetTrickplayDirectory(item, true)); + } + paths.Add(GetChapterImageFolderPath(item)); return paths; |
