Pull from trunk

This commit is contained in:
Kovid Goyal 2009-03-15 14:57:34 -07:00
commit c29cff431a
20 changed files with 458 additions and 357 deletions

View File

@ -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)

View File

@ -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'

View File

@ -4,9 +4,9 @@ __copyright__ = '2009, John Schember <john at nachtimwald.com>'
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<title>[^-]+)-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):

View File

@ -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)

View File

@ -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):
@ -226,14 +226,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

View File

@ -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]

View File

@ -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'

View File

@ -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

View File

@ -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'))

View File

@ -92,6 +92,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]
@ -537,7 +539,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

View File

@ -224,7 +224,10 @@ class AddRecursive(Add):
files = _('<p>Books with the same title as the following already '
'exist in the database. Add them anyway?<ul>')
for mi in self.duplicates:
files += '<li>'+mi[0].title+'</li>\n'
title = mi[0].title
if not isinstance(title, unicode):
title = title.decode(preferred_encoding, 'replace')
files += '<li>'+title+'</li>\n'
d = WarningDialog (_('Duplicates found!'),
_('Duplicates found!'),
files+'</ul></p>', parent=self._parent)

View File

@ -1,7 +1,8 @@
<ui version="4.0" >
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>MetadataSingleDialog</class>
<widget class="QDialog" name="MetadataSingleDialog" >
<property name="geometry" >
<widget class="QDialog" name="MetadataSingleDialog">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
@ -9,36 +10,36 @@
<height>750</height>
</rect>
</property>
<property name="sizePolicy" >
<sizepolicy vsizetype="MinimumExpanding" hsizetype="MinimumExpanding" >
<property name="sizePolicy">
<sizepolicy hsizetype="MinimumExpanding" vsizetype="MinimumExpanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="windowTitle" >
<property name="windowTitle">
<string>Edit Meta Information</string>
</property>
<property name="windowIcon" >
<iconset resource="../images.qrc" >
<property name="windowIcon">
<iconset resource="../images.qrc">
<normaloff>:/images/edit_input.svg</normaloff>:/images/edit_input.svg</iconset>
</property>
<property name="sizeGripEnabled" >
<property name="sizeGripEnabled">
<bool>true</bool>
</property>
<property name="modal" >
<property name="modal">
<bool>true</bool>
</property>
<layout class="QVBoxLayout" name="verticalLayout_6" >
<layout class="QVBoxLayout" name="verticalLayout_6">
<item>
<widget class="QScrollArea" name="scrollArea" >
<property name="frameShape" >
<widget class="QScrollArea" name="scrollArea">
<property name="frameShape">
<enum>QFrame::NoFrame</enum>
</property>
<property name="widgetResizable" >
<property name="widgetResizable">
<bool>true</bool>
</property>
<widget class="QWidget" name="scrollAreaWidgetContents" >
<property name="geometry" >
<widget class="QWidget" name="scrollAreaWidgetContents">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
@ -46,65 +47,65 @@
<height>710</height>
</rect>
</property>
<layout class="QVBoxLayout" name="verticalLayout_5" >
<property name="margin" >
<layout class="QVBoxLayout" name="verticalLayout_5">
<property name="margin">
<number>0</number>
</property>
<item>
<widget class="QWidget" native="1" name="central_widget" >
<property name="minimumSize" >
<widget class="QWidget" name="central_widget" native="true">
<property name="minimumSize">
<size>
<width>800</width>
<height>665</height>
</size>
</property>
<layout class="QVBoxLayout" name="verticalLayout_3" >
<layout class="QVBoxLayout" name="verticalLayout_3">
<item>
<widget class="QSplitter" name="splitter" >
<property name="orientation" >
<widget class="QSplitter" name="splitter">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<widget class="QWidget" name="layoutWidget" >
<layout class="QVBoxLayout" >
<widget class="QWidget" name="layoutWidget">
<layout class="QVBoxLayout">
<item>
<widget class="QGroupBox" name="meta_box" >
<property name="title" >
<widget class="QGroupBox" name="meta_box">
<property name="title">
<string>Meta information</string>
</property>
<layout class="QGridLayout" name="gridLayout_3" >
<item row="0" column="0" >
<widget class="QLabel" name="label" >
<property name="text" >
<layout class="QGridLayout" name="gridLayout_3">
<item row="0" column="0">
<widget class="QLabel" name="label">
<property name="text">
<string>&amp;Title: </string>
</property>
<property name="alignment" >
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
<property name="buddy" >
<property name="buddy">
<cstring>title</cstring>
</property>
</widget>
</item>
<item row="0" column="1" >
<widget class="QLineEdit" name="title" >
<property name="toolTip" >
<item row="0" column="1">
<widget class="QLineEdit" name="title">
<property name="toolTip">
<string>Change the title of this book</string>
</property>
</widget>
</item>
<item rowspan="2" row="0" column="2" >
<widget class="QToolButton" name="swap_button" >
<property name="toolTip" >
<item row="0" column="2" rowspan="2">
<widget class="QToolButton" name="swap_button">
<property name="toolTip">
<string>Swap the author and title</string>
</property>
<property name="text" >
<property name="text">
<string>...</string>
</property>
<property name="icon" >
<iconset resource="../images.qrc" >
<property name="icon">
<iconset resource="../images.qrc">
<normaloff>:/images/swap.svg</normaloff>:/images/swap.svg</iconset>
</property>
<property name="iconSize" >
<property name="iconSize">
<size>
<width>16</width>
<height>16</height>
@ -112,305 +113,305 @@
</property>
</widget>
</item>
<item row="1" column="0" >
<widget class="QLabel" name="label_2" >
<property name="text" >
<item row="1" column="0">
<widget class="QLabel" name="label_2">
<property name="text">
<string>&amp;Author(s): </string>
</property>
<property name="alignment" >
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
<property name="buddy" >
<property name="buddy">
<cstring>authors</cstring>
</property>
</widget>
</item>
<item row="2" column="0" >
<widget class="QLabel" name="label_8" >
<property name="text" >
<item row="2" column="0">
<widget class="QLabel" name="label_8">
<property name="text">
<string>Author S&amp;ort: </string>
</property>
<property name="alignment" >
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
<property name="buddy" >
<property name="buddy">
<cstring>author_sort</cstring>
</property>
</widget>
</item>
<item row="2" column="1" colspan="2" >
<layout class="QHBoxLayout" name="horizontalLayout" >
<item row="2" column="1" colspan="2">
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QLineEdit" name="author_sort" >
<property name="toolTip" >
<widget class="QLineEdit" name="author_sort">
<property name="toolTip">
<string>Specify how the author(s) of this book should be sorted. For example Charles Dickens should be sorted as Dickens, Charles.</string>
</property>
</widget>
</item>
<item>
<widget class="QToolButton" name="auto_author_sort" >
<property name="toolTip" >
<widget class="QToolButton" name="auto_author_sort">
<property name="toolTip">
<string>Automatically create the author sort entry based on the current author entry</string>
</property>
<property name="text" >
<property name="text">
<string>...</string>
</property>
<property name="icon" >
<iconset resource="../images.qrc" >
<property name="icon">
<iconset resource="../images.qrc">
<normaloff>:/images/auto_author_sort.svg</normaloff>:/images/auto_author_sort.svg</iconset>
</property>
</widget>
</item>
</layout>
</item>
<item row="3" column="0" >
<widget class="QLabel" name="label_6" >
<property name="text" >
<item row="3" column="0">
<widget class="QLabel" name="label_6">
<property name="text">
<string>&amp;Rating:</string>
</property>
<property name="alignment" >
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
<property name="buddy" >
<property name="buddy">
<cstring>rating</cstring>
</property>
</widget>
</item>
<item row="3" column="1" colspan="2" >
<widget class="QSpinBox" name="rating" >
<property name="toolTip" >
<item row="3" column="1" colspan="2">
<widget class="QSpinBox" name="rating">
<property name="toolTip">
<string>Rating of this book. 0-5 stars</string>
</property>
<property name="whatsThis" >
<property name="whatsThis">
<string>Rating of this book. 0-5 stars</string>
</property>
<property name="buttonSymbols" >
<property name="buttonSymbols">
<enum>QAbstractSpinBox::PlusMinus</enum>
</property>
<property name="suffix" >
<property name="suffix">
<string> stars</string>
</property>
<property name="maximum" >
<property name="maximum">
<number>5</number>
</property>
</widget>
</item>
<item row="4" column="0" >
<widget class="QLabel" name="label_3" >
<property name="text" >
<item row="4" column="0">
<widget class="QLabel" name="label_3">
<property name="text">
<string>&amp;Publisher: </string>
</property>
<property name="alignment" >
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
<property name="buddy" >
<property name="buddy">
<cstring>publisher</cstring>
</property>
</widget>
</item>
<item row="5" column="0" >
<widget class="QLabel" name="label_4" >
<property name="text" >
<item row="5" column="0">
<widget class="QLabel" name="label_4">
<property name="text">
<string>Ta&amp;gs: </string>
</property>
<property name="alignment" >
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
<property name="buddy" >
<property name="buddy">
<cstring>tags</cstring>
</property>
</widget>
</item>
<item row="5" column="1" colspan="2" >
<layout class="QHBoxLayout" name="_2" >
<item row="5" column="1" colspan="2">
<layout class="QHBoxLayout" name="_2">
<item>
<widget class="QLineEdit" name="tags" >
<property name="toolTip" >
<string>Tags categorize the book. This is particularly useful while searching. &lt;br>&lt;br>They can be any words or phrases, separated by commas.</string>
<widget class="QLineEdit" name="tags">
<property name="toolTip">
<string>Tags categorize the book. This is particularly useful while searching. &lt;br&gt;&lt;br&gt;They can be any words or phrases, separated by commas.</string>
</property>
</widget>
</item>
<item>
<widget class="QToolButton" name="tag_editor_button" >
<property name="toolTip" >
<widget class="QToolButton" name="tag_editor_button">
<property name="toolTip">
<string>Open Tag Editor</string>
</property>
<property name="text" >
<property name="text">
<string>Open Tag Editor</string>
</property>
<property name="icon" >
<iconset resource="../images.qrc" >
<property name="icon">
<iconset resource="../images.qrc">
<normaloff>:/images/chapters.svg</normaloff>:/images/chapters.svg</iconset>
</property>
</widget>
</item>
</layout>
</item>
<item row="6" column="0" >
<widget class="QLabel" name="label_7" >
<property name="text" >
<item row="6" column="0">
<widget class="QLabel" name="label_7">
<property name="text">
<string>&amp;Series:</string>
</property>
<property name="textFormat" >
<property name="textFormat">
<enum>Qt::PlainText</enum>
</property>
<property name="alignment" >
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
<property name="buddy" >
<property name="buddy">
<cstring>series</cstring>
</property>
</widget>
</item>
<item row="6" column="1" colspan="2" >
<layout class="QHBoxLayout" name="_3" >
<property name="spacing" >
<item row="6" column="1" colspan="2">
<layout class="QHBoxLayout" name="_3">
<property name="spacing">
<number>5</number>
</property>
<item>
<widget class="QComboBox" name="series" >
<property name="sizePolicy" >
<sizepolicy vsizetype="Fixed" hsizetype="MinimumExpanding" >
<widget class="QComboBox" name="series">
<property name="sizePolicy">
<sizepolicy hsizetype="MinimumExpanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="toolTip" >
<property name="toolTip">
<string>List of known series. You can add new series.</string>
</property>
<property name="whatsThis" >
<property name="whatsThis">
<string>List of known series. You can add new series.</string>
</property>
<property name="editable" >
<property name="editable">
<bool>true</bool>
</property>
<property name="insertPolicy" >
<property name="insertPolicy">
<enum>QComboBox::InsertAlphabetically</enum>
</property>
<property name="sizeAdjustPolicy" >
<property name="sizeAdjustPolicy">
<enum>QComboBox::AdjustToContents</enum>
</property>
</widget>
</item>
<item>
<widget class="QToolButton" name="remove_series_button" >
<property name="toolTip" >
<widget class="QToolButton" name="remove_series_button">
<property name="toolTip">
<string>Remove unused series (Series that have no books)</string>
</property>
<property name="text" >
<property name="text">
<string>...</string>
</property>
<property name="icon" >
<iconset resource="../images.qrc" >
<property name="icon">
<iconset resource="../images.qrc">
<normaloff>:/images/trash.svg</normaloff>:/images/trash.svg</iconset>
</property>
</widget>
</item>
</layout>
</item>
<item row="7" column="1" colspan="2" >
<widget class="QSpinBox" name="series_index" >
<property name="enabled" >
<item row="7" column="1" colspan="2">
<widget class="QSpinBox" name="series_index">
<property name="enabled">
<bool>false</bool>
</property>
<property name="toolTip" >
<property name="toolTip">
<string>Series index.</string>
</property>
<property name="whatsThis" >
<property name="whatsThis">
<string>Series index.</string>
</property>
<property name="prefix" >
<property name="prefix">
<string>Book </string>
</property>
<property name="minimum" >
<property name="minimum">
<number>1</number>
</property>
<property name="maximum" >
<property name="maximum">
<number>10000</number>
</property>
</widget>
</item>
<item row="8" column="0" >
<widget class="QLabel" name="label_9" >
<property name="text" >
<item row="8" column="0">
<widget class="QLabel" name="label_9">
<property name="text">
<string>IS&amp;BN:</string>
</property>
<property name="alignment" >
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
<property name="buddy" >
<property name="buddy">
<cstring>isbn</cstring>
</property>
</widget>
</item>
<item row="8" column="1" colspan="2" >
<widget class="QLineEdit" name="isbn" />
<item row="8" column="1" colspan="2">
<widget class="QLineEdit" name="isbn"/>
</item>
<item row="4" column="1" >
<widget class="QComboBox" name="publisher" >
<property name="editable" >
<item row="4" column="1">
<widget class="QComboBox" name="publisher">
<property name="editable">
<bool>true</bool>
</property>
</widget>
</item>
<item row="1" column="1" >
<widget class="QLineEdit" name="authors" />
<item row="1" column="1">
<widget class="QLineEdit" name="authors"/>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="groupBox_2" >
<property name="title" >
<widget class="QGroupBox" name="groupBox_2">
<property name="title">
<string>Comments</string>
</property>
<layout class="QGridLayout" name="gridLayout_4" >
<item row="0" column="0" >
<widget class="QTextEdit" name="comments" />
<layout class="QGridLayout" name="gridLayout_4">
<item row="0" column="0">
<widget class="QTextEdit" name="comments"/>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QPushButton" name="fetch_metadata_button" >
<property name="text" >
<string>Fetch metadata from server</string>
<widget class="QPushButton" name="fetch_metadata_button">
<property name="text">
<string>&amp;Fetch metadata from server</string>
</property>
</widget>
</item>
</layout>
</widget>
<widget class="QWidget" name="layoutWidget_2" >
<layout class="QVBoxLayout" name="verticalLayout_2" >
<widget class="QWidget" name="layoutWidget_2">
<layout class="QVBoxLayout" name="verticalLayout_2">
<item>
<widget class="QGroupBox" name="af_group_box" >
<property name="sizePolicy" >
<sizepolicy vsizetype="Minimum" hsizetype="Preferred" >
<widget class="QGroupBox" name="af_group_box">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Minimum">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="title" >
<property name="title">
<string>Available Formats</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout" >
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<layout class="QGridLayout" name="gridLayout" >
<item rowspan="3" row="0" column="0" >
<widget class="QListWidget" name="formats" >
<property name="sizePolicy" >
<sizepolicy vsizetype="Minimum" hsizetype="Minimum" >
<layout class="QGridLayout" name="gridLayout">
<item row="0" column="0" rowspan="3">
<widget class="QListWidget" name="formats">
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Minimum">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="maximumSize" >
<property name="maximumSize">
<size>
<width>16777215</width>
<height>130</height>
</size>
</property>
<property name="iconSize" >
<property name="iconSize">
<size>
<width>64</width>
<height>64</height>
@ -418,19 +419,19 @@
</property>
</widget>
</item>
<item row="0" column="1" >
<widget class="QToolButton" name="add_format_button" >
<property name="toolTip" >
<item row="0" column="1">
<widget class="QToolButton" name="add_format_button">
<property name="toolTip">
<string>Add a new format for this book to the database</string>
</property>
<property name="text" >
<property name="text">
<string>...</string>
</property>
<property name="icon" >
<iconset resource="../images.qrc" >
<property name="icon">
<iconset resource="../images.qrc">
<normaloff>:/images/add_book.svg</normaloff>:/images/add_book.svg</iconset>
</property>
<property name="iconSize" >
<property name="iconSize">
<size>
<width>32</width>
<height>32</height>
@ -438,19 +439,19 @@
</property>
</widget>
</item>
<item row="2" column="1" >
<widget class="QToolButton" name="remove_format_button" >
<property name="toolTip" >
<item row="2" column="1">
<widget class="QToolButton" name="remove_format_button">
<property name="toolTip">
<string>Remove the selected formats for this book from the database.</string>
</property>
<property name="text" >
<property name="text">
<string>...</string>
</property>
<property name="icon" >
<iconset resource="../images.qrc" >
<property name="icon">
<iconset resource="../images.qrc">
<normaloff>:/images/trash.svg</normaloff>:/images/trash.svg</iconset>
</property>
<property name="iconSize" >
<property name="iconSize">
<size>
<width>32</width>
<height>32</height>
@ -458,19 +459,19 @@
</property>
</widget>
</item>
<item row="1" column="1" >
<widget class="QToolButton" name="button_set_cover" >
<property name="toolTip" >
<item row="1" column="1">
<widget class="QToolButton" name="button_set_cover">
<property name="toolTip">
<string>Set the cover for the book from the selected format</string>
</property>
<property name="text" >
<property name="text">
<string>...</string>
</property>
<property name="icon" >
<iconset resource="../images.qrc" >
<property name="icon">
<iconset resource="../images.qrc">
<normaloff>:/images/book.svg</normaloff>:/images/book.svg</iconset>
</property>
<property name="iconSize" >
<property name="iconSize">
<size>
<width>32</width>
<height>32</height>
@ -485,96 +486,96 @@
</widget>
</item>
<item>
<widget class="QGroupBox" name="bc_box" >
<property name="sizePolicy" >
<sizepolicy vsizetype="Expanding" hsizetype="Preferred" >
<widget class="QGroupBox" name="bc_box">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Expanding">
<horstretch>0</horstretch>
<verstretch>10</verstretch>
</sizepolicy>
</property>
<property name="title" >
<property name="title">
<string>Book Cover</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_4" >
<layout class="QVBoxLayout" name="verticalLayout_4">
<item>
<widget class="ImageView" name="cover" >
<property name="sizePolicy" >
<sizepolicy vsizetype="Expanding" hsizetype="Expanding" >
<widget class="ImageView" name="cover">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text" >
<property name="text">
<string/>
</property>
<property name="pixmap" >
<pixmap resource="../images.qrc" >:/images/book.svg</pixmap>
<property name="pixmap">
<pixmap resource="../images.qrc">:/images/book.svg</pixmap>
</property>
<property name="scaledContents" >
<property name="scaledContents">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<layout class="QVBoxLayout" name="_4" >
<property name="spacing" >
<layout class="QVBoxLayout" name="_4">
<property name="spacing">
<number>6</number>
</property>
<property name="sizeConstraint" >
<property name="sizeConstraint">
<enum>QLayout::SetMaximumSize</enum>
</property>
<property name="margin" >
<property name="margin">
<number>0</number>
</property>
<item>
<widget class="QLabel" name="label_5" >
<property name="text" >
<widget class="QLabel" name="label_5">
<property name="text">
<string>Change &amp;cover image:</string>
</property>
<property name="buddy" >
<property name="buddy">
<cstring>cover_path</cstring>
</property>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="_5" >
<property name="spacing" >
<layout class="QHBoxLayout" name="_5">
<property name="spacing">
<number>6</number>
</property>
<property name="margin" >
<property name="margin">
<number>0</number>
</property>
<item>
<widget class="QLineEdit" name="cover_path" >
<property name="readOnly" >
<widget class="QLineEdit" name="cover_path">
<property name="readOnly">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QToolButton" name="cover_button" >
<property name="toolTip" >
<widget class="QToolButton" name="cover_button">
<property name="toolTip">
<string>Browse for an image to use as the cover of this book.</string>
</property>
<property name="text" >
<property name="text">
<string>...</string>
</property>
<property name="icon" >
<iconset resource="../images.qrc" >
<property name="icon">
<iconset resource="../images.qrc">
<normaloff>:/images/document_open.svg</normaloff>:/images/document_open.svg</iconset>
</property>
</widget>
</item>
<item>
<widget class="QToolButton" name="reset_cover" >
<property name="toolTip" >
<widget class="QToolButton" name="reset_cover">
<property name="toolTip">
<string>Reset cover to default</string>
</property>
<property name="text" >
<property name="text">
<string>...</string>
</property>
<property name="icon" >
<iconset resource="../images.qrc" >
<property name="icon">
<iconset resource="../images.qrc">
<normaloff>:/images/trash.svg</normaloff>:/images/trash.svg</iconset>
</property>
</widget>
@ -584,21 +585,21 @@
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="_6" >
<layout class="QHBoxLayout" name="_6">
<item>
<widget class="QPushButton" name="fetch_cover_button" >
<property name="text" >
<string>Fetch cover image from server</string>
<widget class="QPushButton" name="fetch_cover_button">
<property name="text">
<string>Fetch &amp;cover image from server</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="password_button" >
<property name="toolTip" >
<widget class="QPushButton" name="password_button">
<property name="toolTip">
<string>Change the username and/or password for your account at LibraryThing.com</string>
</property>
<property name="text" >
<string>Change password</string>
<property name="text">
<string>Change &amp;password</string>
</property>
</widget>
</item>
@ -619,11 +620,11 @@
</widget>
</item>
<item>
<widget class="QDialogButtonBox" name="button_box" >
<property name="orientation" >
<widget class="QDialogButtonBox" name="button_box">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="standardButtons" >
<property name="standardButtons">
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
</property>
</widget>
@ -666,7 +667,7 @@
<tabstop>button_box</tabstop>
</tabstops>
<resources>
<include location="../images.qrc" />
<include location="../images.qrc"/>
</resources>
<connections>
<connection>
@ -675,11 +676,11 @@
<receiver>MetadataSingleDialog</receiver>
<slot>accept()</slot>
<hints>
<hint type="sourcelabel" >
<hint type="sourcelabel">
<x>261</x>
<y>710</y>
</hint>
<hint type="destinationlabel" >
<hint type="destinationlabel">
<x>157</x>
<y>274</y>
</hint>
@ -691,11 +692,11 @@
<receiver>MetadataSingleDialog</receiver>
<slot>reject()</slot>
<hints>
<hint type="sourcelabel" >
<hint type="sourcelabel">
<x>329</x>
<y>710</y>
</hint>
<hint type="destinationlabel" >
<hint type="destinationlabel">
<x>286</x>
<y>274</y>
</hint>

View File

@ -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)

View File

@ -341,6 +341,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:

View File

@ -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

View File

@ -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
------------------

View File

@ -177,11 +177,11 @@ else:
compatibility='%s works on OS X Tiger and above.'%(__appname__,),
path=MOBILEREAD+file, app=__appname__,
note=Markup(\
'''
u'''
<ol>
<li>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.</li>
<li>The app cannot be run from within the dmg. You must drag it to a folder on your filesystem (The Desktop, Applications, wherever).</li>
<li>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.</li>
<li>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.</li>
</ol>
'''))
return 'binary.html', data, None

View File

@ -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()

View File

@ -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):
'''

View File

@ -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' )
]
#!/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'