From ff7969051eada1a17d7ada9d4d5d158228577aa3 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Sat, 14 Mar 2009 18:39:46 -0700 Subject: [PATCH 1/8] IGN:... --- src/calibre/library/server.py | 2 +- src/calibre/utils/Zeroconf.py | 6 +++++- src/calibre/utils/mdns.py | 39 +---------------------------------- 3 files changed, 7 insertions(+), 40 deletions(-) diff --git a/src/calibre/library/server.py b/src/calibre/library/server.py index 930a644be8..4ba6253819 100644 --- a/src/calibre/library/server.py +++ b/src/calibre/library/server.py @@ -7,7 +7,7 @@ __docformat__ = 'restructuredtext en' HTTP server for remote access to the calibre database. ''' -import sys, textwrap, operator, os, re, logging, subprocess +import sys, textwrap, operator, os, re, logging from itertools import repeat from logging.handlers import RotatingFileHandler from datetime import datetime diff --git a/src/calibre/utils/Zeroconf.py b/src/calibre/utils/Zeroconf.py index 610c353fca..3bdb992685 100755 --- a/src/calibre/utils/Zeroconf.py +++ b/src/calibre/utils/Zeroconf.py @@ -933,7 +933,11 @@ class Reaper(threading.Thread): def run(self): while 1: - self.zeroconf.wait(10 * 1000) + try: + self.zeroconf.wait(10 * 1000) + except TypeError: # By Kovid + globals()['_GLOBAL_DONE'] = 1 + return if globals()['_GLOBAL_DONE']: return now = currentTimeMillis() diff --git a/src/calibre/utils/mdns.py b/src/calibre/utils/mdns.py index f941446202..033b903e11 100644 --- a/src/calibre/utils/mdns.py +++ b/src/calibre/utils/mdns.py @@ -41,7 +41,7 @@ def publish(desc, type, port, properties=None, add_hostname=True): ''' port = int(port) server = start_server() - hostname = socket.gethostname() + hostname = socket.gethostname().partition('.')[0] if add_hostname: desc += ' (on %s)'%hostname local_ip = get_external_ip() @@ -58,40 +58,3 @@ def stop_server(): global _server if _server is not None: _server.close() - -''' -class Publish(object): - - def __init__(self, desc, name, port, txt=''): - self.desc = desc - self.name = name - self.port = port - self.txt = txt - - def start(self): - if iswindows: - return - if isosx: - args = ['dns-sd', self.desc, self.name, '.', self.port] - else: - args = ['avahi-publish-service', self.desc, self.name, self.port] - if self.txt: - args.append(self.txt) - - self.process = subprocess.Popen(args) - - def stop(self): - if iswindows: - pass - else: - process = getattr(self, 'process', None) - if process is not None: - process.poll() - if process.returncode is not None: - process.terminate() - process.poll() - if process.returncode is not None: - process.kill() - -def publish(desc, name, port, txt): -''' \ No newline at end of file From 570a9eaeac458cdd81bfc92a2dc722ea11886c68 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Sun, 15 Mar 2009 09:14:59 -0700 Subject: [PATCH 2/8] Fix #2076 (Error during Recursive Search) --- src/calibre/gui2/add.py | 5 ++++- src/calibre/manual/faq.rst | 7 ++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/calibre/gui2/add.py b/src/calibre/gui2/add.py index 2f4ba281fd..948cf4f3af 100644 --- a/src/calibre/gui2/add.py +++ b/src/calibre/gui2/add.py @@ -224,7 +224,10 @@ class AddRecursive(Add): files = _('

Books with the same title as the following already ' 'exist in the database. Add them anyway?

', parent=self._parent) diff --git a/src/calibre/manual/faq.rst b/src/calibre/manual/faq.rst index 0e5d5fffc8..7ca4b1b876 100644 --- a/src/calibre/manual/faq.rst +++ b/src/calibre/manual/faq.rst @@ -123,15 +123,12 @@ turned into a collection on the reader. Note that the PRS-500 does not support c How do I use |app| with my iPhone? ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ First install the Stanza reader on your iPhone from http://www.lexcycle.com . Then, + * Set the output format for calibre to EPUB (The output format can be set next to the big red heart) * Convert the books you want to read on your iPhone to EPUB format by selecting them and clicking the Convert button. * Turn on the Content Server in |app|'s preferences and leave |app| running. - * Now you should be able to access your books on your iPhone. If not, try the following: - In the Stanza reader on your iPhone, add a new catalog. The URL of the catalog is of the form - ``http://10.34.56.89:8080/stanza``, where you should replace the IP address ``10.34.56.89`` - with the IP address of your computer. Stanza will the use the |app| content server to access all the - EPUB books in your |app| database. +Now you should be able to access your books on your iPhone. Library Management ------------------ From 04f0e389af1532b33e6a7ce659b9d7b70e780000 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Sun, 15 Mar 2009 11:54:46 -0700 Subject: [PATCH 3/8] Fix #2074 (Clicking "table of contents" on a "lit") --- src/calibre/ebooks/epub/iterator.py | 8 +- src/calibre/ebooks/metadata/opf2.py | 2 + src/calibre/gui2/dialogs/metadata_single.ui | 459 ++++++++++---------- src/calibre/trac/plugins/download.py | 4 +- 4 files changed, 241 insertions(+), 232 deletions(-) diff --git a/src/calibre/ebooks/epub/iterator.py b/src/calibre/ebooks/epub/iterator.py index e953cbda51..880377f11d 100644 --- a/src/calibre/ebooks/epub/iterator.py +++ b/src/calibre/ebooks/epub/iterator.py @@ -118,10 +118,16 @@ class EbookIterator(object): self.spine = [SpineItem(i.path) for i in self.opf.spine] cover = self.opf.cover - if os.path.splitext(self.pathtoebook)[1].lower() in ('.lit', '.mobi', '.prc') and cover: + if os.path.splitext(self.pathtoebook)[1].lower() in \ + ('.lit', '.mobi', '.prc') and cover: cfile = os.path.join(os.path.dirname(self.spine[0]), 'calibre_ei_cover.html') open(cfile, 'wb').write(TITLEPAGE%cover) self.spine[0:0] = [SpineItem(cfile)] + + if self.opf.path_to_html_toc is not None and \ + self.opf.path_to_html_toc not in self.spine: + self.spine.append(SpineItem(self.opf.path_to_html_toc)) + sizes = [i.character_count for i in self.spine] self.pages = [math.ceil(i/float(self.CHARACTERS_PER_PAGE)) for i in sizes] diff --git a/src/calibre/ebooks/metadata/opf2.py b/src/calibre/ebooks/metadata/opf2.py index c172d26e1a..86a1129b54 100644 --- a/src/calibre/ebooks/metadata/opf2.py +++ b/src/calibre/ebooks/metadata/opf2.py @@ -444,6 +444,7 @@ class OPF(object): if not hasattr(stream, 'read'): stream = open(stream, 'rb') self.basedir = self.base_dir = basedir + self.path_to_html_toc = None raw, self.encoding = xml_to_unicode(stream.read(), strip_encoding_pats=True, resolve_entities=True) raw = raw[raw.find('<'):] self.root = etree.fromstring(raw, self.PARSER) @@ -495,6 +496,7 @@ class OPF(object): if f: self.toc.read_ncx_toc(f[0]) else: + self.path_to_html_toc = toc self.toc.read_html_toc(toc) except: pass diff --git a/src/calibre/gui2/dialogs/metadata_single.ui b/src/calibre/gui2/dialogs/metadata_single.ui index c693cf3eea..8a5fc4f99d 100644 --- a/src/calibre/gui2/dialogs/metadata_single.ui +++ b/src/calibre/gui2/dialogs/metadata_single.ui @@ -1,7 +1,8 @@ - + + MetadataSingleDialog - - + + 0 0 @@ -9,36 +10,36 @@ 750 - - + + 0 0 - + Edit Meta Information - - + + :/images/edit_input.svg:/images/edit_input.svg - + true - + true - + - - + + QFrame::NoFrame - + true - - + + 0 0 @@ -46,65 +47,65 @@ 710 - - + + 0 - - + + 800 665 - + - - + + Qt::Horizontal - - + + - - + + Meta information - - - - + + + + &Title: - + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - + title - - - + + + Change the title of this book - - - + + + Swap the author and title - + ... - - + + :/images/swap.svg:/images/swap.svg - + 16 16 @@ -112,305 +113,305 @@ - - - + + + &Author(s): - + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - + authors - - - + + + Author S&ort: - + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - + author_sort - - + + - - + + Specify how the author(s) of this book should be sorted. For example Charles Dickens should be sorted as Dickens, Charles. - - + + Automatically create the author sort entry based on the current author entry - + ... - - + + :/images/auto_author_sort.svg:/images/auto_author_sort.svg - - - + + + &Rating: - + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - + rating - - - + + + Rating of this book. 0-5 stars - + Rating of this book. 0-5 stars - + QAbstractSpinBox::PlusMinus - + stars - + 5 - - - + + + &Publisher: - + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - + publisher - - - + + + Ta&gs: - + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - + tags - - + + - - - Tags categorize the book. This is particularly useful while searching. <br><br>They can be any words or phrases, separated by commas. + + + Tags categorize the book. This is particularly useful while searching. <br><br>They can be any words or phrases, separated by commas. - - + + Open Tag Editor - + Open Tag Editor - - + + :/images/chapters.svg:/images/chapters.svg - - - + + + &Series: - + Qt::PlainText - + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - + series - - - + + + 5 - - - + + + 0 0 - + List of known series. You can add new series. - + List of known series. You can add new series. - + true - + QComboBox::InsertAlphabetically - + QComboBox::AdjustToContents - - + + Remove unused series (Series that have no books) - + ... - - + + :/images/trash.svg:/images/trash.svg - - - + + + false - + Series index. - + Series index. - + Book - + 1 - + 10000 - - - + + + IS&BN: - + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - + isbn - - + + - - - + + + true - - + + - - + + Comments - - - + + + - - - Fetch metadata from server + + + &Fetch metadata from server - - + + - - - + + + 0 0 - + Available Formats - + - - - - - + + + + + 0 0 - + 16777215 130 - + 64 64 @@ -418,19 +419,19 @@ - - - + + + Add a new format for this book to the database - + ... - - + + :/images/add_book.svg:/images/add_book.svg - + 32 32 @@ -438,19 +439,19 @@ - - - + + + Remove the selected formats for this book from the database. - + ... - - + + :/images/trash.svg:/images/trash.svg - + 32 32 @@ -458,19 +459,19 @@ - - - + + + Set the cover for the book from the selected format - + ... - - + + :/images/book.svg:/images/book.svg - + 32 32 @@ -485,96 +486,96 @@ - - - + + + 0 10 - + Book Cover - + - - - + + + 0 0 - + - - :/images/book.svg + + :/images/book.svg - + true - - + + 6 - + QLayout::SetMaximumSize - + 0 - - + + Change &cover image: - + cover_path - - + + 6 - + 0 - - + + true - - + + Browse for an image to use as the cover of this book. - + ... - - + + :/images/document_open.svg:/images/document_open.svg - - + + Reset cover to default - + ... - - + + :/images/trash.svg:/images/trash.svg @@ -584,21 +585,21 @@ - + - - - Fetch cover image from server + + + Fetch &cover image from server - - + + Change the username and/or password for your account at LibraryThing.com - - Change password + + Change &password @@ -619,11 +620,11 @@ - - + + Qt::Horizontal - + QDialogButtonBox::Cancel|QDialogButtonBox::Ok @@ -666,7 +667,7 @@ button_box - + @@ -675,11 +676,11 @@ MetadataSingleDialog accept() - + 261 710 - + 157 274 @@ -691,11 +692,11 @@ MetadataSingleDialog reject() - + 329 710 - + 286 274 diff --git a/src/calibre/trac/plugins/download.py b/src/calibre/trac/plugins/download.py index ff98123f1d..00bea7d65f 100644 --- a/src/calibre/trac/plugins/download.py +++ b/src/calibre/trac/plugins/download.py @@ -177,11 +177,11 @@ else: compatibility='%s works on OS X Tiger and above.'%(__appname__,), path=MOBILEREAD+file, app=__appname__, note=Markup(\ - ''' + u'''
  1. Before trying to use the command line tools, you must run the app at least once. This will ask you for you password and then setup the symbolic links for the command line tools.
  2. The app cannot be run from within the dmg. You must drag it to a folder on your filesystem (The Desktop, Applications, wherever).
  3. -
  4. In order for localization of the user interface in your language, select your language in the configuration dialog (by clicking the hammer icon next to the search bar) and select your language.
  5. +
  6. In order for localization of the user interface in your language, select your language in the preferences (by pressing u\2318+P) and select your language.
''')) return 'binary.html', data, None From 7294199798db2f085ae06cd522650d5577916c7b Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Sun, 15 Mar 2009 12:09:33 -0700 Subject: [PATCH 4/8] Fix #2066 (AttributeError: 'BookHeader' object has no attribute 'first_image_index') --- src/calibre/ebooks/mobi/reader.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/calibre/ebooks/mobi/reader.py b/src/calibre/ebooks/mobi/reader.py index 967a68aea8..7fab3ac2d8 100644 --- a/src/calibre/ebooks/mobi/reader.py +++ b/src/calibre/ebooks/mobi/reader.py @@ -89,6 +89,8 @@ class BookHeader(object): self.sublanguage = 'NEUTRAL' self.exth_flag, self.exth = 0, None self.ancient = True + self.first_image_index = -1 + self.mobi_version = 1 else: self.ancient = False self.doctype = raw[16:20] @@ -519,7 +521,7 @@ class MobiReader(object): os.makedirs(output_dir) image_index = 0 self.image_names = [] - start = self.book_header.first_image_index + start = getattr(self.book_header, 'first_image_index', -1) if start > self.num_sections or start < 0: # BAEN PRC files have bad headers start=0 From fad3f7a7d0d55b30929facf2d68acf6bcdd142cf Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Sun, 15 Mar 2009 13:09:45 -0700 Subject: [PATCH 5/8] Fix #2058 (Calibre reads Kindle metadata wrong) --- src/calibre/__init__.py | 4 ++++ src/calibre/devices/kindle/driver.py | 17 ++++++++++++-- src/calibre/devices/mime.py | 35 ++++++++++++++-------------- src/calibre/devices/usbms/driver.py | 15 +++++++----- src/calibre/ebooks/metadata/meta.py | 8 ++++--- 5 files changed, 51 insertions(+), 28 deletions(-) diff --git a/src/calibre/__init__.py b/src/calibre/__init__.py index 214c52f14a..cb53dff24b 100644 --- a/src/calibre/__init__.py +++ b/src/calibre/__init__.py @@ -26,12 +26,16 @@ mimetypes.add_type('text/x-sony-bbeb+xml', '.lrs') mimetypes.add_type('application/xhtml+xml', '.xhtml') mimetypes.add_type('image/svg+xml', '.svg') mimetypes.add_type('application/x-sony-bbeb', '.lrf') +mimetypes.add_type('application/x-sony-bbeb', '.lrx') mimetypes.add_type('application/x-dtbncx+xml', '.ncx') 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') +mimetypes.add_type('application/mobi', '.mobi') +mimetypes.add_type('application/mobi', '.prc') +mimetypes.add_type('application/mobi', '.azw') guess_type = mimetypes.guess_type import cssutils cssutils.log.setLevel(logging.WARN) diff --git a/src/calibre/devices/kindle/driver.py b/src/calibre/devices/kindle/driver.py index e35d5f8cd3..14b056944b 100755 --- a/src/calibre/devices/kindle/driver.py +++ b/src/calibre/devices/kindle/driver.py @@ -4,9 +4,9 @@ __copyright__ = '2009, John Schember ' Device driver for Amazon's Kindle ''' -import os +import os, re -from calibre.devices.usbms.driver import USBMS +from calibre.devices.usbms.driver import USBMS, metadata_from_formats class KINDLE(USBMS): # Ordered list of supported formats @@ -29,6 +29,9 @@ class KINDLE(USBMS): EBOOK_DIR_MAIN = "documents" EBOOK_DIR_CARD = "documents" SUPPORTS_SUB_DIRS = True + + WIRELESS_FILE_NAME_PATTERN = re.compile( + r'(?P[^-]+)-asin_(?P<asin>[a-zA-Z\d]{10,})-type_(?P<type>\w{4})-v_(?P<index>\d+).*') def delete_books(self, paths, end_session=True): for path in paths: @@ -40,6 +43,16 @@ class KINDLE(USBMS): # Delete the ebook auxiliary file if os.path.exists(filepath + '.mbp'): os.unlink(filepath + '.mbp') + + @classmethod + def metadata_from_path(cls, path): + mi = metadata_from_formats([path]) + if mi.title == _('Unknown') or ('-asin' in mi.title and '-type' in mi.title): + match = cls.WIRELESS_FILE_NAME_PATTERN.match(os.path.basename(path)) + if match is not None: + mi.title = match.group('title') + return mi + class KINDLE2(KINDLE): diff --git a/src/calibre/devices/mime.py b/src/calibre/devices/mime.py index 0035a7b8d7..140c822ca9 100644 --- a/src/calibre/devices/mime.py +++ b/src/calibre/devices/mime.py @@ -1,19 +1,20 @@ -__license__ = 'GPL v3' -__copyright__ = '2009, John Schember <john at nachtimwald.com>' -''' -Global Mime mapping of ebook types. -''' +from __future__ import with_statement +__license__ = 'GPL 3' +__copyright__ = '2009, Kovid Goyal <kovid@kovidgoyal.net>' +__docformat__ = 'restructuredtext en' -MIME_MAP = { - 'azw' : 'application/azw', - 'epub' : 'application/epub+zip', - 'html' : 'text/html', - 'lrf' : 'application/x-sony-bbeb', - 'lrx' : 'application/x-sony-bbeb', - 'mobi' : 'application/mobi', - 'pdf' : 'application/pdf', - 'prc' : 'application/prc', - 'rtf' : 'application/rtf', - 'txt' : 'text/plain', - } +from calibre import guess_type +def _mt(path): + mt = guess_type(path)[0] + if not mt: + mt = 'application/octet-stream' + return mt + +def mime_type_ext(ext): + if not ext.startswith('.'): + ext = '.'+ext + return _mt('a'+ext) + +def mime_type_path(path): + return _mt(path) \ No newline at end of file diff --git a/src/calibre/devices/usbms/driver.py b/src/calibre/devices/usbms/driver.py index 0c73c4412c..4740bc54de 100644 --- a/src/calibre/devices/usbms/driver.py +++ b/src/calibre/devices/usbms/driver.py @@ -15,7 +15,7 @@ from calibre.ebooks.metadata import authors_to_string from calibre.devices.usbms.device import Device from calibre.devices.usbms.books import BookList, Book from calibre.devices.errors import FreeSpaceError, PathError -from calibre.devices.mime import MIME_MAP +from calibre.devices.mime import mime_type_ext class File(object): def __init__(self, path): @@ -216,14 +216,17 @@ class USBMS(Device): if not os.path.isdir(path): os.utime(path, None) + @classmethod + def metadata_from_path(cls, path): + return metadata_from_formats([path]) + @classmethod def book_from_path(cls, path): fileext = path_to_ext(path) - - mi = metadata_from_formats([path]) - mime = MIME_MAP[fileext] if fileext in MIME_MAP.keys() else 'Unknown' - + mi = cls.metadata_from_path(path) + mime = mime_type_ext(fileext) authors = authors_to_string(mi.authors) - return Book(path, mi.title, authors, mime) + book = Book(path, mi.title, authors, mime) + return book diff --git a/src/calibre/ebooks/metadata/meta.py b/src/calibre/ebooks/metadata/meta.py index 43053a43b9..de7ac8eeea 100644 --- a/src/calibre/ebooks/metadata/meta.py +++ b/src/calibre/ebooks/metadata/meta.py @@ -15,7 +15,7 @@ _METADATA_PRIORITIES = [ 'html', 'htm', 'xhtml', 'xhtm', 'rtf', 'fb2', 'pdf', 'prc', 'odt', 'epub', 'lit', 'lrx', 'lrf', 'mobi', - 'rb', 'imp' + 'rb', 'imp', 'azw' ] # The priorities for loading metadata from different file types @@ -41,7 +41,9 @@ def metadata_from_formats(formats): for path, ext in zip(formats, extensions): with open(path, 'rb') as stream: try: - mi.smart_update(get_metadata(stream, stream_type=ext, use_libprs_metadata=True)) + newmi = get_metadata(stream, stream_type=ext, + use_libprs_metadata=True) + mi.smart_update(newmi) except: continue if getattr(mi, 'application_id', None) is not None: @@ -58,7 +60,7 @@ def get_metadata(stream, stream_type='lrf', use_libprs_metadata=False): if stream_type: stream_type = stream_type.lower() if stream_type in ('html', 'html', 'xhtml', 'xhtm', 'xml'): stream_type = 'html' - if stream_type in ('mobi', 'prc'): + if stream_type in ('mobi', 'prc', 'azw'): stream_type = 'mobi' if stream_type in ('odt', 'ods', 'odp', 'odg', 'odf'): stream_type = 'odt' From f785c7688309d605b6c644ecfe80dcec3025447c Mon Sep 17 00:00:00 2001 From: Kovid Goyal <kovid@kovidgoyal.net> Date: Sun, 15 Mar 2009 13:46:58 -0700 Subject: [PATCH 6/8] Add vim like shortcuts to ebook-viewer for scrolling. Fixes #2083 (Add Keyboard shorcuts to Viewer) --- src/calibre/gui2/viewer/documentview.py | 24 +++++++++++++++++++++++- src/calibre/gui2/viewer/main.py | 6 ++++++ 2 files changed, 29 insertions(+), 1 deletion(-) diff --git a/src/calibre/gui2/viewer/documentview.py b/src/calibre/gui2/viewer/documentview.py index 3745036249..6b821eed55 100644 --- a/src/calibre/gui2/viewer/documentview.py +++ b/src/calibre/gui2/viewer/documentview.py @@ -450,7 +450,9 @@ class DocumentView(QWebView): self.manager.scrolled(self.scroll_fraction) def wheel_event(self, down=True): - QWebView.wheelEvent(self, QWheelEvent(QPoint(100, 100), (-120 if down else 120), Qt.NoButton, Qt.NoModifier)) + QWebView.wheelEvent(self, + QWheelEvent(QPoint(100, 100), (-120 if down else 120), + Qt.NoButton, Qt.NoModifier)) def next_page(self): if self.document.at_bottom: @@ -538,6 +540,26 @@ class DocumentView(QWebView): self.next_page() elif key in [Qt.Key_PageUp, Qt.Key_Backspace, Qt.Key_Up]: self.previous_page() + elif key in [Qt.Key_Home]: + if event.modifiers() & Qt.ControlModifier: + if self.manager is not None: + self.manager.goto_start() + else: + self.scroll_to(0) + elif key in [Qt.Key_End]: + if event.modifiers() & Qt.ControlModifier: + if self.manager is not None: + self.manager.goto_end() + else: + self.scroll_to(1) + elif key in [Qt.Key_J]: + self.wheel_event() + elif key in [Qt.Key_K]: + self.wheel_event(down=False) + elif key in [Qt.Key_H]: + self.scroll_by(x=-15) + elif key in [Qt.Key_L]: + self.scroll_by(x=15) else: return QWebView.keyPressEvent(self, event) diff --git a/src/calibre/gui2/viewer/main.py b/src/calibre/gui2/viewer/main.py index b4512cd0fc..14cfbc80a1 100644 --- a/src/calibre/gui2/viewer/main.py +++ b/src/calibre/gui2/viewer/main.py @@ -325,6 +325,12 @@ class EbookViewer(MainWindow, Ui_EbookViewer): pos = self.history.forward() if pos is not None: self.goto_page(pos) + + def goto_start(self): + self.goto_page(1) + + def goto_end(self): + self.goto_page(self.pos.maximum()) def goto_page(self, new_page): if self.current_page is not None: From 6e5547011a18d4a314ec18b45789861681717f17 Mon Sep 17 00:00:00 2001 From: Kovid Goyal <kovid@kovidgoyal.net> Date: Sun, 15 Mar 2009 14:20:12 -0700 Subject: [PATCH 7/8] Implement support for reading metadata from Amazon Topaz books --- src/calibre/customize/builtins.py | 13 ++++++++- src/calibre/ebooks/metadata/topaz.py | 40 ++++++++++++++++++++++++++++ 2 files changed, 52 insertions(+), 1 deletion(-) create mode 100644 src/calibre/ebooks/metadata/topaz.py diff --git a/src/calibre/customize/builtins.py b/src/calibre/customize/builtins.py index 14d3c79062..a087e7f36d 100644 --- a/src/calibre/customize/builtins.py +++ b/src/calibre/customize/builtins.py @@ -132,13 +132,24 @@ class HTMLMetadataReader(MetadataReaderPlugin): class MOBIMetadataReader(MetadataReaderPlugin): name = 'Read MOBI metadata' - file_types = set(['mobi', 'prc', '.azw']) + file_types = set(['mobi', 'prc', 'azw']) description = _('Read metadata from %s files')%'MOBI' def get_metadata(self, stream, ftype): from calibre.ebooks.mobi.reader import get_metadata return get_metadata(stream) + +class TOPAZMetadataReader(MetadataReaderPlugin): + + name = 'Read Topaz metadata' + file_types = set(['tpz', 'azw1']) + description = _('Read metadata from %s files')%'MOBI' + + def get_metadata(self, stream, ftype): + from calibre.ebooks.metadata.topaz import get_metadata + return get_metadata(stream) + class ODTMetadataReader(MetadataReaderPlugin): name = 'Read ODT metadata' diff --git a/src/calibre/ebooks/metadata/topaz.py b/src/calibre/ebooks/metadata/topaz.py new file mode 100644 index 0000000000..55eb9d6e69 --- /dev/null +++ b/src/calibre/ebooks/metadata/topaz.py @@ -0,0 +1,40 @@ +from __future__ import with_statement +__license__ = 'GPL 3' +__copyright__ = '2009, Kovid Goyal <kovid@kovidgoyal.net>' +__docformat__ = 'restructuredtext en' + +''' Read metadata from Amazon's topaz format ''' + +def read_record(raw, name): + idx = raw.find(name) + if idx > -1: + length = ord(raw[idx+len(name)]) + return raw[idx+len(name)+1:idx+len(name)+1+length] + +def get_metadata(stream): + raw = stream.read(8*1024) + if not raw.startswith('TPZ'): + raise ValueError('Not a Topaz file') + first = raw.find('metadata') + if first < 0: + raise ValueError('Invalid Topaz file') + second = raw.find('metadata', first+10) + if second < 0: + raise ValueError('Invalid Topaz file') + raw = raw[second:second+1000] + authors = read_record(raw, 'Authors') + if authors: + authors = authors.decode('utf-8', 'replace').split(';') + else: + authors = [_('Unknown')] + title = read_record(raw, 'Title') + if title: + title = title.decode('utf-8', 'replace') + else: + raise ValueError('No metadata in file') + from calibre.ebooks.metadata import MetaInformation + return MetaInformation(title, authors) + +if __name__ == '__main__': + import sys + print get_metadata(open(sys.argv[1], 'rb')) \ No newline at end of file From 08b3ee1a791ddf351dcad753e1990a0ef53dedc8 Mon Sep 17 00:00:00 2001 From: Kovid Goyal <kovid@kovidgoyal.net> Date: Sun, 15 Mar 2009 14:21:36 -0700 Subject: [PATCH 8/8] Fix #2084 (Updated New Scientist recipe) --- .../web/feeds/recipes/recipe_new_scientist.py | 117 +++++++++++------- 1 file changed, 69 insertions(+), 48 deletions(-) diff --git a/src/calibre/web/feeds/recipes/recipe_new_scientist.py b/src/calibre/web/feeds/recipes/recipe_new_scientist.py index b87883ef6b..acd98a8c6e 100644 --- a/src/calibre/web/feeds/recipes/recipe_new_scientist.py +++ b/src/calibre/web/feeds/recipes/recipe_new_scientist.py @@ -1,48 +1,69 @@ -#!/usr/bin/env python - -__license__ = 'GPL v3' -__copyright__ = '2008, Darko Miletic <darko.miletic at gmail.com>' -''' -newscientist.com -''' -#!/usr/bin/env python - -__license__ = 'GPL v3' -__copyright__ = '2008, Darko Miletic <darko.miletic at gmail.com>, AprilHare' -''' -newscientist.com -''' - -from calibre.web.feeds.news import BasicNewsRecipe - -class NewScientist(BasicNewsRecipe): - title = u'New Scientist - Online News' - __author__ = 'Darko Miletic' - description = 'News from Science' - language = _('English') - oldest_article = 7 - max_articles_per_feed = 100 - no_stylesheets = True - use_embedded_content = False - - keep_only_tags = [ - dict(name='div' , attrs={'id':'pgtop' }) - ,dict(name='div' , attrs={'id':'maincol' }) - ] - remove_tags = [ - dict(name='div' , attrs={'class':'hldBd' }) - ,dict(name='div' , attrs={'id':'compnl' }) - ,dict(name='div' , attrs={'id':'artIssueInfo' }) - ] - - feeds = [ - (u'Latest Headlines' , u'http://feeds.newscientist.com/science-news' ) - ,(u'Magazine' , u'http://www.newscientist.com/feed/magazine' ) - ,(u'Health' , u'http://www.newscientist.com/feed/view?id=2&type=channel' ) - ,(u'Life' , u'http://www.newscientist.com/feed/view?id=3&type=channel' ) - ,(u'Space' , u'http://www.newscientist.com/feed/view?id=6&type=channel' ) - ,(u'Physics and Mathematics' , u'http://www.newscientist.com/feed/view?id=4&type=channel' ) - ,(u'Environment' , u'http://www.newscientist.com/feed/view?id=1&type=channel' ) - ,(u'Science in Society' , u'http://www.newscientist.com/feed/view?id=5&type=channel' ) - ,(u'Tech' , u'http://www.newscientist.com/feed/view?id=7&type=channel' ) - ] \ No newline at end of file +#!/usr/bin/env python + +__license__ = 'GPL v3' +__copyright__ = '2008-2009, AprilHare, Darko Miletic <darko.miletic at gmail.com>' +''' +newscientist.com +''' + +from calibre.web.feeds.news import BasicNewsRecipe + +class NewScientist(BasicNewsRecipe): + title = 'New Scientist - Online News' + __author__ = 'Darko Miletic' + description = 'Science news and science articles from New Scientist.' + language = _('English') + publisher = 'New Scientist' + category = 'science news, science articles, science jobs, drugs, cancer, depression, computer software, sex' + delay = 3 + oldest_article = 7 + max_articles_per_feed = 100 + no_stylesheets = True + use_embedded_content = False + remove_javascript = True + encoding = 'utf-8' + + html2lrf_options = [ + '--comment', description + , '--category', category + , '--publisher', publisher + ] + + html2epub_options = 'publisher="' + publisher + '"\ncomments="' + description + '"\ntags="' + category + '"' + + keep_only_tags = [dict(name='div', attrs={'id':['pgtop','maincol']})] + + remove_tags = [ + dict(name='div', attrs={'class':['hldBd','adline','pnl','infotext' ]}) + ,dict(name='div', attrs={'id' :['compnl','artIssueInfo','artTools']}) + ,dict(name='p' , attrs={'class':['marker','infotext' ]}) + ] + + feeds = [ + (u'Latest Headlines' , u'http://feeds.newscientist.com/science-news' ) + ,(u'Magazine' , u'http://www.newscientist.com/feed/magazine' ) + ,(u'Health' , u'http://www.newscientist.com/feed/view?id=2&type=channel' ) + ,(u'Life' , u'http://www.newscientist.com/feed/view?id=3&type=channel' ) + ,(u'Space' , u'http://www.newscientist.com/feed/view?id=6&type=channel' ) + ,(u'Physics and Mathematics' , u'http://www.newscientist.com/feed/view?id=4&type=channel' ) + ,(u'Environment' , u'http://www.newscientist.com/feed/view?id=1&type=channel' ) + ,(u'Science in Society' , u'http://www.newscientist.com/feed/view?id=5&type=channel' ) + ,(u'Tech' , u'http://www.newscientist.com/feed/view?id=7&type=channel' ) + ] + + def get_article_url(self, article): + url = article.get('link', None) + raw = article.get('description', None) + rsoup = self.index_to_soup(raw) + atags = rsoup.findAll('a',href=True) + for atag in atags: + if atag['href'].startswith('http://res.feedsportal.com/viral/sendemail2.html?'): + st, sep, rest = atag['href'].partition('&link=') + real_url, sep2, drest = rest.partition('" target=') + return real_url + return url + + def print_version(self, url): + rawurl, sep, params = url.partition('?') + return rawurl + '?full=true&print=true' + \ No newline at end of file