Directory.Packages.props
+10
-13
diff --git a/Directory.Packages.props b/Directory.Packages.props
index 5c24826..eabda70 100644
@@ -18,21 +18,18 @@
<PackageVersion Include="Microsoft.Extensions.AI" Version="10.5.2" />
<PackageVersion Include="Microsoft.Extensions.AI.Abstractions" Version="10.5.2" />
<PackageVersion Include="Microsoft.Extensions.Hosting" Version="11.0.0-preview.3.26207.106" />
<PackageVersion
Include="Microsoft.Extensions.Hosting.Abstractions"
Version="11.0.0-preview.3.26207.106"
/>
<PackageVersion Include="Microsoft.Extensions.Hosting.Abstractions" Version="11.0.0-preview.3.26207.106" />
<PackageVersion Include="Microsoft.Extensions.Http" Version="11.0.0-preview.3.26207.106" />
<PackageVersion
Include="Microsoft.Extensions.Logging.Abstractions"
Version="11.0.0-preview.3.26207.106"
/>
<PackageVersion Include="Microsoft.Extensions.Logging.Abstractions" Version="11.0.0-preview.3.26207.106" />
<PackageVersion Include="Microsoft.Extensions.Options" Version="11.0.0-preview.3.26207.106" />
<PackageVersion
Include="Microsoft.Extensions.Options.ConfigurationExtensions"
Version="11.0.0-preview.3.26207.106"
/>
<PackageVersion Include="Microsoft.Extensions.Options.ConfigurationExtensions" Version="11.0.0-preview.3.26207.106" />
<PackageVersion Include="OllamaSharp" Version="5.4.25" />
<PackageVersion Include="OpenTelemetry" Version="1.15.3" />
<PackageVersion Include="OpenTelemetry.Exporter.OpenTelemetryProtocol" Version="1.15.3" />
<PackageVersion Include="OpenTelemetry.Extensions.Hosting" Version="1.15.3" />
<PackageVersion Include="OpenTelemetry.Instrumentation.EntityFrameworkCore" Version="1.15.1-beta.1" />
<PackageVersion Include="OpenTelemetry.Instrumentation.Http" Version="1.15.1" />
<PackageVersion Include="OpenTelemetry.Instrumentation.Runtime" Version="1.15.1" />
<PackageVersion Include="SubtitlesParserV2" Version="2.4.0" />
</ItemGroup>
</Project>
</Project>
\ No newline at end of file
src/Cli/Cli.csproj
+5
-0
diff --git a/src/Cli/Cli.csproj b/src/Cli/Cli.csproj
index 4b9fbde..0109a40 100644
@@ -21,5 +21,10 @@
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Extensions.Hosting" />
<PackageReference Include="OpenTelemetry.Exporter.OpenTelemetryProtocol" />
<PackageReference Include="OpenTelemetry.Extensions.Hosting" />
<PackageReference Include="OpenTelemetry.Instrumentation.EntityFrameworkCore" />
<PackageReference Include="OpenTelemetry.Instrumentation.Http" />
<PackageReference Include="OpenTelemetry.Instrumentation.Runtime" />
</ItemGroup>
</Project>
src/Cli/OpenTelemetryExtensions.cs
+56
-0
diff --git a/src/Cli/OpenTelemetryExtensions.cs b/src/Cli/OpenTelemetryExtensions.cs
new file mode 100644
index 0000000..9a6059f
@@ -0,0 +1,56 @@
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using OpenTelemetry;
using OpenTelemetry.Metrics;
using OpenTelemetry.Trace;
using Slopper.Infrastructure.Ffmpeg;
namespace Slopper.Cli;
public static class OpenTelemetryExtensions
{
extension<TBuilder>(TBuilder builder)
where TBuilder : IHostApplicationBuilder
{
public TBuilder ConfigureOpenTelemetry()
{
builder.Logging.AddOpenTelemetry(logging =>
{
logging.IncludeFormattedMessage = true;
logging.IncludeScopes = true;
});
builder
.Services.AddOpenTelemetry()
.WithMetrics(metrics =>
{
metrics.AddHttpClientInstrumentation().AddRuntimeInstrumentation();
})
.WithTracing(tracing =>
{
tracing
.AddSource(builder.Environment.ApplicationName)
.AddHttpClientInstrumentation()
.AddEntityFrameworkCoreInstrumentation()
.AddFfmpegInstrumentation();
});
builder.AddOpenTelemetryExporters();
return builder;
}
private TBuilder AddOpenTelemetryExporters()
{
var useOtlpExporter = !string.IsNullOrWhiteSpace(builder.Configuration["OTEL_EXPORTER_OTLP_ENDPOINT"]);
if (useOtlpExporter)
{
builder.Services.AddOpenTelemetry().UseOtlpExporter();
}
return builder;
}
}
}
src/Cli/Program.cs
+3
-0
diff --git a/src/Cli/Program.cs b/src/Cli/Program.cs
index c04afa7..35f8194 100644
@@ -2,6 +2,7 @@
using System.Threading;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Slopper.Cli;
using Slopper.Domain;
using Slopper.Infrastructure.Ai;
using Slopper.Infrastructure.Database;
@@ -9,6 +10,8 @@ using Slopper.Infrastructure.Ffmpeg;
var builder = Host.CreateApplicationBuilder();
builder.ConfigureOpenTelemetry();
builder.Services.AddClipSelector().AddClipGenerator();
builder.Services.AddJellyfinDatabase().AddSlopperDatabase().AddFfmpegServices().AddAi();
src/Cli/packages.lock.json
+76
-0
diff --git a/src/Cli/packages.lock.json b/src/Cli/packages.lock.json
index f8c58ac..e6d940a 100644
@@ -38,6 +38,56 @@
"Microsoft.Extensions.Options": "11.0.0-preview.3.26207.106"
}
},
"OpenTelemetry.Exporter.OpenTelemetryProtocol": {
"type": "Direct",
"requested": "[1.15.3, )",
"resolved": "1.15.3",
"contentHash": "FEXJepcseTGbATiCkUfP7ipoFEYYfl/0UmmUwi0KxCPg9PaUA8ab2P1LGopK+/HExasJ1ZutFhZrN6WvUIR23g==",
"dependencies": {
"OpenTelemetry": "1.15.3"
}
},
"OpenTelemetry.Extensions.Hosting": {
"type": "Direct",
"requested": "[1.15.3, )",
"resolved": "1.15.3",
"contentHash": "u8n/W8yIlqv0BXZmvId1iVaeWXG42tGKdTkuLYg5g57Y/r9CeUNzqtrSHNdG5IoO8iPX79w3v+WsbAHgUQbfeg==",
"dependencies": {
"Microsoft.Extensions.Hosting.Abstractions": "10.0.0",
"OpenTelemetry": "1.15.3"
}
},
"OpenTelemetry.Instrumentation.EntityFrameworkCore": {
"type": "Direct",
"requested": "[1.15.1-beta.1, )",
"resolved": "1.15.1-beta.1",
"contentHash": "NmtYLk3HJUUkV0g2VnFLs67/PGIVbI8JXtu0g4f9bk2ZaInp211/AL2+Qcbgkb9ih/uOi8loN6E1Aw2o0YIWag==",
"dependencies": {
"Microsoft.Extensions.Configuration": "10.0.0",
"Microsoft.Extensions.Options": "10.0.0",
"OpenTelemetry.Api.ProviderBuilderExtensions": "[1.15.3, 2.0.0)"
}
},
"OpenTelemetry.Instrumentation.Http": {
"type": "Direct",
"requested": "[1.15.1, )",
"resolved": "1.15.1",
"contentHash": "vFO4Fj/dXkoVNGo/nhoGpO2zYQmZwr4jTID7oRGo+XlQ8LqksyZjUXQ4p39RfUvTID7IzzL8Qe71tW7CcAFymA==",
"dependencies": {
"Microsoft.Extensions.Configuration": "10.0.0",
"Microsoft.Extensions.Options": "10.0.0",
"OpenTelemetry.Api.ProviderBuilderExtensions": "[1.15.3, 2.0.0)"
}
},
"OpenTelemetry.Instrumentation.Runtime": {
"type": "Direct",
"requested": "[1.15.1, )",
"resolved": "1.15.1",
"contentHash": "cpPwlUT5HXcLGPaIgsbSy0W9eFYAPGVbTP1p8/uyQ4Osvf5BJuPpEXE7crL09SmEd44r0DGNKDtsqxaAz0HxQw==",
"dependencies": {
"OpenTelemetry.Api": "[1.15.3, 2.0.0)"
}
},
"Instances": {
"type": "Transitive",
"resolved": "3.0.2",
@@ -332,6 +382,20 @@
"resolved": "11.0.0-preview.3.26207.106",
"contentHash": "IBOlwyX13ax6/fXA7AoZFswKFytta9TExBv3/8qemMJGBoDXYlQEcw4WerHQCvmerJ5uP2o8bjIAvxcNdTZVLQ=="
},
"OpenTelemetry.Api": {
"type": "Transitive",
"resolved": "1.15.3",
"contentHash": "fX+fkCysfPut+qCcT3bKqyX4QN9Saf4CgX8HLOHywEVD+Xr7sULtfuypITpoDysjx8R59dn/3mWhgimMH8cm/g=="
},
"OpenTelemetry.Api.ProviderBuilderExtensions": {
"type": "Transitive",
"resolved": "1.15.3",
"contentHash": "SYn0lqYDwLMWhv/zlNGsQcl2yX++yTumanX46bmOZE/ZDOd1WjPBO2kZaZgKLEZTZk48pavIFGJ6vOvxXgWVFQ==",
"dependencies": {
"Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.0",
"OpenTelemetry.Api": "1.15.3"
}
},
"Polly": {
"type": "Transitive",
"resolved": "8.6.5",
@@ -414,6 +478,7 @@
"dependencies": {
"FFMpegCore": "[5.4.0, )",
"Microsoft.Extensions.Logging.Abstractions": "[11.0.0-preview.3.26207.106, )",
"OpenTelemetry": "[1.15.3, )",
"Slopper.Domain": "[1.0.0, )",
"SubtitlesParserV2": "[2.4.0, )"
}
@@ -539,6 +604,17 @@
"Microsoft.Extensions.AI.Abstractions": "10.4.1"
}
},
"OpenTelemetry": {
"type": "CentralTransitive",
"requested": "[1.15.3, )",
"resolved": "1.15.3",
"contentHash": "N0i6WjPoHPbZyms1ugbDIFAJFuGlpeExJMU/+XSL0lQRUkg/D0utFkDoLXf8Z1km5B+xVZ2GyMXXiX8qdeNmPg==",
"dependencies": {
"Microsoft.Extensions.Diagnostics.Abstractions": "10.0.0",
"Microsoft.Extensions.Logging.Configuration": "10.0.0",
"OpenTelemetry.Api.ProviderBuilderExtensions": "1.15.3"
}
},
"SubtitlesParserV2": {
"type": "CentralTransitive",
"requested": "[2.4.0, )",
src/Infrastructure/Ffmpeg/ClipExtractor.cs
+2
-0
diff --git a/src/Infrastructure/Ffmpeg/ClipExtractor.cs b/src/Infrastructure/Ffmpeg/ClipExtractor.cs
index 13c958d..93400ce 100644
@@ -68,6 +68,7 @@ internal sealed partial class ClipExtractor(ILogger<ClipExtractor> logger) : ICl
crops.Add(new(x, y, width, height));
})
.CancellableThrough(cancellationToken);
using var activity = Tracing.StartCropAreaAnalysis(media.Path, start, duration);
logger.LogInformation("Running ffmpeg {FfmpegArguments}", args.Arguments);
await args.ProcessAsynchronously();
return crops.MaxRectangle;
@@ -121,6 +122,7 @@ internal sealed partial class ClipExtractor(ILogger<ClipExtractor> logger) : ICl
.ForceFormat("mp4")
)
.CancellableThrough(cancellationToken);
using var activity = Tracing.StartCreateClip(media.Path, start, duration);
logger.LogInformation("Running ffmpeg {FfmpegArguments}", args.Arguments);
await args.ProcessAsynchronously();
}
src/Infrastructure/Ffmpeg/Ffmpeg.csproj
+1
-0
diff --git a/src/Infrastructure/Ffmpeg/Ffmpeg.csproj b/src/Infrastructure/Ffmpeg/Ffmpeg.csproj
index 733ea29..12b84e3 100644
@@ -9,6 +9,7 @@
<ItemGroup>
<PackageReference Include="FFMpegCore" />
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" />
<PackageReference Include="OpenTelemetry" />
<PackageReference Include="SubtitlesParserV2" />
</ItemGroup>
</Project>
src/Infrastructure/Ffmpeg/OpenTelemetryExtensions.cs
+11
-0
diff --git a/src/Infrastructure/Ffmpeg/OpenTelemetryExtensions.cs b/src/Infrastructure/Ffmpeg/OpenTelemetryExtensions.cs
new file mode 100644
index 0000000..2518b42
@@ -0,0 +1,11 @@
using OpenTelemetry.Trace;
namespace Slopper.Infrastructure.Ffmpeg;
public static class OpenTelemetryExtensions
{
extension(TracerProviderBuilder tracing)
{
public TracerProviderBuilder AddFfmpegInstrumentation() => tracing.AddSource(Tracing.FfmpegActivity.Name);
}
}
src/Infrastructure/Ffmpeg/SubtitleReader.cs
+5
-2
diff --git a/src/Infrastructure/Ffmpeg/SubtitleReader.cs b/src/Infrastructure/Ffmpeg/SubtitleReader.cs
index 369fd05..c117e2f 100644
@@ -36,8 +36,11 @@ internal sealed class SubtitleReader(ILogger<SubtitleReader> logger) : ISubtitle
new StreamPipeSink(stream),
addArguments: options => options.SelectStream(index).ForceFormat("srt")
);
logger.LogInformation("Running ffmpeg {FfmpegArguments}", args.Arguments);
await args.ProcessAsynchronously();
using (Tracing.StartReadSubtitles(path, index))
{
logger.LogInformation("Running ffmpeg {FfmpegArguments}", args.Arguments);
await args.ProcessAsynchronously();
}
stream.Position = 0;
return ReadSubtitles(stream);
}
src/Infrastructure/Ffmpeg/Tracing.cs
+29
-0
diff --git a/src/Infrastructure/Ffmpeg/Tracing.cs b/src/Infrastructure/Ffmpeg/Tracing.cs
new file mode 100644
index 0000000..9e0ce44
@@ -0,0 +1,29 @@
using System;
using System.Diagnostics;
namespace Slopper.Infrastructure.Ffmpeg;
internal static class Tracing
{
internal static ActivitySource FfmpegActivity { get; } = new("Slopper.Infrastructure.Ffmpeg");
public static Activity? StartCropAreaAnalysis(string path, TimeSpan start, TimeSpan duration) =>
FfmpegActivity
.StartActivity("CropAreaAnalysis", ActivityKind.Internal)
?.SetTag("Path", path)
?.SetTag("Start", start)
?.SetTag("Duration", duration);
public static Activity? StartCreateClip(string path, TimeSpan start, TimeSpan duration) =>
FfmpegActivity
.StartActivity("CropAreaAnalysis", ActivityKind.Internal)
?.SetTag("Path", path)
?.SetTag("Start", start)
?.SetTag("Duration", duration);
public static Activity? StartReadSubtitles(string path, int index) =>
FfmpegActivity
.StartActivity("CropAreaAnalysis", ActivityKind.Internal)
?.SetTag("Path", path)
?.SetTag("Index", index);
}
src/Infrastructure/Ffmpeg/packages.lock.json
+67
-0
diff --git a/src/Infrastructure/Ffmpeg/packages.lock.json b/src/Infrastructure/Ffmpeg/packages.lock.json
index 72c0e1e..2ea4d17 100644
@@ -26,6 +26,17 @@
"Microsoft.Extensions.DependencyInjection.Abstractions": "11.0.0-preview.3.26207.106"
}
},
"OpenTelemetry": {
"type": "Direct",
"requested": "[1.15.3, )",
"resolved": "1.15.3",
"contentHash": "N0i6WjPoHPbZyms1ugbDIFAJFuGlpeExJMU/+XSL0lQRUkg/D0utFkDoLXf8Z1km5B+xVZ2GyMXXiX8qdeNmPg==",
"dependencies": {
"Microsoft.Extensions.Diagnostics.Abstractions": "10.0.0",
"Microsoft.Extensions.Logging.Configuration": "10.0.0",
"OpenTelemetry.Api.ProviderBuilderExtensions": "1.15.3"
}
},
"SubtitlesParserV2": {
"type": "Direct",
"requested": "[2.4.0, )",
@@ -66,16 +77,72 @@
"Microsoft.Extensions.Configuration.Abstractions": "11.0.0-preview.3.26207.106"
}
},
"Microsoft.Extensions.DependencyInjection": {
"type": "Transitive",
"resolved": "10.0.0",
"contentHash": "f0RBabswJq+gRu5a+hWIobrLWiUYPKMhCD9WO3sYBAdSy3FFH14LMvLVFZc2kPSCimBLxSuitUhsd6tb0TAY6A==",
"dependencies": {
"Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.0"
}
},
"Microsoft.Extensions.DependencyInjection.Abstractions": {
"type": "Transitive",
"resolved": "11.0.0-preview.3.26207.106",
"contentHash": "+gJnv1/kfXLXPv21R3iluhKqfXdf2zPWUaHBiSvlJurThv2D5HRUfU5z5SpmBII4I0JSpuprX9DlHrKz/1wCXA=="
},
"Microsoft.Extensions.Diagnostics.Abstractions": {
"type": "Transitive",
"resolved": "10.0.0",
"contentHash": "SfK89ytD61S7DgzorFljSkUeluC1ncn6dtZgwc0ot39f/BEYWBl5jpgvodxduoYAs1d9HG8faCDRZxE95UMo2A==",
"dependencies": {
"Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.0",
"Microsoft.Extensions.Options": "10.0.0"
}
},
"Microsoft.Extensions.Logging": {
"type": "Transitive",
"resolved": "10.0.0",
"contentHash": "BStFkd5CcnEtarlcgYDBcFzGYCuuNMzPs02wN3WBsOFoYIEmYoUdAiU+au6opzoqfTYJsMTW00AeqDdnXH2CvA==",
"dependencies": {
"Microsoft.Extensions.DependencyInjection": "10.0.0",
"Microsoft.Extensions.Logging.Abstractions": "10.0.0",
"Microsoft.Extensions.Options": "10.0.0"
}
},
"Microsoft.Extensions.Logging.Configuration": {
"type": "Transitive",
"resolved": "10.0.0",
"contentHash": "j8zcwhS6bYB6FEfaY3nYSgHdpiL2T+/V3xjpHtslVAegyI1JUbB9yAt/BFdvZdsNbY0Udm4xFtvfT/hUwcOOOg==",
"dependencies": {
"Microsoft.Extensions.Configuration": "10.0.0",
"Microsoft.Extensions.Configuration.Abstractions": "10.0.0",
"Microsoft.Extensions.Configuration.Binder": "10.0.0",
"Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.0",
"Microsoft.Extensions.Logging": "10.0.0",
"Microsoft.Extensions.Logging.Abstractions": "10.0.0",
"Microsoft.Extensions.Options": "10.0.0",
"Microsoft.Extensions.Options.ConfigurationExtensions": "10.0.0"
}
},
"Microsoft.Extensions.Primitives": {
"type": "Transitive",
"resolved": "11.0.0-preview.3.26207.106",
"contentHash": "IBOlwyX13ax6/fXA7AoZFswKFytta9TExBv3/8qemMJGBoDXYlQEcw4WerHQCvmerJ5uP2o8bjIAvxcNdTZVLQ=="
},
"OpenTelemetry.Api": {
"type": "Transitive",
"resolved": "1.15.3",
"contentHash": "fX+fkCysfPut+qCcT3bKqyX4QN9Saf4CgX8HLOHywEVD+Xr7sULtfuypITpoDysjx8R59dn/3mWhgimMH8cm/g=="
},
"OpenTelemetry.Api.ProviderBuilderExtensions": {
"type": "Transitive",
"resolved": "1.15.3",
"contentHash": "SYn0lqYDwLMWhv/zlNGsQcl2yX++yTumanX46bmOZE/ZDOd1WjPBO2kZaZgKLEZTZk48pavIFGJ6vOvxXgWVFQ==",
"dependencies": {
"Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.0",
"OpenTelemetry.Api": "1.15.3"
}
},
"Slopper.Domain": {
"type": "Project",
"dependencies": {