Mappehåndtering
This commit is contained in:
183
linedance-app/local/watchdog_process.py
Normal file
183
linedance-app/local/watchdog_process.py
Normal file
@@ -0,0 +1,183 @@
|
||||
"""
|
||||
watchdog_process.py — Kører som selvstændig subprocess.
|
||||
Overvåger musikmapper og opdaterer SQLite ved fil-ændringer.
|
||||
|
||||
Start: python watchdog_process.py <db_path>
|
||||
"""
|
||||
import sys
|
||||
import os
|
||||
import time
|
||||
import json
|
||||
import sqlite3
|
||||
import logging
|
||||
from pathlib import Path
|
||||
|
||||
logging.basicConfig(
|
||||
level=logging.INFO,
|
||||
format="%(asctime)s [watchdog] %(message)s",
|
||||
stream=sys.stderr
|
||||
)
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
SUPPORTED = {'.mp3', '.flac', '.m4a', '.ogg', '.wav', '.aiff', '.wma'}
|
||||
|
||||
|
||||
def is_supported(path: Path) -> bool:
|
||||
return path.suffix.lower() in SUPPORTED
|
||||
|
||||
|
||||
def get_libraries(db_path: str) -> list[dict]:
|
||||
try:
|
||||
conn = sqlite3.connect(db_path)
|
||||
conn.row_factory = sqlite3.Row
|
||||
libs = conn.execute(
|
||||
"SELECT id, path FROM libraries WHERE is_active=1"
|
||||
).fetchall()
|
||||
conn.close()
|
||||
return [dict(l) for l in libs]
|
||||
except Exception:
|
||||
return []
|
||||
|
||||
|
||||
def process_file(db_path: str, library_id: int, file_path: str,
|
||||
deleted: bool = False):
|
||||
"""Opdater SQLite for én fil."""
|
||||
try:
|
||||
# Tilføj app-mappen til sys.path så tag_reader kan importeres
|
||||
app_dir = str(Path(__file__).parent.parent)
|
||||
if app_dir not in sys.path:
|
||||
sys.path.insert(0, app_dir)
|
||||
|
||||
conn = sqlite3.connect(db_path)
|
||||
conn.row_factory = sqlite3.Row
|
||||
|
||||
if deleted:
|
||||
conn.execute(
|
||||
"UPDATE songs SET file_missing=1 WHERE local_path=?",
|
||||
(file_path,)
|
||||
)
|
||||
conn.commit()
|
||||
conn.close()
|
||||
return
|
||||
|
||||
from local.tag_reader import read_tags
|
||||
import uuid
|
||||
|
||||
mtime = str(os.path.getmtime(file_path))
|
||||
tags = read_tags(Path(file_path))
|
||||
extra = json.dumps(tags.get("extra_tags", {}), ensure_ascii=False)
|
||||
|
||||
existing = conn.execute(
|
||||
"SELECT id, bpm FROM songs WHERE local_path=?", (file_path,)
|
||||
).fetchone()
|
||||
|
||||
if existing:
|
||||
bpm = tags.get("bpm", 0) or existing["bpm"] or 0
|
||||
conn.execute("""
|
||||
UPDATE songs SET
|
||||
library_id=?, title=?, artist=?, album=?,
|
||||
bpm=?, duration_sec=?, file_format=?,
|
||||
file_modified_at=?, file_missing=0, extra_tags=?
|
||||
WHERE id=?
|
||||
""", (library_id, tags.get("title",""), tags.get("artist",""),
|
||||
tags.get("album",""), bpm, tags.get("duration_sec",0),
|
||||
tags.get("file_format",""), mtime, extra, existing["id"]))
|
||||
else:
|
||||
conn.execute("""
|
||||
INSERT OR IGNORE INTO songs
|
||||
(id, library_id, local_path, title, artist, album,
|
||||
bpm, duration_sec, file_format, file_modified_at, extra_tags)
|
||||
VALUES (?,?,?,?,?,?,?,?,?,?,?)
|
||||
""", (str(uuid.uuid4()), library_id, file_path,
|
||||
tags.get("title",""), tags.get("artist",""),
|
||||
tags.get("album",""), tags.get("bpm",0),
|
||||
tags.get("duration_sec",0), tags.get("file_format",""),
|
||||
mtime, extra))
|
||||
|
||||
conn.commit()
|
||||
conn.close()
|
||||
logger.info(f"Opdateret: {Path(file_path).name}")
|
||||
except Exception as e:
|
||||
logger.error(f"Fejl ved {file_path}: {e}")
|
||||
|
||||
|
||||
def run(db_path: str):
|
||||
try:
|
||||
from watchdog.observers.polling import PollingObserver
|
||||
from watchdog.events import FileSystemEventHandler
|
||||
except ImportError:
|
||||
logger.error("watchdog ikke installeret")
|
||||
sys.exit(1)
|
||||
|
||||
class Handler(FileSystemEventHandler):
|
||||
def __init__(self, library_id: int):
|
||||
self.library_id = library_id
|
||||
|
||||
def on_created(self, event):
|
||||
if not event.is_directory and is_supported(Path(event.src_path)):
|
||||
time.sleep(0.5) # Vent til filen er skrevet færdig
|
||||
process_file(db_path, self.library_id, event.src_path)
|
||||
|
||||
def on_modified(self, event):
|
||||
if not event.is_directory and is_supported(Path(event.src_path)):
|
||||
process_file(db_path, self.library_id, event.src_path)
|
||||
|
||||
def on_deleted(self, event):
|
||||
if not event.is_directory and is_supported(Path(event.src_path)):
|
||||
process_file(db_path, self.library_id, event.src_path,
|
||||
deleted=True)
|
||||
|
||||
def on_moved(self, event):
|
||||
if not event.is_directory:
|
||||
if is_supported(Path(event.src_path)):
|
||||
process_file(db_path, self.library_id, event.src_path,
|
||||
deleted=True)
|
||||
if is_supported(Path(event.dest_path)):
|
||||
process_file(db_path, self.library_id, event.dest_path)
|
||||
|
||||
# Brug 60 sekunders poll-interval — opdager ændringer inden for 1 minut
|
||||
observer = PollingObserver(timeout=60)
|
||||
|
||||
libraries = get_libraries(db_path)
|
||||
if not libraries:
|
||||
logger.info("Ingen biblioteker — venter...")
|
||||
|
||||
for lib in libraries:
|
||||
path = Path(lib["path"])
|
||||
if path.exists():
|
||||
observer.schedule(Handler(lib["id"]), str(path), recursive=True)
|
||||
logger.info(f"Overvåger: {path}")
|
||||
else:
|
||||
logger.warning(f"Mappe ikke fundet: {path}")
|
||||
|
||||
observer.start()
|
||||
logger.info("Watchdog kører")
|
||||
|
||||
try:
|
||||
while True:
|
||||
time.sleep(30)
|
||||
# Tjek om der er kommet nye biblioteker siden start
|
||||
current = get_libraries(db_path)
|
||||
current_paths = {lib["path"] for lib in current}
|
||||
watched_paths = {str(w.path) for w in observer.emitters}
|
||||
for lib in current:
|
||||
if lib["path"] not in watched_paths:
|
||||
path = Path(lib["path"])
|
||||
if path.exists():
|
||||
observer.schedule(
|
||||
Handler(lib["id"]), str(path), recursive=True
|
||||
)
|
||||
logger.info(f"Tilføjet overvågning: {path}")
|
||||
except KeyboardInterrupt:
|
||||
pass
|
||||
finally:
|
||||
observer.stop()
|
||||
observer.join()
|
||||
logger.info("Watchdog stoppet")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
if len(sys.argv) < 2:
|
||||
print("Brug: python watchdog_process.py <db_path>")
|
||||
sys.exit(1)
|
||||
run(sys.argv[1])
|
||||
Reference in New Issue
Block a user