@@ -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+
1887export 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