is_cli = php_sapi_name() === 'cli'; $this->initStorage(); $this->showMenu(); } private function initStorage() { if (!file_exists($this->storage_file)) { file_put_contents($this->storage_file, json_encode([ 'series' => [], 'episodes' => [] ], JSON_PRETTY_PRINT)); } } private function getData() { return json_decode(file_get_contents($this->storage_file), true); } private function saveData($data) { file_put_contents($this->storage_file, json_encode($data, JSON_PRETTY_PRINT)); } private function showMenu() { if ($this->is_cli) { echo "\n=== Goyabu Scraper ===\n"; echo "1. Adicionar série\n"; echo "2. Extrair episódios e players\n"; echo "3. Mostrar resultados\n"; echo "4. Sair\n"; echo "Escolha: "; $option = trim(fgets(STDIN)); } else { echo "

Goyabu Scraper

"; if (!isset($_GET['option'])) { echo ""; return; } $option = $_GET['option']; } switch ($option) { case '1': $this->addSeries(); break; case '2': $this->extractEpisodesAndPlayers(); break; case '3': $this->showResults(); break; case '4': exit("Saindo...\n"); default: echo "Opção inválida!\n"; $this->showMenu(); } } private function addSeries() { if ($this->is_cli) { echo "\nURL da série (ex: {$this->base_url}/assistir/nome-do-anime/): "; $url = trim(fgets(STDIN)); } else { if (!isset($_POST['url'])) { echo "
"; return; } $url = $_POST['url']; } if (!filter_var($url, FILTER_VALIDATE_URL)) { $this->showMessage("URL inválida!"); return; } $data = $this->getData(); $data['series'][] = [ 'url' => $url, 'processed' => false, 'added_at' => date('Y-m-d H:i:s') ]; $this->saveData($data); $this->showMessage("Série adicionada com sucesso!"); if (!$this->is_cli) { echo "Voltar ao menu"; } else { $this->showMenu(); } } private function extractEpisodesAndPlayers() { $data = $this->getData(); if (empty($data['series'])) { $this->showMessage("Nenhuma série para processar."); return; } foreach ($data['series'] as &$series) { if ($series['processed']) continue; $this->showMessage("\nProcessando: {$series['url']}"); $html = $this->fetchUrl($series['url']); if (!$html) { $this->showMessage("Falha ao acessar a página da série."); continue; } // Encontrar a posição do texto "Episódios" $episodes_pos = stripos($html, 'Episódios'); if ($episodes_pos === false) { $this->showMessage("Não encontrou o texto 'Episódios' na página."); continue; } // Pegar o conteúdo após "Episódios" $html_after_episodes = substr($html, $episodes_pos); // Extrair os itens da lista que terminam com "Episódio XX" preg_match_all('/]*href=["\']([^"\']+)["\'][^>]*>(.*? - Episódio \d+)<\/a>/i', $html_after_episodes, $matches, PREG_SET_ORDER); if (empty($matches)) { $this->showMessage("Não encontrou episódios no formato esperado."); continue; } $episodes = []; foreach ($matches as $match) { $url = $match[1]; $title = trim($match[2]); // Extrair o número do episódio preg_match('/Episódio (\d+)$/', $title, $ep_match); $ep_number = $ep_match[1] ?? '0'; // Garantir URL absoluta if (!parse_url($url, PHP_URL_SCHEME)) { $url = $this->base_url . $url; } $episodes[] = [ 'url' => $url, 'title' => $title, 'number' => $ep_number ]; } $this->showMessage("Encontrados " . count($episodes) . " episódios."); // Processar cada episódio para extrair players foreach ($episodes as $episode) { $this->showMessage(" Processando: {$episode['title']}"); $play_links = $this->extractPlayLinks($episode['url']); if (!empty($play_links)) { $this->showMessage(" Encontrados " . count($play_links) . " links de play."); $data['episodes'][] = [ 'series_url' => $series['url'], 'episode_url' => $episode['url'], 'title' => $episode['title'], 'number' => $episode['number'], 'play_links' => $play_links, 'processed_at' => date('Y-m-d H:i:s') ]; } else { $this->showMessage(" Nenhum link de play encontrado."); } sleep(2); // Delay entre episódios } $series['processed'] = true; $series['processed_at'] = date('Y-m-d H:i:s'); $this->saveData($data); sleep(3); // Delay entre séries } $this->showMessage("\nProcessamento concluído!"); if (!$this->is_cli) { echo "Voltar ao menu"; } else { $this->showMenu(); } } private function extractPlayLinks($episode_url) { $html = $this->fetchUrl($episode_url); if (!$html) return []; // Padrões para encontrar players $patterns = [ // Iframes '/]+src=["\']([^"\']+)["\']/i', // Players em script '/player\.setup\(({.*?})\)/i', // Links diretos '/https?:\/\/[^\s"\']+\.(?:mp4|m3u8|mkv|avi)[^\s"\']*/i', // Players específicos do Goyabu '/https?:\/\/[^\s"\']+\.go(?:yabu|odrive)\.(?:to|com)[^\s"\']*/i' ]; $play_links = []; foreach ($patterns as $pattern) { preg_match_all($pattern, $html, $matches); if (!empty($matches[1])) { foreach ($matches[1] as $match) { // Se for configuração JSON if (strpos($match, '{') === 0) { $config = json_decode($match, true); if ($config && isset($config['file'])) { $play_links[] = $config['file']; } } else { // URL normal if (strpos($match, '//') === 0) { $match = 'https:' . $match; } if (filter_var($match, FILTER_VALIDATE_URL)) { $play_links[] = $match; } } } } } return array_unique($play_links); } private function showResults() { $data = $this->getData(); if (empty($data['episodes'])) { $this->showMessage("Nenhum episódio processado ainda."); return; } if ($this->is_cli) { echo "\n=== RESULTADOS ===\n"; // Agrupar por série $grouped = []; foreach ($data['episodes'] as $episode) { $grouped[$episode['series_url']][] = $episode; } foreach ($grouped as $series_url => $episodes) { echo "\nSérie: {$series_url}\n"; foreach ($episodes as $episode) { echo "\nEpisódio {$episode['number']}: {$episode['title']}\n"; echo "URL: {$episode['episode_url']}\n"; if (!empty($episode['play_links'])) { echo "Links de play:\n"; foreach ($episode['play_links'] as $link) { echo "- {$link}\n"; } } else { echo "Nenhum link de play encontrado.\n"; } } } } else { echo "

Resultados

"; // Agrupar por série $grouped = []; foreach ($data['episodes'] as $episode) { $grouped[$episode['series_url']][] = $episode; } foreach ($grouped as $series_url => $episodes) { echo "
"; echo "

Série: {$series_url}

"; foreach ($episodes as $episode) { echo "
"; echo "

Episódio {$episode['number']}: {$episode['title']}

"; echo "

URL: {$episode['episode_url']}

"; if (!empty($episode['play_links'])) { echo "

Links de play:

    "; foreach ($episode['play_links'] as $link) { echo "
  • {$link}
  • "; } echo "
"; } else { echo "

Nenhum link de play encontrado.

"; } echo "
"; } echo "
"; } } if (!$this->is_cli) { echo "Voltar ao menu"; } else { $this->showMenu(); } } private function showMessage($message) { if ($this->is_cli) { echo $message . "\n"; } else { echo "
$message
"; } } private function fetchUrl($url) { $ch = curl_init(); curl_setopt_array($ch, [ CURLOPT_URL => $url, CURLOPT_RETURNTRANSFER => true, CURLOPT_FOLLOWLOCATION => true, CURLOPT_USERAGENT => $this->user_agent, CURLOPT_SSL_VERIFYPEER => false, CURLOPT_TIMEOUT => 15, CURLOPT_CONNECTTIMEOUT => 10, CURLOPT_HTTPHEADER => [ 'Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8', 'Accept-Language: pt-BR,pt;q=0.8,en-US;q=0.5,en;q=0.3', 'Referer: ' . $this->base_url ] ]); $response = curl_exec($ch); curl_close($ch); return $response; } } new GoyabuScraper();