1
0
Fork 0
Everything2Everything/src/Everything2Everything.Core/ConvertOptions.cs
Yun Chan 729b9b699d refactor(P6): Independent 배치 병렬화 + Magick ResourceLimits (즉효 최적화)
순차 for-loop(멀티코어를 1코어만 사용)을 코어 수 병렬로 — 50장 배치가 코어 수만큼 가속.
ConvertOptions 불변(P4)이라 공유 읽기 스레드 안전. 시그니처 불변(내부 교체).

- ConversionEngine.ConvertManyAsync: Independent 분기를 Parallel.ForEachAsync(MaxDOP=BatchParallelism)로.
  결과는 인덱스 고정 ConvertResult[]로 순서 보존. 진행률은 완료 건수(Interlocked.Increment)로 보고
  (파일별 분수 진행률은 동시 실행에서 의미가 흐려짐). DOP<=1이면 기존 순차 경로(세부 진행률 보존).
  CombineToSingle은 단일 연산이라 병렬 대상 아님(기존대로).
- ConvertOptions.BatchParallelism(기본 = 논리 코어 수) 추가 — 미디어 위주 배치는 낮춰 튜닝 가능.
- MagickProvider 정적 생성자: ResourceLimits.LimitMemory(60%) — 병렬 시 동시 MagickImage OOM 방지
  (초과분 디스크 스필, 출력 바이트 무영향).
- 신규 테스트: 8장 병렬 배치의 순서 보존(results[i]↔sources[i]) + 전수 변환 검증.

71개 테스트 전부 그린(골든마스터 동일성 유지), 빌드 0경고/0오류.
⚠ 진행률 UX(완료 건수 모델) 변경 — GUI 스모크 권장.
2026-06-02 09:43:20 +09:00

174 lines
5.2 KiB
C#

namespace Everything2Everything.Core;
public enum OutputLocation
{
SubfolderBesideSource,
SameFolderAsSource,
Custom
}
public enum NameCollision
{
AppendNumber,
Overwrite,
Skip
}
/// <summary>
/// 변환 옵션. 불변(record + init-only) — 구성된 뒤에는 변경되지 않으므로 배치 병렬 변환에서 안전하게 공유된다.
/// 변경이 필요하면 with 식으로 새 인스턴스를 만든다. (P4: mutable God Object → immutable record)
/// </summary>
public sealed record ConvertOptions
{
public OutputLocation OutputLocation { get; init; } = OutputLocation.SubfolderBesideSource;
public string SubfolderSuffix { get; init; } = "_converted";
public string? CustomOutputDirectory { get; init; }
public NameCollision OnCollision { get; init; } = NameCollision.AppendNumber;
public int? MaxLongEdgePixels { get; init; }
public bool KeepExifWhenPossible { get; init; } = true;
public bool FlattenTransparency { get; init; } = false;
public string TransparencyBackground { get; init; } = "#FFFFFF";
public JpegEncodingOptions Jpeg { get; init; } = new();
public PngEncodingOptions Png { get; init; } = new();
public WebpEncodingOptions Webp { get; init; } = new();
public AvifEncodingOptions Avif { get; init; } = new();
public TiffEncodingOptions Tiff { get; init; } = new();
public BmpEncodingOptions Bmp { get; init; } = new();
public GifEncodingOptions Gif { get; init; } = new();
public PdfRenderOptions PdfRender { get; init; } = new();
public PdfBuildOptions PdfBuild { get; init; } = new();
public HtmlRenderOptions HtmlRender { get; init; } = new();
public OcrOptions Ocr { get; init; } = new();
// --- 변환 그래프 경로 옵션 (P1) ---
/// <summary>멀티홉 경로 자동 합성 허용. false면 직접(1홉) 변환만.</summary>
public bool AllowMultiHop { get; init; } = true;
/// <summary>멀티홉 최대 홉 수.</summary>
public int MaxHops { get; init; } = 3;
/// <summary>래스터화 같은 큰 손실 엣지를 회피한다.</summary>
public bool AvoidLossy { get; init; } = false;
/// <summary>독립(Independent) 배치 변환의 최대 병렬 수. 기본 = 논리 코어 수. 미디어(FFmpeg) 위주 배치는 낮춰 오버서브스크립션 회피.</summary>
public int BatchParallelism { get; init; } = Environment.ProcessorCount;
/// <summary>영상 인코딩 시 GPU 하드웨어 가속(NVENC)을 우선 시도하고, 실패하면 CPU로 자동 폴백한다.</summary>
public bool VideoPreferGpu { get; init; } = true;
public PdfCompressOptions PdfCompress { get; init; } = new();
public AiOptions Ai { get; init; } = new();
public static ConvertOptions Quick() => new();
}
public sealed record JpegEncodingOptions
{
public int Quality { get; init; } = 92;
public bool Progressive { get; init; } = false;
}
public sealed record PngEncodingOptions
{
public int Compression { get; init; } = 7;
public bool Interlace { get; init; } = false;
}
public sealed record WebpEncodingOptions
{
public int Quality { get; init; } = 90;
public bool Lossless { get; init; } = false;
}
public sealed record AvifEncodingOptions
{
public int Quality { get; init; } = 60;
public int Speed { get; init; } = 6;
}
public sealed record TiffEncodingOptions
{
public string Compression { get; init; } = "lzw";
}
public sealed record BmpEncodingOptions
{
}
public sealed record GifEncodingOptions
{
}
public sealed record PdfRenderOptions
{
public int Dpi { get; init; } = 200;
public bool WithAnnotations { get; init; } = true;
public bool WithFormFill { get; init; } = true;
}
public sealed record PdfBuildOptions
{
public string PageSize { get; init; } = "Auto";
public int MarginPoints { get; init; } = 24;
public bool FitToPage { get; init; } = true;
}
public sealed record HtmlRenderOptions
{
public int ViewportWidth { get; init; } = 1280;
public int? ViewportHeight { get; init; }
public int WaitMilliseconds { get; init; } = 2000;
public bool FullPage { get; init; } = true;
}
public sealed record OcrOptions
{
public string Language { get; init; } = "ko+en";
public bool PreserveLayout { get; init; } = true;
public string Backend { get; init; } = "auto";
}
public sealed record PdfCompressOptions
{
/// <summary>Light(구조 최적화·무손실) | Strong(렌더 재인코딩) | Max(Ghostscript). P1은 Light만 구현.</summary>
public string Level { get; init; } = "Light";
}
public sealed record AiOptions
{
/// <summary>auto | openai | anthropic. auto는 설정된 키 중 가용한 것을 선택.</summary>
public string Backend { get; init; } = "auto";
/// <summary>모델 ID. null이면 백엔드별 기본값.</summary>
public string? Model { get; init; }
/// <summary>summarize | translate | proofread | custom.</summary>
public string Task { get; init; } = "summarize";
/// <summary>translate 작업의 대상 언어 (예: "영어", "일본어").</summary>
public string? TargetLanguage { get; init; }
/// <summary>custom 작업의 사용자 지정 지시문.</summary>
public string? Instruction { get; init; }
public int MaxOutputTokens { get; init; } = 2000;
}