Merge upstream changes

This commit is contained in:
Marshall T. Vandegrift 2008-08-18 12:56:26 -04:00
commit f09065f127
17 changed files with 214 additions and 65 deletions

View File

@ -1,30 +0,0 @@
PYTHON = python
all : gui2 translations resources
clean :
cd src/calibre/gui2 && ${PYTHON} make.py clean
gui2 :
cd src/calibre/gui2 && ${PYTHON} make.py
test : gui2
cd src/calibre/gui2 && ${PYTHON} make.py test
translations :
cd src/calibre/translations && ${PYTHON} __init__.py
resources:
${PYTHON} resources.py
manual:
make -C src/calibre/manual clean html
pot :
cd src/calibre/translations && ${PYTHON} __init__.py pot
egg :
${PYTHON} setup.py bdist_egg --exclude-source-files
linux_binary:
${PYTHON} -c "import upload; upload._build_linux()"

View File

@ -37,7 +37,9 @@ def main(args=sys.argv):
if not translations_found: if not translations_found:
print 'WARNING: Could not find Qt transations' print 'WARNING: Could not find Qt transations'
open('src'+os.sep+__appname__+os.sep+'/resources.py', 'wb').write(data) dest = os.path.abspath(os.path.join('src', __appname__, 'resources.py'))
print 'Writing resources to', dest
open(dest, 'wb').write(data)
return 0 return 0
if __name__ == '__main__': if __name__ == '__main__':

View File

@ -47,9 +47,68 @@ main_functions = {
if __name__ == '__main__': if __name__ == '__main__':
from setuptools import setup, find_packages, Extension from setuptools import setup, find_packages, Extension
from distutils.command.build import build as _build
from distutils.core import Command
from pyqtdistutils import PyQtExtension, build_ext from pyqtdistutils import PyQtExtension, build_ext
import subprocess, glob import subprocess, glob
class pot(Command):
user_options = []
def initialize_options(self): pass
def finalize_options(self): pass
def run(self):
from calibre.translations import create_pot
create_pot()
def build_manual():
cwd = os.path.abspath(os.getcwd())
os.chdir(os.path.join('src', 'calibre', 'manual'))
try:
for d in ('.build', 'cli'):
if os.path.exists(d):
shutil.rmtree(d)
os.makedirs(d)
if not os.path.exists('.build'+os.sep+'html'):
os.makedirs('.build'+os.sep+'html')
subprocess.check_call(['sphinx-build', '-b', 'custom', '-d',
'.build/doctrees', '.', '.build/html'])
finally:
os.chdir(cwd)
class manual(Command):
user_options = []
def initialize_options(self): pass
def finalize_options(self): pass
def run(self):
build_manual()
class build(_build):
def run(self):
# Build resources
resources = __import__('resources')
resources.main([sys.executable, 'resources.py'])
from calibre.translations import main as translations
cwd = os.path.abspath(os.getcwd())
# Build translations
try:
os.chdir(os.path.join('src', 'calibre', 'translations'))
translations([sys.executable])
finally:
os.chdir(cwd)
# Build GUI
from calibre.gui2.make import main as gui2
try:
os.chdir(os.path.join('src', 'calibre', 'gui2'))
print 'Compiling GUI resources...'
gui2([sys.executable])
finally:
os.chdir(cwd)
_build.run(self)
entry_points['console_scripts'].append('calibre_postinstall = calibre.linux:post_install') entry_points['console_scripts'].append('calibre_postinstall = calibre.linux:post_install')
ext_modules = [ ext_modules = [
Extension('calibre.plugins.lzx', Extension('calibre.plugins.lzx',
@ -125,7 +184,8 @@ if __name__ == '__main__':
'Topic :: Software Development :: Libraries :: Python Modules', 'Topic :: Software Development :: Libraries :: Python Modules',
'Topic :: System :: Hardware :: Hardware Drivers' 'Topic :: System :: Hardware :: Hardware Drivers'
], ],
cmdclass = {'build_ext': build_ext}, cmdclass = {'build_ext': build_ext, 'build' : build, 'pot' : pot,
'manual' : manual},
) )
if 'develop' in ' '.join(sys.argv) and islinux: if 'develop' in ' '.join(sys.argv) and islinux:

View File

@ -182,7 +182,8 @@ class PageProcessor(list):
MagickQuantizeImage(wand, self.opts.colors, RGBColorspace, 0, 1, 0) MagickQuantizeImage(wand, self.opts.colors, RGBColorspace, 0, 1, 0)
dest = '%d_%d.png'%(self.num, i) dest = '%d_%d.png'%(self.num, i)
dest = os.path.join(self.dest, dest) dest = os.path.join(self.dest, dest)
MagickWriteImage(wand, dest) MagickWriteImage(wand, dest+'8')
os.rename(dest+'8', dest)
self.append(dest) self.append(dest)
DestroyPixelWand(pw) DestroyPixelWand(pw)

View File

@ -110,6 +110,12 @@ class LrsParser(object):
tb.append(self.process_paragraph(item)) tb.append(self.process_paragraph(item))
elif item.name == 'cr': elif item.name == 'cr':
tb.append(CR()) tb.append(CR())
elif item.name == 'charbutton': # BookDesigner does this
p = Paragraph()
tb.append(p)
elem = self.text_tag_to_element(item)
self.process_text_element(item, elem)
p.append(elem)
def fourth_pass(self): def fourth_pass(self):
for tag in self.soup.findAll('page'): for tag in self.soup.findAll('page'):
@ -130,7 +136,6 @@ class LrsParser(object):
for tag in self.soup.findAll('textblock'): for tag in self.soup.findAll('textblock'):
self.process_text_block(tag) self.process_text_block(tag)
toc = self.soup.find('toc') toc = self.soup.find('toc')
if toc: if toc:
for tag in toc.findAll('toclabel'): for tag in toc.findAll('toclabel'):

View File

@ -41,6 +41,10 @@ def get_metadata(stream):
mi.author_sort = lastname+'; '+firstname mi.author_sort = lastname+'; '+firstname
if series: if series:
mi.series = series.get('name', None) mi.series = series.get('name', None)
try:
mi.series_index = int(series.get('number', None))
except (TypeError, ValueError):
pass
if cdata: if cdata:
mi.cover_data = cdata mi.cover_data = cdata
return mi return mi

View File

@ -225,7 +225,7 @@ class Main(MainWindow, Ui_MainWindow):
pd.show() pd.show()
db.migrate_old(self.olddb, pd) db.migrate_old(self.olddb, pd)
self.olddb = None self.olddb = None
Settings().set('library path', self.library_path) prefs['library_path'] = self.library_path
self.library_view.sortByColumn(3, Qt.DescendingOrder) self.library_view.sortByColumn(3, Qt.DescendingOrder)
if not self.library_view.restore_column_widths(): if not self.library_view.restore_column_widths():
self.library_view.resizeColumnsToContents() self.library_view.resizeColumnsToContents()
@ -1106,7 +1106,7 @@ class Main(MainWindow, Ui_MainWindow):
_('<p>An invalid database already exists at %s, delete it before trying to move the existing database.<br>Error: %s')%(newloc, str(err))) _('<p>An invalid database already exists at %s, delete it before trying to move the existing database.<br>Error: %s')%(newloc, str(err)))
d.exec_() d.exec_()
self.library_path = self.library_view.model().db.library_path self.library_path = self.library_view.model().db.library_path
prefs['library path'] = self.library_path prefs['library_path'] = self.library_path
except Exception, err: except Exception, err:
traceback.print_exc() traceback.print_exc()
d = error_dialog(self, _('Could not move database'), unicode(err)) d = error_dialog(self, _('Could not move database'), unicode(err))
@ -1204,31 +1204,33 @@ class Main(MainWindow, Ui_MainWindow):
def initialize_database(self): def initialize_database(self):
self.library_path = prefs['library path'] self.library_path = prefs['library_path']
self.olddb = None self.olddb = None
if self.library_path is None: # Need to migrate to new database layout if self.library_path is None: # Need to migrate to new database layout
self.database_path = prefs['database_path'] self.database_path = prefs['database_path']
if not os.access(os.path.dirname(self.database_path), os.W_OK): if not os.access(os.path.dirname(self.database_path), os.W_OK):
error_dialog(self, _('Database does not exist'), _('The directory in which the database should be: %s no longer exists. Please choose a new database location.')%self.database_path).exec_() error_dialog(self, _('Database does not exist'),
self.database_path = choose_dir(self, 'database path dialog', 'Choose new location for database') _('The directory in which the database should be: %s no longer exists. Please choose a new database location.')%self.database_path).exec_()
self.database_path = choose_dir(self, 'database path dialog',
_('Choose new location for database'))
if not self.database_path: if not self.database_path:
self.database_path = os.path.expanduser('~').decode(sys.getfilesystemencoding()) self.database_path = os.path.expanduser('~').decode(sys.getfilesystemencoding())
if not os.path.exists(self.database_path): if not os.path.exists(self.database_path):
os.makedirs(self.database_path) os.makedirs(self.database_path)
self.database_path = os.path.join(self.database_path, 'library1.db') self.database_path = os.path.join(self.database_path, 'library1.db')
settings.set('database path', self.database_path) prefs['database_path'] = self.database_path
home = os.path.dirname(self.database_path) home = os.path.dirname(self.database_path)
if not os.path.exists(home): if not os.path.exists(home):
home = os.getcwd() home = os.getcwd()
from PyQt4.QtGui import QFileDialog from PyQt4.QtGui import QFileDialog
dir = qstring_to_unicode(QFileDialog.getExistingDirectory(self, _('Choose a location for your ebook library.'), home)) dir = unicode(QFileDialog.getExistingDirectory(self,
_('Choose a location for your ebook library.'), home))
if not dir: if not dir:
dir = os.path.dirname(self.database_path) dir = os.path.dirname(self.database_path)
self.library_path = os.path.abspath(dir) self.library_path = os.path.abspath(dir)
self.olddb = LibraryDatabase(self.database_path) self.olddb = LibraryDatabase(self.database_path)
def read_settings(self): def read_settings(self):
self.initialize_database() self.initialize_database()
geometry = config['main_window_geometry'] geometry = config['main_window_geometry']

View File

@ -21,7 +21,7 @@ help:
@echo " linkcheck to check all external links for integrity" @echo " linkcheck to check all external links for integrity"
clean: clean:
-rm -rf .build/* cli rm -rf .build/* cli
html: html:
mkdir -p .build/html .build/doctrees mkdir -p .build/html .build/doctrees

View File

@ -451,6 +451,8 @@ def _prefs():
help=_('Access key for isbndb.com')) help=_('Access key for isbndb.com'))
c.add_opt('network_timeout', default=5, c.add_opt('network_timeout', default=5,
help=_('Default timeout for network operations (seconds)')) help=_('Default timeout for network operations (seconds)'))
c.add_opt('library_path', default=None,
help=_('Path to directory in which your library of books is stored'))
c.add_opt('migrated', default=False, help='For Internal use. Don\'t modify.') c.add_opt('migrated', default=False, help='For Internal use. Don\'t modify.')
return c return c

View File

@ -648,7 +648,7 @@ class NamespaceFlattener(object):
elif kind is END_NS: elif kind is END_NS:
if data is '': if data is '':
default.pop() default.pop()
if data in prefixes: if data in prefixes and prefixes.get(data):
uris = prefixes.get(data) uris = prefixes.get(data)
uri = uris.pop() uri = uris.pop()
if not uris: if not uris:

View File

@ -98,6 +98,10 @@ class BasicNewsRecipe(object, LoggingInterface):
#: embedded content. #: embedded content.
use_embedded_content = None use_embedded_content = None
#: Set to True and implement :method:`get_obfuscated_article` to handle
#: websites that try to make it difficult to scrape content.
articles_are_obfuscated = False
#: Specify any extra :term:`CSS` that should be addded to downloaded :term:`HTML` files #: Specify any extra :term:`CSS` that should be addded to downloaded :term:`HTML` files
#: It will be inserted into `<style>` tags, just before the closing #: It will be inserted into `<style>` tags, just before the closing
#: `</head>` tag thereby overrinding all :term:`CSS` except that which is #: `</head>` tag thereby overrinding all :term:`CSS` except that which is
@ -360,12 +364,25 @@ class BasicNewsRecipe(object, LoggingInterface):
''' '''
raise NotImplementedError raise NotImplementedError
def get_obfuscated_article(self, url, logger):
'''
If you set :member:`articles_are_obfuscated` this method is called with
every article URL. It should return the path to a file on the filesystem
that contains the article HTML. That file is processed by the recursive
HTML fetching engine, so it can contain links to pages/images on the web.
This method is typically useful for sites that try to make it difficult to
access article content automatically. See for example the
:module:`calibre.web.recipes.iht` recipe.
'''
raise NotImplementedError
def __init__(self, options, parser, progress_reporter): def __init__(self, options, parser, progress_reporter):
''' '''
Initialize the recipe. Initialize the recipe.
@param options: Parsed commandline options :param options: Parsed commandline options
@param parser: Command line option parser. Used to intelligently merge options. :param parser: Command line option parser. Used to intelligently merge options.
@param progress_reporter: A Callable that takes two arguments: progress (a number between 0 and 1) and a string message. The message should be optional. :param progress_reporter: A Callable that takes two arguments: progress (a number between 0 and 1) and a string message. The message should be optional.
''' '''
LoggingInterface.__init__(self, logging.getLogger('feeds2disk')) LoggingInterface.__init__(self, logging.getLogger('feeds2disk'))
if not isinstance(self.title, unicode): if not isinstance(self.title, unicode):
@ -565,6 +582,10 @@ class BasicNewsRecipe(object, LoggingInterface):
def fetch_article(self, url, dir, logger, f, a, num_of_feeds): def fetch_article(self, url, dir, logger, f, a, num_of_feeds):
return self._fetch_article(url, dir, logger, f, a, num_of_feeds) return self._fetch_article(url, dir, logger, f, a, num_of_feeds)
def fetch_obfuscated_article(self, url, dir, logger, f, a, num_of_feeds):
path = os.path.abspath(self.get_obfuscated_article(url, logger))
url = ('file:'+path) if iswindows else ('file://'+path)
return self._fetch_article(url, dir, logger, f, a, num_of_feeds)
def fetch_embedded_article(self, article, dir, logger, f, a, num_of_feeds): def fetch_embedded_article(self, article, dir, logger, f, a, num_of_feeds):
pt = PersistentTemporaryFile('_feeds2disk.html') pt = PersistentTemporaryFile('_feeds2disk.html')
@ -620,7 +641,8 @@ class BasicNewsRecipe(object, LoggingInterface):
continue continue
func, arg = (self.fetch_embedded_article, article) if self.use_embedded_content else \ func, arg = (self.fetch_embedded_article, article) if self.use_embedded_content else \
(self.fetch_article, url) ((self.fetch_obfuscated_article if self.articles_are_obfuscated \
else self.fetch_article), url)
req = WorkRequest(func, (arg, art_dir, logger, f, a, len(feed)), req = WorkRequest(func, (arg, art_dir, logger, f, a, len(feed)),
{}, (f, a), self.article_downloaded, {}, (f, a), self.article_downloaded,
self.error_in_article_download) self.error_in_article_download)

View File

@ -8,7 +8,7 @@ recipes = [
'newsweek', 'atlantic', 'economist', 'portfolio', 'newsweek', 'atlantic', 'economist', 'portfolio',
'nytimes', 'usatoday', 'outlook_india', 'bbc', 'greader', 'wsj', 'nytimes', 'usatoday', 'outlook_india', 'bbc', 'greader', 'wsj',
'wired', 'globe_and_mail', 'smh', 'espn', 'business_week', 'wired', 'globe_and_mail', 'smh', 'espn', 'business_week',
'ars_technica', 'upi', 'new_yorker', 'ars_technica', 'upi', 'new_yorker', 'irish_times', 'iht',
] ]
import re, imp, inspect, time import re, imp, inspect, time

View File

@ -0,0 +1,51 @@
__license__ = 'GPL v3'
__copyright__ = '2008, Derry FitzGerald'
'''
iht.com
'''
from calibre.web.feeds.news import BasicNewsRecipe
from calibre.ptempfile import PersistentTemporaryFile
class InternationalHeraldTribune(BasicNewsRecipe):
title = u'The International Herald Tribune'
__author__ = 'Derry FitzGerald'
oldest_article = 1
max_articles_per_feed = 10
no_stylesheets = True
remove_tags = [dict(name='div', attrs={'class':'footer'})]
extra_css = '.headline {font-size: x-large;} \n .fact { padding-top: 10pt }'
feeds = [
(u'Frontpage', u'http://www.iht.com/rss/frontpage.xml'),
(u'Business', u'http://www.iht.com/rss/business.xml'),
(u'Americas', u'http://www.iht.com/rss/america.xml'),
(u'Europe', u'http://www.iht.com/rss/europe.xml'),
(u'Asia', u'http://www.iht.com/rss/asia.xml'),
(u'Africa and Middle East', u'http://www.iht.com/rss/africa.xml'),
(u'Opinion', u'http://www.iht.com/rss/opinion.xml'),
(u'Technology', u'http://www.iht.com/rss/technology.xml'),
(u'Health and Science', u'http://www.iht.com/rss/healthscience.xml'),
(u'Sports', u'http://www.iht.com/rss/sports.xml'),
(u'Culture', u'http://www.iht.com/rss/arts.xml'),
(u'Style and Design', u'http://www.iht.com/rss/style.xml'),
(u'Travel', u'http://www.iht.com/rss/travel.xml'),
(u'At Home Abroad', u'http://www.iht.com/rss/athome.xml'),
(u'Your Money', u'http://www.iht.com/rss/yourmoney.xml'),
(u'Properties', u'http://www.iht.com/rss/properties.xml')
]
temp_files = []
articles_are_obfuscated = True
def get_obfuscated_article(self, url, logger):
br = self.get_browser()
br.open(url)
br.select_form(name='printFriendly')
res = br.submit()
html = res.read()
self.temp_files.append(PersistentTemporaryFile('_iht.html'))
self.temp_files[-1].write(html)
self.temp_files[-1].close()
return self.temp_files[-1].name

View File

@ -0,0 +1,36 @@
__license__ = 'GPL v3'
__copyright__ = '2008, Derry FitzGerald'
'''
irishtimes.com
'''
from calibre.web.feeds.news import BasicNewsRecipe
class IrishTimes(BasicNewsRecipe):
title = u'The Irish Times'
__author__ = 'Derry FitzGerald'
no_stylesheets = True
remove_tags = [dict(name='div', attrs={'class':'footer'})]
extra_css = '.headline {font-size: x-large;} \n .fact { padding-top: 10pt }'
feeds = [
('Frontpage', 'http://www.irishtimes.com/feeds/rss/newspaper/index.rss'),
('Ireland', 'http://www.irishtimes.com/feeds/rss/newspaper/ireland.rss'),
('World', 'http://www.irishtimes.com/feeds/rss/newspaper/world.rss'),
('Finance', 'http://www.irishtimes.com/feeds/rss/newspaper/finance.rss'),
('Features', 'http://www.irishtimes.com/feeds/rss/newspaper/features.rss'),
('Sport', 'http://www.irishtimes.com/feeds/rss/newspaper/sport.rss'),
('Opinion', 'http://www.irishtimes.com/feeds/rss/newspaper/opinion.rss'),
('Letters', 'http://www.irishtimes.com/feeds/rss/newspaper/letters.rss'),
('Health', 'http://www.irishtimes.com/feeds/rss/newspaper/health.rss'),
('Education and Parenting', 'http://www.irishtimes.com/feeds/rss/newspaper/education.rss'),
('Science Today', 'http://www.irishtimes.com/feeds/rss/newspaper/sciencetoday.rss'),
('The Ticket', 'http://www.irishtimes.com/feeds/rss/newspaper/theticket.rss'),
('Weekend', 'http://www.irishtimes.com/feeds/rss/newspaper/weekend.rss'),
('News Features', 'http://www.irishtimes.com/feeds/rss/newspaper/newsfeatures.rss'),
('Magazine', 'http://www.irishtimes.com/feeds/rss/newspaper/magazine.rss'),
]
def print_version(self, url):
return url.replace('.html', '_pf.html')

View File

@ -201,14 +201,8 @@ def upload_docs():
check_call('''scp docs/pdf/api.pdf divok:%s/'''%(DOCS,)) check_call('''scp docs/pdf/api.pdf divok:%s/'''%(DOCS,))
def upload_user_manual(): def upload_user_manual():
cwd = os.getcwdu() check_call('python setup.py manual')
os.chdir('src/calibre/manual')
try:
check_call('make clean html')
check_call('ssh divok rm -rf %s/\\*'%USER_MANUAL)
check_call('scp -r .build/html/* divok:%s'%USER_MANUAL) check_call('scp -r .build/html/* divok:%s'%USER_MANUAL)
finally:
os.chdir(cwd)
def build_src_tarball(): def build_src_tarball():
check_call('bzr export dist/calibre-%s.tar.bz2'%__version__) check_call('bzr export dist/calibre-%s.tar.bz2'%__version__)