mirror of
https://github.com/janeczku/calibre-web
synced 2025-01-08 08:20:30 +00:00
Merge remote-tracking branch 'simple_view/master' into simple_view
This commit is contained in:
commit
f0ca56522d
90
cps/basic.py
Normal file
90
cps/basic.py
Normal file
@ -0,0 +1,90 @@
|
|||||||
|
# This file is part of the Calibre-Web (https://github.com/janeczku/calibre-web)
|
||||||
|
# Copyright (C) 2018-2019 OzzieIsaacs, cervinko, jkrehm, bodybybuddha, ok11,
|
||||||
|
# andy29485, idalin, Kyosfonica, wuqi, Kennyl, lemmsh,
|
||||||
|
# falgh1, grunjol, csitko, ytils, xybydy, trasba, vrabe,
|
||||||
|
# ruben-herold, marblepebble, JackED42, SiphonSquirrel,
|
||||||
|
# apetresc, nanu-c, mutschler, carderne
|
||||||
|
#
|
||||||
|
# This program is free software: you can redistribute it and/or modify
|
||||||
|
# it under the terms of the GNU General Public License as published by
|
||||||
|
# the Free Software Foundation, either version 3 of the License, or
|
||||||
|
# (at your option) any later version.
|
||||||
|
#
|
||||||
|
# This program is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
# GNU General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU General Public License
|
||||||
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
|
||||||
|
from cps.pagination import Pagination
|
||||||
|
from flask import Blueprint
|
||||||
|
from flask_babel import gettext as _
|
||||||
|
from flask_babel import get_locale
|
||||||
|
from flask import request, redirect, url_for
|
||||||
|
|
||||||
|
from . import logger, isoLanguages
|
||||||
|
from . import db, config
|
||||||
|
from . import calibre_db
|
||||||
|
from .usermanagement import login_required_if_no_ano
|
||||||
|
from .render_template import render_title_template
|
||||||
|
from .web import get_sort_function
|
||||||
|
|
||||||
|
try:
|
||||||
|
from natsort import natsorted as sort
|
||||||
|
except ImportError:
|
||||||
|
sort = sorted # Just use regular sort then, may cause issues with badly named pages in cbz/cbr files
|
||||||
|
|
||||||
|
basic = Blueprint('basic', __name__)
|
||||||
|
|
||||||
|
log = logger.create()
|
||||||
|
|
||||||
|
|
||||||
|
@basic.route("/basic", methods=["GET"])
|
||||||
|
@login_required_if_no_ano
|
||||||
|
def index():
|
||||||
|
term = request.args.get("query", "") # default to showing all books
|
||||||
|
limit = 15
|
||||||
|
page = int(request.args.get("page") or 1)
|
||||||
|
off = (page - 1) * limit
|
||||||
|
order = get_sort_function("stored", "search")
|
||||||
|
join = db.books_series_link, db.Books.id == db.books_series_link.c.book, db.Series
|
||||||
|
entries, result_count, pagination = calibre_db.get_search_results(term,
|
||||||
|
config,
|
||||||
|
off,
|
||||||
|
order,
|
||||||
|
limit,
|
||||||
|
*join)
|
||||||
|
return render_title_template('basic_index.html',
|
||||||
|
searchterm=term,
|
||||||
|
pagination=pagination,
|
||||||
|
query=term,
|
||||||
|
adv_searchterm=term,
|
||||||
|
entries=entries,
|
||||||
|
result_count=result_count,
|
||||||
|
title=_("Search"),
|
||||||
|
page="search",
|
||||||
|
order=order[1])
|
||||||
|
|
||||||
|
|
||||||
|
@basic.route("/basic_book/<int:book_id>")
|
||||||
|
@login_required_if_no_ano
|
||||||
|
def show_book(book_id):
|
||||||
|
entries = calibre_db.get_book_read_archived(book_id, config.config_read_column, allow_show_archived=True)
|
||||||
|
if entries:
|
||||||
|
entry = entries[0]
|
||||||
|
for lang_index in range(0, len(entry.languages)):
|
||||||
|
entry.languages[lang_index].language_name = isoLanguages.get_language_name(get_locale(), entry.languages[
|
||||||
|
lang_index].lang_code)
|
||||||
|
entry.ordered_authors = calibre_db.order_authors([entry])
|
||||||
|
|
||||||
|
return render_title_template('basic_detail.html',
|
||||||
|
entry=entry,
|
||||||
|
is_xhr=request.headers.get('X-Requested-With') == 'XMLHttpRequest',
|
||||||
|
title=entry.title,
|
||||||
|
page="book")
|
||||||
|
else:
|
||||||
|
log.debug("Selected book is unavailable. File does not exist or is not accessible")
|
||||||
|
return redirect(url_for("basic.index"))
|
@ -43,6 +43,8 @@ def url_for_other_page(page):
|
|||||||
args = request.view_args.copy()
|
args = request.view_args.copy()
|
||||||
args['page'] = page
|
args['page'] = page
|
||||||
for get, val in request.args.items():
|
for get, val in request.args.items():
|
||||||
|
if get == "page":
|
||||||
|
continue
|
||||||
args[get] = val
|
args[get] = val
|
||||||
return url_for(request.endpoint, **args)
|
return url_for(request.endpoint, **args)
|
||||||
|
|
||||||
|
@ -31,6 +31,7 @@ def main():
|
|||||||
app = create_app()
|
app = create_app()
|
||||||
|
|
||||||
from .web import web
|
from .web import web
|
||||||
|
from .basic import basic
|
||||||
from .opds import opds
|
from .opds import opds
|
||||||
from .admin import admi
|
from .admin import admi
|
||||||
from .gdrive import gdrive
|
from .gdrive import gdrive
|
||||||
@ -64,6 +65,7 @@ def main():
|
|||||||
app.register_blueprint(search)
|
app.register_blueprint(search)
|
||||||
app.register_blueprint(tasks)
|
app.register_blueprint(tasks)
|
||||||
app.register_blueprint(web)
|
app.register_blueprint(web)
|
||||||
|
app.register_blueprint(basic)
|
||||||
app.register_blueprint(opds)
|
app.register_blueprint(opds)
|
||||||
limiter.limit("3/minute", key_func=request_username)(opds)
|
limiter.limit("3/minute", key_func=request_username)(opds)
|
||||||
app.register_blueprint(jinjia)
|
app.register_blueprint(jinjia)
|
||||||
|
81
cps/templates/basic_detail.html
Normal file
81
cps/templates/basic_detail.html
Normal file
@ -0,0 +1,81 @@
|
|||||||
|
{% extends "basic_layout.html" %}
|
||||||
|
|
||||||
|
{% block body %}
|
||||||
|
<div>
|
||||||
|
|
||||||
|
<h2 id="title">{{ entry.title }}</h2>
|
||||||
|
<div>
|
||||||
|
{% for author in entry.ordered_authors %}
|
||||||
|
<p>{{ author.name.replace("|",",") }}</p>
|
||||||
|
{% endfor %}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="cover">
|
||||||
|
<img title="{{ entry.title }}" src="{{ url_for('web.get_cover', book_id=entry.id, resolution='og', c=entry|last_modified) }}"/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{% if current_user.role_download() %}
|
||||||
|
{% if entry.data|length %}
|
||||||
|
<div>
|
||||||
|
<h2>Download</h2>
|
||||||
|
{% for format in entry.data %}
|
||||||
|
<p>
|
||||||
|
<a href="{{ url_for('web.download_link', book_id=entry.id, book_format=format.format|lower, anyname=entry.id|string+'.'+format.format|lower) }}">
|
||||||
|
{{ format.format }} ({{ format.uncompressed_size|filesizeformat }})</a>
|
||||||
|
</p>
|
||||||
|
{% endfor %}
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
<h2>Details</h2>
|
||||||
|
|
||||||
|
{% if entry.series|length > 0 %}
|
||||||
|
<p>{{ _("Book %(index)s of %(range)s", index=entry.series_index | formatfloat(2), range=(entry.series[0].name)|safe) }}</p>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
{% if entry.languages|length > 0 %}
|
||||||
|
<div>
|
||||||
|
<p>
|
||||||
|
<span>
|
||||||
|
{{_('Language')}}: {% for language in entry.languages %}{{language.language_name}}{% if not loop.last %}, {% endif %}{% endfor %}
|
||||||
|
</span>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
{% if entry.identifiers|length > 0 %}
|
||||||
|
<div>
|
||||||
|
<p>
|
||||||
|
<span></span>
|
||||||
|
{% for identifier in entry.identifiers %}
|
||||||
|
<p>{{ identifier.format_type() }}: {{ identifier|escape }}</p>
|
||||||
|
{% endfor %}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
{% if entry.publishers|length > 0 %}
|
||||||
|
<div>
|
||||||
|
<p>
|
||||||
|
<span>{{ _('Publisher') }}:
|
||||||
|
<span>{{ entry.publishers[0].name }}</span>
|
||||||
|
</span>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
{% if (entry.pubdate|string)[:10] != '0101-01-01' %}
|
||||||
|
<div>
|
||||||
|
<p>{{ _('Published') }}: {{ entry.pubdate|formatdate }} </p>
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
{% if entry.comments|length > 0 and entry.comments[0].text|length > 0 %}
|
||||||
|
<div>
|
||||||
|
<h2 id="decription">{{ _('Description:') }}</h2>
|
||||||
|
{{ entry.comments[0].text|safe }}
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
{% endblock %}
|
32
cps/templates/basic_index.html
Normal file
32
cps/templates/basic_index.html
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
{% extends "basic_layout.html" %}
|
||||||
|
{% block body %}
|
||||||
|
|
||||||
|
<div class="pagination">
|
||||||
|
<div>
|
||||||
|
{% if pagination.has_prev %}
|
||||||
|
<a href="{{ (pagination.page - 1)|url_for_other_page }}">« {{_('Previous')}}</a>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
{% if pagination.has_next %}
|
||||||
|
<a href="{{ (pagination.page + 1)|url_for_other_page }}">{{_('Next')}} »</a>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{% if entries|length < 1 %}
|
||||||
|
<p>{{_('No Results Found')}}</p>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
{% for entry in entries %}
|
||||||
|
{% if entry.Books.authors %}
|
||||||
|
{% set author = entry.Books.authors[0].name.replace('|',',')|shortentitle(30) %}
|
||||||
|
{% else %}
|
||||||
|
{% set author = '' %}
|
||||||
|
{% endif %}
|
||||||
|
<a href="{{ url_for('basic.show_book', book_id=entry.Books.id) }}">
|
||||||
|
<p class="listing" title="{{entry.Books.title}}">{{ author }} - {{entry.Books.title|shortentitle}}</p>
|
||||||
|
</a>
|
||||||
|
{% endfor %}
|
||||||
|
|
||||||
|
{% endblock %}
|
147
cps/templates/basic_layout.html
Normal file
147
cps/templates/basic_layout.html
Normal file
@ -0,0 +1,147 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="{{ current_user.locale }}">
|
||||||
|
|
||||||
|
<head>
|
||||||
|
<title>{{instance}} | {{title}}</title>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<meta name='viewport' content='initial-scale=1,maximum-scale=5,user-scalable=no' />
|
||||||
|
<style>
|
||||||
|
body {
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
nav {
|
||||||
|
height: 75px;
|
||||||
|
padding: 5px 20px;
|
||||||
|
width: 100%;
|
||||||
|
display: table;
|
||||||
|
box-sizing: border-box;
|
||||||
|
border-bottom: 1px solid black;
|
||||||
|
}
|
||||||
|
|
||||||
|
nav > * {
|
||||||
|
display: inline-block;
|
||||||
|
display: table-cell;
|
||||||
|
vertical-align: middle;
|
||||||
|
float: none;
|
||||||
|
text-align: center;
|
||||||
|
width: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
nav > *:first-child {
|
||||||
|
text-align: left;
|
||||||
|
width: 1%;
|
||||||
|
}
|
||||||
|
|
||||||
|
nav > *:last-child {
|
||||||
|
text-align: right;
|
||||||
|
width: 1%;
|
||||||
|
}
|
||||||
|
|
||||||
|
nav > a {
|
||||||
|
color: black;
|
||||||
|
margin: 0 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.search {
|
||||||
|
margin: auto auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
form > input {
|
||||||
|
width: 18ch;
|
||||||
|
padding-left: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
form > * {
|
||||||
|
height: 50px;
|
||||||
|
background-color: white;
|
||||||
|
border-radius: 0;
|
||||||
|
border: 1px solid #ccc;
|
||||||
|
padding: 0;
|
||||||
|
margin: 0;
|
||||||
|
display: inline-block;
|
||||||
|
vertical-align: top;
|
||||||
|
}
|
||||||
|
|
||||||
|
form > span {
|
||||||
|
margin-left: -5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
button {
|
||||||
|
border: none;
|
||||||
|
padding: 0 10px;
|
||||||
|
margin: 0;
|
||||||
|
width: 160px;
|
||||||
|
height: 100%;
|
||||||
|
background-color: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
.body {
|
||||||
|
padding: 5px 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
a {
|
||||||
|
color: black;
|
||||||
|
}
|
||||||
|
|
||||||
|
img {
|
||||||
|
width: 150px;
|
||||||
|
height: 250px;
|
||||||
|
object-fit: cover;
|
||||||
|
}
|
||||||
|
|
||||||
|
.listing {
|
||||||
|
white-space: nowrap;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
margin-right: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pagination {
|
||||||
|
padding: 10px 0;
|
||||||
|
height: 20px;
|
||||||
|
font-weight: 700;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pagination > div {
|
||||||
|
float: left;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pagination > div:last-child {
|
||||||
|
float: right;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<div>
|
||||||
|
<div>
|
||||||
|
{% if current_user.is_authenticated or g.allow_anonymous %}
|
||||||
|
<nav>
|
||||||
|
<a href="/basic">
|
||||||
|
<span><h1>{{_('Home')}}</h1></span>
|
||||||
|
</a>
|
||||||
|
<div class="search">
|
||||||
|
<form role="search" action="{{url_for('basic.index')}}" method="GET">
|
||||||
|
<input type="text" id="query" name="query" placeholder="{{_('Search Library')}}" value="{{searchterm}}">
|
||||||
|
<span>
|
||||||
|
<button type="submit" id="query_submit">{{_('Search')}}</button>
|
||||||
|
</span>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
{% if not current_user.is_anonymous %}
|
||||||
|
<a href="{{url_for('web.logout')}}">
|
||||||
|
<span>{{_('Logout')}}</span>
|
||||||
|
</a>
|
||||||
|
{% endif %}
|
||||||
|
</nav>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="body">
|
||||||
|
{% block body %}
|
||||||
|
{% endblock %}
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
|
||||||
|
</html>
|
@ -37,11 +37,13 @@
|
|||||||
<a class="navbar-brand" href="{{url_for('web.index')}}">{{instance}}</a>
|
<a class="navbar-brand" href="{{url_for('web.index')}}">{{instance}}</a>
|
||||||
</div>
|
</div>
|
||||||
{% if g.current_theme == 1 %}
|
{% if g.current_theme == 1 %}
|
||||||
<div class="home-btn"><a class="home-btn-tooltip" href="{{url_for("web.index",page=1)}}" data-toggle="tooltip" title="" data-placement="bottom" data-original-title="Home"></a></div>
|
<div class="home-btn"><a class="home-btn-tooltip" href="{{url_for("web.index", page=1)}}" data-toggle="tooltip" title="" data-placement="bottom" data-original-title="Home"></a></div>
|
||||||
<div class="plexBack"><a href="{{url_for('web.index')}}"></a></div>
|
<div class="plexBack"><a href="{{url_for('web.index')}}"></a></div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% if current_user.is_authenticated or g.allow_anonymous %}
|
{% if current_user.is_authenticated or g.allow_anonymous %}
|
||||||
<form class="navbar-form navbar-left" role="search" action="{{url_for('search.simple_search')}}" method="GET">
|
<a href="{{url_for('basic.index')}}" class="navbar-form navbar-left btn btn-default" id="basic"><span class="glyphicon glyphicon-phone"></span><span>{{_('Simple view')}}</span></a>
|
||||||
|
<!--# margin 0, padding 15, background color-->
|
||||||
|
<form class="navbar-form navbar-left" role="search" action="{{url_for('search.simple_search')}}" method="GET">
|
||||||
<div class="form-group input-group input-group-sm">
|
<div class="form-group input-group input-group-sm">
|
||||||
<label for="query" class="sr-only">{{_('Search')}}</label>
|
<label for="query" class="sr-only">{{_('Search')}}</label>
|
||||||
<input type="text" class="form-control" id="query" name="query" placeholder="{{_('Search Library')}}" value="{{searchterm}}">
|
<input type="text" class="form-control" id="query" name="query" placeholder="{{_('Search Library')}}" value="{{searchterm}}">
|
||||||
|
Loading…
Reference in New Issue
Block a user