mtosity 0.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,71 @@
1
+ # mtosity
2
+
3
+ My personal terminal, whatevery I need in one place. You can use it too! You may know something about me while using it.
4
+
5
+ ```
6
+ __ __ _ _ _
7
+ | \/ | | |_ ___ ___ (_) | |_ _ _
8
+ | |\/| | | __| / _ \ / __| | | | __| | | | |
9
+ | | | | | |_ | (_) | \__ \ | | | |_ | |_| |
10
+ |_| |_| \__| \___/ |___/ |_| \__| \__, |
11
+ |___/
12
+ ```
13
+
14
+ ## Prerequisites
15
+
16
+ - [yt-dlp](https://github.com/yt-dlp/yt-dlp) installed and available in your PATH
17
+
18
+ ## Install
19
+
20
+ ```bash
21
+ npm install -g mtosity
22
+ ```
23
+
24
+ ## Usage
25
+
26
+ Run the CLI:
27
+
28
+ ```bash
29
+ mtosity
30
+ ```
31
+
32
+ ### Commands
33
+
34
+ | Command | Description |
35
+ |---|---|
36
+ | `yt <url> [start] [end]` | Download YouTube video (MP4) |
37
+ | `yt-mp3 <url> [start] [end]` | Download YouTube audio (MP3) |
38
+ | `neofetch` | Display system information |
39
+ | `help` | Show available commands |
40
+ | `clear` | Clear the terminal |
41
+ | `exit` | Exit the CLI |
42
+
43
+ ### Examples
44
+
45
+ ```bash
46
+ # Download a video
47
+ yt https://youtu.be/dQw4w9WgXcQ
48
+
49
+ # Download audio only
50
+ yt-mp3 https://youtu.be/dQw4w9WgXcQ
51
+
52
+ # Download and trim (start at 0:30, duration 1:00)
53
+ yt https://youtu.be/dQw4w9WgXcQ 00:00:30 00:01:00
54
+ ```
55
+
56
+ ## Development
57
+
58
+ ```bash
59
+ # Install dependencies
60
+ bun install
61
+
62
+ # Run in dev mode
63
+ bun run dev
64
+
65
+ # Build
66
+ bun run build
67
+ ```
68
+
69
+ ## License
70
+
71
+ MIT
package/bin/mtosity.js ADDED
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env bun
2
+ require('../dist/index.js');
package/dist/index.js ADDED
@@ -0,0 +1,171 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ const inquirer_1 = __importDefault(require("inquirer"));
7
+ const chalk_1 = __importDefault(require("chalk"));
8
+ const figlet_1 = __importDefault(require("figlet"));
9
+ const ora_1 = __importDefault(require("ora"));
10
+ const fs_1 = __importDefault(require("fs"));
11
+ const path_1 = __importDefault(require("path"));
12
+ const fluent_ffmpeg_1 = __importDefault(require("fluent-ffmpeg"));
13
+ const ffmpeg_static_1 = __importDefault(require("ffmpeg-static"));
14
+ const systeminformation_1 = __importDefault(require("systeminformation"));
15
+ const os_1 = __importDefault(require("os"));
16
+ if (ffmpeg_static_1.default) {
17
+ fluent_ffmpeg_1.default.setFfmpegPath(ffmpeg_static_1.default);
18
+ }
19
+ const youtubedlPkg = require("youtube-dl-exec");
20
+ const youtubedl = youtubedlPkg.create(process.env.YTDLP_PATH || "yt-dlp");
21
+ // --- Commands ---
22
+ async function runNeofetch() {
23
+ const cpu = await systeminformation_1.default.cpu();
24
+ const mem = await systeminformation_1.default.mem();
25
+ const osInfo = await systeminformation_1.default.osInfo();
26
+ console.log(chalk_1.default.cyan(`
27
+ . OS: ${osInfo.distro} ${osInfo.release}
28
+ / \\ Host: ${os_1.default.hostname()}
29
+ / \\ Kernel: ${os_1.default.release()}
30
+ / | \\ Uptime: ${(os_1.default.uptime() / 3600).toFixed(2)} hours
31
+ /___|___\\ Shell: ${process.env.SHELL || "unknown"}
32
+ | CPU: ${cpu.manufacturer} ${cpu.brand}
33
+ | Memory: ${(mem.used / 1024 / 1024 / 1024).toFixed(2)}GiB / ${(mem.total / 1024 / 1024 / 1024).toFixed(2)}GiB
34
+ `));
35
+ }
36
+ async function downloadYouTube(url, format, start, end) {
37
+ const spinner = (0, ora_1.default)("Initializing yt-dlp...").start();
38
+ try {
39
+ // Get title first for filename (optional, or just use generic and rename? yt-dlp can output template)
40
+ // But we want to show title in spinner.
41
+ spinner.text = "Fetching metadata...";
42
+ const info = await youtubedl(url, {
43
+ dumpSingleJson: true,
44
+ noWarnings: true,
45
+ noCheckCertificates: true,
46
+ preferFreeFormats: true,
47
+ userAgent: 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36'
48
+ });
49
+ const title = info.title || "video";
50
+ const safeTitle = title.replace(/[^a-zA-Z0-9_\-\s]/g, "").replace(/\s+/g, "_") || "download";
51
+ const ext = format === "audio" ? "mp3" : "mp4";
52
+ const filename = `${safeTitle}.${ext}`;
53
+ const outputPath = path_1.default.resolve(process.cwd(), filename);
54
+ spinner.text = `Downloading: ${title}`;
55
+ const flags = {
56
+ output: outputPath,
57
+ noWarnings: true,
58
+ noCheckCertificates: true,
59
+ preferFreeFormats: true,
60
+ ffmpegLocation: ffmpeg_static_1.default,
61
+ userAgent: 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36'
62
+ };
63
+ if (format === "audio") {
64
+ flags.extractAudio = true;
65
+ flags.audioFormat = "mp3";
66
+ flags.format = "bestaudio/best";
67
+ }
68
+ else {
69
+ flags.format = "bestvideo+bestaudio/best";
70
+ flags.mergeOutputFormat = "mp4";
71
+ }
72
+ // Handling start/end trimming via post-processor args if supported, or via ffmpeg externally.
73
+ // yt-dlp supports --download-sections usually.
74
+ // But simpler: download then trim? Or use ffmpeg directly on stream?
75
+ // yt-dlp can pipe to ffmpeg.
76
+ // But `youtube-dl-exec` with `exec`?
77
+ // For simplicity given the library constraints, let's just download first.
78
+ // If user wants trim, we can run ffmpeg on the output file?
79
+ // Or just let yt-dlp handle it if possible.
80
+ // `download_ranges` is complex.
81
+ // Let's ignore trim for now or apply it after download if user provided args.
82
+ await youtubedl(url, flags);
83
+ if ((start || end) && ffmpeg_static_1.default) {
84
+ spinner.text = "Trimming...";
85
+ const tempPath = outputPath + ".tmp";
86
+ fs_1.default.renameSync(outputPath, tempPath);
87
+ await new Promise((resolve, reject) => {
88
+ let command = (0, fluent_ffmpeg_1.default)(tempPath);
89
+ if (start)
90
+ command = command.setStartTime(start);
91
+ if (end)
92
+ command = command.setDuration(end);
93
+ command
94
+ .save(outputPath)
95
+ .on("end", () => {
96
+ fs_1.default.unlinkSync(tempPath);
97
+ resolve();
98
+ })
99
+ .on("error", (err) => reject(err));
100
+ });
101
+ }
102
+ spinner.succeed(chalk_1.default.green(`Downloaded to ${filename}`));
103
+ }
104
+ catch (error) {
105
+ const msg = error instanceof Error ? error.message : String(error);
106
+ spinner.fail(chalk_1.default.red(`Error: ${msg}`));
107
+ }
108
+ }
109
+ // --- Main Loop ---
110
+ async function main() {
111
+ console.clear();
112
+ console.log(chalk_1.default.green(figlet_1.default.textSync("Mtosity", { horizontalLayout: "full" })));
113
+ console.log(chalk_1.default.dim("Welcome to Mtosity CLI. Type 'help' for commands.\n"));
114
+ while (true) {
115
+ const { input } = await inquirer_1.default.prompt([{
116
+ type: "input",
117
+ name: "input",
118
+ message: chalk_1.default.green("mtosity >"),
119
+ prefix: ""
120
+ }]);
121
+ const parts = input.trim().split(" ");
122
+ const cmd = parts[0];
123
+ const args = parts.slice(1);
124
+ switch (cmd) {
125
+ case "neofetch":
126
+ await runNeofetch();
127
+ break;
128
+ case "yt":
129
+ let url = args[0];
130
+ if (!url) {
131
+ const { inputUrl } = await inquirer_1.default.prompt([{
132
+ type: "input",
133
+ name: "inputUrl",
134
+ message: "Enter YouTube URL:",
135
+ }]);
136
+ url = inputUrl;
137
+ }
138
+ if (url)
139
+ await downloadYouTube(url, "video", args[1], args[2]);
140
+ break;
141
+ case "yt-mp3":
142
+ let urlMp3 = args[0];
143
+ if (!urlMp3) {
144
+ const { inputUrl } = await inquirer_1.default.prompt([{
145
+ type: "input",
146
+ name: "inputUrl",
147
+ message: "Enter YouTube URL:",
148
+ }]);
149
+ urlMp3 = inputUrl;
150
+ }
151
+ if (urlMp3)
152
+ await downloadYouTube(urlMp3, "audio", args[1], args[2]);
153
+ break;
154
+ case "clear":
155
+ console.clear();
156
+ break;
157
+ case "exit":
158
+ console.log(chalk_1.default.green("Goodbye!"));
159
+ process.exit(0);
160
+ case "":
161
+ break;
162
+ case "help":
163
+ console.log(chalk_1.default.cyan("Commands: neofetch, yt <url>, yt-mp3 <url>, clear, exit"));
164
+ break;
165
+ default:
166
+ console.log(chalk_1.default.red(`Unknown command: ${cmd}`));
167
+ break;
168
+ }
169
+ }
170
+ }
171
+ main().catch(console.error);
package/package.json ADDED
@@ -0,0 +1,52 @@
1
+ {
2
+ "name": "mtosity",
3
+ "version": "0.0.2",
4
+ "description": "Mtosity CLI - YouTube downloader and system info tool",
5
+ "main": "dist/index.js",
6
+ "bin": {
7
+ "mtosity": "bin/mtosity.js"
8
+ },
9
+ "files": [
10
+ "dist",
11
+ "bin"
12
+ ],
13
+ "keywords": [
14
+ "cli",
15
+ "youtube",
16
+ "downloader",
17
+ "neofetch",
18
+ "terminal"
19
+ ],
20
+ "author": "mtosity",
21
+ "license": "MIT",
22
+ "repository": {
23
+ "type": "git",
24
+ "url": "git+https://github.com/mtosity/mtosity-cli.git"
25
+ },
26
+ "scripts": {
27
+ "prepublishOnly": "tsc",
28
+ "build": "bun run tsc",
29
+ "start": "bun dist/index.js",
30
+ "dev": "bun src/index.ts"
31
+ },
32
+ "dependencies": {
33
+ "@ffmpeg/ffmpeg": "^0.12.15",
34
+ "@ffmpeg/util": "^0.12.2",
35
+ "chalk": "^5.6.2",
36
+ "ffmpeg-static": "^5.3.0",
37
+ "figlet": "^1.10.0",
38
+ "fluent-ffmpeg": "^2.1.3",
39
+ "inquirer": "^13.2.2",
40
+ "ora": "^9.3.0",
41
+ "systeminformation": "^5.30.7",
42
+ "youtube-dl-exec": "^3.1.1"
43
+ },
44
+ "devDependencies": {
45
+ "@types/figlet": "^1.7.0",
46
+ "@types/fluent-ffmpeg": "^2.1.28",
47
+ "@types/inquirer": "^9.0.9",
48
+ "@types/node": "^20.19.33",
49
+ "eslint": "^9",
50
+ "typescript": "^5.9.3"
51
+ }
52
+ }