import sqlite3 import os import logging from .config import Settings logger = logging.getLogger(__name__) class StashService: def __init__(self, settings: Settings): self.settings = settings self.db_path = settings.stash_db_path def get_db_connection(self): if not os.path.exists(self.db_path): logger.warning(f"Stash database not found at {self.db_path}") return None return sqlite3.connect(self.db_path) def translate_to_stash_path(self, local_path): """ Translates a local/SMB path to Stash internal container path. Example: /media/videos/Girl/Scene.mp4 -> /data/Girl/Scene.mp4 """ remote_base = self.settings.stash_remote_base.rstrip('/') container_base = self.settings.stash_container_base.rstrip('/') if local_path.startswith(remote_base): return local_path.replace(remote_base, container_base, 1) # If it doesn't start with remote_base, maybe it's already relative or formatted differently? # Stash also uses basenames in 'files' table. return local_path def get_file_metadata(self, local_path): """ Returns (phash, oshash, scene_id, duration) from Stash DB for a given file. """ stash_path = self.translate_to_stash_path(local_path) conn = self.get_db_connection() if not conn: return None, None, None, None try: cursor = conn.cursor() cursor.execute("SELECT id FROM files WHERE path = ?", (stash_path,)) row = cursor.fetchone() if not row: return None, None, None, None file_id = row[0] # Get fingerprints cursor.execute("SELECT type, fingerprint FROM files_fingerprints WHERE file_id = ?", (file_id,)) fingerprints = cursor.fetchall() phash = None oshash = None for f_type, f_val in fingerprints: if f_type == 'phash': phash = hex(int(f_val) & 0xffffffffffffffff)[2:].zfill(16) elif f_type == 'oshash': oshash = f_val # Get scene_id cursor.execute("SELECT scene_id FROM scenes_files WHERE file_id = ?", (file_id,)) scene_row = cursor.fetchone() scene_id = scene_row[0] if scene_row else None # Get duration cursor.execute("SELECT duration FROM video_files WHERE file_id = ?", (file_id,)) dur_row = cursor.fetchone() duration = dur_row[0] if dur_row else None return phash, oshash, scene_id, duration except Exception as e: logger.error(f"Failed to query Stash metadata for {local_path}: {e}") return None, None, None, None finally: conn.close() def get_sprite_path(self, oshash): """ Returns the remote SMB path for the sprite. Example: /media/stashapp/generated/vtt/{oshash}_sprite.jpg """ if not oshash: return None return f"{self.settings.stash_generated_path}/vtt/{oshash}_sprite.jpg"