diff --git a/.gitignore b/.gitignore deleted file mode 100644 index a81c8ee..0000000 --- a/.gitignore +++ /dev/null @@ -1,138 +0,0 @@ -# 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/ diff --git a/README.md b/README.md deleted file mode 100644 index cf618b4..0000000 --- a/README.md +++ /dev/null @@ -1,55 +0,0 @@ -
- BitAnime -

BitAnime

-

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

- · Download executable version · -
- -## 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 - -
- 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 -``` - -## 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- 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/requirements.txt b/requirements.txt deleted file mode 100644 index 079fb68..0000000 Binary files a/requirements.txt and /dev/null 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/src/bitanime.py b/src/bitanime.py deleted file mode 100644 index 57a0a83..0000000 --- a/src/bitanime.py +++ /dev/null @@ -1,167 +0,0 @@ -import requests as req -import ctypes -import os -import concurrent.futures -from backend import Download, CustomMessage, get_download_links -from tqdm.contrib.concurrent import thread_map -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") -except AttributeError: - pass - - -def bitanime(): - os.system("cls") - while True: - print( - f""" {Fore.LIGHTBLUE_EX} - ____ _ _ _ _ - | __ )(_) |_ / \ _ __ (_)_ __ ___ ___ - | _ \| | __| / _ \ | '_ \| | '_ ` _ \ / _ \\ - | |_) | | |_ / ___ \| | | | | | | | | | __/ - |____/|_|\__/_/ \_\_| |_|_|_| |_| |_|\___| - {Fore.LIGHTYELLOW_EX} - By: sh1nobu - Github: https://github.com/sh1nobuu/BitAnime - """ - ) - while True: - name = input(f"{IN}Enter anime name > ").lower() - if "-" in name: - title = name.replace("-", " ").title().strip() - else: - title = name.title().strip() - source = f"https://gogoanime.{CURRENT_DOMAIN}/category/{name}" - with req.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()) - break - else: - print(f"{ERR}Error 404: Anime not found. Please try again.") - while True: - quality = input( - 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" - break - elif quality == "2": - episode_quality = "SHD" - break - elif quality == "3": - episode_quality = "HDP" - break - elif quality == "4": - episode_quality = "FullHDP" - break - else: - print(f"{ERR}Invalid input. Please try again.") - 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) - - choice = "y" - - if all_episodes != 1: - while True: - choice = input( - f"{IN}Do you want to download all episode? (y/n) > " - ).lower() - if choice in ["y", "n"]: - break - else: - print(f"{ERR}Invalid input. Please try again.") - - episode_start = None - episode_end = None - - if choice == "n": - while True: - try: - episode_start = int(input(f"{IN}Episode start > ")) - episode_end = int(input(f"{IN}Episode end > ")) - if episode_start <= 0 or episode_end <= 0: - CustomMessage( - f"{ERR}episode_start or episode_end cannot be less than or equal to 0" - ).print_error() - elif episode_start >= all_episodes or episode_end > all_episodes: - CustomMessage( - f"{ERR}episode_start or episode_end cannot be more than {all_episodes}" - ).print_error() - elif episode_end <= episode_start: - CustomMessage( - f"{ERR}episode_end cannot be less than or equal to episode_start" - ).print_error() - else: - break - except ValueError: - print(f"{ERR}Invalid input. Please try again.") - - if episode_start is not None: - pass - else: - episode_start = 1 - if episode_end is not None: - pass - else: - episode_end = all_episodes - - download = Download( - name, episode_quality, folder, all_episodes, episode_start, episode_end - ) - - source = f"https://gogoanime.{CURRENT_DOMAIN}/{name}" - with req.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), - ) - - 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") - else: - break - - -if __name__ == "__main__": - bitanime()