Beispiel #1
0
 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))
Beispiel #2
0
 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)
Beispiel #3
0
    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()
Beispiel #4
0
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_()
Beispiel #5
0
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()
Beispiel #6
0
    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")
Beispiel #9
0
 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
Beispiel #10
0
    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
Beispiel #11
0
    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")
Beispiel #13
0
    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"))
Beispiel #14
0
    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)
Beispiel #15
0
    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
Beispiel #16
0
    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)
Beispiel #17
0
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
Beispiel #18
0
 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
Beispiel #19
0
 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.')
Beispiel #20
0
    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})
Beispiel #21
0
 def init_loader_thread(self):
     self._img_loader.moveToThread(self._img_loader_thread)
     self._img_loader_thread.start()
     LOGGER.debug('Loader thread started.')
Beispiel #22
0
    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