@uptechworks/url-preview-field 0.1.0 → 0.2.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.
- package/dist/_chunks/{UrlFieldInput-BhujW-Fo.js → UrlFieldInput-BJqXq4bW.js} +89 -4
- package/dist/_chunks/{UrlFieldInput-D0OIBuvW.mjs → UrlFieldInput-C_X4HT42.mjs} +89 -4
- package/dist/admin/index.js +1 -1
- package/dist/admin/index.mjs +1 -1
- package/dist/server/index.js +76 -0
- package/dist/server/index.mjs +76 -0
- package/dist/server/src/index.d.ts +59 -0
- package/dist/server/src/services/index.d.ts +59 -0
- package/dist/server/src/services/service.d.ts +59 -0
- package/package.json +1 -1
|
@@ -24,6 +24,24 @@ function _interopNamespace(e) {
|
|
|
24
24
|
return Object.freeze(n);
|
|
25
25
|
}
|
|
26
26
|
const React__namespace = /* @__PURE__ */ _interopNamespace(React);
|
|
27
|
+
const JW_PLAYER_MEDIA_ID_PATTERN = /^[A-Za-z0-9]{8}$/;
|
|
28
|
+
const buildJwPlayerManifestUrl = (mediaId) => `https://cdn.jwplayer.com/manifests/${mediaId}.m3u8`;
|
|
29
|
+
const formatSegmentDate = (iso) => {
|
|
30
|
+
const date = new Date(iso);
|
|
31
|
+
const datePart = new Intl.DateTimeFormat("en-US", {
|
|
32
|
+
month: "long",
|
|
33
|
+
day: "numeric",
|
|
34
|
+
year: "numeric",
|
|
35
|
+
timeZone: "America/Los_Angeles"
|
|
36
|
+
}).format(date);
|
|
37
|
+
const timePart = new Intl.DateTimeFormat("en-US", {
|
|
38
|
+
hour: "numeric",
|
|
39
|
+
minute: "2-digit",
|
|
40
|
+
hour12: true,
|
|
41
|
+
timeZone: "America/Los_Angeles"
|
|
42
|
+
}).format(date);
|
|
43
|
+
return `${datePart} — ${timePart}`;
|
|
44
|
+
};
|
|
27
45
|
const PreviewCard = styledComponents.styled(designSystem.Box)`
|
|
28
46
|
border: 1px solid #e4e7eb;
|
|
29
47
|
border-radius: 4px;
|
|
@@ -61,6 +79,47 @@ const PreviewLink = styledComponents.styled.a`
|
|
|
61
79
|
margin-top: 8px;
|
|
62
80
|
display: inline-block;
|
|
63
81
|
`;
|
|
82
|
+
const SegmentHeader = styledComponents.styled.div`
|
|
83
|
+
display: flex;
|
|
84
|
+
align-items: center;
|
|
85
|
+
gap: 12px;
|
|
86
|
+
margin-bottom: 12px;
|
|
87
|
+
`;
|
|
88
|
+
const SegmentIcon = styledComponents.styled.img`
|
|
89
|
+
width: 40px;
|
|
90
|
+
height: 40px;
|
|
91
|
+
object-fit: cover;
|
|
92
|
+
border-radius: 4px;
|
|
93
|
+
flex-shrink: 0;
|
|
94
|
+
`;
|
|
95
|
+
const SegmentTitle = styledComponents.styled(designSystem.Typography)`
|
|
96
|
+
font-size: 1.25rem;
|
|
97
|
+
font-weight: bold;
|
|
98
|
+
color: #222;
|
|
99
|
+
`;
|
|
100
|
+
const SegmentBulletList = styledComponents.styled.ul`
|
|
101
|
+
margin: 0 0 12px 0;
|
|
102
|
+
padding-left: 20px;
|
|
103
|
+
color: #333;
|
|
104
|
+
|
|
105
|
+
li {
|
|
106
|
+
margin-bottom: 6px;
|
|
107
|
+
font-size: 0.9rem;
|
|
108
|
+
line-height: 1.4;
|
|
109
|
+
}
|
|
110
|
+
`;
|
|
111
|
+
const SegmentReadMoreLink = styledComponents.styled.a`
|
|
112
|
+
color: #b8272c;
|
|
113
|
+
text-decoration: underline;
|
|
114
|
+
font-weight: 600;
|
|
115
|
+
font-size: 0.95rem;
|
|
116
|
+
display: inline-block;
|
|
117
|
+
`;
|
|
118
|
+
const SegmentFooter = styledComponents.styled.div`
|
|
119
|
+
margin-top: 12px;
|
|
120
|
+
color: #666;
|
|
121
|
+
font-size: 0.8rem;
|
|
122
|
+
`;
|
|
64
123
|
const UrlFieldInput = React__namespace.forwardRef(
|
|
65
124
|
({ hint, disabled, labelAction, label, name, required, attribute, onChange, error, ...props }, forwardedRef) => {
|
|
66
125
|
const { value } = admin.useField(name);
|
|
@@ -101,12 +160,20 @@ const UrlFieldInput = React__namespace.forwardRef(
|
|
|
101
160
|
};
|
|
102
161
|
React.useEffect(() => {
|
|
103
162
|
const timeoutId = setTimeout(() => {
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
} else {
|
|
163
|
+
const trimmedValue = inputValue.trim();
|
|
164
|
+
if (!trimmedValue) {
|
|
107
165
|
setMetadata(null);
|
|
108
166
|
setFetchError(null);
|
|
167
|
+
return;
|
|
168
|
+
}
|
|
169
|
+
if (JW_PLAYER_MEDIA_ID_PATTERN.test(trimmedValue)) {
|
|
170
|
+
const canonicalUrl = buildJwPlayerManifestUrl(trimmedValue);
|
|
171
|
+
setInputValue(canonicalUrl);
|
|
172
|
+
onChange?.({ target: { name, value: canonicalUrl } });
|
|
173
|
+
fetchUrlMetadata(canonicalUrl);
|
|
174
|
+
return;
|
|
109
175
|
}
|
|
176
|
+
fetchUrlMetadata(trimmedValue);
|
|
110
177
|
}, 1e3);
|
|
111
178
|
return () => clearTimeout(timeoutId);
|
|
112
179
|
}, [inputValue]);
|
|
@@ -137,7 +204,25 @@ const UrlFieldInput = React__namespace.forwardRef(
|
|
|
137
204
|
/* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "pi", children: "Fetching preview..." })
|
|
138
205
|
] }),
|
|
139
206
|
fetchError && /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "pi", textColor: "danger600", children: fetchError }),
|
|
140
|
-
metadata && !loading && !fetchError &&
|
|
207
|
+
metadata && !loading && !fetchError && metadata.bullets && metadata.bullets.length > 0 && /* @__PURE__ */ jsxRuntime.jsxs(PreviewCard, { children: [
|
|
208
|
+
/* @__PURE__ */ jsxRuntime.jsxs(SegmentHeader, { children: [
|
|
209
|
+
metadata.image && /* @__PURE__ */ jsxRuntime.jsx(
|
|
210
|
+
SegmentIcon,
|
|
211
|
+
{
|
|
212
|
+
src: metadata.image,
|
|
213
|
+
alt: metadata.showTitle,
|
|
214
|
+
onError: (e) => {
|
|
215
|
+
e.currentTarget.style.display = "none";
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
),
|
|
219
|
+
metadata.title && /* @__PURE__ */ jsxRuntime.jsx(SegmentTitle, { as: "div", children: metadata.title })
|
|
220
|
+
] }),
|
|
221
|
+
/* @__PURE__ */ jsxRuntime.jsx(SegmentBulletList, { children: metadata.bullets.map((bullet, index) => /* @__PURE__ */ jsxRuntime.jsx("li", { children: bullet }, index)) }),
|
|
222
|
+
metadata.url && /* @__PURE__ */ jsxRuntime.jsx(SegmentReadMoreLink, { href: metadata.url, target: "_blank", rel: "noopener noreferrer", children: "Read more" }),
|
|
223
|
+
metadata.publishedAt && /* @__PURE__ */ jsxRuntime.jsx(SegmentFooter, { children: formatSegmentDate(metadata.publishedAt) })
|
|
224
|
+
] }),
|
|
225
|
+
metadata && !loading && !fetchError && !(metadata.bullets && metadata.bullets.length > 0) && /* @__PURE__ */ jsxRuntime.jsx(PreviewCard, { children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { direction: "column", gap: 3, alignItems: "flex-start", children: [
|
|
141
226
|
/* @__PURE__ */ jsxRuntime.jsxs(UrlPreview, { children: [
|
|
142
227
|
/* @__PURE__ */ jsxRuntime.jsx(icons.Globe, { width: "12", height: "12" }),
|
|
143
228
|
/* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "pi", children: metadata.domain })
|
|
@@ -5,6 +5,24 @@ import { Field, Flex, Loader, Typography, Box } from "@strapi/design-system";
|
|
|
5
5
|
import { Globe } from "@strapi/icons";
|
|
6
6
|
import { useField } from "@strapi/strapi/admin";
|
|
7
7
|
import { styled } from "styled-components";
|
|
8
|
+
const JW_PLAYER_MEDIA_ID_PATTERN = /^[A-Za-z0-9]{8}$/;
|
|
9
|
+
const buildJwPlayerManifestUrl = (mediaId) => `https://cdn.jwplayer.com/manifests/${mediaId}.m3u8`;
|
|
10
|
+
const formatSegmentDate = (iso) => {
|
|
11
|
+
const date = new Date(iso);
|
|
12
|
+
const datePart = new Intl.DateTimeFormat("en-US", {
|
|
13
|
+
month: "long",
|
|
14
|
+
day: "numeric",
|
|
15
|
+
year: "numeric",
|
|
16
|
+
timeZone: "America/Los_Angeles"
|
|
17
|
+
}).format(date);
|
|
18
|
+
const timePart = new Intl.DateTimeFormat("en-US", {
|
|
19
|
+
hour: "numeric",
|
|
20
|
+
minute: "2-digit",
|
|
21
|
+
hour12: true,
|
|
22
|
+
timeZone: "America/Los_Angeles"
|
|
23
|
+
}).format(date);
|
|
24
|
+
return `${datePart} — ${timePart}`;
|
|
25
|
+
};
|
|
8
26
|
const PreviewCard = styled(Box)`
|
|
9
27
|
border: 1px solid #e4e7eb;
|
|
10
28
|
border-radius: 4px;
|
|
@@ -42,6 +60,47 @@ const PreviewLink = styled.a`
|
|
|
42
60
|
margin-top: 8px;
|
|
43
61
|
display: inline-block;
|
|
44
62
|
`;
|
|
63
|
+
const SegmentHeader = styled.div`
|
|
64
|
+
display: flex;
|
|
65
|
+
align-items: center;
|
|
66
|
+
gap: 12px;
|
|
67
|
+
margin-bottom: 12px;
|
|
68
|
+
`;
|
|
69
|
+
const SegmentIcon = styled.img`
|
|
70
|
+
width: 40px;
|
|
71
|
+
height: 40px;
|
|
72
|
+
object-fit: cover;
|
|
73
|
+
border-radius: 4px;
|
|
74
|
+
flex-shrink: 0;
|
|
75
|
+
`;
|
|
76
|
+
const SegmentTitle = styled(Typography)`
|
|
77
|
+
font-size: 1.25rem;
|
|
78
|
+
font-weight: bold;
|
|
79
|
+
color: #222;
|
|
80
|
+
`;
|
|
81
|
+
const SegmentBulletList = styled.ul`
|
|
82
|
+
margin: 0 0 12px 0;
|
|
83
|
+
padding-left: 20px;
|
|
84
|
+
color: #333;
|
|
85
|
+
|
|
86
|
+
li {
|
|
87
|
+
margin-bottom: 6px;
|
|
88
|
+
font-size: 0.9rem;
|
|
89
|
+
line-height: 1.4;
|
|
90
|
+
}
|
|
91
|
+
`;
|
|
92
|
+
const SegmentReadMoreLink = styled.a`
|
|
93
|
+
color: #b8272c;
|
|
94
|
+
text-decoration: underline;
|
|
95
|
+
font-weight: 600;
|
|
96
|
+
font-size: 0.95rem;
|
|
97
|
+
display: inline-block;
|
|
98
|
+
`;
|
|
99
|
+
const SegmentFooter = styled.div`
|
|
100
|
+
margin-top: 12px;
|
|
101
|
+
color: #666;
|
|
102
|
+
font-size: 0.8rem;
|
|
103
|
+
`;
|
|
45
104
|
const UrlFieldInput = React.forwardRef(
|
|
46
105
|
({ hint, disabled, labelAction, label, name, required, attribute, onChange, error, ...props }, forwardedRef) => {
|
|
47
106
|
const { value } = useField(name);
|
|
@@ -82,12 +141,20 @@ const UrlFieldInput = React.forwardRef(
|
|
|
82
141
|
};
|
|
83
142
|
useEffect(() => {
|
|
84
143
|
const timeoutId = setTimeout(() => {
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
} else {
|
|
144
|
+
const trimmedValue = inputValue.trim();
|
|
145
|
+
if (!trimmedValue) {
|
|
88
146
|
setMetadata(null);
|
|
89
147
|
setFetchError(null);
|
|
148
|
+
return;
|
|
149
|
+
}
|
|
150
|
+
if (JW_PLAYER_MEDIA_ID_PATTERN.test(trimmedValue)) {
|
|
151
|
+
const canonicalUrl = buildJwPlayerManifestUrl(trimmedValue);
|
|
152
|
+
setInputValue(canonicalUrl);
|
|
153
|
+
onChange?.({ target: { name, value: canonicalUrl } });
|
|
154
|
+
fetchUrlMetadata(canonicalUrl);
|
|
155
|
+
return;
|
|
90
156
|
}
|
|
157
|
+
fetchUrlMetadata(trimmedValue);
|
|
91
158
|
}, 1e3);
|
|
92
159
|
return () => clearTimeout(timeoutId);
|
|
93
160
|
}, [inputValue]);
|
|
@@ -118,7 +185,25 @@ const UrlFieldInput = React.forwardRef(
|
|
|
118
185
|
/* @__PURE__ */ jsx(Typography, { variant: "pi", children: "Fetching preview..." })
|
|
119
186
|
] }),
|
|
120
187
|
fetchError && /* @__PURE__ */ jsx(Typography, { variant: "pi", textColor: "danger600", children: fetchError }),
|
|
121
|
-
metadata && !loading && !fetchError &&
|
|
188
|
+
metadata && !loading && !fetchError && metadata.bullets && metadata.bullets.length > 0 && /* @__PURE__ */ jsxs(PreviewCard, { children: [
|
|
189
|
+
/* @__PURE__ */ jsxs(SegmentHeader, { children: [
|
|
190
|
+
metadata.image && /* @__PURE__ */ jsx(
|
|
191
|
+
SegmentIcon,
|
|
192
|
+
{
|
|
193
|
+
src: metadata.image,
|
|
194
|
+
alt: metadata.showTitle,
|
|
195
|
+
onError: (e) => {
|
|
196
|
+
e.currentTarget.style.display = "none";
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
),
|
|
200
|
+
metadata.title && /* @__PURE__ */ jsx(SegmentTitle, { as: "div", children: metadata.title })
|
|
201
|
+
] }),
|
|
202
|
+
/* @__PURE__ */ jsx(SegmentBulletList, { children: metadata.bullets.map((bullet, index) => /* @__PURE__ */ jsx("li", { children: bullet }, index)) }),
|
|
203
|
+
metadata.url && /* @__PURE__ */ jsx(SegmentReadMoreLink, { href: metadata.url, target: "_blank", rel: "noopener noreferrer", children: "Read more" }),
|
|
204
|
+
metadata.publishedAt && /* @__PURE__ */ jsx(SegmentFooter, { children: formatSegmentDate(metadata.publishedAt) })
|
|
205
|
+
] }),
|
|
206
|
+
metadata && !loading && !fetchError && !(metadata.bullets && metadata.bullets.length > 0) && /* @__PURE__ */ jsx(PreviewCard, { children: /* @__PURE__ */ jsxs(Flex, { direction: "column", gap: 3, alignItems: "flex-start", children: [
|
|
122
207
|
/* @__PURE__ */ jsxs(UrlPreview, { children: [
|
|
123
208
|
/* @__PURE__ */ jsx(Globe, { width: "12", height: "12" }),
|
|
124
209
|
/* @__PURE__ */ jsx(Typography, { variant: "pi", children: metadata.domain })
|
package/dist/admin/index.js
CHANGED
|
@@ -64,7 +64,7 @@ const index = {
|
|
|
64
64
|
},
|
|
65
65
|
icon: UrlFieldIcon,
|
|
66
66
|
components: {
|
|
67
|
-
Input: async () => Promise.resolve().then(() => require("../_chunks/UrlFieldInput-
|
|
67
|
+
Input: async () => Promise.resolve().then(() => require("../_chunks/UrlFieldInput-BJqXq4bW.js")).then((module2) => ({
|
|
68
68
|
default: module2.UrlFieldInput
|
|
69
69
|
}))
|
|
70
70
|
}
|
package/dist/admin/index.mjs
CHANGED
|
@@ -63,7 +63,7 @@ const index = {
|
|
|
63
63
|
},
|
|
64
64
|
icon: UrlFieldIcon,
|
|
65
65
|
components: {
|
|
66
|
-
Input: async () => import("../_chunks/UrlFieldInput-
|
|
66
|
+
Input: async () => import("../_chunks/UrlFieldInput-C_X4HT42.mjs").then((module) => ({
|
|
67
67
|
default: module.UrlFieldInput
|
|
68
68
|
}))
|
|
69
69
|
}
|
package/dist/server/index.js
CHANGED
|
@@ -76,11 +76,87 @@ const routes = [
|
|
|
76
76
|
}
|
|
77
77
|
}
|
|
78
78
|
];
|
|
79
|
+
const JW_PLAYER_MEDIA_ID_PATTERN = /^[A-Za-z0-9]{8}$/;
|
|
80
|
+
const JW_PLAYER_MANIFEST_PATTERN = /cdn\.jwplayer\.com\/manifests\/([A-Za-z0-9]{8})\.m3u8/;
|
|
81
|
+
const buildJwPlayerManifestUrl = (mediaId) => `https://cdn.jwplayer.com/manifests/${mediaId}.m3u8`;
|
|
82
|
+
const KQED_VIDEO_API = "https://mobile.api.kqed.org/videos/vertical";
|
|
83
|
+
const KQED_SEGMENT_URL_PATTERN = /kqed\.org\/radio\/search\/([^/?#]+)/;
|
|
84
|
+
const KQED_SEGMENT_API = "https://mobile.api.kqed.org/chat/segments";
|
|
79
85
|
const service = ({ strapi }) => ({
|
|
80
86
|
getWelcomeMessage() {
|
|
81
87
|
return "Welcome to Strapi 🚀";
|
|
82
88
|
},
|
|
89
|
+
async getKqedVideoMetadata(url, mediaId) {
|
|
90
|
+
try {
|
|
91
|
+
const response = await fetch__default.default(`${KQED_VIDEO_API}/${mediaId}`);
|
|
92
|
+
if (!response.ok) {
|
|
93
|
+
throw new Error(`Failed to fetch KQED video metadata: ${response.statusText}`);
|
|
94
|
+
}
|
|
95
|
+
const data = await response.json();
|
|
96
|
+
const video = data.verticalVideo;
|
|
97
|
+
if (!video) {
|
|
98
|
+
throw new Error("KQED video API returned no video data");
|
|
99
|
+
}
|
|
100
|
+
const image = video.thumbnails?.find((t) => t.type === "static")?.url || "";
|
|
101
|
+
return {
|
|
102
|
+
title: video.episodeTitle || "",
|
|
103
|
+
image,
|
|
104
|
+
url,
|
|
105
|
+
domain: "kqed.org",
|
|
106
|
+
description: video.episodeDescription || ""
|
|
107
|
+
};
|
|
108
|
+
} catch (err) {
|
|
109
|
+
console.error("Error fetching KQED video metadata:", err);
|
|
110
|
+
return {
|
|
111
|
+
title: "",
|
|
112
|
+
image: "",
|
|
113
|
+
url,
|
|
114
|
+
domain: "",
|
|
115
|
+
error: err.message
|
|
116
|
+
};
|
|
117
|
+
}
|
|
118
|
+
},
|
|
119
|
+
async getKqedSegmentMetadata(url, segmentId) {
|
|
120
|
+
try {
|
|
121
|
+
const response = await fetch__default.default(`${KQED_SEGMENT_API}/${segmentId}`);
|
|
122
|
+
if (!response.ok) {
|
|
123
|
+
throw new Error(`Failed to fetch KQED segment metadata: ${response.statusText}`);
|
|
124
|
+
}
|
|
125
|
+
const data = await response.json();
|
|
126
|
+
const segment = data.segment;
|
|
127
|
+
if (!segment) {
|
|
128
|
+
throw new Error("KQED segment API returned no segment data");
|
|
129
|
+
}
|
|
130
|
+
return {
|
|
131
|
+
title: segment.summaryTitle || segment.episodeTitle || "",
|
|
132
|
+
bullets: segment.summaryBullets || [],
|
|
133
|
+
image: segment.showImageUrl || "",
|
|
134
|
+
url: segment.shareUrl || url,
|
|
135
|
+
domain: "kqed.org",
|
|
136
|
+
showTitle: segment.showTitle || "",
|
|
137
|
+
publishedAt: segment.segmentScheduleStart ? new Date(segment.segmentScheduleStart).toISOString() : void 0
|
|
138
|
+
};
|
|
139
|
+
} catch (err) {
|
|
140
|
+
console.error("Error fetching KQED segment metadata:", err);
|
|
141
|
+
return {
|
|
142
|
+
title: "",
|
|
143
|
+
image: "",
|
|
144
|
+
url,
|
|
145
|
+
domain: "",
|
|
146
|
+
error: err.message
|
|
147
|
+
};
|
|
148
|
+
}
|
|
149
|
+
},
|
|
83
150
|
async getUrlMetadata(url) {
|
|
151
|
+
const trimmed = url.trim();
|
|
152
|
+
const segmentMatch = trimmed.match(KQED_SEGMENT_URL_PATTERN);
|
|
153
|
+
if (segmentMatch) {
|
|
154
|
+
return this.getKqedSegmentMetadata(trimmed, segmentMatch[1]);
|
|
155
|
+
}
|
|
156
|
+
const mediaId = trimmed.match(JW_PLAYER_MANIFEST_PATTERN)?.[1] || (JW_PLAYER_MEDIA_ID_PATTERN.test(trimmed) ? trimmed : void 0);
|
|
157
|
+
if (mediaId) {
|
|
158
|
+
return this.getKqedVideoMetadata(buildJwPlayerManifestUrl(mediaId), mediaId);
|
|
159
|
+
}
|
|
84
160
|
try {
|
|
85
161
|
const response = await fetch__default.default(url);
|
|
86
162
|
if (!response.ok) {
|
package/dist/server/index.mjs
CHANGED
|
@@ -55,11 +55,87 @@ const routes = [
|
|
|
55
55
|
}
|
|
56
56
|
}
|
|
57
57
|
];
|
|
58
|
+
const JW_PLAYER_MEDIA_ID_PATTERN = /^[A-Za-z0-9]{8}$/;
|
|
59
|
+
const JW_PLAYER_MANIFEST_PATTERN = /cdn\.jwplayer\.com\/manifests\/([A-Za-z0-9]{8})\.m3u8/;
|
|
60
|
+
const buildJwPlayerManifestUrl = (mediaId) => `https://cdn.jwplayer.com/manifests/${mediaId}.m3u8`;
|
|
61
|
+
const KQED_VIDEO_API = "https://mobile.api.kqed.org/videos/vertical";
|
|
62
|
+
const KQED_SEGMENT_URL_PATTERN = /kqed\.org\/radio\/search\/([^/?#]+)/;
|
|
63
|
+
const KQED_SEGMENT_API = "https://mobile.api.kqed.org/chat/segments";
|
|
58
64
|
const service = ({ strapi }) => ({
|
|
59
65
|
getWelcomeMessage() {
|
|
60
66
|
return "Welcome to Strapi 🚀";
|
|
61
67
|
},
|
|
68
|
+
async getKqedVideoMetadata(url, mediaId) {
|
|
69
|
+
try {
|
|
70
|
+
const response = await fetch(`${KQED_VIDEO_API}/${mediaId}`);
|
|
71
|
+
if (!response.ok) {
|
|
72
|
+
throw new Error(`Failed to fetch KQED video metadata: ${response.statusText}`);
|
|
73
|
+
}
|
|
74
|
+
const data = await response.json();
|
|
75
|
+
const video = data.verticalVideo;
|
|
76
|
+
if (!video) {
|
|
77
|
+
throw new Error("KQED video API returned no video data");
|
|
78
|
+
}
|
|
79
|
+
const image = video.thumbnails?.find((t) => t.type === "static")?.url || "";
|
|
80
|
+
return {
|
|
81
|
+
title: video.episodeTitle || "",
|
|
82
|
+
image,
|
|
83
|
+
url,
|
|
84
|
+
domain: "kqed.org",
|
|
85
|
+
description: video.episodeDescription || ""
|
|
86
|
+
};
|
|
87
|
+
} catch (err) {
|
|
88
|
+
console.error("Error fetching KQED video metadata:", err);
|
|
89
|
+
return {
|
|
90
|
+
title: "",
|
|
91
|
+
image: "",
|
|
92
|
+
url,
|
|
93
|
+
domain: "",
|
|
94
|
+
error: err.message
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
},
|
|
98
|
+
async getKqedSegmentMetadata(url, segmentId) {
|
|
99
|
+
try {
|
|
100
|
+
const response = await fetch(`${KQED_SEGMENT_API}/${segmentId}`);
|
|
101
|
+
if (!response.ok) {
|
|
102
|
+
throw new Error(`Failed to fetch KQED segment metadata: ${response.statusText}`);
|
|
103
|
+
}
|
|
104
|
+
const data = await response.json();
|
|
105
|
+
const segment = data.segment;
|
|
106
|
+
if (!segment) {
|
|
107
|
+
throw new Error("KQED segment API returned no segment data");
|
|
108
|
+
}
|
|
109
|
+
return {
|
|
110
|
+
title: segment.summaryTitle || segment.episodeTitle || "",
|
|
111
|
+
bullets: segment.summaryBullets || [],
|
|
112
|
+
image: segment.showImageUrl || "",
|
|
113
|
+
url: segment.shareUrl || url,
|
|
114
|
+
domain: "kqed.org",
|
|
115
|
+
showTitle: segment.showTitle || "",
|
|
116
|
+
publishedAt: segment.segmentScheduleStart ? new Date(segment.segmentScheduleStart).toISOString() : void 0
|
|
117
|
+
};
|
|
118
|
+
} catch (err) {
|
|
119
|
+
console.error("Error fetching KQED segment metadata:", err);
|
|
120
|
+
return {
|
|
121
|
+
title: "",
|
|
122
|
+
image: "",
|
|
123
|
+
url,
|
|
124
|
+
domain: "",
|
|
125
|
+
error: err.message
|
|
126
|
+
};
|
|
127
|
+
}
|
|
128
|
+
},
|
|
62
129
|
async getUrlMetadata(url) {
|
|
130
|
+
const trimmed = url.trim();
|
|
131
|
+
const segmentMatch = trimmed.match(KQED_SEGMENT_URL_PATTERN);
|
|
132
|
+
if (segmentMatch) {
|
|
133
|
+
return this.getKqedSegmentMetadata(trimmed, segmentMatch[1]);
|
|
134
|
+
}
|
|
135
|
+
const mediaId = trimmed.match(JW_PLAYER_MANIFEST_PATTERN)?.[1] || (JW_PLAYER_MEDIA_ID_PATTERN.test(trimmed) ? trimmed : void 0);
|
|
136
|
+
if (mediaId) {
|
|
137
|
+
return this.getKqedVideoMetadata(buildJwPlayerManifestUrl(mediaId), mediaId);
|
|
138
|
+
}
|
|
63
139
|
try {
|
|
64
140
|
const response = await fetch(url);
|
|
65
141
|
if (!response.ok) {
|
|
@@ -45,11 +45,62 @@ declare const _default: {
|
|
|
45
45
|
strapi: import("@strapi/types/dist/core").Strapi;
|
|
46
46
|
}) => {
|
|
47
47
|
getWelcomeMessage(): string;
|
|
48
|
+
getKqedVideoMetadata(url: string, mediaId: string): Promise<{
|
|
49
|
+
title: string;
|
|
50
|
+
image: string;
|
|
51
|
+
url: string;
|
|
52
|
+
domain: string;
|
|
53
|
+
description: string;
|
|
54
|
+
error?: undefined;
|
|
55
|
+
} | {
|
|
56
|
+
title: string;
|
|
57
|
+
image: string;
|
|
58
|
+
url: string;
|
|
59
|
+
domain: string;
|
|
60
|
+
error: string;
|
|
61
|
+
description?: undefined;
|
|
62
|
+
}>;
|
|
63
|
+
getKqedSegmentMetadata(url: string, segmentId: string): Promise<{
|
|
64
|
+
title: string;
|
|
65
|
+
bullets: string[];
|
|
66
|
+
image: string;
|
|
67
|
+
url: string;
|
|
68
|
+
domain: string;
|
|
69
|
+
showTitle: string;
|
|
70
|
+
publishedAt: string;
|
|
71
|
+
error?: undefined;
|
|
72
|
+
} | {
|
|
73
|
+
title: string;
|
|
74
|
+
image: string;
|
|
75
|
+
url: string;
|
|
76
|
+
domain: string;
|
|
77
|
+
error: string;
|
|
78
|
+
bullets?: undefined;
|
|
79
|
+
showTitle?: undefined;
|
|
80
|
+
publishedAt?: undefined;
|
|
81
|
+
}>;
|
|
48
82
|
getUrlMetadata(url: string): Promise<{
|
|
49
83
|
title: string;
|
|
50
84
|
image: string;
|
|
51
85
|
url: string;
|
|
52
86
|
domain: string;
|
|
87
|
+
description: string;
|
|
88
|
+
error?: undefined;
|
|
89
|
+
} | {
|
|
90
|
+
title: string;
|
|
91
|
+
image: string;
|
|
92
|
+
url: string;
|
|
93
|
+
domain: string;
|
|
94
|
+
error: string;
|
|
95
|
+
description?: undefined;
|
|
96
|
+
} | {
|
|
97
|
+
title: string;
|
|
98
|
+
bullets: string[];
|
|
99
|
+
image: string;
|
|
100
|
+
url: string;
|
|
101
|
+
domain: string;
|
|
102
|
+
showTitle: string;
|
|
103
|
+
publishedAt: string;
|
|
53
104
|
error?: undefined;
|
|
54
105
|
} | {
|
|
55
106
|
title: string;
|
|
@@ -57,6 +108,14 @@ declare const _default: {
|
|
|
57
108
|
url: string;
|
|
58
109
|
domain: string;
|
|
59
110
|
error: string;
|
|
111
|
+
bullets?: undefined;
|
|
112
|
+
showTitle?: undefined;
|
|
113
|
+
publishedAt?: undefined;
|
|
114
|
+
} | {
|
|
115
|
+
title: string;
|
|
116
|
+
image: string;
|
|
117
|
+
url: string;
|
|
118
|
+
domain: string;
|
|
60
119
|
}>;
|
|
61
120
|
};
|
|
62
121
|
};
|
|
@@ -3,11 +3,62 @@ declare const _default: {
|
|
|
3
3
|
strapi: import("@strapi/types/dist/core").Strapi;
|
|
4
4
|
}) => {
|
|
5
5
|
getWelcomeMessage(): string;
|
|
6
|
+
getKqedVideoMetadata(url: string, mediaId: string): Promise<{
|
|
7
|
+
title: string;
|
|
8
|
+
image: string;
|
|
9
|
+
url: string;
|
|
10
|
+
domain: string;
|
|
11
|
+
description: string;
|
|
12
|
+
error?: undefined;
|
|
13
|
+
} | {
|
|
14
|
+
title: string;
|
|
15
|
+
image: string;
|
|
16
|
+
url: string;
|
|
17
|
+
domain: string;
|
|
18
|
+
error: string;
|
|
19
|
+
description?: undefined;
|
|
20
|
+
}>;
|
|
21
|
+
getKqedSegmentMetadata(url: string, segmentId: string): Promise<{
|
|
22
|
+
title: string;
|
|
23
|
+
bullets: string[];
|
|
24
|
+
image: string;
|
|
25
|
+
url: string;
|
|
26
|
+
domain: string;
|
|
27
|
+
showTitle: string;
|
|
28
|
+
publishedAt: string;
|
|
29
|
+
error?: undefined;
|
|
30
|
+
} | {
|
|
31
|
+
title: string;
|
|
32
|
+
image: string;
|
|
33
|
+
url: string;
|
|
34
|
+
domain: string;
|
|
35
|
+
error: string;
|
|
36
|
+
bullets?: undefined;
|
|
37
|
+
showTitle?: undefined;
|
|
38
|
+
publishedAt?: undefined;
|
|
39
|
+
}>;
|
|
6
40
|
getUrlMetadata(url: string): Promise<{
|
|
7
41
|
title: string;
|
|
8
42
|
image: string;
|
|
9
43
|
url: string;
|
|
10
44
|
domain: string;
|
|
45
|
+
description: string;
|
|
46
|
+
error?: undefined;
|
|
47
|
+
} | {
|
|
48
|
+
title: string;
|
|
49
|
+
image: string;
|
|
50
|
+
url: string;
|
|
51
|
+
domain: string;
|
|
52
|
+
error: string;
|
|
53
|
+
description?: undefined;
|
|
54
|
+
} | {
|
|
55
|
+
title: string;
|
|
56
|
+
bullets: string[];
|
|
57
|
+
image: string;
|
|
58
|
+
url: string;
|
|
59
|
+
domain: string;
|
|
60
|
+
showTitle: string;
|
|
61
|
+
publishedAt: string;
|
|
11
62
|
error?: undefined;
|
|
12
63
|
} | {
|
|
13
64
|
title: string;
|
|
@@ -15,6 +66,14 @@ declare const _default: {
|
|
|
15
66
|
url: string;
|
|
16
67
|
domain: string;
|
|
17
68
|
error: string;
|
|
69
|
+
bullets?: undefined;
|
|
70
|
+
showTitle?: undefined;
|
|
71
|
+
publishedAt?: undefined;
|
|
72
|
+
} | {
|
|
73
|
+
title: string;
|
|
74
|
+
image: string;
|
|
75
|
+
url: string;
|
|
76
|
+
domain: string;
|
|
18
77
|
}>;
|
|
19
78
|
};
|
|
20
79
|
};
|
|
@@ -3,11 +3,62 @@ declare const service: ({ strapi }: {
|
|
|
3
3
|
strapi: Core.Strapi;
|
|
4
4
|
}) => {
|
|
5
5
|
getWelcomeMessage(): string;
|
|
6
|
+
getKqedVideoMetadata(url: string, mediaId: string): Promise<{
|
|
7
|
+
title: string;
|
|
8
|
+
image: string;
|
|
9
|
+
url: string;
|
|
10
|
+
domain: string;
|
|
11
|
+
description: string;
|
|
12
|
+
error?: undefined;
|
|
13
|
+
} | {
|
|
14
|
+
title: string;
|
|
15
|
+
image: string;
|
|
16
|
+
url: string;
|
|
17
|
+
domain: string;
|
|
18
|
+
error: string;
|
|
19
|
+
description?: undefined;
|
|
20
|
+
}>;
|
|
21
|
+
getKqedSegmentMetadata(url: string, segmentId: string): Promise<{
|
|
22
|
+
title: string;
|
|
23
|
+
bullets: string[];
|
|
24
|
+
image: string;
|
|
25
|
+
url: string;
|
|
26
|
+
domain: string;
|
|
27
|
+
showTitle: string;
|
|
28
|
+
publishedAt: string;
|
|
29
|
+
error?: undefined;
|
|
30
|
+
} | {
|
|
31
|
+
title: string;
|
|
32
|
+
image: string;
|
|
33
|
+
url: string;
|
|
34
|
+
domain: string;
|
|
35
|
+
error: string;
|
|
36
|
+
bullets?: undefined;
|
|
37
|
+
showTitle?: undefined;
|
|
38
|
+
publishedAt?: undefined;
|
|
39
|
+
}>;
|
|
6
40
|
getUrlMetadata(url: string): Promise<{
|
|
7
41
|
title: string;
|
|
8
42
|
image: string;
|
|
9
43
|
url: string;
|
|
10
44
|
domain: string;
|
|
45
|
+
description: string;
|
|
46
|
+
error?: undefined;
|
|
47
|
+
} | {
|
|
48
|
+
title: string;
|
|
49
|
+
image: string;
|
|
50
|
+
url: string;
|
|
51
|
+
domain: string;
|
|
52
|
+
error: string;
|
|
53
|
+
description?: undefined;
|
|
54
|
+
} | {
|
|
55
|
+
title: string;
|
|
56
|
+
bullets: string[];
|
|
57
|
+
image: string;
|
|
58
|
+
url: string;
|
|
59
|
+
domain: string;
|
|
60
|
+
showTitle: string;
|
|
61
|
+
publishedAt: string;
|
|
11
62
|
error?: undefined;
|
|
12
63
|
} | {
|
|
13
64
|
title: string;
|
|
@@ -15,6 +66,14 @@ declare const service: ({ strapi }: {
|
|
|
15
66
|
url: string;
|
|
16
67
|
domain: string;
|
|
17
68
|
error: string;
|
|
69
|
+
bullets?: undefined;
|
|
70
|
+
showTitle?: undefined;
|
|
71
|
+
publishedAt?: undefined;
|
|
72
|
+
} | {
|
|
73
|
+
title: string;
|
|
74
|
+
image: string;
|
|
75
|
+
url: string;
|
|
76
|
+
domain: string;
|
|
18
77
|
}>;
|
|
19
78
|
};
|
|
20
79
|
export default service;
|
package/package.json
CHANGED