roxify 1.3.0 → 1.3.2
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/cli.js +76 -3
- package/dist/roxify-cli +0 -0
- package/dist/utils/encoder.js +1 -1
- package/dist/utils/rust-cli-wrapper.d.ts +2 -1
- package/dist/utils/rust-cli-wrapper.js +63 -6
- package/libroxify_native.node +0 -0
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -5,7 +5,8 @@ import { open } from 'fs/promises';
|
|
|
5
5
|
import { basename, dirname, join, resolve } from 'path';
|
|
6
6
|
import { DataFormatError, decodePngToBinary, encodeBinaryToPng, hasPassphraseInPng, IncorrectPassphraseError, listFilesInPng, PassphraseRequiredError, } from './index.js';
|
|
7
7
|
import { packPathsGenerator, unpackBuffer } from './pack.js';
|
|
8
|
-
|
|
8
|
+
import { encodeWithRustCLI, isRustBinaryAvailable, } from './utils/rust-cli-wrapper.js';
|
|
9
|
+
const VERSION = '1.3.2';
|
|
9
10
|
async function readLargeFile(filePath) {
|
|
10
11
|
const st = statSync(filePath);
|
|
11
12
|
if (st.size <= 2 * 1024 * 1024 * 1024) {
|
|
@@ -47,6 +48,7 @@ Options:
|
|
|
47
48
|
-m, --mode <mode> Mode: screenshot (default)
|
|
48
49
|
-e, --encrypt <type> auto|aes|xor|none
|
|
49
50
|
--no-compress Disable compression
|
|
51
|
+
--force-ts Force TypeScript encoder (slower but supports encryption)
|
|
50
52
|
-o, --output <path> Output file path
|
|
51
53
|
-s, --sizes Show file sizes in 'list' output (default)
|
|
52
54
|
--no-sizes Disable file size reporting in 'list'
|
|
@@ -89,6 +91,10 @@ function parseArgs(args) {
|
|
|
89
91
|
parsed.debug = true;
|
|
90
92
|
i++;
|
|
91
93
|
}
|
|
94
|
+
else if (key === 'force-ts') {
|
|
95
|
+
parsed.forceTs = true;
|
|
96
|
+
i++;
|
|
97
|
+
}
|
|
92
98
|
else if (key === 'debug-dir') {
|
|
93
99
|
parsed.debugDir = args[i + 1];
|
|
94
100
|
i += 2;
|
|
@@ -166,7 +172,15 @@ async function encodeCommand(args) {
|
|
|
166
172
|
console.log('Usage: npx rox encode <input> [output] [options]');
|
|
167
173
|
process.exit(1);
|
|
168
174
|
}
|
|
169
|
-
|
|
175
|
+
let safeCwd = '/';
|
|
176
|
+
try {
|
|
177
|
+
safeCwd = process.cwd();
|
|
178
|
+
}
|
|
179
|
+
catch (e) {
|
|
180
|
+
// ENOENT: fallback sur racine
|
|
181
|
+
safeCwd = '/';
|
|
182
|
+
}
|
|
183
|
+
const resolvedInputs = inputPaths.map((p) => resolve(safeCwd, p));
|
|
170
184
|
let outputName = inputPaths.length === 1 ? basename(firstInput) : 'archive';
|
|
171
185
|
if (inputPaths.length === 1 && !statSync(resolvedInputs[0]).isDirectory()) {
|
|
172
186
|
outputName = outputName.replace(/(\.[^.]+)?$/, '.png');
|
|
@@ -174,7 +188,65 @@ async function encodeCommand(args) {
|
|
|
174
188
|
else {
|
|
175
189
|
outputName += '.png';
|
|
176
190
|
}
|
|
177
|
-
|
|
191
|
+
let resolvedOutput;
|
|
192
|
+
try {
|
|
193
|
+
resolvedOutput = resolve(safeCwd, parsed.output || outputPath || outputName);
|
|
194
|
+
}
|
|
195
|
+
catch (e) {
|
|
196
|
+
resolvedOutput = join('/', parsed.output || outputPath || outputName);
|
|
197
|
+
}
|
|
198
|
+
if (isRustBinaryAvailable() && !parsed.forceTs) {
|
|
199
|
+
try {
|
|
200
|
+
console.log(`Encoding to ${resolvedOutput} (Using native Rust encoder)\n`);
|
|
201
|
+
const startTime = Date.now();
|
|
202
|
+
const encodeBar = new cliProgress.SingleBar({ format: ' {bar} {percentage}% | {step} | {elapsed}s' }, cliProgress.Presets.shades_classic);
|
|
203
|
+
let barValue = 0;
|
|
204
|
+
encodeBar.start(100, 0, { step: 'Encoding', elapsed: '0' });
|
|
205
|
+
const progressInterval = setInterval(() => {
|
|
206
|
+
barValue = Math.min(barValue + 1, 99);
|
|
207
|
+
const elapsed = Math.floor((Date.now() - startTime) / 1000);
|
|
208
|
+
encodeBar.update(barValue, {
|
|
209
|
+
step: 'Encoding',
|
|
210
|
+
elapsed: String(elapsed),
|
|
211
|
+
});
|
|
212
|
+
}, 500);
|
|
213
|
+
const encryptType = parsed.encrypt === 'xor' ? 'xor' : 'aes';
|
|
214
|
+
await encodeWithRustCLI(inputPaths.length === 1 ? resolvedInputs[0] : resolvedInputs[0], resolvedOutput, 3, parsed.passphrase, encryptType);
|
|
215
|
+
clearInterval(progressInterval);
|
|
216
|
+
const encodeTime = Date.now() - startTime;
|
|
217
|
+
encodeBar.update(100, {
|
|
218
|
+
step: 'done',
|
|
219
|
+
elapsed: String(Math.floor(encodeTime / 1000)),
|
|
220
|
+
});
|
|
221
|
+
encodeBar.stop();
|
|
222
|
+
const { statSync: fstatSync } = await import('fs');
|
|
223
|
+
let inputSize = 0;
|
|
224
|
+
if (inputPaths.length === 1 &&
|
|
225
|
+
fstatSync(resolvedInputs[0]).isDirectory()) {
|
|
226
|
+
const { execSync } = await import('child_process');
|
|
227
|
+
const sizeOutput = execSync(`du -sb "${resolvedInputs[0]}"`, {
|
|
228
|
+
encoding: 'utf-8',
|
|
229
|
+
});
|
|
230
|
+
inputSize = parseInt(sizeOutput.split(/\s+/)[0]);
|
|
231
|
+
}
|
|
232
|
+
else {
|
|
233
|
+
inputSize = fstatSync(resolvedInputs[0]).size;
|
|
234
|
+
}
|
|
235
|
+
const outputSize = fstatSync(resolvedOutput).size;
|
|
236
|
+
const ratio = ((outputSize / inputSize) * 100).toFixed(1);
|
|
237
|
+
console.log(`\nSuccess!`);
|
|
238
|
+
console.log(` Input: ${(inputSize / 1024 / 1024).toFixed(2)} MB`);
|
|
239
|
+
console.log(` Output: ${(outputSize / 1024 / 1024).toFixed(2)} MB (${ratio}% of original)`);
|
|
240
|
+
console.log(` Time: ${encodeTime}ms`);
|
|
241
|
+
console.log(` Saved: ${resolvedOutput}`);
|
|
242
|
+
console.log(' ');
|
|
243
|
+
return;
|
|
244
|
+
}
|
|
245
|
+
catch (err) {
|
|
246
|
+
console.warn('\nRust encoder failed, falling back to TypeScript encoder...');
|
|
247
|
+
console.warn(`Reason: ${err.message}\n`);
|
|
248
|
+
}
|
|
249
|
+
}
|
|
178
250
|
let options = {};
|
|
179
251
|
try {
|
|
180
252
|
const encodeBar = new cliProgress.SingleBar({
|
|
@@ -212,6 +284,7 @@ async function encodeCommand(args) {
|
|
|
212
284
|
mode,
|
|
213
285
|
name: parsed.outputName || 'archive',
|
|
214
286
|
skipOptimization: true,
|
|
287
|
+
compressionLevel: 19,
|
|
215
288
|
});
|
|
216
289
|
if (parsed.verbose)
|
|
217
290
|
options.verbose = true;
|
package/dist/roxify-cli
CHANGED
|
Binary file
|
package/dist/utils/encoder.js
CHANGED
|
@@ -53,7 +53,7 @@ export async function encodeBinaryToPng(input, opts = {}) {
|
|
|
53
53
|
}
|
|
54
54
|
if (opts.onProgress)
|
|
55
55
|
opts.onProgress({ phase: 'compress_start', total: totalLen });
|
|
56
|
-
const compressionLevel = opts.compressionLevel ??
|
|
56
|
+
const compressionLevel = opts.compressionLevel ?? 19;
|
|
57
57
|
let payload = await parallelZstdCompress(payloadInput, compressionLevel, (loaded, total) => {
|
|
58
58
|
if (opts.onProgress) {
|
|
59
59
|
opts.onProgress({
|
|
@@ -1 +1,2 @@
|
|
|
1
|
-
export declare function
|
|
1
|
+
export declare function isRustBinaryAvailable(): boolean;
|
|
2
|
+
export declare function encodeWithRustCLI(inputPath: string, outputPath: string, compressionLevel?: number, passphrase?: string, encryptType?: 'aes' | 'xor'): Promise<void>;
|
|
@@ -4,19 +4,76 @@ import { dirname, join } from 'path';
|
|
|
4
4
|
import { fileURLToPath } from 'url';
|
|
5
5
|
const __filename = fileURLToPath(import.meta.url);
|
|
6
6
|
const __dirname = dirname(__filename);
|
|
7
|
-
|
|
8
|
-
const
|
|
9
|
-
|
|
10
|
-
|
|
7
|
+
function findRustBinary() {
|
|
8
|
+
const candidates = [];
|
|
9
|
+
const binNames = process.platform === 'win32'
|
|
10
|
+
? ['roxify-cli.exe', 'roxify_cli.exe', 'roxify_native.exe']
|
|
11
|
+
: ['roxify-cli', 'roxify_cli', 'roxify_native'];
|
|
12
|
+
// Possible locations relative to this file (works in repo and in packaged dist)
|
|
13
|
+
const relativeDirs = [
|
|
14
|
+
join(__dirname, '..', '..', 'target', 'release'),
|
|
15
|
+
join(__dirname, '..', '..', 'dist'),
|
|
16
|
+
join(__dirname, '..'),
|
|
17
|
+
join(__dirname, '..', '..'),
|
|
18
|
+
];
|
|
19
|
+
for (const dir of relativeDirs) {
|
|
20
|
+
for (const name of binNames) {
|
|
21
|
+
candidates.push(join(dir, name));
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
// Common global paths
|
|
25
|
+
if (process.platform !== 'win32') {
|
|
26
|
+
candidates.push('/usr/local/bin/roxify_native');
|
|
27
|
+
candidates.push('/usr/bin/roxify_native');
|
|
28
|
+
}
|
|
29
|
+
for (const p of candidates) {
|
|
30
|
+
try {
|
|
31
|
+
if (existsSync(p))
|
|
32
|
+
return p;
|
|
33
|
+
}
|
|
34
|
+
catch (e) { }
|
|
35
|
+
}
|
|
36
|
+
// Search in PATH for common binary names
|
|
37
|
+
try {
|
|
38
|
+
const which = process.platform === 'win32' ? 'where' : 'which';
|
|
39
|
+
const { execSync } = require('child_process');
|
|
40
|
+
for (const name of binNames) {
|
|
41
|
+
try {
|
|
42
|
+
const out = execSync(`${which} ${name}`, { encoding: 'utf-8' })
|
|
43
|
+
.split('\n')[0]
|
|
44
|
+
.trim();
|
|
45
|
+
if (out && existsSync(out))
|
|
46
|
+
return out;
|
|
47
|
+
}
|
|
48
|
+
catch (e) {
|
|
49
|
+
// ignore
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
catch (e) { }
|
|
54
|
+
return null;
|
|
55
|
+
}
|
|
56
|
+
export function isRustBinaryAvailable() {
|
|
57
|
+
return findRustBinary() !== null;
|
|
58
|
+
}
|
|
59
|
+
export async function encodeWithRustCLI(inputPath, outputPath, compressionLevel = 3, passphrase, encryptType = 'aes') {
|
|
60
|
+
const cliPath = findRustBinary();
|
|
61
|
+
if (!cliPath) {
|
|
62
|
+
throw new Error('Rust CLI binary not found. Run: cargo build --release');
|
|
11
63
|
}
|
|
12
64
|
return new Promise((resolve, reject) => {
|
|
13
|
-
const
|
|
65
|
+
const args = [
|
|
14
66
|
'encode',
|
|
15
67
|
inputPath,
|
|
16
68
|
outputPath,
|
|
17
69
|
'--level',
|
|
18
70
|
String(compressionLevel),
|
|
19
|
-
]
|
|
71
|
+
];
|
|
72
|
+
if (passphrase) {
|
|
73
|
+
args.push('--passphrase', passphrase);
|
|
74
|
+
args.push('--encrypt', encryptType);
|
|
75
|
+
}
|
|
76
|
+
const proc = spawn(cliPath, args);
|
|
20
77
|
let stderr = '';
|
|
21
78
|
proc.stderr.on('data', (data) => {
|
|
22
79
|
stderr += data.toString();
|
package/libroxify_native.node
CHANGED
|
Binary file
|
package/package.json
CHANGED