2023-11-06 18:14:21 +01:00
|
|
|
import type { SmartArchive } from './classes.smartarchive.js';
|
2025-11-25 12:32:13 +00:00
|
|
|
import type { TSupportedMime } from './interfaces.js';
|
2023-11-06 18:14:21 +01:00
|
|
|
import * as plugins from './plugins.js';
|
|
|
|
|
|
2025-11-25 12:32:13 +00:00
|
|
|
/**
|
|
|
|
|
* Type for decompression streams
|
|
|
|
|
*/
|
|
|
|
|
export type TDecompressionStream =
|
|
|
|
|
| plugins.stream.Transform
|
|
|
|
|
| plugins.stream.Duplex
|
|
|
|
|
| plugins.tarStream.Extract;
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Result of archive analysis
|
|
|
|
|
*/
|
2023-11-06 18:14:21 +01:00
|
|
|
export interface IAnalyzedResult {
|
2025-11-25 12:32:13 +00:00
|
|
|
fileType: plugins.fileType.FileTypeResult | undefined;
|
2023-11-06 18:14:21 +01:00
|
|
|
isArchive: boolean;
|
2025-11-25 12:32:13 +00:00
|
|
|
resultStream: plugins.smartstream.SmartDuplex<Buffer, Buffer>;
|
|
|
|
|
decompressionStream: TDecompressionStream;
|
2023-11-06 18:14:21 +01:00
|
|
|
}
|
|
|
|
|
|
2025-11-25 12:32:13 +00:00
|
|
|
/**
|
|
|
|
|
* Analyzes archive streams to detect format and provide decompression
|
|
|
|
|
*/
|
2023-11-06 18:14:21 +01:00
|
|
|
export class ArchiveAnalyzer {
|
2025-11-25 12:32:13 +00:00
|
|
|
private smartArchiveRef: SmartArchive;
|
2023-11-06 18:14:21 +01:00
|
|
|
|
|
|
|
|
constructor(smartArchiveRefArg: SmartArchive) {
|
|
|
|
|
this.smartArchiveRef = smartArchiveRefArg;
|
|
|
|
|
}
|
|
|
|
|
|
2025-11-25 12:32:13 +00:00
|
|
|
/**
|
|
|
|
|
* Check if a MIME type represents an archive format
|
|
|
|
|
*/
|
|
|
|
|
private async mimeTypeIsArchive(mimeType: string | undefined): Promise<boolean> {
|
|
|
|
|
if (!mimeType) return false;
|
|
|
|
|
|
2023-11-06 18:14:21 +01:00
|
|
|
const archiveMimeTypes: Set<string> = new Set([
|
|
|
|
|
'application/zip',
|
|
|
|
|
'application/x-rar-compressed',
|
|
|
|
|
'application/x-tar',
|
|
|
|
|
'application/gzip',
|
|
|
|
|
'application/x-7z-compressed',
|
|
|
|
|
'application/x-bzip2',
|
|
|
|
|
]);
|
2025-08-18 01:29:06 +00:00
|
|
|
|
2023-11-06 18:14:21 +01:00
|
|
|
return archiveMimeTypes.has(mimeType);
|
|
|
|
|
}
|
|
|
|
|
|
2025-11-25 12:32:13 +00:00
|
|
|
/**
|
|
|
|
|
* Get the appropriate decompression stream for a MIME type
|
|
|
|
|
*/
|
|
|
|
|
private async getDecompressionStream(mimeTypeArg: TSupportedMime): Promise<TDecompressionStream> {
|
2023-11-06 18:14:21 +01:00
|
|
|
switch (mimeTypeArg) {
|
|
|
|
|
case 'application/gzip':
|
|
|
|
|
return this.smartArchiveRef.gzipTools.getDecompressionStream();
|
2024-03-17 00:29:42 +01:00
|
|
|
case 'application/zip':
|
|
|
|
|
return this.smartArchiveRef.zipTools.getDecompressionStream();
|
2023-11-06 18:14:21 +01:00
|
|
|
case 'application/x-bzip2':
|
2025-11-25 12:32:13 +00:00
|
|
|
return this.smartArchiveRef.bzip2Tools.getDecompressionStream();
|
2023-11-06 18:14:21 +01:00
|
|
|
case 'application/x-tar':
|
2025-11-25 12:32:13 +00:00
|
|
|
return this.smartArchiveRef.tarTools.getDecompressionStream();
|
2023-11-06 18:14:21 +01:00
|
|
|
default:
|
|
|
|
|
// Handle unsupported formats or no decompression needed
|
2023-11-13 22:11:24 +01:00
|
|
|
return plugins.smartstream.createPassThrough();
|
2023-11-06 18:14:21 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-11-25 12:32:13 +00:00
|
|
|
/**
|
|
|
|
|
* Create an analyzed stream that detects archive type and provides decompression
|
|
|
|
|
* Emits a single IAnalyzedResult object
|
|
|
|
|
*/
|
|
|
|
|
public getAnalyzedStream(): plugins.smartstream.SmartDuplex<Buffer, IAnalyzedResult> {
|
2023-11-06 18:14:21 +01:00
|
|
|
let firstRun = true;
|
2023-11-13 22:11:24 +01:00
|
|
|
const resultStream = plugins.smartstream.createPassThrough();
|
2025-11-25 12:32:13 +00:00
|
|
|
|
|
|
|
|
const analyzerstream = new plugins.smartstream.SmartDuplex<Buffer, IAnalyzedResult>({
|
2023-11-06 18:14:21 +01:00
|
|
|
readableObjectMode: true,
|
2023-11-08 17:01:48 +01:00
|
|
|
writeFunction: async (chunkArg: Buffer, streamtools) => {
|
2023-11-06 18:14:21 +01:00
|
|
|
if (firstRun) {
|
|
|
|
|
firstRun = false;
|
2023-11-14 10:55:19 +01:00
|
|
|
const fileType = await plugins.fileType.fileTypeFromBuffer(chunkArg);
|
2025-11-25 12:32:13 +00:00
|
|
|
const decompressionStream = await this.getDecompressionStream(fileType?.mime as TSupportedMime);
|
|
|
|
|
|
2023-11-06 18:14:21 +01:00
|
|
|
const result: IAnalyzedResult = {
|
|
|
|
|
fileType,
|
2023-11-07 04:19:54 +01:00
|
|
|
isArchive: await this.mimeTypeIsArchive(fileType?.mime),
|
2023-11-06 18:14:21 +01:00
|
|
|
resultStream,
|
|
|
|
|
decompressionStream,
|
|
|
|
|
};
|
2023-11-13 22:11:24 +01:00
|
|
|
await streamtools.push(result);
|
2023-11-06 18:14:21 +01:00
|
|
|
}
|
2023-11-14 10:55:19 +01:00
|
|
|
await resultStream.backpressuredPush(chunkArg);
|
|
|
|
|
return null;
|
2023-11-06 18:14:21 +01:00
|
|
|
},
|
2025-11-25 12:32:13 +00:00
|
|
|
finalFunction: async () => {
|
2023-11-07 04:19:54 +01:00
|
|
|
resultStream.push(null);
|
|
|
|
|
return null;
|
2025-08-18 01:29:06 +00:00
|
|
|
},
|
2023-11-06 18:14:21 +01:00
|
|
|
});
|
2025-11-25 12:32:13 +00:00
|
|
|
|
2023-11-06 18:14:21 +01:00
|
|
|
return analyzerstream;
|
|
|
|
|
}
|
|
|
|
|
}
|