plugins/studio/skills/compressing-epub-images/SKILL.md
EPUB ファイル内の画像(主にスキャン本の JPEG)を ImageMagick(magick)で再エンコード(quality 調整)・リサイズし、PNG は pngquant で圧縮してファイルサイズを削減するスキル。AskUserQuestion で圧縮レベル選択フローを提供し、実測サンプリングから予測サイズを提示してからユーザーに選ばせる。mimetype 先頭・無圧縮での再 zip など EPUB 規格を守った再パッケージ手順を含む。 Use when: EPUB ファイルが大きく画像圧縮で削減したい時、スキャン本(画像ベース EPUB)の容量を減らしたい時、Kindle などの電子書籍リーダーに転送する前にサイズ最適化したい時。 画像ベース EPUB(スキャン本)と通常 EPUB の両方に対応。書誌情報・xhtml・css は触らず画像のみ再エンコード。For OCR text conversion use certificate:converting-content; for Anki flashcards use certificate:creating-flashcards.
npx skillsauth add sumik5/sumik-claude-plugin compressing-epub-imagesInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
EPUB ファイルの容量の 95% 以上は画像(JPEG)が占めるケースが多い。特にスキャン本(資格試験対策本・学術書・技術書)は 1 ページ 1 JPEG で構成されているため、JPEG 品質値(quality)を下げるだけで劇的にサイズが削減できる。
代表的な効果の例:
ただし JPEG の再エンコードは不可逆処理。元ファイルは必ず保持すること。
EPUB は ZIP 形式のアーカイブ。unzip で展開する。
EPUB_PATH="/path/to/book.epub"
WORK_DIR=$(mktemp -d)
unzip -q "$EPUB_PATH" -d "$WORK_DIR"
echo "展開先: $WORK_DIR"
画像ファイルの枚数・代表解像度・現在の quality を確認する。
# JPEG・PNG の一覧と枚数
find "$WORK_DIR" -iname '*.jpg' -o -iname '*.jpeg' -o -iname '*.png' | wc -l
# 代表的な 3 枚の解像度と現在の quality を確認
find "$WORK_DIR" -iname '*.jpg' -o -iname '*.jpeg' | head -3 | while read f; do
magick identify -verbose "$f" | grep -E "Image:|Geometry:|Quality:"
done
# ファイルサイズの合計(MB)
find "$WORK_DIR" -iname '*.jpg' -o -iname '*.jpeg' -o -iname '*.png' \
-exec du -k {} + | awk '{sum+=$1} END {printf "画像合計: %.1f MB\n", sum/1024}'
10 枚程度の代表サンプルを複数の圧縮設定で実測し、全体のサイズを予測する。
# 均等サンプリング(10 枚)
TOTAL=$(find "$WORK_DIR" -iname '*.jpg' -o -iname '*.jpeg' | wc -l)
STEP=$(( TOTAL / 10 ))
SAMPLES=$(find "$WORK_DIR" -iname '*.jpg' -o -iname '*.jpeg' | sort | awk "NR % $STEP == 0")
# 各圧縮レベルで実測(サンプル 10 枚の合計サイズ → 全体予測)
SAMPLE_ORIG=0
SAMPLE_Q70=0
SAMPLE_Q60=0
SAMPLE_R1080Q70=0
SAMPLE_R1080Q60=0
SAMPLE_TMP=$(mktemp -d)
for f in $SAMPLES; do
orig=$(du -k "$f" | cut -f1)
SAMPLE_ORIG=$((SAMPLE_ORIG + orig))
base=$(basename "$f")
magick "$f" -quality 70 "$SAMPLE_TMP/q70_${base}.new" && mv "$SAMPLE_TMP/q70_${base}.new" "$SAMPLE_TMP/q70_${base}"
magick "$f" -quality 60 "$SAMPLE_TMP/q60_${base}.new" && mv "$SAMPLE_TMP/q60_${base}.new" "$SAMPLE_TMP/q60_${base}"
magick "$f" -resize '1080x1530>' -quality 70 "$SAMPLE_TMP/r70_${base}.new" && mv "$SAMPLE_TMP/r70_${base}.new" "$SAMPLE_TMP/r70_${base}"
magick "$f" -resize '1080x1530>' -quality 60 "$SAMPLE_TMP/r60_${base}.new" && mv "$SAMPLE_TMP/r60_${base}.new" "$SAMPLE_TMP/r60_${base}"
SAMPLE_Q70=$((SAMPLE_Q70 + $(du -k "$SAMPLE_TMP/q70_${base}" | cut -f1)))
SAMPLE_Q60=$((SAMPLE_Q60 + $(du -k "$SAMPLE_TMP/q60_${base}" | cut -f1)))
SAMPLE_R1080Q70=$((SAMPLE_R1080Q70 + $(du -k "$SAMPLE_TMP/r70_${base}" | cut -f1)))
SAMPLE_R1080Q60=$((SAMPLE_R1080Q60 + $(du -k "$SAMPLE_TMP/r60_${base}" | cut -f1)))
done
# 全体サイズ(JPEG 合計)から予測
EPUB_SIZE=$(du -m "$EPUB_PATH" | cut -f1)
RATIO_Q70=$(awk "BEGIN {printf \"%.2f\", $SAMPLE_Q70 / $SAMPLE_ORIG}")
RATIO_Q60=$(awk "BEGIN {printf \"%.2f\", $SAMPLE_Q60 / $SAMPLE_ORIG}")
RATIO_R70=$(awk "BEGIN {printf \"%.2f\", $SAMPLE_R1080Q70 / $SAMPLE_ORIG}")
RATIO_R60=$(awk "BEGIN {printf \"%.2f\", $SAMPLE_R1080Q60 / $SAMPLE_ORIG}")
echo "軽圧縮(q70) 予測: $(awk "BEGIN {printf \"%.0f\", $EPUB_SIZE * $RATIO_Q70}") MB"
echo "標準圧縮(q60) 予測: $(awk "BEGIN {printf \"%.0f\", $EPUB_SIZE * $RATIO_Q60}") MB"
echo "強圧縮+リサイズ(1080p+q70) 予測: $(awk "BEGIN {printf \"%.0f\", $EPUB_SIZE * $RATIO_R70}") MB"
echo "最大圧縮(1080p+q60) 予測: $(awk "BEGIN {printf \"%.0f\", $EPUB_SIZE * $RATIO_R60}") MB"
rm -rf "$SAMPLE_TMP"
サンプリング結果をもとに AskUserQuestion を呼び出す。(AskUserQuestion が使えない環境(Codex CLI 等)では、同じ選択肢と予測サイズを番号付きテキストで提示し、ユーザーの返答を待ってから続行する)
AskUserQuestion 第 1 問: 圧縮レベル
question: |
EPUB の圧縮レベルを選択してください。
現在のサイズ: 203 MB(例)
options:
- value: "light"
label: "軽圧縮"
preview: |
解像度 : 維持(元のまま)
品質 : quality 70
予測 : 145 MB(29% 削減)
削減量 : 58 MB
用途 : 印刷・PC 閲覧・画質優先
- value: "standard"
label: "標準圧縮"
preview: |
解像度 : 維持(元のまま)
品質 : quality 60
予測 : 130 MB(36% 削減)
削減量 : 73 MB
用途 : タブレット閲覧・バランス重視
- value: "strong"
label: "強圧縮 + リサイズ"
preview: |
解像度 : 1080px に縮小(大きい画像のみ)
品質 : quality 70
予測 : 104 MB(49% 削減)
削減量 : 99 MB
用途 : スマートフォン・Kindle・容量節約
- value: "max"
label: "最大圧縮"
preview: |
解像度 : 1080px に縮小(大きい画像のみ)
品質 : quality 60
予測 : 91 MB(55% 削減)
削減量 : 112 MB
用途 : ストレージ最優先・転送用途
AskUserQuestion 第 2 問: 出力方式
question: 出力方式を選択してください。
options:
- value: "new_file"
label: "別ファイルに保存(推奨)"
preview: |
元ファイルを保持したまま
book_compressed.epub を新規作成
元に戻せる(安全)
- value: "overwrite"
label: "上書き"
preview: |
元ファイルを置き換える
元に戻せない
ストレージを節約したい場合に
- value: "backup_overwrite"
label: "バックアップ後に上書き"
preview: |
book.epub.bak を作成してから上書き
中間的な安全策
ストレージに余裕がある場合に推奨
# ---- 圧縮レベルに応じた MAGICK_ARGS を設定 ----
case "$COMPRESS_LEVEL" in
light) MAGICK_ARGS="-quality 70" ;;
standard) MAGICK_ARGS="-quality 60" ;;
strong) MAGICK_ARGS="-resize 1080x1530> -quality 70" ;;
max) MAGICK_ARGS="-resize 1080x1530> -quality 60" ;;
esac
# ---- 全 JPEG を並列圧縮(8 並列)----
find "$WORK_DIR" -iname '*.jpg' -o -iname '*.jpeg' | \
xargs -P 8 -I{} bash -c \
'magick "$1" '"$MAGICK_ARGS"' "$1.new" && mv "$1.new" "$1"' _ {}
# ---- mimetype を先頭・無圧縮格納で再 zip ----
# !! EPUB 規格: mimetype は必ず先頭・無圧縮でなければならない !!
OUTPUT_EPUB="/path/to/book_compressed.epub"
cd "$WORK_DIR"
zip -X0 "$OUTPUT_EPUB" mimetype # 先頭・無圧縮(-X: extra field 除去, -0: 圧縮なし)
zip -r "$OUTPUT_EPUB" . --exclude mimetype # 残りのファイルを追加
echo "完了: $OUTPUT_EPUB"
du -mh "$OUTPUT_EPUB"
preview 文字列はモノスペース的な整形で 5〜8 行に収めること。長すぎると UI で表示が崩れる。
各オプションの preview に必ず含めるべき 5 要素:
実測ベースの予測値を提示することで、ユーザーが期待値を持って選択できる。
# 正しい手順
zip -X0 output.epub mimetype # mimetype を先頭・無圧縮で追加
zip -rq output.epub . --exclude mimetype # 残りのファイルを圧縮で追加
# NG: 通常の zip は mimetype を圧縮してしまう(EPUB リーダーが拒否)
zip -r output.epub .
EPUB 規格(OPS 2.0 / EPUB 3)では mimetype ファイルを ZIP 内の最初のエントリとして非圧縮で格納することが必須。これを破ると Kindle・楽天 kobo・iBooks 等の主要リーダーがファイルを開けなくなる。
既に quality=76 で保存された JPEG を quality=76 で再エンコードしても画質は元通りにならない(劣化は蓄積する)。必ずオリジナルファイルを保持した上で圧縮すること。
magick input.jpg -quality 70 input.jpg のように同じパスを指定すると、処理途中にファイルが壊れることがある。必ず .new サフィックスを経由して mv する。
# 正しいパターン
magick "$f" -quality 70 "$f.new" && mv "$f.new" "$f"
# 危険なパターン(破損リスクあり)
magick "$f" -quality 70 "$f"
JPEG 圧縮とは別に pngquant で処理する。
# PNG は pngquant で圧縮
find "$WORK_DIR" -iname '*.png' | \
xargs -P 4 -I{} pngquant --force --quality=65-80 --output {} {}
> 修飾子で「大きい画像のみ縮小」を保証-resize 1080x1530 は画像を必ず指定サイズに変更する。元画像が既に小さい場合に拡大されてしまう。> を付けることで「それより大きい場合のみ縮小」という挙動になる。
# 正しい: 大きい画像のみ縮小
magick "$f" -resize '1080x1530>' -quality 70 "$f.new"
# NG: 小さい画像も拡大してしまう
magick "$f" -resize '1080x1530' -quality 70 "$f.new"
EPUB_PATH="/path/to/book.epub"
WORK_DIR=$(mktemp -d)
unzip -q "$EPUB_PATH" -d "$WORK_DIR"
ls "$WORK_DIR"
SAMPLE_TMP=$(mktemp -d)
find "$WORK_DIR" -iname '*.jpg' | sort | awk 'NR%10==0' | head -10 > /tmp/samples.txt
while IFS= read -r f; do
base=$(basename "$f")
for cfg in "q70:-quality 70" "q60:-quality 60" \
"r70:-resize 1080x1530> -quality 70" \
"r60:-resize 1080x1530> -quality 60"; do
label="${cfg%%:*}"
args="${cfg#*:}"
magick "$f" $args "$SAMPLE_TMP/${label}_${base}.new" \
&& mv "$SAMPLE_TMP/${label}_${base}.new" "$SAMPLE_TMP/${label}_${base}"
done
done < /tmp/samples.txt
# 各レベルの合計サイズ(KB)
for label in q70 q60 r70 r60; do
total=$(find "$SAMPLE_TMP" -name "${label}_*" -exec du -k {} + | awk '{s+=$1} END{print s}')
echo "$label: ${total} KB(サンプル 10 枚合計)"
done
rm -rf "$SAMPLE_TMP"
# JPEG のみ quality 70 で圧縮
find "$WORK_DIR" -iname '*.jpg' -o -iname '*.jpeg' | \
xargs -P 8 -I{} bash -c \
'magick "$1" -quality 70 "$1.new" && mv "$1.new" "$1"' _ {}
# 1080px に縮小(大きい画像のみ)+ quality 60
find "$WORK_DIR" -iname '*.jpg' -o -iname '*.jpeg' | \
xargs -P 8 -I{} bash -c \
'magick "$1" -resize '"'"'1080x1530>'"'"' -quality 60 "$1.new" && mv "$1.new" "$1"' _ {}
OUTPUT_EPUB="book_compressed.epub"
cd "$WORK_DIR"
# 1. mimetype を先頭・無圧縮(-X0)で追加
zip -X0 "$OUTPUT_EPUB" mimetype
# 2. mimetype 以外をすべて追加
zip -rq "$OUTPUT_EPUB" . --exclude mimetype
# 3. 検証: mimetype が先頭にあることを確認
unzip -l "$OUTPUT_EPUB" | head -5
cd -
find "$WORK_DIR" -iname '*.png' | \
xargs -P 4 -I{} pngquant --force --quality=65-80 --output {} {}
| 用途 | 推奨 quality | 備考 | |------|-------------|------| | スキャン本(資格試験・学術書) | 65〜70 | quality 65 以下で文字判読性が低下し始める | | マンガ・イラスト本 | 70〜75 | 線や色の再現性が重要 | | 写真集・ビジュアル本 | 80 | 劣化が視覚的に目立ちやすい | | Kindle 転送用(最小化優先) | 60 | 文字主体なら実用上問題ない範囲 |
| ツール | 用途 | インストール |
|-------|------|------------|
| unzip / zip | EPUB 展開・再 zip | OS 標準または brew install zip |
| magick(ImageMagick) | JPEG 再エンコード・リサイズ | brew install imagemagick |
| pngquant | PNG 圧縮 | brew install pngquant |
| xargs | 並列実行 | OS 標準 |
macOS の場合、magick は brew install imagemagick でインストールできる。convert コマンドは ImageMagick v7 以降では magick に統合されている。
case "$OUTPUT_MODE" in
new_file)
OUTPUT_EPUB="${EPUB_PATH%.epub}_compressed.epub"
;;
overwrite)
OUTPUT_EPUB="$EPUB_PATH"
;;
backup_overwrite)
cp "$EPUB_PATH" "${EPUB_PATH}.bak"
OUTPUT_EPUB="$EPUB_PATH"
;;
esac
tools
FastAPI (Python) web API development guide covering fundamentals (routing, path/query params, Pydantic models, request/response, error handling), data persistence (SQLAlchemy, SQLModel, async DB, MongoDB, CRUD, Alembic migrations), dependency injection (Depends, dependency_overrides, scopes/lifespan), auth & security (OAuth2, JWT, CORS), async & concurrency (async/await, Starlette, BackgroundTasks, WebSocket, SSE/streaming), testing (TestClient, pytest, httpx, mocking), production deployment & scaling (uvicorn/gunicorn, Docker, optimization), generative-AI services (model serving, streaming, concurrency), and microservice/GraphQL/OpenAPI patterns. MUST load when fastapi is in pyproject.toml/requirements.txt or .py files import fastapi. For Python language/tooling fundamentals (uv/ruff/mypy, packaging, non-FastAPI patterns), use lang:developing-python. For REST/HTTP-spec design, versioning, and API test strategy, use developing-web-apis. For Node.js/Fastify backend services, use building-nodejs-services.
development
studying(スタディング, member.studying.jp)のコースレッスン一覧URLから、配下の「スマート問題集」 「セレクト過去問集(学科試験対策・実技試験対策)」の全問題(問題文・選択肢・正解・解説)を科目単位で 取得し、1科目1JSONファイルに保存する。出力は creating-flashcards スキルへ渡して科目ごとに Anki フラッシュカード化できる。 Use when studying のコースレッスン一覧URL(https://member.studying.jp/course/id/<course_id>/ 形式) を渡され「問題を全部保存したい」「Anki カードにしたい」等と言われたとき。 補足トリガー: studying, スタディング, 資格試験, スマート問題集, セレクト過去問集, 問題集, JSON 保存, Anki 連携。 ブラウザ操作自体の汎用ガイドは web:automating-browser、E2E テストは web:testing-e2e-with-playwright を使う。本スキルは studying 専用の収集ワークフロー + bundled script(scripts/collect-studying.sh) +ログイン認証を提供する。
development
Whizlabs のコース practice test 一覧URLから、配下の全クイズ(Free Test・Practice Test 1〜N 等)を practice mode で巡回し、各問題(問題文・選択肢・正解・解説・参考資料)を取得して 1 クイズ 1 JSON ファイルに 保存する。出力は creating-flashcards スキルへ渡してクイズごとに Anki フラッシュカード化できる。 Use when Whizlabs のコースURL(/learn/course/<slug>/<course-id>/pt 形式)を渡され 「問題を全部保存したい」「Anki カードにしたい」等と言われたとき。 補足トリガー: whizlabs, ホイズラボ, 資格試験, 問題集, practice test, quiz 収集, JSON 保存, Anki 連携。 ブラウザ操作自体の汎用ガイドは web:automating-browser、E2E テストは web:testing-e2e-with-playwright を使う。 本スキルは Whizlabs 専用の収集ワークフロー + bundled script(scripts/collect-whizlabs.sh)+ ログイン認証(agent-browser Auth Vault)を提供する。
development
Guides production Flutter app development with Dart targeting iOS, Android, web, and desktop from a single codebase, including project setup, the widget system (Stateless/Stateful, Material and Cupertino), layout, theming and animation, state management (setState, Provider, Riverpod, BLoC/Cubit, Redux, GetX), navigation and routing (Navigator, go_router, auto_route), networking and backends (http, dio, REST, Firebase, GraphQL), local persistence, responsive multi-platform design, architecture patterns (Clean Architecture, BLoC, MVVM), the pub.dev package ecosystem, widget and integration testing, performance optimization, and build and store release. REQUIRED when creating, implementing, modifying, testing, or shipping a Flutter app, or when pubspec.yaml lists the flutter SDK. For the Dart language itself (types, null safety, OOP, async, collections), use developing-dart. For native iOS/iPadOS apps in Swift/SwiftUI, use developing-ios-apps. For Apple HIG UI/UX decisions, use applying-apple-hig.