首页 web前端 js教程 AWS JavaScript WordPress = 使用人工智能的有趣内容自动化策略

AWS JavaScript WordPress = 使用人工智能的有趣内容自动化策略

Jan 05, 2025 pm 08:40 PM

几个月前,我开始为一个专注于科技领域的客户合作一个关于人工智能生成内容的项目。我的职责主要是使用 WordPress 设置 SSG 作为 Headless CMS 用于 Nuxt 前端。

客户过去每周写几次关于影响该行业的不同趋势或情况的文章,为了增加网站的流量和文章的输出,他决定使用人工智能为他生成文章。

一段时间后,在正确的提示下,客户得到的信息与人类撰写的文章几乎完全匹配,很难发现它们是机器制作的。

在我开始研究不同的功能后,我会不断被问到一件特定的事情。

哎,你能更新一下这篇文章的特色图片吗?

经过两周的每日更新帖子后,我突然灵光一现。

AWS   JavaScript   WordPress = Fun Content Automation Strategies Using Artificial Intelligence

为什么我不使用人工智能自动为这些文章生成特色图像?

我们已经自动化撰写帖子,为什么不自动化精选图片?

在空闲时间,我在计算机上尝试生成法学硕士,因此我或多或少对如何解决这个支线任务有了一个扎实的想法。我向客户发送了一条消息,详细说明了问题是什么、我想要做什么以及优势是什么,无需令人信服,我就获得了使用此功能的绿灯,并立即同意了我的第一步。

1. 设计解决方案的外观。

鉴于我接触过在本地运行模型,我立即知道自行托管这些模型是不可行的。放弃这个之后,我开始尝试根据文本提示生成图像的 API。

特色图片由两部分组成:主要组成的图形和吸引人的标语。

组成的图形将是与文章相关的一些元素,以良好的方式排列,然后应用一些颜色和纹理,并应用一些混合模式来实现品牌之后的一些奇特效果。

标语是简短的 8-12 个单词的句子,下面有一个简单的阴影。

根据我的测试,我意识到追求图像生成的人工智能路线是不切实际的。图像质量没有达到预期,而且过程太耗时,无法证明其使用的合理性。考虑到这将作为 AWS Lambda 函数运行,其中执行时间直接影响成本。

放弃了这一点,我选择了 B 计划:使用 JavaScript 的 Canvas API 将图像和设计资源混合在一起。

深入观察,我们主要有 5 种风格的简单帖子,以及大约 4 种类型的纹理,其中 3 种使用相同的文本对齐方式、样式和位置。做了一些数学计算后,我想:

嗯,如果我拍摄这 3 个图像,抓取 8 个纹理并使用混合模式,我就可以解决 24 种变化

鉴于这 3 种类型的帖子具有相同的文本样式,它实际上是一个模板。

解决了这个问题后,我转向了标语生成器。我想根据文章的内容和标题创建一个口号。鉴于公司已经支付了费用,我决定使用 ChatGPT 的 API,经过一些实验和提示调整后,我的口号生成器有了一个非常好的 MVP。

弄清楚任务中最困难的 2 个部分后,我花了一些时间在 Figma 中整理出我服务的最终架构的图表。

AWS   JavaScript   WordPress = Fun Content Automation Strategies Using Artificial Intelligence

2.编写我的 lambda

计划创建一个 Lambda 函数,能够分析帖子内容、生成标语并组装特色图像 - 所有这些都与 WordPress 无缝集成。

我将提供一些代码,但足以向ke传达总体想法。

分析内容

Lambda 函数首先从传入事件负载中提取必要的参数:

const { title: request_title, content, backend, app_password} = JSON.parse(event.body);

  • 标题和内容:这些提供了文章的上下文。
  • 后端: 用于图像上传的 WordPress 后端 URL。
  • app_password: 我将使用 WordPress Rest API 以我的用户身份上传的身份验证令牌。

生成标语

该函数的第一个主要任务是使用analyzeContent函数生成标语,该函数使用OpenAI的API根据文章的标题和内容制作值得点击的标语。

我们的函数获取帖子标题和内容,但返回标语、帖子情绪以了解帖子是正面、负面还是中性意见,以及来自标准普尔指数公司的可选公司符号。

const { 口号、情感、公司 } =等待analyzeContent({ title: request_title, content });

这一步很关键,因为口号直接影响图片的美感。

创建特色图像

接下来,generateImage函数开始运行:

let buffer;

buffer = await generateImage({
    title: tagline,
    company_logo: company_logo,
    sentiment: sentiment,
});

登录后复制
登录后复制

该函数处理:

  • 设计构图。
  • 分层纹理、颜色和品牌元素。
  • 应用效果并创建标题。

以下是其工作原理的分步分析:

generateImage 函数首先设置一个空白画布,定义其尺寸,并准备好处理所有设计元素。

let buffer;

buffer = await generateImage({
    title: tagline,
    company_logo: company_logo,
    sentiment: sentiment,
});

登录后复制
登录后复制

从那里,从预定义的资源集合中加载随机背景图像。这些图像经过精心设计,以适应以技术为导向的品牌,同时允许帖子之间有足够的多样性。背景图像是根据其情绪随机选择的。

为了确保每个背景图像看起来都很棒,我根据宽高比动态计算其尺寸。这样可以避免扭曲,同时保持视觉平衡完好。

添加标语

标语很短,但根据一些规则,这个有影响力的句子被分成可管理的部分,并动态设计样式以确保它始终可读,无论长度或画布大小如何(基于行的字数、字长等) .

const COLOURS = {
        BLUE: "#33b8e1",
        BLACK: "#000000",
    }

    const __filename = fileURLToPath(import.meta.url);
    const __dirname = path.dirname(__filename);
    const images_path = path.join(__dirname, 'images/');
    const files_length = fs.readdirSync(images_path).length;
    const images_folder = process.env.ENVIRONMENT === "local"
        ? "./images/" : "/var/task/images/";


    registerFont("/var/task/fonts/open-sans.bold.ttf", { family: "OpenSansBold" });
    registerFont("/var/task/fonts/open-sans.regular.ttf", { family: "OpenSans" });


    console.log("1. Created canvas");

    const canvas = createCanvas(1118, 806);

    let image = await loadImage(`${images_folder}/${Math.floor(Math.random() * (files_length - 1 + 1)) + 1}.jpg`);


    let textBlockHeight = 0;

    console.log("2. Image loaded");

    const canvasWidth = canvas.width;
    const canvasHeight = canvas.height;
    const aspectRatio = image.width / image.height;


    console.log("3. Defined ASPECT RATIO",)

    let drawWidth, drawHeight;
    if (image.width > image.height) {
        // Landscape orientation: fit by width
        drawWidth = canvasWidth;
        drawHeight = canvasWidth / aspectRatio;
    } else {
        // Portrait orientation: fit by height
        drawHeight = canvasHeight;
        drawWidth = canvasHeight * aspectRatio;
    }

    // Center the image
    const x = (canvasWidth - drawWidth) / 2;
    const y = (canvasHeight - drawHeight) / 2;
    const ctx = canvas.getContext("2d");
    console.log("4. Centered Image")
    ctx.drawImage(image, x, y, drawWidth, drawHeight);

登录后复制

最后,画布被转换为 PNG 缓冲区。

console.log("4.1 Text splitting");
if (splitText.length === 1) {

    const isItWiderThanHalf = ctx.measureText(splitText[0]).width > ((canvasWidth / 2) + 160);
    const wordCount = splitText[0].split(" ").length;

    if (isItWiderThanHalf && wordCount > 4) {

        const refactored_line = splitText[0].split(" ").reduce((acc, curr, i) => {
            if (i % 3 === 0) {
                acc.push([curr]);
            } else {
                acc[acc.length - 1].push(curr);
            }
            return acc;
        }, []).map((item) => item.join(" "));

        refactored_line[1] = "[s]" + refactored_line[1] + "[s]";

        splitText = refactored_line

    }
}

let tagline = splitText.filter(item => item !== '' && item !== '[br]' && item !== '[s]' && item !== '[/s]' && item !== '[s]');
let headlineSentences = [];
let lineCounter = {
    total: 0,
    reduced_line_counter: 0,
    reduced_lines_indexes: []
}

console.log("4.2 Tagline Preparation", tagline);

for (let i = 0; i < tagline.length; i++) {
    let line = tagline[i];

    if (line.includes("[s]") || line.includes("[/s]")) {

        const finalLine = line.split(/(\[s\]|\[\/s\])/).filter(item => item !== '' && item !== '[s]' && item !== '[/s]');

        const lineWidth = ctx.measureText(finalLine[0]).width
        const halfOfWidth = canvasWidth / 2;

        if (lineWidth > halfOfWidth && finalLine[0]) {

            let splitted_text = finalLine[0].split(" ").reduce((acc, curr, i) => {

                const modulus = finalLine[0].split(" ").length >= 5 ? 3 : 2;
                if (i % modulus === 0) {
                    acc.push([curr]);
                } else {
                    acc[acc.length - 1].push(curr);
                }
                return acc;
            }, []);

            let splitted_text_arr = []

            splitted_text.forEach((item, _) => {
                let lineText = item.join(" ");

                item = lineText

                splitted_text_arr.push(item)
            })

            headlineSentences[i] = splitted_text_arr[0] + '/s/'

            if (splitted_text_arr[1]) {
                headlineSentences.splice(i + 1, 0, splitted_text_arr[1] + '/s/')
            }
        } else {
            headlineSentences.push("/s/" + finalLine[0] + "/s/")
        }


    } else {
        headlineSentences.push(line)
    }
}

console.log("5. Drawing text on canvas", headlineSentences);

const headlineSentencesLength = headlineSentences.length;
let textHeightAccumulator = 0;

for (let i = 0; i < headlineSentencesLength; i++) {
    headlineSentences = headlineSentences.filter(item => item !== '/s/');
    const nextLine = headlineSentences[i + 1];
    if (nextLine && /^\s*$/.test(nextLine)) {
        headlineSentences.splice(i + 1, 1);
    }

    let line = headlineSentences[i];

    if (!line) continue;
    let lineText = line.trim();

    let textY;

    ctx.font = " 72px OpenSans";

    const cleanedUpLine = lineText.includes('/s/') ? lineText.replace(/\s+/g, ' ') : lineText;
    const lineWidth = ctx.measureText(cleanedUpLine).width
    const halfOfWidth = canvasWidth / 2;

    lineCounter.total += 1

    const isLineTooLong = lineWidth > (halfOfWidth + 50);

    if (isLineTooLong) {

        if (lineText.includes(':')) {
            const split_line_arr = lineText.split(":")
            if (split_line_arr.length > 1) {
                lineText = split_line_arr[0] + ":";
                if (split_line_arr[1]) {
                    headlineSentences.splice(i + 1, 0, split_line_arr[1])
                }
            }
        }

        ctx.font = "52px OpenSans";

        lineCounter.reduced_line_counter += 1

        if (i === 0 && headlineSentencesLength === 2) {
            is2LinesAndPreviewsWasReduced = true
        }


        lineCounter.reduced_lines_indexes.push(i)

    } else {

        if (i === 0 && headlineSentencesLength === 2) {
            is2LinesAndPreviewsWasReduced = false
        }


    }

    if (lineText.includes("/s/")) {

        lineText = lineText.replace(/\/s\//g, "");

        if (headlineSentencesLength > (i + 1) && i < headlineSentencesLength - 1 && nextLine) {

            if (nextLine.slice(0, 2).includes("?") && nextLine.length < 3) {
                lineText += '?';
                headlineSentences.pop();
            }

            if (nextLine.slice(0, 2).includes(":")) {
                lineText += ':';
                headlineSentences[i + 1] = headlineSentences[i + 1].slice(2);
            }

        }

        let lineWidth = ctx.measureText(lineText).width


        let assignedSize;


        if (lineText.split(" ").length <= 2) {

            if (lineWidth > (canvasWidth / 2.35)) {

                ctx.font = "84px OpenSansBold";

                assignedSize = 80

            } else {

                ctx.font = "84px OpenSansBold";

                assignedSize = 84

            }
        } else {


            if (i === headlineSentencesLength - 1 && lineWidth < (canvasWidth / 2.5) && lineText.split(" ").length === 3) {

                ctx.font = "84px OpenSansBold";
                assignedSize = 84

            } else {

                lineCounter.reduced_line_counter += 1;

                ctx.font = "52px OpenSansBold";
                assignedSize = 52

            }

            lineCounter.reduced_lines_indexes.push(i)

        }

        lineWidth = ctx.measureText(lineText).width



        if (lineWidth > (canvasWidth / 2) + 120) {

            if (assignedSize === 84) {
                ctx.font = "72px OpenSansBold";
            } else if (assignedSize === 80) {
                ctx.font = "64px OpenSansBold";

                textHeightAccumulator += 8
            } else {
                ctx.font = "52px OpenSansBold";
            }
        }



    } else {

        const textWidth = ctx.measureText(lineText).width


        if (textWidth > (canvasWidth / 2)) {
            ctx.font = "44px OpenSans";
            textHeightAccumulator += 12
        } else if (i === headlineSentencesLength - 1) {
            textHeightAccumulator += 12
        }

    }

    ctx.fillStyle = "white";
    ctx.textAlign = "center";

    const textHeight = ctx.measureText(lineText).emHeightAscent;

    textHeightAccumulator += textHeight;

    if (headlineSentencesLength == 3) {
        textY = (canvasHeight / 3)
    } else if (headlineSentencesLength == 4) {
        textY = (canvasHeight / 3.5)
    } else {
        textY = 300
    }

    textY += textHeightAccumulator;

    const words = lineText.split(' ');
    console.log("words", words, lineText, headlineSentences)
    const capitalizedWords = words.map(word => {
        if (word.length > 0) return word[0].toUpperCase() + word.slice(1)
        return word
    });
    const capitalizedLineText = capitalizedWords.join(' ');

    ctx.fillText(capitalizedLineText, canvasWidth / 2, textY);

}

登录后复制

最后!!!将图像上传到 WordPress

成功生成图像缓冲区后,调用 uploadImageToWordpress 函数。

此函数通过对 WordPress 图像进行编码,处理使用其 REST API 将图像发送到 WordPress 的繁重工作。

该函数首先通过清理空格和特殊字符来准备用作文件名的标语:

const buffer = canvas.toBuffer("image/png");
return buffer;
登录后复制

然后将图像缓冲区转换为 Blob 对象,以使其与 WordPress API 兼容:

const file = new Blob([buffer], { type: "image/png" });

准备 API 请求 使用编码的图像和标语,该函数构建一个 FormData 对象,并添加可选的元数据,例如用于可访问性的 alt_text 和用于上下文的标题。

const createSlug = (string) => {
    return string.toLowerCase().replace(/ /g, '-').replace(/[^\w-]+/g, '');
};

const image_name = createSlug(tagline);
登录后复制

为了进行身份验证,用户名和应用程序密码以 Base64 进行编码并包含在请求标头中:

formData.append("file", file, image_name + ".png");
formData.append("alt_text", `${tagline} image`);
formData.append("caption", "Uploaded via API");
登录后复制

发送图像使用准备好的数据和标头向 WordPress 媒体端点发出 POST 请求,并在等待响应后验证成功或错误。

const credentials = `${username}:${app_password}`;
const base64Encoded = Buffer.from(credentials).toString("base64");

登录后复制

如果成功,我会在 lambda 中返回相同的媒体响应。

这就是我的 lambda 最终的样子。

const response = await fetch(`${wordpress_url}wp-json/wp/v2/media`, {
    method: "POST",
    headers: {
        Authorization: "Basic " + base64Encoded,
        contentType: "multipart/form-data",
    },
    body: formData,
});

if (!response.ok) {
    const errorText = await response.text();
    throw new Error(`Error uploading image: ${response.statusText}, Details: ${errorText}`);
}

登录后复制

这是我的脚本生成的示例图像。它没有在生产中使用,只是使用本示例的通用资源创建。

AWS   JavaScript   WordPress = Fun Content Automation Strategies Using Artificial Intelligence

后果

一段时间过去了,每个人都很高兴我们不再有劣质或空洞的无图像文章,图像与设计师制作的图像非常匹配,设计师很高兴他只专注于为整个公司的其他营销活动进行设计。

但随后出现了一个新问题:有时客户不喜欢生成的图像,他会要求我启动脚本为特定帖子生成新图像。

这给我带来了下一个支线任务:Wordpress 插件,使用人工智能为特定帖子手动生成特色图像

以上是AWS JavaScript WordPress = 使用人工智能的有趣内容自动化策略的详细内容。更多信息请关注PHP中文网其他相关文章!

本站声明
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn

热AI工具

Undresser.AI Undress

Undresser.AI Undress

人工智能驱动的应用程序,用于创建逼真的裸体照片

AI Clothes Remover

AI Clothes Remover

用于从照片中去除衣服的在线人工智能工具。

Undress AI Tool

Undress AI Tool

免费脱衣服图片

Clothoff.io

Clothoff.io

AI脱衣机

Video Face Swap

Video Face Swap

使用我们完全免费的人工智能换脸工具轻松在任何视频中换脸!

热门文章

<🎜>:泡泡胶模拟器无穷大 - 如何获取和使用皇家钥匙
3 周前 By 尊渡假赌尊渡假赌尊渡假赌
Mandragora:巫婆树的耳语 - 如何解锁抓钩
3 周前 By 尊渡假赌尊渡假赌尊渡假赌
北端:融合系统,解释
3 周前 By 尊渡假赌尊渡假赌尊渡假赌

热工具

记事本++7.3.1

记事本++7.3.1

好用且免费的代码编辑器

SublimeText3汉化版

SublimeText3汉化版

中文版,非常好用

禅工作室 13.0.1

禅工作室 13.0.1

功能强大的PHP集成开发环境

Dreamweaver CS6

Dreamweaver CS6

视觉化网页开发工具

SublimeText3 Mac版

SublimeText3 Mac版

神级代码编辑软件(SublimeText3)

热门话题

Java教程
1668
14
CakePHP 教程
1427
52
Laravel 教程
1329
25
PHP教程
1273
29
C# 教程
1256
24
JavaScript引擎:比较实施 JavaScript引擎:比较实施 Apr 13, 2025 am 12:05 AM

不同JavaScript引擎在解析和执行JavaScript代码时,效果会有所不同,因为每个引擎的实现原理和优化策略各有差异。1.词法分析:将源码转换为词法单元。2.语法分析:生成抽象语法树。3.优化和编译:通过JIT编译器生成机器码。4.执行:运行机器码。V8引擎通过即时编译和隐藏类优化,SpiderMonkey使用类型推断系统,导致在相同代码上的性能表现不同。

Python vs. JavaScript:学习曲线和易用性 Python vs. JavaScript:学习曲线和易用性 Apr 16, 2025 am 12:12 AM

Python更适合初学者,学习曲线平缓,语法简洁;JavaScript适合前端开发,学习曲线较陡,语法灵活。1.Python语法直观,适用于数据科学和后端开发。2.JavaScript灵活,广泛用于前端和服务器端编程。

从C/C到JavaScript:所有工作方式 从C/C到JavaScript:所有工作方式 Apr 14, 2025 am 12:05 AM

从C/C 转向JavaScript需要适应动态类型、垃圾回收和异步编程等特点。1)C/C 是静态类型语言,需手动管理内存,而JavaScript是动态类型,垃圾回收自动处理。2)C/C 需编译成机器码,JavaScript则为解释型语言。3)JavaScript引入闭包、原型链和Promise等概念,增强了灵活性和异步编程能力。

JavaScript和Web:核心功能和用例 JavaScript和Web:核心功能和用例 Apr 18, 2025 am 12:19 AM

JavaScript在Web开发中的主要用途包括客户端交互、表单验证和异步通信。1)通过DOM操作实现动态内容更新和用户交互;2)在用户提交数据前进行客户端验证,提高用户体验;3)通过AJAX技术实现与服务器的无刷新通信。

JavaScript在行动中:现实世界中的示例和项目 JavaScript在行动中:现实世界中的示例和项目 Apr 19, 2025 am 12:13 AM

JavaScript在现实世界中的应用包括前端和后端开发。1)通过构建TODO列表应用展示前端应用,涉及DOM操作和事件处理。2)通过Node.js和Express构建RESTfulAPI展示后端应用。

了解JavaScript引擎:实施详细信息 了解JavaScript引擎:实施详细信息 Apr 17, 2025 am 12:05 AM

理解JavaScript引擎内部工作原理对开发者重要,因为它能帮助编写更高效的代码并理解性能瓶颈和优化策略。1)引擎的工作流程包括解析、编译和执行三个阶段;2)执行过程中,引擎会进行动态优化,如内联缓存和隐藏类;3)最佳实践包括避免全局变量、优化循环、使用const和let,以及避免过度使用闭包。

Python vs. JavaScript:社区,图书馆和资源 Python vs. JavaScript:社区,图书馆和资源 Apr 15, 2025 am 12:16 AM

Python和JavaScript在社区、库和资源方面的对比各有优劣。1)Python社区友好,适合初学者,但前端开发资源不如JavaScript丰富。2)Python在数据科学和机器学习库方面强大,JavaScript则在前端开发库和框架上更胜一筹。3)两者的学习资源都丰富,但Python适合从官方文档开始,JavaScript则以MDNWebDocs为佳。选择应基于项目需求和个人兴趣。

Python vs. JavaScript:开发环境和工具 Python vs. JavaScript:开发环境和工具 Apr 26, 2025 am 12:09 AM

Python和JavaScript在开发环境上的选择都很重要。1)Python的开发环境包括PyCharm、JupyterNotebook和Anaconda,适合数据科学和快速原型开发。2)JavaScript的开发环境包括Node.js、VSCode和Webpack,适用于前端和后端开发。根据项目需求选择合适的工具可以提高开发效率和项目成功率。

See all articles