mirror of https://git.lenooby09.tech/LeNooby09/social-app.git
at no-pointer-events 403 lines 11 kB view raw
1import {Dimensions, Platform} from 'react-native' 2const {height: SCREEN_HEIGHT} = Dimensions.get('window') 3 4export const embedPlayerSources = [ 5 'youtube', 6 'youtubeShorts', 7 'twitch', 8 'spotify', 9 'soundcloud', 10 'appleMusic', 11 'vimeo', 12 'giphy', 13 'tenor', 14] as const 15 16export type EmbedPlayerSource = (typeof embedPlayerSources)[number] 17 18export type EmbedPlayerType = 19 | 'youtube_video' 20 | 'youtube_short' 21 | 'twitch_video' 22 | 'spotify_album' 23 | 'spotify_playlist' 24 | 'spotify_song' 25 | 'soundcloud_track' 26 | 'soundcloud_set' 27 | 'apple_music_playlist' 28 | 'apple_music_album' 29 | 'apple_music_song' 30 | 'vimeo_video' 31 | 'giphy_gif' 32 | 'tenor_gif' 33 34export const externalEmbedLabels: Record<EmbedPlayerSource, string> = { 35 youtube: 'YouTube', 36 youtubeShorts: 'YouTube Shorts', 37 vimeo: 'Vimeo', 38 twitch: 'Twitch', 39 giphy: 'GIPHY', 40 tenor: 'Tenor', 41 spotify: 'Spotify', 42 appleMusic: 'Apple Music', 43 soundcloud: 'SoundCloud', 44} 45 46export interface EmbedPlayerParams { 47 type: EmbedPlayerType 48 playerUri: string 49 isGif?: boolean 50 source: EmbedPlayerSource 51 metaUri?: string 52 hideDetails?: boolean 53} 54 55const giphyRegex = /media(?:[0-4]\.giphy\.com|\.giphy\.com)/i 56const gifFilenameRegex = /^(\S+)\.(webp|gif|mp4)$/i 57 58export function parseEmbedPlayerFromUrl( 59 url: string, 60): EmbedPlayerParams | undefined { 61 let urlp 62 try { 63 urlp = new URL(url) 64 } catch (e) { 65 return undefined 66 } 67 68 // youtube 69 if (urlp.hostname === 'youtu.be') { 70 const videoId = urlp.pathname.split('/')[1] 71 if (videoId) { 72 return { 73 type: 'youtube_video', 74 source: 'youtube', 75 playerUri: `https://www.youtube.com/embed/${videoId}?autoplay=1&playsinline=1`, 76 } 77 } 78 } 79 if ( 80 urlp.hostname === 'www.youtube.com' || 81 urlp.hostname === 'youtube.com' || 82 urlp.hostname === 'm.youtube.com' 83 ) { 84 const [_, page, shortVideoId] = urlp.pathname.split('/') 85 const videoId = 86 page === 'shorts' ? shortVideoId : (urlp.searchParams.get('v') as string) 87 88 if (videoId) { 89 return { 90 type: page === 'shorts' ? 'youtube_short' : 'youtube_video', 91 source: page === 'shorts' ? 'youtubeShorts' : 'youtube', 92 hideDetails: page === 'shorts' ? true : undefined, 93 playerUri: `https://www.youtube.com/embed/${videoId}?autoplay=1&playsinline=1`, 94 } 95 } 96 } 97 98 // twitch 99 if ( 100 urlp.hostname === 'twitch.tv' || 101 urlp.hostname === 'www.twitch.tv' || 102 urlp.hostname === 'm.twitch.tv' 103 ) { 104 const parent = 105 Platform.OS === 'web' ? window.location.hostname : 'localhost' 106 107 const [_, channelOrVideo, clipOrId, id] = urlp.pathname.split('/') 108 109 if (channelOrVideo === 'videos') { 110 return { 111 type: 'twitch_video', 112 source: 'twitch', 113 playerUri: `https://player.twitch.tv/?volume=0.5&!muted&autoplay&video=${clipOrId}&parent=${parent}`, 114 } 115 } else if (clipOrId === 'clip') { 116 return { 117 type: 'twitch_video', 118 source: 'twitch', 119 playerUri: `https://clips.twitch.tv/embed?volume=0.5&autoplay=true&clip=${id}&parent=${parent}`, 120 } 121 } else if (channelOrVideo) { 122 return { 123 type: 'twitch_video', 124 source: 'twitch', 125 playerUri: `https://player.twitch.tv/?volume=0.5&!muted&autoplay&channel=${channelOrVideo}&parent=${parent}`, 126 } 127 } 128 } 129 130 // spotify 131 if (urlp.hostname === 'open.spotify.com') { 132 const [_, typeOrLocale, idOrType, id] = urlp.pathname.split('/') 133 134 if (idOrType) { 135 if (typeOrLocale === 'playlist' || idOrType === 'playlist') { 136 return { 137 type: 'spotify_playlist', 138 source: 'spotify', 139 playerUri: `https://open.spotify.com/embed/playlist/${ 140 id ?? idOrType 141 }`, 142 } 143 } 144 if (typeOrLocale === 'album' || idOrType === 'album') { 145 return { 146 type: 'spotify_album', 147 source: 'spotify', 148 playerUri: `https://open.spotify.com/embed/album/${id ?? idOrType}`, 149 } 150 } 151 if (typeOrLocale === 'track' || idOrType === 'track') { 152 return { 153 type: 'spotify_song', 154 source: 'spotify', 155 playerUri: `https://open.spotify.com/embed/track/${id ?? idOrType}`, 156 } 157 } 158 } 159 } 160 161 // soundcloud 162 if ( 163 urlp.hostname === 'soundcloud.com' || 164 urlp.hostname === 'www.soundcloud.com' 165 ) { 166 const [_, user, trackOrSets, set] = urlp.pathname.split('/') 167 168 if (user && trackOrSets) { 169 if (trackOrSets === 'sets' && set) { 170 return { 171 type: 'soundcloud_set', 172 source: 'soundcloud', 173 playerUri: `https://w.soundcloud.com/player/?url=${url}&auto_play=true&visual=false&hide_related=true`, 174 } 175 } 176 177 return { 178 type: 'soundcloud_track', 179 source: 'soundcloud', 180 playerUri: `https://w.soundcloud.com/player/?url=${url}&auto_play=true&visual=false&hide_related=true`, 181 } 182 } 183 } 184 185 if ( 186 urlp.hostname === 'music.apple.com' || 187 urlp.hostname === 'music.apple.com' 188 ) { 189 // This should always have: locale, type (playlist or album), name, and id. We won't use spread since we want 190 // to check if the length is correct 191 const pathParams = urlp.pathname.split('/') 192 const type = pathParams[2] 193 const songId = urlp.searchParams.get('i') 194 195 if (pathParams.length === 5 && (type === 'playlist' || type === 'album')) { 196 // We want to append the songId to the end of the url if it exists 197 const embedUri = `https://embed.music.apple.com${urlp.pathname}${ 198 urlp.search ? '?i=' + songId : '' 199 }` 200 201 if (type === 'playlist') { 202 return { 203 type: 'apple_music_playlist', 204 source: 'appleMusic', 205 playerUri: embedUri, 206 } 207 } else if (type === 'album') { 208 if (songId) { 209 return { 210 type: 'apple_music_song', 211 source: 'appleMusic', 212 playerUri: embedUri, 213 } 214 } else { 215 return { 216 type: 'apple_music_album', 217 source: 'appleMusic', 218 playerUri: embedUri, 219 } 220 } 221 } 222 } 223 } 224 225 if (urlp.hostname === 'vimeo.com' || urlp.hostname === 'www.vimeo.com') { 226 const [_, videoId] = urlp.pathname.split('/') 227 if (videoId) { 228 return { 229 type: 'vimeo_video', 230 source: 'vimeo', 231 playerUri: `https://player.vimeo.com/video/${videoId}?autoplay=1`, 232 } 233 } 234 } 235 236 if (urlp.hostname === 'giphy.com' || urlp.hostname === 'www.giphy.com') { 237 const [_, gifs, nameAndId] = urlp.pathname.split('/') 238 239 /* 240 * nameAndId is a string that consists of the name (dash separated) and the id of the gif (the last part of the name) 241 * We want to get the id of the gif, then direct to media.giphy.com/media/{id}/giphy.webp so we can 242 * use it in an <Image> component 243 */ 244 245 if (gifs === 'gifs' && nameAndId) { 246 const gifId = nameAndId.split('-').pop() 247 248 if (gifId) { 249 return { 250 type: 'giphy_gif', 251 source: 'giphy', 252 isGif: true, 253 hideDetails: true, 254 metaUri: `https://giphy.com/gifs/${gifId}`, 255 playerUri: `https://i.giphy.com/media/${gifId}/giphy.webp`, 256 } 257 } 258 } 259 } 260 261 // There are five possible hostnames that also can be giphy urls: media.giphy.com and media0-4.giphy.com 262 // These can include (presumably) a tracking id in the path name, so we have to check for that as well 263 if (giphyRegex.test(urlp.hostname)) { 264 // We can link directly to the gif, if its a proper link 265 const [_, media, trackingOrId, idOrFilename, filename] = 266 urlp.pathname.split('/') 267 268 if (media === 'media') { 269 if (idOrFilename && gifFilenameRegex.test(idOrFilename)) { 270 return { 271 type: 'giphy_gif', 272 source: 'giphy', 273 isGif: true, 274 hideDetails: true, 275 metaUri: `https://giphy.com/gifs/${trackingOrId}`, 276 playerUri: `https://i.giphy.com/media/${trackingOrId}/giphy.webp`, 277 } 278 } else if (filename && gifFilenameRegex.test(filename)) { 279 return { 280 type: 'giphy_gif', 281 source: 'giphy', 282 isGif: true, 283 hideDetails: true, 284 metaUri: `https://giphy.com/gifs/${idOrFilename}`, 285 playerUri: `https://i.giphy.com/media/${idOrFilename}/giphy.webp`, 286 } 287 } 288 } 289 } 290 291 // Finally, we should see if it is a link to i.giphy.com. These links don't necessarily end in .gif but can also 292 // be .webp 293 if (urlp.hostname === 'i.giphy.com' || urlp.hostname === 'www.i.giphy.com') { 294 const [_, mediaOrFilename, filename] = urlp.pathname.split('/') 295 296 if (mediaOrFilename === 'media' && filename) { 297 const gifId = filename.split('.')[0] 298 return { 299 type: 'giphy_gif', 300 source: 'giphy', 301 isGif: true, 302 hideDetails: true, 303 metaUri: `https://giphy.com/gifs/${gifId}`, 304 playerUri: `https://i.giphy.com/media/${gifId}/giphy.webp`, 305 } 306 } else if (mediaOrFilename) { 307 const gifId = mediaOrFilename.split('.')[0] 308 return { 309 type: 'giphy_gif', 310 source: 'giphy', 311 isGif: true, 312 hideDetails: true, 313 metaUri: `https://giphy.com/gifs/${gifId}`, 314 playerUri: `https://i.giphy.com/media/${ 315 mediaOrFilename.split('.')[0] 316 }/giphy.webp`, 317 } 318 } 319 } 320 321 if (urlp.hostname === 'tenor.com' || urlp.hostname === 'www.tenor.com') { 322 const [_, pathOrIntl, pathOrFilename, intlFilename] = 323 urlp.pathname.split('/') 324 const isIntl = pathOrFilename === 'view' 325 const filename = isIntl ? intlFilename : pathOrFilename 326 327 if ((pathOrIntl === 'view' || pathOrFilename === 'view') && filename) { 328 const includesExt = filename.split('.').pop() === 'gif' 329 330 return { 331 type: 'tenor_gif', 332 source: 'tenor', 333 isGif: true, 334 hideDetails: true, 335 playerUri: `${url}${!includesExt ? '.gif' : ''}`, 336 } 337 } 338 } 339} 340 341export function getPlayerHeight({ 342 type, 343 width, 344 hasThumb, 345}: { 346 type: EmbedPlayerParams['type'] 347 width: number 348 hasThumb: boolean 349}) { 350 if (!hasThumb) return (width / 16) * 9 351 352 switch (type) { 353 case 'youtube_video': 354 case 'twitch_video': 355 case 'vimeo_video': 356 return (width / 16) * 9 357 case 'youtube_short': 358 if (SCREEN_HEIGHT < 600) { 359 return ((width / 9) * 16) / 1.75 360 } else { 361 return ((width / 9) * 16) / 1.5 362 } 363 case 'spotify_album': 364 case 'apple_music_album': 365 case 'apple_music_playlist': 366 case 'spotify_playlist': 367 case 'soundcloud_set': 368 return 380 369 case 'spotify_song': 370 if (width <= 300) { 371 return 155 372 } 373 return 232 374 case 'soundcloud_track': 375 return 165 376 case 'apple_music_song': 377 return 150 378 default: 379 return width 380 } 381} 382 383export function getGifDims( 384 originalHeight: number, 385 originalWidth: number, 386 viewWidth: number, 387) { 388 const scaledHeight = (originalHeight / originalWidth) * viewWidth 389 390 return { 391 height: scaledHeight > 250 ? 250 : scaledHeight, 392 width: (250 / scaledHeight) * viewWidth, 393 } 394} 395 396export function getGiphyMetaUri(url: URL) { 397 if (giphyRegex.test(url.hostname) || url.hostname === 'i.giphy.com') { 398 const params = parseEmbedPlayerFromUrl(url.toString()) 399 if (params && params.type === 'giphy_gif') { 400 return params.metaUri 401 } 402 } 403}