printhive/test/makerworld.test.js
dlawler489 a313e0e224 Retry MakerWorld's anti-bot check, and flag imports that got no files
A run of downloads gets challenged by Cloudflare partway through: the answer
comes back as an ordinary reply, so nothing retried it and the model landed
with only its metadata — looking, on the watch page, like a success.

resolveDownload now recognises a challenge and gives FlareSolverr's browser a
couple more goes; what still fails is recorded as 'partial' and shown with its
reason and a "Get files" button, which clears the flag once the files arrive.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-16 12:34:04 +10:00

97 lines
5.5 KiB
JavaScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// MakerWorld importer helpers (lib/importers/makerworld.js).
const test = require('node:test');
const assert = require('node:assert/strict');
const makerworld = require('../lib/importers/makerworld');
const page = (design) =>
`<html><script id="__NEXT_DATA__" type="application/json">${JSON.stringify({ props: { pageProps: { design } } })}</script></html>`;
test('printable instances and the license are read from the page data', () => {
const html = page({
license: 'Standard Digital File License',
instances: [{ id: 650088, title: 'AMS - 0.4 Nozzle - 0.08 Layer' }, { id: 650089 }],
});
assert.deepEqual(makerworld.parseInstances(html), [
{ id: 650088, title: 'AMS - 0.4 Nozzle - 0.08 Layer' },
{ id: 650089, title: '' },
]);
assert.equal(makerworld.parseLicense(html), 'Standard Digital File License');
assert.deepEqual(makerworld.parseInstances('<html><title>Just a moment...</title></html>'), []);
});
test('a download answer without a link says why, and flags a refused token', () => {
assert.equal(makerworld.downloadProblem({ name: 'bookmarks.3mf', url: 'https://cdn.example/bookmarks.3mf' }), null);
const refused = makerworld.downloadProblem({ code: 1, error: 'Please log in to download models.' });
assert.equal(refused.tokenRejected, true);
assert.match(refused.message, /token \(it has probably expired\)/);
assert.deepEqual(makerworld.downloadProblem({ code: 7, error: 'Model not found' }),
{ message: 'MakerWorld didn’t give a download link: Model not found', tokenRejected: false });
assert.deepEqual(makerworld.downloadProblem(null), { message: 'MakerWorld didn’t give a download link', tokenRejected: false });
});
const collectionPage = (favorite, favoriteDesigns) =>
`<html><script id="__NEXT_DATA__" type="application/json">${JSON.stringify({ props: { pageProps: { favorite, favoriteDesigns } } })}</script></html>`;
test('a collection page gives its models, name and total', () => {
const html = collectionPage(
{ id: 31103374, title: 'markets', designCnt: 3 },
{ total: 3, hits: [
{ id: 719274, slug: 'skeleton-bookmarks', title: 'Skeleton Bookmarks' },
{ id: 926432, slug: 'doomhammer', title: 'Doomhammer' },
{ id: 12345, title: 'No slug' },
] });
const collection = makerworld.parseCollection(html);
assert.equal(collection.id, '31103374');
assert.equal(collection.title, 'markets');
assert.equal(collection.total, 3);
assert.deepEqual(collection.designs.map((d) => d.id), ['719274', '926432', '12345']);
assert.equal(makerworld.designUrl(collection.designs[0]), 'https://makerworld.com/en/models/719274-skeleton-bookmarks');
assert.equal(makerworld.designUrl(collection.designs[2]), 'https://makerworld.com/en/models/12345');
// The page can hold fewer models than the collection does.
const partial = makerworld.parseCollection(collectionPage({ id: 1, title: 'big', designCnt: 120 }, { total: 120, hits: [{ id: 5, slug: 'a', title: 'A' }] }));
assert.equal(partial.total, 120);
assert.equal(partial.designs.length, 1);
});
test('a page without a collection (private, or Cloudflare) parses as nothing', () => {
assert.equal(makerworld.parseCollection('<html><title>Just a moment...</title></html>'), null);
assert.equal(makerworld.parseCollection(collectionPage(null, null)), null);
// What MakerWorld serves for a private collection when the request isn't signed in.
assert.equal(makerworld.parseCollection(collectionPage({ id: -1, title: '', status: 0, designCnt: 0 }, { total: 0, hits: [] })), null);
});
test('more models are read from the collection’s own load-more call', () => {
assert.equal(makerworld.collectionDesignsUrl('1465524', { offset: 64, limit: 64, seed: 0 }),
'https://makerworld.com/api/v1/design-service/favorites/1465524/designs?offset=64&limit=64&seed=0');
assert.equal(makerworld.collectionDesignsUrl('7'),
'https://makerworld.com/api/v1/design-service/favorites/7/designs?offset=0&limit=64');
const page = makerworld.parseCollectionDesigns({ total: 535, seed: 0, hits: [{ id: 516328, slug: 'a-thing', title: 'A thing' }, { id: 887552 }] });
assert.equal(page.total, 535);
assert.equal(page.seed, 0);
assert.deepEqual(page.designs, [{ id: '516328', slug: 'a-thing', title: 'A thing' }, { id: '887552', slug: '', title: '' }]);
assert.deepEqual(makerworld.parseCollectionDesigns(null), { designs: [], total: 0, seed: undefined });
});
test('collection links are recognised', () => {
assert.equal(makerworld.collectionIdFromUrl('https://makerworld.com/en/collections/31103374-markets'), '31103374');
assert.equal(makerworld.collectionIdFromUrl('https://makerworld.com/collections/31103374'), '31103374');
assert.equal(makerworld.collectionIdFromUrl('https://makerworld.com/en/models/719274-skeleton-bookmarks'), null);
});
test('MakerWorld’s anti-bot check is told apart from a refused token', () => {
const challenge = { code: 1, error: 'We need to confirm that you are not a robot.' };
assert.equal(makerworld.isChallenge(challenge), true);
assert.equal(makerworld.isChallenge({ url: 'https://cdn.example/x.3mf' }), false);
assert.equal(makerworld.isChallenge({ code: 1, error: 'Please log in to download models.' }), false);
const blocked = makerworld.downloadProblem(challenge);
assert.equal(blocked.challenged, true);
assert.equal(blocked.tokenRejected, false);
assert.match(blocked.message, /anti-bot/);
const refused = makerworld.downloadProblem({ code: 1, error: 'Please log in to download models.' });
assert.equal(refused.tokenRejected, true);
assert.equal(refused.challenged, undefined);
});