Rework fs monitor

This commit is contained in:
Zoe Roux 2025-05-07 22:18:30 +02:00
parent e3a04c0e0f
commit b3a13882c1
No known key found for this signature in database
2 changed files with 49 additions and 55 deletions

View File

@ -1,48 +0,0 @@
from logging import getLogger
from os.path import isdir, dirname, exists, join
from watchfiles import awatch, Change
from .publisher import Publisher
from .scanner import scan, get_ignore_pattern
from providers.kyoo_client import KyooClient
logger = getLogger(__name__)
def is_ignored_path(path: str) -> bool:
"""Check if the path is within a directory that contains a `.ignore` file."""
current_path = path
while current_path != "/": # Traverse up to the root directory
if exists(join(current_path, ".ignore")):
return True
current_path = dirname(current_path)
return False
async def monitor(path: str, publisher: Publisher, client: KyooClient):
ignore_pattern = get_ignore_pattern()
async for changes in awatch(path, ignore_permission_denied=True):
for event, file in changes:
# Check for ignore conditions
if is_ignored_path(file):
logger.info(
"Ignoring event %s for file %s (due to .ignore file)", event, file
)
continue
if ignore_pattern and ignore_pattern.match(file):
logger.info(
"Ignoring event %s for file %s (due to IGNORE_PATTERN)", event, file
)
continue
logger.info("Change %s occurred for file %s", event, file)
match event:
case Change.added if isdir(file):
await scan(file, publisher, client)
case Change.added:
await publisher.add(file)
case Change.deleted:
await publisher.delete(file)
case Change.modified:
pass
case _:
logger.warning("Unknown file event %s (for file %s)", event, file)

View File

@ -2,12 +2,15 @@ import os
import re import re
from logging import getLogger from logging import getLogger
from mimetypes import guess_file_type from mimetypes import guess_file_type
from os.path import dirname, exists, isdir, join
from typing import Optional from typing import Optional
from watchfiles import Change, awatch
from .client import KyooClient from .client import KyooClient
from .identify import identify from .identify import identify
from .models.metadataid import EpisodeId, MetadataId from .models.metadataid import EpisodeId, MetadataId
from .models.videos import For, Guess, Video, VideoInfo from .models.videos import For, Video, VideoInfo
from .queue import Request, enqueue from .queue import Request, enqueue
logger = getLogger(__name__) logger = getLogger(__name__)
@ -25,9 +28,14 @@ def get_ignore_pattern():
ignore_pattern = get_ignore_pattern() ignore_pattern = get_ignore_pattern()
def is_video(path: str) -> bool: def is_ignored_path(path: str) -> bool:
(mime, _) = guess_file_type(path, strict=False) current_path = path
return mime is not None and mime.startswith("video/") # Traverse up to the root directory
while current_path != "/":
if exists(join(current_path, ".ignore")):
return True
current_path = dirname(current_path)
return False
def walk_fs(root_path: str) -> set[str]: def walk_fs(root_path: str) -> set[str]:
@ -49,11 +57,17 @@ def walk_fs(root_path: str) -> set[str]:
return videos return videos
def is_video(path: str) -> bool:
(mime, _) = guess_file_type(path, strict=False)
return mime is not None and mime.startswith("video/")
async def scan(path: Optional[str], client: KyooClient, remove_deleted=False): async def scan(path: Optional[str], client: KyooClient, remove_deleted=False):
if path is None:
logger.info("Starting scan at %s. This may take some time...", path)
if ignore_pattern:
logger.info(f"Applying ignore pattern: {ignore_pattern}")
path = path or os.environ.get("SCANNER_LIBRARY_ROOT", "/video") path = path or os.environ.get("SCANNER_LIBRARY_ROOT", "/video")
logger.info("Starting scan at %s. This may take some time...", path)
if ignore_pattern:
logger.info(f"Applying ignore pattern: {ignore_pattern}")
videos = walk_fs(path) videos = walk_fs(path)
info = await client.get_videos_info() info = await client.get_videos_info()
@ -101,6 +115,34 @@ async def scan(path: Optional[str], client: KyooClient, remove_deleted=False):
logger.info("Scan finished for %s.", path) logger.info("Scan finished for %s.", path)
async def monitor(path: str, client: KyooClient):
async for changes in awatch(path, ignore_permission_denied=True):
for event, file in changes:
if not isdir(file) and not is_video(file):
continue
if ignore_pattern and ignore_pattern.match(file) or is_ignored_path(file):
logger.info("Ignoring event %s for file %s", event, file)
continue
match event:
case Change.added if isdir(file):
logger.info("New dir found: %s", file)
await scan(file, client)
case Change.added:
logger.info("New video found: %s", file)
try:
vid = await identify(file)
vid = match(info, vid)
await client.create_videos([vid])
except Exception as e:
logger.error("Couldn't identify %s.", file, exc_info=e)
case Change.deleted:
logger.info("Delete video at: %s", file)
await client.delete_videos([file])
case Change.modified:
pass
def match(info: VideoInfo, video: Video) -> Video: def match(info: VideoInfo, video: Video) -> Video:
video.for_ = [] video.for_ = []