normalizeText($item['lista_name'] ?? ''), 'name_chanel' => normalizeText($item['name_chanel'] ?? ''), 'link_chanel' => normalizeText($item['link_chanel'] ?? ''), 'id_chanel' => normalizeText($item['id_chanel'] ?? ''), 'logo_chanel' => normalizeText($item['logo_chanel'] ?? ''), ]; if ($canal['name_chanel'] === '' && $canal['link_chanel'] === '') continue; $canal['channel_key'] = makeChannelKey($canal); $canal['search_name'] = normalizeSearchText($canal['name_chanel']); $canal['search_id'] = normalizeSearchText($canal['id_chanel']); $canais[$canal['channel_key']] = $canal; } return [$canais, null]; } function buscarCanais($canaisMap, $query, $limit = 100, $listaFilter = 'all'): array { $queryNorm = normalizeSearchText($query); if ($queryNorm === '') return []; $listaFilter = mb_strtolower(normalizeText($listaFilter), 'UTF-8'); $prefix = []; $contains = []; foreach ($canaisMap as $canal) { $canalLista = mb_strtolower(normalizeText($canal['lista_name'] ?? ''), 'UTF-8'); if ($listaFilter !== 'all' && $canalLista !== $listaFilter) continue; $name = $canal['search_name'] ?? ''; $id = $canal['search_id'] ?? ''; if (($name !== '' && startsWithNormalized($name, $queryNorm)) || ($id !== '' && startsWithNormalized($id, $queryNorm))) { $prefix[] = $canal; } else if (($name !== '' && strpos($name, $queryNorm) !== false) || ($id !== '' && strpos($id, $queryNorm) !== false)) { $contains[] = $canal; } } $resultados = array_merge($prefix, $contains); if ($limit > 0) $resultados = array_slice($resultados, 0, $limit); return array_map(function ($canal) { return [ 'channel_key' => $canal['channel_key'], 'lista_name' => $canal['lista_name'], 'name_chanel' => $canal['name_chanel'], 'id_chanel' => $canal['id_chanel'], ]; }, $resultados); } function chamarVisionOCR($imagePath, $apiKey) { $imageData = base64_encode(file_get_contents($imagePath)); $payload = ['requests' => [['image' => ['content' => $imageData],'features' => [['type' => 'TEXT_DETECTION','maxResults' => 1]]]]]; $ch = curl_init('https://vision.googleapis.com/v1/images:annotate?key=' . urlencode($apiKey)); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); curl_setopt($ch, CURLOPT_POST, true); curl_setopt($ch, CURLOPT_HTTPHEADER, ['Content-Type: application/json']); curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($payload)); $response = curl_exec($ch); $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE); curl_close($ch); if ($httpCode !== 200) throw new Exception('Erro OCR: ' . $response); return json_decode($response, true); } function topLeftFromBoundingPoly(array $poly): array { $vertices = $poly['vertices'] ?? []; $xs = []; $ys = []; foreach ($vertices as $v) { $xs[] = isset($v['x']) ? (int)$v['x'] : 0; $ys[] = isset($v['y']) ? (int)$v['y'] : 0; } return ['x' => $xs ? min($xs) : 0, 'y' => $ys ? min($ys) : 0, 'x2' => $xs ? max($xs) : 0, 'y2' => $ys ? max($ys) : 0]; } function extrairData(array $annotations): ?string { $tokens = []; foreach ($annotations as $i => $ann) { if ($i === 0 || empty($ann['description']) || empty($ann['boundingPoly'])) continue; $desc = normalizeText($ann['description']); $pos = topLeftFromBoundingPoly($ann['boundingPoly']); if ($pos['y'] >= 180 && $pos['y'] <= 270) { $tokens[] = ['text' => $desc, 'x' => $pos['x'], 'y' => $pos['y']]; } } usort($tokens, fn($a, $b) => $a['x'] <=> $b['x']); $joined = normalizeText(implode(' ', array_column($tokens, 'text'))); if (preg_match('/\b\d{1,2}\s+DE\s+[A-ZÇÃÕÁÉÍÓÚ]+\b/iu', $joined, $m)) return mb_strtoupper($m[0], 'UTF-8'); return null; } function agruparPorLinhas(array $annotations, int $tolerance = 18): array { $items = []; foreach ($annotations as $i => $ann) { if ($i === 0 || empty($ann['description']) || empty($ann['boundingPoly'])) continue; $desc = normalizeText($ann['description']); if ($desc === '') continue; $pos = topLeftFromBoundingPoly($ann['boundingPoly']); $items[] = ['text' => $desc, 'x' => $pos['x'], 'y' => $pos['y'], 'x2' => $pos['x2'], 'y2' => $pos['y2']]; } usort($items, function ($a, $b) { if (abs($a['y'] - $b['y']) <= 5) { return $a['x'] <=> $b['x']; } return $a['y'] <=> $b['y']; }); $rows = []; foreach ($items as $item) { $placed = false; foreach ($rows as &$row) { if (abs($row['avg_y'] - $item['y']) <= $tolerance) { $row['items'][] = $item; $row['avg_y'] = (int) round(array_sum(array_column($row['items'], 'y')) / count($row['items'])); $placed = true; break; } } if (!$placed) $rows[] = ['avg_y' => $item['y'], 'items' => [$item]]; } foreach ($rows as &$row) usort($row['items'], fn($a, $b) => $a['x'] <=> $b['x']); usort($rows, fn($a, $b) => $a['avg_y'] <=> $b['avg_y']); return $rows; } function rowText(array $row): string { return normalizeText(implode(' ', array_map(fn($i) => $i['text'], $row['items']))); } function extrairTimesDaLinha(array $items, ?string $horaDetectada = null): ?array { $tokens = []; foreach ($items as $it) { $txt = normalizeText($it['text'] ?? ''); if ($txt === '') continue; $up = mb_strtoupper($txt, 'UTF-8'); if ($horaDetectada && $up === $horaDetectada) continue; if (preg_match('/^\d{2}:\d{2}$/', $up)) continue; if ($it['x'] < 110 || $it['x2'] > 700) continue; if (strlen($up) <= 1 || in_array($up, ['FEN', 'TESI', 'BFC', 'U.S', 'S.A'], true)) continue; $tokens[] = ['text' => $up, 'x' => $it['x']]; } if (empty($tokens)) return null; usort($tokens, fn($a, $b) => $a['x'] <=> $b['x']); $vsIndex = -1; foreach ($tokens as $i => $tk) { if ($tk['text'] === 'VS') { $vsIndex = $i; break; } } if ($vsIndex === -1) return null; $timeA = normalizeText(implode(' ', array_column(array_slice($tokens, 0, $vsIndex), 'text'))); $timeB = normalizeText(implode(' ', array_column(array_slice($tokens, $vsIndex + 1), 'text'))); return ['time_a' => $timeA, 'time_b' => $timeB]; } function extrairJogosDoOCR(array $ocr): array { $annotations = $ocr['responses'][0]['textAnnotations'] ?? []; $saida = ['data' => extrairData($annotations), 'jogos' => []]; if (!$annotations) return $saida; $rows = array_values(array_filter(agruparPorLinhas($annotations, 22), fn($r) => $r['avg_y'] >= 150 && $r['avg_y'] <= 3000)); $pendingHora = null; foreach ($rows as $row) { preg_match('/\b\d{2}:\d{2}\b/', mb_strtoupper(rowText($row), 'UTF-8'), $hm); $horaNaLinha = $hm[0] ?? null; $times = extrairTimesDaLinha($row['items'], $horaNaLinha); if ($horaNaLinha) $pendingHora = $horaNaLinha; if ($times && $pendingHora) { $key = $pendingHora . '|' . $times['time_a'] . '|' . $times['time_b']; $saida['jogos'][$key] = ['data' => $saida['data'], 'hora' => $pendingHora, 'time_a' => $times['time_a'], 'time_b' => $times['time_b']]; $pendingHora = null; } } return $saida; } function gerarGameKey($jogo): string { return sha1(implode('||', [mb_strtolower($jogo['data']??''), mb_strtolower($jogo['hora']??''), mb_strtolower($jogo['time_a']??''), mb_strtolower($jogo['time_b']??'')])); } function montarNomeJogoBase($jogo): string { return trim(($jogo['hora'] ?? '00:00') . ' ' . ($jogo['time_a'] ?? '') . ' x ' . ($jogo['time_b'] ?? '')); } function montarNomeJogo($jogo): string { $gk = $jogo['game_key'] ?? gerarGameKey($jogo); if (isset($_SESSION['nomes_editados'][$gk])) return $_SESSION['nomes_editados'][$gk]; $base = montarNomeJogoBase($jogo); $suffix = !empty($jogo['duplicate_suffix']) ? $jogo['duplicate_suffix'] : '1'; return trim($base . ' ' . $suffix); } function obterJogosBaseDaSessao(): array { $todos = []; foreach ($_SESSION['jogos_manuais'] as $jm) { $todos[] = $jm; } foreach ($_SESSION['banners_lidos'] as $banner) { foreach (($banner['jogos'] ?? []) as $jogo) { $jogo['is_duplicate'] = false; $jogo['duplicate_suffix'] = '1'; $jogo['game_key'] = gerarGameKey($jogo); $jogo['nome_jogo'] = montarNomeJogo($jogo); $todos[] = $jogo; } } $unicos = []; foreach ($todos as $jogo) { $unicos[$jogo['game_key']] = $jogo; } return array_values($unicos); } function obterTodosJogosDaSessao(): array { $base = obterJogosBaseDaSessao(); if (isset($_SESSION['reordenar_por_horario'])) { usort($base, function($a, $b) { $nomeA = montarNomeJogo($a); $nomeB = montarNomeJogo($b); preg_match('/\b\d{2}:\d{2}\b/', $nomeA, $mA); preg_match('/\b\d{2}:\d{2}\b/', $nomeB, $mB); $hA = $mA[0] ?? '00:00'; $hB = $mB[0] ?? '00:00'; if ($hA !== $hB) return strcmp($hA, $hB); return strcmp($nomeA, $nomeB); }); } $listaFinal = []; foreach ($base as $j) { $listaFinal[] = $j; $srcKey = $j['game_key']; if (isset($_SESSION['duplicated_games'][$srcKey])) { $dup = $j; $dup['is_duplicate'] = true; $dup['duplicate_suffix'] = '2'; $dup['original_game_key'] = $srcKey; $dup['game_key'] = sha1($srcKey . '|dup|2'); if (isset($_SESSION['nomes_editados'][$srcKey])) { $nomeOriginalEditado = $_SESSION['nomes_editados'][$srcKey]; $dup['nome_jogo'] = preg_replace('/\s1$/', ' 2', $nomeOriginalEditado); if (!isset($_SESSION['nomes_editados'][$dup['game_key']])) { $_SESSION['nomes_editados'][$dup['game_key']] = $dup['nome_jogo']; } } else { $dup['nome_jogo'] = montarNomeJogo($dup); } $listaFinal[] = $dup; } } return $listaFinal; } function obterFiltroAtualJogo($jogo): string { $val = $_SESSION['filtros_lista'][$jogo['game_key']] ?? (!empty($jogo['is_duplicate']) ? 'good' : 'samir'); return in_array($val, ['all', 'samir', 'nizer', 'good', 'cris']) ? $val : 'samir'; } function sincronizarSessoesComJogos(): void { $base = obterJogosBaseDaSessao(); $baseKeys = []; foreach ($base as $j) { $baseKeys[$j['game_key']] = true; } $_SESSION['duplicated_games'] = array_filter($_SESSION['duplicated_games'], fn($k) => isset($baseKeys[$k]), ARRAY_FILTER_USE_KEY); } [$canaisMap, $erroCanais] = carregarCanaisJson(__DIR__ . DIRECTORY_SEPARATOR . 'output_channels.json'); if (isset($_GET['ajax']) && $_GET['ajax'] === 'buscar_canais') { header('Content-Type: application/json'); echo json_encode(['ok'=>true, 'results'=>buscarCanais($canaisMap, $_GET['q']??'', 100, $_GET['lista_filter']??'all')]); exit; } if ($_SERVER['REQUEST_METHOD'] === 'POST') { $acao = $_POST['acao'] ?? ''; if ($acao === 'limpar_tudo') { session_destroy(); header('Location: '.$_SERVER['PHP_SELF']); exit; } if ($acao === 'adicionar_manual') { $id = uniqid(); $novo = ['data'=>date('d DE MARCO'), 'hora'=>'00:00', 'time_a'=>'NOVO', 'time_b'=>'JOGO', 'is_duplicate'=>false, 'duplicate_suffix'=>'1', 'game_key' => 'man_' . $id]; $novo['nome_jogo'] = montarNomeJogo($novo); $_SESSION['filtros_lista'][$novo['game_key']] = 'samir'; array_unshift($_SESSION['jogos_manuais'], $novo); header('Location: '.$_SERVER['PHP_SELF']); exit; } if ($acao === 'reordenar') { $_SESSION['reordenar_por_horario'] = true; header('Location: '.$_SERVER['PHP_SELF']); exit; } if ($acao === 'salvar_nome') { $gk = $_POST['game_key']??''; $novoNome = $_POST['novo_nome']??''; if ($gk) { $_SESSION['nomes_editados'][$gk] = $novoNome; foreach($_SESSION['jogos_manuais'] as &$jm) { if ($jm['game_key'] === $gk && preg_match('/\b\d{2}:\d{2}\b/', $novoNome, $m)) { $jm['hora'] = $m[0]; } } } header('Location: '.$_SERVER['PHP_SELF']); exit; } if ($acao === 'adicionar_banner') { if (isset($_FILES['banner']) && $_FILES['banner']['error'] === 0) { try { $ocr = chamarVisionOCR($_FILES['banner']['tmp_name'], $apiKey); $res = extrairJogosDoOCR($ocr); $_SESSION['banners_lidos'][] = ['id'=>uniqid(), 'nome'=>$_FILES['banner']['name'], 'data'=>$res['data'], 'jogos'=>$res['jogos']]; sincronizarSessoesComJogos(); } catch (Exception $e) { $erro = $e->getMessage(); } } header('Location: '.$_SERVER['PHP_SELF']); exit; } if ($acao === 'duplicar_lista') { $base = obterJogosBaseDaSessao(); foreach ($base as $j) { if (!isset($_SESSION['duplicated_games'][$j['game_key']])) { $_SESSION['duplicated_games'][$j['game_key']] = ['suffix'=>'2']; } } header('Location: '.$_SERVER['PHP_SELF']); exit; } if ($acao === 'definir_filtro_lista') { $_SESSION['filtros_lista'][$_POST['game_key']] = $_POST['filter']; header('Location: '.$_SERVER['PHP_SELF']); exit; } if ($acao === 'vincular_canal') { $_SESSION['relacoes_canais'][$_POST['game_key']] = $_POST['channel_key']; header('Location: '.$_SERVER['PHP_SELF']); exit; } if ($acao === 'gerar_m3u') { $jogos = obterTodosJogosDaSessao(); $linhas = ['#EXTM3U']; foreach ($jogos as $j) { $ck = $_SESSION['relacoes_canais'][$j['game_key']] ?? null; if ($ck && isset($canaisMap[$ck])) { $c = $canaisMap[$ck]; $n = montarNomeJogo($j); $linhas[] = '#EXTINF:-1 tvg-id="'.e($c['id_chanel']).'" tvg-name="'.e($n).'" tvg-logo="'.e($c['logo_chanel']).'" group-title="'.e($n).'",'.$n; $linhas[] = $c['link_chanel']; } } @file_put_contents('output_jogos.m3u', implode("\r\n", $linhas)); $sucesso = 'M3U gerada com sucesso.'; $arquivoM3uGerado = 'output_jogos.m3u'; } } $todosJogos = obterTodosJogosDaSessao(); ?> Dashboard | Leitor de Banner

Leitor de Banner de Jogos

Lista de Jogos ()

CÓPIA - FILTRO GOOD
Linked: [".$canaisMap[$ck]['lista_name']."] ".$canaisMap[$ck]['name_chanel'] : "Nenhum canal vinculado"; ?>