-
Notifications
You must be signed in to change notification settings - Fork 4
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Signed-off-by: ClaytonTDM <[email protected]>
- Loading branch information
1 parent
f4d2676
commit 0fbbd2f
Showing
3 changed files
with
248 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,44 @@ | ||
name: GIF to WebP Conversion | ||
|
||
on: | ||
push: | ||
branches: | ||
- main # or your default branch name | ||
|
||
jobs: | ||
convert-and-commit: | ||
runs-on: ubuntu-latest | ||
|
||
steps: | ||
- name: Checkout code | ||
uses: actions/checkout@v3 | ||
|
||
- name: Set up Node.js | ||
uses: actions/setup-node@v3 | ||
with: | ||
node-version: '18' | ||
|
||
- name: Install dependencies | ||
run: | | ||
sudo apt-get update | ||
sudo apt-get install -y imagemagick | ||
sudo apt-get install -y webp | ||
curl -L https://github.com/ImageOptim/gifski/releases/download/1.11.0/gifski_1.11.0_amd64.deb -o gifski.deb | ||
sudo dpkg -i gifski.deb | ||
- name: Run conversion script | ||
run: | | ||
node gif-to-webp-converter.js | ||
- name: Commit changes | ||
run: | | ||
git config --local user.email "[email protected]" | ||
git config --local user.name "GitHub Action" | ||
git add -A | ||
git diff --quiet && git diff --staged --quiet || git commit -m "Convert GIF files to WebP" | ||
- name: Push changes | ||
uses: ad-m/github-push-action@master | ||
with: | ||
github_token: ${{ secrets.GITHUB_TOKEN }} | ||
branch: ${{ github.ref }} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,4 +1,3 @@ | ||
convert.js | ||
ffmpeg* | ||
node_modules/ | ||
package.json | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,204 @@ | ||
const fs = require("fs").promises; | ||
const path = require("path"); | ||
const { execFile } = require("child_process"); | ||
const util = require("util"); | ||
const execFilePromise = util.promisify(execFile); | ||
|
||
const gifskiPath = "gifski"; | ||
const convertPath = "convert"; | ||
const identifyPath = "identify"; | ||
|
||
const textFileExtensions = [ | ||
".txt", | ||
".md", | ||
".json", | ||
".xml", | ||
".html", | ||
".htm", | ||
".css", | ||
".js", | ||
".ts", | ||
".jsx", | ||
".tsx", | ||
".vue", | ||
".php", | ||
".py", | ||
".rb", | ||
".java", | ||
".c", | ||
".cpp", | ||
".h", | ||
".cs", | ||
".go", | ||
".rs", | ||
".swift", | ||
".kt", | ||
".kts", | ||
".scala", | ||
".sh", | ||
".bash", | ||
".yaml", | ||
".yml", | ||
".toml", | ||
".ini", | ||
".cfg", | ||
".conf", | ||
]; | ||
|
||
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; | ||
} | ||
} | ||
|
||
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; | ||
} | ||
} | ||
|
||
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}`); | ||
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}`); | ||
await processDirectory(rootDirectory); | ||
console.log("Conversion process completed"); | ||
} | ||
|
||
main().catch((error) => console.error("An error occurred:", error)); |