mirror of https://github.com/muk-it/muk_base
MuK IT GmbH
6 years ago
3 changed files with 390 additions and 390 deletions
-
2muk_thumbnails/__manifest__.py
-
356muk_thumbnails/doc/index.rst
-
422muk_thumbnails/service/thumbnail.py
@ -1,178 +1,178 @@ |
|||
============== |
|||
MuK Thumbnails |
|||
============== |
|||
|
|||
Technical module to provide some utility features and libraries that can be used |
|||
in other applications. This module has no direct effect on the running system. |
|||
|
|||
Requirements |
|||
============= |
|||
|
|||
Unoconv |
|||
------------- |
|||
|
|||
Universal Office Converter (unoconv) is a command line tool to convert any |
|||
document format that LibreOffice can import to any document format that |
|||
LibreOffice can export. It makes use of the LibreOffice's UNO bindings for |
|||
non-interactive conversion of documents. |
|||
|
|||
To install unoconv please follow the instructions (`here <https://github.com/dagwieers/unoconv>`_). |
|||
|
|||
Make sure that unoconv can be executed from the console and the conversion |
|||
is done correctly. |
|||
|
|||
To set an individual path for the LibreOffice installation, the config |
|||
variable ``uno_path`` can be set accordingly in the Odoo config. |
|||
|
|||
Under Windows you should rename the ``unoconv`` file to ``unoconv.py`` and set |
|||
the corresponding path for the ``uno_path`` variable. Since it does not work |
|||
reliably with the environment variable ``UNO_PATH``. |
|||
|
|||
FFmpeg |
|||
------------- |
|||
|
|||
FFmpeg is a complete, cross-platform solution to record, convert and stream audio and video. |
|||
|
|||
To install FFmpeg please follow the instructions (`here <https://www.ffmpeg.org/download.html>`_). |
|||
|
|||
Ghostscript |
|||
------------- |
|||
|
|||
Ghostscript is a suite of software based on an interpreter for Adobe Systems PostScript and |
|||
Portable Document Format (PDF) page description languages. Its main purposes are the |
|||
rasterization or rendering of such page description language files, for the display or printing |
|||
of document pages, and the conversion between PostScript and PDF files. |
|||
|
|||
To install Ghostscript please follow the instructions (`here <https://www.ghostscript.com/download.html>`_). |
|||
|
|||
ImageMagick |
|||
------------- |
|||
|
|||
ImageMagick can be used to create, edit, compose, or convert bitmap images. It can read and write |
|||
images in a variety of formats (over 200) including PNG, JPEG, GIF, HEIC, TIFF, DPX, EXR, WebP, |
|||
Postscript, PDF, and SVG. Use ImageMagick to resize, flip, mirror, rotate, distort, shear and |
|||
transform images, adjust image colors, apply various special effects, or draw text, lines, polygons, |
|||
ellipses and Bezier curves. |
|||
|
|||
To install ImageMagick please follow the instructions (`here <https://www.imagemagick.org/script/download.php>`_). |
|||
|
|||
Wand |
|||
------------- |
|||
|
|||
Wand is a ctypes-based simple ImageMagick binding for Python. It is used to interact with ImageMagick via a Python interface. |
|||
|
|||
To install Wand please follow the instructions (`here <http://docs.wand-py.org/en/0.4.1/guide/install.html>`_). |
|||
|
|||
Imageio |
|||
------------- |
|||
|
|||
Imageio is a Python library that provides an easy interface to read and write a wide range of image data, including animated images, volumetric data, and scientific formats. |
|||
|
|||
To install Imageio please follow the instructions (`here <https://pypi.org/project/imageio/>`_). |
|||
|
|||
MoviePy |
|||
------------- |
|||
|
|||
MoviePy is a Python module for video editing, which can be used for basic operations (like cuts, concatenations, title insertions), video compositing (a.k.a. non-linear editing), video processing, or to create advanced effects. It can read and write the most common video formats, including GIF. |
|||
|
|||
To install MoviePy please follow the instructions (`here <https://zulko.github.io/moviepy/install.html>`_). |
|||
|
|||
Installation |
|||
============ |
|||
|
|||
To install this module, you need to: |
|||
|
|||
Download the module and add it to your Odoo addons folder. Afterward, log on to |
|||
your Odoo server and go to the Apps menu. Trigger the debug mode and update the |
|||
list by clicking on the "Update Apps List" link. Now install the module by |
|||
clicking on the install button. |
|||
|
|||
Another way to install this module is via the package management for Python |
|||
(`PyPI <https://pypi.org/project/pip/>`_). |
|||
|
|||
To install our modules using the package manager make sure |
|||
`odoo-autodiscover <https://pypi.org/project/odoo-autodiscover/>`_ is installed |
|||
correctly. Then open a console and install the module by entering the following |
|||
command: |
|||
|
|||
``pip install --extra-index-url https://nexus.mukit.at/repository/odoo/simple <module>`` |
|||
|
|||
The module name consists of the Odoo version and the module name, where |
|||
underscores are replaced by a dash. |
|||
|
|||
**Module:** |
|||
|
|||
``odoo<version>-addon-<module_name>`` |
|||
|
|||
**Example:** |
|||
|
|||
``sudo -H pip3 install --extra-index-url https://nexus.mukit.at/repository/odoo/simple odoo11-addon-muk-utils`` |
|||
|
|||
Once the installation has been successfully completed, the app is already in the |
|||
correct folder. Log on to your Odoo server and go to the Apps menu. Trigger the |
|||
debug mode and update the list by clicking on the "Update Apps List" link. Now |
|||
install the module by clicking on the install button. |
|||
|
|||
The biggest advantage of this variant is that you can now also update the app |
|||
using the "pip" command. To do this, enter the following command in your console: |
|||
|
|||
``pip install --upgrade --extra-index-url https://nexus.mukit.at/repository/odoo/simple <module>`` |
|||
|
|||
When the process is finished, restart your server and update the application in |
|||
Odoo. The steps are the same as for the installation only the button has changed |
|||
from "Install" to "Upgrade". |
|||
|
|||
You can also view available Apps directly in our `repository <https://nexus.mukit.at/#browse/browse:odoo>`_ |
|||
and find a more detailed installation guide on our `website <https://mukit.at/page/open-source>`_. |
|||
|
|||
For modules licensed under OPL-1, you will receive access data when you purchase |
|||
the module. If the modules were not purchased directly from |
|||
`MuK IT <https://www.mukit.at/>`_ please contact our support (support@mukit.at) |
|||
with a confirmation of purchase to receive the corresponding access data. |
|||
|
|||
Upgrade |
|||
============ |
|||
|
|||
To upgrade this module, you need to: |
|||
|
|||
Download the module and add it to your Odoo addons folder. Restart the server |
|||
and log on to your Odoo server. Select the Apps menu and upgrade the module by |
|||
clicking on the upgrade button. |
|||
|
|||
If you installed the module using the "pip" command, you can also update the |
|||
module in the same way. Just type the following command into the console: |
|||
|
|||
``pip install --upgrade --extra-index-url https://nexus.mukit.at/repository/odoo/simple <module>`` |
|||
|
|||
When the process is finished, restart your server and update the application in |
|||
Odoo, just like you would normally. |
|||
|
|||
Configuration |
|||
============= |
|||
|
|||
No additional configuration is needed to use this module. |
|||
|
|||
Usage |
|||
============= |
|||
|
|||
This module has no direct visible effect on the system. It provide utility features. |
|||
|
|||
Credits |
|||
======= |
|||
|
|||
Contributors |
|||
------------ |
|||
|
|||
* Mathias Markl <mathias.markl@mukit.at> |
|||
|
|||
Author & Maintainer |
|||
------------------- |
|||
|
|||
This module is maintained by the `MuK IT GmbH <https://www.mukit.at/>`_. |
|||
|
|||
MuK IT is an Austrian company specialized in customizing and extending Odoo. |
|||
We develop custom solutions for your individual needs to help you focus on |
|||
your strength and expertise to grow your business. |
|||
|
|||
If you want to get in touch please contact us via mail |
|||
(sale@mukit.at) or visit our website (https://mukit.at). |
|||
============== |
|||
MuK Thumbnails |
|||
============== |
|||
|
|||
Technical module to provide some utility features and libraries that can be used |
|||
in other applications. This module has no direct effect on the running system. |
|||
|
|||
Requirements |
|||
============= |
|||
|
|||
Unoconv |
|||
------------- |
|||
|
|||
Universal Office Converter (unoconv) is a command line tool to convert any |
|||
document format that LibreOffice can import to any document format that |
|||
LibreOffice can export. It makes use of the LibreOffice's UNO bindings for |
|||
non-interactive conversion of documents. |
|||
|
|||
To install unoconv please follow the instructions (`here <https://github.com/dagwieers/unoconv>`_). |
|||
|
|||
Make sure that unoconv can be executed from the console and the conversion |
|||
is done correctly. |
|||
|
|||
To set an individual path for the LibreOffice installation, the config |
|||
variable ``uno_path`` can be set accordingly in the Odoo config. |
|||
|
|||
Under Windows you should rename the ``unoconv`` file to ``unoconv.py`` and set |
|||
the corresponding path for the ``uno_path`` variable. Since it does not work |
|||
reliably with the environment variable ``UNO_PATH``. |
|||
|
|||
FFmpeg |
|||
------------- |
|||
|
|||
FFmpeg is a complete, cross-platform solution to record, convert and stream audio and video. |
|||
|
|||
To install FFmpeg please follow the instructions (`here <https://www.ffmpeg.org/download.html>`_). |
|||
|
|||
Ghostscript |
|||
------------- |
|||
|
|||
Ghostscript is a suite of software based on an interpreter for Adobe Systems PostScript and |
|||
Portable Document Format (PDF) page description languages. Its main purposes are the |
|||
rasterization or rendering of such page description language files, for the display or printing |
|||
of document pages, and the conversion between PostScript and PDF files. |
|||
|
|||
To install Ghostscript please follow the instructions (`here <https://www.ghostscript.com/download.html>`_). |
|||
|
|||
ImageMagick |
|||
------------- |
|||
|
|||
ImageMagick can be used to create, edit, compose, or convert bitmap images. It can read and write |
|||
images in a variety of formats (over 200) including PNG, JPEG, GIF, HEIC, TIFF, DPX, EXR, WebP, |
|||
Postscript, PDF, and SVG. Use ImageMagick to resize, flip, mirror, rotate, distort, shear and |
|||
transform images, adjust image colors, apply various special effects, or draw text, lines, polygons, |
|||
ellipses and Bezier curves. |
|||
|
|||
To install ImageMagick please follow the instructions (`here <https://www.imagemagick.org/script/download.php>`_). |
|||
|
|||
Wand |
|||
------------- |
|||
|
|||
Wand is a ctypes-based simple ImageMagick binding for Python. It is used to interact with ImageMagick via a Python interface. |
|||
|
|||
To install Wand please follow the instructions (`here <http://docs.wand-py.org/en/0.4.1/guide/install.html>`_). |
|||
|
|||
Imageio |
|||
------------- |
|||
|
|||
Imageio is a Python library that provides an easy interface to read and write a wide range of image data, including animated images, volumetric data, and scientific formats. |
|||
|
|||
To install Imageio please follow the instructions (`here <https://pypi.org/project/imageio/>`_). |
|||
|
|||
MoviePy |
|||
------------- |
|||
|
|||
MoviePy is a Python module for video editing, which can be used for basic operations (like cuts, concatenations, title insertions), video compositing (a.k.a. non-linear editing), video processing, or to create advanced effects. It can read and write the most common video formats, including GIF. |
|||
|
|||
To install MoviePy please follow the instructions (`here <https://zulko.github.io/moviepy/install.html>`_). |
|||
|
|||
Installation |
|||
============ |
|||
|
|||
To install this module, you need to: |
|||
|
|||
Download the module and add it to your Odoo addons folder. Afterward, log on to |
|||
your Odoo server and go to the Apps menu. Trigger the debug mode and update the |
|||
list by clicking on the "Update Apps List" link. Now install the module by |
|||
clicking on the install button. |
|||
|
|||
Another way to install this module is via the package management for Python |
|||
(`PyPI <https://pypi.org/project/pip/>`_). |
|||
|
|||
To install our modules using the package manager make sure |
|||
`odoo-autodiscover <https://pypi.org/project/odoo-autodiscover/>`_ is installed |
|||
correctly. Then open a console and install the module by entering the following |
|||
command: |
|||
|
|||
``pip install --extra-index-url https://nexus.mukit.at/repository/odoo/simple <module>`` |
|||
|
|||
The module name consists of the Odoo version and the module name, where |
|||
underscores are replaced by a dash. |
|||
|
|||
**Module:** |
|||
|
|||
``odoo<version>-addon-<module_name>`` |
|||
|
|||
**Example:** |
|||
|
|||
``sudo -H pip3 install --extra-index-url https://nexus.mukit.at/repository/odoo/simple odoo11-addon-muk-utils`` |
|||
|
|||
Once the installation has been successfully completed, the app is already in the |
|||
correct folder. Log on to your Odoo server and go to the Apps menu. Trigger the |
|||
debug mode and update the list by clicking on the "Update Apps List" link. Now |
|||
install the module by clicking on the install button. |
|||
|
|||
The biggest advantage of this variant is that you can now also update the app |
|||
using the "pip" command. To do this, enter the following command in your console: |
|||
|
|||
``pip install --upgrade --extra-index-url https://nexus.mukit.at/repository/odoo/simple <module>`` |
|||
|
|||
When the process is finished, restart your server and update the application in |
|||
Odoo. The steps are the same as for the installation only the button has changed |
|||
from "Install" to "Upgrade". |
|||
|
|||
You can also view available Apps directly in our `repository <https://nexus.mukit.at/#browse/browse:odoo>`_ |
|||
and find a more detailed installation guide on our `website <https://mukit.at/page/open-source>`_. |
|||
|
|||
For modules licensed under OPL-1, you will receive access data when you purchase |
|||
the module. If the modules were not purchased directly from |
|||
`MuK IT <https://www.mukit.at/>`_ please contact our support (support@mukit.at) |
|||
with a confirmation of purchase to receive the corresponding access data. |
|||
|
|||
Upgrade |
|||
============ |
|||
|
|||
To upgrade this module, you need to: |
|||
|
|||
Download the module and add it to your Odoo addons folder. Restart the server |
|||
and log on to your Odoo server. Select the Apps menu and upgrade the module by |
|||
clicking on the upgrade button. |
|||
|
|||
If you installed the module using the "pip" command, you can also update the |
|||
module in the same way. Just type the following command into the console: |
|||
|
|||
``pip install --upgrade --extra-index-url https://nexus.mukit.at/repository/odoo/simple <module>`` |
|||
|
|||
When the process is finished, restart your server and update the application in |
|||
Odoo, just like you would normally. |
|||
|
|||
Configuration |
|||
============= |
|||
|
|||
No additional configuration is needed to use this module. |
|||
|
|||
Usage |
|||
============= |
|||
|
|||
This module has no direct visible effect on the system. It provide utility features. |
|||
|
|||
Credits |
|||
======= |
|||
|
|||
Contributors |
|||
------------ |
|||
|
|||
* Mathias Markl <mathias.markl@mukit.at> |
|||
|
|||
Author & Maintainer |
|||
------------------- |
|||
|
|||
This module is maintained by the `MuK IT GmbH <https://www.mukit.at/>`_. |
|||
|
|||
MuK IT is an Austrian company specialized in customizing and extending Odoo. |
|||
We develop custom solutions for your individual needs to help you focus on |
|||
your strength and expertise to grow your business. |
|||
|
|||
If you want to get in touch please contact us via mail |
|||
(sale@mukit.at) or visit our website (https://mukit.at). |
@ -1,211 +1,211 @@ |
|||
################################################################################### |
|||
# |
|||
# 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.") |
|||
################################################################################### |
|||
# |
|||
# 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.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.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.UNOCONV_IMPORTS): |
|||
pdf_data = binary if extension in PDF_IMPORTS else None |
|||
if not pdf_data: |
|||
image_extension = "pdf" |
|||
pdf_data = unoconv.unoconv.convert(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.") |
Write
Preview
Loading…
Cancel
Save
Reference in new issue