Merge from trunk

This commit is contained in:
Sengian 2010-10-18 20:39:47 +02:00
commit 033c3d9f30
46 changed files with 42222 additions and 19524 deletions

View File

@ -4,6 +4,111 @@
# for important features/bug fixes.
# Also, each release can have new and improved recipes.
- version: 0.7.24
date: 2010-10-17
new features:
- title: "Content server: New interface that allows browsing via categories, similar to the Tag Browser in the calibre interface."
description: >
"You can access the new interface by going to /browse. So if your calibre content server is available at http://192.168.1.2, use
http://192.168.1.2/browse. The new interface requires a fairly modern browser, so no Internet Explorer 6,7."
type: major
- title: "Support for the SNB e-book format, used by the Bambook e-book reader"
type: major
- title: "Driver for the Wifi Kobo"
- title: "Edit metadata dialog: If metadata is downloaded successfully, set focus to download cover button"
- title: "News download system: Allow recipes with optional subscriptions"
tickets: [7199]
- title: "Templates: Improve the smarten function"
- title: "Linux device mounting: Use udisks, if it is available, to mount devices, so that I no longer have to hear bug reports from users using distro packages that have crippled calibre-mount-helper. You can turn off udisks by setting the environment variable CALIBRE_DISABLE_UDISKS=1"
- title: "Implement Drag'n'drop to tags in user categories"
tickets: [7172]
- title: "Ebook viewer: Add command line option to start in full screen mode"
- title: "Set completion mode on search boxes to popup completion"
- title: "Update version of jQuery used in content server and viewer. Required a little hackery in the viewer, hopefully nothing broke"
bug fixes:
- title: "Linux device drivers: Ignore read only partition exported by the device"
- title: "E-book viewer: Fix scrolling down with mouse wheel not always reaching bottom in windows"
- title: "Smarten punctuation: Fix bug in handling of comments and <style> tags"
- title: "EPUB Input: Handle EPUB files with components encoded in an encoding other than UTF-8 correctly, though why anyone would do that is a mystery."
tickets: [7196]
- title: "OS X commandline tools: Decode non-ascii command line arguments correctly"
tickets: [6964]
- title: "MOBI Output: Fix bug that broke conversion of <svg> elements in the input document when the <svg> element was followed by non-whitespace text."
tickets: [7083]
- title: "CHM Input: Fix handling of relative file paths in <img> tags."
tickets: [7159]
- title: "EPUB Output: Fix incorrect format for xml:lang when specifying a sub language"
tickets: [7198]
- title: "EPUB Input: Make parsing of toc.ncx more robust."
tickets: [7170]
- title: "Content server: Fix searching with non-ascii characters on windows"
tickets: [5249]
- title: "Fix average rating calculation for rating datatype in Tag Browser incorrect"
- title: "Comic Input: Fix image borders becoming yellow on some windows installs"
- title: "Email sending: Fix sending of email with non ascii chars"
tickets: [7137]
- title: "SONY driver: Fix collections created from series not in order with manual metadata management, if all books in the series are not sent at once"
- title: "Content server: Apply the search restriction when generating category lists as well"
- title: "RTF Input: Fix regression in conversion of WMF images on linux at least, maybe on other platforms as wel"
- title: "Fix isbndb.com metadata downloading sometimes yield a title of Unknown"
tickets: [7114]
- title: "Fix edit metadata dialog causing the hour:minute:seconds of the date column being lost, even when date is not changed"
tickets: [7125]
new recipes:
- title: "Revista El Cultural"
author: "Jefferson Frantz"
- title: "Novaya Gazeta"
author: "muwa"
- title: "frazpc.pl"
author: "Tomasz Dlugosz"
- title: "Orsai and Financial Times UK"
author: "Darko Miletic"
- title: "Malayasian Mirror and Rolling Stones"
author: "Tony Stegall"
improved recipes:
- Globe and Mail
- Business Standard
- Miami Herald
- El Mercurio
- volkskrant.nl
- GoComics.com
- The New Yorker
- version: 0.7.23
date: 2010-10-08
@ -51,6 +156,7 @@
- title: "CHM input: handle another class of broken CHM files"
tickets: [7058]
- title: "Make calibre worker processes use the same temp directory as the calibre GUI"
new recipes:
- title: "Communications of the Association for Computing Machinery"

View File

@ -109,7 +109,7 @@ function toplevel_layout() {
var last = $(".toplevel li").last();
var title = $('.toplevel h3').first();
var bottom = last.position().top + last.height() - title.position().top;
$("#main").height(Math.max(200, bottom));
$("#main").height(Math.max(200, bottom+75));
}
function toplevel() {

View File

@ -51,6 +51,7 @@ div.navigation {
#listing td {
padding: 0.25em;
vertical-align: middle;
}
#listing td.thumbnail {
@ -70,6 +71,7 @@ div.navigation {
overflow: hidden;
text-align: center;
text-decoration: none;
vertical-align: middle;
}
#logo {

View File

@ -2,7 +2,7 @@ __license__ = 'GPL v3'
__copyright__ = '2008, Kovid Goyal kovid@kovidgoyal.net'
__docformat__ = 'restructuredtext en'
__appname__ = 'calibre'
__version__ = '0.7.23'
__version__ = '0.7.24'
__author__ = "Kovid Goyal <kovid@kovidgoyal.net>"
import re

View File

@ -120,6 +120,11 @@ class InputFormatPlugin(Plugin):
#: to make its output suitable for viewing
for_viewer = False
#: The encoding that this input plugin creates files in. A value of
#: None means that the encoding is undefined and must be
#: detected individually
output_encoding = 'utf-8'
#: Options shared by all Input format plugins. Do not override
#: in sub-classes. Use :attr:`options` instead. Every option must be an
#: instance of :class:`OptionRecommendation`.

View File

@ -36,8 +36,8 @@ class KOBO(USBMS):
PRODUCT_ID = [0x4161]
BCD = [0x0110]
VENDOR_NAME = 'KOBO_INC'
WINDOWS_MAIN_MEM = WINDOWS_CARD_A_MEM = '.KOBOEREADER'
VENDOR_NAME = ['KOBO_INC', 'KOBO']
WINDOWS_MAIN_MEM = WINDOWS_CARD_A_MEM = ['.KOBOEREADER', 'EREADER']
EBOOK_DIR_MAIN = ''
SUPPORTS_SUB_DIRS = True

View File

@ -838,7 +838,8 @@ OptionRecommendation(name='timestamp',
self.opts_to_mi(self.user_metadata)
if not hasattr(self.oeb, 'manifest'):
self.oeb = create_oebbook(self.log, self.oeb, self.opts,
self.input_plugin)
self.input_plugin,
encoding=self.input_plugin.output_encoding)
self.input_plugin.postprocess_book(self.oeb, self.opts, self.log)
self.opts.is_image_collection = self.input_plugin.is_image_collection
pr = CompositeProgressReporter(0.34, 0.67, self.ui_reporter)

View File

@ -543,6 +543,13 @@ class HTMLPreProcessor(object):
def smarten_punctuation(self, html):
from calibre.utils.smartypants import smartyPants
from calibre.ebooks.chardet import substitute_entites
from uuid import uuid4
start = 'calibre-smartypants-'+str(uuid4())
stop = 'calibre-smartypants-'+str(uuid4())
html = html.replace('<!--', start)
html = html.replace('-->', stop)
html = smartyPants(html)
html = html.replace(start, '<!--')
html = html.replace(stop, '-->')
return substitute_entites(html)

View File

@ -16,6 +16,7 @@ class EPUBInput(InputFormatPlugin):
author = 'Kovid Goyal'
description = 'Convert EPUB files (.epub) to HTML'
file_types = set(['epub'])
output_encoding = None
recommendations = set([('page_breaks_before', '/', OptionRecommendation.MED)])

View File

@ -514,7 +514,7 @@ class FileDialog(QObject):
if f and os.path.exists(f):
self.selected_files.append(f)
else:
opts = QFileDialog.ShowDirsOnly if mode == QFileDialog.DirectoryOnly else QFileDialog.Option()
opts = QFileDialog.ShowDirsOnly if mode == QFileDialog.Directory else QFileDialog.Option()
f = unicode(QFileDialog.getExistingDirectory(parent, title, initial_dir, opts))
if os.path.exists(f):
self.selected_files.append(f)
@ -534,7 +534,7 @@ class FileDialog(QObject):
def choose_dir(window, name, title, default_dir='~'):
fd = FileDialog(title=title, filters=[], add_all_files_filter=False,
parent=window, name=name, mode=QFileDialog.DirectoryOnly,
parent=window, name=name, mode=QFileDialog.Directory,
default_dir=default_dir)
dir = fd.get_files()
if dir:

View File

@ -3,11 +3,16 @@ __copyright__ = '2008, Kovid Goyal kovid@kovidgoyal.net'
__docformat__ = 'restructuredtext en'
__license__ = 'GPL v3'
import os
from PyQt4.Qt import QDialog, QVBoxLayout, QHBoxLayout, QTreeWidget, QLabel, \
QPushButton, QDialogButtonBox, QApplication, QTreeWidgetItem, \
QLineEdit
QLineEdit, Qt
from calibre.gui2.dialogs.confirm_delete import confirm
from calibre.library.check_library import CheckLibrary, CHECKS
from calibre.library.database2 import delete_file
from calibre import prints
class Item(QTreeWidgetItem):
pass
@ -24,23 +29,28 @@ class CheckLibraryDialog(QDialog):
self.setLayout(self._layout)
self.log = QTreeWidget(self)
self.log.itemChanged.connect(self.item_changed)
self._layout.addWidget(self.log)
self.check = QPushButton(_('Run the check'))
self.check = QPushButton(_('&Run the check'))
self.check.setDefault(False)
self.check.clicked.connect(self.run_the_check)
self.copy = QPushButton(_('Copy to clipboard'))
self.copy = QPushButton(_('Copy &to clipboard'))
self.copy.setDefault(False)
self.copy.clicked.connect(self.copy_to_clipboard)
self.ok = QPushButton('&Done')
self.ok.setDefault(True)
self.ok.clicked.connect(self.accept)
self.delete = QPushButton('Delete &marked')
self.delete.setDefault(False)
self.delete.clicked.connect(self.delete_marked)
self.cancel = QPushButton('&Cancel')
self.cancel.setDefault(False)
self.cancel.clicked.connect(self.reject)
self.bbox = QDialogButtonBox(self)
self.bbox.addButton(self.copy, QDialogButtonBox.ActionRole)
self.bbox.addButton(self.check, QDialogButtonBox.ActionRole)
self.bbox.addButton(self.delete, QDialogButtonBox.ActionRole)
self.bbox.addButton(self.cancel, QDialogButtonBox.RejectRole)
self.bbox.addButton(self.ok, QDialogButtonBox.AcceptRole)
@ -83,35 +93,66 @@ class CheckLibraryDialog(QDialog):
plaintext = []
def builder(tree, checker, check):
attr = check[0]
attr, h, checkable = check
list = getattr(checker, attr, None)
if list is None:
return
h = check[1]
tl = Item([h])
for problem in list:
it = Item()
if checkable:
it.setFlags(Qt.ItemIsEnabled | Qt.ItemIsUserCheckable)
it.setCheckState(1, False)
else:
it.setFlags(Qt.ItemIsEnabled)
it.setText(0, problem[0])
it.setText(1, problem[1])
p = ', '.join(problem[2])
it.setText(2, p)
tl.addChild(it)
plaintext.append(','.join([h, problem[0], problem[1], p]))
self.all_items.append(it)
plaintext.append(','.join([h, problem[0], problem[1]]))
tree.addTopLevelItem(tl)
t = self.log
t.clear()
t.setColumnCount(3);
t.setHeaderLabels([_('Name'), _('Path from library'), _('Additional Information')])
t.setColumnCount(2);
t.setHeaderLabels([_('Name'), _('Path from library')])
self.all_items = []
for check in CHECKS:
builder(t, checker, check)
t.setColumnWidth(0, 200)
t.setColumnWidth(1, 400)
self.delete.setEnabled(False)
self.text_results = '\n'.join(plaintext)
def item_changed(self, item, column):
for it in self.all_items:
if it.checkState(1):
self.delete.setEnabled(True)
return
def delete_marked(self):
if not confirm('<p>'+_('The marked files and folders will be '
'<b>permanently deleted</b>. Are you sure?')
+'</p>', 'check_library_editor_delete', self):
return
# Sort the paths in reverse length order so that we can be sure that
# if an item is in another item, the sub-item will be deleted first.
items = sorted(self.all_items,
key=lambda x: len(x.text(1)),
reverse=True)
for it in items:
if it.checkState(1):
try:
delete_file(os.path.join(self.db.library_path, unicode(it.text(1))))
except:
prints('failed to delete',
os.path.join(self.db.library_path,
unicode(it.text(1))))
self.run_the_check()
def copy_to_clipboard(self):
QApplication.clipboard().setText(self.text_results)

View File

@ -365,6 +365,8 @@ class Main(MainWindow, MainWindowMixin, DeviceMixin, # {{{
except:
olddb = None
db = LibraryDatabase2(newloc)
if self.content_server is not None:
self.content_server.set_database(db)
self.library_path = newloc
self.book_on_device(None, reset=True)
db.set_book_on_device_func(self.book_on_device)

View File

@ -353,6 +353,7 @@ class EbookViewer(MainWindow, Ui_EbookViewer):
self.pending_bookmark = bm
if spine_index < 0 or spine_index >= len(self.iterator.spine):
spine_index = 0
self.pending_bookmark = None
self.load_path(self.iterator.spine[spine_index])
def toc_clicked(self, index):

View File

@ -14,14 +14,14 @@ from calibre.ebooks import BOOK_EXTENSIONS
EBOOK_EXTENSIONS = frozenset(BOOK_EXTENSIONS)
NORMALS = frozenset(['metadata.opf', 'cover.jpg'])
CHECKS = [('invalid_titles', _('Invalid titles')),
('extra_titles', _('Extra titles')),
('invalid_authors', _('Invalid authors')),
('extra_authors', _('Extra authors')),
('missing_formats', _('Missing book formats')),
('extra_formats', _('Extra book formats')),
('extra_files', _('Unknown files in books')),
('failed_folders', _('Folders raising exception'))
CHECKS = [('invalid_titles', _('Invalid titles'), True),
('extra_titles', _('Extra titles'), True),
('invalid_authors', _('Invalid authors'), True),
('extra_authors', _('Extra authors'), True),
('missing_formats', _('Missing book formats'), False),
('extra_formats', _('Extra book formats'), True),
('extra_files', _('Unknown files in books'), True),
('failed_folders', _('Folders raising exception'), False)
]
@ -41,7 +41,6 @@ class CheckLibrary(object):
self.all_lc_dbpaths = frozenset([f.lower() for f in self.all_dbpaths])
self.db_id_regexp = re.compile(r'^.* \((\d+)\)$')
self.bad_ext_pat = re.compile(r'[^a-z0-9]+')
self.dirs = []
self.book_dirs = []
@ -78,7 +77,7 @@ class CheckLibrary(object):
auth_path = os.path.join(lib, auth_dir)
# First check: author must be a directory
if not os.path.isdir(auth_path):
self.invalid_authors.append((auth_dir, auth_dir, []))
self.invalid_authors.append((auth_dir, auth_dir))
continue
self.potential_authors[auth_dir] = {}
@ -93,7 +92,7 @@ class CheckLibrary(object):
m = self.db_id_regexp.search(title_dir)
# Second check: title must have an ID and must be a directory
if m is None or not os.path.isdir(title_path):
self.invalid_titles.append((auth_dir, db_path, [title_dir]))
self.invalid_titles.append((auth_dir, db_path))
continue
id = m.group(1)
@ -101,12 +100,12 @@ class CheckLibrary(object):
if self.is_case_sensitive:
if int(id) not in self.all_ids or \
db_path not in self.all_dbpaths:
self.extra_titles.append((title_dir, db_path, []))
self.extra_titles.append((title_dir, db_path))
continue
else:
if int(id) not in self.all_ids or \
db_path.lower() not in self.all_lc_dbpaths:
self.extra_titles.append((title_dir, db_path, []))
self.extra_titles.append((title_dir, db_path))
continue
# Record the book to check its formats
@ -115,7 +114,7 @@ class CheckLibrary(object):
# Fourth check: author directories that contain no titles
if not found_titles:
self.extra_authors.append((auth_dir, auth_dir, []))
self.extra_authors.append((auth_dir, auth_dir))
for x in self.book_dirs:
try:
@ -132,9 +131,7 @@ class CheckLibrary(object):
ext = ext[1:].lower()
if ext in EBOOK_EXTENSIONS:
return True
if self.bad_ext_pat.search(ext) is not None:
return False
return True
def process_book(self, lib, book_info):
(db_path, title_dir, book_id) = book_info
@ -148,18 +145,18 @@ class CheckLibrary(object):
if self.is_case_sensitive:
unknowns = frozenset(filenames-formats-NORMALS)
# Check: any books that aren't formats or normally there?
if unknowns:
self.extra_files.append((title_dir, db_path, unknowns))
for u in unknowns:
self.extra_files.append((title_dir, os.path.join(db_path, u)))
# Check: any book formats that should be there?
missing = book_formats - formats
if missing:
self.missing_formats.append((title_dir, db_path, missing))
for m in missing:
self.missing_formats.append((title_dir, os.path.join(db_path, m)))
# Check: any book formats that shouldn't be there?
extra = formats - book_formats - NORMALS
if extra:
self.extra_formats.append((title_dir, db_path, extra))
for e in extra:
self.extra_formats.append((title_dir, os.path.join(db_path, e)))
else:
def lc_map(fnames, fset):
m = {}
@ -171,19 +168,16 @@ class CheckLibrary(object):
formats_lc = frozenset([f.lower() for f in formats])
unknowns = frozenset(filenames_lc-formats_lc-NORMALS)
# Check: any books that aren't formats or normally there?
if unknowns:
self.extra_files.append((title_dir, db_path,
lc_map(filenames, unknowns)))
for f in lc_map(filenames, unknowns):
self.extra_files.append((title_dir, os.path.join(db_path, f)))
book_formats_lc = frozenset([f.lower() for f in book_formats])
# Check: any book formats that should be there?
missing = book_formats_lc - formats_lc
if missing:
self.missing_formats.append((title_dir, db_path,
lc_map(book_formats, missing)))
for m in lc_map(book_formats, missing):
self.missing_formats.append((title_dir, os.path.join(db_path, m)))
# Check: any book formats that shouldn't be there?
extra = formats_lc - book_formats_lc - NORMALS
if extra:
self.extra_formats.append((title_dir, db_path,
lc_map(formats, extra)))
for e in lc_map(formats, extra):
self.extra_formats.append((title_dir, os.path.join(db_path, e)))

View File

@ -943,11 +943,11 @@ def command_check_library(args, dbpath):
return
if opts.csv:
for i in list:
print check[1] + ',' + i[0] + ',' + i[1] + ',' + '|'.join(i[2])
print check[1] + ',' + i[0] + ',' + i[1]
else:
print check[1]
for i in list:
print ' %-30.30s - %-30.30s - %s'%(i[0], i[1], ', '.join(i[2]))
print ' %-40.40s - %-40.40s'%(i[0], i[1])
db = LibraryDatabase2(dbpath)
checker = CheckLibrary(dbpath, db)

View File

@ -10,6 +10,7 @@ import logging
from logging.handlers import RotatingFileHandler
import cherrypy
from cherrypy.process.plugins import SimplePlugin
from calibre.constants import __appname__, __version__
from calibre.utils.date import fromtimestamp
@ -54,16 +55,43 @@ class DispatchController(object): # {{{
# }}}
class BonJour(SimplePlugin):
def __init__(self, engine, port=8080):
SimplePlugin.__init__(self, engine)
self.port = port
def start(self):
try:
publish_zeroconf('Books in calibre', '_stanza._tcp',
self.port, {'path':'/stanza'})
except:
import traceback
cherrypy.log.error('Failed to start BonJour:')
cherrypy.log.error(traceback.format_exc())
start.priority = 90
def stop(self):
try:
stop_zeroconf()
except:
import traceback
cherrypy.log.error('Failed to stop BonJour:')
cherrypy.log.error(traceback.format_exc())
stop.priority = 10
cherrypy.engine.bonjour = BonJour(cherrypy.engine)
class LibraryServer(ContentServer, MobileServer, XMLServer, OPDSServer, Cache,
BrowseServer):
server_name = __appname__ + '/' + __version__
def __init__(self, db, opts, embedded=False, show_tracebacks=True):
self.db = db
for item in self.db:
item
break
self.opts = opts
self.embedded = embedded
self.state_callback = None
@ -72,6 +100,13 @@ class LibraryServer(ContentServer, MobileServer, XMLServer, OPDSServer, Cache,
path = P('content_server')
self.build_time = fromtimestamp(os.stat(path).st_mtime)
self.default_cover = open(P('content_server/default_cover.jpg'), 'rb').read()
cherrypy.engine.bonjour.port = opts.port
Cache.__init__(self)
self.set_database(db)
cherrypy.config.update({
'log.screen' : opts.develop,
'engine.autoreload_on' : opts.develop,
@ -97,18 +132,27 @@ class LibraryServer(ContentServer, MobileServer, XMLServer, OPDSServer, Cache,
'tools.digest_auth.users' : {opts.username.strip():opts.password.strip()},
}
sr = getattr(opts, 'restriction', None)
sr = db.prefs.get('cs_restriction', '') if sr is None else sr
self.set_search_restriction(sr)
self.is_running = False
self.exception = None
self.setup_loggers()
cherrypy.engine.bonjour.subscribe()
def set_database(self, db):
self.db = db
sr = getattr(self.opts, 'restriction', None)
sr = db.prefs.get('cs_restriction', '') if sr is None else sr
self.set_search_restriction(sr)
def graceful(self):
cherrypy.engine.graceful()
def set_search_restriction(self, restriction):
if restriction:
self.search_restriction = 'search:"%s"'%restriction
else:
self.search_restriction = ''
self.reset_caches()
def setup_loggers(self):
access_file = log_access_file
@ -140,7 +184,6 @@ class LibraryServer(ContentServer, MobileServer, XMLServer, OPDSServer, Cache,
root_conf['request.dispatch'] = d.dispatcher
self.config['/'] = root_conf
self.setup_loggers()
cherrypy.tree.mount(root=None, config=self.config)
try:
try:
@ -154,24 +197,14 @@ class LibraryServer(ContentServer, MobileServer, XMLServer, OPDSServer, Cache,
cherrypy.engine.start()
self.is_running = True
try:
publish_zeroconf('Books in calibre', '_stanza._tcp',
self.opts.port, {'path':'/stanza'})
except:
import traceback
cherrypy.log.error('Failed to start BonJour:')
cherrypy.log.error(traceback.format_exc())
#if hasattr(cherrypy.engine, 'signal_handler'):
# cherrypy.engine.signal_handler.subscribe()
cherrypy.engine.block()
except Exception, e:
self.exception = e
finally:
self.is_running = False
try:
stop_zeroconf()
except:
import traceback
cherrypy.log.error('Failed to stop BonJour:')
cherrypy.log.error(traceback.format_exc())
try:
if callable(self.state_callback):
self.state_callback(self.is_running)

View File

@ -7,6 +7,7 @@ __docformat__ = 'restructuredtext en'
import operator, os, json
from binascii import hexlify, unhexlify
from urllib import quote
import cherrypy
@ -136,7 +137,7 @@ def get_category_items(category, items, db, datatype): # {{{
q = i.category
if not q:
q = category
href = '/browse/matches/%s/%s'%(q, id_)
href = '/browse/matches/%s/%s'%(quote(q), quote(id_))
return templ.format(xml(name), rating,
xml(desc), xml(href), rstring)
@ -329,7 +330,7 @@ class BrowseServer(object):
cats = [('<li title="{2} {0}"><img src="{src}" alt="{0}" />'
'<span class="label">{0}</span>'
'<span class="url">/browse/category/{1}</span></li>')
.format(xml(x, True), xml(y), xml(_('Browse books by')),
.format(xml(x, True), xml(quote(y)), xml(_('Browse books by')),
src='/browse/icon/'+z)
for x, y, z in cats]

View File

@ -10,7 +10,10 @@ from calibre.utils.ordered_dict import OrderedDict
class Cache(object):
def add_routes(self, c):
def __init__(self):
self.reset_caches()
def reset_caches(self):
self._category_cache = OrderedDict()
self._search_cache = OrderedDict()

View File

@ -5,7 +5,7 @@ __license__ = 'GPL v3'
__copyright__ = '2010, Kovid Goyal <kovid@kovidgoyal.net>'
__docformat__ = 'restructuredtext en'
import os, sys
import sys
from threading import Thread
from calibre.library.server import server_config as config
@ -38,50 +38,18 @@ def option_parser():
' in the GUI'))
return parser
def daemonize(stdin='/dev/null', stdout='/dev/null', stderr='/dev/null'):
try:
pid = os.fork()
if pid > 0:
# exit first parent
sys.exit(0)
except OSError, e:
print >>sys.stderr, "fork #1 failed: %d (%s)" % (e.errno, e.strerror)
sys.exit(1)
# decouple from parent environment
os.chdir("/")
os.setsid()
os.umask(0)
# do second fork
try:
pid = os.fork()
if pid > 0:
# exit from second parent
sys.exit(0)
except OSError, e:
print >>sys.stderr, "fork #2 failed: %d (%s)" % (e.errno, e.strerror)
sys.exit(1)
# Redirect standard file descriptors.
si = file(stdin, 'r')
so = file(stdout, 'a+')
se = file(stderr, 'a+', 0)
os.dup2(si.fileno(), sys.stdin.fileno())
os.dup2(so.fileno(), sys.stdout.fileno())
os.dup2(se.fileno(), sys.stderr.fileno())
def main(args=sys.argv):
from calibre.library.database2 import LibraryDatabase2
parser = option_parser()
opts, args = parser.parse_args(args)
if opts.daemonize and not iswindows:
daemonize()
from cherrypy.process.plugins import Daemonizer
d = Daemonizer(cherrypy.engine)
d.subscribe()
if opts.pidfile is not None:
with open(opts.pidfile, 'wb') as f:
f.write(str(os.getpid()))
from cherrypy.process.plugins import PIDFile
PIDFile(cherrypy.engine, opts.pidfile).subscribe()
cherrypy.log.screen = True
from calibre.utils.config import prefs
if opts.with_library is None:

View File

@ -16,7 +16,7 @@ __builtin__.__dict__['_'] = lambda s: s
# immediately translated to the environment language
__builtin__.__dict__['__'] = lambda s: s
from calibre.constants import iswindows, preferred_encoding, plugins
from calibre.constants import iswindows, preferred_encoding, plugins, isosx
_run_once = False
winutil = winutilerror = None
@ -35,9 +35,17 @@ if not _run_once:
################################################################################
# Convert command line arguments to unicode
enc = preferred_encoding
if isosx:
# Newer versions of OS X seem to use UTF-8
try:
[x.decode('utf-8') for x in sys.argv[1:]]
enc = 'utf-8'
except:
pass
for i in range(1, len(sys.argv)):
if not isinstance(sys.argv[i], unicode):
sys.argv[i] = sys.argv[i].decode(preferred_encoding, 'replace')
sys.argv[i] = sys.argv[i].decode(enc, 'replace')
################################################################################
# Setup resources

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

11559
src/calibre/translations/ur.po Normal file

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

View File

@ -58,11 +58,12 @@ def publish(desc, type, port, properties=None, add_hostname=True):
'''
port = int(port)
server = start_server()
if add_hostname:
try:
hostname = socket.gethostname().partition('.')[0]
except:
hostname = 'Unknown'
if add_hostname:
desc += ' (on %s)'%hostname
local_ip = get_external_ip()
type = type+'.local.'