Skip to content

Commit c308423

Browse files
committed
update for single processing every 30
1 parent f5d4b9b commit c308423

2 files changed

Lines changed: 131 additions & 95 deletions

File tree

app/api/cron/route.tsx

Lines changed: 20 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ export const fetchCache = "force-no-store";
33
import { publicURL } from "@/lib/utils";
44
import type { NextRequest } from "next/server";
55

6-
export async function GET(request: NextRequest) {
6+
export function GET(request: NextRequest) {
77
const authHeader = request.headers.get("authorization");
88
if (authHeader !== `Bearer ${process.env.CRON_SECRET}`) {
99
console.error("[CRON] Unauthorized request: invalid authorization header");
@@ -12,34 +12,26 @@ export async function GET(request: NextRequest) {
1212
});
1313
}
1414
try {
15-
let totalUpdated = 0;
16-
let keepGoing = true;
17-
let loopCount = 0;
18-
const maxLoops = 200; // Safety: 200*10 = 2000 videos max per day
19-
while (keepGoing && loopCount < maxLoops) {
20-
const url = `${publicURL()}/api/youtube/views`;
21-
const res = await fetch(url, {
22-
method: "POST",
23-
headers: {
24-
authorization: `Bearer ${process.env.CRON_SECRET}`,
25-
"Cache-Control": "no-cache",
26-
},
15+
const url = `${publicURL()}/api/youtube/views`;
16+
console.log("[CRON] Triggering YouTube views update:", url);
17+
fetch(url, {
18+
method: "POST",
19+
headers: {
20+
authorization: `Bearer ${process.env.CRON_SECRET}`,
21+
"Cache-Control": "no-cache",
22+
},
23+
})
24+
.then((res) => {
25+
if (!res.ok) {
26+
console.error("[CRON] Failed to trigger YouTube views:", res.status);
27+
} else {
28+
console.log("[CRON] Successfully triggered YouTube views update.");
29+
}
30+
})
31+
.catch((err) => {
32+
console.error("[CRON] Error triggering YouTube views:", err);
2733
});
28-
if (!res.ok) {
29-
console.error("[CRON] Failed to trigger YouTube views:", res.status);
30-
break;
31-
}
32-
const json = await res.json();
33-
if (json && json.updatedCount) {
34-
totalUpdated += json.updatedCount;
35-
}
36-
// If no more tasks, stop
37-
if (!json || !json.updatedCount || json.updatedCount === 0) {
38-
keepGoing = false;
39-
}
40-
loopCount++;
41-
}
42-
return Response.json({ success: true, totalUpdated, loops: loopCount });
34+
return Response.json({ success: true });
4335
} catch (err) {
4436
console.error("[CRON] Unexpected error:", err);
4537
return Response.json(

app/api/youtube/views/route.tsx

Lines changed: 111 additions & 67 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,75 @@ const sanityWriteClient = createClient({
1515
});
1616

1717

18+
19+
async function processSingleTask() {
20+
let tasks = await sanityWriteClient.fetch(
21+
`*[_type == "youtubeUpdateTask" && (status == "pending" || status == "inProgress")]| order(lastChecked asc)[0...1]{ _id, targetDoc->{_id, _type, youtube}, status }`
22+
);
23+
if (!tasks || tasks.length === 0) return false;
24+
const task = tasks[0];
25+
const { _id: taskId, targetDoc, status } = task;
26+
if (!targetDoc || !targetDoc.youtube) {
27+
await sanityWriteClient.patch(taskId)
28+
.set({ status: "error", errorMessage: "Missing YouTube field on targetDoc", lastChecked: new Date().toISOString() })
29+
.commit();
30+
return false;
31+
}
32+
// Mark as inProgress
33+
await sanityWriteClient.patch(taskId)
34+
.set({ status: "inProgress", lastChecked: new Date().toISOString() })
35+
.commit();
36+
37+
const id = youtubeParser(targetDoc.youtube);
38+
if (!id) {
39+
await sanityWriteClient.patch(taskId)
40+
.set({ status: "error", errorMessage: "Invalid YouTube URL", lastChecked: new Date().toISOString() })
41+
.commit();
42+
return false;
43+
}
44+
45+
try {
46+
const videoResp = await fetch(
47+
`https://www.googleapis.com/youtube/v3/videos?id=${id}&key=${process.env.YOUTUBE_API_KEY}&fields=items(id,statistics)&part=statistics`,
48+
);
49+
const json = await videoResp.json();
50+
if (videoResp.status !== 200) {
51+
await sanityWriteClient.patch(taskId)
52+
.set({ status: "error", errorMessage: JSON.stringify(json), lastChecked: new Date().toISOString() })
53+
.commit();
54+
return false;
55+
}
56+
const statistics = json?.items?.at(0)?.statistics;
57+
if (!statistics) {
58+
await sanityWriteClient.patch(taskId)
59+
.set({ status: "error", errorMessage: "No statistics found", lastChecked: new Date().toISOString() })
60+
.commit();
61+
return false;
62+
}
63+
64+
// Update target doc with stats
65+
await sanityWriteClient.patch(targetDoc._id)
66+
.set({
67+
"statistics.youtube.commentCount": Number.parseInt(statistics.commentCount),
68+
"statistics.youtube.favoriteCount": Number.parseInt(statistics.favoriteCount),
69+
"statistics.youtube.likeCount": Number.parseInt(statistics.likeCount),
70+
"statistics.youtube.viewCount": Number.parseInt(statistics.viewCount),
71+
})
72+
.commit();
73+
74+
// Mark task as completed
75+
await sanityWriteClient.patch(taskId)
76+
.set({ status: "completed", lastChecked: new Date().toISOString(), errorMessage: undefined })
77+
.commit();
78+
return true;
79+
} catch (err) {
80+
await sanityWriteClient.patch(taskId)
81+
.set({ status: "error", errorMessage: String(err), lastChecked: new Date().toISOString() })
82+
.commit();
83+
return false;
84+
}
85+
}
86+
1887
export async function POST(request: NextRequest) {
1988
const authHeader = request.headers.get("authorization");
2089
if (authHeader !== `Bearer ${process.env.CRON_SECRET}`) {
@@ -23,80 +92,55 @@ export async function POST(request: NextRequest) {
2392
}
2493

2594
try {
26-
// Fetch up to 10 pending/inProgress youtubeUpdateTask docs
27-
const tasks = await sanityWriteClient.fetch(
28-
`*[_type == "youtubeUpdateTask" && (status == "pending" || status == "inProgress")]| order(lastChecked asc)[0...10]{ _id, targetDoc->{_id, _type, youtube}, status }`
95+
// Repopulate youtubeUpdateTask queue if empty
96+
let tasks = await sanityWriteClient.fetch(
97+
`*[_type == "youtubeUpdateTask" && (status == "pending" || status == "inProgress")]| order(lastChecked asc)[0...1]{ _id, targetDoc->{_id, _type, youtube}, status }`
2998
);
30-
3199
if (!tasks || tasks.length === 0) {
32-
const message = `[YOUTUBE] No youtubeUpdateTask docs to process`;
33-
console.log(message);
34-
return Response.json({ success: true, message }, { status: 200 });
100+
const posts = await sanityWriteClient.fetch(
101+
'*[_type == "post" && defined(youtube)]{_id, _type, youtube}'
102+
);
103+
const podcasts = await sanityWriteClient.fetch(
104+
'*[_type == "podcast" && defined(youtube)]{_id, _type, youtube}'
105+
);
106+
const allDocs = [...posts, ...podcasts];
107+
const existingTasks = await sanityWriteClient.fetch(
108+
'*[_type == "youtubeUpdateTask" && defined(targetDoc._ref)]{targetDoc}'
109+
);
110+
const existingIds = new Set(existingTasks.map((t: { targetDoc?: { _ref?: string } }) => t.targetDoc?._ref));
111+
for (const doc of allDocs) {
112+
if (!existingIds.has(doc._id)) {
113+
await sanityWriteClient.create({
114+
_type: "youtubeUpdateTask",
115+
targetDoc: { _type: "reference", _ref: doc._id },
116+
status: "pending",
117+
lastChecked: null,
118+
});
119+
}
120+
}
35121
}
36122

37-
let updatedCount = 0;
38-
for (const task of tasks) {
39-
const { _id: taskId, targetDoc, status } = task;
40-
if (!targetDoc || !targetDoc.youtube) {
41-
await sanityWriteClient.patch(taskId)
42-
.set({ status: "error", errorMessage: "Missing YouTube field on targetDoc", lastChecked: new Date().toISOString() })
43-
.commit();
44-
continue;
45-
}
46-
// Mark as inProgress
47-
await sanityWriteClient.patch(taskId)
48-
.set({ status: "inProgress", lastChecked: new Date().toISOString() })
49-
.commit();
123+
// Process a single task
124+
const didProcess = await processSingleTask();
50125

51-
const id = youtubeParser(targetDoc.youtube);
52-
if (!id) {
53-
await sanityWriteClient.patch(taskId)
54-
.set({ status: "error", errorMessage: "Invalid YouTube URL", lastChecked: new Date().toISOString() })
55-
.commit();
56-
continue;
57-
}
126+
// Wait in a while loop until 30 seconds have passed
127+
const startTime = Date.now();
128+
const maxDuration = 30 * 1000; // 30 seconds
129+
while (Date.now() - startTime < maxDuration) {
130+
// Busy-wait (not recommended for production, but per user request)
131+
console.log('waiting...');
132+
}
58133

59-
try {
60-
const videoResp = await fetch(
61-
`https://www.googleapis.com/youtube/v3/videos?id=${id}&key=${process.env.YOUTUBE_API_KEY}&fields=items(id,statistics)&part=statistics`,
62-
);
63-
const json = await videoResp.json();
64-
if (videoResp.status !== 200) {
65-
await sanityWriteClient.patch(taskId)
66-
.set({ status: "error", errorMessage: JSON.stringify(json), lastChecked: new Date().toISOString() })
67-
.commit();
68-
continue;
69-
}
70-
const statistics = json?.items?.at(0)?.statistics;
71-
if (!statistics) {
72-
await sanityWriteClient.patch(taskId)
73-
.set({ status: "error", errorMessage: "No statistics found", lastChecked: new Date().toISOString() })
74-
.commit();
75-
continue;
76-
}
134+
// Trigger the next batch by calling this API again
135+
fetch(`${publicURL()}/api/youtube/views`, {
136+
method: "POST",
137+
headers: {
138+
authorization: `Bearer ${process.env.CRON_SECRET}`,
139+
"Cache-Control": "no-cache",
140+
},
141+
});
77142

78-
// Update target doc with stats
79-
await sanityWriteClient.patch(targetDoc._id)
80-
.set({
81-
"statistics.youtube.commentCount": Number.parseInt(statistics.commentCount),
82-
"statistics.youtube.favoriteCount": Number.parseInt(statistics.favoriteCount),
83-
"statistics.youtube.likeCount": Number.parseInt(statistics.likeCount),
84-
"statistics.youtube.viewCount": Number.parseInt(statistics.viewCount),
85-
})
86-
.commit();
87-
88-
// Mark task as completed
89-
await sanityWriteClient.patch(taskId)
90-
.set({ status: "completed", lastChecked: new Date().toISOString(), errorMessage: undefined })
91-
.commit();
92-
updatedCount++;
93-
} catch (err) {
94-
await sanityWriteClient.patch(taskId)
95-
.set({ status: "error", errorMessage: String(err), lastChecked: new Date().toISOString() })
96-
.commit();
97-
}
98-
}
99-
return Response.json({ success: true, updatedCount });
143+
return Response.json({ success: true, didProcess });
100144
} catch (error) {
101145
console.error("[YOUTUBE] Unexpected error:", error);
102146
return Response.json({ success: false, error: String(error) }, { status: 500 });

0 commit comments

Comments
 (0)