Skip to content

Commit

Permalink
feat(plugin-md-power): add @[youtube](url) syntax supported
Browse files Browse the repository at this point in the history
  • Loading branch information
pengzhanbo committed Mar 28, 2024
1 parent 0715ee9 commit b8379e2
Show file tree
Hide file tree
Showing 2 changed files with 151 additions and 0 deletions.
45 changes: 45 additions & 0 deletions plugins/plugin-md-power/src/client/components/Youtube.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
<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_youtube_iframe"
:src="src"
:title="title || 'Youtube'"
:style="{ width, height }"
:allow="IFRAME_ALLOW"
@load="onLoad"
/>
</ClientOnly>
</template>

<style>
.video_youtube_iframe {
width: 100%;
margin: 16px auto;
border: none;
border-radius: 5px;
}
</style>
106 changes: 106 additions & 0 deletions plugins/plugin-md-power/src/node/features/video/youtube.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
/**
* @[youtube](id)
*/
import { URLSearchParams } from 'node:url'
import type { PluginWithOptions } from 'markdown-it'
import type { RuleBlock } from 'markdown-it/lib/parser_block.js'
import type { YoutubeTokenMeta } from '../../../shared/video.js'
import { resolveAttrs } from '../../utils/resolveAttrs.js'
import { parseRect } from '../../utils/parseRect.js'
import { timeToSeconds } from '../../utils/timeToSeconds.js'

const YOUTUBE_LINK = 'https://www.youtube.com/embed/'

// @[youtube]()
const MIN_LENGTH = 13

// char codes of '@[youtube'
const START_CODES = [64, 91, 121, 111, 117, 116, 117, 98, 101]

// regexp to match the import syntax
const SYNTAX_RE = /^@\[youtube(?:\s+([^]*?))?\]\(([^)]*)\)/

function createYoutubeRuleBlock(): RuleBlock {
return (state, startLine, endLine, silent) => {
const pos = state.bMarks[startLine] + state.tShift[startLine]
const max = state.eMarks[startLine]

// return false if the length is shorter than min length
if (pos + MIN_LENGTH > max)
return false

// check if it's matched the start
for (let i = 0; i < START_CODES.length; i += 1) {
if (state.src.charCodeAt(pos + i) !== START_CODES[i])
return false
}

// check if it's matched the syntax
const match = state.src.slice(pos, max).match(SYNTAX_RE)
if (!match)
return false

// return true as we have matched the syntax
if (silent)
return true

const [, info = '', id = ''] = match

const { attrs } = resolveAttrs(info)

const meta: YoutubeTokenMeta = {
id,
autoplay: attrs.autoplay ?? false,
loop: attrs.loop ?? false,
start: timeToSeconds(attrs.start),
end: timeToSeconds(attrs.end),
title: attrs.title,
width: attrs.width ? parseRect(attrs.width) : '100%',
height: attrs.height ? parseRect(attrs.height) : '',
ratio: attrs.ratio ? parseRect(attrs.ratio) : '',
}

const token = state.push('video_youtube', '', 0)

token.meta = meta
token.map = [startLine, startLine + 1]
token.info = info

state.line = startLine + 1

return true
}
}

function resolveYoutube(meta: YoutubeTokenMeta): string {
const params = new URLSearchParams()

meta.autoplay && params.set('autoplay', '1')
meta.loop && params.set('loop', '1')
meta.start && params.set('start', meta.start.toString())
meta.end && params.set('end', meta.end.toString())

const source = `${YOUTUBE_LINK}/${meta.id}?${params.toString()}`

return `<VideoYoutube src="${source}" width="${meta.width}" height="${meta.height}" ratio="${meta.ratio}" title="${meta.title}" />`
}

export const youtubePlugin: PluginWithOptions<never> = (md) => {
md.block.ruler.before(
'import_code',
'video_youtube',
createYoutubeRuleBlock(),
{
alt: ['paragraph', 'reference', 'blockquote', 'list'],
},
)

md.renderer.rules.video_youtube = (tokens, index) => {
const token = tokens[index]

const content = resolveYoutube(token.meta)
token.content = content

return content
}
}

0 comments on commit b8379e2

Please sign in to comment.