feat: trying to create clips...probs a waste of time grrrrrrr
This commit is contained in:
@@ -63,7 +63,7 @@ public sealed class GeminiAnalyzer(
|
||||
.Parts?.FirstOrDefault()?
|
||||
.Text;
|
||||
|
||||
var clips = JsonSerializer.Deserialize<List<ShortClip>>(candidatesText ?? string.Empty);
|
||||
var clips = JsonSerializer.Deserialize<List<ShortCandidate>>(candidatesText ?? string.Empty);
|
||||
return new TranscriptAnalysis(clips ?? []);
|
||||
}
|
||||
}
|
||||
|
||||
+2
-2
@@ -3,9 +3,9 @@ using System.Text.Json.Serialization;
|
||||
namespace StreamShorts.Library.Analysis;
|
||||
|
||||
/// <summary>
|
||||
/// Represents a short clip derived from a transcript
|
||||
/// Represents a short candidate derived from a transcript.
|
||||
/// </summary>
|
||||
public record ShortClip(
|
||||
public record ShortCandidate(
|
||||
[property: JsonPropertyName("title")]
|
||||
string Title,
|
||||
[property: JsonPropertyName("description")]
|
||||
@@ -3,10 +3,10 @@ namespace StreamShorts.Library.Analysis;
|
||||
/// <summary>
|
||||
/// Represents the analysis of a transcript, containing short clips derived from the transcript.
|
||||
/// </summary>
|
||||
public sealed class TranscriptAnalysis(IEnumerable<ShortClip> shortClips)
|
||||
public sealed class TranscriptAnalysis(IEnumerable<ShortCandidate> candidates)
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the short clips derived from the transcript.
|
||||
/// </summary>
|
||||
public IEnumerable<ShortClip> ShortClips { get; init; } = shortClips;
|
||||
public IEnumerable<ShortCandidate> Candidates { get; init; } = candidates;
|
||||
}
|
||||
@@ -1,3 +1,4 @@
|
||||
|
||||
using FFMpegCore;
|
||||
using FFMpegCore.Enums;
|
||||
using FFMpegCore.Pipes;
|
||||
@@ -21,4 +22,20 @@ internal sealed class FFMpegService : IVideoService
|
||||
.ProcessAsynchronously()
|
||||
.ConfigureAwait(false);
|
||||
}
|
||||
|
||||
public async Task<Stream> ExtractClipFromVideoAsync(Stream video, TimeSpan start, TimeSpan end, TimeSpan? buffer = null)
|
||||
{
|
||||
var startTime = start - (buffer ?? TimeSpan.Zero);
|
||||
var endTime = end + (buffer ?? TimeSpan.Zero);
|
||||
var outputStream = new MemoryStream();
|
||||
|
||||
await FFMpegArguments
|
||||
.FromPipeInput(new StreamPipeSource(video), o => o.Seek(startTime).EndSeek(endTime))
|
||||
.OutputToPipe(new StreamPipeSink(outputStream), o => o.CopyChannel().ForceFormat("webm"))
|
||||
.ProcessAsynchronously()
|
||||
.ConfigureAwait(false);
|
||||
|
||||
outputStream.Position = 0;
|
||||
return outputStream;
|
||||
}
|
||||
}
|
||||
@@ -18,7 +18,7 @@ internal interface IAudioService
|
||||
/// <summary>
|
||||
/// Gets the number of segments in a WAV stream based on the specified segment duration.
|
||||
/// </summary>
|
||||
/// param name="wavStream">The input WAV stream.</param>
|
||||
/// <param name="wavStream">The input WAV stream.</param>
|
||||
/// <param name="segmentDuration">The duration of each segment.</param>
|
||||
/// <returns>The number of segments.</returns>
|
||||
/// <exception cref="ArgumentNullException">Thrown when the WAV stream is null.</exception>
|
||||
@@ -29,7 +29,7 @@ internal interface IAudioService
|
||||
/// <summary>
|
||||
/// Gets a segment of a WAV stream based on the specified segment number and duration.
|
||||
/// </summary>
|
||||
/// param name="wavStream">The input WAV stream.</param>
|
||||
/// <param name="wavStream">The input WAV stream.</param>
|
||||
/// <param name="segmentNumber">The segment number to retrieve.</param>
|
||||
/// <param name="segmentDuration">The duration of each segment.</param>
|
||||
/// <returns>The segment stream.</returns>
|
||||
|
||||
@@ -12,4 +12,14 @@ internal interface IVideoService
|
||||
/// <param name="audio">The output audio stream.</param>
|
||||
/// <returns>A task that represents the asynchronous operation. The task result indicates whether the extraction was successful.</returns>
|
||||
Task<bool> ExtractAudioFromVideoAsync(Stream video, Stream audio);
|
||||
|
||||
/// <summary>
|
||||
/// Extracts a clip from a video stream.
|
||||
/// </summary>
|
||||
/// <param name="video">The input video stream.</param>
|
||||
/// <param name="start">The start time of the clip.</param>
|
||||
/// <param name="end">The end time of the clip.</param>
|
||||
/// <param name="buffer">The duration of the buffer to include before the start time and after the end time.</param>
|
||||
/// <returns>A task that represents the asynchronous operation. The task result contains the extracted clip as a stream.</returns>
|
||||
Task<Stream> ExtractClipFromVideoAsync(Stream video, TimeSpan start, TimeSpan end, TimeSpan? buffer = null);
|
||||
}
|
||||
@@ -1,5 +1,20 @@
|
||||
using StreamShorts.Library.Analysis;
|
||||
|
||||
namespace StreamShorts.Library.Media.Video;
|
||||
|
||||
/// <summary>
|
||||
/// Represents a service that creates video shorts.
|
||||
/// </summary>
|
||||
public interface IShortsCreator
|
||||
{
|
||||
/// <summary>
|
||||
/// Creates video shorts from the provided transcript analysis and video stream.
|
||||
/// </summary>
|
||||
/// <param name="analysis">The transcript analysis.</param>
|
||||
/// <param name="video">The video stream.</param>
|
||||
/// <param name="buffer">An optional buffer duration to include before the start time and after the end time of each short.</param>
|
||||
/// <returns>An asynchronous enumerable of <see cref="ShortClip"/>.</returns>
|
||||
/// <exception cref="ArgumentNullException">Thrown when <paramref name="analysis"/> is null.</exception>
|
||||
/// <exception cref="ArgumentNullException">Thrown when <paramref name="video"/> is null.</exception>
|
||||
public IAsyncEnumerable<ShortClip> CreateShortsAsync(TranscriptAnalysis analysis, Stream video, TimeSpan? buffer = null);
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
using StreamShorts.Library.Analysis;
|
||||
|
||||
namespace StreamShorts.Library.Media.Video;
|
||||
|
||||
/// <summary>
|
||||
/// Represents a short video clip.
|
||||
/// </summary>
|
||||
public record ShortClip(
|
||||
ShortCandidate Candidate,
|
||||
Stream Segment
|
||||
);
|
||||
@@ -1,5 +1,45 @@
|
||||
using StreamShorts.Library.Analysis;
|
||||
|
||||
namespace StreamShorts.Library.Media.Video;
|
||||
|
||||
/// <summary>
|
||||
/// Represents a service that creates video shorts.
|
||||
/// </summary>
|
||||
/// <inheritdoc/>
|
||||
public class ShortsCreator : IShortsCreator
|
||||
{
|
||||
}
|
||||
private readonly IVideoService _videoService = new FFMpegService();
|
||||
|
||||
public ShortsCreator()
|
||||
{
|
||||
}
|
||||
|
||||
internal ShortsCreator(IVideoService videoService)
|
||||
{
|
||||
_videoService = videoService ?? throw new ArgumentNullException(nameof(videoService), $"{nameof(videoService)} cannot be null");
|
||||
}
|
||||
|
||||
public async IAsyncEnumerable<ShortClip> CreateShortsAsync(TranscriptAnalysis analysis, Stream video, TimeSpan? buffer = null)
|
||||
{
|
||||
if (analysis is null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(analysis), $"{nameof(analysis)} cannot be null");
|
||||
}
|
||||
|
||||
if (video is null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(video), $"{nameof(video)} cannot be null");
|
||||
}
|
||||
|
||||
foreach (var candidate in analysis.Candidates)
|
||||
{
|
||||
var clip = await _videoService.ExtractClipFromVideoAsync(video, candidate.StartTime, candidate.EndTime, buffer)
|
||||
.ConfigureAwait(false);
|
||||
|
||||
yield return new ShortClip(
|
||||
candidate,
|
||||
clip
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user