@pikatorrent/node
Advanced tools
Comparing version 0.1.4 to 0.2.0
@@ -13,2 +13,3 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { | ||
import Transmission from 'transmission-native'; | ||
import * as readline from 'node:readline'; | ||
import * as crypto from 'node:crypto'; | ||
@@ -20,2 +21,3 @@ import * as path from 'node:path'; | ||
import envPaths from 'env-paths'; | ||
import wrtc from 'wrtc'; | ||
const { SIGNALING_URL, APP_URL } = config; | ||
@@ -35,29 +37,49 @@ if (!SIGNALING_URL) | ||
let wrtcInstance; | ||
let isPromptingForNewPeer = false; | ||
let onUpdateSettings; | ||
const peers = new Map(); // clientId -> SimplePeer | ||
let settings = null; | ||
// load settings.json | ||
if (fs.existsSync(settingsFilePath)) { | ||
const settingsFileData = fs.readFileSync(settingsFilePath); | ||
if (settingsFileData) { | ||
settings = JSON.parse(settingsFileData.toString()); | ||
const defaultSettings = { | ||
nodeId: null, | ||
acceptedPeers: [], | ||
rejectedPeers: [], | ||
}; | ||
let settings = Object.assign({}, defaultSettings); | ||
const loadSettings = () => { | ||
// load settings.json | ||
if (fs.existsSync(settingsFilePath)) { | ||
const settingsFileData = fs.readFileSync(settingsFilePath); | ||
if (settingsFileData) { | ||
settings = Object.assign(Object.assign({}, settings), JSON.parse(settingsFileData.toString())); | ||
} | ||
} | ||
}; | ||
loadSettings(); | ||
const nodeId = settings && settings.nodeId ? settings.nodeId : crypto.randomUUID(); | ||
const updateSettings = (update) => { | ||
settings = Object.assign(Object.assign({}, settings), update); | ||
if (onUpdateSettings) { | ||
onUpdateSettings(settings); | ||
} | ||
saveSettings(); | ||
}; | ||
const saveSettings = () => { | ||
fs.writeFileSync(settingsFilePath, JSON.stringify(settings)); | ||
}; | ||
if (!settings) { | ||
// Save nodeId to settings.json | ||
updateSettings({ nodeId }); | ||
} | ||
const nodeId = settings && settings.nodeId ? settings.nodeId : crypto.randomUUID(); | ||
const printNodeInfo = () => __awaiter(void 0, void 0, void 0, function* () { | ||
console.log('printNodeInfo'); | ||
const qrcode = yield QRCode.toString(`${APP_URL}/settings?nodeId=` + nodeId); | ||
console.log('> Node ID (keep it secret):', nodeId, '\n'); | ||
console.log(`> Add this node ID to a pikatorrent manually, click on the url, or scan the qrcode:`); | ||
console.log(`- ${APP_URL}/settings?nodeId=` + nodeId); | ||
console.log(`> Add this node ID to a pikatorrent app manually, click on the url, or scan the qrcode:`); | ||
console.log(qrcode); | ||
console.log(`${APP_URL}/settings?nodeId=` + nodeId); | ||
}); | ||
if (!settings) { | ||
// Save nodeId to settings.json | ||
fs.writeFileSync(settingsFilePath, JSON.stringify({ | ||
nodeId, | ||
})); | ||
} | ||
const initWebSocket = () => { | ||
// TODO: Encrypt signaling data ? | ||
const initWebSocket = ({ onAcceptOrRejectPeer }) => { | ||
ws = new WS(SIGNALING_URL); | ||
// Listen for messages | ||
ws.on('message', (message) => { | ||
ws.on('message', (message) => __awaiter(void 0, void 0, void 0, function* () { | ||
const json = JSON.parse(message); | ||
@@ -67,4 +89,28 @@ if (json.type === 'signal' && | ||
peers.has(json.fromId) === false) { | ||
const peerId = json.fromId; | ||
// Create new peer | ||
initPeer(json.fromId, json.signal); | ||
if (settings.rejectedPeers.find((p) => p.id === peerId)) { | ||
// Ignore rejected peer | ||
return; | ||
} | ||
else if (settings.acceptedPeers.find((p) => p.id === peerId)) { | ||
// accept peer connection immediately | ||
initPeer(json.fromId, json.signal); | ||
} | ||
else { | ||
if (isPromptingForNewPeer) { | ||
// Ignore new request while old one is still pending | ||
return false; | ||
} | ||
isPromptingForNewPeer = true; | ||
const isApproved = yield onAcceptOrRejectPeer(json.fromId, json.fromName); | ||
if (isApproved) { | ||
saveAcceptedPeer(json.fromId, json.fromName); | ||
initPeer(json.fromId, json.signal); | ||
} | ||
else if (!isApproved) { | ||
saveRejectedPeer(json.fromId, json.fromName); | ||
} | ||
isPromptingForNewPeer = false; | ||
} | ||
} | ||
@@ -78,6 +124,6 @@ else { | ||
} | ||
}); | ||
})); | ||
ws.on('close', () => { | ||
// Retry | ||
setTimeout(initWebSocket, 1000); | ||
setTimeout(() => initWebSocket({ onAcceptOrRejectPeer }), 1000); | ||
}); | ||
@@ -196,15 +242,60 @@ ws.on('error', console.error); | ||
}; | ||
// Handle exit gracefully | ||
process.on('SIGINT', () => { | ||
tr.close(); | ||
process.exit(); | ||
const onAcceptOrRejectPeerCli = (peerId, peerName) => __awaiter(void 0, void 0, void 0, function* () { | ||
return new Promise((resolve, reject) => { | ||
const readlineInterface = readline.createInterface({ | ||
input: process.stdin, | ||
output: process.stdout, | ||
}); | ||
readlineInterface.question(`Accept connection from ${peerName} (${peerId}) ? Y/N\n`, (response) => { | ||
readlineInterface.close(); | ||
if (['y', 'Y'].includes(response)) { | ||
resolve(true); | ||
} | ||
else if (['n', 'N'].includes(response)) { | ||
resolve(false); | ||
} | ||
else { | ||
// Do not save until we have a correct response | ||
resolve(false); | ||
} | ||
}); | ||
}); | ||
}); | ||
const startNode = (options = {}) => { | ||
if (options.wrtc) { | ||
wrtcInstance = options.wrtc; | ||
const startNode = (options = { | ||
connectWebsocket: true, | ||
onAcceptOrRejectPeer: onAcceptOrRejectPeerCli, | ||
onUpdateSettings: null, | ||
}) => { | ||
wrtcInstance = options.wrtc || wrtc; | ||
if (options.onUpdateSettings) { | ||
onUpdateSettings = options.onUpdateSettings; | ||
} | ||
printNodeInfo(); | ||
initWebSocket(); | ||
if (options.connectWebsocket) { | ||
console.log('initWebSocket', options.onAcceptOrRejectPeer); | ||
initWebSocket({ | ||
onAcceptOrRejectPeer: options.onAcceptOrRejectPeer || onAcceptOrRejectPeerCli, | ||
}); | ||
} | ||
return nodeId; | ||
}; | ||
export default startNode; | ||
const saveAcceptedPeer = (peerId, name) => { | ||
console.log('saveAcceptedPeer'); | ||
updateSettings({ | ||
acceptedPeers: [...settings.acceptedPeers, { id: peerId, name }], | ||
}); | ||
}; | ||
const saveRejectedPeer = (peerId, name) => { | ||
updateSettings({ | ||
rejectedPeers: [...settings.rejectedPeers, { id: peerId, name }], | ||
}); | ||
}; | ||
const transmission = { | ||
request: (...args) => tr.request(...args), | ||
}; | ||
// Handle exit gracefully : TODO: expose close/destroy function | ||
process.on('SIGINT', () => { | ||
tr.close(); | ||
process.exit(); | ||
}); | ||
export { startNode, transmission, settings, updateSettings }; |
{ | ||
"name": "@pikatorrent/node", | ||
"version": "0.1.4", | ||
"version": "0.2.0", | ||
"description": "", | ||
@@ -9,3 +9,3 @@ "main": "./dist/index.js", | ||
"build": "tsc", | ||
"start": "tsc --watch & node --watch ./dist/index.js", | ||
"watch": "tsc --watch", | ||
"prepare": "npm run build" | ||
@@ -27,3 +27,3 @@ }, | ||
"simple-peer": "^9.11.1", | ||
"transmission-native": "^0.2.0", | ||
"transmission-native": "^0.3.1", | ||
"wrtc": "^0.4.7", | ||
@@ -33,2 +33,3 @@ "ws": "^8.12.1" | ||
"devDependencies": { | ||
"@types/node": "^20.3.1", | ||
"@typescript-eslint/eslint-plugin": "^5.57.1", | ||
@@ -41,3 +42,4 @@ "@typescript-eslint/parser": "^5.57.1", | ||
"dist/" | ||
] | ||
], | ||
"license": "GPL-3.0" | ||
} |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Copyleft License
License(Experimental) Copyleft license information was found.
Found 1 instance in 1 package
Non-permissive License
License(Experimental) A license not known to be considered permissive was found.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
12310
305
5
2
70
+ Addedtransmission-native@0.3.2(transitive)
- Removedtransmission-native@0.2.1(transitive)
Updatedtransmission-native@^0.3.1