Video tag/link markdown filter

This commit is contained in:
tidy-dev 2022-03-14 12:39:32 -04:00
parent 2ac089dc11
commit b67fc6a6c2
5 changed files with 149 additions and 0 deletions

View file

@ -4,6 +4,8 @@ import { EmojiFilter } from './emoji-filter'
import { IssueLinkFilter } from './issue-link-filter'
import { IssueMentionFilter } from './issue-mention-filter'
import { MentionFilter } from './mention-filter'
import { VideoLinkFilter } from './video-link-filter'
import { VideoTagFilter } from './video-tag-filter'
export interface INodeFilter {
/**
@ -44,6 +46,8 @@ export const buildCustomMarkDownNodeFilterPipe = memoizeOne(
new IssueLinkFilter(repository),
new EmojiFilter(emoji),
new MentionFilter(repository),
new VideoTagFilter(),
new VideoLinkFilter(),
]
)

View file

@ -0,0 +1,82 @@
import { INodeFilter } from './node-filter'
import { githubAssetVideoRegex } from './video-url-regex'
/**
* The Video Link filter matches a github-flavored markdown target of a link,
* like
* <p><a href="https://user-images.githubusercontent.com/7559041/1234.mp4"></a></p>.
*
* This type of pattern is formed when a user pastes the video url in markdown
* editor and then the markdown parser auto links it.
*
* If the url is in the format of a github user asset, it will replace the
* paragraph with link with a a video tag. If not, the link is left unmodified.
*/
export class VideoLinkFilter implements INodeFilter {
/**
* Video link matches on p tags with an a tag with a single child of a tag
* with an href that's host matches a pattern of video url that is a github
* user asset.
*/
public createFilterTreeWalker(doc: Document): TreeWalker {
// eslint-disable-next-line @typescript-eslint/no-this-alias
const filter = this
return doc.createTreeWalker(doc, NodeFilter.SHOW_ELEMENT, {
acceptNode: function (el: Element) {
return filter.getGithubVideoLink(el) === null
? NodeFilter.FILTER_SKIP
: NodeFilter.FILTER_ACCEPT
},
})
}
/**
* Takes a paragraph element with a single anchor element that's href appears
* to be be a video link and replaces it with a video tag.
*
* Example:
* <p>
* <a href="https://user-images.githubusercontent.com/7559041/1234.mp4">
* https://user-images.githubusercontent.com/7559041/1234.mp4
* </a>
* </p>
*
* Output = [
* <videosrc="https://user-images.githubusercontent.com/7559041/1234.mp4"></video>
* ]
*/
public async filter(node: Node): Promise<ReadonlyArray<Node> | null> {
const videoSrc = this.getGithubVideoLink(node)
if (videoSrc === null) {
return null
}
const videoNode = document.createElement('video')
videoNode.src = videoSrc
return [videoNode]
}
/**
* If the give node is a video url post markdown parsing, it returns the video
* url, else return null.
*
* Video url post markdown parsing looks like:
* <p>
* <a href="https://user-images.githubusercontent.com/7559041/1234.mp4">
* https://user-images.githubusercontent.com/7559041/1234.mp4
* </a>
* </p>
* */
private getGithubVideoLink(node: Node): string | null {
if (
node instanceof HTMLParagraphElement &&
node.childElementCount === 1 &&
node.firstChild instanceof HTMLAnchorElement &&
githubAssetVideoRegex.test(node.firstChild.href)
) {
return node.firstChild.href
}
return null
}
}

View file

@ -0,0 +1,45 @@
import { INodeFilter } from './node-filter'
import { githubAssetVideoRegex } from './video-url-regex'
/**
* The Video Link filter matches embedded video tags, like
*
* <video src="https://user-images.githubusercontent.com/7559041/1234.mp4"></video>
*
* If the url for the src does NOT match the pattern of a github user asset, we
* remove the video tag.
*/
export class VideoTagFilter implements INodeFilter {
/**
* Video link filter matches on video tags that src does not match a github user asset url.
*/
public createFilterTreeWalker(doc: Document): TreeWalker {
// eslint-disable-next-line @typescript-eslint/no-this-alias
return doc.createTreeWalker(doc, NodeFilter.SHOW_ELEMENT, {
acceptNode: function (el: Element) {
return !(el instanceof HTMLVideoElement) ||
githubAssetVideoRegex.test(el.src)
? NodeFilter.FILTER_SKIP
: NodeFilter.FILTER_ACCEPT
},
})
}
/**
* Takes a video element who's src host is not a github user asset url and removes it.
*/
public async filter(node: Node): Promise<ReadonlyArray<Node> | null> {
if (
!(node instanceof HTMLVideoElement) ||
githubAssetVideoRegex.test(node.src)
) {
// If it is video element with a valid source, we return null to leave it alone.
// This is different then dotcom that regenerates a video tag because it
// verifies through a db call that the assets exists
return null
}
// Return empty array so that video tag is removed
return []
}
}

View file

@ -0,0 +1,14 @@
import { escapeRegExp } from 'lodash'
const user_images_cdn_url = 'https://user-images.githubusercontent.com'
// List of common video formats obtained from
// https://developer.mozilla.org/en-US/docs/https://developer.mozilla.org/en-US/docs/Web/Media/Formats/Video_codecs/Media/Formats/Video_codecs
// The MP4, WebM, and Ogg formats are supported by HTML standard.
const videoExtensionRegex = /(mp4|webm|ogg|mov|qt|avi|wmv|3gp|mpg|mpeg|)$/
/** Regex for checking if a url is a github asset cdn video url */
export const githubAssetVideoRegex = new RegExp(
escapeRegExp(user_images_cdn_url) + '.+' + videoExtensionRegex.source,
'i'
)

View file

@ -284,6 +284,10 @@ changes from the original, just a note that this will not match 1-1)
background-color: var(--md-canvas-default-color)
}
.markdown-body video {
max-width: 100%;
}
.markdown-body img[align=right] {
padding-left: 20px
}