From 3d2469d6f3239f17e5ab5823312d573be67ecab1 Mon Sep 17 00:00:00 2001 From: Gleb Voitenko Date: Wed, 6 Nov 2024 13:42:42 +0300 Subject: [PATCH] feat: support video by url only --- src/transform/plugins/video/const.ts | 7 ------- src/transform/plugins/video/index.ts | 19 ++++++++++++------- src/transform/plugins/video/parsers.ts | 19 ++++++++++--------- src/transform/plugins/video/types.ts | 7 +++++++ test/data/video/video-fixtures.txt | 2 +- test/video.test.ts | 26 +++++++++++++------------- 6 files changed, 43 insertions(+), 37 deletions(-) diff --git a/src/transform/plugins/video/const.ts b/src/transform/plugins/video/const.ts index 28f4b6f6..5f005c66 100644 --- a/src/transform/plugins/video/const.ts +++ b/src/transform/plugins/video/const.ts @@ -15,13 +15,6 @@ export enum VideoService { url = 'url', } -export type Service = { - csp?: Record; - extract(url: string): string; -}; - -export type Services = [VideoService, Service][]; - export const defaults: VideoFullOptions = { videoUrl, youtube: {width: 640, height: 390}, diff --git a/src/transform/plugins/video/index.ts b/src/transform/plugins/video/index.ts index 82678616..9262740c 100644 --- a/src/transform/plugins/video/index.ts +++ b/src/transform/plugins/video/index.ts @@ -83,7 +83,7 @@ function tokenizeVideo(md: MarkdownIt, options: VideoFullOptions): Renderer.Rend }; } -const EMBED_REGEX = /@\[([a-zA-Z].+)]\([\s]*(.*?)[\s]*[)]/im; +const EMBED_REGEX = /@\[([a-zA-Z]*?)]\([\s]*(.*?)[\s]*[)]/im; function videoEmbed(md: MarkdownIt, _options: VideoFullOptions): ParserInline.RuleInline { return (state, silent) => { @@ -98,12 +98,11 @@ function videoEmbed(md: MarkdownIt, _options: VideoFullOptions): ParserInline.Ru } const match = EMBED_REGEX.exec(state.src.slice(state.pos, state.src.length)); - if (!match || match.length < 3) { return false; } - const service = match[1]; + const service = match[1] || 'url'; const parsed = parseVideoUrl(service, match[2]); if (parsed === false) { @@ -126,13 +125,19 @@ function videoEmbed(md: MarkdownIt, _options: VideoFullOptions): ParserInline.Ru const newState = new theState.md.inline.State(service, theState.md, theState.env, []); newState.md.inline.tokenize(newState); - const token = theState.push('video', '', 0); - (token as VideoToken).videoID = videoID; - (token as VideoToken).service = service; + const token = theState.push('video', '', 0) as VideoToken; + + token.videoID = videoID; + token.service = service; + token.level = theState.level; } - theState.pos += theState.src.indexOf(')', theState.pos); + if (service === 'url') { + theState.pos = theState.src.indexOf(')', theState.pos) + 1; + } else { + theState.pos += theState.src.indexOf(')', theState.pos); + } if (csp) { state.env.meta ??= {}; diff --git a/src/transform/plugins/video/parsers.ts b/src/transform/plugins/video/parsers.ts index 0a01d28d..46aa3a1b 100644 --- a/src/transform/plugins/video/parsers.ts +++ b/src/transform/plugins/video/parsers.ts @@ -1,4 +1,5 @@ -import {Services} from './const'; +import {VideoService} from './const'; +import {Services} from './types'; const ytRegex = /^.*((youtu.be\/)|(v\/)|(\/u\/\w\/)|(embed\/)|(watch\?))\??v?=?([^#&?]*).*/; export function youtubeParser(url: string) { @@ -53,7 +54,7 @@ export function rutubeParser(url: string) { const urlParser = (url: string) => url; -const supportedServices = Object.entries({ +const supportedServices = { osf: { extract: mfrParser, }, @@ -75,37 +76,37 @@ const supportedServices = Object.entries({ vk: { extract: vkParser, csp: { - 'frame-src': 'https://vk.com/', + 'frame-src': ['https://vk.com/'], }, }, rutube: { extract: rutubeParser, csp: { - 'frame-src': 'https://rutube.ru/play/embed/', + 'frame-src': ['https://rutube.ru/play/embed/'], }, }, url: { extract: urlParser, }, -}) as Services; +} as Services; export function parseVideoUrl(service: string, url: string) { let videoID = ''; const normalizedService = service.toLowerCase(); - const parsed = supportedServices.find(([name]) => name === normalizedService); + const parsed = supportedServices[normalizedService as VideoService]; if (!parsed) { return false; } - const [, videoParser] = parsed; + const {extract, csp} = parsed; - videoID = videoParser.extract(url); + videoID = extract(url); // If the videoID field is empty, regex currently make it the close parenthesis. if (videoID === ')') { videoID = ''; } - return [videoID, videoParser.csp] as const; + return [videoID, csp] as const; } diff --git a/src/transform/plugins/video/types.ts b/src/transform/plugins/video/types.ts index 73557c59..6b525bc5 100644 --- a/src/transform/plugins/video/types.ts +++ b/src/transform/plugins/video/types.ts @@ -17,6 +17,13 @@ export type VideoServicesOptions = { }; }; +export type Service = { + csp?: Record; + extract(url: string): string; +}; + +export type Services = Record; + export type VideoFullOptions = VideoServicesOptions & { videoUrl: VideoUrlFn; }; diff --git a/test/data/video/video-fixtures.txt b/test/data/video/video-fixtures.txt index b603792a..e4289550 100644 --- a/test/data/video/video-fixtures.txt +++ b/test/data/video/video-fixtures.txt @@ -325,7 +325,7 @@ Coverage. Line Breaks . @[vine](MhQ2lvg29Un) @[vine](MhQ2lvg29Un) . -

@vine

+

. . @[vine](MhQ2lvg29Un) diff --git a/test/video.test.ts b/test/video.test.ts index e7618197..0973cedd 100644 --- a/test/video.test.ts +++ b/test/video.test.ts @@ -11,42 +11,42 @@ function preserveId(html: string) { .replace(/new mfr\.Render\("\d+.\d+"/, 'new mfr.Render("1.2"'); } -describe('md-video', function () { +describe('md-video', () => { const md = new MarkdownIt({}).use(video); generate(path.join(__dirname, 'data/video/video-fixtures.txt'), md); }); // Because the mfr iframe requires a random id these tests cannont be part of // the markdown-it-testgen fixture -describe('md-video-mfr', function () { +describe('md-video-mfr', () => { const md = new MarkdownIt({}).use(video); - it('make sure normal iframe generates properly when empty', function () { + it('make sure normal iframe generates properly when empty', () => { const renderedHtml = preserveId(md.render('@[osf]()')); expect(renderedHtml).toMatchSnapshot(); }); - it('make sure normal iframe generates properly with guid', function () { + it('make sure normal iframe generates properly with guid', () => { const renderedHtml = preserveId(md.render('@[osf](xxxxx)')); expect(renderedHtml).toMatchSnapshot(); }); - it('make sure normal iframe generates properly with guid and line break', function () { + it('make sure normal iframe generates properly with guid and line break', () => { const renderedHtml = preserveId(md.render('@[osf](xxxxx\n)')); expect(renderedHtml).toMatchSnapshot(); }); - it('make sure normal iframe generates properly with guid and extra space', function () { + it('make sure normal iframe generates properly with guid and extra space', () => { const renderedHtml = preserveId(md.render('@[osf](xxxxx )')); expect(renderedHtml).toMatchSnapshot(); }); - it('make sure normal iframe generates properly with guid and two extra spaces', function () { + it('make sure normal iframe generates properly with guid and two extra spaces', () => { const renderedHtml = preserveId(md.render('@[osf]( xxxxx )')); expect(renderedHtml).toMatchSnapshot(); }); - it('make sure normal iframe generates properly with link', function () { + it('make sure normal iframe generates properly with link', () => { const renderedHtml = preserveId( md.render( '@[osf](https://mfr.osf.io/render?url=https://osf.io/xxxxx/?action=download%26mode=render)', @@ -55,7 +55,7 @@ describe('md-video-mfr', function () { expect(renderedHtml).toMatchSnapshot(); }); - it('make sure normal iframe generates properly with link and extra space', function () { + it('make sure normal iframe generates properly with link and extra space', () => { const renderedHtml = preserveId( md.render( '@[osf](https://mfr.osf.io/render?url=https://osf.io/xxxxx/?action=download%26mode=render )', @@ -64,7 +64,7 @@ describe('md-video-mfr', function () { expect(renderedHtml).toMatchSnapshot(); }); - it('make sure normal iframe generates properly with link and two extra spaces', function () { + it('make sure normal iframe generates properly with link and two extra spaces', () => { const renderedHtml = preserveId( md.render( '@[osf](https://mfr.osf.io/render?url=https://osf.io/xxxxx/?action=download%26mode=render )', @@ -73,7 +73,7 @@ describe('md-video-mfr', function () { expect(renderedHtml).toMatchSnapshot(); }); - it('make sure normal iframe generates properly with link to staging', function () { + it('make sure normal iframe generates properly with link to staging', () => { const renderedHtml = preserveId( md.render( '@[osf](https://mfr-staging3.osf.io/render?url=https://staging3.osf.io/xxxxx/?action=download%26mode=render)', @@ -82,7 +82,7 @@ describe('md-video-mfr', function () { expect(renderedHtml).toMatchSnapshot(); }); - it('make sure normal iframe generates properly with link to local', function () { + it('make sure normal iframe generates properly with link to local', () => { const renderedHtml = preserveId( md.render( '@[osf](https://localhost:7778/render?url=https://localhost:5000/xxxxx/?action=download%26mode=render)', @@ -91,7 +91,7 @@ describe('md-video-mfr', function () { expect(renderedHtml).toMatchSnapshot(); }); - it('make sure normal iframe generates properly with link to local ip', function () { + it('make sure normal iframe generates properly with link to local ip', () => { const renderedHtml = preserveId( md.render( '@[osf](http://localhost:7778/render?mode=render&url=http://192.168.168.167:5000/y98tn/?action=download%26mode=render%26direct)',