From 382d3b18dd24879782cd27dd367fc2fc2a895cb4 Mon Sep 17 00:00:00 2001 From: ghostboats <106226990+ghostboats@users.noreply.github.com> Date: Mon, 22 Jul 2024 08:29:44 -0500 Subject: [PATCH] convewr vdoe added --- commands/convertVideoToGif.js | 143 ++++++++++++++++++++++++++++++++++ extension.js | 3 + package-lock.json | 119 ++++++++++++++++++++++++++++ package.json | 2 + 4 files changed, 267 insertions(+) create mode 100644 commands/convertVideoToGif.js diff --git a/commands/convertVideoToGif.js b/commands/convertVideoToGif.js new file mode 100644 index 00000000..37acb059 --- /dev/null +++ b/commands/convertVideoToGif.js @@ -0,0 +1,143 @@ +const vscode = require('vscode'); +const fs = require('fs'); +const path = require('path'); +const ffmpeg = require('fluent-ffmpeg'); +const ffmpegPath = require('ffmpeg-static'); + +const { getConfig } = require('../support_files/config'); +const { rootModPath } = getConfig(); + +console.log("FFmpeg Path:", ffmpegPath); +console.log("Type of FFmpeg Path:", typeof ffmpegPath); + +if (typeof ffmpegPath === 'string') { + ffmpeg.setFfmpegPath(ffmpegPath); +} else { + console.error("FFmpeg Path is not a string"); +} + +// Command to open the video conversion webview +const convertVideoToGifCommand = vscode.commands.registerCommand('bg3-mod-helper.convertVideoToGif', async function () { + const panel = vscode.window.createWebviewPanel( + 'videoConversion', + 'Video Conversion', + vscode.ViewColumn.One, + { enableScripts: true } + ); + + const videoFiles = await getAllVideoFiles(rootModPath); + + panel.webview.html = getWebviewContent(videoFiles); + + // Handle messages from the webview + panel.webview.onDidReceiveMessage(async message => { + switch (message.command) { + case 'convert': + await convertVideoToGifFile(message.videoPath, message.gifPath); + break; + case 'convertAll': + for (const video of videoFiles) { + const gifPath = video.replace(path.extname(video), '.gif'); + await convertVideoToGifFile(video, gifPath); + } + break; + case 'selectFile': + const options = { + canSelectMany: false, + openLabel: 'Select a video file', + filters: { 'Video files': ['mp4', 'mkv', 'avi', 'mov'] } + }; + const fileUri = await vscode.window.showOpenDialog(options); + if (fileUri && fileUri[0]) { + const videoPath = fileUri[0].fsPath; + const gifPath = videoPath.replace(path.extname(videoPath), '.gif'); + await convertVideoToGifFile(videoPath, gifPath); + } + break; + } + }); +}); + +async function getAllVideoFiles(dir) { + let files = []; + const items = await fs.promises.readdir(dir, { withFileTypes: true }); + for (const item of items) { + const fullPath = path.join(dir, item.name); + if (item.isDirectory()) { + files = files.concat(await getAllVideoFiles(fullPath)); + } else if (/\.(mp4|mkv|avi|mov)$/i.test(item.name)) { + files.push(fullPath); + } + } + return files; +} + +function getWebviewContent(videoFiles) { + const videoFileItems = videoFiles.map(file => ` + + ${path.basename(file)} + ${file} + + + `).join(''); + + return ` + + +

Video Conversion

+ + + + + + + ${videoFileItems} +
NamePathAction
+ + + + + + `; +} + +async function convertVideoToGifFile(inputPath, outputPath) { + return new Promise((resolve, reject) => { + const normalizedInput = path.normalize(inputPath); + const normalizedOutput = path.normalize(outputPath); + + console.log("Normalized Input Path:", normalizedInput); + console.log("Normalized Output Path:", normalizedOutput); + + ffmpeg(normalizedInput) + .outputOptions([ + '-vf', 'fps=24', // Increase fps for smoother animation + '-gifflags', 'transdiff', + '-y', // Overwrite output files without asking + '-q:v', '5' // Set quality level (lower is better, 0 is the best quality) + ]) + .save(normalizedOutput) + .on('end', () => { + vscode.window.showInformationMessage(`GIF created: ${normalizedOutput}`); + resolve(); + }) + .on('error', (err) => { + vscode.window.showErrorMessage(`Error: ${err.message}`); + reject(err); + }); + }); +} + + +module.exports = { convertVideoToGifCommand }; diff --git a/extension.js b/extension.js index 939289b6..aee2baff 100644 --- a/extension.js +++ b/extension.js @@ -39,6 +39,7 @@ let packModImport, xmlMergerCommand, symlinkerCommand, indentXmlFilesCommand, + convertVideoToGifCommand, debugCommand, debug2Command, unpackGameDataCommand, @@ -108,6 +109,7 @@ function setCommands() { organizeDataFilesCommand = require('./commands/organizeDataFiles'); symlinkerCommand = require('./commands/symlinker'); indentXmlFilesCommand = require('./commands/indentXmlFiles'); + convertVideoToGifCommand = require('./commands/convertVideoToGif'); // debug commands debugCommand = require('./commands/debug'); @@ -226,6 +228,7 @@ function aSimpleDataProvider() { { label: 'Generate Folder Structure', command: 'bg3-mod-helper.createModTemplate' }, { label: 'Atlas Generator (Supply a folder of icons to make an atlas and its corresponding .dds with those icons, as well as its merged.lsx)', command: 'bg3-mod-helper.createAtlas' }, { label: 'BBCode/Markdown Editor ', command: 'bg3-mod-helper.textEditorTool'}, + { label: 'Convert Video to GIF', command: 'bg3-mod-helper.convertVideoToGif' }, { label: 'Version Generator', command: 'bg3-mod-helper.versionGenerator' }, { label: 'Merge Xmls', command: 'bg3-mod-helper.xmlMerger' }, { label: 'Add/Remove Symlink (in development)', command: 'bg3-mod-helper.symlinker' }, diff --git a/package-lock.json b/package-lock.json index face3d44..5bae7f0a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,6 +9,8 @@ "version": "2.2.53", "license": "LGPL-3.0-or-later", "dependencies": { + "ffmpeg-static": "^5.2.0", + "fluent-ffmpeg": "^2.1.3", "jszip": "^3.10.1", "log4js": "^6.9.1", "magickwand.js": "^1.1.0", @@ -23,6 +25,20 @@ "vscode": "^1.86.0" } }, + "node_modules/@derhuerst/http-basic": { + "version": "8.2.4", + "resolved": "https://registry.npmjs.org/@derhuerst/http-basic/-/http-basic-8.2.4.tgz", + "integrity": "sha512-F9rL9k9Xjf5blCz8HsJRO4diy111cayL2vkY2XE4r4t3n0yPXVYy3KD3nJ1qbrSn9743UWSXH4IwuCa/HWlGFw==", + "dependencies": { + "caseless": "^0.12.0", + "concat-stream": "^2.0.0", + "http-response-object": "^3.0.1", + "parse-cache-control": "^1.0.1" + }, + "engines": { + "node": ">=6.0.0" + } + }, "node_modules/@emnapi/runtime": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.2.0.tgz", @@ -200,6 +216,11 @@ "node": ">=14" } }, + "node_modules/@types/node": { + "version": "10.17.60", + "resolved": "https://registry.npmjs.org/@types/node/-/node-10.17.60.tgz", + "integrity": "sha512-F0KIgDJfy2nA3zMLmWGKxcH2ZVEtCZXHHdOQs2gSaQ27+lNeEfGxzkIw90aXswATX7AZ33tahPbzy6KAfUreVw==" + }, "node_modules/abbrev": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", @@ -269,6 +290,11 @@ "node": ">=10" } }, + "node_modules/async": { + "version": "0.2.10", + "resolved": "https://registry.npmjs.org/async/-/async-0.2.10.tgz", + "integrity": "sha512-eAkdoKxU6/LkKDBzLpT+t6Ff5EtfSF4wx1WfJiPEEV7WNLnDaRXk0oVysiEPm262roaachGexwUv94WhSgN5TQ==" + }, "node_modules/balanced-match": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", @@ -284,6 +310,11 @@ "concat-map": "0.0.1" } }, + "node_modules/buffer-from": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", + "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==" + }, "node_modules/cacache": { "version": "18.0.2", "resolved": "https://registry.npmjs.org/cacache/-/cacache-18.0.2.tgz", @@ -376,6 +407,11 @@ "node": ">=16 || 14 >=14.17" } }, + "node_modules/caseless": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", + "integrity": "sha512-4tYFyifaFfGacoiObjJegolkwSU4xQNGbVgUiNYVUxbQ2x2lUsFvY4hVgVzGiIe6WLOPqycWXA40l+PWsxthUw==" + }, "node_modules/chalk": { "version": "5.3.0", "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.3.0.tgz", @@ -435,6 +471,20 @@ "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", "license": "MIT" }, + "node_modules/concat-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-2.0.0.tgz", + "integrity": "sha512-MWufYdFw53ccGjCA+Ol7XJYpAlW6/prSMzuPOTRnJGcGzuhLn4Scrz7qf6o8bROZ514ltazcIFJZevcfbo0x7A==", + "engines": [ + "node >= 6.0" + ], + "dependencies": { + "buffer-from": "^1.0.0", + "inherits": "^2.0.3", + "readable-stream": "^3.0.2", + "typedarray": "^0.0.6" + } + }, "node_modules/console-control-strings": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz", @@ -598,11 +648,54 @@ "node": "^12.20 || >= 14.13" } }, + "node_modules/ffmpeg-static": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ffmpeg-static/-/ffmpeg-static-5.2.0.tgz", + "integrity": "sha512-WrM7kLW+do9HLr+H6tk7LzQ7kPqbAgLjdzNE32+u3Ff11gXt9Kkkd2nusGFrlWMIe+XaA97t+I8JS7sZIrvRgA==", + "hasInstallScript": true, + "dependencies": { + "@derhuerst/http-basic": "^8.2.0", + "env-paths": "^2.2.0", + "https-proxy-agent": "^5.0.0", + "progress": "^2.0.3" + }, + "engines": { + "node": ">=16" + } + }, "node_modules/flatted": { "version": "3.3.1", "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.1.tgz", "integrity": "sha512-X8cqMLLie7KsNUDSdzeN8FYK9rEt4Dt67OsG/DNGnYTSDBG4uFAJFBnUeiV+zCVAvwFy56IjM9sH51jVaEhNxw==" }, + "node_modules/fluent-ffmpeg": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/fluent-ffmpeg/-/fluent-ffmpeg-2.1.3.tgz", + "integrity": "sha512-Be3narBNt2s6bsaqP6Jzq91heDgOEaDCJAXcE3qcma/EJBSy5FB4cvO31XBInuAuKBx8Kptf8dkhjK0IOru39Q==", + "dependencies": { + "async": "^0.2.9", + "which": "^1.1.1" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/fluent-ffmpeg/node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==" + }, + "node_modules/fluent-ffmpeg/node_modules/which": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", + "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "which": "bin/which" + } + }, "node_modules/foreground-child": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.1.1.tgz", @@ -762,6 +855,14 @@ "node": ">= 14" } }, + "node_modules/http-response-object": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/http-response-object/-/http-response-object-3.0.2.tgz", + "integrity": "sha512-bqX0XTF6fnXSQcEJ2Iuyr75yVakyjIDCqroJQ/aHfSdlM743Cwqoi2nDYMzLGWUcuTWGWy8AAvOKXTfiv6q9RA==", + "dependencies": { + "@types/node": "^10.0.3" + } + }, "node_modules/https-proxy-agent": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", @@ -1431,6 +1532,11 @@ "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz", "integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==" }, + "node_modules/parse-cache-control": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parse-cache-control/-/parse-cache-control-1.0.1.tgz", + "integrity": "sha512-60zvsJReQPX5/QP0Kzfd/VrpjScIQ7SHBW6bFCYfEP+fp0Eppr1SHhIO5nd1PjZtvclzSzES9D/p5nFJurwfWg==" + }, "node_modules/path-is-absolute": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", @@ -1484,6 +1590,14 @@ "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==" }, + "node_modules/progress": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz", + "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==", + "engines": { + "node": ">=0.4.0" + } + }, "node_modules/promise-retry": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/promise-retry/-/promise-retry-2.0.1.tgz", @@ -1785,6 +1899,11 @@ "integrity": "sha512-xNvxJEOUiWPGhUuUdQgAJPKOOJfGnIyKySOc09XkKsgdUV/3E2zvwZYdejjmRgPCgcym1juLH3226yA7sEFJKQ==", "license": "0BSD" }, + "node_modules/typedarray": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", + "integrity": "sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA==" + }, "node_modules/unique-filename": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/unique-filename/-/unique-filename-3.0.0.tgz", diff --git a/package.json b/package.json index b1fd7259..ad027731 100644 --- a/package.json +++ b/package.json @@ -494,6 +494,8 @@ "dependencies": "npm install" }, "dependencies": { + "ffmpeg-static": "^5.2.0", + "fluent-ffmpeg": "^2.1.3", "jszip": "^3.10.1", "log4js": "^6.9.1", "magickwand.js": "^1.1.0",