diff --git a/.github/workflows/md2html.yml b/.github/workflows/md2html.yml
index b0ea865..6696835 100644
--- a/.github/workflows/md2html.yml
+++ b/.github/workflows/md2html.yml
@@ -6,27 +6,43 @@ jobs:
convert_via_pandoc:
name: Convert via Pandoc
runs-on: ubuntu-24.04
+ permissions:
+ contents: write
steps:
- - uses: actions/checkout@v4
+ - uses: actions/checkout@v5
with:
fetch-depth: 2
- name: create CHANGED_FILES list
+ id: changed
# https://stackoverflow.com/a/57862507/11480802
# https://docs.github.com/ja/actions/reference/environment-variables
- # .md の変更がないとこの行で失敗するので注意
- run: git diff --name-only ${GITHUB_SHA}^..$GITHUB_SHA | grep -e ".*\.md" > CHANGED_FILES
+ # 対象を md/ep*.md に限定しないと、CLAUDE.md や README.md だけの変更で
+ # entrypoint.sh が md/epREADME.md.md.tmp を探しに行き pandoc が落ちる
+ run: |
+ git diff --name-only "${GITHUB_SHA}^..${GITHUB_SHA}" \
+ | grep -e "^md/ep.*\.md$" > CHANGED_FILES || true
+ if [ -s CHANGED_FILES ]; then
+ echo "found=true" >> "$GITHUB_OUTPUT"
+ cat CHANGED_FILES
+ else
+ echo "found=false" >> "$GITHUB_OUTPUT"
+ echo "md/ep*.md の変更が無いので変換をスキップします"
+ fi
- uses: ./
+ if: steps.changed.outputs.found == 'true'
env:
FDL_KEY: ${{ secrets.FDL_KEY }}
- - name: Commit files
+ - name: Commit and push
+ if: steps.changed.outputs.found == 'true'
+ env:
+ BRANCH: ${{ github.ref_name }}
run: |
git config --local user.email "action@github.com"
git config --local user.name "GitHub Action"
- git add html/*.html
- git add md/*.md
- git add chapters/episodes.yml
+ git add html/*.html md/*.md chapters/episodes.yml
+ if git diff --cached --quiet; then
+ echo "コミットする変更が無いので終了します"
+ exit 0
+ fi
git commit -m "update html by GitHub Action"
- - name: Push changes
- uses: ad-m/github-push-action@master
- with:
- github_token: ${{ secrets.GITHUB_TOKEN }}
+ git push origin "HEAD:$BRANCH"
diff --git a/CLAUDE.md b/CLAUDE.md
index 260743f..d8a6c10 100644
--- a/CLAUDE.md
+++ b/CLAUDE.md
@@ -79,7 +79,37 @@ PY
なお `html/ep60.html` / `ep62.html` / `ep65.html` は対応する md と内容がずれている
(md 更新後に再生成されていない)。手元での再現結果が合わなくても、それが原因のことがある。
+## ワークフロー (md2html) の癖
+
+**CHANGED_FILES は tip コミットの差分しか見ない。** `git diff ${GITHUB_SHA}^..${GITHUB_SHA}`
+なので、`md/ep.md` を**最後のコミットに含めないと変換されない**。ep68 追加の後に
+CLAUDE.md のコミットを重ねたせいで、ep68 の HTML が生成されずに終わった実例がある。
+md を追加したコミットは push 時の先頭に置くか、後続コミットを重ねないこと。
+(マージコミットは第一親との差分になるので、master へマージした時点で拾われる)
+
+変換対象の grep は `^md/ep.*\.md$`。以前は `.*\.md` と緩く、`CLAUDE.md` や `README.md`
+だけを変更すると `md/epREADME.md.md.tmp` を探しに行って pandoc が落ちていた (run #320)。
+
+`md/ep*.md` の変更が無い push では、変換とコミットをスキップして正常終了する。
+以前はこの場合 grep が空振りしてジョブごと失敗していた (run #313/#314/#316/#322)。
+
+生成物は `github.ref_name` へ push するので、feature ブランチでも完走する。
+以前は `ad-m/github-push-action` が常に `master` へ push しようとして
+`! [rejected] HEAD -> master` で必ず失敗していた。
+
+## MP3 のチャプター付け
+
+`chapters/add-chapters.sh` (既定で最新1件、`--all` で全件)。音声の場所は `AUDIO_DIR`、
+既定はリポジトリと同じ階層の `finished_audio`。詳細は `chapters/README.md`。
+
+`add-chapters.py` は `episodes.yml` と `cover_art.jpg` を**相対パスで開く**ので、
+直接呼ぶ場合は `chapters/` に `cd` してから。ラッパーはこれを吸収している。
+
+`episodes.yml` は Actions 生成物なので、新しい回を処理する前に master を pull すること。
+
## その他
- URL 短縮 (Firebase Dynamic Links) はサービス終了に伴い `entrypoint.sh` で無効化済み
-- ワークフローは push 時に走るが、`.md` の変更が1つも無いと CHANGED_FILES 作成の行で失敗する
+- Docker イメージは `pandoc/latex:2.6` (Alpine 3.9、EOL 済み) のまま。今は動いているが、
+ Alpine のミラーが消えた時点で壊れる。その際は runner に pandoc を直接入れる形へ移行する。
+ その場合も **`--wrap=none` を必ず付ける**こと (付けないと既存の出力と数十文字ずれる)
diff --git a/chapters/README.md b/chapters/README.md
index b5b4b05..b2b8310 100644
--- a/chapters/README.md
+++ b/chapters/README.md
@@ -1,5 +1,44 @@
-# add-chapters.py
+# add-chapters
+
+MP3 にチャプターとメタデータ (タイトル・アートワーク) を付ける。
+
+## 準備
+
+```sh
+pip3 install -r requirements.txt
+```
+
+## 使い方
+
+```sh
+chapters/add-chapters.sh # finished_audio 内の最新 MP3 1件だけ
+chapters/add-chapters.sh --all # ファイル名に old を含むものを除く全件
+```
+
+音声の置き場所は `AUDIO_DIR` で変えられる。既定はリポジトリと同じ階層の `finished_audio`
+(`show-notes` と `finished_audio` が並んでいる従来の配置を想定)。
+
+```sh
+AUDIO_DIR=/path/to/mp3 chapters/add-chapters.sh
+```
+
+個別に実行したい場合は、`chapters/` に移動してから `add-chapters.py` を直接呼ぶ
+(`episodes.yml` と `cover_art.jpg` を相対パスで開くため、CWD が `chapters/` である必要がある)。
+
+```sh
+cd chapters && python3 add-chapters.py /path/to/68.mp3
+```
+
+## 注意
+
+- MP3 のファイル名は `68.mp3` / `19.5.mp3` のように**エピソード番号のみ**である必要がある。
+ この番号で `episodes.yml` を引く。
+- `episodes.yml` は `../md/ep*.md` を元に GitHub Actions が生成する。
+ 新しい回のチャプターを付ける前に、**最新の master を pull しておくこと**。
+- タイムスタンプの無い見出し (「お知らせ」など) はチャプターにならない。
+- 実行すると `add-chapters.py` が該当回に当たるまで全エピソードを標準出力に吐くが、
+ デバッグ出力なので無視してよい。
+
+## 出典
- Forked from [rui314/add-chapters.py](https://gist.github.com/rui314/6e435fcebe3998333d37904e893c8c12).
-- `../md` を元に GitHub Actions で `episodes.yml` が作られるので、その後 `add-chapters.py` を実行することで MP3 にチャプターを付けることができる。
-- MP3 のファイル名は現状 `1.mp3` のような形式のみ受け付ける。
diff --git a/chapters/add-chapters.sh b/chapters/add-chapters.sh
new file mode 100755
index 0000000..591c59f
--- /dev/null
+++ b/chapters/add-chapters.sh
@@ -0,0 +1,61 @@
+#!/bin/bash
+#
+# finished_audio 内の MP3 にチャプターとメタデータを付ける。
+#
+# chapters/add-chapters.sh # 最新の1件だけ (既定)
+# chapters/add-chapters.sh --all # ファイル名に old を含むものを除く全件
+#
+# 音声の置き場所は AUDIO_DIR で変えられる。既定はリポジトリと同じ階層の
+# finished_audio (従来どおり show-notes と finished_audio が並んでいる前提)。
+#
+# AUDIO_DIR=/path/to/mp3 chapters/add-chapters.sh
+#
+# episodes.yml は md/ep*.md から GitHub Actions が生成するので、
+# 先に最新の master を pull しておくこと。
+
+set -euo pipefail
+
+SCRIPT_DIR=$(cd "$(dirname "$0")" && pwd)
+REPO_ROOT=$(cd "$SCRIPT_DIR/.." && pwd)
+AUDIO_DIR=${AUDIO_DIR:-$(dirname "$REPO_ROOT")/finished_audio}
+
+if [ ! -d "$AUDIO_DIR" ]; then
+ echo "音声ディレクトリが見つかりません: $AUDIO_DIR" >&2
+ echo "AUDIO_DIR=/path/to/mp3 で指定してください。" >&2
+ exit 1
+fi
+
+# add-chapters.py は episodes.yml と cover_art.jpg を相対パスで開くので
+# chapters/ に移動してから実行する。
+cd "$SCRIPT_DIR"
+
+mp3s=()
+while IFS= read -r line; do
+ mp3s+=("$line")
+done < <(ls -1t "$AUDIO_DIR"/*.mp3 2>/dev/null || true)
+
+if [ "${#mp3s[@]}" -eq 0 ]; then
+ echo "MP3 が見つかりません: $AUDIO_DIR" >&2
+ exit 1
+fi
+
+targets=()
+if [ "${1:-}" = "--all" ]; then
+ for mp3 in "${mp3s[@]}"; do
+ case "$(basename "$mp3")" in
+ *old*) echo "skip: $mp3" ;;
+ *) targets+=("$mp3") ;;
+ esac
+ done
+elif [ $# -gt 0 ]; then
+ echo "不明な引数: $1" >&2
+ echo "使い方: $0 [--all]" >&2
+ exit 1
+else
+ targets=("${mp3s[0]}")
+fi
+
+for mp3 in "${targets[@]}"; do
+ echo "add-chapters for $mp3"
+ python3 add-chapters.py "$mp3"
+done
diff --git a/chapters/requirements.txt b/chapters/requirements.txt
index 6316a6a..62a8365 100644
--- a/chapters/requirements.txt
+++ b/chapters/requirements.txt
@@ -1,2 +1,4 @@
mutagen==1.44.0
-PyYAML==5.4
+# PyYAML 5.4 は Cython 3 系でビルドが通らず、新しめの Python では install できない
+# (Python 3.11 で "Getting requirements to build wheel did not run successfully")
+PyYAML>=6.0