def get_updates(self): url = settings.get( SettingType.CHECK_UPDATE_URL, 'https://api.github.com/repos/juliendz/imagius/releases/latest') qurl = QtCore.QUrl(url) LOGGER.info("Checking for updates....") self._net_mgr.get(QtNetwork.QNetworkRequest(qurl))
def create_meta_db_from_schema(self): try: self.connect() self.conn.cursor().executescript(self.schema_meta) self.disconnect() except sqlite3.OperationalError as msg: LOGGER.critical('[Error while creating meta db ]: %s' % msg)
def watch_all(self): """ <TODO> """ self._img_integrity_ts = start_time = time.time() LOGGER.info('Watch all started.') self._meta_files_mgr.connect() self.scan_folders() elapsed = round(time.time() - start_time, 2) suffix = 'seconds' if elapsed > 60: elapsed /= 60 suffix = 'minutes' LOGGER.info('Watch all completed in %.2f %s.' % (elapsed, suffix)) self.watch_all_done.emit(elapsed, suffix) orphaned_scan_dirs = self._meta_files_mgr.get_orphaned_scan_dirs( self._img_integrity_ts) for dir in orphaned_scan_dirs: self.dir_empty_or_deleted.emit({'id': dir['id']}) self._meta_files_mgr.prune_scan_dir(dir['id']) self.watch_empty_or_deleted_done.emit() self._meta_files_mgr.disconnect()
def run(argv): init_app_data_dir() init_app_db() from imagius.log import LOGGER LOGGER.info( '======================================Imagius starting up=======================================') import imagius.settings settings.load_settings() from imagius.main_window import MainWindow app = QApplication(argv) app.setApplicationName(HUMAN_APP_NAME) w = MainWindow() # with open('styles/Aqua.qss', 'r') as stylesheet: # qss = stylesheet.read() # w.setStyleSheet(qss) w.setWindowTitle('Imagius') w.showMaximized() return app.exec_()
def run(argv): init.init_app_data_dir() from imagius.log import LOGGER LOGGER.info( '======================================Imagius starting up=======================================') init.init_app_db() from imagius import settings settings.load_settings()
def closeEvent(self, event): LOGGER.debug('Shutting down gracefully....') self._meta_files_mgr.disconnect() settings.persist_to_disk() self._dir_watcher_thread.quit() self._dir_watcher_thread.wait() self._img_loader_thread.quit() self._img_loader_thread.wait()
def add_folder(self): """ <TODO> """ folder_path = QFileDialog.getExistingDirectory(self) folder_name = os.path.basename((folder_path)) folder_id = self.folder_mgr.add_watched_folder( folder_path, folder_name) LOGGER.info('Added watched folder (fid:%s)' % folder_id) self.populate_folder_tree()
def delete_folder(self): selected = self.listView_FolderList.selectedIndexes() if len(selected) > 0: selected_index = selected[0] folder_id = selected_index.data(QtCore.Qt.UserRole + 1) self.folder_mgr.delete_watched_folder(folder_id) LOGGER.info('Deleted watched folder (fid:%s)' % folder_id) self.populate_folder_tree() else: QMessageBox.about(self, "No selection", "Please select an entry to delete")
def get_img_exif(self, abspath): exif = {} try: with PIL.Image.open(abspath) as image: exif = { PIL.ExifTags.TAGS[k]: v for k, v in image._getexif().items() if k in PIL.ExifTags.TAGS } except Exception as ex: LOGGER.debug("EXIF data for %s could not be loaded." % abspath) return exif
def _generate_thumb(self, abspath, thumb_size): thumb_bytes = io.BytesIO() try: ext = QtCore.QFileInfo(abspath).suffix() if ext == 'png': with Image.open(abspath) as image: image.verify() with Image.open(abspath) as image: image.thumbnail(thumb_size, Image.ANTIALIAS) image.save(thumb_bytes, image.format) except Exception as err: LOGGER.error('Error while generating thumbs: %s' % err) return thumb_bytes
def scan_folders(self): watched_folders = self._meta_files_mgr.get_watched_dirs() # Scan the watched directories. for idx, folder in enumerate(watched_folders): self.scan_folder(folder['id'], folder['abspath'], folder['name']) # TODO: Emit list of unclean entries for notification # self._meta_files_mgr.get_unclean_entries # Finally, remove the non-existent files # self._meta_files_mgr.clean_db(self._img_integrity_ts) LOGGER.debug("Folder scan completed.")
def edit_folder(self): selected = self.listView_FolderList.selectedIndexes() if len(selected) > 0: selected_index = selected[0] folder_id = selected_index.data(QtCore.Qt.UserRole + 1) new_folder_path = QFileDialog.getExistingDirectory(self) new_folder_name = os.path.basename((new_folder_path)) self.folder_mgr.edit_watched_folder( folder_id, new_folder_path, new_folder_name) LOGGER.info('Edited watched folder (fid:%s)' % folder_id) self.populate_folder_tree() else: QMessageBox.about(self, "No selection", "Please select an entry to edit")
def _request_finished(self, reply): if reply.error() == QtNetwork.QNetworkReply.NoError: json_doc = QtCore.QJsonDocument.fromJson(reply.readAll()) json_obj = json_doc.object() latest_release = Version(json_obj['tag_name'].toString()) is_draft = json_obj['draft'].toBool() assets = json_obj['assets'].toArray() asset_url = '' if len(assets) > 0: asset = assets[0] asset_url = asset['browser_download_url'] if asset_url == '': LOGGER.error("Update Manager: Assets object was null or empty") self._setup_msgbox( "Error checking for Updates", 'No downloads available for this release. Redirect to website downloads page ?', "Release API 'Assets' object was null or empty") self._msgbox.exec() if self._msgbox.clickedButton() == self._msgox_btn_ok: QtGui.QDesktopServices.openUrl( QtCore.QUrl( "https://juliendz.github.io/imagius/#download")) return if latest_release > Version( settings.app_version) and is_draft is False: LOGGER.info("Update found. New version(%s) is available" % str(latest_release)) self._setup_msgbox( "Update info !", 'Update found. Latest version is (%s)' % str(latest_release), None, "Download", "Later") self._msgbox.exec() if self._msgbox.clickedButton() == self._msgox_btn_ok: QtGui.QDesktopServices.openUrl( QtCore.QUrl(asset_url.toString())) else: LOGGER.info("No updates found! Already on latest release.") msgbox = QtWidgets.QMessageBox() msgbox.setIcon(QtWidgets.QMessageBox.Information) msgbox.setWindowTitle('Update Info') msgbox.setText("No updates found! Already on latest release.") msgbox.exec() else: LOGGER.error( "Update Manager: Unable to retrieve updates from release api (Error: %s)" % error_code) self._setup_msgbox( "Error checking for Updates", 'Unable to load the release api. Redirect to website downloads page ?', 'Network Request Error Code: %s' % error_code) self._msgbox.exec() if self._msgbox.clickedButton() == self._msgox_btn_ok: QtGui.QDesktopServices.openUrl( QtCore.QUrl( "https://juliendz.github.io/imagius/#download"))
def load_scandir(self, sd_id, serial, load_count, scrollDirection): """ <TODO> """ self._img_integrity_ts = start_time = time.time() self._meta_files_mgr.connect() LOGGER.debug('Load Scan Dir started.') dir_info = self._meta_files_mgr.get_scan_dir(sd_id) self.load_scan_dir_info_success.emit(dir_info) reverse = True if scrollDirection == ScrollDirection.Up else False images = self._meta_files_mgr.get_scan_dir_images_by_serial( sd_id, serial, load_count, reverse) self.load_images_success.emit(images, scrollDirection)
def run_select_query(self, query, params=()): """ Description: Executes an select sql query using Pythons DB-API (Parameter substitution) Arguments: 'query: The sql query string 'params' : tuple containing parameters # Returns: False on failure | list[row_number][column_name] on success """ self.conn.row_factory = dict_factory cursor = self.conn.cursor() try: cursor.execute(query, params) except sqlite3.OperationalError as msg: LOGGER.error('[DB]: %s' % msg) data = cursor.fetchall() return data
def _handle_load_images_sucess(self, images, scrollDirection): img_count = len(images) print('Recevied %s images' % img_count) LOGGER.debug('Recevied %s images' % img_count) for img in images: img['thumb'] = QImage.fromData(img['thumb']) item = QStandardItem() thumb_caption_type = settings.get( SettingType.UI_THUMBS_CAPTION_DISPLAY_MODE, Thumb_Caption_Type.NoCaption.name) if thumb_caption_type == Thumb_Caption_Type.FileName.name: item.setText(img['name']) item.setData(img['id'], QtCore.Qt.UserRole + 1) item.setData(img['serial'], QtCore.Qt.UserRole + 2) item.setIcon(QIcon(QPixmap.fromImage(img['thumb']))) item.setText(str(img['serial'])) if scrollDirection == ScrollDirection.Up: self._thumbs_view_model.insertRow(0, item) if scrollDirection == ScrollDirection.Down: self._thumbs_view_model.appendRow(item)
def upgrade_from_previous_versions(cur_version, meta_db_path): meta_db = dbmgr(meta_db_path) meta_db.connect() # Upgrade code to Version(0.8.1) if cur_version < Version('0.8.1'): LOGGER.info('Upgrading from Version:%s to Version:%s' % (cur_version, '0.8.1')) queries = [ 'CREATE TEMPORARY TABLE scan_img_backup(a,b,c,d,e,f,g,h);', """INSERT INTO scan_img_backup SELECT id, abspath, name, thumb, sdid, mtime, integrity_check, serial FROM scan_img; """, 'DROP TABLE scan_img;', """CREATE TABLE IF NOT EXISTS "scan_img" ( `id` INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, `abspath` TEXT NOT NULL UNIQUE, `name` TEXT NOT NULL, `thumb` BLOB NOT NULL, `sdid` INTEGER NOT NULL DEFAULT 0, `mtime` INTEGER, `integrity_check` INTEGER, `serial` INTEGER, FOREIGN KEY(`sdid`) REFERENCES `scan_dir`(`id`) ON DELETE CASCADE ) """, 'INSERT INTO scan_img SELECT a,b,c,d,e,f,g,h FROM scan_img_backup;', 'DROP TABLE scan_img_backup;', ] for query in queries: meta_db.run_query(query) meta_db.commit() cur_version = Version('0.8.1') meta_db.disconnect() return
def run_insert_query(self, query, params, commit=True): """ Description: Executes an insert sql query using Pythons DB-API (Parameter substitution) Arguments: 'query': The sql query string 'params' : tuple containing parameters """ cursor = self.conn.cursor() try: cursor.execute(query, params) if commit: self.conn.commit() except sqlite3.OperationalError as msg: LOGGER.error('[DB]: %s' % msg) except sqlite3.IntegrityError as msg: LOGGER.debug('[DB]: %s : %s : %s' % (msg, query, params)) LOGGER.error('[DB]: %s : %s' % (msg, params)) return cursor.lastrowid
def init_watch_thread(self): self._watch.moveToThread(self._dir_watcher_thread) self._dir_watcher_thread.start() self._run_watcher() LOGGER.debug('Watcher thread started.')
def scan_folder(self, parent_id, abs_path, dir_name): """ <TODO> """ is_new_or_modified = False if not os.path.isdir(abs_path): return modified_time = os.path.getmtime(abs_path) sd_id = 0 sd_info = self._meta_files_mgr.get_scan_dir_id(abs_path) if not sd_info or sd_info['id'] <= 0: sd_id = self._meta_files_mgr.add_scan_dir(parent_id, abs_path, dir_name, self._img_integrity_ts) sd_info = self._meta_files_mgr.get_scan_dir_id(abs_path) is_new_or_modified = True else: sd_id = sd_info['id'] self._meta_files_mgr.update_scan_dir_integrity_check( sd_id, self._img_integrity_ts) if not sd_info['mtime'] or (modified_time > sd_info['mtime']): LOGGER.debug("Folder(%s):(%s) has changed since last scan." % (sd_id, sd_info['abspath'])) is_new_or_modified = True if is_new_or_modified is True: dir_iter = QDirIterator( abs_path, self._img_ext_filter, QDir.AllEntries | QDir.AllDirs | QDir.NoDotAndDotDot, QDirIterator.FollowSymlinks) else: dir_iter = QDirIterator(abs_path, QDir.AllDirs | QDir.NoDotAndDotDot, QDirIterator.FollowSymlinks) has_new_images = False img_serial = self._meta_files_mgr.get_scan_dir_img_next_serial(sd_id) while dir_iter.hasNext(): dir_iter.next() file_info = dir_iter.fileInfo() if file_info.isDir(): LOGGER.debug("Found Directory: %s" % file_info.absoluteFilePath()) self.scan_folder(parent_id, file_info.absoluteFilePath(), file_info.fileName()) else: si_info = self._meta_files_mgr.get_image_id( file_info.absoluteFilePath()) # file exists if si_info and si_info['id'] > 0: LOGGER.debug("Found Image: %s" % file_info.absoluteFilePath()) latest_mtime = os.path.getmtime( file_info.absoluteFilePath()) if latest_mtime > si_info['mtime']: self._meta_files_mgr.update_image_thumb( si_info['id'], file_info.absoluteFilePath(), latest_mtime, self._img_integrity_ts) else: self._meta_files_mgr.update_image( si_info['id'], self._img_integrity_ts) # new file to add elif not si_info: LOGGER.debug("Found New image: %s" % file_info.absoluteFilePath()) self.new_img_found.emit({ 'dir': dir_name, 'filename': file_info.fileName() }) self._meta_files_mgr.add_image( sd_id, file_info.absoluteFilePath(), file_info.fileName(), self._img_integrity_ts, img_serial) has_new_images = True img_serial = img_serial + 1 self._meta_files_mgr.commit() if is_new_or_modified is True: img_del_count = self._meta_files_mgr.prune_scan_img( sd_id, self._img_integrity_ts) img_count = self._meta_files_mgr.get_scan_dir_img_count(sd_id) self._meta_files_mgr.update_scan_dir_img_count(sd_id, img_count) if has_new_images or img_del_count > 0: self.dir_added_or_updated.emit({ 'id': sd_id, 'name': dir_name, 'img_count': img_count }) # Only update `mtime` if the scan_dir is new or modified if img_count > 0: self._meta_files_mgr.update_scan_dir_mtime( sd_id, modified_time) else: self._meta_files_mgr.remove_scan_dir(sd_id) self.dir_empty_or_deleted.emit({'id': sd_id})
def init_loader_thread(self): self._img_loader.moveToThread(self._img_loader_thread) self._img_loader_thread.start() LOGGER.debug('Loader thread started.')
def get_img_properties(self, si_id, sd_id): if not self._meta_db.is_open: self.connect() dr_img = self.get_image_from_id(si_id, sd_id) exif = self.get_img_exif(dr_img['abspath']) # print(exif) properties = {} properties['abspath'] = dr_img['abspath'] properties['filename'] = dr_img['name'] properties['filesize'] = self.format_size( QtCore.QFileInfo(dr_img['abspath']).size()) properties['dimensions'] = '' if exif: if 'DateTime' in exif: properties['DateTime'] = exif['DateTime'] if 'DateTimeDigitized' in exif: properties['DateTimeDigitized'] = exif['DateTimeDigitized'] if 'ImageWidth' in exif: properties['ImageWidth'] = exif['ImageWidth'] if 'ImageLength' in exif: properties['ImageLength'] = exif['ImageLength'] if 'ExifImageWidth' in exif: properties['ExifImageWidth'] = exif['ExifImageWidth'] if 'ExifImageHeight' in exif: properties['ExifImageHeight'] = exif['ExifImageHeight'] if 'Orientation' in exif: properties['Orientation'] = exif['Orientation'] if 'XPKeywords' in exif: properties['XPKeywords'] = exif['XPKeywords'].decode("utf-16") if 'ImageUniqueID' in exif: properties['ImageUniqueID'] = exif['ImageUniqueID'] if 'ColorSpace' in exif: properties['ColorSpace'] = exif['ColorSpace'] if 'BitsPerSample' in exif: properties['BitsPerSample'] = exif['BitsPerSample'] if 'PhotometricInterpretation' in exif: properties['PhotometricInterpretation'] = exif[ 'PhotometricInterpretation'] if 'ResolutionUnit' in exif: properties['ResolutionUnit'] = exif['ResolutionUnit'] if 'Software' in exif: properties['Software'] = exif['Software'] if 'SamplesPerPixel' in exif: properties['SamplesPerPixel'] = exif['SamplesPerPixel'] if 'XResolution' in exif: properties['XResolution'] = exif['XResolution'] if 'YResolution' in exif: properties['YResolution'] = exif['YResolution'] if 'ImageWidth' in exif: properties['dimensions'] = "%sx%s" % ( properties['ImageWidth'], properties['ImageLength']) elif 'ExifImageWidth' in exif: properties['dimensions'] = "%sx%s" % ( properties['ExifImageWidth'], properties['ExifImageHeight']) else: LOGGER.debug("EXIF data for %s not found." % dr_img['abspath']) # TODO: IPTC support # iptc_info = IPTCInfo(dr_img['abspath']) # if len(iptc_info.data) < 4: # LOGGER.debug("IPTC dat for %s not found." % dr_img['abspath']) # else: # print(iptc_info) return properties