// ==UserScript==
// @name 通用ai插图脚本8
// @namespace http://tampermonkey.net/
// @version 8.8
// @license MIT
// @description 免费或者使用sd的api或使用novelai3配合ai生成插图
// @author 从前跟你一样
// @grant unsafeWindow
// @match *://*/*
// @require https://cdn.staticfile.org/jquery/3.4.1/jquery.min.js
// @require https://cdnjs.cloudflare.com/ajax/libs/jszip/3.7.1/jszip.min.js
// @connect vagrantup.com
// @grant unsafeWindow
// @grant GM_xmlhttpRequest
// @connect sd则此处换成你的电脑域名ip、不需要带端口。
// @connect *
// @connect 192.168.10.2
// @connect 127.0.0.1
// @connect novelai.net
// @match *://*/*
// @description Save user settings
// @grant GM_xmlhttpRequest
// @grant GM_setValue
// @grant GM_getValue
// ==/UserScript==
//必须在connect填你的sd域名,也就是你电脑的ip地址(本地ip)或网址xxx.com
(function() {
'use strict';
let ster=false;
// 定义默认设置
const defaultSettings = {
scriptEnabled:false,
mode: 'free',
sdUrl: 'http://localhost:7860',
novelaiApi: '000000',
startTag: 'image:{',
endTag: '}',
fixedPrompt: 'best quality, amazing quality, very aesthetic, absurdres',
nai3Scale: '10',
sdCfgScale: '7',
steps: '28',
width: '1024',
height: '1024',
seed: '0',
restoreFaces: 'false',
samplerName: 'DPM++ 2M',
negativePrompt: 'bad proportions, out of focus, username, text, bad anatomy, lowres, worstquality, watermark, cropped, bad body, deformed, mutated, disfigured, poorly drawn face, malformed hands, extra arms, extra limb, missing limb, too many fingers, extra legs, bad feet, missing fingers, fused fingers, acnes, floating limbs, disconnected limbs, long neck, long body, mutation, ugly, blurry, low quality, sketches, normal quality, monochrome, grayscale, signature, logo, jpeg artifacts, unfinished, displeasing, chromatic aberration, extra digits, artistic error, scan, abstract, photo, realism, screencap'
};
let settings = {};
for (const [key, defaultValue] of Object.entries(defaultSettings)) {
settings[key] = GM_getValue(key, defaultValue);
// 如果没有读取到值,就使用默认值并保存
if (settings[key] === defaultValue) {
GM_setValue(key, defaultValue);
}
}
function addNewElement() {
const targetElement = document.querySelector('#option_toggle_AN');
if (targetElement) {
const newElement = document.createElement('a');
newElement.id = 'option_toggle_AN2';
const icon = document.createElement('i');
icon.className = 'fa-lg fa-solid fa-note-sticky';
newElement.appendChild(icon);
const span = document.createElement('span');
span.setAttribute('data-i18n', "打开设置");
span.textContent = '打开文生图设置';
newElement.appendChild(span);
targetElement.parentNode.insertBefore(newElement, targetElement.nextSibling);
console.log("New element added successfully");
document.getElementById('option_toggle_AN2').addEventListener('click', showSettingsPanel);
return true; // 表示操作成功完成
}
}
function createSettingsPanel() {
const panel = document.createElement('div');
panel.id = 'settings-panel';
panel.style.position = 'absolute';
panel.style.top = '50%';
panel.style.left = '50%';
panel.style.transform = 'translate(-50%, -50%)';
panel.style.backgroundColor = 'black'; // 设置背景为黑色
panel.style.color = 'white';// 设置字体为白色
panel.style.padding = '20px';
panel.style.border = '1px solid white';// 设置边框为白色
panel.style.zIndex = '10000';
panel.style.display = 'none';
panel.style.overflowY = 'auto';
panel.style.maxHeight = '80vh';
panel.innerHTML += `
<style>
#settings-panel input, #settings-panel select {
background-color: #444;
color: white;
background-color: black;
border: none;
padding: 5px;
margin: 5px 0;
}
#settings-panel button {
background-color: #444;
color: white;
border: none;
padding: 5px 10px;
cursor: pointer;
}
#settings-panel button:hover {
background-color: #555;
}
</style>
`;
panel.innerHTML = `
<h2>设置面板</h2>
<label class="switch">
<input type="checkbox" id="scriptToggle" ${settings.scriptEnabled ? 'checked' : ''}>
<span class="slider"></span>
</label>
<label for="scriptToggle" style="display: inline-block; margin-left: 10px;">启用脚本</label>
<br><br>
<label>模式:
<select id="mode">
<option value="sd" ${settings.mode === 'sd' ? 'selected' : ''}>SD</option>
<option value="novelai" ${settings.mode === 'novelai' ? 'selected' : ''}>NovelAI</option>
<option value="free" ${settings.mode === 'free' ? 'selected' : ''}>Free</option>
</select>
</label><br>
<label>SD URL:<input type="text" id="sdUrl" value="${settings.sdUrl}"></label><br>
<label>NovelAI API:<input type="text" id="novelaiApi" value="${settings.novelaiApi}"></label><br>
<label>开始标记:<input type="text" id="startTag" value="${settings.startTag}"></label><br>
<label>结束标记:<input type="text" id="endTag" value="${settings.endTag}"></label><br>
<label>固定正面prompt:<input type="text" id="fixedPrompt" value="${settings.fixedPrompt}"></label><br>
<label>NAI3关键词关联性scale:<input type="number" id="nai3Scale" value="${settings.nai3Scale}"></label><br>
<label>SD关键词关联性cfg_scale:<input type="number" id="sdCfgScale" value="${settings.sdCfgScale}"></label><br>
<label>生成步数steps:<input type="number" id="steps" value="${settings.steps}"></label><br>
<label>宽度width:<input type="number" id="width" value="${settings.width}"></label><br>
<label>高度height:<input type="number" id="height" value="${settings.height}"></label><br>
<label>生成种子seed:<input type="number" id="seed" value="${settings.seed}"></label><br>
<label>SD面部修复restore_faces:
<select id="restoreFaces">
<option value="true" ${settings.restoreFaces === 'true' ? 'selected' : ''}>True</option>
<option value="false" ${settings.restoreFaces === 'false' ? 'selected' : ''}>False</option>
</select>
</label><br>
<label>SD采样方式sampler_name:<input type="text" id="samplerName" value="${settings.samplerName}"></label><br>
<label>negative_prompt:<input type="text" id="negativePrompt" value="${settings.negativePrompt}"></label><br>
<button id="save-settings">保存设置</button>
<button id="close-settings">关闭</button>
<a id="visit-website-link" href="https://asgdd1kjanhq.sg.larksuite.com/wiki/MqOIw9n3qisWc9kXNhdlkoKCguu?from=from_copylink" target="_blank">帮助</a>
<a id="visit-website-link" href="https://discord.com/channels/1134557553011998840/1215675312721887272/1215675312721887272" target="_blank">dc讨论</a>
<a id="visit-website-link">BY从前我跟你一样</a>
`;
const style = document.createElement('style');
style.textContent = `
#settings-panel input, #settings-panel select {
background-color: #444;
color: white;
background-color: black;
border: none;
padding: 5px;
margin: 5px 0;
}
#settings-panel button {
background-color: #444;
color: white;
border: none;
padding: 5px 10px;
cursor: pointer;
}
#settings-panel button:hover {
background-color: #555;
}
.switch {
position: relative;
display: inline-block;
width: 60px;
height: 34px;
}
.switch input {
opacity: 0;
width: 0;
height: 0;
}
.slider {
position: absolute;
cursor: pointer;
top: 0;
left: 0;
right: 0;
bottom: 0;
background-color: #ccc;
transition: .4s;
border-radius: 34px;
}
.slider:before {
position: absolute;
content: "";
height: 26px;
width: 26px;
left: 4px;
bottom: 4px;
background-color: white;
transition: .4s;
border-radius: 50%;
}
input:checked + .slider {
background-color: #2196F3;
}
input:checked + .slider:before {
transform: translateX(26px);
}
`;
document.body.appendChild(panel);
document.head.appendChild(style);
document.getElementById('save-settings').addEventListener('click', saveSettings);
document.getElementById('close-settings').addEventListener('click', closeSettings);
// 添加滑块切换事件监听器
document.getElementById('scriptToggle').addEventListener('change', function() {
settings.scriptEnabled = this.checked;
GM_setValue('scriptEnabled', this.checked);
console.log('Script ' + (this.checked ? 'enabled' : 'disabled'));
});
return panel;
}
function saveSettings() {
for (const key of Object.keys(defaultSettings)) {
const element = document.getElementById(key);
if (element) {
settings[key] = element.value;
GM_setValue(key, element.value);
}
}
console.log('Settings saved');
hideSettingsPanel();
}
function closeSettings() {
hideSettingsPanel();
}
function showSettingsPanel() {
for (const key of Object.keys(defaultSettings)) {
const element = document.getElementById(key);
if (element) {
settings[key] = element.value;
GM_setValue(key, element.value);
}
}
console.log('Settings saved:', settings);
const panel = document.getElementById('settings-panel');
if (!panel) {
createSettingsPanel();
}
document.getElementById('settings-panel').style.display = 'block';
}
function hideSettingsPanel() {
document.getElementById('settings-panel').style.display = 'none';
}
// 添加点击事件监听器到您的按钮或元素上
$(document).ready(function() {
addNewElement();
ster=true;
});
function unzipFile(arrayBuffer) {
return new Promise((resolve, reject) => {
JSZip.loadAsync(arrayBuffer)
.then(function(zip) {
console.log("ZIP 文件加载成功");
// 遍历 ZIP 文件中的所有文件
zip.forEach(function (relativePath, zipEntry) {
console.log("文件名:", zipEntry.name);
// 解压每个文件
zipEntry.async('blob').then(function(blob) {
console.log("文件大小:", blob.size);
// 处理解压后的文件内容
resolve(blob);
});
});
}).catch(reject);
}) }
function extractPrompt(str, start, end) {
const startIndex = str.indexOf(start);
if (startIndex === -1) return str; // 如果没有找到开始标记,返回原字符串
const endIndex = str.lastIndexOf(end);
if (endIndex === -1 || endIndex <= startIndex) return str; // 如果没有找到结束标记或结束标记在开始标记之前,返回原字符串
return str.slice(startIndex + start.length, endIndex);
}
async function replaceWithnovelai() {
if(!settings.scriptEnabled){
return;
}
unsafeWindow.模式=settings.mode; //sd novelai free 使用sd 或novelai 或 免费的 novelai需要获取api sd启动器需要启用api功能。例如绘世启动器中 的高级选项 启用api选择开启。
unsafeWindow.start=settings.startTag;//检测ai输出的提示词以什么开头 会被去除;可以自定义。
unsafeWindow.end=settings.endTag;///检测ai输出的提示词以什么结尾,不去除,可以自定义。提示词会采用两者之间的文字。
// 以下为novelai的设置 注意更改脚本设置 需要刷新网页。
var ps = document.getElementsByClassName("mes_text");
for (var i = 0; i < ps.length; i++) {
var p = ps[i];
var linkText = p.textContent;
var regex = new RegExp(`${unsafeWindow.start}(.*?)${unsafeWindow.end}`);
var matches = linkText.match(regex);
if(matches){
var targetText = matches[0];
var link = extractPrompt(targetText,unsafeWindow.start,unsafeWindow.end);
//alert(targetText)
const button = document.createElement('button');
var uniqueId = "button_" + Math.random().toString(36).substr(2, 9);
button.id = uniqueId;
button.textContent = '生成图片';
button.dataset.link = link;
p.innerHTML = p.innerHTML.replace(targetText, button.outerHTML);
// alert(targetText);
// 重新找到新创建的按钮
var newbutton = document.getElementById(uniqueId);
const imgSpan = document.createElement('span');
newbutton.addEventListener('click', async function() {
let access_token = settings.novelaiApi//填写你的novelai的apikey,在官方网站的设置 Account 里 Get Persistent API Token
let model = "nai-diffusion-3"//使用的模型 多个选择 "safe-diffusion" "nai-diffusion" "nai-diffusion-furry" "furry-diffusion-inpainting" "nai-diffusion-2" "nai-diffusion-3-inpainting" "nai-diffusion-furry-3-inpainting"
let prompt=settings.fixedPrompt//固定正面提示词
let preset_data = {"params_version":1,
"width":settings.width,
"height":settings.height,
"scale":settings.nai3Scale,//提示词关联性
"sampler":"k_euler",//使用的采样器 "k_dpm_2" "k_dpmpp_2m" "ddim_v3"
"steps":settings.steps,//生成的步数
"n_samples":1,
"ucPreset":3,//预设
"qualityToggle":true,
"sm":false,
"sm_dyn":false,
"dynamic_thresholding":false,
"controlnet_strength":1,
"legacy":false,
"add_original_image":true,
"cfg_rescale":0.18,//关联性调整
"noise_schedule":"native",
"legacy_v3_extend":false,
"seed":settings.seed,//生成的种子,下面是固定的负面提示词
"negative_prompt":settings.negativePrompt,
"reference_image_multiple":[],
"reference_information_extracted_multiple":[],
"reference_strength_multiple":[]}
const payload = preset_data;
const urlObj = new URL("https://api.novelai.net/ai/generate-image");
const Authorization="Bearer "+access_token;
const data11 = {
"input": this.dataset.link+prompt,//+
"model": "nai-diffusion-3",
"action": "generate",
"parameters": preset_data
}
try {
const response = await new Promise((resolve, reject) => {
GM_xmlhttpRequest({
method: "POST",
url: urlObj,
data: JSON.stringify(data11),
responseType: "arraybuffer",
headers: {
"Content-Type": "application/json",
"Authorization":Authorization
},
onload: function(response) {
if (response.status >= 200 && response.status < 400) {
resolve(response);
//alert("以成功返回"+response.status);
} else {
if(response.status==400){
alert("验证错误");
}
if(response.status==401){
alert("api错误请检测api是否正确");
}
if(response.status==402){
alert("需要有效订阅才能访问此端点。");
}
if(response.status==409){
alert("发生冲突错误");
}
if(response.status==500){
alert("未知错误");
}
//alert("响应内容:", response.responseText);
reject(new Error(`请求失败,状态码: ${response.status}`));
}
},
onerror: function(error,response) {
alert("请求错误,请检查代理");
reject(error);
}
});
});
const data123 = await response.response;
let re= await unzipFile(data123);
console.log('base64data'+Object.prototype.toString.call(re));
let blob = new Blob([re], { type: 'image/png' });
let imageUrl = URL.createObjectURL(blob);
const img2 = document.createElement('img');
img2.src=imageUrl;
img2.alt = "Generated Image2";
imgSpan.innerHTML = '';
imgSpan.appendChild(img2);
} catch (error) {
console.error('Error generating image:', error);
}
});
newbutton.parentNode.insertBefore(imgSpan, button.nextSibling);
}
}
}
async function replaceSpansWithImagesst() {
if(!settings.scriptEnabled){
return;
}
//以下为sd的设置
unsafeWindow.start=settings.startTag;
unsafeWindow.end=settings.endTag;
unsafeWindow.url = settings.sdUrl;//sd地址 记得要修改上面的connect 的sd域名才能访问 此处要带端口!
unsafeWindow.prompts = settings.fixedPrompt; //额外固定的提示词 nsfw? 也可以固定你要的 lora 。每次都会加载提示词后面。下面是反向提示词
unsafeWindow.negative_prompt =settings.negativePrompt
unsafeWindow.cfg_scale = settings.sdCfgScale;//关键词关联性
unsafeWindow.width = settings.width; //宽度
unsafeWindow.height = settings.height;//长度
unsafeWindow.restore_faces = settings.restoreFaces; //面部修复
unsafeWindow.steps = settings.steps;//步数
unsafeWindow.sampler_name=settings.samplerName ; //采样方式
var ps = document.getElementsByClassName("mes_text");
for (var i = 0; i < ps.length; i++) {
var p = ps[i];
var linkText = p.textContent;
var regex = new RegExp(`${unsafeWindow.start}(.*?)${unsafeWindow.end}`);
var matches = linkText.match(regex);
if(matches){
var targetText = matches[0];
var link = extractPrompt(targetText,unsafeWindow.start,unsafeWindow.end);
const button = document.createElement('button');
var uniqueId = "button_" + Math.random().toString(36).substr(2, 9);
button.id = uniqueId;
button.textContent = '生成图片';
button.dataset.link = link;
p.innerHTML = p.innerHTML.replace(targetText, button.outerHTML);
// alert(targetText);
// 重新找到新创建的按钮
var newbutton = document.getElementById(uniqueId);
const imgSpan = document.createElement('span');
newbutton.addEventListener('click', async function() {
const url = unsafeWindow.url;
const payload = {
"prompt": this.dataset.link+" "+unsafeWindow.prompts,
"negative_prompt": unsafeWindow.negative_prompt,
"steps": unsafeWindow.steps,
"sampler_name": unsafeWindow.sampler_name,
"width": unsafeWindow.width,
"height": unsafeWindow.height,
"restore_faces": unsafeWindow.restore_faces,
"cfg_scale":unsafeWindow.cfg_scale,
"seed":settings.seed === 0||settings.seed === "0" ? -1 : settings.seed
};
const urlObj = new URL(url+"/sdapi/v1/txt2img");
try {
const response = await new Promise((resolve, reject) => {
GM_xmlhttpRequest({
method: "POST",
url: urlObj,
data: JSON.stringify(payload),
headers: {
"Content-Type": "application/json",
"Access-Control-Allow-Origin": "*"
},
onload: function(response) {
if (response.status >= 200 && response.status < 400) {
resolve(response);
} else {
alert("请求URL:", response.finalUrl);
alert("响应状态码:", response.status);
alert("响应内容:", response.responseText);
reject(new Error(`请求失败,状态码: ${response.status}`));
}
},
onerror: function(error,response) {
alert("请求错误。请检测地址是否正确.或sd已正确开启,sd启动器需要启用api功能。例如绘世启动器中 的高级选项 启用api选择开启。以及在// @connect 处填写sd所在的电脑ip。不需要带端口例如局域网为192.168.1.2,本机则是127.0.0.1");
reject(error);
}
});
});
const r = JSON.parse(response.responseText);
for (let i of r['images']) {
const png_payload = {
"image": "data:image/png;base64," + i
};
const response2 = await new Promise((resolve, reject) => {
GM_xmlhttpRequest({
method: "POST",
url: `${url}/sdapi/v1/png-info`,
data: JSON.stringify(png_payload),
headers: {
"Content-Type": "application/json"
},
onload: resolve,
onerror: reject
});
});
const pngInfo = JSON.parse(response2.responseText).info;
const dataURL = "data:image/png;base64," + i;
const img = document.createElement('img');
img.src = dataURL;
img.alt = "Generated Image";
img.dataset.parameters = pngInfo;
imgSpan.innerHTML = '';
imgSpan.appendChild(img);
}
} catch (error) {
console.error('Error generating image:', error);
}
});
// p.parentNode.replaceChild(button, span);
newbutton.parentNode.insertBefore(imgSpan, button.nextSibling);
}
}
}
async function replaceSpansWithImagesfree() {
if(!settings.scriptEnabled){
return;
}
unsafeWindow.start=settings.startTag;
unsafeWindow.end=settings.endTag;
unsafeWindow.url = settings.sdUrl;//sd地址 记得要修改上面的connect 的sd域名才能访问 此处要带端口!
unsafeWindow.prompts = settings.fixedPrompt; //额外固定的提示词 nsfw? 也可以固定你要的 lora 。每次都会加载提示词后面。下面是反向提示词
unsafeWindow.negative_prompt =settings.negativePrompt
unsafeWindow.cfg_scale = settings.sdCfgScale;//关键词关联性
unsafeWindow.width = settings.width; //宽度
unsafeWindow.height = settings.height;//长度
unsafeWindow.restore_faces = settings.restoreFaces; //面部修复
unsafeWindow.steps = settings.steps;//步数
unsafeWindow.sampler_name=settings.samplerName ; //采样方式
var ps = document.getElementsByClassName("mes_text");
for (var i = 0; i < ps.length; i++) {
var p = ps[i];
var linkText = p.textContent;
var regex = new RegExp(`${unsafeWindow.start}(.*?)${unsafeWindow.end}`);
var matches = linkText.match(regex);
if(matches){
var targetText = matches[0];
var link = extractPrompt(targetText,unsafeWindow.start,unsafeWindow.end);
const button = document.createElement('button');
var uniqueId = "button_" + Math.random().toString(36).substr(2, 9);
button.id = uniqueId;
button.textContent = '生成图片';
button.dataset.link = link;
p.innerHTML = p.innerHTML.replace(targetText, button.outerHTML);
// alert(targetText);
// 重新找到新创建的按钮
var newbutton = document.getElementById(uniqueId);
const imgSpan = document.createElement('span');
newbutton.addEventListener('click', async function() {
var ran = Math.floor(Math.random() * 10000).toString();
const prompt = this.dataset.link+ " " +unsafeWindow.prompts+" "+ran;
const url = `https://image.pollinations.ai/prompt/${encodeURIComponent(prompt)}`;
const img = document.createElement('img');
img.src = url;
img.alt = "Generated Image";
imgSpan.innerHTML = '';
imgSpan.appendChild(img);
});
newbutton.parentNode.insertBefore(imgSpan, button.nextSibling);
}
}
}
function chenk(){
if(settings.mode=="sd"){
replaceSpansWithImagesst();
}else if(settings.mode=="novelai"){
replaceWithnovelai();
}else{
replaceSpansWithImagesfree();
}
}
setInterval(chenk, 2000);
})();