@xuda.io/ai_module 1.1.4355 → 1.1.4357
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.
- package/index.mjs +208 -203
- package/package.json +1 -1
package/index.mjs
CHANGED
|
@@ -5679,72 +5679,72 @@ export const create_profile_avatar = async function (req, job_id, headers) {
|
|
|
5679
5679
|
const uniqueId = Date.now() + '_' + Math.random().toString(36).substring(7);
|
|
5680
5680
|
const tempOutputPath = path.join(tempDir, `output_${uniqueId}.png`);
|
|
5681
5681
|
|
|
5682
|
-
async function normalizeBase64To1024(base64Image) {
|
|
5683
|
-
|
|
5684
|
-
|
|
5685
|
-
|
|
5686
|
-
|
|
5687
|
-
|
|
5688
|
-
|
|
5689
|
-
|
|
5690
|
-
|
|
5691
|
-
|
|
5692
|
-
|
|
5693
|
-
|
|
5694
|
-
|
|
5695
|
-
|
|
5696
|
-
|
|
5697
|
-
|
|
5698
|
-
}
|
|
5699
|
-
|
|
5700
|
-
async function hasTransparentBackground(filePath) {
|
|
5701
|
-
|
|
5702
|
-
|
|
5703
|
-
|
|
5704
|
-
|
|
5705
|
-
|
|
5706
|
-
|
|
5707
|
-
|
|
5708
|
-
|
|
5709
|
-
|
|
5710
|
-
|
|
5711
|
-
|
|
5712
|
-
|
|
5713
|
-
|
|
5714
|
-
|
|
5715
|
-
|
|
5716
|
-
|
|
5717
|
-
|
|
5718
|
-
|
|
5719
|
-
|
|
5720
|
-
|
|
5721
|
-
|
|
5722
|
-
|
|
5723
|
-
|
|
5724
|
-
|
|
5725
|
-
|
|
5726
|
-
|
|
5727
|
-
|
|
5728
|
-
}
|
|
5729
|
-
|
|
5730
|
-
async function imageToPngBlob(filePath) {
|
|
5731
|
-
|
|
5732
|
-
|
|
5733
|
-
|
|
5734
|
-
|
|
5735
|
-
|
|
5736
|
-
|
|
5737
|
-
|
|
5738
|
-
|
|
5739
|
-
|
|
5740
|
-
|
|
5741
|
-
|
|
5742
|
-
|
|
5743
|
-
|
|
5744
|
-
|
|
5745
|
-
|
|
5746
|
-
|
|
5747
|
-
}
|
|
5682
|
+
// async function normalizeBase64To1024(base64Image) {
|
|
5683
|
+
// const inputBuffer = Buffer.from(base64Image, 'base64');
|
|
5684
|
+
|
|
5685
|
+
// const normalizedBuffer = await sharp(inputBuffer)
|
|
5686
|
+
// .resize(1024, 1024, {
|
|
5687
|
+
// fit: 'contain', // Preserve aspect ratio, fit within 211x211
|
|
5688
|
+
// background: { r: 0, g: 0, b: 0, alpha: 0 }, // Transparent background for padding
|
|
5689
|
+
// })
|
|
5690
|
+
// .png({
|
|
5691
|
+
// quality: 100,
|
|
5692
|
+
// compressionLevel: 9,
|
|
5693
|
+
// force: true,
|
|
5694
|
+
// })
|
|
5695
|
+
// .toBuffer();
|
|
5696
|
+
|
|
5697
|
+
// return normalizedBuffer.toString('base64'); // Return normalized base64 string
|
|
5698
|
+
// }
|
|
5699
|
+
|
|
5700
|
+
// async function hasTransparentBackground(filePath) {
|
|
5701
|
+
// const image = sharp(filePath);
|
|
5702
|
+
// const metadata = await image.metadata();
|
|
5703
|
+
|
|
5704
|
+
// // If the image doesn't have an alpha channel at all → no transparency possible
|
|
5705
|
+
// if (!metadata.hasAlpha) {
|
|
5706
|
+
// return false;
|
|
5707
|
+
// }
|
|
5708
|
+
|
|
5709
|
+
// // For images with alpha channel, check if any pixel actually has alpha < 255
|
|
5710
|
+
// const { data, info } = await image.raw().toBuffer({ resolveWithObject: true });
|
|
5711
|
+
|
|
5712
|
+
// // Determine position of alpha channel based on number of channels
|
|
5713
|
+
// const channels = info.channels; // 2 (grayscale+alpha), 4 (RGBA), etc.
|
|
5714
|
+
// const alphaOffset = channels === 2 ? 1 : channels === 4 ? 3 : -1;
|
|
5715
|
+
|
|
5716
|
+
// if (alphaOffset === -1) {
|
|
5717
|
+
// // Unusual channel count – be conservative and assume it might have transparency
|
|
5718
|
+
// return true;
|
|
5719
|
+
// }
|
|
5720
|
+
|
|
5721
|
+
// for (let i = alphaOffset; i < data.length; i += channels) {
|
|
5722
|
+
// if (data[i] < 255) {
|
|
5723
|
+
// return true; // Found at least one transparent/semi-transparent pixel
|
|
5724
|
+
// }
|
|
5725
|
+
// }
|
|
5726
|
+
|
|
5727
|
+
// return false; // Has alpha channel but all pixels are fully opaque
|
|
5728
|
+
// }
|
|
5729
|
+
|
|
5730
|
+
// async function imageToPngBlob(filePath) {
|
|
5731
|
+
// const ext = path.extname(filePath).toLowerCase();
|
|
5732
|
+
|
|
5733
|
+
// let buffer;
|
|
5734
|
+
|
|
5735
|
+
// let is_transparent = false;
|
|
5736
|
+
// if (ext === '.png') {
|
|
5737
|
+
// buffer = await fs.promises.readFile(filePath);
|
|
5738
|
+
// is_transparent = await hasTransparentBackground(filePath);
|
|
5739
|
+
// } else {
|
|
5740
|
+
// console.log(`Converting ${path.basename(filePath)} to PNG...`);
|
|
5741
|
+
// buffer = await sharp(filePath).png({ quality: 100, compressionLevel: 9, force: true }).toBuffer();
|
|
5742
|
+
// }
|
|
5743
|
+
|
|
5744
|
+
// // Create a Blob with correct MIME type
|
|
5745
|
+
|
|
5746
|
+
// return { is_transparent, image_blob: new Blob([buffer], { type: 'image/png' }) };
|
|
5747
|
+
// }
|
|
5748
5748
|
|
|
5749
5749
|
const detect_real_person_in_image = async function (base64) {
|
|
5750
5750
|
const ret = await submit_chat_gpt_prompt({
|
|
@@ -5782,54 +5782,55 @@ export const create_profile_avatar = async function (req, job_id, headers) {
|
|
|
5782
5782
|
await update_profile_avatar_status(account_obj._id, 2);
|
|
5783
5783
|
}
|
|
5784
5784
|
try {
|
|
5785
|
-
if (!url) {
|
|
5786
|
-
|
|
5787
|
-
}
|
|
5785
|
+
// if (!url) {
|
|
5786
|
+
// throw new Error('No URL provided in request');
|
|
5787
|
+
// }
|
|
5788
5788
|
|
|
5789
5789
|
// 3. Download the image to a Buffer
|
|
5790
|
-
const res = await fetch(url);
|
|
5791
|
-
if (!res.ok) {
|
|
5792
|
-
|
|
5793
|
-
}
|
|
5790
|
+
// const res = await fetch(url);
|
|
5791
|
+
// if (!res.ok) {
|
|
5792
|
+
// throw new Error(`Failed to fetch image URL: ${res.status} ${res.statusText}`);
|
|
5793
|
+
// }
|
|
5794
5794
|
|
|
5795
|
-
const arrayBuffer = await res.arrayBuffer();
|
|
5796
|
-
const imageBuffer = Buffer.from(arrayBuffer);
|
|
5795
|
+
// const arrayBuffer = await res.arrayBuffer();
|
|
5796
|
+
// const imageBuffer = Buffer.from(arrayBuffer);
|
|
5797
5797
|
|
|
5798
|
-
const tempDir = os.tmpdir();
|
|
5799
|
-
const uniqueId = Date.now() + '_' + Math.random().toString(36).substring(7);
|
|
5798
|
+
// const tempDir = os.tmpdir();
|
|
5799
|
+
// const uniqueId = Date.now() + '_' + Math.random().toString(36).substring(7);
|
|
5800
5800
|
|
|
5801
|
-
const inputExt = path.extname(url) || '.jpg';
|
|
5802
|
-
const tempInputPath = path.join(tempDir, `input_${uniqueId}${inputExt}`);
|
|
5803
|
-
const tempOutputPath = path.join(tempDir, `output_${uniqueId}.png`); // Output is always PNG (transparent)
|
|
5801
|
+
// const inputExt = path.extname(url) || '.jpg';
|
|
5802
|
+
// const tempInputPath = path.join(tempDir, `input_${uniqueId}${inputExt}`);
|
|
5803
|
+
// const tempOutputPath = path.join(tempDir, `output_${uniqueId}.png`); // Output is always PNG (transparent)
|
|
5804
5804
|
|
|
5805
5805
|
// 5. Write the downloaded image to disk
|
|
5806
|
-
fs.writeFileSync(tempInputPath, imageBuffer);
|
|
5806
|
+
// fs.writeFileSync(tempInputPath, imageBuffer);
|
|
5807
5807
|
/////////////////////////////////
|
|
5808
5808
|
|
|
5809
|
-
const imageFiles = [tempInputPath];
|
|
5809
|
+
// const imageFiles = [tempInputPath];
|
|
5810
5810
|
|
|
5811
|
-
const images = await Promise.all(
|
|
5812
|
-
|
|
5813
|
-
|
|
5814
|
-
|
|
5811
|
+
// const images = await Promise.all(
|
|
5812
|
+
// imageFiles.map(async (filePath) => {
|
|
5813
|
+
// const img_ret = await imageToPngBlob(filePath);
|
|
5814
|
+
// const blob = img_ret.image_blob;
|
|
5815
5815
|
|
|
5816
|
-
|
|
5817
|
-
|
|
5818
|
-
|
|
5819
|
-
|
|
5820
|
-
|
|
5816
|
+
// // OpenAI expects a filename for logging/debugging
|
|
5817
|
+
// const filename = path.basename(filePath, path.extname(filePath)) + '.png';
|
|
5818
|
+
// blob.name = filename; // Optional but helpful
|
|
5819
|
+
// // @ts-ignore – name is allowed in Node 20+
|
|
5820
|
+
// blob.lastModified = Date.now();
|
|
5821
5821
|
|
|
5822
|
-
|
|
5823
|
-
|
|
5824
|
-
);
|
|
5822
|
+
// return { is_transparent: img_ret.is_transparent, image_blob: blob };
|
|
5823
|
+
// }),
|
|
5824
|
+
// );
|
|
5825
5825
|
|
|
5826
5826
|
let imageBase64;
|
|
5827
|
+
const image_blob_ret = await get_image_blob_from_downloaded_image(url);
|
|
5827
5828
|
let avatar_source = '';
|
|
5828
5829
|
if (prompt) {
|
|
5829
5830
|
const model = 'chatgpt-image-latest';
|
|
5830
5831
|
const ai_avatar_response = await client.images.edit({
|
|
5831
5832
|
model, // 'gpt-image-1',
|
|
5832
|
-
image:
|
|
5833
|
+
image: image_blob_ret.image_blob, //base photo
|
|
5833
5834
|
prompt,
|
|
5834
5835
|
background: 'transparent',
|
|
5835
5836
|
});
|
|
@@ -5837,13 +5838,13 @@ export const create_profile_avatar = async function (req, job_id, headers) {
|
|
|
5837
5838
|
record_chat_usage(uid, ai_avatar_response.usage.input_tokens, ai_avatar_response.usage.output_tokens, 'profile avatar', prompt, model, { url }, account_profile_info);
|
|
5838
5839
|
} else {
|
|
5839
5840
|
//////////////////
|
|
5840
|
-
imageBase64 = Buffer.from(await
|
|
5841
|
+
imageBase64 = Buffer.from(await image_blob_ret.image_blob.arrayBuffer()).toString('base64');
|
|
5841
5842
|
if (account_obj.account_info.account_type === 'business') {
|
|
5842
5843
|
// create avatar from logo - BUSINESS
|
|
5843
5844
|
const { bio, business_name, country, mainCategory, subCategory } = account_obj.account_info;
|
|
5844
5845
|
// const ai_avatar_response = await client.images.edit({
|
|
5845
5846
|
// model: 'chatgpt-image-latest',
|
|
5846
|
-
// image:
|
|
5847
|
+
// image: image_blob_ret.image_blob, // only one image allowed
|
|
5847
5848
|
// prompt: `Generate a realistic fictional portrait wearing a hat. The portrait should be centered, head-and-shoulders only, on a transparent background. Place the provided logo on the hat, and ensure the subject is large enough to fill most of the frame. Use the supplied name, country, and context to guide the character’s appearance.
|
|
5848
5849
|
// Context: ${JSON.stringify({ bio, business_name, country, mainCategory, subCategory })}`,
|
|
5849
5850
|
// background: 'transparent',
|
|
@@ -5851,7 +5852,7 @@ export const create_profile_avatar = async function (req, job_id, headers) {
|
|
|
5851
5852
|
///////////////////
|
|
5852
5853
|
// const ai_avatar_response = await client.images.edit({
|
|
5853
5854
|
// model: 'chatgpt-image-latest',
|
|
5854
|
-
// image:
|
|
5855
|
+
// image: image_blob_ret.image_blob, //base photo
|
|
5855
5856
|
// prompt: `Create a transparent PNG profile image for a business account featuring building branded as ${business_name}.
|
|
5856
5857
|
// Display the attached logo clearly as prominent exterior signage.
|
|
5857
5858
|
// The building’s architecture and overall atmosphere should reflect the nature of the business, aligning with ${mainCategory} and ${subCategory}.
|
|
@@ -5899,7 +5900,7 @@ export const create_profile_avatar = async function (req, job_id, headers) {
|
|
|
5899
5900
|
const model = 'chatgpt-image-latest';
|
|
5900
5901
|
const ai_avatar_response = await client.images.edit({
|
|
5901
5902
|
model,
|
|
5902
|
-
image:
|
|
5903
|
+
image: image_blob_ret.image_blob, //logo
|
|
5903
5904
|
prompt: `
|
|
5904
5905
|
Create a transparent PNG profile image for a business account featuring building branded as ${business_name}.
|
|
5905
5906
|
Display the attached logo clearly as prominent exterior signage.
|
|
@@ -5922,7 +5923,7 @@ export const create_profile_avatar = async function (req, job_id, headers) {
|
|
|
5922
5923
|
const model = 'chatgpt-image-latest';
|
|
5923
5924
|
const ai_avatar_response = await client.images.edit({
|
|
5924
5925
|
model,
|
|
5925
|
-
image:
|
|
5926
|
+
image: image_blob_ret.image_blob,
|
|
5926
5927
|
|
|
5927
5928
|
prompt: `Completely remove the background, enhance and color-correct the subject, and return only a centered head-and-shoulders portrait facing the camera. Add extra top margin, include no labels or text, and export the result as a clean transparent PNG.
|
|
5928
5929
|
Ensure the subject is large enough to fill most of the frame. Use the supplied name, country, and context to guide the character’s appearance.
|
|
@@ -5945,9 +5946,9 @@ export const create_profile_avatar = async function (req, job_id, headers) {
|
|
|
5945
5946
|
// personal account
|
|
5946
5947
|
const is_real_person_in_picture = await detect_real_person_in_image(imageBase64);
|
|
5947
5948
|
if (is_real_person_in_picture) {
|
|
5948
|
-
if (
|
|
5949
|
+
if (image_blob_ret.is_transparent) {
|
|
5949
5950
|
// keep the original
|
|
5950
|
-
imageBase64 = Buffer.from(await
|
|
5951
|
+
imageBase64 = Buffer.from(await image_blob_ret.image_blob.arrayBuffer()).toString('base64');
|
|
5951
5952
|
avatar_source = 'authentic profile';
|
|
5952
5953
|
} else {
|
|
5953
5954
|
// create avatar from profile picture
|
|
@@ -6194,72 +6195,76 @@ const delete_depended_chats = async function (uid, reference_id) {
|
|
|
6194
6195
|
};
|
|
6195
6196
|
|
|
6196
6197
|
const create_ai_agent_image = async function (req, job_id, headers) {
|
|
6197
|
-
|
|
6198
|
-
|
|
6199
|
-
|
|
6200
|
-
const normalizedBuffer = await sharp(inputBuffer)
|
|
6201
|
-
.resize(1024, 1024, {
|
|
6202
|
-
fit: 'contain', // Preserve aspect ratio, fit within 211x211
|
|
6203
|
-
background: { r: 0, g: 0, b: 0, alpha: 0 }, // Transparent background for padding
|
|
6204
|
-
})
|
|
6205
|
-
.png({
|
|
6206
|
-
quality: 100,
|
|
6207
|
-
compressionLevel: 9,
|
|
6208
|
-
force: true,
|
|
6209
|
-
})
|
|
6210
|
-
.toBuffer();
|
|
6211
|
-
|
|
6212
|
-
return normalizedBuffer.toString('base64'); // Return normalized base64 string
|
|
6213
|
-
}
|
|
6214
|
-
|
|
6215
|
-
async function hasTransparentBackground(filePath) {
|
|
6216
|
-
const image = sharp(filePath);
|
|
6217
|
-
const metadata = await image.metadata();
|
|
6218
|
-
|
|
6219
|
-
// If the image doesn't have an alpha channel at all → no transparency possible
|
|
6220
|
-
if (!metadata.hasAlpha) {
|
|
6221
|
-
return false;
|
|
6222
|
-
}
|
|
6223
|
-
|
|
6224
|
-
// For images with alpha channel, check if any pixel actually has alpha < 255
|
|
6225
|
-
const { data, info } = await image.raw().toBuffer({ resolveWithObject: true });
|
|
6226
|
-
|
|
6227
|
-
// Determine position of alpha channel based on number of channels
|
|
6228
|
-
const channels = info.channels; // 2 (grayscale+alpha), 4 (RGBA), etc.
|
|
6229
|
-
const alphaOffset = channels === 2 ? 1 : channels === 4 ? 3 : -1;
|
|
6230
|
-
|
|
6231
|
-
if (alphaOffset === -1) {
|
|
6232
|
-
// Unusual channel count – be conservative and assume it might have transparency
|
|
6233
|
-
return true;
|
|
6234
|
-
}
|
|
6235
|
-
|
|
6236
|
-
for (let i = alphaOffset; i < data.length; i += channels) {
|
|
6237
|
-
if (data[i] < 255) {
|
|
6238
|
-
return true; // Found at least one transparent/semi-transparent pixel
|
|
6239
|
-
}
|
|
6240
|
-
}
|
|
6241
|
-
|
|
6242
|
-
return false; // Has alpha channel but all pixels are fully opaque
|
|
6243
|
-
}
|
|
6244
|
-
|
|
6245
|
-
async function imageToPngBlob(filePath) {
|
|
6246
|
-
const ext = path.extname(filePath).toLowerCase();
|
|
6247
|
-
|
|
6248
|
-
let buffer;
|
|
6249
|
-
|
|
6250
|
-
let is_transparent = false;
|
|
6251
|
-
if (ext === '.png') {
|
|
6252
|
-
buffer = await fs.promises.readFile(filePath);
|
|
6253
|
-
is_transparent = await hasTransparentBackground(filePath);
|
|
6254
|
-
} else {
|
|
6255
|
-
console.log(`Converting ${path.basename(filePath)} to PNG...`);
|
|
6256
|
-
buffer = await sharp(filePath).png({ quality: 100, compressionLevel: 9, force: true }).toBuffer();
|
|
6257
|
-
}
|
|
6258
|
-
|
|
6259
|
-
// Create a Blob with correct MIME type
|
|
6198
|
+
const tempDir = os.tmpdir();
|
|
6199
|
+
const uniqueId = Date.now() + '_' + Math.random().toString(36).substring(7);
|
|
6200
|
+
const tempOutputPath = path.join(tempDir, `output_${uniqueId}.png`);
|
|
6260
6201
|
|
|
6261
|
-
|
|
6262
|
-
|
|
6202
|
+
// async function normalizeBase64To1024(base64Image) {
|
|
6203
|
+
// const inputBuffer = Buffer.from(base64Image, 'base64');
|
|
6204
|
+
|
|
6205
|
+
// const normalizedBuffer = await sharp(inputBuffer)
|
|
6206
|
+
// .resize(1024, 1024, {
|
|
6207
|
+
// fit: 'contain', // Preserve aspect ratio, fit within 211x211
|
|
6208
|
+
// background: { r: 0, g: 0, b: 0, alpha: 0 }, // Transparent background for padding
|
|
6209
|
+
// })
|
|
6210
|
+
// .png({
|
|
6211
|
+
// quality: 100,
|
|
6212
|
+
// compressionLevel: 9,
|
|
6213
|
+
// force: true,
|
|
6214
|
+
// })
|
|
6215
|
+
// .toBuffer();
|
|
6216
|
+
|
|
6217
|
+
// return normalizedBuffer.toString('base64'); // Return normalized base64 string
|
|
6218
|
+
// }
|
|
6219
|
+
|
|
6220
|
+
// async function hasTransparentBackground(filePath) {
|
|
6221
|
+
// const image = sharp(filePath);
|
|
6222
|
+
// const metadata = await image.metadata();
|
|
6223
|
+
|
|
6224
|
+
// // If the image doesn't have an alpha channel at all → no transparency possible
|
|
6225
|
+
// if (!metadata.hasAlpha) {
|
|
6226
|
+
// return false;
|
|
6227
|
+
// }
|
|
6228
|
+
|
|
6229
|
+
// // For images with alpha channel, check if any pixel actually has alpha < 255
|
|
6230
|
+
// const { data, info } = await image.raw().toBuffer({ resolveWithObject: true });
|
|
6231
|
+
|
|
6232
|
+
// // Determine position of alpha channel based on number of channels
|
|
6233
|
+
// const channels = info.channels; // 2 (grayscale+alpha), 4 (RGBA), etc.
|
|
6234
|
+
// const alphaOffset = channels === 2 ? 1 : channels === 4 ? 3 : -1;
|
|
6235
|
+
|
|
6236
|
+
// if (alphaOffset === -1) {
|
|
6237
|
+
// // Unusual channel count – be conservative and assume it might have transparency
|
|
6238
|
+
// return true;
|
|
6239
|
+
// }
|
|
6240
|
+
|
|
6241
|
+
// for (let i = alphaOffset; i < data.length; i += channels) {
|
|
6242
|
+
// if (data[i] < 255) {
|
|
6243
|
+
// return true; // Found at least one transparent/semi-transparent pixel
|
|
6244
|
+
// }
|
|
6245
|
+
// }
|
|
6246
|
+
|
|
6247
|
+
// return false; // Has alpha channel but all pixels are fully opaque
|
|
6248
|
+
// }
|
|
6249
|
+
|
|
6250
|
+
// async function imageToPngBlob(filePath) {
|
|
6251
|
+
// const ext = path.extname(filePath).toLowerCase();
|
|
6252
|
+
|
|
6253
|
+
// let buffer;
|
|
6254
|
+
|
|
6255
|
+
// let is_transparent = false;
|
|
6256
|
+
// if (ext === '.png') {
|
|
6257
|
+
// buffer = await fs.promises.readFile(filePath);
|
|
6258
|
+
// is_transparent = await hasTransparentBackground(filePath);
|
|
6259
|
+
// } else {
|
|
6260
|
+
// console.log(`Converting ${path.basename(filePath)} to PNG...`);
|
|
6261
|
+
// buffer = await sharp(filePath).png({ quality: 100, compressionLevel: 9, force: true }).toBuffer();
|
|
6262
|
+
// }
|
|
6263
|
+
|
|
6264
|
+
// // Create a Blob with correct MIME type
|
|
6265
|
+
|
|
6266
|
+
// return { is_transparent, image_blob: new Blob([buffer], { type: 'image/png' }) };
|
|
6267
|
+
// }
|
|
6263
6268
|
|
|
6264
6269
|
const { app_id, uid, ai_agent_id, tags, account_profile_info } = req;
|
|
6265
6270
|
|
|
@@ -6308,55 +6313,55 @@ const create_ai_agent_image = async function (req, job_id, headers) {
|
|
|
6308
6313
|
let url = account_doc.account_info.profile_avatar;
|
|
6309
6314
|
|
|
6310
6315
|
try {
|
|
6311
|
-
if (!url) {
|
|
6312
|
-
|
|
6313
|
-
}
|
|
6316
|
+
// if (!url) {
|
|
6317
|
+
// throw new Error('No URL provided in request');
|
|
6318
|
+
// }
|
|
6314
6319
|
|
|
6315
|
-
// 3. Download the image to a Buffer
|
|
6316
|
-
const res = await fetch(url);
|
|
6317
|
-
if (!res.ok) {
|
|
6318
|
-
|
|
6319
|
-
}
|
|
6320
|
+
// // 3. Download the image to a Buffer
|
|
6321
|
+
// const res = await fetch(url);
|
|
6322
|
+
// if (!res.ok) {
|
|
6323
|
+
// throw new Error(`Failed to fetch image URL: ${res.status} ${res.statusText}`);
|
|
6324
|
+
// }
|
|
6320
6325
|
|
|
6321
|
-
const arrayBuffer = await res.arrayBuffer();
|
|
6322
|
-
const imageBuffer = Buffer.from(arrayBuffer);
|
|
6326
|
+
// const arrayBuffer = await res.arrayBuffer();
|
|
6327
|
+
// const imageBuffer = Buffer.from(arrayBuffer);
|
|
6323
6328
|
|
|
6324
|
-
const tempDir = os.tmpdir();
|
|
6325
|
-
const uniqueId = Date.now() + '_' + Math.random().toString(36).substring(7);
|
|
6329
|
+
// const tempDir = os.tmpdir();
|
|
6330
|
+
// const uniqueId = Date.now() + '_' + Math.random().toString(36).substring(7);
|
|
6326
6331
|
|
|
6327
|
-
const inputExt = path.extname(url) || '.jpg';
|
|
6328
|
-
const tempInputPath = path.join(tempDir, `input_${uniqueId}${inputExt}`);
|
|
6329
|
-
const tempOutputPath = path.join(tempDir, `output_${uniqueId}.png`); // Output is always PNG (transparent)
|
|
6332
|
+
// const inputExt = path.extname(url) || '.jpg';
|
|
6333
|
+
// const tempInputPath = path.join(tempDir, `input_${uniqueId}${inputExt}`);
|
|
6334
|
+
// const tempOutputPath = path.join(tempDir, `output_${uniqueId}.png`); // Output is always PNG (transparent)
|
|
6330
6335
|
|
|
6331
6336
|
// 5. Write the downloaded image to disk
|
|
6332
|
-
fs.writeFileSync(tempInputPath, imageBuffer);
|
|
6337
|
+
// fs.writeFileSync(tempInputPath, imageBuffer);
|
|
6333
6338
|
/////////////////////////////////
|
|
6334
6339
|
|
|
6335
|
-
const imageFiles = [tempInputPath];
|
|
6340
|
+
// const imageFiles = [tempInputPath];
|
|
6336
6341
|
|
|
6337
|
-
const images = await Promise.all(
|
|
6338
|
-
|
|
6339
|
-
|
|
6340
|
-
|
|
6342
|
+
// const images = await Promise.all(
|
|
6343
|
+
// imageFiles.map(async (filePath) => {
|
|
6344
|
+
// const img_ret = await imageToPngBlob(filePath);
|
|
6345
|
+
// const blob = img_ret.image_blob;
|
|
6341
6346
|
|
|
6342
|
-
|
|
6343
|
-
|
|
6344
|
-
|
|
6345
|
-
|
|
6346
|
-
|
|
6347
|
+
// // OpenAI expects a filename for logging/debugging
|
|
6348
|
+
// const filename = path.basename(filePath, path.extname(filePath)) + '.png';
|
|
6349
|
+
// blob.name = filename; // Optional but helpful
|
|
6350
|
+
// // @ts-ignore – name is allowed in Node 20+
|
|
6351
|
+
// blob.lastModified = Date.now();
|
|
6347
6352
|
|
|
6348
|
-
|
|
6349
|
-
|
|
6350
|
-
);
|
|
6353
|
+
// return { is_transparent: img_ret.is_transparent, image_blob: blob };
|
|
6354
|
+
// }),
|
|
6355
|
+
// );
|
|
6351
6356
|
|
|
6352
6357
|
let imageBase64;
|
|
6353
|
-
|
|
6358
|
+
const image_blob_ret = await get_image_blob_from_downloaded_image(url);
|
|
6354
6359
|
//////////////////
|
|
6355
|
-
imageBase64 = Buffer.from(await
|
|
6360
|
+
imageBase64 = Buffer.from(await image_blob_ret.image_blob.arrayBuffer()).toString('base64');
|
|
6356
6361
|
const model = 'chatgpt-image-latest';
|
|
6357
6362
|
const ai_avatar_response = await client.images.edit({
|
|
6358
6363
|
model,
|
|
6359
|
-
image:
|
|
6364
|
+
image: image_blob_ret.image_blob,
|
|
6360
6365
|
|
|
6361
6366
|
prompt: `
|
|
6362
6367
|
Create a futuristic, profile portrait with a transparent background.
|
|
@@ -6752,7 +6757,7 @@ const get_profile_image = async function (uid, account_type = 'business', name,
|
|
|
6752
6757
|
.replace(/^_+|_+$/g, ''); // trim leading/trailing underscores
|
|
6753
6758
|
}
|
|
6754
6759
|
|
|
6755
|
-
let prompt, factual_prompt, originalname, factual_image_mode, imageBase64,
|
|
6760
|
+
let prompt, factual_prompt, originalname, factual_image_mode, imageBase64, image_blob_ret, file;
|
|
6756
6761
|
|
|
6757
6762
|
switch (account_type) {
|
|
6758
6763
|
case 'personal': {
|
|
@@ -6804,7 +6809,7 @@ const get_profile_image = async function (uid, account_type = 'business', name,
|
|
|
6804
6809
|
// success
|
|
6805
6810
|
|
|
6806
6811
|
try {
|
|
6807
|
-
|
|
6812
|
+
image_blob_ret = await get_image_blob_from_downloaded_image(url);
|
|
6808
6813
|
} catch (err) {
|
|
6809
6814
|
if (!err.message.includes('Failed to fetch image URL')) {
|
|
6810
6815
|
console.error(err);
|
|
@@ -6823,7 +6828,7 @@ const get_profile_image = async function (uid, account_type = 'business', name,
|
|
|
6823
6828
|
`;
|
|
6824
6829
|
const image_response = await client.images.edit({
|
|
6825
6830
|
model,
|
|
6826
|
-
image: image_blob,
|
|
6831
|
+
image: image_blob_ret.image_blob,
|
|
6827
6832
|
prompt,
|
|
6828
6833
|
background: 'transparent',
|
|
6829
6834
|
});
|
|
@@ -6964,5 +6969,5 @@ const get_image_blob_from_downloaded_image = async function (url) {
|
|
|
6964
6969
|
}),
|
|
6965
6970
|
);
|
|
6966
6971
|
|
|
6967
|
-
return images[0]
|
|
6972
|
+
return images[0];
|
|
6968
6973
|
};
|