Python入門トップページ


目次

  1. 仮想環境を構築し,watchdog をインストールする
  2. Watchdog でフォルダを監視する
  3. 新しいファイルを検知する
  4. 検知した新しいファイルを待ち行列フォルダに移動する
  5. 文字起こし処理用のワーカープログラムのひな形を作成する
  6. ワーカープログラムを常駐化する
  7. フォルダ監視とワーカープログラムでパイプライン処理する
  8. 処理用フォルダを設置してそこで処理を行う
  9. ファイルサイズの変化を監視する
  10. サイズの大きなファイルのコピーに対応する
  11. タイムスタンプを表示する
  12. Subprocess で外部コマンドを起動する
  13. WhisperX の設定を行う
  14. ワーカープログラムで音声データからの文字起こしを実行する
  15. 文字起こし結果を完了用フォルダに移動する
  16. ログを保存し例外を処理する
  17. サブフォルダに結果を格納する
  18. Ollama をインストールする
  19. Python から Ollama にプロンプトを送る
  20. Ollama 処理を関数化する
  21. 文字起こしデータから議事録を作成する
  22. サイズの大きなテキストファイルにも対応する
  23. ワーカープログラムで文字起こし結果から議事録を生成する
  24. 議事録をマークダウンファイルに出力する
  25. 文字コードを強制的に指定する

フォルダ監視により,音声ファイルから文字起こしと議事録生成を自動化しよう

ワーカープログラムで音声データからの文字起こしを実行する

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"

# ディレクトリを作っておく
QUEUE_DIR.mkdir(parents=True, exist_ok=True)
WORKING_DIR.mkdir(parents=True, exist_ok=True)
DONE_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')}] 処理開始: {src.name}")
            working_file = WORKING_DIR / src.name
            transcription_dir = WORKING_DIR / src.stem
            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')}] 処理中...")
            subprocess.run([
                "whisperx",
                "--model", "turbo", # tubo / large ...
                "--language", "ja",
                # "--device", "cuda",  # NVIDIA GPU を利用できる場合
                "-o", str(transcription_dir),
                "--diarize",  # 話者分離
                str(working_file)
            ])
            time.sleep(3)
            dst = DONE_DIR / src.name
            shutil.move(working_file, dst)
            print(f"[{datetime.datetime.now().strftime('%H:%M:%S.%f')}] 完了: {dst}")
        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つのプログラムが起動できたら監視用 watch フォルダに4個の音声ファイルを設置します.

(py313watchdog) PS C:\Users\rinsaka\Documents\python\watchdog> python watch.py
[17:09:52.154133] 新しいファイルを検出: C:\Users\rinsaka\Documents\python\watchdog\watch\voice-sample04.m4a
[17:09:52.154368] 新しいファイルの名前: voice-sample04.m4a
[17:10:02.155480] ファイルの受取りが完了しました
[17:10:02.155583] ファイルの移動開始: C:\Users\rinsaka\Documents\python\watchdog\queue\voice-sample04.m4a
[17:10:02.156019] ファイルの移動完了: C:\Users\rinsaka\Documents\python\watchdog\queue\voice-sample04.m4a
[17:10:02.156186] 新しいファイルを検出: C:\Users\rinsaka\Documents\python\watchdog\watch\voice-sample01.m4a
[17:10:02.156260] 新しいファイルの名前: voice-sample01.m4a
[17:10:12.157192] ファイルの受取りが完了しました
[17:10:12.157296] ファイルの移動開始: C:\Users\rinsaka\Documents\python\watchdog\queue\voice-sample01.m4a
[17:10:12.158090] ファイルの移動完了: C:\Users\rinsaka\Documents\python\watchdog\queue\voice-sample01.m4a
[17:10:12.158181] 新しいファイルを検出: C:\Users\rinsaka\Documents\python\watchdog\watch\voice-sample02.m4a
[17:10:12.158223] 新しいファイルの名前: voice-sample02.m4a
[17:10:22.159228] ファイルの受取りが完了しました
[17:10:22.159347] ファイルの移動開始: C:\Users\rinsaka\Documents\python\watchdog\queue\voice-sample02.m4a
[17:10:22.159806] ファイルの移動完了: C:\Users\rinsaka\Documents\python\watchdog\queue\voice-sample02.m4a
[17:10:22.159956] 新しいファイルを検出: C:\Users\rinsaka\Documents\python\watchdog\watch\voice-sample03.m4a
[17:10:22.160020] 新しいファイルの名前: voice-sample03.m4a
[17:10:32.161164] ファイルの受取りが完了しました
[17:10:32.161310] ファイルの移動開始: C:\Users\rinsaka\Documents\python\watchdog\queue\voice-sample03.m4a
[17:10:32.162708] ファイルの移動完了: C:\Users\rinsaka\Documents\python\watchdog\queue\voice-sample03.m4a
Ctrl + C
[17:11:44.877413] 停止します
(py313watchdog) PS C:\Users\rinsaka\Documents\python\watchdog>

ワーカープログラムからの出力を確認します.WhisperXの処理状況や文字起こし結果が出力されていることが分かります.

(py313watchdog) PS C:\Users\rinsaka\Documents\python\watchdog> python worker.py
[17:10:02.535803] 処理開始: voice-sample04.m4a
[17:10:02.536585] 作業開始: C:\Users\rinsaka\Documents\python\watchdog\working\voice-sample04.m4a
[17:10:02.536654] 出力先 : C:\Users\rinsaka\Documents\python\watchdog\working\voice-sample04
[17:10:02.536712] 処理中...
...(中略)...
Transcript: [2.022 --> 24.989] 台風6号が接近する中、気象庁が線状降水帯の発生と直前予測の情報を適切に発表できなくなっていた問題で、気象庁は今日午後6時に復旧したと説明しました。大変申し訳なく思っております。このようなことを二度と起きないようにと重く受け止めております。
Transcript: [26.947 --> 55.583] この問題は気象庁のシステムトラブルのため、線状降水帯発生、線状降水帯直前予測の情報が、昨日の夕方から適切に発表されない事態になっていたもので、気象庁は今日午後6時に復旧したと説明しました。システムを構成するソフトウェアの一部に不具合があり、情報を作成する処理が正常に行われなかったことが原因だったということです。
Transcript: [57.035 --> 67.514] 本来であれば、きのう午後5時40分ごろに、高知県西部に線状降水帯直前予測の情報を出すはずが発表できなかったとしています。
Loading weights: 100%|████████████| 424/424 [00:00<00:00, 47569.48it/s]
2026-11-02 17:10:24 - whisperx.transcribe - INFO - Performing alignment...
...(中略)...
[17:10:32.565277] 完了: C:\Users\rinsaka\Documents\python\watchdog\done\voice-sample04.m4a
[17:10:33.565916] 処理開始: voice-sample01.m4a
[17:10:33.566862] 作業開始: C:\Users\rinsaka\Documents\python\watchdog\working\voice-sample01.m4a
[17:10:33.566944] 出力先 : C:\Users\rinsaka\Documents\python\watchdog\working\voice-sample01
[17:10:33.566991] 処理中...
...(中略)...
Transcript: [3.473 --> 26.761] 日銀の上田総裁はきょう都内で講演し、中東情勢が不透明な状況が続いたとしても、利上げを行う可能性があるとの考えを示しました。日銀は今月15日から2日間、金融政策決定会合を開きます。会合を前に都内で講演した日銀の上田総裁は、
Transcript: [27.301 --> 34.844] 仮に中東情勢が不透明な状況が続くとしても、利上げに踏み切る可能性があるとの考えを示しました。
Loading weights: 100%|████████████| 424/424 [00:00<00:00, 57129.52it/s]
2026-11-02 17:10:47 - whisperx.transcribe - INFO - Performing alignment...
...(中略)...
[17:10:53.980341] 完了: C:\Users\rinsaka\Documents\python\watchdog\done\voice-sample01.m4a
[17:10:53.980459] 処理開始: voice-sample02.m4a
[17:10:53.981049] 作業開始: C:\Users\rinsaka\Documents\python\watchdog\working\voice-sample02.m4a
[17:10:53.981111] 出力先 : C:\Users\rinsaka\Documents\python\watchdog\working\voice-sample02
[17:10:53.981193] 処理中...
...(中略)...
Transcript: [2.832 --> 27.47] プーチン大統領肝入りの国際会議が開幕したロシアのサンクトペテルブルクで、3日にかけてウクライナ軍による大規模な無人機攻撃がありました。ウクライナのゼレンスキー大統領は3日、ロシア第二の都市サンクトペテルブルクの石油ターミナルなどに無人機部隊が長距離攻撃を仕掛けたと発表しました。
Transcript: [28.6 --> 55.432] 重要施設への攻撃で良好な成果を上げたと主張していますサンクトペテルブルクでは各地で黒煙が上がる様子が確認され地元当局によりますと複数の地区が無人機による攻撃を受けけが人も出ているということですサンクトペテルブルクでは3日からプーチン大統領肝入りの国際経済フォーラムが開幕していて
Transcript: [56.022 --> 62.468] ウクライナ側はこのタイミングを狙うことでプーチン政権を揺さぶる狙いがあるとみられます。
Loading weights: 100%|████████████| 424/424 [00:00<00:00, 61506.01it/s]
2026-11-02 17:11:08 - whisperx.transcribe - INFO - Performing alignment...
...(中略)...
[17:11:15.823212] 完了: C:\Users\rinsaka\Documents\python\watchdog\done\voice-sample02.m4a
[17:11:15.823487] 処理開始: voice-sample03.m4a
[17:11:15.824446] 作業開始: C:\Users\rinsaka\Documents\python\watchdog\working\voice-sample03.m4a
[17:11:15.824496] 出力先 : C:\Users\rinsaka\Documents\python\watchdog\working\voice-sample03
[17:11:15.824536] 処理中...
...(中略)...
Transcript: [0.031 --> 25.175] 先週羽田空港を離陸した日光機のタイヤがパンクしたトラブルで離陸後の滑走路で金属片が浮き上がっていた様子が公開されましたタイヤがパンクしたのは先月29日の午前羽田空港から鹿児島に向け離陸した日本航空の機体でその後成田空港に緊急着陸しました
Transcript: [26.356 --> 50.977] 国土交通省によりますと、離陸後の滑走路では、早朝の点検ではなかった最大8センチの金属片の浮き上がりが確認されたということです。パンクとの因果関係はわかっていません。金属片は、桟橋と埋め立て地のつなぎ目のゴムに包まれていました。こうした構造の滑走路は国内唯一で、
Transcript: [51.348 --> 65.607] 16年前の供用開始以降金属片の浮き上がりはなかったといいますこの4日前には同じ滑走路を離陸したスカイマークの機体でもタイヤがパンクしその後引き返しています
Loading weights: 100%|████████████| 424/424 [00:00<00:00, 43408.06it/s]
2026-11-02 17:11:29 - whisperx.transcribe - INFO - Performing alignment...
...(中略)...
[17:11:37.138667] 完了: C:\Users\rinsaka\Documents\python\watchdog\done\voice-sample03.m4a
Ctrl + C
[17:11:46.725781] 停止します
(py313watchdog) PS C:\Users\rinsaka\Documents\python\watchdog>

完了用 done フォルダには処理を終えたオーディオファイルが移動しています.一方で,文字起こし・話者分離の結果は作業用 working フォルダに残ったままになっていることに注意してください.なお,-Recurse オプションはサブフォルダも含めて再帰的に一覧を取得するためのオプションです.

(py313watchdog) PS C:\Users\rinsaka\Documents\python\watchdog> gci done ⏎

    ディレクトリ:C:\Users\rinsaka\Documents\python\watchdog\done

Mode                 LastWriteTime         Length Name
----                 -------------         ------ ----
-a---          2026/10/27    12:36         329637 voice-sample01.m4a
-a---          2026/10/27    12:39         554089 voice-sample02.m4a
-a---          2026/10/27    12:41         596090 voice-sample03.m4a
-a---          2026/10/27    12:42         612295 voice-sample04.m4a

(py313watchdog) PS C:\Users\rinsaka\Documents\python\watchdog> gci working ⏎

    ディレクトリ:C:\Users\rinsaka\Documents\python\watchdog\working

Mode                 LastWriteTime         Length Name
----                 -------------         ------ ----
d----          2026/11/02    17:10                voice-sample01
d----          2026/11/02    17:11                voice-sample02
d----          2026/11/02    17:11                voice-sample03
d----          2026/11/02    17:10                voice-sample04

(py313watchdog) PS C:\Users\rinsaka\Documents\python\watchdog> gci working -Recurse ⏎

    ディレクトリ:C:\Users\rinsaka\Documents\python\watchdog\working

Mode                 LastWriteTime         Length Name
----                 -------------         ------ ----
d----          2026/11/02    17:10                voice-sample01
d----          2026/11/02    17:11                voice-sample02
d----          2026/11/02    17:11                voice-sample03
d----          2026/11/02    17:10                voice-sample04

    ディレクトリ:C:\Users\rinsaka\Documents\python\watchdog\working\voice-sample01

Mode                 LastWriteTime         Length Name
----                 -------------         ------ ----
-a---          2026/11/02    17:10          28431 voice-sample01.json
-a---          2026/11/02    17:10            566 voice-sample01.srt
-a---          2026/11/02    17:10            505 voice-sample01.tsv
-a---          2026/11/02    17:10            494 voice-sample01.txt
-a---          2026/11/02    17:10            558 voice-sample01.vtt

    ディレクトリ:C:\Users\rinsaka\Documents\python\watchdog\working\voice-sample02

Mode                 LastWriteTime         Length Name
----                 -------------         ------ ----
-a---          2026/11/02    17:11          58602 voice-sample02.json
-a---          2026/11/02    17:11           1113 voice-sample02.srt
-a---          2026/11/02    17:11           1014 voice-sample02.tsv
-a---          2026/11/02    17:11           1005 voice-sample02.txt
-a---          2026/11/02    17:11           1096 voice-sample02.vtt

    ディレクトリ:C:\Users\rinsaka\Documents\python\watchdog\working\voice-sample03

Mode                 LastWriteTime         Length Name
----                 -------------         ------ ----
-a---          2026/11/02    17:11          59247 voice-sample03.json
-a---          2026/11/02    17:11           1119 voice-sample03.srt
-a---          2026/11/02    17:11           1020 voice-sample03.tsv
-a---          2026/11/02    17:11           1011 voice-sample03.txt
-a---          2026/11/02    17:11           1102 voice-sample03.vtt

    ディレクトリ:C:\Users\rinsaka\Documents\python\watchdog\working\voice-sample04

Mode                 LastWriteTime         Length Name
----                 -------------         ------ ----
-a---          2026/11/02    17:10          61102 voice-sample04.json
-a---          2026/11/02    17:10           1149 voice-sample04.srt
-a---          2026/11/02    17:10           1050 voice-sample04.tsv
-a---          2026/11/02    17:10           1041 voice-sample04.txt
-a---          2026/11/02    17:10           1132 voice-sample04.vtt

(py313watchdog) PS C:\Users\rinsaka\Documents\python\watchdog>

拡張子が .txt のファイルには次のような情報が記録されています.

(py313watchdog) PS C:\Users\rinsaka\Documents\python\watchdog> Get-Content working\voice-sample01\voice-sample01.txt ⏎
[SPEAKER_00]: 日銀の上田総裁はきょう都内で講演し、中東情勢が不透明な状況が続いたとしても、利上げを行う可能性があるとの考えを示しました。日銀は今月15日から2日間、金融政策決定会合を開きます。会合を前に都内で講演した日銀の上田総裁は、
[SPEAKER_00]: 仮に中東情勢が不透明な状況が続くとしても、利上げに踏み切る可能性があるとの考えを示しました。
(py313watchdog) PS C:\Users\rinsaka\Documents\python\watchdog>

一方で,拡張子が .vtt のファイルには時間情報も残されています.

(py313watchdog) PS C:\Users\rinsaka\Documents\python\watchdog> Get-Content working\voice-sample01\voice-sample01.vtt ⏎
WEBVTT

00:03.493 --> 00:26.681
[SPEAKER_00]: 日銀の上田総裁はきょう都内で講演し、中東情勢が不透明な状況が続いたとしても、利上げを行う可能性があるとの考えを示しました。日銀は今月15日から2日間、金融政策決定会合を開きます。会合を前に都内で講演した日銀の上田総裁は、

00:27.341 --> 00:34.784
[SPEAKER_00]: 仮に中東情勢が不透明な状況が続くとしても、利上げに踏み切る可能性があるとの考えを示しました。

(py313watchdog) PS C:\Users\rinsaka\Documents\python\watchdog>

ここまでで音声ファイルから自動的に文字起こしを処理する部分までができました.ただし,出力結果が作業用 working フォルダに残ったままになっています.次のページではこれらを完了用 done フォルダに移動する要します.

目次に戻る