@bytebrand/export-cars 1.19.1 → 1.21.0
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/dist/configs/config.d.ts +1 -0
- package/dist/configs/config.js +1 -0
- package/dist/configs/config.js.map +1 -1
- package/dist/configs/evium.config.js +1 -0
- package/dist/configs/evium.config.js.map +1 -1
- package/dist/constants/images.d.ts +1 -0
- package/dist/constants/images.js +73 -0
- package/dist/constants/images.js.map +1 -0
- package/dist/exporters/as24.exporter.js +0 -2
- package/dist/exporters/as24.exporter.js.map +1 -1
- package/dist/exporters/auto24.exporter.js +0 -2
- package/dist/exporters/auto24.exporter.js.map +1 -1
- package/dist/exporters/autouncle.exporter.js +0 -2
- package/dist/exporters/autouncle.exporter.js.map +1 -1
- package/dist/exporters/facebook-shop.exporter.js +0 -2
- package/dist/exporters/facebook-shop.exporter.js.map +1 -1
- package/dist/exporters/fb.exporter.js +0 -2
- package/dist/exporters/fb.exporter.js.map +1 -1
- package/dist/exporters/meinstadt.exporter.js +0 -2
- package/dist/exporters/meinstadt.exporter.js.map +1 -1
- package/dist/exporters/mobile.exporter.d.ts +3 -2
- package/dist/exporters/mobile.exporter.js +10 -5
- package/dist/exporters/mobile.exporter.js.map +1 -1
- package/dist/exporters/modix.exporter.js +0 -2
- package/dist/exporters/modix.exporter.js.map +1 -1
- package/dist/exporters/ooyyo.exporter.js +0 -2
- package/dist/exporters/ooyyo.exporter.js.map +1 -1
- package/dist/exporters/rubrikk.exporter.js +0 -2
- package/dist/exporters/rubrikk.exporter.js.map +1 -1
- package/dist/exporters/trovit.exporter.js +0 -2
- package/dist/exporters/trovit.exporter.js.map +1 -1
- package/dist/formats/modix-xml.builder.js +1 -1
- package/dist/formats/modix-xml.builder.js.map +1 -1
- package/dist/services/images.service.d.ts +7 -3
- package/dist/services/images.service.js +107 -39
- package/dist/services/images.service.js.map +1 -1
- package/dist/transformers/fb.transformer.js +0 -4
- package/dist/transformers/fb.transformer.js.map +1 -1
- package/dist/transformers/mobile.transformer.js +2 -0
- package/dist/transformers/mobile.transformer.js.map +1 -1
- package/dist/transformers/modix.transformer.d.ts +3 -2
- package/dist/transformers/modix.transformer.js +5 -3
- package/dist/transformers/modix.transformer.js.map +1 -1
- package/dist/tsconfig.build.tsbuildinfo +1 -1
- package/package.json +3 -2
- package/src/configs/config.ts +1 -0
- package/src/configs/evium.config.ts +1 -0
- package/src/constants/images.ts +69 -0
- package/src/exporters/as24.exporter.ts +2 -2
- package/src/exporters/auto24.exporter.ts +2 -2
- package/src/exporters/autouncle.exporter.ts +2 -2
- package/src/exporters/facebook-shop.exporter.ts +2 -2
- package/src/exporters/fb.exporter.ts +2 -2
- package/src/exporters/meinstadt.exporter.ts +2 -2
- package/src/exporters/mobile.exporter.ts +12 -7
- package/src/exporters/modix.exporter.ts +2 -2
- package/src/exporters/ooyyo.exporter.ts +2 -2
- package/src/exporters/rubrikk.exporter.ts +2 -2
- package/src/exporters/trovit.exporter.ts +2 -2
- package/src/formats/modix-xml.builder.ts +1 -1
- package/src/services/images.service.ts +122 -62
- package/src/transformers/fb.transformer.ts +4 -4
- package/src/transformers/mobile.transformer.ts +5 -1
- package/src/transformers/modix.transformer.ts +3 -3
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bytebrand/export-cars",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.21.0",
|
|
4
4
|
"description": "",
|
|
5
5
|
"author": "bytebrand",
|
|
6
6
|
"license": "UNLICENSED",
|
|
@@ -28,6 +28,7 @@
|
|
|
28
28
|
"@nestjs/cache-manager": "^2.1.0",
|
|
29
29
|
"@nestjs/common": "^10.2.7",
|
|
30
30
|
"@nestjs/core": "^10.2.7",
|
|
31
|
+
"axios": "^1.5.1",
|
|
31
32
|
"bottleneck": "^2.19.5",
|
|
32
33
|
"cache-manager": "^5.2.4",
|
|
33
34
|
"csv-write-stream": "^2.0.0",
|
|
@@ -61,5 +62,5 @@
|
|
|
61
62
|
"engines": {
|
|
62
63
|
"node": ">=14.0.0"
|
|
63
64
|
},
|
|
64
|
-
"gitHead": "
|
|
65
|
+
"gitHead": "3ef72636ca23cee78df87f16e66089e1a532b053"
|
|
65
66
|
}
|
package/src/configs/config.ts
CHANGED
|
@@ -10,6 +10,7 @@ export class EviumConfig extends Config {
|
|
|
10
10
|
this.imageUrl = `https://images.${this.domain}/carimage`;
|
|
11
11
|
this.mappingSpreadsheetTitle = 'mobile_yescar';
|
|
12
12
|
this.imageType = ImageType.default;
|
|
13
|
+
this.downloadImage = true;
|
|
13
14
|
this.outputFiles = [
|
|
14
15
|
`${this.pathesConfig.localConvertedDirectory}/api_export_cars__${ExportType.EVIUM}.${FileType.CSV}`,
|
|
15
16
|
`${this.pathesConfig.localConvertedDirectory}/api_export_cars_${ExportType.EVIUM}.${FileType.ZIP}`,
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
export const IMAGES_SORT_ORDER: any = {
|
|
2
|
+
ext_f_right: { order: 1 },
|
|
3
|
+
ext_f_cowl: { order: 2 },
|
|
4
|
+
ext_f_left: { order: 3 },
|
|
5
|
+
ext_l_main: { order: 4 },
|
|
6
|
+
ext_b_left: { order: 5 },
|
|
7
|
+
ext_b_main: { order: 6 },
|
|
8
|
+
ext_b_trunk: { order: 7 },
|
|
9
|
+
ext_b_right: { order: 8 },
|
|
10
|
+
ext_r_main: { order: 9 },
|
|
11
|
+
ext_f_main: { order: 10 },
|
|
12
|
+
ext_f_main_opened: { order: 11 },
|
|
13
|
+
ext_f_left_opened: { order: 12 },
|
|
14
|
+
ext_l_main_opened: { order: 13 },
|
|
15
|
+
ext_b_left_opened: { order: 14 },
|
|
16
|
+
ext_b_main_opened: { order: 15 },
|
|
17
|
+
ext_b_right_opened: { order: 16 },
|
|
18
|
+
ext_r_main_opened: { order: 17 },
|
|
19
|
+
ext_f_right_opened: { order: 18 },
|
|
20
|
+
conext_f_main: { order: 19 },
|
|
21
|
+
conext_f_left: { order: 20 },
|
|
22
|
+
conext_l_main: { order: 21 },
|
|
23
|
+
conext_b_left: { order: 22 },
|
|
24
|
+
conext_b_main: { order: 23 },
|
|
25
|
+
conext_b_right: { order: 24 },
|
|
26
|
+
conext_r_main: { order: 25 },
|
|
27
|
+
conext_f_right: { order: 26 },
|
|
28
|
+
ext_feat_engine: { order: 27 },
|
|
29
|
+
int_f_ipanel1: { order: 28 },
|
|
30
|
+
int_f_ipanel2: { order: 29 },
|
|
31
|
+
int_f_ipanel5: { order: 30 },
|
|
32
|
+
int_f_driverseats: { order: 31 },
|
|
33
|
+
int_f_assistantseats: { order: 32 },
|
|
34
|
+
int_b_rearseats: { order: 33 },
|
|
35
|
+
int_f_fandrseats: { order: 34 },
|
|
36
|
+
int_f_driverdashboard: { order: 35 },
|
|
37
|
+
int_f_doordriverside: { order: 36 },
|
|
38
|
+
int_f_stickshift: { order: 37 },
|
|
39
|
+
int_b_trunk: { order: 38 },
|
|
40
|
+
int_t_sunroof: { order: 39 },
|
|
41
|
+
int_t_panorama_sunroof: { order: 40 },
|
|
42
|
+
int_f_ipanel3: { order: 41 },
|
|
43
|
+
int_f_ipanel4: { order: 42 },
|
|
44
|
+
int_feat_seatheating: { order: 43 },
|
|
45
|
+
ext_feat_ledlights: { order: 44 },
|
|
46
|
+
ext_feat_cruisecontrol: { order: 45 },
|
|
47
|
+
int_feat_climatization: { order: 46 },
|
|
48
|
+
int_feat_bluetooth: { order: 47 },
|
|
49
|
+
ext_feat_rainsensor: { order: 48 },
|
|
50
|
+
ext_feat_rims: { order: 49 },
|
|
51
|
+
ext_feat_parkassistant: { order: 50 },
|
|
52
|
+
ext_feat_lightsensor: { order: 51 },
|
|
53
|
+
int_feat_handsfree: { order: 52 },
|
|
54
|
+
int_feat_headupdisplay: { order: 53 },
|
|
55
|
+
ext_feat_trafficsignrec: { order: 54 },
|
|
56
|
+
ext_feat_auxiliaryheating: { order: 55 },
|
|
57
|
+
ext_feat_laneassistant: { order: 56 },
|
|
58
|
+
int_feat_startstop: { order: 57 },
|
|
59
|
+
int_feat_breakassistant: { order: 58 },
|
|
60
|
+
ext_feat_nightassistant: { order: 59 },
|
|
61
|
+
dam_out_left: { order: 1000 },
|
|
62
|
+
dam_out_front: { order: 2000 },
|
|
63
|
+
dam_out_back: { order: 3000 },
|
|
64
|
+
dam_out_right: { order: 4000 },
|
|
65
|
+
dam_out_top: { order: 5000 },
|
|
66
|
+
gen_feature: { order: 6000 },
|
|
67
|
+
simple_temp: { order: 7000 },
|
|
68
|
+
simple_: { order: 8000 }
|
|
69
|
+
};
|
|
@@ -40,9 +40,9 @@ export class As24Exporter implements Exporter {
|
|
|
40
40
|
const data = chunkData[i];
|
|
41
41
|
const ownerId = get(data.car, 'metaData.ownerId');
|
|
42
42
|
|
|
43
|
-
console.time(`Transform ${data.car._id}`);
|
|
43
|
+
// console.time(`Transform ${data.car._id}`);
|
|
44
44
|
const transformedData = await as24Transformer.transform(data.car);
|
|
45
|
-
console.timeEnd(`Transform ${data.car._id}`);
|
|
45
|
+
// console.timeEnd(`Transform ${data.car._id}`);
|
|
46
46
|
const transformedDataCSV = `${convertToCSV(transformedData)}\n`;
|
|
47
47
|
|
|
48
48
|
const filename = `${this.configService.config.pathesConfig.localConvertedDirectory}/${ownerId}.${FileType.CSV}`
|
|
@@ -35,9 +35,9 @@ export class Auto24Exporter implements Exporter {
|
|
|
35
35
|
for (let i = 0; i < chunkData.length; i += 1) {
|
|
36
36
|
const data = chunk.data[i];
|
|
37
37
|
const { car, images } = data;
|
|
38
|
-
console.time(`Transform ${data.car._id}`);
|
|
38
|
+
// console.time(`Transform ${data.car._id}`);
|
|
39
39
|
await xmlBuilder.addItem(await auto24Transformer.transform(car, images), 'Deutschland');
|
|
40
|
-
console.timeEnd(`Transform ${data.car._id}`);
|
|
40
|
+
// console.timeEnd(`Transform ${data.car._id}`);
|
|
41
41
|
this.carService.counter.countCar(images);
|
|
42
42
|
}
|
|
43
43
|
});
|
|
@@ -38,9 +38,9 @@ export class AutouncleExporter implements Exporter {
|
|
|
38
38
|
for (const data of chunk.data) {
|
|
39
39
|
const ownerId = get(data.car, 'metaData.ownerId');
|
|
40
40
|
const owner = await this.carService.getOwner(ownerId);
|
|
41
|
-
console.time(`Transform ${data.car._id}`);
|
|
41
|
+
// console.time(`Transform ${data.car._id}`);
|
|
42
42
|
const car = await autouncleTransformer.transform({ owner, ...data });
|
|
43
|
-
console.timeEnd(`Transform ${data.car._id}`);
|
|
43
|
+
// console.timeEnd(`Transform ${data.car._id}`);
|
|
44
44
|
|
|
45
45
|
await autouncleJsonBuilder.addItem(car, firstCar);
|
|
46
46
|
|
|
@@ -35,9 +35,9 @@ export class FacebookShopExporter implements Exporter {
|
|
|
35
35
|
for (let i = 0; i < chunkData.length; i += 1) {
|
|
36
36
|
const data = chunk.data[i];
|
|
37
37
|
|
|
38
|
-
console.time(`Transform ${data.car._id}`);
|
|
38
|
+
// console.time(`Transform ${data.car._id}`);
|
|
39
39
|
const transformedCar = await facebookShopTransformer.transform(data);
|
|
40
|
-
console.timeEnd(`Transform ${data.car._id}`);
|
|
40
|
+
// console.timeEnd(`Transform ${data.car._id}`);
|
|
41
41
|
|
|
42
42
|
await facebookShopXmkBuilder.addItem(transformedCar);
|
|
43
43
|
this.carService.counter.countCar(data.images);
|
|
@@ -35,9 +35,9 @@ export class FbExporter implements Exporter {
|
|
|
35
35
|
for (const data of chunkData) {
|
|
36
36
|
const ownerId = get(data.car, 'metaData.ownerId', null);
|
|
37
37
|
const owner = await this.carService.getOwner(ownerId);
|
|
38
|
-
console.time(`Transform ${data.car._id}`);
|
|
38
|
+
// console.time(`Transform ${data.car._id}`);
|
|
39
39
|
const car = await fbTransformer.transform({ owner, ...data });
|
|
40
|
-
console.timeEnd(`Transform ${data.car._id}`);
|
|
40
|
+
// console.timeEnd(`Transform ${data.car._id}`);
|
|
41
41
|
|
|
42
42
|
await fbXmlBuilder.addItem(car as FbXmlItem);
|
|
43
43
|
this.carService.counter.countCar(data.images);
|
|
@@ -36,9 +36,9 @@ export class MeinstadtExporter implements Exporter {
|
|
|
36
36
|
chunk.data = chunk.data.filter(e => this.carService.filterCar(e, this.rulesService.rules));
|
|
37
37
|
|
|
38
38
|
for (const data of chunk.data) {
|
|
39
|
-
console.time(`Transform ${data.car._id}`);
|
|
39
|
+
// console.time(`Transform ${data.car._id}`);
|
|
40
40
|
const car = await meinstadtTransformer.transform(data);
|
|
41
|
-
console.timeEnd(`Transform ${data.car._id}`);
|
|
41
|
+
// console.timeEnd(`Transform ${data.car._id}`);
|
|
42
42
|
await meinstadtJsonBuilder.addItem(car as MeinstadtJsonItem, firstCar);
|
|
43
43
|
this.carService.counter.countCar(data.images);
|
|
44
44
|
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { writeFile } from 'fs/promises';
|
|
2
|
-
import {
|
|
2
|
+
import { normalize, parse } from 'path';
|
|
3
3
|
|
|
4
4
|
import { Injectable } from '@nestjs/common';
|
|
5
5
|
import { ModuleRef } from '@nestjs/core';
|
|
@@ -8,10 +8,10 @@ import { CronicleUtils } from '@bytebrand/cronicle-helper';
|
|
|
8
8
|
import { FsChunkInterface } from '@bytebrand/chunkable';
|
|
9
9
|
import { Zipper } from '@bytebrand/zipper';
|
|
10
10
|
|
|
11
|
-
import { CarService, RulesService, LoaderService, ConfigService } from '../services';
|
|
11
|
+
import { CarService, RulesService, LoaderService, ConfigService, ImagesService } from '../services';
|
|
12
12
|
import { MobileTransformer } from '../transformers/mobile.transformer';
|
|
13
|
-
import { convertToCSV } from '../utils/converter.utils';
|
|
14
13
|
import { Exporter } from '../interfaces/exporter.interface';
|
|
14
|
+
import { convertToCSV } from '../utils/converter.utils';
|
|
15
15
|
import { FileType } from '../enums';
|
|
16
16
|
|
|
17
17
|
@Injectable()
|
|
@@ -21,6 +21,7 @@ export class MobileExporter implements Exporter {
|
|
|
21
21
|
private configService: ConfigService,
|
|
22
22
|
private rulesService: RulesService,
|
|
23
23
|
private loaderService: LoaderService,
|
|
24
|
+
private imageService: ImagesService,
|
|
24
25
|
private moduleRef: ModuleRef,
|
|
25
26
|
) { }
|
|
26
27
|
|
|
@@ -30,6 +31,7 @@ export class MobileExporter implements Exporter {
|
|
|
30
31
|
await CronicleUtils.emptyDir(this.configService.config.pathesConfig.localConvertedDirectory);
|
|
31
32
|
|
|
32
33
|
const mobileTransformer = await this.moduleRef.create(MobileTransformer);
|
|
34
|
+
const imagesPath = [];
|
|
33
35
|
|
|
34
36
|
await this.loaderService.process(async (chunk: FsChunkInterface) => {
|
|
35
37
|
chunk.data = chunk.data.filter(e => this.carService.filterCar(e, this.rulesService.rules));
|
|
@@ -38,9 +40,12 @@ export class MobileExporter implements Exporter {
|
|
|
38
40
|
for (let i = 0; i < chunk.data.length; i += 1) {
|
|
39
41
|
const data = chunk.data[i];
|
|
40
42
|
const { car, images } = data;
|
|
41
|
-
console.time(`Transform ${data.car._id}`);
|
|
42
43
|
const transformedData = await mobileTransformer.transform(car, images);
|
|
43
|
-
|
|
44
|
+
|
|
45
|
+
if (this.configService.config.downloadImage) {
|
|
46
|
+
const urls = await this.imageService.getImages(car, images, this.configService.config.imageType, true);
|
|
47
|
+
imagesPath.push(...await this.imageService.downloadInParallel(car.metaData.vehicleId, urls, this.configService.config, 10));
|
|
48
|
+
}
|
|
44
49
|
|
|
45
50
|
await writeFile(fileName, Buffer.from(`${convertToCSV(transformedData)}\n`), { flag: 'a' });
|
|
46
51
|
|
|
@@ -50,9 +55,9 @@ export class MobileExporter implements Exporter {
|
|
|
50
55
|
});
|
|
51
56
|
|
|
52
57
|
const fileZIP = normalize(`${parse(fileName).dir}/${parse(fileName).name}.${FileType.ZIP}`);
|
|
53
|
-
|
|
58
|
+
|
|
54
59
|
if (await CronicleUtils.exists(fileName)) {
|
|
55
|
-
await Zipper.zip({ fileList: [fileName], dest: fileZIP });
|
|
60
|
+
await Zipper.zip({ fileList: [fileName, ...imagesPath], dest: fileZIP });
|
|
56
61
|
}
|
|
57
62
|
console.log(this.carService.counter.getCountedCars());
|
|
58
63
|
}
|
|
@@ -40,9 +40,9 @@ export class ModixExporter implements Exporter {
|
|
|
40
40
|
for (let i = 0; i < chunk.data.length; i += 1) {
|
|
41
41
|
const data = chunk.data[i];
|
|
42
42
|
|
|
43
|
-
console.time(`Transform ${data.car._id}`);
|
|
43
|
+
// console.time(`Transform ${data.car._id}`);
|
|
44
44
|
const transformedCar = await modixTransformer.transform(data, mappingBuilder.getMappingRules({ reversTransforming: true }));
|
|
45
|
-
console.timeEnd(`Transform ${data.car._id}`);
|
|
45
|
+
// console.timeEnd(`Transform ${data.car._id}`);
|
|
46
46
|
await modixXmlBuilder.addItem(transformedCar);
|
|
47
47
|
|
|
48
48
|
this.carService.counter.countCar(data.images);
|
|
@@ -33,9 +33,9 @@ export class OoyyoExporter implements Exporter {
|
|
|
33
33
|
|
|
34
34
|
for (let i = 0; i < chunkData.length; i += 1) {
|
|
35
35
|
const { car, images } = chunkData[i];
|
|
36
|
-
console.time(`Transform ${car._id}`);
|
|
36
|
+
// console.time(`Transform ${car._id}`);
|
|
37
37
|
await ooyyoXmlBuilder.addItem(await ooyyoTransformer.transform(car, images));
|
|
38
|
-
console.timeEnd(`Transform ${car._id}`);
|
|
38
|
+
// console.timeEnd(`Transform ${car._id}`);
|
|
39
39
|
this.carService.counter.countCar(images);
|
|
40
40
|
}
|
|
41
41
|
});
|
|
@@ -36,9 +36,9 @@ export class RubrikkExporter implements Exporter {
|
|
|
36
36
|
for (let i = 0; i < chunkData.length; i += 1) {
|
|
37
37
|
const data = chunkData[i];
|
|
38
38
|
|
|
39
|
-
console.time(`Transform ${data.car._id}`);
|
|
39
|
+
// console.time(`Transform ${data.car._id}`);
|
|
40
40
|
const transformedCar = await rubrikkTransformer.transform(data);
|
|
41
|
-
console.timeEnd(`Transform ${data.car._id}`);
|
|
41
|
+
// console.timeEnd(`Transform ${data.car._id}`);
|
|
42
42
|
|
|
43
43
|
await rubrikkXmlBuilder.addItem(transformedCar);
|
|
44
44
|
this.carService.counter.countCar(data.images);
|
|
@@ -35,9 +35,9 @@ export class TrovitExporter implements Exporter {
|
|
|
35
35
|
for (let i = 0; i < chunk.data.length; i += 1) {
|
|
36
36
|
const data = chunk.data[i];
|
|
37
37
|
|
|
38
|
-
console.time(`Transform ${data.car._id}`);
|
|
38
|
+
// console.time(`Transform ${data.car._id}`);
|
|
39
39
|
const transformedCar = await trovitTransformer.transform(data);
|
|
40
|
-
console.timeEnd(`Transform ${data.car._id}`);
|
|
40
|
+
// console.timeEnd(`Transform ${data.car._id}`);
|
|
41
41
|
|
|
42
42
|
await trovitXmlBuilder.addItem(transformedCar);
|
|
43
43
|
this.carService.counter.countCar(data.images);
|
|
@@ -139,7 +139,7 @@ export class ModixXmlBuilder extends XMLBuilderBase {
|
|
|
139
139
|
let item = `\n<media>\n<images url="${imageData.url}/">\n`;
|
|
140
140
|
|
|
141
141
|
imageData.imageList.forEach(image => {
|
|
142
|
-
item += `<image name="${image.atributes?.name}" />\n`;
|
|
142
|
+
item += `<image name="${image.atributes?.name}" position="${image.atributes?.position}" />\n`;
|
|
143
143
|
});
|
|
144
144
|
|
|
145
145
|
item += '\n</images>\n</media>';
|
|
@@ -1,4 +1,6 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { createWriteStream } from 'fs';
|
|
2
|
+
import { extname } from 'path';
|
|
3
|
+
import axios from 'axios';
|
|
2
4
|
|
|
3
5
|
import { Injectable } from '@nestjs/common';
|
|
4
6
|
|
|
@@ -7,45 +9,99 @@ import { ImboImageResponseInterface } from '@bytebrand/chunkable';
|
|
|
7
9
|
import { DataSchemes } from '@bytebrand/car-schema';
|
|
8
10
|
|
|
9
11
|
import { FB_MAX_IMAGE_COUNT_FOR_DEAFULT } from '../constants/fb.constants';
|
|
10
|
-
import { isNotEmptyArray } from '../utils';
|
|
11
12
|
import { ConfigService } from './config.service';
|
|
12
13
|
import { UserService } from './user.service';
|
|
14
|
+
import { isNotEmptyArray } from '../utils';
|
|
15
|
+
import { Config } from '../configs/config';
|
|
13
16
|
import { ImageType } from '../enums';
|
|
14
17
|
|
|
15
18
|
@Injectable()
|
|
16
19
|
export class ImagesService {
|
|
20
|
+
private readonly IMAGES_SORT_ORDER: { [imageCode: string]: number } = {
|
|
21
|
+
ext_f_right: 1,
|
|
22
|
+
ext_f_cowl: 2,
|
|
23
|
+
ext_f_left: 3,
|
|
24
|
+
ext_l_main: 4,
|
|
25
|
+
ext_b_left: 5,
|
|
26
|
+
ext_b_main: 6,
|
|
27
|
+
ext_b_trunk: 7,
|
|
28
|
+
ext_b_right: 8,
|
|
29
|
+
ext_r_main: 9,
|
|
30
|
+
ext_f_main: 10,
|
|
31
|
+
ext_f_main_opened: 11,
|
|
32
|
+
ext_f_left_opened: 12,
|
|
33
|
+
ext_l_main_opened: 13,
|
|
34
|
+
ext_b_left_opened: 14,
|
|
35
|
+
ext_b_main_opened: 15,
|
|
36
|
+
ext_b_right_opened: 16,
|
|
37
|
+
ext_r_main_opened: 17,
|
|
38
|
+
ext_f_right_opened: 18,
|
|
39
|
+
conext_f_main: 19,
|
|
40
|
+
conext_f_left: 20,
|
|
41
|
+
conext_l_main: 21,
|
|
42
|
+
conext_b_left: 22,
|
|
43
|
+
conext_b_main: 23,
|
|
44
|
+
conext_b_right: 24,
|
|
45
|
+
conext_r_main: 25,
|
|
46
|
+
conext_f_right: 26,
|
|
47
|
+
ext_feat_engine: 27,
|
|
48
|
+
int_f_ipanel1: 28,
|
|
49
|
+
int_f_ipanel2: 29,
|
|
50
|
+
int_f_ipanel5: 30,
|
|
51
|
+
int_f_driverseats: 31,
|
|
52
|
+
int_f_assistantseats: 32,
|
|
53
|
+
int_b_rearseats: 33,
|
|
54
|
+
int_f_fandrseats: 34,
|
|
55
|
+
int_f_driverdashboard: 35,
|
|
56
|
+
int_f_doordriverside: 36,
|
|
57
|
+
int_f_stickshift: 37,
|
|
58
|
+
int_b_trunk: 38,
|
|
59
|
+
int_t_sunroof: 39,
|
|
60
|
+
int_t_panorama_sunroof: 40,
|
|
61
|
+
int_f_ipanel3: 41,
|
|
62
|
+
int_f_ipanel4: 42,
|
|
63
|
+
int_feat_seatheating: 43,
|
|
64
|
+
ext_feat_ledlights: 44,
|
|
65
|
+
ext_feat_cruisecontrol: 45,
|
|
66
|
+
int_feat_climatization: 46,
|
|
67
|
+
int_feat_bluetooth: 47,
|
|
68
|
+
ext_feat_rainsensor: 48,
|
|
69
|
+
ext_feat_rims: 49,
|
|
70
|
+
ext_feat_parkassistant: 50,
|
|
71
|
+
ext_feat_lightsensor: 51,
|
|
72
|
+
int_feat_handsfree: 52,
|
|
73
|
+
int_feat_headupdisplay: 53,
|
|
74
|
+
ext_feat_trafficsignrec: 54,
|
|
75
|
+
ext_feat_auxiliaryheating: 55,
|
|
76
|
+
ext_feat_laneassistant: 56,
|
|
77
|
+
int_feat_startstop: 57,
|
|
78
|
+
int_feat_breakassistant: 58,
|
|
79
|
+
ext_feat_nightassistant: 59,
|
|
80
|
+
};
|
|
81
|
+
|
|
17
82
|
private readonly DEFAULT_IMAGES = [
|
|
18
83
|
'https://images.auto.de/carimage/noimage/7pNvWELuWhZX/large-cached.jpeg',
|
|
19
84
|
'https://images.auto.de/carimage/noimage/GvyXmrpaf1D6/large-cached.jpeg',
|
|
20
85
|
'https://images.auto.de/carimage/noimage/pFyfBsqBHEso/large-cached.jpeg',
|
|
21
86
|
];
|
|
22
87
|
|
|
23
|
-
constructor(private readonly configService: ConfigService, private readonly userService: UserService) {}
|
|
88
|
+
constructor(private readonly configService: ConfigService, private readonly userService: UserService) { }
|
|
24
89
|
|
|
25
|
-
async getImages(
|
|
26
|
-
car: DataSchemes.Car,
|
|
27
|
-
images: ImboImageResponseInterface[],
|
|
28
|
-
imageType: ImageType,
|
|
29
|
-
ignoreDefImages: boolean,
|
|
30
|
-
): Promise<Array<string>> {
|
|
90
|
+
async getImages(car: DataSchemes.Car, images: ImboImageResponseInterface[], imageType: ImageType, ignoreDefImages: boolean): Promise<Array<string>> {
|
|
31
91
|
if (imageType === ImageType.default) {
|
|
32
|
-
return this.getDefaultImages(car, images, ignoreDefImages);
|
|
92
|
+
return this.getDefaultImages(car._id, images, ignoreDefImages);
|
|
33
93
|
} else {
|
|
34
|
-
return await this.getMarketingImages(car, images, ignoreDefImages);
|
|
94
|
+
return await this.getMarketingImages(car._id, images, ignoreDefImages);
|
|
35
95
|
}
|
|
36
96
|
}
|
|
37
97
|
|
|
38
|
-
getDefaultImages(
|
|
39
|
-
car: DataSchemes.Car,
|
|
40
|
-
images: ImboImageResponseInterface[],
|
|
41
|
-
ignoreDefImages: boolean,
|
|
42
|
-
): Array<string> {
|
|
98
|
+
getDefaultImages(carId: string, images: ImboImageResponseInterface[], ignoreDefImages: boolean): Array<string> {
|
|
43
99
|
if (!isNotEmptyArray(images)) {
|
|
44
100
|
return this.DEFAULT_IMAGES;
|
|
45
101
|
}
|
|
46
102
|
|
|
47
|
-
const sortedImages = this.sortImages(images
|
|
48
|
-
const transformedImages = this.generateImageLinks(sortedImages,
|
|
103
|
+
const sortedImages = this.sortImages(images);
|
|
104
|
+
const transformedImages = this.generateImageLinks(sortedImages, carId);
|
|
49
105
|
|
|
50
106
|
if (!ignoreDefImages && transformedImages.length < 17) {
|
|
51
107
|
transformedImages.push(...this.DEFAULT_IMAGES);
|
|
@@ -60,14 +116,10 @@ export class ImagesService {
|
|
|
60
116
|
});
|
|
61
117
|
}
|
|
62
118
|
|
|
63
|
-
async getMarketingImages(
|
|
64
|
-
car: DataSchemes.Car,
|
|
65
|
-
images: ImboImageResponseInterface[],
|
|
66
|
-
ignoreDefImages: boolean,
|
|
67
|
-
): Promise<string[]> {
|
|
119
|
+
async getMarketingImages(carId: string, images: ImboImageResponseInterface[], ignoreDefImages: boolean): Promise<string[]> {
|
|
68
120
|
const imageUrl = this.configService.config.marketingImageUrl;
|
|
69
121
|
|
|
70
|
-
const transformedImages = this.transformImagesArrayIntoObject(images);
|
|
122
|
+
const transformedImages = this.transformImagesArrayIntoObject(this.sortImages(images));
|
|
71
123
|
const config = await this.userService.getMarketingImageConfig(this.configService.config.marketingImageConfigId);
|
|
72
124
|
const marketingImageConfigs = config.marketingImages.map(i => ({ marketingImageCodes: i.imageCodes }));
|
|
73
125
|
|
|
@@ -78,7 +130,7 @@ export class ImagesService {
|
|
|
78
130
|
const imageCode = marketingImage.marketingImageCodes[i];
|
|
79
131
|
const imageId = transformedImages[imageCode];
|
|
80
132
|
if (imageId) {
|
|
81
|
-
imageUrls.push(this.getImagePathWithExtension(imageUrl,
|
|
133
|
+
imageUrls.push(this.getImagePathWithExtension(imageUrl, carId, imageId, config));
|
|
82
134
|
break;
|
|
83
135
|
}
|
|
84
136
|
}
|
|
@@ -91,54 +143,62 @@ export class ImagesService {
|
|
|
91
143
|
return imageUrls;
|
|
92
144
|
}
|
|
93
145
|
|
|
94
|
-
private transformImagesArrayIntoObject(images: ImboImageResponseInterface[]) {
|
|
95
|
-
|
|
96
|
-
images.forEach(image => {
|
|
146
|
+
private transformImagesArrayIntoObject(images: ImboImageResponseInterface[]): { [imageCode: string]: string } {
|
|
147
|
+
return images.reduce((result, image) => {
|
|
97
148
|
const imageCode = image?.imageCodePredicted?.split('-')[0];
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
149
|
+
if (imageCode) {
|
|
150
|
+
result[imageCode] = image.imageIdentifier;
|
|
151
|
+
}
|
|
152
|
+
return result;
|
|
153
|
+
}, {});
|
|
101
154
|
}
|
|
102
155
|
|
|
103
156
|
protected getChannelPath(imageUrl: string, carId: string): string {
|
|
104
157
|
return `${imageUrl}/${[carId[0], carId[1], carId[2], carId].join('/')}`;
|
|
105
158
|
}
|
|
106
159
|
|
|
107
|
-
protected getImagePathWithExtension(
|
|
108
|
-
imageUrl: string,
|
|
109
|
-
carId: string,
|
|
110
|
-
imageId: string,
|
|
111
|
-
config: MarketingImageConfigsInterface,
|
|
112
|
-
): string {
|
|
160
|
+
protected getImagePathWithExtension(imageUrl: string, carId: string, imageId: string, config: MarketingImageConfigsInterface): string {
|
|
113
161
|
const { channelCode, imageExtension } = config;
|
|
114
162
|
return `${this.getChannelPath(imageUrl, carId)}/${channelCode.replace(/ /g, '-')}-${imageId}.${imageExtension}`;
|
|
115
163
|
}
|
|
116
164
|
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
images[2] = tmp;
|
|
138
|
-
}
|
|
139
|
-
});
|
|
140
|
-
return images.filter(i => i); // sometimes thirdImageId exists but image is not
|
|
165
|
+
sortImages(images: ImboImageResponseInterface[]): ImboImageResponseInterface[] {
|
|
166
|
+
return images.sort((a, b) => {
|
|
167
|
+
const imageCodePredictedA = a.imageCodePredicted.includes('-') ? a.imageCodePredicted.split('-')[0] : a.imageCodePredicted;
|
|
168
|
+
const imageCodePredictedB = b.imageCodePredicted.includes('-') ? b.imageCodePredicted.split('-')[0] : b.imageCodePredicted;
|
|
169
|
+
const orderA = this.IMAGES_SORT_ORDER[imageCodePredictedA] || 100;
|
|
170
|
+
const orderB = this.IMAGES_SORT_ORDER[imageCodePredictedB] || 100;
|
|
171
|
+
|
|
172
|
+
return orderA - orderB;
|
|
173
|
+
});
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
async downloadInParallel(vehicleId: string, urls: string[], config: Config, concurrency = 1): Promise<string[]> {
|
|
177
|
+
for (let i = 0; i < urls.length; i += concurrency) {
|
|
178
|
+
const downloadPromises = urls.map(
|
|
179
|
+
(url: string, j: number) => this.download(
|
|
180
|
+
url,
|
|
181
|
+
`${config.pathesConfig.localConvertedDirectory}/${vehicleId}_${(i + 1).toLocaleString('en-US', { minimumIntegerDigits: 2 })}.jpeg`
|
|
182
|
+
)
|
|
183
|
+
);
|
|
184
|
+
return Promise.all(downloadPromises);
|
|
141
185
|
}
|
|
142
|
-
return
|
|
186
|
+
return [];
|
|
143
187
|
}
|
|
188
|
+
|
|
189
|
+
private async download(url: string, imagePath: string) {
|
|
190
|
+
return new Promise<string>(async resolve => {
|
|
191
|
+
try {
|
|
192
|
+
const response = await axios.get(url, { responseType: 'stream' });
|
|
193
|
+
response.data.pipe(
|
|
194
|
+
createWriteStream(imagePath)
|
|
195
|
+
.on('finish', () => { console.log(`Downloaded: ${url}`); resolve(imagePath); })
|
|
196
|
+
.on('error', (err) => { console.error(`Failed to download: ${url}`); resolve(null); })
|
|
197
|
+
);
|
|
198
|
+
} catch (error) {
|
|
199
|
+
console.error(`Failed to download: ${url}`);
|
|
200
|
+
resolve(null);
|
|
201
|
+
}
|
|
202
|
+
});
|
|
203
|
+
};
|
|
144
204
|
}
|
|
@@ -71,12 +71,12 @@ export class FbTransformer {
|
|
|
71
71
|
const autodeId = toString(get(car, '_id', ''));
|
|
72
72
|
const year = this.getYear(car);
|
|
73
73
|
const dealerInfo = this.getDealerItems(car);
|
|
74
|
-
console.time(`Address ${car._id}`);
|
|
74
|
+
// console.time(`Address ${car._id}`);
|
|
75
75
|
const address = await this.getAddress(car, owner);
|
|
76
|
-
console.timeEnd(`Address ${car._id}`);
|
|
77
|
-
console.time(`Images ${car._id}`);
|
|
76
|
+
// console.timeEnd(`Address ${car._id}`);
|
|
77
|
+
// console.time(`Images ${car._id}`);
|
|
78
78
|
const image_links = await this.imagesService.getImages(car, images, ImageType.marketing, false);
|
|
79
|
-
console.timeEnd(`Images ${car._id}`);
|
|
79
|
+
// console.timeEnd(`Images ${car._id}`);
|
|
80
80
|
const transformedImages = image_links.map(el => {
|
|
81
81
|
return { url: el };
|
|
82
82
|
});
|
|
@@ -12,6 +12,7 @@ import { MappingRules } from '../interfaces/mapping-rules.interface';
|
|
|
12
12
|
import { isNotEmptyArray } from '../utils';
|
|
13
13
|
import { ConfigService } from '../services';
|
|
14
14
|
import { ExportType } from '../enums';
|
|
15
|
+
import axios from "axios";
|
|
15
16
|
|
|
16
17
|
const vinGenerator = require('vin-generator');
|
|
17
18
|
|
|
@@ -24,7 +25,7 @@ export class MobileTransformer {
|
|
|
24
25
|
private userService: UserService,
|
|
25
26
|
private configService: ConfigService,
|
|
26
27
|
private imagesService: ImagesService,
|
|
27
|
-
) {}
|
|
28
|
+
) { }
|
|
28
29
|
|
|
29
30
|
async transform(carData: DataSchemes.Car, images: ImboImageResponseInterface[]): Promise<unknown> {
|
|
30
31
|
this.currentCar = carData;
|
|
@@ -364,6 +365,9 @@ export class MobileTransformer {
|
|
|
364
365
|
false,
|
|
365
366
|
);
|
|
366
367
|
let imagesAsALine;
|
|
368
|
+
// console.log(' --> transformedImages', transformedImages)
|
|
369
|
+
if (this.configService.config.campaign == ExportType.EVIUM) return null;
|
|
370
|
+
|
|
367
371
|
if (isNotEmptyArray(transformedImages)) {
|
|
368
372
|
imagesAsALine = transformedImages.map((el: string) => el).join(';');
|
|
369
373
|
}
|
|
@@ -11,7 +11,7 @@ import { DataSchemes } from '@bytebrand/car-schema';
|
|
|
11
11
|
|
|
12
12
|
import { imageItem, IAtributeWrapItem, ModixXmlItem } from '../interfaces';
|
|
13
13
|
import { isNotEmptyArray, isValidVin, xmlStringReplacer } from '../utils';
|
|
14
|
-
import { ConfigService } from '../services';
|
|
14
|
+
import { ConfigService, ImagesService } from '../services';
|
|
15
15
|
import {
|
|
16
16
|
BodyColorList,
|
|
17
17
|
VehicleClassList,
|
|
@@ -25,7 +25,7 @@ import {
|
|
|
25
25
|
|
|
26
26
|
@Injectable()
|
|
27
27
|
export class ModixTransformer {
|
|
28
|
-
constructor(private readonly configService: ConfigService) {}
|
|
28
|
+
constructor(private readonly configService: ConfigService, private readonly imagesService: ImagesService) {}
|
|
29
29
|
|
|
30
30
|
async transform(
|
|
31
31
|
chunkData: { car: DataSchemes.Car; images: ImboImageResponseInterface[] },
|
|
@@ -205,7 +205,7 @@ export class ModixTransformer {
|
|
|
205
205
|
const imageList: IAtributeWrapItem[] = [];
|
|
206
206
|
|
|
207
207
|
if (isNotEmptyArray(images)) {
|
|
208
|
-
images.forEach((image: ImboImageResponseInterface, index) => {
|
|
208
|
+
this.imagesService.sortImages(images).forEach((image: ImboImageResponseInterface, index) => {
|
|
209
209
|
imageList.push({ value: null, atributes: { name: `${image.imageIdentifier}/large`, position: index + 1 } });
|
|
210
210
|
});
|
|
211
211
|
}
|