summaryrefslogtreecommitdiff
path: root/raphodo
diff options
context:
space:
mode:
Diffstat (limited to 'raphodo')
-rw-r--r--raphodo/__about__.py2
-rwxr-xr-xraphodo/backupfile.py2
-rwxr-xr-xraphodo/copyfiles.py8
-rw-r--r--raphodo/generatename.py16
-rw-r--r--raphodo/newversion.py10
-rwxr-xr-xraphodo/rapid.py34
-rwxr-xr-xraphodo/renameandmovefile.py39
-rw-r--r--raphodo/rpdfile.py23
-rwxr-xr-xraphodo/scan.py35
-rw-r--r--raphodo/thumbnaildisplay.py2
-rwxr-xr-xraphodo/thumbnailextractor.py6
11 files changed, 143 insertions, 34 deletions
diff --git a/raphodo/__about__.py b/raphodo/__about__.py
index 1c2dd96..f000424 100644
--- a/raphodo/__about__.py
+++ b/raphodo/__about__.py
@@ -29,7 +29,7 @@ __summary__ = 'Downloads, renames and backs up photos and videos from cameras, p
'memory cards and other devices'
__uri__ = 'http://www.damonlynch.net/rapid'
-__version__ = '0.9.0'
+__version__ = '0.9.1'
__author__ = 'Damon Lynch'
__email__ = 'damonlynch@gmail.com'
diff --git a/raphodo/backupfile.py b/raphodo/backupfile.py
index f16842d..e83fdd7 100755
--- a/raphodo/backupfile.py
+++ b/raphodo/backupfile.py
@@ -209,6 +209,8 @@ class BackupFilesWorker(WorkerInPublishPullPipeline, FileCopy):
self.backup_associate_file(dest_dir, rpd_file.download_audio_full_name)
if rpd_file.download_xmp_full_name:
self.backup_associate_file(dest_dir, rpd_file.download_xmp_full_name)
+ if rpd_file.download_log_full_name:
+ self.backup_associate_file(dest_dir, rpd_file.download_log_full_name)
self.total_downloaded += rpd_file.size
bytes_not_downloaded = rpd_file.size - self.amount_downloaded
diff --git a/raphodo/copyfiles.py b/raphodo/copyfiles.py
index 41e9ee2..eaf4908 100755
--- a/raphodo/copyfiles.py
+++ b/raphodo/copyfiles.py
@@ -488,9 +488,15 @@ class CopyFilesWorker(WorkerInPublishPullPipeline, FileCopy):
rpd_file, temp_name, dest_dir, rpd_file.xmp_file_full_name, 'XMP'
)
+ # copy magic lantern LOG file if there is one
+ if rpd_file.log_file_full_name:
+ rpd_file.temp_log_full_name = self.copy_associate_file(
+ rpd_file, temp_name, dest_dir, rpd_file.log_file_full_name, 'LOG'
+ )
+
download_count = idx + 1
- self.content = pickle.dumps(
+ self.content = pickle.dumps(
CopyFilesResults(
copy_succeeded=copy_succeeded,
rpd_file=rpd_file,
diff --git a/raphodo/generatename.py b/raphodo/generatename.py
index 7723995..79ad2fc 100644
--- a/raphodo/generatename.py
+++ b/raphodo/generatename.py
@@ -206,7 +206,7 @@ class NameGeneration:
# else keep extension case the same as what it originally was
return extension
- def _get_thm_extension(self):
+ def _get_thm_extension(self) -> None:
"""
Generates THM extension with correct capitalization, if needed
"""
@@ -214,7 +214,7 @@ class NameGeneration:
self.rpd_file.thm_full_name
)
- def _get_audio_extension(self):
+ def _get_audio_extension(self) -> None:
"""
Generates audio extension with correct capitalization, if needed
e.g. WAV or wav
@@ -223,7 +223,7 @@ class NameGeneration:
self.rpd_file.audio_file_full_name
)
- def _get_xmp_extension(self):
+ def _get_xmp_extension(self) -> None:
"""
Generates XMP extension with correct capitalization, if needed.
"""
@@ -232,6 +232,15 @@ class NameGeneration:
self.rpd_file.xmp_file_full_name
)
+ def _get_log_extension(self) -> None:
+ """
+ Generates LOG extension with correct capitalization, if needed.
+ """
+
+ self.rpd_file.log_extension = self._get_associated_file_extension(
+ self.rpd_file.log_file_full_name
+ )
+
def _get_filename_component(self):
"""
Returns portion of new file / subfolder name based on the file name
@@ -487,6 +496,7 @@ class NameGeneration:
self._get_thm_extension()
self._get_audio_extension()
self._get_xmp_extension()
+ self._get_log_extension()
name = self._filter_name(name, parts)
diff --git a/raphodo/newversion.py b/raphodo/newversion.py
index b448ba6..5f4f05b 100644
--- a/raphodo/newversion.py
+++ b/raphodo/newversion.py
@@ -99,7 +99,9 @@ class NewVersion(QObject):
:return: True if installed via pip, else False
"""
- command_line = '{} -m pip show --verbose {}'.format(sys.executable, package)
+ command_line = '{} -m pip show --disable-pip-version-check --verbose {}'.format(
+ sys.executable, package
+ )
args = shlex.split(command_line)
try:
pip_output = subprocess.check_output(args)
@@ -122,16 +124,14 @@ class NewVersion(QObject):
status_code = r.status_code
success = status_code == 200
if not success:
- logging.debug("Got error code %d while accessing versions file", self.status_code)
- self._reset_values()
+ logging.debug("Got error code %d while accessing versions file", status_code)
self.status_code = r.status_code
else:
try:
self.version = r.json()
except:
- logging.error("Error accessing versions JSON file", self.status_code)
+ logging.error("Error %d accessing versions JSON file", status_code)
success = False
- self._reset_values()
self.status_code = r.status_code
else:
stable = self.version['stable']
diff --git a/raphodo/rapid.py b/raphodo/rapid.py
index 0364fc0..6d0cf05 100755
--- a/raphodo/rapid.py
+++ b/raphodo/rapid.py
@@ -730,6 +730,7 @@ class RapidWindow(QMainWindow):
# For meaning of 'Devices', see devices.py
self.devices = DeviceCollection(self.exiftool_process, self)
+ self.backup_devices = BackupDeviceCollection(rapidApp=self)
logging.debug("Starting thumbnail daemon model")
@@ -826,8 +827,11 @@ class RapidWindow(QMainWindow):
logging.warning("Desktop environment is Unity, but could not load Unity 7.0 module")
else:
# Unity auto-generated desktop files use underscores, it seems
- for launcher in ('rapid_photo_downloader.desktop',
- 'rapid-photo-downloader.desktop'):
+ launchers = (
+ 'net.damonlynch.rapid-photo-downloader.desktop',
+ 'net.damonlynch.rapid_photo_downloader.desktop',
+ )
+ for launcher in launchers:
desktop_launcher = Unity.LauncherEntry.get_for_desktop_id(launcher)
if desktop_launcher is not None:
self.desktop_launchers.append(desktop_launcher)
@@ -839,6 +843,13 @@ class RapidWindow(QMainWindow):
else:
logging.debug("Unity progress indicator found")
+ self.createPathViews()
+
+ self.createActions()
+ logging.debug("Laying out main window")
+ self.createMenus()
+ self.createLayoutAndButtons(centralWidget)
+
logging.debug("Have GIO module: %s", have_gio)
self.gvfsControlsMounts = gvfs_controls_mounts() and have_gio
if have_gio:
@@ -882,13 +893,6 @@ class RapidWindow(QMainWindow):
self.gvolumeMonitor.volumeAddedNoAutomount.connect(self.noGVFSAutoMount)
self.gvolumeMonitor.cameraPossiblyRemoved.connect(self.cameraRemoved)
- self.createPathViews()
-
- self.createActions()
- logging.debug("Laying out main window")
- self.createMenus()
- self.createLayoutAndButtons(centralWidget)
-
logging.debug("Starting version check")
self.newVersion = NewVersion(self)
self.newVersionThread = QThread()
@@ -1031,8 +1035,6 @@ class RapidWindow(QMainWindow):
self.splash.setProgress(80)
- self.backup_devices = BackupDeviceCollection(rapidApp=self)
-
self.backupThread = QThread()
self.backupmq = BackupManager(logging_port=self.logging_port)
@@ -1326,19 +1328,19 @@ class RapidWindow(QMainWindow):
if current_version < stable_version.version:
self.latest_version = stable_version
- if check_dev_version and current_version < dev_version.version:
+ if check_dev_version and (
+ current_version < dev_version.version or
+ current_version < stable_version.version
+ ):
if dev_version.version > stable_version.version:
self.latest_version = dev_version
else:
self.latest_version = stable_version
- # remove in development testing code if in production!
if (
self.latest_version is not None and str(self.latest_version.version) not in
self.prefs.ignore_versions
- ): # or True:
-
- self.latest_version = dev_version
+ ):
version = str(self.latest_version.version)
changelog_url = self.latest_version.changelog_url
diff --git a/raphodo/renameandmovefile.py b/raphodo/renameandmovefile.py
index f123308..2da31cc 100755
--- a/raphodo/renameandmovefile.py
+++ b/raphodo/renameandmovefile.py
@@ -499,6 +499,42 @@ class RenameMoveFileWorker(DaemonProcess):
logging.error("Failed to move file's associated XMP file %s",
rpd_file.download_xmp_full_name)
+ def move_log_file(self, rpd_file: Union[Photo, Video]) -> None:
+ """
+ Move (rename) the associate XMP file using the pre-generated
+ name
+ """
+
+ try:
+ if rpd_file.log_extension:
+ ext = rpd_file.log_extension
+ else:
+ ext = '.LOG'
+ except AttributeError:
+ ext = '.LOG'
+
+ try:
+ rpd_file.download_log_full_name = self._move_associate_file(
+ extension=ext,
+ full_base_name=rpd_file.download_full_base_name,
+ temp_associate_file=rpd_file.temp_log_full_name
+ )
+ except (OSError, FileNotFoundError) as e:
+ self.problems.append(
+ RenamingAssociateFileProblem(
+ source=make_href(
+ name=os.path.basename(rpd_file.download_log_full_name),
+ uri=get_uri(
+ full_file_name=rpd_file.download_log_full_name,
+ camera_details=rpd_file.camera_details
+ )
+ ),
+ exception=e
+ )
+ )
+ logging.error("Failed to move file's associated LOG file %s",
+ rpd_file.download_log_full_name)
+
def check_for_fatal_name_generation_errors(self, rpd_file: Union[Photo, Video]) -> bool:
"""
:return False if either the download subfolder or filename are
@@ -759,6 +795,9 @@ class RenameMoveFileWorker(DaemonProcess):
if rpd_file.temp_xmp_full_name:
self.move_xmp_file(rpd_file)
+ if rpd_file.temp_log_full_name:
+ self.move_log_file(rpd_file)
+
return move_succeeded
def run(self) -> None:
diff --git a/raphodo/rpdfile.py b/raphodo/rpdfile.py
index 34898ff..055018a 100644
--- a/raphodo/rpdfile.py
+++ b/raphodo/rpdfile.py
@@ -47,8 +47,6 @@ import raphodo.metadatavideo as metadatavideo
from raphodo.utilities import thousands, make_internationalized_list, datetime_roughly_equal
from raphodo.problemnotification import Problem, make_href
-import raphodo.problemnotification as pn
-
RAW_EXTENSIONS = ['arw', 'dcr', 'cr2', 'crw', 'dng', 'mos', 'mef', 'mrw',
'nef', 'nrw', 'orf', 'pef', 'raf', 'raw', 'rw2', 'sr2',
@@ -76,7 +74,7 @@ VIDEO_EXTENSIONS = ['3gp', 'avi', 'm2t', 'm2ts', 'mov', 'mp4', 'mpeg','mpg', 'mo
VIDEO_THUMBNAIL_EXTENSIONS = ['thm']
-ALL_USER_VISIBLE_EXTENSIONS = PHOTO_EXTENSIONS + VIDEO_EXTENSIONS + ['xmp']
+ALL_USER_VISIBLE_EXTENSIONS = PHOTO_EXTENSIONS + VIDEO_EXTENSIONS + ['xmp', 'log']
ALL_KNOWN_EXTENSIONS = ALL_USER_VISIBLE_EXTENSIONS + AUDIO_EXTENSIONS + VIDEO_THUMBNAIL_EXTENSIONS
@@ -84,7 +82,7 @@ MUST_CACHE_VIDEOS = [video for video in VIDEO_EXTENSIONS
if thumbnail_offset.get(video) is None]
-def file_type(file_extension: str) -> FileType:
+def file_type(file_extension: str) -> Optional[FileType]:
"""
Returns file type (photo/video), or None if it's neither.
Checks only the file's extension
@@ -147,6 +145,7 @@ def get_rpdfile(name: str,
thm_full_name: Optional[str],
audio_file_full_name: Optional[str],
xmp_file_full_name: Optional[str],
+ log_file_full_name: Optional[str],
scan_id: bytes,
file_type: FileType,
from_camera: bool,
@@ -172,6 +171,7 @@ def get_rpdfile(name: str,
thm_full_name=thm_full_name,
audio_file_full_name=audio_file_full_name,
xmp_file_full_name=xmp_file_full_name,
+ log_file_full_name=log_file_full_name,
scan_id=scan_id,
from_camera=from_camera,
camera_details=camera_details,
@@ -194,6 +194,7 @@ def get_rpdfile(name: str,
thm_full_name=thm_full_name,
audio_file_full_name=audio_file_full_name,
xmp_file_full_name=xmp_file_full_name,
+ log_file_full_name=log_file_full_name,
scan_id=scan_id,
from_camera=from_camera,
camera_details=camera_details,
@@ -356,6 +357,7 @@ class RPDFile:
thm_full_name: Optional[str],
audio_file_full_name: Optional[str],
xmp_file_full_name: Optional[str],
+ log_file_full_name: Optional[str],
scan_id: bytes,
from_camera: bool,
never_read_mdatatime: bool,
@@ -387,6 +389,8 @@ class RPDFile:
audio file
:param xmp_file_full_name: name and path of any associated XMP
file
+ :param log_file_full_name: name and path of any associated LOG
+ file
:param scan_id: id of the scan
:param from_camera: whether the file is being downloaded from a
camera
@@ -516,6 +520,8 @@ class RPDFile:
self.audio_file_full_name = audio_file_full_name
self.xmp_file_full_name = xmp_file_full_name
+ # log files: see https://wiki.magiclantern.fm/userguide#movie_logging
+ self.log_file_full_name = log_file_full_name
self.status = DownloadStatus.not_downloaded
self.problem = problem
@@ -547,6 +553,7 @@ class RPDFile:
self.temp_thm_full_name = ''
self.temp_audio_full_name = ''
self.temp_xmp_full_name = ''
+ self.temp_log_full_name = ''
self.temp_cache_full_file_chunk = ''
self.download_start_time = None
@@ -559,8 +566,14 @@ class RPDFile:
self.download_full_base_name = '' # filename with path but no extension
self.download_thm_full_name = '' # name of THM (thumbnail) file with path
self.download_xmp_full_name = '' # name of XMP sidecar with path
+ self.download_log_full_name = '' # name of LOG associate file with path
self.download_audio_full_name = '' # name of the WAV or MP3 audio file with path
+ self.thm_extension = ''
+ self.audio_extension = ''
+ self.xmp_extension = ''
+ self.log_extension = ''
+
self.metadata = None # type: Optional[Union[metadataphoto.MetaData, metadatavideo.MetaData]]
self.metadata_failure = False # type: bool
@@ -885,6 +898,7 @@ class SamplePhoto(Photo):
thm_full_name=None,
audio_file_full_name=None,
xmp_file_full_name=None,
+ log_file_full_name=None,
scan_id=b'0',
from_camera=False,
never_read_mdatatime=False,
@@ -912,6 +926,7 @@ class SampleVideo(Video):
thm_full_name=None,
audio_file_full_name=None,
xmp_file_full_name=None,
+ log_file_full_name=None,
scan_id=b'0',
from_camera=False,
never_read_mdatatime=False,
diff --git a/raphodo/scan.py b/raphodo/scan.py
index 1a6de86..3d78198 100755
--- a/raphodo/scan.py
+++ b/raphodo/scan.py
@@ -256,6 +256,7 @@ class ScanWorker(WorkerInPublishPullPipeline):
self._camera_audio_files = defaultdict(list)
self._camera_video_thumbnails = defaultdict(list)
self._camera_xmp_files = defaultdict(list)
+ self._camera_log_files = defaultdict(list)
self._folder_identifiers = {}
self._folder_identifers_for_file = \
defaultdict(list) # type: DefaultDict[int, List[int]]
@@ -496,6 +497,8 @@ class ScanWorker(WorkerInPublishPullPipeline):
self._camera_video_thumbnails[base_name].append((path, ext))
elif ext_lower == 'xmp':
self._camera_xmp_files[base_name].append((path, ext))
+ elif ext_lower == 'log':
+ self._camera_log_files[base_name].append((path, ext))
else:
logging.info("Ignoring unknown file %s on %s",
os.path.join(path, name), self.display_name)
@@ -644,6 +647,9 @@ class ScanWorker(WorkerInPublishPullPipeline):
# check if an XMP file is associated with the photo or video
xmp_file_full_name = self.get_xmp_file(base_name, camera_file)
+ # check if a Magic Lantern LOG file is associated with the video
+ log_file_full_name = self.get_log_file(base_name, camera_file)
+
# check if an audio file is associated with the photo or video
audio_file_full_name = self.get_audio_file(base_name, camera_file)
@@ -705,6 +711,7 @@ class ScanWorker(WorkerInPublishPullPipeline):
thm_full_name=thm_full_name,
audio_file_full_name=audio_file_full_name,
xmp_file_full_name=xmp_file_full_name,
+ log_file_full_name=log_file_full_name,
scan_id=self.worker_id,
file_type=file_type,
from_camera=self.download_from_camera,
@@ -794,6 +801,7 @@ class ScanWorker(WorkerInPublishPullPipeline):
thm_full_name=None,
audio_file_full_name=None,
xmp_file_full_name=None,
+ log_file_full_name=None,
scan_id=self.worker_id,
file_type=file_type,
from_camera=self.download_from_camera,
@@ -1139,10 +1147,25 @@ class ScanWorker(WorkerInPublishPullPipeline):
else:
return self._get_associate_file(base_name, rpdfile.AUDIO_EXTENSIONS)
+ def get_log_file(self, base_name: str, camera_file: CameraFile) -> Optional[str]:
+ """
+ Checks to see if an XMP file with the same base name
+ is in the same directory as the file.
+
+ :param base_name: the file name without the extension
+ :return: filename, including path, if found, else returns None
+ """
+ if self.download_from_camera:
+ return self._get_associate_file_from_camera(
+ base_name, self._camera_log_files, camera_file
+ )
+ else:
+ return self._get_associate_file(base_name, ['log'])
+
def get_xmp_file(self, base_name: str, camera_file: CameraFile) -> Optional[str]:
"""
Checks to see if an XMP file with the same base name
- is in the same directory as tthe file.
+ is in the same directory as the file.
:param base_name: the file name without the extension
:return: filename, including path, if found, else returns None
@@ -1152,14 +1175,20 @@ class ScanWorker(WorkerInPublishPullPipeline):
base_name, self._camera_xmp_files, camera_file
)
else:
- return self._get_associate_file(base_name, ['XMP'])
+ return self._get_associate_file(base_name, ['xmp'])
def _get_associate_file(self, base_name: str, extensions_to_check: List[str]) -> Optional[str]:
+ """
+ :param base_name: base name of file, without directory
+ :param extensions_to_check: list of extensions in lower case without leading period
+ :return: full file path if found, else None
+ """
+
full_file_name_no_ext = os.path.join(self.dir_name, base_name)
for e in extensions_to_check:
possible_file = '{}.{}'.format(full_file_name_no_ext, e)
if os.path.exists(possible_file):
- return possible_file
+ return possible_file
possible_file = '{}.{}'.format(full_file_name_no_ext, e.upper())
if os.path.exists(possible_file):
return possible_file
diff --git a/raphodo/thumbnaildisplay.py b/raphodo/thumbnaildisplay.py
index 9478945..bce2781 100644
--- a/raphodo/thumbnaildisplay.py
+++ b/raphodo/thumbnaildisplay.py
@@ -391,6 +391,8 @@ class ThumbnailListModel(QAbstractListModel):
elif role == Roles.secondary_attribute:
if rpd_file.xmp_file_full_name:
return 'XMP'
+ elif rpd_file.log_file_full_name:
+ return 'LOG'
else:
return None
elif role== Roles.path:
diff --git a/raphodo/thumbnailextractor.py b/raphodo/thumbnailextractor.py
index 27c8089..74e63db 100755
--- a/raphodo/thumbnailextractor.py
+++ b/raphodo/thumbnailextractor.py
@@ -152,9 +152,13 @@ def get_video_frame(full_file_name: str,
try:
assert pipeline.seek_simple(Gst.Format.TIME, Gst.SeekFlags.FLUSH, offset)
except AssertionError:
+ logging.warning(
+ 'seek_simple() failed for %s. Is the necessary gstreamer plugin installed for this '
+ 'file format?', full_file_name
+ )
return None
# Wait for seek to finish.
- pipeline.get_state(Gst.CLOCK_TIME_NONE)
+ pipeline.get_state(Gst.CLOCK_TIME_NONE) # alternative is Gst.SECOND * 10
sample = pipeline.emit('convert-sample', caps)
if sample is not None:
buffer = sample.get_buffer()