Greasy Fork

Arcalive Night Restaurant Secretary

2021. 7. 13. 오전 1:16:21

目前为 2021-07-21 提交的版本。查看 最新版本

// ==UserScript==
// @name        Arcalive Night Restaurant Secretary
// @namespace   Violentmonkey Scripts
// @match       https://arca.live/b/*
// @grant       GM_xmlhttpRequest
// @grant       GM_registerMenuCommand
// @version     1.0.0
// @author      -
// @description 2021. 7. 13. 오전 1:16:21
// @noframes
// ==/UserScript==

// onload init
let loaded = false
window.addEventListener('load', e => {
  if (!loaded) {
    loaded = true
    registerMenuCommand();
    asideMake();
    //easyFroalaInsertImageTrigger()
    autoWriteEditor();
    arcaconLinkInit();
    userInfoViewInit();
  }
});

// 배열 자르기, 페이지네이션
Array.prototype.division = function(n) {
  var arr = this;
  var len = arr.length;
  var cnt = Math.floor(len / n) + (Math.floor(len % n) > 0 ? 1 : 0);
  var tmp = [];
  for (var i = 0; i < cnt; i++) {
    tmp.push(arr.splice(0, n));
  }
  return tmp;
}

// 바이트값 정리 함수
function formatBytes(bytes, decimals = 2) {
  if (bytes === 0) return '-';
  const k = 1024;
  const dm = decimals < 0 ? 0 : decimals;
  const sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'];
  const i = Math.floor(Math.log(bytes) / Math.log(k));
  return parseFloat((bytes / Math.pow(k, i)).toFixed(dm)) + ' ' + sizes[i];
}

// 키 시뮬레이트
function simulateKey(keyCode, type, modifiers) {
  var evtName = (typeof(type) === "string") ? "key" + type : "keydown";
  var modifier = (typeof(modifiers) === "object") ? modifier : {};

  var event = document.createEvent("HTMLEvents");
  event.initEvent(evtName, true, false);
  event.keyCode = keyCode;

  for (var i in modifiers) {
    event[i] = modifiers[i];
  }

  document.dispatchEvent(event);
}

// froalaeditor 이미지 삽입 감지 (미완성)
async function easyFroalaInsertImageTrigger() {
  if (location.href.includes("/write") || location.href.includes("/edit")) {
    let imageResizer = document.querySelector(".fr-image-resizer")
    const editorTextarea = document.querySelector("#content")
    let oldMatch = 0
    while (true) {
      await sleep(100)
      let newMatch = editorTextarea.value.match(/<img.*?class="fr-fic fr-dii".*?>/g)
      if (newMatch && oldMatch !== newMatch.length) {
        oldMatch = newMatch.length
        await sleep(500)
        simulateKey(37);
        await sleep(10)
        simulateKey(37, "up");
        simulateKey(40);
        await sleep(10)
        simulateKey(40, "up");
        await sleep(10)
        simulateKey(13, "up");
      } else if (newMatch === null) {
        oldMatch = 0
      }
    }
  }
}

// fetch 함수
function doFetch(url, options = {
  method: 'GET',
  responseType: 'document'
}, silent = false) {
  return new Promise((resolve, reject) => {
    GM_xmlhttpRequest({
      url: url,
      method: options.method,
      responseType: options.responseType,
      headers: options.headers,
      data: options.data,
      onload: result => {
        console.debug(result)
        if (result.status == 200) {
          resolve(result.response);
        } else {
          if (!silent) {
            console.log(result)
            alert("불러오기 에러 발생 : " + url)
            reject(result.status);
          } else {
            console.debug(result)
            reject(result.status);
          }
        }
      }
    });
  });
}

// 사이드바 추가
function asideMake() {

  const rightSidebar = document.querySelector(".right-sidebar")

  const sidebarItem = document.createElement('div')
  sidebarItem.setAttribute("class", "sidebar-item")
  sidebarItem.setAttribute("id", "smtranslation")

  const itemTitle = document.createElement('div')
  itemTitle.setAttribute("class", "item-title")
  itemTitle.innerText = "번역 소식"

  var style = document.createElement('style');
  style.type = 'text/css';
  style.innerHTML = '.sm-translate-tab { padding-bottom: .25rem;border-bottom: 0 solid #00a495;margin:8.7%;cursor:pointer;transition-duration: 0.1s }';
  style.innerHTML += '.sm-translate-tab-activated { padding-bottom: .25rem;border-bottom: 2px solid #00a495;margin:8.7%;cursor:pointer;transition-duration: 0.1s }';
  style.innerHTML += '.sm-translate-arrow-left { margin: auto 5px;float:right;width: 0; height: 0; border-top: 5px solid transparent; border-bottom: 5px solid transparent; border-right: 7px solid black;cursor:pointer }';
  style.innerHTML += '.sm-translate-arrow-right { margin: auto 5px;float:right;width: 0; height: 0; border-top: 5px solid transparent; border-bottom: 5px solid transparent; border-left: 7px solid black;cursor:pointer }';
  document.getElementsByTagName('head')[0].appendChild(style);

  const smallTitle = document.createElement('div')
  smallTitle.setAttribute("style", "margin-bottom:12px")

  const linkList = document.createElement('div')
  linkList.setAttribute("class", "link-list clearfix")

  const arrowDiv = document.createElement('div')
  arrowDiv.setAttribute("class", "sm-translation-arrows")

  const arrowRight = document.createElement("div")
  arrowRight.setAttribute("class", "sm-translate-arrow-right")
  const arrowLeft = document.createElement("div")
  arrowLeft.setAttribute("class", "sm-translate-arrow-left")

  arrowDiv.appendChild(arrowRight)
  arrowDiv.appendChild(arrowLeft)

  const translating = document.createElement('span')
  translating.setAttribute("class", "sm-translate-tab-activated")
  translating.innerText = "번역중"
  translating.onclick = (e) => {
    activateTab(e)
    arrowDiv.setAttribute("style", "display: none")
    translatingPageNum = 0
    // 카테고리: 실황, 키워드: 번역 / 색인
    getTranslatePageFetch([
        `https://arca.live/b/smpeople?target=content&keyword=${srt}`,
        "https://arca.live/b/smpeople?category=실황&target=title&keyword=번역"
      ], linkList, arrowRight, arrowLeft)
      .then(t => {
        if (t) {
          arrowDiv.removeAttribute("style")
        }
      })
  }

  const notranslate = document.createElement('span')
  notranslate.setAttribute("class", "sm-translate-tab")
  notranslate.innerText = "미번역"
  notranslate.onclick = (e) => {
    activateTab(e)
    arrowDiv.setAttribute("style", "display: none")
    translatingPageNum = 0
    // 키워드: 번역요청
    getTranslatePageFetch([
        "https://arca.live/b/smpeople?target=title&keyword=번역요청"
      ], linkList, arrowRight, arrowLeft)
      .then(t => {
        if (t) {
          arrowDiv.removeAttribute("style")
        }
      })
  }

  const dotranslate = document.createElement('span')
  dotranslate.setAttribute("class", "sm-translate-tab")
  dotranslate.innerText = "찜하기"
  dotranslate.onclick = (e) => {
    activateTab(e)
    arrowDiv.setAttribute("style", "display: none")
    translatingPageNum = 0
    makeReservation(linkList, arrowRight, arrowLeft)
      .then(t => {
        arrowDiv.removeAttribute("style")
      })
  }

  smallTitle.appendChild(translating)
  smallTitle.appendChild(notranslate)
  smallTitle.appendChild(dotranslate)

  sidebarItem.appendChild(itemTitle)
  sidebarItem.appendChild(smallTitle)
  sidebarItem.appendChild(linkList)
  sidebarItem.appendChild(arrowDiv)

  rightSidebar.prepend(sidebarItem)

  translating.click()
}

// 사이드바 내용 채우기
function getTranslatePageFetch(urlList, element, arrowRight, arrowLeft, nonPage = false) {
  return new Promise(async (resolve, reject) => {
    if (typeof urlList === "string") {
      urlList = [urlList]
    }
    let articleMetaArray = [];
    let articleArray = [];
    for (let url of urlList) {
      await doFetch(url)
        .then(dom => {
          let articleList = dom.querySelector(".article-list").querySelectorAll(".vrow:not(.notice):not(.head)")

          articleList.forEach(a => {
            let article = new Object
            article.href = a.href.split("?")[0]
            let articleSplit = a.querySelector(".title").innerText.split(/(\[?.?번역요청.?\]|\(?.?번역요청.?\))/)
            article.title = articleSplit[2] || articleSplit[0]
            article.author = a.querySelector("span.vcol.col-author > span > span:nth-child(1)").innerText

            let articleSpan = document.createElement('span')
            articleSpan.setAttribute("class", "leaf-info float-right")
            articleSpan.style.margin = 'auto 0 auto 10px'
            articleSpan.innerText = article.author

            let articleA = document.createElement('a')
            articleA.setAttribute("href", article.href)
            articleA.setAttribute("target", "_blank")
            articleA.style.padding = '.15rem 0 .15rem 0'
            articleA.appendChild(articleSpan)
            articleA.append(article.title)

            articleArray.push(articleA)
          })
        })
    }

    if (nonPage) {
      articleMetaArray = [articleArray]
      if (articleArray.length > 10) {
        contentStretch()
      }
    } else {
      articleMetaArray = articleArray.division(10)
    }
    arrowRight.onclick = (e) => {
      translatingPage(element, articleMetaArray, 1)
    }
    arrowLeft.onclick = (e) => {
      translatingPage(element, articleMetaArray, -1)
    }
    translatingPage(element, articleMetaArray, 0)
    if (articleMetaArray[0].length > 0) {
      resolve(true)
    } else {
      resolve(false)
    }
  })
}

// 본문 늘리기
function contentStretch() {
  const contentWrapper = document.querySelector(".root-container > .content-wrapper")
  contentWrapper.setAttribute("style", "padding-bottom:" + contentWrapper.offsetHeight / 2 + "px")
  document.querySelectorAll(".ad").forEach(ad => {
    ad.remove()
  })
}

// 페이지 이동
let translatingPageNum = 0

function translatingPage(element, arr, delta) {
  let tmpPage = translatingPageNum + delta
  let pageContent = arr[tmpPage]

  if (arr[0].length > 0 && arr[tmpPage]) {
    translatingPageNum = tmpPage
    element.innerHTML = ''
    pageContent.forEach(content => {
      element.appendChild(content)
    })
  }
}

// 번역찜
function makeReservation(element, arrowRight, arrowLeft) {
  return new Promise((resolve, reject) => {

    const inputGroup = document.createElement("div")
    inputGroup.setAttribute("class", "input-group input-group-sm mb-3")
    inputGroup.setAttribute("style", "border-radius: 5px;")

    const inputGroupPrepend = document.createElement("div")
    inputGroupPrepend.setAttribute("class", "input-group-prepend")

    const inputGroupButton = document.createElement("div")
    inputGroupButton.setAttribute("class", "btn btn-sm btn-arca")
    inputGroupButton.setAttribute("data-toggle", "dropdown")
    inputGroupButton.innerText = "플랫폼"

    inputGroupPrepend.appendChild(inputGroupButton)

    const inputGroupDropdown = document.createElement("div")
    inputGroupDropdown.setAttribute("class", "dropdown-menu")

    const inputCode = document.createElement("input")
    inputCode.setAttribute("class", "form-control")
    inputCode.setAttribute("placeholder", "플랫폼을 선택하세요")
    inputCode.setAttribute("type", "text")

    const dropdowns = ["dlsite", "hitomi", "e-hentai", "getchu", "fanza"]

    dropdowns.forEach(str => {
      const inputGroupDropdownMenu = document.createElement("div")
      inputGroupDropdownMenu.setAttribute("class", "dropdown-item")
      inputGroupDropdownMenu.setAttribute("style", "cursor:pointer")
      inputGroupDropdownMenu.onclick = function(e) {
        inputGroupButton.innerText = e.target.innerText
        switch (e.target.innerText) {
          case "dlsite":
            inputCode.setAttribute("placeholder", "작품 코드 또는 주소")
            break;
          case "hitomi":
            inputCode.setAttribute("placeholder", "자료 코드 또는 주소")
            break;
          case "e-hentai":
            inputCode.setAttribute("placeholder", "자료 주소")
            break;
          case "getchu":
          case "fanza":
            inputCode.setAttribute("placeholder", "작품 주소")
            break;
        }
      }
      inputGroupDropdownMenu.innerText = str
      inputGroupDropdown.appendChild(inputGroupDropdownMenu)
    })

    inputGroupPrepend.appendChild(inputGroupDropdown)
    inputGroup.appendChild(inputGroupPrepend)
    inputGroup.appendChild(inputCode)

    const checkboxDiv = document.createElement("div")

    const checkboxValues = [
      ["역자", "식자"],
      ["손번역", "기계번역", "이미지번역"],
      ["개인", "팀", "동료 모집"]
    ]

    easyCheckbox(checkboxDiv, checkboxValues, "sm-translation-reservation-checkbox")

    const inputDescription = document.createElement("textarea")
    inputDescription.setAttribute("class", "form-control")
    inputDescription.setAttribute("style", "border-color: #ccc;border-radius: 5px;margin-bottom:10px;resize:none;height:10rem")
    inputDescription.setAttribute("placeholder", "덧붙이는 글 (선택)")

    const submitButton = document.createElement("div")
    submitButton.setAttribute("class", "btn btn-sm btn-arca float-right")
    submitButton.setAttribute("style", "margin:5px")
    submitButton.innerText = "번역찜"
    submitButton.onclick = function(e) {
      const optionList = []
      let optionStr
      document.querySelectorAll(".sm-translation-reservation-checkbox").forEach(chkb => {
        if (chkb.checked) {
          optionList.push(chkb.value)
        }
      })
      if (optionList.length > 0) {
        optionStr = arr2str(optionList)
      } else {
        optionStr = "선택되지 않음"
      }
      doReservation(inputGroupButton.innerText, inputCode.value, element, arrowRight, arrowLeft)
        .then(async data => {
          const dataJSON = data[1]
          let title
          if (optionStr.includes("모집")) {
            title = `[번역찜] [모집] ${dataJSON.title[1]}`
          } else {
            title = `[번역찜] ${dataJSON.title[1]}`
          }
          if (title.length > 50) {
            title = title.substring(0, 49) + "…"
          }
          dataJSON.sm_translation_option = ["번역찜 옵션", optionStr]
          let content
          await makeTable(dataJSON, true).then(table => {
            content = (table.outerHTML + `<p><br></p><p>${inputDescription.value}</p>` + indexerHTML(data[2]))
          })
          const editorButton = document.createElement("div")
          editorButton.setAttribute("class", "btn btn-sm btn-arca float-right")
          editorButton.setAttribute("style", "margin:5px")
          editorButton.innerText = "에디터에서 수정"
          editorButton.onclick = function(e) {
            localStorage.setItem('sm_translation_tmp_title', title);
            localStorage.setItem('sm_translation_tmp_content', content.replace(/\n/g, "<br>"));
            location.href = "/b/testtesttest/write/"
          }

          const directButton = document.createElement("div")
          directButton.setAttribute("class", "btn btn-sm btn-arca float-right")
          directButton.setAttribute("style", "margin:5px")
          directButton.innerText = "번역찜 제출"
          directButton.onclick = function(e) {
            submitReservation(title, content.replace(/\n/g, "<br>"))
          }

          element.appendChild(directButton)
          element.appendChild(editorButton)
          if (data[0]) {
            alert("번통사고 예방을 위해 목록을 확인해 주십시오")
          } else {
            submitButton.remove()
          }
        })
    }
    element.innerHTML = ''
    element.appendChild(inputGroup)
    element.appendChild(checkboxDiv)
    element.appendChild(inputDescription)
    element.appendChild(submitButton)
  })
}

// 체크박스 만들기
function easyCheckbox(element, valMetaList, className) {
  valMetaList.forEach(valList => {
    let checkbox
    let checkboxLabel
    let pTag = document.createElement("p")
    valList.forEach(val => {
      checkbox = document.createElement("input")
      checkbox.setAttribute("class", className)
      checkbox.setAttribute("type", "checkbox")
      checkbox.setAttribute("value", val)

      checkboxLabel = document.createElement("label")
      checkboxLabel.setAttribute("style", "margin:auto 5px")
      checkboxLabel.appendChild(checkbox)
      checkboxLabel.append(` ${val}`)
      pTag.appendChild(checkboxLabel)
    })
    element.appendChild(pTag)
  })
}

// 색인
const srt = "Q4VR D8W2 9H5K 0PFK 3JM7 GX0M"

function indexerHTML(str) {
  let a = `<p style="font-size:0;">${str} ${srt}</p>`
  return a
}

// 번역찜 하기
function doReservation(platform, code, element, arrowRight, arrowLeft) {
  return new Promise((resolve, reject) => {
    let ProcessedCode
    let parser
    ProcessedCode = code.split("?")[0]
    switch (platform) {
      case "dlsite":
        ProcessedCode = dlsiteAuto(code)
        parser = dlInfo
        break;
      case "hitomi":
        ProcessedCode = hitomiAuto(code)
        parser = hitomiInfo
        break;
      case "e-hentai":
        ProcessedCode = ehAuto(code)
        parser = ehInfo
        break;
      case "getchu":
        ProcessedCode = getchuAuto(code)
        parser = getchuInfo
        break;
      case "fanza":
        ProcessedCode = fanzaAuto(code)
        parser = fanzaInfo
        break;
      default:
        alert("플랫폼을 선택해 주십시오")
        break;
    }
    parser(ProcessedCode).then(obj => {
      let searchList = [
        `https://arca.live/b/smpeople?target=content&keyword=${ProcessedCode} ${srt}`,
        `https://arca.live/b/smpeople?category=번역&target=all&keyword=${obj.title[1]}`,
        `https://arca.live/b/smpeople?category=실황&target=all&keyword=${obj.title[1]} 번역`
      ]
      let numCode = ProcessedCode.match(/[A-Z]{2}\d+/g)
      if (numCode && numCode.length === 1) {
        searchList.push(`https://arca.live/b/smpeople?target=all&keyword=${numCode[0].replace(/[A-Z]{2}(\d+)/g, "$1")} 번역`)
      }
      getTranslatePageFetch(searchList, element, arrowRight, arrowLeft, true)
        .then(t => {
          resolve([t, obj, ProcessedCode])
        })
    })
  })
}

// 번역찜 글 제출
function submitReservation(title, content) {
  const url = "https://arca.live/b/testtesttest/write"
  getWriteToken()
    .then(tokenList => {
      fetch(url, {
          method: 'POST',
          body: new URLSearchParams({
            '_csrf': tokenList[0],
            'token': tokenList[1],
            'contentType': 'html',
            'category': 'ℹ️',
            'title': String(title),
            'content': String(content)
          })
        })
        .then(response => {
          if (response.ok) {
            location.href = response.url
          } else {
            console.log(response)
            alert("번역찜 글이 제출 과정에서 오류가 발생했습니다.")
          }
        })
    })
}

// 에디터 자동 내용 채우기
async function autoWriteEditor() {
  if (location.href.includes("/write")) {
    const title = localStorage.getItem('sm_translation_tmp_title');
    const content = localStorage.getItem('sm_translation_tmp_content');
    if (title && content) {
      localStorage.removeItem('sm_translation_tmp_title')
      localStorage.removeItem('sm_translation_tmp_content')

      let titleBox = document.querySelector("#inputTitle")
      let editorBox = document.querySelector('.write-body .fr-element')
      while (titleBox === null || editorBox === null) {
        await sleep(10)
        titleBox = document.querySelector("#inputTitle")
        editorBox = document.querySelector('.write-body .fr-element')
      }
      titleBox.value = title
      editorBox.innerHTML = content
      document.querySelector("#category-ℹ️").click()
    }
  }
}

// 글쓰기 토큰
function getWriteToken() {
  return new Promise((resolve, reject) => {
    if (location.href.includes("/write") || location.href.includes("/edit")) {
      const csrf = document.querySelector("input[name=_csrf]")
      const token = document.querySelector("input[name=token]")
      if (csrf && token) {
        resolve([csrf.value, token.value])
      }
    } else {
      let url = "https://arca.live/b/testtesttest/write"
      doFetch(url)
        .then(html => {
          const csrf = html.querySelector("input[name=_csrf]")
          const token = html.querySelector("input[name=token]")
          if (csrf && token) {
            resolve([csrf.value, token.value])
          }
        })
    }
  })
}

// 이미지(Blob) 업로드 함수
function UploadImageFile(Blob, pretoken = null, original = false) {
  return new Promise(async (resolve, reject) => {
    var xhr = new XMLHttpRequest();
    var formData = new FormData();

    if (!document.querySelector("#article_write_form > input[name=token]")) {
      await getWriteToken()
        .then(tokenList => {
          pretoken = tokenList[1]
        })
    }

    formData.append('upload', Blob, Math.random().toString(36).substring(7) + '.jpg');
    formData.append('token', pretoken || document.querySelector("#article_write_form > input[name=token]").value);
    formData.append('saveExif', original);
    formData.append('saveFilename', false);

    xhr.onload = function() {
      responseJSON = JSON.parse(xhr.responseText)
      if (responseJSON.uploaded === true) {
        resolve(responseJSON.url)
      } else {
        alert("이미지 업로드 실패 : " + xhr.responseText)
        console.error(xhr.responseText);
      }
    }
    xhr.open("POST", "https://arca.live/b/upload");
    xhr.send(formData);
  });
}

// 탭 전환
function activateTab(e) {
  const tab = document.querySelector(".sm-translate-tab-activated")
  tab.setAttribute("class", "sm-translate-tab")
  e.target.setAttribute("class", "sm-translate-tab-activated")
}

// 배열을 문자열로 정리
function arr2str(arr, opt = ", ") {
  let idx = arr.indexOf("")
  while (idx > -1) {
    arr.splice(idx, 1)
    idx = arr.indexOf("")
  }
  return String(arr).replace(/"/g, '').replace('[', "").replace(']', "").replace(/,/g, opt)
}

// 문자열 정리
function strTrim(str) {
  return str.replace(/\n/g, '').replace(/^\s+|\s+$/g, '').replace(/ +/g, " ").trim()
}

// JSON을 글 양식을 표로
async function makeTable(obj, getTable = false) {
  if (location.href.includes("/write") || location.href.includes("/edit") || getTable) {
    const table = document.createElement("table")
    table.setAttribute("style", "width:100%")
    const tbody = document.createElement("tbody")

    for (let [key, value] of Object.entries(obj)) {
      if (value[1]) {
        const tr = document.createElement("tr")
        const tdA = document.createElement("td")
        tdA.setAttribute("style", "width: 20%; text-align: center;")
        tdA.innerText = value[0]
        const tdB = document.createElement("td")
        tdB.setAttribute("style", "width: 80%; text-align: center;")
        tdB.innerHTML = value[1]
        tr.appendChild(tdA)
        tr.appendChild(tdB)
        tbody.appendChild(tr)
      }
    }
    table.appendChild(tbody)

    if (!getTable) {
      let editorBox = document.querySelector('.write-body .fr-element')

      while (editorBox === null) {
        await sleep(10)
        editorBox = document.querySelector('.write-body .fr-element')
      }
      editorBox.appendChild(table)
    } else {
      return table
    }
  } else {
    return obj
  }
}

// dlsite api사용
function dlInfo(code, getTable = false) {
  return new Promise((resolve, reject) => {
    const url = `https://www.dlsite.com/maniax/api/=/product.json?workno=${code}&locale=ko-K`
    doFetch(url, {
        method: 'GET',
        responseType: 'application/json'
      })
      .then(result => {
        const dlJSON = JSON.parse(result)[0]
        if (dlJSON) {
          const dlProcessedJSON = Object()
          dlProcessedJSON.title = ["제목", dlJSON["work_name"]]
          doFetch(`https:${dlJSON["image_main"]["url"]}`, {
              method: "GET",
              responseType: "blob"
            })
            .then(blob => {
              UploadImageFile(blob)
                .then(imgsrc => {
                  dlProcessedJSON.image = ["이미지", `<img src='${imgsrc}' alt='이미지'>`]
                  dlProcessedJSON.work_type = ["분류", `${dlJSON["work_type"]} / ${dlJSON["work_type_string"]}`]
                  dlProcessedJSON.file_info = ["파일(용량)", `${dlJSON["file_type"]}(${formatBytes(dlJSON["contents_file_size"])})`]

                  if (dlJSON["creaters"]) {
                    dlProcessedJSON.creators = []
                    for (const [key, value] of Object.entries(dlJSON["creaters"])) {
                      let tmpArr = []
                      value.forEach(name => {
                        tmpArr.push(name.name)
                      })
                      dlProcessedJSON.creators.push(key + ': ' + arr2str(tmpArr, "|"))
                    }
                    dlProcessedJSON.creators = ["만든이", arr2str(dlProcessedJSON.creators, " / ").replace(/\|/g, ", ")]
                  }

                  dlProcessedJSON.maker = ["서클", dlJSON["maker_name"]]

                  if (dlJSON["author"]) {
                    dlProcessedJSON.authors = []
                    for (const [key, value] of Object.entries(dlJSON["author"])) {
                      dlProcessedJSON.authors.push(value.author_name)
                    }
                    dlProcessedJSON.authors = ["작가", arr2str(dlProcessedJSON.authors)]
                  }
                  resolve(dlProcessedJSON)

                  if (dlJSON["genres"]) {
                    dlProcessedJSON.genres = []
                    dlJSON["genres"].forEach(genre => {
                      dlProcessedJSON.genres.push(genre["name"])
                    })
                    dlProcessedJSON.genres = ["태그", arr2str(dlProcessedJSON.genres)]
                  }

                  if (dlJSON["trials"]) {
                    dlProcessedJSON.trial = ["체험판", `<a href='https:${dlJSON["trials"][0]["url"]}'>체험판 다운로드(${dlJSON["trials"][0]["file_size_unit"]})</a>`]
                  }
                  dlProcessedJSON.url = ["출처", `<a href='https://www.dlsite.com/maniax/work/=/product_id/${code}.html'>보러가기</a>`]

                  resolve(makeTable(dlProcessedJSON, getTable))
                })
            })
        } else {
          alert("유효하지 않은 코드입니다!")
        }
      })
  })
}

// dlsite 정보 채우기
function dlsiteAuto(code = null) {
  let result = code || window.prompt("dlsite 코드 또는 주소를 입력하세요")
  if (result && result.match(/[A-Z]{2}\d+/g)) {
    if (!code) {
      dlInfo(result.match(/[A-Z]{2}\d+/g)[0])
    } else {
      return result.match(/[A-Z]{2}\d+/g)[0]
    }
  } else {
    alert("dlsite 코드 또는 주소를 읽을 수 없습니다!")
  }
}

// hitomi api사용
function hitomiInfo(code, getTable = false) {
  return new Promise(async (resolve, reject) => {
    const url = `https://hitomi.la/galleries/${code}.html`
    await doFetch(url)
      .then(async html => {
        const redirect = html.querySelector("body > a")
        await doFetch(redirect.href)
          .then(async html => {
            const hitomiProcessedJSON = Object()
            const infoBox = html.querySelector(".content")
            const author = html.querySelector(".gallery > h2 > ul")
            const authors = []
            author.querySelectorAll("li").forEach(tag => {
              authors.push(tag.innerText)
            })
            const imgUrl = infoBox.querySelector(".cover > a > picture > img").srcset.split(" 2x")[0]
            await doFetch(imgUrl, {
                method: 'GET',
                responseType: 'blob'
              })
              .then(blob => {
                UploadImageFile(blob)
                  .then(imgsrc => {
                    hitomiProcessedJSON.title = ["제목", infoBox.querySelector(".gallery > h1").innerText]
                    hitomiProcessedJSON.image = ["표지", `<img src='${imgsrc}' alt='표지'>`]
                    hitomiProcessedJSON.author = ["작가", arr2str(authors)]
                    infoBox.querySelector(".gallery-info > table > tbody").querySelectorAll("tr").forEach(tr => {
                      let rtd1 = tr.querySelector("td:nth-child(1)")
                      let rtd2 = tr.querySelector("td:nth-child(2)")
                      if (rtd1 && rtd2) {
                        let td1 = strTrim(rtd1.innerText)
                        let td2 = strTrim(rtd2.innerText)
                        switch (td1) {
                          case "Group":
                            hitomiProcessedJSON.group = ["그룹", td2]
                            break;
                          case "Type":
                            hitomiProcessedJSON.type = ["분류", td2]
                            break;
                          case "Language":
                            hitomiProcessedJSON.language = ["언어", td2]
                            break;
                          case "Series":
                            hitomiProcessedJSON.series = ["시리즈", td2]
                            break;
                          case "Characters":
                            let charList = []
                            tr.querySelectorAll("li").forEach(tag => {
                              charList.push(strTrim(tag.innerText))
                            })
                            hitomiProcessedJSON.characters = ["캐릭터", arr2str(charList)]
                            break;
                          case "Tags":
                            let tagList = []
                            tr.querySelectorAll("li").forEach(tag => {
                              tagList.push(strTrim(tag.innerText))
                            })
                            hitomiProcessedJSON.tags = ["태그", arr2str(tagList)]
                            break;
                        }
                      }
                    })
                    hitomiProcessedJSON.url = ["출처", `<a href='${url}'>보러가기</a>`]
                    resolve(makeTable(hitomiProcessedJSON, getTable))
                  })
              })
          })
      })
  })
}

// hitomi 정보 채우기
function hitomiAuto(code = null) {
  let result = code || window.prompt("hitomi 코드 또는 주소를 입력하세요");
  if (result && result.match(/\d+\.htm/)) {
    if (!code) {
      hitomiInfo(result.match(/\d+\.htm/)[0].replace(".htm", ""))
    } else {
      return result.match(/\d+\.htm/)[0].replace(".htm", "")
    }
  } else {
    if (result && result.match(/\d+/g) && result.match(/\d+/g).length === 1) {
      if (!code) {
        hitomiInfo(result.match(/\d+/g)[0])
      } else {
        return result.match(/\d+/g)[0]
      }
    } else {
      alert("hitomi 코드 또는 주소를 읽을 수 없습니다!")
    }
  }
}

// hitomi 작품 전체 가져오기
async function hitomiGetImage(code) {

  function subdomain_from_url(url, base) {
    var retval = 'b';
    if (base) {
      retval = base;
    }

    var number_of_frontends = 3;
    var b = 16;

    var r = /\/[0-9a-f]\/([0-9a-f]{2})\//;
    var m = r.exec(url);
    if (!m) {
      return 'a';
    }

    var g = parseInt(m[1], b);
    if (!isNaN(g)) {
      var o = 0;
      if (g < 0x80) {
        o = 1;
      }
      if (g < 0x40) {
        o = 2;
      }
      retval = String.fromCharCode(97 + o) + retval;
    }

    return retval;
  }

  function url_from_url(url, base) {
    return url.replace(/\/\/..?\.hitomi\.la\//, '//' + subdomain_from_url(url, base) + '.hitomi.la/');
  }

  function full_path_from_hash(hash) {
    if (hash.length < 3) {
      return hash;
    }
    return hash.replace(/^.*(..)(.)$/, '$2/$1/' + hash);
  }

  function url_from_hash(galleryid, image, dir, ext) {
    ext = ext || dir || image.name.split('.').pop();
    dir = dir || 'images';

    return 'https://a.hitomi.la/' + dir + '/' + full_path_from_hash(image.hash) + '.' + ext;
  }

  function url_from_url_from_hash(galleryid, image, dir, ext, base) {
    return url_from_url(url_from_hash(galleryid, image, dir, ext), base);
  }

  const ltnUrl = `https://ltn.hitomi.la/galleries/${code}.js`
  const reqReferer = `https://hitomi.la/reader/${code}.html`
  let editorBox
  editorBox = document.querySelector('.write-body .fr-element')
  while (editorBox === null) {
    await sleep(10)
    editorBox = document.querySelector('.write-body .fr-element')
  }

  await doFetch(ltnUrl, {
      method: 'GET',
      responseType: 'application/json'
    })
    .then(async str => {
      const hitomiJSON = JSON.parse(str.replace("var galleryinfo = ", ""))

      let fileProgress = document.createElement("progress");
      fileProgress.setAttribute("value", "0")
      fileProgress.setAttribute("max", "1")
      fileProgress.setAttribute("style", "width:100%;")
      fileProgress.setAttribute("id", "sm-translation-hitomiAuto-fileProgress")

      const row = document.querySelector("#article_write_form > div.row > div")
      row.appendChild(fileProgress)
      let index = 0

      for (let dict of hitomiJSON.files) {
        let url = url_from_url_from_hash(code, dict)
        var tmpPromise = await doFetch(url, {
            method: "GET",
            responseType: 'blob',
            headers: {
              referer: reqReferer
            }
          })
          .then(async res => {
            var tmpPromise = await UploadImageFile(res).then(url => {
              let imgTag = document.createElement("img")
              imgTag.setAttribute("src", url)
              editorBox.appendChild(imgTag)
            })
          })
        index += 1
        fileProgress.value = index / hitomiJSON.files.length
      }
      fileProgress.remove()
    })
}

// hitomi 전체 가져오기
function hitomiFetch() {
  let result = window.prompt("hitomi코드를 입력하세요");
  if (result && result.match(/\d+\.htm/)) {
    hitomiGetImage(result.match(/\d+\.htm/)[0].replace(".htm", ""))
  } else {
    if (result && result.match(/\d+/g) && result.match(/\d+/g).length === 1) {
      hitomiGetImage(result.match(/\d+/g)[0])
    } else {
      alert("hitomi 코드 또는 주소를 읽을 수 없습니다!")
    }
  }
}

// e-hentai 정보 가져오기
function ehInfo(url, getTable = false) {
  return new Promise((resolve, reject) => {
    const apiUrl = "https://api.e-hentai.org/api.php"
    const gidlist = url.match(/\d+\/\w+/)[0].split("/")

    doFetch(apiUrl, {
        method: "POST",
        responseType: 'json',
        data: JSON.stringify({
          "method": "gdata",
          "gidlist": [gidlist],
          "namespace": 1
        })
      })
      .then(json => {
        const ehJSON = json.gmetadata[0]
        const ehProcessedJSON = Object()
        doFetch(ehJSON.thumb.replace("_l", "_250"), {
            method: 'GET',
            responseType: 'blob'
          })
          .then(blob => {
            UploadImageFile(blob)
              .then(imgsrc => {
                ehProcessedJSON.title = ["제목", ehJSON.title]
                ehProcessedJSON.thumb = ["표지", `<img src='${imgsrc}' alt='표지'>`]
                const ehTags = Object()
                ehTags.artist = []
                ehTags.group = []
                ehTags.character = []
                ehTags.language = []
                ehTags.male = []
                ehTags.female = []
                ehTags.tags = []
                ehJSON.tags.forEach(tag => {
                  let tagsl = tag.split(":")
                  switch (tagsl[0]) {
                    case "artist":
                      ehTags.artist.push(tagsl[1]);
                      break;
                    case "group":
                      ehTags.group.push(tagsl[1]);
                      break;
                    case "character":
                      ehTags.character.push(tagsl[1]);
                      break;
                    case "parody":
                      ehTags.character.push(tagsl[1]);
                      break;
                    case "language":
                      ehTags.language.push(tagsl[1]);
                      break;
                    case "male":
                      ehTags.male.push(tagsl[1] + " ♂");
                      break;
                    case "female":
                      ehTags.female.push(tagsl[1] + " ♀");
                      break;
                    default:
                      ehTags.tags.push(tagsl[1]);
                      break;
                  }
                })
                ehProcessedJSON.author = ["작가", arr2str(ehTags.artist)]
                ehProcessedJSON.group = ["그룹", arr2str(ehTags.group)]
                ehProcessedJSON.language = ["언어", arr2str(ehTags.language)]
                ehProcessedJSON.character = ["캐릭터", arr2str(ehTags.character)]
                ehProcessedJSON.tags = ["태그", arr2str(ehTags.tags.concat(ehTags.female.concat(ehTags.male)))]
                ehProcessedJSON.filecount = ["분량", `${ehJSON.filecount}장(${formatBytes(ehJSON.filesize)})`]
                ehProcessedJSON.url = ["출처", `<a href='${url}'>보러가기</a>`]
                resolve(makeTable(ehProcessedJSON, getTable))
              })
          })
      })
  })
}

// e-hentai 정보 채우기
function ehAuto(code = null) {
  let result = code || window.prompt("e-hentai 주소를 입력하세요");
  if (result && result.includes("e-hentai")) {
    if (!code) {
      ehInfo(result)
    } else {
      return result
    }
  }
}

// getchu 파싱
function getchuInfo(url, getTable = false) {
  return new Promise((resolve, reject) => {
    doFetch(url)
      .then(html => {
        const getchuProcessedJSON = Object()
        const infoBox = html.querySelector("#soft_table")
        getchuProcessedJSON.title = ["제목", strTrim(infoBox.querySelector("#soft-title").innerText)]
        const infoTable = infoBox.querySelectorAll("tbody > tr:nth-child(2) > th > table > tbody > tr")
        doFetch(`http://www.getchu.com/brandnew${infoBox.querySelector(".highslide").href.split("brandnew")[1]}`, {
            method: "GET",
            responseType: "blob",
            headers: {
              referer: url
            }
          })
          .then(async blob => {
            var tmpPromise = UploadImageFile(blob)
              .then(imgUrl => {
                getchuProcessedJSON.image = ["이미지", `<img src='${imgUrl}' alt="이미지">`]
                infoTable.forEach(tr => {
                  let infoTd = tr.querySelectorAll("td")
                  if (infoTd.length > 1) {
                    let td1 = strTrim(infoTd[0].innerText)
                    let td2 = strTrim(infoTd[1].innerText)

                    switch (td1) {
                      case "ブランド:":
                        getchuProcessedJSON.brand = ["브랜드", td2];
                        break;
                      case "定価:":
                        //getchuProcessedJSON.price = ["정가", td2];
                        break;
                      case "発売日:":
                        getchuProcessedJSON.o_date = ["발매일", td2];
                        break;
                      case "メディア:":
                        getchuProcessedJSON.media = ["미디어", td2];
                        break;
                      case "ジャンル:":
                        getchuProcessedJSON.genre = ["장르", dictionary(td2)];
                        break;
                      case "原画:":
                        getchuProcessedJSON.c_artist = ["원화", td2];
                        break;
                      case "シナリオ:":
                        getchuProcessedJSON.scenario = ["시나리오", td2];
                        break;
                      case "時間:":
                        getchuProcessedJSON.playtime = ["시간", td2];
                        break;
                      case "アーティスト:":
                        getchuProcessedJSON.artist = ["아티스트", td2];
                        break;
                      case "商品同梱特典:":
                        getchuProcessedJSON.special = ["상품 동봉 특전", td2];
                        break;
                      default:
                        break;
                    }
                  }
                })
                getchuProcessedJSON.url = ["출처", `<a href='${url}'>보러가기</a>`]
                resolve(makeTable(getchuProcessedJSON, getTable))
              })
          })
      })
  })
}

// getchu 정보 채우기
function getchuAuto(code = null) {
  let result = code || window.prompt("getchu 주소를 입력하세요");
  if (result) {
    if (!code) {
      getchuInfo(result)
    } else {
      return result
    }
  }
}

// fanza 파싱
function fanzaInfo(url, getTable = false) {
  return new Promise((resolve, reject) => {
    doFetch(url)
      .then(html => {
        const fanzaProcessedJSON = Object()
        if (url.includes("doujin")) {
          const infoBox = html.querySelector("div.l-areaMainColumn")
          doFetch(infoBox.querySelector("#fn-slides > li:nth-child(1) > a").href, {
              method: 'GET',
              responseType: 'blob'
            })
            .then(blob => {
              UploadImageFile(blob)
                .then(imgsrc => {
                  fanzaProcessedJSON.title = ["제목", strTrim(infoBox.querySelector("h1.productTitle__txt").innerText)]
                  fanzaProcessedJSON.image = ["이미지", `<img src='${imgsrc}' alt="이미지">`]
                  infoBox.querySelectorAll(".productAttribute-listItem > span").forEach(item => {
                    if (item.className.includes("productGenre")) {
                      let td1 = "분류"
                      let td2 = strTrim(item.innerText)
                      switch (td2) {
                        case "ボイス":
                          fanzaProcessedJSON.type = [td1, "보이스"];
                          break;
                        case "コミック":
                          fanzaProcessedJSON.type = [td1, "만화"];
                          break;
                        case "CG":
                          fanzaProcessedJSON.type = [td1, "CG"];
                          break;
                        case "ゲーム":
                          fanzaProcessedJSON.type = [td1, "게임"];
                          break;
                        case "コスプレ動画":
                          fanzaProcessedJSON.type = [td1, "코스프레 동영상"]
                          alert("실사 자료는 아카라이브 및 심야식당 규정에 어긋납니다.")
                          throw new Error("규정 위반 자료")
                      }
                    }
                  })
                  infoBox.querySelectorAll(".productInformation__item").forEach(child => {
                    let td1 = child.querySelector(".informationList__ttl").innerText
                    let td2
                    let tdd = child.querySelector(".informationList__txt")
                    if (tdd) {
                      td2 = tdd.innerText
                    }
                    switch (td1) {
                      case "シリーズ":
                        fanzaProcessedJSON.series = ["시리즈", td2]
                        break;
                      case "題材":
                        fanzaProcessedJSON.sanction = ["제재", dictionary(td2)]
                        break;
                      case "音声本数":
                        fanzaProcessedJSON.voice_count = ["음성 개수", td2]
                        break;
                      case "動画本数":
                        fanzaProcessedJSON.video_count = ["영상 개수", td2]
                        break;
                      case "ジャンル":
                        let tagList = []
                        infoBox.querySelectorAll(".genreTag__item").forEach(tag => {
                          tagList.push(dictionary(tag.innerText))
                        })
                        fanzaProcessedJSON.tags = ["태그", arr2str(tagList)]
                        break;
                      case "ファイル容量":
                        fanzaProcessedJSON.size = ["용량", td2]
                        break;
                    }
                  })
                  let sample = infoBox.querySelectorAll("div.sampleButton__item")
                  if (sample.length) {
                    const sampleUrlList = []
                    sample.forEach(tag => {
                      if (tag.querySelector(".sampleButton__btn") && tag.querySelector(".sampleButton__txt")) {
                        sampleUrlList.push(`<a href='${tag.querySelector(".sampleButton__btn").href}'>${tag.querySelector(".sampleButton__txt").innerText}</a>`)
                      }
                    })
                    fanzaProcessedJSON.sample = ["샘플", arr2str(sampleUrlList)]
                  }
                  fanzaProcessedJSON.url = ["출처", `<a href='${url}'>보러가기</a>`]
                  resolve(makeTable(fanzaProcessedJSON, getTable))
                })
            })
        }
        if (url.includes("dlsoft")) {
          const infoBox = html.querySelector("#mu > div.page-detail")
          doFetch(infoBox.querySelector("div.slider-area > ul > li:nth-child(1) > img").src, {
              method: 'GET',
              responseType: 'blob'
            })
            .then(blob => {
              UploadImageFile(blob)
                .then(imgsrc => {
                  fanzaProcessedJSON.title = ["제목", strTrim(infoBox.querySelector("#title").innerText)]
                  fanzaProcessedJSON.image = ["이미지", `<img src='${imgsrc}' alt="이미지">`]
                  if (infoBox.querySelector("tr.brand > td.content")) {
                    fanzaProcessedJSON.brand = ["브랜드", infoBox.querySelector("tr.brand > td.content").innerText]
                  }
                  infoBox.querySelector("div.container02").querySelectorAll("tr").forEach(tr => {
                    let rtd1 = tr.querySelector("td.type-left")
                    let rtd2 = tr.querySelector("td.type-right")
                    if (rtd1 && rtd2) {
                      let td1 = strTrim(rtd1.innerText)
                      let td2 = strTrim(rtd2.innerText)
                      switch (td1) {
                        case "シリーズ":
                          fanzaProcessedJSON.sanction = ["시리즈", td2]
                          break;
                        case "ゲームジャンル":
                          fanzaProcessedJSON.genre = ["게임 장르", dictionary(td2)]
                          break;
                        case "ボイス":
                          switch (td2) {
                            case "あり":
                            case "有り":
                              fanzaProcessedJSON.voice = ["음성", "있음"]
                              break;
                            case "なし":
                            case "無し":
                              fanzaProcessedJSON.voice = ["음성", "있음"]
                              break;
                            default:
                              fanzaProcessedJSON.voice = ["음성", td2]
                              break;
                          }
                          break;
                        case "原画":
                          fanzaProcessedJSON.c_artist = ["원화", td2]
                          break;
                        case "シナリオ":
                          fanzaProcessedJSON.scenario = ["시나리오", td2]
                          break;
                        case "ジャンル":
                          let tagList = []
                          tr.querySelectorAll("li").forEach(tag => {
                            tagList.push(dictionary(tag.innerText))
                          })
                          fanzaProcessedJSON.tags = ["태그", arr2str(tagList)]
                          break;
                      }
                    }
                  })
                  let sampleBx = infoBox.querySelector(".bx-detail-dlsample")
                  if (sampleBx) {
                    let sample = sampleBx.querySelectorAll("li")
                    const sampleUrlList = []
                    sample.forEach(tag => {
                      if (tag.querySelector("a") && tag.querySelector(".download-txt")) {
                        sampleUrlList.push(`<a href='${tag.querySelector("a").href}'>${tag.querySelector(".download-txt").innerText}</a>`)
                      }
                    })
                    fanzaProcessedJSON.sample = ["샘플", arr2str(sampleUrlList)]
                  }
                  fanzaProcessedJSON.url = ["출처", `<a href='${url}'>보러가기</a>`]
                  resolve(makeTable(fanzaProcessedJSON, getTable))
                })
            })
        }
        if (url.includes("book.")) {
          const infoBox = html.querySelector("div.m-boxDetailProduct")
          doFetch(infoBox.querySelector("span.m-boxDetailProduct__pack__item > a").href, {
              method: 'GET',
              responseType: 'blob'
            })
            .then(blob => {
              UploadImageFile(blob)
                .then(imgsrc => {
                  fanzaProcessedJSON.title = ["제목", strTrim(infoBox.querySelector("#title").innerText)]
                  fanzaProcessedJSON.image = ["이미지", `<img src='${imgsrc}' alt="이미지">`]
                  let index = 0
                  for (let list of infoBox.querySelectorAll(".m-boxDetailProductInfoMainList__description__list")) {
                    let tmpList = []
                    list.querySelectorAll("li").forEach(tag => {
                      tmpList.push(strTrim(tag.innerText))
                    })
                    if (index === 0) {
                      fanzaProcessedJSON.artist = ["작가", arr2str(tmpList)]
                    }
                    if (index === 1) {
                      fanzaProcessedJSON.series = ["시리즈", arr2str(tmpList)]
                    }
                    index += 1
                  }
                  fanzaProcessedJSON.summary = ["줄거리", infoBox.querySelector(".m-boxDetailProduct__info__story").innerText]
                  fanzaProcessedJSON.url = ["출처", `<a href='${url}'>보러가기</a>`]
                  resolve(makeTable(fanzaProcessedJSON, getTable))
                })
            })
        }
        if (url.includes("anime")) {
          const infoBox = html.querySelector("div.page-detail")
          doFetch(infoBox.querySelector("#sample-video > a").href, {
              method: 'GET',
              responseType: 'blob'
            })
            .then(blob => {
              UploadImageFile(blob)
                .then(imgsrc => {
                  fanzaProcessedJSON.title = ["제목", strTrim(infoBox.querySelector("#title").innerText)]
                  fanzaProcessedJSON.image = ["이미지", `<img src='${imgsrc}' alt="이미지">`]
                  infoBox.querySelector("table.mg-b20").querySelectorAll("tr").forEach(tag => {
                    let rtd1 = tag.querySelector("td:nth-child(1)")
                    let rtd2 = tag.querySelector("td:nth-child(2)")
                    if (rtd1 && rtd2) {
                      let td1 = strTrim(rtd1.innerText)
                      let td2 = strTrim(rtd2.innerText)
                      switch (td1) {
                        case "収録時間:":
                          fanzaProcessedJSON.playtime = ["러닝타임", td2]
                          break;
                        case "シリーズ:":
                          fanzaProcessedJSON.series = ["시리즈", td2]
                          break;
                        case "メーカー:":
                          fanzaProcessedJSON.maker = ["제조사", td2]
                          break;
                        case "レーベル:":
                          fanzaProcessedJSON.label = ["레이블", td2]
                          break;
                        case "ジャンル:":
                          fanzaProcessedJSON.genre = ["장르", dictionary(td2)]
                          break;
                        default:
                          break;
                      }
                    }
                  })
                  const sampleVideo = infoBox.querySelector("#detail-sample-movie > div > a.d-btn")
                  if (sampleVideo) {
                    sampleVideo.getAttribute("onclick").match(/\/digital.*\//)
                    fanzaProcessedJSON.sample = ["샘플", `<a href='https://www.dmm.co.jp${sampleVideo}'>샘플 영상 보기</a>`]
                  }
                  fanzaProcessedJSON.url = ["출처", `<a href='${url}'>보러가기</a>`]
                  resolve(makeTable(fanzaProcessedJSON, getTable))
                })
            })
        }
      })
  })
}

// fanza 정보 채우기
function fanzaAuto(code = null) {
  let result = code || window.prompt("fanza 주소를 입력하세요");
  if (result) {
    if (!code) {
      fanzaInfo(result)
    } else {
      return result
    }
  }
}

// pixiv 파싱 (미완성)
function pixivInfo(url) {
  doFetch(url)
    .then(html => {
      console.log(html)
    })
}

// blob을 이미지 객체로
const blobToImage = (blob) => {
  return new Promise(resolve => {
    const url = URL.createObjectURL(blob)
    let img = new Image()
    img.onload = () => {
      URL.revokeObjectURL(url)
      resolve(img)
    }
    img.src = url
  })
}

// 미방짤 제너레이터
function deviantTaste(optList, custom) {
  return new Promise((resolve, reject) => {
    doFetch("https://p-ac.namu.la/20210402/312ddf2b6cd9d18672d8818b8545c4c6dde0e92b80da025f9b52eecae181455f.png?type=orig", {
        method: "GET",
        responseType: "blob"
      })
      .then(blob => {
        blobToImage(blob)
          .then(image => {
            const canvas = document.createElement("canvas")
            canvas.setAttribute("id", "sm-translation-image-process-canvas")
            canvas.setAttribute("width", "1200")
            canvas.setAttribute("height", "800")

            const ctx = canvas.getContext("2d")
            ctx.drawImage(image, 0, 0)
            ctx.lineWidth = 5;
            ctx.strokeStyle = 'red';
            optList.forEach(taste => {
              deviantTasteCanvasRect(ctx, taste)
            })
            if (custom) {
              ctx.font = "28pt 나눔고딕 extraBold";
              ctx.fillStyle = "white";
              ctx.fillText(custom, 130, 708);

              ctx.lineWidth = 5;
              ctx.strokeStyle = 'red';
              ctx.strokeRect(125, 670, 10 + ctx.measureText(custom).width, 53);

              ctx.lineWidth = 2.5;
              ctx.rotate(45 * Math.PI / 180);
              ctx.strokeStyle = 'white';
              ctx.strokeRect(555, 412, 16.5, 16.5);
            }
            canvas.toBlob(function(blob) {
              UploadImageFile(blob)
                .then(url => {
                  resolve(`<img src="${url}" alt="미방짤 : ${arr2str(optList)}">`)
                })
            }, "image/png")
          })
      })
  })
}

// 미방짤 모달
function deviantTasteMenu() {
  let smtModalStyle = document.createElement("style")
  smtModalStyle.append(`
.smtmodal {
  display: none; 
  position: fixed; 
  z-index: 100; 
  padding-top: 100px; 
  left: 0;
  top: 0;
  width: 100%; 
  height: 100%; 
  overflow: auto; 
  background-color: rgb(0,0,0); 
  background-color: rgba(0,0,0,0.4); 
}

.smtmodal-content {
  position: relative;
  background-color: #fefefe;
  margin: auto;
  padding: 0;
  border: 1px solid #888;
  width: 40%;
  box-shadow: 0 4px 8px 0 rgba(0,0,0,0.2),0 6px 20px 0 rgba(0,0,0,0.19);
  -webkit-animation-name: animatetop;
  -webkit-animation-duration: 0.4s;
  animation-name: animatetop;
  animation-duration: 0.4s
}

@-webkit-keyframes animatetop {
  from {top:-300px; opacity:0} 
  to {top:0; opacity:1}
}

@keyframes animatetop {
  from {top:-300px; opacity:0}
  to {top:0; opacity:1}
}

.smtclose {
  color: white;
  float: right;
  font-size: 28px;
  font-weight: bold;
}

.smtclose:hover,
.smtclose:focus {
  color: white;
  text-decoration: none;
  cursor: pointer;
}

.smtmodal-header {
  padding: 0 20px 45px 20px;
  background-color: lightgray;
  color: white;
}

.smtmodal-body {
  padding: 2px 30px;
}

.smtmodal-footer {
  padding: 0 0 45px 0;
  background-color: lightgray;
  color: white;
}

.sexy-color-fresh {
  background: #0fb8ad;  
  background: -webkit-linear-gradient(135deg, #0fb8ad 0%, #1fc8db 51%, #2cb5e8 75%); 
  background: -o-linear-gradient(135deg, #0fb8ad 0%, #1fc8db 51%, #2cb5e8 75%); 
  background: -moz-linear-gradient(135deg, #0fb8ad 0%, #1fc8db 51%, #2cb5e8 75%); 
  background: linear-gradientlinear-gradient(135deg, #0fb8ad 0%, #1fc8db 51%, #2cb5e8 75%); 
  background: linear-gradient(135deg, #0fb8ad 0%, #1fc8db 51%, #2cb5e8 75%);
}

`)
  let smtModal = document.createElement("div")
  smtModal.setAttribute("id", "sm-taste-modal")
  smtModal.setAttribute("class", "smtmodal")
  smtModal.innerHTML = `
    <div class="smtmodal-content">
      <div class="smtmodal-header sexy-color-fresh">
        <span id ="sm-taste-modal-close" class="smtclose">&times;</span>
      </div>
      <div id="sm-taste-modal-body" class="smtmodal-body clearfix">
        <h2 style="margin:30px 0">미방짤 메이커</h2>
      </div>
      <div class="smtmodal-footer sexy-color-fresh">
      </div>
    </div>`

  document.head.appendChild(smtModalStyle)
  document.body.appendChild(smtModal)

  let modal = document.getElementById("sm-taste-modal");
  let span = document.getElementById("sm-taste-modal-close")
  let body = document.getElementById("sm-taste-modal-body");

  let checkList = [
    ["게이", "보추", "후타"],
    ["청아", "중노년여성"],
    ["스캇", "방뇨"],
    ["수간", "충간", "이종간"],
    ["고어", "료나", "보어(식인)"],
    ["보태", "임신", "출산"]
  ]

  const customTaste = document.createElement("input")
  customTaste.setAttribute("id", "sm-taste-modal-cutom-input")
  customTaste.setAttribute("class", "form-control form-control-sm float-right")

  const customTasteLabel = document.createElement("p")
  customTasteLabel.innerText = "또다른 취향 태그"

  const customTasteP = document.createElement("p")
  customTasteP.setAttribute("class", "clearfix")
  customTasteP.setAttribute("style", "margin-bottom:20px")
  customTasteP.appendChild(customTaste)

  const confirmBtn = document.createElement("div")
  confirmBtn.setAttribute("id", "sm-taste-modal-confirm-btn")
  confirmBtn.setAttribute("class", "btn btn-arca float-right")
  confirmBtn.setAttribute("style", "margin-bottom:20px")
  confirmBtn.innerText = "미방짤 만들기"
  confirmBtn.onclick = function(e) {
    const optionList = []
    document.querySelectorAll(".sm-taste-modal-taste-checkbox").forEach(chkb => {
      if (chkb.checked) {
        optionList.push(chkb.value)
      }
    })
    deviantTaste(optionList, customTaste.value)
      .then(img => {
        let editorBox = document.querySelector('.write-body .fr-element')
        editorBox.innerHTML = img + editorBox.innerHTML
        modal.style.display = "none"
      })
  }

  easyCheckbox(body, checkList, "sm-taste-modal-taste-checkbox")
  body.appendChild(customTasteLabel)
  body.appendChild(customTasteP)
  body.appendChild(confirmBtn)

  span.onclick = function() {
    modal.style.display = "none";
  }

  window.onclick = function(event) {
    if (event.target == modal) {
      modal.style.display = "none";
    }
  }
  modal.style.display = "block";
}

// 미방짤 표시 좌표
function deviantTasteCanvasRect(ctx, opt) {
  switch (opt) {
    case "게이":
      ctx.strokeRect(125, 355, 85, 50);
      break;
    case "보추":
      ctx.strokeRect(225, 355, 85, 50);
      break;
    case "후타":
      ctx.strokeRect(325, 355, 85, 50);
      break;
    case "청아":
      ctx.strokeRect(125, 407, 85, 50);
      break;
    case "중노년여성":
      ctx.strokeRect(225, 407, 332, 50);
      break;
    case "스캇":
      ctx.strokeRect(125, 460, 85, 50);
      break;
    case "방뇨":
      ctx.strokeRect(225, 460, 85, 50);
      break;
    case "수간":
      ctx.strokeRect(125, 513, 85, 50);
      break;
    case "충간":
      ctx.strokeRect(225, 513, 85, 50);
      break;
    case "이종간":
      ctx.strokeRect(325, 513, 120, 50);
      break;
    case "고어":
      ctx.strokeRect(125, 566, 85, 50);
      break;
    case "료나":
      ctx.strokeRect(225, 566, 85, 50);
      break;
    case "보어(식인)":
      ctx.strokeRect(325, 566, 183, 50);
      break;
    case "보태":
      ctx.strokeRect(125, 619, 85, 50);
      break;
    case "임신":
      ctx.strokeRect(225, 619, 85, 50);
      break;
    case "출산":
      ctx.strokeRect(325, 619, 85, 50);
      break;
  }
}

// 미방짤 init
function deviantTasteMenuInit() {
  let modal = document.getElementById("sm-taste-modal");
  if (modal === null && (location.href.includes("/write") || location.href.includes("/edit"))) {
    deviantTasteMenu()
  } else {
    if (modal !== null) {
      modal.style.display = "block"
    }
  }
}

// 파일 -> 어레이버퍼
function fileToArrayBuffer(myFile) {
  return new Promise((resolve, reject) => {
    var reader = new FileReader();
    reader.readAsArrayBuffer(myFile);
    reader.onloadend = function(evt) {
      if (evt.target.readyState == FileReader.DONE) {
        var arrayBuffer = evt.target.result
        resolve(arrayBuffer)
      }
      reject(new Error("ArrayBuffer 변환 실패"));
    }
  })
}

// 어레이버퍼 덧셈 -> Uint8Array
function appendBufferToArray(buffer1, buffer2) {
  var tmp = new Uint8Array(buffer1.byteLength + buffer2.byteLength);
  tmp.set(new Uint8Array(buffer1), 0);
  tmp.set(new Uint8Array(buffer2), buffer1.byteLength);
  return tmp;
};

// 이미지 첨부파일 제너레이터
function imagezipBind(image, file) {
  return new Promise((resolve, reject) => {
    fileToArrayBuffer(image)
      .then(imageBuffer => {
        fileToArrayBuffer(file)
          .then(fileBuffer => {
            let blob = new Blob([appendBufferToArray(imageBuffer, fileBuffer)])
            UploadImageFile(blob, null, true)
              .then(url => {
                resolve(`<img src="${url}" alt="이미지+파일">`)
              })
          }, "image/jpeg")
      })
  })
}

// 이미지 jpg 컨버터
function convert() {
  let c = document.createElement("canvas")
  let ctx = c.getContext("2d");
  c.width = this.width;
  c.height = this.height;
  ctx.drawImage(this, 0, 0);
  c.toBlob(function(blob) {
    resolve(blob)
  }, "image/jpeg");
}

// 이미지 첨부파일 모달
function imagezipMaker() {
  let smiModalStyle = document.createElement("style")
  smiModalStyle.append(`
.smimodal {
  display: none; 
  position: fixed; 
  z-index: 100; 
  padding-top: 100px; 
  left: 0;
  top: 0;
  width: 100%; 
  height: 100%; 
  overflow: auto; 
  background-color: rgb(0,0,0); 
  background-color: rgba(0,0,0,0.4); 
}

.smimodal-content {
  position: relative;
  background-color: #fefefe;
  margin: auto;
  padding: 0;
  border: 1px solid #888;
  width: 40%;
  box-shadow: 0 4px 8px 0 rgba(0,0,0,0.2),0 6px 20px 0 rgba(0,0,0,0.19);
  -webkit-animation-name: animatetop;
  -webkit-animation-duration: 0.4s;
  animation-name: animatetop;
  animation-duration: 0.4s
}

@-webkit-keyframes animatetop {
  from {top:-300px; opacity:0} 
  to {top:0; opacity:1}
}

@keyframes animatetop {
  from {top:-300px; opacity:0}
  to {top:0; opacity:1}
}

.smiclose {
  color: white;
  float: right;
  font-size: 28px;
  font-weight: bold;
}

.smiclose:hover,
.smiclose:focus {
  color: white;
  text-decoration: none;
  cursor: pointer;
}

.smimodal-header {
  padding: 0 20px 45px 20px;
  background-color: lightgray;
  color: white;
}

.smimodal-body {
  padding: 2px 30px;
}

.smimodal-footer {
  padding: 0 0 45px 0;
  background-color: lightgray;
  color: white;
}

.sexy-color-gold {
  background: linear-gradient(to right, gold 0%, goldenrod 100%);
}

`)
  let smiModal = document.createElement("div")
  smiModal.setAttribute("id", "sm-imagezip-modal")
  smiModal.setAttribute("class", "smimodal")
  smiModal.innerHTML = `
    <div class="smimodal-content">
      <div class="smimodal-header sexy-color-gold">
        <span id ="sm-imagezip-modal-close" class="smiclose">&times;</span>
      </div>
      <div id="sm-imagezip-modal-body" class="smimodal-body clearfix">
        <h2 style="margin:30px 0">이미지+파일 바인더</h2>
      </div>
      <div class="smimodal-footer sexy-color-gold">
      </div>
    </div>`

  document.head.appendChild(smiModalStyle)
  document.body.appendChild(smiModal)

  let modal = document.getElementById("sm-imagezip-modal");
  let span = document.getElementById("sm-imagezip-modal-close")
  let body = document.getElementById("sm-imagezip-modal-body");

  const imageFileH5 = document.createElement("h5")
  imageFileH5.setAttribute("style", "margin-bottom:20px")
  imageFileH5.innerText = "이미지 선택"

  const imageFileInput = document.createElement("input")
  imageFileInput.setAttribute("id", "sm-imagezip-modal-file-input")
  imageFileInput.setAttribute("type", "file")
  imageFileInput.setAttribute("style", "margin-bottom:20px")

  const imageUrl = document.createElement("input")
  imageUrl.setAttribute("id", "sm-imagezip-modal-url-input")
  imageUrl.setAttribute("class", "form-control form-control-sm float-right")

  const imageUrlLabel = document.createElement("p")
  imageUrlLabel.innerText = "또는 링크로 이미지 가져오기"

  const imageUrlP = document.createElement("p")
  imageUrlP.setAttribute("class", "clearfix")
  imageUrlP.setAttribute("style", "margin-bottom:20px")
  imageUrlP.appendChild(imageUrl)

  const fileH5 = document.createElement("h5")
  fileH5.innerText = "파일 선택"
  fileH5.setAttribute("style", "margin-bottom:20px")

  const fileInput = document.createElement("input")
  fileInput.setAttribute("id", "sm-imagezip-modal-file-input")
  fileInput.setAttribute("type", "file")
  fileInput.setAttribute("style", "margin-bottom:20px")

  const confirmBtn = document.createElement("div")
  confirmBtn.setAttribute("id", "sm-imagezip-modal-confirm-btn")
  confirmBtn.setAttribute("class", "btn btn-arca float-right")
  confirmBtn.setAttribute("style", "margin:60px 0 20px 0")
  confirmBtn.innerText = "이미지+파일 합치기"
  confirmBtn.onclick = function(e) {
    new Promise((resolve, reject) => {
        if (imageUrl.value) {
          doFetch(imageUrl.value, {
              method: "GET",
              responseType: "blob"
            })
            .then(blob => {
              console.log(blob)
              resolve(imagezipBind(blob, fileInput.files[0]))
            })
        } else {
          resolve(imagezipBind(imageFileInput.files[0], fileInput.files[0]))
        }
      })
      .then(img => {
        let editorBox = document.querySelector('.write-body .fr-element')
        editorBox.innerHTML = img + editorBox.innerHTML
        modal.style.display = "none"
      })
  }

  body.appendChild(imageFileH5)
  body.appendChild(imageFileInput)
  body.appendChild(imageUrlLabel)
  body.appendChild(imageUrlP)
  body.appendChild(fileH5)
  body.appendChild(fileInput)
  body.appendChild(confirmBtn)

  span.onclick = function() {
    modal.style.display = "none";
  }

  window.onclick = function(event) {
    if (event.target == modal) {
      modal.style.display = "none";
    }
  }
  modal.style.display = "block";
}

// 이미지 첨부파일 init
function imagezipMakerInit() {
  let modal = document.getElementById("sm-imagezip-modal");
  if (modal === null && (location.href.includes("/write") || location.href.includes("/edit"))) {
    imagezipMaker()
  } else {
    if (modal !== null) {
      modal.style.display = "block"
    }
  }
}

// 글 댓글 수 찾기 함수
function userInfoParse(url) {
  return new Promise((resolve, reject) => {
    doFetch(url, {
        method: 'GET',
        responseType: 'document'
      }, true)
      .then(html => {
        const infoBox = html.querySelector(".card-block")
        let filtered = Array.from(infoBox.children).filter(function(value, index, arr) {
          if (value.localName === "div") {
            return value
          }
        })
        const point = filtered.findIndex(function(e) {
          if (e.className === "clearfix") {
            return true
          }
        })
        const articleNum = point
        const replyNum = filtered.length - point - 1
        resolve(String(articleNum) + " / " + String(replyNum))
      })
      .catch(e => {
        resolve("삭제됨")
      })
  })
}

// 글 댓글 수 표시 함수
function userInfoView() {
  const smtTooltipStyle = document.createElement("style")
  smtTooltipStyle.append(`
.smttooltip {
  position: relative;
  display: inline-block;
}

.smttooltip .smttooltiptext {
  visibility: hidden;
  width: 60px;
  background-color: #555;
  color: #fff;
  text-align: center;
  border-radius: 6px;
  padding: 5px 0;
  position: absolute;
  z-index: 50;
  bottom: 125%;
  left: 50%;
  margin-left: -30px;
  opacity: 0;
  transition: opacity 0.3s;
}

.smttooltip .smttooltiptext::after {
  content: "";
  position: absolute;
  top: 100%;
  left: 50%;
  margin-left: -5px;
  border-width: 5px;
  border-style: solid;
  border-color: #555 transparent transparent transparent;
}

.smttooltip:hover .smttooltiptext {
  visibility: visible;
  opacity: 1;
}`)
  document.head.appendChild(smtTooltipStyle)
  document.querySelectorAll("div.list-area").forEach(tag => {
    tag.setAttribute("style", "overflow:visible")
  })
  document.querySelectorAll("div.info-row").forEach(tag => {
    tag.setAttribute("style", "overflow:visible")
  })
  document.querySelector(".article-wrapper").querySelectorAll("span.user-info").forEach(tag => {
    const smtTooltip = document.createElement("span")
    smtTooltip.setAttribute("class", "smttooltiptext")
    smtTooltip.innerText = " "

    tag.querySelector("a").classList.add("smttooltip")
    tag.querySelector("a").appendChild(smtTooltip)
    tag.querySelector("a").onmouseover = function(e) {
      if (e.target.href) {
        userInfoParse(e.target.href)
          .then(str => {
            smtTooltip.innerText = str
          })
      }
    }
  })
}

// 글 댓글 수 표시 init
function userInfoViewInit() {
  if (location.href.match(/\/b\/.*\/\d+/) && !location.href.includes("/edit") && window.innerWidth > 580) {
    userInfoView()
  }
}

// 클래스 변경 감지
function addClassNameListener(element, callback) {
  var lastClassName = element.className;
  window.setInterval(function() {
    var className = element.className;
    if (className !== lastClassName) {
      callback();
      lastClassName = className;
    }
  }, 10);
}

// 아카콘 링크하기
function arcaconLink() {
  const arcaconSrc = new Set()
  let arcaconId
  document.querySelector(".emoticons").querySelectorAll("img").forEach(tag => {
    arcaconSrc.add(tag.src)
    if (tag.getAttribute("data-emoticonid")) {
      arcaconId = tag.getAttribute("data-emoticonid")
    }
  })
  const editorBox = document.querySelector("#article_write_form > div.write-body > div > div.fr-wrapper > div")
  editorBox.querySelectorAll("img").forEach(tag => {
    if (arcaconSrc.has(tag.src) && arcaconId !== "0" && !tag.closest("a")) {
      const cloneImg = tag.cloneNode(true)
      const aTag = document.createElement("a")
      aTag.appendChild(cloneImg)
      aTag.href = `https://arca.live/e/${arcaconId}`
      tag.replaceWith(aTag)
    }
  })
}

// 아카콘 링크된 주소 글에서 바꾸기
async function arcaconLinkInArticle() {
  await sleep(1000)
  document.querySelector(".article-content").childNodes.forEach(tag => {
    const ta = tag.querySelectorAll("a").forEach(pta => {
      if (pta) {
        const taa = pta.querySelector("a")
        if (taa) {
          taa.href = pta.href
        }
      }
    })
  })
}

// 아카콘 링크하기 init
async function arcaconLinkInit() {
  if (location.href.includes("/write") || location.href.includes("/edit")) {
    let arcaconButton = document.querySelector(".btn-namlacon")
    while (arcaconButton === null) {
      await sleep(10)
      arcaconButton = document.querySelector(".btn-namlacon")
    }
    arcaconButton.onclick = function(e) {
      let arcaconPopup = document.querySelector(".write-body").querySelector(".fr-popup")
      addClassNameListener(arcaconPopup, arcaconLink)
    }
  } else if (location.href.match(/b\/.*?\/\d+/)) {
    arcaconLinkInArticle()
  }
}

// 메뉴 커맨드
function registerMenuCommand() {
  GM_registerMenuCommand("< 미방짤 만들기 >", deviantTasteMenuInit, "T");
  GM_registerMenuCommand("< 이미지+파일 합치기 >", imagezipMakerInit, "Z");
  GM_registerMenuCommand("DLsite 정보 채우기", dlsiteAuto, "D");
  GM_registerMenuCommand("hitomi 정보 채우기", hitomiAuto, "H");
  GM_registerMenuCommand("hitomi 전체 가져오기", hitomiFetch, "I");
  GM_registerMenuCommand("e-hentai 정보 채우기", ehAuto, "E");
  GM_registerMenuCommand("getchu 정보 채우기", getchuAuto, "G");
  GM_registerMenuCommand("fanza 정보 채우기", fanzaAuto, "F");
};

// 기다리기 함수
function sleep(ms) {
  return new Promise(resolve => setTimeout(resolve, ms));
}

// 사전 from https://arca.live/b/smpeople/30169768, 일부 수정 및 추가함
function dictionary(str) {
  let trList = []
  str.split(/[\s\t\r\n\v\f\/・]/).forEach(tok => {
    tok = strTrim(tok)
    switch (tok) {
      case "萌え":
        trList.push("모에")
        break;
      case "燃え":
        trList.push("열혈")
        break;
      case "感動":
        trList.push("감동")
        break;
      case "癒し":
        trList.push("힐링")
        break;
      case "鬱":
        trList.push("우울")
        break;
      case "淡白":
      case "あっさり":
        trList.push("담백")
        break;
      case "オールハッピー":
        trList.push("올 해피")
        break;
      case "着衣":
        trList.push("착의")
        break;
      case "チラリズム":
        trList.push("치라리즘")
        break;
      case "フェチ":
        trList.push("페티즘")
        break;
      case "女性視点":
        trList.push("여성시점")
        break;
      case "女主人公":
        trList.push("여주인공")
        break;
      case "男主人公":
        trList.push("남주인공")
        break;
      case "逆転無し":
        trList.push("역전 없음")
        break;
      case "マニアック":
      case "変態":
        trList.push("마니악/변태")
        break;
      case "おさわり":
        trList.push("만지기")
        break;
      case "きせかえ":
        trList.push("옷갈아입히기")
        break;
      case "脚":
        trList.push("다리")
        break;
      case "お尻":
      case "ヒップ":
        trList.push("엉덩이")
        break;
      case "おっぱい":
        trList.push("가슴")
        break;
      case "淫語":
        trList.push("음어")
        break;
      case "汁":
      case "液大量":
        trList.push("체액 대량")
        break;
      case "連続絶頂":
        trList.push("연속 절정")
        break;
      case "断面図":
        trList.push("단면도")
        break;
      case "ドット":
      case "ドット制作":
        trList.push("도트")
        break;
      case "アニメ":
        trList.push("애니메이션")
        break;
      case "総集編":
        trList.push("총집편")
        break;
      case "バイノーラル":
      case "ダミヘ":
        trList.push("바이노럴/더미헤드폰")
        break;
      case "催眠音声":
        trList.push("최면음성")
        break;
      case "アンソロジー":
        trList.push("앤솔로지")
        break;
      case "技術書":
        trList.push("기술서")
        break;
      case "ツクール":
        trList.push("알만툴")
        break;
      case "3D作品":
        trList.push("3D 작품")
        break;
      case "東方Project":
        trList.push("동방Project")
        break;
      case "ピアス":
        trList.push("피어스")
        break;
      case "装飾品":
        trList.push("장식품")
        break;
      case "首輪":
        trList.push("목줄")
        break;
      case "鎖":
        trList.push("사슬")
        break;
      case "拘束具":
        trList.push("구속구")
        break;
      case "ムチ":
        trList.push("채찍")
        break;
      case "縄":
        trList.push("밧줄")
        break;
      case "蝋燭":
        trList.push("촛농")
        break;
      case "薬物":
        trList.push("약물")
        break;
      case "ローション":
        trList.push("로션")
        break;
      case "おむつ":
        trList.push("기저귀")
        break;
      case "おもちゃ":
        trList.push("장난감")
        break;
      case "道具":
        trList.push("도구")
        break;
      case "異物":
        trList.push("이물질")
        break;
      case "メガネ":
      case "めがね":
        trList.push("안경")
        break;
      case "靴下":
        trList.push("양말")
        break;
      case "少女":
        trList.push("소녀")
        break;
      case "ロリ":
        trList.push("로리")
        break;
      case "ぷに":
        trList.push("뿌니")
        break;
      case "少年":
        trList.push("소년")
        break;
      case "ショタ":
        trList.push("쇼타")
        break;
      case "年上":
        trList.push("연상")
        break;
      case "妹":
        trList.push("여동생")
        break;
      case "母親":
        trList.push("모친")
        break;
      case "義妹":
        trList.push("의여동생")
        break;
      case "娘":
        trList.push("딸")
        break;
      case "義母":
        trList.push("의어머니")
        break;
      case "実姉":
        trList.push("친누나/친언니")
        break;
      case "義姉":
        trList.push("의누나/의언니")
        break;
      case "おやじ":
        trList.push("아버지")
        break;
      case "熟女":
        trList.push("숙녀")
        break;
      case "人妻":
        trList.push("유부녀")
        break;
      case "お姉さん":
        trList.push("누나/언니")
        break;
      case "未亡人":
        trList.push("미망인")
        break;
      case "既婚者":
        trList.push("기혼자")
        break;
      case "幼なじみ":
        trList.push("소꿉친구")
        break;
      case "双子":
        trList.push("쌍둥이")
        break;
      case "姉妹":
        trList.push("자매")
        break;
      case "保健医":
        trList.push("보건의")
        break;
      case "女医":
        trList.push("여의사")
        break;
      case "女教師":
        trList.push("여교사")
        break;
      case "教師":
        trList.push("교사")
        break;
      case "学生":
        trList.push("학생")
        break;
      case "同級生":
        trList.push("동급생")
        break;
      case "同僚":
        trList.push("동료")
        break;
      case "先輩":
        trList.push("선배")
        break;
      case "後輩":
        trList.push("후배")
        break;
      case "お嬢様":
        trList.push("아가씨")
        break;
      case "ギャル":
        trList.push("갸루")
        break;
      case "ビッチ":
        trList.push("빗치")
        break;
      case "天然":
        trList.push("천연")
        break;
      case "主従":
        trList.push("주종")
        break;
      case "主婦":
        trList.push("주부")
        break;
      case "女王様":
        trList.push("여왕님")
        break;
      case "お姫様":
        trList.push("공주님")
        break;
      case "エルフ":
        trList.push("엘프")
        break;
      case "妖精":
        trList.push("요정")
        break;
      case "天使":
        trList.push("천사")
        break;
      case "悪魔":
        trList.push("악마")
        break;
      case "変身ヒロイン":
        trList.push("변신 히로인")
        break;
      case "魔法少女":
        trList.push("마법 소녀")
        break;
      case "魔法使い":
        trList.push("마법사")
        break;
      case "魔女":
        trList.push("마녀")
        break;
      case "男の娘":
        trList.push("오토코노코")
        break;
      case "妖怪":
        trList.push("요괴")
        break;
      case "擬人化":
        trList.push("의인화")
        break;
      case "ヤンデレ":
        trList.push("얀데레")
        break;
      case "人外娘":
        trList.push("인외")
        break;
      case "モンスター娘":
        trList.push("몬스터 소녀")
        break;
      case "ロボット":
        trList.push("로봇")
        break;
      case "アンドロイド":
        trList.push("안드로이드")
        break;
      case "芸能人":
        trList.push("예능인")
        break;
      case "アイドル":
        trList.push("아이돌")
        break;
      case "モデル":
        trList.push("모델")
        break;
      case "警察":
        trList.push("경찰")
        break;
      case "刑事":
        trList.push("형사")
        break;
      case "ヤクザ":
      case "裏社会":
        trList.push("야쿠자")
        break;
      case "不良":
        trList.push("불량")
        break;
      case "ヤンキー":
        trList.push("일진")
        break;
      case "レスラー":
        trList.push("레슬러")
        break;
      case "格闘家":
        trList.push("격투가")
        break;
      case "幽霊":
        trList.push("유령")
        break;
      case "ゾンビ":
        trList.push("좀비")
        break;
      case "けもの":
        trList.push("짐승")
        break;
      case "獣化":
        trList.push("동물화")
        break;
      case "外国人":
        trList.push("외국인")
        break;
      case "体育会系":
        trList.push("체육")
        break;
      case "スポーツ選手":
        trList.push("스포츠 선수")
        break;
      case "ニューハーフ":
        trList.push("뉴하프")
        break;
      case "戦士":
        trList.push("전사")
        break;
      case "くノ一":
        trList.push("쿠노이치")
        break;
      case "サキュバス":
      case "淫魔":
        trList.push("서큐버스/음마")
        break;
      case "制服":
      case "제복":
        trList.push("교복")
        break;
      case "セーラー服":
        trList.push("세일러복")
        break;
      case "体操着":
        trList.push("체조복")
        break;
      case "水着":
        trList.push("수영복")
        break;
      case "メイド":
        trList.push("메이드")
        break;
      case "看護婦":
      case "ナース":
        trList.push("간호사")
        break;
      case "巫女":
        trList.push("무녀")
        break;
      case "軍服":
        trList.push("군복")
        break;
      case "下着":
        trList.push("속옷")
        break;
      case "パンツ":
        trList.push("팬티")
        break;
      case "ゴスロリ":
        trList.push("고스로리")
        break;
      case "コスプレ":
        trList.push("코스프레")
        break;
      case "ボンデージ":
      case "ボンテージ":
        trList.push("본디지")
        break;
      case "ブルマ":
        trList.push("브루마")
        break;
      case "ミニスカ":
        trList.push("미니 스커트")
        break;
      case "着物":
      case "和服":
        trList.push("기모노")
        break;
      case "エプロン":
        trList.push("앞치마")
        break;
      case "ラバー":
        trList.push("고무재질")
        break;
      case "レオタード":
        trList.push("레오타드")
        break;
      case "シスター":
        trList.push("수녀")
        break;
      case "ウェイトレス":
        trList.push("웨이트리스")
        break;
      case "バニーガール":
      case "버니걸":
        trList.push("바니걸")
        break;
      case "スパッツ":
        trList.push("스판츠")
        break;
      case "ニーソックス":
        trList.push("니삭스")
        break;
      case "ストッキング":
        trList.push("스타킹")
        break;
      case "スクール水着":
        trList.push("학교 수영복")
        break;
      case "スーツ":
        trList.push("정장")
        break;
      case "ガーター":
        trList.push("가터")
        break;
      case "女装":
        trList.push("여장")
        break;
      case "学校":
      case "学園":
      case "学園もの":
        trList.push("학원")
        break;
      case "オフィス":
      case "職場":
        trList.push("오피스/직장")
        break;
      case "ラブコメ":
        trList.push("러브 코메디")
        break;
      case "耳かき":
        trList.push("귀청소")
        break;
      case "屋外":
        trList.push("옥외")
        break;
      case "ギャグ":
        trList.push("개그")
        break;
      case "ラブラブ":
      case "あまあま":
        trList.push("러브러브/달콤달콤")
        break;
      case "退廃":
      case "背徳":
      case "インモラル":
        trList.push("퇴폐/배덕/인모럴")
        break;
      case "憑依":
        trList.push("빙의")
        break;
      case "石化":
        trList.push("석화")
        break;
      case "コメディ":
        trList.push("코메디")
        break;
      case "日常":
      case "生活":
        trList.push("일상/생활")
        break;
      case "時間停止":
        trList.push("시간 정지")
        break;
      case "ミリタリー":
        trList.push("밀리터리")
        break;
      case "スポーツ":
        trList.push("스포츠")
        break;
      case "格闘":
        trList.push("격투")
        break;
      case "ほのぼの":
        trList.push("푸근함")
        break;
      case "同棲":
        trList.push("동거(애인끼리)")
        break;
      case "恋人同士":
        trList.push("연인끼리")
        break;
      case "初体験":
      case "첫체험":
        trList.push("첫 체험")
        break;
      case "色仕掛け":
        trList.push("미인계")
        break;
      case "女体化":
        trList.push("여체화")
        break;
      case "性転換(TS)":
        trList.push("성전환(TS)")
        break;
      case "浮気":
        trList.push("바람")
        break;
      case "売春":
      case "援交":
        trList.push("매춘/원교")
        break;
      case "風俗":
      case "ソープ":
        trList.push("풍속/소프")
        break;
      case "シリアス":
        trList.push("시리어스")
        break;
      case "ファンタジー":
        trList.push("판타지")
        break;
      case "歴史":
      case "時代物":
        trList.push("역사/시대물")
        break;
      case "ホラー":
        trList.push("호러")
        break;
      case "キャットファイト":
        trList.push("캣 파이트")
        break;
      case "サスペンス":
        trList.push("서스펜스")
        break;
      case "バイオレンス":
        trList.push("바이올런스")
        break;
      case "ノンフィクション":
      case "体験談":
        trList.push("논픽션/체험담")
        break;
      case "オカルト":
        trList.push("오컬트")
        break;
      case "歳の差":
        trList.push("나이차")
        break;
      case "魔法":
        trList.push("마법")
        break;
      case "同居":
        trList.push("동거")
        break;
      case "純愛":
        trList.push("순애")
        break;
      case "戦場":
        trList.push("전장")
        break;
      case "おもらし":
        trList.push("실금")
        break;
      case "ハーレム":
        trList.push("할렘")
        break;
      case "寝取られ":
        trList.push("NTR")
        break;
      case "女子校生":
        trList.push("여고생")
        break;
      case "百合":
        trList.push("백합")
        break;
      case "ミステリー":
        trList.push("미스터리")
        break;
      case "丸呑み":
        trList.push("통째 삼키기")
        break;
      case "のぞき":
        trList.push("엿보기")
        break;
      case "電車":
        trList.push("전차")
        break;
      case "寝取り":
      case "네토리":
        trList.push("NTL")
        break;
      case "おねショタ":
        trList.push("오네쇼타")
        break;
      case "睡眠姦":
        trList.push("수면간")
        break;
      case "ツンデレ":
        trList.push("츤데레")
        break;
      case "アヘ顔":
        trList.push("아헤가오")
        break;
      case "ソフトエッチ":
        trList.push("소프트 엣찌")
        break;
      case "手コキ":
        trList.push("손으로")
        break;
      case "足コキ":
        trList.push("발로")
        break;
      case "ぶっかけ":
        trList.push("붓카케")
        break;
      case "顔射":
        trList.push("안면 사정")
        break;
      case "中出し":
        trList.push("질내 사정")
        break;
      case "妊娠":
      case "孕ませ":
        trList.push("임신")
        break;
      case "パイズリ":
        trList.push("파이즈리")
        break;
      case "レズ":
      case "女同士":
        trList.push("레즈/여자끼리")
        break;
      case "ゲイ":
      case "男同士":
        trList.push("게이/남자끼리")
        break;
      case "母乳":
        trList.push("모유")
        break;
      case "搾乳":
        trList.push("착유")
        break;
      case "出産":
        trList.push("출산")
        break;
      case "産卵":
        trList.push("산란")
        break;
      case "陵辱":
        trList.push("능욕")
        break;
      case "オナニー":
        trList.push("자위")
        break;
      case "オナサポ":
        trList.push("자위 서포트")
        break;
      case "緊縛":
        trList.push("묶기/긴박")
        break;
      case "フェラ":
      case "フェラチオ":
        trList.push("펠라치오")
        break;
      case "痴漢":
        trList.push("치한")
        break;
      case "調教":
        trList.push("조교")
        break;
      case "淫乱":
        trList.push("음란")
        break;
      case "露出":
        trList.push("노출")
        break;
      case "言葉責め":
        trList.push("언어고문/음어")
        break;
      case "青姦":
        trList.push("야외 플레이")
        break;
      case "拘束":
        trList.push("구속")
        break;
      case "奴隷":
        trList.push("노예")
        break;
      case "浣腸":
        trList.push("관장")
        break;
      case "羞恥":
      case "恥辱":
      case "辱め":
        trList.push("수치/치욕")
        break;
      case "監禁":
        trList.push("감금")
        break;
      case "焦らし":
        trList.push("애태우기")
        break;
      case "くすぐり":
        trList.push("간지럼")
        break;
      case "鬼畜":
        trList.push("귀축")
        break;
      case "ノーマルプレイ":
        trList.push("노멀 플레이")
        break;
      case "放置プレイ":
        trList.push("방치 플레이")
        break;
      case "複数プレイ":
        trList.push("복수 플레이")
        break;
      case "乱交":
        trList.push("난교")
        break;
      case "強制":
      case "無理矢理":
        trList.push("강제")
        break;
      case "レイプ":
        trList.push("레이프")
        break;
      case "輪姦":
        trList.push("윤간")
        break;
      case "和姦":
        trList.push("화간")
        break;
      case "近親相姦":
        trList.push("근친상간")
        break;
      case "逆レイプ":
        trList.push("역 레이프")
        break;
      case "盗撮":
        trList.push("도촬")
        break;
      case "男性受け":
        trList.push("수비남")
        break;
      case "催眠":
        trList.push("최면")
        break;
      case "放尿":
      case "おしっこ":
        trList.push("방뇨/오줌")
        break;
      case "アナル":
        trList.push("애널")
        break;
      case "スカトロ":
        trList.push("스캇물")
        break;
      case "尿道":
        trList.push("요도")
        break;
      case "触手":
        trList.push("촉수")
        break;
      case "獣姦":
        trList.push("수간")
        break;
      case "機械姦":
        trList.push("기계간")
        break;
      case "下克上":
        trList.push("하극상")
        break;
      case "モブ姦":
        trList.push("몹 캐릭 간")
        break;
      case "異種姦":
        trList.push("이종간")
        break;
      case "悪堕ち":
        trList.push("타락")
        break;
      case "洗脳":
        trList.push("세뇌")
        break;
      case "ごっくん":
      case "食ザー":
        trList.push("정액 삼킴")
        break;
      case "口内射精":
        trList.push("구내 사정")
        break;
      case "イラマチオ":
        trList.push("이라마치오")
        break;
      case "スパンキング":
        trList.push("스파킹")
        break;
      case "耳舐め":
        trList.push("귀햝기")
        break;
      case "潮吹き":
        trList.push("시오후키")
        break;
      case "ささやき":
        trList.push("속삭임")
        break;
      case "拡張":
        trList.push("확장")
        break;
      case "ボクっ娘":
        trList.push("보쿠코")
        break;
      case "ショートカット":
        trList.push("짧은 머리")
        break;
      case "ロングヘア":
        trList.push("긴 머리")
        break;
      case "金髪":
        trList.push("금발")
        break;
      case "黒髪":
        trList.push("흑발")
        break;
      case "ポニーテール":
        trList.push("포니테일")
        break;
      case "ツインテール":
        trList.push("트윈테일")
        break;
      case "ネコミミ":
        trList.push("고양이귀")
        break;
      case "獣耳":
        trList.push("짐승귀")
        break;
      case "長身":
        trList.push("장신")
        break;
      case "筋肉":
        trList.push("근육")
        break;
      case "巨乳":
      case "爆乳":
        trList.push("거유/폭유")
        break;
      case "貧乳":
      case "微乳":
      case "つるぺた":
        trList.push("빈유/미유")
        break;
      case "複乳":
      case "怪乳":
      case "超乳":
        trList.push("복유/괴유/초유")
        break;
      case "乳首":
      case "乳輪":
        trList.push("유두/유륜")
        break;
      case "ぼて腹":
      case "妊婦":
        trList.push("볼록 배/임산부")
        break;
      case "スレンダー":
        trList.push("슬렌더")
        break;
      case "ツルペタ":
        trList.push("평평한 가슴")
        break;
      case "パイパン":
        trList.push("음모 없음")
        break;
      case "陰毛":
        trList.push("음모")
        break;
      case "腋毛":
        trList.push("겨드랑이 털")
        break;
      case "フタナリ":
      case "ふたなり":
        trList.push("후타나리")
        break;
      case "巨根":
        trList.push("거근")
        break;
      case "童貞":
        trList.push("동정")
        break;
      case "処女":
        trList.push("처녀")
        break;
      case "巨大化":
        trList.push("거대화")
        break;
      case "方言":
        trList.push("사투리")
        break;
      case "無表情":
        trList.push("무표정")
        break;
      case "褐色":
      case "日焼け":
        trList.push("갈색 피부")
        break;
      case "包茎":
        trList.push("포경")
        break;
      case "ムチムチ":
        trList.push("쭉쭉빵빵")
        break;
      case "太め":
        trList.push("통통한")
        break;
      case "デブ":
        trList.push("뚱뚱한")
        break;
      case "蟲姦":
        trList.push("충간")
        break;
      case "腹パン":
        trList.push("배빵")
        break;
      case "猟奇":
        trList.push("엽기")
        break;
      case "人体改造":
        trList.push("인체 개조")
        break;
      case "拷問":
        trList.push("고문")
        break;
      case "フィストファック":
        trList.push("Fist fuck")
        break;
      case "ニプルファック":
        trList.push("Nipple fuck")
        break;
      case "血液":
      case "流血":
      case "スプラッター":
        trList.push("혈액/유혈")
        break;
      case "狂気":
        trList.push("광기")
        break;
      case "リョナ":
        trList.push("료나")
        break;
      case "料理":
      case "グルメ":
        trList.push("요리/미식가")
        break;
      case "評論":
        trList.push("평론")
        break;
      case "シリーズもの":
        trList.push("시리즈물")
        break;
      case "遠距離恋愛":
        trList.push("원거리 연애")
        break;
      case "家族":
        trList.push("가족")
        break;
      case "ギャンブル":
        trList.push("도박")
        break;
      case "劇画":
        trList.push("극화")
        break;
      case "耽美":
        trList.push("탐미")
        break;
      case "ティーンズラブ":
        trList.push("어린 사랑")
        break;
      case "伝奇":
        trList.push("전기")
        break;
      case "ハードボイルド":
        trList.push("비장한")
        break;
      case "パラレル":
        trList.push("평행세계")
        break;
      case "パンチラ":
        trList.push("팬티보임")
        break;
      case "ブラチラ":
        trList.push("브라보임")
        break;
      case "ボーイズラブ":
        trList.push("BL")
        break;
      case "恋愛":
        trList.push("연애")
        break;
      case "委員長":
        trList.push("위원장")
        break;
      case "叔父":
        trList.push("숙부")
        break;
      case "義父":
        trList.push("시아버지")
        break;
      case "ガテン系":
        trList.push("가텐계")
        break;
      case "サラリーマン":
        trList.push("직장인")
        break;
      case "爺":
        trList.push("할아버지")
        break;
      case "実妹":
        trList.push("친동생")
        break;
      case "秘書":
        trList.push("비서")
        break;
      case "ヤリチン":
      case "プレイボーイ":
        trList.push("야리칭/플레이 보이")
        break;
      case "インテリ":
        trList.push("지식인")
        break;
      case "おかっぱ":
        trList.push("단발")
        break;
      case "タトゥー":
      case "刺青":
        trList.push("타투/문신")
        break;
      case "ハード系":
        trList.push("하드계")
        break;
      case "痴女":
        trList.push("치녀")
        break;
      case "茶髪":
        trList.push("갈색 머리")
        break;
      case "ドジっ娘":
        trList.push("도짓코/덜렁이")
        break;
      case "ぽっちゃり":
        trList.push("풍만")
        break;
      case "三つ編み":
        trList.push("땋은 머리")
        break;
      case "ミニ系":
        trList.push("작은체형/어린")
        break;
      case "ガードル":
        trList.push("거들")
        break;
      case "カチューシャ":
        trList.push("머리띠")
        break;
      case "しっぽ":
        trList.push("꼬리")
        break;
      case "スタンガン":
        trList.push("전기 충격")
        break;
      case "スポユニ":
        trList.push("운동복/스포츠 유니폼")
        break;
      case "男装":
        trList.push("남장")
        break;
      case "チャイナ":
        trList.push("차이나/중국풍")
        break;
      case "道着":
        trList.push("도복")
        break;
      case "ドラッグ":
        trList.push("마약")
        break;
      case "バイブ":
        trList.push("바이브/진동")
        break;
      case "白衣":
        trList.push("백의")
        break;
      case "半ズボン":
        trList.push("반바지")
        break;
      case "ブレザー":
        trList.push("재킷/블레이저")
        break;
      case "ふんどし":
        trList.push("훈도시")
        break;
      case "包帯":
        trList.push("붕대")
        break;
      case "注射器":
        trList.push("주사기")
        break;
      case "リボン":
        trList.push("리본")
        break;
      case "ローター":
        trList.push("로터")
        break;
      case "ローレグ":
        trList.push("로우 레그 컷/속옷")
        break;
      case "ワイシャツ":
        trList.push("와이셔츠")
        break;
      case "乙女受け":
        trList.push("처녀역할")
        break;
      case "オヤジ受け":
        trList.push("아버지역할")
        break;
      case "俺様攻め":
        trList.push("오레사마역할")
        break;
      case "クール受け":
      case "クール攻め":
        trList.push("쿨 플레이")
        break;
      case "クンニ":
        trList.push("쿤닐링구스/애무")
        break;
      case "健気受け":
        trList.push("씩씩한")
        break;
      case "誘い受け":
        trList.push("권유받은")
        break;
      case "強気受け":
        trList.push("강하게 받는")
        break;
      case "ヘタレ攻め":
        trList.push("엉터리공격역할")
        break;
      case "やんちゃ受け":
        trList.push("응석받이")
        break;
      case "アクション":
        trList.push("액션")
        break;
      case "アドベンチャー":
        trList.push("어드벤처")
        break;
      case "クイズ":
        trList.push("퀴즈")
        break;
      case "シミュレーション":
        trList.push("시뮬레이션")
        break;
      case "シューティング":
        trList.push("슈팅")
        break;
      case "その他ゲーム":
        trList.push("기타 게임")
        break;
      case "タイピング":
        trList.push("타이핑")
        break;
      case "テーブルゲーム":
        trList.push("테이블 게임")
        break;
      case "デジタルノベル":
        trList.push("디지털 노벨")
        break;
      case "パズル":
        trList.push("퍼즐")
        break;
      case "ロールプレイング":
        trList.push("롤 플래잉")
        break;
      case "オリジナル":
        trList.push("오리지널")
        break;
      case "二次創作":
        trList.push("2차 창작")
        break;
      case "漫画":
        trList.push("만화")
        break;
      case "アニメ":
        trList.push("애니메이션")
        break;
      case "二次創作":
        trList.push("2차 창작")
        break;
      case "ゲーム系":
        trList.push("게임 계열")
        break;
      case "パロディ":
        trList.push("패러디")
        break;
      case "その他":
        trList.push("기타")
        break;
      case "成人向け":
        trList.push("성인용")
        break;
      case "アクセサリー":
        trList.push("악세서리")
        break;
      case "イラスト":
        trList.push("일러스트")
        break;
      case "CG集":
        trList.push("CG집")
        break;
      case "男無":
        trList.push("남자 없음")
        break;
      case "音声付き":
        trList.push("음성 첨부")
        break;
      case "女主人公のみ":
        trList.push("여주인공만")
        break;
      case "擬人化":
        trList.push("의인화")
        break;
      case "逆転無し":
        trList.push("역전없음")
        break;
      case "作家複数":
        trList.push("여러작가")
        break;
      case "残虐表現":
        trList.push("잔학 표현")
        break;
      case "新作":
        trList.push("신작")
        break;
      case "準新作":
        trList.push("준신작")
        break;
      case "旧作":
        trList.push("구작")
        break;
      case "女性視点":
        trList.push("여성시점")
        break;
      case "シリーズもの":
        trList.push("시리즈물")
        break;
      case "全年齢向け":
        trList.push("전연령용")
        break;
      case "断面図あり":
        trList.push("단면도 있음")
        break;
      case "デモ":
        trList.push("데모")
        break;
      case "体験版あり":
        trList.push("체험판 있음")
        break;
      case "動画":
        trList.push("동영상")
        break;
      case "アニメーション":
        trList.push("애니메이션")
        break;
      case "ノベル":
        trList.push("노벨")
        break;
      case "ベスト":
        trList.push("베스트")
        break;
      case "総集編":
        trList.push("총집편")
        break;
      case "男性向け":
        trList.push("남성용")
        break;
      case "女性向け":
        trList.push("여성용")
        break;
      case "DL版独占販売":
        trList.push("DL독점")
        break;
      case "FANZA専売":
        trList.push("FANZA독점")
        break;
      case "がんばろう同人!":
        trList.push("힘내자 동인!")
        break;
      case "ゲームCP":
        trList.push("게임CP")
        break;
      default:
        trList.push(tok)
        break;
    }
  })
  return arr2str(Array.from(new Set(trList)))
}