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
e66a543fa7
4
setup.py
4
setup.py
@ -52,7 +52,8 @@ if __name__ == '__main__':
|
||||
resources, clean, gui, translations, update, \
|
||||
tag_release, upload_demo, build_linux, build_windows, \
|
||||
build_osx, upload_installers, upload_user_manual, \
|
||||
upload_to_pypi, stage3, stage2, stage1, upload
|
||||
upload_to_pypi, stage3, stage2, stage1, upload, \
|
||||
upload_rss
|
||||
|
||||
entry_points['console_scripts'].append(
|
||||
'calibre_postinstall = calibre.linux:post_install')
|
||||
@ -170,6 +171,7 @@ if __name__ == '__main__':
|
||||
'upload_installers': upload_installers,
|
||||
'upload_user_manual': upload_user_manual,
|
||||
'upload_to_pypi': upload_to_pypi,
|
||||
'upload_rss' : upload_rss,
|
||||
'stage3' : stage3,
|
||||
'stage2' : stage2,
|
||||
'stage1' : stage1,
|
||||
|
@ -31,6 +31,8 @@ mimetypes.add_type('application/adobe-page-template+xml', '.xpgt')
|
||||
mimetypes.add_type('application/x-font-opentype', '.otf')
|
||||
mimetypes.add_type('application/x-font-truetype', '.ttf')
|
||||
mimetypes.add_type('application/oebps-package+xml', '.opf')
|
||||
mimetypes.add_type('application/ereader', '.pdb')
|
||||
guess_type = mimetypes.guess_type
|
||||
import cssutils
|
||||
cssutils.log.setLevel(logging.WARN)
|
||||
|
||||
|
@ -1306,7 +1306,10 @@ class BeautifulStoneSoup(Tag, SGMLParser):
|
||||
if self.convertEntities:
|
||||
if ref.lower().startswith('x'): #
|
||||
ref = int(ref[1:], 16) # Added by Kovid to handle hex numeric entities
|
||||
try:
|
||||
data = unichr(int(ref))
|
||||
except ValueError: # Bad numerical entity. Added by Kovid
|
||||
data = u''
|
||||
else:
|
||||
data = '&#%s;' % ref
|
||||
self.handle_data(data)
|
||||
|
@ -346,19 +346,22 @@ class MetadataSingleDialog(ResizableDialog, Ui_MetadataSingleDialog):
|
||||
pix = QPixmap()
|
||||
pix.loadFromData(cover_data)
|
||||
if pix.isNull():
|
||||
error_dialog(self.window, "The cover is not a valid picture").exec_()
|
||||
error_dialog(self.window, _('Bad cover'),
|
||||
_('The cover is not a valid picture')).exec_()
|
||||
else:
|
||||
self.cover.setPixmap(pix)
|
||||
self.cover_changed = True
|
||||
self.cpixmap = pix
|
||||
except LibraryThingError, err:
|
||||
error_dialog(self, _('Could not fetch cover'), _('<b>Could not fetch cover.</b><br/>')+repr(err)).exec_()
|
||||
error_dialog(self, _('Cannot fetch cover'),
|
||||
_('<b>Could not fetch cover.</b><br/>')+repr(err)).exec_()
|
||||
finally:
|
||||
self.fetch_cover_button.setEnabled(True)
|
||||
self.unsetCursor()
|
||||
|
||||
else:
|
||||
error_dialog(self, _('Cannot fetch cover'), _('You must specify the ISBN identifier for this book.')).exec_()
|
||||
error_dialog(self, _('Cannot fetch cover'),
|
||||
_('You must specify the ISBN identifier for this book.')).exec_()
|
||||
|
||||
|
||||
def fetch_metadata(self):
|
||||
|
@ -818,7 +818,8 @@ class Main(MainWindow, Ui_MainWindow):
|
||||
rows = self.library_view.selectionModel().selectedRows()
|
||||
previous = self.library_view.currentIndex()
|
||||
if not rows or len(rows) == 0:
|
||||
d = error_dialog(self, _('Cannot edit metadata'), _('No books selected'))
|
||||
d = error_dialog(self, _('Cannot edit metadata'),
|
||||
_('No books selected'))
|
||||
d.exec_()
|
||||
return
|
||||
|
||||
@ -1152,6 +1153,8 @@ class Main(MainWindow, Ui_MainWindow):
|
||||
self.job_manager.server.run_free_job('lrfviewer', kwdargs=dict(args=args))
|
||||
else:
|
||||
args = ['ebook-viewer', name]
|
||||
if isosx:
|
||||
args.append('--raise-window')
|
||||
self.job_manager.server.run_free_job('ebook-viewer', kwdargs=dict(args=args))
|
||||
else:
|
||||
QDesktopServices.openUrl(QUrl('file:'+name))#launch(name)
|
||||
|
@ -604,6 +604,10 @@ def config(defaults=None):
|
||||
c = Config('viewer', desc)
|
||||
else:
|
||||
c = StringConfig(defaults, desc)
|
||||
|
||||
c.add_opt('--raise-window', default=False,
|
||||
help=_('If specified, viewer window will try to come to the '
|
||||
'front when started.'))
|
||||
return c
|
||||
|
||||
def option_parser():
|
||||
@ -617,7 +621,7 @@ View an ebook.
|
||||
|
||||
def main(args=sys.argv):
|
||||
parser = option_parser()
|
||||
args = parser.parse_args(args)[-1]
|
||||
opts, args = parser.parse_args(args)
|
||||
pid = os.fork() if False and islinux else -1
|
||||
if pid <= 0:
|
||||
app = Application(args)
|
||||
@ -627,6 +631,8 @@ def main(args=sys.argv):
|
||||
main = EbookViewer(args[1] if len(args) > 1 else None)
|
||||
sys.excepthook = main.unhandled_exception
|
||||
main.show()
|
||||
if opts.raise_window:
|
||||
main.raise_()
|
||||
with main:
|
||||
return app.exec_()
|
||||
return 0
|
||||
|
@ -7,7 +7,7 @@ __docformat__ = 'restructuredtext en'
|
||||
HTTP server for remote access to the calibre database.
|
||||
'''
|
||||
|
||||
import sys, textwrap, mimetypes, operator, os, re, logging
|
||||
import sys, textwrap, operator, os, re, logging
|
||||
from itertools import repeat
|
||||
from logging.handlers import RotatingFileHandler
|
||||
from datetime import datetime
|
||||
@ -18,7 +18,7 @@ from PyQt4.Qt import QImage, QApplication, QByteArray, Qt, QBuffer
|
||||
|
||||
from calibre.constants import __version__, __appname__
|
||||
from calibre.utils.genshi.template import MarkupTemplate
|
||||
from calibre import fit_image
|
||||
from calibre import fit_image, guess_type
|
||||
from calibre.resources import jquery, server_resources, build_time
|
||||
from calibre.library import server_config as config
|
||||
from calibre.library.database2 import LibraryDatabase2, FIELD_MAP
|
||||
@ -77,7 +77,7 @@ class LibraryServer(object):
|
||||
<id>urn:calibre:${record[FM['id']]}</id>
|
||||
<author><name>${authors}</name></author>
|
||||
<updated>${record[FM['timestamp']].strftime('%Y-%m-%dT%H:%M:%S+00:00')}</updated>
|
||||
<link type="application/epub+zip" href="/get/epub/${record[FM['id']]}" />
|
||||
<link type="${mimetype}" href="/get/${fmt}/${record[FM['id']]}" />
|
||||
<link rel="x-stanza-cover-image" type="image/jpeg" href="/get/cover/${record[FM['id']]}" />
|
||||
<link rel="x-stanza-cover-image-thumbnail" type="image/jpeg" href="/get/thumb/${record[FM['id']]}" />
|
||||
<content type="xhtml">
|
||||
@ -218,7 +218,7 @@ class LibraryServer(object):
|
||||
fmt = self.db.format(id, format, index_is_id=True, as_file=True, mode='rb')
|
||||
if fmt is None:
|
||||
raise cherrypy.HTTPError(404, 'book: %d does not have format: %s'%(id, format))
|
||||
mt = mimetypes.guess_type('dummy.'+format.lower())[0]
|
||||
mt = guess_type('dummy.'+format.lower())[0]
|
||||
if mt is None:
|
||||
mt = 'application/octet-stream'
|
||||
cherrypy.response.headers['Content-Type'] = mt
|
||||
@ -258,8 +258,9 @@ class LibraryServer(object):
|
||||
for record in iter(self.db):
|
||||
r = record[FIELD_MAP['formats']]
|
||||
r = r.upper() if r else ''
|
||||
if 'EPUB' in r:
|
||||
authors = ' & '.join([i.replace('|', ',') for i in record[FIELD_MAP['authors']].split(',')])
|
||||
if 'EPUB' in r or 'PDB' in r:
|
||||
authors = ' & '.join([i.replace('|', ',') for i in
|
||||
record[FIELD_MAP['authors']].split(',')])
|
||||
extra = []
|
||||
rating = record[FIELD_MAP['rating']]
|
||||
if rating > 0:
|
||||
@ -270,11 +271,17 @@ class LibraryServer(object):
|
||||
extra.append('TAGS: %s<br />'%', '.join(tags.split(',')))
|
||||
series = record[FIELD_MAP['series']]
|
||||
if series:
|
||||
extra.append('SERIES: %s [%d]<br />'%(series, record[FIELD_MAP['series_index']]))
|
||||
books.append(self.STANZA_ENTRY.generate(authors=authors,
|
||||
extra.append('SERIES: %s [%d]<br />'%(series,
|
||||
record[FIELD_MAP['series_index']]))
|
||||
fmt = 'epub' if 'EPUB' in r else 'pdb'
|
||||
mimetype = guess_type('dummy.'+fmt)[0]
|
||||
books.append(self.STANZA_ENTRY.generate(
|
||||
authors=authors,
|
||||
record=record, FM=FIELD_MAP,
|
||||
port=self.opts.port,
|
||||
extra = ''.join(extra),
|
||||
mimetype=mimetype,
|
||||
fmt=fmt,
|
||||
).render('xml').decode('utf8'))
|
||||
|
||||
updated = self.db.last_modified()
|
||||
|
@ -33,9 +33,10 @@ def get_linux_data(version='1.0.0'):
|
||||
data['title'] = 'Download calibre for linux'
|
||||
data['supported'] = []
|
||||
for name, title in [
|
||||
('ubuntu', 'Ubuntu Jaunty Jackalope'),
|
||||
('debian', 'Debian Sid'),
|
||||
('exherbo', 'Exherbo'),
|
||||
('foresight', 'Foresight 2.1'),
|
||||
('ubuntu', 'Ubuntu Jaunty Jackalope'),
|
||||
]:
|
||||
data['supported'].append(CoolDistro(name, title,
|
||||
prefix='http://calibre.kovidgoyal.net'))
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -13,7 +13,7 @@ msgstr ""
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Launchpad-Export-Date: 2009-03-06 17:12+0000\n"
|
||||
"X-Launchpad-Export-Date: 2009-03-11 18:28+0000\n"
|
||||
"X-Generator: Launchpad (build Unknown)\n"
|
||||
"Generated-By: pygettext.py 1.5\n"
|
||||
|
||||
|
@ -17,7 +17,7 @@ msgstr ""
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Launchpad-Export-Date: 2009-03-06 17:12+0000\n"
|
||||
"X-Launchpad-Export-Date: 2009-03-11 18:28+0000\n"
|
||||
"X-Generator: Launchpad (build Unknown)\n"
|
||||
|
||||
#: /home/kovid/work/calibre/src/calibre/customize/__init__.py:41
|
||||
|
@ -8,13 +8,13 @@ msgstr ""
|
||||
"Project-Id-Version: calibre\n"
|
||||
"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"POT-Creation-Date: 2009-02-26 19:09+0000\n"
|
||||
"PO-Revision-Date: 2009-03-04 20:00+0000\n"
|
||||
"Last-Translator: Plazec <Unknown>\n"
|
||||
"PO-Revision-Date: 2009-03-11 12:54+0000\n"
|
||||
"Last-Translator: raduz <raduzator@gmail.com>\n"
|
||||
"Language-Team: Czech <cs@li.org>\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Launchpad-Export-Date: 2009-03-06 17:12+0000\n"
|
||||
"X-Launchpad-Export-Date: 2009-03-11 18:28+0000\n"
|
||||
"X-Generator: Launchpad (build Unknown)\n"
|
||||
|
||||
#: /home/kovid/work/calibre/src/calibre/customize/__init__.py:41
|
||||
@ -610,8 +610,8 @@ msgid ""
|
||||
"Create the output in a zip file. If this option is specified, the --output "
|
||||
"should be the name of a file not a directory."
|
||||
msgstr ""
|
||||
"Výstuo je uložen do ZIP souboru. Pokud je zvolena tato volba, položka --"
|
||||
"output by měly obsahovat název souboru, nikoliv adresáře."
|
||||
"Výstup je uložen do ZIP souboru. Pokud je zvolena tato volba, položka --"
|
||||
"output by měla obsahovat název souboru, nikoliv adresáře."
|
||||
|
||||
#: /home/kovid/work/calibre/src/calibre/ebooks/html.py:933
|
||||
msgid "Control the following of links in HTML files."
|
||||
|
@ -14,7 +14,7 @@ msgstr ""
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Launchpad-Export-Date: 2009-03-06 17:12+0000\n"
|
||||
"X-Launchpad-Export-Date: 2009-03-11 18:28+0000\n"
|
||||
"X-Generator: Launchpad (build Unknown)\n"
|
||||
"Generated-By: pygettext.py 1.5\n"
|
||||
|
||||
|
@ -14,7 +14,7 @@ msgstr ""
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Launchpad-Export-Date: 2009-03-06 17:12+0000\n"
|
||||
"X-Launchpad-Export-Date: 2009-03-11 18:28+0000\n"
|
||||
"X-Generator: Launchpad (build Unknown)\n"
|
||||
|
||||
#: /home/kovid/work/calibre/src/calibre/customize/__init__.py:41
|
||||
|
@ -17,7 +17,7 @@ msgstr ""
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Launchpad-Export-Date: 2009-03-06 17:12+0000\n"
|
||||
"X-Launchpad-Export-Date: 2009-03-11 18:28+0000\n"
|
||||
"X-Generator: Launchpad (build Unknown)\n"
|
||||
|
||||
#: /home/kovid/work/calibre/src/calibre/customize/__init__.py:41
|
||||
|
@ -7,13 +7,13 @@ msgstr ""
|
||||
"Project-Id-Version: calibre 0.4.22\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2009-02-26 19:09+0000\n"
|
||||
"PO-Revision-Date: 2009-03-05 20:05+0000\n"
|
||||
"PO-Revision-Date: 2009-03-07 19:24+0000\n"
|
||||
"Last-Translator: Vincent C. <Unknown>\n"
|
||||
"Language-Team: fr\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Launchpad-Export-Date: 2009-03-06 17:12+0000\n"
|
||||
"X-Launchpad-Export-Date: 2009-03-11 18:28+0000\n"
|
||||
"X-Generator: Launchpad (build Unknown)\n"
|
||||
"Generated-By: pygettext.py 1.5\n"
|
||||
|
||||
@ -4160,6 +4160,29 @@ msgid ""
|
||||
"expression on a few sample filenames. The group names for the various "
|
||||
"metadata entries are documented in tooltips.</p></body></html>"
|
||||
msgstr ""
|
||||
"Copy text \t\n"
|
||||
"<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.0//EN\" "
|
||||
"\"http://www.w3.org/TR/REC-html40/strict.dtd\">\n"
|
||||
"<html><head><meta name=\"qrichtext\" content=\"1\" /><style "
|
||||
"type=\"text/css\">\n"
|
||||
"p, li { white-space: pre-wrap; }\n"
|
||||
"</style></head><body style=\" font-family:'DejaVu Sans'; font-size:10pt; "
|
||||
"font-weight:400; font-style:normal;\">\n"
|
||||
"<p style=\" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-"
|
||||
"right:0px; -qt-block-indent:0; text-indent:0px;\">Indiquer un modèle "
|
||||
"d'expression régulière à utiliser pour déduire les méta-données à partir des "
|
||||
"noms de fichiers. </p>\n"
|
||||
"<p style=\" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-"
|
||||
"right:0px; -qt-block-indent:0; text-indent:0px;\">Un <a "
|
||||
"href=\"http://docs.python.org/lib/re-syntax.html\"><span style=\" text-"
|
||||
"decoration: underline; color:#0000ff;\">renvoi</span></a> sur la syntaxe "
|
||||
"d'une expression régulière est disponible.</p>\n"
|
||||
"<p style=\" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-"
|
||||
"right:0px; -qt-block-indent:0; text-indent:0px;\">Utiliser la fonctionnalité "
|
||||
"de <span style=\" font-weight:600;\">test</span> ci-dessous pour valider "
|
||||
"votre expression régulière avec quelques exemples de noms de fichiers. Les "
|
||||
"groupes pour chacune des entrées de méta-données sont affichés dans les "
|
||||
"bulles d'aide.</p></body></html>"
|
||||
|
||||
#: /home/kovid/work/calibre/src/calibre/gui2/filename_pattern_ui.py:104
|
||||
msgid "Regular &expression"
|
||||
@ -4200,7 +4223,7 @@ msgstr "Pas de correspondance"
|
||||
|
||||
#: /home/kovid/work/calibre/src/calibre/gui2/filename_pattern_ui.py:111
|
||||
msgid "Authors:"
|
||||
msgstr "Auteurs"
|
||||
msgstr "Auteurs:"
|
||||
|
||||
#: /home/kovid/work/calibre/src/calibre/gui2/filename_pattern_ui.py:112
|
||||
msgid "Regular expression (?P<authors>)"
|
||||
@ -4477,12 +4500,12 @@ msgstr "Sauvegarder sur le disque"
|
||||
|
||||
#: /home/kovid/work/calibre/src/calibre/gui2/main.py:195
|
||||
msgid "Save to disk in a single directory"
|
||||
msgstr "Sauvegarde vers le disque dans un répertoire"
|
||||
msgstr "Sauvegarder sur le disque dans un seul répetoire"
|
||||
|
||||
#: /home/kovid/work/calibre/src/calibre/gui2/main.py:196
|
||||
#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1261
|
||||
msgid "Save only %s format to disk"
|
||||
msgstr "Sauvegarde seulement le format %s vers le disque"
|
||||
msgstr "Sauvegarder seulement le format %s vers le disque"
|
||||
|
||||
#: /home/kovid/work/calibre/src/calibre/gui2/main.py:199
|
||||
#: /home/kovid/work/calibre/src/calibre/gui2/main_ui.py:364
|
||||
@ -4491,7 +4514,7 @@ msgstr "Visualiser"
|
||||
|
||||
#: /home/kovid/work/calibre/src/calibre/gui2/main.py:200
|
||||
msgid "View specific format"
|
||||
msgstr "Afficher le format spécifique"
|
||||
msgstr "Visualiser le format spécifique"
|
||||
|
||||
#: /home/kovid/work/calibre/src/calibre/gui2/main.py:217
|
||||
msgid "Convert individually"
|
||||
@ -4904,11 +4927,11 @@ msgstr "Réinitialisation de la recherche rapide"
|
||||
|
||||
#: /home/kovid/work/calibre/src/calibre/gui2/main_ui.py:347
|
||||
msgid "Match any"
|
||||
msgstr ""
|
||||
msgstr "Aucune correspondance"
|
||||
|
||||
#: /home/kovid/work/calibre/src/calibre/gui2/main_ui.py:348
|
||||
msgid "Match all"
|
||||
msgstr ""
|
||||
msgstr "Toutes les correspondances"
|
||||
|
||||
#: /home/kovid/work/calibre/src/calibre/gui2/main_ui.py:349
|
||||
msgid "Sort by &popularity"
|
||||
|
@ -14,7 +14,7 @@ msgstr ""
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Launchpad-Export-Date: 2009-03-06 17:12+0000\n"
|
||||
"X-Launchpad-Export-Date: 2009-03-11 18:28+0000\n"
|
||||
"X-Generator: Launchpad (build Unknown)\n"
|
||||
|
||||
#: /home/kovid/work/calibre/src/calibre/customize/__init__.py:41
|
||||
|
@ -14,7 +14,7 @@ msgstr ""
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Launchpad-Export-Date: 2009-03-06 17:12+0000\n"
|
||||
"X-Launchpad-Export-Date: 2009-03-11 18:28+0000\n"
|
||||
"X-Generator: Launchpad (build Unknown)\n"
|
||||
|
||||
#: /home/kovid/work/calibre/src/calibre/customize/__init__.py:41
|
||||
|
@ -14,7 +14,7 @@ msgstr ""
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Launchpad-Export-Date: 2009-03-06 17:12+0000\n"
|
||||
"X-Launchpad-Export-Date: 2009-03-11 18:28+0000\n"
|
||||
"X-Generator: Launchpad (build Unknown)\n"
|
||||
|
||||
#: /home/kovid/work/calibre/src/calibre/customize/__init__.py:41
|
||||
|
@ -15,7 +15,7 @@ msgstr ""
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Launchpad-Export-Date: 2009-03-06 17:12+0000\n"
|
||||
"X-Launchpad-Export-Date: 2009-03-11 18:28+0000\n"
|
||||
"X-Generator: Launchpad (build Unknown)\n"
|
||||
"Generated-By: pygettext.py 1.5\n"
|
||||
|
||||
|
@ -14,7 +14,7 @@ msgstr ""
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Launchpad-Export-Date: 2009-03-06 17:12+0000\n"
|
||||
"X-Launchpad-Export-Date: 2009-03-11 18:28+0000\n"
|
||||
"X-Generator: Launchpad (build Unknown)\n"
|
||||
|
||||
#: /home/kovid/work/calibre/src/calibre/customize/__init__.py:41
|
||||
|
@ -14,7 +14,7 @@ msgstr ""
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Launchpad-Export-Date: 2009-03-06 17:12+0000\n"
|
||||
"X-Launchpad-Export-Date: 2009-03-11 18:28+0000\n"
|
||||
"X-Generator: Launchpad (build Unknown)\n"
|
||||
"Generated-By: pygettext.py 1.5\n"
|
||||
|
||||
|
@ -14,7 +14,7 @@ msgstr ""
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Launchpad-Export-Date: 2009-03-06 17:12+0000\n"
|
||||
"X-Launchpad-Export-Date: 2009-03-11 18:28+0000\n"
|
||||
"X-Generator: Launchpad (build Unknown)\n"
|
||||
|
||||
#: /home/kovid/work/calibre/src/calibre/customize/__init__.py:41
|
||||
|
@ -8,13 +8,13 @@ msgstr ""
|
||||
"Project-Id-Version: calibre\n"
|
||||
"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"POT-Creation-Date: 2009-02-26 19:09+0000\n"
|
||||
"PO-Revision-Date: 2009-01-30 09:03+0000\n"
|
||||
"PO-Revision-Date: 2009-03-11 01:37+0000\n"
|
||||
"Last-Translator: Andrzej MoST (Marcin Ostajewski) <Unknown>\n"
|
||||
"Language-Team: Polish <pl@li.org>\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Launchpad-Export-Date: 2009-03-06 17:12+0000\n"
|
||||
"X-Launchpad-Export-Date: 2009-03-11 18:28+0000\n"
|
||||
"X-Generator: Launchpad (build Unknown)\n"
|
||||
|
||||
#: /home/kovid/work/calibre/src/calibre/customize/__init__.py:41
|
||||
@ -159,7 +159,7 @@ msgstr "Odczyt metadanych z e-booków w archiwach RAR"
|
||||
#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:227
|
||||
#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:237
|
||||
msgid "Set metadata in %s files"
|
||||
msgstr ""
|
||||
msgstr "Ustaw metadane w %s plikach"
|
||||
|
||||
#: /home/kovid/work/calibre/src/calibre/customize/ui.py:28
|
||||
msgid "Installed plugins"
|
||||
@ -481,7 +481,7 @@ msgstr ""
|
||||
|
||||
#: /home/kovid/work/calibre/src/calibre/ebooks/epub/__init__.py:221
|
||||
msgid "Do not force text to be justified in output."
|
||||
msgstr ""
|
||||
msgstr "Nie wymuszaj wyjustowania tekstu w pliku wynikowym."
|
||||
|
||||
#: /home/kovid/work/calibre/src/calibre/ebooks/epub/__init__.py:223
|
||||
msgid ""
|
||||
@ -1216,7 +1216,7 @@ msgstr "Wydrukuj wygenerowany HTML do wyjścia standardowego i zamknij."
|
||||
|
||||
#: /home/kovid/work/calibre/src/calibre/ebooks/lrf/fb2/convert_from.py:30
|
||||
msgid "Keep generated HTML files after completing conversion to LRF."
|
||||
msgstr ""
|
||||
msgstr "Zachowaj wygenerowany plik HTML po konwersji do LRF."
|
||||
|
||||
#: /home/kovid/work/calibre/src/calibre/ebooks/lrf/feeds/convert_from.py:20
|
||||
msgid "Options to control the behavior of feeds2disk"
|
||||
@ -1450,7 +1450,7 @@ msgstr "Wypakuj miniaturkę z pliku LRF"
|
||||
#: /home/kovid/work/calibre/src/calibre/ebooks/lrf/meta.py:606
|
||||
#: /home/kovid/work/calibre/src/calibre/ebooks/metadata/mobi.py:191
|
||||
msgid "Set the publisher"
|
||||
msgstr ""
|
||||
msgstr "Ustaw wydawcę"
|
||||
|
||||
#: /home/kovid/work/calibre/src/calibre/ebooks/lrf/meta.py:607
|
||||
msgid "Set the book classification"
|
||||
@ -1715,7 +1715,7 @@ msgstr ""
|
||||
|
||||
#: /home/kovid/work/calibre/src/calibre/ebooks/metadata/mobi.py:193
|
||||
msgid "Set the ISBN"
|
||||
msgstr ""
|
||||
msgstr "Ustaw ISBN"
|
||||
|
||||
#: /home/kovid/work/calibre/src/calibre/ebooks/metadata/opf2.py:1014
|
||||
msgid "Set the dc:language field"
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -14,7 +14,7 @@ msgstr ""
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Launchpad-Export-Date: 2009-03-06 17:12+0000\n"
|
||||
"X-Launchpad-Export-Date: 2009-03-11 18:28+0000\n"
|
||||
"X-Generator: Launchpad (build Unknown)\n"
|
||||
|
||||
#: /home/kovid/work/calibre/src/calibre/customize/__init__.py:41
|
||||
|
@ -13,7 +13,7 @@ msgstr ""
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Launchpad-Export-Date: 2009-03-06 17:12+0000\n"
|
||||
"X-Launchpad-Export-Date: 2009-03-11 18:28+0000\n"
|
||||
"X-Generator: Launchpad (build Unknown)\n"
|
||||
"X-Poedit-Country: RUSSIAN FEDERATION\n"
|
||||
"X-Poedit-Language: Russian\n"
|
||||
|
@ -14,7 +14,7 @@ msgstr ""
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Launchpad-Export-Date: 2009-03-06 17:12+0000\n"
|
||||
"X-Launchpad-Export-Date: 2009-03-11 18:28+0000\n"
|
||||
"X-Generator: Launchpad (build Unknown)\n"
|
||||
|
||||
#: /home/kovid/work/calibre/src/calibre/customize/__init__.py:41
|
||||
|
@ -13,7 +13,7 @@ msgstr ""
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Launchpad-Export-Date: 2009-03-06 17:12+0000\n"
|
||||
"X-Launchpad-Export-Date: 2009-03-11 18:28+0000\n"
|
||||
"X-Generator: Launchpad (build Unknown)\n"
|
||||
"Generated-By: pygettext.py 1.5\n"
|
||||
|
||||
|
@ -14,7 +14,7 @@ msgstr ""
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Launchpad-Export-Date: 2009-03-06 17:12+0000\n"
|
||||
"X-Launchpad-Export-Date: 2009-03-11 18:28+0000\n"
|
||||
"X-Generator: Launchpad (build Unknown)\n"
|
||||
|
||||
#: /home/kovid/work/calibre/src/calibre/customize/__init__.py:41
|
||||
|
@ -14,7 +14,7 @@ msgstr ""
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Launchpad-Export-Date: 2009-03-06 17:12+0000\n"
|
||||
"X-Launchpad-Export-Date: 2009-03-11 18:28+0000\n"
|
||||
"X-Generator: Launchpad (build Unknown)\n"
|
||||
|
||||
#: /home/kovid/work/calibre/src/calibre/customize/__init__.py:41
|
||||
|
443
src/calibre/utils/rss_gen.py
Normal file
443
src/calibre/utils/rss_gen.py
Normal file
@ -0,0 +1,443 @@
|
||||
"""PyRSS2Gen - A Python library for generating RSS 2.0 feeds."""
|
||||
|
||||
__name__ = "PyRSS2Gen"
|
||||
__version__ = (1, 0, 0)
|
||||
__author__ = "Andrew Dalke <dalke@dalkescientific.com>"
|
||||
|
||||
_generator_name = __name__ + "-" + ".".join(map(str, __version__))
|
||||
|
||||
import datetime
|
||||
|
||||
# Could make this the base class; will need to add 'publish'
|
||||
class WriteXmlMixin:
|
||||
def write_xml(self, outfile, encoding = "iso-8859-1"):
|
||||
from xml.sax import saxutils
|
||||
handler = saxutils.XMLGenerator(outfile, encoding)
|
||||
handler.startDocument()
|
||||
self.publish(handler)
|
||||
handler.endDocument()
|
||||
|
||||
def to_xml(self, encoding = "iso-8859-1"):
|
||||
try:
|
||||
import cStringIO as StringIO
|
||||
except ImportError:
|
||||
import StringIO
|
||||
f = StringIO.StringIO()
|
||||
self.write_xml(f, encoding)
|
||||
return f.getvalue()
|
||||
|
||||
|
||||
def _element(handler, name, obj, d = {}):
|
||||
if isinstance(obj, basestring) or obj is None:
|
||||
# special-case handling to make the API easier
|
||||
# to use for the common case.
|
||||
handler.startElement(name, d)
|
||||
if obj is not None:
|
||||
handler.characters(obj)
|
||||
handler.endElement(name)
|
||||
else:
|
||||
# It better know how to emit the correct XML.
|
||||
obj.publish(handler)
|
||||
|
||||
def _opt_element(handler, name, obj):
|
||||
if obj is None:
|
||||
return
|
||||
_element(handler, name, obj)
|
||||
|
||||
|
||||
def _format_date(dt):
|
||||
"""convert a datetime into an RFC 822 formatted date
|
||||
|
||||
Input date must be in GMT.
|
||||
"""
|
||||
# Looks like:
|
||||
# Sat, 07 Sep 2002 00:00:01 GMT
|
||||
# Can't use strftime because that's locale dependent
|
||||
#
|
||||
# Isn't there a standard way to do this for Python? The
|
||||
# rfc822 and email.Utils modules assume a timestamp. The
|
||||
# following is based on the rfc822 module.
|
||||
return "%s, %02d %s %04d %02d:%02d:%02d GMT" % (
|
||||
["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"][dt.weekday()],
|
||||
dt.day,
|
||||
["Jan", "Feb", "Mar", "Apr", "May", "Jun",
|
||||
"Jul", "Aug", "Sep", "Oct", "Nov", "Dec"][dt.month-1],
|
||||
dt.year, dt.hour, dt.minute, dt.second)
|
||||
|
||||
|
||||
##
|
||||
# A couple simple wrapper objects for the fields which
|
||||
# take a simple value other than a string.
|
||||
class IntElement:
|
||||
"""implements the 'publish' API for integers
|
||||
|
||||
Takes the tag name and the integer value to publish.
|
||||
|
||||
(Could be used for anything which uses str() to be published
|
||||
to text for XML.)
|
||||
"""
|
||||
element_attrs = {}
|
||||
def __init__(self, name, val):
|
||||
self.name = name
|
||||
self.val = val
|
||||
def publish(self, handler):
|
||||
handler.startElement(self.name, self.element_attrs)
|
||||
handler.characters(str(self.val))
|
||||
handler.endElement(self.name)
|
||||
|
||||
class DateElement:
|
||||
"""implements the 'publish' API for a datetime.datetime
|
||||
|
||||
Takes the tag name and the datetime to publish.
|
||||
|
||||
Converts the datetime to RFC 2822 timestamp (4-digit year).
|
||||
"""
|
||||
def __init__(self, name, dt):
|
||||
self.name = name
|
||||
self.dt = dt
|
||||
def publish(self, handler):
|
||||
_element(handler, self.name, _format_date(self.dt))
|
||||
####
|
||||
|
||||
class Category:
|
||||
"""Publish a category element"""
|
||||
def __init__(self, category, domain = None):
|
||||
self.category = category
|
||||
self.domain = domain
|
||||
def publish(self, handler):
|
||||
d = {}
|
||||
if self.domain is not None:
|
||||
d["domain"] = self.domain
|
||||
_element(handler, "category", self.category, d)
|
||||
|
||||
class Cloud:
|
||||
"""Publish a cloud"""
|
||||
def __init__(self, domain, port, path,
|
||||
registerProcedure, protocol):
|
||||
self.domain = domain
|
||||
self.port = port
|
||||
self.path = path
|
||||
self.registerProcedure = registerProcedure
|
||||
self.protocol = protocol
|
||||
def publish(self, handler):
|
||||
_element(handler, "cloud", None, {
|
||||
"domain": self.domain,
|
||||
"port": str(self.port),
|
||||
"path": self.path,
|
||||
"registerProcedure": self.registerProcedure,
|
||||
"protocol": self.protocol})
|
||||
|
||||
class Image:
|
||||
"""Publish a channel Image"""
|
||||
element_attrs = {}
|
||||
def __init__(self, url, title, link,
|
||||
width = None, height = None, description = None):
|
||||
self.url = url
|
||||
self.title = title
|
||||
self.link = link
|
||||
self.width = width
|
||||
self.height = height
|
||||
self.description = description
|
||||
|
||||
def publish(self, handler):
|
||||
handler.startElement("image", self.element_attrs)
|
||||
|
||||
_element(handler, "url", self.url)
|
||||
_element(handler, "title", self.title)
|
||||
_element(handler, "link", self.link)
|
||||
|
||||
width = self.width
|
||||
if isinstance(width, int):
|
||||
width = IntElement("width", width)
|
||||
_opt_element(handler, "width", width)
|
||||
|
||||
height = self.height
|
||||
if isinstance(height, int):
|
||||
height = IntElement("height", height)
|
||||
_opt_element(handler, "height", height)
|
||||
|
||||
_opt_element(handler, "description", self.description)
|
||||
|
||||
handler.endElement("image")
|
||||
|
||||
class Guid:
|
||||
"""Publish a guid
|
||||
|
||||
Defaults to being a permalink, which is the assumption if it's
|
||||
omitted. Hence strings are always permalinks.
|
||||
"""
|
||||
def __init__(self, guid, isPermaLink = 1):
|
||||
self.guid = guid
|
||||
self.isPermaLink = isPermaLink
|
||||
def publish(self, handler):
|
||||
d = {}
|
||||
if self.isPermaLink:
|
||||
d["isPermaLink"] = "true"
|
||||
else:
|
||||
d["isPermaLink"] = "false"
|
||||
_element(handler, "guid", self.guid, d)
|
||||
|
||||
class TextInput:
|
||||
"""Publish a textInput
|
||||
|
||||
Apparently this is rarely used.
|
||||
"""
|
||||
element_attrs = {}
|
||||
def __init__(self, title, description, name, link):
|
||||
self.title = title
|
||||
self.description = description
|
||||
self.name = name
|
||||
self.link = link
|
||||
|
||||
def publish(self, handler):
|
||||
handler.startElement("textInput", self.element_attrs)
|
||||
_element(handler, "title", self.title)
|
||||
_element(handler, "description", self.description)
|
||||
_element(handler, "name", self.name)
|
||||
_element(handler, "link", self.link)
|
||||
handler.endElement("textInput")
|
||||
|
||||
|
||||
class Enclosure:
|
||||
"""Publish an enclosure"""
|
||||
def __init__(self, url, length, type):
|
||||
self.url = url
|
||||
self.length = length
|
||||
self.type = type
|
||||
def publish(self, handler):
|
||||
_element(handler, "enclosure", None,
|
||||
{"url": self.url,
|
||||
"length": str(self.length),
|
||||
"type": self.type,
|
||||
})
|
||||
|
||||
class Source:
|
||||
"""Publish the item's original source, used by aggregators"""
|
||||
def __init__(self, name, url):
|
||||
self.name = name
|
||||
self.url = url
|
||||
def publish(self, handler):
|
||||
_element(handler, "source", self.name, {"url": self.url})
|
||||
|
||||
class SkipHours:
|
||||
"""Publish the skipHours
|
||||
|
||||
This takes a list of hours, as integers.
|
||||
"""
|
||||
element_attrs = {}
|
||||
def __init__(self, hours):
|
||||
self.hours = hours
|
||||
def publish(self, handler):
|
||||
if self.hours:
|
||||
handler.startElement("skipHours", self.element_attrs)
|
||||
for hour in self.hours:
|
||||
_element(handler, "hour", str(hour))
|
||||
handler.endElement("skipHours")
|
||||
|
||||
class SkipDays:
|
||||
"""Publish the skipDays
|
||||
|
||||
This takes a list of days as strings.
|
||||
"""
|
||||
element_attrs = {}
|
||||
def __init__(self, days):
|
||||
self.days = days
|
||||
def publish(self, handler):
|
||||
if self.days:
|
||||
handler.startElement("skipDays", self.element_attrs)
|
||||
for day in self.days:
|
||||
_element(handler, "day", day)
|
||||
handler.endElement("skipDays")
|
||||
|
||||
class RSS2(WriteXmlMixin):
|
||||
"""The main RSS class.
|
||||
|
||||
Stores the channel attributes, with the "category" elements under
|
||||
".categories" and the RSS items under ".items".
|
||||
"""
|
||||
|
||||
rss_attrs = {"version": "2.0"}
|
||||
element_attrs = {}
|
||||
def __init__(self,
|
||||
title,
|
||||
link,
|
||||
description,
|
||||
|
||||
language = None,
|
||||
copyright = None,
|
||||
managingEditor = None,
|
||||
webMaster = None,
|
||||
pubDate = None, # a datetime, *in* *GMT*
|
||||
lastBuildDate = None, # a datetime
|
||||
|
||||
categories = None, # list of strings or Category
|
||||
generator = _generator_name,
|
||||
docs = "http://blogs.law.harvard.edu/tech/rss",
|
||||
cloud = None, # a Cloud
|
||||
ttl = None, # integer number of minutes
|
||||
|
||||
image = None, # an Image
|
||||
rating = None, # a string; I don't know how it's used
|
||||
textInput = None, # a TextInput
|
||||
skipHours = None, # a SkipHours with a list of integers
|
||||
skipDays = None, # a SkipDays with a list of strings
|
||||
|
||||
items = None, # list of RSSItems
|
||||
):
|
||||
self.title = title
|
||||
self.link = link
|
||||
self.description = description
|
||||
self.language = language
|
||||
self.copyright = copyright
|
||||
self.managingEditor = managingEditor
|
||||
|
||||
self.webMaster = webMaster
|
||||
self.pubDate = pubDate
|
||||
self.lastBuildDate = lastBuildDate
|
||||
|
||||
if categories is None:
|
||||
categories = []
|
||||
self.categories = categories
|
||||
self.generator = generator
|
||||
self.docs = docs
|
||||
self.cloud = cloud
|
||||
self.ttl = ttl
|
||||
self.image = image
|
||||
self.rating = rating
|
||||
self.textInput = textInput
|
||||
self.skipHours = skipHours
|
||||
self.skipDays = skipDays
|
||||
|
||||
if items is None:
|
||||
items = []
|
||||
self.items = items
|
||||
|
||||
def publish(self, handler):
|
||||
handler.startElement("rss", self.rss_attrs)
|
||||
handler.startElement("channel", self.element_attrs)
|
||||
_element(handler, "title", self.title)
|
||||
_element(handler, "link", self.link)
|
||||
_element(handler, "description", self.description)
|
||||
|
||||
self.publish_extensions(handler)
|
||||
|
||||
_opt_element(handler, "language", self.language)
|
||||
_opt_element(handler, "copyright", self.copyright)
|
||||
_opt_element(handler, "managingEditor", self.managingEditor)
|
||||
_opt_element(handler, "webMaster", self.webMaster)
|
||||
|
||||
pubDate = self.pubDate
|
||||
if isinstance(pubDate, datetime.datetime):
|
||||
pubDate = DateElement("pubDate", pubDate)
|
||||
_opt_element(handler, "pubDate", pubDate)
|
||||
|
||||
lastBuildDate = self.lastBuildDate
|
||||
if isinstance(lastBuildDate, datetime.datetime):
|
||||
lastBuildDate = DateElement("lastBuildDate", lastBuildDate)
|
||||
_opt_element(handler, "lastBuildDate", lastBuildDate)
|
||||
|
||||
for category in self.categories:
|
||||
if isinstance(category, basestring):
|
||||
category = Category(category)
|
||||
category.publish(handler)
|
||||
|
||||
_opt_element(handler, "generator", self.generator)
|
||||
_opt_element(handler, "docs", self.docs)
|
||||
|
||||
if self.cloud is not None:
|
||||
self.cloud.publish(handler)
|
||||
|
||||
ttl = self.ttl
|
||||
if isinstance(self.ttl, int):
|
||||
ttl = IntElement("ttl", ttl)
|
||||
_opt_element(handler, "tt", ttl)
|
||||
|
||||
if self.image is not None:
|
||||
self.image.publish(handler)
|
||||
|
||||
_opt_element(handler, "rating", self.rating)
|
||||
if self.textInput is not None:
|
||||
self.textInput.publish(handler)
|
||||
if self.skipHours is not None:
|
||||
self.skipHours.publish(handler)
|
||||
if self.skipDays is not None:
|
||||
self.skipDays.publish(handler)
|
||||
|
||||
for item in self.items:
|
||||
item.publish(handler)
|
||||
|
||||
handler.endElement("channel")
|
||||
handler.endElement("rss")
|
||||
|
||||
def publish_extensions(self, handler):
|
||||
# Derived classes can hook into this to insert
|
||||
# output after the three required fields.
|
||||
pass
|
||||
|
||||
|
||||
|
||||
class RSSItem(WriteXmlMixin):
|
||||
"""Publish an RSS Item"""
|
||||
element_attrs = {}
|
||||
def __init__(self,
|
||||
title = None, # string
|
||||
link = None, # url as string
|
||||
description = None, # string
|
||||
author = None, # email address as string
|
||||
categories = None, # list of string or Category
|
||||
comments = None, # url as string
|
||||
enclosure = None, # an Enclosure
|
||||
guid = None, # a unique string
|
||||
pubDate = None, # a datetime
|
||||
source = None, # a Source
|
||||
):
|
||||
|
||||
if title is None and description is None:
|
||||
raise TypeError(
|
||||
"must define at least one of 'title' or 'description'")
|
||||
self.title = title
|
||||
self.link = link
|
||||
self.description = description
|
||||
self.author = author
|
||||
if categories is None:
|
||||
categories = []
|
||||
self.categories = categories
|
||||
self.comments = comments
|
||||
self.enclosure = enclosure
|
||||
self.guid = guid
|
||||
self.pubDate = pubDate
|
||||
self.source = source
|
||||
# It sure does get tedious typing these names three times...
|
||||
|
||||
def publish(self, handler):
|
||||
handler.startElement("item", self.element_attrs)
|
||||
_opt_element(handler, "title", self.title)
|
||||
_opt_element(handler, "link", self.link)
|
||||
self.publish_extensions(handler)
|
||||
_opt_element(handler, "description", self.description)
|
||||
_opt_element(handler, "author", self.author)
|
||||
|
||||
for category in self.categories:
|
||||
if isinstance(category, basestring):
|
||||
category = Category(category)
|
||||
category.publish(handler)
|
||||
|
||||
_opt_element(handler, "comments", self.comments)
|
||||
if self.enclosure is not None:
|
||||
self.enclosure.publish(handler)
|
||||
_opt_element(handler, "guid", self.guid)
|
||||
|
||||
pubDate = self.pubDate
|
||||
if isinstance(pubDate, datetime.datetime):
|
||||
pubDate = DateElement("pubDate", pubDate)
|
||||
_opt_element(handler, "pubDate", pubDate)
|
||||
|
||||
if self.source is not None:
|
||||
self.source.publish(handler)
|
||||
|
||||
handler.endElement("item")
|
||||
|
||||
def publish_extensions(self, handler):
|
||||
# Derived classes can hook into this to insert
|
||||
# output after the title and link elements
|
||||
pass
|
@ -5,7 +5,7 @@ __copyright__ = '2008, Kovid Goyal <kovid at kovidgoyal.net>'
|
||||
Builtin recipes.
|
||||
'''
|
||||
recipe_modules = ['recipe_' + r for r in (
|
||||
'newsweek', 'atlantic', 'economist', 'portfolio',
|
||||
'newsweek', 'atlantic', 'economist', 'portfolio', 'the_register',
|
||||
'nytimes', 'usatoday', 'outlook_india', 'bbc', 'greader', 'wsj',
|
||||
'wired', 'globe_and_mail', 'smh', 'espn', 'business_week', 'miami_herald',
|
||||
'ars_technica', 'upi', 'new_yorker', 'irish_times', 'iht', 'lanacion',
|
||||
@ -86,11 +86,15 @@ def compile_recipe(src):
|
||||
match = re.search(r'coding[:=]\s*([-\w.]+)', src[:200])
|
||||
enc = match.group(1) if match else 'utf-8'
|
||||
src = src.decode(enc)
|
||||
src = re.sub(r'from __future__.*', '', src)
|
||||
f = open(temp, 'wb')
|
||||
src = 'from %s.web.feeds.news import BasicNewsRecipe, AutomaticNewsRecipe\n'%__appname__ + src
|
||||
src = 'from %s.ebooks.lrf.web.profiles import DefaultProfile, FullContentProfile\n'%__appname__ + src
|
||||
src = '# coding: utf-8\n' + src
|
||||
f.write(src.replace('from libprs500', 'from calibre').encode('utf-8'))
|
||||
src = 'from __future__ import with_statement\n' + src
|
||||
|
||||
src = src.replace('from libprs500', 'from calibre').encode('utf-8')
|
||||
f.write(src)
|
||||
f.close()
|
||||
module = imp.find_module(temp.namebase, [temp.dirname()])
|
||||
module = imp.load_module(temp.namebase, *module)
|
||||
|
@ -13,6 +13,7 @@ class SecurityWatch(BasicNewsRecipe):
|
||||
filter_regexps = [r'ad\.doubleclick']
|
||||
filter_regexps = [r'advert']
|
||||
language = _('English')
|
||||
extra_css = 'div {text-align:left}'
|
||||
|
||||
remove_tags = [dict(id='topBannerContainer'),
|
||||
dict(id='topBannerSmall'),
|
||||
@ -30,3 +31,7 @@ class SecurityWatch(BasicNewsRecipe):
|
||||
feeds = [(u'securitywatch', u'http://feeds.ziffdavisenterprise.com/RSS/security_watch/')]
|
||||
|
||||
|
||||
def postprocess_html(self, soup, first_fetch):
|
||||
for t in soup.findAll(['table', 'tr', 'td']):
|
||||
t.name = 'div'
|
||||
return soup
|
||||
|
19
src/calibre/web/feeds/recipes/recipe_the_register.py
Normal file
19
src/calibre/web/feeds/recipes/recipe_the_register.py
Normal file
@ -0,0 +1,19 @@
|
||||
from calibre.web.feeds.recipes import BasicNewsRecipe
|
||||
|
||||
class TheRegister(BasicNewsRecipe):
|
||||
title = u'The Register'
|
||||
__author__ = 'vgrama'
|
||||
language = _('English')
|
||||
oldest_article = 1
|
||||
max_articles_per_feed = 100
|
||||
use_embedded_content = False
|
||||
|
||||
feeds = [(u'Register', u'http://www.theregister.co.uk/headlines.atom')]
|
||||
|
||||
remove_tags = [
|
||||
dict(name='div', attrs={'id':'leader'}),
|
||||
dict(name='div', attrs={'id':'footer'}),
|
||||
dict(name='div', attrs={'id':'masthead'})]
|
||||
|
||||
def print_version(self, url):
|
||||
return '%s/print.html' % url
|
67
upload.py
67
upload.py
@ -4,7 +4,8 @@ __copyright__ = '2009, Kovid Goyal <kovid@kovidgoyal.net>'
|
||||
__docformat__ = 'restructuredtext en'
|
||||
|
||||
import shutil, os, glob, re, cStringIO, sys, tempfile, time, textwrap, socket, \
|
||||
struct
|
||||
struct, subprocess
|
||||
from datetime import datetime
|
||||
from setuptools.command.build_py import build_py as _build_py, convert_path
|
||||
from distutils.core import Command
|
||||
from subprocess import check_call, call, Popen
|
||||
@ -664,6 +665,7 @@ class stage3(OptionlessCommand):
|
||||
('upload_installers', None),
|
||||
('upload_user_manual', None),
|
||||
('upload_to_pypi', None),
|
||||
('upload_rss', None),
|
||||
]
|
||||
|
||||
@classmethod
|
||||
@ -706,3 +708,66 @@ class upload(OptionlessCommand):
|
||||
('stage2', None),
|
||||
('stage3', None)
|
||||
]
|
||||
|
||||
class upload_rss(OptionlessCommand):
|
||||
|
||||
from bzrlib import log as blog
|
||||
|
||||
class ChangelogFormatter(blog.LogFormatter):
|
||||
supports_tags = True
|
||||
supports_merge_revisions = False
|
||||
|
||||
def __init__(self, num_of_versions=20):
|
||||
from calibre.utils.rss_gen import RSS2
|
||||
self.num_of_versions = num_of_versions
|
||||
self.rss = RSS2(
|
||||
title = 'calibre releases',
|
||||
link = 'http://calibre.kovidgoyal.net/wiki/Changelog',
|
||||
description = 'Latest release of calibre',
|
||||
lastBuildDate = datetime.utcnow()
|
||||
)
|
||||
self.current_entry = None
|
||||
|
||||
def log_revision(self, r):
|
||||
from calibre.utils.rss_gen import RSSItem, Guid
|
||||
if len(self.rss.items) > self.num_of_versions-1:
|
||||
return
|
||||
msg = r.rev.message
|
||||
match = re.match(r'version\s+(\d+\.\d+.\d+)', msg)
|
||||
|
||||
if match:
|
||||
if self.current_entry is not None:
|
||||
mkup = '<div><ul>%s</ul></div>'
|
||||
self.current_entry.description = mkup%(''.join(
|
||||
self.current_entry.description))
|
||||
|
||||
self.rss.items.append(self.current_entry)
|
||||
timestamp = r.rev.timezone + r.rev.timestamp
|
||||
self.current_entry = RSSItem(
|
||||
title = 'calibre %s released'%match.group(1),
|
||||
link = 'http://calibre.kovidgoyal.net/download',
|
||||
guid = Guid(match.group(), False),
|
||||
pubDate = datetime(*time.gmtime(timestamp)[:6]),
|
||||
description = []
|
||||
)
|
||||
elif self.current_entry is not None:
|
||||
if re.search(r'[a-zA-Z]', msg) and len(msg.strip()) > 5:
|
||||
if 'translation' not in msg and not msg.startswith('IGN'):
|
||||
msg = msg.replace('<', '<').replace('>', '>')
|
||||
msg = re.sub('#(\d+)', r'<a href="http://calibre.kovidgoyal.net/ticket/\1">#\1</a>',
|
||||
msg)
|
||||
|
||||
self.current_entry.description.append(
|
||||
'<li>%s</li>'%msg.strip())
|
||||
|
||||
|
||||
def run(self):
|
||||
from bzrlib import log, branch
|
||||
bzr_path = os.path.expanduser('~/work/calibre')
|
||||
b = branch.Branch.open(bzr_path)
|
||||
lf = upload_rss.ChangelogFormatter()
|
||||
log.show_log(b, lf)
|
||||
lf.rss.write_xml(open('/tmp/releases.xml', 'wb'))
|
||||
subprocess.check_call('scp /tmp/releases.xml divok:/var/www/calibre.kovidgoyal.net/htdocs/downloads'.split())
|
||||
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user