diff --git a/recipes/arcamax.recipe b/recipes/arcamax.recipe index 39fa199cc3..db4d753cef 100644 --- a/recipes/arcamax.recipe +++ b/recipes/arcamax.recipe @@ -6,12 +6,13 @@ __copyright__ = 'Copyright 2010 Starson17' www.arcamax.com ''' from calibre.web.feeds.news import BasicNewsRecipe +from calibre.ebooks.BeautifulSoup import Tag class Arcamax(BasicNewsRecipe): title = 'Arcamax' __author__ = 'Starson17' - __version__ = '1.03' - __date__ = '25 November 2010' + __version__ = '1.04' + __date__ = '18 April 2011' description = u'Family Friendly Comics - Customize for more days/comics: Defaults to 7 days, 25 comics - 20 general, 5 editorial.' category = 'news, comics' language = 'en' @@ -30,8 +31,15 @@ class Arcamax(BasicNewsRecipe): , 'language' : language } - keep_only_tags = [dict(name='div', attrs={'class':['toon']}), - ] + keep_only_tags = [dict(name='div', attrs={'class':['comics-header']}), + dict(name='b', attrs={'class':['current']}), + dict(name='article', attrs={'class':['comic']}), + ] + + remove_tags = [dict(name='div', attrs={'id':['comicfull' ]}), + dict(name='div', attrs={'class':['calendar' ]}), + dict(name='nav', attrs={'class':['calendar-nav' ]}), + ] def parse_index(self): feeds = [] @@ -71,7 +79,6 @@ class Arcamax(BasicNewsRecipe): #(u"Rugrats", u"http://www.arcamax.com/rugrats"), (u"Speed Bump", u"http://www.arcamax.com/speedbump"), (u"Wizard of Id", u"http://www.arcamax.com/wizardofid"), - (u"Dilbert", u"http://www.arcamax.com/dilbert"), (u"Zits", u"http://www.arcamax.com/zits"), ]: articles = self.make_links(url) @@ -86,24 +93,37 @@ class Arcamax(BasicNewsRecipe): for page in pages: page_soup = self.index_to_soup(url) if page_soup: - title = page_soup.find(name='div', attrs={'class':'toon'}).p.img['alt'] + title = page_soup.find(name='div', attrs={'class':'comics-header'}).h1.contents[0] page_url = url - prev_page_url = 'http://www.arcamax.com' + page_soup.find('a', attrs={'class':'next'}, text='Previous').parent['href'] - current_articles.append({'title': title, 'url': page_url, 'description':'', 'date':''}) + # orig prev_page_url = 'http://www.arcamax.com' + page_soup.find('a', attrs={'class':'prev'}, text='Previous').parent['href'] + prev_page_url = 'http://www.arcamax.com' + page_soup.find('span', text='Previous').parent.parent['href'] + date = self.tag_to_string(page_soup.find(name='b', attrs={'class':['current']})) + current_articles.append({'title': title, 'url': page_url, 'description':'', 'date': date}) url = prev_page_url current_articles.reverse() return current_articles def preprocess_html(self, soup): - main_comic = soup.find('p',attrs={'class':'m0'}) - if main_comic.a['target'] == '_blank': - main_comic.a.img['id'] = 'main_comic' + for img_tag in soup.findAll('img'): + parent_tag = img_tag.parent + if parent_tag.name == 'a': + new_tag = Tag(soup,'p') + new_tag.insert(0,img_tag) + parent_tag.replaceWith(new_tag) + elif parent_tag.name == 'p': + if not self.tag_to_string(parent_tag) == '': + new_div = Tag(soup,'div') + new_tag = Tag(soup,'p') + new_tag.insert(0,img_tag) + parent_tag.replaceWith(new_div) + new_div.insert(0,new_tag) + new_div.insert(1,parent_tag) return soup extra_css = ''' h1{font-family:Arial,Helvetica,sans-serif; font-weight:bold;font-size:large;} h2{font-family:Arial,Helvetica,sans-serif; font-weight:normal;font-size:small;} - img#main_comic {max-width:100%; min-width:100%;} + img {max-width:100%; min-width:100%;} p{font-family:Arial,Helvetica,sans-serif;font-size:small;} body{font-family:Helvetica,Arial,sans-serif;font-size:small;} ''' diff --git a/src/calibre/ebooks/metadata/sources/test.py b/src/calibre/ebooks/metadata/sources/test.py index e280b0c038..c55f963003 100644 --- a/src/calibre/ebooks/metadata/sources/test.py +++ b/src/calibre/ebooks/metadata/sources/test.py @@ -67,6 +67,23 @@ def authors_test(authors): return test +def series_test(series, series_index): + series = series.lower() + + def test(mi): + ms = mi.series.lower() if mi.series else '' + if (ms == series) and (series_index == mi.series_index): + return True + if mi.series: + prints('Series test failed. Expected: \'%s [%d]\' found \'%s[%d]\''% \ + (series, series_index, ms, mi.series_index)) + else: + prints('Series test failed. Expected: \'%s [%d]\' found no series'% \ + (series, series_index)) + return False + + return test + def init_test(tdir_name): tdir = tempfile.gettempdir() lf = os.path.join(tdir, tdir_name.replace(' ', '')+'_identify_test.txt') diff --git a/src/calibre/gui2/actions/edit_metadata.py b/src/calibre/gui2/actions/edit_metadata.py index 18a73fb282..9d4d3891ca 100644 --- a/src/calibre/gui2/actions/edit_metadata.py +++ b/src/calibre/gui2/actions/edit_metadata.py @@ -37,8 +37,6 @@ class EditMetadataAction(InterfaceAction): md.addSeparator() if test_eight_code: dall = self.download_metadata - dident = partial(self.download_metadata, covers=False) - dcovers = partial(self.download_metadata, identify=False) else: dall = partial(self.download_metadata_old, False, covers=True) dident = partial(self.download_metadata_old, False, covers=False) @@ -47,9 +45,9 @@ class EditMetadataAction(InterfaceAction): md.addAction(_('Download metadata and covers'), dall, Qt.ControlModifier+Qt.Key_D) - md.addAction(_('Download only metadata'), dident) - md.addAction(_('Download only covers'), dcovers) if not test_eight_code: + md.addAction(_('Download only metadata'), dident) + md.addAction(_('Download only covers'), dcovers) md.addAction(_('Download only social metadata'), partial(self.download_metadata_old, False, covers=False, set_metadata=False, set_social_metadata=True)) @@ -80,7 +78,7 @@ class EditMetadataAction(InterfaceAction): self.qaction.setEnabled(enabled) self.action_merge.setEnabled(enabled) - def download_metadata(self, identify=True, covers=True, ids=None): + def download_metadata(self, ids=None): if ids is None: rows = self.gui.library_view.selectionModel().selectedRows() if not rows or len(rows) == 0: @@ -90,7 +88,7 @@ class EditMetadataAction(InterfaceAction): ids = [db.id(row.row()) for row in rows] from calibre.gui2.metadata.bulk_download2 import start_download start_download(self.gui, ids, - Dispatcher(self.bulk_metadata_downloaded), identify, covers) + Dispatcher(self.bulk_metadata_downloaded)) def bulk_metadata_downloaded(self, job): if job.failed: diff --git a/src/calibre/gui2/custom_column_widgets.py b/src/calibre/gui2/custom_column_widgets.py index 81016d3c6a..d1acd2ed83 100644 --- a/src/calibre/gui2/custom_column_widgets.py +++ b/src/calibre/gui2/custom_column_widgets.py @@ -289,6 +289,7 @@ class Series(Base): values = self.all_values = list(self.db.all_custom(num=self.col_id)) values.sort(key=sort_key) w = MultiCompleteComboBox(parent) + w.set_separator(None) w.setSizeAdjustPolicy(w.AdjustToMinimumContentsLengthWithIcon) w.setMinimumContentsLength(25) self.name_widget = w diff --git a/src/calibre/gui2/dialogs/tweak_epub.py b/src/calibre/gui2/dialogs/tweak_epub.py index a42fb07e40..edc274c9b2 100755 --- a/src/calibre/gui2/dialogs/tweak_epub.py +++ b/src/calibre/gui2/dialogs/tweak_epub.py @@ -12,7 +12,7 @@ from zipfile import ZipFile, ZIP_DEFLATED, ZIP_STORED from PyQt4.Qt import QDialog -from calibre.constants import isosx, iswindows +from calibre.constants import isosx from calibre.gui2 import open_local_file from calibre.gui2.dialogs.tweak_epub_ui import Ui_Dialog from calibre.libunzip import extract as zipextract diff --git a/src/calibre/gui2/metadata/basic_widgets.py b/src/calibre/gui2/metadata/basic_widgets.py index 9502fcb205..593a3839ac 100644 --- a/src/calibre/gui2/metadata/basic_widgets.py +++ b/src/calibre/gui2/metadata/basic_widgets.py @@ -278,11 +278,13 @@ class AuthorSortEdit(EnLineEdit): def copy_to_authors(self): aus = self.current_val + meth = tweaks['author_sort_copy_method'] if aus: ln, _, rest = aus.partition(',') if rest: - au = rest.strip() + ' ' + ln.strip() - self.authors_edit.current_val = [au] + if meth in ('invert', 'nocomma', 'comma'): + aus = rest.strip() + ' ' + ln.strip() + self.authors_edit.current_val = [aus] def auto_generate(self, *args): au = unicode(self.authors_edit.text()) @@ -465,16 +467,22 @@ class FormatsManager(QWidget): # {{{ self.metadata_from_format_button = QToolButton(self) self.metadata_from_format_button.setIcon(QIcon(I('edit_input.png'))) self.metadata_from_format_button.setIconSize(QSize(32, 32)) + self.metadata_from_format_button.setToolTip( + _('Set metadata for the book from the selected format')) self.add_format_button = QToolButton(self) self.add_format_button.setIcon(QIcon(I('add_book.png'))) self.add_format_button.setIconSize(QSize(32, 32)) self.add_format_button.clicked.connect(self.add_format) + self.add_format_button.setToolTip( + _('Add a format to this book')) self.remove_format_button = QToolButton(self) self.remove_format_button.setIcon(QIcon(I('trash.png'))) self.remove_format_button.setIconSize(QSize(32, 32)) self.remove_format_button.clicked.connect(self.remove_format) + self.remove_format_button.setToolTip( + _('Remove the selected format from this book')) self.formats = FormatList(self) self.formats.setAcceptDrops(True) @@ -939,7 +947,13 @@ class IdentifiersEdit(QLineEdit): # {{{ def fset(self, val): if not val: val = {} - txt = ', '.join(['%s:%s'%(k, v) for k, v in val.iteritems()]) + def keygen(x): + x = x[0] + if x == 'isbn': + x = '00isbn' + return x + ids = sorted(val.iteritems(), key=keygen) + txt = ', '.join(['%s:%s'%(k, v) for k, v in ids]) self.setText(txt.strip()) self.setCursorPosition(0) return property(fget=fget, fset=fset) @@ -959,7 +973,7 @@ class IdentifiersEdit(QLineEdit): # {{{ tt = self.BASE_TT extra = '' if not isbn: - col = 'rgba(0,255,0,0%)' + col = 'none' elif check_isbn(isbn) is not None: col = 'rgba(0,255,0,20%)' extra = '\n\n'+_('This ISBN number is valid') diff --git a/src/calibre/gui2/metadata/bulk_download2.py b/src/calibre/gui2/metadata/bulk_download2.py index 5f0af1b316..4aa4561078 100644 --- a/src/calibre/gui2/metadata/bulk_download2.py +++ b/src/calibre/gui2/metadata/bulk_download2.py @@ -12,7 +12,8 @@ from functools import partial from itertools import izip from PyQt4.Qt import (QIcon, QDialog, QVBoxLayout, QTextBrowser, QSize, - QDialogButtonBox, QApplication, QTimer, QLabel, QProgressBar) + QDialogButtonBox, QApplication, QTimer, QLabel, QProgressBar, + QGridLayout, QPixmap, Qt) from calibre.gui2.dialogs.message_box import MessageBox from calibre.gui2.threaded_jobs import ThreadedJob @@ -25,37 +26,86 @@ from calibre.ebooks.metadata.book.base import Metadata from calibre.customize.ui import metadata_plugins from calibre.ptempfile import PersistentTemporaryFile +# Start download {{{ def show_config(gui, parent): from calibre.gui2.preferences import show_config_widget show_config_widget('Sharing', 'Metadata download', parent=parent, gui=gui, never_shutdown=True) -def start_download(gui, ids, callback, identify, covers): - q = MessageBox(MessageBox.QUESTION, _('Schedule download?'), +class ConfirmDialog(QDialog): + + def __init__(self, ids, parent): + QDialog.__init__(self, parent) + self.setWindowTitle(_('Schedule download?')) + self.setWindowIcon(QIcon(I('dialog_question.png'))) + + l = self.l = QGridLayout() + self.setLayout(l) + + i = QLabel(self) + i.setPixmap(QPixmap(I('dialog_question.png'))) + l.addWidget(i, 0, 0) + + t = QLabel( '
'+_('The download of metadata for the %d selected book(s) will' ' run in the background. Proceed?')%len(ids) + '
'+_('You can monitor the progress of the download ' 'by clicking the rotating spinner in the bottom right ' 'corner.') + '
'+_('When the download completes you will be asked for' - ' confirmation before calibre applies the downloaded metadata.'), - show_copy_button=False, parent=gui) - b = q.bb.addButton(_('Configure download'), q.bb.ActionRole) - b.setIcon(QIcon(I('config.png'))) - b.clicked.connect(partial(show_config, gui, q)) - q.det_msg_toggle.setVisible(False) + ' confirmation before calibre applies the downloaded metadata.') + ) + t.setWordWrap(True) + l.addWidget(t, 0, 1) + l.setColumnStretch(0, 1) + l.setColumnStretch(1, 100) - ret = q.exec_() - b.clicked.disconnect() - if ret != q.Accepted: + self.identify = self.covers = True + self.bb = QDialogButtonBox(QDialogButtonBox.Cancel) + self.bb.rejected.connect(self.reject) + b = self.bb.addButton(_('Download only &metadata'), + self.bb.AcceptRole) + b.clicked.connect(self.only_metadata) + b.setIcon(QIcon(I('edit_input.png'))) + b = self.bb.addButton(_('Download only &covers'), + self.bb.AcceptRole) + b.clicked.connect(self.only_covers) + b.setIcon(QIcon(I('default_cover.png'))) + b = self.b = self.bb.addButton(_('&Configure download'), self.bb.ActionRole) + b.setIcon(QIcon(I('config.png'))) + b.clicked.connect(partial(show_config, parent, self)) + l.addWidget(self.bb, 1, 0, 1, 2) + b = self.bb.addButton(_('Download &both'), + self.bb.AcceptRole) + b.clicked.connect(self.accept) + b.setDefault(True) + b.setAutoDefault(True) + b.setIcon(QIcon(I('ok.png'))) + + self.resize(self.sizeHint()) + b.setFocus(Qt.OtherFocusReason) + + def only_metadata(self): + self.covers = False + self.accept() + + def only_covers(self): + self.identify = False + self.accept() + +def start_download(gui, ids, callback): + d = ConfirmDialog(ids, gui) + ret = d.exec_() + d.b.clicked.disconnect() + if ret != d.Accepted: return job = ThreadedJob('metadata bulk download', _('Download metadata for %d books')%len(ids), - download, (ids, gui.current_db, identify, covers), {}, callback) + download, (ids, gui.current_db, d.identify, d.covers), {}, callback) gui.job_manager.run_threaded_job(job) gui.status_bar.show_message(_('Metadata download started'), 3000) - +# }}} class ViewLog(QDialog): # {{{ @@ -93,9 +143,10 @@ def view_log(job, parent): # }}} +# Apply downloaded metadata {{{ class ApplyDialog(QDialog): - def __init__(self, id_map, gui): + def __init__(self, gui): QDialog.__init__(self, gui) self.l = l = QVBoxLayout() @@ -104,27 +155,33 @@ class ApplyDialog(QDialog): self.pb = QProgressBar(self) l.addWidget(self.pb) - self.pb.setMinimum(0) - self.pb.setMaximum(len(id_map)) self.bb = QDialogButtonBox(QDialogButtonBox.Cancel) self.bb.rejected.connect(self.reject) - self.bb.accepted.connect(self.accept) l.addWidget(self.bb) self.gui = gui + self.timer = QTimer(self) + self.timer.timeout.connect(self.do_one) + + def start(self, id_map): self.id_map = list(id_map.iteritems()) self.current_idx = 0 - self.failures = [] self.ids = [] self.canceled = False - - QTimer.singleShot(20, self.do_one) + self.pb.setMinimum(0) + self.pb.setMaximum(len(id_map)) + self.timer.start(50) def do_one(self): if self.canceled: return + if self.current_idx >= len(self.id_map): + self.timer.stop() + self.finalize() + return + i, mi = self.id_map[self.current_idx] db = self.gui.current_db try: @@ -144,15 +201,11 @@ class ApplyDialog(QDialog): pass self.pb.setValue(self.pb.value()+1) - - if self.current_idx >= len(self.id_map) - 1: - self.finalize() - else: - self.current_idx += 1 - QTimer.singleShot(20, self.do_one) + self.current_idx += 1 def reject(self): self.canceled = True + self.timer.stop() QDialog.reject(self) def finalize(self): @@ -169,17 +222,18 @@ class ApplyDialog(QDialog): title += ' - ' + authors_to_string(authors) msg.append(title+'\n\n'+tb+'\n'+('*'*80)) - error_dialog(self, _('Some failures'), + parent = self if self.isVisible() else self.parent() + error_dialog(parent, _('Some failures'), _('Failed to apply updated metadata for some books' ' in your library. Click "Show Details" to see ' 'details.'), det_msg='\n\n'.join(msg), show=True) - self.accept() if self.ids: cr = self.gui.library_view.currentIndex().row() self.gui.library_view.model().refresh_ids( self.ids, cr) if self.gui.cover_flow: self.gui.cover_flow.dataChanged() + self.accept() _amd = None def apply_metadata(job, gui, q, result): @@ -217,8 +271,11 @@ def apply_metadata(job, gui, q, result): 'Do you want to proceed?'), det_msg='\n'.join(modified)): return - _amd = ApplyDialog(id_map, gui) - _amd.exec_() + if _amd is None: + _amd = ApplyDialog(gui) + _amd.start(id_map) + if len(id_map) > 3: + _amd.exec_() def proceed(gui, job): gui.status_bar.show_message(_('Metadata download completed'), 3000) @@ -248,6 +305,8 @@ def proceed(gui, job): q.show() q.finished.connect(partial(apply_metadata, job, gui, q)) +# }}} + def merge_result(oldmi, newmi): dummy = Metadata(_('Unknown')) for f in msprefs['ignore_fields']: diff --git a/src/calibre/gui2/metadata/single.py b/src/calibre/gui2/metadata/single.py index 52b9e99872..d527dda022 100644 --- a/src/calibre/gui2/metadata/single.py +++ b/src/calibre/gui2/metadata/single.py @@ -156,6 +156,9 @@ class MetadataSingleDialogBase(ResizableDialog): self.identifiers = IdentifiersEdit(self) self.basic_metadata_widgets.append(self.identifiers) + self.clear_identifiers_button = QToolButton(self) + self.clear_identifiers_button.setIcon(QIcon(I('trash.png'))) + self.clear_identifiers_button.clicked.connect(self.identifiers.clear) self.publisher = PublisherEdit(self) self.basic_metadata_widgets.append(self.publisher) @@ -541,8 +544,8 @@ class MetadataSingleDialog(MetadataSingleDialogBase): # {{{ sto(self.rating, self.tags) create_row2(2, self.tags, self.tags_editor_button) sto(self.tags_editor_button, self.identifiers) - create_row2(3, self.identifiers) - sto(self.identifiers, self.timestamp) + create_row2(3, self.identifiers, self.clear_identifiers_button) + sto(self.clear_identifiers_button, self.timestamp) create_row2(4, self.timestamp, self.timestamp.clear_button) sto(self.timestamp.clear_button, self.pubdate) create_row2(5, self.pubdate, self.pubdate.clear_button) @@ -657,7 +660,8 @@ class MetadataSingleDialogAlt1(MetadataSingleDialogBase): # {{{ create_row(9, self.publisher, self.timestamp) create_row(10, self.timestamp, self.identifiers, button=self.timestamp.clear_button, icon='trash.png') - create_row(11, self.identifiers, self.comments) + create_row(11, self.identifiers, self.comments, + button=self.clear_identifiers_button, icon='trash.png') tl.addItem(QSpacerItem(1, 1, QSizePolicy.Fixed, QSizePolicy.Expanding), 12, 1, 1 ,1) diff --git a/src/calibre/gui2/metadata/single_download.py b/src/calibre/gui2/metadata/single_download.py index 8f01c6df1e..7e30f02420 100644 --- a/src/calibre/gui2/metadata/single_download.py +++ b/src/calibre/gui2/metadata/single_download.py @@ -30,6 +30,7 @@ from calibre.ebooks.metadata.book.base import Metadata from calibre.gui2 import error_dialog, NONE from calibre.utils.date import utcnow, fromordinal, format_date from calibre.library.comments import comments_to_html +from calibre.constants import islinux from calibre import force_unicode # }}} @@ -116,6 +117,12 @@ class CoverDelegate(QStyledItemDelegate): # {{{ def paint(self, painter, option, index): QStyledItemDelegate.paint(self, painter, option, index) + if islinux: + # On linux for some reason the selected color is drawn on top of + # the decoration + style = QApplication.style() + style.drawItemPixmap(painter, option.rect, Qt.AlignTop|Qt.AlignHCenter, + QPixmap(index.data(Qt.DecorationRole))) if self.timer.isActive() and index.data(Qt.UserRole).toBool(): rect = QRect(0, 0, self.spinner_width, self.spinner_width) rect.moveCenter(option.rect.center()) @@ -945,7 +952,7 @@ class CoverFetch(QDialog): # {{{ # }}} if __name__ == '__main__': - #DEBUG_DIALOG = True + DEBUG_DIALOG = True app = QApplication([]) d = FullFetch() d.start(title='great gatsby', authors=['fitzgerald'])