Skip to content

Commit

Permalink
v0.1.1-alpha
Browse files Browse the repository at this point in the history
- GUI added
- changed source code accordingly
- added anime-dl.py for GUI executable
- Updated README
  • Loading branch information
Oshan96 committed Mar 15, 2020
1 parent 5ec2f4e commit af541d2
Show file tree
Hide file tree
Showing 9 changed files with 184 additions and 27 deletions.
21 changes: 14 additions & 7 deletions Anime_Downloader.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,14 @@
directory = ""
threads = 1

token = None

titles = False

args = None

gui = None

class Worker(Thread) :
def __init__(self, tasks) :
Thread.__init__(self)
Expand All @@ -29,13 +33,14 @@ def __init__(self, tasks) :
self.start()

def run(self) :
global gui
while True :
func, arg, kargs = self.tasks.get()
try :
func(*arg, **kargs)
except Exception as ex :
# print(ex)
Color.printer("ERROR", ex)
Color.printer("ERROR", ex, gui)
finally :
self.tasks.task_done()

Expand Down Expand Up @@ -63,9 +68,9 @@ def clean_file_name(file_name) :
return file_name

def download_episode(episode) :
global titles
global titles, gui

Color.printer("INFO","Downloading "+episode.episode+"...")
Color.printer("INFO","Downloading "+episode.episode+"...", gui)

if system() == "Windows" :
episode.title = clean_file_name(episode.title)
Expand All @@ -79,11 +84,11 @@ def download_episode(episode) :
with open(file_name, 'wb') as f:
shutil.copyfileobj(r.raw, f, length=16*1024*1024)

Color.printer("INFO",episode.episode + " finished downloading...")
Color.printer("INFO",episode.episode + " finished downloading...", gui)


def download() :
global directory, threads
global directory, threads, gui

try:
_create_unverified_https_context = ssl._create_unverified_context
Expand All @@ -94,7 +99,7 @@ def download() :
# Handle target environment that doesn't support HTTPS verification
ssl._create_default_https_context = _create_unverified_https_context

Color.printer("INFO","Downloading started...")
Color.printer("INFO","Downloading started...", gui)

# for episode in Anime_Scraper.episodes :
# print("Downloading", episode.episode)
Expand All @@ -105,14 +110,16 @@ def download() :
pool.map(download_episode, Anime_Scraper.episodes)
pool.wait_completion()

Color.printer("INFO", "Downloading finished!", gui)


def print_banner() :
banner = text2art("Anime Downloader")
Color.printer("BANNER", banner)


def main() :
global directory, args, threads, titles
global directory, args, threads, titles, token

print_banner()

Expand Down
44 changes: 28 additions & 16 deletions Anime_Scraper.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import json
import sys
import os
# import browser_cookie3 as bc
import Color
from bs4 import BeautifulSoup
from time import sleep
Expand All @@ -24,12 +25,15 @@
site_key = "6LfEtpwUAAAAABoJ_595sf-Hh0psstoatwZpLex1"
api_key = None

cookies = None
gui = None

session = requests.Session()

episodes = []

def get_token(url) :
global session, site_key, api_key
global session, site_key, api_key, gui

s = requests.Session()

Expand All @@ -49,7 +53,7 @@ def get_token(url) :
return recaptcha_answer

except Exception:
Color.printer("ERROR",'Failed to solve ReCaptcha!')
Color.printer("ERROR",'Failed to solve ReCaptcha!', gui)
return None


Expand All @@ -75,29 +79,34 @@ def verify(token) :
session.post("https://9anime.to/waf-verify", data=payload)

def extract_page_urls(start_episode, end_episode, token) :
global session, episodes, nine_anime_url, download_9anime_url, ts_no, episodes, api_key
global session, episodes, nine_anime_url, download_9anime_url, ts_no, episodes, api_key, cookies, gui

session.headers.update ({
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3112.113 Safari/537.36'
})

if token is None :
if api_key is None :
Color.printer("ERROR", "No API Key Provided!")
Color.printer("ERROR", "No API Key Provided!", gui)
sys.exit(0)

if api_key != "" and api_key != "insert_2captcha_api_key":
Color.printer("INFO", "Solving recaptcha...")
Color.printer("INFO", "Solving recaptcha...", gui)

token = get_token("https://9anime.to/waf-verify")
if not token :
Color.printer("ERROR", "Captcha solving failed! Exiting...")
sys.exit(0)
Color.printer("ERROR", "Captcha solving failed!", gui)
Color.printer("INFO", "Trying to continue using browser cookies...", gui)
# sys.exit(0)

if token:
verify(token)
else :
Color.printer("INFO", "Collecting browser cookies...", gui)
# cookies = bc.load() #collect all browser cookies
# session.cookies = cookies #set browser cookies for requests

Color.printer("INFO", "Extracting page URLs...")
Color.printer("INFO", "Extracting page URLs...", gui)

anime_page = session.get(download_9anime_url).content
soup_html = BeautifulSoup(anime_page, "html.parser")
Expand Down Expand Up @@ -143,10 +152,10 @@ def extract_page_urls(start_episode, end_episode, token) :
return episodes

def extract_download_urls() :
global session
global session, gui
down_base = "https://9anime.to/ajax/episode/info?"

Color.printer("INFO", "Extracting download URLs...")
Color.printer("INFO", "Extracting download URLs...", gui)
for episode in episodes :
if(episode.id is None) :
episode.download_url = None
Expand Down Expand Up @@ -191,9 +200,9 @@ def set_titles(start_episode, end_episode) :


def writeData() :
global episodes
global episodes, gui

Color.printer("INFO","Writing results to results.csv file...")
Color.printer("INFO","Writing results to results.csv file...", gui)
data_file = open("results.csv", "w")
for episode in episodes :
data_file.write(episode.episode+","+episode.download_url+"\n")
Expand All @@ -202,9 +211,12 @@ def writeData() :


def main(start_episode=-1, end_episode=-1, token = None) :
global episodes, download_9anime_url, episodes_url, api_key
global episodes, download_9anime_url, episodes_url, api_key, gui

start_episode = int(start_episode)
end_episode = int(end_episode)

if not ts_no :
if not token :
with open("settings.json") as (json_file):
data = json.load(json_file)
api_key = data["api_key"]
Expand All @@ -228,8 +240,8 @@ def main(start_episode=-1, end_episode=-1, token = None) :
if title_url :
set_titles(start_episode, end_episode)
else :
Color.printer("INFO", "animefiller.com URL not provided to collect episode names...")
Color.printer("INFO", "Skipping collecting episode names...")
Color.printer("INFO", "animefiller.com URL not provided to collect episode names...", gui)
Color.printer("INFO", "Skipping collecting episode names...", gui)

extract_download_urls()

Expand Down
5 changes: 4 additions & 1 deletion Color.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,10 @@ class Color():
UNDERLINE = lambda x: '\u001b[4m' + str(x)
RESET = lambda x: '\u001b[0m' + str(x)

def printer(msg_type, msg) :
def printer(msg_type, msg, gui=None) :
if gui :
gui.gui_queue.put("["+msg_type+"] : "+msg)

if msg_type == "INFO" :
print(Color.YELLOW("[INFO!] : "+msg) + Color.RESET(" "))
elif msg_type == "ERROR" :
Expand Down
23 changes: 21 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# Anime Downloader [![Total Downloads](https://img.shields.io/github/downloads/Oshan96/Anime-Downloader/total.svg?style=flat-square)](https://github.com/Oshan96/Anime-Downloader/releases)
# Anime Downloader [![Total Downloads](https://img.shields.io/github/downloads/Oshan96/Anime-Downloader/total.svg?style=for-the-badge)](https://github.com/Oshan96/Anime-Downloader/releases)

There are two scripts (Anime_Downloader.py, Anime_Scraper.py) to download given anime to a directory and to extract direct download links.
Anime_Scraper.py scraper is used to collect and extract direct anime download links from 9anime.to (From its Mp4Upload server)
Expand All @@ -22,7 +22,26 @@ Open settings.json and set [2captcha](https://2captcha.com/) API key in "api_key

Navigate to the extracted folder and open a cmd or powershell window from that folder and execute "anime-dl.exe" from command line.

## How to download using anime-dl?
## How to download using GUI version (v0.1.1-alpha upwards)
It is same as the CLI version, but provided a graphical user interface to collect necessary parameters.

> Note : The GUI version is still in development and this is a pre-release. The code and execution methods will probably change in future
Execute the "anime-dl.exe" to start.

If you're running from source files, execute the "anime-dl.py" script

```bash
python3 ./anime-dl.py
```

And the GUI will appear as following :

![GUI](docs/images/gui.png)

#### Note : If you don't have a 2captcha API key, you need to [provide "Recaptcha Token" in the given text field](#Q---I-don't-have-a-2captcha-API-key,-is-there-any-workaround-for-that?)

## How to download using anime-dl (CLI)?

First of all, you need to be familiar with the commands you can use with the Anime Downloader.

Expand Down
9 changes: 9 additions & 0 deletions anime-dl.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import Anime_Downloader
import Anime_Scraper
import warnings
from queue import Queue
from gui.GUI import Anime_GUI

if __name__ == "__main__" :
warnings.filterwarnings("ignore")
Anime_GUI(Queue(), Anime_Downloader, Anime_Scraper).run()
Binary file added docs/images/gui.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
106 changes: 106 additions & 0 deletions gui/GUI.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
import queue
import PySimpleGUI as sg
from threading import Thread

sg.theme('Dark Amber')
downloader = None
scraper = None

def execute(downloader, scraper, start_epi, end_epi) :
scraper.main(start_epi, end_epi, downloader.token)
downloader.download()

class Anime_GUI() :

def __init__(self, gui_queue, downloader, scraper) :
self.gui_queue = gui_queue
self.downloader = downloader
self.scraper = scraper
self.window = None

def create_ui(self) :
layout = [

[sg.Text("General Details",size=(15,1)),sg.Text("_"*60, pad=(0,15))],
[sg.Text("Anime URL (9anime.to)", text_color="white", size=(25,1)), sg.InputText(key="anime_url")],
[sg.Text("Animefillerlist URL", text_color="white", size=(25,1)), sg.InputText(key="names_url")],
[sg.Text("Save To", size=(25,1), text_color="white"), sg.InputText(key="location"), sg.FolderBrowse()],

[sg.Text("Episodes Details",size=(15,1)),sg.Text("_"*60, pad=(0,15))],
[sg.Text("From", text_color="white"), sg.InputText(key="start_epi", size=(5,1)), sg.Text("To", text_color="white"), sg.InputText(key="end_epi", size=(5,1)), sg.Text("Download Filler Episodes?", text_color="white"), sg.Combo(["Yes", "No"], size=(4,1), default_value="Yes", key="isFiller"), sg.Text("Threads", text_color="white"), sg.Spin([i for i in range(1,21)],initial_value=1, size=(3,1), key="threads")],
[],

[sg.Text("Optional Settings (Fill this if you don't have 2captcha key)",size=(45,1)),sg.Text("_"*25, pad=(0,15))],
[sg.Text("Recaptcha Token (Optional)", text_color="white", size=(25,1)), sg.Multiline(size=(45, 4), key="token")],
[sg.Column([[sg.Button("Download", size=(10,1))]], justification="right", pad=(35,5))],
[],
[sg.Text("Messages")],
[sg.Multiline(size=(None, 8), key="txt_msg", disabled=True)],
[]
]

self.window = sg.Window("Anime Downloader v0.1.1-alpha", layout)

def check_messages(self, values) :
txt = values["txt_msg"].strip()
while True :
try: # see if something has been posted to Queue
message = self.gui_queue.get_nowait()
except queue.Empty: # get_nowait() will get exception when Queue is empty
break # break from the loop if no more messages are queued up
# if message received from queue, display the message in the Window
if message:
txt += "\n" + message
self.window['txt_msg'].update(txt)
# do a refresh because could be showing multiple messages before next Read
self.window.refresh()
# print(message)

def run(self) :
self.create_ui()
while True :
# wait for up to 100 ms for a GUI event
event, values = self.window.read(timeout=100)
if event in (None, 'Exit'):
break

if event == "Download" :
self.scraper.download_9anime_url = values["anime_url"]
self.scraper.title_url = values["names_url"]

if values["names_url"] != "" :
self.downloader.titles = True

if values["isFiller"] == "Yes":
self.scraper.isFiller = True
else :
self.scraper.isFiller = False

tok = values["token"].rstrip()

if tok != "":
self.downloader.token = tok

directory = values["location"]

if directory != "" :
directory = directory.replace("\\", "/")
if not directory.endswith("/") :
directory+="/"

self.downloader.directory = directory
self.downloader.threads = values["threads"]

self.scraper.gui = self
self.downloader.gui = self

self.window["txt_msg"].update("[INFO] : Download started!")
self.window.refresh()

thread = Thread(target=execute, args=(self.downloader, self.scraper, values["start_epi"], values["end_epi"]), daemon=True)
thread.start()

self.check_messages(values)

self.window.close()

Empty file added gui/__init__.py
Empty file.
3 changes: 2 additions & 1 deletion requirements.txt
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
art==4.5
requests==2.22.0
art==4.5
beautifulsoup4==4.8.2
PySimpleGUI==4.16.0

0 comments on commit af541d2

Please sign in to comment.