OverviewFilesCommitsIssuesDocsReleasesPeers

keet-deobfuscated / @holepunchto / keet-store / store / media / file / file.model.js

163 lines · javascript ref:
import {
  isAudioMimeType as e,
  isAudioVoicePath as t,
  base64ToWaveform as r,
  waveformToBase64 as i,
} from "../../lib/file"
import {
  FILE_PREVIEW_MAX_BYTES_MEDIUM as n,
  FILE_PREVIEW_MAX_BYTES_SMALL as o,
  FILE_PREVIEW_MAX_FRAMES_MEDIUM as a,
  FILE_PREVIEW_MAX_FRAMES_SMALL as l,
  FILE_PREVIEW_MIME_TYPE as s,
  FILE_PREVIEW_SIZE_LARGE as m,
  FILE_PREVIEW_SIZE_MEDIUM as u,
  FILE_PREVIEW_SIZE_SMALL as p,
  FILE_VOICE_MIME_TYPE as h,
} from "./file.constants"
export const getFileId = (e) => {
  if (!e) return ""
  const { externalBlob: t } = e
  if (t) return `${t.id}_${t.blob?.blockOffset}`
  const { externalDrive: r } = e
  return r ? `${r.id}_${r.path}_${r.version}` : ""
}
export const getFileByteLength = (e) => e?.externalBlob?.blob?.byteLength || 0
export const PREVIEW_SIZE = { SMALL: "small", MEDIUM: "medium", LARGE: "large" }
export const PREVIEW_SIZE_VALUES = Object.values(PREVIEW_SIZE)
export const PREVIEW_PARAMS_BY_SIZE = {
  small: { maxHeight: p, maxWidth: p, maxBytes: o, maxFrames: l },
  medium: { maxHeight: u, maxWidth: u, maxBytes: n, maxFrames: a },
}
export const PREVIEW_PARAMS_DEFAULT = { maxHeight: m, maxWidth: m }
export function flattenFileWaveForm(i) {
  return i && i.userdata && e(i.mimetype) && t(i.name) ? r(i.userdata) : null
}
export const getMaxDimension = (e) => (e ? Math.max(e.height || 0, e.width || 0) : 0)
export function getShouldOverridePreviewPointer(e, t) {
  if (e.mimetype !== s) return !1
  const { maxHeight: r } = PREVIEW_PARAMS_BY_SIZE[t] || PREVIEW_PARAMS_DEFAULT
  return r > getMaxDimension(e)
}
export function flattenFileSource(e, t) {
  const r = e.pointer?.inlined
  return r ? `data:${e.metadata?.mimetype};base64,${r}` : t?.httpLink
}
export const getIsVoiceNote = (e, t) => Boolean(h === e && t?.startsWith("recording-"))
export function flattenFileMetadata(e, t) {
  if (!e || !e.metadata) return null
  const { pointer: r, metadata: i } = e,
    { own: n = !1, cleared: o = !1 } = t || {},
    {
      dimensions: a,
      byteLength: l = getFileByteLength(r),
      name: s = "",
      mimetype: m = "application/octet-stream",
      duration: u = 0,
      isPreview: p,
    } = i,
    h = getFileId(r),
    c = getIsVoiceNote(m, s)
  return {
    isPreview: p,
    width: a?.width,
    height: a?.height,
    name: s,
    own: n,
    duration: u,
    byteLength: l,
    mimetype: m,
    id: h,
    pointer: r,
    httpLink: flattenFileSource(e, t),
    cleared: o,
    isVoiceNote: c,
  }
}
export function flattenFilePreviews(e) {
  const t = e.preview,
    r = e.entry?.preview || {}
  return t
    ? {
        small: flattenFileMetadata(t.small, r.small),
        medium: flattenFileMetadata(t.medium, r.medium),
        large: flattenFileMetadata(t.large, r.large),
        audio: flattenFileWaveForm(e.metadata),
      }
    : {}
}
export function flattenFile(e) {
  return e ? { ...flattenFileMetadata(e, e.entry), preview: flattenFilePreviews(e) } : null
}
export function restoreFileCoreMetadata(e) {
  if (!e) return null
  const t = {}
  for (const r of Object.keys(e))
    ["name", "mimetype", "duration", "byteLength", "isPreview"].includes(r) && (t[r] = e[r])
  return (
    e.preview?.audio && (t.userdata = i(e.preview.audio)),
    e.height && e.width && (t.dimensions = { height: e.height, width: e.width }),
    t
  )
}
export function restoreFileCoreEntry(e) {
  if (!e) return null
  const { cleared: t, httpLink: r, own: i, byteLength: n } = e
  return { cleared: t, httpLink: r, own: i, byteLength: n }
}
export function restoreCoreFileWithoutPreview(e) {
  return e
    ? { pointer: e.pointer, metadata: restoreFileCoreMetadata(e), entry: restoreFileCoreEntry(e) }
    : null
}
export function restoreFileCorePreview(e) {
  const t = {}
  if (!e?.preview) return t
  const { preview: r } = e
  return (
    r.small && (t.small = restoreCoreFileWithoutPreview(r.small)),
    r.medium && (t.medium = restoreCoreFileWithoutPreview(r.small)),
    r.large && (t.large = restoreCoreFileWithoutPreview(r.small)),
    t
  )
}
export function restoreCoreFile(e) {
  return e ? { ...restoreCoreFileWithoutPreview(e), preview: restoreFileCorePreview(e) } : null
}
export function getHttpLinkPreview(e, t) {
  return e?.preview?.[t]?.httpLink
}
export function parseCoreFile(e) {
  if (e) {
    if (!e.pointer) throw new Error("parseCoreFile: no pointer")
    return flattenFile(e)
  }
}
export const getCanPreviewFile = (e) =>
  Boolean(
    "number" == typeof e.height &&
    "number" == typeof e.width &&
    ["image", "video"].includes(e.mimetype.split("/")[0] || ""),
  )
export const isFileEntryChanged = (e, t) => {
  if (!e || !t) return !!e || !!t
  if (e.width !== t.width || e.height !== t.height || e.mimetype !== t.mimetype) return !0
  const r = e.pointer?.blob,
    i = t.pointer?.blob
  return r && i
    ? r.blockOffset !== i.blockOffset ||
        r.blockLength !== i.blockLength ||
        r.byteOffset !== i.byteOffset ||
        r.byteLength !== i.byteLength
    : !!r || !!i
}
export const mergeFileEntry = (e, t) => ({ ...e, ...t, httpLink: t?.httpLink || e?.httpLink })
export const mergeRoomInfoFiles = (e, t) => {
  const r = new Map()
  return (
    e.forEach((e) => r.set(e.id, e)),
    t.forEach((e) => r.set(e.id, e)),
    Array.from(r.values())
  )
}