andrade-soulseek-downloader
Version:
Simple, safe Soulseek download library with built-in rate limiting to prevent bans
187 lines • 7.24 kB
JavaScript
;
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
var ownKeys = function(o) {
ownKeys = Object.getOwnPropertyNames || function (o) {
var ar = [];
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
return ar;
};
return ownKeys(o);
};
return function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
__setModuleDefault(result, mod);
return result;
};
})();
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.downloadPeerFile = void 0;
const net = __importStar(require("net"));
const fs = __importStar(require("fs"));
const events_1 = require("events");
const message_factory_1 = require("../message-factory");
const stack_1 = __importDefault(require("../stack"));
const debug = (msg) => {
if (process.env.DEBUG?.includes('slsk:peer:file')) {
console.log(`[slsk:peer:file] ${msg}`);
}
};
const downloadPeerFile = (host, port, token, user, noPierce) => {
const emitter = new events_1.EventEmitter();
debug(`downloadPeerFile ${user}`);
const conn = net.createConnection({
host,
port
}, () => {
if (noPierce) {
debug(`noPierce ${user} connected`);
conn.write(message_factory_1.MessageFactory.to.peer
.peerInit(stack_1.default.currentLogin, 'F', token)
.getBuff());
setTimeout(() => {
debug('noPierce sending 8* 00');
if (conn.destroyed) {
debug('socket closed');
return;
}
conn.write(Buffer.from('00000000' + '00000000', 'hex'));
}, 1000);
}
else {
conn.write(message_factory_1.MessageFactory.to.peer.pierceFw(token).getBuff());
}
});
let received = false;
let requestToken = noPierce ? token : undefined;
let buf = Buffer.alloc(0);
let tok;
let down;
let i = 0;
let writeStream = null;
let totalBytesReceived = 0;
conn.on('data', data => {
if (!noPierce && !received) {
requestToken = data.toString('hex', 0, 4);
conn.write(Buffer.from('00000000' + '00000000', 'hex'));
received = true;
}
else {
debug('file data');
// Get download info if not already fetched
if (!tok) {
tok = stack_1.default.downloadTokens[requestToken];
if (tok) {
down = stack_1.default.download[tok.user + '_' + tok.file];
debug(`Got download info for ${tok.user} - ${tok.file}, size: ${tok.size}`);
// Create write stream for progressive writing
if (down && down.path && !writeStream) {
debug(`Creating write stream for ${down.path}`);
writeStream = fs.createWriteStream(down.path);
emitter.emit('start', { size: tok.size, file: tok.file });
}
}
else {
debug(`Warning: No download token found for ${requestToken}`);
}
}
// Write data progressively to file
if (writeStream) {
writeStream.write(data);
}
// Also buffer data for compatibility
buf = Buffer.concat([buf, data]);
totalBytesReceived += data.length;
// Emit progress event
if (tok && tok.size) {
const percentage = (totalBytesReceived / tok.size) * 100;
if (i % 10 === 0 || percentage >= 100) {
debug(`Progress: ${totalBytesReceived}/${tok.size} (${percentage.toFixed(1)}%)`);
}
emitter.emit('progress', {
received: totalBytesReceived,
total: tok.size,
percentage: percentage
});
}
if (down && down.stream) {
debug('push to stream');
down.stream.push(data);
}
}
if (tok) {
if (i % 10 === 0) {
debug(`buf: ${buf.length} size: ${tok.size}`);
}
i++;
}
if (tok && buf.length >= tok.size) {
debug(`disconnect, buf: ${buf.length} size: ${tok.size}`);
conn.end();
}
});
conn.on('close', () => {
debug(`file socket close ${user}`);
if (tok && down) {
if (down.stream)
down.stream.push(null);
// Close write stream if used
if (writeStream) {
writeStream.end(() => {
down.buffer = buf;
emitter.emit('complete', { path: down.path, size: totalBytesReceived });
if (typeof down.cb === 'function')
down.cb(null, down);
});
}
else {
// Fallback to original behavior if no write stream
const filePath = down.path || getFilePathName(tok.user, tok.file);
fs.writeFile(filePath, buf, () => {
down.path = filePath;
down.buffer = buf;
emitter.emit('complete', { path: filePath, size: totalBytesReceived });
if (typeof down.cb === 'function')
down.cb(null, down);
});
}
}
else {
fs.writeFile(`${user}-${token}.mp3`, buf, () => { });
debug(`ERROR: token ${token} not exist`);
emitter.emit('error', new Error(`Token ${token} not found`));
}
});
conn.on('error', (err) => {
debug(`file socket error ${user}, destroying`);
emitter.emit('error', err);
conn.destroy();
});
return emitter;
};
exports.downloadPeerFile = downloadPeerFile;
function getFilePathName(user, file) {
const parts = file.split('\\');
return `/tmp/slsk/${user}_${parts[parts.length - 1]}`;
}
//# sourceMappingURL=download-peer-file.js.map