Files
personal_development/servermanagerbase/stashtoolkit_webtop/backend/test_db_sync.py
T
2026-01-03 13:30:35 -05:00

142 lines
6.9 KiB
Python

import os
import sqlite3
import smbclient
import tempfile
import json
import threading
from unittest.mock import patch, MagicMock
# --- App Initialization & Path Configuration ---
APP_ROOT = os.path.dirname(os.path.abspath(__file__))
INSTANCE_PATH = os.path.join(os.path.dirname(APP_ROOT), 'instance')
LOCAL_DB_PATH = "local_cache.db"
CONFIG_FILE = "config.json"
REMOTE_DB_LOCK = threading.Lock()
DB_PATH = "//servervm.local/main/appdata/stashapp/config/stash-go.sqlite"
credentials = {}
def get_local_db():
db_path = os.path.join(INSTANCE_PATH, LOCAL_DB_PATH)
conn = sqlite3.connect(db_path)
conn.row_factory = sqlite3.Row
return conn
def init_local_db():
"""Initializes and resets cache tables to ensure schema is correct."""
try:
os.makedirs(INSTANCE_PATH)
except OSError:
pass # Already exists
with get_local_db() as conn:
cursor = conn.cursor()
cursor.execute("CREATE TABLE IF NOT EXISTS config (key TEXT PRIMARY KEY, value TEXT)")
cursor.execute("CREATE TABLE IF NOT EXISTS sync_metadata (key TEXT PRIMARY KEY, value TEXT)")
# cursor.execute("CREATE TABLE IF NOT EXISTS scan_history (id INTEGER PRIMARY KEY AUTOINCREMENT, scan_type TEXT NOT NULL, timestamp DATETIME DEFAULT CURRENT_TIMESTAMP, status TEXT NOT NULL, message TEXT, log TEXT)")
# cursor.execute("CREATE TABLE IF NOT EXISTS duplicate_results (id INTEGER PRIMARY KEY AUTOINCREMENT, scan_id INTEGER NOT NULL, file_path TEXT NOT NULL, file_size INTEGER, file_basename TEXT, set_id INTEGER NOT NULL, FOREIGN KEY (scan_id) REFERENCES scan_history (id) ON DELETE CASCADE)")
# cursor.execute("CREATE TABLE IF NOT EXISTS transcode_plan_results (id INTEGER PRIMARY KEY AUTOINCREMENT, scan_id INTEGER NOT NULL, original_path TEXT NOT NULL, transcoded_path TEXT NOT NULL, FOREIGN KEY (scan_id) REFERENCES scan_history (id) ON DELETE CASCADE)")
print("Rebuilding local cache tables to ensure schema is up-to-date.")
cursor.execute("DROP TABLE IF EXISTS scenes")
cursor.execute("DROP TABLE IF EXISTS files")
cursor.execute("DROP TABLE IF EXISTS paths")
cursor.execute("CREATE TABLE scenes (id INTEGER PRIMARY KEY, oshash TEXT)")
cursor.execute("CREATE TABLE files (scene_id INTEGER, path TEXT, basename TEXT, path_id INTEGER)")
cursor.execute("CREATE TABLE paths (id INTEGER PRIMARY KEY, path TEXT)")
conn.commit()
def load_credentials():
global credentials
# In a test script, we can simplify this to just load from the migrated file
# if it exists, for simplicity.
old_config_path = os.path.join(os.path.dirname(APP_ROOT), CONFIG_FILE + ".migrated")
if os.path.exists(old_config_path):
print("Migrating credentials from config.json.migrated...")
try:
with open(old_config_path, "r") as f:
credentials = json.load(f)
print("Successfully loaded credentials from migrated config.")
except (json.JSONDecodeError, OSError) as e:
print(f"Error loading from config.json.migrated: {e}")
credentials = {}
else:
print("config.json.migrated not found. No credentials loaded.")
def get_smb_credentials(path):
server = path.split("/")[2].split('@')[-1]
return credentials.get(server, {})
def sync_remote_db():
print("Background sync thread started.")
print("Sync thread trying to acquire DB lock...")
with REMOTE_DB_LOCK:
print("Sync thread acquired DB lock.")
try:
creds = get_smb_credentials(DB_PATH)
remote_stat = smbclient.stat(DB_PATH, **creds)
remote_mtime = remote_stat.st_mtime
except Exception as e:
print(f"Sync thread failed to check remote DB status: {e}")
return
with get_local_db() as conn_local:
row = conn_local.execute("SELECT value FROM sync_metadata WHERE key = 'last_sync_mtime'").fetchone()
last_sync_mtime = float(row['value']) if (row and row['value']) else 0
# For testing, let's always sync
# if remote_mtime <= last_sync_mtime:
# print("Local database is already up to date.")
# return
print(f"Remote DB is newer. Syncing from {remote_mtime} > {last_sync_mtime}.")
temp_db_path = None
try:
with smbclient.open_file(DB_PATH, mode='rb', **creds) as smb_file:
with tempfile.NamedTemporaryFile(delete=False, suffix=".sqlite") as temp_db:
temp_db.write(smb_file.read())
temp_db_path = temp_db.name
with sqlite3.connect(temp_db_path) as conn_remote:
conn_local.execute("DELETE FROM scenes"); conn_local.execute("DELETE FROM files"); conn_local.execute("DELETE FROM paths")
cursor_remote_scenes = conn_remote.execute("SELECT id, oshash FROM scenes")
conn_local.executemany("INSERT INTO scenes (id, oshash) VALUES (?, ?)", cursor_remote_scenes)
cursor_remote_files = conn_remote.execute("SELECT scene_id, path, basename, path_id FROM files")
conn_local.executemany("INSERT INTO files (scene_id, path, basename, path_id) VALUES (?, ?, ?, ?)", cursor_remote_files)
cursor_remote_paths = conn_remote.execute("SELECT id, path FROM paths")
conn_local.executemany("INSERT INTO paths (id, path) VALUES (?, ?)", cursor_remote_paths)
conn_local.execute("REPLACE INTO sync_metadata (key, value) VALUES (?, ?)", ('last_sync_mtime', remote_mtime))
conn_local.commit()
print("Database sync completed successfully.")
except sqlite3.Error as e:
print(f"Error during database sync: {e}")
conn_local.rollback()
finally:
if temp_db_path and os.path.exists(temp_db_path): os.remove(temp_db_path)
print("Sync thread released DB lock.")
if __name__ == '__main__':
print("Setting up test environment...")
init_local_db()
load_credentials()
print("\nStarting DB sync test...")
sync_remote_db()
print("\nDB sync test finished.")
print("\nVerifying local database content...")
with get_local_db() as conn:
scenes_count = conn.execute("SELECT COUNT(*) FROM scenes").fetchone()[0]
files_count = conn.execute("SELECT COUNT(*) FROM files").fetchone()[0]
paths_count = conn.execute("SELECT COUNT(*) FROM paths").fetchone()[0]
print(f"Scenes table count: {scenes_count}")
print(f"Files table count: {files_count}")
print(f"Paths table count: {paths_count}")
if scenes_count > 0 and files_count > 0 and paths_count > 0:
print("\nVerification successful: Local database appears to be populated.")
else:
print("\nVerification failed: Local database is empty after sync.")