private static void DisplayHero(PreviewAttribute attribute) { Console.WriteLine("Clipster running with the following arguments:"); Console.WriteLine(""); Console.WriteLine($" Begin capture at: {attribute.BeginSeconds} second(s)"); Console.WriteLine($" Desired Snippets: {attribute.DesiredSnippets}"); Console.WriteLine($" Output File: {attribute.Output}"); Console.WriteLine($" Snippet Length: {attribute.SnippetLengthInSeconds} second(s)"); Console.WriteLine($" Source File: {attribute.Source}"); Console.WriteLine($" Verbose Logging: {attribute.Verbose}"); Console.WriteLine($" Video Height: {attribute.VideoHeightInPixels} pixel(s)"); Console.WriteLine($" Video Width: {attribute.VideoWidthInPixels} pixel(s)"); Console.WriteLine(""); Console.WriteLine(""); }
static void Main(string[] args) { var builder = new ConfigurationBuilder() .SetBasePath(Directory.GetCurrentDirectory()) .AddJsonFile("appsettings.json", optional: true, reloadOnChange: true) .AddEnvironmentVariables(); IConfigurationRoot configuration = builder.Build(); var argDefaults = configuration.GetSection("ArgumentDefaults"); var valDefaults = configuration.GetSection("ValidatorDefaults"); var idx = args.Length > 0 ? Array.FindIndex(args, s => s.Equals("-s") || s.Equals("--source")) + 1 : -1; var app = new CommandLineApplication(); app.HelpOption(); var optionSource = app.Option("-s|--source <Source>", "Source video file", CommandOptionType.SingleValue) .IsRequired() .Accepts(v => v.ExistingFile()); var optionOutput = app.Option("-o|--output <Output>", "Output of the generated preview file", CommandOptionType.SingleValue) .Accepts(v => v.LegalFilePath()); if (idx > -1) { optionOutput.Validators.Add(new OutputFileValidator(args[idx])); } var optionBeginSeconds = app.Option("-b|--begin-seconds <Seconds>", $"When to start the preview - default {argDefaults["BEGIN_SECONDS"]} seconds", CommandOptionType.SingleValue); optionBeginSeconds.Validators.Add(new RangeValidator(int.Parse(valDefaults["BEGIN_SECONDS_LOWER"]), int.Parse(valDefaults["BEGIN_SECONDS_UPPER"]))); var optionLengthSeconds = app.Option("-l|--length-seconds <Seconds>", $"Length of snippet - default {argDefaults["LENGTH_SECONDS"]} seconds", CommandOptionType.SingleValue); optionLengthSeconds.Validators.Add(new RangeValidator(int.Parse(valDefaults["LENGTH_SECONDS_LOWER"]), int.Parse(valDefaults["LENGTH_SECONDS_UPPER"]))); var optionDesiredSnippets = app.Option("-d|--desired-snippets <Snippets>", $"Number of desired snippet - default {argDefaults["DESIRED_SNIPPETS"]} snippets", CommandOptionType.SingleValue); optionDesiredSnippets.Validators.Add(new RangeValidator(int.Parse(valDefaults["DESIRED_SNIPPETS_LOWER"]), int.Parse(valDefaults["DESIRED_SNIPPETS_UPPER"]))); var optionVideoHeight = app.Option("-h|--video-height <Pixels>", $"Video height dimension - default {argDefaults["VIDEO_HEIGHT"]} pixels", CommandOptionType.SingleValue); optionVideoHeight.Validators.Add(new RangeValidator(int.Parse(valDefaults["VIDEO_HEIGHT_LOWER"]), int.Parse(valDefaults["VIDEO_HEIGHT_UPPER"]))); var optionVideoWidth = app.Option("-w|--video-width <Pixels>", $"Video height dimension - default {argDefaults["VIDEO_WIDTH"]} pixels", CommandOptionType.SingleValue); optionVideoWidth.Validators.Add(new RangeValidator(int.Parse(valDefaults["VIDEO_WIDTH_LOWER"]), int.Parse(valDefaults["VIDEO_WIDTH_UPPER"]))); var optionVerbose = app.Option("-v|--verbose", "Level of logging", CommandOptionType.NoValue); app.OnExecute(() => { var verbose = optionVerbose.HasValue(); var beginSeconds = optionBeginSeconds.HasValue() ? int.Parse(optionBeginSeconds.Value()) : int.Parse(argDefaults["BEGIN_SECONDS"]); var lengthSeconds = optionLengthSeconds.HasValue() ? int.Parse(optionLengthSeconds.Value()) : int.Parse(argDefaults["LENGTH_SECONDS"]); var desiredSnippets = optionDesiredSnippets.HasValue() ? int.Parse(optionDesiredSnippets.Value()) : int.Parse(argDefaults["DESIRED_SNIPPETS"]); var videoHeight = optionVideoHeight.HasValue() ? int.Parse(optionVideoHeight.Value()) : int.Parse(argDefaults["VIDEO_HEIGHT"]); var videoWidth = optionVideoWidth.HasValue() ? int.Parse(optionVideoWidth.Value()) : int.Parse(argDefaults["VIDEO_WIDTH"]); var attribute = new PreviewAttribute() { Source = optionSource.Value(), Output = optionOutput.Value(), BeginSeconds = beginSeconds, DesiredSnippets = desiredSnippets, SnippetLengthInSeconds = lengthSeconds, VideoHeightInPixels = videoHeight, VideoWidthInPixels = videoWidth, Verbose = verbose }; PreviewEngine.Run(attribute); }); app.Execute(args); }
public static void Run(PreviewAttribute attribute) { SetWorkingDir(attribute.Source, attribute.Output); DisplayHero(attribute); double totalSeconds; AspectRatio ratio; GetVideoData(attribute.Source, out totalSeconds, out ratio); // Get video length in seconds var length = (int)Math.Round(totalSeconds, 0); // Ensure the video is long enough to even bother previewing var minLength = attribute.SnippetLengthInSeconds * attribute.DesiredSnippets; // Display and check video length if (length < minLength) { Console.WriteLine("Video is too short. Skipping"); Environment.Exit(0); } // Video dimension var dimensions = ratio == AspectRatio.FullScreen ? @"(iw*sar)*min(427/(iw*sar)\,240/ih):ih*min(427/(iw*sar)\,240/ih), pad=427:240:(427-iw*min(427/iw\,240/ih))/2:(240-ih*min(427/iw\,240/ih))/2" : "426x240"; var interval = ((length - attribute.BeginSeconds) / attribute.DesiredSnippets); string arguments; for (var i = 1; i <= attribute.DesiredSnippets; i++) { var start = ((i * interval) + attribute.BeginSeconds); // only create snippet if the start time is not at the end of the video // or if the start time + snippet length does not exceed the length of the video if (start < length && start + attribute.SnippetLengthInSeconds <= length) { var formattedStart = string.Format("{0}:{1}:{2}", (start / 3600).ToString("D2"), ((start % 3600) / 60).ToString("D2"), (start % 60).ToString("D2")); Console.WriteLine($"Generating preview part {i}{_outputFileExt} at {formattedStart}"); // Generating the snippet at calculated time arguments = $@"-i {_sourceFile} -vf ""scale={dimensions}"" -an -preset fast -qmin 1 -qmax 1 -ss { formattedStart} -t {attribute.SnippetLengthInSeconds} {_snippetsDir}\\{i}{_outputFileExt}"; RunVideoTool(arguments, attribute.Verbose); } } // Concat videos Console.WriteLine("Generating final preview file"); CreateListFile(); // Generate a text file with one snippet video location per line // (https://trac.ffmpeg.org/wiki/Concatenate) arguments = $@"-y -f concat -safe 0 -i {_listFile} -c copy {_outputFile}"; RunVideoTool(arguments, attribute.Verbose); File.Copy(_outputFile, attribute.Output, true); Console.WriteLine($"Clip creation completed! File is located at {attribute.Output}"); }