diff options
Diffstat (limited to 'rapid')
-rw-r--r-- | rapid/ChangeLog | 32 | ||||
-rw-r--r-- | rapid/INSTALL | 8 | ||||
-rw-r--r-- | rapid/config.py | 2 | ||||
-rwxr-xr-x | rapid/media.py | 1 | ||||
-rwxr-xr-x | rapid/metadata.py | 2 | ||||
-rwxr-xr-x | rapid/rapid.py | 146 | ||||
-rw-r--r-- | rapid/renamesubfolderprefs.py | 20 | ||||
-rwxr-xr-x | rapid/videometadata.py | 92 |
8 files changed, 222 insertions, 81 deletions
diff --git a/rapid/ChangeLog b/rapid/ChangeLog index 4ac5796..d749497 100644 --- a/rapid/ChangeLog +++ b/rapid/ChangeLog @@ -1,3 +1,35 @@ +Version 0.3.3 +------------- + +2010-10-24 + +Added support for mod, tod and 3gp video files. + +Hachoir-metadata is now used to extract selected metadata from video files. It +has less bugs than kaa-metadata, and is better maintained. One benefit of this +change is that more video file types can have their metadata extracted. Another +is that the video creation date is now correctly read (the creation time read by +kaa metadata was sometimes wrong by a few hours). Kaa-metadata is still used to +extract some the codec, fourcc and frames per second (FPS) metadata. + +Fixed bug #640722: Added preliminary support for Samusung SRW files. Current +versions of Exiv2 and pyexiv2 can read some but not all metadata from this new +RAW format. If you try to use metadata that cannot be extracted, Rapid Photo +Downloader will issue a warning. + +Fixed bug #550883: Generation of subfolders and filenames using the time a +download was started. + +Fixed bugs related to missing video download directory at program startup. + +Added command line option to output to the terminal information usesful for +debugging. + +Added Norwegian Bokmal and Portuguese translations. Updated Brazilian +Portuguese, Dutch, Finnish, German, Hungarian, Italian, Norwegian Nynorsk, +Polish, Russian, Serbian, Slovak and Ukrainian translations. + + Version 0.3.2 ------------- diff --git a/rapid/INSTALL b/rapid/INSTALL index 3967608..de0e15e 100644 --- a/rapid/INSTALL +++ b/rapid/INSTALL @@ -22,15 +22,17 @@ types). If you want to download videos, you can install: +- python-hachoir-metadata - python-kaa-metadata - ffmpegthumbnailer -kaa metadata is required to download videos. ffmpegthumbnailer is used only to +hachoir metadata is required to download videos. kaa metadata is used to +extract additional metadata from videos. ffmpegthumbnailer is used only to display thumbnail images as the download occurs. This is a useful feature, and if you can install it, it is recommended. -kaa metadata and ffmpegthumbnailer are optional. The program will run without -them. +hachoir metadata, kaa metadata and ffmpegthumbnailer are optional. The program +will run without them. To start from a fairly basic system, I suggest the following: diff --git a/rapid/config.py b/rapid/config.py index cb77ca6..c621848 100644 --- a/rapid/config.py +++ b/rapid/config.py @@ -15,7 +15,7 @@ ### along with this program; if not, write to the Free Software ### Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -version = '0.3.2' +version = '0.3.3' GCONF_KEY="/apps/rapid-photo-downloader" GLADE_FILE = "glade3/rapid.glade" diff --git a/rapid/media.py b/rapid/media.py index 06ebb2e..3ad09d2 100755 --- a/rapid/media.py +++ b/rapid/media.py @@ -174,6 +174,7 @@ class MediaFile: self.metadata.read() else: self.metadata = videometadata.VideoMetaData(self.fullFileName) + def dateTime(self, alternative_if_date_missing=None): date = None diff --git a/rapid/metadata.py b/rapid/metadata.py index 3077a61..0f3c48d 100755 --- a/rapid/metadata.py +++ b/rapid/metadata.py @@ -92,7 +92,7 @@ def exiv2_version_info(): return __version_info(exiv2_version) RAW_FILE_EXTENSIONS = ['arw', 'dcr', 'cr2', 'crw', 'dng', 'mos', 'mrw', - 'nef', 'orf', 'pef', 'raf', 'raw', 'sr2'] + 'nef', 'orf', 'pef', 'raf', 'raw', 'sr2', 'srw'] #exiv2 0.18.1 introduces support for Panasonic .RW2 files #pyexiv2 in combination with exiv2 0.18 segfaults when trying to read an diff --git a/rapid/rapid.py b/rapid/rapid.py index e2444fb..ded7324 100755 --- a/rapid/rapid.py +++ b/rapid/rapid.py @@ -580,6 +580,7 @@ class ImageRenameTable(tpm.TablePlusMinus): self.getParentAppPrefs() self.getPrefsFactory() + self.prefsFactory.setDownloadStartTime(datetime.datetime.now()) try: self.prefsFactory.checkPrefsForValidity() @@ -1663,7 +1664,7 @@ class CopyPhotos(Thread): self.autoStart = autoStart self.cardMedia = cardMedia - self.initializeDisplay(thread_id, self.cardMedia) + self.initializeDisplay(thread_id, self.cardMedia) self.scanComplete = self.downloadStarted = self.downloadComplete = False @@ -1673,6 +1674,9 @@ class CopyPhotos(Thread): self.scanResultsStale = False # name and subfolder self.scanResultsStaleDownloadFolder = False #download folder only + self.noErrors = self.noWarnings = 0 + self.videoTempWorkingDir = self.photoTempWorkingDir = '' + if DOWNLOAD_VIDEO: self.types_searched_for = _('photos or videos') else: @@ -1729,21 +1733,26 @@ class CopyPhotos(Thread): self.prefs = self.parentApp.prefs #Image and Video filename preferences + sample_download_start_time = datetime.datetime.now() self.imageRenamePrefsFactory = rn.ImageRenamePreferences(self.prefs.image_rename, self, self.fileSequenceLock, sequences) + self.imageRenamePrefsFactory.setDownloadStartTime(sample_download_start_time) checkPrefs(self.imageRenamePrefsFactory) self.videoRenamePrefsFactory = rn.VideoRenamePreferences(self.prefs.video_rename, self, self.fileSequenceLock, sequences) + self.videoRenamePrefsFactory.setDownloadStartTime(sample_download_start_time) checkPrefs(self.videoRenamePrefsFactory) #Image and Video subfolder preferences self.subfolderPrefsFactory = rn.SubfolderPreferences(self.prefs.subfolder, self) + self.subfolderPrefsFactory.setDownloadStartTime(sample_download_start_time) checkPrefs(self.subfolderPrefsFactory) self.videoSubfolderPrefsFactory = rn.VideoSubfolderPreferences(self.prefs.video_subfolder, self) + self.videoSubfolderPrefsFactory.setDownloadStartTime(sample_download_start_time) checkPrefs(self.videoSubfolderPrefsFactory) # copy this variable, as it is used heavily in the loop @@ -1799,15 +1808,14 @@ class CopyPhotos(Thread): return True except: - if notifyOnError: - display_queue.put((media_collection_treeview.removeCard, (self.thread_id, ))) - msg = _("The following download path could not be created:\n") - msg += _("%(path)s: ") % {'path': path} - logError(config.CRITICAL_ERROR, _("Download cannot proceed"), msg) - cmd_line(_("Download cannot proceed")) - cmd_line(msg) - display_queue.put((self.parentApp.downloadFailed, (self.thread_id, ))) - display_queue.close("rw") + display_queue.put((media_collection_treeview.removeCard, (self.thread_id, ))) + msg = _("The following download path could not be created:\n") + msg += _("%(path)s: ") % {'path': path} + logError(config.CRITICAL_ERROR, _("Download cannot proceed"), msg) + cmd_line(_("Download cannot proceed")) + cmd_line(msg) + display_queue.put((self.parentApp.downloadFailed, (self.thread_id, ))) + display_queue.close("rw") return False def getPrefs(notifyOnError): @@ -1896,6 +1904,9 @@ class CopyPhotos(Thread): return (download, isImage, isVideo) def addFile(name, path, size, modificationTime, device, volume, isImage): + #~ if debug_info: + #~ cmd_line("Scanning %s" % name) + if isImage: downloadFolder = self.prefs.download_folder else: @@ -2763,8 +2774,31 @@ class CopyPhotos(Thread): self.running = False self.lock.acquire() self.running = True + + # set download started time + display_queue.put((self.parentApp.setDownloadStartTime, ())) while not all_files_downloaded: + + # set the download start time to be the time that the user clicked the download button, or if on auto start, the value just set + i = 0 + while self.parentApp.download_start_time is None or i > 2: + time.sleep(0.5) + i += 1 + + if self.parentApp.download_start_time: + start_time = self.parentApp.download_start_time + else: + # in a bizarre corner case situation, with mulitple cards of greatly varying size, + # it's possible the start time was set above and then in the meantime unset (very unlikely, but conceivably it could happen) + # fall back to the current time in this less than satisfactory situation + start_time = datetime.datetime.now() + + self.imageRenamePrefsFactory.setDownloadStartTime(start_time) + self.subfolderPrefsFactory.setDownloadStartTime(start_time) + if DOWNLOAD_VIDEO: + self.videoRenamePrefsFactory.setDownloadStartTime(start_time) + self.videoSubfolderPrefsFactory.setDownloadStartTime(start_time) self.noErrors = self.noWarnings = 0 @@ -2776,10 +2810,10 @@ class CopyPhotos(Thread): self.downloadStarted = True cmd_line(_("Download has started from %s") % self.cardMedia.prettyName(limit=0)) + noFiles, sizeFiles, fileIndex = self.cardMedia.sizeAndNumberDownloadPending() cmd_line(_("Attempting to download %s files") % noFiles) - - + no_backup_devices = setupBackup() # include the time it takes to copy to the backup volumes @@ -2813,6 +2847,7 @@ class CopyPhotos(Thread): progressBarText = _("%(number)s of %(total)s %(filetypes)s") % {'number': 0, 'total': noFiles, 'filetypes':self.display_file_types} display_queue.put((media_collection_treeview.updateProgress, (self.thread_id, 0.0, progressBarText, 0))) + while i < noFiles: # if the user pauses the download, then this will be triggered if not self.running: @@ -3680,6 +3715,8 @@ class SelectionTreeView(gtk.TreeView): yield self.liststore.get_iter(path) def add_file(self, mediaFile): + if debug_info: + cmd_line('Adding file %s' % mediaFile.fullFileName) if mediaFile.metadata: date = mediaFile.dateTime() timestamp = mediaFile.metadata.timeStamp(missing=None) @@ -3713,6 +3750,19 @@ class SelectionTreeView(gtk.TreeView): type_icon = self.icon_video status_icon = self.get_status_icon(mediaFile.status) + + if debug_info: + cmd_line('Thumbnail icon: %s' % thumbnail_icon) + cmd_line('Name: %s' % name) + cmd_line('Timestamp: %s' % timestamp) + cmd_line('Date: %s' % date_human_readable) + cmd_line('Size: %s %s' % (size, common.formatSizeForUser(size))) + cmd_line('Is an image: %s' % mediaFile.isImage) + cmd_line('Status: %s' % self.status_human_readable(mediaFile)) + cmd_line('Path: %s' % mediaFile.path) + cmd_line('Device name: %s' % mediaFile.deviceName) + cmd_line('Thread: %s' % mediaFile.thread_id) + cmd_line(' ') iter = self.liststore.append((thumbnail_icon, name, timestamp, date_human_readable, size, common.formatSizeForUser(size), mediaFile.isImage, type_icon, '', mediaFile, status_icon, mediaFile.status, mediaFile.path, mediaFile.deviceName, mediaFile.thread_id)) @@ -3846,12 +3896,17 @@ class SelectionTreeView(gtk.TreeView): self.show_preview(iter) def _refreshNameFactories(self): + sample_download_start_time = datetime.datetime.now() self.imageRenamePrefsFactory = rn.ImageRenamePreferences(self.rapidApp.prefs.image_rename, self, self.rapidApp.fileSequenceLock, sequences) + self.imageRenamePrefsFactory.setDownloadStartTime(sample_download_start_time) self.videoRenamePrefsFactory = rn.VideoRenamePreferences(self.rapidApp.prefs.video_rename, self, self.rapidApp.fileSequenceLock, sequences) + self.videoRenamePrefsFactory.setDownloadStartTime(sample_download_start_time) self.subfolderPrefsFactory = rn.SubfolderPreferences(self.rapidApp.prefs.subfolder, self) + self.subfolderPrefsFactory.setDownloadStartTime(sample_download_start_time) self.videoSubfolderPrefsFactory = rn.VideoSubfolderPreferences(self.rapidApp.prefs.video_subfolder, self) + self.videoSubfolderPrefsFactory.setDownloadStartTime(sample_download_start_time) self.strip_characters = self.rapidApp.prefs.strip_characters @@ -3911,29 +3966,29 @@ class SelectionTreeView(gtk.TreeView): self.videoRenameUsesJobCode = rn.usesJobCode(self.rapidApp.prefs.video_rename) self.videoSubfolderUsesJobCode = rn.usesJobCode(self.rapidApp.prefs.video_subfolder) - def show_preview(self, iter): + + def status_human_readable(self, mediaFile): + if mediaFile.status == STATUS_DOWNLOADED: + v = _('%(filetype)s was downloaded successfully') % {'filetype': mediaFile.displayNameCap} + elif mediaFile.status == STATUS_DOWNLOAD_FAILED: + v = _('%(filetype)s was not downloaded') % {'filetype': mediaFile.displayNameCap} + elif mediaFile.status == STATUS_DOWNLOADED_WITH_WARNING: + v = _('%(filetype)s was downloaded with warnings') % {'filetype': mediaFile.displayNameCap} + elif mediaFile.status == STATUS_BACKUP_PROBLEM: + v = _('%(filetype)s was downloaded but there were problems backing up') % {'filetype': mediaFile.displayNameCap} + elif mediaFile.status == STATUS_DOWNLOAD_AND_BACKUP_FAILED: + v = _('%(filetype)s was neither downloaded nor backed up') % {'filetype': mediaFile.displayNameCap} + elif mediaFile.status == STATUS_NOT_DOWNLOADED: + v = _('%(filetype)s is ready to be downloaded') % {'filetype': mediaFile.displayNameCap} + elif mediaFile.status == STATUS_DOWNLOAD_PENDING: + v = _('%(filetype)s is about to be downloaded') % {'filetype': mediaFile.displayNameCap} + elif mediaFile.status == STATUS_WARNING: + v = _('%(filetype)s will be downloaded with warnings')% {'filetype': mediaFile.displayNameCap} + elif mediaFile.status == STATUS_CANNOT_DOWNLOAD: + v = _('%(filetype)s cannot be downloaded') % {'filetype': mediaFile.displayNameCap} + return v - def status_human_readable(mediaFile): - if mediaFile.status == STATUS_DOWNLOADED: - v = _('%(filetype)s was downloaded successfully') % {'filetype': mediaFile.displayNameCap} - elif mediaFile.status == STATUS_DOWNLOAD_FAILED: - v = _('%(filetype)s was not downloaded') % {'filetype': mediaFile.displayNameCap} - elif mediaFile.status == STATUS_DOWNLOADED_WITH_WARNING: - v = _('%(filetype)s was downloaded with warnings') % {'filetype': mediaFile.displayNameCap} - elif mediaFile.status == STATUS_BACKUP_PROBLEM: - v = _('%(filetype)s was downloaded but there were problems backing up') % {'filetype': mediaFile.displayNameCap} - elif mediaFile.status == STATUS_DOWNLOAD_AND_BACKUP_FAILED: - v = _('%(filetype)s was neither downloaded nor backed up') % {'filetype': mediaFile.displayNameCap} - elif mediaFile.status == STATUS_NOT_DOWNLOADED: - v = _('%(filetype)s is ready to be downloaded') % {'filetype': mediaFile.displayNameCap} - elif mediaFile.status == STATUS_DOWNLOAD_PENDING: - v = _('%(filetype)s is about to be downloaded') % {'filetype': mediaFile.displayNameCap} - elif mediaFile.status == STATUS_WARNING: - v = _('%(filetype)s will be downloaded with warnings')% {'filetype': mediaFile.displayNameCap} - elif mediaFile.status == STATUS_CANNOT_DOWNLOAD: - v = _('%(filetype)s cannot be downloaded') % {'filetype': mediaFile.displayNameCap} - return v - + def show_preview(self, iter): if not iter: # clear everything except the label Preview at the top @@ -4017,7 +4072,7 @@ class SelectionTreeView(gtk.TreeView): self.parentApp.preview_destination_path_label.set_text(mediaFile.downloadPath) self.parentApp.preview_destination_path_label.set_tooltip_text(mediaFile.downloadPath) - status_text = status_human_readable(mediaFile) + status_text = self.status_human_readable(mediaFile) self.parentApp.preview_status_icon.set_from_pixbuf(self.get_status_icon(mediaFile.status, preview=True)) self.parentApp.preview_status_label.set_markup('<b>' + status_text + '</b>') self.parentApp.preview_status_label.set_tooltip_text(status_text) @@ -4708,6 +4763,8 @@ class RapidApp(gnomeglade.GnomeApp, dbus.service.Object): duplicate_files = {} downloaded_files = DownloadedFiles() + self.download_start_time = None + downloadsToday = self.prefs.getAndMaybeResetDownloadsToday() sequences = rn.Sequences(downloadsToday, self.prefs.stored_sequence_no) @@ -5606,6 +5663,9 @@ class RapidApp(gnomeglade.GnomeApp, dbus.service.Object): self.totalDownloadSizeThisRun = self.totalDownloadedSoFarThisRun = 0 # there is no need to clear self.timeRemaining, as when each thread is completed, it removes itself + # this next value is used by the date time option "Download Time" + self.download_start_time = None + global job_code job_code = None @@ -5630,6 +5690,11 @@ class RapidApp(gnomeglade.GnomeApp, dbus.service.Object): for w in workers.getPausedDownloadingWorkers(): w.startStop() self.timeRemaining.setTimeMark(w) + + # set the time that the download started - this is used + # in the "Download Time" date time renaming option. + self.setDownloadStartTime() + #start any new workers that have downloads pending for i in threads: @@ -5638,6 +5703,9 @@ class RapidApp(gnomeglade.GnomeApp, dbus.service.Object): if is_beta and verbose and False: workers.printWorkerStatus() + def setDownloadStartTime(self): + if not self.download_start_time: + self.download_start_time = datetime.datetime.now() def updateOverallProgress(self, thread_id, bytesDownloaded, percentComplete): """ @@ -6268,6 +6336,7 @@ def start (): # Translators: this text is displayed to the user when they request information on the command line options. # The text %default should not be modified or left out. parser.add_option("-v", "--verbose", action="store_true", dest="verbose", help=_("display program information on the command line as the program runs (default: %default)")) + parser.add_option("-d", "--debug", action="store_true", dest="debug", help=_('display debugging information when run from the command line')) parser.add_option("-q", "--quiet", action="store_false", dest="verbose", help=_("only output errors to the command line")) # image file extensions are recognized RAW files plus TIFF and JPG parser.add_option("-e", "--extensions", action="store_true", dest="extensions", help=_("list photo and video file extensions the program recognizes and exit")) @@ -6276,6 +6345,11 @@ def start (): global verbose verbose = options.verbose + global debug_info + debug_info = options.debug + if debug_info: + verbose = True + if verbose: atexit.register(programStatus) @@ -6300,7 +6374,7 @@ def start (): cmd_line(_("Using") + " pyexiv2 " + metadata.version_info()) cmd_line(_("Using") + " exiv2 " + metadata.exiv2_version_info()) if DOWNLOAD_VIDEO: - cmd_line(_("Using") + " kaa " + videometadata.version_info()) + cmd_line(_("Using") + " hachoir " + videometadata.version_info()) else: cmd_line(_("\n" + "Video downloading functionality disabled.\nTo download videos, please install the kaa metadata package for python.") + "\n") diff --git a/rapid/renamesubfolderprefs.py b/rapid/renamesubfolderprefs.py index 727eef0..845f809 100644 --- a/rapid/renamesubfolderprefs.py +++ b/rapid/renamesubfolderprefs.py @@ -98,6 +98,7 @@ IMAGE_DATE = 'Image date' TODAY = 'Today' YESTERDAY = 'Yesterday' VIDEO_DATE = 'Video date' +DOWNLOAD_TIME = 'Download time' # File name NAME_EXTENSION = 'Name + extension' @@ -200,6 +201,8 @@ class i18TranslateMeThanks: _('Video date') _('Today') _('Yesterday') + # Translators: Download time is the time and date that the download started (when the user clicked the Download button) + _('Download time') # Translators: for an explanation of what this means, see http://damonlynch.net/rapid/documentation/index.html#renamefilename _('Name + extension') # Translators: for an explanation of what this means, see http://damonlynch.net/rapid/documentation/index.html#renamefilename @@ -357,13 +360,14 @@ LIST_SHUTTER_COUNT_L2 = [ ] # Level 1 -LIST_DATE_TIME_L1 = [IMAGE_DATE, TODAY, YESTERDAY] -LIST_VIDEO_DATE_TIME_L1 = [VIDEO_DATE, TODAY, YESTERDAY] +LIST_DATE_TIME_L1 = [IMAGE_DATE, TODAY, YESTERDAY, DOWNLOAD_TIME] +LIST_VIDEO_DATE_TIME_L1 = [VIDEO_DATE, TODAY, YESTERDAY, DOWNLOAD_TIME] DICT_DATE_TIME_L1 = { IMAGE_DATE: LIST_IMAGE_DATE_TIME_L2, TODAY: LIST_DATE_TIME_L2, YESTERDAY: LIST_DATE_TIME_L2, + DOWNLOAD_TIME: LIST_DATE_TIME_L2, ORDER_KEY: LIST_DATE_TIME_L1 } @@ -371,6 +375,7 @@ VIDEO_DICT_DATE_TIME_L1 = { VIDEO_DATE: LIST_IMAGE_DATE_TIME_L2, TODAY: LIST_DATE_TIME_L2, YESTERDAY: LIST_DATE_TIME_L2, + DOWNLOAD_TIME: LIST_DATE_TIME_L2, ORDER_KEY: LIST_VIDEO_DATE_TIME_L1 } @@ -918,10 +923,11 @@ class ImageRenamePreferences: return v - def setJobCode(self, job_code): + def setJobCode(self, job_code): self.job_code = job_code - + def setDownloadStartTime(self, download_start_time): + self.download_start_time = download_start_time def _getDateComponent(self): """ @@ -946,6 +952,8 @@ class ImageRenamePreferences: elif self.L1 == YESTERDAY: delta = datetime.timedelta(days = 1) d = datetime.datetime.now() - delta + elif self.L1 == DOWNLOAD_TIME: + d = self.download_start_time else: raise("Date options invalid") @@ -1405,7 +1413,7 @@ def getVideoMetadataComponent(video): elif video.L1 == HEIGHT: v = video.metadata.height() elif video.L1 == FPS: - v = video.metadata.fps() + v = video.metadata.framesPerSecond() elif video.L1 == LENGTH: v = video.metadata.length() else: @@ -1416,7 +1424,7 @@ def getVideoMetadataComponent(video): elif video.L2 == LOWERCASE: v = v.lower() if not v: - self.problem.add_problem(self.component, pn.MISSING_METADATA, _(video.L1)) + video.problem.add_problem(video.component, pn.MISSING_METADATA, _(video.L1)) return v class VideoRenamePreferences(ImageRenamePreferences): diff --git a/rapid/videometadata.py b/rapid/videometadata.py index ab5d18c..77f6791 100755 --- a/rapid/videometadata.py +++ b/rapid/videometadata.py @@ -21,6 +21,7 @@ DOWNLOAD_VIDEO = True import os import datetime +import time import subprocess import tempfile @@ -31,11 +32,14 @@ from filmstrip import add_filmstrip try: import kaa.metadata + from hachoir_core.cmd_line import unicodeFilename + from hachoir_parser import createParser + from hachoir_metadata import extractMetadata except ImportError: DOWNLOAD_VIDEO = False VIDEO_THUMBNAIL_FILE_EXTENSIONS = ['thm'] -VIDEO_FILE_EXTENSIONS = ['avi', 'mov', 'mp4', 'mpg'] +VIDEO_FILE_EXTENSIONS = ['3gp', 'avi', 'm2t', 'mov', 'mp4', 'mpeg','mpg', 'mod', 'tod'] @@ -50,7 +54,8 @@ if DOWNLOAD_VIDEO: def version_info(): - return str(kaa.metadata.VERSION) + from hachoir_metadata.version import VERSION + return VERSION def get_video_THM_file(fullFileName): """ @@ -74,62 +79,81 @@ if DOWNLOAD_VIDEO: class VideoMetaData(): def __init__(self, filename): - self.info = kaa.metadata.parse(filename) + """ + Initialize by loading metadata using hachoir + """ + self.filename = filename + self.u_filename = unicodeFilename(filename) + self.parser = createParser(self.u_filename, self.filename) + self.metadata = extractMetadata(self.parser) - def rpd_keys(self): - return self.info.keys() - def _get(self, key, missing, stream=None): - if stream != None: - v = self.info['video'][stream][key] + def _kaa_get(self, key, missing, stream=None): + if not hasattr(self, 'info'): + self.info = kaa.metadata.parse(self.filename) + if self.info: + if stream != None: + v = self.info['video'][stream][key] + else: + v = self.info[key] else: - v = self.info[key] + v = None if v: return str(v) else: - return missing + return missing + + def _get(self, key, missing): + try: + v = self.metadata.get(key) + except: + v = missing + return v def dateTime(self, missing=''): - dt = self._get('timestamp', missing=None) - if dt: - try: - return datetime.datetime.fromtimestamp(self.info['timestamp']) - except: - return missing - else: - return missing + return self._get('creation_date', missing) def timeStamp(self, missing=''): """ Returns a float value representing the time stamp, if it exists """ - v = self._get('timestamp', missing=missing) - try: - v = float(v) - except: + dt = self.dateTime(missing=None) + if dt: + # convert it to a timestamp (not optimal, but better than nothing!) + v = time.mktime(dt.timetuple()) + else: v = missing return v def codec(self, stream=0, missing=''): - return self._get('codec', missing, stream) + return self._kaa_get('codec', missing, stream) def length(self, missing=''): - l = self._get('length', missing) - try: - l = '%.0f' % float(l) - except: - pass + """ + return the duration (length) of the video, rounded to the nearest second, in string format + """ + delta = self.metadata.get('duration') + l = '%.0f' % (86400 * delta.days + delta.seconds + float('.%s' % delta.microseconds)) return l - def width(self, stream=0, missing=''): - return self._get('width', missing, stream) - def height(self, stream=0, missing=''): - return self._get('height', missing, stream) + def width(self, missing=''): + v = self._get('width', missing) + if v != None: + return str(v) + else: + return None + + def height(self, missing=''): + v = self._get('height', missing) + if v != None: + return str(v) + else: + return None def framesPerSecond(self, stream=0, missing=''): - fps = self._get('fps', missing, stream) + fps = self._kaa_get('fps', missing, stream) try: fps = '%.0f' % float(fps) except: @@ -137,7 +161,7 @@ if DOWNLOAD_VIDEO: return fps def fourcc(self, stream=0, missing=''): - return self._get('fourcc', missing, stream) + return self._kaa_get('fourcc', missing, stream) def getThumbnailData(self, size, tempWorkingDir): """ |