multifile support, added all filetypes, precision

This commit is contained in:
Djkato 2022-05-03 23:27:58 +02:00
parent 981f8804f9
commit 57fdf245e1
11 changed files with 900 additions and 431 deletions

6
.gitignore vendored
View file

@ -4,6 +4,8 @@ node_modules
*.webm
*.exe
*.zip
.gitignore
*.webp
*.ogg
settings.json
*test*
/testing ground/
/bin/index regular terminal.js

2
.npmrc Normal file
View file

@ -0,0 +1,2 @@
loglevel=silent

View file

@ -6,4 +6,15 @@ Easy to use: Just drag a file on the executable and off it goes!
![explorer_e0Y0mYI4fH](https://user-images.githubusercontent.com/25299243/166175734-8e0a8783-3bac-4617-8762-db0ddfaa761a.gif)
How to install(Windows with binaries):
1. make a folder in `C:\Program Files` called "DMC", put release binaries and [ffmpeg windows full-build executables](https://github.com/GyanD/codexffmpeg/releases/) into `C:\Program Files\DMC\`
2. Add ffmpeg to PATH: `[Win BTN] + R`, type `SystemPropertiesAdvanced`, click `Environment Variables`, under "User variables for (user)" find variable Path, click on it and edit, in the now open window click `new`, and paste `C:\Program Files\DMC\`.
3. If the command `ffmpeg` in cmd works, you can now drag and drop files onto the binary and have it work!
4. to set performance preset, doubleclick the binary
How to install(Windows, Linux, MacOS):
1. get node.js from [here](https://nodejs.org)
2. clone repo into any folder you like.
3. run `npm install --save` to get all dependencies
4. run `npm install -g {PATH_TO_PROJECT_FOLDER}`
*might have bugs, currently videos only and windows only. Future plans will include audio: .ogg, and photos: .webm*

121
bin/index.js Normal file
View file

@ -0,0 +1,121 @@
import fs from 'fs'
import { Encoder } from "../lib/encoder.js"
import { UI } from "../lib/ui.js"
import termkit from "terminal-kit"
import { SettingsManager } from "../lib/settingsManager.js"
import path from "path"
//get settings
let settings = new SettingsManager()
await settings.start()
let resolve = path.resolve
let term = termkit.terminal
const ui = new UI(settings.settings, settings.currentSetting)
/**
* TODO : Adapt audio quality as well to accomodate long videos(Currently 5m is too much)
* FIND A WAY TO COMPILE THIS:..
*/
const inputList = process.argv.slice(2)
//if launched without params
if (!inputList[0]) {
ui.startMenu() //stops program here
}
//Parse file inputs (n Drag n drop or arguments)
let filePaths = [], fileNames = [], fileTypes = []
let presetIndexArg = undefined
//if preset argument go through list from 2 and add argument
if (inputList[0] == "-preset") {
presetIndexArg = inputList[1]
for (let i = 2; i < inputList.length; i++) {
let file
file = resolve(inputList[i])
filePaths.push(file)
file = file.split("\\")
file = file[file.length - 1]
file = file.split(".")
fileTypes.push(file[1])
fileNames.push(file[0])
}
}
else {
for (let i = 0; i < inputList.length; i++) {
let file
file = resolve(inputList[i])
filePaths.push(file)
file = file.split("\\")
file = file[file.length - 1]
file = file.split(".")
fileTypes.push(file[1])
fileNames.push(file[0])
}
}
main()
async function main() {
//file checks
let isListEncodable = true
//check if all files exist
for (let i = 0; i < filePaths.length; i++) {
if (!fs.existsSync(filePaths[i])) {
term.italic(`${filePaths[i]}`).bold.red(" <- Path or File doesn't exist\n")
term.grey("press enter to exit...")
isListEncodable = false
term.inputField(function () { process.exit() })
}
}
//check if all files are valid formats
if (isListEncodable) {
for (let i = 0; i < filePaths.length; i++) {
if (fileTypes[i] == "jpg" || fileTypes[i] == "JPG" || fileTypes[i] == "png" || fileTypes[i] == "PNG" || fileTypes[i] == "webp" ||
fileTypes[i] == "webm" || fileTypes[i] == "mp4" || fileTypes[i] == "mov" || fileTypes[i] == "mkv" || fileTypes[i] == "avi" ||
fileTypes[i] == "ogg" || fileTypes[i] == "mp3" || fileTypes[i] == "aiff" || fileTypes[i] == "wav" || fileTypes[i] == "flac") {
}
else {
term.italic(`${fileTypes[i]}`).bold.red(` <- Unsupported format\n`)
term.grey("press enter to exit...")
isListEncodable = false
term.inputField(function () { process.exit() })
}
}
}
//start encoding all
if (isListEncodable) {
let encoder = []
for (let i = 0; i < filePaths.length; i++) {
encoder.push(new Encoder(settings.settings, settings.currentSetting, presetIndexArg))
console.log(`Encoding with "${settings.currentSetting.name}" preset...`)
if (fileTypes[i] == "jpg" || fileTypes[i] == "JPG" || fileTypes[i] == "png" || fileTypes[i] == "PNG" || fileTypes[i] == "webp") {
ui.newBar(await encoder[i].encodePicture(filePaths[i], fileNames[i]))
ui.updateBar("time=00:00:01", i, false, true)
encoder[i].on("close", () => { ui.encodeFinished(i) })
}
else if (fileTypes[i] == "webm" || fileTypes[i] == "mp4" || fileTypes[i] == "mov" || fileTypes[i] == "mkv" || fileTypes[i] == "avi") {
ui.newBar(await encoder[i].encodeVideo(filePaths[i], fileNames[i]))
encoder[i].on("update", (chunk) => { ui.updateBar(chunk, i) })
encoder[i].on("close", () => { ui.encodeFinished(i) })
}
else if (fileTypes[i] == "ogg" || fileTypes[i] == "mp3" || fileTypes[i] == "aiff" || fileTypes[i] == "wav" || fileTypes[i] == "flac") {
ui.newBar(await encoder[i].encodeAudio(filePaths[i], fileNames[i]))
encoder[i].on("update", (chunk) => { ui.updateBar(chunk, i, false) })
encoder[i].on("close", () => { ui.encodeFinished(i) })
}
}
}
}

26
fieldTest.cjs Normal file
View file

@ -0,0 +1,26 @@
const child_process = require("child_process")
const FileHound = require('filehound')
let exec = child_process.exec
let command = "npm run start"
let testFiles = FileHound.create()
.path("testing ground")
.findSync()
testFiles.forEach((value, index) => {
console.log(value, index)
command += ` "${value}"`
})
console.log(command)
let test = exec(command)
test.stdout.on("data", (chunk) => {
console.log(chunk)
})
test.stderr.on("data", (chunk) => {
console.log(chunk)
})
test.on("close", () => {
console.log("finished")
})

423
index.js
View file

@ -1,423 +0,0 @@
const fs = require('fs')
const { exec, execSync } = require('child_process')
const cliProgress = require('cli-progress')
const term = require("terminal-kit").terminal
/**
* TODO : Adapt audio quality as well to accomodate long videos(Currently 5m is too much)
* FIND A WAY TO COMPILE THIS:..
*/
//Parse file inputs (Drag n drop or arguments)
inputList = process.argv.slice(2)
input = inputList[0]
let file, fileType, bar1
//if launched without params
if (!input) {
startMenu()
}
else {
file = input.split("\\")
file = file[file.length - 1]
fileType = file.split(".")[1]
if (!fs.existsSync(input)) {
term.italic(`${input}`).bold.red(" <- Path or File doesn't exist\n")
term.grey("press enter to exit...")
term.inputField(function () { process.exit() })
}
else {
bar1 = new cliProgress.SingleBar({
synchronousUpdate: true,
align: "left",
hideCursor: true
}, cliProgress.Presets.shades_classic)
if (fileType == "jpg" || fileType == "JPG" || fileType == "png" || fileType == "PNG" || fileType == "webp") {
encodePicture(input, file.split(".")[0])
}
else if (fileType == "webm" || fileType == "mp4" || fileType == "mov" || fileType == "mkv" || fileType == "avi") {
encodeVideo(input, file.split(".")[0])
}
else if ("ogg" || "mp3" || "aiff" || "wav" || "flac") {
encodeAudio(input, file.split(".")[0])
}
else {
term.italic(`${file}`).bold.red(` <- Unsupported format\n`)
term.grey("press enter to exit...")
term.inputField(function () { process.exit() })
}
}
}
async function encodeVideo(path, out) {
//create progress bar
const [command, presetName, duration, isTwoPass] = await constructVideoCommand(path, out)
bar1.start(duration, 0, { speed: "N/A" })
let isPastHalf = false
let encoder = exec(command)
encoder.stderr.on("data", (chunk) => {
currentTime = chunk.split("time=")[1]?.split(" ")[0]
if (currentTime) {
const arr = currentTime.split(":") // splitting the string by colon
let seconds = Number.parseFloat(arr[0] * 3600 + arr[1] * 60 + (+arr[2])) // converting to s
console.clear()
//If 2nd pass add that portion in
console.log(`Encoding ${out}.webm with "${presetName}" preset...`)
if (isTwoPass) {
if (seconds / 2 >= (duration - 0.2) / 2) isPastHalf = true
isPastHalf ? bar1.update(Math.round(seconds * 50) / 100 + (duration / 2)) : bar1.update(Math.round(seconds * 50) / 100)
}
else {
bar1.update(Math.round(seconds * 100) / 100)
}
}
})
encoder.on("close", () => {
console.clear()
bar1.stop()
fs.rm("ffmpeg2pass-0.log", (error) => { error })
term.bold.green("Finished!\n")
term.grey("press enter to exit...\n")
term.inputField(() => { process.exit() })
})
}
async function encodeAudio(path, out) {
let [duration, resolution] = await getDurationAndResolution(path)
const bitrateLimit = Math.round(62000 / duration)
bar1.start(duration, 0, { speed: "N/A" })
const encoder = exec(`ffmpeg -y -i "${path}" -c:a libvorbis -b:a ${bitrateLimit}k ${out}.ogg`)
encoder.stderr.on("data", (chunk) => {
currentTime = chunk.split("time=")[1]?.split(" ")[0]
if (currentTime) {
const arr = currentTime.split(":")
let seconds = Number.parseFloat(arr[0] * 3600 + arr[1] * 60 + (+arr[2])) // converting to s
console.clear()
console.log(`Encoding ${out}.ogg`)
bar1.update(Math.round(seconds * 100) / 100)
}
})
encoder.on("close", () => {
console.clear()
term.bold.green("Finished!\n")
term.grey("press enter to exit...\n")
term.inputField(() => { process.exit() })
})
}
async function encodePicture(path, out) {
const encoder = exec(`ffmpeg -y -i "${path}" -qscale 80 -compression_level 6 ${out}.webp`)
encoder.stderr.on("data", (chunk) => {
console.clear()
term.yellow(`Encoding ${out}.webp...`)
})
encoder.on("close", () => {
console.clear()
term.bold.green("Finished!\n")
term.grey("press enter to exit...\n")
term.inputField(() => { process.exit() })
})
}
async function constructVideoCommand(path, out) {
//gets settings file, if doesnt exist makes a new file and uses those defaults
let settings = await getSettings().catch(async (err) => {
settings = undefined
})
if (!settings) settings = await makeNewSettingsFile()
settings = JSON.parse(settings.toString())
settings = settings.presets[settings.currentSetting]
let [duration, resolutionHeight] = await getDurationAndResolution(path)
//Calculates video bitrate to fit right under 8mb @224kb vorbis audio bitrate
const bitrateLimit = Math.round((62000 - (224 * duration)) / duration)
let command = ""
let crfIndex = 0
let isTwoPass = true
while (resolutionHeight > settings.crfMap[crfIndex].resolution) {
crfIndex++
//if the resolution is still higher, just use highest res
if (!settings.crfMap[crfIndex]?.resolution) {
crfIndex--
break
}
}
for (pass = 1; pass <= 2; pass++) {
command += `ffmpeg -y -i "${path}" -vcodec libvpx-vp9 -acodec libvorbis -qscale:a 7 `
command += `-deadline ${settings.deadline} `
command += `-cpu-used ${settings.cpuUsed} `
if (settings?.minrate) {
command += `-b:v ${Math.round(bitrateLimit * 0.95)}k `
command += `-minrate ${Math.round(bitrateLimit / 100 * settings.minrate)}k `
command += `-maxrate ${bitrateLimit}k `
}
else {
command += `-b:v ${bitrateLimit}k `
command += `-crf ${settings.crfMap[crfIndex].crf} `
}
//realtime doesnt support two pass
if (settings.deadline == "realtime") {
command += `-row-mt 1 "${out}.webm"`
isTwoPass = false
break
}
pass == 1 ? command += `-pass 1 -row-mt 1 -f webm NUL && ` : command += `-pass 2 -row-mt 1 "${out}.webm" `
}
return [command, settings.name, duration, isTwoPass]
}
async function getDurationAndResolution(file) {
let query = await ffprobe(file)
//duration in seconds
duration = query.split("Duration: ")[1].split(",")[0]
const arr = duration.split(":") // splitting the string by colon
const seconds = arr[0] * 3600 + arr[1] * 60 + (+arr[2]) // converting to s
//resolution height
resolutionHeight = query.split("Stream #0:0")[1]?.split(",")[2].split(" ")[1].split("x")[1]
return [Number.parseFloat(seconds), resolutionHeight]
}
function ffprobe(file) {
return new Promise((resolve, reject) => {
exec(`ffprobe "${file}"`, (error, stdout, stderr) => {
resolve(stderr)
})
})
}
function getSettings() {
return new Promise((resolve, reject) => {
getSettings = fs.readFile("settings.json", (err, data) => {
resolve(data)
reject(err)
})
})
}
async function startMenu() {
console.clear()
//gets settings file, if doesnt exist makes a new file and uses those defaults
let settings = await getSettings().catch(async (err) => {
settings = undefined
})
if (!settings) settings = await makeNewSettingsFile()
settings = JSON.parse(settings.toString())
let menu = []
for (i = 0; i < settings.presets.length; i++) {
menu.push(`${i}. ${settings.presets[i].name}`)
}
term.italic("How to convert: [app] [filename.extension]\n")
term.yellow("Hello! This menu is for selecting performance/speed preset.\n")
term.yellow("Currently using ").bgMagenta(`"${settings.presets[settings.currentSetting].name}"`).yellow(" preset")
term.singleColumnMenu(menu, (error, response) => {
settings.currentSetting = response.selectedIndex
fs.writeFileSync("settings.json", JSON.stringify(settings))
term.green("\n Using").green.bold(` ${settings.presets[settings.currentSetting].name} `).green("setting\n")
term.grey("Press enter to exit...")
term.inputField(() => { process.exit() })
})
}
function makeNewSettingsFile() {
const settings = `
{
"currentSetting": 2,
"presets": [{
"name": "Most efficient 8 megabytes of your life",
"cpuUsed": 0,
"deadline": "best",
"minrate": 90,
"crfMap": [{
"resolution": 240,
"crf": 1
},
{
"resolution": 360,
"crf": 1
},
{
"resolution": 480,
"crf": 1
},
{
"resolution": 720,
"crf": 1
},
{
"resolution": 1080,
"crf": 1
},
{
"resolution": 1440,
"crf": 1
},
{
"resolution": 2160,
"crf": 1
}
]
},
{
"name": "I have some time to kill",
"cpuUsed": 1,
"deadline": "good",
"minrate": 75,
"crfMap": [{
"resolution": 240,
"crf": 20
},
{
"resolution": 360,
"crf": 20
},
{
"resolution": 480,
"crf": 20
},
{
"resolution": 720,
"crf": 20
},
{
"resolution": 1080,
"crf": 17
},
{
"resolution": 1440,
"crf": 15
},
{
"resolution": 2160,
"crf": 10
}
]
},
{
"name": "Mid",
"cpuUsed": 3,
"deadline": "good",
"minrate":75,
"crfMap": [{
"resolution": 240,
"crf": 30
},
{
"resolution": 360,
"crf": 30
},
{
"resolution": 480,
"crf": 30
},
{
"resolution": 720,
"crf": 25
},
{
"resolution": 1080,
"crf": 20
},
{
"resolution": 1440,
"crf": 15
},
{
"resolution": 2160,
"crf": 10
}
]
},
{
"name": "I don't like waiting",
"cpuUsed": 4,
"deadline": 100,
"minrate": 90,
"crfMap": [{
"resolution": 240,
"crf": 45
},
{
"resolution": 360,
"crf": 42
},
{
"resolution": 480,
"crf": 40
},
{
"resolution": 720,
"crf": 35
},
{
"resolution": 1080,
"crf": 30
},
{
"resolution": 1440,
"crf": 25
},
{
"resolution": 2160,
"crf": 20
}
]
},
{
"name": "I want it, NOW!",
"cpuUsed": 4,
"deadline": "realtime",
"minrate": 50,
"crfMap": [{
"resolution": 240,
"crf": 40
},
{
"resolution": 360,
"crf": 35
},
{
"resolution": 480,
"crf": 30
},
{
"resolution": 720,
"crf": 25
},
{
"resolution": 1080,
"crf": 20
},
{
"resolution": 1440,
"crf": 15
},
{
"resolution": 2160,
"crf": 10
}
]
}
]
}
`
return new Promise((resolve, reject) => {
fs.writeFile("settings.json", settings, () => {
resolve(settings)
})
})
}

138
lib/encoder.js Normal file
View file

@ -0,0 +1,138 @@
import { exec } from 'child_process'
export class Encoder {
currentSetting
settings
encoder
encodePresetIndexArg
constructor(settings, currentSetting, encodePresetIndexArg = undefined) {
this.settings = settings
this.currentSetting = currentSetting
this.encodePresetIndexArg = encodePresetIndexArg
}
/**
*
* @param {String} path absolute path to file
* @param {String} out output filename
* @returns [duration, isTwoPass]
*/
async encodeVideo(path, out) {
//create progress bar
const [command, duration, isTwoPass] = await this.#constructVideoCommand(path, out)
this.encoder = exec(command)
return [duration, out, isTwoPass]
}
/**
*
* @param {String} path absolute path to file
* @param {String} out output filename
* @returns duration
*/
async encodeAudio(path, out) {
let [duration, resolution] = await this.#getDurationAndResolution(path)
const videoBitRate = Math.round(62000 / duration)
this.encoder = exec(`ffmpeg -y -i "${path}" -c:a libvorbis -b:a ${videoBitRate}k ${out}.ogg`)
return [duration, out, undefined]
}
/**
*
* @param {String} path absolute path to file
* @param {String} out output filename
*/
async encodePicture(path, out) {
this.encoder = exec(`ffmpeg -y -i "${path}" -qscale 80 -compression_level 6 ${out}.webp`)
return [1, out, undefined]
}
async #constructVideoCommand(path, out) {
let [duration, resolutionHeight] = await this.#getDurationAndResolution(path)
//Calculates video bitrate to fit right under 8mb 2:6 audio:video
const audioBitRate = Math.round(62000 / 8 * 2 / duration)
const videoBitRate = Math.round(62000 / 8 * 6 / duration)
//if command had argument of anotehr quality setting change to use that setting
if (this.encodePresetIndexArg) {
this.currentSetting = this.settings.presets[this.encodePresetIndexArg]
}
let command = ""
let crfIndex = 0
let isTwoPass = true
//Compares current video height to CRFMAP to determine optimal CRF
while (resolutionHeight > this.currentSetting.crfMap[crfIndex].resolution) {
crfIndex++
//if the resolution is still higher, just use highest res
if (!this.currentSetting.crfMap[crfIndex]?.resolution) {
crfIndex--
break
}
}
for (let pass = 1; pass <= 2; pass++) {
command += `ffmpeg -y -i "${path}" -vcodec libvpx-vp9 -acodec libvorbis `
command += `-deadline ${this.currentSetting.deadline} `
command += `-cpu-used ${this.currentSetting.cpuUsed} `
if (this.currentSetting?.minrate) {
command += `-b:v ${Math.round(videoBitRate * 0.95)}k `
command += `-minrate ${Math.round(videoBitRate / 100 * this.currentSetting.minrate)}k `
command += `-maxrate ${videoBitRate}k `
}
else {
command += `-b:v ${videoBitRate}k `
command += `-b:a ${audioBitRate}k `
command += `-crf ${this.currentSetting.crfMap[crfIndex].crf} `
}
//realtime doesnt support two pass
if (this.currentSetting.deadline == "realtime") {
command += `-row-mt 1 "${out}.webm"`
isTwoPass = false
break
}
pass == 1 ? command += `-pass 1 -row-mt 1 -f webm NUL && ` : command += `-pass 2 -row-mt 1 "${out}.webm" `
}
return [command, duration, isTwoPass]
}
async #getDurationAndResolution(file) {
let query = await this.#ffprobe(file)
//duration in seconds
const duration = query.split("Duration: ")[1].split(",")[0]
const arr = duration.split(":") // splitting the string by colon
const seconds = arr[0] * 3600 + arr[1] * 60 + (+arr[2]) // converting to s
//resolution height
const resolutionHeight = query.split("Stream #0:0")[1]?.split(",")[2].split(" ")[1].split("x")[1]
return [Number.parseFloat(seconds), resolutionHeight]
}
#ffprobe(file) {
return new Promise((resolve, reject) => {
exec(`ffprobe "${file}"`, (error, stdout, stderr) => {
resolve(stderr)
})
})
}
on(channel, callback) {
switch (channel) {
case "close":
this.encoder.on("close", () => {
callback(true)
})
break
case "update":
this.encoder.stderr.on("data", (chunk) => {
callback(chunk)
})
break
default:
throw new Error("Incorrect Channel")
}
}
}

221
lib/settingsManager.js Normal file
View file

@ -0,0 +1,221 @@
import fs from "fs"
export class SettingsManager {
settings
currentSetting
constructor() {
}
async start() {
await this.#init()
}
async #init() {
this.settings = await this.#getSettings().catch(async (err) => {
this.settings = undefined
})
if (!this.settings) this.settings = await this.#makeNewSettingsFile()
this.settings = JSON.parse(this.settings.toString())
this.currentSetting = this.settings.presets[this.settings.currentSetting]
}
async #getSettings() {
return new Promise((resolve, reject) => {
getSettings = fs.readFile("settings.json", (err, data) => {
resolve(data)
reject(err)
})
})
}
async #makeNewSettingsFile() {
const settings = `
{
"currentSetting": 2,
"presets": [{
"name": "Most efficient 8 megabytes of your life",
"cpuUsed": 0,
"deadline": "best",
"minrate": 90,
"crfMap": [{
"resolution": 240,
"crf": 1
},
{
"resolution": 360,
"crf": 1
},
{
"resolution": 480,
"crf": 1
},
{
"resolution": 720,
"crf": 1
},
{
"resolution": 1080,
"crf": 1
},
{
"resolution": 1440,
"crf": 1
},
{
"resolution": 2160,
"crf": 1
}
]
},
{
"name": "I have some time to kill",
"cpuUsed": 1,
"deadline": "good",
"minrate": 75,
"crfMap": [{
"resolution": 240,
"crf": 20
},
{
"resolution": 360,
"crf": 20
},
{
"resolution": 480,
"crf": 20
},
{
"resolution": 720,
"crf": 20
},
{
"resolution": 1080,
"crf": 17
},
{
"resolution": 1440,
"crf": 15
},
{
"resolution": 2160,
"crf": 10
}
]
},
{
"name": "Mid",
"cpuUsed": 3,
"deadline": "good",
"minrate":75,
"crfMap": [{
"resolution": 240,
"crf": 30
},
{
"resolution": 360,
"crf": 30
},
{
"resolution": 480,
"crf": 30
},
{
"resolution": 720,
"crf": 25
},
{
"resolution": 1080,
"crf": 20
},
{
"resolution": 1440,
"crf": 15
},
{
"resolution": 2160,
"crf": 10
}
]
},
{
"name": "I don't like waiting",
"cpuUsed": 4,
"deadline": 100,
"minrate": 90,
"crfMap": [{
"resolution": 240,
"crf": 45
},
{
"resolution": 360,
"crf": 42
},
{
"resolution": 480,
"crf": 40
},
{
"resolution": 720,
"crf": 35
},
{
"resolution": 1080,
"crf": 30
},
{
"resolution": 1440,
"crf": 25
},
{
"resolution": 2160,
"crf": 20
}
]
},
{
"name": "I want it, NOW!",
"cpuUsed": 4,
"deadline": "realtime",
"minrate": 50,
"crfMap": [{
"resolution": 240,
"crf": 40
},
{
"resolution": 360,
"crf": 35
},
{
"resolution": 480,
"crf": 30
},
{
"resolution": 720,
"crf": 25
},
{
"resolution": 1080,
"crf": 20
},
{
"resolution": 1440,
"crf": 15
},
{
"resolution": 2160,
"crf": 10
}
]
}
]
}
`
return new Promise((resolve, reject) => {
fs.writeFile("settings.json", settings, () => {
resolve(settings)
})
})
}
}

121
lib/ui.js Normal file
View file

@ -0,0 +1,121 @@
import termkit from "terminal-kit"
import cliProgress from "cli-progress"
import fs from "fs"
export class UI {
term
bars = []
multibar
settings
currentSetting
constructor(settings, currentSetting) {
this.term = termkit.terminal
this.multibar = new cliProgress.MultiBar({
format: '[{bar}] {percentage}% | output: "{filename}" | ETA: {eta}s | {value}/{total}',
align: "left",
hideCursor: true,
autopadding: true,
}, cliProgress.Presets.shades_grey)
this.settings = settings
this.currentSetting = currentSetting
}
/**
*
* @param {Number} duration Duration of the encoded media
* @param {String} filename name of the encoding file
* @param {Boolean} isTwoPass is the encoded media two pass
* @returns
*/
async newBar(encoderOutput) {
let duration = encoderOutput[0]
let filename = encoderOutput[1]
let isTwoPass = encoderOutput[2]
this.bars.push({
"bar": this.multibar.create(duration, 0, { speed: "N/A" }),
"isTwoPass": isTwoPass,
"isPastHalf": false,
"filename": filename,
"duration": duration,
"finished": false
}
)
const barIndex = this.bars.length - 1
return barIndex
}
async updateBar(chunk, barIndex = 0, isVideo = true, isImage = false) {
if (!chunk) return
if (isImage) {
this.bars[barIndex]?.bar.update(1, { filename: `${this.bars[barIndex].filename}.webp` })
return
}
if (isVideo) {
const currentTime = chunk.split("time=")[1]?.split(" ")[0]
if (!currentTime) return
const arr = currentTime.split(":")
let seconds = Number.parseFloat(arr[0] * 3600 + arr[1] * 60 + (+arr[2])) // converting to s
//If 2 pass divide bar into two parts to show both in progress in one progress
if (this.bars[barIndex].isTwoPass) {
if (seconds / 2 >= (this.bars[barIndex].duration - 0.2) / 2) this.bars[barIndex].isPastHalf = true
if (this.bars[barIndex].isPastHalf) this.bars[barIndex].bar.update(Math.round(seconds * 50) / 100 + (this.bars[barIndex].duration / 2), { filename: `${this.bars[barIndex].filename}.webm` })
else this.bars[barIndex].bar.update(Math.round(seconds * 50) / 100, { filename: `${this.bars[barIndex].filename}.webm` })
}
else {
this.bars[barIndex].bar.update(Math.round(seconds * 100) / 100, { filename: `${this.bars[barIndex].filename}.webm` })
}
}
else {
const currentTime = chunk.split("time=")[1]?.split(" ")[0]
if (!currentTime) return
const arr = currentTime.split(":")
let seconds = Number.parseFloat(arr[0] * 3600 + arr[1] * 60 + (+arr[2])) // converting to s
this.bars[barIndex].bar.update(Math.round(seconds * 100) / 100, { filename: `${this.bars[barIndex].filename}.ogg` })
}
}
encodeFinished(barIndex) {
this.bars[barIndex].fininshed = true
// if all are finished stop multibars and exit
for (let i = 0; i < this.bars.length; i++) {
if (this.bars[i].finished) return
}
this.multibar.stop()
fs.rm("ffmpeg2pass-0.log", (error) => { error })
console.clear()
this.term.bold.green("Finished!\n")
this.term.grey("press enter to exit...\n")
this.term.inputField(() => { process.exit() })
}
async startMenu() {
await this.#menu()
}
async #menu() {
console.clear()
let menu = []
for (let i = 0; i < settings.presets.length; i++) {
menu.push(`${i}. ${settings.presets[i].name}`)
}
this.term("How to convert: [app] [optional: -preset {Index}] [filename.extension(s)]\n")
this.term("examples: \n")
this.term.italic(" npx DMC -preset 0 file.mp3 file4.mov img.jpg\n")
this.term.italic(" DMC.exe -preset 2 file34.wav file2.mp3\n\n")
this.term.yellow("Hello! This menu is for selecting performance/speed preset.\n")
this.term.yellow("Currently using ").bgMagenta(`"${settings.presets[settings.currentSetting].name}"`).yellow(" preset")
this.term.singleColumnMenu(menu, (error, response) => {
currentSetting = response.selectedIndex
fs.writeFileSync("settings.json", JSON.stringify(settings))
this.term.green("\n Using").green.bold(` ${settings.presets[settings.currentSetting].name} `).green("setting\n")
this.term.grey("Press enter to exit...")
this.term.inputField(() => { process.exit() })
})
}
}

251
package-lock.json generated
View file

@ -1,21 +1,22 @@
{
"name": "discord-media-compressor-8mb",
"version": "1.0.0",
"version": "1.0.1",
"lockfileVersion": 2,
"requires": true,
"packages": {
"": {
"name": "discord-media-compressor-8mb",
"version": "1.0.0",
"version": "1.0.1",
"license": "ISC",
"dependencies": {
"cli-progress": "^3.11.0",
"filehound": "^1.17.6",
"pkg": "^5.6.0",
"require-runtime": "^2.0.0",
"terminal-kit": "^2.4.0"
},
"bin": {
"discord-media-compressor-8mb": "index.js"
"DMC": "bin/index.js"
}
},
"node_modules/@babel/helper-validator-identifier": {
@ -157,6 +158,11 @@
"node": ">= 4.0.0"
}
},
"node_modules/balanced-match": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
"integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="
},
"node_modules/base64-js": {
"version": "1.5.1",
"resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz",
@ -199,6 +205,19 @@
"node": ">= 6"
}
},
"node_modules/bluebird": {
"version": "3.7.2",
"resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz",
"integrity": "sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg=="
},
"node_modules/brace-expansion": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz",
"integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==",
"dependencies": {
"balanced-match": "^1.0.0"
}
},
"node_modules/braces": {
"version": "3.0.2",
"resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz",
@ -383,6 +402,11 @@
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="
},
"node_modules/concat-map": {
"version": "0.0.1",
"resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
"integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s="
},
"node_modules/console-control-strings": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz",
@ -481,6 +505,11 @@
"once": "^1.4.0"
}
},
"node_modules/err-code": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/err-code/-/err-code-1.1.2.tgz",
"integrity": "sha1-BuARbTAo9q70gGhJ6w6mp0iuaWA="
},
"node_modules/escalade": {
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz",
@ -546,6 +575,11 @@
"node": ">=6"
}
},
"node_modules/extend": {
"version": "3.0.2",
"resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz",
"integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g=="
},
"node_modules/fast-glob": {
"version": "3.2.11",
"resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.11.tgz",
@ -574,6 +608,49 @@
"reusify": "^1.0.4"
}
},
"node_modules/file-js": {
"version": "0.3.0",
"resolved": "https://registry.npmjs.org/file-js/-/file-js-0.3.0.tgz",
"integrity": "sha1-+rRr94I0bJKUSZ8fDSrQfYOPJdE=",
"dependencies": {
"bluebird": "^3.4.7",
"minimatch": "^3.0.3",
"proper-lockfile": "^1.2.0"
}
},
"node_modules/file-js/node_modules/brace-expansion": {
"version": "1.1.11",
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
"integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
"dependencies": {
"balanced-match": "^1.0.0",
"concat-map": "0.0.1"
}
},
"node_modules/file-js/node_modules/minimatch": {
"version": "3.1.2",
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
"integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
"dependencies": {
"brace-expansion": "^1.1.7"
},
"engines": {
"node": "*"
}
},
"node_modules/filehound": {
"version": "1.17.6",
"resolved": "https://registry.npmjs.org/filehound/-/filehound-1.17.6.tgz",
"integrity": "sha512-5q4zjFkI8W2zLmvbvyvI//K882IpEj6sMNXPUQlk5H6W4Wh3OSSylEAIEmMLELP9G7ileYjTKPXOn0YzzS55Lg==",
"dependencies": {
"bluebird": "^3.7.2",
"file-js": "0.3.0",
"lodash": "^4.17.21",
"minimatch": "^5.0.0",
"moment": "^2.29.1",
"unit-compare": "^1.0.1"
}
},
"node_modules/fill-range": {
"version": "7.0.1",
"resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz",
@ -869,6 +946,11 @@
"node": ">= 0.8.0"
}
},
"node_modules/lodash": {
"version": "4.17.21",
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
"integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg=="
},
"node_modules/lru-cache": {
"version": "6.0.0",
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz",
@ -911,6 +993,17 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/minimatch": {
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.0.1.tgz",
"integrity": "sha512-nLDxIFRyhDblz3qMuq+SoRZED4+miJ/G+tdDrjkkkRnjAsBexeGpgjLEQ0blJy7rHhR2b93rhQY4SvyWu9v03g==",
"dependencies": {
"brace-expansion": "^2.0.1"
},
"engines": {
"node": ">=10"
}
},
"node_modules/minimist": {
"version": "1.2.6",
"resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.6.tgz",
@ -921,6 +1014,14 @@
"resolved": "https://registry.npmjs.org/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz",
"integrity": "sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A=="
},
"node_modules/moment": {
"version": "2.29.3",
"resolved": "https://registry.npmjs.org/moment/-/moment-2.29.3.tgz",
"integrity": "sha512-c6YRvhEo//6T2Jz/vVtYzqBzwvPT95JBQ+smCytzf7c50oMZRsR/a4w88aD34I+/QVSfnoAnSBFPJHItlOMJVw==",
"engines": {
"node": "*"
}
},
"node_modules/ms": {
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
@ -1230,6 +1331,17 @@
"node": ">=0.4.0"
}
},
"node_modules/proper-lockfile": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/proper-lockfile/-/proper-lockfile-1.2.0.tgz",
"integrity": "sha1-zv9d2J0+XxD7deHo52vHWAGlnDQ=",
"dependencies": {
"err-code": "^1.0.0",
"extend": "^3.0.0",
"graceful-fs": "^4.1.2",
"retry": "^0.10.0"
}
},
"node_modules/pump": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz",
@ -1315,6 +1427,14 @@
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/retry": {
"version": "0.10.1",
"resolved": "https://registry.npmjs.org/retry/-/retry-0.10.1.tgz",
"integrity": "sha1-52OI0heZLCUnUCQdPTlW/tmNj/Q=",
"engines": {
"node": "*"
}
},
"node_modules/reusify": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz",
@ -1633,6 +1753,14 @@
"resolved": "https://registry.npmjs.org/uniq/-/uniq-1.0.1.tgz",
"integrity": "sha1-sxxa6CVIRKOoKBVBzisEuGWnNP8="
},
"node_modules/unit-compare": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/unit-compare/-/unit-compare-1.0.1.tgz",
"integrity": "sha1-DHRZ8OW/U2N+qHPKPO4Y3i7so4Y=",
"dependencies": {
"moment": "^2.14.1"
}
},
"node_modules/universalify": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz",
@ -1917,6 +2045,11 @@
"resolved": "https://registry.npmjs.org/at-least-node/-/at-least-node-1.0.0.tgz",
"integrity": "sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg=="
},
"balanced-match": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
"integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="
},
"base64-js": {
"version": "1.5.1",
"resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz",
@ -1944,6 +2077,19 @@
}
}
},
"bluebird": {
"version": "3.7.2",
"resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz",
"integrity": "sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg=="
},
"brace-expansion": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz",
"integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==",
"requires": {
"balanced-match": "^1.0.0"
}
},
"braces": {
"version": "3.0.2",
"resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz",
@ -2076,6 +2222,11 @@
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="
},
"concat-map": {
"version": "0.0.1",
"resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
"integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s="
},
"console-control-strings": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz",
@ -2151,6 +2302,11 @@
"once": "^1.4.0"
}
},
"err-code": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/err-code/-/err-code-1.1.2.tgz",
"integrity": "sha1-BuARbTAo9q70gGhJ6w6mp0iuaWA="
},
"escalade": {
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz",
@ -2188,6 +2344,11 @@
"resolved": "https://registry.npmjs.org/expand-template/-/expand-template-2.0.3.tgz",
"integrity": "sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg=="
},
"extend": {
"version": "3.0.2",
"resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz",
"integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g=="
},
"fast-glob": {
"version": "3.2.11",
"resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.11.tgz",
@ -2213,6 +2374,48 @@
"reusify": "^1.0.4"
}
},
"file-js": {
"version": "0.3.0",
"resolved": "https://registry.npmjs.org/file-js/-/file-js-0.3.0.tgz",
"integrity": "sha1-+rRr94I0bJKUSZ8fDSrQfYOPJdE=",
"requires": {
"bluebird": "^3.4.7",
"minimatch": "^3.0.3",
"proper-lockfile": "^1.2.0"
},
"dependencies": {
"brace-expansion": {
"version": "1.1.11",
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
"integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
"requires": {
"balanced-match": "^1.0.0",
"concat-map": "0.0.1"
}
},
"minimatch": {
"version": "3.1.2",
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
"integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
"requires": {
"brace-expansion": "^1.1.7"
}
}
}
},
"filehound": {
"version": "1.17.6",
"resolved": "https://registry.npmjs.org/filehound/-/filehound-1.17.6.tgz",
"integrity": "sha512-5q4zjFkI8W2zLmvbvyvI//K882IpEj6sMNXPUQlk5H6W4Wh3OSSylEAIEmMLELP9G7ileYjTKPXOn0YzzS55Lg==",
"requires": {
"bluebird": "^3.7.2",
"file-js": "0.3.0",
"lodash": "^4.17.21",
"minimatch": "^5.0.0",
"moment": "^2.29.1",
"unit-compare": "^1.0.1"
}
},
"fill-range": {
"version": "7.0.1",
"resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz",
@ -2435,6 +2638,11 @@
"type-check": "~0.3.2"
}
},
"lodash": {
"version": "4.17.21",
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
"integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg=="
},
"lru-cache": {
"version": "6.0.0",
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz",
@ -2462,6 +2670,14 @@
"resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-2.1.0.tgz",
"integrity": "sha512-wXqjST+SLt7R009ySCglWBCFpjUygmCIfD790/kVbiGmUgfYGuB14PiTd5DwVxSV4NcYHjzMkoj5LjQZwTQLEA=="
},
"minimatch": {
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.0.1.tgz",
"integrity": "sha512-nLDxIFRyhDblz3qMuq+SoRZED4+miJ/G+tdDrjkkkRnjAsBexeGpgjLEQ0blJy7rHhR2b93rhQY4SvyWu9v03g==",
"requires": {
"brace-expansion": "^2.0.1"
}
},
"minimist": {
"version": "1.2.6",
"resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.6.tgz",
@ -2472,6 +2688,11 @@
"resolved": "https://registry.npmjs.org/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz",
"integrity": "sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A=="
},
"moment": {
"version": "2.29.3",
"resolved": "https://registry.npmjs.org/moment/-/moment-2.29.3.tgz",
"integrity": "sha512-c6YRvhEo//6T2Jz/vVtYzqBzwvPT95JBQ+smCytzf7c50oMZRsR/a4w88aD34I+/QVSfnoAnSBFPJHItlOMJVw=="
},
"ms": {
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
@ -2698,6 +2919,17 @@
"resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz",
"integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA=="
},
"proper-lockfile": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/proper-lockfile/-/proper-lockfile-1.2.0.tgz",
"integrity": "sha1-zv9d2J0+XxD7deHo52vHWAGlnDQ=",
"requires": {
"err-code": "^1.0.0",
"extend": "^3.0.0",
"graceful-fs": "^4.1.2",
"retry": "^0.10.0"
}
},
"pump": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz",
@ -2757,6 +2989,11 @@
"supports-preserve-symlinks-flag": "^1.0.0"
}
},
"retry": {
"version": "0.10.1",
"resolved": "https://registry.npmjs.org/retry/-/retry-0.10.1.tgz",
"integrity": "sha1-52OI0heZLCUnUCQdPTlW/tmNj/Q="
},
"reusify": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz",
@ -2988,6 +3225,14 @@
"resolved": "https://registry.npmjs.org/uniq/-/uniq-1.0.1.tgz",
"integrity": "sha1-sxxa6CVIRKOoKBVBzisEuGWnNP8="
},
"unit-compare": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/unit-compare/-/unit-compare-1.0.1.tgz",
"integrity": "sha1-DHRZ8OW/U2N+qHPKPO4Y3i7so4Y=",
"requires": {
"moment": "^2.14.1"
}
},
"universalify": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz",

View file

@ -3,10 +3,14 @@
"version": "1.0.1",
"description": "helps free discord users to send any media(image, video, audio) and not get limited by discords 8mb file limit",
"main": "index.js",
"bin": "./index.js",
"bin": {
"DMC": "./bin/index.js"
},
"type": "module",
"scripts": {
"start": "node index.js",
"build": "pkg -t --compress GZip package.json"
"start": "node bin/index.js",
"build": "pkg .",
"test": "node fieldTest.cjs"
},
"keywords": [
"discord",
@ -18,6 +22,7 @@
"license": "ISC",
"dependencies": {
"cli-progress": "^3.11.0",
"filehound": "^1.17.6",
"pkg": "^5.6.0",
"require-runtime": "^2.0.0",
"terminal-kit": "^2.4.0"