@@ -16,72 +16,93 @@ const sanityWriteClient = createClient({
1616
1717
1818
19- async function processSingleTask ( ) {
19+
20+ async function processBatchTasks ( ) {
21+ // Fetch up to 50 pending tasks
2022 let tasks = await sanityWriteClient . fetch (
21- `*[_type == "youtubeUpdateTask" && ( status == "pending" || status == "inProgress") ]| order(lastChecked asc)[0...1 ]{ _id, targetDoc->{_id, _type, youtube}, status }`
23+ `*[_type == "youtubeUpdateTask" && status == "pending"]| order(lastChecked asc)[0...50 ]{ _id, targetDoc->{_id, _type, youtube}, status }`
2224 ) ;
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 ( ) } )
25+ if ( ! tasks || tasks . length === 0 ) return { processed : 0 } ;
26+
27+ // Prepare video IDs and map taskId to docId
28+ const validTasks = [ ] ;
29+ const errorTasks = [ ] ;
30+ for ( const task of tasks ) {
31+ const { _id : taskId , targetDoc } = task ;
32+ if ( ! targetDoc || ! targetDoc . youtube ) {
33+ errorTasks . push ( { taskId, error : "Missing YouTube field on targetDoc" } ) ;
34+ continue ;
35+ }
36+ const id = youtubeParser ( targetDoc . youtube ) ;
37+ if ( ! id ) {
38+ errorTasks . push ( { taskId, error : "Invalid YouTube URL" } ) ;
39+ continue ;
40+ }
41+ validTasks . push ( { taskId, docId : targetDoc . _id , youtubeId : id } ) ;
42+ }
43+
44+ // Mark all valid tasks as inProgress
45+ for ( const t of validTasks ) {
46+ await sanityWriteClient . patch ( t . taskId )
47+ . set ( { status : "inProgress" , lastChecked : new Date ( ) . toISOString ( ) } )
2948 . commit ( ) ;
30- return false ;
3149 }
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 ( ) } )
50+
51+ // Mark all error tasks as error
52+ for ( const t of errorTasks ) {
53+ await sanityWriteClient . patch ( t . taskId )
54+ . set ( { status : "error" , errorMessage : t . error , lastChecked : new Date ( ) . toISOString ( ) } )
4155 . commit ( ) ;
42- return false ;
4356 }
4457
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 )
58+ if ( validTasks . length === 0 ) return { processed : 0 , errors : errorTasks . length } ;
59+
60+ // Batch YouTube API call
61+ const ids = validTasks . map ( t => t . youtubeId ) . join ( "," ) ;
62+ console . log ( "[YOUTUBE] Fetching stats for IDs:" , ids ) ;
63+ const videoResp = await fetch (
64+ `https://www.googleapis.com/youtube/v3/videos?id=${ ids } &key=${ process . env . YOUTUBE_API_KEY } &fields=items(id,statistics)&part=statistics` ,
65+ ) ;
66+ const json = await videoResp . json ( ) ;
67+ if ( videoResp . status !== 200 ) {
68+ // Mark all as error
69+ for ( const t of validTasks ) {
70+ await sanityWriteClient . patch ( t . taskId )
5271 . set ( { status : "error" , errorMessage : JSON . stringify ( json ) , lastChecked : new Date ( ) . toISOString ( ) } )
5372 . commit ( ) ;
54- return false ;
5573 }
56- const statistics = json ?. items ?. at ( 0 ) ?. statistics ;
74+ return { processed : 0 , errors : validTasks . length } ;
75+ }
76+ const statsMap = new Map ( ) ;
77+ for ( const item of json ?. items || [ ] ) {
78+ statsMap . set ( item . id , item . statistics ) ;
79+ }
80+
81+ let completed = 0 ;
82+ for ( const t of validTasks ) {
83+ const statistics = statsMap . get ( t . youtubeId ) ;
5784 if ( ! statistics ) {
58- await sanityWriteClient . patch ( taskId )
85+ await sanityWriteClient . patch ( t . taskId )
5986 . set ( { status : "error" , errorMessage : "No statistics found" , lastChecked : new Date ( ) . toISOString ( ) } )
6087 . commit ( ) ;
61- return false ;
88+ continue ;
6289 }
63-
6490 // Update target doc with stats
65- await sanityWriteClient . patch ( targetDoc . _id )
91+ await sanityWriteClient . patch ( t . docId )
6692 . set ( {
6793 "statistics.youtube.commentCount" : Number . parseInt ( statistics . commentCount ) ,
6894 "statistics.youtube.favoriteCount" : Number . parseInt ( statistics . favoriteCount ) ,
6995 "statistics.youtube.likeCount" : Number . parseInt ( statistics . likeCount ) ,
7096 "statistics.youtube.viewCount" : Number . parseInt ( statistics . viewCount ) ,
7197 } )
7298 . commit ( ) ;
73-
7499 // Mark task as completed
75- await sanityWriteClient . patch ( taskId )
100+ await sanityWriteClient . patch ( t . taskId )
76101 . set ( { status : "completed" , lastChecked : new Date ( ) . toISOString ( ) , errorMessage : undefined } )
77102 . 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 ;
103+ completed ++ ;
84104 }
105+ return { processed : completed , errors : errorTasks . length + ( validTasks . length - completed ) } ;
85106}
86107
87108export async function POST ( request : NextRequest ) {
@@ -94,7 +115,7 @@ export async function POST(request: NextRequest) {
94115 try {
95116 // Repopulate youtubeUpdateTask queue if empty
96117 let tasks = await sanityWriteClient . fetch (
97- `*[_type == "youtubeUpdateTask" && ( status == "pending" || status == "inProgress") ]| order(lastChecked asc)[0...1]{ _id, targetDoc->{_id, _type, youtube}, status }`
118+ `*[_type == "youtubeUpdateTask" && status == "pending"]| order(lastChecked asc)[0...1]{ _id }`
98119 ) ;
99120 if ( ! tasks || tasks . length === 0 ) {
100121 const posts = await sanityWriteClient . fetch (
@@ -120,27 +141,9 @@ export async function POST(request: NextRequest) {
120141 }
121142 }
122143
123- // Process a single task
124- const didProcess = await processSingleTask ( ) ;
125-
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- }
133-
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- } ) ;
142-
143- return Response . json ( { success : true , didProcess } ) ;
144+ // Process a batch of tasks
145+ const result = await processBatchTasks ( ) ;
146+ return Response . json ( { success : true , ...result } ) ;
144147 } catch ( error ) {
145148 console . error ( "[YOUTUBE] Unexpected error:" , error ) ;
146149 return Response . json ( { success : false , error : String ( error ) } , { status : 500 } ) ;
0 commit comments