?php // player_api.php /** * XCodeStream / XtreamCodes Compatible IPTV API * * Endpoints suportados: * ?username=&password=&action=get_vod_streams * ?username=&password=&action=get_vod_categories * ?username=&password=&action=get_series (vazio) * ?username=&password=&action=get_live_streams (vazio) * /movie/{username}/{password}/{stream_id}.mp4 (redirecionamento) */ ini_set('display_errors', 0); error_reporting(E_ALL); // Credenciais de Autenticação define('VALID_USER', '0000'); define('VALID_PASS', '0000'); // URL do JSON remoto define('JSON_SOURCE', 'https://voee.eu/videos.json'); // Cache do JSON por 30 minutos (evita requisições excessivas) define('CACHE_TIME', 1800); // Identifica a URL base do script dinamicamente define( 'SERVER_URL', ( (isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] === 'on') || (isset($_SERVER['HTTP_X_FORWARDED_PROTO']) && $_SERVER['HTTP_X_FORWARDED_PROTO'] === 'https') ? 'https' : 'http' ) . '://' . ($_SERVER['HTTP_HOST'] ?? 'localhost') ); define('SERVER_PORT', $_SERVER['SERVER_PORT'] ?? 80); // ── Log de debug (remova depois de resolver) ────────────────────────────────── // $log_line = date('H:i:s') . ' URI=' . ($_SERVER['REQUEST_URI'] ?? 'NULL') // . ' PATH_INFO=' . ($_SERVER['PATH_INFO'] ?? 'NULL') . "\n"; // @file_put_contents(sys_get_temp_dir() . '/iptv_debug.log', $log_line, FILE_APPEND); header('Content-Type: application/json; charset=utf-8'); header('Access-Control-Allow-Origin: *'); // ── Roteamento por PATH_INFO (ex: /movie/user/pass/1001.mp4) ────────────────── $path = parse_url($_SERVER['REQUEST_URI'] ?? '', PHP_URL_PATH); // Remove prefixo de subpasta caso exista (ex: /iptv/movie/... → /movie/...) $script_dir = rtrim(dirname($_SERVER['SCRIPT_NAME']), '/'); if ($script_dir !== '' && strpos($path, $script_dir) === 0) { $path = substr($path, strlen($script_dir)); } // Aceita /movie/user/pass/ID ou /movie/user/pass/ID.ext (mp4, ts, mkv, avi…) if (preg_match('#^/movie/([^/]+)/([^/]+)/(\d+)(?:\.\w+)?$#', $path, $m)) { auth($m[1], $m[2]); $catalog = load_catalog(); // O JSON da voee.eu é um array simples, o ID é o índice $video_id = (int)$m[3]; if (isset($catalog[$video_id])) { $url = $catalog[$video_id]['direct_source']; // Remove Content-Type JSON para não confundir o player header_remove('Content-Type'); header('Location: ' . $url, true, 302); exit; } else { http_response_code(404); echo json_encode(['error' => 'Stream not found']); exit; } } // ── Parâmetros padrão XtreamCodes ───────────────────────────────────────────── $username = $_REQUEST['username'] ?? ''; $password = $_REQUEST['password'] ?? ''; $action = $_REQUEST['action'] ?? ''; $type = $_REQUEST['type'] ?? ''; // Sem action = info do servidor (usado por muitos players para testar) if ($action === '' && $type === '') { auth($username, $password); echo json_encode(server_info($username, $password)); exit; } auth($username, $password); $catalog = load_catalog(); switch ($action) { case 'get_server_info': echo json_encode(server_info($username, $password)); break; case 'get_vod_categories': // Para o JSON da voee.eu, todos os vídeos estão na mesma 'categoria' echo json_encode([ ['category_id' => '1', 'category_name' => 'Videos', 'parent_id' => 0] ]); break; case 'get_vod_streams': $cat_filter = $_REQUEST['category_id'] ?? null; $out = []; foreach ($catalog as $id => $item) { // O JSON da voee.eu não tem category_id, então todos pertencem à categoria '1' if ($cat_filter !== null && (string)$cat_filter !== '1') { continue; } $out[] = vod_entry($item, $id, $username, $password); } // 1. FORÇA A ORDENAÇÃO: O JSON da voee.eu não tem 'added', então usamos o ID como base usort($out, function($a, $b) { return (int)$b['stream_id'] <=> (int)$a['stream_id']; }); // 2. CORRIGE O 'num': Refaz o número da ordem (1, 2, 3...) para o Xcloud não se perder $counter = 1; foreach ($out as &$item) { $item['num'] = $counter++; } echo json_encode($out); break; case 'get_vod_info': $sid = (int)($_REQUEST['vod_id'] ?? 0); if (isset($catalog[$sid])) { $item = $catalog[$sid]; echo json_encode([ 'info' => vod_info_block($item, $sid), 'movie_data' => vod_entry($item, $sid, $username, $password), ]); exit; } http_response_code(404); echo json_encode(['error' => 'Not found']); break; case 'get_series': case 'get_series_categories': case 'get_live_streams': case 'get_live_categories': echo json_encode([]); // Não suportado pelo JSON da voee.eu break; default: http_response_code(400); echo json_encode(['error' => 'Unknown action: ' . htmlspecialchars($action)]); } // ═════════════════════════════════════════════════════════════════════════════ // Funções auxiliares // ═════════════════════════════════════════════════════════════════════════════ function auth(string $u, string $p): void { if ($u !== VALID_USER || $p !== VALID_PASS) { http_response_code(403); echo json_encode(['error' => 'Wrong username or password']); exit; } } function load_catalog(): array { $cache_file = sys_get_temp_dir() . '/iptv_videos_voee_cache.json'; if (file_exists($cache_file) && (time() - filemtime($cache_file) < CACHE_TIME)) { $data = file_get_contents($cache_file); } else { $data = @file_get_contents(JSON_SOURCE); if ($data === false && file_exists($cache_file)) { // Fallback para o cache se a API externa falhar $data = file_get_contents($cache_file); } elseif ($data !== false) { file_put_contents($cache_file, $data); } } $decoded = json_decode($data, true); return is_array($decoded) ? $decoded : []; } function server_info(string $user, string $pass): array { return [ 'user_info' => [ 'username' => $user, 'password' => $pass, 'message' => '', 'auth' => 1, 'status' => 'Active', 'exp_date' => null, 'is_trial' => '0', 'active_cons' => '0', 'created_at' => '1683734247', 'max_connections' => '1', 'allowed_output_formats' => ['m3u8', 'ts', 'rtmp'], ], 'server_info' => [ 'url' => SERVER_URL, 'port' => (string)SERVER_PORT, 'https_port' => '443', 'server_protocol' => ((isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] === 'on') || (isset($_SERVER['HTTP_X_FORWARDED_PROTO']) && $_SERVER['HTTP_X_FORWARDED_PROTO'] === 'https')) ? 'https' : 'http', 'rtmp_port' => '1935', 'timezone' => 'America/Sao_Paulo', 'timestamp_now' => time(), 'time_now' => date('Y-m-d H:i:s'), ], ]; } function vod_entry(array $item, int $id, string $user, string $pass): array { // O JSON da voee.eu não tem todos os campos, preenchemos com valores padrão ou baseados no ID return [ 'num' => $id + 1, // Baseado no índice do array 'name' => 'Filme ' . ($id + 1), 'title' => 'Filme ' . ($id + 1), 'year' => '', // Não disponível no JSON da voee.eu 'stream_type' => 'movie', 'stream_id' => $id, 'stream_icon' => $item['stream_icon'] ?? '', 'rating' => $id, // Usando ID como rating provisório 'rating_5based' => 0, 'added' => (string)time(), // Usando tempo atual como 'added' 'last_modified' => (string)time(), // Usando tempo atual como 'last_modified' 'category_id' => '1', // Categoria padrão 'container_extension' => 'mp4', // Assumindo mp4 'direct_source' => $item['direct_source'] ?? '', 'stream_url' => SERVER_URL . '/movie/' . $user . '/' . $pass . '/' . $id . '.mp4', ]; } function vod_info_block(array $item, int $id): array { // O JSON da voee.eu não tem todos os campos, preenchemos com valores padrão ou baseados no ID return [ 'name' => 'Filme ' . ($id + 1), 'title' => 'Filme ' . ($id + 1), 'year' => '', 'cover' => $item['stream_icon'] ?? '', 'plot' => 'Descrição do Filme ' . ($id + 1), // Descrição genérica 'cast' => '', 'director' => '', 'genre' => 'Filmes', 'releasedate' => '', 'last_modified' => (string)time(), 'rating' => $id, 'rating_5based' => 0, 'duration_secs' => 0, 'duration' => '00:00:00', 'video' => [], 'audio' => [], 'bitrate' => 0, ]; } ?>