Browse Source

optimize local library scanning (#112)

pull/113/head v0.0.25-prealpha
Jason Dove 5 years ago committed by GitHub
parent
commit
076a88230e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 14
      ErsatzTV.Application/MediaSources/Commands/ScanLocalLibraryHandler.cs
  2. 60
      ErsatzTV.Core.Tests/Metadata/MovieFolderScannerTests.cs
  3. 5
      ErsatzTV.Core/Interfaces/Metadata/IMovieFolderScanner.cs
  4. 5
      ErsatzTV.Core/Interfaces/Metadata/ITelevisionFolderScanner.cs
  5. 10
      ErsatzTV.Core/Metadata/MovieFolderScanner.cs
  6. 20
      ErsatzTV.Core/Metadata/TelevisionFolderScanner.cs

14
ErsatzTV.Application/MediaSources/Commands/ScanLocalLibraryHandler.cs

@ -1,4 +1,5 @@
using System; using System;
using System.Diagnostics;
using System.IO; using System.IO;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
@ -61,21 +62,30 @@ namespace ErsatzTV.Application.MediaSources.Commands
var lastScan = new DateTimeOffset(localLibrary.LastScan ?? DateTime.MinValue, TimeSpan.Zero); var lastScan = new DateTimeOffset(localLibrary.LastScan ?? DateTime.MinValue, TimeSpan.Zero);
if (forceScan || lastScan < DateTimeOffset.Now - TimeSpan.FromHours(6)) if (forceScan || lastScan < DateTimeOffset.Now - TimeSpan.FromHours(6))
{ {
var sw = new Stopwatch();
sw.Start();
foreach (LibraryPath libraryPath in localLibrary.Paths) foreach (LibraryPath libraryPath in localLibrary.Paths)
{ {
switch (localLibrary.MediaKind) switch (localLibrary.MediaKind)
{ {
case LibraryMediaKind.Movies: case LibraryMediaKind.Movies:
await _movieFolderScanner.ScanFolder(libraryPath, ffprobePath); await _movieFolderScanner.ScanFolder(libraryPath, ffprobePath, lastScan);
break; break;
case LibraryMediaKind.Shows: case LibraryMediaKind.Shows:
await _televisionFolderScanner.ScanFolder(libraryPath, ffprobePath); await _televisionFolderScanner.ScanFolder(libraryPath, ffprobePath, lastScan);
break; break;
} }
} }
localLibrary.LastScan = DateTime.UtcNow; localLibrary.LastScan = DateTime.UtcNow;
await _libraryRepository.UpdateLastScan(localLibrary); await _libraryRepository.UpdateLastScan(localLibrary);
sw.Stop();
_logger.LogDebug(
"Scan of library {Name} completed in {Duration}",
localLibrary.Name,
TimeSpan.FromMilliseconds(sw.ElapsedMilliseconds));
} }
else else
{ {

60
ErsatzTV.Core.Tests/Metadata/MovieFolderScannerTests.cs

@ -81,7 +81,10 @@ namespace ErsatzTV.Core.Tests.Metadata
); );
var libraryPath = new LibraryPath { Path = BadFakeRoot }; var libraryPath = new LibraryPath { Path = BadFakeRoot };
Either<BaseError, Unit> result = await service.ScanFolder(libraryPath, FFprobePath); Either<BaseError, Unit> result = await service.ScanFolder(
libraryPath,
FFprobePath,
DateTimeOffset.MinValue);
result.IsLeft.Should().BeTrue(); result.IsLeft.Should().BeTrue();
result.IfLeft(error => error.Should().BeOfType<MediaSourceInaccessible>()); result.IfLeft(error => error.Should().BeOfType<MediaSourceInaccessible>());
@ -101,7 +104,10 @@ namespace ErsatzTV.Core.Tests.Metadata
); );
var libraryPath = new LibraryPath { Id = 1, Path = FakeRoot }; var libraryPath = new LibraryPath { Id = 1, Path = FakeRoot };
Either<BaseError, Unit> result = await service.ScanFolder(libraryPath, FFprobePath); Either<BaseError, Unit> result = await service.ScanFolder(
libraryPath,
FFprobePath,
DateTimeOffset.MinValue);
result.IsRight.Should().BeTrue(); result.IsRight.Should().BeTrue();
@ -137,7 +143,10 @@ namespace ErsatzTV.Core.Tests.Metadata
); );
var libraryPath = new LibraryPath { Id = 1, Path = FakeRoot }; var libraryPath = new LibraryPath { Id = 1, Path = FakeRoot };
Either<BaseError, Unit> result = await service.ScanFolder(libraryPath, FFprobePath); Either<BaseError, Unit> result = await service.ScanFolder(
libraryPath,
FFprobePath,
DateTimeOffset.MinValue);
result.IsRight.Should().BeTrue(); result.IsRight.Should().BeTrue();
@ -174,7 +183,10 @@ namespace ErsatzTV.Core.Tests.Metadata
); );
var libraryPath = new LibraryPath { Id = 1, Path = FakeRoot }; var libraryPath = new LibraryPath { Id = 1, Path = FakeRoot };
Either<BaseError, Unit> result = await service.ScanFolder(libraryPath, FFprobePath); Either<BaseError, Unit> result = await service.ScanFolder(
libraryPath,
FFprobePath,
DateTimeOffset.MinValue);
result.IsRight.Should().BeTrue(); result.IsRight.Should().BeTrue();
@ -215,7 +227,10 @@ namespace ErsatzTV.Core.Tests.Metadata
); );
var libraryPath = new LibraryPath { Id = 1, Path = FakeRoot }; var libraryPath = new LibraryPath { Id = 1, Path = FakeRoot };
Either<BaseError, Unit> result = await service.ScanFolder(libraryPath, FFprobePath); Either<BaseError, Unit> result = await service.ScanFolder(
libraryPath,
FFprobePath,
DateTimeOffset.MinValue);
result.IsRight.Should().BeTrue(); result.IsRight.Should().BeTrue();
@ -259,7 +274,10 @@ namespace ErsatzTV.Core.Tests.Metadata
); );
var libraryPath = new LibraryPath { Id = 1, Path = FakeRoot }; var libraryPath = new LibraryPath { Id = 1, Path = FakeRoot };
Either<BaseError, Unit> result = await service.ScanFolder(libraryPath, FFprobePath); Either<BaseError, Unit> result = await service.ScanFolder(
libraryPath,
FFprobePath,
DateTimeOffset.MinValue);
result.IsRight.Should().BeTrue(); result.IsRight.Should().BeTrue();
@ -303,7 +321,10 @@ namespace ErsatzTV.Core.Tests.Metadata
); );
var libraryPath = new LibraryPath { Id = 1, Path = FakeRoot }; var libraryPath = new LibraryPath { Id = 1, Path = FakeRoot };
Either<BaseError, Unit> result = await service.ScanFolder(libraryPath, FFprobePath); Either<BaseError, Unit> result = await service.ScanFolder(
libraryPath,
FFprobePath,
DateTimeOffset.MinValue);
result.IsRight.Should().BeTrue(); result.IsRight.Should().BeTrue();
@ -346,7 +367,10 @@ namespace ErsatzTV.Core.Tests.Metadata
); );
var libraryPath = new LibraryPath { Id = 1, Path = FakeRoot }; var libraryPath = new LibraryPath { Id = 1, Path = FakeRoot };
Either<BaseError, Unit> result = await service.ScanFolder(libraryPath, FFprobePath); Either<BaseError, Unit> result = await service.ScanFolder(
libraryPath,
FFprobePath,
DateTimeOffset.MinValue);
result.IsRight.Should().BeTrue(); result.IsRight.Should().BeTrue();
@ -385,7 +409,10 @@ namespace ErsatzTV.Core.Tests.Metadata
); );
var libraryPath = new LibraryPath { Id = 1, Path = FakeRoot }; var libraryPath = new LibraryPath { Id = 1, Path = FakeRoot };
Either<BaseError, Unit> result = await service.ScanFolder(libraryPath, FFprobePath); Either<BaseError, Unit> result = await service.ScanFolder(
libraryPath,
FFprobePath,
DateTimeOffset.MinValue);
result.IsRight.Should().BeTrue(); result.IsRight.Should().BeTrue();
@ -418,7 +445,10 @@ namespace ErsatzTV.Core.Tests.Metadata
); );
var libraryPath = new LibraryPath { Id = 1, Path = FakeRoot }; var libraryPath = new LibraryPath { Id = 1, Path = FakeRoot };
Either<BaseError, Unit> result = await service.ScanFolder(libraryPath, FFprobePath); Either<BaseError, Unit> result = await service.ScanFolder(
libraryPath,
FFprobePath,
DateTimeOffset.MinValue);
result.IsRight.Should().BeTrue(); result.IsRight.Should().BeTrue();
@ -453,7 +483,10 @@ namespace ErsatzTV.Core.Tests.Metadata
); );
var libraryPath = new LibraryPath { Id = 1, Path = FakeRoot }; var libraryPath = new LibraryPath { Id = 1, Path = FakeRoot };
Either<BaseError, Unit> result = await service.ScanFolder(libraryPath, FFprobePath); Either<BaseError, Unit> result = await service.ScanFolder(
libraryPath,
FFprobePath,
DateTimeOffset.MinValue);
result.IsRight.Should().BeTrue(); result.IsRight.Should().BeTrue();
@ -477,7 +510,10 @@ namespace ErsatzTV.Core.Tests.Metadata
); );
var libraryPath = new LibraryPath { Id = 1, Path = FakeRoot }; var libraryPath = new LibraryPath { Id = 1, Path = FakeRoot };
Either<BaseError, Unit> result = await service.ScanFolder(libraryPath, FFprobePath); Either<BaseError, Unit> result = await service.ScanFolder(
libraryPath,
FFprobePath,
DateTimeOffset.MinValue);
result.IsRight.Should().BeTrue(); result.IsRight.Should().BeTrue();

5
ErsatzTV.Core/Interfaces/Metadata/IMovieFolderScanner.cs

@ -1,4 +1,5 @@
using System.Threading.Tasks; using System;
using System.Threading.Tasks;
using ErsatzTV.Core.Domain; using ErsatzTV.Core.Domain;
using LanguageExt; using LanguageExt;
@ -6,6 +7,6 @@ namespace ErsatzTV.Core.Interfaces.Metadata
{ {
public interface IMovieFolderScanner public interface IMovieFolderScanner
{ {
Task<Either<BaseError, Unit>> ScanFolder(LibraryPath libraryPath, string ffprobePath); Task<Either<BaseError, Unit>> ScanFolder(LibraryPath libraryPath, string ffprobePath, DateTimeOffset lastScan);
} }
} }

5
ErsatzTV.Core/Interfaces/Metadata/ITelevisionFolderScanner.cs

@ -1,4 +1,5 @@
using System.Threading.Tasks; using System;
using System.Threading.Tasks;
using ErsatzTV.Core.Domain; using ErsatzTV.Core.Domain;
using LanguageExt; using LanguageExt;
@ -6,6 +7,6 @@ namespace ErsatzTV.Core.Interfaces.Metadata
{ {
public interface ITelevisionFolderScanner public interface ITelevisionFolderScanner
{ {
Task<Either<BaseError, Unit>> ScanFolder(LibraryPath libraryPath, string ffprobePath); Task<Either<BaseError, Unit>> ScanFolder(LibraryPath libraryPath, string ffprobePath, DateTimeOffset lastScan);
} }
} }

10
ErsatzTV.Core/Metadata/MovieFolderScanner.cs

@ -42,7 +42,10 @@ namespace ErsatzTV.Core.Metadata
_logger = logger; _logger = logger;
} }
public async Task<Either<BaseError, Unit>> ScanFolder(LibraryPath libraryPath, string ffprobePath) public async Task<Either<BaseError, Unit>> ScanFolder(
LibraryPath libraryPath,
string ffprobePath,
DateTimeOffset lastScan)
{ {
if (!_localFileSystem.IsLibraryPathAccessible(libraryPath)) if (!_localFileSystem.IsLibraryPathAccessible(libraryPath))
{ {
@ -76,6 +79,11 @@ namespace ErsatzTV.Core.Metadata
continue; continue;
} }
if (_localFileSystem.GetLastWriteTime(movieFolder) < lastScan)
{
continue;
}
foreach (string file in allFiles.OrderBy(identity)) foreach (string file in allFiles.OrderBy(identity))
{ {
// TODO: figure out how to rebuild playlists // TODO: figure out how to rebuild playlists

20
ErsatzTV.Core/Metadata/TelevisionFolderScanner.cs

@ -45,7 +45,10 @@ namespace ErsatzTV.Core.Metadata
_logger = logger; _logger = logger;
} }
public async Task<Either<BaseError, Unit>> ScanFolder(LibraryPath libraryPath, string ffprobePath) public async Task<Either<BaseError, Unit>> ScanFolder(
LibraryPath libraryPath,
string ffprobePath,
DateTimeOffset lastScan)
{ {
if (!_localFileSystem.IsLibraryPathAccessible(libraryPath)) if (!_localFileSystem.IsLibraryPathAccessible(libraryPath))
{ {
@ -77,7 +80,7 @@ namespace ErsatzTV.Core.Metadata
await _searchIndex.UpdateItems(new List<MediaItem> { result.Item }); await _searchIndex.UpdateItems(new List<MediaItem> { result.Item });
} }
await ScanSeasons(libraryPath, ffprobePath, result.Item, showFolder); await ScanSeasons(libraryPath, ffprobePath, result.Item, showFolder, lastScan);
}, },
_ => Task.FromResult(Unit.Default)); _ => Task.FromResult(Unit.Default));
} }
@ -113,7 +116,8 @@ namespace ErsatzTV.Core.Metadata
LibraryPath libraryPath, LibraryPath libraryPath,
string ffprobePath, string ffprobePath,
Show show, Show show,
string showFolder) string showFolder,
DateTimeOffset lastScan)
{ {
foreach (string seasonFolder in _localFileSystem.ListSubdirectories(showFolder).Filter(ShouldIncludeFolder) foreach (string seasonFolder in _localFileSystem.ListSubdirectories(showFolder).Filter(ShouldIncludeFolder)
.OrderBy(identity)) .OrderBy(identity))
@ -127,7 +131,7 @@ namespace ErsatzTV.Core.Metadata
.BindT(season => UpdatePoster(season, seasonFolder)); .BindT(season => UpdatePoster(season, seasonFolder));
await maybeSeason.Match( await maybeSeason.Match(
season => ScanEpisodes(libraryPath, ffprobePath, season, seasonFolder), season => ScanEpisodes(libraryPath, ffprobePath, season, seasonFolder, lastScan),
_ => Task.FromResult(Unit.Default)); _ => Task.FromResult(Unit.Default));
}); });
} }
@ -139,8 +143,14 @@ namespace ErsatzTV.Core.Metadata
LibraryPath libraryPath, LibraryPath libraryPath,
string ffprobePath, string ffprobePath,
Season season, Season season,
string seasonPath) string seasonPath,
DateTimeOffset lastScan)
{ {
if (_localFileSystem.GetLastWriteTime(seasonPath) < lastScan)
{
return Unit.Default;
}
foreach (string file in _localFileSystem.ListFiles(seasonPath) foreach (string file in _localFileSystem.ListFiles(seasonPath)
.Filter(f => VideoFileExtensions.Contains(Path.GetExtension(f))).OrderBy(identity)) .Filter(f => VideoFileExtensions.Contains(Path.GetExtension(f))).OrderBy(identity))
{ {

Loading…
Cancel
Save