@@ -240,87 +240,108 @@ export class TiktokProvider extends SocialAbstract implements SocialProvider {
240240 integration : Integration
241241 ) : Promise < PostResponse [ ] > {
242242 const [ firstPost , ...comments ] = postDetails ;
243+ const maxRetries = 3 ;
244+ let lastError : Error | null = null ;
243245
244- console . log ( firstPost ) ;
245- const {
246- data : { publish_id } ,
247- } = await (
248- await this . fetch (
249- `https://open.tiktokapis.com/v2/post/publish${ this . postingMethod (
250- firstPost . settings . content_posting_method ,
251- ( firstPost ?. media ?. [ 0 ] ?. path ?. indexOf ( 'mp4' ) || - 1 ) === - 1
252- ) } `,
253- {
254- method : 'POST' ,
255- headers : {
256- 'Content-Type' : 'application/json; charset=UTF-8' ,
257- Authorization : `Bearer ${ accessToken } ` ,
246+ for ( let attempt = 1 ; attempt <= maxRetries ; attempt ++ ) {
247+ try {
248+ console . log ( `TikTok post attempt ${ attempt } /${ maxRetries } ` , firstPost ) ;
249+
250+ const {
251+ data : { publish_id } ,
252+ } = await (
253+ await this . fetch (
254+ `https://open.tiktokapis.com/v2/post/publish${ this . postingMethod (
255+ firstPost . settings . content_posting_method ,
256+ ( firstPost ?. media ?. [ 0 ] ?. path ?. indexOf ( 'mp4' ) || - 1 ) === - 1
257+ ) } `,
258+ {
259+ method : 'POST' ,
260+ headers : {
261+ 'Content-Type' : 'application/json; charset=UTF-8' ,
262+ Authorization : `Bearer ${ accessToken } ` ,
263+ } ,
264+ body : JSON . stringify ( {
265+ ...( ( firstPost ?. settings ?. content_posting_method ||
266+ 'DIRECT_POST' ) === 'DIRECT_POST'
267+ ? {
268+ post_info : {
269+ title : firstPost . message ,
270+ privacy_level :
271+ firstPost . settings . privacy_level || 'PUBLIC_TO_EVERYONE' ,
272+ disable_duet : ! firstPost . settings . duet || false ,
273+ disable_comment : ! firstPost . settings . comment || false ,
274+ disable_stitch : ! firstPost . settings . stitch || false ,
275+ brand_content_toggle :
276+ firstPost . settings . brand_content_toggle || false ,
277+ brand_organic_toggle :
278+ firstPost . settings . brand_organic_toggle || false ,
279+ ...( ( firstPost ?. media ?. [ 0 ] ?. path ?. indexOf ( 'mp4' ) || - 1 ) ===
280+ - 1
281+ ? {
282+ auto_add_music :
283+ firstPost . settings . autoAddMusic === 'yes' ,
284+ }
285+ : { } ) ,
286+ } ,
287+ }
288+ : { } ) ,
289+ ...( ( firstPost ?. media ?. [ 0 ] ?. path ?. indexOf ( 'mp4' ) || - 1 ) > - 1
290+ ? {
291+ source_info : {
292+ source : 'PULL_FROM_URL' ,
293+ video_url : firstPost ?. media ?. [ 0 ] ?. path ! ,
294+ ...( firstPost ?. media ?. [ 0 ] ?. thumbnailTimestamp !
295+ ? {
296+ video_cover_timestamp_ms :
297+ firstPost ?. media ?. [ 0 ] ?. thumbnailTimestamp ! ,
298+ }
299+ : { } ) ,
300+ } ,
301+ }
302+ : {
303+ source_info : {
304+ source : 'PULL_FROM_URL' ,
305+ photo_cover_index : 0 ,
306+ photo_images : firstPost . media ?. map ( ( p ) => p . path ) ,
307+ } ,
308+ post_mode : 'DIRECT_POST' ,
309+ media_type : 'PHOTO' ,
310+ } ) ,
311+ } ) ,
312+ }
313+ )
314+ ) . json ( ) ;
315+
316+ const { url, id : videoId } = await this . uploadedVideoSuccess (
317+ integration . profile ! ,
318+ publish_id ,
319+ accessToken
320+ ) ;
321+
322+ return [
323+ {
324+ id : firstPost . id ,
325+ releaseURL : url ,
326+ postId : String ( videoId ) ,
327+ status : 'success' ,
258328 } ,
259- body : JSON . stringify ( {
260- ...( ( firstPost ?. settings ?. content_posting_method ||
261- 'DIRECT_POST' ) === 'DIRECT_POST'
262- ? {
263- post_info : {
264- title : firstPost . message ,
265- privacy_level :
266- firstPost . settings . privacy_level || 'PUBLIC_TO_EVERYONE' ,
267- disable_duet : ! firstPost . settings . duet || false ,
268- disable_comment : ! firstPost . settings . comment || false ,
269- disable_stitch : ! firstPost . settings . stitch || false ,
270- brand_content_toggle :
271- firstPost . settings . brand_content_toggle || false ,
272- brand_organic_toggle :
273- firstPost . settings . brand_organic_toggle || false ,
274- ...( ( firstPost ?. media ?. [ 0 ] ?. path ?. indexOf ( 'mp4' ) || - 1 ) ===
275- - 1
276- ? {
277- auto_add_music :
278- firstPost . settings . autoAddMusic === 'yes' ,
279- }
280- : { } ) ,
281- } ,
282- }
283- : { } ) ,
284- ...( ( firstPost ?. media ?. [ 0 ] ?. path ?. indexOf ( 'mp4' ) || - 1 ) > - 1
285- ? {
286- source_info : {
287- source : 'PULL_FROM_URL' ,
288- video_url : firstPost ?. media ?. [ 0 ] ?. path ! ,
289- ...( firstPost ?. media ?. [ 0 ] ?. thumbnailTimestamp !
290- ? {
291- video_cover_timestamp_ms :
292- firstPost ?. media ?. [ 0 ] ?. thumbnailTimestamp ! ,
293- }
294- : { } ) ,
295- } ,
296- }
297- : {
298- source_info : {
299- source : 'PULL_FROM_URL' ,
300- photo_cover_index : 0 ,
301- photo_images : firstPost . media ?. map ( ( p ) => p . path ) ,
302- } ,
303- post_mode : 'DIRECT_POST' ,
304- media_type : 'PHOTO' ,
305- } ) ,
306- } ) ,
329+ ] ;
330+ } catch ( error ) {
331+ lastError = error as Error ;
332+ console . log ( `TikTok post attempt ${ attempt } failed:` , error ) ;
333+
334+ // If it's the last attempt, throw the error
335+ if ( attempt === maxRetries ) {
336+ throw error ;
307337 }
308- )
309- ) . json ( ) ;
310-
311- const { url, id : videoId } = await this . uploadedVideoSuccess (
312- integration . profile ! ,
313- publish_id ,
314- accessToken
315- ) ;
338+
339+ // Wait before retrying (exponential backoff)
340+ await timer ( attempt * 2000 ) ;
341+ }
342+ }
316343
317- return [
318- {
319- id : firstPost . id ,
320- releaseURL : url ,
321- postId : String ( videoId ) ,
322- status : 'success' ,
323- } ,
324- ] ;
344+ // This should never be reached, but just in case
345+ throw lastError || new Error ( 'TikTok post failed after all retries' ) ;
325346 }
326347}
0 commit comments