mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-07-09 03:04:10 -04:00
API endpoints to get notes from the server
This commit is contained in:
parent
8b154c1b75
commit
14f4d8366f
@ -365,7 +365,8 @@ class Notes:
|
|||||||
path = make_long_path_useable(path)
|
path = make_long_path_useable(path)
|
||||||
os.listdir(os.path.dirname(path))
|
os.listdir(os.path.dirname(path))
|
||||||
with suppress(FileNotFoundError), open(path, 'rb') as f:
|
with suppress(FileNotFoundError), open(path, 'rb') as f:
|
||||||
return {'name': name, 'data': f.read(), 'hash': resource_hash}
|
mtime = os.stat(f.fileno()).st_mtime
|
||||||
|
return {'name': name, 'data': f.read(), 'hash': resource_hash, 'mtime': mtime}
|
||||||
|
|
||||||
def all_notes(self, conn, restrict_to_fields=(), limit=None, snippet_size=64, return_text=True, process_each_result=None) -> list[dict]:
|
def all_notes(self, conn, restrict_to_fields=(), limit=None, snippet_size=64, return_text=True, process_each_result=None) -> list[dict]:
|
||||||
if snippet_size is None:
|
if snippet_size is None:
|
||||||
|
@ -11,8 +11,9 @@ from functools import partial
|
|||||||
from io import BytesIO
|
from io import BytesIO
|
||||||
from threading import Lock
|
from threading import Lock
|
||||||
|
|
||||||
from calibre import fit_image, sanitize_file_name
|
from calibre import fit_image, guess_type, sanitize_file_name
|
||||||
from calibre.constants import config_dir, iswindows
|
from calibre.constants import config_dir, iswindows
|
||||||
|
from calibre.db.constants import RESOURCE_URL_SCHEME
|
||||||
from calibre.db.errors import NoSuchFormat
|
from calibre.db.errors import NoSuchFormat
|
||||||
from calibre.ebooks.covers import (
|
from calibre.ebooks.covers import (
|
||||||
cprefs, generate_cover, override_prefs, scale_cover, set_use_roman,
|
cprefs, generate_cover, override_prefs, scale_cover, set_use_roman,
|
||||||
@ -160,17 +161,22 @@ def cover(ctx, rd, library_id, db, book_id, width=None, height=None):
|
|||||||
return create_file_copy(ctx, rd, prefix, library_id, book_id, 'jpg', mtime, copy_func)
|
return create_file_copy(ctx, rd, prefix, library_id, book_id, 'jpg', mtime, copy_func)
|
||||||
|
|
||||||
|
|
||||||
def book_filename(rd, book_id, mi, fmt, as_encoded_unicode=False):
|
def fname_for_content_disposition(fname, as_encoded_unicode=False):
|
||||||
au = authors_to_string(mi.authors or [_('Unknown')])
|
|
||||||
title = mi.title or _('Unknown')
|
|
||||||
ext = (fmt or '').lower()
|
|
||||||
fname = f'{title[:30]} - {au[:30]}_{book_id}.{ext}'
|
|
||||||
if as_encoded_unicode:
|
if as_encoded_unicode:
|
||||||
# See https://tools.ietf.org/html/rfc6266
|
# See https://tools.ietf.org/html/rfc6266
|
||||||
fname = sanitize_file_name(fname).encode('utf-8')
|
fname = sanitize_file_name(fname).encode('utf-8')
|
||||||
fname = str(quote(fname))
|
fname = str(quote(fname))
|
||||||
else:
|
else:
|
||||||
fname = ascii_filename(fname).replace('"', '_')
|
fname = ascii_filename(fname).replace('"', '_')
|
||||||
|
return fname
|
||||||
|
|
||||||
|
|
||||||
|
def book_filename(rd, book_id, mi, fmt, as_encoded_unicode=False):
|
||||||
|
au = authors_to_string(mi.authors or [_('Unknown')])
|
||||||
|
title = mi.title or _('Unknown')
|
||||||
|
ext = (fmt or '').lower()
|
||||||
|
fname = f'{title[:30]} - {au[:30]}_{book_id}.{ext}'
|
||||||
|
fname = fname_for_content_disposition(fname, as_encoded_unicode)
|
||||||
if ext == 'kepub' and 'Kobo Touch' in rd.inheaders.get('User-Agent', ''):
|
if ext == 'kepub' and 'Kobo Touch' in rd.inheaders.get('User-Agent', ''):
|
||||||
fname = fname.replace('!', '_')
|
fname = fname.replace('!', '_')
|
||||||
fname += '.epub'
|
fname += '.epub'
|
||||||
@ -347,3 +353,48 @@ def get(ctx, rd, what, book_id, library_id):
|
|||||||
return book_fmt(ctx, rd, library_id, db, book_id, what.lower())
|
return book_fmt(ctx, rd, library_id, db, book_id, what.lower())
|
||||||
except NoSuchFormat:
|
except NoSuchFormat:
|
||||||
raise HTTPNotFound(f'No {what.lower()} format for the book {book_id!r}')
|
raise HTTPNotFound(f'No {what.lower()} format for the book {book_id!r}')
|
||||||
|
|
||||||
|
|
||||||
|
@endpoint('/get-note/{field}/{item_id}/{library_id=None}')
|
||||||
|
def get_note(ctx, rd, field, item_id, library_id):
|
||||||
|
db = get_db(ctx, rd, library_id)
|
||||||
|
if db is None:
|
||||||
|
raise HTTPNotFound(f'Library {library_id} not found')
|
||||||
|
note_data = db.notes_data_for(field, item_id)
|
||||||
|
if not note_data:
|
||||||
|
raise HTTPNotFound(f'Note for {field!r}:{item_id!r} not found')
|
||||||
|
note_data.pop('searchable_text', None)
|
||||||
|
resources = note_data.pop('resource_hashes', None)
|
||||||
|
if resources:
|
||||||
|
import re
|
||||||
|
html = note_data['doc']
|
||||||
|
def r(x):
|
||||||
|
scheme, digest = x.split(':', 1)
|
||||||
|
return f'{scheme}/{digest}'
|
||||||
|
pat = re.compile(rf'{RESOURCE_URL_SCHEME}://({{}})'.format('|'.join(map(r, resources))))
|
||||||
|
def sub(m):
|
||||||
|
s, d = m.group(1).split('/', 1)
|
||||||
|
kw = {'scheme': s, 'digest': d}
|
||||||
|
if library_id:
|
||||||
|
kw['library_id'] = library_id
|
||||||
|
return ctx.url_for('/get-note-resource', **kw)
|
||||||
|
note_data['doc'] = pat.sub(sub, html)
|
||||||
|
rd.outheaders['Content-Type'] = 'text/html; charset=UTF-8'
|
||||||
|
rd.outheaders['Last-Modified'] = http_date(note_data['mtime'])
|
||||||
|
return note_data['doc']
|
||||||
|
|
||||||
|
|
||||||
|
@endpoint('/get-note-resource/{scheme}/{digest}/{library_id=None}')
|
||||||
|
def get_note_resource(ctx, rd, scheme, digest, library_id):
|
||||||
|
db = get_db(ctx, rd, library_id)
|
||||||
|
if db is None:
|
||||||
|
raise HTTPNotFound(f'Library {library_id} not found')
|
||||||
|
d = db.get_notes_resource(f'{scheme}:{digest}')
|
||||||
|
if not d:
|
||||||
|
raise HTTPNotFound(f'Notes resource {scheme}:{digest} not found')
|
||||||
|
name = d['name']
|
||||||
|
rd.outheaders['Content-Type'] = guess_type(name)[0] or 'application/octet-stream'
|
||||||
|
rd.outheaders['Content-Disposition'] = '''inline; filename="{}"; filename*=utf-8''{}'''.format(
|
||||||
|
fname_for_content_disposition(name), fname_for_content_disposition(name, as_encoded_unicode=True))
|
||||||
|
rd.outheaders['Last-Modified'] = http_date(d['mtime'])
|
||||||
|
return d['data']
|
||||||
|
Loading…
x
Reference in New Issue
Block a user