Beispiel #1
0
def files_unrar(abs_path, ext=None):
    """ Unzip rar files

    Arguments:
        abs_path {string} -- Path to the directory containg rar files
    """

    stderr = ""
    exts = ext if isinstance(ext, str) else tuple(ext)
    for rar_file in files_find_ext(abs_path, "rar"):
        files = []
        if (exts):
            process = Popen(["unrar", "lb", rar_file],
                            stdout=PIPE,
                            stderr=PIPE)
            stdout, stderr = process.communicate()
            if len(stderr) > 1: break
            files = stdout.decode("UTF-8").split("\n")
            files = [f for f in files if f.endswith(exts)]
        if (files or exts == None):
            infomsg("Found RAR archive in source directory, extract it",
                    "Postprocessing", (rar_file, ))
            process = Popen(["unrar", "x", "-o+", rar_file, abs_path],
                            stdout=PIPE,
                            stderr=PIPE)
            stderr = process.communicate()[1]
            if len(stderr) > 1: break

    if len(stderr) > 1:
        errmsg("Unrar archive failed with the following error message")
        print(stderr)
        exit()
Beispiel #2
0
def main():
    """ Name:    VS-Handbrake (Part of the VS-Package)
        Summary: After handbrake finished the convertion process, the script is called and
                 handles the right naming for the video file and relocates it according to
                 its video type (episode or movie).
    """

    ## Get the shell arguments
    args = argparse.Namespace()
    parser = argparse.ArgumentParser(description='Naming and locate script for Handbrake docker container')
    parser.add_argument('-f','--file', help='Path to the video file', required=True)
    args = parser.parse_args()
    args.script_dir = cur_dir
    args.scope = scope_get()

    ## Check whether the file exists
    if not os.path.isfile(args.file):
        errmsg("File does not exist, check the path", "Postprocessing")

    ## Parse the config
    config_file = os.path.join(cur_dir, "config.txt")
    cfg = parse_cfg(config_file, "vs-handbrake", "docker")

    ## Initialize the logging
    init_logging(args, cfg)

    ## Print the date and the file
    infomsg("-" * 35, "Postprocessing")
    infomsg("Handbrake finished converting file", "Postprocessing", (args.file,))

    ## Check for the source file, continue if convert file doesnt exist
    args = get_convert_source_path(args)

    ## Check which media type it is and process it accordingly
    processing_file(cfg, args)
Beispiel #3
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)
Beispiel #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)
Beispiel #5
0
def main():

    ## Parse all shell arguments
    args = parse_arguments()

    ## Initialize the logging
    init_notification_log(args)

    ## Get all users and mail address of the current system
    args.receivers = mail_get_addresses(args)

    ## Get all categories and the corresponding items
    categories = movie_get_releases(args)
    if not categories:
        infomsg("No new items for any category", "Notification")
        exit()

    ## Create a html-structure and add all categories and items
    content = html_add_header(args)
    for category in categories:
        category_name = os.path.basename(category[0])
        content = html_add_category(content, category_name)
        content = html_add_items(content, category[0], category[1])
    content = html_add_closing(args, content)

    ## Send the mail via sendmail (postfix) command
    mail_send(args, content)
Beispiel #6
0
def mail_get_addresses(args):
    """ Get all users and mail address of the current system

    Arguments:
        args {Namespace} -- Namespace containing all arguments.

    Returns:
        string -- List of tuples with users and mail addresses
    """

    ## Get the username, real name and the mail address of each user
    admin = getpass.getuser()
    user_file = os.path.join(os.sep, "volume1", "homes", admin, "users.csv")
    user_mails = os.path.join(args.scriptdir, "user_mails.sh")
    p = Popen(["/bin/bash", user_mails, admin], stdout=PIPE, stderr=PIPE)
    stderr = p.communicate()[1]
    if p.returncode != 0:
        errmsg("Could not get user mail addresses", (stderr, ))
        exit()
    try:
        with open(user_file, 'r', encoding='utf-8', errors='replace') as f:
            users = f.read()
    except EnvironmentError:
        errmsg("Could not read the user mail addresses file", (user_file, ))
        exit()
    os.remove(user_file)

    ## Parse user names and separate the admin
    users = ("".join(i for i in users if ord(i) < 128)).split("\n")
    users = [u.replace("\n", "").replace("\"", "") for u in users]
    users = [u.replace(" (user)", "").replace(" (admin)", "") for u in users]
    users = list(filter(None, users))
    fs_admin = users_get_selection(2, False)
    admin = [u for u in users if u.split(',')[0] == fs_admin]
    users = list(set(users) - set(admin))

    ## Get the mail tuples
    fs_admin = users_get_selection(2, False)
    user_mails = [
        "{} <{}>".format(u.split(",")[1],
                         u.split(",")[2]) for u in users
    ]
    admin_mails = [
        "{} <{}>".format(a.split(",")[1],
                         a.split(",")[2]) for a in admin
    ]
    mails = admin_mails if args.receivers == "admin" else user_mails

    if args.receivers == "all": mails = admin_mails + user_mails
    infomsg("Selected list of mail addresses by receivers argument", "Mail",
            (",".join(mails), ))
    return mails
Beispiel #7
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()
Beispiel #8
0
def naming_episode(args, cfg):

    ## Get the delimiter of the video filename
    delimiter = delimiter_get(args.file)

    ## Get all information about the episode and season
    path = os.path.abspath(os.path.join(args.file, os.pardir))
    series = Namespace(file=args.file,
                       path=path,
                       delim=delimiter,
                       original=args.source_host,
                       series_path=args.root_host)
    series = analyze_series(cfg, series)

    ## Move the file back to the original path
    file_name = "%s.%s.%s%s" % (series.name, series.episode, series.resolution,
                                series.extension)
    file_dst = os.path.join(series.dst, file_name)
    infomsg("The new file path is", "Naming", (file_dst, ))
    return file_dst
Beispiel #9
0
def mail_send(args, content):
    """ Send the mail via sendmail (postfix) command

    Arguments:
        args {Namespace} -- Namespace containing all arguments.
        content {string} -- HTML content as string.
    """

    infomsg("Start sending the individual mails", "Mail")
    for receiver in args.receivers:
        message = MIMEMultipart('alternative')
        message['From'] = "{} <{}>".format(args.name, args.address)
        message['To'] = receiver
        if args.date:
            td = datetime.today()
            current_day = datetime.strftime(
                datetime(td.year, td.month, td.day), "%d %B %Y")
            message['Subject'] = '{subject} since {date}'.format(
                subject=args.subject, date=current_day)
        else:
            message['Subject'] = '{subject}'.format(subject=args.subject)
        message.attach(MIMEText(content, 'html'))
        p = Popen(["/usr/sbin/sendmail", "-t", "-oi"], stdin=PIPE)
        stderr = p.communicate(message.as_bytes())[1]
        if p.returncode != 0:
            infomsg("sendmail returned with non-zero return value", "Mail",
                    stderr)
        infomsg("Send mail", "Mail", (receiver, ))
Beispiel #10
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)
Beispiel #11
0
def naming_movie(args):

    ## Get the delimiter of the video filename
    delimiter = delimiter_get(args.source_host)

    ## Get all information about the episode and season
    path = os.path.abspath(os.path.join(args.file, os.pardir))
    movie = Namespace(file=args.file,
                      path=path,
                      delim=delimiter,
                      original=args.source_host,
                      movies_path=args.root_host)
    movie = analyze_movie(movie)

    ## Move the file back to the original path
    if (movie.resolution == "2160p"):
        file_name = "%s.%s%s" % (movie.name, movie.resolution, movie.extension)
    else:
        file_name = "%s%s" % (movie.name, movie.extension)
    file_dst = os.path.join(movie.dst, file_name)
    infomsg("The new file path is", "Naming", (file_dst, ))
    return file_dst
Beispiel #12
0
def parse_strlist(strlist, paths=False):
    ''' Parse a stringlist which may be a list of paths '''
    try:
        strlist = list(filter(None, strlist.split(',')))
        strlist = [s.strip() for s in strlist]
    except ValueError:
        errmsg("Invalid string list in config")
        exit()
    if not strlist:
        errmsg("Invalid string list in config")
        exit()
    if paths:
        paths = [p.rstrip(os.sep).lstrip() for p in strlist]
        non_paths = [p for p in paths if not os.path.isdir(p)]
        paths = [p for p in paths if os.path.isdir(p)]
        if not paths:
            infomsg("Config contains list of non-existent file paths",
                    "Parsing", (paths, ))
        if non_paths:
            infomsg("Config contains path which does not exist", "Parsing",
                    (non_paths, ))
        return paths
    return strlist
Beispiel #13
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)
Beispiel #14
0
def html_add_items(content, category, items):
    """ Add all items of a single category as html div-blocks.

    Arguments:
        content {string}  -- HTML content as string.
        category {string} -- Path of the current category.
        items {list}      -- List of items of the category.

    Returns:
        string -- HTML content with all items added.
    """

    ## Intialite IMDBPy
    access = imdb.IMDb()

    history = []
    for movie_name in items:

        ## Add information whether its 4K or not
        f4k = ["4K", "2160p", "UHD", "HDR"]
        format_4k = True if any(u for u in f4k if u in movie_name) else False

        ## Series name correction
        season = ""
        if (os.path.basename(category) != "Filme"):
            movie_name, season = movie_name.replace(category, "").split(os.sep)[1:3]
            movie_name = movie_name.replace("oe", "ö").replace("ue", "ü").replace("ae", "ä")
            season = "- {} ".format(season)
            movie_season = "{}{}".format(movie_name, season)

        ## Movie name correction
        else:
            movie_name = movie_get_name(category, os.path.dirname(movie_name))
            movie_name = movie_name.replace("oe", "ö").replace("ue", "ü").replace("ae", "ä")

        ## Check for duplicates
        if (os.path.basename(category) == "Filme" and movie_name in history):
            infomsg("Skip entry due to duplicate", "HTML", (movie_name,))
            continue
        elif (os.path.basename(category) != "Filme" and movie_season in history):
            infomsg("Skip entry due to duplicate", "HTML", (movie_season,))
            continue

        ## Search for the movie/series and add the item to the html code
        search_results  = access.search_movie(movie_name)
        if search_results:
            movieID = search_results[0].movieID
            movie = access.get_movie(movieID)
            movie_url = access.get_imdbURL(movie)
            content = html_add_item(content, movie, season, movie_name, movie_url, format_4k)
            infomsg("Added imdb entry (with image) for item", "HTML", (movie_name, movie, season))
        else:
            content = html_add_item(content, None, season, movie_name, None, format_4k)
            infomsg("Add non-imdb entry for item", "HTML", (movie_name, season))

        if (os.path.basename(category) == "Filme"):
            history.append(movie_name)
        else:
            history.append(movie_season)

    content = content + '<p>&nbsp;</p>'
    return content