From 2772908a3ce1ca7636eaeeaadaaa2a84a9f8a0ad Mon Sep 17 00:00:00 2001 From: John Peterson Date: Mon, 5 Nov 2012 02:08:31 +0000 Subject: [PATCH 01/28] Fixes for viewer etc. Fix: Remember maximized state when toggle fullscreen. New: Adding saved state: fullscreen state. New: Adding context menu item: search online, back (Backspace)/forward, toc, font size, fullscreen, exit (Esc). Fix: Hiding non-selection context menu items when showing selection context menu items. Fix: Hiding selection context menu items when showing image context menu items. Fix: Return in book list would not open viewer for selected items. Fix: The top book list row was not selected on program start, as intended. Fix: Reset quick search would unselect the book list selection even when not in a search. Fix: Reset quick search when in a search would not return selection to a book list row. Fix: Don't open empty viewer if no book is selected. Change: Removing fullscreen message because it's not useful for many users. New: Applying auto convert option for manual add too. New: Making the post import phase accessible to plugins (for metadata creation or conversion). New: Adding viewer option to not show the cover. --- src/calibre/customize/__init__.py | 11 ++ src/calibre/customize/ui.py | 22 ++++ src/calibre/ebooks/oeb/iterator/book.py | 27 +++-- src/calibre/gui2/actions/view.py | 1 - src/calibre/gui2/add.py | 6 + src/calibre/gui2/init.py | 4 +- src/calibre/gui2/library/views.py | 9 +- src/calibre/gui2/search_box.py | 6 +- src/calibre/gui2/viewer/config.py | 4 + src/calibre/gui2/viewer/config.ui | 7 ++ src/calibre/gui2/viewer/documentview.py | 143 ++++++++++++++++++++++-- src/calibre/gui2/viewer/main.py | 115 +++++++------------ src/calibre/library/database2.py | 3 +- 13 files changed, 258 insertions(+), 100 deletions(-) diff --git a/src/calibre/customize/__init__.py b/src/calibre/customize/__init__.py index 2570efebe1..1f57305945 100644 --- a/src/calibre/customize/__init__.py +++ b/src/calibre/customize/__init__.py @@ -308,6 +308,10 @@ class FileTypePlugin(Plugin): # {{{ #: to the database on_import = False + #: If True, this plugin is run after books are added + #: to the database + on_postimport = False + #: If True, this plugin is run just before a conversion on_preprocess = False @@ -336,7 +340,14 @@ class FileTypePlugin(Plugin): # {{{ ''' # Default implementation does nothing return path_to_ebook + + def postimport(self, id): + ''' + Run post import. + :param id: Library id of the added book. + ''' + # }}} class MetadataReaderPlugin(Plugin): # {{{ diff --git a/src/calibre/customize/ui.py b/src/calibre/customize/ui.py index 2799ed747a..15d448161e 100644 --- a/src/calibre/customize/ui.py +++ b/src/calibre/customize/ui.py @@ -104,14 +104,17 @@ def is_disabled(plugin): # File type plugins {{{ _on_import = {} +_on_postimport = {} _on_preprocess = {} _on_postprocess = {} def reread_filetype_plugins(): global _on_import + global _on_postimport global _on_preprocess global _on_postprocess _on_import = {} + _on_postimport = {} _on_preprocess = {} _on_postprocess = {} @@ -122,6 +125,10 @@ def reread_filetype_plugins(): if not _on_import.has_key(ft): _on_import[ft] = [] _on_import[ft].append(plugin) + if plugin.on_postimport: + if not _on_postimport.has_key(ft): + _on_postimport[ft] = [] + _on_postimport[ft].append(plugin) if plugin.on_preprocess: if not _on_preprocess.has_key(ft): _on_preprocess[ft] = [] @@ -163,6 +170,21 @@ run_plugins_on_preprocess = functools.partial(_run_filetype_plugins, occasion='preprocess') run_plugins_on_postprocess = functools.partial(_run_filetype_plugins, occasion='postprocess') + +def postimport_plugins(id, format): + customization = config['plugin_customization'] + format = format.lower() + for plugin in _on_postimport.get(format, []): + if is_disabled(plugin): + continue + plugin.site_customization = customization.get(plugin.name, '') + with plugin: + try: + plugin.postimport(id) + except: + print 'Running file type plugin %s failed with traceback:'%plugin.name + traceback.print_exc() + # }}} # Plugin customization {{{ diff --git a/src/calibre/ebooks/oeb/iterator/book.py b/src/calibre/ebooks/oeb/iterator/book.py index 992d5304c6..65fc48cad7 100644 --- a/src/calibre/ebooks/oeb/iterator/book.py +++ b/src/calibre/ebooks/oeb/iterator/book.py @@ -49,8 +49,9 @@ class EbookIterator(BookmarksMixin): CHARACTERS_PER_PAGE = 1000 - def __init__(self, pathtoebook, log=None): + def __init__(self, pathtoebook, exclude_cover=False, log=None): self.log = log or default_log + self.exclude_cover = exclude_cover pathtoebook = pathtoebook.strip() self.pathtoebook = os.path.abspath(pathtoebook) self.config = DynamicConfig(name='iterator') @@ -139,16 +140,20 @@ class EbookIterator(BookmarksMixin): self.log.warn('Missing spine item:', repr(spath)) cover = self.opf.cover - if cover and self.ebook_ext in {'lit', 'mobi', 'prc', 'opf', 'fb2', - 'azw', 'azw3'}: - cfile = os.path.join(self.base, 'calibre_iterator_cover.html') - rcpath = os.path.relpath(cover, self.base).replace(os.sep, '/') - chtml = (TITLEPAGE%prepare_string_for_xml(rcpath, True)).encode('utf-8') - with open(cfile, 'wb') as f: - f.write(chtml) - self.spine[0:0] = [Spiny(cfile, - mime_type='application/xhtml+xml')] - self.delete_on_exit.append(cfile) + if cover: + if self.ebook_ext in {'lit', 'mobi', 'prc', 'opf', 'fb2', + 'azw', 'azw3'}: + if not self.exclude_cover: + cfile = os.path.join(self.base, 'calibre_iterator_cover.html') + rcpath = os.path.relpath(cover, self.base).replace(os.sep, '/') + chtml = (TITLEPAGE%prepare_string_for_xml(rcpath, True)).encode('utf-8') + with open(cfile, 'wb') as f: + f.write(chtml) + self.spine[0:0] = [Spiny(cfile, + mime_type='application/xhtml+xml')] + self.delete_on_exit.append(cfile) + elif self.exclude_cover: + self.spine.remove(self.spine[0]) if self.opf.path_to_html_toc is not None and \ self.opf.path_to_html_toc not in self.spine: diff --git a/src/calibre/gui2/actions/view.py b/src/calibre/gui2/actions/view.py index 5a7a991607..8672338b6a 100644 --- a/src/calibre/gui2/actions/view.py +++ b/src/calibre/gui2/actions/view.py @@ -267,7 +267,6 @@ class ViewAction(InterfaceAction): def _view_books(self, rows): if not rows or len(rows) == 0: - self._launch_viewer() return if not self._view_check(len(rows)): diff --git a/src/calibre/gui2/add.py b/src/calibre/gui2/add.py index 234c72abf4..4336148816 100644 --- a/src/calibre/gui2/add.py +++ b/src/calibre/gui2/add.py @@ -99,6 +99,7 @@ class DBAdder(QObject): # {{{ def __init__(self, parent, db, ids, nmap): QObject.__init__(self, parent) + self.parent = parent self.db, self.ids, self.nmap = db, dict(**ids), dict(**nmap) self.critical = {} self.number_of_books_added = 0 @@ -232,6 +233,11 @@ class DBAdder(QObject): # {{{ with open(path, 'rb') as f: self.db.add_format(id, fmt, f, index_is_id=True, notify=False, replace=replace) + if gprefs['auto_add_auto_convert']: + of = prefs['output_format'] + if not of == fmt.lower(): + gui = self.parent._parent + gui.iactions['Convert Books'].auto_convert([id], None, of) # }}} diff --git a/src/calibre/gui2/init.py b/src/calibre/gui2/init.py index 338a558f29..adcd0fc6cf 100644 --- a/src/calibre/gui2/init.py +++ b/src/calibre/gui2/init.py @@ -8,7 +8,7 @@ __docformat__ = 'restructuredtext en' import functools from PyQt4.Qt import Qt, QStackedWidget, QMenu, \ - QSize, QSizePolicy, QStatusBar, QLabel, QFont + QSize, QSizePolicy, QStatusBar, QTimer, QLabel, QFont from calibre.utils.config import prefs from calibre.constants import (isosx, __appname__, preferred_encoding, @@ -274,7 +274,7 @@ class LayoutMixin(object): # {{{ m = self.library_view.model() if m.rowCount(None) > 0: - self.library_view.set_current_row(0) + QTimer.singleShot(1, self.library_view.set_current_row) m.current_changed(self.library_view.currentIndex(), self.library_view.currentIndex()) self.library_view.setFocus(Qt.OtherFocusReason) diff --git a/src/calibre/gui2/library/views.py b/src/calibre/gui2/library/views.py index e263e2c489..85636104cb 100644 --- a/src/calibre/gui2/library/views.py +++ b/src/calibre/gui2/library/views.py @@ -754,6 +754,13 @@ class BooksView(QTableView): # {{{ # }}} + def keyPressEvent(self, event): + if event.key() == Qt.Key_Return: + selected_rows = [r.row() for r in self.selectionModel().selectedRows()] + self.display_parent.iactions['View']._view_books(selected_rows) + else: + return super(BooksView, self).keyPressEvent(event) + @property def column_map(self): return self._model.column_map @@ -777,7 +784,7 @@ class BooksView(QTableView): # {{{ self.scrollTo(self.model().index(row, i), self.PositionAtCenter) break - def set_current_row(self, row, select=True): + def set_current_row(self, row=0, select=True): if row > -1 and row < self.model().rowCount(QModelIndex()): h = self.horizontalHeader() logical_indices = list(range(h.count())) diff --git a/src/calibre/gui2/search_box.py b/src/calibre/gui2/search_box.py index 54a80571e6..50ead17d1d 100644 --- a/src/calibre/gui2/search_box.py +++ b/src/calibre/gui2/search_box.py @@ -128,7 +128,7 @@ class SearchBox2(QComboBox): # {{{ def clear(self, emit_search=True): self.normalize_state() self.setEditText('') - if emit_search: + if self.in_a_search() and emit_search: self.search.emit('') self._in_a_search = False self.cleared.emit() @@ -159,7 +159,6 @@ class SearchBox2(QComboBox): # {{{ self.normalize_state() if self._in_a_search: self.changed.emit() - self._in_a_search = False if event.key() in (Qt.Key_Return, Qt.Key_Enter): self.do_search() self.focus_to_library.emit() @@ -424,6 +423,7 @@ class SearchBoxMixin(object): # {{{ self.tags_view.clear() self.saved_search.clear() self.set_number_of_books_shown() + self.focus_to_library() def search_box_changed(self): self.saved_search.clear() @@ -440,6 +440,8 @@ class SearchBoxMixin(object): # {{{ def focus_to_library(self): self.current_view().setFocus(Qt.OtherFocusReason) + if not self.current_view().get_selected_ids(): + self.current_view().set_current_row() # }}} diff --git a/src/calibre/gui2/viewer/config.py b/src/calibre/gui2/viewer/config.py index 8643ce63d0..e3726c1ecb 100644 --- a/src/calibre/gui2/viewer/config.py +++ b/src/calibre/gui2/viewer/config.py @@ -41,6 +41,8 @@ def config(defaults=None): help=_('Default language for hyphenation rules')) c.add_opt('remember_current_page', default=True, help=_('Save the current position in the document, when quitting')) + c.add_opt('dont_show_cover', default=False, + help=_('Don\'t show the cover.')) c.add_opt('wheel_flips_pages', default=False, help=_('Have the mouse wheel turn pages')) c.add_opt('line_scrolling_stops_on_pagebreaks', default=False, @@ -177,6 +179,7 @@ class ConfigDialog(QDialog, Ui_Dialog): def load_options(self, opts): self.opt_remember_window_size.setChecked(opts.remember_window_size) self.opt_remember_current_page.setChecked(opts.remember_current_page) + self.opt_dont_show_cover.setChecked(opts.dont_show_cover) self.opt_wheel_flips_pages.setChecked(opts.wheel_flips_pages) self.opt_page_flip_duration.setValue(opts.page_flip_duration) fms = opts.font_magnification_step @@ -267,6 +270,7 @@ class ConfigDialog(QDialog, Ui_Dialog): c.set('max_fs_width', int(self.max_fs_width.value())) c.set('hyphenate', self.hyphenate.isChecked()) c.set('remember_current_page', self.opt_remember_current_page.isChecked()) + c.set('dont_show_cover', self.opt_dont_show_cover.isChecked()) c.set('wheel_flips_pages', self.opt_wheel_flips_pages.isChecked()) c.set('page_flip_duration', self.opt_page_flip_duration.value()) c.set('font_magnification_step', diff --git a/src/calibre/gui2/viewer/config.ui b/src/calibre/gui2/viewer/config.ui index f91462e5b2..abc5391ebf 100644 --- a/src/calibre/gui2/viewer/config.ui +++ b/src/calibre/gui2/viewer/config.ui @@ -605,6 +605,13 @@ QToolBox::tab:hover { + + + + Don't show the c&over (needs restart) + + + diff --git a/src/calibre/gui2/viewer/documentview.py b/src/calibre/gui2/viewer/documentview.py index 81ee82f2b6..c1927e1907 100644 --- a/src/calibre/gui2/viewer/documentview.py +++ b/src/calibre/gui2/viewer/documentview.py @@ -16,6 +16,7 @@ from PyQt4.QtWebKit import QWebPage, QWebView, QWebSettings from calibre.gui2.viewer.flip import SlideFlip from calibre.gui2.shortcuts import Shortcuts +from calibre.gui2 import open_url from calibre import prints from calibre.customize.ui import all_viewer_plugins from calibre.gui2.viewer.keys import SHORTCUTS @@ -478,7 +479,12 @@ class DocumentView(QWebView): # {{{ d = self.document self.unimplemented_actions = list(map(self.pageAction, [d.DownloadImageToDisk, d.OpenLinkInNewWindow, d.DownloadLinkToDisk, - d.OpenImageInNewWindow, d.OpenLink, d.Reload])) + d.OpenImageInNewWindow, d.OpenLink, d.Reload, d.InspectElement, d.Copy])) + + self.search_online_action = QAction(QIcon(I('search.png')), _(''), self) + self.search_online_action.setShortcut(Qt.CTRL+Qt.Key_E) + self.search_online_action.triggered.connect(self.search_online) + self.addAction(self.search_online_action) self.dictionary_action = QAction(QIcon(I('dictionary.png')), _('&Lookup in dictionary'), self) self.dictionary_action.setShortcut(Qt.CTRL+Qt.Key_L) @@ -523,6 +529,44 @@ class DocumentView(QWebView): # {{{ self.goto_location_action.setMenu(self.goto_location_menu) self.grabGesture(Qt.SwipeGesture) + self.toc_action = QAction(_('Table of contents'), self) + self.toc_action.setCheckable(True) + self.toc_action.triggered.connect(self.toggle_toc) + + self.back_action = QAction(_('&Back'), self) + self.back_action.triggered.connect(self.back) + self.back_action.setShortcut(Qt.Key_Backspace) + self.addAction(self.back_action) + self.forward_action = QAction(_('&Forward'), self) + self.forward_action.triggered.connect(self.forward) + + self.magnify_fonts_action = QAction(_('Larger font'), self) + self.magnify_fonts_action.triggered.connect(self.font_size_larger) + self.restore_fonts_action = QAction(_('Normal font size'), self) + self.restore_fonts_action.setCheckable(True) + self.restore_fonts_action.triggered.connect(self.font_size_restore) + self.shrink_fonts_action = QAction(_('Smaller font'), self) + self.shrink_fonts_action.triggered.connect(self.font_size_smaller) + + self.fullscreen_action = QAction(_('Full screen'), self) + self.fullscreen_action.triggered.connect(self.toggle_fullscreen) + self.fullscreen_action.setCheckable(True) + self.fullscreen_action.setShortcut(Qt.Key_F11) + self.addAction(self.fullscreen_action) + + self.quit_action = QAction(_('Exit'), self) + self.quit_action.triggered.connect(self.quit_toggle) + self.quit_action.setShortcut(Qt.Key_Escape) + self.addAction(self.quit_action) + + def back(self, *args): + if self.manager is not None: + self.manager.back() + + def forward(self, *args): + if self.manager is not None: + self.manager.forward() + def goto_next_section(self, *args): if self.manager is not None: self.manager.goto_next_section() @@ -539,6 +583,30 @@ class DocumentView(QWebView): # {{{ if self.manager is not None: self.manager.goto_end() + def font_size_larger(self, *args): + if self.manager is not None: + self.manager.font_size_larger() + + def font_size_smaller(self, *args): + if self.manager is not None: + self.manager.font_size_smaller() + + def font_size_restore(self, *args): + if self.manager is not None: + self.manager.font_size_restore() + + def toggle_toc(self, *args): + if self.manager is not None: + self.manager.toggle_toc() + + def toggle_fullscreen(self, *args): + if self.manager is not None: + self.manager.toggle_fullscreen() + + def quit_toggle(self, *args): + if self.manager is not None: + self.manager.quit() + @property def copy_action(self): return self.pageAction(self.document.Copy) @@ -579,6 +647,17 @@ class DocumentView(QWebView): # {{{ if self.manager is not None: self.manager.selection_changed(unicode(self.document.selectedText())) + def _selectedText(self): + t = self.selectedText().trimmed() + if not t: + return "" + if t.size() > 40: + t.truncate(40) + t = t + "..." + t = t.replace('&', '&&') + t = "S&earch Google for '" + t + "'" + return unicode(t) + def contextMenuEvent(self, ev): mf = self.document.mainFrame() r = mf.hitTestContent(ev.pos()) @@ -588,17 +667,52 @@ class DocumentView(QWebView): # {{{ menu = self.document.createStandardContextMenu() for action in self.unimplemented_actions: menu.removeAction(action) - text = unicode(self.selectedText()) - if text: - menu.insertAction(list(menu.actions())[0], self.dictionary_action) - menu.insertAction(list(menu.actions())[0], self.search_action) + if not img.isNull(): menu.addAction(self.view_image_action) - menu.addSeparator() - menu.addAction(self.goto_location_action) - if self.document.in_fullscreen_mode and self.manager is not None: + + text = self._selectedText() + if text and img.isNull(): + copyAction = self.pageAction(self.document.Copy) + copyAction.setText("&Copy") + menu.addAction(copyAction) + self.search_online_action.setText(text) + menu.addAction(self.search_online_action) + menu.addAction(self.dictionary_action) + menu.addAction(self.search_action) + + if not text and img.isNull(): menu.addSeparator() - menu.addAction(self.manager.toggle_toolbar_action) + menu.addAction(self.back_action) + menu.addAction(self.forward_action) + menu.addAction(self.goto_location_action) + + menu.addSeparator() + self.toc_action.setChecked(self.manager.toc.isVisible()) + menu.addAction(self.toc_action) + + menu.addSeparator() + menu.addAction(self.magnify_fonts_action) + self.restore_fonts_action.setChecked(self.multiplier == 1) + menu.addAction(self.restore_fonts_action) + menu.addAction(self.shrink_fonts_action) + + menu.addSeparator() + inspectAction = self.pageAction(self.document.InspectElement) + inspectAction.setText("I&nspect element") + menu.addAction(inspectAction) + + if not text and img.isNull(): + menu.addSeparator() + if self.document.in_fullscreen_mode and self.manager is not None: + self.manager.toggle_toolbar_action.setChecked(self.manager.tool_bar.isVisible()) + menu.addAction(self.manager.toggle_toolbar_action) + self.fullscreen_action.setChecked(self.manager.isFullScreen()) + menu.addAction(self.fullscreen_action) + + menu.addSeparator() + menu.addAction(self.quit_action) + menu.exec_(ev.globalPos()) def lookup(self, *args): @@ -613,6 +727,12 @@ class DocumentView(QWebView): # {{{ if t: self.manager.search.set_search_string(t) + def search_online(self): + t = unicode(self.selectedText()).strip() + if t: + url = 'https://www.google.com/search?q=' + QUrl().toPercentEncoding(t) + open_url(QUrl.fromEncoded(url)) + def set_manager(self, manager): self.manager = manager self.scrollbar = manager.horizontal_scrollbar @@ -990,6 +1110,11 @@ class DocumentView(QWebView): # {{{ self.multiplier -= amount return self.document.scroll_fraction + def restore_font_size(self): + with self.document.page_position: + self.multiplier = 1 + return self.document.scroll_fraction + def changeEvent(self, event): if event.type() == event.EnabledChange: self.update() diff --git a/src/calibre/gui2/viewer/main.py b/src/calibre/gui2/viewer/main.py index 7b624f170a..c11fda7437 100644 --- a/src/calibre/gui2/viewer/main.py +++ b/src/calibre/gui2/viewer/main.py @@ -230,7 +230,7 @@ class EbookViewer(MainWindow, Ui_EbookViewer): self.action_reference_mode.setCheckable(True) self.action_reference_mode.triggered[bool].connect(self.view.reference_mode) self.action_metadata.triggered[bool].connect(self.metadata.setVisible) - self.action_table_of_contents.toggled[bool].connect(self.set_toc_visible) + self.action_table_of_contents.toggled[bool].connect(self.toggle_toc) self.action_copy.triggered[bool].connect(self.copy) self.action_font_size_larger.triggered.connect(self.font_size_larger) self.action_font_size_smaller.triggered.connect(self.font_size_smaller) @@ -240,7 +240,7 @@ class EbookViewer(MainWindow, Ui_EbookViewer): self.action_find_next.triggered.connect(self.find_next) self.action_find_previous.triggered.connect(self.find_previous) self.action_full_screen.triggered[bool].connect(self.toggle_fullscreen) - self.action_full_screen.setShortcuts([Qt.Key_F11, Qt.CTRL+Qt.SHIFT+Qt.Key_F]) + self.action_full_screen.setShortcut(Qt.CTRL+Qt.SHIFT+Qt.Key_F) self.action_full_screen.setToolTip(_('Toggle full screen (%s)') % _(' or ').join([unicode(x.toString(x.NativeText)) for x in self.action_full_screen.shortcuts()])) @@ -274,35 +274,11 @@ class EbookViewer(MainWindow, Ui_EbookViewer): self.tool_bar2.setContextMenuPolicy(Qt.PreventContextMenu) self.tool_bar.widgetForAction(self.action_bookmark).setPopupMode(QToolButton.MenuButtonPopup) self.action_full_screen.setCheckable(True) - self.full_screen_label = QLabel(''' -
-

%s

-

%s

-

%s

-

%s

-
- '''%(_('Full screen mode'), - _('Right click to show controls'), - _('Tap in the left or right page margin to turn pages'), - _('Press Esc to quit')), - self) - self.full_screen_label.setVisible(False) - self.full_screen_label.setStyleSheet(''' - QLabel { - text-align: center; - background-color: white; - color: black; - border-width: 1px; - border-style: solid; - border-radius: 20px; - } - ''') self.window_mode_changed = None self.toggle_toolbar_action = QAction(_('Show/hide controls'), self) + self.toggle_toolbar_action.setCheckable(True) self.toggle_toolbar_action.triggered.connect(self.toggle_toolbars) self.addAction(self.toggle_toolbar_action) - self.full_screen_label_anim = QPropertyAnimation( - self.full_screen_label, 'size') self.clock_label = QLabel('99:99', self) self.clock_label.setVisible(False) self.clock_label.setFocusPolicy(Qt.NoFocus) @@ -324,11 +300,6 @@ class EbookViewer(MainWindow, Ui_EbookViewer): self.pos_label.setFocusPolicy(Qt.NoFocus) self.clock_timer = QTimer(self) self.clock_timer.timeout.connect(self.update_clock) - self.esc_full_screen_action = a = QAction(self) - self.addAction(a) - a.setShortcut(Qt.Key_Escape) - a.setEnabled(False) - a.triggered.connect(self.action_full_screen.trigger) self.print_menu = QMenu() self.print_menu.addAction(QIcon(I('print-preview.png')), _('Print Preview')) @@ -374,8 +345,13 @@ class EbookViewer(MainWindow, Ui_EbookViewer): self.pending_restore = True self.load_path(self.view.last_loaded_path) - def set_toc_visible(self, yes): - self.toc.setVisible(yes) + def toggle_toc(self): + if self.toc.isVisible(): + self.toc.setVisible(False) + self.action_table_of_contents.setChecked(False) + else: + self.toc.setVisible(True) + self.action_table_of_contents.setChecked(True) def clear_recent_history(self, *args): vprefs.set('viewer_open_history', []) @@ -397,9 +373,6 @@ class EbookViewer(MainWindow, Ui_EbookViewer): count += 1 def shutdown(self): - if self.isFullScreen(): - self.action_full_screen.trigger() - return False self.save_state() return True @@ -421,7 +394,8 @@ class EbookViewer(MainWindow, Ui_EbookViewer): def save_state(self): state = bytearray(self.saveState(self.STATE_VERSION)) vprefs['viewer_toolbar_state'] = state - vprefs.set('viewer_window_geometry', bytearray(self.saveGeometry())) + if not self.isFullScreen(): + vprefs.set('viewer_window_geometry', bytearray(self.saveGeometry())) if self.current_book_has_toc: vprefs.set('viewer_toc_isvisible', bool(self.toc.isVisible())) if self.toc.isVisible(): @@ -429,6 +403,7 @@ class EbookViewer(MainWindow, Ui_EbookViewer): bytearray(self.splitter.saveState())) vprefs['multiplier'] = self.view.multiplier vprefs['in_paged_mode'] = not self.action_toggle_paged_mode.isChecked() + vprefs['fullscreen'] = self.isFullScreen() def restore_state(self): state = vprefs.get('viewer_toolbar_state', None) @@ -449,6 +424,9 @@ class EbookViewer(MainWindow, Ui_EbookViewer): True)) self.toggle_paged_mode(self.action_toggle_paged_mode.isChecked(), at_start=True) + fullscreen = vprefs.get('fullscreen', None) + if fullscreen: + self.showFullScreen() def lookup(self, word): self.dictionary_view.setHtml('

'+ \ @@ -468,7 +446,12 @@ class EbookViewer(MainWindow, Ui_EbookViewer): from calibre.gui2.viewer.documentview import config c = config().parse() return c.remember_current_page - + + def get_dont_show_cover_opt(self): + from calibre.gui2.viewer.documentview import config + c = config().parse() + return c.dont_show_cover + def print_book(self): p = Printing(self.iterator, self) p.start_print() @@ -477,7 +460,7 @@ class EbookViewer(MainWindow, Ui_EbookViewer): p = Printing(self.iterator, self) p.start_preview() - def toggle_fullscreen(self, x): + def toggle_fullscreen(self): if self.isFullScreen(): self.showNormal() else: @@ -485,9 +468,12 @@ class EbookViewer(MainWindow, Ui_EbookViewer): def showFullScreen(self): self.view.document.page_position.save() + self.view.document.switch_to_fullscreen_mode() self.window_mode_changed = 'fullscreen' + self.was_maximized = super(EbookViewer, self).isMaximized() self.tool_bar.setVisible(False) self.tool_bar2.setVisible(False) + self.action_full_screen.setChecked(True) if not self.view.document.fullscreen_scrollbar: self.vertical_scrollbar.setVisible(False) self.frame.layout().setSpacing(0) @@ -500,26 +486,6 @@ class EbookViewer(MainWindow, Ui_EbookViewer): super(EbookViewer, self).showFullScreen() - def show_full_screen_label(self): - f = self.full_screen_label - self.esc_full_screen_action.setEnabled(True) - f.setVisible(True) - height = 200 - width = int(0.7*self.view.width()) - f.resize(width, height) - f.move((self.view.width() - width)//2, (self.view.height()-height)//2) - a = self.full_screen_label_anim - a.setDuration(500) - a.setStartValue(QSize(width, 0)) - a.setEndValue(QSize(width, height)) - a.start() - QTimer.singleShot(3500, self.full_screen_label.hide) - self.view.document.switch_to_fullscreen_mode() - if self.view.document.fullscreen_clock: - self.show_clock() - if self.view.document.fullscreen_pos: - self.show_pos_label() - def show_clock(self): self.clock_label.setVisible(True) self.clock_label.setText(QTime(22, 33, @@ -559,6 +525,7 @@ class EbookViewer(MainWindow, Ui_EbookViewer): def showNormal(self): self.view.document.page_position.save() + self.view.document.switch_to_window_mode() self.clock_label.setVisible(False) self.pos_label.setVisible(False) self.frame.setFrameStyle(self.original_frame_style) @@ -566,23 +533,23 @@ class EbookViewer(MainWindow, Ui_EbookViewer): self.clock_timer.stop() self.vertical_scrollbar.setVisible(True) self.window_mode_changed = 'normal' - self.esc_full_screen_action.setEnabled(False) + self.action_full_screen.setChecked(False) self.tool_bar.setVisible(True) self.tool_bar2.setVisible(True) - self.full_screen_label.setVisible(False) if hasattr(self, '_original_frame_margins'): om = self._original_frame_margins self.centralwidget.layout().setContentsMargins(om[0]) self.frame.layout().setContentsMargins(om[1]) - super(EbookViewer, self).showNormal() + if self.was_maximized: + super(EbookViewer, self).showMaximized() + else: + super(EbookViewer, self).showNormal() def handle_window_mode_toggle(self): if self.window_mode_changed: fs = self.window_mode_changed == 'fullscreen' self.window_mode_changed = None - if fs: - self.show_full_screen_label() - else: + if not fs: self.view.document.switch_to_window_mode() self.view.document.page_position.restore() self.scrolled(self.view.scroll_fraction) @@ -634,7 +601,7 @@ class EbookViewer(MainWindow, Ui_EbookViewer): if self.selected_text: QApplication.clipboard().setText(self.selected_text) - def back(self, x): + def back(self): pos = self.history.back(self.pos.value()) if pos is not None: self.goto_page(pos) @@ -643,7 +610,7 @@ class EbookViewer(MainWindow, Ui_EbookViewer): num = self.pos.value() self.goto_page(num) - def forward(self, x): + def forward(self): pos = self.history.forward() if pos is not None: self.goto_page(pos) @@ -657,6 +624,7 @@ class EbookViewer(MainWindow, Ui_EbookViewer): def goto_page(self, new_page, loaded_check=True): if self.current_page is not None or not loaded_check: for page in self.iterator.spine: + # print "goto_page", new_page, page.start_page, page.max_page if new_page >= page.start_page and new_page <= page.max_page: try: frac = float(new_page-page.start_page)/(page.pages-1) @@ -681,20 +649,21 @@ class EbookViewer(MainWindow, Ui_EbookViewer): def font_size_larger(self): self.view.magnify_fonts() - self.action_font_size_larger.setEnabled(self.view.multiplier < 3) - self.action_font_size_smaller.setEnabled(self.view.multiplier > 0.2) def font_size_smaller(self): self.view.shrink_fonts() - self.action_font_size_larger.setEnabled(self.view.multiplier < 3) - self.action_font_size_smaller.setEnabled(self.view.multiplier > 0.2) + def font_size_restore(self): + self.view.restore_font_size() + def magnification_changed(self, val): tt = _('Make font size %(which)s\nCurrent magnification: %(mag).1f') self.action_font_size_larger.setToolTip( tt %dict(which=_('larger'), mag=val)) self.action_font_size_smaller.setToolTip( tt %dict(which=_('smaller'), mag=val)) + self.action_font_size_larger.setEnabled(self.view.multiplier < 3) + self.action_font_size_smaller.setEnabled(self.view.multiplier > 0.2) def find(self, text, repeat=False, backwards=False): if not text: @@ -961,7 +930,7 @@ class EbookViewer(MainWindow, Ui_EbookViewer): if self.iterator is not None: self.save_current_position() self.iterator.__exit__() - self.iterator = EbookIterator(pathtoebook) + self.iterator = EbookIterator(pathtoebook, self.get_dont_show_cover_opt()) self.open_progress_indicator(_('Loading ebook...')) worker = Worker(target=partial(self.iterator.__enter__, extract_embedded_fonts_for_qt=True)) diff --git a/src/calibre/library/database2.py b/src/calibre/library/database2.py index 5952e11e57..d7dcbac75a 100644 --- a/src/calibre/library/database2.py +++ b/src/calibre/library/database2.py @@ -28,7 +28,7 @@ from calibre.ebooks.metadata.book.base import Metadata from calibre.constants import preferred_encoding, iswindows, filesystem_encoding from calibre.ptempfile import (PersistentTemporaryFile, base_dir, SpooledTemporaryFile) -from calibre.customize.ui import run_plugins_on_import +from calibre.customize.ui import run_plugins_on_import, postimport_plugins from calibre import isbytestring from calibre.utils.filenames import (ascii_filename, samefile, WindowsAtomicFolderMove, hardlink_file) @@ -1534,6 +1534,7 @@ class LibraryDatabase2(LibraryDatabase, SchemaUpgrade, CustomColumns): self.refresh_ids([id]) if notify: self.notify('metadata', [id]) + postimport_plugins(id, format) return True def save_original_format(self, book_id, fmt, notify=True): From 8a3ca41c0b94de0c2576ada555b3cac9db099d18 Mon Sep 17 00:00:00 2001 From: John Peterson Date: Mon, 5 Nov 2012 06:00:05 +0000 Subject: [PATCH 02/28] Response to merge review. 1) Fixed by making it optional. 1.5) Fixed by making it optional. 2) Fixed. 3) Not fixed. Can you provide a test file result in the unhandled exception that you describe? The option should be available because (i) the option is off by default and therefore the most novice users (that are least likely to identify the unhandled exception when it occurs) will be unaware of the option and (ii) the option has a larger net value available than not available. 4) Fixed. 5) Fixed. --- src/calibre/customize/__init__.py | 3 +- src/calibre/customize/ui.py | 4 +- src/calibre/gui2/__init__.py | 1 + src/calibre/gui2/add.py | 2 +- src/calibre/gui2/library/views.py | 3 +- src/calibre/gui2/preferences/adding.py | 1 + src/calibre/gui2/preferences/adding.ui | 26 +++++++++++- src/calibre/gui2/viewer/config.py | 8 ++++ src/calibre/gui2/viewer/config.ui | 14 +++++++ src/calibre/gui2/viewer/documentview.py | 2 + src/calibre/gui2/viewer/main.py | 54 +++++++++++++++++++++++-- 11 files changed, 109 insertions(+), 9 deletions(-) diff --git a/src/calibre/customize/__init__.py b/src/calibre/customize/__init__.py index 1f57305945..75ef4b0bd0 100644 --- a/src/calibre/customize/__init__.py +++ b/src/calibre/customize/__init__.py @@ -341,11 +341,12 @@ class FileTypePlugin(Plugin): # {{{ # Default implementation does nothing return path_to_ebook - def postimport(self, id): + def postimport(self, id, db): ''' Run post import. :param id: Library id of the added book. + :param db: Library db. ''' # }}} diff --git a/src/calibre/customize/ui.py b/src/calibre/customize/ui.py index 15d448161e..987a6fbb84 100644 --- a/src/calibre/customize/ui.py +++ b/src/calibre/customize/ui.py @@ -172,6 +172,8 @@ run_plugins_on_postprocess = functools.partial(_run_filetype_plugins, occasion='postprocess') def postimport_plugins(id, format): + from calibre.gui2.ui import get_gui + db = get_gui().current_db customization = config['plugin_customization'] format = format.lower() for plugin in _on_postimport.get(format, []): @@ -180,7 +182,7 @@ def postimport_plugins(id, format): plugin.site_customization = customization.get(plugin.name, '') with plugin: try: - plugin.postimport(id) + plugin.postimport(id, db) except: print 'Running file type plugin %s failed with traceback:'%plugin.name traceback.print_exc() diff --git a/src/calibre/gui2/__init__.py b/src/calibre/gui2/__init__.py index 895bb5a6b2..14776d1363 100644 --- a/src/calibre/gui2/__init__.py +++ b/src/calibre/gui2/__init__.py @@ -93,6 +93,7 @@ gprefs.defaults['tag_browser_dont_collapse'] = [] gprefs.defaults['edit_metadata_single_layout'] = 'default' gprefs.defaults['default_author_link'] = 'http://en.wikipedia.org/w/index.php?search={author}' gprefs.defaults['preserve_date_on_ctl'] = True +gprefs.defaults['manual_add_auto_convert'] = False gprefs.defaults['cb_fullscreen'] = False gprefs.defaults['worker_max_time'] = 0 gprefs.defaults['show_files_after_save'] = True diff --git a/src/calibre/gui2/add.py b/src/calibre/gui2/add.py index 4336148816..eb4a2105d7 100644 --- a/src/calibre/gui2/add.py +++ b/src/calibre/gui2/add.py @@ -233,7 +233,7 @@ class DBAdder(QObject): # {{{ with open(path, 'rb') as f: self.db.add_format(id, fmt, f, index_is_id=True, notify=False, replace=replace) - if gprefs['auto_add_auto_convert']: + if gprefs['manual_add_auto_convert']: of = prefs['output_format'] if not of == fmt.lower(): gui = self.parent._parent diff --git a/src/calibre/gui2/library/views.py b/src/calibre/gui2/library/views.py index 85636104cb..edfc0dc762 100644 --- a/src/calibre/gui2/library/views.py +++ b/src/calibre/gui2/library/views.py @@ -756,8 +756,7 @@ class BooksView(QTableView): # {{{ def keyPressEvent(self, event): if event.key() == Qt.Key_Return: - selected_rows = [r.row() for r in self.selectionModel().selectedRows()] - self.display_parent.iactions['View']._view_books(selected_rows) + self.display_parent.iactions['View'].view_book(True) else: return super(BooksView, self).keyPressEvent(event) diff --git a/src/calibre/gui2/preferences/adding.py b/src/calibre/gui2/preferences/adding.py index fafc5b5a1c..266b0ca9cc 100644 --- a/src/calibre/gui2/preferences/adding.py +++ b/src/calibre/gui2/preferences/adding.py @@ -28,6 +28,7 @@ class ConfigWidget(ConfigWidgetBase, Ui_Form): r('swap_author_names', prefs) r('add_formats_to_existing', prefs) r('preserve_date_on_ctl', gprefs) + r('manual_add_auto_convert', gprefs) choices = [ (_('Ignore duplicate incoming formats'), 'ignore'), (_('Overwrite existing duplicate formats'), 'overwrite'), diff --git a/src/calibre/gui2/preferences/adding.ui b/src/calibre/gui2/preferences/adding.ui index abf5d5f7a5..703d2de1a9 100644 --- a/src/calibre/gui2/preferences/adding.ui +++ b/src/calibre/gui2/preferences/adding.ui @@ -68,13 +68,37 @@ - + When using the "&Copy to library" action to copy books between libraries, preserve the date + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Automatically &convert added files to the current output format + + + + + diff --git a/src/calibre/gui2/viewer/config.py b/src/calibre/gui2/viewer/config.py index e3726c1ecb..10c519cad9 100644 --- a/src/calibre/gui2/viewer/config.py +++ b/src/calibre/gui2/viewer/config.py @@ -61,6 +61,10 @@ def config(defaults=None): help=_('Show reading position in fullscreen mode.')) c.add_opt('fullscreen_scrollbar', default=True, action='store_false', help=_('Show the scrollbar in fullscreen mode.')) + c.add_opt('fullscreen_message', default=True, + help=_('Show information message when enabling fullscreen.')) + c.add_opt('fullscreen_save_state', default=False, + help=_('Save the fullscreen state.')) c.add_opt('cols_per_screen', default=1) c.add_opt('use_book_margins', default=False, action='store_true') c.add_opt('top_margin', default=20) @@ -210,6 +214,8 @@ class ConfigDialog(QDialog, Ui_Dialog): self.opt_fullscreen_clock.setChecked(opts.fullscreen_clock) self.opt_fullscreen_scrollbar.setChecked(opts.fullscreen_scrollbar) self.opt_fullscreen_pos.setChecked(opts.fullscreen_pos) + self.opt_fullscreen_message.setChecked(opts.fullscreen_message) + self.opt_fullscreen_save_state.setChecked(opts.fullscreen_save_state) self.opt_cols_per_screen.setValue(opts.cols_per_screen) self.opt_override_book_margins.setChecked(not opts.use_book_margins) for x in ('top', 'bottom', 'side'): @@ -283,6 +289,8 @@ class ConfigDialog(QDialog, Ui_Dialog): c.set('fullscreen_clock', self.opt_fullscreen_clock.isChecked()) c.set('fullscreen_pos', self.opt_fullscreen_pos.isChecked()) c.set('fullscreen_scrollbar', self.opt_fullscreen_scrollbar.isChecked()) + c.set('fullscreen_message', self.opt_fullscreen_message.isChecked()) + c.set('fullscreen_save_state', self.opt_fullscreen_save_state.isChecked()) c.set('cols_per_screen', int(self.opt_cols_per_screen.value())) c.set('use_book_margins', not self.opt_override_book_margins.isChecked()) diff --git a/src/calibre/gui2/viewer/config.ui b/src/calibre/gui2/viewer/config.ui index abc5391ebf..23e31dd93f 100644 --- a/src/calibre/gui2/viewer/config.ui +++ b/src/calibre/gui2/viewer/config.ui @@ -402,6 +402,20 @@ QToolBox::tab:hover { + + + + Show fullscreen &message + + + + + + + Save fullscreen state + + + diff --git a/src/calibre/gui2/viewer/documentview.py b/src/calibre/gui2/viewer/documentview.py index c1927e1907..6bd6c7c740 100644 --- a/src/calibre/gui2/viewer/documentview.py +++ b/src/calibre/gui2/viewer/documentview.py @@ -145,6 +145,8 @@ class Document(QWebPage): # {{{ self.fullscreen_clock = opts.fullscreen_clock self.fullscreen_scrollbar = opts.fullscreen_scrollbar self.fullscreen_pos = opts.fullscreen_pos + self.fullscreen_message = opts.fullscreen_message + self.fullscreen_save_state = opts.fullscreen_save_state self.use_book_margins = opts.use_book_margins self.cols_per_screen = opts.cols_per_screen self.side_margin = opts.side_margin diff --git a/src/calibre/gui2/viewer/main.py b/src/calibre/gui2/viewer/main.py index c11fda7437..d1cb7b3d86 100644 --- a/src/calibre/gui2/viewer/main.py +++ b/src/calibre/gui2/viewer/main.py @@ -274,11 +274,36 @@ class EbookViewer(MainWindow, Ui_EbookViewer): self.tool_bar2.setContextMenuPolicy(Qt.PreventContextMenu) self.tool_bar.widgetForAction(self.action_bookmark).setPopupMode(QToolButton.MenuButtonPopup) self.action_full_screen.setCheckable(True) + self.full_screen_label = QLabel(''' +

+

%s

+

%s

+

%s

+

%s

+
+ '''%(_('Full screen mode'), + _('Right click to show controls'), + _('Tap in the left or right page margin to turn pages'), + _('Press Esc to quit')), + self) + self.full_screen_label.setVisible(False) + self.full_screen_label.setStyleSheet(''' + QLabel { + text-align: center; + background-color: white; + color: black; + border-width: 1px; + border-style: solid; + border-radius: 20px; + } + ''') self.window_mode_changed = None self.toggle_toolbar_action = QAction(_('Show/hide controls'), self) self.toggle_toolbar_action.setCheckable(True) self.toggle_toolbar_action.triggered.connect(self.toggle_toolbars) self.addAction(self.toggle_toolbar_action) + self.full_screen_label_anim = QPropertyAnimation( + self.full_screen_label, 'size') self.clock_label = QLabel('99:99', self) self.clock_label.setVisible(False) self.clock_label.setFocusPolicy(Qt.NoFocus) @@ -300,7 +325,7 @@ class EbookViewer(MainWindow, Ui_EbookViewer): self.pos_label.setFocusPolicy(Qt.NoFocus) self.clock_timer = QTimer(self) self.clock_timer.timeout.connect(self.update_clock) - + self.print_menu = QMenu() self.print_menu.addAction(QIcon(I('print-preview.png')), _('Print Preview')) self.action_print.setMenu(self.print_menu) @@ -425,7 +450,7 @@ class EbookViewer(MainWindow, Ui_EbookViewer): self.toggle_paged_mode(self.action_toggle_paged_mode.isChecked(), at_start=True) fullscreen = vprefs.get('fullscreen', None) - if fullscreen: + if fullscreen and self.view.document.fullscreen_save_state: self.showFullScreen() def lookup(self, word): @@ -486,6 +511,25 @@ class EbookViewer(MainWindow, Ui_EbookViewer): super(EbookViewer, self).showFullScreen() + def show_full_screen_label(self): + f = self.full_screen_label + f.setVisible(True) + height = 200 + width = int(0.7*self.view.width()) + f.resize(width, height) + f.move((self.view.width() - width)//2, (self.view.height()-height)//2) + a = self.full_screen_label_anim + a.setDuration(500) + a.setStartValue(QSize(width, 0)) + a.setEndValue(QSize(width, height)) + a.start() + QTimer.singleShot(3500, self.full_screen_label.hide) + self.view.document.switch_to_fullscreen_mode() + if self.view.document.fullscreen_clock: + self.show_clock() + if self.view.document.fullscreen_pos: + self.show_pos_label() + def show_clock(self): self.clock_label.setVisible(True) self.clock_label.setText(QTime(22, 33, @@ -536,6 +580,7 @@ class EbookViewer(MainWindow, Ui_EbookViewer): self.action_full_screen.setChecked(False) self.tool_bar.setVisible(True) self.tool_bar2.setVisible(True) + self.full_screen_label.setVisible(False) if hasattr(self, '_original_frame_margins'): om = self._original_frame_margins self.centralwidget.layout().setContentsMargins(om[0]) @@ -549,7 +594,10 @@ class EbookViewer(MainWindow, Ui_EbookViewer): if self.window_mode_changed: fs = self.window_mode_changed == 'fullscreen' self.window_mode_changed = None - if not fs: + if fs: + if self.view.document.fullscreen_message: + self.show_full_screen_label() + else: self.view.document.switch_to_window_mode() self.view.document.page_position.restore() self.scrolled(self.view.scroll_fraction) From 47bcd99264e43b341216b9525665de4ee034662c Mon Sep 17 00:00:00 2001 From: GRiker Date: Wed, 14 Nov 2012 09:00:58 -0700 Subject: [PATCH 03/28] Added library_name as available output field for CSV and XML catalogs. --- src/calibre/customize/__init__.py | 14 ++++++++++++-- src/calibre/library/catalogs/__init__.py | 2 +- src/calibre/library/catalogs/csv_xml.py | 10 ++++++++-- src/calibre/library/catalogs/epub_mobi.py | 4 +++- 4 files changed, 24 insertions(+), 6 deletions(-) diff --git a/src/calibre/customize/__init__.py b/src/calibre/customize/__init__.py index 2570efebe1..ceeea863d3 100644 --- a/src/calibre/customize/__init__.py +++ b/src/calibre/customize/__init__.py @@ -447,8 +447,8 @@ class CatalogPlugin(Plugin): # {{{ # Return a list of requested fields, with opts.sort_by first all_std_fields = set( ['author_sort','authors','comments','cover','formats', - 'id','isbn','ondevice','pubdate','publisher','rating', - 'series_index','series','size','tags','timestamp', + 'id','isbn','library_name','ondevice','pubdate','publisher', + 'rating','series_index','series','size','tags','timestamp', 'title_sort','title','uuid','languages','identifiers']) all_custom_fields = set(db.custom_field_keys()) for field in list(all_custom_fields): @@ -460,6 +460,16 @@ class CatalogPlugin(Plugin): # {{{ if opts.fields != 'all': # Make a list from opts.fields requested_fields = set(opts.fields.split(',')) + + # Validate requested_fields + if requested_fields - all_fields: + from calibre.library import current_library_name + invalid_fields = sorted(list(requested_fields - all_fields)) + print("invalid --fields specified: %s" % ', '.join(invalid_fields)) + print("available fields in '%s': %s" % + (current_library_name(), ', '.join(sorted(list(all_fields))))) + raise ValueError("unable to generate catalog with specified fields") + fields = list(all_fields & requested_fields) else: fields = list(all_fields) diff --git a/src/calibre/library/catalogs/__init__.py b/src/calibre/library/catalogs/__init__.py index a5c98955e8..fdc8a53eb3 100644 --- a/src/calibre/library/catalogs/__init__.py +++ b/src/calibre/library/catalogs/__init__.py @@ -9,7 +9,7 @@ __docformat__ = 'restructuredtext en' FIELDS = ['all', 'title', 'title_sort', 'author_sort', 'authors', 'comments', - 'cover', 'formats','id', 'isbn', 'ondevice', 'pubdate', 'publisher', + 'cover', 'formats','id', 'isbn', 'library_name','ondevice', 'pubdate', 'publisher', 'rating', 'series_index', 'series', 'size', 'tags', 'timestamp', 'uuid', 'languages', 'identifiers'] diff --git a/src/calibre/library/catalogs/csv_xml.py b/src/calibre/library/catalogs/csv_xml.py index 0b7df385a1..90843bf154 100644 --- a/src/calibre/library/catalogs/csv_xml.py +++ b/src/calibre/library/catalogs/csv_xml.py @@ -48,17 +48,18 @@ class CSV_XML(CatalogPlugin): "Applies to: CSV, XML output formats"))] def run(self, path_to_output, opts, db, notification=DummyReporter()): + from calibre.library import current_library_name from calibre.utils.date import isoformat from calibre.utils.html2text import html2text - from lxml import etree from calibre.utils.logging import default_log as log + from lxml import etree self.fmt = path_to_output.rpartition('.')[2] self.notification = notification if opts.verbose: opts_dict = vars(opts) - log("%s(): Generating %s" % (self.name,self.fmt.upper())) + log("%s('%s'): Generating %s" % (self.name, current_library_name(), self.fmt.upper())) if opts.connected_device['is_device_connected']: log(" connected_device: %s" % opts.connected_device['name']) if opts_dict['search_text']: @@ -110,6 +111,8 @@ class CSV_XML(CatalogPlugin): for field in fields: if field.startswith('#'): item = db.get_field(entry['id'],field,index_is_id=True) + elif field == 'library_name': + item = current_library_name() elif field == 'title_sort': item = entry['sort'] else: @@ -215,6 +218,9 @@ class CSV_XML(CatalogPlugin): fmt.append(E.format(f.replace(os.sep, '/'))) record.append(fmt) + if 'library_name' in fields: + record.append(E.library_name(current_library_name())) + with open(path_to_output, 'w') as f: f.write(etree.tostring(root, encoding='utf-8', xml_declaration=True, pretty_print=True)) diff --git a/src/calibre/library/catalogs/epub_mobi.py b/src/calibre/library/catalogs/epub_mobi.py index 0645416ecf..3b36642c10 100644 --- a/src/calibre/library/catalogs/epub_mobi.py +++ b/src/calibre/library/catalogs/epub_mobi.py @@ -14,6 +14,7 @@ from calibre import strftime from calibre.customize import CatalogPlugin from calibre.customize.conversion import OptionRecommendation, DummyReporter from calibre.ebooks import calibre_cover +from calibre.library import current_library_name from calibre.library.catalogs import AuthorSortMismatchException, EmptyCatalogException from calibre.ptempfile import PersistentTemporaryFile from calibre.utils.localization import get_lang @@ -208,8 +209,9 @@ class EPUB_MOBI(CatalogPlugin): build_log = [] - build_log.append(u"%s(): Generating %s %sin %s environment, locale: '%s'" % + build_log.append(u"%s('%s'): Generating %s %sin %s environment, locale: '%s'" % (self.name, + current_library_name(), self.fmt, 'for %s ' % opts.output_profile if opts.output_profile else '', 'CLI' if opts.cli_environment else 'GUI', From 26e0b8d2ecfb41b7dce64dfac7704dd1cb727694 Mon Sep 17 00:00:00 2001 From: GRiker Date: Thu, 15 Nov 2012 04:49:12 -0700 Subject: [PATCH 04/28] Fixed problem with CSV, XML library_name when alternate library is specified --- src/calibre/library/catalogs/csv_xml.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/calibre/library/catalogs/csv_xml.py b/src/calibre/library/catalogs/csv_xml.py index 90843bf154..49df903320 100644 --- a/src/calibre/library/catalogs/csv_xml.py +++ b/src/calibre/library/catalogs/csv_xml.py @@ -56,10 +56,13 @@ class CSV_XML(CatalogPlugin): self.fmt = path_to_output.rpartition('.')[2] self.notification = notification + current_library = current_library_name() + if getattr(opts, 'library_path', None): + current_library = os.path.basename(opts.library_path) if opts.verbose: opts_dict = vars(opts) - log("%s('%s'): Generating %s" % (self.name, current_library_name(), self.fmt.upper())) + log("%s('%s'): Generating %s" % (self.name, current_library, self.fmt.upper())) if opts.connected_device['is_device_connected']: log(" connected_device: %s" % opts.connected_device['name']) if opts_dict['search_text']: @@ -112,7 +115,7 @@ class CSV_XML(CatalogPlugin): if field.startswith('#'): item = db.get_field(entry['id'],field,index_is_id=True) elif field == 'library_name': - item = current_library_name() + item = current_library elif field == 'title_sort': item = entry['sort'] else: @@ -219,7 +222,7 @@ class CSV_XML(CatalogPlugin): record.append(fmt) if 'library_name' in fields: - record.append(E.library_name(current_library_name())) + record.append(E.library_name(current_library)) with open(path_to_output, 'w') as f: f.write(etree.tostring(root, encoding='utf-8', From 910c1a17e74f6284c265eba06a7fb9fc49e431b8 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Sun, 18 Nov 2012 08:28:05 +0530 Subject: [PATCH 05/28] Buchreport by a.peter --- recipes/buchreport.recipe | 45 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) create mode 100644 recipes/buchreport.recipe diff --git a/recipes/buchreport.recipe b/recipes/buchreport.recipe new file mode 100644 index 0000000000..68d98d0622 --- /dev/null +++ b/recipes/buchreport.recipe @@ -0,0 +1,45 @@ +from calibre.web.feeds.recipes import BasicNewsRecipe + +'''Calibre recipe to convert the RSS feeds of the Buchreport to an ebook.''' + +class Buchreport(BasicNewsRecipe) : + __author__ = 'a.peter' + __copyright__ = 'a.peter' + __license__ = 'GPL v3' + description = 'Buchreport' + version = 4 + title = u'Buchreport' + timefmt = ' [%d.%m.%Y]' + encoding = 'cp1252' + language = 'de_DE' + + + extra_css = 'body { margin-left: 0.00em; margin-right: 0.00em; } \ + article, articledate, articledescription { text-align: left; } \ + h1 { text-align: left; font-size: 140%; font-weight: bold; } \ + h2 { text-align: left; font-size: 100%; font-weight: bold; font-style: italic; } \ + h3 { text-align: left; font-size: 100%; font-weight: regular; font-style: italic; } \ + h4, h5, h6 { text-align: left; font-size: 100%; font-weight: bold; }' + + oldest_article = 7.0 + no_stylesheets = True + remove_javascript = True + use_embedded_content = False + publication_type = 'newspaper' + + remove_tags_before = dict(name='h2') + remove_tags_after = [ + dict(name='div', attrs={'style':["padding-top:10px;clear:both"]}) + ] + remove_tags = [ + dict(name='div', attrs={'style':["padding-top:10px;clear:both"]}), + dict(name='iframe'), + dict(name='img') + ] + + feeds = [ + (u'Buchreport', u'http://www.buchreport.de/index.php?id=5&type=100') + ] + + def get_masthead_url(self): + return 'http://www.buchreport.de/fileadmin/template/img/buchreport_logo.jpg' From d660f7f025e5bfe55f6b6bc73b994b36e677ba55 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Sun, 18 Nov 2012 09:00:30 +0530 Subject: [PATCH 06/28] Fix #1079130 (Updated recipe for Business standard) --- recipes/business_standard.recipe | 11 +++++++++-- recipes/icons/business_standard.png | Bin 290 -> 786 bytes 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/recipes/business_standard.recipe b/recipes/business_standard.recipe index badca48733..a61c32aa42 100644 --- a/recipes/business_standard.recipe +++ b/recipes/business_standard.recipe @@ -1,5 +1,5 @@ __license__ = 'GPL v3' -__copyright__ = '2009-2010, Darko Miletic ' +__copyright__ = '2009-2012, Darko Miletic ' ''' www.business-standard.com ''' @@ -14,10 +14,12 @@ class BusinessStandard(BasicNewsRecipe): max_articles_per_feed = 100 no_stylesheets = True use_embedded_content = False + auto_cleanup = False encoding = 'cp1252' publisher = 'Business Standard Limited' category = 'news, business, money, india, world' language = 'en_IN' + masthead_url = 'http://feeds.business-standard.com/images/logo_08.jpg' conversion_options = { 'comments' : description @@ -26,7 +28,7 @@ class BusinessStandard(BasicNewsRecipe): ,'publisher' : publisher ,'linearize_tables': True } - keep_only_tags=[dict(attrs={'class':'TableClas'})] + #keep_only_tags=[dict(name='td', attrs={'class':'TableClas'})] remove_tags = [ dict(name=['object','link','script','iframe','base','meta']) ,dict(attrs={'class':'rightDiv2'}) @@ -45,3 +47,8 @@ class BusinessStandard(BasicNewsRecipe): ,(u'Management & Mktg' , u'http://feeds.business-standard.com/rss/7_0.xml' ) ,(u'Opinion' , u'http://feeds.business-standard.com/rss/5_0.xml' ) ] + + def print_version(self, url): + l, s, tp = url.rpartition('/') + t, k, autono = l.rpartition('/') + return 'http://www.business-standard.com/india/printpage.php?autono=' + autono + '&tp=' + tp diff --git a/recipes/icons/business_standard.png b/recipes/icons/business_standard.png index 1edff420c0d49815ee1cb0cc518d43e654a9381c..f4c04e566a8a97586f3eb6d73a22b7aebbb543a6 100644 GIT binary patch delta 773 zcmV+g1N!`;0+I%h8Gix*005AYXf^-<0@+DKK~#904U$V}TvY&u|8wp=xsOcd&X`Fi zX>3d%gf4s#3R0pLs-jjTDHw|j6}pi=4Ag}MK~NV(=|-pvwTe((ST$Kl?V`k;0qw$y zE|LaJg9FLj3Fgk@zUH2L9^UoG|HJ?D5uJrOFEKXI@8NsA^wB8ZiUBNt4^wE)UUAV|Wm}nC%G`i8sXK%G`TpxA@eZs~LJvMXt;@JEti0=A(i?>(* zI5_{puvea$KY!=ehbd%)a_N~A&aLMk zMx!x%{-oAI{mG;KHh=CJMbtz#&8ho@dXpCN1eEH_K8a#6M^rRMM zt4d$|c=@5HpHMsgZ;P*&f(Au}lC(I?@KVpqs$96d=FKj=y1g0tOSkGG3J96H{&Vw< zv+K>^{(tiKmR3|adG2T~cP%3#_J2L>U`qP3yORXX&p+|M_{vq4xl13-9)G+P?INMn z0L#DP`Pn7jPUHi6aPY|9eV}M8kL82UKS(ee$ zy{ak~(m7J-#FQFIB)n<{mxLg?A4_dhLSsv}5s$+#b30+mRfftWH4P}_CX@jq*QO?f z>!2f@k<2cDC;_6NtT0uXpjx+8$jx#EusR_eJFwXQ#4>9)tN6{{00000NkvXXu0mjf DSv=Y-*ffQySyY)f>Xk{Hls^`sKotm-PiMGV@2)b+9PyJFxTyYY+20X~xot zvg$U8Hc>!hRZCnWN>UO_QmvAUQh^kMk%57wE)W@+gcw*_nVMP|8R#0ASs54@Tuu-{ n(U6;;l9^VCtijO0%E;Wx$Oxk0q3iR7Kn)C@u6{1-oD!M<(WFqR From 34a2758d3cd12e0b5905fe2d2c1e7408c276ebe0 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Sun, 18 Nov 2012 11:46:46 +0530 Subject: [PATCH 07/28] ... --- src/calibre/ebooks/__init__.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/calibre/ebooks/__init__.py b/src/calibre/ebooks/__init__.py index a5417be220..bdbdca85c8 100644 --- a/src/calibre/ebooks/__init__.py +++ b/src/calibre/ebooks/__init__.py @@ -263,8 +263,11 @@ def generate_masthead(title, output_path=None, width=600, height=60): masthead_font_family = recs.get('masthead_font', 'Default') if masthead_font_family != 'Default': - from calibre.utils.fonts.scanner import font_scanner - faces = font_scanner.fonts_for_family(masthead_font_family) + from calibre.utils.fonts.scanner import font_scanner, NoFonts + try: + faces = font_scanner.fonts_for_family(masthead_font_family) + except NoFonts: + faces = [] if faces: font_path = faces[0]['path'] From f0d32cb528f39fb2d847a1cdb8444e16163ce2d6 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Sun, 18 Nov 2012 11:48:27 +0530 Subject: [PATCH 08/28] Edit metadata dialog: Show the size of the current book cover in the edit metadata dialog. Fixes #1079781 ([Enhancement] Display cover size in pixels) --- src/calibre/gui2/metadata/basic_widgets.py | 1 + src/calibre/gui2/widgets.py | 21 +++++++++++++++++---- 2 files changed, 18 insertions(+), 4 deletions(-) diff --git a/src/calibre/gui2/metadata/basic_widgets.py b/src/calibre/gui2/metadata/basic_widgets.py index 409088d315..95255d4abb 100644 --- a/src/calibre/gui2/metadata/basic_widgets.py +++ b/src/calibre/gui2/metadata/basic_widgets.py @@ -867,6 +867,7 @@ class Cover(ImageView): # {{{ def __init__(self, parent): ImageView.__init__(self, parent) + self.show_size = True self.dialog = parent self._cdata = None self.cover_changed.connect(self.set_pixmap_from_data) diff --git a/src/calibre/gui2/widgets.py b/src/calibre/gui2/widgets.py index 7730d2d733..e2f0e0bfd2 100644 --- a/src/calibre/gui2/widgets.py +++ b/src/calibre/gui2/widgets.py @@ -283,6 +283,7 @@ class ImageView(QWidget, ImageDropMixin): # {{{ self.setMinimumSize(QSize(150, 200)) ImageDropMixin.__init__(self) self.draw_border = True + self.show_size = False def setPixmap(self, pixmap): if not isinstance(pixmap, QPixmap): @@ -305,6 +306,7 @@ class ImageView(QWidget, ImageDropMixin): # {{{ if pmap.isNull(): return w, h = pmap.width(), pmap.height() + ow, oh = w, h cw, ch = self.rect().width(), self.rect().height() scaled, nw, nh = fit_image(w, h, cw, ch) if scaled: @@ -317,12 +319,23 @@ class ImageView(QWidget, ImageDropMixin): # {{{ p = QPainter(self) p.setRenderHints(QPainter.Antialiasing | QPainter.SmoothPixmapTransform) p.drawPixmap(target, pmap) - pen = QPen() - pen.setWidth(self.BORDER_WIDTH) - p.setPen(pen) if self.draw_border: + pen = QPen() + pen.setWidth(self.BORDER_WIDTH) + p.setPen(pen) p.drawRect(target) - #p.drawRect(self.rect()) + if self.show_size: + sztgt = target.adjusted(0, 0, 0, -4) + f = p.font() + f.setBold(True) + f.setPointSize(12) + p.setFont(f) + sz = u'\u00a0%d x %d\u00a0'%(ow, oh) + flags = Qt.AlignBottom|Qt.AlignRight|Qt.TextSingleLine + szrect = p.boundingRect(sztgt, flags, sz) + p.fillRect(szrect.adjusted(0, 0, 0, 4), QColor(0, 0, 0, 200)) + p.setPen(QPen(QColor(255,255,255))) + p.drawText(sztgt, flags, sz) p.end() # }}} From 41f93db6f6180af233e5aa0e122f433e531e77b5 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Sun, 18 Nov 2012 14:08:03 +0530 Subject: [PATCH 09/28] Fix rows in the device books view sometimes being too high --- src/calibre/gui2/library/models.py | 6 ++++++ src/calibre/gui2/library/views.py | 17 +++++++++++++---- 2 files changed, 19 insertions(+), 4 deletions(-) diff --git a/src/calibre/gui2/library/models.py b/src/calibre/gui2/library/models.py index 49e5c497fe..b2b2597434 100644 --- a/src/calibre/gui2/library/models.py +++ b/src/calibre/gui2/library/models.py @@ -1057,6 +1057,7 @@ class DeviceBooksModel(BooksModel): # {{{ booklist_dirtied = pyqtSignal() upload_collections = pyqtSignal(object) + resize_rows = pyqtSignal() def __init__(self, parent): BooksModel.__init__(self, parent) @@ -1163,6 +1164,11 @@ class DeviceBooksModel(BooksModel): # {{{ return flags def search(self, text, reset=True): + # This should not be here, but since the DeviceBooksModel does not + # implement count_changed and I am too lazy to fix that, this kludge + # will have to do + self.resize_rows.emit() + if not text or not text.strip(): self.map = list(range(len(self.db))) else: diff --git a/src/calibre/gui2/library/views.py b/src/calibre/gui2/library/views.py index 452cab2430..c0d5a0e827 100644 --- a/src/calibre/gui2/library/views.py +++ b/src/calibre/gui2/library/views.py @@ -90,6 +90,7 @@ class BooksView(QTableView): # {{{ def __init__(self, parent, modelcls=BooksModel, use_edit_metadata_dialog=True): QTableView.__init__(self, parent) + self.row_sizing_done = False if not tweaks['horizontal_scrolling_per_column']: self.setHorizontalScrollMode(self.ScrollPerPixel) @@ -141,6 +142,8 @@ class BooksView(QTableView): # {{{ self.display_parent = parent self._model = modelcls(self) self.setModel(self._model) + self._model.count_changed_signal.connect(self.do_row_sizing, + type=Qt.QueuedConnection) self.setSelectionBehavior(QAbstractItemView.SelectRows) self.setSortingEnabled(True) self.selectionModel().currentRowChanged.connect(self._model.current_changed) @@ -521,13 +524,17 @@ class BooksView(QTableView): # {{{ self.apply_state(old_state, max_sort_levels=max_levels) self.column_header.blockSignals(False) - # Resize all rows to have the correct height - if self.model().rowCount(QModelIndex()) > 0: - self.resizeRowToContents(0) - self.verticalHeader().setDefaultSectionSize(self.rowHeight(0)) + self.do_row_sizing() self.was_restored = True + def do_row_sizing(self): + # Resize all rows to have the correct height + if not self.row_sizing_done and self.model().rowCount(QModelIndex()) > 0: + self.resizeRowToContents(0) + self.verticalHeader().setDefaultSectionSize(self.rowHeight(0)) + self.row_sizing_done = True + def resize_column_to_fit(self, column): col = self.column_map.index(column) self.column_resized(col, self.columnWidth(col), self.columnWidth(col)) @@ -943,6 +950,8 @@ class DeviceBooksView(BooksView): # {{{ def __init__(self, parent): BooksView.__init__(self, parent, DeviceBooksModel, use_edit_metadata_dialog=False) + self._model.resize_rows.connect(self.do_row_sizing, + type=Qt.QueuedConnection) self.can_add_columns = False self.columns_resized = False self.resize_on_select = False From c412eadeb09e5ecded57da3540e03fa80c9a10c8 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Sun, 18 Nov 2012 14:17:20 +0530 Subject: [PATCH 10/28] Windows driver for Bebook Pure. Fixes #1078600 (Add device support) --- src/calibre/devices/teclast/driver.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/calibre/devices/teclast/driver.py b/src/calibre/devices/teclast/driver.py index d0818b27a5..192f03cb45 100644 --- a/src/calibre/devices/teclast/driver.py +++ b/src/calibre/devices/teclast/driver.py @@ -19,9 +19,9 @@ class TECLAST_K3(USBMS): PRODUCT_ID = [0x3203] BCD = [0x0000, 0x0100] - VENDOR_NAME = ['TECLAST', 'IMAGIN', 'RK28XX', 'PER3274B'] + VENDOR_NAME = ['TECLAST', 'IMAGIN', 'RK28XX', 'PER3274B', 'BEBOOK'] WINDOWS_MAIN_MEM = WINDOWS_CARD_A_MEM = ['DIGITAL_PLAYER', 'TL-K5', - 'EREADER', 'USB-MSC', 'PER3274B'] + 'EREADER', 'USB-MSC', 'PER3274B', 'BEBOOK'] MAIN_MEMORY_VOLUME_LABEL = 'K3 Main Memory' STORAGE_CARD_VOLUME_LABEL = 'K3 Storage Card' From 5e4f2aa6ac2b320de2bdbd072b188860ce52584f Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Sun, 18 Nov 2012 21:51:37 +0530 Subject: [PATCH 11/28] Nicer error message --- src/calibre/gui2/actions/choose_library.py | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/calibre/gui2/actions/choose_library.py b/src/calibre/gui2/actions/choose_library.py index 829268adee..3e8d6154b3 100644 --- a/src/calibre/gui2/actions/choose_library.py +++ b/src/calibre/gui2/actions/choose_library.py @@ -320,15 +320,23 @@ class ChooseLibraryAction(InterfaceAction): _('Path to library too long. Must be less than' ' %d characters.')%LibraryDatabase2.WINDOWS_LIBRARY_PATH_LIMIT, show=True) + if not os.path.exists(loc): + error_dialog(self.gui, _('Not found'), + _('Cannot rename as no library was found at %s. ' + 'Try switching to this library first, then switch back ' + 'and retry the renaming.')%loc, show=True) + return try: os.rename(loc, newloc) except: import traceback + det_msg = 'Location: %r New Location: %r\n%s'%(loc, newloc, + traceback.format_exc()) error_dialog(self.gui, _('Rename failed'), _('Failed to rename the library at %s. ' 'The most common cause for this is if one of the files' ' in the library is open in another program.') % loc, - det_msg=traceback.format_exc(), show=True) + det_msg=det_msg, show=True) return self.stats.rename(location, newloc) self.build_menus() From 4f43a7fa8fceec82014bf87aabd1a6ac7418a17f Mon Sep 17 00:00:00 2001 From: John Peterson Date: Sun, 18 Nov 2012 17:01:20 +0000 Subject: [PATCH 12/28] Response to merge review. 3) Fixed. --- src/calibre/ebooks/oeb/iterator/book.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/calibre/ebooks/oeb/iterator/book.py b/src/calibre/ebooks/oeb/iterator/book.py index 65fc48cad7..37ef43f5f1 100644 --- a/src/calibre/ebooks/oeb/iterator/book.py +++ b/src/calibre/ebooks/oeb/iterator/book.py @@ -153,7 +153,7 @@ class EbookIterator(BookmarksMixin): mime_type='application/xhtml+xml')] self.delete_on_exit.append(cfile) elif self.exclude_cover: - self.spine.remove(self.spine[0]) + self.spine.remove(cover) if self.opf.path_to_html_toc is not None and \ self.opf.path_to_html_toc not in self.spine: From 2822623f7e154eaa17f386157d3f5f69b4a6fa98 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20D=C5=82ugosz?= Date: Sun, 18 Nov 2012 18:47:45 +0100 Subject: [PATCH 13/28] telepolis --- recipes/icons/telepolis_pl.png | Bin 0 -> 1179 bytes recipes/telepolis_pl.recipe | 67 +++++++++++++++++++++++++++++++++ 2 files changed, 67 insertions(+) create mode 100644 recipes/icons/telepolis_pl.png create mode 100644 recipes/telepolis_pl.recipe diff --git a/recipes/icons/telepolis_pl.png b/recipes/icons/telepolis_pl.png new file mode 100644 index 0000000000000000000000000000000000000000..0b94658d947f4785ef1618f478fcad335ceeb748 GIT binary patch literal 1179 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`jKx9jP7LeL$-D$|*pj^6T^Rm@ z;DWu&Cj&(|3p^r=85p>QL70(Y)*K0-AbW|YuPgf<4mn|Y=H#{UQy3Unq&;06Lo5Ut z1z6ty2CAS8Fp{EumWE0@Q2h_69*(pdcPd%1830ts0F=ZgRe>gm-2jkgpdtT}6$8bv zx)$sLY)(Vg3}d4kvZkb%5#|L9rvWu}05ROZAVc;aIP{-^5#bO=ds{{(kOa{Hr1?LJ z&lp%h&Isx0MHq`P4ZEut8Btut0@VC}|G~p3#)6!NFb6*i76urq&;tyr0K-)<126)N zq-eyj9)|$B*MMGUfVlv}X+SeDT!n5f&_K8W=yIT>T!|rwO#tK!bbw6^T@BUvA8$Gt T'), + lambda match: ''), + (re.compile(r'Zobacz:.*?', re.DOTALL), + lambda match: ''), + (re.compile(r'<-ankieta.*?>'), + lambda match: ''), + (re.compile(r'\(Q\!\)'), + lambda match: ''), + (re.compile(r'\(plik.*?\)'), + lambda match: ''), + (re.compile(r'', re.DOTALL), + lambda match: '') + ] + + extra_css = '''.tb { font-weight: bold; font-size: 20px;}''' + + feeds = [ + (u'Wiadomości', u'http://www.telepolis.pl/rss/news.php'), + (u'Artykuły', u'http://www.telepolis.pl/rss/artykuly.php') + ] + + def print_version(self, url): + if 'news.php' in url: + print_url = url.replace('news.php', 'news_print.php') + else: + print_url = url.replace('artykuly.php', 'art_print.php') + return print_url + + def preprocess_html(self, soup): + for image in soup.findAll('img'): + if 'm.jpg' in image['src']: + image_big = image['src'] + image_big = image_big.replace('m.jpg', '.jpg') + image['src'] = image_big + logo = soup.find('tr') + logo.extract() + for tag in soup.findAll('tr'): + for strings in ['Wiadomość wydrukowana', 'copyright']: + if strings in self.tag_to_string(tag): + tag.extract() + return self.adeify_images(soup) From fc2023911f9e885ddaf1ec1cbd9b39c8c396267b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20D=C5=82ugosz?= Date: Sun, 18 Nov 2012 18:48:30 +0100 Subject: [PATCH 14/28] antyweb --- recipes/antyweb.recipe | 49 ++++++++++++++++++++++++++++++++++++++ recipes/icons/antyweb.png | Bin 0 -> 668 bytes 2 files changed, 49 insertions(+) create mode 100644 recipes/antyweb.recipe create mode 100644 recipes/icons/antyweb.png diff --git a/recipes/antyweb.recipe b/recipes/antyweb.recipe new file mode 100644 index 0000000000..0b8c5af9f4 --- /dev/null +++ b/recipes/antyweb.recipe @@ -0,0 +1,49 @@ +import re + +from calibre.web.feeds.news import BasicNewsRecipe + +class AntywebRecipe(BasicNewsRecipe): + encoding = 'utf-8' + __license__ = 'GPL v3' + __author__ = u'Artur Stachecki ' + language = 'pl' + version = 1 + title = u'Antyweb' + category = u'News' + description = u'Blog o internecie i nowych technologiach' + cover_url='' + remove_empty_feeds= True + auto_cleanup = False + no_stylesheets=True + use_embedded_content = False + oldest_article = 1 + max_articles_per_feed = 100 + remove_javascript = True + simultaneous_downloads = 3 + + keep_only_tags =[] + keep_only_tags.append(dict(name = 'h1', attrs = { 'class' : 'mm-article-title'})) + keep_only_tags.append(dict(name = 'div', attrs = {'class' : 'mm-article-content'})) + + + remove_tags =[] + remove_tags.append(dict(name = 'h2', attrs = {'class' : 'widgettitle'})) + remove_tags.append(dict(name = 'img', attrs = {'class' : 'alignleft'})) + remove_tags.append(dict(name = 'div', attrs = {'class' : 'float: right;margin-left:1em;margin-bottom: 0.5em;padding-bottom: 3px; width: 72px;'})) + remove_tags.append(dict(name = 'img', attrs = {'src' : 'http://antyweb.pl/wp-content/uploads/2011/09/HOSTERSI_testy_pasek600x30.gif'})) + remove_tags.append(dict(name = 'div', attrs = {'class' : 'podwpisowe'})) + + + extra_css = ''' + body {font-family: verdana, arial, helvetica, geneva, sans-serif ;} + ''' + + feeds = [ + (u'Artykuly', u'feed://feeds.feedburner.com/Antyweb?format=xml'), + ] + def preprocess_html(self, soup): + for alink in soup.findAll('a'): + if alink.string is not None: + tstr = alink.string + alink.replaceWith(tstr) + return soup diff --git a/recipes/icons/antyweb.png b/recipes/icons/antyweb.png new file mode 100644 index 0000000000000000000000000000000000000000..8ca9870f60072171a8822aef03f2c67ddeddc594 GIT binary patch literal 668 zcmV;N0%QG&P) zsc2A?peR)nb0v-DntK!94mXx=9em;N42SdZp7T79kW%uNr@Uzz&;TBek7dQS@rp$% zwvBUlhm*@uxxc4k+dSvJ`0Io6_ddA05glSTBU1YtX)CZ#RINix4&9NBN$r5vOq*hjFO(ZB?U;8&`h{cdV zLsu7HHa7UWv4Lt?bk59>+T0`%i%|n=fEkZtB@*0UUQ#O-{Tl?MQ54{JF2~c;6I~-C zbdQemV|khO!9l9FO=5PIYNbNy@DNp1sX7i`spJ!wk%%va>!Os)eE;^1_~ay~=`>f{ z+w@FL5$^BDYHQ^%(gJ`H2=FeMer)eJ^rsg;l zvRUHO(-e=6P=MOwqfcPAwjzLVZ!cBP<7{`AtDPM_EiF;b=c&3bLf1K4T?NbH@!|pj z)Ji38Z*HV!G&Nx~Hu7;{0mrr}Y;Ex?805pm1Wg?s>@O~&s4C9+Iq6giY1{q}N+mqU zK}acC@92<^&(BEL#VD8Yq{IWvx_qyeeU(}QLf08OIPjgQ1%q&NgIYJxt9cLtp=k)q z@+%ZVX>3F^G$5i;yq6cBAUZb2^T`Qnb2C~bf*KB^hC+W)4~Nl1A#}?^4+PKy0W{Mj z(bp%$YZ`xMGEy^548y=M3^Y}xGa42D3zK+joc{n$!0;i>Ta9r50000 Date: Sun, 18 Nov 2012 18:51:39 +0100 Subject: [PATCH 15/28] f1 ultra --- recipes/f1_ultra.recipe | 35 +++++++++++++++++++++++++++++++++++ recipes/icons/f1_ultra.png | Bin 0 -> 490 bytes 2 files changed, 35 insertions(+) create mode 100644 recipes/f1_ultra.recipe create mode 100644 recipes/icons/f1_ultra.png diff --git a/recipes/f1_ultra.recipe b/recipes/f1_ultra.recipe new file mode 100644 index 0000000000..ada82542fc --- /dev/null +++ b/recipes/f1_ultra.recipe @@ -0,0 +1,35 @@ +from calibre.web.feeds.news import BasicNewsRecipe +import re + +class f1ultra(BasicNewsRecipe): + title = u'Formuła 1 - F1 ultra' + __license__ = 'GPL v3' + __author__ = 'MrStefan , Artur Stachecki ' + language = 'pl' + description =u'Formuła 1, Robert Kubica, F3, GP2 oraz inne serie wyścigowe.' + masthead_url='http://www.f1ultra.pl/templates/f1ultra/images/logo.gif' + remove_empty_feeds= True + oldest_article = 1 + max_articles_per_feed = 100 + remove_javascript=True + no_stylesheets=True + + keep_only_tags =[(dict(name = 'div', attrs = {'id' : 'main'}))] + remove_tags_after =[dict(attrs = {'style' : 'margin-top:5px;margin-bottom:5px;display: inline;'})] + remove_tags =[(dict(attrs = {'class' : ['buttonheading', 'avPlayerContainer', 'createdate']}))] + remove_tags.append(dict(attrs = {'title' : ['PDF', 'Drukuj', 'Email']})) + remove_tags.append(dict(name = 'form', attrs = {'method' : 'post'})) + remove_tags.append(dict(name = 'hr', attrs = {'size' : '2'})) + + preprocess_regexps = [(re.compile(r'align="left"'), lambda match: ''), + (re.compile(r'align="right"'), lambda match: ''), + (re.compile(r'width=\"*\"'), lambda match: ''), + (re.compile(r'\'), lambda match: '')] + + + extra_css = '''.contentheading { font-size: 1.4em; font-weight: bold; } + img { display: block; clear: both;} + ''' + remove_attributes = ['width','height','position','float','padding-left','padding-right','padding','text-align'] + + feeds = [(u'F1 Ultra', u'http://www.f1ultra.pl/index.php?option=com_rd_rss&id=1&Itemid=245')] diff --git a/recipes/icons/f1_ultra.png b/recipes/icons/f1_ultra.png new file mode 100644 index 0000000000000000000000000000000000000000..f45a94f53adf1189058039c877c0735420896e5f GIT binary patch literal 490 zcmV8|3!g$Vuv>%y+2U5YC| zA`@DiXqjoac-n6931U&*nNUrJ}D%ss^pm9PV<3 z)XpbbN|96z>a`k)=!bufk4PULa9*v(3h0K#V&xs4eE~OIH!KQEi}3=T(G3d#TWT~* zQ(&5;BYe1Aju*HzO@7vD;o-T}6$YQb0HE*51lq?B)NeSzgM1ztJh+bIXHO18XiJUU zt0nrUXGQzeY^jlcHreh#`~3?|HSk;)fU$obV!R;k;sD;@P`kk2W|LH8oZ{n$ zTrIrx0o>(EjO&KQKg1<}ce%jI@~PJzJ&=C`+ayT5m<;9xtkH3ougsWI~U4T(%f1Q$d(T(33k;Og`{ zN!1A9K(QDezK_dJfx2OZg2K*va2W`UCJ+>)7ncCJrt11dqv^$k>o|UWro`^q^^A4b g<-Oy?1oq Date: Sun, 18 Nov 2012 18:52:01 +0100 Subject: [PATCH 16/28] bankier.pl --- recipes/bankier_pl.recipe | 51 +++++++++++++++++++++++++++++++++++ recipes/icons/bankier_pl.png | Bin 0 -> 190 bytes 2 files changed, 51 insertions(+) create mode 100644 recipes/bankier_pl.recipe create mode 100644 recipes/icons/bankier_pl.png diff --git a/recipes/bankier_pl.recipe b/recipes/bankier_pl.recipe new file mode 100644 index 0000000000..d65b0c17ed --- /dev/null +++ b/recipes/bankier_pl.recipe @@ -0,0 +1,51 @@ +#!/usr/bin/env python + +__license__ = 'GPL v3' +__author__ = 'teepel ' + +''' +bankier.pl +''' + +from calibre.web.feeds.news import BasicNewsRecipe +import re + +class bankier(BasicNewsRecipe): + title = u'Bankier.pl' + __author__ = 'teepel ' + language = 'pl' + description ='Polski portal finansowy. Informacje o: gospodarka, inwestowanie, finanse osobiste, prowadzenie firmy, kursy walut, notowania akcji, fundusze.' + masthead_url='http://www.bankier.pl/gfx/hd-mid-02.gif' + INDEX='http://bankier.pl/' + remove_empty_feeds= True + oldest_article = 1 + max_articles_per_feed = 100 + remove_javascript=True + no_stylesheets=True + simultaneous_downloads = 5 + + keep_only_tags =[] + keep_only_tags.append(dict(name = 'div', attrs = {'align' : 'left'})) + + remove_tags =[] + remove_tags.append(dict(name = 'table', attrs = {'cellspacing' : '2'})) + remove_tags.append(dict(name = 'div', attrs = {'align' : 'center'})) + remove_tags.append(dict(name = 'img', attrs = {'src' : '/gfx/hd-mid-02.gif'})) + #remove_tags.append(dict(name = 'a', attrs = {'target' : '_blank'})) + #remove_tags.append(dict(name = 'br', attrs = {'clear' : 'all'})) + + feeds = [ + (u'Wiadomości dnia', u'http://feeds.feedburner.com/bankier-wiadomosci-dnia'), + (u'Finanse osobiste', u'http://feeds.feedburner.com/bankier-finanse-osobiste'), + (u'Firma', u'http://feeds.feedburner.com/bankier-firma'), + (u'Giełda', u'http://feeds.feedburner.com/bankier-gielda'), + (u'Rynek walutowy', u'http://feeds.feedburner.com/bankier-rynek-walutowy'), + (u'Komunikaty ze spółek', u'http://feeds.feedburner.com/bankier-espi'), + ] + def print_version(self, url): + segment = url.split('.') + urlPart = segment[2] + segments = urlPart.split('-') + urlPart2 = segments[-1] + return 'http://www.bankier.pl/wiadomosci/print.html?article_id=' + urlPart2 + \ No newline at end of file diff --git a/recipes/icons/bankier_pl.png b/recipes/icons/bankier_pl.png new file mode 100644 index 0000000000000000000000000000000000000000..c26f006a57cf1532311ef04ae4a2c42cbfeff33f GIT binary patch literal 190 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!73?$#)eFPGa4)6(a1=3&QSW?_tNldzopr0Nr>tNdN!< literal 0 HcmV?d00001 From 1b637e7f15e6220d3d3ad4ff5a283e518758e954 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20D=C5=82ugosz?= Date: Sun, 18 Nov 2012 18:52:19 +0100 Subject: [PATCH 17/28] myapple --- recipes/icons/myapple_pl.png | Bin 0 -> 1176 bytes recipes/myapple_pl.recipe | 50 +++++++++++++++++++++++++++++++++++ 2 files changed, 50 insertions(+) create mode 100644 recipes/icons/myapple_pl.png create mode 100644 recipes/myapple_pl.recipe diff --git a/recipes/icons/myapple_pl.png b/recipes/icons/myapple_pl.png new file mode 100644 index 0000000000000000000000000000000000000000..a68cf4e7efc3c395730cf255545eb990e9899993 GIT binary patch literal 1176 zcma))YfO_@7{?DH5$Ri6CeQ|uAp}^4Y;EPXru0T{FO*s;g`yCy1qv1zb~1=^al@b{ z(*-t3#JKeeEw`6a!E(P8Au1s1bdyChV73k06!@?i27B2)ZJ&0M=lst9g@o0LIS{<0|2!Z076BZHH8Miz5`-mDi=+-v2^i^yNayNh&&`l zHOQ8A9gXYt0uE3$mdd_?&ycZ^7iv{c-0tq~q;sUi9RGUmVTm;%r{@FZfZ*dnfk5Y{ zuyC?FKfGcqPVNZ)da*Xk607Wusk7w?G=XJ9aM=K?7~nK7)tsBiJZlwCIR_ru=emBp znc4Mz&8Y0Nse)^>CB;b8sV<7Fmz3B+Wa)4cQ%I4S)_6O*dMr(7Jj~YMlMKYn9tvzE z!X5q$EhIJtr*wv;bwxDTa@cB*crC=&5em$-`mvMw{nXMy!QA?UK<~@^7UCHQsm9Qa zjo+GVCH!_`dQW84on*d&02>43{q*HGGfFF;r}sHGeX7h%MT_$czS8dSsv)VcBT#G# zY8=lM>fdE(arynUsWl6;4VR$vDe8&se0=qUDV*C*%<79}-NdogUd8=%r{$HS;#>0UJj0}@nUIk3=KA^Js?5vP1j)Adc43$p*;oy5fz6vM0aE+6V|x!iRx0sPeonm$ z51SmRJRC;6o{@h1#rWcEtJ|bJnjC%XR-B@*G1R|xVLn$LZ=3n9%sAp2-n3x*#UAzG zd)pJGb?5-@+QPk8j_9f9cb_iQ@qjBs%nTwGmsB+66hFKF+WkmSb{wT7O$FT&oL*l4 zeSgX~^|HLBwpH&pkNfY;JKkZA7Vv%^?mXSmw)_WXV?)=lkK)#dxpFxv@t*Hj>9ZHs zTTGe5TBEhl(f0$jx>w`vyuOf)R7w-_n}esHBt9@bJ*rSBOVcmDP1I@3rXfSR&#$GH z(Vc@%$Q^)|X4=b#J*E)!wV^7mOjVGpl2h|5A-IO?&Oe+=EshCb8)V6KSM&Ca>-7Y2LPCjbBd literal 0 HcmV?d00001 diff --git a/recipes/myapple_pl.recipe b/recipes/myapple_pl.recipe new file mode 100644 index 0000000000..eee333012c --- /dev/null +++ b/recipes/myapple_pl.recipe @@ -0,0 +1,50 @@ +import re + +from calibre.web.feeds.news import BasicNewsRecipe + +class MyAppleRecipe(BasicNewsRecipe): + __license__ = 'GPL v3' + __author__ = u'Artur Stachecki ' + language = 'pl' + version = 1 + + title = u'MyApple.pl' + category = u'News' + description = u' Największy w Polsce serwis zajmujący się tematyką związaną z Apple i wszelkimi produktami tej firmy.' + cover_url='' + remove_empty_feeds= True + no_stylesheets=True + oldest_article = 7 + max_articles_per_feed = 100000 + recursions = 0 + + no_stylesheets = True + remove_javascript = True + simultaneous_downloads = 3 + + keep_only_tags =[] + keep_only_tags.append(dict(name = 'div', attrs = {'id' : 'article_content'})) + + remove_tags =[] + remove_tags.append(dict(name = 'div', attrs = {'class' : 'article_author_date_comment_container'})) + remove_tags.append(dict(name = 'div', attrs = {'class' : 'fullwidth'})) + remove_tags.append(dict(name = 'div', attrs = {'class' : 'cmslinks'})) + remove_tags.append(dict(name = 'div', attrs = {'class' : 'googleads-468'})) + remove_tags.append(dict(name = 'div', attrs = {'id' : 'comments'})) + + + extra_css = ''' + body {font-family: verdana, arial, helvetica, geneva, sans-serif ;} + td.contentheading{font-size: large; font-weight: bold;} + ''' + + feeds = [ + ('News', 'feed://myapple.pl/external.php?do=rss&type=newcontent§ionid=1&days=120&count=10'), + ] + + def preprocess_html(self, soup): + for alink in soup.findAll('a'): + if alink.string is not None: + tstr = alink.string + alink.replaceWith(tstr) + return soup \ No newline at end of file From b7bd073d4a486c911ae727479f28cbe632dd00d8 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Sun, 18 Nov 2012 23:53:33 +0530 Subject: [PATCH 18/28] Various Czech and Hungarian recipes by bubak --- recipes/aktualne.cz.recipe | 69 +++++++++++++++++++++++ recipes/blesk.recipe | 55 ++++++++++++++++++ recipes/ceska_pozice.recipe | 68 ++++++++++++++++++++++ recipes/ceske_noviny.recipe | 30 ++++++++++ recipes/cesky_rozhlas_6.recipe | 26 +++++++++ recipes/demagog.cz.recipe | 39 +++++++++++++ recipes/denik.cz.recipe | 36 ++++++++++++ recipes/denik_referendum.recipe | 28 +++++++++ recipes/ihned.cz.recipe | 36 ++++++++++++ recipes/insider.recipe | 59 +++++++++++++++++++ recipes/kudy_z_nudy.recipe | 32 +++++++++++ recipes/lidovky.recipe | 40 +++++++++++++ recipes/metropol_tv.recipe | 29 ++++++++++ recipes/nadacni_fond_proti_korupci.recipe | 30 ++++++++++ recipes/nepszabadsag.recipe | 56 ++++++++++++++++++ recipes/neviditelny_pes.recipe | 32 +++++++++++ recipes/novinky.cz.recipe | 50 ++++++++++++++++ recipes/parlamentni_listy.recipe | 38 +++++++++++++ recipes/piratska_strana.recipe | 40 +++++++++++++ recipes/piratske_noviny.recipe | 34 +++++++++++ recipes/pravo.recipe | 64 +++++++++++++++++++++ recipes/respekt.recipe | 37 ++++++++++++ recipes/tyden.cz.recipe | 44 +++++++++++++++ 23 files changed, 972 insertions(+) create mode 100644 recipes/aktualne.cz.recipe create mode 100644 recipes/blesk.recipe create mode 100644 recipes/ceska_pozice.recipe create mode 100644 recipes/ceske_noviny.recipe create mode 100644 recipes/cesky_rozhlas_6.recipe create mode 100644 recipes/demagog.cz.recipe create mode 100644 recipes/denik.cz.recipe create mode 100644 recipes/denik_referendum.recipe create mode 100644 recipes/ihned.cz.recipe create mode 100644 recipes/insider.recipe create mode 100644 recipes/kudy_z_nudy.recipe create mode 100644 recipes/lidovky.recipe create mode 100644 recipes/metropol_tv.recipe create mode 100644 recipes/nadacni_fond_proti_korupci.recipe create mode 100644 recipes/nepszabadsag.recipe create mode 100644 recipes/neviditelny_pes.recipe create mode 100644 recipes/novinky.cz.recipe create mode 100644 recipes/parlamentni_listy.recipe create mode 100644 recipes/piratska_strana.recipe create mode 100644 recipes/piratske_noviny.recipe create mode 100644 recipes/pravo.recipe create mode 100644 recipes/respekt.recipe create mode 100644 recipes/tyden.cz.recipe diff --git a/recipes/aktualne.cz.recipe b/recipes/aktualne.cz.recipe new file mode 100644 index 0000000000..cd2dcc5f09 --- /dev/null +++ b/recipes/aktualne.cz.recipe @@ -0,0 +1,69 @@ +# vim:fileencoding=UTF-8:ts=4:sw=4:sta:et:sts=4:ai +from __future__ import unicode_literals +from calibre.web.feeds.recipes import BasicNewsRecipe +import re + +class aktualneRecipe(BasicNewsRecipe): + __author__ = 'bubak' + title = u'aktualne.cz' + publisher = u'Centrum holdings' + description = 'aktuálně.cz' + oldest_article = 1 + max_articles_per_feed = 20 + + feeds = [ + (u'Domácí', u'http://aktualne.centrum.cz/feeds/rss/domaci/?photo=0'), + (u'Zprávy', u'http://aktualne.centrum.cz/feeds/rss/zpravy/?photo=0'), + (u'Praha', u'http://aktualne.centrum.cz/feeds/rss/domaci/regiony/praha/?photo=0'), + (u'Ekonomika', u'http://aktualne.centrum.cz/feeds/rss/ekonomika/?photo=0'), + (u'Finance', u'http://aktualne.centrum.cz/feeds/rss/finance/?photo=0'), + (u'Blogy a názory', u'http://blog.aktualne.centrum.cz/export-all.php') + ] + + + language = 'cs' + cover_url = 'http://img.aktualne.centrum.cz/design/akt4/o/l/logo-akt-ciste.png' + remove_javascript = True + no_stylesheets = True + + remove_attributes = [] + remove_tags_before = dict(name='h1', attrs={'class':['titulek-clanku']}) + filter_regexps = [r'img.aktualne.centrum.cz'] + remove_tags = [dict(name='div', attrs={'id':['social-bookmark']}), + dict(name='div', attrs={'class':['box1', 'svazane-tagy']}), + dict(name='div', attrs={'class':'itemcomment id0'}), + dict(name='div', attrs={'class':'hlavicka'}), + dict(name='div', attrs={'class':'hlavni-menu'}), + dict(name='div', attrs={'class':'top-standard-brand-obal'}), + dict(name='div', attrs={'class':'breadcrumb'}), + dict(name='div', attrs={'id':'start-standard'}), + dict(name='div', attrs={'id':'forum'}), + dict(name='span', attrs={'class':'akce'}), + dict(name='span', attrs={'class':'odrazka vetsi'}), + dict(name='div', attrs={'class':'boxP'}), + dict(name='div', attrs={'class':'box2'})] + preprocess_regexps = [ + (re.compile(r'
'), + (re.compile(r'
')] + + keep_only_tags = [] + + visited_urls = {} + def get_article_url(self, article): + url = BasicNewsRecipe.get_article_url(self, article) + if url in self.visited_urls: + self.log.debug('Ignoring duplicate: ' + url) + return None + else: + self.visited_urls[url] = True + self.log.debug('Accepting: ' + url) + return url + + def encoding(self, source): + if source.newurl.find('blog.aktualne') >= 0: + enc = 'utf-8' + else: + enc = 'iso-8859-2' + self.log.debug('Called encoding ' + enc + " " + str(source.newurl)) + return source.decode(enc, 'replace') + diff --git a/recipes/blesk.recipe b/recipes/blesk.recipe new file mode 100644 index 0000000000..7eff4c42d0 --- /dev/null +++ b/recipes/blesk.recipe @@ -0,0 +1,55 @@ +# vim:fileencoding=UTF-8:ts=4:sw=4:sta:et:sts=4:ai +from __future__ import unicode_literals +from calibre.web.feeds.recipes import BasicNewsRecipe +import re + +class bleskRecipe(BasicNewsRecipe): + __author__ = 'bubak' + title = u'Blesk' + publisher = u'' + description = 'blesk.cz' + oldest_article = 1 + max_articles_per_feed = 20 + use_embedded_content = False + + feeds = [ + (u'Zprávy', u'http://www.blesk.cz/rss/7'), + (u'Blesk', u'http://www.blesk.cz/rss/1'), + (u'Sex a tabu', u'http://www.blesk.cz/rss/2'), + (u'Celebrity', u'http://www.blesk.cz/rss/5'), + (u'Cestování', u'http://www.blesk.cz/rss/12') + ] + + + #encoding = 'iso-8859-2' + language = 'cs' + cover_url = 'http://img.blesk.cz/images/blesk/blesk-logo.png' + remove_javascript = True + no_stylesheets = True + extra_css = """ + """ + + remove_attributes = [] + remove_tags_before = dict(name='div', attrs={'id':['boxContent']}) + remove_tags_after = dict(name='div', attrs={'class':['artAuthors']}) + remove_tags = [dict(name='div', attrs={'class':['link_clanek']}), + dict(name='div', attrs={'id':['partHeader']}), + dict(name='div', attrs={'id':['top_bottom_box', 'lista_top']})] + preprocess_regexps = [(re.compile(r'
')] + + keep_only_tags = [dict(name='div', attrs={'class':'articleContent'})] + + visited_urls = {} + def get_article_url(self, article): + url = BasicNewsRecipe.get_article_url(self, article) + if url in self.visited_urls: + self.log.debug('Ignoring duplicate: ' + url) + return None + else: + self.visited_urls[url] = True + self.log.debug('Accepting: ' + url) + return url + + + + diff --git a/recipes/ceska_pozice.recipe b/recipes/ceska_pozice.recipe new file mode 100644 index 0000000000..478f6823b9 --- /dev/null +++ b/recipes/ceska_pozice.recipe @@ -0,0 +1,68 @@ +# vim:fileencoding=UTF-8:ts=4:sw=4:sta:et:sts=4:ai +from __future__ import unicode_literals +from calibre.web.feeds.recipes import BasicNewsRecipe + +class ceskaPoziceRecipe(BasicNewsRecipe): + __author__ = 'bubak' + title = u'Česká pozice' + description = 'Česká pozice' + oldest_article = 2 + max_articles_per_feed = 20 + + feeds = [ + (u'Všechny články', u'http://www.ceskapozice.cz/rss.xml'), + (u'Domov', u'http://www.ceskapozice.cz/taxonomy/term/16/feed'), + (u'Chrono', u'http://www.ceskapozice.cz/chrono/feed'), + (u'Evropa', u'http://www.ceskapozice.cz/taxonomy/term/17/feed') + ] + + + language = 'cs' + cover_url = 'http://www.ceskapozice.cz/sites/default/files/cpozice_logo.png' + remove_javascript = True + no_stylesheets = True + domain = u'http://www.ceskapozice.cz' + use_embedded_content = False + + + remove_tags = [dict(name='div', attrs={'class':['block-ad', 'region region-content-ad']}), + dict(name='ul', attrs={'class':'links'}), + dict(name='div', attrs={'id':['comments', 'back-to-top']}), + dict(name='div', attrs={'class':['next-page', 'region region-content-ad']}), + dict(name='cite')] + + keep_only_tags = [dict(name='div', attrs={'id':'content'})] + + visited_urls = {} + def get_article_url(self, article): + url = BasicNewsRecipe.get_article_url(self, article) + if url in self.visited_urls: + self.log.debug('Ignoring duplicate: ' + url) + return None + else: + self.visited_urls[url] = True + self.log.debug('Accepting: ' + url) + return url + + def preprocess_html(self, soup): + self.append_page(soup, soup.body, 3) + return soup + + def append_page(self, soup, appendtag, position): + pager = soup.find('div', attrs={'class':'paging-bottom'}) + if pager: + nextbutton = pager.find('li', attrs={'class':'pager-next'}) + if nextbutton: + nexturl = self.domain + nextbutton.a['href'] + soup2 = self.index_to_soup(nexturl) + texttag = soup2.find('div', attrs={'class':'main-body'}) + for it in texttag.findAll('div', attrs={'class':'region region-content-ad'}): + it.extract() + for it in texttag.findAll('cite'): + it.extract() + newpos = len(texttag.contents) + self.append_page(soup2, texttag, newpos) + texttag.extract() + appendtag.insert(position, texttag) + pager.extract() + diff --git a/recipes/ceske_noviny.recipe b/recipes/ceske_noviny.recipe new file mode 100644 index 0000000000..10dd16689d --- /dev/null +++ b/recipes/ceske_noviny.recipe @@ -0,0 +1,30 @@ +# vim:fileencoding=UTF-8:ts=4:sw=4:sta:et:sts=4:ai +from __future__ import unicode_literals +from calibre.web.feeds.recipes import BasicNewsRecipe + +class ceskenovinyRecipe(BasicNewsRecipe): + __author__ = 'bubak' + title = u'České Noviny' + description = 'ceskenoviny.cz' + oldest_article = 1 + max_articles_per_feed = 20 + + feeds = [ + (u'Domácí', u'http://www.ceskenoviny.cz/sluzby/rss/domov.php') + #,(u'Hlavní události', u'http://www.ceskenoviny.cz/sluzby/rss/index.php') + #,(u'Přehled zpráv', u'http://www.ceskenoviny.cz/sluzby/rss/zpravy.php') + #,(u'Ze světa', u'http://www.ceskenoviny.cz/sluzby/rss/svet.php') + #,(u'Kultura', u'http://www.ceskenoviny.cz/sluzby/rss/kultura.php') + #,(u'IT', u'http://www.ceskenoviny.cz/sluzby/rss/pocitace.php') + ] + + + language = 'cs' + cover_url = 'http://i4.cn.cz/grafika/cn_logo-print.gif' + remove_javascript = True + no_stylesheets = True + + remove_attributes = [] + filter_regexps = [r'img.aktualne.centrum.cz'] + + keep_only_tags = [dict(name='div', attrs={'id':'clnk'})] diff --git a/recipes/cesky_rozhlas_6.recipe b/recipes/cesky_rozhlas_6.recipe new file mode 100644 index 0000000000..eca32af02c --- /dev/null +++ b/recipes/cesky_rozhlas_6.recipe @@ -0,0 +1,26 @@ +# vim:fileencoding=UTF-8:ts=4:sw=4:sta:et:sts=4:ai +from __future__ import unicode_literals +from calibre.web.feeds.recipes import BasicNewsRecipe + +class cro6Recipe(BasicNewsRecipe): + __author__ = 'bubak' + title = u'Český rozhlas 6' + description = 'Český rozhlas 6' + oldest_article = 1 + max_articles_per_feed = 20 + + feeds = [ + (u'Český rozhlas 6', u'http://www.rozhlas.cz/export/cro6/') + ] + + + language = 'cs' + cover_url = 'http://www.rozhlas.cz/img/e5/logo/cro6.png' + remove_javascript = True + no_stylesheets = True + + remove_attributes = [] + remove_tags = [dict(name='div', attrs={'class':['audio-play-all', 'poradHeaders', 'actions']}), + dict(name='p', attrs={'class':['para-last']})] + + keep_only_tags = [dict(name='div', attrs={'id':'article'})] diff --git a/recipes/demagog.cz.recipe b/recipes/demagog.cz.recipe new file mode 100644 index 0000000000..7d89af41bd --- /dev/null +++ b/recipes/demagog.cz.recipe @@ -0,0 +1,39 @@ +# vim:fileencoding=UTF-8:ts=4:sw=4:sta:et:sts=4:ai +from __future__ import unicode_literals +from calibre.web.feeds.recipes import BasicNewsRecipe +import re + +class demagogRecipe(BasicNewsRecipe): + __author__ = 'bubak' + title = u'Demagog.cz' + publisher = u'' + description = 'demagog.cz' + oldest_article = 6 + max_articles_per_feed = 20 + use_embedded_content = False + remove_empty_feeds = True + + feeds = [ + (u'Aktuality', u'http://demagog.cz/rss') + ] + + + #encoding = 'iso-8859-2' + language = 'cs' + cover_url = 'http://demagog.cz/content/images/demagog.cz.png' + remove_javascript = True + no_stylesheets = True + extra_css = """ + .vyrok_suhrn{margin-top:50px; } + .vyrok{margin-bottom:30px; } + """ + + remove_tags = [dict(name='a', attrs={'class':'vyrok_odovodnenie_tgl'}), + dict(name='img', attrs={'class':'vyrok_fotografia'})] + remove_tags_before = dict(name='h1') + remove_tags_after = dict(name='div', attrs={'class':'vyrok_text_after'}) + preprocess_regexps = [(re.compile(r'(
)', re.DOTALL|re.IGNORECASE), lambda match: '\1
')] + + + + diff --git a/recipes/denik.cz.recipe b/recipes/denik.cz.recipe new file mode 100644 index 0000000000..2ccf8caa40 --- /dev/null +++ b/recipes/denik.cz.recipe @@ -0,0 +1,36 @@ +# vim:fileencoding=UTF-8:ts=4:sw=4:sta:et:sts=4:ai +from __future__ import unicode_literals +from calibre.web.feeds.recipes import BasicNewsRecipe + +class ceskyDenikRecipe(BasicNewsRecipe): + __author__ = 'bubak' + title = u'denik.cz' + publisher = u'' + description = u'Český deník' + oldest_article = 1 + max_articles_per_feed = 20 + use_embedded_content = False + remove_empty_feeds = True + + feeds = [ + (u'Z domova', u'http://www.denik.cz/rss/z_domova.html') + ,(u'Pražský deník - Moje Praha', u'http://prazsky.denik.cz/rss/zpravy_region.html') + #,(u'Zahraničí', u'http://www.denik.cz/rss/ze_sveta.html') + #,(u'Kultura', u'http://www.denik.cz/rss/kultura.html') + ] + + + #encoding = 'iso-8859-2' + language = 'cs' + cover_url = 'http://g.denik.cz/images/loga/denik.png' + remove_javascript = True + no_stylesheets = True + extra_css = """ + """ + + remove_tags = [] + keep_only_tags = [dict(name='div', attrs={'class':'content'})] + #remove_tags_before = dict(name='h1') + remove_tags_after = dict(name='p', attrs={'class':'clanek-autor'}) + + diff --git a/recipes/denik_referendum.recipe b/recipes/denik_referendum.recipe new file mode 100644 index 0000000000..e04871d067 --- /dev/null +++ b/recipes/denik_referendum.recipe @@ -0,0 +1,28 @@ +# vim:fileencoding=UTF-8:ts=4:sw=4:sta:et:sts=4:ai +from __future__ import unicode_literals +from calibre.web.feeds.recipes import BasicNewsRecipe + +class denikReferendumRecipe(BasicNewsRecipe): + __author__ = 'bubak' + title = u'Den\u00edk Referendum' + publisher = u'' + description = '' + oldest_article = 1 + max_articles_per_feed = 20 + + feeds = [ + (u'Deník Referendum', u'http://feeds.feedburner.com/DenikReferendum') + ] + + + #encoding = 'iso-8859-2' + language = 'cs' + remove_javascript = True + no_stylesheets = True + use_embedded_content = False + remove_attributes = [] + remove_tags_after = dict(name='div', attrs={'class':['text']}) + remove_tags = [dict(name='div', attrs={'class':['box boxLine', 'box noprint', 'box']}), + dict(name='h3', attrs={'class':'head alt'})] + + keep_only_tags = [dict(name='div', attrs={'id':['content']})] diff --git a/recipes/ihned.cz.recipe b/recipes/ihned.cz.recipe new file mode 100644 index 0000000000..a35be06dd1 --- /dev/null +++ b/recipes/ihned.cz.recipe @@ -0,0 +1,36 @@ +# vim:fileencoding=UTF-8:ts=4:sw=4:sta:et:sts=4:ai +from __future__ import unicode_literals +from calibre.web.feeds.recipes import BasicNewsRecipe + +class ihnedRecipe(BasicNewsRecipe): + __author__ = 'bubak' + title = u'iHNed.cz' + publisher = u'' + description = 'ihned.cz' + oldest_article = 1 + max_articles_per_feed = 20 + use_embedded_content = False + + feeds = [ + (u'Zprávy', u'http://zpravy.ihned.cz/?m=rss'), + (u'Hospodářské noviny', u'http://hn.ihned.cz/?p=500000_rss'), + (u'Byznys', u'http://byznys.ihned.cz/?m=rss'), + (u'Life', u'http://life.ihned.cz/?m=rss'), + (u'Dialog', u'http://dialog.ihned.cz/?m=rss') + ] + + + #encoding = 'iso-8859-2' + language = 'cs' + cover_url = 'http://rss.ihned.cz/img/0/0_hp09/ihned.cz.gif' + remove_javascript = True + no_stylesheets = True + extra_css = """ + """ + + remove_attributes = [] + remove_tags_before = dict(name='div', attrs={'id':['heading']}) + remove_tags_after = dict(name='div', attrs={'id':['next-authors']}) + remove_tags = [dict(name='ul', attrs={'id':['comm']}), + dict(name='div', attrs={'id':['r-big']}), + dict(name='div', attrs={'class':['tools tools-top']})] diff --git a/recipes/insider.recipe b/recipes/insider.recipe new file mode 100644 index 0000000000..1374cb408d --- /dev/null +++ b/recipes/insider.recipe @@ -0,0 +1,59 @@ +# vim:fileencoding=UTF-8:ts=4:sw=4:sta:et:sts=4:ai +from __future__ import unicode_literals +import re + +from calibre.web.feeds.news import BasicNewsRecipe + +class insider(BasicNewsRecipe): + __author__ = 'bubak' + title = 'Insider' + language = 'cz' + + remove_tags = [dict(name='div', attrs={'class':'article-related-content'}) + ,dict(name='div', attrs={'class':'calendar'}) + ,dict(name='span', attrs={'id':'labelHolder'}) + ] + + no_stylesheets = True + keep_only_tags = [dict(name='div', attrs={'class':['doubleBlock textContentFormat']})] + + preprocess_regexps = [(re.compile(r'T.mata:.*', re.DOTALL|re.IGNORECASE), lambda m: '')] + needs_subscription = True + + def get_browser(self): + br = BasicNewsRecipe.get_browser() + br.open('http://www.denikinsider.cz/') + br.select_form(nr=0) + br['login-name'] = self.username + br['login-password'] = self.password + res = br.submit() + raw = res.read() + if u'Odhlásit se' not in raw: + raise ValueError('Failed to login to insider.cz' + 'Check your username and password.') + return br + + def parse_index(self): + articles = [] + + soup = self.index_to_soup('http://www.denikinsider.cz') + titles = soup.findAll('span', attrs={'class':'homepageArticleTitle'}) + if titles is None: + raise ValueError('Could not find category content') + + articles = [] + seen_titles = set([]) + for title in titles: + if title.string in seen_titles: + continue + article = title.parent + seen_titles.add(title.string) + url = article['href'] + if url.startswith('/'): + url = 'http://www.denikinsider.cz/'+url + self.log('\tFound article:', title, 'at', url) + articles.append({'title':title.string, 'url':url, 'description':'', + 'date':''}) + return [(self.title, articles)] + + diff --git a/recipes/kudy_z_nudy.recipe b/recipes/kudy_z_nudy.recipe new file mode 100644 index 0000000000..d7c0d9ecf9 --- /dev/null +++ b/recipes/kudy_z_nudy.recipe @@ -0,0 +1,32 @@ +# vim:fileencoding=UTF-8:ts=4:sw=4:sta:et:sts=4:ai +from __future__ import unicode_literals +from calibre.web.feeds.recipes import BasicNewsRecipe + +class kudyznudyRecipe(BasicNewsRecipe): + __author__ = 'bubak' + title = u'Kudy z nudy' + publisher = u'' + description = 'kudyznudy.cz' + oldest_article = 3 + max_articles_per_feed = 20 + use_embedded_content = False + + feeds = [ + (u'Praha nejnovější', u'http://www.kudyznudy.cz/RSS/Charts.aspx?Type=Newest&Lang=cs-CZ&RegionId=1') + ] + + + #encoding = 'iso-8859-2' + language = 'cs' + cover_url = 'http://www.kudyznudy.cz/App_Themes/KzN/Images/Containers/Header/HeaderLogoKZN.png' + remove_javascript = True + no_stylesheets = True + extra_css = """ + """ + + remove_attributes = [] + remove_tags_before = dict(name='div', attrs={'class':['C_WholeContentPadding']}) + remove_tags_after = dict(name='div', attrs={'class':['SurroundingsContainer']}) + remove_tags = [dict(name='div', attrs={'class':['Details', 'buttons', 'SurroundingsContainer', 'breadcrumb']})] + + keep_only_tags = [] diff --git a/recipes/lidovky.recipe b/recipes/lidovky.recipe new file mode 100644 index 0000000000..8e4754829b --- /dev/null +++ b/recipes/lidovky.recipe @@ -0,0 +1,40 @@ +# vim:fileencoding=UTF-8:ts=4:sw=4:sta:et:sts=4:ai +from __future__ import unicode_literals +from calibre.web.feeds.recipes import BasicNewsRecipe +import re + +class lnRecipe(BasicNewsRecipe): + __author__ = 'bubak' + title = u'lidovky' + publisher = u'' + description = 'lidovky.cz' + oldest_article = 1 + max_articles_per_feed = 20 + + feeds = [ + (u'Události', u'http://www.lidovky.cz/export/rss.asp?r=ln_domov'), + (u'Svět', u'http://www.lidovky.cz/export/rss.asp?r=ln_zahranici'), + (u'Byznys', u'http://www.lidovky.cz/export/rss.asp?c=ln_byznys'), + (u'Věda', u'http://www.lidovky.cz/export/rss.asp?r=ln_veda'), + (u'Názory', u'http://www.lidovky.cz/export/rss.asp?r=ln_nazory'), + (u'Relax', u'http://www.lidovky.cz/export/rss.asp?c=ln_relax') + ] + + + #encoding = 'iso-8859-2' + language = 'cs' + cover_url = 'http://g.lidovky.cz/o/lidovky_ln3b/lidovky-logo.png' + remove_javascript = True + no_stylesheets = True + use_embedded_content = False + remove_attributes = [] + remove_tags_before = dict(name='div', attrs={'id':['content']}) + remove_tags_after = dict(name='div', attrs={'class':['authors']}) + preprocess_regexps = [(re.compile(r'
')] + + keep_only_tags = [] + + + + + diff --git a/recipes/metropol_tv.recipe b/recipes/metropol_tv.recipe new file mode 100644 index 0000000000..56f393c96a --- /dev/null +++ b/recipes/metropol_tv.recipe @@ -0,0 +1,29 @@ +# vim:fileencoding=UTF-8:ts=4:sw=4:sta:et:sts=4:ai +from __future__ import unicode_literals +from calibre.web.feeds.recipes import BasicNewsRecipe + +class metropolRecipe(BasicNewsRecipe): + __author__ = 'bubak' + title = u'Metropol TV' + publisher = u'' + description = 'metropol.cz' + oldest_article = 1 + max_articles_per_feed = 20 + use_embedded_content = False + + feeds = [ + (u'Metropolcv.cz', u'http://www.metropol.cz/rss/') + ] + + + #encoding = 'iso-8859-2' + language = 'cs' + cover_url = 'http://www.metropol.cz/public/css/../images/logo/metropoltv.png' + remove_javascript = True + no_stylesheets = True + extra_css = """ + """ + + remove_attributes = [] + + keep_only_tags = [dict(name='div', attrs={'id':['art-full']})] diff --git a/recipes/nadacni_fond_proti_korupci.recipe b/recipes/nadacni_fond_proti_korupci.recipe new file mode 100644 index 0000000000..2a8a69283c --- /dev/null +++ b/recipes/nadacni_fond_proti_korupci.recipe @@ -0,0 +1,30 @@ +# vim:fileencoding=UTF-8:ts=4:sw=4:sta:et:sts=4:ai +from __future__ import unicode_literals +from calibre.web.feeds.recipes import BasicNewsRecipe + +class nfpkRecipe(BasicNewsRecipe): + __author__ = 'bubak' + title = u'Nadační fond proti korupci' + publisher = u'' + description = 'nfpk.cz' + oldest_article = 7 + max_articles_per_feed = 20 + use_embedded_content = False + remove_empty_feeds = True + + feeds = [ + (u'Aktuality', u'http://feeds.feedburner.com/nfpk') + ] + + + #encoding = 'iso-8859-2' + language = 'cs' + cover_url = 'http://www.nfpk.cz/_templates/nfpk/_images/logo.gif' + remove_javascript = True + no_stylesheets = True + extra_css = """ + """ + + remove_attributes = [] + keep_only_tags = [dict(name='div', attrs={'id':'content'})] + diff --git a/recipes/nepszabadsag.recipe b/recipes/nepszabadsag.recipe new file mode 100644 index 0000000000..8ae5447dd6 --- /dev/null +++ b/recipes/nepszabadsag.recipe @@ -0,0 +1,56 @@ +# vim:fileencoding=UTF-8:ts=4:sw=4:sta:et:sts=4:ai +from __future__ import unicode_literals +''' +Fetch Népszabadság +''' +from calibre.web.feeds.news import BasicNewsRecipe + +class nepszabadsag(BasicNewsRecipe): + title = u'N\u00e9pszabads\u00e1g' + description = '' + __author__ = 'bubak' + use_embedded_content = False + timefmt = ' [%d %b %Y]' + oldest_article = 2 + max_articles_per_feed = 20 + no_stylesheets = True + language = 'hu' + #delay = 1 + #timeout = 10 + simultaneous_downloads = 5 + + #encoding = 'utf-8' + remove_javascript = True + cover_url = 'http://nol.hu/_design/image/logo_nol_live.jpg' + + feeds = [ + (u'Belföld', u'http://nol.hu/feed/belfold.rss') + #,(u'Külföld', u'http://nol.hu/feed/kulfold.rss') + #,(u'Gazdaság', u'http://nol.hu/feed/gazdasag.rss') + #,(u'Kultúra', u'http://nol.hu/feed/kult.rss') + ] + + extra_css = ''' + ''' + + remove_attributes = [] + remove_tags_before = dict(name='div', attrs={'class':['d-source']}) + remove_tags_after = dict(name='div', attrs={'class':['tags']}) + remove_tags = [dict(name='div', attrs={'class':['h']}), + dict(name='tfoot')] + + + keep_only_tags = [dict(name='table', attrs={'class':'article-box'})] + + # NS sends an ad page sometimes but not frequently enough, TBD + def AAskip_ad_pages(self, soup): + if ('advertisement' in soup.find('title').string.lower()): + href = soup.find('a').get('href') + self.log.debug('Skipping to: ' + href) + new = self.browser.open(href).read().decode('utf-8', 'ignore') + #ipython(locals()) + self.log.debug('Finished: ' + href) + return new + else: + return None + diff --git a/recipes/neviditelny_pes.recipe b/recipes/neviditelny_pes.recipe new file mode 100644 index 0000000000..65cfb2b7ec --- /dev/null +++ b/recipes/neviditelny_pes.recipe @@ -0,0 +1,32 @@ +# vim:fileencoding=UTF-8:ts=4:sw=4:sta:et:sts=4:ai +from __future__ import unicode_literals +from calibre.web.feeds.recipes import BasicNewsRecipe + +class pesRecipe(BasicNewsRecipe): + __author__ = 'bubak' + title = u'Neviditelný pes' + publisher = u'' + description = u'Neviditelný pes' + oldest_article = 1 + max_articles_per_feed = 20 + use_embedded_content = False + remove_empty_feeds = True + + feeds = [ + (u'Neviditelný pes', u'http://neviditelnypes.lidovky.cz/export/rss.asp?c=pes_neviditelny') + ] + + + #encoding = 'iso-8859-2' + language = 'cs' + cover_url = 'http://g.zpravy.cz/o/pes/logo_pes.jpg' + remove_javascript = True + no_stylesheets = True + extra_css = """ + """ + + remove_tags = [] + remove_tags_before = dict(name='div', attrs={'id':'art-full'}) + remove_tags_after = dict(name='div', attrs={'id':'authors'}) + + diff --git a/recipes/novinky.cz.recipe b/recipes/novinky.cz.recipe new file mode 100644 index 0000000000..19fd52a371 --- /dev/null +++ b/recipes/novinky.cz.recipe @@ -0,0 +1,50 @@ +# vim:fileencoding=UTF-8:ts=4:sw=4:sta:et:sts=4:ai +from __future__ import unicode_literals +from calibre.web.feeds.recipes import BasicNewsRecipe + +class novinkyRecipe(BasicNewsRecipe): + __author__ = 'bubak' + title = u'novinky.cz' + publisher = u'seznam.cz' + description = 'novinky.cz' + oldest_article = 1 + max_articles_per_feed = 20 + + feeds = [ + (u'Domácí', u'http://www.novinky.cz/rss2/domaci/'), + (u'Praha', u'http://www.novinky.cz/rss2/vase-zpravy/praha/'), + (u'Ekonomika', u'http://www.novinky.cz/rss2/ekonomika/'), + (u'Finance', u'http://www.novinky.cz/rss2/finance/'), + ] + + + #encoding = 'utf-8' + language = 'cs' + cover_url = 'http://www.novinky.cz/static/images/logo.gif' + remove_javascript = True + no_stylesheets = True + + remove_tags = [dict(name='div', attrs={'id':['pictureInnerBox']}), + dict(name='div', attrs={'id':['discussionEntry']}), + dict(name='span', attrs={'id':['mynews-hits', 'mynews-author']}), + dict(name='div', attrs={'class':['related']}), + dict(name='div', attrs={'id':['multimediaInfo']})] + remove_tags_before = dict(name='div',attrs={'class':['articleHeader']}) + remove_tags_after = dict(name='div',attrs={'class':'related'}) + + keep_only_tags = [] + + # This source has identical articles under different links + # which are redirected to the common url. I've found + # just this API method that has the real URL + visited_urls = {} + def encoding(self, source): + url = source.newurl + if url in self.visited_urls: + self.log.debug('Ignoring duplicate: ' + url) + return None + else: + self.visited_urls[url] = True + self.log.debug('Accepting: ' + url) + return source.decode('utf-8', 'replace') + diff --git a/recipes/parlamentni_listy.recipe b/recipes/parlamentni_listy.recipe new file mode 100644 index 0000000000..71d904866a --- /dev/null +++ b/recipes/parlamentni_listy.recipe @@ -0,0 +1,38 @@ +# vim:fileencoding=UTF-8:ts=4:sw=4:sta:et:sts=4:ai +from __future__ import unicode_literals +from calibre.web.feeds.recipes import BasicNewsRecipe +import re + +class plRecipe(BasicNewsRecipe): + __author__ = 'bubak' + title = u'Parlamentn\u00ed Listy' + publisher = u'' + description = '' + oldest_article = 1 + max_articles_per_feed = 20 + + feeds = [ + (u'Parlamentní listy.cz', u'http://www.parlamentnilisty.cz/export/rss.aspx') + ] + + + #encoding = 'iso-8859-2' + language = 'cs' + cover_url = 'http://www.parlamentnilisty.cz/design/listy-logo2.png' + remove_javascript = True + no_stylesheets = True + use_embedded_content = False + remove_attributes = [] + remove_tags = [dict(name='div', attrs={'class':['articledetailboxin','crumbs', 'relatedarticles articledetailbox']}), + dict(name='div', attrs={'class':['socialshare-1 noprint', 'socialshare-2 noprint']}), + dict(name='div', attrs={'id':'widget'}), + dict(name='div', attrs={'class':'article-discussion-box noprint'})] + preprocess_regexps = [(re.compile(r'<(span|strong)[^>]*>\s*Ptejte se politik.*', re.DOTALL|re.IGNORECASE), lambda match: '')] + + keep_only_tags = [dict(name='div', attrs={'class':['article-detail']})] + + + + + + diff --git a/recipes/piratska_strana.recipe b/recipes/piratska_strana.recipe new file mode 100644 index 0000000000..c125eb8aad --- /dev/null +++ b/recipes/piratska_strana.recipe @@ -0,0 +1,40 @@ +# vim:fileencoding=UTF-8:ts=4:sw=4:sta:et:sts=4:ai +from __future__ import unicode_literals +from calibre.web.feeds.recipes import BasicNewsRecipe + +class cpsRecipe(BasicNewsRecipe): + __author__ = 'bubak' + title = u'Piratská strana' + publisher = u'' + description = '' + oldest_article = 3 + max_articles_per_feed = 20 + use_embedded_content = False + remove_empty_feeds = True + + feeds = [ + (u'Články', u'http://www.pirati.cz/rss.xml') + ] + + + #encoding = 'iso-8859-2' + language = 'cs' + cover_url = 'http://www.pirati.cz/sites/all/themes/addari-cps/images/headbg.jpg' + remove_javascript = True + no_stylesheets = True + extra_css = """ + """ + + remove_attributes = [] + keep_only_tags = [dict(name='div', attrs={'id':'postarea'})] + remove_tags = [dict(name='div', attrs={'class':['breadcrumb', 'submitted', 'links-readmore']}), + dict(name='div', attrs={'id':['comments']})] + remove_tags_before = dict(name='font', attrs={'size':'+3'}) + remove_tags_after = [dict(name='iframe')] + + conversion_options = {'linearize_tables' : True} + + + + + diff --git a/recipes/piratske_noviny.recipe b/recipes/piratske_noviny.recipe new file mode 100644 index 0000000000..a2d30374ed --- /dev/null +++ b/recipes/piratske_noviny.recipe @@ -0,0 +1,34 @@ +# vim:fileencoding=UTF-8:ts=4:sw=4:sta:et:sts=4:ai +from __future__ import unicode_literals +from calibre.web.feeds.recipes import BasicNewsRecipe + +class nfpkRecipe(BasicNewsRecipe): + __author__ = 'bubak' + title = u'Piratské noviny' + publisher = u'' + description = 'nfpk.cz' + oldest_article = 2 + max_articles_per_feed = 20 + use_embedded_content = False + remove_empty_feeds = True + + feeds = [ + (u'Aktuality', u'http://www.piratskenoviny.cz/run/rss.php') + ] + + + #encoding = 'iso-8859-2' + language = 'cs' + cover_url = 'http://www.piratskenoviny.cz/imgs/piratske-noviny.gif' + remove_javascript = True + no_stylesheets = True + extra_css = """ + """ + + remove_attributes = [] + remove_tags_before = dict(name='font', attrs={'size':'+3'}) + remove_tags_after = [dict(name='iframe')] + conversion_options = {'linearize_tables' : True} + + + diff --git a/recipes/pravo.recipe b/recipes/pravo.recipe new file mode 100644 index 0000000000..04fab99fb2 --- /dev/null +++ b/recipes/pravo.recipe @@ -0,0 +1,64 @@ +# vim:fileencoding=UTF-8:ts=4:sw=4:sta:et:sts=4:ai +from __future__ import unicode_literals + +from calibre.web.feeds.news import BasicNewsRecipe + +class pravo(BasicNewsRecipe): + __author__ = 'bubak' + title = 'Právo' + language = 'cz' + + remove_tags_before = dict(name='div', attrs={'class':'rubrika-ostat'}) + remove_tags_after = dict(name='td', attrs={'class':'rubrika'}) + remove_tags = [dict(name='td', attrs={'width':'273'}) + ,dict(name='td', attrs={'class':'rubrika'}) + ,dict(name='div', attrs={'class':'rubrika-ostat'}) + ] + extra_css = '.nadpis {font-weight: bold; font-size: 130%;} .medium {text-align: justify;}' + cover_url = 'http://pravo.novinky.cz/images/horni_6_logo.gif' + cover_margins = (0, 100, '#ffffff') + conversion_options = {'linearize_tables' : True} + + no_stylesheets = True + + # our variables + seen_titles = set([]) + # only yesterday's articles are online + parent_url = 'http://pravo.novinky.cz/minule/' + feeds = [ + ('Hlavní stránka', 'http://pravo.novinky.cz/minule/index.php'), + ('Zpravodajství', 'http://pravo.novinky.cz/minule/zpravodajstvi.php'), + ('Komentáře', 'http://pravo.novinky.cz/minule/komentare.php'), + ('Praha a střední Čechy', 'http://pravo.novinky.cz/minule/praha_stredni_cechy.php') + ] + + + def parse_index(self): + articles = [] + + for feed in self.feeds: + articles.append(self.parse_page(feed)) + return articles + + def parse_page(self, (feed_title, url)): + articles = [] + + soup = self.index_to_soup(url) + titles = soup.findAll('a', attrs={'class':'nadpis'}) + if titles is None: + raise ValueError('Could not find any articles on page ' + url) + + articles = [] + for article in titles: + title = article.string + if title in self.seen_titles: + continue + self.seen_titles.add(title) + url = article['href'] + if not url.startswith('http'): + url = self.parent_url + url + self.log('\tFound article:', title, 'at', url) + articles.append({'title':title.string, 'url':url, 'description':'', + 'date':''}) + return (feed_title, articles) + diff --git a/recipes/respekt.recipe b/recipes/respekt.recipe new file mode 100644 index 0000000000..91aa2edb40 --- /dev/null +++ b/recipes/respekt.recipe @@ -0,0 +1,37 @@ +# vim:fileencoding=UTF-8:ts=4:sw=4:sta:et:sts=4:ai +from __future__ import unicode_literals +from calibre.web.feeds.recipes import BasicNewsRecipe +import re + +class respektRecipe(BasicNewsRecipe): + __author__ = 'bubak' + title = u'Respekt' + publisher = u'Respekt' + description = 'Respekt' + oldest_article = 1 + max_articles_per_feed = 20 + + feeds = [ + (u'Všechny články', u'http://respekt.ihned.cz/index.php?p=R00000_rss') + ,(u'Blogy', u'http://blog.respekt.ihned.cz/?p=Rb00VR_rss') + #,(u'Respekt DJ', u'http://respekt.ihned.cz/index.php?p=R00RDJ_rss') + ] + + + encoding = 'cp1250' + language = 'cs' + cover_url = 'http://respekt.ihned.cz/img/R/respekt_logo.png' + remove_javascript = True + no_stylesheets = True + + remove_tags = [dict(name='div', attrs={'class':['d-tools', 'actions']})] + remove_tags_before = dict(name='div',attrs={'id':['detail']}) + remove_tags_after = dict(name='div',attrs={'class':'d-tools'}) + preprocess_regexps = [(re.compile(r'