From 2772908a3ce1ca7636eaeeaadaaa2a84a9f8a0ad Mon Sep 17 00:00:00 2001 From: John Peterson Date: Mon, 5 Nov 2012 02:08:31 +0000 Subject: [PATCH 1/9] 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 @@ def run(self, path_to_ebook): ''' # 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 @@ def _run_filetype_plugins(path_to_file, ft=None, occasion='preprocess'): 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 @@ def __enter__(self, processed=False, only_input_plugin=False, 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 @@ def view_device_book(self, path): 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 @@ def add_formats(self, id, formats, replace=True): 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 @@ 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 @@ def finalize_layout(self): 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 @@ def dropEvent(self, event): # }}} + 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 @@ def scroll_to_row(self, row): 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 @@ def clear_history(self, *args): 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 @@ def key_pressed(self, event): 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 @@ def search_box_cleared(self): 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 @@ def do_search_button(self): 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 @@ def restore_defaults(self): 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 @@ def save_options(self, c): 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 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 @@ def initialize_view(self, debug_javascript=False): 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 @@ def initialize_view(self, debug_javascript=False): 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 @@ def goto_document_end(self, *args): 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 @@ def selection_changed(self): 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 @@ def contextMenuEvent(self, ev): 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 @@ def search_next(self): 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 @@ def shrink_fonts(self, amount=None): 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 @@ def __init__(self, pathtoebook=None, debug_javascript=False, open_at=None): 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 @@ def __init__(self, pathtoebook=None, debug_javascript=False, open_at=None): 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 @@ def __init__(self, pathtoebook=None, debug_javascript=False, open_at=None): 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 @@ def __init__(self, pathtoebook=None, debug_javascript=False, open_at=None): 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 @@ def reload(self): 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 @@ def build_recent_menu(self): count += 1 def shutdown(self): - if self.isFullScreen(): - self.action_full_screen.trigger() - return False self.save_state() return True @@ -421,7 +394,8 @@ def toggle_toolbars(self): 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 @@ def save_state(self): 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 @@ def restore_state(self): 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 @@ def get_remember_current_page_opt(self): 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 @@ def print_preview(self): 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 @@ def toggle_fullscreen(self, x): 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 @@ def showFullScreen(self): 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 @@ def update_pos_label(self, *args): 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 @@ def showNormal(self): 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 @@ def copy(self, x): 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 @@ def goto_page_num(self): 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 @@ def goto_end(self): 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 @@ def open_recent(self, action): 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 @@ def load_ebook(self, pathtoebook, open_at=None): 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.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 @@ def add_format(self, index, format, stream, index_is_id=False, path=None, 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 2/9] 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 @@ def run(self, path_to_ebook): # 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 @@ def _run_filetype_plugins(path_to_file, ft=None, occasion='preprocess'): 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['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 @@ def add_formats(self, id, formats, replace=True): 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 @@ def dropEvent(self, event): 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 @@ def genesis(self, gui): 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 @@ def load_options(self, opts): 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 @@ def save_options(self, c): 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 @@ def misc_config(self, opts): 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 @@ def __init__(self, pathtoebook=None, debug_javascript=False, open_at=None): 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 @@ def __init__(self, pathtoebook=None, debug_javascript=False, open_at=None): 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 @@ def restore_state(self): 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 @@ def showFullScreen(self): 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 @@ def showNormal(self): 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 @@ def handle_window_mode_toggle(self): 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 4f43a7fa8fceec82014bf87aabd1a6ac7418a17f Mon Sep 17 00:00:00 2001 From: John Peterson Date: Sun, 18 Nov 2012 17:01:20 +0000 Subject: [PATCH 3/9] 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 @@ def __enter__(self, processed=False, only_input_plugin=False, 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 b7bd073d4a486c911ae727479f28cbe632dd00d8 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Sun, 18 Nov 2012 23:53:33 +0530 Subject: [PATCH 4/9] 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'