![]() |
#1 |
Enthusiast
![]() Posts: 39
Karma: 10
Join Date: Dec 2021
Device: Kindle Oasis
|
Word Count in Calibre?
Is there any way to get Calibre to automatically add a column for word counts in each book? That way I can tally them all up at the end of the year and know how many words I’ve read instead of counting on page counts which is useless from a statistic standpoint.
Bonus point if it can add that when I do the metadata search while inputting a book. |
![]() |
![]() |
![]() |
#2 |
null operator (he/him)
![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() Posts: 21,731
Karma: 29711016
Join Date: Mar 2012
Location: Sydney Australia
Device: none
|
Count Pages plugin can provide word counts, you can probably automate via the Actions Chains plugin ==>> Index of plugins
BR |
![]() |
![]() |
![]() |
#3 |
want to learn what I want
![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() Posts: 1,611
Karma: 7891011
Join Date: Sep 2020
Device: none
|
I have a Words column populated by Count Pages Plugin:
https://github.com/kiwidude68/calibr...-custom-column |
![]() |
![]() |
![]() |
#4 | |
Enthusiast
![]() Posts: 39
Karma: 10
Join Date: Dec 2021
Device: Kindle Oasis
|
Quote:
|
|
![]() |
![]() |
![]() |
#5 |
want to learn what I want
![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() Posts: 1,611
Karma: 7891011
Join Date: Sep 2020
Device: none
|
With the following settings, it's a single-click operation:
It does the count in this case, doesn't get it from anywhere (very fast by the way, thanks @kiwidude). Actually I never used the "Download from sources" option but it's necessary for Pbooks. |
![]() |
![]() |
![]() |
#6 |
null operator (he/him)
![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() Posts: 21,731
Karma: 29711016
Join Date: Mar 2012
Location: Sydney Australia
Device: none
|
@Comfy.n - For me it's a single keystroke — Alt+C
![]() Could Action Chains fire up WC after a metadata download? BR |
![]() |
![]() |
![]() |
#7 |
Enthusiast
![]() Posts: 39
Karma: 10
Join Date: Dec 2021
Device: Kindle Oasis
|
|
![]() |
![]() |
![]() |
#8 | |
Enthusiast
![]() Posts: 39
Karma: 10
Join Date: Dec 2021
Device: Kindle Oasis
|
Quote:
|
|
![]() |
![]() |
![]() |
#9 |
want to learn what I want
![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() Posts: 1,611
Karma: 7891011
Join Date: Sep 2020
Device: none
|
First make sure you have the Count Page columns as instructed in the plugin Wiki.
Then install Action Chains and create the Download Metadata module: This is the module code: Code:
#!/usr/bin/env python # ~*~ coding: utf-8 ~*~ __license__ = 'GPL v3' __copyright__ = '2022, Ahmed Zaki <azaki00.dev@gmail.com>' __docformat__ = 'restructuredtext en' from qt.core import (QApplication, Qt, QWidget, QVBoxLayout, QCheckBox, QGroupBox, QRadioButton, QAction) import copy import types from functools import partial from calibre import prints from calibre.constants import DEBUG from calibre.gui2 import Dispatcher, error_dialog from calibre.ptempfile import PersistentTemporaryFile from calibre.gui2.metadata.bulk_download import Job, download from calibre.gui2.actions.edit_metadata import EditMetadataAction from polyglot.builtins import iteritems from calibre_plugins.action_chains.actions.base import ChainAction from calibre_plugins.action_chains.common_utils import responsive_wait, responsive_wait_until def unfinished_job_ids(gui): return set([job.id for job in gui.job_manager.unfinished_jobs()]) class ModifiedEditMetadataAction(EditMetadataAction): name = 'Modified Edit Metadata' action_spec = (_('Modified Edit metadata'), 'edit_input.png', _('Change the title/author/cover etc. of books'), _('')) action_type = 'current' action_add_menu = True def __init__(self, parent, site_customization): EditMetadataAction.__init__(self, parent, site_customization) self.do_genesis() @property def unique_name(self): bn = self.__class__.__name__ return 'Interface Action: %s (%s)'%(bn, self.name) def genesis(self): pass def location_selected(self, loc): pass def library_changed(self): pass def shutting_down(self): pass def metadata_downloaded(self, job): if job.failed: self.gui.job_exception(job, dialog_title=_('Failed to download metadata')) return from calibre.gui2.metadata.bulk_download import get_job_details (aborted, id_map, tdir, log_file, failed_ids, failed_covers, all_failed, det_msg, lm_map) = get_job_details(job) if aborted: return self.cleanup_bulk_download(tdir) if all_failed: num = len(failed_ids | failed_covers) self.cleanup_bulk_download(tdir) return error_dialog(self.gui, _('Download failed'), ngettext( 'Failed to download metadata or cover for the selected book.', 'Failed to download metadata or covers for any of the {} books.', num ).format(num), det_msg=det_msg, show=True) self.gui.status_bar.show_message(_('Metadata download completed'), 3000) msg = '<p>' + ngettext( 'Finished downloading metadata for the selected book.', 'Finished downloading metadata for <b>{} books</b>.', len(id_map)).format(len(id_map)) + ' ' + \ _('Proceed with updating the metadata in your library?') show_copy_button = False checkbox_msg = None if failed_ids or failed_covers: show_copy_button = True num = len(failed_ids.union(failed_covers)) msg += '<p>'+_('Could not download metadata and/or covers for %d of the books. Click' ' "Show details" to see which books.')%num checkbox_msg = _('Show the &failed books in the main book list ' 'after updating metadata') if getattr(job, 'metadata_and_covers', None) == (False, True): # Only covers, remove failed cover downloads from id_map for book_id in failed_covers: if hasattr(id_map, 'discard'): id_map.discard(book_id) payload = (id_map, tdir, log_file, lm_map, failed_ids.union(failed_covers)) if self.do_review: QApplication.setOverrideCursor(Qt.ArrowCursor) try: self.apply_downloaded_metadata(True, payload, self.restrict_to_failed) finally: QApplication.restoreOverrideCursor() else: self.apply_downloaded_metadata(False, payload, self.restrict_to_failed) 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) opt_gb = QGroupBox(_('Options')) opt_gb_l = QVBoxLayout() opt_gb.setLayout(opt_gb_l) l.addWidget(opt_gb) self.metadata_opt = QRadioButton(_('Download Metadata')) self.covers_opt = QRadioButton(_('Download Covers')) self.both_opt = QRadioButton(_('Download Both')) self.both_opt.setChecked(True) opt_gb_l.addWidget(self.metadata_opt) opt_gb_l.addWidget(self.covers_opt) opt_gb_l.addWidget(self.both_opt) self.review_chk = QCheckBox(_('Review downloaded metadata before applying them')) self.wait_chk = QCheckBox(_('Wait for metadata download jobs to finish')) self.wait_chk.setToolTip(_('Check this if this action in not the last action in the chain.')) l.addWidget(self.review_chk) l.addWidget(self.wait_chk) l.addStretch(1) self.setMinimumSize(500,300) def load_settings(self, settings): if settings: self.metadata_opt.setChecked(settings.get('download_metadata', False)) self.covers_opt.setChecked(settings.get('download_covers', False)) self.both_opt.setChecked(settings.get('download_both', True)) self.review_chk.setChecked(settings.get('review', True)) self.wait_chk.setChecked(settings.get('wait_jobs', False)) def save_settings(self): settings = {} settings['download_metadata'] = self.metadata_opt.isChecked() settings['download_covers'] = self.covers_opt.isChecked() settings['download_both'] = self.both_opt.isChecked() settings['review'] = self.review_chk.isChecked() settings['wait_jobs'] = self.wait_chk.isChecked() return settings class DownloadMetadata(ChainAction): name = 'Download Metadata' support_scopes = True def run(self, gui, settings, chain): identify = settings.get('download_metadata') or settings.get('download_both', True) covers = settings.get('download_covers') or settings.get('download_both', True) wait_jobs = settings.get('wait_jobs', False) ensure_fields = None edit_metadata = ModifiedEditMetadataAction(gui, '') edit_metadata.do_review = settings.get('review', True) edit_metadata.restrict_to_failed = settings.get('restrict_to_failed', True) callback = Dispatcher(edit_metadata.metadata_downloaded) ids = chain.scope().get_book_ids() if len(ids) == 0: return error_dialog(gui, _('Cannot download metadata'), _('No books selected'), show=True) jobs_before_ids = unfinished_job_ids(gui) tf = PersistentTemporaryFile('_metadata_bulk.log') tf.close() job = Job('metadata bulk download', ngettext( 'Download metadata for one book', 'Download metadata for {} books', len(ids)).format(len(ids)), download, (ids, tf.name, gui.current_db, identify, covers, ensure_fields), {}, callback) job.metadata_and_covers = (identify, covers) job.download_debug_log = tf.name gui.job_manager.run_threaded_job(job) gui.status_bar.show_message(_('Metadata download started'), 3000) if wait_jobs: # wait for jobs spawned by action to kick in responsive_wait(1) # save ids of jobs started after running the action ids_jobs_by_action = unfinished_job_ids(gui).difference(jobs_before_ids) # wait for jobs to finish responsive_wait_until(lambda: ids_jobs_by_action.intersection(unfinished_job_ids(gui)) == set()) def validate(self, settings): #if not settings: #return (_('Settings Error'), _('You must configure this action before running it')) return True def config_widget(self): return ConfigWidget Then import the attached chain this way: I tested it and it works fine on a Calibre portable install. |
![]() |
![]() |
![]() |
#10 | |
want to learn what I want
![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() Posts: 1,611
Karma: 7891011
Join Date: Sep 2020
Device: none
|
Quote:
|
|
![]() |
![]() |
![]() |
#11 | |
Enthusiast
![]() Posts: 39
Karma: 10
Join Date: Dec 2021
Device: Kindle Oasis
|
Quote:
In your experience, which is the most accurate count? I'd assume having the book file itself scanned, but that's just a guess. |
|
![]() |
![]() |
![]() |
#12 |
want to learn what I want
![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() Posts: 1,611
Karma: 7891011
Join Date: Sep 2020
Device: none
|
The Ebook Viewer algorithm is what I've only ever used, as I want the words/pages count columns just for comparison and filtering/searching. And for this purpose it is accurate as it reflects the exact number of pages displayed by the Viewer.
Also, at one point I had a custom column like the one in this thread: https://www.mobileread.com/forums/sh...d.php?t=311272 |
![]() |
![]() |
![]() |
|
![]() |
||||
Thread | Thread Starter | Forum | Replies | Last Post |
Add page count, word count and reading time | ZodWallop | Kobo Reader | 4 | 08-12-2024 05:56 AM |
Word Count and Page Count? | CrossReach | Library Management | 2 | 07-19-2018 05:44 PM |
Word count in calibre | C-novice | Library Management | 2 | 03-11-2018 10:57 PM |
Word Count? | noirverse | Marvin | 0 | 11-11-2016 08:23 PM |
Possible to get a word count in Calibre? | Notjohn | Calibre | 15 | 01-23-2016 06:20 PM |