1
0
mirror of https://github.com/haiwen/seahub.git synced 2025-09-08 02:10:24 +00:00

Merge pull request #1553 from haiwen/video

video thumbnail
This commit is contained in:
xiez
2017-04-24 16:26:23 +08:00
committed by GitHub
8 changed files with 131 additions and 53 deletions

View File

@@ -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" />

View File

@@ -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 %}

View File

@@ -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)

View File

@@ -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)

View File

@@ -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)

View File

@@ -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'

View File

@@ -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);
}
}

View File

@@ -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