Bing AI 生成每日图片

前段时间刷到一篇分享 liruifengv/daily-poetry-image:用Bing-AI的文生图功能结合诗词api生成对应图片。联想到之前fork的bing-wallpaper项目,就来简单改造下。

bing-wallpaper

bing-wallpaper项目比较简单,就是利用GitHub-action跑定时任务爬取Bing每日壁纸链接,随后更新README.md文档。

这个项目是用java写的,当时直接fork的niumoo/bing-wallpape,修改下ci的配置就能跑了。目前已经稳定运行快两年了,每天会跑两次防止更新遗漏,会有一次异常部署是因为当时还没更新今日壁纸。

改造的思路就是利用Bing-AI来代替Bing每日壁纸,每天都是AI-Generate图片。

bing-ai-poetry

Github 地址 Mulander-J/bing-ai-poetry

前提准备,需要有edge浏览器+账号,以及科学上网。能正常访问 https://bing.com/create,并生成一次图片,记录cookie后面会用到。

文生图的文字部分由今日诗词api随机生成。

bash 复制代码
curl https://v1.jinrishici.com/all

{
  "content" : "正蹇驴吟影,茶烟灶冷,酒亭门闭。",
  "origin" : "无闷·催雪",
  "author" : "吴文英",
  "category" : "古诗文-食物-写茶"
}

liruifengv/daily-poetry-image 的基础上,砍掉原有的前端项目(不需要前端渲染,自带README.md足够。)

diff 复制代码
- website

稍微修改入口文件 bin/cli.ts

之前在本地测试的时候,因为网络原因图片经常只能下到一两张,且只有一半的数据。修改的部分主要是将主函数套上async,然后就可以在内部await图片下载和读写事务,确保进程退出时图片不会下到一半。

另外下载好图片的同时会同步修改README.md文件,只需要append新内容就行了。

核心的getImageBySentence 随机诗词文生图方法原项目都封装好了。

ts 复制代码
import { getImageBySentence } from "../src/get-up";
import type { Response } from "../src/types";
import path from "path";
import fs from "fs";
import stream from "stream";
import { promisify } from "util";

const pipeline = promisify(stream.pipeline);

async function init() {
   const cwd = process.cwd();

   const argv = require("minimist")(process.argv.slice(2));
   if (argv.cookie && typeof argv.cookie !== 'boolean') { // 此处兼容 --cookie="xx" 和 --cookie "xx"
       try {
           // const res =  {
           //     images: [
           //         'https://tse1.mm.bing.net/th/id/OIG.0lT30AZgF0Y9TGnssoUu',
           //         'https://tse2.mm.bing.net/th/id/OIG.AYKgb1ftqrAfxCfwGjCs',
           //         'https://tse1.mm.bing.net/th/id/OIG.GcNrrihRgfhlHOwiRtR.'
           //     ],
           //     content: '关山别荡子,风月守空闺。',
           //     origin: '昔昔盐',
           //     author: '薛道衡',
           //     category: '古诗文-抒情-离别'
           // }
           const res: Response = await getImageBySentence(argv.cookie);
           console.log("Create Successful: ", res);

           const imagesPath = path.join(cwd, "images");
           if (!fs.existsSync(imagesPath)) {
               fs.mkdirSync(imagesPath);
           }

           // 在 images 目录下,创建一个以时间戳命名的文件夹,将图片放入其中
           const imagesFolderName = Date.now().toString();
           const imagesFolderPath = path.join(imagesPath, imagesFolderName);
           if (!fs.existsSync(imagesFolderPath)) {
               fs.mkdirSync(imagesFolderPath);
           }
           const images = res.images
           let imgPaths:any[] = []
           // 将图片放入 images 目录下的文件夹中
           for(let i = 0; i < images.length; i++){
                // images 中是网络url,请求图片,将图片保存到 images 目录下的文件夹中
               const imageFileName = `${i}.jpg`;
               const imageFilePath = path.join(imagesFolderPath, imageFileName);
               const imageDocPath = `./images/${imagesFolderName}/${imageFileName}`
               imgPaths.push(`![${imagesFolderName}_${imageFileName}](${imageDocPath})[${imageFileName}](${images[i]})`)

               // 下载图片
               console.log('fetching...', images[i])
               const res:any = await fetch(images[i])
               if (!res.ok) throw new Error(`unexpected response ${res.statusText}`);
               await pipeline(res.body, fs.createWriteStream(imageFilePath)).catch((e) => {
                   console.error("Something went wrong while saving the image", e);
               });
               console.log('>fetched', images[i])
           }
           console.log('Fetch Images Ended')

           const options = { timeZone: "Asia/Shanghai", hour12: false };
           const outputData = {
               ...res,
               date: new Date().toLocaleString("zh-CN", options),
               localImagesPath: imagesFolderName,
           };

           const contentFile = path.join(imagesFolderPath, 'index.json');

           fs.writeFileSync(contentFile, JSON.stringify(outputData));

           const rowHead = `> ${res.content} ------ ${res.author}《${res.origin}》\n`
           let appendCtx = `|${imgPaths.map(_=>'      ').join('|')}|\n`
           appendCtx += `|${imgPaths.map(_=>' :----: ').join('|')}|\n`
           appendCtx += `|${imgPaths.join('|')}|`
           fs.appendFileSync('./README.md', [
               '',
               rowHead,
               appendCtx,
               ''
           ].join('\n'))

           // 缓冲等待,确保未捕获的异步任务都能跑完
           console.log('Wating Sec...')
           setTimeout(() => {
               process.exit(0);
           }, 5 * 1000);
       } catch (e) {
           console.error(e);
           process.exit(1);
       }
   } else {
       throw new Error("Please provide a cookie using the --cookie argument");
   }
}

init().catch((e) => {
   console.error(e);
});

以及,处理Bing接口返回时,获取图片数据是采用正则/src="([^"]*)"/g全局匹配的,偶尔会跑出来几张https://r.bing.com/rp/xxx的乱入图片需要剔除。

ts 复制代码
    // /src/bing-image-creator.ts
    parseResult(result: string) {
        console.log("Parsing result...");
        // Use regex to search for src=""
        const regex = /src="([^"]*)"/g;
        const matches = [...result.matchAll(regex)].map((match) => match[1]);
        // Remove Bad Images(https://r.bing.com/rp/xxx)
        const safe_image_links = matches.filter(link => !/r.bing.com\/rp/i.test(link))
        // Remove size limit
        const normal_image_links = safe_image_links.map(link => (link.split("?w=")[0]));
        // Remove duplicates
        const unique_image_links = [...new Set(normal_image_links)];
        // No images
        if (unique_image_links.length === 0) {
            throw new Error("error_no_images");
        }
        return unique_image_links;
    }

完工。接下来就是actions定时跑任务了, 一切正常。

相关推荐
会发光的猪。21 分钟前
vue中el-select选择框带搜索和输入,根据用户输入的值显示下拉列表
前端·javascript·vue.js·elementui
旺旺大力包38 分钟前
【 Git 】git 的安装和使用
前端·笔记·git
雪落满地香1 小时前
前端:改变鼠标点击物体的颜色
前端
余生H1 小时前
前端Python应用指南(二)深入Flask:理解Flask的应用结构与模块化设计
前端·后端·python·flask·全栈
outstanding木槿2 小时前
JS中for循环里的ajax请求不数据
前端·javascript·react.js·ajax
酥饼~2 小时前
html固定头和第一列简单例子
前端·javascript·html
一只不会编程的猫2 小时前
高德地图自定义折线矢量图形
前端·vue.js·vue
m0_748250932 小时前
html 通用错误页面
前端·html
来吧~2 小时前
vue3使用video-player实现视频播放(可拖动视频窗口、调整大小)
前端·vue.js·音视频
han_2 小时前
不是哥们,我的console.log突然打印不出东西了!
前端·javascript·chrome