feat: support dplayer (#497)

This commit is contained in:
Steve Yu 2023-05-12 18:48:26 +08:00 committed by GitHub
parent d6bb7d2155
commit 452e913f77
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 856 additions and 714 deletions

View File

@ -72,6 +72,7 @@
"emoji-mart": "5.5.2",
"fast-average-color": "^9.3.0",
"fast-deep-equal": "3.1.3",
"file-size": "^1.0.0",
"hast-util-to-html": "^8.0.4",
"hast-util-to-text": "^3.1.2",
"i18next": "^22.4.15",
@ -97,6 +98,7 @@
"pangu": "^4.0.7",
"pinyin-pro": "^3.14.0",
"prismjs": "1.29.0",
"rc-dplayer": "^0.1.0",
"qier-progress": "1.0.4",
"react": "18.2.0",
"react-dom": "18.2.0",
@ -155,6 +157,7 @@
"@types/canvas-confetti": "^1.6.0",
"@types/chroma-js": "^2.4.0",
"@types/cookie": "0.5.1",
"@types/file-size": "^1.0.1",
"@types/hast": "2.3.4",
"@types/js-yaml": "4.0.5",
"@types/katex": "^0.16.0",
@ -200,4 +203,4 @@
"minimumChangeThreshold": 0,
"showDetails": true
}
}
}

File diff suppressed because it is too large Load Diff

View File

@ -1,6 +1,7 @@
"use client"
import { useDebounceEffect } from "ahooks"
import filesize from "file-size"
import type { Root } from "mdast"
import { nanoid } from "nanoid"
import { useParams, useRouter, useSearchParams } from "next/navigation"
@ -44,6 +45,7 @@ import { useIsMobileLayout } from "~/hooks/useMobileLayout"
import { useSyncOnce } from "~/hooks/useSyncOnce"
import { useUploadFile } from "~/hooks/useUploadFile"
import { showConfetti } from "~/lib/confetti"
import { MAXIMUM_FILE_SIZE } from "~/lib/constants"
import { getDefaultSlug } from "~/lib/default-slug"
import { CSB_SCAN } from "~/lib/env"
import { getSiteLink, getTwitterShareUrl } from "~/lib/helpers"
@ -348,9 +350,22 @@ export default function SubdomainEditor() {
try {
if (
!file.type.startsWith("image/") &&
!file.type.startsWith("audio/")
!file.type.startsWith("audio/") &&
!file.type.startsWith("video/")
) {
throw new Error("You can only upload images and audios")
throw new Error("You can only upload images, audios and videos")
}
const uploadFilesize = filesize(file.size).to("MB")
if (Number(uploadFilesize) > MAXIMUM_FILE_SIZE) {
toast.error(
`File Size is too big. It should be less than ${MAXIMUM_FILE_SIZE} MB`,
{
id: toastId,
},
)
return
}
const { key } = await uploadFile(file)
@ -389,6 +404,12 @@ export default function SubdomainEditor() {
} ${cover ? `cover="${cover}"` : ""}><audio>\n`,
),
)
} else if (file.type.startsWith("video/")) {
view?.dispatch(
view.state.replaceSelection(
`\n<video>\n\t<source src=\"${key}\" type=\"${file.type}\" />\n</video>\n`,
),
)
} else if (file.type === "text/plain") {
view?.dispatch(view.state.replaceSelection(key))
} else {

View File

@ -0,0 +1,36 @@
import { DPlayerProps, Player as RcDPlayer } from "rc-dplayer"
import { FC, memo } from "react"
import { ReactElement } from "rehype-react/lib"
import { toGateway } from "~/lib/ipfs-parser"
const DPlayer: FC<{
children: ReactElement[]
}> = memo(function DPlayer({ children }) {
const sources = children.filter(
(child) =>
child && typeof child.type === "string" && child.type === "source",
)
return (
<>
{sources.map((source, idx) => {
const { src: _src, ...props } = source.props as DPlayerProps
if (!_src) {
return <div key={idx} />
}
const src = toGateway(_src)
return (
<div className="my-8" key={idx}>
<RcDPlayer src={src} {...props} />
</div>
)
})}
</>
)
})
export default DPlayer

View File

@ -8,3 +8,5 @@ export const IS_DEV = process.env.NODE_ENV === "development"
export const IS_VERCEL_PREVIEW =
process.env.NEXT_PUBLIC_VERCEL_ENV === "preview" ||
process.env.VERCEL_ENV === "preview"
export const MAXIMUM_FILE_SIZE = 100 // MB

View File

@ -1,6 +1,7 @@
import jsYaml from "js-yaml"
import type { Root } from "mdast"
import { Result as TocResult, toc } from "mdast-util-toc"
import dynamic from "next/dynamic"
import { ReactElement, createElement } from "react"
import { toast } from "react-hot-toast"
import { refractor } from "refractor"
@ -43,6 +44,7 @@ import {
import { rehypeImage } from "./rehype-image"
import { rehypeTable } from "./rehype-table"
import { rehypeTweet } from "./rehype-tweet"
import { rehypeVideo } from "./rehype-video"
import { rehypeWrapCode } from "./rehype-wrap-code"
import { rehypeExternalLink } from "./rehyper-external-link"
import { remarkCallout } from "./remark-callout"
@ -51,6 +53,10 @@ import { remarkPangu } from "./remark-pangu"
import { remarkYoutube } from "./remark-youtube"
import sanitizeScheme from "./sanitize-schema"
const DPlayer = dynamic(() => import("~/components/ui/DPlayer"), {
ssr: false,
})
export type MarkdownEnv = {
excerpt: string
frontMatter: Record<string, any>
@ -142,6 +148,7 @@ export const renderPageContent = (
.use(rehypeRaw, { passThrough: allowedCustomWrappers })
.use(rehypeImage, { env })
.use(rehypeAudio, { env })
.use(rehypeVideo, { env })
.use(rehypeSlug)
.use(rehypeAutolinkHeadings, {
properties: {
@ -216,6 +223,7 @@ export const renderPageContent = (
mention: Mention,
mermaid: Mermaid,
audio: APlayer,
video: DPlayer,
tweet: Tweet,
style: Style,
} as any,

View File

@ -0,0 +1,36 @@
import { Root } from "rehype-raw"
import { Plugin } from "unified"
import { visit } from "unist-util-visit"
import { toGateway } from "~/lib/ipfs-parser"
import { MarkdownEnv } from "."
export const rehypeVideo: Plugin<Array<{ env: MarkdownEnv }>, Root> = ({
env,
}) => {
return (tree) => {
let first = true
visit(tree, { tagName: "video" }, (node, i, parent) => {
if (!node.properties) {
return
}
for (const child of node.children) {
if (child.type === "element" && child.tagName === "source") {
if (!child.properties) {
continue
}
const src = child.properties.src
if (!src || typeof src !== "string") {
continue
}
child.properties.src = toGateway(src)
}
}
})
}
}