This commit is contained in:
parent
2dbe0416de
commit
09d8e2bf2d
6
.pre-commit-config.yaml
Normal file
6
.pre-commit-config.yaml
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
repos:
|
||||||
|
- repo: https://github.com/psf/black
|
||||||
|
rev: stable
|
||||||
|
hooks:
|
||||||
|
- id: black
|
||||||
|
language_version: python3.7
|
139
backend.py
139
backend.py
|
@ -12,9 +12,9 @@ DEFAULT_WEBP_QUALITY = 80
|
||||||
DEFAULT_WEBP_METHOD = 0
|
DEFAULT_WEBP_METHOD = 0
|
||||||
DEFAULT_WEBP_SIZE = 1908 # width of FullHD monitor without scroll bar in full screen
|
DEFAULT_WEBP_SIZE = 1908 # width of FullHD monitor without scroll bar in full screen
|
||||||
|
|
||||||
mimetypes['.webp'] = 'image/webp'
|
mimetypes[".webp"] = "image/webp"
|
||||||
|
|
||||||
if os.environ.get('DISK_CACHE', '1') == '0':
|
if os.environ.get("DISK_CACHE", "1") == "0":
|
||||||
disk_cache = DiskCache(enabled=False)
|
disk_cache = DiskCache(enabled=False)
|
||||||
else:
|
else:
|
||||||
disk_cache = DiskCache()
|
disk_cache = DiskCache()
|
||||||
|
@ -44,27 +44,29 @@ class BaseDB:
|
||||||
webp_lossless = False
|
webp_lossless = False
|
||||||
|
|
||||||
self.webp_config = {
|
self.webp_config = {
|
||||||
'quality': webp_quality,
|
"quality": webp_quality,
|
||||||
'method': webp_method,
|
"method": webp_method,
|
||||||
'lossless': webp_lossless,
|
"lossless": webp_lossless,
|
||||||
'size': webp_size
|
"size": webp_size,
|
||||||
}
|
}
|
||||||
|
|
||||||
def _generate_webp(self, fp, max_size=None):
|
def _generate_webp(self, fp, max_size=None):
|
||||||
if max_size is None:
|
if max_size is None:
|
||||||
max_size = tuple([self.webp_config['size']] * 2)
|
max_size = tuple([self.webp_config["size"]] * 2)
|
||||||
|
|
||||||
image = Image.open(fp)
|
image = Image.open(fp)
|
||||||
image.thumbnail(max_size)
|
image.thumbnail(max_size)
|
||||||
image_buffer = BytesIO()
|
image_buffer = BytesIO()
|
||||||
image.save(
|
image.save(
|
||||||
image_buffer,
|
image_buffer,
|
||||||
format='webp',
|
format="webp",
|
||||||
save_all=True, #
|
save_all=True,
|
||||||
append_images=[image], # https://github.com/python-pillow/Pillow/issues/4042
|
append_images=[
|
||||||
quality=self.webp_config['quality'],
|
image
|
||||||
method=self.webp_config['method'],
|
], # https://github.com/python-pillow/Pillow/issues/4042
|
||||||
lossless=self.webp_config['lossless']
|
quality=self.webp_config["quality"],
|
||||||
|
method=self.webp_config["method"],
|
||||||
|
lossless=self.webp_config["lossless"],
|
||||||
)
|
)
|
||||||
|
|
||||||
image_buffer.seek(0)
|
image_buffer.seek(0)
|
||||||
|
@ -79,35 +81,39 @@ class BaseDB:
|
||||||
etag = str(crc32(data))
|
etag = str(crc32(data))
|
||||||
|
|
||||||
thumbnail_cache[filepath] = {
|
thumbnail_cache[filepath] = {
|
||||||
'data_raw': data,
|
"data_raw": data,
|
||||||
'etag': etag,
|
"etag": etag,
|
||||||
'mimetype': 'image/webp'
|
"mimetype": "image/webp",
|
||||||
}
|
}
|
||||||
|
|
||||||
thumbnail = thumbnail_cache[filepath]
|
thumbnail = thumbnail_cache[filepath]
|
||||||
thumbnail['buffer'] = BytesIO()
|
thumbnail["buffer"] = BytesIO()
|
||||||
thumbnail['buffer'].write(thumbnail['data_raw'])
|
thumbnail["buffer"].write(thumbnail["data_raw"])
|
||||||
thumbnail['buffer'].seek(0)
|
thumbnail["buffer"].seek(0)
|
||||||
|
|
||||||
return thumbnail
|
return thumbnail
|
||||||
|
|
||||||
def _generate_page(self, page_buffer, volume, page):
|
def _generate_page(self, page_buffer, volume, page):
|
||||||
page_buffer = self._generate_webp(page_buffer)
|
page_buffer = self._generate_webp(page_buffer)
|
||||||
disk_cache.set(f'{volume}-{page}', page_buffer)
|
disk_cache.set(f"{volume}-{page}", page_buffer)
|
||||||
page_buffer.seek(0)
|
page_buffer.seek(0)
|
||||||
|
|
||||||
return page_buffer
|
return page_buffer
|
||||||
|
|
||||||
|
|
||||||
class CalibreDB(BaseDB):
|
class CalibreDB(BaseDB):
|
||||||
def __init__(self, path='metadata.db', webp_quality=DEFAULT_WEBP_QUALITY, webp_method=DEFAULT_WEBP_METHOD, webp_size=DEFAULT_WEBP_SIZE):
|
def __init__(
|
||||||
|
self,
|
||||||
|
path="metadata.db",
|
||||||
|
webp_quality=DEFAULT_WEBP_QUALITY,
|
||||||
|
webp_method=DEFAULT_WEBP_METHOD,
|
||||||
|
webp_size=DEFAULT_WEBP_SIZE,
|
||||||
|
):
|
||||||
super().__init__(
|
super().__init__(
|
||||||
webp_quality=webp_quality,
|
webp_quality=webp_quality, webp_method=webp_method, webp_size=webp_size
|
||||||
webp_method=webp_method,
|
|
||||||
webp_size=webp_size
|
|
||||||
)
|
)
|
||||||
|
|
||||||
self.database_path = f'file:{path}?mode=ro'
|
self.database_path = f"file:{path}?mode=ro"
|
||||||
|
|
||||||
def create_cursor(self):
|
def create_cursor(self):
|
||||||
conn = sqlite3.connect(self.database_path, uri=True)
|
conn = sqlite3.connect(self.database_path, uri=True)
|
||||||
|
@ -116,7 +122,8 @@ class CalibreDB(BaseDB):
|
||||||
|
|
||||||
def get_series_list(self):
|
def get_series_list(self):
|
||||||
cursor = self.create_cursor()
|
cursor = self.create_cursor()
|
||||||
series = cursor.execute('''
|
series = cursor.execute(
|
||||||
|
"""
|
||||||
select
|
select
|
||||||
series.id as id,
|
series.id as id,
|
||||||
series.name as name,
|
series.name as name,
|
||||||
|
@ -133,13 +140,15 @@ class CalibreDB(BaseDB):
|
||||||
data.format = \'CBZ\'
|
data.format = \'CBZ\'
|
||||||
group by series.name
|
group by series.name
|
||||||
having min(books.series_index)
|
having min(books.series_index)
|
||||||
''')
|
"""
|
||||||
|
)
|
||||||
|
|
||||||
return series
|
return series
|
||||||
|
|
||||||
def get_series_cover(self, series_id):
|
def get_series_cover(self, series_id):
|
||||||
cursor = self.create_cursor()
|
cursor = self.create_cursor()
|
||||||
first_volume = cursor.execute('''
|
first_volume = cursor.execute(
|
||||||
|
"""
|
||||||
select
|
select
|
||||||
books.id
|
books.id
|
||||||
from
|
from
|
||||||
|
@ -152,27 +161,33 @@ class CalibreDB(BaseDB):
|
||||||
series.id = ?
|
series.id = ?
|
||||||
group by series.name
|
group by series.name
|
||||||
having min(books.series_index)
|
having min(books.series_index)
|
||||||
''', (str(series_id),)).fetchone()
|
""",
|
||||||
|
(str(series_id),),
|
||||||
|
).fetchone()
|
||||||
|
|
||||||
if first_volume is None:
|
if first_volume is None:
|
||||||
raise exceptions.NotFound()
|
raise exceptions.NotFound()
|
||||||
|
|
||||||
return self.get_volume_cover(first_volume['id'])
|
return self.get_volume_cover(first_volume["id"])
|
||||||
|
|
||||||
def get_series_cover_thumbnail(self, series_id):
|
def get_series_cover_thumbnail(self, series_id):
|
||||||
return self._generate_thumbnail(self.get_series_cover(series_id))
|
return self._generate_thumbnail(self.get_series_cover(series_id))
|
||||||
|
|
||||||
def get_series_volumes(self, series_id):
|
def get_series_volumes(self, series_id):
|
||||||
cursor = self.create_cursor()
|
cursor = self.create_cursor()
|
||||||
title = cursor.execute('''
|
title = cursor.execute(
|
||||||
|
"""
|
||||||
select
|
select
|
||||||
series.name
|
series.name
|
||||||
from
|
from
|
||||||
series
|
series
|
||||||
where
|
where
|
||||||
series.id = ?
|
series.id = ?
|
||||||
''', (str(series_id),)).fetchone()['name']
|
""",
|
||||||
volumes = cursor.execute('''
|
(str(series_id),),
|
||||||
|
).fetchone()["name"]
|
||||||
|
volumes = cursor.execute(
|
||||||
|
"""
|
||||||
select
|
select
|
||||||
books.id,
|
books.id,
|
||||||
books.title,
|
books.title,
|
||||||
|
@ -186,16 +201,16 @@ class CalibreDB(BaseDB):
|
||||||
books_series_link.series = series.id and
|
books_series_link.series = series.id and
|
||||||
series.id = ?
|
series.id = ?
|
||||||
order by books.series_index
|
order by books.series_index
|
||||||
''', (str(series_id),)).fetchall()
|
""",
|
||||||
|
(str(series_id),),
|
||||||
|
).fetchall()
|
||||||
|
|
||||||
return {
|
return {"title": title, "volumes": volumes}
|
||||||
'title': title,
|
|
||||||
'volumes': volumes
|
|
||||||
}
|
|
||||||
|
|
||||||
def get_volume_cover(self, volume_id):
|
def get_volume_cover(self, volume_id):
|
||||||
cursor = self.create_cursor()
|
cursor = self.create_cursor()
|
||||||
volume = cursor.execute('''
|
volume = cursor.execute(
|
||||||
|
"""
|
||||||
select
|
select
|
||||||
books.has_cover as has_cover,
|
books.has_cover as has_cover,
|
||||||
books.path as path
|
books.path as path
|
||||||
|
@ -204,10 +219,12 @@ class CalibreDB(BaseDB):
|
||||||
where
|
where
|
||||||
books.id = ?
|
books.id = ?
|
||||||
order by books.series_index
|
order by books.series_index
|
||||||
''', (str(volume_id),)).fetchone()
|
""",
|
||||||
|
(str(volume_id),),
|
||||||
|
).fetchone()
|
||||||
|
|
||||||
if volume['has_cover']:
|
if volume["has_cover"]:
|
||||||
return volume['path'] + '/cover.jpg'
|
return volume["path"] + "/cover.jpg"
|
||||||
else:
|
else:
|
||||||
raise exceptions.NotFound()
|
raise exceptions.NotFound()
|
||||||
|
|
||||||
|
@ -216,7 +233,8 @@ class CalibreDB(BaseDB):
|
||||||
|
|
||||||
def get_volume_filepath(self, volume_id):
|
def get_volume_filepath(self, volume_id):
|
||||||
cursor = self.create_cursor()
|
cursor = self.create_cursor()
|
||||||
location = cursor.execute('''
|
location = cursor.execute(
|
||||||
|
"""
|
||||||
select
|
select
|
||||||
books.path as path,
|
books.path as path,
|
||||||
data.name as filename,
|
data.name as filename,
|
||||||
|
@ -227,16 +245,21 @@ class CalibreDB(BaseDB):
|
||||||
where
|
where
|
||||||
data.book = books.id and
|
data.book = books.id and
|
||||||
books.id = ?
|
books.id = ?
|
||||||
''', (str(volume_id),)).fetchone()
|
""",
|
||||||
|
(str(volume_id),),
|
||||||
|
).fetchone()
|
||||||
|
|
||||||
if location is None:
|
if location is None:
|
||||||
raise exceptions.NotFound()
|
raise exceptions.NotFound()
|
||||||
|
|
||||||
return location['path'] + '/' + location['filename'] + '.' + location['extension']
|
return (
|
||||||
|
location["path"] + "/" + location["filename"] + "." + location["extension"]
|
||||||
|
)
|
||||||
|
|
||||||
def get_volume_info(self, volume_id):
|
def get_volume_info(self, volume_id):
|
||||||
cursor = self.create_cursor()
|
cursor = self.create_cursor()
|
||||||
volume_info = cursor.execute('''
|
volume_info = cursor.execute(
|
||||||
|
"""
|
||||||
select
|
select
|
||||||
books.title,
|
books.title,
|
||||||
books_series_link.series
|
books_series_link.series
|
||||||
|
@ -246,15 +269,17 @@ class CalibreDB(BaseDB):
|
||||||
where
|
where
|
||||||
books_series_link.book = books.id and
|
books_series_link.book = books.id and
|
||||||
books.id = ?
|
books.id = ?
|
||||||
''', (str(volume_id),)).fetchone()
|
""",
|
||||||
|
(str(volume_id),),
|
||||||
|
).fetchone()
|
||||||
|
|
||||||
volume_info['pages'] = self.get_volume_page_number(volume_id)
|
volume_info["pages"] = self.get_volume_page_number(volume_id)
|
||||||
|
|
||||||
return volume_info
|
return volume_info
|
||||||
|
|
||||||
def get_volume_page_number(self, volume_id):
|
def get_volume_page_number(self, volume_id):
|
||||||
path = self.get_volume_filepath(volume_id)
|
path = self.get_volume_filepath(volume_id)
|
||||||
with ZipFile(path, 'r') as volume:
|
with ZipFile(path, "r") as volume:
|
||||||
filelist = filter_zip_filelist(volume.filelist)
|
filelist = filter_zip_filelist(volume.filelist)
|
||||||
return len(filelist)
|
return len(filelist)
|
||||||
|
|
||||||
|
@ -262,7 +287,7 @@ class CalibreDB(BaseDB):
|
||||||
if page_number < 1:
|
if page_number < 1:
|
||||||
raise exceptions.NotFound()
|
raise exceptions.NotFound()
|
||||||
path = self.get_volume_filepath(volume_id)
|
path = self.get_volume_filepath(volume_id)
|
||||||
with ZipFile(path, 'r') as volume:
|
with ZipFile(path, "r") as volume:
|
||||||
try:
|
try:
|
||||||
filelist = filter_zip_filelist(volume.filelist)
|
filelist = filter_zip_filelist(volume.filelist)
|
||||||
zip_info = filelist[page_number - 1]
|
zip_info = filelist[page_number - 1]
|
||||||
|
@ -279,16 +304,18 @@ class CalibreDB(BaseDB):
|
||||||
page_buffer.write(volume.read(page_filename))
|
page_buffer.write(volume.read(page_filename))
|
||||||
page_buffer.seek(0)
|
page_buffer.seek(0)
|
||||||
else:
|
else:
|
||||||
mimetype = 'image/webp'
|
mimetype = "image/webp"
|
||||||
|
|
||||||
try:
|
try:
|
||||||
page_buffer = disk_cache.get(f'{volume_id}-{page_number}')
|
page_buffer = disk_cache.get(f"{volume_id}-{page_number}")
|
||||||
except FileNotFoundError:
|
except FileNotFoundError:
|
||||||
with volume.open(page_filename) as orig_page_buffer:
|
with volume.open(page_filename) as orig_page_buffer:
|
||||||
page_buffer = self._generate_page(orig_page_buffer, volume_id, page_number)
|
page_buffer = self._generate_page(
|
||||||
|
orig_page_buffer, volume_id, page_number
|
||||||
|
)
|
||||||
|
|
||||||
return {
|
return {
|
||||||
'buffer': page_buffer,
|
"buffer": page_buffer,
|
||||||
'mimetype': mimetype,
|
"mimetype": mimetype,
|
||||||
'etag': str(zip_info.CRC)
|
"etag": str(zip_info.CRC),
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,7 +5,7 @@ import os
|
||||||
class DiskCache:
|
class DiskCache:
|
||||||
def __init__(self, path=None, enabled=True):
|
def __init__(self, path=None, enabled=True):
|
||||||
if path is None:
|
if path is None:
|
||||||
self.path = os.path.join(os.path.dirname(__file__), 'cache')
|
self.path = os.path.join(os.path.dirname(__file__), "cache")
|
||||||
else:
|
else:
|
||||||
self.path = path
|
self.path = path
|
||||||
|
|
||||||
|
@ -19,14 +19,14 @@ class DiskCache:
|
||||||
if self.enabled is False:
|
if self.enabled is False:
|
||||||
return
|
return
|
||||||
|
|
||||||
with open(self._build_path(identifier), 'wb') as f:
|
with open(self._build_path(identifier), "wb") as f:
|
||||||
f.write(buf.read())
|
f.write(buf.read())
|
||||||
|
|
||||||
def get(self, identifier):
|
def get(self, identifier):
|
||||||
if self.enabled is False:
|
if self.enabled is False:
|
||||||
raise FileNotFoundError
|
raise FileNotFoundError
|
||||||
|
|
||||||
with open(self._build_path(identifier), 'rb') as f:
|
with open(self._build_path(identifier), "rb") as f:
|
||||||
return BytesIO(f.read())
|
return BytesIO(f.read())
|
||||||
|
|
||||||
def _build_path(self, identifier):
|
def _build_path(self, identifier):
|
||||||
|
|
|
@ -9,33 +9,31 @@ import webbrowser
|
||||||
import werkzeug.exceptions as exceptions
|
import werkzeug.exceptions as exceptions
|
||||||
|
|
||||||
# disable access log
|
# disable access log
|
||||||
log = logging.getLogger('werkzeug')
|
log = logging.getLogger("werkzeug")
|
||||||
log.setLevel(logging.ERROR)
|
log.setLevel(logging.ERROR)
|
||||||
# disable flask startup message
|
# disable flask startup message
|
||||||
os.environ['WERKZEUG_RUN_MAIN'] = 'true'
|
os.environ["WERKZEUG_RUN_MAIN"] = "true"
|
||||||
|
|
||||||
mimetypes['.webp'] = 'image/webp'
|
mimetypes[".webp"] = "image/webp"
|
||||||
archive = ZipFile(sys.argv[1], 'r')
|
archive = ZipFile(sys.argv[1], "r")
|
||||||
app = Flask(__name__, static_folder='frontend/dist/static', template_folder='frontend/dist')
|
app = Flask(
|
||||||
|
__name__, static_folder="frontend/dist/static", template_folder="frontend/dist"
|
||||||
|
)
|
||||||
|
|
||||||
# kind of redundant, but avoids returning 200 if page does not exist
|
# kind of redundant, but avoids returning 200 if page does not exist
|
||||||
@app.route('/')
|
@app.route("/")
|
||||||
@app.route('/series/0')
|
@app.route("/series/0")
|
||||||
@app.route('/volume/0')
|
@app.route("/volume/0")
|
||||||
def serve_app(**kwargs):
|
def serve_app(**kwargs):
|
||||||
return render_template('index.html')
|
return render_template("index.html")
|
||||||
|
|
||||||
|
|
||||||
@app.route('/api/volume/0')
|
@app.route("/api/volume/0")
|
||||||
def get_volume_info():
|
def get_volume_info():
|
||||||
return jsonify({
|
return jsonify({"pages": len(archive.filelist), "series": 0, "title": sys.argv[1]})
|
||||||
'pages': len(archive.filelist),
|
|
||||||
'series': 0,
|
|
||||||
'title': sys.argv[1]
|
|
||||||
})
|
|
||||||
|
|
||||||
|
|
||||||
@app.route('/api/volume/0/page/<int:page_number>')
|
@app.route("/api/volume/0/page/<int:page_number>")
|
||||||
def get_volume_page(page_number):
|
def get_volume_page(page_number):
|
||||||
try:
|
try:
|
||||||
page = archive.filelist[page_number - 1]
|
page = archive.filelist[page_number - 1]
|
||||||
|
@ -45,6 +43,6 @@ def get_volume_page(page_number):
|
||||||
return Response(archive.read(page.filename), mimetype=mimetype)
|
return Response(archive.read(page.filename), mimetype=mimetype)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == "__main__":
|
||||||
webbrowser.open('http://localhost:5051/volume/0')
|
webbrowser.open("http://localhost:5051/volume/0")
|
||||||
app.run(host='127.0.0.1', port=5051)
|
app.run(host="127.0.0.1", port=5051)
|
||||||
|
|
|
@ -1,6 +1,13 @@
|
||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3
|
||||||
from backend import CalibreDB
|
from backend import CalibreDB
|
||||||
from flask import Flask, jsonify, send_from_directory, send_file, render_template, request
|
from flask import (
|
||||||
|
Flask,
|
||||||
|
jsonify,
|
||||||
|
send_from_directory,
|
||||||
|
send_file,
|
||||||
|
render_template,
|
||||||
|
request,
|
||||||
|
)
|
||||||
from flask_cors import CORS
|
from flask_cors import CORS
|
||||||
from werkzeug.routing import BaseConverter
|
from werkzeug.routing import BaseConverter
|
||||||
import os
|
import os
|
||||||
|
@ -12,7 +19,7 @@ def send_from_cwd(filename):
|
||||||
|
|
||||||
def send_file_with_etag(fp, etag, **kwargs):
|
def send_file_with_etag(fp, etag, **kwargs):
|
||||||
if request.if_none_match and etag in request.if_none_match:
|
if request.if_none_match and etag in request.if_none_match:
|
||||||
return '', 304
|
return "", 304
|
||||||
|
|
||||||
response = send_file(fp, **kwargs)
|
response = send_file(fp, **kwargs)
|
||||||
response.set_etag(etag)
|
response.set_etag(etag)
|
||||||
|
@ -21,70 +28,75 @@ def send_file_with_etag(fp, etag, **kwargs):
|
||||||
|
|
||||||
|
|
||||||
def send_image(image):
|
def send_image(image):
|
||||||
return send_file_with_etag(image['buffer'], image['etag'], mimetype=image['mimetype'])
|
return send_file_with_etag(
|
||||||
|
image["buffer"], image["etag"], mimetype=image["mimetype"]
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
app = Flask(__name__, static_folder='frontend/dist/static', template_folder='frontend/dist')
|
app = Flask(
|
||||||
app.config['SEND_FILE_MAX_AGE_DEFAULT'] = 604800 # 1 week
|
__name__, static_folder="frontend/dist/static", template_folder="frontend/dist"
|
||||||
|
)
|
||||||
|
app.config["SEND_FILE_MAX_AGE_DEFAULT"] = 604800 # 1 week
|
||||||
CORS(app)
|
CORS(app)
|
||||||
|
|
||||||
db = CalibreDB()
|
db = CalibreDB()
|
||||||
|
|
||||||
# kind of redundant, but avoids returning 200 if page does not exist
|
# kind of redundant, but avoids returning 200 if page does not exist
|
||||||
@app.route('/')
|
@app.route("/")
|
||||||
@app.route('/series/<id>')
|
@app.route("/series/<id>")
|
||||||
@app.route('/volume/<id>')
|
@app.route("/volume/<id>")
|
||||||
def serve_app(**kwargs):
|
def serve_app(**kwargs):
|
||||||
return render_template('index.html')
|
return render_template("index.html")
|
||||||
|
|
||||||
|
|
||||||
@app.route('/api/series')
|
@app.route("/api/series")
|
||||||
def get_series_list():
|
def get_series_list():
|
||||||
return jsonify(list(db.get_series_list()))
|
return jsonify(list(db.get_series_list()))
|
||||||
|
|
||||||
|
|
||||||
@app.route('/api/series/<int:series_id>/cover')
|
@app.route("/api/series/<int:series_id>/cover")
|
||||||
def get_series_cover(series_id):
|
def get_series_cover(series_id):
|
||||||
return send_from_cwd(db.get_series_cover(series_id))
|
return send_from_cwd(db.get_series_cover(series_id))
|
||||||
|
|
||||||
|
|
||||||
@app.route('/api/series/<int:series_id>/cover/thumbnail')
|
@app.route("/api/series/<int:series_id>/cover/thumbnail")
|
||||||
def get_series_cover_thumbnail(series_id):
|
def get_series_cover_thumbnail(series_id):
|
||||||
thumbnail = db.get_series_cover_thumbnail(series_id)
|
thumbnail = db.get_series_cover_thumbnail(series_id)
|
||||||
return send_image(thumbnail)
|
return send_image(thumbnail)
|
||||||
|
|
||||||
|
|
||||||
@app.route('/api/series/<int:series_id>')
|
@app.route("/api/series/<int:series_id>")
|
||||||
def get_series_info(series_id):
|
def get_series_info(series_id):
|
||||||
return jsonify(db.get_series_volumes(series_id))
|
return jsonify(db.get_series_volumes(series_id))
|
||||||
|
|
||||||
|
|
||||||
@app.route('/api/volume/<int:volume_id>/cover')
|
@app.route("/api/volume/<int:volume_id>/cover")
|
||||||
def get_volume_cover(volume_id):
|
def get_volume_cover(volume_id):
|
||||||
return send_from_cwd(db.get_volume_cover(volume_id))
|
return send_from_cwd(db.get_volume_cover(volume_id))
|
||||||
|
|
||||||
|
|
||||||
@app.route('/api/volume/<int:volume_id>/cover/thumbnail')
|
@app.route("/api/volume/<int:volume_id>/cover/thumbnail")
|
||||||
def get_volume_cover_thumbnail(volume_id):
|
def get_volume_cover_thumbnail(volume_id):
|
||||||
thumbnail = db.get_volume_cover_thumbnail(volume_id)
|
thumbnail = db.get_volume_cover_thumbnail(volume_id)
|
||||||
return send_image(thumbnail)
|
return send_image(thumbnail)
|
||||||
|
|
||||||
|
|
||||||
@app.route('/api/volume/<int:volume_id>')
|
@app.route("/api/volume/<int:volume_id>")
|
||||||
def get_volume_info(volume_id):
|
def get_volume_info(volume_id):
|
||||||
return jsonify(db.get_volume_info(volume_id))
|
return jsonify(db.get_volume_info(volume_id))
|
||||||
|
|
||||||
|
|
||||||
@app.route('/api/volume/<int:volume_id>/page/<int:page_number>')
|
@app.route("/api/volume/<int:volume_id>/page/<int:page_number>")
|
||||||
def get_volume_page(volume_id, page_number):
|
def get_volume_page(volume_id, page_number):
|
||||||
page = db.get_volume_page(volume_id, page_number)
|
page = db.get_volume_page(volume_id, page_number)
|
||||||
return send_image(page)
|
return send_image(page)
|
||||||
|
|
||||||
@app.route('/api/volume/<int:volume_id>/page/<int:page_number>/original')
|
|
||||||
|
@app.route("/api/volume/<int:volume_id>/page/<int:page_number>/original")
|
||||||
def get_volume_page_original(volume_id, page_number):
|
def get_volume_page_original(volume_id, page_number):
|
||||||
page = db.get_volume_page(volume_id, page_number, original=True)
|
page = db.get_volume_page(volume_id, page_number, original=True)
|
||||||
return send_image(page)
|
return send_image(page)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == "__main__":
|
||||||
app.run(host='0.0.0.0')
|
app.run(host="0.0.0.0")
|
||||||
|
|
Reference in a new issue