Sync to trunk.

This commit is contained in:
John Schember 2010-02-23 06:25:46 -05:00
commit b4e861dcbf
17 changed files with 228 additions and 203 deletions

View File

@ -9,17 +9,15 @@ from calibre.web.feeds.news import BasicNewsRecipe
class heiseDe(BasicNewsRecipe):
title = 'heise'
description = 'Computernews from Germany'
__author__ = 'Oliver Niesner'
language = 'de'
use_embedded_content = False
timefmt = ' [%d %b %Y]'
max_articles_per_feed = 40
no_stylesheets = True
remove_tags = [dict(id='navi_top'),
dict(id='navi_bottom'),
dict(id='logo'),
@ -35,12 +33,10 @@ class heiseDe(BasicNewsRecipe):
dict(name='div', attrs={'class':'bcadv ISI_IGNORE'}),
dict(name='p', attrs={'class':'news_option'}),
dict(name='p', attrs={'class':'news_navi'}),
dict(name='p', attrs={'class':'news_foren'})]
remove_tags_after = [dict(name='p', attrs={'class':'news_foren'})]
feeds = [ ('heise', 'http://www.heise.de/newsticker/heise.rdf') ]
dict(name='div', attrs={'class':'news_foren'})]
remove_tags_after = [dict(name='div', attrs={'class':'news_foren'})]
feeds = [ ('heise', 'http://www.heise.de/newsticker/heise.rdf') ]

View File

@ -1,11 +1,11 @@
#!/usr/bin/env python
__license__ = 'GPL v3'
__copyright__ = '2008-2009, AprilHare, Darko Miletic <darko.miletic at gmail.com>'
__copyright__ = '2008-2010, AprilHare, Darko Miletic <darko.miletic at gmail.com>'
'''
newscientist.com
'''
import re
from calibre.web.feeds.news import BasicNewsRecipe
class NewScientist(BasicNewsRecipe):
@ -15,12 +15,14 @@ class NewScientist(BasicNewsRecipe):
language = 'en'
publisher = 'New Scientist'
category = 'science news, science articles, science jobs, drugs, cancer, depression, computer software'
delay = 3
oldest_article = 7
max_articles_per_feed = 100
no_stylesheets = True
use_embedded_content = False
cover_url = 'http://www.newscientist.com/currentcover.jpg'
masthead_url = 'http://www.newscientist.com/img/misc/ns_logo.jpg'
encoding = 'utf-8'
extra_css = ' body{font-family: Arial,sans-serif} img{margin-bottom: 0.8em} '
conversion_options = {
'comment' : description
@ -28,14 +30,18 @@ class NewScientist(BasicNewsRecipe):
, 'publisher' : publisher
, 'language' : language
}
preprocess_regexps = [(re.compile(r'</title>.*?</head>', re.DOTALL|re.IGNORECASE),lambda match: '</title></head>')]
keep_only_tags = [dict(name='div', attrs={'id':['pgtop','maincol']})]
keep_only_tags = [dict(name='div', attrs={'id':['pgtop','maincol','nsblgposts','hldgalcols']})]
remove_tags = [
dict(name='div', attrs={'class':['hldBd','adline','pnl','infotext' ]})
,dict(name='div', attrs={'id' :['compnl','artIssueInfo','artTools']})
,dict(name='p' , attrs={'class':['marker','infotext' ]})
dict(name='div' , attrs={'class':['hldBd','adline','pnl','infotext' ]})
,dict(name='div' , attrs={'id' :['compnl','artIssueInfo','artTools']})
,dict(name='p' , attrs={'class':['marker','infotext' ]})
,dict(name='meta' , attrs={'name' :'description' })
]
remove_tags_after = dict(attrs={'class':'nbpcopy'})
remove_attributes = ['height','width']
feeds = [
(u'Latest Headlines' , u'http://feeds.newscientist.com/science-news' )
@ -50,9 +56,15 @@ class NewScientist(BasicNewsRecipe):
]
def get_article_url(self, article):
url = article.get('guid', None)
return url
return article.get('guid', None)
def print_version(self, url):
return url + '?full=true&print=true'
def preprocess_html(self, soup):
for tg in soup.findAll('a'):
if tg.string == 'Home':
tg.parent.extract()
return self.adeify_images(soup)
return self.adeify_images(soup)

View File

@ -407,7 +407,7 @@ from calibre.devices.blackberry.driver import BLACKBERRY
from calibre.devices.cybook.driver import CYBOOK
from calibre.devices.eb600.driver import EB600, COOL_ER, SHINEBOOK, \
POCKETBOOK360, GER2, ITALICA, ECLICTO, DBOOK, INVESBOOK, \
BOOQ
BOOQ, ELONEX
from calibre.devices.iliad.driver import ILIAD
from calibre.devices.irexdr.driver import IREXDR1000, IREXDR800
from calibre.devices.jetbook.driver import JETBOOK
@ -496,6 +496,7 @@ plugins += [
README,
N516,
EB511,
ELONEX
]
plugins += [x for x in list(locals().values()) if isinstance(x, type) and \
x.__name__.endswith('MetadataReader')]

View File

@ -235,7 +235,7 @@ class SonyReaderOutput(OutputProfile):
description = _('This profile is intended for the SONY PRS line. '
'The 500/505/600/700 etc.')
screen_size = (600, 775)
screen_size = (590, 775)
dpi = 168.451
fbase = 12
fsizes = [7.5, 9, 10, 12, 15.5, 20, 22, 24]

View File

@ -195,3 +195,15 @@ class BOOQ(EB600):
WINDOWS_MAIN_MEM = 'EB600'
WINDOWS_CARD_A_MEM = 'EB600'
class ELONEX(EB600):
name = 'Elonex 600EB'
gui_name = 'Elonex'
FORMATS = ['epub', 'pdf', 'txt', 'html']
VENDOR_NAME = 'ELONEX'
WINDOWS_MAIN_MEM = 'EBOOK'
WINDOWS_CARD_A_MEM = 'EBOOK'

View File

@ -4,11 +4,11 @@ __license__ = 'GPL v3'
__copyright__ = '2008, Kovid Goyal <kovid at kovidgoyal.net>,' \
' and Alex Bramley <a.bramley at gmail.com>.'
import os, shutil, uuid
import os, shutil, uuid, re
from tempfile import mkdtemp
from mimetypes import guess_type as guess_mimetype
from BeautifulSoup import BeautifulSoup
from BeautifulSoup import BeautifulSoup, NavigableString
from lxml import html
from pychm.chm import CHMFile
from pychm.chmlib import (
@ -29,6 +29,17 @@ def match_string(s1, s2_already_lowered):
return True
return False
def check_all_prev_empty(tag):
if tag is None:
return True
if tag.__class__ == NavigableString and not check_empty(tag):
return False
return check_all_prev_empty(tag.previousSibling)
def check_empty(s, rex = re.compile(r'\S')):
return rex.search(s) is None
def option_parser():
parser = OptionParser(usage=_('%prog [options] mybook.chm'))
parser.add_option('--output-dir', '-d', default='.', help=_('Output directory. Defaults to current directory'), dest='output')
@ -155,6 +166,12 @@ class CHMReader(CHMFile):
# for some very odd reason each page's content appears to be in a table
# too. and this table has sub-tables for random asides... grr.
# remove br at top of page if present after nav bars removed
br = soup('br')
if br:
if check_all_prev_empty(br[0].previousSibling):
br[0].extract()
# some images seem to be broken in some chm's :/
for img in soup('img'):
try:

View File

@ -311,9 +311,10 @@ class MetadataUpdater(object):
return StreamSlicer(self.stream, start, stop)
def update(self, mi):
def pop_exth_record(exth_id):
if exth_id in self.original_exth_records:
self.original_exth_records.pop(exth_id)
def update_exth_record(rec):
recs.append(rec)
if rec[0] in self.original_exth_records:
self.original_exth_records.pop(rec[0])
if self.type != "BOOKMOBI":
raise MobiError("Setting metadata only supported for MOBI files of type 'BOOK'.\n"
@ -328,47 +329,36 @@ class MetadataUpdater(object):
pas = False
if mi.author_sort and pas:
authors = mi.author_sort
recs.append((100, authors.encode(self.codec, 'replace')))
pop_exth_record(100)
update_exth_record((100, authors.encode(self.codec, 'replace')))
elif mi.authors:
authors = '; '.join(mi.authors)
recs.append((100, authors.encode(self.codec, 'replace')))
pop_exth_record(100)
update_exth_record((100, authors.encode(self.codec, 'replace')))
if mi.publisher:
recs.append((101, mi.publisher.encode(self.codec, 'replace')))
pop_exth_record(101)
update_exth_record((101, mi.publisher.encode(self.codec, 'replace')))
if mi.comments:
recs.append((103, mi.comments.encode(self.codec, 'replace')))
pop_exth_record(103)
update_exth_record((103, mi.comments.encode(self.codec, 'replace')))
if mi.isbn:
recs.append((104, mi.isbn.encode(self.codec, 'replace')))
pop_exth_record(104)
update_exth_record((104, mi.isbn.encode(self.codec, 'replace')))
if mi.tags:
subjects = '; '.join(mi.tags)
recs.append((105, subjects.encode(self.codec, 'replace')))
pop_exth_record(105)
update_exth_record((105, subjects.encode(self.codec, 'replace')))
if mi.pubdate:
recs.append((106, str(mi.pubdate).encode(self.codec, 'replace')))
pop_exth_record(106)
update_exth_record((106, str(mi.pubdate).encode(self.codec, 'replace')))
elif mi.timestamp:
recs.append((106, str(mi.timestamp).encode(self.codec, 'replace')))
pop_exth_record(106)
update_exth_record((106, str(mi.timestamp).encode(self.codec, 'replace')))
elif self.timestamp:
recs.append((106, self.timestamp))
pop_exth_record(106)
update_exth_record((106, self.timestamp))
else:
recs.append((106, nowf().isoformat().encode(self.codec, 'replace')))
pop_exth_record(106)
update_exth_record((106, nowf().isoformat().encode(self.codec, 'replace')))
if self.cover_record is not None:
recs.append((201, pack('>I', self.cover_rindex)))
recs.append((203, pack('>I', 0)))
pop_exth_record(201)
pop_exth_record(203)
update_exth_record((201, pack('>I', self.cover_rindex)))
update_exth_record((203, pack('>I', 0)))
if self.thumbnail_record is not None:
recs.append((202, pack('>I', self.thumbnail_rindex)))
pop_exth_record(202)
update_exth_record((202, pack('>I', self.thumbnail_rindex)))
if 503 in self.original_exth_records:
update_exth_record((503, mi.title.encode(self.codec, 'replace')))
# Restore any original EXTH fields that weren't updated
# Include remaining original EXTH fields
for id in sorted(self.original_exth_records):
recs.append((id, self.original_exth_records[id]))
recs = sorted(recs, key=lambda x:(x[0],x[0]))

View File

@ -10,6 +10,7 @@ import re
import struct
import textwrap
import cStringIO
import sys
try:
from PIL import Image as PILImage
@ -806,13 +807,20 @@ def get_metadata(stream):
if mh.exth.mi is not None:
mi = mh.exth.mi
else:
with TemporaryDirectory('_mobi_meta_reader') as tdir:
with CurrentDir(tdir):
mr = MobiReader(stream, log)
parse_cache = {}
mr.extract_content(tdir, parse_cache)
if mr.embedded_mi is not None:
mi = mr.embedded_mi
size = sys.maxint
if hasattr(stream, 'seek') and hasattr(stream, 'tell'):
pos = stream.tell()
stream.seek(0, 2)
size = stream.tell()
stream.seek(pos)
if size < 4*1024*1024:
with TemporaryDirectory('_mobi_meta_reader') as tdir:
with CurrentDir(tdir):
mr = MobiReader(stream, log)
parse_cache = {}
mr.extract_content(tdir, parse_cache)
if mr.embedded_mi is not None:
mi = mr.embedded_mi
if hasattr(mh.exth, 'cover_offset'):
cover_index = mh.first_image_index + mh.exth.cover_offset
data = mh.section_data(int(cover_index))

View File

@ -64,5 +64,4 @@ class PluginWidget(QWidget,Ui_Form):
opts_dict[opt[0]] = opt_value.split(',')
opts_dict['output_profile'] = [load_defaults('page_setup')['output_profile']]
return opts_dict

View File

@ -8,7 +8,8 @@ __docformat__ = 'restructuredtext en'
import os, sys
from PyQt4.Qt import QDialog
from PyQt4 import QtGui
from PyQt4.Qt import QDialog, SIGNAL
from calibre.customize.ui import config
from calibre.gui2.dialogs.catalog_ui import Ui_Dialog
@ -119,9 +120,11 @@ class Catalog(QDialog, Ui_Dialog):
self.sync.setChecked(dynamic.get('catalog_sync_to_device', True))
self.format.currentIndexChanged.connect(self.show_plugin_tab)
self.connect(self.buttonBox.button(QtGui.QDialogButtonBox.Apply),
SIGNAL("clicked()"),
self.apply)
self.show_plugin_tab(None)
def show_plugin_tab(self, idx):
cf = unicode(self.format.currentText()).lower()
while self.tabs.count() > 1:
@ -147,11 +150,20 @@ class Catalog(QDialog, Ui_Dialog):
ans = w.options()
return ans
def accept(self):
def save_catalog_settings(self):
self.catalog_format = unicode(self.format.currentText())
dynamic.set('catalog_preferred_format', self.catalog_format)
self.catalog_title = unicode(self.title.text())
dynamic.set('catalog_last_used_title', self.catalog_title)
self.catalog_sync = bool(self.sync.isChecked())
dynamic.set('catalog_sync_to_device', self.catalog_sync)
QDialog.accept(self)
def apply(self):
# Store current values without building catalog
self.save_catalog_settings()
if self.tabs.count() > 1:
self.tabs.widget(1).options()
def accept(self):
self.save_catalog_settings()
return QDialog.accept(self)

View File

@ -17,110 +17,94 @@
<iconset>
<normaloff>:/images/library.png</normaloff>:/images/library.png</iconset>
</property>
<widget class="QDialogButtonBox" name="buttonBox">
<property name="geometry">
<rect>
<x>430</x>
<y>470</y>
<width>164</width>
<height>32</height>
</rect>
</property>
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="standardButtons">
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
</property>
</widget>
<widget class="QTabWidget" name="tabs">
<property name="geometry">
<rect>
<x>12</x>
<y>39</y>
<width>579</width>
<height>411</height>
</rect>
</property>
<property name="currentIndex">
<number>0</number>
</property>
<widget class="QWidget" name="tab">
<attribute name="title">
<string>Catalog options</string>
</attribute>
<layout class="QGridLayout" name="gridLayout_2">
<item row="0" column="0">
<widget class="QLabel" name="label">
<property name="text">
<string>Catalog &amp;format:</string>
</property>
<property name="buddy">
<cstring>format</cstring>
</property>
</widget>
</item>
<item row="0" column="2">
<widget class="QComboBox" name="format"/>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label_2">
<property name="text">
<string>Catalog &amp;title (existing catalog with the same title will be replaced):</string>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
<property name="buddy">
<cstring>title</cstring>
</property>
</widget>
</item>
<item row="1" column="2">
<widget class="QLineEdit" name="title"/>
</item>
<item row="3" column="0">
<widget class="QCheckBox" name="sync">
<property name="text">
<string>&amp;Send catalog to device automatically</string>
</property>
</widget>
</item>
<item row="2" column="1">
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>299</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
</widget>
<widget class="QLabel" name="count">
<property name="geometry">
<rect>
<x>12</x>
<y>12</y>
<width>301</width>
<height>17</height>
</rect>
</property>
<property name="font">
<font>
<weight>75</weight>
<bold>true</bold>
</font>
</property>
<property name="text">
<string>Generate catalog for {0} books</string>
</property>
</widget>
<layout class="QGridLayout" name="gridLayout">
<item row="0" column="0">
<widget class="QLabel" name="count">
<property name="font">
<font>
<weight>75</weight>
<bold>true</bold>
</font>
</property>
<property name="text">
<string>Generate catalog for {0} books</string>
</property>
</widget>
</item>
<item row="1" column="0" colspan="2">
<widget class="QTabWidget" name="tabs">
<property name="currentIndex">
<number>0</number>
</property>
<widget class="QWidget" name="tab">
<attribute name="title">
<string>Catalog options</string>
</attribute>
<layout class="QGridLayout" name="gridLayout_2">
<item row="0" column="0">
<widget class="QLabel" name="label">
<property name="text">
<string>Catalog &amp;format:</string>
</property>
<property name="buddy">
<cstring>format</cstring>
</property>
</widget>
</item>
<item row="0" column="2">
<widget class="QComboBox" name="format"/>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label_2">
<property name="text">
<string>Catalog &amp;title (existing catalog with the same title will be replaced):</string>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
<property name="buddy">
<cstring>title</cstring>
</property>
</widget>
</item>
<item row="1" column="2">
<widget class="QLineEdit" name="title"/>
</item>
<item row="3" column="0">
<widget class="QCheckBox" name="sync">
<property name="text">
<string>&amp;Send catalog to device automatically</string>
</property>
</widget>
</item>
<item row="2" column="1">
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>299</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
</widget>
</item>
<item row="2" column="1">
<widget class="QDialogButtonBox" name="buttonBox">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="standardButtons">
<set>QDialogButtonBox::Apply|QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
</property>
</widget>
</item>
</layout>
</widget>
<resources>
<include location="../../../work/calibre/resources/images.qrc"/>

View File

@ -250,7 +250,8 @@ def generate_catalog(parent, dbspec, ids, device):
# Profile the connected device
# Parallel initialization in calibre.library.cli:command_catalog()
connected_device = { 'storage':None,'serial':None,'name':None}
connected_device = { 'storage':None,'serial':None,'save_template':None,'name':None}
if device:
try:
storage = []
@ -260,10 +261,11 @@ def generate_catalog(parent, dbspec, ids, device):
storage.append(os.path.join(device._card_a_prefix, device.EBOOK_DIR_CARD_A))
if device._card_b_prefix:
storage.append(os.path.join(device._card_b_prefix, device.EBOOK_DIR_CARD_B))
connected_device = {'storage': storage,
'serial': device.detected_device.serial if \
hasattr(device.detected_device,'serial') else None,
'name': device.gui_name}
connected_device = { 'storage': storage,
'serial': device.detected_device.serial if \
hasattr(device.detected_device,'serial') else None,
'save_template': device.save_template(),
'name': device.gui_name}
except:
pass

View File

@ -1226,7 +1226,7 @@ class EPUB_MOBI(CatalogPlugin):
emTag = Tag(soup, "em")
emTag.insert(0, aTag)
if ttc < len(title['tags']):
emTag.insert(1, NavigableString(', '))
emTag.insert(1, NavigableString(' &middot; '))
tagsTag.insert(ttc, emTag)
ttc += 1
@ -3448,6 +3448,10 @@ class EPUB_MOBI(CatalogPlugin):
build_log = []
build_log.append(u"%s(): Generating %s %sin %s environment" %
(self.name,self.fmt,'for %s ' % opts.output_profile if opts.output_profile else '',
'CLI' if opts.cli_environment else 'GUI'))
# If exclude_genre is blank, assume user wants all genre tags included
if opts.exclude_genre.strip() == '':
opts.exclude_genre = '\[^.\]'
@ -3455,22 +3459,21 @@ class EPUB_MOBI(CatalogPlugin):
if opts.connected_device['name']:
if opts.connected_device['serial']:
build_log.append(" connected_device: '%s' #%s%s " % \
build_log.append(u" connected_device: '%s' #%s%s " % \
(opts.connected_device['name'],
opts.connected_device['serial'][0:4],
'x' * (len(opts.connected_device['serial']) - 4)))
build_log.append(u" save_template: '%s'" % opts.connected_device['save_template'])
else:
build_log.append(" connected_device: '%s'" % opts.connected_device['name'])
build_log.append(u" connected_device: '%s'" % opts.connected_device['name'])
for storage in opts.connected_device['storage']:
if storage:
build_log.append(" mount point: %s" % storage)
build_log.append(u" mount point: %s" % storage)
build_log.append(u" save_template: '%s'" % opts.connected_device['save_template'])
opts_dict = vars(opts)
build_log.append(u"%s(): Generating %s %sin %s environment" %
(self.name,self.fmt,'for %s ' % opts.output_profile if opts.output_profile else '',
'CLI' if opts.cli_environment else 'GUI'))
if opts_dict['ids']:
build_log.append(" Book count: %d" % len(opts_dict['ids']))
build_log.append(" book count: %d" % len(opts_dict['ids']))
sections_list = ['Descriptions','Authors']
if opts.generate_titles:
@ -3479,7 +3482,7 @@ class EPUB_MOBI(CatalogPlugin):
sections_list.append('Recently Added')
if not opts.exclude_genre.strip() == '.':
sections_list.append('Genres')
build_log.append(u"Creating Sections for %s" % ', '.join(sections_list))
build_log.append(u" Sections: %s" % ', '.join(sections_list))
# Display opts
keys = opts_dict.keys()
@ -3499,16 +3502,16 @@ class EPUB_MOBI(CatalogPlugin):
# Launch the Catalog builder
catalog = self.CatalogBuilder(db, opts, self, report_progress=notification)
if opts.verbose:
log.info("Begin catalog source generation")
log.info(" Begin catalog source generation")
catalog.createDirectoryStructure()
catalog.copyResources()
catalog.calculateThumbnailSize()
catalog_source_built = catalog.buildSources()
if opts.verbose:
if catalog_source_built:
log.info("Finished catalog source generation\n")
log.info(" Finished catalog source generation\n")
else:
log.warn("No database hits with supplied criteria")
log.warn(" No database hits with supplied criteria")
if catalog_source_built:
recommendations = []

View File

@ -683,7 +683,7 @@ def command_catalog(args, dbpath):
# No support for connected device in CLI environment
# Parallel initialization in calibre.gui2.tools:generate_catalog()
opts.connected_device = {'storage':None,'serial':None,'name':None}
opts.connected_device = { 'storage':None,'serial':None,'save_template':None,'name':None}
with plugin:
plugin.run(args[1], opts, get_db(dbpath, opts))

View File

@ -180,7 +180,7 @@ Why is my device not detected in linux?
grep SYSFS_DEPRECATED /boot/config-`uname -r`
You should see something like ``CONFIG_SYSFS_DEPRECATED_V2 is not set``.
You should see something like ``CONFIG_SYSFS_DEPRECATED_V2 is not set``. If you don't you have to either recompile your kernel with the correct setting, or upgrade your linux distro to a more modern version, where this will not be set.
Library Management
------------------
@ -305,7 +305,7 @@ Your antivirus program is wrong. |app| is a completely open source product. You
How do I use purchased EPUB books with |app|?
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Most purchased EPUB books have `DRM <http://wiki.mobileread.com/wiki/DRM>`_. This prevents |app| from opening them. You can still use |app| to store and transfer them to your SONY Reader. First, you must authorize your reader on a windows machine with Adobe Digital Editions. Once this is done, EPUB books transferred with |app| will work fine on your reader. When you purchase an epub book from a website, you will get an ".acsm" file. This file should be opened with Adobe Digital Editions, which will then download the actual ".epub" e-book. The e-book file will be stored in the folder "My Digital Editions", from where you can add it to |app|.
Most purchased EPUB books have `DRM <http://wiki.mobileread.com/wiki/DRM>`_. This prevents |app| from opening them. You can still use |app| to store and transfer them to your e-book reader. First, you must authorize your reader on a windows machine with Adobe Digital Editions. Once this is done, EPUB books transferred with |app| will work fine on your reader. When you purchase an epub book from a website, you will get an ".acsm" file. This file should be opened with Adobe Digital Editions, which will then download the actual ".epub" e-book. The e-book file will be stored in the folder "My Digital Editions", from where you can add it to |app|.
I want some feature added to |app|. What can I do?

View File

@ -134,7 +134,7 @@ The :guilabel:`Convert E-books` action has three variations, accessed by the arr
2. **Bulk convert**: This allows you to specify options only once to convert a number of ebooks in bulk.
3. **Create catalog**: This action allow yous to generate a complete listing with all metadata of the books in your library, in several formats, like XML, CSV, EPUB and MOBI. The catalog will contain all the books showing in the library view currently, so you can use the search features to limit the books to be catalogued. In addition, if you select multiple books using the mouse, only those books will be added to the catalog. If you generate the catalog in an e-book format such as EPUB or MOBI, the next time you connect your e-book reader, the catalog will be automatically sent to the device.
3. **Create catalog**: This action allows you to generate a complete listing with all metadata of the books in your library, in several formats, like XML, CSV, EPUB and MOBI. The catalog will contain all the books showing in the library view currently, so you can use the search features to limit the books to be catalogued. In addition, if you select multiple books using the mouse, only those books will be added to the catalog. If you generate the catalog in an e-book format such as EPUB or MOBI, the next time you connect your e-book reader, the catalog will be automatically sent to the device. For details on how catalogs work, see `here <http://www.mobileread.com/forums/showthread.php?p=755468#post755468>`_.
.. _view:
@ -156,7 +156,7 @@ A *catalog* is a collection of books. |app| can manage three different catalogs:
2. **Reader**: This is a collection of books stored in the main memory of your ebook reader. It will be available when you connect the reader to your computer.
3. **Card**: This is a collection of books stored on the storage card in your reader. Note that ${app} supports only a single storage card at a time.
3. **Card**: This is a collection of books stored on the storage card in your reader.
.. _search_sort:

11
todo
View File

@ -1,11 +0,0 @@
* Refactor web.fetch.simple to use per connection timeouts via the timeout kwarg for mechanize.open
* Testing framework
* Add a languages column to books. Best implementation is comma separated IANA codes
* Add a hash column to the formats table (used for stanza identifier)
* Fix blockquote handling in sphinx templates
* Fix ebook-viewer going to links