image-video-optimizer 2.0.3 → 3.0.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/bin/cli.js CHANGED
@@ -1,26 +1,26 @@
1
1
  #!/usr/bin/env node
2
2
 
3
3
  const { program } = require('commander');
4
- const chalk = require('chalk');
5
4
  const path = require('path');
6
5
  const optimizer = require('../src/index');
7
6
 
8
7
  program
9
8
  .name('image-video-optimizer')
10
9
  .description('Optimize and compress images and videos in a directory')
11
- .version('1.0.0')
10
+ .version('3.0.1')
12
11
  .argument('[target-dir]', 'Target directory to optimize (default: current directory)', process.cwd())
13
12
  .action(async (targetDir) => {
14
13
  try {
15
14
  const resolvedPath = path.resolve(targetDir);
16
- console.log(chalk.blue.bold('\nšŸš€ Image Video Optimizer\n'));
17
- console.log(chalk.gray(`Target directory: ${resolvedPath}`));
15
+ console.log('\nšŸš€ Image Video Optimizer\n');
16
+ console.log('Target directory: ' + resolvedPath);
18
17
 
19
18
  await optimizer.optimize(resolvedPath);
20
19
 
21
- console.log(chalk.green.bold('\nāœ… Optimization complete!\n'));
20
+ console.log('\nāœ… Optimization complete!\n');
22
21
  } catch (error) {
23
- console.error(chalk.red.bold('\nāŒ Error:\n'), chalk.red(error.message));
22
+ console.error('\nāŒ Error:\n');
23
+ console.error(error.message);
24
24
  process.exit(1);
25
25
  }
26
26
  });
package/package.json CHANGED
@@ -1,43 +1,36 @@
1
1
  {
2
2
  "name": "image-video-optimizer",
3
- "version": "2.0.3",
4
- "description": "A CLI tool to optimize images and videos by resizing and converting them according to configuration",
3
+ "version": "3.0.1",
4
+ "description": "CLI tool to optimize and compress images and videos with configurable settings",
5
5
  "main": "src/index.js",
6
6
  "bin": {
7
7
  "image-video-optimizer": "./bin/cli.js"
8
8
  },
9
9
  "scripts": {
10
- "test": "echo \"Error: no test specified\" && exit 1",
11
- "start": "node bin/cli.js"
10
+ "start": "node bin/cli.js",
11
+ "test": "echo \"Error: no test specified\" && exit 1"
12
12
  },
13
13
  "keywords": [
14
14
  "image",
15
15
  "video",
16
16
  "optimizer",
17
- "resize",
18
- "convert",
19
- "batch",
17
+ "compress",
18
+ "ffmpeg",
20
19
  "cli"
21
20
  ],
22
21
  "author": "",
23
22
  "license": "MIT",
24
23
  "dependencies": {
25
- "sharp": "^0.33.0",
26
- "fluent-ffmpeg": "^2.1.3",
27
- "commander": "^11.1.0",
28
- "chalk": "^5.3.0",
29
- "ora": "^8.0.1"
30
- },
31
- "devDependencies": {
32
- "ffmpeg-static": "^5.2.0",
33
- "ffprobe-static": "^3.1.0"
24
+ "commander": "^11.0.0",
25
+ "sharp": "^0.32.0",
26
+ "fluent-ffmpeg": "^2.1.2"
34
27
  },
28
+ "devDependencies": {},
35
29
  "engines": {
36
30
  "node": ">=14.0.0"
37
31
  },
38
- "preferGlobal": true,
39
32
  "repository": {
40
33
  "type": "git",
41
- "url": "https://github.com/yourusername/image-video-optimizer.git"
34
+ "url": "https://git.siliconpin.com/kar/image-video-optimezer-npm-cli"
42
35
  }
43
36
  }
@@ -1,51 +1,99 @@
1
- const fs = require('fs');
1
+ const fs = require('fs').promises;
2
2
  const path = require('path');
3
3
 
4
- const SUPPORTED_IMAGES = ['.jpg', '.jpeg', '.png', '.gif', '.bmp', '.tiff', '.webp'];
5
- const SUPPORTED_VIDEOS = ['.avi', '.mov', '.wmv', '.flv', '.webm', '.mkv', '.m4v', '.mp4'];
4
+ const SUPPORTED_IMAGES = ['jpg', 'jpeg', 'png', 'gif', 'bmp', 'tiff', 'webp'];
5
+ const SUPPORTED_VIDEOS = ['avi', 'mov', 'wmv', 'flv', 'webm', 'mkv', 'm4v', 'mp4'];
6
6
 
7
7
  /**
8
8
  * Recursively search for media files in a directory
9
- * @param {string} dirPath - Directory path to search
10
- * @returns {Promise<{images: Array, videos: Array}>} Found media files
9
+ * @param {string} dirPath - Starting directory path
10
+ * @param {Object} options - Search options
11
+ * @returns {Promise<Object>} Object with images and videos arrays
11
12
  */
12
- async function searchMediaFiles(dirPath) {
13
+ async function searchMediaFiles(dirPath, options = {}) {
13
14
  const images = [];
14
15
  const videos = [];
16
+ const verbose = options.verbose || false;
15
17
 
16
18
  async function walk(currentPath) {
17
19
  try {
18
- const files = await fs.promises.readdir(currentPath);
19
-
20
- for (const file of files) {
21
- const filePath = path.join(currentPath, file);
22
- const stat = await fs.promises.stat(filePath);
23
-
24
- if (stat.isDirectory()) {
25
- // Recursively search subdirectories
26
- await walk(filePath);
27
- } else if (stat.isFile()) {
28
- const ext = path.extname(file).toLowerCase();
29
-
30
- if (SUPPORTED_IMAGES.includes(ext)) {
31
- images.push(filePath);
32
- } else if (SUPPORTED_VIDEOS.includes(ext)) {
33
- videos.push(filePath);
20
+ const entries = await fs.readdir(currentPath, { withFileTypes: true });
21
+
22
+ for (const entry of entries) {
23
+ const fullPath = path.join(currentPath, entry.name);
24
+
25
+ try {
26
+ if (entry.isDirectory()) {
27
+ // Recursively walk subdirectories
28
+ await walk(fullPath);
29
+ } else if (entry.isFile()) {
30
+ const ext = path.extname(entry.name).toLowerCase().slice(1);
31
+
32
+ if (SUPPORTED_IMAGES.includes(ext)) {
33
+ images.push(fullPath);
34
+ if (verbose) {
35
+ console.log(` Found image: ${fullPath}`);
36
+ }
37
+ } else if (SUPPORTED_VIDEOS.includes(ext)) {
38
+ videos.push(fullPath);
39
+ if (verbose) {
40
+ console.log(` Found video: ${fullPath}`);
41
+ }
42
+ }
43
+ }
44
+ } catch (error) {
45
+ if (verbose) {
46
+ console.warn(` Warning: Could not process ${fullPath}: ${error.message}`);
34
47
  }
35
48
  }
36
49
  }
37
50
  } catch (error) {
38
- console.warn(`Warning: Could not read directory ${currentPath}: ${error.message}`);
51
+ if (verbose) {
52
+ console.warn(` Warning: Could not read directory ${currentPath}: ${error.message}`);
53
+ }
39
54
  }
40
55
  }
41
56
 
42
57
  await walk(dirPath);
43
58
 
44
- return { images, videos };
59
+ return {
60
+ images,
61
+ videos,
62
+ total: images.length + videos.length
63
+ };
64
+ }
65
+
66
+ /**
67
+ * Get file size in bytes
68
+ * @param {string} filePath - Path to file
69
+ * @returns {Promise<number>} File size in bytes
70
+ */
71
+ async function getFileSize(filePath) {
72
+ try {
73
+ const stats = await fs.stat(filePath);
74
+ return stats.size;
75
+ } catch (error) {
76
+ throw new Error(`Could not get file size for ${filePath}: ${error.message}`);
77
+ }
78
+ }
79
+
80
+ /**
81
+ * Get human-readable file size
82
+ * @param {number} bytes - Size in bytes
83
+ * @returns {string} Human-readable size
84
+ */
85
+ function formatFileSize(bytes) {
86
+ if (bytes === 0) return '0 Bytes';
87
+ const k = 1024;
88
+ const sizes = ['Bytes', 'KB', 'MB', 'GB'];
89
+ const i = Math.floor(Math.log(bytes) / Math.log(k));
90
+ return Math.round((bytes / Math.pow(k, i)) * 100) / 100 + ' ' + sizes[i];
45
91
  }
46
92
 
47
93
  module.exports = {
48
94
  searchMediaFiles,
95
+ getFileSize,
96
+ formatFileSize,
49
97
  SUPPORTED_IMAGES,
50
98
  SUPPORTED_VIDEOS
51
99
  };
@@ -1,7 +1,6 @@
1
- const sharp = require('sharp');
2
1
  const fs = require('fs');
2
+ const sharp = require('sharp');
3
3
  const path = require('path');
4
- const chalk = require('chalk');
5
4
 
6
5
  /**
7
6
  * Process a single image file
@@ -78,7 +77,7 @@ async function processImage(imagePath, config) {
78
77
  success: true,
79
78
  originalSize,
80
79
  optimizedSize,
81
- message: `${chalk.green('āœ“')} ${action} ${sizeInfo}`,
80
+ message: `āœ“ ${action} ${sizeInfo}`,
82
81
  filePath: finalPath
83
82
  };
84
83
  } else {
@@ -89,7 +88,7 @@ async function processImage(imagePath, config) {
89
88
  success: false,
90
89
  originalSize,
91
90
  optimizedSize,
92
- message: `${chalk.yellow('ā—‹')} ${path.basename(imagePath)} - Already optimized (${(originalSize / 1024).toFixed(1)}KB)`,
91
+ message: `ā—‹ ${path.basename(imagePath)} - Already optimized (${(originalSize / 1024).toFixed(1)}KB)`,
93
92
  filePath: imagePath
94
93
  };
95
94
  }
@@ -98,7 +97,7 @@ async function processImage(imagePath, config) {
98
97
  success: false,
99
98
  originalSize: 0,
100
99
  optimizedSize: 0,
101
- message: `${chalk.red('āœ—')} ${path.basename(imagePath)} - Error: ${error.message}`,
100
+ message: `āœ— ${path.basename(imagePath)} - Error: ${error.message}`,
102
101
  error: true
103
102
  };
104
103
  }
package/src/index.js CHANGED
@@ -1,6 +1,5 @@
1
1
  const fs = require('fs');
2
2
  const path = require('path');
3
- const chalk = require('chalk');
4
3
  const fileSearcher = require('./fileSearcher');
5
4
  const imageProcessor = require('./imageProcessor');
6
5
  const videoProcessor = require('./videoProcessor');
@@ -49,7 +48,7 @@ function parseConfig(configPath) {
49
48
 
50
49
  return config;
51
50
  } catch (error) {
52
- console.warn(chalk.yellow(`Warning: Could not parse config file: ${error.message}`));
51
+ console.warn(`Warning: Could not parse config file: ${error.message}`);
53
52
  return DEFAULT_CONFIG;
54
53
  }
55
54
  }
@@ -73,9 +72,9 @@ video_encode=${DEFAULT_CONFIG.video_encode} # Video encoding format
73
72
 
74
73
  try {
75
74
  fs.writeFileSync(configPath, configContent);
76
- console.log(chalk.cyan(`šŸ“ Created default config: ${CONFIG_FILENAME}`));
75
+ console.log(`šŸ“ Created default config: ${CONFIG_FILENAME}`);
77
76
  } catch (error) {
78
- console.warn(chalk.yellow(`Warning: Could not create config file: ${error.message}`));
77
+ console.warn(`Warning: Could not create config file: ${error.message}`);
79
78
  }
80
79
  }
81
80
  }
@@ -97,20 +96,20 @@ async function optimize(targetDir) {
97
96
  const configPath = path.join(targetDir, CONFIG_FILENAME);
98
97
  const config = parseConfig(configPath);
99
98
 
100
- console.log(chalk.gray('Configuration:'));
101
- console.log(chalk.gray(` Image Max Width: ${config.img_max_width}px`));
102
- console.log(chalk.gray(` Image Format: ${config.img_format}`));
103
- console.log(chalk.gray(` Video Max Width: ${config.video_max_width}px`));
104
- console.log(chalk.gray(` Video Encode: ${config.video_encode}\n`));
99
+ console.log('Configuration:');
100
+ console.log(` Image Max Width: ${config.img_max_width}px`);
101
+ console.log(` Image Format: ${config.img_format}`);
102
+ console.log(` Video Max Width: ${config.video_max_width}px`);
103
+ console.log(` Video Encode: ${config.video_encode}\n`);
105
104
 
106
105
  // Search for media files
107
- console.log(chalk.blue('šŸ” Searching for media files...'));
106
+ console.log('šŸ” Searching for media files...');
108
107
  const { images, videos } = await fileSearcher.searchMediaFiles(targetDir);
109
108
 
110
- console.log(chalk.gray(`Found ${images.length} image(s) and ${videos.length} video(s)\n`));
109
+ console.log(`Found ${images.length} image(s) and ${videos.length} video(s)\n`);
111
110
 
112
111
  if (images.length === 0 && videos.length === 0) {
113
- console.log(chalk.yellow('No media files found to optimize.'));
112
+ console.log('No media files found to optimize.');
114
113
  return;
115
114
  }
116
115
 
@@ -120,7 +119,7 @@ async function optimize(targetDir) {
120
119
 
121
120
  // Process images
122
121
  if (images.length > 0) {
123
- console.log(chalk.blue('šŸ“· Processing images...'));
122
+ console.log('šŸ“· Processing images...');
124
123
  for (const imagePath of images) {
125
124
  const result = await imageProcessor.processImage(imagePath, config);
126
125
  console.log(result.message);
@@ -137,7 +136,7 @@ async function optimize(targetDir) {
137
136
 
138
137
  // Process videos
139
138
  if (videos.length > 0) {
140
- console.log(chalk.blue('šŸŽ¬ Processing videos...'));
139
+ console.log('šŸŽ¬ Processing videos...');
141
140
  for (const videoPath of videos) {
142
141
  const result = await videoProcessor.processVideo(videoPath, config);
143
142
  console.log(result.message);
@@ -153,16 +152,16 @@ async function optimize(targetDir) {
153
152
  }
154
153
 
155
154
  // Print summary
156
- console.log(chalk.blue('šŸ“Š Summary:'));
157
- console.log(chalk.gray(` Files optimized: ${successCount}/${images.length + videos.length}`));
155
+ console.log('šŸ“Š Summary:');
156
+ console.log(` Files optimized: ${successCount}/${images.length + videos.length}`);
158
157
 
159
158
  if (totalOriginalSize > 0) {
160
159
  const savedSize = totalOriginalSize - totalOptimizedSize;
161
160
  const savedPercent = ((savedSize / totalOriginalSize) * 100).toFixed(1);
162
161
 
163
- console.log(chalk.gray(` Original size: ${(totalOriginalSize / 1024 / 1024).toFixed(2)}MB`));
164
- console.log(chalk.gray(` Optimized size: ${(totalOptimizedSize / 1024 / 1024).toFixed(2)}MB`));
165
- console.log(chalk.green(` Space saved: ${(savedSize / 1024 / 1024).toFixed(2)}MB (${savedPercent}%)`));
162
+ console.log(` Original size: ${(totalOriginalSize / 1024 / 1024).toFixed(2)}MB`);
163
+ console.log(` Optimized size: ${(totalOptimizedSize / 1024 / 1024).toFixed(2)}MB`);
164
+ console.log(` Space saved: ${(savedSize / 1024 / 1024).toFixed(2)}MB (${savedPercent}%)`);
166
165
  }
167
166
  }
168
167
 
@@ -1,7 +1,6 @@
1
1
  const ffmpeg = require('fluent-ffmpeg');
2
2
  const fs = require('fs');
3
3
  const path = require('path');
4
- const chalk = require('chalk');
5
4
 
6
5
  /**
7
6
  * Process a single video file
@@ -18,7 +17,7 @@ async function processVideo(videoPath, config) {
18
17
  success: false,
19
18
  originalSize: 0,
20
19
  optimizedSize: 0,
21
- message: `${chalk.red('āœ—')} ${path.basename(videoPath)} - Cannot access file`,
20
+ message: `āœ— ${path.basename(videoPath)} - Cannot access file`,
22
21
  error: true
23
22
  });
24
23
  }
@@ -72,7 +71,7 @@ async function processVideo(videoPath, config) {
72
71
  success: true,
73
72
  originalSize,
74
73
  optimizedSize,
75
- message: `${chalk.green('āœ“')} ${path.basename(videoPath)} → ${path.basename(finalPath)} (${(originalSize / 1024 / 1024).toFixed(1)}MB → ${(optimizedSize / 1024 / 1024).toFixed(1)}MB)`,
74
+ message: `āœ“ ${path.basename(videoPath)} → ${path.basename(finalPath)} (${(originalSize / 1024 / 1024).toFixed(1)}MB → ${(optimizedSize / 1024 / 1024).toFixed(1)}MB)`,
76
75
  filePath: finalPath
77
76
  });
78
77
  } else {
@@ -83,7 +82,7 @@ async function processVideo(videoPath, config) {
83
82
  success: false,
84
83
  originalSize,
85
84
  optimizedSize,
86
- message: `${chalk.yellow('ā—‹')} ${path.basename(videoPath)} - Already optimized (${(originalSize / 1024 / 1024).toFixed(1)}MB)`,
85
+ message: `ā—‹ ${path.basename(videoPath)} - Already optimized (${(originalSize / 1024 / 1024).toFixed(1)}MB)`,
87
86
  filePath: videoPath
88
87
  });
89
88
  }
@@ -92,7 +91,7 @@ async function processVideo(videoPath, config) {
92
91
  success: false,
93
92
  originalSize,
94
93
  optimizedSize: 0,
95
- message: `${chalk.red('āœ—')} ${path.basename(videoPath)} - Post-processing error: ${error.message}`,
94
+ message: `āœ— ${path.basename(videoPath)} - Post-processing error: ${error.message}`,
96
95
  error: true
97
96
  });
98
97
  }
@@ -117,7 +116,7 @@ async function processVideo(videoPath, config) {
117
116
  success: false,
118
117
  originalSize,
119
118
  optimizedSize: 0,
120
- message: `${chalk.red('āœ—')} ${path.basename(videoPath)} - ${errorMsg}`,
119
+ message: `āœ— ${path.basename(videoPath)} - ${errorMsg}`,
121
120
  error: true
122
121
  });
123
122
  });
@@ -129,7 +128,7 @@ async function processVideo(videoPath, config) {
129
128
  success: false,
130
129
  originalSize: 0,
131
130
  optimizedSize: 0,
132
- message: `${chalk.red('āœ—')} ${path.basename(videoPath)} - Error: ${error.message}`,
131
+ message: `āœ— ${path.basename(videoPath)} - Error: ${error.message}`,
133
132
  error: true
134
133
  });
135
134
  }