Add improved video syntax: links to .mp4
and .mov
turn into videos. like github video
features.
The following is a sample test of the video preview in GitHub:
test.mov
This package is ESM only: Node 12+ is needed to use it and it must be import instead of require.
npm install rehype-video
import { unified } from 'unified';
import remark2rehype from 'remark-rehype';
import remarkParse from 'remark-parse';
import rehypeVideo from 'rehype-video';
import stringify from 'rehype-stringify';
const string = `
https://files.github.com/001.mp4 hi!
https://files.github.com/002.mp4
Good \`idea\`!!
https://github.com/002.mp4?!#title=Custom%20Title
`;
const htmlStr = unified()
.use(remarkParse)
.use(remark2rehype, { allowDangerousHtml: true })
.use(rehypeVideo)
.use(stringify)
.processSync(string)
.toString();
Output:
<p>https://files.github.com/001.mp4 hi!</p>
<details open>
<summary>
<svg aria-hidden height="16" width="16" viewBox="0 0 16 16" version="1.1" class="octicon octicon-device-camera-video"><path fill-rule="evenodd" d="M16 3.75a.75.75 0 00-1.136-.643L11 5.425V4.75A1.75 1.75 0 009.25 3h-7.5A1.75 1.75 0 000 4.75v6.5C0 12.216.784 13 1.75 13h7.5A1.75 1.75 0 0011 11.25v-.675l3.864 2.318A.75.75 0 0016 12.25v-8.5zm-5 5.075l3.5 2.1v-5.85l-3.5 2.1v1.65zM9.5 6.75v-2a.25.25 0 00-.25-.25h-7.5a.25.25 0 00-.25.25v6.5c0 .138.112.25.25.25h7.5a.25.25 0 00.25-.25v-4.5z"></path></svg>
<span aria-label="Video description 002.mp4">002.mp4</span>
<span class="dropdown-caret"></span>
</summary>
<video muted controls style="max-height:640px;" src="https://github.com/002.mp4"></video>
</details>
<p>Good <code>idea</code>!!</p>
<details open>
<summary>
<svg aria-hidden height="16" width="16" viewBox="0 0 16 16" version="1.1" class="octicon octicon-device-camera-video"><path fill-rule="evenodd" d="M16 3.75a.75.75 0 00-1.136-.643L11 5.425V4.75A1.75 1.75 0 009.25 3h-7.5A1.75 1.75 0 000 4.75v6.5C0 12.216.784 13 1.75 13h7.5A1.75 1.75 0 0011 11.25v-.675l3.864 2.318A.75.75 0 0016 12.25v-8.5zm-5 5.075l3.5 2.1v-5.85l-3.5 2.1v1.65zM9.5 6.75v-2a.25.25 0 00-.25-.25h-7.5a.25.25 0 00-.25.25v6.5c0 .138.112.25.25.25h7.5a.25.25 0 00.25-.25v-4.5z"></path></svg>
<span aria-label="Video description Custom Title">Custom Title</span>
<span class="dropdown-caret"></span>
</summary>
<video muted controls style="max-height:640px;" src="https://github.com/002.mp4"></video>
</details>
import { rehype } from 'rehype';
import rehypeVideo from 'rehype-video';
const mrkStr = `<p>https://github.com/004.mp4</p>`;
const htmlStr = rehype()
.data('settings', { fragment: true })
.use(rehypeVideo, { details: false })
.processSync(mrkStr)
.toString();
Output:
<video muted controls style="max-height:640px;" src="https://github.com/004.mp4"></video>
import { rehype } from 'rehype';
import rehypeVideo from 'rehype-video';
const mrkStr = `<p><a href="https://github.com/004.mp4">https://github.com/004.mp4</a></p`;
const htmlStr = rehype()
.data('settings', { fragment: true })
.use(rehypeVideo, { details: false })
.processSync(mrkStr)
.toString();
Output:
<video muted controls style="max-height:640px;" src="https://github.com/004.mp4"></video>
import remarkParse from 'remark-parse';
import rehypeVideo from 'rehype-video';
import { unified } from 'unified';
import remark2rehype from 'remark-rehype';
import remarkParse from 'remark-parse';
import stringify from 'rehype-stringify';
const mrkStr = 'https://github.com/user-attachments/assets/0d808e2e-84c7-46ca-a220-440fa9f34118?title=rehype-video&rehype=video'
const htmlStr = unified()
.use(remarkParse)
.use(remark2rehype, { allowDangerousHtml: true })
.use(rehypeVideo, {
test: (url) => {
return /\.(mp4|mov)|[?&]rehype=video/i.test(url);
}
})
.use(stringify)
.processSync(mrkStr)
.toString();
Output:
<details open class="octicon octicon-video">
<summary>
<svg aria-hidden height="16" width="16" viewBox="0 0 16 16" version="1.1" data-view-component class="octicon octicon-device-camera-video"><path fill-rule="evenodd" d="M16 3.75a.75.75 0 00-1.136-.643L11 5.425V4.75A1.75 1.75 0 009.25 3h-7.5A1.75 1.75 0 000 4.75v6.5C0 12.216.784 13 1.75 13h7.5A1.75 1.75 0 0011 11.25v-.675l3.864 2.318A.75.75 0 0016 12.25v-8.5zm-5 5.075l3.5 2.1v-5.85l-3.5 2.1v1.65zM9.5 6.75v-2a.25.25 0 00-.25-.25h-7.5a.25.25 0 00-.25.25v6.5c0 .138.112.25.25.25h7.5a.25.25 0 00.25-.25v-4.5z"></path></svg>
<span aria-label="Video description rehype-video">rehype-video</span><span class="dropdown-caret"></span>
</summary>
<video muted controls style="max-height:640px;" src="https://github.com/user-attachments/assets/0d808e2e-84c7-46ca-a220-440fa9f34118?title=rehype-video&rehype=video">
</video>
</details>
Define custom title parameter(E.g: title=RehypeVideo
) with hash route:
const string = `https://github.com/002.mp4?!#title=Custom%20Title`;
const htmlStr = unified()
.use(remarkParse)
.use(remark2rehype, { allowDangerousHtml: true })
.use(rehypeVideo)
.use(stringify)
.processSync(string)
.toString();
Output:
<details open>
<summary>
<svg aria-hidden height="16" width="16" viewBox="0 0 16 16" version="1.1" class="octicon octicon-device-camera-video"><path fill-rule="evenodd" d="M16 3.75a.75.75 0 00-1.136-.643L11 5.425V4.75A1.75 1.75 0 009.25 3h-7.5A1.75 1.75 0 000 4.75v6.5C0 12.216.784 13 1.75 13h7.5A1.75 1.75 0 0011 11.25v-.675l3.864 2.318A.75.75 0 0016 12.25v-8.5zm-5 5.075l3.5 2.1v-5.85l-3.5 2.1v1.65zM9.5 6.75v-2a.25.25 0 00-.25-.25h-7.5a.25.25 0 00-.25.25v6.5c0 .138.112.25.25.25h7.5a.25.25 0 00.25-.25v-4.5z"></path></svg>
<span aria-label="Video description Custom Title">Custom Title</span>
<span class="dropdown-caret"></span>
</summary>
<video muted controls style="max-height:640px;" src="https://github.com/002.mp4"></video>
</details>
We will reference all two of the relevant VTT files by adding <track> elements inside our HTML <video> element:
const mrkStr = `https://github.com/sintel-short.mp4?!#track['en']=captions/vtt/sintel-en.vtt&track['en:label']=English&track['en:kind']=subtitles&track['en:default']=true&track['de']=captions/vtt/sintel-de.vtt&track['de:label']=Deutsch&track['de:kind']=subtitles`;
const htmlStr = unified()
.use(remarkParse)
.use(remark2rehype, { allowDangerousHtml: true })
.use(rehypeVideo, { })
.use(stringify)
.processSync(mrkStr)
.toString();
Output:
<details open class="octicon octicon-video">
<summary>
<svg aria-hidden height="16" width="16" viewBox="0 0 16 16" version="1.1" data-view-component class="octicon octicon-device-camera-video">
<path fill-rule="evenodd" d="M16 3.75a.75.75 0 00-1.136-.643L11 5.425V4.75A1.75 1.75 0 009.25 3h-7.5A1.75 1.75 0 000 4.75v6.5C0 12.216.784 13 1.75 13h7.5A1.75 1.75 0 0011 11.25v-.675l3.864 2.318A.75.75 0 0016 12.25v-8.5zm-5 5.075l3.5 2.1v-5.85l-3.5 2.1v1.65zM9.5 6.75v-2a.25.25 0 00-.25-.25h-7.5a.25.25 0 00-.25.25v6.5c0 .138.112.25.25.25h7.5a.25.25 0 00.25-.25v-4.5z"></path>
</svg>
<span aria-label="Video description sintel-short.mp4">sintel-short.mp4</span>
<span class="dropdown-caret"></span>
</summary>
<video muted controls style="max-height:640px;" src="https://github.com/sintel-short.mp4?!#track['en']=captions/vtt/sintel-en.vtt&track['en:label']=English&track['en:kind']=subtitles&track['en:default']=true&track['de']=captions/vtt/sintel-de.vtt&track['de:label']=Deutsch&track['de:kind']=subtitles">
<track kind="subtitles" src="captions/vtt/sintel-en.vtt" label="English" default>
<track kind="subtitles" src="captions/vtt/sintel-de.vtt" label="Deutsch">
</video>
</details>
export declare type RehypeVideoOptions = {
/**
* URL suffix verification.
* @default /\/(.*)(.mp4|.mov)$/
*/
test?: RegExp;
/**
* Support `<details>` tag to wrap <video>.
* @default true
*/
details?: boolean;
/**
* Support `<track>` tag to wrap <video>.
* @default true
*/
track?: boolean;
};
rehype-attr
New syntax to add attributes to Markdown.rehype-rewrite
Rewrite element with rehype.rehype-ignore
Ignore content display via HTML comments, Shown in GitHub readme, excluded in HTML.remark-github-blockquote-alert
Remark plugin to add support for GitHub Alert
MIT © Kenny Wong