示例#1
0
    def find_all_assets(self, force_update=False, force_login=True):
        """
        Returns a list of all assets in the project
        :param force_update: bool, Whether assets cache updated must be forced or not
        :param force_login: bool, Whether logging to production tracker is forced or not
        :return: variant, ArtellaAsset or list(ArtellaAsset)
        """

        self._check_project()

        if self.__class__._assets and not force_update:
            return self.__class__._assets

        python.clear_list(self.__class__._assets)

        if not artellapipe.Tracker().is_logged() and force_login:
            artellapipe.Tracker().login()
        if not artellapipe.Tracker().is_logged():
            LOGGER.warning(
                'Impossible to find assets of current project because user is not log into production tracker'
            )
            return None
        tracker = artellapipe.Tracker()
        assets_list = tracker.all_project_assets()
        if not assets_list:
            LOGGER.warning("No assets found in current project!")
            return None
        for asset_data in assets_list:
            new_asset = self.create_asset(asset_data)
            if not new_asset:
                continue
            self.__class__._assets.append(new_asset)

        return self.__class__._assets
示例#2
0
    def find_all_sequences(self, force_update=False, force_login=True):
        """
        Returns a list of all sequences in the current project
        :param force_update: bool, Whether sequences cache updated must be forced or not
        :param force_login: bool, Whether logging to production tracker is forced or not
        :return: list(ArtellaSequence))
        """

        self._check_project()

        if self.sequences and not force_update:
            return self.sequences

        python.clear_list(self.__class__._sequences)

        if not artellapipe.Tracker().is_logged() and force_login:
            artellapipe.Tracker().login()
        if not artellapipe.Tracker().is_logged():
            LOGGER.warning(
                'Impossible to find sequences of current project because user is not log into production tracker')
            return None
        tracker = artellapipe.Tracker()
        sequences_list = tracker.all_project_sequences()
        if not sequences_list:
            LOGGER.warning('No sequences found in current project!')
            return None

        for sequence_data in sequences_list:
            new_sequence = self.create_sequence(sequence_data)
            self.__class__._sequences.append(new_sequence)

        return self.sequences
示例#3
0
    def get_assets_in_shot(self, shot, force_login=True):
        """
        Returns all the assets contained in given shot breakdown defined in production tracker
        :param shot:
        :return:
        """

        if not artellapipe.Tracker().is_logged() and force_login:
            artellapipe.Tracker().login()
        if not artellapipe.Tracker().is_logged():
            LOGGER.warning(
                'Impossible to find assets of current project because user is not log into production tracker'
            )
            return None
        tracker = artellapipe.Tracker()
        assets_in_shots = tracker.all_assets_in_shot(shot)
        if not assets_in_shots:
            LOGGER.warning('No assets found in shot breakdown')
            return None

        found_assets = list()
        for asset_data in assets_in_shots:
            new_asset = self.create_asset(asset_data)
            found_assets.append(new_asset)

        return found_assets
    def _create_login_form(self):
        """
        Internal callback function that checks current Kitsu login status
        """

        email = artellapipe.Tracker().email
        password = artellapipe.Tracker().password
        store_credentials = artellapipe.Tracker().store_credentials

        return KitsuLoginForm(email=email, password=password, store_credentials=store_credentials)
    def ui(self):
        super(ArtellaAssetsLibrary, self).ui()

        supported_files = self.config.get('supported_files')
        self._library_widget = self.LIBRARY_WIDGET(
            project=self._project, supported_files=supported_files)
        self.main_layout.addWidget(self._library_widget)

        artellapipe.Tracker().logged.connect(self._on_valid_login)
        artellapipe.Tracker().unlogged.connect(self._on_valid_unlogin)
 def setup_signals(self):
     self._project_artella_btn.clicked.connect(self._on_open_project_in_artella)
     self._project_folder_btn.clicked.connect(self._on_open_project_folder)
     self._settings_btn.clicked.connect(self._on_open_settings)
     self._assets_widget.assetAdded.connect(self._on_asset_added)
     self._attrs_stack.animFinished.connect(self._on_attrs_stack_anim_finished)
     self._shots_widget.shotAdded.connect(self._on_shot_added)
     self._settings_widget.closed.connect(self._on_close_settings)
     artellapipe.Tracker().logged.connect(self._on_valid_login)
     artellapipe.Tracker().unlogged.connect(self._on_valid_unlogin)
示例#7
0
    def _check_tracker(self, force_login=True):
        """
        Internal function that checks whether or not production tracking is ready to be used with this manager
        :return: bool
        """

        if not artellapipe.Tracker().is_logged() and force_login:
            artellapipe.Tracker().login()
        if not artellapipe.Tracker().is_logged():
            LOGGER.warning(
                'Impossible to find casting of current project because user is not log into production tracker'
            )
            return None
    def _on_kitsu_login_accepted(self, email, password, store_credentials):
        """
        Internal callback function that is called when the user successfully log into Kitsu through Kitsu login form
        :param email: variant, str or None
        :param password: variant, str or None
        :param store_credentials: bool
        """

        artellapipe.Tracker().email = email
        artellapipe.Tracker().password = password
        artellapipe.Tracker().store_credentials = store_credentials

        index = self._main_stack.indexOf(self._kitsu_waiter)
        self._main_stack.slide_in_index(index, force=True)
示例#9
0
    def get_ocurrences_of_asset_in_shot(self,
                                        asset_name,
                                        shot_name,
                                        force_update=False):
        """
        Returns the number of ocurrences of given asset in given shot
        :param asset_name: str, name of the asset
        :param shot_name: str, name of the shot
        :return: int or None
        """

        if not self._check_project():
            return None

        asset = artellapipe.AssetsMgr().find_asset(asset_name)
        if not asset:
            LOGGER.warning(
                'Impossible to return occurrences because asset "{}" does not exists!'
                .format(asset_name))
            return None

        shot = shots.ShotsManager().find_shot(shot_name)
        if not shot:
            LOGGER.warning(
                'Impossible to return occurrences because shot "{}" does not exists!'
                .format(shot_name))
            return None

        shot_id = shot.get_id()

        tracker = artellapipe.Tracker()
        total_occurrences = tracker.get_occurrences_of_asset_in_shot(
            shot_id, asset_name, force_update=force_update)

        return total_occurrences
示例#10
0
    def get_all_task_statuses(self):
        """
        Returns all task statuses for current project
        :return: list(str)
        """

        return artellapipe.Tracker().all_task_statuses()
示例#11
0
    def _on_open_kitsu_login(self):
        """
        Internal callback function that is called when the user presses the Kitsu button
        """

        if artellapipe.Tracker().is_logged():
            pass
        #     user_data = artellapipe.Tracker().user_data
        #     self._ballon = KitsuUserBalloon(user_data=user_data)
        #     rect_btn = self._kitsu_btn.geometry()
        #     rect_balloon = self._ballon.geometry()
        #     pos = QCursor.pos()
        #     pos.setX(pos.x() - (self._kitsu_btn.width() / 2) - 20)
        #     rect_balloon.setRect(
        #         pos.x(), pos.y(), rect_btn.width(), rect_btn.height()
        #     )
        #     self._ballon.setGeometry(rect_balloon)
        #     self._ballon.show()
        else:
            login_dialog = logindialog.KitsuLoginDialog(project=self._project,
                                                        parent=self._window)
            login_dialog.validLogin.connect(self._on_kitsu_login)
            login_dialog.invalidLogin.connect(self._on_kitsu_logout)
            login_dialog.canceledLogin.connect(self._on_kitsu_cancel)

            self._slider_panel = panel.SliderPanel('Kitsu Login',
                                                   parent=self._window)
            self._slider_panel.position = 'right'
            self._slider_panel.setFixedWidth(315)
            self._slider_panel.set_widget(login_dialog)
            self._slider_panel.show()
    def _kitsu_login(self, *args, **kwargs):
        """
        Internal function that is called by Kitsu Worker to execute login
        :param data:
        :return:
        """

        email = artellapipe.Tracker().email
        password = artellapipe.Tracker().password

        if not email or not password:
            LOGGER.warning('Impossible to login into Kitsu because user and password are not given!')
            return

        valid_login = artellapipe.Tracker().login(email=email, password=password)

        return valid_login
    def _on_kitsu_worker_failure(self, uid, msg):
        """
        Internal callback function that is called when Kitsu worker fails
        :param uid: str
        :param msg: str
        """

        artellapipe.Tracker().reset_user_info()
        LOGGER.error('{} | {}'.format(uid, msg))
        self._main_stack.slide_in_index(0)
示例#14
0
    def try_kitsu_login(self):
        """
        Function that tries to log into Kitsu
        """

        valid_login = artellapipe.Tracker().login()
        if valid_login:
            self._kitsu_login()
            return True

        return False
示例#15
0
    def get_tasks_for_shot(self, shot_name):
        """
        Returns all tasks attached to the given shot
        :param shot_name: str
        :return:
        """

        shot_found = artellapipe.ShotsMgr().find_shot(shot_name)
        if not shot_found:
            LOGGER.warning('No shot found with name: "{}"!'.format(shot_name))
            return None

        return artellapipe.Tracker().get_tasks_in_shot(shot_found.get_id())
示例#16
0
 def run(self):
     try:
         if self._preview_id and self._path:
             if not os.path.isfile(self._path) or self._force:
                 artellapipe.Tracker().download_preview_file_thumbnail(
                     self._preview_id, self._path)
             if not self._path or not os.path.isfile(self._path):
                 icon = QIcon()
             else:
                 icon_pixmap = QPixmap(self._path)
                 icon = QIcon(icon_pixmap)
             self.signals.triggered.emit(icon)
     except Exception as exc:
         LOGGER.error('Cannot load thumbnail image: {}!'.format(exc))
    def _on_kitsu_worker_completed(self, uid, valid_login):
        """
        Internal callback function that is called when Kitsu worker finishes
        :param uid: str
        :param valid_login: bool
        """

        if valid_login:
            if not artellapipe.Tracker().is_logged():
                LOGGER.warning('Something went wrong during Kitsu login')
                return False
            if artellapipe.Tracker().email and artellapipe.Tracker().password:
                store_credentials = bool(artellapipe.Tracker().store_credentials)
                self._project.settings.set('kitsu_store_credentials', store_credentials)
                self.validLogin.emit()
                self.close()
        else:
            qtutils.show_error(
                self, 'Error while logging into Kitsu', 'Kitsu credentials are not valid. Try again please!')
            self._main_stack.slide_in_index(0)
            return False

        return True
示例#18
0
    def find_all_shots(self, force_update=False, force_login=True):
        """
        Returns all shots of the project
        :param force_update:  bool, Whether shots cache updated must be forced or not
        :param force_login: bool, Whether logging to production tracker is forced or not
        :return: list(ArtellaShot)
        """

        self._check_project()

        if self.shots and not force_update:
            return self.shots

        python.clear_list(self.__class__._shots)

        if not artellapipe.Tracker().is_logged() and force_login:
            artellapipe.Tracker().login()
        if not artellapipe.Tracker().is_logged():
            LOGGER.warning(
                'Impossible to find shots of current project because user is not log into production tracker'
            )
            return None
        tracker = artellapipe.Tracker()
        shots_list = tracker.all_project_shots()
        if not shots_list:
            LOGGER.warning('No shots found in current project!')
            return None

        for shot_data in shots_list:
            new_shot = self.create_shot(shot_data)
            self.__class__._shots.append(new_shot)

        self.__class__._shots.sort(key=lambda x: x.get_start_frame(),
                                   reverse=True)

        return self.shots
示例#19
0
    def update_kitsu_status(self):
        """
        Synchronizes current Kitsu status between UserInfo and current project
        """

        if not self._project:
            LOGGER.warning(
                'Impossible to update Kitsu Status because Project is not defined!'
            )
            return

        if artellapipe.Tracker().is_logged():
            self._kitsu_btn.setIcon(self._kitsu_on_icon)
            self._kitsu_logout_btn.setVisible(True)
        else:
            self._kitsu_btn.setIcon(self._kitsu_off_icon)
            self._kitsu_logout_btn.setVisible(False)
示例#20
0
    def _on_kitsu_logout(self):
        """
        Internal callback function that is called when the user presses the logout button
        """

        remove_credentials = False
        res = qtutils.show_question(
            self, 'Kitsu Logout',
            'Do you want to remove Kitsu stored credentials?')
        if res == QMessageBox.Yes:
            remove_credentials = True

        valid = artellapipe.Tracker().logout(
            remove_credentials=remove_credentials)
        if not valid:
            LOGGER.warning('Error while logging out from Kitsu')
            return

        self.update_kitsu_status()
        self.logout.emit()
示例#21
0
    def get_task_status_for_shot(self, shot_name, task_name):
        """
        Returns the status of the given task in the given shot
        :param shot_name: str, name of the shot
        :param task_name: str, name of the task
        :return: str, status name
        """

        tasks_for_shot = self.get_tasks_for_shot(shot_name)
        if not tasks_for_shot:
            return

        task_found = None
        for task in tasks_for_shot:
            if task.name == task_name:
                task_found = task
                break

        if not task_found:
            return

        return artellapipe.Tracker().get_task_status(task_found.id)
    def ui(self):
        super(ArtellaAssetsManager, self).ui()

        # Create Top Menu Bar
        self._menu_bar = self._setup_menubar()
        if not self._menu_bar:
            self._menu_bar = QMenuBar(self)
        self.main_layout.addWidget(self._menu_bar)
        sep = QFrame()
        sep.setFrameShape(QFrame.HLine)
        sep.setFrameShadow(QFrame.Raised)
        self.main_layout.addWidget(sep)

        self._main_stack = stack.SlidingStackedWidget(parent=self)
        self._attrs_stack = stack.SlidingStackedWidget(parent=self)
        self._shots_stack = stack.SlidingStackedWidget(parent=self)

        no_items_widget = QFrame()
        no_items_widget.setFrameShape(QFrame.StyledPanel)
        no_items_widget.setFrameShadow(QFrame.Sunken)
        no_items_layout = QVBoxLayout()
        no_items_layout.setContentsMargins(0, 0, 0, 0)
        no_items_layout.setSpacing(0)
        no_items_widget.setLayout(no_items_layout)
        no_items_lbl = QLabel()
        no_items_pixmap = tpDcc.ResourcesMgr().pixmap('no_asset_selected')
        no_items_lbl.setPixmap(no_items_pixmap)
        no_items_lbl.setAlignment(Qt.AlignCenter)
        no_items_layout.addItem(QSpacerItem(0, 10, QSizePolicy.Preferred, QSizePolicy.Expanding))
        no_items_layout.addWidget(no_items_lbl)
        no_items_layout.addItem(QSpacerItem(0, 10, QSizePolicy.Preferred, QSizePolicy.Expanding))

        no_shot_selected_widget = QFrame()
        no_shot_selected_widget.setFrameShape(QFrame.StyledPanel)
        no_shot_selected_widget.setFrameShadow(QFrame.Sunken)
        no_shot_selected_layout = QVBoxLayout()
        no_shot_selected_layout.setContentsMargins(0, 0, 0, 0)
        no_shot_selected_layout.setSpacing(0)
        no_shot_selected_widget.setLayout(no_shot_selected_layout)
        no_shot_selected_lbl = QLabel()
        no_sequence_selected_pixmap = tpDcc.ResourcesMgr().pixmap('no_shot_selected')
        no_shot_selected_lbl.setPixmap(no_sequence_selected_pixmap)
        no_shot_selected_lbl.setAlignment(Qt.AlignCenter)
        no_shot_selected_layout.addItem(QSpacerItem(0, 10, QSizePolicy.Preferred, QSizePolicy.Expanding))
        no_shot_selected_layout.addWidget(no_shot_selected_lbl)
        no_shot_selected_layout.addItem(QSpacerItem(0, 10, QSizePolicy.Preferred, QSizePolicy.Expanding))

        no_assets_widget = QWidget()
        no_assets_layout = QVBoxLayout()
        no_assets_layout.setContentsMargins(2, 2, 2, 2)
        no_assets_layout.setSpacing(2)
        no_assets_widget.setLayout(no_assets_layout)
        no_assets_frame = QFrame()
        no_assets_frame.setFrameShape(QFrame.StyledPanel)
        no_assets_frame.setFrameShadow(QFrame.Sunken)
        no_assets_frame_layout = QHBoxLayout()
        no_assets_frame_layout.setContentsMargins(2, 2, 2, 2)
        no_assets_frame_layout.setSpacing(2)
        no_assets_frame.setLayout(no_assets_frame_layout)
        no_assets_layout.addWidget(no_assets_frame)
        no_assets_found_label = QLabel()
        no_assets_found_pixmap = tpDcc.ResourcesMgr().pixmap('no_assets_found')
        no_assets_found_label.setPixmap(no_assets_found_pixmap)
        no_assets_frame_layout.addItem(QSpacerItem(10, 0, QSizePolicy.Expanding, QSizePolicy.Preferred))
        no_assets_frame_layout.addWidget(no_assets_found_label)
        no_assets_frame_layout.addItem(QSpacerItem(10, 0, QSizePolicy.Expanding, QSizePolicy.Preferred))

        self._waiter = waiter.ArtellaWaiter()

        self._user_info_layout = QVBoxLayout()
        self._user_info_layout.setContentsMargins(0, 0, 0, 0)
        self._user_info_layout.setSpacing(0)
        self._user_info_widget = QWidget()
        self._user_info_widget.setLayout(self._user_info_layout)

        self._shots_info_layout = QVBoxLayout()
        self._shots_info_layout.setContentsMargins(0, 0, 0, 0)
        self._shots_info_layout.setSpacing(0)
        self._shots_info_widget = QWidget()
        self._shots_info_widget.setLayout(self._shots_info_layout)

        self._tab_widget = tabs.TearOffTabWidget()
        self._tab_widget.setTabsClosable(False)
        self._tab_widget.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
        self._tab_widget.setMinimumHeight(330)

        self._assets_widget = self.ASSET_WIDGET_CLASS(project=self._project, show_viewer_menu=True)
        self._shots_widget = self.SHOTS_WIDGET_CLASS(project=self._project)
        self._settings_widget = AssetsManagerSettingsWidget(settings=self.settings)

        assets_widget = QWidget()
        assets_layout = QVBoxLayout()
        assets_layout.setContentsMargins(0, 0, 0, 0)
        assets_layout.setSpacing(0)
        assets_widget.setLayout(assets_layout)

        shots_widget = QWidget()
        shots_layout = QVBoxLayout()
        shots_layout.setContentsMargins(0, 0, 0, 0)
        shots_layout.setSpacing(0)
        shots_widget.setLayout(shots_layout)

        self.main_layout.addWidget(self._main_stack)

        self._main_stack.addWidget(no_assets_widget)
        self._main_stack.addWidget(self._tab_widget)
        self._main_stack.addWidget(self._settings_widget)

        self._attrs_stack.addWidget(no_items_widget)
        self._attrs_stack.addWidget(self._waiter)
        self._attrs_stack.addWidget(self._user_info_widget)

        self._shots_stack.addWidget(no_shot_selected_widget)
        self._shots_stack.addWidget(self._shots_info_widget)

        assets_splitter = QSplitter(Qt.Horizontal)
        assets_splitter.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
        assets_splitter.addWidget(self._assets_widget)
        assets_splitter.addWidget(self._attrs_stack)

        shots_splitter = QSplitter(Qt.Horizontal)
        shots_splitter.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
        shots_splitter.addWidget(self._shots_widget)
        shots_splitter.addWidget(self._shots_stack)

        self._tab_widget.addTab(assets_splitter, 'Assets')
        self._tab_widget.addTab(shots_splitter, 'Sequences | Shots')

        if not artellapipe.Tracker().needs_login():
            self._main_stack.slide_in_index(1)
示例#23
0
    def stamp_image(self, source, output, config_dict=None):
        res_x = image.get_image_width(source)
        res_y = image.get_image_height(source)

        top_band = config_dict.get('top_band', None)
        bottom_band = config_dict.get('bottom_band', None)
        # font_file = config_dict.get('text_font', 'Arial.ttf')
        font_file = 'Arial.ttf'
        font_family = config_dict.get('text_font_family', 'Regular')
        text_margin_x = config_dict.get('text_margin_x', 300)
        text_margin_y = config_dict.get('text_margin_y', 100)
        framecode = config_dict.get('framecode', '00:00:00:00')

        top_band_height = 0
        if top_band and os.path.isfile(top_band):
            top_band_height = image.get_image_height(top_band)
            res_y += top_band_height

        bottom_band_height = 0
        if bottom_band and os.path.isfile(bottom_band):
            bottom_band_height = image.get_image_height(bottom_band)
            res_y += bottom_band_height

        font_db = QFontDatabase()
        font_db.addApplicationFont(font_file)
        text_font = QFont(font_family)
        font_size = 32
        font_height = QFontMetrics(text_font).height()
        user = str(artellapipe.Tracker().get_user_name())
        shot_name = config_dict.get('shot_name', '') or ''
        task_name = config_dict.get('task_name', '') or ''
        task_comment = str(config_dict.get('task_comment', ''))

        if artellapipe.Tracker().is_tracking_available():
            fps = artellapipe.Tracker().get_project_fps()
        else:
            fps = 24

        camera = str(config_dict.get('camera', 'No camera'))
        start_frame = str(config_dict.get('start_frame', None))
        focal_length = None
        if camera and camera != 'No camera' and tp.Dcc.object_exists(camera):
            focal_length = tp.Dcc.get_camera_focal_length(camera)

        sequence = fileseq.findSequenceOnDisk(source)
        frames_dict = OrderedDict()
        total_frames = len(sequence)
        for i in range(total_frames):
            sequence_frame = sequence[i]
            empty_frame_path = self._get_temp_file_path(
                sequence_frame, 'empty')
            empty_frame = image.create_empty_image(
                empty_frame_path,
                resolution_x=res_x,
                resolution_y=res_y,
                background_color=[92, 92, 92])
            frames_dict[sequence_frame] = empty_frame

        frame_outputs = dict()
        for i, (frame, empty_frame) in enumerate(frames_dict.items()):
            # Overlay playblast
            stream = ffmpeglib.overlay_inputs(empty_frame,
                                              frame,
                                              y=top_band_height)

            # Overlay top and bottom bands
            if top_band:
                stream = ffmpeglib.overlay_inputs(stream, top_band)
            if bottom_band:
                stream = ffmpeglib.overlay_inputs(stream,
                                                  bottom_band,
                                                  y=res_y - bottom_band_height)

            # Draw task and comment texts
            stream = ffmpeglib.draw_text(stream,
                                         task_name,
                                         x=40,
                                         y=text_margin_y + font_height,
                                         font_file=font_file,
                                         font_size=font_size)
            stream = ffmpeglib.draw_text(stream,
                                         task_comment,
                                         x=40,
                                         y=text_margin_y + (font_height * 2) +
                                         25,
                                         font_file=font_file,
                                         font_size=font_size)

            # Draw frame
            current_frame = int(float(start_frame)) + i
            fps_text = '{} ({})'.format(current_frame, i + 1)
            stream = ffmpeglib.draw_text(stream,
                                         fps_text,
                                         x=40,
                                         y=res_y - font_height -
                                         text_margin_y - 70,
                                         font_file=font_file,
                                         font_size=font_size)

            # Draw camera name and focal length texts
            camera_text_width = QFontMetrics(text_font).width(camera)
            stream = ffmpeglib.draw_text(stream,
                                         camera,
                                         x=res_x / 2 - camera_text_width,
                                         y=res_y - font_height -
                                         text_margin_y - 70,
                                         font_file=font_file,
                                         font_size=font_size)
            fps_text = 'FPS: {}'.format(str(int(float(fps))))
            if focal_length:
                fps_text = ' Focal Length: {}'.format(focal_length)
            fps_focal_length_width = QFontMetrics(text_font).width(
                str(fps_text))
            stream = ffmpeglib.draw_text(stream,
                                         fps_text,
                                         x=res_x / 2 - fps_focal_length_width,
                                         y=res_y - font_height -
                                         text_margin_y - 20,
                                         font_file=font_file,
                                         font_size=font_size)

            # Draw user and shot texts
            user_text_width = QFontMetrics(text_font).width(user)
            shot_text_width = QFontMetrics(text_font).width(shot_name)
            user_shot_text_width = max(user_text_width, shot_text_width)
            stream = ffmpeglib.draw_text(
                stream,
                user,
                x=res_x - user_shot_text_width - text_margin_x,
                y=res_y - font_height - text_margin_y - 70,
                font_file=font_file,
                font_size=font_size)
            stream = ffmpeglib.draw_text(
                stream,
                shot_name,
                x=res_x - user_shot_text_width - text_margin_x,
                y=res_y - font_height - text_margin_y - 20,
                font_file=font_file,
                font_size=font_size)

            new_file_path = self._get_temp_file_path(empty_frame,
                                                     'main',
                                                     index=i)
            frame_save = ffmpeglib.save_to_file(stream, new_file_path)
            frame_outputs[new_file_path] = frame_save

        if not frame_outputs:
            return

        frame_paths = frame_outputs.keys()
        frame_outs = frame_outputs.values()
        ffmpeglib.run_multiples_outputs_at_once(frame_outs)

        for frame_path in frame_paths:
            if not frame_path or not os.path.isfile(frame_path):
                LOGGER.warning(
                    'Some frames were not generated properly. Aborting operation ...'
                )
                return

        video_file_path = self._get_temp_file_path(source, 'video')
        if not video_file_path.endswith('.mp4'):
            video_file_path_split = os.path.splitext(video_file_path)
            video_file_path = '{}.mp4'.format(video_file_path_split[0])
        ffmpeglib.create_video_from_sequence_file(frame_paths[0],
                                                  video_file_path)
        if not os.path.isfile(video_file_path):
            LOGGER.error('Error while stamping playblast video ...')
            return

        # scale = ffmpeglib.scale_video(video_file_path, 1920, 1080)
        # ffmpeglib.save_to_file(scale, output, run_stream=True)

        draw_timestamp_stream = ffmpeglib.draw_timestamp_on_video(
            video_file_path,
            text='Time: ',
            x=40,
            y=res_y - font_height - text_margin_y - 20,
            font_file=font_file,
            font_size=font_size,
            timecode_rate=fps,
            timecode=framecode)
        ffmpeglib.save_to_file(draw_timestamp_stream, output, run_stream=True)

        return output