Add video preview, source crop, find-videos, and render toasts
Bring the Video tab closer to Shorts with 4K preview stills and crop modes (none, auto, center, plus the visual editor). Settings store a default source folder so Find videos can collect output.avi sequences. Cap the FFmpeg log with auto-scroll, hide repeat spam, and show a Windows notification when a video or short finishes or fails.
This commit is contained in:
@@ -127,6 +127,154 @@ public class RenderPipelineTests
|
||||
RenderPipeline.Validate(settings);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void ValidatePreview_WithMissingOutputName_DoesNotThrow()
|
||||
{
|
||||
// Arrange
|
||||
string directory = CreateTempDirectory();
|
||||
string input = Path.Combine(directory, "clip.avi");
|
||||
File.WriteAllText(input, "data");
|
||||
RenderSettings settings = ValidSettings([input], "") with { OutputName = "" };
|
||||
|
||||
// Act & Assert (no exception)
|
||||
RenderPipeline.ValidatePreview(settings);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void ValidatePreview_WithNoInputFiles_ThrowsArgumentException()
|
||||
{
|
||||
// Arrange
|
||||
RenderSettings settings = ValidSettings([], @"C:\out");
|
||||
|
||||
// Act & Assert
|
||||
Assert.ThrowsExactly<ArgumentException>(() => RenderPipeline.ValidatePreview(settings));
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void BuildJoinVideoFilter_IncludesCropThenScaleAndPad()
|
||||
{
|
||||
// Arrange
|
||||
RenderSettings settings = ValidSettings(["clip.avi"], @"C:\out");
|
||||
|
||||
// Act
|
||||
string filter = RenderPipeline.BuildJoinVideoFilter(settings, "crop=iw-10-10:ih-4-4:10:4");
|
||||
|
||||
// Assert
|
||||
StringAssert.StartsWith(filter, "crop=iw-10-10:ih-4-4:10:4,scale=2880:2160");
|
||||
StringAssert.Contains(filter, "pad=3840:2160");
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void BuildSourceCropFilter_WithManualInsets_UsesEdgeCrop()
|
||||
{
|
||||
// Arrange
|
||||
RenderSettings settings = ValidSettings(["clip.avi"], @"C:\out") with
|
||||
{
|
||||
CropLeft = 20,
|
||||
CropRight = 10,
|
||||
CropTop = 4,
|
||||
CropBottom = 6,
|
||||
};
|
||||
|
||||
// Act
|
||||
string filter = RenderPipeline.BuildSourceCropFilter(settings, "crop=800:ih:40:0");
|
||||
|
||||
// Assert
|
||||
Assert.AreEqual("crop=iw-20-10:ih-4-6:20:4", filter);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void BuildSourceCropFilter_WithAutoDetection_UsesDetectedFilter()
|
||||
{
|
||||
// Arrange
|
||||
RenderSettings settings = ValidSettings(["clip.avi"], @"C:\out") with
|
||||
{
|
||||
SourceCropMode = ShortSourceCropMode.Auto,
|
||||
};
|
||||
|
||||
// Act
|
||||
string filter = RenderPipeline.BuildSourceCropFilter(settings, "crop=800:ih:40:0");
|
||||
|
||||
// Assert
|
||||
Assert.AreEqual("crop=800:ih:40:0", filter);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void BuildSourceCropFilter_WithCenterMode_UsesFourByThreeCrop()
|
||||
{
|
||||
// Arrange
|
||||
RenderSettings settings = ValidSettings(["clip.avi"], @"C:\out") with
|
||||
{
|
||||
SourceCropMode = ShortSourceCropMode.Center,
|
||||
};
|
||||
|
||||
// Act
|
||||
string filter = RenderPipeline.BuildSourceCropFilter(settings, "crop=800:ih:40:0");
|
||||
|
||||
// Assert
|
||||
Assert.AreEqual(ShortsPipeline.DefaultSourceCropFilter, filter);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void BuildSourceCropFilter_WithNoneMode_AppliesNoCrop()
|
||||
{
|
||||
// Arrange
|
||||
RenderSettings settings = ValidSettings(["clip.avi"], @"C:\out") with
|
||||
{
|
||||
SourceCropMode = ShortSourceCropMode.None,
|
||||
};
|
||||
|
||||
// Act
|
||||
string filter = RenderPipeline.BuildSourceCropFilter(settings, "crop=800:ih:40:0");
|
||||
|
||||
// Assert
|
||||
Assert.AreEqual("", filter);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void BuildJoinVideoFilter_WithNoCrop_StartsAtScale()
|
||||
{
|
||||
// Arrange
|
||||
RenderSettings settings = ValidSettings(["clip.avi"], @"C:\out");
|
||||
|
||||
// Act
|
||||
string filter = RenderPipeline.BuildJoinVideoFilter(settings, "");
|
||||
|
||||
// Assert
|
||||
StringAssert.StartsWith(filter, "scale=2880:2160");
|
||||
Assert.IsFalse(filter.Contains("crop=", StringComparison.Ordinal));
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void PreviewSeekSeconds_WithDurationAfterTrim_StaysInsideRemainingClip()
|
||||
{
|
||||
// Act
|
||||
double seek = RenderPipeline.PreviewSeekSeconds(4, 30);
|
||||
|
||||
// Assert
|
||||
Assert.AreEqual(5.2, seek, 0.001);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void PreviewSeekSeconds_WhenTrimIsPastDuration_ReturnsTrimStart()
|
||||
{
|
||||
// Act
|
||||
double seek = RenderPipeline.PreviewSeekSeconds(40, 10);
|
||||
|
||||
// Assert
|
||||
Assert.AreEqual(40, seek, 0.001);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void FormatTimestamp_FormatsInvariantHoursMinutesSeconds()
|
||||
{
|
||||
// Act
|
||||
string stamp = RenderPipeline.FormatTimestamp(4.414);
|
||||
|
||||
// Assert
|
||||
Assert.AreEqual("00:00:04.414", stamp);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void BuildProgressMessage_WithOnlyTime_ReturnsTimeRangeOnly()
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user