From 77dd64c0757c0191b276e65c24ee9874959790c8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rg=20Frings-F=C3=BCrst?= Date: Tue, 25 Jul 2017 06:17:26 +0200 Subject: New upstream version 0.9.1 --- raphodo/__about__.py | 2 +- raphodo/backupfile.py | 2 ++ raphodo/copyfiles.py | 8 +++++++- raphodo/generatename.py | 16 +++++++++++++--- raphodo/newversion.py | 10 +++++----- raphodo/rapid.py | 34 ++++++++++++++++++---------------- raphodo/renameandmovefile.py | 39 +++++++++++++++++++++++++++++++++++++++ raphodo/rpdfile.py | 23 +++++++++++++++++++---- raphodo/scan.py | 35 ++++++++++++++++++++++++++++++++--- raphodo/thumbnaildisplay.py | 2 ++ raphodo/thumbnailextractor.py | 6 +++++- 11 files changed, 143 insertions(+), 34 deletions(-) (limited to 'raphodo') 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() -- cgit v1.2.3