142 lines
6.9 KiB
Python
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.")
|