From ea0dc95d0a52d008760e4774769e4d665e73b9be Mon Sep 17 00:00:00 2001 From: Cesar Augusto Date: Mon, 30 Sep 2024 14:02:43 +0200 Subject: [PATCH] Fix youtube links match --- .../react/src/components/Youtube/Youtube.tsx | 4 +- src/extensions/LinkExtension.ts | 2 +- src/helpers/__tests__/utils.test.ts | 38 +++++++++++++++++++ src/helpers/utils.ts | 15 +++++--- 4 files changed, 50 insertions(+), 9 deletions(-) create mode 100644 src/helpers/__tests__/utils.test.ts diff --git a/examples/react/src/components/Youtube/Youtube.tsx b/examples/react/src/components/Youtube/Youtube.tsx index 90ef8f0..331d5d7 100644 --- a/examples/react/src/components/Youtube/Youtube.tsx +++ b/examples/react/src/components/Youtube/Youtube.tsx @@ -2,7 +2,7 @@ import { useMemo } from 'react' import LiteYouTubeEmbed from 'react-lite-youtube-embed' import 'react-lite-youtube-embed/dist/LiteYouTubeEmbed.css' -const REGEX_VIDEO_ID = /.*(?:youtu.be\/|v\/|u\/\w\/|embed\/|watch\?v=)([^#&?]*).*/ +const REGEX_VIDEO_ID = /.*(?:youtu.be\/|v\/|u\/\w\/|shorts|embed\/|watch\?v=)([^#&?]*).*/ type Props = { src: string @@ -12,7 +12,7 @@ export function Youtube(props: Props) { const { src } = props const embedId = useMemo(() => { - return src.match(REGEX_VIDEO_ID)?.[1] + return src.match(REGEX_VIDEO_ID)?.[1].replace('/', '') }, [src]) return ( diff --git a/src/extensions/LinkExtension.ts b/src/extensions/LinkExtension.ts index 7000291..c432a51 100644 --- a/src/extensions/LinkExtension.ts +++ b/src/extensions/LinkExtension.ts @@ -30,7 +30,7 @@ export const LinkExtension = Link.configure({ autolink: false }).extend({ const { nodes, marks } = state.schema const imeta = this.editor.storage.nostr.imeta const url = match.data?.href - const kind = getLinkKind(url, url, imeta) + const kind = getLinkKind(url, imeta) if (kind !== 'text' && nodes[kind]) { state.tr.replaceWith(from, to, nodes[kind].create({ src: url })) return diff --git a/src/helpers/__tests__/utils.test.ts b/src/helpers/__tests__/utils.test.ts new file mode 100644 index 0000000..cce9249 --- /dev/null +++ b/src/helpers/__tests__/utils.test.ts @@ -0,0 +1,38 @@ +import { getLinkKind } from '../utils' + +describe('getLinkKind', () => { + test('assert image links', () => { + expect(getLinkKind('https://nostr.com/image')).toBe('text') + expect(getLinkKind('https://nostr.com/image.jpg')).toBe('image') + expect(getLinkKind('https://nostr.com/image.png')).toBe('image') + expect(getLinkKind('https://nostr.com/image.webp')).toBe('image') + expect(getLinkKind('https://nostr.com/imagejpg')).toBe('text') + expect(getLinkKind('https://nostr.com/imagepng')).toBe('text') + }) + + test('assert video links', () => { + expect(getLinkKind('https://nostr.com/video')).toBe('text') + expect(getLinkKind('https://nostr.com/video.webm')).toBe('video') + expect(getLinkKind('https://nostr.com/video.mp4')).toBe('video') + expect(getLinkKind('https://nostr.com/video.ogg')).toBe('video') + expect(getLinkKind('https://nostr.com/videowebm')).toBe('text') + expect(getLinkKind('https://nostr.com/videomp4')).toBe('text') + }) + + test('assert youtube links', () => { + expect(getLinkKind('https://youtube.com')).toBe('text') + expect(getLinkKind('https://youtube.com/@user')).toBe('text') + expect(getLinkKind('https://www.youtube.com/@user')).toBe('text') + expect(getLinkKind('https://youtube.com/@user/feature')).toBe('text') + expect(getLinkKind('https://youtube.com/shorts/abcdef12345')).toBe('youtube') + expect(getLinkKind('https://www.youtube.com/watch?v=aA-jiiepOrE&t=924s')).toBe('youtube') + expect(getLinkKind('https://youtube.com/watch?v=aA-jiiepOrE&t=924s')).toBe('youtube') + expect(getLinkKind('https://youtu.be/aA-jiiepOrE?si=YguTWCcr8-fBWq9h')).toBe('youtube') + expect(getLinkKind('https://www.youtube.com/embed/aA-jiiepOrE?si=tVlJ8q_QSP_3yPHM')).toBe('youtube') + }) + + test('assert twitter links', () => { + expect(getLinkKind('https://x.com/halfin/status/1110302988')).toBe('tweet') + expect(getLinkKind('https://twitter.com/halfin/status/1110302988')).toBe('tweet') + }) +}) diff --git a/src/helpers/utils.ts b/src/helpers/utils.ts index 555e033..0b60c10 100644 --- a/src/helpers/utils.ts +++ b/src/helpers/utils.ts @@ -13,22 +13,25 @@ export function parseRelayAttribute(element: HTMLElement) { export type LinkKinds = 'text' | 'image' | 'video' | 'tweet' | 'youtube' -const IMAGE_EXTENSIONS = /.(jpg|jpeg|gif|png|bmp|svg|webp)$/ -const VIDEO_EXTENSIONS = /.(webm|mp4|ogg|mov)$/ +const IMAGE_EXTENSIONS = /\.(jpg|jpeg|gif|png|bmp|svg|webp)$/ +const VIDEO_EXTENSIONS = /\.(webm|mp4|ogg|mov)$/ +const YOUTUBE_EMBED = + /^(?:(?:https?:)?\/\/)?(?:(?:(?:www|m(?:usic)?)\.)?youtu(?:\.be|be\.com)\/(?:shorts\/|live\/|v\/|e(?:mbed)?\/|watch(?:\/|\?(?:\S+=\S+&)*v=)|oembed\?url=https?%3A\/\/(?:www|m(?:usic)?)\.youtube\.com\/watch\?(?:\S+=\S+&)*v%3D|attribution_link\?(?:\S+=\S+&)*u=(?:\/|%2F)watch(?:\?|%3F)v(?:=|%3D))?|www\.youtube-nocookie\.com\/embed\/)([\w-]{1})[?&#]?\S*$/ +const TWITTER_EMBED = /^https?:\/\/(twitter|x)\.com\/(?:#!\/)?(\w+)\/status(es)?\/(\d+)/ -export function getLinkKind(url: string, href: string, imeta?: IMetaTags): LinkKinds { +export function getLinkKind(url: string, imeta?: IMetaTags): LinkKinds { const mimetype = imeta?.[url]?.m?.split?.('/')?.[0] if (mimetype === 'image') { return 'image' } else if (mimetype === 'video') { return 'video' - } else if (/youtube|youtu.be/.test(url)) { + } else if (YOUTUBE_EMBED.test(url)) { return 'youtube' - } else if (/^https?:\/\/(twitter|x)\.com\/(?:#!\/)?(\w+)\/status(es)?\/(\d+)/.test(url)) { + } else if (TWITTER_EMBED.test(url)) { return 'tweet' } else { try { - const { pathname } = new URL(href) + const { pathname } = new URL(url) return IMAGE_EXTENSIONS.test(pathname) ? 'image' : VIDEO_EXTENSIONS.test(pathname) ? 'video' : 'text' } catch (error) { console.log('url parser error', error)