From 3adbce88418183aedae7612f38be61f58c0781cc Mon Sep 17 00:00:00 2001 From: purocean Date: Thu, 2 Jan 2025 14:02:47 +0800 Subject: [PATCH] feat(media-player): add media player plugin for audio and video files --- src/renderer/plugins.ts | 2 + src/renderer/plugins/media-player.ts | 84 ++++++++++++++++++++++++++++ 2 files changed, 86 insertions(+) create mode 100644 src/renderer/plugins/media-player.ts diff --git a/src/renderer/plugins.ts b/src/renderer/plugins.ts index 701fbb9c..f01629b2 100644 --- a/src/renderer/plugins.ts +++ b/src/renderer/plugins.ts @@ -32,6 +32,7 @@ import editorRestoreState from '@fe/plugins/editor-restore-state' import copyText from '@fe/plugins/copy-text' import switchTodo from '@fe/plugins/switch-todo' import imageViewer from '@fe/plugins/image-viewer' +import mediaPlayer from '@fe/plugins/media-player' import emoji from '@fe/plugins/emoji' import getStarted from '@fe/plugins/get-started' import markdownWikiLinks from '@fe/plugins/markdown-wiki-links' @@ -111,6 +112,7 @@ export default [ copyText, switchTodo, imageViewer, + mediaPlayer, emoji, getStarted, markdownWikiLinks, diff --git a/src/renderer/plugins/media-player.ts b/src/renderer/plugins/media-player.ts new file mode 100644 index 00000000..4230de52 --- /dev/null +++ b/src/renderer/plugins/media-player.ts @@ -0,0 +1,84 @@ +import { defineComponent, h, ref, watchEffect } from 'vue' +import mime from 'mime' +import type { Plugin } from '@fe/context' +import store from '@fe/support/store' +import { getAttachmentURL } from '@fe/services/base' +import { sleep } from '@fe/utils' +import type { Doc } from '@fe/types' + +import 'viewerjs/dist/viewer.css' +import { isElectron } from '@fe/support/env' + +function isMediaFile (doc?: Doc | null): 'video' | 'audio' | false { + if (!doc) { + return false + } + + if (doc.plain) { + return false + } + + const fileMime = mime.getType(doc.name) + if (!fileMime) { + return false + } + + if (fileMime.startsWith('video/')) { + return 'video' + } + + if (fileMime.startsWith('audio/')) { + return 'audio' + } + + return false +} + +const MediaPlayer = defineComponent({ + setup () { + const src = ref('') + const type = ref<'video' | 'audio' | false>(false) + + watchEffect(async () => { + const doc = store.state.currentFile + type.value = false + src.value = '' + await sleep(0) + + if (doc) { + type.value = isMediaFile(doc) + + if (isElectron && doc.absolutePath) { + src.value = 'file://' + doc.absolutePath + } else { + src.value = getAttachmentURL(doc) + } + } + }) + + return () => src.value ? h( + 'div', + { style: 'width: 100%; height: 100%; display: flex; align-items: center; justify-content: center' }, + type.value === 'video' + ? h('video', { src: src.value, controls: true, disablePictureInPicture: true, style: 'width: 100%; height: 100%; object-fit: contain' }) + : type.value === 'audio' + ? h('audio', { src: src.value, controls: true }) + : '' + ) : null + } +}) + +export default { + name: 'media-player', + register: ctx => { + ctx.editor.registerCustomEditor({ + name: 'image-player', + displayName: 'Image Viewer', + hiddenPreview: true, + when ({ doc }) { + return !!isMediaFile(doc) + }, + component: MediaPlayer, + }) + } +} as Plugin