View Single Post
Old 08-30-2021, 05:06 AM   #16
capink
Wizard
capink ought to be getting tired of karma fortunes by now.capink ought to be getting tired of karma fortunes by now.capink ought to be getting tired of karma fortunes by now.capink ought to be getting tired of karma fortunes by now.capink ought to be getting tired of karma fortunes by now.capink ought to be getting tired of karma fortunes by now.capink ought to be getting tired of karma fortunes by now.capink ought to be getting tired of karma fortunes by now.capink ought to be getting tired of karma fortunes by now.capink ought to be getting tired of karma fortunes by now.capink ought to be getting tired of karma fortunes by now.
 
Posts: 1,185
Karma: 1988646
Join Date: Aug 2015
Device: Kindle
Books added event

Code:
#!/usr/bin/env python
# ~*~ coding: utf-8 ~*~

__license__ = 'GPL v3'
__copyright__ = '2021, Ahmed Zaki <azaki00.dev@gmail.com>'
__docformat__ = 'restructuredtext en'

from functools import partial

from qt.core import (QApplication, Qt, QTimer, QWidget, QVBoxLayout, QCheckBox, pyqtSignal)

from calibre import prints
from calibre.constants import DEBUG
from calibre.db.listeners import EventType
from calibre.utils.date import now

from calibre_plugins.action_chains.events.base import ChainEvent
import calibre_plugins.action_chains.config as cfg

try:
    load_translations()
except NameError:
    prints("ActionChains::events/books_added.py - exception when loading translations")

class ConfigWidget(QWidget):
    def __init__(self, plugin_action):
        QWidget.__init__(self)
        self.plugin_action = plugin_action
        self.gui = plugin_action.gui
        self.db = self.gui.current_db
        self._init_controls()

    def _init_controls(self):

        l = self.l = QVBoxLayout()
        self.setLayout(l)

        self.select_chk = QCheckBox(_('Select newly added books'))
        self.select_chk.setChecked(True)
        l.addWidget(self.select_chk)

        l.addStretch(1)

        self.setMinimumSize(300,300)

    def load_settings(self, settings):
        if settings:
            self.select_chk.setChecked(settings.get('select_books', False))

    def save_settings(self):
        settings = {}
        settings['select_books'] = self.select_chk.isChecked()
        return settings

class BooksAddedEvent(ChainEvent):

    name = 'Books Added'
    books_added = pyqtSignal(object)
    timer_interval = 30

    def __init__(self, plugin_action):
        ChainEvent.__init__(self, plugin_action)
        self.db = plugin_action.gui.current_db
        self.gui.add_db_listener(self.process_event_in_db)
        self.book_created_cache = set()
        self.book_created_cache_last_updated = None
        QTimer.singleShot(self.timer_interval * 1000, self._on_timeout)

    def process_event_in_db(self, db, event_type, event_data):
        if not db.library_id == self.gui.current_db.library_id:
            return
        if event_type == EventType.book_created:
            book_id = event_data[0]
            self.add_to_book_created_cache(book_id, now())
        elif event_type == EventType.books_removed:
            removed_book_ids = event_data[0]
            self.book_created_cache = self.book_created_cache.difference(set(removed_book_ids))

    def _on_timeout(self):
        # Make sure no modal widget dialog is present (e.g. add books duplicate dialog). Otherwise, postpone
        if QApplication.instance().activeModalWidget():
            pass
        # postpone event if another action chains is running
        elif self.plugin_action.chainStack:
            pass
        else:
            utime = self.book_created_cache_last_updated
            if utime:
                elapsed = now() - utime
                # Wait for some seconds to make sure all books are added. If books are added by calibre
                # auto-add, there will not be a modal dialog, so we have to wait a little to make sure
                # auto-add finished adding all the books
                wait_time = 20
                if elapsed.total_seconds() > wait_time:
                    QTimer.singleShot(0, partial(self.books_added.emit, self.book_created_cache))
                    QTimer.singleShot(0, self.clean_book_created_cache)

        # keep the timer runnig
        QTimer.singleShot(self.timer_interval * 1000, self._on_timeout)

    def add_to_book_created_cache(self, book_id, timestamp):
        self.book_created_cache.add(book_id)
        self.book_created_cache_last_updated = timestamp

    def clean_book_created_cache(self):
        self.book_created_cache = set()
        self.book_created_cache_last_updated = None

    def get_event_signal(self):
        return self.books_added

    def config_widget(self):
        return ConfigWidget

    def pre_chains_event_actions(self, event_args, event_opts):
        if event_opts.get('select_books', False):
            book_ids = event_args[0]
            self.gui.library_view.select_rows(book_ids)
            if DEBUG:
                prints('Action Chains: Books Added Event: Selecting newly added book_ids: {}')
After adding this you should have an event called "Books Added" in the dropdown list of the Event Manager. You can attach whatever chains you want to that event.

Notes:
  • The event will automatically select all added books, so that any chains attached to the event can act on them. If whatever reason you don't want this, you can turn it off (Event Manager > Books Added > Event options)
  • There is a lag of several seconds before the event is activated. This is done to ensure calibre's adder (and auto-adder) has added all the books.
  • You can attach one or more chain(s) to the event: Event Manager > Add Event > Books Added > click settings button next to the event.
  • Whenever you change the name of a chain attached to an event, you will need to open the Event Manager to re-attach it.

Last edited by capink; 01-20-2025 at 01:12 PM.
capink is offline   Reply With Quote