1
0
mirror of https://github.com/haiwen/seahub.git synced 2025-08-17 14:37:58 +00:00

add pdf thumbnail (#6157)

Co-authored-by: 孙永强 <11704063+s-yongqiang@user.noreply.gitee.com>
This commit is contained in:
awu0403 2024-06-04 16:06:57 +08:00 committed by GitHub
parent 0b84b22df9
commit ca83ef90dc
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
16 changed files with 83 additions and 11 deletions

View File

@ -3,7 +3,7 @@ import PropTypes from 'prop-types';
import cookie from 'react-cookies'; import cookie from 'react-cookies';
import moment from 'moment'; import moment from 'moment';
import { navigate } from '@gatsbyjs/reach-router'; import { navigate } from '@gatsbyjs/reach-router';
import { gettext, siteRoot, username, enableVideoThumbnail } from '../../utils/constants'; import { gettext, siteRoot, username, enableVideoThumbnail, enablePDFThumbnail } from '../../utils/constants';
import { seafileAPI } from '../../utils/seafile-api'; import { seafileAPI } from '../../utils/seafile-api';
import { Utils } from '../../utils/utils'; import { Utils } from '../../utils/utils';
import collabServer from '../../utils/collab-server'; import collabServer from '../../utils/collab-server';
@ -538,7 +538,7 @@ class LibContentView extends React.Component {
getThumbnails = (repoID, path, direntList) => { getThumbnails = (repoID, path, direntList) => {
let items = direntList.filter((item) => { let items = direntList.filter((item) => {
return (Utils.imageCheck(item.name) || (enableVideoThumbnail && Utils.videoCheck(item.name))) && !item.encoded_thumbnail_src; return (Utils.imageCheck(item.name) || (enableVideoThumbnail && Utils.videoCheck(item.name)) || (enablePDFThumbnail && Utils.pdfCheck(item.name))) && !item.encoded_thumbnail_src;
}); });
if (items.length == 0) { if (items.length == 0) {
return ; return ;

View File

@ -5,7 +5,7 @@ import { Link, navigate } from '@gatsbyjs/reach-router';
import moment from 'moment'; import moment from 'moment';
import { seafileAPI } from '../../utils/seafile-api'; import { seafileAPI } from '../../utils/seafile-api';
import { Utils } from '../../utils/utils'; import { Utils } from '../../utils/utils';
import { gettext, siteRoot, enableVideoThumbnail } from '../../utils/constants'; import { gettext, siteRoot, enableVideoThumbnail, enablePDFThumbnail } from '../../utils/constants';
import EmptyTip from '../../components/empty-tip'; import EmptyTip from '../../components/empty-tip';
import Loading from '../../components/loading'; import Loading from '../../components/loading';
import toaster from '../../components/toast'; import toaster from '../../components/toast';
@ -79,7 +79,10 @@ class TableBody extends Component {
getThumbnails() { getThumbnails() {
let items = this.state.items.filter((item) => { let items = this.state.items.filter((item) => {
return (Utils.imageCheck(item.obj_name) || (enableVideoThumbnail && Utils.videoCheck(item.obj_name))) && !item.repo_encrypted && !item.encoded_thumbnail_src && !item.deleted; return (Utils.imageCheck(item.obj_name) ||
(enableVideoThumbnail && Utils.videoCheck(item.obj_name)) ||
(enablePDFThumbnail && Utils.pdfCheck(item.obj_name))) &&
!item.repo_encrypted && !item.encoded_thumbnail_src && !item.deleted;
}); });
if (items.length == 0) { if (items.length == 0) {
return ; return ;

View File

@ -30,7 +30,7 @@ let {
repoID, relativePath, repoID, relativePath,
mode, thumbnailSize, zipped, mode, thumbnailSize, zipped,
trafficOverLimit, canDownload, trafficOverLimit, canDownload,
noQuota, canUpload, enableVideoThumbnail noQuota, canUpload, enableVideoThumbnail, enablePDFThumbnail
} = window.shared.pageOptions; } = window.shared.pageOptions;
const showDownloadIcon = !trafficOverLimit && canDownload; const showDownloadIcon = !trafficOverLimit && canDownload;
@ -111,7 +111,8 @@ class SharedDirView extends React.Component {
let items = this.state.items.filter((item) => { let items = this.state.items.filter((item) => {
return !item.is_dir && return !item.is_dir &&
(Utils.imageCheck(item.file_name) || (Utils.imageCheck(item.file_name) ||
(enableVideoThumbnail && Utils.videoCheck(item.file_name))) && (enableVideoThumbnail && Utils.videoCheck(item.file_name)) ||
(enablePDFThumbnail && Utils.pdfCheck(item.file_name))) &&
!item.encoded_thumbnail_src; !item.encoded_thumbnail_src;
}); });
if (items.length == 0) { if (items.length == 0) {

View File

@ -91,6 +91,7 @@ export const curNoteID = window.app.pageOptions.curNoteID;
export const enableTC = window.app.pageOptions.enableTC; export const enableTC = window.app.pageOptions.enableTC;
export const enableVideoThumbnail = window.app.pageOptions.enableVideoThumbnail; export const enableVideoThumbnail = window.app.pageOptions.enableVideoThumbnail;
export const enablePDFThumbnail = window.app.pageOptions.enablePDFThumbnail;
export const enableOnlyoffice = window.app.pageOptions.enableOnlyoffice || false; export const enableOnlyoffice = window.app.pageOptions.enableOnlyoffice || false;
export const onlyofficeConverterExtensions = window.app.pageOptions.onlyofficeConverterExtensions || []; export const onlyofficeConverterExtensions = window.app.pageOptions.onlyofficeConverterExtensions || [];

View File

@ -124,6 +124,19 @@ export const Utils = {
} }
}, },
pdfCheck: function(filename) {
if (filename.lastIndexOf('.') == -1) {
return false;
}
var file_ext = filename.substr(filename.lastIndexOf('.') + 1).toLowerCase();
var image_exts = ['pdf'];
if (image_exts.indexOf(file_ext) != -1) {
return true;
} else {
return false;
}
},
getShareLinkPermissionList: function(itemType, permission, path, canEdit) { getShareLinkPermissionList: function(itemType, permission, path, canEdit) {
// itemType: library, dir, file // itemType: library, dir, file
// permission: rw, r, admin, cloud-edit, preview, custom-* // permission: rw, r, admin, cloud-edit, preview, custom-*

View File

@ -26,3 +26,4 @@ Markdown==3.4.*
bleach==5.0.* bleach==5.0.*
python-ldap==3.4.* python-ldap==3.4.*
pypinyin==0.50.* pypinyin==0.50.*
PyMuPDF==1.24.*

View File

@ -23,7 +23,7 @@ from seahub.utils import check_filename_with_rename, is_valid_dirent_name, \
normalize_dir_path, is_pro_version, FILEEXT_TYPE_MAP, get_file_type_and_ext normalize_dir_path, is_pro_version, FILEEXT_TYPE_MAP, get_file_type_and_ext
from seahub.utils.timeutils import timestamp_to_isoformat_timestr from seahub.utils.timeutils import timestamp_to_isoformat_timestr
from seahub.utils.file_tags import get_files_tags_in_dir from seahub.utils.file_tags import get_files_tags_in_dir
from seahub.utils.file_types import IMAGE, VIDEO, XMIND, SEADOC from seahub.utils.file_types import IMAGE, VIDEO, XMIND, SEADOC, PDF
from seahub.base.models import UserStarredFiles from seahub.base.models import UserStarredFiles
from seahub.base.templatetags.seahub_tags import email2nickname, \ from seahub.base.templatetags.seahub_tags import email2nickname, \
email2contact_email email2contact_email
@ -175,7 +175,7 @@ def get_dir_file_info_list(username, request_type, repo_obj, parent_dir,
fileExt = os.path.splitext(file_name)[1][1:].lower() fileExt = os.path.splitext(file_name)[1][1:].lower()
file_type = FILEEXT_TYPE_MAP.get(fileExt) file_type = FILEEXT_TYPE_MAP.get(fileExt)
if file_type in (IMAGE, XMIND) or \ if file_type in (IMAGE, XMIND, PDF) or \
(file_type == VIDEO and ENABLE_VIDEO_THUMBNAIL): (file_type == VIDEO and ENABLE_VIDEO_THUMBNAIL):
# if thumbnail has already been created, return its src. # if thumbnail has already been created, return its src.

View File

@ -747,6 +747,9 @@ THUMBNAIL_IMAGE_ORIGINAL_SIZE_LIMIT = 256
ENABLE_VIDEO_THUMBNAIL = False ENABLE_VIDEO_THUMBNAIL = False
THUMBNAIL_VIDEO_FRAME_TIME = 5 # use the frame at 5 second as thumbnail THUMBNAIL_VIDEO_FRAME_TIME = 5 # use the frame at 5 second as thumbnail
# pdf thumbnails
ENABLE_PDF_THUMBNAIL = True
# template for create new office file # template for create new office file
OFFICE_TEMPLATE_ROOT = os.path.join(MEDIA_ROOT, 'office-template') OFFICE_TEMPLATE_ROOT = os.path.join(MEDIA_ROOT, 'office-template')

View File

@ -141,6 +141,7 @@
enableTC: {% if enable_terms_and_conditions %} true {% else %} false {% endif %}, enableTC: {% if enable_terms_and_conditions %} true {% else %} false {% endif %},
enableSSOToThirdpartWebsite: {% if enable_sso_to_thirdpart_website %} true {% else %} false {% endif %}, enableSSOToThirdpartWebsite: {% if enable_sso_to_thirdpart_website %} true {% else %} false {% endif %},
enableVideoThumbnail: {% if enable_video_thumbnail %} true {% else %} false {% endif %}, enableVideoThumbnail: {% if enable_video_thumbnail %} true {% else %} false {% endif %},
enablePDFThumbnail: {% if enable_pdf_thumbnail %} true {% else %} false {% endif %},
showLogoutIcon: {% if show_logout_icon %} true {% else %} false {% endif %}, showLogoutIcon: {% if show_logout_icon %} true {% else %} false {% endif %},
additionalShareDialogNote: {% if additional_share_dialog_note %} {{ additional_share_dialog_note|safe }} {% else %} null {% endif %}, additionalShareDialogNote: {% if additional_share_dialog_note %} {{ additional_share_dialog_note|safe }} {% else %} null {% endif %},
additionalAppBottomLinks: {% if additional_app_bottom_links %} {{ additional_app_bottom_links|safe }} {% else %} null {% endif %}, additionalAppBottomLinks: {% if additional_app_bottom_links %} {{ additional_app_bottom_links|safe }} {% else %} null {% endif %},

View File

@ -24,6 +24,10 @@ thumbnailSizeForOriginal: {{ thumbnail_size_for_original }},
enableVideoThumbnail: {% if enable_video_thumbnail %} true {% else %} false {% endif %}, enableVideoThumbnail: {% if enable_video_thumbnail %} true {% else %} false {% endif %},
{% endif %} {% endif %}
{% if filetype == 'PDF' %}
enablePDFThumbnail: {% if enable_pdf_thumbnail %} true {% else %} false {% endif %},
{% endif %}
{% if filetype == 'XMind' %} {% if filetype == 'XMind' %}
xmindImageSrc: '{{ xmind_image_src|escapejs }}', xmindImageSrc: '{{ xmind_image_src|escapejs }}',
{% endif %} {% endif %}

View File

@ -38,6 +38,7 @@
mode: '{{ mode }}', mode: '{{ mode }}',
thumbnailSize: {{ thumbnail_size }}, thumbnailSize: {{ thumbnail_size }},
enableVideoThumbnail: {% if enable_video_thumbnail %}true{% else %}false{% endif %}, enableVideoThumbnail: {% if enable_video_thumbnail %}true{% else %}false{% endif %},
enablePDFThumbnail: {% if enable_pdf_thumbnail %}true{% else %}false{% endif %},
trafficOverLimit: {% if traffic_over_limit %}true{% else %}false{% endif %}, trafficOverLimit: {% if traffic_over_limit %}true{% else %}false{% endif %},
canDownload: {% if permissions.can_download %}true{% else %}false{% endif %}, canDownload: {% if permissions.can_download %}true{% else %}false{% endif %},
noQuota: {% if no_quota %}true{% else %}false{% endif %}, noQuota: {% if no_quota %}true{% else %}false{% endif %},

View File

@ -8,6 +8,7 @@ import logging
import subprocess import subprocess
from io import BytesIO from io import BytesIO
import zipfile import zipfile
from fitz import open as fitz_open
try: # Py2 and Py3 compatibility try: # Py2 and Py3 compatibility
from urllib.request import urlretrieve from urllib.request import urlretrieve
except: except:
@ -18,7 +19,7 @@ from seaserv import get_file_id_by_path, get_repo, get_file_size, \
seafile_api seafile_api
from seahub.utils import gen_inner_file_get_url, get_file_type_and_ext from seahub.utils import gen_inner_file_get_url, get_file_type_and_ext
from seahub.utils.file_types import VIDEO, XMIND from seahub.utils.file_types import VIDEO, XMIND, PDF
from seahub.settings import THUMBNAIL_IMAGE_SIZE_LIMIT, \ from seahub.settings import THUMBNAIL_IMAGE_SIZE_LIMIT, \
THUMBNAIL_EXTENSION, THUMBNAIL_ROOT, THUMBNAIL_IMAGE_ORIGINAL_SIZE_LIMIT,\ THUMBNAIL_EXTENSION, THUMBNAIL_ROOT, THUMBNAIL_IMAGE_ORIGINAL_SIZE_LIMIT,\
ENABLE_VIDEO_THUMBNAIL, THUMBNAIL_VIDEO_FRAME_TIME ENABLE_VIDEO_THUMBNAIL, THUMBNAIL_VIDEO_FRAME_TIME
@ -117,7 +118,10 @@ def generate_thumbnail(request, repo_id, size, path):
thumbnail_file, file_size) thumbnail_file, file_size)
else: else:
return (False, 400) return (False, 400)
if filetype == PDF:
# pdf thumbnails
return create_pdf_thumbnails(repo, file_id, path, size,
thumbnail_file, file_size)
if filetype == XMIND: if filetype == XMIND:
return extract_xmind_image(repo_id, path, size) return extract_xmind_image(repo_id, path, size)
@ -181,6 +185,41 @@ def create_psd_thumbnails(repo, file_id, path, size, thumbnail_file, file_size):
os.unlink(tmp_img_path) os.unlink(tmp_img_path)
return (False, 500) return (False, 500)
def create_pdf_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)
if not token:
return (False, 500)
inner_path = gen_inner_file_get_url(token, os.path.basename(path))
tmp_path = str(os.path.join(tempfile.gettempdir(), '%s.png' % file_id[:8]))
pdf_file = urllib.request.urlopen(inner_path)
pdf_stream = BytesIO(pdf_file.read())
try:
pdf_doc = fitz_open(stream=pdf_stream)
pdf_stream.close()
page = pdf_doc[0]
pix = page.get_pixmap()
pix.save(tmp_path)
pdf_doc.close()
except Exception as e:
logger.error(e)
return (False, 500)
t2 = timeit.default_timer()
logger.debug('Create PDF 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_video_thumbnails(repo, file_id, path, size, thumbnail_file, file_size): def create_video_thumbnails(repo, file_id, path, size, thumbnail_file, file_size):
t1 = timeit.default_timer() t1 = timeit.default_timer()

View File

@ -1089,6 +1089,7 @@ def react_fake_view(request, **kwargs):
'ocm_remote_servers': OCM_REMOTE_SERVERS, 'ocm_remote_servers': OCM_REMOTE_SERVERS,
'enable_share_to_department': settings.ENABLE_SHARE_TO_DEPARTMENT, 'enable_share_to_department': settings.ENABLE_SHARE_TO_DEPARTMENT,
'enable_video_thumbnail': settings.ENABLE_VIDEO_THUMBNAIL, 'enable_video_thumbnail': settings.ENABLE_VIDEO_THUMBNAIL,
'enable_pdf_thumbnail': settings.ENABLE_PDF_THUMBNAIL,
'group_import_members_extra_msg': GROUP_IMPORT_MEMBERS_EXTRA_MSG, 'group_import_members_extra_msg': GROUP_IMPORT_MEMBERS_EXTRA_MSG,
'request_from_onlyoffice_desktop_editor': ONLYOFFICE_DESKTOP_EDITOR_HTTP_USER_AGENT in request.headers.get('user-agent', ''), 'request_from_onlyoffice_desktop_editor': ONLYOFFICE_DESKTOP_EDITOR_HTTP_USER_AGENT in request.headers.get('user-agent', ''),
'enable_sso_to_thirdpart_website': settings.ENABLE_SSO_TO_THIRDPART_WEBSITE, 'enable_sso_to_thirdpart_website': settings.ENABLE_SSO_TO_THIRDPART_WEBSITE,

View File

@ -765,6 +765,8 @@ def view_lib_file(request, repo_id, path):
send_file_access_msg(request, repo, path, 'web') send_file_access_msg(request, repo, path, 'web')
if filetype == VIDEO: if filetype == VIDEO:
return_dict['enable_video_thumbnail'] = settings.ENABLE_VIDEO_THUMBNAIL return_dict['enable_video_thumbnail'] = settings.ENABLE_VIDEO_THUMBNAIL
if filetype == PDF:
return_dict['enable_pdf_thumbnail'] = settings.ENABLE_PDF_THUMBNAIL
return render(request, template, return_dict) return render(request, template, return_dict)
elif filetype == XMIND: elif filetype == XMIND:

View File

@ -31,7 +31,7 @@ from seahub.settings import ENABLE_UPLOAD_FOLDER, \
THUMBNAIL_ROOT, THUMBNAIL_DEFAULT_SIZE, THUMBNAIL_SIZE_FOR_GRID, \ THUMBNAIL_ROOT, THUMBNAIL_DEFAULT_SIZE, THUMBNAIL_SIZE_FOR_GRID, \
MAX_NUMBER_OF_FILES_FOR_FILEUPLOAD, SHARE_LINK_EXPIRE_DAYS_MIN, \ MAX_NUMBER_OF_FILES_FOR_FILEUPLOAD, SHARE_LINK_EXPIRE_DAYS_MIN, \
SHARE_LINK_EXPIRE_DAYS_MAX, SEAFILE_COLLAB_SERVER, \ SHARE_LINK_EXPIRE_DAYS_MAX, SEAFILE_COLLAB_SERVER, \
ENABLE_SHARE_LINK_REPORT_ABUSE ENABLE_SHARE_LINK_REPORT_ABUSE, ENABLE_PDF_THUMBNAIL
from seahub.utils.file_types import IMAGE, VIDEO, XMIND from seahub.utils.file_types import IMAGE, VIDEO, XMIND
from seahub.thumbnail.utils import get_share_link_thumbnail_src from seahub.thumbnail.utils import get_share_link_thumbnail_src
from seahub.group.utils import is_group_admin from seahub.group.utils import is_group_admin
@ -363,6 +363,7 @@ def view_shared_dir(request, fileshare):
'desc_for_ogp': desc_for_ogp, 'desc_for_ogp': desc_for_ogp,
'enable_share_link_report_abuse': ENABLE_SHARE_LINK_REPORT_ABUSE, 'enable_share_link_report_abuse': ENABLE_SHARE_LINK_REPORT_ABUSE,
'enable_video_thumbnail': ENABLE_VIDEO_THUMBNAIL, 'enable_video_thumbnail': ENABLE_VIDEO_THUMBNAIL,
'enable_pdf_thumbnail': ENABLE_PDF_THUMBNAIL,
}) })

View File

@ -7,3 +7,4 @@ splinter
pytest==7.4.4 pytest==7.4.4
pytest-django pytest-django
selenium selenium
PyMuPDF==1.24.3