ここまで作成してきた監視プログラム (watch.py) は,監視用フォルダに音声ファイルをダウンロードして保存しようとしたとき,音声ファイルの全てのダウンロードが完了する前に待ち行列用フォルダに移動する処理が動いてしまう可能性があります.この問題を解決するために,前のページではファイルサイズが変わらなくなるまでコピー完了を判断しないという処理を考えました.ここでは監視プログラム (watch.py) にファイルサイズ検証によるコピー完了判断の機能を追加します.
具体的には次のとおり,wait_until_complete という関数を定義して,ファイルサイズの検証によるコピー完了確認を行います.ここで,念には念を入れるという意味で,5秒間のスリープを2回いれ,3回のファイルサイズ判定でいずれも変化がない場合に完了の判断を行っています(実際にはここまでやらなくても良いかもしれません).また,スリープしている間にファイル名を変更したり,削除するというようなイタズラをしたとしてもプログラムが強制終了しないように例外処理も加えています.
watch.py
from pathlib import Path
from watchdog.observers import Observer
from watchdog.events import FileSystemEventHandler
import shutil # ファイルのコピー,移動などを行う標準ライブラリ
import time
BASE_DIR = Path(__file__).parent
WATCH_DIR = BASE_DIR / "watch"
QUEUE_DIR = BASE_DIR / "queue"
# ディレクトリを作っておく
WATCH_DIR.mkdir(parents=True, exist_ok=True)
QUEUE_DIR.mkdir(parents=True, exist_ok=True)
def wait_until_complete(path):
"""
watch ディレクトリへのコピー,ファイルサイズの変化がない -> コピー(ダウンロード)が終わったと判断
待っている間にファイル名が変更されたら強制終了
"""
while True:
try:
size1 = path.stat().st_size
time.sleep(5)
size2 = path.stat().st_size
time.sleep(5)
size3 = path.stat().st_size
if size1 == size2 == size3:
print("ファイルの受取りが完了しました")
return True
except FileNotFoundError:
print(f"ファイルが見つかりません: {path}") # 途中でファイル名を変更したり,削除した場合
return False
class MyHandler(FileSystemEventHandler):
"""
watchdog が用意している FileSystemEventHandler クラスの継承
「ファイル監視イベントが発生したときに何をするか」を定義する
"""
def on_created(self, event):
if event.is_directory: # ディレクトリは無視
return
src = Path(event.src_path)
print(f"新しいファイルを検出: {src}")
print(f"新しいファイルの名前: {src.name}")
if not wait_until_complete(src):
return
dst = QUEUE_DIR / src.name # 移動先の指定
print(f"ファイルの移動開始: {dst}")
shutil.move(src, dst) # ファイルの移動
print(f"ファイルの移動完了: {dst}")
observer = Observer() # 「監視員を作成する」処理
# 監視条件設定
observer.schedule(
MyHandler(),
path=WATCH_DIR,
recursive=False # とりあえず直下だけを監視
)
observer.start() # 監視を始める → 別スレッド(observerスレッド)が生成される
try:
while True:
time.sleep(1)
except KeyboardInterrupt:
print("停止します")
observer.stop()
observer.join() # observerスレッドが終わるまで待つ
次はワーカープログラムです.これまではマークダンファイルだけを走査していましたが,音声ファイルなども処理ができるように,監視対象とする拡張子を変更しました.また音声ファイルの中身を表示させることは現実的ではないため,中身を表示せず「処理中」とだけ表示するように微調整も行っています.
worker.py
from pathlib import Path
import shutil
import time
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"処理開始: {src.name}")
working_file = WORKING_DIR / src.name
shutil.move(src, working_file)
print(f"作業開始: {working_file}")
print("処理中...")
time.sleep(25)
# with open(working_file, "r", encoding="utf-8") as f:
# print(f.read())
dst = DONE_DIR / src.name
shutil.move(working_file, dst)
print(f"完了: {dst}")
time.sleep(1)
except KeyboardInterrupt:
print("停止します")
ターミナルを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 ⏎
プログラムを起動できたら,4個のマークダウンファイルを監視用の watch フォルダに設置します.サイズの小さなファイルであっても5秒間待ってサイズに変化がないことを確認してから待ち行列用 queue フォルダに移動する様子が分かるはずです.
(py313watchdog) PS C:\Users\rinsaka\Documents\python\watchdog> python watch.py
新しいファイルを検出: C:\Users\rinsaka\Documents\python\watchdog\watch\sample1.md
新しいファイルの名前: sample1.md
ファイルの受取りが完了しました
ファイルの移動開始: C:\Users\rinsaka\Documents\python\watchdog\queue\sample1.md
ファイルの移動完了: C:\Users\rinsaka\Documents\python\watchdog\queue\sample1.md
新しいファイルを検出: C:\Users\rinsaka\Documents\python\watchdog\watch\sample2.md
新しいファイルの名前: sample2.md
ファイルの受取りが完了しました
ファイルの移動開始: C:\Users\rinsaka\Documents\python\watchdog\queue\sample2.md
ファイルの移動完了: C:\Users\rinsaka\Documents\python\watchdog\queue\sample2.md
新しいファイルを検出: C:\Users\rinsaka\Documents\python\watchdog\watch\sample3.md
新しいファイルの名前: sample3.md
ファイルの受取りが完了しました
ファイルの移動開始: C:\Users\rinsaka\Documents\python\watchdog\queue\sample3.md
ファイルの移動完了: C:\Users\rinsaka\Documents\python\watchdog\queue\sample3.md
新しいファイルを検出: C:\Users\rinsaka\Documents\python\watchdog\watch\sample4.md
新しいファイルの名前: sample4.md
ファイルの受取りが完了しました
ファイルの移動開始: C:\Users\rinsaka\Documents\python\watchdog\queue\sample4.md
ファイルの移動完了: C:\Users\rinsaka\Documents\python\watchdog\queue\sample4.md
Ctrl + C
停止します
(py313watchdog) PS C:\Users\rinsaka\Documents\python\watchdog>
ワーカープログラムは待ち行列用 queue フォルダにあるファイルから一個を取り出して作業用 working フォルダに移動し,ファイル名だけを表示して完了用 done フォルダに格納します.
(py313watchdog) PS C:\Users\rinsaka\Documents\python\watchdog> python worker.py
処理開始: sample1.md
作業開始: C:\Users\rinsaka\Documents\python\watchdog\working\sample1.md
処理中...
完了: C:\Users\rinsaka\Documents\python\watchdog\done\sample1.md
処理開始: sample2.md
作業開始: C:\Users\rinsaka\Documents\python\watchdog\working\sample2.md
処理中...
完了: C:\Users\rinsaka\Documents\python\watchdog\done\sample2.md
処理開始: sample3.md
作業開始: C:\Users\rinsaka\Documents\python\watchdog\working\sample3.md
処理中...
完了: C:\Users\rinsaka\Documents\python\watchdog\done\sample3.md
処理開始: sample4.md
作業開始: C:\Users\rinsaka\Documents\python\watchdog\working\sample4.md
処理中...
完了: C:\Users\rinsaka\Documents\python\watchdog\done\sample4.md
Ctrl + C
停止します
(py313watchdog) PS C:\Users\rinsaka\Documents\python\watchdog>
全ての作業が終わると done フォルダに格納されます.
(py313watchdog) PS C:\Users\rinsaka\Documents\python\watchdog> gci done ⏎
ディレクトリ:C:\Users\rinsaka\Documents\python\watchdog\done
Mode LastWriteTime Length Name
---- ------------- ------ ----
-a--- 2026/10/25 18:18 56 sample1.md
-a--- 2026/10/25 18:18 115 sample2.md
-a--- 2026/10/25 18:19 47 sample3.md
-a--- 2026/10/25 18:19 59 sample4.md
(py313watchdog) PS C:\Users\rinsaka\Documents\python\watchdog>
なお,4つのファイルを監視用 watch フォルダに格納した直後には watch フォルダに4個全てのファイルがあります.
(py313watchdog) PS C:\Users\rinsaka\Documents\python\watchdog> gci watch ⏎
ディレクトリ:C:\Users\rinsaka\Documents\python\watchdog\watch
Mode LastWriteTime Length Name
---- ------------- ------ ----
-a--- 2026/10/25 18:18 56 sample1.md
-a--- 2026/10/25 18:18 115 sample2.md
-a--- 2026/10/25 18:19 47 sample3.md
-a--- 2026/10/25 18:19 59 sample4.md
(py313watchdog) PS C:\Users\rinsaka\Documents\python\watchdog>
数十秒経過後は監視用 watch,待ち行列用 queue,作業用 working,完了用 done フォルダに1個ずつのファイルがある状態になりました.
(py313watchdog) PS C:\Users\rinsaka\Documents\python\watchdog> gci watch ⏎ ディレクトリ:C:\Users\rinsaka\Documents\python\watchdog\watch Mode LastWriteTime Length Name ---- ------------- ------ ---- -a--- 2026/10/25 18:19 59 sample4.md (py313watchdog) PS C:\Users\rinsaka\Documents\python\watchdog> gci queue ⏎ ディレクトリ:C:\Users\rinsaka\Documents\python\watchdog\queue Mode LastWriteTime Length Name ---- ------------- ------ ---- -a--- 2026/10/25 18:19 47 sample3.md (py313watchdog) PS C:\Users\rinsaka\Documents\python\watchdog> gci working ⏎ ディレクトリ:C:\Users\rinsaka\Documents\python\watchdog\working Mode LastWriteTime Length Name ---- ------------- ------ ---- -a--- 2026/10/25 18:18 115 sample2.md (py313watchdog) PS C:\Users\rinsaka\Documents\python\watchdog> gci done ⏎ ディレクトリ:C:\Users\rinsaka\Documents\python\watchdog\done Mode LastWriteTime Length Name ---- ------------- ------ ---- -a--- 2026/10/25 18:18 56 sample1.md (py313watchdog) PS C:\Users\rinsaka\Documents\python\watchdog>
さらに少し経過すると,1個が作業中で,待ち行列に1個,完了が2個という状態になりました.
(py313watchdog) PS C:\Users\rinsaka\Documents\python\watchdog> gci queue ⏎ ディレクトリ:C:\Users\rinsaka\Documents\python\watchdog\queue Mode LastWriteTime Length Name ---- ------------- ------ ---- -a--- 2026/10/25 18:19 59 sample4.md (py313watchdog) PS C:\Users\rinsaka\Documents\python\watchdog> gci working ⏎ ディレクトリ:C:\Users\rinsaka\Documents\python\watchdog\working Mode LastWriteTime Length Name ---- ------------- ------ ---- -a--- 2026/10/25 18:19 47 sample3.md (py313watchdog) PS C:\Users\rinsaka\Documents\python\watchdog> gci done ⏎ ディレクトリ:C:\Users\rinsaka\Documents\python\watchdog\done Mode LastWriteTime Length Name ---- ------------- ------ ---- -a--- 2026/10/25 18:18 56 sample1.md -a--- 2026/10/25 18:18 115 sample2.md (py313watchdog) PS C:\Users\rinsaka\Documents\python\watchdog>
すべての処理が終わると,完了用以外のフォルダは空になったことが分かります.
(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> gci done ⏎ ディレクトリ:C:\Users\rinsaka\Documents\python\watchdog\done Mode LastWriteTime Length Name ---- ------------- ------ ---- -a--- 2026/10/25 18:18 56 sample1.md -a--- 2026/10/25 18:18 115 sample2.md -a--- 2026/10/25 18:19 47 sample3.md -a--- 2026/10/25 18:19 59 sample4.md (py313watchdog) PS C:\Users\rinsaka\Documents\python\watchdog>