forked from LeenkxTeam/LNXSDK
P2P Export - Nodes - Patch
This commit is contained in:
@ -130,6 +130,11 @@ extern class Krom {
|
||||
static function readStorage(name: String): haxe.io.BytesData;
|
||||
|
||||
static function fileSaveBytes(path: String, bytes: haxe.io.BytesData): Void;
|
||||
static function fileWriteBytes(path: String, bytes: haxe.io.BytesData, ?offset: Float, ?byteLength: Int): Void;
|
||||
static function fileReadBytes(path: String, ?offset: Float, ?length: Int): haxe.io.BytesData;
|
||||
static function fileSize(path: String): Float;
|
||||
static function fileExists(path: String): Bool;
|
||||
static function deleteFile(path: String): Void;
|
||||
static function sysCommand(cmd: String, ?args: Array<String>): Int;
|
||||
static function savePath(): String;
|
||||
static function getArgCount(): Int;
|
||||
|
||||
@ -1,79 +0,0 @@
|
||||
package leenkx.logicnode;
|
||||
import leenkx.system.Event;
|
||||
#if js
|
||||
import leenkx.network.Leenkx;
|
||||
#end
|
||||
import iron.object.Object;
|
||||
|
||||
class AddTorrentNode extends LogicNode {
|
||||
public var title: String;
|
||||
public var values: Array<Dynamic>;
|
||||
public var net_Url: String;
|
||||
public var data: Dynamic;
|
||||
public var runner: Bool = false;
|
||||
|
||||
|
||||
public function new(tree:LogicTree) {
|
||||
super(tree);
|
||||
}
|
||||
|
||||
public function promiseResult(connection,torrentid,net_Url){
|
||||
#if js
|
||||
if (runner == false){
|
||||
values = [];
|
||||
var script = 'var torrentId = "' + torrentid + '";
|
||||
lx_' + net_Url +'.wt.add(torrentId, function (torrent) {
|
||||
leenkx.network.Leenkx.torrent.set("' + net_Url +'", torrent.files);
|
||||
torrent.on("done", function () {
|
||||
leenkx.network.Leenkx.connections.h["' + net_Url +'"].ontorrentdone();
|
||||
})
|
||||
});
|
||||
';
|
||||
js.Syntax.code('(1, eval)({0})', script.toString());
|
||||
runner = true;
|
||||
promiseResult(connection,torrentid,net_Url);
|
||||
return;
|
||||
}else{
|
||||
try{
|
||||
values = leenkx.network.Leenkx.torrent.get(net_Url);
|
||||
if(values.length > 0){
|
||||
runOutput(0);
|
||||
runner = false;
|
||||
return;
|
||||
}
|
||||
}
|
||||
catch(error){
|
||||
haxe.Timer.delay(function () {
|
||||
promiseResult(connection,torrentid,net_Url);
|
||||
return;
|
||||
}, 100);
|
||||
}
|
||||
}
|
||||
#end
|
||||
}
|
||||
|
||||
|
||||
override function run(from:Int) {
|
||||
var connection = inputs[1].get();
|
||||
if (connection == null) return;
|
||||
var torrentid = inputs[2].get();
|
||||
if (torrentid == null) return;
|
||||
#if js
|
||||
net_Url = connection._url;
|
||||
promiseResult(connection,torrentid,net_Url);
|
||||
#end
|
||||
}
|
||||
|
||||
override function get(from: Int): Dynamic {
|
||||
#if js
|
||||
return switch (from) {
|
||||
case 1: Leenkx.id.get(net_Url);
|
||||
case 2: leenkx.network.Leenkx.torrent.get(net_Url);
|
||||
default: throw "Unreachable";
|
||||
}
|
||||
#else
|
||||
return null;
|
||||
#end
|
||||
}
|
||||
|
||||
}
|
||||
@ -1,17 +1,21 @@
|
||||
package leenkx.logicnode;
|
||||
import iron.object.Object;
|
||||
#if js
|
||||
import leenkx.network.Types;
|
||||
import leenkx.network.Util;
|
||||
#if lnx_torrent
|
||||
import leenkx.network.Leenkx;
|
||||
import leenkx.network.torrent.LeenkxClient;
|
||||
import leenkx.network.torrent.TorrentClient;
|
||||
import leenkx.network.torrent.peer.RtcPeerPool;
|
||||
#end
|
||||
import leenkx.system.Event;
|
||||
|
||||
|
||||
class CreateLeenkxNode extends LogicNode {
|
||||
public var property0: String;
|
||||
public var property1: Bool;
|
||||
public var property2: String;
|
||||
public var property3: String;
|
||||
public var net_Url: String;
|
||||
public var data: Dynamic;
|
||||
public var client: Dynamic;
|
||||
public var value: String;
|
||||
public var listener: TEvent = null;
|
||||
|
||||
@ -20,112 +24,145 @@ class CreateLeenkxNode extends LogicNode {
|
||||
}
|
||||
|
||||
override function run(from:Int) {
|
||||
#if js
|
||||
#if lnx_torrent
|
||||
net_Url = inputs[1].get();
|
||||
if (net_Url == null) return;
|
||||
if(Leenkx.connections[net_Url] == null){
|
||||
var object = tree.object;
|
||||
if (Leenkx.connections[net_Url] == null) {
|
||||
value = Leenkx.onLoadEvent;
|
||||
listener = Event.add(value, onEvent, tree.object.uid);
|
||||
client = new leenkx.network.Leenkx(net_Url, object);
|
||||
|
||||
} else {
|
||||
return;
|
||||
new leenkx.network.Leenkx(net_Url, tree.object);
|
||||
}
|
||||
#end
|
||||
}
|
||||
|
||||
function onEvent() {
|
||||
#if js
|
||||
var lnxjs:Dynamic = js.Lib.global;
|
||||
var lnxCxNew = lnxjs.lnxNew;
|
||||
|
||||
if (lnxCxNew == null) {
|
||||
trace("ERROR: lnxNew not available");
|
||||
if (listener != null) {
|
||||
Event.removeListener(listener);
|
||||
listener = null;
|
||||
}
|
||||
#if lnx_torrent
|
||||
var sock = Leenkx.connections[net_Url];
|
||||
if (sock == null) return;
|
||||
if (sock.client != null) {
|
||||
runOutput(0);
|
||||
return;
|
||||
}
|
||||
var opts:leenkx.network.torrent.Types.LeenkxOptions = {deferJoin: true};
|
||||
var nextInput = 2;
|
||||
if (property0 == "datachannel") {
|
||||
opts.transport = "datachannel";
|
||||
var imode = property2 != null ? property2 : "inherit";
|
||||
if (imode != "inherit") {
|
||||
if (inputs.length > nextInput) {
|
||||
var ice = TorrentClient.trackerUrls(inputs[nextInput].get());
|
||||
if (ice != null) {
|
||||
opts.iceServers = imode == "override" ? ice : RtcPeerPool.defaultIceServers.concat(ice);
|
||||
}
|
||||
}
|
||||
nextInput++;
|
||||
}
|
||||
}
|
||||
var tmode = property3 != null ? property3 : "inherit";
|
||||
var trackers:Array<String> = null;
|
||||
if (tmode != "inherit" && inputs.length > nextInput) {
|
||||
trackers = TorrentClient.trackerUrls(inputs[nextInput].get());
|
||||
}
|
||||
opts.announce = tmode == "override" && trackers != null ? trackers
|
||||
: trackers != null ? TorrentClient.defaultTrackers.concat(trackers)
|
||||
: TorrentClient.defaultTrackers;
|
||||
var lnxCn = new LeenkxClient(net_Url, opts);
|
||||
lnxCn.isRelay = property1 == true;
|
||||
|
||||
var lnxCn:Dynamic = lnxCxNew(net_Url);
|
||||
lnxCn.onOpen = function() {
|
||||
Leenkx.data.set(net_Url, lnxCn.lastPeerCount);
|
||||
Leenkx.id.set(net_Url, lnxCn.lastPeerId);
|
||||
sock.onopen();
|
||||
};
|
||||
lnxCn.onConnections = function(c) {
|
||||
Leenkx.data.set(net_Url, c + 1);
|
||||
sock.onconnections();
|
||||
};
|
||||
lnxCn.onMessage = function(address, message, nonce) {
|
||||
Leenkx.data.set(net_Url, message);
|
||||
Leenkx.id.set(net_Url, address);
|
||||
sock.onmessage();
|
||||
};
|
||||
lnxCn.onSeen = function(address) {
|
||||
Leenkx.id.set(net_Url, address);
|
||||
sock.onseen();
|
||||
};
|
||||
lnxCn.onLeft = function(address) {
|
||||
Leenkx.id.set(net_Url, address);
|
||||
sock.onleft();
|
||||
};
|
||||
lnxCn.onServer = function(address) {
|
||||
Leenkx.id.set(net_Url, address);
|
||||
sock.onserver();
|
||||
};
|
||||
lnxCn.onPing = function(address) {
|
||||
Leenkx.id.set(net_Url, address);
|
||||
sock.onping();
|
||||
};
|
||||
lnxCn.onTimeout = function(address) {
|
||||
Leenkx.id.set(net_Url, address);
|
||||
sock.ontimeout();
|
||||
};
|
||||
lnxCn.onWireLeft = function(wirecount, wire) {
|
||||
Leenkx.data.set(net_Url, wirecount);
|
||||
if (wire != null && wire.peerId != null) {
|
||||
Leenkx.id.set(net_Url, wire.peerId);
|
||||
}
|
||||
sock.onwireleft();
|
||||
};
|
||||
lnxCn.onWireSeen = function(wirecount, wire) {
|
||||
Leenkx.data.set(net_Url, wirecount);
|
||||
if (wire != null && wire.peerId != null) {
|
||||
Leenkx.id.set(net_Url, wire.peerId);
|
||||
}
|
||||
sock.onwireseen();
|
||||
};
|
||||
lnxCn.onTorrent = function(identifier, torrent) {
|
||||
Leenkx.data.set(net_Url, torrent);
|
||||
Leenkx.id.set(net_Url, identifier);
|
||||
sock.ontorrent();
|
||||
};
|
||||
lnxCn.onTracker = function(identifier, tracker) {
|
||||
Leenkx.id.set(net_Url, identifier);
|
||||
sock.ontracker();
|
||||
};
|
||||
lnxCn.onAnnounce = function(identifier) {
|
||||
Leenkx.id.set(net_Url, identifier);
|
||||
sock.onannounce();
|
||||
};
|
||||
lnxCn.onError = function(msg) {
|
||||
Leenkx.data.set(net_Url, msg);
|
||||
sock.onerror();
|
||||
};
|
||||
lnxCn.onTorrentAdded = function(t) {
|
||||
Leenkx.torrent.set(net_Url, t);
|
||||
Leenkx.file.set(net_Url, t.files);
|
||||
Leenkx.data.set(net_Url, t);
|
||||
sock.ontorrentadded();
|
||||
};
|
||||
lnxCn.onTorrentDone = function(t) {
|
||||
Leenkx.data.set(net_Url, t);
|
||||
sock.ontorrentdone();
|
||||
};
|
||||
|
||||
lnxCn.on("connections", function(c) {
|
||||
leenkx.network.Leenkx.data.set(net_Url, c + 1);
|
||||
leenkx.network.Leenkx.connections[net_Url].onconnections();
|
||||
});
|
||||
lnxCn.on("message", function(address, message) {
|
||||
leenkx.network.Leenkx.data.set(net_Url, message);
|
||||
leenkx.network.Leenkx.id.set(net_Url, address);
|
||||
leenkx.network.Leenkx.connections[net_Url].onmessage();
|
||||
});
|
||||
lnxCn.on("seen", function(address) {
|
||||
leenkx.network.Leenkx.id.set(net_Url, address);
|
||||
leenkx.network.Leenkx.connections[net_Url].onseen();
|
||||
});
|
||||
lnxCn.on("left", function(address) {
|
||||
leenkx.network.Leenkx.id.set(net_Url, address);
|
||||
leenkx.network.Leenkx.connections[net_Url].onleft();
|
||||
});
|
||||
lnxCn.on("server", function(address) {
|
||||
leenkx.network.Leenkx.id.set(net_Url, address);
|
||||
leenkx.network.Leenkx.connections[net_Url].onserver();
|
||||
});
|
||||
lnxCn.on("ping", function(address) {
|
||||
leenkx.network.Leenkx.id.set(net_Url, address);
|
||||
leenkx.network.Leenkx.connections[net_Url].onping();
|
||||
});
|
||||
lnxCn.on("timeout", function(address) {
|
||||
leenkx.network.Leenkx.id.set(net_Url, address);
|
||||
leenkx.network.Leenkx.connections[net_Url].ontimeout();
|
||||
});
|
||||
lnxCn.on("rpc", function(address, call, args, nonce) {
|
||||
leenkx.network.Leenkx.data.set(net_Url, call);
|
||||
leenkx.network.Leenkx.id.set(net_Url, address);
|
||||
call(args);
|
||||
leenkx.network.Leenkx.connections[net_Url].onrpc();
|
||||
});
|
||||
lnxCn.on("rpc-response", function(address, nonce, response) {
|
||||
leenkx.network.Leenkx.id.set(net_Url, address);
|
||||
leenkx.network.Leenkx.connections[net_Url].onrpcresponse();
|
||||
});
|
||||
lnxCn.on("wireleft", function(wirecount, wire) {
|
||||
leenkx.network.Leenkx.data.set(net_Url, wirecount);
|
||||
leenkx.network.Leenkx.id.set(net_Url, wire.peerId);
|
||||
leenkx.network.Leenkx.connections[net_Url].onwireleft();
|
||||
});
|
||||
lnxCn.on("wireseen", function(wirecount, wire) {
|
||||
leenkx.network.Leenkx.data.set(net_Url, wirecount);
|
||||
leenkx.network.Leenkx.id.set(net_Url, wire.peerId);
|
||||
leenkx.network.Leenkx.connections[net_Url].onwireseen();
|
||||
});
|
||||
lnxCn.on("torrent", function(identifier, torrent) {
|
||||
leenkx.network.Leenkx.data.set(net_Url, torrent);
|
||||
leenkx.network.Leenkx.id.set(net_Url, identifier);
|
||||
leenkx.network.Leenkx.connections[net_Url].ontorrent();
|
||||
});
|
||||
lnxCn.on("tracker", function(identifier) {
|
||||
leenkx.network.Leenkx.id.set(net_Url, identifier);
|
||||
leenkx.network.Leenkx.connections[net_Url].ontracker();
|
||||
});
|
||||
lnxCn.on("announce", function(identifier) {
|
||||
leenkx.network.Leenkx.id.set(net_Url, identifier);
|
||||
leenkx.network.Leenkx.connections[net_Url].onannounce();
|
||||
});
|
||||
sock.client = lnxCn;
|
||||
|
||||
Reflect.setField(lnxjs, "lnx_" + net_Url, lnxCn);
|
||||
Leenkx.connections[net_Url].client = lnxCn;
|
||||
|
||||
var script = 'globalThis.addEventListener("beforeunload", function (e) {
|
||||
leenkx.network.Leenkx.connections.h["' + net_Url + '"].client.destroy();
|
||||
delete e["returnValue"];
|
||||
});';
|
||||
js.Syntax.code('(1, eval)({0})', script);
|
||||
|
||||
runOutput(0);
|
||||
lnxCn.connect();
|
||||
#if (js && !kha_krom)
|
||||
js.Browser.window.addEventListener("beforeunload", function(e) {
|
||||
lnxCn.destroy();
|
||||
});
|
||||
#end
|
||||
#end
|
||||
runOutput(0);
|
||||
}
|
||||
|
||||
|
||||
override function get(from: Int): Dynamic {
|
||||
#if js
|
||||
#if lnx_torrent
|
||||
return switch (from) {
|
||||
case 1: Leenkx.connections[net_Url];
|
||||
case 2: Leenkx.id.get(net_Url);
|
||||
|
||||
@ -1,43 +1,100 @@
|
||||
package leenkx.logicnode;
|
||||
import leenkx.system.Event;
|
||||
#if lnx_torrent
|
||||
import leenkx.network.Leenkx;
|
||||
import iron.object.Object;
|
||||
import leenkx.network.torrent.Magnet;
|
||||
import leenkx.network.torrent.TorrentClient;
|
||||
#end
|
||||
|
||||
|
||||
class CreateTorrentNode extends LogicNode {
|
||||
public var torrent: Dynamic;
|
||||
public var net_Url: String;
|
||||
public var meta:Dynamic;
|
||||
public var metaBytes:Dynamic;
|
||||
public var magnetUri:Dynamic;
|
||||
public var infoHashHex:String;
|
||||
public var property0:String;
|
||||
public var property1:String;
|
||||
|
||||
public function new(tree:LogicTree) {
|
||||
super(tree);
|
||||
}
|
||||
|
||||
override function run(from:Int) {
|
||||
#if lnx_torrent
|
||||
meta = null;
|
||||
metaBytes = null;
|
||||
magnetUri = null;
|
||||
infoHashHex = null;
|
||||
|
||||
var connection = inputs[1].get();
|
||||
if (connection == null) return;
|
||||
var data = inputs[2].get();
|
||||
var data = inputs[1].get();
|
||||
if (data == null) return;
|
||||
net_Url = connection._url;
|
||||
var mode = property1 != null ? property1 : "file";
|
||||
var opts:Dynamic = {};
|
||||
var mv = property0 != null ? Std.parseInt(property0) : null;
|
||||
if (mv != null) Reflect.setField(opts, "metaVersion", mv);
|
||||
field(2, opts, "name");
|
||||
field(3, opts, "comment");
|
||||
field(4, opts, "createdBy");
|
||||
field(5, opts, "source");
|
||||
field(6, opts, "announce");
|
||||
field(7, opts, "private");
|
||||
field(8, opts, "pieceLength");
|
||||
field(9, opts, "urlList");
|
||||
|
||||
var script = 'var file = ' + data + ';
|
||||
lx_' + net_Url +'.wt.add(file, function (torrent) {
|
||||
leenkx.network.Leenkx.file.set("' + net_Url +'", torrent.files);
|
||||
torrent.on("done", function () {
|
||||
leenkx.network.Leenkx.connections.h["' + net_Url +'"].ontorrentdone();
|
||||
});
|
||||
});
|
||||
';
|
||||
if (mode == "folder") {
|
||||
#if sys
|
||||
data = TorrentClient.readFolder(Std.string(data));
|
||||
var first:Dynamic = data[0];
|
||||
if (first != null && Reflect.field(opts, "name") == null) {
|
||||
var p = Std.string(first.path);
|
||||
var i = p.indexOf("/");
|
||||
Reflect.setField(opts, "name", p.substring(0, i >= 0 ? i : p.length));
|
||||
}
|
||||
#else
|
||||
trace("Folder mode requires a sys target";
|
||||
#end
|
||||
}
|
||||
|
||||
js.Syntax.code('(1, eval)({0})', script.toString());
|
||||
var m;
|
||||
try {
|
||||
m = TorrentClient.buildMeta(data, opts);
|
||||
} catch(e:Dynamic) {
|
||||
trace(e);
|
||||
return;
|
||||
}
|
||||
meta = m;
|
||||
metaBytes = m.toTorrentBytes();
|
||||
infoHashHex = m.infoHashHex != null ? m.infoHashHex : m.infoHashV2Hex;
|
||||
magnetUri = Magnet.build(m.infoHash, m.name, m.announce, m.urlList, m.infoHashV2);
|
||||
#end
|
||||
runOutput(0);
|
||||
}
|
||||
|
||||
#if lnx_torrent
|
||||
function field(i:Int, opts:Dynamic, name:String):Void {
|
||||
if (i >= inputs.length) return;
|
||||
var v:Dynamic = inputs[i].get();
|
||||
if (v == null || Std.string(v) == "") {
|
||||
return;
|
||||
}
|
||||
if (name == "announce") {
|
||||
v = TorrentClient.trackerUrls(v);
|
||||
if (v == null) return;
|
||||
}
|
||||
Reflect.setField(opts, name, v);
|
||||
}
|
||||
#end
|
||||
|
||||
override function get(from: Int): Dynamic {
|
||||
override function get(from:Int):Dynamic {
|
||||
#if lnx_torrent
|
||||
return switch (from) {
|
||||
case 1: Leenkx.file.get(net_Url);
|
||||
case 1: meta;
|
||||
case 2: metaBytes;
|
||||
case 3: infoHashHex;
|
||||
case 4: magnetUri;
|
||||
default: throw "Unreachable";
|
||||
}
|
||||
#else
|
||||
return null;
|
||||
#end
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
88
leenkx/Sources/leenkx/logicnode/CreateTrackerNode.hx
Normal file
88
leenkx/Sources/leenkx/logicnode/CreateTrackerNode.hx
Normal file
@ -0,0 +1,88 @@
|
||||
package leenkx.logicnode;
|
||||
#if (lnx_torrent && (sys || kha_krom))
|
||||
import leenkx.network.torrent.tracker.HttpTracker.HttpTrackerServer;
|
||||
import leenkx.network.torrent.tracker.UdpTracker.UdpTrackerServer;
|
||||
import leenkx.network.torrent.transport.WsTrackerClient.WsTrackerServer;
|
||||
#end
|
||||
#if (lnx_torrent && sys)
|
||||
import leenkx.network.torrent.transport.WsTrackerClient.WssTrackerServer;
|
||||
#end
|
||||
|
||||
class CreateTrackerNode extends LogicNode {
|
||||
public var property0:String;
|
||||
public var tracker:Dynamic;
|
||||
public var url:String;
|
||||
|
||||
public function new(tree:LogicTree) {
|
||||
super(tree);
|
||||
}
|
||||
|
||||
override function run(from:Int) {
|
||||
tracker = null;
|
||||
url = null;
|
||||
startServer();
|
||||
runOutput(0);
|
||||
}
|
||||
|
||||
function startServer():Void {
|
||||
#if (lnx_torrent && (sys || kha_krom))
|
||||
var hv = inputs[1].get();
|
||||
var host = hv != null ? Std.string(hv) : "127.0.0.1";
|
||||
if (host == "") host = "127.0.0.1";
|
||||
var pv = inputs[2].get();
|
||||
var port = pv != null ? Std.int(Std.parseFloat(Std.string(pv))) : 0;
|
||||
if (port <= 0) port = 6969;
|
||||
var scheme = property0 != null ? property0 : "udp";
|
||||
switch (scheme) {
|
||||
case "http":
|
||||
var s = new HttpTrackerServer();
|
||||
s.onError = function(e) trace(e);
|
||||
s.start(port);
|
||||
tracker = s;
|
||||
url = "http://" + host + ":" + port + "/announce";
|
||||
case "ws":
|
||||
var s = new WsTrackerServer(host, port);
|
||||
s.onError = function(e) trace(e);
|
||||
s.start();
|
||||
tracker = s;
|
||||
url = "ws://" + host + ":" + port;
|
||||
case "wss":
|
||||
#if sys
|
||||
var cert = inputs.length > 3 ? Std.string(inputs[3].get()) : null;
|
||||
var key = inputs.length > 4 ? Std.string(inputs[4].get()) : null;
|
||||
if (cert == null || cert == "" || key == null || key == "") {
|
||||
trace("wss server needs Cert File and Key File");
|
||||
return;
|
||||
}
|
||||
var s = new WssTrackerServer(host, port, cert, key);
|
||||
s.onError = function(e) trace(e);
|
||||
s.start();
|
||||
tracker = s;
|
||||
url = "wss://" + host + ":" + port;
|
||||
#else
|
||||
trace("wss server requires a sys target");
|
||||
#end
|
||||
default:
|
||||
var s = new UdpTrackerServer();
|
||||
s.onError = function(e) trace(e);
|
||||
s.start(port);
|
||||
tracker = s;
|
||||
url = "udp://" + host + ":" + port;
|
||||
}
|
||||
#else
|
||||
trace("server mode needs a sys or krom target");
|
||||
#end
|
||||
}
|
||||
|
||||
override function get(from:Int):Dynamic {
|
||||
#if lnx_torrent
|
||||
return switch (from) {
|
||||
case 1: tracker;
|
||||
case 2: url;
|
||||
default: throw "Unreachable";
|
||||
}
|
||||
#else
|
||||
return null;
|
||||
#end
|
||||
}
|
||||
}
|
||||
101
leenkx/Sources/leenkx/logicnode/DownloadTorrentNode.hx
Normal file
101
leenkx/Sources/leenkx/logicnode/DownloadTorrentNode.hx
Normal file
@ -0,0 +1,101 @@
|
||||
package leenkx.logicnode;
|
||||
import leenkx.system.Event;
|
||||
#if lnx_torrent
|
||||
import leenkx.network.Leenkx;
|
||||
import leenkx.network.torrent.TorrentClient;
|
||||
#end
|
||||
|
||||
class DownloadTorrentNode extends LogicNode {
|
||||
public var net_Url:String;
|
||||
public var torrent:Dynamic;
|
||||
public var property0:String;
|
||||
public var property1:String;
|
||||
var listener:TEvent = null;
|
||||
|
||||
public function new(tree:LogicTree) {
|
||||
super(tree);
|
||||
}
|
||||
|
||||
public function promiseResult(connection, torrentid, net_Url, ?opts:Dynamic) {
|
||||
#if lnx_torrent
|
||||
var socket = Leenkx.connections[net_Url];
|
||||
if (socket == null) {
|
||||
if (listener == null) {
|
||||
var self = this;
|
||||
listener = Event.add(Leenkx.onLoadEvent, function() {
|
||||
if (Leenkx.connections[net_Url] == null) return;
|
||||
if (self.listener != null) {
|
||||
Event.removeListener(self.listener);
|
||||
self.listener = null;
|
||||
}
|
||||
self.promiseResult(connection, torrentid, net_Url, opts);
|
||||
});
|
||||
}
|
||||
return;
|
||||
}
|
||||
var client = socket.client;
|
||||
if (client == null) return;
|
||||
var self = this;
|
||||
client.add(torrentid, opts, function(torrent) {
|
||||
self.torrent = torrent;
|
||||
var onReady = function() {
|
||||
self.runOutput(0);
|
||||
};
|
||||
var onDone = function() {
|
||||
self.runOutput(4);
|
||||
};
|
||||
if (torrent.ready) onReady();
|
||||
else torrent.on("ready", onReady);
|
||||
if (torrent.done) onDone();
|
||||
else torrent.on("done", onDone);
|
||||
});
|
||||
#end
|
||||
}
|
||||
|
||||
function onRemove() {
|
||||
if (listener != null) {
|
||||
Event.removeListener(listener);
|
||||
listener = null;
|
||||
}
|
||||
}
|
||||
|
||||
override function run(from:Int) {
|
||||
var connection = inputs[1].get();
|
||||
if (connection == null) return;
|
||||
var v = inputs[2].get();
|
||||
if (v == null) return;
|
||||
#if lnx_torrent
|
||||
net_Url = connection._url;
|
||||
var torrentId:Dynamic = switch (property0 != null ? property0 : "magnet") {
|
||||
case "file": v;
|
||||
case "id": TorrentClient.torrentIdFor(v);
|
||||
default: Std.string(v);
|
||||
};
|
||||
var opts:Dynamic = null;
|
||||
var tmode = property1 != null ? property1 : "inherit";
|
||||
if (tmode != "inherit" && inputs.length > 3) {
|
||||
var announce = TorrentClient.trackerUrls(inputs[3].get());
|
||||
opts = {};
|
||||
if (announce != null) opts.announce = announce;
|
||||
if (tmode == "override") opts.announceOverride = true;
|
||||
}
|
||||
promiseResult(connection, torrentId, net_Url, opts);
|
||||
#end
|
||||
}
|
||||
|
||||
override function get(from:Int):Dynamic {
|
||||
#if lnx_torrent
|
||||
var t:leenkx.network.torrent.Torrent = torrent;
|
||||
return switch (from) {
|
||||
case 1: torrent;
|
||||
case 2: t != null && t.meta != null ? t.meta.name : null;
|
||||
case 3: t != null ? t.files : null;
|
||||
case 5: t != null ? t.infoHashHex : null;
|
||||
case 6: t != null ? t.magnetURI : null;
|
||||
default: throw "Unreachable";
|
||||
}
|
||||
#else
|
||||
return null;
|
||||
#end
|
||||
}
|
||||
}
|
||||
83
leenkx/Sources/leenkx/logicnode/GetTorrentFileNode.hx
Normal file
83
leenkx/Sources/leenkx/logicnode/GetTorrentFileNode.hx
Normal file
@ -0,0 +1,83 @@
|
||||
package leenkx.logicnode;
|
||||
#if lnx_torrent
|
||||
import leenkx.network.Leenkx;
|
||||
import leenkx.network.torrent.Torrent;
|
||||
import leenkx.network.torrent.TorrentFile;
|
||||
#end
|
||||
|
||||
class GetTorrentFileNode extends LogicNode {
|
||||
public var property0:String;
|
||||
public var file:Dynamic;
|
||||
var net_Url:String;
|
||||
|
||||
public function new(tree:LogicTree) {
|
||||
super(tree);
|
||||
}
|
||||
|
||||
override function run(from:Int) {
|
||||
#if lnx_torrent
|
||||
file = null;
|
||||
var id = inputs[2].get();
|
||||
var match = inputs[3].get();
|
||||
if (id != null && match != null) {
|
||||
var connection = inputs[1].get();
|
||||
if (connection != null) net_Url = connection._url;
|
||||
var socket = net_Url != null ? Leenkx.connections[net_Url] : null;
|
||||
var client = socket != null ? socket.client : null;
|
||||
var t = client != null ? client.get(id) : (Std.isOfType(id, Torrent) ? id : null);
|
||||
if (t != null) file = findFile(t, match);
|
||||
}
|
||||
#end
|
||||
runOutput(0);
|
||||
}
|
||||
|
||||
#if lnx_torrent
|
||||
function findFile(t:Torrent, match:Dynamic):TorrentFile {
|
||||
var files = t.files;
|
||||
switch (property0) {
|
||||
case "index":
|
||||
var i = Std.parseInt(Std.string(match));
|
||||
if (i != null && i >= 0 && i < files.length) {
|
||||
return files[i];
|
||||
}
|
||||
case "name":
|
||||
var s = Std.string(match);
|
||||
for (f in files) {
|
||||
if (f.name == s) return f;
|
||||
}
|
||||
case "path":
|
||||
var s = Std.string(match);
|
||||
for (f in files) {
|
||||
if (f.path == s) return f;
|
||||
}
|
||||
default:
|
||||
var s = Std.string(match);
|
||||
if (!StringTools.startsWith(s, ".")) s = "." + s;
|
||||
for (f in files) {
|
||||
if (StringTools.endsWith(f.name, s)) return f;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
#end
|
||||
|
||||
override function get(from:Int):Dynamic {
|
||||
#if lnx_torrent
|
||||
var f:TorrentFile = file;
|
||||
return switch (from) {
|
||||
case 1: file;
|
||||
case 2: f != null ? f.name : null;
|
||||
case 3: f != null ? f.path : null;
|
||||
case 4: f != null ? f.length : 0;
|
||||
case 5: f != null ? f.type : null;
|
||||
case 6: f != null ? f.getBytes() : null;
|
||||
case 7: f != null;
|
||||
case 8: f != null ? f.downloaded : 0.0;
|
||||
case 9: f != null ? f.progress : 0.0;
|
||||
default: throw "Unreachable";
|
||||
}
|
||||
#else
|
||||
return null;
|
||||
#end
|
||||
}
|
||||
}
|
||||
@ -1,8 +1,8 @@
|
||||
package leenkx.logicnode;
|
||||
import leenkx.system.Event;
|
||||
import leenkx.network.Connect;
|
||||
import iron.object.Object;
|
||||
|
||||
#if lnx_torrent
|
||||
import leenkx.network.Leenkx;
|
||||
import leenkx.network.torrent.LeenkxClient;
|
||||
#end
|
||||
|
||||
class LeenkxCloseConnectionNode extends LogicNode {
|
||||
public var property0: String;
|
||||
@ -12,41 +12,29 @@ class LeenkxCloseConnectionNode extends LogicNode {
|
||||
}
|
||||
|
||||
override function run(from:Int) {
|
||||
#if lnx_torrent
|
||||
var connection = inputs[1].get();
|
||||
if (connection == null) return;
|
||||
var socket = Leenkx.connections[connection._url];
|
||||
if (socket == null) return;
|
||||
|
||||
if(property0 == "client") {
|
||||
var script = '
|
||||
for (p in leenkx.network.Leenkx.connections.h["' + connection._url + '"].client.torrent._peers){
|
||||
try{
|
||||
leenkx.network.Leenkx.id.set("' + connection._url + '",p);
|
||||
leenkx.network.Leenkx.connections.h["' + connection._url + '"].client.torrent._peers[p].conn._pc.close();
|
||||
leenkx.network.Leenkx.connections.h["' + connection._url + '"].client.torrent._peers[p].conn.destroy();
|
||||
leenkx.network.Leenkx.connections.h["' + connection._url + '"].onclose();
|
||||
}catch(error){
|
||||
console.log("Error: " + error);
|
||||
var lnxCn:LeenkxClient = socket.client;
|
||||
if (property0 == "peer") {
|
||||
var id:String = inputs.length > 2 ? inputs[2].get() : null;
|
||||
if (id != null && lnxCn != null) {
|
||||
lnxCn.peers.remove(id);
|
||||
lnxCn.seenFired.remove(id);
|
||||
}
|
||||
runOutput(0);
|
||||
return;
|
||||
}
|
||||
';
|
||||
js.Syntax.code('(1, eval)({0})', script.toString());
|
||||
} else {
|
||||
var script = '
|
||||
try{
|
||||
var lnxConn = leenkx.network.Leenkx.connections.h["' + connection._url + '"];
|
||||
if (lnxConn && lnxConn.client && lnxConn.client.torrent && lnxConn.client.torrent._peers) {
|
||||
for (var p in lnxConn.client.torrent._peers) {
|
||||
lnxConn.client.torrent._peers[p].conn._pc.close();
|
||||
lnxConn.client.torrent._peers[p].conn.destroy();
|
||||
leenkx.network.Leenkx.id.set("' + connection._url + '", p);
|
||||
lnxConn.onclose();
|
||||
}
|
||||
}
|
||||
}catch(error){
|
||||
console.log("Error: " + error);
|
||||
}
|
||||
';
|
||||
js.Syntax.code('(1, eval)({0})', script.toString());
|
||||
if (lnxCn != null) {
|
||||
lnxCn.destroy();
|
||||
socket.client = null;
|
||||
}
|
||||
Leenkx.connections.remove(connection._url);
|
||||
socket.onclose();
|
||||
#end
|
||||
runOutput(0);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,10 +1,10 @@
|
||||
package leenkx.logicnode;
|
||||
import leenkx.system.Event;
|
||||
#if lnx_torrent
|
||||
import leenkx.network.Leenkx;
|
||||
import iron.object.Object;
|
||||
#end
|
||||
|
||||
class LeenkxEventNode extends LogicNode {
|
||||
public var property0: String;
|
||||
public var property1: String;
|
||||
|
||||
public var value: String;
|
||||
@ -14,90 +14,63 @@ class LeenkxEventNode extends LogicNode {
|
||||
public function new(tree:LogicTree) {
|
||||
super(tree);
|
||||
tree.notifyOnInit(init);
|
||||
tree.notifyOnRemove(onRemove);
|
||||
}
|
||||
|
||||
function init() {
|
||||
if (property0 == "client") {
|
||||
net_Url = inputs[0].get();
|
||||
#if lnx_torrent
|
||||
var u:Dynamic = inputs[0].get();
|
||||
net_Url = Std.isOfType(u, String) ? u : (u != null ? u._url : null);
|
||||
switch (property1) {
|
||||
case "onopen": value = Leenkx.onOpenEvent;listener = Event.add(value, onEvent, tree.object.uid);
|
||||
case "onmessage": value = Leenkx.onMessageEvent;listener = Event.add(value, onEvent, tree.object.uid);
|
||||
case "onerror": value = Leenkx.onErrorEvent;listener = Event.add(value, onEvent, tree.object.uid);
|
||||
case "onclose": value = Leenkx.onCloseEvent;listener = Event.add(value, onEvent, tree.object.uid);
|
||||
case "onseen": value = Leenkx.onSeenEvent;listener = Event.add(value, onEvent, tree.object.uid);
|
||||
case "onserver": value = Leenkx.onServerEvent;listener = Event.add(value, onEvent, tree.object.uid);
|
||||
case "onconnections": value = Leenkx.onConnectionsEvent;listener = Event.add(value, onEvent, tree.object.uid);
|
||||
case "onping": value = Leenkx.onPingEvent;listener = Event.add(value, onEvent, tree.object.uid);
|
||||
case "onleft": value = Leenkx.onLeftEvent;listener = Event.add(value, onEvent, tree.object.uid);
|
||||
case "ontimeout": value = Leenkx.onTimeOutEvent;listener = Event.add(value, onEvent, tree.object.uid);
|
||||
case "onrpc": value = Leenkx.onRpcEvent;listener = Event.add(value, onEvent, tree.object.uid);
|
||||
case "onrpcresponse": value = Leenkx.onRpcResponseEvent;listener = Event.add(value, onEvent, tree.object.uid);
|
||||
case "onwireleft": value = Leenkx.onWireLeftEvent;listener = Event.add(value, onEvent, tree.object.uid);
|
||||
case "onwireseen": value = Leenkx.onWireSeenEvent;listener = Event.add(value, onEvent, tree.object.uid);
|
||||
case "ontorrent": value = Leenkx.onTorrentEvent;listener = Event.add(value, onEvent, tree.object.uid);
|
||||
case "ontracker": value = Leenkx.onTrackerEvent;listener = Event.add(value, onEvent, tree.object.uid);
|
||||
case "onannounce": value = Leenkx.onAnnounceEvent;listener = Event.add(value, onEvent, tree.object.uid);
|
||||
default: throw "Failed to set client event type.";
|
||||
}
|
||||
} else if (property0 == "host") {
|
||||
#if (sys || kha_krom)
|
||||
var net_Domain = inputs[0].get();
|
||||
var net_Port = inputs[1].get();
|
||||
net_Url = "ws://" + net_Domain + ":" + Std.string(net_Port);
|
||||
switch (property1) {
|
||||
case "onopen": value = Leenkx.onOpenEvent;listener = Event.add(value, onEvent, tree.object.uid);
|
||||
case "onmessage": value = Leenkx.onMessageEvent;listener = Event.add(value, onEvent, tree.object.uid);
|
||||
case "onerror": value = Leenkx.onErrorEvent;listener = Event.add(value, onEvent, tree.object.uid);
|
||||
case "onclose": value = Leenkx.onCloseEvent;listener = Event.add(value, onEvent, tree.object.uid);
|
||||
default: throw "Failed to set host event type.";
|
||||
case "onopen": value = Leenkx.onOpenEvent;
|
||||
case "onmessage": value = Leenkx.onMessageEvent;
|
||||
case "onerror": value = Leenkx.onErrorEvent;
|
||||
case "onclose": value = Leenkx.onCloseEvent;
|
||||
case "onseen": value = Leenkx.onSeenEvent;
|
||||
case "onserver": value = Leenkx.onServerEvent;
|
||||
case "onconnections": value = Leenkx.onConnectionsEvent;
|
||||
case "onping": value = Leenkx.onPingEvent;
|
||||
case "onleft": value = Leenkx.onLeftEvent;
|
||||
case "ontimeout": value = Leenkx.onTimeOutEvent;
|
||||
case "onwireleft": value = Leenkx.onWireLeftEvent;
|
||||
case "onwireseen": value = Leenkx.onWireSeenEvent;
|
||||
case "ontorrent": value = Leenkx.onTorrentEvent;
|
||||
case "ontorrentadded": value = Leenkx.onTorrentAddedEvent;
|
||||
case "ontracker": value = Leenkx.onTrackerEvent;
|
||||
case "onannounce": value = Leenkx.onAnnounceEvent;
|
||||
case "ontorrentdone": value = Leenkx.onTorrentDoneEvent;
|
||||
case "onload": value = Leenkx.onLoadEvent;
|
||||
default:
|
||||
trace("Failed to set client event type");
|
||||
}
|
||||
listener = Event.add(value, onEvent, tree.object.uid);
|
||||
#end
|
||||
} else if (property0 == "securehost"){
|
||||
#if (sys || kha_krom)
|
||||
var net_Domain = inputs[0].get();
|
||||
var net_Port = inputs[1].get();
|
||||
net_Url = "wss://" + net_Domain + ":" + Std.string(net_Port);
|
||||
switch (property1) {
|
||||
case "onopen": value = Leenkx.onOpenEvent;listener = Event.add(value, onEvent, tree.object.uid);
|
||||
case "onmessage": value = Leenkx.onMessageEvent;listener = Event.add(value, onEvent, tree.object.uid);
|
||||
case "onerror": value = Leenkx.onErrorEvent;listener = Event.add(value, onEvent, tree.object.uid);
|
||||
case "onclose": value = Leenkx.onCloseEvent;listener = Event.add(value, onEvent, tree.object.uid);
|
||||
default: throw "Failed to set host event type.";
|
||||
}
|
||||
#end
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
function onEvent() {
|
||||
runOutput(0);
|
||||
}
|
||||
|
||||
function onRemove() {
|
||||
if (listener != null) {
|
||||
Event.removeListener(listener);
|
||||
listener = null;
|
||||
}
|
||||
}
|
||||
|
||||
override function get(from: Int): Dynamic {
|
||||
if (property0 == "host") {
|
||||
#if lnx_torrent
|
||||
return switch (from) {
|
||||
case 1: Leenkx.id.get(net_Url);
|
||||
case 2: Leenkx.data.get(net_Url);
|
||||
default: throw "Unreachable";
|
||||
}
|
||||
}
|
||||
else if (property0 == "securehost") {
|
||||
return switch (from) {
|
||||
case 1: Leenkx.id.get(net_Url);
|
||||
case 2: Leenkx.data.get(net_Url);
|
||||
default: throw "Unreachable";
|
||||
}
|
||||
}
|
||||
else {
|
||||
return switch (from) {
|
||||
case 1: Leenkx.id.get(net_Url);
|
||||
case 2: Leenkx.data.get(net_Url);
|
||||
default: throw "Unreachable";
|
||||
}
|
||||
}
|
||||
#else
|
||||
return null;
|
||||
#end
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@ -1,8 +1,6 @@
|
||||
package leenkx.logicnode;
|
||||
import leenkx.network.Connect;
|
||||
import leenkx.network.Buffer;
|
||||
import haxe.io.Bytes;
|
||||
import iron.object.Object;
|
||||
import iron.math.Vec4;
|
||||
import iron.math.Quat;
|
||||
import iron.math.Mat4;
|
||||
@ -22,139 +20,106 @@ class LeenkxMessageParserNode extends LogicNode {
|
||||
if (data == null) return;
|
||||
|
||||
api = inputs[1].get();
|
||||
if (api == null) api = "";
|
||||
|
||||
var buffer:Buffer;
|
||||
if (Std.isOfType(data, Buffer)) {
|
||||
buffer = cast data;
|
||||
} else {
|
||||
var rawBytes:Bytes;
|
||||
try {
|
||||
rawBytes = haxe.crypto.Base64.decode(Std.string(data));
|
||||
} catch(e) {
|
||||
trace("Base64 decode failed: " + e);
|
||||
return;
|
||||
}
|
||||
buffer = new Buffer();
|
||||
buffer.writeBytes(rawBytes);
|
||||
}
|
||||
|
||||
/**
|
||||
var buffer = cast(data, Buffer);
|
||||
api = inputs[1].get();
|
||||
if(buffer.endsWith(api) == false){
|
||||
return;
|
||||
}
|
||||
**/
|
||||
var buffer = new Buffer();
|
||||
if(!StringTools.startsWith(data,api)){
|
||||
return;
|
||||
}
|
||||
switch (property0) {
|
||||
case "string":
|
||||
parsed = data.substr(api.length, data.length);
|
||||
runOutput(0);
|
||||
case "vector":
|
||||
var vec = data.substr(api.length, data.length);
|
||||
vec = StringTools.replace(vec, "(", "");
|
||||
vec = StringTools.replace(vec, ")", "");
|
||||
vec = StringTools.replace(vec, " ", "");
|
||||
vec = StringTools.trim(vec);
|
||||
var array = vec.split(",").map(Std.parseFloat);
|
||||
parsed = new Vec4( array[0], array[1], array[2], array[3] );
|
||||
runOutput(0);
|
||||
case "float":
|
||||
var flt:String = data.substr(api.length, data.length);
|
||||
var float = StringTools.trim(flt);
|
||||
parsed = Std.parseFloat(float);
|
||||
runOutput(0);
|
||||
case "integer":
|
||||
var int:String = data.substr(api.length, data.length);
|
||||
var integer = StringTools.trim(int);
|
||||
parsed = Std.parseInt(integer);
|
||||
runOutput(0);
|
||||
case "boolean":
|
||||
var boolean:String = data.substr(api.length, data.length);
|
||||
if(boolean == "true"){
|
||||
parsed = true;
|
||||
} else {
|
||||
parsed = false;
|
||||
}
|
||||
runOutput(0);
|
||||
case "transform":
|
||||
var tsfm = data.substr(api.length, data.length);
|
||||
tsfm = StringTools.replace(tsfm, "[", "");
|
||||
tsfm = StringTools.replace(tsfm, "]", "");
|
||||
tsfm = StringTools.replace(tsfm, " ", "");
|
||||
tsfm = StringTools.trim(tsfm);
|
||||
var array = tsfm.split(",").map(Std.parseFloat);
|
||||
parsed = new Mat4(
|
||||
array[0], array[1], array[2], array[3],
|
||||
array[4], array[5], array[6], array[7],
|
||||
array[8], array[9], array[10], array[11],
|
||||
array[12], array[13], array[14], array[15]
|
||||
);
|
||||
runOutput(0);
|
||||
case "rotation":
|
||||
var bytes = new haxe.io.BytesInput(buffer.readUntil(api));
|
||||
var rot = new Quat();
|
||||
for (f in Reflect.fields(rot)) {
|
||||
Reflect.setField(rot, f, bytes.readFloat());
|
||||
}
|
||||
parsed = rot;
|
||||
runOutput(0);
|
||||
default: throw "Failed to parse data.";
|
||||
}
|
||||
return;
|
||||
|
||||
|
||||
|
||||
switch (property0) {
|
||||
case "string":
|
||||
var bytes = buffer.readAllAvailableBytes();
|
||||
parsed = bytes.toString().substr(0, bytes.length - api.length);
|
||||
trace(parsed);
|
||||
runOutput(0);
|
||||
case "vector":
|
||||
var bytes = new haxe.io.BytesInput(buffer.readUntil(api));
|
||||
var readBytes = buffer.readUntil(api);
|
||||
if (readBytes == null) return;
|
||||
var bytes = new haxe.io.BytesInput(readBytes);
|
||||
var vec = new Vec4();
|
||||
for (f in Reflect.fields(vec)) {
|
||||
Reflect.setField(vec, f, bytes.readFloat());
|
||||
}
|
||||
vec.x = bytes.readFloat();
|
||||
vec.y = bytes.readFloat();
|
||||
vec.z = bytes.readFloat();
|
||||
vec.w = bytes.readFloat();
|
||||
parsed = vec;
|
||||
runOutput(0);
|
||||
case "float":
|
||||
var bytes = new haxe.io.BytesInput(buffer.readUntil(api));
|
||||
var readBytes = buffer.readUntil(api);
|
||||
if (readBytes == null) return;
|
||||
var bytes = new haxe.io.BytesInput(readBytes);
|
||||
var float: Float = bytes.readFloat();
|
||||
parsed = float;
|
||||
runOutput(0);
|
||||
case "integer":
|
||||
var bytes = new haxe.io.BytesInput(buffer.readUntil(api));
|
||||
var readBytes = buffer.readUntil(api);
|
||||
if (readBytes == null) return;
|
||||
var bytes = new haxe.io.BytesInput(readBytes);
|
||||
var integer: Int = bytes.readInt32();
|
||||
parsed = integer;
|
||||
runOutput(0);
|
||||
case "boolean":
|
||||
var bytes = buffer.readAllAvailableBytes();
|
||||
var boolean = bytes.toString().substr(0, bytes.length - api.length);
|
||||
if(boolean == "true"){
|
||||
if (boolean == "true") {
|
||||
parsed = true;
|
||||
} else {
|
||||
parsed = false;
|
||||
}
|
||||
runOutput(0);
|
||||
case "transform":
|
||||
var bytes = new haxe.io.BytesInput(buffer.readUntil(api));
|
||||
var readBytes = buffer.readUntil(api);
|
||||
if (readBytes == null) return;
|
||||
var bytes = new haxe.io.BytesInput(readBytes);
|
||||
var loc: Vec4 = new Vec4();
|
||||
var rot: Quat = new Quat();
|
||||
var scl: Vec4 = new Vec4();
|
||||
for (f in Reflect.fields(loc)) {
|
||||
Reflect.setField(loc, f, bytes.readFloat());
|
||||
}
|
||||
for (f in Reflect.fields(rot)) {
|
||||
Reflect.setField(rot, f, bytes.readFloat());
|
||||
}
|
||||
for (f in Reflect.fields(scl)) {
|
||||
Reflect.setField(scl, f, bytes.readFloat());
|
||||
try {
|
||||
loc.x = bytes.readFloat();
|
||||
loc.y = bytes.readFloat();
|
||||
loc.z = bytes.readFloat();
|
||||
loc.w = bytes.readFloat();
|
||||
rot.x = bytes.readFloat();
|
||||
rot.y = bytes.readFloat();
|
||||
rot.z = bytes.readFloat();
|
||||
rot.w = bytes.readFloat();
|
||||
scl.x = bytes.readFloat();
|
||||
scl.y = bytes.readFloat();
|
||||
scl.z = bytes.readFloat();
|
||||
scl.w = bytes.readFloat();
|
||||
} catch(e: Dynamic) {
|
||||
trace("readFloat error: " + e);
|
||||
return;
|
||||
}
|
||||
var transform = Mat4.identity();
|
||||
parsed = transform.compose(loc, rot, scl);
|
||||
runOutput(0);
|
||||
case "rotation":
|
||||
var bytes = new haxe.io.BytesInput(buffer.readUntil(api));
|
||||
var readBytes = buffer.readUntil(api);
|
||||
if (readBytes == null) return;
|
||||
var bytes = new haxe.io.BytesInput(readBytes);
|
||||
var rot = new Quat();
|
||||
for (f in Reflect.fields(rot)) {
|
||||
Reflect.setField(rot, f, bytes.readFloat());
|
||||
}
|
||||
rot.x = bytes.readFloat();
|
||||
rot.y = bytes.readFloat();
|
||||
rot.z = bytes.readFloat();
|
||||
rot.w = bytes.readFloat();
|
||||
parsed = rot;
|
||||
runOutput(0);
|
||||
default: throw "Failed to parse data.";
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
override function get(from: Int): Dynamic {
|
||||
|
||||
@ -1,401 +1,124 @@
|
||||
package leenkx.logicnode;
|
||||
import leenkx.network.Connect;
|
||||
import leenkx.network.Leenkx;
|
||||
import leenkx.network.Buffer;
|
||||
import haxe.io.Bytes;
|
||||
import iron.object.Object;
|
||||
#if lnx_torrent
|
||||
import leenkx.network.Leenkx;
|
||||
import leenkx.network.torrent.LeenkxClient;
|
||||
#end
|
||||
import iron.math.Vec4;
|
||||
import iron.math.Quat;
|
||||
import iron.math.Mat4;
|
||||
|
||||
|
||||
class LeenkxSendMessageNode extends LogicNode {
|
||||
public var property0: String;
|
||||
public var property1: String;
|
||||
public var id: String;
|
||||
public var all: Bool;
|
||||
public var property2: Bool;
|
||||
|
||||
public function new(tree:LogicTree) {
|
||||
super(tree);
|
||||
}
|
||||
|
||||
override function run(from:Int) {
|
||||
#if js
|
||||
var connection = inputs[1].get();
|
||||
if (connection == null) return;
|
||||
var api: String = inputs[2].get();
|
||||
var message: Dynamic = inputs[3].get();
|
||||
|
||||
try {
|
||||
if(inputs[5].get() == true){
|
||||
var script = 'for (p in lnx_' + connection._url +'.torrent._peers){
|
||||
try{
|
||||
lnx_' + connection._url +'.torrent._peers[p].conn.send(`'+ api + message + '`);
|
||||
}catch(error){
|
||||
console.log("Error: " + error);
|
||||
}
|
||||
}';
|
||||
js.Syntax.code('(1, eval)({0})', script.toString());
|
||||
runOutput(0);
|
||||
} else {
|
||||
var script = 'lnx_' + connection._url +'.send(`' + inputs[4].get() + '`, `'+ api + message + '` );';
|
||||
js.Syntax.code('(1, eval)({0})', script.toString());
|
||||
runOutput(0);
|
||||
}
|
||||
|
||||
} catch(error) {
|
||||
trace("Error: " + error);
|
||||
}
|
||||
if (message == null) return;
|
||||
var broadcast: Bool = inputs.length > 4 && inputs[4].get() == true;
|
||||
var targetAddress: String = inputs.length > 5 ? inputs[5].get() : null;
|
||||
if (!broadcast && (targetAddress == null || targetAddress == "")) {
|
||||
return;
|
||||
}
|
||||
|
||||
#if lnx_torrent
|
||||
var socket = Leenkx.connections[connection._url];
|
||||
if (socket == null) return;
|
||||
var lnxCn:LeenkxClient = socket.client;
|
||||
if (lnxCn == null) return;
|
||||
|
||||
var buffer = new Buffer();
|
||||
|
||||
switch (property1) {
|
||||
case "string":
|
||||
if(property0 == "client"){
|
||||
var buffer = new Buffer();
|
||||
buffer.writeBytes(Bytes.ofString(message));
|
||||
if(api != "" || api != null){
|
||||
buffer.writeBytes(Bytes.ofString(api));
|
||||
|
||||
try {
|
||||
connection.buffer = buffer;
|
||||
if(inputs[5].get() == true){
|
||||
var script = 'for (p in lnx_' + connection._url +'.torrent._peers){
|
||||
try{
|
||||
lnx_' + connection._url +'.torrent._peers[p].conn.send(leenkx.network.Leenkx.connections.h["'+ connection._url +'"].buffer );
|
||||
}catch(error){
|
||||
console.log("Error: " + error);
|
||||
}
|
||||
}';
|
||||
js.Syntax.code('(1, eval)({0})', script.toString());
|
||||
runOutput(0);
|
||||
} else {
|
||||
var script = 'lnx_' + connection._url +'.send("' + inputs[4].get() + '",leenkx.network.Leenkx.connections.h["'+ connection._url +'"].buffer );';
|
||||
js.Syntax.code('(1, eval)({0})', script.toString());
|
||||
runOutput(0);
|
||||
}
|
||||
} catch(error) {
|
||||
trace("Error: " + error);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if(inputs[5].get() == true){
|
||||
var script = 'lnx_' + connection._url + '.send(`' + api + message + '`);';
|
||||
js.Syntax.code('(1, eval)({0})', script);
|
||||
}else{
|
||||
var script = 'lnx_' + connection._url + '.send(`' + inputs[4].get() + '`, `' + api + message + '`);';
|
||||
js.Syntax.code('(1, eval)({0})', script);
|
||||
}
|
||||
runOutput(0);
|
||||
return;
|
||||
}
|
||||
case "vector":
|
||||
if(property0 == "client"){
|
||||
var bytesOut = new haxe.io.BytesOutput();
|
||||
var i = Reflect.fields(message);
|
||||
for (b in i) {
|
||||
bytesOut.writeFloat(Reflect.field(message, b));
|
||||
}
|
||||
var buffer = new Buffer();
|
||||
var f:Null<Float>;
|
||||
if ((f = message.x) != null) bytesOut.writeFloat(f);
|
||||
if ((f = message.y) != null) bytesOut.writeFloat(f);
|
||||
if ((f = message.z) != null) bytesOut.writeFloat(f);
|
||||
if ((f = message.w) != null) bytesOut.writeFloat(f);
|
||||
buffer.writeBytes(bytesOut.getBytes());
|
||||
if(api != "" || api != null){
|
||||
buffer.writeBytes(Bytes.ofString(api));
|
||||
}
|
||||
|
||||
try {
|
||||
connection.buffer = buffer;
|
||||
if(inputs[5].get() == true){
|
||||
var script = 'for (p in lnx_' + connection._url +'.torrent._peers){
|
||||
try{
|
||||
lnx_' + connection._url +'.torrent._peers[p].conn.send("' + api + message + '");
|
||||
}catch(error){
|
||||
console.log("Error: " + error);
|
||||
}
|
||||
}';
|
||||
js.Syntax.code('(1, eval)({0})', script.toString());
|
||||
runOutput(0);
|
||||
} else {
|
||||
var script = 'lnx_' + connection._url +'.send("' + inputs[4].get() + '",leenkx.network.Leenkx.connections.h["'+ connection._url +'"].buffer );';
|
||||
js.Syntax.code('(1, eval)({0})', script.toString());
|
||||
runOutput(0);
|
||||
}
|
||||
} catch(error) {
|
||||
trace("Error: " + error);
|
||||
}
|
||||
} else {
|
||||
if(inputs[5].get() == true){
|
||||
var script = 'lnx_' + connection._url + '.send(`' + api + message + '`);';
|
||||
js.Syntax.code('(1, eval)({0})', script);
|
||||
}else{
|
||||
var script = 'lnx_' + connection._url + '.send(`' + inputs[4].get() + '`, `' + api + message + '`);';
|
||||
js.Syntax.code('(1, eval)({0})', script);
|
||||
}
|
||||
runOutput(0);
|
||||
return;
|
||||
}
|
||||
case "float":
|
||||
if(property0 == "client"){
|
||||
var bytesOut = new haxe.io.BytesOutput();
|
||||
bytesOut.writeFloat(message);
|
||||
var buffer = new Buffer();
|
||||
buffer.writeBytes(bytesOut.getBytes());
|
||||
if(api != "" || api != null){
|
||||
buffer.writeBytes(Bytes.ofString(api));
|
||||
}
|
||||
try {
|
||||
connection.buffer = buffer;
|
||||
if(inputs[5].get() == true){
|
||||
var script = 'for (p in lnx_' + connection._url +'.torrent._peers){
|
||||
try{
|
||||
lnx_' + connection._url +'.torrent._peers[p].conn.send(leenkx.network.Leenkx.connections.h["'+ connection._url +'"].buffer );
|
||||
}catch(error){
|
||||
console.log("Error: " + error);
|
||||
}
|
||||
}';
|
||||
js.Syntax.code('(1, eval)({0})', script.toString());
|
||||
runOutput(0);
|
||||
} else {
|
||||
var script = 'lnx_' + connection._url +'.send("' + inputs[4].get() + '",leenkx.network.Leenkx.connections.h["'+ connection._url +'"].buffer );';
|
||||
js.Syntax.code('(1, eval)({0})', script.toString());
|
||||
runOutput(0);
|
||||
}
|
||||
} catch(error) {
|
||||
trace("Error: " + error);
|
||||
}
|
||||
} else {
|
||||
if(inputs[5].get() == true){
|
||||
var script = 'lnx_' + connection._url + '.send(`' + api + message + '`);';
|
||||
js.Syntax.code('(1, eval)({0})', script);
|
||||
}else{
|
||||
var script = 'lnx_' + connection._url + '.send(`' + inputs[4].get() + '`, `' + api + message + '`);';
|
||||
js.Syntax.code('(1, eval)({0})', script);
|
||||
}
|
||||
runOutput(0);
|
||||
return;
|
||||
}
|
||||
case "integer":
|
||||
if(property0 == "client"){
|
||||
var bytesOut = new haxe.io.BytesOutput();
|
||||
bytesOut.writeInt32(message);
|
||||
var buffer = new Buffer();
|
||||
buffer.writeBytes(bytesOut.getBytes());
|
||||
if(api != "" || api != null){
|
||||
buffer.writeBytes(Bytes.ofString(api));
|
||||
}
|
||||
try {
|
||||
connection.buffer = buffer;
|
||||
if(inputs[5].get() == true){
|
||||
var script = 'for (p in lnx_' + connection._url +'.torrent._peers){
|
||||
try{
|
||||
lnx_' + connection._url +'.torrent._peers[p].conn.send(leenkx.network.Leenkx.connections.h["'+ connection._url +'"].buffer );
|
||||
}catch(error){
|
||||
console.log("Error: " + error);
|
||||
}
|
||||
}';
|
||||
js.Syntax.code('(1, eval)({0})', script.toString());
|
||||
runOutput(0);
|
||||
} else {
|
||||
var script = 'lnx_' + connection._url +'.send("' + inputs[4].get() + '",leenkx.network.Leenkx.connections.h["'+ connection._url +'"].buffer );';
|
||||
js.Syntax.code('(1, eval)({0})', script.toString());
|
||||
runOutput(0);
|
||||
}
|
||||
} catch(error) {
|
||||
trace("Error: " + error);
|
||||
}
|
||||
} else {
|
||||
if(inputs[5].get() == true){
|
||||
var script = 'lnx_' + connection._url + '.send(`' + api + message + '`);';
|
||||
js.Syntax.code('(1, eval)({0})', script);
|
||||
}else{
|
||||
var script = 'lnx_' + connection._url + '.send(`' + inputs[4].get() + '`, `' + api + message + '`);';
|
||||
js.Syntax.code('(1, eval)({0})', script);
|
||||
}
|
||||
runOutput(0);
|
||||
return;
|
||||
}
|
||||
case "boolean":
|
||||
if(property0 == "client"){
|
||||
var buffer = new Buffer();
|
||||
if(message == true){
|
||||
if (message == true) {
|
||||
buffer.writeBytes(Bytes.ofString("true"));
|
||||
} else {
|
||||
buffer.writeBytes(Bytes.ofString("false"));
|
||||
}
|
||||
if(api != "" || api != null){
|
||||
buffer.writeBytes(Bytes.ofString(api));
|
||||
}
|
||||
try {
|
||||
connection.buffer = buffer;
|
||||
if(inputs[5].get() == true){
|
||||
var script = 'for (p in lnx_' + connection._url +'.torrent._peers){
|
||||
try{
|
||||
lnx_' + connection._url +'.torrent._peers[p].conn.send(leenkx.network.Leenkx.connections.h["'+ connection._url +'"].buffer );
|
||||
}catch(error){
|
||||
console.log("Error: " + error);
|
||||
}
|
||||
}';
|
||||
js.Syntax.code('(1, eval)({0})', script.toString());
|
||||
runOutput(0);
|
||||
} else {
|
||||
var script = 'lnx_' + connection._url +'.send("' + inputs[4].get() + '",leenkx.network.Leenkx.connections.h["'+ connection._url +'"].buffer );';
|
||||
js.Syntax.code('(1, eval)({0})', script.toString());
|
||||
runOutput(0);
|
||||
}
|
||||
} catch(error) {
|
||||
trace("Error: " + error);
|
||||
}
|
||||
} else {
|
||||
if(inputs[5].get() == true){
|
||||
var script = 'lnx_' + connection._url + '.send(`' + api + message + '`);';
|
||||
js.Syntax.code('(1, eval)({0})', script);
|
||||
}else{
|
||||
var script = 'lnx_' + connection._url + '.send(`' + inputs[4].get() + '`, `' + api + message + '`);';
|
||||
js.Syntax.code('(1, eval)({0})', script);
|
||||
}
|
||||
runOutput(0);
|
||||
return;
|
||||
}
|
||||
case "transform":
|
||||
if(property0 == "client"){
|
||||
var bytesOut = new haxe.io.BytesOutput();
|
||||
var transform:Mat4 = message;
|
||||
var loc = new Vec4();
|
||||
var rot = new Quat();
|
||||
var scale = new Vec4();
|
||||
transform.decompose(loc, rot, scale);
|
||||
var l = Reflect.fields(loc);
|
||||
for (b in l) {
|
||||
bytesOut.writeFloat(Reflect.field(loc, b));
|
||||
}
|
||||
var r = Reflect.fields(rot);
|
||||
for (b in r) {
|
||||
bytesOut.writeFloat(Reflect.field(rot, b));
|
||||
}
|
||||
var s = Reflect.fields(scale);
|
||||
for (b in s) {
|
||||
bytesOut.writeFloat(Reflect.field(scale, b));
|
||||
}
|
||||
var buffer = new Buffer();
|
||||
bytesOut.writeFloat(loc.x);
|
||||
bytesOut.writeFloat(loc.y);
|
||||
bytesOut.writeFloat(loc.z);
|
||||
bytesOut.writeFloat(loc.w);
|
||||
bytesOut.writeFloat(rot.x);
|
||||
bytesOut.writeFloat(rot.y);
|
||||
bytesOut.writeFloat(rot.z);
|
||||
bytesOut.writeFloat(rot.w);
|
||||
bytesOut.writeFloat(scale.x);
|
||||
bytesOut.writeFloat(scale.y);
|
||||
bytesOut.writeFloat(scale.z);
|
||||
bytesOut.writeFloat(scale.w);
|
||||
buffer.writeBytes(bytesOut.getBytes());
|
||||
if(api != "" || api != null){
|
||||
buffer.writeBytes(Bytes.ofString(api));
|
||||
}
|
||||
try {
|
||||
|
||||
connection.buffer = buffer;
|
||||
|
||||
if(inputs[5].get() == true){
|
||||
var script = 'for (p in lnx_' + connection._url +'.torrent._peers){
|
||||
try{
|
||||
//console.log("Mine: " + lx_8001.torrent.discovery.peerId + " || Incomming: " + p);
|
||||
lnx_' + connection._url +'.torrent._peers[p].conn.send(leenkx.network.Leenkx.connections.h["'+ connection._url +'"].buffer );
|
||||
}catch(error){
|
||||
console.log("Error: " + error);
|
||||
}
|
||||
}';
|
||||
js.Syntax.code('(1, eval)({0})', script.toString());
|
||||
runOutput(0);
|
||||
} else {
|
||||
//var script = 'lnx_' + connection._url +'.send("' + inputs[4].get() + '",leenkx.network.Leenkx.connections.h["'+ connection._url +'"].buffer );';
|
||||
var script = 'lnx_' + connection._url +'.send(leenkx.network.Leenkx.connections.h["'+ connection._url +'"].buffer );';
|
||||
js.Syntax.code('(1, eval)({0})', script.toString());
|
||||
runOutput(0);
|
||||
}
|
||||
|
||||
} catch(error) {
|
||||
trace("Error: " + error);
|
||||
}
|
||||
}
|
||||
case "rotation":
|
||||
if(property0 == "client"){
|
||||
var bytesOut = new haxe.io.BytesOutput();
|
||||
var i = Reflect.fields(message);
|
||||
for (b in i) {
|
||||
bytesOut.writeFloat(Reflect.field(message, b));
|
||||
}
|
||||
var buffer = new Buffer();
|
||||
var f:Null<Float>;
|
||||
if ((f = message.x) != null) bytesOut.writeFloat(f);
|
||||
if ((f = message.y) != null) bytesOut.writeFloat(f);
|
||||
if ((f = message.z) != null) bytesOut.writeFloat(f);
|
||||
if ((f = message.w) != null) bytesOut.writeFloat(f);
|
||||
buffer.writeBytes(bytesOut.getBytes());
|
||||
if(api != "" || api != null){
|
||||
default: trace("Failed to send data.");
|
||||
}
|
||||
|
||||
if (api != null && api != "") {
|
||||
buffer.writeBytes(Bytes.ofString(api));
|
||||
}
|
||||
|
||||
var rawBytes = buffer.readAllAvailableBytes();
|
||||
|
||||
try {
|
||||
connection.buffer = buffer;
|
||||
if(inputs[5].get() == true){
|
||||
var script = 'for (p in lnx_' + connection._url +'.torrent._peers){
|
||||
try{
|
||||
lnx_' + connection._url +'.torrent._peers[p].conn.send(leenkx.network.Leenkx.connections.h["'+ connection._url +'"].buffer );
|
||||
}catch(error){
|
||||
console.log("Error: " + error);
|
||||
}
|
||||
}';
|
||||
js.Syntax.code('(1, eval)({0})', script.toString());
|
||||
runOutput(0);
|
||||
if (property2) {
|
||||
var payload = haxe.crypto.Base64.encode(rawBytes);
|
||||
if (broadcast) {
|
||||
lnxCn.send(null, payload);
|
||||
} else {
|
||||
var script = 'lnx_' + connection._url +'.send("' + inputs[4].get() + '",leenkx.network.Leenkx.connections.h["'+ connection._url +'"].buffer );';
|
||||
js.Syntax.code('(1, eval)({0})', script.toString());
|
||||
runOutput(0);
|
||||
lnxCn.send(targetAddress, payload);
|
||||
}
|
||||
} else {
|
||||
lnxCn.sendRawMessage(rawBytes, broadcast ? null : targetAddress);
|
||||
}
|
||||
} catch(error) {
|
||||
trace("Error: " + error);
|
||||
}
|
||||
} else {
|
||||
if(inputs[5].get() == true){
|
||||
var script = 'lnx_' + connection._url + '.send(`' + api + message + '`);';
|
||||
js.Syntax.code('(1, eval)({0})', script);
|
||||
}else{
|
||||
var script = 'lnx_' + connection._url + '.send(`' + inputs[4].get() + '`, `' + api + message + '`);';
|
||||
js.Syntax.code('(1, eval)({0})', script);
|
||||
}
|
||||
runOutput(0);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
#else
|
||||
runOutput(0);
|
||||
|
||||
#end
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/*******_________________________________________
|
||||
|
||||
try {
|
||||
|
||||
if(inputs[5].get() == true){
|
||||
var script = 'for (p in lnx_' + connection._url +'.torrent._peers){
|
||||
try{
|
||||
lnx_' + connection._url +'.torrent._peers[p].conn.send("' + api + message + '");
|
||||
}catch(error){
|
||||
console.log("Error: " + error);
|
||||
}
|
||||
}';
|
||||
js.Syntax.code('(1, eval)({0})', script.toString());
|
||||
runOutput(0);
|
||||
} else {
|
||||
var script = 'for (p in lnx_' + connection._url +'.torrent._peers){
|
||||
try{
|
||||
|
||||
lx_' + connection._url +'.torrent._peers[' + inputs[4].get() + '].conn.send("' + api + message + '");
|
||||
}catch(error){
|
||||
console.log("Error: " + error);
|
||||
}
|
||||
}';
|
||||
js.Syntax.code('(1, eval)({0})', script.toString());
|
||||
runOutput(0);
|
||||
}
|
||||
} catch(error) {
|
||||
trace("Error: " + error);
|
||||
}
|
||||
return;
|
||||
|
||||
_________________________________________*****/
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@ -23,6 +23,7 @@ class NetworkMessageParserNode extends LogicNode {
|
||||
|
||||
var buffer = cast(data, Buffer);
|
||||
api = inputs[1].get();
|
||||
if (api == null) api = "";
|
||||
if(buffer.endsWith(api) == false){
|
||||
return;
|
||||
}
|
||||
|
||||
65
leenkx/Sources/leenkx/logicnode/OnTorrentEventNode.hx
Normal file
65
leenkx/Sources/leenkx/logicnode/OnTorrentEventNode.hx
Normal file
@ -0,0 +1,65 @@
|
||||
package leenkx.logicnode;
|
||||
#if lnx_torrent
|
||||
import leenkx.network.Leenkx;
|
||||
import leenkx.network.torrent.Torrent;
|
||||
#end
|
||||
|
||||
class OnTorrentEventNode extends LogicNode {
|
||||
public var property0:String;
|
||||
public var eventValue:Dynamic;
|
||||
var net_Url:String;
|
||||
var torrent:Dynamic;
|
||||
var registeredTorrents:Map<String, Bool> = new Map();
|
||||
|
||||
public function new(tree:LogicTree) {
|
||||
super(tree);
|
||||
}
|
||||
|
||||
override function run(from:Int) {
|
||||
#if lnx_torrent
|
||||
var connection = inputs[1].get();
|
||||
if (connection == null) return;
|
||||
var id = inputs[2].get();
|
||||
if (id == null) return;
|
||||
net_Url = connection._url;
|
||||
|
||||
var socket = Leenkx.connections[connection._url];
|
||||
var client = socket != null ? socket.client : null;
|
||||
var t = client != null ? client.get(id) : (Std.isOfType(id, Torrent) ? id : null);
|
||||
if (t == null) return;
|
||||
|
||||
var event = property0 != null ? property0 : "done";
|
||||
var self = this;
|
||||
var fire = function(v:Dynamic) {
|
||||
self.eventValue = v;
|
||||
self.runOutput(0);
|
||||
};
|
||||
var key = t.infoHashHex != null ? t.infoHashHex : "v2:" + t.infoHashV2Hex;
|
||||
var alreadyRegistered = registeredTorrents.exists(key);
|
||||
if (!alreadyRegistered) registeredTorrents.set(key, true);
|
||||
switch (event) {
|
||||
case "ready":
|
||||
if (t.ready) fire(null);
|
||||
else if (!alreadyRegistered) t.on("ready", function() fire(null));
|
||||
case "piece" | "wire" | "metadata" | "error":
|
||||
if (!alreadyRegistered) t.on(event, function(v) fire(v));
|
||||
default:
|
||||
if (t.done) fire(null);
|
||||
else if (!alreadyRegistered) t.on("done", function() fire(null));
|
||||
}
|
||||
torrent = t;
|
||||
#end
|
||||
}
|
||||
|
||||
override function get(from:Int):Dynamic {
|
||||
#if lnx_torrent
|
||||
return switch (from) {
|
||||
case 1: torrent;
|
||||
case 2: eventValue;
|
||||
default: throw "Unreachable";
|
||||
}
|
||||
#else
|
||||
return null;
|
||||
#end
|
||||
}
|
||||
}
|
||||
73
leenkx/Sources/leenkx/logicnode/ParseTorrentNode.hx
Normal file
73
leenkx/Sources/leenkx/logicnode/ParseTorrentNode.hx
Normal file
@ -0,0 +1,73 @@
|
||||
package leenkx.logicnode;
|
||||
#if lnx_torrent
|
||||
import haxe.io.Bytes;
|
||||
import leenkx.network.torrent.Magnet;
|
||||
import leenkx.network.torrent.MetaInfo;
|
||||
#end
|
||||
|
||||
class ParseTorrentNode extends LogicNode {
|
||||
var meta:Dynamic;
|
||||
var magnet:Dynamic;
|
||||
var idHex:String;
|
||||
public var property0:String;
|
||||
|
||||
public function new(tree:LogicTree) {
|
||||
super(tree);
|
||||
}
|
||||
|
||||
override function run(from:Int) {
|
||||
#if lnx_torrent
|
||||
meta = null;
|
||||
magnet = null;
|
||||
idHex = null;
|
||||
var src = inputs[1].get();
|
||||
if (src == null) return;
|
||||
|
||||
switch (property0 != null ? property0 : "magnet") {
|
||||
case "file":
|
||||
if (Std.isOfType(src, MetaInfo)) {
|
||||
meta = src;
|
||||
} else {
|
||||
var b = Std.isOfType(src, Bytes) ? src : Bytes.ofString(Std.string(src));
|
||||
try {
|
||||
meta = MetaInfo.fromTorrentBytes(b);
|
||||
} catch(e:Dynamic) {
|
||||
trace(e);
|
||||
return;
|
||||
}
|
||||
}
|
||||
case "id":
|
||||
var s = Std.string(src);
|
||||
idHex = s.length == 64 ? "v2:" + s : s;
|
||||
default:
|
||||
var m = Magnet.parse(Std.string(src));
|
||||
if (m == null) {
|
||||
return;
|
||||
}
|
||||
magnet = m;
|
||||
idHex = m.infoHashHex != null ? m.infoHashHex : m.infoHashV2Hex;
|
||||
}
|
||||
#end
|
||||
runOutput(0);
|
||||
}
|
||||
|
||||
override function get(from:Int):Dynamic {
|
||||
#if lnx_torrent
|
||||
var m:MetaInfo = meta;
|
||||
var mg:Magnet = magnet;
|
||||
return switch (from) {
|
||||
case 1: m;
|
||||
case 2: m != null ? (m.infoHashHex != null ? m.infoHashHex : m.infoHashV2Hex) : idHex;
|
||||
case 3: m != null ? m.name : (mg != null ? mg.name : null);
|
||||
case 4: m != null ? m.announce : (mg != null ? mg.trackers : null);
|
||||
case 5: m != null ? m.urlList : (mg != null ? mg.webSeeds : null);
|
||||
case 6: m != null ? m.files : null;
|
||||
case 7: m != null ? m.totalLength : 0;
|
||||
case 8: m != null ? m.toTorrentBytes() : null;
|
||||
default: throw "Unreachable";
|
||||
}
|
||||
#else
|
||||
return null;
|
||||
#end
|
||||
}
|
||||
}
|
||||
@ -1,52 +1,124 @@
|
||||
package leenkx.logicnode;
|
||||
import leenkx.system.Event;
|
||||
import iron.object.Object;
|
||||
|
||||
#if lnx_torrent
|
||||
import haxe.io.Bytes;
|
||||
import leenkx.network.Leenkx;
|
||||
import leenkx.network.torrent.MetaInfo;
|
||||
import leenkx.network.torrent.Torrent;
|
||||
#end
|
||||
|
||||
|
||||
class SeedTorrentNode extends LogicNode {
|
||||
public var torrent: Dynamic;
|
||||
public var net_Url: Dynamic;
|
||||
|
||||
public var torrent:Dynamic;
|
||||
public var net_Url:String;
|
||||
|
||||
public function new(tree:LogicTree) {
|
||||
super(tree);
|
||||
}
|
||||
|
||||
|
||||
override function run(from:Int) {
|
||||
#if js
|
||||
#if lnx_torrent
|
||||
var connection = inputs[1].get();
|
||||
if (connection == null) return;
|
||||
trace("got connection");
|
||||
var data = inputs[3].get();
|
||||
if (data == null) return;
|
||||
trace("got data");
|
||||
var opts = inputs[2].get();
|
||||
var src = inputs[2].get();
|
||||
net_Url = connection._url;
|
||||
|
||||
var cx = "lx_" + net_Url;
|
||||
var lx:Dynamic = untyped js.Browser.window[cx];
|
||||
var args = opts.toString();
|
||||
trace(args);
|
||||
var func = Reflect.field(lx.wt, "seed");
|
||||
if (func != null) {
|
||||
torrent = func(data, function (torrent) {
|
||||
trace('Client is seeding ' + torrent.magnetURI);
|
||||
});
|
||||
//trace(torrent);
|
||||
var socket = Leenkx.connections[connection._url];
|
||||
if (socket == null || socket.client == null) return;
|
||||
var client = socket.client;
|
||||
var self = this;
|
||||
|
||||
var onSeeded = function(t:Torrent) {
|
||||
if (t == null) return;
|
||||
self.torrent = t;
|
||||
var onDone = function() {
|
||||
self.runOutput(2);
|
||||
};
|
||||
if (t.done) onDone();
|
||||
else t.on("done", onDone);
|
||||
};
|
||||
|
||||
if (Std.isOfType(src, Torrent)) {
|
||||
var t:Torrent = src;
|
||||
t.start();
|
||||
torrent = t;
|
||||
runOutput(0);
|
||||
return;
|
||||
}
|
||||
|
||||
var meta:MetaInfo = null;
|
||||
if (Std.isOfType(src, MetaInfo)) {
|
||||
meta = cast src;
|
||||
} else if (Std.isOfType(src, Bytes)) {
|
||||
try {
|
||||
meta = MetaInfo.fromTorrentBytes(cast src);
|
||||
} catch(e:Dynamic) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (meta == null) {
|
||||
return;
|
||||
}
|
||||
var files = filesFor(meta, inputs.length > 3 ? inputs[3].get() : null);
|
||||
if (files == null) {
|
||||
return;
|
||||
}
|
||||
client.seedMeta(meta, files, null, onSeeded);
|
||||
runOutput(0);
|
||||
#end
|
||||
}
|
||||
|
||||
override function get(from: Int): Dynamic {
|
||||
#if lnx_torrent
|
||||
function filesFor(meta:MetaInfo, data:Dynamic):Array<{path:String, data:Bytes}> {
|
||||
if (data == null) return null;
|
||||
var entries = meta.files;
|
||||
if (Std.isOfType(data, Bytes)) {
|
||||
var blob:Bytes = data;
|
||||
if (blob.length != meta.totalLength) return null;
|
||||
var files:Array<{path:String, data:Bytes}> = [];
|
||||
var cursor = 0;
|
||||
for (f in entries) {
|
||||
files.push({path: f.path, data: blob.sub(cursor, f.length)});
|
||||
cursor += f.length;
|
||||
}
|
||||
return files;
|
||||
}
|
||||
if (Std.isOfType(data, Array)) {
|
||||
var items:Array<Dynamic> = data;
|
||||
var files:Array<{path:String, data:Bytes}> = [];
|
||||
for (it in items) {
|
||||
var p = Reflect.field(it, "path");
|
||||
var d:Bytes = Reflect.field(it, "data");
|
||||
if (d == null) d = Reflect.field(it, "content");
|
||||
if (p == null || d == null) continue;
|
||||
var sp = Std.string(p);
|
||||
var matched = false;
|
||||
for (f in entries) {
|
||||
if (f.path == sp || StringTools.endsWith(f.path, "/" + sp)) {
|
||||
files.push({path: f.path, data: d});
|
||||
matched = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!matched) files.push({path: sp, data: d});
|
||||
}
|
||||
if (files.length == entries.length) return files;
|
||||
return null;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
#end
|
||||
|
||||
override function get(from:Int):Dynamic {
|
||||
#if lnx_torrent
|
||||
var t:Torrent = torrent;
|
||||
return switch (from) {
|
||||
case 1: torrent;
|
||||
case 3: t != null ? t.infoHashHex : null;
|
||||
case 4: t != null ? t.magnetURI : null;
|
||||
default: throw "Unreachable";
|
||||
}
|
||||
#else
|
||||
return null;
|
||||
#end
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
90
leenkx/Sources/leenkx/logicnode/SetTrackersNode.hx
Normal file
90
leenkx/Sources/leenkx/logicnode/SetTrackersNode.hx
Normal file
@ -0,0 +1,90 @@
|
||||
package leenkx.logicnode;
|
||||
#if lnx_torrent
|
||||
import leenkx.network.Leenkx;
|
||||
import leenkx.network.torrent.Torrent;
|
||||
import leenkx.network.torrent.TorrentClient;
|
||||
#end
|
||||
|
||||
class SetTrackersNode extends LogicNode {
|
||||
public var property0:String;
|
||||
public var target:Dynamic;
|
||||
|
||||
public function new(tree:LogicTree) {
|
||||
super(tree);
|
||||
}
|
||||
|
||||
override function run(from:Int) {
|
||||
#if lnx_torrent
|
||||
var p = property0 != null ? property0 : "add";
|
||||
switch (p) {
|
||||
case "add" | "override":
|
||||
setTorrentTrackers(p == "override");
|
||||
default:
|
||||
setTrackerProperty(p);
|
||||
}
|
||||
#end
|
||||
runOutput(0);
|
||||
}
|
||||
|
||||
#if lnx_torrent
|
||||
function setTorrentTrackers(trackerOverride:Bool):Void {
|
||||
var id = inputs[2].get();
|
||||
var urls = TorrentClient.trackerUrls(inputs[3].get());
|
||||
if (urls == null) return;
|
||||
var t:Torrent = Std.isOfType(id, Torrent) ? id : null;
|
||||
if (t == null && id != null) {
|
||||
var connection = inputs[1].get();
|
||||
var socket = connection != null ? Leenkx.connections[connection._url] : null;
|
||||
var client = socket != null ? socket.client : null;
|
||||
if (client != null) t = client.get(id);
|
||||
}
|
||||
if (t != null) {
|
||||
target = t;
|
||||
if (trackerOverride) {
|
||||
t.setTrackers(urls);
|
||||
} else {
|
||||
for (u in urls) t.addTracker(u);
|
||||
}
|
||||
} else if (id == null) {
|
||||
target = null;
|
||||
if (trackerOverride) {
|
||||
TorrentClient.defaultTrackers = urls;
|
||||
} else {
|
||||
TorrentClient.mergeTrackers(TorrentClient.defaultTrackers, urls);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function setTrackerProperty(prop:String):Void {
|
||||
var tracker:Dynamic = inputs[1].get();
|
||||
if (tracker == null) return;
|
||||
var v = inputs[2].get();
|
||||
if (v == null) return;
|
||||
var field = prop == "interval" ? "intervalSec" : prop;
|
||||
var val:Dynamic = field == "trackerId" ? Std.string(v)
|
||||
: Std.int(Std.parseFloat(Std.string(v)));
|
||||
var setter:Dynamic = field == "intervalSec"
|
||||
&& Reflect.field(tracker, "intervalSec") == null
|
||||
? Reflect.field(tracker, "setAnnounceInterval") : null;
|
||||
if (setter != null) {
|
||||
try {
|
||||
Reflect.callMethod(tracker, setter, [(val:Int) * 1000]);
|
||||
} catch(e:Dynamic) {}
|
||||
} else {
|
||||
Reflect.setField(tracker, field, val);
|
||||
}
|
||||
target = tracker;
|
||||
}
|
||||
#end
|
||||
|
||||
override function get(from:Int):Dynamic {
|
||||
#if lnx_torrent
|
||||
return switch (from) {
|
||||
case 1: target;
|
||||
default: throw "Unreachable";
|
||||
}
|
||||
#else
|
||||
return null;
|
||||
#end
|
||||
}
|
||||
}
|
||||
74
leenkx/Sources/leenkx/logicnode/TorrentClientInfoNode.hx
Normal file
74
leenkx/Sources/leenkx/logicnode/TorrentClientInfoNode.hx
Normal file
@ -0,0 +1,74 @@
|
||||
package leenkx.logicnode;
|
||||
#if lnx_torrent
|
||||
import leenkx.network.Leenkx;
|
||||
import leenkx.network.torrent.Torrent;
|
||||
#end
|
||||
|
||||
class TorrentClientInfoNode extends LogicNode {
|
||||
var stats:Dynamic;
|
||||
var list:Array<Dynamic>;
|
||||
|
||||
public function new(tree:LogicTree) {
|
||||
super(tree);
|
||||
}
|
||||
|
||||
override function run(from:Int) {
|
||||
#if lnx_torrent
|
||||
list = [];
|
||||
stats = {downRate: 0.0, upRate: 0.0, progress: 0.0, ratio: 0.0};
|
||||
var connection = inputs[1].get();
|
||||
if (connection != null) {
|
||||
var socket = Leenkx.connections[connection._url];
|
||||
var client = socket != null ? socket.client : null;
|
||||
if (client != null) collect(client);
|
||||
}
|
||||
#end
|
||||
runOutput(0);
|
||||
}
|
||||
|
||||
#if lnx_torrent
|
||||
function collect(client:Dynamic) {
|
||||
var down = 0.0;
|
||||
var up = 0.0;
|
||||
var received = 0.0;
|
||||
var sent = 0.0;
|
||||
var total = 0.0;
|
||||
var left = 0.0;
|
||||
var torrents:Map<String, Torrent> = client.torrents;
|
||||
for (t in torrents) {
|
||||
list.push(t);
|
||||
var s = t.getStats();
|
||||
down += s.downRate;
|
||||
up += s.upRate;
|
||||
received += s.downloaded;
|
||||
sent += s.uploaded;
|
||||
if (t.meta != null) {
|
||||
total += t.meta.totalLength;
|
||||
left += s.left;
|
||||
}
|
||||
}
|
||||
stats = {
|
||||
downRate: down,
|
||||
upRate: up,
|
||||
progress: total > 0 ? 1.0 - left / total : 0.0,
|
||||
ratio: received > 0 ? sent / received : 0.0
|
||||
};
|
||||
}
|
||||
#end
|
||||
|
||||
override function get(from:Int):Dynamic {
|
||||
#if lnx_torrent
|
||||
return switch (from) {
|
||||
case 1: list;
|
||||
case 2: list.length;
|
||||
case 3: stats.downRate;
|
||||
case 4: stats.upRate;
|
||||
case 5: stats.progress;
|
||||
case 6: stats.ratio;
|
||||
default: throw "Unreachable";
|
||||
}
|
||||
#else
|
||||
return null;
|
||||
#end
|
||||
}
|
||||
}
|
||||
93
leenkx/Sources/leenkx/logicnode/TorrentControlNode.hx
Normal file
93
leenkx/Sources/leenkx/logicnode/TorrentControlNode.hx
Normal file
@ -0,0 +1,93 @@
|
||||
package leenkx.logicnode;
|
||||
#if lnx_torrent
|
||||
import leenkx.network.Leenkx;
|
||||
import leenkx.network.torrent.Torrent;
|
||||
#end
|
||||
|
||||
class TorrentControlNode extends LogicNode {
|
||||
public var property0:String;
|
||||
var net_Url:String;
|
||||
|
||||
public function new(tree:LogicTree) {
|
||||
super(tree);
|
||||
}
|
||||
|
||||
override function run(from:Int) {
|
||||
#if lnx_torrent
|
||||
var connection = inputs[1].get();
|
||||
if (connection == null) return;
|
||||
var id = inputs[2].get();
|
||||
if (id == null) return;
|
||||
net_Url = connection._url;
|
||||
|
||||
var socket = Leenkx.connections[connection._url];
|
||||
if (socket == null) return;
|
||||
var client = socket.client;
|
||||
var t = client != null ? client.get(id) : (Std.isOfType(id, Torrent) ? id : null);
|
||||
var action = property0 != null ? property0 : "remove";
|
||||
|
||||
var file:Dynamic = inputs.length > 3 ? inputs[3].get() : null;
|
||||
var t2 = t;
|
||||
var startB = 0;
|
||||
var endB = -1;
|
||||
if (file != null) {
|
||||
startB = file.offset;
|
||||
endB = file.offset + file.length - 1;
|
||||
if (t2 == null) t2 = file.torrent;
|
||||
} else {
|
||||
var sv = inputs.length > 4 ? inputs[4].get() : null;
|
||||
var ev = inputs.length > 5 ? inputs[5].get() : null;
|
||||
if (sv != null && Std.string(sv) != "") {
|
||||
startB = Std.int(Std.parseFloat(Std.string(sv)));
|
||||
}
|
||||
if (ev != null && Std.string(ev) != "") {
|
||||
endB = Std.int(Std.parseFloat(Std.string(ev)));
|
||||
}
|
||||
}
|
||||
var pv = inputs.length > 6 ? inputs[6].get() : null;
|
||||
var priority = pv != null && Std.string(pv) != "" ? Std.int(Std.parseFloat(Std.string(pv))) : null;
|
||||
|
||||
switch (action) {
|
||||
case "start":
|
||||
if (t != null) t.start();
|
||||
case "stop":
|
||||
if (t != null) t.stop();
|
||||
case "select", "critical", "deselect":
|
||||
if (t2 != null) {
|
||||
if (endB < 0) {
|
||||
endB = t2.meta != null ? t2.meta.totalLength - 1 : 0;
|
||||
}
|
||||
if (action == "critical") {
|
||||
t2.critical(startB, endB);
|
||||
} else if (action == "select") {
|
||||
t2.select(startB, endB, priority);
|
||||
} else {
|
||||
t2.deselect(startB, endB, priority);
|
||||
}
|
||||
}
|
||||
case "add-peer":
|
||||
var peer = inputs.length > 3 ? inputs[3].get() : null;
|
||||
if (t != null && peer != null) {
|
||||
t.addPeer(Std.string(peer));
|
||||
}
|
||||
case "add-web-seed":
|
||||
var url = inputs.length > 3 ? inputs[3].get() : null;
|
||||
if (t != null && url != null) {
|
||||
t.addWebSeed(Std.string(url));
|
||||
}
|
||||
case "remove", "remove-data":
|
||||
var hex = t != null ? t.infoHashHex : Std.string(id);
|
||||
if (hex == null) hex = Std.string(id);
|
||||
if (client != null) {
|
||||
client.remove(hex, action == "remove-data");
|
||||
}
|
||||
if (t != null && Leenkx.torrent.get(net_Url) == t) {
|
||||
Leenkx.torrent.remove(net_Url);
|
||||
Leenkx.file.remove(net_Url);
|
||||
}
|
||||
default:
|
||||
}
|
||||
#end
|
||||
runOutput(0);
|
||||
}
|
||||
}
|
||||
74
leenkx/Sources/leenkx/logicnode/TorrentInfoNode.hx
Normal file
74
leenkx/Sources/leenkx/logicnode/TorrentInfoNode.hx
Normal file
@ -0,0 +1,74 @@
|
||||
package leenkx.logicnode;
|
||||
#if lnx_torrent
|
||||
import leenkx.network.Leenkx;
|
||||
import leenkx.network.torrent.Torrent;
|
||||
import leenkx.network.torrent.TorrentClient;
|
||||
#end
|
||||
|
||||
class TorrentInfoNode extends LogicNode {
|
||||
public var torrent:Dynamic;
|
||||
public var net_Url:String;
|
||||
|
||||
public function new(tree:LogicTree) {
|
||||
super(tree);
|
||||
}
|
||||
|
||||
override function run(from:Int) {
|
||||
#if lnx_torrent
|
||||
var id = inputs[2].get();
|
||||
if (id == null) return;
|
||||
torrent = null;
|
||||
|
||||
if (Std.isOfType(id, Torrent)) {
|
||||
torrent = id;
|
||||
} else {
|
||||
var connection = inputs[1].get();
|
||||
if (connection == null) return;
|
||||
net_Url = connection._url;
|
||||
var socket = Leenkx.connections[net_Url];
|
||||
if (socket == null || socket.client == null) return;
|
||||
torrent = socket.client.get(id);
|
||||
}
|
||||
#end
|
||||
runOutput(0);
|
||||
}
|
||||
|
||||
override function get(from:Int):Dynamic {
|
||||
#if lnx_torrent
|
||||
var t:Torrent = torrent;
|
||||
return switch (from) {
|
||||
case 1: t != null ? t.infoHashHex : null;
|
||||
case 2: t != null && t.meta != null ? t.meta.name : null;
|
||||
case 3: t != null ? t.numPeers : 0;
|
||||
case 4: progress(t);
|
||||
case 5: t != null ? t.getStats().downloaded : 0.0;
|
||||
case 6: t != null ? t.getStats().uploaded : 0.0;
|
||||
case 7: t != null ? t.done : false;
|
||||
case 8: t != null ? t.magnetURI : null;
|
||||
case 9: t != null ? t.files : null;
|
||||
case 10: t != null && t.meta != null ? t.meta.numPieces : 0;
|
||||
case 11: t != null && t.meta != null ? t.meta.pieceLength : 0;
|
||||
case 12: t != null && t.meta != null ? t.meta.totalLength : 0;
|
||||
case 13: t != null && t.meta != null ? t.meta.comment : null;
|
||||
case 14: t != null && t.meta != null ? t.meta.createdBy : null;
|
||||
case 15: t != null && t.meta != null ? t.meta.protocolVersion : 1;
|
||||
case 16: t != null ? t.infoHashV2Hex : null;
|
||||
case 17: t != null ? t.getStats().downRate : 0.0;
|
||||
case 18: t != null ? t.getStats().upRate : 0.0;
|
||||
case 19: t != null ? t.stopped : false;
|
||||
case 20: t;
|
||||
default: throw "Unreachable";
|
||||
}
|
||||
#else
|
||||
return null;
|
||||
#end
|
||||
}
|
||||
|
||||
#if lnx_torrent
|
||||
function progress(t:Torrent):Float {
|
||||
if (t == null || t.meta == null || t.meta.totalLength == 0) return 0;
|
||||
var stats = t.getStats();
|
||||
return 1.0 - stats.left / t.meta.totalLength;
|
||||
}
|
||||
#end
|
||||
}
|
||||
@ -121,7 +121,6 @@ class Leenkx {
|
||||
class LeenkxSocket {
|
||||
public var _url:String;
|
||||
public var client:LeenkxClient;
|
||||
public var torrentClient:leenkx.network.torrent.TorrentClient;
|
||||
public var buffer:Dynamic;
|
||||
public var onopen:Void->Void;
|
||||
public var onclose:Void->Void;
|
||||
|
||||
@ -9,7 +9,8 @@ import leenkx.network.torrent.TorrentInfo;
|
||||
|
||||
class LeenkxClient {
|
||||
|
||||
static var PEERTIMEOUT:Float = 5 * 60 * 1000;
|
||||
public static var PEERTIMEOUT:Float = 5 * 60 * 1000;
|
||||
public static var defaultHeartbeat:Int = 30000;
|
||||
|
||||
public var identifier:String;
|
||||
public var seed:String;
|
||||
@ -53,6 +54,54 @@ class LeenkxClient {
|
||||
private var deferredOpts:TransportOpts = null;
|
||||
private var heartbeatInterval:Int = 30000;
|
||||
|
||||
public var torrents(get, never):Map<String, Torrent>;
|
||||
public var onTorrentAdded:Torrent -> Void;
|
||||
public var onTorrentDone:Torrent -> Void;
|
||||
var swarm:TorrentClient = null;
|
||||
|
||||
function get_torrents():Map<String, Torrent> {
|
||||
return swarm != null ? swarm.torrents : [];
|
||||
}
|
||||
|
||||
function getSwarm():TorrentClient {
|
||||
if (swarm == null) {
|
||||
swarm = new TorrentClient();
|
||||
swarm.onError = function(e) {
|
||||
if (onError != null) onError(e);
|
||||
};
|
||||
swarm.onTorrentAdded = function(t) {
|
||||
if (onTorrentAdded != null) onTorrentAdded(t);
|
||||
};
|
||||
swarm.onTorrentDone = function(t) {
|
||||
if (onTorrentDone != null) onTorrentDone(t);
|
||||
};
|
||||
}
|
||||
return swarm;
|
||||
}
|
||||
|
||||
public function add(torrentId:Dynamic, ?opts:Dynamic, ?cb:Torrent -> Void):Torrent {
|
||||
return getSwarm().add(torrentId, opts, cb);
|
||||
}
|
||||
|
||||
public function seedMeta(meta:MetaInfo,
|
||||
files:Array<{path:String, data:Bytes}>,
|
||||
?opts:Dynamic, ?cb:Torrent -> Void):Torrent {
|
||||
return getSwarm().seedMeta(meta, files, opts, cb);
|
||||
}
|
||||
|
||||
public function get(id:Dynamic):Torrent {
|
||||
if (Std.isOfType(id, Torrent)) return id;
|
||||
return swarm != null ? swarm.get(id) : null;
|
||||
}
|
||||
|
||||
public function remove(infoHashHex:String, ?deleteData:Bool):Void {
|
||||
if (swarm != null) swarm.remove(infoHashHex, deleteData);
|
||||
}
|
||||
|
||||
public function listen(port:Int):Void {
|
||||
getSwarm().listen(port);
|
||||
}
|
||||
|
||||
public function new(?identifier:String, ?opts:LeenkxOptions) {
|
||||
if (identifier != null && !Std.isOfType(identifier, String)) {
|
||||
opts = cast identifier;
|
||||
@ -60,7 +109,7 @@ class LeenkxClient {
|
||||
}
|
||||
opts = opts != null ? opts : {};
|
||||
|
||||
this.announce = opts.announce != null ? opts.announce : ["wss://ws1.leenkx.com"];
|
||||
this.announce = opts.announce != null ? opts.announce : TorrentClient.defaultTrackers;
|
||||
this.timeout = opts.timeout != null ? opts.timeout : PEERTIMEOUT;
|
||||
|
||||
if (opts.seed != null) {
|
||||
@ -90,7 +139,7 @@ class LeenkxClient {
|
||||
|
||||
createTransport(opts, opts.deferJoin == true);
|
||||
|
||||
this.heartbeatInterval = opts.heartbeat != null ? opts.heartbeat : 30000;
|
||||
this.heartbeatInterval = opts.heartbeat != null ? opts.heartbeat : defaultHeartbeat;
|
||||
}
|
||||
|
||||
function createTransport(opts:LeenkxOptions, defer:Bool):Void {
|
||||
@ -281,6 +330,10 @@ class LeenkxClient {
|
||||
}
|
||||
var packet = makePacket({y: PacketType.Leave});
|
||||
sendRaw(packet);
|
||||
if (swarm != null) {
|
||||
swarm.destroy();
|
||||
swarm = null;
|
||||
}
|
||||
if (transport != null) {
|
||||
transport.destroy(cb);
|
||||
} else if (cb != null) {
|
||||
|
||||
@ -39,12 +39,9 @@ class Torrent {
|
||||
public var announcePort:Int = 0;
|
||||
public var downloadDir:String = null;
|
||||
|
||||
public function getStats():{downloaded:Float, uploaded:Float,
|
||||
left:Float, downRate:Float, upRate:Float} {
|
||||
public function getStats():{downloaded:Float, uploaded:Float, left:Float, downRate:Float, upRate:Float} {
|
||||
var total = meta != null ? meta.totalLength : 0;
|
||||
var downloaded = pieces != null
|
||||
? Math.min(pieces.numHave * (meta.pieceLength:Float), total)
|
||||
: 0;
|
||||
var downloaded = pieces != null ? Math.min(pieces.numHave * (meta.pieceLength:Float), total) : 0;
|
||||
var now = haxe.Timer.stamp();
|
||||
var dt = now - lastStatT;
|
||||
var downRate:Float = 0;
|
||||
@ -93,8 +90,7 @@ class Torrent {
|
||||
public function new(infoHash:Bytes, peerId:Bytes, trackers:Array<String>, ?meta:MetaInfo, ?storage:IStorage, ?downloadDir:String) {
|
||||
TorrentClient.initMain();
|
||||
this.infoHash = infoHash;
|
||||
this.infoHashHex = infoHash != null
|
||||
? Crypto.toHex(infoHash) : null;
|
||||
this.infoHashHex = infoHash != null ? Crypto.toHex(infoHash) : null;
|
||||
this.peerId = peerId;
|
||||
this.trackers = trackers;
|
||||
this.storage = storage;
|
||||
@ -121,12 +117,9 @@ class Torrent {
|
||||
function setMeta(m:MetaInfo):Void {
|
||||
meta = m;
|
||||
if (storage == null) {
|
||||
var sz = m.storageLength > 0 ? m.storageLength
|
||||
: m.totalLength;
|
||||
var sz = m.storageLength > 0 ? m.storageLength : m.totalLength;
|
||||
#if (sys || kha_krom)
|
||||
storage = downloadDir != null
|
||||
? new FileTreeStorage(downloadDir, m.files)
|
||||
: new MemoryStorage(sz);
|
||||
storage = downloadDir != null ? new FileTreeStorage(downloadDir, m.files) : new MemoryStorage(sz);
|
||||
#else
|
||||
storage = new MemoryStorage(sz);
|
||||
#end
|
||||
@ -139,8 +132,7 @@ class Torrent {
|
||||
if (m.fileHashes == null && m.files != null) {
|
||||
m.fileHashes = [for (f in m.files) null];
|
||||
}
|
||||
files = [for (f in m.files)
|
||||
new TorrentFile(f, storage, this)];
|
||||
files = [for (f in m.files) new TorrentFile(f, storage, this)];
|
||||
infoHashV2 = m.infoHashV2;
|
||||
infoHashV2Hex = m.infoHashV2Hex;
|
||||
if (infoHash == null && m.infoHash != null) {
|
||||
@ -151,8 +143,7 @@ class Torrent {
|
||||
if (m.urlList == null && pendingWebSeeds != null) {
|
||||
m.urlList = pendingWebSeeds;
|
||||
}
|
||||
magnetURI = Magnet.build(m.infoHash, m.name, trackers,
|
||||
m.urlList, m.infoHashV2);
|
||||
magnetURI = Magnet.build(m.infoHash, m.name, trackers, m.urlList, m.infoHashV2);
|
||||
ready = true;
|
||||
initWebSeeds();
|
||||
emit1("metadata", m);
|
||||
@ -214,6 +205,24 @@ class Torrent {
|
||||
return startTracker(url);
|
||||
}
|
||||
|
||||
public function setTrackers(urls:Array<String>):Void {
|
||||
if (stopped) {
|
||||
trackers = urls != null ? urls.copy() : [];
|
||||
return;
|
||||
}
|
||||
if (rtcPool != null) {
|
||||
rtcPool.close();
|
||||
rtcPool = null;
|
||||
}
|
||||
for (t in httpTrackers) t.stop();
|
||||
for (t in udpTrackers) t.stop();
|
||||
httpTrackers = [];
|
||||
udpTrackers = [];
|
||||
trackerObjects = [];
|
||||
trackers = urls != null ? urls.copy() : [];
|
||||
startTrackers();
|
||||
}
|
||||
|
||||
function startTracker(url:String):Dynamic {
|
||||
var infoHashBin = TorrentInfo.toBinaryString(infoHash);
|
||||
var peerIdBin = TorrentInfo.toBinaryString(peerId);
|
||||
@ -231,9 +240,7 @@ class Torrent {
|
||||
}
|
||||
#if (sys || kha_krom)
|
||||
else if (url.indexOf("http") == 0) {
|
||||
var t = new leenkx.network.torrent.tracker.HttpTracker(
|
||||
url, infoHash, peerId,
|
||||
announcePort > 0 ? announcePort : 6881);
|
||||
var t = new leenkx.network.torrent.tracker.HttpTracker(url, infoHash, peerId, announcePort > 0 ? announcePort : 6881);
|
||||
t.statsProvider = getStats;
|
||||
t.onPeers = onClassicPeers;
|
||||
t.onError = function(e) {
|
||||
@ -246,9 +253,7 @@ class Torrent {
|
||||
#end
|
||||
#if (sys || kha_krom)
|
||||
else if (url.indexOf("udp") == 0) {
|
||||
var t = new leenkx.network.torrent.tracker.UdpTracker(
|
||||
url, infoHash, peerId,
|
||||
announcePort > 0 ? announcePort : 6881);
|
||||
var t = new leenkx.network.torrent.tracker.UdpTracker(url, infoHash, peerId, announcePort > 0 ? announcePort : 6881);
|
||||
t.statsProvider = getStats;
|
||||
t.onPeers = onClassicPeers;
|
||||
t.onError = function(e) {
|
||||
@ -272,8 +277,7 @@ class Torrent {
|
||||
for (p in list) {
|
||||
var key = "tcp:" + p.host + ":" + p.port;
|
||||
if (wires.exists(key)) continue;
|
||||
var ch = new leenkx.network.torrent.peer.TcpPeer(
|
||||
p.host, p.port);
|
||||
var ch = new leenkx.network.torrent.peer.TcpPeer(p.host, p.port);
|
||||
addWire(key, ch);
|
||||
}
|
||||
}
|
||||
@ -308,8 +312,7 @@ class Torrent {
|
||||
requestBlocks(key);
|
||||
};
|
||||
wire.onHave = function(index) {
|
||||
if (meta == null || index < 0
|
||||
|| index >= meta.numPieces) return;
|
||||
if (meta == null || index < 0 || index >= meta.numPieces) return;
|
||||
var bf = peerBitfields.get(key);
|
||||
if (bf != null && index < bf.length) bf[index] = true;
|
||||
requestBlocks(key);
|
||||
@ -317,17 +320,13 @@ class Torrent {
|
||||
wire.onPiece = function(index, begin, data) {
|
||||
var n = outstanding.get(key);
|
||||
if (n != null && n > 0) outstanding.set(key, n - 1);
|
||||
if (pieces != null && index >= 0
|
||||
&& index < meta.numPieces
|
||||
&& pieces.addBlock(index, begin, data)) {
|
||||
if (pieces != null && index >= 0 && index < meta.numPieces && pieces.addBlock(index, begin, data)) {
|
||||
broadcastHave(index);
|
||||
}
|
||||
requestBlocks(key);
|
||||
};
|
||||
wire.onRequest = function(index, begin, length) {
|
||||
if (wire.amChoking || pieces == null || index < 0
|
||||
|| index >= meta.numPieces || begin < 0 || length <= 0
|
||||
|| begin + length > meta.pieceSize(index)) return;
|
||||
if (wire.amChoking || pieces == null || index < 0 || index >= meta.numPieces || begin < 0 || length <= 0 || begin + length > meta.pieceSize(index)) return;
|
||||
if (!pieces.have[index]) return;
|
||||
var data = pieces.readBlock(index, begin, length);
|
||||
uploadedBytes += data.length;
|
||||
@ -384,8 +383,7 @@ class Torrent {
|
||||
var out:Array<Bool> = [];
|
||||
for (i in 0...n) {
|
||||
var byte = i >> 3;
|
||||
out.push(byte < bf.length
|
||||
&& (bf.get(byte) & (0x80 >> (i & 7))) != 0);
|
||||
out.push(byte < bf.length && (bf.get(byte) & (0x80 >> (i & 7))) != 0);
|
||||
}
|
||||
return out;
|
||||
}
|
||||
@ -479,8 +477,7 @@ class Torrent {
|
||||
if (host == "" || port == null) return false;
|
||||
var key = "tcp:" + host + ":" + port;
|
||||
if (wires.exists(key)) return false;
|
||||
addWire(key,
|
||||
new leenkx.network.torrent.peer.TcpPeer(host, port));
|
||||
addWire(key, new leenkx.network.torrent.peer.TcpPeer(host, port));
|
||||
return true;
|
||||
#else
|
||||
return false;
|
||||
@ -509,8 +506,7 @@ class Torrent {
|
||||
var pendingLayers:Map<Int, {arr:Array<Bytes>, got:Int}> = [];
|
||||
|
||||
function pieceLayerBase():Int {
|
||||
return Std.int(Math.log(meta.pieceLength / 16384)
|
||||
/ Math.log(2));
|
||||
return Std.int(Math.log(meta.pieceLength / 16384) / Math.log(2));
|
||||
}
|
||||
|
||||
function requestPieceLayer(wire:WireProtocol, fi:Int):Void {
|
||||
@ -528,8 +524,7 @@ class Torrent {
|
||||
if (chunk > 512) chunk = 512;
|
||||
var len = 2;
|
||||
while (len < chunk) len <<= 1;
|
||||
wire.sendHashRequest(root, pieceLayerBase(), sent,
|
||||
len, 0);
|
||||
wire.sendHashRequest(root, pieceLayerBase(), sent, len, 0);
|
||||
hashReqNext.set(fi, sent + len);
|
||||
hashReqTime.set(fi, haxe.Timer.stamp());
|
||||
}
|
||||
@ -547,11 +542,9 @@ class Torrent {
|
||||
if (count <= 0) return;
|
||||
for (fi in 0...meta.files.length) {
|
||||
var f = meta.files[fi];
|
||||
if (f.piecesRoot == null
|
||||
|| !bytesEq(root, f.piecesRoot)) continue;
|
||||
if (f.piecesRoot == null || !bytesEq(root, f.piecesRoot)) continue;
|
||||
if (meta.fileHashes[fi] != null) return;
|
||||
var np = Std.int(Math.ceil(f.length
|
||||
/ meta.pieceLength));
|
||||
var np = Std.int(Math.ceil(f.length / meta.pieceLength));
|
||||
var pl = pendingLayers.get(fi);
|
||||
if (pl == null) {
|
||||
pl = {arr: [for (i in 0...np) null], got: 0};
|
||||
@ -585,8 +578,7 @@ class Torrent {
|
||||
var root = payload.sub(0, 32);
|
||||
for (fi in 0...meta.files.length) {
|
||||
var f = meta.files[fi];
|
||||
if (f.piecesRoot != null
|
||||
&& bytesEq(root, f.piecesRoot)) {
|
||||
if (f.piecesRoot != null && bytesEq(root, f.piecesRoot)) {
|
||||
hashReqNext.remove(fi);
|
||||
pendingLayers.remove(fi);
|
||||
return;
|
||||
@ -604,10 +596,8 @@ class Torrent {
|
||||
var np = Std.int(Math.ceil(f.length / meta.pieceLength));
|
||||
l = [];
|
||||
for (p in 0...np) {
|
||||
var size = Std.int(Math.min(meta.pieceLength,
|
||||
f.length - p * meta.pieceLength));
|
||||
l.push(meta.pieceRootV2(storage.read(
|
||||
f.offset + p * meta.pieceLength, size)));
|
||||
var size = Std.int(Math.min(meta.pieceLength, f.length - p * meta.pieceLength));
|
||||
l.push(meta.pieceRootV2(storage.read(f.offset + p * meta.pieceLength, size)));
|
||||
}
|
||||
layerCache.set(fi, l);
|
||||
return l;
|
||||
@ -620,13 +610,11 @@ class Torrent {
|
||||
t = [level];
|
||||
var depth = 0;
|
||||
while (level.length > 1) {
|
||||
var zero = MetaInfo.layerZero(pieceLayerBase()
|
||||
+ depth + 1);
|
||||
var zero = MetaInfo.layerZero(pieceLayerBase() + depth + 1);
|
||||
var next:Array<Bytes> = [];
|
||||
var i = 0;
|
||||
while (i < level.length) {
|
||||
var b = i + 1 < level.length
|
||||
? level[i + 1] : zero;
|
||||
var b = i + 1 < level.length ? level[i + 1] : zero;
|
||||
next.push(MetaInfo.hashPair(level[i], b));
|
||||
i += 2;
|
||||
}
|
||||
|
||||
@ -5,9 +5,7 @@ import leenkx.network.torrent.storage.Storage;
|
||||
import leenkx.network.torrent.storage.Storage.IStorage;
|
||||
|
||||
class TorrentClient {
|
||||
public static var defaultTrackers:Array<String> = [
|
||||
"wss://ws1.leenkx.com"
|
||||
];
|
||||
public static var defaultTrackers:Array<String> = [];
|
||||
|
||||
public static var defaultDownloadDir:String = null;
|
||||
public var downloadDir:String = null;
|
||||
@ -247,8 +245,7 @@ class TorrentClient {
|
||||
files.push({path: name, data: Bytes.ofString(Std.string(data))});
|
||||
}
|
||||
|
||||
return {meta: MetaInfo.create(name, files, trackers, opts),
|
||||
files: files};
|
||||
return {meta: MetaInfo.create(name, files, trackers, opts), files: files};
|
||||
}
|
||||
|
||||
public static function readFolder(path:String, ?base:String):Array<{path:String, data:Bytes}> {
|
||||
@ -259,8 +256,7 @@ class TorrentClient {
|
||||
root = root.substr(0, root.length - 1);
|
||||
}
|
||||
if (base == null) {
|
||||
var i = Std.int(Math.max(root.lastIndexOf("/"),
|
||||
root.lastIndexOf("\\")));
|
||||
var i = Std.int(Math.max(root.lastIndexOf("/"), root.lastIndexOf("\\")));
|
||||
base = i >= 0 ? root.substr(i + 1) : root;
|
||||
}
|
||||
var files:Array<{path:String, data:Bytes}> = [];
|
||||
@ -317,20 +313,79 @@ class TorrentClient {
|
||||
return t;
|
||||
}
|
||||
|
||||
public function remove(infoHashHex:String, ?deleteData:Bool):Void {
|
||||
var t = torrents.get(infoHashHex);
|
||||
if (t == null) {
|
||||
t = torrents.get("v2:" + infoHashHex);
|
||||
if (t != null) infoHashHex = "v2:" + infoHashHex;
|
||||
public function get(id:Dynamic):Torrent {
|
||||
if (Std.isOfType(id, Torrent)) return id;
|
||||
if (id == null) return null;
|
||||
var hex = Std.string(id);
|
||||
if (StringTools.startsWith(hex, "v2:")) {
|
||||
hex = hex.substr(3);
|
||||
}
|
||||
if (t != null) {
|
||||
t.destroy();
|
||||
if (deleteData) t.deleteData();
|
||||
torrents.remove(infoHashHex);
|
||||
var t = torrents.get(hex);
|
||||
if (t == null) t = torrents.get("v2:" + hex);
|
||||
if (t == null) {
|
||||
for (f in torrents) {
|
||||
if (f.infoHashHex == hex || f.infoHashV2Hex == hex || (f.infoHash != null && Crypto.toHex(f.infoHash) == hex)) {
|
||||
t = f;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return t;
|
||||
}
|
||||
|
||||
public static function torrentIdFor(v:Dynamic):Dynamic {
|
||||
if (v == null) return null;
|
||||
if (Std.isOfType(v, haxe.io.Bytes)) return v;
|
||||
var s = Std.string(v);
|
||||
if (s.indexOf("magnet:") == 0) return s;
|
||||
if (s.indexOf("v2:") == 0) {
|
||||
var h = s.substr(3);
|
||||
return h.length == 64 ? "magnet:?xt=urn:btmh:1220" + h : s;
|
||||
}
|
||||
switch (s.length) {
|
||||
case 40 | 32:
|
||||
return "magnet:?xt=urn:btih:" + s;
|
||||
case 64:
|
||||
return "magnet:?xt=urn:btmh:1220" + s;
|
||||
case 68:
|
||||
return "magnet:?xt=urn:btmh:" + s;
|
||||
default:
|
||||
return s;
|
||||
}
|
||||
}
|
||||
|
||||
static function mergeTrackers(trackers:Array<String>, urls:Array<String>):Void {
|
||||
public static function trackerUrls(v:Dynamic):Array<String> {
|
||||
if (v == null) return null;
|
||||
var items:Array<Dynamic> = Std.isOfType(v, Array)
|
||||
? (v:Array<Dynamic>) : [v];
|
||||
var out:Array<String> = [];
|
||||
for (item in items) {
|
||||
var u:Dynamic = null;
|
||||
if (!Std.isOfType(item, String)) {
|
||||
try {
|
||||
u = Reflect.field(item, "url");
|
||||
} catch(e:Dynamic) {}
|
||||
}
|
||||
var s = u != null ? Std.string(u) : Std.string(item);
|
||||
if (s != "" && s != "null") out.push(s);
|
||||
}
|
||||
return out.length > 0 ? out : null;
|
||||
}
|
||||
|
||||
public function remove(infoHashHex:String, ?deleteData:Bool):Void {
|
||||
var t = get(infoHashHex);
|
||||
if (t != null) {
|
||||
t.destroy();
|
||||
if (deleteData) t.deleteData();
|
||||
var key:String = null;
|
||||
for (k in torrents.keys()) {
|
||||
if (torrents.get(k) == t) { key = k; break; }
|
||||
}
|
||||
if (key != null) torrents.remove(key);
|
||||
}
|
||||
}
|
||||
|
||||
public static function mergeTrackers(trackers:Array<String>, urls:Array<String>):Void {
|
||||
if (urls == null) return;
|
||||
for (u in urls) {
|
||||
if (u != null && Lambda.indexOf(trackers, u) < 0) {
|
||||
|
||||
@ -51,10 +51,12 @@ class RtcPeerPool {
|
||||
var rtcInitialized = false;
|
||||
#end
|
||||
|
||||
public static var defaultIceServers:Array<String> =
|
||||
["stun:stun.l.google.com:19302", "stun:global.stun.twilio.com:3478"];
|
||||
|
||||
public function new(?iceServers:Array<String>, ?channelLabel:String, unordered:Bool = false, maxRetransmits:Int = -1) {
|
||||
this.iceServers = iceServers != null ? iceServers
|
||||
: ["stun:stun.l.google.com:19302",
|
||||
"stun:global.stun.twilio.com:3478"];
|
||||
: defaultIceServers.copy();
|
||||
if (channelLabel != null) this.channelLabel = channelLabel;
|
||||
this.unordered = unordered;
|
||||
this.maxRetransmits = maxRetransmits;
|
||||
@ -72,8 +74,7 @@ class RtcPeerPool {
|
||||
}
|
||||
#end
|
||||
|
||||
var signaling = new RtcSignaling(trackerUrl, infoHashBinary,
|
||||
peerIdBinary);
|
||||
var signaling = new RtcSignaling(trackerUrl, infoHashBinary, peerIdBinary);
|
||||
signalings.push(signaling);
|
||||
signaling.statsProvider = statsProvider;
|
||||
signaling.onConnected = function() {
|
||||
@ -223,8 +224,7 @@ class RtcPeerPool {
|
||||
}
|
||||
};
|
||||
var dc = pc.createDataChannel(channelLabel,
|
||||
{ordered: !unordered,
|
||||
maxRetransmits: unordered ? maxRetransmits : null});
|
||||
{ordered: !unordered, maxRetransmits: unordered ? maxRetransmits : null});
|
||||
attachOfferChannel(offerIdHex, new DataChannelPeer(dc));
|
||||
var iceComplete = false;
|
||||
var iceTimer:haxe.Timer = null;
|
||||
@ -271,8 +271,7 @@ class RtcPeerPool {
|
||||
iceComplete = true;
|
||||
if (iceTimer != null) iceTimer.stop();
|
||||
if (!offerPeers.exists(offerIdHex)) return;
|
||||
sig.sendAnswer(peerIdHex, offerIdHex,
|
||||
pc.getLocalDescription());
|
||||
sig.sendAnswer(peerIdHex, offerIdHex, pc.getLocalDescription());
|
||||
};
|
||||
pc.onGatheringStateChange = function(state) {
|
||||
if (state == RTC_GATHERING_COMPLETE) sendAnswerSdp();
|
||||
@ -312,8 +311,7 @@ class RtcPeerPool {
|
||||
iceComplete = true;
|
||||
if (iceTimer != null) iceTimer.stop();
|
||||
if (!offerPeers.exists(offerIdHex)) return;
|
||||
sig.sendAnswer(peerIdHex, offerIdHex,
|
||||
RtcNative.getLocalDescription(pcId));
|
||||
sig.sendAnswer(peerIdHex, offerIdHex, RtcNative.getLocalDescription(pcId));
|
||||
};
|
||||
RtcNative.setGatheringStateCallback(pcId, function(state:Int) {
|
||||
if (state == RtcNative.RTC_GATHERING_COMPLETE) sendAnswerSdp();
|
||||
|
||||
@ -36,6 +36,10 @@ class DatachannelTransport implements ITransport {
|
||||
if (opts.announce != null && opts.announce.length > 0) {
|
||||
url = opts.announce[0];
|
||||
}
|
||||
if (url == null || url == "") {
|
||||
if (onError != null) onError("no announce tracker configured");
|
||||
return;
|
||||
}
|
||||
if (opts.iceServers != null) iceServers = opts.iceServers;
|
||||
pk = opts.pk != null ? opts.pk : "";
|
||||
ek = opts.ek != null ? opts.ek : "";
|
||||
|
||||
@ -48,7 +48,7 @@ class WebTorrentTransport implements ITransport {
|
||||
var blob:Dynamic = createBlob(identifier);
|
||||
var torrentOpts:Dynamic = {
|
||||
name: identifier,
|
||||
announce: opts.announce != null ? opts.announce : ["wss://ws1.leenkx.com"]
|
||||
announce: opts.announce != null ? opts.announce : leenkx.network.torrent.TorrentClient.defaultTrackers
|
||||
};
|
||||
if (opts.torrentOpts != null) {
|
||||
torrentOpts = js.lib.Object.assign(
|
||||
|
||||
@ -43,6 +43,10 @@ class WireTransport implements ITransport {
|
||||
if (opts.announce != null && opts.announce.length > 0) {
|
||||
url = opts.announce[0];
|
||||
}
|
||||
if (url == null || url == "") {
|
||||
if (onError != null) onError("no announce tracker configured");
|
||||
return;
|
||||
}
|
||||
if (opts.iceServers != null) iceServers = opts.iceServers;
|
||||
pk = opts.pk != null ? opts.pk : "";
|
||||
ek = opts.ek != null ? opts.ek : "";
|
||||
|
||||
@ -223,6 +223,9 @@ class LeenkxExporter:
|
||||
cls.export_network = False
|
||||
if wrd.lnx_network == 'Enabled':
|
||||
cls.export_network = True
|
||||
cls.export_torrent = False
|
||||
if wrd.lnx_torrent == 'Enabled':
|
||||
cls.export_torrent = True
|
||||
|
||||
@staticmethod
|
||||
def write_matrix(matrix):
|
||||
|
||||
@ -1,30 +0,0 @@
|
||||
from lnx.logicnode.lnx_nodes import *
|
||||
|
||||
|
||||
class AddTorrentNode(LnxLogicTreeNode):
|
||||
"""Add Torrent"""
|
||||
bl_idname = 'LNAddTorrentNode'
|
||||
bl_label = 'Add Torrent'
|
||||
bl_description = 'Add a torrent file from the network'
|
||||
lnx_category = 'Leenkx'
|
||||
lnx_version = 1
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(AddTorrentNode, self).__init__(*args, **kwargs)
|
||||
array_nodes[str(id(self))] = self
|
||||
|
||||
|
||||
def lnx_init(self, context):
|
||||
self.add_input('LnxNodeSocketAction', 'In')
|
||||
self.add_input('LnxDynamicSocket', 'Connection')
|
||||
self.add_input('LnxStringSocket', 'Torrent')
|
||||
|
||||
self.add_output('LnxNodeSocketAction', 'Out')
|
||||
self.add_output('LnxDynamicSocket', 'Title')
|
||||
self.add_output('LnxDynamicSocket', 'Files')
|
||||
self.add_output('LnxNodeSocketAction', 'Done')
|
||||
|
||||
|
||||
def register():
|
||||
add_category('Leenkx', icon='ORIENTATION_CURSOR')
|
||||
AddTorrentNode.on_register()
|
||||
@ -4,19 +4,71 @@ class CreateLeenkxNode(LnxLogicTreeNode):
|
||||
"""Create Leenkx"""
|
||||
bl_idname = 'LNCreateLeenkxNode'
|
||||
bl_label = 'Create Leenkx'
|
||||
bl_description = 'Create Leenkx'
|
||||
bl_description = ('Open a Leenkx peer connection on the given swarm identifier')
|
||||
lnx_category = 'Leenkx'
|
||||
lnx_version = 1
|
||||
|
||||
def update_sockets(self, context):
|
||||
if not self.inputs:
|
||||
return
|
||||
links = {}
|
||||
for socket in self.inputs:
|
||||
for link in socket.links:
|
||||
links.setdefault(socket.name, []).append(
|
||||
(link.from_node, link.from_socket))
|
||||
for socket in self.inputs[2:]:
|
||||
self.inputs.remove(socket)
|
||||
if (self.property0 == 'datachannel'
|
||||
and self.property2 != 'inherit'):
|
||||
self.add_input('LnxNodeSocketArray', 'ICE Servers')
|
||||
if self.property3 != 'inherit':
|
||||
self.add_input('LnxNodeSocketArray', 'Trackers')
|
||||
tree = self.id_data
|
||||
for socket in self.inputs:
|
||||
if socket.name in links:
|
||||
for from_node, from_socket in links[socket.name]:
|
||||
tree.links.new(from_socket, socket)
|
||||
|
||||
property0: HaxeEnumProperty('property0',
|
||||
items=[('wire', 'Wire', 'BitTorrent wire protocol transport (default)'),
|
||||
('datachannel', 'DataChannel', 'WebRTC data channels for browser/krom peers - shows the ICE Servers input')],
|
||||
name='Transport', default='wire', update=update_sockets)
|
||||
|
||||
property1: HaxeBoolProperty('property1',
|
||||
name='Gossip', description='Relay mode - forward messages to all connected peers', default=False)
|
||||
|
||||
property2: HaxeEnumProperty('property2',
|
||||
items=[('inherit', 'Inherit', 'Use the built-in STUN server list; no ICE Servers input'),
|
||||
('add', 'Add', 'Merge the ICE Servers input on top of the built-in STUN servers'),
|
||||
('override', 'Override', 'Replace the ICE server list with the ICE Servers input')],
|
||||
name='ICE Servers', default='inherit', update=update_sockets)
|
||||
|
||||
property3: HaxeEnumProperty('property3',
|
||||
items=[('inherit', 'Inherit', 'Use the project torrent tracker list; no Trackers input'),
|
||||
('add', 'Add', 'Merge the Trackers input on top of the project tracker list'),
|
||||
('override', 'Override', 'Replace the announce tracker list with the Trackers input')],
|
||||
name='Trackers', default='inherit', update=update_sockets)
|
||||
|
||||
def lnx_init(self, context):
|
||||
self.add_input('LnxNodeSocketAction', 'In')
|
||||
self.add_input('LnxStringSocket', 'Url', default_value="8001")
|
||||
self.add_input('LnxStringSocket', 'Channel', default_value='8001')
|
||||
if (self.property0 == 'datachannel'
|
||||
and self.property2 != 'inherit'):
|
||||
self.add_input('LnxNodeSocketArray', 'ICE Servers')
|
||||
if self.property3 != 'inherit':
|
||||
self.add_input('LnxNodeSocketArray', 'Trackers')
|
||||
|
||||
self.add_output('LnxNodeSocketAction', 'Out')
|
||||
self.add_output('LnxDynamicSocket', 'Connection')
|
||||
self.add_output('LnxDynamicSocket', 'ID')
|
||||
|
||||
def draw_buttons(self, context, layout):
|
||||
layout.prop(self, 'property0')
|
||||
layout.prop(self, 'property1')
|
||||
if self.property0 == 'datachannel':
|
||||
layout.prop(self, 'property2')
|
||||
layout.prop(self, 'property3')
|
||||
|
||||
|
||||
def register():
|
||||
add_category('Leenkx', icon='ORIENTATION_CURSOR')
|
||||
|
||||
@ -5,7 +5,7 @@ class CreateTorrentNode(LnxLogicTreeNode):
|
||||
"""Create Torrent"""
|
||||
bl_idname = 'LNCreateTorrentNode'
|
||||
bl_label = 'Create Torrent'
|
||||
bl_description = 'Create a torrent file from the network'
|
||||
bl_description = ('Build a .torrent from content (file, files or folder)')
|
||||
lnx_category = 'Leenkx'
|
||||
lnx_version = 1
|
||||
|
||||
@ -13,15 +13,65 @@ class CreateTorrentNode(LnxLogicTreeNode):
|
||||
super(CreateTorrentNode, self).__init__(*args, **kwargs)
|
||||
array_nodes[str(id(self))] = self
|
||||
|
||||
def update_sockets(self, context):
|
||||
if not self.inputs:
|
||||
return
|
||||
links = {}
|
||||
for socket in self.inputs:
|
||||
for link in socket.links:
|
||||
links.setdefault(socket.name, []).append(
|
||||
(link.from_node, link.from_socket))
|
||||
for socket in self.inputs[1:]:
|
||||
self.inputs.remove(socket)
|
||||
self._add_data_inputs()
|
||||
tree = self.id_data
|
||||
for socket in self.inputs:
|
||||
if socket.name in links:
|
||||
for from_node, from_socket in links[socket.name]:
|
||||
tree.links.new(from_socket, socket)
|
||||
|
||||
def _add_data_inputs(self):
|
||||
mode_sockets = {
|
||||
'file': ('LnxDynamicSocket', 'File (Bytes)', 'Content of the single file as Bytes'),
|
||||
'files': ('LnxNodeSocketArray', 'Files ({path, data})', 'Array of {path, data} objects'),
|
||||
'folder': ('LnxStringSocket', 'Folder Path', 'Folder read recursively for content (native targets only)'),
|
||||
}
|
||||
socket_type, label, desc = mode_sockets.get(self.property1, mode_sockets['file'])
|
||||
self.add_input(socket_type, label)
|
||||
self.add_input('LnxStringSocket', 'Name')
|
||||
self.add_input('LnxStringSocket', 'Comment')
|
||||
self.add_input('LnxStringSocket', 'Created By')
|
||||
self.add_input('LnxStringSocket', 'Source')
|
||||
self.add_input('LnxNodeSocketArray', 'Trackers')
|
||||
self.add_input('LnxBoolSocket', 'Private')
|
||||
self.add_input('LnxIntSocket', 'Piece Length')
|
||||
self.add_input('LnxNodeSocketArray', 'Web Seeds')
|
||||
|
||||
property0: HaxeEnumProperty('property0',
|
||||
items=[('1', 'V1', 'BitTorrent v1 - SHA-1 piece list over one packed stream; works with every client'),
|
||||
('2', 'V2', 'BitTorrent v2 (BEP 52) - SHA-256 merkle tree per file, pieces aligned to file boundaries; needs modern peers'),
|
||||
('3', 'Hybrid', 'One .torrent with both v1 and v2 metadata - two infohashes (btih + btmh) over the same info dict')],
|
||||
name='Version', default='1')
|
||||
|
||||
property1: HaxeEnumProperty('property1',
|
||||
items=[('file', 'File', 'Single file: Data is a Bytes payload'),
|
||||
('files', 'Files', 'Multiple files: Data is an array of {path, data}'),
|
||||
('folder', 'Folder', 'Read a folder recursively (native targets only): Data is a folder path string')],
|
||||
name='Data', default='file', update=update_sockets)
|
||||
|
||||
def lnx_init(self, context):
|
||||
self.add_input('LnxNodeSocketAction', 'In')
|
||||
self.add_input('LnxDynamicSocket', 'Connection')
|
||||
self.add_input('LnxDynamicSocket', 'Data')
|
||||
self._add_data_inputs()
|
||||
|
||||
self.add_output('LnxNodeSocketAction', 'Out')
|
||||
self.add_output('LnxDynamicSocket', 'Torrent')
|
||||
self.add_output('LnxNodeSocketAction', 'Done')
|
||||
self.add_output('LnxDynamicSocket', 'Meta')
|
||||
self.add_output('LnxDynamicSocket', 'Torrent Bytes')
|
||||
self.add_output('LnxStringSocket', 'InfoHash')
|
||||
self.add_output('LnxStringSocket', 'Magnet')
|
||||
|
||||
def draw_buttons(self, context, layout):
|
||||
layout.prop(self, 'property0')
|
||||
layout.prop(self, 'property1')
|
||||
|
||||
|
||||
def register():
|
||||
|
||||
61
leenkx/blender/lnx/logicnode/custom/LN_create_tracker.py
Normal file
61
leenkx/blender/lnx/logicnode/custom/LN_create_tracker.py
Normal file
@ -0,0 +1,61 @@
|
||||
from lnx.logicnode.lnx_nodes import *
|
||||
|
||||
|
||||
class CreateTrackerNode(LnxLogicTreeNode):
|
||||
"""Create Tracker"""
|
||||
bl_idname = 'LNCreateTrackerNode'
|
||||
bl_label = 'Create Tracker Server'
|
||||
bl_description = ('Hosts a local tracker')
|
||||
lnx_category = 'Leenkx'
|
||||
lnx_version = 1
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(CreateTrackerNode, self).__init__(*args, **kwargs)
|
||||
array_nodes[str(id(self))] = self
|
||||
|
||||
def update_sockets(self, context):
|
||||
if not self.inputs:
|
||||
return
|
||||
links = {}
|
||||
for socket in self.inputs:
|
||||
for link in socket.links:
|
||||
links.setdefault(socket.name, []).append(
|
||||
(link.from_node, link.from_socket))
|
||||
for socket in self.inputs[1:]:
|
||||
self.inputs.remove(socket)
|
||||
self._add_mode_inputs()
|
||||
tree = self.id_data
|
||||
for socket in self.inputs:
|
||||
if socket.name in links:
|
||||
for from_node, from_socket in links[socket.name]:
|
||||
tree.links.new(from_socket, socket)
|
||||
|
||||
def _add_mode_inputs(self):
|
||||
self.add_input('LnxStringSocket', 'Host', default_value='127.0.0.1')
|
||||
self.add_input('LnxIntSocket', 'Port', default_value=6969)
|
||||
if self.property0 == 'wss':
|
||||
self.add_input('LnxStringSocket', 'Cert File')
|
||||
self.add_input('LnxStringSocket', 'Key File')
|
||||
|
||||
property0: HaxeEnumProperty('property0',
|
||||
items=[('udp', 'UDP', 'UDP tracker (BEP 15, sys targets only)'),
|
||||
('http', 'HTTP', 'HTTP tracker (BEP 3)'),
|
||||
('ws', 'WS', 'Plain WebSocket tracker (local relay, WebTorrent/web peers)'),
|
||||
('wss', 'WSS', 'Secure WebSocket tracker (WebTorrent/web peers)')],
|
||||
name='Type', default='udp', update=update_sockets)
|
||||
|
||||
def lnx_init(self, context):
|
||||
self.add_input('LnxNodeSocketAction', 'In')
|
||||
self._add_mode_inputs()
|
||||
|
||||
self.add_output('LnxNodeSocketAction', 'Out')
|
||||
self.add_output('LnxDynamicSocket', 'Tracker')
|
||||
self.add_output('LnxStringSocket', 'URL')
|
||||
|
||||
def draw_buttons(self, context, layout):
|
||||
layout.prop(self, 'property0')
|
||||
|
||||
|
||||
def register():
|
||||
add_category('Leenkx', icon='ORIENTATION_CURSOR')
|
||||
CreateTrackerNode.on_register()
|
||||
76
leenkx/blender/lnx/logicnode/custom/LN_download_torrent.py
Normal file
76
leenkx/blender/lnx/logicnode/custom/LN_download_torrent.py
Normal file
@ -0,0 +1,76 @@
|
||||
from lnx.logicnode.lnx_nodes import *
|
||||
|
||||
|
||||
class DownloadTorrentNode(LnxLogicTreeNode):
|
||||
"""Download Torrent"""
|
||||
bl_idname = 'LNDownloadTorrentNode'
|
||||
bl_label = 'Download Torrent'
|
||||
bl_description = ('Add a torrent to a Leenkx connection and start downloading')
|
||||
lnx_category = 'Leenkx'
|
||||
lnx_version = 1
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(DownloadTorrentNode, self).__init__(*args, **kwargs)
|
||||
array_nodes[str(id(self))] = self
|
||||
|
||||
def update_sockets(self, context):
|
||||
if not self.inputs:
|
||||
return
|
||||
links = {}
|
||||
for socket in self.inputs:
|
||||
for link in socket.links:
|
||||
links.setdefault(socket.name, []).append(
|
||||
(link.from_node, link.from_socket))
|
||||
for socket in self.inputs[2:]:
|
||||
self.inputs.remove(socket)
|
||||
self._add_id_inputs()
|
||||
tree = self.id_data
|
||||
for socket in self.inputs:
|
||||
if socket.name in links:
|
||||
for from_node, from_socket in links[socket.name]:
|
||||
tree.links.new(from_socket, socket)
|
||||
|
||||
def _add_id_inputs(self):
|
||||
mode_sockets = {
|
||||
'magnet': ('LnxStringSocket', 'Magnet URI', 'Magnet URI (magnet:?xt=...)'),
|
||||
'id': ('LnxStringSocket', 'Infohash', '40-char v1 or 64-char v2 infohash hex'),
|
||||
'file': ('LnxDynamicSocket', 'Torrent File (Bytes)', '.torrent file bytes'),
|
||||
}
|
||||
socket_type, label, desc = mode_sockets.get(self.property0, mode_sockets['magnet'])
|
||||
self.add_input(socket_type, label)
|
||||
if self.property1 != 'inherit':
|
||||
self.add_input('LnxNodeSocketArray', 'Trackers')
|
||||
|
||||
property0: HaxeEnumProperty('property0',
|
||||
items=[('magnet', 'Magnet', 'Magnet URI (magnet:?xt=...)'),
|
||||
('id', 'Infohash', '40-char v1 or 64-char v2 infohash hex'),
|
||||
('file', 'Torrent File', '.torrent file bytes')],
|
||||
name='Input', default='magnet', update=update_sockets)
|
||||
|
||||
property1: HaxeEnumProperty('property1',
|
||||
items=[('inherit', 'Inherit', 'Use the torrent\'s own trackers plus the connection\'s; no Trackers input'),
|
||||
('add', 'Add', 'Merge the Trackers input on top of the torrent\'s own trackers'),
|
||||
('override', 'Override', 'Replace all trackers with the Trackers input (torrent\'s own and connection defaults are dropped)')],
|
||||
name='Trackers', default='inherit', update=update_sockets)
|
||||
|
||||
def lnx_init(self, context):
|
||||
self.add_input('LnxNodeSocketAction', 'In')
|
||||
self.add_input('LnxDynamicSocket', 'Connection')
|
||||
self._add_id_inputs()
|
||||
|
||||
self.add_output('LnxNodeSocketAction', 'Out')
|
||||
self.add_output('LnxDynamicSocket', 'Torrent')
|
||||
self.add_output('LnxStringSocket', 'Name')
|
||||
self.add_output('LnxNodeSocketArray', 'Files')
|
||||
self.add_output('LnxNodeSocketAction', 'Done')
|
||||
self.add_output('LnxStringSocket', 'InfoHash')
|
||||
self.add_output('LnxStringSocket', 'Magnet')
|
||||
|
||||
def draw_buttons(self, context, layout):
|
||||
layout.prop(self, 'property0')
|
||||
layout.prop(self, 'property1')
|
||||
|
||||
|
||||
def register():
|
||||
add_category('Leenkx', icon='ORIENTATION_CURSOR')
|
||||
DownloadTorrentNode.on_register()
|
||||
77
leenkx/blender/lnx/logicnode/custom/LN_get_torrent_file.py
Normal file
77
leenkx/blender/lnx/logicnode/custom/LN_get_torrent_file.py
Normal file
@ -0,0 +1,77 @@
|
||||
from lnx.logicnode.lnx_nodes import *
|
||||
|
||||
|
||||
class GetTorrentFileNode(LnxLogicTreeNode):
|
||||
"""Get Torrent File"""
|
||||
bl_idname = 'LNGetTorrentFileNode'
|
||||
bl_label = 'Get Torrent File'
|
||||
bl_description = ('Find one file inside a torrent and read its '
|
||||
'stored bytes (webtorrent torrent.files.find + '
|
||||
'file.arrayBuffer). Match By selects how the '
|
||||
'Match input is interpreted: index, exact file '
|
||||
'name, exact path, or extension')
|
||||
lnx_category = 'Leenkx'
|
||||
lnx_version = 1
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(GetTorrentFileNode, self).__init__(*args, **kwargs)
|
||||
array_nodes[str(id(self))] = self
|
||||
|
||||
def update_sockets(self, context):
|
||||
if not self.inputs:
|
||||
return
|
||||
links = {}
|
||||
for socket in self.inputs:
|
||||
for link in socket.links:
|
||||
links.setdefault(socket.name, []).append(
|
||||
(link.from_node, link.from_socket))
|
||||
for socket in self.inputs[3:]:
|
||||
self.inputs.remove(socket)
|
||||
self._add_match_input()
|
||||
tree = self.id_data
|
||||
for socket in self.inputs:
|
||||
if socket.name in links:
|
||||
for from_node, from_socket in links[socket.name]:
|
||||
tree.links.new(from_socket, socket)
|
||||
|
||||
def _add_match_input(self):
|
||||
match_sockets = {
|
||||
'index': ('LnxIntSocket', 'Index', 'Position in the torrent file list'),
|
||||
'name': ('LnxStringSocket', 'Name', 'Exact file name, e.g. movie.mp4'),
|
||||
'path': ('LnxStringSocket', 'Path', 'Exact path inside the torrent, e.g. docs/readme.txt'),
|
||||
'ext': ('LnxStringSocket', 'Extension', 'File extension like mp4 or .mp4; first matching file wins'),
|
||||
}
|
||||
socket_type, label, desc = match_sockets.get(self.property0, match_sockets['name'])
|
||||
self.add_input(socket_type, label)
|
||||
|
||||
property0: HaxeEnumProperty('property0',
|
||||
items=[('index', 'Index', 'Position in the torrent file list; Match is an int'),
|
||||
('name', 'Name', 'Exact file name; Match is a string like movie.mp4'),
|
||||
('path', 'Path', 'Exact path inside the torrent; Match is a string like docs/readme.txt'),
|
||||
('ext', 'Extension', 'First file whose name ends with the extension; Match is mp4 or .mp4')],
|
||||
name='Match By', default='name', update=update_sockets)
|
||||
|
||||
def lnx_init(self, context):
|
||||
self.add_input('LnxNodeSocketAction', 'In')
|
||||
self.add_input('LnxDynamicSocket', 'Connection')
|
||||
self.add_input('LnxDynamicSocket', 'Torrent')
|
||||
self._add_match_input()
|
||||
|
||||
self.add_output('LnxNodeSocketAction', 'Out')
|
||||
self.add_output('LnxDynamicSocket', 'File')
|
||||
self.add_output('LnxStringSocket', 'Name')
|
||||
self.add_output('LnxStringSocket', 'Path')
|
||||
self.add_output('LnxFloatSocket', 'Size')
|
||||
self.add_output('LnxStringSocket', 'Type')
|
||||
self.add_output('LnxDynamicSocket', 'Data')
|
||||
self.add_output('LnxBoolSocket', 'Found')
|
||||
self.add_output('LnxFloatSocket', 'Downloaded')
|
||||
self.add_output('LnxFloatSocket', 'Progress')
|
||||
|
||||
def draw_buttons(self, context, layout):
|
||||
layout.prop(self, 'property0')
|
||||
|
||||
|
||||
def register():
|
||||
add_category('Leenkx', icon='ORIENTATION_CURSOR')
|
||||
GetTorrentFileNode.on_register()
|
||||
@ -5,63 +5,40 @@ class LeenkxCloseConnectionNode(LnxLogicTreeNode):
|
||||
"""Close connection on the network"""
|
||||
bl_idname = 'LNLeenkxCloseConnectionNode'
|
||||
bl_label = 'Close Leenkx Connection'
|
||||
bl_description = ('Tear down a Leenkx connection, or drop a single peer from it')
|
||||
lnx_version = 1
|
||||
lnx_category = 'Leenkx'
|
||||
ind = 2
|
||||
|
||||
@staticmethod
|
||||
def get_enum_id_value(obj, prop_name, value):
|
||||
return obj.bl_rna.properties[prop_name].enum_items[value].identifier
|
||||
|
||||
@staticmethod
|
||||
def get_count_in(type_name):
|
||||
return {
|
||||
'client': 0,
|
||||
'peer': 1
|
||||
}.get(type_name, 0)
|
||||
|
||||
def get_enum(self):
|
||||
return self.get('property0', 0)
|
||||
|
||||
def set_enum(self, value):
|
||||
# Checking the selection of each type
|
||||
select_current = self.get_enum_id_value(self, 'property0', value)
|
||||
select_prev = self.property0
|
||||
|
||||
#Check if type changed
|
||||
if select_prev != select_current:
|
||||
|
||||
for i in self.inputs:
|
||||
self.inputs.remove(i)
|
||||
|
||||
# Arguements for type Host
|
||||
if (self.get_count_in(select_current) == 0):
|
||||
self.add_input('LnxNodeSocketAction', 'In')
|
||||
self.add_input('LnxDynamicSocket', 'Connection')
|
||||
self.add_input('LnxStringSocket', 'Url')
|
||||
|
||||
# Arguements for type Client
|
||||
if (self.get_count_in(select_current) == 1):
|
||||
self.add_input('LnxNodeSocketAction', 'In')
|
||||
self.add_input('LnxDynamicSocket', 'Connection')
|
||||
def update_sockets(self, context):
|
||||
if not self.inputs:
|
||||
return
|
||||
links = {}
|
||||
for socket in self.inputs:
|
||||
for link in socket.links:
|
||||
links.setdefault(socket.name, []).append(
|
||||
(link.from_node, link.from_socket))
|
||||
for socket in self.inputs[2:]:
|
||||
self.inputs.remove(socket)
|
||||
if self.property0 == 'peer':
|
||||
self.add_input('LnxStringSocket', 'ID')
|
||||
tree = self.id_data
|
||||
for socket in self.inputs:
|
||||
if socket.name in links:
|
||||
for from_node, from_socket in links[socket.name]:
|
||||
tree.links.new(from_socket, socket)
|
||||
|
||||
self['property0'] = value
|
||||
|
||||
|
||||
property0: HaxeEnumProperty(
|
||||
'property1',
|
||||
property0: HaxeEnumProperty('property0',
|
||||
items = [('client', 'Leenkx Client', 'Close the Leenkx Client connection on the network'),
|
||||
('peer', 'Single Peer', 'Close a Peer connection on network')],
|
||||
('peer', 'Single Peer', 'Drop a peer from the connection (forgets its address; it can rejoin on a new handshake)')],
|
||||
name='', default='client',
|
||||
set=set_enum,
|
||||
get=get_enum)
|
||||
update=update_sockets)
|
||||
|
||||
|
||||
def lnx_init(self, context):
|
||||
self.add_input('LnxNodeSocketAction', 'In')
|
||||
self.add_input('LnxDynamicSocket', 'Connection')
|
||||
self.add_input('LnxStringSocket', 'Url')
|
||||
if self.property0 == 'peer':
|
||||
self.add_input('LnxStringSocket', 'ID')
|
||||
|
||||
self.add_output('LnxNodeSocketAction', 'Out')
|
||||
|
||||
|
||||
@ -9,84 +9,26 @@ class LeenkxEventNode(LnxLogicTreeNode):
|
||||
lnx_category = 'Leenkx'
|
||||
lnx_version = 1
|
||||
|
||||
@staticmethod
|
||||
def get_enum_id_value(obj, prop_name, value):
|
||||
return obj.bl_rna.properties[prop_name].enum_items[value].identifier
|
||||
|
||||
@staticmethod
|
||||
def get_count_in(type_name):
|
||||
return {
|
||||
'client': 0,
|
||||
'host': 1,
|
||||
'securehost': 2
|
||||
}.get(type_name, 0)
|
||||
|
||||
def get_enum(self):
|
||||
return self.get('property0', 0)
|
||||
|
||||
def set_enum(self, value):
|
||||
# Checking the selection of each type
|
||||
select_current = self.get_enum_id_value(self, 'property0', value)
|
||||
select_prev = self.property0
|
||||
|
||||
#Check if type changed
|
||||
if select_prev != select_current:
|
||||
|
||||
for i in self.inputs:
|
||||
self.inputs.remove(i)
|
||||
|
||||
# Arguements for type Client
|
||||
if (self.get_count_in(select_current) == 0):
|
||||
self.add_input('LnxStringSocket', 'Url', default_value="8001")
|
||||
|
||||
|
||||
# Arguements for type Host
|
||||
if (self.get_count_in(select_current) == 1):
|
||||
self.add_input('LnxStringSocket', 'Domain', default_value="127.0.0.1")
|
||||
self.add_input('LnxIntSocket', 'Port', default_value=8001)
|
||||
|
||||
|
||||
# Arguements for type Secure Host
|
||||
if (self.get_count_in(select_current) == 2):
|
||||
self.add_input('LnxStringSocket', 'Domain', default_value="127.0.0.1")
|
||||
self.add_input('LnxIntSocket', 'Port', default_value=8001)
|
||||
|
||||
|
||||
self['property0'] = value
|
||||
|
||||
|
||||
property0: HaxeEnumProperty(
|
||||
'property0',
|
||||
items = [('client', 'Client', 'Network client Event listener'),
|
||||
('host', 'Host', 'Network host Event listener'),
|
||||
('securehost', 'Secure Host', 'Network secure host Event listener')],
|
||||
name='',
|
||||
default='client',
|
||||
set=set_enum,
|
||||
get=get_enum)
|
||||
|
||||
|
||||
property1: HaxeEnumProperty(
|
||||
'property1',
|
||||
items = [('onopen', 'OnOpen', 'Listens to onOpen event'),
|
||||
('onmessage', 'OnMessage', 'Listens to onMessage event'),
|
||||
('onerror', 'OnError', 'Listens to onError event'),
|
||||
('onclose', 'OnClose', 'Listens to onClose event'),
|
||||
('onseen', 'OnSeen', 'Listens to onClose event'),
|
||||
('onserver', 'OnServer', 'Listens to onClose event'),
|
||||
('onconnections', 'OnConnections', 'Listens to onClose event'),
|
||||
('onping', 'OnPing', 'Listens to onClose event'),
|
||||
('onleft', 'OnLeft', 'Listens to onClose event'),
|
||||
('ontimeout', 'OnTimeout', 'Listens to onClose event'),
|
||||
('onrpc', 'OnRpc', 'Listens to onClose event'),
|
||||
('onrpcresponse', 'OnRpcResponse', 'Listens to onClose event'),
|
||||
('onwireleft', 'OnWireLeft', 'Listens to onClose event'),
|
||||
('onwireseen', 'OnWireSeen', 'Listens to onClose event'),
|
||||
('ontorrent', 'OnTorrent', 'Listens to onClose event'),
|
||||
('ontracker', 'OnTracker', 'Listens to onClose event'),
|
||||
('onannounce', 'OnAnnounce', 'Listens to onClose event')],
|
||||
name='',
|
||||
default='onopen')
|
||||
property1: HaxeEnumProperty('property1',
|
||||
items = [('onopen', 'OnOpen', 'The connection to the server is open'),
|
||||
('onmessage', 'OnMessage', 'A message arrived; ID is the sender peer id, Data the payload'),
|
||||
('onerror', 'OnError', 'A connection error occurred'),
|
||||
('onclose', 'OnClose', 'The connection was closed'),
|
||||
('onseen', 'OnSeen', 'A peer was discovered; ID is the peer id'),
|
||||
('onserver', 'OnServer', 'The signaling server responded'),
|
||||
('onconnections', 'OnConnections', 'The server sent the peer list; Data is the connection list'),
|
||||
('onping', 'OnPing', 'Heartbeat ping roundtrip completed'),
|
||||
('onleft', 'OnLeft', 'A peer left; ID is the peer id'),
|
||||
('ontimeout', 'OnTimeout', 'A handshake timed out'),
|
||||
('onwireleft', 'OnWireLeft', 'A BitTorrent wire peer disconnected; ID is the peer id'),
|
||||
('onwireseen', 'OnWireSeen', 'A BitTorrent wire peer connected; ID is the peer id'),
|
||||
('ontorrent', 'OnTorrent', 'Torrent activity on the connection; Data is the torrent'),
|
||||
('ontorrentadded', 'OnTorrentAdded', 'A torrent registered on the connection; Data is the Torrent handle'),
|
||||
('ontracker', 'OnTracker', 'A tracker event on the connection'),
|
||||
('onannounce', 'OnAnnounce', 'A tracker announce completed'),
|
||||
('ontorrentdone', 'OnTorrentDone', 'A torrent finished downloading; Data is the torrent'),
|
||||
('onload', 'OnLoad', 'The connection was registered; fires once at startup')],
|
||||
name='', default='onopen')
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(LeenkxEventNode, self).__init__(*args, **kwargs)
|
||||
@ -95,7 +37,7 @@ class LeenkxEventNode(LnxLogicTreeNode):
|
||||
|
||||
def lnx_init(self, context):
|
||||
#self.add_input('LnxNodeSocketAction', 'In')
|
||||
self.add_input('LnxStringSocket', 'Url', default_value="ws://127.0.0.1:8001")
|
||||
self.add_input('LnxStringSocket', 'Channel', default_value='8001')
|
||||
|
||||
self.add_output('LnxNodeSocketAction', 'Out')
|
||||
self.add_output('LnxDynamicSocket', 'ID')
|
||||
@ -103,7 +45,6 @@ class LeenkxEventNode(LnxLogicTreeNode):
|
||||
|
||||
|
||||
def draw_buttons(self, context, layout):
|
||||
layout.prop(self, 'property0')
|
||||
layout.prop(self, 'property1')
|
||||
|
||||
def register():
|
||||
|
||||
@ -9,16 +9,15 @@ class LeenkxMessageParserNode(LnxLogicTreeNode):
|
||||
lnx_category = 'Leenkx'
|
||||
lnx_version = 1
|
||||
|
||||
property0: HaxeEnumProperty(
|
||||
'property0',
|
||||
items = [('string', 'String', 'Event for a string over the network'),
|
||||
('vector', 'Vector', 'Event for a vector over the network'),
|
||||
('float', 'Float', 'Event for a float over the network'),
|
||||
('integer', 'Integer', 'Event for an integer over the network'),
|
||||
('boolean', 'Boolean', 'Event for a boolean over the network'),
|
||||
('transform', 'Transform', 'Event for a transform over the network'),
|
||||
('rotation', 'Rotation', 'Event for a rotation over the network')],
|
||||
name='', default='string')
|
||||
property0: HaxeEnumProperty('property0', items=[
|
||||
('string', 'String', 'Parse the message as a string'),
|
||||
('vector', 'Vector', 'Parse the message as a vector (Vec4)'),
|
||||
('float', 'Float', 'Parse the message as a float'),
|
||||
('integer', 'Integer', 'Parse the message as an integer'),
|
||||
('boolean', 'Boolean', 'Parse the message as a boolean'),
|
||||
('transform', 'Transform', 'Parse the message as a transform (Mat4)'),
|
||||
('rotation', 'Rotation', 'Parse the message as a rotation (Quat)')
|
||||
], name='', default='string')
|
||||
|
||||
|
||||
def lnx_init(self, context):
|
||||
|
||||
@ -9,77 +9,55 @@ class LeenkxSendMessageNode(LnxLogicTreeNode):
|
||||
lnx_category = 'Leenkx'
|
||||
lnx_version = 1
|
||||
|
||||
@staticmethod
|
||||
def get_enum_id_value(obj, prop_name, value):
|
||||
return obj.bl_rna.properties[prop_name].enum_items[value].identifier
|
||||
def update_sockets(self, context):
|
||||
if not self.inputs:
|
||||
return
|
||||
links = {}
|
||||
send_all = True
|
||||
for socket in self.inputs:
|
||||
for link in socket.links:
|
||||
links.setdefault(socket.name, []).append(
|
||||
(link.from_node, link.from_socket))
|
||||
if socket.name == 'Send All':
|
||||
send_all = socket.default_value_raw
|
||||
for socket in self.inputs[3:]:
|
||||
self.inputs.remove(socket)
|
||||
self._add_data_inputs(send_all)
|
||||
tree = self.id_data
|
||||
for socket in self.inputs:
|
||||
if socket.name in links:
|
||||
for from_node, from_socket in links[socket.name]:
|
||||
tree.links.new(from_socket, socket)
|
||||
|
||||
@staticmethod
|
||||
def get_count_in(type_name):
|
||||
return {
|
||||
'client': 0,
|
||||
'host': 1,
|
||||
'securehost': 2
|
||||
}.get(type_name, 0)
|
||||
def _add_data_inputs(self, send_all=True):
|
||||
data_sockets = {
|
||||
'string': ('LnxStringSocket', 'String payload to send'),
|
||||
'vector': ('LnxVectorSocket', 'Vector payload to send'),
|
||||
'float': ('LnxFloatSocket', 'Float payload to send'),
|
||||
'integer': ('LnxIntSocket', 'Integer payload to send'),
|
||||
'boolean': ('LnxBoolSocket', 'Boolean payload to send'),
|
||||
'transform': ('LnxDynamicSocket', 'Transform (Mat4) payload to send'),
|
||||
'rotation': ('LnxRotationSocket', 'Rotation (Quat) payload to send'),
|
||||
}
|
||||
socket_type, desc = data_sockets.get(self.property1, data_sockets['string'])
|
||||
self.add_input(socket_type, 'Data')
|
||||
self.add_input('LnxBoolSocket', 'Send All', default_value=send_all)
|
||||
if not send_all:
|
||||
self._add_id_input()
|
||||
|
||||
def get_enum(self):
|
||||
return self.get('property0', 0)
|
||||
|
||||
def set_enum(self, value):
|
||||
# Checking the selection of each type
|
||||
select_current = self.get_enum_id_value(self, 'property0', value)
|
||||
select_prev = self.property0
|
||||
|
||||
#Check if type changed
|
||||
if select_prev != select_current:
|
||||
|
||||
for i in self.inputs:
|
||||
self.inputs.remove(i)
|
||||
|
||||
# Arguements for type Client
|
||||
if (self.get_count_in(select_current) == 0):
|
||||
self.add_input('LnxNodeSocketAction', 'In')
|
||||
self.add_input('LnxDynamicSocket', 'Connection')
|
||||
self.add_input('LnxStringSocket', 'API')
|
||||
self.add_input('LnxDynamicSocket', 'Data')
|
||||
def _add_id_input(self):
|
||||
self.add_input('LnxStringSocket', 'ID')
|
||||
self.add_input('LnxBoolSocket', 'Send All')
|
||||
|
||||
# Arguements for type Host
|
||||
if (self.get_count_in(select_current) == 1):
|
||||
self.add_input('LnxNodeSocketAction', 'In')
|
||||
self.add_input('LnxDynamicSocket', 'Connection')
|
||||
self.add_input('LnxStringSocket', 'API')
|
||||
self.add_input('LnxDynamicSocket', 'Data')
|
||||
self.add_input('LnxStringSocket', 'ID')
|
||||
self.add_input('LnxBoolSocket', 'Send All')
|
||||
def on_socket_val_update(self, context, socket):
|
||||
if socket.name == 'Send All':
|
||||
if (len(self.inputs) > 5
|
||||
and self.inputs[5].name == 'ID'):
|
||||
self.inputs.remove(self.inputs[5])
|
||||
if not socket.default_value_raw:
|
||||
self._add_id_input()
|
||||
super().on_socket_val_update(context, socket)
|
||||
|
||||
|
||||
# Arguements for type Secure Host
|
||||
if (self.get_count_in(select_current) == 2):
|
||||
self.add_input('LnxNodeSocketAction', 'In')
|
||||
self.add_input('LnxDynamicSocket', 'Connection')
|
||||
self.add_input('LnxStringSocket', 'API')
|
||||
self.add_input('LnxDynamicSocket', 'Data')
|
||||
self.add_input('LnxStringSocket', 'ID')
|
||||
self.add_input('LnxBoolSocket', 'Send All')
|
||||
|
||||
|
||||
self['property0'] = value
|
||||
|
||||
|
||||
property0: HaxeEnumProperty(
|
||||
'property0',
|
||||
items = [('client', 'Client', 'Network client Event listener'),
|
||||
('host', 'Host', 'Network host Event listener'),
|
||||
('securehost', 'Secure Host', 'Network secure host Event listener')],
|
||||
name='',
|
||||
default='client',
|
||||
set=set_enum,
|
||||
get=get_enum)
|
||||
|
||||
|
||||
property1: HaxeEnumProperty(
|
||||
'property1',
|
||||
property1: HaxeEnumProperty('property1',
|
||||
items = [('string', 'String', 'Send a string over the network to one or all of the clients'),
|
||||
('vector', 'Vector', 'Send a vector over the network to one or all of the clients'),
|
||||
('float', 'Float', 'Send a float over the network to one or all of the clients'),
|
||||
@ -87,8 +65,12 @@ class LeenkxSendMessageNode(LnxLogicTreeNode):
|
||||
('boolean', 'Boolean', 'Send a boolean over the network to one or all of the clients'),
|
||||
('transform', 'Transform', 'Send a transform over the network to one or all of the clients'),
|
||||
('rotation', 'Rotation', 'Send a rotation over the network to one or all of the clients')],
|
||||
name='',
|
||||
default='string')
|
||||
name='', default='string', update=update_sockets)
|
||||
|
||||
property2: HaxeBoolProperty('property2',
|
||||
name='Encrypted',
|
||||
description='Use full encryption protocol (signing, bencode, box encryption). Disable for fast raw message mode',
|
||||
default=False)
|
||||
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
@ -100,17 +82,15 @@ class LeenkxSendMessageNode(LnxLogicTreeNode):
|
||||
self.add_input('LnxNodeSocketAction', 'In')
|
||||
self.add_input('LnxDynamicSocket', 'Connection')
|
||||
self.add_input('LnxStringSocket', 'API')
|
||||
self.add_input('LnxDynamicSocket', 'Data')
|
||||
self.add_input('LnxStringSocket', 'ID')
|
||||
self.add_input('LnxBoolSocket', 'Send All')
|
||||
self._add_data_inputs()
|
||||
|
||||
self.add_output('LnxNodeSocketAction', 'Out')
|
||||
|
||||
|
||||
|
||||
def draw_buttons(self, context, layout):
|
||||
layout.prop(self, 'property0')
|
||||
layout.prop(self, 'property1')
|
||||
layout.prop(self, 'property2')
|
||||
|
||||
def register():
|
||||
add_category('Leenkx', icon='ORIENTATION_CURSOR')
|
||||
|
||||
68
leenkx/blender/lnx/logicnode/custom/LN_parse_torrent.py
Normal file
68
leenkx/blender/lnx/logicnode/custom/LN_parse_torrent.py
Normal file
@ -0,0 +1,68 @@
|
||||
from lnx.logicnode.lnx_nodes import *
|
||||
|
||||
|
||||
class ParseTorrentNode(LnxLogicTreeNode):
|
||||
"""Parse Torrent"""
|
||||
bl_idname = 'LNParseTorrentNode'
|
||||
bl_label = 'Parse Torrent'
|
||||
bl_description = ('Parse a torrent id without joining a swarm')
|
||||
lnx_category = 'Leenkx'
|
||||
lnx_version = 1
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(ParseTorrentNode, self).__init__(*args, **kwargs)
|
||||
array_nodes[str(id(self))] = self
|
||||
|
||||
def update_sockets(self, context):
|
||||
if not self.inputs:
|
||||
return
|
||||
links = {}
|
||||
for socket in self.inputs:
|
||||
for link in socket.links:
|
||||
links.setdefault(socket.name, []).append(
|
||||
(link.from_node, link.from_socket))
|
||||
for socket in self.inputs[1:]:
|
||||
self.inputs.remove(socket)
|
||||
self._add_id_input()
|
||||
tree = self.id_data
|
||||
for socket in self.inputs:
|
||||
if socket.name in links:
|
||||
for from_node, from_socket in links[socket.name]:
|
||||
tree.links.new(from_socket, socket)
|
||||
|
||||
def _add_id_input(self):
|
||||
mode_sockets = {
|
||||
'magnet': ('LnxStringSocket', 'Magnet URI', 'Magnet URI (magnet:?xt=...)'),
|
||||
'id': ('LnxStringSocket', 'Infohash', '40-char v1 or 64-char v2 infohash hex'),
|
||||
'file': ('LnxDynamicSocket', 'Torrent File (Bytes)', '.torrent file bytes'),
|
||||
}
|
||||
socket_type, label, desc = mode_sockets.get(self.property0, mode_sockets['magnet'])
|
||||
self.add_input(socket_type, label)
|
||||
|
||||
property0: HaxeEnumProperty('property0',
|
||||
items=[('magnet', 'Magnet', 'Magnet URI (magnet:?xt=...)'),
|
||||
('id', 'Infohash', '40-char v1 or 64-char v2 infohash hex'),
|
||||
('file', 'Torrent File', '.torrent file bytes')],
|
||||
name='Input', default='magnet', update=update_sockets)
|
||||
|
||||
def lnx_init(self, context):
|
||||
self.add_input('LnxNodeSocketAction', 'In')
|
||||
self._add_id_input()
|
||||
|
||||
self.add_output('LnxNodeSocketAction', 'Out')
|
||||
self.add_output('LnxDynamicSocket', 'Meta')
|
||||
self.add_output('LnxStringSocket', 'InfoHash')
|
||||
self.add_output('LnxStringSocket', 'Name')
|
||||
self.add_output('LnxNodeSocketArray', 'Trackers')
|
||||
self.add_output('LnxNodeSocketArray', 'Web Seeds')
|
||||
self.add_output('LnxNodeSocketArray', 'Files')
|
||||
self.add_output('LnxFloatSocket', 'Total Size')
|
||||
self.add_output('LnxDynamicSocket', 'Torrent Bytes')
|
||||
|
||||
def draw_buttons(self, context, layout):
|
||||
layout.prop(self, 'property0')
|
||||
|
||||
|
||||
def register():
|
||||
add_category('Leenkx', icon='ORIENTATION_CURSOR')
|
||||
ParseTorrentNode.on_register()
|
||||
@ -17,12 +17,14 @@ class SeedTorrentNode(LnxLogicTreeNode):
|
||||
def lnx_init(self, context):
|
||||
self.add_input('LnxNodeSocketAction', 'In')
|
||||
self.add_input('LnxDynamicSocket', 'Connection')
|
||||
self.add_input('LnxDynamicSocket', 'Opts [Map]')
|
||||
self.add_input('LnxDynamicSocket', 'Torrent')
|
||||
self.add_input('LnxDynamicSocket', 'Data')
|
||||
|
||||
self.add_output('LnxNodeSocketAction', 'Out')
|
||||
self.add_output('LnxDynamicSocket', 'Torrent')
|
||||
self.add_output('LnxNodeSocketAction', 'Done')
|
||||
self.add_output('LnxStringSocket', 'InfoHash')
|
||||
self.add_output('LnxStringSocket', 'Magnet')
|
||||
|
||||
|
||||
def register():
|
||||
|
||||
75
leenkx/blender/lnx/logicnode/custom/LN_set_trackers.py
Normal file
75
leenkx/blender/lnx/logicnode/custom/LN_set_trackers.py
Normal file
@ -0,0 +1,75 @@
|
||||
from lnx.logicnode.lnx_nodes import *
|
||||
|
||||
|
||||
class SetTrackersNode(LnxLogicTreeNode):
|
||||
"""Set Trackers"""
|
||||
bl_idname = 'LNSetTrackersNode'
|
||||
bl_label = 'Set Trackers'
|
||||
bl_description = ('Add/Override edit the tracker announce list of a live torrent')
|
||||
lnx_category = 'Leenkx'
|
||||
lnx_version = 1
|
||||
|
||||
tracker_props = {
|
||||
'interval': ('LnxIntSocket', 'Interval'),
|
||||
'port': ('LnxIntSocket', 'Port'),
|
||||
'key': ('LnxIntSocket', 'Key'),
|
||||
'numwant': ('LnxIntSocket', 'Num Want'),
|
||||
'trackerId': ('LnxStringSocket', 'Tracker ID'),
|
||||
}
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(SetTrackersNode, self).__init__(*args, **kwargs)
|
||||
array_nodes[str(id(self))] = self
|
||||
|
||||
def update_sockets(self, context):
|
||||
if not self.inputs:
|
||||
return
|
||||
links = {}
|
||||
for socket in self.inputs:
|
||||
for link in socket.links:
|
||||
links.setdefault(socket.name, []).append(
|
||||
(link.from_node, link.from_socket))
|
||||
for socket in self.inputs[1:]:
|
||||
self.inputs.remove(socket)
|
||||
self._add_target_inputs()
|
||||
tree = self.id_data
|
||||
for socket in self.inputs:
|
||||
if socket.name in links:
|
||||
for from_node, from_socket in links[socket.name]:
|
||||
tree.links.new(from_socket, socket)
|
||||
|
||||
def _add_target_inputs(self):
|
||||
if self.property0 in ('add', 'override'):
|
||||
self.add_input('LnxDynamicSocket', 'Connection')
|
||||
self.add_input('LnxDynamicSocket', 'Torrent')
|
||||
self.add_input('LnxNodeSocketArray', 'Trackers')
|
||||
else:
|
||||
self.add_input('LnxDynamicSocket', 'Tracker')
|
||||
socket_type, label = self.tracker_props.get(
|
||||
self.property0, self.tracker_props['interval'])
|
||||
self.add_input(socket_type, label)
|
||||
|
||||
property0: HaxeEnumProperty('property0',
|
||||
items=[('add', 'Add', 'Merge the Trackers input on top of the existing announce list'),
|
||||
('override', 'Override', 'Replace the announce list entirely; running tracker connections are restarted'),
|
||||
('interval', 'Interval', 'Announce interval in seconds'),
|
||||
('port', 'Port', 'Listening port reported to the tracker'),
|
||||
('key', 'Key', 'Session key sent with announces'),
|
||||
('numwant', 'Num Want', 'Number of peers requested per announce'),
|
||||
('trackerId', 'Tracker ID', 'Tracker id echoed back by the tracker')],
|
||||
name='Property', default='add', update=update_sockets)
|
||||
|
||||
def lnx_init(self, context):
|
||||
self.add_input('LnxNodeSocketAction', 'In')
|
||||
self._add_target_inputs()
|
||||
|
||||
self.add_output('LnxNodeSocketAction', 'Out')
|
||||
self.add_output('LnxDynamicSocket', 'Target')
|
||||
|
||||
def draw_buttons(self, context, layout):
|
||||
layout.prop(self, 'property0')
|
||||
|
||||
|
||||
def register():
|
||||
add_category('Leenkx', icon='ORIENTATION_CURSOR')
|
||||
SetTrackersNode.on_register()
|
||||
@ -0,0 +1,31 @@
|
||||
from lnx.logicnode.lnx_nodes import *
|
||||
|
||||
|
||||
class TorrentClientInfoNode(LnxLogicTreeNode):
|
||||
"""Torrent Client Info"""
|
||||
bl_idname = 'LNTorrentClientInfoNode'
|
||||
bl_label = 'Connection Torrents'
|
||||
bl_description = ('Aggregate stats for the torrent client attached')
|
||||
lnx_category = 'Leenkx'
|
||||
lnx_version = 1
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(TorrentClientInfoNode, self).__init__(*args, **kwargs)
|
||||
array_nodes[str(id(self))] = self
|
||||
|
||||
def lnx_init(self, context):
|
||||
self.add_input('LnxNodeSocketAction', 'In')
|
||||
self.add_input('LnxDynamicSocket', 'Connection')
|
||||
|
||||
self.add_output('LnxNodeSocketAction', 'Out')
|
||||
self.add_output('LnxNodeSocketArray', 'Torrents')
|
||||
self.add_output('LnxIntSocket', 'Count')
|
||||
self.add_output('LnxFloatSocket', 'Down Rate')
|
||||
self.add_output('LnxFloatSocket', 'Up Rate')
|
||||
self.add_output('LnxFloatSocket', 'Progress')
|
||||
self.add_output('LnxFloatSocket', 'Ratio')
|
||||
|
||||
|
||||
def register():
|
||||
add_category('Leenkx', icon='ORIENTATION_CURSOR')
|
||||
TorrentClientInfoNode.on_register()
|
||||
71
leenkx/blender/lnx/logicnode/custom/LN_torrent_control.py
Normal file
71
leenkx/blender/lnx/logicnode/custom/LN_torrent_control.py
Normal file
@ -0,0 +1,71 @@
|
||||
from lnx.logicnode.lnx_nodes import *
|
||||
|
||||
|
||||
class TorrentControlNode(LnxLogicTreeNode):
|
||||
"""Torrent Control"""
|
||||
bl_idname = 'LNTorrentControlNode'
|
||||
bl_label = 'Torrent Control'
|
||||
bl_description = ('Start, stop, remove or prioritize a torrent.')
|
||||
lnx_category = 'Leenkx'
|
||||
lnx_version = 1
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(TorrentControlNode, self).__init__(*args, **kwargs)
|
||||
array_nodes[str(id(self))] = self
|
||||
|
||||
property0: HaxeEnumProperty('property0',
|
||||
items=[('start', 'Start', 'Start or resume the torrent session'),
|
||||
('stop', 'Stop', 'Pause the torrent (announces stopped to trackers); it stays registered and can be started again'),
|
||||
('remove', 'Remove', 'Destroy the torrent and remove it from the client'),
|
||||
('remove-data', 'Remove + Data', 'Destroy the torrent and delete the stored content bytes'),
|
||||
('select', 'Select', 'Prioritize a piece range: File handle or Start/End bytes, Priority optional (webtorrent torrent.select)'),
|
||||
('deselect', 'Deselect', 'Drop a priority selection, back to normal order'),
|
||||
('critical', 'Critical', 'Top-priority selection for streaming seeks'),
|
||||
('add-peer', 'Add Peer', 'Connect to a peer directly; Address is host:port (sys/Krom targets only)'),
|
||||
('add-web-seed', 'Add Web Seed', 'Register a BEP 19 webseed; Address is an http(s) URL, fills pieces when peers can\'t serve them')],
|
||||
name='Action', default='start',
|
||||
update=lambda self, context: self._rebuild_inputs())
|
||||
|
||||
def _rebuild_inputs(self):
|
||||
if not self.inputs:
|
||||
return
|
||||
links = {}
|
||||
for socket in self.inputs:
|
||||
for link in socket.links:
|
||||
links.setdefault(socket.name, []).append(
|
||||
(link.from_node, link.from_socket))
|
||||
for socket in self.inputs[3:]:
|
||||
self.inputs.remove(socket)
|
||||
self._add_action_inputs()
|
||||
tree = self.id_data
|
||||
for socket in self.inputs:
|
||||
if socket.name in links:
|
||||
for from_node, from_socket in links[socket.name]:
|
||||
tree.links.new(from_socket, socket)
|
||||
|
||||
def _add_action_inputs(self):
|
||||
if self.property0 in ('select', 'deselect', 'critical'):
|
||||
self.add_input('LnxDynamicSocket', 'File')
|
||||
self.add_input('LnxIntSocket', 'Start')
|
||||
self.add_input('LnxIntSocket', 'End')
|
||||
self.add_input('LnxIntSocket', 'Priority')
|
||||
elif self.property0 == 'add-peer':
|
||||
self.add_input('LnxStringSocket', 'Address')
|
||||
elif self.property0 == 'add-web-seed':
|
||||
self.add_input('LnxStringSocket', 'Address')
|
||||
|
||||
def lnx_init(self, context):
|
||||
self.add_input('LnxNodeSocketAction', 'In')
|
||||
self.add_input('LnxDynamicSocket', 'Connection')
|
||||
self.add_input('LnxDynamicSocket', 'Torrent')
|
||||
self._add_action_inputs()
|
||||
|
||||
self.add_output('LnxNodeSocketAction', 'Out')
|
||||
|
||||
def draw_buttons(self, context, layout):
|
||||
layout.prop(self, 'property0')
|
||||
|
||||
|
||||
def register():
|
||||
add_category('Leenkx', icon='ORIENTATION_CURSOR')
|
||||
TorrentControlNode.on_register()
|
||||
40
leenkx/blender/lnx/logicnode/custom/LN_torrent_event.py
Normal file
40
leenkx/blender/lnx/logicnode/custom/LN_torrent_event.py
Normal file
@ -0,0 +1,40 @@
|
||||
from lnx.logicnode.lnx_nodes import *
|
||||
|
||||
|
||||
class OnTorrentEventNode(LnxLogicTreeNode):
|
||||
"""On Torrent Event"""
|
||||
bl_idname = 'LNOnTorrentEventNode'
|
||||
bl_label = 'On Torrent Event'
|
||||
bl_description = ('Fires when the selected torrent event happens ')
|
||||
lnx_category = 'Leenkx'
|
||||
lnx_version = 1
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(OnTorrentEventNode, self).__init__(*args, **kwargs)
|
||||
array_nodes[str(id(self))] = self
|
||||
|
||||
property0: HaxeEnumProperty('property0',
|
||||
items=[('ready', 'Ready', 'Metainfo is known; fires immediately if already ready'),
|
||||
('piece', 'Piece', 'A piece finished downloading; Value is the piece index'),
|
||||
('wire', 'Wire', 'A peer connected; Value is the WireProtocol'),
|
||||
('metadata', 'Metadata', 'ut_metadata fetch completed; Value is the MetaInfo'),
|
||||
('done', 'Done', 'All pieces are downloaded'),
|
||||
('error', 'Error', 'Torrent failed; Value is the error string')],
|
||||
name='Event', default='done')
|
||||
|
||||
def lnx_init(self, context):
|
||||
self.add_input('LnxNodeSocketAction', 'In')
|
||||
self.add_input('LnxDynamicSocket', 'Connection')
|
||||
self.add_input('LnxDynamicSocket', 'Torrent')
|
||||
|
||||
self.add_output('LnxNodeSocketAction', 'Out')
|
||||
self.add_output('LnxDynamicSocket', 'Torrent')
|
||||
self.add_output('LnxDynamicSocket', 'Value')
|
||||
|
||||
def draw_buttons(self, context, layout):
|
||||
layout.prop(self, 'property0')
|
||||
|
||||
|
||||
def register():
|
||||
add_category('Leenkx', icon='ORIENTATION_CURSOR')
|
||||
OnTorrentEventNode.on_register()
|
||||
46
leenkx/blender/lnx/logicnode/custom/LN_torrent_info.py
Normal file
46
leenkx/blender/lnx/logicnode/custom/LN_torrent_info.py
Normal file
@ -0,0 +1,46 @@
|
||||
from lnx.logicnode.lnx_nodes import *
|
||||
|
||||
|
||||
class TorrentInfoNode(LnxLogicTreeNode):
|
||||
"""Torrent Info"""
|
||||
bl_idname = 'LNTorrentInfoNode'
|
||||
bl_label = 'Torrent Info'
|
||||
bl_description = ('Read the state of a torrent: infohash, name, peer count, progress, transfer totals, magnet URI')
|
||||
lnx_category = 'Leenkx'
|
||||
lnx_version = 1
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(TorrentInfoNode, self).__init__(*args, **kwargs)
|
||||
array_nodes[str(id(self))] = self
|
||||
|
||||
def lnx_init(self, context):
|
||||
self.add_input('LnxNodeSocketAction', 'In')
|
||||
self.add_input('LnxDynamicSocket', 'Connection')
|
||||
self.add_input('LnxDynamicSocket', 'Torrent')
|
||||
|
||||
self.add_output('LnxNodeSocketAction', 'Out')
|
||||
self.add_output('LnxStringSocket', 'InfoHash')
|
||||
self.add_output('LnxStringSocket', 'Name')
|
||||
self.add_output('LnxIntSocket', 'Peers')
|
||||
self.add_output('LnxFloatSocket', 'Progress')
|
||||
self.add_output('LnxFloatSocket', 'Downloaded')
|
||||
self.add_output('LnxFloatSocket', 'Uploaded')
|
||||
self.add_output('LnxBoolSocket', 'Done')
|
||||
self.add_output('LnxStringSocket', 'Magnet')
|
||||
self.add_output('LnxNodeSocketArray', 'Files')
|
||||
self.add_output('LnxIntSocket', 'Num Pieces')
|
||||
self.add_output('LnxIntSocket', 'Piece Length')
|
||||
self.add_output('LnxFloatSocket', 'Total Size')
|
||||
self.add_output('LnxStringSocket', 'Comment')
|
||||
self.add_output('LnxStringSocket', 'Created By')
|
||||
self.add_output('LnxIntSocket', 'Version')
|
||||
self.add_output('LnxStringSocket', 'InfoHash2')
|
||||
self.add_output('LnxFloatSocket', 'Down Rate')
|
||||
self.add_output('LnxFloatSocket', 'Up Rate')
|
||||
self.add_output('LnxBoolSocket', 'Stopped')
|
||||
self.add_output('LnxDynamicSocket', 'Torrent')
|
||||
|
||||
|
||||
def register():
|
||||
add_category('Leenkx', icon='ORIENTATION_CURSOR')
|
||||
TorrentInfoNode.on_register()
|
||||
@ -248,6 +248,7 @@ def export_data_impl(fp, sdk_path):
|
||||
export_navigation = bpy.data.worlds['Lnx'].lnx_navigation != 'Disabled'
|
||||
export_ui = bpy.data.worlds['Lnx'].lnx_ui != 'Disabled'
|
||||
export_network = bpy.data.worlds['Lnx'].lnx_network != 'Disabled'
|
||||
export_torrent = bpy.data.worlds['Lnx'].lnx_torrent != 'Disabled'
|
||||
|
||||
assets.reset()
|
||||
mat_state.material_cache.clear()
|
||||
@ -264,6 +265,7 @@ def export_data_impl(fp, sdk_path):
|
||||
navigation_found = False
|
||||
ui_found = False
|
||||
network_found = False
|
||||
torrent_found = False
|
||||
LeenkxExporter.compress_enabled = wrd.lnx_asset_compression
|
||||
LeenkxExporter.optimize_enabled = state.is_publish and wrd.lnx_optimize_data
|
||||
if not os.path.exists(build_dir + '/compiled/Assets'):
|
||||
@ -326,6 +328,8 @@ def export_data_impl(fp, sdk_path):
|
||||
ui_found = True
|
||||
if LeenkxExporter.export_network:
|
||||
network_found = True
|
||||
if LeenkxExporter.export_torrent:
|
||||
torrent_found = True
|
||||
assets.add(asset_path)
|
||||
|
||||
if physics_found is False: # Disable physics if no rigid body is exported
|
||||
@ -340,6 +344,9 @@ def export_data_impl(fp, sdk_path):
|
||||
if network_found == False:
|
||||
export_network = False
|
||||
|
||||
if torrent_found == False:
|
||||
export_torrent = False
|
||||
|
||||
# Ugly workaround: some logic nodes require Zui code even if no UI is used,
|
||||
# for now enable UI export unless explicitly disabled.
|
||||
export_ui = True
|
||||
@ -349,6 +356,9 @@ def export_data_impl(fp, sdk_path):
|
||||
if wrd.lnx_network == 'Enabled':
|
||||
export_network = True
|
||||
|
||||
if wrd.lnx_torrent == 'Enabled':
|
||||
export_torrent = True
|
||||
|
||||
modules = []
|
||||
if wrd.lnx_audio == 'Enabled':
|
||||
modules.append('audio')
|
||||
@ -360,6 +370,8 @@ def export_data_impl(fp, sdk_path):
|
||||
modules.append('ui')
|
||||
if export_network:
|
||||
modules.append('network')
|
||||
if export_torrent:
|
||||
modules.append('torrent')
|
||||
|
||||
defs = lnx.utils.def_strings_to_array(wrd.world_defs)
|
||||
cdefs = lnx.utils.def_strings_to_array(wrd.compo_defs)
|
||||
@ -451,7 +463,7 @@ def export_data_impl(fp, sdk_path):
|
||||
wrd.lnx_project_version = lnx.utils.change_version_project(wrd.lnx_project_version)
|
||||
|
||||
# Write khafile.js
|
||||
write_data.write_khafilejs(state.is_play, export_physics, export_navigation, export_ui, export_network, state.is_publish, LeenkxExporter.import_traits)
|
||||
write_data.write_khafilejs(state.is_play, export_physics, export_navigation, export_ui, export_network, export_torrent, state.is_publish, LeenkxExporter.import_traits)
|
||||
|
||||
# Write Main.hx - depends on write_khafilejs for writing number of assets
|
||||
scene_name = lnx.utils.get_project_scene_name()
|
||||
|
||||
@ -34,6 +34,16 @@ else:
|
||||
lnx_version = '2026.9'
|
||||
lnx_commit = '$Id: 6b2644d47db169cedd95593497cc283207d23a74 $'
|
||||
|
||||
LNX_TORRENT_DEFAULT_TRACKERS = [
|
||||
'wss://ws1.leenkx.com',
|
||||
'wss://video.blender.org/tracker/socket',
|
||||
]
|
||||
|
||||
LNX_TORRENT_DEFAULT_ICE_SERVERS = [
|
||||
'stun:stun.l.google.com:19302',
|
||||
'stun:global.stun.twilio.com:3478',
|
||||
]
|
||||
|
||||
def get_project_html5_copy(self):
|
||||
return self.get('lnx_project_html5_copy', False)
|
||||
|
||||
@ -282,6 +292,17 @@ def init_properties():
|
||||
('Enabled', 'Enabled', 'Enabled'),
|
||||
('Auto', 'Auto', 'Auto')],
|
||||
name="Networking", default='Auto', description="Include Network library", update=assets.invalidate_compiler_cache)
|
||||
bpy.types.World.lnx_torrent = EnumProperty(
|
||||
items=[('Disabled', 'Disabled', 'Disabled'),
|
||||
('Enabled', 'Enabled', 'Enabled')],
|
||||
name="Torrent", default='Disabled', description="Include Torrent (Leenkx) library", update=assets.invalidate_compiler_cache)
|
||||
bpy.types.World.lnx_torrent_download_dir = StringProperty(name="Download Path", description="Directory torrent downloads are written to. Relative paths resolve against the project directory at runtime; absolute paths are used as-is", default="Downloads", subtype='DIR_PATH', update=assets.invalidate_compiler_cache)
|
||||
bpy.types.World.lnx_torrent_trackers = CollectionProperty(name="Torrent Trackers", description="Default torrent tracker announce URLs", type=bpy.types.PropertyGroup)
|
||||
bpy.types.World.lnx_torrent_trackers_index = IntProperty(name="Torrent Tracker Index", default=0)
|
||||
bpy.types.World.lnx_torrent_ice_servers = CollectionProperty(name="ICE Servers", description="Default ICE/STUN servers used for WebRTC peer connections", type=bpy.types.PropertyGroup)
|
||||
bpy.types.World.lnx_torrent_ice_servers_index = IntProperty(name="ICE Server Index", default=0)
|
||||
bpy.types.World.lnx_torrent_peer_timeout = IntProperty(name="Peer Timeout", description="Milliseconds after which an unresponsive peer is dropped", default=5 * 60 * 1000, min=0, update=assets.invalidate_compiler_cache)
|
||||
bpy.types.World.lnx_torrent_heartbeat = IntProperty(name="Heartbeat", description="Milliseconds between heartbeat packets sent to peers", default=30000, min=0, update=assets.invalidate_compiler_cache)
|
||||
bpy.types.World.lnx_audio = EnumProperty(
|
||||
items=[('Disabled', 'Disabled', 'Disabled'),
|
||||
('Enabled', 'Enabled', 'Enabled')],
|
||||
@ -701,13 +722,23 @@ def create_wrd():
|
||||
wrd.use_fake_user = True # Store data world object, add fake user to keep it alive
|
||||
wrd.lnx_version = lnx_version
|
||||
wrd.lnx_commit = lnx_commit
|
||||
seed_torrent_trackers(wrd)
|
||||
|
||||
def seed_torrent_trackers(wrd):
|
||||
if len(wrd.lnx_torrent_trackers) == 0:
|
||||
for url in LNX_TORRENT_DEFAULT_TRACKERS:
|
||||
wrd.lnx_torrent_trackers.add().name = url
|
||||
if len(wrd.lnx_torrent_ice_servers) == 0:
|
||||
for url in LNX_TORRENT_DEFAULT_ICE_SERVERS:
|
||||
wrd.lnx_torrent_ice_servers.add().name = url
|
||||
|
||||
def init_properties_on_load():
|
||||
if 'Lnx' not in bpy.data.worlds:
|
||||
init_properties()
|
||||
wrd = bpy.data.worlds['Lnx']
|
||||
seed_torrent_trackers(wrd)
|
||||
# New project?
|
||||
if bpy.data.filepath == '':
|
||||
wrd = bpy.data.worlds['Lnx']
|
||||
wrd.lnx_debug_console = lnx.utils.get_debug_console_auto()
|
||||
lnx.utils.fetch_script_names()
|
||||
|
||||
|
||||
@ -1358,11 +1358,150 @@ class LNX_PT_ProjectModulesPanel(bpy.types.Panel):
|
||||
layout.prop(wrd, 'lnx_navigation_engine')
|
||||
layout.prop(wrd, 'lnx_ui')
|
||||
layout.prop(wrd, 'lnx_network')
|
||||
layout.prop(wrd, 'lnx_torrent')
|
||||
|
||||
layout.prop_search(wrd, 'lnx_khafile', bpy.data, 'texts')
|
||||
layout.prop(wrd, 'lnx_project_root')
|
||||
layout.prop(wrd, 'lnx_external_blends_path')
|
||||
|
||||
class LNX_UL_TorrentTrackerList(bpy.types.UIList):
|
||||
def draw_item(self, context, layout, data, item, icon, active_data, active_propname, index):
|
||||
layout.prop(item, "name", text="", emboss=False, icon='NETWORK_DRIVE')
|
||||
|
||||
class LnxTorrentTrackerListNewItem(bpy.types.Operator):
|
||||
bl_idname = "lnx_torrent_tracker_list.new_item"
|
||||
bl_label = "Add a new tracker"
|
||||
|
||||
def execute(self, context):
|
||||
wrd = bpy.data.worlds['Lnx']
|
||||
wrd.lnx_torrent_trackers.add()
|
||||
wrd.lnx_torrent_trackers_index = len(wrd.lnx_torrent_trackers) - 1
|
||||
assets.invalidate_compiler_cache(wrd, context)
|
||||
return{'FINISHED'}
|
||||
|
||||
class LnxTorrentTrackerListDeleteItem(bpy.types.Operator):
|
||||
bl_idname = "lnx_torrent_tracker_list.delete_item"
|
||||
bl_label = "Deletes a tracker"
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
wrd = bpy.data.worlds['Lnx']
|
||||
return len(wrd.lnx_torrent_trackers) > 0
|
||||
|
||||
def execute(self, context):
|
||||
wrd = bpy.data.worlds['Lnx']
|
||||
index = wrd.lnx_torrent_trackers_index
|
||||
|
||||
wrd.lnx_torrent_trackers.remove(index)
|
||||
wrd.lnx_torrent_trackers_index = min(max(0, index - 1), len(wrd.lnx_torrent_trackers) - 1)
|
||||
assets.invalidate_compiler_cache(wrd, context)
|
||||
return{'FINISHED'}
|
||||
|
||||
class LnxTorrentTrackerListReset(bpy.types.Operator):
|
||||
bl_idname = "lnx_torrent_tracker_list.reset_items"
|
||||
bl_label = "Reset To Default Trackers"
|
||||
bl_description = "Remove all trackers and add back the default ones"
|
||||
|
||||
def execute(self, context):
|
||||
wrd = bpy.data.worlds['Lnx']
|
||||
wrd.lnx_torrent_trackers.clear()
|
||||
for url in props.LNX_TORRENT_DEFAULT_TRACKERS:
|
||||
wrd.lnx_torrent_trackers.add().name = url
|
||||
wrd.lnx_torrent_trackers_index = 0
|
||||
assets.invalidate_compiler_cache(wrd, context)
|
||||
return{'FINISHED'}
|
||||
|
||||
class LNX_UL_TorrentIceServerList(bpy.types.UIList):
|
||||
def draw_item(self, context, layout, data, item, icon, active_data, active_propname, index):
|
||||
layout.prop(item, "name", text="", emboss=False, icon='URL')
|
||||
|
||||
class LnxTorrentIceServerListNewItem(bpy.types.Operator):
|
||||
bl_idname = "lnx_torrent_ice_server_list.new_item"
|
||||
bl_label = "Add a new ICE server"
|
||||
|
||||
def execute(self, context):
|
||||
wrd = bpy.data.worlds['Lnx']
|
||||
wrd.lnx_torrent_ice_servers.add()
|
||||
wrd.lnx_torrent_ice_servers_index = len(wrd.lnx_torrent_ice_servers) - 1
|
||||
assets.invalidate_compiler_cache(wrd, context)
|
||||
return{'FINISHED'}
|
||||
|
||||
class LnxTorrentIceServerListDeleteItem(bpy.types.Operator):
|
||||
bl_idname = "lnx_torrent_ice_server_list.delete_item"
|
||||
bl_label = "Deletes an ICE server"
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
wrd = bpy.data.worlds['Lnx']
|
||||
return len(wrd.lnx_torrent_ice_servers) > 0
|
||||
|
||||
def execute(self, context):
|
||||
wrd = bpy.data.worlds['Lnx']
|
||||
index = wrd.lnx_torrent_ice_servers_index
|
||||
|
||||
wrd.lnx_torrent_ice_servers.remove(index)
|
||||
wrd.lnx_torrent_ice_servers_index = min(max(0, index - 1), len(wrd.lnx_torrent_ice_servers) - 1)
|
||||
assets.invalidate_compiler_cache(wrd, context)
|
||||
return{'FINISHED'}
|
||||
|
||||
class LnxTorrentIceServerListReset(bpy.types.Operator):
|
||||
bl_idname = "lnx_torrent_ice_server_list.reset_items"
|
||||
bl_label = "Reset To Default ICE Servers"
|
||||
bl_description = "Remove all ICE servers and add back the default ones"
|
||||
|
||||
def execute(self, context):
|
||||
wrd = bpy.data.worlds['Lnx']
|
||||
wrd.lnx_torrent_ice_servers.clear()
|
||||
for url in props.LNX_TORRENT_DEFAULT_ICE_SERVERS:
|
||||
wrd.lnx_torrent_ice_servers.add().name = url
|
||||
wrd.lnx_torrent_ice_servers_index = 0
|
||||
assets.invalidate_compiler_cache(wrd, context)
|
||||
return{'FINISHED'}
|
||||
|
||||
class LNX_PT_LeenkxNetworkPanel(bpy.types.Panel):
|
||||
bl_label = "Leenkx Network"
|
||||
bl_space_type = "PROPERTIES"
|
||||
bl_region_type = "WINDOW"
|
||||
bl_context = "render"
|
||||
bl_options = {'DEFAULT_CLOSED'}
|
||||
|
||||
def draw(self, context):
|
||||
layout = self.layout
|
||||
layout.use_property_split = True
|
||||
layout.use_property_decorate = False
|
||||
wrd = bpy.data.worlds['Lnx']
|
||||
|
||||
col = layout.column()
|
||||
col.enabled = wrd.lnx_torrent != 'Disabled'
|
||||
|
||||
col.prop(wrd, "lnx_torrent_download_dir")
|
||||
col.prop(wrd, "lnx_torrent_peer_timeout")
|
||||
col.prop(wrd, "lnx_torrent_heartbeat")
|
||||
|
||||
col.label(text="Trackers")
|
||||
rows = 2
|
||||
if len(wrd.lnx_torrent_trackers) > 1:
|
||||
rows = 4
|
||||
row = col.row()
|
||||
row.template_list("LNX_UL_TorrentTrackerList", "The_List", wrd, "lnx_torrent_trackers", wrd, "lnx_torrent_trackers_index", rows=rows)
|
||||
btns = row.column(align=True)
|
||||
btns.operator("lnx_torrent_tracker_list.new_item", icon='ADD', text="")
|
||||
btns.operator("lnx_torrent_tracker_list.delete_item", icon='REMOVE', text="")
|
||||
|
||||
col.operator("lnx_torrent_tracker_list.reset_items", icon='FILE_REFRESH')
|
||||
|
||||
col.label(text="ICE Servers")
|
||||
rows = 2
|
||||
if len(wrd.lnx_torrent_ice_servers) > 1:
|
||||
rows = 4
|
||||
row = col.row()
|
||||
row.template_list("LNX_UL_TorrentIceServerList", "The_List", wrd, "lnx_torrent_ice_servers", wrd, "lnx_torrent_ice_servers_index", rows=rows)
|
||||
btns = row.column(align=True)
|
||||
btns.operator("lnx_torrent_ice_server_list.new_item", icon='ADD', text="")
|
||||
btns.operator("lnx_torrent_ice_server_list.delete_item", icon='REMOVE', text="")
|
||||
|
||||
col.operator("lnx_torrent_ice_server_list.reset_items", icon='FILE_REFRESH')
|
||||
|
||||
class LnxVirtualInputPanel(bpy.types.Panel):
|
||||
bl_label = "Leenkx Virtual Input"
|
||||
bl_space_type = "PROPERTIES"
|
||||
@ -3107,6 +3246,15 @@ __REG_CLASSES = (
|
||||
LNX_PT_ProjectFlagsDebugConsolePanel,
|
||||
LNX_PT_ProjectWindowPanel,
|
||||
LNX_PT_ProjectModulesPanel,
|
||||
LNX_UL_TorrentTrackerList,
|
||||
LnxTorrentTrackerListNewItem,
|
||||
LnxTorrentTrackerListDeleteItem,
|
||||
LnxTorrentTrackerListReset,
|
||||
LNX_UL_TorrentIceServerList,
|
||||
LnxTorrentIceServerListNewItem,
|
||||
LnxTorrentIceServerListDeleteItem,
|
||||
LnxTorrentIceServerListReset,
|
||||
LNX_PT_LeenkxNetworkPanel,
|
||||
LNX_PT_RenderPathPanel,
|
||||
LNX_PT_RenderPathRendererPanel,
|
||||
LNX_PT_RenderPathShadowsPanel,
|
||||
|
||||
@ -158,7 +158,7 @@ def remove_readonly(func, path, excinfo):
|
||||
func(path)
|
||||
|
||||
|
||||
def write_khafilejs(is_play, export_physics: bool, export_navigation: bool, export_ui: bool, export_network: bool, is_publish: bool,
|
||||
def write_khafilejs(is_play, export_physics: bool, export_navigation: bool, export_ui: bool, export_network: bool, export_torrent: bool, is_publish: bool,
|
||||
import_traits: List[str]) -> None:
|
||||
wrd = bpy.data.worlds['Lnx']
|
||||
rpdat = lnx.utils.get_rp()
|
||||
@ -482,9 +482,16 @@ let project = new Project('""" + lnx.utils.safesrc(wrd.lnx_project_name + '-' +
|
||||
if not os.path.exists('Libraries/network'):
|
||||
khafile.write(add_leenkx_library(sdk_path, 'lib/network', rel_path=do_relpath_sdk))
|
||||
assets.add_khafile_def('lnx_network')
|
||||
lnxjs_path = sdk_path + '/lib/leenkx_tools/lnxjs/Leenkx.js'
|
||||
lnxjs_path = lnxjs_path.replace('\\', '/').replace('//', '/')
|
||||
khafile.write(add_assets(lnxjs_path, rel_path=do_relpath_sdk))
|
||||
#lnxjs_path = sdk_path + '/lib/leenkx_tools/lnxjs/Leenkx.js'
|
||||
#lnxjs_path = lnxjs_path.replace('\\', '/').replace('//', '/')
|
||||
#khafile.write(add_assets(lnxjs_path, rel_path=do_relpath_sdk))
|
||||
|
||||
if export_torrent:
|
||||
assets.add_khafile_def('lnx_torrent')
|
||||
if state.target in ('windows-hl', 'linux-hl', 'macos-hl'):
|
||||
if not os.path.exists('Libraries/datachannel'):
|
||||
khafile.write(add_leenkx_library(sdk_path + '/lib/', 'datachannel', rel_path=do_relpath_sdk))
|
||||
assets.add_khafile_def('lnx_datachannel')
|
||||
|
||||
if not wrd.lnx_minimize:
|
||||
assets.add_khafile_def('lnx_json')
|
||||
@ -736,6 +743,19 @@ class Main {
|
||||
elif wrd.lnx_canvas_img_scaling_quality == 'high':
|
||||
f.write("\n" + content_indent + "leenkx.ui.Canvas.imageScaleQuality = kha.graphics2.ImageScaleQuality.High;")
|
||||
|
||||
if wrd.lnx_torrent != 'Disabled':
|
||||
dl_dir = wrd.lnx_torrent_download_dir
|
||||
if dl_dir.startswith('//'):
|
||||
dl_dir = bpy.path.abspath(dl_dir)
|
||||
if dl_dir != '':
|
||||
f.write("\n" + content_indent + "leenkx.network.torrent.TorrentClient.defaultDownloadDir = " + json.dumps(dl_dir) + ";")
|
||||
trackers_list = [json.dumps(t.name) for t in wrd.lnx_torrent_trackers if t.name != '']
|
||||
f.write("\n" + content_indent + "leenkx.network.torrent.TorrentClient.defaultTrackers = [" + ", ".join(trackers_list) + "];")
|
||||
ice_list = [json.dumps(t.name) for t in wrd.lnx_torrent_ice_servers if t.name != '']
|
||||
f.write("\n" + content_indent + "leenkx.network.torrent.peer.RtcPeerPool.defaultIceServers = [" + ", ".join(ice_list) + "];")
|
||||
f.write("\n" + content_indent + "leenkx.network.torrent.LeenkxClient.PEERTIMEOUT = " + str(wrd.lnx_torrent_peer_timeout) + ";")
|
||||
f.write("\n" + content_indent + "leenkx.network.torrent.LeenkxClient.defaultHeartbeat = " + str(wrd.lnx_torrent_heartbeat) + ";")
|
||||
|
||||
# Write Starter.main call
|
||||
starter_indent = content_indent + " "
|
||||
f.write("\n" + content_indent + "leenkx.system.Starter.main(")
|
||||
|
||||
Reference in New Issue
Block a user