From 41431f8c3b4c358dfb66e9b3997e0560f536718d Mon Sep 17 00:00:00 2001 From: Eduardo Sacco <3680995+nastita@users.noreply.github.com> Date: Fri, 18 Oct 2024 16:35:50 -0300 Subject: [PATCH 01/36] WIP parser refactor and updates --- .../grid/utils/__tests__/gridParser.spec.ts | 640 ++++++++++-------- domains/grid/utils/gridParser.ts | 357 +++++----- 2 files changed, 566 insertions(+), 431 deletions(-) diff --git a/domains/grid/utils/__tests__/gridParser.spec.ts b/domains/grid/utils/__tests__/gridParser.spec.ts index 9c3a372e..e0fdf2ad 100644 --- a/domains/grid/utils/__tests__/gridParser.spec.ts +++ b/domains/grid/utils/__tests__/gridParser.spec.ts @@ -1,5 +1,4 @@ import { describe, expect, it } from 'vitest' -import { parsePlatformInput } from '../gridParser' const YOUTUBE_IFRAME_ALLOW = 'accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share' @@ -8,300 +7,395 @@ const SPOTIFY_IFRAME_ALLOW = const SOUNDCLOUD_IFRAME_ALLOW = 'autoplay; clipboard-write; encrypted-media; fullscreen; picture-in-picture' -describe('Widget Input Parsing', () => { - it.each([ - [ - 'X (Twitter) Post URL', - GRID_WIDGET_TYPE.X, - 'https://x.com/feindura/status/1804519711377436675', - { - type: GRID_WIDGET_TYPE.X, - properties: { - src: 'https://twitter.com/feindura/status/1804519711377436675?ref_src=twsrc%5Etfw', - type: 'post', +describe('parsePlatformInput', () => { + describe('X', () => { + it.each([ + [ + 'Handle', + GRID_WIDGET_TYPE.X, + '@feindura', + { + type: GRID_WIDGET_TYPE.X, + properties: { + src: 'https://twitter.com/feindura?ref_src=twsrc%5Etfw', + handle: 'feindura', + }, }, - }, - ], - [ - 'X (Twitter) Post Embed Code', - GRID_WIDGET_TYPE.X, - '

Get to know the @lukso_io ecosystem in 3min ๐ŸŽง pic.twitter.com/UUh23NgdfQ

— Fabian Vogelsteller (@feindura) June 22, 2024
', - { - type: GRID_WIDGET_TYPE.X, - properties: { - src: 'https://twitter.com/feindura/status/1804519711377436675?ref_src=twsrc%5Etfw', - type: 'post', + ], + [ + 'Post URL', + GRID_WIDGET_TYPE.X, + 'https://x.com/feindura/status/1804519711377436675', + { + type: GRID_WIDGET_TYPE.X, + properties: { + src: 'https://twitter.com/feindura/status/1804519711377436675', + handle: 'feindura', + id: '1804519711377436675', + }, }, - }, - ], - [ - 'X (Twitter) Timeline URL', - GRID_WIDGET_TYPE.X, - 'https://twitter.com/lukso_io', - { - type: GRID_WIDGET_TYPE.X, - properties: { - src: 'https://twitter.com/lukso_io?ref_src=twsrc%5Etfw', - type: 'timeline', + ], + [ + 'Post URL twitter.com', + GRID_WIDGET_TYPE.X, + 'https://twitter.com/feindura/status/1804519711377436675', + { + type: GRID_WIDGET_TYPE.X, + properties: { + src: 'https://twitter.com/feindura/status/1804519711377436675', + handle: 'feindura', + id: '1804519711377436675', + }, }, - }, - ], - [ - 'X (Twitter) Timeline Embed Code', - GRID_WIDGET_TYPE.X, - 'Tweets by lukso_io ', - { - type: GRID_WIDGET_TYPE.X, - properties: { - src: 'https://twitter.com/lukso_io?ref_src=twsrc%5Etfw', - type: 'timeline', + ], + [ + 'Post Embed Code', + GRID_WIDGET_TYPE.X, + '

Get to know the @lukso_io ecosystem in 3min ๐ŸŽง pic.twitter.com/UUh23NgdfQ

— Fabian Vogelsteller (@feindura) June 22, 2024
', + { + type: GRID_WIDGET_TYPE.X, + properties: { + src: 'https://twitter.com/feindura/status/1804519711377436675?ref_src=twsrc%5Etfw', + handle: 'feindura', + id: '1804519711377436675', + }, }, - }, - ], - [ - 'Instagram Post URL', - GRID_WIDGET_TYPE.INSTAGRAM, - 'https://www.instagram.com/p/C98OXs6yhAq/?utm_source=ig_embed&utm_campaign=loading', - { - type: GRID_WIDGET_TYPE.INSTAGRAM, - properties: { - src: 'https://www.instagram.com/p/C98OXs6yhAq/?utm_source=ig_embed&utm_campaign=loading', - type: 'p', + ], + [ + 'Timeline URL', + GRID_WIDGET_TYPE.X, + 'https://x.com/lukso_io', + { + type: GRID_WIDGET_TYPE.X, + properties: { + src: 'https://twitter.com/lukso_io', + handle: 'lukso_io', + }, }, - }, - ], - [ - 'Instagram Post Embed Code', - GRID_WIDGET_TYPE.INSTAGRAM, - '

A post shared by Red Hot Chili Peppers (@chilipeppers)

', - { - type: GRID_WIDGET_TYPE.INSTAGRAM, - properties: { - src: 'https://www.instagram.com/p/C98OXs6yhAq/?utm_source=ig_embed&utm_campaign=loading', - type: 'p', + ], + [ + 'Timeline URL twitter.com', + GRID_WIDGET_TYPE.X, + 'https://twitter.com/lukso_io', + { + type: GRID_WIDGET_TYPE.X, + properties: { + src: 'https://twitter.com/lukso_io', + handle: 'lukso_io', + }, }, - }, - ], - [ - 'Instagram Reel URL', - GRID_WIDGET_TYPE.INSTAGRAM, - 'https://www.instagram.com/reel/DAlOgHkuyxd/?utm_source=ig_embed&utm_campaign=loading', - { - type: GRID_WIDGET_TYPE.INSTAGRAM, - properties: { - src: 'https://www.instagram.com/reel/DAlOgHkuyxd/?utm_source=ig_embed&utm_campaign=loading', - type: 'reel', + ], + [ + 'Timeline Embed Code', + GRID_WIDGET_TYPE.X, + 'Tweets by lukso_io ', + { + type: GRID_WIDGET_TYPE.X, + properties: { + src: 'https://twitter.com/lukso_io?ref_src=twsrc%5Etfw', + handle: 'lukso_io', + }, }, - }, - ], - [ - 'Instagram Reel Embed Code', - GRID_WIDGET_TYPE.INSTAGRAM, - '
View this post on Instagram

A post shared by memes (@uncrustable.memess)

', - { - type: GRID_WIDGET_TYPE.INSTAGRAM, - properties: { - src: 'https://www.instagram.com/reel/DAlOgHkuyxd/?utm_source=ig_embed&utm_campaign=loading', - type: 'reel', + ], + ])( + 'correctly parses %s', + async (_description, platform, input, expected) => { + const result = await parsePlatformInput(platform, input) + expect(result).toEqual(expected) + } + ) + }) + + describe('YOUTUBE', () => { + it.each([ + [ + 'URL', + GRID_WIDGET_TYPE.YOUTUBE, + 'https://www.youtube.com/watch?v=Vw4JE64hsO8', + { + type: GRID_WIDGET_TYPE.IFRAME, + properties: { + src: 'https://www.youtube.com/embed/Vw4JE64hsO8', + allow: YOUTUBE_IFRAME_ALLOW, + }, }, - }, - ], - [ - 'YouTube URL', - GRID_WIDGET_TYPE.YOUTUBE, - 'https://www.youtube.com/watch?v=Vw4JE64hsO8', - { - type: GRID_WIDGET_TYPE.IFRAME, - properties: { - src: 'https://www.youtube.com/embed/Vw4JE64hsO8', - allow: YOUTUBE_IFRAME_ALLOW, + ], + [ + 'Embed Code', + GRID_WIDGET_TYPE.YOUTUBE, + '', + { + type: GRID_WIDGET_TYPE.IFRAME, + properties: { + src: 'https://www.youtube.com/embed/Vw4JE64hsO8', + allow: YOUTUBE_IFRAME_ALLOW, + }, }, - }, - ], - [ - 'YouTube Embed Code', - GRID_WIDGET_TYPE.YOUTUBE, - '', - { - type: GRID_WIDGET_TYPE.IFRAME, - properties: { - src: 'https://www.youtube.com/embed/Vw4JE64hsO8', - allow: YOUTUBE_IFRAME_ALLOW, + ], + ])( + 'correctly parses %s', + async (_description, platform, input, expected) => { + const result = await parsePlatformInput(platform, input) + expect(result).toEqual(expected) + } + ) + }) + + describe('SPOTIFY', () => { + it.each([ + [ + 'Track URL', + GRID_WIDGET_TYPE.SPOTIFY, + 'https://open.spotify.com/track/7xGfFoTpQ2E7fRF5lN10tr', + { + type: GRID_WIDGET_TYPE.IFRAME, + properties: { + src: 'https://open.spotify.com/embed/track/7xGfFoTpQ2E7fRF5lN10tr?utm_source=oembed', + allow: SPOTIFY_IFRAME_ALLOW, + type: 'track', + id: '7xGfFoTpQ2E7fRF5lN10tr', + }, }, - }, - ], - [ - 'Spotify Track URL', - GRID_WIDGET_TYPE.SPOTIFY, - 'https://open.spotify.com/track/7xGfFoTpQ2E7fRF5lN10tr', - { - type: GRID_WIDGET_TYPE.SPOTIFY, - properties: { - src: 'https://open.spotify.com/embed/track/7xGfFoTpQ2E7fRF5lN10tr?utm_source=generator', - allow: SPOTIFY_IFRAME_ALLOW, - type: 'track', + ], + [ + 'Track Embed Code', + GRID_WIDGET_TYPE.SPOTIFY, + '', + { + type: GRID_WIDGET_TYPE.IFRAME, + properties: { + src: 'https://open.spotify.com/embed/track/2BHj31ufdEqVK5CkYDp9mA?utm_source=generator', + allow: SPOTIFY_IFRAME_ALLOW, + type: 'track', + id: '2BHj31ufdEqVK5CkYDp9mA', + }, }, - }, - ], - [ - 'Spotify Track Embed Code', - GRID_WIDGET_TYPE.SPOTIFY, - '', - { - type: GRID_WIDGET_TYPE.SPOTIFY, - properties: { - src: 'https://open.spotify.com/embed/track/2BHj31ufdEqVK5CkYDp9mA?utm_source=generator', - allow: SPOTIFY_IFRAME_ALLOW, - type: 'track', + ], + [ + 'Track Embed Code with theme', + GRID_WIDGET_TYPE.SPOTIFY, + '', + { + type: GRID_WIDGET_TYPE.IFRAME, + properties: { + src: 'https://open.spotify.com/embed/track/48K735Rd3UQExzjXH004k1?utm_source=generator&theme=0', + allow: SPOTIFY_IFRAME_ALLOW, + type: 'track', + id: '48K735Rd3UQExzjXH004k1', + theme: '0', + }, }, - }, - ], - [ - 'Spotify Track Embed Code with theme', - GRID_WIDGET_TYPE.SPOTIFY, - '', - { - type: GRID_WIDGET_TYPE.SPOTIFY, - properties: { - src: 'https://open.spotify.com/embed/track/48K735Rd3UQExzjXH004k1?utm_source=generator&theme=0', - allow: SPOTIFY_IFRAME_ALLOW, - type: 'track', - theme: '0', + ], + [ + 'Playlist URL', + GRID_WIDGET_TYPE.SPOTIFY, + 'https://open.spotify.com/playlist/7KFoK4LJ23EncELJwYmTDG', + { + type: GRID_WIDGET_TYPE.IFRAME, + properties: { + src: 'https://open.spotify.com/embed/playlist/7KFoK4LJ23EncELJwYmTDG?utm_source=oembed', + allow: SPOTIFY_IFRAME_ALLOW, + type: 'playlist', + id: '7KFoK4LJ23EncELJwYmTDG', + }, }, - }, - ], - [ - 'Spotify Playlist URL', - GRID_WIDGET_TYPE.SPOTIFY, - 'https://open.spotify.com/playlist/7KFoK4LJ23EncELJwYmTDG', - { - type: GRID_WIDGET_TYPE.SPOTIFY, - properties: { - src: 'https://open.spotify.com/embed/playlist/7KFoK4LJ23EncELJwYmTDG?utm_source=generator', - allow: SPOTIFY_IFRAME_ALLOW, - type: 'playlist', + ], + [ + 'Playlist Embed Code', + GRID_WIDGET_TYPE.SPOTIFY, + '', + { + type: GRID_WIDGET_TYPE.IFRAME, + properties: { + src: 'https://open.spotify.com/embed/playlist/7KFoK4LJ23EncELJwYmTDG?utm_source=generator', + allow: SPOTIFY_IFRAME_ALLOW, + type: 'playlist', + id: '7KFoK4LJ23EncELJwYmTDG', + }, }, - }, - ], - [ - 'Spotify Playlist Embed Code', - GRID_WIDGET_TYPE.SPOTIFY, - '', - { - type: GRID_WIDGET_TYPE.SPOTIFY, - properties: { - src: 'https://open.spotify.com/embed/playlist/7KFoK4LJ23EncELJwYmTDG?utm_source=generator', - allow: SPOTIFY_IFRAME_ALLOW, - type: 'playlist', + ], + [ + 'Artist URL', + GRID_WIDGET_TYPE.SPOTIFY, + 'https://open.spotify.com/artist/4KY9rCrokaoFzvMfX98u1q', + { + type: GRID_WIDGET_TYPE.IFRAME, + properties: { + src: 'https://open.spotify.com/embed/artist/4KY9rCrokaoFzvMfX98u1q?utm_source=oembed', + allow: SPOTIFY_IFRAME_ALLOW, + type: 'artist', + id: '4KY9rCrokaoFzvMfX98u1q', + }, }, - }, - ], - [ - 'Spotify Artist URL', - GRID_WIDGET_TYPE.SPOTIFY, - 'https://open.spotify.com/artist/4KY9rCrokaoFzvMfX98u1q', - { - type: GRID_WIDGET_TYPE.SPOTIFY, - properties: { - src: 'https://open.spotify.com/embed/artist/4KY9rCrokaoFzvMfX98u1q?utm_source=generator', - allow: SPOTIFY_IFRAME_ALLOW, - type: 'artist', + ], + [ + 'Artist Embed Code', + GRID_WIDGET_TYPE.SPOTIFY, + '', + { + type: GRID_WIDGET_TYPE.IFRAME, + properties: { + src: 'https://open.spotify.com/embed/artist/4KY9rCrokaoFzvMfX98u1q?utm_source=generator', + allow: SPOTIFY_IFRAME_ALLOW, + type: 'artist', + id: '4KY9rCrokaoFzvMfX98u1q', + }, }, - }, - ], - [ - 'Spotify Artist Embed Code', - GRID_WIDGET_TYPE.SPOTIFY, - '', - { - type: GRID_WIDGET_TYPE.SPOTIFY, - properties: { - src: 'https://open.spotify.com/embed/artist/4KY9rCrokaoFzvMfX98u1q?utm_source=generator', - allow: SPOTIFY_IFRAME_ALLOW, - type: 'artist', + ], + ])( + 'correctly parses %s', + async (_description, platform, input, expected) => { + const result = await parsePlatformInput(platform, input) + expect(result).toEqual(expected) + } + ) + }) + + describe('SOUNDCLOUD', () => { + it.each([ + [ + 'Track Share URL', + GRID_WIDGET_TYPE.SOUNDCLOUD, + 'https://soundcloud.com/occams-laser/with-you', + { + type: GRID_WIDGET_TYPE.IFRAME, + properties: { + src: 'https://w.soundcloud.com/player/?visual=true&url=https%3A%2F%2Fapi.soundcloud.com%2Ftracks%2F1856391039&show_artwork=true', + allow: SOUNDCLOUD_IFRAME_ALLOW, + type: 'tracks', + }, }, - }, - ], - [ - 'SoundCloud Track Share URL', - GRID_WIDGET_TYPE.SOUNDCLOUD, - 'https://soundcloud.com/occams-laser/with-you', - { - type: GRID_WIDGET_TYPE.IFRAME, - properties: { - src: 'https://w.soundcloud.com/player/?visual=true&url=https%3A%2F%2Fapi.soundcloud.com%2Ftracks%2F1856391039&show_artwork=true', - allow: SOUNDCLOUD_IFRAME_ALLOW, - type: 'tracks', + ], + [ + 'Set Share URL', + GRID_WIDGET_TYPE.SOUNDCLOUD, + 'https://w.soundcloud.com/player/?visual=true&url=https%3A%2F%2Fapi.soundcloud.com%2Fplaylists%2F505007376&show_artwork=true', + { + type: GRID_WIDGET_TYPE.IFRAME, + properties: { + src: 'https://w.soundcloud.com/player/?visual=true&url=https%3A%2F%2Fapi.soundcloud.com%2Fplaylists%2F505007376&show_artwork=true', + allow: SPOTIFY_IFRAME_ALLOW, + type: 'playlists', + }, }, - }, - ], - [ - 'SoundCloud Set Share URL', - GRID_WIDGET_TYPE.SOUNDCLOUD, - 'https://w.soundcloud.com/player/?visual=true&url=https%3A%2F%2Fapi.soundcloud.com%2Fplaylists%2F505007376&show_artwork=true', - { - type: GRID_WIDGET_TYPE.IFRAME, - properties: { - src: 'https://w.soundcloud.com/player/?visual=true&url=https%3A%2F%2Fapi.soundcloud.com%2Fplaylists%2F505007376&show_artwork=true', - allow: SPOTIFY_IFRAME_ALLOW, - type: 'playlists', + ], + [ + 'Users Embed Code', + GRID_WIDGET_TYPE.SOUNDCLOUD, + 'https://soundcloud.com/fabian-vogelsteller', + { + type: GRID_WIDGET_TYPE.IFRAME, + properties: { + src: 'https://w.soundcloud.com/player/?visual=true&url=https%3A%2F%2Fapi.soundcloud.com%2Fusers%2F227118126&show_artwork=true', + allow: SOUNDCLOUD_IFRAME_ALLOW, + type: 'users', + }, }, - }, - ], - [ - 'SoundCloud Users Embed Code', - GRID_WIDGET_TYPE.SOUNDCLOUD, - 'https://soundcloud.com/fabian-vogelsteller', - { - type: GRID_WIDGET_TYPE.IFRAME, - properties: { - src: 'https://w.soundcloud.com/player/?visual=true&url=https%3A%2F%2Fapi.soundcloud.com%2Fusers%2F227118126&show_artwork=true', - allow: SOUNDCLOUD_IFRAME_ALLOW, - type: 'users', + ], + [ + 'Track Embed Code', + GRID_WIDGET_TYPE.SOUNDCLOUD, + '
Occams Laser ยท With You
', + { + type: GRID_WIDGET_TYPE.IFRAME, + properties: { + src: 'https://w.soundcloud.com/player/?url=https://api.soundcloud.com/tracks/1856391039&color=%23ff5500&auto_play=false&hide_related=false&show_comments=true&show_user=true&show_reposts=false&show_teaser=true&visual=true', + allow: SOUNDCLOUD_IFRAME_ALLOW, + type: 'tracks', + }, + }, + ], + [ + 'Track Embed Code with URL encoded characters', + GRID_WIDGET_TYPE.SOUNDCLOUD, + '', + { + type: GRID_WIDGET_TYPE.IFRAME, + properties: { + src: 'https://w.soundcloud.com/player/?visual=true&url=https%3A%2F%2Fapi.soundcloud.com%2Ftracks%2F1856391039&show_artwork=true', + allow: SOUNDCLOUD_IFRAME_ALLOW, + type: 'tracks', + }, + }, + ], + [ + 'Playlist Embed Code', + GRID_WIDGET_TYPE.SOUNDCLOUD, + '
vaporwave archive ยท Middle Mgmt - ๅคใ‚’่ชžใ‚‹ใฏๆ˜“ใ— // it's easy to talk about summer
', + { + type: GRID_WIDGET_TYPE.IFRAME, + properties: { + src: 'https://w.soundcloud.com/player/?url=https://api.soundcloud.com/playlists/1850298147&color=%23ff5500&auto_play=false&hide_related=false&show_comments=true&show_user=true&show_reposts=false&show_teaser=true&visual=true', + allow: SOUNDCLOUD_IFRAME_ALLOW, + type: 'playlists', + }, + }, + ], + ])( + 'correctly parses %s', + async (_description, platform, input, expected) => { + const result = await parsePlatformInput(platform, input) + expect(result).toEqual(expected) + } + ) + }) + + describe('INSTAGRAM', () => { + it.each([ + [ + 'Post URL', + GRID_WIDGET_TYPE.INSTAGRAM, + 'https://www.instagram.com/p/C98OXs6yhAq/?utm_source=ig_embed&utm_campaign=loading', + { + type: GRID_WIDGET_TYPE.INSTAGRAM, + properties: { + src: 'https://www.instagram.com/p/C98OXs6yhAq/?utm_source=ig_embed&utm_campaign=loading', + type: 'p', + }, }, - }, - ], - [ - 'SoundCloud Track Embed Code', - GRID_WIDGET_TYPE.SOUNDCLOUD, - '
Occams Laser ยท With You
', - { - type: GRID_WIDGET_TYPE.IFRAME, - properties: { - src: 'https://w.soundcloud.com/player/?url=https://api.soundcloud.com/tracks/1856391039&color=%23ff5500&auto_play=false&hide_related=false&show_comments=true&show_user=true&show_reposts=false&show_teaser=true&visual=true', - allow: SOUNDCLOUD_IFRAME_ALLOW, - type: 'tracks', + ], + [ + 'Post Embed Code', + GRID_WIDGET_TYPE.INSTAGRAM, + '

A post shared by Red Hot Chili Peppers (@chilipeppers)

', + { + type: GRID_WIDGET_TYPE.INSTAGRAM, + properties: { + src: 'https://www.instagram.com/p/C98OXs6yhAq/?utm_source=ig_embed&utm_campaign=loading', + type: 'p', + }, }, - }, - ], - [ - 'SoundCloud Track Embed Code with URL encoded characters', - GRID_WIDGET_TYPE.SOUNDCLOUD, - '', - { - type: GRID_WIDGET_TYPE.IFRAME, - properties: { - src: 'https://w.soundcloud.com/player/?visual=true&url=https%3A%2F%2Fapi.soundcloud.com%2Ftracks%2F1856391039&show_artwork=true', - allow: SOUNDCLOUD_IFRAME_ALLOW, - type: 'tracks', + ], + [ + 'Reel URL', + GRID_WIDGET_TYPE.INSTAGRAM, + 'https://www.instagram.com/reel/DAlOgHkuyxd/?utm_source=ig_embed&utm_campaign=loading', + { + type: GRID_WIDGET_TYPE.INSTAGRAM, + properties: { + src: 'https://www.instagram.com/reel/DAlOgHkuyxd/?utm_source=ig_embed&utm_campaign=loading', + type: 'reel', + }, }, - }, - ], - [ - 'SoundCloud Playlist Embed Code', - GRID_WIDGET_TYPE.SOUNDCLOUD, - '
vaporwave archive ยท Middle Mgmt - ๅคใ‚’่ชžใ‚‹ใฏๆ˜“ใ— // it's easy to talk about summer
', - { - type: GRID_WIDGET_TYPE.IFRAME, - properties: { - src: 'https://w.soundcloud.com/player/?url=https://api.soundcloud.com/playlists/1850298147&color=%23ff5500&auto_play=false&hide_related=false&show_comments=true&show_user=true&show_reposts=false&show_teaser=true&visual=true', - allow: SOUNDCLOUD_IFRAME_ALLOW, - type: 'playlists', + ], + [ + 'Reel Embed Code', + GRID_WIDGET_TYPE.INSTAGRAM, + '
View this post on Instagram

A post shared by memes (@uncrustable.memess)

', + { + type: GRID_WIDGET_TYPE.INSTAGRAM, + properties: { + src: 'https://www.instagram.com/reel/DAlOgHkuyxd/?utm_source=ig_embed&utm_campaign=loading', + type: 'reel', + }, }, - }, - ], - ])('correctly parses %s', async (_description, platform, input, expected) => { - const result = await parsePlatformInput(platform, input) - expect(result).toEqual(expected) + ], + ])( + 'correctly parses %s', + async (_description, platform, input, expected) => { + const result = await parsePlatformInput(platform, input) + expect(result).toEqual(expected) + } + ) }) }) diff --git a/domains/grid/utils/gridParser.ts b/domains/grid/utils/gridParser.ts index c90c9a40..c443150a 100644 --- a/domains/grid/utils/gridParser.ts +++ b/domains/grid/utils/gridParser.ts @@ -1,198 +1,239 @@ -export const parsePlatformInput = async ( - platform: GridWidgetType, - input: string -): Promise => { - switch (platform) { - case GRID_WIDGET_TYPE.X: - return parseXWidgetInput(input) - case GRID_WIDGET_TYPE.INSTAGRAM: - return parseInstagramWidgetInput(input) - case GRID_WIDGET_TYPE.YOUTUBE: - return parseYoutubeWidgetInput(input) - case GRID_WIDGET_TYPE.SPOTIFY: - return parseSpotifyWidgetInput(input) - case GRID_WIDGET_TYPE.SOUNDCLOUD: - return await parseSoundCloudWidgetInput(input) - default: - throw new Error('Invalid platform') - } +export type RegexWithCallback = { + regex: RegExp + callback: (url: string) => Promise } -const parseYoutubeWidgetInput = (input: string): LayoutItemExtended | never => { - const YOUTUBE_URL_REGEX = - /(?:https?:\/\/)?(?:www\.)?youtube\.com\/watch\?v=([^&]+)/ - const YOUTUBE_EMBED_REGEX = - /(?:https?:\/\/)?(?:www\.)?youtube\.com\/embed\/([^?]+)/ - const YOUTUBE_IFRAME_ALLOW = - 'accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share' - - const youtubeUrlMatch = input.match(YOUTUBE_URL_REGEX) - - if (youtubeUrlMatch) { - return { - type: GRID_WIDGET_TYPE.IFRAME, - properties: { - src: `https://www.youtube.com/embed/${youtubeUrlMatch[1]}`, - allow: YOUTUBE_IFRAME_ALLOW, - }, - } - } - - const youtubeEmbedMatch = input.match(YOUTUBE_EMBED_REGEX) - - if (youtubeEmbedMatch) { - return { - type: GRID_WIDGET_TYPE.IFRAME, - properties: { - src: `https://www.youtube.com/embed/${youtubeEmbedMatch[1]}`, - allow: YOUTUBE_IFRAME_ALLOW, - }, - } - } - - throw new Error('Invalid YouTube input') +export type PlatformParsingParameters = { + type: GridWidgetType + embedRegex: RegExp + secondaryRegexesWithCallbacks?: RegexWithCallback[] + constantProperties?: Record } -const parseXWidgetInput = (input: string): LayoutItemExtended | never => { - const X_POST_REGEX = - /https?:\/\/(?:www\.)?(?:x\.com|twitter\.com)\/(\w+)\/status\/(\d+)(?:\?ref_src=twsrc%5Etfw)?/ - const X_TIMELINE_REGEX = - /https?:\/\/(?:www\.)?(?:x\.com|twitter\.com)\/(\w+)(?:\?ref_src=twsrc%5Etfw)?/ - - const [, postUser, postId] = input.match(X_POST_REGEX) || [] - - if (postUser && postId) { - return { - type: GRID_WIDGET_TYPE.X, - properties: { - src: `https://twitter.com/${postUser}/status/${postId}?ref_src=twsrc%5Etfw`, - type: 'post', +const PLATFORM_PARSING_PARAMETERS: Record< + GridWidgetType, + PlatformParsingParameters | undefined +> = { + [GRID_WIDGET_TYPE.TITLE_LINK]: undefined, + [GRID_WIDGET_TYPE.TEXT]: undefined, + [GRID_WIDGET_TYPE.IFRAME]: undefined, + [GRID_WIDGET_TYPE.IMAGE]: undefined, + [GRID_WIDGET_TYPE.X]: { + type: GRID_WIDGET_TYPE.X, + embedRegex: + /https?:\/\/twitter\.com\/(?[a-zA-Z0-9_]+)(?:\/status\/(?\d+))?(?:\?[^"'\s]*)?/, + secondaryRegexesWithCallbacks: [ + // Match a handle with @ symbol + { + regex: /@([a-zA-Z0-9_]{1,15})/, + callback: getXOEmbedFromHandle, }, - } - } - - const [, timelineUser] = input.match(X_TIMELINE_REGEX) || [] - - if (timelineUser) { - return { - type: GRID_WIDGET_TYPE.X, - properties: { - src: `https://twitter.com/${timelineUser}?ref_src=twsrc%5Etfw`, - type: 'timeline', + // Match a Twitter URL with or without https www and status + { + regex: + /(https?:\/\/)?(?:x\.com|twitter\.com)\/[a-zA-Z0-9_]+(?:\/status\/(\d+))?/, + callback: async url => sanitizeXEmbedUrl(url), }, - } - } - - throw new Error('Invalid X input') -} - -const parseSpotifyWidgetInput = (input: string): LayoutItemExtended | never => { - const SPOTIFY_URL_REGEX = - /https?:\/\/(?:open\.)?spotify\.com\/(?:embed\/)?(?track|playlist|artist)\/(?[^?]+)(?:\?utm_source=generator)?(?:&theme=(?\d))?/ - const SPOTIFY_IFRAME_ALLOW = - 'autoplay; clipboard-write; encrypted-media; fullscreen; picture-in-picture' - - const { groups } = input.match(SPOTIFY_URL_REGEX) || {} - - if (groups?.type && groups?.id) { - const theme = groups.theme ? `&theme=${groups.theme}` : '' - - return { - type: GRID_WIDGET_TYPE.SPOTIFY, - properties: { - src: `https://open.spotify.com/embed/${groups.type}/${groups.id}?utm_source=generator${theme}`, - type: groups.type, - allow: SPOTIFY_IFRAME_ALLOW, - theme: groups.theme, + ], + }, + [GRID_WIDGET_TYPE.INSTAGRAM]: { + type: GRID_WIDGET_TYPE.INSTAGRAM, + embedRegex: + /https:\/\/www\.instagram\.com\/(p|reel|profile|tv)\/([\w-]+)\/(\?[^"]*)?/, + }, + [GRID_WIDGET_TYPE.WARPCAST]: undefined, + [GRID_WIDGET_TYPE.SPOTIFY]: { + type: GRID_WIDGET_TYPE.IFRAME, + embedRegex: + /https?:\/\/(?:open\.)?spotify\.com\/embed\/?(?track|playlist|artist)\/(?[^?]+)(?:\?utm_source=(?:generator|oembed))?(?:&theme=(?\d))?/, + secondaryRegexesWithCallbacks: [ + { + regex: + /https:\/\/open\.spotify\.com\/(?track|playlist|artist)\/(?[^?]+)/, + callback: getSpotifyOEmbed, }, - } - } - - throw new Error('Invalid Spotify input') + ], + constantProperties: { + allow: + 'autoplay; clipboard-write; encrypted-media; fullscreen; picture-in-picture', + }, + }, + [GRID_WIDGET_TYPE.SOUNDCLOUD]: { + type: GRID_WIDGET_TYPE.IFRAME, + embedRegex: + /https?:\/\/w\.soundcloud\.com\/player\/\?(?:(?!url=https).)*url=https(?::|%3A)(?:\/|%2F){2}api\.soundcloud\.com(?:\/|%2F)(?tracks|playlists|users)(?:\/|%2F)\d+(?:[^"]*)?/, + secondaryRegexesWithCallbacks: [ + { + regex: + /https:\/\/soundcloud\.com\/([a-zA-Z0-9_-]+)(?:\/(sets\/[a-zA-Z0-9_-]+|[a-zA-Z0-9_-]+))?\/?/, + callback: getSoundCloudOEmbed, + }, + ], + constantProperties: { + allow: + 'autoplay; clipboard-write; encrypted-media; fullscreen; picture-in-picture', + }, + }, + [GRID_WIDGET_TYPE.YOUTUBE]: { + type: GRID_WIDGET_TYPE.IFRAME, + embedRegex: /(?:https?:\/\/)?(?:www\.)?youtube\.com\/watch\?v=([^&]+)/, + }, + [GRID_WIDGET_TYPE.ADD_CONTENT]: undefined, } -const parseSoundCloudWidgetInput = async ( +export const parsePlatformInput = async ( + platform: GridWidgetType, input: string -): Promise => { - const SOUNDCLOUD_SHARE_URL_REGEX = - /https:\/\/soundcloud\.com\/([a-zA-Z0-9_-]+)(?:\/(sets\/[a-zA-Z0-9_-]+|[a-zA-Z0-9_-]+))?\/?/ +): Promise => { + const platformParsingParameters = PLATFORM_PARSING_PARAMETERS[platform] + if (!platformParsingParameters) throw new Error('Invalid platform') - // Check if the input already contains an embed URL + // Check if the input matches the embed regex try { - return parseSoundCloudWidgetInputFromEmbed(input) + return parsePlatformEmbed(input, platformParsingParameters) } catch {} - // Check if the input is a SoundCloud share URL - const [shareUrlMatch] = input.match(SOUNDCLOUD_SHARE_URL_REGEX) || [] - if (shareUrlMatch) { - const soundCloudEmbed = await getSoundCloudEmbedUrl(shareUrlMatch) + const { secondaryRegexesWithCallbacks } = platformParsingParameters + if (!secondaryRegexesWithCallbacks) throw new Error('Invalid input') + + // Check if the input matches a secondary regex + let callbackResult: string | undefined + for (const { regex, callback } of secondaryRegexesWithCallbacks) { + const match = input.match(regex) - if (soundCloudEmbed) { - return parseSoundCloudWidgetInputFromEmbed(soundCloudEmbed) + if (match) { + callbackResult = await callback(match[0]) + + break } } - throw new Error('Invalid SoundCloud input') + if (!callbackResult) throw new Error('Invalid input') + + return parsePlatformEmbed(callbackResult, platformParsingParameters) } -const parseSoundCloudWidgetInputFromEmbed = ( - input: string +const parsePlatformEmbed = ( + input: string, + platformParsingParameters: PlatformParsingParameters ): LayoutItemExtended | never => { - const SOUNDCLOUD_EMBED_URL_REGEX = - /https?:\/\/w\.soundcloud\.com\/player\/\?(?:(?!url=https).)*url=https(?::|%3A)(?:\/|%2F){2}api\.soundcloud\.com(?:\/|%2F)(tracks|playlists|users)(?:\/|%2F)\d+(?:[^"]*)?/ - const SOUNDCLOUD_IFRAME_ALLOW = - 'autoplay; clipboard-write; encrypted-media; fullscreen; picture-in-picture' - const [match, type] = input.match(SOUNDCLOUD_EMBED_URL_REGEX) || [] - - if (match) { - return { - type: GRID_WIDGET_TYPE.IFRAME, - properties: { - src: match, - allow: SOUNDCLOUD_IFRAME_ALLOW, - type, + const { type, embedRegex, constantProperties } = platformParsingParameters + const match = input.match(embedRegex) + + if (!match) throw new Error('Invalid input') + + const { groups } = match + let extractedProperties: Record = {} + if (groups) { + // Extract the properties from capture groups from the regex match + extractedProperties = Object.entries(groups).reduce( + (acc: Record, [key, value]) => { + if (value) acc[key] = value + + return acc }, - } + {} + ) } - throw new Error('Invalid SoundCloud input') + return { + type, + properties: { + src: match[0], + ...constantProperties, + ...extractedProperties, + }, + } } -const getSoundCloudEmbedUrl = async ( - url: string -): Promise => { +// const parseYoutubeWidgetInput = (input: string): LayoutItemExtended | never => { +// const YOUTUBE_URL_REGEX = +// /(?:https?:\/\/)?(?:www\.)?youtube\.com\/watch\?v=([^&]+)/ +// const YOUTUBE_EMBED_REGEX = +// /(?:https?:\/\/)?(?:www\.)?youtube\.com\/embed\/([^?]+)/ +// const YOUTUBE_IFRAME_ALLOW = +// 'accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share' + +// const youtubeUrlMatch = input.match(YOUTUBE_URL_REGEX) + +// if (youtubeUrlMatch) { +// return { +// type: GRID_WIDGET_TYPE.IFRAME, +// properties: { +// src: `https://www.youtube.com/embed/${youtubeUrlMatch[1]}`, +// allow: YOUTUBE_IFRAME_ALLOW, +// }, +// } +// } + +// const youtubeEmbedMatch = input.match(YOUTUBE_EMBED_REGEX) + +// if (youtubeEmbedMatch) { +// return { +// type: GRID_WIDGET_TYPE.IFRAME, +// properties: { +// src: `https://www.youtube.com/embed/${youtubeEmbedMatch[1]}`, +// allow: YOUTUBE_IFRAME_ALLOW, +// }, +// } +// } + +// throw new Error('Invalid YouTube input') +// } + +async function getSoundCloudOEmbed(url: string): Promise { const encodedUrl = encodeURI(url) const response = await fetch( `https://soundcloud.com/oembed?url=${encodedUrl}&format=json` ) - if (!response.ok) { - return response.statusText - } + return response.ok ? ((await response.json())?.html as string) : undefined +} - const { html } = await response.json() - const [, srcMatch] = html.match(/src="([^"]+)"/) || [] +async function getSpotifyOEmbed(url: string): Promise { + const response = await fetch( + `https://open.spotify.com/oembed?url=${url}&format=json` + ) - return srcMatch + return response.ok ? ((await response.json())?.html as string) : undefined } -const parseInstagramWidgetInput = ( - input: string -): LayoutItemExtended | never => { - const INSTAGRAM_URL_REGEX = - /https:\/\/www\.instagram\.com\/(p|reel|profile|tv)\/([\w-]+)\/(\?[^"]*)?/ +async function getXOEmbedFromHandle(handle: string) { + handle = handle.replace('@', '') + const response = await fetch( + `https://publish.twitter.com/oembed?url=https://twitter.com/${handle}` + ) - const [, type, id, params] = input.match(INSTAGRAM_URL_REGEX) || [] + return response.ok ? ((await response.json())?.html as string) : undefined +} - if (id && type) { - return { - type: GRID_WIDGET_TYPE.INSTAGRAM, - properties: { - src: `https://www.instagram.com/${type}/${id}/${params}`, - type: type, - }, - } +function sanitizeXEmbedUrl(url: string): string { + url = url.replace('x.com', 'twitter.com') + + if (!url.startsWith('https://')) { + url = `https://${url}` } - throw new Error('Invalid Instagram input') + return url } + +// const parseInstagramWidgetInput = ( +// input: string +// ): LayoutItemExtended | never => { +// const INSTAGRAM_URL_REGEX = +// /https:\/\/www\.instagram\.com\/(p|reel|profile|tv)\/([\w-]+)\/(\?[^"]*)?/ + +// const [, type, id, params] = input.match(INSTAGRAM_URL_REGEX) || [] + +// if (id && type) { +// return { +// type: GRID_WIDGET_TYPE.INSTAGRAM, +// properties: { +// src: `https://www.instagram.com/${type}/${id}/${params}`, +// type: type, +// }, +// } +// } + +// throw new Error('Invalid Instagram input') +// } From bab74f0b2a0116c3c433276112e82b09a0f7a9b1 Mon Sep 17 00:00:00 2001 From: Eduardo Sacco <3680995+nastita@users.noreply.github.com> Date: Mon, 21 Oct 2024 13:21:55 -0300 Subject: [PATCH 02/36] Simplify parser result --- .../grid/utils/__tests__/gridParser.spec.ts | 223 ++++++------------ domains/grid/utils/gridParser.ts | 15 +- 2 files changed, 75 insertions(+), 163 deletions(-) diff --git a/domains/grid/utils/__tests__/gridParser.spec.ts b/domains/grid/utils/__tests__/gridParser.spec.ts index e0fdf2ad..656a86b8 100644 --- a/domains/grid/utils/__tests__/gridParser.spec.ts +++ b/domains/grid/utils/__tests__/gridParser.spec.ts @@ -15,11 +15,8 @@ describe('parsePlatformInput', () => { GRID_WIDGET_TYPE.X, '@feindura', { - type: GRID_WIDGET_TYPE.X, - properties: { - src: 'https://twitter.com/feindura?ref_src=twsrc%5Etfw', - handle: 'feindura', - }, + src: 'https://twitter.com/feindura?ref_src=twsrc%5Etfw', + handle: 'feindura', }, ], [ @@ -27,12 +24,9 @@ describe('parsePlatformInput', () => { GRID_WIDGET_TYPE.X, 'https://x.com/feindura/status/1804519711377436675', { - type: GRID_WIDGET_TYPE.X, - properties: { - src: 'https://twitter.com/feindura/status/1804519711377436675', - handle: 'feindura', - id: '1804519711377436675', - }, + src: 'https://twitter.com/feindura/status/1804519711377436675', + handle: 'feindura', + id: '1804519711377436675', }, ], [ @@ -40,12 +34,9 @@ describe('parsePlatformInput', () => { GRID_WIDGET_TYPE.X, 'https://twitter.com/feindura/status/1804519711377436675', { - type: GRID_WIDGET_TYPE.X, - properties: { - src: 'https://twitter.com/feindura/status/1804519711377436675', - handle: 'feindura', - id: '1804519711377436675', - }, + src: 'https://twitter.com/feindura/status/1804519711377436675', + handle: 'feindura', + id: '1804519711377436675', }, ], [ @@ -53,12 +44,9 @@ describe('parsePlatformInput', () => { GRID_WIDGET_TYPE.X, ' ', { - type: GRID_WIDGET_TYPE.X, - properties: { - src: 'https://twitter.com/feindura/status/1804519711377436675?ref_src=twsrc%5Etfw', - handle: 'feindura', - id: '1804519711377436675', - }, + src: 'https://twitter.com/feindura/status/1804519711377436675?ref_src=twsrc%5Etfw', + handle: 'feindura', + id: '1804519711377436675', }, ], [ @@ -66,11 +54,8 @@ describe('parsePlatformInput', () => { GRID_WIDGET_TYPE.X, 'https://x.com/lukso_io', { - type: GRID_WIDGET_TYPE.X, - properties: { - src: 'https://twitter.com/lukso_io', - handle: 'lukso_io', - }, + src: 'https://twitter.com/lukso_io', + handle: 'lukso_io', }, ], [ @@ -78,11 +63,8 @@ describe('parsePlatformInput', () => { GRID_WIDGET_TYPE.X, 'https://twitter.com/lukso_io', { - type: GRID_WIDGET_TYPE.X, - properties: { - src: 'https://twitter.com/lukso_io', - handle: 'lukso_io', - }, + src: 'https://twitter.com/lukso_io', + handle: 'lukso_io', }, ], [ @@ -90,11 +72,8 @@ describe('parsePlatformInput', () => { GRID_WIDGET_TYPE.X, ' ', { - type: GRID_WIDGET_TYPE.X, - properties: { - src: 'https://twitter.com/lukso_io?ref_src=twsrc%5Etfw', - handle: 'lukso_io', - }, + src: 'https://twitter.com/lukso_io?ref_src=twsrc%5Etfw', + handle: 'lukso_io', }, ], ])( @@ -113,11 +92,8 @@ describe('parsePlatformInput', () => { GRID_WIDGET_TYPE.YOUTUBE, 'https://www.youtube.com/watch?v=Vw4JE64hsO8', { - type: GRID_WIDGET_TYPE.IFRAME, - properties: { - src: 'https://www.youtube.com/embed/Vw4JE64hsO8', - allow: YOUTUBE_IFRAME_ALLOW, - }, + src: 'https://www.youtube.com/embed/Vw4JE64hsO8', + allow: YOUTUBE_IFRAME_ALLOW, }, ], [ @@ -125,11 +101,8 @@ describe('parsePlatformInput', () => { GRID_WIDGET_TYPE.YOUTUBE, '', { - type: GRID_WIDGET_TYPE.IFRAME, - properties: { - src: 'https://www.youtube.com/embed/Vw4JE64hsO8', - allow: YOUTUBE_IFRAME_ALLOW, - }, + src: 'https://www.youtube.com/embed/Vw4JE64hsO8', + allow: YOUTUBE_IFRAME_ALLOW, }, ], ])( @@ -148,13 +121,9 @@ describe('parsePlatformInput', () => { GRID_WIDGET_TYPE.SPOTIFY, 'https://open.spotify.com/track/7xGfFoTpQ2E7fRF5lN10tr', { - type: GRID_WIDGET_TYPE.IFRAME, - properties: { - src: 'https://open.spotify.com/embed/track/7xGfFoTpQ2E7fRF5lN10tr?utm_source=oembed', - allow: SPOTIFY_IFRAME_ALLOW, - type: 'track', - id: '7xGfFoTpQ2E7fRF5lN10tr', - }, + src: 'https://open.spotify.com/embed/track/7xGfFoTpQ2E7fRF5lN10tr?utm_source=oembed', + allow: SPOTIFY_IFRAME_ALLOW, + id: '7xGfFoTpQ2E7fRF5lN10tr', }, ], [ @@ -162,13 +131,9 @@ describe('parsePlatformInput', () => { GRID_WIDGET_TYPE.SPOTIFY, '', { - type: GRID_WIDGET_TYPE.IFRAME, - properties: { - src: 'https://open.spotify.com/embed/track/2BHj31ufdEqVK5CkYDp9mA?utm_source=generator', - allow: SPOTIFY_IFRAME_ALLOW, - type: 'track', - id: '2BHj31ufdEqVK5CkYDp9mA', - }, + src: 'https://open.spotify.com/embed/track/2BHj31ufdEqVK5CkYDp9mA?utm_source=generator', + allow: SPOTIFY_IFRAME_ALLOW, + id: '2BHj31ufdEqVK5CkYDp9mA', }, ], [ @@ -176,14 +141,10 @@ describe('parsePlatformInput', () => { GRID_WIDGET_TYPE.SPOTIFY, '', { - type: GRID_WIDGET_TYPE.IFRAME, - properties: { - src: 'https://open.spotify.com/embed/track/48K735Rd3UQExzjXH004k1?utm_source=generator&theme=0', - allow: SPOTIFY_IFRAME_ALLOW, - type: 'track', - id: '48K735Rd3UQExzjXH004k1', - theme: '0', - }, + src: 'https://open.spotify.com/embed/track/48K735Rd3UQExzjXH004k1?utm_source=generator&theme=0', + allow: SPOTIFY_IFRAME_ALLOW, + id: '48K735Rd3UQExzjXH004k1', + theme: '0', }, ], [ @@ -191,13 +152,9 @@ describe('parsePlatformInput', () => { GRID_WIDGET_TYPE.SPOTIFY, 'https://open.spotify.com/playlist/7KFoK4LJ23EncELJwYmTDG', { - type: GRID_WIDGET_TYPE.IFRAME, - properties: { - src: 'https://open.spotify.com/embed/playlist/7KFoK4LJ23EncELJwYmTDG?utm_source=oembed', - allow: SPOTIFY_IFRAME_ALLOW, - type: 'playlist', - id: '7KFoK4LJ23EncELJwYmTDG', - }, + src: 'https://open.spotify.com/embed/playlist/7KFoK4LJ23EncELJwYmTDG?utm_source=oembed', + allow: SPOTIFY_IFRAME_ALLOW, + id: '7KFoK4LJ23EncELJwYmTDG', }, ], [ @@ -205,13 +162,9 @@ describe('parsePlatformInput', () => { GRID_WIDGET_TYPE.SPOTIFY, '', { - type: GRID_WIDGET_TYPE.IFRAME, - properties: { - src: 'https://open.spotify.com/embed/playlist/7KFoK4LJ23EncELJwYmTDG?utm_source=generator', - allow: SPOTIFY_IFRAME_ALLOW, - type: 'playlist', - id: '7KFoK4LJ23EncELJwYmTDG', - }, + src: 'https://open.spotify.com/embed/playlist/7KFoK4LJ23EncELJwYmTDG?utm_source=generator', + allow: SPOTIFY_IFRAME_ALLOW, + id: '7KFoK4LJ23EncELJwYmTDG', }, ], [ @@ -219,13 +172,9 @@ describe('parsePlatformInput', () => { GRID_WIDGET_TYPE.SPOTIFY, 'https://open.spotify.com/artist/4KY9rCrokaoFzvMfX98u1q', { - type: GRID_WIDGET_TYPE.IFRAME, - properties: { - src: 'https://open.spotify.com/embed/artist/4KY9rCrokaoFzvMfX98u1q?utm_source=oembed', - allow: SPOTIFY_IFRAME_ALLOW, - type: 'artist', - id: '4KY9rCrokaoFzvMfX98u1q', - }, + src: 'https://open.spotify.com/embed/artist/4KY9rCrokaoFzvMfX98u1q?utm_source=oembed', + allow: SPOTIFY_IFRAME_ALLOW, + id: '4KY9rCrokaoFzvMfX98u1q', }, ], [ @@ -233,13 +182,9 @@ describe('parsePlatformInput', () => { GRID_WIDGET_TYPE.SPOTIFY, '', { - type: GRID_WIDGET_TYPE.IFRAME, - properties: { - src: 'https://open.spotify.com/embed/artist/4KY9rCrokaoFzvMfX98u1q?utm_source=generator', - allow: SPOTIFY_IFRAME_ALLOW, - type: 'artist', - id: '4KY9rCrokaoFzvMfX98u1q', - }, + src: 'https://open.spotify.com/embed/artist/4KY9rCrokaoFzvMfX98u1q?utm_source=generator', + allow: SPOTIFY_IFRAME_ALLOW, + id: '4KY9rCrokaoFzvMfX98u1q', }, ], ])( @@ -258,12 +203,9 @@ describe('parsePlatformInput', () => { GRID_WIDGET_TYPE.SOUNDCLOUD, 'https://soundcloud.com/occams-laser/with-you', { - type: GRID_WIDGET_TYPE.IFRAME, - properties: { - src: 'https://w.soundcloud.com/player/?visual=true&url=https%3A%2F%2Fapi.soundcloud.com%2Ftracks%2F1856391039&show_artwork=true', - allow: SOUNDCLOUD_IFRAME_ALLOW, - type: 'tracks', - }, + src: 'https://w.soundcloud.com/player/?visual=true&url=https%3A%2F%2Fapi.soundcloud.com%2Ftracks%2F1856391039&show_artwork=true', + allow: SOUNDCLOUD_IFRAME_ALLOW, + type: 'tracks', }, ], [ @@ -271,12 +213,9 @@ describe('parsePlatformInput', () => { GRID_WIDGET_TYPE.SOUNDCLOUD, 'https://w.soundcloud.com/player/?visual=true&url=https%3A%2F%2Fapi.soundcloud.com%2Fplaylists%2F505007376&show_artwork=true', { - type: GRID_WIDGET_TYPE.IFRAME, - properties: { - src: 'https://w.soundcloud.com/player/?visual=true&url=https%3A%2F%2Fapi.soundcloud.com%2Fplaylists%2F505007376&show_artwork=true', - allow: SPOTIFY_IFRAME_ALLOW, - type: 'playlists', - }, + src: 'https://w.soundcloud.com/player/?visual=true&url=https%3A%2F%2Fapi.soundcloud.com%2Fplaylists%2F505007376&show_artwork=true', + allow: SPOTIFY_IFRAME_ALLOW, + type: 'playlists', }, ], [ @@ -284,12 +223,9 @@ describe('parsePlatformInput', () => { GRID_WIDGET_TYPE.SOUNDCLOUD, 'https://soundcloud.com/fabian-vogelsteller', { - type: GRID_WIDGET_TYPE.IFRAME, - properties: { - src: 'https://w.soundcloud.com/player/?visual=true&url=https%3A%2F%2Fapi.soundcloud.com%2Fusers%2F227118126&show_artwork=true', - allow: SOUNDCLOUD_IFRAME_ALLOW, - type: 'users', - }, + src: 'https://w.soundcloud.com/player/?visual=true&url=https%3A%2F%2Fapi.soundcloud.com%2Fusers%2F227118126&show_artwork=true', + allow: SOUNDCLOUD_IFRAME_ALLOW, + type: 'users', }, ], [ @@ -297,12 +233,9 @@ describe('parsePlatformInput', () => { GRID_WIDGET_TYPE.SOUNDCLOUD, '', { - type: GRID_WIDGET_TYPE.IFRAME, - properties: { - src: 'https://w.soundcloud.com/player/?url=https://api.soundcloud.com/tracks/1856391039&color=%23ff5500&auto_play=false&hide_related=false&show_comments=true&show_user=true&show_reposts=false&show_teaser=true&visual=true', - allow: SOUNDCLOUD_IFRAME_ALLOW, - type: 'tracks', - }, + src: 'https://w.soundcloud.com/player/?url=https://api.soundcloud.com/tracks/1856391039&color=%23ff5500&auto_play=false&hide_related=false&show_comments=true&show_user=true&show_reposts=false&show_teaser=true&visual=true', + allow: SOUNDCLOUD_IFRAME_ALLOW, + type: 'tracks', }, ], [ @@ -310,12 +243,9 @@ describe('parsePlatformInput', () => { GRID_WIDGET_TYPE.SOUNDCLOUD, '', { - type: GRID_WIDGET_TYPE.IFRAME, - properties: { - src: 'https://w.soundcloud.com/player/?visual=true&url=https%3A%2F%2Fapi.soundcloud.com%2Ftracks%2F1856391039&show_artwork=true', - allow: SOUNDCLOUD_IFRAME_ALLOW, - type: 'tracks', - }, + src: 'https://w.soundcloud.com/player/?visual=true&url=https%3A%2F%2Fapi.soundcloud.com%2Ftracks%2F1856391039&show_artwork=true', + allow: SOUNDCLOUD_IFRAME_ALLOW, + type: 'tracks', }, ], [ @@ -323,12 +253,9 @@ describe('parsePlatformInput', () => { GRID_WIDGET_TYPE.SOUNDCLOUD, '', { - type: GRID_WIDGET_TYPE.IFRAME, - properties: { - src: 'https://w.soundcloud.com/player/?url=https://api.soundcloud.com/playlists/1850298147&color=%23ff5500&auto_play=false&hide_related=false&show_comments=true&show_user=true&show_reposts=false&show_teaser=true&visual=true', - allow: SOUNDCLOUD_IFRAME_ALLOW, - type: 'playlists', - }, + src: 'https://w.soundcloud.com/player/?url=https://api.soundcloud.com/playlists/1850298147&color=%23ff5500&auto_play=false&hide_related=false&show_comments=true&show_user=true&show_reposts=false&show_teaser=true&visual=true', + allow: SOUNDCLOUD_IFRAME_ALLOW, + type: 'playlists', }, ], ])( @@ -347,11 +274,8 @@ describe('parsePlatformInput', () => { GRID_WIDGET_TYPE.INSTAGRAM, 'https://www.instagram.com/p/C98OXs6yhAq/?utm_source=ig_embed&utm_campaign=loading', { - type: GRID_WIDGET_TYPE.INSTAGRAM, - properties: { - src: 'https://www.instagram.com/p/C98OXs6yhAq/?utm_source=ig_embed&utm_campaign=loading', - type: 'p', - }, + src: 'https://www.instagram.com/p/C98OXs6yhAq/?utm_source=ig_embed&utm_campaign=loading', + type: 'p', }, ], [ @@ -359,11 +283,8 @@ describe('parsePlatformInput', () => { GRID_WIDGET_TYPE.INSTAGRAM, '
', { - type: GRID_WIDGET_TYPE.INSTAGRAM, - properties: { - src: 'https://www.instagram.com/p/C98OXs6yhAq/?utm_source=ig_embed&utm_campaign=loading', - type: 'p', - }, + src: 'https://www.instagram.com/p/C98OXs6yhAq/?utm_source=ig_embed&utm_campaign=loading', + type: 'p', }, ], [ @@ -371,11 +292,8 @@ describe('parsePlatformInput', () => { GRID_WIDGET_TYPE.INSTAGRAM, 'https://www.instagram.com/reel/DAlOgHkuyxd/?utm_source=ig_embed&utm_campaign=loading', { - type: GRID_WIDGET_TYPE.INSTAGRAM, - properties: { - src: 'https://www.instagram.com/reel/DAlOgHkuyxd/?utm_source=ig_embed&utm_campaign=loading', - type: 'reel', - }, + src: 'https://www.instagram.com/reel/DAlOgHkuyxd/?utm_source=ig_embed&utm_campaign=loading', + type: 'reel', }, ], [ @@ -383,11 +301,8 @@ describe('parsePlatformInput', () => { GRID_WIDGET_TYPE.INSTAGRAM, '
', { - type: GRID_WIDGET_TYPE.INSTAGRAM, - properties: { - src: 'https://www.instagram.com/reel/DAlOgHkuyxd/?utm_source=ig_embed&utm_campaign=loading', - type: 'reel', - }, + src: 'https://www.instagram.com/reel/DAlOgHkuyxd/?utm_source=ig_embed&utm_campaign=loading', + type: 'reel', }, ], ])( diff --git a/domains/grid/utils/gridParser.ts b/domains/grid/utils/gridParser.ts index c443150a..3db63ece 100644 --- a/domains/grid/utils/gridParser.ts +++ b/domains/grid/utils/gridParser.ts @@ -84,7 +84,7 @@ const PLATFORM_PARSING_PARAMETERS: Record< export const parsePlatformInput = async ( platform: GridWidgetType, input: string -): Promise => { +): Promise => { const platformParsingParameters = PLATFORM_PARSING_PARAMETERS[platform] if (!platformParsingParameters) throw new Error('Invalid platform') @@ -116,8 +116,8 @@ export const parsePlatformInput = async ( const parsePlatformEmbed = ( input: string, platformParsingParameters: PlatformParsingParameters -): LayoutItemExtended | never => { - const { type, embedRegex, constantProperties } = platformParsingParameters +): GridWidgetProperties | never => { + const { embedRegex, constantProperties } = platformParsingParameters const match = input.match(embedRegex) if (!match) throw new Error('Invalid input') @@ -137,12 +137,9 @@ const parsePlatformEmbed = ( } return { - type, - properties: { - src: match[0], - ...constantProperties, - ...extractedProperties, - }, + src: match[0], + ...constantProperties, + ...extractedProperties, } } From 776467d5986e6d1076bb3810bc535ed807f3741d Mon Sep 17 00:00:00 2001 From: Eduardo Sacco <3680995+nastita@users.noreply.github.com> Date: Mon, 21 Oct 2024 13:33:50 -0300 Subject: [PATCH 03/36] update yt parsing params --- domains/grid/utils/gridParser.ts | 59 ++++++++++++-------------------- 1 file changed, 22 insertions(+), 37 deletions(-) diff --git a/domains/grid/utils/gridParser.ts b/domains/grid/utils/gridParser.ts index 3db63ece..6ec270e7 100644 --- a/domains/grid/utils/gridParser.ts +++ b/domains/grid/utils/gridParser.ts @@ -75,8 +75,18 @@ const PLATFORM_PARSING_PARAMETERS: Record< }, }, [GRID_WIDGET_TYPE.YOUTUBE]: { - type: GRID_WIDGET_TYPE.IFRAME, - embedRegex: /(?:https?:\/\/)?(?:www\.)?youtube\.com\/watch\?v=([^&]+)/, + type: GRID_WIDGET_TYPE.YOUTUBE, + embedRegex: /(?:https?:\/\/)?(?:www\.)?youtube\.com\/embed\/([^?]+)/, + secondaryRegexesWithCallbacks: [ + { + regex: /(?:https?:\/\/)?(?:www\.)?youtube\.com\/watch\?v=([^&]+)/, + callback: async url => sanitizeYoutubeEmbedUrl(url), + }, + ], + constantProperties: { + allow: + 'accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share', + }, }, [GRID_WIDGET_TYPE.ADD_CONTENT]: undefined, } @@ -143,41 +153,6 @@ const parsePlatformEmbed = ( } } -// const parseYoutubeWidgetInput = (input: string): LayoutItemExtended | never => { -// const YOUTUBE_URL_REGEX = -// /(?:https?:\/\/)?(?:www\.)?youtube\.com\/watch\?v=([^&]+)/ -// const YOUTUBE_EMBED_REGEX = -// /(?:https?:\/\/)?(?:www\.)?youtube\.com\/embed\/([^?]+)/ -// const YOUTUBE_IFRAME_ALLOW = -// 'accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share' - -// const youtubeUrlMatch = input.match(YOUTUBE_URL_REGEX) - -// if (youtubeUrlMatch) { -// return { -// type: GRID_WIDGET_TYPE.IFRAME, -// properties: { -// src: `https://www.youtube.com/embed/${youtubeUrlMatch[1]}`, -// allow: YOUTUBE_IFRAME_ALLOW, -// }, -// } -// } - -// const youtubeEmbedMatch = input.match(YOUTUBE_EMBED_REGEX) - -// if (youtubeEmbedMatch) { -// return { -// type: GRID_WIDGET_TYPE.IFRAME, -// properties: { -// src: `https://www.youtube.com/embed/${youtubeEmbedMatch[1]}`, -// allow: YOUTUBE_IFRAME_ALLOW, -// }, -// } -// } - -// throw new Error('Invalid YouTube input') -// } - async function getSoundCloudOEmbed(url: string): Promise { const encodedUrl = encodeURI(url) const response = await fetch( @@ -214,6 +189,16 @@ function sanitizeXEmbedUrl(url: string): string { return url } +function sanitizeYoutubeEmbedUrl(url: string): string { + url = url.replace('watch?v=', 'embed/') + + if (!url.startsWith('https://')) { + url = `https://${url}` + } + + return url +} + // const parseInstagramWidgetInput = ( // input: string // ): LayoutItemExtended | never => { From c4793da8143eff3b7bb26c9ed19c46050d4a9674 Mon Sep 17 00:00:00 2001 From: Eduardo Sacco <3680995+nastita@users.noreply.github.com> Date: Mon, 21 Oct 2024 14:19:17 -0300 Subject: [PATCH 04/36] Update IG parsing parameters --- .../grid/utils/__tests__/gridParser.spec.ts | 8 +++++++ domains/grid/utils/gridParser.ts | 23 +------------------ 2 files changed, 9 insertions(+), 22 deletions(-) diff --git a/domains/grid/utils/__tests__/gridParser.spec.ts b/domains/grid/utils/__tests__/gridParser.spec.ts index 656a86b8..aadb0844 100644 --- a/domains/grid/utils/__tests__/gridParser.spec.ts +++ b/domains/grid/utils/__tests__/gridParser.spec.ts @@ -276,6 +276,8 @@ describe('parsePlatformInput', () => { { src: 'https://www.instagram.com/p/C98OXs6yhAq/?utm_source=ig_embed&utm_campaign=loading', type: 'p', + id: 'C98OXs6yhAq', + params: '?utm_source=ig_embed&utm_campaign=loading', }, ], [ @@ -285,6 +287,8 @@ describe('parsePlatformInput', () => { { src: 'https://www.instagram.com/p/C98OXs6yhAq/?utm_source=ig_embed&utm_campaign=loading', type: 'p', + id: 'C98OXs6yhAq', + params: '?utm_source=ig_embed&utm_campaign=loading', }, ], [ @@ -294,6 +298,8 @@ describe('parsePlatformInput', () => { { src: 'https://www.instagram.com/reel/DAlOgHkuyxd/?utm_source=ig_embed&utm_campaign=loading', type: 'reel', + id: 'DAlOgHkuyxd', + params: '?utm_source=ig_embed&utm_campaign=loading', }, ], [ @@ -303,6 +309,8 @@ describe('parsePlatformInput', () => { { src: 'https://www.instagram.com/reel/DAlOgHkuyxd/?utm_source=ig_embed&utm_campaign=loading', type: 'reel', + id: 'DAlOgHkuyxd', + params: '?utm_source=ig_embed&utm_campaign=loading', }, ], ])( diff --git a/domains/grid/utils/gridParser.ts b/domains/grid/utils/gridParser.ts index 6ec270e7..d5f68751 100644 --- a/domains/grid/utils/gridParser.ts +++ b/domains/grid/utils/gridParser.ts @@ -39,7 +39,7 @@ const PLATFORM_PARSING_PARAMETERS: Record< [GRID_WIDGET_TYPE.INSTAGRAM]: { type: GRID_WIDGET_TYPE.INSTAGRAM, embedRegex: - /https:\/\/www\.instagram\.com\/(p|reel|profile|tv)\/([\w-]+)\/(\?[^"]*)?/, + /https:\/\/www\.instagram\.com\/(?p|reel|profile|tv)\/(?[\w-]+)\/(?\?[^"]*)?/, }, [GRID_WIDGET_TYPE.WARPCAST]: undefined, [GRID_WIDGET_TYPE.SPOTIFY]: { @@ -198,24 +198,3 @@ function sanitizeYoutubeEmbedUrl(url: string): string { return url } - -// const parseInstagramWidgetInput = ( -// input: string -// ): LayoutItemExtended | never => { -// const INSTAGRAM_URL_REGEX = -// /https:\/\/www\.instagram\.com\/(p|reel|profile|tv)\/([\w-]+)\/(\?[^"]*)?/ - -// const [, type, id, params] = input.match(INSTAGRAM_URL_REGEX) || [] - -// if (id && type) { -// return { -// type: GRID_WIDGET_TYPE.INSTAGRAM, -// properties: { -// src: `https://www.instagram.com/${type}/${id}/${params}`, -// type: type, -// }, -// } -// } - -// throw new Error('Invalid Instagram input') -// } From 8d89fc5c898bcf2034199f08eeaba8f96a4bf60a Mon Sep 17 00:00:00 2001 From: Eduardo Sacco <3680995+nastita@users.noreply.github.com> Date: Mon, 21 Oct 2024 16:31:32 -0300 Subject: [PATCH 05/36] Fix parsePlatformInput calls after updates --- domains/grid/components/AddWidgetGenericPlatform.vue | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/domains/grid/components/AddWidgetGenericPlatform.vue b/domains/grid/components/AddWidgetGenericPlatform.vue index 674e0515..23970a83 100644 --- a/domains/grid/components/AddWidgetGenericPlatform.vue +++ b/domains/grid/components/AddWidgetGenericPlatform.vue @@ -25,10 +25,7 @@ const handleSave = async () => { } try { - const { properties } = await parsePlatformInput( - props.type, - inputValue.value - ) + const properties = await parsePlatformInput(props.type, inputValue.value) if (isEdit.value) { updateGridLayoutItem(props.id, { @@ -71,7 +68,7 @@ const handleInput = async (customEvent: CustomEvent) => { // validation try { - const { properties } = await parsePlatformInput(props.type, input.value) + const properties = await parsePlatformInput(props.type, input.value) inputValue.value = properties.src } catch (error) { console.warn(error) From 34933f6cc7838b7fbada27f27e0b1b4df4eba36a Mon Sep 17 00:00:00 2001 From: Eduardo Sacco <3680995+nastita@users.noreply.github.com> Date: Mon, 21 Oct 2024 16:34:22 -0300 Subject: [PATCH 06/36] Update spotify test cases --- .../grid/utils/__tests__/gridParser.spec.ts | 21 ++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/domains/grid/utils/__tests__/gridParser.spec.ts b/domains/grid/utils/__tests__/gridParser.spec.ts index aadb0844..8452534b 100644 --- a/domains/grid/utils/__tests__/gridParser.spec.ts +++ b/domains/grid/utils/__tests__/gridParser.spec.ts @@ -122,8 +122,9 @@ describe('parsePlatformInput', () => { 'https://open.spotify.com/track/7xGfFoTpQ2E7fRF5lN10tr', { src: 'https://open.spotify.com/embed/track/7xGfFoTpQ2E7fRF5lN10tr?utm_source=oembed', - allow: SPOTIFY_IFRAME_ALLOW, + type: 'track', id: '7xGfFoTpQ2E7fRF5lN10tr', + allow: SPOTIFY_IFRAME_ALLOW, }, ], [ @@ -132,8 +133,9 @@ describe('parsePlatformInput', () => { '', { src: 'https://open.spotify.com/embed/track/2BHj31ufdEqVK5CkYDp9mA?utm_source=generator', - allow: SPOTIFY_IFRAME_ALLOW, + type: 'track', id: '2BHj31ufdEqVK5CkYDp9mA', + allow: SPOTIFY_IFRAME_ALLOW, }, ], [ @@ -142,8 +144,9 @@ describe('parsePlatformInput', () => { '', { src: 'https://open.spotify.com/embed/track/48K735Rd3UQExzjXH004k1?utm_source=generator&theme=0', - allow: SPOTIFY_IFRAME_ALLOW, + type: 'track', id: '48K735Rd3UQExzjXH004k1', + allow: SPOTIFY_IFRAME_ALLOW, theme: '0', }, ], @@ -153,8 +156,9 @@ describe('parsePlatformInput', () => { 'https://open.spotify.com/playlist/7KFoK4LJ23EncELJwYmTDG', { src: 'https://open.spotify.com/embed/playlist/7KFoK4LJ23EncELJwYmTDG?utm_source=oembed', - allow: SPOTIFY_IFRAME_ALLOW, + type: 'playlist', id: '7KFoK4LJ23EncELJwYmTDG', + allow: SPOTIFY_IFRAME_ALLOW, }, ], [ @@ -163,8 +167,9 @@ describe('parsePlatformInput', () => { '', { src: 'https://open.spotify.com/embed/playlist/7KFoK4LJ23EncELJwYmTDG?utm_source=generator', - allow: SPOTIFY_IFRAME_ALLOW, + type: 'playlist', id: '7KFoK4LJ23EncELJwYmTDG', + allow: SPOTIFY_IFRAME_ALLOW, }, ], [ @@ -173,8 +178,9 @@ describe('parsePlatformInput', () => { 'https://open.spotify.com/artist/4KY9rCrokaoFzvMfX98u1q', { src: 'https://open.spotify.com/embed/artist/4KY9rCrokaoFzvMfX98u1q?utm_source=oembed', - allow: SPOTIFY_IFRAME_ALLOW, + type: 'artist', id: '4KY9rCrokaoFzvMfX98u1q', + allow: SPOTIFY_IFRAME_ALLOW, }, ], [ @@ -183,8 +189,9 @@ describe('parsePlatformInput', () => { '', { src: 'https://open.spotify.com/embed/artist/4KY9rCrokaoFzvMfX98u1q?utm_source=generator', - allow: SPOTIFY_IFRAME_ALLOW, + type: 'artist', id: '4KY9rCrokaoFzvMfX98u1q', + allow: SPOTIFY_IFRAME_ALLOW, }, ], ])( From 8267b2830515068a5d7c3582bf94d45992c94798 Mon Sep 17 00:00:00 2001 From: Eduardo Sacco <3680995+nastita@users.noreply.github.com> Date: Mon, 21 Oct 2024 17:04:12 -0300 Subject: [PATCH 07/36] Fix lint issues --- domains/grid/utils/gridParser.ts | 19 ++++++++----------- 1 file changed, 8 insertions(+), 11 deletions(-) diff --git a/domains/grid/utils/gridParser.ts b/domains/grid/utils/gridParser.ts index d5f68751..8078fffc 100644 --- a/domains/grid/utils/gridParser.ts +++ b/domains/grid/utils/gridParser.ts @@ -171,30 +171,27 @@ async function getSpotifyOEmbed(url: string): Promise { } async function getXOEmbedFromHandle(handle: string) { - handle = handle.replace('@', '') const response = await fetch( - `https://publish.twitter.com/oembed?url=https://twitter.com/${handle}` + `https://publish.twitter.com/oembed?url=https://twitter.com/${handle.replace('@', '')}` ) return response.ok ? ((await response.json())?.html as string) : undefined } function sanitizeXEmbedUrl(url: string): string { - url = url.replace('x.com', 'twitter.com') - - if (!url.startsWith('https://')) { - url = `https://${url}` + let newUrl = url.replace('x.com', 'twitter.com') + if (!newUrl.startsWith('https://')) { + newUrl = `https://${newUrl}` } - return url + return newUrl } function sanitizeYoutubeEmbedUrl(url: string): string { - url = url.replace('watch?v=', 'embed/') - + let newUrl = url.replace('watch?v=', 'embed/') if (!url.startsWith('https://')) { - url = `https://${url}` + newUrl = `https://${newUrl}` } - return url + return newUrl } From 0d51b4a45c396a63ec63f708ad6416f19c73cec4 Mon Sep 17 00:00:00 2001 From: Dominik Zborowski Date: Thu, 31 Oct 2024 00:18:00 +0100 Subject: [PATCH 08/36] Remove autoplay from iframes --- domains/grid/shared/config.ts | 8 +++----- domains/grid/utils/__tests__/gridParser.spec.ts | 6 +++--- 2 files changed, 6 insertions(+), 8 deletions(-) diff --git a/domains/grid/shared/config.ts b/domains/grid/shared/config.ts index 79062289..388ef6d9 100644 --- a/domains/grid/shared/config.ts +++ b/domains/grid/shared/config.ts @@ -102,8 +102,7 @@ export const PLATFORM_PARSING_PARAMETERS: Partial< }, ], constantProperties: { - allow: - 'autoplay; clipboard-write; encrypted-media; fullscreen; picture-in-picture', + allow: 'clipboard-write; encrypted-media; fullscreen; picture-in-picture', }, }, [GRID_WIDGET_TYPE.enum.SOUNDCLOUD]: { @@ -118,8 +117,7 @@ export const PLATFORM_PARSING_PARAMETERS: Partial< }, ], constantProperties: { - allow: - 'autoplay; clipboard-write; encrypted-media; fullscreen; picture-in-picture', + allow: 'clipboard-write; encrypted-media; fullscreen; picture-in-picture', }, }, [GRID_WIDGET_TYPE.enum.YOUTUBE]: { @@ -133,7 +131,7 @@ export const PLATFORM_PARSING_PARAMETERS: Partial< ], constantProperties: { allow: - 'accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share', + 'accelerometer; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share', }, }, } diff --git a/domains/grid/utils/__tests__/gridParser.spec.ts b/domains/grid/utils/__tests__/gridParser.spec.ts index 588a7932..ba097314 100644 --- a/domains/grid/utils/__tests__/gridParser.spec.ts +++ b/domains/grid/utils/__tests__/gridParser.spec.ts @@ -1,11 +1,11 @@ import { describe, expect, it } from 'vitest' const YOUTUBE_IFRAME_ALLOW = - 'accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share' + 'accelerometer; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share' const SPOTIFY_IFRAME_ALLOW = - 'autoplay; clipboard-write; encrypted-media; fullscreen; picture-in-picture' + 'clipboard-write; encrypted-media; fullscreen; picture-in-picture' const SOUNDCLOUD_IFRAME_ALLOW = - 'autoplay; clipboard-write; encrypted-media; fullscreen; picture-in-picture' + 'clipboard-write; encrypted-media; fullscreen; picture-in-picture' describe('parsePlatformInput', () => { describe('X', () => { From 5f7257323a8535b7e6e2fbd84e3b2877805fc939 Mon Sep 17 00:00:00 2001 From: Dominik Zborowski Date: Thu, 31 Oct 2024 01:39:27 +0100 Subject: [PATCH 09/36] Add mode support in fetcher --- utils/fetcher.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/utils/fetcher.ts b/utils/fetcher.ts index a2f83c42..11bc26b2 100644 --- a/utils/fetcher.ts +++ b/utils/fetcher.ts @@ -2,7 +2,8 @@ * Generic url fetcher. * * @param string url - an url to call - * @param string address - a method used in the request, either 'GET' or 'POST' + * @param string method - a method used in the request, either 'GET' or 'POST' + * @param RequestMode mode - a mode of the request * @param Request data - a request data to be sent * @param Record headers - additional headers to be sent * @returns a result from calling the url @@ -11,6 +12,7 @@ export const fetcher = async (config: { url: string method: 'GET' | 'POST' + mode?: RequestMode data?: Request headers?: Record }): Promise => { @@ -21,6 +23,7 @@ export const fetcher = async (config: { ...(config.data ? { 'Content-Type': 'application/json' } : {}), }, redirect: 'follow', + mode: config.mode || 'cors', } if (config.data) { fetchConfig.body = JSON.stringify(config.data) From a6d157084db21eb7c0958d648754d19a2a680f84 Mon Sep 17 00:00:00 2001 From: Dominik Zborowski Date: Thu, 31 Oct 2024 02:09:55 +0100 Subject: [PATCH 10/36] Move parser to own folder --- .../grid/{utils => platform-parser}/__tests__/gridParser.spec.ts | 0 domains/grid/{utils => platform-parser}/gridParser.ts | 0 2 files changed, 0 insertions(+), 0 deletions(-) rename domains/grid/{utils => platform-parser}/__tests__/gridParser.spec.ts (100%) rename domains/grid/{utils => platform-parser}/gridParser.ts (100%) diff --git a/domains/grid/utils/__tests__/gridParser.spec.ts b/domains/grid/platform-parser/__tests__/gridParser.spec.ts similarity index 100% rename from domains/grid/utils/__tests__/gridParser.spec.ts rename to domains/grid/platform-parser/__tests__/gridParser.spec.ts diff --git a/domains/grid/utils/gridParser.ts b/domains/grid/platform-parser/gridParser.ts similarity index 100% rename from domains/grid/utils/gridParser.ts rename to domains/grid/platform-parser/gridParser.ts From 6834c38acb1e5319471c59000c49056ed5ab6b5b Mon Sep 17 00:00:00 2001 From: Eduardo Sacco <3680995+nastita@users.noreply.github.com> Date: Fri, 1 Nov 2024 19:39:41 -0300 Subject: [PATCH 11/36] WIP: Updating parser --- .../__tests__/gridParser.spec.ts | 331 ------------------ .../__tests__/instagramParser.spec.ts | 53 +++ .../__tests__/soundcloudParser.spec.ts | 71 ++++ .../__tests__/spotifyParser.spec.ts | 90 +++++ .../platform-parser/__tests__/xParser.spec.ts | 80 +++++ .../__tests__/youtubeParser.spec.ts | 29 ++ domains/grid/platform-parser/gridParser.ts | 142 ++------ .../grid/platform-parser/instagramParser.ts | 11 + .../grid/platform-parser/soundcloudParser.ts | 27 ++ domains/grid/platform-parser/spotifyParser.ts | 28 ++ domains/grid/platform-parser/xParser.ts | 38 ++ domains/grid/platform-parser/youtubeParser.ts | 27 ++ domains/grid/shared/config.ts | 78 +---- domains/schema/xWidgetSchema.ts | 7 +- 14 files changed, 507 insertions(+), 505 deletions(-) delete mode 100644 domains/grid/platform-parser/__tests__/gridParser.spec.ts create mode 100644 domains/grid/platform-parser/__tests__/instagramParser.spec.ts create mode 100644 domains/grid/platform-parser/__tests__/soundcloudParser.spec.ts create mode 100644 domains/grid/platform-parser/__tests__/spotifyParser.spec.ts create mode 100644 domains/grid/platform-parser/__tests__/xParser.spec.ts create mode 100644 domains/grid/platform-parser/__tests__/youtubeParser.spec.ts create mode 100644 domains/grid/platform-parser/instagramParser.ts create mode 100644 domains/grid/platform-parser/soundcloudParser.ts create mode 100644 domains/grid/platform-parser/spotifyParser.ts create mode 100644 domains/grid/platform-parser/xParser.ts create mode 100644 domains/grid/platform-parser/youtubeParser.ts diff --git a/domains/grid/platform-parser/__tests__/gridParser.spec.ts b/domains/grid/platform-parser/__tests__/gridParser.spec.ts deleted file mode 100644 index ba097314..00000000 --- a/domains/grid/platform-parser/__tests__/gridParser.spec.ts +++ /dev/null @@ -1,331 +0,0 @@ -import { describe, expect, it } from 'vitest' - -const YOUTUBE_IFRAME_ALLOW = - 'accelerometer; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share' -const SPOTIFY_IFRAME_ALLOW = - 'clipboard-write; encrypted-media; fullscreen; picture-in-picture' -const SOUNDCLOUD_IFRAME_ALLOW = - 'clipboard-write; encrypted-media; fullscreen; picture-in-picture' - -describe('parsePlatformInput', () => { - describe('X', () => { - it.each([ - [ - 'Handle', - GRID_WIDGET_TYPE.enum.X, - '@feindura', - { - src: 'https://twitter.com/feindura?ref_src=twsrc%5Etfw', - handle: 'feindura', - }, - ], - [ - 'Post URL', - GRID_WIDGET_TYPE.enum.X, - 'https://x.com/feindura/status/1804519711377436675', - { - src: 'https://twitter.com/feindura/status/1804519711377436675', - handle: 'feindura', - id: '1804519711377436675', - }, - ], - [ - 'Post URL twitter.com', - GRID_WIDGET_TYPE.enum.X, - 'https://twitter.com/feindura/status/1804519711377436675', - { - src: 'https://twitter.com/feindura/status/1804519711377436675', - handle: 'feindura', - id: '1804519711377436675', - }, - ], - [ - 'Post Embed Code', - GRID_WIDGET_TYPE.enum.X, - ' ', - { - src: 'https://twitter.com/feindura/status/1804519711377436675?ref_src=twsrc%5Etfw', - handle: 'feindura', - id: '1804519711377436675', - }, - ], - [ - 'Timeline URL', - GRID_WIDGET_TYPE.enum.X, - 'https://x.com/lukso_io', - { - src: 'https://twitter.com/lukso_io', - handle: 'lukso_io', - }, - ], - [ - 'Timeline URL twitter.com', - GRID_WIDGET_TYPE.enum.X, - 'https://twitter.com/lukso_io', - { - src: 'https://twitter.com/lukso_io', - handle: 'lukso_io', - }, - ], - [ - 'Timeline Embed Code', - GRID_WIDGET_TYPE.enum.X, - ' ', - { - src: 'https://twitter.com/lukso_io?ref_src=twsrc%5Etfw', - handle: 'lukso_io', - }, - ], - ])( - 'correctly parses %s', - async (_description, platform, input, expected) => { - const result = await parsePlatformInput(platform, input) - expect(result).toEqual(expected) - } - ) - }) - - describe('YOUTUBE', () => { - it.each([ - [ - 'URL', - GRID_WIDGET_TYPE.enum.YOUTUBE, - 'https://www.youtube.com/watch?v=Vw4JE64hsO8', - { - src: 'https://www.youtube.com/embed/Vw4JE64hsO8', - allow: YOUTUBE_IFRAME_ALLOW, - }, - ], - [ - 'Embed Code', - GRID_WIDGET_TYPE.enum.YOUTUBE, - '', - { - src: 'https://www.youtube.com/embed/Vw4JE64hsO8', - allow: YOUTUBE_IFRAME_ALLOW, - }, - ], - ])( - 'correctly parses %s', - async (_description, platform, input, expected) => { - const result = await parsePlatformInput(platform, input) - expect(result).toEqual(expected) - } - ) - }) - - describe('SPOTIFY', () => { - it.each([ - [ - 'Track URL', - GRID_WIDGET_TYPE.enum.SPOTIFY, - 'https://open.spotify.com/track/7xGfFoTpQ2E7fRF5lN10tr', - { - src: 'https://open.spotify.com/embed/track/7xGfFoTpQ2E7fRF5lN10tr?utm_source=oembed', - type: 'track', - id: '7xGfFoTpQ2E7fRF5lN10tr', - allow: SPOTIFY_IFRAME_ALLOW, - }, - ], - [ - 'Track Embed Code', - GRID_WIDGET_TYPE.enum.SPOTIFY, - '', - { - src: 'https://open.spotify.com/embed/track/2BHj31ufdEqVK5CkYDp9mA?utm_source=generator', - type: 'track', - id: '2BHj31ufdEqVK5CkYDp9mA', - allow: SPOTIFY_IFRAME_ALLOW, - }, - ], - [ - 'Track Embed Code with theme', - GRID_WIDGET_TYPE.enum.SPOTIFY, - '', - { - src: 'https://open.spotify.com/embed/track/48K735Rd3UQExzjXH004k1?utm_source=generator&theme=0', - type: 'track', - id: '48K735Rd3UQExzjXH004k1', - allow: SPOTIFY_IFRAME_ALLOW, - theme: '0', - }, - ], - [ - 'Playlist URL', - GRID_WIDGET_TYPE.enum.SPOTIFY, - 'https://open.spotify.com/playlist/7KFoK4LJ23EncELJwYmTDG', - { - src: 'https://open.spotify.com/embed/playlist/7KFoK4LJ23EncELJwYmTDG?utm_source=oembed', - type: 'playlist', - id: '7KFoK4LJ23EncELJwYmTDG', - allow: SPOTIFY_IFRAME_ALLOW, - }, - ], - [ - 'Playlist Embed Code', - GRID_WIDGET_TYPE.enum.SPOTIFY, - '', - { - src: 'https://open.spotify.com/embed/playlist/7KFoK4LJ23EncELJwYmTDG?utm_source=generator', - type: 'playlist', - id: '7KFoK4LJ23EncELJwYmTDG', - allow: SPOTIFY_IFRAME_ALLOW, - }, - ], - [ - 'Artist URL', - GRID_WIDGET_TYPE.enum.SPOTIFY, - 'https://open.spotify.com/artist/4KY9rCrokaoFzvMfX98u1q', - { - src: 'https://open.spotify.com/embed/artist/4KY9rCrokaoFzvMfX98u1q?utm_source=oembed', - type: 'artist', - id: '4KY9rCrokaoFzvMfX98u1q', - allow: SPOTIFY_IFRAME_ALLOW, - }, - ], - [ - 'Artist Embed Code', - GRID_WIDGET_TYPE.enum.SPOTIFY, - '', - { - src: 'https://open.spotify.com/embed/artist/4KY9rCrokaoFzvMfX98u1q?utm_source=generator', - type: 'artist', - id: '4KY9rCrokaoFzvMfX98u1q', - allow: SPOTIFY_IFRAME_ALLOW, - }, - ], - ])( - 'correctly parses %s', - async (_description, platform, input, expected) => { - const result = await parsePlatformInput(platform, input) - expect(result).toEqual(expected) - } - ) - }) - - describe('SOUNDCLOUD', () => { - it.each([ - [ - 'Track Share URL', - GRID_WIDGET_TYPE.enum.SOUNDCLOUD, - 'https://soundcloud.com/occams-laser/with-you', - { - src: 'https://w.soundcloud.com/player/?visual=true&url=https%3A%2F%2Fapi.soundcloud.com%2Ftracks%2F1856391039&show_artwork=true', - allow: SOUNDCLOUD_IFRAME_ALLOW, - type: 'tracks', - }, - ], - [ - 'Set Share URL', - GRID_WIDGET_TYPE.enum.SOUNDCLOUD, - 'https://w.soundcloud.com/player/?visual=true&url=https%3A%2F%2Fapi.soundcloud.com%2Fplaylists%2F505007376&show_artwork=true', - { - src: 'https://w.soundcloud.com/player/?visual=true&url=https%3A%2F%2Fapi.soundcloud.com%2Fplaylists%2F505007376&show_artwork=true', - allow: SPOTIFY_IFRAME_ALLOW, - type: 'playlists', - }, - ], - [ - 'Users Embed Code', - GRID_WIDGET_TYPE.enum.SOUNDCLOUD, - 'https://soundcloud.com/fabian-vogelsteller', - { - src: 'https://w.soundcloud.com/player/?visual=true&url=https%3A%2F%2Fapi.soundcloud.com%2Fusers%2F227118126&show_artwork=true', - allow: SOUNDCLOUD_IFRAME_ALLOW, - type: 'users', - }, - ], - [ - 'Track Embed Code', - GRID_WIDGET_TYPE.enum.SOUNDCLOUD, - '', - { - src: 'https://w.soundcloud.com/player/?url=https://api.soundcloud.com/tracks/1856391039&color=%23ff5500&auto_play=false&hide_related=false&show_comments=true&show_user=true&show_reposts=false&show_teaser=true&visual=true', - allow: SOUNDCLOUD_IFRAME_ALLOW, - type: 'tracks', - }, - ], - [ - 'Track Embed Code with URL encoded characters', - GRID_WIDGET_TYPE.enum.SOUNDCLOUD, - '', - { - src: 'https://w.soundcloud.com/player/?visual=true&url=https%3A%2F%2Fapi.soundcloud.com%2Ftracks%2F1856391039&show_artwork=true', - allow: SOUNDCLOUD_IFRAME_ALLOW, - type: 'tracks', - }, - ], - [ - 'Playlist Embed Code', - GRID_WIDGET_TYPE.enum.SOUNDCLOUD, - '', - { - src: 'https://w.soundcloud.com/player/?url=https://api.soundcloud.com/playlists/1850298147&color=%23ff5500&auto_play=false&hide_related=false&show_comments=true&show_user=true&show_reposts=false&show_teaser=true&visual=true', - allow: SOUNDCLOUD_IFRAME_ALLOW, - type: 'playlists', - }, - ], - ])( - 'correctly parses %s', - async (_description, platform, input, expected) => { - const result = await parsePlatformInput(platform, input) - expect(result).toEqual(expected) - } - ) - }) - - describe('INSTAGRAM', () => { - it.each([ - [ - 'Post URL', - GRID_WIDGET_TYPE.enum.INSTAGRAM, - 'https://www.instagram.com/p/C98OXs6yhAq/?utm_source=ig_embed&utm_campaign=loading', - { - src: 'https://www.instagram.com/p/C98OXs6yhAq/?utm_source=ig_embed&utm_campaign=loading', - type: 'p', - id: 'C98OXs6yhAq', - params: '?utm_source=ig_embed&utm_campaign=loading', - }, - ], - [ - 'Post Embed Code', - GRID_WIDGET_TYPE.enum.INSTAGRAM, - '
', - { - src: 'https://www.instagram.com/p/C98OXs6yhAq/?utm_source=ig_embed&utm_campaign=loading', - type: 'p', - id: 'C98OXs6yhAq', - params: '?utm_source=ig_embed&utm_campaign=loading', - }, - ], - [ - 'Reel URL', - GRID_WIDGET_TYPE.enum.INSTAGRAM, - 'https://www.instagram.com/reel/DAlOgHkuyxd/?utm_source=ig_embed&utm_campaign=loading', - { - src: 'https://www.instagram.com/reel/DAlOgHkuyxd/?utm_source=ig_embed&utm_campaign=loading', - type: 'reel', - id: 'DAlOgHkuyxd', - params: '?utm_source=ig_embed&utm_campaign=loading', - }, - ], - [ - 'Reel Embed Code', - GRID_WIDGET_TYPE.enum.INSTAGRAM, - '
', - { - src: 'https://www.instagram.com/reel/DAlOgHkuyxd/?utm_source=ig_embed&utm_campaign=loading', - type: 'reel', - id: 'DAlOgHkuyxd', - params: '?utm_source=ig_embed&utm_campaign=loading', - }, - ], - ])( - 'correctly parses %s', - async (_description, platform, input, expected) => { - const result = await parsePlatformInput(platform, input) - expect(result).toEqual(expected) - } - ) - }) -}) diff --git a/domains/grid/platform-parser/__tests__/instagramParser.spec.ts b/domains/grid/platform-parser/__tests__/instagramParser.spec.ts new file mode 100644 index 00000000..4ef59d20 --- /dev/null +++ b/domains/grid/platform-parser/__tests__/instagramParser.spec.ts @@ -0,0 +1,53 @@ +import { describe, expect, it } from 'vitest' + +describe('INSTAGRAM Input Parser', () => { + it.each([ + [ + 'Post URL', + GRID_WIDGET_TYPE.enum.INSTAGRAM, + 'https://www.instagram.com/p/C98OXs6yhAq/?utm_source=ig_embed&utm_campaign=loading', + { + src: 'https://www.instagram.com/p/C98OXs6yhAq/?utm_source=ig_embed&utm_campaign=loading', + type: 'p', + id: 'C98OXs6yhAq', + params: '?utm_source=ig_embed&utm_campaign=loading', + }, + ], + [ + 'Post Embed Code', + GRID_WIDGET_TYPE.enum.INSTAGRAM, + '
', + { + src: 'https://www.instagram.com/p/C98OXs6yhAq/?utm_source=ig_embed&utm_campaign=loading', + type: 'p', + id: 'C98OXs6yhAq', + params: '?utm_source=ig_embed&utm_campaign=loading', + }, + ], + [ + 'Reel URL', + GRID_WIDGET_TYPE.enum.INSTAGRAM, + 'https://www.instagram.com/reel/DAlOgHkuyxd/?utm_source=ig_embed&utm_campaign=loading', + { + src: 'https://www.instagram.com/reel/DAlOgHkuyxd/?utm_source=ig_embed&utm_campaign=loading', + type: 'reel', + id: 'DAlOgHkuyxd', + params: '?utm_source=ig_embed&utm_campaign=loading', + }, + ], + [ + 'Reel Embed Code', + GRID_WIDGET_TYPE.enum.INSTAGRAM, + '
', + { + src: 'https://www.instagram.com/reel/DAlOgHkuyxd/?utm_source=ig_embed&utm_campaign=loading', + type: 'reel', + id: 'DAlOgHkuyxd', + params: '?utm_source=ig_embed&utm_campaign=loading', + }, + ], + ])('correctly parses %s', async (_description, platform, input, expected) => { + const result = await parsePlatformInput(platform, input) + expect(result).toEqual(expected) + }) +}) diff --git a/domains/grid/platform-parser/__tests__/soundcloudParser.spec.ts b/domains/grid/platform-parser/__tests__/soundcloudParser.spec.ts new file mode 100644 index 00000000..38dcb32b --- /dev/null +++ b/domains/grid/platform-parser/__tests__/soundcloudParser.spec.ts @@ -0,0 +1,71 @@ +import { describe, expect, it } from 'vitest' +const SOUNDCLOUD_IFRAME_ALLOW = + 'clipboard-write; encrypted-media; fullscreen; picture-in-picture' + +describe('SOUNDCLOUD Input Parser', () => { + it.each([ + [ + 'Track Share URL', + GRID_WIDGET_TYPE.enum.SOUNDCLOUD, + 'https://soundcloud.com/occams-laser/with-you', + { + src: 'https://w.soundcloud.com/player/?visual=true&url=https%3A%2F%2Fapi.soundcloud.com%2Ftracks%2F1856391039&show_artwork=true', + allow: SOUNDCLOUD_IFRAME_ALLOW, + type: 'tracks', + }, + ], + [ + 'Set Share URL', + GRID_WIDGET_TYPE.enum.SOUNDCLOUD, + 'https://w.soundcloud.com/player/?visual=true&url=https%3A%2F%2Fapi.soundcloud.com%2Fplaylists%2F505007376&show_artwork=true', + { + src: 'https://w.soundcloud.com/player/?visual=true&url=https%3A%2F%2Fapi.soundcloud.com%2Fplaylists%2F505007376&show_artwork=true', + allow: SOUNDCLOUD_IFRAME_ALLOW, + type: 'playlists', + }, + ], + [ + 'Users Embed Code', + GRID_WIDGET_TYPE.enum.SOUNDCLOUD, + 'https://soundcloud.com/fabian-vogelsteller', + { + src: 'https://w.soundcloud.com/player/?visual=true&url=https%3A%2F%2Fapi.soundcloud.com%2Fusers%2F227118126&show_artwork=true', + allow: SOUNDCLOUD_IFRAME_ALLOW, + type: 'users', + }, + ], + [ + 'Track Embed Code', + GRID_WIDGET_TYPE.enum.SOUNDCLOUD, + '', + { + src: 'https://w.soundcloud.com/player/?url=https://api.soundcloud.com/tracks/1856391039&color=%23ff5500&auto_play=false&hide_related=false&show_comments=true&show_user=true&show_reposts=false&show_teaser=true&visual=true', + allow: SOUNDCLOUD_IFRAME_ALLOW, + type: 'tracks', + }, + ], + [ + 'Track Embed Code with URL encoded characters', + GRID_WIDGET_TYPE.enum.SOUNDCLOUD, + '', + { + src: 'https://w.soundcloud.com/player/?visual=true&url=https%3A%2F%2Fapi.soundcloud.com%2Ftracks%2F1856391039&show_artwork=true', + allow: SOUNDCLOUD_IFRAME_ALLOW, + type: 'tracks', + }, + ], + [ + 'Playlist Embed Code', + GRID_WIDGET_TYPE.enum.SOUNDCLOUD, + '', + { + src: 'https://w.soundcloud.com/player/?url=https://api.soundcloud.com/playlists/1850298147&color=%23ff5500&auto_play=false&hide_related=false&show_comments=true&show_user=true&show_reposts=false&show_teaser=true&visual=true', + allow: SOUNDCLOUD_IFRAME_ALLOW, + type: 'playlists', + }, + ], + ])('correctly parses %s', async (_description, platform, input, expected) => { + const result = await parsePlatformInput(platform, input) + expect(result).toEqual(expected) + }) +}) diff --git a/domains/grid/platform-parser/__tests__/spotifyParser.spec.ts b/domains/grid/platform-parser/__tests__/spotifyParser.spec.ts new file mode 100644 index 00000000..c8dcb0d6 --- /dev/null +++ b/domains/grid/platform-parser/__tests__/spotifyParser.spec.ts @@ -0,0 +1,90 @@ +import { describe, expect, it } from 'vitest' + +const SPOTIFY_IFRAME_ALLOW = + 'clipboard-write; encrypted-media; fullscreen; picture-in-picture' + +describe('SPOTIFY Input Parser', () => { + it.each([ + [ + 'Track URL', + GRID_WIDGET_TYPE.enum.SPOTIFY, + 'https://open.spotify.com/track/7xGfFoTpQ2E7fRF5lN10tr', + { + src: 'https://open.spotify.com/embed/track/7xGfFoTpQ2E7fRF5lN10tr?utm_source=oembed', + type: 'track', + id: '7xGfFoTpQ2E7fRF5lN10tr', + allow: SPOTIFY_IFRAME_ALLOW, + }, + ], + [ + 'Track Embed Code', + GRID_WIDGET_TYPE.enum.SPOTIFY, + '', + { + src: 'https://open.spotify.com/embed/track/2BHj31ufdEqVK5CkYDp9mA?utm_source=generator', + type: 'track', + id: '2BHj31ufdEqVK5CkYDp9mA', + allow: SPOTIFY_IFRAME_ALLOW, + }, + ], + [ + 'Track Embed Code with theme', + GRID_WIDGET_TYPE.enum.SPOTIFY, + '', + { + src: 'https://open.spotify.com/embed/track/48K735Rd3UQExzjXH004k1?utm_source=generator&theme=0', + type: 'track', + id: '48K735Rd3UQExzjXH004k1', + allow: SPOTIFY_IFRAME_ALLOW, + theme: '0', + }, + ], + [ + 'Playlist URL', + GRID_WIDGET_TYPE.enum.SPOTIFY, + 'https://open.spotify.com/playlist/7KFoK4LJ23EncELJwYmTDG', + { + src: 'https://open.spotify.com/embed/playlist/7KFoK4LJ23EncELJwYmTDG?utm_source=oembed', + type: 'playlist', + id: '7KFoK4LJ23EncELJwYmTDG', + allow: SPOTIFY_IFRAME_ALLOW, + }, + ], + [ + 'Playlist Embed Code', + GRID_WIDGET_TYPE.enum.SPOTIFY, + '', + { + src: 'https://open.spotify.com/embed/playlist/7KFoK4LJ23EncELJwYmTDG?utm_source=generator', + type: 'playlist', + id: '7KFoK4LJ23EncELJwYmTDG', + allow: SPOTIFY_IFRAME_ALLOW, + }, + ], + [ + 'Artist URL', + GRID_WIDGET_TYPE.enum.SPOTIFY, + 'https://open.spotify.com/artist/4KY9rCrokaoFzvMfX98u1q', + { + src: 'https://open.spotify.com/embed/artist/4KY9rCrokaoFzvMfX98u1q?utm_source=oembed', + type: 'artist', + id: '4KY9rCrokaoFzvMfX98u1q', + allow: SPOTIFY_IFRAME_ALLOW, + }, + ], + [ + 'Artist Embed Code', + GRID_WIDGET_TYPE.enum.SPOTIFY, + '', + { + src: 'https://open.spotify.com/embed/artist/4KY9rCrokaoFzvMfX98u1q?utm_source=generator', + type: 'artist', + id: '4KY9rCrokaoFzvMfX98u1q', + allow: SPOTIFY_IFRAME_ALLOW, + }, + ], + ])('correctly parses %s', async (_description, platform, input, expected) => { + const result = await parsePlatformInput(platform, input) + expect(result).toEqual(expected) + }) +}) diff --git a/domains/grid/platform-parser/__tests__/xParser.spec.ts b/domains/grid/platform-parser/__tests__/xParser.spec.ts new file mode 100644 index 00000000..b3023375 --- /dev/null +++ b/domains/grid/platform-parser/__tests__/xParser.spec.ts @@ -0,0 +1,80 @@ +import { describe, expect, it } from 'vitest' + +describe('X Input Parser', () => { + it.each([ + [ + 'Handle', + '@feindura', + { + type: 'timeline', + username: 'feindura', + }, + ], + [ + 'Handle without @', + 'feindura', + { + type: 'timeline', + username: 'feindura', + }, + ], + [ + 'Status URL', + 'https://x.com/feindura/status/1804519711377436675', + { + type: 'status', + username: 'feindura', + id: '1804519711377436675', + }, + ], + [ + 'Status URL twitter.com', + + 'https://twitter.com/feindura/status/1804519711377436675', + { + type: 'status', + username: 'feindura', + id: '1804519711377436675', + }, + ], + [ + 'Status Embed Code with extra props', + ' ', + { + type: 'status', + username: 'feindura', + id: '1804519711377436675', + theme: 'dark', + language: 'en', + doNotTrack: true, + }, + ], + [ + 'Timeline URL', + 'https://x.com/lukso_io', + { + type: 'timeline', + username: 'lukso_io', + }, + ], + [ + 'Timeline URL twitter.com', + 'https://twitter.com/lukso_io', + { + type: 'timeline', + username: 'lukso_io', + }, + ], + [ + 'Timeline Embed Code', + ' ', + { + type: 'timeline', + username: 'lukso_io', + }, + ], + ])('correctly parses %s', async (_description, input, expected) => { + const result = await parsePlatformInput(GRID_WIDGET_TYPE.Enum.X, input) + expect(result).toEqual(expected) + }) +}) diff --git a/domains/grid/platform-parser/__tests__/youtubeParser.spec.ts b/domains/grid/platform-parser/__tests__/youtubeParser.spec.ts new file mode 100644 index 00000000..1c5530f5 --- /dev/null +++ b/domains/grid/platform-parser/__tests__/youtubeParser.spec.ts @@ -0,0 +1,29 @@ +import { describe, expect, it } from 'vitest' +const YOUTUBE_IFRAME_ALLOW = + 'accelerometer; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share' + +describe('YOUTUBE Input Parser', () => { + it.each([ + [ + 'URL', + GRID_WIDGET_TYPE.enum.YOUTUBE, + 'https://www.youtube.com/watch?v=Vw4JE64hsO8', + { + src: 'https://www.youtube.com/embed/Vw4JE64hsO8', + allow: YOUTUBE_IFRAME_ALLOW, + }, + ], + [ + 'Embed Code', + GRID_WIDGET_TYPE.enum.YOUTUBE, + '', + { + src: 'https://www.youtube.com/embed/Vw4JE64hsO8', + allow: YOUTUBE_IFRAME_ALLOW, + }, + ], + ])('correctly parses %s', async (_description, platform, input, expected) => { + const result = await parsePlatformInput(platform, input) + expect(result).toEqual(expected) + }) +}) diff --git a/domains/grid/platform-parser/gridParser.ts b/domains/grid/platform-parser/gridParser.ts index be4d1fa7..5c400c9a 100644 --- a/domains/grid/platform-parser/gridParser.ts +++ b/domains/grid/platform-parser/gridParser.ts @@ -1,13 +1,13 @@ export type RegexWithCallback = { regex: RegExp - callback: (url: string) => Promise + callback: ( + matches: RegExpMatchArray[] + ) => Promise | undefined> } export type PlatformParsingParameters = { - type: GridWidgetType - embedRegex: RegExp - secondaryRegexesWithCallbacks?: RegexWithCallback[] - constantProperties?: Record + // type: GridWidgetType // TODO: Does the parser need to select the type? + regexWithCallbacks?: RegexWithCallback[] } export const parsePlatformInput = async ( @@ -20,115 +20,45 @@ export const parsePlatformInput = async ( throw new Error('Invalid platform') } - // Check if the input matches the embed regex - try { - return parsePlatformEmbed(input, platformParsingParameters) - } catch {} + const { regexWithCallbacks: regexesWithCallbacks } = platformParsingParameters - const { secondaryRegexesWithCallbacks } = platformParsingParameters - - if (!secondaryRegexesWithCallbacks) { - throw new Error('Invalid input') + if (!regexesWithCallbacks?.length) { + throw new Error('No regex patterns configured') } - // Check if the input matches a secondary regex - let callbackResult: string | undefined - - for (const { regex, callback } of secondaryRegexesWithCallbacks) { - const match = input.match(regex) - - if (match) { - callbackResult = await callback(match[0]) - - break + // Check each regex in order + for (const { regex, callback } of regexesWithCallbacks) { + let matches: RegExpMatchArray[] = [] + // Check if the regex is global + if (regex.global) { + matches = Array.from(input.matchAll(regex)) + } else { + const match = input.match(regex) + console.log(JSON.stringify(match)) + if (match) { + matches.push(match) + } } - } - - if (!callbackResult) { - throw new Error('Invalid input') - } - - return parsePlatformEmbed(callbackResult, platformParsingParameters) -} -const parsePlatformEmbed = ( - input: string, - platformParsingParameters: PlatformParsingParameters -) => { - const { embedRegex, constantProperties } = platformParsingParameters - const match = input.match(embedRegex) - - if (!match) { - throw new Error('Invalid input') - } - - const { groups } = match - let extractedProperties: Record = {} - - if (groups) { - // Extract the properties from capture groups from the regex match - extractedProperties = Object.entries(groups).reduce( - (acc: Record, [key, value]) => { - if (value) acc[key] = value - - return acc - }, - {} - ) - } - - return { - src: match[0], - ...constantProperties, - ...extractedProperties, + if (matches.length) { + console.log(matches) + return await callback(matches) + } } -} - -export async function getSoundCloudOEmbed( - url: string -): Promise { - const encodedUrl = encodeURI(url) - const response = await fetch( - `https://soundcloud.com/oembed?url=${encodedUrl}&format=json` - ) - return response.ok ? ((await response.json())?.html as string) : undefined + throw new Error('Invalid input') } -export async function getSpotifyOEmbed( - url: string -): Promise { - const response = await fetch( - `https://open.spotify.com/oembed?url=${url}&format=json` +export const getPropertiesFromGroups = (matches: RegExpMatchArray[]) => { + return matches.reduce( + (acc, match) => ({ + ...acc, + ...Object.fromEntries( + Object.entries(match.groups || {}).filter( + ([, value]) => value !== undefined + ) // Filter out undefined values + ), + }), + {} as Record ) - - return response.ok ? ((await response.json())?.html as string) : undefined -} - -export async function getXOEmbedFromHandle(handle: string) { - const response = await fetch( - `https://publish.twitter.com/oembed?url=https://twitter.com/${handle.replace('@', '')}` - ) - - return response.ok ? ((await response.json())?.html as string) : undefined -} - -export function sanitizeXEmbedUrl(url: string): string { - let newUrl = url.replace('x.com', 'twitter.com') - - if (!newUrl.startsWith('https://')) { - newUrl = `https://${newUrl}` - } - - return newUrl -} - -export function sanitizeYoutubeEmbedUrl(url: string): string { - let newUrl = url.replace('watch?v=', 'embed/') - - if (!url.startsWith('https://')) { - newUrl = `https://${newUrl}` - } - - return newUrl } diff --git a/domains/grid/platform-parser/instagramParser.ts b/domains/grid/platform-parser/instagramParser.ts new file mode 100644 index 00000000..3fa88a0a --- /dev/null +++ b/domains/grid/platform-parser/instagramParser.ts @@ -0,0 +1,11 @@ +export const PLATFORM_PARSING_PARAMETERS_INSTAGRAM: PlatformParsingParameters = + { + // type: GRID_WIDGET_TYPE.enum.INSTAGRAM, + regexWithCallbacks: [ + { + regex: + /https:\/\/www\.instagram\.com\/(?p|reel|profile|tv)\/(?[\w-]+)\/(?\?[^"]*)?/, + callback: async () => undefined, + }, + ], + } diff --git a/domains/grid/platform-parser/soundcloudParser.ts b/domains/grid/platform-parser/soundcloudParser.ts new file mode 100644 index 00000000..5b4b52dc --- /dev/null +++ b/domains/grid/platform-parser/soundcloudParser.ts @@ -0,0 +1,27 @@ +export const PLATFORM_PARSING_PARAMETERS_SOUNDCLOUD: PlatformParsingParameters = + { + // type: GRID_WIDGET_TYPE.enum.IFRAME, + regexWithCallbacks: [ + { + regex: + /https?:\/\/w\.soundcloud\.com\/player\/\?(?:(?!url=https).)*url=https(?::|%3A)(?:\/|%2F){2}api\.soundcloud\.com(?:\/|%2F)(?tracks|playlists|users)(?:\/|%2F)\d+(?:[^"]*)?/, + callback: async () => undefined, + }, + { + regex: + /https:\/\/soundcloud\.com\/([a-zA-Z0-9_-]+)(?:\/(sets\/[a-zA-Z0-9_-]+|[a-zA-Z0-9_-]+))?\/?/, + callback: async () => undefined, + }, + ], + } + +export async function getSoundCloudOEmbed( + url: string +): Promise { + const encodedUrl = encodeURI(url) + const response = await fetch( + `https://soundcloud.com/oembed?url=${encodedUrl}&format=json` + ) + + return response.ok ? ((await response.json())?.html as string) : undefined +} diff --git a/domains/grid/platform-parser/spotifyParser.ts b/domains/grid/platform-parser/spotifyParser.ts new file mode 100644 index 00000000..ef8da138 --- /dev/null +++ b/domains/grid/platform-parser/spotifyParser.ts @@ -0,0 +1,28 @@ +export const PLATFORM_PARSING_PARAMETERS_SPOTIFY: PlatformParsingParameters = { + // type: GRID_WIDGET_TYPE.enum.IFRAME, + regexWithCallbacks: [ + { + regex: + /https?:\/\/(?:open\.)?spotify\.com\/embed\/?(?track|playlist|artist)\/(?[^?]+)(?:\?utm_source=(?:generator|oembed))?(?:&theme=(?\d))?/, + callback: async () => undefined, + }, + { + regex: + /https:\/\/open\.spotify\.com\/(?track|playlist|artist)\/(?[^?]+)/, + callback: async () => undefined, + }, + ], + // constantProperties: { + // allow: 'clipboard-write; encrypted-media; fullscreen; picture-in-picture', + // }, +} + +export async function getSpotifyOEmbed( + url: string +): Promise { + const response = await fetch( + `https://open.spotify.com/oembed?url=${url}&format=json` + ) + + return response.ok ? ((await response.json())?.html as string) : undefined +} diff --git a/domains/grid/platform-parser/xParser.ts b/domains/grid/platform-parser/xParser.ts new file mode 100644 index 00000000..2cbfc37a --- /dev/null +++ b/domains/grid/platform-parser/xParser.ts @@ -0,0 +1,38 @@ +import { getPropertiesFromGroups } from './gridParser' + +export const PLATFORM_PARSING_PARAMETERS_X: PlatformParsingParameters = { + // type: GRID_WIDGET_TYPE.enum.X, + regexWithCallbacks: [ + { + regex: + /(?:https?:\/\/(?:(?:www\.)?(?:twitter|x)\.com)\/(?[a-zA-Z0-9_]+)(?:\/status\/(?\d+))?(?:\?[^"'\s]*)?)|(?:data-media-max-width="(?\d+)")|(?:data-theme="(?\w+)")|(?:data-lang="(?\w+)")|(?:data-dnt="(?true|false)")/g, + callback: async (matches: RegExpMatchArray[]) => { + // Flatten all matches' groups into a single object + const properties = getPropertiesFromGroups(matches) + const { username, id, mediaWidth, theme, language, doNotTrack } = + properties + + return { + type: mediaWidth ? 'video' : id ? 'status' : 'timeline', + username, + ...(id && { id }), + ...(theme && { theme }), + ...(language && { language }), + ...(doNotTrack && { doNotTrack: doNotTrack === 'true' }), + } + }, + }, + // Match a handle with @ symbol + { + regex: /^@?(?[a-zA-Z0-9_]{1,15})$/, + callback: async (matches: RegExpMatchArray[]) => { + const { username } = matches[0]?.groups ?? {} // Get the first match's first group + + return { + type: 'timeline', + username, + } + }, + }, + ], +} diff --git a/domains/grid/platform-parser/youtubeParser.ts b/domains/grid/platform-parser/youtubeParser.ts new file mode 100644 index 00000000..181a2c7e --- /dev/null +++ b/domains/grid/platform-parser/youtubeParser.ts @@ -0,0 +1,27 @@ +export const PLATFORM_PARSING_PARAMETERS_YOUTUBE: PlatformParsingParameters = { + // type: GRID_WIDGET_TYPE.enum.YOUTUBE, + regexWithCallbacks: [ + { + regex: /(?:https?:\/\/)?(?:www\.)?youtube\.com\/embed\/([^?]+)/, + callback: async () => undefined, + }, + { + regex: /(?:https?:\/\/)?(?:www\.)?youtube\.com\/watch\?v=([^&]+)/, + callback: async () => undefined, + }, + ], + // constantProperties: { + // allow: + // 'accelerometer; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share', + // }, +} + +export function sanitizeYoutubeEmbedUrl(url: string): string { + let newUrl = url.replace('watch?v=', 'embed/') + + if (!url.startsWith('https://')) { + newUrl = `https://${newUrl}` + } + + return newUrl +} diff --git a/domains/grid/shared/config.ts b/domains/grid/shared/config.ts index 388ef6d9..51d8cbb7 100644 --- a/domains/grid/shared/config.ts +++ b/domains/grid/shared/config.ts @@ -1,5 +1,11 @@ import { type ZodEffects, type ZodObject, z } from 'zod' +import { PLATFORM_PARSING_PARAMETERS_X } from '../platform-parser/xParser' +import { PLATFORM_PARSING_PARAMETERS_INSTAGRAM } from '../platform-parser/instagramParser' +import { PLATFORM_PARSING_PARAMETERS_SOUNDCLOUD } from '../platform-parser/soundcloudParser' +import { PLATFORM_PARSING_PARAMETERS_YOUTUBE } from '../platform-parser/youtubeParser' +import { PLATFORM_PARSING_PARAMETERS_SPOTIFY } from '../platform-parser/spotifyParser' + export const GRID_WIDGET_TYPE = z.enum([ // custom 'TEXT', @@ -67,71 +73,9 @@ export const GRID_COLUMNS_MAX = 4 export const PLATFORM_PARSING_PARAMETERS: Partial< Record > = { - [GRID_WIDGET_TYPE.enum.X]: { - type: GRID_WIDGET_TYPE.enum.X, - embedRegex: - /https?:\/\/twitter\.com\/(?[a-zA-Z0-9_]+)(?:\/status\/(?\d+))?(?:\?[^"'\s]*)?/, - secondaryRegexesWithCallbacks: [ - // Match a handle with @ symbol - { - regex: /@([a-zA-Z0-9_]{1,15})/, - callback: getXOEmbedFromHandle, - }, - // Match a Twitter URL with or without https www and status - { - regex: - /(https?:\/\/)?(?:x\.com|twitter\.com)\/[a-zA-Z0-9_]+(?:\/status\/(\d+))?/, - callback: async url => sanitizeXEmbedUrl(url), - }, - ], - }, - [GRID_WIDGET_TYPE.enum.INSTAGRAM]: { - type: GRID_WIDGET_TYPE.enum.INSTAGRAM, - embedRegex: - /https:\/\/www\.instagram\.com\/(?p|reel|profile|tv)\/(?[\w-]+)\/(?\?[^"]*)?/, - }, - [GRID_WIDGET_TYPE.enum.SPOTIFY]: { - type: GRID_WIDGET_TYPE.enum.IFRAME, - embedRegex: - /https?:\/\/(?:open\.)?spotify\.com\/embed\/?(?track|playlist|artist)\/(?[^?]+)(?:\?utm_source=(?:generator|oembed))?(?:&theme=(?\d))?/, - secondaryRegexesWithCallbacks: [ - { - regex: - /https:\/\/open\.spotify\.com\/(?track|playlist|artist)\/(?[^?]+)/, - callback: getSpotifyOEmbed, - }, - ], - constantProperties: { - allow: 'clipboard-write; encrypted-media; fullscreen; picture-in-picture', - }, - }, - [GRID_WIDGET_TYPE.enum.SOUNDCLOUD]: { - type: GRID_WIDGET_TYPE.enum.IFRAME, - embedRegex: - /https?:\/\/w\.soundcloud\.com\/player\/\?(?:(?!url=https).)*url=https(?::|%3A)(?:\/|%2F){2}api\.soundcloud\.com(?:\/|%2F)(?tracks|playlists|users)(?:\/|%2F)\d+(?:[^"]*)?/, - secondaryRegexesWithCallbacks: [ - { - regex: - /https:\/\/soundcloud\.com\/([a-zA-Z0-9_-]+)(?:\/(sets\/[a-zA-Z0-9_-]+|[a-zA-Z0-9_-]+))?\/?/, - callback: getSoundCloudOEmbed, - }, - ], - constantProperties: { - allow: 'clipboard-write; encrypted-media; fullscreen; picture-in-picture', - }, - }, - [GRID_WIDGET_TYPE.enum.YOUTUBE]: { - type: GRID_WIDGET_TYPE.enum.YOUTUBE, - embedRegex: /(?:https?:\/\/)?(?:www\.)?youtube\.com\/embed\/([^?]+)/, - secondaryRegexesWithCallbacks: [ - { - regex: /(?:https?:\/\/)?(?:www\.)?youtube\.com\/watch\?v=([^&]+)/, - callback: async url => sanitizeYoutubeEmbedUrl(url), - }, - ], - constantProperties: { - allow: - 'accelerometer; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share', - }, - }, + [GRID_WIDGET_TYPE.enum.X]: PLATFORM_PARSING_PARAMETERS_X, + [GRID_WIDGET_TYPE.enum.INSTAGRAM]: PLATFORM_PARSING_PARAMETERS_INSTAGRAM, + [GRID_WIDGET_TYPE.enum.SPOTIFY]: PLATFORM_PARSING_PARAMETERS_SPOTIFY, + [GRID_WIDGET_TYPE.enum.SOUNDCLOUD]: PLATFORM_PARSING_PARAMETERS_SOUNDCLOUD, + [GRID_WIDGET_TYPE.enum.YOUTUBE]: PLATFORM_PARSING_PARAMETERS_YOUTUBE, } diff --git a/domains/schema/xWidgetSchema.ts b/domains/schema/xWidgetSchema.ts index f6bf72b7..1f933796 100644 --- a/domains/schema/xWidgetSchema.ts +++ b/domains/schema/xWidgetSchema.ts @@ -3,7 +3,12 @@ import { z } from 'zod' export const xWidgetSchema = z .object({ src: z.string(), - type: z.string().optional(), + type: z.string(), + username: z.string(), + id: z.string().optional(), + theme: z.string().optional(), + language: z.string().optional(), + doNotTrack: z.boolean().optional(), }) .transform((values, ctx) => platformTransform(values, ctx, GRID_WIDGET_TYPE.enum.X) From efcbbfc5941baf82e766caf9aaa336ae07e729ff Mon Sep 17 00:00:00 2001 From: Eduardo Sacco <3680995+nastita@users.noreply.github.com> Date: Sun, 3 Nov 2024 11:46:40 -0300 Subject: [PATCH 12/36] Update iframe parsers --- .../__tests__/soundcloudParser.spec.ts | 44 ++++++---- .../__tests__/spotifyParser.spec.ts | 69 ++++++++------- .../__tests__/youtubeParser.spec.ts | 20 ++++- domains/grid/platform-parser/gridParser.ts | 2 +- .../platform-parser/iframeParserFactory.ts | 84 +++++++++++++++++++ .../grid/platform-parser/soundcloudParser.ts | 54 +++++++----- domains/grid/platform-parser/spotifyParser.ts | 38 ++++----- domains/grid/platform-parser/youtubeParser.ts | 66 ++++++++++----- 8 files changed, 267 insertions(+), 110 deletions(-) create mode 100644 domains/grid/platform-parser/iframeParserFactory.ts diff --git a/domains/grid/platform-parser/__tests__/soundcloudParser.spec.ts b/domains/grid/platform-parser/__tests__/soundcloudParser.spec.ts index 38dcb32b..19b99c39 100644 --- a/domains/grid/platform-parser/__tests__/soundcloudParser.spec.ts +++ b/domains/grid/platform-parser/__tests__/soundcloudParser.spec.ts @@ -9,9 +9,11 @@ describe('SOUNDCLOUD Input Parser', () => { GRID_WIDGET_TYPE.enum.SOUNDCLOUD, 'https://soundcloud.com/occams-laser/with-you', { - src: 'https://w.soundcloud.com/player/?visual=true&url=https%3A%2F%2Fapi.soundcloud.com%2Ftracks%2F1856391039&show_artwork=true', - allow: SOUNDCLOUD_IFRAME_ALLOW, - type: 'tracks', + allow: + 'autoplay; clipboard-write; encrypted-media; fullscreen; picture-in-picture', + height: '166', + src: 'https://w.soundcloud.com/player/?url=https://soundcloud.com/occams-laser/with-you', + width: '100%', }, ], [ @@ -19,9 +21,11 @@ describe('SOUNDCLOUD Input Parser', () => { GRID_WIDGET_TYPE.enum.SOUNDCLOUD, 'https://w.soundcloud.com/player/?visual=true&url=https%3A%2F%2Fapi.soundcloud.com%2Fplaylists%2F505007376&show_artwork=true', { - src: 'https://w.soundcloud.com/player/?visual=true&url=https%3A%2F%2Fapi.soundcloud.com%2Fplaylists%2F505007376&show_artwork=true', - allow: SOUNDCLOUD_IFRAME_ALLOW, - type: 'playlists', + allow: + 'autoplay; clipboard-write; encrypted-media; fullscreen; picture-in-picture', + height: '166', + src: 'https://w.soundcloud.com/player/?url=https://api.soundcloud.com/playlists/505007376&show_artwork=true', + width: '100%', }, ], [ @@ -29,9 +33,11 @@ describe('SOUNDCLOUD Input Parser', () => { GRID_WIDGET_TYPE.enum.SOUNDCLOUD, 'https://soundcloud.com/fabian-vogelsteller', { - src: 'https://w.soundcloud.com/player/?visual=true&url=https%3A%2F%2Fapi.soundcloud.com%2Fusers%2F227118126&show_artwork=true', - allow: SOUNDCLOUD_IFRAME_ALLOW, - type: 'users', + allow: + 'autoplay; clipboard-write; encrypted-media; fullscreen; picture-in-picture', + height: '166', + src: 'https://w.soundcloud.com/player/?url=https://soundcloud.com/fabian-vogelsteller', + width: '100%', }, ], [ @@ -39,9 +45,11 @@ describe('SOUNDCLOUD Input Parser', () => { GRID_WIDGET_TYPE.enum.SOUNDCLOUD, '', { + allow: 'autoplay', + height: '300', src: 'https://w.soundcloud.com/player/?url=https://api.soundcloud.com/tracks/1856391039&color=%23ff5500&auto_play=false&hide_related=false&show_comments=true&show_user=true&show_reposts=false&show_teaser=true&visual=true', - allow: SOUNDCLOUD_IFRAME_ALLOW, - type: 'tracks', + style: 'color: #cccccc; text-decoration: none;', + width: '100%', }, ], [ @@ -49,9 +57,11 @@ describe('SOUNDCLOUD Input Parser', () => { GRID_WIDGET_TYPE.enum.SOUNDCLOUD, '', { - src: 'https://w.soundcloud.com/player/?visual=true&url=https%3A%2F%2Fapi.soundcloud.com%2Ftracks%2F1856391039&show_artwork=true', - allow: SOUNDCLOUD_IFRAME_ALLOW, - type: 'tracks', + allow: + 'autoplay; clipboard-write; encrypted-media; fullscreen; picture-in-picture', + height: '400', + src: 'https://w.soundcloud.com/player/?url=https://api.soundcloud.com/tracks/1856391039&show_artwork=true', + width: '100%', }, ], [ @@ -59,9 +69,11 @@ describe('SOUNDCLOUD Input Parser', () => { GRID_WIDGET_TYPE.enum.SOUNDCLOUD, '', { + allow: 'autoplay', + height: '300', src: 'https://w.soundcloud.com/player/?url=https://api.soundcloud.com/playlists/1850298147&color=%23ff5500&auto_play=false&hide_related=false&show_comments=true&show_user=true&show_reposts=false&show_teaser=true&visual=true', - allow: SOUNDCLOUD_IFRAME_ALLOW, - type: 'playlists', + style: 'color: #cccccc; text-decoration: none;', + width: '100%', }, ], ])('correctly parses %s', async (_description, platform, input, expected) => { diff --git a/domains/grid/platform-parser/__tests__/spotifyParser.spec.ts b/domains/grid/platform-parser/__tests__/spotifyParser.spec.ts index c8dcb0d6..c19e623f 100644 --- a/domains/grid/platform-parser/__tests__/spotifyParser.spec.ts +++ b/domains/grid/platform-parser/__tests__/spotifyParser.spec.ts @@ -1,8 +1,5 @@ import { describe, expect, it } from 'vitest' -const SPOTIFY_IFRAME_ALLOW = - 'clipboard-write; encrypted-media; fullscreen; picture-in-picture' - describe('SPOTIFY Input Parser', () => { it.each([ [ @@ -10,10 +7,9 @@ describe('SPOTIFY Input Parser', () => { GRID_WIDGET_TYPE.enum.SPOTIFY, 'https://open.spotify.com/track/7xGfFoTpQ2E7fRF5lN10tr', { - src: 'https://open.spotify.com/embed/track/7xGfFoTpQ2E7fRF5lN10tr?utm_source=oembed', - type: 'track', - id: '7xGfFoTpQ2E7fRF5lN10tr', - allow: SPOTIFY_IFRAME_ALLOW, + src: 'https://open.spotify.com/embed/track/7xGfFoTpQ2E7fRF5lN10tr', + allow: + 'clipboard-write; encrypted-media; fullscreen; picture-in-picture', }, ], [ @@ -22,9 +18,14 @@ describe('SPOTIFY Input Parser', () => { '', { src: 'https://open.spotify.com/embed/track/2BHj31ufdEqVK5CkYDp9mA?utm_source=generator', - type: 'track', - id: '2BHj31ufdEqVK5CkYDp9mA', - allow: SPOTIFY_IFRAME_ALLOW, + allow: + 'autoplay; clipboard-write; encrypted-media; fullscreen; picture-in-picture', + style: 'border-radius:12px', + width: '100%', + allowfullscreen: true, + frameBorder: 0, + height: '352', + loading: 'lazy', }, ], [ @@ -33,10 +34,14 @@ describe('SPOTIFY Input Parser', () => { '', { src: 'https://open.spotify.com/embed/track/48K735Rd3UQExzjXH004k1?utm_source=generator&theme=0', - type: 'track', - id: '48K735Rd3UQExzjXH004k1', - allow: SPOTIFY_IFRAME_ALLOW, - theme: '0', + allow: + 'autoplay; clipboard-write; encrypted-media; fullscreen; picture-in-picture', + allowfullscreen: true, + frameBorder: 0, + height: '352', + loading: 'lazy', + style: 'border-radius:12px', + width: '100%', }, ], [ @@ -44,10 +49,9 @@ describe('SPOTIFY Input Parser', () => { GRID_WIDGET_TYPE.enum.SPOTIFY, 'https://open.spotify.com/playlist/7KFoK4LJ23EncELJwYmTDG', { - src: 'https://open.spotify.com/embed/playlist/7KFoK4LJ23EncELJwYmTDG?utm_source=oembed', - type: 'playlist', - id: '7KFoK4LJ23EncELJwYmTDG', - allow: SPOTIFY_IFRAME_ALLOW, + src: 'https://open.spotify.com/embed/playlist/7KFoK4LJ23EncELJwYmTDG', + allow: + 'clipboard-write; encrypted-media; fullscreen; picture-in-picture', }, ], [ @@ -56,9 +60,14 @@ describe('SPOTIFY Input Parser', () => { '', { src: 'https://open.spotify.com/embed/playlist/7KFoK4LJ23EncELJwYmTDG?utm_source=generator', - type: 'playlist', - id: '7KFoK4LJ23EncELJwYmTDG', - allow: SPOTIFY_IFRAME_ALLOW, + allow: + 'autoplay; clipboard-write; encrypted-media; fullscreen; picture-in-picture', + allowfullscreen: true, + frameBorder: 0, + height: '352', + loading: 'lazy', + style: 'border-radius:12px', + width: '100%', }, ], [ @@ -66,10 +75,9 @@ describe('SPOTIFY Input Parser', () => { GRID_WIDGET_TYPE.enum.SPOTIFY, 'https://open.spotify.com/artist/4KY9rCrokaoFzvMfX98u1q', { - src: 'https://open.spotify.com/embed/artist/4KY9rCrokaoFzvMfX98u1q?utm_source=oembed', - type: 'artist', - id: '4KY9rCrokaoFzvMfX98u1q', - allow: SPOTIFY_IFRAME_ALLOW, + src: 'https://open.spotify.com/embed/artist/4KY9rCrokaoFzvMfX98u1q', + allow: + 'clipboard-write; encrypted-media; fullscreen; picture-in-picture', }, ], [ @@ -78,9 +86,14 @@ describe('SPOTIFY Input Parser', () => { '', { src: 'https://open.spotify.com/embed/artist/4KY9rCrokaoFzvMfX98u1q?utm_source=generator', - type: 'artist', - id: '4KY9rCrokaoFzvMfX98u1q', - allow: SPOTIFY_IFRAME_ALLOW, + allow: + 'autoplay; clipboard-write; encrypted-media; fullscreen; picture-in-picture', + allowfullscreen: true, + frameBorder: 0, + height: '352', + loading: 'lazy', + style: 'border-radius:12px', + width: '100%', }, ], ])('correctly parses %s', async (_description, platform, input, expected) => { diff --git a/domains/grid/platform-parser/__tests__/youtubeParser.spec.ts b/domains/grid/platform-parser/__tests__/youtubeParser.spec.ts index 1c5530f5..86a58061 100644 --- a/domains/grid/platform-parser/__tests__/youtubeParser.spec.ts +++ b/domains/grid/platform-parser/__tests__/youtubeParser.spec.ts @@ -9,8 +9,14 @@ describe('YOUTUBE Input Parser', () => { GRID_WIDGET_TYPE.enum.YOUTUBE, 'https://www.youtube.com/watch?v=Vw4JE64hsO8', { - src: 'https://www.youtube.com/embed/Vw4JE64hsO8', - allow: YOUTUBE_IFRAME_ALLOW, + allow: + 'accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share', + allowfullscreen: true, + frameborder: '0', + height: '315', + referrerpolicy: 'strict-origin-when-cross-origin', + src: 'https://www.youtube-nocookie.com/embed/Vw4JE64hsO8', + width: '560', }, ], [ @@ -18,8 +24,14 @@ describe('YOUTUBE Input Parser', () => { GRID_WIDGET_TYPE.enum.YOUTUBE, '', { - src: 'https://www.youtube.com/embed/Vw4JE64hsO8', - allow: YOUTUBE_IFRAME_ALLOW, + allow: + 'accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share', + allowfullscreen: true, + frameborder: '0', + height: '315', + referrerpolicy: 'strict-origin-when-cross-origin', + src: 'https://www.youtube-nocookie.com/embed/Vw4JE64hsO8?si=OwVdFZCStaL90G2W', + width: '560', }, ], ])('correctly parses %s', async (_description, platform, input, expected) => { diff --git a/domains/grid/platform-parser/gridParser.ts b/domains/grid/platform-parser/gridParser.ts index 5c400c9a..ee80572b 100644 --- a/domains/grid/platform-parser/gridParser.ts +++ b/domains/grid/platform-parser/gridParser.ts @@ -2,7 +2,7 @@ export type RegexWithCallback = { regex: RegExp callback: ( matches: RegExpMatchArray[] - ) => Promise | undefined> + ) => Promise | undefined> } export type PlatformParsingParameters = { diff --git a/domains/grid/platform-parser/iframeParserFactory.ts b/domains/grid/platform-parser/iframeParserFactory.ts new file mode 100644 index 00000000..4c5565ab --- /dev/null +++ b/domains/grid/platform-parser/iframeParserFactory.ts @@ -0,0 +1,84 @@ +import { getPropertiesFromGroups } from './gridParser' + +// Common iframe attributes regex patterns +const COMMON_IFRAME_ATTRIBUTES: Record = { + style: '(?:style="(?