text: """
  Support for discovery using GDM (Good Day Mate), multicast protocol by Plex.
  
  # Licensed Apache 2.0
  # From https://github.com/home-assistant/netdisco/netdisco/gdm.py
  
  Inspired by:
    hippojay's plexGDM: https://github.com/hippojay/script.plexbmc.helper/resources/lib/plexgdm.py
    iBaa's PlexConnect: https://github.com/iBaa/PlexConnect/PlexAPI.py
  """
  import socket
  import struct
  
  
  class GDM:
      """Base class to discover GDM services."""
  
      def __init__(self):
          self.entries = []
          self.last_scan = None
  
      def scan(self, scan_for_clients=False):
          """Scan the network."""
          self.update(scan_for_clients)
  
      def all(self, scan_for_clients=False):
          """Return all found entries.
  
          Will scan for entries if not scanned recently.
          """
          self.scan(scan_for_clients)
          return list(self.entries)
  
      def find_by_content_type(self, value):
          """Return a list of entries that match the content_type."""
          self.scan()
          return [entry for entry in self.entries
                  if value in entry['data']['Content_Type']]
  
      def find_by_data(self, values):
          """Return a list of entries that match the search parameters."""
          self.scan()
          return [entry for entry in self.entries
                  if all(item in entry['data'].items()
                         for item in values.items())]
  
      def update(self, scan_for_clients):
          """Scan for new GDM services.
  
          Examples of the dict list assigned to self.entries by this function:
  
              Server:
  
                  [{'data': {
                       'Content-Type': 'plex/media-server',
                       'Host': '53f4b5b6023d41182fe88a99b0e714ba.plex.direct',
                       'Name': 'myfirstplexserver',
                       'Port': '32400',
                       'Resource-Identifier': '646ab0aa8a01c543e94ba975f6fd6efadc36b7',
                       'Updated-At': '1585769946',
                       'Version': '1.18.8.2527-740d4c206',
                  },
                   'from': ('10.10.10.100', 32414)}]
  
              Clients:
  
                  [{'data': {'Content-Type': 'plex/media-player',
                       'Device-Class': 'stb',
                       'Name': 'plexamp',
                       'Port': '36000',
                       'Product': 'Plexamp',
                       'Protocol': 'plex',
                       'Protocol-Capabilities': 'timeline,playback,playqueues,playqueues-creation',
                       'Protocol-Version': '1',
                       'Resource-Identifier': 'b6e57a3f-e0f8-494f-8884-f4b58501467e',
                       'Version': '1.1.0',
                  },
                   'from': ('10.10.10.101', 32412)}]
          """
  
          gdm_msg = 'M-SEARCH * HTTP/1.0'.encode('ascii')
          gdm_timeout = 1
  
          self.entries = []
          known_responses = []
  
          # setup socket for discovery -> multicast message
          sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
          sock.settimeout(gdm_timeout)
  
          # Set the time-to-live for messages for local network
          sock.setsockopt(socket.IPPROTO_IP,
                          socket.IP_MULTICAST_TTL,
                          struct.pack("B", gdm_timeout))
  
          if scan_for_clients:
              # setup socket for broadcast to Plex clients
              sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
              sock.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1)
              gdm_ip = '255.255.255.255'
              gdm_port = 32412
          else:
              # setup socket for multicast to Plex server(s)
              gdm_ip = '239.0.0.250'
              gdm_port = 32414
  
          try:
              # Send data to the multicast group
              sock.sendto(gdm_msg, (gdm_ip, gdm_port))
  
              # Look for responses from all recipients
              while True:
                  try:
                      bdata, host = sock.recvfrom(1024)
                      data = bdata.decode('utf-8')
                      if '200 OK' in data.splitlines()[0]:
                          ddata = {k: v.strip() for (k, v) in (
                              line.split(':') for line in
                              data.splitlines() if ':' in line)}
                          identifier = ddata.get('Resource-Identifier')
                          if identifier and identifier in known_responses:
                              continue
                          known_responses.append(identifier)
                          self.entries.append({'data': ddata,
                                               'from': host})
                  except socket.timeout:
                      break
          finally:
              sock.close()
  
  
  def main():
      """Test GDM discovery."""
      from pprint import pprint
  
      gdm = GDM()
  
      pprint("Scanning GDM for servers...")
      gdm.scan()
      pprint(gdm.entries)
  
      pprint("Scanning GDM for clients...")
      gdm.scan(scan_for_clients=True)
      pprint(gdm.entries)
  
  
  if __name__ == "__main__":
      main()
  