mirror of https://git.lenooby09.tech/LeNooby09/social-app.git
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}