feat(sync): enable scheduled asset synchronization mode

This update introduces a persistent background synchronization mode, allowing the external assets container to run continuously and periodically sync albums instead of executing once and exiting. The system now checks a configurable interval, retries on temporary failures, and manages its lifecycle using `time.sleep`.

- Added SYNC_INTERVAL_SECONDS configuration for scheduling
- Updated compose service to restart automatically and loop indefinitely
- Implemented continuous run logic in main function and added corresponding unit tests
This commit is contained in:
2026-07-23 14:04:13 +02:00
parent 22dcae73ba
commit 7a43db8dd8
5 changed files with 89 additions and 7 deletions
+34 -3
View File
@@ -2,6 +2,7 @@ import logging
import os
import posixpath
import sys
import time
from pathlib import PurePosixPath
from typing import Any
@@ -31,6 +32,9 @@ ALBUM_MODE = os.environ.get("ALBUM_MODE", "root").lower()
ALBUM_SEPARATOR = os.environ.get("ALBUM_SEPARATOR", " - ")
PAGE_SIZE = int(os.environ.get("PAGE_SIZE", "1000"))
DRY_RUN = os.environ.get("DRY_RUN", "false").lower() == "true"
SYNC_INTERVAL_SECONDS = int(
os.environ.get("SYNC_INTERVAL_SECONDS", "0")
)
logging.basicConfig(
level=logging.INFO,
@@ -73,6 +77,11 @@ def check_configuration() -> None:
if not EXTERNAL_ROOT.startswith("/"):
raise RuntimeError("EXTERNAL_ROOT must be an absolute path")
if SYNC_INTERVAL_SECONDS < 0:
raise RuntimeError(
"SYNC_INTERVAL_SECONDS must be zero or greater"
)
def api_request(
method: str,
@@ -232,9 +241,7 @@ def get_external_assets() -> list[dict[str, Any]]:
return assets
def main() -> int:
check_configuration()
def synchronize() -> None:
existing_albums = get_albums()
assets = get_external_assets()
@@ -292,6 +299,30 @@ def main() -> int:
)
logging.info("Synchronization completed")
def main() -> int:
check_configuration()
while True:
try:
synchronize()
except Exception:
if SYNC_INTERVAL_SECONDS == 0:
raise
logging.exception(
"Synchronization failed; the next run will retry"
)
if SYNC_INTERVAL_SECONDS == 0:
break
logging.info(
"Next synchronization in %d seconds",
SYNC_INTERVAL_SECONDS,
)
time.sleep(SYNC_INTERVAL_SECONDS)
return 0