class MgProgressbar():
    """
    Calls in a loop to create terminal progress bar.
    """

    def __init__(
            self,
            total=100,
            time_limit=0.5,
            prefix='Progress',
            suffix='Complete',
            decimals=1,
            length=40,
            fill='█'):
        """
        Initialize the MgProgressbar object.

        Args:
            total (int, optional): Total iterations. Defaults to 100.
            time_limit (float, optional): The minimum refresh rate of the progressbar in seconds. Defaults to 0.5.
            prefix (str, optional): Prefix string. Defaults to 'Progress'.
            suffix (str, optional): Suffix string. Defaults to 'Complete'.
            decimals (int, optional): Positive number of decimals in process percent. Defaults to 1.
            length (int, optional): Character length of the status bar. Defaults to 40.
            fill (str, optional): Bar fill character. Defaults to '█'.
        """

        self.total = total - 1
        self.time_limit = time_limit
        self.prefix = prefix
        self.suffix = suffix
        self.decimals = decimals
        self.length = length
        self.fill = fill
        self.now = self.get_now()
        self.finished = False
        self.could_not_get_terminal_window = False
        self.tw_width = 0
        self.tw_height = 0
        self.display_only_percent = False

    def get_now(self):
        """
        Gets the current time.

        Returns:
            datetime.datetime.timestamp: The current time.
        """
        from datetime import datetime
        return datetime.timestamp(datetime.now())

    def over_time_limit(self):
        """
        Checks if we should redraw the progress bar at this moment.

        Returns:
            bool: True if equal or more time has passed than `self.time_limit` since the last redraw.
        """
        callback_time = self.get_now()
        return callback_time - self.now >= self.time_limit

    def adjust_printlength(self):
        if self.tw_width <= 0:
            return
        elif self.could_not_get_terminal_window:
            return
        else:
            _length_before = self.length
            current_length = len(self.prefix) + self.length + \
                self.decimals + len(self.suffix) + 10

            # if the length of printed line is longer than the terminal window's width
            if current_length > self.tw_width:
                diff = current_length - self.tw_width

                # if the difference is shorter than the progress bar length
                if diff < self.length:
                    self.length -= diff  # shorten the progress bar

                # if the difference is at least as long as the progress bar or longer
                else:  # remove suffix
                    current_length = current_length - \
                        len(self.suffix)  # remove suffix
                    diff = current_length - self.tw_width  # recalculate difference

                    # if the terminal width is long enough without suffix
                    if diff <= 0:
                        self.suffix = ""  # just remove suffix

                    # the terminal window is too short even without suffix
                    # remove suffix and test again
                    else:
                        self.suffix = ""

                        # --- SUFFIX IS REMOVED ---

                        # if the difference is shorter than the progress bar
                        if diff < self.length:
                            self.length -= diff  # shorten progress bar

                        # if the difference is longer than the progress bar, remove prefix
                        else:  # remove prefix
                            current_length = current_length - len(self.prefix)
                            diff = current_length - self.tw_width

                            # if the terminal width is long enough without prefix
                            if diff <= 0:
                                self.prefix = ""  # just remove prefix

                            # the terminal window is too short even without prefix (and suffix)
                            # remove prefix and test again
                            else:
                                self.prefix = ""

                                # --- PREFFIX IS REMOVED ---

                                # if the difference is shorter than the progress bar
                                if diff < self.length:
                                    self.length -= diff  # shorten progress bar

                                else:  # display only percent
                                    self.display_only_percent = True

    def progress(self, iteration):
        """
        Progresses the progress bar to the next step.

        Args:
            iteration (float): The current iteration. For example, the 57th out of 100 steps, or 12.3s out of the total 60s.
        """
        if self.finished:
            return
        import sys
        import shutil

        if not self.could_not_get_terminal_window:
            self.tw_width, self.tw_height = shutil.get_terminal_size((0, 0))
            if self.tw_width + self.tw_height == 0:
                self.could_not_get_terminal_window = True
            else:
                self.adjust_printlength()  # this line cannot be tested :'(

        capped_iteration = iteration if iteration <= self.total else self.total
        # Print New Line on Complete
        if iteration >= self.total:
            self.finished = True
            percent = ("{0:." + str(self.decimals) + "f}").format(100)
            filledLength = int(round(self.length))
            bar = self.fill * filledLength
            sys.stdout.flush()
            if self.display_only_percent:
                sys.stdout.write('\r%s' % (percent))
            else:
                sys.stdout.write('\r%s |%s| %s%% %s' %
                                 (self.prefix, bar, percent, self.suffix))
            print()
        elif self.over_time_limit():
            self.now = self.get_now()
            percent = ("{0:." + str(self.decimals) + "f}").format(100 *
                                                                  (capped_iteration / float(self.total)))
            filledLength = int(self.length * capped_iteration // self.total)
            bar = self.fill * filledLength + '-' * (self.length - filledLength)
            sys.stdout.flush()
            if self.display_only_percent:
                sys.stdout.write('\r%s' % (percent))
            else:
                sys.stdout.write('\r%s |%s| %s%% %s' %
                                 (self.prefix, bar, percent, self.suffix))
        else:
            return

    def __repr__(self):
        return "MgProgressbar"

def roundup(num, modulo_num):
    """
    Rounds up a number to the next integer multiple of another.

    Args:
        num (int): The number to round up.
        modulo_num (int): The number whose next integer multiple we want.

    Returns:
        int: The rounded-up number.
    """
    num, modulo_num = int(num), int(modulo_num)
    return num - (num % modulo_num) + modulo_num*((num % modulo_num) != 0)

def clamp(num, min_value, max_value):
    """
    Clamps a number between a minimum and maximum value.

    Args:
        num (float): The number to clamp.
        min_value (float): The minimum allowed value.
        max_value (float): The maximum allowed value.

    Returns:
        float: The clamped number.
    """
    return max(min(num, max_value), min_value)

def scale_num(val, in_low, in_high, out_low, out_high):
    """
    Scales a number linearly.

    Args:
        val (float): The value to be scaled.
        in_low (float): Minimum of input range.
        in_high (float): Maximum of input range.
        out_low (float): Minimum of output range.
        out_high (float): Maximum of output range.

    Returns:
        float: The scaled number.
    """

    return ((val - in_low) * (out_high - out_low)) / (in_high - in_low) + out_low

def scale_array(array, out_low, out_high):
    """
    Scales an array linearly.

    Args:
        array (arraylike): The array to be scaled.
        out_low (float): Minimum of output range.
        out_high (float): Maximum of output range.

    Returns:
        arraylike: The scaled array.
    """

    import numpy as np
    minimum, maximum = np.min(array), np.max(array)
    m = (out_high - out_low) / (maximum - minimum)
    b = out_low - m * minimum
    return m * array + b

def generate_outfilename(requested_name):
    """Returns a unique filepath to avoid overwriting existing files. Increments requested
    filename if necessary by appending an integer, like "_0" or "_1", etc to the file name.

    Args:
        requested_name (str): Requested file name as path string.

    Returns:
        str: If file at requested_name is not present, then requested_name, else an incremented filename.
    """
    import os
    requested_name = os.path.abspath(requested_name).replace('\\', '/')
    req_of, req_fex = os.path.splitext(requested_name)
    req_of = req_of.replace('\\', '/')
    req_folder = os.path.dirname(requested_name).replace('\\', '/')
    req_of_base = os.path.basename(req_of)
    req_file_base = os.path.basename(requested_name)
    out_increment = 0
    files_in_folder = os.listdir(req_folder)
    # if the target folder is empty, return the requested path
    if len(files_in_folder) == 0:
        return requested_name
    # filter files with same ext
    files_w_same_ext = list(filter(lambda x: os.path.splitext(x)[
                            1] == req_fex, files_in_folder))
    # if there are no files with the same ext
    if len(files_w_same_ext) == 0:
        return requested_name
    # filter for files with same start and ext
    files_w_same_start_ext = list(
        filter(lambda x: x.startswith(req_of_base), files_w_same_ext))
    # if there are no files with the same start and ext
    if len(files_w_same_start_ext) == 0:
        return requested_name
    # check if requested file is already present
    present = None
    try:
        ind = files_w_same_start_ext.index(req_file_base)
        present = True
    except ValueError:
        present = False
    # if requested file is not present
    if not present:
        return requested_name
    # if the original filename is already taken, check if there are incremented filenames
    files_w_increment = list(filter(lambda x: x.startswith(
        req_of_base+"_"), files_w_same_start_ext))
    # if there are no files with increments
    if len(files_w_increment) == 0:
        return f'{req_of}_0{req_fex}'
    # parse increments, discard the ones that are invalid, increment highest
    for file in files_w_increment:
        _of = os.path.splitext(file)[0]
        _only_incr = _of[len(req_of_base)+1:]
        try:
            found_incr = int(_only_incr)
            found_incr = max(0, found_incr)  # clip at 0
            out_increment = max(out_increment, found_incr+1)
        except ValueError:  # if cannot be converted to int
            pass
    # return incremented filename
    return f'{req_of}_{out_increment}{req_fex}'

def get_frame_planecount(frame):
    """
    Gets the planecount (color channel count) of a video frame.

    Args:
        frame (numpy array): A frame extracted by `cv2.VideoCapture().read()`.

    Returns:
        int: The planecount of the input frame, 3 or 1.
    """

    import numpy as np
    return 3 if len(np.array(frame).shape) == 3 else 1

def frame2ms(frame, fps):
    """
    Converts frames to milliseconds.

    Args:
        frame (int): The index of the frame to be converted to milliseconds.
        fps (int): Frames per second.

    Returns:
        int: The rounded millisecond value of the input frame index.
    """

    return round(frame / fps * 1000)

class MgImage():
    """
    Class for handling images in the Musical Gestures Toolbox.
    """

    def __init__(self, filename):
        """
        Initializes the MgImage object.

        Args:
            filename (str): The path to the image file to load.
        """
        self.filename = filename
        import os
        self.of = os.path.splitext(self.filename)[0]
        self.fex = os.path.splitext(self.filename)[1]
    from musicalgestures._show import mg_show as show

    def __repr__(self):
        return f"MgImage('{self.filename}')"

class MgFigure():
    """
    Class for working with figures and plots within the Musical Gestures Toolbox.
    """

    def __init__(self, figure=None, figure_type=None, data=None, layers=None, image=None):
        """
        Initializes the MgFigure object.

        Args:
            figure (matplotlib.pyplot.figure, optional): The internal figure. Defaults to None.
            figure_type (str, optional): A keyword describing the type of the figure, such as "audio.spectrogram", "audio.tempogram", "audio.descriptors", "layers", etc. Defaults to None.
            data (dictionary, optional): The dictionary containing all the necessary variables, lists and (typically) NumPy arrays necessary to rebuild each subplot in the figure. Defaults to None.
            layers (list, optional): This is only relevant if the MgFigure instance is of "layers" type, which indicates that it is a composit of several MgFigures and/or MgImages. In this case the layers list should contain all the child instances (MgFigures, MgImages, or MgLists of these) which are included in this MgFigure and are displayed as subplots. Defaults to None.
            image (str, optional): Path to the image file (the rendered figure). Defaults to None.
        """
        self.figure = figure
        self.figure_type = figure_type
        self.data = data
        self.layers = layers
        self.image = image

    def __repr__(self):
        return f"MgFigure(figure_type='{self.figure_type}')"

    def show(self):
        """
        Shows the internal matplotlib.pyplot.figure.
        """
        return self.figure

class WrongContainer(Exception):
    def __init__(self, message):
        self.message = message

def pass_if_containers_match(file_1, file_2):
    """Checks if file extensions match between two files. If they do it passes, is they don't it raises WrongContainer exception.

    Args:
        file_1 (str): First file in comparison.
        file_2 (str): Second file in comparison.

    Raises:
        WrongContainer: If file extensions (containers) mismatch.
    """
    import os
    fex_1 = os.path.splitext(file_1)[1].lower()
    fex_2 = os.path.splitext(file_2)[1]. lower()
    if fex_1 != fex_2:
        raise WrongContainer(
            f"Container mismatch: {fex_1} vs {fex_2}; between {file_1} and {file_2}.")

def pass_if_container_is(container, file):
    """Checks if a file's extension matches a desired one. Passes if so, raises WrongContainer if not.

    Args:
        container (str): The container to match.
        file (str): Path to the file to inspect.

    Raises:
        WrongContainer: If the file extension (container) matches the desired one.
    """
    import os
    if os.path.splitext(file)[1].lower() != container.lower():
        raise WrongContainer(
            f"Container should be {container.lower()}, but it is {os.path.splitext(file)[1].lower()} in file {file}.")

def convert(filename, target_name, overwrite=False):
    """
    Converts a video to another format/container using ffmpeg.

    Args:
        filename (str): Path to the input video file to convert.
        target_name (str): Target filename as path.
        overwrite (bool, optional): Whether to allow overwriting existing files or to automatically increment target filename to avoid overwriting. Defaults to False.

    Returns:
        str: The path to the output file.
    """

    import os
    of, fex = os.path.splitext(filename)
    target_of, target_fex = os.path.splitext(target_name)
    if fex.lower() == target_fex.lower():
        print(f'{filename} is already in {fex} container.')
        return filename
    if not overwrite:
        target_name = generate_outfilename(target_name)
    cmds = ['ffmpeg', '-y', '-i', filename,
            "-q:v", "3", target_name]
    ffmpeg_cmd(cmds, get_length(filename),
               pb_prefix=f'Converting to {target_fex}:')
    return target_name

def convert_to_avi(filename, target_name=None, overwrite=False):
    """
    Converts a video to one with .avi extension using ffmpeg.

    Args:
        filename (str): Path to the input video file to convert.
        target_name (str, optional): Target filename as path. Defaults to None (which assumes that the input filename should be used).
        overwrite (bool, optional): Whether to allow overwriting existing files or to automatically increment target filename to avoid overwriting. Defaults to False.

    Returns:
        str: The path to the output '.avi' file.
    """

    import os
    of, fex = os.path.splitext(filename)
    if fex.lower() == '.avi':
        print(f'{filename} is already in avi container.')
        return filename
    if not target_name:
        target_name = of + '.avi'
    if not overwrite:
        target_name = generate_outfilename(target_name)
    pass_if_container_is(".avi", target_name)
    cmds = ['ffmpeg', '-y', '-i', filename, "-c:v", "mjpeg",
            "-q:v", "3", "-c:a", "copy", target_name]
    ffmpeg_cmd(cmds, get_length(filename), pb_prefix='Converting to avi:')
    return target_name

def convert_to_mp4(filename, target_name=None, overwrite=False):
    """
    Converts a video to one with .mp4 extension using ffmpeg.

    Args:
        filename (str): Path to the input video file to convert.
        target_name (str, optional): Target filename as path. Defaults to None (which assumes that the input filename should be used).
        overwrite (bool, optional): Whether to allow overwriting existing files or to automatically increment target filename to avoid overwriting. Defaults to False.

    Returns:
        str: The path to the output '.mp4' file.
    """

    import os
    of, fex = os.path.splitext(filename)
    if fex.lower() == '.mp4':
        print(f'{filename} is already in mp4 container.')
        return filename
    if not target_name:
        target_name = of + '.mp4'
    if not overwrite:
        target_name = generate_outfilename(target_name)
    pass_if_container_is(".mp4", target_name)
    cmds = ['ffmpeg', '-y', '-i', filename,
            "-q:v", "3", target_name]
    ffmpeg_cmd(cmds, get_length(filename), pb_prefix='Converting to mp4:')
    return target_name

def convert_to_webm(filename, target_name=None, overwrite=False):
    """
    Converts a video to one with .webm extension using ffmpeg.

    Args:
        filename (str): Path to the input video file to convert.
        target_name (str, optional): Target filename as path. Defaults to None (which assumes that the input filename should be used).
        overwrite (bool, optional): Whether to allow overwriting existing files or to automatically increment target filename to avoid overwriting. Defaults to False.

    Returns:
        str: The path to the output '.webm' file.
    """

    import os
    of, fex = os.path.splitext(filename)
    if fex.lower() == '.webm':
        print(f'{filename} is already in webm container.')
        return filename
    if not target_name:
        target_name = of + '.webm'
    if not overwrite:
        target_name = generate_outfilename(target_name)
    pass_if_container_is(".webm", target_name)
    cmds = ['ffmpeg', '-y', '-i', filename,
            "-q:v", "3", target_name]
    ffmpeg_cmd(cmds, get_length(filename), pb_prefix='Converting to webm:')
    return target_name

def cast_into_avi(filename, target_name=None, overwrite=False):
    """
    *Experimental*
    Casts a video into and .avi container using ffmpeg. Much faster than `convert_to_avi`,
    but does not always work well with cv2 or built-in video players.

    Args:
        filename (str): Path to the input video file.
        target_name (str, optional): Target filename as path. Defaults to None (which assumes that the input filename should be used).
        overwrite (bool, optional): Whether to allow overwriting existing files or to automatically increment target filename to avoid overwriting. Defaults to False.

    Returns:
        str: The path to the output '.avi' file.
    """

    import os
    of = os.path.splitext(filename)[0]
    if not target_name:
        target_name = of + '.avi'
    if not overwrite:
        target_name = generate_outfilename(target_name)
    pass_if_container_is(".avi", target_name)
    cmds = ['ffmpeg', '-y', '-i', filename, "-codec", "copy", target_name]
    ffmpeg_cmd(cmds, get_length(filename), pb_prefix='Casting to avi')
    return target_name

def extract_subclip(filename, t1, t2, target_name=None, overwrite=False):
    """
    Extracts a section of the video using ffmpeg.

    Args:
        filename (str): Path to the input video file.
        t1 (float): The start of the section to extract in seconds.
        t2 (float): The end of the section to extract in seconds.
        target_name (str, optional): The name for the output file. If None, the name will be \<input name\>SUB\<start time in ms\>_\<end time in ms\>.\<file extension\>. Defaults to None.
        overwrite (bool, optional): Whether to allow overwriting existing files or to automatically increment target filename to avoid overwriting. Defaults to False.

    Returns:
        str: Path to the extracted section as a video.
    """

    import os
    import numpy as np
    name, ext = os.path.splitext(filename)
    length = get_length(filename)
    start, end = np.clip(t1, 0, length), np.clip(t2, 0, length)
    if start > end:
        # end = length
        start, end = end, start

    if not target_name:
        T1, T2 = [int(1000*t) for t in [start, end]]
        target_name = "%sSUB%d_%d.%s" % (name, T1, T2, ext)

    if not overwrite:
        target_name = generate_outfilename(target_name)

    pass_if_containers_match(filename, target_name)

    # avoiding ffmpeg glitch if format is not avi:
    if os.path.splitext(filename)[1] != '.avi':
        cmd = ['ffmpeg', "-y",
               "-ss", "%0.2f" % start,
               "-i", filename,
               "-t", "%0.2f" % (end-start),
               "-max_muxing_queue_size", "9999",
               "-map", "0", target_name]
    else:
        cmd = ['ffmpeg', "-y",
               "-ss", "%0.2f" % start,
               "-i", filename,
               "-t", "%0.2f" % (end-start),
               "-max_muxing_queue_size", "9999",
               "-map", "0", "-codec", "copy", target_name]

    ffmpeg_cmd(cmd, length, pb_prefix='Trimming:')
    return target_name

def rotate_video(filename, angle, target_name=None, overwrite=False):
    """
    Rotates a video by an `angle` using ffmpeg.

    Args:
        filename (str): Path to the input video file.
        angle (float): The angle (in degrees) specifying the amount of rotation. Positive values rotate clockwise.
        target_name (str, optional): Target filename as path. Defaults to None (which assumes that the input filename with the suffix "_rot" should be used).
        overwrite (bool, optional): Whether to allow overwriting existing files or to automatically increment target filename to avoid overwriting. Defaults to False.

    Returns:
        str: The path to the rotated video file.
    """

    import os
    import math
    import numpy as np
    of, fex = os.path.splitext(filename)

    if not target_name:
        target_name = of + '_rot' + fex
    if not overwrite:
        target_name = generate_outfilename(target_name)

    pass_if_containers_match(filename, target_name)

    if np.abs(angle) == 90 or np.abs(angle) == 180:
        # Rotate video without encoding for faster computation
        cmds = ['ffmpeg', '-y', '-i', filename,
                '-metadata:s:v:0', f'rotate={angle}', '-codec', 'copy', target_name]
    else:
        # Rotate video with encoding
        cmds = ['ffmpeg', '-y', '-i', filename, "-vf",
                f"rotate={math.radians(angle)}", "-q:v", "3", "-c:a", "copy", target_name]
    ffmpeg_cmd(cmds, get_length(filename),
               pb_prefix=f"Rotating video by {angle} degrees:")
    return target_name

def convert_to_grayscale(filename, target_name=None, overwrite=False):
    """
    Converts a video to grayscale using ffmpeg.

    Args:
        filename (str): Path to the input video file.
        target_name (str, optional): Target filename as path. Defaults to None (which assumes that the input filename with the suffix "_gray" should be used).
        overwrite (bool, optional): Whether to allow overwriting existing files or to automatically increment target filename to avoid overwriting. Defaults to False.

    Returns:
        str: The path to the grayscale video file.
    """

    import os
    of, fex = os.path.splitext(filename)

    if not target_name:
        target_name = of + '_gray' + fex
    if not overwrite:
        target_name = generate_outfilename(target_name)

    pass_if_containers_match(filename, target_name)

    cmds = ['ffmpeg', '-y', '-i', filename, '-vf',
            'hue=s=0', "-q:v", "3", "-c:a", "copy", target_name]
    ffmpeg_cmd(cmds, get_length(filename),
               pb_prefix='Converting to grayscale:')
    return target_name

def transform_frame(out, height, width, color):
    import numpy

    # transform the bytes read into a numpy array
    frame =  numpy.frombuffer(out, dtype='uint8')
    try:
        if color:
            frame = frame.reshape((height,width,3)) # height, width, channels
        else:
            frame = frame.reshape((height,width)) # height, width
    except ValueError:
        pass

    return frame

def framediff_ffmpeg(filename, target_name=None, color=True, overwrite=False):
    """
    Renders a frame difference video from the input using ffmpeg.

    Args:
        filename (str): Path to the input video file.
        target_name (str, optional): The name of the output video. Defaults to None (which assumes that the input filename with the suffix "_framediff" should be used).
        color (bool, optional): If False, the output will be grayscale. Defaults to True.
        overwrite (bool, optional): Whether to allow overwriting existing files or to automatically increment target filename to avoid overwriting. Defaults to False.

    Returns:
        str: Path to the output video.
    """

    import os
    of, fex = os.path.splitext(filename)

    if target_name == None:
        target_name = of + '_framediff' + fex
    if not overwrite:
        target_name = generate_outfilename(target_name)
    pass_if_containers_match(filename, target_name)
    if color == True:
        pixformat = 'gbrp'
    else:
        pixformat = 'gray'
    cmd = ['ffmpeg', '-y', '-i', filename, '-filter_complex',
           f'format={pixformat},tblend=all_mode=difference', '-q:v', '3', "-c:a", "copy", target_name]
    ffmpeg_cmd(cmd, get_length(filename),
               pb_prefix='Rendering frame difference video:')
    return target_name

def threshold_ffmpeg(filename, threshold=0.1, target_name=None, binary=False, overwrite=False):
    """
    Renders a pixel-thresholded video from the input using ffmpeg.

    Args:
        filename (str): Path to the input video file.
        threshold (float, optional): The normalized pixel value to use as the threshold. Pixels below the threshold will turn black. Defaults to 0.1.
        target_name (str, optional): The name of the output video. Defaults to None (which assumes that the input filename with the suffix "_thresh" should be used).
        binary (bool, optional): If True, the pixels above the threshold will turn white. Defaults to False.
        overwrite (bool, optional): Whether to allow overwriting existing files or to automatically increment target filename to avoid overwriting. Defaults to False.

    Returns:
        str: Path to the output video.
    """

    import os
    import matplotlib
    of, fex = os.path.splitext(filename)

    if target_name == None:
        target_name = of + '_thresh' + fex
    if not overwrite:
        target_name = generate_outfilename(target_name)

    pass_if_containers_match(filename, target_name)

    width, height = get_widthheight(filename)

    thresh_color = matplotlib.colors.to_hex([threshold, threshold, threshold])
    thresh_color = '0x' + thresh_color[1:]

    if binary == False:
        cmd = ['ffmpeg', '-y', '-i', filename, '-f', 'lavfi', '-i', f'color={thresh_color},scale={width}:{height}', '-f', 'lavfi',
               '-i', f'color=black,scale={width}:{height}', '-i', filename, '-lavfi', 'format=gbrp,threshold', '-q:v', '3', "-c:a", "copy", target_name]
    else:
        cmd = ['ffmpeg', '-y', '-i', filename, '-f', 'lavfi', '-i', f'color={thresh_color},scale={width}:{height}', '-f', 'lavfi',
               '-i', f'color=black,scale={width}:{height}', '-f', 'lavfi', '-i', f'color=white,scale={width}:{height}', '-lavfi', 'format=gray,threshold', '-q:v', '3', "-c:a", "copy", target_name]

    ffmpeg_cmd(cmd, get_length(filename),
               pb_prefix='Rendering threshold video:')

    return target_name

def motionvideo_ffmpeg(
        filename,
        color=True,
        filtertype='regular',
        threshold=0.05,
        blur='none',
        use_median=False,
        kernel_size=5,
        invert=False,
        target_name=None,
        overwrite=False):
    """
    Renders a motion video using ffmpeg.

    Args:
        filename (str): Path to the input video file.
        color (bool, optional): If False the input is converted to grayscale at the start of the process. This can significantly reduce render time. Defaults to True.
        filtertype (str, optional): 'Regular' turns all values below `thresh` to 0. 'Binary' turns all values below `thresh` to 0, above `thresh` to 1. 'Blob' removes individual pixels with erosion method. Defaults to 'Regular'.
        thresh (float, optional): Eliminates pixel values less than given threshold. Ranges from 0 to 1. Defaults to 0.05.
        blur (str, optional): 'Average' to apply a 10px * 10px blurring filter, 'None' otherwise. Defaults to 'None'.
        use_median (bool, optional): If True the algorithm applies a median filter on the thresholded frame-difference stream. Defaults to False.
        kernel_size (int, optional): Size of the median filter (if `use_median=True`) or the erosion filter (if `filtertype='blob'`). Defaults to 5.
        invert (bool, optional): If True, inverts colors of the motion video. Defaults to False.
        target_name (str, optional): Defaults to None (which assumes that the input filename with the suffix "_motion" should be used).
        overwrite (bool, optional): Whether to allow overwriting existing files or to automatically increment target filename to avoid overwriting. Defaults to False.

    Returns:
        str: Path to the output video.
    """

    import os
    from musicalgestures._filter import filter_frame_ffmpeg

    of, fex = os.path.splitext(filename)

    cmd = ['ffmpeg', '-y', '-i', filename]
    # cmd_filter = ''

    if target_name == None:
        target_name = of + '_motion' + fex
    if not overwrite:
        target_name = generate_outfilename(target_name)

    cmd, cmd_filter = filter_frame_ffmpeg(filename, cmd, color, blur, filtertype, threshold, kernel_size, use_median, invert=invert)
    # remove last comma after previous filter
    cmd_filter = cmd_filter[: -1]

    pass_if_containers_match(filename, target_name)
    cmd_end = ['-q:v', '3', "-c:a", "copy", target_name]
    cmd += ['-filter_complex', cmd_filter] + cmd_end

    ffmpeg_cmd(cmd, get_length(filename), pb_prefix='Rendering motion video:')

    return target_name

def motiongrams_ffmpeg(
        filename,
        color=True,
        filtertype='regular',
        threshold=0.05,
        blur='none',
        use_median=False,
        kernel_size=5,
        invert=False,
        target_name_x=None,
        target_name_y=None,
        overwrite=False):
    """
    Renders horizontal and vertical motiongrams using ffmpeg.

    Args:
        filename (str): Path to the input video file.
        color (bool, optional): If False the input is converted to grayscale at the start of the process. This can significantly reduce render time. Defaults to True.
        filtertype (str, optional): 'Regular' turns all values below `thresh` to 0. 'Binary' turns all values below `thresh` to 0, above `thresh` to 1. 'Blob' removes individual pixels with erosion method. Defaults to 'Regular'.
        thresh (float, optional): Eliminates pixel values less than given threshold. Ranges from 0 to 1. Defaults to 0.05.
        blur (str, optional): 'Average' to apply a 10px * 10px blurring filter, 'None' otherwise. Defaults to 'None'.
        use_median (bool, optional): If True the algorithm applies a median filter on the thresholded frame-difference stream. Defaults to False.
        kernel_size (int, optional): Size of the median filter (if `use_median=True`) or the erosion filter (if `filtertype='blob'`). Defaults to 5.
        invert (bool, optional): If True, inverts colors of the motiongrams. Defaults to False.
        target_name_x (str, optional): Target output name for the motiongram on the X axis. Defaults to None (which assumes that the input filename with the suffix "_mgx_ffmpeg" should be used).
        target_name_y (str, optional): Target output name for the motiongram on the Y axis. Defaults to None (which assumes that the input filename with the suffix "_mgy_ffmpeg" should be used).
        overwrite (bool, optional): Whether to allow overwriting existing files or to automatically increment target filenames to avoid overwriting. Defaults to False.

    Returns:
        str: Path to the output horizontal motiongram (_mgx).
        str: Path to the output vertical motiongram (_mgy).
    """

    import os
    from musicalgestures._filter import filter_frame_ffmpeg

    of, fex = os.path.splitext(filename)

    if target_name_x == None:
        target_name_x = of+'_mgx_ffmpeg.png'
    if target_name_y == None:
        target_name_y = of+'_mgy_ffmpeg.png'
    if not overwrite:
        target_name_x = generate_outfilename(target_name_x)
        target_name_y = generate_outfilename(target_name_y)

    pass_if_container_is(".png", target_name_x)
    pass_if_container_is(".png", target_name_y)

    cmd = ['ffmpeg', '-y', '-i', filename]

    width, height = get_widthheight(filename)
    framecount = get_framecount(filename)

    cmd_end_y = ['-aspect', f'{framecount}:{height}', '-frames', '1', target_name_y]
    cmd_end_x = ['-aspect', f'{width}:{framecount}', '-frames', '1', target_name_x]

    cmd, cmd_filter = filter_frame_ffmpeg(filename, cmd, color, blur, filtertype, threshold, kernel_size, use_median, invert=invert)
    cmd_filter += 'atadenoise=s=129,' # apply adaptive temporal averaging denoiser every 129 frames

    cmd_filter_y = cmd_filter + \
        f'scale=1:{height},tile={framecount}x1,normalize=independence=0'
    # f'scale=1:{height}:sws_flags=area,normalize,tile={framecount}x1'
    cmd_filter_x = cmd_filter + \
        f'scale={width}:1,tile=1x{framecount},normalize=independence=0'
    # f'scale={width}:1:sws_flags=area,normalize,tile=1x{framecount}'

    cmd_y = cmd + ['-filter_complex', cmd_filter_y] + cmd_end_y
    cmd_x = cmd + ['-filter_complex', cmd_filter_x] + cmd_end_x

    ffmpeg_cmd(cmd_x, get_length(filename), pb_prefix='Rendering horizontal motiongram:', stream=False)
    ffmpeg_cmd(cmd_y, get_length(filename), pb_prefix='Rendering vertical motiongram:', stream=False)

    return target_name_x, target_name_y

def crop_ffmpeg(filename, w, h, x, y, target_name=None, overwrite=False):
    """
    Crops a video using ffmpeg.

    Args:
        filename (str): Path to the input video file.
        w (int): The desired width.
        h (int): The desired height.
        x (int): The horizontal coordinate of the top left pixel of the cropping rectangle.
        y (int): The vertical coordinate of the top left pixel of the cropping rectangle.
        target_name (str, optional): The name of the output video. Defaults to None (which assumes that the input filename with the suffix "_crop" should be used).
        overwrite (bool, optional): Whether to allow overwriting existing files or to automatically increment target filenames to avoid overwriting. Defaults to False.

    Returns:
        str: Path to the output video.
    """

    import os

    of, fex = os.path.splitext(filename)

    if target_name == None:
        target_name = of + '_crop' + fex
    if not overwrite:
        target_name = generate_outfilename(target_name)

    pass_if_containers_match(filename, target_name)

    cmd = ['ffmpeg', '-y', '-i', filename, '-vf',
           f'crop={w}:{h}:{x}:{y}', '-q:v', '3', "-c:a", "copy", target_name]

    ffmpeg_cmd(cmd, get_length(filename), pb_prefix='Rendering cropped video:')

    return target_name

def extract_wav(filename, target_name=None, overwrite=False):
    """
    Extracts audio from video into a .wav file via ffmpeg.

    Args:
        filename (str): Path to the video file from which the audio track shall be extracted.
        target_name (str, optional): The name of the output video. Defaults to None (which assumes that the input filename should be used).
        overwrite (bool, optional): Whether to allow overwriting existing files or to automatically increment target filename to avoid overwriting. Defaults to False.

    Returns:
        str: The path to the output audio file.
    """

    import os
    of, fex = os.path.splitext(filename)

    if target_name == None:
        target_name = of + '.wav'
    if not overwrite:
        target_name = generate_outfilename(target_name)

    pass_if_container_is(".wav", target_name)

    if fex in ['.wav', '.WAV']:
        print(f'{filename} is already in .wav container.')
        return filename

    cmds = ' '.join(['ffmpeg', '-y', '-i', wrap_str(filename), "-acodec",
                     "pcm_s16le", wrap_str(target_name)])
    os.system(cmds)
    return target_name

class FFprobeError(Exception):
    def __init__(self, message):
        self.message = message

class NoStreamError(FFprobeError):
    pass

class NoDurationError(FFprobeError):
    pass

def ffprobe(filename):
    """
    Returns info about video/audio file using FFprobe.

    Args:
        filename (str): Path to the video file to measure.

    Returns:
        str: decoded FFprobe output (stdout) as one string.
    """
    import subprocess
    command = ['ffprobe', filename]
    process = subprocess.Popen(
        command, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, universal_newlines=True)
    try:
        out, err = process.communicate(timeout=10)
    except subprocess.TimeoutExpired:
        process.kill()
        out, err = process.communicate()

    if err:
        raise FFprobeError(err)
    else:
        if out.splitlines()[-1].find("No such file or directory") != -1:
            raise FileNotFoundError(out.splitlines()[-1])
        else:
            return out

def get_metadata(filename):
    """
    Returns metadata about video/audio/format file using ffprobe.

    Args:
        filename (str): Path to the video file to measure.

    Returns:
        str: decoded ffprobe output (stdout) as a list containing three dictionaries for video, audio and format metadata.
    """

    import subprocess
    # Get streams and format information (https://ffmpeg.org/ffprobe.html)
    cmd = ["ffprobe", "-loglevel", "0", "-show_streams", "-show_format", filename]
    process = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, universal_newlines=True)
    try:
        out, err = process.communicate(timeout=10)
        splitted = out.split('\n')
    except subprocess.TimeoutExpired:
        process.kill()
    out, err = process.communicate()
    splitted = out.split('\n')

    metadata = []
    # Retrieve information and export it in a dictionary
    for i, info in enumerate(splitted):
        if info == "[STREAM]" or info == "[SIDE_DATA]" or info == "[FORMAT]":
            metadata.append(dict())
            i +=1
        elif info == "[/STREAM]" or info == "[/SIDE_DATA]" or info == "[/FORMAT]" or info == "":
            i +=1
        else:
            try:
                key, value = splitted[i].split('=')
                metadata[-1][key] = value
            except ValueError:
                key = splitted[i]
                metadata[-1][key] = ''

    if len(metadata) > 3:
        # Merge video stream with side data dictionary
        metadata[0] = {**metadata[0], **metadata[1]}
        metadata.pop(1)

    return metadata

def get_widthheight(filename):
    """
    Gets the width and height of a video using FFprobe.

    Args:
        filename (str): Path to the video file to measure.

    Returns:
        int: The width of the input video file.
        int: The height of the input video file.
    """
    out = ffprobe(filename)
    out_array = out.splitlines()
    video_stream = None
    at_line = -1
    while video_stream == None:
        video_stream = out_array[at_line] if out_array[at_line].find("Video:") != -1 else None

        if out_array[at_line].find("displaymatrix:") != -1:
            import re
            rotation = [d for d in re.findall("\d+\.\d+", out_array[at_line])]

        at_line -= 1
        if at_line < -len(out_array):
            raise NoStreamError("No video stream found. (Is this a video file?)")

    try:
        if int(float(rotation[0])) == 90:
            # If the video has been rotated for 90°, we need to invert width and height
            width = int(video_stream.split('x')[-1].split(',')[0].split(' ')[0])
            height = int(video_stream.split('x')[-2].split(' ')[-1])
        else:
            width = int(video_stream.split('x')[-2].split(' ')[-1])
            height = int(video_stream.split('x')[-1].split(',')[0].split(' ')[0])
    except:
        width = int(video_stream.split('x')[-2].split(' ')[-1])
        height = int(video_stream.split('x')[-1].split(',')[0].split(' ')[0])

    return width, height

def has_audio(filename):
    """
    Checks if video has audio track using FFprobe.

    Args:
        filename (str): Path to the video file to check.

    Returns:
        bool: True if `filename` has an audio track, False otherwise.
    """
    out = ffprobe(filename)
    out_array = out.splitlines()
    audio_stream = None
    at_line = -1
    while audio_stream == None:
        audio_stream = out_array[at_line] if out_array[at_line].find(
            "Audio:") != -1 else None
        at_line -= 1
        if at_line < -len(out_array):
            break
    if audio_stream == None:
        return False
    else:
        return True

def get_length(filename):
    """
    Gets the length (in seconds) of a video using FFprobe.

    Args:
        filename (str): Path to the video file to measure.

    Returns:
        float: The length of the input video file in seconds.
    """
    out = ffprobe(filename)
    out_array = out.splitlines()
    duration = None
    at_line = -1
    while duration == None:
        duration = out_array[at_line] if out_array[at_line].find(
            "Duration:") != -1 else None
        at_line -= 1
        if at_line < -len(out_array):
            raise NoDurationError(
                "Could not get duration.")
    duration_array = duration.split(' ')
    time_string_index = duration_array.index("Duration:") + 1
    time_string = duration_array[time_string_index][:-1]
    elems = [float(elem) for elem in time_string.split(':')]
    return elems[0]*3600 + elems[1]*60 + elems[2]

def get_framecount(filename, fast=True):
    """
    Returns the number of frames in a video using FFprobe.

    Args:
        filename (str): Path to the video file to measure.

    Returns:
        int: The number of frames in the input video file.
    """
    import subprocess
    command_query_container = 'ffprobe -v error -select_streams v:0 -show_entries stream=nb_frames -of default=nokey=1:noprint_wrappers=1'.split(
        ' ')
    command_query_container.append(filename)
    command_count = 'ffprobe -v error -count_frames -select_streams v:0 -show_entries stream=nb_read_frames -of default=nokey=1:noprint_wrappers=1'.split(
        ' ')
    command_count.append(filename)
    command = command_query_container if fast else command_count

    process = subprocess.Popen(
        command, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, universal_newlines=True)
    try:
        out, err = process.communicate(timeout=10)
    except subprocess.TimeoutExpired:
        process.kill()
        out, err = process.communicate()

    if err:
        raise FFprobeError(err)

    elif out:
        if out.splitlines()[-1].find("No such file or directory") != -1:
            raise FileNotFoundError(out.splitlines()[-1])
        elif out.startswith("N/A"):
            if fast:
                return get_framecount(filename, fast=False)
            else:
                raise FFprobeError(
                    "Could not count frames. (Is this a video file?) If you are working with audio file use MgAudio instead.")
        else:
            return int(out)

    else:
        if fast:
            return get_framecount(filename, fast=False)
        else:
            raise FFprobeError(
                "Could not count frames. (Is this a video file?). If you are working with audio file use MgAudio instead.")

def get_fps(filename):
    """
    Gets the FPS (frames per second) value of a video using FFprobe.

    Args:
        filename (str): Path to the video file to measure.

    Returns:
        float: The FPS value of the input video file.
    """
    out = ffprobe(filename)
    out_array = out.splitlines()
    video_stream = None
    at_line = -1
    while video_stream == None:
        video_stream = out_array[at_line] if out_array[at_line].find(
            "Video:") != -1 else None
        at_line -= 1
        if at_line < -len(out_array):
            raise NoStreamError(
                "No video stream found. (Is this a video file?)")
    video_stream_array = video_stream.split(',')
    fps = None
    at_chunk = -1
    while fps == None:
        fps = float(video_stream_array[at_chunk].split(
            ' ')[-2]) if video_stream_array[at_chunk].split(' ')[-1] == 'fps' else None
        at_chunk -= 1
        if at_chunk < -len(video_stream_array):
            raise FFprobeError("Could not fetch FPS.")
    return fps

def get_first_frame_as_image(filename, target_name=None, pict_format='.png', overwrite=False):
    """
    Extracts the first frame of a video and saves it as an image using ffmpeg.

    Args:
        filename (str): Path to the input video file.
        target_name (str, optional): The name for the output image. Defaults to None (which assumes that the input filename should be used).
        pict_format (str, optional): The format to use for the output image. Defaults to '.png'.
        overwrite (bool, optional): Whether to allow overwriting existing files or to automatically increment target filename to avoid overwriting. Defaults to False.

    Returns:
        str: Path to the output image file.
    """

    import os
    of = os.path.splitext(filename)[0]

    if target_name == None:
        target_name = of + pict_format
    if not overwrite:
        target_name = generate_outfilename(target_name)

    pass_if_container_is(pict_format, target_name)

    cmd = ' '.join(['ffmpeg', '-y', '-i', wrap_str(filename),
                    '-frames', '1', wrap_str(target_name)])

    os.system(cmd)

    return target_name

def get_box_video_ratio(filename, box_width=800, box_height=600):
    """
    Gets the box-to-video ratio between an arbitrarily defind box and the video dimensions. Useful to fit windows into a certain area.

    Args:
        filename (str): Path to the input video file.
        box_width (int, optional): The width of the box to fit the video into.
        box_height (int, optional): The height of the box to fit the video into.

    Returns:
        int: The smallest ratio (ie. the one to use for scaling the video window to fit into the box).
    """

    video_width, video_height = get_widthheight(filename)

    ratio_x, ratio_y = clamp(box_width / video_width,
                             0, 1), clamp(box_height / video_height, 0, 1)

    smallest_ratio = sorted([ratio_x, ratio_y])[0]

    if smallest_ratio < 1:
        smallest_ratio *= 0.9

    return smallest_ratio

def audio_dilate(filename, dilation_ratio=1, target_name=None, overwrite=False):
    """
    Time-stretches or -shrinks (dilates) an audio file using ffmpeg.

    Args:
        filename (str): Path to the audio file to dilate.
        dilation_ratio (float, optional): The source file's length divided by the resulting file's length. Defaults to 1.
        target_name (str, optional): The name of the output video. Defaults to None (which assumes that the input filename with the suffix "_dilated" should be used).
        overwrite (bool, optional): Whether to allow overwriting existing files or to automatically increment target filename to avoid overwriting. Defaults to False.

    Returns:
        str: The path to the output audio file.
    """

    import os
    of, fex = os.path.splitext(filename)

    if target_name == None:
        target_name = of + '_dilated' + fex
    if not overwrite:
        target_name = generate_outfilename(target_name)

    pass_if_containers_match(filename, target_name)

    cmds = ' '.join(['ffmpeg', '-y', '-i', wrap_str(filename), '-codec:a', 'pcm_s16le',
                     '-filter:a', 'atempo=' + str(dilation_ratio), wrap_str(target_name)])
    os.system(cmds)
    return target_name

def embed_audio_in_video(source_audio, destination_video, dilation_ratio=1):
    """
    Embeds an audio file as the audio channel of a video file using ffmpeg.

    Args:
        source_audio (str): Path to the audio file to embed.
        destination_video (str): Path to the video file to embed the audio file in.
        dilation_ratio (float, optional): The source file's length divided by the resulting file's length. Defaults to 1.
    """

    import os
    of, fex = os.path.splitext(destination_video)

    # dilate audio file if necessary (ie. when skipping)
    if dilation_ratio != 1:
        audio_to_embed = audio_dilate(
            source_audio, dilation_ratio)  # creates '_dilated.wav'
        dilated = True
    else:
        audio_to_embed = source_audio
        dilated = False

    # embed audio in video
    outname = of + '_w_audio' + fex
    cmds = ' '.join(['ffmpeg', '-y', '-i', wrap_str(destination_video), '-i', wrap_str(audio_to_embed), '-c:v',
                     'copy', '-map', '0:v:0', '-map', '1:a:0', '-shortest', wrap_str(outname)])
    os.system(cmds)  # creates '_w_audio.avi'

    # cleanup:
    # if we needed to create an additional (dilated) audio file, delete it
    if dilated:
        os.remove(audio_to_embed)
    # replace (silent) destination_video with the one with the embedded audio
    os.remove(destination_video)
    os.rename(outname, destination_video)

class FFmpegError(Exception):
    def __init__(self, message):
        self.message = message

def ffmpeg_cmd(command, total_time, pb_prefix='Progress', print_cmd=False, stream=True):
    """
    Run an ffmpeg command in a subprocess and show progress using an MgProgressbar.

    Args:
        command (list): The ffmpeg command to execute as a list. Eg. ['ffmpeg', '-y', '-i', 'myVid.mp4', 'myVid.mov']
        total_time (float): The length of the output. Needed mainly for the progress bar.
        pb_prefix (str, optional): The prefix for the progress bar. Defaults to 'Progress'.
        print_cmd (bool, optional): Whether to print the full ffmpeg command to the console before executing it. Good for debugging. Defaults to False.
        stream (bool, optional): Whether to have a continuous output stream or just (the last) one. Defaults to True (continuous stream).

    Raises:
        KeyboardInterrupt: If the user stops the process.
        FFmpegError: If the ffmpeg process was unsuccessful.
    """
    import subprocess
    pb = MgProgressbar(total=total_time, prefix=pb_prefix)

    # hide banner
    command = ['ffmpeg', '-hide_banner'] + command[1:]

    if print_cmd:
        if type(command) == list:
            print(' '.join(command))
        else:
            print(command)

    process = subprocess.Popen(
        command, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, universal_newlines=True)
    returncode = None
    all_out = ''

    try:
        while True:

            if stream:
                out = process.stdout.readline()
            else:
                out = process.stdout.read()
            all_out += out
            if out == '':
                process.wait()
                returncode = process.returncode
                break
            elif out.startswith('frame='):
                out_list = out.split()
                time_ind = [elem.startswith('time=')
                            for elem in out_list].index(True)
                time_str = out_list[time_ind][5:]
                time_sec = str2sec(time_str)
                pb.progress(time_sec)

        if returncode in [None, 0]:
            pb.progress(total_time)
        else:
            raise FFmpegError(all_out)

    except KeyboardInterrupt:
        try:
            process.terminate()
        except OSError:
            pass
        process.wait()
        raise KeyboardInterrupt

def str2sec(time_string):
    """
    Converts a time code string into seconds.

    Args:
        time_string (str): The time code to convert. Eg. '01:33:42'.

    Returns:
        float: The time code converted to seconds.
    """
    elems = [float(elem) for elem in time_string.split(':')]
    return elems[0]*3600 + elems[1]*60 + elems[2]

def wrap_str(string, matchers=[" ", "(", ")"]):
    """
    Wraps a string in double quotes if it contains any of `matchers` - by default: space or parentheses.
    Useful when working with shell commands.

    Args:
        string (str): The string to inspect.
        matchers (list, optional): The list of characters to look for in the string. Defaults to [" ", "(", ")"].

    Returns:
        str: The (wrapped) string.
    """

    matchers = [" ", "(", ")"]

    if any(True for char in string if char in matchers) and '"' not in [string[0], string[-1]]:
        return '"' + string + '"'
    else:
        return string

def unwrap_str(string):
    """
    Unwraps a string from quotes.

    Args:
        string (str): The string to inspect.

    Returns:
        str: The (unwrapped) string.
    """
    if '"' in [string[0], string[-1]]:
        return string[1:-1]
    elif "'" in [string[0], string[-1]]:
        return string[1:-1]
    else:
        return string

def in_colab():
    """
    Check's if the environment is a Google Colab document.

    Returns:
        bool: True if the environment is a Colab document, otherwise False.
    """
    result = None
    try:
        result = 'google.colab' in str(get_ipython())
    except NameError:
        result = False
    return result

{'datetime': 'datetime.datetime', 'sys': 'sys', 'shutil': 'shutil'}


import flask
from xml_sitemap_generator.job import main
from jinja2 import Template

##### **** APPLICATION **** #####

app = flask.Flask(__name__)

@app.route('/')
def index():
    # html with link to /load page

    return flask.render_template('index.html')

@app.route('/load', methods=['GET', 'POST'])
def load_data_to_database_csv():
  url = '/load'
  html = f"""
  <form action="{url}" method="post" enctype="multipart/form-data">
    <input type="file" name="file">
    <input type="submit" value="Upload">
  </form>
  """

  # if request is GET then load html
  if flask.request.method == 'GET':
    return html

  # if  request is POST then load data to database
  if flask.request.method == 'POST':

    file = flask.request.files['file']

    list_of_filenames = main(file)

    print(list_of_filenames)

    # list_of_filenames to html template
    html = Template('''

            {% for filename in list_of_filenames %}

                    {{ filename }}

            {% endfor %}

    ''')

    return html.render(list_of_filenames = list_of_filenames)

if __name__ == "__main__":
    app.run(debug=True)


from django.shortcuts import render
import json, requests

def home(request):
    try:
        response = requests.get("https://api.chucknorris.io/jokes/random")
        json_data = json.loads(response.text)
        data = {
            'frase': json_data,
        }
        return render(request, 'api/home.html', data)
    except:
        return render(request, 'api/erro.html')

def search(request):
    try:
        data = {}
        data['total'] = -1
        data['erro'] = ""
        form = request.GET
        if form and form['key'] != "":
            link = "https://api.chucknorris.io/jokes/search?query="+form['key']
            response = requests.get(link)
            json_data = json.loads(response.text)
            data['total'] = json_data['total']
            data['busca'] = json_data['result']
            if data['total'] == 0:
                data['erro'] = "Unfortunately not found!"
        return render(request, 'api/search.html', data)
    except:
        return render(request, 'api/erro.html')


import torch
import random
import torchvision
import torchvision.transforms as transforms
import torch.nn as nn
import torch.nn.functional as F
import torch.optim as optim
import matplotlib.pyplot as plt
import numpy as np
import argparse
import os
from utils import on_device

def epoch_train(networks,optimizers,num_models,num_preds,trainloader):

    criterion = nn.CrossEntropyLoss()
    for j in range(num_models):
        networks[j]=networks[j].train()

    for data in trainloader:

        assgn_ls = []

        for _ in range(num_models):
            assgn_ls.append([])

        inputs, labels = data

        sample_size = inputs.size()[0]

        inputs,labels = on_device(inputs,labels,num_models)

        for op in optimizers:
            op.zero_grad()

        logits_list = [networks[j](inputs[j]) for j in range(num_models)]

        for b in range(sample_size):

            with torch.no_grad():

                loss_ls = [criterion(torch.unsqueeze(logits_list[j][b,:],dim=0), torch.unsqueeze(labels[j][b],dim=0)) for j in range(num_models)]

            _, min_index_ls = torch.topk(-(torch.tensor(loss_ls)),num_preds)

            for index in min_index_ls:

                assgn_ls[index].append(b)

        for m,assgn in enumerate(assgn_ls):
            if(len(assgn)!=0):

                if(len(assgn)>1):
                    loss = criterion(logits_list[m][assgn,:], labels[m][assgn])
                else:

                    loss = criterion(torch.unsqueeze(logits_list[m][assgn[0],:],dim=0), torch.unsqueeze(labels[m][assgn[0]],dim=0))

                loss.backward()
                optimizers[m].step()

def epoch_val(networks,num_models,testloader):

    criterion = nn.CrossEntropyLoss()
    for j in range(num_models):
        networks[j]=networks[j].eval()

    correct = 0
    total = 0
    val_loss = 0.0

    with torch.no_grad():
        for data in testloader:

            inputs, labels = data

            inputs = inputs.cuda()
            labels = labels.cuda()

            logits_list = [networks[j](inputs) for j in range(num_models)]
            loss_ls = [criterion(logits_list[j],labels) for j in range(num_models)]
            preds_ls = [torch.max(logits_list[j].data, 1)[1].item() for j in range(num_models)]

            value = torch.min(torch.tensor(loss_ls))

            val_loss = val_loss + value.item()

            if(labels.item() in preds_ls):
                correct = correct + 1

            total += labels.size(0)

        return [(100 * (correct / total)), (val_loss/total)]


import logging
import uuid

from rest_framework import status
from rest_framework.response import Response
from rest_framework.views import exception_handler

logger = logging.getLogger(__name__)

def custom_exception_handler(exc, context):
    # Call REST framework's default exception handler first,
    # to get the standard error response.
    response = exception_handler(exc, context)

    if response is None:
        exception_identifier = uuid.uuid4()
        logger.exception(f'Unexpected REST API error: {exception_identifier}')
        return Response(
            data={
                'detail': 'Unexpected server error. '
                f'Refer to error {exception_identifier} in the server logs.'
            },
            status=status.HTTP_500_INTERNAL_SERVER_ERROR,
        )
    return response


#!/usr/bin/python
#
# Tests the functionality of the convertnum esh plugin.
# Written for CS 3214 Spring 2015.
#
# To run this test on your own shell, simply run:
#
# python /web/courses/cs3214/spring2015/projects/student-plugins/jareds94_coreym94/convertnum/convertnum_test.py eshoutput.py
#
# from the directory in which your "esh" and your "eshoutput.py" is located.
#
# @author jareds94
# @author coreym94

##########
# Set up #
##########

import sys, imp, atexit, os
sys.path.append("/home/courses/cs3214/software/pexpect-dpty/");
import pexpect, shellio, signal, time, os, re, proc_check

# Determine the path this file is in
thisdir = os.path.dirname(os.path.realpath(__file__))

#Ensure the shell process is terminated
def force_shell_termination(shell_process):
    c.close(force=True)

# pulling in the regular expression and other definitions
# this should be the eshoutput.py file of the hosting shell, see usage above
definitions_scriptname = sys.argv[1]
def_module = imp.load_source('', definitions_scriptname)

# you can define logfile=open("log.txt", "w") in your eshoutput.py if you want logging!
logfile = None
if hasattr(def_module, 'logfile'):
    logfile = def_module.logfile

#spawn an instance of the shell, note the -p flags
c = pexpect.spawn(def_module.shell, drainpty=True, logfile=logfile, args=['-p', thisdir])

atexit.register(force_shell_termination, shell_process=c)

# set timeout for all following 'expect*' calls to 2 seconds
c.timeout = 2

################
# Actual tests #
################

# ensure that shell prints expected prompt
assert c.expect(def_module.prompt) == 0, "Shell did not print expected prompt (1)"

# Tests invalid arguement  --help
c.sendline("convertnum")
assert c.expect("convertnum: incorrect arguments, use --help") == 0, \
	"expected convertnum %s" % ('convertnum: incorrect arguments, use --help')

#Tests the command with no flags, default
c.sendline("convertnum 32")
assert c.expect('32 Bit Binary Value: 00000000000000000000000000100000\r\nHexadecimal Value: 0x00000020') == 0, \
    "expected convertnum %s" % ('32 Bit Binary Value: 00000000000000000000000000100000\r\nHexadecimal Value: 0x00000020')

#Tests the command with -32
c.sendline("convertnum 28 -32")
assert c.expect('32 Bit Binary Value: 00000000000000000000000000011100') == 0, \
    "expected convertnum %s" % ('32 Bit Binary Value: 00000000000000000000000000011100')

#Tests the -16 option
c.sendline("convertnum 28 -16")
assert c.expect('16 Bit Binary Value: 0000000000011100') == 0, \
    "expected convertnum %s" % ('16 Bit Binary Value: 0000000000011100')

#Tests the -8 option
c.sendline("convertnum 28 -8")
assert c.expect('8 Bit Binary Value: 00011100') == 0, \
    "expected convertnum %s" % ('8 Bit Binary Value: 00011100')

#Tests the -h option
c.sendline("convertnum 28 -h")
assert c.expect('Hexadecimal Value: 0x0000001C') == 0, \
    "expected convertnum %s" % ('Hexadecimal Value: 0x0000001C')

#Error check if invalid information
c.sendline("convertnum cmac")
assert c.expect('convertnum: incorrect arguments, use --help') == 0, \
    "expected convertednum %s" % ('convertnum: incorrect arguments, use --help')

#Exit the test
shellio.success()


import textwrap
import config
import time

class StoryGameScreen:  # a class that constructs a single instance of the game screen
    # below a dictionary declaration
    # there are seven entries - each one for every line on the game screen
    gameScreenStringDictionary = {'string0': '',
                                  'string1': '',
                                  'string2': '',
                                  'string3': '',
                                  'string4': '',
                                  'string5': '',
                                  'string6': '',
                                  'left': '',
                                  'right': '',
                                  'leftFlag': 0,
                                  'rightFlag': 0}

    currentStoryString = " "
    currentLeftChoice = " "
    currentRightChoice = " "

    string0 = gameScreenStringDictionary['string0']
    string1 = gameScreenStringDictionary['string1']
    string2 = gameScreenStringDictionary['string2']
    string3 = gameScreenStringDictionary['string3']
    string4 = gameScreenStringDictionary['string4']
    string5 = gameScreenStringDictionary['string5']
    string6 = gameScreenStringDictionary['string6']
    left = gameScreenStringDictionary['left']
    right = gameScreenStringDictionary['right']

    def __init__(self, string0, string1, string2, string3, string4, string5, string6, left, right):
        self.string0 = string0
        self.string1 = string1
        self.string2 = string2
        self.string3 = string3
        self.string4 = string4
        self.string5 = string5
        self.string6 = string6
        self.left = left
        self.right = right

    @staticmethod
    def current_screen_setter():
        setattr(currentScreen, 'string0', StoryGameScreen.gameScreenStringDictionary.get("string0"))
        setattr(currentScreen, 'string1', StoryGameScreen.gameScreenStringDictionary.get("string1"))
        setattr(currentScreen, 'string2', StoryGameScreen.gameScreenStringDictionary.get("string2"))
        setattr(currentScreen, 'string3', StoryGameScreen.gameScreenStringDictionary.get("string3"))
        setattr(currentScreen, 'string4', StoryGameScreen.gameScreenStringDictionary.get("string4"))
        setattr(currentScreen, 'string5', StoryGameScreen.gameScreenStringDictionary.get("string5"))
        setattr(currentScreen, 'string6', StoryGameScreen.gameScreenStringDictionary.get("string6"))
        setattr(currentScreen, 'left', StoryGameScreen.gameScreenStringDictionary.get("left"))
        setattr(currentScreen, 'right', StoryGameScreen.gameScreenStringDictionary.get("right"))

    @staticmethod
    def current_screen_updater():
        StoryGameScreen.currentScreen = StoryGameScreen(currentScreen.string0,
                                                        currentScreen.string1,
                                                        currentScreen.string2,
                                                        currentScreen.string3,
                                                        currentScreen.string4,
                                                        currentScreen.string5,
                                                        currentScreen.string6,
                                                        currentScreen.left,
                                                        currentScreen.right)
        return StoryGameScreen.currentScreen

    # a function that slices the string into string displayed on one of the seven lines
    @staticmethod
    def stringChopper(stringToChop, leftChoice, rightChoice):
        j = 0  # a counter used to point to a specific entry in the dictionary
        global gameScreenStringDictionary  # accessing the global dictionary - function returns implicitly

        if len(stringToChop) > 95:  # for strings longer than one line
            StoryGameScreen.gameScreenStringDictionary["left"] = leftChoice
            StoryGameScreen.gameScreenStringDictionary["right"] = rightChoice
            choppedStringList = textwrap.wrap(stringToChop, width=95)  # using the text wrap to return list of lines
            for stringLine in choppedStringList:  # assign entries from the generated list to the dictionary
                StoryGameScreen.gameScreenStringDictionary["string{}".format(j)] = stringLine
                j += 1
        elif len(stringToChop) <= 95:  # for short strings just assign to the first dictionary entry
            StoryGameScreen.gameScreenStringDictionary["string0"] = stringToChop
            StoryGameScreen.gameScreenStringDictionary["left"] = leftChoice
            StoryGameScreen.gameScreenStringDictionary["right"] = rightChoice

    @staticmethod
    def dictionaryCleaner():
        global gameScreenStringDictionary
        StoryGameScreen.gameScreenStringDictionary.clear()

chosenLeft = False  # flags for left and rights choices
chosenRight = False
currentStoryKey = "start"  # stores the current key of the story dictionary
currentLeftKey = " "
currentRightKey = " "
currentStoryString = ""  # stores the current story strings
currentLeftChoice = ""
currentRightChoice = ""
storyLog = ['start']  # initializes the story log list
screen = 0

# below the initial story string
StoryGameScreen.currentStoryString = "It's been almost 30 months since Marius left with proconsul Julius Caesar's army. You're looking at the sunset as slaves prepare the house for the night."
StoryGameScreen.currentLeftChoice = "Choose: Keep staring"
StoryGameScreen.currentRightChoice = "Choose: Close eyes"
StoryGameScreen.stringChopper(StoryGameScreen.currentStoryString,
                              StoryGameScreen.currentLeftChoice,
                              StoryGameScreen.currentRightChoice)

currentScreen = StoryGameScreen(  # store the story in the dictionary
                                StoryGameScreen.gameScreenStringDictionary['string0'],
                                StoryGameScreen.gameScreenStringDictionary['string1'],
                                StoryGameScreen.gameScreenStringDictionary['string2'],
                                StoryGameScreen.gameScreenStringDictionary['string3'],
                                StoryGameScreen.gameScreenStringDictionary['string4'],
                                StoryGameScreen.gameScreenStringDictionary['string5'],
                                StoryGameScreen.gameScreenStringDictionary['string6'],
                                StoryGameScreen.gameScreenStringDictionary['left'],
                                StoryGameScreen.gameScreenStringDictionary['right'])

def sendToChopper(string, left, right):  # sends strings to the chopper
    StoryGameScreen.stringChopper(string,
                                  left,
                                  right)

# functions controls the flow of story strings
def takeCurrentStoryString():
        # a nested function that assigns proper story strings to keys
        def dictionarySetter(currentStoryKey, currentLeftKey, currentRightKey):
            global screen
            StoryGameScreen.currentStoryString = storyStringDictionary[currentStoryKey]
            StoryGameScreen.currentLeftChoice = storyStringDictionary[currentLeftKey]
            StoryGameScreen.currentRightChoice = storyStringDictionary[currentRightKey]
            screen += 1

        # ---- declarations ---------------------------------------------------------------------------------------
        global storyStringDictionary
        global currentStoryKey
        global currentLeftKey
        global currentRightKey
        global currentStoryString
        global currentLeftChoice
        global currentRightChoice
        global screen

        # dictionary for the actual story strings
        # each strings in a one story screen
        # strings are represented by Y&Z coordinates
        # storyStringDictionary = {
        #     'x01y00Left': 'left2',
        #     'x01y00Right': 'right2',
        #     'x01y00Story': 'eyes hurt',
        #     'x00y01Story': 'slaves working',
        #     'x00y01Left': 'left3',
        #     'x00y01Right': 'right3'
        # }

        # ---- story strings --------------------------------------------------------------------------------------
        # -- each function tests the current story string and picks a new one accordingly
        if currentStoryKey == 'x01y00Left':
            currentStoryKey = 'x01y00Story'
            currentLeftKey = 'x01y00Left'
            currentRightKey = 'x01y00Right'

        if currentStoryKey == 'x00y01Right':
            currentStoryKey = 'x00y01Story'
            currentLeftKey = 'x00y01Left'
            currentRightKey = 'x00y01Right'

        if currentStoryKey == 'x01y00Story' and screen == 2:
            currentStoryKey = 'x02y00Story'
            currentLeftKey = 'x02y00Left'
            currentRightKey = 'x02y00Right'

        if currentStoryKey == 'x00y01Story' and screen == 2:
            currentStoryKey = 'x00y02Story'
            currentLeftKey = 'x00y02Left'
            currentRightKey = 'x00y02Right'

        if currentStoryKey == 'x02y00Story' and screen == 3:
            currentStoryKey = 'x03y00Story'
            currentLeftKey = 'x03y00Left'
            currentRightKey = 'x03y00Right'

        if currentStoryKey == 'x00y02Story' and screen == 3:
            currentStoryKey = 'x00y03Story'
            currentLeftKey = 'x00y03Left'
            currentRightKey = 'x00y03Right'

        if currentStoryKey == 'x03y00Story' and screen == 4:
            currentStoryKey = 'x04y00Story'  # first ending
            currentLeftKey = 'x04y00Left'
            currentRightKey = 'x04y00Right'

        if currentStoryKey == 'x00y03Story' and screen == 4:
            currentStoryKey = 'x00y04Story'
            currentLeftKey = 'x00y04Left'
            currentRightKey = 'x00y04Right'

        if currentStoryKey == 'x00y04Story' and screen == 5:
            currentStoryKey = 'x05y00Story'
            currentLeftKey = 'x05y00Left'
            currentRightKey = 'x05y00Right'
            config.chosenLeft = False

        if currentStoryKey == 'x00y04Story' and screen == 5:
            currentStoryKey = 'x00y05Story'
            currentLeftKey = 'x00y05Left'
            currentRightKey = 'x00y05Right'
            config.chosenRight = False

        if (currentStoryKey == 'x00y05Story' or currentStoryKey == 'x05y00Story') and screen == 6:
            currentStoryKey = 'x06y00Story'
            currentLeftKey = 'x06y00Left'
            currentRightKey = 'x06y00Right'

        if currentStoryKey == 'x06y00Story' and screen == 7:
            currentStoryKey = 'x07y00Story'
            currentLeftKey = 'x07y00Left'
            currentRightKey = 'x07y00Right'

        if currentStoryKey == 'x07y00Story' and screen == 8:
            currentStoryKey = 'x08y00Story'
            currentLeftKey = 'x08y00Left'
            currentRightKey = 'x08y00Right'

        if currentStoryKey == 'x08y00Story' and screen == 9 and config.chosenLeft == True:
            currentStoryKey = 'x09y00Story'  # second ending
            currentLeftKey = 'x09y00Left'
            currentRightKey = 'x09y00Right'
            config.chosenLeft = False

        if currentStoryKey == 'x08y00Story' and screen == 9 and config.chosenRight == True:
            currentStoryKey = 'x00y09Story'
            currentLeftKey = 'x00y09Left'
            currentRightKey = 'x00y09Right'
            config.chosenLeft = False

        config.chosenLeft = False
        config.chosenRight = False

        dictionarySetter(currentStoryKey, currentLeftKey, currentRightKey)

        if storyLog[len(storyLog) - 1] != currentStoryKey:  # adds to the story log while avoiding duplicates
            storyLog.append(currentStoryKey)

storyStringDictionary = {
    'x01y00Left': 'Ask him in',
    'x01y00Right': 'Go to the gate',
    'x01y00Story': 'Your eyes hurt but you can\'t turn your head away. The pain helps your forget about everything: loneliness, burdens of managing the farm without Marius, due taxes that are meant to be paid with Marius\' war spoils. An old slaves approaches you and says "Lady, there is a centurion at the gate asking to speak with you".',

    'x00y01Story': 'You go back to your memories. Last day before Marius left with the army. He didn\'t let slave clean his armor or tend to his horse. He had spent hours getting ready for the departure. It is easier to wage war than to tell goodbye. An old slaves approaches you and says "Lady, there is a centurion at the gate asking to speak with you".',
    'x00y01Left': 'Ask him in',
    'x00y01Right': 'Go to the gate',

    'x02y00Left': 'Pray to Jupiter',
    'x02y00Right': 'Pray to Mars',
    'x02y00Story': '"Let him into the atrium" - you command - "I will meet him there". Instantly you\'re petrified. You had felt it: god\'s laughter trembling in the distance. Centurion\'s visit could mean one thing: something had happened to Marius. You wait for few moments as you don\'t want to seem desperate. You take a deep breathe and head to the atrium.',

    'x00y02Left': 'Pray to Jupiter',
    'x00y02Right': 'Pray to Mars',
    'x00y02Story': '"Did he say what is the reason of the visit at this time?" - you shout at the slave - "No, my lady. Lord centurion just asked to speak with you" - answers the old man with his eyes pointing to the ground. Your heart is pounding as you know what it means - oficer visiting woman\'s house at this time is to deliver grief news. You rush to the gate',

    'x03y00Left': 'May be the god\'s will',
    'x03y00Right': 'May be the god\'s will',
    'x03y00Story': 'You\'re managing to keep a straight face as you enter the atrium. Centurion is standing with his arms crossed looking at the house altar. He is dirty, he\'s legs are covered in mud, he seems tired as he clearly spent weeks on a horseback. Centurions\'s posture gets dignified and muscles tight when he notices you. Your eyes meet. "I am sorry, my lady" - he says - "for interrupting at this late time. I come to deliver the sad news. Centurion Marius had choined our brave heroes in Elysium when serving under great Consul Julius. Hail to hero Marius!" - he shouts. After a moment Centurion adds quietely - "I am sorry, my lady"',

    'x00y03Left': 'NO!!!',
    'x00y03Right': 'STOP!',
    'x00y03Story': 'Your robe is waving as you run to the household gate. Centurion is leaning against the stone wall while your slaves give food and water to his tired horse. He is dirty, he\'s legs are covered in mud, he seems tired as he clearly spent weeks on a horseback. Centurions\'s posture gets dignified and muscles tight when he notices you. Your eyes meet. "I am sorry, my lady" - he says - "for interrupting at this late time. I come to deliver the sad news. Centurion Marius had choined our brave heroes in Elysium when serving under great Consul Julius. Hail to hero Marius!" - he shouts. After a moment Centurion adds quietely - "I am sorry, my lady"',

    # first ending
    'x04y00Left': 'May be the god\'s will',
    'x04y00Right': 'May be the god\'s will',
    'x04y00Story': 'Marius\' body was never recovered. After the funeral ceremony Senate had paid honors to your brave husband and for a long time tales of his bravery were told by folks. You had lived a long file, farm and house had prospered under your wise hands. The rest of your life was lonely, though. You were never able to find peace. But what else could you do? And so may be the god\'s will',

    'x00y04Left': 'Threaten the gods',
    'x00y04Right': 'Stab yourself',
    'x00y04Story': 'Centurion is shocked by your outbreak - "I am sorry for your loss, my lady but please, do not spoil your husbands heroic name!" - he shouts. You are not listenning to this nonsens, you will not be gods\' toy. In fury you wrench Centurion\'s sword from a slave\'s hand and...',

    'x05y00Left': '...',
    'x05y00Right': '...',
    'x05y00Story': '"I will not let you take my husband away!" - you scream to the setting sun with the sword in your hand. - "I defy you, I defy you with your laws and games! Give me back my husband or I will hunt you down and make you pay for what you had done!" - you yell as the darknest clenches you.',

    'x00y05Left': '...',
    'x00y05Right': '...',
    'x00y05Story': '"I will not let you take my husband away!" - you scream to the setting sun with the sword in your hand. - "I command you to take me to my husband! I will rip him out of your claws!" - you scream as you press the sword firmly against your chest.',

    'x06y00Left': 'Who...',
    'x06y00Right': 'Where...',
    'x06y00Story': 'You wake up in a complete darkness and feel wet, cold stone beneath. You cannot hear or smell anything. Before you are able to gather your thoughts a giant, glowing spectre appears in front of you and you here its voice - "Quite a show out there"- it says.',

    'x00y06Left': '...',
    'x00y06Right': '...',
    'x00y06Story': '',

    'x07y00Left': 'Wait!',
    'x07y00Right': 'Am I...',
    'x07y00Story': '"No, no, no. We have just met and there are already too many questions" - you can sens the irritation in its voice. - "I will give you the basics around here. My name is Uf, simply Uf. Your name is inrelevant. You thouht you could challenge the gods and here you are. Uf will be your guide throught the misery" - said the specter and disappeared.',

    'x00y07Left': '...',
    'x00y07Right': '...',
    'x00y07Story': '',

    'x08y00Left': 'Wait for Uf',
    'x08y00Right': 'Just go',
    'x08y00Story': 'Once again your are left in the darkness. You check your pulse but you can\'t feel anything. You do not feel like being dead, however, you are not certain how being dead feels. You wait for a brief moment, wet stone beneath you does not get warmer but you are almost sure you can feel your own weight and body. What you will do?',

    'x00y08Left': '...',
    'x00y08Right': '...',
    'x00y08Story': '',

    # second ending
    'x09y00Left': 'A little bit longer',
    'x09y00Right': 'Just a moment',
    'x09y00Story': 'You wait for Uf to come back. Surely, it will be back any moment. You think about your farm and slaves, they need attention and guidance. You hope that Marius will step out of the darkness, grab your hand and take you back home.',

    'x00y09Left': 'Call Uf',
    'x00y09Right': 'Call Marius',
    'x00y09Story': 'You slow stand up in the darkness and focus to catch some sound or smell that might help you understand the surroundings. There is nothing no matter how hard you try. Nothing apart from the wet stone on the ground. But for some reason you feel like you cannot stay here.',
}

def assignStringsAfterLoad():
    StoryGameScreen.currentStoryString = currentStoryKey
    StoryGameScreen.currentLeftChoice = currentLeftKey
    StoryGameScreen.currentRightChoice = currentRightKey
    StoryGameScreen.stringChopper(StoryGameScreen.currentStoryString,
                                  StoryGameScreen.currentLeftChoice,
                                  StoryGameScreen.currentRightChoice)


from typing import Iterable

import gams
import pandas as pd
from six import string_types

def all_na(x):
  """Returns bool of whether a series or scalar consists of all NAs"""
  if is_iterable(x):
    return all(pd.isna(x))
  else:
    return pd.isna(x)

def index_names_from_symbol(symbol):
  """
  Return the domain names of a GAMS symbol,
  except ['*'] cases are replaced by the name of the symbol
  and ['*',..,'*'] cases are replaced with ['index_0',..'index_n']
  """
  index_names = list(symbol.domains_as_strings)
  if index_names == ["*"]:
    return [symbol.name]
  if index_names.count("*") > 1:
    for i, name in enumerate(index_names):
      if name == "*":
        index_names[i] = f"index_{i}"
  return index_names

def index_from_symbol(symbol):
  """Return a Pandas Index based on the records and domain names of a GAMS symbol."""
  if len(symbol.domains_as_strings) > 1:
    keys = map_to_int_where_possible([rec.keys for rec in symbol])
    index = pd.MultiIndex.from_tuples(keys, names=index_names_from_symbol(symbol))
    index.name = symbol.name
  elif len(symbol.domains_as_strings) == 1:
    keys = map_to_int_where_possible([rec.keys[0] for rec in symbol])
    index = pd.Index(keys, name=index_names_from_symbol(symbol)[0])
  else:
    return None
  if isinstance(symbol, gams.GamsSet):
    index.text = symbol.text
    index.domains = symbol.domains_as_strings
    index.texts = pd.Series([rec.text for rec in symbol], index, name=symbol.name)
  return index

def symbol_is_scalar(symbol):
  return not symbol.domains_as_strings

def is_iterable(arg):
  return isinstance(arg, Iterable) and not isinstance(arg, string_types)

def map_lowest_level(func, x):
  """Map lowest level of zero or more nested lists."""
  if is_iterable(x):
    return [map_lowest_level(func, i) for i in x]
  else:
    return func(x)

def try_to_int(x):
  """Cast input to int if possible, else return input unchanged."""
  try:
    if str(int(x)) == str(x):
      return int(x)
    else:
      return x
  except ValueError:
    return x

def map_to_int_where_possible(iter):
  """Returns an iterable where each element is converted to an integer if possible for that element."""
  return map_lowest_level(try_to_int, iter)

def merge_symbol_records(series, symbol):
  """Convert Pandas series to records in a GAMS Symbol"""
  if isinstance(symbol, gams.GamsSet):
    attr = "text"
  elif isinstance(symbol, gams.GamsVariable):
    attr = "level"
  elif isinstance(symbol, gams.GamsParameter):
    attr = "value"
  for k, v in series.items():
    setattr(symbol.merge_record(k), attr, v)

def fill_missing_combinations(series, sets_database=None, fill_value=pd.NA):
  """
  Return copy of series with all combinations filled with fill_value.
  If a database is supplied we look up the sets in the database, otherwise we only fill set elements already in use.
  """
  sets = [i.unique() for i in series.index.levels[:]]
  if sets_database is not None:
    for i, set_name in enumerate(series.index.names):
      if set_name in sets_database:
        sets[i] = sets_database[set_name]
  all_combinations = pd.MultiIndex.from_product(sets)
  return series.reindex(all_combinations, fill_value=fill_value)


from tqdm import tqdm
import os
from pydub import AudioSegment
import sys
def flac_to_wav(old_path,new_path):
	newAudio = AudioSegment.from_file(old_path)

	newAudio.export(new_path, bitrate ='192k', format ="wav")

def main(old_dir='db/eng-wcp-us_flac/flac/',new_dir='db/english_audio/'):
	try:
		os.mkdir(new_dir)
	except:
		pass
	for file in tqdm(os.listdir(old_dir)[:]):

		if '.flac' in file:
			word = file.split('.')[0].split('-')[-1]
			# print(word)
			old_path = os.path.join(old_dir,file)
			new_path = os.path.join(new_dir , word+'.wav')
			flac_to_wav(old_path,new_path)
			# break

old_dir = sys.argv[1]
new_dir = sys.argv[2]
print('from ',old_dir,' to ',new_dir)
main(old_dir,new_dir)