sveltekit-embeds 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (81) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +90 -0
  3. package/dist/components/apple-music.svelte +39 -0
  4. package/dist/components/apple-music.svelte.d.ts +11 -0
  5. package/dist/components/apple-podcasts.svelte +39 -0
  6. package/dist/components/apple-podcasts.svelte.d.ts +11 -0
  7. package/dist/components/bluesky.svelte +75 -0
  8. package/dist/components/bluesky.svelte.d.ts +10 -0
  9. package/dist/components/dailymotion.svelte +70 -0
  10. package/dist/components/dailymotion.svelte.d.ts +12 -0
  11. package/dist/components/deezer.svelte +40 -0
  12. package/dist/components/deezer.svelte.d.ts +11 -0
  13. package/dist/components/general-observer.svelte +46 -0
  14. package/dist/components/general-observer.svelte.d.ts +9 -0
  15. package/dist/components/instagram.svelte +37 -0
  16. package/dist/components/instagram.svelte.d.ts +10 -0
  17. package/dist/components/linked-in.svelte +37 -0
  18. package/dist/components/linked-in.svelte.d.ts +10 -0
  19. package/dist/components/mastodon.svelte +78 -0
  20. package/dist/components/mastodon.svelte.d.ts +10 -0
  21. package/dist/components/pinterest.svelte +86 -0
  22. package/dist/components/pinterest.svelte.d.ts +7 -0
  23. package/dist/components/reddit.svelte +38 -0
  24. package/dist/components/reddit.svelte.d.ts +11 -0
  25. package/dist/components/sound-cloud.svelte +48 -0
  26. package/dist/components/sound-cloud.svelte.d.ts +13 -0
  27. package/dist/components/spotify.svelte +40 -0
  28. package/dist/components/spotify.svelte.d.ts +11 -0
  29. package/dist/components/threads.svelte +88 -0
  30. package/dist/components/threads.svelte.d.ts +7 -0
  31. package/dist/components/tidal.svelte +37 -0
  32. package/dist/components/tidal.svelte.d.ts +10 -0
  33. package/dist/components/tik-tok.svelte +74 -0
  34. package/dist/components/tik-tok.svelte.d.ts +13 -0
  35. package/dist/components/twitch.svelte +71 -0
  36. package/dist/components/twitch.svelte.d.ts +12 -0
  37. package/dist/components/x-embed.svelte +94 -0
  38. package/dist/components/x-embed.svelte.d.ts +8 -0
  39. package/dist/components/you-tube.svelte +85 -0
  40. package/dist/components/you-tube.svelte.d.ts +14 -0
  41. package/dist/index.d.ts +20 -0
  42. package/dist/index.js +20 -0
  43. package/dist/utils/apple-music.d.ts +11 -0
  44. package/dist/utils/apple-music.js +59 -0
  45. package/dist/utils/apple-podcasts.d.ts +11 -0
  46. package/dist/utils/apple-podcasts.js +56 -0
  47. package/dist/utils/bluesky.d.ts +8 -0
  48. package/dist/utils/bluesky.js +35 -0
  49. package/dist/utils/dailymotion.d.ts +2 -0
  50. package/dist/utils/dailymotion.js +21 -0
  51. package/dist/utils/deezer.d.ts +10 -0
  52. package/dist/utils/deezer.js +56 -0
  53. package/dist/utils/index.d.ts +19 -0
  54. package/dist/utils/index.js +25 -0
  55. package/dist/utils/instagram.d.ts +2 -0
  56. package/dist/utils/instagram.js +21 -0
  57. package/dist/utils/linkedin.d.ts +7 -0
  58. package/dist/utils/linkedin.js +38 -0
  59. package/dist/utils/mastodon.d.ts +12 -0
  60. package/dist/utils/mastodon.js +36 -0
  61. package/dist/utils/pinterest.d.ts +2 -0
  62. package/dist/utils/pinterest.js +21 -0
  63. package/dist/utils/reddit.d.ts +2 -0
  64. package/dist/utils/reddit.js +45 -0
  65. package/dist/utils/soundcloud.d.ts +11 -0
  66. package/dist/utils/soundcloud.js +26 -0
  67. package/dist/utils/spotify.d.ts +10 -0
  68. package/dist/utils/spotify.js +71 -0
  69. package/dist/utils/threads.d.ts +9 -0
  70. package/dist/utils/threads.js +49 -0
  71. package/dist/utils/tidal.d.ts +10 -0
  72. package/dist/utils/tidal.js +53 -0
  73. package/dist/utils/tiktok.d.ts +2 -0
  74. package/dist/utils/tiktok.js +18 -0
  75. package/dist/utils/twitch.d.ts +9 -0
  76. package/dist/utils/twitch.js +37 -0
  77. package/dist/utils/twitter.d.ts +2 -0
  78. package/dist/utils/twitter.js +35 -0
  79. package/dist/utils/youtube.d.ts +3 -0
  80. package/dist/utils/youtube.js +40 -0
  81. package/package.json +79 -0
@@ -0,0 +1,85 @@
1
+ <script lang="ts">
2
+ import GeneralObserver from './general-observer.svelte';
3
+ import { getPadding } from '../utils/index.js';
4
+ import { extractYouTubeVideoId, getYouTubeEmbedUrl } from '../utils/youtube.js';
5
+
6
+ interface Props {
7
+ url: string;
8
+ aspectRatio?: string;
9
+ autoPlay?: boolean;
10
+ mute?: boolean;
11
+ controls?: boolean;
12
+ loop?: boolean;
13
+ start?: number;
14
+ disable_observer?: boolean;
15
+ iframe_styles?: string;
16
+ }
17
+
18
+ let {
19
+ url,
20
+ aspectRatio = '16:9',
21
+ autoPlay = false,
22
+ mute = false,
23
+ controls = true,
24
+ loop = false,
25
+ start = 0,
26
+ disable_observer = false,
27
+ iframe_styles
28
+ }: Props = $props();
29
+
30
+ const videoId = $derived(extractYouTubeVideoId(url));
31
+ const baseEmbedUrl = $derived(getYouTubeEmbedUrl(url));
32
+ const paddingStyle = $derived(getPadding(aspectRatio));
33
+ const embedUrl = $derived.by(() => {
34
+ if (!baseEmbedUrl) {
35
+ return null;
36
+ }
37
+
38
+ const params = new URLSearchParams({
39
+ autoplay: autoPlay ? '1' : '0',
40
+ mute: mute ? '1' : '0',
41
+ controls: controls ? '1' : '0'
42
+ });
43
+
44
+ if (loop && videoId) {
45
+ params.set('loop', '1');
46
+ params.set('playlist', videoId);
47
+ }
48
+
49
+ if (start > 0) {
50
+ params.set('start', String(start));
51
+ }
52
+
53
+ return `${baseEmbedUrl}?${params.toString()}`;
54
+ });
55
+ </script>
56
+
57
+ <GeneralObserver {disable_observer}>
58
+ {#if embedUrl}
59
+ <div class="wrapper" style={paddingStyle}>
60
+ <iframe
61
+ src={embedUrl}
62
+ title={`youtube-${videoId}`}
63
+ allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share"
64
+ allowfullscreen
65
+ loading="lazy"
66
+ style={iframe_styles}
67
+ ></iframe>
68
+ </div>
69
+ {/if}
70
+ </GeneralObserver>
71
+
72
+ <style>
73
+ .wrapper {
74
+ position: relative;
75
+ width: 100%;
76
+ }
77
+
78
+ iframe {
79
+ position: absolute;
80
+ inset: 0;
81
+ width: 100%;
82
+ height: 100%;
83
+ border: 0;
84
+ }
85
+ </style>
@@ -0,0 +1,14 @@
1
+ interface Props {
2
+ url: string;
3
+ aspectRatio?: string;
4
+ autoPlay?: boolean;
5
+ mute?: boolean;
6
+ controls?: boolean;
7
+ loop?: boolean;
8
+ start?: number;
9
+ disable_observer?: boolean;
10
+ iframe_styles?: string;
11
+ }
12
+ declare const YouTube: import("svelte").Component<Props, {}, "">;
13
+ type YouTube = ReturnType<typeof YouTube>;
14
+ export default YouTube;
@@ -0,0 +1,20 @@
1
+ export { default as GeneralObserver } from './components/general-observer.svelte';
2
+ export { default as YouTube } from './components/you-tube.svelte';
3
+ export { default as TikTok } from './components/tik-tok.svelte';
4
+ export { default as Twitch } from './components/twitch.svelte';
5
+ export { default as Dailymotion } from './components/dailymotion.svelte';
6
+ export { default as Spotify } from './components/spotify.svelte';
7
+ export { default as AppleMusic } from './components/apple-music.svelte';
8
+ export { default as ApplePodcasts } from './components/apple-podcasts.svelte';
9
+ export { default as SoundCloud } from './components/sound-cloud.svelte';
10
+ export { default as Deezer } from './components/deezer.svelte';
11
+ export { default as Tidal } from './components/tidal.svelte';
12
+ export { default as XEmbed } from './components/x-embed.svelte';
13
+ export { default as Instagram } from './components/instagram.svelte';
14
+ export { default as Threads } from './components/threads.svelte';
15
+ export { default as Bluesky } from './components/bluesky.svelte';
16
+ export { default as Mastodon } from './components/mastodon.svelte';
17
+ export { default as LinkedIn } from './components/linked-in.svelte';
18
+ export { default as Reddit } from './components/reddit.svelte';
19
+ export { default as Pinterest } from './components/pinterest.svelte';
20
+ export * from './utils/index.js';
package/dist/index.js ADDED
@@ -0,0 +1,20 @@
1
+ export { default as GeneralObserver } from './components/general-observer.svelte';
2
+ export { default as YouTube } from './components/you-tube.svelte';
3
+ export { default as TikTok } from './components/tik-tok.svelte';
4
+ export { default as Twitch } from './components/twitch.svelte';
5
+ export { default as Dailymotion } from './components/dailymotion.svelte';
6
+ export { default as Spotify } from './components/spotify.svelte';
7
+ export { default as AppleMusic } from './components/apple-music.svelte';
8
+ export { default as ApplePodcasts } from './components/apple-podcasts.svelte';
9
+ export { default as SoundCloud } from './components/sound-cloud.svelte';
10
+ export { default as Deezer } from './components/deezer.svelte';
11
+ export { default as Tidal } from './components/tidal.svelte';
12
+ export { default as XEmbed } from './components/x-embed.svelte';
13
+ export { default as Instagram } from './components/instagram.svelte';
14
+ export { default as Threads } from './components/threads.svelte';
15
+ export { default as Bluesky } from './components/bluesky.svelte';
16
+ export { default as Mastodon } from './components/mastodon.svelte';
17
+ export { default as LinkedIn } from './components/linked-in.svelte';
18
+ export { default as Reddit } from './components/reddit.svelte';
19
+ export { default as Pinterest } from './components/pinterest.svelte';
20
+ export * from './utils/index.js';
@@ -0,0 +1,11 @@
1
+ export type AppleMusicItemType = 'song' | 'album' | 'playlist' | 'artist' | 'station' | 'music-video';
2
+ export interface AppleMusicUrlInfo {
3
+ id: string;
4
+ type: AppleMusicItemType;
5
+ country: string;
6
+ isValid: boolean;
7
+ originalUrl: string;
8
+ }
9
+ export declare const isValidAppleMusicUrl: (url: string) => boolean;
10
+ export declare const parseAppleMusicUrl: (url: string) => AppleMusicUrlInfo;
11
+ export declare const getAppleMusicEmbedUrl: (url: string, theme?: "light" | "dark") => string | null;
@@ -0,0 +1,59 @@
1
+ export const isValidAppleMusicUrl = (url) => {
2
+ if (!url) {
3
+ return false;
4
+ }
5
+ try {
6
+ const urlObj = new URL(url);
7
+ return urlObj.hostname.includes('music.apple.com');
8
+ }
9
+ catch {
10
+ return false;
11
+ }
12
+ };
13
+ export const parseAppleMusicUrl = (url) => {
14
+ const fallback = {
15
+ id: '',
16
+ type: 'song',
17
+ country: '',
18
+ isValid: false,
19
+ originalUrl: url
20
+ };
21
+ if (!isValidAppleMusicUrl(url)) {
22
+ return fallback;
23
+ }
24
+ try {
25
+ const urlObj = new URL(url);
26
+ const pathParts = urlObj.pathname.split('/').filter(Boolean);
27
+ if (pathParts.length < 3) {
28
+ return fallback;
29
+ }
30
+ const country = pathParts[0] ?? '';
31
+ const type = (pathParts[1] ?? 'song');
32
+ const id = pathParts[pathParts.length - 1] ?? '';
33
+ const songId = urlObj.searchParams.get('i');
34
+ return {
35
+ id,
36
+ type: songId ? 'song' : type,
37
+ country,
38
+ isValid: Boolean(id),
39
+ originalUrl: url
40
+ };
41
+ }
42
+ catch {
43
+ return fallback;
44
+ }
45
+ };
46
+ export const getAppleMusicEmbedUrl = (url, theme = 'light') => {
47
+ if (!isValidAppleMusicUrl(url)) {
48
+ return null;
49
+ }
50
+ try {
51
+ const embedUrl = new URL(url);
52
+ embedUrl.hostname = 'embed.music.apple.com';
53
+ embedUrl.searchParams.set('theme', theme);
54
+ return embedUrl.toString();
55
+ }
56
+ catch {
57
+ return null;
58
+ }
59
+ };
@@ -0,0 +1,11 @@
1
+ export type ApplePodcastsItemType = 'show' | 'episode';
2
+ export interface ApplePodcastsUrlInfo {
3
+ id: string;
4
+ type: ApplePodcastsItemType;
5
+ country: string;
6
+ isValid: boolean;
7
+ originalUrl: string;
8
+ }
9
+ export declare const isValidApplePodcastsUrl: (url: string) => boolean;
10
+ export declare const parseApplePodcastsUrl: (url: string) => ApplePodcastsUrlInfo;
11
+ export declare const getApplePodcastsEmbedUrl: (url: string, theme?: "light" | "dark") => string | null;
@@ -0,0 +1,56 @@
1
+ export const isValidApplePodcastsUrl = (url) => {
2
+ if (!url) {
3
+ return false;
4
+ }
5
+ try {
6
+ const urlObj = new URL(url);
7
+ return urlObj.hostname.includes('podcasts.apple.com');
8
+ }
9
+ catch {
10
+ return false;
11
+ }
12
+ };
13
+ export const parseApplePodcastsUrl = (url) => {
14
+ const fallback = {
15
+ id: '',
16
+ type: 'show',
17
+ country: '',
18
+ isValid: false,
19
+ originalUrl: url
20
+ };
21
+ if (!isValidApplePodcastsUrl(url)) {
22
+ return fallback;
23
+ }
24
+ try {
25
+ const urlObj = new URL(url);
26
+ const pathParts = urlObj.pathname.split('/').filter(Boolean);
27
+ const country = pathParts[0] ?? '';
28
+ const episodeId = urlObj.searchParams.get('i');
29
+ const showId = urlObj.pathname.match(/\/id(\d+)/)?.[1] ?? '';
30
+ const id = episodeId || showId;
31
+ return {
32
+ id,
33
+ type: episodeId ? 'episode' : 'show',
34
+ country,
35
+ isValid: Boolean(id),
36
+ originalUrl: url
37
+ };
38
+ }
39
+ catch {
40
+ return fallback;
41
+ }
42
+ };
43
+ export const getApplePodcastsEmbedUrl = (url, theme = 'light') => {
44
+ if (!isValidApplePodcastsUrl(url)) {
45
+ return null;
46
+ }
47
+ try {
48
+ const embedUrl = new URL(url);
49
+ embedUrl.hostname = 'embed.podcasts.apple.com';
50
+ embedUrl.searchParams.set('theme', theme);
51
+ return embedUrl.toString();
52
+ }
53
+ catch {
54
+ return null;
55
+ }
56
+ };
@@ -0,0 +1,8 @@
1
+ export interface BlueskyUrlInfo {
2
+ handle: string;
3
+ postId: string;
4
+ isValid: boolean;
5
+ originalUrl: string;
6
+ }
7
+ export declare const parseBlueskyUrl: (url: string) => BlueskyUrlInfo;
8
+ export declare const getBlueskyEmbedUrl: (url: string) => string | null;
@@ -0,0 +1,35 @@
1
+ export const parseBlueskyUrl = (url) => {
2
+ const fallback = {
3
+ handle: '',
4
+ postId: '',
5
+ isValid: false,
6
+ originalUrl: url
7
+ };
8
+ try {
9
+ const urlObj = new URL(url);
10
+ const hostname = urlObj.hostname.toLowerCase();
11
+ if (!hostname.includes('bsky.app')) {
12
+ return fallback;
13
+ }
14
+ const match = urlObj.pathname.match(/\/profile\/([^/]+)\/post\/([^/?#]+)/);
15
+ if (!match?.[1] || !match?.[2]) {
16
+ return fallback;
17
+ }
18
+ return {
19
+ handle: match[1],
20
+ postId: match[2],
21
+ isValid: true,
22
+ originalUrl: url
23
+ };
24
+ }
25
+ catch {
26
+ return fallback;
27
+ }
28
+ };
29
+ export const getBlueskyEmbedUrl = (url) => {
30
+ const info = parseBlueskyUrl(url);
31
+ if (!info.isValid) {
32
+ return null;
33
+ }
34
+ return `https://embed.bsky.app/embed/${info.handle}/post/${info.postId}`;
35
+ };
@@ -0,0 +1,2 @@
1
+ export declare const extractDailymotionId: (url: string) => string | null;
2
+ export declare const getDailymotionEmbedUrl: (url: string) => string | null;
@@ -0,0 +1,21 @@
1
+ export const extractDailymotionId = (url) => {
2
+ try {
3
+ const urlObj = new URL(url);
4
+ const hostname = urlObj.hostname.toLowerCase();
5
+ if (hostname.includes('dai.ly')) {
6
+ return urlObj.pathname.split('/').filter(Boolean)[0] ?? null;
7
+ }
8
+ const match = urlObj.pathname.match(/\/video\/([a-zA-Z0-9]+)/);
9
+ return match?.[1] ?? null;
10
+ }
11
+ catch {
12
+ return null;
13
+ }
14
+ };
15
+ export const getDailymotionEmbedUrl = (url) => {
16
+ const id = extractDailymotionId(url);
17
+ if (!id) {
18
+ return null;
19
+ }
20
+ return `https://www.dailymotion.com/embed/video/${id}`;
21
+ };
@@ -0,0 +1,10 @@
1
+ export type DeezerItemType = 'track' | 'album' | 'playlist' | 'artist' | 'podcast' | 'episode';
2
+ export interface DeezerUrlInfo {
3
+ id: string;
4
+ type: DeezerItemType;
5
+ isValid: boolean;
6
+ originalUrl: string;
7
+ }
8
+ export declare const isValidDeezerUrl: (url: string) => boolean;
9
+ export declare const parseDeezerUrl: (url: string) => DeezerUrlInfo;
10
+ export declare const getDeezerEmbedUrl: (url: string, theme?: "light" | "dark") => string | null;
@@ -0,0 +1,56 @@
1
+ const DEEZER_TYPES = ['track', 'album', 'playlist', 'artist', 'podcast', 'episode'];
2
+ export const isValidDeezerUrl = (url) => {
3
+ if (!url) {
4
+ return false;
5
+ }
6
+ try {
7
+ const urlObj = new URL(url);
8
+ return urlObj.hostname.includes('deezer.com');
9
+ }
10
+ catch {
11
+ return false;
12
+ }
13
+ };
14
+ export const parseDeezerUrl = (url) => {
15
+ const fallback = {
16
+ id: '',
17
+ type: 'track',
18
+ isValid: false,
19
+ originalUrl: url
20
+ };
21
+ if (!isValidDeezerUrl(url)) {
22
+ return fallback;
23
+ }
24
+ try {
25
+ const urlObj = new URL(url);
26
+ const pathParts = urlObj.pathname.split('/').filter(Boolean);
27
+ const firstPart = pathParts[0] ?? '';
28
+ const hasCountryPrefix = firstPart.length === 2 && !DEEZER_TYPES.includes(firstPart);
29
+ const normalizedParts = hasCountryPrefix ? pathParts.slice(1) : pathParts;
30
+ const type = (normalizedParts[0] ?? '');
31
+ const id = normalizedParts[1] ?? '';
32
+ if (!DEEZER_TYPES.includes(type) || !id) {
33
+ return fallback;
34
+ }
35
+ return {
36
+ id,
37
+ type,
38
+ isValid: true,
39
+ originalUrl: url
40
+ };
41
+ }
42
+ catch {
43
+ return fallback;
44
+ }
45
+ };
46
+ export const getDeezerEmbedUrl = (url, theme = 'light') => {
47
+ const info = parseDeezerUrl(url);
48
+ if (!info.isValid) {
49
+ return null;
50
+ }
51
+ const themeSegment = theme === 'dark' ? 'dark' : 'light';
52
+ if (info.type === 'artist') {
53
+ return `https://widget.deezer.com/widget/${themeSegment}/artist/${info.id}/top_tracks`;
54
+ }
55
+ return `https://widget.deezer.com/widget/${themeSegment}/${info.type}/${info.id}`;
56
+ };
@@ -0,0 +1,19 @@
1
+ export declare const getPadding: (aspectRatio?: string) => string;
2
+ export * from './youtube.js';
3
+ export * from './spotify.js';
4
+ export * from './tiktok.js';
5
+ export * from './twitch.js';
6
+ export * from './dailymotion.js';
7
+ export * from './apple-music.js';
8
+ export * from './apple-podcasts.js';
9
+ export * from './soundcloud.js';
10
+ export * from './deezer.js';
11
+ export * from './tidal.js';
12
+ export * from './twitter.js';
13
+ export * from './instagram.js';
14
+ export * from './threads.js';
15
+ export * from './bluesky.js';
16
+ export * from './mastodon.js';
17
+ export * from './linkedin.js';
18
+ export * from './reddit.js';
19
+ export * from './pinterest.js';
@@ -0,0 +1,25 @@
1
+ const PADDING_BY_ASPECT_RATIO = {
2
+ '1:1': 'padding-top: 100%;',
3
+ '16:9': 'padding-top: 56.25%;',
4
+ '4:3': 'padding-top: 75%;',
5
+ '3:2': 'padding-top: 66.66%;'
6
+ };
7
+ export const getPadding = (aspectRatio = '16:9') => PADDING_BY_ASPECT_RATIO[aspectRatio] ?? PADDING_BY_ASPECT_RATIO['16:9'];
8
+ export * from './youtube.js';
9
+ export * from './spotify.js';
10
+ export * from './tiktok.js';
11
+ export * from './twitch.js';
12
+ export * from './dailymotion.js';
13
+ export * from './apple-music.js';
14
+ export * from './apple-podcasts.js';
15
+ export * from './soundcloud.js';
16
+ export * from './deezer.js';
17
+ export * from './tidal.js';
18
+ export * from './twitter.js';
19
+ export * from './instagram.js';
20
+ export * from './threads.js';
21
+ export * from './bluesky.js';
22
+ export * from './mastodon.js';
23
+ export * from './linkedin.js';
24
+ export * from './reddit.js';
25
+ export * from './pinterest.js';
@@ -0,0 +1,2 @@
1
+ export declare const extractInstagramId: (url: string) => string | null;
2
+ export declare const getInstagramEmbedUrl: (url: string) => string | null;
@@ -0,0 +1,21 @@
1
+ export const extractInstagramId = (url) => {
2
+ try {
3
+ const urlObj = new URL(url);
4
+ const hostname = urlObj.hostname.toLowerCase();
5
+ if (!hostname.includes('instagram.com')) {
6
+ return null;
7
+ }
8
+ const match = urlObj.pathname.match(/\/(?:p|reel|tv)\/([^/?#]+)/);
9
+ return match?.[1] ?? null;
10
+ }
11
+ catch {
12
+ return null;
13
+ }
14
+ };
15
+ export const getInstagramEmbedUrl = (url) => {
16
+ const postId = extractInstagramId(url);
17
+ if (!postId) {
18
+ return null;
19
+ }
20
+ return `https://www.instagram.com/p/${postId}/embed`;
21
+ };
@@ -0,0 +1,7 @@
1
+ export interface LinkedInUrlInfo {
2
+ postId: string;
3
+ isValid: boolean;
4
+ originalUrl: string;
5
+ }
6
+ export declare const extractLinkedInPostId: (url: string) => string | null;
7
+ export declare const getLinkedInEmbedUrl: (url: string) => string | null;
@@ -0,0 +1,38 @@
1
+ export const extractLinkedInPostId = (url) => {
2
+ try {
3
+ const urlObj = new URL(url);
4
+ const hostname = urlObj.hostname.toLowerCase();
5
+ if (!hostname.includes('linkedin.com')) {
6
+ return null;
7
+ }
8
+ const activityId = urlObj.pathname.match(/urn:li:activity:(\d+)/)?.[1];
9
+ if (activityId) {
10
+ return activityId;
11
+ }
12
+ const ugcPostId = urlObj.pathname.match(/urn:li:ugcPost:(\d+)/)?.[1];
13
+ if (ugcPostId) {
14
+ return ugcPostId;
15
+ }
16
+ return null;
17
+ }
18
+ catch {
19
+ return null;
20
+ }
21
+ };
22
+ export const getLinkedInEmbedUrl = (url) => {
23
+ try {
24
+ const urlObj = new URL(url);
25
+ const hostname = urlObj.hostname.toLowerCase();
26
+ if (!hostname.includes('linkedin.com')) {
27
+ return null;
28
+ }
29
+ const path = urlObj.pathname.replace(/\/$/, '');
30
+ if (!path.includes('/feed/update/')) {
31
+ return null;
32
+ }
33
+ return `https://www.linkedin.com/embed${path}`;
34
+ }
35
+ catch {
36
+ return null;
37
+ }
38
+ };
@@ -0,0 +1,12 @@
1
+ export interface MastodonUrlInfo {
2
+ instance: string;
3
+ path: string;
4
+ isValid: boolean;
5
+ originalUrl: string;
6
+ }
7
+ export declare const parseMastodonUrl: (url: string) => MastodonUrlInfo;
8
+ export declare const getMastodonEmbedInfo: (url: string) => {
9
+ scriptUrl: string;
10
+ embedUrl: string;
11
+ instance: string;
12
+ } | null;
@@ -0,0 +1,36 @@
1
+ export const parseMastodonUrl = (url) => {
2
+ const fallback = {
3
+ instance: '',
4
+ path: '',
5
+ isValid: false,
6
+ originalUrl: url
7
+ };
8
+ try {
9
+ const urlObj = new URL(url);
10
+ const path = urlObj.pathname.replace(/\/$/, '');
11
+ if (!path.includes('/@')) {
12
+ return fallback;
13
+ }
14
+ return {
15
+ instance: urlObj.origin,
16
+ path,
17
+ isValid: true,
18
+ originalUrl: url
19
+ };
20
+ }
21
+ catch {
22
+ return fallback;
23
+ }
24
+ };
25
+ export const getMastodonEmbedInfo = (url) => {
26
+ const info = parseMastodonUrl(url);
27
+ if (!info.isValid) {
28
+ return null;
29
+ }
30
+ const embedPath = info.path.endsWith('/embed') ? info.path : `${info.path}/embed`;
31
+ return {
32
+ scriptUrl: `${info.instance}/embed.js`,
33
+ embedUrl: `${info.instance}${embedPath}`,
34
+ instance: info.instance
35
+ };
36
+ };
@@ -0,0 +1,2 @@
1
+ export declare const extractPinterestId: (url: string) => string | null;
2
+ export declare const getPinterestPostUrl: (url: string) => string | null;
@@ -0,0 +1,21 @@
1
+ export const extractPinterestId = (url) => {
2
+ try {
3
+ const urlObj = new URL(url);
4
+ const hostname = urlObj.hostname.toLowerCase();
5
+ if (!hostname.includes('pinterest.') && hostname !== 'pin.it') {
6
+ return null;
7
+ }
8
+ const match = urlObj.pathname.match(/\/pin\/(\d+)/);
9
+ return match?.[1] ?? null;
10
+ }
11
+ catch {
12
+ return null;
13
+ }
14
+ };
15
+ export const getPinterestPostUrl = (url) => {
16
+ const pinId = extractPinterestId(url);
17
+ if (!pinId) {
18
+ return null;
19
+ }
20
+ return `https://www.pinterest.com/pin/${pinId}/`;
21
+ };
@@ -0,0 +1,2 @@
1
+ export declare const extractRedditPostId: (rawUrl: string) => string | null;
2
+ export declare const getRedditEmbedUrl: (rawUrl: string, theme?: "light" | "dark") => string | null;
@@ -0,0 +1,45 @@
1
+ export const extractRedditPostId = (rawUrl) => {
2
+ const trimmed = rawUrl.trim();
3
+ if (!trimmed) {
4
+ return null;
5
+ }
6
+ try {
7
+ const url = new URL(trimmed);
8
+ const hostname = url.hostname.replace(/^www\./, '');
9
+ const pathParts = url.pathname.split('/').filter(Boolean);
10
+ if (hostname === 'redd.it') {
11
+ return pathParts[0] ?? null;
12
+ }
13
+ const commentsIndex = pathParts.indexOf('comments');
14
+ if (commentsIndex !== -1) {
15
+ return pathParts[commentsIndex + 1] ?? null;
16
+ }
17
+ return null;
18
+ }
19
+ catch {
20
+ return null;
21
+ }
22
+ };
23
+ export const getRedditEmbedUrl = (rawUrl, theme = 'light') => {
24
+ try {
25
+ const url = new URL(rawUrl);
26
+ const hostname = url.hostname.replace(/^www\./, '');
27
+ if (hostname !== 'reddit.com' && hostname !== 'redd.it') {
28
+ return null;
29
+ }
30
+ const id = extractRedditPostId(rawUrl);
31
+ if (!id) {
32
+ return null;
33
+ }
34
+ const pathParts = url.pathname.split('/').filter(Boolean);
35
+ const commentsIndex = pathParts.indexOf('comments');
36
+ if (commentsIndex !== -1 && pathParts.length >= commentsIndex + 2) {
37
+ const embedPath = `/${pathParts.slice(0, commentsIndex + 2).join('/')}/embed`;
38
+ return `https://www.reddit.com${embedPath}?theme=${theme}`;
39
+ }
40
+ return `https://www.reddit.com/comments/${id}/embed?theme=${theme}`;
41
+ }
42
+ catch {
43
+ return null;
44
+ }
45
+ };