mirror of
https://github.com/zoriya/Kyoo.git
synced 2025-05-24 02:02:36 -04:00
Move fs scanner
This commit is contained in:
parent
9b66e7db5f
commit
a35b38755d
@ -13,7 +13,7 @@ LIBRARY_LANGUAGES=en
|
||||
# If this is true, kyoo will prefer to download the media in the original language of the item.
|
||||
MEDIA_PREFER_ORIGINAL_LANGUAGE=false
|
||||
# A pattern (regex) to ignore files.
|
||||
LIBRARY_IGNORE_PATTERN=".*/[dD]ownloads?/.*|.*[Tt][Rr][Aa][Ii][Ll][Ee][Rr].*"
|
||||
LIBRARY_IGNORE_PATTERN=".*/[dD]ownloads?/.*"
|
||||
|
||||
# If this is true, new accounts wont have any permissions before you approve them in your admin dashboard.
|
||||
REQUIRE_ACCOUNT_VERIFICATION=true
|
||||
|
@ -1,12 +1,7 @@
|
||||
# vi: ft=sh
|
||||
# shellcheck disable=SC2034
|
||||
|
||||
# RabbitMQ settings
|
||||
# URL examples: https://docs.aio-pika.com/#url-examples
|
||||
# This uses AIORMQ (https://github.com/mosquito/aiormq/) under the hood, and supports whatever the library supports.
|
||||
# RABBITMQ_URL=ampqs://user:password@rabbitmq-server:1234/vhost?capath=/path/to/cacert.pem&certfile=/path/to/cert.pem&keyfile=/path/to/key.pem
|
||||
# These values are ignored when the RABBITMQ_URL is set
|
||||
RABBITMQ_HOST=rabbitmq
|
||||
RABBITMQ_PORT=5672
|
||||
RABBITMQ_USER=guest
|
||||
RABBITMQ_PASSWORD=guest
|
||||
# Root directory that will be traversed to find video files (inside the container)
|
||||
SCANNER_LIBRARY_ROOT="/video"
|
||||
# A pattern (regex) to ignore video files.
|
||||
LIBRARY_IGNORE_PATTERN=".*/[dD]ownloads?/.*"
|
||||
|
@ -1,73 +0,0 @@
|
||||
import os
|
||||
import re
|
||||
import asyncio
|
||||
from typing import Optional
|
||||
from logging import getLogger
|
||||
|
||||
from .publisher import Publisher
|
||||
from providers.kyoo_client import KyooClient
|
||||
|
||||
logger = getLogger(__name__)
|
||||
|
||||
|
||||
def get_ignore_pattern():
|
||||
try:
|
||||
pattern = os.environ.get("LIBRARY_IGNORE_PATTERN")
|
||||
return re.compile(pattern) if pattern else None
|
||||
except re.error as e:
|
||||
logger.error(f"Invalid ignore pattern. Ignoring. Error: {e}")
|
||||
return None
|
||||
|
||||
|
||||
async def scan(
|
||||
path_: Optional[str], publisher: Publisher, client: KyooClient, remove_deleted=False
|
||||
):
|
||||
path = path_ or os.environ.get("SCANNER_LIBRARY_ROOT", "/video")
|
||||
logger.info("Starting scan at %s. This may take some time...", path)
|
||||
|
||||
ignore_pattern = get_ignore_pattern()
|
||||
if ignore_pattern:
|
||||
logger.info(f"Applying ignore pattern: {ignore_pattern}")
|
||||
|
||||
registered = set(await client.get_registered_paths())
|
||||
videos = set()
|
||||
|
||||
for dirpath, dirnames, files in os.walk(path):
|
||||
# Skip directories with a `.ignore` file
|
||||
if ".ignore" in files:
|
||||
dirnames.clear() # Prevents os.walk from descending into this directory
|
||||
continue
|
||||
|
||||
for file in files:
|
||||
file_path = os.path.join(dirpath, file)
|
||||
# Apply ignore pattern, if any
|
||||
if ignore_pattern and ignore_pattern.match(file_path):
|
||||
continue
|
||||
videos.add(file_path)
|
||||
|
||||
to_register = videos - registered
|
||||
to_delete = registered - videos if remove_deleted else set()
|
||||
|
||||
if not any(to_register) and any(to_delete) and len(to_delete) == len(registered):
|
||||
logger.warning("All video files are unavailable. Check your disks.")
|
||||
return
|
||||
|
||||
# delete stale files before creating new ones to prevent potential conflicts
|
||||
if to_delete:
|
||||
logger.info("Removing %d stale files.", len(to_delete))
|
||||
await asyncio.gather(*[publisher.delete(path) for path in to_delete])
|
||||
|
||||
if to_register:
|
||||
logger.info("Found %d new files to register.", len(to_register))
|
||||
await asyncio.gather(*[publisher.add(path) for path in to_register])
|
||||
|
||||
if remove_deleted:
|
||||
issues = set(await client.get_issues())
|
||||
issues_to_delete = issues - videos
|
||||
if issues_to_delete:
|
||||
logger.info("Removing %d stale issues.", len(issues_to_delete))
|
||||
await asyncio.gather(
|
||||
*[client.delete_issue(issue) for issue in issues_to_delete]
|
||||
)
|
||||
|
||||
logger.info("Scan finished for %s.", path)
|
@ -5,6 +5,7 @@ app = FastAPI(
|
||||
description="API to control the long running scanner or interacting with external databases (themoviedb, tvdb...)\n\n"
|
||||
+ "Most of those APIs are for admins only.",
|
||||
openapi_prefix="/scanner",
|
||||
#lifetime=smth
|
||||
)
|
||||
|
||||
|
||||
|
74
scanner/scanner/fsscan.py
Normal file
74
scanner/scanner/fsscan.py
Normal file
@ -0,0 +1,74 @@
|
||||
|
||||
import os
|
||||
import re
|
||||
import asyncio
|
||||
from typing import Optional
|
||||
from logging import getLogger
|
||||
|
||||
from .publisher import Publisher
|
||||
from providers.kyoo_client import KyooClient
|
||||
|
||||
logger = getLogger(__name__)
|
||||
|
||||
|
||||
def get_ignore_pattern():
|
||||
try:
|
||||
pattern = os.environ.get("LIBRARY_IGNORE_PATTERN")
|
||||
return re.compile(pattern) if pattern else None
|
||||
except re.error as e:
|
||||
logger.error(f"Invalid ignore pattern. Ignoring. Error: {e}")
|
||||
return None
|
||||
|
||||
|
||||
async def scan(
|
||||
path_: Optional[str], publisher: Publisher, client: KyooClient, remove_deleted=False
|
||||
):
|
||||
path = path_ or os.environ.get("SCANNER_LIBRARY_ROOT", "/video")
|
||||
logger.info("Starting scan at %s. This may take some time...", path)
|
||||
|
||||
ignore_pattern = get_ignore_pattern()
|
||||
if ignore_pattern:
|
||||
logger.info(f"Applying ignore pattern: {ignore_pattern}")
|
||||
|
||||
registered = set(await client.get_registered_paths())
|
||||
videos = set()
|
||||
|
||||
for dirpath, dirnames, files in os.walk(path):
|
||||
# Skip directories with a `.ignore` file
|
||||
if ".ignore" in files:
|
||||
dirnames.clear() # Prevents os.walk from descending into this directory
|
||||
continue
|
||||
|
||||
for file in files:
|
||||
file_path = os.path.join(dirpath, file)
|
||||
# Apply ignore pattern, if any
|
||||
if ignore_pattern and ignore_pattern.match(file_path):
|
||||
continue
|
||||
videos.add(file_path)
|
||||
|
||||
to_register = videos - registered
|
||||
to_delete = registered - videos if remove_deleted else set()
|
||||
|
||||
if not any(to_register) and any(to_delete) and len(to_delete) == len(registered):
|
||||
logger.warning("All video files are unavailable. Check your disks.")
|
||||
return
|
||||
|
||||
# delete stale files before creating new ones to prevent potential conflicts
|
||||
if to_delete:
|
||||
logger.info("Removing %d stale files.", len(to_delete))
|
||||
await asyncio.gather(*[publisher.delete(path) for path in to_delete])
|
||||
|
||||
if to_register:
|
||||
logger.info("Found %d new files to register.", len(to_register))
|
||||
await asyncio.gather(*[publisher.add(path) for path in to_register])
|
||||
|
||||
if remove_deleted:
|
||||
issues = set(await client.get_issues())
|
||||
issues_to_delete = issues - videos
|
||||
if issues_to_delete:
|
||||
logger.info("Removing %d stale issues.", len(issues_to_delete))
|
||||
await asyncio.gather(
|
||||
*[client.delete_issue(issue) for issue in issues_to_delete]
|
||||
)
|
||||
|
||||
logger.info("Scan finished for %s.", path)
|
Loading…
x
Reference in New Issue
Block a user