|
a/rdpl2stream/StreamDecoder.py |
|
b/rdpl2stream/StreamDecoder.py |
|
... |
|
... |
17 |
# along with Radio Tray. If not, see <http://www.gnu.org/licenses/>.
|
17 |
# along with Radio Tray. If not, see <http://www.gnu.org/licenses/>.
|
18 |
#
|
18 |
#
|
19 |
##########################################################################
|
19 |
##########################################################################
|
20 |
from __future__ import print_function
|
20 |
from __future__ import print_function
|
21 |
|
21 |
|
22 |
import urllib2
|
|
|
23 |
import sys
|
22 |
import sys
|
|
|
23 |
|
|
|
24 |
PY3 = sys.version > '3'
|
|
|
25 |
if PY3:
|
|
|
26 |
from urllib.request import Request as UrlRequest
|
|
|
27 |
import urllib.request, urllib.error, urllib.parse
|
|
|
28 |
from urllib.error import HTTPError as HTTPError
|
|
|
29 |
from urllib.error import URLError as URLError
|
|
|
30 |
from http.client import BadStatusLine as BadStatusLine
|
|
|
31 |
from urllib.request import build_opener as urlBuild_opener
|
|
|
32 |
from urllib.request import HTTPSHandler
|
|
|
33 |
else:
|
|
|
34 |
from urllib2 import Request as UrlRequest
|
|
|
35 |
from urllib2 import HTTPError as HTTPError
|
|
|
36 |
from urllib2 import URLError as URLError
|
|
|
37 |
from urllib2 import build_opener as urlBuild_opener
|
|
|
38 |
from urllib2 import HTTPSHandler
|
|
|
39 |
class BadStatusLine:
|
|
|
40 |
pass
|
24 |
import ssl
|
41 |
import ssl
|
25 |
|
42 |
|
26 |
from lib.common import USER_AGENT
|
43 |
from lib.common import USER_AGENT, Logger
|
27 |
from lib.DummyMMSHandler import DummyMMSHandler
|
44 |
from lib.DummyMMSHandler import DummyMMSHandler
|
28 |
from PlsPlaylistDecoder import PlsPlaylistDecoder
|
45 |
from PlsPlaylistDecoder import PlsPlaylistDecoder
|
29 |
from M3uPlaylistDecoder import M3uPlaylistDecoder
|
46 |
from M3uPlaylistDecoder import M3uPlaylistDecoder
|
30 |
from AsxPlaylistDecoder import AsxPlaylistDecoder
|
47 |
from AsxPlaylistDecoder import AsxPlaylistDecoder
|
31 |
from XspfPlaylistDecoder import XspfPlaylistDecoder
|
48 |
from XspfPlaylistDecoder import XspfPlaylistDecoder
|
32 |
from AsfPlaylistDecoder import AsfPlaylistDecoder
|
49 |
from AsfPlaylistDecoder import AsfPlaylistDecoder
|
33 |
from RamPlaylistDecoder import RamPlaylistDecoder
|
50 |
from RamPlaylistDecoder import RamPlaylistDecoder
|
34 |
from UrlInfo import UrlInfo
|
51 |
from UrlInfo import UrlInfo
|
35 |
|
52 |
|
36 |
class Logger:
|
|
|
37 |
def mprint(self, m):
|
|
|
38 |
#print("%s"%m, file=sys.stderr)
|
|
|
39 |
pass
|
|
|
40 |
def error(self, m):
|
|
|
41 |
self.mprint(m)
|
|
|
42 |
def warn(self, m):
|
|
|
43 |
self.mprint(m)
|
|
|
44 |
def info(self, m):
|
|
|
45 |
self.mprint(m)
|
|
|
46 |
def debug(self, m):
|
|
|
47 |
self.mprint(m)
|
|
|
48 |
|
53 |
|
49 |
class StreamDecoder:
|
54 |
class StreamDecoder:
|
50 |
|
55 |
|
51 |
def __init__(self, cfg_provider):
|
56 |
def __init__(self, cfg_provider):
|
52 |
plsDecoder = PlsPlaylistDecoder()
|
57 |
plsDecoder = PlsPlaylistDecoder()
|
53 |
m3uDecoder = M3uPlaylistDecoder()
|
58 |
m3uDecoder = M3uPlaylistDecoder()
|
|
... |
|
... |
66 |
self.url_timeout = cfg_provider.getConfigValue("url_timeout")
|
71 |
self.url_timeout = cfg_provider.getConfigValue("url_timeout")
|
67 |
if (self.url_timeout == None):
|
72 |
if (self.url_timeout == None):
|
68 |
self.log.warn("Couldn't find url_timeout configuration")
|
73 |
self.log.warn("Couldn't find url_timeout configuration")
|
69 |
self.url_timeout = 100
|
74 |
self.url_timeout = 100
|
70 |
cfg_provider.setConfigValue("url_timeout", str(self.url_timeout))
|
75 |
cfg_provider.setConfigValue("url_timeout", str(self.url_timeout))
|
71 |
except Exception, e:
|
76 |
except Exception as e:
|
72 |
self.log.warn("Couldn't find url_timeout configuration")
|
77 |
self.log.warn("Couldn't find url_timeout configuration")
|
73 |
self.url_timeout = 100
|
78 |
self.url_timeout = 100
|
74 |
cfg_provider.setConfigValue("url_timeout", str(self.url_timeout))
|
79 |
cfg_provider.setConfigValue("url_timeout", str(self.url_timeout))
|
75 |
|
80 |
|
76 |
self.log.info('Using url timeout = %s'% str(self.url_timeout))
|
81 |
self.log.info('Using url timeout = %s'% str(self.url_timeout))
|
77 |
|
82 |
|
78 |
|
83 |
|
79 |
def getMediaStreamInfo(self, url):
|
84 |
def getMediaStreamInfo(self, url):
|
80 |
|
85 |
if type(url) != type(u""):
|
|
|
86 |
url = url.decode('utf-8')
|
81 |
if url.startswith("http") == False:
|
87 |
if url.startswith("http") == False:
|
82 |
self.log.info('Not an HTTP url. Maybe direct stream...')
|
88 |
self.log.info('Not an HTTP url. Maybe direct stream...')
|
83 |
return UrlInfo(url, False, None)
|
89 |
return UrlInfo(url, False, None)
|
84 |
|
90 |
|
85 |
self.log.info('Requesting stream... %s'% url)
|
91 |
self.log.info('Requesting stream... %s'% url)
|
86 |
req = urllib2.Request(url)
|
92 |
req = UrlRequest(url)
|
87 |
req.add_header('User-Agent', USER_AGENT)
|
93 |
req.add_header('User-Agent', USER_AGENT)
|
88 |
|
94 |
|
89 |
try:
|
95 |
try:
|
90 |
opener = urllib2.build_opener(
|
96 |
opener = urlBuild_opener(
|
91 |
DummyMMSHandler(),
|
97 |
DummyMMSHandler(),
|
92 |
urllib2.HTTPSHandler(context =
|
98 |
HTTPSHandler(context = ssl._create_unverified_context()))
|
93 |
ssl._create_unverified_context()))
|
|
|
94 |
f = opener.open(req, timeout=float(self.url_timeout))
|
99 |
f = opener.open(req, timeout=float(self.url_timeout))
|
95 |
|
|
|
96 |
except urllib2.HTTPError, e:
|
100 |
except HTTPError as e:
|
97 |
self.log.warn('HTTP Error: No radio stream found for %s - %s' %
|
101 |
self.log.warn('HTTP Error for %s: %s' % (url, e))
|
98 |
(url, str(e)))
|
|
|
99 |
return None
|
102 |
return None
|
100 |
except urllib2.URLError, e:
|
103 |
except URLError as e:
|
101 |
self.log.info('No radio stream found for %s'% url)
|
104 |
self.log.info('URLError for %s: %s ' % (url, e))
|
102 |
if str(e.reason).startswith('MMS REDIRECT'):
|
105 |
if str(e.reason).startswith('MMS REDIRECT'):
|
103 |
newurl = e.reason.split("MMS REDIRECT:",1)[1]
|
106 |
newurl = e.reason.split("MMS REDIRECT:",1)[1]
|
104 |
self.log.info('Found mms redirect for: %s' % newurl)
|
107 |
self.log.info('Found mms redirect for: %s' % newurl)
|
105 |
return UrlInfo(newurl, False, None)
|
108 |
return UrlInfo(newurl, False, None)
|
106 |
else:
|
109 |
else:
|
107 |
return None
|
110 |
return None
|
|
|
111 |
except BadStatusLine as e:
|
|
|
112 |
if str(e).startswith('ICY 200'):
|
|
|
113 |
self.log.info('Found ICY stream')
|
|
|
114 |
return UrlInfo(url, False, None)
|
|
|
115 |
else:
|
|
|
116 |
return None
|
108 |
except Exception, e:
|
117 |
except Exception as e:
|
109 |
self.log.warn('No radio stream found. Error: %s'% str(e))
|
118 |
self.log.warn('%s: for %s: Error %s: %s' % (type(e), url, e))
|
110 |
return None
|
119 |
return None
|
111 |
|
120 |
|
112 |
metadata = f.info()
|
121 |
metadata = f.info()
|
113 |
firstbytes = f.read(500)
|
122 |
firstbytes = f.read(500)
|
114 |
f.close()
|
123 |
f.close()
|
115 |
|
124 |
|
116 |
try:
|
125 |
try:
|
117 |
self.log.debug('Metadata obtained...')
|
|
|
118 |
contentType = metadata["Content-Type"]
|
126 |
contentType = metadata["content-type"]
|
119 |
self.log.info('Content-Type: %s'% contentType)
|
127 |
self.log.info('Content-Type: %s'% contentType)
|
120 |
|
|
|
121 |
|
|
|
122 |
except Exception, e:
|
128 |
except Exception as e:
|
123 |
self.log.info("Couldn't read content-type. Maybe direct stream...")
|
129 |
self.log.info("Couldn't read content-type. Maybe direct stream...")
|
124 |
self.log.info('Error: %s'%e)
|
|
|
125 |
return UrlInfo(url, False, None)
|
130 |
return UrlInfo(url, False, None)
|
126 |
|
131 |
|
127 |
for decoder in self.decoders:
|
132 |
for decoder in self.decoders:
|
128 |
|
133 |
|
129 |
self.log.info('Checking decoder')
|
134 |
self.log.info('Checking decoder')
|
130 |
if(decoder.isStreamValid(contentType, firstbytes)):
|
135 |
if decoder.isStreamValid(contentType, firstbytes):
|
131 |
|
|
|
132 |
return UrlInfo(url, True, contentType, decoder)
|
136 |
return UrlInfo(url, True, contentType, decoder)
|
133 |
|
137 |
|
134 |
# no playlist decoder found. Maybe a direct stream
|
138 |
# no playlist decoder found. Maybe a direct stream
|
135 |
self.log.info('No playlist decoder could handle the stream. Maybe direct stream...')
|
139 |
self.log.info('No playlist decoder could handle the stream. Maybe direct stream...')
|
136 |
return UrlInfo(url, False, contentType)
|
140 |
return UrlInfo(url, False, contentType)
|
137 |
|
141 |
|
138 |
|
142 |
|
139 |
|
|
|
140 |
def getPlaylist(self, urlInfo):
|
143 |
def getPlaylist(self, urlInfo):
|
141 |
|
|
|
142 |
return urlInfo.getDecoder().extractPlaylist(urlInfo.getUrl())
|
144 |
return urlInfo.getDecoder().extractPlaylist(urlInfo.getUrl())
|
143 |
|
145 |
|