메가스캔 에셋을 모두 추가하는 스크립트

메가스캔 에셋을 모두 추가하는 스크립트

사용 방법

  1. 아래 스크립트 run.js를 복사합니다.
  2. https://quixel.com 에 로그인합니다.
  3. https://quixel.com/megascans/collections 로 이동합니다.
  4. 개발자 도구(F12)를 열고 "Console" 탭으로 이동합니다.
  5. 복사한 스크립트를 붙여넣고 Enter 키를 누릅니다.
  6. 실행을 확인하는 대화 상자가 나타나면 "OK"를 클릭합니다.

run.js
((async (startPage = 0, autoClearConsole = true) => {
 
  const getCookie = (name) => {
    const value = `; ${document.cookie}`;
    const parts = value.split(`; ${name}=`);
    if (parts.length === 2) return parts.pop().split(';').shift();
  }
 
  const callCacheApi = async (params = {}) => {
    const defaultParams = {
      page: 0,
      maxValuesPerFacet: 1000,
      hitsPerPage: 1000,
      attributesToRetrieve: ["id", "name"].join(",")
    }
    const response = await fetch("https://proxy-algolia-prod.quixel.com/algolia/cache", {
      "headers": {
        "x-api-key": "2Zg8!d2WAHIUW?pCO28cVjfOt9seOWPx@2j"
      },
      "body": JSON.stringify({
        url: "https://6UJ1I5A072-2.algolianet.com/1/indexes/assets/query?x-algolia-application-id=6UJ1I5A072&x-algolia-api-key=e93907f4f65fb1d9f813957bdc344892",
        params: new URLSearchParams({ ...defaultParams, ...params }).toString()
      }),
      "method": "POST",
    })
    return await response.json()
  }
 
  const callAcl = async ({ id, name }) => {
    const response = await fetch("https://quixel.com/v1/acl", {
      "headers": {
        "authorization": "Bearer " + authToken,
        "content-type": "application/json;charset=UTF-8",
      },
      "body": JSON.stringify({ assetID: id }),
      "method": "POST",
    });
    const json = await response.json()
    if (json?.isError) {
      console.error(`  --> **UNABLE TO ADD ITEM** Item ${id} | ${name} (${json?.msg})`)
    } else {
      console.log(`  --> ADDED ITEM Item ${id} | ${name}`)
    }
  }
 
  const callAcquired = async () => {
    const response = await fetch("https://quixel.com/v1/assets/acquired", {
      "headers": {
        "authorization": "Bearer " + authToken,
        "content-type": "application/json;charset=UTF-8",
      },
      "method": "GET",
    });
    return await response.json()
  }
 
  // 1. Check token exist, quixel API needs it
  console.log("-> Checking Auth API Token...")
  let authToken = ""
  try {
    const authCookie = getCookie("auth") ?? "{}"
    authToken = JSON.parse(decodeURIComponent(authCookie))?.token
    if (!authToken) {
      return console.error("-> Error: cannot find authentication token. Please login again.")
    }
  } catch (_) {
    return console.error("-> Error: cannot find authentication token. Please login again.")
  }
 
  // 2. Get all currently acquired items
  console.log("-> Get Acquired Items...")
  const acquiredItems = (await callAcquired()).map(a => a.assetID)
 
  // 3. Get total count of items
  console.log("-> Getting Total Number of Pages....")
  const { nbPages: totalPages, hitsPerPage: itemsPerPage, nbHits: totalItems } = await callCacheApi()
 
 
  console.log("-> ==============================================")
  console.log(`-> Total # of items: ${totalItems}`)
  console.log(`-> ${totalPages} total pages with ${itemsPerPage} per page`)
  console.log(`-> Total Items to add: ${(totalItems - acquiredItems.length)}.`)
  console.log("-> ==============================================")
 
  if (!confirm(`Click OK to start adding ${(totalItems - acquiredItems.length)} items in your account.`)) return
 
  // Loop
  for (let pageIdx = startPage || 0; pageIdx < totalPages; pageIdx++) {
    console.log(`-> ======================= PAGE ${pageIdx + 1}/${totalPages} START =======================`)
 
    console.log("-> Getting Items from page " + (pageIdx + 1) + " ...")
 
    const { hits: items } = await callCacheApi({ page: pageIdx })
 
    console.log("-> Adding non-acquired items...")
 
    // Filter out owned items
    const unownedItems = items.filter(i => !acquiredItems.includes(i.id))
    const aclPromises = unownedItems.map(callAcl)
 
    await Promise.all(aclPromises)
    console.log(`-> ======================= PAGE ${pageIdx + 1}/${totalPages} COMPLETED =======================`)
    if (autoClearConsole) console.clear() // Fix the issue that too much log hangs the console. Set autoClearConsole = false to keep the logs
  }
 
  console.log("-> Getting new acquired info...")
  // Get acquired items again
  const newItemsAcquired = (await callAcquired()).length
  const newTotalCount = (await callCacheApi()).nbHits
 
  console.log(`-> Completed. Your account now have a total of ${newItemsAcquired} out of ${newTotalCount} items.`)
 
  alert(`-> Your account now have a total of ${newItemsAcquired} out of ${newTotalCount} items.\n\nIf you find some items missing, try refresh the page and run the script again.`)
})())

자주 발생하는 문제

  • "Forbidden" 오류가 발생하는 경우 (새로 고침 후에도 페이지가 계속 "Forbidden" 상태로 표시됨)
    • API가 너무 빠르게 추가되어 API의 속도 제한에 걸릴 가능성이 있습니다.
      (테스트 결과 약 10페이지 정도, 약 10,000개 항목 후 발생)
    • 약 10~20분 후에 다시 시도해보세요. https://quixel.com 페이지를 로드할 수 있게 되면,
      Common Fixes -> Restart script을 통해 실행을 계속할 수 있습니다.

  • 스크립트가 멈추는 경우
    • 로그가 너무 많이 출력되어 문제가 발생할 수 있습니다.
      스크립트를 모니터링하고 "END PAGE X"라는 메시지가 나타나면 페이지 번호를 기록한 후
      개발자 도구 콘솔에서 "🚫" 아이콘을 클릭해 콘솔을 지우십시오.
    • Common Fixes -> Restart script을 통해 실행을 계속할 수 있습니다.

  • **UNABLE TO ADD ITEM** 오류가 발생하는 경우
    • ( ) 안에 오류 메시지가 표시됩니다.
      오류 메시지가 user already owns specified asset at a higher or equal resolution인 경우,
      해당 항목은 이미 계정에 추가된 것입니다.

  • cannot find authentication token. Please login again 오류가 발생하는 경우
    • 브라우저 쿠키를 삭제한 후 quixel에 다시 로그인합니다.
      에셋 하나를 수동으로 추가해보세요.
      성공하면 Common Fixes -> Restart script을 통해 실행을 계속할 수 있습니다.

일반적인 해결 방법

스크립트 재시작

  1. 스크립트가 실행 중이던 페이지를 기록합니다.
  2. run.js 스크립트를 복사합니다.
  3. 첫 번째 줄에 있는 startPage = 0startPage = 10으로 업데이트합니다.
    (예를 들어, 페이지 10에서 멈췄을 경우)

변경 사항

  1. 초기 스크립트 출시
  2. 로그를 지워 멈출 확률을 줄이는 업데이트
  3. 이미 획득한 항목을 건너뛰도록 수정. 로그를 줄임.
    스크립트 실행 완료 후 구매된 항목 수를 표시하는 정보 추가.
    이제 구매된 항목을 건너뛰므로 startPage를 지정할 필요가 없어짐.

 참고 자료