示例#1
0
class PlsPlaylistDecoder:
    def __init__(self):
        self.log = Logger()
        self.log.debug('PLS playlist decoder')

    def isStreamValid(self, contentType, firstBytes):
        if 'audio/x-scpls' in contentType or \
           'application/pls+xml' in contentType or \
           firstBytes.strip().lower().startswith(b'[playlist]'):
            self.log.info('Stream is readable by PLS Playlist Decoder')
            return True
        else:
            return False

    def extractPlaylist(self, url):

        self.log.info('Downloading playlist...')

        req = UrlRequest(url)
        req.add_header('User-Agent', USER_AGENT)
        f = urlUrlopen(req)
        str = f.read()
        f.close()

        self.log.info('Playlist downloaded')
        self.log.info('Decoding playlist...')

        playlist = []
        lines = str.splitlines()
        for line in lines:
            if line.startswith(b"File") == True:
                list = line.split(b"=", 1)
                playlist.append(list[1])

        return playlist
示例#2
0
class M3uPlaylistDecoder:
    def __init__(self):
        self.log = Logger()


    def isStreamValid(self, contentType, firstBytes):
        if 'audio/mpegurl' in contentType or 'audio/x-mpegurl' in contentType:
            self.log.info('Stream is readable by M3U Playlist Decoder')
            return True
        else:
            lines = firstBytes.splitlines()
            for line in lines:
                if line.startswith(b"http://"):
                    return True
        return False


    def extractPlaylist(self,  url):
        self.log.info('M3u: downloading playlist...')
        req = UrlRequest(url)
        req.add_header('User-Agent', USER_AGENT)
        f = urlUrlopen(req)
        str = f.read()
        f.close()

        self.log.info('M3U: playlist downloaded, decoding... ')

        lines = str.splitlines()
        playlist = []

        for line in lines:
            if line.startswith(b"#") == False and len(line) > 0:
                playlist.append(line)

        return playlist
示例#3
0
class XspfPlaylistDecoder:
    def __init__(self):
        self.log = Logger()

    def isStreamValid(self, contentType, firstBytes):
        if 'application/xspf+xml' in contentType:
            self.log.info('Stream is readable by XSPF Playlist Decoder')
            return True
        else:
            return False

    def extractPlaylist(self, url):
        self.log.info('XSPF: downloading playlist...')
        req = UrlRequest(url)
        req.add_header('User-Agent', USER_AGENT)
        f = urlUrlopen(req)
        str = f.read()
        f.close()
        self.log.info('XSPF: playlist downloaded, decoding...')

        root = ET.parse(BytesIO(str))
        ns = {'xspf': 'http://xspf.org/ns/0/'}
        elements = root.findall(".//xspf:track/xspf:location", ns)

        result = []
        for r in elements:
            result.append(r.text)

        return result
示例#4
0
class AsfPlaylistDecoder:
    def __init__(self):
        self.log = Logger()

    def isStreamValid(self, contentType, firstBytes):
        if 'video/x-ms-asf' in contentType and \
           firstBytes.strip().lower().startswith('b[reference]'):
            self.log.info('Stream is readable by ASF Playlist Decoder')
            return True
        else:
            return False

    def extractPlaylist(self, url):
        self.log.info('ASF: downloading playlist..')
        req = UrlRequest(url)
        req.add_header('User-Agent', USER_AGENT)
        f = urlUrlopen(req)
        str = f.read()
        f.close()

        self.log.info('ASF: playlist downloaded, decoding...')

        playlist = []
        lines = str.splitlines()
        for line in lines:
            if line.startswith(b"Ref"):
                list = line.split(b"=", 1)
                tmp = list[1].strip()
                if tmp.endswith(b"?MSWMExt=.asf"):
                    playlist.append(tmp.replace(b"http", b"mms"))
                else:
                    playlist.append(tmp)

        return playlist
示例#5
0
class RamPlaylistDecoder:
    def __init__(self):
        self.log = Logger()


    def isStreamValid(self, contentType, firstBytes):
        if 'audio/x-pn-realaudio' in contentType or \
           'audio/vnd.rn-realaudio' in contentType:
            self.log.info('Stream is readable by RAM Playlist Decoder')
            return True
        else:
            return False


    def extractPlaylist(self,  url):
        self.log.info('RAM: Downloading playlist...')
        req = UrlRequest(url)
        req.add_header('User-Agent', USER_AGENT)
        f = urUrlopen(req)
        str = f.read()
        f.close()

        self.log.info('RAM Playlist downloaded, decoding...')

        lines = str.splitlines()
        playlist = []
        for line in lines:
            if len(line) > 0 and not line.startswith(b"#"):
                tmp = line.strip()
                if len(tmp) > 0:
                    playlist.append(line.strip())

        return playlist
示例#6
0
class AsxPlaylistDecoder:
    def __init__(self):
        self.log = Logger()

    def isStreamValid(self, contentType, firstBytes):
        if ('audio/x-ms-wax' in contentType or \
            'video/x-ms-wvx' in contentType or \
            'video/x-ms-asf' in contentType or \
            'video/x-ms-wmv' in contentType) and \
            firstBytes.strip().lower().startswith(b'<asx'):
            self.log.info('Stream is readable by ASX Playlist Decoder')
            return True
        else:
            return False

    def extractPlaylist(self, url):
        self.log.info('ASX: Downloading playlist...')

        req = UrlRequest(url)
        req.add_header('User-Agent', USER_AGENT)
        f = urlUrlopen(req)
        str = f.read()
        f.close()

        self.log.info('ASX: playlist downloaded, decoding...')

        try:
            root = ET.parse(BytesIO(str))
        except:
            # Last ditch: try to fix docs with mismatched tag name case
            str = re.sub('''<([A-Za-z0-9/]+)''', \
                         lambda m: "<" + m.group(1).lower(),
                         str)
            root = ET.parse(BytesIO(str))

        #ugly hack to normalize the XML
        for element in root.iter():
            tmp = element.tag
            element.tag = tmp.lower()

            keys = element.attrib.keys()
            for key in keys:
                element.attrib[key.lower()] = element.attrib[key]

        elts = root.findall(".//ref/[@href]")

        result = []
        for elt in elts:
            tmp = elt.attrib['href']
            if (tmp.endswith("?MSWMExt=.asf")):
                tmp = tmp.replace("http", "mms")
            result.append(tmp)

        return result
示例#7
0
class StreamDecoder:
    def __init__(self, cfg_provider):
        plsDecoder = PlsPlaylistDecoder()
        m3uDecoder = M3uPlaylistDecoder()
        asxDecoder = AsxPlaylistDecoder()
        xspfDecoder = XspfPlaylistDecoder()
        asfDecoder = AsfPlaylistDecoder()
        ramDecoder = RamPlaylistDecoder()

        self.log = Logger()

        self.decoders = [
            plsDecoder, asxDecoder, asfDecoder, xspfDecoder, ramDecoder,
            m3uDecoder
        ]

        self.url_timeout = None

        try:
            self.url_timeout = cfg_provider.getConfigValue("url_timeout")
            if (self.url_timeout == None):
                self.log.warn("Couldn't find url_timeout configuration")
                self.url_timeout = 100
                cfg_provider.setConfigValue("url_timeout",
                                            str(self.url_timeout))
        except Exception as e:
            self.log.warn("Couldn't find url_timeout configuration")
            self.url_timeout = 100
            cfg_provider.setConfigValue("url_timeout", str(self.url_timeout))

        self.log.info('Using url timeout = %s' % str(self.url_timeout))

    def getMediaStreamInfo(self, url):
        if type(url) != type(u""):
            url = url.decode('utf-8')
        if url.startswith("http") == False:
            self.log.info('Not an HTTP url. Maybe direct stream...')
            return UrlInfo(url, False, None)

        self.log.info('Requesting stream... %s' % url)
        req = UrlRequest(url)
        req.add_header('User-Agent', USER_AGENT)

        try:
            opener = urlBuild_opener(
                DummyMMSHandler(),
                HTTPSHandler(context=my_ssl_create_unverified_context()))
            f = opener.open(req, timeout=float(self.url_timeout))
        except HTTPError as e:
            self.log.warn('HTTP Error for %s: %s' % (url, e))
            return None
        except URLError as e:
            self.log.info('URLError for %s: %s ' % (url, e))
            if str(e.reason).startswith('MMS REDIRECT'):
                newurl = e.reason.split("MMS REDIRECT:", 1)[1]
                self.log.info('Found mms redirect for: %s' % newurl)
                return UrlInfo(newurl, False, None)
            else:
                return None
        except BadStatusLine as e:
            if str(e).startswith('ICY 200'):
                self.log.info('Found ICY stream')
                return UrlInfo(url, False, None)
            else:
                return None
        except Exception as e:
            print('%s: for %s: %s' % (type(e), url, e), file=sys.stderr)
            self.log.warn('%s: for %s: %s' % (type(e), url, e))
            return None

        metadata = f.info()
        firstbytes = f.read(500)
        f.close()

        try:
            contentType = metadata["content-type"]
            self.log.info('Content-Type: %s' % contentType)
        except Exception as e:
            self.log.info("Couldn't read content-type. Maybe direct stream...")
            return UrlInfo(url, False, None)

        for decoder in self.decoders:

            self.log.info('Checking decoder')
            if decoder.isStreamValid(contentType, firstbytes):
                return UrlInfo(url, True, contentType, decoder)

        # no playlist decoder found. Maybe a direct stream
        self.log.info(
            'No playlist decoder could handle the stream. Maybe direct stream...'
        )
        return UrlInfo(url, False, contentType)

    def getPlaylist(self, urlInfo):
        return urlInfo.getDecoder().extractPlaylist(urlInfo.getUrl())