import { IExecuteFunctions, INodeExecutionData, INodeType, INodeTypeDescription, } from 'n8n-workflow'; export class TikTokUpload implements INodeType { description: INodeTypeDescription = { displayName: 'TikTok Upload', name: 'tikTokUpload', icon: 'file:tiktok.png', group: ['transform'], version: 1, description: 'Upload videos to TikTok with product anchors', defaults: { name: 'TikTok Upload', }, inputs: ['main'], outputs: ['main'], credentials: [ { name: 'tikTokSession', required: true, }, ], properties: [ { displayName: 'Caption Text', name: 'text', type: 'string', default: '', description: 'Text caption for the video', typeOptions: { rows: 4, }, }, { displayName: 'Visibility Type', name: 'visibilityType', type: 'options', options: [ { name: 'Public', value: 0 }, { name: 'Friends', value: 1 }, { name: 'Private', value: 2 }, ], default: 0, description: 'Who can see this video', }, { displayName: 'Allow Comments', name: 'allowComment', type: 'options', options: [ { name: 'Yes', value: 1 }, { name: 'No', value: 0 }, ], default: 1, description: 'Whether to allow comments on this video', }, { displayName: 'Schedule Time', name: 'scheduleTime', type: 'number', default: 0, description: 'Unix timestamp for scheduled publishing (0 for immediate)', }, { displayName: 'Anchors', name: 'anchors', type: 'fixedCollection', typeOptions: { multipleValues: true, }, default: {}, options: [ { name: 'anchor', displayName: 'Product Anchor', values: [ { displayName: 'Type', name: 'type', type: 'options', options: [ { name: 'Product', value: 'product' }, ], default: 'product', }, { displayName: 'Product ID', name: 'productId', type: 'string', default: '', description: 'ID of the product to anchor', }, { displayName: 'Display Name', name: 'displayName', type: 'string', default: '', description: 'Display name for the product anchor', }, ], }, ], }, { displayName: 'Video Input Type', name: 'videoInputType', type: 'options', options: [ { name: 'Binary Input Field', value: 'binaryData' }, { name: 'URL', value: 'url' }, ], default: 'binaryData', description: 'Where to get the video from', }, { displayName: 'Video Binary Field', name: 'videoBinaryField', type: 'string', default: 'data', description: 'Name of the binary field containing video', displayOptions: { show: { videoInputType: ['binaryData'], }, }, }, { displayName: 'Video URL', name: 'videoUrl', type: 'string', default: '', description: 'URL of the video to upload', displayOptions: { show: { videoInputType: ['url'], }, }, }, { displayName: 'Cover Input Type', name: 'coverInputType', type: 'options', options: [ { name: 'None', value: 'none' }, { name: 'Binary Input Field', value: 'binaryData' }, { name: 'URL', value: 'url' }, ], default: 'none', description: 'Where to get the cover image from', }, ], }; async execute(this: IExecuteFunctions): Promise { const items = this.getInputData(); const returnData: INodeExecutionData[] = []; // Get credentials const credentials = await this.getCredentials('tikTokSession'); const sessionData = JSON.parse(credentials.sessionData as string); const headers = sessionData.headers || {}; for (let i = 0; i < items.length; i++) { try { const text = this.getNodeParameter('text', i, '') as string; const visibilityType = this.getNodeParameter('visibilityType', i, 0) as number; const allowComment = this.getNodeParameter('allowComment', i, 1) as number; const scheduleTime = this.getNodeParameter('scheduleTime', i, 0) as number; const anchors = this.getNodeParameter('anchors', i, {}) as any; const videoInputType = this.getNodeParameter('videoInputType', i) as string; let videoBuffer: Buffer; // Get video data if (videoInputType === 'binaryData') { const binaryField = this.getNodeParameter('videoBinaryField', i) as string; const binaryData = items[i].binary?.[binaryField]; if (!binaryData) { throw new Error(`No binary data found in field "${binaryField}"`); } videoBuffer = Buffer.from(binaryData.data, 'base64'); } else { const videoUrl = this.getNodeParameter('videoUrl', i) as string; const response = await this.helpers.request({ method: 'GET', url: videoUrl, encoding: null, }); videoBuffer = Buffer.from(response); } // Prepare upload request // NOTE: This is a placeholder - you need to capture real TikTok upload endpoints const uploadResponse = await this.helpers.request({ method: 'POST', url: sessionData.url || 'https://www.tiktok.com/api/v1/item/create/', headers, body: { text, visibilityType, allowComment, scheduleTime, anchors, video_size: videoBuffer.length, }, json: true, }); returnData.push({ json: { success: true, ...uploadResponse, }, }); } catch (error: any) { if (this.continueOnFail()) { returnData.push({ json: { error: error.message, statusCode: error.statusCode, } }); continue; } throw error; } } return [returnData]; } }