Skip to content

Commit

Permalink
d
Browse files Browse the repository at this point in the history
Signed-off-by: ClaytonTDM <[email protected]>
  • Loading branch information
ClaytonTDM committed Oct 9, 2024
1 parent 6d802af commit 5a7e2d6
Show file tree
Hide file tree
Showing 2 changed files with 70 additions and 173 deletions.
240 changes: 69 additions & 171 deletions .github/workflows/gif-to-webp-converter.js
Original file line number Diff line number Diff line change
@@ -1,185 +1,83 @@
const fs = require("fs").promises;
const path = require("path");
const { execFile } = require("child_process");
const util = require("util");
const execFilePromise = util.promisify(execFile);

const gifsiclePath = "gifsicle";
const gifskiPath = "gifski";
const convertPath = "convert";
const identifyPath = "identify";

const textFileExtensions = [
".md",
".json",
".js",
".css",
".html",
".htm",
".htm",
".txt",
".asp"
];

async function isAnimatedGif(filePath) {
try {
const { stdout } = await execFilePromise("identify", [
"-format",
"%n\n",
filePath,
]);
const frameCount = parseInt(stdout.trim(), 10);
return frameCount > 1;
} catch (error) {
console.error(`Error checking if GIF is animated: ${error.message}`);
return false;
}
try {
const { stdout } = await execFilePromise(gifsiclePath, ["--info", filePath]);
return stdout.includes("+ image #");
} catch (error) {
console.error(`Error checking if GIF is animated: ${error.message}`);
return false;
}
}

async function getGifLoopCount(filePath) {
try {
const { stdout } = await execFilePromise(identifyPath, [
"-format",
"%L",
filePath,
]);
const loopCount = parseInt(stdout.trim(), 10);
return isNaN(loopCount) ? 0 : loopCount;
} catch (error) {
console.error(`Error getting GIF loop count: ${error.message}`);
return 0;
}
try {
const { stdout } = await execFilePromise(gifsiclePath, ["--info", filePath]);
const loopMatch = stdout.match(/loop forever|loop count (\d+)/);
if (loopMatch) {
if (loopMatch[0] === "loop forever") {
return 0; // Infinite loop
} else {
return parseInt(loopMatch[1], 10);
}
}
return -1; // No explicit loop information found
} catch (error) {
console.error(`Error getting GIF loop count: ${error.message}`);
return -1; // Error case
}
}

async function convertGifToWebp(inputPath, outputPath) {
try {
const animated = await isAnimatedGif(inputPath);
if (animated) {
const loopCount = await getGifLoopCount(inputPath);
const loopArg =
loopCount === 0 ? "--repeat=0" : `--repeat=${loopCount - 1}`;
await execFilePromise(gifskiPath, [
"--quality=100",
loopArg,
"--output",
outputPath,
inputPath,
]);
} else {
await execFilePromise(convertPath, [
inputPath,
"-define",
"webp:lossless=true",
outputPath,
]);
}
console.log(`Converted ${inputPath} to ${outputPath}`);

// Delete the original GIF file
await fs.unlink(inputPath);
console.log(`Deleted original GIF: ${inputPath}`);

return true;
} catch (error) {
console.error(`Error converting ${inputPath}: ${error.message}`);
if (error.code === "ENOENT") {
console.error(
"The required executable was not found. Please make sure gifski and ImageMagick are installed and the paths are correct."
);
}
return false;
}
}

async function isTextFile(filePath) {
const ext = path.extname(filePath).toLowerCase();
if (textFileExtensions.includes(ext)) {
return true;
}

try {
const buffer = await fs.readFile(filePath);
const fileString = buffer.toString("utf8", 0, 1000); // Read first 1000 bytes
return /^[\x20-\x7E\r\n]*$/.test(fileString);
} catch (error) {
console.error(`Error checking if file is text: ${error.message}`);
return false;
}
}

async function replaceInFile(filePath, replacements) {
try {
if (await isTextFile(filePath)) {
let data = await fs.readFile(filePath, "utf8");
let changed = false;
for (const { oldName, newName } of replacements) {
const regex = new RegExp(oldName, "g");
const newData = data.replace(regex, newName);
if (newData !== data) {
data = newData;
changed = true;
}
}
if (changed) {
await fs.writeFile(filePath, data, "utf8");
console.log(`Updated references in ${filePath}`);
}
}
} catch (error) {
console.error(`Error updating ${filePath}: ${error.message}`);
}
}

async function processDirectory(directoryPath, allConvertedFiles = []) {
try {
const entries = await fs.readdir(directoryPath, {
withFileTypes: true,
});
const convertedFiles = [];

for (const entry of entries) {
const fullPath = path.join(directoryPath, entry.name);

if (entry.isDirectory()) {
await processDirectory(fullPath, allConvertedFiles);
} else if (entry.isFile()) {
if (path.extname(entry.name).toLowerCase() === ".gif") {
const outputPath = path.join(
path.dirname(fullPath),
`${path.basename(fullPath, ".gif")}.webp`
);
const success = await convertGifToWebp(
fullPath,
outputPath
);
if (success) {
convertedFiles.push({
oldName: entry.name,
newName: path.basename(outputPath),
});
}
} else {
await replaceInFile(fullPath, allConvertedFiles);
}
}
}

allConvertedFiles.push(...convertedFiles);

return allConvertedFiles;
} catch (error) {
console.error(
`Error processing directory ${directoryPath}: ${error.message}`
);
return allConvertedFiles;
}
}

async function main() {
const rootDirectory = ".";
console.log(`Starting conversion process in ${rootDirectory}`);
const convertedFiles = await processDirectory(rootDirectory);
console.log("Conversion process completed");
console.log(`Total files converted: ${convertedFiles.length}`);
}

main().catch((error) => console.error("An error occurred:", error));
try {
const animated = await isAnimatedGif(inputPath);
if (animated) {
const loopCount = await getGifLoopCount(inputPath);
let loopArg;
if (loopCount === 0) {
loopArg = "--repeat=0"; // Infinite loop
} else if (loopCount === -1) {
loopArg = "--repeat=1"; // Play once (no loop)
} else {
loopArg = `--repeat=${loopCount}`; // Specific number of loops
}

await execFilePromise(gifskiPath, [
"--quality=100",
loopArg,
"--output",
outputPath,
inputPath,
]);
} else {
// For non-animated GIFs, use ImageMagick's convert
await execFilePromise(convertPath, [
inputPath,
"-define",
"webp:lossless=true",
outputPath,
]);
}
console.log(`Converted ${inputPath} to ${outputPath}`);

// Delete the original GIF file
await fs.unlink(inputPath);
console.log(`Deleted original GIF: ${inputPath}`);

return true;
} catch (error) {
console.error(`Error converting ${inputPath}: ${error.message}`);
if (error.code === "ENOENT") {
console.error(
"The required executable was not found. Please make sure gifsicle, gifski, and ImageMagick are installed and the paths are correct."
);
}
return false;
}
}
3 changes: 1 addition & 2 deletions .github/workflows/gifToWebp.yml
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,7 @@
# - name: Install dependencies
# run: |
# sudo apt-get update
# sudo apt-get install -y imagemagick
# sudo apt-get install -y webp
# sudo apt-get install -y imagemagick webp gifsicle
# curl -L https://github.com/ImageOptim/gifski/releases/download/1.32.0/gifski_1.32.0-1_amd64.deb -o gifski.deb
# sudo dpkg -i gifski.deb
# rm gifski.deb
Expand Down

0 comments on commit 5a7e2d6

Please sign in to comment.