@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.
Files changed (64) hide show
  1. package/dist/configs/config.d.ts +1 -0
  2. package/dist/configs/config.js +1 -0
  3. package/dist/configs/config.js.map +1 -1
  4. package/dist/configs/evium.config.js +1 -0
  5. package/dist/configs/evium.config.js.map +1 -1
  6. package/dist/constants/images.d.ts +1 -0
  7. package/dist/constants/images.js +73 -0
  8. package/dist/constants/images.js.map +1 -0
  9. package/dist/exporters/as24.exporter.js +0 -2
  10. package/dist/exporters/as24.exporter.js.map +1 -1
  11. package/dist/exporters/auto24.exporter.js +0 -2
  12. package/dist/exporters/auto24.exporter.js.map +1 -1
  13. package/dist/exporters/autouncle.exporter.js +0 -2
  14. package/dist/exporters/autouncle.exporter.js.map +1 -1
  15. package/dist/exporters/facebook-shop.exporter.js +0 -2
  16. package/dist/exporters/facebook-shop.exporter.js.map +1 -1
  17. package/dist/exporters/fb.exporter.js +0 -2
  18. package/dist/exporters/fb.exporter.js.map +1 -1
  19. package/dist/exporters/meinstadt.exporter.js +0 -2
  20. package/dist/exporters/meinstadt.exporter.js.map +1 -1
  21. package/dist/exporters/mobile.exporter.d.ts +3 -2
  22. package/dist/exporters/mobile.exporter.js +10 -5
  23. package/dist/exporters/mobile.exporter.js.map +1 -1
  24. package/dist/exporters/modix.exporter.js +0 -2
  25. package/dist/exporters/modix.exporter.js.map +1 -1
  26. package/dist/exporters/ooyyo.exporter.js +0 -2
  27. package/dist/exporters/ooyyo.exporter.js.map +1 -1
  28. package/dist/exporters/rubrikk.exporter.js +0 -2
  29. package/dist/exporters/rubrikk.exporter.js.map +1 -1
  30. package/dist/exporters/trovit.exporter.js +0 -2
  31. package/dist/exporters/trovit.exporter.js.map +1 -1
  32. package/dist/formats/modix-xml.builder.js +1 -1
  33. package/dist/formats/modix-xml.builder.js.map +1 -1
  34. package/dist/services/images.service.d.ts +7 -3
  35. package/dist/services/images.service.js +107 -39
  36. package/dist/services/images.service.js.map +1 -1
  37. package/dist/transformers/fb.transformer.js +0 -4
  38. package/dist/transformers/fb.transformer.js.map +1 -1
  39. package/dist/transformers/mobile.transformer.js +2 -0
  40. package/dist/transformers/mobile.transformer.js.map +1 -1
  41. package/dist/transformers/modix.transformer.d.ts +3 -2
  42. package/dist/transformers/modix.transformer.js +5 -3
  43. package/dist/transformers/modix.transformer.js.map +1 -1
  44. package/dist/tsconfig.build.tsbuildinfo +1 -1
  45. package/package.json +3 -2
  46. package/src/configs/config.ts +1 -0
  47. package/src/configs/evium.config.ts +1 -0
  48. package/src/constants/images.ts +69 -0
  49. package/src/exporters/as24.exporter.ts +2 -2
  50. package/src/exporters/auto24.exporter.ts +2 -2
  51. package/src/exporters/autouncle.exporter.ts +2 -2
  52. package/src/exporters/facebook-shop.exporter.ts +2 -2
  53. package/src/exporters/fb.exporter.ts +2 -2
  54. package/src/exporters/meinstadt.exporter.ts +2 -2
  55. package/src/exporters/mobile.exporter.ts +12 -7
  56. package/src/exporters/modix.exporter.ts +2 -2
  57. package/src/exporters/ooyyo.exporter.ts +2 -2
  58. package/src/exporters/rubrikk.exporter.ts +2 -2
  59. package/src/exporters/trovit.exporter.ts +2 -2
  60. package/src/formats/modix-xml.builder.ts +1 -1
  61. package/src/services/images.service.ts +122 -62
  62. package/src/transformers/fb.transformer.ts +4 -4
  63. package/src/transformers/mobile.transformer.ts +5 -1
  64. 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.19.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": "7671c694fd83f4290ad8fa0488175cf9ef79797b"
65
+ "gitHead": "3ef72636ca23cee78df87f16e66089e1a532b053"
65
66
  }
@@ -12,6 +12,7 @@ export class Config {
12
12
  baseImageUrl: string;
13
13
  marketingImageUrl: string;
14
14
  imageType: ImageType;
15
+ downloadImage: boolean;
15
16
  isNewCar: boolean;
16
17
  isCriteo: boolean;
17
18
  exportType: ExportType;
@@ -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 { basename, normalize, parse } from 'path';
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
- console.timeEnd(`Transform ${data.car._id}`);
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
- console.log(fileZIP);
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 { get } from 'lodash';
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, car);
48
- const transformedImages = this.generateImageLinks(sortedImages, get(car, '_id'));
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, get(car, '_id', ''), imageId, config));
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
- const result: { [imageCode: string]: string } = {};
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
- result[imageCode] = image.imageIdentifier;
99
- });
100
- return result;
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
- private sortImages(images: ImboImageResponseInterface[], car: DataSchemes.Car): ImboImageResponseInterface[] {
118
- const firstImageId = get(car, 'metaData.mainImageId', null);
119
- const secondImageId = get(car, 'metaData.secImageId', null);
120
- const thirdImageId = get(car, 'metaData.thirdImageId', null);
121
-
122
- if (firstImageId) {
123
- images.forEach((image, index) => {
124
- if (firstImageId === image?.imageIdentifier) {
125
- const tmp = images[index];
126
- images[index] = images[0];
127
- images[0] = tmp;
128
- }
129
- if (secondImageId === image?.imageIdentifier) {
130
- const tmp = images[index];
131
- images[index] = images[1];
132
- images[1] = tmp;
133
- }
134
- if (thirdImageId === image?.imageIdentifier) {
135
- const tmp = images[index];
136
- images[index] = images[2];
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 images;
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
  }