This repository has no description
1export const getDomain = (url: string) => {
2 return new URL(url).hostname;
3};
4
5export const getUrlFromSlug = (slug: string[]) => {
6 const decoded = slug.map(decodeURIComponent);
7 const url = decoded.join('/');
8
9 // only normalize if the scheme has a single slash after it (i.e. malformed)
10 const normalizedUrl = /^([a-zA-Z]+:)\/[^/]/.test(url)
11 ? url.replace(/^([a-zA-Z]+:)\//, '$1//')
12 : url;
13
14 return normalizedUrl;
15};
16
17export const isCollectionPage = (url: string = window.location.pathname) => {
18 try {
19 const { pathname } = new URL(url);
20 // expect /profile/:handle/collections/:id
21 const pattern = /^\/profile\/[^/]+\/collections\/[^/]+\/?$/;
22 return pattern.test(pathname);
23 } catch {
24 // invalid URL
25 return false;
26 }
27};
28
29export const isProfilePage = (url: string = window.location.pathname) => {
30 try {
31 const { pathname } = new URL(url, window.location.origin);
32 // matches:
33 // /profile/:handle
34 // /profile/:handle/:subroute
35 const pattern = /^\/profile\/[^/]+(?:\/[^/]+)?\/?$/;
36 return pattern.test(pathname);
37 } catch {
38 // invalid URL
39 return false;
40 }
41};
42
43export enum SupportedPlatform {
44 BLUESKY_POST = 'bluesky post',
45 BLACKSKY_POST = 'blacksky post',
46 SEMBLE_COLLECTION = 'semble collection',
47 SPOTIFY = 'spotify',
48 PLYRFM_TRACK = 'plyr.fm',
49 YOUTUBE_VIDEO = 'youtube video',
50 BANDCAMP_ALBUM = 'bandcamp album',
51 BANDCAMP_TRACK = 'bandcamp track',
52 DEFAULT = 'default',
53}
54
55type PlatformData =
56 | {
57 type: SupportedPlatform.SEMBLE_COLLECTION;
58 url: string;
59 handle: string;
60 rkey: string;
61 }
62 | {
63 type: Exclude<SupportedPlatform, SupportedPlatform.SEMBLE_COLLECTION>;
64 url: string;
65 };
66
67export const detectUrlPlatform = (url: string): PlatformData => {
68 if (isCollectionPage(url)) {
69 try {
70 const parsedUrl = new URL(url, window.location.origin);
71 const pathParts = parsedUrl.pathname.split('/').filter(Boolean);
72 // expected format: profile/:handle/collections/:rkey
73 const handle = pathParts[1];
74 const rkey = pathParts[3];
75 return { type: SupportedPlatform.SEMBLE_COLLECTION, url, handle, rkey };
76 } catch {
77 return {
78 type: SupportedPlatform.SEMBLE_COLLECTION,
79 url,
80 handle: '',
81 rkey: '',
82 };
83 }
84 }
85
86 try {
87 const parsedUrl = new URL(url);
88
89 // bluesky posts
90 // https://bsky.app/profile/handle/post/id
91 if (
92 parsedUrl.hostname === 'bsky.app' &&
93 parsedUrl.pathname.includes('/post/')
94 ) {
95 return { type: SupportedPlatform.BLUESKY_POST, url };
96 }
97
98 // blacksky posts
99 // https://blacksky.community/profile/handle/post/id
100 if (
101 parsedUrl.hostname === 'blacksky.community' &&
102 parsedUrl.pathname.includes('/post/')
103 ) {
104 return { type: SupportedPlatform.BLACKSKY_POST, url };
105 }
106
107 // youtube
108 if (parsedUrl.hostname === 'youtu.be') {
109 const videoId = parsedUrl.pathname.split('/')[1];
110 const t = parsedUrl.searchParams.get('t') ?? '0';
111 const seek = encodeURIComponent(t.replace(/s$/, ''));
112
113 if (videoId) {
114 return {
115 type: SupportedPlatform.YOUTUBE_VIDEO,
116 url: `https://www.youtube.com/embed/${videoId}?start=${seek}`,
117 };
118 }
119 }
120
121 // youtube
122 if (
123 parsedUrl.hostname === 'www.youtube.com' ||
124 parsedUrl.hostname === 'youtube.com' ||
125 parsedUrl.hostname === 'm.youtube.com' ||
126 parsedUrl.hostname === 'music.youtube.com'
127 ) {
128 const [__, page, shortOrLiveVideoId] = parsedUrl.pathname.split('/');
129
130 const isShorts = page === 'shorts';
131 const isLive = page === 'live';
132 const videoId =
133 isShorts || isLive
134 ? shortOrLiveVideoId
135 : (parsedUrl.searchParams.get('v') as string);
136 const t = parsedUrl.searchParams.get('t') ?? '0';
137 const seek = encodeURIComponent(t.replace(/s$/, ''));
138
139 return {
140 type: SupportedPlatform.YOUTUBE_VIDEO,
141 url: `https://www.youtube.com/embed/${videoId}?start=${seek}`,
142 };
143 }
144
145 // spotify
146 if (parsedUrl.hostname === 'open.spotify.com') {
147 const [__, typeOrLocale, idOrType, id] = parsedUrl.pathname.split('/');
148
149 if (typeOrLocale === 'album' || idOrType === 'album') {
150 return {
151 type: SupportedPlatform.SPOTIFY,
152 url: `https://open.spotify.com/embed/album/${id ?? idOrType}`,
153 };
154 }
155 if (typeOrLocale === 'track' || idOrType === 'track') {
156 return {
157 type: SupportedPlatform.SPOTIFY,
158 url: `https://open.spotify.com/embed/track/${id ?? idOrType}`,
159 };
160 }
161 if (typeOrLocale === 'episode' || idOrType === 'episode') {
162 return {
163 type: SupportedPlatform.SPOTIFY,
164 url: `https://open.spotify.com/embed/episode/${id ?? idOrType}`,
165 };
166 }
167 if (typeOrLocale === 'show' || idOrType === 'show') {
168 return {
169 type: SupportedPlatform.SPOTIFY,
170 url: `https://open.spotify.com/embed/show/${id ?? idOrType}`,
171 };
172 }
173 }
174
175 // plyr.fm
176 if (
177 parsedUrl.hostname === 'plyr.fm' ||
178 parsedUrl.hostname === 'www.plyr.fm'
179 ) {
180 const [__, type, id] = parsedUrl.pathname.split('/');
181
182 if (type === 'track' && id) {
183 return {
184 type: SupportedPlatform.PLYRFM_TRACK,
185 url: `https://plyr.fm/embed/track/${id}`,
186 };
187 }
188 }
189
190 // bandcamp
191 const bandcampRegex = /^[a-z\d][a-z\d-]{2,}[a-z\d]\.bandcamp\.com$/i;
192 if (bandcampRegex.test(parsedUrl.hostname)) {
193 const pathComponents = parsedUrl.pathname.split('/');
194 switch (pathComponents[1]) {
195 case 'album':
196 return {
197 type: SupportedPlatform.BANDCAMP_ALBUM,
198 url: `https://bandcamp.com/EmbeddedPlayer/url=${encodeURIComponent(
199 parsedUrl.href,
200 )}/size=medium/minimal=true/transparent=true/`,
201 };
202 case 'track':
203 return {
204 type: SupportedPlatform.BANDCAMP_TRACK,
205 url: `https://bandcamp.com/EmbeddedPlayer/url=${encodeURIComponent(
206 parsedUrl.href,
207 )}/size=medium/minimal=true/transparent=true/`,
208 };
209 }
210 }
211
212 return { type: SupportedPlatform.DEFAULT, url }; // no supported service detected
213 } catch (e) {
214 // invalid url
215 return { type: SupportedPlatform.DEFAULT, url };
216 }
217};