Exemplo n.º 1
0
def input_from_local(session, log, project_string_id, http_input, file,
                     directory_id):
    # TODO review how we want to handle header options
    # Especially if needs to be outside of function for python requests...
    # immediate_mode = request.headers['immediate_mode']
    # Issues to be careful with ie string treamtment of 'True' vs True...
    immediate_mode = True

    input = Input()
    input.directory_id = directory_id

    if http_input['instance_list']:
        input.instance_list = {}
        input.instance_list['list'] = http_input['instance_list']

    if http_input['frame_packet_map']:
        input.frame_packet_map = http_input['frame_packet_map']

    # only need to make temp dir if file doesn't already exist...

    original_filename = secure_filename(
        file.filename
    )  # http://flask.pocoo.org/docs/0.12/patterns/fileuploads/

    input.extension = os.path.splitext(original_filename)[1].lower()
    input.original_filename = os.path.split(original_filename)[1]

    input.temp_dir = tempfile.mkdtemp()
    input.temp_dir_path_and_filename = input.temp_dir + \
                                       "/" + original_filename + input.extension

    project = Project.get(session, project_string_id)

    input.project = project

    input.media_type = None
    input.media_type = Process_Media.determine_media_type(input.extension)
    if not input.media_type:
        input.status = "failed"
        input.status_text = "Invalid file type: " + input.extension
        return False, log, input

    session.add(input)
    session.flush()

    with open(input.temp_dir_path_and_filename, "wb") as f:

        f.write(file.stream.read())

    # For LOCAL not normal upload
    file_size_limit = 9 * 1024 * 1024 * 1024

    file_size = os.path.getsize(
        input.temp_dir_path_and_filename)  # gets size in bytes

    if file_size > file_size_limit:
        input.status = "failed"
        input.status_text = "Exceeded max file size"
        return False, log, input

    if immediate_mode == True or immediate_mode is None:
        # Leave this as a direct call for time being, as we pass
        # the input back to thing on front end

        process_media = Process_Media(session=session, input=input)

        result = process_media.main_entry()

        # Always return input along with file?

        if result == True:
            return True, log, input

        if result == False:
            return False, log, input

    # Default
    priority = 100

    item = PrioritizedItem(priority=priority,
                           input_id=input.id,
                           media_type=input.media_type)

    add_item_to_queue(item)

    return True, log, input
Exemplo n.º 2
0
    def load(self,
             video_file_name,
             original_filename,
             extension,
             input: Input,
             directory_id=None):
        """

        Convert to .mp4 format if needed
        Upload .mp4 video
        Process each frame

        Arguments
            video_file_name, String, complete file path including directory, filename, and extension
            original_filename, String
            extension, String, includes ".", ie ".mp4"

        Returns
            None
        """

        try:

            clip = moviepy_editor.VideoFileClip(video_file_name)
            input.status = "loaded_video"
            input.time_loaded_video = datetime.datetime.utcnow()
            input.percent_complete = 20.0
            self.try_to_commit()

        except Exception as exception:
            input.status = "failed"
            input.status_text = "Could not load video. Try again, try a different format or contact us."
            # only for internal use
            # could look at storing in DB later or Using event logging.
            logger.error(
                'Could not load video. Try again, try a different format or contact us. Exception:  {}'
                .format(str(exception)))
            return None

        # https://stackoverflow.com/questions/43966523/getting-oserror-winerror-6-the-handle-is-invalid-in-videofileclip-function
        clip.reader.close()
        # Audio thing here too still doesn't seem to fix it...
        # clip.audio.reader.close_proc()

        # fps handling
        fps = self.project.settings_input_video_fps

        if fps is None:
            fps = 5

        if fps < 0 or fps > 120:
            input.status = "failed"
            input.status_text = "Invalid fps setting of " + fps
            return None

        original_fps = clip.fps  # Cache, since it will change

        # Always using original. FPS conversion is now deprecated
        fps = original_fps

        clip = clip.set_fps(fps)
        # https://zulko.github.io/moviepy/ref/VideoClip/VideoClip.html#moviepy.video.VideoClip.VideoClip.set_fps
        # Returns a copy of the clip with a new default fps for functions like write_videofile, iterframe, etc.

        # TODO do we want to save original

        # note these statements need to be after here in order to make sure
        # we update fps properly
        # otherwise have fps of say 0 and it's funny
        length = int(
            clip.duration * fps
        )  # Frame count (ESTIMATED) otherwise requires iteration / loop to get exact

        # temp higher limit for testing stuff
        # enough for a 120fps 5 minutes, or 60 fps 10 minutes
        frame_count_limit = 36000

        if length > frame_count_limit:
            input.status = "failed"
            input.status_text = "Frame count of " + str(length) + \
                                " exceeded limit of " + str(frame_count_limit) + " (per video)" + \
                                " Lower FPS conversion in settings, split into seperate files, or upgrade account."
            return None

        max_size = settings.DEFAULT_MAX_SIZE

        if clip.w > max_size or clip.h > max_size:
            clip = resize_video(clip)

        video_file_name = os.path.splitext(
            video_file_name)[0] + "_re_saved.mp4"

        if settings.PROCESS_MEDIA_TRY_BLOCK_ON is True:
            try:
                # See https://zulko.github.io/moviepy/ref/VideoClip/VideoClip.html?highlight=write_videofile#moviepy.video.io.VideoFileClip.VideoFileClip.write_videofile
                # And https://github.com/Zulko/moviepy/issues/645
                #	BUT note it's been renamed to "logger"

                # TODO maybe capture log output somewhere else for debugging?
                # Maybe we could use log to update input status / percent complete
                """
                Feb 9 2020 Audio to True seems to add issues
                ie : index -100001 is out of bounds for axis 0 with size 0 ffmpeg
                found this
                but I don't think that's it
                https://stackoverflow.com/questions/59358680/how-to-fix-out-of-bounds-error-in-to-soundarray-in-moviepy
            
                The strange part is that some of it works...
                TODO IF audio is a common issue, could have 2 try blocks
                but would want to have this as a function then.
                ie video with no audio is perhaps better then total failure, or total no audio.
                """

                clip.write_videofile(video_file_name,
                                     audio=False,
                                     threads=4,
                                     logger=None)
            except Exception as exception:
                input.status = "failed"
                input.status_text = "Could not write video file. Try a different format or contact us."
                logger.error(
                    'Could not write video file. Try a different format or contact us.)'
                )
                return None

        else:
            clip.write_videofile(video_file_name,
                                 audio=False,
                                 threads=4,
                                 logger=None)
        if not directory_id:
            directory_id = self.project.directory_default_id

        # Video file gets created in advance so
        # be careful to add project here
        """
        This is in the context of Video potentially wanting more stuff from the 
        "parent video".
        This needs a lot of work. For the moment we just get the parent input
        and copy a single attribute here for easier access later on.
        Directionally we want to think about stronger connections between
        split clips.
        And forest wise we need to grab this here because going back to get the input
        afterwards from file can be challenging becasue as the system does
        various modifications the parent gets further and further removed.
        """

        parent_video_split_duration = None
        try:
            parent_input = input.parent_input(self.session)
            if parent_input:
                parent_video_split_duration = parent_input.video_split_duration
        except:
            print("Could not get parent input")
        video, input.file = Video.new(
            session=self.session,
            project=self.project,
            filename=original_filename,
            frame_rate=clip.fps,
            frame_count=0,
            width=clip.w,
            height=clip.h,
            directory_id=directory_id,
            parent_input_id=input.parent_input_id,
            parent_video_split_duration=parent_video_split_duration,
            file_metadata=input.file_metadata,
        )

        if self.input.frame_packet_map:
            self.__prepare_sequences(parent_input=input)
            if self.check_update_log_errors() is False:
                return

        input.file.input_id = input.id  # revsere link is sometimes handy to have.

        # Jan 13, 2020 these are both computed above
        # Video object is not created yet so stored locally and then used here...
        video.original_fps = original_fps
        video.fps = fps
        video.offset_in_seconds = input.offset_in_seconds

        video.root_blob_path_to_frames = settings.PROJECT_IMAGES_BASE_DIR + \
                                         str(self.project.id) + "/" + str(video.id) + "/frames/"

        self.upload_video_file(video_file_name, ".mp4", video)

        input.status = "finished_writing_video_file"
        input.time_video_write_finished = datetime.datetime.utcnow()
        input.percent_complete = 30.0

        self.try_to_commit()

        self.session.add(video)
        initial_global_frame = 0
        if input.type == 'from_video_split':
            initial_global_frame = video.fps * input.offset_in_seconds
        for index, frame in enumerate(clip.iter_frames()):
            global_frame_number = frame

            if input.type == 'from_video_split':
                seconds_offset = input.offset_in_seconds
                offset_in_frames = video.fps * seconds_offset
                global_frame_number = index + offset_in_frames

            if index == 0:
                input.status = "pushing_frames_into_processing_queue"

            # This setups up input, see function below
            self.add_frame_to_queue(
                frame,
                index,
                original_filename,
                self.project,
                directory_id,
                video,
                length,
                input.file,  # assumes this is video_parent_file
                global_frame_number,
                initial_global_frame)

            # TODO clarify if this is actually showing up the queue as expected
            video.frame_count += 1

            # This is really key for monitoring efforts
            # Because at the moment this loop can be fairly slow

            if index % 10 == 0:
                # Where 10 is adding this every 10 frames
                # to be completed by next phase
                # at most this adds 1 when compelte so multiple by 30 to represent
                # this portion of the work
                input.percent_complete += (10 / length) * 30
                self.try_to_commit()

        # Clean up handled in process media..

        input.time_pushed_all_frames_to_queue = datetime.datetime.utcnow()

        return input.file