Ejemplo n.º 1
0
def cdb_run(ctx, rd, which, version):
    try:
        m = module_for_cmd(which)
    except ImportError:
        raise HTTPNotFound('No module named: {}'.format(which))
    if not getattr(m, 'readonly', False):
        ctx.check_for_write_access(rd)
    if getattr(m, 'version', 0) != int(version):
        raise HTTPNotFound(('The module {} is not available in version: {}.'
                           'Make sure the version of calibre used for the'
                            ' server and calibredb match').format(which, version))
    db = get_library_data(ctx, rd, strict_library_id=True)[0]
    if ctx.restriction_for(rd, db):
        raise HTTPForbidden('Cannot use the command-line db interface with a user who has per library restrictions')
    raw = rd.read()
    ct = rd.inheaders.get('Content-Type', all=True)
    try:
        if MSGPACK_MIME in ct:
            args = msgpack_loads(raw)
        elif 'application/json' in ct:
            args = json_loads(raw)
        else:
            raise HTTPBadRequest('Only JSON or msgpack requests are supported')
    except Exception:
        raise HTTPBadRequest('args are not valid encoded data')
    if getattr(m, 'needs_srv_ctx', False):
        args = [ctx] + list(args)
    try:
        result = m.implementation(db, partial(ctx.notify_changes, db.backend.library_path), *args)
    except Exception as err:
        import traceback
        return {'err': as_unicode(err), 'tb': traceback.format_exc()}
    return {'result': result}
Ejemplo n.º 2
0
def field_names(ctx, rd, field):
    '''
    Get a list of all names for the specified field
    Optional: ?library_id=<default library>
    '''
    db, library_id = get_library_data(ctx, rd)[:2]
    return tuple(db.all_field_names(field))
Ejemplo n.º 3
0
def book_manifest(ctx, rd, book_id, fmt):
    db, library_id = get_library_data(ctx, rd)[:2]
    force_reload = rd.query.get('force_reload') == '1'
    if plugin_for_input_format(fmt) is None:
        raise HTTPNotFound('The format %s cannot be viewed' % fmt.upper())
    if book_id not in ctx.allowed_book_ids(rd, db):
        raise HTTPNotFound('No book with id: %s in library: %s' % (book_id, library_id))
    with db.safe_read_lock:
        fm = db.format_metadata(book_id, fmt)
        if not fm:
            raise HTTPNotFound('No %s format for the book (id:%s) in the library: %s' % (fmt, book_id, library_id))
        size, mtime = map(int, (fm['size'], time.mktime(fm['mtime'].utctimetuple())*10))
        bhash = book_hash(db.library_id, book_id, fmt, size, mtime)
        with cache_lock:
            mpath = abspath(os.path.join(books_cache_dir(), 'f', bhash, 'calibre-book-manifest.json'))
            if force_reload:
                safe_remove(mpath, True)
            try:
                os.utime(mpath, None)
                with lopen(mpath, 'rb') as f:
                    ans = jsonlib.load(f)
                ans['metadata'] = book_as_json(db, book_id)
                return ans
            except EnvironmentError as e:
                if e.errno != errno.ENOENT:
                    raise
            x = failed_jobs.pop(bhash, None)
            if x is not None:
                return {'aborted':x[0], 'traceback':x[1], 'job_status':'finished'}
            job_id = queued_jobs.get(bhash)
            if job_id is None:
                job_id = queue_job(ctx, partial(db.copy_format_to, book_id, fmt), bhash, fmt, book_id, size, mtime)
    status, result, tb, aborted = ctx.job_status(job_id)
    return {'aborted': aborted, 'traceback':tb, 'job_status':status, 'job_id':job_id}
Ejemplo n.º 4
0
def more_books(ctx, rd):
    '''
    Get more results from the specified search-query, which must
    be specified as JSON in the request body.

    Optional: ?num=50&library_id=<default library>
    '''
    db, library_id = get_library_data(ctx, rd)[:2]

    try:
        num = int(rd.query.get('num', DEFAULT_NUMBER_OF_BOOKS))
    except Exception:
        raise HTTPNotFound('Invalid number of books: %r' % rd.query.get('num'))
    try:
        search_query = load_json_file(rd.request_body_file)
        query, offset, sorts, orders = search_query['query'], search_query[
            'offset'], search_query['sort'], search_query['sort_order']
    except KeyError as err:
        raise HTTPBadRequest('Search query missing key: %s' % as_unicode(err))
    except Exception as err:
        raise HTTPBadRequest('Invalid query: %s' % as_unicode(err))
    ans = {}
    with db.safe_read_lock:
        ans['search_result'] = search_result(ctx, rd, db, query, num, offset,
                                             sorts, orders)
        mdata = ans['metadata'] = {}
        for book_id in ans['search_result']['book_ids']:
            data = book_as_json(db, book_id)
            if data is not None:
                mdata[book_id] = data

    return ans
Ejemplo n.º 5
0
def conversion_status(ctx, rd, job_id):
    with cache_lock:
        job_status = conversion_jobs.get(job_id)
        if job_status is None:
            raise HTTPNotFound('No job with id: {}'.format(job_id))
        job_status.last_check_at = monotonic()
        if job_status.running:
            percent, msg = job_status.current_status
            if rd.query.get('abort_job'):
                ctx.abort_job(job_id)
            return {'running': True, 'percent': percent, 'msg': msg}

        del conversion_jobs[job_id]

    try:
        ans = {'running': False, 'ok': job_status.ok, 'was_aborted':
               job_status.was_aborted, 'traceback': job_status.traceback,
               'log': job_status.log}
        if job_status.ok:
            db, library_id = get_library_data(ctx, rd)[:2]
            if library_id != job_status.library_id:
                raise HTTPNotFound('job library_id does not match')
            fmt = job_status.output_path.rpartition('.')[-1]
            try:
                db.add_format(job_status.book_id, fmt, job_status.output_path)
            except NoSuchBook:
                raise HTTPNotFound(
                    'book_id {} not found in library'.format(job_status.book_id))
            formats_added({job_status.book_id: (fmt,)})
            ans['size'] = os.path.getsize(job_status.output_path)
            ans['fmt'] = fmt
        return ans
    finally:
        job_status.cleanup()
Ejemplo n.º 6
0
def get_books(ctx, rd):
    '''
    Get books for the specified query

    Optional: ?library_id=<default library>&num=50&sort=timestamp.desc&search=''&vl=''
    '''
    library_id, db, sorts, orders, vl = get_basic_query_data(ctx, rd)
    try:
        num = int(rd.query.get('num', rd.opts.num_per_page))
    except Exception:
        raise HTTPNotFound('Invalid number of books: %r' % rd.query.get('num'))
    searchq = rd.query.get('search', '')
    db = get_library_data(ctx, rd)[0]
    ans = {}
    mdata = ans['metadata'] = {}
    with db.safe_read_lock:
        try:
            ans['search_result'] = search_result(
                ctx, rd, db, searchq, num, 0, ','.join(sorts), ','.join(orders), vl
            )
        except ParseException as err:
            # This must not be translated as it is used by the front end to
            # detect invalid search expressions
            raise HTTPBadRequest('Invalid search expression: %s' % as_unicode(err))
        for book_id in ans['search_result']['book_ids']:
            data = book_as_json(db, book_id)
            if data is not None:
                mdata[book_id] = data
    return ans
Ejemplo n.º 7
0
def mobile(ctx, rd):
    db, library_id, library_map, default_library = get_library_data(ctx, rd)
    try:
        start = max(1, int(rd.query.get('start', 1)))
    except ValueError:
        raise HTTPBadRequest('start is not an integer')
    try:
        num = max(0, int(rd.query.get('num', 25)))
    except ValueError:
        raise HTTPBadRequest('num is not an integer')
    search = rd.query.get('search') or ''
    with db.safe_read_lock:
        book_ids = ctx.search(rd, db, search)
        total = len(book_ids)
        ascending = rd.query.get('order', '').lower().strip() == 'ascending'
        sort_by = sanitize_sort_field_name(db.field_metadata, rd.query.get('sort') or 'date')
        try:
            book_ids = db.multisort([(sort_by, ascending)], book_ids)
        except Exception:
            sort_by = 'date'
            book_ids = db.multisort([(sort_by, ascending)], book_ids)
        books = [db.get_metadata(book_id) for book_id in book_ids[(start-1):(start-1)+num]]
    rd.outheaders['Last-Modified'] = http_date(timestampfromdt(db.last_modified()))
    order = 'ascending' if ascending else 'descending'
    q = {b'search':search.encode('utf-8'), b'order':bytes(order), b'sort':sort_by.encode('utf-8'), b'num':bytes(num), 'library_id':library_id}
    url_base = ctx.url_for('/mobile') + '?' + urlencode(q)
    lm = {k:v for k, v in library_map.iteritems() if k != library_id}
    return build_index(books, num, search, sort_by, order, start, total, url_base, db.field_metadata, ctx, lm, library_id)
Ejemplo n.º 8
0
def conversion_data(ctx, rd, book_id):
    from calibre.ebooks.conversion.config import (
        NoSupportedInputFormats, get_input_format_for_book, get_sorted_output_formats)
    db = get_library_data(ctx, rd)[0]
    if not ctx.has_id(rd, db, book_id):
        raise BookNotFound(book_id, db)
    try:
        input_format, input_formats = get_input_format_for_book(db, book_id)
    except NoSupportedInputFormats:
        input_formats = []
    else:
        if rd.query.get('input_fmt') and rd.query.get('input_fmt').lower() in input_formats:
            input_format = rd.query.get('input_fmt').lower()
        if input_format in input_formats:
            input_formats.remove(input_format)
            input_formats.insert(0, input_format)
    input_fmt = input_formats[0] if input_formats else 'epub'
    output_formats = get_sorted_output_formats(rd.query.get('output_fmt'))
    ans = {
        'input_formats': [x.upper() for x in input_formats],
        'output_formats': output_formats,
        'profiles': profiles(),
        'conversion_options': get_conversion_options(input_fmt, output_formats[0], book_id, db),
        'title': db.field_for('title', book_id),
        'authors': db.field_for('authors', book_id),
        'book_id': book_id
    }
    return ans
Ejemplo n.º 9
0
def conversion_data(ctx, rd, book_id):
    from calibre.ebooks.conversion.config import (NoSupportedInputFormats,
                                                  get_input_format_for_book,
                                                  get_sorted_output_formats)
    db = get_library_data(ctx, rd)[0]
    if not ctx.has_id(rd, db, book_id):
        raise BookNotFound(book_id, db)
    try:
        input_format, input_formats = get_input_format_for_book(db, book_id)
    except NoSupportedInputFormats:
        input_formats = []
    else:
        if rd.query.get('input_fmt') and rd.query.get(
                'input_fmt').lower() in input_formats:
            input_format = rd.query.get('input_fmt').lower()
        if input_format in input_formats:
            input_formats.remove(input_format)
            input_formats.insert(0, input_format)
    input_fmt = input_formats[0] if input_formats else 'epub'
    output_formats = get_sorted_output_formats(rd.query.get('output_fmt'))
    ans = {
        'input_formats': [x.upper() for x in input_formats],
        'output_formats':
        output_formats,
        'conversion_options':
        get_conversion_options(input_fmt, output_formats[0], book_id, db),
        'title':
        db.field_for('title', book_id),
        'authors':
        db.field_for('authors', book_id),
        'book_id':
        book_id
    }
    return ans
Ejemplo n.º 10
0
def get_books(ctx, rd):
    '''
    Get books for the specified query

    Optional: ?library_id=<default library>&num=50&sort=timestamp.desc&search=''
    '''
    library_id, db, sorts, orders = get_basic_query_data(ctx, rd)
    try:
        num = int(rd.query.get('num', DEFAULT_NUMBER_OF_BOOKS))
    except Exception:
        raise HTTPNotFound('Invalid number of books: %r' % rd.query.get('num'))
    searchq = rd.query.get('search', '')
    db = get_library_data(ctx, rd)[0]
    ans = {}
    mdata = ans['metadata'] = {}
    with db.safe_read_lock:
        try:
            ans['search_result'] = search_result(ctx, rd, db, searchq, num, 0,
                                                 ','.join(sorts),
                                                 ','.join(orders))
        except ParseException as err:
            # This must not be translated as it is used by the front end to
            # detect invalid search expressions
            raise HTTPBadRequest('Invalid search expression: %s' %
                                 as_unicode(err))
        for book_id in ans['search_result']['book_ids']:
            data = book_as_json(db, book_id)
            if data is not None:
                mdata[book_id] = data
    return ans
Ejemplo n.º 11
0
def more_books(ctx, rd):
    '''
    Get more results from the specified search-query, which must
    be specified as JSON in the request body.

    Optional: ?num=50&library_id=<default library>
    '''
    db, library_id = get_library_data(ctx, rd)[:2]

    try:
        num = int(rd.query.get('num', rd.opts.num_per_page))
    except Exception:
        raise HTTPNotFound('Invalid number of books: %r' % rd.query.get('num'))
    try:
        search_query = load_json_file(rd.request_body_file)
        query, offset, sorts, orders, vl = search_query['query'], search_query[
            'offset'
        ], search_query['sort'], search_query['sort_order'], search_query['vl']
    except KeyError as err:
        raise HTTPBadRequest('Search query missing key: %s' % as_unicode(err))
    except Exception as err:
        raise HTTPBadRequest('Invalid query: %s' % as_unicode(err))
    ans = {}
    with db.safe_read_lock:
        ans['search_result'] = search_result(
            ctx, rd, db, query, num, offset, sorts, orders, vl
        )
        mdata = ans['metadata'] = {}
        for book_id in ans['search_result']['book_ids']:
            data = book_as_json(db, book_id)
            if data is not None:
                mdata[book_id] = data

    return ans
Ejemplo n.º 12
0
def book_manifest(ctx, rd, book_id, fmt):
    db, library_id = get_library_data(ctx, rd)[:2]
    force_reload = rd.query.get('force_reload') == '1'
    if plugin_for_input_format(fmt) is None:
        raise HTTPNotFound('The format %s cannot be viewed' % fmt.upper())
    if book_id not in ctx.allowed_book_ids(rd, db):
        raise HTTPNotFound('No book with id: %s in library: %s' % (book_id, library_id))
    with db.safe_read_lock:
        fm = db.format_metadata(book_id, fmt)
        if not fm:
            raise HTTPNotFound('No %s format for the book (id:%s) in the library: %s' % (fmt, book_id, library_id))
        size, mtime = map(int, (fm['size'], time.mktime(fm['mtime'].utctimetuple())*10))
        bhash = book_hash(db.library_id, book_id, fmt, size, mtime)
        with cache_lock:
            mpath = abspath(os.path.join(books_cache_dir(), 'f', bhash, 'calibre-book-manifest.json'))
            if force_reload:
                safe_remove(mpath, True)
            try:
                os.utime(mpath, None)
                with lopen(mpath, 'rb') as f:
                    ans = jsonlib.load(f)
                ans['metadata'] = book_as_json(db, book_id)
                return ans
            except EnvironmentError as e:
                if e.errno != errno.ENOENT:
                    raise
            x = failed_jobs.pop(bhash, None)
            if x is not None:
                return {'aborted':x[0], 'traceback':x[1], 'job_status':'finished'}
            job_id = queued_jobs.get(bhash)
            if job_id is None:
                job_id = queue_job(ctx, partial(db.copy_format_to, book_id, fmt), bhash, fmt, book_id, size, mtime)
    status, result, tb, aborted = ctx.job_status(job_id)
    return {'aborted': aborted, 'traceback':tb, 'job_status':status, 'job_id':job_id}
Ejemplo n.º 13
0
def start_conversion(ctx, rd, book_id):
    db, library_id = get_library_data(ctx, rd)[:2]
    if not ctx.has_id(rd, db, book_id):
        raise BookNotFound(book_id, db)
    data = json.loads(rd.request_body_file.read())
    input_fmt = data['input_fmt']
    job_id = queue_job(ctx, rd, library_id, db, input_fmt, book_id, data)
    return job_id
Ejemplo n.º 14
0
def field_names(ctx, rd, field):
    '''
    Get a list of all names for the specified field
    Optional: ?library_id=<default library>
    '''
    if field == 'languages':
        ans = all_lang_names()
    else:
        db, library_id = get_library_data(ctx, rd)[:2]
        ans = tuple(sorted(db.all_field_names(field), key=numeric_sort_key))
    return ans
Ejemplo n.º 15
0
def field_names(ctx, rd, field):
    '''
    Get a list of all names for the specified field
    Optional: ?library_id=<default library>
    '''
    if field == 'languages':
        ans = all_lang_names()
    else:
        db, library_id = get_library_data(ctx, rd)[:2]
        ans = tuple(sorted(db.all_field_names(field), key=numeric_sort_key))
    return ans
Ejemplo n.º 16
0
def tag_browser(ctx, rd):
    '''
    Get the Tag Browser serialized as JSON
    Optional: ?library_id=<default library>&sort_tags_by=name&partition_method=first letter
              &collapse_at=25&dont_collapse=&hide_empty_categories=
    '''
    db, library_id = get_library_data(ctx, rd)[:2]
    etag = '%s||%s||%s' % (db.last_modified(), rd.username, library_id)
    etag = hashlib.sha1(etag.encode('utf-8')).hexdigest()
    def generate():
        db, library_id = get_library_data(ctx, rd)[:2]
        return json(ctx, rd, tag_browser, categories_as_json(ctx, rd, db))
    return rd.etagged_dynamic_response(etag, generate)
Ejemplo n.º 17
0
def tag_browser(ctx, rd):
    '''
    Get the Tag Browser serialized as JSON
    Optional: ?library_id=<default library>&sort_tags_by=name&partition_method=first letter
              &collapse_at=25&dont_collapse=&hide_empty_categories=
    '''
    db, library_id = get_library_data(ctx, rd)[:2]
    etag = '%s||%s||%s' % (db.last_modified(), rd.username, library_id)
    etag = hashlib.sha1(etag.encode('utf-8')).hexdigest()
    def generate():
        db, library_id = get_library_data(ctx, rd)[:2]
        return json(ctx, rd, tag_browser, categories_as_json(ctx, rd, db))
    return rd.etagged_dynamic_response(etag, generate)
Ejemplo n.º 18
0
def book_file(ctx, rd, book_id, fmt, size, mtime, name):
    db, library_id = get_library_data(ctx, rd)[:2]
    if not ctx.has_id(rd, db, book_id):
        raise BookNotFound(book_id, db)
    bhash = book_hash(db.library_id, book_id, fmt, size, mtime)
    base = abspath(os.path.join(books_cache_dir(), 'f'))
    mpath = abspath(os.path.join(base, bhash, name))
    if not mpath.startswith(base):
        raise HTTPNotFound('No book file with hash: %s and name: %s' % (bhash, name))
    try:
        return rd.filesystem_file_with_custom_etag(lopen(mpath, 'rb'), bhash, name)
    except EnvironmentError as e:
        if e.errno != errno.ENOENT:
            raise
        raise HTTPNotFound('No book file with hash: %s and name: %s' % (bhash, name))
Ejemplo n.º 19
0
def book_file(ctx, rd, book_id, fmt, size, mtime, name):
    db, library_id = get_library_data(ctx, rd)[:2]
    if not ctx.has_id(rd, db, book_id):
        raise BookNotFound(book_id, db)
    bhash = book_hash(db.library_id, book_id, fmt, size, mtime)
    base = abspath(os.path.join(books_cache_dir(), 'f'))
    mpath = abspath(os.path.join(base, bhash, name))
    if not mpath.startswith(base):
        raise HTTPNotFound('No book file with hash: %s and name: %s' % (bhash, name))
    try:
        return rd.filesystem_file_with_custom_etag(lopen(mpath, 'rb'), bhash, name)
    except EnvironmentError as e:
        if e.errno != errno.ENOENT:
            raise
        raise HTTPNotFound('No book file with hash: %s and name: %s' % (bhash, name))
Ejemplo n.º 20
0
def tag_browser(ctx, rd):
    '''
    Get the Tag Browser serialized as JSON
    Optional: ?library_id=<default library>&sort_tags_by=name&partition_method=first letter
              &collapse_at=25&dont_collapse=&hide_empty_categories=&vl=''
    '''
    db, library_id = get_library_data(ctx, rd)[:2]
    opts = categories_settings(rd.query, db)
    vl = rd.query.get('vl') or ''
    etag = cPickle.dumps([db.last_modified().isoformat(), rd.username, library_id, vl, list(opts)], -1)
    etag = hashlib.sha1(etag).hexdigest()

    def generate():
        return json(ctx, rd, tag_browser, categories_as_json(ctx, rd, db, opts, vl))

    return rd.etagged_dynamic_response(etag, generate)
Ejemplo n.º 21
0
def tag_browser(ctx, rd):
    '''
    Get the Tag Browser serialized as JSON
    Optional: ?library_id=<default library>&sort_tags_by=name&partition_method=first letter
              &collapse_at=25&dont_collapse=&hide_empty_categories=&vl=''
    '''
    db, library_id = get_library_data(ctx, rd)[:2]
    opts = categories_settings(rd.query, db)
    vl = rd.query.get('vl') or ''
    etag = cPickle.dumps([db.last_modified().isoformat(), rd.username, library_id, vl, list(opts)], -1)
    etag = hashlib.sha1(etag).hexdigest()

    def generate():
        return json(ctx, rd, tag_browser, categories_as_json(ctx, rd, db, opts, vl))

    return rd.etagged_dynamic_response(etag, generate)
Ejemplo n.º 22
0
def get_basic_query_data(ctx, rd):
    db, library_id, library_map, default_library = get_library_data(ctx, rd)
    skeys = db.field_metadata.sortable_field_keys()
    sorts, orders = [], []
    for x in rd.query.get('sort', '').split(','):
        if x:
            s, o = x.rpartition('.')[::2]
            if o and not s:
                s, o = o, ''
            if o not in ('asc', 'desc'):
                o = 'asc'
            if s.startswith('_'):
                s = '#' + s[1:]
            s = sanitize_sort_field_name(db.field_metadata, s)
            if s in skeys:
                sorts.append(s), orders.append(o)
    if not sorts:
        sorts, orders = ['timestamp'], ['desc']
    return library_id, db, sorts, orders, rd.query.get('vl') or ''
Ejemplo n.º 23
0
def get_basic_query_data(ctx, rd):
    db, library_id, library_map, default_library = get_library_data(ctx, rd)
    skeys = db.field_metadata.sortable_field_keys()
    sorts, orders = [], []
    for x in rd.query.get('sort', '').split(','):
        if x:
            s, o = x.rpartition('.')[::2]
            if o and not s:
                s, o = o, ''
            if o not in ('asc', 'desc'):
                o = 'asc'
            if s.startswith('_'):
                s = '#' + s[1:]
            s = sanitize_sort_field_name(db.field_metadata, s)
            if s in skeys:
                sorts.append(s), orders.append(o)
    if not sorts:
        sorts, orders = ['timestamp'], ['desc']
    return library_id, db, sorts, orders, rd.query.get('vl') or ''
Ejemplo n.º 24
0
def cdb_run(ctx, rd, which, version):
    try:
        m = module_for_cmd(which)
    except ImportError:
        raise HTTPNotFound('No module named: {}'.format(which))
    if not getattr(m, 'readonly', False):
        ctx.check_for_write_access(rd)
    if getattr(m, 'version', 0) != int(version):
        raise HTTPNotFound(
            ('The module {} is not available in version: {}.'
             'Make sure the version of calibre used for the'
             ' server and calibredb match').format(which, version))
    db = get_library_data(ctx, rd, strict_library_id=True)[0]
    if ctx.restriction_for(rd, db):
        raise HTTPForbidden(
            'Cannot use the command-line db interface with a user who has per library restrictions'
        )
    raw = rd.read()
    ct = rd.inheaders.get('Content-Type', all=True)
    ct = {x.lower().partition(';')[0] for x in ct}
    try:
        if MSGPACK_MIME in ct:
            args = msgpack_loads(raw)
        elif 'application/json' in ct:
            args = json_loads(raw)
        else:
            raise HTTPBadRequest('Only JSON or msgpack requests are supported')
    except Exception:
        raise HTTPBadRequest('args are not valid encoded data')
    if getattr(m, 'needs_srv_ctx', False):
        args = [ctx] + list(args)
    try:
        result = m.implementation(
            db, partial(ctx.notify_changes, db.backend.library_path), *args)
    except Exception as err:
        tb = ''
        if not getattr(err, 'suppress_traceback', False):
            import traceback
            tb = traceback.format_exc()
        return {'err': as_unicode(err), 'tb': tb}
    return {'result': result}
Ejemplo n.º 25
0
def mobile(ctx, rd):
    db, library_id, library_map, default_library = get_library_data(ctx, rd)
    try:
        start = max(1, int(rd.query.get('start', 1)))
    except ValueError:
        raise HTTPBadRequest('start is not an integer')
    try:
        num = max(0, int(rd.query.get('num', 25)))
    except ValueError:
        raise HTTPBadRequest('num is not an integer')
    search = rd.query.get('search') or ''
    with db.safe_read_lock:
        book_ids = ctx.search(rd, db, search)
        total = len(book_ids)
        ascending = rd.query.get('order', '').lower().strip() == 'ascending'
        sort_by = sanitize_sort_field_name(db.field_metadata,
                                           rd.query.get('sort') or 'date')
        try:
            book_ids = db.multisort([(sort_by, ascending)], book_ids)
        except Exception:
            sort_by = 'date'
            book_ids = db.multisort([(sort_by, ascending)], book_ids)
        books = [
            db.get_metadata(book_id)
            for book_id in book_ids[(start - 1):(start - 1) + num]
        ]
    rd.outheaders['Last-Modified'] = http_date(
        timestampfromdt(db.last_modified()))
    order = 'ascending' if ascending else 'descending'
    q = {
        b'search': search.encode('utf-8'),
        b'order': order.encode('ascii'),
        b'sort': sort_by.encode('utf-8'),
        b'num': as_bytes(num),
        'library_id': library_id
    }
    url_base = ctx.url_for('/mobile') + '?' + urlencode(q)
    lm = {k: v for k, v in iteritems(library_map) if k != library_id}
    return build_index(rd, books, num, search, sort_by, order, start, total,
                       url_base, db.field_metadata, ctx, lm, library_id)
Ejemplo n.º 26
0
def conversion_status(ctx, rd, job_id):
    with cache_lock:
        job_status = conversion_jobs.get(job_id)
        if job_status is None:
            raise HTTPNotFound(f'No job with id: {job_id}')
        job_status.last_check_at = monotonic()
        if job_status.running:
            percent, msg = job_status.current_status
            if rd.query.get('abort_job'):
                ctx.abort_job(job_id)
            return {'running': True, 'percent': percent, 'msg': msg}

        del conversion_jobs[job_id]

    try:
        ans = {
            'running': False,
            'ok': job_status.ok,
            'was_aborted': job_status.was_aborted,
            'traceback': job_status.traceback,
            'log': job_status.log
        }
        if job_status.ok:
            db, library_id = get_library_data(ctx, rd)[:2]
            if library_id != job_status.library_id:
                raise HTTPNotFound('job library_id does not match')
            fmt = job_status.output_path.rpartition('.')[-1]
            try:
                db.add_format(job_status.book_id, fmt, job_status.output_path)
            except NoSuchBook:
                raise HTTPNotFound(
                    f'book_id {job_status.book_id} not found in library')
            formats_added({job_status.book_id: (fmt, )})
            ans['size'] = os.path.getsize(job_status.output_path)
            ans['fmt'] = fmt
        return ans
    finally:
        job_status.cleanup()
Ejemplo n.º 27
0
def book_manifest(ctx, rd, book_id, fmt):
    db, library_id = get_library_data(ctx, rd)[:2]
    force_reload = rd.query.get('force_reload') == '1'
    if plugin_for_input_format(fmt) is None:
        raise HTTPNotFound('The format %s cannot be viewed' % fmt.upper())
    if not ctx.has_id(rd, db, book_id):
        raise BookNotFound(book_id, db)
    with db.safe_read_lock:
        fm = db.format_metadata(book_id, fmt, allow_cache=False)
        if not fm:
            raise HTTPNotFound(f'No {fmt} format for the book (id:{book_id}) in the library: {library_id}')
        size, mtime = map(int, (fm['size'], time.mktime(fm['mtime'].utctimetuple())*10))
        bhash = book_hash(db.library_id, book_id, fmt, size, mtime)
        with cache_lock:
            mpath = abspath(os.path.join(books_cache_dir(), 'f', bhash, 'calibre-book-manifest.json'))
            if force_reload:
                safe_remove(mpath, True)
            try:
                os.utime(mpath, None)
                with lopen(mpath, 'rb') as f:
                    ans = jsonlib.load(f)
                ans['metadata'] = book_as_json(db, book_id)
                user = rd.username or None
                ans['last_read_positions'] = db.get_last_read_positions(book_id, fmt, user) if user else []
                ans['annotations_map'] = db.annotations_map_for_book(book_id, fmt, user_type='web', user=user or '*')
                return ans
            except OSError as e:
                if e.errno != errno.ENOENT:
                    raise
            x = failed_jobs.pop(bhash, None)
            if x is not None:
                return {'aborted':x[0], 'traceback':x[1], 'job_status':'finished'}
            job_id = queued_jobs.get(bhash)
            if job_id is None:
                job_id = queue_job(ctx, partial(db.copy_format_to, book_id, fmt), bhash, fmt, book_id, size, mtime)
    status, result, tb, aborted = ctx.job_status(job_id)
    return {'aborted': aborted, 'traceback':tb, 'job_status':status, 'job_id':job_id}
Ejemplo n.º 28
0
 def __init__(self, ctx, rd):
     self.db, self.library_id, self.library_map, self.default_library = get_library_data(
         ctx, rd)
     self.ctx, self.rd = ctx, rd
Ejemplo n.º 29
0
 def generate():
     db, library_id = get_library_data(ctx, rd)[:2]
     return json(ctx, rd, tag_browser, categories_as_json(ctx, rd, db))
Ejemplo n.º 30
0
 def generate():
     db, library_id = get_library_data(ctx, rd)[:2]
     return json(ctx, rd, tag_browser, categories_as_json(ctx, rd, db))
Ejemplo n.º 31
0
 def __init__(self, ctx, rd):
     self.db, self.library_id, self.library_map, self.default_library = get_library_data(ctx, rd)
     self.ctx, self.rd = ctx, rd