feat(plugin-md-power): add acfun video support, close #625 (#629)

This commit is contained in:
pengzhanbo 2025-06-29 14:36:26 +08:00 committed by GitHub
parent e2fb1c10e8
commit dc42be6035
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
18 changed files with 203 additions and 68 deletions

View File

@ -56,6 +56,7 @@
"vue"
],
"cSpell.words": [
"acfun",
"bilibili",
"bumpp",
"caniuse",

View File

@ -109,6 +109,7 @@ export const themeGuide: ThemeNote = defineNoteConfig({
items: [
'pdf',
'bilibili',
'acfun',
'youtube',
'artplayer',
'audioReader',

View File

@ -35,6 +35,7 @@ export const theme: Theme = plumeTheme({
imageSize: 'all',
pdf: true,
caniuse: true,
acfun: true,
bilibili: true,
youtube: true,
artPlayer: true,

View File

@ -29,6 +29,7 @@ export default defineUserConfig({
// demo: true, // :::demo
// pdf: true, // @[pdf](url) 嵌入 PDF 文件
// bilibili: true, // @[bilibili](bvid) 嵌入 bilibili 视频
// acfun: true, // @[acfun](id) 嵌入 AcFun 视屏
// youtube: true, // @[youtube](id) 嵌入 youtube 视频
// codepen: true, // @[codepen](user/slash) 嵌入 codepen
// replit: true, // @[replit](user/repl-name) 嵌入 Replit
@ -86,6 +87,18 @@ __语法:__
请查看 [完整使用文档](../../guide/embed/bilibili.md)
### AcFun 视频
插件默认不启用该功能,你需要手动设置 `acfun``true`
__语法:__
```md
@[acfun](id)
```
请查看 [完整使用文档](../../guide/embed/video/acfun.md)
### youtube 视频
插件默认不启用该功能,你需要手动设置 `youtube``true`

View File

@ -0,0 +1,62 @@
---
title: AcFun 视频
icon: lets-icons:video-fill
createTime: 2025/06/28 10:57:45
permalink: /guide/embed/video/acfun/
badge: 新
---
## 概述
主题提供了 嵌入 AcFun 视频 的功能。
该功能由 [vuepress-plugin-md-power](../../config/plugins/markdown-power.md) 提供支持。
## 配置
该功能默认不启用。你需要在主题配置中开启。
```ts title=".vuepress/config.ts"
export default defineUserConfig({
theme: plumeTheme({
markdown: {
acfun: true, // [!code ++]
},
})
})
```
## 语法
简单的语法:
```md
@[acfun](id)
```
更多选项:
```md
@[acfun width="100%" height="400px" ratio="16:9"](id)
```
**选项说明:**
- id: 视频 ID
- width: 视频宽度
- height: 视频高度
- ratio: 视频比例,默认 `16:9`
## 示例
### 宽频视频
输入:
```md
@[acfun](ac47431669)
```
输出:
@[acfun](ac47431669)

View File

@ -0,0 +1,10 @@
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
exports[`acfunPlugin > should not work 1`] = `
"<VPVideoEmbed src="https://www.acfun.cn/player/" width="100%" ratio="16:10" title="AcFun" type="acfun" /><p>@[acfun]xxx</p>
<p>@[ acfun]123456</p>
<p>@<a href="123456"> acfun</a></p>
"
`;
exports[`acfunPlugin > should work 1`] = `"<VPVideoEmbed src="https://www.acfun.cn/player/123456" width="100%" ratio="16:10" title="AcFun" type="acfun" /><VPVideoEmbed src="https://www.acfun.cn/player/123456" width="100%" ratio="16:10" title="test" type="acfun" /><VPVideoEmbed src="https://www.acfun.cn/player/123456" width="100%" height="600px" ratio="16:10" title="AcFun" type="acfun" /><VPVideoEmbed src="https://www.acfun.cn/player/123456" width="100%" ratio="16:9" title="AcFun" type="acfun" />"`;

View File

@ -1,10 +1,10 @@
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
exports[`bilibiliPlugin > should not work 1`] = `
"<VideoBilibili src="https://player.bilibili.com/player.html?autoplay=0&high_quality=1" width="100%" /><p>@[bilibili]xxx</p>
"<VPVideoEmbed src="https://player.bilibili.com/player.html?autoplay=0&high_quality=1" width="100%" title="Bilibili" type="bilibili" /><p>@[bilibili]xxx</p>
<p>@[ bilibili]BV12345</p>
<p>@[bilibili](BV12345</p>
"
`;
exports[`bilibiliPlugin > should work 1`] = `"<VideoBilibili src="https://player.bilibili.com/player.html?bvid=BV12345&autoplay=0&high_quality=1" width="100%" /><VideoBilibili src="https://player.bilibili.com/player.html?aid=12432&cid=12345&autoplay=0&high_quality=1" width="100%" /><VideoBilibili src="https://player.bilibili.com/player.html?bvid=BV12345&aid=12343&cid=45678&autoplay=0&high_quality=1" width="100%" /><VideoBilibili src="https://player.bilibili.com/player.html?bvid=BV12345&p=1&t=1&autoplay=1&high_quality=1" width="100%" /><VideoBilibili src="https://player.bilibili.com/player.html?bvid=BV12345&p=1&autoplay=1&high_quality=1" width="100%" height="600px" /><VideoBilibili src="https://player.bilibili.com/player.html?bvid=BV12345&p=1&autoplay=1&high_quality=1" width="100%" ratio="16:9" />"`;
exports[`bilibiliPlugin > should work 1`] = `"<VPVideoEmbed src="https://player.bilibili.com/player.html?bvid=BV12345&autoplay=0&high_quality=1" width="100%" title="Bilibili" type="bilibili" /><VPVideoEmbed src="https://player.bilibili.com/player.html?aid=12432&cid=12345&autoplay=0&high_quality=1" width="100%" title="Bilibili" type="bilibili" /><VPVideoEmbed src="https://player.bilibili.com/player.html?bvid=BV12345&aid=12343&cid=45678&autoplay=0&high_quality=1" width="100%" title="Bilibili" type="bilibili" /><VPVideoEmbed src="https://player.bilibili.com/player.html?bvid=BV12345&p=1&t=1&autoplay=1&high_quality=1" width="100%" title="Bilibili" type="bilibili" /><VPVideoEmbed src="https://player.bilibili.com/player.html?bvid=BV12345&p=1&autoplay=1&high_quality=1" width="100%" height="600px" title="Bilibili" type="bilibili" /><VPVideoEmbed src="https://player.bilibili.com/player.html?bvid=BV12345&p=1&autoplay=1&high_quality=1" width="100%" ratio="16:9" title="Bilibili" type="bilibili" />"`;

View File

@ -1,10 +1,10 @@
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
exports[`youtubePlugin > should not work 1`] = `
"<VideoYoutube src="https://www.youtube.com/embed//?" width="100%" /><p>@[youtube]xxx</p>
"<VPVideoEmbed src="https://www.youtube.com/embed//?" width="100%" title="YouTube" type="youtube" /><p>@[youtube]xxx</p>
<p>@[ youtube]123456</p>
<p>@<a href="123456"> youtube</a></p>
"
`;
exports[`youtubePlugin > should work 1`] = `"<VideoYoutube src="https://www.youtube.com/embed//123456?" width="100%" /><VideoYoutube src="https://www.youtube.com/embed//123456?autoplay=1&loop=1" width="100%" title="test" /><VideoYoutube src="https://www.youtube.com/embed//123456?autoplay=1&start=40&end=80" width="100%" /><VideoYoutube src="https://www.youtube.com/embed//123456?" width="100%" height="600px" /><VideoYoutube src="https://www.youtube.com/embed//123456?" width="100%" ratio="16:9" />"`;
exports[`youtubePlugin > should work 1`] = `"<VPVideoEmbed src="https://www.youtube.com/embed//123456?" width="100%" title="YouTube" type="youtube" /><VPVideoEmbed src="https://www.youtube.com/embed//123456?autoplay=1&loop=1" width="100%" title="test" type="youtube" /><VPVideoEmbed src="https://www.youtube.com/embed//123456?autoplay=1&start=40&end=80" width="100%" title="YouTube" type="youtube" /><VPVideoEmbed src="https://www.youtube.com/embed//123456?" width="100%" height="600px" title="YouTube" type="youtube" /><VPVideoEmbed src="https://www.youtube.com/embed//123456?" width="100%" ratio="16:9" title="YouTube" type="youtube" />"`;

View File

@ -0,0 +1,42 @@
import MarkdownIt from 'markdown-it'
import { describe, expect, it } from 'vitest'
import { acfunPlugin } from '../src/node/embed/video/acfun.js'
function createMarkdown() {
return MarkdownIt().use((md) => {
md.block.ruler.before('code', 'import_code', () => false)
md.renderer.rules.import_code = () => ''
}).use(acfunPlugin)
}
describe('acfunPlugin', () => {
it('should work', () => {
const md = createMarkdown()
const code = `\
@[acfun](123456)
@[acfun title="test"](123456)
@[acfun width="100%" height="600px"](123456)
@[acfun width="100%" ratio="16:9"](123456)
`
expect(md.render(code)).toMatchSnapshot()
})
it('should not work', () => {
const md = createMarkdown()
const code = `\
@[acfun]()
@[acfun]xxx
@[ acfun]123456
@[ acfun](123456)
`
expect(md.render(code)).toMatchSnapshot()
})
})

View File

@ -1,45 +0,0 @@
<script setup lang="ts">
import { toRefs } from 'vue'
import { useSize } from '../composables/size.js'
const props = defineProps<{
src: string
title: string
width?: string
height?: string
ratio?: string
}>()
const IFRAME_ALLOW = 'accelerometer; autoplay; clipboard-write; encrypted-media; fullscreen; gyroscope; picture-in-picture'
const options = toRefs(props)
const { el, width, height, resize } = useSize(options)
function onLoad() {
resize()
}
</script>
<template>
<ClientOnly>
<iframe
ref="el"
class="video_bilibili_iframe"
:src="src"
:title="title || 'Bilibili'"
:style="{ width, height }"
:allow="IFRAME_ALLOW"
@load="onLoad"
/>
</ClientOnly>
</template>
<style>
.video_bilibili_iframe {
width: 100%;
margin: 16px auto;
border: none;
border-radius: 5px;
}
</style>

View File

@ -5,6 +5,7 @@ import { useSize } from '../composables/size.js'
const props = defineProps<{
src: string
title: string
type?: string
width?: string
height?: string
ratio?: string
@ -21,9 +22,9 @@ const { el, width, height, resize } = useSize(options)
<ClientOnly>
<iframe
ref="el"
class="video-youtube-iframe"
class="video-iframe" :class="type"
:src="src"
:title="title || 'Youtube'"
:title="title || type"
:style="{ width, height }"
:allow="IFRAME_ALLOW"
@load="resize"
@ -32,7 +33,7 @@ const { el, width, height, resize } = useSize(options)
</template>
<style>
.video-youtube-iframe {
.video-iframe {
width: 100%;
margin: 16px auto;
border: none;

View File

@ -7,6 +7,7 @@ import { codeSandboxPlugin } from './code/codeSandbox.js'
import { jsfiddlePlugin } from './code/jsfiddle.js'
import { replitPlugin } from './code/replit.js'
import { pdfPlugin } from './pdf.js'
import { acfunPlugin } from './video/acfun.js'
import { artPlayerPlugin } from './video/artPlayer.js'
import { bilibiliPlugin } from './video/bilibili.js'
import { youtubePlugin } from './video/youtube.js'
@ -25,6 +26,11 @@ export function embedSyntaxPlugin(md: Markdown, options: MarkdownPowerPluginOpti
md.use(pdfPlugin)
}
if (options.acfun) {
// @[acfun](id)
md.use(acfunPlugin)
}
if (options.bilibili) {
// @[bilibili](bvid aid cid)
md.use(bilibiliPlugin)

View File

@ -0,0 +1,35 @@
/**
* @[acfun](acid)
*/
import type { PluginWithOptions } from 'markdown-it'
import type { AcFunTokenMeta } from '../../../shared/index.js'
import { parseRect } from '../../utils/parseRect.js'
import { resolveAttrs } from '../../utils/resolveAttrs.js'
import { stringifyAttrs } from '../../utils/stringifyAttrs.js'
import { createEmbedRuleBlock } from '../createEmbedRuleBlock.js'
const AC_FUN_LINK = 'https://www.acfun.cn/player'
export const acfunPlugin: PluginWithOptions<never> = (md) => {
createEmbedRuleBlock<AcFunTokenMeta>(md, {
type: 'acfun',
name: 'video_acfun',
syntaxPattern: /^@\[acfun([^\]]*)\]\(([^)]*)\)/,
meta([, info, id]) {
const { attrs } = resolveAttrs(info)
return {
id,
title: attrs.title || 'AcFun',
width: attrs.width ? parseRect(attrs.width) : '100%',
height: attrs.height ? parseRect(attrs.height) : undefined,
ratio: attrs.ratio ?? '16:10',
}
},
content(meta) {
const { id, width, height, ratio, title } = meta
const src = `${AC_FUN_LINK}/${id}`
return `<VPVideoEmbed${stringifyAttrs({ src, width, height, ratio, title, type: 'acfun' })} />`
},
})
}

View File

@ -34,10 +34,10 @@ export const bilibiliPlugin: PluginWithOptions<never> = (md) => {
cid,
autoplay: attrs.autoplay ?? false,
time: timeToSeconds(attrs.time),
title: attrs.title,
title: attrs.title || 'Bilibili',
width: attrs.width ? parseRect(attrs.width) : '100%',
height: attrs.height ? parseRect(attrs.height) : undefined,
ratio: attrs.ratio ? parseRect(attrs.ratio) : undefined,
ratio: attrs.ratio,
}
},
content(meta) {
@ -61,10 +61,10 @@ export const bilibiliPlugin: PluginWithOptions<never> = (md) => {
params.set('autoplay', meta.autoplay ? '1' : '0')
params.set('high_quality', '1')
const source = `${BILIBILI_LINK}?${params.toString()}`
const src = `${BILIBILI_LINK}?${params.toString()}`
const { width, height, ratio, title } = meta
return `<VideoBilibili${stringifyAttrs({ src: source, width, height, ratio, title })} />`
return `<VPVideoEmbed${stringifyAttrs({ src, width, height, ratio, title, type: 'bilibili' })} />`
},
})
}

View File

@ -26,10 +26,10 @@ export const youtubePlugin: PluginWithOptions<never> = (md) => {
loop: attrs.loop ?? false,
start: timeToSeconds(attrs.start),
end: timeToSeconds(attrs.end),
title: attrs.title,
title: attrs.title || 'YouTube',
width: attrs.width ? parseRect(attrs.width) : '100%',
height: attrs.height ? parseRect(attrs.height) : undefined,
ratio: attrs.ratio ? parseRect(attrs.ratio) : undefined,
ratio: attrs.ratio,
}
},
content(meta) {
@ -47,10 +47,10 @@ export const youtubePlugin: PluginWithOptions<never> = (md) => {
if (meta.end)
params.set('end', meta.end.toString())
const source = `${YOUTUBE_LINK}/${meta.id}?${params.toString()}`
const src = `${YOUTUBE_LINK}/${meta.id}?${params.toString()}`
const { width, height, ratio, title } = meta
return `<VideoYoutube${stringifyAttrs({ src: source, width, height, ratio, title })} />`
return `<VPVideoEmbed${stringifyAttrs({ src, width, height, ratio, title, type: 'youtube' })} />`
},
})
}

View File

@ -26,14 +26,9 @@ export async function prepareConfigFile(app: App, options: MarkdownPowerPluginOp
enhances.add(`app.component('PDFViewer', PDFViewer)`)
}
if (options.bilibili) {
imports.add(`import Bilibili from '${CLIENT_FOLDER}components/Bilibili.vue'`)
enhances.add(`app.component('VideoBilibili', Bilibili)`)
}
if (options.youtube) {
imports.add(`import Youtube from '${CLIENT_FOLDER}components/Youtube.vue'`)
enhances.add(`app.component('VideoYoutube', Youtube)`)
if (options.acfun || options.bilibili || options.youtube) {
imports.add(`import VPVideoEmbed from '${CLIENT_FOLDER}components/VPVideoEmbed.vue'`)
enhances.add(`app.component('VPVideoEmbed', VPVideoEmbed)`)
}
if (options.codepen) {

View File

@ -128,6 +128,14 @@ export interface MarkdownPowerPluginOptions {
*/
field?: boolean
// video embed
/**
* acfun
*
* `@[acfun](acid)`
*
* @default false
*/
acfun?: boolean
/**
* bilibili
*

View File

@ -5,6 +5,11 @@ export interface VideoOptions {
youtube?: boolean
}
export interface AcFunTokenMeta extends SizeOptions {
title?: string
id: string
}
export interface BilibiliTokenMeta extends SizeOptions {
title?: string
bvid?: string