@@ -22,6 +22,7 @@ export async function GET(req: Request) {
2222 const { searchParams } = new URL ( req . url ) ;
2323 const url = searchParams . get ( 'url' ) ;
2424 let page = null ;
25+ const MAX_RETRIES = 3 ;
2526
2627 if ( ! url ) {
2728 return NextResponse . json (
@@ -30,93 +31,112 @@ export async function GET(req: Request) {
3031 ) ;
3132 }
3233
33- try {
34- // Get browser instance
35- const browser = await getBrowser ( ) ;
34+ for ( let attempt = 0 ; attempt < MAX_RETRIES ; attempt ++ ) {
35+ try {
36+ logger . info ( `Screenshot attempt ${ attempt + 1 } for ${ url } ` ) ;
37+
38+ // Get browser instance
39+ const browser = await getBrowser ( ) ;
3640
37- // Create a new page
38- page = await browser . newPage ( ) ;
41+ // Create a new page
42+ page = await browser . newPage ( ) ;
3943
4044 // Set viewport to a reasonable size
4145 await page . setViewport ( {
4246 width : 1600 ,
4347 height : 900 ,
4448 } ) ;
4549
46- // Navigate to URL with increased timeout and more reliable wait condition
47- await page . goto ( url , {
48- waitUntil : 'domcontentloaded ' , // Less strict than networkidle0
49- timeout : 60000 , // Increased timeout to 60 seconds
50- } ) ;
50+ // Navigate to URL with increased timeout and more reliable wait condition
51+ await page . goto ( url , {
52+ waitUntil : 'networkidle2 ' , // 更改为等待网络空闲状态,确保页面完全加载
53+ timeout : 90000 , // 增加超时时间到90秒
54+ } ) ;
5155
52- // 等待额外的时间让页面完全渲染
53- await page . waitForTimeout ( 3000 ) ;
56+ // 等待额外的时间让页面完全渲染
57+ await page . waitForTimeout ( 8000 ) ; // 增加等待时间到8秒
5458
55- // 尝试等待页面上的内容加载,如果失败也继续处理
56- try {
57- // 等待页面上可能存在的主要内容元素
58- await Promise . race ( [
59- page . waitForSelector ( 'main' , { timeout : 2000 } ) ,
60- page . waitForSelector ( '#root' , { timeout : 2000 } ) ,
61- page . waitForSelector ( '.app' , { timeout : 2000 } ) ,
62- page . waitForSelector ( 'h1' , { timeout : 2000 } ) ,
63- ] ) ;
64- } catch ( waitError ) {
65- // 忽略等待选择器的错误,继续截图
66- logger . info ( 'Unable to find common page elements, continuing with screenshot' ) ;
67- }
59+ // 尝试等待页面上的内容加载,如果失败也继续处理
60+ try {
61+ // 等待页面上可能存在的主要内容元素
62+ await Promise . race ( [
63+ page . waitForSelector ( 'main' , { timeout : 5000 } ) ,
64+ page . waitForSelector ( '#root' , { timeout : 5000 } ) ,
65+ page . waitForSelector ( '.app' , { timeout : 5000 } ) ,
66+ page . waitForSelector ( 'h1' , { timeout : 5000 } ) ,
67+ page . waitForSelector ( 'div' , { timeout : 5000 } ) , // 添加更通用的选择器
68+ ] ) ;
69+ } catch ( waitError ) {
70+ // 忽略等待选择器的错误,继续截图
71+ logger . info ( 'Unable to find common page elements, continuing with screenshot' ) ;
72+ }
6873
6974 // Take screenshot
7075 const screenshot = await page . screenshot ( {
7176 type : 'png' ,
7277 fullPage : true ,
7378 } ) ;
7479
75- // Always close the page when done
76- if ( page ) {
77- await page . close ( ) ;
78- }
79-
80- // Return the screenshot as a PNG image
81- return new Response ( screenshot , {
82- headers : {
83- 'Content-Type' : 'image/png' ,
84- 'Cache-Control' : 's-maxage=3600' ,
85- } ,
86- } ) ;
87- } catch ( error : any ) {
88- logger . error ( 'Screenshot error:' , error ) ;
89-
90- // Ensure page is closed even if an error occurs
91- if ( page ) {
92- try {
80+ // Always close the page when done
81+ if ( page ) {
9382 await page . close ( ) ;
94- } catch ( closeError ) {
95- logger . error ( 'Error closing page:' , closeError ) ;
9683 }
97- }
9884
99- // If browser seems to be in a bad state, recreate it
100- if (
101- error . message . includes ( 'Target closed' ) ||
102- error . message . includes ( 'Protocol error' ) ||
103- error . message . includes ( 'Target.createTarget' )
104- ) {
105- try {
106- if ( browserInstance ) {
107- await browserInstance . close ( ) ;
108- browserInstance = null ;
85+ // Return the screenshot as a PNG image
86+ return new Response ( screenshot , {
87+ headers : {
88+ 'Content-Type' : 'image/png' ,
89+ 'Cache-Control' : 's-maxage=3600' ,
90+ } ,
91+ } ) ;
92+ } catch ( error : any ) {
93+ logger . error ( `Screenshot error on attempt ${ attempt + 1 } :` , error ) ;
94+
95+ // Ensure page is closed even if an error occurs
96+ if ( page ) {
97+ try {
98+ await page . close ( ) ;
99+ } catch ( closeError ) {
100+ logger . error ( 'Error closing page:' , closeError ) ;
109101 }
110- } catch ( closeBrowserError ) {
111- logger . error ( 'Error closing browser:' , closeBrowserError ) ;
112102 }
113- }
114103
115- return NextResponse . json (
116- { error : error . message || 'Failed to capture screenshot' } ,
117- { status : 500 }
118- ) ;
104+ // If browser seems to be in a bad state, recreate it
105+ if (
106+ error . message . includes ( 'Target closed' ) ||
107+ error . message . includes ( 'Protocol error' ) ||
108+ error . message . includes ( 'Target.createTarget' )
109+ ) {
110+ try {
111+ if ( browserInstance ) {
112+ await browserInstance . close ( ) ;
113+ browserInstance = null ;
114+ }
115+ } catch ( closeBrowserError ) {
116+ logger . error ( 'Error closing browser:' , closeBrowserError ) ;
117+ }
118+ }
119+
120+ // 如果这不是最后一次尝试,则继续
121+ if ( attempt < MAX_RETRIES - 1 ) {
122+ // 等待一会儿再重试
123+ await new Promise ( resolve => setTimeout ( resolve , 3000 ) ) ;
124+ continue ;
125+ }
126+
127+ // 最后一次尝试失败
128+ return NextResponse . json (
129+ { error : error . message || 'Failed to capture screenshot after multiple attempts' } ,
130+ { status : 500 }
131+ ) ;
132+ }
119133 }
134+
135+ // 如果重试都失败
136+ return NextResponse . json (
137+ { error : 'Failed to capture screenshot after exhausting all retries' } ,
138+ { status : 500 }
139+ ) ;
120140}
121141
122142// Handle process termination to close browser
0 commit comments