Compare commits
1
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7d71fb9f94 |
+79
@@ -1,5 +1,6 @@
|
||||
using ErsatzTV.Core;
|
||||
using ErsatzTV.Core.Domain;
|
||||
using ErsatzTV.Core.Errors;
|
||||
using ErsatzTV.Core.Interfaces.Jellyfin;
|
||||
using ErsatzTV.Core.Interfaces.Repositories;
|
||||
using ErsatzTV.Core.Jellyfin;
|
||||
@@ -95,5 +96,83 @@ public class SynchronizeJellyfinLibraryByIdHandlerTests
|
||||
Arg.Any<CancellationToken>());
|
||||
await libraryRepository.Received(1).UpdateLastScan(library);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task Should_Not_Log_Error_When_Scan_Is_Canceled()
|
||||
{
|
||||
var scannerProxy = Substitute.For<IScannerProxy>();
|
||||
var mediaSourceRepository = Substitute.For<IMediaSourceRepository>();
|
||||
var jellyfinSecretStore = Substitute.For<IJellyfinSecretStore>();
|
||||
var jellyfinMovieLibraryScanner = Substitute.For<IJellyfinMovieLibraryScanner>();
|
||||
var jellyfinTelevisionLibraryScanner = Substitute.For<IJellyfinTelevisionLibraryScanner>();
|
||||
var jellyfinMusicVideoLibraryScanner = Substitute.For<IJellyfinMusicVideoLibraryScanner>();
|
||||
var libraryRepository = Substitute.For<ILibraryRepository>();
|
||||
var configElementRepository = Substitute.For<IConfigElementRepository>();
|
||||
var logger = Substitute.For<ILogger<SynchronizeJellyfinLibraryByIdHandler>>();
|
||||
|
||||
var library = new JellyfinLibrary
|
||||
{
|
||||
Id = 42,
|
||||
Name = "Concerts",
|
||||
MediaKind = LibraryMediaKind.MusicVideos,
|
||||
MediaSourceId = 7
|
||||
};
|
||||
var mediaSource = new JellyfinMediaSource
|
||||
{
|
||||
Id = 7,
|
||||
Connections =
|
||||
[
|
||||
new JellyfinConnection
|
||||
{
|
||||
Address = "http://jellyfin.example",
|
||||
JellyfinMediaSourceId = 7
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
mediaSourceRepository.GetJellyfinByLibraryId(library.Id).Returns(Some(mediaSource).AsTask());
|
||||
mediaSourceRepository.GetJellyfinLibrary(library.Id).Returns(Some(library).AsTask());
|
||||
jellyfinSecretStore.ReadSecrets().Returns(new JellyfinSecrets
|
||||
{
|
||||
Address = "http://jellyfin.example",
|
||||
ApiKey = "abc"
|
||||
});
|
||||
configElementRepository.GetValue<int>(
|
||||
Arg.Is<ConfigElementKey>(key => key.Key == ConfigElementKey.LibraryRefreshInterval.Key),
|
||||
Arg.Any<CancellationToken>())
|
||||
.Returns(Task.FromResult<Option<int>>(Some(0)));
|
||||
jellyfinMusicVideoLibraryScanner.ScanLibrary(
|
||||
Arg.Any<JellyfinConnectionParameters>(),
|
||||
library,
|
||||
true,
|
||||
Arg.Any<CancellationToken>())
|
||||
.Returns(Left<BaseError, Unit>(new ScanCanceled()).AsTask());
|
||||
|
||||
var handler = new SynchronizeJellyfinLibraryByIdHandler(
|
||||
scannerProxy,
|
||||
mediaSourceRepository,
|
||||
jellyfinSecretStore,
|
||||
jellyfinMovieLibraryScanner,
|
||||
jellyfinTelevisionLibraryScanner,
|
||||
jellyfinMusicVideoLibraryScanner,
|
||||
libraryRepository,
|
||||
configElementRepository,
|
||||
logger);
|
||||
|
||||
await handler.Handle(
|
||||
new SynchronizeJellyfinLibraryById("http://ersatztv.example", library.Id, true, true),
|
||||
CancellationToken.None);
|
||||
|
||||
// a user-initiated cancellation is not a failure and must not be logged at ERROR (#410)
|
||||
await libraryRepository.DidNotReceive().UpdateLastScan(Arg.Any<Library>());
|
||||
logger.ReceivedCalls()
|
||||
.Any(call => call.GetArguments().ElementAtOrDefault(2)?.ToString()
|
||||
== "Error synchronizing jellyfin library: Scan was canceled")
|
||||
.ShouldBeFalse();
|
||||
logger.ReceivedCalls()
|
||||
.Any(call => call.GetArguments().ElementAtOrDefault(2)?.ToString()
|
||||
== "Scan of jellyfin library Concerts was canceled")
|
||||
.ShouldBeTrue();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
using ErsatzTV.Core;
|
||||
using ErsatzTV.Core.Domain;
|
||||
using ErsatzTV.Core.Errors;
|
||||
using ErsatzTV.Core.Interfaces.Repositories;
|
||||
using ErsatzTV.Scanner.Application.MediaSources;
|
||||
using ErsatzTV.Scanner.Core.Interfaces;
|
||||
@@ -96,6 +97,27 @@ public class ScanLocalLibraryHandlerTests
|
||||
ShouldHaveLogged("Error scanning local library path /movies: scan failed");
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task Should_Not_Log_Error_When_A_Path_Scan_Is_Canceled()
|
||||
{
|
||||
ScanResult(Left<BaseError, Unit>(new ScanCanceled()));
|
||||
|
||||
await Handler().Handle(
|
||||
new ScanLocalLibrary("http://ersatztv.example", _library.Id, true),
|
||||
CancellationToken.None);
|
||||
|
||||
// a user-initiated cancellation is not a failure and must not be logged at ERROR (#410);
|
||||
// it's still correctly excluded from the "last scan" stamp, same as any other failure
|
||||
_library.LastScan.ShouldBeNull();
|
||||
await _libraryRepository.DidNotReceive().UpdateLastScan(Arg.Any<Library>());
|
||||
|
||||
_logger.ReceivedCalls()
|
||||
.Any(call => call.GetArguments().ElementAtOrDefault(2)?.ToString()
|
||||
== "Error scanning local library path /movies: Scan was canceled")
|
||||
.ShouldBeFalse();
|
||||
ShouldHaveLogged("Scan of local library path /movies was canceled");
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task Should_Not_Set_Library_LastScan_When_A_Later_Path_Fails()
|
||||
{
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
using ErsatzTV.Core;
|
||||
using ErsatzTV.Core;
|
||||
using ErsatzTV.Core.Domain;
|
||||
using ErsatzTV.Core.Emby;
|
||||
using ErsatzTV.Core.Errors;
|
||||
using ErsatzTV.Core.Interfaces.Emby;
|
||||
using ErsatzTV.Core.Interfaces.Repositories;
|
||||
using ErsatzTV.Scanner.Core.Interfaces;
|
||||
@@ -86,7 +87,14 @@ public class SynchronizeEmbyLibraryByIdHandler : IRequestHandler<SynchronizeEmby
|
||||
|
||||
foreach (BaseError error in result.LeftToSeq())
|
||||
{
|
||||
_logger.LogError("Error synchronizing emby library: {Error}", error);
|
||||
if (error is ScanCanceled)
|
||||
{
|
||||
_logger.LogInformation("Scan of emby library {Name} was canceled", parameters.Library.Name);
|
||||
}
|
||||
else
|
||||
{
|
||||
_logger.LogError("Error synchronizing emby library: {Error}", error);
|
||||
}
|
||||
}
|
||||
|
||||
return result.Map(_ => parameters.Library.Name);
|
||||
|
||||
+12
-2
@@ -1,5 +1,6 @@
|
||||
using ErsatzTV.Core;
|
||||
using ErsatzTV.Core;
|
||||
using ErsatzTV.Core.Domain;
|
||||
using ErsatzTV.Core.Errors;
|
||||
using ErsatzTV.Core.Interfaces.Jellyfin;
|
||||
using ErsatzTV.Core.Interfaces.Repositories;
|
||||
using ErsatzTV.Core.Jellyfin;
|
||||
@@ -95,7 +96,16 @@ public class
|
||||
|
||||
foreach (BaseError error in result.LeftToSeq())
|
||||
{
|
||||
_logger.LogError("Error synchronizing jellyfin library: {Error}", error);
|
||||
if (error is ScanCanceled)
|
||||
{
|
||||
_logger.LogInformation(
|
||||
"Scan of jellyfin library {Name} was canceled",
|
||||
parameters.Library.Name);
|
||||
}
|
||||
else
|
||||
{
|
||||
_logger.LogError("Error synchronizing jellyfin library: {Error}", error);
|
||||
}
|
||||
}
|
||||
|
||||
return result.Map(_ => parameters.Library.Name);
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
using System.Diagnostics;
|
||||
using ErsatzTV.Core;
|
||||
using ErsatzTV.Core.Domain;
|
||||
using ErsatzTV.Core.Errors;
|
||||
using ErsatzTV.Core.Interfaces.Repositories;
|
||||
using ErsatzTV.Scanner.Core.Interfaces;
|
||||
using ErsatzTV.Scanner.Core.Interfaces.Metadata;
|
||||
@@ -152,13 +153,23 @@ public class ScanLocalLibraryHandler : IRequestHandler<ScanLocalLibrary, Either<
|
||||
|
||||
// a failed path now suppresses the library-level scan time below, so without this the
|
||||
// user sees "Never scanned" with nothing explaining why. The remote scanners log the
|
||||
// same way.
|
||||
// same way. A cancellation is user-initiated, not a failure, so it's logged separately
|
||||
// at a lower level; genuine errors still log at ERROR.
|
||||
foreach (BaseError error in result.LeftToSeq())
|
||||
{
|
||||
_logger.LogError(
|
||||
"Error scanning local library path {Path}: {Error}",
|
||||
libraryPath.Path,
|
||||
error);
|
||||
if (error is ScanCanceled)
|
||||
{
|
||||
_logger.LogInformation(
|
||||
"Scan of local library path {Path} was canceled",
|
||||
libraryPath.Path);
|
||||
}
|
||||
else
|
||||
{
|
||||
_logger.LogError(
|
||||
"Error scanning local library path {Path}: {Error}",
|
||||
libraryPath.Path,
|
||||
error);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
using ErsatzTV.Core;
|
||||
using ErsatzTV.Core.Domain;
|
||||
using ErsatzTV.Core.Errors;
|
||||
using ErsatzTV.Core.Interfaces.Plex;
|
||||
using ErsatzTV.Core.Interfaces.Repositories;
|
||||
using ErsatzTV.Core.Plex;
|
||||
@@ -96,7 +97,14 @@ public class SynchronizePlexLibraryByIdHandler : IRequestHandler<SynchronizePlex
|
||||
|
||||
foreach (BaseError error in result.LeftToSeq())
|
||||
{
|
||||
_logger.LogError("Error synchronizing plex library: {Error}", error);
|
||||
if (error is ScanCanceled)
|
||||
{
|
||||
_logger.LogInformation("Scan of plex library {Name} was canceled", parameters.Library.Name);
|
||||
}
|
||||
else
|
||||
{
|
||||
_logger.LogError("Error synchronizing plex library: {Error}", error);
|
||||
}
|
||||
}
|
||||
|
||||
return result.Map(_ => parameters.Library.Name);
|
||||
|
||||
Reference in New Issue
Block a user