-
-
Save 02strich/5504767 to your computer and use it in GitHub Desktop.
import socket, thread, sys | |
import logging | |
BUFLEN = 1024 | |
logger = logging.getLogger(__name__) | |
class DLNAProxy(object): | |
MEDIASERVER_IP = "" | |
def __init__(self, connection, address, timeout): | |
try: | |
logger.debug("new connection handler") | |
self.client = connection | |
self.client_buffer = '' | |
self.timeout = timeout | |
# connect to target server | |
(soc_family, _, _, _, address) = socket.getaddrinfo(self.MEDIASERVER_IP, 5001)[0] | |
self.target = socket.socket(soc_family) | |
self.target.connect(address) | |
# get request | |
request = "".join([part[1] for part in self.read_response(self.client)]) | |
request2 = request.replace("192.168.178.001", self.MEDIASERVER_IP) | |
logger.debug("--------------------------------------------") | |
logger.debug(request) | |
logger.debug("--------------------------------------------") | |
# send request to target server | |
self.target.send(request2) | |
# read response | |
for text_content, response_part in self.read_response(self.target): | |
if text_content: | |
self.client.send(response_part.replace(self.MEDIASERVER_IP, "192.168.178.001")) | |
else: | |
self.client.send(response_part) | |
self.client.close() | |
self.target.close() | |
except: | |
logger.exception("Something went wrong") | |
def read_response(self, read_socket): | |
buffer = "" | |
socket_file = read_socket.makefile() | |
content_length = 0 | |
text_content = False | |
while 1: | |
line = socket_file.readline() | |
buffer += line | |
if "Content-Length" in line: | |
content_length = int(line.split()[1]) | |
if "Content-Type" in line: | |
text_content = "text/xml" in line | |
if line == "\r\n": | |
break | |
logger.debug("Read header: %d", content_length) | |
yield True, buffer | |
if content_length > 1024*1024: | |
data_part = socket_file.read(8*1024) | |
while data_part: | |
yield text_content, data_part | |
data_part = socket_file.read(8*1024) | |
else: | |
yield text_content, socket_file.read(content_length) | |
class DLNAMulticastHelper(object): | |
MCAST_GRP = '239.255.255.250' | |
MCAST_PORT = 1900 | |
def __init__(self, mediaserver_ip): | |
self.mediaserver_ip = mediaserver_ip | |
def run(self): | |
sock_send = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, socket.IPPROTO_UDP) | |
sock_send.setsockopt(socket.IPPROTO_IP, socket.IP_MULTICAST_TTL, 2) | |
sock_send.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) | |
sock_send.setsockopt(socket.IPPROTO_IP, socket.IP_MULTICAST_IF, socket.inet_aton("192.168.178.1")) | |
sock_recv = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, socket.IPPROTO_UDP) | |
sock_recv.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) | |
sock_recv.setsockopt(socket.SOL_IP, socket.IP_ADD_MEMBERSHIP, socket.inet_aton(self.MCAST_GRP)+socket.inet_aton("192.168.179.1")) | |
sock_recv.bind((self.MCAST_GRP, self.MCAST_PORT)) | |
while True: | |
dgram, addr = sock_recv.recvfrom(10240) | |
dgram2 = dgram.replace(self.mediaserver_ip, "192.168.178.1") | |
sock_send.sendto(dgram2, (self.MCAST_GRP, self.MCAST_PORT)) | |
if __name__ == '__main__': | |
logging.basicConfig() | |
# start multicast helper | |
multi_helper = DLNAMulticastHelper(sys.argv[1]) | |
thread.start_new_thread(multi_helper.run, tuple()) | |
# start HTTP proxy | |
DLNAProxy.MEDIASERVER_IP = sys.argv[1] | |
soc = socket.socket(socket.AF_INET) | |
soc.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) | |
soc.bind(("192.168.178.1", 5001)) | |
soc.listen(0) | |
while 1: | |
thread.start_new_thread(DLNAProxy, soc.accept()+(60,)) |
Anybody got the script to work ?
What is the local IP, and where is the IP of the "remote" DLNA server ?
With python3 it fails on:
dgram2 = dgram.replace(self.mediaserver_ip, "aaa.bbb.ccc.ddd")
TypeError: a bytes-like object is required, not 'str'
At first glance, I see that you hard-coded the IP 192.168.* at multiple locations. You could probably do a DRY refactoring here?
yeah probably, but the IP isn't changing in my setup so doesn't really help me
Can you explain a little here please, it's really interesting your code :D
The code binds to the local multicast interface that DLNA uses to communicate and proxies any requests towards the real mediaserver (using a unicast connection). In the response it replaces the media server IPs with the local ones (and visa-versa on the first leg).
Anybody got the script to work ? What is the local IP, and where is the IP of the "remote" DLNA server ? With python3 it fails on:
dgram2 = dgram.replace(self.mediaserver_ip, "aaa.bbb.ccc.ddd") TypeError: a bytes-like object is required, not 'str'
Assuming you have a different home network layout, you need to replace all references to 192.168.17[89].1 to IPs that fit your setup (those are the IPs used by the proxy not the server). Then you should be able to run this with python2 and providing the IP of your mediaserver as argument, e.g. python2 dlna_proxy.py 192.168.200.10
This had been developed on python2, so adjustments are needed to make it work on python3 (as the error you encountered confirms).
Can you explain a little here please, it's really interesting your code :D