image-video-optimizer 1.1.3 → 1.2.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "image-video-optimizer",
3
- "version": "1.1.3",
3
+ "version": "1.2.1",
4
4
  "description": "CLI tool to optimize images and videos with configurable resize and compression settings",
5
5
  "main": "src/index.js",
6
6
  "bin": {
@@ -27,60 +27,9 @@ class ImageProcessor {
27
27
  const outputPath = this.generateOutputPath(inputPath, targetFormat);
28
28
  const parsedPath = path.parse(inputPath);
29
29
 
30
- // Check if output path would be same as input path
31
- if (outputPath === inputPath) {
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
- }
83
- }
30
+ // Always use temporary file for processing
31
+ const tempPath = path.join(parsedPath.dir, `${parsedPath.name}_temp.${targetFormat}`);
32
+ console.log(`Using temporary path for processing: ${path.basename(tempPath)}`);
84
33
 
85
34
  let sharpInstance = sharp(inputPath);
86
35
 
@@ -108,11 +57,14 @@ class ImageProcessor {
108
57
  sharpInstance = sharpInstance.jpeg({ quality: 85 });
109
58
  }
110
59
 
111
- await sharpInstance.toFile(outputPath);
60
+ await sharpInstance.toFile(tempPath);
112
61
 
113
- const compressionResult = this.checkCompression(inputPath, outputPath);
62
+ const compressionResult = this.checkCompression(inputPath, tempPath);
114
63
 
115
64
  if (compressionResult.effective) {
65
+ // Replace original with processed file
66
+ fs.unlinkSync(inputPath);
67
+ fs.renameSync(tempPath, outputPath);
116
68
  console.log(`✓ Processed: ${path.basename(inputPath)} (${compressionResult.compressionPercent}% reduction)`);
117
69
  return {
118
70
  processed: true,
@@ -123,7 +75,7 @@ class ImageProcessor {
123
75
  };
124
76
  } else {
125
77
  console.log(`✗ Ineffective compression, keeping original: ${path.basename(inputPath)}`);
126
- fs.unlinkSync(outputPath);
78
+ fs.unlinkSync(tempPath);
127
79
  return { processed: false, reason: 'ineffective_compression' };
128
80
  }
129
81
 
@@ -18,6 +18,11 @@ class VideoProcessor {
18
18
  console.log(`Processing video: ${path.basename(inputPath)}`);
19
19
 
20
20
  const outputPath = this.generateOutputPath(inputPath);
21
+ const parsedPath = path.parse(inputPath);
22
+
23
+ // Always use temporary file for processing
24
+ const tempPath = path.join(parsedPath.dir, `${parsedPath.name}_temp.mp4`);
25
+ console.log(`Using temporary path for processing: ${path.basename(tempPath)}`);
21
26
 
22
27
  ffmpeg.ffprobe(inputPath, (err, metadata) => {
23
28
  if (err) {
@@ -44,115 +49,45 @@ class VideoProcessor {
44
49
  return;
45
50
  }
46
51
 
47
- const outputPath = this.generateOutputPath(inputPath);
48
- const parsedPath = path.parse(inputPath);
52
+ let tempFfmpegCommand = ffmpeg(inputPath);
49
53
 
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
- // Add input/output options with proper quoting
79
- ffmpegCommand = ffmpegCommand.inputOptions(['-fflags', '+genpts']);
80
- ffmpegCommand = ffmpegCommand.outputOptions(['-crf', '23', '-preset', 'medium']);
81
- ffmpegCommand = ffmpegCommand.format('mp4');
82
- ffmpegCommand = ffmpegCommand.output(tempPath);
83
-
84
- ffmpegCommand
85
- .on('start', (commandLine) => {
86
- console.log(`FFmpeg command: ${commandLine}`);
87
- })
88
- .on('end', () => {
89
- const compressionResult = this.checkCompression(inputPath, tempPath);
90
-
91
- if (compressionResult.effective) {
92
- // Replace original with processed file
93
- fs.unlinkSync(inputPath);
94
- fs.renameSync(tempPath, outputPath);
95
- console.log(`✓ Processed: ${path.basename(inputPath)} (${compressionResult.compressionPercent}% reduction)`);
96
- resolve({
97
- processed: true,
98
- outputPath,
99
- originalSize: compressionResult.originalSize,
100
- newSize: compressionResult.newSize,
101
- compressionPercent: compressionResult.compressionPercent
102
- });
103
- } else {
104
- console.log(`✗ Ineffective compression, keeping original: ${path.basename(inputPath)}`);
105
- fs.unlinkSync(tempPath);
106
- resolve({ processed: false, reason: 'ineffective_compression' });
107
- }
108
- })
109
- .on('error', (err) => {
110
- console.error(`Error processing video ${inputPath}:`, err.message);
111
- console.error(`FFmpeg stderr: ${err.stderr || 'No stderr available'}`);
112
- if (fs.existsSync(tempPath)) {
113
- fs.unlinkSync(tempPath);
114
- }
115
- resolve({ processed: false, error: err.message });
116
- })
117
- .run();
118
- return;
119
- }
120
-
121
- let ffmpegCommand = ffmpeg(inputPath);
122
-
123
54
  if (needsResize) {
124
55
  const newHeight = Math.round((maxWidth / videoStream.width) * videoStream.height);
125
- ffmpegCommand = ffmpegCommand.videoFilters(`scale=${maxWidth}:${newHeight}`);
56
+ tempFfmpegCommand = tempFfmpegCommand.videoFilters(`scale=${maxWidth}:${newHeight}`);
126
57
  console.log(`Resizing to: ${maxWidth}x${newHeight}`);
127
58
  }
128
59
 
129
60
  switch (encodeFormat.toLowerCase()) {
130
61
  case 'h264':
131
- ffmpegCommand = ffmpegCommand.videoCodec('libx264').audioCodec('aac');
62
+ tempFfmpegCommand = tempFfmpegCommand.videoCodec('libx264').audioCodec('aac');
132
63
  break;
133
64
  case 'h265':
134
- ffmpegCommand = ffmpegCommand.videoCodec('libx265').audioCodec('aac');
65
+ tempFfmpegCommand = tempFfmpegCommand.videoCodec('libx265').audioCodec('aac');
135
66
  break;
136
67
  case 'vp9':
137
- ffmpegCommand = ffmpegCommand.videoCodec('libvpx-vp9').audioCodec('libvorbis');
68
+ tempFfmpegCommand = tempFfmpegCommand.videoCodec('libvpx-vp9').audioCodec('libvorbis');
138
69
  break;
139
70
  default:
140
- ffmpegCommand = ffmpegCommand.videoCodec('libx264').audioCodec('aac');
71
+ tempFfmpegCommand = tempFfmpegCommand.videoCodec('libx264').audioCodec('aac');
141
72
  }
142
73
 
143
- ffmpegCommand = ffmpegCommand.inputOptions(['-fflags', '+genpts']);
144
- ffmpegCommand = ffmpegCommand.outputOptions(['-crf', '23', '-preset', 'medium']);
145
- ffmpegCommand = ffmpegCommand.format('mp4');
146
- ffmpegCommand = ffmpegCommand.output(outputPath);
74
+ // Add input/output options with proper quoting
75
+ tempFfmpegCommand = tempFfmpegCommand.inputOptions(['-fflags', '+genpts']);
76
+ tempFfmpegCommand = tempFfmpegCommand.outputOptions(['-crf', '23', '-preset', 'medium', '-y']);
77
+ tempFfmpegCommand = tempFfmpegCommand.format('mp4');
78
+ tempFfmpegCommand = tempFfmpegCommand.output(tempPath);
147
79
 
148
- ffmpegCommand
80
+ tempFfmpegCommand
149
81
  .on('start', (commandLine) => {
150
82
  console.log(`FFmpeg command: ${commandLine}`);
151
83
  })
152
84
  .on('end', () => {
153
- const compressionResult = this.checkCompression(inputPath, outputPath);
85
+ const compressionResult = this.checkCompression(inputPath, tempPath);
154
86
 
155
87
  if (compressionResult.effective) {
88
+ // Replace original with processed file
89
+ fs.unlinkSync(inputPath);
90
+ fs.renameSync(tempPath, outputPath);
156
91
  console.log(`✓ Processed: ${path.basename(inputPath)} (${compressionResult.compressionPercent}% reduction)`);
157
92
  resolve({
158
93
  processed: true,
@@ -163,15 +98,15 @@ class VideoProcessor {
163
98
  });
164
99
  } else {
165
100
  console.log(`✗ Ineffective compression, keeping original: ${path.basename(inputPath)}`);
166
- fs.unlinkSync(outputPath);
101
+ fs.unlinkSync(tempPath);
167
102
  resolve({ processed: false, reason: 'ineffective_compression' });
168
103
  }
169
104
  })
170
105
  .on('error', (err) => {
171
106
  console.error(`Error processing video ${inputPath}:`, err.message);
172
107
  console.error(`FFmpeg stderr: ${err.stderr || 'No stderr available'}`);
173
- if (fs.existsSync(outputPath)) {
174
- fs.unlinkSync(outputPath);
108
+ if (fs.existsSync(tempPath)) {
109
+ fs.unlinkSync(tempPath);
175
110
  }
176
111
  resolve({ processed: false, error: err.message });
177
112
  })