summaryrefslogtreecommitdiff
path: root/raphodo
diff options
context:
space:
mode:
Diffstat (limited to 'raphodo')
-rw-r--r--raphodo/__about__.py2
-rw-r--r--raphodo/cache.py17
-rw-r--r--raphodo/devicedisplay.py104
-rw-r--r--raphodo/jobcodepanel.py52
-rwxr-xr-xraphodo/rapid.py14
-rwxr-xr-xraphodo/rpdsql.py9
6 files changed, 124 insertions, 74 deletions
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 <damonlynch@gmail.com>
+# Copyright (C) 2015-2018 Damon Lynch <damonlynch@gmail.com>
# 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 <damonlynch@gmail.com>
+# Copyright (C) 2015-2018 Damon Lynch <damonlynch@gmail.com>
# 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 <damonlynch@gmail.com>
+# Copyright (C) 2017-2018 Damon Lynch <damonlynch@gmail.com>
# 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