P2P Export - Nodes - Patch

This commit is contained in:
2026-10-06 02:10:08 -07:00
parent 9fee67d231
commit 4ab646015d
49 changed files with 2478 additions and 1164 deletions

View File

@ -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()

View File

@ -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')

View File

@ -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,17 +13,67 @@ 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():
add_category('Leenkx', icon='ORIENTATION_CURSOR')
CreateTorrentNode.on_register()
CreateTorrentNode.on_register()

View 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()

View 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()

View 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()

View File

@ -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
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)
@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')
self.add_input('LnxStringSocket', 'ID')
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')

View File

@ -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():

View File

@ -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):

View File

@ -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 _add_id_input(self):
self.add_input('LnxStringSocket', 'ID')
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
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)
#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')
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')
# 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')

View 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()

View File

@ -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():

View 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()

View File

@ -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()

View 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()

View 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()

View 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()