|
a |
|
b/rdpl2stream/StreamDecoder.py |
|
|
1 |
##########################################################################
|
|
|
2 |
# Copyright 2009 Carlos Ribeiro
|
|
|
3 |
#
|
|
|
4 |
# This file is part of Radio Tray
|
|
|
5 |
#
|
|
|
6 |
# Radio Tray is free software: you can redistribute it and/or modify
|
|
|
7 |
# it under the terms of the GNU General Public License as published by
|
|
|
8 |
# the Free Software Foundation, either version 1 of the License, or
|
|
|
9 |
# (at your option) any later version.
|
|
|
10 |
#
|
|
|
11 |
# Radio Tray is distributed in the hope that it will be useful,
|
|
|
12 |
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
13 |
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
14 |
# GNU General Public License for more details.
|
|
|
15 |
#
|
|
|
16 |
# You should have received a copy of the GNU General Public License
|
|
|
17 |
# along with Radio Tray. If not, see <http://www.gnu.org/licenses/>.
|
|
|
18 |
#
|
|
|
19 |
##########################################################################
|
|
|
20 |
import urllib2
|
|
|
21 |
from lib.common import USER_AGENT
|
|
|
22 |
from lib.DummyMMSHandler import DummyMMSHandler
|
|
|
23 |
from PlsPlaylistDecoder import PlsPlaylistDecoder
|
|
|
24 |
from M3uPlaylistDecoder import M3uPlaylistDecoder
|
|
|
25 |
from AsxPlaylistDecoder import AsxPlaylistDecoder
|
|
|
26 |
from XspfPlaylistDecoder import XspfPlaylistDecoder
|
|
|
27 |
from AsfPlaylistDecoder import AsfPlaylistDecoder
|
|
|
28 |
from RamPlaylistDecoder import RamPlaylistDecoder
|
|
|
29 |
from UrlInfo import UrlInfo
|
|
|
30 |
import logging
|
|
|
31 |
|
|
|
32 |
class StreamDecoder:
|
|
|
33 |
|
|
|
34 |
def __init__(self, cfg_provider):
|
|
|
35 |
plsDecoder = PlsPlaylistDecoder()
|
|
|
36 |
m3uDecoder = M3uPlaylistDecoder()
|
|
|
37 |
asxDecoder = AsxPlaylistDecoder()
|
|
|
38 |
xspfDecoder = XspfPlaylistDecoder()
|
|
|
39 |
asfDecoder = AsfPlaylistDecoder()
|
|
|
40 |
ramDecoder = RamPlaylistDecoder()
|
|
|
41 |
|
|
|
42 |
self.log = logging.getLogger('radiotray')
|
|
|
43 |
|
|
|
44 |
self.decoders = [plsDecoder, asxDecoder, asfDecoder, xspfDecoder, ramDecoder, m3uDecoder]
|
|
|
45 |
|
|
|
46 |
self.url_timeout = None
|
|
|
47 |
|
|
|
48 |
try:
|
|
|
49 |
self.url_timeout = cfg_provider.getConfigValue("url_timeout")
|
|
|
50 |
if (self.url_timeout == None):
|
|
|
51 |
self.log.warn("Couldn't find url_timeout configuration")
|
|
|
52 |
self.url_timeout = 100
|
|
|
53 |
cfg_provider.setConfigValue("url_timeout", str(self.url_timeout))
|
|
|
54 |
except Exception, e:
|
|
|
55 |
self.log.warn("Couldn't find url_timeout configuration")
|
|
|
56 |
self.url_timeout = 100
|
|
|
57 |
cfg_provider.setConfigValue("url_timeout", str(self.url_timeout))
|
|
|
58 |
|
|
|
59 |
self.log.info('Using url timeout = %s', str(self.url_timeout))
|
|
|
60 |
|
|
|
61 |
|
|
|
62 |
def getMediaStreamInfo(self, url):
|
|
|
63 |
|
|
|
64 |
if url.startswith("http") == False:
|
|
|
65 |
self.log.info('Not an HTTP url. Maybe direct stream...')
|
|
|
66 |
return UrlInfo(url, False, None)
|
|
|
67 |
|
|
|
68 |
self.log.info('Requesting stream... %s', url)
|
|
|
69 |
req = urllib2.Request(url)
|
|
|
70 |
req.add_header('User-Agent', USER_AGENT)
|
|
|
71 |
|
|
|
72 |
try:
|
|
|
73 |
opener = urllib2.build_opener(DummyMMSHandler())
|
|
|
74 |
f = opener.open(req, timeout=float(self.url_timeout))
|
|
|
75 |
|
|
|
76 |
except urllib2.HTTPError, e:
|
|
|
77 |
self.log.warn('HTTP Error: No radio stream found for %s - %s', url, str(e))
|
|
|
78 |
return None
|
|
|
79 |
except urllib2.URLError, e:
|
|
|
80 |
self.log.info('No radio stream found for %s', url)
|
|
|
81 |
if str(e.reason).startswith('MMS REDIRECT'):
|
|
|
82 |
newurl = e.reason.split("MMS REDIRECT:",1)[1]
|
|
|
83 |
self.log.info('Found mms redirect for: %s', newurl)
|
|
|
84 |
return UrlInfo(newurl, False, None)
|
|
|
85 |
else:
|
|
|
86 |
return None
|
|
|
87 |
except Exception, e:
|
|
|
88 |
self.log.warn('No radio stream found. Error: %s', str(e))
|
|
|
89 |
return None
|
|
|
90 |
|
|
|
91 |
metadata = f.info()
|
|
|
92 |
firstbytes = f.read(500)
|
|
|
93 |
f.close()
|
|
|
94 |
|
|
|
95 |
try:
|
|
|
96 |
self.log.debug('Metadata obtained...')
|
|
|
97 |
contentType = metadata["Content-Type"]
|
|
|
98 |
self.log.info('Content-Type: %s', contentType)
|
|
|
99 |
|
|
|
100 |
|
|
|
101 |
except Exception, e:
|
|
|
102 |
self.log.info("Couldn't read content-type. Maybe direct stream...")
|
|
|
103 |
self.log.info('Error: %s',e)
|
|
|
104 |
return UrlInfo(url, False, None)
|
|
|
105 |
|
|
|
106 |
for decoder in self.decoders:
|
|
|
107 |
|
|
|
108 |
self.log.info('Checking decoder')
|
|
|
109 |
if(decoder.isStreamValid(contentType, firstbytes)):
|
|
|
110 |
|
|
|
111 |
return UrlInfo(url, True, contentType, decoder)
|
|
|
112 |
|
|
|
113 |
# no playlist decoder found. Maybe a direct stream
|
|
|
114 |
self.log.info('No playlist decoder could handle the stream. Maybe direct stream...')
|
|
|
115 |
return UrlInfo(url, False, contentType)
|
|
|
116 |
|
|
|
117 |
|
|
|
118 |
|
|
|
119 |
def getPlaylist(self, urlInfo):
|
|
|
120 |
|
|
|
121 |
return urlInfo.getDecoder().extractPlaylist(urlInfo.getUrl())
|
|
|
122 |
|