def __init__(self, timeline, parent=None):
        QWidget.__init__(self, parent)
        self.layout = QVBoxLayout()
        self.setLayout(self.layout)

        # Setup video preview QWidget
        self.videoPreview = VideoWidget(self)
        self.layout.addWidget(self.videoPreview)

        # Set max size of video preview (for speed)
        viewport_rect = self.videoPreview.centeredViewport(self.videoPreview.width(), self.videoPreview.height())
        timeline.SetMaxSize(viewport_rect.width(), viewport_rect.height())

        self.setLayout(self.layout)

        self.initialized = False

        # Start the preview thread
        self.preview_parent = PreviewParent()
        self.preview_parent.Init(self, timeline, self.videoPreview)
        self.preview_thread = self.preview_parent.worker

        # Set pause callback
        self.PauseSignal.connect(self.handlePausedVideo)

        self.hover = YYPlayerHover(self, self)
Beispiel #2
0
    def __init__(self):
        QWidget.__init__(self)
        # Setup video preview QWidget
        self.videoPreview = VideoWidget()
        #self.layout().insertWidget(0, self.videoPreview)
        wlayout = QHBoxLayout()
        wlayout.addWidget(self.videoPreview)
        self.setLayout(wlayout)

        # Start the preview thread
        self.preview_parent = PreviewParent(self)
        self.preview_parent.MInit(self, self.videoPreview)
        self.preview_thread = self.preview_parent.worker
        self.setFixedSize(600, 800)
        self.initialized = True
    def __init__(self, cuts_json, clips_json, preview=False):
        _ = get_app()._tr

        # Create dialog class
        QDialog.__init__(self)

        # Load UI from designer
        ui_util.load_ui(self, self.ui_path)

        # Init UI
        ui_util.init_ui(self)

        # Track metrics
        track_metric_screen("cutting-screen")

        # If preview, hide cutting controls
        if preview:
            self.lblInstructions.setVisible(False)
            self.widgetControls.setVisible(False)
            self.setWindowTitle(_("Preview"))

        self.start_frame = 1
        self.start_image = None
        self.end_frame = 1
        self.end_image = None

        project = get_app().project

        # Keep track of file object
        #self.file = file
        self.file_path = file.absolute_path()
        self.video_length = int(file.data['video_length'])
        self.fps_num = int(file.data['fps']['num'])
        self.fps_den = int(file.data['fps']['den'])
        self.fps = float(self.fps_num) / float(self.fps_den)
        self.width = int(file.data['width'])
        self.height = int(file.data['height'])
        self.sample_rate = int(file.data['sample_rate'])
        self.channels = int(file.data['channels'])
        self.channel_layout = int(file.data['channel_layout'])

        # Open video file with Reader
        log.info(self.file_path)

        # Create an instance of a libopenshot Timeline object
        self.r = openshot.Timeline(
            self.width, self.height,
            openshot.Fraction(self.fps_num, self.fps_den), self.sample_rate,
            self.channels, self.channel_layout)
        self.r.info.channel_layout = self.channel_layout

        try:
            # Add clip for current preview file
            self.clip = openshot.Clip(self.file_path)

            # Show waveform for audio files
            if not self.clip.Reader().info.has_video and self.clip.Reader(
            ).info.has_audio:
                self.clip.Waveform(True)

            # Set has_audio property
            self.r.info.has_audio = self.clip.Reader().info.has_audio

            if preview:
                # Display frame #'s during preview
                self.clip.display = openshot.FRAME_DISPLAY_CLIP

            self.r.AddClip(self.clip)
        except:
            log.error('Failed to load media file into preview player: %s' %
                      self.file_path)
            return

        # Add Video Widget
        self.videoPreview = VideoWidget()
        self.videoPreview.setSizePolicy(QSizePolicy.Preferred,
                                        QSizePolicy.Expanding)
        self.verticalLayout.insertWidget(0, self.videoPreview)

        # Set max size of video preview (for speed)
        viewport_rect = self.videoPreview.centeredViewport(
            self.videoPreview.width(), self.videoPreview.height())
        self.r.SetMaxSize(viewport_rect.width(), viewport_rect.height())

        # Open reader
        self.r.Open()

        # Start the preview thread
        self.initialized = False
        self.transforming_clip = False
        self.preview_parent = PreviewParent()
        self.preview_parent.Init(self, self.r, self.videoPreview)
        self.preview_thread = self.preview_parent.worker

        # Set slider constraints
        self.sliderIgnoreSignal = False
        self.sliderVideo.setMinimum(1)
        self.sliderVideo.setMaximum(self.video_length)
        self.sliderVideo.setSingleStep(1)
        self.sliderVideo.setSingleStep(1)
        self.sliderVideo.setPageStep(24)

        # Determine if a start or end attribute is in this file
        start_frame = 1
        if 'start' in self.file.data.keys():
            start_frame = (float(self.file.data['start']) * self.fps) + 1

        # Display start frame (and then the previous frame)
        QTimer.singleShot(
            500, functools.partial(self.sliderVideo.setValue, start_frame + 1))
        QTimer.singleShot(
            600, functools.partial(self.sliderVideo.setValue, start_frame))

        # Connect signals
        self.actionPlay.triggered.connect(self.actionPlay_Triggered)
        self.btnPlay.clicked.connect(self.btnPlay_clicked)
        self.sliderVideo.valueChanged.connect(self.sliderVideo_valueChanged)
        self.btnStart.clicked.connect(self.btnStart_clicked)
        self.btnEnd.clicked.connect(self.btnEnd_clicked)
        self.btnClear.clicked.connect(self.btnClear_clicked)
        self.btnAddClip.clicked.connect(self.btnAddClip_clicked)
        self.initialized = True
class YYCutPlayerDlg(QDialog):
    """ Cutting Dialog """

    # Path to ui file
    ui_path = os.path.join(info.PATH, 'windows', 'ui', 'cutting.ui')

    # Signals for preview thread
    previewFrameSignal = pyqtSignal(int)
    refreshFrameSignal = pyqtSignal()
    LoadFileSignal = pyqtSignal(str)
    PlaySignal = pyqtSignal(int)
    PauseSignal = pyqtSignal()
    SeekSignal = pyqtSignal(int)
    SpeedSignal = pyqtSignal(float)
    StopSignal = pyqtSignal()

    def __init__(self, cuts_json, clips_json, preview=False):
        _ = get_app()._tr

        # Create dialog class
        QDialog.__init__(self)

        # Load UI from designer
        ui_util.load_ui(self, self.ui_path)

        # Init UI
        ui_util.init_ui(self)

        # Track metrics
        track_metric_screen("cutting-screen")

        # If preview, hide cutting controls
        if preview:
            self.lblInstructions.setVisible(False)
            self.widgetControls.setVisible(False)
            self.setWindowTitle(_("Preview"))

        self.start_frame = 1
        self.start_image = None
        self.end_frame = 1
        self.end_image = None

        project = get_app().project

        # Keep track of file object
        #self.file = file
        self.file_path = file.absolute_path()
        self.video_length = int(file.data['video_length'])
        self.fps_num = int(file.data['fps']['num'])
        self.fps_den = int(file.data['fps']['den'])
        self.fps = float(self.fps_num) / float(self.fps_den)
        self.width = int(file.data['width'])
        self.height = int(file.data['height'])
        self.sample_rate = int(file.data['sample_rate'])
        self.channels = int(file.data['channels'])
        self.channel_layout = int(file.data['channel_layout'])

        # Open video file with Reader
        log.info(self.file_path)

        # Create an instance of a libopenshot Timeline object
        self.r = openshot.Timeline(
            self.width, self.height,
            openshot.Fraction(self.fps_num, self.fps_den), self.sample_rate,
            self.channels, self.channel_layout)
        self.r.info.channel_layout = self.channel_layout

        try:
            # Add clip for current preview file
            self.clip = openshot.Clip(self.file_path)

            # Show waveform for audio files
            if not self.clip.Reader().info.has_video and self.clip.Reader(
            ).info.has_audio:
                self.clip.Waveform(True)

            # Set has_audio property
            self.r.info.has_audio = self.clip.Reader().info.has_audio

            if preview:
                # Display frame #'s during preview
                self.clip.display = openshot.FRAME_DISPLAY_CLIP

            self.r.AddClip(self.clip)
        except:
            log.error('Failed to load media file into preview player: %s' %
                      self.file_path)
            return

        # Add Video Widget
        self.videoPreview = VideoWidget()
        self.videoPreview.setSizePolicy(QSizePolicy.Preferred,
                                        QSizePolicy.Expanding)
        self.verticalLayout.insertWidget(0, self.videoPreview)

        # Set max size of video preview (for speed)
        viewport_rect = self.videoPreview.centeredViewport(
            self.videoPreview.width(), self.videoPreview.height())
        self.r.SetMaxSize(viewport_rect.width(), viewport_rect.height())

        # Open reader
        self.r.Open()

        # Start the preview thread
        self.initialized = False
        self.transforming_clip = False
        self.preview_parent = PreviewParent()
        self.preview_parent.Init(self, self.r, self.videoPreview)
        self.preview_thread = self.preview_parent.worker

        # Set slider constraints
        self.sliderIgnoreSignal = False
        self.sliderVideo.setMinimum(1)
        self.sliderVideo.setMaximum(self.video_length)
        self.sliderVideo.setSingleStep(1)
        self.sliderVideo.setSingleStep(1)
        self.sliderVideo.setPageStep(24)

        # Determine if a start or end attribute is in this file
        start_frame = 1
        if 'start' in self.file.data.keys():
            start_frame = (float(self.file.data['start']) * self.fps) + 1

        # Display start frame (and then the previous frame)
        QTimer.singleShot(
            500, functools.partial(self.sliderVideo.setValue, start_frame + 1))
        QTimer.singleShot(
            600, functools.partial(self.sliderVideo.setValue, start_frame))

        # Connect signals
        self.actionPlay.triggered.connect(self.actionPlay_Triggered)
        self.btnPlay.clicked.connect(self.btnPlay_clicked)
        self.sliderVideo.valueChanged.connect(self.sliderVideo_valueChanged)
        self.btnStart.clicked.connect(self.btnStart_clicked)
        self.btnEnd.clicked.connect(self.btnEnd_clicked)
        self.btnClear.clicked.connect(self.btnClear_clicked)
        self.btnAddClip.clicked.connect(self.btnAddClip_clicked)
        self.initialized = True

    def actionPlay_Triggered(self):
        # Trigger play button (This action is invoked from the preview thread, so it must exist here)
        self.btnPlay.click()

    def movePlayhead(self, frame_number):
        """Update the playhead position"""

        # Move slider to correct frame position
        self.sliderIgnoreSignal = True
        self.sliderVideo.setValue(frame_number)
        self.sliderIgnoreSignal = False

        # Convert frame to seconds
        seconds = (frame_number - 1) / self.fps

        # Convert seconds to time stamp
        time_text = self.secondsToTime(seconds, self.fps_num, self.fps_den)
        timestamp = "%s:%s:%s:%s" % (time_text["hour"], time_text["min"],
                                     time_text["sec"], time_text["frame"])

        # Update label
        self.lblVideoTime.setText(timestamp)

    def btnPlay_clicked(self, force=None):
        log.info("btnPlay_clicked")

        if force == "pause":
            self.btnPlay.setChecked(False)
        elif force == "play":
            self.btnPlay.setChecked(True)

        if self.btnPlay.isChecked():
            log.info('play (icon to pause)')
            ui_util.setup_icon(self, self.btnPlay, "actionPlay",
                               "media-playback-pause")
            self.preview_thread.Play(self.video_length)
        else:
            log.info('pause (icon to play)')
            ui_util.setup_icon(self, self.btnPlay, "actionPlay",
                               "media-playback-start")  # to default
            self.preview_thread.Pause()

        # Send focus back to toolbar
        self.sliderVideo.setFocus()

    def sliderVideo_valueChanged(self, new_frame):
        if self.preview_thread and not self.sliderIgnoreSignal:
            log.info('sliderVideo_valueChanged')

            # Pause video
            self.btnPlay_clicked(force="pause")

            # Seek to new frame
            self.preview_thread.previewFrame(new_frame)

    def btnStart_clicked(self):
        """Start of clip button was clicked"""
        _ = get_app()._tr

        # Pause video
        self.btnPlay_clicked(force="pause")

        # Get the current frame
        current_frame = self.sliderVideo.value()

        # Check if starting frame less than end frame
        if self.btnEnd.isEnabled() and current_frame >= self.end_frame:
            # Handle exception
            msg = QMessageBox()
            msg.setText(
                _("Please choose valid 'start' and 'end' values for your clip."
                  ))
            msg.exec_()
            return

        # remember frame #
        self.start_frame = current_frame

        # Save thumbnail image
        self.start_image = os.path.join(info.USER_PATH, 'thumbnail',
                                        '%s.png' % self.start_frame)
        self.r.GetFrame(self.start_frame).Thumbnail(self.start_image, 160, 90,
                                                    '', '', '#000000', True)

        # Set CSS on button
        self.btnStart.setStyleSheet('background-image: url(%s);' %
                                    self.start_image.replace('\\', '/'))

        # Enable end button
        self.btnEnd.setEnabled(True)
        self.btnClear.setEnabled(True)

        # Send focus back to toolbar
        self.sliderVideo.setFocus()

        log.info('btnStart_clicked, current frame: %s' % self.start_frame)

    def btnEnd_clicked(self):
        """End of clip button was clicked"""
        _ = get_app()._tr

        # Pause video
        self.btnPlay_clicked(force="pause")

        # Get the current frame
        current_frame = self.sliderVideo.value()

        # Check if ending frame greater than start frame
        if current_frame <= self.start_frame:
            # Handle exception
            msg = QMessageBox()
            msg.setText(
                _("Please choose valid 'start' and 'end' values for your clip."
                  ))
            msg.exec_()
            return

        # remember frame #
        self.end_frame = current_frame

        # Save thumbnail image
        self.end_image = os.path.join(info.USER_PATH, 'thumbnail',
                                      '%s.png' % self.end_frame)
        self.r.GetFrame(self.end_frame).Thumbnail(self.end_image, 160, 90, '',
                                                  '', '#000000', True)

        # Set CSS on button
        self.btnEnd.setStyleSheet('background-image: url(%s);' %
                                  self.end_image.replace('\\', '/'))

        # Enable create button
        self.btnAddClip.setEnabled(True)

        # Send focus back to toolbar
        self.sliderVideo.setFocus()

        log.info('btnEnd_clicked, current frame: %s' % self.end_frame)

    def btnClear_clicked(self):
        """Clear the current clip and reset the form"""
        log.info('btnClear_clicked')

        # Reset form
        self.clearForm()

    def clearForm(self):
        """Clear all form controls"""
        # Clear buttons
        self.start_frame = 1
        self.end_frame = 1
        self.start_image = ''
        self.end_image = ''
        self.btnStart.setStyleSheet('background-image: None;')
        self.btnEnd.setStyleSheet('background-image: None;')

        # Clear text
        self.txtName.setText('')

        # Disable buttons
        self.btnEnd.setEnabled(False)
        self.btnAddClip.setEnabled(False)
        self.btnClear.setEnabled(False)

    def btnAddClip_clicked(self):
        """Add the selected clip to the project"""
        log.info('btnAddClip_clicked')

        # Remove unneeded attributes
        if 'name' in self.file.data.keys():
            self.file.data.pop('name')

        # Save new file
        self.file.id = None
        self.file.key = None
        self.file.type = 'insert'
        self.file.data['start'] = (self.start_frame - 1) / self.fps
        self.file.data['end'] = (self.end_frame - 1) / self.fps
        if self.txtName.text():
            self.file.data['name'] = self.txtName.text()
        self.file.save()

        # Reset form
        self.clearForm()

    def padNumber(self, value, pad_length):
        format_mask = '%%0%sd' % pad_length
        return format_mask % value

    def secondsToTime(self, secs, fps_num, fps_den):
        # calculate time of playhead
        milliseconds = secs * 1000
        sec = math.floor(milliseconds / 1000)
        milli = milliseconds % 1000
        min = math.floor(sec / 60)
        sec = sec % 60
        hour = math.floor(min / 60)
        min = min % 60
        day = math.floor(hour / 24)
        hour = hour % 24
        week = math.floor(day / 7)
        day = day % 7

        frame = round((milli / 1000.0) * (fps_num / fps_den)) + 1
        return {
            "week": self.padNumber(week, 2),
            "day": self.padNumber(day, 2),
            "hour": self.padNumber(hour, 2),
            "min": self.padNumber(min, 2),
            "sec": self.padNumber(sec, 2),
            "milli": self.padNumber(milli, 2),
            "frame": self.padNumber(frame, 2)
        }

    # TODO: Remove these 4 methods
    def accept(self):
        """ Ok button clicked """
        log.info('accept')

    def close(self):
        """ Actually close window and accept dialog """
        log.info('close')

    def closeEvent(self, event):
        log.info('closeEvent')

        # Stop playback
        self.preview_parent.worker.Stop()

        # Stop preview thread (and wait for it to end)
        self.preview_parent.worker.kill()
        self.preview_parent.background.exit()
        self.preview_parent.background.wait(5000)

        # Close readers
        self.r.Close()
        self.clip.Close()
        self.r.ClearAllCache()

    def reject(self):
        log.info('reject')
Beispiel #5
0
    def __init__(self, file=None):

        # Create dialog class
        QDialog.__init__(self)

        # Load UI from designer
        ui_util.load_ui(self, self.ui_path)

        # Init UI
        ui_util.init_ui(self)

        # Track metrics
        track_metric_screen("cutting-screen")

        self.start_frame = 1
        self.start_image = None
        self.end_frame = 1
        self.end_image = None

        # Keep track of file object
        self.file = file
        self.file_path = file.absolute_path()
        self.video_length = int(file.data['video_length'])
        self.fps_num = int(file.data['fps']['num'])
        self.fps_den = int(file.data['fps']['den'])
        self.fps = float(self.fps_num) / float(self.fps_den)

        # Open video file with Reader
        log.info(self.file_path)
        self.r = openshot.FFmpegReader(self.file_path)
        self.r.Open()

        # Add Video Widget
        self.videoPreview = VideoWidget()
        self.videoPreview.setSizePolicy(QSizePolicy.Preferred,
                                        QSizePolicy.Expanding)
        self.verticalLayout.insertWidget(0, self.videoPreview)

        # Start the preview thread
        self.preview_parent = PreviewParent()
        self.preview_parent.Init(self, self.r, self.videoPreview)
        self.preview_thread = self.preview_parent.worker

        # Set slider constraints
        self.sliderIgnoreSignal = False
        self.sliderVideo.setMinimum(1)
        self.sliderVideo.setMaximum(self.video_length)
        self.sliderVideo.setSingleStep(1)
        self.sliderVideo.setSingleStep(1)
        self.sliderVideo.setPageStep(24)

        # Determine if a start or end attribute is in this file
        start_frame = 1
        if 'start' in self.file.data.keys():
            start_frame = (float(self.file.data['start']) * self.fps) + 1

        # Display start frame (and then the previous frame)
        QTimer.singleShot(
            500, functools.partial(self.sliderVideo.setValue, start_frame + 1))
        QTimer.singleShot(
            600, functools.partial(self.sliderVideo.setValue, start_frame))

        # Connect signals
        self.btnPlay.clicked.connect(self.btnPlay_clicked)
        self.sliderVideo.valueChanged.connect(self.sliderVideo_valueChanged)
        self.btnStart.clicked.connect(self.btnStart_clicked)
        self.btnEnd.clicked.connect(self.btnEnd_clicked)
        self.btnClear.clicked.connect(self.btnClear_clicked)
        self.btnAddClip.clicked.connect(self.btnAddClip_clicked)
Beispiel #6
0
class Player(QWidget, updates.UpdateWatcher):
    file_lists = []
    current_timeline = None
    initialized = False

    previewFrameSignal = pyqtSignal(int)
    refreshFrameSignal = pyqtSignal()

    LoadFileSignal = pyqtSignal(str)
    PlaySignal = pyqtSignal(int)
    PauseSignal = pyqtSignal()
    StopSignal = pyqtSignal()
    SeekSignal = pyqtSignal(int)
    SpeedSignal = pyqtSignal(float)

    def __init__(self):
        QWidget.__init__(self)
        # Setup video preview QWidget
        self.videoPreview = VideoWidget()
        #self.layout().insertWidget(0, self.videoPreview)
        wlayout = QHBoxLayout()
        wlayout.addWidget(self.videoPreview)
        self.setLayout(wlayout)

        # Start the preview thread
        self.preview_parent = PreviewParent(self)
        self.preview_parent.MInit(self, self.videoPreview)
        self.preview_thread = self.preview_parent.worker
        self.setFixedSize(600, 800)
        self.initialized = True

    # Save window settings on close
    def closeEvent(self, event):
        # Stop threads
        self.StopSignal.emit()

        # Process any queued events
        QCoreApplication.processEvents()

        # Stop preview thread (and wait for it to end)
        self.preview_thread.player.CloseAudioDevice()
        self.preview_thread.kill()
        self.preview_parent.background.exit()
        self.preview_parent.background.wait(5000)

    def open(self, filename):
        self.LoadFileSignal.emit(filename)

    def play(self):
        self.PlaySignal.emit(99999)

    def pause(self):
        self.PauseSignal.emit()

    def stop(self):
        self.StopSignal.emit()

    def seek(self, position):
        self.SeekSignal.emit(position)

    def speed(self, sd):
        self.SpeedSignal.emit(sd)
    def __init__(self, cuts=[], preview=False):
        _ = get_app()._tr

        # Create dialog class
        QDialog.__init__(self)

        # Load UI from designer
        ui_util.load_ui(self, self.ui_path)

        # Init UI
        ui_util.init_ui(self)

        # Track metrics
        track_metric_screen("cutting-screen")

        # If preview, hide cutting controls
        if preview:
            self.lblInstructions.setVisible(False)
            self.widgetControls.setVisible(False)
            self.setWindowTitle(_("Preview"))

        self.start_frame = 1
        self.start_image = None
        self.end_frame = 1
        self.end_image = None

        #timeline = get_app().window.timeline_sync.timeline
        #app = get_app()
        #project = app.project

        # Get some settings from the project
        '''
        self.fps = project.get(["fps"])
        self.width = project.get(["width"])
        self.height = project.get(["height"])
        self.sample_rate = project.get(["sample_rate"])
        self.channels = project.get(["channels"])
        self.channel_layout = project.get(["channel_layout"])
        self.fps_num = int(self.fps['num'])
        self.fps_den = int(self.fps['den'])
        self.fps = float(self.fps_num) / float(self.fps_den)
        '''
        # Get the original timeline settings
        self.width = get_app().window.timeline_sync.timeline.info.width
        self.height = get_app().window.timeline_sync.timeline.info.height
        self.fps = get_app().window.timeline_sync.timeline.info.fps
        self.sample_rate = get_app(
        ).window.timeline_sync.timeline.info.sample_rate
        self.channels = get_app().window.timeline_sync.timeline.info.channels
        self.channel_layout = get_app(
        ).window.timeline_sync.timeline.info.channel_layout
        self.fps_num = int(self.fps.num)
        self.fps_den = int(self.fps.den)
        #self.fps = float(self.fps_num) / float(self.fps_den)
        '''
        # Keep track of file object
        self.file = file
        self.file_path = file.absolute_path()
        self.video_length = 0
        self.fps_num = int(file.data['fps']['num'])
        self.fps_den = int(file.data['fps']['den'])
        self.fps = float(self.fps_num) / float(self.fps_den)
        self.width = int(file.data['width'])
        self.height = int(file.data['height'])
        self.sample_rate = int(file.data['sample_rate'])
        self.channels = int(file.data['channels'])
        self.channel_layout = int(file.data['channel_layout'])

        # Open video file with Reader
        log.info(self.file_path)
        '''

        self.video_length = 0
        # Create an instance of a libopenshot Timeline object
        self.r = openshot.Timeline(
            self.width, self.height,
            openshot.Fraction(self.fps_num, self.fps_den), self.sample_rate,
            self.channels, self.channel_layout)
        self.r.info.channel_layout = self.channel_layout
        '''
        #cuts = [{"start_seconds":8.466666666666667, "end_seconds":15.3}, {"start_seconds":18.9, "end_seconds":22.133333333333333}]
        position = 0

        print(cuts)

        cs = app.window.timeline_sync.timeline.Clips()
        for c in cs:
            print("-----id=", c.Id())
            clip_json = Clip.filter(id=c.Id())
            path = clip_json[0].data["reader"]["path"]
            print("==============", path, c.Position())
            offset = c.Position()

            for cut in cuts:
                try:
                    # Add clip for current preview file
                    clip = openshot.Clip(path)
                    self.clips.append(clip)

                    # Show waveform for audio files
                    if not clip.Reader().info.has_video and clip.Reader().info.has_audio:
                        clip.Waveform(True)

                    # Set has_audio property
                    self.r.info.has_audio = clip.Reader().info.has_audio

                    if preview:
                        # Display frame #'s during preview
                        clip.display = openshot.FRAME_DISPLAY_CLIP
                    
                    start = float(cut["start_seconds"] - offset)
                    end = float(cut["end_seconds"] - offset)
                    print("=======================-------start:", start, "end:", end)
                    clip.Start(start)
                    clip.End(end)
                    #clip.Position(0)
                    clip.Position(position)
                    position = position + (end - start) - offset
                    #clip.Duration(end-start)
                    self.r.AddClip(clip)
                    self.video_length = self.video_length + (end - start)
                except:
                    log.error('Failed to load media file into preview player: %s' % self.file_path)
                    return
        '''

        self.clips, self.video_length = CutsToClips(cuts)
        for clip in self.clips:
            # Show waveform for audio files
            if not clip.Reader().info.has_video and clip.Reader(
            ).info.has_audio:
                clip.Waveform(True)
            if preview:
                # Display frame #'s during preview
                clip.display = openshot.FRAME_DISPLAY_CLIP
            self.r.AddClip(clip)

        #self.video_length = self.video_length * self.fps_num / self.fps_den
        # Add Video Widget
        self.videoPreview = VideoWidget()
        self.videoPreview.setSizePolicy(QSizePolicy.Preferred,
                                        QSizePolicy.Expanding)
        self.verticalLayout.insertWidget(0, self.videoPreview)

        # Set max size of video preview (for speed)
        viewport_rect = self.videoPreview.centeredViewport(
            self.videoPreview.width(), self.videoPreview.height())
        self.r.SetMaxSize(viewport_rect.width(), viewport_rect.height())

        # Open reader
        self.r.Open()

        # Start the preview thread
        self.initialized = False
        self.transforming_clip = False
        self.preview_parent = PreviewParent()
        self.preview_parent.Init(self, self.r, self.videoPreview)
        self.preview_thread = self.preview_parent.worker

        # Set slider constraints
        self.sliderIgnoreSignal = False
        self.sliderVideo.setMinimum(1)
        self.sliderVideo.setMaximum(self.video_length)
        self.sliderVideo.setSingleStep(1)
        self.sliderVideo.setSingleStep(1)
        self.sliderVideo.setPageStep(24)

        # Determine if a start or end attribute is in this file
        start_frame = 1
        #if 'start' in self.file.data.keys():
        #    start_frame = (float(self.file.data['start']) * self.fps) + 1

        # Display start frame (and then the previous frame)
        QTimer.singleShot(
            500, functools.partial(self.sliderVideo.setValue, start_frame + 1))
        QTimer.singleShot(
            600, functools.partial(self.sliderVideo.setValue, start_frame))

        # Connect signals
        self.actionPlay.triggered.connect(self.actionPlay_Triggered)
        self.btnPlay.clicked.connect(self.btnPlay_clicked)
        self.sliderVideo.valueChanged.connect(self.sliderVideo_valueChanged)
        self.btnStart.clicked.connect(self.btnStart_clicked)
        self.btnEnd.clicked.connect(self.btnEnd_clicked)
        self.btnClear.clicked.connect(self.btnClear_clicked)
        self.btnAddClip.clicked.connect(self.btnAddClip_clicked)
        self.initialized = True
class YYPlayerBaseWidget(QWidget, updates.UpdateWatcher):
    previewFrameSignal = pyqtSignal(int)
    refreshFrameSignal = pyqtSignal()
    LoadFileSignal = pyqtSignal(str)
    PlaySignal = pyqtSignal(int)
    PauseSignal = pyqtSignal()
    StopSignal = pyqtSignal()
    SeekSignal = pyqtSignal(int)
    SpeedSignal = pyqtSignal(float)

    movePlayheadSignal = pyqtSignal(float)
    PlayModeChangedSignal = pyqtSignal(int)
    MaxSizeChanged = pyqtSignal(object)

    def __init__(self, timeline, parent=None):
        QWidget.__init__(self, parent)
        self.layout = QVBoxLayout()
        self.setLayout(self.layout)

        # Setup video preview QWidget
        self.videoPreview = VideoWidget(self)
        self.layout.addWidget(self.videoPreview)

        # Set max size of video preview (for speed)
        viewport_rect = self.videoPreview.centeredViewport(self.videoPreview.width(), self.videoPreview.height())
        timeline.SetMaxSize(viewport_rect.width(), viewport_rect.height())

        self.setLayout(self.layout)

        self.initialized = False

        # Start the preview thread
        self.preview_parent = PreviewParent()
        self.preview_parent.Init(self, timeline, self.videoPreview)
        self.preview_thread = self.preview_parent.worker

        # Set pause callback
        self.PauseSignal.connect(self.handlePausedVideo)

        self.hover = YYPlayerHover(self, self)

    # Save window settings on close
    def closeEvent(self, event):
        # Stop threads
        self.StopSignal.emit()

        # Process any queued events
        QCoreApplication.processEvents()

        # Stop preview thread (and wait for it to end)
        self.preview_thread.player.CloseAudioDevice()
        self.preview_thread.kill()
        self.preview_parent.background.exit()
        self.preview_parent.background.wait(5000)

    def btnPlay_clicked(self, force=None):
        log.info("btnPlay_clicked")

        if force == "pause":
            self.hover.btnPlay.setChecked(False)
        elif force == "play":
            self.hover.btnPlay.setChecked(True)

        if self.hover.btnPlay.isChecked():
            log.info('play (icon to pause)')
            #ui_util.setup_icon(self, self.btnPlay, "actionPlay", "media-playback-pause")
            self.preview_thread.Play(1000)#======todo
        else:
            log.info('pause (icon to play)')
            #ui_util.setup_icon(self, self.btnPlay, "actionPlay", "media-playback-start")  # to default
            self.preview_thread.Pause()

        # Send focus back to toolbar
        #self.sliderVideo.setFocus()


    def resizeEvent(self, QResizeEvent):
        super().resizeEvent(QResizeEvent)
        self.hover.setGeometry((self.width() - self.hover.width()) / 2, 20, self.hover.width(), self.hover.height())

    def onModeChanged(self, current_mode):
        log.info('onModeChanged %s', current_mode)
        self.PlayModeChangedSignal.emit(current_mode)

    def Mode(self):
        return self.preview_thread.player.Mode()

    def handlePausedVideo(self):
        log.info("base handlePausedVideo")

    def onPlayFinished(self):
        log.info("base onPlayFinished")

    def movePlayhead(self, position_frames):
        log.info("movePlayhead %s", position_frames)
        """Update playhead position"""
Beispiel #9
0
    def __init__(self, file=None, clip=None):
        _ = get_app()._tr

        # Create dialog class
        QDialog.__init__(self)

        # Load UI from designer
        ui_util.load_ui(self, self.ui_path)

        # Init UI
        ui_util.init_ui(self)

        # Track metrics
        track_metric_screen("cutting-screen")

        self.start_frame = 1
        self.start_image = None
        self.end_frame = 1
        self.end_image = None
        self.current_frame = 1

        # Create region clip with Reader
        self.clip = openshot.Clip(clip.Reader())

        self.clip.Open()

        # Set region clip start and end
        self.clip.Start(clip.Start())
        self.clip.End(clip.End())
        self.clip.Id(get_app().project.generate_id())

        print("IDS {} {}".format(clip.Id(), self.clip.Id()))

        # Keep track of file object
        self.file = file
        self.file_path = file.absolute_path()

        c_info = clip.Reader().info
        self.fps = c_info.fps.ToInt(
        )  #float(self.fps_num) / float(self.fps_den)
        self.fps_num = self.fps  #int(file.data['fps']['num'])
        self.fps_den = 1  #int(file.data['fps']['den'])
        self.width = c_info.width  #int(file.data['width'])
        self.height = c_info.height  #int(file.data['height'])
        self.sample_rate = c_info.sample_rate  #int(file.data['sample_rate'])
        self.channels = c_info.channels  #int(file.data['channels'])
        self.channel_layout = c_info.channel_layout  #int(file.data['channel_layout'])
        self.video_length = int(self.clip.Duration() *
                                self.fps) + 1  #int(file.data['video_length'])

        # Apply effects to region frames
        for effect in clip.Effects():
            self.clip.AddEffect(effect)

        # Open video file with Reader
        log.info(self.clip.Reader())

        # Add Video Widget
        self.videoPreview = VideoWidget()
        self.videoPreview.setSizePolicy(QSizePolicy.Preferred,
                                        QSizePolicy.Expanding)
        self.verticalLayout.insertWidget(0, self.videoPreview)

        # Set aspect ratio to match source content
        aspect_ratio = openshot.Fraction(self.width, self.height)
        aspect_ratio.Reduce()
        self.videoPreview.aspect_ratio = aspect_ratio

        # Set max size of video preview (for speed)
        self.viewport_rect = self.videoPreview.centeredViewport(
            self.width, self.height)

        # Create an instance of a libopenshot Timeline object
        self.r = openshot.Timeline(
            self.viewport_rect.width(), self.viewport_rect.height(),
            openshot.Fraction(self.fps_num, self.fps_den), self.sample_rate,
            self.channels, self.channel_layout)
        self.r.info.channel_layout = self.channel_layout
        self.r.SetMaxSize(self.viewport_rect.width(),
                          self.viewport_rect.height())

        try:
            # Add clip for current preview file
            self.clip = openshot.Clip(self.file_path)

            # Show waveform for audio files
            if not self.clip.Reader().info.has_video and self.clip.Reader(
            ).info.has_audio:
                self.clip.Waveform(True)

            # Set has_audio property
            self.r.info.has_audio = self.clip.Reader().info.has_audio

            # Update video_length property of the Timeline object
            self.r.info.video_length = self.video_length

            self.r.AddClip(self.clip)

        except:
            log.error(
                'Failed to load media file into region select player: %s' %
                self.file_path)
            return

        # Open reader
        self.r.Open()

        # Start the preview thread
        self.initialized = False
        self.transforming_clip = False
        self.preview_parent = PreviewParent()
        self.preview_parent.Init(self, self.r, self.videoPreview)
        self.preview_thread = self.preview_parent.worker

        # Set slider constraints
        self.sliderIgnoreSignal = False
        self.sliderVideo.setMinimum(1)
        self.sliderVideo.setMaximum(self.video_length)
        self.sliderVideo.setSingleStep(1)
        self.sliderVideo.setSingleStep(1)
        self.sliderVideo.setPageStep(24)

        # Determine if a start or end attribute is in this file
        start_frame = 1
        # if 'start' in self.file.data.keys():
        #     start_frame = (float(self.file.data['start']) * self.fps) + 1

        # Display start frame (and then the previous frame)
        QTimer.singleShot(
            500, functools.partial(self.sliderVideo.setValue, start_frame + 1))
        QTimer.singleShot(
            600, functools.partial(self.sliderVideo.setValue, start_frame))

        # Add buttons
        self.cancel_button = QPushButton(_('Cancel'))
        self.process_button = QPushButton(_('Select Region'))
        self.buttonBox.addButton(self.process_button,
                                 QDialogButtonBox.AcceptRole)
        self.buttonBox.addButton(self.cancel_button,
                                 QDialogButtonBox.RejectRole)

        # Connect signals
        self.actionPlay.triggered.connect(self.actionPlay_Triggered)
        self.btnPlay.clicked.connect(self.btnPlay_clicked)
        self.sliderVideo.valueChanged.connect(self.sliderVideo_valueChanged)
        self.initialized = True

        get_app().window.SelectRegionSignal.emit(clip.Id())
Beispiel #10
0
class SelectRegion(QDialog):
    """ SelectRegion Dialog """

    # Path to ui file
    ui_path = os.path.join(info.PATH, 'windows', 'ui', 'region.ui')

    # Signals for preview thread
    previewFrameSignal = pyqtSignal(int)
    refreshFrameSignal = pyqtSignal()
    LoadFileSignal = pyqtSignal(str)
    PlaySignal = pyqtSignal(int)
    PauseSignal = pyqtSignal()
    SeekSignal = pyqtSignal(int)
    SpeedSignal = pyqtSignal(float)
    StopSignal = pyqtSignal()

    def __init__(self, file=None, clip=None):
        _ = get_app()._tr

        # Create dialog class
        QDialog.__init__(self)

        # Load UI from designer
        ui_util.load_ui(self, self.ui_path)

        # Init UI
        ui_util.init_ui(self)

        # Track metrics
        track_metric_screen("cutting-screen")

        self.start_frame = 1
        self.start_image = None
        self.end_frame = 1
        self.end_image = None
        self.current_frame = 1

        # Create region clip with Reader
        self.clip = openshot.Clip(clip.Reader())

        self.clip.Open()

        # Set region clip start and end
        self.clip.Start(clip.Start())
        self.clip.End(clip.End())
        self.clip.Id(get_app().project.generate_id())

        print("IDS {} {}".format(clip.Id(), self.clip.Id()))

        # Keep track of file object
        self.file = file
        self.file_path = file.absolute_path()

        c_info = clip.Reader().info
        self.fps = c_info.fps.ToInt(
        )  #float(self.fps_num) / float(self.fps_den)
        self.fps_num = self.fps  #int(file.data['fps']['num'])
        self.fps_den = 1  #int(file.data['fps']['den'])
        self.width = c_info.width  #int(file.data['width'])
        self.height = c_info.height  #int(file.data['height'])
        self.sample_rate = c_info.sample_rate  #int(file.data['sample_rate'])
        self.channels = c_info.channels  #int(file.data['channels'])
        self.channel_layout = c_info.channel_layout  #int(file.data['channel_layout'])
        self.video_length = int(self.clip.Duration() *
                                self.fps) + 1  #int(file.data['video_length'])

        # Apply effects to region frames
        for effect in clip.Effects():
            self.clip.AddEffect(effect)

        # Open video file with Reader
        log.info(self.clip.Reader())

        # Add Video Widget
        self.videoPreview = VideoWidget()
        self.videoPreview.setSizePolicy(QSizePolicy.Preferred,
                                        QSizePolicy.Expanding)
        self.verticalLayout.insertWidget(0, self.videoPreview)

        # Set aspect ratio to match source content
        aspect_ratio = openshot.Fraction(self.width, self.height)
        aspect_ratio.Reduce()
        self.videoPreview.aspect_ratio = aspect_ratio

        # Set max size of video preview (for speed)
        self.viewport_rect = self.videoPreview.centeredViewport(
            self.width, self.height)

        # Create an instance of a libopenshot Timeline object
        self.r = openshot.Timeline(
            self.viewport_rect.width(), self.viewport_rect.height(),
            openshot.Fraction(self.fps_num, self.fps_den), self.sample_rate,
            self.channels, self.channel_layout)
        self.r.info.channel_layout = self.channel_layout
        self.r.SetMaxSize(self.viewport_rect.width(),
                          self.viewport_rect.height())

        try:
            # Add clip for current preview file
            self.clip = openshot.Clip(self.file_path)

            # Show waveform for audio files
            if not self.clip.Reader().info.has_video and self.clip.Reader(
            ).info.has_audio:
                self.clip.Waveform(True)

            # Set has_audio property
            self.r.info.has_audio = self.clip.Reader().info.has_audio

            # Update video_length property of the Timeline object
            self.r.info.video_length = self.video_length

            self.r.AddClip(self.clip)

        except:
            log.error(
                'Failed to load media file into region select player: %s' %
                self.file_path)
            return

        # Open reader
        self.r.Open()

        # Start the preview thread
        self.initialized = False
        self.transforming_clip = False
        self.preview_parent = PreviewParent()
        self.preview_parent.Init(self, self.r, self.videoPreview)
        self.preview_thread = self.preview_parent.worker

        # Set slider constraints
        self.sliderIgnoreSignal = False
        self.sliderVideo.setMinimum(1)
        self.sliderVideo.setMaximum(self.video_length)
        self.sliderVideo.setSingleStep(1)
        self.sliderVideo.setSingleStep(1)
        self.sliderVideo.setPageStep(24)

        # Determine if a start or end attribute is in this file
        start_frame = 1
        # if 'start' in self.file.data.keys():
        #     start_frame = (float(self.file.data['start']) * self.fps) + 1

        # Display start frame (and then the previous frame)
        QTimer.singleShot(
            500, functools.partial(self.sliderVideo.setValue, start_frame + 1))
        QTimer.singleShot(
            600, functools.partial(self.sliderVideo.setValue, start_frame))

        # Add buttons
        self.cancel_button = QPushButton(_('Cancel'))
        self.process_button = QPushButton(_('Select Region'))
        self.buttonBox.addButton(self.process_button,
                                 QDialogButtonBox.AcceptRole)
        self.buttonBox.addButton(self.cancel_button,
                                 QDialogButtonBox.RejectRole)

        # Connect signals
        self.actionPlay.triggered.connect(self.actionPlay_Triggered)
        self.btnPlay.clicked.connect(self.btnPlay_clicked)
        self.sliderVideo.valueChanged.connect(self.sliderVideo_valueChanged)
        self.initialized = True

        get_app().window.SelectRegionSignal.emit(clip.Id())

    def actionPlay_Triggered(self):
        # Trigger play button (This action is invoked from the preview thread, so it must exist here)
        self.btnPlay.click()

    def movePlayhead(self, frame_number):
        """Update the playhead position"""

        self.current_frame = frame_number
        # Move slider to correct frame position
        self.sliderIgnoreSignal = True
        self.sliderVideo.setValue(frame_number)
        self.sliderIgnoreSignal = False

        # Convert frame to seconds
        seconds = (frame_number - 1) / self.fps

        # Convert seconds to time stamp
        time_text = time_parts.secondsToTime(seconds, self.fps_num,
                                             self.fps_den)
        timestamp = "%s:%s:%s:%s" % (time_text["hour"], time_text["min"],
                                     time_text["sec"], time_text["frame"])

        # Update label
        self.lblVideoTime.setText(timestamp)

    def btnPlay_clicked(self, force=None):
        log.info("btnPlay_clicked")

        if force == "pause":
            self.btnPlay.setChecked(False)
        elif force == "play":
            self.btnPlay.setChecked(True)

        if self.btnPlay.isChecked():
            log.info('play (icon to pause)')
            ui_util.setup_icon(self, self.btnPlay, "actionPlay",
                               "media-playback-pause")
            self.preview_thread.Play(self.video_length)
        else:
            log.info('pause (icon to play)')
            ui_util.setup_icon(self, self.btnPlay, "actionPlay",
                               "media-playback-start")  # to default
            self.preview_thread.Pause()

        # Send focus back to toolbar
        self.sliderVideo.setFocus()

    def sliderVideo_valueChanged(self, new_frame):
        if self.preview_thread and not self.sliderIgnoreSignal:
            log.info('sliderVideo_valueChanged')

            # Pause video
            self.btnPlay_clicked(force="pause")

            # Seek to new frame
            self.preview_thread.previewFrame(new_frame)

    def accept(self):
        """ Ok button clicked """

        self.shutdownPlayer()
        get_app().window.SelectRegionSignal.emit("")
        super(SelectRegion, self).accept()

    def shutdownPlayer(self):

        log.info('shutdownPlayer')

        # Stop playback
        self.preview_parent.worker.Stop()

        # Stop preview thread (and wait for it to end)
        self.preview_parent.worker.kill()
        self.preview_parent.background.exit()
        self.preview_parent.background.wait(5000)

        # Close readers
        self.clip.Close()
        # self.r.RemoveClip(self.clip)
        self.r.Close()
        # self.clip.Close()
        self.r.ClearAllCache()

    def reject(self):

        # Cancel dialog
        self.shutdownPlayer()
        get_app().window.SelectRegionSignal.emit("")
        super(SelectRegion, self).reject()
class YYMainWindow(QMainWindow, updates.UpdateWatcher):
    """ This class contains the logic for the main window widget """

    # Path to ui file
    ui_path = os.path.join(info.PATH, 'windows', 'ui', 'yy-main-window.ui')

    previewFrameSignal = pyqtSignal(int)
    refreshFrameSignal = pyqtSignal()
    LoadFileSignal = pyqtSignal(str)
    PlaySignal = pyqtSignal(int)
    PauseSignal = pyqtSignal()
    StopSignal = pyqtSignal()
    SeekSignal = pyqtSignal(int)
    SpeedSignal = pyqtSignal(float)
    RecoverBackup = pyqtSignal()
    FoundVersionSignal = pyqtSignal(str)
    WaveformReady = pyqtSignal(str, list)
    TransformSignal = pyqtSignal(str)
    ExportStarted = pyqtSignal(str, int, int)
    ExportFrame = pyqtSignal(str, int, int, int)
    ExportEnded = pyqtSignal(str)
    MaxSizeChanged = pyqtSignal(object)
    #InsertKeyframe = pyqtSignal(object)
    OpenProjectSignal = pyqtSignal(str)

    #ThumbnailUpdated = pyqtSignal(str)

    # Save window settings on close
    def closeEvent(self, event):

        # Close any tutorial dialogs
        #self.tutorial_manager.exit_manager()

        # Prompt user to save (if needed)
        if get_app().project.needs_save():
            log.info('Prompt user to save project')
            # Translate object
            _ = get_app()._tr

            # Handle exception
            ret = QMessageBox.question(
                self, _("Unsaved Changes"),
                _("Save changes to project before closing?"),
                QMessageBox.Cancel | QMessageBox.No | QMessageBox.Yes)
            if ret == QMessageBox.Yes:
                # Save project
                self.actionSave_trigger(event)
                event.accept()
            elif ret == QMessageBox.Cancel:
                # User canceled prompt - don't quit
                event.ignore()
                return

        # Save settings
        self.save_settings()

        # Track end of session
        track_metric_session(False)

        # Stop threads
        self.StopSignal.emit()

        # Process any queued events
        QCoreApplication.processEvents()

        # Stop preview thread (and wait for it to end)
        self.preview_thread.player.CloseAudioDevice()
        self.preview_thread.kill()
        self.preview_parent.background.exit()
        self.preview_parent.background.wait(5000)

        # Close & Stop libopenshot logger
        openshot.ZmqLogger.Instance().Close()
        get_app().logger_libopenshot.kill()

        # Destroy lock file
        self.destroy_lock_file()

    def __init__(self, mode=None):
        # Create main window base class
        QMainWindow.__init__(self)
        self.initialized = False

        # set window on app for reference during initialization of children
        get_app().window = self
        _ = get_app()._tr

        # Track metrics
        track_metric_session()  # start session

        # Load user settings for window
        s = settings.get_settings()

        # Load UI from designer
        ui_util.load_ui(self, self.ui_path)

        # Set all keyboard shortcuts from the settings file
        #self.InitKeyboardShortcuts()

        # Init UI
        ui_util.init_ui(self)

        # Setup toolbars that aren't on main window, set initial state of items, etc
        self.setup_toolbars()

        # Add window as watcher to receive undo/redo status updates
        get_app().updates.add_watcher(self)

        # Create the timeline sync object (used for previewing timeline)
        self.timeline_sync = TimelineSync(self)

        # Setup timeline
        self.timeline = TimelineWebView(self)
        self.frameWeb.layout().addWidget(self.timeline)

        # Configure the side docks to full-height
        self.setCorner(Qt.TopLeftCorner, Qt.LeftDockWidgetArea)
        self.setCorner(Qt.BottomLeftCorner, Qt.LeftDockWidgetArea)
        self.setCorner(Qt.TopRightCorner, Qt.RightDockWidgetArea)
        self.setCorner(Qt.BottomRightCorner, Qt.RightDockWidgetArea)

        # Process events before continuing
        # TODO: Figure out why this is needed for a backup recovery to correctly show up on the timeline
        get_app().processEvents()

        # Setup video preview QWidget
        self.videoPreview = VideoWidget()
        self.tabVideo.layout().insertWidget(0, self.videoPreview)

        # Load window state and geometry
        #self.load_settings()

        # Start the preview thread
        self.preview_parent = PreviewParent()
        self.preview_parent.Init(self, self.timeline_sync.timeline,
                                 self.videoPreview)
        self.preview_thread = self.preview_parent.worker

        # Set pause callback
        self.PauseSignal.connect(self.handlePausedVideo)

        # QTimer for Autosave
        self.auto_save_timer = QTimer(self)
        self.auto_save_timer.setInterval(
            s.get("autosave-interval") * 1000 * 60)
        self.auto_save_timer.timeout.connect(self.auto_save_project)
        if s.get("enable-auto-save"):
            self.auto_save_timer.start()

        # Set hardware decode
        if s.get("hardware_decode"):
            openshot.Settings.Instance().HARDWARE_DECODE = True
        else:
            openshot.Settings.Instance().HARDWARE_DECODE = False

        # Set hardware encode
        if s.get("hardware_encode"):
            openshot.Settings.Instance().HARDWARE_ENCODE = True
        else:
            openshot.Settings.Instance().HARDWARE_ENCODE = False

        # Set OMP thread enabled flag (for stability)
        if s.get("omp_threads_enabled"):
            openshot.Settings.Instance().WAIT_FOR_VIDEO_PROCESSING_TASK = False
        else:
            openshot.Settings.Instance().WAIT_FOR_VIDEO_PROCESSING_TASK = True

        # Set scaling mode to lower quality scaling (for faster previews)
        openshot.Settings.Instance().HIGH_QUALITY_SCALING = False

        # Create lock file
        self.create_lock_file()

        # Connect OpenProject Signal
        self.OpenProjectSignal.connect(self.open_project)

        # Show window
        self.show()

        # Save settings
        s.save()

        # Refresh frame
        QTimer.singleShot(100, self.refreshFrameSignal.emit)

        # Main window is initialized
        self.initialized = True

    def setup_toolbars(self):
        _ = get_app()._tr  # Get translation function

        # Start undo and redo actions disabled
        self.actionUndo.setEnabled(False)
        self.actionRedo.setEnabled(False)

        # Add Video Preview toolbar
        self.videoToolbar = QToolBar("Video Toolbar")

        # Add fixed spacer(s) (one for each "Other control" to keep playback controls centered)
        ospacer1 = QWidget(self)
        ospacer1.setMinimumSize(32, 1)  # actionSaveFrame
        self.videoToolbar.addWidget(ospacer1)
        #ospacer2 = QWidget(self)
        #ospacer2.setMinimumSize(32, 1) # ???
        #self.videoToolbar.addWidget(ospacer2)

        # Add left spacer
        spacer = QWidget(self)
        spacer.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
        self.videoToolbar.addWidget(spacer)

        # Playback controls (centered)
        self.videoToolbar.addAction(self.actionJumpStart)
        self.videoToolbar.addAction(self.actionRewind)
        self.videoToolbar.addAction(self.actionPlay)
        self.videoToolbar.addAction(self.actionFastForward)
        self.videoToolbar.addAction(self.actionJumpEnd)
        self.actionPlay.setCheckable(True)

        # Add right spacer
        spacer = QWidget(self)
        spacer.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
        self.videoToolbar.addWidget(spacer)

        # Other controls (right-aligned)
        #self.videoToolbar.addAction(self.actionSaveFrame)

        self.tabVideo.layout().addWidget(self.videoToolbar)

        # Add Timeline toolbar
        self.timelineToolbar = QToolBar("Timeline Toolbar", self)

        self.timelineToolbar.addAction(self.actionAddTrack)
        self.timelineToolbar.addSeparator()

        # rest of options
        self.timelineToolbar.addAction(self.actionSnappingTool)
        self.timelineToolbar.addAction(self.actionRazorTool)
        self.timelineToolbar.addSeparator()
        self.timelineToolbar.addAction(self.actionAddMarker)
        self.timelineToolbar.addAction(self.actionPreviousMarker)
        self.timelineToolbar.addAction(self.actionNextMarker)
        self.timelineToolbar.addSeparator()

        # Get project's initial zoom value
        initial_scale = get_app().project.get(["scale"]) or 15
        # Round non-exponential scale down to next lowest power of 2
        initial_zoom = secondsToZoom(initial_scale)

        # Setup Zoom slider
        self.sliderZoom = QSlider(Qt.Horizontal, self)
        self.sliderZoom.setPageStep(1)
        self.sliderZoom.setRange(0, 30)
        self.sliderZoom.setValue(initial_zoom)
        self.sliderZoom.setInvertedControls(True)
        self.sliderZoom.resize(100, 16)

        self.zoomScaleLabel = QLabel(
            _("{} seconds").format(zoomToSeconds(self.sliderZoom.value())))

        # add zoom widgets
        self.timelineToolbar.addAction(self.actionTimelineZoomIn)
        self.timelineToolbar.addWidget(self.sliderZoom)
        self.timelineToolbar.addAction(self.actionTimelineZoomOut)
        self.timelineToolbar.addWidget(self.zoomScaleLabel)

        # Add timeline toolbar to web frame
        self.frameWeb.addWidget(self.timelineToolbar)

    def actionImportFiles_trigger(self, event):
        app = get_app()
        _ = app._tr
        recommended_path = app.project.get(["import_path"])
        if not recommended_path or not os.path.exists(recommended_path):
            recommended_path = os.path.join(info.HOME_PATH)
        files = QFileDialog.getOpenFileNames(self, _("Import File..."),
                                             recommended_path)[0]
        for file_path in files:
            #self.filesTreeView.add_file(file_path)
            #self.filesTreeView.refresh_view()
            self.add_file(file_path)
            app.updates.update(["import_path"], os.path.dirname(file_path))
            log.info("Imported media file {}".format(file_path))

    # Save window settings on close
    def closeEvent(self, event):

        # Close any tutorial dialogs
        #self.tutorial_manager.exit_manager()

        # Prompt user to save (if needed)
        if get_app().project.needs_save():
            log.info('Prompt user to save project')
            # Translate object
            _ = get_app()._tr

            # Handle exception
            ret = QMessageBox.question(
                self, _("Unsaved Changes"),
                _("Save changes to project before closing?"),
                QMessageBox.Cancel | QMessageBox.No | QMessageBox.Yes)
            if ret == QMessageBox.Yes:
                # Save project
                self.actionSave_trigger(event)
                event.accept()
            elif ret == QMessageBox.Cancel:
                # User canceled prompt - don't quit
                event.ignore()
                return

        # Save settings
        self.save_settings()

        # Track end of session
        #track_metric_session(False)

        # Stop threads
        self.StopSignal.emit()

        # Process any queued events
        QCoreApplication.processEvents()

        # Stop preview thread (and wait for it to end)
        self.preview_thread.player.CloseAudioDevice()
        self.preview_thread.kill()
        self.preview_parent.background.exit()
        self.preview_parent.background.wait(5000)

        # Close & Stop libopenshot logger
        openshot.ZmqLogger.Instance().Close()
        get_app().logger_libopenshot.kill()

        # Destroy lock file
        self.destroy_lock_file()

    def handlePausedVideo(self):
        print("handlePausedVideo")
        """Handle the pause signal, by refreshing the properties dialog"""
        #self.propertyTableView.select_frame(self.preview_thread.player.Position())

    def auto_save_project(self):
        """Auto save the project"""
        log.info("auto_save_project")

        # Get current filepath (if any)
        file_path = get_app().project.current_filepath
        if get_app().project.needs_save():
            if file_path:
                # A Real project file exists
                # Append .osp if needed
                if ".osp" not in file_path:
                    file_path = "%s.osp" % file_path

                # Save project
                log.info("Auto save project file: %s" % file_path)
                self.save_project(file_path)

            else:
                # No saved project found
                recovery_path = os.path.join(info.BACKUP_PATH, "backup.osp")
                log.info("Creating backup of project file: %s" % recovery_path)
                get_app().project.save(recovery_path,
                                       move_temp_files=False,
                                       make_paths_relative=False)

                # Clear the file_path (which is set by saving the project)
                get_app().project.current_filepath = None
                get_app().project.has_unsaved_changes = True

    # Update window settings in setting store
    def save_settings(self):
        log.info("save settings")
        s = settings.get_settings()

        # Save window state and geometry (saves toolbar and dock locations)
        #s.set('window_state_v2', qt_types.bytes_to_str(self.saveState()))
        #s.set('window_geometry_v2', qt_types.bytes_to_str(self.saveGeometry()))

    def create_lock_file(self):
        """Create a lock file"""
        lock_path = os.path.join(info.USER_PATH, ".lock")
        lock_value = str(uuid4())

        # Check if it already exists
        if os.path.exists(lock_path):
            # Walk the libopenshot log (if found), and try and find last line before this launch
            log_path = os.path.join(info.USER_PATH, "libopenshot.log")
            last_log_line = ""
            last_stack_trace = ""
            found_stack = False
            log_start_counter = 0
            if os.path.exists(log_path):
                with open(log_path, "rb") as f:
                    # Read from bottom up
                    for raw_line in reversed(self.tail_file(f, 500)):
                        line = str(raw_line, 'utf-8')
                        # Detect stack trace
                        if "End of Stack Trace" in line:
                            found_stack = True
                            continue
                        elif "Unhandled Exception: Stack Trace" in line:
                            found_stack = False
                            continue
                        elif "libopenshot logging:" in line:
                            log_start_counter += 1
                            if log_start_counter > 1:
                                # Found the previous log start, too old now
                                break

                        if found_stack:
                            # Append line to beginning of stacktrace
                            last_stack_trace = line + last_stack_trace

                        # Ignore certain unuseful lines
                        if line.strip(
                        ) and "---" not in line and "libopenshot logging:" not in line and not last_log_line:
                            last_log_line = line

            # Split last stack trace (if any)
            if last_stack_trace:
                # Get top line of stack trace (for metrics)
                last_log_line = last_stack_trace.split("\n")[0].strip()

                # Send stacktrace for debugging (if send metrics is enabled)
                track_exception_stacktrace(last_stack_trace, "libopenshot")

            # Clear / normalize log line (so we can roll them up in the analytics)
            if last_log_line:
                # Format last log line based on OS (since each OS can be formatted differently)
                if platform.system() == "Darwin":
                    last_log_line = "mac-%s" % last_log_line[58:].strip()
                elif platform.system() == "Windows":
                    last_log_line = "windows-%s" % last_log_line
                elif platform.system() == "Linux":
                    last_log_line = "linux-%s" % last_log_line.replace(
                        "/usr/local/lib/", "")

                # Remove '()' from line, and split. Trying to grab the beginning of the log line.
                last_log_line = last_log_line.replace("()", "")
                log_parts = last_log_line.split("(")
                if len(log_parts) == 2:
                    last_log_line = "-%s" % log_parts[0].replace(
                        "logger_libopenshot:INFO ", "").strip()[:64]
                elif len(log_parts) >= 3:
                    last_log_line = "-%s (%s" % (log_parts[0].replace(
                        "logger_libopenshot:INFO ",
                        "").strip()[:64], log_parts[1])
            else:
                last_log_line = ""

            # Throw exception (with last libopenshot line... if found)
            log.error(
                "Unhandled crash detected... will attempt to recover backup project: %s"
                % info.BACKUP_PATH)
            #track_metric_error("unhandled-crash%s" % last_log_line, True)

            # Remove file
            self.destroy_lock_file()

        #else:
        # Normal startup, clear thumbnails
        #self.clear_all_thumbnails()

        # Write lock file (try a few times if failure)
        attempts = 5
        while attempts > 0:
            try:
                # Create lock file
                with open(lock_path, 'w') as f:
                    f.write(lock_value)
                break
            except Exception:
                attempts -= 1
                sleep(0.25)

    def destroy_lock_file(self):
        """Destroy the lock file"""
        lock_path = os.path.join(info.USER_PATH, ".lock")

        # Remove file (try a few times if failure)
        attempts = 5
        while attempts > 0:
            try:
                os.remove(lock_path)
                break
            except Exception:
                attempts -= 1
                sleep(0.25)

    def open_project(self, file_path, clear_thumbnails=True):
        """ Open a project from a file path, and refresh the screen """

        app = get_app()
        _ = app._tr  # Get translation function

        # Do we have unsaved changes?
        if get_app().project.needs_save():
            ret = QMessageBox.question(
                self, _("Unsaved Changes"),
                _("Save changes to project first?"),
                QMessageBox.Cancel | QMessageBox.No | QMessageBox.Yes)
            if ret == QMessageBox.Yes:
                # Save project
                self.actionSave.trigger()
            elif ret == QMessageBox.Cancel:
                # User canceled prompt
                return

        # Set cursor to waiting
        get_app().setOverrideCursor(QCursor(Qt.WaitCursor))

        try:
            if os.path.exists(file_path):
                # Clear any previous thumbnails
                #if clear_thumbnails:
                #    self.clear_all_thumbnails()

                # Load project file
                app.project.load(file_path)

                # Set Window title
                self.SetWindowTitle()

                # Reset undo/redo history
                app.updates.reset()
                app.updates.load_history(app.project)

                # Reset selections
                self.clearSelections()

                # Refresh file tree
                self.filesTreeView.refresh_view()

                # Load recent projects again
                self.load_recent_menu()

                log.info("Loaded project {}".format(file_path))
            else:
                # If statement is required, as if the user hits "Cancel"
                # on the "load file" dialog, it is interpreted as trying
                # to open a file with a blank name. This could use some
                # improvement.
                if file_path != "":
                    # Prepare to use status bar
                    self.statusBar = QStatusBar()
                    self.setStatusBar(self.statusBar)

                    log.info("File not found at {}".format(file_path))
                    self.statusBar.showMessage(
                        _("Project {} is missing (it may have been moved or deleted). It has been removed from the Recent Projects menu."
                          .format(file_path)), 5000)
                    self.remove_recent_project(file_path)
                    self.load_recent_menu()

        except Exception as ex:
            log.error("Couldn't open project {}".format(file_path))
            QMessageBox.warning(self, _("Error Opening Project"), str(ex))

        # Restore normal cursor
        get_app().restoreOverrideCursor()

    def tail_file(self, f, n, offset=None):
        """Read the end of a file (n number of lines)"""
        avg_line_length = 90
        to_read = n + (offset or 0)

        while True:
            try:
                # Seek to byte position
                f.seek(-(avg_line_length * to_read), 2)
            except IOError:
                # Byte position not found
                f.seek(0)
            pos = f.tell()
            lines = f.read().splitlines()
            if len(lines) >= to_read or pos == 0:
                # Return the lines
                return lines[-to_read:offset and -offset or None]
            avg_line_length *= 2

    '''
    #from files_treeview
    def add_file(self, filepath):
        path, filename = os.path.split(filepath)

        # Add file into project
        app = get_app()
        _ = get_app()._tr

        # Check for this path in our existing project data
        file = File.get(path=filepath)

        # If this file is already found, exit
        if file:
            return

        # Load filepath in libopenshot clip object (which will try multiple readers to open it)
        clip = openshot.Clip(filepath)

        # Get the JSON for the clip's internal reader
        try:
            reader = clip.Reader()
            file_data = json.loads(reader.Json())

            # Determine media type
            if file_data["has_video"]:
                file_data["media_type"] = "video"
            elif file_data["has_audio"] and not file_data["has_video"]:
                file_data["media_type"] = "audio"

            # Save new file to the project data
            file = File()
            file.data = file_data

            # Is this file an image sequence / animation?
            image_seq_details = self.get_image_sequence_details(filepath)
            if image_seq_details:
                # Update file with correct path
                folder_path = image_seq_details["folder_path"]
                file_name = image_seq_details["file_path"]
                base_name = image_seq_details["base_name"]
                fixlen = image_seq_details["fixlen"]
                digits = image_seq_details["digits"]
                extension = image_seq_details["extension"]

                if not fixlen:
                    zero_pattern = "%d"
                else:
                    zero_pattern = "%%0%sd" % digits

                # Generate the regex pattern for this image sequence
                pattern = "%s%s.%s" % (base_name, zero_pattern, extension)

                # Split folder name
                (parentPath, folderName) = os.path.split(folder_path)
                if not base_name:
                    # Give alternate name
                    file.data["name"] = "%s (%s)" % (folderName, pattern)

                # Load image sequence (to determine duration and video_length)
                image_seq = openshot.Clip(os.path.join(folder_path, pattern))

                # Update file details
                file.data["path"] = os.path.join(folder_path, pattern)
                file.data["media_type"] = "video"
                file.data["duration"] = image_seq.Reader().info.duration
                file.data["video_length"] = image_seq.Reader().info.video_length

            # Save file
            file.save()
            return True

        except:
            # Handle exception
            msg = QMessageBox()
            msg.setText(_("{} is not a valid video, audio, or image file.".format(filename)))
            msg.exec_()
            return False
    '''

    #from files_treeview
    def add_file(self, filepath):
        path, filename = os.path.split(filepath)

        # Add file into project
        app = get_app()
        _ = get_app()._tr

        # Check for this path in our existing project data
        file = File.get(path=filepath)

        # If this file is already found, exit
        if file:
            return

        # Load filepath in libopenshot clip object (which will try multiple readers to open it)
        clip = openshot.Clip(filepath)

        # Get the JSON for the clip's internal reader
        reader = clip.Reader()
        file_data = json.loads(reader.Json())

        print("file_data:", file_data)

        # Determine media type
        if file_data["has_video"]:
            file_data["media_type"] = "video"
        elif file_data["has_audio"] and not file_data["has_video"]:
            file_data["media_type"] = "audio"

        # Save new file to the project data
        file = File()
        file.data = file_data

        # Save file
        file.save()

        # open in timeline added by yanght======
        self.timeline.addNewClip(file)

        return True

    # Update undo and redo buttons enabled/disabled to available changes
    def updateStatusChanged(self, undo_status, redo_status):
        log.info('updateStatusChanged')
        self.actionUndo.setEnabled(undo_status)
        self.actionRedo.setEnabled(redo_status)
        self.SetWindowTitle()

    def SetWindowTitle(self, profile=None):
        """ Set the window title based on a variety of factors """

        # Get translation function
        _ = get_app()._tr

        if not profile:
            profile = get_app().project.get(["profile"])

        # Determine if the project needs saving (has any unsaved changes)
        save_indicator = ""
        if get_app().project.needs_save():
            save_indicator = "*"
            self.actionSave.setEnabled(True)
        else:
            self.actionSave.setEnabled(False)

        # Is this a saved project?
        if not get_app().project.current_filepath:
            # Not saved yet
            self.setWindowTitle("%s %s [%s] - %s" %
                                (save_indicator, _("Untitled Project"),
                                 profile, "YYSPortEditor"))
        else:
            # Yes, project is saved
            # Get just the filename
            parent_path, filename = os.path.split(
                get_app().project.current_filepath)
            filename, ext = os.path.splitext(filename)
            filename = filename.replace("_", " ").replace("-",
                                                          " ").capitalize()
            self.setWindowTitle(
                "%s %s [%s] - %s" %
                (save_indicator, filename, profile, "YYSPortEditor"))

    # Add to the selected items
    def addSelection(self, item_id, item_type, clear_existing=False):
        log.info(
            'main::addSelection: item_id: %s, item_type: %s, clear_existing: %s'
            % (item_id, item_type, clear_existing))
        '''
        # Clear existing selection (if needed)
        if clear_existing:
            if item_type == "clip":
                self.selected_clips.clear()

        if item_id:
            # If item_id is not blank, store it
            if item_type == "clip" and item_id not in self.selected_clips:
                self.selected_clips.append(item_id)
        '''

    def movePlayhead(self, position_frames):
        """Update playhead position"""
        # Notify preview thread
        self.timeline.movePlayhead(position_frames)

    def actionPlay_trigger(self, event, force=None):

        # Determine max frame (based on clips)
        timeline_length = 0.0
        fps = get_app().window.timeline_sync.timeline.info.fps.ToFloat()
        clips = get_app().window.timeline_sync.timeline.Clips()
        for clip in clips:
            clip_last_frame = clip.Position() + clip.Duration()
            if clip_last_frame > timeline_length:
                # Set max length of timeline
                timeline_length = clip_last_frame

        # Convert to int and round
        timeline_length_int = round(timeline_length * fps) + 1

        if force == "pause":
            self.actionPlay.setChecked(False)
        elif force == "play":
            self.actionPlay.setChecked(True)

        if self.actionPlay.isChecked():
            ui_util.setup_icon(self, self.actionPlay, "actionPlay",
                               "media-playback-pause")
            self.PlaySignal.emit(timeline_length_int)

        else:
            ui_util.setup_icon(self, self.actionPlay,
                               "actionPlay")  # to default
            self.PauseSignal.emit()

    def actionAddTrack_trigger(self, event):
        log.info("actionAddTrack_trigger")

        self.addTrack("test-action")

    def addTrack(self, name):
        log.info("add track %s", name)
        # Get # of tracks
        all_tracks = get_app().project.get(["layers"])
        track_number = 1000000
        if len(list(reversed(sorted(all_tracks,
                                    key=itemgetter('number'))))) > 0:
            track_number = list(
                reversed(sorted(
                    all_tracks,
                    key=itemgetter('number'))))[0].get("number") + 1000000

        # Create new track above existing layer(s)
        track = Track()
        track.data = {
            "number": track_number,
            id: str(len(all_tracks)),
            "y": 0,
            "label": "",
            "lock": False,
            "name": name
        }
        track.save()
        return track

    def cut(self, key, current_frame, color):
        log.info("add cut %s,%s, %s", key, current_frame, color)
        '''
        layerid = -1;
        layers = get_app().project.get(["layers"])
        if (len(layers) == 0):  # add first track if no
            layerid = self.addTrack("track_test_auto").data["id"]
        else:
            layerid = layers[0].data["id"]
        '''

        # Get # of tracks
        find = False
        cutList = Cut.filter(shortCut=key)
        for cut in cutList:
            if cut and cut.data["end"] == -1:
                cut.data["end"] = current_frame
                cut.save()
                find = True

        # Create new track above existing layer(s)
        if not find:
            id = len(get_app().project.get(["cuts"]))
            cut = Cut()
            cut.data = {
                "id": str(id),
                "layer": "0",
                "color": color,
                "start": current_frame,
                "duration": 0.0,
                "shortCut": key,
                "end": -1
            }
            cut.save()

    def keyPressEvent(self, event):
        key_value = event.key()
        print(key_value)
        modifiers = int(event.modifiers())
        if (key_value > 0 and key_value != Qt.Key_Shift
                and key_value != Qt.Key_Alt and key_value != Qt.Key_Control
                and key_value != Qt.Key_Meta):
            # A valid keysequence was detected
            key = QKeySequence(modifiers + key_value)
        else:
            # No valid keysequence detected
            return

        # Debug
        log.info("keyPressEvent: %s" % (key.toString()))

        color = self.getColorByName("actionCut+" + key.toString())
        if color:
            self.cut(key.toString(), self.preview_thread.current_frame, color)
        elif key.matches(self.getShortcutByName(
                "actionAddTrack")) == QKeySequence.ExactMatch:
            self.addTrack("track_test")

    def getShortcutByName(self, setting_name):
        """ Get a key sequence back from the setting name """
        s = settings.get_settings()
        shortcut = QKeySequence(s.get(setting_name))
        return shortcut

    def getColorByName(self, setting_name):
        """ Get a key sequence back from the setting name """
        s = settings.get_settings()
        return s.get(setting_name)

    def previewFrame(self, position_frames):
        """Preview a specific frame"""
        # Notify preview thread
        self.previewFrameSignal.emit(position_frames)
    def __init__(self, mode=None):
        # Create main window base class
        QMainWindow.__init__(self)
        self.initialized = False

        # set window on app for reference during initialization of children
        get_app().window = self
        _ = get_app()._tr

        # Track metrics
        track_metric_session()  # start session

        # Load user settings for window
        s = settings.get_settings()

        # Load UI from designer
        ui_util.load_ui(self, self.ui_path)

        # Set all keyboard shortcuts from the settings file
        #self.InitKeyboardShortcuts()

        # Init UI
        ui_util.init_ui(self)

        # Setup toolbars that aren't on main window, set initial state of items, etc
        self.setup_toolbars()

        # Add window as watcher to receive undo/redo status updates
        get_app().updates.add_watcher(self)

        # Create the timeline sync object (used for previewing timeline)
        self.timeline_sync = TimelineSync(self)

        # Setup timeline
        self.timeline = TimelineWebView(self)
        self.frameWeb.layout().addWidget(self.timeline)

        # Configure the side docks to full-height
        self.setCorner(Qt.TopLeftCorner, Qt.LeftDockWidgetArea)
        self.setCorner(Qt.BottomLeftCorner, Qt.LeftDockWidgetArea)
        self.setCorner(Qt.TopRightCorner, Qt.RightDockWidgetArea)
        self.setCorner(Qt.BottomRightCorner, Qt.RightDockWidgetArea)

        # Process events before continuing
        # TODO: Figure out why this is needed for a backup recovery to correctly show up on the timeline
        get_app().processEvents()

        # Setup video preview QWidget
        self.videoPreview = VideoWidget()
        self.tabVideo.layout().insertWidget(0, self.videoPreview)

        # Load window state and geometry
        #self.load_settings()

        # Start the preview thread
        self.preview_parent = PreviewParent()
        self.preview_parent.Init(self, self.timeline_sync.timeline,
                                 self.videoPreview)
        self.preview_thread = self.preview_parent.worker

        # Set pause callback
        self.PauseSignal.connect(self.handlePausedVideo)

        # QTimer for Autosave
        self.auto_save_timer = QTimer(self)
        self.auto_save_timer.setInterval(
            s.get("autosave-interval") * 1000 * 60)
        self.auto_save_timer.timeout.connect(self.auto_save_project)
        if s.get("enable-auto-save"):
            self.auto_save_timer.start()

        # Set hardware decode
        if s.get("hardware_decode"):
            openshot.Settings.Instance().HARDWARE_DECODE = True
        else:
            openshot.Settings.Instance().HARDWARE_DECODE = False

        # Set hardware encode
        if s.get("hardware_encode"):
            openshot.Settings.Instance().HARDWARE_ENCODE = True
        else:
            openshot.Settings.Instance().HARDWARE_ENCODE = False

        # Set OMP thread enabled flag (for stability)
        if s.get("omp_threads_enabled"):
            openshot.Settings.Instance().WAIT_FOR_VIDEO_PROCESSING_TASK = False
        else:
            openshot.Settings.Instance().WAIT_FOR_VIDEO_PROCESSING_TASK = True

        # Set scaling mode to lower quality scaling (for faster previews)
        openshot.Settings.Instance().HIGH_QUALITY_SCALING = False

        # Create lock file
        self.create_lock_file()

        # Connect OpenProject Signal
        self.OpenProjectSignal.connect(self.open_project)

        # Show window
        self.show()

        # Save settings
        s.save()

        # Refresh frame
        QTimer.singleShot(100, self.refreshFrameSignal.emit)

        # Main window is initialized
        self.initialized = True
class YYPlayerBase():
    previewFrameSignal = pyqtSignal(int)
    refreshFrameSignal = pyqtSignal()
    LoadFileSignal = pyqtSignal(str)
    PlaySignal = pyqtSignal(int)
    PauseSignal = pyqtSignal()
    StopSignal = pyqtSignal()
    SeekSignal = pyqtSignal(int)
    SpeedSignal = pyqtSignal(float)

    movePlayheadSignal = pyqtSignal(float)
    PlayModeChangedSignal = pyqtSignal(int)
    MaxSizeChanged = pyqtSignal(object)

    def __init__(self, timeline):
        self.layout = QVBoxLayout()
        self.setLayout(self.layout)

        # Setup video preview QWidget
        self.videoPreview = VideoWidget(self)
        self.layout.addWidget(self.videoPreview)

        # Set max size of video preview (for speed) #todo ============
        #viewport_rect = self.videoPreview.centeredViewport(self.videoPreview.width(), self.videoPreview.height())
        #timeline.SetMaxSize(viewport_rect.width(), viewport_rect.height())

        self.initialized = False

        # Start the preview thread
        self.preview_parent = PreviewParent()
        self.preview_parent.Init(self, timeline, self.videoPreview)
        self.preview_thread = self.preview_parent.worker

        # Set pause callback
        self.PauseSignal.connect(self.handlePausedVideo)

        self.hover = YYPlayerHover(self, self)

    # Save window settings on close
    def closeEvent(self, event):
        # Stop threads
        self.StopSignal.emit()

        # Process any queued events
        QCoreApplication.processEvents()

        # Stop preview thread (and wait for it to end)
        self.preview_thread.player.CloseAudioDevice()
        self.preview_thread.kill()
        self.preview_parent.background.exit()
        self.preview_parent.background.wait(5000)

        del self.preview_thread.player
        self.preview_thread.player = None

        del self.preview_thread
        self.preview_thread = None

        del self.preview_parent.background
        self.preview_parent.background = None

        del self.preview_parent
        self.preview_parent = None

    def btnPlay_clicked(self, force=None):
        # Determine max frame (based on clips)
        timeline_length = 0.0
        fps = self.timeline.info.fps.ToFloat()
        clips = self.timeline.Clips()
        for clip in clips:
            clip_last_frame = clip.Position() + clip.Duration()
            if clip_last_frame > timeline_length:
                # Set max length of timeline
                timeline_length = clip_last_frame

        # Convert to int and round
        timeline_length_int = round(timeline_length * fps) + 1

        if force == "pause":
            self.hover.btnPlay.setChecked(False)
        elif force == "play":
            self.hover.btnPlay.setChecked(True)

        if self.hover.btnPlay.isChecked():
            # ui_util.setup_icon(self, self.actionPlay, "actionPlay", "media-playback-pause")
            self.PlaySignal.emit(timeline_length_int)

        else:
            # ui_util.setup_icon(self, self.actionPlay, "actionPlay")  # to default
            self.PauseSignal.emit()

    def resizeEvent(self, QResizeEvent):
        super().resizeEvent(QResizeEvent)
        self.hover.setGeometry((self.width() - self.hover.width()) / 2, 20,
                               self.hover.width(), self.hover.height())

    def onModeChanged(self, current_mode):
        log.info('onModeChanged %s', current_mode)
        self.PlayModeChangedSignal.emit(current_mode)

    def Mode(self):
        return self.preview_thread.player.Mode()

    def handlePausedVideo(self):
        log.info("base handlePausedVideo")

    def onPlayFinished(self):
        log.info("base onPlayFinished")

    def movePlayhead(self, position_frames):
        log.info("movePlayhead %s", position_frames)
        """Update playhead position"""