Created
May 2, 2013 19:33
-
-
Save 02strich/5504767 to your computer and use it in GitHub Desktop.
DLNA Proxy (including SSDP and Multicast)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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,)) |
Author
Author
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).
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
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).