187 lines
5.3 KiB
TypeScript
187 lines
5.3 KiB
TypeScript
import type { DriveFileInfo } from "@/lib/api/types"
|
|
import { isOnlyOfficeFile, isOnlyOfficeExtension, fileExtension as officeFileExtension } from "@/lib/drive/onlyoffice-formats"
|
|
import { isRichTextFile, isUltidocPath } from "@/lib/drive/richtext-formats"
|
|
import { isExcalidrawFile } from "@/lib/drive/ultidraw-formats"
|
|
|
|
export type DrivePreviewKind = "image" | "video" | "audio" | "pdf" | "text"
|
|
|
|
const IMAGE_EXT = new Set([
|
|
"jpg",
|
|
"jpeg",
|
|
"png",
|
|
"gif",
|
|
"webp",
|
|
"svg",
|
|
"bmp",
|
|
"avif",
|
|
"heic",
|
|
"heif",
|
|
"ico",
|
|
"tif",
|
|
"tiff",
|
|
"apng",
|
|
"jfif",
|
|
])
|
|
|
|
const VIDEO_EXT = new Set(["mp4", "webm", "mov", "mkv", "ogv", "m4v", "3gp", "avi"])
|
|
|
|
const AUDIO_EXT = new Set(["mp3", "wav", "ogg", "flac", "m4a", "aac", "opus", "weba", "aiff", "mid", "midi"])
|
|
|
|
const TEXT_EXT = new Set([
|
|
"md",
|
|
"markdown",
|
|
"txt",
|
|
"json",
|
|
"yaml",
|
|
"yml",
|
|
"log",
|
|
"ini",
|
|
"conf",
|
|
"cfg",
|
|
"env",
|
|
])
|
|
|
|
function isTextMime(mime: string): boolean {
|
|
if (!mime.startsWith("text/")) return false
|
|
if (mime.includes("html") || mime.includes("calendar")) return false
|
|
return true
|
|
}
|
|
|
|
function fileExt(name: string): string {
|
|
return name.split(".").pop()?.toLowerCase() ?? ""
|
|
}
|
|
|
|
export function isSvgFile(file: { name: string; mime_type?: string }): boolean {
|
|
const mime = (file.mime_type ?? "").toLowerCase()
|
|
return mime === "image/svg+xml" || mime === "application/svg+xml" || fileExt(file.name) === "svg"
|
|
}
|
|
|
|
/** Formats the browser renders from the original file — skip Nextcloud raster preview. */
|
|
export function driveClientNativePreview(file: {
|
|
name: string
|
|
mime_type?: string
|
|
type?: string
|
|
}): boolean {
|
|
if (file.type === "directory") return false
|
|
const kind = drivePreviewKind(file)
|
|
if (!kind) return false
|
|
if (kind === "video" || kind === "audio" || kind === "pdf" || kind === "text") return true
|
|
if (kind === "image" && isSvgFile(file)) return true
|
|
return false
|
|
}
|
|
|
|
export function isOfficeFormat(file: { name: string; mime_type?: string }): boolean {
|
|
return isOnlyOfficeFile(file)
|
|
}
|
|
|
|
/** Word-processor formats edited in TipTap (when rich-text editor is enabled). */
|
|
export function shouldOpenInRichTextEditor(file: {
|
|
name: string
|
|
mime_type?: string
|
|
type?: string
|
|
}): boolean {
|
|
if (file.type === "directory") return false
|
|
if (isUltidocPath(file.name)) return true
|
|
if (drivePreviewKind(file) === "text") {
|
|
const ext = officeFileExtension(file.name)
|
|
return ext === "md" || ext === "markdown" || ext === "txt" || ext === "html" || ext === "htm"
|
|
}
|
|
if (drivePreviewKind(file)) return false
|
|
return isRichTextFile(file)
|
|
}
|
|
|
|
/** Excalidraw drawings edited in UltiDraw. */
|
|
export function shouldOpenInUltidrawEditor(file: {
|
|
name: string
|
|
mime_type?: string
|
|
type?: string
|
|
}): boolean {
|
|
if (file.type === "directory") return false
|
|
return isExcalidrawFile(file)
|
|
}
|
|
|
|
/** Open in OnlyOffice for cell/slide/diagram only (word formats use TipTap). */
|
|
export function shouldOpenInOnlyOffice(file: {
|
|
name: string
|
|
mime_type?: string
|
|
type?: string
|
|
}): boolean {
|
|
if (file.type === "directory") return false
|
|
if (shouldOpenInUltidrawEditor(file)) return false
|
|
if (shouldOpenInRichTextEditor(file)) return false
|
|
if (drivePreviewKind(file)) return false
|
|
if (!isOfficeFormat(file)) return false
|
|
const ext = officeFileExtension(file.name)
|
|
return isOnlyOfficeExtension(ext) && !isRichTextFile(file)
|
|
}
|
|
|
|
export function drivePreviewKind(file: {
|
|
name: string
|
|
mime_type?: string
|
|
}): DrivePreviewKind | null {
|
|
const mime = (file.mime_type ?? "").toLowerCase()
|
|
if (mime.startsWith("image/") || mime === "application/svg+xml") return "image"
|
|
if (mime.startsWith("video/")) return "video"
|
|
if (mime.startsWith("audio/")) return "audio"
|
|
if (mime === "application/pdf") return "pdf"
|
|
if (isTextMime(mime)) return "text"
|
|
if (mime === "application/json" || mime === "application/x-yaml") return "text"
|
|
|
|
const ext = fileExt(file.name)
|
|
if (IMAGE_EXT.has(ext)) return "image"
|
|
if (VIDEO_EXT.has(ext)) return "video"
|
|
if (AUDIO_EXT.has(ext)) return "audio"
|
|
if (ext === "pdf") return "pdf"
|
|
if (TEXT_EXT.has(ext)) return "text"
|
|
return null
|
|
}
|
|
|
|
/** Files that can use Nextcloud server-side preview thumbnails. */
|
|
export function driveServerThumbnail(file: { name: string; mime_type?: string; type?: string }): boolean {
|
|
if (file.type === "directory") return false
|
|
if (isOfficeFormat(file)) return true
|
|
const kind = drivePreviewKind(file)
|
|
if (kind === "pdf") return true
|
|
if (kind === "image" && !isSvgFile(file)) return true
|
|
return false
|
|
}
|
|
|
|
export function isPreviewNavigable(file: {
|
|
name: string
|
|
mime_type?: string
|
|
type?: string
|
|
}): boolean {
|
|
return file.type !== "directory" && drivePreviewKind(file) !== null
|
|
}
|
|
|
|
export function previewTargetToFileInfo(target: {
|
|
path: string
|
|
name: string
|
|
mime_type: string
|
|
is_favorite: boolean
|
|
}): DriveFileInfo {
|
|
return {
|
|
path: target.path,
|
|
name: target.name,
|
|
type: "file",
|
|
size: 0,
|
|
mime_type: target.mime_type,
|
|
last_modified: "",
|
|
etag: "",
|
|
is_favorite: target.is_favorite,
|
|
}
|
|
}
|
|
export function toPreviewTarget(file: {
|
|
path: string
|
|
name: string
|
|
mime_type?: string
|
|
is_favorite?: boolean
|
|
}): { path: string; name: string; mime_type: string; is_favorite: boolean } {
|
|
return {
|
|
path: file.path,
|
|
name: file.name,
|
|
mime_type: file.mime_type ?? "",
|
|
is_favorite: file.is_favorite ?? false,
|
|
}
|
|
}
|