コード例 #1
0
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)
コード例 #2
0
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)
コード例 #3
0
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)
コード例 #4
0
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)
コード例 #5
0
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()
コード例 #6
0
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
コード例 #7
0
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")
コード例 #8
0
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
コード例 #9
0
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
コード例 #10
0
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)
コード例 #11
0
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)
コード例 #12
0
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, ))
コード例 #13
0
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)
コード例 #14
0
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)
コード例 #15
0
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)
コード例 #16
0
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