from ffmpeg import FFmpeg as ffmpeg

input_stream = ffmpeg.input('input.mp4', f='mp4')
output_stream = ffmpeg.output(input_stream, 'output.m3u8', format='hls', start_number=0, hls_time=5, hls_list_size=0)
ffmpeg.run(output_stream)
Esempio n. 2
0
class QtMainWindow(QMainWindow, MainWindow_UI.Ui_MainWindow):
    # todo прикрутить ПКМ меню в списке видосов
    # todo сделать сортировку в меню списка видосов
    def __init__(self, input_folder=default_project_folder):
        super(QtMainWindow, self).__init__()
        self.setupUi(self)
        self.setStyleSheet(stylesheet.houdini)

        self.working_directory = input_folder
        self.videolist_model = None
        self.ffmpeg = None
        self.gifsicle = None
        self.movie136 = QMovie()
        self.movie280 = QMovie()
        self.working_emoji = None
        self.lossy_file_size = None
        self.lossy_factor = None
        self.output_file = None
        self.original_280_gif = None
        self.original_136_gif = None
        self.loaded_280 = None
        self.loaded_136 = None
        self.tp = None
        self.color_table = None
        # todo разобраться с self.launcher self.launcher = Launcher()
        # todo разобраться с self.main_task_pool self.main_task_pool = TasksPool()

        # ############################ MODIFY INTERFACE ############################## #
        # todo исправить размер интерфейса self.setGeometry(200, 200, 40, 40)

        self.setWindowTitle('Gifcher | v 0.1')

        self.splitter_right.addWidget(self.viewport_136)
        self.splitter_main.insertWidget(1, self.viewport_280)
        # Modify relationship between main interface columns
        self.splitter_main.setStretchFactor(0, 2)
        self.splitter_main.setStretchFactor(1, 4)
        self.splitter_main.setStretchFactor(2, 3)

        self.splitter_right.setStretchFactor(0, 15)
        self.splitter_right.setStretchFactor(1, 10)
        # self.viewport_widget.open_image(Emoji("C:\Python\Giftcher\BrandinCooksEmojiTest_01\BrandinCooksEmojiTest_01_280x280_30fps.gif"))

        # Max size of icons in video list
        self.list_videoslist.setIconSize(QSize(32, 32))

        # Update the video list on initial program start
        if len(self.working_directory):
            self.make_video_list()

        # ################################# TOP BAR ################################## #
        # File menu
        # Connect "Open folder" to other Open folder button
        self.actionChooseFolder.triggered.connect(
            self.btn_input_folder.clicked)

        @self.actionExit.triggered.connect
        def exit_ui():
            exit(0)

        # Options menu
        @self.actionConfig.triggered.connect
        def call_settings():
            self.dial = settings.QtSettings()
            self.dial.exec_()

        # todo доработать окно settings

        @self.actionDelete_temp_files.triggered.connect
        def clear_temp_folder():
            if len(listdir('temp')) != 0:
                cleaning_result = clean_folder('temp')
                if cleaning_result:
                    self.console_add(cleaning_result)
                sleep(.1)
                if len(listdir('temp')) == 0:
                    self.statusbar.showMessage('Temp folder is cleaned')
                else:
                    self.statusbar.showMessage(
                        'Trying to clean temp folder, but failed')

        # todo вынести добавление консоли в другое место
        self.console = QTextBrowser(self)
        self.console.setWordWrapMode(QTextOption.NoWrap)
        # self.layout3in1.addWidget(self.console)
        self.console.setMinimumWidth(500)
        self.console.setVisible(console_flag)

        @self.actionShow_console.triggered.connect
        def show_console():
            self.console.setVisible(not self.console.isVisible())
            if self.console.isVisible():
                self.statusbar.showMessage('Console is enabled')
            else:
                self.statusbar.showMessage('Console is disabled')

        # Button for deleting gif files in the working directory
        self.actionDelete_gif_files = QAction(self)
        self.actionDelete_gif_files.setObjectName("actionDelete_temp_files")
        self.menuOptions.addAction(self.actionDelete_gif_files)
        self.actionDelete_gif_files.setText(
            QApplication.translate("MainWindow", "&Clean generated gifs", None,
                                   QApplication.UnicodeUTF8))

        @self.actionDelete_gif_files.triggered.connect
        def clean_gifs():
            self.actionUnloadGifs.triggered.emit(
            )  # Stop and unload playing gifs
            for i in files_in_folder(self.working_directory, 'gif'):
                remove(i)
            # self.update_video_list()
            self.make_video_list()

        # Button for unloading running gifs in the viewports
        self.actionUnloadGifs = QAction(self)
        self.actionUnloadGifs.setObjectName("actionDelete_temp_files")
        self.menuOptions.addAction(self.actionUnloadGifs)
        self.actionUnloadGifs.setText(
            QApplication.translate("MainWindow", "&Unload gifs", None,
                                   QApplication.UnicodeUTF8))

        @self.actionUnloadGifs.triggered.connect
        def unload_gifs():
            self.viewport_280.unload_image()
            self.viewport_136.unload_image()

        self.actionmov2mp4.triggered.connect(self.convert_mov_to_mp4)

        # About menu
        @self.actionAbout.triggered.connect
        def call_about():
            # self.dial = settings.QtSettings() # Изменить
            # self.dial.exec_()
            print(self.size())
            self.dial = about.QtAbout()
            self.dial.exec_()

        # todo доработать окно about

        # ############################### LEFT COLUMN ################################ #
        @self.btn_input_folder.clicked.connect
        def input_folder():
            # options = QFileDialog.DontResolveSymlinks | QFileDialog.ShowDirsOnly
            directory = QFileDialog.getExistingDirectory(self)
            if directory:
                self.working_directory = directory
                # self.update_video_list()
                self.make_video_list()
                self.actlist_model.update(directory)
                self.dropdown_colortable.setCurrentIndex(0)
                # load the most fps image
                if preload_files:
                    self.movie136.setFileName('')
                    self.movie136.stop()
                    self.movie280.setFileName('')
                    self.movie280.stop()
                    # get count of how many items are in the video list
                    items_count = self.videolist_model.rowCount(
                        self.videolist_model)
                    res136 = {}  # Dict for only 136 entries
                    res280 = {}  # Dict for only 280 entries
                    # Walk through the model and separate entries by resolution
                    for item in range(items_count):
                        emoji = self.videolist_model.data(
                            self.actlist_model.index(item), 32)
                        print(emoji)
                        if emoji.resolution == '136x136':
                            res136.update({str(emoji.fps): item})
                        elif emoji.resolution == '280x280':
                            res280.update({str(emoji.fps): item})
                    # Sort entries by highest FPS
                    fps_136 = sorted(res136.keys(), reverse=True)
                    fps_280 = sorted(res280.keys(), reverse=True)
                    # print(res136.keys())
                    if len(fps_136):
                        top_fps_136 = sorted(res136.keys(), reverse=True)[0]
                        # Click on the appropriate items in the ModelViewer
                        avi_activated(
                            self.videolist_model.index(res136[top_fps_136]))
                        avi_activated(
                            self.videolist_model.index(res136[top_fps_136]))
                    if len(fps_280):
                        top_fps_280 = sorted(res280.keys(), reverse=True)[0]
                        # Click on the appropriate items in the ModelViewer
                        avi_activated(
                            self.videolist_model.index(res280[top_fps_280]))
                        avi_activated(
                            self.videolist_model.index(res280[top_fps_280]))

        self.btn_input_folder.setContextMenuPolicy(Qt.CustomContextMenu)

        @self.btn_input_folder.customContextMenuRequested.connect
        def btn_input_folder_open_menu(pos):
            subprocess.Popen(r'explorer "{}"'.format(self.working_directory))

        def avi_activated(video_list_item):
            self.working_emoji = video_list_item.data(32)
            # Calling FFmpeg if there is no gif created
            print(type(settings.overwrite_gifs))
            if not self.working_emoji.has_gif or settings.overwrite_gifs:
                self.statusbar.showMessage('Generating the gif')
                self.ffmpeg = FFmpeg()
                self.ffmpeg.return_signal.connect(self.console_add)
                self.ffmpeg.add(self.working_emoji.full_path,
                                self.working_emoji.fps)
                self.console_add('=' * 50)
                self.console_add('Converting {} using ffmpeg'.format(
                    self.working_emoji.full_path))
                self.ffmpeg.run()
                self.console_add('=' * 50 + '\n')
                self.ffmpeg.return_signal.disconnect(self.console_add)
                self.working_file = self.working_emoji.full_path
                # self.load_gif(self.working_emoji.gif_path)
                # self.update_video_list()
                self.make_video_list()
            else:
                self.load_gif(self.working_emoji.gif_path)
            if self.working_emoji.resolution == '136x136':
                self.viewport_136.open_image(self.working_emoji)
            elif self.working_emoji.resolution == '280x280':
                self.viewport_280.open_image(self.working_emoji)

        self.list_videoslist.activated.connect(avi_activated)

        # Add acts from folder to list widget
        # todo if len(self.working_directory):
        if True:
            self.actlist_model = ActListModel(self.working_directory)
            # self.actlist_model.no_act_files_found.connect(QtError())
            # QtError()
            # todo 000
            self.dropdown_colortable.setModel(self.actlist_model)

        @self.dropdown_colortable.currentIndexChanged.connect
        def dropdown_colortable_selected(index_of_selected_item):
            act_file_path = self.dropdown_colortable.itemData(
                index_of_selected_item, 32)
            self.current_act = self.load_act(act_file_path)

        @self.btn_import_act.clicked.connect
        def import_act_clicked():
            photoshop_paths = PsFolder().ps_paths
            # print(photoshop_paths[0])
            if len(photoshop_paths) > 1:
                logging.warning(
                    'Multiple Photoshop paths found, using {}'.format(
                        photoshop_paths[0]))
            files, filtr = QFileDialog.getOpenFileNames(
                self, "Choose your color table",
                '{}'.format(photoshop_paths[0]),
                "All Files (*.*);;A color table (*.act)",
                "A color table (*.act)")

            def copy_act(act_file):  # Compact repeating function
                copy2(
                    act_file,
                    path.join(self.working_directory, path.basename(act_file)))

            user_choice = None
            for file in files:
                # If there is a file existing and if user has NOT clicked YesToAll ask him
                if path.exists(
                        path.join(path.abspath(self.working_directory),
                                  path.basename(file))
                ) and user_choice != QMessageBox.YesToAll:
                    error_box = QMessageBox()
                    error_box.setStyleSheet(self.styleSheet())
                    error_box.setWindowTitle('File error')
                    error_box.setText('The file {} exists in {}'.format(
                        path.basename(file),
                        path.abspath(self.working_directory)))
                    error_box.setInformativeText(
                        'Do you want to overwrite it?')
                    error_box.setStandardButtons(QMessageBox.YesToAll
                                                 | QMessageBox.Yes
                                                 | QMessageBox.No)
                    error_box.setDefaultButton(QMessageBox.No)
                    user_choice = error_box.exec_()
                    if user_choice == QMessageBox.Yes or user_choice == QMessageBox.YesToAll:
                        copy_act(file)
                else:
                    copy_act(file)

            self.actlist_model.update(self.working_directory)
            # Select the first of selected files in the dropdown menu
            first_file = files[0]
            first_file_stripped = path.splitext(path.basename(first_file))[0]
            index = self.actlist_model.index(0)
            index_of_first_item = self.actlist_model.match(
                index, Qt.DisplayRole, first_file_stripped)
            if len(index_of_first_item):
                index_of_first_item = index_of_first_item[0].row()
                self.dropdown_colortable.setCurrentIndex(index_of_first_item)
            else:
                raise FileNotFoundError

                # todo update the act file model

        @self.btn_export.clicked.connect
        def btn_export_clicked():

            if self.actlist_model.rowCount(self) == 0:
                if self.working_directory == '':
                    error_message = 'There is no project directory specified'
                else:
                    error_message = 'Please import one color_palette.act inside \n{}'.format(
                        self.working_directory)
                logging.warning(error_message.replace('\n', ''))
                error_box = QMessageBox()
                error_box.setStyleSheet(stylesheet.houdini)
                error_box.setWindowTitle('File error')
                error_box.setText('There is .act file missing' + ' ' * 50)
                error_box.setInformativeText(error_message)
                error_box.exec_()
                return 1

            # Dictionary two lossy values from their interface spinners
            lossy_dict = {
                '136': self.viewport_136.spin_quality.text(),
                '280': self.viewport_280.spin_quality.text()
            }
            self.color_table = path.join('.\\temp', 'current_act.txt')
            # We generate a colormap from the colormap viewer window
            with open(self.color_table, 'w') as txt:
                txt.writelines(self.plaintext_act_readout.toPlainText())
            color_table = self.color_table
            # self.actionUnloadGifs.triggered.emit()  # Stop and unload playing gifs
            self.viewport_280.unload_image()
            self.viewport_136.unload_image()
            # Start export conversion using dir user selected and lossy dict
            self.conversion = Conversion()
            self.conversion.conversion1.connect(
                lambda i, t: self.progress_bar1.setValue(int(i / t * 100)))
            self.conversion.conversion2.connect(
                lambda i, t: self.progress_bar2.setValue(int(i / t * 100)))
            self.conversion.conversion3.connect(
                lambda i, t: self.progress_bar3.setValue(int(i / t * 100)))
            self.conversion.conversion4.connect(
                lambda i, t: self.progress_bar4.setValue(int(i / t * 100)))
            self.conversion.conversion5.connect(
                lambda i, t: self.progress_bar5.setValue(int(i / t * 100)))

            self.conversion.true_init(self.working_directory, lossy_dict,
                                      color_table)
            self.conversion.conversion5_done.connect(
                lambda: self.make_video_list(self.working_directory))

        @self.btn_clean.clicked.connect
        def clean():
            self.console_add('Cleaning process has started')
            self.statusbar.showMessage('Cleaning process has started')
            files_to_delete = files_in_folder(self.working_directory, 'avi')
            files_to_delete.extend(
                files_in_folder(self.working_directory, 'tmp'))
            files_to_delete_names = [
                path.basename(file) for file in files_to_delete
            ]
            box = QMessageBox()
            box.setStyleSheet(self.styleSheet())
            # box_layout = box.layout()
            # box_layout.setColumnMinimumWidth(1,500)
            # QGridLayout.set
            box.setWindowTitle('Clean up')
            box.setText('You are about to delete: \n{}'.format('\n'.join(
                str(x) for x in files_to_delete_names)))
            box.setInformativeText('Are you sure?')
            box.setStandardButtons(QMessageBox.Yes | QMessageBox.No)
            box.setDefaultButton(QMessageBox.No)
            user_choice = box.exec_()
            if user_choice == QMessageBox.Yes:
                [send2trash(file) for file in files_to_delete]

        self.btn_export.setContextMenuPolicy(Qt.CustomContextMenu)

        @self.btn_export.customContextMenuRequested.connect
        def btn_input_folder_open_menu(pos):
            print(self.size())

        # self.progress_bar1 = QProgressBar()
        # self.layout_fileops.addWidget(self.progress_bar1)
        # self.progress_bar2 = QProgressBar()
        # self.layout_fileops.addWidget(self.progress_bar2)
        # self.progress_bar3 = QProgressBar()
        # self.layout_fileops.addWidget(self.progress_bar3)
        # self.progress_bar4 = QProgressBar()
        # self.layout_fileops.addWidget(self.progress_bar4)
        # self.progress_bar5 = QProgressBar()
        # self.layout_fileops.addWidget(self.progress_bar5)

        # ############################## MIDDLE COLUMN ############################### #
        # @self.btn_fb280.clicked.connect
        # def btn_fb280_clicked():
        #     current_frame = self.movie280.currentFrameNumber()
        #     self.movie280.jumpToFrame(0)
        #     for i in range(current_frame - 1):
        #         self.movie280.jumpToNextFrame()
        #     fps = '\tFPS: ' + str(round(1000 / self.movie280.nextFrameDelay(), 2))
        #     delay = '\tDelay: ' + str(self.movie280.nextFrameDelay())
        #     frame_n = str(self.movie280.currentFrameNumber())
        #     self.statusbar.showMessage('280px: Jumped to frame #' + frame_n + fps + delay)
        #
        # @self.btn_playpause280.clicked.connect
        # def btn_playpause280_clicked():
        #     if self.btn_playpause280.isChecked():
        #         self.movie280.setPaused(True)
        #         self.statusbar.showMessage('280px: Paused on frame #' + str(self.movie280.currentFrameNumber()))
        #     else:
        #         self.movie280.setPaused(False)
        #         self.statusbar.showMessage('280px: Playing')
        #
        # @self.btn_ff280.clicked.connect
        # def btn_ff280_clicked():
        #     self.movie280.jumpToNextFrame()
        #     fps = '\tFPS: ' + str(round(1000 / self.movie280.nextFrameDelay(), 2))
        #     delay = '\tDelay: ' + str(self.movie280.nextFrameDelay())
        #     frame_n = str(self.movie280.currentFrameNumber())
        #     self.statusbar.showMessage('280px: Jumped to frame #' + frame_n + fps + delay)
        #
        # @self.slider_speed280.valueChanged.connect
        # def speed280_slider_changed(value):
        #     self.statusbar.showMessage('Speed of 280px changed to {}x'.format(value/100))
        #     self.spin_speed280.blockSignals(True)
        #     self.spin_speed280.setValue(value * 0.01)
        #     self.spin_speed280.blockSignals(False)
        #     self.movie280.setSpeed(value)
        #
        # @self.spin_speed280.valueChanged.connect
        # def speed280_spinner_changed(value):
        #     value = round(value, 2)
        #     self.statusbar.showMessage('Speed of 280px changed to {}x'.format(value))
        #     value *= 100
        #     self.slider_speed280.setValue(value)
        #     self.movie280.setSpeed(value)
        #
        # self.previous_scale280 = self.spin_scale280.value()
        # @self.spin_scale280.valueChanged.connect
        # def spin_scale280_value_changed(value):
        #     self.statusbar.showMessage('Zoom of 280px changed to {}x'.format(value))
        #     self.graphicsView_280.scale(1/self.previous_scale280, 1/self.previous_scale280)
        #     self.graphicsView_280.scale(value, value)
        #     self.slider_scale280.setValue(value)
        #     self.previous_scale280 = self.spin_scale280.value()
        #
        # self.spin_scale280.valueChanged.emit(self.spin_scale280.value())
        #
        # @self.spin_quality280.valueChanged.connect
        # def spin_quality280_value_changed():
        #     if self.check_livepreview280.isChecked():
        #         btn_update280_clicked()
        #
        # def btn_update280_clicked():
        #     # working_file = self.movie280.fileName()
        #     working_file = self.loaded_280.gif_path
        #     print(working_file)
        #     output_file = path.splitext(working_file)[0] + '.tmp'
        #     self.movie280.stop()
        #     lossy_factor = self.spin_quality280.text()
        #     # Instead of generating a txt file for a colortable
        #     # color_table = act_reader.create_gifsicle_colormap(self.dropdown_colortable.currentText())
        #     self.color_table = path.join('.\\temp', 'current_act.txt')
        #     # We generate a colormap from the colormap viewer window
        #     with open(self.color_table, 'w') as txt:
        #         txt.writelines(self.plaintext_act_readout.toPlainText())
        #     color_table = self.color_table
        #
        #     # self.btn_update280.setEnabled(False)
        #     self.gc = GifSicle(self.loaded_280, lossy_factor, color_table)
        #     # self.gc = GifSicle() todo разобраться что происходит тут
        #     # self.gc.return_signal.connect(lambda x: print(x))
        #     # self.gc.add(self.working_emoji, lossy_factor, color_table)
        #     # self.gc.run()
        #     # .return_signal.connect(self.console_add)
        #
        #     self.load_gif(output_file)
        #     temp_file_size = path.getsize(output_file)/1024
        #     self.statusbar.showMessage('Resulting filesize is: {:.2f} Kb'.format(temp_file_size))
        # self.btn_update280.clicked.connect(btn_update280_clicked)
        #
        # # ############################### RIGHT COLUMN ############################### #
        #
        # # Load the color table viewer
        # if len(default_project_folder):
        #     files = files_in_folder(self.working_directory, 'act')
        #     if len(files):
        #         self.load_act(files[self.dropdown_colortable.currentIndex()])
        #
        # @self.btn_fb136.clicked.connect
        # def btn_fb136_clicked():
        #     current_frame = self.movie136.currentFrameNumber()
        #     self.movie136.jumpToFrame(0)
        #     for i in range(current_frame - 1):
        #         self.movie136.jumpToNextFrame()
        #     fps = '\tFPS: ' + str(round(1000 / self.movie136.nextFrameDelay(), 2))
        #     delay = '\tDelay: ' + str(self.movie136.nextFrameDelay())
        #     frame_n = str(self.movie136.currentFrameNumber())
        #     self.statusbar.showMessage('136px: Jumped to frame #' + frame_n + fps + delay)
        #
        # @self.btn_playpause136.clicked.connect
        # def btn_playpause136_clicked():
        #     if self.btn_playpause136.isChecked():
        #         self.movie136.setPaused(True)
        #         self.statusbar.showMessage('136px: Paused on frame #' + str(self.movie136.currentFrameNumber()))
        #     else:
        #         self.movie136.setPaused(False)
        #         self.statusbar.showMessage('136px: Playing')
        #
        # @self.btn_ff136.clicked.connect
        # def btn_ff136_clicked():
        #     self.movie136.jumpToNextFrame()
        #     fps = '\tFPS: ' + str(round(1000 / self.movie136.nextFrameDelay(), 2))
        #     delay = '\tDelay: ' + str(self.movie136.nextFrameDelay())
        #     frame_n = str(self.movie136.currentFrameNumber())
        #     self.statusbar.showMessage('136px: Jumped to frame #' + frame_n + fps + delay)
        #
        # @self.slider_speed136.valueChanged.connect
        # def speed136_slider_changed(value):
        #     self.statusbar.showMessage('Speed of 136px changed to {}x'.format(value/100))
        #     self.spin_speed136.blockSignals(True)
        #     self.spin_speed136.setValue(value * 0.01)
        #     self.spin_speed136.blockSignals(False)
        #     self.movie136.setSpeed(value)
        #
        # @self.spin_speed136.valueChanged.connect
        # def speed136_spinner_changed(value):
        #     value = round(value, 2)
        #     self.statusbar.showMessage('Speed of 136px changed to {}x'.format(value))
        #     value *= 100
        #     self.slider_speed136.setValue(value)
        #     self.movie136.setSpeed(value)
        #
        # self.previous_scale136 = self.spin_scale136.value()
        # @self.spin_scale136.valueChanged.connect
        # def spin_scale136_value_changed(value):
        #     self.statusbar.showMessage('Zoom of 136px changed to {}x'.format(value))
        #     self.graphicsView_136.scale(1/self.previous_scale136, 1/self.previous_scale136)
        #     self.graphicsView_136.scale(value, value)
        #     self.slider_scale136.setValue(value)
        #     self.previous_scale136 = self.spin_scale136.value()
        # self.spin_scale136.valueChanged.emit(self.spin_scale136.value())
        #
        # @self.spin_quality136.valueChanged.connect
        # def spin_quality136_value_changed():
        #     if self.check_livepreview136.isChecked():
        #         btn_update136_clicked()
        #
        # def btn_update136_clicked():
        #     # working_file = self.movie136.fileName()
        #     working_file = self.loaded_136.gif_path
        #     output_file = path.splitext(working_file)[0] + '.tmp'
        #     self.movie136.stop()
        #     lossy_factor = self.spin_quality136.text()
        #     # Instead of generating a txt file for a colortable
        #     # color_table = act_reader.create_gifsicle_colormap(self.dropdown_colortable.currentText())
        #     self.color_table = path.join('.\\temp', 'current_act.txt')
        #     # We generate a colormap from the colormap viewer window
        #     with open(self.color_table, 'w') as txt:
        #         txt.writelines(self.plaintext_act_readout.toPlainText())
        #     color_table = self.color_table
        #
        #     GifSicle(self.loaded_136, lossy_factor, color_table)
        #     self.load_gif(output_file)
        #     temp_file_size = path.getsize(output_file)/1024
        #     self.statusbar.showMessage('Resulting filesize is: {:.2f} Kb'.format(temp_file_size))
        # self.btn_update136.clicked.connect(btn_update136_clicked)
        #
        # self.gifplayer136_widget = QWidget()
        # self.gifplayer136 = QLabel(self.gifplayer136_widget)
        # self.gifplayer136.setMinimumSize(QSize(136, 136))
        #
        # self.graphics_scene_136 = QGraphicsScene()
        # self.graphicsView_136.setScene(self.graphics_scene_136)
        # self.graphicsView_136.setInteractive(1)
        #
        # self.graphics_scene_136.addWidget(self.gifplayer136_widget)
        # self.graphicsView_136.scale(2, 2)
        #
        # self.gifplayer280_widget = QWidget()
        # self.gifplayer280 = QLabel(self.gifplayer280_widget)
        # self.gifplayer280.setMinimumSize(QSize(280, 280))
        #
        # self.graphics_scene_280 = QGraphicsScene()
        # self.graphicsView_280.setScene(self.graphics_scene_280)
        # self.graphicsView_280.setInteractive(1)
        #
        # self.graphics_scene_280.addWidget(self.gifplayer280_widget)
        # self.graphicsView_280.scale(2, 2)
    def make_video_list(self, folder=None, ext='avi'):
        # If no folder specified, update the current working directory
        if not folder:
            folder = self.working_directory
        if len(files_in_folder(folder, ext)) > 0:
            # Make a dictionary out of emojis, when emoji object is not none (has been successfully created)
            emoji_dict = {
                Emoji(emoji).filename: Emoji(emoji)
                for emoji in files_in_folder(folder, ext) if Emoji(emoji)
            }
            # Make a model
            self.videolist_model = VideoListModel(emoji_dict)
            # Assign the model to the list view
            self.list_videoslist.setModel(self.videolist_model)
            # Enable the collect button
            self.btn_clean.setEnabled(True)

    # def update_video_list(self, folder=None, ext='avi'):
    #     # If no folder specified, update the current working directory
    #     if not folder:
    #         folder = self.working_directory
    #     if len(files_in_folder(folder, ext)) > 0:
    #         # Make a dictionary out of emojis, when emoji object is not none (has been successfully created)
    #         emoji_dict = {Emoji(emoji).filename: Emoji(emoji) for emoji in files_in_folder(folder, ext) if Emoji(emoji)}
    #         # Make a model
    #         self.videolist_model = VideoListModel(emoji_dict)
    #         # Assign the model to the list view
    #         self.list_videoslist.setModel(self.videolist_model)
    #         # Enable the collect button
    #         self.btn_collect.setEnabled(True)

    # ################################# LOADERS ################################## #

    def load_act(self, act_file):
        self.plaintext_act_readout.setToolTip(
            '{} is loaded.\n\n'
            'You can see and edit the color map here.\n'
            'Those changes appear on update and export.'.format(act_file))
        self.plaintext_act_readout.clear()
        act = act_reader.act_to_list(act_file)
        # self.graphics_scene.addText(''.join(act[0]))
        self.plaintext_act_readout.setPlainText('\n'.join(act[0]))
        self.statusbar.showMessage('"' + act_file + '"' +
                                   ' contains {} color(s)'.format(act[1]))
        if act[1] > 256:
            error_msg = corrupted_palette(act_file)
            QMessageBox.warning(self, *error_msg)
        return act

    def load_gif(self, gif_path: str) -> None:
        """
        This method chooses, and loads, in which viewport to load the gif, 280 or 136 one.

        :type gif_path: str
        :param gif_path: Full path to the gif, you want to load.
        """
        if '280' in gif_path:
            self.load280(gif_path)
        elif '136' in gif_path:
            self.load136(gif_path)
        else:
            logging.error(
                'load_gif function encountered a weird gif_path: {}'.format(
                    gif_path))

    def load280(self, file280):
        self.btn_playpause280.setChecked(
            False)  # Unpress the play-pause button
        self.btn_fb280.setEnabled(True)  # Enable back button
        self.btn_playpause280.setEnabled(True)  # Enable play-pause button
        self.btn_ff280.setEnabled(True)  # Enable forward button
        self.layout_gif280.setTitle(
            path.split(file280)[1])  # Set name of the gif as the title
        self.movie280.setFileName('')  # Free (close) the previous loaded image
        self.movie280 = QMovie(file280)  # Create a QMovie instance
        self.gifplayer280.setMovie(
            self.movie280)  # And assign it to the player widget
        self.movie280.setSpeed(
            self.spin_speed280.value() *
            100)  # Automatically set speed using the speed spinner
        self.movie280.start()
        return self.movie280.isValid()

    def load136(self, file136):
        self.btn_playpause136.setChecked(
            False)  # Unpress the play-pause button
        self.btn_fb136.setEnabled(True)  # Enable back button
        self.btn_playpause136.setEnabled(True)  # Enable play-pause button
        self.btn_ff136.setEnabled(True)  # Enable forward button
        self.layout_gif136.setTitle(
            path.split(file136)[1])  # Set name of the gif as the title
        self.movie136.setFileName('')  # Free (close) the previous loaded image
        self.movie136 = QMovie(file136)  # Create a QMovie instance
        self.gifplayer136.setMovie(
            self.movie136)  # And assign it to the player widget
        self.movie136.setSpeed(
            self.spin_speed136.value() *
            100)  # Automatically set speed using the speed spinner
        self.movie136.start()
        return self.movie136.isValid()

    def load_palette(self, palette: str) -> None:
        """
        This method chooses loads a palette image to 136 viewport.

        :type palette: str
        :param palette: Full path to the image, you want to load.
        """
        pixmap = QPixmap(palette)
        pixmap = pixmap.scaled(136, 136, mode=Qt.FastTransformation)
        self.gifplayer136.setPixmap(pixmap)
        # self.gifplayer136.scaled todo
        # self.gifplayer136.setScaledContents(True) todo

    def console_add(self, log_input):
        self.console.append(str(log_input))  #.rstrip())

    def convert_mov_to_mp4(self):
        print(QFileDialog())
        files, filtr = QFileDialog.getOpenFileNames(
            self, "Choose your files for conversion", '.',
            "All Files (*.*);;MOV (*.mov)", "MOV (*.mov)")
        print(files, filtr)
        for input_file in files:
            Handbrake(input_file)

    def minimal_size(self):
        self.resize(0, 0)

    def keyPressEvent(self, event):
        if event.key() == Qt.Key_ScrollLock:
            self.viewport_136.scroll_lock = not self.viewport_136.scroll_lock
            self.viewport_280.scroll_lock = not self.viewport_280.scroll_lock
            self.viewport_136.check_embedded()
            self.viewport_280.check_embedded()
        else:
            super(QtMainWindow, self).keyPressEvent(event)