Skip to content

Instantly share code, notes, and snippets.

@Aopodo
Forked from jamiephan/README.md
Created September 19, 2024 17:10
Show Gist options
  • Select an option

  • Save Aopodo/b7e1710db9893b155af5b3623972edfc to your computer and use it in GitHub Desktop.

Select an option

Save Aopodo/b7e1710db9893b155af5b3623972edfc to your computer and use it in GitHub Desktop.
A script to automatically add ALL items to your account in quixel

Script to add all items from quixel

As quixel is being removed, all items are free to aquire. This script is to automate the process to add items to your account (As of writing, a total of 18874 items)

Note: This script only tested in the latest version of Chrome.

How to use

  1. Copy the script from below (run.js)
  2. Login into https://quixel.com
  3. Go to https://quixel.com/megascans/collections
  4. Open devtools (F12) -> Go to "Console" tab
  5. Paste in the script and press Enter.
  6. A dialog should popup confirming the execution, click "OK"
  7. Sit back and wait

Common issues

  • Getting "Forbidden" error. (Even after refresh, the whole page just shows "Forbidden")
    • There is a chance that the API adding too fast and you hit the rate limit of the API. (My testing is around after 10 pages, so ~10k items).
    • Wait after ~10-20 minutes and continue. See Common Fixes -> Restart script to continue the execution after you can load https://quixel.com.
  • The script seems to be paused/hang
    • It could be too much logging going it. Try monitor the script, if it says "END PAGE X", note the page number down (in case need restart) and clear the console by clicking the "🚫" icon in devtools.
    • See Common Fixes -> Restart script for fixing.
  • Getting the error **UNABLE TO ADD ITEM**
    • There should have the error message shown in ( ). If it is user already owns specified asset at a higher or equal resolution, then its already in your account.
  • Getting the error cannot find authentication token. Please login again
    • Clear browser cookies and re-login quixel again. Try just simply add 1 item manully. If it success, then see Common Fixes -> Restart script for continue the execution.

Common Fixes

Restart Script

  1. Note which page it was running
  2. Copy the run.js script
  3. Update the startPage = 0 on the first line to startPage = 10 (assuming page 10 was hanged)

Change Log

  1. Initial Script launch
  2. Update to clear logs to reduce chance of hanging
  3. [CURRENT] Skip adding items that already was acquired. Reduced logs. Added more info after script completion to show purchased item count. Due to now skipping purchased items, you technically don't need to specify the startPage anymore.
((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.`)
})())
@Aopodo
Copy link
Author

Aopodo commented Sep 19, 2024

async function fbo(url, options, retries = 1, delay = 1000) {
    try {
        const response = await fetch(url, options);
        if (!response.ok) {
            throw new Error(`HTTP error! status: ${response.status}`);
        } else {
            return await response.json();
        }
    } catch (error) {
        await new Promise(resolve => setTimeout(resolve, delay * Math.pow(2, retries - 1)));
        if (retries <= 3) {
            return fbo(url, options, retries + 1, delay);
        } else {
            throw error;
        }
    }
};

let acquiredItems = undefined
let authToken = undefined
let totalPages = undefined

// 查看缓存数据
async function callCacheApi(params={}) {
    const defaultParams = {
        page: 0,
        maxValuesPerFacet: 1000,
        hitsPerPage: 1000,
        attributesToRetrieve: ["id", "name"].join(",")
      }
    return fbo("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",
    })
}

function getAuthToken(name="auth") {
    const value = `; ${document.cookie}`;
    const parts = value.split(`; ${name}=`);
    if (parts.length === 2) { 
        authToken = parts.pop().split(';').shift() ?? "{}";
        try {
            authToken = JSON.parse(decodeURIComponent(authToken))?.token
        } catch (_) {
            return console.error("-> ERROR:未找到token,请重新登录.")
        }
    }
}


// 添加物品
async function callAcl ({ id, name }) {
    const json = 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",
    })
    .then(_ => console.log(`  --> 添加物品成功: ${id} | ${name}`))
    .catch(err => console.error(`  --> **添加物品失败** ${id} | ${name} (${err})`));
}

// 查看已购物品列表
async function callAcquired() {
    return fbo("https://quixel.com/v1/assets/acquired", {
      "headers": {
        "authorization": "Bearer " + authToken,
        "content-type": "application/json;charset=UTF-8",
      },
      "method": "GET",
    });
}

async function check() {
    console.log("-> 检查API Token...")
    getAuthToken()
    if (!authToken) {
        return console.error("-> 未找到token,请重新登录.")
    }
    console.log("-> 获取已购物品列表...")
    acquiredItems = (await callAcquired()).map(a => a.assetID)
    console.log("-> 获取缓存数据.....")
    const { nbPages: pages, hitsPerPage: itemsPerPage, nbHits: totalItems } = await callCacheApi()
    console.log("-> ==============================================")
    console.log(`-> 物品总数: ${totalItems}, 共 ${pages} 页, 每页 ${itemsPerPage} 个物品`)
    totalPages = pages
    console.log(`-> 还需添加: ${(totalItems - acquiredItems.length)}个.`)
    console.log("-> ==============================================")
}

async function addAll(startPage=0, autoClearConsole=true) {
    if (!acquiredItems) {
        console.log("请先执行check函数!") 
        return;
    }
    for (let pageIdx = startPage || 0; pageIdx < totalPages; pageIdx++) {
        console.log(`-> ======================= 第${pageIdx + 1}页开始,共${totalPages}页 =======================`)
        const { hits: items } = await callCacheApi({ page: pageIdx })    
        const unownedItems = items.filter(i => !acquiredItems.includes(i.id))
        const aclPromises = unownedItems.map(callAcl)
        await Promise.all(aclPromises)
        if (autoClearConsole) console.clear()
    }
    console.log("-> 全部添加完成!")
}

(async () => {
    await check()
    await addAll()
})()

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment