초기 커밋: Phase 1 — 레지스트리 기반 컨텍스트 메뉴 + 핵심 변환 파이프라인
- Provider 추상화(IConverterProvider + ProviderCapability) — Available/RequiresExternal/ComingSoon 1급 시민 - 변환 구현: Magick(이미지·RAW·GIF·TIFF), HEIC/HEIF(libheif decode→Magick encode), PDF(PDFium), DOCX(Word COM 또는 LibreOffice 자동 감지) - ComingSoon 스텁: HTML, HWP/HWPX (UI 노출, 컨텍스트 메뉴 등록 자동 제외) - WPF + WPF-UI 4.3 Fluent 2 UI: 메인 창(드래그&드롭, Provider 카드), 변환 창(썸네일·옵션·진행률), 빠른 변환 진행 창, 진단 창 - 단일 EXE에 verb 라우팅: quick / dialog / register / unregister / diagnose / help - HKCU SystemFileAssociations 기반 컨텍스트 메뉴 등록 (Win11 "추가 옵션 표시"에 노출) - README, packaging/ Phase 2 placeholder 포함 Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
commit
8fa4a613d7
37 changed files with 2727 additions and 0 deletions
99
src/EverythingToJpeg.Core/ConversionEngine.cs
Normal file
99
src/EverythingToJpeg.Core/ConversionEngine.cs
Normal file
|
|
@ -0,0 +1,99 @@
|
|||
using EverythingToJpeg.Core.Providers;
|
||||
|
||||
namespace EverythingToJpeg.Core;
|
||||
|
||||
public sealed class ConversionEngine
|
||||
{
|
||||
private readonly ProviderRegistry _registry;
|
||||
|
||||
public ConversionEngine(ProviderRegistry registry)
|
||||
{
|
||||
_registry = registry;
|
||||
}
|
||||
|
||||
public ProviderRegistry Providers => _registry;
|
||||
|
||||
public async Task<IReadOnlyList<ConvertResult>> ConvertManyAsync(
|
||||
IEnumerable<string> sources,
|
||||
ConvertOptions options,
|
||||
IProgress<ConvertProgress>? progress = null,
|
||||
CancellationToken cancellationToken = default)
|
||||
{
|
||||
var sourceList = sources.ToList();
|
||||
var results = new List<ConvertResult>(sourceList.Count);
|
||||
|
||||
for (var i = 0; i < sourceList.Count; i++)
|
||||
{
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
|
||||
var source = sourceList[i];
|
||||
progress?.Report(new ConvertProgress(i, sourceList.Count, source, 0));
|
||||
|
||||
var result = await ConvertOneAsync(source, options,
|
||||
new Progress<double>(p => progress?.Report(new ConvertProgress(i, sourceList.Count, source, p))),
|
||||
cancellationToken).ConfigureAwait(false);
|
||||
|
||||
results.Add(result);
|
||||
progress?.Report(new ConvertProgress(i + 1, sourceList.Count, source, 1));
|
||||
}
|
||||
|
||||
return results;
|
||||
}
|
||||
|
||||
public async Task<ConvertResult> ConvertOneAsync(
|
||||
string sourcePath,
|
||||
ConvertOptions options,
|
||||
IProgress<double>? progress = null,
|
||||
CancellationToken cancellationToken = default)
|
||||
{
|
||||
if (!File.Exists(sourcePath))
|
||||
return ConvertResult.Fail(sourcePath, "파일을 찾을 수 없습니다.");
|
||||
|
||||
if (!_registry.TryGetForFile(sourcePath, out var provider) || provider is null)
|
||||
return ConvertResult.Fail(sourcePath, $"지원하지 않는 형식입니다: {Path.GetExtension(sourcePath)}");
|
||||
|
||||
var availability = await provider.CheckAvailabilityAsync(cancellationToken).ConfigureAwait(false);
|
||||
if (!availability.IsReady)
|
||||
{
|
||||
var missing = availability.MissingDependencies?.Select(d => d.Name) ?? Array.Empty<string>();
|
||||
var detail = availability.Reason ?? "필수 의존성이 준비되지 않았습니다.";
|
||||
if (missing.Any()) detail += $" (필요: {string.Join(", ", missing)})";
|
||||
return ConvertResult.Fail(sourcePath, detail);
|
||||
}
|
||||
|
||||
var outputDir = ResolveOutputDirectory(sourcePath, options);
|
||||
Directory.CreateDirectory(outputDir);
|
||||
|
||||
try
|
||||
{
|
||||
return await provider.ConvertAsync(sourcePath, outputDir, options, progress, cancellationToken)
|
||||
.ConfigureAwait(false);
|
||||
}
|
||||
catch (OperationCanceledException)
|
||||
{
|
||||
throw;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return ConvertResult.Fail(sourcePath, ex.Message, ex);
|
||||
}
|
||||
}
|
||||
|
||||
private static string ResolveOutputDirectory(string sourcePath, ConvertOptions options)
|
||||
{
|
||||
var sourceDir = Path.GetDirectoryName(Path.GetFullPath(sourcePath))
|
||||
?? throw new InvalidOperationException("소스 경로에서 폴더를 결정할 수 없습니다.");
|
||||
|
||||
return options.OutputLocation switch
|
||||
{
|
||||
OutputLocation.SameFolderAsSource => sourceDir,
|
||||
OutputLocation.Custom => string.IsNullOrWhiteSpace(options.CustomOutputDirectory)
|
||||
? sourceDir
|
||||
: options.CustomOutputDirectory!,
|
||||
_ => Path.Combine(sourceDir,
|
||||
Path.GetFileNameWithoutExtension(sourcePath) + options.SubfolderSuffix),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
public sealed record ConvertProgress(int Index, int Total, string CurrentPath, double FileProgress);
|
||||
Loading…
Add table
Add a link
Reference in a new issue