mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-07-09 03:04:10 -04:00
Merge from trunk
This commit is contained in:
commit
be78420241
@ -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() {
|
||||
|
@ -53,6 +53,7 @@ div.navigation {
|
||||
}
|
||||
#listing td {
|
||||
padding: 0.25em;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
#listing td.thumbnail {
|
||||
@ -73,6 +74,7 @@ div.navigation {
|
||||
overflow: hidden;
|
||||
text-align: center;
|
||||
text-decoration: none;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
#logo {
|
||||
|
@ -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:
|
||||
|
@ -12,6 +12,7 @@ from PyQt4.Qt import QDialog, QVBoxLayout, QHBoxLayout, QTreeWidget, QLabel, \
|
||||
from calibre.gui2.dialogs.confirm_delete import confirm
|
||||
from calibre.library.check_library import CheckLibrary, CHECKS
|
||||
from calibre.library.database2 import delete_file, delete_tree
|
||||
from calibre import prints
|
||||
|
||||
class Item(QTreeWidgetItem):
|
||||
pass
|
||||
@ -126,14 +127,12 @@ class CheckLibraryDialog(QDialog):
|
||||
self.text_results = '\n'.join(plaintext)
|
||||
|
||||
def item_changed(self, item, column):
|
||||
print 'item_changed'
|
||||
for it in self.all_items:
|
||||
if it.checkState(1):
|
||||
self.delete.setEnabled(True)
|
||||
return
|
||||
|
||||
def delete_marked(self):
|
||||
print 'delete marked'
|
||||
if not confirm('<p>'+_('The marked files and folders will be '
|
||||
'<b>permanently deleted</b>. Are you sure?')
|
||||
+'</p>', 'check_library_editor_delete', self):
|
||||
@ -153,7 +152,9 @@ class CheckLibraryDialog(QDialog):
|
||||
else:
|
||||
delete_file(p)
|
||||
except:
|
||||
print 'failed to delete', os.path.join(self.db.library_path ,unicode(it.text(1)))
|
||||
prints('failed to delete',
|
||||
os.path.join(self.db.library_path,
|
||||
unicode(it.text(1))))
|
||||
self.run_the_check()
|
||||
|
||||
def copy_to_clipboard(self):
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
@ -71,7 +99,14 @@ class LibraryServer(ContentServer, MobileServer, XMLServer, OPDSServer, Cache,
|
||||
map(int, self.opts.max_cover.split('x'))
|
||||
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()
|
||||
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)
|
||||
|
@ -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]
|
||||
|
||||
|
@ -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()
|
||||
|
||||
|
@ -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:
|
||||
|
@ -58,11 +58,12 @@ def publish(desc, type, port, properties=None, add_hostname=True):
|
||||
'''
|
||||
port = int(port)
|
||||
server = start_server()
|
||||
try:
|
||||
hostname = socket.gethostname().partition('.')[0]
|
||||
except:
|
||||
hostname = 'Unknown'
|
||||
|
||||
if add_hostname:
|
||||
try:
|
||||
hostname = socket.gethostname().partition('.')[0]
|
||||
except:
|
||||
hostname = 'Unknown'
|
||||
desc += ' (on %s)'%hostname
|
||||
local_ip = get_external_ip()
|
||||
type = type+'.local.'
|
||||
|
Loading…
x
Reference in New Issue
Block a user