Astro-Session-Watcher/services/watch_service.py

119 lines
4.5 KiB
Python
Raw Normal View History

2026-05-07 17:15:56 +03:00
"""
WatchService - сервис отслеживания файлов с очередью
"""
import time
import threading
import queue
from pathlib import Path
from typing import Callable, Optional
from watchdog.observers import Observer
from watchdog.events import FileSystemEventHandler
from services.file_service import FileService
class PhotoHandler(FileSystemEventHandler):
"""Обработчик событий файловой системы"""
def __init__(self, callback: Callable[[Path], None]):
self.callback = callback
self._pending_files = queue.Queue()
self._processing = True
self._processor_thread = threading.Thread(target=self._process_queue, daemon=True)
self._processor_thread.start()
def on_created(self, event):
if not event.is_directory:
src_path = Path(event.src_path)
if FileService.is_photo(src_path):
print(f"[Watchdog] Обнаружен файл: {src_path}")
time.sleep(0.1)
self._pending_files.put(src_path)
def on_modified(self, event):
"""Также обрабатываем modified, так как некоторые программы сначала создают временный файл"""
if not event.is_directory:
src_path = Path(event.src_path)
if FileService.is_photo(src_path):
print(f"[Watchdog] Изменён файл: {src_path}")
time.sleep(0.1)
2026-05-07 17:15:56 +03:00
self._pending_files.put(src_path)
def _process_queue(self):
while self._processing:
try:
file_path = self._pending_files.get(timeout=1)
if self.callback and file_path.exists():
self.callback(file_path)
except queue.Empty:
continue
except Exception as e:
print(f"Ошибка обработки файла: {e}")
def stop(self):
self._processing = False
class WatchService:
"""Сервис для отслеживания папки на новые файлы"""
def __init__(self):
self._observer: Optional[Observer] = None
self._event_handler: Optional[PhotoHandler] = None
self._is_running = False
def start(self, watch_folder: Path, on_new_file: Callable[[Path], None]) -> bool:
"""Запускает отслеживание папки"""
2026-05-07 17:15:56 +03:00
if self._is_running:
print("Watcher already running")
2026-05-07 17:15:56 +03:00
return False
if not watch_folder.exists():
print(f"Папка не существует: {watch_folder}")
2026-05-07 17:15:56 +03:00
return False
try:
print(f"Запуск отслеживания папки: {watch_folder}")
2026-05-07 17:15:56 +03:00
self._event_handler = PhotoHandler(on_new_file)
self._observer = Observer()
self._observer.schedule(self._event_handler, str(watch_folder), recursive=False)
self._observer.start()
self._is_running = True
print(f"Отслеживание успешно запущено для: {watch_folder}")
2026-05-07 17:15:56 +03:00
return True
except Exception as e:
print(f"Ошибка запуска отслеживания: {e}")
import traceback
traceback.print_exc()
2026-05-07 17:15:56 +03:00
return False
def stop(self):
"""Останавливает отслеживание"""
print("Остановка отслеживания...")
2026-05-07 17:15:56 +03:00
if self._observer:
self._observer.stop()
self._observer.join()
self._observer = None
if self._event_handler:
self._event_handler.stop()
self._event_handler = None
self._is_running = False
print("Отслеживание остановлено")
2026-05-07 17:15:56 +03:00
def is_running(self) -> bool:
return self._is_running
def move_all_existing_files(self, watch_folder: Path, on_file_moved: Callable[[Path], None]) -> int:
"""Перемещает все существующие файлы из папки наблюдения"""
2026-05-07 17:15:56 +03:00
count = 0
if watch_folder.exists():
for file_path in watch_folder.iterdir():
if file_path.is_file() and FileService.is_photo(file_path):
try:
on_file_moved(file_path)
count += 1
time.sleep(0.05)
except Exception as e:
print(f"Ошибка перемещения {file_path.name}: {e}")
return count