Last active
January 14, 2016 17:51
-
-
Save ozodrukh/b7a12b0b9255d6ac5525 to your computer and use it in GitHub Desktop.
Player.uz script to grab and open direct link to serial or download sequentially by seasons and episodes
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# coding: utf8 | |
import re, json, bs4, requests, argparse, os, optparse | |
from urlparse import parse_qs, urlsplit, urlparse | |
ALL = -100 | |
KEY_EPISODE_NUMBER = "episode" | |
KEY_EPISODE_SEASON = "season" | |
KEY_EPISODE_FILE = "file" | |
URL_PATTERN = re.compile( | |
ur'https?:\/\/(www\.)?[-a-zA-Z0-9@:%._\+~#=]{2,256}\.[a-z]{2,6}\b([-a-zA-Z0-9@:%_\+.~#?&//=]*)') | |
class PlayerInterface(object): | |
def __init__(self, *args): | |
""" | |
Pass configuration tuple | |
:param args: Configurations | |
""" | |
pass | |
def get_episodes_url(self, url, season_list=ALL, episodes_list=ALL): | |
self.__init_internal_fields(url) | |
urls = [] | |
seasons = None | |
# convert to list if is any other integer value except ALL constant | |
episodes_list = [episodes_list] if type(episodes_list) is int and not episodes_list == ALL else episodes_list | |
if type(season_list) is int: # exact season | |
seasons = [ | |
(value for value in self.filesMap.itervalues()) if season_list is ALL else self.filesMap[season_list]] | |
elif type(season_list) is list: | |
seasons = [value for key, value in self.filesMap.iteritems() if key in season_list] | |
for season in seasons: | |
urls.extend([value for key, value in season.iteritems() if episodes_list == ALL or key in episodes_list]) | |
return urls | |
def __init_internal_fields(self, url): | |
javascript_code = self.__fetch_necessary_part(url) | |
self.json = json.loads(self.__find_episodes_json_in_code(javascript_code)) | |
self.endpoint = self.__find_endpoint_in_code(javascript_code) | |
self.filesMap = self.__build_episodes_map(self.json) | |
def __build_episodes_map(self, json_array): | |
""" | |
Makes dictionary object with outer dict object as season and | |
inner dictionary with episodes list | |
:json: json of episodes & seasons | |
:return: dictionary of seasons(outer) and episodes(inner) in json | |
""" | |
seasons_list = {} | |
for json_object in json_array: | |
season = int(json_object[KEY_EPISODE_SEASON]) | |
if not season in seasons_list: | |
seasons_list[season] = {} | |
url = (self.endpoint + json_object[KEY_EPISODE_FILE]) | |
if not hasattr(self, "suggested_folder"): | |
episode_remote_path = os.path.split(urlparse(url).path) | |
seasons_list[season][int(json_object[KEY_EPISODE_NUMBER])] = url | |
return seasons_list | |
@staticmethod | |
def __find_endpoint_in_code(javascript_code): | |
""" | |
Searches for URL Pattern and returns first matched url, | |
it will be used as endpoint for further | |
:param javascript_code: part of code where js with episodes json | |
(10th script in human numeric system) | |
:return: base url to concatenate with file part url | |
""" | |
return re.search(URL_PATTERN, javascript_code).group() | |
@staticmethod | |
def __find_episodes_json_in_code(javascript_code): | |
""" | |
We are parsing specific part of code, so we need to find | |
json with enumerated episodes | |
:param javascript_code: part of code where js with episodes json | |
(10th script in human numeric system) | |
:return: json with episodes in string type | |
""" | |
return javascript_code[str.find(javascript_code, "[{"): str.rfind(javascript_code, "}]") + 2] | |
@staticmethod | |
def __fetch_necessary_part(url): | |
""" | |
Current implementation: | |
Takes html from url and finds 10th script and returns it | |
:param url: serial url | |
:return: (str) 10th script where json with enumerated episodes | |
""" | |
r = requests.get(url) | |
if not 200 <= r.status_code < 300: | |
raise requests.HTTPError(request=r) | |
html = bs4.BeautifulSoup(r.text, "html.parser") | |
return str(html.find_all("script")[9].string) | |
@staticmethod | |
def find_season_and_episode_in_query(url, season, episode): | |
""" | |
Makes dictionary of query: value | |
""" | |
query = parse_qs(urlsplit(url).query) | |
return int(query[KEY_EPISODE_SEASON][0]) if KEY_EPISODE_SEASON in query else season, \ | |
int(query[KEY_EPISODE_NUMBER][0]) if KEY_EPISODE_NUMBER in query else episode | |
@staticmethod | |
def suggest_folder(url, local_folder_base="~/Movies/"): | |
episode_remote_path = os.path.split(urlparse(url).path) | |
remote_dir = episode_remote_path[0] | |
remote_dir = remote_dir[len("/mp4/"):len(remote_dir)] | |
return os.path.join(os.path.expanduser(local_folder_base), remote_dir) | |
def main(): | |
SCRIPT_DESCRIPTION = "Enjoy player.uz without paying subscription even if you are not TPS user." | |
ARG_URL_HELP = "We support 2 types of url normal http://player.uz/{serial_id}/ where {serial_id} is " \ | |
"number in real life and with season and episode in query that will be passed as argument to watch" | |
ARG_SEASONS_HELP = "You can pass as many number as you want separating them by space " \ | |
"for example (season 1 2 3 4 5) or just a number, if you not set this argument " \ | |
"it means all seasons" | |
parser = argparse.ArgumentParser(description=SCRIPT_DESCRIPTION) | |
parser.add_argument('URL', type=str, help=ARG_URL_HELP) | |
parser.add_argument('-s', type=int, default=ALL, nargs='*', required=False, help=ARG_SEASONS_HELP) | |
parser.add_argument('-e', type=int, default=ALL, nargs='*', required=False, | |
help="list of episodes enumerated by space to watch") | |
parser.add_argument("-info", action='store_true', default=False) | |
parser.add_argument("-save", action='store_true', default=False) | |
args = parser.parse_args() | |
print(args) | |
api = PlayerInterface() | |
seasons, episodes = api.find_season_and_episode_in_query(args.URL, args.s, args.e) | |
urls = api.get_episodes_url(args.URL, seasons, episodes) | |
commands = [] | |
if args.save: | |
for url in urls: | |
commands.extend([ | |
"wget","-c", | |
"-P", api.suggest_folder(url), | |
url | |
]) | |
os.system(" ".join(commands)) | |
elif len(urls) is 1: | |
commands.extend(["open", "\"{0}\"".format(urls[0])] if len(urls) is 1 else []) | |
os.system(" ".join(commands)) | |
else: | |
for url in urls: print(url) | |
if __name__ == "__main__": | |
main() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment