#!/usr/bin/env python import os, subprocess, sys, argparse, signal # required files CURRENT_DIR = os.getcwd() VERSION = "1.0" if sys.platform == "win32": CONFIG_FILE_PATH = os.path.join('C:\\', 'Users', str(os.getlogin()), 'Documents', 'Adl', 'config.json') DEFAULT_DOWNLOAD_LOCATION = os.path.join('C:\\', 'Users', str(os.getlogin()), 'Videos', 'Anime') CLEAR = 'cls' else: CONFIG_FILE_PATH = os.path.join('home', str(os.getlogin()), '.config', 'adl', 'config.json') DEFAULT_DOWNLOAD_LOCATION = os.path.join('home', str(os.getlogin()), 'Videos', 'Anime') CLEAR = 'clear' PROBLEMATIC_TITLES = ['Komi-san wa, Komyushou desu ', 'SK∞'] GOOD_TITLES = ['Komi-san wa, Komyushou desu.', 'SK∞'] # exit function def exit_adl(): sys.exit() # def interupt_command(signum, frame): exit_adl() # colored print def color_print(text): print(f"\033[0;36m{text } \033[0m") # colored watch primpt def watch_prompt(title, episode, msg): print(f"Now {msg} \033[0;34m{title}\033[0m, episode \033[0;34m{str(episode)} \033[0m") # colored input def color_prommpt(text): return input(f"\033[0;34m{text}\033[0m") # retrieve new list def retrieve_list(account): color_print (f"Running trackma retrieve for account {account}...") subprocess.run(["trackma", "-a", account, "retrieve"]) subprocess.run(CLEAR) # retrieve updated list def retrieve_list_update(account): color_print(f"Running trackma retrieve for account {account} to get the updated list...") subprocess.run(["trackma", "-a", account, "retrieve"]) subprocess.run(CLEAR) # load list def load_list(account): alist = subprocess.run(["trackma", "-a", account, "list"], stdout=subprocess.PIPE).stdout.decode('utf-8').splitlines() alist.pop(0) alist = alist[: len(alist) - 3] alist = "\n".join(alist) return alist # exit prompt def exit_ask(): while True: subprocess.run(CLEAR) choice = color_prommpt("Want to watch another anime? [Y/n]: ") if choice == "N" or choice == "n": exit_adl() elif choice == "Y" or choice == "y" or choice == "": return # check for problematic title def check_title(title): for problem in PROBLEMATIC_TITLES: if problem == title: title = GOOD_TITLES[PROBLEMATIC_TITLES.index(problem)].encode('utf-8') title = title.decode('utf-8') return title # get chosen anime info def get_info(choice): # get index index = int(choice[4:7]) # get title title = str(choice[9:-19]) title = title.replace('.','') title = check_title(title) # get episode episode = int(choice[-19:-15]) # get score score = int(choice[-10:-5]) # print(f'/{title}/') # exit_adl() return index, title, episode, score # watch animes def watch(title, episode, download, provider, download_location): cmd = ['animdl'] if download: cmd.append('download') if os.path.isdir(download_location): os.chdir(download_location) else: os.mkdir(download_location) os.chdir(download_location) else: cmd.append('stream') cmd.append(f'"{provider}:{title}"') cmd.append('-r') cmd.append(episode) subprocess.run(cmd) if download: os.chdir(CURRENT_DIR) # next episode def next_episode(title,episode, msg, download, provider, download_location): if not download: watch_next = True while watch_next: episode = episode + 1 watch_prompt(title, str(episode), msg) watch(title, str(episode), download, provider, download_location) while True: color_print(f"Current watched episode: {str(episode)}") yn = color_prommpt("Wanna watch next episode? [Y/n]: ") if yn == "Y" or yn == "y" or yn == "": break elif yn == "N" or yn == "n": watch_next = False break else: episode = episode + 1 watch_prompt(title, str(episode), msg) watch(title, str(episode), download, provider, download_location) # all from last watched def all_from_last(title,episode, msg, download, provider, download_location): watch_prompt(title, f"{str(episode)} all left episodes", msg) watch(title, f'{str(episode + 1)}:', download, provider, download_location) # all episode def all_episodes(title, msg, download, provider, download_location): watch_prompt(title, "all", msg) watch(title, '1:', download, provider, download_location) # watch from custom range def custom_episode_range(title, msg, download, provider, download_location): begginig = color_prommpt("Beggining of interval?: ") end = color_prommpt("End of interval?: ") watch_prompt(title, f"{begginig} to {end}", msg) watch(title, f"{begginig}:{end}", download, provider, download_location) # add to last watched m def next_plus_n(title, episode, action, msg, download, provider, download_location): watch_prompt(title, str(episode + int(action)), msg) watch(title, str(episode + int(action)), download, provider, download_location) # rewatch current episode def rewatch_episode(title, episode, msg, download, provider, download_location): watch_prompt(title, str(episode), msg) watch(title, str(episode), download, provider, download_location) # watch custom episode def custom_episode(title, msg, download,provider, download_location): episode = color_prommpt("Enter custom episode: ") watch_prompt(title, episode, msg) watch(title, episode, download, provider, download_location) # update title def update_title(index, title, episode, account): color_print(f"Current episode for {title} is {str(episode)}") custom = color_prommpt("Enter updated episode number: ") if custom != "": subprocess.run(['trackma', '-a', account, 'update', str(index), custom]) subprocess.run(['trackma', '-a', account, 'send']) retrieve_list_update(account) else: color_print("Skipping updating...") # update score def update_score(index, title, score, account): color_print(f"Current score for {title} is {score}") custom = color_prommpt("Enter updated score: ") if custom != "": subprocess.run(['trackma', '-a', account, 'score', str(index), custom]) subprocess.run(['trackma', '-a', account, 'send']) retrieve_list_update(account) else: color_print("Skipping updating...") # update question def update_question(index, title, episode, score, account): while True: color_print("Skipping watching episodes. Modifing entry.") choice = color_prommpt("Update episode number or update score [E/s]: ") if choice == "e" or choice == "E" or choice == "": update_title(index, title, episode, account) break elif choice == "s" or choice == "S": update_score(index, title, score, account) break # ask if you wanna continus watching def wanna_continu_watch(download): while True: if not download: yn = color_prommpt("Wanna continue watching? [Y/n]: ") else: yn = color_prommpt("Wanna continue downloading? [Y/n]: ") if yn == "y" or yn == "Y" or yn == "": return True elif yn == "n" or yn == "N": return False # ask if you wanna update title meta after watch def wanna_update_title_after_watch(index, title, episode, score, download, account): if not download: while True: yn = color_prommpt("Wanna update episode number or update score of watched anime? [N/e/s]: ") if yn == "E" or yn == "e": update_title(index, title, episode, account) break elif yn == "S" or yn == "s": update_score(index, title, score, account) break elif yn == "N" or yn == "n" or yn == "": break # choose what to do with episode def choose_episode(): subprocess.run(CLEAR) color_print("Enter lowercase or uppercase to issue command:") color_print(" N - Next episode (default, press )") color_print(" L - from current to Last known:") color_print(" A - All available, from episode 1") color_print(" I - custom Interval (range) of episodes") color_print(" 0-9 - Plus n episodes relative to last seen (type number)") color_print(" R - Rewatch/redownload current episode in list") color_print(" C - Custom episode") color_print(" U - Update entry chosen instead of streaming") color_print(" S - Skip. Choose another show.") return color_prommpt("Your choice? [N/l/a/i/0-9/r/c/u/s]: ") def choose_episode_specific_show(): subprocess.run(CLEAR) color_print("Enter lowercase or uppercase to issue command:") color_print(" A - All available, from episode 1") color_print(" I - custom Interval (range) of episodes") color_print(" C - Custom episode") color_print(" S - Skip. Exit adl.") return color_prommpt("Your choice? [A/i/c/s]: ") def argument_and_config_parser(): # config config = {} ConfigExists = False if os.path.exists(CONFIG_FILE_PATH): config_content = open(CONFIG_FILE_PATH, encoding='utf-8-sig').read() config = eval(config_content) ConfigExists = True # argument parser ap = argparse.ArgumentParser() ap.add_argument("-p", "--provider", required=False, help="Define provider used for streaming (check \033[0;36m$anime dl --help\033[0m for providers list)") ap.add_argument("-s", "--show", required=False, help='Watch custom show. Ep nr optional, careful with the quotes. Ex: \033[0;36m$adl -s "gegege 2018"\033[0m') ap.add_argument("-n", "--number", required=False, help='Specify episode number that will be used with "-s / --show" option. Ex: \033[0;36m$adl -s "gegege 2018" -n "4"\033[0m') ap.add_argument("-a", "--account", required=False, help="By default trackma will use account 1. Use '-a 2' for example to change trackma account") ap.add_argument("-d", "--download", required=False, type=bool, nargs='?', const=True, default=False, help="Download instead of streaming") ap.add_argument("-l", "--download-location", required=False, help="Define downloads location, Default location is in 'User folder/Videos/Anime'") ap.add_argument("-t", "--test-providers", required=False, type=bool, nargs='?', const=True, default=False, help="Check the state of possible providers") ap.add_argument("-v", "--version", required=False, nargs='?', const=True, help="Display version and exit") args = vars(ap.parse_args()) # print the version if args["version"]: print(f"Py-adl version {VERSION}") sys.exit() # check if providers are working if args["test_providers"]: subprocess.run(["animdl", "test"]) sys.exit() # get provider if args['provider']: provider = str(args["provider"]) elif ConfigExists and 'provider' in config and config['provider']: provider = str(config["provider"]) else: provider = "zoro" # get show if args['show']: show = str(args["show"]) else: show = "" # get episode if args['number']: if args['number'] and args['show']: episode = int(args['number']) else: print("You need to also specify a show name to use this option") sys.exit() else: episode = 0 # get account if args['account']: account = str(int(args["account"]) - 1) # take the account from input elif ConfigExists and 'account' in config and config["account"]: account = str(int(config["account"]) - 1) # take the account from config else: account = "0" # default account # enable downloading if args["download"]: download = True # enable downloading msg = "downloading" # download message else: download = False # specify whether to download or not msg = "watching" # msg for the watch prompt if not download and args["download_location"]: color_print("You need to be downloading to use this option!") exit_adl() elif download and args["download_location"]: download_location = str(args["download_location"]) else: download_location = DEFAULT_DOWNLOAD_LOCATION return (provider, show, episode, account, download, msg, download_location) def specific_show_loop(show, msg, download, provider, download_location): while True: # choose what to do with the choosen anime action = choose_episode_specific_show() if action == "a" or action == "A" or action == "": all_episodes(show, msg, download, provider, download_location) exit_adl() # custom range of episodes elif action == "i" or action == "I": custom_episode_range(show, msg, download, provider, download_location) if wanna_continu_watch(download): continue else: exit_adl() # watch custom episode elif action == "c" or action == "C": custom_episode(show, msg, download, provider, download_location) if wanna_continu_watch(download): continue else: exit_adl() # skip the anime elif action == "s" or action == "S": exit_adl() def main_loop(retrieve, account, msg, download,provider, download_location): # main loop while True: # retrieving the list on start if retrieve: retrieve_list(account) retrieve = False # get the list of anime alist = load_list(account) # get choice from fzf choice = subprocess.run(["fzf", "--ansi", "--unicode", "--reverse", "--prompt", "Choose anime to watch: "], input=alist, stdout=subprocess.PIPE, encoding='utf-8').stdout if choice: # get needed info index, title, episode, score = get_info(choice) # the watch loop while True: # choose what to do with the choosen anime action = choose_episode() # watch next episode if action == "n" or action == "N" or action == "": next_episode(title, episode, msg, download,provider, download_location) wanna_update_title_after_watch(index, title, episode, score, download, account) exit_ask() break # watch all left episodes elif action == "l" or action == "L": all_from_last(title, episode, msg, download,provider, download_location) wanna_update_title_after_watch(index, title, episode, score, download, account) exit_ask() break # watch every episode available elif action == "a" or action == "A": all_episodes(title, msg, download,provider, download_location) wanna_update_title_after_watch(index, title, episode, score, download, account) exit_ask() break # custom range of episodes elif action == "i" or action == "I": custom_episode_range(title, msg, download,provider, download_location) if wanna_continu_watch(download): continue else: wanna_update_title_after_watch(index, title, episode, score, download, account) exit_ask() break # something? elif action == "1" or action == "2" or action == "3" or action == "4" or action == "5" or action == "6" or action == "7" or action == "8" or action == "9": next_plus_n(title, episode, action, msg, download,provider, download_location) if wanna_continu_watch(download): continue else: wanna_update_title_after_watch(index, title, episode, score, download, account) exit_ask() break # rewatch current episode elif action == "r" or action == "R": rewatch_episode(title, episode, msg, download,provider, download_location) if wanna_continu_watch(download): continue else: wanna_update_title_after_watch(index, title, episode, score, download, account) exit_ask() break # watch custom episode elif action == "c" or action == "C": custom_episode(title, msg, download,provider, download_location) if wanna_continu_watch(download): continue else: wanna_update_title_after_watch(index, title, episode, score, download, account) exit_ask() break # update anime meta elif action == "u" or action == "U": update_question(index, title, episode, score, account) exit_ask() break # skip the anime elif action == "s" or action == "S": break else: exit_ask() def main(): signal.signal(signal.SIGINT, interupt_command) # print("Inside") # setup env variables for better readability of outputs os.environ['LINES'] = '25' os.environ['COLUMNS'] = '120' os.environ['PYTHONIOENCODING'] = 'utf-8' # get argument and config parameters (provider, show, episode, account, download, msg, download_location) = argument_and_config_parser() # retrieve the trackma list on run retrieve = True if not show == "" and not episode == 0: # watching just a specific show and episode only watch(show, str(episode), download, provider, download_location) elif not show == "": # choose want to do with a specific show specific_show_loop(show, msg, download, provider, download_location) else: # main loop that connets with your list with trackma main_loop(retrieve, account, msg, download,provider, download_location) exit_adl() # run only if runned directly if __name__ == "__main__" : main()