Skip to content

Commit

Permalink
Merge pull request #4 from tahsinature/node-implementation
Browse files Browse the repository at this point in the history
Node implementation
  • Loading branch information
tahsinature authored Nov 10, 2024
2 parents 8dfd558 + 19bfd51 commit 0c126ca
Show file tree
Hide file tree
Showing 13 changed files with 318 additions and 148 deletions.
15 changes: 0 additions & 15 deletions Makefile

This file was deleted.

Binary file modified bun.lockb
Binary file not shown.
13 changes: 9 additions & 4 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,20 @@
"name": "hardbrake",
"module": "src/index.ts",
"type": "module",
"scripts": {
"start": "bun run src/index.ts"
},
"devDependencies": {
"@types/blessed": "^0.1.25",
"@types/bun": "latest"
"@types/bun": "latest",
"@types/cli-progress": "^3.11.6",
"@types/prompts": "^2.4.9"
},
"peerDependencies": {
"typescript": "^5.0.0"
},
"dependencies": {
"blessed": "^0.1.81",
"blessed-contrib": "^4.11.0"
"cli-progress": "^3.12.0",
"prompts": "^2.4.2",
"readline": "^1.3.0"
}
}
10 changes: 0 additions & 10 deletions requirements-dev.txt

This file was deleted.

5 changes: 0 additions & 5 deletions requirements.txt

This file was deleted.

19 changes: 0 additions & 19 deletions setup.py

This file was deleted.

25 changes: 25 additions & 0 deletions src/blueprints.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import path from "path";
import fs from "fs";

export class File {
dir: string;
originalFullPath: string;
fileNameWithoutExtension: string;
cmd = null as string | null;
outputFullPath = null as string | null;

constructor(fullPath: string) {
this.originalFullPath = fullPath;
this.dir = path.dirname(fullPath);
this.fileNameWithoutExtension = path.basename(fullPath, path.extname(fullPath));
}

async deleteOriginal() {
return fs.promises.unlink(this.originalFullPath);
}

async deleteOutput() {
if (this.outputFullPath) fs.promises.unlink(this.outputFullPath);
else console.error(`Output file not found for ${this.originalFullPath}`);
}
}
44 changes: 44 additions & 0 deletions src/check.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import { runShellCommandAndReturnLine } from "./utils";

const checkIfBinaryExists = async (binary: string) => {
const lines: string[] = [];
await runShellCommandAndReturnLine(`which ${binary}`, (l) => l && lines.push(l));
return lines.length > 0;
};

const requiredBinaries = [
{
name: "HandBrakeCLI",
purpose: "HandBrake Command Line Version to encode videos.",
howTo: `Download HandBrake Command Line Version from: https://handbrake.fr/downloads.php`,
},
{
name: "fzf",
purpose: "Fuzzy Finder to select multiple files.",
howTo: `Install fzf from: https://github.com/junegunn/fzf#installation`,
},
{
name: "fd",
purpose: "To find and filter files.",
howTo: `Install fd from: https://github.com/sharkdp/fd#installation`,
},
];

export const checkRequiredBinaries = async () => {
const missing = [];

for (const binary of requiredBinaries) {
const isFound = await checkIfBinaryExists(binary.name);
if (!isFound) missing.push(binary);
}

if (missing.length) {
console.error(`The following binaries are required to run this program:
----------`);
for (const binary of missing) {
console.error(`- ${binary.name}: ${binary.purpose}`);
console.error(binary.howTo);
}
process.exit(1);
}
};
117 changes: 27 additions & 90 deletions src/engine.ts
Original file line number Diff line number Diff line change
@@ -1,102 +1,39 @@
import { execSync, spawn } from "child_process";
import blessed from "blessed";
import contrib, { Widgets } from "blessed-contrib";
import path from "path";
import type { File } from "./blueprints";
import { ProgressBar } from "./progress";
import { runShellCommandAndReturnLine } from "./utils";

const ping = (server: string, log: Widgets.LogElement) => {
const command = `ping ${server}`;
// console.log current ping
// on each ping, update the console.log. no new lines
const getProgressAndRemainingTime = (line: string) => {
const progressMatch = line.match(/Encoding: task 1 of 1, (\d+\.\d+) %/);
const progress = progressMatch ? parseFloat(progressMatch[1]) : null;

var out = spawn(command, { stdio: ["inherit", "pipe", "inherit"], shell: true, argv0: "node" });
const remainingTimeMatch = line.match(/ETA (\d+h\d+m\d+s)/);
const remainingTime = remainingTimeMatch ? remainingTimeMatch[1] : null;

out.stdout.setEncoding("utf-8");
out.stdout.on("readable", () => {
const data = out.stdout.read();
log.log(data);
});
return { progress, remainingTime };
};

export const main = async () => {
const screen = blessed.screen();
export const main = async (files: File[], preset: string) => {
for (const file of files) {
const outputFileName = `${file.fileNameWithoutExtension}__HandBraked__${preset}.mp4`;
file.outputFullPath = path.resolve(file.dir, outputFileName);
file.cmd = `HandBrakeCLI -i '${file.originalFullPath}' -o '${file.outputFullPath}' -Z '${preset}'`;
}

screen.key(["escape", "q", "C-c"], function (ch, key) {
screen.destroy();
// screen.removeAllListeners();
// return process.exit(0);
});
const grid = new contrib.grid({ rows: 2, cols: 1, screen });
const progressBar = new ProgressBar(files.length);

const log1 = grid.set(0, 0, 1, 1, contrib.log, { fg: "green", selectedFg: "green", label: "Google Log", height: 2 });
const log2 = grid.set(1, 0, 1, 1, contrib.log, { fg: "green", selectedFg: "green", label: "Yahoo Log", height: "100%" });
for (const file of files) {
if (!file.cmd) throw new Error(`Command not found for file: ${file.originalFullPath}`);

ping("google.com", log1);
ping("yahoo.com", log2);

// const runShellCommand = async (cmd: string): Promise<string> => {
// return new Promise((resolve, reject) => {
// var out = spawn(cmd, {
// stdio: ["inherit", "pipe", "inherit"],
// shell: true,
// argv0: "node",
// });

// out.stdout.setEncoding("utf-8");
// out.stdout.on("readable", () => resolve(out.stdout.read()));
// });
// };

// const askFiles = async () => {
// const files = await runShellCommand("fzf --multi");
// const filtered = files.split("\n").filter(Boolean);
// return filtered.map((file) => path.resolve(file));
// };

// const printHandBrakeCLIVersion = async () => {
// const o = execSync("HandBrakeCLI --version", { stdio: ["inherit", "pipe"] });
// const out = o.toString().split("\n").filter(Boolean);

// console.log(out[0]);
// };

// printHandBrakeCLIVersion();

// const files = await askFiles();

// for (const file of files) {
// console.log(file);
// // const cmd = `hardbrake -i ${file} -o ${file}.mp4`;
// // console.log(cmd);
// // await runShellCommand(cmd);
// }

// function clearLine() {
// readline.cursorTo(process.stdout, 0, 0);
// readline.clearLine(process.stdout, 0);
// }

// var log = contrib.log({ fg: "green", selectedFg: "green", label: "Server Log" });

// log.log("Starting server...");

const displayCurrentTask = async () => {
const command = `ping facebook.com.com`;
// console.log current ping
// on each ping, update the console.log. no new lines

var out = spawn(command, { stdio: ["inherit", "pipe", "inherit"], shell: true, argv0: "node" });

out.stdout.setEncoding("utf-8");
out.stdout.on("readable", () => {
const data = out.stdout.read();
log1.log(data);
// clearLine();
// process.stdout.write(data);
await runShellCommandAndReturnLine(file.cmd, (line) => {
const { progress, remainingTime } = getProgressAndRemainingTime(line);
if (progress && remainingTime) {
progressBar.handleNewLine(progress, remainingTime, file.fileNameWithoutExtension);
}
});
};

displayCurrentTask();
progressBar.handleOneFileDone();
}

screen.append(log1);
screen.append(log2);
screen.render();
progressBar.stop();
};
29 changes: 24 additions & 5 deletions src/index.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,28 @@
import path from "path";
import { askFiles, askPreset, askToggle } from "./prompts";
import { main } from "./engine";
import { checkRequiredBinaries } from "./check";

await main();
await checkRequiredBinaries();

const wait = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms));
const files = await askFiles();
if (files.length === 0) {
console.log("No files selected. Exiting.");
process.exit(0);
}

await wait(1000);
console.log("hehe");
const preset = await askPreset();

await main(files, preset);

const happyWithResults = await askToggle("Are you happy with the results?");
if (!happyWithResults) {
for (const file of files) await file.deleteOutput();
console.log("Deleted all the output files.");
process.exit(0);
}

const deleteOriginalFiles = await askToggle("Do you want to delete the original files?");
if (deleteOriginalFiles) {
for (const file of files) await file.deleteOriginal();
console.log("Deleted all the original files.");
}
64 changes: 64 additions & 0 deletions src/progress.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
import cliProgress from "cli-progress";

export class ProgressBar {
multibar = new cliProgress.MultiBar(
{
clearOnComplete: false,
hideCursor: true,
format: "{context} {bar} | {percent} | ETA: {eta}s | ETA HB: {etaHB} | {filename}",
},
cliProgress.Presets.shades_grey
);

b1 = this.multibar.create(100, 0, {
context: "Current",
});

b2 = this.multibar.create(0, 0, {
context: " Total",
percent: "-",
etaHB: "-",
filename: "-",
});

constructor(totalFiles: number) {
this.b2.setTotal(totalFiles);
}

handleNewLine(percent: number, eta: string, filename: string) {
this.b1.update(percent, {
filename,
percent: `${percent}%`,
etaHB: eta,
});
}

handleOneFileDone() {
this.b1.update(100, {
percent: "100%",
etaHB: "0s",
});
this.b2.increment();
}

stop() {
this.multibar.stop();
this.b1.stop();
this.b2.stop();
}
}

// const files = ["file1.mp4", "file2.mp4", "file3.mp4", "file4.mp4", "file5.mp4"];

// const progress = new ProgressBar(files.length);
// for (const file of files) {
// for (let i = 0; i <= 97; i++) {
// progress.handleNewLine(i, "10s", file);
// await sleep(10);
// }

// progress.handleOneFileDone();
// }

// await sleep(1000);
// progress.stop();
Loading

0 comments on commit 0c126ca

Please sign in to comment.