mirror of https://github.com/ErsatzTV/ErsatzTV.git
23 changed files with 695 additions and 1 deletions
@ -0,0 +1,8 @@
@@ -0,0 +1,8 @@
|
||||
using System.Collections.Generic; |
||||
using ErsatzTV.Core.Health; |
||||
using MediatR; |
||||
|
||||
namespace ErsatzTV.Application.Health.Queries |
||||
{ |
||||
public record GetAllHealthCheckResults : IRequest<List<HealthCheckResult>>; |
||||
} |
||||
@ -0,0 +1,21 @@
@@ -0,0 +1,21 @@
|
||||
using System.Collections.Generic; |
||||
using System.Threading; |
||||
using System.Threading.Tasks; |
||||
using ErsatzTV.Core.Health; |
||||
using MediatR; |
||||
|
||||
namespace ErsatzTV.Application.Health.Queries |
||||
{ |
||||
public class GetAllHealthCheckResultsHandler : IRequestHandler<GetAllHealthCheckResults, List<HealthCheckResult>> |
||||
{ |
||||
private readonly IHealthCheckService _healthCheckService; |
||||
|
||||
public GetAllHealthCheckResultsHandler(IHealthCheckService healthCheckService) => |
||||
_healthCheckService = healthCheckService; |
||||
|
||||
public Task<List<HealthCheckResult>> Handle( |
||||
GetAllHealthCheckResults request, |
||||
CancellationToken cancellationToken) => |
||||
_healthCheckService.PerformHealthChecks(); |
||||
} |
||||
} |
||||
@ -0,0 +1,6 @@
@@ -0,0 +1,6 @@
|
||||
namespace ErsatzTV.Core.Health.Checks |
||||
{ |
||||
public interface IEpisodeMetadataHealthCheck : IHealthCheck |
||||
{ |
||||
} |
||||
} |
||||
@ -0,0 +1,6 @@
@@ -0,0 +1,6 @@
|
||||
namespace ErsatzTV.Core.Health.Checks |
||||
{ |
||||
public interface IFFmpegReportsHealthCheck : IHealthCheck |
||||
{ |
||||
} |
||||
} |
||||
@ -0,0 +1,6 @@
@@ -0,0 +1,6 @@
|
||||
namespace ErsatzTV.Core.Health.Checks |
||||
{ |
||||
public interface IFFmpegVersionHealthCheck : IHealthCheck |
||||
{ |
||||
} |
||||
} |
||||
@ -0,0 +1,6 @@
@@ -0,0 +1,6 @@
|
||||
namespace ErsatzTV.Core.Health.Checks |
||||
{ |
||||
public interface IHardwareAccelerationHealthCheck : IHealthCheck |
||||
{ |
||||
} |
||||
} |
||||
@ -0,0 +1,6 @@
@@ -0,0 +1,6 @@
|
||||
namespace ErsatzTV.Core.Health.Checks |
||||
{ |
||||
public interface IMovieMetadataHealthCheck : IHealthCheck |
||||
{ |
||||
} |
||||
} |
||||
@ -0,0 +1,6 @@
@@ -0,0 +1,6 @@
|
||||
namespace ErsatzTV.Core.Health.Checks |
||||
{ |
||||
public interface IZeroDurationHealthCheck : IHealthCheck |
||||
{ |
||||
} |
||||
} |
||||
@ -0,0 +1,4 @@
@@ -0,0 +1,4 @@
|
||||
namespace ErsatzTV.Core.Health |
||||
{ |
||||
public record HealthCheckResult(string Title, HealthCheckStatus Status, string Message); |
||||
} |
||||
@ -0,0 +1,10 @@
@@ -0,0 +1,10 @@
|
||||
namespace ErsatzTV.Core.Health |
||||
{ |
||||
public enum HealthCheckStatus |
||||
{ |
||||
Pass, |
||||
Fail, |
||||
Warning, |
||||
Info |
||||
} |
||||
} |
||||
@ -0,0 +1,9 @@
@@ -0,0 +1,9 @@
|
||||
using System.Threading.Tasks; |
||||
|
||||
namespace ErsatzTV.Core.Health |
||||
{ |
||||
public interface IHealthCheck |
||||
{ |
||||
Task<HealthCheckResult> Check(); |
||||
} |
||||
} |
||||
@ -0,0 +1,10 @@
@@ -0,0 +1,10 @@
|
||||
using System.Collections.Generic; |
||||
using System.Threading.Tasks; |
||||
|
||||
namespace ErsatzTV.Core.Health |
||||
{ |
||||
public interface IHealthCheckService |
||||
{ |
||||
Task<List<HealthCheckResult>> PerformHealthChecks(); |
||||
} |
||||
} |
||||
@ -0,0 +1,52 @@
@@ -0,0 +1,52 @@
|
||||
using System.Collections.Generic; |
||||
using System.Diagnostics; |
||||
using System.Threading.Tasks; |
||||
using ErsatzTV.Core.Health; |
||||
using LanguageExt; |
||||
using Lucene.Net.Util; |
||||
|
||||
namespace ErsatzTV.Infrastructure.Health.Checks |
||||
{ |
||||
public abstract class BaseHealthCheck |
||||
{ |
||||
protected abstract string Title { get; } |
||||
|
||||
protected HealthCheckResult Result(HealthCheckStatus status, string message) => |
||||
new(Title, status, message); |
||||
|
||||
protected HealthCheckResult OkResult() => |
||||
new(Title, HealthCheckStatus.Pass, string.Empty); |
||||
|
||||
protected HealthCheckResult FailResult(string message) => |
||||
new(Title, HealthCheckStatus.Fail, message); |
||||
|
||||
protected HealthCheckResult WarningResult(string message) => |
||||
new(Title, HealthCheckStatus.Warning, message); |
||||
|
||||
protected HealthCheckResult InfoResult(string message) => |
||||
new(Title, HealthCheckStatus.Info, message); |
||||
|
||||
protected static async Task<string> GetProcessOutput(string path, IEnumerable<string> arguments) |
||||
{ |
||||
var startInfo = new ProcessStartInfo |
||||
{ |
||||
FileName = path, |
||||
RedirectStandardOutput = true, |
||||
RedirectStandardError = true, |
||||
UseShellExecute = false |
||||
}; |
||||
|
||||
startInfo.ArgumentList.AddRange(arguments); |
||||
|
||||
var process = new Process |
||||
{ |
||||
StartInfo = startInfo |
||||
}; |
||||
|
||||
process.Start(); |
||||
string result = await process.StandardOutput.ReadToEndAsync(); |
||||
await process.WaitForExitAsync(); |
||||
return result; |
||||
} |
||||
} |
||||
} |
||||
@ -0,0 +1,53 @@
@@ -0,0 +1,53 @@
|
||||
using System.Collections.Generic; |
||||
using System.IO; |
||||
using System.Linq; |
||||
using System.Threading.Tasks; |
||||
using ErsatzTV.Core.Domain; |
||||
using ErsatzTV.Core.Health; |
||||
using ErsatzTV.Core.Health.Checks; |
||||
using ErsatzTV.Infrastructure.Data; |
||||
using LanguageExt; |
||||
using Microsoft.EntityFrameworkCore; |
||||
using static LanguageExt.Prelude; |
||||
|
||||
namespace ErsatzTV.Infrastructure.Health.Checks |
||||
{ |
||||
public class EpisodeMetadataHealthCheck : BaseHealthCheck, IEpisodeMetadataHealthCheck |
||||
{ |
||||
private readonly IDbContextFactory<TvContext> _dbContextFactory; |
||||
|
||||
public EpisodeMetadataHealthCheck(IDbContextFactory<TvContext> dbContextFactory) => |
||||
_dbContextFactory = dbContextFactory; |
||||
|
||||
protected override string Title => "Episode Metadata"; |
||||
|
||||
public async Task<HealthCheckResult> Check() |
||||
{ |
||||
await using TvContext dbContext = _dbContextFactory.CreateDbContext(); |
||||
|
||||
List<Episode> episodes = await dbContext.Episodes |
||||
.Filter(e => e.EpisodeMetadata.Count == 0) |
||||
.Include(e => e.MediaVersions) |
||||
.ThenInclude(mv => mv.MediaFiles) |
||||
.ToListAsync(); |
||||
|
||||
if (episodes.Any()) |
||||
{ |
||||
var paths = episodes.SelectMany(e => e.MediaVersions.Map(mv => mv.MediaFiles)) |
||||
.Flatten() |
||||
.Map(f => Optional<string>(Path.GetDirectoryName(f.Path))) |
||||
.Sequence() |
||||
.Flatten() |
||||
.Distinct() |
||||
.Take(5) |
||||
.ToList(); |
||||
|
||||
var folders = string.Join(", ", paths); |
||||
|
||||
return WarningResult($"There are {episodes.Count} episodes with missing metadata, including in the following folders: {folders}"); |
||||
} |
||||
|
||||
return OkResult(); |
||||
} |
||||
} |
||||
} |
||||
@ -0,0 +1,37 @@
@@ -0,0 +1,37 @@
|
||||
using System.Threading.Tasks; |
||||
using ErsatzTV.Core.Domain; |
||||
using ErsatzTV.Core.Health; |
||||
using ErsatzTV.Core.Health.Checks; |
||||
using ErsatzTV.Core.Interfaces.Repositories; |
||||
using LanguageExt; |
||||
|
||||
namespace ErsatzTV.Infrastructure.Health.Checks |
||||
{ |
||||
public class FFmpegReportsHealthCheck : BaseHealthCheck, IFFmpegReportsHealthCheck |
||||
{ |
||||
private readonly IConfigElementRepository _configElementRepository; |
||||
|
||||
public FFmpegReportsHealthCheck(IConfigElementRepository configElementRepository) => |
||||
_configElementRepository = configElementRepository; |
||||
|
||||
public async Task<HealthCheckResult> Check() |
||||
{ |
||||
Option<bool> saveReports = |
||||
await _configElementRepository.GetValue<bool>(ConfigElementKey.FFmpegSaveReports); |
||||
|
||||
foreach (bool value in saveReports) |
||||
{ |
||||
if (value) |
||||
{ |
||||
return Result( |
||||
HealthCheckStatus.Warning, |
||||
"FFmpeg troubleshooting reports are enabled and may use a lot of disk space"); |
||||
} |
||||
} |
||||
|
||||
return OkResult(); |
||||
} |
||||
|
||||
protected override string Title => "FFmpeg Reports"; |
||||
} |
||||
} |
||||
@ -0,0 +1,110 @@
@@ -0,0 +1,110 @@
|
||||
using System.Text.RegularExpressions; |
||||
using System.Threading.Tasks; |
||||
using ErsatzTV.Core.Domain; |
||||
using ErsatzTV.Core.Health; |
||||
using ErsatzTV.Core.Health.Checks; |
||||
using ErsatzTV.Core.Interfaces.Repositories; |
||||
using LanguageExt; |
||||
using static LanguageExt.Prelude; |
||||
|
||||
namespace ErsatzTV.Infrastructure.Health.Checks |
||||
{ |
||||
public class FFmpegVersionHealthCheck : BaseHealthCheck, IFFmpegVersionHealthCheck |
||||
{ |
||||
private readonly IConfigElementRepository _configElementRepository; |
||||
|
||||
public FFmpegVersionHealthCheck(IConfigElementRepository configElementRepository) |
||||
{ |
||||
_configElementRepository = configElementRepository; |
||||
} |
||||
|
||||
public async Task<HealthCheckResult> Check() |
||||
{ |
||||
Option<ConfigElement> maybeFFmpegPath = await _configElementRepository.Get(ConfigElementKey.FFmpegPath); |
||||
if (maybeFFmpegPath.IsNone) |
||||
{ |
||||
return FailResult("Unable to locate ffmpeg"); |
||||
} |
||||
|
||||
Option<ConfigElement> maybeFFprobePath = await _configElementRepository.Get(ConfigElementKey.FFprobePath); |
||||
if (maybeFFprobePath.IsNone) |
||||
{ |
||||
return FailResult("Unable to locate ffprobe"); |
||||
} |
||||
foreach (ConfigElement ffmpegPath in maybeFFmpegPath) |
||||
{ |
||||
Option<string> maybeVersion = await GetVersion(ffmpegPath.Value); |
||||
if (maybeVersion.IsNone) |
||||
{ |
||||
return WarningResult("Unable to determine ffmpeg version"); |
||||
} |
||||
|
||||
foreach (string version in maybeVersion) |
||||
{ |
||||
foreach (HealthCheckResult result in ValidateVersion(version, "ffmpeg")) |
||||
{ |
||||
return result; |
||||
} |
||||
} |
||||
} |
||||
|
||||
foreach (ConfigElement ffprobePath in maybeFFprobePath) |
||||
{ |
||||
Option<string> maybeVersion = await GetVersion(ffprobePath.Value); |
||||
if (maybeVersion.IsNone) |
||||
{ |
||||
return WarningResult("Unable to determine ffprobe version"); |
||||
} |
||||
|
||||
foreach (string version in maybeVersion) |
||||
{ |
||||
foreach (HealthCheckResult result in ValidateVersion(version, "ffprobe")) |
||||
{ |
||||
return result; |
||||
} |
||||
} |
||||
} |
||||
|
||||
return new HealthCheckResult("FFmpeg Version", HealthCheckStatus.Pass, string.Empty); |
||||
} |
||||
|
||||
private Option<HealthCheckResult> ValidateVersion(string version, string app) |
||||
{ |
||||
if (version.StartsWith("3.")) |
||||
{ |
||||
return FailResult($"{app} version {version} is too old; please install 4.3!"); |
||||
} |
||||
|
||||
if (version.StartsWith("4.4")) |
||||
{ |
||||
return FailResult($"{app} version 4.4 is known to have issues; please install 4.3!"); |
||||
} |
||||
|
||||
if (!version.StartsWith("4.3")) |
||||
{ |
||||
return WarningResult($"{app} version {version} is unexpected and may have problems; please install 4.3!"); |
||||
} |
||||
|
||||
return None; |
||||
} |
||||
|
||||
private static async Task<Option<string>> GetVersion(string path) |
||||
{ |
||||
Option<string> maybeLine = await GetProcessOutput(path, new[] { "-version" }) |
||||
.Map(s => s.Split("\n").HeadOrNone().Map(h => h.Trim())); |
||||
foreach (string line in maybeLine) |
||||
{ |
||||
const string PATTERN = @"version\s+([^\s]+)"; |
||||
Match match = Regex.Match(line, PATTERN); |
||||
if (match.Success) |
||||
{ |
||||
return match.Groups[1].Value; |
||||
} |
||||
} |
||||
|
||||
return None; |
||||
} |
||||
|
||||
protected override string Title => "FFmpeg Version"; |
||||
} |
||||
} |
||||
@ -0,0 +1,127 @@
@@ -0,0 +1,127 @@
|
||||
using System; |
||||
using System.Collections.Generic; |
||||
using System.Linq; |
||||
using System.Reflection; |
||||
using System.Runtime.InteropServices; |
||||
using System.Threading.Tasks; |
||||
using ErsatzTV.Core.Domain; |
||||
using ErsatzTV.Core.Health; |
||||
using ErsatzTV.Core.Health.Checks; |
||||
using ErsatzTV.Core.Interfaces.Repositories; |
||||
using ErsatzTV.Infrastructure.Data; |
||||
using LanguageExt; |
||||
using LanguageExt.UnsafeValueAccess; |
||||
using Microsoft.EntityFrameworkCore; |
||||
using static LanguageExt.Prelude; |
||||
|
||||
namespace ErsatzTV.Infrastructure.Health.Checks |
||||
{ |
||||
public class HardwareAccelerationHealthCheck : BaseHealthCheck, IHardwareAccelerationHealthCheck |
||||
{ |
||||
private readonly IDbContextFactory<TvContext> _dbContextFactory; |
||||
private readonly IConfigElementRepository _configElementRepository; |
||||
|
||||
public HardwareAccelerationHealthCheck( |
||||
IDbContextFactory<TvContext> dbContextFactory, |
||||
IConfigElementRepository configElementRepository) |
||||
{ |
||||
_dbContextFactory = dbContextFactory; |
||||
_configElementRepository = configElementRepository; |
||||
} |
||||
|
||||
public async Task<HealthCheckResult> Check() |
||||
{ |
||||
Option<ConfigElement> maybeFFmpegPath = await _configElementRepository.Get(ConfigElementKey.FFmpegPath); |
||||
if (maybeFFmpegPath.IsNone) |
||||
{ |
||||
return FailResult("Unable to locate ffmpeg"); |
||||
} |
||||
|
||||
string version = Assembly.GetEntryAssembly()?.GetCustomAttribute<AssemblyInformationalVersionAttribute>() |
||||
?.InformationalVersion ?? "unknown"; |
||||
|
||||
var accelerationKinds = new List<HardwareAccelerationKind>(); |
||||
|
||||
if (version.Contains("docker", StringComparison.OrdinalIgnoreCase)) |
||||
{ |
||||
if (version.Contains("nvidia", StringComparison.OrdinalIgnoreCase)) |
||||
{ |
||||
accelerationKinds.Add(HardwareAccelerationKind.Nvenc); |
||||
} |
||||
else if (version.Contains("vaapi", StringComparison.OrdinalIgnoreCase)) |
||||
{ |
||||
accelerationKinds.Add(HardwareAccelerationKind.Vaapi); |
||||
} |
||||
} |
||||
|
||||
if (!accelerationKinds.Any()) |
||||
{ |
||||
accelerationKinds.AddRange(await GetSupportedAccelerationKinds(maybeFFmpegPath.ValueUnsafe().Value)); |
||||
} |
||||
|
||||
if (!accelerationKinds.Any()) |
||||
{ |
||||
return InfoResult("No compatible hardware acceleration kinds are supported by ffmpeg"); |
||||
} |
||||
|
||||
Option<HealthCheckResult> maybeResult = await VerifyProfilesUseAcceleration(accelerationKinds); |
||||
foreach (HealthCheckResult result in maybeResult) |
||||
{ |
||||
return result; |
||||
} |
||||
|
||||
return OkResult(); |
||||
} |
||||
|
||||
private async Task<Option<HealthCheckResult>> VerifyProfilesUseAcceleration( |
||||
IEnumerable<HardwareAccelerationKind> accelerationKinds) |
||||
{ |
||||
await using TvContext dbContext = _dbContextFactory.CreateDbContext(); |
||||
|
||||
List<Channel> badChannels = await dbContext.Channels |
||||
.Filter(c => c.StreamingMode != StreamingMode.HttpLiveStreamingDirect) |
||||
.Filter(c => !accelerationKinds.Contains(c.FFmpegProfile.HardwareAcceleration)) |
||||
.ToListAsync(); |
||||
|
||||
if (badChannels.Any()) |
||||
{ |
||||
var accel = string.Join(", ", accelerationKinds); |
||||
var channels = string.Join(", ", badChannels.Map(c => $"{c.Number} - {c.Name}")); |
||||
return WarningResult( |
||||
$"The following channels are transcoding without hardware acceleration ({accel}): {channels}"); |
||||
} |
||||
|
||||
return None; |
||||
} |
||||
|
||||
private static async Task<List<HardwareAccelerationKind>> GetSupportedAccelerationKinds(string ffmpegPath) |
||||
{ |
||||
var result = new System.Collections.Generic.HashSet<HardwareAccelerationKind>(); |
||||
|
||||
string output = await GetProcessOutput(ffmpegPath, new[] { "-v", "quiet", "-hwaccels" }); |
||||
foreach (string method in output.Split("\n").Map(s => s.Trim()).Skip(1)) |
||||
{ |
||||
switch (method) |
||||
{ |
||||
case "vaapi": |
||||
result.Add(HardwareAccelerationKind.Vaapi); |
||||
break; |
||||
case "nvenc": |
||||
result.Add(HardwareAccelerationKind.Nvenc); |
||||
break; |
||||
case "qsv": |
||||
// qsv is only supported on windows
|
||||
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) |
||||
{ |
||||
result.Add(HardwareAccelerationKind.Qsv); |
||||
} |
||||
break; |
||||
} |
||||
} |
||||
|
||||
return result.ToList(); |
||||
} |
||||
|
||||
protected override string Title => "Hardware Acceleration"; |
||||
} |
||||
} |
||||
@ -0,0 +1,53 @@
@@ -0,0 +1,53 @@
|
||||
using System.Collections.Generic; |
||||
using System.IO; |
||||
using System.Linq; |
||||
using System.Threading.Tasks; |
||||
using ErsatzTV.Core.Domain; |
||||
using ErsatzTV.Core.Health; |
||||
using ErsatzTV.Core.Health.Checks; |
||||
using ErsatzTV.Infrastructure.Data; |
||||
using LanguageExt; |
||||
using Microsoft.EntityFrameworkCore; |
||||
using static LanguageExt.Prelude; |
||||
|
||||
namespace ErsatzTV.Infrastructure.Health.Checks |
||||
{ |
||||
public class MovieMetadataHealthCheck : BaseHealthCheck, IMovieMetadataHealthCheck |
||||
{ |
||||
private readonly IDbContextFactory<TvContext> _dbContextFactory; |
||||
|
||||
public MovieMetadataHealthCheck(IDbContextFactory<TvContext> dbContextFactory) => |
||||
_dbContextFactory = dbContextFactory; |
||||
|
||||
protected override string Title => "Movie Metadata"; |
||||
|
||||
public async Task<HealthCheckResult> Check() |
||||
{ |
||||
await using TvContext dbContext = _dbContextFactory.CreateDbContext(); |
||||
|
||||
List<Movie> movies = await dbContext.Movies |
||||
.Filter(e => e.MovieMetadata.Count == 0) |
||||
.Include(e => e.MediaVersions) |
||||
.ThenInclude(mv => mv.MediaFiles) |
||||
.ToListAsync(); |
||||
|
||||
if (movies.Any()) |
||||
{ |
||||
var paths = movies.SelectMany(e => e.MediaVersions.Map(mv => mv.MediaFiles)) |
||||
.Flatten() |
||||
.Map(f => Optional<string>(Path.GetDirectoryName(f.Path))) |
||||
.Sequence() |
||||
.Flatten() |
||||
.Distinct() |
||||
.Take(5) |
||||
.ToList(); |
||||
|
||||
var folders = string.Join(", ", paths); |
||||
|
||||
return WarningResult($"There are {movies.Count} movies with missing metadata, including in the following folders: {folders}"); |
||||
} |
||||
|
||||
return OkResult(); |
||||
} |
||||
} |
||||
} |
||||
@ -0,0 +1,54 @@
@@ -0,0 +1,54 @@
|
||||
using System; |
||||
using System.Collections.Generic; |
||||
using System.Linq; |
||||
using System.Threading.Tasks; |
||||
using ErsatzTV.Core.Domain; |
||||
using ErsatzTV.Core.Health; |
||||
using ErsatzTV.Core.Health.Checks; |
||||
using ErsatzTV.Infrastructure.Data; |
||||
using Microsoft.EntityFrameworkCore; |
||||
|
||||
namespace ErsatzTV.Infrastructure.Health.Checks |
||||
{ |
||||
public class ZeroDurationHealthCheck : BaseHealthCheck, IZeroDurationHealthCheck |
||||
{ |
||||
private readonly IDbContextFactory<TvContext> _dbContextFactory; |
||||
|
||||
public ZeroDurationHealthCheck(IDbContextFactory<TvContext> dbContextFactory) => |
||||
_dbContextFactory = dbContextFactory; |
||||
|
||||
protected override string Title => "Zero Duration"; |
||||
|
||||
public async Task<HealthCheckResult> Check() |
||||
{ |
||||
await using TvContext dbContext = _dbContextFactory.CreateDbContext(); |
||||
|
||||
List<Episode> episodes = await dbContext.Episodes |
||||
.Filter(e => e.MediaVersions.Any(mv => mv.Duration == TimeSpan.Zero)) |
||||
.Include(e => e.MediaVersions) |
||||
.ThenInclude(mv => mv.MediaFiles) |
||||
.ToListAsync(); |
||||
|
||||
List<Movie> movies = await dbContext.Movies |
||||
.Filter(e => e.MediaVersions.Any(mv => mv.Duration == TimeSpan.Zero)) |
||||
.Include(e => e.MediaVersions) |
||||
.ThenInclude(mv => mv.MediaFiles) |
||||
.ToListAsync(); |
||||
|
||||
List<string> all = movies.Map(m => m.MediaVersions.Head().MediaFiles.Head().Path) |
||||
.Append(episodes.Map(e => e.MediaVersions.Head().MediaFiles.Head().Path)) |
||||
.ToList(); |
||||
|
||||
if (all.Any()) |
||||
{ |
||||
var paths = all.Take(5).ToList(); |
||||
|
||||
var files = string.Join(", ", paths); |
||||
|
||||
return WarningResult($"There are {all.Count} files with zero duration, including the following: {files}"); |
||||
} |
||||
|
||||
return OkResult(); |
||||
} |
||||
} |
||||
} |
||||
@ -0,0 +1,37 @@
@@ -0,0 +1,37 @@
|
||||
using System.Collections.Generic; |
||||
using System.Linq; |
||||
using System.Threading.Tasks; |
||||
using ErsatzTV.Core.Health; |
||||
using ErsatzTV.Core.Health.Checks; |
||||
using LanguageExt; |
||||
|
||||
namespace ErsatzTV.Infrastructure.Health |
||||
{ |
||||
public class HealthCheckService : IHealthCheckService |
||||
{ |
||||
private readonly List<IHealthCheck> _checks; |
||||
|
||||
// ReSharper disable SuggestBaseTypeForParameterInConstructor
|
||||
public HealthCheckService( |
||||
IFFmpegVersionHealthCheck ffmpegVersionHealthCheck, |
||||
IFFmpegReportsHealthCheck fFmpegReportsHealthCheck, |
||||
IHardwareAccelerationHealthCheck hardwareAccelerationHealthCheck, |
||||
IMovieMetadataHealthCheck movieMetadataHealthCheck, |
||||
IEpisodeMetadataHealthCheck episodeMetadataHealthCheck, |
||||
IZeroDurationHealthCheck zeroDurationHealthCheck) |
||||
{ |
||||
_checks = new List<IHealthCheck> |
||||
{ |
||||
ffmpegVersionHealthCheck, |
||||
fFmpegReportsHealthCheck, |
||||
hardwareAccelerationHealthCheck, |
||||
movieMetadataHealthCheck, |
||||
episodeMetadataHealthCheck, |
||||
zeroDurationHealthCheck |
||||
}; |
||||
} |
||||
|
||||
public Task<List<HealthCheckResult>> PerformHealthChecks() => |
||||
_checks.Map(c => c.Check()).Sequence().Map(results => results.ToList()); |
||||
} |
||||
} |
||||
Loading…
Reference in new issue