clean repo

This commit is contained in:
Karl Hudgell 2022-01-18 20:53:34 +00:00
parent 1fa2e28b70
commit 3262b2c69e
8 changed files with 0 additions and 583 deletions

138
.gitignore vendored
View File

@ -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/

View File

@ -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-

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

Binary file not shown.

View File

@ -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()

View File

@ -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()