前のページのプログラムでは,同じファイル名の音声ファイルを繰り返し処理したときに,出力用の done フォルダで結果が上書きされてしまいます.例えば WhisperX のモデルを turbo から large に変更するなど,結果の比較を行いたい場合にはこの設計は不便に感じる可能性があります.ここでは完了用フォルダやエラーフォルダに日時情報を含んだサブフォルダを作成し,その中に結果を保存するようにしてみます.
worker.py
from pathlib import Path
import shutil
import time
import datetime
import subprocess
BASE_DIR = Path(__file__).parent
QUEUE_DIR = BASE_DIR / "queue"
WORKING_DIR = BASE_DIR / "working"
DONE_DIR = BASE_DIR / "done"
ERROR_DIR = BASE_DIR / "error"
# ディレクトリを作っておく
QUEUE_DIR.mkdir(parents=True, exist_ok=True)
WORKING_DIR.mkdir(parents=True, exist_ok=True)
DONE_DIR.mkdir(parents=True, exist_ok=True)
ERROR_DIR.mkdir(parents=True, exist_ok=True)
try:
while True:
for src in QUEUE_DIR.glob("*.m*"): # 監視するファイルの拡張子
print(f"[{datetime.datetime.now().strftime('%H:%M:%S.%f')}] ----------------")
print(f"[{datetime.datetime.now().strftime('%H:%M:%S.%f')}] 処理開始: {src.name}")
working_file = WORKING_DIR / src.name
transcription_dir = WORKING_DIR / src.stem
transcription_dir.mkdir(parents=True, exist_ok=True) # ディレクトリを作っておく
log_file = transcription_dir / "whisperx.log"
shutil.move(src, working_file)
print(f"[{datetime.datetime.now().strftime('%H:%M:%S.%f')}] 作業開始: {working_file}")
print(f"[{datetime.datetime.now().strftime('%H:%M:%S.%f')}] 出力先 : {transcription_dir}")
print(f"[{datetime.datetime.now().strftime('%H:%M:%S.%f')}] 処理中...")
try:
with open(log_file, "w", encoding="utf-8") as f:
subprocess.run([
"whisperx",
"--model", "turbo", # tubo / large ...
"--language", "ja",
# "--device", "cuda", # NVIDIA GPU を利用できる場合
"-o", str(transcription_dir),
"--diarize", # 話者分離
str(working_file)
],
stdout=f,
stderr=subprocess.STDOUT,
check=True)
except subprocess.CalledProcessError:
print(f"[{datetime.datetime.now().strftime('%H:%M:%S.%f')}] WhisperXの処理に失敗しました")
dt = datetime.datetime.now()
err_dir_name = f"{dt.strftime('%Y%m%d-%H%M%S')}_{src.stem}"
err_dst = ERROR_DIR / err_dir_name / src.name
err_transcription_dir = ERROR_DIR / err_dir_name / src.stem
err_transcription_dir.mkdir(parents=True, exist_ok=True) # ディレクトリを作っておく
shutil.move(working_file, err_dst)
shutil.move(transcription_dir, err_transcription_dir)
print(f"[{datetime.datetime.now().strftime('%H:%M:%S.%f')}] 失敗: {err_dst}")
continue
time.sleep(3)
dt = datetime.datetime.now()
done_dir_name = f"{dt.strftime('%Y%m%d-%H%M%S')}_{src.stem}"
done_file = DONE_DIR / done_dir_name / src.name
done_transcription_dir = DONE_DIR / done_dir_name
done_transcription_dir.mkdir(parents=True, exist_ok=True) # ディレクトリを作っておく
shutil.move(working_file, done_file)
shutil.move(transcription_dir, done_transcription_dir)
print(f"[{datetime.datetime.now().strftime('%H:%M:%S.%f')}] 完了: {done_file}")
time.sleep(1)
except KeyboardInterrupt:
print(f"[{datetime.datetime.now().strftime('%H:%M:%S.%f')}] 停止します")
ターミナルを2個起動し,それぞれで watch.py と worker.py を実行します.
(py313watchdog) PS C:\Users\rinsaka\Documents\python\watchdog> python watch.py ⏎
(py313watchdog) PS C:\Users\rinsaka\Documents\python\watchdog> python worker.py ⏎
2つのプログラムが起動したらマークダウンファイルと音声ファイルを2個ずつ監視用フォルダに設置して処理を開始します.
(py313watchdog) PS C:\Users\rinsaka\Documents\python\watchdog> python watch.py
[18:06:02.236970] 新しいファイルを検出: C:\Users\rinsaka\Documents\python\watchdog\watch\voice-sample02.m4a
[18:06:02.237319] 新しいファイルの名前: voice-sample02.m4a
[18:06:12.238836] ファイルの受取りが完了しました
[18:06:12.238999] ファイルの移動開始: C:\Users\rinsaka\Documents\python\watchdog\queue\voice-sample02.m4a
[18:06:12.239678] ファイルの移動完了: C:\Users\rinsaka\Documents\python\watchdog\queue\voice-sample02.m4a
[18:06:12.239833] 新しいファイルを検出: C:\Users\rinsaka\Documents\python\watchdog\watch\sample1.md
[18:06:12.239889] 新しいファイルの名前: sample1.md
[18:06:22.241144] ファイルの受取りが完了しました
[18:06:22.241231] ファイルの移動開始: C:\Users\rinsaka\Documents\python\watchdog\queue\sample1.md
[18:06:22.241899] ファイルの移動完了: C:\Users\rinsaka\Documents\python\watchdog\queue\sample1.md
[18:06:22.242041] 新しいファイルを検出: C:\Users\rinsaka\Documents\python\watchdog\watch\sample2.md
[18:06:22.242123] 新しいファイルの名前: sample2.md
[18:06:32.242981] ファイルの受取りが完了しました
[18:06:32.243070] ファイルの移動開始: C:\Users\rinsaka\Documents\python\watchdog\queue\sample2.md
[18:06:32.243550] ファイルの移動完了: C:\Users\rinsaka\Documents\python\watchdog\queue\sample2.md
[18:06:32.243663] 新しいファイルを検出: C:\Users\rinsaka\Documents\python\watchdog\watch\voice-sample01.m4a
[18:06:32.243732] 新しいファイルの名前: voice-sample01.m4a
[18:06:42.244400] ファイルの受取りが完了しました
[18:06:42.244492] ファイルの移動開始: C:\Users\rinsaka\Documents\python\watchdog\queue\voice-sample01.m4a
[18:06:42.245451] ファイルの移動完了: C:\Users\rinsaka\Documents\python\watchdog\queue\voice-sample01.m4a
Ctrl + C
[18:07:30.592455] 停止します
(py313watchdog) PS C:\Users\rinsaka\Documents\python\watchdog>
ワーカープログラムでは日時情報から始まる新規フォルダが作成され,その中に処理結果が格納されていることが分かります.
(py313watchdog) PS C:\Users\rinsaka\Documents\python\watchdog> python worker.py [18:06:13.162973] ---------------- [18:06:13.163229] 処理開始: voice-sample02.m4a [18:06:13.181613] 作業開始: C:\Users\rinsaka\Documents\python\watchdog\working\voice-sample02.m4a [18:06:13.181790] 出力先 : C:\Users\rinsaka\Documents\python\watchdog\working\voice-sample02 [18:06:13.181922] 処理中... [18:06:36.061433] 完了: C:\Users\rinsaka\Documents\python\watchdog\done\20261102-180636_voice-sample02\voice-sample02.m4a [18:06:37.062362] ---------------- [18:06:37.062565] 処理開始: sample1.md [18:06:37.064537] 作業開始: C:\Users\rinsaka\Documents\python\watchdog\working\sample1.md [18:06:37.064637] 出力先 : C:\Users\rinsaka\Documents\python\watchdog\working\sample1 [18:06:37.064657] 処理中... [18:06:49.754223] WhisperXの処理に失敗しました [18:06:49.756209] 失敗: C:\Users\rinsaka\Documents\python\watchdog\error\20261102-180649_sample1\sample1.md [18:06:49.756340] ---------------- [18:06:49.756383] 処理開始: sample2.md [18:06:49.757133] 作業開始: C:\Users\rinsaka\Documents\python\watchdog\working\sample2.md [18:06:49.757222] 出力先 : C:\Users\rinsaka\Documents\python\watchdog\working\sample2 [18:06:49.757263] 処理中... [18:07:00.497862] WhisperXの処理に失敗しました [18:07:00.515651] 失敗: C:\Users\rinsaka\Documents\python\watchdog\error\20261102-180700_sample2\sample2.md [18:07:01.516580] ---------------- [18:07:01.516700] 処理開始: voice-sample01.m4a [18:07:01.517982] 作業開始: C:\Users\rinsaka\Documents\python\watchdog\working\voice-sample01.m4a [18:07:01.518041] 出力先 : C:\Users\rinsaka\Documents\python\watchdog\working\voice-sample01 [18:07:01.518097] 処理中... [18:07:21.038238] 完了: C:\Users\rinsaka\Documents\python\watchdog\done\20261102-180721_voice-sample01\voice-sample01.m4a Ctrl + C [18:07:31.423532] 停止します (py313watchdog) PS C:\Users\rinsaka\Documents\python\watchdog>
完了用フォルダの中身を確認します.
(py313watchdog) PS C:\Users\rinsaka\Documents\python\watchdog> gci done -Recurse ⏎ ディレクトリ:C:\Users\rinsaka\Documents\python\watchdog\done Mode LastWriteTime Length Name ---- ------------- ------ ---- d---- 2026/11/02 18:06 20261102-180636_voice-sample02 d---- 2026/11/02 18:07 20261102-180721_voice-sample01 ディレクトリ:C:\Users\rinsaka\Documents\python\watchdog\done\20261102-180636_voice-sample02 Mode LastWriteTime Length Name ---- ------------- ------ ---- d---- 2026/11/02 18:06 voice-sample02 -a--- 2026/10/27 12:39 554089 voice-sample02.m4a ディレクトリ:C:\Users\rinsaka\Documents\python\watchdog\done\20261102-180636_voice-sample02\voice-sample02 Mode LastWriteTime Length Name ---- ------------- ------ ---- -a--- 2026/11/02 18:06 58602 voice-sample02.json -a--- 2026/11/02 18:06 1113 voice-sample02.srt -a--- 2026/11/02 18:06 1014 voice-sample02.tsv -a--- 2026/11/02 18:06 1005 voice-sample02.txt -a--- 2026/11/02 18:06 1096 voice-sample02.vtt -a--- 2026/11/02 18:06 3355 whisperx.log ディレクトリ:C:\Users\rinsaka\Documents\python\watchdog\done\20261102-180721_voice-sample01 Mode LastWriteTime Length Name ---- ------------- ------ ---- d---- 2026/11/02 18:07 voice-sample01 -a--- 2026/10/27 12:36 329637 voice-sample01.m4a ディレクトリ:C:\Users\rinsaka\Documents\python\watchdog\done\20261102-180721_voice-sample01\voice-sample01 Mode LastWriteTime Length Name ---- ------------- ------ ---- -a--- 2026/11/02 18:07 28431 voice-sample01.json -a--- 2026/11/02 18:07 566 voice-sample01.srt -a--- 2026/11/02 18:07 505 voice-sample01.tsv -a--- 2026/11/02 18:07 494 voice-sample01.txt -a--- 2026/11/02 18:07 558 voice-sample01.vtt -a--- 2026/11/02 18:07 2994 whisperx.log (py313watchdog) PS C:\Users\rinsaka\Documents\python\watchdog>
エラー用のフォルダにも日時情報からなるサブフォルダが作成されて,その中に結果が格納されています.
(py313watchdog) PS C:\Users\rinsaka\Documents\python\watchdog> gci error -Recurse ⏎ ディレクトリ:C:\Users\rinsaka\Documents\python\watchdog\error Mode LastWriteTime Length Name ---- ------------- ------ ---- d---- 2026/11/02 18:06 20261102-180649_sample1 d---- 2026/11/02 18:07 20261102-180700_sample2 ディレクトリ:C:\Users\rinsaka\Documents\python\watchdog\error\20261102-180649_sample1 Mode LastWriteTime Length Name ---- ------------- ------ ---- d---- 2026/11/02 18:06 sample1 -a--- 2026/10/25 18:18 56 sample1.md ディレクトリ:C:\Users\rinsaka\Documents\python\watchdog\error\20261102-180649_sample1\sample1 Mode LastWriteTime Length Name ---- ------------- ------ ---- d---- 2026/11/02 18:06 sample1 ディレクトリ:C:\Users\rinsaka\Documents\python\watchdog\error\20261102-180649_sample1\sample1\sample1 Mode LastWriteTime Length Name ---- ------------- ------ ---- -a--- 2026/11/02 18:06 4412 whisperx.log ディレクトリ:C:\Users\rinsaka\Documents\python\watchdog\error\20261102-180700_sample2 Mode LastWriteTime Length Name ---- ------------- ------ ---- d---- 2026/11/02 18:07 sample2 -a--- 2026/10/25 18:18 115 sample2.md ディレクトリ:C:\Users\rinsaka\Documents\python\watchdog\error\20261102-180700_sample2\sample2 Mode LastWriteTime Length Name ---- ------------- ------ ---- d---- 2026/11/02 18:06 sample2 ディレクトリ:C:\Users\rinsaka\Documents\python\watchdog\error\20261102-180700_sample2\sample2\sample2 Mode LastWriteTime Length Name ---- ------------- ------ ---- -a--- 2026/11/02 18:06 4412 whisperx.log (py313watchdog) PS C:\Users\rinsaka\Documents\python\watchdog>
監視用 watch フォルダ,待ち行列用 queue フォルダと処理用 working フォルダはいずれも空になっていることが分かります.
(py313watchdog) PS C:\Users\rinsaka\Documents\python\watchdog> gci watch ⏎ (py313watchdog) PS C:\Users\rinsaka\Documents\python\watchdog> gci queue ⏎ (py313watchdog) PS C:\Users\rinsaka\Documents\python\watchdog> gci working ⏎ (py313watchdog) PS C:\Users\rinsaka\Documents\python\watchdog>
ここまでの作業によって,監視フォルダに音声ファイルを設置するだけで自動的に文字起こし結果が完了用フォルダに出力されるパイプライン処理ができました.次のページでは文字起こし結果からローカルの生成AI (LLM) によって自動的に議事録を作成することを目標に作業を進めていきます.