Skip to content

Commit

Permalink
feat: remove the track url parameter. #17
Browse files Browse the repository at this point in the history
  • Loading branch information
jaywcjlove committed Jul 1, 2024
1 parent 3040763 commit 9f36ae2
Show file tree
Hide file tree
Showing 3 changed files with 80 additions and 24 deletions.
34 changes: 23 additions & 11 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,27 +22,39 @@ export type RehypeVideoOptions = {
track?: boolean;
}

const delimiter = /((?:https?:\/\/)(?:(?:[a-z0-9]?(?:[a-z0-9\-]{1,61}[a-z0-9])?\.[^\.|\s])+[a-z\.]*[a-z]+|(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)(?:\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3})(?::\d{1,5})*[a-z0-9.,_\/~#&=;%+?\-\\(\\)]*)/g;
function isValidURL(string: string) {
try {
new URL(string);
return true;
} catch (err) {
return false;
}
}

const properties = { muted: 'muted', controls: 'controls', style: 'max-height:640px;' };
const queryStringToObject = (url: string) =>
[...new URLSearchParams(url.split('?!#')[1])].reduce(
[...new URLSearchParams(url.split('?')[1])].reduce(
(a: Record<string, string>, [k, v]) => ((a[k] = v), a),
{}
);

function reElement(node: Element, details: boolean, track: boolean, href: string) {
let url = /https?/.test(href) ? new URL(href).pathname : href;
const filename = url.split('/').pop()?.replace(/(\?|!|\#|$).+/, '');
node.properties = { ...properties, src: href };
const url = isValidURL(href.trim()) ? new URL(href) : null;
const pathname = url?.pathname || href;
const filename = pathname.split('/').pop()?.replace(/(\?|!|\#|$).+/, '');
const params = queryStringToObject(href.replace(/\?\!/, "?").replace(/\?\!\#/, "?"));
const { title = filename } = params;
const result = trackNode(params);
const searchParams = new URLSearchParams({ ...result.query });
if (url) {
url.search = searchParams.toString() ?? "";
}
node.tagName = 'video';
node.children = [];
const { title = filename, ...other }= queryStringToObject(href);

node.properties = { ...properties, src: url?.toString() || href };
if (track) {
const trNode = trackNode(other);
trNode.forEach((tr) => node.children.push({ ...tr }));
result.element.forEach((tr) => node.children.push({ ...tr }));
}

if (details) {
const reNode = detailsNode(title);
reNode.children.push({ ...node });
Expand All @@ -60,7 +72,7 @@ const RehypeVideo: Plugin<[RehypeVideoOptions?], Root> = (options) => {
const child = node.children[0];

if (node.tagName === 'p' && node.children.length === 1) {
if (child.type === 'text' && delimiter.test(child.value) && isChecked(child.value)) {
if (child.type === 'text' && isValidURL(child.value) && isChecked(child.value)) {
reElement(node, details, track, child.value);
}
if (child.type === 'element' && child.tagName === 'a' && child.properties && typeof child.properties.href === 'string' && isChecked(child.properties.href)) {
Expand Down
24 changes: 15 additions & 9 deletions src/trackNode.ts
Original file line number Diff line number Diff line change
@@ -1,21 +1,25 @@
import type { Element } from 'hast';

export function trackNode(query: Record<string, string> = {}): Element[] {
let result: Record<string, Record<string, string>> = {}
let resultElement: Element[] = []
export const trackNode = (query: Record<string, string> = {}): { query: Record<string, string>, element: Element[] } => {
const resultTrack: Record<string, Record<string, string>> = {}
const resultElement: Element[] = []
const resultQuery: Record<string, string> = {}
const result = { query: resultQuery, element: resultElement }
Object.keys(query).forEach((key) => {
let keyMatch = key.match(/track\[['"](\w+:?\w+)['"]\]/i)
let keyMatch = key.trim().match(/track\[['"](\w+:?\w+)['"]\]/i)
let queryKey = keyMatch ? keyMatch[1] : null
if (queryKey) {
let [lang, keyString] = queryKey.split(':')
if (!result[lang]) result[lang] = {}
if (!resultTrack[lang]) resultTrack[lang] = {}
const value = query[key]
result[lang][keyString ?? "src"] = value
resultTrack[lang][keyString ?? "src"] = value
} else {
resultQuery[key] = query[key]
}
});

Object.keys(result).forEach((lang) => {
const track = result[lang]
Object.keys(resultTrack).forEach((lang) => {
const track = resultTrack[lang]
resultElement.push({
type: 'element',
tagName: 'track',
Expand All @@ -26,5 +30,7 @@ export function trackNode(query: Record<string, string> = {}): Element[] {
children: []
})
})
return resultElement
result.query = resultQuery
result.element = resultElement
return { ...result }
}
46 changes: 42 additions & 4 deletions test/index.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@ import stringify from 'rehype-stringify';
import rehypeVideo from '../src/index.js';

it('rehype-video title test case', () => {
const mrkStr = `https://github.com/002.mp4?!#title=rehype-video`;
const expected = `<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/002.mp4?!#title=rehype-video\"></video></details>`
const mrkStr = `https://github.com/002.mp4?title=rehype-video`;
const expected = `<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/002.mp4?title=rehype-video\"></video></details>`
const htmlStr = unified()
.use(remarkParse)
.use(remark2rehype, { allowDangerousHtml: true })
Expand Down Expand Up @@ -145,10 +145,9 @@ https://github.com/003.mp4
expect(htmlStr).toEqual(expected);
});


it('rehype-video WebVTT query test case', () => {
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 expected = `<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[&#x27;en&#x27;]=captions/vtt/sintel-en.vtt&#x26;track[&#x27;en:label&#x27;]=English&#x26;track[&#x27;en:kind&#x27;]=subtitles&#x26;track[&#x27;en:default&#x27;]=true&#x26;track[&#x27;de&#x27;]=captions/vtt/sintel-de.vtt&#x26;track[&#x27;de:label&#x27;]=Deutsch&#x26;track[&#x27;de:kind&#x27;]=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>`
const expected = `<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[&#x27;en&#x27;]=captions/vtt/sintel-en.vtt&#x26;track[&#x27;en:label&#x27;]=English&#x26;track[&#x27;en:kind&#x27;]=subtitles&#x26;track[&#x27;en:default&#x27;]=true&#x26;track[&#x27;de&#x27;]=captions/vtt/sintel-de.vtt&#x26;track[&#x27;de:label&#x27;]=Deutsch&#x26;track[&#x27;de:kind&#x27;]=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>`
const htmlStr = unified()
.use(remarkParse)
.use(remark2rehype, { allowDangerousHtml: true })
Expand All @@ -157,4 +156,43 @@ it('rehype-video WebVTT query test case', () => {
.processSync(mrkStr)
.toString();
expect(htmlStr).toEqual(expected);
});

it('rehype-video WebVTT `track=false` test case', () => {
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 expected = `<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[&#x27;en&#x27;]=captions/vtt/sintel-en.vtt&#x26;track[&#x27;en:label&#x27;]=English&#x26;track[&#x27;en:kind&#x27;]=subtitles&#x26;track[&#x27;en:default&#x27;]=true&#x26;track[&#x27;de&#x27;]=captions/vtt/sintel-de.vtt&#x26;track[&#x27;de:label&#x27;]=Deutsch&#x26;track[&#x27;de:kind&#x27;]=subtitles\"></video></details>`
const htmlStr = unified()
.use(remarkParse)
.use(remark2rehype, { allowDangerousHtml: true })
.use(rehypeVideo, { track: false })
.use(stringify)
.processSync(mrkStr)
.toString();
expect(htmlStr).toEqual(expected);
});

it('rehype-video WebVTT `remove the track parameter` test case', () => {
const mrkStr2 = `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 expected2 = `<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 kind=\"subtitles\" src=\"captions/vtt/sintel-en.vtt\" label=\"English\" default><track kind=\"subtitles\" src=\"captions/vtt/sintel-de.vtt\" label=\"Deutsch\"></video></details>`
const htmlStr = unified()
.use(remarkParse)
.use(remark2rehype, { allowDangerousHtml: true })
.use(rehypeVideo, { })
.use(stringify)
.processSync(mrkStr2)
.toString();
expect(htmlStr).toEqual(expected2);
});

it('rehype-video WebVTT `double-quoted parameter` test case', () => {
const mrkStr2 = `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 expected2 = `<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 kind=\"subtitles\" src=\"captions/vtt/sintel-en.vtt\" label=\"English\" default><track kind=\"subtitles\" src=\"captions/vtt/sintel-de.vtt\" label=\"Deutsch\"></video></details>`
const htmlStr = unified()
.use(remarkParse)
.use(remark2rehype, { allowDangerousHtml: true })
.use(rehypeVideo, { })
.use(stringify)
.processSync(mrkStr2)
.toString();
expect(htmlStr).toEqual(expected2);
});

0 comments on commit 9f36ae2

Please # to comment.