def _execute_ffmpeg( content: bytes, cli: List[str], program: str = "ffmpeg", ignore_error_if_data: bool = False, get_logs: bool = False, ) -> bytes: mime_type = mime.get_mime_type(content) if mime.is_heif(mime_type): # FFmpeg does not support HEIF. # https://trac.ffmpeg.org/ticket/6521 content = convert_heif_to_png(content) cli = [program, "-loglevel", "32" if get_logs else "24"] + cli proc = Popen(cli, stdout=PIPE, stdin=PIPE, stderr=PIPE) out, err = proc.communicate(input=content) if proc.returncode != 0: args = " ".join(shlex.quote(arg) for arg in cli) logger.warning( f"Failed to execute {program} command (cli={args}, err={err})") if (len(out) > 0 and not ignore_error_if_data) or len(out) == 0: raise errors.ProcessingError("Error while processing media.\n" + err.decode("utf-8")) return err if get_logs else out
def to_thumbnail(self, width: int, height: int) -> bytes: width_greater = self.width > self.height width, height = (-1, height) if width_greater else (width, -1) cli = [] if float(self.duration) > 3.0: cli += ["-ss", math.floor(self.duration * 0.3)] cli += [ "-i", "-", "-f", "image2", "-vf", f"scale={width}:{height}", "-vframes", "1", "-vcodec", "mjpeg", "-", ] content = _execute_ffmpeg(self.content, cli, ignore_error_if_data=True) if not content: raise errors.ProcessingError( "Error while creating thumbnail from video.") return content
def resize_fill(self, width: int, height: int) -> None: width_greater = self.width > self.height width, height = (-1, height) if width_greater else (width, -1) cli = [ "-i", "{path}", "-f", "image2", "-filter:v", "scale='{width}:{height}'".format(width=width, height=height), "-map", "0:v:0", "-vframes", "1", "-vcodec", "png", "-", ] if ("duration" in self.info["format"] and self.info["format"]["format_name"] != "swf"): duration = float(self.info["format"]["duration"]) if duration > 3: cli = [ "-ss", "%d" % math.floor(duration * 0.3), ] + cli content = self._execute(cli, ignore_error_if_data=True) if not content: raise errors.ProcessingError("Error while resizing image.") self.content = content self._reload_info()
def resize_fill(self, width: int, height: int) -> None: cli = [ '-i', '{path}', '-f', 'image2', '-vf', _SCALE_FIT_FMT.format(width=width, height=height), '-map', '0:v:0', '-vframes', '1', '-vcodec', 'png', '-', ] if 'duration' in self.info['format'] \ and self.info['format']['format_name'] != 'swf': duration = float(self.info['format']['duration']) if duration > 3: cli = [ '-ss', '%d' % math.floor(duration * 0.3), ] + cli content = self._execute(cli) if not content: raise errors.ProcessingError('Error while resizing image.') self.content = content self._reload_info()
def _preprocess_image(content: bytes) -> NpMatrix: try: img = Image.open(BytesIO(content)) return np.asarray(img.convert("L"), dtype=np.uint8) except IOError: raise errors.ProcessingError("Unable to generate a signature hash " "for this image.")
def _execute( self, cli: List[str], program: str = 'ffmpeg', ignore_error_if_data: bool = False) -> bytes: extension = mime.get_extension(mime.get_mime_type(self.content)) assert extension with util.create_temp_file(suffix='.' + extension) as handle: handle.write(self.content) handle.flush() cli = [program, '-loglevel', '24'] + cli cli = [part.format(path=handle.name) for part in cli] proc = subprocess.Popen( cli, stdout=subprocess.PIPE, stdin=subprocess.PIPE, stderr=subprocess.PIPE) out, err = proc.communicate(input=self.content) if proc.returncode != 0: logger.warning( 'ffmpeg 명령어 실행 실패 (cli=%r, err=%r)', ' '.join(shlex.quote(arg) for arg in cli), err) if ((len(out) > 0 and not ignore_error_if_data) or len(out) == 0): raise errors.ProcessingError( '이미지 처리 중 오류.\n' + err.decode('utf-8')) return out
def _execute( self, cli: List[str], program: str = "ffmpeg", ignore_error_if_data: bool = False, get_logs: bool = False, ) -> bytes: extension = mime.get_extension(mime.get_mime_type(self.content)) assert extension with util.create_temp_file(suffix="." + extension) as handle: handle.write(self.content) handle.flush() cli = [program, "-loglevel", "32" if get_logs else "24"] + cli cli = [part.format(path=handle.name) for part in cli] proc = subprocess.Popen( cli, stdout=subprocess.PIPE, stdin=subprocess.PIPE, stderr=subprocess.PIPE, ) out, err = proc.communicate(input=self.content) if proc.returncode != 0: logger.warning( "Failed to execute ffmpeg command (cli=%r, err=%r)", " ".join(shlex.quote(arg) for arg in cli), err, ) if (len(out) > 0 and not ignore_error_if_data) or len(out) == 0: raise errors.ProcessingError( "Error while processing image.\n" + err.decode("utf-8")) return err if get_logs else out
def resize_fill(self, width: int, height: int) -> None: width_greater = self.width > self.height width, height = (-1, height) if width_greater else (width, -1) cli = [ '-i', '{path}', '-f', 'image2', '-filter:v', "scale='{width}:{height}'".format( width=width, height=height), '-map', '0:v:0', '-vframes', '1', '-vcodec', 'png', '-', ] if 'duration' in self.info['format'] \ and self.info['format']['format_name'] != 'swf': duration = float(self.info['format']['duration']) if duration > 3: cli = [ '-ss', '%d' % math.floor(duration * 0.3), ] + cli content = self._execute(cli, ignore_error_if_data=True) if not content: raise errors.ProcessingError('이미지 리사이징 오류.') self.content = content self._reload_info()
def download(url: str) -> bytes: assert url request = urllib.request.Request(url) request.add_header('Referer', url) try: with urllib.request.urlopen(request) as handle: return handle.read() except Exception as ex: raise errors.ProcessingError('Error downloading %s (%s)' % (url, ex))
def _reload_info(self) -> None: try: self._extract_from_exif() except Exception: self._extract_using_ffmpeg() assert self.width > 0 assert self.height > 0 if (not self.width) or (not self.height): logger.warning("Error processing this image.") raise errors.ProcessingError("Error processing this image.")
def download(url: str) -> bytes: assert url request = urllib.request.Request(url) if config.config['user_agent']: request.add_header('User-Agent', config.config['user_agent']) request.add_header('Referer', url) try: with urllib.request.urlopen(request) as handle: return handle.read() except Exception as ex: raise errors.ProcessingError('%s 다운로드 중 오류 (%s)' % (url, ex))
def wrapper_inner(*args: Any, **kwargs: Any) -> Any: try: return target_function(*args, **kwargs) except elasticsearch.exceptions.NotFoundError: # index not yet created, will be created dynamically by # add_image() return default_param_factory() except elasticsearch.exceptions.ElasticsearchException as ex: logger.warning('Problem with elastic search: %s', ex) raise errors.ThirdPartyError('Elastic search 연결 오류.') except IOError: raise errors.ProcessingError('이미지가 아닙니다.') except Exception as ex: raise errors.ThirdPartyError('알 수 없는 에러 (%s).' % ex)
def _reload_info(self) -> None: self.info = json.loads(self._execute([ '-i', '{path}', '-of', 'json', '-select_streams', 'v', '-show_format', '-show_streams', ], program='ffprobe').decode('utf-8')) assert 'format' in self.info assert 'streams' in self.info if len(self.info['streams']) < 1: logger.warning('The video contains no video streams.') raise errors.ProcessingError( '동영상에 동영상 스트림이 없습니다.')
def check_for_sound(self) -> bool: audioinfo = json.loads( _execute_ffmpeg( self.content, [ "-i", "-", "-of", "json", "-select_streams", "a", "-show_streams", ], program="ffprobe", ).decode("utf-8")) assert "streams" in audioinfo if len(audioinfo["streams"]) < 1: return False log = _execute_ffmpeg( self.content, [ "-hide_banner", "-progress", "-", "-i", "-", "-af", "volumedetect", "-max_muxing_queue_size", "99999", "-vn", "-sn", "-f", "null", "-y", "/dev/null", ], get_logs=True, ).decode("utf-8", errors="replace") log_match = re.search(r".*volumedetect.*mean_volume: (.*) dB", log) if not log_match or not log_match.groups(): raise errors.ProcessingError( "A problem occured when trying to check for audio") meanvol = float(log_match.groups()[0]) # -91.0 dB is the minimum for 16-bit audio, assume sound if > -80.0 dB return meanvol > -80.0
def _execute(self, cli, program='ffmpeg'): with util.create_temp_file() as handle: handle.write(self.content) handle.flush() cli = [program, '-loglevel', '24'] + cli cli = [part.format(path=handle.name) for part in cli] proc = subprocess.Popen(cli, stdout=subprocess.PIPE, stdin=subprocess.PIPE, stderr=subprocess.PIPE) out, err = proc.communicate(input=self.content) if proc.returncode != 0: raise errors.ProcessingError( 'Error while processing image.\n' + err.decode('utf-8')) return out
def download(url: str, use_video_downloader: bool = False) -> bytes: assert url request = urllib.request.Request(url) if config.config['user_agent']: request.add_header('User-Agent', config.config['user_agent']) request.add_header('Referer', url) try: with urllib.request.urlopen(request) as handle: content = handle.read() except Exception as ex: raise errors.ProcessingError('Error downloading %s (%s)' % (url, ex)) if (use_video_downloader and mime.get_mime_type(content) == 'application/octet-stream'): return _youtube_dl_wrapper(url) return content
def to_thumbnail(self, width: int, height: int) -> bytes: width_greater = self.width > self.height width, height = (-1, height) if width_greater else (width, -1) cli = ["-i", "-"] self._add_orientation_filters(cli, f"scale='{width}:{height}'") cli += ["-f", "image2", "-vframes", "1", "-vcodec", "png", "-"] content = _execute_ffmpeg(self.content, cli, ignore_error_if_data=True) if not content: raise errors.ProcessingError( "Error while creating thumbnail from image.") return content
def _reload_info(self): cmd = [ "-i", "-", "-print_format", "json", "-show_streams", "-show_format", ] info = json.loads( _execute_ffmpeg( self.content, cmd, program="ffprobe", ).decode("utf-8")) assert "streams" in info if len(info["streams"]) < 1: logger.warning("This video contains no video streams.") raise errors.ProcessingError( "The video contains no video streams.") self.width = info["streams"][0]["width"] self.height = info["streams"][0]["height"] assert "format" in info assert "tags" in info["format"] if "creation_time" in info["format"]["tags"]: self.date_taken = info["format"]["tags"]["creation_time"] # List of tuples where only one value can be valid option_tuples = ( ("manufacturer", "com.android.manufacturer"), ("model", "com.android.model"), ) camera_string = [] for option_tuple in option_tuples: for option in option_tuple: if option in info["format"]["tags"]: camera_string.append(info["format"]["tags"][option]) break if camera_string: self.camera = " ".join(camera_string)
def _reload_info(self): self.info = json.loads( self._execute([ '-of', 'json', '-select_streams', 'v', '-show_streams', '-count_frames', '-i', '-', ], program='ffprobe').decode('utf-8')) assert 'streams' in self.info if len(self.info['streams']) != 1: raise errors.ProcessingError('Multiple video streams detected.')
def check_for_sound(self) -> bool: audioinfo = json.loads( self._execute([ '-i', '{path}', '-of', 'json', '-select_streams', 'a', '-show_streams', ], program='ffprobe').decode('utf-8')) assert 'streams' in audioinfo if len(audioinfo['streams']) < 1: return False log = self._execute([ '-hide_banner', '-progress', '-', '-i', '{path}', '-af', 'volumedetect', '-max_muxing_queue_size', '99999', '-vn', '-sn', '-f', 'null', '-y', '/dev/null', ], get_logs=True).decode('utf-8', errors='replace') log_match = re.search(r'.*volumedetect.*mean_volume: (.*) dB', log) if not log_match or not log_match.groups(): raise errors.ProcessingError( 'A problem occured when trying to check for audio') meanvol = float(log_match.groups()[0]) # -91.0 dB is the minimum for 16-bit audio, assume sound if > -80.0 dB return meanvol > -80.0
def _execute(self, cli: List[str], program: str = 'ffmpeg') -> bytes: extension = mime.get_extension(mime.get_mime_type(self.content)) assert extension with util.create_temp_file(suffix='.' + extension) as handle: handle.write(self.content) handle.flush() cli = [program, '-loglevel', '24'] + cli cli = [part.format(path=handle.name) for part in cli] proc = subprocess.Popen(cli, stdout=subprocess.PIPE, stdin=subprocess.PIPE, stderr=subprocess.PIPE) out, err = proc.communicate(input=self.content) if proc.returncode != 0: logger.warning( 'Failed to execute ffmpeg command (cli=%r, err=%r)', ' '.join(shlex.quote(arg) for arg in cli), err) raise errors.ProcessingError( 'Error while processing image.\n' + err.decode('utf-8')) return out
def _reload_info(self) -> None: self.info = json.loads( self._execute( [ "-i", "{path}", "-of", "json", "-select_streams", "v", "-show_format", "-show_streams", ], program="ffprobe", ).decode("utf-8")) assert "format" in self.info assert "streams" in self.info if len(self.info["streams"]) < 1: logger.warning("The video contains no video streams.") raise errors.ProcessingError( "The video contains no video streams.")