diff --git a/.gitignore b/.gitignore index a81c8ee..bfb8e83 100644 --- a/.gitignore +++ b/.gitignore @@ -1,138 +1,7 @@ -# 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__/ +build/ +dist/ +main.spec diff --git a/src/bitanime.py b/GoGoDownloader.py similarity index 57% rename from src/bitanime.py rename to GoGoDownloader.py index a7e2756..3309948 100644 --- a/src/bitanime.py +++ b/GoGoDownloader.py @@ -1,50 +1,56 @@ -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 gogoanime, CustomMessage, config_check from bs4 import BeautifulSoup from colorama import Fore -import sys -import subprocess +import logging 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}] " try: - ctypes.windll.kernel32.SetConsoleTitleW("BitAnime") + ctypes.windll.kernel32.SetConsoleTitleW("GoGo Downloader") except AttributeError: pass -def bitanime(): - os.system("cls") +def gogodownloader(config): + CURRENT_DOMAIN = config["CurrentGoGoAnimeDomain"] + os.system("cls" if os.name == "nt" else "clear") 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: name = input(f"{IN}Enter anime name > ").lower() + logging.info("episode searched for " + name) if "-" in name: title = name.replace("-", " ").title().strip() else: title = name.title().strip() - source = f"https://gogoanime.wiki/category/{name}" - with req.get(source) as res: + source = f"https://gogoanime.{CURRENT_DOMAIN}/category/{name}" + 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"}) - all_episodes = int(all_episodes.get_text().split("-")[-1].strip()) + all_episodes = int(list(filter(None, "-".join(all_episodes.get_text().splitlines()).split("-")))[-1].strip()) break else: print(f"{ERR}Error 404: Anime not found. Please try again.") @@ -53,24 +59,25 @@ 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.") + logging.info("quality selected " + episode_quality) print(f"{OK}Title: {Fore.LIGHTCYAN_EX}{title}") print(f"{OK}Episode/s: {Fore.LIGHTCYAN_EX}{all_episodes}") print(f"{OK}Quality: {Fore.LIGHTCYAN_EX}{episode_quality}") print(f"{OK}Link: {Fore.LIGHTCYAN_EX}{source}") - + folder = os.path.join(os.getcwd(), title) if not os.path.exists(folder): os.mkdir(folder) @@ -121,44 +128,49 @@ def bitanime(): else: episode_end = all_episodes - download = Download( - name, episode_quality, folder, all_episodes, episode_start, episode_end + gogo = gogoanime( + config, + name, + episode_quality, + folder, + all_episodes, + episode_start, + episode_end, + title, ) - - source = f"https://gogoanime.wiki/{name}" - with req.get(source) as res: + gogo.user_logged_in_check() + source = f"https://gogoanime.{CURRENT_DOMAIN}/{name}" + 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 - 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) - ) + dl_links = [] + episode_links = gogo.get_links(source) + print(f"{OK}Scraping Links") + for link in episode_links: + dl_links.append(gogo.get_download_link(link)) + + result = gogo.file_downloader(dl_links) + if len(result.errors) > 0: + while len(result.errors) > 0: + print(f"{ERR}{len(result.errors)} links failed retrying.") + episode_links = gogo.get_links(source) + print(f"{OK}Re-Scraping Links") + dl_links.clear() + for link in episode_links: + dl_links.append(gogo.get_download_link(link)) + result = gogo.file_downloader(dl_links, overwrite_downloads=0) - try: - os.startfile(folder) - except AttributeError: - opener = "open" if sys.platform == "darwin" else "xdg-open" - subprocess.call([opener, folder]) - print("\n") use_again = input(f"{IN}Do you want to use the app again? (y|n) > ").lower() if use_again == "y": - os.system("cls") + os.system("cls" if os.name == "nt" else "clear") else: break if __name__ == "__main__": - bitanime() + config = config_check() + gogodownloader(config) diff --git a/README.md b/README.md index cf618b4..62c2176 100644 --- a/README.md +++ b/README.md @@ -1,55 +1,72 @@ +
BitAnime -

BitAnime

+

GoGo Downloader

+

Forked from BitAnime

A Python script that allows you to download all of an anime's episodes at once.

- · Download executable version · +
-## About BitAnime +## About GoGo Downloader -**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. +GoGo Downloader is based on the now broken **BitAnime**. I have had to rework quite a bit of the code to get it working again, and have ideas for some other improvements, I don't want to mess with the original codebase too much, hence **GoGo Downloader**. + +**GoGo Downloader** gets its content from [gogoanime](https://gogoanime.film/). If you get a **404** error, please look up the correct anime name on [gogoanime](https://gogoanime.film/). The application will let you download all the episodes, or you can choose how many episodes you want to download. + +GoGo Anime has changed the way they show download links, and this no longer works via BS4, as the recaptcha blocks the links, but if you are logged in, there are other routes to get to download links, I have taken one of these routes to restore the application. + +## Features + +- Download all qualities options from GoGoAnime +- Update the current GoGoAnime domain via config file (as they keep changing it) +- Specify the number of concurrent downloads via config file (Max is 6) +- Set file overwrite via config file (0 = Skip / 1 = Overwrite) ## Installation +You have 2 options here, you can download the exe on the releases page and run on Windows -```console -git clone https://github.com/Arctic4161/BitAnime.git -``` +- Download the zip +- Extract and set your GoGoAnime Username and Password in the config.json +- Run the exe + +If you want to run from source, or are using Linux/Mac you can run directly from source doing the following - + +- `git clone https://github.com/karl0ss/GoGoDownloader.git` +- `pip install -r requirements.txt` +- Create config.json from config.json.default +- Add your GoGoAnime Username and Password to config.json (Can't be a Google account) +- Run the app with `python GoGoDownloader.py` ## Screenshot
- BitAnime Screenshot - Downloaded anime with BitAnime
## Dependencies -**BitAnime** is highly reliant on the python modules `requests`, `colorama`, `tqdm`, and `BeautifulSoup`. - -```console -pip install -r requirements.txt -``` +**GoGo Downloader** is highly reliant on the python modules `requests`, `colorama`, `parfive`, and `BeautifulSoup`. ## 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. +The anime name is separated by "-". You can either type it manually, or go to [gogoanime.film](https://gogoanime.film/) 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 +- https://gogoanime.film/category/bakemonogatari >> bakemonogatari +- https://gogoanime.film/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- +- https://gogoanime.film/category/shadows-house >> shadows-house +- https://gogoanime.film/category/kono-subarashii-sekai-ni-shukufuku-wo- >> kono-subarashii-sekai-ni-shukufuku-wo- diff --git a/backend.py b/backend.py new file mode 100644 index 0000000..0a42c22 --- /dev/null +++ b/backend.py @@ -0,0 +1,242 @@ +import re +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 +import logging + +logging.basicConfig( + level=logging.INFO, + filename="app.log", + filemode="w", + format="%(name)s - %(levelname)s - %(message)s", +) + + +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(): + """Check for config.json and check required keys are set + + Returns: + [object]: Config object + """ + if os.path.exists("./config.json"): + logging.info("Config.json loaded") + with open("./config.json", "r") as f: + CONFIG = json.load(f) + if not "GoGoAnime_Username" in CONFIG or len(CONFIG["GoGoAnime_Username"]) == 0: + logging.error("GoGoAnime_Username not set in config.json") + print("GoGoAnime_Username not set in config.json") + exit(0) + else: + if ( + not "GoGoAnime_Password" in CONFIG + or len(CONFIG["GoGoAnime_Password"]) == 0 + ): + logging.error("GoGoAnime_Password not set in config.json") + print("GoGoAnime_Password not set in config.json") + exit(0) + else: + logging.info( + "Config loaded and " + + CONFIG["GoGoAnime_Username"] + + " username found" + ) + return CONFIG + else: + logging.error("config.json not found") + print("config.json file not found") + exit(0) + + +def max_concurrent_downloads(max_conn: int): + """Check max_concurrent_downloads value and restrict to below 6 + + Args: + max_conn (int): Max concurrent downloads to allow + + Returns: + [int]: Max concurrent downloads allowed + """ + if max_conn > 6: + return 6 + else: + return max_conn + + +CURRENT_DOMAIN = "film" + + +@dataclass(init=True) +class gogoanime: + config: object + name: str + episode_quality: str + folder: str + all_episodes: int + episode_start: int + episode_end: int + title: str + printed: bool = False + + def get_gogoanime_auth_cookie(self): + session = requests.session() + page = session.get( + f"https://gogoanime.{self.config['CurrentGoGoAnimeDomain']}/login.html" + ) + soup = BeautifulSoup(page.content, "html.parser") + meta_path = soup.select('meta[name="csrf-token"]') + csrf_token = meta_path[0].attrs["content"] + + url = f"https://gogoanime.{self.config['CurrentGoGoAnimeDomain']}/login.html" + payload = f"email={self.config['GoGoAnime_Username']}&password={self.config['GoGoAnime_Password']}&_csrf={csrf_token}" + headers = { + "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.{self.config['CurrentGoGoAnimeDomain']}/", + "content-type": "application/x-www-form-urlencoded", + } + session.headers = headers + + r = session.post(url, data=payload, headers=headers) + + if r.status_code == 200: + return session.cookies.get_dict().get("auth") + else: + print("ldldl") + + def user_logged_in_check( + self, + ): + page = requests.get( + f"https://gogoanime.{self.config['CurrentGoGoAnimeDomain']}/one-piece-episode-1", + cookies=dict(auth=gogoanime.get_gogoanime_auth_cookie(self)), + ) + soup = BeautifulSoup(page.content, "html.parser") + loginCheck = soup(text=re.compile("Logout")) + if len(loginCheck) == 0: + raise Exception( + "User is not logged in, make sure account has been activated" + ) + + 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(self, url): + page = requests.get( + url, + cookies=dict(auth=gogoanime.get_gogoanime_auth_cookie(self)), + ) + quality_arr = ["1080", "720", "640", "480"] + soup = BeautifulSoup(page.content, "html.parser") + try: + for link in soup.find_all( + "a", href=True, string=re.compile(self.episode_quality) + ): + return link["href"] + else: + ep_num = url.rsplit("-", 1)[1] + print( + f"{self.episode_quality} not found for ep{ep_num} checking for next best" + ) + for q in quality_arr: + for link in soup.find_all("a", href=True, string=re.compile(q)): + print(f"{q} found.") + return link["href"] + except: + print("No matching download found") + + def file_downloader(self, file_list: dict, overwrite_downloads: bool = None): + """[summary] + + Args: + file_list (dict): [description] + overwrite_downloads (bool, optional): [description]. Defaults to None. + + Returns: + [type]: [description] + """ + if overwrite_downloads is None: + overwrite = self.config["OverwriteDownloads"] + else: + overwrite = overwrite_downloads + dl = Downloader( + max_conn=max_concurrent_downloads(self.config["MaxConcurrentDownloads"]), + overwrite=overwrite, + 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.{self.config['CurrentGoGoAnimeDomain']}/", + ), + ] + ), + ) + + for link in file_list: + if link is not None: + dl.enqueue_file( + link, + path=f"./{self.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() diff --git a/config.json.default b/config.json.default new file mode 100644 index 0000000..210af6f --- /dev/null +++ b/config.json.default @@ -0,0 +1,7 @@ +{ + "GoGoAnime_Username":"", + "GoGoAnime_Password":"", + "MaxConcurrentDownloads": 4, + "CurrentGoGoAnimeDomain": "gg", + "OverwriteDownloads": 0 +} diff --git a/images/ba-logo.png b/images/ba-logo.png deleted file mode 100644 index cb8d0e6..0000000 Binary files a/images/ba-logo.png and /dev/null differ diff --git a/images/ba-screenshot.PNG b/images/ba-screenshot.PNG deleted file mode 100644 index 1aca9c7..0000000 Binary files a/images/ba-screenshot.PNG and /dev/null differ diff --git a/images/downloaded.PNG b/images/downloaded.PNG deleted file mode 100644 index cc56f1c..0000000 Binary files a/images/downloaded.PNG and /dev/null differ diff --git a/img/gogo_icon.ico b/img/gogo_icon.ico new file mode 100644 index 0000000..d3f9b53 Binary files /dev/null and b/img/gogo_icon.ico differ diff --git a/img/gogo_logo.png b/img/gogo_logo.png new file mode 100644 index 0000000..ccc5992 Binary files /dev/null and b/img/gogo_logo.png differ diff --git a/img/screenshot.png b/img/screenshot.png new file mode 100644 index 0000000..437ef55 Binary files /dev/null and b/img/screenshot.png differ diff --git a/requirements.txt b/requirements.txt index 079fb68..bafbca4 100644 Binary files a/requirements.txt and b/requirements.txt differ diff --git a/src/backend.py b/src/backend.py deleted file mode 100644 index 1df1655..0000000 --- a/src/backend.py +++ /dev/null @@ -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() diff --git a/version.txt b/version.txt new file mode 100644 index 0000000..50e47c8 --- /dev/null +++ b/version.txt @@ -0,0 +1 @@ +3.1.1 \ No newline at end of file