diff --git a/CHANGELOG.md b/CHANGELOG.md index 3e7dcc0ca..a24591918 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,8 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). ## [Unreleased] +### Fixed +- Fix all local library scanners to ignore dot underscore files (`._`) ## [0.0.54-alpha] - 2021-08-21 ### Added diff --git a/ErsatzTV.Core.Tests/Metadata/MovieFolderScannerTests.cs b/ErsatzTV.Core.Tests/Metadata/MovieFolderScannerTests.cs index a513de347..0fd24f524 100644 --- a/ErsatzTV.Core.Tests/Metadata/MovieFolderScannerTests.cs +++ b/ErsatzTV.Core.Tests/Metadata/MovieFolderScannerTests.cs @@ -405,6 +405,48 @@ namespace ErsatzTV.Core.Tests.Metadata It.Is(i => i.MediaVersions.Head().MediaFiles.Head().Path == moviePath)), Times.Once); } + + [Test] + public async Task Should_Ignore_Dot_Underscore_Files( + [ValueSource(typeof(LocalFolderScanner), nameof(LocalFolderScanner.VideoFileExtensions))] + string videoExtension) + { + string moviePath = Path.Combine( + FakeRoot, + Path.Combine("Movie (2020)", $"Movie (2020){videoExtension}")); + + MovieFolderScanner service = GetService( + new FakeFileEntry(moviePath) { LastWriteTime = DateTime.Now }, + new FakeFileEntry( + Path.Combine( + Path.GetDirectoryName(moviePath) ?? string.Empty, + $"._Movie (2020){videoExtension}")) + ); + var libraryPath = new LibraryPath + { Id = 1, Path = FakeRoot, LibraryFolders = new List() }; + + Either result = await service.ScanFolder( + libraryPath, + FFprobePath, + 0, + 1); + + result.IsRight.Should().BeTrue(); + + _movieRepository.Verify(x => x.GetOrAdd(It.IsAny(), It.IsAny()), Times.Once); + _movieRepository.Verify(x => x.GetOrAdd(libraryPath, moviePath), Times.Once); + + _localStatisticsProvider.Verify( + x => x.RefreshStatistics( + FFprobePath, + It.Is(i => i.MediaVersions.Head().MediaFiles.Head().Path == moviePath)), + Times.Once); + + _localMetadataProvider.Verify( + x => x.RefreshFallbackMetadata( + It.Is(i => i.MediaVersions.Head().MediaFiles.Head().Path == moviePath)), + Times.Once); + } [Test] public async Task Should_Ignore_Extra_Folders( diff --git a/ErsatzTV.Core/Metadata/MovieFolderScanner.cs b/ErsatzTV.Core/Metadata/MovieFolderScanner.cs index c319b5b25..e7f0266d2 100644 --- a/ErsatzTV.Core/Metadata/MovieFolderScanner.cs +++ b/ErsatzTV.Core/Metadata/MovieFolderScanner.cs @@ -87,6 +87,7 @@ namespace ErsatzTV.Core.Metadata var allFiles = filesForEtag .Filter(f => VideoFileExtensions.Contains(Path.GetExtension(f))) + .Filter(f => !Path.GetFileName(f).StartsWith("._")) .Filter( f => !ExtraFiles.Any( e => Path.GetFileNameWithoutExtension(f).EndsWith(e, StringComparison.OrdinalIgnoreCase))) @@ -157,6 +158,12 @@ namespace ErsatzTV.Core.Metadata List ids = await _movieRepository.DeleteByPath(libraryPath, path); await _searchIndex.RemoveItems(ids); } + else if (Path.GetFileName(path).StartsWith("._")) + { + _logger.LogInformation("Removing dot underscore file at {Path}", path); + List ids = await _movieRepository.DeleteByPath(libraryPath, path); + await _searchIndex.RemoveItems(ids); + } } _searchIndex.Commit(); diff --git a/ErsatzTV.Core/Metadata/MusicVideoFolderScanner.cs b/ErsatzTV.Core/Metadata/MusicVideoFolderScanner.cs index 7a25ec239..defca8659 100644 --- a/ErsatzTV.Core/Metadata/MusicVideoFolderScanner.cs +++ b/ErsatzTV.Core/Metadata/MusicVideoFolderScanner.cs @@ -134,6 +134,12 @@ namespace ErsatzTV.Core.Metadata List musicVideoIds = await _musicVideoRepository.DeleteByPath(libraryPath, path); await _searchIndex.RemoveItems(musicVideoIds); } + else if (Path.GetFileName(path).StartsWith("._")) + { + _logger.LogInformation("Removing dot underscore file at {Path}", path); + List musicVideoIds = await _musicVideoRepository.DeleteByPath(libraryPath, path); + await _searchIndex.RemoveItems(musicVideoIds); + } } List artistIds = await _artistRepository.DeleteEmptyArtists(libraryPath); @@ -238,6 +244,7 @@ namespace ErsatzTV.Core.Metadata var allFiles = _localFileSystem.ListFiles(musicVideoFolder) .Filter(f => VideoFileExtensions.Contains(Path.GetExtension(f))) + .Filter(f => !Path.GetFileName(f).StartsWith("._")) .ToList(); foreach (string subdirectory in _localFileSystem.ListSubdirectories(musicVideoFolder) diff --git a/ErsatzTV.Core/Metadata/TelevisionFolderScanner.cs b/ErsatzTV.Core/Metadata/TelevisionFolderScanner.cs index 1d98069a5..9f7858c6d 100644 --- a/ErsatzTV.Core/Metadata/TelevisionFolderScanner.cs +++ b/ErsatzTV.Core/Metadata/TelevisionFolderScanner.cs @@ -124,6 +124,11 @@ namespace ErsatzTV.Core.Metadata _logger.LogInformation("Removing missing episode at {Path}", path); await _televisionRepository.DeleteByPath(libraryPath, path); } + else if (Path.GetFileName(path).StartsWith("._")) + { + _logger.LogInformation("Removing dot underscore file at {Path}", path); + await _televisionRepository.DeleteByPath(libraryPath, path); + } } await _televisionRepository.DeleteEmptySeasons(libraryPath); @@ -201,7 +206,9 @@ namespace ErsatzTV.Core.Metadata string seasonPath) { foreach (string file in _localFileSystem.ListFiles(seasonPath) - .Filter(f => VideoFileExtensions.Contains(Path.GetExtension(f))).OrderBy(identity)) + .Filter(f => VideoFileExtensions.Contains(Path.GetExtension(f))) + .Filter(f => !Path.GetFileName(f).StartsWith("._")) + .OrderBy(identity)) { // TODO: figure out how to rebuild playlists Either maybeEpisode = await _televisionRepository