AI 이미지 EXIF 뷰어

AI 이미지 메타데이터 보기

目前为 2022-10-10 提交的版本。查看 最新版本

// ==UserScript==
// @name        AI 이미지 EXIF 뷰어
// @namespace   https://gf.qytechs.cn/users/815641
// @match       https://arca.live/b/aiart/*
// @version     1.2.1
// @author      우흐
// @require     https://gf.qytechs.cn/scripts/452821-upng-js/code/UPNGjs.js?version=1103227
// @require     https://cdn.jsdelivr.net/npm/sweetalert2@11
// @require     https://cdn.jsdelivr.net/npm/[email protected]/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==

(async function () {
  async function http(url) {
    return new Promise((resolve, reject) => {
      GM_xmlhttpRequest({
        responseType: "blob",
        url: url,
        method: "GET",
        onload: (response) => resolve(response.response),
        onerror: reject,
      });
    });
  }

  function analyze(png) {
    try {
      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) {
      console.log(error);
    }
  }

  function deepDanbooru(src) {
    Swal.fire({
      icon: "error",
      title: "메타데이터 없음!",
      text: "Deep Danbooru로 찾아볼까요?",
      showCancelButton: true,
      confirmButtonText: "네",
      cancelButtonText: "아니오",
      showLoaderOnConfirm: true,
      backdrop: true,
      preConfirm: async () => {
        return http(`https://deepdanbooru.donmai.us/?url=${src}&min_score=0.4`)
          .then((res) => {
            return res.text();
          })
          .catch((error) => {
            Swal.showValidationMessage(`Request failed: ${error}`);
          });
      },
      allowOutsideClick: () => !Swal.isLoading(),
    }).then((result) => {
      const obj = JSON.parse(result.value);
      if (result.isConfirmed) {
        const tags = obj.map((el) => el[0]).join(", ");

        Swal.fire({
          confirmButtonText: "닫기",
          html: `
          <style>
          #tags {
            width: 100%;
            height: 250px;
            padding: 10px;
            background: #FFF;
            color: black;
            border-radius: 8px;
            font-size: 15px;
            resize:none;
          }
          #tags::-webkit-scrollbar{
            display:none;
          }
          .copy_btn {
            cursor: pointer;
          }
        </style>
          <textarea id="tags">${tags}</textarea>
          <span class="copy_btn" data-clipboard-target="#tags">여기 눌려 전체 복사</span>
          `,
        });
      }
    });
  }
  new ClipboardJS(".copy_btn");

  document.arrive("a > img", function () {
    if (this.classList.contains("channel-icon")) return;
    this.parentNode.removeAttribute("href");

    this.onclick = async () => {
      const src = `${this.src}?type=orig`;

      const res = await http(src);
      const buffer = await res.arrayBuffer();

      if (src.includes(".png")) {
        const png = await UPNG.decode(buffer);

        if (png.tabs.tEXt?.Description || png.tabs.tEXt?.parameters) {
          analyze(png);
        } else {
          deepDanbooru(src);
        }
      } else {
        deepDanbooru(src);
      }
    };
  });
})();

QingJ © 2025

镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址