View Single Post
Old 08-07-2021, 12:48 PM   #640
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,092
Karma: 1948136
Join Date: Aug 2015
Device: Kindle
@BetterRed

Here is another method of achieving what you want, you need to add the following custom event (Manage modules > add module > copy paste the code below):

Code:
from functools import partial

# python 3 compatibility
from six import text_type as unicode

from PyQt5.Qt 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

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['select_books'])

    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
                if elapsed.seconds > 20:
                    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 optinos)
  • 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.

Edit: The solution above is updated to work with auto-add. The File Type plugin solution could work, but it will need a separate plugin, also it will invoke the chain once after each book is added instead of waiting for all books before running the chain.

Edit2: The solution above is updated to be more cleaner:
  • It no longer monkeypatches calibre code.
  • It waits for all books to be added, and then releases one event for all added books, instead of a separate event per book.
  • This will only work with Action Chains version >= 1.11.1

Last edited by capink; 08-17-2021 at 03:39 PM.
capink is offline   Reply With Quote