Register Guidelines E-Books Today's Posts Search

Go Back   MobileRead Forums > E-Book Software > Calibre > Plugins

Notices

Reply
 
Thread Tools Search this Thread
Old 06-16-2025, 12:23 PM   #76
ownedbycats
Custom User Title
ownedbycats ought to be getting tired of karma fortunes by now.ownedbycats ought to be getting tired of karma fortunes by now.ownedbycats ought to be getting tired of karma fortunes by now.ownedbycats ought to be getting tired of karma fortunes by now.ownedbycats ought to be getting tired of karma fortunes by now.ownedbycats ought to be getting tired of karma fortunes by now.ownedbycats ought to be getting tired of karma fortunes by now.ownedbycats ought to be getting tired of karma fortunes by now.ownedbycats ought to be getting tired of karma fortunes by now.ownedbycats ought to be getting tired of karma fortunes by now.ownedbycats ought to be getting tired of karma fortunes by now.
 
ownedbycats's Avatar
 
Posts: 11,171
Karma: 77304081
Join Date: Oct 2018
Location: Canada
Device: Kobo Libra H2O, formerly Aura HD
Quote:
Originally Posted by dunhill View Post
I didn't imagine something like this would be planned, and it was making my life complicated. Thanks so much. I'll try it now.
Can't change VLs directly in AC. I'd do a selection modifiier, clear VL and select books based on a template search virtual_libraries() (or a composite column using that). Then view action.
ownedbycats is offline   Reply With Quote
Old 06-16-2025, 12:25 PM   #77
dunhill
Guru
dunhill ought to be getting tired of karma fortunes by now.dunhill ought to be getting tired of karma fortunes by now.dunhill ought to be getting tired of karma fortunes by now.dunhill ought to be getting tired of karma fortunes by now.dunhill ought to be getting tired of karma fortunes by now.dunhill ought to be getting tired of karma fortunes by now.dunhill ought to be getting tired of karma fortunes by now.dunhill ought to be getting tired of karma fortunes by now.dunhill ought to be getting tired of karma fortunes by now.dunhill ought to be getting tired of karma fortunes by now.dunhill ought to be getting tired of karma fortunes by now.
 
dunhill's Avatar
 
Posts: 911
Karma: 810834
Join Date: Sep 2017
Location: Buenos Aires, Argentina
Device: moon+ reader, kindle paperwhite
Quote:
Originally Posted by theducks View Post
Be prepared to keep being Amazed as to all the things Kovid and his helpers have included in Calibre

IIRC this one has been there since VL's was first added.
It's true that the universe within Calibre is vast.
Okay, Calibre opens from the virtual library, but what I was looking for through action chains was for the book viewer to open books from that virtual library.
dunhill is offline   Reply With Quote
Old 06-16-2025, 12:45 PM   #78
chaley
Grand Sorcerer
chaley ought to be getting tired of karma fortunes by now.chaley ought to be getting tired of karma fortunes by now.chaley ought to be getting tired of karma fortunes by now.chaley ought to be getting tired of karma fortunes by now.chaley ought to be getting tired of karma fortunes by now.chaley ought to be getting tired of karma fortunes by now.chaley ought to be getting tired of karma fortunes by now.chaley ought to be getting tired of karma fortunes by now.chaley ought to be getting tired of karma fortunes by now.chaley ought to be getting tired of karma fortunes by now.chaley ought to be getting tired of karma fortunes by now.
 
Posts: 12,510
Karma: 8065348
Join Date: Jan 2010
Location: Notts, England
Device: Kobo Libra 2
Quote:
Originally Posted by ownedbycats View Post
Can't change VLs directly in AC. I'd do a selection modifiier, clear VL and select books based on a template search virtual_libraries() (or a composite column using that). Then view action.
No need for a template search. In the selection modifier, clear all VLs etc, search "vl:the_vl_name and formats:true", then select all books in view. The formats:true is there to filter out any books that don't have a format.

The attached chain shows how to do it. Change the name of the VL in the selection modifier to what you want.
Attached Files
File Type: zip View books in virtual library.zip (518 Bytes, 29 views)
chaley is offline   Reply With Quote
Old 08-05-2025, 04:01 AM   #79
un_pogaz
Chalut o/
un_pogaz ought to be getting tired of karma fortunes by now.un_pogaz ought to be getting tired of karma fortunes by now.un_pogaz ought to be getting tired of karma fortunes by now.un_pogaz ought to be getting tired of karma fortunes by now.un_pogaz ought to be getting tired of karma fortunes by now.un_pogaz ought to be getting tired of karma fortunes by now.un_pogaz ought to be getting tired of karma fortunes by now.un_pogaz ought to be getting tired of karma fortunes by now.un_pogaz ought to be getting tired of karma fortunes by now.un_pogaz ought to be getting tired of karma fortunes by now.un_pogaz ought to be getting tired of karma fortunes by now.
 
un_pogaz's Avatar
 
Posts: 452
Karma: 678910
Join Date: Dec 2017
Device: Kobo
Re-calc Book Size

A little module to re-calc the size column of a book.
Useful if you've had edited the book files by a third-party program.

Code:
import os

from calibre_plugins.action_chains.actions.base import ChainAction

class ReCalcSize(ChainAction):

    name = 'Re-calc Size'

    def run(self, gui, settings, chain):
        db = gui.current_db.new_api
        ids = chain.scope().get_book_ids()
        rslt = {}
        
        for book_id in ids:
            sizes = [0]
            for fmt in db.fields['formats'].for_book(book_id):
                path = db.format_abspath(book_id, fmt)
                sizes.append(os.path.getsize(path))
            max_size = max(sizes)
            if max_size != db.fields['size'].for_book(book_id):
                rslt[book_id] = max_size
        
        db.fields['size'].table.update_sizes(rslt)
        db.update_last_modified(rslt.keys())
Attached Files
File Type: py Re-calc Size.py (751 Bytes, 15 views)

Last edited by un_pogaz; 08-06-2025 at 04:29 AM.
un_pogaz is offline   Reply With Quote
Old Yesterday, 03:22 PM   #80
ownedbycats
Custom User Title
ownedbycats ought to be getting tired of karma fortunes by now.ownedbycats ought to be getting tired of karma fortunes by now.ownedbycats ought to be getting tired of karma fortunes by now.ownedbycats ought to be getting tired of karma fortunes by now.ownedbycats ought to be getting tired of karma fortunes by now.ownedbycats ought to be getting tired of karma fortunes by now.ownedbycats ought to be getting tired of karma fortunes by now.ownedbycats ought to be getting tired of karma fortunes by now.ownedbycats ought to be getting tired of karma fortunes by now.ownedbycats ought to be getting tired of karma fortunes by now.ownedbycats ought to be getting tired of karma fortunes by now.
 
ownedbycats's Avatar
 
Posts: 11,171
Karma: 77304081
Join Date: Oct 2018
Location: Canada
Device: Kobo Libra H2O, formerly Aura HD
Quote:
Originally Posted by ownedbycats View Post
For FanFicFare users, this chain I made to easily add a new StoryURL section. First it copies this to clipboard:

Code:
## Test Author - Test Title
[https://www.fanfiction.net/s/123456/1/]

Then it opens personal.ini to paste it in.
Here's my enhanced version of the chain that additionally also copies story-specific settings from a column. This allows me to reduce the size of personal.ini by removing settings for completed stories while still keeping them available if I later need them.

to use, create a long-text column (interpret type: plain text) with lookup name #fanficsettings.

As I suggest editing settings in the personal.ini editor rather than the column itself, I also included Calibre ID in the commented header to make it easier to locate the book the section's associated with. An example from my library:

Code:
## stillwaters01 - There Were Days (8382)
[https://archiveofourown.org/works/959950]
exclude_notes:chaptersummary
Attached Files
File Type: zip CopyStoryUrl.zip (2.5 KB, 4 views)
ownedbycats is offline   Reply With Quote
Old Today, 04:45 AM   #81
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,212
Karma: 1995558
Join Date: Aug 2015
Device: Kindle
Here is a custom action that addresses filtering problem, without the need for the user to use template language at all. It allows the following:

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

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

from qt.core import (QWidget, QVBoxLayout, QGroupBox, QRadioButton,
                     QLineEdit, QCheckBox, QLabel)

from calibre import prints
from calibre.constants import DEBUG
from calibre.utils.search_query_parser import ParseException

from calibre_plugins.action_chains.actions.base import ChainAction
from calibre_plugins.action_chains.database import is_search_valid

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)


        search_lbl = QLabel(_('Calibre search'))
        search_lbl.setToolTip(_('Use a regular calibre search'))
        l.addWidget(search_lbl)
        searchbox = self.searchbox = QLineEdit(self)
        searchbox.textChanged.connect(self._on_searchbox_text_change)
        l.addWidget(searchbox)

        selection_groupbox = QGroupBox(_('Selection options'))
        selection_groupbox_layout = QVBoxLayout()
        selection_groupbox.setLayout(selection_groupbox_layout)
        selected_opt = self.selected_opt = QRadioButton(_('Restrict to currently selected books'))
        selection_groupbox_layout.addWidget(selected_opt)
        selected_opt.setChecked(True)
        all_opt = self.all_opt = QRadioButton(_('Act on all books'))
        selection_groupbox_layout.addWidget(all_opt)
        scope_opt = self.scope_opt = QRadioButton(_('Restrict to books in current scope'))
        selection_groupbox_layout.addWidget(scope_opt)
        saved_selection_opt = self.saved_selection_opt = QRadioButton(_('Restrict to books saved in a variable'))
        selection_groupbox_layout.addWidget(saved_selection_opt)
        retrieve_variable_ledit = self.retrieve_variable_ledit = QLineEdit()
        selection_groupbox_layout.addWidget(retrieve_variable_ledit)
        self.l.addWidget(selection_groupbox)

        save_selection_gb = self.save_selection_gb = QGroupBox(_('Save currently selected books into a variable'))
        save_selection_l = QVBoxLayout()
        save_selection_gb.setLayout(save_selection_l)
        save_selection_gb.setCheckable(True)
        save_selection_lbl = QLabel(_('Variable name'))
        save_selection_ledit = self.save_selection_ledit = QLineEdit()
        save_selection_l.addWidget(save_selection_lbl)
        save_selection_l.addWidget(save_selection_ledit)
        l.addWidget(save_selection_gb)

        self.l.addStretch(1)
        self.setMinimumSize(300,400)

    def _on_searchbox_text_change(self):
        text = self.searchbox.text()
        if text:
            ok = is_search_valid(self.db, text)
            ss = 'QLineEdit { border: 2px solid %s; border-radius: 3px }' % (
            '#50c878' if ok else '#FF2400')
            self.searchbox.setStyleSheet(ss)
        else:
            self.searchbox.setStyleSheet('')

    def load_settings(self, settings):
        if settings:
            search_text = settings.get('search_text', '')
            self.searchbox.setText(search_text)
            superset = settings.get('opt', 'selected_books')
            if superset == 'selected_books':
                self.selected_opt.setChecked(True)
            elif superset == 'current_scope':
                self.scope_opt.setChecked(True)
            elif superset == 'all_books':
                self.all_opt.setChecked(True)
            elif superset == 'saved_selection':
                self.saved_selection_opt.setChecked(True)
                self.retrieve_variable_ledit.setText(settings['retrieve_variable'])
            self.save_selection_gb.setChecked(settings['save_selection'])
            self.save_selection_ledit.setText(settings.get('save_variable', ''))

    def save_settings(self):
        settings = {}
        settings['search_text'] = self.searchbox.text()
        if self.selected_opt.isChecked():
            settings['opt'] = 'selected_books'
        elif self.scope_opt.isChecked():
            settings['opt'] = 'current_scope'
        elif self.all_opt.isChecked():
            settings['opt'] = 'all_books'
        elif self.saved_selection_opt.isChecked():
            settings['opt'] = 'saved_selection'
            settings['retrieve_variable'] = self.retrieve_variable_ledit.text()
        settings['save_selection'] = self.save_selection_gb.isChecked()
        if settings['save_selection']:
            settings['save_variable'] = self.save_selection_ledit.text().strip()
        return settings

class SubsetSelector(ChainAction):

    name = 'Subset Selector'
    _is_builtin = False
    support_scopes = True

    def run(self, gui, settings, chain):
        db = gui.current_db
        cache = {}
        # dont save them yet, cache and save later
        if settings['save_selection']:
            cache['current_ids'] = gui.current_view().get_selected_ids()
        superset = settings.get('opt', 'selected_books')
        search_text = settings['search_text'].strip()
        if search_text:
            if superset == 'selected_books':
                if cache.get('current_ids'):
                    superset_ids = cache['current_ids']
                else:
                    superset_ids = gui.current_view().get_selected_ids()
            elif superset == 'current_scope':
                superset_ids = chain.scope().get_book_ids()
            elif superset == 'all_books':
                superset_ids = gui.current_db.all_ids()
            elif superset == 'saved_selection':
                superset_ids = getattr(chain, 'python_vars', {}).get(settings['retrieve_variable'], None)
                if superset_ids == None:
                    prints(f'Action Chains: Subset Selector: {settings["retrieve_variable"]} is empty')
                    superset_ids = []

            try:
                if not search_text:
                    book_ids = superset_ids
                else:
                    search_ids = db.data.search_getting_ids(search_text, '', use_virtual_library=False)
                    if superset == 'all_books':
                        book_ids = search_ids
                    else:
                        book_ids = list(set(search_ids).intersection(superset_ids))
            except ParseException:
                book_ids = []

            gui.current_view().select_rows(book_ids, change_current=True, scroll=False)

        # Avoid saving selected books early in case the opt
        # saved_selection is ticked
        if settings['save_selection']:
            save_variable = settings['save_variable']
            if not hasattr(chain, 'python_vars'):
                chain.python_vars = {}
            chain.python_vars[save_variable] = cache['current_ids']
            chain.chain_vars[save_variable] = ','.join([str(x) for x in chain.python_vars[save_variable]])


    def validate(self, settings):
        gui = self.plugin_action.gui
        db = gui.current_db
        if not settings:
            return (_('Settings Error'), _('You must configure this superset before running it'))
        search_text = settings['search_text']
        if not is_search_valid(db, search_text):
            return (_('Invalid search'), _(f'Search "{search_text}" is not valid'))
        if settings['save_selection'] and not settings.get('save_variable', '').strip():
            return (_('Empty variable'), _('You must choose a variable name to save books'))
        if settings['opt'] == 'saved_selection' and not settings.get('retrieve_variable', '').strip():
            return (_('Empty variable'), _('You must enter saved variable name'))
        return True

    def config_widget(self):
        return ConfigWidget
  • The user can use the action to save current selections to a named variable (e.g. chain_selected_ids, previously_selected_ids, third_action_ids .... etc)
  • The user can restore the selections from a named variable.
  • The user can apply a regular calibre search that will limit the selection to a subset of one of the following:
    • Currently selected books.
    • Book ids stored in a variable
    • Books in current scope

Notes:
  • Regular calibre search allows for using templates. You consult the calibre search documentation, looking for the section named: "Search using templates"
  • The selection is going to be limited by the current library view including the search vls applied. To clear those, you can use the builtin "Selection Modifier". There is no need to duplicate the same functionality.
  • The action is called "Subset Selector" for lack of better name. More descriptive suggestions are welcome.
  • The action will not be part of the stock plugin until it fully matures and has a stable api.
Attached Thumbnails
Click image for larger version

Name:	1.jpg
Views:	5
Size:	43.0 KB
ID:	218136   Click image for larger version

Name:	2.jpg
Views:	5
Size:	38.1 KB
ID:	218137  

Last edited by capink; Today at 08:42 AM.
capink is online now   Reply With Quote
Reply


Forum Jump

Similar Threads
Thread Thread Starter Forum Replies Last Post
[GUI Plugin] Action Chains capink Plugins 1548 Today 10:55 AM
Book Scanning tool chains tomsem Workshop 17 12-03-2023 09:19 AM
Mystery and Crime Thorne, Guy: Chance in Chains (1914); v1 Pulpmeister Kindle Books 0 11-25-2018 09:09 PM
Mystery and Crime Thorne, Guy: Chance in Chains (1914); v1 Pulpmeister ePub Books 0 11-25-2018 09:08 PM
Could this be the last year for the big chains? Connallmac News 66 01-07-2011 04:11 PM


All times are GMT -4. The time now is 01:29 PM.


MobileRead.com is a privately owned, operated and funded community.