Merge branch 'main' into deldesir-youtube-download

This commit is contained in:
Blondel MONDESIR 2023-09-26 10:18:25 -04:00 committed by GitHub
commit d522e0a1ca
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 176 additions and 13 deletions

View File

@ -142,14 +142,17 @@ except ValueError:
del env_CALIBRE_PORT
EXTENSIONS_AUDIO = {'mp3', 'mp4', 'ogg', 'opus', 'wav', 'flac', 'm4a', 'm4b'}
EXTENSIONS_AUDIO = {'mp3', 'ogg', 'opus', 'wav', 'flac', 'm4a', 'm4b'}
EXTENSIONS_VIDEO = {'mp4', 'webm', 'avi', 'mkv', 'm4v', 'mpg', 'mpeg', 'ogv'}
EXTENSIONS_IMAGE = {'jpg', 'jpeg', 'png', 'gif', 'svg', 'webp'}
EXTENSIONS_CONVERT_FROM = ['pdf', 'epub', 'mobi', 'azw3', 'docx', 'rtf', 'fb2', 'lit', 'lrf',
'txt', 'htmlz', 'rtf', 'odt', 'cbz', 'cbr']
EXTENSIONS_CONVERT_TO = ['pdf', 'epub', 'mobi', 'azw3', 'docx', 'rtf', 'fb2',
'lit', 'lrf', 'txt', 'htmlz', 'rtf', 'odt']
EXTENSIONS_UPLOAD = {'txt', 'pdf', 'epub', 'kepub', 'mobi', 'azw', 'azw3', 'cbr', 'cbz', 'cbt', 'cb7', 'djvu', 'djv',
'prc', 'doc', 'docx', 'fb2', 'html', 'rtf', 'lit', 'odt', 'mp3', 'mp4', 'ogg',
'opus', 'wav', 'flac', 'm4a', 'm4b'}
'opus', 'wav', 'flac', 'm4a', 'm4b', "avi", "mkv", "webm", "m4v", "mov", "wmv", "mpg",
"mpeg", "flv", "3gp", "3g2", "ogv", "jpg", "jpeg", "png", "gif", "svg", "webp"}
def has_flag(value, bit_flag):

View File

@ -21,8 +21,10 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>.
import os
import smartcrop
from datetime import datetime
import json
from PIL import Image
from shutil import copyfile
from uuid import uuid4
from markupsafe import escape # dependency of flask
@ -972,9 +974,19 @@ def move_coverfile(meta, db_book):
else:
cover_file = os.path.join(constants.STATIC_DIR, 'generic_cover.jpg')
new_cover_path = os.path.join(config.config_calibre_dir, db_book.path)
try:
os.makedirs(new_cover_path, exist_ok=True)
copyfile(cover_file, os.path.join(new_cover_path, "cover.jpg"))
image = Image.open(cover_file)
# crop image to square 150x150 using smartcrop
cropper = smartcrop.SmartCrop()
result = cropper.crop(image, 150, 150)
x, y, width, height = result['top_crop']['x'], result['top_crop']['y'], \
result['top_crop']['width'], result['top_crop']['height']
cropped_image = image.crop((x, y, x + width, y + height))
cropped_image.save(os.path.join(new_cover_path, "cover.jpg"))
if meta.cover:
os.unlink(meta.cover)
except OSError as e:

View File

@ -1,4 +1,3 @@
.tooltip.bottom .tooltip-inner {
font-size: 13px;
font-family: Open Sans Semibold, Helvetica Neue, Helvetica, Arial, sans-serif;
@ -140,15 +139,36 @@ table .bg-dark-danger a { color: #fff; }
.container-fluid .book {
margin-top: 20px;
max-width: 180px;
display: flex;
flex-direction: column;
margin-right: 10px;
max-width: 150px;
display: inline-block;
vertical-align: top;
box-sizing: border-box;
text-align: center;
}
.container-fluid .book:last-child {
margin-right: 0;
}
.cover { margin-bottom: 10px; }
.container-fluid .book .cover {
height: 225px;
width: 150px;
height: 150px;
position: relative;
overflow: hidden;
border: 1px solid #ddd;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
}
.container-fluid .book .cover img {
height: 100%;
width: auto;
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
}
.author-link img {
@ -437,4 +457,4 @@ div.log {
.error-list {
margin-top: 5px;
}
}

View File

@ -120,7 +120,61 @@
<a target="_blank" href="{{ url_for('web.read_book', book_id=entry.id, book_format=entry.audio_entries[0]) }}" id="listenbtn" class="btn btn-primary" role="button"><span class="glyphicon glyphicon-music"></span> {{ _('Listen in Browser') }} - {{ entry.audio_entries[0] }}</a>
{% endif %}
</div>
{% endif %}
{% elif entry.video_entries|length > 0 and current_user.role_viewer() %}
<div class="btn-group" role="group">
{% if entry.video_entries|length > 1 %}
<button id="listen-in-browser" type="button" class="btn btn-primary dropdown-toggle" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
<span class="glyphicon glyphicon-film"></span> {{ _('Watch in Browser') }}
<span class="caret"></span>
</button>
<ul class="dropdown-menu" aria-labelledby="listen-in-browser">
{% for format in entry.reader_list %}
<li><a target="_blank" href="{{ url_for('web.read_book', book_id=entry.id, book_format=format) }}">{{ format }}</a>
</li>
{% endfor %}
</ul>
<ul class="dropdown-menu" aria-labelledby="listen-in-browser">
{% for format in entry.data %}
{% if format.format|lower in entry.video_entries %}
<li><a target="_blank"
href="{{ url_for('web.read_book', book_id=entry.id, book_format=format.format|lower) }}">{{ format.format|lower }}</a>
</li>
{% endif %}
{% endfor %}
</ul>
{% else %}
<a target="_blank" href="{{ url_for('web.read_book', book_id=entry.id, book_format=entry.video_entries[0]) }}" id="listenbtn" class="btn btn-primary" role="button"><span class="glyphicon glyphicon-film"></span> {{ _('Watch in Browser') }} - {{ entry.video_entries[0] }}</a>
{% endif %}
</div>
{% elif entry.image_entries|length > 0 and current_user.role_viewer() %}
<div class="btn-group" role="group">
{% if entry.image_entries|length > 1 %}
<button id="listen-in-browser" type="button" class="btn btn-primary dropdown-toggle" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
<span class="glyphicon glyphicon-picture"></span> {{ _('View in Browser') }}
<span class="caret"></span>
</button>
<ul class="dropdown-menu" aria-labelledby="listen-in-browser">
{% for format in entry.reader_list %}
<li><a target="_blank" href="{{ url_for('web.read_book', book_id=entry.id, book_format=format) }}">{{ format }}</a>
</li>
{% endfor %}
</ul>
<ul class="dropdown-menu" aria-labelledby="listen-in-browser">
{% for format in entry.data %}
{% if format.format|lower in entry.image_entries %}
<li><a target="_blank"
href="{{ url_for('web.read_book', book_id=entry.id, book_format=format.format|lower) }}">{{ format.format|lower }}</a>
</li>
{% endif %}
{% endfor %}
</ul>
{% else %}
<a target="_blank" href="{{ url_for('web.read_book', book_id=entry.id, book_format=entry.image_entries[0]) }}" id="listenbtn" class="btn btn-primary" role="button"><span class="glyphicon glyphicon-picture"></span> {{ _('View in Browser') }} - {{ entry.image_entries[0] }}</a>
{% endif %}
</div>
{% endif %}
</div>
</div>
<h2 id="title">{{ entry.title }}</h2>

View File

@ -25,7 +25,7 @@ from tempfile import gettempdir
from flask_babel import gettext as _
from . import logger, comic, isoLanguages
from .constants import BookMeta
from .constants import BookMeta, EXTENSIONS_IMAGE, EXTENSIONS_VIDEO
from .helper import split_authors
log = logger.create()
@ -89,7 +89,6 @@ def process(tmp_file_path, original_file_name, original_file_extension, rar_exec
rar_executable)
elif extension_upper in ['.MP4', '.WEBM', '.AVI', '.MKV', '.M4V', '.MPG', '.MPEG','.OGV']:
meta = video_metadata(tmp_file_path, original_file_name, original_file_extension)
elif extension_upper in ['.JPG', '.JPEG', '.PNG', '.GIF', '.SVG', '.WEBP']:
meta = image_metadata(tmp_file_path, original_file_name, original_file_extension)
@ -217,7 +216,7 @@ def pdf_meta(tmp_file_path, original_file_name, original_file_extension):
cover=pdf_preview(tmp_file_path, original_file_name),
description=subject,
tags=tags,
series="",
series="",Deldesir y
series_id="",
languages=','.join(languages),
publisher=publisher,
@ -247,6 +246,63 @@ def pdf_preview(tmp_file_path, tmp_dir):
log.warning('On Windows this error could be caused by missing ghostscript')
return None
def video_metadata(tmp_file_path, original_file_name, original_file_extension):
video_cover(tmp_file_path)
meta = BookMeta(
file_path=tmp_file_path,
extension=original_file_extension,
title=original_file_name,
author='Unknown',
cover=os.path.splitext(tmp_file_path)[0] + '.cover.jpg',
description='',
tags='',
series="",
series_id="",
languages="",
publisher="",
pubdate="",
identifiers=[])
return meta
def video_cover(tmp_file_path):
"""generate cover image from video using ffmpeg"""
ffmpeg_executable = os.getenv('FFMPEG_PATH', 'ffmpeg')
ffmpeg_output_file = os.path.splitext(tmp_file_path)[0] + '.cover.jpg'
ffmpeg_args = [
ffmpeg_executable,
'-i', tmp_file_path,
'-vframes', '1',
'-y', ffmpeg_output_file
]
try:
ffmpeg_result = run(ffmpeg_args, capture_output=True, check=True)
log.debug(f"ffmpeg output: {ffmpeg_result.stdout}")
except Exception as e:
log.warning(f"ffmpeg failed: {e}")
return None
def image_metadata(tmp_file_path, original_file_name, original_file_extension):
shutil.copyfile(tmp_file_path, os.path.splitext(tmp_file_path)[0] + '.cover.jpg')
meta = BookMeta(
file_path=tmp_file_path,
extension=original_file_extension,
title=original_file_name,
author='Unknown',
cover=os.path.splitext(tmp_file_path)[0] + '.cover.jpg',
description='',
tags='',
series="",
series_id="",
languages="",
publisher="",
pubdate="",
identifiers=[])
return meta
def video_metadata(tmp_file_path, original_file_name, original_file_extension):
video_id = os.path.splitext(original_file_name)[0][:11]

View File

@ -1559,6 +1559,16 @@ def read_book(book_id, book_format):
log.debug("Start mp3 listening for %d", book_id)
return render_title_template('listenmp3.html', mp3file=book_id, audioformat=book_format.lower(),
entry=entries, bookmark=bookmark)
for fileExt in constants.EXTENSIONS_IMAGE:
if book_format.lower() == fileExt:
entries = calibre_db.get_filtered_book(book_id)
log.debug("Start image viewing for %d", book_id)
return serve_book.__closure__[0].cell_contents(book_id, book_format.lower(), anyname="")
for fileExt in constants.EXTENSIONS_VIDEO:
if book_format.lower() == fileExt:
entries = calibre_db.get_filtered_book(book_id)
log.debug("Start video watching for %d", book_id)
return serve_book.__closure__[0].cell_contents(book_id, book_format.lower(), anyname="")
for fileExt in ["cbr", "cbt", "cbz"]:
if book_format.lower() == fileExt:
all_name = str(book_id)
@ -1603,9 +1613,15 @@ def show_book(book_id):
entry.reader_list = check_read_formats(entry)
entry.audio_entries = []
entry.video_entries = []
entry.image_entries = []
for media_format in entry.data:
if media_format.format.lower() in constants.EXTENSIONS_AUDIO:
entry.audio_entries.append(media_format.format.lower())
if media_format.format.lower() in constants.EXTENSIONS_VIDEO:
entry.video_entries.append(media_format.format.lower())
if media_format.format.lower() in constants.EXTENSIONS_IMAGE:
entry.image_entries.append(media_format.format.lower())
return render_title_template('detail.html',
entry=entry,

View File

@ -17,3 +17,5 @@ flask-wtf>=0.14.2,<1.2.0
chardet>=3.0.0,<4.1.0
advocate>=1.0.0,<1.1.0
Flask-Limiter>=2.3.0,<3.4.0
smartcrop>=0.4.0
Pillow>=10.0.0