Browse Source

fix errors; add nouveau vaapi driver option (#692)

* fix service shutdown errors

* add nouveau vaapi driver option
pull/696/head
Jason Dove 4 years ago committed by GitHub
parent
commit
6be5111195
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 1
      CHANGELOG.md
  2. 2
      ErsatzTV.Core/FFmpeg/FFmpegLibraryProcessService.cs
  3. 3
      ErsatzTV.Core/FFmpeg/FFmpegProcessBuilder.cs
  4. 3
      ErsatzTV.Core/FFmpeg/VaapiDriver.cs
  5. 89
      ErsatzTV/Services/EmbyService.cs
  6. 53
      ErsatzTV/Services/FFmpegWorkerService.cs
  7. 89
      ErsatzTV/Services/JellyfinService.cs
  8. 89
      ErsatzTV/Services/PlexService.cs
  9. 10
      ErsatzTV/Services/SchedulerService.cs
  10. 128
      ErsatzTV/Services/WorkerService.cs

1
CHANGELOG.md

@ -10,6 +10,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). @@ -10,6 +10,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
### Added
- Perform additional duration analysis on files with missing duration metadata
- Add `nouveau` VAAPI driver option
## [0.4.3-alpha] - 2022-03-05
### Fixed

2
ErsatzTV.Core/FFmpeg/FFmpegLibraryProcessService.cs

@ -391,6 +391,8 @@ public class FFmpegLibraryProcessService : IFFmpegProcessService @@ -391,6 +391,8 @@ public class FFmpegLibraryProcessService : IFFmpegProcessService
return "iHD";
case VaapiDriver.RadeonSI:
return "radeonsi";
case VaapiDriver.Nouveau:
return "nouveau";
}
}

3
ErsatzTV.Core/FFmpeg/FFmpegProcessBuilder.cs

@ -682,6 +682,9 @@ internal class FFmpegProcessBuilder @@ -682,6 +682,9 @@ internal class FFmpegProcessBuilder
case VaapiDriver.RadeonSI:
startInfo.EnvironmentVariables["LIBVA_DRIVER_NAME"] = "radeonsi";
break;
case VaapiDriver.Nouveau:
startInfo.EnvironmentVariables["LIBVA_DRIVER_NAME"] = "nouveau";
break;
}
}

3
ErsatzTV.Core/FFmpeg/VaapiDriver.cs

@ -8,5 +8,6 @@ public enum VaapiDriver @@ -8,5 +8,6 @@ public enum VaapiDriver
Default = 0,
iHD = 1,
i965 = 2,
RadeonSI = 3
RadeonSI = 3,
Nouveau = 4
}

89
ErsatzTV/Services/EmbyService.cs

@ -26,61 +26,68 @@ public class EmbyService : BackgroundService @@ -26,61 +26,68 @@ public class EmbyService : BackgroundService
protected override async Task ExecuteAsync(CancellationToken cancellationToken)
{
if (!File.Exists(FileSystemLayout.EmbySecretsPath))
try
{
await File.WriteAllTextAsync(FileSystemLayout.EmbySecretsPath, "{}", cancellationToken);
}
if (!File.Exists(FileSystemLayout.EmbySecretsPath))
{
await File.WriteAllTextAsync(FileSystemLayout.EmbySecretsPath, "{}", cancellationToken);
}
_logger.LogInformation(
"Emby service started; secrets are at {EmbySecretsPath}",
FileSystemLayout.EmbySecretsPath);
_logger.LogInformation(
"Emby service started; secrets are at {EmbySecretsPath}",
FileSystemLayout.EmbySecretsPath);
// synchronize sources on startup
await SynchronizeSources(new SynchronizeEmbyMediaSources(), cancellationToken);
// synchronize sources on startup
await SynchronizeSources(new SynchronizeEmbyMediaSources(), cancellationToken);
await foreach (IEmbyBackgroundServiceRequest request in _channel.ReadAllAsync(cancellationToken))
{
try
{
Task requestTask;
switch (request)
{
case SynchronizeEmbyMediaSources synchronizeEmbyMediaSources:
requestTask = SynchronizeSources(synchronizeEmbyMediaSources, cancellationToken);
break;
// case SynchronizeEmbyAdminUserId synchronizeEmbyAdminUserId:
// requestTask = SynchronizeAdminUserId(synchronizeEmbyAdminUserId, cancellationToken);
// break;
case SynchronizeEmbyLibraries synchronizeEmbyLibraries:
requestTask = SynchronizeLibraries(synchronizeEmbyLibraries, cancellationToken);
break;
case ISynchronizeEmbyLibraryById synchronizeEmbyLibraryById:
requestTask = SynchronizeEmbyLibrary(synchronizeEmbyLibraryById, cancellationToken);
break;
default:
throw new NotSupportedException($"Unsupported request type: {request.GetType().Name}");
}
await requestTask;
}
catch (Exception ex)
await foreach (IEmbyBackgroundServiceRequest request in _channel.ReadAllAsync(cancellationToken))
{
_logger.LogWarning(ex, "Failed to process Emby background service request");
try
{
using (IServiceScope scope = _serviceScopeFactory.CreateScope())
Task requestTask;
switch (request)
{
IClient client = scope.ServiceProvider.GetRequiredService<IClient>();
client.Notify(ex);
case SynchronizeEmbyMediaSources synchronizeEmbyMediaSources:
requestTask = SynchronizeSources(synchronizeEmbyMediaSources, cancellationToken);
break;
// case SynchronizeEmbyAdminUserId synchronizeEmbyAdminUserId:
// requestTask = SynchronizeAdminUserId(synchronizeEmbyAdminUserId, cancellationToken);
// break;
case SynchronizeEmbyLibraries synchronizeEmbyLibraries:
requestTask = SynchronizeLibraries(synchronizeEmbyLibraries, cancellationToken);
break;
case ISynchronizeEmbyLibraryById synchronizeEmbyLibraryById:
requestTask = SynchronizeEmbyLibrary(synchronizeEmbyLibraryById, cancellationToken);
break;
default:
throw new NotSupportedException($"Unsupported request type: {request.GetType().Name}");
}
await requestTask;
}
catch (Exception)
catch (Exception ex)
{
// do nothing
_logger.LogWarning(ex, "Failed to process Emby background service request");
try
{
using (IServiceScope scope = _serviceScopeFactory.CreateScope())
{
IClient client = scope.ServiceProvider.GetRequiredService<IClient>();
client.Notify(ex);
}
}
catch (Exception)
{
// do nothing
}
}
}
}
catch (Exception ex) when (ex is TaskCanceledException or OperationCanceledException)
{
_logger.LogInformation("Emby service shutting down");
}
}
private async Task SynchronizeSources(

53
ErsatzTV/Services/FFmpegWorkerService.cs

@ -27,40 +27,47 @@ public class FFmpegWorkerService : BackgroundService @@ -27,40 +27,47 @@ public class FFmpegWorkerService : BackgroundService
protected override async Task ExecuteAsync(CancellationToken cancellationToken)
{
_logger.LogInformation("FFmpeg worker service started");
await foreach (IFFmpegWorkerRequest request in _channel.ReadAllAsync(cancellationToken))
try
{
using IServiceScope scope = _serviceScopeFactory.CreateScope();
_logger.LogInformation("FFmpeg worker service started");
try
await foreach (IFFmpegWorkerRequest request in _channel.ReadAllAsync(cancellationToken))
{
using IServiceScope scope = _serviceScopeFactory.CreateScope();
switch (request)
try
{
case TouchFFmpegSession touchFFmpegSession:
foreach (DirectoryInfo parent in Optional(Directory.GetParent(touchFFmpegSession.Path)))
{
_ffmpegSegmenterService.TouchChannel(parent.Name);
}
break;
}
}
catch (Exception ex)
{
_logger.LogWarning(ex, "Failed to handle ffmpeg worker request");
switch (request)
{
case TouchFFmpegSession touchFFmpegSession:
foreach (DirectoryInfo parent in Optional(Directory.GetParent(touchFFmpegSession.Path)))
{
_ffmpegSegmenterService.TouchChannel(parent.Name);
}
try
{
IClient client = scope.ServiceProvider.GetRequiredService<IClient>();
client.Notify(ex);
break;
}
}
catch (Exception)
catch (Exception ex)
{
// do nothing
_logger.LogWarning(ex, "Failed to handle ffmpeg worker request");
try
{
IClient client = scope.ServiceProvider.GetRequiredService<IClient>();
client.Notify(ex);
}
catch (Exception)
{
// do nothing
}
}
}
}
catch (Exception ex) when (ex is TaskCanceledException or OperationCanceledException)
{
_logger.LogInformation("FFmpeg worker service shutting down");
}
}
}

89
ErsatzTV/Services/JellyfinService.cs

@ -26,61 +26,68 @@ public class JellyfinService : BackgroundService @@ -26,61 +26,68 @@ public class JellyfinService : BackgroundService
protected override async Task ExecuteAsync(CancellationToken cancellationToken)
{
if (!File.Exists(FileSystemLayout.JellyfinSecretsPath))
try
{
await File.WriteAllTextAsync(FileSystemLayout.JellyfinSecretsPath, "{}", cancellationToken);
}
if (!File.Exists(FileSystemLayout.JellyfinSecretsPath))
{
await File.WriteAllTextAsync(FileSystemLayout.JellyfinSecretsPath, "{}", cancellationToken);
}
_logger.LogInformation(
"Jellyfin service started; secrets are at {JellyfinSecretsPath}",
FileSystemLayout.JellyfinSecretsPath);
_logger.LogInformation(
"Jellyfin service started; secrets are at {JellyfinSecretsPath}",
FileSystemLayout.JellyfinSecretsPath);
// synchronize sources on startup
await SynchronizeSources(new SynchronizeJellyfinMediaSources(), cancellationToken);
// synchronize sources on startup
await SynchronizeSources(new SynchronizeJellyfinMediaSources(), cancellationToken);
await foreach (IJellyfinBackgroundServiceRequest request in _channel.ReadAllAsync(cancellationToken))
{
try
{
Task requestTask;
switch (request)
{
case SynchronizeJellyfinMediaSources synchronizeJellyfinMediaSources:
requestTask = SynchronizeSources(synchronizeJellyfinMediaSources, cancellationToken);
break;
case SynchronizeJellyfinAdminUserId synchronizeJellyfinAdminUserId:
requestTask = SynchronizeAdminUserId(synchronizeJellyfinAdminUserId, cancellationToken);
break;
case SynchronizeJellyfinLibraries synchronizeJellyfinLibraries:
requestTask = SynchronizeLibraries(synchronizeJellyfinLibraries, cancellationToken);
break;
case ISynchronizeJellyfinLibraryById synchronizeJellyfinLibraryById:
requestTask = SynchronizeJellyfinLibrary(synchronizeJellyfinLibraryById, cancellationToken);
break;
default:
throw new NotSupportedException($"Unsupported request type: {request.GetType().Name}");
}
await requestTask;
}
catch (Exception ex)
await foreach (IJellyfinBackgroundServiceRequest request in _channel.ReadAllAsync(cancellationToken))
{
_logger.LogWarning(ex, "Failed to process Jellyfin background service request");
try
{
using (IServiceScope scope = _serviceScopeFactory.CreateScope())
Task requestTask;
switch (request)
{
IClient client = scope.ServiceProvider.GetRequiredService<IClient>();
client.Notify(ex);
case SynchronizeJellyfinMediaSources synchronizeJellyfinMediaSources:
requestTask = SynchronizeSources(synchronizeJellyfinMediaSources, cancellationToken);
break;
case SynchronizeJellyfinAdminUserId synchronizeJellyfinAdminUserId:
requestTask = SynchronizeAdminUserId(synchronizeJellyfinAdminUserId, cancellationToken);
break;
case SynchronizeJellyfinLibraries synchronizeJellyfinLibraries:
requestTask = SynchronizeLibraries(synchronizeJellyfinLibraries, cancellationToken);
break;
case ISynchronizeJellyfinLibraryById synchronizeJellyfinLibraryById:
requestTask = SynchronizeJellyfinLibrary(synchronizeJellyfinLibraryById, cancellationToken);
break;
default:
throw new NotSupportedException($"Unsupported request type: {request.GetType().Name}");
}
await requestTask;
}
catch (Exception)
catch (Exception ex)
{
// do nothing
_logger.LogWarning(ex, "Failed to process Jellyfin background service request");
try
{
using (IServiceScope scope = _serviceScopeFactory.CreateScope())
{
IClient client = scope.ServiceProvider.GetRequiredService<IClient>();
client.Notify(ex);
}
}
catch (Exception)
{
// do nothing
}
}
}
}
catch (Exception ex) when (ex is TaskCanceledException or OperationCanceledException)
{
_logger.LogInformation("Jellyfin service shutting down");
}
}
private async Task SynchronizeSources(

89
ErsatzTV/Services/PlexService.cs

@ -26,61 +26,68 @@ public class PlexService : BackgroundService @@ -26,61 +26,68 @@ public class PlexService : BackgroundService
protected override async Task ExecuteAsync(CancellationToken cancellationToken)
{
if (!File.Exists(FileSystemLayout.PlexSecretsPath))
try
{
await File.WriteAllTextAsync(FileSystemLayout.PlexSecretsPath, "{}", cancellationToken);
}
if (!File.Exists(FileSystemLayout.PlexSecretsPath))
{
await File.WriteAllTextAsync(FileSystemLayout.PlexSecretsPath, "{}", cancellationToken);
}
_logger.LogInformation(
"Plex service started; secrets are at {PlexSecretsPath}",
FileSystemLayout.PlexSecretsPath);
_logger.LogInformation(
"Plex service started; secrets are at {PlexSecretsPath}",
FileSystemLayout.PlexSecretsPath);
// synchronize sources on startup
await SynchronizeSources(new SynchronizePlexMediaSources(), cancellationToken);
// synchronize sources on startup
await SynchronizeSources(new SynchronizePlexMediaSources(), cancellationToken);
await foreach (IPlexBackgroundServiceRequest request in _channel.ReadAllAsync(cancellationToken))
{
try
{
Task requestTask;
switch (request)
{
case TryCompletePlexPinFlow pinRequest:
requestTask = CompletePinFlow(pinRequest, cancellationToken);
break;
case SynchronizePlexMediaSources sourcesRequest:
requestTask = SynchronizeSources(sourcesRequest, cancellationToken);
break;
case SynchronizePlexLibraries synchronizePlexLibrariesRequest:
requestTask = SynchronizeLibraries(synchronizePlexLibrariesRequest, cancellationToken);
break;
case ISynchronizePlexLibraryById synchronizePlexLibraryById:
requestTask = SynchronizePlexLibrary(synchronizePlexLibraryById, cancellationToken);
break;
default:
throw new NotSupportedException($"Unsupported request type: {request.GetType().Name}");
}
await requestTask;
}
catch (Exception ex)
await foreach (IPlexBackgroundServiceRequest request in _channel.ReadAllAsync(cancellationToken))
{
_logger.LogWarning(ex, "Failed to process plex background service request");
try
{
using (IServiceScope scope = _serviceScopeFactory.CreateScope())
Task requestTask;
switch (request)
{
IClient client = scope.ServiceProvider.GetRequiredService<IClient>();
client.Notify(ex);
case TryCompletePlexPinFlow pinRequest:
requestTask = CompletePinFlow(pinRequest, cancellationToken);
break;
case SynchronizePlexMediaSources sourcesRequest:
requestTask = SynchronizeSources(sourcesRequest, cancellationToken);
break;
case SynchronizePlexLibraries synchronizePlexLibrariesRequest:
requestTask = SynchronizeLibraries(synchronizePlexLibrariesRequest, cancellationToken);
break;
case ISynchronizePlexLibraryById synchronizePlexLibraryById:
requestTask = SynchronizePlexLibrary(synchronizePlexLibraryById, cancellationToken);
break;
default:
throw new NotSupportedException($"Unsupported request type: {request.GetType().Name}");
}
await requestTask;
}
catch (Exception)
catch (Exception ex)
{
// do nothing
_logger.LogWarning(ex, "Failed to process plex background service request");
try
{
using (IServiceScope scope = _serviceScopeFactory.CreateScope())
{
IClient client = scope.ServiceProvider.GetRequiredService<IClient>();
client.Notify(ex);
}
}
catch (Exception)
{
// do nothing
}
}
}
}
catch (Exception ex) when (ex is TaskCanceledException or OperationCanceledException)
{
_logger.LogInformation("Plex service shutting down");
}
}
private async Task<List<PlexMediaSource>> SynchronizeSources(

10
ErsatzTV/Services/SchedulerService.cs

@ -51,7 +51,15 @@ public class SchedulerService : BackgroundService @@ -51,7 +51,15 @@ public class SchedulerService : BackgroundService
int currentMinutes = DateTime.Now.TimeOfDay.Minutes;
int toWait = currentMinutes < 30 ? 30 - currentMinutes : 60 - currentMinutes;
_logger.LogDebug("Scheduler sleeping for {Minutes} minutes", toWait);
await Task.Delay(TimeSpan.FromMinutes(toWait), cancellationToken);
try
{
await Task.Delay(TimeSpan.FromMinutes(toWait), cancellationToken);
}
catch (TaskCanceledException)
{
// do nothing
}
if (!cancellationToken.IsCancellationRequested)
{

128
ErsatzTV/Services/WorkerService.cs

@ -29,74 +29,90 @@ public class WorkerService : BackgroundService @@ -29,74 +29,90 @@ public class WorkerService : BackgroundService
protected override async Task ExecuteAsync(CancellationToken cancellationToken)
{
_logger.LogInformation("Worker service started");
await foreach (IBackgroundServiceRequest request in _channel.ReadAllAsync(cancellationToken))
try
{
using IServiceScope scope = _serviceScopeFactory.CreateScope();
_logger.LogInformation("Worker service started");
try
await foreach (IBackgroundServiceRequest request in _channel.ReadAllAsync(cancellationToken))
{
IMediator mediator = scope.ServiceProvider.GetRequiredService<IMediator>();
switch (request)
if (cancellationToken.IsCancellationRequested)
{
case BuildPlayout buildPlayout:
Either<BaseError, Unit> buildPlayoutResult = await mediator.Send(
buildPlayout,
cancellationToken);
buildPlayoutResult.BiIter(
_ => _logger.LogDebug("Built playout {PlayoutId}", buildPlayout.PlayoutId),
error => _logger.LogWarning(
"Unable to build playout {PlayoutId}: {Error}",
buildPlayout.PlayoutId,
error.Value));
break;
case IScanLocalLibrary scanLocalLibrary:
Either<BaseError, string> scanResult = await mediator.Send(
scanLocalLibrary,
cancellationToken);
scanResult.BiIter(
name => _logger.LogDebug(
"Done scanning local library {Library}",
name),
error => _logger.LogWarning(
"Unable to scan local library {LibraryId}: {Error}",
scanLocalLibrary.LibraryId,
error.Value));
break;
case RebuildSearchIndex rebuildSearchIndex:
await mediator.Send(rebuildSearchIndex, cancellationToken);
break;
case DeleteOrphanedArtwork deleteOrphanedArtwork:
_logger.LogInformation("Deleting orphaned artwork from the database");
await mediator.Send(deleteOrphanedArtwork, cancellationToken);
break;
case AddTraktList addTraktList:
await mediator.Send(addTraktList, cancellationToken);
break;
case DeleteTraktList deleteTraktList:
await mediator.Send(deleteTraktList, cancellationToken);
break;
case MatchTraktListItems matchTraktListItems:
await mediator.Send(matchTraktListItems, cancellationToken);
break;
break;
}
}
catch (Exception ex)
{
_logger.LogWarning(ex, "Failed to process background service request");
using IServiceScope scope = _serviceScopeFactory.CreateScope();
try
{
IClient client = scope.ServiceProvider.GetRequiredService<IClient>();
client.Notify(ex);
IMediator mediator = scope.ServiceProvider.GetRequiredService<IMediator>();
switch (request)
{
case BuildPlayout buildPlayout:
Either<BaseError, Unit> buildPlayoutResult = await mediator.Send(
buildPlayout,
cancellationToken);
buildPlayoutResult.BiIter(
_ => _logger.LogDebug("Built playout {PlayoutId}", buildPlayout.PlayoutId),
error => _logger.LogWarning(
"Unable to build playout {PlayoutId}: {Error}",
buildPlayout.PlayoutId,
error.Value));
break;
case IScanLocalLibrary scanLocalLibrary:
Either<BaseError, string> scanResult = await mediator.Send(
scanLocalLibrary,
cancellationToken);
scanResult.BiIter(
name => _logger.LogDebug(
"Done scanning local library {Library}",
name),
error => _logger.LogWarning(
"Unable to scan local library {LibraryId}: {Error}",
scanLocalLibrary.LibraryId,
error.Value));
break;
case RebuildSearchIndex rebuildSearchIndex:
await mediator.Send(rebuildSearchIndex, cancellationToken);
break;
case DeleteOrphanedArtwork deleteOrphanedArtwork:
_logger.LogInformation("Deleting orphaned artwork from the database");
await mediator.Send(deleteOrphanedArtwork, cancellationToken);
break;
case AddTraktList addTraktList:
await mediator.Send(addTraktList, cancellationToken);
break;
case DeleteTraktList deleteTraktList:
await mediator.Send(deleteTraktList, cancellationToken);
break;
case MatchTraktListItems matchTraktListItems:
await mediator.Send(matchTraktListItems, cancellationToken);
break;
}
}
catch (Exception)
catch (ObjectDisposedException) when (cancellationToken.IsCancellationRequested)
{
// do nothing
// this can happen when we're shutting down
}
catch (Exception ex)
{
_logger.LogWarning(ex, "Failed to process background service request");
try
{
IClient client = scope.ServiceProvider.GetRequiredService<IClient>();
client.Notify(ex);
}
catch (Exception)
{
// do nothing
}
}
}
}
catch (Exception ex) when (ex is TaskCanceledException or OperationCanceledException)
{
_logger.LogInformation("Worker service shutting down");
}
}
}
Loading…
Cancel
Save