mirror of
https://github.com/karl0ss/GoGoDownloader.git
synced 2025-04-28 12:23:41 +01:00
Compare commits
89 Commits
Author | SHA1 | Date | |
---|---|---|---|
![]() |
f55d917cc9 | ||
![]() |
475466617d | ||
![]() |
02fa241ec6 | ||
3b599893bc | |||
664f5f4974 | |||
![]() |
62712a3897 | ||
![]() |
d865913154 | ||
2fc41cfa56 | |||
![]() |
5211c127e4 | ||
![]() |
fd1046f8a7 | ||
78a394ffd5 | |||
![]() |
2d1eda3b92 | ||
![]() |
8210e0b07b | ||
![]() |
fc19a690bd | ||
![]() |
b002368705 | ||
![]() |
ee82a26e76 | ||
![]() |
7c4a569678 | ||
![]() |
9ea3c6a6ce | ||
![]() |
64acd0abb2 | ||
![]() |
a09ed8c802 | ||
![]() |
a8d54be2d3 | ||
![]() |
0dffecc6ff | ||
![]() |
46001b041c | ||
![]() |
5a2d682cae | ||
1b2738d4b2 | |||
72cef9b292 | |||
![]() |
6ba9477a89 | ||
093437f464 | |||
![]() |
7a382de2f6 | ||
![]() |
02f275e13a | ||
![]() |
bc957b0607 | ||
b3b02e040c | |||
![]() |
bbec182b38 | ||
0b48cca908 | |||
![]() |
7bfc18383d | ||
![]() |
81b4c63892 | ||
18092af206 | |||
79a5b7ee5f | |||
259973f780 | |||
![]() |
1852b8fe2e | ||
![]() |
79f0c1e248 | ||
![]() |
7adc913e3f | ||
![]() |
f5dc0915f4 | ||
![]() |
01dab52b42 | ||
![]() |
088c7b3dca | ||
![]() |
2df3c5ce1a | ||
90f041d1f6 | |||
a6d0b6036f | |||
a82d14cf7b | |||
9679fc4cb0 | |||
99f808d37c | |||
bf777d1a29 | |||
3262b2c69e | |||
1fa2e28b70 | |||
![]() |
6fd7e6ee9e | ||
![]() |
5f46810de6 | ||
![]() |
ecebb4b7c0 | ||
![]() |
4606c148a6 | ||
![]() |
df43704459 | ||
![]() |
326f934c8a | ||
![]() |
bded493598 | ||
![]() |
196c192479 | ||
![]() |
c24be4c4e9 | ||
![]() |
bde87f1294 | ||
![]() |
06fec65d2e | ||
![]() |
c6bad6cc13 | ||
![]() |
2f9ba1c78b | ||
![]() |
e9e89e3159 | ||
![]() |
d1348cbd95 | ||
![]() |
bda3ead826 | ||
![]() |
9d4dc53961 | ||
![]() |
1ecec7c05d | ||
![]() |
23b99e6362 | ||
![]() |
f81a3699a3 | ||
![]() |
01562f595b | ||
![]() |
a3a5ca7ecd | ||
![]() |
4d456ba9b9 | ||
![]() |
fbf352579c | ||
![]() |
187983b348 | ||
![]() |
d1c222ff91 | ||
![]() |
b3766d5a6e | ||
![]() |
218eb40c40 | ||
![]() |
6b59251753 | ||
![]() |
7b23113f8b | ||
![]() |
a307c706cd | ||
![]() |
1acd36e45e | ||
![]() |
a5619c8c4d | ||
![]() |
46038e93a3 | ||
![]() |
13c49e5fe4 |
147
.gitignore
vendored
147
.gitignore
vendored
@ -1,138 +1,11 @@
|
||||
# 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
|
||||
.vscode/launch.json
|
||||
app.log
|
||||
bookmarkList.json
|
||||
downloadHistory.json
|
||||
|
3
CLIOutput/eng.bat
Normal file
3
CLIOutput/eng.bat
Normal file
@ -0,0 +1,3 @@
|
||||
for /R %%a in ("*.mp4") do MP4Box -lang eng "%%a"
|
||||
|
||||
exit
|
176
GoGoDownloader.py
Normal file
176
GoGoDownloader.py
Normal file
@ -0,0 +1,176 @@
|
||||
import requests
|
||||
import ctypes
|
||||
import os
|
||||
from backend import gogoanime, CustomMessage, config_check
|
||||
from bs4 import BeautifulSoup
|
||||
from colorama import Fore
|
||||
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("GoGo Downloader")
|
||||
except AttributeError:
|
||||
pass
|
||||
|
||||
|
||||
def gogodownloader(config):
|
||||
CURRENT_URL = config["CurrentGoGoAnimeURL"]
|
||||
os.system("cls" if os.name == "nt" else "clear")
|
||||
while True:
|
||||
print(
|
||||
f""" {Fore.LIGHTBLUE_EX}
|
||||
|
||||
______ ______
|
||||
/ ____/___ / ____/___
|
||||
/ / __/ __ \/ / __/ __ \
|
||||
/ /_/ / /_/ / /_/ / /_/ /
|
||||
\__________/\____/\____/ __ __
|
||||
/ __ \____ _ ______ / /___ ____ _____/ /__ _____
|
||||
/ / / / __ \ | /| / / __ \/ / __ \/ __ `/ __ / _ \/ ___/
|
||||
/ /_/ / /_/ / |/ |/ / / / / / /_/ / /_/ / /_/ / __/ /
|
||||
/_____/\____/|__/|__/_/ /_/_/\____/\__,_/\__,_/\___/_/
|
||||
|
||||
{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://{CURRENT_URL}/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(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.")
|
||||
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 = "360"
|
||||
break
|
||||
elif quality == "2":
|
||||
episode_quality = "480"
|
||||
break
|
||||
elif quality == "3":
|
||||
episode_quality = "720"
|
||||
break
|
||||
elif quality == "4":
|
||||
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)
|
||||
|
||||
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 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
|
||||
|
||||
gogo = gogoanime(
|
||||
config,
|
||||
name,
|
||||
episode_quality,
|
||||
folder,
|
||||
all_episodes,
|
||||
episode_start,
|
||||
episode_end,
|
||||
title,
|
||||
)
|
||||
gogo.user_logged_in_check()
|
||||
source = f"https://{CURRENT_URL}/{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
|
||||
|
||||
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)
|
||||
|
||||
use_again = input(f"{IN}Do you want to use the app again? (y|n) > ").lower()
|
||||
if use_again == "y":
|
||||
os.system("cls" if os.name == "nt" else "clear")
|
||||
else:
|
||||
break
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
config = config_check()
|
||||
gogodownloader(config)
|
128
GoGoDownloaderCLI.py
Normal file
128
GoGoDownloaderCLI.py
Normal file
@ -0,0 +1,128 @@
|
||||
import json
|
||||
import io
|
||||
import os
|
||||
import re
|
||||
from backend import *
|
||||
|
||||
|
||||
def renameFile(filename: str):
|
||||
"""_summary_
|
||||
|
||||
Args:
|
||||
filename (str): _description_
|
||||
|
||||
Returns:
|
||||
_type_: _description_
|
||||
"""
|
||||
newFileName = "".join(re.split("\(|\)|\[|\]", filename)[::2])
|
||||
try:
|
||||
os.rename(filename, newFileName)
|
||||
return True
|
||||
except OSError as err:
|
||||
return err
|
||||
|
||||
|
||||
def loadDownloadHistory():
|
||||
"""Loads the downloadHistory.json, creates it if it doesn't exist
|
||||
|
||||
Returns:
|
||||
object: download history list
|
||||
"""
|
||||
if os.path.isfile("./downloadHistory.json") and os.access(
|
||||
"./downloadHistory.json", os.R_OK
|
||||
):
|
||||
return json.load(open("./downloadHistory.json"))
|
||||
else:
|
||||
with io.open(os.path.join("./", "downloadHistory.json"), "w") as db_file:
|
||||
db_file.write(json.dumps([]))
|
||||
return json.load(open("./downloadHistory.json"))
|
||||
|
||||
|
||||
def writeShowToDownloadHistory(showName: str, downloadHistory: list):
|
||||
"""Writes the showName and latestEpisode to the downloadHistory.json file
|
||||
|
||||
Args:
|
||||
showName (str): _description_
|
||||
downloadHistory (list): _description_
|
||||
|
||||
Returns:
|
||||
_type_: _description_
|
||||
"""
|
||||
downloadHistory.append(showName)
|
||||
with io.open(os.path.join("./", "downloadHistory.json"), "w") as db_file:
|
||||
db_file.write(json.dumps(downloadHistory))
|
||||
return json.load(open("./downloadHistory.json"))
|
||||
|
||||
|
||||
def readDownloadHistory(fileNameObject: object, downloadHistory: list):
|
||||
"""Reads the downloadHistory.json and checks if the fileName is present
|
||||
|
||||
Args:
|
||||
fileNameObject (str): _description_
|
||||
downloadHistory (list): _description_
|
||||
|
||||
Returns:
|
||||
_type_: _description_
|
||||
"""
|
||||
dhFileName = (
|
||||
fileNameObject["showName"] + " - " + str(fileNameObject["latestEpisode"])
|
||||
)
|
||||
if dhFileName not in downloadHistory:
|
||||
writeShowToDownloadHistory(dhFileName, downloadHistory)
|
||||
return False
|
||||
else:
|
||||
return True
|
||||
|
||||
|
||||
def main():
|
||||
dh = loadDownloadHistory()
|
||||
config = config_check()
|
||||
downloader = gogoanime(
|
||||
config,
|
||||
1,
|
||||
config["CLIQuality"],
|
||||
"a",
|
||||
1,
|
||||
1,
|
||||
1,
|
||||
config["CLIDownloadLocation"],
|
||||
)
|
||||
list = downloader.get_show_from_bookmark()
|
||||
dl_links = {}
|
||||
for ep in list:
|
||||
if readDownloadHistory(ep, dh):
|
||||
showName = ep["showName"] + " - " + str(ep["latestEpisode"])
|
||||
print(f"{IN}{showName} already downloaded")
|
||||
else:
|
||||
print(
|
||||
f"{IN}Scraping DL for "
|
||||
+ ep["showName"]
|
||||
+ " Ep "
|
||||
+ str(ep["latestEpisode"])
|
||||
)
|
||||
dl_links[downloader.get_download_link(ep["downloadURL"])] = (
|
||||
ep["showName"],
|
||||
ep["latestEpisode"],
|
||||
)
|
||||
result = downloader.file_downloader(dl_links)
|
||||
if config["CleanUpFileName"]:
|
||||
for file in result.data:
|
||||
renameFile(file)
|
||||
if len(result.errors) > 0:
|
||||
while len(result.errors) > 0:
|
||||
print(f"{ERR}{len(result.errors)} links failed retrying.")
|
||||
print(f"{IN}Re-Scraping Links")
|
||||
dl_links.clear()
|
||||
for ep in list:
|
||||
dl_links[downloader.get_download_link(ep["downloadURL"])] = (
|
||||
ep["showName"],
|
||||
ep["latestEpisode"],
|
||||
)
|
||||
result = downloader.file_downloader(dl_links, overwrite_downloads=0)
|
||||
if config["CleanUpFileName"]:
|
||||
for file in result.data:
|
||||
renameFile(file)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
40
GoGoDownloaderCLI.spec
Normal file
40
GoGoDownloaderCLI.spec
Normal file
@ -0,0 +1,40 @@
|
||||
# -*- mode: python ; coding: utf-8 -*-
|
||||
|
||||
|
||||
block_cipher = None
|
||||
|
||||
|
||||
a = Analysis(['GoGoDownloaderCLI.py'],
|
||||
pathex=[],
|
||||
binaries=[],
|
||||
datas=[],
|
||||
hiddenimports=[],
|
||||
hookspath=[],
|
||||
hooksconfig={},
|
||||
runtime_hooks=[],
|
||||
excludes=[],
|
||||
win_no_prefer_redirects=False,
|
||||
win_private_assemblies=False,
|
||||
cipher=block_cipher,
|
||||
noarchive=False)
|
||||
pyz = PYZ(a.pure, a.zipped_data,
|
||||
cipher=block_cipher)
|
||||
|
||||
exe = EXE(pyz,
|
||||
a.scripts,
|
||||
a.binaries,
|
||||
a.zipfiles,
|
||||
a.datas,
|
||||
[],
|
||||
name='GoGoDownloaderCLI',
|
||||
debug=False,
|
||||
bootloader_ignore_signals=False,
|
||||
strip=False,
|
||||
upx=True,
|
||||
upx_exclude=[],
|
||||
runtime_tmpdir=None,
|
||||
console=True,
|
||||
disable_windowed_traceback=False,
|
||||
target_arch=None,
|
||||
codesign_identity=None,
|
||||
entitlements_file=None )
|
67
README.md
67
README.md
@ -1,55 +1,76 @@
|
||||
|
||||
<div align="center">
|
||||
<img
|
||||
style="width: 165px; height: 165px"
|
||||
src="https://i.postimg.cc/VkSMVQrg/ba-logo.png"
|
||||
title="BitAnime"
|
||||
alt="BitAnime"
|
||||
style="width: 300px; height: 300px"
|
||||
src="https://github.com/karl0ss/GoGoDownloader/raw/master/img/gogo_logo.png"
|
||||
title="GoGoDownloader"
|
||||
alt="GoGoDownloader"
|
||||
/>
|
||||
<h3>BitAnime</h3>
|
||||
<h3>GoGo Downloader</h3>
|
||||
<h4>Forked from <a href="https://github.com/sh1nobuu/BitAnime">BitAnime</a></h4>
|
||||
<p>
|
||||
A Python script that allows you to download all of an anime's episodes at once.
|
||||
</p>
|
||||
<a href="https://github.com/sh1nobuu/BitAnime/releases"> <strong>· Download executable version ·</strong></a>
|
||||
|
||||
</div>
|
||||
|
||||
## 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](http://gogoanime3.net). If you get a **404** error, please look up the correct anime name on [gogoanime](http://gogoanime3.net). 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/sh1nobuu/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
|
||||
|
||||
<div align="center">
|
||||
<img style="height:386px; width:688px;" src="https://i.postimg.cc/cLgf8994/ba-screenshot.png"
|
||||
<img style="height:386px; width:688px;" src="https://github.com/karl0ss/GoGoDownloader/raw/master/img/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
|
||||
```
|
||||
**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.gg](https://gogoanime3.gg/) 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://gogoanime3.gg/category/bakemonogatari >> bakemonogatari
|
||||
- https://gogoanime3.gg/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://gogoanime3.gg/category/shadows-house >> shadows-house
|
||||
- https://gogoanime3.gg/category/kono-subarashii-sekai-ni-shukufuku-wo- >> kono-subarashii-sekai-ni-shukufuku-wo-
|
||||
|
||||
|
||||
# GoGoDownloader CLI
|
||||
I have now also created the GoGoDownloader CLI, this tool can be used to run on a scheduled basis, it will login and get the latest episodes from your GoGoAnime bookmarks, and download the latest episode if it has not been downloaded yet.
|
280
backend.py
Normal file
280
backend.py
Normal file
@ -0,0 +1,280 @@
|
||||
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://{self.config['CurrentGoGoAnimeURL']}/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://{self.config['CurrentGoGoAnimeURL']}/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://{self.config['CurrentGoGoAnimeURL']}/",
|
||||
"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://{self.config['CurrentGoGoAnimeURL']}/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://{self.config['CurrentGoGoAnimeURL']}/{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://{self.config['CurrentGoGoAnimeURL']}/{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://{self.config['CurrentGoGoAnimeURL']}/",
|
||||
),
|
||||
]
|
||||
),
|
||||
)
|
||||
|
||||
for link in file_list:
|
||||
if link is not None:
|
||||
try:
|
||||
dl.enqueue_file(
|
||||
link,
|
||||
path=f"./{self.title}",
|
||||
)
|
||||
except:
|
||||
pass
|
||||
files = dl.download()
|
||||
return files
|
||||
|
||||
def get_show_from_bookmark(self):
|
||||
print(f"{IN}Loading shows from bookmarks")
|
||||
bookmarkList = []
|
||||
a = dict(auth=gogoanime.get_gogoanime_auth_cookie(self))
|
||||
resp = requests.get(
|
||||
f"https://{self.config['CurrentGoGoAnimeURL']}/user/bookmark",
|
||||
cookies=a,
|
||||
)
|
||||
soup = BeautifulSoup(resp.text, "html.parser")
|
||||
table = soup.find("div", attrs={"class": "article_bookmark"})
|
||||
splitTableLines = table.text.split("Remove")
|
||||
for rows in splitTableLines:
|
||||
fullRow = " ".join(rows.split())
|
||||
if "Anime name" in fullRow:
|
||||
fullRow = fullRow.replace("Anime name Latest", "")
|
||||
splitRow = fullRow.split("Latest")
|
||||
elif fullRow == "Status":
|
||||
break
|
||||
else:
|
||||
fullRow = fullRow.replace("Status ", "")
|
||||
splitRow = fullRow.split("Latest")
|
||||
animeName = splitRow[0].strip().encode("ascii", "ignore").decode()
|
||||
animeName = re.sub("[^A-Za-z0-9 ]+", "", animeName)
|
||||
animeDownloadName = animeName.replace(" ", "-").lower()
|
||||
episodeNum = splitRow[-1].split()[-1]
|
||||
bookmarkList.append(
|
||||
{
|
||||
"showName": animeName,
|
||||
"latestEpisode": float(episodeNum),
|
||||
"downloadURL": f"https://{self.config['CurrentGoGoAnimeURL']}/{animeDownloadName}-episode-{str(episodeNum)}",
|
||||
}
|
||||
)
|
||||
with open("bookmarkList.json", "w") as f:
|
||||
json.dump(bookmarkList, f)
|
||||
return bookmarkList
|
||||
|
||||
|
||||
@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()
|
10
config.json.default
Normal file
10
config.json.default
Normal file
@ -0,0 +1,10 @@
|
||||
{
|
||||
"GoGoAnime_Username":"",
|
||||
"GoGoAnime_Password":"",
|
||||
"MaxConcurrentDownloads": 4,
|
||||
"CurrentGoGoAnimeURL": "gogoanime3.net",
|
||||
"OverwriteDownloads": 0,
|
||||
"CLIQuality":"720",
|
||||
"CLIDownloadLocation": "CLIOutput",
|
||||
"CleanUpFileName": false
|
||||
}
|
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 |
BIN
img/gogo_icon.ico
Normal file
BIN
img/gogo_icon.ico
Normal file
Binary file not shown.
After Width: | Height: | Size: 31 KiB |
BIN
img/gogo_logo.png
Normal file
BIN
img/gogo_logo.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 66 KiB |
BIN
img/screenshot.png
Normal file
BIN
img/screenshot.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 17 KiB |
BIN
requirements.txt
BIN
requirements.txt
Binary file not shown.
@ -1,83 +0,0 @@
|
||||
# Dependencies
|
||||
import requests as req
|
||||
import shutil
|
||||
import re
|
||||
import os
|
||||
from bs4 import BeautifulSoup
|
||||
from dataclasses import dataclass
|
||||
|
||||
|
||||
@dataclass(init=True)
|
||||
class Download:
|
||||
name: str
|
||||
episode_quality: str
|
||||
episode_number: int
|
||||
folder: str
|
||||
|
||||
def get_links(self, source=None):
|
||||
if source != None:
|
||||
source_ep = f"https://gogoanime.pe/{self.name}-episode-"
|
||||
episode_links = [
|
||||
f"{source_ep}{i}" for i in range(1, self.episode_number + 1)
|
||||
]
|
||||
episode_links.insert(0, source)
|
||||
else:
|
||||
source_ep = f"https://gogoanime.pe/{self.name}-episode-"
|
||||
episode_links = [
|
||||
f"{source_ep}{i}" for i in range(1, self.episode_number + 1)
|
||||
]
|
||||
return episode_links
|
||||
|
||||
def get_download_links(self, episode_link):
|
||||
with req.get(episode_link) as res:
|
||||
soup = BeautifulSoup(res.content, "html.parser")
|
||||
exist = soup.find("h1", {"class": "entry-title"})
|
||||
if exist is None:
|
||||
# Episode link == 200
|
||||
episode_link = soup.find("li", {"class": "dowloads"})
|
||||
return episode_link.a.get("href")
|
||||
else:
|
||||
# Episode link == 404
|
||||
episode_link = f"{episode_link}-"
|
||||
with req.get(episode_link) as res:
|
||||
soup = BeautifulSoup(res.content, "html.parser")
|
||||
exist = soup.find("h1", {"class": "entry-title"})
|
||||
if exist is None:
|
||||
episode_link = soup.find("li", {"class": "dowloads"})
|
||||
return episode_link.a.get("href")
|
||||
else:
|
||||
return None
|
||||
|
||||
def get_download_urls(self, download_link):
|
||||
with req.get(download_link) as res:
|
||||
soup = BeautifulSoup(res.content, "html.parser")
|
||||
link = soup.find("div", {"class": "mirror_link"}).find(
|
||||
"div",
|
||||
text=re.compile(fr"\b{self.episode_quality}\b"),
|
||||
attrs={"class": "dowload"},
|
||||
)
|
||||
if link == None:
|
||||
link = soup.find("div", {"class": "mirror_link"}).find(
|
||||
"div", {"class": "dowload"}
|
||||
)
|
||||
return [download_link.split("+")[-1], link.a.get("href")]
|
||||
|
||||
def download_episodes(self, url):
|
||||
header = {
|
||||
"User-Agent": "Mozilla/5.0 (X11; Linux x86_64; rv:78.0) Gecko/20100101 Firefox/78.0",
|
||||
"Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8",
|
||||
"Accept-Language": "en-US,en;q=0.5",
|
||||
"Accept-Encoding": "gzip, deflate",
|
||||
"Connection": "close",
|
||||
}
|
||||
with req.get(url[1], headers=header, stream=True) as res:
|
||||
episode_name = f"EP.{url[0]}.mp4"
|
||||
file_loc = os.path.join(self.folder, episode_name)
|
||||
with open(file_loc, "wb") as file:
|
||||
shutil.copyfileobj(res.raw, file, 8192)
|
||||
|
||||
|
||||
class InvalidInputValue(Exception):
|
||||
"""Raise when custom_episode_number is equal to 0 or custom_episode_number is greater than episode_number"""
|
||||
|
||||
pass
|
161
src/bitanime.py
161
src/bitanime.py
@ -1,161 +0,0 @@
|
||||
import requests as req
|
||||
import ctypes
|
||||
import os
|
||||
import backend as bd
|
||||
import colorama
|
||||
import concurrent.futures
|
||||
from tqdm.contrib.concurrent import thread_map
|
||||
from bs4 import BeautifulSoup
|
||||
from colorama import Fore
|
||||
|
||||
colorama.init(autoreset=True)
|
||||
try:
|
||||
ctypes.windll.kernel32.SetConsoleTitleW("BitAnime")
|
||||
except (AttributeError):
|
||||
pass
|
||||
|
||||
|
||||
def bitanime():
|
||||
while True:
|
||||
print(
|
||||
f""" {Fore.LIGHTBLUE_EX}
|
||||
____ _ _ _ _
|
||||
| __ )(_) |_ / \ _ __ (_)_ __ ___ ___
|
||||
| _ \| | __| / _ \ | '_ \| | '_ ` _ \ / _ \\
|
||||
| |_) | | |_ / ___ \| | | | | | | | | | __/
|
||||
|____/|_|\__/_/ \_\_| |_|_|_| |_| |_|\___|
|
||||
{Fore.LIGHTYELLOW_EX}
|
||||
By: sh1nobu
|
||||
Github: https://github.com/sh1nobuu/BitAnime
|
||||
"""
|
||||
)
|
||||
while True:
|
||||
name = input(f"[{Fore.GREEN}+{Fore.RESET}] Enter anime name > ").lower()
|
||||
if "-" in name:
|
||||
title = name.replace("-", " ").title().strip()
|
||||
else:
|
||||
title = name.title().strip()
|
||||
source = f"https://gogoanime.pe/category/{name}"
|
||||
with req.get(source) as res:
|
||||
if res.status_code == 200:
|
||||
soup = BeautifulSoup(res.content, "html.parser")
|
||||
episode_number = soup.find("ul", {"id": "episode_page"})
|
||||
episode_number = episode_number.get_text().split("-")[-1].strip()
|
||||
break
|
||||
else:
|
||||
print(
|
||||
f"[{Fore.RED}-{Fore.RESET}] {Fore.LIGHTRED_EX}Error 404: Anime not found. Please try again."
|
||||
)
|
||||
while True:
|
||||
quality = input(
|
||||
f"[{Fore.GREEN}+{Fore.RESET}] Enter episode quality (1.SD/360P|2.HD/720P|3.FULLHD/1080P) > "
|
||||
)
|
||||
if quality == "1" or quality == "":
|
||||
episode_quality = "SDP"
|
||||
break
|
||||
elif quality == "2":
|
||||
episode_quality = "HDP"
|
||||
break
|
||||
elif quality == "3":
|
||||
episode_quality = "FullHDP"
|
||||
break
|
||||
else:
|
||||
print(
|
||||
f"[{Fore.RED}-{Fore.RESET}] {Fore.LIGHTRED_EX}Invalid input. Please try again."
|
||||
)
|
||||
print(f"[{Fore.GREEN}+{Fore.RESET}] Title: {Fore.LIGHTCYAN_EX}{title}")
|
||||
print(
|
||||
f"[{Fore.GREEN}+{Fore.RESET}] Episode/s: {Fore.LIGHTCYAN_EX}{episode_number}"
|
||||
)
|
||||
print(
|
||||
f"[{Fore.GREEN}+{Fore.RESET}] Quality: {Fore.LIGHTCYAN_EX}{episode_quality}"
|
||||
)
|
||||
print(f"[{Fore.GREEN}+{Fore.RESET}] Link: {Fore.LIGHTCYAN_EX}{source}")
|
||||
folder = os.path.join(os.getcwd(), title)
|
||||
if not os.path.exists(folder):
|
||||
os.mkdir(folder)
|
||||
while True:
|
||||
choice = input(
|
||||
f"[{Fore.GREEN}+{Fore.RESET}] Do you want to download all episode? (y/n) > "
|
||||
)
|
||||
if choice in ["y", "n"]:
|
||||
break
|
||||
else:
|
||||
print(
|
||||
f"[{Fore.RED}-{Fore.RESET}] {Fore.LIGHTRED_EX}Invalid input. Please try again."
|
||||
)
|
||||
if choice == "n":
|
||||
while True:
|
||||
try:
|
||||
custom_episode_number = int(
|
||||
input(
|
||||
f"[{Fore.GREEN}+{Fore.RESET}] How many episode do you want to download? > "
|
||||
)
|
||||
)
|
||||
if custom_episode_number == 0 or custom_episode_number > int(
|
||||
episode_number
|
||||
):
|
||||
raise bd.InvalidInputValue
|
||||
else:
|
||||
episode_number = custom_episode_number
|
||||
break
|
||||
except ValueError:
|
||||
print(
|
||||
f"[{Fore.RED}-{Fore.RESET}] {Fore.LIGHTRED_EX}Invalid input. Please try again."
|
||||
)
|
||||
except bd.InvalidInputValue:
|
||||
print(
|
||||
f"[{Fore.RED}-{Fore.RESET}] {Fore.LIGHTRED_EX}Custom episode cannot be equal to 0 or it cannot be greater than {episode_number}"
|
||||
)
|
||||
download = bd.Download(name, episode_quality, int(episode_number), folder)
|
||||
source = f"https://gogoanime.pe/{name}"
|
||||
with req.get(source) as res:
|
||||
soup = BeautifulSoup(res.content, "html.parser")
|
||||
episode_zero = soup.find("h1", {"class": "entry-title"})
|
||||
if episode_zero is None:
|
||||
# Episode 0 == 200
|
||||
with concurrent.futures.ThreadPoolExecutor() as exec:
|
||||
episode_links = download.get_links(source)
|
||||
download_links = list(
|
||||
exec.map(download.get_download_links, episode_links)
|
||||
)
|
||||
download_urls = list(
|
||||
exec.map(download.get_download_urls, download_links)
|
||||
)
|
||||
else:
|
||||
# Episode 0 == 404
|
||||
with concurrent.futures.ThreadPoolExecutor() as exec:
|
||||
episode_links = download.get_links()
|
||||
download_links = list(
|
||||
exec.map(download.get_download_links, episode_links)
|
||||
)
|
||||
download_urls = list(
|
||||
exec.map(download.get_download_urls, download_links)
|
||||
)
|
||||
print(
|
||||
f"[{Fore.GREEN}+{Fore.RESET}] 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):
|
||||
import sys, subprocess
|
||||
|
||||
opener = "open" if sys.platform == "darwin" else "xdg-open"
|
||||
subprocess.call([opener, folder])
|
||||
use_again = input(
|
||||
f"[{Fore.GREEN}+{Fore.RESET}] Do you want to use the app again? (y|n) > "
|
||||
).lower()
|
||||
if use_again == "y":
|
||||
os.system("cls")
|
||||
else:
|
||||
break
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
bitanime()
|
1
version.txt
Normal file
1
version.txt
Normal file
@ -0,0 +1 @@
|
||||
3.2.0
|
1
winRun.bat
Normal file
1
winRun.bat
Normal file
@ -0,0 +1 @@
|
||||
cd C:\Users\Karl.Hudgell\Documents\GoGoDownloader && venv\Scripts\activate && python GoGoDownloaderCLI.py
|
Loading…
x
Reference in New Issue
Block a user