1):
+ weeknum -= 1
+
+ title = u'.tyzden ' + str(weeknum) + '/' + str(year)
+
+ base_url_path = 'http://www.tyzden.sk/casopis/' + str(year) + '/' + str(weeknum)
+ base_url = base_url_path + '.html'
+
+ oldest_article = 20
+ max_articles_per_feed = 100
+ remove_javascript = True
+
+ use_embedded_content = False
+ no_stylesheets = True
+
+ keep_only_tags = []
+ keep_only_tags.append(dict(name = 'h1'))
+ keep_only_tags.append(dict(name = 'div', attrs = {'class': 'text_area top_nofoto'}))
+ keep_only_tags.append(dict(name = 'div', attrs = {'class': 'text_block'}))
+
+ remove_tags_after = [dict(name = 'div', attrs = {'class': 'text_block'})]
+
+ def find_sections(self):
+ soup = self.index_to_soup(self.base_url)
+ # find cover pic
+ imgdiv = soup.find('div', attrs = {'class': 'foto'})
+ if imgdiv is not None:
+ img = imgdiv.find('img')
+ if img is not None:
+ self.cover_url = 'http://www.tyzden.sk/' + img['src']
+ # end find cover pic
+
+ for s in soup.findAll('a', attrs={'href': re.compile(r'rubrika/.*')}):
+ yield (self.tag_to_string(s), s)
+
+ def find_articles(self, soup):
+ for art in soup.findAllNext('a'):
+ if (not art['href'].startswith('casopis/')):
+ break;
+
+ url = art['href']
+ title = self.tag_to_string(art)
+ yield {
+ 'title': title, 'url':self.base_url_path + '/' + url, 'description':title,
+ 'date' : strftime('%a, %d %b'),
+ }
+
+ def parse_index(self):
+ feeds = []
+ for title, soup in self.find_sections():
+ feeds.append((title, list(self.find_articles(soup))))
+
+ return feeds
diff --git a/setup/build_environment.py b/setup/build_environment.py
index 10ab1b0735..f0adaf9584 100644
--- a/setup/build_environment.py
+++ b/setup/build_environment.py
@@ -117,7 +117,6 @@ if iswindows:
poppler_inc_dirs = consolidate('POPPLER_INC_DIR',
r'%s\poppler;%s'%(sw_inc_dir, sw_inc_dir))
- popplerqt4_inc_dirs = poppler_inc_dirs + [poppler_inc_dirs[1]+r'\qt4']
poppler_lib_dirs = consolidate('POPPLER_LIB_DIR', sw_lib_dir)
popplerqt4_lib_dirs = poppler_lib_dirs
poppler_libs = ['poppler']
@@ -131,7 +130,6 @@ elif isosx:
fc_lib = '/sw/lib'
poppler_inc_dirs = consolidate('POPPLER_INC_DIR',
'/sw/build/poppler-0.14.5/poppler:/sw/build/poppler-0.14.5')
- popplerqt4_inc_dirs = poppler_inc_dirs + [poppler_inc_dirs[0]+'/qt4']
poppler_lib_dirs = consolidate('POPPLER_LIB_DIR',
'/sw/lib')
poppler_libs = ['poppler']
@@ -150,9 +148,6 @@ else:
# Include directories
poppler_inc_dirs = pkgconfig_include_dirs('poppler',
'POPPLER_INC_DIR', '/usr/include/poppler')
- popplerqt4_inc_dirs = pkgconfig_include_dirs('poppler-qt4', '', '')
- if not popplerqt4_inc_dirs:
- popplerqt4_inc_dirs = poppler_inc_dirs + [poppler_inc_dirs[0]+'/qt4']
png_inc_dirs = pkgconfig_include_dirs('libpng', 'PNG_INC_DIR',
'/usr/include')
magick_inc_dirs = pkgconfig_include_dirs('MagickWand', 'MAGICK_INC', '/usr/include/ImageMagick')
@@ -187,20 +182,17 @@ if not poppler_inc_dirs or not os.path.exists(
poppler_error = \
('Poppler not found on your system. Various PDF related',
' functionality will not work. Use the POPPLER_INC_DIR and',
- ' POPPLER_LIB_DIR environment variables.')
-
-popplerqt4_error = None
-if not popplerqt4_inc_dirs or not os.path.exists(
- os.path.join(popplerqt4_inc_dirs[-1], 'poppler-qt4.h')):
- popplerqt4_error = \
- ('Poppler Qt4 bindings not found on your system.')
+ ' POPPLER_LIB_DIR environment variables. calibre requires '
+ ' the poppler XPDF headers. If your distro does not '
+ ' include them you will have to re-compile poppler '
+ ' by hand with --enable-xpdf-headers')
magick_error = None
if not magick_inc_dirs or not os.path.exists(os.path.join(magick_inc_dirs[0],
'wand')):
magick_error = ('ImageMagick not found on your system. '
'Try setting the environment variables MAGICK_INC '
- 'and MAGICK_LIB to help calibre locate the inclue and libbrary '
+ 'and MAGICK_LIB to help calibre locate the include and library '
'files.')
podofo_lib = os.environ.get('PODOFO_LIB_DIR', podofo_lib)
diff --git a/src/calibre/devices/android/driver.py b/src/calibre/devices/android/driver.py
index 73c930778e..5a82882dfa 100644
--- a/src/calibre/devices/android/driver.py
+++ b/src/calibre/devices/android/driver.py
@@ -53,6 +53,9 @@ class ANDROID(USBMS):
# LG
0x1004 : { 0x61cc : [0x100] },
+ # Archos
+ 0x0e79 : { 0x1420 : [0x0216]},
+
}
EBOOK_DIR_MAIN = ['eBooks/import', 'wordplayer/calibretransfer', 'Books']
EXTRA_CUSTOMIZATION_MESSAGE = _('Comma separated list of directories to '
@@ -61,18 +64,19 @@ class ANDROID(USBMS):
EXTRA_CUSTOMIZATION_DEFAULT = ', '.join(EBOOK_DIR_MAIN)
VENDOR_NAME = ['HTC', 'MOTOROLA', 'GOOGLE_', 'ANDROID', 'ACER',
- 'GT-I5700', 'SAMSUNG', 'DELL', 'LINUX', 'GOOGLE']
+ 'GT-I5700', 'SAMSUNG', 'DELL', 'LINUX', 'GOOGLE', 'ARCHOS']
WINDOWS_MAIN_MEM = ['ANDROID_PHONE', 'A855', 'A853', 'INC.NEXUS_ONE',
'__UMS_COMPOSITE', '_MB200', 'MASS_STORAGE', '_-_CARD', 'SGH-I897',
'GT-I9000', 'FILE-STOR_GADGET', 'SGH-T959', 'SAMSUNG_ANDROID',
'SCH-I500_CARD', 'SPH-D700_CARD', 'MB810', 'GT-P1000', 'DESIRE',
- 'SGH-T849', '_MB300']
+ 'SGH-T849', '_MB300', 'A70S']
WINDOWS_CARD_A_MEM = ['ANDROID_PHONE', 'GT-I9000_CARD', 'SGH-I897',
- 'FILE-STOR_GADGET', 'SGH-T959', 'SAMSUNG_ANDROID', 'GT-P1000_CARD']
+ 'FILE-STOR_GADGET', 'SGH-T959', 'SAMSUNG_ANDROID', 'GT-P1000_CARD',
+ 'A70S']
- OSX_MAIN_MEM = 'HTC Android Phone Media'
+ OSX_MAIN_MEM = 'Android Device Main Memory'
- MAIN_MEMORY_VOLUME_LABEL = 'Android Phone Internal Memory'
+ MAIN_MEMORY_VOLUME_LABEL = 'Android Device Main Memory'
SUPPORTS_SUB_DIRS = True
diff --git a/src/calibre/devices/prs505/driver.py b/src/calibre/devices/prs505/driver.py
index 98a7241a36..874fbe4b10 100644
--- a/src/calibre/devices/prs505/driver.py
+++ b/src/calibre/devices/prs505/driver.py
@@ -76,12 +76,23 @@ class PRS505(USBMS):
'sending DRMed books in which you cannot change the cover.'
' WARNING: This option should only be used with newer '
'SONY readers: 350, 650, 950 and newer.'),
+ _('Refresh separate covers when using automatic management (newer readers)') +
+ ':::' +
+ _('Set this option to have separate book covers uploaded '
+ 'every time you connect your device. Unset this option if '
+ 'you have so many books on the reader that performance is '
+ 'unacceptable.')
]
EXTRA_CUSTOMIZATION_DEFAULT = [
', '.join(['series', 'tags']),
+ False,
False
]
+ OPT_COLLECTIONS = 0
+ OPT_UPLOAD_COVERS = 1
+ OPT_REFRESH_COVERS = 2
+
plugboard = None
plugboard_func = None
@@ -171,7 +182,7 @@ class PRS505(USBMS):
opts = self.settings()
if opts.extra_customization:
collections = [x.strip() for x in
- opts.extra_customization[0].split(',')]
+ opts.extra_customization[self.OPT_COLLECTIONS].split(',')]
else:
collections = []
debug_print('PRS505: collection fields:', collections)
@@ -183,6 +194,20 @@ class PRS505(USBMS):
c.update(blists, collections, pb)
c.write()
+ if opts.extra_customization[self.OPT_REFRESH_COVERS]:
+ debug_print('PRS505: uploading covers in sync_booklists')
+ for idx,bl in blists.items():
+ prefix = self._card_a_prefix if idx == 1 else \
+ self._card_b_prefix if idx == 2 \
+ else self._main_prefix
+ for book in bl:
+ p = os.path.join(prefix, book.lpath)
+ self._upload_cover(os.path.dirname(p),
+ os.path.splitext(os.path.basename(p))[0],
+ book, p)
+ else:
+ debug_print('PRS505: NOT uploading covers in sync_booklists')
+
USBMS.sync_booklists(self, booklists, end_session=end_session)
debug_print('PRS505: finished sync_booklists')
@@ -199,11 +224,14 @@ class PRS505(USBMS):
def upload_cover(self, path, filename, metadata, filepath):
opts = self.settings()
- if not opts.extra_customization[1]:
+ if not opts.extra_customization[self.OPT_UPLOAD_COVERS]:
# Building thumbnails disabled
- debug_print('PRS505: not uploading covers')
+ debug_print('PRS505: not uploading cover')
return
- debug_print('PRS505: uploading covers')
+ debug_print('PRS505: uploading cover')
+ self._upload_cover(path, filename, metadata, filepath)
+
+ def _upload_cover(self, path, filename, metadata, filepath):
if metadata.thumbnail and metadata.thumbnail[-1]:
path = path.replace('/', os.sep)
is_main = path.startswith(self._main_prefix)
diff --git a/src/calibre/ebooks/conversion/preprocess.py b/src/calibre/ebooks/conversion/preprocess.py
index f994888f19..56c3077118 100644
--- a/src/calibre/ebooks/conversion/preprocess.py
+++ b/src/calibre/ebooks/conversion/preprocess.py
@@ -480,7 +480,7 @@ class HTMLPreProcessor(object):
end_rules.append((re.compile(u'(?<=.{%i}[–—])\s*
\s*(?=[[a-z\d])' % length), lambda match: ''))
end_rules.append(
# Un wrap using punctuation
- (re.compile(u'(?<=.{%i}([a-zäëïöüàèìòùáćéíóńśúâêîôûçąężıãõñæøþðß,:)\IA\u00DF]|(?(i|b|u)>)?\s*(
\s*
\s*)+\s*(?=(<(i|b|u)>)?\s*[\w\d$(])' % length, re.UNICODE), wrap_lines),
+ (re.compile(u'(?<=.{%i}([a-zäëïöüàèìòùáćéíóńśúâêîôûçąężıãõñæøþðßě,:)\IA\u00DF]|(?(i|b|u)>)?\s*(
\s*
\s*)+\s*(?=(<(i|b|u)>)?\s*[\w\d$(])' % length, re.UNICODE), wrap_lines),
)
for rule in self.PREPROCESS + start_rules:
diff --git a/src/calibre/ebooks/conversion/utils.py b/src/calibre/ebooks/conversion/utils.py
index cfa57a28c3..7732bb2b4d 100644
--- a/src/calibre/ebooks/conversion/utils.py
+++ b/src/calibre/ebooks/conversion/utils.py
@@ -186,20 +186,20 @@ class PreProcessor(object):
def punctuation_unwrap(self, length, content, format):
# define the pieces of the regex
- lookahead = "(?<=.{"+str(length)+"}([a-zäëïöüàèìòùáćéíóńśúâêîôûçąężıãõñæøþðß,:)\IA\u00DF]|(?\s*((p|span|div)>)?"
blanklines = "\s*(?P<(p|span|div)[^>]*>\s*(<(p|span|div)[^>]*>\s*(span|p|div)>\s*)(span|p|div)>\s*){0,3}\s*"
line_opening = "<(span|div|p)[^>]*>\s*(<(span|div|p)[^>]*>)?\s*"
txt_line_wrap = u"((\u0020|\u0009)*\n){1,4}"
-
+
unwrap_regex = lookahead+line_ending+blanklines+line_opening
if format == 'txt':
unwrap_regex = lookahead+txt_line_wrap
-
+
unwrap = re.compile(u"%s" % unwrap_regex, re.UNICODE)
content = unwrap.sub(' ', content)
return content
-
+
def __call__(self, html):
self.log("********* Preprocessing HTML *********")
diff --git a/src/calibre/ebooks/rtf/input.py b/src/calibre/ebooks/rtf/input.py
index 8c7561f68c..5154373eda 100644
--- a/src/calibre/ebooks/rtf/input.py
+++ b/src/calibre/ebooks/rtf/input.py
@@ -296,7 +296,7 @@ class RTFInput(InputFormatPlugin):
u'\u00a0
\n'.encode('utf-8'), res)
if self.opts.preprocess_html:
preprocessor = PreProcessor(self.opts, log=getattr(self, 'log', None))
- res = preprocessor(res)
+ res = preprocessor(res.decode('utf-8')).encode('utf-8')
f.write(res)
self.write_inline_css(inline_class, border_styles)
stream.seek(0)
diff --git a/src/calibre/ebooks/txt/input.py b/src/calibre/ebooks/txt/input.py
index 3957391494..aaff8b55c0 100644
--- a/src/calibre/ebooks/txt/input.py
+++ b/src/calibre/ebooks/txt/input.py
@@ -53,7 +53,7 @@ class TXTInput(InputFormatPlugin):
def convert(self, stream, options, file_ext, log,
accelerators):
log.debug('Reading text from file...')
-
+
txt = stream.read()
# Get the encoding of the document.
if options.input_encoding:
@@ -80,7 +80,7 @@ class TXTInput(InputFormatPlugin):
# Get length for hyphen removal and punctuation unwrap
docanalysis = DocAnalysis('txt', txt)
length = docanalysis.line_length(.5)
-
+
if options.formatting_type == 'auto':
options.formatting_type = detect_formatting_type(txt)
@@ -122,7 +122,7 @@ class TXTInput(InputFormatPlugin):
txt = preprocessor.punctuation_unwrap(length, txt, 'txt')
flow_size = getattr(options, 'flow_size', 0)
-
+
if options.formatting_type == 'heuristic':
html = convert_heuristic(txt, epub_split_size_kb=flow_size)
else:
diff --git a/src/calibre/ebooks/txt/txtml.py b/src/calibre/ebooks/txt/txtml.py
index 786f50824d..00992a8612 100644
--- a/src/calibre/ebooks/txt/txtml.py
+++ b/src/calibre/ebooks/txt/txtml.py
@@ -218,6 +218,10 @@ class TXTMLizer(object):
if tag in SPACE_TAGS:
text.append(u' ')
+
+ # Scene breaks.
+ if tag == 'hr':
+ text.append('\n\n* * *\n\n')
# Process tags that contain text.
if hasattr(elem, 'text') and elem.text:
diff --git a/src/calibre/gui2/preferences/plugboard.py b/src/calibre/gui2/preferences/plugboard.py
index 296387106c..e1dc6b03bd 100644
--- a/src/calibre/gui2/preferences/plugboard.py
+++ b/src/calibre/gui2/preferences/plugboard.py
@@ -5,11 +5,11 @@ __license__ = 'GPL v3'
__copyright__ = '2010, Kovid Goyal '
__docformat__ = 'restructuredtext en'
-from PyQt4 import QtGui
-from PyQt4.Qt import Qt
+from PyQt4.Qt import Qt, QLineEdit, QComboBox, SIGNAL, QListWidgetItem
from calibre.gui2 import error_dialog
from calibre.gui2.device import device_name_for_plugboards
+from calibre.gui2.dialogs.template_dialog import TemplateDialog
from calibre.gui2.preferences import ConfigWidgetBase, test_widget
from calibre.gui2.preferences.plugboard_ui import Ui_Form
from calibre.customize.ui import metadata_writers, device_plugins
@@ -17,6 +17,27 @@ from calibre.library.save_to_disk import plugboard_any_format_value, \
plugboard_any_device_value, plugboard_save_to_disk_value
from calibre.utils.formatter import validation_formatter
+
+class LineEditWithTextBox(QLineEdit):
+
+ '''
+ Extend the context menu of a QLineEdit to include more actions.
+ '''
+
+ def contextMenuEvent(self, event):
+ menu = self.createStandardContextMenu()
+ menu.addSeparator()
+
+ action_open_editor = menu.addAction(_('Open Editor'))
+
+ self.connect(action_open_editor, SIGNAL('triggered()'), self.open_editor)
+ menu.exec_(event.globalPos())
+
+ def open_editor(self):
+ t = TemplateDialog(self, self.text())
+ if t.exec_():
+ self.setText(t.textbox.toPlainText())
+
class ConfigWidget(ConfigWidgetBase, Ui_Form):
def genesis(self, gui):
@@ -72,10 +93,10 @@ class ConfigWidget(ConfigWidgetBase, Ui_Form):
self.source_widgets = []
self.dest_widgets = []
for i in range(0, len(self.dest_fields)-1):
- w = QtGui.QLineEdit(self)
+ w = LineEditWithTextBox(self)
self.source_widgets.append(w)
self.fields_layout.addWidget(w, 5+i, 0, 1, 1)
- w = QtGui.QComboBox(self)
+ w = QComboBox(self)
self.dest_widgets.append(w)
self.fields_layout.addWidget(w, 5+i, 1, 1, 1)
@@ -297,7 +318,7 @@ class ConfigWidget(ConfigWidgetBase, Ui_Form):
for op in self.current_plugboards[f][d]:
ops.append('([' + op[0] + '] -> ' + op[1] + ')')
txt = '%s:%s = %s\n'%(f, d, ', '.join(ops))
- item = QtGui.QListWidgetItem(txt)
+ item = QListWidgetItem(txt)
item.setData(Qt.UserRole, (f, d))
self.existing_plugboards.addItem(item)
self.refilling = False
diff --git a/src/calibre/library/custom_columns.py b/src/calibre/library/custom_columns.py
index d925f7c91d..f94081f046 100644
--- a/src/calibre/library/custom_columns.py
+++ b/src/calibre/library/custom_columns.py
@@ -151,6 +151,8 @@ class CustomColumns(object):
return v
def adapt_number(x, d):
+ if x is None:
+ return None
if isinstance(x, (str, unicode, bytes)):
if x.lower() == 'none':
return None
@@ -195,8 +197,8 @@ class CustomColumns(object):
data = self.custom_column_num_map[num]
row = self.data._data[idx] if index_is_id else self.data[idx]
ans = row[self.FIELD_MAP[data['num']]]
- if ans and data['is_multiple'] and data['datatype'] == 'text':
- ans = ans.split('|')
+ if data['is_multiple'] and data['datatype'] == 'text':
+ ans = ans.split('|') if ans else []
if data['display'].get('sort_alpha', False):
ans.sort(cmp=lambda x,y:cmp(x.lower(), y.lower()))
return ans
diff --git a/src/calibre/library/server/browse.py b/src/calibre/library/server/browse.py
index 37799c4cbc..3e4687be95 100644
--- a/src/calibre/library/server/browse.py
+++ b/src/calibre/library/server/browse.py
@@ -756,7 +756,7 @@ class BrowseServer(object):
sort = self.browse_sort_book_list(items, list_sort)
ids = [x[0] for x in items]
html = render_book_list(ids, self.opts.url_prefix,
- suffix=_('in search')+': '+query)
+ suffix=_('in search')+': '+xml(query))
return self.browse_template(sort, category=False, initial_search=query).format(
title=_('Matching books'),
script='booklist();', main=html)
diff --git a/src/calibre/library/sqlite.py b/src/calibre/library/sqlite.py
index 75856dd0f6..83f19b8711 100644
--- a/src/calibre/library/sqlite.py
+++ b/src/calibre/library/sqlite.py
@@ -98,9 +98,10 @@ class AumSortedConcatenate(object):
def finalize(self):
keys = self.ans.keys()
- if len(keys) == 0:
- return None
- if len(keys) == 1:
+ l = len(keys)
+ if l == 0:
+ return 'Unknown:::Unknown'
+ if l == 1:
return self.ans[keys[0]]
return ':#:'.join([self.ans[v] for v in sorted(keys)])
diff --git a/src/calibre/utils/formatter.py b/src/calibre/utils/formatter.py
index 2e4f843c3d..40760bf91b 100644
--- a/src/calibre/utils/formatter.py
+++ b/src/calibre/utils/formatter.py
@@ -98,9 +98,10 @@ class _Parser(object):
m = 'Formatter: ' + message + _(' near ')
if self.lex_pos > 0:
m = '{0} {1}'.format(m, self.prog[self.lex_pos-1][1])
- m = '{0} {1}'.format(m, self.prog[self.lex_pos][1])
- if self.lex_pos < len(self.prog):
+ elif self.lex_pos < len(self.prog):
m = '{0} {1}'.format(m, self.prog[self.lex_pos+1][1])
+ else:
+ m = '{0} {1}'.format(m, _('end of program'))
raise ValueError(m)
def token(self):