mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-07-09 03:04:10 -04:00
Pull from trunk
This commit is contained in:
commit
b3e1025c7c
@ -571,9 +571,6 @@ Condition 08195201-0797-932C-4B51-E5EF9D1D41BD -active Yes -parent 710F2507-2557
|
||||
Condition 2E18F4AE-F1BB-5C62-2900-73A576A49261 -active Yes -parent 710F2507-2557-652D-EA55-440D710EFDFA -title {String Is Condition} -component StringIsCondition -TreeObject::id 2E18F4AE-F1BB-5C62-2900-73A576A49261
|
||||
InstallComponent 21B897C4-24BE-70D1-58EA-DE78EFA60719 -setup Install -type action -conditions 76FA3CA2-1F09-75C5-C6CF-72719A8EC4A5 -title {Message Box} -component MessageBox -command insert -active Yes -parent 8A7FD0C2-F053-8764-F204-4BAE71E05708
|
||||
Condition 76FA3CA2-1F09-75C5-C6CF-72719A8EC4A5 -active Yes -parent 21B897C4-24BE-70D1-58EA-DE78EFA60719 -title {String Is Condition} -component StringIsCondition -TreeObject::id 76FA3CA2-1F09-75C5-C6CF-72719A8EC4A5
|
||||
InstallComponent 5D20DD8D-064A-9922-29E1-A7FABEF3666A -setup Install -type action -conditions {E5D227F7-E549-EFA9-1781-EFA6C5EEEC5C A8856922-E6C1-160B-E55C-5C1806A89136} -title {Launch Application Checkbutton} -component AddWidget -command insert -active Yes -parent 8A7FD0C2-F053-8764-F204-4BAE71E05708
|
||||
Condition E5D227F7-E549-EFA9-1781-EFA6C5EEEC5C -active Yes -parent 5D20DD8D-064A-9922-29E1-A7FABEF3666A -title {File Exists Condition} -component FileExistsCondition -TreeObject::id E5D227F7-E549-EFA9-1781-EFA6C5EEEC5C
|
||||
Condition A8856922-E6C1-160B-E55C-5C1806A89136 -active Yes -parent 5D20DD8D-064A-9922-29E1-A7FABEF3666A -title {String Is Condition} -component StringIsCondition -TreeObject::id A8856922-E6C1-160B-E55C-5C1806A89136
|
||||
InstallComponent 940F7FED-7D20-7264-3BF9-ED78205A76B3 -setup Install -type action -conditions {96440B8B-C6D0-FCCA-6D3C-7ECE1C304CC0 FBA33088-C809-DD6B-D337-EADBF1CEE966} -title {Desktop Shortcut Checkbutton} -component AddWidget -command insert -active Yes -parent 8A7FD0C2-F053-8764-F204-4BAE71E05708
|
||||
Condition 96440B8B-C6D0-FCCA-6D3C-7ECE1C304CC0 -active Yes -parent 940F7FED-7D20-7264-3BF9-ED78205A76B3 -title {File Exists Condition} -component FileExistsCondition -TreeObject::id 96440B8B-C6D0-FCCA-6D3C-7ECE1C304CC0
|
||||
Condition FBA33088-C809-DD6B-D337-EADBF1CEE966 -active Yes -parent 940F7FED-7D20-7264-3BF9-ED78205A76B3 -title {String Is Condition} -component StringIsCondition -TreeObject::id FBA33088-C809-DD6B-D337-EADBF1CEE966
|
||||
@ -630,7 +627,7 @@ Condition 03FA7EEF-F626-B69A-09C6-0AA7A54EE9E7 -active Yes -parent E32519F3-A540
|
||||
InstallComponent D86BBA5C-4903-33BA-59F8-4266A3D45896 -setup Install -type action -conditions {C4C0A903-CF2A-D25A-27AB-A64219FB7E70 5EC7056B-6F90-311E-2C6F-76E96164CFFD} -title {Install Quick Launch Shortcut} -component InstallWindowsShortcut -command insert -active Yes -parent 28BAE662-E103-4E3F-D298-C8FBA36361FC
|
||||
Condition C4C0A903-CF2A-D25A-27AB-A64219FB7E70 -active Yes -parent D86BBA5C-4903-33BA-59F8-4266A3D45896 -title {String Is Condition} -component StringIsCondition -TreeObject::id C4C0A903-CF2A-D25A-27AB-A64219FB7E70
|
||||
Condition 5EC7056B-6F90-311E-2C6F-76E96164CFFD -active Yes -parent D86BBA5C-4903-33BA-59F8-4266A3D45896 -title {File Exists Condition} -component FileExistsCondition -TreeObject::id 5EC7056B-6F90-311E-2C6F-76E96164CFFD
|
||||
InstallComponent 2A230259-3A6F-8669-8B8B-23C3E7C1BFC2 -setup Install -type action -conditions {4E5FC4FE-5D37-B216-CFFE-E046A2D6321E E560F3A1-208D-2B4F-2C87-E08595F8E1CD 9C1E4BD9-066D-ABCE-28D0-9E194B9F8475} -title {Launch Application} -component ExecuteExternalProgram -command insert -active Yes -parent 28BAE662-E103-4E3F-D298-C8FBA36361FC
|
||||
InstallComponent 2A230259-3A6F-8669-8B8B-23C3E7C1BFC2 -setup Install -type action -conditions {4E5FC4FE-5D37-B216-CFFE-E046A2D6321E E560F3A1-208D-2B4F-2C87-E08595F8E1CD 9C1E4BD9-066D-ABCE-28D0-9E194B9F8475} -title {Launch Application} -component ExecuteExternalProgram -command insert -active No -parent 28BAE662-E103-4E3F-D298-C8FBA36361FC
|
||||
Condition 4E5FC4FE-5D37-B216-CFFE-E046A2D6321E -active Yes -parent 2A230259-3A6F-8669-8B8B-23C3E7C1BFC2 -title {String Is Condition} -component StringIsCondition -TreeObject::id 4E5FC4FE-5D37-B216-CFFE-E046A2D6321E
|
||||
Condition E560F3A1-208D-2B4F-2C87-E08595F8E1CD -active Yes -parent 2A230259-3A6F-8669-8B8B-23C3E7C1BFC2 -title {String Is Condition} -component StringIsCondition -TreeObject::id E560F3A1-208D-2B4F-2C87-E08595F8E1CD
|
||||
Condition 9C1E4BD9-066D-ABCE-28D0-9E194B9F8475 -active Yes -parent 2A230259-3A6F-8669-8B8B-23C3E7C1BFC2 -title {File Exists Condition} -component FileExistsCondition -TreeObject::id 9C1E4BD9-066D-ABCE-28D0-9E194B9F8475
|
||||
@ -802,6 +799,9 @@ CreateQuickLaunchShortcut
|
||||
28FDA3F4-B799-901F-8A27-AA04F0C022AB,Title,subst
|
||||
1
|
||||
|
||||
2A230259-3A6F-8669-8B8B-23C3E7C1BFC2,Active
|
||||
No
|
||||
|
||||
2A230259-3A6F-8669-8B8B-23C3E7C1BFC2,Conditions
|
||||
{3 conditions}
|
||||
|
||||
@ -976,27 +976,6 @@ disabled
|
||||
5C66451D-6042-DBDE-0D8C-31156EE244AD,Widget
|
||||
{Back Button;Next Button}
|
||||
|
||||
5D20DD8D-064A-9922-29E1-A7FABEF3666A,Background
|
||||
white
|
||||
|
||||
5D20DD8D-064A-9922-29E1-A7FABEF3666A,Conditions
|
||||
{2 conditions}
|
||||
|
||||
5D20DD8D-064A-9922-29E1-A7FABEF3666A,Text,subst
|
||||
1
|
||||
|
||||
5D20DD8D-064A-9922-29E1-A7FABEF3666A,Type
|
||||
checkbutton
|
||||
|
||||
5D20DD8D-064A-9922-29E1-A7FABEF3666A,VirtualText
|
||||
LaunchApplication
|
||||
|
||||
5D20DD8D-064A-9922-29E1-A7FABEF3666A,X
|
||||
185
|
||||
|
||||
5D20DD8D-064A-9922-29E1-A7FABEF3666A,Y
|
||||
130
|
||||
|
||||
5EC7056B-6F90-311E-2C6F-76E96164CFFD,CheckCondition
|
||||
{Before Action is Executed}
|
||||
|
||||
@ -1408,15 +1387,6 @@ disabled
|
||||
A75C97CC-01AC-C12A-D663-A54E3257F11B,Widget
|
||||
{Back Button;Next Button}
|
||||
|
||||
A8856922-E6C1-160B-E55C-5C1806A89136,CheckCondition
|
||||
{Before Action is Executed}
|
||||
|
||||
A8856922-E6C1-160B-E55C-5C1806A89136,Operator
|
||||
false
|
||||
|
||||
A8856922-E6C1-160B-E55C-5C1806A89136,String
|
||||
<%InstallStopped%>
|
||||
|
||||
AAEC34E6-7F02-18F2-30BB-744738192A3B,Conditions
|
||||
{2 conditions}
|
||||
|
||||
@ -1730,12 +1700,6 @@ disabled
|
||||
E5CBB018-A89D-3145-CFF5-CFC3B62BEA97,Widget
|
||||
{NextButton; CancelButton}
|
||||
|
||||
E5D227F7-E549-EFA9-1781-EFA6C5EEEC5C,CheckCondition
|
||||
{Before Action is Executed}
|
||||
|
||||
E5D227F7-E549-EFA9-1781-EFA6C5EEEC5C,Filename
|
||||
<%ProgramExecutable%>
|
||||
|
||||
E611105F-DC85-9E20-4F7B-E63C54E5DF06,Message,subst
|
||||
1
|
||||
|
||||
@ -2340,9 +2304,6 @@ Please make sure that calibre is not running, as this will cause the install to
|
||||
48E8A9D6-B57E-C506-680D-898C65DD2A1B,Title
|
||||
<%InstallApplicationText%>
|
||||
|
||||
5D20DD8D-064A-9922-29E1-A7FABEF3666A,Text
|
||||
<%LaunchApplicationText%>
|
||||
|
||||
64B8D0F3-4B11-DA22-D6E7-7248872D5FA7,Message
|
||||
<%UninstallStartupText%>
|
||||
|
||||
@ -2356,7 +2317,7 @@ Please make sure that calibre is not running, as this will cause the install to
|
||||
{<%AppName%> Installation complete}
|
||||
|
||||
8A7FD0C2-F053-8764-F204-4BAE71E05708,Message
|
||||
{Installation of <%AppName%> was successful. Click Finish to quit the installer.}
|
||||
{Installation of <%AppName%> was successful. Click Finish to quit the installer. <%AppName%> can be launched from the start menu.}
|
||||
|
||||
940F7FED-7D20-7264-3BF9-ED78205A76B3,Text
|
||||
<%CreateDesktopShortcutText%>
|
||||
|
@ -33,7 +33,7 @@ def get_metadata(stream):
|
||||
covers.sort(cmp=lambda x, y:cmp(len(x[0]), len(y[0])), reverse=True)
|
||||
idx = 0
|
||||
if len(covers) > 1:
|
||||
if covers[1][1] == covers[1][0]+'-standard':
|
||||
if covers[1][1] == covers[0][1]+'-standard':
|
||||
idx = 1
|
||||
mi.cover_data = ('jpg', covers[idx][0])
|
||||
return mi
|
||||
|
@ -108,7 +108,7 @@ def set_metadata(stream, mi, stream_type='lrf'):
|
||||
|
||||
|
||||
def metadata_from_filename(name, pat=None):
|
||||
name = os.path.splitext(name)[0]
|
||||
name = name.rpartition('.')[0]
|
||||
mi = MetaInformation(None, None)
|
||||
if pat is None:
|
||||
pat = re.compile(prefs.get('filename_pattern'))
|
||||
|
@ -218,7 +218,7 @@ class Serializer(object):
|
||||
for elem in item.data.find(XHTML('body')):
|
||||
self.serialize_elem(elem, item)
|
||||
#buffer.write('</mbp:section>')
|
||||
buffer.write('</mbp:pagebreak>')
|
||||
buffer.write('<mbp:pagebreak/>')
|
||||
|
||||
def serialize_elem(self, elem, item, nsrmap=NSRMAP):
|
||||
buffer = self.buffer
|
||||
|
@ -69,6 +69,8 @@ def _config():
|
||||
'clicked'))
|
||||
c.add_opt('show_donate_button', default=True,
|
||||
help='Show donation button')
|
||||
c.add_opt('asked_library_thing_password', default=False,
|
||||
help='Asked library thing password at least once.')
|
||||
return ConfigProxy(c)
|
||||
|
||||
config = _config()
|
||||
|
@ -25,24 +25,48 @@ from calibre import islinux
|
||||
from calibre.ebooks.metadata.meta import get_metadata
|
||||
from calibre.utils.config import prefs
|
||||
from calibre.customize.ui import run_plugins_on_import
|
||||
from calibre.gui2 import config as gui_conf
|
||||
|
||||
class CoverFetcher(QThread):
|
||||
|
||||
def __init__(self, username, password, isbn, timeout):
|
||||
self.username = username
|
||||
self.password = password
|
||||
def __init__(self, username, password, isbn, timeout, title, author):
|
||||
self.username = username.strip() if username else username
|
||||
self.password = password.strip() if password else password
|
||||
self.timeout = timeout
|
||||
self.isbn = isbn
|
||||
self.title = title
|
||||
self.needs_isbn = False
|
||||
self.author = author
|
||||
QThread.__init__(self)
|
||||
self.exception = self.traceback = self.cover_data = None
|
||||
|
||||
def run(self):
|
||||
try:
|
||||
login(self.username, self.password, force=False)
|
||||
if not self.isbn:
|
||||
from calibre.ebooks.metadata.fetch import search
|
||||
if not self.title:
|
||||
self.needs_isbn = True
|
||||
return
|
||||
au = self.author if self.author else None
|
||||
key = prefs['isbndb_com_key']
|
||||
if not key:
|
||||
key = None
|
||||
results = search(title=self.title, author=au,
|
||||
isbndb_key=key)[0]
|
||||
results = sorted([x.isbn for x in results if x.isbn],
|
||||
cmp=lambda x,y:cmp(len(x),len(y)), reverse=True)
|
||||
if not results:
|
||||
self.needs_isbn = True
|
||||
return
|
||||
self.isbn = results[0]
|
||||
|
||||
if self.username and self.password:
|
||||
login(self.username, self.password, force=False)
|
||||
self.cover_data = cover_from_isbn(self.isbn, timeout=self.timeout)[0]
|
||||
except Exception, e:
|
||||
self.exception = e
|
||||
self.traceback = traceback.format_exc()
|
||||
print self.traceback
|
||||
|
||||
|
||||
|
||||
@ -64,6 +88,8 @@ class AuthorCompleter(QCompleter):
|
||||
|
||||
class MetadataSingleDialog(ResizableDialog, Ui_MetadataSingleDialog):
|
||||
|
||||
COVER_FETCH_TIMEOUT = 240 # seconds
|
||||
|
||||
def do_reset_cover(self, *args):
|
||||
pix = QPixmap(':/images/book.svg')
|
||||
self.cover.setPixmap(pix)
|
||||
@ -345,36 +371,39 @@ class MetadataSingleDialog(ResizableDialog, Ui_MetadataSingleDialog):
|
||||
|
||||
def lt_password_dialog(self):
|
||||
return PasswordDialog(self, 'LibraryThing account',
|
||||
_('<p>Enter your username and password for <b>LibraryThing.com</b>. <br/>If you do not have one, you can <a href=\'http://www.librarything.com\'>register</a> for free!.</p>'))
|
||||
_('<p>Enter your username and password for '
|
||||
'<b>LibraryThing.com</b>. This is <b>optional</b>. It will '
|
||||
'make fetching of covers faster and more reliable.<br/>If '
|
||||
'you do not have an account, you can '
|
||||
'<a href=\'http://www.librarything.com\'>register</a> for '
|
||||
'free.</p>'))
|
||||
|
||||
def change_password(self):
|
||||
d = self.lt_password_dialog()
|
||||
d.exec_()
|
||||
|
||||
def fetch_cover(self):
|
||||
isbn = qstring_to_unicode(self.isbn.text())
|
||||
if isbn:
|
||||
d = self.lt_password_dialog()
|
||||
if not d.username() or not d.password():
|
||||
d.exec_()
|
||||
if d.result() != PasswordDialog.Accepted:
|
||||
return
|
||||
self.fetch_cover_button.setEnabled(False)
|
||||
self.setCursor(Qt.WaitCursor)
|
||||
self.cover_fetcher = CoverFetcher(d.username(), d.password(), isbn,
|
||||
self.timeout)
|
||||
self.cover_fetcher.start()
|
||||
self._hangcheck = QTimer(self)
|
||||
self.connect(self._hangcheck, SIGNAL('timeout()'), self.hangcheck)
|
||||
self.cf_start_time = time.time()
|
||||
self.pi.start(_('Downloading cover...'))
|
||||
self._hangcheck.start(100)
|
||||
else:
|
||||
error_dialog(self, _('Cannot fetch cover'),
|
||||
_('You must specify the ISBN identifier for this book.')).exec_()
|
||||
isbn = unicode(self.isbn.text()).strip()
|
||||
d = self.lt_password_dialog()
|
||||
if not gui_conf['asked_library_thing_password'] and \
|
||||
(not d.username() or not d.password()):
|
||||
d.exec_()
|
||||
gui_conf['asked_library_thing_password'] = True
|
||||
self.fetch_cover_button.setEnabled(False)
|
||||
self.setCursor(Qt.WaitCursor)
|
||||
title, author = map(unicode, (self.title.text(), self.authors.text()))
|
||||
self.cover_fetcher = CoverFetcher(d.username(), d.password(), isbn,
|
||||
self.timeout, title, author)
|
||||
self.cover_fetcher.start()
|
||||
self._hangcheck = QTimer(self)
|
||||
self.connect(self._hangcheck, SIGNAL('timeout()'), self.hangcheck)
|
||||
self.cf_start_time = time.time()
|
||||
self.pi.start(_('Downloading cover...'))
|
||||
self._hangcheck.start(100)
|
||||
|
||||
def hangcheck(self):
|
||||
if not (self.cover_fetcher.isFinished() or time.time()-self.cf_start_time > 150):
|
||||
if not self.cover_fetcher.isFinished() and \
|
||||
time.time()-self.cf_start_time < self.COVER_FETCH_TIMEOUT:
|
||||
return
|
||||
|
||||
self._hangcheck.stop()
|
||||
@ -385,6 +414,11 @@ class MetadataSingleDialog(ResizableDialog, Ui_MetadataSingleDialog):
|
||||
_('<b>Could not fetch cover.</b><br/>')+
|
||||
_('The download timed out.')).exec_()
|
||||
return
|
||||
if self.cover_fetcher.needs_isbn:
|
||||
error_dialog(self, _('Cannot fetch cover'),
|
||||
_('Could not find cover for this book. Try '
|
||||
'specifying the ISBN first.')).exec_()
|
||||
return
|
||||
if self.cover_fetcher.exception is not None:
|
||||
err = self.cover_fetcher.exception
|
||||
error_dialog(self, _('Cannot fetch cover'),
|
||||
|
@ -1,7 +1,8 @@
|
||||
<ui version="4.0" >
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>Dialog</class>
|
||||
<widget class="QDialog" name="Dialog" >
|
||||
<property name="geometry" >
|
||||
<widget class="QDialog" name="Dialog">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
@ -9,66 +10,70 @@
|
||||
<height>209</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle" >
|
||||
<property name="windowTitle">
|
||||
<string>Password needed</string>
|
||||
</property>
|
||||
<property name="windowIcon" >
|
||||
<iconset resource="../images.qrc" >:/images/mimetypes/unknown.svg</iconset>
|
||||
<property name="windowIcon">
|
||||
<iconset resource="../images.qrc">
|
||||
<normaloff>:/images/mimetypes/unknown.svg</normaloff>:/images/mimetypes/unknown.svg</iconset>
|
||||
</property>
|
||||
<layout class="QGridLayout" >
|
||||
<item row="0" column="1" >
|
||||
<widget class="QLabel" name="msg" >
|
||||
<property name="text" >
|
||||
<layout class="QGridLayout">
|
||||
<item row="0" column="1">
|
||||
<widget class="QLabel" name="msg">
|
||||
<property name="text">
|
||||
<string>TextLabel</string>
|
||||
</property>
|
||||
<property name="openExternalLinks" >
|
||||
<property name="wordWrap">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="openExternalLinks">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="0" >
|
||||
<widget class="QLabel" name="label" >
|
||||
<property name="text" >
|
||||
<item row="1" column="0">
|
||||
<widget class="QLabel" name="label">
|
||||
<property name="text">
|
||||
<string>&Username:</string>
|
||||
</property>
|
||||
<property name="buddy" >
|
||||
<property name="buddy">
|
||||
<cstring>gui_username</cstring>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="1" >
|
||||
<widget class="QLineEdit" name="gui_username" />
|
||||
<item row="1" column="1">
|
||||
<widget class="QLineEdit" name="gui_username"/>
|
||||
</item>
|
||||
<item row="2" column="0" >
|
||||
<widget class="QLabel" name="label_2" >
|
||||
<property name="text" >
|
||||
<item row="2" column="0">
|
||||
<widget class="QLabel" name="label_2">
|
||||
<property name="text">
|
||||
<string>&Password:</string>
|
||||
</property>
|
||||
<property name="buddy" >
|
||||
<property name="buddy">
|
||||
<cstring>gui_password</cstring>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="1" >
|
||||
<widget class="QLineEdit" name="gui_password" >
|
||||
<property name="echoMode" >
|
||||
<item row="2" column="1">
|
||||
<widget class="QLineEdit" name="gui_password">
|
||||
<property name="echoMode">
|
||||
<enum>QLineEdit::Password</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="4" column="1" >
|
||||
<widget class="QDialogButtonBox" name="buttonBox" >
|
||||
<property name="orientation" >
|
||||
<item row="4" column="1">
|
||||
<widget class="QDialogButtonBox" name="buttonBox">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="standardButtons" >
|
||||
<set>QDialogButtonBox::Cancel|QDialogButtonBox::NoButton|QDialogButtonBox::Ok</set>
|
||||
<property name="standardButtons">
|
||||
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="1" >
|
||||
<widget class="QCheckBox" name="show_password" >
|
||||
<property name="text" >
|
||||
<item row="3" column="1">
|
||||
<widget class="QCheckBox" name="show_password">
|
||||
<property name="text">
|
||||
<string>&Show password</string>
|
||||
</property>
|
||||
</widget>
|
||||
@ -76,7 +81,7 @@
|
||||
</layout>
|
||||
</widget>
|
||||
<resources>
|
||||
<include location="../images.qrc" />
|
||||
<include location="../images.qrc"/>
|
||||
</resources>
|
||||
<connections>
|
||||
<connection>
|
||||
@ -85,11 +90,11 @@
|
||||
<receiver>Dialog</receiver>
|
||||
<slot>accept()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel" >
|
||||
<hint type="sourcelabel">
|
||||
<x>248</x>
|
||||
<y>254</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel" >
|
||||
<hint type="destinationlabel">
|
||||
<x>157</x>
|
||||
<y>274</y>
|
||||
</hint>
|
||||
@ -101,11 +106,11 @@
|
||||
<receiver>Dialog</receiver>
|
||||
<slot>reject()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel" >
|
||||
<hint type="sourcelabel">
|
||||
<x>316</x>
|
||||
<y>260</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel" >
|
||||
<hint type="destinationlabel">
|
||||
<x>286</x>
|
||||
<y>274</y>
|
||||
</hint>
|
||||
|
BIN
src/calibre/gui2/images/news/hna.png
Normal file
BIN
src/calibre/gui2/images/news/hna.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 827 B |
BIN
src/calibre/gui2/images/news/nzz_ger.png
Normal file
BIN
src/calibre/gui2/images/news/nzz_ger.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 811 B |
@ -243,9 +243,22 @@ class LibraryServer(object):
|
||||
raise cherrypy.HTTPError(400, '%s is not a valid sort field'%field)
|
||||
cmpf = cmp if field in ('rating', 'size', 'timestamp') else \
|
||||
lambda x, y: cmp(x.lower() if x else '', y.lower() if y else '')
|
||||
field = FIELD_MAP[field]
|
||||
getter = operator.itemgetter(field)
|
||||
items.sort(cmp=lambda x, y: cmpf(getter(x), getter(y)), reverse=not order)
|
||||
if field == 'series':
|
||||
items.sort(cmp=self.seriescmp, reverse=not order)
|
||||
else:
|
||||
field = FIELD_MAP[field]
|
||||
getter = operator.itemgetter(field)
|
||||
items.sort(cmp=lambda x, y: cmpf(getter(x), getter(y)), reverse=not order)
|
||||
|
||||
def seriescmp(self, x, y):
|
||||
si = FIELD_MAP['series']
|
||||
try:
|
||||
ans = cmp(x[si].lower(), y[si].lower())
|
||||
except AttributeError: # Some entries may be None
|
||||
ans = cmp(x[si], y[si])
|
||||
if ans != 0: return ans
|
||||
return cmp(x[FIELD_MAP['series_index']], y[FIELD_MAP['series_index']])
|
||||
|
||||
|
||||
def last_modified(self, updated):
|
||||
lm = updated.strftime('day, %d month %Y %H:%M:%S GMT')
|
||||
|
@ -39,7 +39,7 @@ recipe_modules = ['recipe_' + r for r in (
|
||||
'nacional_cro', '24sata', 'dnevni_avaz', 'glas_srpske', '24sata_rs',
|
||||
'krstarica', 'krstarica_en', 'tanjug', 'laprensa_ni', 'azstarnet',
|
||||
'corriere_della_sera_it', 'corriere_della_sera_en', 'msdnmag_en',
|
||||
'moneynews',
|
||||
'moneynews', 'der_standard', 'diepresse', 'nzz_ger', 'hna',
|
||||
)]
|
||||
|
||||
import re, imp, inspect, time, os
|
||||
|
42
src/calibre/web/feeds/recipes/recipe_der_standard.py
Normal file
42
src/calibre/web/feeds/recipes/recipe_der_standard.py
Normal file
@ -0,0 +1,42 @@
|
||||
|
||||
''' http://www.derstandard.at - Austrian Newspaper '''
|
||||
import re
|
||||
from calibre.web.feeds.news import BasicNewsRecipe
|
||||
|
||||
class DerStandardRecipe(BasicNewsRecipe):
|
||||
title = u'derStandard'
|
||||
__author__ = 'Gerhard Aigner'
|
||||
|
||||
oldest_article = 1
|
||||
max_articles_per_feed = 100
|
||||
feeds = [(u'International', u'http://derstandard.at/?page=rss&ressort=internationalpolitik'),
|
||||
(u'Inland', u'http://derstandard.at/?page=rss&ressort=innenpolitik'),
|
||||
(u'Wirtschaft', u'http://derstandard.at/?page=rss&ressort=investor'),
|
||||
(u'Web', u'http://derstandard.at/?page=rss&ressort=webstandard'),
|
||||
(u'Sport', u'http://derstandard.at/?page=rss&ressort=sport'),
|
||||
(u'Panorama', u'http://derstandard.at/?page=rss&ressort=panorama'),
|
||||
(u'Etat', u'http://derstandard.at/?page=rss&ressort=etat'),
|
||||
(u'Kultur', u'http://derstandard.at/?page=rss&ressort=kultur'),
|
||||
(u'Wissenschaft', u'http://derstandard.at/?page=rss&ressort=wissenschaft'),
|
||||
(u'Gesundheit', u'http://derstandard.at/?page=rss&ressort=gesundheit'),
|
||||
(u'Bildung', u'http://derstandard.at/?page=rss&ressort=subildung')]
|
||||
|
||||
encoding = 'utf-8'
|
||||
language = _('German')
|
||||
recursions = 0
|
||||
remove_tags = [dict(name='div'), dict(name='a'), dict(name='link'), dict(name='meta'),
|
||||
dict(name='form',attrs={'name':'sitesearch'}), dict(name='hr')]
|
||||
preprocess_regexps = [
|
||||
(re.compile(r'\[[\d*]\]', re.DOTALL|re.IGNORECASE), lambda match: ''),
|
||||
(re.compile(r'bgcolor="#\w{3,6}"', re.DOTALL|re.IGNORECASE), lambda match: '')
|
||||
]
|
||||
|
||||
def print_version(self, url):
|
||||
return url.replace('?id=', 'txt/?id=')
|
||||
|
||||
def get_article_url(self, article):
|
||||
'''if the article links to a index page (ressort) or a picture gallery
|
||||
(ansichtssache), don't add it'''
|
||||
if (article.link.count('ressort') > 0 or article.title.lower().count('ansichtssache') > 0):
|
||||
return None
|
||||
return article.link
|
40
src/calibre/web/feeds/recipes/recipe_diepresse.py
Normal file
40
src/calibre/web/feeds/recipes/recipe_diepresse.py
Normal file
@ -0,0 +1,40 @@
|
||||
import re
|
||||
|
||||
from calibre.web.feeds.news import BasicNewsRecipe
|
||||
|
||||
class DiePresseRecipe(BasicNewsRecipe):
|
||||
title = u'diePresse'
|
||||
oldest_article = 1
|
||||
max_articles_per_feed = 100
|
||||
recursions = 0
|
||||
language = _('German')
|
||||
__author__ = 'Gerhard Aigner'
|
||||
|
||||
preprocess_regexps = [
|
||||
(re.compile(r'Textversion', re.DOTALL), lambda match: ''),
|
||||
]
|
||||
remove_tags = [dict(name='hr'),
|
||||
dict(name='br'),
|
||||
dict(name='small'),
|
||||
dict(name='img'),
|
||||
dict(name='div', attrs={'class':'textnavi'}),
|
||||
dict(name='h1', attrs={'class':'titel'}),
|
||||
dict(name='a', attrs={'class':'print'}),
|
||||
dict(name='div', attrs={'class':'hline'})]
|
||||
feeds = [(u'Politik', u'http://diepresse.com/rss/Politik'),
|
||||
(u'Wirtschaft', u'http://diepresse.com/rss/Wirtschaft'),
|
||||
(u'Europa', u'http://diepresse.com/rss/EU'),
|
||||
(u'Panorama', u'http://diepresse.com/rss/Panorama'),
|
||||
(u'Sport', u'http://diepresse.com/rss/Sport'),
|
||||
(u'Kultur', u'http://diepresse.com/rss/Kultur'),
|
||||
(u'Leben', u'http://diepresse.com/rss/Leben'),
|
||||
(u'Tech', u'http://diepresse.com/rss/Tech'),
|
||||
(u'Science', u'http://diepresse.com/rss/Science'),
|
||||
(u'Bildung', u'http://diepresse.com/rss/Bildung'),
|
||||
(u'Gesundheit', u'http://diepresse.com/rss/Gesundheit'),
|
||||
(u'Recht', u'http://diepresse.com/rss/Recht'),
|
||||
(u'Spectrum', u'http://diepresse.com/rss/Spectrum'),
|
||||
(u'Meinung', u'http://diepresse.com/rss/Meinung')]
|
||||
|
||||
def print_version(self, url):
|
||||
return url.replace('home','text/home')
|
40
src/calibre/web/feeds/recipes/recipe_hna.py
Normal file
40
src/calibre/web/feeds/recipes/recipe_hna.py
Normal file
@ -0,0 +1,40 @@
|
||||
__license__ = 'GPL v3'
|
||||
__copyright__ = '2008, Kovid Goyal <kovid at kovidgoyal.net>'
|
||||
|
||||
'''
|
||||
Fetch Hessisch Niedersachsische Allgemeine.
|
||||
'''
|
||||
|
||||
from calibre.web.feeds.news import BasicNewsRecipe
|
||||
|
||||
|
||||
class hnaDe(BasicNewsRecipe):
|
||||
|
||||
title = 'HNA'
|
||||
description = 'local news from Hessen/Germany'
|
||||
__author__ = 'Oliver Niesner'
|
||||
use_embedded_content = False
|
||||
language = _('German')
|
||||
use_embedded_content = False
|
||||
timefmt = ' [%d %b %Y]'
|
||||
max_articles_per_feed = 40
|
||||
no_stylesheets = True
|
||||
encoding = 'iso-8859-1'
|
||||
|
||||
remove_tags = [dict(id='topnav'),
|
||||
dict(id='nav_main'),
|
||||
dict(id='suchen'),
|
||||
dict(id=''),
|
||||
dict(name='span'),
|
||||
dict(name='ul', attrs={'class':'linklist'}),
|
||||
dict(name='a', attrs={'href':'#'}),
|
||||
dict(name='p', attrs={'class':'breadcrumb'}),
|
||||
dict(name='p', attrs={'class':'h5'})]
|
||||
#remove_tags_after = [dict(name='div', attrs={'class':'rahmenbreaking'})]
|
||||
remove_tags_after = [dict(name='a', attrs={'href':'#'})]
|
||||
|
||||
feeds = [ ('hna_soehre', 'http://feeds2.feedburner.com/hna/soehre'),
|
||||
('hna_kassel', 'http://feeds2.feedburner.com/hna/kassel') ]
|
||||
|
||||
|
||||
|
@ -14,16 +14,16 @@ class Sueddeutsche(BasicNewsRecipe):
|
||||
description = 'News about Linux driven Hardware'
|
||||
__author__ = 'Oliver Niesner'
|
||||
use_embedded_content = False
|
||||
timefmt = ' [%a, %d %b %Y]'
|
||||
language = _('English')
|
||||
timefmt = ' [%a %d %b %Y]'
|
||||
max_articles_per_feed = 50
|
||||
no_stylesheets = True
|
||||
html2epub_options = 'linearize_tables = True\nbase_font_size2=14'
|
||||
encoding = 'latin1'
|
||||
|
||||
|
||||
remove_tags_after = [dict(id='nointelliTXT')]
|
||||
filter_regexps = [r'ad\.doubleclick\.net']
|
||||
|
||||
|
||||
remove_tags = [dict(name='div', attrs={'class':'bannerSuperBanner'}),
|
||||
dict(name='div', attrs={'class':'bannerSky'}),
|
||||
dict(name='div', attrs={'class':'footerLinks'}),
|
||||
@ -38,7 +38,6 @@ class Sueddeutsche(BasicNewsRecipe):
|
||||
dict(name='table', attrs={'class':'artikelBox'}),
|
||||
dict(name='table', attrs={'class':'kommentare'}),
|
||||
dict(name='table', attrs={'class':'pageBoxBot'}),
|
||||
#dict(name='table', attrs={'with':'100%'}),
|
||||
dict(name='td', attrs={'nowrap':'nowrap'}),
|
||||
dict(name='td', attrs={'valign':'middle'}),
|
||||
dict(name='td', attrs={'align':'left'}),
|
||||
@ -56,7 +55,6 @@ class Sueddeutsche(BasicNewsRecipe):
|
||||
dict(name='a', attrs={'href':'/cgi-bin/board/UltraBoard.pl'}),
|
||||
dict(name='iframe'),
|
||||
dict(name='form'),
|
||||
#dict(name='tr', attrs={'td':'Click here to learn'}),
|
||||
dict(name='span', attrs={'class':'hidePrint'}),
|
||||
dict(id='headerLBox'),
|
||||
dict(id='nointelliTXT'),
|
||||
|
66
src/calibre/web/feeds/recipes/recipe_nzz_ger.py
Normal file
66
src/calibre/web/feeds/recipes/recipe_nzz_ger.py
Normal file
@ -0,0 +1,66 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
__license__ = 'GPL v3'
|
||||
__copyright__ = '2009, Darko Miletic <darko.miletic at gmail.com>'
|
||||
|
||||
'''
|
||||
www.nzz.ch
|
||||
'''
|
||||
|
||||
from calibre.web.feeds.recipes import BasicNewsRecipe
|
||||
|
||||
class Nzz(BasicNewsRecipe):
|
||||
title = 'NZZ Online'
|
||||
__author__ = 'Darko Miletic'
|
||||
description = 'Laufend aktualisierte Nachrichten, Analysen und Hintergruende zu Politik, Wirtschaft, Kultur und Sport'
|
||||
publisher = 'NZZ AG'
|
||||
category = 'news, politics, nachrichten, Switzerland'
|
||||
oldest_article = 2
|
||||
max_articles_per_feed = 100
|
||||
no_stylesheets = True
|
||||
encoding = 'utf-8'
|
||||
use_embedded_content = False
|
||||
lang = 'de-CH'
|
||||
language = _('German')
|
||||
|
||||
html2lrf_options = [
|
||||
'--comment', description
|
||||
, '--category', category
|
||||
, '--publisher', publisher
|
||||
]
|
||||
|
||||
html2epub_options = 'publisher="' + publisher + '"\ncomments="' + description + '"\ntags="' + category + '"\noverride_css=" p {text-indent: 0em; margin-top: 0em; margin-bottom: 0.5em} img {margin-top: 0em; margin-bottom: 0.4em}"'
|
||||
|
||||
keep_only_tags = [dict(name='div', attrs={'class':'article'})]
|
||||
|
||||
remove_tags = [
|
||||
dict(name=['object','link','base','script'])
|
||||
,dict(name='div',attrs={'class':['more','teaser','advXertXoriXals','legal']})
|
||||
,dict(name='div',attrs={'id':['popup-src','readercomments','google-ad','advXertXoriXals']})
|
||||
]
|
||||
|
||||
feeds = [
|
||||
(u'Neuste Artikel', u'http://www.nzz.ch/feeds/recent/' )
|
||||
,(u'International' , u'http://www.nzz.ch/nachrichten/international?rss=true')
|
||||
,(u'Schweiz' , u'http://www.nzz.ch/nachrichten/schweiz?rss=true')
|
||||
,(u'Wirtschaft' , u'http://www.nzz.ch/nachrichten/wirtschaft/aktuell?rss=true')
|
||||
,(u'Finanzmaerkte' , u'http://www.nzz.ch/finanzen/nachrichten?rss=true')
|
||||
,(u'Zuerich' , u'http://www.nzz.ch/nachrichten/zuerich?rss=true')
|
||||
,(u'Sport' , u'http://www.nzz.ch/nachrichten/sport?rss=true')
|
||||
,(u'Panorama' , u'http://www.nzz.ch/nachrichten/panorama?rss=true')
|
||||
,(u'Kultur' , u'http://www.nzz.ch/nachrichten/kultur/aktuell?rss=true')
|
||||
,(u'Wissenschaft' , u'http://www.nzz.ch/nachrichten/wissenschaft?rss=true')
|
||||
,(u'Medien' , u'http://www.nzz.ch/nachrichten/medien?rss=true')
|
||||
,(u'Reisen' , u'http://www.nzz.ch/magazin/reisen?rss=true')
|
||||
]
|
||||
|
||||
def preprocess_html(self, soup):
|
||||
soup.html['xml:lang'] = self.lang
|
||||
soup.html['lang'] = self.lang
|
||||
mtag = '<meta http-equiv="Content-Type" content="text/html; charset=' + self.encoding + '">'
|
||||
soup.head.insert(0,mtag)
|
||||
return soup
|
||||
|
||||
def print_version(self, url):
|
||||
return url + '?printview=true'
|
||||
|
@ -8,25 +8,18 @@ Fetch tomshardware.
|
||||
from calibre.web.feeds.news import BasicNewsRecipe
|
||||
|
||||
|
||||
class TomsHardwareDe(BasicNewsRecipe):
|
||||
class cdnet(BasicNewsRecipe):
|
||||
|
||||
title = 'Tom\'s Hardware German'
|
||||
description = 'Computer news in german'
|
||||
title = 'tomshardware'
|
||||
description = 'computer news in german'
|
||||
__author__ = 'Oliver Niesner'
|
||||
use_embedded_content = False
|
||||
timefmt = ' [%d %b %Y]'
|
||||
max_articles_per_feed = 50
|
||||
language = _('German')
|
||||
no_stylesheets = True
|
||||
language = _('German')
|
||||
encoding = 'utf-8'
|
||||
|
||||
#preprocess_regexps = \
|
||||
# [(re.compile(i[0], re.IGNORECASE | re.DOTALL), i[1]) for i in
|
||||
# [
|
||||
# (r'<84>', lambda match: ''),
|
||||
# (r'<93>', lambda match: ''),
|
||||
# ]
|
||||
# ]
|
||||
|
||||
remove_tags = [dict(id='outside-advert'),
|
||||
dict(id='advertRightWhite'),
|
||||
@ -36,9 +29,15 @@ class TomsHardwareDe(BasicNewsRecipe):
|
||||
dict(id='header-top'),
|
||||
dict(id='header-tools'),
|
||||
dict(id='nbComment'),
|
||||
dict(id='commentTools'),
|
||||
dict(id='internalSidebar'),
|
||||
dict(id='header-news-infos'),
|
||||
dict(id='header-news-tools'),
|
||||
dict(id='breadcrumbs'),
|
||||
dict(id='emailTools'),
|
||||
dict(id='bookmarkTools'),
|
||||
dict(id='printTools'),
|
||||
dict(id='header-nextNews'),
|
||||
dict(id=''),
|
||||
dict(name='div', attrs={'class':'pyjama'}),
|
||||
dict(name='href', attrs={'class':'comment'}),
|
||||
@ -47,8 +46,10 @@ class TomsHardwareDe(BasicNewsRecipe):
|
||||
dict(name='div', attrs={'class':'greyBox clearfix'}),
|
||||
dict(id='')]
|
||||
#remove_tags_before = [dict(id='header-news-title')]
|
||||
remove_tags_after = [dict(name='div', attrs={'class':'news-elm'})]
|
||||
remove_tags_after = [dict(name='div', attrs={'class':'btmGreyTables'})]
|
||||
#remove_tags_after = [dict(name='div', attrs={'class':'intelliTXT'})]
|
||||
|
||||
feeds = [ ('tomshardware', 'http://www.tomshardware.com/de/feeds/rss2/tom-s-hardware-de,12-1.xml') ]
|
||||
|
||||
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user