diff --git a/.github/workflows/publish_console.yml b/.github/workflows/publish_console.yml
index 7210d03..1d52733 100644
--- a/.github/workflows/publish_console.yml
+++ b/.github/workflows/publish_console.yml
@@ -5,6 +5,10 @@ on:
paths:
- src/StreamShorts.Console/**
- src/StreamShorts.Console.Tests/**
+ - src/StreamShorts.Library/**
+ - src/StreamShorts.Library.Tests/**
+ - StreamShorts.sln
+ - Directory.Build.props
branches:
- main
jobs:
diff --git a/src/StreamShorts.Console/Commands/DefaultCommand.cs b/src/StreamShorts.Console/Commands/DefaultCommand.cs
index f8f0e58..e469c7c 100644
--- a/src/StreamShorts.Console/Commands/DefaultCommand.cs
+++ b/src/StreamShorts.Console/Commands/DefaultCommand.cs
@@ -1,9 +1,8 @@
-using System.Text.Json;
-
-using StreamShorts.Library.Media.Video;
-
namespace StreamShorts.Console.Commands;
+///
+/// The default command for processing video streams to create short clips.
+///
internal sealed class DefaultCommand(
IFileSystem fileSystem,
IAnsiConsole console,
@@ -28,8 +27,14 @@ internal sealed class DefaultCommand(
private readonly IShortsCreator _shortsCreator = shortsCreator ?? throw new ArgumentNullException(nameof(shortsCreator));
private readonly TimeProvider _timeProvider = timeProvider ?? throw new ArgumentNullException(nameof(timeProvider));
+ ///
+ /// Represents the settings for the default command.
+ ///
internal class Settings : CommandSettings
{
+ ///
+ /// Gets or sets the path to the stream.
+ ///
[CommandArgument(0, "[Stream]")]
[Description("The path to the stream")]
public string Stream { get; init; } = string.Empty;
@@ -103,7 +108,7 @@ internal sealed class DefaultCommand(
);
_fileSystem.Directory.CreateDirectory(outputDirectoryPath);
-
+
await _fileSystem.File.WriteAllTextAsync(
_fileSystem.Path.Combine(outputDirectoryPath, "transcription.txt"),
string.Join(Environment.NewLine, transcriptionSegments)
diff --git a/src/StreamShorts.Console/Hosting/HostBuilderExtensions.cs b/src/StreamShorts.Console/Hosting/HostBuilderExtensions.cs
index 374372e..28c1516 100644
--- a/src/StreamShorts.Console/Hosting/HostBuilderExtensions.cs
+++ b/src/StreamShorts.Console/Hosting/HostBuilderExtensions.cs
@@ -1,7 +1,15 @@
namespace StreamShorts.Console.Hosting;
+///
+/// Provides extension methods for building command applications from host builders.
+///
internal static class HostBuilderExtensions
{
+ ///
+ /// Builds a command application from the host builder.
+ ///
+ /// The host builder.
+ /// A configured command application.
public static CommandApp BuildApp(this IHostBuilder builder)
{
var registrar = new TypeRegistrar(builder);
diff --git a/src/StreamShorts.Console/Hosting/TypeRegistrar.cs b/src/StreamShorts.Console/Hosting/TypeRegistrar.cs
index e8bf08d..bf236e0 100644
--- a/src/StreamShorts.Console/Hosting/TypeRegistrar.cs
+++ b/src/StreamShorts.Console/Hosting/TypeRegistrar.cs
@@ -1,5 +1,9 @@
namespace StreamShorts.Console.Hosting;
+///
+/// Provides type registration services for the dependency injection container.
+///
+///
internal sealed class TypeRegistrar(IHostBuilder builder) : ITypeRegistrar
{
private readonly IHostBuilder _builder = builder;
diff --git a/src/StreamShorts.Console/Hosting/TypeResolver.cs b/src/StreamShorts.Console/Hosting/TypeResolver.cs
index ae3ac08..8814c1a 100644
--- a/src/StreamShorts.Console/Hosting/TypeResolver.cs
+++ b/src/StreamShorts.Console/Hosting/TypeResolver.cs
@@ -1,5 +1,9 @@
namespace StreamShorts.Console.Hosting;
+///
+/// Provides type resolution services using the dependency injection container.
+///
+///
internal sealed class TypeResolver(IHost provider) : ITypeResolver, IDisposable
{
private readonly IHost _host = provider ?? throw new ArgumentNullException(nameof(provider));
diff --git a/src/StreamShorts.Console/Program.cs b/src/StreamShorts.Console/Program.cs
index 59d69cf..3da4dab 100644
--- a/src/StreamShorts.Console/Program.cs
+++ b/src/StreamShorts.Console/Program.cs
@@ -1,8 +1,4 @@
-using Microsoft.Extensions.Configuration;
-
-using StreamShorts.Library.Media.Video;
-
-Log.Logger = new LoggerConfiguration()
+Log.Logger = new LoggerConfiguration()
.WriteTo.File(
formatter: new CompactJsonFormatter(),
path: Path.Combine(AppContext.BaseDirectory, "logs", "log.jsonl"),
@@ -42,7 +38,7 @@ try
{
throw new InvalidOperationException($"{keyOptionName} is not configured in appsettings.json.");
}
-
+
var clientFactory = sp.GetRequiredService();
return new GeminiAnalyzer(clientFactory, key, model);
@@ -62,4 +58,4 @@ catch (Exception ex)
finally
{
await Log.CloseAndFlushAsync();
-}
+}
\ No newline at end of file
diff --git a/src/StreamShorts.Console/StreamShorts.Console.csproj b/src/StreamShorts.Console/StreamShorts.Console.csproj
index ecca060..4b2629c 100644
--- a/src/StreamShorts.Console/StreamShorts.Console.csproj
+++ b/src/StreamShorts.Console/StreamShorts.Console.csproj
@@ -1,7 +1,14 @@
+ StreamShorts.Console
+ StreamShorts.Console
+ A command-line interface for StreamShorts
+ 0.0.0
+ Stevan Freeborn
Exe
+ true
+ true
diff --git a/src/StreamShorts.Console/Usings.cs b/src/StreamShorts.Console/Usings.cs
index fd2723d..c076a35 100644
--- a/src/StreamShorts.Console/Usings.cs
+++ b/src/StreamShorts.Console/Usings.cs
@@ -1,7 +1,9 @@
global using System.ComponentModel;
global using System.IO.Abstractions;
global using System.Reflection;
+global using System.Text.Json;
+global using Microsoft.Extensions.Configuration;
global using Microsoft.Extensions.DependencyInjection;
global using Microsoft.Extensions.Hosting;
global using Microsoft.Extensions.Logging;
@@ -18,4 +20,5 @@ global using StreamShorts.Console.Hosting;
global using StreamShorts.Library.Analysis;
global using StreamShorts.Library.Analysis.Gemini;
global using StreamShorts.Library.Media.Audio;
-global using StreamShorts.Library.Transcription;
+global using StreamShorts.Library.Media.Video;
+global using StreamShorts.Library.Transcription;
\ No newline at end of file
diff --git a/src/StreamShorts.Library/Analysis/FailedTranscriptAnalysisException.cs b/src/StreamShorts.Library/Analysis/FailedTranscriptAnalysisException.cs
index c918499..eae15a9 100644
--- a/src/StreamShorts.Library/Analysis/FailedTranscriptAnalysisException.cs
+++ b/src/StreamShorts.Library/Analysis/FailedTranscriptAnalysisException.cs
@@ -28,4 +28,4 @@ public sealed class FailedTranscriptAnalysisException : Exception
public FailedTranscriptAnalysisException(string message, Exception innerException) : base(message, innerException)
{
}
-}
+}
\ No newline at end of file
diff --git a/src/StreamShorts.Library/Analysis/Gemini/Content.cs b/src/StreamShorts.Library/Analysis/Gemini/Content.cs
index 66615a9..3810ab1 100644
--- a/src/StreamShorts.Library/Analysis/Gemini/Content.cs
+++ b/src/StreamShorts.Library/Analysis/Gemini/Content.cs
@@ -1,5 +1,10 @@
using System.Text.Json.Serialization;
+///
+/// Represents content with role and parts for Gemini API.
+///
+/// The role of the content (e.g., "user", "assistant").
+/// The array of content parts.
internal record Content(
[property: JsonPropertyName("role")]
string Role,
@@ -7,6 +12,10 @@ internal record Content(
Part[] Parts
);
+///
+/// Represents a part of content for Gemini API.
+///
+/// The text content of the part.
internal record Part(
[property: JsonPropertyName("text")]
string Text
diff --git a/src/StreamShorts.Library/Analysis/Gemini/GenerateContentRequest.cs b/src/StreamShorts.Library/Analysis/Gemini/GenerateContentRequest.cs
index f4bd08e..e6a2989 100644
--- a/src/StreamShorts.Library/Analysis/Gemini/GenerateContentRequest.cs
+++ b/src/StreamShorts.Library/Analysis/Gemini/GenerateContentRequest.cs
@@ -2,6 +2,11 @@ using System.Text.Json.Serialization;
namespace StreamShorts.Library.Analysis.Gemini;
+///
+/// Represents a request to generate content using Gemini.
+///
+/// The content array for the request.
+/// The generation configuration.
internal record GenerateContentRequest(
[property: JsonPropertyName("contents")]
Content[] Contents,
@@ -9,6 +14,10 @@ internal record GenerateContentRequest(
GenerationConfig GenerationConfig
);
+///
+/// Represents configuration for content generation.
+///
+/// The MIME type for the response.
internal record GenerationConfig(
[property: JsonPropertyName("responseMimeType")]
string ResponseMimeType
diff --git a/src/StreamShorts.Library/Analysis/Gemini/GenerateContentResponse.cs b/src/StreamShorts.Library/Analysis/Gemini/GenerateContentResponse.cs
index 4a45668..7a87edd 100644
--- a/src/StreamShorts.Library/Analysis/Gemini/GenerateContentResponse.cs
+++ b/src/StreamShorts.Library/Analysis/Gemini/GenerateContentResponse.cs
@@ -2,13 +2,20 @@ using System.Text.Json.Serialization;
namespace StreamShorts.Library.Analysis.Gemini;
+///
+/// Represents a response from the Gemini content generation API.
+///
+/// The array of candidate responses.
internal record GenerateContentResponse(
[property: JsonPropertyName("candidates")]
Candidate[] Candidates
);
+///
+/// Represents a candidate response from content generation.
+///
+/// The generated content.
internal record Candidate(
[property: JsonPropertyName("content")]
Content Content
-);
-
+);
\ No newline at end of file
diff --git a/src/StreamShorts.Library/Analysis/ShortCandidate.cs b/src/StreamShorts.Library/Analysis/ShortCandidate.cs
index 42433cc..2828185 100644
--- a/src/StreamShorts.Library/Analysis/ShortCandidate.cs
+++ b/src/StreamShorts.Library/Analysis/ShortCandidate.cs
@@ -16,4 +16,4 @@ public record ShortCandidate(
TimeSpan StartTime,
[property: JsonPropertyName("end_time")]
TimeSpan EndTime
-);
+);
\ No newline at end of file
diff --git a/src/StreamShorts.Library/Media/Audio/AudioExtractor.cs b/src/StreamShorts.Library/Media/Audio/AudioExtractor.cs
index cd9d7d3..6e0a3f0 100644
--- a/src/StreamShorts.Library/Media/Audio/AudioExtractor.cs
+++ b/src/StreamShorts.Library/Media/Audio/AudioExtractor.cs
@@ -16,10 +16,10 @@ public sealed class AudioExtractor : IAudioExtractor
}
///
- /// Initializes a new instance of the class with a specified FFMpeg service.
+ /// Initializes a new instance of the class with a specified .
///
/// The video service to use for audio extraction.
- /// Thrown when the FFMpeg service is null.
+ /// Thrown when the video service is null.
internal AudioExtractor(IVideoService videoService)
{
_videoService = videoService ?? throw new ArgumentNullException(nameof(videoService), $"{nameof(videoService)} cannot be null");
diff --git a/src/StreamShorts.Library/Media/FFMpegService.cs b/src/StreamShorts.Library/Media/FFMpegService.cs
index 3f74a72..8ab00fd 100644
--- a/src/StreamShorts.Library/Media/FFMpegService.cs
+++ b/src/StreamShorts.Library/Media/FFMpegService.cs
@@ -23,8 +23,8 @@ internal sealed class FFMpegService : IVideoService
}
public async Task CreateClipFromVideoAsync(
- string sourcePath,
- string destinationPath,
+ string sourcePath,
+ string destinationPath,
TimeSpan startTime,
TimeSpan endTime,
TimeSpan? buffer = null
@@ -32,7 +32,7 @@ internal sealed class FFMpegService : IVideoService
{
var start = startTime - (buffer ?? TimeSpan.Zero);
var end = endTime + (buffer ?? TimeSpan.Zero);
-
+
await FFMpeg.SubVideoAsync(sourcePath, destinationPath, start, end)
.ConfigureAwait(false);
}
diff --git a/src/StreamShorts.Library/Media/IVideoService.cs b/src/StreamShorts.Library/Media/IVideoService.cs
index 2818580..2c5bef1 100644
--- a/src/StreamShorts.Library/Media/IVideoService.cs
+++ b/src/StreamShorts.Library/Media/IVideoService.cs
@@ -12,7 +12,7 @@ internal interface IVideoService
/// The output audio stream.
/// A task that represents the asynchronous operation. The task result indicates whether the extraction was successful.
Task ExtractAudioFromVideoAsync(Stream video, Stream audio);
-
+
///
/// Creates a clip from a video file based on the specified start and end times.
///
diff --git a/src/StreamShorts.Library/Media/Video/ShortsCreator.cs b/src/StreamShorts.Library/Media/Video/ShortsCreator.cs
index 04d7240..f314f0f 100644
--- a/src/StreamShorts.Library/Media/Video/ShortsCreator.cs
+++ b/src/StreamShorts.Library/Media/Video/ShortsCreator.cs
@@ -6,14 +6,22 @@ namespace StreamShorts.Library.Media.Video;
/// Represents a service that creates video shorts.
///
///
-public class ShortsCreator : IShortsCreator
+public sealed class ShortsCreator : IShortsCreator
{
private readonly IVideoService _videoService = new FFMpegService();
+ ///
+ /// Initializes a new instance of the class.
+ ///
public ShortsCreator()
{
}
+ ///
+ /// Initializes a new instance of the class with a specified .
+ ///
+ /// The video service.
+ /// Thrown when the video service is null.
internal ShortsCreator(IVideoService videoService)
{
_videoService = videoService ?? throw new ArgumentNullException(nameof(videoService), $"{nameof(videoService)} cannot be null");
@@ -25,17 +33,17 @@ public class ShortsCreator : IShortsCreator
{
throw new ArgumentNullException(nameof(sourcePath), $"{nameof(sourcePath)} cannot be null or whitespace");
}
-
+
if (candidate is null)
{
throw new ArgumentNullException(nameof(candidate), $"{nameof(candidate)} cannot be null");
}
-
+
if (string.IsNullOrWhiteSpace(destinationPath))
{
throw new ArgumentNullException(nameof(destinationPath), $"{nameof(destinationPath)} cannot be null or whitespace");
}
-
+
await _videoService.CreateClipFromVideoAsync(sourcePath, destinationPath, candidate.StartTime, candidate.EndTime, buffer)
.ConfigureAwait(false);
}
diff --git a/src/StreamShorts.Library/Transcription/ITranscriber.cs b/src/StreamShorts.Library/Transcription/ITranscriber.cs
index 207c854..7c4df20 100644
--- a/src/StreamShorts.Library/Transcription/ITranscriber.cs
+++ b/src/StreamShorts.Library/Transcription/ITranscriber.cs
@@ -12,4 +12,4 @@ public interface ITranscriber
/// A cancellation token to cancel the operation.
/// An where T is .
IAsyncEnumerable TranscribeAsync(Stream audio, CancellationToken cancellationToken = default);
-}
+}
\ No newline at end of file
diff --git a/tests/StreamShorts.Console.Tests/Unit/Hosting/TypeRegistrarTests.cs b/tests/StreamShorts.Console.Tests/Unit/Hosting/TypeRegistrarTests.cs
index 1b76528..30b61b7 100644
--- a/tests/StreamShorts.Console.Tests/Unit/Hosting/TypeRegistrarTests.cs
+++ b/tests/StreamShorts.Console.Tests/Unit/Hosting/TypeRegistrarTests.cs
@@ -1,6 +1,6 @@
namespace StreamShorts.Console.Tests.Unit.Hosting;
-public class TypeRegistrarTests
+internal class TypeRegistrarTests
{
[Fact]
public void Constructor_WhenCalled_ItShouldNotThrowShould()
diff --git a/tests/StreamShorts.Console.Tests/Unit/Hosting/TypeResolverTests.cs b/tests/StreamShorts.Console.Tests/Unit/Hosting/TypeResolverTests.cs
index 8cfaf73..0533c80 100644
--- a/tests/StreamShorts.Console.Tests/Unit/Hosting/TypeResolverTests.cs
+++ b/tests/StreamShorts.Console.Tests/Unit/Hosting/TypeResolverTests.cs
@@ -1,6 +1,6 @@
namespace StreamShorts.Console.Tests.Unit.Hosting;
-public class TypeResolverTests
+internal class TypeResolverTests
{
[Fact]
public void Constructor_WhenCalledWithNullHost_ItShouldThrowArgumentNullException()
diff --git a/tests/StreamShorts.Console.Tests/Usings.cs b/tests/StreamShorts.Console.Tests/Usings.cs
index 7c8e815..a3ca487 100644
--- a/tests/StreamShorts.Console.Tests/Usings.cs
+++ b/tests/StreamShorts.Console.Tests/Usings.cs
@@ -5,4 +5,4 @@ global using Microsoft.Extensions.Hosting;
global using Moq;
-global using StreamShorts.Console.Hosting;
+global using StreamShorts.Console.Hosting;
\ No newline at end of file
diff --git a/tests/StreamShorts.Library.Tests/Integration/Media/Audio/AudioExtractorTests.cs b/tests/StreamShorts.Library.Tests/Integration/Media/Audio/AudioExtractorTests.cs
index 39ca12d..ab5bbba 100644
--- a/tests/StreamShorts.Library.Tests/Integration/Media/Audio/AudioExtractorTests.cs
+++ b/tests/StreamShorts.Library.Tests/Integration/Media/Audio/AudioExtractorTests.cs
@@ -1,6 +1,6 @@
namespace StreamShorts.Library.Tests.Integration.Media.Audio;
-public class AudioExtractorTests
+internal class AudioExtractorTests
{
private readonly AudioExtractor _sut = new();
diff --git a/tests/StreamShorts.Library.Tests/Unit/Media/Audio/AudioExtractorTests.cs b/tests/StreamShorts.Library.Tests/Unit/Media/Audio/AudioExtractorTests.cs
index d70f76b..366bdd8 100644
--- a/tests/StreamShorts.Library.Tests/Unit/Media/Audio/AudioExtractorTests.cs
+++ b/tests/StreamShorts.Library.Tests/Unit/Media/Audio/AudioExtractorTests.cs
@@ -4,7 +4,7 @@ using StreamShorts.Library.Media;
namespace StreamShorts.Library.Tests.Unit.Media.Audio;
-public class AudioExtractorTests
+internal class AudioExtractorTests
{
private readonly Mock _mockFfmpegService = new();
private readonly AudioExtractor _sut;