p2p-transfer 1.0.0
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/LICENSE +21 -0
- package/README.md +248 -0
- package/p2p.js +289 -0
- package/package.json +59 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2024 P2P Transfer
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,248 @@
|
|
|
1
|
+
# P2P Transfer
|
|
2
|
+
|
|
3
|
+
🚀 A pure CLI tool for peer-to-peer file transfer between different networks, powered by WebTorrent and BitTorrent DHT.
|
|
4
|
+
|
|
5
|
+
## ✨ Features
|
|
6
|
+
|
|
7
|
+
- 🌐 **Pure CLI** - No web UI or browser required, operate directly in terminal
|
|
8
|
+
- 🔧 **Zero Configuration** - One command to send, one command to receive
|
|
9
|
+
- 👶 **Beginner Friendly** - Intuitive commands with progress bar and speed display
|
|
10
|
+
- ⚡ **Maximum Speed** - DHT + configurable Tracker subscription for optimal peer discovery
|
|
11
|
+
- 🔒 **Cross-Network** - Transfer between different LANs without requiring public IP
|
|
12
|
+
- 📦 **Large File Support** - Handles files of any size with automatic chunking
|
|
13
|
+
|
|
14
|
+
## 🚀 Quick Start
|
|
15
|
+
|
|
16
|
+
### Installation
|
|
17
|
+
|
|
18
|
+
```bash
|
|
19
|
+
# Install via npm (after publishing)
|
|
20
|
+
npm install -g p2p-transfer
|
|
21
|
+
|
|
22
|
+
# Or use npx directly
|
|
23
|
+
npx p2p-transfer share <file>
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
### Basic Usage
|
|
27
|
+
|
|
28
|
+
**Share a file (on sender's machine):**
|
|
29
|
+
```bash
|
|
30
|
+
p2p share ~/movies/vacation.mp4
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
**Download a file (on receiver's machine):**
|
|
34
|
+
```bash
|
|
35
|
+
p2p get "magnet:?xt=urn:btih:abc123..."
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
## 📖 Detailed Usage
|
|
39
|
+
|
|
40
|
+
### Share Command
|
|
41
|
+
|
|
42
|
+
```bash
|
|
43
|
+
p2p share <file> [options]
|
|
44
|
+
|
|
45
|
+
Options:
|
|
46
|
+
-v, --verbose Show detailed logs
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
**Example:**
|
|
50
|
+
```bash
|
|
51
|
+
p2p share ~/documents/report.pdf
|
|
52
|
+
p2p share ./large-video.mp4 --verbose
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
**Output:**
|
|
56
|
+
```
|
|
57
|
+
🚀 Initializing P2P share...
|
|
58
|
+
📁 File: report.pdf
|
|
59
|
+
📊 Size: 2.5 MB
|
|
60
|
+
|
|
61
|
+
✅ Share successful!
|
|
62
|
+
|
|
63
|
+
🔗 Magnet URI (copy and send to receiver):
|
|
64
|
+
magnet:?xt=urn:btih:abc123def456...
|
|
65
|
+
|
|
66
|
+
📋 BTIH Hash: abc123def456
|
|
67
|
+
|
|
68
|
+
⏳ Waiting for connections... (Press Ctrl+C to stop)
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
### Download Command
|
|
72
|
+
|
|
73
|
+
```bash
|
|
74
|
+
p2p get <magnet> [path] [options]
|
|
75
|
+
|
|
76
|
+
Arguments:
|
|
77
|
+
magnet Magnet URI
|
|
78
|
+
path Download directory (default: current directory)
|
|
79
|
+
|
|
80
|
+
Options:
|
|
81
|
+
-v, --verbose Show detailed logs
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
**Example:**
|
|
85
|
+
```bash
|
|
86
|
+
p2p get "magnet:?xt=urn:btih:abc123..." ~/Downloads
|
|
87
|
+
p2p get "magnet:?xt=urn:btih:abc123..." ./my-files
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
**Output:**
|
|
91
|
+
```
|
|
92
|
+
📥 Initializing P2P download...
|
|
93
|
+
|
|
94
|
+
✅ File found!
|
|
95
|
+
|
|
96
|
+
📁 Filename: report.pdf
|
|
97
|
+
📊 Total size: 2.5 MB
|
|
98
|
+
📋 Hash: abc123def456
|
|
99
|
+
📁 Save to: ~/Downloads
|
|
100
|
+
|
|
101
|
+
📥 Download Progress |████████████░░░░| 45% | 1.1 MB/2.5 MB | 5.2 MB/s | ETA: 00:00:03
|
|
102
|
+
|
|
103
|
+
✅ Download complete!
|
|
104
|
+
⏱️ Total time: 2.50 seconds
|
|
105
|
+
📁 Location: ~/Downloads/report.pdf
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
### Help Command
|
|
109
|
+
|
|
110
|
+
```bash
|
|
111
|
+
p2p info
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
## 🎯 How It Works
|
|
115
|
+
|
|
116
|
+
```
|
|
117
|
+
┌─────────────────┐ ┌─────────────────┐
|
|
118
|
+
│ Sender │ P2P │ Receiver │
|
|
119
|
+
│ │◄───────►│ │
|
|
120
|
+
│ 1. Seed file │ DHT │ 2. Download │
|
|
121
|
+
│ 3. Get Magnet │ │ 4. Parse Magnet│
|
|
122
|
+
│ 4. Share Link │ │ 5. Start D/L │
|
|
123
|
+
└─────────────────┘ └─────────────────┘
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
1. **Sender** runs `p2p share <file>` to start seeding
|
|
127
|
+
2. Generates unique **Magnet URI** containing file hash and Tracker addresses
|
|
128
|
+
3. **Receiver** runs `p2p get <magnet>` with the received URI
|
|
129
|
+
4. **DHT + Trackers** discover peers automatically
|
|
130
|
+
5. **P2P connection** established for direct transfer
|
|
131
|
+
|
|
132
|
+
## 🔧 Technical Details
|
|
133
|
+
|
|
134
|
+
- **Protocol**: BitTorrent
|
|
135
|
+
- **Peer Discovery**: DHT (Distributed Hash Table)
|
|
136
|
+
- **Trackers**: 7 public trackers for maximum connectivity
|
|
137
|
+
- **No Public IP Required**: Works behind NAT and firewalls
|
|
138
|
+
|
|
139
|
+
### Default Trackers
|
|
140
|
+
|
|
141
|
+
```
|
|
142
|
+
- udp://tracker.opentrackr.org:1337/announce
|
|
143
|
+
- udp://tracker.openbittorrent.com:6969/announce
|
|
144
|
+
- udp://exodus.desync.com:6969/announce
|
|
145
|
+
- udp://tracker.coppersurfer.tk:6969/announce
|
|
146
|
+
- wss://tracker.btorrent.xyz
|
|
147
|
+
- wss://tracker.openwebtorrent.com
|
|
148
|
+
- wss://tracker.webtorrent.io
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
## 💡 Use Cases
|
|
152
|
+
|
|
153
|
+
- **NAS to Local**: Transfer files from NAS to your computer
|
|
154
|
+
- **Cross-Network**: Share files between different networks (home ↔ office)
|
|
155
|
+
- **Quick Sharing**: No need to upload to cloud first
|
|
156
|
+
- **Large Files**: Perfect for big files without size limits
|
|
157
|
+
- **Offline Sharing**: Works without internet servers (via DHT)
|
|
158
|
+
|
|
159
|
+
## ⚙️ Requirements
|
|
160
|
+
|
|
161
|
+
- Node.js >= 18.0.0
|
|
162
|
+
- npm >= 8.0.0
|
|
163
|
+
|
|
164
|
+
## 📦 Installation from Source
|
|
165
|
+
|
|
166
|
+
```bash
|
|
167
|
+
git clone https://gitcode.com/yunqiang_wu/p2p-transfer.git
|
|
168
|
+
cd p2p-transfer
|
|
169
|
+
npm install
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
## 🌟 Advanced Usage
|
|
173
|
+
|
|
174
|
+
### Global Installation
|
|
175
|
+
|
|
176
|
+
```bash
|
|
177
|
+
npm install -g p2p-transfer
|
|
178
|
+
|
|
179
|
+
# Now you can use it anywhere
|
|
180
|
+
p2p share ~/file.txt
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
### Use as Project Dependency
|
|
184
|
+
|
|
185
|
+
```bash
|
|
186
|
+
npm install p2p-transfer
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
Then in your code:
|
|
190
|
+
```javascript
|
|
191
|
+
import { share, get } from 'p2p-transfer';
|
|
192
|
+
|
|
193
|
+
await share('/path/to/file');
|
|
194
|
+
await get('magnet:?xt=urn:btih:...');
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
## 🐛 Troubleshooting
|
|
198
|
+
|
|
199
|
+
### No peers connecting?
|
|
200
|
+
|
|
201
|
+
1. **Check firewall**: Ensure outbound UDP/TCP is allowed
|
|
202
|
+
2. **Wait longer**: DHT peer discovery may take 1-2 minutes
|
|
203
|
+
3. **Try verbose mode**: `p2p share <file> -v`
|
|
204
|
+
4. **Use more trackers**: Add additional trackers in code
|
|
205
|
+
|
|
206
|
+
### Download speed is slow?
|
|
207
|
+
|
|
208
|
+
1. **Wait for more peers**: Speed increases with more seeders
|
|
209
|
+
2. **Check NAT**: Some networks have bandwidth limits
|
|
210
|
+
3. **Try different network**: Some ISPs throttle BitTorrent
|
|
211
|
+
|
|
212
|
+
### Connection issues?
|
|
213
|
+
|
|
214
|
+
- Ensure both sides have internet access
|
|
215
|
+
- Check if magnet link is complete (no truncation)
|
|
216
|
+
- Try restarting both sender and receiver
|
|
217
|
+
|
|
218
|
+
## 🤝 Contributing
|
|
219
|
+
|
|
220
|
+
Contributions are welcome! Please feel free to submit a Pull Request.
|
|
221
|
+
|
|
222
|
+
## 📄 License
|
|
223
|
+
|
|
224
|
+
MIT License - see [LICENSE](LICENSE) file for details.
|
|
225
|
+
|
|
226
|
+
## 🙏 Acknowledgments
|
|
227
|
+
|
|
228
|
+
- [WebTorrent](https://webtorrent.io/) - BitTorrent implementation for Node.js
|
|
229
|
+
- [BitTorrent DHT](https://en.wikipedia.org/wiki/Mainline_DHT) - Distributed hash table
|
|
230
|
+
- All the public tracker operators
|
|
231
|
+
|
|
232
|
+
## 📈 Roadmap
|
|
233
|
+
|
|
234
|
+
- [ ] Add WebRTC support for browser-based sharing
|
|
235
|
+
- [ ] Implement pause/resume functionality
|
|
236
|
+
- [ ] Add multiple file sharing support
|
|
237
|
+
- [ ] Implement bandwidth limiting
|
|
238
|
+
- [ ] Add encryption option
|
|
239
|
+
- [ ] Create GUI version
|
|
240
|
+
|
|
241
|
+
## 📞 Support
|
|
242
|
+
|
|
243
|
+
- 🐛 Issues: [GitHub Issues](https://gitcode.com/yunqiang_wu/p2p-transfer/issues)
|
|
244
|
+
- 💬 Discussions: [GitHub Discussions](https://gitcode.com/yunqiang_wu/p2p-transfer/discussions)
|
|
245
|
+
|
|
246
|
+
---
|
|
247
|
+
|
|
248
|
+
Made with ❤️ for the P2P community
|
package/p2p.js
ADDED
|
@@ -0,0 +1,289 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import WebTorrent from 'webtorrent';
|
|
4
|
+
import cliProgress from 'cli-progress';
|
|
5
|
+
import yargs from 'yargs';
|
|
6
|
+
import { hideBin } from 'yargs/helpers';
|
|
7
|
+
import chalk from 'chalk';
|
|
8
|
+
import fs from 'fs';
|
|
9
|
+
import path from 'path';
|
|
10
|
+
import os from 'os';
|
|
11
|
+
import { fileURLToPath } from 'url';
|
|
12
|
+
|
|
13
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
14
|
+
const __dirname = path.dirname(__filename);
|
|
15
|
+
|
|
16
|
+
const DEFAULT_TRACKERS = [
|
|
17
|
+
'udp://tracker.opentrackr.org:1337/announce',
|
|
18
|
+
'udp://tracker.openbittorrent.com:6969/announce',
|
|
19
|
+
'udp://exodus.desync.com:6969/announce',
|
|
20
|
+
'udp://tracker.coppersurfer.tk:6969/announce',
|
|
21
|
+
'wss://tracker.btorrent.xyz',
|
|
22
|
+
'wss://tracker.openwebtorrent.com',
|
|
23
|
+
'wss://tracker.webtorrent.io',
|
|
24
|
+
];
|
|
25
|
+
|
|
26
|
+
function formatSize(bytes) {
|
|
27
|
+
if (bytes === 0) return '0 B';
|
|
28
|
+
const k = 1024;
|
|
29
|
+
const sizes = ['B', 'KB', 'MB', 'GB', 'TB'];
|
|
30
|
+
const i = Math.floor(Math.log(bytes) / Math.log(k));
|
|
31
|
+
return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i];
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function formatSpeed(bytesPerSecond) {
|
|
35
|
+
return formatSize(bytesPerSecond) + '/s';
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function formatTime(seconds) {
|
|
39
|
+
if (!isFinite(seconds) || seconds < 0) return '--:--:--';
|
|
40
|
+
const h = Math.floor(seconds / 3600);
|
|
41
|
+
const m = Math.floor((seconds % 3600) / 60);
|
|
42
|
+
const s = Math.floor(seconds % 60);
|
|
43
|
+
return `${h.toString().padStart(2, '0')}:${m.toString().padStart(2, '0')}:${s.toString().padStart(2, '0')}`;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
async function shareFile(filePath, options) {
|
|
47
|
+
console.log(chalk.blue('🚀 初始化 P2P 分享...'));
|
|
48
|
+
|
|
49
|
+
const client = new WebTorrent({
|
|
50
|
+
tracker: {
|
|
51
|
+
RTCPeerConnection: false,
|
|
52
|
+
},
|
|
53
|
+
dht: {
|
|
54
|
+
pool: true,
|
|
55
|
+
},
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
const absolutePath = path.resolve(filePath);
|
|
59
|
+
|
|
60
|
+
if (!fs.existsSync(absolutePath)) {
|
|
61
|
+
console.error(chalk.red('❌ 文件不存在: ' + absolutePath));
|
|
62
|
+
client.destroy();
|
|
63
|
+
process.exit(1);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
const stats = fs.statSync(absolutePath);
|
|
67
|
+
const fileName = path.basename(absolutePath);
|
|
68
|
+
|
|
69
|
+
console.log(chalk.cyan('📁 文件: ') + fileName);
|
|
70
|
+
console.log(chalk.cyan('📊 大小: ') + formatSize(stats.size));
|
|
71
|
+
|
|
72
|
+
return new Promise((resolve, reject) => {
|
|
73
|
+
client.seed(absolutePath, {
|
|
74
|
+
announceList: [DEFAULT_TRACKERS],
|
|
75
|
+
name: fileName,
|
|
76
|
+
}, (torrent) => {
|
|
77
|
+
console.log(chalk.green('\n✅ 分享成功!\n'));
|
|
78
|
+
console.log(chalk.yellow('🔗 Magnet 链接 (复制发送给接收方):'));
|
|
79
|
+
console.log(chalk.white(torrent.magnetURI));
|
|
80
|
+
console.log(chalk.cyan('\n📋 BTIH Hash: ') + torrent.infoHash);
|
|
81
|
+
|
|
82
|
+
console.log(chalk.blue('\n⏳ 等待连接中... (按 Ctrl+C 停止)\n'));
|
|
83
|
+
|
|
84
|
+
torrent.on('wire', (wire) => {
|
|
85
|
+
const peerAddr = wire.remoteAddress || 'unknown';
|
|
86
|
+
console.log(chalk.green('🔗 新连接: ') + peerAddr);
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
torrent.on('error', (err) => {
|
|
90
|
+
console.error(chalk.red('Torrent error:'), err.message);
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
if (options.verbose) {
|
|
94
|
+
torrent.on('warning', (warning) => {
|
|
95
|
+
console.log(chalk.yellow('⚠️ Warning:'), warning.message);
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
client.on('error', (err) => {
|
|
101
|
+
console.error(chalk.red('❌ 客户端错误:'), err.message);
|
|
102
|
+
reject(err);
|
|
103
|
+
});
|
|
104
|
+
});
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
async function getFile(magnetUri, downloadPath, options) {
|
|
108
|
+
console.log(chalk.blue('📥 初始化 P2P 下载...'));
|
|
109
|
+
|
|
110
|
+
const client = new WebTorrent({
|
|
111
|
+
tracker: {
|
|
112
|
+
RTCPeerConnection: false,
|
|
113
|
+
},
|
|
114
|
+
dht: {
|
|
115
|
+
pool: true,
|
|
116
|
+
},
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
const savePath = downloadPath || process.cwd();
|
|
120
|
+
|
|
121
|
+
if (!fs.existsSync(savePath)) {
|
|
122
|
+
fs.mkdirSync(savePath, { recursive: true });
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
return new Promise((resolve, reject) => {
|
|
126
|
+
const torrent = client.add(magnetUri, {
|
|
127
|
+
path: savePath,
|
|
128
|
+
announceList: [DEFAULT_TRACKERS],
|
|
129
|
+
});
|
|
130
|
+
|
|
131
|
+
const progressBar = new cliProgress.SingleBar({
|
|
132
|
+
format: chalk.cyan('📥 下载进度') + ' |' + chalk.cyan('{bar}') + '| {percentage}% | {downloaded}/{total} | {speed} | ETA: {eta}',
|
|
133
|
+
barCompleteChar: '\u2588',
|
|
134
|
+
barIncompleteChar: '\u2591',
|
|
135
|
+
hideCursor: true,
|
|
136
|
+
});
|
|
137
|
+
|
|
138
|
+
let startTime = Date.now();
|
|
139
|
+
let lastDownloaded = 0;
|
|
140
|
+
let updateInterval;
|
|
141
|
+
|
|
142
|
+
torrent.on('metadata', () => {
|
|
143
|
+
console.log(chalk.green('\n✅ 找到文件!\n'));
|
|
144
|
+
console.log(chalk.cyan('📁 文件名: ') + torrent.name);
|
|
145
|
+
console.log(chalk.cyan('📊 总大小: ') + formatSize(torrent.length));
|
|
146
|
+
console.log(chalk.cyan('📋 Hash: ') + torrent.infoHash);
|
|
147
|
+
console.log(chalk.cyan('📁 保存到: ') + savePath);
|
|
148
|
+
|
|
149
|
+
progressBar.start(torrent.length, 0, {
|
|
150
|
+
downloaded: '0 B',
|
|
151
|
+
total: formatSize(torrent.length),
|
|
152
|
+
speed: '0 B/s',
|
|
153
|
+
eta: '--:--:--',
|
|
154
|
+
});
|
|
155
|
+
|
|
156
|
+
updateInterval = setInterval(() => {
|
|
157
|
+
if (torrent.length > 0) {
|
|
158
|
+
const downloaded = torrent.downloaded || 0;
|
|
159
|
+
const speed = torrent.downloadSpeed || 0;
|
|
160
|
+
const eta = torrent.timeRemaining / 1000;
|
|
161
|
+
|
|
162
|
+
progressBar.update(downloaded, {
|
|
163
|
+
downloaded: formatSize(downloaded),
|
|
164
|
+
total: formatSize(torrent.length),
|
|
165
|
+
speed: formatSpeed(speed),
|
|
166
|
+
eta: formatTime(eta),
|
|
167
|
+
});
|
|
168
|
+
}
|
|
169
|
+
}, 500);
|
|
170
|
+
});
|
|
171
|
+
|
|
172
|
+
torrent.on('done', () => {
|
|
173
|
+
clearInterval(updateInterval);
|
|
174
|
+
const totalTime = ((Date.now() - startTime) / 1000).toFixed(2);
|
|
175
|
+
console.log(chalk.green('\n✅ 下载完成!'));
|
|
176
|
+
console.log(chalk.cyan('⏱️ 总耗时: ') + totalTime + ' 秒');
|
|
177
|
+
console.log(chalk.cyan('📁 保存位置: ') + path.join(savePath, torrent.name));
|
|
178
|
+
});
|
|
179
|
+
|
|
180
|
+
torrent.on('wire', (wire) => {
|
|
181
|
+
const peerAddr = wire.remoteAddress || 'unknown';
|
|
182
|
+
console.log(chalk.green('🔗 新连接: ') + peerAddr + ' (已连接: ' + torrent.numPeers + ' 个节点)');
|
|
183
|
+
});
|
|
184
|
+
|
|
185
|
+
torrent.on('peer', (peer) => {
|
|
186
|
+
console.log(chalk.green('➕ 新节点: ') + (peer.remoteAddress || peer.ip || 'unknown'));
|
|
187
|
+
});
|
|
188
|
+
|
|
189
|
+
torrent.on('error', (err) => {
|
|
190
|
+
console.error(chalk.red('❌ 下载错误:'), err.message);
|
|
191
|
+
clearInterval(updateInterval);
|
|
192
|
+
reject(err);
|
|
193
|
+
});
|
|
194
|
+
|
|
195
|
+
client.on('error', (err) => {
|
|
196
|
+
console.error(chalk.red('❌ 客户端错误:'), err.message);
|
|
197
|
+
clearInterval(updateInterval);
|
|
198
|
+
reject(err);
|
|
199
|
+
});
|
|
200
|
+
|
|
201
|
+
process.on('SIGINT', () => {
|
|
202
|
+
console.log(chalk.yellow('\n\n⚠️ 正在停止下载...'));
|
|
203
|
+
clearInterval(updateInterval);
|
|
204
|
+
if (progressBar.isActive) {
|
|
205
|
+
progressBar.stop();
|
|
206
|
+
}
|
|
207
|
+
client.destroy(() => {
|
|
208
|
+
console.log(chalk.green('✅ 已停止。'));
|
|
209
|
+
process.exit(0);
|
|
210
|
+
});
|
|
211
|
+
});
|
|
212
|
+
});
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
function showInfo() {
|
|
216
|
+
console.log(chalk.bold('\n🌐 P2P 文件传输工具\n'));
|
|
217
|
+
console.log(chalk.cyan('使用方法:'));
|
|
218
|
+
console.log(' 发送文件:');
|
|
219
|
+
console.log(chalk.white(' p2p share <文件路径>'));
|
|
220
|
+
console.log(' 例如: p2p share ~/movies/vacation.mp4\n');
|
|
221
|
+
console.log(' 接收文件:');
|
|
222
|
+
console.log(chalk.white(' p2p get <magnet链接> [保存目录]'));
|
|
223
|
+
console.log(' 例如: p2p get "magnet:?xt=..." ~/Downloads\n');
|
|
224
|
+
console.log(chalk.cyan('原理:'));
|
|
225
|
+
console.log(' - 使用 BitTorrent 协议进行 P2P 传输');
|
|
226
|
+
console.log(' - 通过 DHT 网络发现节点,无需公网 IP');
|
|
227
|
+
console.log(' - 支持跨局域网传输\n');
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
const argv = yargs(hideBin(process.argv))
|
|
231
|
+
.usage(chalk.bold('\n🌐 P2P 文件传输工具\n\n用法: $0 <命令> [参数]'))
|
|
232
|
+
.command('share', '分享文件,生成 magnet 链接', (yargs) => {
|
|
233
|
+
return yargs
|
|
234
|
+
.positional('file', {
|
|
235
|
+
describe: '要分享的文件路径',
|
|
236
|
+
type: 'string',
|
|
237
|
+
})
|
|
238
|
+
.option('v', {
|
|
239
|
+
alias: 'verbose',
|
|
240
|
+
describe: '显示详细日志',
|
|
241
|
+
type: 'boolean',
|
|
242
|
+
default: false,
|
|
243
|
+
});
|
|
244
|
+
}, async (argv) => {
|
|
245
|
+
const file = argv._[1];
|
|
246
|
+
if (!file) {
|
|
247
|
+
console.error(chalk.red('❌ 请指定要分享的文件路径'));
|
|
248
|
+
process.exit(1);
|
|
249
|
+
}
|
|
250
|
+
await shareFile(file, argv);
|
|
251
|
+
})
|
|
252
|
+
.command('get', '下载文件', (yargs) => {
|
|
253
|
+
return yargs
|
|
254
|
+
.positional('magnet', {
|
|
255
|
+
describe: 'Magnet 链接',
|
|
256
|
+
type: 'string',
|
|
257
|
+
})
|
|
258
|
+
.positional('path', {
|
|
259
|
+
describe: '保存目录',
|
|
260
|
+
type: 'string',
|
|
261
|
+
default: process.cwd(),
|
|
262
|
+
})
|
|
263
|
+
.option('v', {
|
|
264
|
+
alias: 'verbose',
|
|
265
|
+
describe: '显示详细日志',
|
|
266
|
+
type: 'boolean',
|
|
267
|
+
default: false,
|
|
268
|
+
});
|
|
269
|
+
}, async (argv) => {
|
|
270
|
+
const magnet = argv._[1];
|
|
271
|
+
const savePath = argv._[2] || process.cwd();
|
|
272
|
+
if (!magnet) {
|
|
273
|
+
console.error(chalk.red('❌ 请提供 Magnet 链接'));
|
|
274
|
+
process.exit(1);
|
|
275
|
+
}
|
|
276
|
+
await getFile(magnet, savePath, argv);
|
|
277
|
+
})
|
|
278
|
+
.command('info', '显示帮助信息', {}, () => {
|
|
279
|
+
showInfo();
|
|
280
|
+
})
|
|
281
|
+
.demandCommand(1, chalk.red('❌ 请指定命令: share 或 get'))
|
|
282
|
+
.help('h', '显示帮助')
|
|
283
|
+
.alias('h', 'help')
|
|
284
|
+
.version('v', '版本', '1.0.0')
|
|
285
|
+
.alias('v', 'version')
|
|
286
|
+
.epilog(chalk.cyan('\n💡 提示:'))
|
|
287
|
+
.epilog(chalk.cyan(' 发送方运行: p2p share <文件>'))
|
|
288
|
+
.epilog(chalk.cyan(' 接收方运行: p2p get <magnet链接>'))
|
|
289
|
+
.parse();
|
package/package.json
ADDED
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "p2p-transfer",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "🚀 Pure CLI P2P file transfer tool using WebTorrent and BitTorrent DHT. Transfer files between different networks without public IP.",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"p2p",
|
|
7
|
+
"bittorrent",
|
|
8
|
+
"webtorrent",
|
|
9
|
+
"file-transfer",
|
|
10
|
+
"cli",
|
|
11
|
+
"peer-to-peer",
|
|
12
|
+
"dht",
|
|
13
|
+
"magnet",
|
|
14
|
+
"share",
|
|
15
|
+
"download"
|
|
16
|
+
],
|
|
17
|
+
"homepage": "https://gitcode.com/yunqiang_wu/p2p-transfer#readme",
|
|
18
|
+
"repository": {
|
|
19
|
+
"type": "git",
|
|
20
|
+
"url": "https://gitcode.com/yunqiang_wu/p2p-transfer.git"
|
|
21
|
+
},
|
|
22
|
+
"bugs": {
|
|
23
|
+
"url": "https://gitcode.com/yunqiang_wu/p2p-transfer/issues"
|
|
24
|
+
},
|
|
25
|
+
"license": "MIT",
|
|
26
|
+
"author": {
|
|
27
|
+
"name": "Yunqiang Wu",
|
|
28
|
+
"email": "your.email@example.com",
|
|
29
|
+
"url": "https://gitcode.com/yunqiang_wu"
|
|
30
|
+
},
|
|
31
|
+
"main": "p2p.js",
|
|
32
|
+
"bin": {
|
|
33
|
+
"p2p": "./p2p.js"
|
|
34
|
+
},
|
|
35
|
+
"type": "module",
|
|
36
|
+
"scripts": {
|
|
37
|
+
"start": "node p2p.js",
|
|
38
|
+
"share": "node p2p.js share",
|
|
39
|
+
"get": "node p2p.js get",
|
|
40
|
+
"info": "node p2p.js info",
|
|
41
|
+
"test": "echo \"No tests specified\" && exit 0"
|
|
42
|
+
},
|
|
43
|
+
"dependencies": {
|
|
44
|
+
"webtorrent": "^2.0.0",
|
|
45
|
+
"cli-progress": "^3.12.0",
|
|
46
|
+
"yargs": "^17.7.2",
|
|
47
|
+
"chalk": "^5.3.0"
|
|
48
|
+
},
|
|
49
|
+
"devDependencies": {},
|
|
50
|
+
"engines": {
|
|
51
|
+
"node": ">=18.0.0"
|
|
52
|
+
},
|
|
53
|
+
"preferGlobal": true,
|
|
54
|
+
"files": [
|
|
55
|
+
"p2p.js",
|
|
56
|
+
"README.md",
|
|
57
|
+
"LICENSE"
|
|
58
|
+
]
|
|
59
|
+
}
|