load(); // Load database configuration $dbConfig = require __DIR__ . '/../config/database.php'; \App\Database\Database::setConfig($dbConfig); // Initialize database try { $pdo = \App\Database\Database::getInstance(); } catch (Exception $e) { die('Database connection failed: ' . $e->getMessage()); } use Slim\Factory\AppFactory; use Slim\Views\Twig; use Slim\Views\TwigMiddleware; use DI\Container; use Twig\TwigFunction; use Twig\TwigFilter; use \Twig\Extension\DebugExtension; // Create DI Container $container = new Container(); // Register PDO instance $container->set(PDO::class, function () use ($pdo) { return $pdo; }); // Register Twig view $container->set('view', function () use ($container) { $twig = Twig::create(__DIR__ . '/../resources/views', [ 'cache' => $_ENV['APP_ENV'] === 'production' ? __DIR__ . '/../storage/views' : false, 'debug' => $_ENV['APP_DEBUG'] === 'true', ]); $twig->addExtension(new \Twig\Extension\DebugExtension()); // Add custom functions $twig->getEnvironment()->addFunction(new TwigFunction('base_url', function () { return sprintf( "%s://%s", isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] != 'off' ? 'https' : 'http', $_SERVER['HTTP_HOST'] ?? 'localhost' ); })); // Add media visibility function $twig->getEnvironment()->addFunction(new TwigFunction('is_media_type_visible', function ($mediaType) { return is_media_type_visible($mediaType); })); // Placeholder path_for function - will be updated after routes are registered $twig->getEnvironment()->addFunction(new TwigFunction('path_for', function ($name, $data = [], $queryParams = []) { // Simple implementation for now - will be replaced with proper router-based version $basePath = ''; // Handle common route patterns switch ($name) { case 'home': $basePath = '/'; break; case 'dashboard.index': $basePath = '/'; break; case 'games.index': $basePath = '/media/games'; break; case 'games.show': $basePath = '/media/games/' . ($data['game_key'] ?? ''); break; case 'movies.index': $basePath = '/media/movies'; break; case 'tvshows.index': $basePath = '/media/tv-shows'; break; case 'music.index': $basePath = '/media/music'; break; case 'admin.index': $basePath = '/admin'; break; case 'admin.playnite.import': $basePath = '/admin/playnite/import'; break; case 'admin.playnite.upload': $basePath = '/admin/playnite/import'; break; case 'admin.sync': $basePath = '/admin/sync/' . ($data['id'] ?? ''); break; case 'auth.login': $basePath = '/login'; break; case 'auth.logout': $basePath = '/logout'; break; case 'movies.show': $basePath = '/media/movies/' . ($data['id'] ?? ''); break; case 'tvshows.show': $basePath = '/media/tv-shows/' . ($data['id'] ?? ''); break; case 'music.show': $basePath = '/media/music/' . ($data['id'] ?? ''); break; case 'adult.index': $basePath = '/media/adult'; break; case 'adult.show': $basePath = '/media/adult/' . ($data['id'] ?? ''); break; case 'actors.index': $basePath = '/media/actors'; break; case 'actors.show': $basePath = '/media/actors/' . ($data['id'] ?? ''); break; case 'actors.edit': $basePath = '/media/actors/' . ($data['id'] ?? '') .'/edit'; break; case 'search.index': $basePath = '/search'; break; default: $basePath = '/' . str_replace('.', '/', $name); } // Add query parameters if (!empty($queryParams)) { $basePath .= '?' . http_build_query($queryParams); } return $basePath; })); $twig->getEnvironment()->addFunction(new TwigFunction('is_admin', function () use ($container) { $authService = $container->get(\App\Services\AuthService::class); return $authService->isAdmin(); })); $twig->getEnvironment()->addFunction(new TwigFunction('current_user', function () use ($container) { $authService = $container->get(\App\Services\AuthService::class); $user = $authService->getCurrentUser(); return $user ?: (object)['username' => 'Guest']; })); $twig->getEnvironment()->addFunction(new TwigFunction('csrf_token', function () use ($container) { $authService = $container->get(\App\Services\AuthService::class); return $authService->generateCSRFToken(); })); $twig->getEnvironment()->addFilter(new TwigFilter('format_duration', function ($minutes) { if (!$minutes || $minutes == 0) { return '0m'; } $hours = floor($minutes / 60); $remainingMinutes = $minutes % 60; if ($hours > 0) { return $hours . 'h ' . $remainingMinutes . 'm'; } return $remainingMinutes . 'm'; })); $twig->getEnvironment()->addFilter(new TwigFilter('json_decode', function ($jsonString) { if (!$jsonString) { return null; } $decoded = json_decode($jsonString, true); return $decoded === null ? null : $decoded; })); $twig->getEnvironment()->addFilter(new TwigFilter('filesizeformat', function ($bytes) { if (!$bytes || !is_numeric($bytes)) { return '0 B'; } $units = ['B', 'KB', 'MB', 'GB', 'TB']; $bytes = max($bytes, 0); $pow = floor(($bytes ? log($bytes) : 0) / log(1024)); $pow = min($pow, count($units) - 1); $bytes /= pow(1024, $pow); return round($bytes, 2) . ' ' . $units[$pow]; })); return $twig; }); // Register AuthService $container->set(\App\Services\AuthService::class, function ($c) { return new \App\Services\AuthService($c->get(PDO::class)); }); // Register models $container->set(\App\Models\User::class, function ($c) { return new \App\Models\User($c->get(PDO::class)); }); $container->set(\App\Models\SyncLog::class, function ($c) { return new \App\Models\SyncLog($c->get(PDO::class)); }); // Register controllers $container->set(\App\Controllers\AuthController::class, function ($c) { return new \App\Controllers\AuthController($c->get(\App\Services\AuthService::class), $c->get('view')); }); $container->set(\App\Controllers\AdminController::class, function ($c) { return new \App\Controllers\AdminController($c->get(PDO::class), $c->get('view')); }); $container->set(\App\Controllers\GameController::class, function ($c) { return new \App\Controllers\GameController($c->get(PDO::class), $c->get('view')); }); $container->set(\App\Controllers\DashboardController::class, function ($c) { return new \App\Controllers\DashboardController($c->get(PDO::class), $c->get('view')); }); $container->set(\App\Controllers\MovieController::class, function ($c) { return new \App\Controllers\MovieController($c->get(PDO::class), $c->get('view')); }); $container->set(\App\Controllers\TvShowController::class, function ($c) { return new \App\Controllers\TvShowController($c->get(PDO::class), $c->get('view')); }); $container->set(\App\Controllers\MusicController::class, function ($c) { return new \App\Controllers\MusicController($c->get(PDO::class), $c->get('view')); }); $container->set(\App\Controllers\AdultController::class, function ($c) { return new \App\Controllers\AdultController($c->get(PDO::class), $c->get('view')); }); $container->set(\App\Controllers\ActorController::class, function ($c) { return new \App\Controllers\ActorController($c->get(PDO::class), $c->get('view')); }); $container->set(\App\Controllers\SearchController::class, function ($c) { return new \App\Controllers\SearchController($c->get(PDO::class), $c->get('view')); }); $container->set(\App\Controllers\ImageController::class, function ($c) { return new \App\Controllers\ImageController($c->get('view')); }); $container->set(\App\Controllers\SettingsController::class, function ($c) { return new \App\Controllers\SettingsController($c->get(PDO::class), $c->get('view')); }); // Register API controllers $container->set(\App\Controllers\Api\PlayniteController::class, function ($c) { return new \App\Controllers\Api\PlayniteController($c->get(PDO::class)); }); $container->set(\App\Controllers\Api\AuthController::class, function ($c) { return new \App\Controllers\Api\AuthController($c->get(\App\Services\AuthService::class)); }); // Register PlayniteImportService $container->set(\App\Services\PlayniteImportService::class, function ($c) { return new \App\Services\PlayniteImportService($c->get(PDO::class)); }); // Register middleware $container->set(\App\Http\Middleware\AuthMiddleware::class, function ($c) { return new \App\Http\Middleware\AuthMiddleware($c->get(\App\Services\AuthService::class)); }); $container->set(\App\Controllers\MediaSourceController::class, function ($c) { return new \App\Controllers\MediaSourceController($c->get(PDO::class), $c->get('view')); }); $container->set(\App\Http\Middleware\MediaVisibilityMiddleware::class, function ($c) { return new \App\Http\Middleware\MediaVisibilityMiddleware(); }); // Create App with DI Container AppFactory::setContainer($container); $app = AppFactory::create(); // Add Twig-View Middleware $twig = $container->get('view'); $app->add(TwigMiddleware::create($app, $twig)); // PHP DebugBar Setup (only in development) if ($_ENV['APP_DEBUG'] === 'true') { $debugbar = new \DebugBar\StandardDebugBar(); // Set up the debug bar renderer with the correct base URL $baseUrl = rtrim($app->getBasePath(), '/'); $debugbarRenderer = $debugbar->getJavascriptRenderer($baseUrl . '/phpdebugbar'); // Add DebugBar to Twig globals $twig->getEnvironment()->addGlobal('debugbarRenderer', $debugbarRenderer); // Add route to serve DebugBar assets $app->get('/phpdebugbar/{path:.*}', function ($request, $response, $args) use ($debugbar) { $debugbarRenderer = $debugbar->getJavascriptRenderer(); $path = $args['path']; // Serve CSS files if (preg_match('/\.css$/', $path)) { $content = file_get_contents($debugbarRenderer->getBasePath() . '/' . $path); $response->getBody()->write($content); return $response->withHeader('Content-Type', 'text/css'); } // Serve JS files if (preg_match('/\.js$/', $path)) { $content = file_get_contents($debugbarRenderer->getBasePath() . '/' . $path); $response->getBody()->write($content); return $response->withHeader('Content-Type', 'application/javascript'); } // Serve other assets (fonts, etc.) $content = @file_get_contents($debugbarRenderer->getBasePath() . '/' . $path); if ($content !== false) { $response->getBody()->write($content); return $response; } return $response->withStatus(404); }); // Add middleware to collect data $app->add(function ($request, $handler) use ($debugbar) { // Start timing the request $debugbar['time']->startMeasure('app', 'Application'); try { $response = $handler->handle($request); // Stop timing if it was started if ($debugbar['time']->hasStartedMeasure('app')) { $debugbar['time']->stopMeasure('app'); } return $response; } catch (\Exception $e) { // Make sure to stop timing even if an exception occurs if ($debugbar['time']->hasStartedMeasure('app')) { $debugbar['time']->stopMeasure('app'); } throw $e; } }); } // Add Error Middleware $errorMiddleware = $app->addErrorMiddleware( $_ENV['APP_DEBUG'] === 'true', true, true ); // Register routes require __DIR__ . '/../routes/web.php'; require __DIR__ . '/../routes/api.php'; $app->run();