From f0a1e1d4713604f597fd1e852281885ce2851666 Mon Sep 17 00:00:00 2001 From: Jim Miller Date: Tue, 8 May 2012 10:43:02 -0500 Subject: [PATCH] Plugin--Add feature to integrate with Generate Cover plugin by kiwidude. --- calibre-plugin/__init__.py | 2 +- calibre-plugin/about.txt | 2 +- calibre-plugin/config.py | 122 ++++++++++++++++++++++---- calibre-plugin/ffdl_plugin.py | 46 +++++++++- fanficdownloader/adapters/__init__.py | 3 + plugin-defaults.ini | 14 +++ 6 files changed, 166 insertions(+), 23 deletions(-) diff --git a/calibre-plugin/__init__.py b/calibre-plugin/__init__.py index bd75cf1..73d517f 100644 --- a/calibre-plugin/__init__.py +++ b/calibre-plugin/__init__.py @@ -27,7 +27,7 @@ class FanFictionDownLoaderBase(InterfaceActionBase): description = 'UI plugin to download FanFiction stories from various sites.' supported_platforms = ['windows', 'osx', 'linux'] author = 'Jim Miller' - version = (1, 5, 16) + version = (1, 5, 18) minimum_calibre_version = (0, 8, 30) #: This field defines the GUI plugin class that contains all the code diff --git a/calibre-plugin/about.txt b/calibre-plugin/about.txt index 9174f8b..9ea9cd0 100644 --- a/calibre-plugin/about.txt +++ b/calibre-plugin/about.txt @@ -1,6 +1,6 @@
-

Created by Jim Miller, borrowing heavily from Grant Drake's +

Plugin created by Jim Miller, borrowing heavily from Grant Drake's 'Reading List', 'Extract ISBN' and 'Count Pages' diff --git a/calibre-plugin/config.py b/calibre-plugin/config.py index 5d33643..35d05a0 100644 --- a/calibre-plugin/config.py +++ b/calibre-plugin/config.py @@ -9,16 +9,17 @@ __docformat__ = 'restructuredtext en' import traceback, copy -from PyQt4.Qt import (QDialog, QWidget, QVBoxLayout, QHBoxLayout, QLabel, QLineEdit, QFont, - QTextEdit, QComboBox, QCheckBox, QPushButton, QTabWidget, QVariant) +from PyQt4.Qt import (QDialog, QWidget, QVBoxLayout, QHBoxLayout, QLabel, QLineEdit, QFont, QWidget, + QTextEdit, QComboBox, QCheckBox, QPushButton, QTabWidget, QVariant, QScrollArea) from calibre.gui2 import dynamic, info_dialog from calibre.utils.config import JSONConfig from calibre.gui2.ui import get_gui from calibre_plugins.fanfictiondownloader_plugin.dialogs \ - import (SKIP, ADDNEW, UPDATE, UPDATEALWAYS, OVERWRITE, OVERWRITEALWAYS, - CALIBREONLY,collision_order) + import (UPDATE, UPDATEALWAYS, OVERWRITE, collision_order) + +from calibre_plugins.fanfictiondownloader_plugin.fanficdownloader.adapters import getSiteDomains from calibre_plugins.fanfictiondownloader_plugin.common_utils \ import ( get_library_uuid, KeyboardConfigDialog ) @@ -53,6 +54,9 @@ all_prefs.defaults['addtolists'] = False all_prefs.defaults['addtoreadlists'] = False all_prefs.defaults['addtolistsonread'] = False +all_prefs.defaults['gc_site_settings'] = {} +all_prefs.defaults['allow_gc_from_ini'] = True + all_prefs.defaults['custom_cols'] = {} # The list of settings to copy from all_prefs or the previous library @@ -67,7 +71,9 @@ copylist = ['personal.ini', 'collision', 'deleteotherforms', 'adddialogstaysontop', - 'includeimages'] + 'includeimages', + 'gc_site_settings', + 'allow_gc_from_ini'] # fake out so I don't have to change the prefs calls anywhere. The # Java programmer in me is offended by op-overloading, but it's very @@ -87,7 +93,8 @@ class PrefsFacade(): self.all_prefs[libraryid] = dict(self._get_copylist_prefs(self.all_prefs)) else: self.all_prefs[libraryid] = dict(self._get_copylist_prefs(self.all_prefs[self.lastlibid])) - self.lastlibid = libraryid + + self.lastlibid = libraryid return self.all_prefs[libraryid] @@ -139,10 +146,15 @@ class ConfigWidget(QWidget): self.personalini_tab = PersonalIniTab(self, plugin_action) tab_widget.addTab(self.personalini_tab, 'personal.ini') - self.list_tab = ListTab(self, plugin_action) - tab_widget.addTab(self.list_tab, 'Reading Lists') + self.readinglist_tab = ReadingListTab(self, plugin_action) + tab_widget.addTab(self.readinglist_tab, 'Reading Lists') if 'Reading List' not in plugin_action.gui.iactions: - self.list_tab.setEnabled(False) + self.readinglist_tab.setEnabled(False) + + self.generatecover_tab = GenerateCoverTab(self, plugin_action) + tab_widget.addTab(self.generatecover_tab, 'Generate Cover') + if 'Generate Cover' not in plugin_action.gui.iactions: + self.generatecover_tab.setEnabled(False) self.columns_tab = ColumnsTab(self, plugin_action) tab_widget.addTab(self.columns_tab, 'Custom Columns') @@ -165,15 +177,15 @@ class ConfigWidget(QWidget): prefs['adddialogstaysontop'] = self.basic_tab.adddialogstaysontop.isChecked() prefs['includeimages'] = self.basic_tab.includeimages.isChecked() - if self.list_tab: + if self.readinglist_tab: # lists - prefs['send_lists'] = ', '.join(map( lambda x : x.strip(), filter( lambda x : x.strip() != '', unicode(self.list_tab.send_lists_box.text()).split(',')))) - prefs['read_lists'] = ', '.join(map( lambda x : x.strip(), filter( lambda x : x.strip() != '', unicode(self.list_tab.read_lists_box.text()).split(',')))) + prefs['send_lists'] = ', '.join(map( lambda x : x.strip(), filter( lambda x : x.strip() != '', unicode(self.readinglist_tab.send_lists_box.text()).split(',')))) + prefs['read_lists'] = ', '.join(map( lambda x : x.strip(), filter( lambda x : x.strip() != '', unicode(self.readinglist_tab.read_lists_box.text()).split(',')))) # print("send_lists: %s"%prefs['send_lists']) # print("read_lists: %s"%prefs['read_lists']) - prefs['addtolists'] = self.list_tab.addtolists.isChecked() - prefs['addtoreadlists'] = self.list_tab.addtoreadlists.isChecked() - prefs['addtolistsonread'] = self.list_tab.addtolistsonread.isChecked() + prefs['addtolists'] = self.readinglist_tab.addtolists.isChecked() + prefs['addtoreadlists'] = self.readinglist_tab.addtoreadlists.isChecked() + prefs['addtolistsonread'] = self.readinglist_tab.addtolistsonread.isChecked() # personal.ini ini = unicode(self.personalini_tab.ini.toPlainText()) @@ -183,6 +195,16 @@ class ConfigWidget(QWidget): # if they've removed everything, reset to default. prefs['personal.ini'] = get_resources('plugin-example.ini') + # Generate Covers tab + gc_site_settings = {} + for (site,combo) in self.generatecover_tab.gc_dropdowns.iteritems(): + val = unicode(combo.itemData(combo.currentIndex()).toString()) + if val != 'none': + gc_site_settings[site] = val + #print("gc_site_settings[%s]:%s"%(site,gc_site_settings[site])) + prefs['gc_site_settings'] = gc_site_settings + prefs['allow_gc_from_ini'] = self.generatecover_tab.allow_gc_from_ini.isChecked() + # Custom Columns tab colsmap = {} for (col,combo) in self.columns_tab.custcol_dropdowns.iteritems(): @@ -191,7 +213,7 @@ class ConfigWidget(QWidget): colsmap[col] = val #print("colsmap[%s]:%s"%(col,colsmap[col])) prefs['custom_cols'] = colsmap - + def edit_shortcuts(self): self.save_settings() # Force the menus to be rebuilt immediately, so we have all our actions registered @@ -286,7 +308,7 @@ class BasicTab(QWidget): self.includeimages.setToolTip("Download and include images in EPUB stories. This is equivalent to adding:\n\n[epub]\ninclude_images:true\nkeep_summary_html:true\nmake_firstimage_cover:true\n\n ...to the top of personal.ini. Your settings in personal.ini will override this.") self.includeimages.setChecked(prefs['includeimages']) self.l.addWidget(self.includeimages) - + self.l.insertStretch(-1) def set_collisions(self): @@ -372,7 +394,7 @@ class ShowDefaultsIniDialog(QDialog): self.ok_button.clicked.connect(self.hide) self.l.addWidget(self.ok_button) -class ListTab(QWidget): +class ReadingListTab(QWidget): def __init__(self, parent_dialog, plugin_action): self.parent_dialog = parent_dialog @@ -432,6 +454,70 @@ class ListTab(QWidget): self.l.insertStretch(-1) +class GenerateCoverTab(QWidget): + + def __init__(self, parent_dialog, plugin_action): + self.parent_dialog = parent_dialog + self.plugin_action = plugin_action + QWidget.__init__(self) + + self.l = QVBoxLayout() + self.setLayout(self.l) + + try: + gc_plugin = plugin_action.gui.iactions['Generate Cover'] + gc_settings = gc_plugin.get_saved_setting_names() + except KeyError: + gc_settings= [] + + label = QLabel('The Generate Cover plugin can create cover images for books using various metadata and configurations. If you have GC installed, FFDL can run GC on new downloads and metadata updates. Pick a GC setting by site or Default.') + label.setWordWrap(True) + self.l.addWidget(label) + self.l.addSpacing(5) + + scrollable = QScrollArea() + scrollcontent = QWidget() + scrollable.setWidget(scrollcontent) + scrollable.setWidgetResizable(True) + self.l.addWidget(scrollable) + + self.sl = QVBoxLayout() + scrollcontent.setLayout(self.sl) + + self.gc_dropdowns = {} + + sitelist = getSiteDomains() + sitelist.sort() + sitelist.insert(0,u"Default") + for site in sitelist: + horz = QHBoxLayout() + label = QLabel(site) + if site == u"Default": + s = "On Metadata update, run Generate Cover with this setting, if not selected for specific site." + else: + s = "On Metadata update, run Generate Cover with this setting for site (%s)."%site + + label.setToolTip(s) + horz.addWidget(label) + dropdown = QComboBox(self) + dropdown.setToolTip(s) + dropdown.addItem('',QVariant('none')) + for setting in gc_settings: + dropdown.addItem(setting,QVariant(setting)) + self.gc_dropdowns[site] = dropdown + if site in prefs['gc_site_settings']: + dropdown.setCurrentIndex(dropdown.findData(QVariant(prefs['gc_site_settings'][site]))) + + horz.addWidget(dropdown) + self.sl.addLayout(horz) + + self.allow_gc_from_ini = QCheckBox('Allow generate_cover_settings from personal.ini to override.',self) + self.allow_gc_from_ini.setToolTip("The INI parameter generate_cover_settings allows you to choose a GC setting based on metadata rather than site,\nbut it's much more complex. generate_cover_settings is ignored when this is off.") + self.allow_gc_from_ini.setChecked(prefs['allow_gc_from_ini']) + self.l.addWidget(self.allow_gc_from_ini) + + self.l.insertStretch(-1) + class OtherTab(QWidget): def __init__(self, parent_dialog, plugin_action): diff --git a/calibre-plugin/ffdl_plugin.py b/calibre-plugin/ffdl_plugin.py index 1c73fc3..a2d2e81 100644 --- a/calibre-plugin/ffdl_plugin.py +++ b/calibre-plugin/ffdl_plugin.py @@ -7,11 +7,12 @@ __license__ = 'GPL v3' __copyright__ = '2012, Jim Miller' __docformat__ = 'restructuredtext en' -import time, os, copy, threading +import time, os, copy, threading, re from ConfigParser import SafeConfigParser from StringIO import StringIO from functools import partial from datetime import datetime +from string import Template from PyQt4.Qt import (QApplication, QMenu, QToolButton) @@ -772,7 +773,6 @@ make_firstimage_cover:true epubmi = get_metadata(existingepub,'EPUB') if epubmi.cover_data[1] is not None: db.set_cover(book_id, epubmi.cover_data[1]) - #mi.cover = epubmi.cover_data[1] # set author link if found. All current adapters have authorUrl. if 'authorUrl' in book['all_metadata']: @@ -812,8 +812,48 @@ make_firstimage_cover:true if meta == 'status-I': val = book['all_metadata']['status'] == 'In-Progress' db.set_custom(book_id, val, label=label, commit=False) - + db.commit() + + if 'Generate Cover' in self.gui.iactions: + + gc_plugin = self.gui.iactions['Generate Cover'] + setting_name = None + if prefs['allow_gc_from_ini']: + ffdlconfig = SafeConfigParser() + ffdlconfig.readfp(StringIO(get_resources("plugin-defaults.ini"))) + ffdlconfig.readfp(StringIO(prefs['personal.ini'])) + adapter = adapters.getAdapter(ffdlconfig,book['url'],options['fileform']) + + # template => regexp to match => GC Setting to use. + # generate_cover_settings: + # ${category} => Buffy:? the Vampire Slayer => Buffy + for line in adapter.getConfig('generate_cover_settings').splitlines(): + if "=>" in line: + (template,regexp,setting) = map( lambda x: x.strip(), line.split("=>") ) + value = Template(template).substitute(book['all_metadata']).encode('utf8') + print("%s(%s) => %s => %s"%(template,value,regexp,setting)) + if re.search(regexp,value): + setting_name = setting + break + + if setting_name: + print("Generate Cover Setting from generate_cover_settings(%s)"%line) + if setting_name not in gc_plugin.get_saved_setting_names(): + print("GC Name %s not found, discarding! (check personal.ini for typos)"%setting_name) + setting_name = None + + if not setting_name and book['all_metadata']['site'] in prefs['gc_site_settings']: + setting_name = prefs['gc_site_settings'][book['all_metadata']['site']] + + if not setting_name and 'Default' in prefs['gc_site_settings']: + setting_name = prefs['gc_site_settings']['Default'] + + if setting_name: + print("Running Generate Cover with settings %s."%setting_name) + realmi = db.get_metadata(book_id, index_is_id=True) + gc_plugin.generate_cover_for_book(realmi,saved_setting_name=setting_name) + def _get_clean_reading_lists(self,lists): if lists == None or lists.strip() == "" : diff --git a/fanficdownloader/adapters/__init__.py b/fanficdownloader/adapters/__init__.py index e8eeb85..51c9a0c 100644 --- a/fanficdownloader/adapters/__init__.py +++ b/fanficdownloader/adapters/__init__.py @@ -100,6 +100,9 @@ def getAdapter(config,url,fileform=None): # No adapter found. raise exceptions.UnknownSite( url, [cls.getSiteDomain() for cls in __class_list] ) +def getSiteDomains(): + return [cls.getSiteDomain() for cls in __class_list] + def getClassFor(domain): for cls in __class_list: if cls.matchesSite(domain): diff --git a/plugin-defaults.ini b/plugin-defaults.ini index 2dedcee..ae955c2 100644 --- a/plugin-defaults.ini +++ b/plugin-defaults.ini @@ -127,6 +127,20 @@ extratags: FanFiction ## doesn't work on some devices either.) #replace_hr: false +## If you have the Generate Cover plugin installed, you can use the +## generate_cover_settings parameter to intelligently decide which GC +## setting to run. There are three parts 1) a template of which +## metadata part(s) to look at, 2) a regular expression to match the +## template, and 3) the name of the GC setting to use, which must +## match exactly. Use this parameter in [defaults], or by site eg, +## [www.ficwad.com] +## Make sure to keep at least one space at the start of each line and +## to escape % to %%, if used. +## template => regexp to match => GC Setting to use. +#generate_cover_settings: +# ${category} => Buffy:? [tT]he Vampire Slayer => BuffyCover +# ${category} => Star Trek => StarTrekCover + ## Each output format has a section that overrides [defaults] [html]