您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
基于DeepDanbooru生成nai魔法串
当前为
// ==UserScript== // @name DeepDanbooru 魔法串生成器 // @namespace http://tampermonkey.net/ // @version 1.04 // @description 基于DeepDanbooru生成nai魔法串 // @author a1606 // @license MIT // @match http://dev.kanotype.net:8003/deepdanbooru/view/general/* // @grant GM_setValue // @grant GM_getValue // @grant GM_deleteValue // @grant GM_listValues // @grant GM_setClipboard // @grant GM_xmlhttpRequest // @connect translation.googleapis.com // @connect translate.googleapis.com // @run-at document-idle // ==/UserScript== (function () { 'use strict'; const STATE = { roles: [], presets: [], artists: [], ignore: [], selectedRoles: new Set(), selectedPresets: new Set(), selectedArtists: new Set(), selectedTags: new Set(), googleApiKey: '' }; async function loadStorage() { STATE.roles = await GM_getValue('custom_roles', []); STATE.presets = await GM_getValue('custom_presets', []); STATE.artists = await GM_getValue('custom_artists', []); STATE.ignore = await GM_getValue('ignored_tags', []); STATE.selectedRoles = new Set(await GM_getValue('selected_roles', [])); STATE.selectedPresets = new Set(await GM_getValue('selected_presets', [])); STATE.selectedArtists = new Set(await GM_getValue('selected_artists', [])); STATE.selectedTags = new Set(await GM_getValue('selected_tags', [])); STATE.googleApiKey = await GM_getValue('google_api_key', ''); } async function saveStorage() { await GM_setValue('custom_roles', STATE.roles); await GM_setValue('custom_presets', STATE.presets); await GM_setValue('custom_artists', STATE.artists); await GM_setValue('ignored_tags', STATE.ignore); await GM_setValue('selected_roles', [...STATE.selectedRoles]); await GM_setValue('selected_presets', [...STATE.selectedPresets]); await GM_setValue('selected_artists', [...STATE.selectedArtists]); await GM_setValue('selected_tags', [...STATE.selectedTags]); await GM_setValue('google_api_key', STATE.googleApiKey); } function splitNoteAndContent(item) { const idx = item.indexOf('::'); return idx !== -1 ? [item.slice(0, idx), item.slice(idx + 2)] : [null, item]; } function createButtonBar(tagTable) { const wrapper = document.createElement('div'); wrapper.style.margin = '10px 0'; const copyBtn = document.createElement('button'); copyBtn.textContent = '🧙 生成魔法串'; copyBtn.style.marginRight = '10px'; const settingBtn = document.createElement('button'); settingBtn.textContent = '⚙️ 设置'; const translateBtn = document.createElement('button'); translateBtn.textContent = '🌐翻译Tag'; translateBtn.style.marginLeft = '10px'; translateBtn.onclick = () => translateTagsGoogle(); copyBtn.onclick = () => generateMagic(tagTable); settingBtn.onclick = openSettingsPanel; wrapper.append(copyBtn, settingBtn, translateBtn); tagTable.parentNode.insertBefore(wrapper, tagTable); const rows = tagTable.querySelectorAll('tr'); rows.forEach(row => { const cells = row.querySelectorAll('td'); if (cells.length >= 2) { const tag = cells[0].textContent.trim(); const checkbox = document.createElement('input'); checkbox.type = 'checkbox'; checkbox.checked = true; checkbox.style.marginRight = '6px'; cells[0].prepend(checkbox); checkbox.dataset.tag = tag; const groupTitle = row.closest('tbody')?.previousElementSibling?.textContent?.trim(); if (groupTitle === 'Character Tags' || groupTitle === 'System Tags') { checkbox.checked = false; } for (const entry of STATE.ignore) { const [, content] = splitNoteAndContent(entry); if (tag === content && STATE.selectedTags.has(entry)) { checkbox.checked = false; break; } } } }); } function translateTagsGoogle() { const rows = document.querySelectorAll('table tr'); const tagPairs = []; const texts = []; rows.forEach(row => { const cells = row.querySelectorAll('td'); if (cells.length >= 2) { const tag = cells[0].textContent.trim(); if (!tag.includes(':') && !row.querySelector('.tag-zh')) { const readable = tag.replace(/_/g, ' '); tagPairs.push({ tag, readable, cell: cells[0] }); texts.push(readable); } } }); if (texts.length === 0) return; // 备用免费接口 function fallbackTranslate() { const batchSize = 40; let completed = 0; let allTranslations = []; function handleBatch(start) { const batch = texts.slice(start, start + batchSize); if (batch.length === 0) { if (allTranslations.length !== tagPairs.length) { alert('备用翻译结果数量不一致'); return; } tagPairs.forEach((pair, i) => { const el = document.createElement('span'); el.className = 'tag-zh'; el.textContent = `(${allTranslations[i]})`; el.style.marginLeft = '6px'; el.style.color = '#888'; el.style.fontSize = '0.9em'; pair.cell.appendChild(el); }); return; } GM_xmlhttpRequest({ method: "GET", url: `https://translate.googleapis.com/translate_a/single?client=gtx&sl=en&tl=zh-CN&dt=t&q=${encodeURIComponent(batch.join('\n'))}`, onload: res => { try { const json = JSON.parse(res.responseText); const translations = json[0].map(item => item[0].replace(/\\n/g, '')); allTranslations = allTranslations.concat(translations); handleBatch(start + batchSize); } catch (e) { alert('备用Google翻译失败:' + e.message); } }, onerror: () => alert('无法连接备用Google翻译接口,请检查网络环境') }); } handleBatch(0); } if (!STATE.googleApiKey) { fallbackTranslate(); return; } GM_xmlhttpRequest({ method: "POST", url: `https://translation.googleapis.com/language/translate/v2?key=${STATE.googleApiKey}`, headers: { "Content-Type": "application/json" }, data: JSON.stringify({ q: texts, source: "en", target: "zh-CN", format: "text" }), onload: res => { try { const json = JSON.parse(res.responseText); if (json.error) throw new Error(json.error.message); const translations = json.data?.translations; if (!Array.isArray(translations) || translations.length !== tagPairs.length) { throw new Error("翻译结果数量不一致"); } tagPairs.forEach((pair, i) => { const el = document.createElement('span'); el.className = 'tag-zh'; el.textContent = `(${translations[i].translatedText})`; el.style.marginLeft = '6px'; el.style.color = '#888'; el.style.fontSize = '0.9em'; pair.cell.appendChild(el); }); } catch (e) { // 主接口出错,降级到免费接口 fallbackTranslate(); } }, onerror: () => { fallbackTranslate(); } }); } function shouldIgnore(tag) { for (const entry of STATE.ignore) { const [, content] = splitNoteAndContent(entry); if (tag === content && STATE.selectedTags.has(entry)) { return true; } } return false; } function generateMagic(tagTable) { const rows = tagTable.querySelectorAll('tr'); const tagList = []; rows.forEach(row => { const cells = row.querySelectorAll('td'); if (cells.length >= 2) { const tag = cells[0].textContent.trim(); const pureTag = tag.replace(/(.*?)/g, '').trim(); const checkbox = cells[0].querySelector('input[type="checkbox"]'); const score = parseFloat(cells[1].textContent.trim()).toFixed(2); if ( checkbox?.checked && !pureTag.includes(':') && !shouldIgnore(pureTag) ) { tagList.push(`${pureTag}:${score}`); } } }); const parts = []; if (STATE.selectedRoles.size > 0) parts.push([...STATE.selectedRoles].map(e => splitNoteAndContent(e)[1]).join(' ')); if (STATE.selectedPresets.size > 0) parts.push([...STATE.selectedPresets].map(e => splitNoteAndContent(e)[1]).join(' ')); let tagStr = tagList.join(', '); if (tagStr.length > 0 && !tagStr.endsWith(',')) tagStr += ','; parts.push(tagStr); if (STATE.selectedArtists.size > 0) parts.push([...STATE.selectedArtists].map(e => splitNoteAndContent(e)[1]).join(' ')); const final = parts.join('\n'); GM_setClipboard(final); alert('✅ 魔法串已复制!'); } function enableDragSorting(wrapper, list, selectedSet, isTextOnly, key) { let draggingElem; wrapper.addEventListener('dragstart', e => { if (e.target.classList.contains('draggable-item')) { draggingElem = e.target; e.dataTransfer.effectAllowed = 'move'; } }); wrapper.addEventListener('dragover', e => { e.preventDefault(); const target = e.target.closest('.draggable-item'); if (target && target !== draggingElem) { const rect = target.getBoundingClientRect(); const next = (e.clientY - rect.top) > rect.height / 2; target.parentNode.insertBefore(draggingElem, next ? target.nextSibling : target); } }); wrapper.addEventListener('drop', async () => { const items = [...wrapper.querySelectorAll('.draggable-item')].map(row => { const span = row.querySelector('span'); const textarea = row.querySelector('textarea'); return textarea ? `${span.textContent}::${textarea.value}` : span.textContent; }); list.splice(0, list.length, ...items); await saveStorage(); }); } function createManager(label, key, list, selectedSet, isTextOnly = false) { const wrapper = document.createElement('div'); wrapper.style.margin = '10px 0'; const row1 = document.createElement('div'); row1.style.display = 'flex'; row1.style.alignItems = 'center'; row1.style.marginBottom = '4px'; const row2 = document.createElement('div'); row2.style.display = 'flex'; row2.style.alignItems = 'center'; const noteInput = document.createElement('input'); noteInput.placeholder = `备注`; noteInput.style.marginRight = '5px'; noteInput.style.flex = '1'; const addBtn = document.createElement('button'); addBtn.textContent = '添加'; addBtn.style.width = '50px'; addBtn.style.height = '30px'; addBtn.onclick = async () => { const note = noteInput.value.trim(); const content = contentInput.value.trim(); const final = note ? `${note}::${content}` : content; if (content && !list.includes(final)) { list.push(final); if (selectedSet) selectedSet.add(final); await saveStorage(); wrapper.append(renderItemRow(final, selectedSet, list, isTextOnly)); noteInput.value = ''; contentInput.value = ''; } }; const contentInput = document.createElement('textarea'); contentInput.placeholder = `内容(将作为输出 tag)`; contentInput.style.flex = '1'; contentInput.style.height = '96px'; contentInput.style.overflowY = 'scroll'; contentInput.style.resize = 'none'; row1.append(noteInput, addBtn); row2.append(contentInput); wrapper.append(row1, row2); list.forEach(item => wrapper.append(renderItemRow(item, selectedSet, list, isTextOnly))); enableDragSorting(wrapper, list, selectedSet, isTextOnly, key); return wrapper; } function renderItemRow(item, selectedSet, list, isTextOnly) { const row = document.createElement('div'); row.className = 'draggable-item'; row.draggable = true; row.style.display = 'flex'; row.style.alignItems = 'center'; row.style.marginTop = '4px'; row.style.flexDirection = 'column'; row.style.border = '1px dashed #ccc'; row.style.padding = '2px'; const [note, content] = item.includes('::') ? item.split('::') : [null, item]; const top = document.createElement('div'); top.style.display = 'flex'; top.style.alignItems = 'center'; top.style.width = '100%'; const checkbox = document.createElement('input'); checkbox.type = 'checkbox'; checkbox.checked = selectedSet?.has(item); checkbox.disabled = isTextOnly && !selectedSet; checkbox.onchange = async () => { if (checkbox.checked) selectedSet.add(item); else selectedSet.delete(item); await saveStorage(); }; const label = document.createElement('span'); label.textContent = note || content; label.style.margin = '0 5px'; label.style.flex = '1'; const toggleBtn = document.createElement('button'); toggleBtn.textContent = '▸'; toggleBtn.style.border = 'none'; toggleBtn.style.background = 'transparent'; toggleBtn.style.cursor = 'pointer'; toggleBtn.style.height = '24px'; toggleBtn.style.width = '24px'; toggleBtn.style.padding = '0'; toggleBtn.onclick = () => { const expanded = toggleBtn.textContent === '▾'; toggleBtn.textContent = expanded ? '▸' : '▾'; detailBox.style.display = expanded ? 'none' : 'block'; }; const delBtn = document.createElement('button'); delBtn.textContent = '✖'; delBtn.style.border = 'none'; delBtn.style.background = 'transparent'; delBtn.style.fontSize = '10px'; delBtn.style.width = '24px'; delBtn.style.height = '24px'; delBtn.onclick = async () => { if (!confirm(`是否要删除${note || content}?`)) return; const idx = list.indexOf(item); if (idx > -1) list.splice(idx, 1); selectedSet?.delete(item); await saveStorage(); row.remove(); }; const detailBox = document.createElement('textarea'); detailBox.style.display = 'none'; detailBox.style.fontSize = '12px'; detailBox.style.color = '#333'; detailBox.style.margin = '5px 0 0 20px'; detailBox.style.width = '90%'; detailBox.style.height = '96px'; detailBox.style.overflowY = 'scroll'; detailBox.style.resize = 'none'; detailBox.value = content; top.append(checkbox, label, toggleBtn, delBtn); row.append(top, detailBox); return row; } function openSettingsPanel() { let panel = document.querySelector('#magic-settings'); if (panel) return panel.style.display = 'block'; panel = document.createElement('div'); panel.id = 'magic-settings'; panel.style.position = 'fixed'; panel.style.top = '50px'; panel.style.left = '10px'; panel.style.background = 'white'; panel.style.border = '1px solid gray'; panel.style.padding = '10px'; panel.style.zIndex = '9999'; panel.style.width = '380px'; const closeBtn = document.createElement('button'); closeBtn.textContent = '关闭'; closeBtn.onclick = () => { panel.style.display = 'none'; }; const saveAllBtn = document.createElement('button'); saveAllBtn.textContent = '保存'; saveAllBtn.style.marginRight = '10px'; saveAllBtn.onclick = async () => { await saveStorage(); alert('所有设置已保存!'); }; const tabMenu = document.createElement('div'); tabMenu.style.display = 'flex'; tabMenu.style.gap = '10px'; tabMenu.style.marginBottom = '10px'; const contentWrapper = document.createElement('div'); const tabs = [ { label: '角色', key: 'custom_roles', list: STATE.roles, selected: STATE.selectedRoles }, { label: '预设', key: 'custom_presets', list: STATE.presets, selected: STATE.selectedPresets }, { label: '艺术家', key: 'custom_artists', list: STATE.artists, selected: STATE.selectedArtists }, { label: '忽略Tag', key: 'ignored_tags', list: STATE.ignore, selected: STATE.selectedTags, isTextOnly: true }, { label: 'API', key: 'google_api_key' } ]; tabs.forEach((tab, idx) => { const btn = document.createElement('button'); btn.textContent = tab.label; btn.style.padding = '2px 6px'; btn.onclick = () => { [...tabMenu.children].forEach(b => b.style.background = ''); btn.style.background = '#def'; contentWrapper.innerHTML = ''; if (tab.key === 'google_api_key') { const apiDiv = document.createElement('div'); apiDiv.style.display = 'flex'; apiDiv.style.flexDirection = 'column'; apiDiv.style.gap = '8px'; const apiTip = document.createElement('div'); apiTip.style.marginBottom = '4px'; apiTip.style.color = '#555'; apiTip.style.fontSize = '13px'; apiTip.innerHTML = '输入谷歌翻译API Key<br>留空使用质量较低的免费API'; const input = document.createElement('input'); input.type = 'text'; input.placeholder = '请输入API Key'; input.value = STATE.googleApiKey || ''; input.style.width = '100%'; const saveBtn = document.createElement('button'); saveBtn.textContent = '保存API Key'; saveBtn.onclick = async () => { STATE.googleApiKey = input.value.trim(); await saveStorage(); alert('API Key已保存'); }; saveBtn.style.margin = '8px 0'; apiDiv.appendChild(apiTip); apiDiv.appendChild(input); apiDiv.appendChild(saveBtn); contentWrapper.appendChild(apiDiv); } else { contentWrapper.append(createManager(tab.label, tab.key, tab.list, tab.selected, tab.isTextOnly)); } }; if (idx === 0) btn.style.background = '#def'; tabMenu.appendChild(btn); }); contentWrapper.append(createManager(tabs[0].label, tabs[0].key, tabs[0].list, tabs[0].selected)); panel.append(tabMenu, contentWrapper, saveAllBtn, closeBtn); document.body.appendChild(panel); } async function init() { const container = document.querySelector('.container'); const tagTable = container?.querySelector('table'); if (!tagTable) return setTimeout(init, 500); await loadStorage(); createButtonBar(tagTable); } init(); })();
QingJ © 2025
镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址