mirror of https://github.com/ErsatzTV/ErsatzTV.git
25 changed files with 14203 additions and 13 deletions
@ -0,0 +1,3 @@
@@ -0,0 +1,3 @@
|
||||
namespace ErsatzTV.Application.Channels; |
||||
|
||||
public record GetSlugSecondsByChannelNumber(string ChannelNumber) : IRequest<Option<double>>; |
||||
@ -0,0 +1,17 @@
@@ -0,0 +1,17 @@
|
||||
using ErsatzTV.Infrastructure.Data; |
||||
using Microsoft.EntityFrameworkCore; |
||||
|
||||
namespace ErsatzTV.Application.Channels; |
||||
|
||||
public class GetSlugSecondsByChannelNumberHandler(IDbContextFactory<TvContext> dbContextFactory) |
||||
: IRequestHandler<GetSlugSecondsByChannelNumber, Option<double>> |
||||
{ |
||||
public async Task<Option<double>> Handle(GetSlugSecondsByChannelNumber request, CancellationToken cancellationToken) |
||||
{ |
||||
await using TvContext dbContext = await dbContextFactory.CreateDbContextAsync(cancellationToken); |
||||
return await dbContext.Channels |
||||
.AsNoTracking() |
||||
.SingleOrDefaultAsync(c => c.Number == request.ChannelNumber, cancellationToken) |
||||
.Map(c => Optional(c?.SlugSeconds)); |
||||
} |
||||
} |
||||
@ -0,0 +1,22 @@
@@ -0,0 +1,22 @@
|
||||
using ErsatzTV.Core.Domain; |
||||
using ErsatzTV.FFmpeg; |
||||
|
||||
namespace ErsatzTV.Application.Streaming; |
||||
|
||||
public record GetSlugProcessByChannelNumber( |
||||
string ChannelNumber, |
||||
StreamingMode Mode, |
||||
DateTimeOffset Now, |
||||
bool HlsRealtime, |
||||
DateTimeOffset ChannelStart, |
||||
TimeSpan PtsOffset, |
||||
Option<FrameRate> TargetFramerate, |
||||
Option<double> SlugSeconds) : FFmpegProcessRequest( |
||||
ChannelNumber, |
||||
Mode, |
||||
Now, |
||||
StartAtZero: true, |
||||
HlsRealtime, |
||||
ChannelStart, |
||||
PtsOffset, |
||||
FFmpegProfileId: Option<int>.None); |
||||
@ -0,0 +1,111 @@
@@ -0,0 +1,111 @@
|
||||
using System.IO.Abstractions; |
||||
using ErsatzTV.Core; |
||||
using ErsatzTV.Core.Domain; |
||||
using ErsatzTV.Core.Domain.Filler; |
||||
using ErsatzTV.Core.FFmpeg; |
||||
using ErsatzTV.Core.Interfaces.FFmpeg; |
||||
using ErsatzTV.Core.Interfaces.Metadata; |
||||
using ErsatzTV.FFmpeg; |
||||
using ErsatzTV.Infrastructure.Data; |
||||
using ErsatzTV.Infrastructure.Extensions; |
||||
using Microsoft.EntityFrameworkCore; |
||||
|
||||
namespace ErsatzTV.Application.Streaming; |
||||
|
||||
public class GetSlugProcessByChannelNumberHandler( |
||||
IDbContextFactory<TvContext> dbContextFactory, |
||||
IFileSystem fileSystem, |
||||
IFFmpegProcessService ffmpegProcessService, |
||||
ILocalStatisticsProvider localStatisticsProvider) |
||||
: FFmpegProcessHandler<GetSlugProcessByChannelNumber>(dbContextFactory) |
||||
{ |
||||
protected override async Task<Either<BaseError, PlayoutItemProcessModel>> GetProcess( |
||||
TvContext dbContext, |
||||
GetSlugProcessByChannelNumber request, |
||||
Channel channel, |
||||
string ffmpegPath, |
||||
string ffprobePath, |
||||
CancellationToken cancellationToken) |
||||
{ |
||||
string videoPath = fileSystem.Path.Combine(FileSystemLayout.ResourcesCacheFolder, "slug.mp4"); |
||||
|
||||
bool saveReports = await dbContext.ConfigElements |
||||
.GetValue<bool>(ConfigElementKey.FFmpegSaveReports, cancellationToken) |
||||
.Map(result => result.IfNone(false)); |
||||
|
||||
Either<BaseError, MediaVersion> maybeVersion = |
||||
await localStatisticsProvider.GetStatistics(ffprobePath, videoPath); |
||||
foreach (var error in maybeVersion.LeftToSeq()) |
||||
{ |
||||
return error; |
||||
} |
||||
|
||||
var version = maybeVersion.RightToSeq().Head(); |
||||
|
||||
var mediaItem = new OtherVideo |
||||
{ |
||||
MediaVersions = [version] |
||||
}; |
||||
|
||||
TimeSpan duration = version.Duration; |
||||
foreach (double slugSeconds in request.SlugSeconds) |
||||
{ |
||||
TimeSpan seconds = TimeSpan.FromSeconds(slugSeconds); |
||||
if (seconds > TimeSpan.Zero && seconds < duration) |
||||
{ |
||||
duration = seconds; |
||||
} |
||||
} |
||||
|
||||
DateTimeOffset finish = request.Now.Add(duration); |
||||
|
||||
PlayoutItemResult playoutItemResult = await ffmpegProcessService.ForPlayoutItem( |
||||
ffmpegPath, |
||||
ffprobePath, |
||||
saveReports, |
||||
channel, |
||||
new MediaItemVideoVersion(mediaItem, version), |
||||
new MediaItemAudioVersion(mediaItem, version), |
||||
videoPath, |
||||
videoPath, |
||||
_ => Task.FromResult<List<Subtitle>>([]), |
||||
string.Empty, |
||||
string.Empty, |
||||
string.Empty, |
||||
ChannelSubtitleMode.None, |
||||
request.Now, |
||||
finish, |
||||
request.Now, |
||||
duration, |
||||
[], |
||||
[], |
||||
channel.FFmpegProfile.VaapiDisplay, |
||||
channel.FFmpegProfile.VaapiDriver, |
||||
channel.FFmpegProfile.VaapiDevice, |
||||
Optional(channel.FFmpegProfile.QsvExtraHardwareFrames), |
||||
request.HlsRealtime, |
||||
StreamInputKind.Vod, |
||||
FillerKind.None, |
||||
inPoint: TimeSpan.Zero, |
||||
request.ChannelStartTime, |
||||
request.PtsOffset, |
||||
request.TargetFramerate, |
||||
Option<string>.None, |
||||
_ => { }, |
||||
canProxy: true, |
||||
cancellationToken); |
||||
|
||||
var result = new PlayoutItemProcessModel( |
||||
playoutItemResult.Process, |
||||
playoutItemResult.GraphicsEngineContext, |
||||
duration, |
||||
finish, |
||||
isComplete: true, |
||||
request.Now.ToUnixTimeSeconds(), |
||||
playoutItemResult.MediaItemId, |
||||
Optional(channel.PlayoutOffset), |
||||
!request.HlsRealtime); |
||||
|
||||
return Right<BaseError, PlayoutItemProcessModel>(result); |
||||
} |
||||
} |
||||
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,28 @@
@@ -0,0 +1,28 @@
|
||||
using Microsoft.EntityFrameworkCore.Migrations; |
||||
|
||||
#nullable disable |
||||
|
||||
namespace ErsatzTV.Infrastructure.MySql.Migrations |
||||
{ |
||||
/// <inheritdoc />
|
||||
public partial class Add_ChannelSlugSeconds : Migration |
||||
{ |
||||
/// <inheritdoc />
|
||||
protected override void Up(MigrationBuilder migrationBuilder) |
||||
{ |
||||
migrationBuilder.AddColumn<double>( |
||||
name: "SlugSeconds", |
||||
table: "Channel", |
||||
type: "double", |
||||
nullable: true); |
||||
} |
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Down(MigrationBuilder migrationBuilder) |
||||
{ |
||||
migrationBuilder.DropColumn( |
||||
name: "SlugSeconds", |
||||
table: "Channel"); |
||||
} |
||||
} |
||||
} |
||||
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,28 @@
@@ -0,0 +1,28 @@
|
||||
using Microsoft.EntityFrameworkCore.Migrations; |
||||
|
||||
#nullable disable |
||||
|
||||
namespace ErsatzTV.Infrastructure.Sqlite.Migrations |
||||
{ |
||||
/// <inheritdoc />
|
||||
public partial class Add_ChannelSlugSeconds : Migration |
||||
{ |
||||
/// <inheritdoc />
|
||||
protected override void Up(MigrationBuilder migrationBuilder) |
||||
{ |
||||
migrationBuilder.AddColumn<double>( |
||||
name: "SlugSeconds", |
||||
table: "Channel", |
||||
type: "REAL", |
||||
nullable: true); |
||||
} |
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Down(MigrationBuilder migrationBuilder) |
||||
{ |
||||
migrationBuilder.DropColumn( |
||||
name: "SlugSeconds", |
||||
table: "Channel"); |
||||
} |
||||
} |
||||
} |
||||
Binary file not shown.
Loading…
Reference in new issue