📄 src/Infrastructure/Ffmpeg/ClipExtractor.cs
using System;
using System.Threading.Tasks;
using FFMpegCore;
using FFMpegCore.Arguments;
using FFMpegCore.Enums;
using Microsoft.Extensions.Logging;
using Slopper.Domain;

namespace Slopper.Infrastructure.Ffmpeg;

public sealed class ClipExtractor(ILogger<ClipExtractor> logger)
{
    public async Task Clip(MediaItem media, TimeSpan start, TimeSpan duration, string outputPath)
    {
        var args = FFMpegArguments
            .FromFileInput(media.Path)
            .OutputToFile(
                outputPath,
                addArguments: options =>
                    options
                        .Seek(start)
                        .EndSeek(start + duration)
                        .WithVideoFilters(filter => filter.HardBurnSubtitle(CreateSubtitleOptions(media)))
                        .WithFastStart()
                        .WithVideoCodec(VideoCodec.LibX264)
                        .WithAudioCodec(AudioCodec.Aac)
                        .WithFramerate(30.0d)
                        .WithVideoBitrate(4000)
                        .WithAudioBitrate(128)
                        .ForceFormat("mp4")
            );
        logger.LogInformation("Running ffmpeg {FfmpegArguments}", args.Arguments);
        await args.ProcessAsynchronously();
    }

    private static SubtitleHardBurnOptions CreateSubtitleOptions(MediaItem media) =>
        (
            media.Subtitles switch
            {
                Subtitles.External(var path) => SubtitleHardBurnOptions.Create(path),
                Subtitles.Embedded(var index) => SubtitleHardBurnOptions.Create(media.Path).SetSubtitleIndex(index),
                _ => throw new ArgumentException("Unknown subtitle type"),
            }
        ).WithParameter(
            "force_style",
            "FontName=Segoe UI,FontSize=20,PrimaryColour=&HFFFFFF&,BackColour=&H000000&,BorderStyle=1,Alignment=2"
        );
}