transfer-coffee-cli/cli.mjs
Kumi d176a8f89b
feat(cli): add file sharing CLI using Transfer.coffee
Introduced a command-line interface that allows users to upload and download files using WebTorrent. Files can be shared using a generated mnemonic phrase for easy retrieval. The CLI supports one-time uploads and continuous seeding options.

Added configuration files to manage dependencies and environmental variables, including `.gitignore` for ignoring the `node_modules`.

This feature enables a decentralized file sharing mechanism and leverages mnemonic phrases for simpler sharing.
2024-10-20 21:13:01 +02:00

111 lines
3.2 KiB
JavaScript
Executable file

#!/usr/bin/env node
import { Command } from 'commander';
import WebTorrent from 'webtorrent';
import bip39 from 'bip39';
import dotenv from 'dotenv';
import fs from 'fs';
import path from 'path';
dotenv.config();
const app = new Command();
const client = new WebTorrent();
const trackerUrl = process.env.TRACKER_URL || "wss://tracker.transfer.coffee";
// Upload file and generate mnemonic
app
.command('upload <filePath>')
.description('Upload a file and get a mnemonic to share')
.option('-o, --one-time', 'Exit once the file has been downloaded once')
.action((filePath, options) => {
if (!fs.existsSync(filePath)) {
console.error('File does not exist.');
process.exit(1);
}
const opts = {
announce: [trackerUrl],
};
console.log(`Connecting to tracker: ${trackerUrl}`);
client.seed(filePath, opts, (torrent) => {
console.log(`File is seeding. InfoHash: ${torrent.infoHash}`);
try {
const mnemonic = bip39.entropyToMnemonic(torrent.infoHash);
console.log(`Mnemonic for sharing: ${mnemonic}`);
if (options.oneTime) {
torrent.on('upload', () => {
const completedPeer = torrent.wires.some(wire => wire.uploaded === torrent.length);
if (completedPeer) {
console.log('A peer has completed the download. Exiting...');
process.exit(0);
}
});
}
} catch (error) {
console.error(`Error generating mnemonic: ${error.message}`);
}
});
});
// Download file using mnemonic
app
.command('download [words...]')
.description('Download a file using a mnemonic')
.option('-s, --seed', 'Continue seeding after download is complete')
.action((words, options) => {
try {
const mnemonic = words.join(' ');
if (!bip39.validateMnemonic(mnemonic)) {
throw new Error('Invalid mnemonic');
}
const infoHash = bip39.mnemonicToEntropy(mnemonic);
console.log(`Connecting to tracker: ${trackerUrl}`);
console.log('Searching for seeds...');
const opts = {
announce: [trackerUrl],
};
client.add(infoHash, opts, (torrent) => {
const file = torrent.files[0];
const output = path.join(process.cwd(), file.name);
const stream = fs.createWriteStream(output);
console.log(`Downloading file: ${file.name}`);
console.log('Size:', file.length, 'bytes');
console.log('');
file.createReadStream().pipe(stream);
stream.on('finish', () => {
console.log("\nDownload complete:", output);
if (!options.seed) {
process.exit(0);
} else {
console.log('Continuing to seed...');
}
});
torrent.on('download', () => {
const progress = (torrent.downloaded / torrent.length * 100).toFixed(2);
process.stdout.write(`\rProgress: ${progress}%`);
});
});
} catch (error) {
console.error(`Error downloading file: ${error.message}`);
process.exit(1);
}
});
app
.command('help')
.description('Display help information')
.action(() => {
app.help();
});
app.parse(process.argv);