import フォルダに置かれたコンテンツを検出し、comic book reader の folio が扱う CBZ 形式に変換して library フォルダへ格納する Go 製 CLI ツール。
| 変数名 | デフォルト値 | 説明 |
|---|---|---|
FOLIO_IMPORT_DIR |
./import |
入力フォルダ |
FOLIO_LIBRARY_DIR |
./library |
出力フォルダ(CBZ の保存先) |
FOLIO_TMP_DIR |
./tmp |
処理済み元データの退避先 |
起動時に library と tmp が存在しない場合は自動作成する。import が存在しない場合は致命的エラーで終了する。
import フォルダの 直下 だけをスキャンする(再帰スキャンは行わない)。
| 検出対象 | 条件 |
|---|---|
| アーカイブ | 拡張子が .zip または .cbz(大文字小文字不問) |
| フォルダ | 直下に子フォルダを持たないディレクトリ |
| スキップ | 上記以外のファイル、または子フォルダを含むディレクトリ(ログに [skip] を出力) |
検出したアイテムはすべて goroutine で並列処理 される。
アーカイブを開く
└─ repack を試みる
├─ 実際に変換が発生した → 新 CBZ を library に書き出し、元ファイルを tmp へ移動
└─ 変換なし → fast-move(元ファイルを library へ直接移動)
変換処理(フラット化・画像リサイズ・画像圧縮)が 1 件も発生しなかった 場合に適用される。
処理:
- 元ファイルを
libraryへ 移動(os.Rename、クロスデバイスの場合は copy + remove) - 拡張子を
.cbzにリネーム(.zipの場合) tmpへの移動は行わない
フラット化またはいずれかの画像変換が実際に発生した場合に適用される。
処理:
- アーカイブ内の全エントリをフラット化してメモリ上に展開
- 画像エントリは 画像処理 を適用
- 新しい CBZ を
libraryに書き出す - 元の zip/cbz を
tmpに移動する
アーカイブ内の階層構造を撤廃し、全ファイルをルートに配置する。ファイル名の衝突を避けるため、パス区切り / を -- に置換してファイル名のプレフィックスとする。
| 元のパス | フラット後のファイル名 |
|---|---|
image.jpg |
image.jpg |
chapter1/image.jpg |
chapter1--image.jpg |
chapter1/sub/image.jpg |
chapter1--sub--image.jpg |
ディレクトリエントリ(中身のないフォルダ)は出力 CBZ に含めない。
import 直下のフォルダのうち、直下に子フォルダを含まないもののみを処理対象とする。子フォルダが 1 つでも存在する場合はエラーログを出力してスキップする。
import/
book1/ ← OK(直下はファイルのみ)
page001.jpg
page002.jpg
readme.txt
book2/ ← NG(子フォルダを含む)→ [skip]
chapter1/
page001.jpg
- フォルダ直下の全ファイルを収集する(再帰スキャンなし)
- 画像ファイルは 画像処理 を適用
- 画像以外のファイルはそのまま CBZ に梱包する
- フォルダ名 +
.cbzをlibraryに書き出す - 元のフォルダを
tmpに移動する(os.Rename)
import/mybook/
page001.jpg
page002.jpg
readme.txt
→ library/mybook.cbz(内部構造)
page001.jpg ← 必要に応じてリサイズ・圧縮済み
page002.jpg
readme.txt ← そのまま梱包
image.go に実装。zip・フォルダの両処理から呼び出される再利用可能なモジュール。
JPEG(.jpg, .jpeg)および PNG(.png)。それ以外のファイルは処理せずそのまま CBZ に格納する。
画像 1 枚ごとに以下を確認し、いずれも不要なら 元のバイト列をそのまま使用する(再エンコードなし)。
| 条件 | 閾値 |
|---|---|
| リサイズ要否 | 横幅 > 1920 px |
| 圧縮要否 | ファイルサイズ ≥ 300 KB |
- 横幅を 1920 px に縮小し、アスペクト比を保って高さを算出(整数除算)
- アルゴリズム:バイリニア補間(標準ライブラリのみ使用、外部依存なし)
- 圧縮不要(300 KB 未満)かつリサイズ後:quality 90 で再エンコード
- 圧縮必要(300 KB 以上):quality 1〜95 の範囲で バイナリサーチ を行い、出力が 200 KB 以下になる最高品質を選択する
- quality 1 でも 200 KB を超える場合:quality 1 の結果をそのまま使用する
png.BestCompressionで再エンコード- 結果が 300 KB 未満 → そのまま使用
- 結果が 300 KB 以上 → JPEG に変換してファイル名の拡張子も
.jpgに変更し、JPEG の圧縮ロジックを適用する
画像デコードや再エンコードに失敗した場合、WARNログを出力し 元のバイト列をそのまま CBZ に格納する(処理は中断しない)。
CBZ の保存先パスを決定する際、すでに同名ファイルが存在する場合はタイムスタンプをサフィックスに付けて上書きを回避する。
通常: library/mybook.cbz
衝突: library/mybook-20260411-153000.cbz
タイムスタンプのフォーマット:YYYYMMDD-HHMMSS
並列処理中の衝突チェックはミューテックスで保護されており、異なる goroutine が同時に同名ファイルを作成することはない。
| ケース | tmp に移動されるもの |
|---|---|
| アーカイブ(変換あり) | 元の zip/cbz ファイル |
| アーカイブ(変換なし・fast-move) | 移動なし(library に直接移動済み) |
| フォルダ | 元のフォルダごと |
移動は os.Rename で行う。クロスデバイスの場合は copy + remove にフォールバックする。
| タイミング | フォーマット | 例 |
|---|---|---|
| 処理開始 | [N/M] Processing: <name> |
[2/4] Processing: mybook |
| 圧縮発生時 | compressed <name>: X KB -> Y KB |
compressed p001.jpg: 4398 KB -> 188 KB |
| 処理完了 | [N/M] Done (P%): <name> |
[2/4] Done (50%): mybook |
| エラー | [N/M] ERROR <name>: <detail> |
[3/4] ERROR bad.zip: open zip: ... |
| 画像処理失敗 | WARN: image processing failed for <name>: <detail> |
|
| スキップ | [skip] <name> |
[skip] readme.txt |
| 最終結果 | Finished. N succeeded, M failed (total T). |
パーセント表示は完了数 / 全アイテム数で算出する。並列実行のため表示順序は不定。
main.go — 起動・スキャン・アーカイブ処理・フォルダ処理・ファイル操作
image.go — 画像処理(processImage, encodeJPEG, encodePNG, resizeBilinear)
image.go は将来の PDF 対応など他の入力ソースからも利用できるよう、zip・フォルダ処理への依存を持たない独立した実装になっている。