mirror of
https://github.com/TautvydasDerzinskas/Thingport.git
synced 2026-10-18 20:59:55 -05:00
fix(extension): check a MakerWorld collection against the library in one call
The collection panel sent one /import/status request per model, all at once. When any failed it counted the model as new, so a collection already imported could offer to import almost everything again. Add POST /import/status/batch and use it, falling back to paced per-model checks on older instances. A failed check now shows an error instead of guessing.
This commit is contained in:
1 parent
1408b09c59
commit
44c687b3ae
3 files changed
+60
-11
No files matched your search
@@ -16,6 +16,7 @@ import {
|
||||
importPrintFromUrl,
|
||||
inspectImportLink,
|
||||
} from "../services/importService";
|
||||
import { findLibraryPrints } from "../services/collectionSyncService";
|
||||
import { resolveMakerworldCookie } from "../services/importResolvers";
|
||||
import {
|
||||
extractMakerworldBearerToken,
|
||||
@@ -190,6 +191,21 @@ router.get(
|
||||
}),
|
||||
);
|
||||
|
||||
const statusBatchSchema = z.object({
|
||||
provider: z.enum(["makerworld", "thingiverse", "printables"]),
|
||||
external_ids: z.array(z.string().max(64)).max(10_000),
|
||||
});
|
||||
|
||||
// One query for a whole collection page; per-model /import/status calls bog down on big ones.
|
||||
router.post(
|
||||
"/import/status/batch",
|
||||
asyncHandler(async (req, res) => {
|
||||
const body = parseBody(statusBatchSchema, req.body);
|
||||
const prints = await findLibraryPrints(req.userId!, body.provider, body.external_ids);
|
||||
res.json({ print_ids: Object.fromEntries(prints) });
|
||||
}),
|
||||
);
|
||||
|
||||
router.post(
|
||||
"/import/zip/entries",
|
||||
asyncHandler(async (req, res) => {
|
||||
|
||||
@@ -227,6 +227,11 @@ describe("filling a model's missing details from its source", () => {
|
||||
.get(`/api/import/status?url=${encodeURIComponent(`https://makerworld.com/en/models/${designId}`)}`)
|
||||
.set(auth());
|
||||
expect(status.body.gaps).toEqual([]);
|
||||
const batch = await request(app)
|
||||
.post("/api/import/status/batch")
|
||||
.set(auth())
|
||||
.send({ provider: "makerworld", external_ids: [designId, "0"] });
|
||||
expect(batch.body.print_ids).toEqual({ [designId]: print.id });
|
||||
|
||||
// Clearing a field the source does have opens a gap again.
|
||||
await prisma.print.update({ where: { id: print.id }, data: { notes: null } });
|
||||
|
||||
@@ -109,6 +109,38 @@ function stepDelayOptions(selectedMs: number): string {
|
||||
|
||||
type ModelStatus = { url: string; already_imported: boolean; print_id: string | null };
|
||||
|
||||
// Per-model checks, for instances without the batch endpoint, run a few at a time.
|
||||
const STATUS_CHECK_CONCURRENCY = 6;
|
||||
|
||||
/** Throws rather than guessing: an unchecked model counted as new would be re-imported. */
|
||||
async function checkLibrary(ids: string[]): Promise<ModelStatus[]> {
|
||||
let printIds: Record<string, string>;
|
||||
try {
|
||||
({ print_ids: printIds } = await api<{ print_ids: Record<string, string> }>("POST", "/import/status/batch", {
|
||||
provider: "makerworld",
|
||||
external_ids: ids,
|
||||
}));
|
||||
} catch {
|
||||
printIds = {};
|
||||
const queue = [...ids];
|
||||
const worker = async () => {
|
||||
for (let id = queue.shift(); id; id = queue.shift()) {
|
||||
const status = await api<ImportStatus>(
|
||||
"GET",
|
||||
`/import/status?url=${encodeURIComponent(makerworldModelUrl(id))}`,
|
||||
);
|
||||
if (status.already_imported && status.print_id) printIds[id] = status.print_id;
|
||||
}
|
||||
};
|
||||
await Promise.all(Array.from({ length: STATUS_CHECK_CONCURRENCY }, worker));
|
||||
}
|
||||
return ids.map((id) => ({
|
||||
url: makerworldModelUrl(id),
|
||||
already_imported: id in printIds,
|
||||
print_id: printIds[id] ?? null,
|
||||
}));
|
||||
}
|
||||
|
||||
export async function loadMakerworldGuidedCollection(): Promise<void> {
|
||||
const collectionTitle = document.getElementsByTagName("h1")[0]?.innerText?.trim() || null;
|
||||
|
||||
@@ -128,17 +160,13 @@ export async function loadMakerworldGuidedCollection(): Promise<void> {
|
||||
}
|
||||
|
||||
renderPanel(statusHtml(`Checking ${ids.length} models against your library…`));
|
||||
const statuses: ModelStatus[] = await Promise.all(
|
||||
ids.map(async (id) => {
|
||||
const url = makerworldModelUrl(id);
|
||||
try {
|
||||
const status = await api<ImportStatus>("GET", `/import/status?url=${encodeURIComponent(url)}`);
|
||||
return { url, already_imported: status.already_imported, print_id: status.print_id ?? null };
|
||||
} catch {
|
||||
return { url, already_imported: false, print_id: null };
|
||||
}
|
||||
}),
|
||||
);
|
||||
let statuses: ModelStatus[];
|
||||
try {
|
||||
statuses = await checkLibrary(ids);
|
||||
} catch (err) {
|
||||
renderPanel(errorHtml(err));
|
||||
return;
|
||||
}
|
||||
const toImport = statuses.filter((s) => !s.already_imported);
|
||||
const alreadyImported = statuses.filter((s) => s.already_imported);
|
||||
const sync = await syncState;
|
||||
|
||||
Reference in new issue
Block a user