def processing_file(cfg, args): ## Check the file type file_dst = None if any(file_type in args.root_host.split(os.sep) for file_type in cfg.series): infomsg("Type: Episode", "Postprocessing") file_dst = naming_episode(args, cfg) elif any(file_type in args.root_host.split(os.sep) for file_type in cfg.movies): infomsg("Type: Movie", "Postprocessing") file_dst = naming_movie(args) else: exit("Error: Unsupported type of converted file") ## Delete the corresponding convert and watch file and add to synoindex if file_dst: msg = "Add converted file to synoindex and {} original file".format(switch_original(cfg.original)) infomsg(msg, "Postprocessing") client(args.scope, cfg.port, file_dst, args.output_host, args.source_host, cfg.original) watch_file = scope_reverse_map_path(cfg, args, args.watch_host) debugmsg("Delete watch file", "Postprocessing", (watch_file,)) try: os.remove(watch_file) except FileNotFoundError: errmsg("Could not find watch file at", "Postprocessing", (watch_file,)); exit() debugmsg("Delete convert file", "Postprocessing", (args.convert_path,)) os.remove(args.convert_path)
def movie_get_releases(args): """ Get all new "releases" sorted by their category for the passed interval. All changelog files of mounts are filtered by the date. Arguments: args {Namespace} -- Namespace containing all arguments. Returns: list -- List of categories and their items. """ ## Get all mounts of the main volume volume_path = os.path.join(os.sep, "volume1") mounts = [m for m in os.listdir(volume_path) if isdir(join(volume_path, m))] mounts = [join(volume_path, m) for m in mounts if "@" not in m] since = datetime.today() - timedelta(days=args.interval) since = since.replace(hour=0, minute=0, second=0, microsecond=0) ## Get all items per category (mount) items, invalid = ([] for _ in range(2)) for mount in mounts: changelog = os.path.join(mount, "changelog.txt") if not os.path.isfile(changelog): invalid.append(mount) continue with open(changelog, 'r') as f: cat_items = f.readlines() cat_items = [i.replace('\n', '').split(',') for i in cat_items] cat_items = [i[1] for i in cat_items if datetime.strptime(i[0], "%Y-%m-%d") > since] cat_items = sorted(list(set(cat_items))) if cat_items: items.append((mount, cat_items)) debugmsg("Releases for mount", "Mounts", (mount, ','.join(cat_items))) infomsg("Skipped following mounts due to no changelog file", "Mounts", (','.join(invalid),)) return sorted(items)
def delete_single_mode(args): ## Connect to Video Station database conn, cur = connect() if (conn == None): exit(-1) debugmsg("Connected successfully to Synology VideoStation database", args.mode) ## Get the userID of the passed username user_id = users_get_userid(args.user) if (user_id == None): close_connection(conn, cur) exit(-1) debugmsg("Get the UserID of the passed user", args.mode) ## Get the collection information collection_info = get_collection(cur, args.playlist, user_id) if (collection_info == None): close_connection(conn, cur) exit(-1) debugmsg("Get the playlist information", args.mode) ## Delete all items of a collection delete_all_items_of_collection(conn, cur, collection_info) debugmsg("Delete all items of the playlist", args.mode) ## Delete the collection itself delete_collection(conn, cur, collection_info) debugmsg("Delete the playlist itself", args.mode) ## Close connection close_connection(conn, cur)
def post_processing(args, cfg): """ Post processing. Arguments: args {Namespace} -- Namespace containing all shell arguments. cfg {Namespace} -- Namespace containing all configurations. """ ## Initialize the logging init_logging(args, cfg) debugmsg("-" * 35, "Postprocessing") ## If torrent is a single file create a directory and copy that file abs_path = files_fix_single(args) ## If there are RAR files extract them into the top directory files_unrar(abs_path, cfg.extensions) ## Import all non-compressed video files source_files = files_find_ext(abs_path, cfg.extensions) for source in source_files: (source_host, root_host, root) = scope_map_path(cfg, args, source) infomsg("Add source file to SynoIndex database", "Postprocessing", (source_host.split(os.sep)[-1], )) client(args.scope, cfg.port, source_host) ## Write changelog file for notification service write_changelog_file(source, source_host, root) ## Copy video file to handbrake if it's configured if (cfg.handbrake): for source in source_files: (source_host, root_host, _) = scope_map_path(cfg, args, source) copy_file_to_handbrake(args, cfg, source, source_host, root_host)
def client(scope, port, source_host, output_host=None, original_host=None, original_mode=0): ## Get the URL to the Syno-Index server and add the arguments url = client_get_url(scope, port) if not output_host: query_vars = {'source_host': source_host} else: query_vars = { "source_host": source_host, "output_host": output_host, "original_host": original_host } query_vars["original_mode"] = original_mode ## Call the url and get the answer of the server url = url + urlencode(query_vars) infomsg(" Source", "SynoClient", (source_host, )) infomsg(" Handbrake Output", "SynoClient", (output_host, )) infomsg(" Original", "SynoClient", (original_host, )) infomsg(" Original mode", "SynoClient", (original_mode, )) try: contents = urlopen(url).read() debugmsg("SynoIndex-Server answered with", "SynoClient", (contents.decode("UTF-8"), )) except urllib.error.URLError: errmsg( "Server is not started yet, start the Triggered Task with the right mode" ) exit()
def files_fix_single(args): """ If a single video file was downloaded create a directory and copy the file """ abs_path = os.path.join(args.directory, args.name) if os.path.isfile(abs_path): new_dir = directory_create_owner(args) abs_path = file_copy(abs_path, new_dir, args) debugmsg("Fixed single video file into directory", "Postprocessing", (new_dir, )) return abs_path
def init_notification_log(args): ## Initialize the logging if (args.log): cfg = argparse.Namespace() cfg.log_dir = args.log cfg.log_level = logging.DEBUG init_logging(args, cfg) else: init_logging(None, None) debugmsg("-" * 35, "Notification")
def delimiter_get(filename): ## Get the delimiter of the video filename delimiters = [".", "-", "_"] delimiter_count = Counter(filename).most_common() delimiter_count = [(key, val) for key, val in delimiter_count if key in delimiters] delimiter = sorted(delimiter_count, key=lambda x: x[1])[-1][0] debugmsg("Delimiter counts and the selected one", "Naming", (delimiter_count, delimiter)) return delimiter
def daemon_mode(args): debugmsg("Started execution", "Daemon mode") ## Connect to Video Station database conn, cur = connect() if (conn == None): errmsg("Could not connect to database") exit(-1) debugmsg("Connected successfully to Synology Video Station database", "Daemon mode") ## Get the admin ID users, admin = users_get_selection(1) ## Get all collection information of the admin admin_collections = get_collections_of_user(cur, admin[1]) if (admin_collections == None): close_connection(conn, cur) exit(-1) changes = False for user in users: ## Check whether user has all admin collections user_collections = get_collections_of_user(cur, user[1]) new_collections = daemon_collections_same(user_collections, admin_collections) for collection_info in new_collections: daemon_collection_add_to_user(conn, cur, collection_info, user) debugmsg("Add another playlist to the user", "Daemon mode", (user[0], collection_info[2])) changes = True ## Check whether all items in every collection is synced for collection_info in admin_collections: user_collection = [ uc for uc in user_collections if uc[2] == collection_info[2] ] if user_collection: user_collection = user_collection[0] if not daemon_items_same(conn, cur, user_collection, collection_info): delete_all_items_of_collection(conn, cur, user_collection) delete_collection(conn, cur, user_collection) daemon_collection_add_to_user(conn, cur, collection_info, user) debugmsg( "Renew playlist due to admin's playlist was updated", "Daemon mode", (user[0], collection_info[2])) changes = True if not changes: debugmsg("Finish daemon mode without any changes", "Daemon mode") return
def delete_all_mode(args): ## Connect to Video Station database conn, cur = connect() if (conn == None): exit(-1) debugmsg("Connected successfully to Synology VideoStation database", args.mode) ## Get admin and its ID admin_id = users_get_selection(2)[1] ## Get all corresponding collection information collection_info = get_collection(cur, args.playlist) if (collection_info == None): close_connection(conn, cur) exit(-1) collection_info = [c for c in collection_info if c[1] != admin_id] debugmsg("Get all playlist information", args.mode) for collection in collection_info: ## Delete all items of a collection delete_all_items_of_collection(conn, cur, collection) debugmsg("Delete all items of the playlist for user", args.mode, (collection[1], )) ## Delete the collection itself delete_collection(conn, cur, collection) debugmsg("Delete the playlist itself for user", args.mode, (collection[1], )) ## Close connection close_connection(conn, cur)
def write_changelog_file(source, source_host, root): """ Write the changelog file to pass the new releases for the notification service. Arguments: source {string} -- Path to the source within docker container. source_host {string} -- Path to the source on host system. root {string} -- Path to the top mount containing the file. """ ## Create changelog file path changelog_file = os.path.join(root, "changelog.txt") ## Write the convert file date = datetime.strftime(datetime.now(), "%Y-%m-%d") changelog_content = "{date},{source}\n".format(date=date, source=source_host) if os.path.isfile(changelog_file): with open(changelog_file, 'r') as f: lines = f.readlines() dupl = [ l for l in lines if l.split(",")[0] == date and l.split(",")[1] == source_host ] if (dupl): debugmsg("Item already exist in changelog file, skip it", "Postprocessing", (changelog_file, )) return debugmsg("Add item to changelog file", "Postprocessing", (changelog_file, )) else: debugmsg("Create changelog file and add item", "Postprocessing", (changelog_file, )) with open(changelog_file, 'a') as f: f.write(changelog_content)
def write_convert_file(cfg, source, source_host, root_host, output_host, watch_host): """ Write the convert file to pass necessary filesystem information to handbrake. Arguments: cfg {Namespace} -- Namespace containing all configurations. source {string} -- Path to the source within docker container. source_host {string} -- Path to the source file on the host system. root_host {string} -- Path to the top mount containing the file. output_host {string} -- Path to the output file of handbrake. watch_host {string} -- Path to the watch file of handbrake. """ ## Create convert file path convert_file = "%s.txt" % (".".join( os.path.basename(source).split(".")[:-1])) convert_file = os.path.join(cfg.handbrake, "convert", convert_file) ## Write the convert file convert_content = "root_host:{}\nsource_host:{}\noutput_host:{}\n" \ "watch_host:{}".format(root_host, source_host, output_host, watch_host) with open(convert_file, 'w+') as f: f.write(convert_content) debugmsg("Created convert file", "Postprocessing", (convert_file, ))
def copy_all_mode(args): ## Connect to Video Station database conn, cur = connect() if (conn == None): exit(-1) debugmsg("Connected successfully to Synology VideoStation database", args.mode) ## Get admin and its ID admin_id = users_get_selection(2)[1] ## Get the collection information collection_info = get_collection(cur, args.playlist, admin_id) if (collection_info == None): close_connection(conn, cur) exit(-1) debugmsg("Get the playlist information", args.mode) ## Get all users except the owner of the collection users_id = [u[1] for u in users_get_selection()] if (len(users_id) == 0): close_connection(conn, cur) exit(-1) debugmsg("Get all user information", args.mode) ## Get all items of the collection items = get_all_items_of_collection(cur, collection_info) if (items == None): close_connection(conn, cur) exit(-1) debugmsg("Get all items of the playlist", args.mode) for user in sorted(users_id): ## Create the new collection new_collection_id = create_new_collection(conn, cur, collection_info, str(user)) if (new_collection_id == None): close_connection(conn, cur) exit(-1) debugmsg("Inserted the new playlist successfully for user", args.mode, (user, )) ## Add all items to the new collection add_all_items_to_collection(conn, cur, items, new_collection_id) if (items == None): close_connection(conn, cur) exit(-1) debugmsg("Added all items to the new playlist for user", args.mode, (user, )) new_collection_id = None ## Close connection close_connection(conn, cur)
def copy_file_to_handbrake(args, cfg, source, source_host, root_host): """ Copy file to the handbrake watch directory and change owner. Arguments: args {Namespace} -- Namespace containing all shell arguments. cfg {Namespace} -- Namespace containing all configurations. source {string} -- Path to the source within docker container. source_host {string} -- Path to the source file on the host system. root_host {string} -- Path to the top mount containing the file. """ ## Get all media info about the file video_info = ffprobe_file(source) codec = video_info["video_codec"] resolution = video_info["resolutionY"] debugmsg("Analyse the video file for codec and resolution", "Mediainfo", (resolution, codec)) ## Check whether it is one codec of the config is present if codec not in cfg.codecs: infomsg("Codec is not watched in file", "Postprocessing", (source, codec)) return ## Only copy files which match no exclude string if any(exclude in source for exclude in cfg.exclude): infomsg("Source file excluded for handbrake by config", "Postprocessing", (source, )) return ## Switch the watch directory depending on the 4K mode and the resolution watch_host = os.path.join(cfg.handbrake, "watch") if (cfg.hb_4k == 1 and int(resolution) > 2000): infomsg("4K mode enabled - file is copied to separate watch directory", "Postprocessing", (watch_host, )) watch_host = os.path.join(cfg.handbrake, "watch2") if not os.path.isdir(watch_host): create_path_directories(watch_host) os.chown(watch_host, cfg.host_admin[0], cfg.host_admin[1]) ## Copy the video file to the handbrake watch directory infomsg("Copying file to handbrake watch directory", "Postprocessing", (watch_host, )) watch_file = file_copy(source, watch_host, args) if not watch_file: errmsg("Could not copy file to handbrake watch directory", "Postprocessing", (watch_host, )) return infomsg("Finished copying file", "Postprocessing", (watch_file, )) ## Convert the output file into host system path output_file = os.path.join(cfg.handbrake, "output", os.path.basename(watch_file)) output_host = scope_map_path(cfg, args, output_file)[0] if output_host == -1: errmsg("Could not get the host path of file", "Postprocessing", (output_file)) return ## Convert the watch file into host system path watch_file = os.path.join(watch_host, os.path.basename(watch_file)) watch_host = scope_map_path(cfg, args, watch_file)[0] if watch_host == -1: errmsg("Could not get the host path of watch file", "Postprocessing", (watch_file)) return ## Write the convert file with all necessary information write_convert_file(cfg, source, source_host, root_host, output_host, watch_host)
def copy_single_mode(args): ## Connect to Video Station database conn, cur = connect() if (conn == None): exit(-1) debugmsg("Connected successfully to Synology VideoStation database", args.mode) ## Get admin and its ID admin_id = users_get_selection(2)[1] ## Get the collection information collection_info = get_collection(cur, args.playlist, admin_id) if (collection_info == None): close_connection(conn, cur) exit(-1) debugmsg("Get the playlist information", args.mode) ## Get the userID of the passed username user_id = users_get_userid(args.user) if (user_id == None): close_connection(conn, cur) exit(-1) debugmsg("Get the UserID of the passed user", args.mode) ## Get all items of the collection items = get_all_items_of_collection(cur, collection_info) if (items == None): close_connection(conn, cur) exit(-1) debugmsg("Get all items of the playlist", args.mode) ## Create the new collection new_collection_id = create_new_collection(conn, cur, collection_info, user_id) if (new_collection_id == None): close_connection(conn, cur) exit(-1) debugmsg("Inserted the new playlist successfully", args.mode) ## Add all items to the new collection add_all_items_to_collection(conn, cur, items, new_collection_id) if (items == None): close_connection(conn, cur) exit(-1) debugmsg("Added all items to the new playlist", args.mode) ## Close connection close_connection(conn, cur)
def analyze_series(cfg, series): ## Get the extensions of the season series.file_base, series.extension = os.path.splitext(series.file) ## Analyze resolution of the season series.resolution = get_resolution(series.file) if (series.resolution == -1): errmsg("The resolution of the series episode was invalid", "Naming") exit() ## Analyze the current season number splitted = series.file_base.split(series.delim) try: debugmsg("Check for regular naming scheme (s01e01)", "Naming") season_ep = [ f for f in splitted if "s0" in f.lower() or "s1" in f.lower() ][0] series.season = "{}".format(re.split('s|e', season_ep.lower())[1]) series.episode = "S%sE%s" % (series.season, re.split('s|e', season_ep.lower())[2]) series.season = "%s %s" % (get_season_desc(cfg), series.season) except IndexError: debugmsg( "Regular naming scheme not found, check for alternative naming scheme (101|1201|122324)", "Naming") season_ep = splitted[-1] if (season_ep.isdigit()): if (int(season_ep) > 100 and int(season_ep) < 400000): ## Single episode - 101, 923 if (int(season_ep) > 100 and int(season_ep) < 1000): series.season = "{:02d}".format(int(season_ep[0])) series.episode = "S{}E{}".format(series.season, season_ep[1:]) ## Single episode - 1001,9023 elif (int(season_ep) > 1000 and int(season_ep) < 4000): series.season = "{:02d}".format(int(season_ep[:2])) series.episode = "S{}E{}".format(series.season, season_ep[-2:]) ## Double episode - 100102, 12324, 92324 elif (int(season_ep) > 10000 and int(season_ep) < 40000): series.season = "{:02d}".format(int(season_ep[0])) series.episode = "S{}E{}".format(series.season, season_ep[1:3]) ## Double episode - 122324 elif (int(season_ep) > 100000 and int(season_ep) < 400000): series.season = "{:02d}".format(int(season_ep[:2])) series.episode = "S{}E{}".format(series.season, season_ep[2:4]) else: errmsg("Undefined naming scheme for episode", "Naming", (series.file, )) exit() series.season = "{} {}".format(get_season_desc(cfg), series.season) debugmsg("Alternative naming scheme found", "Naming") else: errmsg("Undefined naming scheme for episode", "Naming", (series.file, )) exit() else: debugmsg( "Alternative naming scheme not found, check for unusual naming scheme (Ep 01)", "Naming") season = None for part in splitted: if (part.isdigit()): season = series.original.replace( series.series_path, "").split(os.sep)[2].split()[1] series.season = "{:02d}".format(int(season)) series.episode = "S{}E{}".format(series.season, part) series.season = "{} {}".format(get_season_desc(cfg), series.season) debugmsg("Unusual naming scheme found", "Naming") break if not season: errmsg("Undefined naming scheme for episode", "Naming", (series.file, )) exit() ## Get the series name of the file series.name_bk = series.original.replace(series.series_path, "").split(os.sep)[1] series.name = series.name_bk.replace(" ", "-") ## Get the destination directory series.dst = os.path.join(series.series_path, series.name_bk, series.season) return series