-
Notifications
You must be signed in to change notification settings - Fork 368
Expand file tree
/
Copy pathsevenZip.ts
More file actions
178 lines (141 loc) · 5.82 KB
/
Copy pathsevenZip.ts
File metadata and controls
178 lines (141 loc) · 5.82 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
// file: 7z.ts
import type { FileData, FileFormat, FormatHandler } from "../FormatHandler.ts";
import CommonFormats, { Category } from "src/CommonFormats.ts";
import SevenZip from "7z-wasm";
import mime from "mime";
import normalizeMimeType from "src/normalizeMimeType.ts";
const defaultSevenZipOptions = {
locateFile: () => "/convert/wasm/7zz.wasm"
}
class sevenZipHandler implements FormatHandler {
public name: string = "sevenZip";
public supportedFormats: FileFormat[] = [];
public ready: boolean = false;
public supportAnyInput: boolean = true;
#tarCompressedFormats: string[] = [];
async init () {
this.supportedFormats = [];
this.#tarCompressedFormats = [];
const stdout: number[] = [];
const sevenZip = await SevenZip({
...defaultSevenZipOptions,
stdout: (c) => {
stdout.push(c);
},
});
sevenZip.callMain(["i"]);
const text = new TextDecoder().decode(new Uint8Array(stdout));
// no codecs for now
const formatsText = text.match(/\n\n\nFormats:\n(.*?)\n\n/s);
if (!formatsText) throw new Error("7zz output did not have any formats");
const formatLines = formatsText[1].split("\n");
// this will totally break in future 7z versions but its the only way
for (const formatLine of formatLines) {
// 7zz i gives more than 1 extension, but i dont think we will
// need those as they are mostly aliases and thats renamehandler's job.
// also we cant faithfully parse more than 1 extension because there is no
// way to know where extensions stop and weird signature stuff begins
const [flags, name, extension, ...extra] = formatLine.trim().split(/ +/);
if (name === "Hash") continue;
const mimeType = normalizeMimeType(mime.getType(extension) || `application/${extension}`);
let displayName = `${name} archive`;
let format = extension;
if (extra.includes("(.tar)")) {
// compressed formats will be only tar for now
this.#tarCompressedFormats.push(name);
displayName = `${name} compressed tar archive`;
format = `tar.${extension}`;
}
this.supportedFormats.push({
name: displayName,
format,
extension,
mime: mimeType,
from: true,
to: flags.includes("C"),
internal: name,
category: Category.ARCHIVE,
lossless: false, // archive metadata is too complicated
});
}
// push zip and tar up the list
const priority = ["tar", "zip"];
const prioritized = [];
for (const format of priority) {
prioritized.push(this.supportedFormats.find(f => f.internal === format)!);
}
this.supportedFormats = [
...prioritized,
...this.supportedFormats.filter(f => !priority.includes(f.internal))
];
this.ready = true;
}
async doConvert (
inputFiles: FileData[],
inputFormat: FileFormat,
outputFormat: FileFormat
): Promise<FileData[]> {
const outputFiles: FileData[] = [];
if (inputFormat.internal === "brarchive") {
throw new Error(`sevenZipHandler cannot convert from ${inputFormat.mime}`);
}
if (!this.supportedFormats.some(format => format.to && format.internal === outputFormat.internal)) {
throw new TypeError(`sevenZipHandler cannot convert to ${outputFormat.mime}`);
}
// handle compressed tars
if (this.#tarCompressedFormats.includes(inputFormat.internal)
|| this.#tarCompressedFormats.includes(outputFormat.internal)) {
if (outputFormat.internal === "tar") {
for (const inputFile of inputFiles) {
const sevenZip = await SevenZip(defaultSevenZipOptions);
sevenZip.FS.writeFile(inputFile.name, inputFile.bytes);
sevenZip.callMain(["x", inputFile.name]);
const name = inputFile.name.replace(/\.[^.]+$/, "");
const bytes = sevenZip.FS.readFile(name);
outputFiles.push({ bytes, name });
}
} else if (inputFormat.internal === "tar") {
for (const inputFile of inputFiles) {
const sevenZip = await SevenZip(defaultSevenZipOptions);
sevenZip.FS.writeFile(inputFile.name, inputFile.bytes);
const name = inputFile.name + `.${outputFormat.extension}`;
sevenZip.callMain(["a", name, inputFile.name]);
const bytes = sevenZip.FS.readFile(name);
outputFiles.push({ bytes, name });
}
} else {
throw new TypeError(`sevenZipHandler cannot convert from ${inputFormat.mime} to ${outputFormat.mime}`);
}
return outputFiles;
}
if (this.supportedFormats.some(format => format.internal === inputFormat.internal)) {
for (const inputFile of inputFiles) {
const sevenZip = await SevenZip(defaultSevenZipOptions);
sevenZip.FS.writeFile(inputFile.name, inputFile.bytes);
sevenZip.callMain(["x", inputFile.name, `-odata`]);
const name = inputFile.name.replace(/\.[^.]+$/, "") + `.${outputFormat.extension}`;
sevenZip.FS.chdir("data"); // we need to preserve the structure of the input archive
sevenZip.callMain(["a", "../" + name]);
sevenZip.FS.chdir("..");
const bytes = sevenZip.FS.readFile(name);
outputFiles.push({ bytes, name });
}
} else {
const sevenZip = await SevenZip(defaultSevenZipOptions);
sevenZip.FS.mkdir("data");
sevenZip.FS.chdir("data");
for (const inputFile of inputFiles) {
sevenZip.FS.writeFile(inputFile.name, inputFile.bytes);
}
const name = inputFiles.length === 1 ?
inputFiles[0].name + `.${outputFormat.extension}`
: `archive.${outputFormat.extension}`;
sevenZip.callMain(["a", "../" + name]);
sevenZip.FS.chdir("..");
const bytes = sevenZip.FS.readFile(name);
outputFiles.push({ bytes, name });
}
return outputFiles;
}
}
export default sevenZipHandler;