mirror of
https://github.com/janeczku/calibre-web
synced 2024-11-28 04:19:59 +00:00
Now shows read in website, and can be toggled by clicking, two extra options in sidebar (read/unread books)
This commit is contained in:
parent
3cadde6579
commit
d29d079d15
@ -100,6 +100,18 @@
|
||||
</div>
|
||||
</p>
|
||||
{% endif %}
|
||||
<p>
|
||||
<div class="custom_columns" id="have_read">
|
||||
Read
|
||||
{% if have_read %}
|
||||
<span class="glyphicon glyphicon-ok"></span>
|
||||
{% else %}
|
||||
<span class="glyphicon glyphicon-remove"></span>
|
||||
{% endif %}
|
||||
<form id="have_read_form" action="{{ url_for('toggle_read', id=entry.id)}}" method="POST") >
|
||||
</form>
|
||||
</div>
|
||||
</p>
|
||||
|
||||
|
||||
{% if entry.comments|length > 0 and entry.comments[0].text|length > 0%}
|
||||
@ -201,3 +213,34 @@
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
||||
{% block js %}
|
||||
<script src="http://malsup.github.com/jquery.form.js"></script>
|
||||
<script>
|
||||
$( document ).ready(function() {
|
||||
var haveReadForm = $('#have_read_form');
|
||||
haveReadForm.ajaxForm();
|
||||
});
|
||||
$("#have_read").attr('unselectable','on')
|
||||
.css({'-moz-user-select':'-moz-none',
|
||||
'-moz-user-select':'none',
|
||||
'-o-user-select':'none',
|
||||
'-khtml-user-select':'none', /* you could also put this in a class */
|
||||
'-webkit-user-select':'none',/* and add the CSS class here instead */
|
||||
'-ms-user-select':'none',
|
||||
'user-select':'none'
|
||||
}).bind('selectstart', function(){ return false; });
|
||||
$("#have_read").click(function() {
|
||||
var haveReadForm = $('#have_read_form');
|
||||
if ($("#have_read").find('span').hasClass('glyphicon-ok')) {
|
||||
$("#have_read").find('span').removeClass('glyphicon-ok');
|
||||
$("#have_read").find('span').addClass('glyphicon-remove');
|
||||
} else {
|
||||
$("#have_read").find('span').removeClass('glyphicon-remove');
|
||||
$("#have_read").find('span').addClass('glyphicon-ok');
|
||||
}
|
||||
haveReadForm.submit()
|
||||
});
|
||||
</script>
|
||||
{% endblock %}
|
||||
|
||||
|
||||
|
@ -128,6 +128,10 @@
|
||||
{% if g.user.show_best_rated_books() %}
|
||||
<li><a href="{{url_for('best_rated_books')}}"><span class="glyphicon glyphicon-star"></span> {{_('Best rated Books')}}</a></li>
|
||||
{%endif%}
|
||||
{% if g.user.show_read_and_unread() %}
|
||||
<li><a href="{{url_for('read_books')}}"><span class="glyphicon glyphicon-eye-open"></span> {{_('Read Books')}}</a></li>
|
||||
<li><a href="{{url_for('unread_books')}}"><span class="glyphicon glyphicon-eye-close"></span> {{_('Unread Books')}}</a></li>
|
||||
{%endif%}
|
||||
{% if g.user.show_random_books() %}
|
||||
<li id="nav_rand"><a href="{{url_for('discover')}}"><span class="glyphicon glyphicon-random"></span> {{_('Discover')}}</a></li>
|
||||
{%endif%}
|
||||
|
@ -68,6 +68,10 @@
|
||||
<input type="checkbox" name="show_author" id="show_author" {% if content.show_author() %}checked{% endif %}>
|
||||
<label for="show_author">{{_('Show author selection')}}</label>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<input type="checkbox" name="show_read_and_unread" id="show_read_and_unread" {% if content.show_read_and_unread() %}checked{% endif %}>
|
||||
<label for="show_read_and_unread">{{_('Show read and unread')}}</label>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<input type="checkbox" name="show_detail_random" id="show_detail_random" {% if content.show_detail_random() %}checked{% endif %}>
|
||||
<label for="show_detail_random">{{_('Show random books in detail view')}}</label>
|
||||
|
18
cps/ub.py
18
cps/ub.py
@ -32,6 +32,7 @@ SIDEBAR_HOT = 16
|
||||
SIDEBAR_RANDOM = 32
|
||||
SIDEBAR_AUTHOR = 64
|
||||
SIDEBAR_BEST_RATED = 128
|
||||
SIDEBAR_READ_AND_UNREAD = 256
|
||||
|
||||
DEFAULT_PASS = "admin123"
|
||||
DEFAULT_PORT = int(os.environ.get("CALIBRE_PORT", 8083))
|
||||
@ -138,6 +139,12 @@ class UserBase:
|
||||
else:
|
||||
return False
|
||||
|
||||
def show_read_and_unread(self):
|
||||
if self.sidebar_view is not None:
|
||||
return True if self.sidebar_view & SIDEBAR_READ_AND_UNREAD == SIDEBAR_READ_AND_UNREAD else False
|
||||
else:
|
||||
return False
|
||||
|
||||
def show_detail_random(self):
|
||||
if self.sidebar_view is not None:
|
||||
return True if self.sidebar_view & DETAIL_RANDOM == DETAIL_RANDOM else False
|
||||
@ -218,6 +225,14 @@ class BookShelf(Base):
|
||||
def __repr__(self):
|
||||
return '<Book %r>' % self.id
|
||||
|
||||
class ReadBook(Base):
|
||||
__tablename__ = 'book_read_link'
|
||||
|
||||
id=Column(Integer, primary_key=True)
|
||||
book_id = Column(Integer, unique=False)
|
||||
user_id =Column(Integer, ForeignKey('user.id'), unique=False)
|
||||
is_read = Column(Boolean, unique=False)
|
||||
|
||||
|
||||
# Baseclass representing Downloads from calibre-web in app.db
|
||||
class Downloads(Base):
|
||||
@ -336,6 +351,9 @@ class Config:
|
||||
# everywhere to curent should work. Migration is done by checking if relevant coloums are existing, and than adding
|
||||
# rows with SQL commands
|
||||
def migrate_Database():
|
||||
if not engine.dialect.has_table(engine.connect(), "book_read_link"):
|
||||
ReadBook.__table__.create(bind = engine)
|
||||
|
||||
try:
|
||||
session.query(exists().where(User.locale)).scalar()
|
||||
session.commit()
|
||||
|
86
cps/web.py
86
cps/web.py
@ -953,6 +953,23 @@ def category(id, page):
|
||||
title=_(u"Category: %(name)s", name=name))
|
||||
|
||||
|
||||
@app.route("/ajax/toggleread/<int:id>", methods=['POST'])
|
||||
@login_required
|
||||
def toggle_read(id):
|
||||
book = ub.session.query(ub.ReadBook).filter(ub.and_(ub.ReadBook.user_id == int(current_user.id),
|
||||
ub.ReadBook.book_id == id)).first()
|
||||
if book:
|
||||
book.is_read=not book.is_read
|
||||
else:
|
||||
readBook=ub.ReadBook()
|
||||
readBook.user_id=int(current_user.id)
|
||||
readBook.book_id = id
|
||||
readBook.is_read=True
|
||||
book=readBook
|
||||
ub.session.merge(book)
|
||||
ub.session.commit()
|
||||
return ""
|
||||
|
||||
@app.route("/book/<int:id>")
|
||||
@login_required_if_no_ano
|
||||
def show_book(id):
|
||||
@ -975,8 +992,14 @@ def show_book(id):
|
||||
for entry in shelfs:
|
||||
book_in_shelfs.append(entry.shelf)
|
||||
|
||||
#return render_title_template('detail.html', entry=entries, cc=cc,
|
||||
# title=entries.title, books_shelfs=book_in_shelfs)
|
||||
matching_have_read_book=ub.session.query(ub.ReadBook).filter(ub.and_(ub.ReadBook.user_id == int(current_user.id),
|
||||
ub.ReadBook.book_id == id)).all()
|
||||
have_read=len(matching_have_read_book) > 0 and matching_have_read_book[0].is_read
|
||||
|
||||
return render_title_template('detail.html', entry=entries, cc=cc,
|
||||
title=entries.title, books_shelfs=book_in_shelfs)
|
||||
title=entries.title, books_shelfs=book_in_shelfs, have_read=have_read)
|
||||
else:
|
||||
flash(_(u"Error opening eBook. File does not exist or file is not accessible:"), category="error")
|
||||
return redirect(url_for("index"))
|
||||
@ -1160,8 +1183,61 @@ def get_cover(cover_path):
|
||||
@requires_basic_auth_if_no_ano
|
||||
def feed_get_cover(book_id):
|
||||
book = db.session.query(db.Books).filter(db.Books.id == book_id).first()
|
||||
return send_from_directory(os.path.join(config.config_calibre_dir, book.path), "cover.jpg")
|
||||
#return send_from_directory(os.path.join(config.config_calibre_dir, book.path), "cover.jpg")
|
||||
df=gdriveutils.getFileFromEbooksFolder(book.path, 'cover.jpg', Gdrive.Instance().drive)
|
||||
download_url = df.metadata.get('downloadUrl')
|
||||
resp, content = df.auth.Get_Http_Object().request(download_url)
|
||||
resp=make_response(content)
|
||||
resp.headers['Content-Type']='image/jpeg'
|
||||
return resp
|
||||
|
||||
def render_read_books(page, are_read, as_xml=False):
|
||||
readBooks=ub.session.query(ub.ReadBook).filter(ub.ReadBook.user_id == int(current_user.id)).filter(ub.ReadBook.is_read == True).all()
|
||||
readBookIds=[x.book_id for x in readBooks]
|
||||
if are_read:
|
||||
db_filter = db.Books.id.in_(readBookIds)
|
||||
else:
|
||||
db_filter = ~db.Books.id.in_(readBookIds)
|
||||
|
||||
entries, random, pagination = fill_indexpage(page, db.Books,
|
||||
db_filter, db.Books.timestamp.desc())
|
||||
if as_xml:
|
||||
xml = render_title_template('feed.xml', entries=entries, pagination=pagination)
|
||||
response = make_response(xml)
|
||||
response.headers["Content-Type"] = "application/xml"
|
||||
return response
|
||||
else:
|
||||
name=u'Read Books' if are_read else u'Unread Books'
|
||||
return render_title_template('index.html', random=random, entries=entries, pagination=pagination,
|
||||
title=_(name, name=name))
|
||||
|
||||
@app.route("/opds/readbooks/")
|
||||
@login_required_if_no_ano
|
||||
def feed_read_books():
|
||||
off = request.args.get("offset")
|
||||
if not off:
|
||||
off = 0
|
||||
return render_read_books(int(off) / (int(config.config_books_per_page)) + 1, True, True)
|
||||
|
||||
@app.route("/readbooks/", defaults={'page': 1})
|
||||
@app.route("/readbooks/<int:page>'")
|
||||
@login_required_if_no_ano
|
||||
def read_books(page):
|
||||
return render_read_books(page, True)
|
||||
|
||||
@app.route("/opds/unreadbooks/")
|
||||
@login_required_if_no_ano
|
||||
def feed_unread_books():
|
||||
off = request.args.get("offset")
|
||||
if not off:
|
||||
off = 0
|
||||
return render_read_books(int(off) / (int(config.config_books_per_page)) + 1, False, True)
|
||||
|
||||
@app.route("/unreadbooks/", defaults={'page': 1})
|
||||
@app.route("/unreadbooks/<int:page>'")
|
||||
@login_required_if_no_ano
|
||||
def unread_books(page):
|
||||
return render_read_books(page, False)
|
||||
|
||||
@app.route("/read/<int:book_id>/<format>")
|
||||
@login_required
|
||||
@ -1240,6 +1316,10 @@ def get_download_link(book_id, format):
|
||||
file_name = helper.get_valid_filename(file_name)
|
||||
response = make_response(
|
||||
send_from_directory(os.path.join(config.config_calibre_dir, book.path), data.name + "." + format))
|
||||
df=gdriveutils.getFileFromEbooksFolder(book.path, '%s.%s' % (data.name, format), Gdrive.Instance().drive)
|
||||
download_url = df.metadata.get('downloadUrl')
|
||||
resp, content = df.auth.Get_Http_Object().request(download_url)
|
||||
#response=make_response(content)
|
||||
try:
|
||||
response.headers["Content-Type"] = mimetypes.types_map['.' + format]
|
||||
except:
|
||||
@ -1556,6 +1636,8 @@ def profile():
|
||||
content.sidebar_view += ub.SIDEBAR_BEST_RATED
|
||||
if "show_author" in to_save:
|
||||
content.sidebar_view += ub.SIDEBAR_AUTHOR
|
||||
if "show_read_and_unread" in to_save:
|
||||
content.sidebar_view += ub.SIDEBAR_READ_AND_UNREAD
|
||||
if "show_detail_random" in to_save:
|
||||
content.sidebar_view += ub.DETAIL_RANDOM
|
||||
if "default_language" in to_save:
|
||||
|
Loading…
Reference in New Issue
Block a user