@rrrublev/wb-private-api 0.8.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +220 -0
- package/index.js +11 -0
- package/package.json +43 -0
- package/src/Constants.js +1181 -0
- package/src/SessionBuilder.js +283 -0
- package/src/Utils.js +154 -0
- package/src/WBCatalog.js +37 -0
- package/src/WBFeedback.js +26 -0
- package/src/WBPrivateAPI.js +527 -0
- package/src/WBProduct.js +295 -0
- package/src/WBQuestion.js +7 -0
|
@@ -0,0 +1,527 @@
|
|
|
1
|
+
/* eslint-disable camelcase */
|
|
2
|
+
const format = require("string-format");
|
|
3
|
+
const Constants = require("./Constants");
|
|
4
|
+
const WBProduct = require("./WBProduct");
|
|
5
|
+
const Utils = require("./Utils");
|
|
6
|
+
const WBCatalog = require("./WBCatalog");
|
|
7
|
+
const SessionBuilder = require("./SessionBuilder");
|
|
8
|
+
|
|
9
|
+
async function mapWithConcurrency(items, limit, mapper) {
|
|
10
|
+
const results = new Array(items.length);
|
|
11
|
+
let nextIndex = 0;
|
|
12
|
+
|
|
13
|
+
async function worker() {
|
|
14
|
+
while (nextIndex < items.length) {
|
|
15
|
+
const i = nextIndex++;
|
|
16
|
+
results[i] = await mapper(items[i], i);
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
await Promise.all(Array.from({ length: Math.min(limit, items.length) }, () => worker()));
|
|
21
|
+
return results;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
class WBPrivateAPI {
|
|
25
|
+
/* Creating a new instance of the class WBPrivateAPI. */
|
|
26
|
+
constructor({ destination, wbaasToken } = {}) {
|
|
27
|
+
this.session = SessionBuilder.create();
|
|
28
|
+
this.destination = destination;
|
|
29
|
+
this.dest = destination?.ids?.at(-1) ?? null;
|
|
30
|
+
const token = wbaasToken || SessionBuilder.readToken();
|
|
31
|
+
if (token) {
|
|
32
|
+
SessionBuilder.setAntibotToken(this.session, token);
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Устанавливает токен x_wbaas_token для доступа к внутренним API WB.
|
|
38
|
+
* Получить токен можно через консоль браузера на wildberries.ru:
|
|
39
|
+
* @see scripts/get-wb-token.js
|
|
40
|
+
* @param {string} token — значение cookie x_wbaas_token
|
|
41
|
+
*/
|
|
42
|
+
setToken(token) {
|
|
43
|
+
SessionBuilder.setAntibotToken(this.session, token);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* It searches for products by keyword.
|
|
48
|
+
* @param {string} keyword - The keyword to search for
|
|
49
|
+
* @param {number} pageCount - Number of pages to retrieve
|
|
50
|
+
* @returns {WBCatalog} WBCatalog object with plain product objects inside
|
|
51
|
+
*/
|
|
52
|
+
async search(keyword, pageCount = 0, retries = 0, filters = []) {
|
|
53
|
+
const products = [];
|
|
54
|
+
|
|
55
|
+
const totalProducts = await this.searchTotalProducts(keyword);
|
|
56
|
+
if (totalProducts === 0) {
|
|
57
|
+
return new WBCatalog({
|
|
58
|
+
keyword,
|
|
59
|
+
catalog_type: null,
|
|
60
|
+
catalog_value: null,
|
|
61
|
+
pages: 0,
|
|
62
|
+
products: [],
|
|
63
|
+
totalProducts: 0,
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
const { catalog_type, catalog_value } = await this.getQueryMetadata(keyword, 0, false, 1, retries);
|
|
68
|
+
const catalogConfig = { keyword, catalog_type, catalog_value };
|
|
69
|
+
|
|
70
|
+
let totalPages = this.getPageCount(totalProducts);
|
|
71
|
+
|
|
72
|
+
if (pageCount > 0 && pageCount < totalPages) {
|
|
73
|
+
totalPages = pageCount;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
const threads = Array.from({ length: totalPages }, (_, i) => i + 1);
|
|
77
|
+
const parsedPages = await mapWithConcurrency(threads, 5, (thr) => this.getCatalogPage(catalogConfig, thr, retries, filters));
|
|
78
|
+
|
|
79
|
+
const productOptions = { session: this.session, destination: this.destination };
|
|
80
|
+
for (const page of parsedPages) {
|
|
81
|
+
if (Array.isArray(page)) {
|
|
82
|
+
products.push(...page.map((v) => new WBProduct(v, productOptions)));
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
Object.assign(catalogConfig, {
|
|
87
|
+
pages: totalPages,
|
|
88
|
+
products,
|
|
89
|
+
totalProducts,
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
return new WBCatalog(catalogConfig);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* It takes a keyword and returns an array of three elements,
|
|
97
|
+
* shardKey, preset and preset value
|
|
98
|
+
* @param {string} keyword - The keyword you want to search for.
|
|
99
|
+
* @returns {array} - An array of shardKey, preset and preset value
|
|
100
|
+
*/
|
|
101
|
+
async getQueryMetadata(keyword, limit = 0, _withProducts = false, page = 1, retries = 0, suppressSpellcheck = true) {
|
|
102
|
+
const params = {
|
|
103
|
+
appType: Constants.APPTYPES.DESKTOP,
|
|
104
|
+
curr: Constants.CURRENCIES.RUB,
|
|
105
|
+
dest: this.dest,
|
|
106
|
+
query: keyword,
|
|
107
|
+
resultset: "catalog",
|
|
108
|
+
sort: "popular",
|
|
109
|
+
spp: "30",
|
|
110
|
+
suppressSpellcheck,
|
|
111
|
+
};
|
|
112
|
+
if (page !== 1) {
|
|
113
|
+
params.page = page;
|
|
114
|
+
}
|
|
115
|
+
if (limit !== 100) {
|
|
116
|
+
params.limit = limit;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
const res = await this.session.get(Constants.URLS.SEARCH.EXACTMATCH, {
|
|
120
|
+
params,
|
|
121
|
+
headers: {
|
|
122
|
+
"x-queryid": Utils.Search.getQueryIdForSearch(),
|
|
123
|
+
},
|
|
124
|
+
retryOptions: { retries },
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
if ("catalog_type" in (res.data?.metadata ?? {}) && "catalog_value" in (res.data?.metadata ?? {})) {
|
|
128
|
+
return { ...res.data.metadata, products: res.data.data?.products ?? res.data.products };
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
if ("shardKey" in (res.data ?? {}) && "query" in (res.data ?? {})) {
|
|
132
|
+
return {
|
|
133
|
+
catalog_type: res.data.shardKey,
|
|
134
|
+
catalog_value: res.data.query,
|
|
135
|
+
products: [],
|
|
136
|
+
};
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
return { catalog_type: null, catalog_value: null, products: [] };
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
/**
|
|
143
|
+
* It returns the total number of products for a given keyword
|
|
144
|
+
* @param {string} keyword - the search query
|
|
145
|
+
* @returns Total number of products
|
|
146
|
+
*/
|
|
147
|
+
async searchTotalProducts(keyword) {
|
|
148
|
+
const res = await this.session.get(Constants.URLS.SEARCH.EXACTMATCH, {
|
|
149
|
+
params: {
|
|
150
|
+
appType: Constants.APPTYPES.DESKTOP,
|
|
151
|
+
curr: Constants.CURRENCIES.RUB,
|
|
152
|
+
locale: Constants.LOCALES.RU,
|
|
153
|
+
lang: Constants.LOCALES.RU,
|
|
154
|
+
dest: this.dest,
|
|
155
|
+
query: keyword,
|
|
156
|
+
resultset: "catalog",
|
|
157
|
+
limit: 0,
|
|
158
|
+
},
|
|
159
|
+
headers: {
|
|
160
|
+
"x-queryid": Utils.Search.getQueryIdForSearch(),
|
|
161
|
+
},
|
|
162
|
+
});
|
|
163
|
+
|
|
164
|
+
return res.data.total || 0;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
/**
|
|
168
|
+
* It returns the total number of products by supplier
|
|
169
|
+
* @param {number} supplierId - the search query
|
|
170
|
+
* @returns {number} Total number of products
|
|
171
|
+
*/
|
|
172
|
+
async getSupplierProductCount(supplierId) {
|
|
173
|
+
const res = await this.session.get(Constants.URLS.SUPPLIER.CATALOG, {
|
|
174
|
+
params: {
|
|
175
|
+
appType: Constants.APPTYPES.DESKTOP,
|
|
176
|
+
curr: Constants.CURRENCIES.RUB,
|
|
177
|
+
dest: this.dest,
|
|
178
|
+
supplier: supplierId,
|
|
179
|
+
limit: 0,
|
|
180
|
+
},
|
|
181
|
+
});
|
|
182
|
+
|
|
183
|
+
return res.data.total || 0;
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
/**
|
|
187
|
+
* @returns {number} Total number of products for a brand
|
|
188
|
+
*/
|
|
189
|
+
async getBrandProductCount(brandId) {
|
|
190
|
+
const res = await this.session.get(Constants.URLS.BRAND.CATALOG, {
|
|
191
|
+
params: {
|
|
192
|
+
appType: Constants.APPTYPES.DESKTOP,
|
|
193
|
+
curr: Constants.CURRENCIES.RUB,
|
|
194
|
+
dest: this.dest,
|
|
195
|
+
brand: brandId,
|
|
196
|
+
limit: 0,
|
|
197
|
+
},
|
|
198
|
+
});
|
|
199
|
+
return res.data.total || 0;
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
/**
|
|
203
|
+
* @returns {Promise<object>} Raw API response for brand catalog page
|
|
204
|
+
*/
|
|
205
|
+
async getBrandCatalog(brandId, page = 1) {
|
|
206
|
+
const res = await this.session.get(Constants.URLS.BRAND.CATALOG, {
|
|
207
|
+
params: {
|
|
208
|
+
appType: Constants.APPTYPES.DESKTOP,
|
|
209
|
+
curr: Constants.CURRENCIES.RUB,
|
|
210
|
+
dest: this.dest,
|
|
211
|
+
lang: Constants.LOCALES.RU,
|
|
212
|
+
page,
|
|
213
|
+
sort: "popular",
|
|
214
|
+
spp: "30",
|
|
215
|
+
brand: brandId,
|
|
216
|
+
},
|
|
217
|
+
});
|
|
218
|
+
return res.data || {};
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
/**
|
|
222
|
+
* @returns {array} Products from a brand catalog page
|
|
223
|
+
*/
|
|
224
|
+
async getBrandCatalogPage(brandId, page = 1, retries = 0) {
|
|
225
|
+
const res = await this.session.get(Constants.URLS.BRAND.CATALOG, {
|
|
226
|
+
params: {
|
|
227
|
+
appType: Constants.APPTYPES.DESKTOP,
|
|
228
|
+
curr: Constants.CURRENCIES.RUB,
|
|
229
|
+
dest: this.dest,
|
|
230
|
+
lang: Constants.LOCALES.RU,
|
|
231
|
+
page,
|
|
232
|
+
sort: "popular",
|
|
233
|
+
spp: "30",
|
|
234
|
+
brand: brandId,
|
|
235
|
+
},
|
|
236
|
+
retryOptions: { retries },
|
|
237
|
+
});
|
|
238
|
+
return res.data.data?.products ?? res.data.products ?? [];
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
/**
|
|
242
|
+
* It returns the data based on filters array
|
|
243
|
+
* @param {string} keyword - the search query
|
|
244
|
+
* @param {array} filters - array of filters elements like ['fbrand','fsupplier']
|
|
245
|
+
* @returns Total number of products
|
|
246
|
+
*/
|
|
247
|
+
async searchCustomFilters(keyword, filters) {
|
|
248
|
+
const res = await this.session.get(Constants.URLS.SEARCH.EXACTMATCH, {
|
|
249
|
+
params: {
|
|
250
|
+
appType: Constants.APPTYPES.DESKTOP,
|
|
251
|
+
curr: Constants.CURRENCIES.RUB,
|
|
252
|
+
dest: this.dest,
|
|
253
|
+
lang: Constants.LOCALES.RU,
|
|
254
|
+
query: keyword,
|
|
255
|
+
resultset: "filters",
|
|
256
|
+
sort: "popular",
|
|
257
|
+
filters: filters.join(";"),
|
|
258
|
+
},
|
|
259
|
+
headers: {
|
|
260
|
+
"x-queryid": Utils.Search.getQueryIdForSearch(),
|
|
261
|
+
},
|
|
262
|
+
});
|
|
263
|
+
return res.data?.data || {};
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
/**
|
|
267
|
+
* It gets all products from specified page
|
|
268
|
+
* @param {object} catalogConfig - { keyword, catalog_type, catalog_value }
|
|
269
|
+
* @param {number} page - page number
|
|
270
|
+
* @returns {array} - An array of products
|
|
271
|
+
*/
|
|
272
|
+
async getCatalogPage(catalogConfig, page = 1, retries = 0, filters = []) {
|
|
273
|
+
const options = {
|
|
274
|
+
params: {
|
|
275
|
+
appType: Constants.APPTYPES.DESKTOP,
|
|
276
|
+
curr: Constants.CURRENCIES.RUB,
|
|
277
|
+
dest: this.dest,
|
|
278
|
+
query: catalogConfig.keyword.toLowerCase(),
|
|
279
|
+
resultset: "catalog",
|
|
280
|
+
sort: "popular",
|
|
281
|
+
spp: "30",
|
|
282
|
+
suppressSpellcheck: false,
|
|
283
|
+
},
|
|
284
|
+
headers: {
|
|
285
|
+
"x-queryid": Utils.Search.getQueryIdForSearch(),
|
|
286
|
+
referrer: "https://www.wildberries.ru/catalog/0/search.aspx?page=2&sort=popular&search=" + encodeURI(catalogConfig.keyword.toLowerCase()),
|
|
287
|
+
},
|
|
288
|
+
};
|
|
289
|
+
if (page !== 1) {
|
|
290
|
+
options.params.page = page;
|
|
291
|
+
}
|
|
292
|
+
for (const filter of filters) {
|
|
293
|
+
options.params[filter.type] = filter.value;
|
|
294
|
+
}
|
|
295
|
+
options.retryOptions = { retries };
|
|
296
|
+
const res = await this.session.get(Constants.URLS.SEARCH.EXACTMATCH, options);
|
|
297
|
+
if (res.data?.metadata?.catalog_value === "preset=11111111") {
|
|
298
|
+
throw new Error("BAD CATALOG VALUE - 11111111");
|
|
299
|
+
}
|
|
300
|
+
return res.data.data?.products ?? res.data.products;
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
/**
|
|
304
|
+
* Search for adverts and their ads form specified keyword
|
|
305
|
+
* @param {string} keyword - the search query
|
|
306
|
+
* @returns {object} - An object with adverts and their ads
|
|
307
|
+
*/
|
|
308
|
+
async getSearchAds(keyword) {
|
|
309
|
+
const options = { params: { keyword } };
|
|
310
|
+
const res = await this.session.get(Constants.URLS.SEARCH.ADS, options);
|
|
311
|
+
return res.data;
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
/**
|
|
315
|
+
* Search for carousel ads inside product card
|
|
316
|
+
* @param {number} productId - product id
|
|
317
|
+
* @returns {array} - An array with ads
|
|
318
|
+
*/
|
|
319
|
+
async getCarouselAds(productId) {
|
|
320
|
+
const res = await this.session.get(Constants.URLS.SEARCH.CAROUSEL_ADS, {
|
|
321
|
+
params: { nm: productId },
|
|
322
|
+
});
|
|
323
|
+
return res.data;
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
/**
|
|
327
|
+
* It takes a query string and returns a list of suggestions that match the query
|
|
328
|
+
* @param {string} query - the search query
|
|
329
|
+
* @returns {array} - An array of objects.
|
|
330
|
+
*/
|
|
331
|
+
async keyHint(query) {
|
|
332
|
+
const res = await this.session.get(Constants.URLS.SEARCH.HINT, {
|
|
333
|
+
params: {
|
|
334
|
+
query,
|
|
335
|
+
gender: Constants.SEX.COMMON,
|
|
336
|
+
locale: Constants.LOCALES.RU,
|
|
337
|
+
lang: Constants.LOCALES.RU,
|
|
338
|
+
appType: Constants.APPTYPES.DESKTOP,
|
|
339
|
+
},
|
|
340
|
+
});
|
|
341
|
+
return res.data;
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
/**
|
|
345
|
+
* It takes a productId, makes a request to the server, and returns the similar Ids
|
|
346
|
+
* @param productId - The product ID of the product you want to search for similar
|
|
347
|
+
* @returns {object} with similar product Ids
|
|
348
|
+
*/
|
|
349
|
+
async searchSimilarByNm(productId) {
|
|
350
|
+
const res = await this.session.get(Constants.URLS.SEARCH.SIMILAR_BY_NM, {
|
|
351
|
+
params: { nm: productId },
|
|
352
|
+
});
|
|
353
|
+
return res.data;
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
/**
|
|
357
|
+
* It takes an array of productIds and a destination, and returns an array of
|
|
358
|
+
* products with delivery time data
|
|
359
|
+
* @param {array} productIds - array of product IDs
|
|
360
|
+
* @param {number} retries - number of retries
|
|
361
|
+
* @returns {array} of products with delivery times
|
|
362
|
+
*/
|
|
363
|
+
async getDeliveryDataByNms(productIds, retries = 0) {
|
|
364
|
+
const res = await this.session.get(Constants.URLS.PRODUCT.DELIVERYDATA, {
|
|
365
|
+
params: {
|
|
366
|
+
appType: Constants.APPTYPES.DESKTOP,
|
|
367
|
+
locale: Constants.LOCALES.RU,
|
|
368
|
+
dest: this.dest,
|
|
369
|
+
nm: productIds.join(";"),
|
|
370
|
+
},
|
|
371
|
+
retryOptions: {
|
|
372
|
+
retries,
|
|
373
|
+
},
|
|
374
|
+
});
|
|
375
|
+
return res.data.data?.products ?? res.data.products;
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
/**
|
|
379
|
+
* @returns Array of promos
|
|
380
|
+
*/
|
|
381
|
+
async getPromos() {
|
|
382
|
+
const result = await this.session.get(Constants.URLS.PROMOS);
|
|
383
|
+
return result.data;
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
|
|
387
|
+
/**
|
|
388
|
+
* @returns Array of found products
|
|
389
|
+
*/
|
|
390
|
+
async getListOfProducts(productIds) {
|
|
391
|
+
const res = await this.session.get(Constants.URLS.SEARCH.LIST, {
|
|
392
|
+
params: {
|
|
393
|
+
appType: Constants.APPTYPES.DESKTOP,
|
|
394
|
+
dest: this.dest,
|
|
395
|
+
curr: Constants.CURRENCIES.RUB,
|
|
396
|
+
lang: Constants.LOCALES.RU,
|
|
397
|
+
nm: productIds.join(";"),
|
|
398
|
+
},
|
|
399
|
+
});
|
|
400
|
+
return res.data.data?.products ?? res.data.products ?? [];
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
/**
|
|
404
|
+
* @returns Object with supplier info
|
|
405
|
+
*/
|
|
406
|
+
async getSupplierInfo(sellerId) {
|
|
407
|
+
const res = await this.session.get(format(Constants.URLS.SUPPLIER.INFO, sellerId));
|
|
408
|
+
return res.data || {};
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
/**
|
|
412
|
+
* @returns Object with supplier shipment info
|
|
413
|
+
*/
|
|
414
|
+
async getSupplierShipment(sellerId) {
|
|
415
|
+
const res = await this.session.get(format(Constants.URLS.SUPPLIER.SHIPMENT, sellerId), {
|
|
416
|
+
headers: {
|
|
417
|
+
"x-client-name": "site",
|
|
418
|
+
},
|
|
419
|
+
});
|
|
420
|
+
return res.data || {};
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
/**
|
|
424
|
+
* It takes a supplier id and returns an array of products
|
|
425
|
+
* @param {number} supplierId - supplier ID
|
|
426
|
+
* @param {number} page - page number
|
|
427
|
+
* @returns {Promise<object>} - Raw API response data.
|
|
428
|
+
*/
|
|
429
|
+
async getSupplierCatalog(supplierId, page = 1) {
|
|
430
|
+
const res = await this.session.get(Constants.URLS.SUPPLIER.CATALOG, {
|
|
431
|
+
params: {
|
|
432
|
+
appType: Constants.APPTYPES.DESKTOP,
|
|
433
|
+
curr: Constants.CURRENCIES.RUB,
|
|
434
|
+
dest: this.dest,
|
|
435
|
+
lang: Constants.LOCALES.RU,
|
|
436
|
+
page,
|
|
437
|
+
sort: "popular",
|
|
438
|
+
spp: "30",
|
|
439
|
+
supplier: supplierId,
|
|
440
|
+
},
|
|
441
|
+
});
|
|
442
|
+
return res.data || {};
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
/**
|
|
446
|
+
* It gets all products from supplier catalog with pagination
|
|
447
|
+
* @param {number} supplierId - supplier ID
|
|
448
|
+
* @param {number} pageCount - Number of pages to retrieve (0 = all pages)
|
|
449
|
+
* @param {number} retries - Number of retries for failed requests
|
|
450
|
+
* @returns {WBCatalog} WBCatalog object with all supplier products
|
|
451
|
+
*/
|
|
452
|
+
async getSupplierCatalogAll(supplierId, pageCount = 0, retries = 0) {
|
|
453
|
+
const products = [];
|
|
454
|
+
|
|
455
|
+
const totalProducts = await this.getSupplierProductCount(supplierId);
|
|
456
|
+
if (totalProducts === 0) {
|
|
457
|
+
return new WBCatalog({
|
|
458
|
+
supplierId,
|
|
459
|
+
catalog_type: "supplier",
|
|
460
|
+
catalog_value: `supplier=${supplierId}`,
|
|
461
|
+
pages: 0,
|
|
462
|
+
products: [],
|
|
463
|
+
totalProducts: 0,
|
|
464
|
+
});
|
|
465
|
+
}
|
|
466
|
+
|
|
467
|
+
const catalogConfig = {
|
|
468
|
+
supplierId,
|
|
469
|
+
catalog_type: "supplier",
|
|
470
|
+
catalog_value: `supplier=${supplierId}`,
|
|
471
|
+
};
|
|
472
|
+
|
|
473
|
+
let totalPages = this.getPageCount(totalProducts);
|
|
474
|
+
|
|
475
|
+
if (pageCount > 0 && pageCount < totalPages) {
|
|
476
|
+
totalPages = pageCount;
|
|
477
|
+
}
|
|
478
|
+
|
|
479
|
+
const threads = Array.from({ length: totalPages }, (_, i) => i + 1);
|
|
480
|
+
const parsedPages = await mapWithConcurrency(threads, 5, (thr) => this.getSupplierCatalogPage(supplierId, thr, retries));
|
|
481
|
+
|
|
482
|
+
const productOptions = { session: this.session, destination: this.destination };
|
|
483
|
+
for (const page of parsedPages) {
|
|
484
|
+
if (Array.isArray(page)) {
|
|
485
|
+
products.push(...page.map((v) => new WBProduct(v, productOptions)));
|
|
486
|
+
}
|
|
487
|
+
}
|
|
488
|
+
|
|
489
|
+
Object.assign(catalogConfig, {
|
|
490
|
+
pages: totalPages,
|
|
491
|
+
products,
|
|
492
|
+
totalProducts,
|
|
493
|
+
});
|
|
494
|
+
|
|
495
|
+
return new WBCatalog(catalogConfig);
|
|
496
|
+
}
|
|
497
|
+
|
|
498
|
+
/**
|
|
499
|
+
* It gets products from specified supplier catalog page
|
|
500
|
+
* @param {number} supplierId - supplier ID
|
|
501
|
+
* @param {number} page - page number
|
|
502
|
+
* @param {number} retries - number of retries
|
|
503
|
+
* @returns {array} - An array of products
|
|
504
|
+
*/
|
|
505
|
+
async getSupplierCatalogPage(supplierId, page = 1, retries = 0) {
|
|
506
|
+
const res = await this.session.get(Constants.URLS.SUPPLIER.CATALOG, {
|
|
507
|
+
params: {
|
|
508
|
+
appType: Constants.APPTYPES.DESKTOP,
|
|
509
|
+
curr: Constants.CURRENCIES.RUB,
|
|
510
|
+
dest: this.dest,
|
|
511
|
+
lang: Constants.LOCALES.RU,
|
|
512
|
+
page,
|
|
513
|
+
sort: "popular",
|
|
514
|
+
spp: "30",
|
|
515
|
+
supplier: supplierId,
|
|
516
|
+
},
|
|
517
|
+
retryOptions: { retries },
|
|
518
|
+
});
|
|
519
|
+
return res.data.data?.products ?? res.data.products ?? [];
|
|
520
|
+
}
|
|
521
|
+
|
|
522
|
+
getPageCount(totalProducts) {
|
|
523
|
+
return Math.min(Math.ceil(totalProducts / Constants.PRODUCTS_PER_PAGE), Constants.PAGES_PER_CATALOG);
|
|
524
|
+
}
|
|
525
|
+
}
|
|
526
|
+
|
|
527
|
+
module.exports = WBPrivateAPI;
|