Skip to content

Instantly share code, notes, and snippets.

@jamiephan
Last active October 27, 2025 11:42
Show Gist options
  • Save jamiephan/0c04986c7f2e62d5c87c4e8c8ce115fc to your computer and use it in GitHub Desktop.
Save jamiephan/0c04986c7f2e62d5c87c4e8c8ce115fc to your computer and use it in GitHub Desktop.

Revisions

  1. jamiephan revised this gist Sep 18, 2024. 2 changed files with 50 additions and 13 deletions.
    8 changes: 7 additions & 1 deletion README.md
    Original file line number Diff line number Diff line change
    @@ -30,6 +30,12 @@ As quixel is being removed, all items are free to aquire. This script is to auto
    ### Common Fixes

    #### Restart Script
    1. If it hang too long, note what page is had going (Search for log like `PAGE 10 START`)
    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.
    55 changes: 43 additions & 12 deletions run.js
    Original file line number Diff line number Diff line change
    @@ -1,5 +1,4 @@

    (await (async (startPage = 0, autoClearConsole = true) => {
    ((async (startPage = 0, autoClearConsole = true) => {

    const getCookie = (name) => {
    const value = `; ${document.cookie}`;
    @@ -28,8 +27,6 @@
    }

    const callAcl = async ({ id, name }) => {
    console.log(` --> Adding Item ${id} | ${name}...`)

    const response = await fetch("https://quixel.com/v1/acl", {
    "headers": {
    "authorization": "Bearer " + authToken,
    @@ -46,6 +43,17 @@
    }
    }

    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 = ""
    @@ -59,25 +67,48 @@
    return console.error("-> Error: cannot find authentication token. Please login again.")
    }

    // 2. Get total count of items
    // 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(`-> Total Items to add: ${totalItems} | ${totalPages} total pages with ${itemsPerPage} per page`)
    if (!confirm(`Click OK to start adding ${totalItems} items in your account.`)) return

    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 + " START =======================")
    console.log(`-> ======================= PAGE ${pageIdx + 1}/${totalPages} START =======================`)

    console.log("-> Getting Items from page " + pageIdx + " ...")
    console.log("-> Getting Items from page " + (pageIdx + 1) + " ...")

    const { hits: items } = await callCacheApi({ page: pageIdx })
    const aclPromises = items.map(callAcl)

    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 + " COMPLETED =======================")
    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.`)
    })())
  2. jamiephan revised this gist Sep 17, 2024. 1 changed file with 4 additions and 2 deletions.
    6 changes: 4 additions & 2 deletions run.js
    Original file line number Diff line number Diff line change
    @@ -1,4 +1,5 @@
    (await (async (startPage = 0) => {

    (await (async (startPage = 0, autoClearConsole = true) => {

    const getCookie = (name) => {
    const value = `; ${document.cookie}`;
    @@ -76,6 +77,7 @@

    await Promise.all(aclPromises)
    console.log("-> ======================= PAGE " + pageIdx + " COMPLETED =======================")
    if (autoClearConsole) console.clear() // Fix the issue that too much log hangs the console. Set autoClearConsole = false to keep the logs

    }
    })())
    })())
  3. jamiephan revised this gist Sep 17, 2024. 1 changed file with 1 addition and 0 deletions.
    1 change: 1 addition & 0 deletions README.md
    Original file line number Diff line number Diff line change
    @@ -20,6 +20,7 @@ As quixel is being removed, all items are free to aquire. This script is to auto
    - 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.
  4. jamiephan revised this gist Sep 17, 2024. No changes.
  5. jamiephan revised this gist Sep 17, 2024. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion README.md
    Original file line number Diff line number Diff line change
    @@ -6,7 +6,7 @@ As quixel is being removed, all items are free to aquire. This script is to auto
    ### How to use

    1. Copy the script below (`run.js`)
    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
  6. jamiephan created this gist Sep 17, 2024.
    34 changes: 34 additions & 0 deletions README.md
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,34 @@
    ## Script to add all items from [quixel](https://quixel.com/)

    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 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 <kbd>Enter</kbd>.
    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
    - 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. If it hang too long, note what page is had going (Search for log like `PAGE 10 START`)
    2. Copy the `run.js` script
    3. Update the `startPage = 0` on the first line to `startPage = 10` (assuming page 10 was hanged)
    81 changes: 81 additions & 0 deletions run.js
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,81 @@
    (await (async (startPage = 0) => {

    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 }) => {
    console.log(` --> Adding Item ${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}`)
    }
    }

    // 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 total count of items
    console.log("-> Getting Total Number of Pages....")
    const { nbPages: totalPages, hitsPerPage: itemsPerPage, nbHits: totalItems } = await callCacheApi()

    console.log(`-> Total Items to add: ${totalItems} | ${totalPages} total pages with ${itemsPerPage} per page`)
    if (!confirm(`Click OK to start adding ${totalItems} items in your account.`)) return

    // Loop
    for (let pageIdx = startPage || 0; pageIdx < totalPages; pageIdx++) {
    console.log("-> ======================= PAGE " + pageIdx + " START =======================")

    console.log("-> Getting Items from page " + pageIdx + " ...")

    const { hits: items } = await callCacheApi({ page: pageIdx })
    const aclPromises = items.map(callAcl)

    await Promise.all(aclPromises)
    console.log("-> ======================= PAGE " + pageIdx + " COMPLETED =======================")

    }
    })())