Skip to content
Snippets Groups Projects
Commit 5a11f559 authored by Edu Almas's avatar Edu Almas
Browse files

add image compression script copied from...

add image compression script copied from git@gitlab.fabcloud.org:academany/fabacademy/2022/labs/barcelona/students/eduard-almasque.git
parent 59a56138
No related branches found
No related tags found
No related merge requests found
Pipeline #314610 passed
This diff is collapsed.
{
"name": "fablab-documentation",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "jest",
"test:watch": "jest --watch",
"build": "tsc",
"pack": "node dist/app.js"
},
"keywords": [],
"author": "",
"license": "ISC",
"dependencies": {
"imagemagick": "^0.1.3",
"require": "^2.4.20",
"ts-node": "^10.4.0"
},
"devDependencies": {
"@types/jest": "^27.4.0",
"@types/node": "^17.0.12",
"jest": "^27.4.7",
"mocha": "^9.2.0",
"ts-jest": "^27.1.3",
"typescript": "^4.5.5"
}
}
import {processFolder} from "./pathProcessor";
let basePaths = [
// "./test-resources" // use this to validate/inspect compression results with sample files
"./docs/assignments",
"./docs/final-project",
"./docs/about"
];
(async function app() {
for (let i = 0; i < basePaths.length; i++) {
await processFolder(basePaths[i]);
}
})();
const fs = require('fs');
const path = require("path")
export function listAllFiles(dirPath: string, filenameCriteria: (filename: string) => boolean): string[] {
return listAllFilesRecursively(dirPath, [], filenameCriteria);
}
function listAllFilesRecursively(dirPath: string, arrayOfFiles: string[], isValidFileType: (filename: string) => boolean): string[] {
try {
let files: string[] = fs.readdirSync(dirPath);
files.forEach(function (filename) {
if (filename.toLowerCase() === "assets") {
//console.debug("skipping assets directory: " + dirPath + "/" + filename);
} else if (fs.statSync(dirPath + "/" + filename).isDirectory()) {
arrayOfFiles = listAllFilesRecursively(dirPath + "/" + filename, arrayOfFiles, isValidFileType);
} else {
if (isValidFileType(filename)) {
arrayOfFiles.push(path.join(dirPath, "/", filename));
}
}
})
return arrayOfFiles;
} catch (err) {
console.error(err);
return [];
}
}
export function imageFiles(filename: string): boolean {
let validExtensions = [".jpg", ".jpeg", ".svg", ".png"];
return endsWithExtension(validExtensions, filename);
}
export function videoFiles(filename: string): boolean {
let validExtensions = [".mp4", ".gif", ".mkv", ".m4v"];
return endsWithExtension(validExtensions, filename);
}
function endsWithExtension(validExtensions: string[], filename: string) {
return validExtensions.some(
(extension: string) => {
return filename.toLowerCase().endsWith(extension.toLowerCase());
}
);
}
\ No newline at end of file
import {processJpg, processPng, processSvg} from "./imageProcessor";
const fs = require('fs');
const path = require('path');
const originalPrefix = "original.";
export async function processFile(fullPath: string): Promise<void> {
if (isFileProcessed(fullPath)) {
return;
}
console.debug(`${fullPath} - compressing`);
const fileExtension = path.extname(fullPath);
switch (fileExtension) {
case ".jpeg":
case ".jpg":
markFileAsProcessed(fullPath);
await processJpg(fullPath);
break;
case ".png":
markFileAsProcessed(fullPath);
await processPng(fullPath);
break;
case ".svg":
markFileAsProcessed(fullPath);
await processSvg(fullPath);
break;
default:
console.warn(`unknown file type ${fileExtension}`);
}
if (await hasIncreasedInSizeAfterProcessing(fullPath)) {
undoProcessing(fullPath);
}
}
async function hasIncreasedInSizeAfterProcessing(fullPath: string): Promise<boolean> {
const basePath = path.dirname(fullPath);
const fileName = path.basename(fullPath);
let compressedFilename = basePath + "/" + fileName;
let originalFilename = basePath + "/" + originalPrefix + fileName;
const [compressedSize, originalSize] = await Promise.all([
fileSize(compressedFilename),
fileSize(originalFilename)
]);
return compressedSize >= originalSize;
}
async function fileSize(filename: string): Promise<number> {
return new Promise((resolve, reject) => {
fs.open(filename, "r", function (err: NodeJS.ErrnoException | null, fd: number) {
if (err) {
reject(err);
} else {
let fileSize = fs.fstatSync(fd).size;
fs.close(fd, () => {
resolve(fileSize);
});
}
});
});
}
function markFileAsProcessed(fullPath: string): void {
const basePath = path.dirname(fullPath);
const fileName = path.basename(fullPath);
let compressedFilename = basePath + "/" + fileName;
let originalFilename = basePath + "/" + originalPrefix + fileName;
fs.copyFileSync(compressedFilename, originalFilename)
}
function undoProcessing(fullPath: string): void {
const basePath = path.dirname(fullPath);
const fileName = path.basename(fullPath);
let compressedFilename = basePath + "/" + fileName;
let originalFilename = basePath + "/" + originalPrefix + fileName;
fs.copyFileSync(originalFilename, compressedFilename)
}
function isFileProcessed(fullPath: string): boolean {
const basePath = path.dirname(fullPath);
const fileName = path.basename(fullPath);
return fileName?.startsWith(originalPrefix) || fs.existsSync(basePath + "/" + originalPrefix + fileName);
}
const im = require('imagemagick');
const maxImageWidth = 1024
async function imageWidth(fullPath: string): Promise<number> {
return new Promise(async (resolve, reject) => {
await im.identify(fullPath, function (err: any, features: any) {
if (err) reject(err);
if (features.width !== undefined) {
resolve(features.width);
} else {
reject(new Error(`cannot find property 'width' for ${fullPath}`))
}
});
});
}
async function processImage(compressionQuality: number, fullPath: string): Promise<any> {
const actualWidth = await imageWidth(fullPath);
if (actualWidth > maxImageWidth) {
let compressionConfig = {
srcPath: fullPath,
dstPath: fullPath,
width: maxImageWidth,
quality: compressionQuality
};
return new Promise(async (resolve, reject) => {
await im.resize(compressionConfig, function (err: any, stdout: any, stderr: any) {
if (err) throw err;
resolve(fullPath);
});
});
} else {
return Promise.resolve();
}
}
export async function processJpg(fullPath: string) {
return await processImage(0.9, fullPath);
}
export async function processPng(fullPath: string) {
return await processImage(0.9, fullPath);
}
export async function processSvg(fullPath: string) {
return await processImage(0.9, fullPath);
}
\ No newline at end of file
import {imageFiles, listAllFiles} from "./fileManager";
import {processFile} from "./fileProcessor";
export async function processFolder(basePath: string): Promise<any> {
if (basePath.endsWith("/")) {
console.error(`path "${basePath}" must not end in '/'`);
} else {
const allImages = listAllFiles(basePath, imageFiles);
const allPromises = allImages.map(async (filename: string) => {
return new Promise(async (resolve, reject) => {
resolve(await processFile(filename));
});
});
return Promise.all(allPromises);
}
}
\ No newline at end of file
{
"compilerOptions": {
"target": "es5",
"module": "commonjs",
"declaration": true,
"outDir": "./dist",
"strict": true
},
"include": ["src/**/*.ts"],
"exclude": [
"node_modules",
"docs",
"resources",
]
}
\ No newline at end of file
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment