From abf09b4593ffec927ab39e4b1661b694d4a11f4c Mon Sep 17 00:00:00 2001 From: Aries Date: Tue, 18 Mar 2025 17:58:43 +0800 Subject: [PATCH] show video thumbnail (#7639) * show video thumbnail * check preview permission --------- Co-authored-by: zhouwenxuan --- frontend/src/metadata/context.js | 11 +++++ frontend/src/metadata/utils/file.js | 1 - frontend/src/metadata/views/gallery/main.js | 44 +++++++++++++++---- .../views/kanban/boards/board/card/index.js | 2 + .../src/metadata/views/kanban/boards/index.js | 2 + .../table/masks/interaction-masks/index.js | 1 + .../table-main/records/record/cell/index.js | 2 + .../file-name-operation-btn/index.js | 2 + frontend/src/tag/context.js | 11 +++++ .../src/tag/views/tag-files/tag-file/index.js | 3 +- 10 files changed, 69 insertions(+), 10 deletions(-) diff --git a/frontend/src/metadata/context.js b/frontend/src/metadata/context.js index b326bb75c6..600b703223 100644 --- a/frontend/src/metadata/context.js +++ b/frontend/src/metadata/context.js @@ -7,6 +7,7 @@ import { import LocalStorage from './utils/local-storage'; import EventBus from '../components/common/event-bus'; import { username, lang } from '../utils/constants'; +import { Utils } from '../utils/utils'; class Context { @@ -41,6 +42,8 @@ class Context { this.eventBus = eventBus; this.permission = repoInfo.permission !== 'admin' && repoInfo.permission !== 'rw' ? 'r' : 'rw'; + const { isCustomPermission, customPermission } = Utils.getUserPermission(repoInfo.permission); + this.customPermission = isCustomPermission ? customPermission : null; this.hasInit = true; } @@ -187,6 +190,14 @@ class Context { return true; }; + canPreview = () => { + if (this.customPermission) { + const { preview, modify } = this.customPermission.permission; + return preview || modify; + } + return true; + }; + restoreRows = () => { // todo }; diff --git a/frontend/src/metadata/utils/file.js b/frontend/src/metadata/utils/file.js index d9098be10a..41bf819f54 100644 --- a/frontend/src/metadata/utils/file.js +++ b/frontend/src/metadata/utils/file.js @@ -101,7 +101,6 @@ export const openFile = (repoID, record, _openImage = () => {}) => { }; export const openInNewTab = (repoID, record) => { - if (!record) return; const isDir = checkIsDir(record); const fileName = getFileNameFromRecord(record); const parentDir = _getParentDir(record); diff --git a/frontend/src/metadata/views/gallery/main.js b/frontend/src/metadata/views/gallery/main.js index 08d552710b..11666d14d9 100644 --- a/frontend/src/metadata/views/gallery/main.js +++ b/frontend/src/metadata/views/gallery/main.js @@ -8,12 +8,13 @@ import GalleryContextmenu from './context-menu'; import { useMetadataView } from '../../hooks/metadata-view'; import { Utils } from '../../../utils/utils'; import { getDateDisplayString, getFileMTimeFromRecord, getFileNameFromRecord, getParentDirFromRecord, getRecordIdFromRecord } from '../../utils/cell'; -import { siteRoot, fileServerRoot, thumbnailSizeForGrid, thumbnailSizeForOriginal, thumbnailDefaultSize } from '../../../utils/constants'; +import { siteRoot, fileServerRoot, thumbnailSizeForGrid, thumbnailSizeForOriginal, thumbnailDefaultSize, enableVideoThumbnail } from '../../../utils/constants'; import { EVENT_BUS_TYPE, GALLERY_DATE_MODE, DATE_TAG_HEIGHT, STORAGE_GALLERY_DATE_MODE_KEY, STORAGE_GALLERY_ZOOM_GEAR_KEY, VIEW_TYPE_DEFAULT_SORTS, VIEW_TYPE } from '../../constants'; import { getRowById } from '../../../components/sf-table/utils/table'; import { getEventClassName } from '../../../utils/dom'; import { getColumns, getImageSize, getRowHeight } from './utils'; import ObjectUtils from '../../../utils/object'; +import { openFile } from '../../utils/file'; import './index.css'; @@ -36,13 +37,17 @@ const Main = ({ isLoadingMore, metadata, onDelete, onLoadMore, duplicateRecord, const { repoID, updateCurrentDirent } = useMetadataView(); const repoInfo = window.sfMetadataContext.getSetting('repoInfo'); + const canPreview = window.sfMetadataContext.canPreview(); const images = useMemo(() => { if (isFirstLoading) return []; if (!Array.isArray(metadata.rows) || metadata.rows.length === 0) return []; const firstSort = metadata.view.sorts[0] || VIEW_TYPE_DEFAULT_SORTS[VIEW_TYPE.GALLERY]; return metadata.rows - .filter(record => Utils.imageCheck(getFileNameFromRecord(record))) + .filter(record => { + const fileName = getFileNameFromRecord(record); + return Utils.imageCheck(fileName) || Utils.videoCheck(fileName); + }) .map(record => { const id = getRecordIdFromRecord(record); const fileName = getFileNameFromRecord(record); @@ -59,13 +64,32 @@ const Main = ({ isLoadingMore, metadata, onDelete, onLoadMore, duplicateRecord, const year = date.slice(0, 4); const month = date.slice(0, -3); const day = date.slice(-2,); + + const isVideo = Utils.videoCheck(fileName); + const useFallbackIcon = isVideo && !enableVideoThumbnail; + + const baseThumbnailPath = `${siteRoot}thumbnail/${repoID}`; + let src = useFallbackIcon + ? Utils.getFileIconUrl(fileName) + : `${baseThumbnailPath}/${size}${path}`; + + let thumbnail = useFallbackIcon + ? Utils.getFileIconUrl(fileName) + : `${baseThumbnailPath}/${thumbnailSizeForOriginal}${path}?mtime=${mtime}`; + + if (!canPreview) { + const fileIcon = Utils.getFileIconUrl(fileName); + src = fileIcon; + thumbnail = fileIcon; + } + return { id, name: fileName, parentDir, url: `${siteRoot}lib/${repoID}/file${path}`, - src: `${siteRoot}thumbnail/${repoID}/${size}${path}`, - thumbnail: `${siteRoot}thumbnail/${repoID}/${thumbnailSizeForOriginal}${path}?mtime=${mtime}`, + src, + thumbnail, downloadURL: `${fileServerRoot}repos/${repoID}/files${path}?op=download`, year, month, @@ -192,10 +216,14 @@ const Main = ({ isLoadingMore, metadata, onDelete, onLoadMore, duplicateRecord, const handleDoubleClick = useCallback((event, image) => { event.preventDefault(); - const index = images.findIndex(item => item.id === image.id); - setImageIndex(index); - setIsImagePopupOpen(true); - }, [images]); + const record = getRowById(metadata, image.id); + if (!canPreview) return; + openFile(repoID, record, () => { + const index = images.findIndex(item => item.id === image.id); + setImageIndex(index); + setIsImagePopupOpen(true); + }); + }, [repoID, metadata, images]); const handleContextMenu = useCallback((event, image) => { event.preventDefault(); diff --git a/frontend/src/metadata/views/kanban/boards/board/card/index.js b/frontend/src/metadata/views/kanban/boards/board/card/index.js index bbedc3993e..068ab8293a 100644 --- a/frontend/src/metadata/views/kanban/boards/board/card/index.js +++ b/frontend/src/metadata/views/kanban/boards/board/card/index.js @@ -31,6 +31,8 @@ const Card = ({ if (titleColumn?.type !== CellType.FILE_NAME) return; event.stopPropagation(); event.nativeEvent.stopImmediatePropagation(); + const canPreview = window.sfMetadataContext.canPreview(); + if (!canPreview) return; onOpenFile(record); }, [titleColumn, record, onOpenFile]); diff --git a/frontend/src/metadata/views/kanban/boards/index.js b/frontend/src/metadata/views/kanban/boards/index.js index 78c20931ee..d973f37543 100644 --- a/frontend/src/metadata/views/kanban/boards/index.js +++ b/frontend/src/metadata/views/kanban/boards/index.js @@ -182,6 +182,8 @@ const Boards = ({ modifyRecord, deleteRecords, modifyColumnData, onCloseSettings const onOpenFile = useCallback((record) => { const repoID = window.sfMetadataContext.getSetting('repoID'); + const canPreview = window.sfMetadataContext.canPreview(); + if (!canPreview) return; openFile(repoID, record, () => { currentImageRef.current = record; setImagePreviewerVisible(true); diff --git a/frontend/src/metadata/views/table/masks/interaction-masks/index.js b/frontend/src/metadata/views/table/masks/interaction-masks/index.js index d8326938d8..efb7e27a45 100644 --- a/frontend/src/metadata/views/table/masks/interaction-masks/index.js +++ b/frontend/src/metadata/views/table/masks/interaction-masks/index.js @@ -451,6 +451,7 @@ class InteractionMasks extends React.Component { }; handleSpaceKeyDown = (e) => { + e.preventDefault(); e.stopPropagation(); e.nativeEvent.stopImmediatePropagation(); const repoID = window.sfMetadataContext.getSetting('repoID'); diff --git a/frontend/src/metadata/views/table/table-main/records/record/cell/index.js b/frontend/src/metadata/views/table/table-main/records/record/cell/index.js index 20d3504399..c311ebbb3d 100644 --- a/frontend/src/metadata/views/table/table-main/records/record/cell/index.js +++ b/frontend/src/metadata/views/table/table-main/records/record/cell/index.js @@ -154,6 +154,8 @@ const Cell = React.memo(({ event.nativeEvent.stopImmediatePropagation(); if (!isCellSelected) return; const repoID = window.sfMetadataContext.getSetting('repoID'); + const canPreview = window.sfMetadataContext.canPreview(); + if (!canPreview) return; openFile(repoID, record, () => { window.sfMetadataContext.eventBus.dispatch(EVENT_BUS_TYPE.OPEN_EDITOR, EDITOR_TYPE.PREVIEWER); }); diff --git a/frontend/src/metadata/views/table/table-main/records/record/cell/operation-btn/file-name-operation-btn/index.js b/frontend/src/metadata/views/table/table-main/records/record/cell/operation-btn/file-name-operation-btn/index.js index a46af66b69..623831453f 100644 --- a/frontend/src/metadata/views/table/table-main/records/record/cell/operation-btn/file-name-operation-btn/index.js +++ b/frontend/src/metadata/views/table/table-main/records/record/cell/operation-btn/file-name-operation-btn/index.js @@ -22,6 +22,8 @@ const FileNameOperationBtn = ({ column, record, ...props }) => { event.stopPropagation(); event.nativeEvent.stopImmediatePropagation(); const repoID = window.sfMetadataContext.getSetting('repoID'); + const canPreview = window.sfMetadataContext.canPreview(); + if (!canPreview) return; openFile(repoID, record, () => { window.sfMetadataContext.eventBus.dispatch(METADATA_EVENT_BUS_TYPE.OPEN_EDITOR, EDITOR_TYPE.PREVIEWER); }); diff --git a/frontend/src/tag/context.js b/frontend/src/tag/context.js index 243c33ba4d..6c1e2a17a5 100644 --- a/frontend/src/tag/context.js +++ b/frontend/src/tag/context.js @@ -2,6 +2,7 @@ import tagsAPI from './api'; import LocalStorage from '../metadata/utils/local-storage'; import EventBus from '../components/common/event-bus'; import { username, lang } from '../utils/constants'; +import { Utils } from '../utils/utils'; class Context { @@ -36,6 +37,8 @@ class Context { this.eventBus = eventBus; this.permission = repoInfo.permission !== 'admin' && repoInfo.permission !== 'rw' ? 'r' : 'rw'; + const { isCustomPermission, customPermission } = Utils.getUserPermission(repoInfo.permission); + this.customPermission = isCustomPermission ? customPermission : null; this.hasInit = true; } @@ -72,6 +75,14 @@ class Context { return true; }; + canPreview = () => { + if (this.customPermission) { + const { preview, modify } = this.customPermission.permission; + return preview || modify; + } + return true; + }; + canAddTag = () => { if (this.permission === 'r') return false; return true; diff --git a/frontend/src/tag/views/tag-files/tag-file/index.js b/frontend/src/tag/views/tag-files/tag-file/index.js index 3e7772b975..36d32db871 100644 --- a/frontend/src/tag/views/tag-files/tag-file/index.js +++ b/frontend/src/tag/views/tag-files/tag-file/index.js @@ -76,7 +76,8 @@ const TagFile = ({ isSelected, repoID, file, tagsData, onSelectFile, openImagePr const handelClickFileName = useCallback((event) => { event.preventDefault(); - if (isRenameing) return; + const canPreview = window.sfTagsDataContext.canPreview(); + if (isRenameing || !canPreview) return; openFile(repoID, file, () => { openImagePreview(file); });