mirror of https://github.com/ErsatzTV/ErsatzTV.git
12 changed files with 159 additions and 83 deletions
@ -1,68 +1,35 @@ |
|||||||
using System.IO.Compression; |
using System.IO.Compression; |
||||||
using System.Text.Json; |
|
||||||
using System.Text.Json.Serialization; |
|
||||||
using ErsatzTV.Application.MediaItems; |
|
||||||
using ErsatzTV.Application.Troubleshooting.Queries; |
|
||||||
using ErsatzTV.Core; |
using ErsatzTV.Core; |
||||||
using ErsatzTV.Core.Interfaces.Metadata; |
using ErsatzTV.Core.Interfaces.Metadata; |
||||||
|
|
||||||
namespace ErsatzTV.Application.Troubleshooting; |
namespace ErsatzTV.Application.Troubleshooting; |
||||||
|
|
||||||
public class ArchiveTroubleshootingResultsHandler(IMediator mediator, ILocalFileSystem localFileSystem) |
public class ArchiveTroubleshootingResultsHandler(ILocalFileSystem localFileSystem) |
||||||
: IRequestHandler<ArchiveTroubleshootingResults, Option<string>> |
: IRequestHandler<ArchiveTroubleshootingResults, Option<string>> |
||||||
{ |
{ |
||||||
private static readonly JsonSerializerOptions Options = new() |
public Task<Option<string>> Handle(ArchiveTroubleshootingResults request, CancellationToken cancellationToken) |
||||||
{ |
|
||||||
Converters = { new JsonStringEnumConverter() }, |
|
||||||
DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull, |
|
||||||
WriteIndented = true |
|
||||||
}; |
|
||||||
|
|
||||||
public async Task<Option<string>> Handle(ArchiveTroubleshootingResults request, CancellationToken cancellationToken) |
|
||||||
{ |
{ |
||||||
string tempFile = Path.GetTempFileName(); |
string tempFile = Path.GetTempFileName(); |
||||||
using ZipArchive zipArchive = ZipFile.Open(tempFile, ZipArchiveMode.Update); |
using ZipArchive zipArchive = ZipFile.Open(tempFile, ZipArchiveMode.Update); |
||||||
|
|
||||||
string transcodeFolder = Path.Combine(FileSystemLayout.TranscodeFolder, ".troubleshooting"); |
var hasReport = false; |
||||||
|
foreach (string file in localFileSystem.ListFiles(FileSystemLayout.TranscodeTroubleshootingFolder)) |
||||||
bool hasReport = false; |
|
||||||
foreach (string file in localFileSystem.ListFiles(transcodeFolder)) |
|
||||||
{ |
{ |
||||||
|
string fileName = Path.GetFileName(file); |
||||||
|
|
||||||
// add to archive
|
// add to archive
|
||||||
if (Path.GetFileName(file).StartsWith("ffmpeg-", StringComparison.InvariantCultureIgnoreCase)) |
if (fileName.StartsWith("ffmpeg-", StringComparison.OrdinalIgnoreCase)) |
||||||
{ |
{ |
||||||
hasReport = true; |
hasReport = true; |
||||||
zipArchive.CreateEntryFromFile(file, Path.GetFileName(file)); |
zipArchive.CreateEntryFromFile(file, fileName); |
||||||
} |
} |
||||||
} |
|
||||||
|
|
||||||
Either<BaseError, MediaItemInfo> maybeMediaItemInfo = await mediator.Send(new GetMediaItemInfo(request.MediaItemId), cancellationToken); |
|
||||||
foreach (MediaItemInfo info in maybeMediaItemInfo.RightToSeq()) |
|
||||||
{ |
|
||||||
string infoJson = JsonSerializer.Serialize(info, Options); |
|
||||||
string tempMediaInfoFile = Path.GetTempFileName(); |
|
||||||
await File.WriteAllTextAsync(tempMediaInfoFile, infoJson, cancellationToken); |
|
||||||
zipArchive.CreateEntryFromFile(tempMediaInfoFile, "media_info.json"); |
|
||||||
} |
|
||||||
|
|
||||||
TroubleshootingInfo troubleshootingInfo = await mediator.Send(new GetTroubleshootingInfo(), cancellationToken); |
|
||||||
|
|
||||||
string troubleshootingInfoJson = JsonSerializer.Serialize( |
if (Path.GetExtension(file).Equals(".json", StringComparison.OrdinalIgnoreCase)) |
||||||
new |
|
||||||
{ |
{ |
||||||
troubleshootingInfo.Version, |
zipArchive.CreateEntryFromFile(file, fileName); |
||||||
Environment = troubleshootingInfo.Environment.OrderBy(x => x.Key).ToDictionary(x => x.Key, x => x.Value), |
} |
||||||
troubleshootingInfo.Health, |
} |
||||||
troubleshootingInfo.FFmpegSettings, |
|
||||||
troubleshootingInfo.Channels, |
|
||||||
troubleshootingInfo.FFmpegProfiles |
|
||||||
}, |
|
||||||
Options); |
|
||||||
|
|
||||||
string tempTroubleshootingInfoFile = Path.GetTempFileName(); |
|
||||||
await File.WriteAllTextAsync(tempTroubleshootingInfoFile, troubleshootingInfoJson, cancellationToken); |
|
||||||
zipArchive.CreateEntryFromFile(tempTroubleshootingInfoFile, "troubleshooting_info.json"); |
|
||||||
|
|
||||||
return hasReport ? tempFile : Option<string>.None; |
return Task.FromResult(hasReport ? tempFile : Option<string>.None); |
||||||
} |
} |
||||||
} |
} |
||||||
|
@ -1,5 +1,9 @@ |
|||||||
using CliWrap; |
using CliWrap; |
||||||
|
using ErsatzTV.Application.MediaItems; |
||||||
|
|
||||||
namespace ErsatzTV.Application.Troubleshooting; |
namespace ErsatzTV.Application.Troubleshooting; |
||||||
|
|
||||||
public record StartTroubleshootingPlayback(Command Command) : IRequest, IFFmpegWorkerRequest; |
public record StartTroubleshootingPlayback( |
||||||
|
Command Command, |
||||||
|
MediaItemInfo MediaItemInfo, |
||||||
|
TroubleshootingInfo TroubleshootingInfo) : IRequest, IFFmpegWorkerRequest; |
||||||
|
@ -1,33 +1,71 @@ |
|||||||
|
using System.Text.Json; |
||||||
|
using System.Text.Json.Serialization; |
||||||
using CliWrap; |
using CliWrap; |
||||||
using CliWrap.Buffered; |
using CliWrap.Buffered; |
||||||
|
using ErsatzTV.Core; |
||||||
using ErsatzTV.Core.Interfaces.Locking; |
using ErsatzTV.Core.Interfaces.Locking; |
||||||
|
using ErsatzTV.Core.Notifications; |
||||||
using Microsoft.Extensions.Logging; |
using Microsoft.Extensions.Logging; |
||||||
|
|
||||||
namespace ErsatzTV.Application.Troubleshooting; |
namespace ErsatzTV.Application.Troubleshooting; |
||||||
|
|
||||||
public class StartTroubleshootingPlaybackHandler( |
public class StartTroubleshootingPlaybackHandler( |
||||||
|
IMediator mediator, |
||||||
IEntityLocker entityLocker, |
IEntityLocker entityLocker, |
||||||
ILogger<StartTroubleshootingPlaybackHandler> logger) |
ILogger<StartTroubleshootingPlaybackHandler> logger) |
||||||
: IRequestHandler<StartTroubleshootingPlayback> |
: IRequestHandler<StartTroubleshootingPlayback> |
||||||
{ |
{ |
||||||
|
private static readonly JsonSerializerOptions Options = new() |
||||||
|
{ |
||||||
|
Converters = { new JsonStringEnumConverter() }, |
||||||
|
DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull, |
||||||
|
WriteIndented = true |
||||||
|
}; |
||||||
|
|
||||||
public async Task Handle(StartTroubleshootingPlayback request, CancellationToken cancellationToken) |
public async Task Handle(StartTroubleshootingPlayback request, CancellationToken cancellationToken) |
||||||
{ |
{ |
||||||
logger.LogDebug("ffmpeg troubleshooting arguments {FFmpegArguments}", request.Command.Arguments); |
try |
||||||
|
{ |
||||||
|
// write media info without title
|
||||||
|
string infoJson = JsonSerializer.Serialize(request.MediaItemInfo with { Title = null }, Options); |
||||||
|
await File.WriteAllTextAsync( |
||||||
|
Path.Combine(FileSystemLayout.TranscodeTroubleshootingFolder, "media_info.json"), |
||||||
|
infoJson, |
||||||
|
cancellationToken); |
||||||
|
|
||||||
BufferedCommandResult result = await request.Command |
// write troubleshooting info
|
||||||
.WithValidation(CommandResultValidation.None) |
string troubleshootingInfoJson = JsonSerializer.Serialize( |
||||||
.ExecuteBufferedAsync(cancellationToken); |
new |
||||||
|
{ |
||||||
|
request.TroubleshootingInfo.Version, |
||||||
|
Environment = request.TroubleshootingInfo.Environment.OrderBy(x => x.Key) |
||||||
|
.ToDictionary(x => x.Key, x => x.Value), |
||||||
|
request.TroubleshootingInfo.Health, |
||||||
|
request.TroubleshootingInfo.FFmpegSettings, |
||||||
|
request.TroubleshootingInfo.FFmpegProfiles, |
||||||
|
request.TroubleshootingInfo.Watermarks |
||||||
|
}, |
||||||
|
Options); |
||||||
|
await File.WriteAllTextAsync( |
||||||
|
Path.Combine(FileSystemLayout.TranscodeTroubleshootingFolder, "troubleshooting_info.json"), |
||||||
|
troubleshootingInfoJson, |
||||||
|
cancellationToken); |
||||||
|
|
||||||
entityLocker.UnlockTroubleshootingPlayback(); |
logger.LogDebug("ffmpeg troubleshooting arguments {FFmpegArguments}", request.Command.Arguments); |
||||||
|
|
||||||
|
BufferedCommandResult result = await request.Command |
||||||
|
.WithValidation(CommandResultValidation.None) |
||||||
|
.ExecuteBufferedAsync(cancellationToken); |
||||||
|
|
||||||
logger.LogInformation("Troubleshooting playback completed with exit code {ExitCode}", result.ExitCode); |
await mediator.Publish( |
||||||
|
new PlaybackTroubleshootingCompletedNotification(result.ExitCode), |
||||||
|
cancellationToken); |
||||||
|
|
||||||
foreach (KeyValuePair<string, string> env in request.Command.EnvironmentVariables) |
logger.LogDebug("Troubleshooting playback completed with exit code {ExitCode}", result.ExitCode); |
||||||
|
} |
||||||
|
finally |
||||||
{ |
{ |
||||||
logger.LogInformation("{Key} => {Value}", env.Key, env.Value); |
entityLocker.UnlockTroubleshootingPlayback(); |
||||||
} |
} |
||||||
|
|
||||||
// TODO: something with the result ???
|
|
||||||
} |
} |
||||||
} |
} |
||||||
|
@ -0,0 +1,5 @@ |
|||||||
|
using MediatR; |
||||||
|
|
||||||
|
namespace ErsatzTV.Core.Notifications; |
||||||
|
|
||||||
|
public record PlaybackTroubleshootingCompletedNotification(int ExitCode) : INotification; |
Loading…
Reference in new issue