返回 oh-my-ppt
html-editor-media.ts
根目录 / src / main / html-editor / html-editor-media.ts
1 import fs from 'fs'
2 import path from 'path'
3 import { pathToFileURL } from 'node:url'
4 import { nanoid } from 'nanoid'
5
6 export type HtmlEditorMediaType = 'image' | 'video'
7
8 const MEDIA_EXTENSIONS: Record<HtmlEditorMediaType, ReadonlySet<string>> = {
9 image: new Set(['.png', '.jpg', '.jpeg', '.webp', '.gif', '.svg']),
10 video: new Set(['.mp4', '.webm', '.ogg', '.ogv'])
11 }
12
13 export function isSupportedHtmlEditorMediaFile(
14 mediaType: HtmlEditorMediaType,
15 sourcePath: string
16 ): boolean {
17 return MEDIA_EXTENSIONS[mediaType].has(path.extname(sourcePath).toLowerCase())
18 }
19
20 export function getHtmlEditorMediaExtensions(mediaType: HtmlEditorMediaType): string[] {
21 return [...MEDIA_EXTENSIONS[mediaType]].map((extension) => extension.slice(1))
22 }
23
24 export interface HtmlEditorMediaAsset {
25 fileName: string
26 filePath: string
27 relativePath: string
28 url: string
29 }
30
31 function getMediaDir(workspaceDir: string, mediaType: HtmlEditorMediaType): string {
32 return path.join(workspaceDir, 'assets', mediaType === 'video' ? 'videos' : 'images')
33 }
34
35 export async function listHtmlEditorMedia(input: {
36 workspaceDir: string
37 mediaType: HtmlEditorMediaType
38 }): Promise<HtmlEditorMediaAsset[]> {
39 const mediaDir = getMediaDir(input.workspaceDir, input.mediaType)
40 let entries: fs.Dirent[]
41 try {
42 entries = await fs.promises.readdir(mediaDir, { withFileTypes: true })
43 } catch (error) {
44 if ((error as NodeJS.ErrnoException).code === 'ENOENT') return []
45 throw error
46 }
47
48 return entries
49 .filter(
50 (entry) =>
51 entry.isFile() &&
52 !entry.name.startsWith('.') &&
53 isSupportedHtmlEditorMediaFile(input.mediaType, entry.name)
54 )
55 .sort((a, b) => a.name.localeCompare(b.name))
56 .map((entry) => {
57 const filePath = path.join(mediaDir, entry.name)
58 return {
59 fileName: entry.name,
60 filePath,
61 relativePath: path.relative(input.workspaceDir, filePath).split(path.sep).join('/'),
62 url: pathToFileURL(filePath).href
63 }
64 })
65 }
66
67 export async function importHtmlEditorMedia(input: {
68 workspaceDir: string
69 sourcePath: string
70 mediaType: HtmlEditorMediaType
71 }): Promise<{ filePath: string; relativePath: string; url: string }> {
72 const { workspaceDir, sourcePath, mediaType } = input
73 if (!isSupportedHtmlEditorMediaFile(mediaType, sourcePath)) {
74 throw new Error(mediaType === 'video' ? '不支持的视频格式' : '不支持的图片格式')
75 }
76
77 const sourceStat = await fs.promises.stat(sourcePath)
78 if (!sourceStat.isFile()) throw new Error('所选媒体文件不可用')
79
80 const extension = path.extname(sourcePath).toLowerCase()
81 const originalName = path.basename(sourcePath, path.extname(sourcePath)).trim() || mediaType
82 const mediaDir = getMediaDir(workspaceDir, mediaType)
83 const fileName = `${originalName}-${nanoid(8)}${extension}`
84 const filePath = path.join(mediaDir, fileName)
85
86 await fs.promises.mkdir(mediaDir, { recursive: true })
87 await fs.promises.copyFile(sourcePath, filePath)
88
89 return {
90 filePath,
91 relativePath: path.relative(workspaceDir, filePath).split(path.sep).join('/'),
92 url: pathToFileURL(filePath).href
93 }
94 }
95
95 lines TYPESCRIPT