Advertisement
Paulo87

ANIMEFIRE DOWNLOADER IN PYTHON

Dec 19th, 2024
28
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 16.03 KB | None | 0 0
  1. import requests
  2. import os
  3. from bs4 import BeautifulSoup
  4. from rich.console import Console
  5. from rich.panel import Panel
  6. from rich.progress import Progress
  7. import cloudscraper
  8. import re
  9. from urllib.parse import urlparse, parse_qs, unquote
  10. import time
  11. import urllib3
  12. import requests.packages.urllib3.util.ssl_
  13. import sys
  14. import socket
  15.  
  16. # Configurações iniciais
  17. socket.setdefaulttimeout(30)
  18. if hasattr(sys, 'getandroidapilevel'):
  19.     socket._MAXLINE = 65536 * 8
  20.  
  21. requests.packages.urllib3.util.ssl_.DEFAULT_CIPHERS = 'ALL:@SECLEVEL=1'
  22.  
  23. # Diretório base para downloads
  24. BASE_DOWNLOAD_DIR = "/storage/emulated/0/downloads/"
  25.  
  26. os.system('clear' if os.name == 'posix' else 'cls')
  27.  
  28. class AnimeFinder:
  29.     def __init__(self):
  30.         self.console = Console()
  31.         self.base_url = "https://animefire.plus/pesquisar/"
  32.         self.headers = {
  33.             'User-Agent': "Mozilla/5.0 (Linux; Android 14; 22071219CG Build/UP1A.231005.007; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/132.0.6834.56 Mobile Safari/537.36"
  34.         }
  35.         self.scraper = cloudscraper.create_scraper()
  36.  
  37.     def _format_anime_name(self, anime_name):
  38.         return anime_name.strip().replace(" ", "-").lower()
  39.  
  40.     def search_anime(self, anime_name):
  41.         try:
  42.             formatted_name = self._format_anime_name(anime_name)
  43.             url = f"{self.base_url}{formatted_name}"
  44.             response = self.scraper.get(url, headers=self.headers, timeout=30)
  45.             response.raise_for_status()
  46.             soup = BeautifulSoup(response.content, 'html.parser')
  47.             anime_cards = soup.find_all('div', class_='divCardUltimosEps')
  48.             if not anime_cards:
  49.                 self.console.print("[bold red]Nenhum resultado encontrado.[/bold red]")
  50.                 return []
  51.             return self._display_results(anime_cards)
  52.         except Exception as e:
  53.             self.console.print(f"[bold red]Erro: {str(e)}[/bold red]")
  54.             return []
  55.  
  56.     def _display_results(self, anime_cards):
  57.         results = []
  58.         for i, card in enumerate(anime_cards, 1):
  59.             title = self._extract_text(card, 'h3', 'animeTitle')
  60.             full_link = self._extract_full_link(card, 'a')
  61.             results.append({
  62.                 'title': title,
  63.                 'link': full_link
  64.             })
  65.         self.console.print("\nEscolha o número do anime:", style="bold blue")
  66.         for i, result in enumerate(results, 1):
  67.             self.console.print(f"[cyan]{i}. {result['title']}[/cyan]")
  68.         return results
  69.  
  70.     def _extract_text(self, soup, tag, class_name):
  71.         element = soup.find(tag, class_=class_name)
  72.         return element.text.strip() if element else None
  73.  
  74.     def _extract_full_link(self, soup, tag):
  75.         element = soup.find(tag)
  76.         return element.get('href') if element else None
  77.  
  78. def sanitize_filename(filename):
  79.     # Remove caracteres inválidos
  80.     invalid_chars = r'[<>:"/\\|?*]'
  81.     return re.sub(invalid_chars, '', filename)
  82.  
  83. def create_anime_directory(anime_name):
  84.     # Cria diretório do anime
  85.     safe_name = sanitize_filename(anime_name)
  86.     anime_dir = os.path.join(BASE_DOWNLOAD_DIR, safe_name)
  87.     os.makedirs(anime_dir, exist_ok=True)
  88.     return anime_dir
  89.  
  90. def fetch_and_display_anime_info(url):
  91.     scraper = cloudscraper.create_scraper()
  92.     response = scraper.get(url, timeout=30)
  93.     response.raise_for_status()
  94.     soup = BeautifulSoup(response.text, 'html.parser')
  95.     anime_info = {
  96.         "titulos": {
  97.             "principal": soup.find('h1', class_='quicksand400').text.strip() if soup.find('h1', class_='quicksand400') else 'N/A',
  98.             "alternativo": soup.find_all('h6', class_='text-gray')[0].text.strip() if soup.find_all('h6', class_='text-gray') else 'N/A',
  99.             "japones": soup.find_all('h6', class_='text-gray')[-1].text.strip() if len(soup.find_all('h6', class_='text-gray')) > 1 else 'N/A'
  100.         },
  101.         "avaliacao": {
  102.             "score": soup.find('h4', id='anime_score').text.strip() if soup.find('h4', id='anime_score') else 'N/A',
  103.             "total_votos": soup.find('h6', id='anime_votos').text.strip() if soup.find('h6', id='anime_votos') else 'N/A'
  104.         },
  105.         "sinopse": extract_sinopse(soup),
  106.         "midia": {
  107.             "imagem_capa": extract_img_capa(soup)
  108.         },
  109.         "generos": [
  110.             genero.text.strip()
  111.             for genero in soup.find_all('a', class_='spanGenerosLink')
  112.         ],
  113.         "episodios": len(soup.find_all('a', class_='lEp'))
  114.     }
  115.     return anime_info
  116.  
  117. def extract_sinopse(soup):
  118.     sinopse_tag = soup.find('div', class_='divSinopse mb-3 mt-3 ml-2 ml-sm-1 ml-md-2 mr-2')
  119.     sinopse = sinopse_tag.find('span', class_='spanAnimeInfo').text.strip() if sinopse_tag else 'N/A'
  120.     return sinopse
  121.  
  122. def extract_img_capa(soup):
  123.     img_tag = soup.find('img', class_='transitioning_src')
  124.     if img_tag:
  125.         return img_tag.get('data-src', img_tag.get('src'))
  126.     return 'N/A'
  127.  
  128. def save_anime_info(anime_info, download_dir, filename):
  129.     # Salva informações do anime na pasta de download
  130.     try:
  131.         file_path = os.path.join(download_dir, f"{filename}.txt")
  132.        
  133.         with open(file_path, 'w', encoding='utf-8') as f:
  134.             f.write("=== Informações do Anime ===\n\n")
  135.             f.write(f"Título Principal: {anime_info['titulos']['principal']}\n")
  136.             f.write(f"Título Alternativo: {anime_info['titulos']['alternativo']}\n")
  137.             f.write(f"Título Japonês: {anime_info['titulos']['japones']}\n\n")
  138.             f.write(f"Avaliação: {anime_info['avaliacao']['score']}\n")
  139.             f.write(f"Total de Votos: {anime_info['avaliacao']['total_votos']}\n\n")
  140.             f.write("Sinopse:\n")
  141.             f.write(f"{anime_info['sinopse']}\n\n")
  142.             f.write("Gêneros:\n")
  143.             f.write(", ".join(anime_info['generos']) + "\n\n")
  144.             f.write(f"Total de Episódios: {anime_info['episodios']}\n")
  145.        
  146.         return True
  147.     except Exception as e:
  148.         Console().print(f"[bold red]Erro ao salvar informações: {str(e)}[/bold red]")
  149.         return False
  150.  
  151. class AnimeDownloader:
  152.     def __init__(self):
  153.         self.scraper = cloudscraper.create_scraper(
  154.             browser={
  155.                 'browser': 'chrome',
  156.                 'platform': 'android',
  157.                 'desktop': False
  158.             }
  159.         )
  160.         self.console = Console()
  161.         self.headers = {
  162.             'User-Agent': "Mozilla/5.0 (Linux; Android 14; 22071219CG Build/UP1A.231005.007; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/132.0.6834.56 Mobile Safari/537.36"
  163.         }
  164.  
  165.     def get_episode_links(self, anime_url):
  166.         try:
  167.             response = self.scraper.get(anime_url, headers=self.headers)
  168.             response.raise_for_status()
  169.             soup = BeautifulSoup(response.content, 'html.parser')
  170.             episodes = soup.find_all('a', class_='lEp')
  171.             episode_links = [(ep.text.strip(), ep['href']) for ep in episodes]
  172.             return episode_links
  173.         except Exception as e:
  174.             self.console.print(f"[bold red]Erro ao obter episódios: {str(e)}[/bold red]")
  175.             return []
  176.  
  177.     def select_episodes(self, episodes):
  178.         # Mostra opções para seleção de episódios
  179.         self.console.print("\n[bold cyan]Escolha uma opção:[/bold cyan]")
  180.         self.console.print("1. Baixar todos os episódios", style="bold blue")
  181.         self.console.print("2. Selecionar episódios específicos", style="bold blue")
  182.        
  183.         choice = input("\nEscolha uma opção (1-2): ").strip()
  184.        
  185.         if choice == "1":
  186.             return episodes
  187.         elif choice == "2":
  188.             # Mostra episódios para seleção
  189.             self.console.print("\n[bold cyan]Escolha os episódios para download:[/bold cyan]")
  190.             for i, (ep_name, _) in enumerate(episodes, 1):
  191.                 self.console.print(f"[cyan]{i}. {ep_name}[/cyan]")
  192.            
  193.             while True:
  194.                 self.console.print("\n[bold cyan]Digite os números dos episódios separados por vírgula (ex: 1,3,5):[/bold cyan]")
  195.                 choice = input("\nEscolha os episódios: ").strip()
  196.                
  197.                 try:
  198.                     ep_numbers = [int(x.strip()) for x in choice.split(",")]
  199.                     return [episodes[i-1] for i in ep_numbers if 1 <= i <= len(episodes)]
  200.                 except (ValueError, IndexError):
  201.                     self.console.print("[bold red]Entrada inválida! Tente novamente.[/bold red]")
  202.         else:
  203.             self.console.print("[bold red]Opção inválida! Tente novamente.[/bold red]")
  204.             return self.select_episodes(episodes)
  205.  
  206.     def get_download_link(self, episode_url):
  207.         try:
  208.             response = self.scraper.get(episode_url, headers=self.headers)
  209.             response.raise_for_status()
  210.             soup = BeautifulSoup(response.content, 'html.parser')
  211.             download_button = soup.find('a', id='dw')
  212.             if not download_button:
  213.                 self.console.print("[bold red]Botão de download não encontrado[/bold red]")
  214.                 return None, None
  215.            
  216.             download_page_url = download_button['href']
  217.             response = self.scraper.get(download_page_url, headers=self.headers)
  218.             response.raise_for_status()
  219.             soup = BeautifulSoup(response.content, 'html.parser')
  220.             hd_link = soup.find('a', attrs={'download': True})
  221.            
  222.             if hd_link:
  223.                 download_url = hd_link['href']
  224.                 parsed_url = urlparse(download_url)
  225.                 query_params = parse_qs(parsed_url.query)
  226.                 filename = query_params.get('title', ['episode.mp4'])[0]
  227.                 filename = unquote(filename)
  228.                 filename = sanitize_filename(filename)
  229.                 if not filename.endswith('.mp4'):
  230.                     filename += '.mp4'
  231.                 return download_url, filename
  232.                
  233.             self.console.print("[bold red]Link de download não encontrado[/bold red]")
  234.             return None, None
  235.         except Exception as e:
  236.             self.console.print(f"[bold red]Erro ao obter link de download: {str(e)}[/bold red]")
  237.             return None, None
  238.  
  239.     def download_episode(self, url, filename, download_dir):
  240.         try:
  241.             self.console.print(f"[bold blue]Iniciando download do episódio: {filename}[/bold blue]")
  242.            
  243.             session = requests.Session()
  244.             session.verify = False
  245.             requests.packages.urllib3.disable_warnings()
  246.            
  247.             retry_count = 3
  248.             while retry_count > 0:
  249.                 try:
  250.                     response = session.get(url, stream=True, headers=self.headers, timeout=30)
  251.                     break
  252.                 except (requests.exceptions.RequestException, urllib3.exceptions.RequestError) as e:
  253.                     retry_count -= 1
  254.                     if retry_count == 0:
  255.                         raise e
  256.                     time.sleep(2)
  257.            
  258.             total_size = int(response.headers.get('content-length', 0))
  259.            
  260.             file_path = os.path.join(download_dir, filename)
  261.            
  262.             chunk_size = 1024 * 1024  # 1MB chunks
  263.            
  264.             with open(file_path, 'wb') as file:
  265.                 with Progress() as progress:
  266.                     task = progress.add_task("[green]Baixando...", total=total_size)
  267.                     for data in response.iter_content(chunk_size=chunk_size):
  268.                         file.write(data)
  269.                         progress.update(task, advance=len(data))
  270.            
  271.             self.console.print(f"\n[bold green]Download completo: {filename}[/bold green]")
  272.             return True
  273.            
  274.         except Exception as e:
  275.             self.console.print(f"[bold red]Erro no download: {str(e)}[/bold red]")
  276.             return False
  277.  
  278. def main():
  279.     finder = AnimeFinder()
  280.     downloader = AnimeDownloader()
  281.     console = Console()
  282.    
  283.     while True:
  284.         try:
  285.             console.print(Panel.fit(
  286.                 "🌟 [bold blue]Anime Scraper[/bold blue] 🌟\n[bold blue]Anime downloader by goofynn7[/bold blue]",
  287.                 border_style="bold blue"
  288.             ))
  289.            
  290.             console.print("\n[bold cyan]Selecione uma opção:[/bold cyan]")
  291.             console.print("1. Buscar anime", style="bold blue")
  292.             console.print("2. Sair", style="bold blue")
  293.            
  294.             choice = input("\nEscolha uma opção (1-2): ").strip()
  295.            
  296.             if choice == "1":
  297.                 anime_name = input("\nDigite o nome do anime: ").strip()
  298.                 if not anime_name:
  299.                     console.print("[bold yellow]Por favor, digite um nome de anime.[/bold yellow]")
  300.                     continue
  301.                
  302.                 results = finder.search_anime(anime_name)
  303.                 if results:
  304.                     choice = input("\nDigite o número do anime (ou 'voltar' para nova busca): ").strip()
  305.                     if choice.lower() == 'voltar':
  306.                         continue
  307.                        
  308.                     try:
  309.                         choice_index = int(choice) - 1
  310.                         if 0 <= choice_index < len(results):
  311.                             selected_anime = results[choice_index]
  312.                             selected_anime_url = selected_anime['link']
  313.                            
  314.                             anime_info = fetch_and_display_anime_info(selected_anime_url)
  315.                             download_dir = create_anime_directory(selected_anime['title'])
  316.                             safe_filename = sanitize_filename(selected_anime['title'])
  317.                             if save_anime_info(anime_info, download_dir, safe_filename):
  318.                                 console.print("[bold green]Informações do anime salvas com sucesso![/bold green]")
  319.                            
  320.                             episodes = downloader.get_episode_links(selected_anime_url)
  321.                             if episodes:
  322.                                 console.print(f"[bold blue]Encontrados {len(episodes)} episódios[/bold blue]")
  323.                                
  324.                                 episodes_to_download = downloader.select_episodes(episodes)
  325.                                
  326.                                 if episodes_to_download:
  327.                                     for ep_name, ep_url in episodes_to_download:
  328.                                         download_url, filename = downloader.get_download_link(ep_url)
  329.                                         if download_url:
  330.                                             downloader.download_episode(download_url, filename, download_dir)
  331.                                         else:
  332.                                             console.print(f"[bold red]Não foi possível obter o link de download para {ep_name}[/bold red]")
  333.                                         time.sleep(1)
  334.                                 else:
  335.                                     console.print("[bold yellow]Nenhum episódio selecionado para download.[/bold yellow]")
  336.                                    
  337.                         else:
  338.                             console.print("[bold red]Seleção inválida![/bold red]")
  339.                            
  340.                     except ValueError:
  341.                         console.print("[bold red]Por favor, insira um número válido.[/bold red]")
  342.            
  343.             elif choice == "2":
  344.                 console.print("[bold green]Obrigado por usar o Anime Finder![/bold green]")
  345.                 break
  346.            
  347.             else:
  348.                 console.print("[bold red]Opção inválida![/bold red]")
  349.                
  350.         except KeyboardInterrupt:
  351.             console.print("\n[bold green]Programa encerrado pelo usuário.[/bold green]")
  352.             break
  353.         except Exception as e:
  354.             console.print(f"[bold red]Erro inesperado: {str(e)}[/bold red]")
  355.             continue
  356.  
  357. if __name__ == "__main__":
  358.     main()
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement