175 lines
5.8 KiB
Python
175 lines
5.8 KiB
Python
from __future__ import unicode_literals
|
|
|
|
import base64
|
|
import logging
|
|
import os
|
|
import time
|
|
|
|
from furl import furl
|
|
|
|
from django.core.files import File
|
|
from django.core.files.base import ContentFile
|
|
from django.urls import reverse
|
|
from django.utils.encoding import force_text, python_2_unicode_compatible
|
|
from django.utils.six.moves.urllib.parse import quote_plus, unquote_plus
|
|
|
|
from mayan.apps.converter.transformations import TransformationResize
|
|
from mayan.apps.converter.utils import get_converter_class
|
|
|
|
from .storages import storage_staging_file_image_cache
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
|
|
class PseudoFile(File):
|
|
def __init__(self, file, name):
|
|
self.name = name
|
|
self.file = file
|
|
self.file.seek(0, os.SEEK_END)
|
|
self.size = self.file.tell()
|
|
self.file.seek(0)
|
|
|
|
|
|
class SourceUploadedFile(File):
|
|
def __init__(self, source, file, extra_data=None):
|
|
self.file = file
|
|
self.source = source
|
|
self.extra_data = extra_data
|
|
|
|
|
|
@python_2_unicode_compatible
|
|
class StagingFile(object):
|
|
"""
|
|
Simple class to extend the File class to add preview capabilities
|
|
files in a directory on a storage
|
|
"""
|
|
def __init__(self, staging_folder, filename=None, encoded_filename=None):
|
|
self.staging_folder = staging_folder
|
|
if encoded_filename:
|
|
self.encoded_filename = str(encoded_filename)
|
|
self.filename = base64.urlsafe_b64decode(
|
|
unquote_plus(self.encoded_filename)
|
|
).decode('utf8')
|
|
else:
|
|
self.filename = filename
|
|
self.encoded_filename = quote_plus(base64.urlsafe_b64encode(
|
|
filename.encode('utf8')
|
|
))
|
|
|
|
def __str__(self):
|
|
return force_text(self.filename)
|
|
|
|
def as_file(self):
|
|
return File(
|
|
file=open(self.get_full_path(), mode='rb'), name=self.filename
|
|
)
|
|
|
|
@property
|
|
def cache_filename(self):
|
|
return '{}{}'.format(self.staging_folder.pk, self.encoded_filename)
|
|
|
|
def delete(self):
|
|
storage_staging_file_image_cache.delete(self.cache_filename)
|
|
os.unlink(self.get_full_path())
|
|
|
|
def generate_image(self, *args, **kwargs):
|
|
transformation_list = self.get_combined_transformation_list(*args, **kwargs)
|
|
|
|
# Check is transformed image is available
|
|
logger.debug('transformations cache filename: %s', self.cache_filename)
|
|
|
|
if storage_staging_file_image_cache.exists(self.cache_filename):
|
|
logger.debug(
|
|
'staging file cache file "%s" found', self.cache_filename
|
|
)
|
|
else:
|
|
logger.debug(
|
|
'staging file cache file "%s" not found', self.cache_filename
|
|
)
|
|
image = self.get_image(transformations=transformation_list)
|
|
with storage_staging_file_image_cache.open(self.cache_filename, 'wb+') as file_object:
|
|
file_object.write(image.getvalue())
|
|
|
|
return self.cache_filename
|
|
|
|
def get_api_image_url(self, *args, **kwargs):
|
|
final_url = furl()
|
|
final_url.args = kwargs
|
|
final_url.path = reverse(
|
|
'rest_api:stagingfolderfile-image-view', kwargs={
|
|
'staging_folder_pk': self.staging_folder.pk,
|
|
'encoded_filename': self.encoded_filename
|
|
}
|
|
)
|
|
|
|
return final_url.tostr()
|
|
|
|
def get_combined_transformation_list(self, *args, **kwargs):
|
|
"""
|
|
Return a list of transformation containing the server side
|
|
staging file transformation as well as tranformations created
|
|
from the arguments as transient interactive transformation.
|
|
"""
|
|
# Convert arguments into transformations
|
|
transformations = kwargs.get('transformations', [])
|
|
|
|
# Set sensible defaults if the argument is not specified or if the
|
|
# argument is None
|
|
width = self.staging_folder.preview_width
|
|
height = self.staging_folder.preview_height
|
|
|
|
# Generate transformation hash
|
|
transformation_list = []
|
|
|
|
# Interactive transformations second
|
|
for transformation in transformations:
|
|
transformation_list.append(transformation)
|
|
|
|
if width:
|
|
transformation_list.append(
|
|
TransformationResize(width=width, height=height)
|
|
)
|
|
|
|
return transformation_list
|
|
|
|
def get_date_time_created(self):
|
|
return time.ctime(os.path.getctime(self.get_full_path()))
|
|
|
|
def get_full_path(self):
|
|
return os.path.join(self.staging_folder.folder_path, self.filename)
|
|
|
|
def get_image(self, transformations=None):
|
|
cache_filename = self.cache_filename
|
|
file_object = None
|
|
|
|
try:
|
|
file_object = open(self.get_full_path(), mode='rb')
|
|
converter = get_converter_class()(file_object=file_object)
|
|
|
|
page_image = converter.get_page()
|
|
|
|
# Since open "wb+" doesn't create files, check if the file
|
|
# exists, if not then create it
|
|
if not storage_staging_file_image_cache.exists(cache_filename):
|
|
storage_staging_file_image_cache.save(name=cache_filename, content=ContentFile(content=''))
|
|
|
|
with storage_staging_file_image_cache.open(cache_filename, 'wb+') as file_object:
|
|
file_object.write(page_image.getvalue())
|
|
except Exception as exception:
|
|
# Cleanup in case of error
|
|
logger.error(
|
|
'Error creating staging file cache "%s"; %s',
|
|
cache_filename, exception
|
|
)
|
|
storage_staging_file_image_cache.delete(cache_filename)
|
|
if file_object:
|
|
file_object.close()
|
|
raise
|
|
|
|
for transformation in transformations:
|
|
converter.transform(transformation=transformation)
|
|
|
|
result = converter.get_page()
|
|
file_object.close()
|
|
return result
|