Sync to pluginize

This commit is contained in:
John Schember 2009-03-16 17:38:17 -04:00
commit 82faaff2cf
60 changed files with 9906 additions and 7001 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('application/xhtml+xml', '.xhtml')
mimetypes.add_type('image/svg+xml', '.svg') mimetypes.add_type('image/svg+xml', '.svg')
mimetypes.add_type('application/x-sony-bbeb', '.lrf') 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/x-dtbncx+xml', '.ncx')
mimetypes.add_type('application/adobe-page-template+xml', '.xpgt') mimetypes.add_type('application/adobe-page-template+xml', '.xpgt')
mimetypes.add_type('application/x-font-opentype', '.otf') mimetypes.add_type('application/x-font-opentype', '.otf')
mimetypes.add_type('application/x-font-truetype', '.ttf') mimetypes.add_type('application/x-font-truetype', '.ttf')
mimetypes.add_type('application/oebps-package+xml', '.opf') mimetypes.add_type('application/oebps-package+xml', '.opf')
mimetypes.add_type('application/ereader', '.pdb') 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 guess_type = mimetypes.guess_type
import cssutils import cssutils
cssutils.log.setLevel(logging.WARN) cssutils.log.setLevel(logging.WARN)

View File

@ -2,7 +2,7 @@ __license__ = 'GPL v3'
__copyright__ = '2008, Kovid Goyal kovid@kovidgoyal.net' __copyright__ = '2008, Kovid Goyal kovid@kovidgoyal.net'
__docformat__ = 'restructuredtext en' __docformat__ = 'restructuredtext en'
__appname__ = 'calibre' __appname__ = 'calibre'
__version__ = '0.5.0' __version__ = '0.5.1'
__author__ = "Kovid Goyal <kovid@kovidgoyal.net>" __author__ = "Kovid Goyal <kovid@kovidgoyal.net>"
''' '''
Various run time constants. Various run time constants.

View File

@ -132,13 +132,24 @@ class HTMLMetadataReader(MetadataReaderPlugin):
class MOBIMetadataReader(MetadataReaderPlugin): class MOBIMetadataReader(MetadataReaderPlugin):
name = 'Read MOBI metadata' name = 'Read MOBI metadata'
file_types = set(['mobi', 'prc', '.azw']) file_types = set(['mobi', 'prc', 'azw'])
description = _('Read metadata from %s files')%'MOBI' description = _('Read metadata from %s files')%'MOBI'
def get_metadata(self, stream, ftype): def get_metadata(self, stream, ftype):
from calibre.ebooks.mobi.reader import get_metadata from calibre.ebooks.mobi.reader import get_metadata
return get_metadata(stream) 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): class ODTMetadataReader(MetadataReaderPlugin):
name = 'Read ODT metadata' name = 'Read ODT metadata'

View File

@ -4,9 +4,9 @@ __copyright__ = '2009, John Schember <john at nachtimwald.com>'
Device driver for Amazon's Kindle 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): class KINDLE(USBMS):
# Ordered list of supported formats # Ordered list of supported formats
@ -30,6 +30,9 @@ class KINDLE(USBMS):
EBOOK_DIR_CARD = "documents" EBOOK_DIR_CARD = "documents"
SUPPORTS_SUB_DIRS = True 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): def delete_books(self, paths, end_session=True):
for path in paths: for path in paths:
if os.path.exists(path): if os.path.exists(path):
@ -41,6 +44,16 @@ class KINDLE(USBMS):
if os.path.exists(filepath + '.mbp'): if os.path.exists(filepath + '.mbp'):
os.unlink(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): class KINDLE2(KINDLE):
PRODUCT_ID = [0x0002] PRODUCT_ID = [0x0002]

View File

@ -1,19 +1,20 @@
__license__ = 'GPL v3' from __future__ import with_statement
__copyright__ = '2009, John Schember <john at nachtimwald.com>' __license__ = 'GPL 3'
''' __copyright__ = '2009, Kovid Goyal <kovid@kovidgoyal.net>'
Global Mime mapping of ebook types. __docformat__ = 'restructuredtext en'
'''
MIME_MAP = { from calibre import guess_type
'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',
}
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.device import Device
from calibre.devices.usbms.books import BookList, Book from calibre.devices.usbms.books import BookList, Book
from calibre.devices.errors import FreeSpaceError, PathError 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): class File(object):
def __init__(self, path): def __init__(self, path):
@ -226,14 +226,17 @@ class USBMS(Device):
if not os.path.isdir(path): if not os.path.isdir(path):
os.utime(path, None) os.utime(path, None)
@classmethod
def metadata_from_path(cls, path):
return metadata_from_formats([path])
@classmethod @classmethod
def book_from_path(cls, path): def book_from_path(cls, path):
fileext = path_to_ext(path) fileext = path_to_ext(path)
mi = cls.metadata_from_path(path)
mi = metadata_from_formats([path]) mime = mime_type_ext(fileext)
mime = MIME_MAP[fileext] if fileext in MIME_MAP.keys() else 'Unknown'
authors = authors_to_string(mi.authors) authors = authors_to_string(mi.authors)
return Book(path, mi.title, authors, mime) book = Book(path, mi.title, authors, mime)
return book

View File

@ -97,6 +97,8 @@ def xml_to_unicode(raw, verbose=False, strip_encoding_pats=False,
if encoding is None: if encoding is None:
encoding = force_encoding(raw, verbose) encoding = force_encoding(raw, verbose)
try: try:
if encoding.lower().strip() == 'macintosh':
encoding = 'mac-roman'
raw = raw.decode(encoding, 'replace') raw = raw.decode(encoding, 'replace')
except LookupError: except LookupError:
encoding = 'utf-8' encoding = 'utf-8'

View File

@ -118,11 +118,17 @@ class EbookIterator(object):
self.spine = [SpineItem(i.path) for i in self.opf.spine] self.spine = [SpineItem(i.path) for i in self.opf.spine]
cover = self.opf.cover 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') cfile = os.path.join(os.path.dirname(self.spine[0]), 'calibre_ei_cover.html')
open(cfile, 'wb').write(TITLEPAGE%cover) open(cfile, 'wb').write(TITLEPAGE%cover)
self.spine[0:0] = [SpineItem(cfile)] 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] sizes = [i.character_count for i in self.spine]
self.pages = [math.ceil(i/float(self.CHARACTERS_PER_PAGE)) for i in sizes] self.pages = [math.ceil(i/float(self.CHARACTERS_PER_PAGE)) for i in sizes]
for p, s in zip(self.pages, self.spine): for p, s in zip(self.pages, self.spine):

View File

@ -15,7 +15,7 @@ _METADATA_PRIORITIES = [
'html', 'htm', 'xhtml', 'xhtm', 'html', 'htm', 'xhtml', 'xhtm',
'rtf', 'fb2', 'pdf', 'prc', 'odt', 'rtf', 'fb2', 'pdf', 'prc', 'odt',
'epub', 'lit', 'lrx', 'lrf', 'mobi', 'epub', 'lit', 'lrx', 'lrf', 'mobi',
'rb', 'imp' 'rb', 'imp', 'azw'
] ]
# The priorities for loading metadata from different file types # 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): for path, ext in zip(formats, extensions):
with open(path, 'rb') as stream: with open(path, 'rb') as stream:
try: 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: except:
continue continue
if getattr(mi, 'application_id', None) is not None: 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: stream_type = stream_type.lower()
if stream_type in ('html', 'html', 'xhtml', 'xhtm', 'xml'): if stream_type in ('html', 'html', 'xhtml', 'xhtm', 'xml'):
stream_type = 'html' stream_type = 'html'
if stream_type in ('mobi', 'prc'): if stream_type in ('mobi', 'prc', 'azw'):
stream_type = 'mobi' stream_type = 'mobi'
if stream_type in ('odt', 'ods', 'odp', 'odg', 'odf'): if stream_type in ('odt', 'ods', 'odp', 'odg', 'odf'):
stream_type = 'odt' stream_type = 'odt'

View File

@ -444,6 +444,7 @@ class OPF(object):
if not hasattr(stream, 'read'): if not hasattr(stream, 'read'):
stream = open(stream, 'rb') stream = open(stream, 'rb')
self.basedir = self.base_dir = basedir 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, self.encoding = xml_to_unicode(stream.read(), strip_encoding_pats=True, resolve_entities=True)
raw = raw[raw.find('<'):] raw = raw[raw.find('<'):]
self.root = etree.fromstring(raw, self.PARSER) self.root = etree.fromstring(raw, self.PARSER)
@ -495,6 +496,7 @@ class OPF(object):
if f: if f:
self.toc.read_ncx_toc(f[0]) self.toc.read_ncx_toc(f[0])
else: else:
self.path_to_html_toc = toc
self.toc.read_html_toc(toc) self.toc.read_html_toc(toc)
except: except:
pass 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.sublanguage = 'NEUTRAL'
self.exth_flag, self.exth = 0, None self.exth_flag, self.exth = 0, None
self.ancient = True self.ancient = True
self.first_image_index = -1
self.mobi_version = 1
else: else:
self.ancient = False self.ancient = False
self.doctype = raw[16:20] self.doctype = raw[16:20]
@ -537,7 +539,7 @@ class MobiReader(object):
os.makedirs(output_dir) os.makedirs(output_dir)
image_index = 0 image_index = 0
self.image_names = [] 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: if start > self.num_sections or start < 0:
# BAEN PRC files have bad headers # BAEN PRC files have bad headers
start=0 start=0

View File

@ -31,7 +31,7 @@ from calibre.ebooks.oeb.transforms.htmltoc import HTMLTOCAdder
from calibre.ebooks.oeb.transforms.manglecase import CaseMangler from calibre.ebooks.oeb.transforms.manglecase import CaseMangler
from calibre.ebooks.mobi.palmdoc import compress_doc from calibre.ebooks.mobi.palmdoc import compress_doc
from calibre.ebooks.mobi.langcodes import iana2mobi from calibre.ebooks.mobi.langcodes import iana2mobi
from calibre.ebooks.mobi.mobiml import MBP_NS, MBP, MobiMLizer from calibre.ebooks.mobi.mobiml import MBP_NS, MobiMLizer
from calibre.customize.ui import run_plugins_on_postprocess from calibre.customize.ui import run_plugins_on_postprocess
from calibre.utils.config import Config, StringConfig from calibre.utils.config import Config, StringConfig
@ -160,7 +160,7 @@ class Serializer(object):
hrefs = self.oeb.manifest.hrefs hrefs = self.oeb.manifest.hrefs
buffer.write('<guide>') buffer.write('<guide>')
for ref in self.oeb.guide.values(): for ref in self.oeb.guide.values():
path, frag = urldefrag(ref.href) path = urldefrag(ref.href)[0]
if hrefs[path].media_type not in OEB_DOCS: if hrefs[path].media_type not in OEB_DOCS:
continue continue
buffer.write('<reference type="') buffer.write('<reference type="')
@ -455,8 +455,6 @@ class MobiWriter(object):
self._oeb.logger.info('Serializing images...') self._oeb.logger.info('Serializing images...')
images = [(index, href) for href, index in self._images.items()] images = [(index, href) for href, index in self._images.items()]
images.sort() images.sort()
metadata = self._oeb.metadata
coverid = metadata.cover[0] if metadata.cover else None
for _, href in images: for _, href in images:
item = self._oeb.manifest.hrefs[href] item = self._oeb.manifest.hrefs[href]
try: try:

View File

@ -159,7 +159,7 @@ class Stylizer(object):
for _, _, cssdict, text, _ in rules: for _, _, cssdict, text, _ in rules:
try: try:
selector = CSSSelector(text) selector = CSSSelector(text)
except ExpressionError: except (AssertionError, ExpressionError, etree.XPathSyntaxError):
continue continue
for elem in selector(tree): for elem in selector(tree):
self.style(elem)._update_cssdict(cssdict) self.style(elem)._update_cssdict(cssdict)

View File

@ -63,6 +63,8 @@ class HTMLTOCAdder(object):
def __call__(self, oeb, context): def __call__(self, oeb, context):
if 'toc' in oeb.guide: if 'toc' in oeb.guide:
return return
if not getattr(getattr(oeb, 'toc', False), 'nodes', False):
return
oeb.logger.info('Generating in-line TOC...') oeb.logger.info('Generating in-line TOC...')
title = self.title or oeb.translate(DEFAULT_TITLE) title = self.title or oeb.translate(DEFAULT_TITLE)
style = self.style style = self.style

View File

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 572 B

View File

@ -94,6 +94,7 @@ class DateDelegate(QStyledItemDelegate):
def createEditor(self, parent, option, index): def createEditor(self, parent, option, index):
qde = QStyledItemDelegate.createEditor(self, parent, option, index) qde = QStyledItemDelegate.createEditor(self, parent, option, index)
qde.setDisplayFormat('MM/dd/yyyy') qde.setDisplayFormat('MM/dd/yyyy')
qde.setMinimumDate(QDate(100,1,1))
return qde return qde
class BooksModel(QAbstractTableModel): class BooksModel(QAbstractTableModel):

View File

@ -238,6 +238,7 @@ class Main(MainWindow, Ui_MainWindow):
QObject.connect(self.config_button, SIGNAL('clicked(bool)'), self.do_config) QObject.connect(self.config_button, SIGNAL('clicked(bool)'), self.do_config)
self.connect(self.preferences_action, SIGNAL('triggered(bool)'), self.do_config) self.connect(self.preferences_action, SIGNAL('triggered(bool)'), self.do_config)
self.connect(self.action_preferences, SIGNAL('triggered(bool)'), self.do_config)
QObject.connect(self.advanced_search_button, SIGNAL('clicked(bool)'), self.do_advanced_search) QObject.connect(self.advanced_search_button, SIGNAL('clicked(bool)'), self.do_advanced_search)
####################### Library view ######################## ####################### Library view ########################
@ -1220,12 +1221,14 @@ class Main(MainWindow, Ui_MainWindow):
if ext in config['internally_viewed_formats']: if ext in config['internally_viewed_formats']:
if ext == 'LRF': if ext == 'LRF':
args = ['lrfviewer', name] args = ['lrfviewer', name]
self.job_manager.server.run_free_job('lrfviewer', kwdargs=dict(args=args)) self.job_manager.server.run_free_job('lrfviewer',
kwdargs=dict(args=args))
else: else:
args = ['ebook-viewer', name] args = ['ebook-viewer', name]
if isosx: if isosx:
args.append('--raise-window') args.append('--raise-window')
self.job_manager.server.run_free_job('ebook-viewer', kwdargs=dict(args=args)) self.job_manager.server.run_free_job('ebook-viewer',
kwdargs=dict(args=args))
else: else:
QDesktopServices.openUrl(QUrl('file:'+name))#launch(name) QDesktopServices.openUrl(QUrl('file:'+name))#launch(name)
time.sleep(5) # User feedback time.sleep(5) # User feedback
@ -1320,7 +1323,7 @@ class Main(MainWindow, Ui_MainWindow):
############################### Do config ################################## ############################### Do config ##################################
def do_config(self): def do_config(self, *args):
if self.job_manager.has_jobs(): if self.job_manager.has_jobs():
d = error_dialog(self, _('Cannot configure'), _('Cannot configure while there are running jobs.')) d = error_dialog(self, _('Cannot configure'), _('Cannot configure while there are running jobs.'))
d.exec_() d.exec_()

View File

@ -1,149 +1,150 @@
<ui version="4.0" > <?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<author>Kovid Goyal</author> <author>Kovid Goyal</author>
<class>MainWindow</class> <class>MainWindow</class>
<widget class="QMainWindow" name="MainWindow" > <widget class="QMainWindow" name="MainWindow">
<property name="geometry" > <property name="geometry">
<rect> <rect>
<x>0</x> <x>0</x>
<y>0</y> <y>0</y>
<width>865</width> <width>1012</width>
<height>822</height> <height>822</height>
</rect> </rect>
</property> </property>
<property name="sizePolicy" > <property name="sizePolicy">
<sizepolicy vsizetype="Preferred" hsizetype="Preferred" > <sizepolicy hsizetype="Preferred" vsizetype="Preferred">
<horstretch>0</horstretch> <horstretch>0</horstretch>
<verstretch>0</verstretch> <verstretch>0</verstretch>
</sizepolicy> </sizepolicy>
</property> </property>
<property name="contextMenuPolicy" > <property name="contextMenuPolicy">
<enum>Qt::NoContextMenu</enum> <enum>Qt::NoContextMenu</enum>
</property> </property>
<property name="windowTitle" > <property name="windowTitle">
<string>__appname__</string> <string>__appname__</string>
</property> </property>
<property name="windowIcon" > <property name="windowIcon">
<iconset resource="images.qrc" > <iconset resource="images.qrc">
<normaloff>:/library</normaloff>:/library</iconset> <normaloff>:/library</normaloff>:/library</iconset>
</property> </property>
<widget class="QWidget" name="centralwidget" > <widget class="QWidget" name="centralwidget">
<layout class="QGridLayout" name="gridLayout" > <layout class="QGridLayout" name="gridLayout">
<item row="0" column="0" > <item row="0" column="0">
<layout class="QHBoxLayout" name="horizontalLayout_3" > <layout class="QHBoxLayout" name="horizontalLayout_3">
<item> <item>
<widget class="LocationView" name="location_view" > <widget class="LocationView" name="location_view">
<property name="sizePolicy" > <property name="sizePolicy">
<sizepolicy vsizetype="Expanding" hsizetype="Expanding" > <sizepolicy hsizetype="Expanding" vsizetype="Expanding">
<horstretch>0</horstretch> <horstretch>0</horstretch>
<verstretch>0</verstretch> <verstretch>0</verstretch>
</sizepolicy> </sizepolicy>
</property> </property>
<property name="maximumSize" > <property name="maximumSize">
<size> <size>
<width>16777215</width> <width>16777215</width>
<height>100</height> <height>100</height>
</size> </size>
</property> </property>
<property name="verticalScrollBarPolicy" > <property name="verticalScrollBarPolicy">
<enum>Qt::ScrollBarAlwaysOff</enum> <enum>Qt::ScrollBarAlwaysOff</enum>
</property> </property>
<property name="horizontalScrollBarPolicy" > <property name="horizontalScrollBarPolicy">
<enum>Qt::ScrollBarAsNeeded</enum> <enum>Qt::ScrollBarAsNeeded</enum>
</property> </property>
<property name="tabKeyNavigation" > <property name="tabKeyNavigation">
<bool>true</bool> <bool>true</bool>
</property> </property>
<property name="showDropIndicator" stdset="0" > <property name="showDropIndicator" stdset="0">
<bool>true</bool> <bool>true</bool>
</property> </property>
<property name="iconSize" > <property name="iconSize">
<size> <size>
<width>40</width> <width>40</width>
<height>40</height> <height>40</height>
</size> </size>
</property> </property>
<property name="movement" > <property name="movement">
<enum>QListView::Static</enum> <enum>QListView::Static</enum>
</property> </property>
<property name="flow" > <property name="flow">
<enum>QListView::LeftToRight</enum> <enum>QListView::LeftToRight</enum>
</property> </property>
<property name="gridSize" > <property name="gridSize">
<size> <size>
<width>175</width> <width>175</width>
<height>90</height> <height>90</height>
</size> </size>
</property> </property>
<property name="viewMode" > <property name="viewMode">
<enum>QListView::ListMode</enum> <enum>QListView::ListMode</enum>
</property> </property>
<property name="wordWrap" > <property name="wordWrap">
<bool>true</bool> <bool>true</bool>
</property> </property>
</widget> </widget>
</item> </item>
<item> <item>
<widget class="QToolButton" name="donate_button" > <widget class="QToolButton" name="donate_button">
<property name="cursor" > <property name="cursor">
<cursorShape>PointingHandCursor</cursorShape> <cursorShape>PointingHandCursor</cursorShape>
</property> </property>
<property name="text" > <property name="text">
<string>...</string> <string>...</string>
</property> </property>
<property name="icon" > <property name="icon">
<iconset resource="images.qrc" > <iconset resource="images.qrc">
<normaloff>:/images/donate.svg</normaloff>:/images/donate.svg</iconset> <normaloff>:/images/donate.svg</normaloff>:/images/donate.svg</iconset>
</property> </property>
<property name="iconSize" > <property name="iconSize">
<size> <size>
<width>64</width> <width>64</width>
<height>64</height> <height>64</height>
</size> </size>
</property> </property>
<property name="autoRaise" > <property name="autoRaise">
<bool>true</bool> <bool>true</bool>
</property> </property>
</widget> </widget>
</item> </item>
<item> <item>
<layout class="QVBoxLayout" name="verticalLayout_3" > <layout class="QVBoxLayout" name="verticalLayout_3">
<item> <item>
<widget class="QLabel" name="vanity" > <widget class="QLabel" name="vanity">
<property name="sizePolicy" > <property name="sizePolicy">
<sizepolicy vsizetype="Preferred" hsizetype="Preferred" > <sizepolicy hsizetype="Preferred" vsizetype="Preferred">
<horstretch>0</horstretch> <horstretch>0</horstretch>
<verstretch>0</verstretch> <verstretch>0</verstretch>
</sizepolicy> </sizepolicy>
</property> </property>
<property name="maximumSize" > <property name="maximumSize">
<size> <size>
<width>16777215</width> <width>16777215</width>
<height>90</height> <height>90</height>
</size> </size>
</property> </property>
<property name="text" > <property name="text">
<string/> <string/>
</property> </property>
<property name="textFormat" > <property name="textFormat">
<enum>Qt::RichText</enum> <enum>Qt::RichText</enum>
</property> </property>
<property name="openExternalLinks" > <property name="openExternalLinks">
<bool>true</bool> <bool>true</bool>
</property> </property>
</widget> </widget>
</item> </item>
<item> <item>
<layout class="QHBoxLayout" name="horizontalLayout_2" > <layout class="QHBoxLayout" name="horizontalLayout_2">
<item> <item>
<widget class="QLabel" name="label_2" > <widget class="QLabel" name="label_2">
<property name="text" > <property name="text">
<string>Output:</string> <string>Output:</string>
</property> </property>
</widget> </widget>
</item> </item>
<item> <item>
<widget class="QComboBox" name="output_format" > <widget class="QComboBox" name="output_format">
<property name="toolTip" > <property name="toolTip">
<string>Set the output format that is used when converting ebooks and downloading news</string> <string>Set the output format that is used when converting ebooks and downloading news</string>
</property> </property>
</widget> </widget>
@ -154,99 +155,99 @@
</item> </item>
</layout> </layout>
</item> </item>
<item row="1" column="0" > <item row="1" column="0">
<layout class="QHBoxLayout" > <layout class="QHBoxLayout">
<property name="spacing" > <property name="spacing">
<number>6</number> <number>6</number>
</property> </property>
<property name="margin" > <property name="margin">
<number>0</number> <number>0</number>
</property> </property>
<item> <item>
<widget class="QToolButton" name="advanced_search_button" > <widget class="QToolButton" name="advanced_search_button">
<property name="toolTip" > <property name="toolTip">
<string>Advanced search</string> <string>Advanced search</string>
</property> </property>
<property name="text" > <property name="text">
<string>...</string> <string>...</string>
</property> </property>
<property name="icon" > <property name="icon">
<iconset resource="images.qrc" > <iconset resource="images.qrc">
<normaloff>:/images/search.svg</normaloff>:/images/search.svg</iconset> <normaloff>:/images/search.svg</normaloff>:/images/search.svg</iconset>
</property> </property>
<property name="shortcut" > <property name="shortcut">
<string>Alt+S</string> <string>Alt+S</string>
</property> </property>
</widget> </widget>
</item> </item>
<item> <item>
<widget class="QLabel" name="label" > <widget class="QLabel" name="label">
<property name="text" > <property name="text">
<string>&amp;Search:</string> <string>&amp;Search:</string>
</property> </property>
<property name="buddy" > <property name="buddy">
<cstring>search</cstring> <cstring>search</cstring>
</property> </property>
</widget> </widget>
</item> </item>
<item> <item>
<widget class="SearchBox" name="search" > <widget class="SearchBox" name="search">
<property name="enabled" > <property name="enabled">
<bool>true</bool> <bool>true</bool>
</property> </property>
<property name="sizePolicy" > <property name="sizePolicy">
<sizepolicy vsizetype="Fixed" hsizetype="Expanding" > <sizepolicy hsizetype="Expanding" vsizetype="Fixed">
<horstretch>1</horstretch> <horstretch>1</horstretch>
<verstretch>0</verstretch> <verstretch>0</verstretch>
</sizepolicy> </sizepolicy>
</property> </property>
<property name="acceptDrops" > <property name="acceptDrops">
<bool>false</bool> <bool>false</bool>
</property> </property>
<property name="toolTip" > <property name="toolTip">
<string>Search the list of books by title or author&lt;br>&lt;br>Words separated by spaces are ANDed</string> <string>Search the list of books by title or author&lt;br&gt;&lt;br&gt;Words separated by spaces are ANDed</string>
</property> </property>
<property name="whatsThis" > <property name="whatsThis">
<string>Search the list of books by title, author, publisher, tags and comments&lt;br>&lt;br>Words separated by spaces are ANDed</string> <string>Search the list of books by title, author, publisher, tags and comments&lt;br&gt;&lt;br&gt;Words separated by spaces are ANDed</string>
</property> </property>
<property name="autoFillBackground" > <property name="autoFillBackground">
<bool>false</bool> <bool>false</bool>
</property> </property>
<property name="text" > <property name="text">
<string/> <string/>
</property> </property>
<property name="frame" > <property name="frame">
<bool>true</bool> <bool>true</bool>
</property> </property>
</widget> </widget>
</item> </item>
<item> <item>
<widget class="QToolButton" name="clear_button" > <widget class="QToolButton" name="clear_button">
<property name="toolTip" > <property name="toolTip">
<string>Reset Quick Search</string> <string>Reset Quick Search</string>
</property> </property>
<property name="text" > <property name="text">
<string>...</string> <string>...</string>
</property> </property>
<property name="icon" > <property name="icon">
<iconset resource="images.qrc" > <iconset resource="images.qrc">
<normaloff>:/images/clear_left.svg</normaloff>:/images/clear_left.svg</iconset> <normaloff>:/images/clear_left.svg</normaloff>:/images/clear_left.svg</iconset>
</property> </property>
</widget> </widget>
</item> </item>
<item> <item>
<widget class="Line" name="line" > <widget class="Line" name="line">
<property name="orientation" > <property name="orientation">
<enum>Qt::Vertical</enum> <enum>Qt::Vertical</enum>
</property> </property>
</widget> </widget>
</item> </item>
<item> <item>
<spacer> <spacer>
<property name="orientation" > <property name="orientation">
<enum>Qt::Horizontal</enum> <enum>Qt::Horizontal</enum>
</property> </property>
<property name="sizeHint" stdset="0" > <property name="sizeHint" stdset="0">
<size> <size>
<width>20</width> <width>20</width>
<height>20</height> <height>20</height>
@ -255,77 +256,77 @@
</spacer> </spacer>
</item> </item>
<item> <item>
<widget class="QToolButton" name="config_button" > <widget class="QToolButton" name="config_button">
<property name="toolTip" > <property name="toolTip">
<string>Configuration</string> <string>Configuration</string>
</property> </property>
<property name="text" > <property name="text">
<string>...</string> <string>...</string>
</property> </property>
<property name="icon" > <property name="icon">
<iconset resource="images.qrc" > <iconset resource="images.qrc">
<normaloff>:/images/config.svg</normaloff>:/images/config.svg</iconset> <normaloff>:/images/config.svg</normaloff>:/images/config.svg</iconset>
</property> </property>
</widget> </widget>
</item> </item>
</layout> </layout>
</item> </item>
<item row="2" column="0" > <item row="2" column="0">
<widget class="QStackedWidget" name="stack" > <widget class="QStackedWidget" name="stack">
<property name="sizePolicy" > <property name="sizePolicy">
<sizepolicy vsizetype="Expanding" hsizetype="Expanding" > <sizepolicy hsizetype="Expanding" vsizetype="Expanding">
<horstretch>100</horstretch> <horstretch>100</horstretch>
<verstretch>100</verstretch> <verstretch>100</verstretch>
</sizepolicy> </sizepolicy>
</property> </property>
<property name="currentIndex" > <property name="currentIndex">
<number>0</number> <number>0</number>
</property> </property>
<widget class="QWidget" name="library" > <widget class="QWidget" name="library">
<layout class="QVBoxLayout" name="verticalLayout_2" > <layout class="QVBoxLayout" name="verticalLayout_2">
<item> <item>
<layout class="QHBoxLayout" name="horizontalLayout" > <layout class="QHBoxLayout" name="horizontalLayout">
<item> <item>
<layout class="QVBoxLayout" name="verticalLayout" > <layout class="QVBoxLayout" name="verticalLayout">
<item> <item>
<widget class="QRadioButton" name="match_any" > <widget class="QRadioButton" name="match_any">
<property name="text" > <property name="text">
<string>Match any</string> <string>Match any</string>
</property> </property>
<property name="checked" > <property name="checked">
<bool>false</bool> <bool>false</bool>
</property> </property>
</widget> </widget>
</item> </item>
<item> <item>
<widget class="QRadioButton" name="match_all" > <widget class="QRadioButton" name="match_all">
<property name="text" > <property name="text">
<string>Match all</string> <string>Match all</string>
</property> </property>
<property name="checked" > <property name="checked">
<bool>true</bool> <bool>true</bool>
</property> </property>
</widget> </widget>
</item> </item>
<item> <item>
<widget class="QCheckBox" name="popularity" > <widget class="QCheckBox" name="popularity">
<property name="text" > <property name="text">
<string>Sort by &amp;popularity</string> <string>Sort by &amp;popularity</string>
</property> </property>
</widget> </widget>
</item> </item>
<item> <item>
<widget class="TagsView" name="tags_view" > <widget class="TagsView" name="tags_view">
<property name="tabKeyNavigation" > <property name="tabKeyNavigation">
<bool>true</bool> <bool>true</bool>
</property> </property>
<property name="alternatingRowColors" > <property name="alternatingRowColors">
<bool>true</bool> <bool>true</bool>
</property> </property>
<property name="animated" > <property name="animated">
<bool>true</bool> <bool>true</bool>
</property> </property>
<property name="headerHidden" > <property name="headerHidden">
<bool>true</bool> <bool>true</bool>
</property> </property>
</widget> </widget>
@ -333,35 +334,35 @@
</layout> </layout>
</item> </item>
<item> <item>
<widget class="BooksView" name="library_view" > <widget class="BooksView" name="library_view">
<property name="sizePolicy" > <property name="sizePolicy">
<sizepolicy vsizetype="Expanding" hsizetype="Expanding" > <sizepolicy hsizetype="Expanding" vsizetype="Expanding">
<horstretch>100</horstretch> <horstretch>100</horstretch>
<verstretch>10</verstretch> <verstretch>10</verstretch>
</sizepolicy> </sizepolicy>
</property> </property>
<property name="acceptDrops" > <property name="acceptDrops">
<bool>true</bool> <bool>true</bool>
</property> </property>
<property name="dragEnabled" > <property name="dragEnabled">
<bool>true</bool> <bool>true</bool>
</property> </property>
<property name="dragDropOverwriteMode" > <property name="dragDropOverwriteMode">
<bool>false</bool> <bool>false</bool>
</property> </property>
<property name="dragDropMode" > <property name="dragDropMode">
<enum>QAbstractItemView::DragDrop</enum> <enum>QAbstractItemView::DragDrop</enum>
</property> </property>
<property name="alternatingRowColors" > <property name="alternatingRowColors">
<bool>true</bool> <bool>true</bool>
</property> </property>
<property name="selectionBehavior" > <property name="selectionBehavior">
<enum>QAbstractItemView::SelectRows</enum> <enum>QAbstractItemView::SelectRows</enum>
</property> </property>
<property name="showGrid" > <property name="showGrid">
<bool>false</bool> <bool>false</bool>
</property> </property>
<property name="wordWrap" > <property name="wordWrap">
<bool>false</bool> <bool>false</bool>
</property> </property>
</widget> </widget>
@ -370,76 +371,76 @@
</item> </item>
</layout> </layout>
</widget> </widget>
<widget class="QWidget" name="main_memory" > <widget class="QWidget" name="main_memory">
<layout class="QGridLayout" > <layout class="QGridLayout">
<item row="0" column="0" > <item row="0" column="0">
<widget class="DeviceBooksView" name="memory_view" > <widget class="DeviceBooksView" name="memory_view">
<property name="sizePolicy" > <property name="sizePolicy">
<sizepolicy vsizetype="Expanding" hsizetype="Expanding" > <sizepolicy hsizetype="Expanding" vsizetype="Expanding">
<horstretch>100</horstretch> <horstretch>100</horstretch>
<verstretch>10</verstretch> <verstretch>10</verstretch>
</sizepolicy> </sizepolicy>
</property> </property>
<property name="acceptDrops" > <property name="acceptDrops">
<bool>true</bool> <bool>true</bool>
</property> </property>
<property name="dragEnabled" > <property name="dragEnabled">
<bool>true</bool> <bool>true</bool>
</property> </property>
<property name="dragDropOverwriteMode" > <property name="dragDropOverwriteMode">
<bool>false</bool> <bool>false</bool>
</property> </property>
<property name="dragDropMode" > <property name="dragDropMode">
<enum>QAbstractItemView::DragDrop</enum> <enum>QAbstractItemView::DragDrop</enum>
</property> </property>
<property name="alternatingRowColors" > <property name="alternatingRowColors">
<bool>true</bool> <bool>true</bool>
</property> </property>
<property name="selectionBehavior" > <property name="selectionBehavior">
<enum>QAbstractItemView::SelectRows</enum> <enum>QAbstractItemView::SelectRows</enum>
</property> </property>
<property name="showGrid" > <property name="showGrid">
<bool>false</bool> <bool>false</bool>
</property> </property>
<property name="wordWrap" > <property name="wordWrap">
<bool>false</bool> <bool>false</bool>
</property> </property>
</widget> </widget>
</item> </item>
</layout> </layout>
</widget> </widget>
<widget class="QWidget" name="page" > <widget class="QWidget" name="page">
<layout class="QGridLayout" > <layout class="QGridLayout">
<item row="0" column="0" > <item row="0" column="0">
<widget class="DeviceBooksView" name="card_view" > <widget class="DeviceBooksView" name="card_view">
<property name="sizePolicy" > <property name="sizePolicy">
<sizepolicy vsizetype="Expanding" hsizetype="Preferred" > <sizepolicy hsizetype="Preferred" vsizetype="Expanding">
<horstretch>10</horstretch> <horstretch>10</horstretch>
<verstretch>10</verstretch> <verstretch>10</verstretch>
</sizepolicy> </sizepolicy>
</property> </property>
<property name="acceptDrops" > <property name="acceptDrops">
<bool>true</bool> <bool>true</bool>
</property> </property>
<property name="dragEnabled" > <property name="dragEnabled">
<bool>true</bool> <bool>true</bool>
</property> </property>
<property name="dragDropOverwriteMode" > <property name="dragDropOverwriteMode">
<bool>false</bool> <bool>false</bool>
</property> </property>
<property name="dragDropMode" > <property name="dragDropMode">
<enum>QAbstractItemView::DragDrop</enum> <enum>QAbstractItemView::DragDrop</enum>
</property> </property>
<property name="alternatingRowColors" > <property name="alternatingRowColors">
<bool>true</bool> <bool>true</bool>
</property> </property>
<property name="selectionBehavior" > <property name="selectionBehavior">
<enum>QAbstractItemView::SelectRows</enum> <enum>QAbstractItemView::SelectRows</enum>
</property> </property>
<property name="showGrid" > <property name="showGrid">
<bool>false</bool> <bool>false</bool>
</property> </property>
<property name="wordWrap" > <property name="wordWrap">
<bool>false</bool> <bool>false</bool>
</property> </property>
</widget> </widget>
@ -450,221 +451,237 @@
</item> </item>
</layout> </layout>
</widget> </widget>
<widget class="QToolBar" name="tool_bar" > <widget class="QToolBar" name="tool_bar">
<property name="minimumSize" > <property name="minimumSize">
<size> <size>
<width>0</width> <width>0</width>
<height>0</height> <height>0</height>
</size> </size>
</property> </property>
<property name="contextMenuPolicy" > <property name="contextMenuPolicy">
<enum>Qt::PreventContextMenu</enum> <enum>Qt::PreventContextMenu</enum>
</property> </property>
<property name="movable" > <property name="movable">
<bool>false</bool> <bool>false</bool>
</property> </property>
<property name="orientation" > <property name="orientation">
<enum>Qt::Horizontal</enum> <enum>Qt::Horizontal</enum>
</property> </property>
<property name="iconSize" > <property name="iconSize">
<size> <size>
<width>48</width> <width>48</width>
<height>48</height> <height>48</height>
</size> </size>
</property> </property>
<property name="toolButtonStyle" > <property name="toolButtonStyle">
<enum>Qt::ToolButtonTextUnderIcon</enum> <enum>Qt::ToolButtonTextUnderIcon</enum>
</property> </property>
<attribute name="toolBarArea" > <attribute name="toolBarArea">
<enum>TopToolBarArea</enum> <enum>TopToolBarArea</enum>
</attribute> </attribute>
<attribute name="toolBarBreak" > <attribute name="toolBarBreak">
<bool>false</bool> <bool>false</bool>
</attribute> </attribute>
<addaction name="action_add" /> <addaction name="action_add"/>
<addaction name="action_del" /> <addaction name="action_edit"/>
<addaction name="action_edit" /> <addaction name="action_convert"/>
<addaction name="separator" /> <addaction name="action_view"/>
<addaction name="action_sync" /> <addaction name="action_news"/>
<addaction name="action_save" /> <addaction name="separator"/>
<addaction name="separator" /> <addaction name="action_sync"/>
<addaction name="action_news" /> <addaction name="action_save"/>
<addaction name="action_convert" /> <addaction name="action_del"/>
<addaction name="action_view" /> <addaction name="separator"/>
<addaction name="action_preferences"/>
</widget> </widget>
<widget class="QStatusBar" name="statusBar" > <widget class="QStatusBar" name="statusBar">
<property name="mouseTracking" > <property name="mouseTracking">
<bool>true</bool> <bool>true</bool>
</property> </property>
</widget> </widget>
<action name="action_add" > <action name="action_add">
<property name="icon" > <property name="icon">
<iconset resource="images.qrc" > <iconset resource="images.qrc">
<normaloff>:/images/add_book.svg</normaloff>:/images/add_book.svg</iconset> <normaloff>:/images/add_book.svg</normaloff>:/images/add_book.svg</iconset>
</property> </property>
<property name="text" > <property name="text">
<string>Add books</string> <string>Add books</string>
</property> </property>
<property name="shortcut" > <property name="shortcut">
<string>A</string> <string>A</string>
</property> </property>
<property name="autoRepeat" > <property name="autoRepeat">
<bool>false</bool> <bool>false</bool>
</property> </property>
</action> </action>
<action name="action_del" > <action name="action_del">
<property name="icon" > <property name="icon">
<iconset resource="images.qrc" > <iconset resource="images.qrc">
<normaloff>:/images/trash.svg</normaloff>:/images/trash.svg</iconset> <normaloff>:/images/trash.svg</normaloff>:/images/trash.svg</iconset>
</property> </property>
<property name="text" > <property name="text">
<string>Remove books</string> <string>Remove books</string>
</property> </property>
<property name="toolTip" > <property name="toolTip">
<string>Remove books</string> <string>Remove books</string>
</property> </property>
<property name="shortcut" > <property name="shortcut">
<string>Del</string> <string>Del</string>
</property> </property>
</action> </action>
<action name="action_edit" > <action name="action_edit">
<property name="icon" > <property name="icon">
<iconset resource="images.qrc" > <iconset resource="images.qrc">
<normaloff>:/images/edit_input.svg</normaloff>:/images/edit_input.svg</iconset> <normaloff>:/images/edit_input.svg</normaloff>:/images/edit_input.svg</iconset>
</property> </property>
<property name="text" > <property name="text">
<string>Edit meta information</string> <string>Edit meta information</string>
</property> </property>
<property name="shortcut" > <property name="shortcut">
<string>E</string> <string>E</string>
</property> </property>
<property name="autoRepeat" > <property name="autoRepeat">
<bool>false</bool> <bool>false</bool>
</property> </property>
</action> </action>
<action name="action_sync" > <action name="action_sync">
<property name="enabled" > <property name="enabled">
<bool>false</bool> <bool>false</bool>
</property> </property>
<property name="icon" > <property name="icon">
<iconset resource="images.qrc" > <iconset resource="images.qrc">
<normaloff>:/images/sync.svg</normaloff>:/images/sync.svg</iconset> <normaloff>:/images/sync.svg</normaloff>:/images/sync.svg</iconset>
</property> </property>
<property name="text" > <property name="text">
<string>Send to device</string> <string>Send to device</string>
</property> </property>
</action> </action>
<action name="action_save" > <action name="action_save">
<property name="icon" > <property name="icon">
<iconset resource="images.qrc" > <iconset resource="images.qrc">
<normaloff>:/images/save.svg</normaloff>:/images/save.svg</iconset> <normaloff>:/images/save.svg</normaloff>:/images/save.svg</iconset>
</property> </property>
<property name="text" > <property name="text">
<string>Save to disk</string> <string>Save to disk</string>
</property> </property>
<property name="shortcut" > <property name="shortcut">
<string>S</string> <string>S</string>
</property> </property>
</action> </action>
<action name="action_news" > <action name="action_news">
<property name="icon" > <property name="icon">
<iconset resource="images.qrc" > <iconset resource="images.qrc">
<normaloff>:/images/news.svg</normaloff>:/images/news.svg</iconset> <normaloff>:/images/news.svg</normaloff>:/images/news.svg</iconset>
</property> </property>
<property name="text" > <property name="text">
<string>Fetch news</string> <string>Fetch news</string>
</property> </property>
<property name="shortcut" > <property name="shortcut">
<string>F</string> <string>F</string>
</property> </property>
</action> </action>
<action name="action_convert" > <action name="action_convert">
<property name="icon" > <property name="icon">
<iconset resource="images.qrc" > <iconset resource="images.qrc">
<normaloff>:/images/convert.svg</normaloff>:/images/convert.svg</iconset> <normaloff>:/images/convert.svg</normaloff>:/images/convert.svg</iconset>
</property> </property>
<property name="text" > <property name="text">
<string>Convert E-books</string> <string>Convert E-books</string>
</property> </property>
<property name="shortcut" > <property name="shortcut">
<string>C</string> <string>C</string>
</property> </property>
</action> </action>
<action name="action_view" > <action name="action_view">
<property name="icon" > <property name="icon">
<iconset resource="images.qrc" > <iconset resource="images.qrc">
<normaloff>:/images/view.svg</normaloff>:/images/view.svg</iconset> <normaloff>:/images/view.svg</normaloff>:/images/view.svg</iconset>
</property> </property>
<property name="text" > <property name="text">
<string>View</string> <string>View</string>
</property> </property>
<property name="shortcut" > <property name="shortcut">
<string>V</string> <string>V</string>
</property> </property>
</action> </action>
<action name="action_open_containing_folder" > <action name="action_open_containing_folder">
<property name="icon" > <property name="icon">
<iconset resource="images.qrc" > <iconset resource="images.qrc">
<normaloff>:/images/document_open.svg</normaloff>:/images/document_open.svg</iconset> <normaloff>:/images/document_open.svg</normaloff>:/images/document_open.svg</iconset>
</property> </property>
<property name="text" > <property name="text">
<string>Open containing folder</string> <string>Open containing folder</string>
</property> </property>
</action> </action>
<action name="action_show_book_details" > <action name="action_show_book_details">
<property name="icon" > <property name="icon">
<iconset resource="images.qrc" > <iconset resource="images.qrc">
<normaloff>:/images/dialog_information.svg</normaloff>:/images/dialog_information.svg</iconset> <normaloff>:/images/dialog_information.svg</normaloff>:/images/dialog_information.svg</iconset>
</property> </property>
<property name="text" > <property name="text">
<string>Show book details</string> <string>Show book details</string>
</property> </property>
</action> </action>
<action name="action_books_by_same_author" > <action name="action_books_by_same_author">
<property name="icon" > <property name="icon">
<iconset resource="images.qrc" > <iconset resource="images.qrc">
<normaloff>:/images/user_profile.svg</normaloff>:/images/user_profile.svg</iconset> <normaloff>:/images/user_profile.svg</normaloff>:/images/user_profile.svg</iconset>
</property> </property>
<property name="text" > <property name="text">
<string>Books by same author</string> <string>Books by same author</string>
</property> </property>
</action> </action>
<action name="action_books_in_this_series" > <action name="action_books_in_this_series">
<property name="icon" > <property name="icon">
<iconset resource="images.qrc" > <iconset resource="images.qrc">
<normaloff>:/images/books_in_series.svg</normaloff>:/images/books_in_series.svg</iconset> <normaloff>:/images/books_in_series.svg</normaloff>:/images/books_in_series.svg</iconset>
</property> </property>
<property name="text" > <property name="text">
<string>Books in this series</string> <string>Books in this series</string>
</property> </property>
</action> </action>
<action name="action_books_by_this_publisher" > <action name="action_books_by_this_publisher">
<property name="icon" > <property name="icon">
<iconset resource="images.qrc" > <iconset resource="images.qrc">
<normaloff>:/images/publisher.png</normaloff>:/images/publisher.png</iconset> <normaloff>:/images/publisher.png</normaloff>:/images/publisher.png</iconset>
</property> </property>
<property name="text" > <property name="text">
<string>Books by this publisher</string> <string>Books by this publisher</string>
</property> </property>
</action> </action>
<action name="action_books_with_the_same_tags" > <action name="action_books_with_the_same_tags">
<property name="icon" > <property name="icon">
<iconset resource="images.qrc" > <iconset resource="images.qrc">
<normaloff>:/images/tags.svg</normaloff>:/images/tags.svg</iconset> <normaloff>:/images/tags.svg</normaloff>:/images/tags.svg</iconset>
</property> </property>
<property name="text" > <property name="text">
<string>Books with the same tags</string> <string>Books with the same tags</string>
</property> </property>
</action> </action>
<action name="action_send_specific_format_to_device" > <action name="action_send_specific_format_to_device">
<property name="icon" > <property name="icon">
<iconset resource="images.qrc" > <iconset resource="images.qrc">
<normaloff>:/images/book.svg</normaloff>:/images/book.svg</iconset> <normaloff>:/images/book.svg</normaloff>:/images/book.svg</iconset>
</property> </property>
<property name="text" > <property name="text">
<string>Send specific format to device</string> <string>Send specific format to device</string>
</property> </property>
</action> </action>
<action name="action_preferences">
<property name="icon">
<iconset resource="images.qrc">
<normaloff>:/images/config.svg</normaloff>:/images/config.svg</iconset>
</property>
<property name="text">
<string>Preferences</string>
</property>
<property name="toolTip">
<string>Configure calibre</string>
</property>
<property name="shortcut">
<string>Ctrl+P</string>
</property>
</action>
</widget> </widget>
<customwidgets> <customwidgets>
<customwidget> <customwidget>
@ -694,7 +711,7 @@
</customwidget> </customwidget>
</customwidgets> </customwidgets>
<resources> <resources>
<include location="images.qrc" /> <include location="images.qrc"/>
</resources> </resources>
<connections> <connections>
<connection> <connection>
@ -703,11 +720,11 @@
<receiver>search</receiver> <receiver>search</receiver>
<slot>clear()</slot> <slot>clear()</slot>
<hints> <hints>
<hint type="sourcelabel" > <hint type="sourcelabel">
<x>787</x> <x>787</x>
<y>215</y> <y>215</y>
</hint> </hint>
<hint type="destinationlabel" > <hint type="destinationlabel">
<x>755</x> <x>755</x>
<y>213</y> <y>213</y>
</hint> </hint>

View File

@ -450,7 +450,9 @@ class DocumentView(QWebView):
self.manager.scrolled(self.scroll_fraction) self.manager.scrolled(self.scroll_fraction)
def wheel_event(self, down=True): 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): def next_page(self):
if self.document.at_bottom: if self.document.at_bottom:
@ -538,6 +540,26 @@ class DocumentView(QWebView):
self.next_page() self.next_page()
elif key in [Qt.Key_PageUp, Qt.Key_Backspace, Qt.Key_Up]: elif key in [Qt.Key_PageUp, Qt.Key_Backspace, Qt.Key_Up]:
self.previous_page() 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: else:
return QWebView.keyPressEvent(self, event) return QWebView.keyPressEvent(self, event)

View File

@ -342,6 +342,12 @@ class EbookViewer(MainWindow, Ui_EbookViewer):
if pos is not None: if pos is not None:
self.goto_page(pos) 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): def goto_page(self, new_page):
if self.current_page is not None: if self.current_page is not None:
for page in self.iterator.spine: for page in self.iterator.spine:
@ -605,7 +611,7 @@ def config(defaults=None):
else: else:
c = StringConfig(defaults, desc) c = StringConfig(defaults, desc)
c.add_opt('--raise-window', default=False, c.add_opt('raise_window', ['--raise-window'], default=False,
help=_('If specified, viewer window will try to come to the ' help=_('If specified, viewer window will try to come to the '
'front when started.')) 'front when started.'))
return c return c

View File

@ -244,7 +244,10 @@ def do_add(db, paths, one_book_per_directory, recurse, add_duplicates):
if os.path.isdir(path): if os.path.isdir(path):
dirs.append(path) dirs.append(path)
else: else:
files.append(path) if os.path.exists(path):
files.append(path)
else:
print path, 'not found'
formats, metadata = [], [] formats, metadata = [], []
for book in files: for book in files:
@ -262,11 +265,13 @@ def do_add(db, paths, one_book_per_directory, recurse, add_duplicates):
formats.append(format) formats.append(format)
metadata.append(mi) metadata.append(mi)
file_duplicates = db.add_books(files, formats, metadata, add_duplicates=add_duplicates) file_duplicates = []
if not file_duplicates[0]: if files:
file_duplicates = [] file_duplicates = db.add_books(files, formats, metadata,
else: add_duplicates=add_duplicates)
file_duplicates = file_duplicates[0] if file_duplicates:
file_duplicates = file_duplicates[0]
dir_dups = [] dir_dups = []
for dir in dirs: for dir in dirs:
@ -286,7 +291,9 @@ def do_add(db, paths, one_book_per_directory, recurse, add_duplicates):
db.import_book(mi, formats) db.import_book(mi, formats)
else: else:
if dir_dups or file_duplicates: if dir_dups or file_duplicates:
print >>sys.stderr, _('The following books were not added as they already exist in the database (see --duplicates option):') print >>sys.stderr, _('The following books were not added as '
'they already exist in the database '
'(see --duplicates option):')
for mi, formats in dir_dups: for mi, formats in dir_dups:
title = mi.title title = mi.title
if isinstance(title, unicode): if isinstance(title, unicode):

View File

@ -23,6 +23,8 @@ from calibre.resources import jquery, server_resources, build_time
from calibre.library import server_config as config from calibre.library import server_config as config
from calibre.library.database2 import LibraryDatabase2, FIELD_MAP from calibre.library.database2 import LibraryDatabase2, FIELD_MAP
from calibre.utils.config import config_dir from calibre.utils.config import config_dir
from calibre.utils.mdns import publish as publish_zeroconf, \
stop_server as stop_zeroconf
build_time = datetime.strptime(build_time, '%d %m %Y %H%M%S') build_time = datetime.strptime(build_time, '%d %m %Y %H%M%S')
server_resources['jquery.js'] = jquery server_resources['jquery.js'] = jquery
@ -171,11 +173,14 @@ class LibraryServer(object):
try: try:
cherrypy.engine.start() cherrypy.engine.start()
self.is_running = True self.is_running = True
publish_zeroconf('Books in calibre', '_stanza._tcp',
self.opts.port, {'path':'/stanza'})
cherrypy.engine.block() cherrypy.engine.block()
except Exception, e: except Exception, e:
self.exception = e self.exception = e
finally: finally:
self.is_running = False self.is_running = False
stop_zeroconf()
def exit(self): def exit(self):
cherrypy.engine.exit() cherrypy.engine.exit()
@ -332,7 +337,11 @@ class LibraryServer(object):
@expose @expose
def index(self, **kwargs): def index(self, **kwargs):
'The / URL' 'The / URL'
return self.static('index.html') stanza = cherrypy.request.headers.get('Stanza-Device-Name', 919)
if stanza == 919:
return self.static('index.html')
return self.stanza()
@expose @expose
def get(self, what, id): def get(self, what, id):

View File

@ -123,13 +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? How do I use |app| with my iPhone?
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
First install the Stanza reader on your iPhone from http://www.lexcycle.com . Then, First install the Stanza reader on your iPhone from http://www.lexcycle.com . Then,
* Set the output format for calibre to EPUB (this can be done in the configuration dialog accessed by the little hammer icon next to the search bar)
* 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. * 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 the configurations dialog and leave |app| running. * Turn on the Content Server in |app|'s preferences and leave |app| running.
* 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`` Now you should be able to access your books on your iPhone.
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.
Library Management Library Management
------------------ ------------------

View File

@ -227,7 +227,8 @@ class WorkerMother(object):
return env return env
def spawn_free_spirit_osx(self, arg, type='free_spirit'): def spawn_free_spirit_osx(self, arg, type='free_spirit'):
script = 'from calibre.parallel import main; main(args=["calibre-parallel", %s]);'%repr(arg) script = ('from calibre.parallel import main; '
'main(args=["calibre-parallel", %s]);')%repr(arg)
exe = self.gui_executable if type == 'free_spirit' else self.executable exe = self.gui_executable if type == 'free_spirit' else self.executable
cmdline = [exe, '-c', self.prefix+script] cmdline = [exe, '-c', self.prefix+script]
child = WorkerStatus(subprocess.Popen(cmdline, env=self.get_env())) child = WorkerStatus(subprocess.Popen(cmdline, env=self.get_env()))

View File

@ -177,11 +177,11 @@ else:
compatibility='%s works on OS X Tiger and above.'%(__appname__,), compatibility='%s works on OS X Tiger and above.'%(__appname__,),
path=MOBILEREAD+file, app=__appname__, path=MOBILEREAD+file, app=__appname__,
note=Markup(\ note=Markup(\
''' u'''
<ol> <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>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>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> </ol>
''')) '''))
return 'binary.html', data, None return 'binary.html', data, None

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

1578
src/calibre/utils/Zeroconf.py Executable file

File diff suppressed because it is too large Load Diff

60
src/calibre/utils/mdns.py Normal file
View File

@ -0,0 +1,60 @@
from __future__ import with_statement
__license__ = 'GPL 3'
__copyright__ = '2009, Kovid Goyal <kovid@kovidgoyal.net>'
__docformat__ = 'restructuredtext en'
import socket
_server = None
def get_external_ip():
'Get IP address of interface used to connect to the outside world'
try:
ipaddr = socket.gethostbyname(socket.gethostname())
except:
ipaddr = '127.0.0.1'
if ipaddr == '127.0.0.1':
try:
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
s.connect(('google.com', 0))
ipaddr = s.getsockname()[0]
except:
pass
return ipaddr
def start_server():
global _server
if _server is None:
from calibre.utils.Zeroconf import Zeroconf
_server = Zeroconf()
return _server
def publish(desc, type, port, properties=None, add_hostname=True):
'''
Publish a service.
:param desc: Description of service
:param type: Name and type of service. For example _stanza._tcp
:param port: Port the service listens on
:param properties: An optional dictionary whose keys and values will be put
into the TXT record.
'''
port = int(port)
server = start_server()
hostname = socket.gethostname().partition('.')[0]
if add_hostname:
desc += ' (on %s)'%hostname
local_ip = get_external_ip()
type = type+'.local.'
from calibre.utils.Zeroconf import ServiceInfo
service = ServiceInfo(type, desc+'.'+type,
address=socket.inet_aton(local_ip),
port=port,
properties=properties,
server=hostname+'.local.')
server.registerService(service)
def stop_server():
global _server
if _server is not None:
_server.close()

View File

@ -34,6 +34,7 @@ recipe_modules = ['recipe_' + r for r in (
'al_jazeera', 'winsupersite', 'borba', 'courrierinternational', 'al_jazeera', 'winsupersite', 'borba', 'courrierinternational',
'lamujerdemivida', 'soldiers', 'theonion', 'news_times', 'lamujerdemivida', 'soldiers', 'theonion', 'news_times',
'el_universal', 'mediapart', 'wikinews_en', 'ecogeek', 'daily_mail', 'el_universal', 'mediapart', 'wikinews_en', 'ecogeek', 'daily_mail',
'new_york_review_of_books_no_sub', 'politico',
)] )]
import re, imp, inspect, time, os import re, imp, inspect, time, os

View File

@ -1,14 +1,7 @@
#!/usr/bin/env python #!/usr/bin/env python
__license__ = 'GPL v3' __license__ = 'GPL v3'
__copyright__ = '2008, Darko Miletic <darko.miletic at gmail.com>' __copyright__ = '2008-2009, AprilHare, 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 newscientist.com
''' '''
@ -16,33 +9,61 @@ newscientist.com
from calibre.web.feeds.news import BasicNewsRecipe from calibre.web.feeds.news import BasicNewsRecipe
class NewScientist(BasicNewsRecipe): class NewScientist(BasicNewsRecipe):
title = u'New Scientist - Online News' title = 'New Scientist - Online News'
__author__ = 'Darko Miletic' __author__ = 'Darko Miletic'
description = 'News from Science' description = 'Science news and science articles from New Scientist.'
language = _('English') language = _('English')
publisher = 'New Scientist'
category = 'science news, science articles, science jobs, drugs, cancer, depression, computer software, sex'
delay = 3
oldest_article = 7 oldest_article = 7
max_articles_per_feed = 100 max_articles_per_feed = 100
no_stylesheets = True no_stylesheets = True
use_embedded_content = False 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']})]
keep_only_tags = [
dict(name='div' , attrs={'id':'pgtop' })
,dict(name='div' , attrs={'id':'maincol' })
]
remove_tags = [ remove_tags = [
dict(name='div' , attrs={'class':'hldBd' }) dict(name='div', attrs={'class':['hldBd','adline','pnl','infotext' ]})
,dict(name='div' , attrs={'id':'compnl' }) ,dict(name='div', attrs={'id' :['compnl','artIssueInfo','artTools']})
,dict(name='div' , attrs={'id':'artIssueInfo' }) ,dict(name='p' , attrs={'class':['marker','infotext' ]})
] ]
feeds = [ feeds = [
(u'Latest Headlines' , u'http://feeds.newscientist.com/science-news' ) (u'Latest Headlines' , u'http://feeds.newscientist.com/science-news' )
,(u'Magazine' , u'http://www.newscientist.com/feed/magazine' ) ,(u'Magazine' , u'http://www.newscientist.com/feed/magazine' )
,(u'Health' , u'http://www.newscientist.com/feed/view?id=2&type=channel' ) ,(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'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'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'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'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'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' ) ,(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'

View File

@ -0,0 +1,44 @@
#!/usr/bin/env python
__license__ = 'GPL v3'
__copyright__ = '2008, Kovid Goyal kovid@kovidgoyal.net'
__docformat__ = 'restructuredtext en'
'''
nybooks.com
'''
from calibre.web.feeds.news import BasicNewsRecipe
from lxml import html
from calibre.constants import preferred_encoding
class NewYorkReviewOfBooks(BasicNewsRecipe):
title = u'New York Review of Books (no subscription)'
description = u'Book reviews'
language = _('English')
__author__ = 'Kovid Goyal'
remove_tags_before = {'id':'container'}
remove_tags = [{'class':['noprint', 'ad', 'footer']}, {'id':'right-content'}]
def parse_index(self):
root = html.fromstring(self.browser.open('http://www.nybooks.com/current-issue').read())
date = root.xpath('//h4[@class = "date"]')[0]
self.timefmt = ' ['+date.text.encode(preferred_encoding)+']'
articles = []
for tag in date.itersiblings():
if tag.tag == 'h4': break
if tag.tag == 'p':
if tag.get('class') == 'indented':
articles[-1]['description'] += html.tostring(tag)
else:
href = tag.xpath('descendant::a[@href]')[0].get('href')
article = {
'title': u''.join(tag.xpath('descendant::text()')),
'date' : '',
'url' : 'http://www.nybooks.com'+href,
'description': '',
}
articles.append(article)
return [('Current Issue', articles)]

View File

@ -0,0 +1,66 @@
#!/usr/bin/env python
__license__ = 'GPL v3'
__copyright__ = '2009, Darko Miletic <darko.miletic at gmail.com>'
'''
politico.com
'''
from calibre.web.feeds.news import BasicNewsRecipe
class Politico(BasicNewsRecipe):
title = 'Politico'
__author__ = 'Darko Miletic'
description = 'Political news from USA'
publisher = 'Capitol News Company, LLC'
category = 'news, politics, USA'
oldest_article = 7
max_articles_per_feed = 100
use_embedded_content = False
no_stylesheets = True
remove_javascript = True
encoding = 'cp1252'
language = _('English')
html2lrf_options = [
'--comment', description
, '--category', category
, '--publisher', publisher
, '--ignore-tables'
]
html2epub_options = 'publisher="' + publisher + '"\ncomments="' + description + '"\ntags="' + category + '"\nlinearize_tables=True'
remove_tags = [dict(name=['notags','embed','object','link','img'])]
feeds = [
(u'Top Stories' , u'http://www.politico.com/rss/politicopicks.xml' )
,(u'Congress' , u'http://www.politico.com/rss/congress.xml' )
,(u'Ideas' , u'http://www.politico.com/rss/ideas.xml' )
,(u'Life' , u'http://www.politico.com/rss/life.xml' )
,(u'Lobbyists' , u'http://www.politico.com/rss/lobbyists.xml' )
,(u'Pitboss' , u'http://www.politico.com/rss/pitboss.xml' )
,(u'Politics' , u'http://www.politico.com/rss/politics.xml' )
,(u'Roger Simon' , u'http://www.politico.com/rss/rogersimon.xml' )
,(u'Suite Talk' , u'http://www.politico.com/rss/suitetalk.xml' )
,(u'Playbook' , u'http://www.politico.com/rss/playbook.xml' )
,(u'The Huddle' , u'http://www.politico.com/rss/huddle.xml' )
]
def preprocess_html(self, soup):
mtag = '<meta http-equiv="Content-Language" content="en-US"/>'
soup.head.insert(0,mtag)
for item in soup.findAll(style=True):
del item['style']
return soup
def print_url(self, soup, default):
printtags = soup.findAll('a',href=True)
for printtag in printtags:
if printtag.string == "Print":
return printtag['href']
return default
def print_version(self, url):
soup = self.index_to_soup(url)
return self.print_url(soup, None)

View File

@ -14,6 +14,7 @@ class Time(BasicNewsRecipe):
description = 'Weekly magazine' description = 'Weekly magazine'
oldest_article = 7 oldest_article = 7
max_articles_per_feed = 100 max_articles_per_feed = 100
encoding = 'utf-8'
no_stylesheets = True no_stylesheets = True
language = _('English') language = _('English')
use_embedded_content = False use_embedded_content = False

View File

@ -5,3 +5,4 @@
* Use multiprocessing for cpu_count instead of QThread * Use multiprocessing for cpu_count instead of QThread
* Rationalize books table. Add a pubdate column, remove the uri column (and associated support in add_books) and convert series_index to a float.

106
upload.py
View File

@ -709,65 +709,67 @@ class upload(OptionlessCommand):
('stage3', None) ('stage3', None)
] ]
class upload_rss(OptionlessCommand): try:
class upload_rss(OptionlessCommand):
from bzrlib import log as blog from bzrlib import log as blog
class ChangelogFormatter(blog.LogFormatter): class ChangelogFormatter(blog.LogFormatter):
supports_tags = True supports_tags = True
supports_merge_revisions = False supports_merge_revisions = False
def __init__(self, num_of_versions=20): def __init__(self, num_of_versions=20):
from calibre.utils.rss_gen import RSS2 from calibre.utils.rss_gen import RSS2
self.num_of_versions = num_of_versions self.num_of_versions = num_of_versions
self.rss = RSS2( self.rss = RSS2(
title = 'calibre releases', title = 'calibre releases',
link = 'http://calibre.kovidgoyal.net/wiki/Changelog', link = 'http://calibre.kovidgoyal.net/wiki/Changelog',
description = 'Latest release of calibre', description = 'Latest release of calibre',
lastBuildDate = datetime.utcnow() lastBuildDate = datetime.utcnow()
) )
self.current_entry = None self.current_entry = None
def log_revision(self, r): def log_revision(self, r):
from calibre.utils.rss_gen import RSSItem, Guid from calibre.utils.rss_gen import RSSItem, Guid
if len(self.rss.items) > self.num_of_versions-1: if len(self.rss.items) > self.num_of_versions-1:
return return
msg = r.rev.message msg = r.rev.message
match = re.match(r'version\s+(\d+\.\d+.\d+)', msg) match = re.match(r'version\s+(\d+\.\d+.\d+)', msg)
if match: if match:
if self.current_entry is not None: if self.current_entry is not None:
mkup = '<div><ul>%s</ul></div>' mkup = '<div><ul>%s</ul></div>'
self.current_entry.description = mkup%(''.join( self.current_entry.description = mkup%(''.join(
self.current_entry.description)) self.current_entry.description))
self.rss.items.append(self.current_entry) self.rss.items.append(self.current_entry)
timestamp = r.rev.timezone + r.rev.timestamp timestamp = r.rev.timezone + r.rev.timestamp
self.current_entry = RSSItem( self.current_entry = RSSItem(
title = 'calibre %s released'%match.group(1), title = 'calibre %s released'%match.group(1),
link = 'http://calibre.kovidgoyal.net/download', link = 'http://calibre.kovidgoyal.net/download',
guid = Guid(match.group(), False), guid = Guid(match.group(), False),
pubDate = datetime(*time.gmtime(timestamp)[:6]), pubDate = datetime(*time.gmtime(timestamp)[:6]),
description = [] description = []
) )
elif self.current_entry is not None: elif self.current_entry is not None:
if re.search(r'[a-zA-Z]', msg) and len(msg.strip()) > 5: if re.search(r'[a-zA-Z]', msg) and len(msg.strip()) > 5:
if 'translation' not in msg and not msg.startswith('IGN'): if 'translation' not in msg and not msg.startswith('IGN'):
msg = msg.replace('<', '&lt;').replace('>', '&gt;') msg = msg.replace('<', '&lt;').replace('>', '&gt;')
msg = re.sub('#(\d+)', r'<a href="http://calibre.kovidgoyal.net/ticket/\1">#\1</a>', msg = re.sub('#(\d+)', r'<a href="http://calibre.kovidgoyal.net/ticket/\1">#\1</a>',
msg) msg)
self.current_entry.description.append( self.current_entry.description.append(
'<li>%s</li>'%msg.strip()) '<li>%s</li>'%msg.strip())
def run(self): def run(self):
from bzrlib import log, branch from bzrlib import log, branch
bzr_path = os.path.expanduser('~/work/calibre') bzr_path = os.path.expanduser('~/work/calibre')
b = branch.Branch.open(bzr_path) b = branch.Branch.open(bzr_path)
lf = upload_rss.ChangelogFormatter() lf = upload_rss.ChangelogFormatter()
log.show_log(b, lf) log.show_log(b, lf)
lf.rss.write_xml(open('/tmp/releases.xml', 'wb')) 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()) subprocess.check_call('scp /tmp/releases.xml divok:/var/www/calibre.kovidgoyal.net/htdocs/downloads'.split())
except ImportError:
upload_rss = None