summaryrefslogtreecommitdiff
path: root/rapid/media.py
diff options
context:
space:
mode:
Diffstat (limited to 'rapid/media.py')
-rwxr-xr-xrapid/media.py216
1 files changed, 189 insertions, 27 deletions
diff --git a/rapid/media.py b/rapid/media.py
index d00855a..06ebb2e 100755
--- a/rapid/media.py
+++ b/rapid/media.py
@@ -1,7 +1,7 @@
#!/usr/bin/python
# -*- coding: latin1 -*-
-### Copyright (C) 2007 Damon Lynch <damonlynch@gmail.com>
+### Copyright (C) 2007, 2008, 2009, 2010 Damon Lynch <damonlynch@gmail.com>
### This program is free software; you can redistribute it and/or modify
### it under the terms of the GNU General Public License as published by
@@ -18,15 +18,30 @@
### Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
import os
+import sys
+import types
+import datetime
+import subprocess
import config
+from config import MAX_THUMBNAIL_SIZE
+from config import STATUS_NOT_DOWNLOADED, \
+ STATUS_DOWNLOAD_PENDING, \
+ STATUS_CANNOT_DOWNLOAD
+
+
import common
import metadata
import videometadata
+from common import Configi18n
+global _
+_ = Configi18n._
+
import operator
+import gtk
-def _getDefaultLocation(options, ignore_missing_dir=False):
+def _getDefaultLocationLegacy(options, ignore_missing_dir=False):
if ignore_missing_dir:
return common.getFullPath(options[0])
for default in options:
@@ -34,12 +49,29 @@ def _getDefaultLocation(options, ignore_missing_dir=False):
if os.path.isdir(path):
return path
return common.getFullPath('')
+
+def _getDefaultLocationXDG(dir_type):
+ proc = subprocess.Popen(['xdg-user-dir', dir_type], stdout=subprocess.PIPE)
+ output = proc.communicate()[0].strip()
+ return output
def getDefaultPhotoLocation(ignore_missing_dir=False):
- return _getDefaultLocation(config.DEFAULT_PHOTO_LOCATIONS, ignore_missing_dir)
+ try:
+ return _getDefaultLocationXDG('PICTURES')
+ except:
+ return _getDefaultLocationLegacy(config.DEFAULT_PHOTO_LOCATIONS, ignore_missing_dir)
def getDefaultVideoLocation(ignore_missing_dir=False):
- return _getDefaultLocation(config.DEFAULT_VIDEO_LOCATIONS, ignore_missing_dir)
+ try:
+ return _getDefaultLocationXDG('VIDEOS')
+ except:
+ return _getDefaultLocationLegacy(config.DEFAULT_VIDEO_LOCATIONS, ignore_missing_dir)
+
+def getDefaultBackupPhotoIdentifier():
+ return os.path.split(getDefaultPhotoLocation(ignore_missing_dir = True))[1]
+
+def getDefaultBackupVideoIdentifier():
+ return os.path.split(getDefaultVideoLocation(ignore_missing_dir = True))[1]
def is_DCIM_Media(path):
""" Returns true if directory specifies some media with photos on it """
@@ -79,25 +111,119 @@ def isVideo(fileName):
ext = os.path.splitext(fileName)[1].lower()[1:]
return (ext in videometadata.VIDEO_FILE_EXTENSIONS)
-def getVideoThumbnailFile(fullFileName):
+
+class MediaFile:
"""
- Checks to see if a thumbnail file is in the same directory as the
- file. Expects a full path to be part of the file name.
-
- Returns the filename, including path, if found, else returns None.
+ A photo or video file, with metadata
"""
- f = None
- name, ext = os.path.splitext(fullFileName)
- for e in videometadata.VIDEO_THUMBNAIL_FILE_EXTENSIONS:
- if os.path.exists(name + '.' + e):
- f = name + '.' + e
- break
- if os.path.exists(name + '.' + e.upper()):
- f = name + '.' + e.upper()
- break
+ def __init__(self, thread_id, name, path, size, fileSystemModificationTime, deviceName, downloadFolder, volume, isPhoto = True):
+ self.thread_id = thread_id
+ self.path = path
+ self.name = name
+ self.fullFileName = os.path.join(path, name)
+ self.size = size # type int
+ self.modificationTime = fileSystemModificationTime
+ self.deviceName = deviceName
+ self.downloadFolder = downloadFolder
+ self.volume = volume
+
+ self.jobcode = ''
- return f
+ # a reference into the SelectionTreeView's liststore
+ self.treerowref = None
+
+ # generated values
+ self.downloadSubfolder = ''
+ self.downloadPath = ''
+ self.downloadName = ''
+ self.downloadFullFileName = ''
+
+ self.isImage = isPhoto
+ self.isVideo = not self.isImage
+ if isPhoto:
+ self.displayName = _("photo")
+ self.displayNameCap = _("Photo")
+ else:
+ self.displayName = _("video")
+ self.displayNameCap = _("Video")
+
+
+ self.metadata = None
+ self.thumbnail = None
+ self.genericThumbnail = False
+ self.sampleName = ''
+ self.sampleSubfolder = ''
+ self.samplePath = ''
+
+ # whether the sample genereated name, subfolder and path need to be refreshed in a preview
+ self.sampleStale = False
+
+ self.status = STATUS_NOT_DOWNLOADED
+ self.problem = None # class Problem in problemnotifcation.py
+
+ def loadMetadata(self):
+ """
+ Attempt to load the metadata for the photo or video
+
+ Raises errors if unable to be loaded
+ """
+ if not self.metadata:
+ if self.isImage:
+ self.metadata = metadata.MetaData(self.fullFileName)
+ self.metadata.read()
+ else:
+ self.metadata = videometadata.VideoMetaData(self.fullFileName)
+
+ def dateTime(self, alternative_if_date_missing=None):
+ date = None
+ if self.metadata:
+ date = self.metadata.dateTime()
+ if not date:
+ if alternative_if_date_missing:
+ date = alternative_if_date_missing
+ else:
+ date = datetime.datetime.fromtimestamp(self.modificationTime)
+ return date
+
+
+ def generateThumbnail(self, tempWorkingDir):
+ """
+ Attempts to generate or extract a thumnail and its orientation for the photo or video
+ """
+ if self.metadata is None:
+ sys.stderr.write("metadata should not be empty!")
+ else:
+ if self.isImage:
+ try:
+ thumbnail = self.metadata.getThumbnailData(MAX_THUMBNAIL_SIZE)
+ if not isinstance(thumbnail, types.StringType):
+ self.thumbnail = None
+ else:
+ orientation = self.metadata.orientation(missing=None)
+ pbloader = gtk.gdk.PixbufLoader()
+ pbloader.write(thumbnail)
+ pbloader.close()
+ # Get the resulting pixbuf and build an image to be displayed
+ pixbuf = pbloader.get_pixbuf()
+ if orientation == 8:
+ pixbuf = pixbuf.rotate_simple(gtk.gdk.PIXBUF_ROTATE_COUNTERCLOCKWISE)
+ elif orientation == 6:
+ pixbuf = pixbuf.rotate_simple(gtk.gdk.PIXBUF_ROTATE_CLOCKWISE)
+ elif orientation == 3:
+ pixbuf = pixbuf.rotate_simple(gtk.gdk.PIXBUF_ROTATE_UPSIDEDOWN)
+
+ self.thumbnail = pixbuf
+ except:
+ pass
+ else:
+ # get thumbnail of video
+ # it may need to be generated
+ self.thumbnail = self.metadata.getThumbnailData(MAX_THUMBNAIL_SIZE, tempWorkingDir)
+ if self.thumbnail:
+ # scale to size
+ self.thumbnail = common.scale2pixbuf(MAX_THUMBNAIL_SIZE, MAX_THUMBNAIL_SIZE, self.thumbnail)
+
class Media:
@@ -138,7 +264,7 @@ class Media:
class CardMedia(Media):
"""Compact Flash cards, hard drives, etc."""
- def __init__(self, path, volume = None, doNotScan=True):
+ def __init__(self, path, volume = None):
"""
volume is a gnomevfs or gio volume, see class Volume in rapid.py
"""
@@ -146,7 +272,7 @@ class CardMedia(Media):
def setMedia(self, imagesAndVideos, fileSizeSum, noFiles):
- self.imagesAndVideos = imagesAndVideos
+ self.imagesAndVideos = imagesAndVideos # class MediaFile
self.fileSizeSum = fileSizeSum
self.noFiles = noFiles
@@ -158,19 +284,55 @@ class CardMedia(Media):
return common.formatSizeForUser(self.fileSizeSum)
else:
return self.fileSizeSum
-
- def _firstFile(self, function_to_check):
+
+ def sizeAndNumberDownloadPending(self):
+ """
+ Returns how many files have their status set to download pending, and their size
+ """
+ v = s = 0
+ fileIndex = []
+ for i in range(len(self.imagesAndVideos)):
+ mediaFile = self.imagesAndVideos[i][0]
+ if mediaFile.status == STATUS_DOWNLOAD_PENDING:
+ v += 1
+ s += mediaFile.size
+ fileIndex.append(i)
+ return (v, s, fileIndex)
+
+ def numberOfFilesNotCannotDownload(self):
+ """
+ Returns how many files whose status is not cannot download
+ """
+ v = 0
+ for i in range(len(self.imagesAndVideos)):
+ mediaFile = self.imagesAndVideos[i][0]
+ if mediaFile.status <> STATUS_CANNOT_DOWNLOAD:
+ v += 1
+
+ return v
+ def downloadPending(self):
+ """
+ Returns true if there a mediaFile with status download pending on the device.
+ Inefficient. Not currently used.
+ """
+ for i in range(len(self.imagesAndVideos)):
+ mediaFile = self.imagesAndVideos[i][0]
+ if mediaFile.status == config.STATUS_DOWNLOAD_PENDING:
+ return True
+ return False
+
+ def _firstFile(self, isImage):
if self.imagesAndVideos:
for i in range(len(self.imagesAndVideos)):
- if function_to_check(self.imagesAndVideos[i][0]):
- return self.imagesAndVideos[i]
+ if self.imagesAndVideos[i][0].isImage == isImage:
+ return self.imagesAndVideos[i][0]
else:
return None
def firstImage(self):
- return self._firstFile(isImage)
+ return self._firstFile(True)
def firstVideo(self):
- return self._firstFile(isVideo)
+ return self._firstFile(False)