From 417d511bc889288a4005054e739213521f076c3f Mon Sep 17 00:00:00 2001 From: unclshura Date: Sun, 24 May 2026 11:09:00 +0100 Subject: [PATCH] Major structure refactoring --- splitter-cli/CommandLine.cs | 117 +--------------- splitter-cli/FfprobeFormat.cs | 40 ------ splitter-cli/FfprobeStream.cs | 129 ------------------ splitter-cli/GlobalUsing.cs | 5 + splitter-cli/Point2f.cs | 13 -- splitter-cli/SimpleSplitter.cs | 2 + splitter-cli/SingleJob.cs | 119 ++++++++++++++++ splitter-cli/TrackingSplitter.cs | 2 + splitter-cli/{ => algo}/CameraController.cs | 2 +- splitter-cli/{ => algo}/IObjectDetector.cs | 2 +- splitter-cli/{ => algo}/ISegmentProcessor.cs | 2 +- splitter-cli/{ => algo}/KalmanTracker.cs | 2 +- splitter-cli/algo/Point2f.cs | 13 ++ splitter-cli/{ => algo}/UltraFaceDetector.cs | 3 +- .../{ => algo}/YoloOnnxObjectDetector.cs | 3 +- splitter-cli/probe/FfprobeFormat.cs | 40 ++++++ splitter-cli/{ => probe}/FfprobeResult.cs | 4 +- splitter-cli/probe/FfprobeStream.cs | 129 ++++++++++++++++++ .../{ => probe}/FlexibleDoubleConverter.cs | 2 +- .../{ => probe}/FlexibleIntConverter.cs | 2 +- .../{ => probe}/FlexibleLongConverter.cs | 2 +- .../{ => probe}/FrameRotationDetector.cs | 18 +-- splitter-cli/{ => probe}/ProbeVideo.cs | 18 ++- splitter-cli/{ => probe}/VideoInfo.cs | 5 +- .../{ => probe}/VideoRotationSampler.cs | 23 ++-- splitter-cli/splitter.cs | 5 +- splitter-cli/splitter.csproj | 1 - splitter-cli/{ => tui}/ILogger.cs | 2 +- splitter-cli/{ => tui}/LoggingBase.cs | 2 +- .../{ => tui}/SpectreConsoleLogger.cs | 2 +- splitter-cli/{ => tui}/TextLogger.cs | 2 +- splitter-cli/{ => util}/FileMaskExpander.cs | 2 +- splitter-cli/{ => util}/SingleTask.cs | 2 + 33 files changed, 370 insertions(+), 345 deletions(-) delete mode 100644 splitter-cli/FfprobeFormat.cs delete mode 100644 splitter-cli/FfprobeStream.cs create mode 100644 splitter-cli/GlobalUsing.cs delete mode 100644 splitter-cli/Point2f.cs create mode 100644 splitter-cli/SingleJob.cs rename splitter-cli/{ => algo}/CameraController.cs (99%) rename splitter-cli/{ => algo}/IObjectDetector.cs (84%) rename splitter-cli/{ => algo}/ISegmentProcessor.cs (74%) rename splitter-cli/{ => algo}/KalmanTracker.cs (98%) create mode 100644 splitter-cli/algo/Point2f.cs rename splitter-cli/{ => algo}/UltraFaceDetector.cs (98%) rename splitter-cli/{ => algo}/YoloOnnxObjectDetector.cs (99%) create mode 100644 splitter-cli/probe/FfprobeFormat.cs rename splitter-cli/{ => probe}/FfprobeResult.cs (76%) create mode 100644 splitter-cli/probe/FfprobeStream.cs rename splitter-cli/{ => probe}/FlexibleDoubleConverter.cs (97%) rename splitter-cli/{ => probe}/FlexibleIntConverter.cs (97%) rename splitter-cli/{ => probe}/FlexibleLongConverter.cs (97%) rename splitter-cli/{ => probe}/FrameRotationDetector.cs (85%) rename splitter-cli/{ => probe}/ProbeVideo.cs (88%) rename splitter-cli/{ => probe}/VideoInfo.cs (71%) rename splitter-cli/{ => probe}/VideoRotationSampler.cs (86%) rename splitter-cli/{ => tui}/ILogger.cs (95%) rename splitter-cli/{ => tui}/LoggingBase.cs (96%) rename splitter-cli/{ => tui}/SpectreConsoleLogger.cs (99%) rename splitter-cli/{ => tui}/TextLogger.cs (94%) rename splitter-cli/{ => util}/FileMaskExpander.cs (95%) rename splitter-cli/{ => util}/SingleTask.cs (86%) diff --git a/splitter-cli/CommandLine.cs b/splitter-cli/CommandLine.cs index 8e9609c..4b4087f 100644 --- a/splitter-cli/CommandLine.cs +++ b/splitter-cli/CommandLine.cs @@ -1,122 +1,9 @@ using System.Globalization; +using splitter.algo; +using splitter.util; namespace splitter; -public class SingleJob -{ - /// - /// File path of the input video. This is required for each job and should be - /// set to a valid video file path. The splitter will read this file, analyze it, - /// and split it into segments based on the specified parameters. - /// The output segments will be saved in the OutputFolder with names - /// derived from this input file and the Mask pattern if provided. - /// - public string InputFile { get; set; } = null!; - /// - /// Output folder where the split segments will be saved. This should be set - /// to a valid directory path. - /// - public string OutputFolder { get; set; } = null!; - /// - /// Crop parameters. Width and height for cropping the video. If set, the - /// splitter will crop the video to the specified dimensions while tracking the subject. - /// - public (int width, int height)? Crop { get; set; } - /// - /// The fallback point to gravitate towards when tracking the subject. Coordinates are normalized (0.0 to 1.0). - /// By default , the splitter gravitates towards the center of the frame (0.5, 0.5). - /// Setting this allows you to bias the tracking towards a specific area of the frame, - /// such as left-center (0.2, 0.5) or top-right (0.8, 0.2). This can be useful for - /// videos where the subject tends to be off-center or for creative framing choices. - /// - public Point2f? GravitateTo { get; set; } - /// - /// Destination file mask. - /// - public string? Mask { get; set; } - /// - /// Instead of producing the output, just generate debug frames with tracking - /// overlay to visually verify that the tracking is working correctly. - /// - public bool Debug { get; set; } - /// - /// Type of detector to use for tracking. Supported values are: face (UltraFace), - /// body (YoloOnnx, default), none (no tracking, just a center point). - /// - public string? Detect { get; set; } - /// - /// Set starget segments length explicitly. By default, the splitter calculates segment - /// lengths to be equal and not exceed 58 seconds. - /// - public double? OverrideTargetDuration { get; set; } - /// - /// Parameters to pass thru to ffmpeg. These are specified after "--" in the command - /// line and are passed directly to the ffmpeg command line for each segment. - /// - public string[] Passthrough { get; set; } = []; - /// - /// Debugging parameter. Instead of text UI putput lines in plain text. - /// This is useful when the output is being piped to a file or another program, - /// or when the user prefers a simpler log format without progress bars and dynamic updates. - /// - public bool PlainText { get; set; } - /// - /// Debugging parameter. Just show estimated segments length, count, and other info - /// without actually performing the splitting. - /// - public bool EstimateOnly { get; set; } - /// - /// Do not adapt segment length. When set, the splitter will use the exact - /// segment duration specified by --duration for all segments except possibly - /// the last one, which may be shorter. - /// - public bool ForceFixed { get; set; } - /// - /// Use single thread for operations. When set, the splitter will not run - /// multiple ffmpeg processes in parallel. - /// - public bool SingleThreaded { get; set; } - /// - /// Rotation angle: 90, 180, or 270 degrees. This is useful for videos that - /// have incorrect orientation metadata. - /// - public int? Rotate { get; set; } - /// - /// Autodetect if rotation is needed. Not very reliable but can work for some videos. - /// Uses edge orientation statistics to determine if the video is rotated and - /// applies the appropriate rotation if needed. - /// - public bool RotateAuto { get; set; } - /// - /// Override internal parameters. This allows you to set custom parameters for the - /// object detector or rotation detector. - /// - public Dictionary Parameters { get; set; } = []; - - public void Override(ref T member, string name) - { - if (!Parameters.TryGetValue(name, out var raw)) - return; - - try - { - // Convert.ChangeType handles int, float, double, etc. - var converted = (T)Convert.ChangeType( - raw, - typeof(T), - CultureInfo.InvariantCulture - ); - - member = converted; - } - catch - { - Console.WriteLine($"Invalid value for parameter '{name}': {raw}"); - } - } - -} - public sealed class CommandLine { public SingleJob Master { get; } = new SingleJob(); diff --git a/splitter-cli/FfprobeFormat.cs b/splitter-cli/FfprobeFormat.cs deleted file mode 100644 index fbacf8f..0000000 --- a/splitter-cli/FfprobeFormat.cs +++ /dev/null @@ -1,40 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Text; -using System.Text.Json.Serialization; - -namespace splitter; - -public sealed class FfprobeFormat -{ - public string? Filename { get; set; } - - [JsonConverter(typeof(FlexibleIntConverter))] - public int? Nb_streams { get; set; } - - [JsonConverter(typeof(FlexibleIntConverter))] - public int? Nb_programs { get; set; } - - [JsonConverter(typeof(FlexibleIntConverter))] - public int? Nb_stream_groups { get; set; } - - public string? Format_name { get; set; } - public string? Format_long_name { get; set; } - - [JsonConverter(typeof(FlexibleDoubleConverter))] - public double? Start_time { get; set; } - - [JsonConverter(typeof(FlexibleDoubleConverter))] - public double? Duration { get; set; } - - [JsonConverter(typeof(FlexibleLongConverter))] - public long? Size { get; set; } - - [JsonConverter(typeof(FlexibleLongConverter))] - public long? Bit_rate { get; set; } - - [JsonConverter(typeof(FlexibleIntConverter))] - public int? Probe_score { get; set; } - - public Dictionary? Tags { get; set; } -} diff --git a/splitter-cli/FfprobeStream.cs b/splitter-cli/FfprobeStream.cs deleted file mode 100644 index e9cf8e0..0000000 --- a/splitter-cli/FfprobeStream.cs +++ /dev/null @@ -1,129 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Text; -using System.Text.Json.Serialization; - -namespace splitter; - -public sealed class FfprobeStream -{ - [JsonConverter(typeof(FlexibleIntConverter))] - public int? Index { get; set; } - - public string? Codec_name { get; set; } - public string? Codec_long_name { get; set; } - public string? Profile { get; set; } - public string? Codec_type { get; set; } - public string? Codec_tag_string { get; set; } - public string? Codec_tag { get; set; } - public string? Mime_codec_string { get; set; } - - [JsonConverter(typeof(FlexibleIntConverter))] - public int? Width { get; set; } - - [JsonConverter(typeof(FlexibleIntConverter))] - public int? Height { get; set; } - - [JsonConverter(typeof(FlexibleIntConverter))] - public int? Coded_width { get; set; } - - [JsonConverter(typeof(FlexibleIntConverter))] - public int? Coded_height { get; set; } - - [JsonConverter(typeof(FlexibleIntConverter))] - public int? Closed_captions { get; set; } - - [JsonConverter(typeof(FlexibleIntConverter))] - public int? Film_grain { get; set; } - - [JsonConverter(typeof(FlexibleIntConverter))] - public int? Has_b_frames { get; set; } - - public string? Sample_aspect_ratio { get; set; } - public string? Display_aspect_ratio { get; set; } - - public string? Pix_fmt { get; set; } - - [JsonConverter(typeof(FlexibleIntConverter))] - public int? Level { get; set; } - - public string? Color_range { get; set; } - public string? Color_space { get; set; } - public string? Color_transfer { get; set; } - public string? Color_primaries { get; set; } - public string? Chroma_location { get; set; } - public string? Field_order { get; set; } - - public string? Is_avc { get; set; } - public string? Nal_length_size { get; set; } - - public string? Id { get; set; } - public string? R_frame_rate { get; set; } - public string? Avg_frame_rate { get; set; } - public string? Time_base { get; set; } - - [JsonConverter(typeof(FlexibleLongConverter))] - public long? Start_pts { get; set; } - - [JsonConverter(typeof(FlexibleDoubleConverter))] - public double? Start_time { get; set; } - - [JsonConverter(typeof(FlexibleLongConverter))] - public long? Duration_ts { get; set; } - - [JsonConverter(typeof(FlexibleDoubleConverter))] - public double? Duration { get; set; } - - [JsonConverter(typeof(FlexibleLongConverter))] - public long? Bit_rate { get; set; } - - [JsonConverter(typeof(FlexibleLongConverter))] - public long? Max_bit_rate { get; set; } - - [JsonConverter(typeof(FlexibleIntConverter))] - public int? Bits_per_raw_sample { get; set; } - - [JsonConverter(typeof(FlexibleIntConverter))] - public int? Bits_per_sample { get; set; } - - [JsonConverter(typeof(FlexibleLongConverter))] - public long? Nb_frames { get; set; } - - [JsonConverter(typeof(FlexibleIntConverter))] - public int? Extradata_size { get; set; } - - [JsonConverter(typeof(FlexibleIntConverter))] - public int? Channels { get; set; } - - public string? Channel_layout { get; set; } - public string? Sample_fmt { get; set; } - - [JsonConverter(typeof(FlexibleIntConverter))] - public int? Sample_rate { get; set; } - - [JsonConverter(typeof(FlexibleIntConverter))] - public int? Initial_padding { get; set; } - - public string? Disposition_raw { get; set; } - public Dictionary? Disposition { get; set; } - - public Dictionary? Tags { get; set; } - - public string? Language { get; set; } - public string? Title { get; set; } - - [JsonConverter(typeof(FlexibleIntConverter))] - public int? Bits_per_coded_sample { get; set; } - - public string? Codec_time_base { get; set; } - - [JsonConverter(typeof(FlexibleDoubleConverter))] - public double? Start_pts_time { get; set; } - - [JsonConverter(typeof(FlexibleDoubleConverter))] - public double? Duration_time { get; set; } - - public string? Extradata { get; set; } - public string? Default { get; set; } - public string? Forced { get; set; } -} diff --git a/splitter-cli/GlobalUsing.cs b/splitter-cli/GlobalUsing.cs new file mode 100644 index 0000000..41f0eee --- /dev/null +++ b/splitter-cli/GlobalUsing.cs @@ -0,0 +1,5 @@ +global using OpenCvSharp; +global using splitter.algo; +global using splitter.probe; +global using splitter.tui; + diff --git a/splitter-cli/Point2f.cs b/splitter-cli/Point2f.cs deleted file mode 100644 index a997ea0..0000000 --- a/splitter-cli/Point2f.cs +++ /dev/null @@ -1,13 +0,0 @@ -namespace splitter; - -public struct Point2f -{ - public float X; - public float Y; - - public Point2f(float x, float y) - { - X = x; - Y = y; - } -} diff --git a/splitter-cli/SimpleSplitter.cs b/splitter-cli/SimpleSplitter.cs index 21d9da5..5a4d0e8 100644 --- a/splitter-cli/SimpleSplitter.cs +++ b/splitter-cli/SimpleSplitter.cs @@ -1,5 +1,7 @@ using System.Diagnostics; using System.Globalization; +using splitter.algo; +using splitter.tui; namespace splitter; diff --git a/splitter-cli/SingleJob.cs b/splitter-cli/SingleJob.cs new file mode 100644 index 0000000..5871afc --- /dev/null +++ b/splitter-cli/SingleJob.cs @@ -0,0 +1,119 @@ +using System.Globalization; +using splitter.algo; + +namespace splitter; + +public class SingleJob +{ + /// + /// File path of the input video. This is required for each job and should be + /// set to a valid video file path. The splitter will read this file, analyze it, + /// and split it into segments based on the specified parameters. + /// The output segments will be saved in the OutputFolder with names + /// derived from this input file and the Mask pattern if provided. + /// + public string InputFile { get; set; } = null!; + /// + /// Output folder where the split segments will be saved. This should be set + /// to a valid directory path. + /// + public string OutputFolder { get; set; } = null!; + /// + /// Crop parameters. Width and height for cropping the video. If set, the + /// splitter will crop the video to the specified dimensions while tracking the subject. + /// + public (int width, int height)? Crop { get; set; } + /// + /// The fallback point to gravitate towards when tracking the subject. Coordinates are normalized (0.0 to 1.0). + /// By default , the splitter gravitates towards the center of the frame (0.5, 0.5). + /// Setting this allows you to bias the tracking towards a specific area of the frame, + /// such as left-center (0.2, 0.5) or top-right (0.8, 0.2). This can be useful for + /// videos where the subject tends to be off-center or for creative framing choices. + /// + public Point2f? GravitateTo { get; set; } + /// + /// Destination file mask. + /// + public string? Mask { get; set; } + /// + /// Instead of producing the output, just generate debug frames with tracking + /// overlay to visually verify that the tracking is working correctly. + /// + public bool Debug { get; set; } + /// + /// Type of detector to use for tracking. Supported values are: face (UltraFace), + /// body (YoloOnnx, default), none (no tracking, just a center point). + /// + public string? Detect { get; set; } + /// + /// Set starget segments length explicitly. By default, the splitter calculates segment + /// lengths to be equal and not exceed 58 seconds. + /// + public double? OverrideTargetDuration { get; set; } + /// + /// Parameters to pass thru to ffmpeg. These are specified after "--" in the command + /// line and are passed directly to the ffmpeg command line for each segment. + /// + public string[] Passthrough { get; set; } = []; + /// + /// Debugging parameter. Instead of text UI putput lines in plain text. + /// This is useful when the output is being piped to a file or another program, + /// or when the user prefers a simpler log format without progress bars and dynamic updates. + /// + public bool PlainText { get; set; } + /// + /// Debugging parameter. Just show estimated segments length, count, and other info + /// without actually performing the splitting. + /// + public bool EstimateOnly { get; set; } + /// + /// Do not adapt segment length. When set, the splitter will use the exact + /// segment duration specified by --duration for all segments except possibly + /// the last one, which may be shorter. + /// + public bool ForceFixed { get; set; } + /// + /// Use single thread for operations. When set, the splitter will not run + /// multiple ffmpeg processes in parallel. + /// + public bool SingleThreaded { get; set; } + /// + /// Rotation angle: 90, 180, or 270 degrees. This is useful for videos that + /// have incorrect orientation metadata. + /// + public int? Rotate { get; set; } + /// + /// Autodetect if rotation is needed. Not very reliable but can work for some videos. + /// Uses edge orientation statistics to determine if the video is rotated and + /// applies the appropriate rotation if needed. + /// + public bool RotateAuto { get; set; } + /// + /// Override internal parameters. This allows you to set custom parameters for the + /// object detector or rotation detector. + /// + public Dictionary Parameters { get; set; } = []; + + public void Override(ref T member, string name) + { + if (!Parameters.TryGetValue(name, out var raw)) + return; + + try + { + // Convert.ChangeType handles int, float, double, etc. + var converted = (T)Convert.ChangeType( + raw, + typeof(T), + CultureInfo.InvariantCulture + ); + + member = converted; + } + catch + { + Console.WriteLine($"Invalid value for parameter '{name}': {raw}"); + } + } + +} diff --git a/splitter-cli/TrackingSplitter.cs b/splitter-cli/TrackingSplitter.cs index 9f3cca6..d669020 100644 --- a/splitter-cli/TrackingSplitter.cs +++ b/splitter-cli/TrackingSplitter.cs @@ -2,6 +2,8 @@ using System.Globalization; using System.Runtime.InteropServices; using OpenCvSharp; +using splitter.algo; +using splitter.tui; namespace splitter; diff --git a/splitter-cli/CameraController.cs b/splitter-cli/algo/CameraController.cs similarity index 99% rename from splitter-cli/CameraController.cs rename to splitter-cli/algo/CameraController.cs index a0a6e12..25b850b 100644 --- a/splitter-cli/CameraController.cs +++ b/splitter-cli/algo/CameraController.cs @@ -1,6 +1,6 @@ using OpenCvSharp; -namespace splitter; +namespace splitter.algo; public enum TrackState { diff --git a/splitter-cli/IObjectDetector.cs b/splitter-cli/algo/IObjectDetector.cs similarity index 84% rename from splitter-cli/IObjectDetector.cs rename to splitter-cli/algo/IObjectDetector.cs index d87f005..6ce5839 100644 --- a/splitter-cli/IObjectDetector.cs +++ b/splitter-cli/algo/IObjectDetector.cs @@ -1,6 +1,6 @@ using OpenCvSharp; -namespace splitter; +namespace splitter.algo; public interface IObjectDetector : IDisposable { diff --git a/splitter-cli/ISegmentProcessor.cs b/splitter-cli/algo/ISegmentProcessor.cs similarity index 74% rename from splitter-cli/ISegmentProcessor.cs rename to splitter-cli/algo/ISegmentProcessor.cs index f8ee12d..68034f9 100644 --- a/splitter-cli/ISegmentProcessor.cs +++ b/splitter-cli/algo/ISegmentProcessor.cs @@ -1,4 +1,4 @@ -namespace splitter; +namespace splitter.algo; public interface ISegmentProcessor { diff --git a/splitter-cli/KalmanTracker.cs b/splitter-cli/algo/KalmanTracker.cs similarity index 98% rename from splitter-cli/KalmanTracker.cs rename to splitter-cli/algo/KalmanTracker.cs index 9e6f04e..32576ad 100644 --- a/splitter-cli/KalmanTracker.cs +++ b/splitter-cli/algo/KalmanTracker.cs @@ -1,4 +1,4 @@ -namespace splitter; +namespace splitter.algo; public sealed class KalmanTracker { diff --git a/splitter-cli/algo/Point2f.cs b/splitter-cli/algo/Point2f.cs new file mode 100644 index 0000000..75998cf --- /dev/null +++ b/splitter-cli/algo/Point2f.cs @@ -0,0 +1,13 @@ +//namespace splitter.algo; + +//public struct Point2f +//{ +// public float X; +// public float Y; + +// public Point2f(float x, float y) +// { +// X = x; +// Y = y; +// } +//} diff --git a/splitter-cli/UltraFaceDetector.cs b/splitter-cli/algo/UltraFaceDetector.cs similarity index 98% rename from splitter-cli/UltraFaceDetector.cs rename to splitter-cli/algo/UltraFaceDetector.cs index effafb9..bb9020a 100644 --- a/splitter-cli/UltraFaceDetector.cs +++ b/splitter-cli/algo/UltraFaceDetector.cs @@ -1,8 +1,9 @@ using System.Runtime.InteropServices; using OpenCvSharp; +using splitter.tui; using UltraFaceDotNet; -namespace splitter; +namespace splitter.algo; public sealed class UltraFaceDetector: LoggingBase, IDisposable, IObjectDetector { diff --git a/splitter-cli/YoloOnnxObjectDetector.cs b/splitter-cli/algo/YoloOnnxObjectDetector.cs similarity index 99% rename from splitter-cli/YoloOnnxObjectDetector.cs rename to splitter-cli/algo/YoloOnnxObjectDetector.cs index 3cd695b..90ce27e 100644 --- a/splitter-cli/YoloOnnxObjectDetector.cs +++ b/splitter-cli/algo/YoloOnnxObjectDetector.cs @@ -2,8 +2,9 @@ using Microsoft.ML.OnnxRuntime; using Microsoft.ML.OnnxRuntime.Tensors; using OpenCvSharp; +using splitter.tui; -namespace splitter; +namespace splitter.algo; public sealed class YoloOnnxObjectDetector : LoggingBase, IObjectDetector, IDisposable { diff --git a/splitter-cli/probe/FfprobeFormat.cs b/splitter-cli/probe/FfprobeFormat.cs new file mode 100644 index 0000000..c8abd6b --- /dev/null +++ b/splitter-cli/probe/FfprobeFormat.cs @@ -0,0 +1,40 @@ +using System; +using System.Collections.Generic; +using System.Text; +using System.Text.Json.Serialization; + +namespace splitter.probe; + +public sealed class FfprobeFormat +{ + public string? Filename { get; set; } + + [JsonConverter(typeof(FlexibleIntConverter))] + public int? Nb_streams { get; set; } + + [JsonConverter(typeof(FlexibleIntConverter))] + public int? Nb_programs { get; set; } + + [JsonConverter(typeof(FlexibleIntConverter))] + public int? Nb_stream_groups { get; set; } + + public string? Format_name { get; set; } + public string? Format_long_name { get; set; } + + [JsonConverter(typeof(FlexibleDoubleConverter))] + public double? Start_time { get; set; } + + [JsonConverter(typeof(FlexibleDoubleConverter))] + public double? Duration { get; set; } + + [JsonConverter(typeof(FlexibleLongConverter))] + public long? Size { get; set; } + + [JsonConverter(typeof(FlexibleLongConverter))] + public long? Bit_rate { get; set; } + + [JsonConverter(typeof(FlexibleIntConverter))] + public int? Probe_score { get; set; } + + public Dictionary? Tags { get; set; } +} diff --git a/splitter-cli/FfprobeResult.cs b/splitter-cli/probe/FfprobeResult.cs similarity index 76% rename from splitter-cli/FfprobeResult.cs rename to splitter-cli/probe/FfprobeResult.cs index db930bd..bab6049 100644 --- a/splitter-cli/FfprobeResult.cs +++ b/splitter-cli/probe/FfprobeResult.cs @@ -1,9 +1,9 @@ using System; using System.Collections.Generic; using System.Text; -using static splitter.ProbeVideo; +using static splitter.probe.ProbeVideo; -namespace splitter; +namespace splitter.probe; public sealed class FfprobeResult { diff --git a/splitter-cli/probe/FfprobeStream.cs b/splitter-cli/probe/FfprobeStream.cs new file mode 100644 index 0000000..d1348f1 --- /dev/null +++ b/splitter-cli/probe/FfprobeStream.cs @@ -0,0 +1,129 @@ +using System; +using System.Collections.Generic; +using System.Text; +using System.Text.Json.Serialization; + +namespace splitter.probe; + +public sealed class FfprobeStream +{ + [JsonConverter(typeof(FlexibleIntConverter))] + public int? Index { get; set; } + + public string? Codec_name { get; set; } + public string? Codec_long_name { get; set; } + public string? Profile { get; set; } + public string? Codec_type { get; set; } + public string? Codec_tag_string { get; set; } + public string? Codec_tag { get; set; } + public string? Mime_codec_string { get; set; } + + [JsonConverter(typeof(FlexibleIntConverter))] + public int? Width { get; set; } + + [JsonConverter(typeof(FlexibleIntConverter))] + public int? Height { get; set; } + + [JsonConverter(typeof(FlexibleIntConverter))] + public int? Coded_width { get; set; } + + [JsonConverter(typeof(FlexibleIntConverter))] + public int? Coded_height { get; set; } + + [JsonConverter(typeof(FlexibleIntConverter))] + public int? Closed_captions { get; set; } + + [JsonConverter(typeof(FlexibleIntConverter))] + public int? Film_grain { get; set; } + + [JsonConverter(typeof(FlexibleIntConverter))] + public int? Has_b_frames { get; set; } + + public string? Sample_aspect_ratio { get; set; } + public string? Display_aspect_ratio { get; set; } + + public string? Pix_fmt { get; set; } + + [JsonConverter(typeof(FlexibleIntConverter))] + public int? Level { get; set; } + + public string? Color_range { get; set; } + public string? Color_space { get; set; } + public string? Color_transfer { get; set; } + public string? Color_primaries { get; set; } + public string? Chroma_location { get; set; } + public string? Field_order { get; set; } + + public string? Is_avc { get; set; } + public string? Nal_length_size { get; set; } + + public string? Id { get; set; } + public string? R_frame_rate { get; set; } + public string? Avg_frame_rate { get; set; } + public string? Time_base { get; set; } + + [JsonConverter(typeof(FlexibleLongConverter))] + public long? Start_pts { get; set; } + + [JsonConverter(typeof(FlexibleDoubleConverter))] + public double? Start_time { get; set; } + + [JsonConverter(typeof(FlexibleLongConverter))] + public long? Duration_ts { get; set; } + + [JsonConverter(typeof(FlexibleDoubleConverter))] + public double? Duration { get; set; } + + [JsonConverter(typeof(FlexibleLongConverter))] + public long? Bit_rate { get; set; } + + [JsonConverter(typeof(FlexibleLongConverter))] + public long? Max_bit_rate { get; set; } + + [JsonConverter(typeof(FlexibleIntConverter))] + public int? Bits_per_raw_sample { get; set; } + + [JsonConverter(typeof(FlexibleIntConverter))] + public int? Bits_per_sample { get; set; } + + [JsonConverter(typeof(FlexibleLongConverter))] + public long? Nb_frames { get; set; } + + [JsonConverter(typeof(FlexibleIntConverter))] + public int? Extradata_size { get; set; } + + [JsonConverter(typeof(FlexibleIntConverter))] + public int? Channels { get; set; } + + public string? Channel_layout { get; set; } + public string? Sample_fmt { get; set; } + + [JsonConverter(typeof(FlexibleIntConverter))] + public int? Sample_rate { get; set; } + + [JsonConverter(typeof(FlexibleIntConverter))] + public int? Initial_padding { get; set; } + + public string? Disposition_raw { get; set; } + public Dictionary? Disposition { get; set; } + + public Dictionary? Tags { get; set; } + + public string? Language { get; set; } + public string? Title { get; set; } + + [JsonConverter(typeof(FlexibleIntConverter))] + public int? Bits_per_coded_sample { get; set; } + + public string? Codec_time_base { get; set; } + + [JsonConverter(typeof(FlexibleDoubleConverter))] + public double? Start_pts_time { get; set; } + + [JsonConverter(typeof(FlexibleDoubleConverter))] + public double? Duration_time { get; set; } + + public string? Extradata { get; set; } + public string? Default { get; set; } + public string? Forced { get; set; } +} diff --git a/splitter-cli/FlexibleDoubleConverter.cs b/splitter-cli/probe/FlexibleDoubleConverter.cs similarity index 97% rename from splitter-cli/FlexibleDoubleConverter.cs rename to splitter-cli/probe/FlexibleDoubleConverter.cs index 5622071..456a098 100644 --- a/splitter-cli/FlexibleDoubleConverter.cs +++ b/splitter-cli/probe/FlexibleDoubleConverter.cs @@ -2,7 +2,7 @@ using System.Text.Json; using System.Text.Json.Serialization; -namespace splitter; +namespace splitter.probe; public sealed class FlexibleDoubleConverter : JsonConverter { diff --git a/splitter-cli/FlexibleIntConverter.cs b/splitter-cli/probe/FlexibleIntConverter.cs similarity index 97% rename from splitter-cli/FlexibleIntConverter.cs rename to splitter-cli/probe/FlexibleIntConverter.cs index ba29204..805ebe3 100644 --- a/splitter-cli/FlexibleIntConverter.cs +++ b/splitter-cli/probe/FlexibleIntConverter.cs @@ -2,7 +2,7 @@ using System.Text.Json; using System.Text.Json.Serialization; -namespace splitter; +namespace splitter.probe; public sealed class FlexibleIntConverter : JsonConverter { diff --git a/splitter-cli/FlexibleLongConverter.cs b/splitter-cli/probe/FlexibleLongConverter.cs similarity index 97% rename from splitter-cli/FlexibleLongConverter.cs rename to splitter-cli/probe/FlexibleLongConverter.cs index a0c2423..b44c496 100644 --- a/splitter-cli/FlexibleLongConverter.cs +++ b/splitter-cli/probe/FlexibleLongConverter.cs @@ -2,7 +2,7 @@ using System.Text.Json; using System.Text.Json.Serialization; -namespace splitter; +namespace splitter.probe; public sealed class FlexibleLongConverter : JsonConverter { diff --git a/splitter-cli/FrameRotationDetector.cs b/splitter-cli/probe/FrameRotationDetector.cs similarity index 85% rename from splitter-cli/FrameRotationDetector.cs rename to splitter-cli/probe/FrameRotationDetector.cs index 1350740..e5f62df 100644 --- a/splitter-cli/FrameRotationDetector.cs +++ b/splitter-cli/probe/FrameRotationDetector.cs @@ -1,6 +1,6 @@ using OpenCvSharp; -namespace splitter; +namespace splitter.probe; public sealed class FrameRotationDetector { @@ -18,17 +18,17 @@ public sealed class FrameRotationDetector public FrameRotationDetector(int width = 320, int height = 180, int bins = 36) { - _w = width; - _h = height; - _bins = bins; + _w = width; + _h = height; + _bins = bins; - _gray = new Mat(height, width, MatType.CV_8UC1); - _gx = new Mat(height, width, MatType.CV_32F); - _gy = new Mat(height, width, MatType.CV_32F); - _mag = new Mat(height, width, MatType.CV_32F); + _gray = new Mat(height, width, MatType.CV_8UC1); + _gx = new Mat(height, width, MatType.CV_32F); + _gy = new Mat(height, width, MatType.CV_32F); + _mag = new Mat(height, width, MatType.CV_32F); _angle = new Mat(height, width, MatType.CV_32F); - _hist = new float[bins]; // allocated once + _hist = new float[bins]; // allocated once } public int GetRotation(Mat frame) diff --git a/splitter-cli/ProbeVideo.cs b/splitter-cli/probe/ProbeVideo.cs similarity index 88% rename from splitter-cli/ProbeVideo.cs rename to splitter-cli/probe/ProbeVideo.cs index f19ca39..842da42 100644 --- a/splitter-cli/ProbeVideo.cs +++ b/splitter-cli/probe/ProbeVideo.cs @@ -2,8 +2,9 @@ using System.Globalization; using System.Text.Json; using System.Text.Json.Serialization; +using splitter.algo; -namespace splitter; +namespace splitter.probe; public static class ProbeVideo { @@ -14,23 +15,20 @@ public static class ProbeVideo _ffprobeJsonOptions.Converters.Add(new FlexibleLongConverter()); } - public static async Task Probe(SingleJob job) + public static async Task Probe(string inputFile, bool detectRotation) { - var info = ProbeSize(job.InputFile); - if ( job.RotateAuto) + var info = ProbeSize(inputFile); + if (detectRotation) { - var rotation = await ProbeRotation(job, info.Duration); + var rotation = await ProbeRotation(inputFile, info.Duration); info = info with { Rotation = rotation }; } return info; } - private static async Task ProbeRotation(SingleJob job, double duration) - { - var rotation = await new VideoRotationSampler(job).DetectRotationAsync(job.InputFile, duration); - return rotation; - } + private static async Task ProbeRotation(string inputFile, double duration) + => await new VideoRotationSampler(null).DetectRotationAsync(inputFile, duration); private static readonly JsonSerializerOptions _ffprobeJsonOptions = new () diff --git a/splitter-cli/VideoInfo.cs b/splitter-cli/probe/VideoInfo.cs similarity index 71% rename from splitter-cli/VideoInfo.cs rename to splitter-cli/probe/VideoInfo.cs index 60743eb..b1525c9 100644 --- a/splitter-cli/VideoInfo.cs +++ b/splitter-cli/probe/VideoInfo.cs @@ -1,4 +1,7 @@ -namespace splitter; +using OpenCvSharp; +using splitter.algo; + +namespace splitter.probe; public record VideoInfo( double Duration, diff --git a/splitter-cli/VideoRotationSampler.cs b/splitter-cli/probe/VideoRotationSampler.cs similarity index 86% rename from splitter-cli/VideoRotationSampler.cs rename to splitter-cli/probe/VideoRotationSampler.cs index 7fa3ce9..8337af5 100644 --- a/splitter-cli/VideoRotationSampler.cs +++ b/splitter-cli/probe/VideoRotationSampler.cs @@ -1,7 +1,7 @@ using OpenCvSharp; using System.Diagnostics; -namespace splitter; +namespace splitter.probe; public sealed class VideoRotationSampler { @@ -16,16 +16,19 @@ public sealed class VideoRotationSampler private readonly byte[] _buffer; private readonly Mat _frameMat; - public VideoRotationSampler(SingleJob _master) + public VideoRotationSampler(IDictionary? overrides) { - if (_master.Parameters.TryGetValue("RotationDetectorSampleCount", out var s)) - RotationDetectorSampleCount = int.Parse(s); - if (_master.Parameters.TryGetValue("RotationDetectorSampleLength", out s)) - RotationDetectorSampleLength = double.Parse(s); - if (_master.Parameters.TryGetValue("RotationDetectorFrameWidth", out s)) - RotationDetectorFrameWidth = int.Parse(s); - if (_master.Parameters.TryGetValue("RotationDetectorFrameHeight", out s)) - RotationDetectorFrameHeight = int.Parse(s); + if (overrides != null) + { + if (overrides.TryGetValue("RotationDetectorSampleCount", out var s)) + RotationDetectorSampleCount = int.Parse(s); + if (overrides.TryGetValue("RotationDetectorSampleLength", out s)) + RotationDetectorSampleLength = double.Parse(s); + if (overrides.TryGetValue("RotationDetectorFrameWidth", out s)) + RotationDetectorFrameWidth = int.Parse(s); + if (overrides.TryGetValue("RotationDetectorFrameHeight", out s)) + RotationDetectorFrameHeight = int.Parse(s); + } int w = RotationDetectorFrameWidth; int h = RotationDetectorFrameHeight; diff --git a/splitter-cli/splitter.cs b/splitter-cli/splitter.cs index 5d8d1e4..f7d5413 100644 --- a/splitter-cli/splitter.cs +++ b/splitter-cli/splitter.cs @@ -2,6 +2,9 @@ using System.Collections.Concurrent; using System.Diagnostics; using Spectre.Console; using splitter; +using splitter.algo; +using splitter.probe; +using splitter.tui; static partial class Program { @@ -76,7 +79,7 @@ static partial class Program if (!Directory.Exists(job.OutputFolder)) Directory.CreateDirectory(job.OutputFolder); - var info = await ProbeVideo.Probe(job); + var info = await ProbeVideo.Probe(job.InputFile, job.RotateAuto); if (info.Duration <= 0) { LogError($"{baseName}: Could not read duration."); diff --git a/splitter-cli/splitter.csproj b/splitter-cli/splitter.csproj index a99264e..d9bf3ef 100644 --- a/splitter-cli/splitter.csproj +++ b/splitter-cli/splitter.csproj @@ -50,7 +50,6 @@ - PreserveNewest diff --git a/splitter-cli/ILogger.cs b/splitter-cli/tui/ILogger.cs similarity index 95% rename from splitter-cli/ILogger.cs rename to splitter-cli/tui/ILogger.cs index 3eaf793..a546c6e 100644 --- a/splitter-cli/ILogger.cs +++ b/splitter-cli/tui/ILogger.cs @@ -1,4 +1,4 @@ -namespace splitter; +namespace splitter.tui; public interface ILogger { diff --git a/splitter-cli/LoggingBase.cs b/splitter-cli/tui/LoggingBase.cs similarity index 96% rename from splitter-cli/LoggingBase.cs rename to splitter-cli/tui/LoggingBase.cs index ca27ea7..0c230f4 100644 --- a/splitter-cli/LoggingBase.cs +++ b/splitter-cli/tui/LoggingBase.cs @@ -1,4 +1,4 @@ -namespace splitter; +namespace splitter.tui; public abstract class LoggingBase(ILogger _logger, int _progressLine) { diff --git a/splitter-cli/SpectreConsoleLogger.cs b/splitter-cli/tui/SpectreConsoleLogger.cs similarity index 99% rename from splitter-cli/SpectreConsoleLogger.cs rename to splitter-cli/tui/SpectreConsoleLogger.cs index 595cfe9..ca9a535 100644 --- a/splitter-cli/SpectreConsoleLogger.cs +++ b/splitter-cli/tui/SpectreConsoleLogger.cs @@ -2,7 +2,7 @@ using Spectre.Console; using Spectre.Console.Rendering; -namespace splitter; +namespace splitter.tui; /// diff --git a/splitter-cli/TextLogger.cs b/splitter-cli/tui/TextLogger.cs similarity index 94% rename from splitter-cli/TextLogger.cs rename to splitter-cli/tui/TextLogger.cs index 8651a9d..4cbe09d 100644 --- a/splitter-cli/TextLogger.cs +++ b/splitter-cli/tui/TextLogger.cs @@ -1,4 +1,4 @@ -namespace splitter; +namespace splitter.tui; public class TextLogger() : ILogger { diff --git a/splitter-cli/FileMaskExpander.cs b/splitter-cli/util/FileMaskExpander.cs similarity index 95% rename from splitter-cli/FileMaskExpander.cs rename to splitter-cli/util/FileMaskExpander.cs index 31dec18..e51d539 100644 --- a/splitter-cli/FileMaskExpander.cs +++ b/splitter-cli/util/FileMaskExpander.cs @@ -1,4 +1,4 @@ -namespace splitter; +namespace splitter.util; public static class FileMaskExpander { diff --git a/splitter-cli/SingleTask.cs b/splitter-cli/util/SingleTask.cs similarity index 86% rename from splitter-cli/SingleTask.cs rename to splitter-cli/util/SingleTask.cs index 770780f..e4c8b36 100644 --- a/splitter-cli/SingleTask.cs +++ b/splitter-cli/util/SingleTask.cs @@ -1,4 +1,6 @@ using splitter; +using splitter.algo; +using splitter.probe; public record SingleTask( SingleJob Job,