Sync to trunk.

This commit is contained in:
John Schember 2011-05-09 21:24:12 -04:00
commit b6bb7237a8
22 changed files with 252 additions and 84 deletions

View File

@ -30,3 +30,4 @@ nbproject/
.project .project
.pydevproject .pydevproject
.settings/ .settings/
*.DS_Store

View File

@ -21,14 +21,19 @@ class Fronda(BasicNewsRecipe):
feeds = [(u'Infformacje', u'http://fronda.pl/news/feed')] feeds = [(u'Infformacje', u'http://fronda.pl/news/feed')]
keep_only_tags = [dict(name='h1', attrs={'class':'big'}), keep_only_tags = [dict(name='h2', attrs={'class':'news_title'}),
dict(name='ul', attrs={'class':'about clear'}), dict(name='div', attrs={'class':'naglowek_tresc'}),
dict(name='div', attrs={'class':'content'})] dict(name='div', attrs={'id':'czytaj'}) ]
remove_tags = [dict(name='a', attrs={'class':'print'})]
preprocess_regexps = [ preprocess_regexps = [
(re.compile(i[0], re.IGNORECASE | re.DOTALL), i[1]) for i in (re.compile(i[0], re.IGNORECASE | re.DOTALL), i[1]) for i in
[ (r'<a href="#" class="print">Drukuj</a>', lambda match: ''), [ (r'<p><a href="http://fronda.pl/sklepy">.*</a></p>', lambda match: ''),
(r'<p><a href="http://fronda.pl/sklepy">.*</a></p>', lambda match: ''),
(r'<p><a href="http://fronda.pl/pasaz">.*</a></p>', lambda match: ''), (r'<p><a href="http://fronda.pl/pasaz">.*</a></p>', lambda match: ''),
(r'<h3><strong>W.* lektury.*</a></p></div>', lambda match: '</div>'), (r'<h3><strong>W.* lektury.*</a></p></div>', lambda match: '</div>'),
(r'<h3>Zobacz t.*?</div>', lambda match: '</div>') ] (r'<h3>Zobacz t.*?</div>', lambda match: '</div>'),
(r'<p[^>]*>&nbsp;</p>', lambda match: ''),
(r'<p><span style=".*?"><br /></span></p> ', lambda match: ''),
(r'<a style=\'float:right;margin-top:3px;\' href="http://www.facebook.com/share.php?.*?</a>', lambda match: '')]
] ]

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

BIN
recipes/icons/ziuaveche.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 554 B

View File

@ -0,0 +1,36 @@
__license__ = 'GPL v3'
__copyright__ = '2011, Seongkyoun Yoo <Seongkyoun.yoo at gmail.com>'
'''
Profile to download KoreaHerald
'''
from calibre.web.feeds.news import BasicNewsRecipe
class KoreaHerald(BasicNewsRecipe):
title = u'KoreaHerald'
language = 'en'
description = u'Korea Herald News articles'
__author__ = 'Seongkyoun Yoo'
oldest_article = 10
recursions = 3
max_articles_per_feed = 10
no_stylesheets = True
keep_only_tags = [
dict(id=['contentLeft', '_article'])
]
remove_tags = [
dict(name='iframe'),
dict(name='div', attrs={'class':['left','htit2', 'navigation','banner_txt','banner_img']}),
dict(name='ul', attrs={'class':['link_icon', 'flow_icon','detailTextAD110113']}),
]
feeds = [
('All News','http://www.koreaherald.com/rss/020000000000.xml'),
('National','http://www.koreaherald.com/rss/020100000000.xml'),
('Business','http://www.koreaherald.com/rss/020200000000.xml'),
('Life&Style','http://www.koreaherald.com/rss/020300000000.xml'),
('Entertainment','http://www.koreaherald.com/rss/020400000000.xml'),
('Sports','http://www.koreaherald.com/rss/020500000000.xml'),
('Opinion','http://www.koreaherald.com/rss/020600000000.xml'),
('English Cafe','http://www.koreaherald.com/rss/021000000000.xml'),
]

View File

@ -2,7 +2,7 @@ from calibre.web.feeds.news import BasicNewsRecipe
class RzeczpospolitaRecipe(BasicNewsRecipe): class RzeczpospolitaRecipe(BasicNewsRecipe):
__license__ = 'GPL v3' __license__ = 'GPL v3'
__author__ = 'kwetal' __author__ = u'kwetal and Tomasz Dlugosz'
language = 'pl' language = 'pl'
version = 1 version = 1
@ -38,6 +38,8 @@ class RzeczpospolitaRecipe(BasicNewsRecipe):
remove_tags.append(dict(name = 'div', attrs = {'class' : 'clr'})) remove_tags.append(dict(name = 'div', attrs = {'class' : 'clr'}))
remove_tags.append(dict(name = 'div', attrs = {'id' : 'share_bottom'})) remove_tags.append(dict(name = 'div', attrs = {'id' : 'share_bottom'}))
remove_tags.append(dict(name = 'div', attrs = {'id' : 'copyright_law'})) remove_tags.append(dict(name = 'div', attrs = {'id' : 'copyright_law'}))
remove_tags.append(dict(name = 'div', attrs = {'class' : 'more'}))
remove_tags.append(dict(name = 'div', attrs = {'class' : 'editorPicks'}))
extra_css = ''' extra_css = '''
body {font-family: verdana, arial, helvetica, geneva, sans-serif ;} body {font-family: verdana, arial, helvetica, geneva, sans-serif ;}
@ -48,6 +50,13 @@ class RzeczpospolitaRecipe(BasicNewsRecipe):
.fot{font-size: x-small; color: #666666;} .fot{font-size: x-small; color: #666666;}
''' '''
def skip_ad_pages(self, soup):
if ('advertisement' in soup.find('title').string.lower()):
href = soup.find('a').get('href')
return self.index_to_soup(href, raw=True)
else:
return None
def print_version(self, url): def print_version(self, url):
start, sep, rest = url.rpartition('/') start, sep, rest = url.rpartition('/')
forget, sep, index = rest.rpartition(',') forget, sep, index = rest.rpartition(',')

View File

@ -1,17 +1,12 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
__license__ = 'GPL v3'
__copyright__ = '2009, Gerhard Aigner <gerhard.aigner at gmail.com>'
import re
from calibre.web.feeds.news import BasicNewsRecipe from calibre.web.feeds.news import BasicNewsRecipe
class TelepolisNews(BasicNewsRecipe): class TelepolisNews(BasicNewsRecipe):
title = u'Telepolis (News+Artikel)' title = u'Telepolis (News+Artikel)'
__author__ = 'Gerhard Aigner' __author__ = 'syntaxis'
publisher = 'Heise Zeitschriften Verlag GmbH & Co KG' publisher = 'Heise Zeitschriften Verlag GmbH & Co KG'
description = 'News from telepolis' description = 'News from Telepolis'
category = 'news' category = 'news'
oldest_article = 7 oldest_article = 7
max_articles_per_feed = 100 max_articles_per_feed = 100
@ -20,14 +15,19 @@ class TelepolisNews(BasicNewsRecipe):
encoding = "utf-8" encoding = "utf-8"
language = 'de' language = 'de'
use_embedded_content =False
remove_empty_feeds = True remove_empty_feeds = True
preprocess_regexps = [(re.compile(r'<a[^>]*>', re.DOTALL|re.IGNORECASE), lambda match: ''),
(re.compile(r'</a>', re.DOTALL|re.IGNORECASE), lambda match: ''),]
keep_only_tags = [dict(name = 'td',attrs={'class':'bloghead'}),dict(name = 'td',attrs={'class':'blogfliess'})]
remove_tags = [dict(name='img'), dict(name='td',attrs={'class':'blogbottom'}), dict(name='td',attrs={'class':'forum'})] keep_only_tags = [dict(name = 'div',attrs={'class':'head'}),dict(name = 'div',attrs={'class':'leftbox'}),dict(name='td',attrs={'class':'strict'})]
remove_tags = [ dict(name='td',attrs={'class':'blogbottom'}),
dict(name='div',attrs={'class':'forum'}), dict(name='div',attrs={'class':'social'}),dict(name='div',attrs={'class':'blog-letter p-news'}),
dict(name='div',attrs={'class':'blog-sub'}),dict(name='div',attrs={'class':'version-div'}),dict(name='div',attrs={'id':'breadcrumb'})
,dict(attrs={'class':'tp-url'}),dict(attrs={'class':'blog-name entry_'}) ]
remove_tags_after = [dict(name='span', attrs={'class':['breadcrumb']})]
feeds = [(u'News', u'http://www.heise.de/tp/news-atom.xml')] feeds = [(u'News', u'http://www.heise.de/tp/news-atom.xml')]
@ -39,15 +39,8 @@ class TelepolisNews(BasicNewsRecipe):
html2epub_options = 'publisher="' + publisher + '"\ncomments="' + description + '"\ntags="' + category + '"' html2epub_options = 'publisher="' + publisher + '"\ncomments="' + description + '"\ntags="' + category + '"'
def get_article_url(self, article):
'''if the linked article is of kind artikel don't take it'''
if (article.link.count('artikel') > 1) :
return None
return article.link
def preprocess_html(self, soup): def preprocess_html(self, soup):
mtag = '<meta http-equiv="Content-Type" content="text/html; charset=' + self.encoding + '">' mtag = '<meta http-equiv="Content-Type" content="text/html; charset=' + self.encoding + '">'
soup.head.insert(0,mtag) soup.head.insert(0,mtag)
return soup return soup

53
recipes/ziuaveche.recipe Normal file
View File

@ -0,0 +1,53 @@
# -*- coding: utf-8 -*-
#!/usr/bin/env python
__license__ = 'GPL v3'
__copyright__ = u'2011, Silviu Cotoar\u0103'
'''
ziuaveche.ro
'''
from calibre.web.feeds.news import BasicNewsRecipe
class ZiuaVeche(BasicNewsRecipe):
title = u'Ziua Veche'
__author__ = u'Silviu Cotoar\u0103'
description = 'Cotidian online'
publisher = 'Ziua Veche'
oldest_article = 5
language = 'ro'
max_articles_per_feed = 100
no_stylesheets = True
use_embedded_content = False
category = 'Ziare,Cotidiane,Stiri'
encoding = 'utf-8'
cover_url = 'http://www.ziuaveche.ro/wp-content/themes/tema/images/zv-logo-alb-old.png'
conversion_options = {
'comments' : description
,'tags' : category
,'language' : language
,'publisher' : publisher
}
keep_only_tags = [
dict(name='div', attrs={'id':'singlePost'})
]
remove_tags = [
dict(name='div', attrs={'id':'LikePluginPagelet'})
]
remove_tags_after = [
dict(name='div', attrs={'id':'LikePluginPagelet'})
]
feeds = [
(u'Feeds', u'http://www.ziuaveche.ro/feed/rss')
]
def preprocess_html(self, soup):
return self.adeify_images(soup)

View File

@ -449,7 +449,7 @@ class CatalogPlugin(Plugin): # {{{
['author_sort','authors','comments','cover','formats', ['author_sort','authors','comments','cover','formats',
'id','isbn','ondevice','pubdate','publisher','rating', 'id','isbn','ondevice','pubdate','publisher','rating',
'series_index','series','size','tags','timestamp', 'series_index','series','size','tags','timestamp',
'title','uuid']) 'title_sort','title','uuid'])
all_custom_fields = set(db.custom_field_keys()) all_custom_fields = set(db.custom_field_keys())
all_fields = all_std_fields.union(all_custom_fields) all_fields = all_std_fields.union(all_custom_fields)

View File

@ -203,9 +203,11 @@ class ITUNES(DriverBase):
# 0x1294 iPhone 3GS # 0x1294 iPhone 3GS
# 0x1297 iPhone 4 # 0x1297 iPhone 4
# 0x129a iPad # 0x129a iPad
# 0x12a2 iPad2 # 0x129f iPad2 (WiFi)
# 0x12a2 iPad2 (GSM)
# 0x12a3 iPad2 (CDMA)
VENDOR_ID = [0x05ac] VENDOR_ID = [0x05ac]
PRODUCT_ID = [0x1292,0x1293,0x1294,0x1297,0x1299,0x129a,0x12a2] PRODUCT_ID = [0x1292,0x1293,0x1294,0x1297,0x1299,0x129a,0x129f,0x12a2,0x12a3]
BCD = [0x01] BCD = [0x01]
# Plugboard ID # Plugboard ID

View File

@ -68,9 +68,9 @@ class USER_DEFINED(USBMS):
'is prepended to any send_to_device template') + '</p>', 'is prepended to any send_to_device template') + '</p>',
] ]
EXTRA_CUSTOMIZATION_DEFAULT = [ EXTRA_CUSTOMIZATION_DEFAULT = [
'0x0000', '0xffff',
'0x0000', '0xffff',
'0x0000', '0xffff',
None, None,
'', '',
'', '',

View File

@ -68,7 +68,8 @@ def check_command_line_options(parser, args, log):
raise SystemExit(1) raise SystemExit(1)
output = args[2] output = args[2]
if output.startswith('.') and output != '.': if output.startswith('.') and (output != '.' and not
output.startswith('..')):
output = os.path.splitext(os.path.basename(input))[0]+output output = os.path.splitext(os.path.basename(input))[0]+output
output = os.path.abspath(output) output = os.path.abspath(output)

View File

@ -338,7 +338,7 @@ class Amazon(Source):
q['field-author'] = ' '.join(author_tokens) q['field-author'] = ' '.join(author_tokens)
if not ('field-keywords' in q or 'field-isbn' in q or if not ('field-keywords' in q or 'field-isbn' in q or
('field-title' in q and 'field-author' in q)): ('field-title' in q)):
# Insufficient metadata to make an identify query # Insufficient metadata to make an identify query
return None return None

View File

@ -212,6 +212,9 @@ class Source(Plugin):
def is_customizable(self): def is_customizable(self):
return True return True
def customization_help(self):
return 'This plugin can only be customized using the GUI'
def config_widget(self): def config_widget(self):
from calibre.gui2.metadata.config import ConfigWidget from calibre.gui2.metadata.config import ConfigWidget
return ConfigWidget(self) return ConfigWidget(self)
@ -288,10 +291,10 @@ class Source(Plugin):
parts = parts[1:] + parts[:1] parts = parts[1:] + parts[:1]
for tok in parts: for tok in parts:
tok = remove_pat.sub('', tok).strip() tok = remove_pat.sub('', tok).strip()
if len(tok) > 2 and tok.lower() not in ('von', ): if len(tok) > 2 and tok.lower() not in ('von', 'van',
_('Unknown').lower()):
yield tok yield tok
def get_title_tokens(self, title, strip_joiners=True, strip_subtitle=False): def get_title_tokens(self, title, strip_joiners=True, strip_subtitle=False):
''' '''
Take a title and return a list of tokens useful for an AND search query. Take a title and return a list of tokens useful for an AND search query.

View File

@ -20,6 +20,9 @@ class GenerateCatalogAction(InterfaceAction):
action_spec = (_('Create a catalog of the books in your calibre library'), 'catalog.png', 'Catalog builder', None) action_spec = (_('Create a catalog of the books in your calibre library'), 'catalog.png', 'Catalog builder', None)
dont_add_to = frozenset(['menubar-device', 'toolbar-device', 'context-menu-device']) dont_add_to = frozenset(['menubar-device', 'toolbar-device', 'context-menu-device'])
def genesis(self):
self.qaction.triggered.connect(self.generate_catalog)
def generate_catalog(self): def generate_catalog(self):
rows = self.gui.library_view.selectionModel().selectedRows() rows = self.gui.library_view.selectionModel().selectedRows()
if not rows or len(rows) < 2: if not rows or len(rows) < 2:

View File

@ -439,6 +439,7 @@ def populate_metadata_page(layout, db, book_id, bulk=False, two_column=False, pa
w = widget_factory(dt, col) w = widget_factory(dt, col)
ans.append(w) ans.append(w)
for c in range(0, len(w.widgets), 2): for c in range(0, len(w.widgets), 2):
w.widgets[c].setWordWrap(True)
w.widgets[c].setBuddy(w.widgets[c+1]) w.widgets[c].setBuddy(w.widgets[c+1])
layout.addWidget(w.widgets[c], row, column) layout.addWidget(w.widgets[c], row, column)
layout.addWidget(w.widgets[c+1], row, column+1) layout.addWidget(w.widgets[c+1], row, column+1)

View File

@ -19,17 +19,23 @@ class MessageBox(QDialog, Ui_Dialog): # {{{
INFO = 2 INFO = 2
QUESTION = 3 QUESTION = 3
def __init__(self, type_, title, msg, det_msg='', show_copy_button=True, def __init__(self, type_, title, msg,
parent=None): det_msg='',
q_icon=None,
show_copy_button=True,
parent=None):
QDialog.__init__(self, parent) QDialog.__init__(self, parent)
icon = { if q_icon is None:
self.ERROR : 'error', icon = {
self.WARNING: 'warning', self.ERROR : 'error',
self.INFO: 'information', self.WARNING: 'warning',
self.QUESTION: 'question', self.INFO: 'information',
}[type_] self.QUESTION: 'question',
icon = 'dialog_%s.png'%icon }[type_]
self.icon = QIcon(I(icon)) icon = 'dialog_%s.png'%icon
self.icon = QIcon(I(icon))
else:
self.icon = q_icon
self.setupUi(self) self.setupUi(self)
self.setWindowTitle(title) self.setWindowTitle(title)
@ -44,7 +50,6 @@ class MessageBox(QDialog, Ui_Dialog): # {{{
self.bb.ActionRole) self.bb.ActionRole)
self.ctc_button.clicked.connect(self.copy_to_clipboard) self.ctc_button.clicked.connect(self.copy_to_clipboard)
self.show_det_msg = _('Show &details') self.show_det_msg = _('Show &details')
self.hide_det_msg = _('Hide &details') self.hide_det_msg = _('Hide &details')
self.det_msg_toggle = self.bb.addButton(self.show_det_msg, self.bb.ActionRole) self.det_msg_toggle = self.bb.addButton(self.show_det_msg, self.bb.ActionRole)

View File

@ -10,7 +10,7 @@ __docformat__ = 'restructuredtext en'
import textwrap, re, os import textwrap, re, os
from PyQt4.Qt import (Qt, QDateEdit, QDate, pyqtSignal, QMessageBox, from PyQt4.Qt import (Qt, QDateEdit, QDate, pyqtSignal, QMessageBox,
QIcon, QToolButton, QWidget, QLabel, QGridLayout, QIcon, QToolButton, QWidget, QLabel, QGridLayout, QApplication,
QDoubleSpinBox, QListWidgetItem, QSize, QPixmap, QDoubleSpinBox, QListWidgetItem, QSize, QPixmap,
QPushButton, QSpinBox, QLineEdit, QSizePolicy) QPushButton, QSpinBox, QLineEdit, QSizePolicy)
@ -1037,6 +1037,13 @@ class IdentifiersEdit(QLineEdit): # {{{
self.setToolTip(tt+extra) self.setToolTip(tt+extra)
self.setStyleSheet('QLineEdit { background-color: %s }'%col) self.setStyleSheet('QLineEdit { background-color: %s }'%col)
def paste_isbn(self):
text = unicode(QApplication.clipboard().text()).strip()
if text:
vals = self.current_val
vals['isbn'] = text
self.current_val = vals
# }}} # }}}
class PublisherEdit(MultiCompleteComboBox): # {{{ class PublisherEdit(MultiCompleteComboBox): # {{{
@ -1119,7 +1126,7 @@ class DateEdit(QDateEdit): # {{{
@dynamic_property @dynamic_property
def current_val(self): def current_val(self):
def fget(self): def fget(self):
return qt_to_dt(self.date()) return qt_to_dt(self.date(), as_utc=False)
def fset(self, val): def fset(self, val):
if val is None: if val is None:
val = UNDEFINED_DATE val = UNDEFINED_DATE

View File

@ -31,6 +31,7 @@ class MetadataSingleDialogBase(ResizableDialog):
view_format = pyqtSignal(object, object) view_format = pyqtSignal(object, object)
cc_two_column = tweaks['metadata_single_use_2_cols_for_custom_fields'] cc_two_column = tweaks['metadata_single_use_2_cols_for_custom_fields']
one_line_comments_toolbar = False one_line_comments_toolbar = False
use_toolbutton_for_config_metadata = True
def __init__(self, db, parent=None): def __init__(self, db, parent=None):
self.db = db self.db = db
@ -69,7 +70,11 @@ class MetadataSingleDialogBase(ResizableDialog):
self.setLayout(self.l) self.setLayout(self.l)
self.l.setMargin(0) self.l.setMargin(0)
self.l.addWidget(self.scroll_area) self.l.addWidget(self.scroll_area)
self.l.addWidget(self.button_box) ll = self.button_box_layout = QHBoxLayout()
self.l.addLayout(ll)
ll.addSpacing(10)
ll.addWidget(self.button_box)
ll.addSpacing(10)
self.setWindowIcon(QIcon(I('edit_input.png'))) self.setWindowIcon(QIcon(I('edit_input.png')))
self.setWindowTitle(_('Edit Metadata')) self.setWindowTitle(_('Edit Metadata'))
@ -125,6 +130,13 @@ class MetadataSingleDialogBase(ResizableDialog):
'Swap the author and title')) 'Swap the author and title'))
self.swap_title_author_button.clicked.connect(self.swap_title_author) self.swap_title_author_button.clicked.connect(self.swap_title_author)
self.manage_authors_button = QToolButton(self)
self.manage_authors_button.setIcon(QIcon(I('user_profile.png')))
self.manage_authors_button.setToolTip('<p>' + _(
'Manage authors. Use to rename authors and correct '
'individual author\'s sort values') + '</p>')
self.manage_authors_button.clicked.connect(self.authors.manage_authors)
self.series = SeriesEdit(self) self.series = SeriesEdit(self)
self.remove_unused_series_button = QToolButton(self) self.remove_unused_series_button = QToolButton(self)
self.remove_unused_series_button.setToolTip( self.remove_unused_series_button.setToolTip(
@ -161,6 +173,12 @@ class MetadataSingleDialogBase(ResizableDialog):
self.clear_identifiers_button = QToolButton(self) self.clear_identifiers_button = QToolButton(self)
self.clear_identifiers_button.setIcon(QIcon(I('trash.png'))) self.clear_identifiers_button.setIcon(QIcon(I('trash.png')))
self.clear_identifiers_button.clicked.connect(self.identifiers.clear) self.clear_identifiers_button.clicked.connect(self.identifiers.clear)
self.paste_isbn_button = QToolButton(self)
self.paste_isbn_button.setToolTip('<p>' +
_('Paste the contents of the clipboard into the '
'identifiers box prefixed with isbn:') + '</p>')
self.paste_isbn_button.setIcon(QIcon(I('edit-paste.png')))
self.paste_isbn_button.clicked.connect(self.identifiers.paste_isbn)
self.publisher = PublisherEdit(self) self.publisher = PublisherEdit(self)
self.basic_metadata_widgets.append(self.publisher) self.basic_metadata_widgets.append(self.publisher)
@ -176,7 +194,12 @@ class MetadataSingleDialogBase(ResizableDialog):
font.setBold(True) font.setBold(True)
self.fetch_metadata_button.setFont(font) self.fetch_metadata_button.setFont(font)
self.config_metadata_button = QToolButton(self) if self.use_toolbutton_for_config_metadata:
self.config_metadata_button = QToolButton(self)
self.config_metadata_button.setIcon(QIcon(I('config.png')))
else:
self.config_metadata_button = QPushButton(self)
self.config_metadata_button.setText(_('Configure download metadata'))
self.config_metadata_button.setIcon(QIcon(I('config.png'))) self.config_metadata_button.setIcon(QIcon(I('config.png')))
self.config_metadata_button.clicked.connect(self.configure_metadata) self.config_metadata_button.clicked.connect(self.configure_metadata)
self.config_metadata_button.setToolTip( self.config_metadata_button.setToolTip(
@ -499,7 +522,8 @@ class MetadataSingleDialog(MetadataSingleDialogBase): # {{{
sto(one, two) sto(one, two)
sto(two, three) sto(two, three)
tl.addWidget(self.swap_title_author_button, 0, 0, 2, 1) tl.addWidget(self.swap_title_author_button, 0, 0, 1, 1)
tl.addWidget(self.manage_authors_button, 1, 0, 1, 1)
create_row(0, self.title, self.deduce_title_sort_button, self.title_sort) create_row(0, self.title, self.deduce_title_sort_button, self.title_sort)
sto(self.title_sort, self.authors) sto(self.title_sort, self.authors)
@ -508,6 +532,7 @@ class MetadataSingleDialog(MetadataSingleDialogBase): # {{{
create_row(2, self.series, self.remove_unused_series_button, create_row(2, self.series, self.remove_unused_series_button,
self.series_index, icon='trash.png') self.series_index, icon='trash.png')
sto(self.series_index, self.swap_title_author_button) sto(self.series_index, self.swap_title_author_button)
sto(self.swap_title_author_button, self.manage_authors_button)
tl.addWidget(self.formats_manager, 0, 6, 3, 1) tl.addWidget(self.formats_manager, 0, 6, 3, 1)
@ -518,7 +543,7 @@ class MetadataSingleDialog(MetadataSingleDialogBase): # {{{
self.tabs[0].gb = gb = QGroupBox(_('Change cover'), self) self.tabs[0].gb = gb = QGroupBox(_('Change cover'), self)
gb.l = l = QGridLayout() gb.l = l = QGridLayout()
gb.setLayout(l) gb.setLayout(l)
sto(self.swap_title_author_button, self.cover.buttons[0]) sto(self.manage_authors_button, self.cover.buttons[0])
for i, b in enumerate(self.cover.buttons[:3]): for i, b in enumerate(self.cover.buttons[:3]):
l.addWidget(b, 0, i, 1, 1) l.addWidget(b, 0, i, 1, 1)
sto(b, self.cover.buttons[i+1]) sto(b, self.cover.buttons[i+1])
@ -532,10 +557,16 @@ class MetadataSingleDialog(MetadataSingleDialogBase): # {{{
w.setLayout(w.l) w.setLayout(w.l)
l.setMargin(0) l.setMargin(0)
self.splitter.addWidget(w) self.splitter.addWidget(w)
def create_row2(row, widget, button=None): def create_row2(row, widget, button=None, front_button=None):
row += 1 row += 1
ql = BuddyLabel(widget) ql = BuddyLabel(widget)
l.addWidget(ql, row, 0, 1, 1) if front_button:
ltl = QHBoxLayout()
ltl.addWidget(front_button)
ltl.addWidget(ql)
l.addLayout(ltl, row, 0, 1, 1)
else:
l.addWidget(ql, row, 0, 1, 1)
l.addWidget(widget, row, 1, 1, 2 if button is None else 1) l.addWidget(widget, row, 1, 1, 2 if button is None else 1)
if button is not None: if button is not None:
l.addWidget(button, row, 2, 1, 1) l.addWidget(button, row, 2, 1, 1)
@ -550,8 +581,10 @@ class MetadataSingleDialog(MetadataSingleDialogBase): # {{{
create_row2(1, self.rating) create_row2(1, self.rating)
sto(self.rating, self.tags) sto(self.rating, self.tags)
create_row2(2, self.tags, self.tags_editor_button) create_row2(2, self.tags, self.tags_editor_button)
sto(self.tags_editor_button, self.identifiers) sto(self.tags_editor_button, self.paste_isbn_button)
create_row2(3, self.identifiers, self.clear_identifiers_button) sto(self.paste_isbn_button, self.identifiers)
create_row2(3, self.identifiers, self.clear_identifiers_button,
front_button=self.paste_isbn_button)
sto(self.clear_identifiers_button, self.timestamp) sto(self.clear_identifiers_button, self.timestamp)
create_row2(4, self.timestamp, self.timestamp.clear_button) create_row2(4, self.timestamp, self.timestamp.clear_button)
sto(self.timestamp.clear_button, self.pubdate) sto(self.timestamp.clear_button, self.pubdate)
@ -589,6 +622,7 @@ class MetadataSingleDialogAlt1(MetadataSingleDialogBase): # {{{
cc_two_column = False cc_two_column = False
one_line_comments_toolbar = True one_line_comments_toolbar = True
use_toolbutton_for_config_metadata = False
on_drag_enter = pyqtSignal() on_drag_enter = pyqtSignal()
@ -624,13 +658,11 @@ class MetadataSingleDialogAlt1(MetadataSingleDialogBase): # {{{
self.tabs[0].l.addWidget(gb, 0, 0, 1, 1) self.tabs[0].l.addWidget(gb, 0, 0, 1, 1)
gb.setLayout(tl) gb.setLayout(tl)
self.button_box.addButton(self.fetch_metadata_button, self.button_box_layout.insertWidget(1, self.fetch_metadata_button)
QDialogButtonBox.ActionRole) self.button_box_layout.insertWidget(2, self.config_metadata_button)
self.config_metadata_button.setToolButtonStyle(Qt.ToolButtonTextOnly) sto(self.button_box, self.fetch_metadata_button)
self.config_metadata_button.setText(_('Configure metadata downloading')) sto(self.fetch_metadata_button, self.config_metadata_button)
self.button_box.addButton(self.config_metadata_button, sto(self.config_metadata_button, self.title)
QDialogButtonBox.ActionRole)
sto(self.button_box, self.title)
def create_row(row, widget, tab_to, button=None, icon=None, span=1): def create_row(row, widget, tab_to, button=None, icon=None, span=1):
ql = BuddyLabel(widget) ql = BuddyLabel(widget)
@ -648,6 +680,8 @@ class MetadataSingleDialogAlt1(MetadataSingleDialogBase): # {{{
sto(widget, tab_to) sto(widget, tab_to)
tl.addWidget(self.swap_title_author_button, 0, 0, 2, 1) tl.addWidget(self.swap_title_author_button, 0, 0, 2, 1)
tl.addWidget(self.manage_authors_button, 2, 0, 1, 1)
tl.addWidget(self.paste_isbn_button, 11, 0, 1, 1)
create_row(0, self.title, self.title_sort, create_row(0, self.title, self.title_sort,
button=self.deduce_title_sort_button, span=2, button=self.deduce_title_sort_button, span=2,
@ -669,6 +703,9 @@ class MetadataSingleDialogAlt1(MetadataSingleDialogBase): # {{{
button=self.timestamp.clear_button, icon='trash.png') button=self.timestamp.clear_button, icon='trash.png')
create_row(11, self.identifiers, self.comments, create_row(11, self.identifiers, self.comments,
button=self.clear_identifiers_button, icon='trash.png') button=self.clear_identifiers_button, icon='trash.png')
sto(self.clear_identifiers_button, self.swap_title_author_button)
sto(self.swap_title_author_button, self.manage_authors_button)
sto(self.manage_authors_button, self.paste_isbn_button)
tl.addItem(QSpacerItem(1, 1, QSizePolicy.Fixed, QSizePolicy.Expanding), tl.addItem(QSpacerItem(1, 1, QSizePolicy.Fixed, QSizePolicy.Expanding),
12, 1, 1 ,1) 12, 1, 1 ,1)
@ -708,7 +745,6 @@ class MetadataSingleDialogAlt1(MetadataSingleDialogBase): # {{{
gb = QGroupBox(_('Change cover'), tab1) gb = QGroupBox(_('Change cover'), tab1)
l = QGridLayout() l = QGridLayout()
gb.setLayout(l) gb.setLayout(l)
sto(self.swap_title_author_button, self.cover.buttons[0])
for i, b in enumerate(self.cover.buttons[:3]): for i, b in enumerate(self.cover.buttons[:3]):
l.addWidget(b, 0, i, 1, 1) l.addWidget(b, 0, i, 1, 1)
sto(b, self.cover.buttons[i+1]) sto(b, self.cover.buttons[i+1])
@ -738,6 +774,7 @@ class MetadataSingleDialogAlt2(MetadataSingleDialogBase): # {{{
cc_two_column = False cc_two_column = False
one_line_comments_toolbar = True one_line_comments_toolbar = True
use_toolbutton_for_config_metadata = False
def do_layout(self): def do_layout(self):
self.central_widget.clear() self.central_widget.clear()
@ -756,13 +793,11 @@ class MetadataSingleDialogAlt2(MetadataSingleDialogBase): # {{{
l.addWidget(gb, 0, 0, 1, 1) l.addWidget(gb, 0, 0, 1, 1)
gb.setLayout(tl) gb.setLayout(tl)
self.button_box.addButton(self.fetch_metadata_button, self.button_box_layout.insertWidget(1, self.fetch_metadata_button)
QDialogButtonBox.ActionRole) self.button_box_layout.insertWidget(2, self.config_metadata_button)
self.config_metadata_button.setToolButtonStyle(Qt.ToolButtonTextOnly) sto(self.button_box, self.fetch_metadata_button)
self.config_metadata_button.setText(_('Configure metadata downloading')) sto(self.fetch_metadata_button, self.config_metadata_button)
self.button_box.addButton(self.config_metadata_button, sto(self.config_metadata_button, self.title)
QDialogButtonBox.ActionRole)
sto(self.button_box, self.title)
def create_row(row, widget, tab_to, button=None, icon=None, span=1): def create_row(row, widget, tab_to, button=None, icon=None, span=1):
ql = BuddyLabel(widget) ql = BuddyLabel(widget)
@ -780,6 +815,8 @@ class MetadataSingleDialogAlt2(MetadataSingleDialogBase): # {{{
sto(widget, tab_to) sto(widget, tab_to)
tl.addWidget(self.swap_title_author_button, 0, 0, 2, 1) tl.addWidget(self.swap_title_author_button, 0, 0, 2, 1)
tl.addWidget(self.manage_authors_button, 2, 0, 2, 1)
tl.addWidget(self.paste_isbn_button, 11, 0, 1, 1)
create_row(0, self.title, self.title_sort, create_row(0, self.title, self.title_sort,
button=self.deduce_title_sort_button, span=2, button=self.deduce_title_sort_button, span=2,
@ -801,6 +838,9 @@ class MetadataSingleDialogAlt2(MetadataSingleDialogBase): # {{{
button=self.timestamp.clear_button, icon='trash.png') button=self.timestamp.clear_button, icon='trash.png')
create_row(11, self.identifiers, self.comments, create_row(11, self.identifiers, self.comments,
button=self.clear_identifiers_button, icon='trash.png') button=self.clear_identifiers_button, icon='trash.png')
sto(self.clear_identifiers_button, self.swap_title_author_button)
sto(self.swap_title_author_button, self.manage_authors_button)
sto(self.manage_authors_button, self.paste_isbn_button)
tl.addItem(QSpacerItem(1, 1, QSizePolicy.Fixed, QSizePolicy.Expanding), tl.addItem(QSpacerItem(1, 1, QSizePolicy.Fixed, QSizePolicy.Expanding),
12, 1, 1 ,1) 12, 1, 1 ,1)
@ -820,7 +860,7 @@ class MetadataSingleDialogAlt2(MetadataSingleDialogBase): # {{{
l.addWidget(gb, 0, 1, 1, 1) l.addWidget(gb, 0, 1, 1, 1)
sp = QSizePolicy() sp = QSizePolicy()
sp.setVerticalStretch(10) sp.setVerticalStretch(10)
sp.setHorizontalPolicy(QSizePolicy.Fixed) sp.setHorizontalPolicy(QSizePolicy.Minimum)
sp.setVerticalPolicy(QSizePolicy.Expanding) sp.setVerticalPolicy(QSizePolicy.Expanding)
gb.setSizePolicy(sp) gb.setSizePolicy(sp)
self.set_custom_metadata_tab_order() self.set_custom_metadata_tab_order()
@ -842,7 +882,7 @@ class MetadataSingleDialogAlt2(MetadataSingleDialogBase): # {{{
lb = QGridLayout() lb = QGridLayout()
gb.setLayout(lb) gb.setLayout(lb)
lb.addWidget(self.cover, 0, 0, 1, 3, alignment=Qt.AlignCenter) lb.addWidget(self.cover, 0, 0, 1, 3, alignment=Qt.AlignCenter)
sto(self.clear_identifiers_button, self.cover.buttons[0]) sto(self.manage_authors_button, self.cover.buttons[0])
for i, b in enumerate(self.cover.buttons[:3]): for i, b in enumerate(self.cover.buttons[:3]):
lb.addWidget(b, 1, i, 1, 1) lb.addWidget(b, 1, i, 1, 1)
sto(b, self.cover.buttons[i+1]) sto(b, self.cover.buttons[i+1])

View File

@ -27,7 +27,7 @@ from calibre.utils.logging import default_log as log
from calibre.utils.magick.draw import thumbnail from calibre.utils.magick.draw import thumbnail
from calibre.utils.zipfile import ZipFile, ZipInfo from calibre.utils.zipfile import ZipFile, ZipInfo
FIELDS = ['all', 'title', 'author_sort', 'authors', 'comments', FIELDS = ['all', 'title', 'title_sort', 'author_sort', 'authors', 'comments',
'cover', 'formats','id', 'isbn', 'ondevice', 'pubdate', 'publisher', 'cover', 'formats','id', 'isbn', 'ondevice', 'pubdate', 'publisher',
'rating', 'series_index', 'series', 'size', 'tags', 'timestamp', 'uuid'] 'rating', 'series_index', 'series', 'size', 'tags', 'timestamp', 'uuid']
@ -66,7 +66,7 @@ class CSV_XML(CatalogPlugin): # {{{
dest = 'sort_by', dest = 'sort_by',
action = None, action = None,
help = _('Output field to sort on.\n' help = _('Output field to sort on.\n'
'Available fields: author_sort, id, rating, size, timestamp, title.\n' 'Available fields: author_sort, id, rating, size, timestamp, title_sort\n'
"Default: '%default'\n" "Default: '%default'\n"
"Applies to: CSV, XML output formats"))] "Applies to: CSV, XML output formats"))]
@ -76,7 +76,7 @@ class CSV_XML(CatalogPlugin): # {{{
if opts.verbose: if opts.verbose:
opts_dict = vars(opts) opts_dict = vars(opts)
log("%s(): Generating %s" % (self.name,self.fmt)) log("%s(): Generating %s" % (self.name,self.fmt.upper()))
if opts.connected_device['is_device_connected']: if opts.connected_device['is_device_connected']:
log(" connected_device: %s" % opts.connected_device['name']) log(" connected_device: %s" % opts.connected_device['name'])
if opts_dict['search_text']: if opts_dict['search_text']:
@ -126,8 +126,11 @@ class CSV_XML(CatalogPlugin): # {{{
for field in fields: for field in fields:
if field.startswith('#'): if field.startswith('#'):
item = db.get_field(entry['id'],field,index_is_id=True) item = db.get_field(entry['id'],field,index_is_id=True)
elif field == 'title_sort':
item = entry['sort']
else: else:
item = entry[field] item = entry[field]
if item is None: if item is None:
outstr.append('""') outstr.append('""')
continue continue
@ -167,7 +170,7 @@ class CSV_XML(CatalogPlugin): # {{{
item = getattr(E, field.replace('#','_'))(val) item = getattr(E, field.replace('#','_'))(val)
record.append(item) record.append(item)
for field in ('id', 'uuid', 'title', 'publisher', 'rating', 'size', for field in ('id', 'uuid', 'publisher', 'rating', 'size',
'isbn','ondevice'): 'isbn','ondevice'):
if field in fields: if field in fields:
val = r[field] val = r[field]
@ -178,6 +181,10 @@ class CSV_XML(CatalogPlugin): # {{{
item = getattr(E, field)(val) item = getattr(E, field)(val)
record.append(item) record.append(item)
if 'title' in fields:
title = E.title(r['title'], sort=r['sort'])
record.append(title)
if 'authors' in fields: if 'authors' in fields:
aus = E.authors(sort=r['author_sort']) aus = E.authors(sort=r['author_sort'])
for au in r['authors']: for au in r['authors']:

View File

@ -3056,7 +3056,7 @@ class LibraryDatabase2(LibraryDatabase, SchemaUpgrade, CustomColumns):
''' '''
if prefix is None: if prefix is None:
prefix = self.library_path prefix = self.library_path
FIELDS = set(['title', 'authors', 'author_sort', 'publisher', 'rating', FIELDS = set(['title', 'sort', 'authors', 'author_sort', 'publisher', 'rating',
'timestamp', 'size', 'tags', 'comments', 'series', 'series_index', 'timestamp', 'size', 'tags', 'comments', 'series', 'series_index',
'uuid', 'pubdate', 'last_modified', 'identifiers']) 'uuid', 'pubdate', 'last_modified', 'identifiers'])
for x in self.custom_column_num_map: for x in self.custom_column_num_map:

View File

@ -10,6 +10,7 @@ License: http://www.opensource.org/licenses/mit-license.php
import re import re
from calibre.utils.icu import capitalize from calibre.utils.icu import capitalize
from calibre.utils.config import prefs
__all__ = ['titlecase'] __all__ = ['titlecase']
__version__ = '0.5' __version__ = '0.5'
@ -67,11 +68,12 @@ def titlecase(text):
line.append(icu_lower(word)) line.append(icu_lower(word))
continue continue
match = MAC_MC.match(word) if prefs['language'].lower().startswith('en'):
if match and not match.group(2)[:3] in ('hin', 'ht'): match = MAC_MC.match(word)
line.append("%s%s" % (capitalize(match.group(1)), if match and not match.group(2)[:3] in ('hin', 'ht'):
capitalize(match.group(2)))) line.append("%s%s" % (capitalize(match.group(1)),
continue capitalize(match.group(2))))
continue
hyphenated = [] hyphenated = []
for item in word.split('-'): for item in word.split('-'):