· 7 years ago · Jul 03, 2018, 08:02 AM
1#!/usr/bin/env python3
2# -*- coding: utf-8 -*-
3
4""" Author: abhiigatty@gmail.com
5 Feel free to reuse the code just don't blame me
6 and support Anime artist by purchasing their merch and swags """
7
8import requests, json, validators, re
9from clint.textui import colored
10from pathlib import Path
11from tqdm import tqdm
12from multiprocessing.dummy import Pool as ThreadPool
13from Crypto.Cipher import AES
14from prettytable import PrettyTable
15from urllib.parse import urlparse, parse_qs
16from download import download_from_url
17
18
19"""Function to download a file and show its progress"""
20def download_from_url(url):
21 # Get file size in bytes
22 file_size = int(requests.head(url).headers['Content-Length'])
23 file_path = Path(filename)
24 if file_path.exists():
25 first_byte = file_path.stat().st_size
26 else:
27 first_byte = 0
28 if first_byte >= file_size:
29 return
30 header = {"Range": "bytes=%s-%s" % (first_byte, file_size)}
31 pbar = tqdm(
32 total=file_size, initial=first_byte,
33 unit='B', unit_scale=True, desc=filename)
34 req = requests.get(url, headers=header, stream=True)
35 with(open(filename, 'ab')) as f:
36 for chunk in req.iter_content(chunk_size=1024):
37 if chunk:
38 f.write(chunk)
39 pbar.update(1024)
40 pbar.close()
41
42""" Function to delete a directory """
43def delete_folder(dir_path) :
44 if dir_path.exists():
45 for sub in dir_path.iterdir() :
46 if sub.is_dir() :
47 delete_folder(sub)
48 else :
49 sub.unlink()
50 dir_path.rmdir()
51
52""" Function used to decrypt the cipher using a key"""
53def decrypt_text(secret_key):
54 # URL encrypted to bytes-string with a secret key
55 cipher_url = b'An\xc77>^(\x00a\xbb\xde6O\xf1syh\xb6\xab\x9c\x94CF\xff\x8f\xb8}\xc5\x81Q8\xafd\x81$\x90\xe5\xbeM"\xbdO\xdff\xd7\xf23\xe4'
56 try:
57 # Creating an object to use decrypt method
58 lock_obj = AES.new(secret_key, AES.MODE_CFB, "is a 16 byte key")
59 # URL decrypted and convert to string
60 plain_url = str(lock_obj.decrypt(cipher_url), "utf-8")
61 return plain_url
62 except:
63 return "invalid key"
64
65""" Function for a 'yes or no' option selector """
66def yes_or_no(question): # Include '?' in the question string
67 # Input returns the empty string for "enter"
68 yes = ('yes','y', 'ye', '')
69 no = ('no','n')
70 # Input choice
71 choice = input(question + colored.green(" [Y]") + "es or " + colored.red("[N]") + "o: ").lower()
72 while True:
73 # If choice not either yes or no then ask input again
74 if choice not in yes + no:
75 choice = input("Please Choose " + colored.green("[Y]") + "es or " + colored.red("[N]") + "o: ").lower()
76 continue
77 if choice in yes:
78 return True
79 elif choice in no:
80 return False
81
82""" Function to get a web page content """
83def download_data(url):
84 try:
85 print(">>> "+colored.cyan("Downloading data...")+": "+colored.yellow(url))
86 data = requests.get(url)
87 return data
88 except requests.exceptions.RequestException as err:
89 print(err)
90 return "invalid"
91
92""" Function to download and save json data in a file """
93def web_to_json(data_url, dir_path="."):
94 data = download_data(data_url)
95 try:
96 data_from_json = data.json()
97 file_name = Path(data_url).parts[-1]
98 file_path = dir_path / file_name
99 with file_path.open(mode="w", encoding ="utf-8") as fh:
100 json.dump(data_from_json, fh, indent=4)
101 print(">> "+colored.cyan("Written to file")+": "+colored.green(file_path))
102 return file_name
103 except ValueError:
104 print("URL file not in json format!")
105 exit()
106
107""" Prepare the multiple-parameters for multiprocessing """
108def multi_run_wrapper(args):
109 return get_anime_episode_urls(*args)
110
111""" Gets the media download url """
112def get_anime_episode_urls(url, data):
113 links = requests.post(url, data=data)
114 try:
115 links = list(links.json().values())
116 while "none" in links: links.remove("none")
117 episode_download_url = links[0]
118 print(colored.yellow(">> URL")+": "+episode_download_url)
119 return episode_download_url
120 except:
121 print("Failed: {}".format(data))
122 exit()
123
124""" Gets the animes urls and saves it """
125def getAnime(main_url):
126 file_details = {}
127 main_data = download_data(main_url).json()[0] # Get json data and convert returned data to dict from list
128
129 # Create the directory if it does not exist
130 dir_name = "getanime_temp_json"
131 Path(dir_name).mkdir(exist_ok=True)
132 dir_path = Path(dir_name)
133
134 print("=> "+colored.cyan("Fetching Database, Please wait...."))
135 # Download the json files
136 for key, value in main_data.items():
137 # Ignore other files for downloading
138 if Path(value).suffix == ".php": # The php file url is required for fetching further information
139 php_url = value
140 print(">> "+colored.cyan("DRIVER_URL")+": "+colored.green(value))
141 continue
142 if validators.url(value): # If url is invalid then do nothing otherwise process it
143 file_name = web_to_json(value, dir_path)
144 file_size = (dir_path / file_name).stat().st_size # Get file size in bytes
145 file_details[file_name] = file_size # Create a dict of file_name and it"s size
146 else:
147 pass
148
149 driver_file = max(file_details, key=file_details.get) # We"ll use the json file with max size
150 file_path = dir_path / driver_file # Build the path of the json file
151
152 # Load json file contents into a dict for faster access
153 with file_path.open(mode="r", encoding ="utf-8") as fh: # Load json from file
154 anime_details = json.load(fh)
155 anime_names = []
156 for anime in anime_details:
157 anime_names.append(anime["Title"])
158
159 # Loop until user wants to stop
160 while True:
161 # Loop until a valid anime title is found
162 anime_to_download = input(">> "+colored.cyan(colored.cyan("Enter Anime Title or [quit]"))+": ")
163 if anime_to_download in ["quit","q","exit"]:
164 return
165 print(colored.red("Searching Keyword: ") + colored.yellow(anime_to_download))
166 regex = re.compile(".*"+anime_to_download+".*", re.IGNORECASE) # Create the regular expression to find the string
167 list_of_result = list(filter(regex.match, anime_names))
168
169 # Start loop again if no animes found
170 if len(list_of_result) is 0:
171 print("=> "+colored.red("No match found! ")+colored.cyan("Try another title"))
172 continue
173
174 # Display the anime titles to choose from in a table
175 table = PrettyTable(['No.', 'Title'])
176 anime_list = {}
177 for pos, anime_name in enumerate(list_of_result, start=1):
178 table.add_row([pos, anime_name])
179 anime_list[pos] = anime_name
180 table.add_row([pos+1, "<<< Exit <<<"])
181 print(table)
182
183 while True:
184 choice = int(input(">> "+colored.cyan("Choose a number")+": "))
185 if anime_list[choice] == "<<< Exit <<<":
186 return
187 elif choice in anime_list.keys():
188 final_anime_choice = anime_list[int(choice)]
189 break
190 else:
191 print(colored.red("Error")+": "+colored.cyan("Invalid! ")+"Please Try Again!")
192
193 # Find the anime download page link
194 for anime in anime_details:
195 if anime["Title"] is final_anime_choice:
196 download_link = anime["Link"]
197 break
198 print(">> "+colored.cyan("Anime Page: "+colored.yellow(download_link)))
199
200 # Find details about anime and download links to each episode
201 data = {}
202 data["Episodes"] = download_link
203 reply = requests.post(php_url, data=data)
204 animes_epi_links = reply.json()["episodes"]
205 no_of_episodes = len(animes_epi_links)
206 print(colored.cyan(final_anime_choice)+colored.cyan(" has ")+colored.green(no_of_episodes)+colored.cyan(" episodes"))
207
208 # Find the find anime media download links
209 list_of_data = []
210 final_links_list = []
211 for anime in animes_epi_links:
212 data = {}
213 data["LinkIos"] = anime["href"]
214 list_of_data.append((php_url, data))
215
216
217 # Make the Pool of workers for multiprocessing
218 # thread_count = int(no_of_episodes/2)
219 pool = ThreadPool(25)
220 # Open the urls in their own threads and return the results
221 final_links_list = pool.map(multi_run_wrapper, list_of_data)
222 # close the pool and wait for the work to finish
223 pool.close()
224 pool.join()
225
226 # Create a file with all the links
227 file_name = str(final_anime_choice.replace(" ","_")+"_getanime.txt")
228 with open(file=file_name, mode="w", encoding ="utf-8") as fh:
229 fh.write("\n".join(final_links_list))
230 print("=> "+colored.cyan("File created: ")+colored.green(file_name))
231
232 # Ask if user wants to run again
233 if yes_or_no(question="\nFind another anime?"):
234 pass
235 else:
236 return
237
238""" Main function that checks the key and removes temp dirs """
239def main():
240 secret_key = str(input("Enter Secret Key: ")).lower()
241 try:
242 project_url = decrypt_text(secret_key)
243 # Check if the decrypted url is in a valid url structure
244 if validators.url(project_url):
245 print(colored.green("Welcome, glad to have you back!"))
246 getAnime(main_url=project_url)
247 else:
248 print(colored.red("Error: ")+colored.cyan("Have a nice day!"))
249 except:
250 # Is executed if the getAnime function has some problems in it
251 print(colored.red("Error: ")+colored.yellow("I see smoke somewhere! "))
252
253 # Clean up the temp directories if created
254 temp_dir = Path("getanime_temp_json")
255 delete_folder(temp_dir)
256 print("=> "+colored.cyan("GoodBye! "))
257
258# Run the code below only if this is executed as a program and not imported as module
259if __name__ == "__main__":
260 main()