summaryrefslogtreecommitdiff
path: root/rapid
diff options
context:
space:
mode:
Diffstat (limited to 'rapid')
-rw-r--r--rapid/ChangeLog32
-rw-r--r--rapid/INSTALL8
-rw-r--r--rapid/config.py2
-rwxr-xr-xrapid/media.py1
-rwxr-xr-xrapid/metadata.py2
-rwxr-xr-xrapid/rapid.py146
-rw-r--r--rapid/renamesubfolderprefs.py20
-rwxr-xr-xrapid/videometadata.py92
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):
"""