Improve crop editor and copy publish output to dist
Add resize handles, zoom, view brightness, and a multi-frame still strip. Keep the crop box on mouse-up, and copy AmiReel.exe to dist.
This commit is contained in:
@@ -160,6 +160,62 @@ public sealed class ShortsPipeline
|
||||
await _runner.RunAsync(tools.Ffmpeg, BuildExtractFrameArguments(inputFile, start, outputPath), log, null, token);
|
||||
}
|
||||
|
||||
/// <summary>Writes several raw source frames across the clip so the crop editor can switch stills.</summary>
|
||||
public async Task<IReadOnlyList<string>> ExtractSourceFramesAsync(
|
||||
string inputFile,
|
||||
string start,
|
||||
IReadOnlyList<double> offsetsSeconds,
|
||||
string ffmpegPath,
|
||||
string ffprobePath,
|
||||
string outputDirectory,
|
||||
Action<string> log,
|
||||
CancellationToken token)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(inputFile) || !File.Exists(inputFile))
|
||||
throw new FileNotFoundException("Select a valid source video for the Short.");
|
||||
if (offsetsSeconds.Count == 0)
|
||||
throw new ArgumentException("At least one preview offset is required.");
|
||||
|
||||
Directory.CreateDirectory(outputDirectory);
|
||||
ToolPaths tools = await ToolExtractor.ResolveAsync(ffmpegPath, ffprobePath, token);
|
||||
List<string> paths = [];
|
||||
for (int index = 0; index < offsetsSeconds.Count; index++)
|
||||
{
|
||||
string seek = AddTimestampOffset(start, offsetsSeconds[index]);
|
||||
string path = Path.Combine(outputDirectory, $"frame-{index:00}.png");
|
||||
log($"Extracting crop preview {index + 1} of {offsetsSeconds.Count} ({seek})...");
|
||||
await _runner.RunAsync(tools.Ffmpeg, BuildExtractFrameArguments(inputFile, seek, path), log, null, token);
|
||||
paths.Add(path);
|
||||
}
|
||||
|
||||
return paths;
|
||||
}
|
||||
|
||||
internal const int CropPreviewFrameCount = 6;
|
||||
|
||||
internal static IReadOnlyList<double> CropPreviewOffsets(double durationSeconds, int count = CropPreviewFrameCount)
|
||||
{
|
||||
if (count <= 1 || durationSeconds <= 0)
|
||||
return [0];
|
||||
|
||||
double last = Math.Max(0, durationSeconds - 0.15);
|
||||
if (last <= 0)
|
||||
return [0];
|
||||
|
||||
double[] offsets = new double[count];
|
||||
for (int index = 0; index < count; index++)
|
||||
offsets[index] = Math.Round(last * index / (count - 1), 3, MidpointRounding.AwayFromZero);
|
||||
return offsets;
|
||||
}
|
||||
|
||||
internal static string FormatPreviewOffsetLabel(double seconds)
|
||||
{
|
||||
TimeSpan value = TimeSpan.FromSeconds(Math.Max(0, seconds));
|
||||
return value.TotalHours >= 1
|
||||
? value.ToString(@"h\:mm\:ss", CultureInfo.InvariantCulture)
|
||||
: value.ToString(@"m\:ss", CultureInfo.InvariantCulture);
|
||||
}
|
||||
|
||||
internal static List<string> BuildExtractFrameArguments(string inputFile, string start, string outputPath) =>
|
||||
[
|
||||
"-hide_banner",
|
||||
|
||||
Reference in New Issue
Block a user