Пример #1
0
    def test_database_update(self) -> None:
        """ Test that the Update Database functionality on the program works. """
        # Assert that the database doesn't already have the tests table
        with Database() as DB:
            self.assertIsNone(DB.get_tests_table())

        # Update the database using the database file
        sql_file_path = os.path.join(
            os.path.dirname(os.path.realpath(__file__)), 'data',
            'update_db.sql')

        self.update_window.file_path.set(sql_file_path)
        self.update_window.update_button.invoke()

        update_program_controller_loop(self.pc)

        # Assert that the database actually got updated with the sql file
        with Database() as DB:
            self.assertEqual(DB.get_tests_table()[0], "This is a test.")
Пример #2
0
    def _update_database(self) -> None:
        """ Function that runs when the `Update` button is pressed. """
        # TODO: Need to find a way to validate the database update.

        logger.debug(
            f'Retrieving the sql file path from the file path entry box.')
        sql_file = self.file_path.get()

        with Database() as DB:
            DB.update_database_with_sql_file(sql_file)

        logger.debug(f'Database updated.')
        self.update_label.configure(text='Done!')
Пример #3
0
def database_updater(response: RequestsResponseAlias) -> None:
    logger.debug(f'Starting database updater.')
    try:
        newest_db_version = response['files']['db_version.txt']['content']
    except TypeError:
        # TODO: need to let user know that there is no network connected
        return

    if newest_db_version != DB_VERSION:
        for i in range(int(DB_VERSION) + 1, int(newest_db_version) + 1):
            sql_file_name = f'db_update_{str(i)}.sql'
            sql_file_url = response['files'][sql_file_name]['raw_url']
            r = requests.get(sql_file_url)
            open(sql_file_name, 'wb').write(r.content)

            with Database() as DB:
                DB.update_database_with_sql_file(sql_file_name)

            os.remove(sql_file_name)
Пример #4
0
    def __init__(self, parent: ttk.PanedWindow,
                 controller: ProgramController) -> None:
        ttk.Frame.__init__(self,
                           parent,
                           width=100,
                           height=300,
                           relief=tk.RIDGE)

        self.controller = controller

        logger.debug(f'Initializing the MainWindow ttk frame.')

        self.ui_elements()
        # Set the status bar labels to always be on the bottom when opening a new pane window.
        self.rowconfigure(100, weight=1)
        logger.debug(f'Created MainWindow UI elements.')

        with Database() as DB:
            if DB.are_settings_saved():
                self.enable_all_buttons()
Пример #5
0
    def lock_unlock_aws_settings(self) -> None:
        """ This function executes when the 'Lock/Unlock' button is clicked. """
        with Database() as DB:

            # If there is an AWS config already saved in the database,
            # then the program checks if the button says 'Lock' or 'Unlock'
            # If there is no data on the database, then the 'Lock/Unlock' button is disabled
            # and enables the save button
            if DB.are_settings_saved():

                # If the button says 'Lock', then the input fields are disabled, the
                # text is grayed out, the save button is removed, and the button is
                # renamed to 'Unlock'
                if self.lock_unlock_button['text'] == 'Lock':
                    logger.debug(f'Configuration window locked.')

                    self._disable_all_widgets()

                    self.lock_unlock_button['text'] = 'Unlock'

                    self.save_button.grid_remove()

                # If the button says 'Unlock', then the input fields are editable, the
                # text is changed to black, the save button is added to the screen, and
                # the button is renamed to 'Lock'
                elif self.lock_unlock_button['text'] == 'Unlock':
                    logger.debug(f'Configuration window unlocked.')

                    self._enable_all_widgets()

                    self.lock_unlock_button['text'] = 'Lock'

                    self.save_button.grid(self.SAVE_BUTTON_GRID)
            else:
                self.lock_unlock_button.configure(text='Lock',
                                                  state='disabled')
                self.save_button.grid(self.SAVE_BUTTON_GRID)
Пример #6
0
    def populate_setup_fields(self) -> None:
        """ Populate the setup fields with the information from the database. 

        This function checks if there are settings saved in the database. 
            - If there are, then the function will add all those settings into 
            the entry boxes the data corresponds to.
            - If there are no settings saved on the database, then the function
            doesn't do anything to the fields.
        """
        with Database() as DB:
            if DB.are_settings_saved():
                logger.debug(
                    f'Populating setup fields with data from the database.')

                # AWS settings
                aws_config_data = DB.get_aws_config(label=True)

                self.access_key_id_string.set(aws_config_data[0])
                self.secret_key_string.set(aws_config_data[1])
                self.region_name_var.set(aws_config_data[2])

                # FFMPEG settings
                ffmpeg_parameters, file_suffix, aws_different_output_extension, local_save_path, local_different_output_extension = DB.get_ffmpeg_config(
                )

                # Set the ffmpeg_parameters data into the entry box,
                # if there is data for that in the database.
                # If there is no data (it's NULL), then an empty string is added to the entry box.
                self.ffmpeg_input_var.set(
                    ffmpeg_parameters if ffmpeg_parameters is not None else "")

                # Set converted file suffix data into the entry box from the database
                # Since the table schema says that it cannot be NULL then there is no need
                #   to check if the output is NULL before setting it.
                self.converted_file_suffix_input_var.set(file_suffix)

                # If the database's `aws_different_output_extension` field is NULL/None,
                # then the different_ffmpeg_output_extension_checkbutton is unchecked
                if aws_different_output_extension is None:
                    self.use_different_extension.set(0)

                # If that field is not NULL/None, then the
                # different_ffmpeg_output_extension_checkbutton is checked,
                # the extension is set to the entry box, and the entry box is
                # added to the grid.
                else:
                    self.use_different_extension.set(1)
                    self.different_output_extension_var.set(
                        aws_different_output_extension)

                    self.different_ffmpeg_output_extension_input.grid(
                        self.DIFFERENT_EXTENSION_INPUT_GRID)

                # If the database's `local_save_path` field is NULL/None,
                # then the local_save_checkbox is unchecked
                if local_save_path is None:
                    self.local_save_var.set(0)

                # If that field is not NULL/None, then the
                # local_save_checkbox is checked, the path is set to the entry field,
                # and the entry box and button is added to the grid.
                else:
                    self.local_save_var.set(1)
                    self.local_save_path_var.set(local_save_path)

                    self.local_save_path_input_field.grid(
                        self.LOCAL_SAVE_PATH_INPUT_GRID)
                    self.local_save_path_button.grid(
                        self.LOCAL_SAVE_PATH_BUTTON_GRID)

                # If the database's `local_different_output_extension` field is NULL/None,
                # then the local_save_different_extension_checkbox is unchecked
                if local_different_output_extension is None:
                    self.local_save_different_extension_checkbox_var.set(0)

                # If that field is not NULL/None, then the
                # local_save_different_extension_checkbox is checked, the extension is
                # added to the entry box, and the checkbox and entry box are added to the grid.
                else:
                    self.local_save_different_extension_checkbox_var.set(1)
                    self.local_save_different_output_extension_input_var.set(
                        local_different_output_extension)

                    self.local_save_different_extension_checkbox.grid(
                        self.LOCAL_SAVE_OUTPUT_EXTENSION_CHECKBOX_GRID)
                    self.local_save_different_output_extension_input.grid(
                        self.LOCAL_SAVE_OUTPUT_EXTENSION_INPUT_GRID)

                # After adding all the data to the page then all the widgets are disabled
                # and the screen is displayed as if the Lock button was pressed.
                self._disable_all_widgets()
                self.lock_unlock_button['text'] = 'Unlock'
                self.lock_unlock_button.configure(state='normal')

                self.save_button.grid_remove()
Пример #7
0
    def configure_aws(self, access_key_id: str, secret_key: str,
                      region_name: str) -> None:
        """ Function to configure AWS settings.

        This function should be ran on another thread so that the UI elements are
        not stuck.

        Args:
            access_key_id (str): User inputted Access Key ID for AWS.
            secret_key (str): User inputted Secret Key for AWS.
            region_name (str): User selected region name from list.

        Raises:
            AWSAuthenticationException: Exception is raised when the HTTP Status Code 
                from AWS is not 200.

        Catches:
            AWSKeyException: Exception is caught when either the Access Key ID or the 
                Secret Key are wrong.
            AWSAuthenticationException: Exception is caught when the Access Key ID and 
                Secret Key are correct but seem to be inactive.
            NoConnectionError: Exception is caught when there is no Internet connection.
        """
        with Database() as DB:
            try:
                region_name_code = DB.get_region_name_code(region_name)

                response = AWS.test_connection(access_key_id, secret_key,
                                               region_name_code)

                if response['ResponseMetadata']['HTTPStatusCode'] == 200:
                    DB.set_aws_config(access_key_id, secret_key,
                                      region_name_code)

                    # Change setup_window_output_message_variable text to `Settings saved.` and set color to green
                    self.setup_window_output_message_variable.set(
                        'Settings saved.')
                    self.setup_window_output_message.configure(
                        foreground='#3fe03f')

                    # Enable the lock/unlock button.
                    self.lock_unlock_button.configure(state='normal')

                    # Enable all main window buttons
                    self.controller.enable_main_window_buttons()

                    # Run function to lock the settings
                    self.lock_unlock_aws_settings()
                else:
                    raise AWSAuthenticationException

            except AWSKeyException:
                self.setup_window_output_message_variable.set(
                    'ERROR: Access Key ID or Secret Access Key invalid.')
                self.setup_window_output_message.configure(foreground='red')
                logger.error(
                    f'ERROR: Invalid AWS Access Key ID or Secret Access Key entered.'
                )

            except AWSAuthenticationException:
                self.setup_window_output_message_variable.set(
                    'ERROR: Keys are correct but they may be inactive.')
                self.setup_window_output_message.configure(foreground='red')
                logger.error(f'ERROR: Innactive AWS keys entered.')

            except NoConnectionError:
                self.setup_window_output_message_variable.set(
                    'ERROR: No Internet connection detected.')
                self.setup_window_output_message.configure(foreground='red')
                logger.error(
                    f'ERROR: No Internet connection, cannot authenticate AWS keys.'
                )
Пример #8
0
    def save_configuration(self) -> None:
        """ Function that runs when the `Save Configuration` button is pressed. """
        logger.debug(f'Starting save configuration process.')

        if not self._ff_in_path:
            self.setup_window_output_message_variable.set(
                'Did not find ffmpeg or ffprobe on the system PATH.\nPlease install and add ffmpeg and ffprobe to PATH to use this program correctly.\nNo data saved.'
            )
            self.setup_window_output_message.configure(foreground='red')

            return

        self.setup_window_output_message_variable.set('')

        # Get AWS input data
        access_key_id = self.access_key_id_input_field.get()
        secret_key = self.secret_key_input_field.get()
        region_name = self.region_name_var.get()

        # Get FFMPEG input data
        ffmpeg_parameters = self.ffmpeg_input_var.get()

        converted_file_suffix = str(
            self.converted_file_suffix_input.get()).strip()

        use_different_output_extension_for_aws = self.use_different_extension.get(
        )
        output_extension_for_aws = self.different_output_extension_var.get()

        use_local_save = self.local_save_var.get()
        local_save_path = self.local_save_path_var.get()

        use_different_output_extension_for_local = self.local_save_different_extension_checkbox_var.get(
        )
        output_extension_for_local = self.local_save_different_output_extension_input_var.get(
        )

        # Check if the user left the converted file suffix input box empty.
        # If the input is empty then an error message is displayed that it cannot be empty.
        if converted_file_suffix == '':
            self.setup_window_output_message_variable.set(
                'The converted file suffix cannot be empty.')
            self.setup_window_output_message.configure(foreground='red')
            logger.warning(f'No converted file suffix provided.')

        # Check if the user checked the "Different output extension for AWS" box but didn't
        #  input the extension in the entry box.
        # If so, then an error message is displayed.
        if use_different_output_extension_for_aws and output_extension_for_aws == '':
            self.setup_window_output_message_variable.set(
                'The output extension for AWS cannot be empty.')
            self.setup_window_output_message.configure(foreground='red')
            logger.warning(
                f'No extension entered when the `Different output extension for AWS`checkbox was selected.'
            )
            return

        # Check if the user checked the "Save locally" box but didn't input
        #  the path in the entry box.
        # If so, then an error message is displayed.
        if use_local_save and local_save_path == '':
            self.setup_window_output_message_variable.set(
                'The local save path cannot be empty.')
            self.setup_window_output_message.configure(foreground='red')
            logger.warning(
                f'No local save path entered when the `Save locally` checkbox was selected.'
            )
            return

        # Check if the user checked the "Different local output extension" box but didn't
        # input the extension in the entry box.
        # If so, then an error message is displayed.
        if use_different_output_extension_for_local and output_extension_for_local == '':
            self.setup_window_output_message_variable.set(
                'The local output extension cannot be empty.')
            self.setup_window_output_message.configure(foreground='red')
            logger.warning(
                f'No extension entered when the `Different local output extension` checkbox was selected.'
            )
            return

        with Database() as DB:
            # Since SQLite3 requires text values to be surrounded in single quotes
            # when executing an SQL statement,
            # I have to do this hack to get the single quotes around the string variable
            # or pass "NULL" if it's not needed
            DB.set_ffmpeg_config(
                ffmpeg_parameters, converted_file_suffix,
                "'{}'".format(output_extension_for_aws)
                if use_different_output_extension_for_aws else "NULL",
                "'{}'".format(local_save_path) if use_local_save else "NULL",
                "'{}'".format(output_extension_for_local)
                if use_different_output_extension_for_local else "NULL")

        # If any of the AWS inputs is empty, then a message is given
        # Else, a configuration message is given and a new thread is created
        #   to test and save the AWS connection
        if access_key_id == '' or secret_key == '' or region_name == '':
            self.setup_window_output_message_variable.set(
                'All AWS fields must to be filled in.')
            self.setup_window_output_message.configure(foreground='red')
            logger.warning(f'Not all AWS fields are filled in.')
        else:
            self.setup_window_output_message_variable.set(
                'Configuring AWS settings. Please wait.')
            self.setup_window_output_message.configure(foreground='black')

            # Force update any pending tasks before having to configure AWS
            self.controller.update_idletasks()

            # Originally was using threading so that the the program wouldn't get stuck while checking
            # the credentials.
            # Had to make it non-multithreaded because of some weird tkinter errors.
            # The errors were not consistent and happened both when running the tests or running the
            # app by itself.

            # Original code:
            #   threading.Thread(
            #       target=self.configure_aws,
            #       args=(access_key_id, secret_key, region_name)
            #   ).start()

            self.configure_aws(access_key_id, secret_key, region_name)
Пример #9
0
    def ui_elements(self) -> None:
        # Row 0
        self.setup_window_top_label = ttk.Label(
            self,
            text='Please enter all the data.',
            style='setup_window_top_label.TLabel',
            justify=tk.CENTER)
        self.setup_window_top_label.grid(self.TOP_LABEL_GRID)

        # Row 1
        self.setup_window_output_message_variable = tk.StringVar()
        self.setup_window_output_message = ttk.Label(
            self,
            text='',
            style='setup_window_output_message_label.TLabel',
            justify=tk.CENTER,
            textvariable=self.setup_window_output_message_variable)
        self.setup_window_output_message.grid(self.OUTPUT_MESSAGE_GRID)

        # Row 2
        self.aws_heading_label = ttk.Label(self,
                                           text='AWS Settings',
                                           style='aws_heading_label.TLabel',
                                           justify=tk.CENTER)
        self.aws_heading_label.grid(self.AWS_HEADING_GRID)

        # Row 3
        self.access_key_id_label = ttk.Label(
            self,
            text='AWS Access Key ID:',
            style='access_key_id_label.TLabel',
            justify=tk.LEFT)
        self.access_key_id_label.grid(self.ACCESS_KEY_ID_LABEL_GRID)

        self.access_key_id_string = tk.StringVar()
        self.access_key_id_input_field = ttk.Entry(
            self, width=44, textvariable=self.access_key_id_string)
        self.access_key_id_input_field.grid(self.ACCESS_KEY_ID_INPUT_GRID)

        # Row 4
        self.secret_key_label = ttk.Label(self,
                                          text='AWS Secret Access Key:',
                                          style='aws_secret_key_label.TLabel',
                                          justify=tk.LEFT)
        self.secret_key_label.grid(self.SECRET_KEY_LABEL_GRID)

        self.secret_key_string = tk.StringVar()
        self.secret_key_input_field = ttk.Entry(
            self, width=44, textvariable=self.secret_key_string)
        self.secret_key_input_field.grid(self.SECRET_KEY_INPUT_GRID)

        # Row 5
        self.region_name_label = ttk.Label(self,
                                           text='Default Region Name:',
                                           style='region_name_label.TLabel',
                                           justify=tk.LEFT)
        self.region_name_label.grid(self.REGION_NAME_LABEL_GRID)

        with Database() as DB:
            region_labels = DB.get_region_name_labels()

        self.region_name_var = tk.StringVar()
        self.region_name_input_field = ttk.Combobox(
            self,
            textvariable=self.region_name_var,
            values=region_labels,
            width=42,
            state='readonly')
        self.region_name_input_field.grid(self.REGION_NAME_INPUT_GRID)

        # Row 6
        self.ffmpeg_heading_label = ttk.Label(
            self,
            text='FFMPEG Settings',
            style='ffmpeg_heading_label.TLabel',
            justify=tk.CENTER)
        self.ffmpeg_heading_label.grid(self.FFMPEG_HEADING_LABEL_GRID)

        # Row 7
        self.ffmpeg_input_label = ttk.Label(self,
                                            text='FFMPEG parameters: ',
                                            style='ffmpeg_input_label.TLabel',
                                            justify=tk.LEFT)
        self.ffmpeg_input_label.grid(self.FFMPEG_INPUT_LABEL_GRID)

        self.ffmpeg_input_var = tk.StringVar()
        self.ffmpeg_input_var.set('-b:v 64k -bufsize 64k')
        self.ffmpeg_input = ttk.Entry(self,
                                      width=44,
                                      textvariable=self.ffmpeg_input_var)
        self.ffmpeg_input.grid(self.FFMPEG_INPUT_GRID)

        # Row 8
        self.converted_file_suffix_label = ttk.Label(
            self,
            text='Converted file suffix:',
            style='converted_file_suffix_label.TLabel',
            justify=tk.LEFT)
        self.converted_file_suffix_label.grid(
            self.CONVERTED_FILE_SUFFIX_LABEL_GRID)

        self.converted_file_suffix_input_var = tk.StringVar()
        self.converted_file_suffix_input_var.set('_converted')
        self.converted_file_suffix_input = ttk.Entry(
            self, width=20, textvariable=self.converted_file_suffix_input_var)
        self.converted_file_suffix_input.grid(
            self.CONVERTED_FILE_SUFFIX_INPUT_GRID)

        # Row 9
        self.use_different_extension = tk.IntVar()
        self.different_ffmpeg_output_extension_checkbutton = ttk.Checkbutton(
            self,
            text='Different output extension for AWS',
            variable=self.use_different_extension,
            style='regular.TCheckbutton',
            command=self._ffmpeg_extension_checkbutton_press)
        self.different_ffmpeg_output_extension_checkbutton.grid(
            self.DIFFERENT_EXTENSION_CHECKBOX_GRID)

        self.different_output_extension_var = tk.StringVar()
        self.different_output_extension_var.set("avi")
        self.different_ffmpeg_output_extension_input = ttk.Entry(
            self, width=10, textvariable=self.different_output_extension_var)

        # Row 10
        self.local_save_var = tk.IntVar()
        self.local_save_checkbox = ttk.Checkbutton(
            self,
            text='Save locally',
            variable=self.local_save_var,
            style='regular.TCheckbutton',
            command=self._local_save_press)
        self.local_save_checkbox.grid(self.LOCAL_SAVE_CHECKBOX_GRID)

        self.local_save_path_var = tk.StringVar()
        self.local_save_path_input_field = ttk.Entry(
            self, width=36, textvariable=self.local_save_path_var)

        self.local_save_path_button = ttk.Button(
            self, text="Open Folder", command=self._open_folder_path)

        # Row 11
        self.local_save_different_extension_checkbox_var = tk.IntVar()
        self.local_save_different_extension_checkbox = ttk.Checkbutton(
            self,
            text='Different local output extension',
            variable=self.local_save_different_extension_checkbox_var,
            style='regular.TCheckbutton',
            command=self._local_save_different_extension_press)

        self.local_save_different_output_extension_input_var = tk.StringVar()
        self.local_save_different_output_extension_input_var.set("mkv")
        self.local_save_different_output_extension_input = ttk.Entry(
            self,
            width=10,
            textvariable=self.local_save_different_output_extension_input_var)

        # Row 12
        self.ffmpeg_example_label = ttk.Label(
            self,
            text='',
            style='ffmpeg_example_label.TLabel',
            justify=tk.CENTER)
        self.ffmpeg_example_label.grid(self.FFMPEG_EXAMPLE_LABEL)

        # Row 13
        self.save_button = ttk.Button(self,
                                      text="Save Configuration",
                                      style='regular.TButton',
                                      command=self.save_configuration)
        self.save_button.grid(self.SAVE_BUTTON_GRID)

        self.lock_unlock_button = ttk.Button(
            self,
            text='Lock',
            style='regular.TButton',
            command=self.lock_unlock_aws_settings,
            # Disabled since there should be nothing saved on the database at first boot
            state='disabled')
        self.lock_unlock_button.grid(self.LOCK_UNLOCK_BUTTON_GRID)