diff options
Diffstat (limited to 'Emby.Server.Implementations/Library')
8 files changed, 112 insertions, 65 deletions
diff --git a/Emby.Server.Implementations/Library/CoreResolutionIgnoreRule.cs b/Emby.Server.Implementations/Library/CoreResolutionIgnoreRule.cs index f29a0b3ad..f9538fbad 100644 --- a/Emby.Server.Implementations/Library/CoreResolutionIgnoreRule.cs +++ b/Emby.Server.Implementations/Library/CoreResolutionIgnoreRule.cs @@ -38,7 +38,8 @@ namespace Emby.Server.Implementations.Library } // Don't ignore top level folders - if (fileInfo.IsDirectory && parent is AggregateFolder) + if (fileInfo.IsDirectory + && (parent is AggregateFolder || (parent?.IsTopParent ?? false))) { return false; } @@ -48,35 +49,21 @@ namespace Emby.Server.Implementations.Library return true; } - var filename = fileInfo.Name; - - if (fileInfo.IsDirectory) + if (parent is null) { - if (parent is not null) - { - // Ignore extras for unsupported types - if (_namingOptions.AllExtrasTypesFolderNames.ContainsKey(filename) - && parent is not AggregateFolder - && parent is not UserRootFolder) - { - return true; - } - } + return false; } - else + + if (fileInfo.IsDirectory) { - if (parent is not null) - { - // Don't resolve theme songs - if (Path.GetFileNameWithoutExtension(filename.AsSpan()).Equals(BaseItem.ThemeSongFileName, StringComparison.Ordinal) - && AudioFileParser.IsAudioFile(filename, _namingOptions)) - { - return true; - } - } + // Ignore extras for unsupported types + return _namingOptions.AllExtrasTypesFolderNames.ContainsKey(fileInfo.Name) + && parent is not UserRootFolder; } - return false; + // Don't resolve theme songs + return Path.GetFileNameWithoutExtension(fileInfo.Name.AsSpan()).Equals(BaseItem.ThemeSongFileName, StringComparison.Ordinal) + && AudioFileParser.IsAudioFile(fileInfo.Name, _namingOptions); } } } diff --git a/Emby.Server.Implementations/Library/DotIgnoreIgnoreRule.cs b/Emby.Server.Implementations/Library/DotIgnoreIgnoreRule.cs index b0ed1de8d..401ca73b8 100644 --- a/Emby.Server.Implementations/Library/DotIgnoreIgnoreRule.cs +++ b/Emby.Server.Implementations/Library/DotIgnoreIgnoreRule.cs @@ -42,6 +42,19 @@ public class DotIgnoreIgnoreRule : IResolverIgnoreRule /// <returns>True if the file should be ignored.</returns> public static bool IsIgnored(FileSystemMetadata fileInfo, BaseItem? parent) { + if (fileInfo.IsDirectory) + { + var dirIgnoreFile = FindIgnoreFile(new DirectoryInfo(fileInfo.FullName)); + if (dirIgnoreFile is null) + { + return false; + } + + // ignore the directory only if the .ignore file is empty + // evaluate individual files otherwise + return string.IsNullOrWhiteSpace(GetFileContent(dirIgnoreFile)); + } + var parentDirPath = Path.GetDirectoryName(fileInfo.FullName); if (string.IsNullOrEmpty(parentDirPath)) { @@ -55,13 +68,9 @@ public class DotIgnoreIgnoreRule : IResolverIgnoreRule return false; } - string ignoreFileString; - using (var reader = ignoreFile.OpenText()) - { - ignoreFileString = reader.ReadToEnd(); - } + string ignoreFileString = GetFileContent(ignoreFile); - if (string.IsNullOrEmpty(ignoreFileString)) + if (string.IsNullOrWhiteSpace(ignoreFileString)) { // Ignore directory if we just have the file return true; @@ -74,4 +83,12 @@ public class DotIgnoreIgnoreRule : IResolverIgnoreRule return ignore.IsIgnored(fileInfo.FullName); } + + private static string GetFileContent(FileInfo dirIgnoreFile) + { + using (var reader = dirIgnoreFile.OpenText()) + { + return reader.ReadToEnd(); + } + } } diff --git a/Emby.Server.Implementations/Library/ExternalDataManager.cs b/Emby.Server.Implementations/Library/ExternalDataManager.cs index 68e3aaff4..d3cfa1d25 100644 --- a/Emby.Server.Implementations/Library/ExternalDataManager.cs +++ b/Emby.Server.Implementations/Library/ExternalDataManager.cs @@ -1,3 +1,4 @@ +using System; using System.IO; using System.Linq; using System.Threading; @@ -6,6 +7,7 @@ using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.IO; using MediaBrowser.Controller.MediaSegments; using MediaBrowser.Controller.Trickplay; +using Microsoft.Extensions.Logging; namespace Emby.Server.Implementations.Library; @@ -18,6 +20,7 @@ public class ExternalDataManager : IExternalDataManager private readonly IMediaSegmentManager _mediaSegmentManager; private readonly IPathManager _pathManager; private readonly ITrickplayManager _trickplayManager; + private readonly ILogger<ExternalDataManager> _logger; /// <summary> /// Initializes a new instance of the <see cref="ExternalDataManager"/> class. @@ -26,16 +29,19 @@ public class ExternalDataManager : IExternalDataManager /// <param name="mediaSegmentManager">The media segment manager.</param> /// <param name="pathManager">The path manager.</param> /// <param name="trickplayManager">The trickplay manager.</param> + /// <param name="logger">The logger.</param> public ExternalDataManager( IKeyframeManager keyframeManager, IMediaSegmentManager mediaSegmentManager, IPathManager pathManager, - ITrickplayManager trickplayManager) + ITrickplayManager trickplayManager, + ILogger<ExternalDataManager> logger) { _keyframeManager = keyframeManager; _mediaSegmentManager = mediaSegmentManager; _pathManager = pathManager; _trickplayManager = trickplayManager; + _logger = logger; } /// <inheritdoc/> @@ -47,7 +53,14 @@ public class ExternalDataManager : IExternalDataManager { foreach (var path in validPaths) { - Directory.Delete(path, true); + 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 d03c614cf..df71868b6 100644 --- a/Emby.Server.Implementations/Library/LibraryManager.cs +++ b/Emby.Server.Implementations/Library/LibraryManager.cs @@ -1954,7 +1954,7 @@ namespace Emby.Server.Implementations.Library try { - return _fileSystem.GetLastWriteTimeUtc(image.Path) != image.DateModified; + return image.DateModified.Subtract(_fileSystem.GetLastWriteTimeUtc(image.Path)).Duration().TotalSeconds > 1; } catch (Exception ex) { @@ -1981,6 +1981,8 @@ namespace Emby.Server.Implementations.Library return; } + var anyChange = false; + foreach (var img in outdated) { var image = img; @@ -2012,6 +2014,7 @@ namespace Emby.Server.Implementations.Library try { size = _imageProcessor.GetImageDimensions(item, image); + anyChange = image.Width != size.Width || image.Height != size.Height; image.Width = size.Width; image.Height = size.Height; } @@ -2019,23 +2022,29 @@ namespace Emby.Server.Implementations.Library { _logger.LogError(ex, "Cannot get image dimensions for {ImagePath}", image.Path); size = default; + anyChange = image.Width != size.Width || image.Height != size.Height; image.Width = 0; image.Height = 0; } try { - image.BlurHash = _imageProcessor.GetImageBlurHash(image.Path, size); + var blurhash = _imageProcessor.GetImageBlurHash(image.Path, size); + anyChange = anyChange || !blurhash.Equals(image.BlurHash, StringComparison.Ordinal); + image.BlurHash = blurhash; } catch (Exception ex) { _logger.LogError(ex, "Cannot compute blurhash for {ImagePath}", image.Path); + anyChange = anyChange || !string.IsNullOrEmpty(image.BlurHash); image.BlurHash = string.Empty; } try { - image.DateModified = _fileSystem.GetLastWriteTimeUtc(image.Path); + var modifiedDate = _fileSystem.GetLastWriteTimeUtc(image.Path); + anyChange = anyChange || modifiedDate != image.DateModified; + image.DateModified = modifiedDate; } catch (Exception ex) { @@ -2043,20 +2052,28 @@ namespace Emby.Server.Implementations.Library } } - _itemRepository.SaveImages(item); + if (anyChange) + { + _itemRepository.SaveImages(item); + } + RegisterItem(item); } /// <inheritdoc /> public async Task UpdateItemsAsync(IReadOnlyList<BaseItem> items, BaseItem parent, ItemUpdateType updateReason, CancellationToken cancellationToken) { - _itemRepository.SaveItems(items, cancellationToken); - foreach (var item in items) { + item.DateLastSaved = DateTime.UtcNow; await RunMetadataSavers(item, updateReason).ConfigureAwait(false); + + // Modify again, so saved value is after write time of externally saved metadata + item.DateLastSaved = DateTime.UtcNow; } + _itemRepository.SaveItems(items, cancellationToken); + if (ItemUpdated is not null) { foreach (var item in items) @@ -2097,8 +2114,6 @@ namespace Emby.Server.Implementations.Library await ProviderManager.SaveMetadataAsync(item, updateReason).ConfigureAwait(false); } - item.DateLastSaved = DateTime.UtcNow; - await UpdateImagesAsync(item, updateReason >= ItemUpdateType.ImageUpdate).ConfigureAwait(false); } @@ -2384,12 +2399,13 @@ namespace Emby.Server.Implementations.Library isNew = true; } - var refresh = isNew || DateTime.UtcNow - item.DateLastRefreshed >= _viewRefreshInterval; + var lastRefreshedUtc = item.DateLastRefreshed; + var refresh = isNew || DateTime.UtcNow - lastRefreshedUtc >= _viewRefreshInterval; if (!refresh && !item.DisplayParentId.IsEmpty()) { var displayParent = GetItemById(item.DisplayParentId); - refresh = displayParent is not null && displayParent.DateLastSaved > item.DateLastRefreshed; + refresh = displayParent is not null && displayParent.DateLastSaved > lastRefreshedUtc; } if (refresh) @@ -2447,12 +2463,13 @@ namespace Emby.Server.Implementations.Library isNew = true; } - var refresh = isNew || DateTime.UtcNow - item.DateLastRefreshed >= _viewRefreshInterval; + var lastRefreshedUtc = item.DateLastRefreshed; + var refresh = isNew || DateTime.UtcNow - lastRefreshedUtc >= _viewRefreshInterval; if (!refresh && !item.DisplayParentId.IsEmpty()) { var displayParent = GetItemById(item.DisplayParentId); - refresh = displayParent is not null && displayParent.DateLastSaved > item.DateLastRefreshed; + refresh = displayParent is not null && displayParent.DateLastSaved > lastRefreshedUtc; } if (refresh) @@ -2522,12 +2539,13 @@ namespace Emby.Server.Implementations.Library item.UpdateToRepositoryAsync(ItemUpdateType.MetadataEdit, CancellationToken.None).GetAwaiter().GetResult(); } - var refresh = isNew || DateTime.UtcNow - item.DateLastRefreshed >= _viewRefreshInterval; + var lastRefreshedUtc = item.DateLastRefreshed; + var refresh = isNew || DateTime.UtcNow - lastRefreshedUtc >= _viewRefreshInterval; if (!refresh && !item.DisplayParentId.IsEmpty()) { var displayParent = GetItemById(item.DisplayParentId); - refresh = displayParent is not null && displayParent.DateLastSaved > item.DateLastRefreshed; + refresh = displayParent is not null && displayParent.DateLastSaved > lastRefreshedUtc; } if (refresh) @@ -2987,21 +3005,28 @@ namespace Emby.Server.Implementations.Library if (personEntity is null) { - var path = Person.GetPath(person.Name); - var info = Directory.CreateDirectory(path); - var lastWriteTime = info.LastWriteTimeUtc; - personEntity = new Person() + try { - Name = person.Name, - Id = GetItemByNameId<Person>(path), - DateCreated = info.CreationTimeUtc, - DateModified = lastWriteTime, - Path = path - }; - - personEntity.PresentationUniqueKey = personEntity.CreatePresentationUniqueKey(); - saveEntity = true; - createEntity = true; + var path = Person.GetPath(person.Name); + var info = Directory.CreateDirectory(path); + personEntity = new Person() + { + Name = person.Name, + Id = GetItemByNameId<Person>(path), + DateCreated = info.CreationTimeUtc, + DateModified = info.LastWriteTimeUtc, + Path = path + }; + + personEntity.PresentationUniqueKey = personEntity.CreatePresentationUniqueKey(); + saveEntity = true; + createEntity = true; + } + catch (Exception ex) + { + _logger.LogWarning(ex, "Failed to create person {Name}", person.Name); + continue; + } } foreach (var id in person.ProviderIds) @@ -3035,6 +3060,8 @@ namespace Emby.Server.Implementations.Library } await RunMetadataSavers(personEntity, itemUpdateType).ConfigureAwait(false); + personEntity.DateLastSaved = DateTime.UtcNow; + CreateItems([personEntity], null, CancellationToken.None); } } diff --git a/Emby.Server.Implementations/Library/MediaSourceManager.cs b/Emby.Server.Implementations/Library/MediaSourceManager.cs index ab30971e2..1e3b8ea76 100644 --- a/Emby.Server.Implementations/Library/MediaSourceManager.cs +++ b/Emby.Server.Implementations/Library/MediaSourceManager.cs @@ -379,7 +379,7 @@ namespace Emby.Server.Implementations.Library var culture = _localizationManager.FindLanguageInfo(language); if (culture is not null) { - return culture.ThreeLetterISOLanguageNames; + return culture.Name.Contains('-', StringComparison.OrdinalIgnoreCase) ? [culture.Name] : culture.ThreeLetterISOLanguageNames; } return [language]; diff --git a/Emby.Server.Implementations/Library/ResolverHelper.cs b/Emby.Server.Implementations/Library/ResolverHelper.cs index ab6bc4907..06aa772bd 100644 --- a/Emby.Server.Implementations/Library/ResolverHelper.cs +++ b/Emby.Server.Implementations/Library/ResolverHelper.cs @@ -140,7 +140,7 @@ namespace Emby.Server.Implementations.Library if (fileCreationDate is not null) { var dateCreated = fileCreationDate; - if (dateCreated.Equals(DateTime.MinValue)) + if (dateCreated == DateTime.MinValue) { dateCreated = DateTime.UtcNow; } diff --git a/Emby.Server.Implementations/Library/Resolvers/Movies/MovieResolver.cs b/Emby.Server.Implementations/Library/Resolvers/Movies/MovieResolver.cs index d78f8b991..b2ceee97d 100644 --- a/Emby.Server.Implementations/Library/Resolvers/Movies/MovieResolver.cs +++ b/Emby.Server.Implementations/Library/Resolvers/Movies/MovieResolver.cs @@ -462,7 +462,11 @@ namespace Emby.Server.Implementations.Library.Resolvers.Movies { var movie = (T)result.Items[0]; movie.IsInMixedFolder = false; - movie.Name = Path.GetFileName(movie.ContainingFolderPath); + if (collectionType == CollectionType.movies || collectionType is null) + { + movie.Name = Path.GetFileName(movie.ContainingFolderPath); + } + return movie; } } diff --git a/Emby.Server.Implementations/Library/Validators/CollectionPostScanTask.cs b/Emby.Server.Implementations/Library/Validators/CollectionPostScanTask.cs index 38631e0de..e62c638ed 100644 --- a/Emby.Server.Implementations/Library/Validators/CollectionPostScanTask.cs +++ b/Emby.Server.Implementations/Library/Validators/CollectionPostScanTask.cs @@ -125,7 +125,6 @@ public class CollectionPostScanTask : ILibraryPostScanTask boxSet = await _collectionManager.CreateCollectionAsync(new CollectionCreationOptions { Name = collectionName, - IsLocked = true }).ConfigureAwait(false); await _collectionManager.AddToCollectionAsync(boxSet.Id, movieIds).ConfigureAwait(false); |
