From baec34cc51c5822c002b22072a9e99d662dce465 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Antoine=20Beaupr=C3=A9?= Date: Mon, 30 Jul 2018 10:50:17 -0400 Subject: New upstream version 0.9.10 --- raphodo/__about__.py | 2 +- raphodo/cache.py | 17 ++++---- raphodo/devicedisplay.py | 104 +++++++++++++++++++++++++++-------------------- raphodo/jobcodepanel.py | 52 +++++++++++++++--------- raphodo/rapid.py | 14 +++++-- raphodo/rpdsql.py | 9 ++++ 6 files changed, 124 insertions(+), 74 deletions(-) (limited to 'raphodo') diff --git a/raphodo/__about__.py b/raphodo/__about__.py index 8a6a281..20c1a7c 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.9' +__version__ = '0.9.10' __author__ = 'Damon Lynch' __email__ = 'damonlynch@gmail.com' diff --git a/raphodo/cache.py b/raphodo/cache.py index 0801762..92e7464 100644 --- a/raphodo/cache.py +++ b/raphodo/cache.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 -# Copyright (C) 2015-2017 Damon Lynch +# Copyright (C) 2015-2018 Damon Lynch # This file is part of Rapid Photo Downloader. # @@ -47,7 +47,7 @@ http://specifications.freedesktop.org/thumbnail-spec/thumbnail-spec-latest.html """ __author__ = 'Damon Lynch' -__copyright__ = "Copyright 2015-2017, Damon Lynch" +__copyright__ = "Copyright 2015-2018, Damon Lynch" import os import sys @@ -69,8 +69,7 @@ from raphodo.rpdsql import CacheSQL GetThumbnail = namedtuple('GetThumbnail', 'disk_status, thumbnail, path') -GetThumbnailPath = namedtuple('GetThumbnailPath', 'disk_status, path, mdatatime, ' - 'orientation_unknown') +GetThumbnailPath = namedtuple('GetThumbnailPath', 'disk_status, path, mdatatime, orientation_unknown') class MD5Name: """Generate MD5 hashes for file names.""" @@ -521,9 +520,13 @@ class ThumbnailCacheSql: os.remove(thumbnail) deleted_thumbnails.append(name) if len(deleted_thumbnails): - self.thumb_db.delete_thumbnails(deleted_thumbnails) - logging.debug('Deleted {} thumbnail files that had not been ' - 'accessed for {} or more days'.format(len(deleted_thumbnails), days)) + if self.thumb_db.cache_exists(): + self.thumb_db.delete_thumbnails(deleted_thumbnails) + logging.debug( + 'Deleted {} thumbnail files that had not been accessed for {} or more days'.format( + len(deleted_thumbnails), days + ) + ) def purge_cache(self) -> None: """ diff --git a/raphodo/devicedisplay.py b/raphodo/devicedisplay.py index 6219648..edfd717 100644 --- a/raphodo/devicedisplay.py +++ b/raphodo/devicedisplay.py @@ -1,4 +1,4 @@ -# Copyright (C) 2015-2017 Damon Lynch +# Copyright (C) 2015-2018 Damon Lynch # Copyright (c) 2012-2014 Alexander Turkin # This file is part of Rapid Photo Downloader. @@ -36,7 +36,7 @@ Copyright notice from QtWaitingSpinner source: """ __author__ = 'Damon Lynch' -__copyright__ = "Copyright 2015-2017, Damon Lynch" +__copyright__ = "Copyright 2015-2018, Damon Lynch" import math from collections import namedtuple, defaultdict @@ -1051,14 +1051,19 @@ class DeviceDelegate(QStyledItemDelegate): else: checked = None - self.deviceDisplay.paint_header(painter=painter, x=x, y=y, width=width, - rotation=rotation, - icon=icon, - device_state=device_state, - display_name=display_name, - checked=checked, - download_statuses=download_statuses, - percent_complete=percent_complete) + self.deviceDisplay.paint_header( + painter=painter, + x=x, + y=y, + width=width, + rotation=rotation, + icon=icon, + device_state=device_state, + display_name=display_name, + checked=checked, + download_statuses=download_statuses, + percent_complete=percent_complete + ) else: assert view_type == ViewRowType.content @@ -1082,39 +1087,52 @@ class DeviceDelegate(QStyledItemDelegate): 'no_videos': thousands(device.file_type_counter[video_key])} photos_size = format_size_for_user(device.file_size_sum[photo_key]) videos_size = format_size_for_user(device.file_size_sum[video_key]) - other_bytes = storage_space.bytes_total - device.file_size_sum.sum(sum_key) - \ - storage_space.bytes_free - other_size = format_size_for_user(other_bytes) - bytes_total_text = format_size_for_user(storage_space.bytes_total, no_decimals=0) - bytes_used = storage_space.bytes_total-storage_space.bytes_free - - percent_used = '{0:.0%}'.format(bytes_used / storage_space.bytes_total) - # Translators: percentage full e.g. 75% full - percent_used = _('%s full') % percent_used - - details = BodyDetails(bytes_total_text=bytes_total_text, - bytes_total=storage_space.bytes_total, - percent_used_text=percent_used, - bytes_free_of_total='', - comp1_file_size_sum=device.file_size_sum[photo_key], - comp2_file_size_sum=device.file_size_sum[video_key], - comp3_file_size_sum=other_bytes, - comp4_file_size_sum=0, - comp1_text = photos, - comp2_text = videos, - comp3_text = self.other, - comp4_text = '', - comp1_size_text=photos_size, - comp2_size_text=videos_size, - comp3_size_text=other_size, - comp4_size_text='', - color1=QColor(CustomColors.color1.value), - color2=QColor(CustomColors.color2.value), - color3=QColor(CustomColors.color3.value), - displaying_files_of_type=DisplayingFilesOfType.photos_and_videos - ) - self.deviceDisplay.paint_body(painter=painter, x=x, y=y, width=width, - details=details) + + # Some devices do not report how many bytes total they have, e.g. some SMB shares + if storage_space.bytes_total: + other_bytes = storage_space.bytes_total - device.file_size_sum.sum(sum_key) - \ + storage_space.bytes_free + other_size = format_size_for_user(other_bytes) + bytes_total_text = format_size_for_user( + storage_space.bytes_total, no_decimals=0 + ) + bytes_used = storage_space.bytes_total-storage_space.bytes_free + percent_used = '{0:.0%}'.format(bytes_used / storage_space.bytes_total) + # Translators: percentage full e.g. 75% full + percent_used = _('%s full') % percent_used + bytes_total = storage_space.bytes_total + else: + percent_used = _('Device size unknown') + bytes_total = device.file_size_sum.sum(sum_key) + other_bytes = 0 + bytes_total_text = format_size_for_user(bytes_total, no_decimals=0) + other_size = '0' + + details = BodyDetails( + bytes_total_text=bytes_total_text, + bytes_total=bytes_total, + percent_used_text=percent_used, + bytes_free_of_total='', + comp1_file_size_sum=device.file_size_sum[photo_key], + comp2_file_size_sum=device.file_size_sum[video_key], + comp3_file_size_sum=other_bytes, + comp4_file_size_sum=0, + comp1_text = photos, + comp2_text = videos, + comp3_text = self.other, + comp4_text = '', + comp1_size_text=photos_size, + comp2_size_text=videos_size, + comp3_size_text=other_size, + comp4_size_text='', + color1=QColor(CustomColors.color1.value), + color2=QColor(CustomColors.color2.value), + color3=QColor(CustomColors.color3.value), + displaying_files_of_type=DisplayingFilesOfType.photos_and_videos + ) + self.deviceDisplay.paint_body( + painter=painter, x=x, y=y, width=width, details=details + ) else: assert len(device.storage_space) == 0 diff --git a/raphodo/jobcodepanel.py b/raphodo/jobcodepanel.py index 2337691..2aaf980 100644 --- a/raphodo/jobcodepanel.py +++ b/raphodo/jobcodepanel.py @@ -1,4 +1,4 @@ -# Copyright (C) 2017 Damon Lynch +# Copyright (C) 2017-2018 Damon Lynch # This file is part of Rapid Photo Downloader. # @@ -21,7 +21,7 @@ Display, edit and apply Job Codes. """ __author__ = 'Damon Lynch' -__copyright__ = "Copyright 2017, Damon Lynch" +__copyright__ = "Copyright 2017-2018, Damon Lynch" from typing import Optional, Dict, Tuple, Union, List import logging @@ -190,7 +190,8 @@ class JobCodeOptionsWidget(QFramedWidget): _('Removing a Job Code removes it only from the list of saved Job Codes, ' 'not from any photos or videos that it may have been applied to.'), _('If you want to use Job Codes, configure file renaming or destination subfolder ' - 'names to use them.'))) + 'names to use them.')) + ) self.setDefaultMessage() @@ -390,15 +391,17 @@ class JobCodeOptionsWidget(QFramedWidget): def applyButtonClicked(self) -> None: row = self.jobCodesWidget.currentRow() if row < 0: - logging.error("Did not expect Apply Job Code button to be enabled when no Job Code " - "is selected.") + logging.error( + "Did not expect Apply Job Code button to be enabled when no Job Code is selected." + ) return try: job_code = self.jobCodesWidget.item(row).text() except: - logging.exception("Job Code did not exist when obtaining its value from the list " - "widget") + logging.exception( + "Job Code did not exist when obtaining its value from the list widget" + ) return self.rapidApp.applyJobCode(job_code=job_code) @@ -406,8 +409,10 @@ class JobCodeOptionsWidget(QFramedWidget): try: self.prefs.del_list_value(key='job_codes', value=job_code) except KeyError: - logging.exception("Attempted to delete non existent value %s from Job Codes while in " - "process of moving it to the front of the list", job_code) + logging.exception( + "Attempted to delete non existent value %s from Job Codes while in process of " + "moving it to the front of the list", job_code + ) self.prefs.add_list_value(key='job_codes', value=job_code) if self.sortCombo.currentIndex() != 1: @@ -420,8 +425,9 @@ class JobCodeOptionsWidget(QFramedWidget): try: self.prefs.del_list_value(key='job_codes', value=item.text()) except KeyError: - logging.exception("Attempted to delete non existent value %s from Job Codes", - item.text()) + logging.exception( + "Attempted to delete non existent value %s from Job Codes", item.text() + ) @pyqtSlot() def removeAllButtonClicked(self) -> None: @@ -445,8 +451,9 @@ class JobCodeOptionsWidget(QFramedWidget): if not self.prompting_for_job_code: logging.debug("Prompting for job code") self.prompting_for_job_code = True - dialog = JobCodeDialog(self.rapidApp, on_download=on_download, - job_codes=self._jobCodes()) + dialog = JobCodeDialog( + self.rapidApp, on_download=on_download, job_codes=self._jobCodes() + ) if dialog.exec(): self.prompting_for_job_code = False logging.debug("Job code entered / selected") @@ -465,7 +472,8 @@ class JobCodeOptionsWidget(QFramedWidget): self.rapidApp.applyJobCode(job_code=job_code) else: self.rapidApp.thumbnailModel.assignJobCodesToMarkedFilesWithNoJobCode( - job_code=job_code) + job_code=job_code + ) return True else: self.prompting_for_job_code = False @@ -490,12 +498,15 @@ class JobCodePanel(QScrollArea): self.setFrameShape(QFrame.NoFrame) - self.jobCodePanel = QPanelView(label=_('Job Codes'), - headerColor=QColor(ThumbnailBackgroundName), - headerFontColor=QColor(Qt.white)) + self.jobCodePanel = QPanelView( + label=_('Job Codes'), + headerColor=QColor(ThumbnailBackgroundName), + headerFontColor=QColor(Qt.white) + ) - self.jobCodeOptions = JobCodeOptionsWidget(prefs=self.prefs, rapidApp=self.rapidApp, - parent=self) + self.jobCodeOptions = JobCodeOptionsWidget( + prefs=self.prefs, rapidApp=self.rapidApp, parent=self + ) self.jobCodePanel.addWidget(self.jobCodeOptions) widget = QWidget() @@ -509,7 +520,8 @@ class JobCodePanel(QScrollArea): if parent is not None: self.rapidApp.thumbnailView.selectionModel().selectionChanged.connect( - self.jobCodeOptions.setWidgetStates) + self.jobCodeOptions.setWidgetStates + ) self.rapidApp.thumbnailModel.selectionReset.connect(self.jobCodeOptions.setWidgetStates) def needToPromptForJobCode(self) -> bool: diff --git a/raphodo/rapid.py b/raphodo/rapid.py index 83e244b..54a3941 100755 --- a/raphodo/rapid.py +++ b/raphodo/rapid.py @@ -53,6 +53,7 @@ import subprocess from urllib.request import pathname2url import tarfile import inspect +from distutils.version import LooseVersion from gettext import gettext as _ @@ -71,7 +72,6 @@ except (ImportError, ValueError): import zmq import psutil import gphoto2 as gp -import sip from PyQt5 import QtCore from PyQt5.QtCore import ( QThread, Qt, QStorageInfo, QSettings, QPoint, QSize, QTimer, QTextStream, QModelIndex, @@ -89,6 +89,11 @@ from PyQt5.QtWidgets import ( ) from PyQt5.QtNetwork import QLocalSocket, QLocalServer +if LooseVersion(QtCore.PYQT_VERSION_STR) >= LooseVersion('5.11'): + from PyQt5 import sip +else: + import sip + from raphodo.storage import ( ValidMounts, CameraHotplug, UDisks2Monitor, GVolumeMonitor, have_gio, has_one_or_more_folders, mountPaths, get_desktop_environment, get_desktop, @@ -763,7 +768,7 @@ class RapidWindow(QMainWindow): self.prefs.optimize_thumbnail_db = False else: # Recreate the cache on the file system - ThumbnailCacheSql(create_table_if_not_exists=True) + t = ThumbnailCacheSql(create_table_if_not_exists=True) # For meaning of 'Devices', see devices.py self.devices = DeviceCollection(self.exiftool_process, self) @@ -2918,7 +2923,7 @@ Do you want to proceed with the download? if scan_id in self.devices.thumbnailing] for scan_id in stop_thumbnailing: device = self.devices[scan_id] - if not scan_id in self.thumbnailModel.generating_thumbnails: + if scan_id not in self.thumbnailModel.generating_thumbnails: logging.debug( "Not terminating thumbnailing of %s because it's not in the thumbnail manager", device.display_name @@ -4421,6 +4426,7 @@ Do you want to proceed with the download? elif auto_start: self.displayMessageInStatusBar() if self.jobCodePanel.needToPromptForJobCode(): + self.showMainWindow() model.setSpinnerState(scan_id, DeviceState.idle) start_download = self.jobCodePanel.getJobCodeBeforeDownload() if not start_download: @@ -5517,6 +5523,7 @@ Do you want to proceed with the download? Returns True if yes or there was no need to ask the user, False if the user said no. """ + self.showMainWindow() path = self.prefs.this_computer_path if path in ( @@ -5544,6 +5551,7 @@ Do you want to proceed with the download? :return: True if scans of such partitions should occur, else False """ + return self.prefs.device_autodetection and not self.prefs.scan_specific_folders def displayMessageInStatusBar(self) -> None: diff --git a/raphodo/rpdsql.py b/raphodo/rpdsql.py index 112aea0..7988158 100755 --- a/raphodo/rpdsql.py +++ b/raphodo/rpdsql.py @@ -734,6 +734,15 @@ class CacheSQL: def db_fs_name(self) -> str: return 'thumbnail_cache.sqlite' + def cache_exists(self) -> bool: + conn = sqlite3.connect(self.db) + row = conn.execute( + """SELECT name FROM sqlite_master WHERE type='table' AND name='{}'""".format(self.table_name) + ).fetchone() + conn.close() + return row is not None + + def update_table(self, reset: bool=False) -> None: """ Create or update the database table -- cgit v1.2.3