image-video-optimizer 1.0.0 → 1.1.1
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/README.md +1 -1
- package/package.json +1 -1
- package/src/imageProcessor.js +52 -2
- package/src/videoProcessor.js +68 -1
package/README.md
CHANGED
package/package.json
CHANGED
package/src/imageProcessor.js
CHANGED
|
@@ -25,11 +25,61 @@ class ImageProcessor {
|
|
|
25
25
|
}
|
|
26
26
|
|
|
27
27
|
const outputPath = this.generateOutputPath(inputPath, targetFormat);
|
|
28
|
+
const parsedPath = path.parse(inputPath);
|
|
28
29
|
|
|
29
30
|
// Check if output path would be same as input path
|
|
30
31
|
if (outputPath === inputPath) {
|
|
31
|
-
|
|
32
|
-
|
|
32
|
+
// Use temporary name for processing
|
|
33
|
+
const tempPath = path.join(parsedPath.dir, `${parsedPath.name}_temp.${targetFormat}`);
|
|
34
|
+
console.log(`Using temporary path for processing: ${path.basename(tempPath)}`);
|
|
35
|
+
|
|
36
|
+
let sharpInstance = sharp(inputPath);
|
|
37
|
+
|
|
38
|
+
if (needsResize) {
|
|
39
|
+
const newHeight = Math.round((maxWidth / metadata.width) * metadata.height);
|
|
40
|
+
sharpInstance = sharpInstance.resize(maxWidth, newHeight, {
|
|
41
|
+
fit: 'inside',
|
|
42
|
+
withoutEnlargement: true
|
|
43
|
+
});
|
|
44
|
+
console.log(`Resizing to: ${maxWidth}x${newHeight}`);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
switch (targetFormat.toLowerCase()) {
|
|
48
|
+
case 'jpg':
|
|
49
|
+
case 'jpeg':
|
|
50
|
+
sharpInstance = sharpInstance.jpeg({ quality: 85 });
|
|
51
|
+
break;
|
|
52
|
+
case 'png':
|
|
53
|
+
sharpInstance = sharpInstance.png({ compressionLevel: 8 });
|
|
54
|
+
break;
|
|
55
|
+
case 'webp':
|
|
56
|
+
sharpInstance = sharpInstance.webp({ quality: 85 });
|
|
57
|
+
break;
|
|
58
|
+
default:
|
|
59
|
+
sharpInstance = sharpInstance.jpeg({ quality: 85 });
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
await sharpInstance.toFile(tempPath);
|
|
63
|
+
|
|
64
|
+
const compressionResult = this.checkCompression(inputPath, tempPath);
|
|
65
|
+
|
|
66
|
+
if (compressionResult.effective) {
|
|
67
|
+
// Replace original with processed file
|
|
68
|
+
fs.unlinkSync(inputPath);
|
|
69
|
+
fs.renameSync(tempPath, outputPath);
|
|
70
|
+
console.log(`✓ Processed: ${path.basename(inputPath)} (${compressionResult.compressionPercent}% reduction)`);
|
|
71
|
+
return {
|
|
72
|
+
processed: true,
|
|
73
|
+
outputPath,
|
|
74
|
+
originalSize: compressionResult.originalSize,
|
|
75
|
+
newSize: compressionResult.newSize,
|
|
76
|
+
compressionPercent: compressionResult.compressionPercent
|
|
77
|
+
};
|
|
78
|
+
} else {
|
|
79
|
+
console.log(`✗ Ineffective compression, keeping original: ${path.basename(inputPath)}`);
|
|
80
|
+
fs.unlinkSync(tempPath);
|
|
81
|
+
return { processed: false, reason: 'ineffective_compression' };
|
|
82
|
+
}
|
|
33
83
|
}
|
|
34
84
|
|
|
35
85
|
let sharpInstance = sharp(inputPath);
|
package/src/videoProcessor.js
CHANGED
|
@@ -44,6 +44,74 @@ class VideoProcessor {
|
|
|
44
44
|
return;
|
|
45
45
|
}
|
|
46
46
|
|
|
47
|
+
const outputPath = this.generateOutputPath(inputPath);
|
|
48
|
+
const parsedPath = path.parse(inputPath);
|
|
49
|
+
|
|
50
|
+
// Check if output path would be same as input path
|
|
51
|
+
if (outputPath === inputPath) {
|
|
52
|
+
// Use temporary name for processing
|
|
53
|
+
const tempPath = path.join(parsedPath.dir, `${parsedPath.name}_temp.mp4`);
|
|
54
|
+
console.log(`Using temporary path for processing: ${path.basename(tempPath)}`);
|
|
55
|
+
|
|
56
|
+
let ffmpegCommand = ffmpeg(inputPath);
|
|
57
|
+
|
|
58
|
+
if (needsResize) {
|
|
59
|
+
const newHeight = Math.round((maxWidth / videoStream.width) * videoStream.height);
|
|
60
|
+
ffmpegCommand = ffmpegCommand.videoFilters(`scale=${maxWidth}:${newHeight}`);
|
|
61
|
+
console.log(`Resizing to: ${maxWidth}x${newHeight}`);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
switch (encodeFormat.toLowerCase()) {
|
|
65
|
+
case 'h264':
|
|
66
|
+
ffmpegCommand = ffmpegCommand.videoCodec('libx264').audioCodec('aac');
|
|
67
|
+
break;
|
|
68
|
+
case 'h265':
|
|
69
|
+
ffmpegCommand = ffmpegCommand.videoCodec('libx265').audioCodec('aac');
|
|
70
|
+
break;
|
|
71
|
+
case 'vp9':
|
|
72
|
+
ffmpegCommand = ffmpegCommand.videoCodec('libvpx-vp9').audioCodec('libvorbis');
|
|
73
|
+
break;
|
|
74
|
+
default:
|
|
75
|
+
ffmpegCommand = ffmpegCommand.videoCodec('libx264').audioCodec('aac');
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
ffmpegCommand
|
|
79
|
+
.outputOptions('-crf 23')
|
|
80
|
+
.outputOptions('-preset medium')
|
|
81
|
+
.format('mp4')
|
|
82
|
+
.output(tempPath)
|
|
83
|
+
.on('end', () => {
|
|
84
|
+
const compressionResult = this.checkCompression(inputPath, tempPath);
|
|
85
|
+
|
|
86
|
+
if (compressionResult.effective) {
|
|
87
|
+
// Replace original with processed file
|
|
88
|
+
fs.unlinkSync(inputPath);
|
|
89
|
+
fs.renameSync(tempPath, outputPath);
|
|
90
|
+
console.log(`✓ Processed: ${path.basename(inputPath)} (${compressionResult.compressionPercent}% reduction)`);
|
|
91
|
+
resolve({
|
|
92
|
+
processed: true,
|
|
93
|
+
outputPath,
|
|
94
|
+
originalSize: compressionResult.originalSize,
|
|
95
|
+
newSize: compressionResult.newSize,
|
|
96
|
+
compressionPercent: compressionResult.compressionPercent
|
|
97
|
+
});
|
|
98
|
+
} else {
|
|
99
|
+
console.log(`✗ Ineffective compression, keeping original: ${path.basename(inputPath)}`);
|
|
100
|
+
fs.unlinkSync(tempPath);
|
|
101
|
+
resolve({ processed: false, reason: 'ineffective_compression' });
|
|
102
|
+
}
|
|
103
|
+
})
|
|
104
|
+
.on('error', (err) => {
|
|
105
|
+
console.error(`Error processing video ${inputPath}:`, err.message);
|
|
106
|
+
if (fs.existsSync(tempPath)) {
|
|
107
|
+
fs.unlinkSync(tempPath);
|
|
108
|
+
}
|
|
109
|
+
resolve({ processed: false, error: err.message });
|
|
110
|
+
})
|
|
111
|
+
.run();
|
|
112
|
+
return;
|
|
113
|
+
}
|
|
114
|
+
|
|
47
115
|
let ffmpegCommand = ffmpeg(inputPath);
|
|
48
116
|
|
|
49
117
|
if (needsResize) {
|
|
@@ -98,7 +166,6 @@ class VideoProcessor {
|
|
|
98
166
|
})
|
|
99
167
|
.run();
|
|
100
168
|
});
|
|
101
|
-
|
|
102
169
|
} catch (error) {
|
|
103
170
|
console.error(`Error processing video ${inputPath}:`, error.message);
|
|
104
171
|
resolve({ processed: false, error: error.message });
|