getConnection()->getPdo(); } public static function getCapsule(): Capsule { if (self::$capsule === null) { self::connect(); } return self::$capsule; } private static function connect(): void { self::$capsule = new Capsule(); self::$capsule->addConnection([ 'driver' => self::$config['driver'] ?? 'sqlite', 'host' => self::$config['host'] ?? '127.0.0.1', 'port' => self::$config['port'] ?? 3306, 'database' => self::$config['database'] ?? '', 'username' => self::$config['username'] ?? '', 'password' => self::$config['password'] ?? '', 'charset' => self::$config['charset'] ?? 'utf8mb4', 'prefix' => self::$config['prefix'] ?? '', 'schema' => self::$config['schema'] ?? 'public', ]); // Set as global for Eloquent self::$capsule->setAsGlobal(); // Boot Eloquent (for schema operations) self::$capsule->bootEloquent(); // Set up facade application if (!self::$capsule->getContainer()->bound('app')) { self::$capsule->getContainer()->instance('app', self::$capsule->getContainer()); } } public static function migrate(): void { $capsule = self::getCapsule(); // Create migrations table if it doesn't exist if (!$capsule->schema()->hasTable('migrations')) { $capsule->schema()->create('migrations', function ($table) { $table->unsignedInteger('id', true); // Primary key with AUTO_INCREMENT $table->string('migration'); $table->integer('batch'); $table->timestamps(); }); } // Get list of migration files $migrationFiles = glob(__DIR__ . '/../../database/migrations/*.php'); foreach ($migrationFiles as $file) { $migrationName = basename($file, '.php'); // Check if migration has already been run $ran = $capsule->table('migrations')->where('migration', $migrationName)->exists(); if ($ran) { echo "Migration {$migrationName} already run. Skipping.\n"; continue; } // Run the migration echo "Running migration {$migrationName}\n"; // Include the migration file to make the class available require_once $file; $className = self::getMigrationClassName($file); $pdo = self::getInstance(); $migration = new $className($pdo); $migration->up(); // Record the migration $capsule->table('migrations')->insert([ 'migration' => $migrationName, 'batch' => 1 ]); } } private static function getMigrationClassName(string $file): string { $content = file_get_contents($file); // Extract namespace and class name from PHP file $namespace = ''; if (preg_match('/namespace\s+([^;]+);/', $content, $namespaceMatches)) { $namespace = $namespaceMatches[1] . '\\'; } // Extract class name from PHP file if (preg_match('/class\s+(\w+)\s+extends\s+Migration/', $content, $matches)) { return $namespace . $matches[1]; } // Fallback: convert filename to class name $filename = basename($file, '.php'); return $namespace . str_replace(' ', '', ucwords(str_replace('_', ' ', $filename))); } public static function seed(): void { $pdo = self::getInstance(); // Seed media types $mediaTypes = [ ['name' => 'games', 'display_name' => 'Games', 'icon' => 'game-controller'], ['name' => 'movies', 'display_name' => 'Movies', 'icon' => 'film'], ['name' => 'tv_shows', 'display_name' => 'TV Shows', 'icon' => 'tv'], ['name' => 'music', 'display_name' => 'Music', 'icon' => 'musical-notes'] ]; foreach ($mediaTypes as $type) { $pdo->prepare("INSERT IGNORE INTO media_types (name, display_name, icon) VALUES (?, ?, ?)") ->execute([$type['name'], $type['display_name'], $type['icon']]); } // Seed sources $sources = [ ['name' => 'steam', 'display_name' => 'Steam', 'api_url' => null, 'api_key' => null], ['name' => 'jellyfin', 'display_name' => 'Jellyfin', 'api_url' => null, 'api_key' => null], ['name' => 'stash', 'display_name' => 'Stash', 'api_url' => null, 'api_key' => null], ['name' => 'xbvr', 'display_name' => 'XBVR', 'api_url' => null, 'api_key' => null] ]; foreach ($sources as $source) { $pdo->prepare("INSERT IGNORE INTO sources (name, display_name, api_url, api_key) VALUES (?, ?, ?, ?)") ->execute([$source['name'], $source['display_name'], $source['api_url'], $source['api_key']]); } } public static function reset(): void { $pdo = self::getInstance(); // Drop all tables (in reverse order due to foreign keys) $tables = [ 'sync_logs', 'music_tracks', 'music_albums', 'music_artists', 'tv_episodes', 'tv_shows', 'movies', 'games', 'sources', 'media_types', 'migrations' ]; foreach ($tables as $table) { try { $pdo->exec("DROP TABLE IF EXISTS {$table}"); } catch (PDOException $e) { // Ignore errors if table doesn't exist } } } }