diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 0000000..91357cb --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,75 @@ +# CLAUDE.md + +This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. + +## Solution Structure + +Three projects in `ComicCompress.sln`, all targeting .NET 8.0: + +- **ComicCompress** (this directory) — Console application entry point. `Program.cs` handles CLI parsing via `McMaster.Extensions.CommandLineUtils` and orchestrates processing. +- **iMGcompress-core** (`../iMGcompress-core`) — Class library containing all compression logic. Both the CLI and GUI depend on this. +- **iMGcompress** (`../iMGcompress`) — Windows Forms GUI (`net8.0-windows`) wrapping the core library. + +## Build & Run + +```powershell +# Development build +dotnet build + +# Run CLI (from project root) +dotnet run -- --help +dotnet run -- -i -o -q 75 -p + +# Publish self-contained Windows executable +dotnet publish -r win-x64 -c Release +``` + +`launchSettings.json` defines named profiles (e.g. `PROD`, `DEV_HOME`, `Compress Image to webp`) for common invocations. + +## CLI Flags + +| Flag | Description | +|------|-------------| +| `-i` | Input file or folder (required) | +| `-o` | Output base path (default: `converted_comics`) | +| `-r` | Recurse into subfolders | +| `-s` | Skip if output already exists | +| `-q` | WebP quality 0–100 (default: 75) | +| `-p` | Parallel processing (all CPUs) | +| `-c` | Parallel with N CPUs | +| `-d` | PDF-only mode | +| `-j` | Image-only mode | +| `-n` | Zebra PRN mode (generates PNG + WebP Base64 URI) | +| `-a` | Copy unprocessed files to output | + +## Architecture + +**Data flow:** Input (`.cbr`/`.cbz` archives or loose images) → extract → convert each image to WebP → repack as `.cbz`. + +### Core library (`iMGcompress-core`) + +- **`Compressor.cs`** — Central engine. Reads RAR/ZIP archives via `SharpCompress`, converts images to WebP via `Imazen.WebP`, and writes output `.cbz`. Handles solid and non-solid archives separately. The first image in an archive (cover) is preserved without conversion. +- **`FileProcessor.cs`** — Orchestrates parallel or sequential processing over a file list. Inner class `ProcessorFlags` carries all options. Writes the final compression report. +- **`FileParser.cs`** — Recursively enumerates input files. +- **`ImageExtractor.cs`** — Implements `IRenderListener` (iText) to extract images from PDF pages and produce a `.cbz`. +- **`PixelMap.cs`** — Parses Portable Bitmap formats (PBM/PGM/PPM, variants P1–P6) into `System.Drawing.Bitmap` before WebP encoding. +- **`Logger.cs`** — Thread-safe colored console/file logger. `LogLevel`: Error, Warning, Info, Verbose. +- **`file_stat.cs`** — Simple DTO tracking per-file uncompressed/compressed sizes for statistics. + +### Console app (`ComicCompress`) + +`Program.cs` parses flags, builds a `ProcessorFlags`, calls `FileParser` to enumerate targets, then hands off to `FileProcessor`. Video (MP4→WebM) conversion uses `Xabe.FFmpeg`. Zebra PRN rendering uses `BinaryKits.Zpl.Viewer`. + +## Key Dependencies + +| Package | Purpose | +|---------|---------| +| `SharpCompress` | Read/write RAR and ZIP archives | +| `Imazen.WebP` + `libwebp.dll` | WebP encoding (requires `libwebp.dll` in output dir) | +| `Magick.NET-Q8-x64` | Advanced image format support (TIFF, etc.) | +| `iTextSharp` / `itext` | PDF parsing | +| `Xabe.FFmpeg` | Video conversion | +| `BinaryKits.Zpl.Viewer` | Zebra PRN label rendering | +| `McMaster.Extensions.CommandLineUtils` | CLI argument parsing | + +**`libwebp.dll` must be present** alongside the executable at runtime — it is checked into the repo root. diff --git a/ComicCompress.csproj b/ComicCompress.csproj index d3608d8..ac9089a 100644 --- a/ComicCompress.csproj +++ b/ComicCompress.csproj @@ -7,7 +7,7 @@ ComicCompress True Debug;Release;ReleaseWDbg - 1.1.0.0 + 3.1.0.0 @@ -28,17 +28,17 @@ - - + + - - - + + + - - + + diff --git a/Program.cs b/Program.cs index c696dc6..481cddd 100644 --- a/Program.cs +++ b/Program.cs @@ -30,7 +30,6 @@ using ImageMagick; using javax.xml.transform; using System.Collections.Concurrent; using iText.Barcodes.Dmcode; -using ZstdSharp; using Xabe.FFmpeg.Downloader; using System.Reflection; using Microsoft.VisualBasic; @@ -63,7 +62,10 @@ namespace ComicCompressor //string progVersion = "Version 3.0.0 - (21/11/2024)"; // In core changed color of percent save to white - string progVersion = "Version 3.0.1 - (23/01/2025)"; + //string progVersion = "Version 3.0.1 - (23/01/2025)"; + + // In core changed color of percent save to white + string progVersion = "Version 3.1.0 - (27/06/2027)"; public static int Main(string[] args) => CommandLineApplication.Execute(args); #region INPUT_FLAGS @@ -103,6 +105,12 @@ namespace ComicCompressor [Option("-a|--copyall", Description = "Copy all unprocessed files")] public bool DoCopyAll { get; } = false; + [Option("-w|--new-webp", Description = "Use Imageflow encoder (faster JPEG→WebP transcoding, no full pixel decode)")] + public bool NewWebp { get; } = false; + + [Option("-P|--par-archive", Description = "Parallel image compression within each archive (~4-5x faster, uses half the CPUs)")] + public bool ParArchive { get; } = false; + #endregion #region STATISTICS @@ -153,19 +161,18 @@ namespace ComicCompressor new_webp: false ); - private void OnExecute() + private async Task OnExecuteAsync() { - File_statistics.Clear(); - flags.File_statistics = File_statistics; + flags.New_webp = NewWebp; + flags.Par_archive = ParArchive; myColors = new Color[names.Count()]; - for(int i = 0; i< myColors.Length; i++) + for (int i = 0; i < myColors.Length; i++) { KnownColor randomColorName = names[randomGen.Next(names.Length)]; - myColors[i] = Color.FromKnownColor(randomColorName); + myColors[i] = Color.FromKnownColor(randomColorName); } - //ImageExtractor7.MainBox(); Stopwatch sw = Stopwatch.StartNew(); @@ -175,28 +182,15 @@ namespace ComicCompressor Logger.Log(progVersion, LogLevel.Verbose, true); - //var progress = new Progress( p => Logger.Log(p.ToString(), LogLevel.Verbose, true) ); - //FFmpeg.SetExecutablesPath(Path.GetDirectoryName(Assembly.GetEntryAssembly().Location)); - //FFmpegDownloader.GetLatestVersion(FFmpegVersion.Official,progress); - - - //==//FFmpeg.SetExecutablesPath (Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), "FFmpeg")); - - //Logger.Log(FFmpeg.ExecutablesPath, LogLevel.Verbose, true); - - IList files = new FileParser().ListAllFiles(Input, Recursive); - Process(files); + await Process(files); long time_query = sw.ElapsedMilliseconds; Logger.Log($"ElapsedTime {time_query} ms", LogLevel.Verbose, true); - - Console.ReadKey(); - } - private async void Process(IList files) + private async Task Process(IList files) { Compressor compressor = new Compressor(Logger); compressor.Quality = Quality;