用于从Quixel添加所有项目的脚本
由于 quixel 正在被移除,所有物品都可以免费获得。此脚本用于自动将物品添加到您的帐户(截至撰写本文时,总共有18874
件物品)
注意:此脚本仅在最新版本的 Chrome 中测试过。
如何使用
- 复制下面的脚本
- 登录 https://quixel.com
- 前往 https://quixel.com/megascans/collections
- 打开 devtools (F12) -> 转到“控制台”选项卡
- 粘贴脚本并按Enter。
- 将会弹出一个对话框确认执行,单击“确定”
- 坐下来出现下方提示,表示全部购买完成
常见问题
- 收到“禁止访问”错误。(即使刷新后,整个页面仍显示“禁止访问”)
- API 添加速度过快可能会导致您达到 API 的速率限制。(我的测试是在 10 页之后进行的,所以大约有 10k 个项目)。
- 等待约 10-20 分钟后继续。加载https://quixel.com
Common Fixes -> Restart script
后请参阅继续执行。
- 脚本似乎已暂停/挂起
- 可能是日志记录太多了。尝试监控脚本,如果显示“END PAGE X”,记下页码(以备需要重新启动)并通过单击 devtools 中的“🚫”图标清除控制台。
- 参见
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
- 清除浏览器 cookies 并重新登录 quixel。尝试手动添加 1 个项目。如果成功,则查看
Common Fixes -> Restart script
是否继续执行。
- 清除浏览器 cookies 并重新登录 quixel。尝试手动添加 1 个项目。如果成功,则查看
常见修复
重启脚本
- 注意它正在运行哪个页面
- 复制
run.js
脚本 startPage = 0
将第一行更新为startPage = 10
(假设第 10 页被挂起)
变更日志
- 初始脚本发布
- 更新以清除日志,减少卡住的机会
- [当前] 跳过已获取的项目。减少日志输出。脚本完成后增加更多显示信息,显示已购买项目的数量。由于现在跳过了已购买的项目,实际上您不再需要指定开始页面。
(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 sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
const fetchWithTimeout = (resource, options = {}) => {
const { timeout = 10000 } = options;
return Promise.race([
fetch(resource, options),
new Promise((_, reject) =>
setTimeout(() => reject(new Error('Request timeout')), timeout)
),
]);
};
const callCacheApi = async (params = {}) => {
const defaultParams = {
page: 0,
maxValuesPerFacet: 1000,
hitsPerPage: 1000,
attributesToRetrieve: ["id", "name"].join(","),
};
const fetchData = async () => {
const response = await fetchWithTimeout("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",
});
if (!response.ok) {
throw new Error(`Error fetching from Cache API: ${response.statusText}`);
}
return await response.json();
};
return await retryOperation(fetchData, 2000, 5);
};
const callAcl = async ({ id, name }) => {
const fetchData = async () => {
const response = await fetchWithTimeout("https://quixel.com/v1/acl", {
headers: {
authorization: "Bearer " + authToken,
"content-type": "application/json;charset=UTF-8",
},
body: JSON.stringify({ assetID: id }),
method: "POST",
});
if (!response.ok) {
throw new Error(`Error adding item ${id} | ${name}: ${response.statusText}`);
}
const json = await response.json();
if (json?.isError) {
console.error(` --> **Failed to add item** Item ${id} | ${name} (${json?.msg})`);
} else {
console.log(` --> Added item ${id} | ${name}`);
}
};
return await retryOperation(fetchData, 2000, 5);
};
const callAcquired = async () => {
const fetchData = async () => {
const response = await fetchWithTimeout("https://quixel.com/v1/assets/acquired", {
headers: {
authorization: "Bearer " + authToken,
"content-type": "application/json;charset=UTF-8",
},
method: "GET",
});
if (!response.ok) {
throw new Error(`Error fetching acquired items: ${response.statusText}`);
}
return await response.json();
};
return await retryOperation(fetchData, 2000, 5);
};
const retryOperation = async (operation, delay, retries) => {
let lastError;
for (let attempt = 1; attempt <= retries; attempt++) {
try {
return await operation();
} catch (error) {
lastError = error;
console.warn(`Attempt ${attempt} failed (${error.message}). Retrying in ${delay}ms...`);
await sleep(delay);
delay *= 2; // Exponential backoff
}
}
throw lastError;
};
let authToken = "";
const initialize = async () => {
console.log("-> Checking Auth API Token...");
try {
const authCookie = getCookie("auth") ?? "{}";
authToken = JSON.parse(decodeURIComponent(authCookie))?.token;
if (!authToken) {
throw new Error("-> Error: Authentication token not found. Please log in again.");
}
} catch (_) {
throw new Error("-> Error: Authentication token not found. Please log in again.");
}
console.log("-> Fetching acquired items...");
acquiredItems = (await callAcquired()).map((a) => a.assetID);
console.log("-> Fetching total number of pages...");
const initialData = await callCacheApi();
totalPages = initialData.nbPages;
itemsPerPage = initialData.hitsPerPage;
totalItems = initialData.nbHits;
console.log("-> ==============================================");
console.log(`-> Total items: ${totalItems}`);
console.log(`-> ${totalPages} total pages with ${itemsPerPage} items per page`);
console.log(`-> Total items to add: ${totalItems - acquiredItems.length}.`);
console.log("-> ==============================================");
if (!confirm(`Click OK to add ${totalItems - acquiredItems.length} items to your account.`)) {
throw new Error("-> Process cancelled by user.");
}
};
let acquiredItems = [];
let totalPages = 0;
let itemsPerPage = 0;
let totalItems = 0;
const MAX_CONCURRENT_REQUESTS = 5;
const mainProcess = async () => {
for (let pageIdx = startPage || 0; pageIdx < totalPages; pageIdx++) {
console.log(`-> ======================= PAGE ${pageIdx + 1}/${totalPages} START =======================`);
console.log("-> Fetching items from page " + (pageIdx + 1) + " ...");
const pageData = await callCacheApi({ page: pageIdx });
const items = pageData.hits;
console.log("-> Adding unacquired items...");
// Filter out already acquired items
const unownedItems = items.filter((i) => !acquiredItems.includes(i.id));
// Save current progress in localStorage
localStorage.setItem('currentPage', pageIdx);
// Limit concurrent requests
const queue = [...unownedItems];
const workers = Array.from({ length: MAX_CONCURRENT_REQUESTS }, async () => {
while (queue.length > 0) {
const item = queue.shift();
try {
await callAcl(item);
} catch (error) {
console.error(`Error with item ${item.id}: ${error.message}`);
}
}
});
await Promise.all(workers);
console.log(`-> ======================= PAGE ${pageIdx + 1}/${totalPages} COMPLETED =======================`);
if (autoClearConsole) console.clear();
}
};
const finalize = async () => {
console.log("-> Fetching new acquisition info...");
const newAcquiredItems = await callAcquired();
const newItemsAcquired = newAcquiredItems.length;
const newTotalCount = (await callCacheApi()).nbHits;
console.log(`-> Completed. Your account now has a total of ${newItemsAcquired} out of ${newTotalCount} items.`);
alert(`-> Your account now has a total of ${newItemsAcquired} out of ${newTotalCount} items.\n\nIf you find some items missing, try refreshing the page and run the script again.`);
};
try {
// Check if progress was saved
const savedPage = localStorage.getItem('currentPage');
if (savedPage !== null) {
startPage = parseInt(savedPage, 10);
console.log(`-> Resuming from page ${startPage + 1}`);
}
await initialize();
await mainProcess();
await finalize();
// Clear progress
localStorage.removeItem('currentPage');
} catch (error) {
console.error(error.message);
console.log("-> The script could not be completed.");
}
})();
1、本站资源大多来自网友发稿,如有侵犯你的权益请联系管理员,我们会第一时间进行审核删除。仅用于个人学习或测试研究使用,Email:[email protected]
2、有很多小伙伴经常问插件无法安装,有很大一部分用英文原版就可以解决问题。以便于在未来的学习和工作可以更高效的吸收英文资源,提高大家的学习效率和学习效果。
3、交流反馈插件素材更多问题~可以联系加QQ群:819091096
解压密码点击查看