NOTICE: By continued use of this site you understand and agree to the binding Terms of Service and Privacy Policy.
// ==UserScript== // @name AI 이미지 EXIF 뷰어 // @namespace https://greasyfork.org/users/815641 // @match https://arca.live/b/aiart/* // @version 1.1.0 // @author 우흐 // @require https://cdn.jsdelivr.net/gh/photopea/UPNG.js/UPNG.js // @require https://cdn.jsdelivr.net/npm/sweetalert2@11 // @require https://cdn.jsdelivr.net/npm/clipboard@2.0.10/dist/clipboard.min.js // @require https://cdnjs.cloudflare.com/ajax/libs/arrive/2.4.1/arrive.min.js // @grant GM_xmlhttpRequest // @description AI 이미지 메타데이터 보기 // @license MIT // ==/UserScript== (function () { function get(url) { GM_xmlhttpRequest({ method: "GET", responseType: "arraybuffer", url: url, onload: (e) => { try { const png = UPNG.decode(e.response); let prompt; let negativePrompt; let steps; let sampler; let cfgScale; let seed; let size; let denoisingStrength; let software; let rawData; if (png.tabs.tEXt.Description) { rawData = `"Prompt": ${png.tabs.tEXt.Description} ${png.tabs.tEXt.Comment}`; const comment = JSON.parse(png.tabs.tEXt.Comment); prompt = png.tabs.tEXt.Description ?? "정보 없음"; negativePrompt = comment.uc ?? "정보 없음"; steps = comment.steps ?? "정보 없음"; sampler = comment.sampler ?? "정보 없음"; cfgScale = comment.scale ?? "정보 없음"; seed = comment.seed ?? "정보 없음"; size = `${png.width}x${png.height}` ?? "정보 없음"; denoisingStrength = comment.strength ?? "정보 없음"; software = png.tabs.tEXt.Software ?? "정보 없음"; } else if (png.tabs.tEXt.parameters) { rawData = png.tabs.tEXt.parameters; const parameters = png.tabs.tEXt.parameters; const data = parameters.split(": "); let temp = []; data.forEach((el, i) => { if (i < 2) { temp = [ ...temp, el.substr(el.lastIndexOf("\n")).replace("\n", ""), ]; } else { temp = [ ...temp, el.substr(el.lastIndexOf(",")).replace(", ", ""), ]; } }); temp.pop(); let dataObj = {}; temp.forEach((el, i) => { if (i === temp.length - 1) { const arr = parameters .substring(parameters.indexOf(temp[i])) .split(": "); const obj = {}; obj[`${arr[0]}`] = arr[1]; dataObj = { ...dataObj, ...obj }; } else { const arr = parameters .substring( parameters.indexOf(temp[i]), parameters.indexOf(temp[i + 1]) ) .split(": "); const obj = {}; obj[`${arr[0]}`] = arr[1].replace(", ", ""); dataObj = { ...dataObj, ...obj }; } }); prompt = data[0]?.replace("Negative prompt", "") ?? "정보 없음"; negativePrompt = dataObj["Negative prompt"] ?? "정보 없음"; steps = dataObj["Steps"] ?? "정보 없음"; sampler = dataObj["Sampler"] ?? "정보 없음"; cfgScale = dataObj["CFG scale"] ?? "정보 없음"; seed = dataObj["Seed"] ?? "정보 없음"; size = dataObj["Size"] ?? "정보 없음"; denoisingStrength = dataObj["Denoising strength"] ?? "정보 없음"; software = "Web UI 또는 기타..."; } Swal.fire({ title: "메타데이터", html: ` <style> .modalTable { border:1px solid #b3adad; padding:5px; font-size: 12px; } .modalTable td { border:1px solid #b3adad; text-align:left; padding:5px; } .modalTable td.nowrap { white-space:nowrap; font-weight: bold; } .copy_btn { border: 0; border-radius: .25em; background-color: #7066e0; font-size: 1em; color: #fff; line-height: 1.5; padding: .375rem .75rem; } </style> <table class="modalTable" width="100%"> <tbody> <tr> <td class="nowrap">Prompt</td> <td id="prompt">${prompt}</td> <td class="nowrap"> <button class="copy_btn" data-clipboard-target="#prompt">복사</button> </td> </tr> <tr> <td class="nowrap">Negative prompt</td> <td id="negativePrompt">${negativePrompt}</td> <td class="nowrap"> <button class="copy_btn" data-clipboard-target="#negativePrompt">복사</button> </td> </tr> <tr> <td class="nowrap">Steps</td> <td id="steps">${steps}</td> <td class="nowrap"> <button class="copy_btn" data-clipboard-target="#steps">복사</button> </td> </tr> <tr> <td class="nowrap">Sampler</td> <td id="sampler">${sampler}</td> <td class="nowrap"> <button class="copy_btn" data-clipboard-target="#sampler">복사</button> </td> </tr> <tr> <td class="nowrap">CFG scale</td> <td id="cfgScale">${cfgScale}</td> <td class="nowrap"> <button class="copy_btn" data-clipboard-target="#cfgScale">복사</button> </td> </tr> <tr> <td class="nowrap">Seed</td> <td id="seed">${seed}</td> <td class="nowrap"> <button class="copy_btn" data-clipboard-target="#seed">복사</button> </td> </tr> <tr> <td class="nowrap">Size</td> <td id="size">${size}</td> <td class="nowrap"> <button class="copy_btn" data-clipboard-target="#size">복사</button> </td> </tr> <tr> <td class="nowrap">Denoising strength</td> <td id="denoisingStrength">${denoisingStrength}</td> <td class="nowrap"> <button class="copy_btn" data-clipboard-target="#denoisingStrength">복사</button> </td> </tr> <tr> <td class="nowrap">Software</td> <td id="software">${software}</td> <td class="nowrap"> <button class="copy_btn" data-clipboard-target="#software">복사</button> </td> </tr> </tbody> </table> `, footer: ` <details> <summary style="text-align: center;">원본 보기</summary> <p>${rawData}}</p> </details> `, width: "50%", confirmButtonText: "확인", }); } catch (error) { const Toast = Swal.mixin({ toast: true, position: "top-end", showConfirmButton: false, timer: 1500, timerProgressBar: true, }); Toast.fire({ icon: "error", title: "메타데이터 업는듯?", }); } }, onerror: (error) => { console.log(error); }, }); } new ClipboardJS(".copy_btn"); document.arrive("a > img", function () { if (this.classList.contains("channel-icon")) return; this.parentNode.removeAttribute("href"); this.onclick = () => { get(`${this.src}?type=orig`); }; }); })();