mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-07-09 03:04:10 -04:00
Sync to trunk.
This commit is contained in:
commit
b686e6615e
@ -12,7 +12,6 @@ class AdvancedUserRecipe1301860159(BasicNewsRecipe):
|
||||
max_articles_per_feed = 100
|
||||
no_stylesheets = True
|
||||
use_embedded_content = False
|
||||
language = 'en_EN'
|
||||
remove_javascript = True
|
||||
keep_only_tags = [dict(name='div', attrs={'class':'modSectionTd2'})]
|
||||
remove_tags = [dict(name='a'),dict(name='hr')]
|
||||
|
@ -1,7 +1,7 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
__license__ = 'GPL v3'
|
||||
__copyright__ = u'2010, Tomasz Dlugosz <tomek3d@gmail.com>'
|
||||
__copyright__ = u'2010-2011, Tomasz Dlugosz <tomek3d@gmail.com>'
|
||||
'''
|
||||
frazpc.pl
|
||||
'''
|
||||
@ -19,17 +19,20 @@ class FrazPC(BasicNewsRecipe):
|
||||
use_embedded_content = False
|
||||
no_stylesheets = True
|
||||
|
||||
feeds = [(u'Aktualno\u015bci', u'http://www.frazpc.pl/feed'), (u'Recenzje', u'http://www.frazpc.pl/kat/recenzje-2/feed') ]
|
||||
|
||||
keep_only_tags = [dict(name='div', attrs={'id':'FRAZ_CONTENT'})]
|
||||
|
||||
remove_tags = [dict(name='p', attrs={'class':'gray tagsP fs11'})]
|
||||
|
||||
preprocess_regexps = [
|
||||
(re.compile(i[0], re.IGNORECASE | re.DOTALL), i[1]) for i in
|
||||
[(r'<div id="post-[0-9]*"', lambda match: '<div id="FRAZ_CONTENT"'),
|
||||
(r'href="/f/news/', lambda match: 'href="http://www.frazpc.pl/f/news/'),
|
||||
(r' <a href="http://www.frazpc.pl/[^>]*?">(Skomentuj|Komentarz(e)?\([0-9]*\))</a> \|', lambda match: '')]
|
||||
feeds = [
|
||||
(u'Aktualno\u015bci', u'http://www.frazpc.pl/feed/aktualnosci'),
|
||||
(u'Artyku\u0142y', u'http://www.frazpc.pl/feed/artykuly')
|
||||
]
|
||||
|
||||
keep_only_tags = [dict(name='div', attrs={'class':'article'})]
|
||||
|
||||
remove_tags = [
|
||||
dict(name='div', attrs={'class':'title-wrapper'}),
|
||||
dict(name='p', attrs={'class':'tags'}),
|
||||
dict(name='p', attrs={'class':'article-links'}),
|
||||
dict(name='div', attrs={'class':'comments_box'})
|
||||
]
|
||||
|
||||
preprocess_regexps = [(re.compile(r'\| <a href="#comments">Komentarze \([0-9]*\)</a>'), lambda match: '')]
|
||||
|
||||
remove_attributes = [ 'width', 'height' ]
|
||||
|
@ -607,6 +607,7 @@ class StoreBase(Plugin): # {{{
|
||||
supported_platforms = ['windows', 'osx', 'linux']
|
||||
author = 'John Schember'
|
||||
type = _('Store')
|
||||
minimum_calibre_version = (0, 7, 59)
|
||||
|
||||
actual_plugin = None
|
||||
|
||||
|
@ -613,6 +613,7 @@ from calibre.devices.misc import PALMPRE, AVANT, SWEEX, PDNOVEL, \
|
||||
from calibre.devices.folder_device.driver import FOLDER_DEVICE_FOR_CONFIG
|
||||
from calibre.devices.kobo.driver import KOBO
|
||||
from calibre.devices.bambook.driver import BAMBOOK
|
||||
from calibre.devices.boeye.driver import BOEYE_BEX, BOEYE_BDX
|
||||
|
||||
from calibre.library.catalog import CSV_XML, EPUB_MOBI, BIBTEX
|
||||
from calibre.ebooks.epub.fix.unmanifested import Unmanifested
|
||||
@ -743,6 +744,8 @@ plugins += [
|
||||
EEEREADER,
|
||||
NEXTBOOK,
|
||||
ITUNES,
|
||||
BOEYE_BEX,
|
||||
BOEYE_BDX,
|
||||
USER_DEFINED,
|
||||
]
|
||||
plugins += [x for x in list(locals().values()) if isinstance(x, type) and \
|
||||
|
0
src/calibre/devices/boeye/__init__.py
Normal file
0
src/calibre/devices/boeye/__init__.py
Normal file
56
src/calibre/devices/boeye/driver.py
Normal file
56
src/calibre/devices/boeye/driver.py
Normal file
@ -0,0 +1,56 @@
|
||||
__license__ = 'GPL v3'
|
||||
__copyright__ = '2011, Ken <ken at szboeye.com>'
|
||||
__docformat__ = 'restructuredtext en'
|
||||
|
||||
'''
|
||||
Device driver for BOEYE serial readers
|
||||
'''
|
||||
|
||||
from calibre.devices.usbms.driver import USBMS
|
||||
|
||||
class BOEYE_BEX(USBMS):
|
||||
name = 'BOEYE BEX reader driver'
|
||||
gui_name = 'BOEYE BEX'
|
||||
description = _('Communicate with BOEYE BEX Serial eBook readers.')
|
||||
author = 'szboeye'
|
||||
supported_platforms = ['windows', 'osx', 'linux']
|
||||
|
||||
FORMATS = ['epub', 'mobi', 'fb2', 'lit', 'prc', 'pdf', 'rtf', 'txt', 'djvu', 'doc', 'chm', 'html', 'zip', 'pdb']
|
||||
|
||||
VENDOR_ID = [0x0085]
|
||||
PRODUCT_ID = [0x600]
|
||||
|
||||
VENDOR_NAME = 'LINUX'
|
||||
WINDOWS_MAIN_MEM = 'FILE-STOR_GADGET'
|
||||
OSX_MAIN_MEM = 'Linux File-Stor Gadget Media'
|
||||
|
||||
MAIN_MEMORY_VOLUME_LABEL = 'BOEYE BEX Storage Card'
|
||||
|
||||
EBOOK_DIR_MAIN = 'Documents'
|
||||
SUPPORTS_SUB_DIRS = True
|
||||
|
||||
class BOEYE_BDX(USBMS):
|
||||
name = 'BOEYE BDX reader driver'
|
||||
gui_name = 'BOEYE BDX'
|
||||
description = _('Communicate with BOEYE BDX serial eBook readers.')
|
||||
author = 'szboeye'
|
||||
supported_platforms = ['windows', 'osx', 'linux']
|
||||
|
||||
FORMATS = ['epub', 'mobi', 'fb2', 'lit', 'prc', 'pdf', 'rtf', 'txt', 'djvu', 'doc', 'chm', 'html', 'zip', 'pdb']
|
||||
|
||||
VENDOR_ID = [0x0085]
|
||||
PRODUCT_ID = [0x800]
|
||||
|
||||
VENDOR_NAME = 'LINUX'
|
||||
WINDOWS_MAIN_MEM = 'FILE-STOR_GADGET'
|
||||
WINDOWS_CARD_A_MEM = 'FILE-STOR_GADGET'
|
||||
|
||||
OSX_MAIN_MEM = 'Linux File-Stor Gadget Media'
|
||||
OSX_CARD_A_MEM = 'Linux File-Stor Gadget Media'
|
||||
|
||||
MAIN_MEMORY_VOLUME_LABEL = 'BOEYE BDX Internal Memory'
|
||||
STORAGE_CARD_VOLUME_LABEL = 'BOEYE BDX Storage Card'
|
||||
|
||||
EBOOK_DIR_MAIN = 'Documents'
|
||||
EBOOK_DIR_CARD_A = 'Documents'
|
||||
SUPPORTS_SUB_DIRS = True
|
@ -34,7 +34,7 @@ if isosx:
|
||||
)
|
||||
gprefs.defaults['action-layout-toolbar'] = (
|
||||
'Add Books', 'Edit Metadata', None, 'Convert Books', 'View', None,
|
||||
'Choose Library', 'Donate', None, 'Fetch News', 'Save To Disk',
|
||||
'Choose Library', 'Donate', None, 'Fetch News', 'Store', 'Save To Disk',
|
||||
'Connect Share', None, 'Remove Books',
|
||||
)
|
||||
gprefs.defaults['action-layout-toolbar-device'] = (
|
||||
@ -48,7 +48,7 @@ else:
|
||||
gprefs.defaults['action-layout-menubar-device'] = ()
|
||||
gprefs.defaults['action-layout-toolbar'] = (
|
||||
'Add Books', 'Edit Metadata', None, 'Convert Books', 'View', None,
|
||||
'Choose Library', 'Donate', None, 'Fetch News', 'Save To Disk',
|
||||
'Choose Library', 'Donate', None, 'Fetch News', 'Store', 'Save To Disk',
|
||||
'Connect Share', None, 'Remove Books', None, 'Help', 'Preferences',
|
||||
)
|
||||
gprefs.defaults['action-layout-toolbar-device'] = (
|
||||
|
@ -10,7 +10,7 @@ from PyQt4.Qt import QIcon, QMenu, Qt
|
||||
from calibre.gui2.actions import InterfaceAction
|
||||
from calibre.gui2.preferences.main import Preferences
|
||||
from calibre.gui2 import error_dialog
|
||||
from calibre.constants import DEBUG
|
||||
from calibre.constants import DEBUG, isosx
|
||||
|
||||
class PreferencesAction(InterfaceAction):
|
||||
|
||||
@ -19,7 +19,8 @@ class PreferencesAction(InterfaceAction):
|
||||
|
||||
def genesis(self):
|
||||
pm = QMenu()
|
||||
pm.addAction(QIcon(I('config.png')), _('Preferences'), self.do_config)
|
||||
acname = _('Change calibre behavior') if isosx else _('Preferences')
|
||||
pm.addAction(QIcon(I('config.png')), acname, self.do_config)
|
||||
pm.addAction(QIcon(I('wizard.png')), _('Run welcome wizard'),
|
||||
self.gui.run_wizard)
|
||||
if not DEBUG:
|
||||
|
@ -198,7 +198,7 @@ class MetadataSingleDialogBase(ResizableDialog):
|
||||
ans = self.custom_metadata_widgets
|
||||
for i in range(len(ans)-1):
|
||||
if before is not None and i == 0:
|
||||
pass# Do something
|
||||
pass
|
||||
if len(ans[i+1].widgets) == 2:
|
||||
sto(ans[i].widgets[-1], ans[i+1].widgets[1])
|
||||
else:
|
||||
@ -206,7 +206,7 @@ class MetadataSingleDialogBase(ResizableDialog):
|
||||
for c in range(2, len(ans[i].widgets), 2):
|
||||
sto(ans[i].widgets[c-1], ans[i].widgets[c+1])
|
||||
if after is not None:
|
||||
pass # Do something
|
||||
pass
|
||||
# }}}
|
||||
|
||||
def do_view_format(self, path, fmt):
|
||||
@ -728,7 +728,135 @@ class MetadataSingleDialogAlt1(MetadataSingleDialogBase): # {{{
|
||||
|
||||
# }}}
|
||||
|
||||
editors = {'default': MetadataSingleDialog, 'alt1': MetadataSingleDialogAlt1}
|
||||
class MetadataSingleDialogAlt2(MetadataSingleDialogBase): # {{{
|
||||
|
||||
cc_two_column = False
|
||||
one_line_comments_toolbar = True
|
||||
|
||||
def do_layout(self):
|
||||
self.central_widget.clear()
|
||||
self.labels = []
|
||||
sto = QWidget.setTabOrder
|
||||
|
||||
self.central_widget.tabBar().setVisible(False)
|
||||
tab0 = QWidget(self)
|
||||
self.central_widget.addTab(tab0, _("&Metadata"))
|
||||
l = QGridLayout()
|
||||
tab0.setLayout(l)
|
||||
|
||||
# Basic metadata in col 0
|
||||
tl = QGridLayout()
|
||||
gb = QGroupBox(_('Basic metadata'), tab0)
|
||||
l.addWidget(gb, 0, 0, 1, 1)
|
||||
gb.setLayout(tl)
|
||||
|
||||
self.button_box.addButton(self.fetch_metadata_button,
|
||||
QDialogButtonBox.ActionRole)
|
||||
self.config_metadata_button.setToolButtonStyle(Qt.ToolButtonTextOnly)
|
||||
self.config_metadata_button.setText(_('Configure metadata downloading'))
|
||||
self.button_box.addButton(self.config_metadata_button,
|
||||
QDialogButtonBox.ActionRole)
|
||||
sto(self.button_box, self.title)
|
||||
|
||||
def create_row(row, widget, tab_to, button=None, icon=None, span=1):
|
||||
ql = BuddyLabel(widget)
|
||||
tl.addWidget(ql, row, 1, 1, 1)
|
||||
tl.addWidget(widget, row, 2, 1, 1)
|
||||
if button is not None:
|
||||
tl.addWidget(button, row, 3, span, 1)
|
||||
if icon is not None:
|
||||
button.setIcon(QIcon(I(icon)))
|
||||
if tab_to is not None:
|
||||
if button is not None:
|
||||
sto(widget, button)
|
||||
sto(button, tab_to)
|
||||
else:
|
||||
sto(widget, tab_to)
|
||||
|
||||
tl.addWidget(self.swap_title_author_button, 0, 0, 2, 1)
|
||||
|
||||
create_row(0, self.title, self.title_sort,
|
||||
button=self.deduce_title_sort_button, span=2,
|
||||
icon='auto_author_sort.png')
|
||||
create_row(1, self.title_sort, self.authors)
|
||||
create_row(2, self.authors, self.author_sort,
|
||||
button=self.deduce_author_sort_button,
|
||||
span=2, icon='auto_author_sort.png')
|
||||
create_row(3, self.author_sort, self.series)
|
||||
create_row(4, self.series, self.series_index,
|
||||
button=self.remove_unused_series_button, icon='trash.png')
|
||||
create_row(5, self.series_index, self.tags)
|
||||
create_row(6, self.tags, self.rating, button=self.tags_editor_button)
|
||||
create_row(7, self.rating, self.pubdate)
|
||||
create_row(8, self.pubdate, self.publisher,
|
||||
button=self.pubdate.clear_button, icon='trash.png')
|
||||
create_row(9, self.publisher, self.timestamp)
|
||||
create_row(10, self.timestamp, self.identifiers,
|
||||
button=self.timestamp.clear_button, icon='trash.png')
|
||||
create_row(11, self.identifiers, self.comments,
|
||||
button=self.clear_identifiers_button, icon='trash.png')
|
||||
tl.addItem(QSpacerItem(1, 1, QSizePolicy.Fixed, QSizePolicy.Expanding),
|
||||
12, 1, 1 ,1)
|
||||
|
||||
# Custom metadata in col 1
|
||||
w = getattr(self, 'custom_metadata_widgets_parent', None)
|
||||
if w is not None:
|
||||
gb = QGroupBox(_('Custom metadata'), tab0)
|
||||
gbl = QVBoxLayout()
|
||||
gb.setLayout(gbl)
|
||||
sr = QScrollArea(gb)
|
||||
sr.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
|
||||
sr.setWidgetResizable(True)
|
||||
sr.setBackgroundRole(QPalette.Base)
|
||||
sr.setFrameStyle(QFrame.NoFrame)
|
||||
sr.setWidget(w)
|
||||
gbl.addWidget(sr)
|
||||
l.addWidget(gb, 0, 1, 1, 1)
|
||||
sp = QSizePolicy()
|
||||
sp.setVerticalStretch(10)
|
||||
sp.setHorizontalPolicy(QSizePolicy.Fixed)
|
||||
sp.setVerticalPolicy(QSizePolicy.Expanding)
|
||||
gb.setSizePolicy(sp)
|
||||
self.set_custom_metadata_tab_order()
|
||||
|
||||
# comments span col 0 & 1
|
||||
w = QGroupBox(_('Comments'), tab0)
|
||||
sp = QSizePolicy()
|
||||
sp.setVerticalStretch(10)
|
||||
sp.setHorizontalPolicy(QSizePolicy.Expanding)
|
||||
sp.setVerticalPolicy(QSizePolicy.Expanding)
|
||||
w.setSizePolicy(sp)
|
||||
lb = QHBoxLayout()
|
||||
w.setLayout(lb)
|
||||
lb.addWidget(self.comments)
|
||||
l.addWidget(w, 1, 0, 1, 2)
|
||||
|
||||
# Cover & formats in col 3
|
||||
gb = QGroupBox(_('Cover'), tab0)
|
||||
lb = QGridLayout()
|
||||
gb.setLayout(lb)
|
||||
lb.addWidget(self.cover, 0, 0, 1, 3, alignment=Qt.AlignCenter)
|
||||
sto(self.clear_identifiers_button, self.cover.buttons[0])
|
||||
for i, b in enumerate(self.cover.buttons[:3]):
|
||||
lb.addWidget(b, 1, i, 1, 1)
|
||||
sto(b, self.cover.buttons[i+1])
|
||||
hl = QHBoxLayout()
|
||||
for b in self.cover.buttons[3:]:
|
||||
hl.addWidget(b)
|
||||
sto(self.cover.buttons[-2], self.cover.buttons[-1])
|
||||
lb.addLayout(hl, 2, 0, 1, 3)
|
||||
l.addWidget(gb, 0, 2, 1, 1)
|
||||
l.addWidget(self.formats_manager, 1, 2, 1, 1)
|
||||
sto(self.cover.buttons[-1], self.formats_manager)
|
||||
|
||||
self.formats_manager.formats.setMaximumWidth(10000)
|
||||
self.formats_manager.formats.setIconSize(QSize(32, 32))
|
||||
|
||||
# }}}
|
||||
|
||||
|
||||
editors = {'default': MetadataSingleDialog, 'alt1': MetadataSingleDialogAlt1,
|
||||
'alt2': MetadataSingleDialogAlt2}
|
||||
|
||||
def edit_metadata(db, row_list, current_row, parent=None, view_slot=None,
|
||||
set_current_callback=None):
|
||||
|
@ -61,7 +61,8 @@ class ConfigWidget(ConfigWidgetBase, Ui_Form):
|
||||
|
||||
r('bools_are_tristate', db.prefs, restart_required=True)
|
||||
r = self.register
|
||||
choices = [(_('Default'), 'default'), (_('Compact Metadata'), 'alt1')]
|
||||
choices = [(_('Default'), 'default'), (_('Compact Metadata'), 'alt1'),
|
||||
(_('All on 1 tab'), 'alt2')]
|
||||
r('edit_metadata_single_layout', gprefs, choices=choices)
|
||||
|
||||
def initialize(self):
|
||||
|
@ -47,7 +47,7 @@ class StorePlugin(object): # {{{
|
||||
|
||||
def __init__(self, gui, name):
|
||||
from calibre.gui2 import JSONConfig
|
||||
|
||||
|
||||
self.gui = gui
|
||||
self.name = name
|
||||
self.base_plugin = None
|
||||
@ -79,14 +79,14 @@ class StorePlugin(object): # {{{
|
||||
return items as a generator.
|
||||
|
||||
Don't be lazy with the search! Load as much data as possible in the
|
||||
:class:`calibre.gui2.store.search_result.SearchResult` object.
|
||||
:class:`calibre.gui2.store.search_result.SearchResult` object.
|
||||
However, if data (such as cover_url)
|
||||
isn't available because the store does not display cover images then it's okay to
|
||||
ignore it.
|
||||
|
||||
|
||||
At the very least a :class:`calibre.gui2.store.search_result.SearchResult`
|
||||
returned by this function must have the title, author and id.
|
||||
|
||||
|
||||
If you have to parse multiple pages to get all of the data then implement
|
||||
:meth:`get_deatils` for retrieving additional information.
|
||||
|
||||
@ -105,24 +105,24 @@ class StorePlugin(object): # {{{
|
||||
item_data is plugin specific and is used in :meth:`open` to open to a specifc place in the store.
|
||||
'''
|
||||
raise NotImplementedError()
|
||||
|
||||
|
||||
def get_details(self, search_result, timeout=60):
|
||||
'''
|
||||
Delayed search for information about specific search items.
|
||||
|
||||
|
||||
Typically, this will be used when certain information such as
|
||||
formats, drm status, cover url are not part of the main search
|
||||
results and the information is on another web page.
|
||||
|
||||
|
||||
Using this function allows for the main information (title, author)
|
||||
to be displayed in the search results while other information can
|
||||
take extra time to load. Splitting retrieving data that takes longer
|
||||
to load into a separate function will give the illusion of the search
|
||||
being faster.
|
||||
|
||||
|
||||
:param search_result: A search result that need details set.
|
||||
:param timeout: The maximum amount of time in seconds to spend downloading details.
|
||||
|
||||
|
||||
:return: True if the search_result was modified otherwise False
|
||||
'''
|
||||
return False
|
||||
@ -133,30 +133,30 @@ class StorePlugin(object): # {{{
|
||||
is called to update the caches. It is recommended to call this function
|
||||
from :meth:`open`. Especially if :meth:`open` does anything other than
|
||||
open a web page.
|
||||
|
||||
|
||||
This function can be called at any time. It is up to the plugin to determine
|
||||
if the cache really does need updating. Unless :param:`force` is True, then
|
||||
the plugin must update the cache. The only time force should be True is if
|
||||
this function is called by the plugin's configuration dialog.
|
||||
|
||||
|
||||
if :param:`suppress_progress` is False it is safe to assume that this function
|
||||
is being called from the main GUI thread so it is safe and recommended to use
|
||||
a QProgressDialog to display what is happening and allow the user to cancel
|
||||
the operation. if :param:`suppress_progress` is True then run the update
|
||||
silently. In this case there is no guarantee what thread is calling this
|
||||
function so no Qt related functionality that requires being run in the main
|
||||
GUI thread should be run. E.G. Open a QProgressDialog.
|
||||
|
||||
GUI thread should be run. E.G. Open a QProgressDialog.
|
||||
|
||||
:param parent: The parent object to be used by an GUI dialogs.
|
||||
|
||||
|
||||
:param timeout: The maximum amount of time that should be spent in
|
||||
any given network connection.
|
||||
|
||||
|
||||
:param force: Force updating the cache even if the plugin has determined
|
||||
it is not necessary.
|
||||
|
||||
|
||||
:param suppress_progress: Should a progress indicator be shown.
|
||||
|
||||
|
||||
:return: True if the cache was updated, False otherwise.
|
||||
'''
|
||||
return False
|
||||
|
Loading…
x
Reference in New Issue
Block a user