|
@@ -7,7 +7,6 @@ import {
|
|
|
} from '@nestjs/common';
|
|
} from '@nestjs/common';
|
|
|
import type { MultipartFile } from '@fastify/multipart';
|
|
import type { MultipartFile } from '@fastify/multipart';
|
|
|
import { MongoPrismaService } from '@box/db/prisma/mongo-prisma.service';
|
|
import { MongoPrismaService } from '@box/db/prisma/mongo-prisma.service';
|
|
|
-import ExcelJS from 'exceljs';
|
|
|
|
|
import * as XLSX from 'xlsx';
|
|
import * as XLSX from 'xlsx';
|
|
|
import { CacheSyncService } from '../../../cache-sync/cache-sync.service';
|
|
import { CacheSyncService } from '../../../cache-sync/cache-sync.service';
|
|
|
import { CacheEntityType } from '../../../cache-sync/cache-sync.types';
|
|
import { CacheEntityType } from '../../../cache-sync/cache-sync.types';
|
|
@@ -20,6 +19,7 @@ import {
|
|
|
UpdateVideoMediaStatusDto,
|
|
UpdateVideoMediaStatusDto,
|
|
|
BatchUpdateVideoMediaStatusDto,
|
|
BatchUpdateVideoMediaStatusDto,
|
|
|
} from './video-media.dto';
|
|
} from './video-media.dto';
|
|
|
|
|
+import ExcelJS from 'exceljs';
|
|
|
import { MEDIA_STORAGE_STRATEGY } from '../../../shared/tokens';
|
|
import { MEDIA_STORAGE_STRATEGY } from '../../../shared/tokens';
|
|
|
|
|
|
|
|
type MongoAggregateResult = {
|
|
type MongoAggregateResult = {
|
|
@@ -302,26 +302,38 @@ export class VideoMediaService {
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
private async readUploadBuffer(file: any): Promise<Buffer> {
|
|
private async readUploadBuffer(file: any): Promise<Buffer> {
|
|
|
- const toBuffer = (value: unknown): Buffer | undefined => {
|
|
|
|
|
- if (!value) return undefined;
|
|
|
|
|
- if (Buffer.isBuffer(value)) return value;
|
|
|
|
|
- if (value instanceof Uint8Array) return Buffer.from(value);
|
|
|
|
|
- if (Array.isArray(value)) return Buffer.from(value);
|
|
|
|
|
- if (typeof value === 'string') return Buffer.from(value, 'binary');
|
|
|
|
|
- return undefined;
|
|
|
|
|
- };
|
|
|
|
|
|
|
+ if (Buffer.isBuffer(file)) return file;
|
|
|
|
|
+ if (file instanceof Uint8Array) return Buffer.from(file);
|
|
|
|
|
+ if (Array.isArray(file)) return Buffer.from(file);
|
|
|
|
|
+ if (typeof file === 'string') return Buffer.from(file, 'binary');
|
|
|
|
|
+ if (file instanceof ArrayBuffer) {
|
|
|
|
|
+ return Buffer.from(new Uint8Array(file));
|
|
|
|
|
+ }
|
|
|
|
|
+ if (ArrayBuffer.isView(file)) {
|
|
|
|
|
+ return Buffer.from(file.buffer, file.byteOffset, file.byteLength);
|
|
|
|
|
+ }
|
|
|
|
|
|
|
|
if (typeof file?.toBuffer === 'function') {
|
|
if (typeof file?.toBuffer === 'function') {
|
|
|
- const raw = await file.toBuffer();
|
|
|
|
|
- const buffer = toBuffer(raw);
|
|
|
|
|
- if (buffer) return buffer;
|
|
|
|
|
|
|
+ return await file.toBuffer();
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- const direct = toBuffer(file);
|
|
|
|
|
- if (direct) return direct;
|
|
|
|
|
|
|
+ if (file?.value && Buffer.isBuffer(file.value)) {
|
|
|
|
|
+ return file.value;
|
|
|
|
|
+ }
|
|
|
|
|
|
|
|
- const fallback = toBuffer(file?.buffer ?? file?.value ?? file?.data);
|
|
|
|
|
- if (fallback) return fallback;
|
|
|
|
|
|
|
+ if (file?.value && file.value instanceof Uint8Array) {
|
|
|
|
|
+ return Buffer.from(file.value);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ if (file?.value && Array.isArray(file.value)) {
|
|
|
|
|
+ return Buffer.from(file.value);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ const value = file?.value ?? file?.buffer ?? file?.data;
|
|
|
|
|
+ if (Buffer.isBuffer(value)) return value;
|
|
|
|
|
+ if (value instanceof Uint8Array) return Buffer.from(value);
|
|
|
|
|
+ if (Array.isArray(value)) return Buffer.from(value);
|
|
|
|
|
+ if (typeof value === 'string') return Buffer.from(value, 'binary');
|
|
|
|
|
|
|
|
const stream = file?.file as NodeJS.ReadableStream | undefined;
|
|
const stream = file?.file as NodeJS.ReadableStream | undefined;
|
|
|
if (!stream) {
|
|
if (!stream) {
|
|
@@ -346,63 +358,42 @@ export class VideoMediaService {
|
|
|
);
|
|
);
|
|
|
|
|
|
|
|
const rowsAH: Array<{ idRaw: unknown; tagsRaw: unknown }> = [];
|
|
const rowsAH: Array<{ idRaw: unknown; tagsRaw: unknown }> = [];
|
|
|
-
|
|
|
|
|
- try {
|
|
|
|
|
- const workbook = XLSX.read(buffer, { type: 'buffer' });
|
|
|
|
|
- const sheetName = workbook.SheetNames[0];
|
|
|
|
|
- const sheet = sheetName ? workbook.Sheets[sheetName] : undefined;
|
|
|
|
|
-
|
|
|
|
|
- if (!sheet) {
|
|
|
|
|
- throw new BadRequestException('No sheet found in Excel file');
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- const rangeRef = sheet['!ref'] ?? 'A1:A1';
|
|
|
|
|
- const range = XLSX.utils.decode_range(rangeRef);
|
|
|
|
|
-
|
|
|
|
|
- for (let r = range.s.r; r <= range.e.r; r += 1) {
|
|
|
|
|
- const idCell = sheet[XLSX.utils.encode_cell({ r, c: 0 })];
|
|
|
|
|
- const tagsCell = sheet[XLSX.utils.encode_cell({ r, c: 7 })];
|
|
|
|
|
- rowsAH.push({
|
|
|
|
|
- idRaw: idCell?.v ?? '',
|
|
|
|
|
- tagsRaw: tagsCell?.v ?? '',
|
|
|
|
|
- });
|
|
|
|
|
- }
|
|
|
|
|
- } catch (error) {
|
|
|
|
|
- const cellToString = (value: unknown): string => {
|
|
|
|
|
- if (value == null) return '';
|
|
|
|
|
- if (typeof value === 'string') return value;
|
|
|
|
|
- if (typeof value === 'number' || typeof value === 'boolean') {
|
|
|
|
|
- return String(value);
|
|
|
|
|
- }
|
|
|
|
|
- if (typeof value === 'object') {
|
|
|
|
|
- const anyValue = value as any;
|
|
|
|
|
- if (typeof anyValue.text === 'string') return anyValue.text;
|
|
|
|
|
- if (Array.isArray(anyValue.richText)) {
|
|
|
|
|
- return anyValue.richText.map((t: any) => t?.text ?? '').join('');
|
|
|
|
|
- }
|
|
|
|
|
- if (typeof anyValue.result !== 'undefined') {
|
|
|
|
|
- return String(anyValue.result);
|
|
|
|
|
|
|
+ const csvText = buffer.toString('utf8');
|
|
|
|
|
+ const lines = csvText.split(/\r?\n/);
|
|
|
|
|
+ const parseCsvLine = (line: string): string[] => {
|
|
|
|
|
+ const cells: string[] = [];
|
|
|
|
|
+ let current = '';
|
|
|
|
|
+ let inQuotes = false;
|
|
|
|
|
+ for (let i = 0; i < line.length; i += 1) {
|
|
|
|
|
+ const ch = line[i];
|
|
|
|
|
+ if (ch === '"') {
|
|
|
|
|
+ if (inQuotes && line[i + 1] === '"') {
|
|
|
|
|
+ current += '"';
|
|
|
|
|
+ i += 1;
|
|
|
|
|
+ } else {
|
|
|
|
|
+ inQuotes = !inQuotes;
|
|
|
}
|
|
}
|
|
|
|
|
+ continue;
|
|
|
}
|
|
}
|
|
|
- return String(value);
|
|
|
|
|
- };
|
|
|
|
|
-
|
|
|
|
|
- const workbook = new ExcelJS.Workbook();
|
|
|
|
|
- await workbook.xlsx.load(buffer as any);
|
|
|
|
|
- const sheet = workbook.worksheets[0];
|
|
|
|
|
-
|
|
|
|
|
- if (!sheet) {
|
|
|
|
|
- throw new BadRequestException('No sheet found in Excel file');
|
|
|
|
|
|
|
+ if (ch === ',' && !inQuotes) {
|
|
|
|
|
+ cells.push(current);
|
|
|
|
|
+ current = '';
|
|
|
|
|
+ continue;
|
|
|
|
|
+ }
|
|
|
|
|
+ current += ch;
|
|
|
}
|
|
}
|
|
|
|
|
+ cells.push(current);
|
|
|
|
|
+ return cells;
|
|
|
|
|
+ };
|
|
|
|
|
|
|
|
- const lastRow = sheet.lastRow?.number ?? 0;
|
|
|
|
|
- for (let r = 1; r <= lastRow; r += 1) {
|
|
|
|
|
- const row = sheet.getRow(r);
|
|
|
|
|
- rowsAH.push({
|
|
|
|
|
- idRaw: cellToString(row.getCell(1).value),
|
|
|
|
|
- tagsRaw: cellToString(row.getCell(8).value),
|
|
|
|
|
- });
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ for (let i = 1; i < lines.length; i += 1) {
|
|
|
|
|
+ const line = lines[i];
|
|
|
|
|
+ if (!line) continue;
|
|
|
|
|
+ const cells = parseCsvLine(line);
|
|
|
|
|
+ rowsAH.push({
|
|
|
|
|
+ idRaw: cells[0] ?? '',
|
|
|
|
|
+ tagsRaw: cells[7] ?? '',
|
|
|
|
|
+ });
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
this.logger.log(
|
|
this.logger.log(
|