n8n-nodes-supermachine 0.5.0 → 0.6.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -734,6 +734,20 @@ class Supermachine {
734
734
  if (!batchId) {
735
735
  throw new Error('No batchId returned from generate API');
736
736
  }
737
+ const skipPolling = additionalFields.skipPolling;
738
+ if (skipPolling) {
739
+ returnData.push({
740
+ json: {
741
+ batchId,
742
+ status: 'submitted',
743
+ message: 'Image generation submitted. Use List Images with this batchId to check status.',
744
+ prompt,
745
+ model: body.model,
746
+ submittedAt: new Date().toISOString(),
747
+ }
748
+ });
749
+ continue;
750
+ }
737
751
  const pollingInterval = additionalFields.pollingInterval || 2;
738
752
  const maxPollingTime = additionalFields.maxPollingTime || 120;
739
753
  const startTime = Date.now();
@@ -845,13 +859,46 @@ class Supermachine {
845
859
  if (operation === 'removeBackground') {
846
860
  const imageSource = this.getNodeParameter('imageSource', i);
847
861
  const additionalOptions = this.getNodeParameter('additionalOptions', i, {});
848
- const body = {};
862
+ let imageUrl = '';
863
+ // Step 1: Get image URL
849
864
  if (imageSource === 'imageId') {
850
- body.imageId = this.getNodeParameter('imageId', i);
865
+ const imageId = this.getNodeParameter('imageId', i);
866
+ // Fetch image details to get URL
867
+ const imageDetails = await this.helpers.requestWithAuthentication.call(this, 'supermachineApi', {
868
+ method: 'GET',
869
+ url: `https://dev.supermachine.art/v1/images/${imageId}`,
870
+ json: true,
871
+ });
872
+ imageUrl = imageDetails.url;
873
+ if (!imageUrl) {
874
+ throw new Error(`Image ID ${imageId} does not have a valid URL`);
875
+ }
851
876
  }
852
877
  else if (imageSource === 'imageUrl') {
853
- body.imageUrl = this.getNodeParameter('imageUrl', i);
878
+ imageUrl = this.getNodeParameter('imageUrl', i);
879
+ }
880
+ // Step 2: Download image as binary
881
+ const imageBuffer = await this.helpers.httpRequest({
882
+ method: 'GET',
883
+ url: imageUrl,
884
+ encoding: 'arraybuffer',
885
+ returnFullResponse: false,
886
+ });
887
+ // Step 3: Convert to base64
888
+ const base64Image = Buffer.from(imageBuffer).toString('base64');
889
+ // Detect image type from URL or use default
890
+ let mimeType = 'image/jpeg';
891
+ if (imageUrl.toLowerCase().includes('.png')) {
892
+ mimeType = 'image/png';
854
893
  }
894
+ else if (imageUrl.toLowerCase().includes('.webp')) {
895
+ mimeType = 'image/webp';
896
+ }
897
+ const dataUri = `data:${mimeType};base64,${base64Image}`;
898
+ // Step 4: Submit to remove background API
899
+ const body = {
900
+ image: dataUri,
901
+ };
855
902
  if (additionalOptions.outputFormat) {
856
903
  body.outputFormat = additionalOptions.outputFormat;
857
904
  }
@@ -870,35 +917,41 @@ class Supermachine {
870
917
  catch (error) {
871
918
  throw new Error(`Failed to remove background: ${error.message}`);
872
919
  }
873
- const jobId = removeResponse.jobId || removeResponse.batchId || removeResponse.id;
874
- if (!jobId) {
920
+ const batchId = removeResponse.batchId;
921
+ if (!batchId) {
875
922
  if (removeResponse.url || removeResponse.imageUrl) {
876
923
  returnData.push({
877
924
  json: {
878
925
  success: true,
879
926
  imageUrl: removeResponse.url || removeResponse.imageUrl,
880
927
  imageId: removeResponse.id || removeResponse.imageId,
928
+ creditsCost: removeResponse.creditsCost,
929
+ creditsRemaining: removeResponse.creditsRemaining,
881
930
  processedAt: new Date().toISOString(),
882
931
  ...removeResponse,
883
932
  }
884
933
  });
885
934
  continue;
886
935
  }
887
- throw new Error('No job ID or result returned from remove background API');
936
+ throw new Error('No batchId or result returned from remove background API');
888
937
  }
889
938
  const skipPolling = additionalOptions.skipPolling;
890
939
  if (skipPolling) {
891
940
  returnData.push({
892
941
  json: {
893
- jobId,
942
+ batchId,
943
+ tool: removeResponse.tool || 'remove-background',
944
+ creditsCost: removeResponse.creditsCost,
945
+ creditsRemaining: removeResponse.creditsRemaining,
894
946
  status: 'submitted',
895
- message: 'Background removal submitted. Use List Images with this job ID to check status.',
947
+ message: 'Background removal submitted. Use List Images with this batchId to check status.',
896
948
  submittedAt: new Date().toISOString(),
897
949
  ...removeResponse,
898
950
  }
899
951
  });
900
952
  continue;
901
953
  }
954
+ // Step 5: Poll for result
902
955
  const pollingInterval = additionalOptions.pollingInterval || 2;
903
956
  const maxPollingTime = additionalOptions.maxPollingTime || 120;
904
957
  const startTime = Date.now();
@@ -906,13 +959,13 @@ class Supermachine {
906
959
  await new Promise((resolve) => setTimeout(resolve, 2000));
907
960
  while (!completedResult) {
908
961
  if ((Date.now() - startTime) / 1000 > maxPollingTime) {
909
- throw new Error(`Background removal timed out after ${maxPollingTime} seconds. Job ID: ${jobId}`);
962
+ throw new Error(`Background removal timed out after ${maxPollingTime} seconds. Batch ID: ${batchId}`);
910
963
  }
911
964
  let pollResponse;
912
965
  try {
913
966
  pollResponse = await this.helpers.requestWithAuthentication.call(this, 'supermachineApi', {
914
967
  method: 'GET',
915
- url: `https://dev.supermachine.art/v1/images?batchId=${jobId}`,
968
+ url: `https://dev.supermachine.art/v1/images?batchId=${batchId}`,
916
969
  json: true,
917
970
  });
918
971
  }
@@ -929,16 +982,19 @@ class Supermachine {
929
982
  break;
930
983
  }
931
984
  else if (status === 'failed') {
932
- throw new Error(`Background removal failed. Job ID: ${jobId}. Error: ${firstItem.error || 'Unknown error'}`);
985
+ throw new Error(`Background removal failed. Batch ID: ${batchId}. Error: ${firstItem.error || 'Unknown error'}`);
933
986
  }
934
987
  }
935
988
  await new Promise((resolve) => setTimeout(resolve, pollingInterval * 1000));
936
989
  }
937
990
  returnData.push({
938
991
  json: {
939
- jobId,
992
+ batchId,
940
993
  success: true,
941
994
  operation: 'removeBackground',
995
+ tool: 'remove-background',
996
+ creditsCost: removeResponse.creditsCost,
997
+ creditsRemaining: removeResponse.creditsRemaining,
942
998
  processedAt: new Date().toISOString(),
943
999
  ...completedResult,
944
1000
  }
@@ -54,9 +54,9 @@ exports.getImageOperations = [
54
54
  },
55
55
  ];
56
56
  exports.getImageFields = [
57
- // ═══════════════════════════════════════════════════════════
57
+ // ═══════════════════════════════════════════════════════════════
58
58
  // GENERATE V1
59
- // ═══════════════════════════════════════════════════════════
59
+ // ═══════════════════════════════════════════════════════════════
60
60
  {
61
61
  displayName: 'Model Name or ID',
62
62
  name: 'model',
@@ -206,6 +206,13 @@ exports.getImageFields = [
206
206
  default: '',
207
207
  description: 'Move image to folder. Choose from the list, or specify an ID using an <a href="https://docs.n8n.io/code-examples/expressions/">expression</a>.',
208
208
  },
209
+ {
210
+ displayName: 'Skip Polling',
211
+ name: 'skipPolling',
212
+ type: 'boolean',
213
+ default: false,
214
+ description: 'Whether to skip waiting for completion (return batchId immediately)',
215
+ },
209
216
  {
210
217
  displayName: 'Polling Interval (seconds)',
211
218
  name: 'pollingInterval',
@@ -326,9 +333,9 @@ exports.getImageFields = [
326
333
  },
327
334
  ],
328
335
  },
329
- // ═══════════════════════════════════════════════════════════
336
+ // ═══════════════════════════════════════════════════════════════
330
337
  // GENERATE V3 - NO AUTH TOKEN FIELD (uses credential)
331
- // ═══════════════════════════════════════════════════════════
338
+ // ═══════════════════════════════════════════════════════════════
332
339
  {
333
340
  displayName: 'Model Name',
334
341
  name: 'modelNameV3',
@@ -612,9 +619,9 @@ exports.getImageFields = [
612
619
  },
613
620
  ],
614
621
  },
615
- // ═══════════════════════════════════════════════════════════
622
+ // ═══════════════════════════════════════════════════════════════
616
623
  // LIST IMAGES
617
- // ═══════════════════════════════════════════════════════════
624
+ // ═══════════════════════════════════════════════════════════════
618
625
  {
619
626
  displayName: 'Return All',
620
627
  name: 'returnAll',
@@ -686,9 +693,9 @@ exports.getImageFields = [
686
693
  },
687
694
  ],
688
695
  },
689
- // ═══════════════════════════════════════════════════════════
696
+ // ═══════════════════════════════════════════════════════════════
690
697
  // GET IMAGE
691
- // ═══════════════════════════════════════════════════════════
698
+ // ═══════════════════════════════════════════════════════════════
692
699
  {
693
700
  displayName: 'Image ID',
694
701
  name: 'imageId',
@@ -704,9 +711,9 @@ exports.getImageFields = [
704
711
  placeholder: '8870379',
705
712
  description: 'ID của ảnh',
706
713
  },
707
- // ═══════════════════════════════════════════════════════════
714
+ // ═══════════════════════════════════════════════════════════════
708
715
  // MOVE IMAGE
709
- // ═══════════════════════════════════════════════════════════
716
+ // ═══════════════════════════════════════════════════════════════
710
717
  {
711
718
  displayName: 'Target Folder Name or ID',
712
719
  name: 'targetFolderId',
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "n8n-nodes-supermachine",
3
- "version": "0.5.0",
3
+ "version": "0.6.1",
4
4
  "description": "n8n community node for Supermachine AI Image API — Generate images, manage models, LoRAs, and characters",
5
5
  "keywords": [
6
6
  "n8n-community-node-package",