yuanjiajun commited on
Commit ·
7262592
1
Parent(s): 1660a7d
完成
Browse files- package.json +1 -0
- src/service/article-service.ts +24 -34
- src/utils/common.ts +22 -26
- src/utils/file-utils.ts +5 -0
package.json
CHANGED
|
@@ -19,6 +19,7 @@
|
|
| 19 |
"axios": "^1.7.7",
|
| 20 |
"fs": "0.0.1-security",
|
| 21 |
"html-docx-js": "^0.3.1",
|
|
|
|
| 22 |
"koa": "^2.15.3",
|
| 23 |
"koa-body": "^6.0.1",
|
| 24 |
"koa-bodyparser": "^4.4.1",
|
|
|
|
| 19 |
"axios": "^1.7.7",
|
| 20 |
"fs": "0.0.1-security",
|
| 21 |
"html-docx-js": "^0.3.1",
|
| 22 |
+
"html-to-docx": "^1.8.0",
|
| 23 |
"koa": "^2.15.3",
|
| 24 |
"koa-body": "^6.0.1",
|
| 25 |
"koa-bodyparser": "^4.4.1",
|
src/service/article-service.ts
CHANGED
|
@@ -1,29 +1,30 @@
|
|
| 1 |
import fs from 'fs';
|
| 2 |
import htmlDocx from 'html-docx-js';
|
| 3 |
import path from 'path';
|
|
|
|
| 4 |
|
| 5 |
import { requestQw, requestQwImage } from '@/utils';
|
| 6 |
|
| 7 |
const uploadDir = path.join(__dirname, '../../uploads');
|
| 8 |
|
| 9 |
-
async function getImageBufferByText(text: string
|
| 10 |
console.log(`------------ 开始获取图片,原文案:${text} ---------------`);
|
| 11 |
|
| 12 |
const summary = await requestQw({
|
| 13 |
-
|
| 14 |
-
|
| 15 |
-
|
| 16 |
-
|
| 17 |
-
|
| 18 |
],
|
| 19 |
-
|
| 20 |
-
})
|
| 21 |
console.log(`summary:${summary}`);
|
| 22 |
const imageBase64 = await requestQwImage({
|
| 23 |
-
|
| 24 |
-
|
| 25 |
-
|
| 26 |
-
})
|
| 27 |
|
| 28 |
console.log('------------- 图片Base64获取成功 ---------------');
|
| 29 |
return imageBase64;
|
|
@@ -41,24 +42,22 @@ export const processArticleServe = async (data: { title: string; content: string
|
|
| 41 |
// 将富文本内容分割成段落数组
|
| 42 |
const paragraphs = content.split('\n').filter((p) => p.trim() !== '');
|
| 43 |
|
| 44 |
-
let htmlWithImages =
|
| 45 |
|
| 46 |
// await preheat(config.hfApiKey);
|
| 47 |
|
| 48 |
// 遍历段落,根据段落内容调用接口生成图片并插入到HTML中
|
| 49 |
for (let i = 0; i < paragraphs.length; i++) {
|
| 50 |
-
|
| 51 |
-
// 确定用于生成图片的描述内容,除了第一段,其余为上一段的内容
|
| 52 |
-
const description = i === 0 ? paragraphs[i] : paragraphs[i - 1];
|
| 53 |
-
|
| 54 |
try {
|
| 55 |
// 如果是文章开头(i === 0)或者符合每两或三个段落插入图片的规则(且不在最后两段内)
|
| 56 |
if (i === 0 || (i % 3 === 0 && i <= paragraphs.length - 2)) {
|
| 57 |
-
const
|
| 58 |
-
|
| 59 |
-
|
| 60 |
const base64ImageUri = `data:image/png;base64,${imageBase64}`; // 根据图像格式调整此处的 mime 类型
|
|
|
|
| 61 |
htmlWithImages += `<img src="${base64ImageUri}" alt="Generated Image"><br>`;
|
|
|
|
| 62 |
}
|
| 63 |
} catch (error: any) {
|
| 64 |
console.error('Error calling API to generate image:', error);
|
|
@@ -67,31 +66,22 @@ export const processArticleServe = async (data: { title: string; content: string
|
|
| 67 |
htmlWithImages += `<p>${paragraphs[i]}</p>`;
|
| 68 |
}
|
| 69 |
|
| 70 |
-
|
| 71 |
-
<!DOCTYPE html>
|
| 72 |
-
<html>
|
| 73 |
-
<head>
|
| 74 |
-
<meta charset="utf-8">
|
| 75 |
-
<title>${title}</title>
|
| 76 |
-
</head>
|
| 77 |
-
<body>
|
| 78 |
-
${htmlWithImages}
|
| 79 |
-
</body>
|
| 80 |
-
</html>
|
| 81 |
-
`;
|
| 82 |
|
| 83 |
if (config.output === 'html') {
|
| 84 |
return {
|
| 85 |
-
article:
|
| 86 |
};
|
| 87 |
}
|
| 88 |
|
| 89 |
if (config.output === 'docx') {
|
| 90 |
const outputFilename = `docx-${Date.now()}.docx`;
|
| 91 |
const outputPath = path.join(uploadDir, outputFilename);
|
| 92 |
-
const
|
|
|
|
| 93 |
const docxBuffer = Buffer.from(arrayBuffer);
|
| 94 |
fs.writeFileSync(outputPath, docxBuffer);
|
|
|
|
| 95 |
return {
|
| 96 |
article: outputFilename,
|
| 97 |
};
|
|
|
|
| 1 |
import fs from 'fs';
|
| 2 |
import htmlDocx from 'html-docx-js';
|
| 3 |
import path from 'path';
|
| 4 |
+
import { blobToArrayBuffer } from '@/utils';
|
| 5 |
|
| 6 |
import { requestQw, requestQwImage } from '@/utils';
|
| 7 |
|
| 8 |
const uploadDir = path.join(__dirname, '../../uploads');
|
| 9 |
|
| 10 |
+
async function getImageBufferByText(text: string) {
|
| 11 |
console.log(`------------ 开始获取图片,原文案:${text} ---------------`);
|
| 12 |
|
| 13 |
const summary = await requestQw({
|
| 14 |
+
messages: [
|
| 15 |
+
{
|
| 16 |
+
role: 'user',
|
| 17 |
+
content: `简化内容提炼核心,控制在八个字内:${text}`,
|
| 18 |
+
},
|
| 19 |
],
|
| 20 |
+
model: 'qwen-plus',
|
| 21 |
+
});
|
| 22 |
console.log(`summary:${summary}`);
|
| 23 |
const imageBase64 = await requestQwImage({
|
| 24 |
+
prompt: `中国现代:${summary}`,
|
| 25 |
+
model: 'qwen-vl-plus',
|
| 26 |
+
response_format: 'b64_json',
|
| 27 |
+
});
|
| 28 |
|
| 29 |
console.log('------------- 图片Base64获取成功 ---------------');
|
| 30 |
return imageBase64;
|
|
|
|
| 42 |
// 将富文本内容分割成段落数组
|
| 43 |
const paragraphs = content.split('\n').filter((p) => p.trim() !== '');
|
| 44 |
|
| 45 |
+
let htmlWithImages = `<h1>${title}</h1>`;
|
| 46 |
|
| 47 |
// await preheat(config.hfApiKey);
|
| 48 |
|
| 49 |
// 遍历段落,根据段落内容调用接口生成图片并插入到HTML中
|
| 50 |
for (let i = 0; i < paragraphs.length; i++) {
|
|
|
|
|
|
|
|
|
|
|
|
|
| 51 |
try {
|
| 52 |
// 如果是文章开头(i === 0)或者符合每两或三个段落插入图片的规则(且不在最后两段内)
|
| 53 |
if (i === 0 || (i % 3 === 0 && i <= paragraphs.length - 2)) {
|
| 54 |
+
const text = i ? paragraphs[i - 3] + paragraphs[i - 2] + paragraphs[i - 1] : title;
|
| 55 |
+
|
| 56 |
+
const imageBase64 = await getImageBufferByText(text);
|
| 57 |
const base64ImageUri = `data:image/png;base64,${imageBase64}`; // 根据图像格式调整此处的 mime 类型
|
| 58 |
+
// 创建 Base64 数据 URI
|
| 59 |
htmlWithImages += `<img src="${base64ImageUri}" alt="Generated Image"><br>`;
|
| 60 |
+
console.log(htmlWithImages);
|
| 61 |
}
|
| 62 |
} catch (error: any) {
|
| 63 |
console.error('Error calling API to generate image:', error);
|
|
|
|
| 66 |
htmlWithImages += `<p>${paragraphs[i]}</p>`;
|
| 67 |
}
|
| 68 |
|
| 69 |
+
console.log(htmlWithImages);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 70 |
|
| 71 |
if (config.output === 'html') {
|
| 72 |
return {
|
| 73 |
+
article: htmlWithImages,
|
| 74 |
};
|
| 75 |
}
|
| 76 |
|
| 77 |
if (config.output === 'docx') {
|
| 78 |
const outputFilename = `docx-${Date.now()}.docx`;
|
| 79 |
const outputPath = path.join(uploadDir, outputFilename);
|
| 80 |
+
const blob = htmlDocx.asBlob(htmlWithImages) as Blob;
|
| 81 |
+
const arrayBuffer = (await blobToArrayBuffer(blob)) as ArrayBuffer;
|
| 82 |
const docxBuffer = Buffer.from(arrayBuffer);
|
| 83 |
fs.writeFileSync(outputPath, docxBuffer);
|
| 84 |
+
|
| 85 |
return {
|
| 86 |
article: outputFilename,
|
| 87 |
};
|
src/utils/common.ts
CHANGED
|
@@ -20,36 +20,32 @@ export async function retryAsync(func: () => Promise<any>, x = 3, timeout = 10 *
|
|
| 20 |
throw new Error(`Async function failed after ${x} attempts.`);
|
| 21 |
}
|
| 22 |
|
| 23 |
-
export async function requestQw(data:any) {
|
| 24 |
-
const qwToken = 'sBy1ogROHqapzX0CcdoyjCl$7wAX1NzRpOPRUMrQGgN8J7jSxmMQWreOgkeheTFbylzpf8Gz1g_n0'
|
| 25 |
-
|
| 26 |
-
const response = await axios.post(
|
| 27 |
-
|
| 28 |
-
|
| 29 |
-
|
| 30 |
-
headers: {
|
| 31 |
-
'Content-Type': 'application/json',
|
| 32 |
-
Authorization: `Bearer ${qwToken}`,
|
| 33 |
-
},
|
| 34 |
},
|
| 35 |
-
);
|
| 36 |
|
| 37 |
return response.data.choices[0].message.content;
|
| 38 |
}
|
| 39 |
|
| 40 |
-
export async function requestQwImage(data:any) {
|
| 41 |
-
const qwToken = 'sBy1ogROHqapzX0CcdoyjCl$7wAX1NzRpOPRUMrQGgN8J7jSxmMQWreOgkeheTFbylzpf8Gz1g_n0'
|
| 42 |
-
|
| 43 |
-
const response = await axios.post(
|
| 44 |
-
|
| 45 |
-
|
| 46 |
-
|
| 47 |
-
headers: {
|
| 48 |
-
'Content-Type': 'application/json',
|
| 49 |
-
Authorization: `Bearer ${qwToken}`,
|
| 50 |
-
},
|
| 51 |
},
|
| 52 |
-
);
|
| 53 |
|
| 54 |
-
|
| 55 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 20 |
throw new Error(`Async function failed after ${x} attempts.`);
|
| 21 |
}
|
| 22 |
|
| 23 |
+
export async function requestQw(data: any) {
|
| 24 |
+
const qwToken = 'sBy1ogROHqapzX0CcdoyjCl$7wAX1NzRpOPRUMrQGgN8J7jSxmMQWreOgkeheTFbylzpf8Gz1g_n0';
|
| 25 |
+
|
| 26 |
+
const response = await axios.post('https://Joey7938-joe-qw-api.hf.space/api/chat/completions', data, {
|
| 27 |
+
headers: {
|
| 28 |
+
'Content-Type': 'application/json',
|
| 29 |
+
Authorization: `Bearer ${qwToken}`,
|
|
|
|
|
|
|
|
|
|
|
|
|
| 30 |
},
|
| 31 |
+
});
|
| 32 |
|
| 33 |
return response.data.choices[0].message.content;
|
| 34 |
}
|
| 35 |
|
| 36 |
+
export async function requestQwImage(data: any) {
|
| 37 |
+
const qwToken = 'sBy1ogROHqapzX0CcdoyjCl$7wAX1NzRpOPRUMrQGgN8J7jSxmMQWreOgkeheTFbylzpf8Gz1g_n0';
|
| 38 |
+
|
| 39 |
+
const { data: response } = await axios.post('https://Joey7938-joe-qw-api.hf.space/api/images/generations', data, {
|
| 40 |
+
headers: {
|
| 41 |
+
'Content-Type': 'application/json',
|
| 42 |
+
Authorization: `Bearer ${qwToken}`,
|
|
|
|
|
|
|
|
|
|
|
|
|
| 43 |
},
|
| 44 |
+
});
|
| 45 |
|
| 46 |
+
if (response.code !== undefined && response.code !== 0) {
|
| 47 |
+
throw new Error(response.message);
|
| 48 |
+
}
|
| 49 |
+
|
| 50 |
+
return response.data[0].b64_json;
|
| 51 |
+
}
|
src/utils/file-utils.ts
CHANGED
|
@@ -90,3 +90,8 @@ export async function getImageBuffer(text: string, apiKey: string) {
|
|
| 90 |
throw new Error(`生成图片失败,错误信息:${error}`);
|
| 91 |
}
|
| 92 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 90 |
throw new Error(`生成图片失败,错误信息:${error}`);
|
| 91 |
}
|
| 92 |
}
|
| 93 |
+
|
| 94 |
+
export async function blobToArrayBuffer(blob:Blob) {
|
| 95 |
+
const arrayBuffer = await blob.arrayBuffer();
|
| 96 |
+
return arrayBuffer;
|
| 97 |
+
}
|