mirror of
				https://github.com/n8n-io/n8n-nodes-starter.git
				synced 2025-11-04 08:02:25 -06:00 
			
		
		
		
	feat: Initial structure for Suno AI n8n node
I've set up the foundational boilerplate for the Suno AI integration. Key changes include: - Restructured directories for nodes, credentials, interfaces, utils, tests, and docs. - Renamed and updated example files to Suno-specific names and conventions (SunoApi.credentials.ts, Suno.node.ts). - Updated package.json and root README.md for the Suno AI node. - Created .env.example with placeholders for Suno environment variables. - Added a dev-log.md with initial notes on authentication research strategy. - Scaffolded utils/sunoApi.ts with placeholder API functions and JSDoc comments. - Scaffolded nodes/Suno/Suno.node.ts with operations, properties, execute routing, and a placeholder SVG icon. - Scaffolded nodes/Suno/SunoTrigger.node.ts with a basic trigger structure and properties. - Defined initial TypeScript types in interfaces/SunoTypes.ts for common data structures (SunoTrack, SunoJob, etc.). - Created placeholder README.md files in new subdirectories. This commit establishes the project structure and lays the groundwork for implementing Suno AI API interactions and node functionality.
This commit is contained in:
		
					parent
					
						
							
								65e06338e3
							
						
					
				
			
			
				commit
				
					
						6c69a287fe
					
				
			
		
					 20 changed files with 684 additions and 449 deletions
				
			
		
							
								
								
									
										212
									
								
								nodes/Suno/Suno.node.ts
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										212
									
								
								nodes/Suno/Suno.node.ts
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,212 @@
 | 
			
		|||
import type {
 | 
			
		||||
	IExecuteFunctions,
 | 
			
		||||
	INodeExecutionData,
 | 
			
		||||
	INodeType,
 | 
			
		||||
	INodeTypeDescription,
 | 
			
		||||
	// Assuming INodeProperties is needed for properties definition
 | 
			
		||||
	INodeProperties,
 | 
			
		||||
} from 'n8n-workflow';
 | 
			
		||||
import { NodeConnectionType, NodeOperationError } from 'n8n-workflow';
 | 
			
		||||
 | 
			
		||||
// TODO: Import functions from sunoApi.ts when they are implemented
 | 
			
		||||
// import * as sunoApi from '../../utils/sunoApi';
 | 
			
		||||
 | 
			
		||||
export class SunoNode implements INodeType {
 | 
			
		||||
	description: INodeTypeDescription = {
 | 
			
		||||
		displayName: 'Suno',
 | 
			
		||||
		name: 'suno',
 | 
			
		||||
		icon: 'file:suno.svg',
 | 
			
		||||
		group: ['ai'],
 | 
			
		||||
		version: 1,
 | 
			
		||||
		subtitle: '={{$parameter["operation"]}}',
 | 
			
		||||
		description: 'Integrates with Suno AI for music generation',
 | 
			
		||||
		defaults: {
 | 
			
		||||
			name: 'Suno',
 | 
			
		||||
		},
 | 
			
		||||
		inputs: [NodeConnectionType.Main],
 | 
			
		||||
		outputs: [NodeConnectionType.Main],
 | 
			
		||||
		credentials: [
 | 
			
		||||
			{
 | 
			
		||||
				name: 'sunoApi', // Matches the name in SunoApi.credentials.ts
 | 
			
		||||
				required: true,
 | 
			
		||||
			},
 | 
			
		||||
		],
 | 
			
		||||
		properties: [
 | 
			
		||||
			{
 | 
			
		||||
				displayName: 'Operation',
 | 
			
		||||
				name: 'operation',
 | 
			
		||||
				type: 'options',
 | 
			
		||||
				noDataExpression: true,
 | 
			
		||||
				options: [
 | 
			
		||||
					{
 | 
			
		||||
						name: 'Generate Song from Prompt',
 | 
			
		||||
						value: 'generateSongFromPrompt',
 | 
			
		||||
						description: 'Create a new song based on a text prompt',
 | 
			
		||||
						action: 'Generate song from prompt',
 | 
			
		||||
					},
 | 
			
		||||
					{
 | 
			
		||||
						name: 'Upload Track Reference',
 | 
			
		||||
						value: 'uploadTrackReference',
 | 
			
		||||
						description: 'Upload a reference track for generation',
 | 
			
		||||
						action: 'Upload track reference',
 | 
			
		||||
					},
 | 
			
		||||
					{
 | 
			
		||||
						name: 'Get Track Status',
 | 
			
		||||
						value: 'getTrackStatus',
 | 
			
		||||
						description: 'Get the status of a generated track',
 | 
			
		||||
						action: 'Get track status',
 | 
			
		||||
					},
 | 
			
		||||
					{
 | 
			
		||||
						name: 'Download Track',
 | 
			
		||||
						value: 'downloadTrack',
 | 
			
		||||
						description: 'Download a generated track',
 | 
			
		||||
						action: 'Download track',
 | 
			
		||||
					},
 | 
			
		||||
					{
 | 
			
		||||
						name: 'List Previous Songs',
 | 
			
		||||
						value: 'listPreviousSongs',
 | 
			
		||||
						description: 'List previously generated songs',
 | 
			
		||||
						action: 'List previous songs',
 | 
			
		||||
					},
 | 
			
		||||
				],
 | 
			
		||||
				default: 'generateSongFromPrompt',
 | 
			
		||||
			},
 | 
			
		||||
			// Properties for 'generateSongFromPrompt'
 | 
			
		||||
			{
 | 
			
		||||
				displayName: 'Prompt',
 | 
			
		||||
				name: 'prompt',
 | 
			
		||||
				type: 'string',
 | 
			
		||||
				default: '',
 | 
			
		||||
				placeholder: 'e.g., Epic orchestral score for a space battle',
 | 
			
		||||
				description: 'The text prompt to generate music from',
 | 
			
		||||
				displayOptions: {
 | 
			
		||||
					show: {
 | 
			
		||||
						operation: ['generateSongFromPrompt'],
 | 
			
		||||
					},
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
			// Properties for 'getTrackStatus' and 'downloadTrack'
 | 
			
		||||
			{
 | 
			
		||||
				displayName: 'Track ID',
 | 
			
		||||
				name: 'trackId',
 | 
			
		||||
				type: 'string',
 | 
			
		||||
				default: '',
 | 
			
		||||
				required: true,
 | 
			
		||||
				placeholder: 'Enter Track ID',
 | 
			
		||||
				description: 'The ID of the track',
 | 
			
		||||
				displayOptions: {
 | 
			
		||||
					show: {
 | 
			
		||||
						operation: ['getTrackStatus', 'downloadTrack'],
 | 
			
		||||
					},
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
			// Properties for 'uploadTrackReference'
 | 
			
		||||
			{
 | 
			
		||||
				displayName: 'File Path',
 | 
			
		||||
				name: 'filePath',
 | 
			
		||||
				type: 'string',
 | 
			
		||||
				default: '',
 | 
			
		||||
				required: true,
 | 
			
		||||
				placeholder: '/path/to/your/audio.mp3',
 | 
			
		||||
				description: 'Path to the audio file to upload as reference',
 | 
			
		||||
				displayOptions: {
 | 
			
		||||
					show: {
 | 
			
		||||
						operation: ['uploadTrackReference'],
 | 
			
		||||
					},
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
		] as INodeProperties[], // Cast to INodeProperties[]
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * Placeholder method for generating a song from a prompt.
 | 
			
		||||
	 * @param {IExecuteFunctions} this - The execution context.
 | 
			
		||||
	 * @returns {Promise<INodeExecutionData[][]>}
 | 
			
		||||
	 */
 | 
			
		||||
	async generateSongFromPrompt(this: IExecuteFunctions): Promise<INodeExecutionData[][]> {
 | 
			
		||||
		const prompt = this.getNodeParameter('prompt', 0, '') as string;
 | 
			
		||||
		// TODO: Call the appropriate sunoApi.ts function (sunoApi.submitPrompt) and handle response
 | 
			
		||||
		console.log('Executing generateSongFromPrompt with prompt:', prompt);
 | 
			
		||||
		// For now, return empty data
 | 
			
		||||
		return [this.prepareOutputData([])];
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * Placeholder method for uploading a track reference.
 | 
			
		||||
	 * @param {IExecuteFunctions} this - The execution context.
 | 
			
		||||
	 * @returns {Promise<INodeExecutionData[][]>}
 | 
			
		||||
	 */
 | 
			
		||||
	async uploadTrackReference(this: IExecuteFunctions): Promise<INodeExecutionData[][]> {
 | 
			
		||||
		const filePath = this.getNodeParameter('filePath', 0, '') as string;
 | 
			
		||||
		// TODO: Call the appropriate sunoApi.ts function (sunoApi.uploadReferenceTrack) and handle response
 | 
			
		||||
		console.log('Executing uploadTrackReference with filePath:', filePath);
 | 
			
		||||
		return [this.prepareOutputData([])];
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * Placeholder method for getting track status.
 | 
			
		||||
	 * @param {IExecuteFunctions} this - The execution context.
 | 
			
		||||
	 * @returns {Promise<INodeExecutionData[][]>}
 | 
			
		||||
	 */
 | 
			
		||||
	async getTrackStatus(this: IExecuteFunctions): Promise<INodeExecutionData[][]> {
 | 
			
		||||
		const trackId = this.getNodeParameter('trackId', 0, '') as string;
 | 
			
		||||
		// TODO: Call the appropriate sunoApi.ts function (sunoApi.pollJobStatus or similar) and handle response
 | 
			
		||||
		console.log('Executing getTrackStatus with trackId:', trackId);
 | 
			
		||||
		return [this.prepareOutputData([])];
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * Placeholder method for downloading a track.
 | 
			
		||||
	 * @param {IExecuteFunctions} this - The execution context.
 | 
			
		||||
	 * @returns {Promise<INodeExecutionData[][]>}
 | 
			
		||||
	 */
 | 
			
		||||
	async downloadTrack(this: IExecuteFunctions): Promise<INodeExecutionData[][]> {
 | 
			
		||||
		const trackId = this.getNodeParameter('trackId', 0, '') as string;
 | 
			
		||||
		// TODO: Call the appropriate sunoApi.ts function (sunoApi.downloadTrack) and handle response
 | 
			
		||||
		// TODO: Consider how to handle binary data output in n8n
 | 
			
		||||
		console.log('Executing downloadTrack with trackId:', trackId);
 | 
			
		||||
		return [this.prepareOutputData([])];
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * Placeholder method for listing previous songs.
 | 
			
		||||
	 * @param {IExecuteFunctions} this - The execution context.
 | 
			
		||||
	 * @returns {Promise<INodeExecutionData[][]>}
 | 
			
		||||
	 */
 | 
			
		||||
	async listPreviousSongs(this: IExecuteFunctions): Promise<INodeExecutionData[][]> {
 | 
			
		||||
		// TODO: Call the appropriate sunoApi.ts function (sunoApi.listPreviousSongs) and handle response
 | 
			
		||||
		console.log('Executing listPreviousSongs');
 | 
			
		||||
		return [this.prepareOutputData([])];
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	async execute(this: IExecuteFunctions): Promise<INodeExecutionData[][]> {
 | 
			
		||||
		const operation = this.getNodeParameter('operation', 0, '') as string;
 | 
			
		||||
 | 
			
		||||
		try {
 | 
			
		||||
			switch (operation) {
 | 
			
		||||
				case 'generateSongFromPrompt':
 | 
			
		||||
					return this.generateSongFromPrompt(this);
 | 
			
		||||
				case 'uploadTrackReference':
 | 
			
		||||
					return this.uploadTrackReference(this);
 | 
			
		||||
				case 'getTrackStatus':
 | 
			
		||||
					return this.getTrackStatus(this);
 | 
			
		||||
				case 'downloadTrack':
 | 
			
		||||
					return this.downloadTrack(this);
 | 
			
		||||
				case 'listPreviousSongs':
 | 
			
		||||
					return this.listPreviousSongs(this);
 | 
			
		||||
				default:
 | 
			
		||||
					throw new NodeOperationError(this.getNode(), `The operation "${operation}" is not supported.`);
 | 
			
		||||
			}
 | 
			
		||||
		} catch (error) {
 | 
			
		||||
			// This node should use NodeOperationError when an error occurs directly within this node's execution logic
 | 
			
		||||
			if (this.continueOnFail()) {
 | 
			
		||||
				// Return error data as per n8n's guidelines for allowing the workflow to continue
 | 
			
		||||
				const item = this.getInputData(0)[0]; // Get the first item if available, otherwise undefined
 | 
			
		||||
				return [this.prepareOutputData([{ json: {}, error: error, pairedItem: item ? { item: 0 } : undefined }])];
 | 
			
		||||
			} else {
 | 
			
		||||
				// If not continuing on fail, rethrow the error to halt the workflow
 | 
			
		||||
				throw error; // NodeOperationError should already be structured correctly
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue