Browse Source

feat: show next version in ui (#2910)

main
Jason Dove 5 hours ago committed by GitHub
parent
commit
9c5d86a161
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
  1. 3
      ErsatzTV.Application/Streaming/Commands/GetNextVersion.cs
  2. 38
      ErsatzTV.Application/Streaming/Commands/GetNextVersionHandler.cs
  3. 59
      ErsatzTV.Application/Streaming/Commands/NextChannelHandlerBase.cs
  4. 63
      ErsatzTV.Application/Streaming/Commands/StartFFmpegNextSessionHandler.cs
  5. 6
      ErsatzTV/NextVersion.cs
  6. 7
      ErsatzTV/Services/RunOnce/PlatformSettingsService.cs
  7. 3
      ErsatzTV/Shared/MainLayout.razor

3
ErsatzTV.Application/Streaming/Commands/GetNextVersion.cs

@ -0,0 +1,3 @@ @@ -0,0 +1,3 @@
namespace ErsatzTV.Application.Streaming;
public record GetNextVersion : IRequest<string>;

38
ErsatzTV.Application/Streaming/Commands/GetNextVersionHandler.cs

@ -0,0 +1,38 @@ @@ -0,0 +1,38 @@
using System.IO.Abstractions;
using System.Text;
using CliWrap;
using ErsatzTV.Core;
namespace ErsatzTV.Application.Streaming;
public class GetNextVersionHandler(IFileSystem fileSystem)
: NextChannelHandlerBase(fileSystem), IRequestHandler<GetNextVersion, string>
{
public async Task<string> Handle(GetNextVersion request, CancellationToken cancellationToken)
{
try
{
Validation<BaseError, string> validation = await ChannelBinaryMustExist();
foreach (string channelBinary in validation.SuccessToSeq())
{
var stdOutBuffer = new StringBuilder();
CommandResult command = await Cli.Wrap(channelBinary)
.WithArguments(["--version"])
.WithStandardOutputPipe(PipeTarget.ToStringBuilder(stdOutBuffer))
.WithValidation(CommandResultValidation.None)
.ExecuteAsync(cancellationToken);
if (command.IsSuccess)
{
return stdOutBuffer.ToString().Replace("ersatztv-channel", string.Empty).Trim();
}
}
}
catch (Exception)
{
// do nothing
}
return "n/a";
}
}

59
ErsatzTV.Application/Streaming/Commands/NextChannelHandlerBase.cs

@ -0,0 +1,59 @@ @@ -0,0 +1,59 @@
using System.IO.Abstractions;
using System.Runtime.InteropServices;
using ErsatzTV.Core;
namespace ErsatzTV.Application.Streaming;
public abstract class NextChannelHandlerBase(IFileSystem fileSystem)
{
protected Task<Validation<BaseError, string>> ChannelBinaryMustExist()
{
string nextFolder = SystemEnvironment.NextFolder;
if (string.IsNullOrWhiteSpace(nextFolder))
{
string processFileName = Environment.ProcessPath ?? string.Empty;
string processExecutable = Path.GetFileNameWithoutExtension(processFileName);
nextFolder = Path.GetDirectoryName(processFileName);
if ("dotnet".Equals(processExecutable, StringComparison.OrdinalIgnoreCase))
{
nextFolder = AppContext.BaseDirectory;
}
}
string executable = RuntimeInformation.IsOSPlatform(OSPlatform.Windows)
? "ersatztv-channel.exe"
: "ersatztv-channel";
string channelBinary = fileSystem.Path.Combine(ReplaceTilde(nextFolder), executable);
if (!fileSystem.Path.Exists(channelBinary))
{
return Task.FromResult<Validation<BaseError, string>>(
BaseError.New("ersatztv-channel binary does not exist!"));
}
return Task.FromResult<Validation<BaseError, string>>(channelBinary);
}
private string ReplaceTilde(string path)
{
if (!path.StartsWith('~'))
{
return path;
}
string userFolder = Environment.GetFolderPath(Environment.SpecialFolder.UserProfile);
switch (path)
{
case "~":
return userFolder;
case not null
when path.Length == 2 &&
(path[1] == fileSystem.Path.DirectorySeparatorChar ||
path[1] == fileSystem.Path.AltDirectorySeparatorChar):
return userFolder + fileSystem.Path.DirectorySeparatorChar;
default:
return fileSystem.Path.Combine(userFolder, path[2..]);
}
}
}

63
ErsatzTV.Application/Streaming/Commands/StartFFmpegNextSessionHandler.cs

@ -1,6 +1,5 @@ @@ -1,6 +1,5 @@
using System.Globalization;
using System.IO.Abstractions;
using System.Runtime.InteropServices;
using System.Threading.Channels;
using ErsatzTV.Application.Channels;
using ErsatzTV.Application.FFmpegProfiles;
@ -32,8 +31,10 @@ public class StartFFmpegNextSessionHandler( @@ -32,8 +31,10 @@ public class StartFFmpegNextSessionHandler(
ChannelWriter<IBackgroundServiceRequest> workerChannel,
ILogger<StartFFmpegNextSessionHandler> logger,
ILogger<NextSessionWorker> sessionWorkerLogger)
: IRequestHandler<StartFFmpegNextSession, Either<BaseError, string>>
: NextChannelHandlerBase(fileSystem), IRequestHandler<StartFFmpegNextSession, Either<BaseError, string>>
{
private readonly IFileSystem _fileSystem = fileSystem;
public Task<Either<BaseError, string>> Handle(
StartFFmpegNextSession request,
CancellationToken cancellationToken) =>
@ -73,7 +74,7 @@ public class StartFFmpegNextSessionHandler( @@ -73,7 +74,7 @@ public class StartFFmpegNextSessionHandler(
NextSessionWorker worker = new NextSessionWorker(
validationResult.ChannelBinary,
config,
fileSystem,
_fileSystem,
localFileSystem,
serviceScopeFactory,
sessionWorkerLogger);
@ -108,7 +109,7 @@ public class StartFFmpegNextSessionHandler( @@ -108,7 +109,7 @@ public class StartFFmpegNextSessionHandler(
SessionMustBeInactive(request)
.BindT(_ => FolderMustBeEmpty(request))
.BindT(_ => ChannelBinaryMustExist())
.BindT(result => ChannelMustExist(request, result, cancellationToken))
.BindT(channelBinary => ChannelMustExist(request, new ValidationResult(channelBinary, null, null), cancellationToken))
.BindT(result => FFmpegProfileMustExist(result, cancellationToken));
private async Task<Validation<BaseError, Unit>> SessionMustBeInactive(StartFFmpegNextSession request)
@ -139,35 +140,6 @@ public class StartFFmpegNextSessionHandler( @@ -139,35 +140,6 @@ public class StartFFmpegNextSessionHandler(
return Task.FromResult<Validation<BaseError, Unit>>(Unit.Default);
}
private Task<Validation<BaseError, ValidationResult>> ChannelBinaryMustExist()
{
string nextFolder = SystemEnvironment.NextFolder;
if (string.IsNullOrWhiteSpace(nextFolder))
{
string processFileName = Environment.ProcessPath ?? string.Empty;
string processExecutable = Path.GetFileNameWithoutExtension(processFileName);
nextFolder = Path.GetDirectoryName(processFileName);
if ("dotnet".Equals(processExecutable, StringComparison.OrdinalIgnoreCase))
{
nextFolder = AppContext.BaseDirectory;
}
}
string executable = RuntimeInformation.IsOSPlatform(OSPlatform.Windows)
? "ersatztv-channel.exe"
: "ersatztv-channel";
string channelBinary = fileSystem.Path.Combine(ReplaceTilde(nextFolder), executable);
if (!fileSystem.Path.Exists(channelBinary))
{
return Task.FromResult<Validation<BaseError, ValidationResult>>(
BaseError.New("ersatztv-channel binary does not exist!"));
}
return Task.FromResult<Validation<BaseError, ValidationResult>>(
new ValidationResult(channelBinary, null, null));
}
private async Task<Validation<BaseError, ValidationResult>> ChannelMustExist(
StartFFmpegNextSession request,
ValidationResult result,
@ -201,29 +173,6 @@ public class StartFFmpegNextSessionHandler( @@ -201,29 +173,6 @@ public class StartFFmpegNextSessionHandler(
return BaseError.New($"FFmpeg profile {result.Channel.FFmpegProfileId} not exist");
}
public string ReplaceTilde(string path)
{
if (!path.StartsWith('~'))
{
return path;
}
string userFolder = Environment.GetFolderPath(Environment.SpecialFolder.UserProfile);
switch (path)
{
case "~":
return userFolder;
case not null
when path.Length == 2 &&
(path[1] == fileSystem.Path.DirectorySeparatorChar ||
path[1] == fileSystem.Path.AltDirectorySeparatorChar):
return userFolder + fileSystem.Path.DirectorySeparatorChar;
default:
return fileSystem.Path.Combine(userFolder, path[2..]);
}
}
private async Task<string> GetMultiVariantPlaylist(StartFFmpegNextSession request)
{
var variantPlaylist =
@ -407,7 +356,7 @@ public class StartFFmpegNextSessionHandler( @@ -407,7 +356,7 @@ public class StartFFmpegNextSessionHandler(
}
};
string playoutFolder = fileSystem.Path.Combine(FileSystemLayout.NextPlayoutsFolder, channel.Number, "current");
string playoutFolder = _fileSystem.Path.Combine(FileSystemLayout.NextPlayoutsFolder, channel.Number, "current");
return new ChannelConfig
{

6
ErsatzTV/NextVersion.cs

@ -0,0 +1,6 @@ @@ -0,0 +1,6 @@
namespace ErsatzTV;
public class NextVersion
{
public static string Version { get; set; }
}

7
ErsatzTV/Services/RunOnce/PlatformSettingsService.cs

@ -1,8 +1,10 @@ @@ -1,8 +1,10 @@
using System.Runtime.InteropServices;
using ErsatzTV.Application.Streaming;
using ErsatzTV.Core.Interfaces.Metadata;
using ErsatzTV.FFmpeg.Capabilities;
using ErsatzTV.FFmpeg.Capabilities.Nvidia;
using ErsatzTV.FFmpeg.Runtime;
using MediatR;
using Microsoft.Extensions.Caching.Memory;
namespace ErsatzTV.Services.RunOnce;
@ -58,6 +60,11 @@ public class PlatformSettingsService(IServiceScopeFactory serviceScopeFactory) : @@ -58,6 +60,11 @@ public class PlatformSettingsService(IServiceScopeFactory serviceScopeFactory) :
memoryCache.Set("ffmpeg.vaapi_displays", displays);
}
}
var mediator = scope.ServiceProvider.GetRequiredService<IMediator>();
NextVersion.Version = await mediator.Send(new GetNextVersion(), stoppingToken);
Serilog.Log.Logger.Information("ErsatzTV Next version {Version}", NextVersion.Version);
}
}
}

3
ErsatzTV/Shared/MainLayout.razor

@ -250,6 +250,9 @@ @@ -250,6 +250,9 @@
{
<MudText Typo="Typo.body2" Color="Color.Warning">@BuildConfiguration</MudText>
}
<br />
<MudText Typo="Typo.body2">ErsatzTV Next Version</MudText>
<MudText Typo="Typo.body2" Color="Color.Info">@NextVersion.Version</MudText>
</MudContainer>
</MudNavMenu>
</MudDrawer>

Loading…
Cancel
Save