|
|
#76 | |
|
Custom User Title
![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() Posts: 11,364
Karma: 79528341
Join Date: Oct 2018
Location: Canada
Device: Kobo Libra H2O, formerly Aura HD
|
Quote:
|
|
|
|
|
|
|
#77 | |
|
Guru
![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() Posts: 923
Karma: 810834
Join Date: Sep 2017
Location: Buenos Aires, Argentina
Device: moon+ reader, kindle paperwhite
|
Quote:
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. |
|
|
|
|
|
|
#78 | |
|
Grand Sorcerer
![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() Posts: 12,525
Karma: 8065948
Join Date: Jan 2010
Location: Notts, England
Device: Kobo Libra 2
|
Quote:
The attached chain shows how to do it. Change the name of the VL in the selection modifier to what you want. |
|
|
|
|
|
|
#79 |
|
Chalut o/
![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() Posts: 486
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. EDIT: If your have Quality Check 1.14.3 and above installed, you can use directly the same action in "Imported From Other Plugin > Quality Check > Run Quality Check fix" and select "Set author sort" or "Set title sort". To have both, you need two distinct actions. 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())
Last edited by un_pogaz; 10-05-2025 at 04:21 AM. |
|
|
|
|
|
#80 | |
|
Custom User Title
![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() Posts: 11,364
Karma: 79528341
Join Date: Oct 2018
Location: Canada
Device: Kobo Libra H2O, formerly Aura HD
|
Quote:
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 |
|
|
|
|
|
|
#81 |
|
Wizard
![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() Posts: 1,216
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
Notes:
Last edited by capink; 09-18-2025 at 03:42 PM. |
|
|
|
![]() |
|
Similar Threads
|
||||
| Thread | Thread Starter | Forum | Replies | Last Post |
| [GUI Plugin] Action Chains | capink | Plugins | 1556 | 10-26-2025 02:09 PM |
| Book Scanning tool chains | tomsem | Workshop | 17 | 12-03-2023 10:19 AM |
| Mystery and Crime Thorne, Guy: Chance in Chains (1914); v1 | Pulpmeister | Kindle Books | 0 | 11-25-2018 10:09 PM |
| Mystery and Crime Thorne, Guy: Chance in Chains (1914); v1 | Pulpmeister | ePub Books | 0 | 11-25-2018 10:08 PM |
| Could this be the last year for the big chains? | Connallmac | News | 66 | 01-07-2011 05:11 PM |