Logger is a non static class now. Interface extracted

This commit is contained in:
Alexander Shabarshov 2026-05-12 07:59:26 +01:00
parent 78f1bb17d6
commit 14bcd31efe
8 changed files with 66 additions and 71 deletions

13
ILogger.cs Normal file
View File

@ -0,0 +1,13 @@
namespace splitter;
public interface ILogger
{
void ClearProgress(int progressLevel);
void DrawProgress(string name, int progressLine, double progress, TimeSpan eta, double speed);
void Log(string prefix, ConsoleColor color, string msg);
void LogInfo(string msg) => Log("[INFO]", ConsoleColor.Cyan, msg);
void LogSuccess(string msg) => Log("[ OK ]", ConsoleColor.Green, msg);
void LogWarn(string msg) => Log("[WARN]", ConsoleColor.Yellow, msg);
void LogError(string msg) => Log("[ERR ]", ConsoleColor.Red, msg);
}

View File

@ -1,17 +1,15 @@
namespace splitter;
public static class Logger
public class Logger(CommandLine cmd) : ILogger
{
static int _logLines = 0;
static readonly object _consoleLock = new();
int _logLines = Math.Max(1, Environment.ProcessorCount / 2) * 2;
readonly object _consoleLock = new();
public static bool PlainText { get; set; }
public static void Log(string prefix, ConsoleColor color, string msg)
public void Log(string prefix, ConsoleColor color, string msg)
{
lock (_consoleLock)
{
if (PlainText)
if (cmd.PlainText)
{
Console.WriteLine($"{prefix} {msg}");
}
@ -21,27 +19,22 @@ public static class Logger
Console.ForegroundColor = ConsoleColor.Cyan;
Console.Write($"{prefix} ");
Console.ForegroundColor = color;
Console.WriteLine(msg);
Console.ResetColor();
_logLines++;
}
}
}
public static void LogInfo(string msg) => Log("[INFO]", ConsoleColor.Cyan, msg);
public static void LogSuccess(string msg) => Log("[ OK ]", ConsoleColor.Green, msg);
public static void LogWarn(string msg) => Log("[WARN]", ConsoleColor.Yellow, msg);
public static void LogError(string msg) => Log("[ERR ]", ConsoleColor.Red, msg);
private readonly Dictionary<int, int> _progressTrack = new();
private static readonly Dictionary<int, int> _progressTrack = new();
public static void DrawProgress(string name, int progressLevel, double progress, TimeSpan eta, double speed)
public void DrawProgress(string name, int progressLine, double progress, TimeSpan eta, double speed)
{
if (PlainText || progressLevel < 0)
if (cmd.PlainText || progressLine < 0)
return;
// Crop name to max 20 chars
@ -60,17 +53,17 @@ public static class Logger
if (filled > barWidth) filled = barWidth;
// --- NEW: skip drawing if visually unchanged ---
if (_progressTrack.TryGetValue(progressLevel, out var lastFilled) &&
if (_progressTrack.TryGetValue(progressLine, out var lastFilled) &&
lastFilled == filled)
{
return; // no visual change → skip
}
_progressTrack[progressLevel] = filled;
_progressTrack[progressLine] = filled;
// ------------------------------------------------
var barLine = _logLines + 1 + progressLevel * 2;
var infoLine = _logLines + 2 + progressLevel * 2;
var barLine = _logLines + 1 + progressLine * 2;
var infoLine = _logLines + 2 + progressLine * 2;
// Draw progress bar
Console.SetCursorPosition(0, barLine);
@ -101,9 +94,9 @@ public static class Logger
}
public static void ClearProgress(int progressLevel)
public void ClearProgress(int progressLevel)
{
if (PlainText || progressLevel < 0)
if (cmd.PlainText || progressLevel < 0)
return;
lock (_consoleLock)

View File

@ -1,23 +1,23 @@
using System;
namespace splitter;
public abstract class LoggingBase(int progressLine)
public abstract class LoggingBase(ILogger _logger, int _progressLine)
{
protected void Log(string level, ConsoleColor color, string message)
=> Logger.Log(level, color, message);
=> _logger.Log(level, color, message);
protected void LogInfo(string message)
=> Logger.LogInfo(message);
=> _logger.LogInfo(message);
protected void LogWarn(string message)
=> Logger.LogWarn(message);
=> _logger.LogWarn(message);
protected void LogError(string message)
=> Logger.LogError(message);
=> _logger.LogError(message);
protected void DrawProgress(string name, double percent, TimeSpan eta, double fps)
=> Logger.DrawProgress(name, progressLine, percent, eta, fps);
=> _logger.DrawProgress(name, _progressLine, percent, eta, fps);
protected void ClearProgress()
=> Logger.ClearProgress(progressLine);
=> _logger.ClearProgress(_progressLine);
}

View File

@ -7,7 +7,7 @@ using FFmpeg.AutoGen;
namespace splitter;
public class SimpleSplitter(int segmentNo) : LoggingBase(segmentNo), ISegmentProcessor
public class SimpleSplitter(int segmentNo, ILogger logger) : LoggingBase(logger, segmentNo), ISegmentProcessor
{
public async Task ProcessSegment(string inputFile, string outputFile, double start, double length, string[] passthrough)
{

View File

@ -18,8 +18,8 @@ public class TrackingSplitter : LoggingBase, ISegmentProcessor, IDisposable
private readonly IObjectDetector _detector;
private readonly CommandLine _cmd;
public TrackingSplitter(int segmentNo, int cropWidth, int cropHeight, bool debugOverlay, bool plainText, IObjectDetector detector, CommandLine cmd)
: base(segmentNo)
public TrackingSplitter(int segmentNo, int cropWidth, int cropHeight, bool debugOverlay, bool plainText, IObjectDetector detector, CommandLine cmd, ILogger logger)
: base(logger, segmentNo)
{
_segmentNo = segmentNo;
_cropWidth = cropWidth;

View File

@ -9,7 +9,7 @@ public sealed class UltraFaceDetector: LoggingBase, IDisposable, IObjectDetector
{
private readonly UltraFace _ultraFace;
public UltraFaceDetector() : base(-1)
public UltraFaceDetector(ILogger logger) : base(logger, -1)
{
var basePath = AppDomain.CurrentDomain.BaseDirectory;
var param = new UltraFaceParameter

View File

@ -59,7 +59,7 @@ public sealed class YoloOnnxObjectDetector : LoggingBase, IObjectDetector, IDisp
}
}
public YoloOnnxObjectDetector() : base(-1)
public YoloOnnxObjectDetector(ILogger logger) : base(logger, -1)
{
var options = new SessionOptions();
options.AppendExecutionProvider_DML();

View File

@ -5,49 +5,38 @@ using splitter;
static class Program
{
private static ILogger _logger = null!;
static async Task Main(string[] args)
{
var cmd = new CommandLine(args);
var estimateOnly = cmd.EstimateOnly;
var forceFixed = cmd.ForceFixed;
var passthrough = cmd.Passthrough;
var inputFile = cmd.InputFile;
var outputFolder = cmd.OutputFolder;
(int width, int height)? crop = cmd.Crop;
string? mask = cmd.Mask;
var debug = cmd.Debug;
string? detect = cmd.Detect;
double? overrideTargetDuration = cmd.OverrideTargetDuration;
Logger.PlainText = cmd.PlainText;
if (!File.Exists(inputFile))
_logger = new Logger(cmd);
if (!File.Exists(cmd.InputFile))
{
LogError("Input file not found.");
return;
}
if (!Directory.Exists(outputFolder))
Directory.CreateDirectory(outputFolder);
var baseName = Path.GetFileNameWithoutExtension(inputFile);
var outputMask = mask ?? $"{baseName}_Seg%03d.mp4";
if (!Directory.Exists(cmd.OutputFolder))
Directory.CreateDirectory(cmd.OutputFolder);
var baseName = Path.GetFileNameWithoutExtension(cmd.InputFile);
var outputMask = cmd.Mask ?? $"{baseName}_Seg%03d.mp4";
LogInfo("Reading duration via ffprobe...");
var duration = GetDuration(inputFile);
var duration = GetDuration(cmd.InputFile);
if (duration <= 0)
{
LogError("Could not read duration.");
return;
}
var target = overrideTargetDuration ?? 58.0;
var target = cmd.OverrideTargetDuration ?? 58.0;
int segments;
double segmentLength;
if (forceFixed)
if (cmd.ForceFixed)
{
// Fixed chunk size, last one may be shorter
segments = (int)Math.Ceiling(duration / target);
@ -60,13 +49,13 @@ static class Program
segmentLength = duration / segments;
}
if (estimateOnly)
if (cmd.EstimateOnly)
{
LogInfo("=== ESTIMATE MODE ===");
LogInfo($"Total duration: {duration:F2}s");
LogInfo($"Target duration: {target:F2}s");
LogInfo($"Segments: {segments}");
LogInfo(forceFixed
LogInfo(cmd.ForceFixed
? $"Fixed segment length: {segmentLength:F2}s (last may be shorter)"
: $"Equalized segment length: {segmentLength:F2}s");
return;
@ -77,45 +66,45 @@ static class Program
LogInfo($"Equal segment length: {segmentLength:F3}s");
Func<int, ISegmentProcessor> processorFactory;
if (crop != null)
if (cmd.Crop != null)
{
processorFactory = i =>
{
IObjectDetector detector = detect switch
IObjectDetector detector = cmd.Detect switch
{
"face" => new UltraFaceDetector(),
"body" => new YoloOnnxObjectDetector(),
_ => throw new InvalidOperationException($"Unknown detector: {detect}")
"face" => new UltraFaceDetector(_logger),
"body" => new YoloOnnxObjectDetector(_logger),
_ => throw new InvalidOperationException($"Unknown detector: {cmd.Detect}")
};
return new TrackingSplitter(i, crop.Value.width, crop.Value.height, debug, cmd.PlainText, detector, cmd);
return new TrackingSplitter(i, cmd.Crop.Value.width, cmd.Crop.Value.height, cmd.Debug, cmd.PlainText, detector, cmd, _logger);
};
}
else
{
processorFactory = i => new SimpleSplitter(i);
processorFactory = i => new SimpleSplitter(i, _logger);
}
if (cmd.SingleThreaded)
{
LogInfo("Starting single-threaded splitting...");
await RunSingleThreaded(processorFactory, inputFile, outputFolder, outputMask, duration, segments, segmentLength, passthrough);
await RunSingleThreaded(processorFactory, cmd.InputFile, cmd.OutputFolder, outputMask, duration, segments, segmentLength, cmd.Passthrough);
}
else
{
LogInfo("Starting multi-threaded splitting...");
await RunMultiThreaded(processorFactory, inputFile, outputFolder, outputMask, duration, segments, segmentLength, passthrough);
await RunMultiThreaded(processorFactory, cmd.InputFile, cmd.OutputFolder, outputMask, duration, segments, segmentLength, cmd.Passthrough);
}
LogInfo("Done.");
}
private static void LogInfo(string message)
=> Logger.LogInfo(message);
=> _logger.LogInfo(message);
private static void LogWarn(string message)
=> Logger.LogWarn(message);
=> _logger.LogWarn(message);
private static void LogError(string message)
=> Logger.LogError(message);
=> _logger.LogError(message);
// -----------------------------
// ffprobe