mirror of
https://github.com/haiwen/seahub.git
synced 2025-09-08 02:10:24 +00:00
@@ -437,7 +437,7 @@
|
||||
</td>
|
||||
<td class="dirent-icon">
|
||||
<div class="pos-rel">
|
||||
<% if (dirent.is_img) { %>
|
||||
<% if (dirent.is_img || dirent.is_video) { %>
|
||||
<% if (dirent.encoded_thumbnail_src) { %>
|
||||
<img class="thumbnail" src="{{ SITE_ROOT }}<%- dirent.encoded_thumbnail_src %>" alt="" />
|
||||
<% } else { %>
|
||||
@@ -532,7 +532,7 @@
|
||||
</td>
|
||||
<td class="dirent-icon">
|
||||
<div class="pos-rel">
|
||||
<% if (dirent.is_img) { %>
|
||||
<% if (dirent.is_img || dirent.is_video) { %>
|
||||
<% if (dirent.encoded_thumbnail_src) { %>
|
||||
<img class="thumbnail" src="{{ SITE_ROOT }}<%- dirent.encoded_thumbnail_src %>" alt="" />
|
||||
<% } else { %>
|
||||
@@ -605,7 +605,7 @@
|
||||
|
||||
<script type="text/template" id="grid-view-file-item-tmpl">
|
||||
<a href="<%= url %>" class="img-link" target="_blank">
|
||||
<% if (dirent.is_img && dirent.encoded_thumbnail_src) { %>
|
||||
<% if ((dirent.is_img || dirent.is_video) && dirent.encoded_thumbnail_src) { %>
|
||||
<img class="thumbnail vam" src="{{ SITE_ROOT }}<%- dirent.encoded_thumbnail_src %>" alt="" />
|
||||
<% } else { %>
|
||||
<img src="<%= icon_url %>" width="96" alt="" class="vam" />
|
||||
|
@@ -79,7 +79,7 @@
|
||||
|
||||
{% for dirent in file_list %}
|
||||
<tr class="file-item" data-name="{{dirent.obj_name}}" >
|
||||
{% if dirent.is_img %}
|
||||
{% if dirent.is_img or dirent.is_video %}
|
||||
{% if dirent.encoded_thumbnail_src %}
|
||||
<td class="alc"><img class="thumbnail" src="{{ SITE_ROOT }}{{ dirent.encoded_thumbnail_src }}" alt="{% trans "File"%}" /></td>
|
||||
{% else %}
|
||||
@@ -130,18 +130,22 @@
|
||||
<li class="file-item grid-item" data-name="{{dirent.obj_name}}" title="{{dirent.obj_name}}">
|
||||
{% if dirent.is_img %}
|
||||
<a class="img-link img-img-link" href="{% url "view_file_via_shared_dir" token %}?p={{ path|urlencode }}{{ dirent.obj_name|urlencode }}" data-mfp-src="{% url "view_file_via_shared_dir" token %}?p={{ path|urlencode }}{{ dirent.obj_name|urlencode }}&raw=1">
|
||||
{% else %}
|
||||
<a class="img-link" href="{% url "view_file_via_shared_dir" token %}?p={{ path|urlencode }}{{ dirent.obj_name|urlencode }}">
|
||||
{% endif %}
|
||||
|
||||
{% if dirent.is_img or dirent.is_video %}
|
||||
{% if dirent.encoded_thumbnail_src %}
|
||||
<img class="thumbnail vam" src="{{ SITE_ROOT }}{{ dirent.encoded_thumbnail_src }}" alt="" />
|
||||
{% else %}
|
||||
<img class="not-thumbnail vam" src="{{ MEDIA_URL }}img/file/{{ dirent.obj_name|file_icon_filter:192 }}" alt="" width="96" />
|
||||
{% endif %}
|
||||
</a>
|
||||
{% else %}
|
||||
<a class="img-link" href="{% url "view_file_via_shared_dir" token %}?p={{ path|urlencode }}{{ dirent.obj_name|urlencode }}">
|
||||
<img class="vam" src="{{ MEDIA_URL }}img/file/{{ dirent.obj_name|file_icon_filter:192 }}" alt="" width="96" />
|
||||
</a>
|
||||
{% endif %}
|
||||
|
||||
</a>
|
||||
|
||||
{% if dirent.is_img %}
|
||||
<a class="normal img-name-link text-link ellipsis" href="{% url "view_file_via_shared_dir" token %}?p={{ path|urlencode }}{{ dirent.obj_name|urlencode }}" data-mfp-src="{% url "view_file_via_shared_dir" token %}?p={{ path|urlencode }}{{ dirent.obj_name|urlencode }}&raw=1">{{ dirent.obj_name }}</a>
|
||||
{% else %}
|
||||
|
@@ -1,22 +1,34 @@
|
||||
# Copyright (c) 2012-2016 Seafile Ltd.
|
||||
import os
|
||||
import posixpath
|
||||
import timeit
|
||||
import tempfile
|
||||
import urllib2
|
||||
import logging
|
||||
from StringIO import StringIO
|
||||
from PIL import Image, ExifTags
|
||||
|
||||
from PIL import Image
|
||||
try:
|
||||
from moviepy.editor import VideoFileClip
|
||||
_ENABLE_VIDEO_THUMBNAIL = True
|
||||
except ImportError:
|
||||
_ENABLE_VIDEO_THUMBNAIL = False
|
||||
from seaserv import get_file_id_by_path, get_repo, get_file_size, \
|
||||
seafile_api
|
||||
|
||||
from seahub.utils import gen_inner_file_get_url
|
||||
|
||||
from seahub.utils import gen_inner_file_get_url, get_file_type_and_ext
|
||||
from seahub.utils.file_types import VIDEO
|
||||
from seahub.settings import THUMBNAIL_IMAGE_SIZE_LIMIT, \
|
||||
THUMBNAIL_EXTENSION, THUMBNAIL_ROOT, THUMBNAIL_IMAGE_ORIGINAL_SIZE_LIMIT
|
||||
|
||||
# Get an instance of a logger
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
if _ENABLE_VIDEO_THUMBNAIL:
|
||||
logger.debug('Video thumbnail is enabled.')
|
||||
else:
|
||||
logger.debug('Video thumbnail is disabled.')
|
||||
|
||||
def get_thumbnail_src(repo_id, size, path):
|
||||
return posixpath.join("thumbnail", repo_id, str(size), path.lstrip('/'))
|
||||
|
||||
@@ -90,33 +102,73 @@ def generate_thumbnail(request, repo_id, size, path):
|
||||
|
||||
repo = get_repo(repo_id)
|
||||
file_size = get_file_size(repo.store_id, repo.version, file_id)
|
||||
filetype, fileext = get_file_type_and_ext(os.path.basename(path))
|
||||
|
||||
if filetype == VIDEO:
|
||||
# video thumbnails
|
||||
if _ENABLE_VIDEO_THUMBNAIL:
|
||||
return create_video_thumbnails(repo, file_id, path, size,
|
||||
thumbnail_file, file_size)
|
||||
else:
|
||||
return (False, 400)
|
||||
|
||||
# image thumbnails
|
||||
if file_size > THUMBNAIL_IMAGE_SIZE_LIMIT * 1024**2:
|
||||
return (False, 403)
|
||||
|
||||
token = seafile_api.get_fileserver_access_token(repo_id, file_id, 'view',
|
||||
'', use_onetime = True)
|
||||
'', use_onetime=True)
|
||||
|
||||
inner_path = gen_inner_file_get_url(token, os.path.basename(path))
|
||||
try:
|
||||
image_file = urllib2.urlopen(inner_path)
|
||||
f = StringIO(image_file.read())
|
||||
image = Image.open(f)
|
||||
|
||||
# check image memory cost size limit
|
||||
# use RGBA as default mode(4x8-bit pixels, true colour with transparency mask)
|
||||
# every pixel will cost 4 byte in RGBA mode
|
||||
width, height = image.size
|
||||
image_memory_cost = width * height * 4 / 1024 / 1024
|
||||
if image_memory_cost > THUMBNAIL_IMAGE_ORIGINAL_SIZE_LIMIT:
|
||||
return (False, 403)
|
||||
|
||||
if image.mode not in ["1", "L", "P", "RGB", "RGBA"]:
|
||||
image = image.convert("RGB")
|
||||
|
||||
image = get_rotated_image(image)
|
||||
image.thumbnail((size, size), Image.ANTIALIAS)
|
||||
image.save(thumbnail_file, THUMBNAIL_EXTENSION)
|
||||
return (True, 200)
|
||||
return _create_thumbnail_common(f, thumbnail_file, size)
|
||||
except Exception as e:
|
||||
logger.error(e)
|
||||
return (False, 500)
|
||||
|
||||
def create_video_thumbnails(repo, file_id, path, size, thumbnail_file, file_size):
|
||||
|
||||
t1 = timeit.default_timer()
|
||||
token = seafile_api.get_fileserver_access_token(repo.id, file_id, 'view',
|
||||
'', use_onetime=False)
|
||||
|
||||
inner_path = gen_inner_file_get_url(token, os.path.basename(path))
|
||||
clip = VideoFileClip(inner_path)
|
||||
tmp_path = str(os.path.join(tempfile.gettempdir(), '%s.png' % file_id[:8]))
|
||||
clip.save_frame(tmp_path)
|
||||
t2 = timeit.default_timer()
|
||||
logger.debug('Create thumbnail of [%s](size: %s) takes: %s' % (path, file_size, (t2 - t1)))
|
||||
|
||||
try:
|
||||
ret = _create_thumbnail_common(tmp_path, thumbnail_file, size)
|
||||
os.unlink(tmp_path)
|
||||
return ret
|
||||
except Exception as e:
|
||||
logger.error(e)
|
||||
os.unlink(tmp_path)
|
||||
return (False, 500)
|
||||
|
||||
def _create_thumbnail_common(fp, thumbnail_file, size):
|
||||
"""Common logic for creating image thumbnail.
|
||||
|
||||
`fp` can be a filename (string) or a file object.
|
||||
"""
|
||||
image = Image.open(fp)
|
||||
|
||||
# check image memory cost size limit
|
||||
# use RGBA as default mode(4x8-bit pixels, true colour with transparency mask)
|
||||
# every pixel will cost 4 byte in RGBA mode
|
||||
width, height = image.size
|
||||
image_memory_cost = width * height * 4 / 1024 / 1024
|
||||
if image_memory_cost > THUMBNAIL_IMAGE_ORIGINAL_SIZE_LIMIT:
|
||||
return (False, 403)
|
||||
|
||||
if image.mode not in ["1", "L", "P", "RGB", "RGBA"]:
|
||||
image = image.convert("RGB")
|
||||
|
||||
image = get_rotated_image(image)
|
||||
image.thumbnail((size, size), Image.ANTIALIAS)
|
||||
image.save(thumbnail_file, THUMBNAIL_EXTENSION)
|
||||
return (True, 200)
|
||||
|
@@ -47,7 +47,7 @@ from seahub.utils import check_filename_with_rename, EMPTY_SHA1, \
|
||||
from seahub.utils.star import get_dir_starred_files
|
||||
from seahub.base.accounts import User
|
||||
from seahub.thumbnail.utils import get_thumbnail_src
|
||||
from seahub.utils.file_types import IMAGE
|
||||
from seahub.utils.file_types import IMAGE, VIDEO
|
||||
from seahub.base.templatetags.seahub_tags import translate_seahub_time, \
|
||||
email2nickname, tsstr_sec
|
||||
|
||||
@@ -343,6 +343,9 @@ def list_lib_dir(request, repo_id):
|
||||
file_type, file_ext = get_file_type_and_ext(f.obj_name)
|
||||
if file_type == IMAGE:
|
||||
f_['is_img'] = True
|
||||
if file_type == VIDEO:
|
||||
f_['is_video'] = True
|
||||
if file_type == IMAGE or file_type == VIDEO:
|
||||
if not repo.encrypted and ENABLE_THUMBNAIL and \
|
||||
os.path.exists(os.path.join(THUMBNAIL_ROOT, str(size), f.obj_id)):
|
||||
file_path = posixpath.join(path, f.obj_name)
|
||||
|
@@ -30,7 +30,7 @@ from seahub.settings import ENABLE_UPLOAD_FOLDER, \
|
||||
ENABLE_RESUMABLE_FILEUPLOAD, ENABLE_THUMBNAIL, \
|
||||
THUMBNAIL_ROOT, THUMBNAIL_DEFAULT_SIZE, THUMBNAIL_SIZE_FOR_GRID, \
|
||||
MAX_NUMBER_OF_FILES_FOR_FILEUPLOAD
|
||||
from seahub.utils.file_types import IMAGE
|
||||
from seahub.utils.file_types import IMAGE, VIDEO
|
||||
from seahub.thumbnail.utils import get_share_link_thumbnail_src
|
||||
|
||||
# Get an instance of a logger
|
||||
@@ -230,6 +230,9 @@ def view_shared_dir(request, fileshare):
|
||||
file_type, file_ext = get_file_type_and_ext(f.obj_name)
|
||||
if file_type == IMAGE:
|
||||
f.is_img = True
|
||||
if file_type == VIDEO:
|
||||
f.is_video = True
|
||||
if file_type == IMAGE or file_type == VIDEO:
|
||||
if os.path.exists(os.path.join(THUMBNAIL_ROOT, str(thumbnail_size), f.obj_id)):
|
||||
req_image_path = posixpath.join(req_path, f.obj_name)
|
||||
src = get_share_link_thumbnail_src(token, thumbnail_size, req_image_path)
|
||||
|
@@ -205,7 +205,7 @@ define([
|
||||
|
||||
this.updateDirOpBarUI(); // after `render_dirents_slice`
|
||||
|
||||
this.getImageThumbnail();
|
||||
this.getThumbnail();
|
||||
},
|
||||
|
||||
updateDirOpBarUI: function() {
|
||||
@@ -316,20 +316,20 @@ define([
|
||||
}
|
||||
},
|
||||
|
||||
getImageThumbnail: function() {
|
||||
getThumbnail: function() {
|
||||
if (!app.pageOptions.enable_thumbnail || this.dir.encrypted) {
|
||||
return false;
|
||||
}
|
||||
|
||||
var images_with_no_thumbnail = this.dir.filter(function(dirent) {
|
||||
var items = this.dir.filter(function(dirent) {
|
||||
// 'dirent' is a model
|
||||
return dirent.get('is_img') && !dirent.get('encoded_thumbnail_src');
|
||||
return (dirent.get('is_img') || dirent.get('is_video')) && !dirent.get('encoded_thumbnail_src');
|
||||
});
|
||||
if (images_with_no_thumbnail.length == 0) {
|
||||
if (items.length == 0) {
|
||||
return ;
|
||||
}
|
||||
|
||||
var images_len = images_with_no_thumbnail.length,
|
||||
var items_length = items.length,
|
||||
repo_id = this.dir.repo_id,
|
||||
cur_path = this.dir.path,
|
||||
_this = this;
|
||||
@@ -338,24 +338,24 @@ define([
|
||||
thumbnail_size = app.pageOptions.thumbnail_size_for_grid;
|
||||
}
|
||||
var get_thumbnail = function(i) {
|
||||
var cur_img = images_with_no_thumbnail[i];
|
||||
var cur_img_path = Common.pathJoin([cur_path, cur_img.get('obj_name')]);
|
||||
var cur_item = items[i];
|
||||
var cur_item_path = Common.pathJoin([cur_path, cur_item.get('obj_name')]);
|
||||
$.ajax({
|
||||
url: Common.getUrl({name: 'thumbnail_create', repo_id: repo_id}),
|
||||
data: {
|
||||
'path': cur_img_path,
|
||||
'path': cur_item_path,
|
||||
'size': thumbnail_size
|
||||
},
|
||||
cache: false,
|
||||
dataType: 'json',
|
||||
success: function(data) {
|
||||
cur_img.set({
|
||||
cur_item.set({
|
||||
'encoded_thumbnail_src': data.encoded_thumbnail_src
|
||||
});
|
||||
},
|
||||
complete: function() {
|
||||
// cur path may be changed. e.g., the user enter another directory
|
||||
if (i < images_len - 1 &&
|
||||
if (i < items_length - 1 &&
|
||||
_this.dir.repo_id == repo_id &&
|
||||
_this.dir.path == cur_path) {
|
||||
get_thumbnail(++i);
|
||||
@@ -1336,7 +1336,7 @@ define([
|
||||
if (this.dir.dirent_more &&
|
||||
$(window).scrollTop() + $(window).height() == $(document).height()) { // scroll to the bottom
|
||||
this.render_dirents_slice(this.dir.last_start, this.dir.limit);
|
||||
this.getImageThumbnail();
|
||||
this.getThumbnail();
|
||||
}
|
||||
|
||||
// fixed 'dir-op-bar'
|
||||
|
@@ -35,49 +35,49 @@ define([
|
||||
this.renderThead();
|
||||
this.starredFiles.each(this.addOne, this);
|
||||
this.$table.show();
|
||||
this.getImageThumbnail();
|
||||
this.getThumbnail();
|
||||
} else {
|
||||
this.$emptyTip.show();
|
||||
this.$table.hide();
|
||||
}
|
||||
},
|
||||
|
||||
getImageThumbnail: function() {
|
||||
getThumbnail: function() {
|
||||
if (!app.pageOptions.enable_thumbnail) {
|
||||
return false;
|
||||
}
|
||||
|
||||
var images = this.starredFiles.filter(function(item) {
|
||||
var items = this.starredFiles.filter(function(item) {
|
||||
// 'item' is a model
|
||||
return Common.imageCheck(item.get('file_name'));
|
||||
return Common.imageCheck(item.get('file_name')) || Common.videoCheck(item.get('file_name'));
|
||||
});
|
||||
if (images.length == 0) {
|
||||
if (items.length == 0) {
|
||||
return ;
|
||||
}
|
||||
|
||||
var images_len = images.length;
|
||||
var items_len = items.length;
|
||||
var thumbnail_size = app.pageOptions.thumbnail_default_size;
|
||||
|
||||
var get_thumbnail = function(i) {
|
||||
var cur_img = images[i];
|
||||
var cur_item = items[i];
|
||||
$.ajax({
|
||||
url: Common.getUrl({
|
||||
name: 'thumbnail_create',
|
||||
repo_id: cur_img.get('repo_id')
|
||||
repo_id: cur_item.get('repo_id')
|
||||
}),
|
||||
data: {
|
||||
'path': cur_img.get('path'),
|
||||
'path': cur_item.get('path'),
|
||||
'size': thumbnail_size
|
||||
},
|
||||
cache: false,
|
||||
dataType: 'json',
|
||||
success: function(data) {
|
||||
cur_img.set({
|
||||
cur_item.set({
|
||||
'encoded_thumbnail_src': data.encoded_thumbnail_src
|
||||
});
|
||||
},
|
||||
complete: function() {
|
||||
if (i < images_len - 1) {
|
||||
if (i < items_len - 1) {
|
||||
get_thumbnail(++i);
|
||||
}
|
||||
}
|
||||
|
@@ -735,6 +735,22 @@ define([
|
||||
}
|
||||
},
|
||||
|
||||
// check if a file is a video
|
||||
videoCheck: function (filename) {
|
||||
// no file ext
|
||||
if (filename.lastIndexOf('.') == -1) {
|
||||
return false;
|
||||
}
|
||||
var file_ext = filename.substr(filename.lastIndexOf('.') + 1).toLowerCase();
|
||||
var exts = ['mp4', 'ogv', 'webm', 'mov'];
|
||||
if (exts.indexOf(file_ext) != -1) {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
compareTwoWord: function(a_name, b_name) {
|
||||
// compare a_name and b_name at lower case
|
||||
// if a_name >= b_name, return 1
|
||||
|
Reference in New Issue
Block a user