Skip to content

Commit bf3aa32

Browse files
authored
feat(sanity): implement youtube transcript intelligence and nightly dataset sync (#696)
- Add transcript document schema with cues, chapters, stats, and metadata - Add transcript reference to content partial inherited by posts, podcasts, pages, lessons, and courses - Implement sync-youtube-transcript Sanity function with YouTube Data API v3 and InnerTube caption fetching - Restrict transcript sync execution to production dataset and enforce YOUTUBE_API_KEY - Implement sync-nightly-dataset scheduled function copying production -> dev nightly at 04:00 UTC - Update sanity.blueprint.ts registering both functions - Update search and shared queries to index and project transcripts
1 parent c24ec3b commit bf3aa32

10 files changed

Lines changed: 4532 additions & 3323 deletions

File tree

‎apps/sanity/extract.json‎

Lines changed: 3732 additions & 3309 deletions
Large diffs are not rendered by default.
Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
import { scheduledEventHandler } from "@sanity/functions";
2+
3+
export const handler = scheduledEventHandler(async ({ context }) => {
4+
console.log("[Sync Nightly Dataset] Starting scheduled dataset sync...");
5+
6+
const projectId =
7+
context.clientOptions?.projectId ||
8+
process.env.SANITY_STUDIO_PROJECT_ID ||
9+
"hfh83o0w";
10+
const token =
11+
context.clientOptions?.token || process.env.SANITY_AUTH_TOKEN;
12+
13+
if (!token) {
14+
throw new Error(
15+
"[Sync Nightly Dataset] Missing Sanity API token (neither context.clientOptions.token nor SANITY_AUTH_TOKEN is present).",
16+
);
17+
}
18+
19+
const apiHost =
20+
context.clientOptions?.apiHost || "https://api.sanity.io";
21+
const apiVersion = "v2025-09-30";
22+
23+
const targetDataset = "dev";
24+
const sourceDataset = "production";
25+
26+
console.log(
27+
`[Sync Nightly Dataset] Resetting dataset "${targetDataset}" from source "${sourceDataset}" for project ${projectId}...`,
28+
);
29+
30+
// 1. Delete existing target dataset (dev) if it exists
31+
const deleteUrl = `${apiHost}/${apiVersion}/projects/${projectId}/datasets/${targetDataset}`;
32+
console.log(`[Sync Nightly Dataset] Deleting existing dataset "${targetDataset}": ${deleteUrl}`);
33+
34+
const deleteResp = await fetch(deleteUrl, {
35+
method: "DELETE",
36+
headers: {
37+
Authorization: `Bearer ${token}`,
38+
},
39+
});
40+
41+
if (deleteResp.ok) {
42+
console.log(
43+
`[Sync Nightly Dataset] Target dataset "${targetDataset}" deleted successfully (HTTP ${deleteResp.status}).`,
44+
);
45+
} else if (deleteResp.status === 404) {
46+
console.log(
47+
`[Sync Nightly Dataset] Target dataset "${targetDataset}" does not exist yet (HTTP 404). Proceeding to clone.`,
48+
);
49+
} else {
50+
const deleteError = await deleteResp.text();
51+
console.warn(
52+
`[Sync Nightly Dataset] Notice when deleting dataset "${targetDataset}": HTTP ${deleteResp.status} — ${deleteError}`,
53+
);
54+
}
55+
56+
// 2. Clone source dataset (production) to target (dev) via Sanity Enterprise Copy API
57+
const copyUrl = `${apiHost}/${apiVersion}/projects/${projectId}/datasets/${sourceDataset}/copy`;
58+
console.log(
59+
`[Sync Nightly Dataset] Copying "${sourceDataset}" -> "${targetDataset}": ${copyUrl}`,
60+
);
61+
62+
const copyResp = await fetch(copyUrl, {
63+
method: "PUT",
64+
headers: {
65+
Authorization: `Bearer ${token}`,
66+
"Content-Type": "application/json",
67+
},
68+
body: JSON.stringify({
69+
targetDataset,
70+
skipHistory: true,
71+
}),
72+
});
73+
74+
if (!copyResp.ok) {
75+
const errorText = await copyResp.text();
76+
throw new Error(
77+
`[Sync Nightly Dataset] Failed to initiate dataset copy from "${sourceDataset}" to "${targetDataset}": HTTP ${copyResp.status} — ${errorText}`,
78+
);
79+
}
80+
81+
const copyResult = await copyResp.json().catch(() => ({}));
82+
console.log(
83+
`[Sync Nightly Dataset] Successfully initiated dataset copy from "${sourceDataset}" to "${targetDataset}":`,
84+
copyResult,
85+
);
86+
});

0 commit comments

Comments
 (0)