@growth-labs/cms 0.6.3 → 0.6.5

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.
@@ -24,6 +24,45 @@ export interface ContentDraftFields {
24
24
  metadata?: ContentMetadata
25
25
  }
26
26
 
27
+ export interface MediaSourceReadinessPayload {
28
+ requestedUrl: string
29
+ resolvedUrl: string
30
+ sourceKind: string
31
+ durationSeconds: number | null
32
+ ready: boolean
33
+ blockers: string[]
34
+ }
35
+
36
+ /**
37
+ * Apply an async host measurement only while its exact input is still current.
38
+ * Returning the original object signals that a newer editor change won.
39
+ */
40
+ export function applyMediaSourceReadiness<T extends ContentDraftFields>(
41
+ contentType: ContentType,
42
+ draft: T,
43
+ readiness: MediaSourceReadinessPayload,
44
+ ): T {
45
+ if (contentType === 'video') {
46
+ if (draft.videoUrl.trim() !== readiness.requestedUrl) return draft
47
+ return {
48
+ ...draft,
49
+ videoUrl: readiness.resolvedUrl,
50
+ videoSourceKind: readiness.sourceKind,
51
+ durationSeconds: normalizeMediaDurationSeconds(readiness.durationSeconds),
52
+ } as T
53
+ }
54
+ if (contentType === 'podcast') {
55
+ if ((draft.podcastSourceUrl ?? '').trim() !== readiness.requestedUrl) return draft
56
+ return {
57
+ ...draft,
58
+ podcastSourceUrl: readiness.resolvedUrl,
59
+ podcastSourceKind: readiness.sourceKind,
60
+ durationSeconds: normalizeMediaDurationSeconds(readiness.durationSeconds),
61
+ } as T
62
+ }
63
+ return draft
64
+ }
65
+
27
66
  type MetadataSelectFieldContract = Pick<
28
67
  ContentMetadataSelectField,
29
68
  'key' | 'defaultValue' | 'inferenceMarkerKey'
@@ -83,10 +122,7 @@ export function canCreateContentDraft(
83
122
  return draft.body.trim().length > 0
84
123
  }
85
124
  if (contentType === 'video') {
86
- return (
87
- draft.videoUrl.trim().length > 0 &&
88
- normalizeMediaDurationSeconds(draft.durationSeconds) !== null
89
- )
125
+ return draft.videoUrl.trim().length > 0
90
126
  }
91
127
  if (contentType === 'podcast') {
92
128
  const hasAudio = draft.audioUrl.trim().length > 0
@@ -168,27 +204,35 @@ export function buildContentUpdatePayload(
168
204
  payload.content = {
169
205
  script: '',
170
206
  videoId,
171
- durationSeconds: normalizeMediaDurationSeconds(draft.durationSeconds),
207
+ durationSeconds: sourceUrl ? normalizeMediaDurationSeconds(draft.durationSeconds) : null,
172
208
  ...(thumbnailImageId ? { thumbnailImageId } : {}),
173
209
  ...(sourceUrl
174
210
  ? {
175
211
  processingSourceUrl: sourceUrl,
176
212
  processingSourceKind: draft.videoSourceKind || inferVideoSourceKind(sourceUrl),
177
213
  }
178
- : {}),
214
+ : {
215
+ processingSourceUrl: null,
216
+ processingSourceKind: null,
217
+ }),
179
218
  }
180
219
  } else if (contentType === 'podcast') {
181
220
  const sourceUrl = draft.podcastSourceUrl?.trim()
221
+ const audioR2Key = draft.audioUrl.trim()
182
222
  payload.content = {
183
223
  transcript: '',
184
- audioR2Key: draft.audioUrl,
185
- durationSeconds: normalizeMediaDurationSeconds(draft.durationSeconds),
224
+ audioR2Key,
225
+ durationSeconds:
226
+ sourceUrl || audioR2Key ? normalizeMediaDurationSeconds(draft.durationSeconds) : null,
186
227
  ...(sourceUrl
187
228
  ? {
188
229
  processingSourceUrl: sourceUrl,
189
230
  processingSourceKind: draft.podcastSourceKind || 'https',
190
231
  }
191
- : {}),
232
+ : {
233
+ processingSourceUrl: null,
234
+ processingSourceKind: null,
235
+ }),
192
236
  }
193
237
  }
194
238