def setFilename(self, filename, append=True): """ Use a file to store all messages. The UTF-8 encoding will be used. Write an informative message if the file can't be created. @param filename: C{L{string}} """ # Look if file already exists or not filename = os.path.expanduser(filename) filename = os.path.realpath(filename) append = os.access(filename, os.F_OK) # Create log file (or open it in append mode, if it already exists) try: import codecs if append: self.__file = codecs.open(filename, "a", "utf-8") else: self.__file = codecs.open(filename, "w", "utf-8") self._writeIntoFile(_("Starting Hachoir")) except IOError, err: if err.errno == 2: self.__file = None self.info( _("[Log] setFilename(%s) fails: no such file") % filename) else: raise
def useHeader(self, header): self.width = header["width"].value self.height = header["height"].value # Read number of colors and pixel format if "/palette/size" in header: nb_colors = header["/palette/size"].value // 3 else: nb_colors = None if not header["has_palette"].value: if header["has_alpha"].value: self.pixel_format = _("RGBA") else: self.pixel_format = _("RGB") elif "/transparency" in header: self.pixel_format = _("Color index with transparency") if nb_colors: nb_colors -= 1 else: self.pixel_format = _("Color index") self.bits_per_pixel = pngBitsPerPixel(header) if nb_colors: self.nb_colors = nb_colors # Read compression, timestamp, etc. self.compression = header["compression"].display
def getHachoirOptions(parser): """ Create an option group (type optparse.OptionGroup) of Hachoir library options. """ def setLogFilename(*args): log.setFilename(args[2]) common = OptionGroup(parser, _("Hachoir library"), \ "Configure Hachoir library") common.add_option("--verbose", help=_("Verbose mode"), default=False, action="store_true") common.add_option("--log", help=_("Write log in a file"), type="string", action="callback", callback=setLogFilename) common.add_option("--quiet", help=_("Quiet mode (don't display warning)"), default=False, action="store_true") common.add_option("--debug", help=_("Debug mode"), default=False, action="store_true") return common
def setFilename(self, filename, append=True): """ Use a file to store all messages. The UTF-8 encoding will be used. Write an informative message if the file can't be created. @param filename: C{L{string}} """ # Look if file already exists or not filename = os.path.expanduser(filename) filename = os.path.realpath(filename) append = os.access(filename, os.F_OK) # Create log file (or open it in append mode, if it already exists) try: import codecs if append: self.__file = codecs.open(filename, "a", "utf-8") else: self.__file = codecs.open(filename, "w", "utf-8") self._writeIntoFile(_("Starting Hachoir")) except IOError, err: if err.errno == 2: self.__file = None self.info(_("[Log] setFilename(%s) fails: no such file") % filename) else: raise
def processMovieHeader(self, hdr): self.creation_date = hdr["creation_date"].value self.last_modification = hdr["lastmod_date"].value self.duration = timedelta(seconds=float(hdr["duration"].value) / hdr["time_scale"].value) self.comment = _("Play speed: %.1f%%") % (hdr["play_speed"].value * 100) self.comment = _("User volume: %.1f%%") % (float(hdr["volume"].value) * 100)
def __init__(self, size, address, got=None): self.size = size self.address = address self.got = got if self.got is not None: msg = _("Can't read %u bits at address %u (got %u bits)") % (self.size, self.address, self.got) else: msg = _("Can't read %u bits at address %u") % (self.size, self.address) InputStreamError.__init__(self, msg)
def useFile(self, field): meta = Metadata(self) meta.filename = field["filename"].value meta.file_size = field["filesize"].value meta.creation_date = field["timestamp"].value attr = field["attributes"].value if attr != "(none)": meta.file_attr = attr if meta.has("filename"): title = _("File \"%s\"") % meta.getText('filename') else: title = _("File") self.addGroup(field.name, meta, title)
def extract(self, tar): max_nb = maxNbFile(self) for index, field in enumerate(tar.array("file")): if max_nb is not None and max_nb <= index: self.warning("TAR archive contains many files, but only first %s files are processed" % max_nb) break meta = Metadata(self) self.extractFile(field, meta) if meta.has("filename"): title = _('File "%s"') % meta.getText('filename') else: title = _("File") self.addGroup(field.name, meta, title)
def extract(self, gif): self.useScreen(gif["/screen"]) if self.has("bits_per_pixel"): self.nb_colors = (1 << self.get('bits_per_pixel')) self.compression = _("LZW") self.format_version = "GIF version %s" % gif["version"].value for comments in gif.array("comments"): for comment in gif.array(comments.name + "/comment"): self.comment = comment.value if "graphic_ctl/has_transp" in gif and gif["graphic_ctl/has_transp"].value: self.pixel_format = _("Color index with transparency") else: self.pixel_format = _("Color index")
def __init__(self, stream, **args): validate = args.pop("validate", False) self._mime_type = None while validate: nbits = self.getParserTags()["min_size"] if stream.sizeGe(nbits): res = self.validate() if res is True: break res = makeUnicode(res) else: res = _("stream is smaller than %s.%s bytes" % divmod(nbits, 8)) raise ValidateError(res or _("no reason given")) self._autofix = True
def extract(self, gif): self.useScreen(gif["/screen"]) if self.has("bits_per_pixel"): self.nb_colors = (1 << self.get('bits_per_pixel')) self.compression = _("LZW") self.format_version = "GIF version %s" % gif["version"].value for comments in gif.array("comments"): for comment in gif.array(comments.name + "/comment"): self.comment = comment.value if "graphic_ctl/has_transp" in gif and gif[ "graphic_ctl/has_transp"].value: self.pixel_format = _("Color index with transparency") else: self.pixel_format = _("Color index")
def startOfFrame(self, sof): # Set compression method key = sof["../type"].value self.compression = "JPEG (%s)" % JpegChunk.START_OF_FRAME[key] # Read image size and bits/pixel self.width = sof["width"].value self.height = sof["height"].value nb_components = sof["nr_components"].value self.bits_per_pixel = 8 * nb_components if nb_components == 3: self.pixel_format = _("YCbCr") elif nb_components == 1: self.pixel_format = _("Grayscale") self.nb_colors = 256
def extract(self, tar): max_nb = maxNbFile(self) for index, field in enumerate(tar.array("file")): if max_nb is not None and max_nb <= index: self.warning( "TAR archive contains many files, but only first %s files are processed" % max_nb) break meta = Metadata(self) self.extractFile(field, meta) if meta.has("filename"): title = _('File "%s"') % meta.getText('filename') else: title = _("File") self.addGroup(field.name, meta, title)
def computeVariableBitrate(self, mp3): if self.quality <= QUALITY_FAST: return count = 0 if QUALITY_BEST <= self.quality: self.warning( "Process all MPEG audio frames to compute exact duration") max_count = None else: max_count = 500 * self.quality total_bit_rate = 0.0 for index, frame in enumerate(mp3.array("frames/frame")): if index < 3: continue bit_rate = frame.getBitRate() if bit_rate: total_bit_rate += float(bit_rate) count += 1 if max_count and max_count <= count: break if not count: return bit_rate = total_bit_rate / count self.bit_rate = (bit_rate, _("%s (Variable bit rate)") % humanBitRate(bit_rate)) duration = timedelta(seconds=float(mp3["frames"].size) / bit_rate) self.duration = duration
def extract(self, icon): for index, header in enumerate(icon.array("icon_header")): image = Metadata(self) # Read size and colors from header image.width = header["width"].value image.height = header["height"].value bpp = header["bpp"].value nb_colors = header["nb_color"].value if nb_colors != 0: image.nb_colors = nb_colors if bpp == 0 and nb_colors in self.color_to_bpp: bpp = self.color_to_bpp[nb_colors] elif bpp == 0: bpp = 8 image.bits_per_pixel = bpp image.setHeader( _("Icon #%u (%sx%s)") % (1 + index, image.get("width", "?"), image.get("height", "?"))) # Read compression from data (if available) key = "icon_data[%u]/header/codec" % index if key in icon: image.compression = icon[key].display key = "icon_data[%u]/pixels" % index if key in icon: computeComprRate(image, icon[key].size) # Store new image self.addGroup("image[%u]" % index, image)
def __delitem__(self, index): """ Delete item at position index. May raise IndexError. >>> d=Dict( ((6, 'six'), (9, 'neuf'), (4, 'quatre')) ) >>> del d[1] >>> d {6: 'six', 4: 'quatre'} """ if index < 0: index += len(self._value_list) if not (0 <= index < len(self._value_list)): raise IndexError( _("list assignment index out of range (%s/%s)") % (index, len(self._value_list))) del self._value_list[index] del self._key_list[index] # First loop which may alter self._index for key, item_index in self._index.iteritems(): if item_index == index: del self._index[key] break # Second loop update indexes for key, item_index in self._index.iteritems(): if index < item_index: self._index[key] -= 1
def extractAVI(self, headers): audio_index = 1 for stream in headers.array("stream"): if "stream_hdr/stream_type" not in stream: continue stream_type = stream["stream_hdr/stream_type"].value if stream_type == "vids": if "stream_hdr" in stream: meta = Metadata(self) self.extractAVIVideo(stream["stream_hdr"], meta) self.addGroup("video", meta, "Video stream") elif stream_type == "auds": if "stream_fmt" in stream: meta = Metadata(self) self.extractAVIAudio(stream["stream_fmt"], meta) self.addGroup("audio[%u]" % audio_index, meta, "Audio stream") audio_index += 1 if "avi_hdr" in headers: self.useAviHeader(headers["avi_hdr"]) # Compute global bit rate if self.has("duration") and "/movie/size" in headers: self.bit_rate = float( headers["/movie/size"].value) * 8 / timedelta2seconds( self.get('duration')) # Video has index? if "/index" in headers: self.comment = _("Has audio/video index (%s)") \ % humanFilesize(headers["/index"].size/8)
def computeBitrate(self, frame): bit_rate = frame.getBitRate() # may returns None on error if not bit_rate: return self.bit_rate = (bit_rate, _("%s (constant)") % humanBitRate(bit_rate)) self.duration = timedelta(seconds=float(frame["/frames"].size) / bit_rate)
def extractAVI(self, headers): audio_index = 1 for stream in headers.array("stream"): if "stream_hdr/stream_type" not in stream: continue stream_type = stream["stream_hdr/stream_type"].value if stream_type == "vids": if "stream_hdr" in stream: meta = Metadata(self) self.extractAVIVideo(stream["stream_hdr"], meta) self.addGroup("video", meta, "Video stream") elif stream_type == "auds": if "stream_fmt" in stream: meta = Metadata(self) self.extractAVIAudio(stream["stream_fmt"], meta) self.addGroup("audio[%u]" % audio_index, meta, "Audio stream") audio_index += 1 if "avi_hdr" in headers: self.useAviHeader(headers["avi_hdr"]) # Compute global bit rate if self.has("duration") and "/movie/size" in headers: self.bit_rate = float(headers["/movie/size"].value) * 8 / timedelta2seconds(self.get('duration')) # Video has index? if "/index" in headers: self.comment = _("Has audio/video index (%s)") \ % humanFilesize(headers["/index"].size/8)
def FileInputStream(filename, real_filename=None, **args): """ Create an input stream of a file. filename must be unicode. real_filename is an optional argument used to specify the real filename, its type can be 'str' or 'unicode'. Use real_filename when you are not able to convert filename to real unicode string (ie. you have to use unicode(name, 'replace') or unicode(name, 'ignore')). """ assert isinstance(filename, unicode) if not real_filename: real_filename = filename try: inputio = FileOpener(real_filename, 'rb') except IOError as err: charset = getTerminalCharset() errmsg = unicode(str(err), charset) raise InputStreamError( _("Unable to open file %s: %s") % (filename, errmsg)) source = "file:" + filename offset = args.pop("offset", 0) size = args.pop("size", None) if offset or size: if size: size = 8 * size stream = InputIOStream(inputio, source=source, **args) return InputSubStream(stream, 8 * offset, size, **args) else: args.setdefault("tags", []).append(("filename", filename)) return InputIOStream(inputio, source=source, **args)
def extract(self, icon): for index, header in enumerate(icon.array("icon_header")): image = Metadata(self) # Read size and colors from header image.width = header["width"].value image.height = header["height"].value bpp = header["bpp"].value nb_colors = header["nb_color"].value if nb_colors != 0: image.nb_colors = nb_colors if bpp == 0 and nb_colors in self.color_to_bpp: bpp = self.color_to_bpp[nb_colors] elif bpp == 0: bpp = 8 image.bits_per_pixel = bpp image.setHeader(_("Icon #%u (%sx%s)") % (1+index, image.get("width", "?"), image.get("height", "?"))) # Read compression from data (if available) key = "icon_data[%u]/header/codec" % index if key in icon: image.compression = icon[key].display key = "icon_data[%u]/pixels" % index if key in icon: computeComprRate(image, icon[key].size) # Store new image self.addGroup("image[%u]" % index, image)
def computeVariableBitrate(self, mp3): if self.quality <= QUALITY_FAST: return count = 0 if QUALITY_BEST <= self.quality: self.warning("Process all MPEG audio frames to compute exact duration") max_count = None else: max_count = 500 * self.quality total_bit_rate = 0.0 for index, frame in enumerate(mp3.array("frames/frame")): if index < 3: continue bit_rate = frame.getBitRate() if bit_rate: total_bit_rate += float(bit_rate) count += 1 if max_count and max_count <= count: break if not count: return bit_rate = total_bit_rate / count self.bit_rate = (bit_rate, _("%s (Variable bit rate)") % humanBitRate(bit_rate)) duration = timedelta(seconds=float(mp3["frames"].size) / bit_rate) self.duration = duration
def _get(self, pos): if not pos: return None, None if pos.parent and pos.field != pos.parent.field[pos.index]: hachoir_log.error(_("assertion failed at urwid_ui.Walker._get")) if not (pos.flags is None and pos.hidden()) and pos.flags != self.flags: self.update(pos) return pos.getWidget(self.start), pos
def ask_split(self, caption, min, max): num = GetNumberFromUser(_('Enter split offset:'), '', caption, min, min, max, self.parent) if -1 == num: return None else: return num
def _getValue(self): try: value = self.createValue() except HACHOIR_ERRORS as err: self.error(_("Unable to create value: %s") % unicode(err)) value = None self._getValue = lambda: value return value
def displayStat(self, stat): """ Display statistics to stdout: - best time (minimum) - average time (arithmetic average) - worst time (maximum) - total time (sum) Use arithmetic avertage instead of geometric average because geometric fails if any value is zero (returns zero) and also because floating point multiplication lose precision with many values. """ average = stat.getSum() / len(stat) values = (stat.getMin(), average, stat.getMax(), stat.getSum()) values = tuple(self.formatTime(value) for value in values) print _("Benchmark: best=%s average=%s worst=%s total=%s") \ % values
def file_open_dialog(): dialog_style = wx.OPEN | wx.FILE_MUST_EXIST dialog = wx.FileDialog( None, message = _('Open'), defaultDir = os.getcwd(), defaultFile = '', style = dialog_style) return dialog
def append(self, key, value): """ Append new value """ if key in self._index: raise UniqKeyError(_("Key '%s' already exists") % key) self._index[key] = len(self._value_list) self._key_list.append(key) self._value_list.append(value)
def __setattr__(self, key, value): """ Add a new value to data with name 'key'. Skip duplicates. """ # Invalid key? if key not in self.__data: raise KeyError(_("%s has no metadata '%s'") % (self.__class__.__name__, key)) # Skip duplicates self.__data[key].add(value)
def file_open_dialog(): dialog_style = wx.OPEN | wx.FILE_MUST_EXIST dialog = wx.FileDialog(None, message=_('Open'), defaultDir=os.getcwd(), defaultFile='', style=dialog_style) return dialog
def __setattr__(self, key, value): """ Add a new value to data with name 'key'. Skip duplicates. """ # Invalid key? if key not in self.__data: raise KeyError( _("%s has no metadata '%s'") % (self.__class__.__name__, key)) # Skip duplicates self.__data[key].add(value)
def extract(self, pcx): self.width = 1 + pcx["xmax"].value self.height = 1 + pcx["ymax"].value self.width_dpi = pcx["horiz_dpi"].value self.height_dpi = pcx["vert_dpi"].value self.bits_per_pixel = pcx["bpp"].value if 1 <= pcx["bpp"].value <= 8: self.nb_colors = 2 ** pcx["bpp"].value self.compression = _("Run-length encoding (RLE)") self.format_version = "PCX: %s" % pcx["version"].display if "image_data" in pcx: computeComprRate(self, pcx["image_data"].size)
def extract(self, pcx): self.width = 1 + pcx["xmax"].value self.height = 1 + pcx["ymax"].value self.width_dpi = pcx["horiz_dpi"].value self.height_dpi = pcx["vert_dpi"].value self.bits_per_pixel = pcx["bpp"].value if 1 <= pcx["bpp"].value <= 8: self.nb_colors = 2**pcx["bpp"].value self.compression = _("Run-length encoding (RLE)") self.format_version = "PCX: %s" % pcx["version"].display if "image_data" in pcx: computeComprRate(self, pcx["image_data"].size)
def main(): try: # Parser options and initialize Hachoir values, filenames = parseOptions() if values.debug: hachoir_config.debug = True elif values.verbose: hachoir_config.verbose = True else: hachoir_config.quiet = True if values.profiler: ok = profile(values, filenames) elif values.bench: ok = benchmarkMetadata(values, filenames) else: ok = processFiles(values, filenames) except KeyboardInterrupt: print _("Program interrupted (CTRL+C).") ok = False sys.exit(int(not ok))
def insert(self, index, key, value): """ Insert an item at specified position index. >>> d=Dict( ((6, 'six'), (9, 'neuf'), (4, 'quatre')) ) >>> d.insert(1, '40', 'quarante') >>> d {6: 'six', '40': 'quarante', 9: 'neuf', 4: 'quatre'} """ if key in self: raise UniqKeyError(_("Insert error: key '%s' ready exists") % key) _index = index if index < 0: index += len(self._value_list) if not (0 <= index <= len(self._value_list)): raise IndexError(_("Insert error: index '%s' is invalid") % _index) for item_key, item_index in self._index.iteritems(): if item_index >= index: self._index[item_key] += 1 self._index[key] = index self._key_list.insert(index, key) self._value_list.insert(index, value)
def displayVersion(*args): import hachoir_core from hachoir_metadata import __version__ print _("Metadata extractor version %s") % __version__ print _("Hachoir library version %s") % hachoir_core.__version__ print print _("Website: %s/wiki/hachoir-metadata") % hachoir_core.WEBSITE sys.exit(0)
def humanFilesize(size): """ Convert a file size in byte to human natural representation. It uses the values: 1 KB is 1024 bytes, 1 MB is 1024 KB, etc. The result is an unicode string. >>> humanFilesize(1) u'1 byte' >>> humanFilesize(790) u'790 bytes' >>> humanFilesize(256960) u'250.9 KB' """ if size < 10000: return ngettext("%u byte", "%u bytes", size) % size units = [_("KB"), _("MB"), _("GB"), _("TB")] size = float(size) divisor = 1024 for unit in units: size = size / divisor if size < divisor: return "%.1f %s" % (size, unit) return "%u %s" % (size, unit)
def run(): msg = _resize = retry = 0 events = ( "window resize", ) profile_display = args.profile_display while True: for e in events: try: if e == "window resize": size = ui.get_cols_rows() resize = log.height else: e = top.keypress(size, e) if e is None: pass elif e in ('f1', '?'): try: body.select(body.tabs.index(help)) except ValueError: body.append(help) resize = log.height elif e in ('esc', 'ctrl w'): body.close() if body.box_widget is None: return resize = log.height elif e == '+': if log.height: resize = log.height - 1 elif e == '-': resize = log.height + 1 elif e == 'q': return #except AssertionError: # hachoir_log.error(getBacktrace()) except NewTab_Stream, e: stream = e.field.getSubIStream() logger.objects[stream] = e = "%u/%s" % (body.active, e.field.absolute_address) parser = guessParser(stream) if not parser: hachoir_log.error(_("No parser found for %s") % stream.source) else: logger.objects[parser] = e body.append((e, TreeBox(charset, Node(parser, None), preload_fields, None, options))) resize = log.height except NeedInput, e: input.do(*e.args) if profile_display: events = events[1:] break
def timestampMac32(value): """ Convert an Mac (32-bit) timestamp to string. The format is the number of seconds since the 1st January 1904 (to 2040). Returns unicode string. >>> timestampMac32(0) datetime.datetime(1904, 1, 1, 0, 0) >>> timestampMac32(2843043290) datetime.datetime(1994, 2, 2, 14, 14, 50) """ if not isinstance(value, (float, int, long)): raise TypeError("an integer or float is required") if not(0 <= value <= 4294967295): return _("invalid Mac timestamp (%s)") % value return MAC_TIMESTAMP_T0 + timedelta(seconds=value)
def doparse(self, stream, fallback=True): fb = None warn = warning for parser in self.parsers: try: parser_obj = parser(stream, validate=self.validate) if self.parser_args: for key, value in self.parser_args.iteritems(): setattr(parser_obj, key, value) return parser_obj except ValidateError as err: res = unicode(err) if fallback and self.fallback: fb = parser except HACHOIR_ERRORS as err: res = unicode(err) if warn: if parser == self.other: warn = info warn(_("Skip parser '%s': %s") % (parser.__name__, res)) fallback = False if self.use_fallback and fb: warning(_("Force use of parser '%s'") % fb.__name__) return fb(stream)
def post_init(self): ListCtrlAutoWidthMixin.__init__(self) columns = [_('address'), _('name'), _('type'), _('size'), _('data'), _('description')] for name in columns: self.append_column(name) self.Layout() self.Fit() self.dispatcher.trigger('field_view_ready', self)
def timestampWin64(value): """ Convert Windows 64-bit timestamp to string. The timestamp format is a 64-bit number which represents number of 100ns since the 1st January 1601 at 00:00. Result is an unicode string. See also durationWin64(). Maximum date is 28 may 60056. >>> timestampWin64(0) datetime.datetime(1601, 1, 1, 0, 0) >>> timestampWin64(127840491566710000) datetime.datetime(2006, 2, 10, 12, 45, 56, 671000) """ try: return WIN64_TIMESTAMP_T0 + durationWin64(value) except OverflowError: raise ValueError(_("date newer than year %s (value=%s)") % (MAXYEAR, value))
def fill_view(self): if self.fields._getParent() is not None: self.view.append_row({ _('name') : '../' }) for field in self.fields: map = { _('address') : self.format_addr(field), _('name') : format_name(field), _('type') : field.getFieldType(), _('size') : format_size(field._getSize()), _('data') : format_data(field), _('description'): format_desc(field) } self.view.append_row(map)
def _get(self, index): if index >= len(self.buffers): return '' buf = self.buffers[index] if buf is None: raise InputStreamError(_("Error: Buffers too small. Can't seek backward.")) if self.last != index: next = buf[1] prev = buf[2] self.buffers[next][2] = prev self.buffers[prev][1] = next first = self.buffers[self.last][1] buf[1] = first buf[2] = self.last self.buffers[first][2] = index self.buffers[self.last][1] = index self.last = index return buf[0]
def FileInputStream(filename, real_filename=None, **args): """ Create an input stream of a file. filename must be unicode. real_filename is an optional argument used to specify the real filename, its type can be 'str' or 'unicode'. Use real_filename when you are not able to convert filename to real unicode string (ie. you have to use unicode(name, 'replace') or unicode(name, 'ignore')). """ assert isinstance(filename, unicode) if not real_filename: real_filename = filename try: inputio = FileOpener(real_filename, 'rb') except IOError, err: charset = getTerminalCharset() errmsg = unicode(str(err), charset) raise InputStreamError(_("Unable to open file %s: %s") % (filename, errmsg))
def __init__(self, input, size=None, **args): if not hasattr(input, "seek"): if size is None: input = InputPipe(input, self._setSize) else: input = InputPipe(input) elif size is None: try: input.seek(0, 2) size = input.tell() * 8 except IOError, err: if err.errno == ESPIPE: input = InputPipe(input, self._setSize) else: charset = getTerminalCharset() errmsg = unicode(str(err), charset) source = args.get("source", "<inputio:%r>" % input) raise InputStreamError(_("Unable to get size of %s: %s") % (source, errmsg))
def createDisplay(self, human=True): if not human: if self._raw_value is None: self._raw_value = GenericString.createValue(self, False) value = makePrintable(self._raw_value, "ASCII", to_unicode=True) elif self._charset: value = makePrintable(self.value, "ISO-8859-1", to_unicode=True) else: value = self.value if config.max_string_length < len(value): # Truncate string if needed value = "%s(...)" % value[:config.max_string_length] if not self._charset or not human: return makePrintable(value, "ASCII", quote='"', to_unicode=True) else: if value: return '"%s"' % value.replace('"', '\\"') else: return _("(empty)")