mirror of https://github.com/muk-it/muk_base
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
211 lines
8.9 KiB
211 lines
8.9 KiB
###################################################################################
|
|
#
|
|
# Copyright (C) 2018 MuK IT GmbH
|
|
#
|
|
# This program is free software: you can redistribute it and/or modify
|
|
# it under the terms of the GNU Affero 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 Affero General Public License for more details.
|
|
#
|
|
# You should have received a copy of the GNU Affero General Public License
|
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
#
|
|
###################################################################################
|
|
|
|
import os
|
|
import io
|
|
import sys
|
|
import PyPDF2
|
|
import base64
|
|
import shutil
|
|
import urllib
|
|
import logging
|
|
import tempfile
|
|
import mimetypes
|
|
|
|
from contextlib import closing
|
|
|
|
from odoo.tools import config
|
|
from odoo.tools.mimetypes import guess_mimetype
|
|
|
|
from odoo.addons.muk_utils.tools import utils_os
|
|
from odoo.addons.muk_converter.service import unoconv
|
|
|
|
_logger = logging.getLogger(__name__)
|
|
|
|
try:
|
|
from wand.image import Image
|
|
from wand.color import Color
|
|
except ImportError:
|
|
Image = False
|
|
Color = False
|
|
_logger.warn('Cannot `import wand`.')
|
|
|
|
try:
|
|
import imageio
|
|
except ImportError:
|
|
imageio = False
|
|
_logger.warn('Cannot `import imageio`.')
|
|
|
|
try:
|
|
from moviepy.editor import VideoFileClip
|
|
except ImportError:
|
|
VideoFileClip = False
|
|
_logger.warn('Cannot `import moviepy`.')
|
|
|
|
FORMATS = [
|
|
"png", "jpg", "jepg"
|
|
]
|
|
|
|
VIDEO_IMPORTS = [
|
|
"mp4", "mov", "wav", "avi", "mpg", "flv", "wmv", "webm"
|
|
]
|
|
|
|
PDF_IMPORTS = [
|
|
"pdf"
|
|
]
|
|
|
|
WAND_IMPORTS = [
|
|
"aai", "art", "arw", "avi", "avs", "bpg", "brf", "cals", "cgm", "cin", "cip", "cmyk", "cmyka", "svg",
|
|
"cr2", "crw", "cur", "cut", "dcm", "dcr", "dcx", "dds", "dib", "djvu", "dng", "dot", "dpx", "tim",
|
|
"emf", "epdf", "epi", "eps", "eps2", "eps3", "epsf", "epsi", "ept", "exr", "fax", "fig", "fits",
|
|
"fpx", "gplt", "gray", "graya", "hdr", "hdr", "heic", "hpgl", "hrz", "html", "ico", "info", "ttf",
|
|
"inline", "isobrl", "isobrl6", "jbig", "jng", "jp2", "jpt", "j2c", "j2k", "jxr", "json", "man", "bmp",
|
|
"mat", "miff", "mono", "mng", "m2v", "mpeg", "mpc", "mpr", "mrw", "msl", "mtv", "mvg", "nef", "yuv",
|
|
"orf", "otb", "p7", "palm", "pam", "clipboard", "pbm", "pcd", "pcds", "pcl", "pcx", "pdb", "jpe",
|
|
"pef", "pes", "pfa", "pfb", "pfm", "pgm", "picon", "pict", "pix", "png8", "png00", "png24", "tiff",
|
|
"png32", "png48", "png64", "pnm", "ppm", "ps", "ps2", "ps3", "psb", "psd", "ptif", "pwp", "rad",
|
|
"raf", "rgb", "rgba", "rgf", "rla", "rle", "sct", "sfw", "sgi", "shtml", "sid", " mrsid", "jpeg",
|
|
"sparse-color", "sun", "tga", "ubrl", "ubrl6", "uyvy", "vicar", "viff", "wbmp", "jpg", "png", "uil",
|
|
"wdp", "webp", "wmf", "wpg", "x", "xbm", "xcf", "xpm", "xwd", "x3f", "ycbcr", "ycbcra", "bmp3", "bmp2",
|
|
]
|
|
|
|
def formats():
|
|
return FORMATS
|
|
|
|
def imports():
|
|
return VIDEO_IMPORTS + PDF_IMPORTS + WAND_IMPORTS + unoconv.IMPORTS
|
|
|
|
def create_thumbnail(binary, mimetype=None, filename=None, export="binary", format="png", page=0, frame=0,
|
|
animation=False, video_resize={'width': 256}, image_resize='256x256>', image_crop=None):
|
|
"""
|
|
Converts a thumbnail for a given file.
|
|
|
|
:param binary: The binary value.
|
|
:param mimetype: The mimetype of the binary value.
|
|
:param filename: The filename of the binary value.
|
|
:param export: The output format (binary, file, base64).
|
|
:param format: Specify the output format for the document.
|
|
:param page: Specifies the page if the file has several pages, e.g. if it is a PDF file.
|
|
:param frame: Specifies the frame if the file has several frames, e.g. if it is a video file.
|
|
:param animation: In this case, the parameter frame specifies the number of frames.
|
|
:param video_resize: Specify to resize the output image.
|
|
:param image_resize: Specify to resize the output image.
|
|
:param image_crop: Specify to crop the output image.
|
|
:return: Returns the output depending on the given format.
|
|
:raises ValueError: The file extension could not be determined or the format is invalid.
|
|
"""
|
|
extension = utils_os.get_extension(binary, filename, mimetype)
|
|
if not extension:
|
|
raise ValueError("The file extension could not be determined.")
|
|
if format not in FORMATS:
|
|
raise ValueError("Invalid export format.")
|
|
if extension not in (VIDEO_IMPORTS + PDF_IMPORTS + WAND_IMPORTS + unoconv.IMPORTS):
|
|
raise ValueError("Invalid import format.")
|
|
if not imageio or not Image or not VideoFileClip:
|
|
raise ValueError("Some libraries couldn't be imported.")
|
|
image_data = None
|
|
image_extension = extension
|
|
if extension in WAND_IMPORTS:
|
|
image_data = binary
|
|
elif not image_data and (extension in PDF_IMPORTS or extension in unoconv.IMPORTS):
|
|
pdf_data = binary if extension in PDF_IMPORTS else None
|
|
if not pdf_data:
|
|
image_extension = "pdf"
|
|
pdf_data = unoconv.convert_binary(binary, mimetype, filename)
|
|
reader = PyPDF2.PdfFileReader(io.BytesIO(pdf_data))
|
|
writer = PyPDF2.PdfFileWriter()
|
|
if reader.getNumPages() >= page:
|
|
writer.addPage(reader.getPage(page))
|
|
else:
|
|
writer.addPage(reader.getPage(0))
|
|
pdf_bytes = io.BytesIO()
|
|
writer.write(pdf_bytes)
|
|
image_data = pdf_bytes.getvalue()
|
|
if image_data:
|
|
with Image(blob=image_data, format=image_extension) as thumbnail:
|
|
thumbnail.format = format
|
|
if image_extension == "pdf":
|
|
thumbnail.background_color = Color('white')
|
|
thumbnail.alpha_channel = 'remove'
|
|
if image_resize:
|
|
thumbnail.transform(resize=image_resize)
|
|
if image_crop:
|
|
thumbnail.transform(crop=image_crop)
|
|
if export == 'file':
|
|
return io.BytesIO(thumbnail.make_blob())
|
|
elif export == 'base64':
|
|
return base64.b64encode(thumbnail.make_blob())
|
|
else:
|
|
return thumbnail.make_blob()
|
|
elif extension in VIDEO_IMPORTS:
|
|
tmp_dir = tempfile.mkdtemp()
|
|
try:
|
|
tmp_wpath = os.path.join(tmp_dir, "tmpfile.%s" % extension)
|
|
if os.name == 'nt':
|
|
tmp_wpath = tmp_wpath.replace("\\", "/")
|
|
with closing(open(tmp_wpath, 'wb')) as file:
|
|
file.write(binary)
|
|
clip = VideoFileClip(tmp_wpath)
|
|
try:
|
|
tmp_opath = os.path.join(tmp_dir, "output.%s" % format)
|
|
clip.resize(**video_resize)
|
|
if animation:
|
|
files = []
|
|
current_frame = 0
|
|
while clip.duration > current_frame and current_frame < frame:
|
|
filename = os.path.join(tmp_dir, "output_%s.png" % frame)
|
|
clip.save_frame(filename, t=frame)
|
|
files.append(filename)
|
|
frame += 0.25
|
|
tmp_opath = os.path.join(tmp_dir, "output.gif")
|
|
with imageio.get_writer(tmp_opath, fps=5, mode='I') as writer:
|
|
for filename in files:
|
|
image = imageio.imread(filename)
|
|
writer.append_data(image)
|
|
elif clip.duration > int(frame):
|
|
clip.save_frame(tmp_opath, t=int(frame))
|
|
else:
|
|
clip.save_frame(tmp_opath, t=int(0))
|
|
if os.path.isfile(tmp_opath):
|
|
with open(tmp_opath, 'rb') as file:
|
|
if export == 'file':
|
|
return io.BytesIO(file.read())
|
|
elif export == 'base64':
|
|
return base64.b64encode(file.read())
|
|
else:
|
|
return file.read()
|
|
else:
|
|
raise ValueError("No output could be created from the video.")
|
|
finally:
|
|
try:
|
|
clip.reader.close()
|
|
del clip.reader
|
|
if clip.audio != None:
|
|
clip.audio.reader.close_proc()
|
|
del clip.audio
|
|
del clip
|
|
except Exception as e:
|
|
sys.exc_clear()
|
|
finally:
|
|
try:
|
|
shutil.rmtree(tmp_dir)
|
|
except PermissionError:
|
|
_logger.warn("Temporary directory could not be deleted.")
|
|
else:
|
|
raise ValueError("No output could be generated.")
|