Fixes for Kobo sync

Better output on upload cover
Fix for download after access to opds/fileformat
Fix osd search link
Added ratings to opds feed
Change for kobo sync for testing
This commit is contained in:
Ozzieisaacs 2020-03-07 11:07:35 +01:00
parent fb83bfb363
commit d267338837
7 changed files with 88 additions and 60 deletions

View file

@ -369,11 +369,11 @@ def upload_cover(request, book):
requested_file = request.files['btn-upload-cover']
# check for empty request
if requested_file.filename != '':
if helper.save_cover(requested_file, book.path) is True:
ret, message = helper.save_cover(requested_file, book.path)
if ret is True:
return True
else:
# ToDo Message not always coorect
flash(_(u"Cover is not a supported imageformat (jpg/png/webp), can't save"), category="error")
flash(message, category="error")
return False
return None

View file

@ -508,16 +508,16 @@ def save_cover_from_filestorage(filepath, saved_filename, img):
os.makedirs(filepath)
except OSError:
log.error(u"Failed to create path for cover")
return False
return False, _(u"Failed to create path for cover")
try:
img.save(os.path.join(filepath, saved_filename))
except IOError:
log.error(u"Cover-file is not a valid image file")
return False
return False, _(u"Cover-file is not a valid image file")
except OSError:
log.error(u"Failed to store cover-file")
return False
return True
return False, _(u"Failed to store cover-file")
return True, None
# saves book cover to gdrive or locally
@ -527,7 +527,7 @@ def save_cover(img, book_path):
if use_PIL:
if content_type not in ('image/jpeg', 'image/png', 'image/webp'):
log.error("Only jpg/jpeg/png/webp files are supported as coverfile")
return False
return False, _("Only jpg/jpeg/png/webp files are supported as coverfile")
# convert to jpg because calibre only supports jpg
if content_type in ('image/png', 'image/webp'):
if hasattr(img,'stream'):
@ -541,17 +541,18 @@ def save_cover(img, book_path):
else:
if content_type not in ('image/jpeg'):
log.error("Only jpg/jpeg files are supported as coverfile")
return False
return False, _("Only jpg/jpeg files are supported as coverfile")
if config.config_use_google_drive:
tmpDir = gettempdir()
if save_cover_from_filestorage(tmpDir, "uploaded_cover.jpg", img) is True:
ret, message = save_cover_from_filestorage(tmpDir, "uploaded_cover.jpg", img)
if ret is True:
gd.uploadFileToEbooksFolder(os.path.join(book_path, 'cover.jpg'),
os.path.join(tmpDir, "uploaded_cover.jpg"))
log.info("Cover is saved on Google Drive")
return True
return True, None
else:
return False
return False, message
else:
return save_cover_from_filestorage(os.path.join(config.config_calibre_dir, book_path), "cover.jpg", img)
@ -818,11 +819,11 @@ def get_download_link(book_id, book_format):
book_format = book_format.split(".")[0]
book = db.session.query(db.Books).filter(db.Books.id == book_id).filter(common_filters()).first()
if book:
data = db.session.query(db.Data).filter(db.Data.book == book.id)\
data1 = db.session.query(db.Data).filter(db.Data.book == book.id)\
.filter(db.Data.format == book_format.upper()).first()
else:
abort(404)
if data:
if data1:
# collect downloaded books only for registered user and not for anonymous user
if current_user.is_authenticated:
ub.update_download(book_id, int(current_user.id))
@ -834,7 +835,7 @@ def get_download_link(book_id, book_format):
headers["Content-Type"] = mimetypes.types_map.get('.' + book_format, "application/octet-stream")
headers["Content-Disposition"] = "attachment; filename=%s.%s; filename*=UTF-8''%s.%s" % (
quote(file_name.encode('utf-8')), book_format, quote(file_name.encode('utf-8')), book_format)
return do_download_file(book, book_format, data, headers)
return do_download_file(book, book_format, data1, headers)
else:
abort(404)

View file

@ -214,29 +214,19 @@ def HandleMetadataRequest(book_uuid):
def get_download_url_for_book(book, book_format):
if not current_app.wsgi_app.is_proxied:
if request.environ['SERVER_NAME'] != '::':
return "{url_scheme}://{url_base}:{url_port}/download/{book_id}/{book_format}".format(
url_scheme=request.environ['wsgi.url_scheme'],
url_base=request.environ['SERVER_NAME'],
url_port=config.config_port,
book_id=book.id,
book_format=book_format.lower()
)
else:
return "{url_scheme}://{url_base}:{url_port}/download/{book_id}/{book_format}".format(
url_scheme=request.environ['wsgi.url_scheme'],
url_base=request.host, # ToDo: both server ??
url_port=config.config_port,
book_id=book.id,
book_format=book_format.lower()
)
else:
return url_for(
"web.download_link",
return "{url_scheme}://{url_base}:{url_port}/download/{book_id}/{book_format}".format(
url_scheme=request.scheme,
url_base=request.host,
url_port=config.config_port,
book_id=book.id,
book_format=book_format.lower(),
_external=True,
book_format=book_format.lower()
)
return url_for(
"web.download_link",
book_id=book.id,
book_format=book_format.lower(),
_external=True,
)
def create_book_entitlement(book):
@ -466,15 +456,11 @@ def HandleInitRequest():
if not current_app.wsgi_app.is_proxied:
log.debug('Kobo: Received unproxied request, changed request port to server port')
if request.environ['SERVER_NAME'] != '::':
calibre_web_url = "{url_scheme}://{url_base}:{url_port}".format(
url_scheme=request.environ['wsgi.url_scheme'],
url_base=request.environ['SERVER_NAME'],
url_port=config.config_port
)
else:
log.debug('Kobo: Received unproxied request, on IPV6 host')
calibre_web_url = url_for("web.index", _external=True).strip("/")
calibre_web_url = "{url_scheme}://{url_base}:{url_port}".format(
url_scheme=request.scheme,
url_base=request.host,
url_port=config.config_port
)
else:
calibre_web_url = url_for("web.index", _external=True).strip("/")

View file

@ -56,6 +56,20 @@ def requires_basic_auth_if_no_ano(f):
return decorated
class FeedObject():
def __init__(self,rating_id , rating_name):
self.rating_id = rating_id
self.rating_name = rating_name
@property
def id(self):
return self.rating_id
@property
def name(self):
return self.rating_name
@opds.route("/opds/")
@opds.route("/opds")
@requires_basic_auth_if_no_ano
@ -214,6 +228,31 @@ def feed_series(book_id):
db.Books, db.Books.series.any(db.Series.id == book_id), [db.Books.series_index])
return render_xml_template('feed.xml', entries=entries, pagination=pagination)
@opds.route("/opds/ratings")
@requires_basic_auth_if_no_ano
def feed_ratingindex():
off = request.args.get("offset") or 0
entries = db.session.query(db.Ratings, func.count('books_ratings_link.book').label('count'),
(db.Ratings.rating / 2).label('name')) \
.join(db.books_ratings_link).join(db.Books).filter(common_filters()) \
.group_by(text('books_ratings_link.rating')).order_by(db.Ratings.rating).all()
pagination = Pagination((int(off) / (int(config.config_books_per_page)) + 1), config.config_books_per_page,
len(entries))
element = list()
for entry in entries:
element.append(FeedObject(entry[0].id, "{} Stars".format(entry.name)))
return render_xml_template('feed.xml', listelements=element, folder='opds.feed_ratings', pagination=pagination)
@opds.route("/opds/ratings/<book_id>")
@requires_basic_auth_if_no_ano
def feed_ratings(book_id):
off = request.args.get("offset") or 0
entries, __, pagination = fill_indexpage((int(off) / (int(config.config_books_per_page)) + 1),
db.Books, db.Books.ratings.any(db.Ratings.id == book_id),[db.Books.timestamp.desc()])
return render_xml_template('feed.xml', entries=entries, pagination=pagination)
@opds.route("/opds/formats")
@requires_basic_auth_if_no_ano
def feed_formatindex():
@ -222,10 +261,11 @@ def feed_formatindex():
.group_by(db.Data.format).order_by(db.Data.format).all()
pagination = Pagination((int(off) / (int(config.config_books_per_page)) + 1), config.config_books_per_page,
len(entries))
element = list()
for entry in entries:
entry.name = entry.format
entry.id = entry.format
return render_xml_template('feed.xml', listelements=entries, folder='opds.feed_format', pagination=pagination)
element.append(FeedObject(entry.format, entry.format))
return render_xml_template('feed.xml', listelements=element, folder='opds.feed_format', pagination=pagination)
@opds.route("/opds/formats/<book_id>")
@ -265,16 +305,9 @@ def feed_languages(book_id):
off = request.args.get("offset") or 0
entries, __, pagination = fill_indexpage((int(off) / (int(config.config_books_per_page)) + 1),
db.Books, db.Books.languages.any(db.Languages.id == book_id), [db.Books.timestamp.desc()])
'''for entry in entries:
for index in range(0, len(entry.languages)):
try:
entry.languages[index].language_name = LC.parse(entry.languages[index].lang_code).get_language_name(
get_locale())
except UnknownLocaleError:
entry.languages[index].language_name = _(
isoLanguages.get(part3=entry.languages[index].lang_code).name)'''
return render_xml_template('feed.xml', entries=entries, pagination=pagination)
@opds.route("/opds/shelfindex", defaults={'public': 0})
@opds.route("/opds/shelfindex/<string:public>")
@requires_basic_auth_if_no_ano
@ -319,11 +352,11 @@ def feed_shelf(book_id):
@requires_basic_auth_if_no_ano
@download_required
def opds_download_link(book_id, book_format):
return get_download_link(book_id,book_format)
return get_download_link(book_id,book_format.lower())
@opds.route("/ajax/book/<string:uuid>/<library>")
@opds.route("/ajax/book/<string:uuid>")
@opds.route("/ajax/book/<string:uuid>",defaults={'library': ""})
@requires_basic_auth_if_no_ano
def get_metadata_calibre_companion(uuid, library):
entry = db.session.query(db.Books).filter(db.Books.uuid.like("%" + uuid + "%")).first()

View file

@ -92,6 +92,14 @@
<updated>{{ current_time }}</updated>
<content type="text">{{_('Books ordered by Languages')}}</content>
</entry>
<entry>
<title>{{_('Ratings')}}</title>
<link href="{{url_for('opds.feed_ratingindex')}}" type="application/atom+xml;profile=opds-catalog"/>
<id>{{url_for('opds.feed_ratingindex')}}</id>
<updated>{{ current_time }}</updated>
<content type="text">{{_('Books ordered by Rating')}}</content>
</entry>
<entry>
<title>{{_('File formats')}}</title>
<link href="{{url_for('opds.feed_formatindex')}}" type="application/atom+xml;profile=opds-catalog"/>

View file

@ -6,7 +6,7 @@
<Developer>Janeczku</Developer>
<Contact>https://github.com/janeczku/calibre-web</Contact>
<Url type="text/html"
template="{{url_for('opds.feed_cc_search')}}{searchTerms}"/>
template="{{url_for('opds.feed_cc_search')}}/{searchTerms}"/>
<Url type="application/atom+xml"
template="{{url_for('opds.feed_normal_search')}}?query={searchTerms}"/>
<SyndicationRight>open</SyndicationRight>

View file

@ -1044,7 +1044,7 @@ def serve_book(book_id, book_format, anyname):
@login_required_if_no_ano
@download_required
def download_link(book_id, book_format):
return get_download_link(book_id, book_format)
return get_download_link(book_id, book_format.lower())
@web.route('/send/<int:book_id>/<book_format>/<int:convert>')