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
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