diff --git a/calibre-plugin/config.py b/calibre-plugin/config.py index 8a3789d1..bf2791b0 100644 --- a/calibre-plugin/config.py +++ b/calibre-plugin/config.py @@ -10,8 +10,10 @@ __docformat__ = 'restructuredtext en' import traceback, copy, threading from collections import OrderedDict -from PyQt4.Qt import (QDialog, QWidget, QVBoxLayout, QHBoxLayout, QLabel, QLineEdit, QFont, QWidget, - QTextEdit, QComboBox, QCheckBox, QPushButton, QTabWidget, QVariant, QScrollArea) +from PyQt4.Qt import (QDialog, QWidget, QVBoxLayout, QHBoxLayout, QLabel, + QLineEdit, QFont, QWidget, QTextEdit, QComboBox, + QCheckBox, QPushButton, QTabWidget, QVariant, QScrollArea, + QDialogButtonBox ) from calibre.gui2 import dynamic, info_dialog from calibre.utils.config import JSONConfig @@ -35,6 +37,9 @@ PREFS_KEY_SETTINGS = 'settings' default_prefs = {} default_prefs['personal.ini'] = get_resources('plugin-example.ini') default_prefs['rejecturls'] = '' +default_prefs['rejectreasons'] = '''Sucked +Boring +Dup from another site''' default_prefs['updatemeta'] = True default_prefs['updatecover'] = False @@ -199,6 +204,9 @@ class RejectURLList: def get_list(self): return copy.deepcopy(self._get_listcache()) + def get_reject_reasons(self): + return self.prefs['rejectreasons'].splitlines() + rejecturllist = RejectURLList(prefs) class ConfigWidget(QWidget): @@ -462,10 +470,19 @@ class BasicTab(QWidget): self.l.addSpacing(10) - self.rejectlist = QPushButton('View Reject URL List', self) - self.rejectlist.setToolTip("View list of URLs FFDL will automatically Reject.") + horz = QHBoxLayout() + + self.rejectlist = QPushButton('Edit Reject URL List', self) + self.rejectlist.setToolTip("Edit list of URLs FFDL will automatically Reject.") self.rejectlist.clicked.connect(self.show_rejectlist) - self.l.addWidget(self.rejectlist) + horz.addWidget(self.rejectlist) + + self.reject_reasons = QPushButton('Edit Reject Reasons List', self) + self.reject_reasons.setToolTip("Customize the Reasons presented when Rejecting URLs") + self.reject_reasons.clicked.connect(self.show_reject_reasons) + horz.addWidget(self.reject_reasons) + + self.l.addLayout(horz) self.l.insertStretch(-1) @@ -486,10 +503,11 @@ class BasicTab(QWidget): def show_rejectlist(self): rejectlist = [] for (url,note) in rejecturllist.get_list().items(): - rejectlist.append((None,url,note)) + rejectlist.append((None,url,note,note)) d = RejectListDialog(self, rejectlist, + rejectreasons=rejecturllist.get_reject_reasons(), header="Edit Reject URLs List", show_delete=False) d.exec_() @@ -503,6 +521,40 @@ class BasicTab(QWidget): rejecturllist.add(rejectlist,clear=True) + def show_reject_reasons(self): + print("rejectreasons:%s"%prefs['rejectreasons']) + d = RejectReasonsDialog(self.windowIcon(),prefs['rejectreasons'],self) + d.exec_() + if d.result() == d.Accepted: + prefs['rejectreasons'] = unicode(d.reasons.toPlainText()) + print("rejectreasons:%s"%prefs['rejectreasons']) + + +class RejectReasonsDialog(QDialog): + + def __init__(self, icon, text, parent=None): + QDialog.__init__(self, parent) + self.resize(600, 500) + self.l = QVBoxLayout() + self.setLayout(self.l) + self.label = QLabel("Customize Reject List Reasons") + tooltip="Customize the Reasons presented when Rejecting URLs" + self.label.setToolTip(tooltip) + self.setWindowTitle(_('Reject Reasons')) + self.setWindowIcon(icon) + self.l.addWidget(self.label) + + self.reasons = QTextEdit(self) + self.reasons.setToolTip(tooltip) + self.reasons.setLineWrapMode(QTextEdit.NoWrap) + self.reasons.setText(text) + self.l.addWidget(self.reasons) + + button_box = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Cancel) + button_box.accepted.connect(self.accept) + button_box.rejected.connect(self.reject) + self.l.addWidget(button_box) + class PersonalIniTab(QWidget): def __init__(self, parent_dialog, plugin_action): @@ -542,34 +594,6 @@ class PersonalIniTab(QWidget): def show_defaults(self): text = get_resources('plugin-defaults.ini') ShowDefaultsIniDialog(self.windowIcon(),text,self).exec_() - -# class RejectUrlsTab(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) - -# label = QLabel("List of story URLs you've previously rejected followed by an optional note. FFDL will stop and ask you if try to download a story on your reject list. The system will put title, author and why you rejected it when added from the FFDL 'Reject Story' option.") -# label.setWordWrap(True) -# self.l.addWidget(label) -# self.l.addSpacing(5) - -# self.label = QLabel('Rejected URLs: (URL,Notes)') -# self.l.addWidget(self.label) - -# self.rejecturls = QTextEdit(self) -# try: -# self.rejecturls.setFont(QFont("Courier", -# self.plugin_action.gui.font().pointSize()+1)); -# except Exception as e: -# print("Couldn't get font: %s"%e) -# self.rejecturls.setLineWrapMode(QTextEdit.NoWrap) -# self.rejecturls.setText(prefs['rejecturls']) -# self.l.addWidget(self.rejecturls) class ShowDefaultsIniDialog(QDialog): diff --git a/calibre-plugin/dialogs.py b/calibre-plugin/dialogs.py index 8c2aee0e..33211dc8 100644 --- a/calibre-plugin/dialogs.py +++ b/calibre-plugin/dialogs.py @@ -8,6 +8,7 @@ __copyright__ = '2011, Jim Miller' __docformat__ = 'restructuredtext en' import traceback +from functools import partial from PyQt4 import QtGui from PyQt4.Qt import (QDialog, QTableWidget, QMessageBox, QVBoxLayout, QHBoxLayout, @@ -19,6 +20,7 @@ from PyQt4.Qt import (QDialog, QTableWidget, QMessageBox, QVBoxLayout, QHBoxLayo from calibre.gui2 import error_dialog, warning_dialog, question_dialog, info_dialog from calibre.gui2.dialogs.confirm_delete import confirm +from calibre.gui2.complete2 import EditWithComplete from calibre import confirm_config_name from calibre.gui2 import dynamic @@ -717,9 +719,10 @@ class StoryListTableWidget(QTableWidget): class RejectListTableWidget(QTableWidget): - def __init__(self, parent): + def __init__(self, parent,rejectreasons=[]): QTableWidget.__init__(self, parent) self.setSelectionBehavior(QAbstractItemView.SelectRows) + self.rejectreasons = rejectreasons def on_headersection_clicked(self): self.setSortingEnabled(True) @@ -755,22 +758,58 @@ class RejectListTableWidget(QTableWidget): def populate_table_row(self, row, rejectrow): - (bookid,url,note) = rejectrow + (bookid,url,titleauth,oldrejnote) = rejectrow + if oldrejnote: + noteprefix = note = oldrejnote + # incase the existing note ends with one of the known reasons. + for reason in self.rejectreasons: + if noteprefix.endswith(' - '+reason): + noteprefix = noteprefix[:-len(' - '+reason)] + break + else: + noteprefix = note = titleauth + url_cell = ReadOnlyTableWidgetItem(url) url_cell.setData(Qt.UserRole, QVariant(bookid)) url_cell.setToolTip('URL to add to the Reject List.') self.setItem(row, 0, url_cell) - - note_cell = QTableWidgetItem(note) - note_cell.setToolTip('Double-click to edit note.') - self.setItem(row, 1, note_cell) + + note_cell = EditWithComplete(self) + + # This is a more than slightly kludgey way to get + # EditWithComplete to *not* alpha-order the reasons, but leave + # them in the order entered. If + # calibre.gui2.complete2.CompleteModel.set_items ever changes, + # this function will need to also. + def complete_model_set_items_kludge(self, items): + items = [unicode(x.strip()) for x in items] + items = [x for x in items if x] + items = tuple(items) + self.all_items = self.current_items = items + self.current_prefix = '' + self.reset() + + note_cell.lineEdit().mcompleter.model().set_items = \ + partial(complete_model_set_items_kludge, + note_cell.lineEdit().mcompleter.model()) + + items = [note]+[ noteprefix+" - "+x for x in self.rejectreasons ] + note_cell.update_items_cache(items) + note_cell.show_initial_value(note) + note_cell.set_separator(None) + note_cell.setToolTip('Select or Edit Reject Note.') + self.setCellWidget(row, 1, note_cell) + + # note_cell = QTableWidgetItem(note) + # note_cell.setToolTip('Double-click to edit note.') + # self.setItem(row, 1, note_cell) def get_reject_list(self): rejectrows = [] for row in range(self.rowCount()): bookid = self.item(row, 0).data(Qt.UserRole).toPyObject() url = unicode(self.item(row, 0).text()) - note = unicode(self.item(row, 1).text()) + note = unicode(self.cellWidget(row, 1).currentText()).strip() rejectrows.append((bookid,url,note)) return rejectrows @@ -849,6 +888,7 @@ class RejectListTableWidget(QTableWidget): class RejectListDialog(SizePersistedDialog): def __init__(self, gui, reject_list, + rejectreasons=[], header="List of Books to Reject", icon='rotate-right.png', show_delete=True, @@ -867,7 +907,7 @@ class RejectListDialog(SizePersistedDialog): rejects_layout = QHBoxLayout() layout.addLayout(rejects_layout) - self.rejects_table = RejectListTableWidget(self) + self.rejects_table = RejectListTableWidget(self,rejectreasons=rejectreasons) rejects_layout.addWidget(self.rejects_table) button_layout = QVBoxLayout() @@ -919,4 +959,3 @@ class RejectListDialog(SizePersistedDialog): def get_deletebooks(self): return self.deletebooks.isChecked() - diff --git a/calibre-plugin/ffdl_plugin.py b/calibre-plugin/ffdl_plugin.py index 2e83f164..55aec83b 100644 --- a/calibre-plugin/ffdl_plugin.py +++ b/calibre-plugin/ffdl_plugin.py @@ -347,10 +347,6 @@ class FanFictionDownLoaderPlugin(InterfaceAction): show_copy_button=False) def reject_list_urls(self): - if self.gui.current_view().selectionModel().selectedRows() == 0 : - self.gui.status_bar.show_message(_('No Selected Books to Get URLs From'), 3000) - return - if self.is_library_view(): book_list = map( partial(self._convert_id_to_book, good=False), self.gui.library_view.get_selected_ids() ) @@ -361,6 +357,10 @@ class FanFictionDownLoaderPlugin(InterfaceAction): #paths = view.model().paths(rows) book_list = map( partial(self._convert_row_to_book, good=False), rows ) + if len(book_list) == 0 : + self.gui.status_bar.show_message(_('No Selected Books have URLs to Reject'), 3000) + return + LoopProgressDialog(self.gui, book_list, partial(self._reject_story_url_for_list, db=self.gui.current_db), @@ -375,21 +375,27 @@ class FanFictionDownLoaderPlugin(InterfaceAction): self._populate_book_from_calibre_id(book,db) book['url'] = self._get_story_url(db,book_id=book['calibre_id']) elif book['path']: - book['url'] = self._get_story_url(db,path=book['path']) if book['url'] == None: book['good']=False else: book['good']=True + # get existing note, if there is one. + book['oldrejnote']=rejecturllist.check(book['url']) def _finish_reject_list_urls(self, book_list): - # construct reject list of (calibre_id, url, note) tuples. - reject_list = [ (x['calibre_id'],x['url'], - "%s by %s"%(x['title'], - ', '.join(x['author']))) for x in book_list if x['good'] ] + + # construct reject list of tuples: + # (calibre_id, url, "title, authors", old reject note). + reject_list = [ ( x['calibre_id'],x['url'], + "%s by %s"%(x['title'], + ', '.join(x['author'])), + x['oldrejnote']) + for x in book_list if x['good'] ] if reject_list: - d = RejectListDialog(self.gui,reject_list) + d = RejectListDialog(self.gui,reject_list, + rejectreasons=rejecturllist.get_reject_reasons()) d.exec_() if d.result() != d.Accepted: @@ -400,6 +406,7 @@ class FanFictionDownLoaderPlugin(InterfaceAction): for (bookid,url,note) in d.get_reject_list(): bookids.append(bookid) rejectlist.append((url,note)) + print("Adding (%s) to Reject List: %s"%(url,note)) rejecturllist.add(rejectlist) @@ -1369,11 +1376,11 @@ make_firstimage_cover:true identifiers = db.get_identifiers(book_id,index_is_id=True) if 'url' in identifiers: # identifiers have :->| in url. - print("url from ident url:"+identifiers['url'].replace('|',':')) + # print("url from ident url:%s"%identifiers['url'].replace('|',':')) return identifiers['url'].replace('|',':') elif 'uri' in identifiers: # identifiers have :->| in uri. - print("uri from ident uri:"+identifiers['uri'].replace('|',':')) + # print("uri from ident uri:%s"%identifiers['uri'].replace('|',':')) return identifiers['uri'].replace('|',':') else: existingepub = None @@ -1382,7 +1389,7 @@ make_firstimage_cover:true mi = get_metadata(existingepub,'EPUB') identifiers = mi.get_identifiers() if 'url' in identifiers: - print("url from get_metadata:"+identifiers['url'].replace('|',':')) + # print("url from get_metadata:%s"%identifiers['url'].replace('|',':')) return identifiers['url'].replace('|',':') elif path.lower().endswith('.epub'): existingepub = path @@ -1392,11 +1399,11 @@ make_firstimage_cover:true # look for dc:source first, then scan HTML if lookforurlinhtml link = get_dcsource(existingepub) if link: - print("url from get_dcsource:"+link) + # print("url from get_dcsource:%s"%link) return link elif prefs['lookforurlinhtml']: link = get_story_url_from_html(existingepub,self._is_good_downloader_url) - print("url from get_story_url_from_html:"+link) + # print("url from get_story_url_from_html:%s"%link) return link return None