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 '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.games.edit': $basePath = '/admin/games/' . $data['id'] . '/edit'; 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()->addFunction(new TwigFunction('country_flag', function ($countryName) { if (!$countryName) { return ''; } // Country name to ISO 3166-1 alpha-2 code mapping (most common countries) $countryMap = [ 'United States' => 'us', 'USA' => 'us', 'America' => 'us', 'United Kingdom' => 'gb', 'UK' => 'gb', 'Britain' => 'gb', 'England' => 'gb', 'Germany' => 'de', 'France' => 'fr', 'Italy' => 'it', 'Spain' => 'es', 'Canada' => 'ca', 'Australia' => 'au', 'Japan' => 'jp', 'China' => 'cn', 'India' => 'in', 'Brazil' => 'br', 'Mexico' => 'mx', 'Russia' => 'ru', 'South Korea' => 'kr', 'Netherlands' => 'nl', 'Sweden' => 'se', 'Norway' => 'no', 'Denmark' => 'dk', 'Finland' => 'fi', 'Poland' => 'pl', 'Czech Republic' => 'cz', 'Hungary' => 'hu', 'Romania' => 'ro', 'Bulgaria' => 'bg', 'Greece' => 'gr', 'Portugal' => 'pt', 'Belgium' => 'be', 'Austria' => 'at', 'Switzerland' => 'ch', 'Ireland' => 'ie', 'New Zealand' => 'nz', 'South Africa' => 'za', 'Argentina' => 'ar', 'Chile' => 'cl', 'Colombia' => 'co', 'Peru' => 'pe', 'Venezuela' => 've', 'Ecuador' => 'ec', 'Uruguay' => 'uy', 'Paraguay' => 'py', 'Bolivia' => 'bo', 'Thailand' => 'th', 'Vietnam' => 'vn', 'Philippines' => 'ph', 'Indonesia' => 'id', 'Malaysia' => 'my', 'Singapore' => 'sg', 'Turkey' => 'tr', 'Israel' => 'il', 'Egypt' => 'eg', 'Morocco' => 'ma', 'Tunisia' => 'tn', 'Algeria' => 'dz', 'Saudi Arabia' => 'sa', 'UAE' => 'ae', 'United Arab Emirates' => 'ae', 'Qatar' => 'qa', 'Kuwait' => 'kw', 'Bahrain' => 'bh', 'Oman' => 'om', 'Jordan' => 'jo', 'Lebanon' => 'lb', 'Syria' => 'sy', 'Iraq' => 'iq', 'Iran' => 'ir', 'Pakistan' => 'pk', 'Bangladesh' => 'bd', 'Sri Lanka' => 'lk', 'Nepal' => 'np', 'Bhutan' => 'bt', 'Maldives' => 'mv', 'Afghanistan' => 'af', 'Kazakhstan' => 'kz', 'Uzbekistan' => 'uz', 'Turkmenistan' => 'tm', 'Kyrgyzstan' => 'kg', 'Tajikistan' => 'tj', 'Mongolia' => 'mn', 'North Korea' => 'kp', 'Taiwan' => 'tw', 'Hong Kong' => 'hk', 'Macau' => 'mo', 'South Korea' => 'kr', 'Cambodia' => 'kh', 'Laos' => 'la', 'Myanmar' => 'mm', 'Brunei' => 'bn', 'East Timor' => 'tl', 'Papua New Guinea' => 'pg', 'Fiji' => 'fj', 'Solomon Islands' => 'sb', 'Vanuatu' => 'vu', 'Samoa' => 'ws', 'Tonga' => 'to', 'Tuvalu' => 'tv', 'Kiribati' => 'ki', 'Marshall Islands' => 'mh', 'Micronesia' => 'fm', 'Palau' => 'pw', 'Nauru' => 'nr', 'Cuba' => 'cu', 'Jamaica' => 'jm', 'Haiti' => 'ht', 'Dominican Republic' => 'do', 'Puerto Rico' => 'pr', 'Bahamas' => 'bs', 'Trinidad and Tobago' => 'tt', 'Barbados' => 'bb', 'Saint Lucia' => 'lc', 'Saint Vincent and the Grenadines' => 'vc', 'Grenada' => 'gd', 'Antigua and Barbuda' => 'ag', 'Saint Kitts and Nevis' => 'kn', 'Dominica' => 'dm', 'Saint Martin' => 'mf', 'Guadeloupe' => 'gp', 'Martinique' => 'mq', 'French Guiana' => 'gf', 'Suriname' => 'sr', 'Guyana' => 'gy', 'Belize' => 'bz', 'Costa Rica' => 'cr', 'Panama' => 'pa', 'Nicaragua' => 'ni', 'Honduras' => 'hn', 'El Salvador' => 'sv', 'Guatemala' => 'gt', 'Greenland' => 'gl', 'Iceland' => 'is', 'Faroe Islands' => 'fo', 'Ă…land Islands' => 'ax', 'Guernsey' => 'gg', 'Jersey' => 'je', 'Isle of Man' => 'im', 'Gibraltar' => 'gi', 'Malta' => 'mt', 'Cyprus' => 'cy', 'Luxembourg' => 'lu', 'Monaco' => 'mc', 'Andorra' => 'ad', 'San Marino' => 'sm', 'Vatican City' => 'va', 'Liechtenstein' => 'li', 'Slovenia' => 'si', 'Croatia' => 'hr', 'Bosnia and Herzegovina' => 'ba', 'Serbia' => 'rs', 'Montenegro' => 'me', 'Kosovo' => 'xk', 'North Macedonia' => 'mk', 'Albania' => 'al', 'Moldova' => 'md', 'Ukraine' => 'ua', 'Belarus' => 'by', 'Lithuania' => 'lt', 'Latvia' => 'lv', 'Estonia' => 'ee', 'Slovakia' => 'sk', 'Armenia' => 'am', 'Azerbaijan' => 'az', 'Georgia' => 'ge', ]; $countryCode = strtolower($countryMap[$countryName] ?? ''); if ($countryCode) { // Return Iconify flag icon HTML return ''; } return ''; })); $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 Method Override Middleware for handling _METHOD field in forms $app->add(new \Slim\Middleware\MethodOverrideMiddleware()); // 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'; require __DIR__ . '/../routes/api2.php'; $app->run();