📄 src/Cli/Program.cs
using System;
using System.IO;
using System.Threading;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using Slopper.Cli;
using Slopper.Domain;
using Slopper.Domain.Describer;
using Slopper.Infrastructure.Ai;
using Slopper.Infrastructure.Database;
using Slopper.Infrastructure.Ffmpeg;

var builder = Host.CreateApplicationBuilder();

builder.ConfigureOpenTelemetry();

builder.Services.AddClipSelector().AddClipGenerator();

builder.Services.AddJellyfinDatabase().AddSlopperDatabase().AddFfmpegServices().AddAi();

using var app = builder.Build();

await app.StartAsync();

var logger = app.Services.GetRequiredService<ILogger<Program>>();
using var scope = app.Services.CreateScope();
var timeProvider = scope.ServiceProvider.GetRequiredService<TimeProvider>();
var clipSelector = scope.ServiceProvider.GetRequiredService<ClipSelector>();
var clipDescriber = scope.ServiceProvider.GetRequiredService<ClipDescriber>();
var clipExtractor = scope.ServiceProvider.GetRequiredService<IClipExtractor>();
var clipRepository = scope.ServiceProvider.GetRequiredService<IClipRepository>();

var utcNow = timeProvider.GetUtcNow();

var media = new MediaItem(Guid.CreateVersion7(utcNow), args[0], new Subtitles.Embedded(2));

var (start, duration) = await clipSelector.PickClip(media, CancellationToken.None);

var clipId = Guid.CreateVersion7(utcNow);
var clipPath = Path.Join(args[1], $"{clipId}.mp4");

var descriptionTask = clipDescriber.DescribeClip(media, start, duration, CancellationToken.None);

await clipExtractor.ExtractClip(media, start, duration, clipPath, CancellationToken.None);

var description = await descriptionTask;

var clip = new Clip(clipId, media.Id, clipPath, start, duration, utcNow, description.Caption)
{
    Tags = description.Tags,
};
await clipRepository.Save(clip, CancellationToken.None);

await app.StopAsync();