def start_recording(self, path: str,
                        filename: str) -> InternalOperationResult:
        if self.is_active():
            self.__logger.error(
                'Can\'t start FFMPEG for file %s: screen is acctually recording ',
                path + '/' + filename)
            return InternalOperationResult(ExecutionStatus.FATAL_ERROR,
                                           'Tablet is actually recording')

        try:
            self.__tablet_client.check_and_create_folder(path)
        except Exception as e:
            self.__logger.error('Error while creating remmote dir: %s', e)
            return InternalOperationResult(ExecutionStatus.FATAL_ERROR)

        command = settings.FFMPEG_TABLET_CMD + path + '/' + filename + ' 2< /dev/null &'

        try:
            self.__tablet_client.execute_remote(command)
        except Exception as e:
            self.__logger.error(
                'Screen recording start failed: %s; FFMPEG command: %s', e,
                command)
            return InternalOperationResult(ExecutionStatus.FATAL_ERROR)

        self.last_processed_file = filename
        self.last_processed_path = path
        self.__logger.info(
            'Successfully start screen recording (FFMPEG command: %s', command)
        return InternalOperationResult(ExecutionStatus.SUCCESS)
 def exec_and_get_output(self, command, stderr=None) -> (InternalOperationResult, str):
     try:
         return InternalOperationResult(ExecutionStatus.SUCCESS), \
                subprocess.check_output(command, stderr=stderr)
     except Exception as e:
         logger.error('Can\'t get execution result of %s: %s', command, str(e))
         return InternalOperationResult(ExecutionStatus.FATAL_ERROR), None
    def stop_recording(self) -> InternalOperationResult:
        if not self.is_active():
            self.__logger.warning(
                'Tablet screencast isn\'t active: can\'t stop non existing FFMPEG process'
            )
            return InternalOperationResult(ExecutionStatus.SUCCESS)

        command = 'pkill -f ffmpeg'

        for _ in range(0, ATTEMPTS_TO_STOP):
            try:
                #  read_output=True is using for synchronized execution
                self.__tablet_client.execute_remote(command,
                                                    allowable_code=1,
                                                    read_output=True)
            except Exception as e:
                self.__logger.error('Problems while stop screen recording: %s',
                                    e)
                return InternalOperationResult(ExecutionStatus.FATAL_ERROR)

            if not self.is_active():
                return InternalOperationResult(ExecutionStatus.SUCCESS)
            else:
                time.sleep(ATTEMPTS_PAUSE)

        self.__logger.error(
            'Can\'t stop screen recording process for %s seconds.',
            ATTEMPTS_PAUSE * ATTEMPTS_TO_STOP)
        return InternalOperationResult(ExecutionStatus.FIXABLE_ERROR)
    def kill_process(pid, including_parent=True) -> InternalOperationResult:
        if not psutil.pid_exists(pid):
            InternalOperationResult(ExecutionStatus.SUCCESS)

        try:
            parent = psutil.Process(pid)
        except psutil.NoSuchProcess:
            return InternalOperationResult(ExecutionStatus.SUCCESS)
        except Exception as e:
            logger.error(str(e))
            return InternalOperationResult(ExecutionStatus.FATAL_ERROR, str(e))

        for child in parent.children(recursive=True):
            try:
                child.kill()
            except Exception as e:
                logger.error('Can\'t kill process with pid %s (subprocess of %s) : %s', child.pid, pid, str(e))
        if including_parent:
            try:
                parent.kill()
            except Exception as e:
                logger.error('Can\'t kill process with pid %s: %s', parent.pid, str(e))
                return InternalOperationResult(ExecutionStatus.FATAL_ERROR, str(e))

        return InternalOperationResult(ExecutionStatus.SUCCESS)
 def delete_recursively(self, path: str) -> InternalOperationResult:
     try:
         shutil.rmtree(path, ignore_errors=True)
         return InternalOperationResult(ExecutionStatus.SUCCESS)
     except Exception as e:
         logger.error('Can\'t delete recursively %s: %s', path, e)
         return InternalOperationResult(ExecutionStatus.FATAL_ERROR, e)
Exemple #6
0
def rename_element_on_disk(from_obj: 'Step', to_obj: 'Step') -> InternalOperationResult:
    if os.path.exists(to_obj.os_path):
        message = 'File with name \'{0}\' already exists'.format(to_obj.name)
        logger.error(message)
        return InternalOperationResult(ExecutionStatus.FIXABLE_ERROR, message)

    if not os.path.exists(from_obj.os_path):
        #  it may means that step created just now - it's OK
        return InternalOperationResult(ExecutionStatus.SUCCESS)

    if not os.path.isdir(from_obj.os_path):
        message = 'Cannot rename non-existent file: \'{0}\' doesn\'t exist'.format(from_obj.os_path)
        logger.error(message)
        return InternalOperationResult(ExecutionStatus.FATAL_ERROR, message)

    try:
        os.rename(from_obj.os_path, to_obj.os_path)
    except Exception as e:
        message = 'Cannot rename element on disk: {0}'.format(str(e))
        logger.exception('Cannot rename element on disk')
        return InternalOperationResult(ExecutionStatus.FATAL_ERROR, message)

    try:
        os.rename(from_obj.os_automontage_path, to_obj.os_automontage_path)
    except Exception as e:
        logger.exception('Cannot rename element on disk: %s', e)

    return InternalOperationResult(ExecutionStatus.SUCCESS)
 def send_quit_signal(self, process) -> InternalOperationResult:
     try:
         process.stdin.write(bytes('q', 'UTF-8'))
         process.stdin.close()
         return InternalOperationResult(ExecutionStatus.SUCCESS)
     except Exception as e:
         logger.error('Can\'t send quit signal to process %s: %s', process.pid, e)
         return InternalOperationResult(ExecutionStatus.FATAL_ERROR)
    def get_storage_capacity(self, path: str) -> (InternalOperationResult, int):
        """Returns total disk capacity in byted."""

        try:
            capacity = psutil.disk_usage(path=path).total
            return InternalOperationResult(ExecutionStatus.SUCCESS), capacity
        except Exception as e:
            logger.warning('Can\'t get information about total disk capacity: %s', str(e))
            return InternalOperationResult(ExecutionStatus.FATAL_ERROR, str(e)), None
    def get_free_disk_space(self, path: str) -> (InternalOperationResult, int):
        """Returns free disk capacity in bytes."""

        try:
            capacity = psutil.disk_usage(path=path).free
            return InternalOperationResult(ExecutionStatus.SUCCESS), capacity
        except Exception as e:
            logger.warning('Can\'t get information about disk free space: %s', str(e))
            return InternalOperationResult(ExecutionStatus.FATAL_ERROR, str(e)), None
 def create_recursively(self, path: str) -> InternalOperationResult:
     try:
         if os.path.exists(path):
             return InternalOperationResult(ExecutionStatus.SUCCESS)
         os.makedirs(path, exist_ok=True, mode=777)
         return InternalOperationResult(ExecutionStatus.SUCCESS)
     except Exception as e:
         logger.error('Can\'t create dirs recursively: %s', e)
         return InternalOperationResult(ExecutionStatus.FATAL_ERROR)
Exemple #11
0
def delete_tablet_lesson_files(lesson) -> InternalOperationResult:
    try:
        client = TabletClient()
        client.delete_folder_recursively(lesson.tablet_path)
        client.close()
        return InternalOperationResult(ExecutionStatus.SUCCESS)
    except Exception as e:
        logger.warning('Failed to remove lesson %s from tablet (id: %s): %s',
                       lesson.name, lesson.id, e)
        return InternalOperationResult(ExecutionStatus.FATAL_ERROR)
Exemple #12
0
 def delete_file(self, path: str) -> InternalOperationResult:
     if not self.__is_alive():
         self.__connect()
     try:
         if not self.__is_exists(path)[0]:
             return InternalOperationResult(ExecutionStatus.SUCCESS)
         self.__sftp.remove(path)
         return InternalOperationResult(ExecutionStatus.SUCCESS)
     except Exception as e:
         self.__logger.error('Can\'t delete file %s on tablet: %s', path, e)
         return InternalOperationResult(ExecutionStatus.FATAL_ERROR)
 def execute_command_sync(command, allowable_code=0) -> InternalOperationResult:
     """Blocking execution."""
     try:
         # raise exception when returncode != 0
         subprocess.check_call(command, shell=True)
         return InternalOperationResult(ExecutionStatus.SUCCESS)
     except subprocess.CalledProcessError as e:
         if e.returncode == allowable_code:
             return InternalOperationResult(ExecutionStatus.SUCCESS)
         logger.error('Cannot exec command: %s', str(e))
         return InternalOperationResult(ExecutionStatus.FATAL_ERROR, 'Cannot exec command: {0}'.format(str(e)))
    def remove_file(file: str) -> InternalOperationResult:
        if not os.path.isfile(file):
            logger.warning('Can\'t delete non-existing file %s ', file)
            return InternalOperationResult(ExecutionStatus.SUCCESS)

        try:
            os.remove(file)
        except Exception as e:
            logger.warning('Can\'t delete %s :', e)
            return InternalOperationResult(ExecutionStatus.FATAL_ERROR)

        return InternalOperationResult(ExecutionStatus.SUCCESS)
Exemple #15
0
    def download_file(self, remote_dir, filename,
                      local_dir) -> InternalOperationResult:
        try:
            if not self.__is_alive():
                self.__connect()

            os.path.exists(local_dir) or os.makedirs(local_dir)
            self.__sftp.get(remote_dir + '/' + filename,
                            os.path.join(local_dir, filename))
            return InternalOperationResult(ExecutionStatus.SUCCESS)
        except Exception as e:
            self.__logger.error('Can\'t download remote file %s: %s',
                                remote_dir + '/' + filename, e)
            return InternalOperationResult(ExecutionStatus.FATAL_ERROR, e)
 def execute_command(self, command: str, stdout=None, stdin=None) -> (InternalOperationResult, subprocess.Popen):
     try:
         proc = subprocess.Popen(command, shell=True, stdout=stdout, stdin=stdin)
         # process still running when returncode is None
         if proc.returncode is not None and proc.returncode != 0:
             _, error = proc.communicate()
             message = 'Cannot exec command: (return code: {0}): {1}'.format(proc.returncode, error)
             logger.error(message)
             return InternalOperationResult(ExecutionStatus.FATAL_ERROR, message), proc
         else:
             return InternalOperationResult(ExecutionStatus.SUCCESS), proc
     except Exception as e:
         message = 'Cannot exec command: {0}'.format(str(e))
         logger.exception('Cannot exec command: ')
         return InternalOperationResult(ExecutionStatus.FATAL_ERROR, message), None
Exemple #17
0
    def sync(self, screen_path, camera_path) -> InternalOperationResult:
        screen_path = os.path.splitext(screen_path)[0] + '.mp4'
        camera_path = os.path.splitext(camera_path)[0] + '.mp4'
        if not os.path.isfile(screen_path) or not os.path.isfile(camera_path):
            self.__logger.warning('Invalid paths to videos: (%s; %s)',
                                  screen_path, camera_path)
            return InternalOperationResult(ExecutionStatus.FATAL_ERROR)

        try:
            duration_1 = self.__get_silence_duration(
                screen_path, self.__tablet_noise_tolerance,
                self.__min_silence_duration)
            duration_2 = self.__get_silence_duration(
                camera_path, self.__camera_noise_tolerance,
                self.__min_silence_duration)
        except Exception:
            self.__logger.warning('Can\'t get silence duration of %s, %s.',
                                  screen_path, camera_path)
            return InternalOperationResult(ExecutionStatus.FATAL_ERROR)

        longer = ''
        try:
            silence_diff = self.__get_valid_silence_diff(
                duration_1, duration_2)

            if silence_diff == 0:
                self.__logger.info(
                    'Video synchronizing: no need to be synchronized, silence difference < %ssec.',
                    self.__min_diff)
                return InternalOperationResult(ExecutionStatus.SUCCESS)

            if silence_diff > 0:
                longer = screen_path
                self.__add_empty_frames(camera_path, silence_diff)
            elif silence_diff < 0:
                longer = camera_path
                self.__add_empty_frames(screen_path, abs(silence_diff))
        except Exception as e:
            self.__logger.warning('Invalide difference: %s', e)
            return InternalOperationResult(ExecutionStatus.FATAL_ERROR)

        self.__logger.info(
            'Videos successfully synchronized (difference: %s sec.; longer silence in  %s; '
            'silence duration of %s - %s sec.; silence duration of %s - %s sec.)',
            '%.3f' % silence_diff, longer, screen_path, duration_1,
            camera_path, duration_2)

        return InternalOperationResult(ExecutionStatus.SUCCESS)
Exemple #18
0
    def raw_cut(self, substep_id: int) -> InternalOperationResult:
        substep = SubStep.objects.get(pk=substep_id)

        if substep.automontage_exist:
            return InternalOperationResult(ExecutionStatus.SUCCESS)

        screen_full_path = substep.os_screencast_path
        prof_full_path = substep.os_path

        output_dir = substep.os_automontage_path
        status = self._fs_client.create_recursively(output_dir)

        if status.status is not ExecutionStatus.SUCCESS:
            return status

        full_output = os.path.join(
            output_dir, substep.name + RAW_CUT_LABEL + MP4_EXTENSION)

        substep.is_locked = True
        substep.save()
        internal_status = self._internal_raw_cut(screen_full_path,
                                                 prof_full_path, full_output)
        substep = SubStep.objects.get(pk=substep_id)
        substep.is_locked = False
        substep.save()

        return internal_status
Exemple #19
0
def start_recording(substep) -> InternalOperationResult:
    create_status = FileSystemClient().create_recursively(substep.dir_path)

    if create_status.status is not ExecutionStatus.SUCCESS:
        logger.error('Can\'t create folder for new substep: %s',
                     create_status.message)
        return create_status

    tablet_exec_info = TabletScreenRecorder().start_recording(
        substep.os_tablet_dir, substep.screencast_name)

    if tablet_exec_info.status is not ExecutionStatus.SUCCESS:
        return tablet_exec_info

    ffmpeg_status = ServerCameraRecorder().start_recording(
        substep.dir_path, substep.camera_recording_name)

    if ffmpeg_status.status is not ExecutionStatus.SUCCESS:
        TabletScreenRecorder().stop_recording()
        return ffmpeg_status

    db_camera = CameraStatus.objects.get(id='1')
    if not db_camera.status:
        db_camera.status = True
        db_camera.start_time = int(round(time.time() * 1000))
        db_camera.save()

    return InternalOperationResult(ExecutionStatus.SUCCESS)
Exemple #20
0
    def start_recording(self, path: str,
                        filename: str) -> InternalOperationResult:
        if self.is_active():
            self.__logger.error(
                'Can\'t start FFMPEG for file %s: camera is acctually recording (process with PID %s)',
                os.path.join(path, filename), self.__process.pid)
            return InternalOperationResult(ExecutionStatus.FATAL_ERROR,
                                           'Camera is actually recording')

        local_command = self.__command + os.path.join(path, filename)
        result, self.__process = self.__fs_client.execute_command(
            local_command, stdin=subprocess.PIPE)

        if result.status is ExecutionStatus.SUCCESS:
            self.__logger.info(
                'Successfully start camera recording (FFMPEG PID: %s; FFMPEG command: %s)',
                self.__process.pid, local_command)
            self.last_processed_file = filename
            self.last_processed_path = path
        else:
            self.__logger.error(
                'Camera recording start failed: %s; FFMPEG command: %s',
                result.message, local_command)

        return result
Exemple #21
0
    def stop_recording(self) -> InternalOperationResult:
        if not self.is_active():
            if self.__process is None:
                pid = None
            else:
                pid = self.__process.pid
                self.__process = None
            self.__logger.warning(
                'Camera isn\'t active: can\'t stop non existing FFMPEG process '
                '(try to stop process with PID %s)', pid)
            return InternalOperationResult(
                ExecutionStatus.SUCCESS,
                'Camera isn\'t active: can\'t stop non existing FFMPEG process'
            )

        result = self.__fs_client.send_quit_signal(self.__process)

        # try to kill ffmpeg process (sending of quit signal may have no effect)
        TaskManager().run_with_delay(FileSystemClient.kill_process,
                                     [self.__process.pid],
                                     delay=KILL_DELAY)

        if result.status is ExecutionStatus.SUCCESS:
            self.__logger.info(
                'Successfully stop camera recording (FFMPEG PID: %s)',
                self.__process.pid)
            self.__process = None
            self._apply_pipe(self.last_processed_path,
                             self.last_processed_file)
            return result
        else:
            self.__logger.error(
                'Problems while stop camera recording (FFMPEG PID: %s) : %s',
                self.__process.pid, result.message)
            return result
Exemple #22
0
    def delete_folder(self, path: str) -> (InternalOperationResult, int):
        if not self.__is_alive():
            self.__connect()

        status, size = self.__is_exists(path)
        if status is False:
            return InternalOperationResult(ExecutionStatus.SUCCESS), 0

        try:
            files = self.__sftp.listdir(path=path)
            size = 0

            for f in files:
                size += self.get_file_size(path + '/' + f)
                self.__sftp.remove(path + '/' + f)

            self.__sftp.rmdir(path)
            return InternalOperationResult(ExecutionStatus.SUCCESS), size
        except Exception as e:
            self.__logger.error('Can\'t delete folder %s: %s', path, e)
            return InternalOperationResult(ExecutionStatus.FATAL_ERROR), 0
Exemple #23
0
    def download_dir(self, remote_dir, local_dir) -> InternalOperationResult:
        try:
            if not self.__is_alive():
                self.__connect()

            os.path.exists(local_dir) or os.makedirs(local_dir)
            dir_items = self.__sftp.listdir_attr(remote_dir)

            for item in dir_items:
                remote_path = remote_dir + '/' + item.filename
                local_path = os.path.join(local_dir, item.filename)
                if S_ISDIR(item.st_mode):
                    status = self.download_dir(remote_path, local_path)
                    if status.status is not ExecutionStatus.SUCCESS:
                        return status
                else:
                    self.__sftp.get(remote_path, local_path)

            return InternalOperationResult(ExecutionStatus.SUCCESS)
        except Exception as e:
            self.__logger.error('Can\'t download remote directory %s: %s',
                                remote_dir, e)
            return InternalOperationResult(ExecutionStatus.FATAL_ERROR, e)
Exemple #24
0
def delete_substep_on_disk(substep) -> InternalOperationResult:
    client = FileSystemClient()

    cam_removing_info = client.remove_file(substep.os_path)
    if cam_removing_info.status is not ExecutionStatus.SUCCESS:
        return cam_removing_info

    screencast_removing_info = client.remove_file(substep.os_screencast_path)
    if screencast_removing_info.status is not ExecutionStatus.SUCCESS:
        return screencast_removing_info

    raw_cut_removing_info = client.remove_file(substep.os_automontage_file)
    if raw_cut_removing_info.status is not ExecutionStatus.SUCCESS:
        return raw_cut_removing_info

    return InternalOperationResult(ExecutionStatus.SUCCESS)
Exemple #25
0
    def _internal_raw_cut(self, video_path1: str, video_path2: str,
                          output_path: str) -> InternalOperationResult:
        status_1 = os.path.isfile(video_path1)
        status_2 = os.path.isfile(video_path2)

        if not status_1 or not status_2:
            self.__logger.warning('Can\'t process invalid videos: %s, %s',
                                  video_path1, video_path2)
            return InternalOperationResult(ExecutionStatus.FATAL_ERROR)

        command = settings.FFMPEG_PATH + ' ' + \
                  settings.RAW_CUT_TEMPLATE.format(video_path1, video_path2, output_path)

        status = self._fs_client.execute_command_sync(command)

        if status.status is not ExecutionStatus.SUCCESS:
            return status
Exemple #26
0
    def delete_folder_recursively(self, path: str):
        if not self.__is_alive():
            self.__connect()

        status, _ = self.__is_exists(path)
        if status is False:
            return InternalOperationResult(ExecutionStatus.SUCCESS)

        files = self.__sftp.listdir(path=path)

        for f in files:
            filepath = path + '/' + f
            try:
                self.__sftp.remove(filepath)
            except IOError:
                self.delete_folder_recursively(filepath)

        self.__sftp.rmdir(path)
Exemple #27
0
def export_obj_to_prproj(db_object,
                         files_extractor) -> InternalOperationResult:
    """Creates PPro project in .prproj format using ExtendScript script.
    Project includes video files of each subitem of corresponding object.
    Screencasts and camera recordings puts on different tracks of single sequence.
    :param files_extractor: function for extracting target filenames from db_object;
    :param db_object: db single object.
    """

    ppro_dir = os.path.dirname(settings.ADOBE_PPRO_PATH)
    if not os.path.isfile(os.path.join(ppro_dir, PRPROJ_REQUIRED_FILE)):
        return InternalOperationResult(
            ExecutionStatus.FATAL_ERROR,
            '\'{0}\' is missing. Please, place \'{0}\' empty file to \n\'{1}\'.'
            .format(PRPROJ_REQUIRED_FILE, ppro_dir))

    if FileSystemClient().process_with_name_exists(PPRO_WIN_PROCESS_NAME):
        return InternalOperationResult(
            ExecutionStatus.FATAL_ERROR,
            'Only one instance of PPro may exist. Please, close PPro and try again.'
        )

    screen_files, prof_files, marker_times, sync_offsets = files_extractor(
        db_object)

    if not screen_files or not prof_files:
        return InternalOperationResult(
            ExecutionStatus.FATAL_ERROR,
            'Object is empty or subitems are broken.')

    try:
        ppro_command = build_ppro_command(
            db_object.os_path, PRPROJ_TEMPLATES_PATH, screen_files, prof_files,
            marker_times, sync_offsets,
            translate_non_alphanumerics(db_object.name))
    except Exception as e:
        return InternalOperationResult(ExecutionStatus.FATAL_ERROR, e)

    exec_status = FileSystemClient().execute_command_sync(
        ppro_command, allowable_code=1)  # may return 1 - it's OK

    if exec_status.status is not ExecutionStatus.SUCCESS:
        logger.error('Cannot execute PPro command: %s \n PPro command: %s',
                     exec_status.message, ppro_command)
        return InternalOperationResult(
            ExecutionStatus.FATAL_ERROR,
            'Cannot execute PPro command. Check PPro configuration.')

    logger.info('Execution of PPro command started; \n PPro command: %s',
                ppro_command)
    return InternalOperationResult(ExecutionStatus.SUCCESS)