赞
踩
Stable Diffusion是一个扩散模型,最早由德国CompVis发表并推出相关程式。后来AUTOMATIC1111开发了图形化界面:【stable diffusion web ui】,成为最多人使用的版本。下面简称SD WebUI。
简单来说,SD WebUI是能用AI技术绘图的开源软件,只要給定一組描述文字,AI就会画图出来;亦能模仿現有的图片,画出另一張图片。甚至給它一部分涂黑的圖片,AI也能按照你的意愿將图片填上适当的內容。除此之外还支援自行訓練模型加強绘制效果。
与其他的AI绘图软件相比,SD最大的优势在于:
其中,SD WebUI支持Linux、Windows、MacOS系统,以及Nvidia/AMD/Apple Silicon M的GPU。其图形界面可通过局域网IP进行网页访问的,上手无难度,还有社群制作的中文界面。
不过需要注意的时,AI绘图十分吃GPU性能,以Windows系统为例,建议电脑操作系统在Windows10以上,需配置独立显卡为Nvidia GTX1050 Ti (VRAM 4GB)以上等級,RAM大于8GB。
但是Stable Diffusion生成的图像只能保存在本地,并没有一键保存到云端的功能,如果需要保存在云端,我还需要再从本地文件夹中将图片上传到云端。
且我们都生成了这么多美丽的图片,却没有一个系统的内容管理,俗称CMS(Content management system),如果我们进行了一个系统的内容管理,那我们可以看到他的效果如下:
你可以把它作为你的作品集。
如果我们将这个内容管理系统扩散开来,成为一个社区,所造成的影响巨大。任何人都可以简单方便地在本地将自己的内容分享到CMS中,AI By The People,AI For The People。
想要实现想要的效果,我们首先得先对原有的WebUI界面进行改造:
增加一个按钮,当我们点击它时,会将我们生成好的图片以及相关的参数上传对应的CMS中。
我们将使用Chrome插件对WebUI进行改造(后续可能会考虑直接进行Stable Diffusion WebUI的扩展开发),使用Notion作为我们的CMS(Notion作为CMS具有易用、灵活、协作和跨平台等优势,特别是其Database功能,不仅让你白嫖免费v数据库,而且界面美观,让你可以更加高效地管理你的内容)。
Notion官网
话不多说,我们直接开始进入正题!
首先,我们利用Chrome插件在我们的Stable Diffusion WebUI页面中选择一个合适的位置插入一个“发送到Notion”的按钮,如下图所示:
发送至Notion
的按钮要实现这个效果,我们首先需要编写manifest.json
:
127.0.0.1:7860
(Stable Diffusion WebUI默认地址)上运行代码如下:
{
"manifest_version": 2, // 浏览器扩展清单文件的版本号,2 表示使用 Manifest V2 版本
"name": "Stable Diffusion To Notion", // 扩展程序的名称
"version": "1.0", // 扩展程序的版本号
"description": "将网页中的特定信息发送给Notion", // 扩展程序的描述
"content_scripts": [ // 注册要注入到浏览器当前页面的脚本
{
"matches": [ // 注入的条件,数组里面的 URL 与当前页面 URL 匹配
"http://127.0.0.1:7860/*" // 要注入的页面 URL,这里匹配了所有以 http://127.0.0.1:7860/ 开头的 URL
],
"js": [ // 注入的 JavaScript 文件,这里只有一个 content.js
"content.js"
]
}
],
"content_security_policy": "script-src 'self' 'unsafe-eval'; object-src 'self'", // 安全策略
"background": { // 后台脚本
"scripts": [ // 执行的脚本文件,这里只有一个 background.js
"background.js"
],
"persistent": false // 是否常驻后台,false 表示不常驻
},
"browser_action": { // 扩展程序的浏览器操作区域,即点击图标后显示的弹出框
"default_popup": "popup.html" // 弹出框所需的 HTML 文件
},
"permissions": [ // 扩展程序需要的权限
"http://127.0.0.1:7860/", // 可以访问的 URL
"activeTab", // 可以获取当前激活的标签页的信息
"tabs", // 可以获取所有标签页的信息
"storage", // 可以使用浏览器存储 API
"https://api.notion.com/*" // 可以访问 Notion API 的 URL
"https://api.github.com/*" // 可以访问 GitHub API 的 URL
]
}
需要注意的是:
您在
manifest.json
中添加了content_security_policy
配置,该配置指定了扩展的内容安全策略。默认情况下,Chrome 扩展的内容脚本会在一个沙箱环境中运行,以防止不受信任的代码在扩展环境中执行,并且默认情况下不允许使用
eval()
等动态代码执行函数,以防止脚本注入攻击。 然而,在某些情况下,这可能会阻止一些正常的脚本行为。在您的情况下,您的
background.js
文件尝试使用chrome.runtime.sendMessage()
函数与您的content.js
文件进行通信,但是由于默认的内容安全策略阻止了eval()
函数的使用,因此该函数无法正常运行。 通过添加content_security_policy
配置并将script-src
指定为'unsafe-eval'
,您允许了动态代码执行函数的使用,因此chrome.runtime.sendMessage()
函数能够正常运行。需要注意的是,虽然在某些情况下禁用默认的内容安全策略可能是必要的,但这可能会导致您的扩展面临更大的安全风险。在使用
content_security_policy
配置时,请确保您仅允许必要的资源,并避免允许来自不受信任的源的代码执行。本回答由Chat GPT生成
编写background.js
代码:
content.js
注入到页面中content.js
的消息代码如下:
// 当插件安装时,输出一条信息到控制台
chrome.runtime.onInstalled.addListener(function () {
console.log("Extension installed");
});
// 监听标签页更新事件
chrome.tabs.onUpdated.addListener(function (tabId, changeInfo, tab) {
// 如果当前标签页的 URL 以 http://127.0.0.1:7860/ 开头且加载完成
if (tab && tab.url && changeInfo.status == "complete" && tab.url.indexOf("http://127.0.0.1:7860/") == 0) {
// 在当前标签页中注入 content.js 脚本
chrome.tabs.executeScript(tabId, { file: "content.js" });
console.log("插入content.js");
} else {
console.log("未插入content.js");
}
});
编写conten.js
代码:
从页面中获取需要发送的信息(这里主要指的是图片以及图片相关的参数)
将按钮
插入至Stable Diffusion WebUI中合适位置,并获取Web中的同类型的ClassName,并套用在嵌入的按钮元素上
获取页面元素位置:
右键,复制其JS路径,即:document.querySelector("body > gradio-app").shadowRoot.querySelector("#image_buttons_txt2img")
复制同类型的样式
代码如下:
// 监听加载完毕的事件
window.addEventListener("load", function () {
console.log("页面加载完毕,延时1s后开始插入button")
// 创建一个按钮元素
const button = document.createElement("button");
// 延迟 1 秒钟执行按钮插入操作(等待页面加载完毕)
setTimeout(function () {
button.innerHTML = "发送到 Notion";
// 获取Web中的同类型的ClassName,并套用在嵌入的按钮元素上
button.className = "gr-button gr-button-lg gr-button-secondary";
//增加元素的title
button.setAttribute("title", "将图像写入Notion");
// 将按钮添加到页面中的特定位置
const container = document.querySelector("body > gradio-app").shadowRoot.querySelector("#component-229")
if (container) {
container.appendChild(button);
console.log("已经将button插入指定位置");
} else {
console.log("未找到容器元素,将按钮添加到页面底部");
// 将按钮添加到页面底部
document.body.appendChild(button);
}
}, 1000);
});
至此,我们就可以实现将“发送至Notion”按钮嵌入到Stable Diffusion WebUI中的效果了!
外观效果有了,接下来,让我们来实现点击按钮的逻辑部分,即点击按钮后,我们生成好的图片以及相关的参数都会上传至Notion对应的数据库中
继续编写content.js
代码
getInfoFromPage()
,获取包括img、prompt、negativePrompt和parameter信息代码如下:
// 从页面中获取需要发送的信息
function getInfoFromPage() {
// 在这里编写代码来获取信息
console.log("执行getInfoFromPage")
const img = document.querySelector("body > gradio-app").shadowRoot.querySelector("#txt2img_gallery > div.overflow-y-auto.h-full.p-2.min-h-\\[350px\\].max-h-\\[55vh\\].xl\\:min-h-\\[450px\\] > div > button > img")
console.log(img)
const all_msg = document.querySelector("body > gradio-app").shadowRoot.querySelector("#html_info_txt2img > p");
let all_msg_str = all_msg.textContent.trim();
console.log(all_msg_str);
// 匹配需要的信息,包括prompt、negativePrompt和parameter
const regex = /^(?:([^<\n]*)(?:\nNegative prompt: ([^<\n]*))?)?\n(.+)$/m;
const match = all_msg_str.match(regex);
console.log(match);
let prompt = '';
let negativePrompt = '';
let parameter = '';
if (match) {
if (match[1].includes("Negative prompt: ")) {
prompt = '';
negativePrompt = match[1] || '';
parameter = match[3] || '';
} else {
prompt = match[1] || '';
negativePrompt = match[2] || '';
parameter = match[3] || '';
}
console.log(prompt);
console.log(negativePrompt);
console.log(parameter);
} else {
console.log('No match found.');
}
// 返回需要发送的信息
return {
"img": img.src,
"prompt": prompt,
"negativePrompt": negativePrompt,
"parameter": parameter
}
}
content.js
代码getInfoFromPage()
中的信息传递给它代码如下:
// 延迟 1 秒钟执行按钮插入操作(等待页面加载完毕)
setTimeout(function () {
// 创建一个按钮元素
const button = document.createElement("button");
button.innerHTML = "发送到 Notion";
// 获取Web中的同类型的ClassName,并套用在嵌入的按钮元素上
button.className = "gr-button gr-button-lg gr-button-secondary";
//增加元素的title
button.setAttribute("title", "将图像写入Notion");
// 将按钮添加到页面中的特定位置
const container = document.querySelector("body > gradio-app").shadowRoot.querySelector("#component-229")
if (container) {
container.appendChild(button);
console.log("已经将button插入指定位置");
} else {
console.log("未找到容器元素,将按钮添加到页面底部");
// 将按钮添加到页面底部
document.body.appendChild(button);
}
// 添加按钮点击事件的监听器
button.addEventListener("click", function () {
// 从页面中获取需要发送的信息
const info = getInfoFromPage();
console.log(info)
// 向后台脚本发送一个消息,指示它打开popup.html并将信息传递给它
chrome.runtime.sendMessage({ action: "open_popup", info: info }, function (response) {
console.log(response);
});
});
}, 1000);
background.js
代码代码如下:
// 监听来自content.js的消息
chrome.runtime.onMessage.addListener(function (request, sender, sendResponse) {
// 如果请求是打开popup.html并传递信息
if (request.action === "open_popup") {
// 创建一个新的窗口,打开popup.html
chrome.windows.create({
url: chrome.extension.getURL("popup.html"),
type: "popup",
width: 600,
height: 400
}, function (window) {
// 在窗口加载完成后向它发送一个消息,将信息传递给它
chrome.tabs.onUpdated.addListener(function (tabId, changeInfo, tab) {
if (changeInfo.status === "complete") {
chrome.tabs.sendMessage(tabId, { info: request.info }, function (response) {
console.log(response);
});
}
});
});
}
});
sendToNotion按钮
,展示从页面中获取的数据代码如下:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>Stable Diffusion To Notion</title>
<!-- 样式定义 -->
<style>
body {
width: 400px;
height: 400px;
}
#info {
overflow: auto;
height: 300px;
}
</style>
<!-- 加载脚本文件 -->
<script src="popup.js"></script>
</head>
<body>
<!-- 标题 -->
<h1>请确认以下信息发送到Notion?</h1>
<!-- 发送到 Notion 按钮 -->
<button id="sendToNotion">确定</button>
<!-- 从页面中获取的信息 -->
<h3>从页面中获取的信息:</h3>
<div id="info_img">img</div>
<div id="info_prompt">Prompt</div>
<div id="info_negativePrompt">Negative Prompt</div>
<div id="info_parameter">Parameter</div>
</body>
</html>
background.js
中编写了一个监听事件,当popup.html加载完毕时,会将获取到的Stable Diffusion WebUI的页面数据info
发送给popup.js,因此我们这里需要编写一个监听事件监听数据接收,并将接收的数据渲染到popup.html界面中编写send2Notion(info)
方法,将popup.html渲染好的数据(包括img、prompt、negativePrompt和parameter)发送至Notion
关于Notion API的详细使用,我们要学会看官方文档
监听sendToNotion按钮点击事件,当sendToNotion按钮被点击时,获取信息并调用将信息发送至Notion方法 - send2Notion(info)
代码如下:
document.addEventListener('DOMContentLoaded', function () {
console.log('Hello, world!');
// 监听发送到 Notion 按钮的点击事件
document.getElementById('sendToNotion').addEventListener('click', function () {
// 获取信息
const info = {
negativePrompt: document.getElementById('info_negativePrompt').textContent,
parameter: document.getElementById('info_parameter').textContent,
prompt: document.getElementById('info_prompt').textContent,
};
console.log(info);
// 发送信息到 Notion
send2Notion(info);
});
// 监听来自 background.js 的消息
chrome.runtime.onMessage.addListener(function (request, sender, sendResponse) {
console.log(request);
if (request.info) {
// 找到要渲染的元素
var info_img = document.getElementById("info_img");
var info_negativePrompt = document.getElementById("info_negativePrompt");
var info_parameter = document.getElementById("info_parameter");
var info_prompt = document.getElementById("info_prompt");
// 将接收到的信息渲染到该元素中
info_img.textContent = request.info.img;
info_negativePrompt.textContent = request.info.negativePrompt;
info_parameter.textContent = request.info.parameter;
info_prompt.textContent = request.info.prompt;
sendResponse("message received");
}
});
});
async function send2Notion(info) {
console.log("data", info);
let notion_api_key = "你的Notion API Key"
let databaseID = "你的数据库ID"
// 发送 POST 请求到 Notion API
let dataResponse = await fetch('https://api.notion.com/v1/pages', {
method: 'POST',
headers: {
'Authorization': 'Bearer ' + notion_api_key,
'Notion-Version': '2022-06-28',
'Content-Type': 'application/json'
},
referrerPolicy: "no-referrer-when-downgrade",
mode: "same-origin",
body: JSON.stringify({
"parent": { "database_id": databaseID },
"properties": {
"Name": {
"title": [
{
"text": {
"content": "Detail"
}
}
]
},
"Prompt": {
"rich_text": [
{
"text": {
"content": info.prompt
}
}
]
},
"Negative Prompt": {
"rich_text": [
{
"text": {
"content": info.negativePrompt
}
}
]
},
"Parameter": {
"rich_text": [
{
"text": {
"content": info.parameter
}
}
]
}
}
})
}).then(response => response.json());
if (dataResponse) {
// 显示弹窗
alert("信息已成功发送至 Notion!");
// 关闭 popup.html 窗口
window.close();
} else {
// 发生错误,打印错误信息
console.log("发送信息至 Notion 时发生错误:", dataResponse);
}
}
至此,本地的Stable Diffusion WebUI已经可以通过上传至Notion按钮与Notion对应的数据库链接,这里给大家看一下实际效果:
接下来我们再来看看数据库这边,
可以看到,数据确实添加进去了,但是没有图片!
原因很明显,我们没有上传图片,而且也Notion也不支持从外部上传图片到其数据库中,其只支持从外部链接读取文件(图片)。
因此我们需要使用外部的文件托管服务(图床),这里我们使用Github为我提供文件托管服务。
关于如何使用GitHub API上传文件以及用GitHub做图床,可以参考这篇:
『转载』使用GitHub API上传文件及GitHub做图床 – 邱少羽梦
基于 Github API 的图床 Chrome 插件开发全纪录 - 掘金
图片上传使用了 content API 的 create-a-file 接口,通过 PUT 发送一条文件内容为 base64 的请求到指定的仓库目录
这里着重圈出了必须把文件进行 base64 编码,否则接口调用将会出错。
在传输数据时,如果使用二进制方式传输,数据传输过程中可能会出现乱码或者数据丢失的问题,而Base64编码能够将二进制数据转换成ASCII字符,不会出现这样的问题,同时也能够减小数据大小,减小传输量。所以在调用API接口时,将图片转换为base64编码是常见手段。
如果是更新文件,需要先获取待更新文件的sha
更新文件的sha可以通过直接Get请求拿到,如下:
然后再切换成PUT,附带参数发送过去即可
使用APIPost7调试
在搭好图床以及了解好GitHub API后,我们要明确我们的业务需求:
现在我们来开始搭建实现我们的业务需求:
选择图片和把图片转化成base64
这里我们首先使用在popup.html
上编写相应的图片展示
代码如下:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>Stable Diffusion To Notion</title>
<style>
body {
width: 400px;
height: 400px;
}
#info {
overflow: auto;
height: 300px;
}
</style>
<script src="popup.js"></script>
</head>
<body>
<h1>请确认以下信息发送到Notion?</h1>
<button id="sendToNotion">确定</button>
<h3>从页面中获取的信息:</h3>
<img id="info_img" />
<div id="info_prompt">Prompt</div>
<div id="info_negativePrompt">Negative Prompt</div>
<div id="info_parameter">Parameter</div>
</body>
</html>
然后在popup.js中编写代码获取其img元素,接着就是对这个img进行base64转换的操作
如何把文件进行base64编码?
可以使用 JavaScript 中的btoa()
方法将 “hello world” 转换为 Base64 编码。btoa()
方法将字符串转换为 Base64 编码字符串。以下是一个将 “hello world” 转换为 Base64 的示例代码:
const str = "hello world"; const base64 = btoa(str); console.log(base64); // 输出 "aGVsbG8gd29ybGQ="
- 1
- 2
- 3
在此示例中,
str
是要转换的字符串,base64
是转换后的 Base64 编码字符串。您可以使用console.log()
输出base64
。在我们的示例中,您也可以通过创建一个Image对象并将其设置为在popup.html中定义的img元素来获取图片数据并将其转换为base64编码。
以下是一个将图片文转换为 Base64 编码字符串的示例代码:
// 将图片解码至base64 let img2Notion_url = "" function translate() { // 创建一个Promise对象 return new Promise((resolve, reject) => { // 获取页面中的图片元素 const img = document.getElementById('info_img'); // 创建一个新的Image对象 const image = new Image(); // 设置Image对象的src属性为图片元素的src属性 image.src = img.src; // 分割图片src属性的字符串并获取图片的名称 const splitArray = image.src.split("/"); const img_name = splitArray[splitArray.length - 2] + "/" + splitArray[splitArray.length - 1]; console.log(img_name); // 构建图片在GitHub上的url地址 img2Notion_url = "https://github.com/Mr-KID-github/Stable-Diffusion-Open-Community/blob/main/images/" + img_name + "?raw=true" // 当图片加载完成后执行回调函数 image.onload = function () { // 创建一个canvas元素并设置其宽度和高度为图片的宽度和高度 const canvas = document.createElement('canvas'); canvas.width = image.width; canvas.height = image.height; // 获取canvas的2D绘图上下文对象 const context = canvas.getContext('2d'); // 在canvas上绘制图片 context.drawImage(image, 0, 0); // 将canvas内容转换为base64格式的字符串 const base64 = canvas.toDataURL().split(',')[1]; console.log(base64); // 将解码后的base64格式的图片和图片名称通过resolve方法返回给调用方 resolve({ data: base64, name: img_name }); }; }); }
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
同时,这里需要注意一个细节点,导入Notion中链接格式应该为如下格式:
https://github.com/Mr-KID-github/Stable-Diffusion-Open-Community/blob/main/images/" + img_name + "?raw=true"
本回答由Chat GPT辅助生成
在我们将图片base64编码后,即可尝试图片上传Github操作了
同样,我们这次用APIPost测试,如图所示即是成功:
接着,我们就可以开始编写我们的将图片文件上传到 GitHub 仓库中方法了,代码如下:
// 定义一个异步函数,用于将图片文件上传到 GitHub 仓库中
async function uploadFileToGithub() {
// 将图片解码至 base64 格式
const imgbase64 = await translate();
console.log(imgbase64.data);
// 构造上传图片所需的 GitHub API 请求 URL
const Github_url = "https://api.github.com/repos/Mr-KID-github/Stable-Diffusion-Open-Community/contents/images/" + imgbase64.name;
// 发送 PUT 请求将图片文件上传到 GitHub 仓库
return window.fetch(Github_url, {
method: 'PUT',
headers: {
"Accept": "application/vnd.github+json ",
"Content-Type": "application/json",
"X-GitHub-Api-Version": "2022-11-28",
"Authorization": "Bearer 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"
},
body: JSON.stringify({
"message": "Stable Diffusion 上传图片 " + imgbase64.name,
"content": imgbase64.data
})
})
// 处理上传结果
.then(async res => {
if (res.status >= 200 && res.status < 400) {
// 如果上传成功,返回成功信息和响应内容
return {
status: res.status,
data: await res.json()
}
} else {
// 如果上传失败,弹出提示框,并返回失败信息和响应内容
alert("图片上传GitHub图床失败!");
console.log(res);
return {
status: res.status,
data: null
}
}
})
// 捕捉可能的异常
.catch(e => e);
}
在send2Notion方法中增加Github图床中的对应图片链接
......
let dataResponse = await fetch('https://api.notion.com/v1/pages', {
method: 'POST',
headers: {
'Authorization': 'Bearer ' + notion_api_key,
'Notion-Version': '2022-06-28',
'Content-Type': 'application/json'
},
referrerPolicy: "no-referrer-when-downgrade",
mode: "same-origin",
body: JSON.stringify({
"parent": { "database_id": databaseID },
"properties": {
"Name": {
"title": [
{
"text": {
"content": "Detail"
}
}
]
},
"Prompt": {
"rich_text": [
{
"text": {
"content": info.prompt
}
}
]
},
"Negative Prompt": {
"rich_text": [
{
"text": {
"content": info.negativePrompt
}
}
]
},
"Parameter": {
"rich_text": [
{
"text": {
"content": info.parameter
}
}
]
}
},
"children": [
{
"object": "block",
"type": "image",
"image": {
"type": "external",
"external": {
"url": img2Notion_url
}
}
},
]
})
})
......
如图所示:
最后确定的样式如下:
至此,我们的工作就全部完成啦!你上传的所有图片都会在这个平台展示出来:
Stable Diffusion Open Community
当然,你也可以在这个平台看到所有人开源出来的图片以及相关的提词!
所有源代码全部放置Github开源平台!如果喜欢的话请点击Star,让更多人关注到我们,一起加入光荣的进化吧!
https://github.com/Mr-KID-github/Stable-Diffusion-Open-Community
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。