mirror of
				https://github.com/karl0ss/GoGoDownloader.git
				synced 2025-11-04 00:21:13 +00:00 
			
		
		
		
	
						commit
						99f808d37c
					
				
							
								
								
									
										140
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										140
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							@ -1,138 +1,4 @@
 | 
			
		||||
# Byte-compiled / optimized / DLL files
 | 
			
		||||
__pycache__/
 | 
			
		||||
*.py[cod]
 | 
			
		||||
*$py.class
 | 
			
		||||
 | 
			
		||||
# C extensions
 | 
			
		||||
*.so
 | 
			
		||||
 | 
			
		||||
# Distribution / packaging
 | 
			
		||||
.Python
 | 
			
		||||
build/
 | 
			
		||||
develop-eggs/
 | 
			
		||||
dist/
 | 
			
		||||
downloads/
 | 
			
		||||
eggs/
 | 
			
		||||
.eggs/
 | 
			
		||||
lib/
 | 
			
		||||
lib64/
 | 
			
		||||
parts/
 | 
			
		||||
sdist/
 | 
			
		||||
var/
 | 
			
		||||
wheels/
 | 
			
		||||
share/python-wheels/
 | 
			
		||||
*.egg-info/
 | 
			
		||||
.installed.cfg
 | 
			
		||||
*.egg
 | 
			
		||||
MANIFEST
 | 
			
		||||
 | 
			
		||||
# PyInstaller
 | 
			
		||||
#  Usually these files are written by a python script from a template
 | 
			
		||||
#  before PyInstaller builds the exe, so as to inject date/other infos into it.
 | 
			
		||||
*.manifest
 | 
			
		||||
*.spec
 | 
			
		||||
 | 
			
		||||
# Installer logs
 | 
			
		||||
pip-log.txt
 | 
			
		||||
pip-delete-this-directory.txt
 | 
			
		||||
 | 
			
		||||
# Unit test / coverage reports
 | 
			
		||||
htmlcov/
 | 
			
		||||
.tox/
 | 
			
		||||
.nox/
 | 
			
		||||
.coverage
 | 
			
		||||
.coverage.*
 | 
			
		||||
.cache
 | 
			
		||||
nosetests.xml
 | 
			
		||||
coverage.xml
 | 
			
		||||
*.cover
 | 
			
		||||
*.py,cover
 | 
			
		||||
.hypothesis/
 | 
			
		||||
.pytest_cache/
 | 
			
		||||
cover/
 | 
			
		||||
 | 
			
		||||
# Translations
 | 
			
		||||
*.mo
 | 
			
		||||
*.pot
 | 
			
		||||
 | 
			
		||||
# Django stuff:
 | 
			
		||||
*.log
 | 
			
		||||
local_settings.py
 | 
			
		||||
db.sqlite3
 | 
			
		||||
db.sqlite3-journal
 | 
			
		||||
 | 
			
		||||
# Flask stuff:
 | 
			
		||||
instance/
 | 
			
		||||
.webassets-cache
 | 
			
		||||
 | 
			
		||||
# Scrapy stuff:
 | 
			
		||||
.scrapy
 | 
			
		||||
 | 
			
		||||
# Sphinx documentation
 | 
			
		||||
docs/_build/
 | 
			
		||||
 | 
			
		||||
# PyBuilder
 | 
			
		||||
.pybuilder/
 | 
			
		||||
target/
 | 
			
		||||
 | 
			
		||||
# Jupyter Notebook
 | 
			
		||||
.ipynb_checkpoints
 | 
			
		||||
 | 
			
		||||
# IPython
 | 
			
		||||
profile_default/
 | 
			
		||||
ipython_config.py
 | 
			
		||||
 | 
			
		||||
# pyenv
 | 
			
		||||
#   For a library or package, you might want to ignore these files since the code is
 | 
			
		||||
#   intended to run in multiple environments; otherwise, check them in:
 | 
			
		||||
# .python-version
 | 
			
		||||
 | 
			
		||||
# pipenv
 | 
			
		||||
#   According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
 | 
			
		||||
#   However, in case of collaboration, if having platform-specific dependencies or dependencies
 | 
			
		||||
#   having no cross-platform support, pipenv may install dependencies that don't work, or not
 | 
			
		||||
#   install all needed dependencies.
 | 
			
		||||
#Pipfile.lock
 | 
			
		||||
 | 
			
		||||
# PEP 582; used by e.g. github.com/David-OConnor/pyflow
 | 
			
		||||
__pypackages__/
 | 
			
		||||
 | 
			
		||||
# Celery stuff
 | 
			
		||||
celerybeat-schedule
 | 
			
		||||
celerybeat.pid
 | 
			
		||||
 | 
			
		||||
# SageMath parsed files
 | 
			
		||||
*.sage.py
 | 
			
		||||
 | 
			
		||||
# Environments
 | 
			
		||||
.env
 | 
			
		||||
.venv
 | 
			
		||||
env/
 | 
			
		||||
venv/
 | 
			
		||||
ENV/
 | 
			
		||||
env.bak/
 | 
			
		||||
venv.bak/
 | 
			
		||||
 | 
			
		||||
# Spyder project settings
 | 
			
		||||
.spyderproject
 | 
			
		||||
.spyproject
 | 
			
		||||
 | 
			
		||||
# Rope project settings
 | 
			
		||||
.ropeproject
 | 
			
		||||
 | 
			
		||||
# mkdocs documentation
 | 
			
		||||
/site
 | 
			
		||||
 | 
			
		||||
# mypy
 | 
			
		||||
.mypy_cache/
 | 
			
		||||
.dmypy.json
 | 
			
		||||
dmypy.json
 | 
			
		||||
 | 
			
		||||
# Pyre type checker
 | 
			
		||||
.pyre/
 | 
			
		||||
 | 
			
		||||
# pytype static type analyzer
 | 
			
		||||
.pytype/
 | 
			
		||||
 | 
			
		||||
# Cython debug symbols
 | 
			
		||||
cython_debug/
 | 
			
		||||
*.mp4
 | 
			
		||||
config.json
 | 
			
		||||
__pycache__/
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										55
									
								
								README.md
									
									
									
									
									
								
							
							
						
						
									
										55
									
								
								README.md
									
									
									
									
									
								
							@ -1,55 +0,0 @@
 | 
			
		||||
<div align="center">
 | 
			
		||||
  <img
 | 
			
		||||
    style="width: 165px; height: 165px"
 | 
			
		||||
    src="https://i.postimg.cc/VkSMVQrg/ba-logo.png"
 | 
			
		||||
    title="BitAnime"
 | 
			
		||||
    alt="BitAnime"
 | 
			
		||||
  />
 | 
			
		||||
  <h3>BitAnime</h3>
 | 
			
		||||
  <p>
 | 
			
		||||
    A Python script that allows you to download all of an anime's episodes at once.
 | 
			
		||||
  </p>
 | 
			
		||||
  <a href="https://github.com/Arctic4161/BitAnime/releases"> <strong>· Download executable version ·</strong></a>
 | 
			
		||||
</div>
 | 
			
		||||
 | 
			
		||||
## About BitAnime
 | 
			
		||||
 | 
			
		||||
**BitAnime** is a python script that allows you to download anime in large batches. It can also be used to download anime films. **BitAnime** gets its content from [gogoanime](https://gogoanime.pe/). If you get a **404** error, please look up the correct anime name on [gogoanime](https://gogoanime.pe/). The application will let you download all the episodes, or you can choose how many episodes you want to download.
 | 
			
		||||
 | 
			
		||||
## Installation
 | 
			
		||||
 | 
			
		||||
```console
 | 
			
		||||
git clone https://github.com/Arctic4161/BitAnime.git
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
## Screenshot
 | 
			
		||||
 | 
			
		||||
<div align="center">
 | 
			
		||||
  <img style="height:386px; width:688px;" src="https://i.postimg.cc/cLgf8994/ba-screenshot.png"
 | 
			
		||||
  title="BitAnime in action" alt="BitAnime Screenshot">
 | 
			
		||||
  <img style="height:386px; width:688px;" src="https://i.postimg.cc/G2qGDpfV/downloade.png" title="Katekyo Hitman Reborn" alt="Downloaded anime with BitAnime">
 | 
			
		||||
</div>
 | 
			
		||||
 | 
			
		||||
## Dependencies
 | 
			
		||||
 | 
			
		||||
**BitAnime** is highly reliant on the python modules `requests`, `colorama`, `tqdm`, and `BeautifulSoup`.
 | 
			
		||||
 | 
			
		||||
```console
 | 
			
		||||
pip install -r requirements.txt
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
## Usage
 | 
			
		||||
 | 
			
		||||
The anime name is separated by "-". You can either type it manually, or go to [gogoanime.pe](https://gogoanime.pe/) and search for the anime you want to download and copy the name from the URL.
 | 
			
		||||
 | 
			
		||||
### Examples
 | 
			
		||||
 | 
			
		||||
##### One word title
 | 
			
		||||
 | 
			
		||||
- https://gogoanime.pe/category/bakemonogatari >> bakemonogatari
 | 
			
		||||
- https://gogoanime.pe/category/steinsgate >> steinsgate
 | 
			
		||||
 | 
			
		||||
##### Multiple word title
 | 
			
		||||
 | 
			
		||||
- https://gogoanime.pe/category/shadows-house >> shadows-house
 | 
			
		||||
- https://gogoanime.pe/category/kono-subarashii-sekai-ni-shukufuku-wo- >> kono-subarashii-sekai-ni-shukufuku-wo-
 | 
			
		||||
							
								
								
									
										130
									
								
								backend.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										130
									
								
								backend.py
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,130 @@
 | 
			
		||||
import requests
 | 
			
		||||
import json
 | 
			
		||||
import os
 | 
			
		||||
from bs4 import BeautifulSoup
 | 
			
		||||
from dataclasses import dataclass
 | 
			
		||||
from colorama import Fore
 | 
			
		||||
from parfive import Downloader
 | 
			
		||||
from threading import Semaphore
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
OK = f"{Fore.RESET}[{Fore.GREEN}+{Fore.RESET}] "
 | 
			
		||||
ERR = f"{Fore.RESET}[{Fore.RED}-{Fore.RESET}] "
 | 
			
		||||
IN = f"{Fore.RESET}[{Fore.LIGHTBLUE_EX}>{Fore.RESET}] "
 | 
			
		||||
 | 
			
		||||
global CONFIG
 | 
			
		||||
 | 
			
		||||
screenlock = Semaphore(value=1)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def config_check():
 | 
			
		||||
    if os.path.exists("./config.json"):
 | 
			
		||||
        with open("./config.json", "r") as f:
 | 
			
		||||
            CONFIG = json.load(f)
 | 
			
		||||
        if not "GoGoAnimeAuthKey" in CONFIG or len(CONFIG["GoGoAnimeAuthKey"]) == 0:
 | 
			
		||||
            print("GoGoAnimeAuthKey not set in config.json")
 | 
			
		||||
            exit(0)
 | 
			
		||||
        else:
 | 
			
		||||
            return CONFIG
 | 
			
		||||
    else:
 | 
			
		||||
        print("config.json file not found")
 | 
			
		||||
        exit(0)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
CURRENT_DOMAIN = "film"
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@dataclass(init=True)
 | 
			
		||||
class Download:
 | 
			
		||||
    name: str
 | 
			
		||||
    episode_quality: str
 | 
			
		||||
    folder: str
 | 
			
		||||
    all_episodes: int
 | 
			
		||||
    episode_start: int
 | 
			
		||||
    episode_end: int
 | 
			
		||||
    config: object
 | 
			
		||||
    printed: bool = False
 | 
			
		||||
 | 
			
		||||
    def get_links(self, source=None):
 | 
			
		||||
        if source is not None:
 | 
			
		||||
            source_ep = f"https://gogoanime.{self.config['CurrentGoGoAnimeDomain']}/{self.name}-episode-"
 | 
			
		||||
            episode_links = [
 | 
			
		||||
                f"{source_ep}{i}"
 | 
			
		||||
                for i in range(self.episode_start, self.episode_end + 1)
 | 
			
		||||
            ]
 | 
			
		||||
            episode_links.insert(0, source)
 | 
			
		||||
        else:
 | 
			
		||||
            source_ep = f"https://gogoanime.{self.config['CurrentGoGoAnimeDomain']}/{self.name}-episode-"
 | 
			
		||||
            episode_links = [
 | 
			
		||||
                f"{source_ep}{i}"
 | 
			
		||||
                for i in range(self.episode_start, self.episode_end + 1)
 | 
			
		||||
            ]
 | 
			
		||||
        return episode_links
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def get_download_link(config, url, episode_quality):
 | 
			
		||||
 | 
			
		||||
    page = requests.get(
 | 
			
		||||
        url,
 | 
			
		||||
        cookies=dict(auth=config["GoGoAnimeAuthKey"]),
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
    soup = BeautifulSoup(page.content, "html.parser")
 | 
			
		||||
 | 
			
		||||
    for link in soup.find_all("a", href=True):
 | 
			
		||||
        if episode_quality in link.text:
 | 
			
		||||
            return link["href"]
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def file_downloader(file_list: dict, title: str, config: object):
 | 
			
		||||
    dl = Downloader(
 | 
			
		||||
        max_conn=config["MaxConcurrentDownloads"],
 | 
			
		||||
        overwrite=False,
 | 
			
		||||
        headers=dict(
 | 
			
		||||
            [
 | 
			
		||||
                (
 | 
			
		||||
                    "User-Agent",
 | 
			
		||||
                    "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/97.0.4692.71 Safari/537.36",
 | 
			
		||||
                ),
 | 
			
		||||
                ("authority", "gogo-cdn.com"),
 | 
			
		||||
                ("referer", f"https://gogoanime.{config['CurrentGoGoAnimeDomain']}/"),
 | 
			
		||||
            ]
 | 
			
		||||
        ),
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
    for link in file_list:
 | 
			
		||||
        dl.enqueue_file(
 | 
			
		||||
            link,
 | 
			
		||||
            path=f"./{title}",
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
    files = dl.download()
 | 
			
		||||
    return files
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@dataclass(init=True)
 | 
			
		||||
class CustomMessage(Exception):
 | 
			
		||||
    """Custom message that will accept message as a parameter and it will print it on the console."""
 | 
			
		||||
 | 
			
		||||
    message: str = None
 | 
			
		||||
    episode_quality: str = None
 | 
			
		||||
    workingepisode: str = None
 | 
			
		||||
 | 
			
		||||
    def print_error(self):
 | 
			
		||||
        screenlock.acquire()
 | 
			
		||||
        print(ERR, self.message, end=" ")
 | 
			
		||||
        screenlock.release()
 | 
			
		||||
 | 
			
		||||
    def qual_not_found(self):
 | 
			
		||||
        screenlock.acquire()
 | 
			
		||||
        print(
 | 
			
		||||
            f"{ERR}Episode {self.workingepisode} {Fore.LIGHTCYAN_EX}{self.episode_quality}{Fore.RESET} quality not found."
 | 
			
		||||
        )
 | 
			
		||||
        screenlock.release()
 | 
			
		||||
 | 
			
		||||
    def use_default_qual(self):
 | 
			
		||||
        screenlock.acquire()
 | 
			
		||||
        print(
 | 
			
		||||
            f"{OK}Trying {Fore.LIGHTCYAN_EX}{self.episode_quality}{Fore.RESET} quality for Episode {self.workingepisode}."
 | 
			
		||||
        )
 | 
			
		||||
        screenlock.release()
 | 
			
		||||
							
								
								
									
										5
									
								
								config.json.default
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								config.json.default
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,5 @@
 | 
			
		||||
{
 | 
			
		||||
    "GoGoAnimeAuthKey": "",
 | 
			
		||||
    "MaxConcurrentDownloads": 4,
 | 
			
		||||
    "CurrentGoGoAnimeDomain": "film"
 | 
			
		||||
}
 | 
			
		||||
										
											Binary file not shown.
										
									
								
							| 
		 Before Width: | Height: | Size: 26 KiB  | 
										
											Binary file not shown.
										
									
								
							| 
		 Before Width: | Height: | Size: 27 KiB  | 
										
											Binary file not shown.
										
									
								
							| 
		 Before Width: | Height: | Size: 652 KiB  | 
@ -1,37 +1,40 @@
 | 
			
		||||
import requests as req
 | 
			
		||||
import requests
 | 
			
		||||
import ctypes
 | 
			
		||||
import os
 | 
			
		||||
import concurrent.futures
 | 
			
		||||
from backend import Download, CustomMessage, get_download_links
 | 
			
		||||
from tqdm.contrib.concurrent import thread_map
 | 
			
		||||
from backend import *
 | 
			
		||||
from bs4 import BeautifulSoup
 | 
			
		||||
from colorama import Fore
 | 
			
		||||
import sys
 | 
			
		||||
import subprocess
 | 
			
		||||
 | 
			
		||||
OK = f"{Fore.RESET}[{Fore.GREEN}+{Fore.RESET}] "
 | 
			
		||||
ERR = f"{Fore.RESET}[{Fore.RED}-{Fore.RESET}] "
 | 
			
		||||
IN = f"{Fore.RESET}[{Fore.LIGHTBLUE_EX}>{Fore.RESET}] "
 | 
			
		||||
CURRENT_DOMAIN = "film"
 | 
			
		||||
try:
 | 
			
		||||
    ctypes.windll.kernel32.SetConsoleTitleW("BitAnime")
 | 
			
		||||
    ctypes.windll.kernel32.SetConsoleTitleW("GoGo Downloader")
 | 
			
		||||
except AttributeError:
 | 
			
		||||
    pass
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def bitanime():
 | 
			
		||||
def gogodownloader(config):
 | 
			
		||||
    CURRENT_DOMAIN = config["CurrentGoGoAnimeDomain"]
 | 
			
		||||
    os.system("cls")
 | 
			
		||||
    while True:
 | 
			
		||||
        print(
 | 
			
		||||
            f""" {Fore.LIGHTBLUE_EX}
 | 
			
		||||
                   ____  _ _      _          _
 | 
			
		||||
                  | __ )(_) |_   / \   _ __ (_)_ __ ___   ___
 | 
			
		||||
                  |  _ \| | __| / _ \ | '_ \| | '_ ` _ \ / _ \\
 | 
			
		||||
                  | |_) | | |_ / ___ \| | | | | | | | | |  __/
 | 
			
		||||
                  |____/|_|\__/_/   \_\_| |_|_|_| |_| |_|\___|
 | 
			
		||||
                              {Fore.LIGHTYELLOW_EX}
 | 
			
		||||
                                  By: sh1nobu
 | 
			
		||||
                  Github: https://github.com/sh1nobuu/BitAnime
 | 
			
		||||
 | 
			
		||||
             ______      ______                                      
 | 
			
		||||
            / ____/___  / ____/___                                   
 | 
			
		||||
           / / __/ __ \/ / __/ __ \                                  
 | 
			
		||||
          / /_/ / /_/ / /_/ / /_/ /                                  
 | 
			
		||||
          \__________/\____/\____/      __                __         
 | 
			
		||||
             / __ \____ _      ______  / /___  ____ _____/ /__  _____
 | 
			
		||||
            / / / / __ \ | /| / / __ \/ / __ \/ __ `/ __  / _ \/ ___/
 | 
			
		||||
           / /_/ / /_/ / |/ |/ / / / / / /_/ / /_/ / /_/ /  __/ /    
 | 
			
		||||
          /_____/\____/|__/|__/_/ /_/_/\____/\__,_/\__,_/\___/_/     
 | 
			
		||||
    
 | 
			
		||||
                              {Fore.RED}
 | 
			
		||||
                                  By: Karl0ss
 | 
			
		||||
                             Forked From: sh1nobuu
 | 
			
		||||
                  Github: https://github.com/karl0ss/GoGoDownloader
 | 
			
		||||
    """
 | 
			
		||||
        )
 | 
			
		||||
        while True:
 | 
			
		||||
@ -41,7 +44,7 @@ def bitanime():
 | 
			
		||||
            else:
 | 
			
		||||
                title = name.title().strip()
 | 
			
		||||
            source = f"https://gogoanime.{CURRENT_DOMAIN}/category/{name}"
 | 
			
		||||
            with req.get(source) as res:
 | 
			
		||||
            with requests.get(source) as res:
 | 
			
		||||
                if res.status_code == 200:
 | 
			
		||||
                    soup = BeautifulSoup(res.content, "html.parser")
 | 
			
		||||
                    all_episodes = soup.find("ul", {"id": "episode_page"})
 | 
			
		||||
@ -54,16 +57,16 @@ def bitanime():
 | 
			
		||||
                f"{IN}Enter episode quality (1.SD/360P|2.SD/480P|3.HD/720P|4.FULLHD/1080P) > "
 | 
			
		||||
            )
 | 
			
		||||
            if quality == "1" or quality == "":
 | 
			
		||||
                episode_quality = "SDP"
 | 
			
		||||
                episode_quality = "360"
 | 
			
		||||
                break
 | 
			
		||||
            elif quality == "2":
 | 
			
		||||
                episode_quality = "SHD"
 | 
			
		||||
                episode_quality = "480"
 | 
			
		||||
                break
 | 
			
		||||
            elif quality == "3":
 | 
			
		||||
                episode_quality = "HDP"
 | 
			
		||||
                episode_quality = "720"
 | 
			
		||||
                break
 | 
			
		||||
            elif quality == "4":
 | 
			
		||||
                episode_quality = "FullHDP"
 | 
			
		||||
                episode_quality = "1080"
 | 
			
		||||
                break
 | 
			
		||||
            else:
 | 
			
		||||
                print(f"{ERR}Invalid input. Please try again.")
 | 
			
		||||
@ -123,39 +126,31 @@ def bitanime():
 | 
			
		||||
            episode_end = all_episodes
 | 
			
		||||
 | 
			
		||||
        download = Download(
 | 
			
		||||
            name, episode_quality, folder, all_episodes, episode_start, episode_end
 | 
			
		||||
            name,
 | 
			
		||||
            episode_quality,
 | 
			
		||||
            folder,
 | 
			
		||||
            all_episodes,
 | 
			
		||||
            episode_start,
 | 
			
		||||
            episode_end,
 | 
			
		||||
            config,
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
        source = f"https://gogoanime.{CURRENT_DOMAIN}/{name}"
 | 
			
		||||
        with req.get(source) as res:
 | 
			
		||||
        with requests.get(source) as res:
 | 
			
		||||
            soup = BeautifulSoup(res.content, "html.parser")
 | 
			
		||||
            episode_zero = soup.find("h1", {"class": "entry-title"})  # value: 404
 | 
			
		||||
 | 
			
		||||
        if choice == "n" or episode_zero is not None:
 | 
			
		||||
            source = None
 | 
			
		||||
 | 
			
		||||
        dl_links = []
 | 
			
		||||
        episode_links = download.get_links(source)
 | 
			
		||||
        with concurrent.futures.ThreadPoolExecutor() as executor:
 | 
			
		||||
            download_links = list(executor.map(get_download_links, episode_links))
 | 
			
		||||
            download_urls = list(
 | 
			
		||||
                executor.map(download.get_download_urls, download_links)
 | 
			
		||||
            )
 | 
			
		||||
        print(
 | 
			
		||||
            f"{OK}Downloading {Fore.LIGHTCYAN_EX}{len(download_urls)}{Fore.RESET} episode/s"
 | 
			
		||||
        )
 | 
			
		||||
        thread_map(
 | 
			
		||||
            download.download_episodes,
 | 
			
		||||
            download_urls,
 | 
			
		||||
            ncols=75,
 | 
			
		||||
            total=len(download_urls),
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
        try:
 | 
			
		||||
            os.startfile(folder)
 | 
			
		||||
        except AttributeError:
 | 
			
		||||
            opener = "open" if sys.platform == "darwin" else "xdg-open"
 | 
			
		||||
            subprocess.call([opener, folder])
 | 
			
		||||
        print("\n")
 | 
			
		||||
        for link in episode_links:
 | 
			
		||||
            dl_links.append(get_download_link(config, link, episode_quality))
 | 
			
		||||
 | 
			
		||||
        file_downloader(dl_links, title, config)
 | 
			
		||||
 | 
			
		||||
        use_again = input(f"{IN}Do you want to use the app again? (y|n) > ").lower()
 | 
			
		||||
        if use_again == "y":
 | 
			
		||||
            os.system("cls")
 | 
			
		||||
@ -164,4 +159,5 @@ def bitanime():
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
if __name__ == "__main__":
 | 
			
		||||
    bitanime()
 | 
			
		||||
    config = config_check()
 | 
			
		||||
    gogodownloader(config)
 | 
			
		||||
							
								
								
									
										
											BIN
										
									
								
								requirements.txt
									
									
									
									
									
								
							
							
						
						
									
										
											BIN
										
									
								
								requirements.txt
									
									
									
									
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										223
									
								
								src/backend.py
									
									
									
									
									
								
							
							
						
						
									
										223
									
								
								src/backend.py
									
									
									
									
									
								
							@ -1,223 +0,0 @@
 | 
			
		||||
# Dependencies
 | 
			
		||||
import requests as req
 | 
			
		||||
import shutil
 | 
			
		||||
import re
 | 
			
		||||
import os
 | 
			
		||||
from bs4 import BeautifulSoup
 | 
			
		||||
from dataclasses import dataclass
 | 
			
		||||
from colorama import Fore
 | 
			
		||||
from random import choice
 | 
			
		||||
from requests.exceptions import Timeout
 | 
			
		||||
import time
 | 
			
		||||
from threading import Semaphore
 | 
			
		||||
 | 
			
		||||
OK = f"{Fore.RESET}[{Fore.GREEN}+{Fore.RESET}] "
 | 
			
		||||
ERR = f"{Fore.RESET}[{Fore.RED}-{Fore.RESET}] "
 | 
			
		||||
IN = f"{Fore.RESET}[{Fore.LIGHTBLUE_EX}>{Fore.RESET}] "
 | 
			
		||||
 | 
			
		||||
screenlock = Semaphore(value=1)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def random_headers():
 | 
			
		||||
    desktop_agents = [
 | 
			
		||||
        "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/94.0.4606.81 Safari/537.36 Edg/94.0.992.47",
 | 
			
		||||
        'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3626.121 Safari/537.36',
 | 
			
		||||
        'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/74.0.3729.157 Safari/537.36',
 | 
			
		||||
        'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3112.113 Safari/537.36',
 | 
			
		||||
        'Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3112.90 Safari/537.36',
 | 
			
		||||
        'Mozilla/5.0 (Windows NT 10.0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3626.121 Safari/537.36',
 | 
			
		||||
        'Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/74.0.3729.169 Safari/537.36',
 | 
			
		||||
        'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/92.0.4515.159 Safari/537.36']
 | 
			
		||||
    return { "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8",
 | 
			
		||||
            "Accept-Language": "en-US,en;q=0.5",
 | 
			
		||||
            "Accept-Encoding": "gzip, deflate, br",
 | 
			
		||||
            "Connection": "keep-alive",
 | 
			
		||||
            "Referer": "https://gogoplay1.com/",
 | 
			
		||||
            'User-Agent': choice(desktop_agents)}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def get_download_links(episode_link):
 | 
			
		||||
    with req.get(episode_link) as res:
 | 
			
		||||
        soup = BeautifulSoup(res.content, "html.parser")
 | 
			
		||||
        exist = soup.find("h1", {"class": "entry-title"})
 | 
			
		||||
        workinglinkis = episode_link.split('-')
 | 
			
		||||
        if exist is None:
 | 
			
		||||
            # Episode link == 200
 | 
			
		||||
            episode_link = soup.find("li", {"class": "dowloads"})
 | 
			
		||||
            return [workinglinkis[-1], episode_link.a.get("href")]
 | 
			
		||||
        else:
 | 
			
		||||
            # Episode link == 404
 | 
			
		||||
            episode_link = f"{episode_link}-"
 | 
			
		||||
            with req.get(episode_link) as find:
 | 
			
		||||
                soup = BeautifulSoup(find.content, "html.parser")
 | 
			
		||||
                exist = soup.find("h1", {"class": "entry-title"})
 | 
			
		||||
                if exist is None:
 | 
			
		||||
                    episode_link = soup.find("li", {"class": "dowloads"})
 | 
			
		||||
                    return [workinglinkis[-1], episode_link.a.get("href")]
 | 
			
		||||
                else:
 | 
			
		||||
                    return None
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@dataclass(init=True)
 | 
			
		||||
class Download:
 | 
			
		||||
    name: str
 | 
			
		||||
    episode_quality: str
 | 
			
		||||
    folder: str
 | 
			
		||||
    all_episodes: int
 | 
			
		||||
    episode_start: int
 | 
			
		||||
    episode_end: int
 | 
			
		||||
    printed: bool = False
 | 
			
		||||
 | 
			
		||||
    def get_links(self, source=None):
 | 
			
		||||
        if source is not None:
 | 
			
		||||
            source_ep = f"https://gogoanime.wiki/{self.name}-episode-"
 | 
			
		||||
            episode_links = [
 | 
			
		||||
                f"{source_ep}{i}"
 | 
			
		||||
                for i in range(self.episode_start, self.episode_end + 1)
 | 
			
		||||
            ]
 | 
			
		||||
            episode_links.insert(0, source)
 | 
			
		||||
        else:
 | 
			
		||||
            source_ep = f"https://gogoanime.wiki/{self.name}-episode-"
 | 
			
		||||
            episode_links = [
 | 
			
		||||
                f"{source_ep}{i}"
 | 
			
		||||
                for i in range(self.episode_start, self.episode_end + 1)
 | 
			
		||||
            ]
 | 
			
		||||
        return episode_links
 | 
			
		||||
 | 
			
		||||
    def get_download_urls(self, download_link):
 | 
			
		||||
        episode_quality = self.episode_quality
 | 
			
		||||
        workingepisode = download_link[0]
 | 
			
		||||
        if episode_quality == "FullHDP":
 | 
			
		||||
            episode_quality = "1080P - mp4"
 | 
			
		||||
        elif episode_quality == "HDP":
 | 
			
		||||
            episode_quality = "720P - mp4"
 | 
			
		||||
        elif episode_quality == "SHD":
 | 
			
		||||
            episode_quality = "480P - mp4"
 | 
			
		||||
        elif episode_quality == "SDP":
 | 
			
		||||
            episode_quality = "360P - mp4"
 | 
			
		||||
        else:
 | 
			
		||||
            episode_quality = "1080P - mp4"
 | 
			
		||||
        with req.get(download_link[1], headers=random_headers(), timeout=3) as res:
 | 
			
		||||
            soup = BeautifulSoup(res.content, "html.parser")
 | 
			
		||||
            link = soup.find("div", {"class": "dowload"}, text=re.compile(episode_quality))
 | 
			
		||||
            if link is None:
 | 
			
		||||
                pass
 | 
			
		||||
            else:
 | 
			
		||||
                try:
 | 
			
		||||
                    with req.get(link.a.get("href"), headers=random_headers(), stream=True,
 | 
			
		||||
                                 timeout=3) as workingit:
 | 
			
		||||
                        if workingit.status_code != 200:
 | 
			
		||||
                            link = None
 | 
			
		||||
                        elif workingit.headers['Content-Type'] != 'video/mp4':
 | 
			
		||||
                            link = None
 | 
			
		||||
                except Timeout:
 | 
			
		||||
                    link = None
 | 
			
		||||
            if link is None:
 | 
			
		||||
                if episode_quality == "1080P - mp4":
 | 
			
		||||
                    episode_quality = "FullHDP"
 | 
			
		||||
                    time.sleep(1)
 | 
			
		||||
                    CustomMessage('None', episode_quality, workingepisode).qual_not_found()
 | 
			
		||||
                    episode_quality = "HDP"
 | 
			
		||||
                    time.sleep(1)
 | 
			
		||||
                    CustomMessage('None', episode_quality, workingepisode).use_default_qual()
 | 
			
		||||
                    episode_quality = "720P - mp4"
 | 
			
		||||
                link = soup.find("div", {"class": "dowload"}, text=re.compile(episode_quality))
 | 
			
		||||
                if link is None:
 | 
			
		||||
                    pass
 | 
			
		||||
                else:
 | 
			
		||||
                    try:
 | 
			
		||||
                        with req.get(link.a.get("href"), headers=random_headers(), stream=True,
 | 
			
		||||
                                     timeout=3) as workingit:
 | 
			
		||||
                            if workingit.status_code != 200:
 | 
			
		||||
                                link = None
 | 
			
		||||
                            elif workingit.headers['Content-Type'] != 'video/mp4':
 | 
			
		||||
                                link = None
 | 
			
		||||
                    except Timeout:
 | 
			
		||||
                        link = None
 | 
			
		||||
                if link is None:
 | 
			
		||||
                    if episode_quality == "720P - mp4":
 | 
			
		||||
                        episode_quality = "HDP"
 | 
			
		||||
                        time.sleep(1)
 | 
			
		||||
                        CustomMessage('None', episode_quality, workingepisode).qual_not_found()
 | 
			
		||||
                        episode_quality = "SHD"
 | 
			
		||||
                        time.sleep(1)
 | 
			
		||||
                        CustomMessage('None', episode_quality, workingepisode).use_default_qual()
 | 
			
		||||
                        episode_quality = "480P - mp4"
 | 
			
		||||
                    link = soup.find("div", {"class": "dowload"}, text=re.compile(episode_quality))
 | 
			
		||||
                    if link is None:
 | 
			
		||||
                        pass
 | 
			
		||||
                    else:
 | 
			
		||||
                        try:
 | 
			
		||||
                            with req.get(link.a.get("href"), headers=random_headers(), stream=True,
 | 
			
		||||
                                         timeout=3) as workingit:
 | 
			
		||||
                                if workingit.status_code != 200:
 | 
			
		||||
                                    link = None
 | 
			
		||||
                                elif workingit.headers['Content-Type'] != 'video/mp4':
 | 
			
		||||
                                    link = None
 | 
			
		||||
                        except Timeout:
 | 
			
		||||
                            link = None
 | 
			
		||||
                    if link is None:
 | 
			
		||||
                        if episode_quality == "480P - mp4":
 | 
			
		||||
                            episode_quality = "SHD"
 | 
			
		||||
                            time.sleep(1)
 | 
			
		||||
                            CustomMessage('None', episode_quality, workingepisode).qual_not_found()
 | 
			
		||||
                            episode_quality = "SDP"
 | 
			
		||||
                            time.sleep(1)
 | 
			
		||||
                            CustomMessage('None', episode_quality, workingepisode).use_default_qual()
 | 
			
		||||
                            episode_quality = "360P - mp4"
 | 
			
		||||
                        link = soup.find("div", {"class": "dowload"}, text=re.compile(episode_quality))
 | 
			
		||||
                    else:
 | 
			
		||||
                        pass
 | 
			
		||||
        return [download_link[1].split("+")[-1], link.a.get("href")]
 | 
			
		||||
 | 
			
		||||
    def download_episodes(self, url):
 | 
			
		||||
        with req.get(url[1], headers=random_headers(), stream=True, timeout=10,
 | 
			
		||||
                     allow_redirects=True) as workingurl:
 | 
			
		||||
            episode_name = "EP." + url[0] + ".mp4"
 | 
			
		||||
            file_loc = os.path.join(self.folder, episode_name)
 | 
			
		||||
            with open(file_loc, "w+b") as file:
 | 
			
		||||
                shutil.copyfileobj(workingurl.raw, file, 8192)
 | 
			
		||||
 | 
			
		||||
        size = os.stat(file_loc).st_size
 | 
			
		||||
        count = 0
 | 
			
		||||
        while int(size) < 5 and count < 5:
 | 
			
		||||
            with req.get(url[1], headers=random_headers(), stream=True, timeout=10,
 | 
			
		||||
                         allow_redirects=True) as workingurl:
 | 
			
		||||
                episode_name = "EP." + url[0] + ".mp4"
 | 
			
		||||
                file_loc = os.path.join(self.folder, episode_name)
 | 
			
		||||
                with open(file_loc, "w+b") as file:
 | 
			
		||||
                    shutil.copyfileobj(workingurl.raw, file, 8192)
 | 
			
		||||
            count += 1
 | 
			
		||||
            size = os.stat(file_loc).st_size
 | 
			
		||||
        size = os.stat(file_loc).st_size
 | 
			
		||||
        if int(size) < 5:
 | 
			
		||||
            print("\n")
 | 
			
		||||
            CustomMessage('Could not download episode ' + url[0]).print_error()
 | 
			
		||||
            os.remove(file_loc)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@dataclass(init=True)
 | 
			
		||||
class CustomMessage(Exception):
 | 
			
		||||
    """Custom message that will accept message as a parameter and it will print it on the console."""
 | 
			
		||||
 | 
			
		||||
    message: str = None
 | 
			
		||||
    episode_quality: str = None
 | 
			
		||||
    workingepisode: str = None
 | 
			
		||||
 | 
			
		||||
    def print_error(self):
 | 
			
		||||
        screenlock.acquire()
 | 
			
		||||
        print(ERR, self.message, end=' ')
 | 
			
		||||
        screenlock.release()
 | 
			
		||||
 | 
			
		||||
    def qual_not_found(self):
 | 
			
		||||
        screenlock.acquire()
 | 
			
		||||
        print(
 | 
			
		||||
            f"{ERR}Episode {self.workingepisode} {Fore.LIGHTCYAN_EX}{self.episode_quality}{Fore.RESET} quality not found.")
 | 
			
		||||
        screenlock.release()
 | 
			
		||||
 | 
			
		||||
    def use_default_qual(self):
 | 
			
		||||
        screenlock.acquire()
 | 
			
		||||
        print(
 | 
			
		||||
            f"{OK}Trying {Fore.LIGHTCYAN_EX}{self.episode_quality}{Fore.RESET} quality for Episode {self.workingepisode}.")
 | 
			
		||||
        screenlock.release()
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user