def __init__(self): xbmcplugin.setContent(HANDLE, 'audio') self.curl = HTTPComm() self.name = ARGS.get('foldername', None) self.mode = ARGS.get('mode', None) self.thumbnailImage = 'special://home/addons/%s/icon.png' % ADDON_ID
def __init__(self): xbmcplugin.setContent(HANDLE, 'audio') self.curl = HTTPComm() self.name = ARGS.get('foldername', None) self.mode = ARGS.get('mode', None)
class musicAddonXbmc: # settings used for multithreading threadMax = 8 workQueue = Queue.Queue() # Resolves path to where plugin settings and cache will be stored addonProfilePath = xbmc.translatePath( ADDON.getAddonInfo('profile')).decode('utf-8') # Init CURL thingy curler = HTTPComm() # regex used to find all streams in a .pls re_playlistStreams = re.compile("File\d=([^\n]+)\s*Title\d=([^\n]+)", re.M | re.I) #dictBitrate = {1: 40, 2: 64, 3: 128, 4: 256} dictBitrate = [40, 64, 128, 256] # list of channels used when caching and retrieving from cache channelsList = [] # unique listenkey if a premium member listenKey = '' # stores how many new channels/channelart was found newChannels = 0 def __init__(self): # If stats is allowed and its been at least 24 hours since last checkin #if (ADDON.getSetting('allowstats') == "true") and ( # self.checkFileTime(self._checkinFile, self._addonProfilePath, 86400) == True): # open(self._checkinFile, "w") # # account = 'public' # if ADDON.getSetting('username') != "": # account = 'premium' # # xbmc.log('Submitting stats', xbmc.LOGNOTICE) # self._httpComm.get('http://stats.qualisoft.dk/?plugin=' + ADDON.getAddonInfo( # 'id') + '&version=' + __version__ + '&account=' + account + '&key=' + parser.get('plugin', # 'checkinkey')) # xbmc.log("[PLUGIN] %s v%s (%s)" % (__plugin__, __version__, __date__), xbmc.LOGNOTICE) """ Let's get some tunes! """ def run(self): threads = [] # check if cache has expired if not ADDON.getSetting("forceupdate") == "true"\ and not int(ADDON.getSetting("cacheexpire_days")) == 0\ and self.checkFileTime(pluginConfig.get('cache', 'cacheChannels'), ADDON.getSetting("cacheexpire_days")): ADDON.setSetting(id="forceupdate", value="true") if ADDON.getSetting("forceupdate") == "true": channels = [] html = "" # if username is set, try to login and go for premium channels if ADDON.getSetting('username') != "": loginData = urllib.urlencode({ 'member_session[username]': ADDON.getSetting('username'), 'member_session[password]': ADDON.getSetting('password') }) # post login info and get frontpage html html = self.curler.request(pluginConfig.get('urls', 'login'), 'post', loginData) # if we could not reach di.fm at all if not bool(html): xbmc.log('di.fm could not be reached', xbmc.LOGWARNING) xbmcgui.Dialog().ok(ADDON.getLocalizedString(30100), ADDON.getLocalizedString(30101), ADDON.getLocalizedString(30102), ADDON.getLocalizedString(30103)) xbmcplugin.endOfDirectory(HANDLE, succeeded=True) return True channels = json.loads( self.curler.request( pluginConfig.get( 'streams', 'premium%sk' % self.dictBitrate[int( ADDON.getSetting('bitrate'))]), 'get')) premiumConfig = self.getPremiumConfig() # if premiumConfig['listenKey'] is blank, we did not log in correctly if premiumConfig['listenKey'] == '': xbmc.log('Login did not succeed', xbmc.LOGWARNING) xbmcgui.Dialog().ok(ADDON.getLocalizedString(30170), ADDON.getLocalizedString(30171), ADDON.getLocalizedString(30172)) xbmcplugin.endOfDirectory(HANDLE, succeeded=True) return True # if we should get the favorites or all channels if ADDON.getSetting("usefavorites") == 'true': channels = self.getFavoriteChannels(html, channels) if len(channels) == 0: xbmcgui.Dialog().ok(ADDON.getLocalizedString(30180), ADDON.getLocalizedString(30181), ADDON.getLocalizedString(30182)) xbmcplugin.endOfDirectory(HANDLE, succeeded=True) return True # add listenkey to playlist urls for channel in channels: channel['playlist'] = '%s?%s' % ( channel['playlist'], premiumConfig['listenKey']) # go for free/public channels else: html = self.curler.request( pluginConfig.get('urls', 'frontpage'), 'get') # if we could not reach di.fm at all if not html: xbmc.log(u'di.fm could not be reached', xbmc.LOGWARNING) xbmcgui.Dialog().ok(ADDON.getLocalizedString(30100), ADDON.getLocalizedString(30101), ADDON.getLocalizedString(30102), ADDON.getLocalizedString(30103)) xbmcplugin.endOfDirectory(HANDLE, succeeded=True) return True channels = json.loads( self.curler.request(pluginConfig.get('streams', 'public'), 'get')) re_channelData = re.compile( "NS\('AudioAddict'\).Channels\s*=\s*([^;]+);", re.M | re.I) channelMeta = json.loads(re_channelData.findall(html)[0]) # put each playlist in a worker queue for threading for channel in channels: self.workQueue.put(channel) # starts 8 threads to download streams for each channel while not self.workQueue.empty(): xbmc.log( 'Worker queue size is %s' % (str(self.workQueue.qsize())), xbmc.LOGNOTICE) if threading.activeCount( ) < self.threadMax and not self.workQueue.empty(): threads.append( scraperThread(self, self.workQueue.get(), channelMeta, len(channels))) threads[-1].start() else: time.sleep(0.1) # wait for all threads to finish before continuing for t in threads: t.join() # Saves channels to cache and reset the "force update" flag if len(channels) > 0: pickle.dump(self.channelsList, open( os.path.join( self.addonProfilePath, pluginConfig.get('cache', 'cacheChannels')), "w"), protocol=0) ADDON.setSetting(id="forceupdate", value="false") # else load channels from cache file else: self.channelsList = pickle.load( open( os.path.join(self.addonProfilePath, pluginConfig.get('cache', 'cacheChannels')), "r")) for channel in self.channelsList: self.addItem(channel['name'], channel['streamUrl'], channel['description'], channel['bitrate'], channel['asset'], channel['isNew'], len(self.channelsList)) # Should channels be sorted A-Z? if ADDON.getSetting('sortaz') == "true": xbmcplugin.addSortMethod(HANDLE, sortMethod=xbmcplugin.SORT_METHOD_LABEL) # tell XMBC there are no more items to list xbmcplugin.endOfDirectory(HANDLE, succeeded=True) if self.newChannels > 0: xbmcgui.Dialog().ok( ADDON.getLocalizedString(30130), ADDON.getLocalizedString(30131) + str(self.newChannels) + ADDON.getLocalizedString(30132), ADDON.getLocalizedString(30133), ADDON.getLocalizedString(30134)) return True """ Parses a playlist (if needed) and calls the function that adds it to the GUI """ def addChannel(self, channel, channelMeta, channelCount): # set to true if new channelart is downloaded isNew = 0 if not os.path.exists(self.addonProfilePath + str(channel['id']) + '.png'): isNew = 1 self.newChannels += 1 self.getChannelAsset(str(channel['id']), channelMeta[str(channel['id'])]['asset_url']) if ADDON.getSetting('randomstream') == "true": playlist = self.curler.request( channel['playlist'] + self.listenKey, 'get') playlistStreams = self.re_playlistStreams.findall(playlist) # gets a random stream from the channels playlist streamUrl = playlistStreams[randrange(len(playlistStreams))][0] else: streamUrl = channel['playlist'] + self.listenKey bitrate = 40 if ADDON.getSetting('username') != "": bitrate = self.dictBitrate[int(ADDON.getSetting('bitrate'))] # stores channel in list to be cached later co = { 'name': channel['name'], 'streamUrl': streamUrl, 'description': channel['description'], 'bitrate': bitrate, 'asset': self.addonProfilePath + str(channel['id']) + '.png', 'isNew': isNew } self.channelsList.append(dict(co)) self.addItem(channel['name'], streamUrl, channel['description'], bitrate, self.addonProfilePath + str(channel['id']) + '.png', isNew, channelCount) """ Returns a bool Adds item to the XBMC GUI """ def addItem(self, channelTitle, streamUrl, streamDescription, bitrate, icon, isNewChannel, totalItems): # tart it up a bit if it's a new channel if isNewChannel: li = xbmcgui.ListItem(label="[COLOR FF007EFF]" + channelTitle + "[/COLOR]", thumbnailImage=icon) xbmc.log( u"New channel found: %s" % channelTitle.encode('ascii', 'xmlcharrefreplace'), xbmc.LOGERROR) else: li = xbmcgui.ListItem(label=channelTitle, thumbnailImage=icon) # 256kb/sec is MP3 if bitrate == 256 and ADDON.getSetting('username') != "": li.setProperty("mimetype", 'audio/mpeg') else: li.setProperty("mimetype", 'audio/aac') li.setInfo(type="Music", infoLabels={ "Genre": channelTitle, "Comment": streamDescription, "Size": bitrate * 1024 }) li.setProperty("IsPlayable", "true") xbmcplugin.addDirectoryItem(handle=HANDLE, url=streamUrl, listitem=li, isFolder=False, totalItems=totalItems) return True """ Returns a bool Will check if channel asset/art is present in cache - if not, try to download """ def getChannelAsset(self, channelId, assetUrl): try: data = self.curler.request(assetUrl, 'get') filepath = self.addonProfilePath + channelId + '.png' open(filepath, 'wb').write(data) xbmc.log('Found new channel art for with ID %s' % str(channelId), xbmc.LOGINFO) except Exception: sys.exc_clear( ) # Clears all exceptions so the script will continue to run xbmcgui.Dialog().ok(ADDON.getLocalizedString(30160), ADDON.getLocalizedString(30161), ADDON.getLocalizedString(30162) + channelId) xbmc.log( ADDON.getLocalizedString(30160) + " " + ADDON.getLocalizedString(30161) + str(channelId) + " " + ADDON.getLocalizedString(30162) + str(channelId), xbmc.LOGERROR) return False return True """ Return a list containing a dictonary Filters out all channels that are not in the favorites list """ def getFavoriteChannels(self, html, allChannels): channels = [] re_favoriteData = re.compile( "NS\('AudioAddict.API.Config.member'\).Favorites\s*=\s*([^;]+);", re.M | re.I) m = re_favoriteData.findall(html) # if favorites list is empty, return empty list if m[0] == '[]': return channels else: favorites = json.loads(re_favoriteData.findall(html)[0]) # sort favorites after user selected positions favorites = sorted(favorites, key=lambda k: k['position']) for fav in favorites: for channel in allChannels: if fav['channel_id'] == channel['id']: channels.append(dict(channel)) return channels """ Return a list containing a dictonary or false Returns the logged in users premium config """ def getPremiumConfig(self): try: if ADDON.getSetting("forceupdate") == "true": # Login to api.audioaddict.com to retrieve useful data # Documentation: http://tobiass.eu/api-doc.html#13 loginData = urllib.urlencode({ 'username': ADDON.getSetting('username'), 'password': ADDON.getSetting('password') }) # Download and save response jsonResponse = self.curler.request( pluginConfig.get('urls', 'apiAuthenticate'), 'post', loginData) jsonData = json.loads(jsonResponse) pickle.dump(jsonData, open( os.path.join( self.addonProfilePath, pluginConfig.get('cache', 'cachePremiumConfig')), "w"), protocol=0) # Load needed data into premiumConfig premiumConfig = {'listenKey': jsonData['listen_key']} else: # Load data from local cache jsonData = pickle.load( open( os.path.join( self.addonProfilePath, pluginConfig.get('cache', 'cachePremiumConfig')), "r")) premiumConfig = {'listenKey': jsonData['listen_key']} return premiumConfig except Exception: sys.exc_clear( ) # Clears all exceptions so the script will continue to run return False """ Return a bool Checks if a file is older than x days """ def checkFileTime(self, filename, days): if not os.path.exists(self.addonProfilePath): os.makedirs(self.addonProfilePath) return True daysInSecs = int(days) * 60 * 60 * 24 file = os.path.join(self.addonProfilePath, filename) # If file exists, check timestamp if os.path.exists(file): if os.path.getmtime(file) > (time.time() - daysInSecs): xbmc.log( u'It has not been %s days since %s was last updated' % (days, file), xbmc.LOGNOTICE) return False else: xbmc.log(u'The cache file %s + has expired' % file, xbmc.LOGNOTICE) return True # If file does not exist, return true so the file will be created by scraping the page else: xbmc.log(u'The cache file %s does not exist' % file, xbmc.LOGNOTICE) return True
class Main: def __init__(self): xbmcplugin.setContent(HANDLE, 'audio') self.curl = HTTPComm() self.name = ARGS.get('foldername', None) self.mode = ARGS.get('mode', None) self.thumbnailImage = 'special://home/addons/%s/icon.png' % ADDON_ID def run(self): if self.mode is None: # Create streams directory url = self.build_url({'mode': 'folder', 'foldername': 'streams'}) li = xbmcgui.ListItem(ADDON.getLocalizedString(30100), iconImage='DefaultFolder.png') xbmcplugin.addDirectoryItem(handle=HANDLE, url=url, listitem=li, isFolder=True) # Create queue directory url = self.build_url({'mode': 'folder', 'foldername': 'queue'}) li = xbmcgui.ListItem(ADDON.getLocalizedString(30101), iconImage='DefaultFolder.png') xbmcplugin.addDirectoryItem(handle=HANDLE, url=url, listitem=li, isFolder=True) xbmcplugin.endOfDirectory(HANDLE) elif self.mode[0] == 'folder' and self.name[0] == 'streams': streams = self.get_streams() for stream in streams: li = xbmcgui.ListItem(stream['name'], iconImage='DefaultAudio.png', thumbnailImage=self.thumbnailImage) li.setInfo(type="Music", infoLabels={"Size": stream['bitrate'] * 1024}) li.setProperty("IsPlayable", "true") xbmcplugin.addDirectoryItem(handle=HANDLE, url=stream['url'], listitem=li, isFolder=False) xbmcplugin.endOfDirectory(HANDLE) elif self.mode[0] == 'folder' and self.name[0] == 'queue': history = self.get_history() # Currently Playing self.add_heading(ADDON.getLocalizedString(30200)) for item in history[0]: li = xbmcgui.ListItem(item["artist"] + " - " + item["song"], thumbnailImage=self.thumbnailImage) li.setProperty("IsPlayable", "false") xbmcplugin.addDirectoryItem(handle=HANDLE, url="nnn", listitem=li, isFolder=False) # Queue self.add_heading(ADDON.getLocalizedString(30201), True) for item in history[1]: li = xbmcgui.ListItem(item["artist"] + " - " + item["song"], thumbnailImage=self.thumbnailImage) li.setProperty("IsPlayable", "false") xbmcplugin.addDirectoryItem(handle=HANDLE, url="nnn", listitem=li, isFolder=False) # History self.add_heading(ADDON.getLocalizedString(30202), True) for item in history[2]: li = xbmcgui.ListItem(item["artist"] + " - " + item["song"], thumbnailImage=self.thumbnailImage) li.setProperty("IsPlayable", "false") xbmcplugin.addDirectoryItem(handle=HANDLE, url="nnn", listitem=li, isFolder=False) xbmcplugin.endOfDirectory(HANDLE) else: # Add items url = 'http://necta.pedroja.tech/necta192.mp3' li = xbmcgui.ListItem(ADDON.getLocalizedString(30199), iconImage='DefaultAudio.png') xbmcplugin.addDirectoryItem(handle=HANDLE, url=url, listitem=li, isFolder=False) xbmcplugin.endOfDirectory(HANDLE) def build_url(self, query): return BASE_URL + '?' + urllib.urlencode(query) def get_streams(self): streams = [] xml = self.curl.request(pluginConfig.get('urls', 'stream_xml'), 'get') dom = minidom.parseString(xml) for node in dom.getElementsByTagName('stream'): id = node.attributes["id"].value name = node.getElementsByTagName('name')[0].firstChild.nodeValue url = node.getElementsByTagName('url')[0].firstChild.nodeValue bitrate = int(node.getElementsByTagName('bitrate')[0].firstChild.nodeValue) country = node.getElementsByTagName('country')[0].firstChild.nodeValue type = node.getElementsByTagName('type')[0].firstChild.nodeValue streams.append({"id": id, "name": name, "url": url, 'bitrate': bitrate, 'country': country, 'type': type}) streams = sorted(streams, key=lambda d: (d['name'][:6].lower(), -d['bitrate'])) #itemgetter('bitrate') return streams def get_history(self): current = [] queue = [] history = [] xml = self.curl.request(pluginConfig.get('urls', 'history_xml'), 'get') dom = minidom.parseString(xml) # Currently Playing currentsong = dom.getElementsByTagName('now')[0] artist = currentsong.getElementsByTagName('artist')[0].firstChild.nodeValue song = currentsong.getElementsByTagName('song')[0].firstChild.nodeValue current.append({"artist": artist, "song": song}) # Read queue for entry in dom.getElementsByTagName('queue')[0].getElementsByTagName('entry'): artist = entry.getElementsByTagName('artist')[0].firstChild.nodeValue song = entry.getElementsByTagName('song')[0].firstChild.nodeValue play_start = entry.getElementsByTagName('playstart')[0].firstChild.nodeValue queue.append({"artist": artist, "song": song, "play_start": play_start}) # Read history for entry in dom.getElementsByTagName('history')[0].getElementsByTagName('entry'): artist = entry.getElementsByTagName('artist')[0].firstChild.nodeValue song = entry.getElementsByTagName('song')[0].firstChild.nodeValue play_start = entry.getElementsByTagName('playstart')[0].firstChild.nodeValue history.append({"artist": artist, "song": song, "play_start": play_start}) return [current, queue, history] def add_heading(self, title, linebreak=False): # Linebreak if linebreak: li = xbmcgui.ListItem(thumbnailImage=self.thumbnailImage) li.setProperty("IsPlayable", "false") xbmcplugin.addDirectoryItem(handle=HANDLE, url="nnn", listitem=li, isFolder=False) li = xbmcgui.ListItem(label="[COLOR FF007EFF]" + title + "[/COLOR]", thumbnailImage=self.thumbnailImage) li.setProperty("IsPlayable", "false") xbmcplugin.addDirectoryItem(handle=HANDLE, url="nnn", listitem=li, isFolder=False)
class Main: def __init__(self): xbmcplugin.setContent(HANDLE, 'audio') self.curl = HTTPComm() self.name = ARGS.get('foldername', None) self.mode = ARGS.get('mode', None) self.thumbnailImage = 'special://home/addons/%s/icon.png' % ADDON_ID def run(self): if self.mode is None: # Create streams directory url = self.build_url({'mode': 'folder', 'foldername': 'streams'}) li = xbmcgui.ListItem(ADDON.getLocalizedString(30100), iconImage='DefaultFolder.png') xbmcplugin.addDirectoryItem(handle=HANDLE, url=url, listitem=li, isFolder=True) # Create queue directory url = self.build_url({'mode': 'folder', 'foldername': 'queue'}) li = xbmcgui.ListItem(ADDON.getLocalizedString(30101), iconImage='DefaultFolder.png') xbmcplugin.addDirectoryItem(handle=HANDLE, url=url, listitem=li, isFolder=True) xbmcplugin.endOfDirectory(HANDLE) elif self.mode[0] == 'folder' and self.name[0] == 'streams': streams = self.get_streams() for stream in streams: li = xbmcgui.ListItem(stream['name'], iconImage='DefaultAudio.png', thumbnailImage=self.thumbnailImage) li.setInfo(type="Music", infoLabels={"Size": stream['bitrate'] * 1024}) li.setProperty("IsPlayable", "true") xbmcplugin.addDirectoryItem(handle=HANDLE, url=stream['url'], listitem=li, isFolder=False) xbmcplugin.endOfDirectory(HANDLE) elif self.mode[0] == 'folder' and self.name[0] == 'queue': history = self.get_history() # Currently Playing self.add_heading(ADDON.getLocalizedString(30200)) for item in history[0]: li = xbmcgui.ListItem(item["artist"] + " - " + item["song"], thumbnailImage=self.thumbnailImage) li.setProperty("IsPlayable", "false") xbmcplugin.addDirectoryItem(handle=HANDLE, url="nnn", listitem=li, isFolder=False) # Queue self.add_heading(ADDON.getLocalizedString(30201), True) for item in history[1]: li = xbmcgui.ListItem(item["artist"] + " - " + item["song"], thumbnailImage=self.thumbnailImage) li.setProperty("IsPlayable", "false") xbmcplugin.addDirectoryItem(handle=HANDLE, url="nnn", listitem=li, isFolder=False) # History self.add_heading(ADDON.getLocalizedString(30202), True) for item in history[2]: li = xbmcgui.ListItem(item["artist"] + " - " + item["song"], thumbnailImage=self.thumbnailImage) li.setProperty("IsPlayable", "false") xbmcplugin.addDirectoryItem(handle=HANDLE, url="nnn", listitem=li, isFolder=False) xbmcplugin.endOfDirectory(HANDLE) else: # Add items url = 'http://no.scenemusic.net:9000/necta.m3u' li = xbmcgui.ListItem(ADDON.getLocalizedString(30199), iconImage='DefaultAudio.png') xbmcplugin.addDirectoryItem(handle=HANDLE, url=url, listitem=li, isFolder=False) xbmcplugin.endOfDirectory(HANDLE) def build_url(self, query): return BASE_URL + '?' + urllib.urlencode(query) def get_streams(self): streams = [] xml = self.curl.request(pluginConfig.get('urls', 'stream_xml'), 'get') dom = minidom.parseString(xml) for node in dom.getElementsByTagName('stream'): id = node.attributes["id"].value name = node.getElementsByTagName('name')[0].firstChild.nodeValue url = node.getElementsByTagName('url')[0].firstChild.nodeValue bitrate = int(node.getElementsByTagName('bitrate')[0].firstChild.nodeValue) country = node.getElementsByTagName('country')[0].firstChild.nodeValue type = node.getElementsByTagName('type')[0].firstChild.nodeValue streams.append({"id": id, "name": name, "url": url, 'bitrate': bitrate, 'country': country, 'type': type}) streams = sorted(streams, key=lambda d: (d['name'][:6].lower(), -d['bitrate'])) #itemgetter('bitrate') return streams def get_history(self): current = [] queue = [] history = [] xml = self.curl.request(pluginConfig.get('urls', 'history_xml'), 'get') dom = minidom.parseString(xml) # Currently Playing currentsong = dom.getElementsByTagName('now')[0] artist = currentsong.getElementsByTagName('artist')[0].firstChild.nodeValue song = currentsong.getElementsByTagName('song')[0].firstChild.nodeValue current.append({"artist": artist, "song": song}) # Read queue for entry in dom.getElementsByTagName('queue')[0].getElementsByTagName('entry'): artist = entry.getElementsByTagName('artist')[0].firstChild.nodeValue song = entry.getElementsByTagName('song')[0].firstChild.nodeValue play_start = entry.getElementsByTagName('playstart')[0].firstChild.nodeValue queue.append({"artist": artist, "song": song, "play_start": play_start}) # Read history for entry in dom.getElementsByTagName('history')[0].getElementsByTagName('entry'): artist = entry.getElementsByTagName('artist')[0].firstChild.nodeValue song = entry.getElementsByTagName('song')[0].firstChild.nodeValue play_start = entry.getElementsByTagName('playstart')[0].firstChild.nodeValue history.append({"artist": artist, "song": song, "play_start": play_start}) return [current, queue, history] def add_heading(self, title, linebreak=False): # Linebreak if linebreak: li = xbmcgui.ListItem(thumbnailImage=self.thumbnailImage) li.setProperty("IsPlayable", "false") xbmcplugin.addDirectoryItem(handle=HANDLE, url="nnn", listitem=li, isFolder=False) li = xbmcgui.ListItem(label="[COLOR FF007EFF]" + title + "[/COLOR]", thumbnailImage=self.thumbnailImage) li.setProperty("IsPlayable", "false") xbmcplugin.addDirectoryItem(handle=HANDLE, url="nnn", listitem=li, isFolder=False)
class musicAddonXbmc: _addonProfilePath = xbmc.translatePath( "special://profile/plugin_data/music/Sky.fm/").decode( 'utf-8') # Dir where plugin settings and cache will be stored _cacheStreams = _addonProfilePath + "cache_streamlist.dat" _cacheListenkey = _addonProfilePath + "cache_listenkey.dat" _checkinFile = _addonProfilePath + "cache_lastcheckin.dat" _baseUrl = parser.get('urls', 'baseUrl') _loginUrl = parser.get('urls', 'loginUrl') _listenkeyUrl = parser.get('urls', 'listenkeyUrl') _publicStreamsJson40k = parser.get( 'urls', 'publicStreamsJson40k') # Public AAC 40k/sec AAC+ JSON url _premiumStreamsJson40k = parser.get( 'urls', 'premiumStreamsJson40k') # AAC 40k/sec AAC+ JSON url _premiumStreamsJson64k = parser.get( 'urls', 'premiumStreamsJson64k') # AAC 64k/sec AAC+ JSON url _premiumStreamsJson128k = parser.get( 'urls', 'premiumStreamsJson128k') # AAC 128k/sec AAC+ JSON url _premiumStreamsJson256k = parser.get( 'urls', 'premiumStreamsJson256k') # MP3 256k/sec AAC+ JSON url _favoritesStreamJson40k = parser.get( 'urls', 'favoritesStreamJson40k') # Favorites AAC 40k/sec AAC+ playlist url _favoritesStreamJson64k = parser.get( 'urls', 'favoritesStreamJson64k') # Favorites AAC 64k/sec AAC+ playlist url _favoritesStreamJson128k = parser.get( 'urls', 'favoritesStreamJson128k') # Favorites AAC 128k/sec AAC+ playlist url _favoritesStreamJson256k = parser.get( 'urls', 'favoritesStreamJson256k') # Favorites MP3 256k/sec AAC+ playlist url _httpComm = HTTPComm() # Init CURL thingy _frontpageHtml = "" _newChannels = 0 _bitrate = 40 _streamMimeType = 'audio/aac' def __init__(self): # If stats is allowed and its been at least 24 hours since last checkin if (ADDON.getSetting('allowstats') == "true") and (self.checkFileTime( self._checkinFile, self._addonProfilePath, 86400) == True): open(self._checkinFile, "w") account = 'public' if ADDON.getSetting('username') != "": account = 'premium' xbmc.log('Submitting stats', xbmc.LOGNOTICE) self._httpComm.get('http://stats.qualisoft.dk/?plugin=' + ADDON.getAddonInfo('id') + '&version=' + __version__ + '&account=' + account + '&key=' + parser.get('plugin', 'checkinkey')) xbmc.log("[PLUGIN] %s v%s (%s)" % (__plugin__, __version__, __date__), xbmc.LOGNOTICE) # Let's get some tunes! def start(self): jsonList = [] # list that data from the JSON will be put in streamList = [ ] # the final list of channels, with small custom additions # Check if cachefile has expired if ADDON.getSetting("forceupdate") == "true" or ( (int(ADDON.getSetting("cacheexpire")) * 60) != 0 and self.checkFileTime( self._cacheStreams, self._addonProfilePath, (int(ADDON.getSetting("cacheexpire")) * 60)) == True): listenkey = "" # will contain the premium listenkey if ADDON.getSetting('username') != "" and ADDON.getSetting( "usefavorites" ) == 'false': # if username is set and not using favorites xbmc.log("Going for Premium streams", xbmc.LOGNOTICE) # Checks if forceupdate is set and if the listenkey cachefile exists if ADDON.getSetting( "forceupdate") == "true" or not os.path.exists( self._cacheListenkey): listenkey = self.getListenkey() pickle.dump(listenkey, open(self._cacheListenkey, "w"), protocol=0) # saves listenkey for further use else: listenkey = pickle.load(open(self._cacheListenkey, "r")) if ADDON.getSetting('bitrate') == '0': self._bitrate = 40 jsonList = self.getJSONChannelList( self._premiumStreamsJson40k) streamList = self.customizeStreamListAddMenuitem( jsonList, listenkey) elif ADDON.getSetting('bitrate') == '1': self._bitrate = 64 jsonList = self.getJSONChannelList( self._premiumStreamsJson64k) streamList = self.customizeStreamListAddMenuitem( jsonList, listenkey) elif ADDON.getSetting('bitrate') == '2': self._bitrate = 128 jsonList = self.getJSONChannelList( self._premiumStreamsJson128k) streamList = self.customizeStreamListAddMenuitem( jsonList, listenkey) else: self._bitrate = 256 self._streamMimeType = 'audio/mpeg' jsonList = self.getJSONChannelList( self._premiumStreamsJson256k) streamList = self.customizeStreamListAddMenuitem( jsonList, listenkey) xbmc.log("Bitrate set to " + str(self._bitrate), xbmc.LOGNOTICE) elif ADDON.getSetting('username') != "" and ADDON.getSetting( "usefavorites" ) == 'true': # if username is set and wants to use favorites xbmc.log("Going for Premium favorite streams", xbmc.LOGNOTICE) listenkey = self.getListenkey() if ADDON.getSetting('bitrate') == '0': self._bitrate = 40 streamList = self.getFavoriteStreamsList( self._favoritesStreamJson40k + "?" + listenkey) elif ADDON.getSetting('bitrate') == '1': self._bitrate = 64 streamList = self.getFavoriteStreamsList( self._favoritesStreamJson64k + "?" + listenkey) elif ADDON.getSetting('bitrate') == '2': self._bitrate = 128 streamList = self.getFavoriteStreamsList( self._favoritesStreamJson128k + "?" + listenkey) else: self._bitrate = 256 self._streamMimeType = 'audio/mpeg' streamList = self.getFavoriteStreamsList( self._favoritesStreamJson256k + "?" + listenkey) xbmc.log("Bitrate set to " + str(self._bitrate), xbmc.LOGNOTICE) for channel in streamList: self.addItem( channel['name'], channel['playlist'], channel["description"], channel['bitrate'], self._addonProfilePath + "art_" + channel['key'] + ".png", channel['isNew']) else: xbmc.log("Going for Public streams", xbmc.LOGNOTICE) jsonList = self.getJSONChannelList(self._publicStreamsJson40k) streamList = self.customizeStreamListAddMenuitem( jsonList, "") # sending a blank string as listenkey # save streams to cachefile pickle.dump(streamList, open(self._cacheStreams, "w"), protocol=0) if (self._newChannels > 0): # Yay! New channels found xbmc.log( ADDON.getLocalizedString(30130) + " " + ADDON.getLocalizedString(30131) + str(self._newChannels) + ADDON.getLocalizedString(30132) + " " + ADDON.getLocalizedString(30133) + " " + ADDON.getLocalizedString(30134), xbmc.LOGNOTICE) xbmcgui.Dialog().ok( ADDON.getLocalizedString(30130), ADDON.getLocalizedString(30131) + str(self._newChannels) + ADDON.getLocalizedString(30132), ADDON.getLocalizedString(30133), ADDON.getLocalizedString(30134)) else: xbmc.log("Using cached streams", xbmc.LOGNOTICE) streamList = pickle.load(open(self._cacheStreams, "r")) # Add streams to GUI for channel in streamList: self.addItem( channel['name'].encode('utf-8'), channel['playlist'], channel["description"], channel['bitrate'], self._addonProfilePath + "art_" + channel['key'] + ".png", channel['isNew']) # If streams should be sorted A-Z if ADDON.getSetting('sortaz') == "true": xbmcplugin.addSortMethod(HANDLE, sortMethod=xbmcplugin.SORT_METHOD_LABEL) # End of channel list xbmcplugin.endOfDirectory(HANDLE, succeeded=True) # Resets the 'Force refresh' setting ADDON.setSetting(id="forceupdate", value="false") return True """return list - False if it fails Gets the favorites playlist and returns the streams as a list Also every channel is added to the GUI from here, as the progress indication in the GUI would not reflect that something is actually happening till the very end """ def customizeStreamListAddMenuitem(self, list, listenkey): # Precompiling regexes streamurl_re = re.compile('File\d+=([^\n]*)', re.I) # streams in .pls file streamList = [] # Will add list elements to a new list, with a few additions for channel in list: channel['key'] = self.makeChannelIconname( channel['name'] ) # customize the key that is used to find channelart channel[ 'isNew'] = False # is used to highlight when it's a new channel channelArt = "art_" + channel['key'] + ".png" channel['bitrate'] = self._bitrate channel["description"] = channel["description"].encode('utf-8') if ADDON.getSetting( 'username' ) != "": # append listenkey to playlist url if username is set channel['playlist'] = self.getFirstStream( channel['playlist'] + "?" + listenkey, streamurl_re) else: channel['playlist'] = self.getFirstStream( channel['playlist'], streamurl_re) if (not os.path.isfile(self._addonProfilePath + channelArt) ): # if channelart is not in cache xbmc.log( "Channelart for " + channel['name'].encode("ascii", "ignore") + " not found in cache at " + self._addonProfilePath + channelArt, xbmc.LOGNOTICE) self.getChannelArt(channel['id'], "art_" + channel['key']) channel['isNew'] = True self._newChannels = self._newChannels + 1 streamList.append(channel) # I'd have prefeered it if I didn't have to add menuitem from within this method # but I have to, too give the user some visual feedback that stuff is happening self.addItem( channel['name'].encode('utf-8'), channel['playlist'], channel["description"], self._bitrate, self._addonProfilePath + "art_" + channel['key'] + ".png", channel['isNew']) return streamList # returns the channellist so it can be saved to cache """return bool Will check if channelart/icon is present in cache - if not, try to download """ def getChannelArt(self, channelId, channelKey): channelArt_re = re.compile( 'data-id="' + str(channelId) + '">(?:[\n\s]*)<a(?:[^>]*)><img(?:[^>]*)src="([^"]*)"', re.I) try: if (self._frontpageHtml == "" ): # If frontpage html has not already been downloaded, do it self._frontpageHtml = self._httpComm.get(self._baseUrl) channelartDict = channelArt_re.findall(self._frontpageHtml) # Will download and save the channelart to the cache self._httpComm.getImage( channelartDict[0], self._addonProfilePath + channelKey + ".png") return True except Exception: sys.exc_clear( ) # Clears all exceptions so the script will continue to run xbmcgui.Dialog().ok( ADDON.getLocalizedString(30160), ADDON.getLocalizedString(30161), ADDON.getLocalizedString(30162) + channelartDict[0]) xbmc.log( ADDON.getLocalizedString(30160) + " " + ADDON.getLocalizedString(30161) + channelKey + " " + ADDON.getLocalizedString(30162) + channelartDict[0], xbmc.LOGERROR) return False return True """return String Extracts the premium listenkey from the listenkey page html """ def getListenkey(self): listenkey_re = re.compile('Key is:<br />[^<]*<strong>([\w\d]*)<', re.DOTALL) try: logindata = urllib.urlencode({ 'member_session[username]': ADDON.getSetting('username'), 'member_session[password]': ADDON.getSetting('password') }) self._httpComm.post( self._loginUrl, logindata) # logs in so the listenkey page is accessible listenkeyHtml = self._httpComm.get(self._listenkeyUrl) listenkeyDict = listenkey_re.findall(listenkeyHtml) xbmc.log("Found listenkey", xbmc.LOGNOTICE) return listenkeyDict[0] except Exception: sys.exc_clear( ) # Clears all exceptions so the script will continue to run xbmcgui.Dialog().ok(ADDON.getLocalizedString(30100), ADDON.getLocalizedString(30101), ADDON.getLocalizedString(30102)) xbmc.log( ADDON.getLocalizedString(30100) + " " + ADDON.getLocalizedString(30101) + " " + ADDON.getLocalizedString(30102), xbmc.LOGERROR) return False return False """return list - False if it fails Will get a HTML page containing JSON data, decode it and return """ def getJSONChannelList(self, url): try: jsonData = self._httpComm.get(url) jsonData = json.loads(jsonData) except Exception: # Show error message in XBMC GUI if failing to parse JSON sys.exc_clear( ) # Clears all exceptions so the script will continue to run xbmcgui.Dialog().ok(ADDON.getLocalizedString(30100), ADDON.getLocalizedString(30101), ADDON.getLocalizedString(30102)) xbmc.log( ADDON.getLocalizedString(30100) + " " + ADDON.getLocalizedString(30101) + " " + ADDON.getLocalizedString(30102), xbmc.LOGERROR) return False return jsonData """return list - False if it fails Gets the favorites playlist and returns the streams as a list """ def getFavoriteStreamsList(self, url): try: favoritesPlaylist = self._httpComm.get( url) # favorites .pls playlist in plaintext favoritesList = [] # list that will contain streamlist streamurl_re = re.compile('File\d+=([^\n]*)', re.I) # first stream in .pls file channeltitle_re = re.compile('Title\d+=([^\n]*)', re.I) streamTitles = channeltitle_re.findall(favoritesPlaylist) streamUrls = streamurl_re.findall(favoritesPlaylist) if len(streamUrls) == len( streamTitles ): # only continue if the count of urls and titles are equal for i in range(len(streamUrls)): listitem = {} listitem['playlist'] = streamUrls[i] listitem['name'] = streamTitles[i].replace( parser.get('plugin', 'playlistStripName') + " ", "" ) # favorite stream titles has some "fluff" text it that is removed listitem['key'] = self.makeChannelIconname( listitem['name']) listitem['isNew'] = False listitem['bitrate'] = self._bitrate listitem['description'] = "" favoritesList.append(listitem) else: return False return favoritesList except Exception: # Show error message in XBMC GUI if failing to parse JSON #sys.exc_clear() # Clears all exceptions so the script will continue to run xbmcgui.Dialog().ok(ADDON.getLocalizedString(30120), ADDON.getLocalizedString(30111), url) xbmc.log( ADDON.getLocalizedString(30120) + " " + ADDON.getLocalizedString(30111) + " " + url, xbmc.LOGERROR) return False return favoritesList """return string Will take a channelname, lowercase it and remove spaces, dashes and other special characters The string returned is normally used as part of the filename for the channelart """ def makeChannelIconname(self, channelname): iconreplacement_re = re.compile( '[^a-z0-9]', re.I) # regex that hits everything but a-z and 0-9 iconname = string.lower(iconreplacement_re.sub('', channelname)) return iconname """return bool Simply adds a music item to the XBMC GUI """ # Adds item to XBMC itemlist def addItem(self, channelTitle, streamUrl, streamDescription, streamBitrate, icon, isNewChannel): if isNewChannel == True: # tart it up a bit if it's a new channel li = xbmcgui.ListItem(label="[COLOR FF007EFF]" + channelTitle + "[/COLOR]", thumbnailImage=icon) xbmc.log("New channel found: " + channelTitle, xbmc.LOGERROR) else: li = xbmcgui.ListItem(label=channelTitle, thumbnailImage=icon) li.setProperty("mimetype", self._streamMimeType) li.setInfo(type="Music", infoLabels={ "label": channelTitle, "Genre": channelTitle, "Comment": streamDescription, "Size": (streamBitrate * 1024) }) li.setProperty("IsPlayable", "true") li.setProperty("IsLive", "true") xbmcplugin.addDirectoryItem(handle=HANDLE, url=streamUrl, listitem=li, isFolder=False) return True """return string Gets the first stream from a playlist """ def getFirstStream(self, playlistUrl, regex): plsData = self._httpComm.get(playlistUrl) streamurls = regex.findall(plsData) return streamurls[0] """return bool Checks if a file is older than x seconds """ def checkFileTime(self, tmpfile, cachedir, timesince): if not os.path.exists(cachedir): os.makedirs(cachedir) return False # If file exists, check timestamp if os.path.exists(tmpfile): if os.path.getmtime(tmpfile) > (time.time() - timesince): xbmc.log( 'It has not been ' + str(timesince / 60) + ' minutes since ' + tmpfile + ' was last updated', xbmc.LOGNOTICE) return False else: xbmc.log('The cachefile ' + tmpfile + ' + has expired', xbmc.LOGNOTICE) return True # If file does not exist, return true so the file will be created by scraping the page else: xbmc.log('The cachefile ' + tmpfile + ' does not exist', xbmc.LOGNOTICE) return True
def getStreams(self): global LABELCOLOR global HTTPCOMM # will be cached streamurls = [] streamtitles = [] streamisnew = [] streambitrate = 40 channelicons = None # Precompiling regexes iconreplacement_re = re.compile("[ '-]", re.I) # generic regex for iconnames streamurl_re = re.compile("File\d+=([^\n]*)", re.I) # first stream in .pls file channeltitle_re = re.compile("Title\d+=([^\n]*)", re.I) channelicon_re = re.compile('="[\d\w\s]+" src="([\d\w:\/\.]+)"', re.I) playlist_re = None HTTPCOMM = HTTPComm() # Init CURL thingy # Check if cachefiles has expired if ( (int(ADDON.getSetting("cacheexpire")) * 60) != 0 and self.checkFileTime(STREAMURLSCACHE, (int(ADDON.getSetting("cacheexpire")) * 60)) == True ) or ADDON.getSetting("forceupdate") == "true": # If username NOT set, get public streams if ADDON.getSetting("username") == "": xbmc.log("Refreshing public streams", xbmc.LOGNOTICE) # Get frontpage of di.fm - if it fails, show a dialog in XBMC try: htmlData = HTTPCOMM.get(BASEURL) except Exception: xbmcgui.Dialog().ok( ADDON.getLocalizedString(30100), ADDON.getLocalizedString(30101), ADDON.getLocalizedString(30102), ) xbmc.log( "Connection error - Could not connect to di.fm - Check your internet connection", xbmc.LOGERROR ) return False # precompiling regexes playlist_re = re.compile( 'AAC-HE</a>[\s\r\n]*<ul>(?:.+?(?!</ul>))<li><a href="([^"]+(?=(?:\.pls))[^"]+)">40k', re.DOTALL ) playlists = playlist_re.findall(htmlData) xbmc.log("Found " + str(len(playlists)) + " streams", xbmc.LOGNOTICE) channelicons = channelicon_re.findall(htmlData) xbmc.log("Found " + str(len(channelicons)) + " pieces of channelart", xbmc.LOGNOTICE) if len(playlists) == 0: xbmcgui.Dialog().ok( ADDON.getLocalizedString(30110), ADDON.getLocalizedString(30111), ADDON.getLocalizedString(30112), ) return False # output public streams to XBMC for index, item in enumerate(playlists): playlist = HTTPCOMM.get(item) if playlist: LABELCOLOR = "FF0000" streamurl = streamurl_re.findall(playlist) streamtitle = channeltitle_re.findall(playlist) streamtitle = streamtitle[0] streamtitle = streamtitle.replace("Digitally Imported - ", "") icon = ART_DIR + string.lower(iconreplacement_re.sub("", streamtitle) + ".png") if not self.getStreamicon(icon, channelicons[index]): # if False is returned, use plugin icon icon = xbmc.translatePath(os.path.join(ADDON.getAddonInfo("path"), "")) + "icon.png" # will highlight new channels/has new channelart if LABELCOLOR != "FF0000": self.addItem(streamtitle, streamurl[0], streambitrate, icon, LABELCOLOR) else: self.addItem(streamtitle, streamurl[0], streambitrate, icon, False) streamtitles.append(streamtitle) # for caching streamurls.append(streamurl[0]) streamisnew.append(LABELCOLOR) else: xbmcgui.Dialog().ok(ADDON.getLocalizedString(30120), ADDON.getLocalizedString(30111), item) xbmc.log( "Connection timed out - Could not get the playlist from this url:" + item, xbmc.LOGERROR ) # Get premium streams elif ADDON.getSetting("username") != "": logindata = urllib.urlencode( { "member_session[username]": ADDON.getSetting("username"), "member_session[password]": ADDON.getSetting("password"), } ) if ADDON.getSetting("bitrate") == "0": streambitrate = 40 elif ADDON.getSetting("bitrate") == "1": streambitrate = 64 else: streambitrate = 128 xbmc.log("Stream bitrate chosen: " + str(streambitrate), xbmc.LOGNOTICE) # Login and get frontpage of di.fm - if it fails, show a dialog in XBMC try: htmlData = HTTPCOMM.post(PREMIUMURL, logindata) except Exception: xbmcgui.Dialog().ok( ADDON.getLocalizedString(30100), ADDON.getLocalizedString(30101), ADDON.getLocalizedString(30102), ) xbmc.log( "Connection error - Could not connect to di.fm - Check your internet connection", xbmc.LOGERROR ) return False channelicons = channelicon_re.findall(htmlData) playlist_re = re.compile( 'AAC-HE</a>[\s\r\n]*<ul>(?:.+?(?!</ul>))<li><a href="([^"]+(?=(?:\.pls))[^"]+)">' + str(streambitrate) + "k", re.DOTALL, ) playlists = playlist_re.findall(htmlData) if len(playlists) == 0: xbmcgui.Dialog().ok( ADDON.getLocalizedString(30110), ADDON.getLocalizedString(30111), ADDON.getLocalizedString(30112), ) return False # output premium streams to XBMC if ADDON.getSetting("usefavorites") == "false": xbmc.log("Refreshing premium streams", xbmc.LOGNOTICE) playlists.pop(0) # removes the favorites playlist for index, item in enumerate(playlists): playlist = HTTPCOMM.get(item) if playlist: LABELCOLOR = "FF0000" streamurl = streamurl_re.findall(playlist) streamtitle = channeltitle_re.findall(playlist) streamtitle = streamtitle[0] streamtitle = streamtitle.replace("Digitally Imported - ", "") icon = ART_DIR + string.lower(iconreplacement_re.sub("", streamtitle) + ".png") if not self.getStreamicon( icon, channelicons[index] ): # if False is returned, use plugin icon icon = xbmc.translatePath(os.path.join(ADDON.getAddonInfo("path"), "")) + "icon.png" # will highlight new channels/has new channelart if LABELCOLOR != "FF0000": self.addItem(streamtitle, streamurl[0], streambitrate, icon, LABELCOLOR) else: self.addItem(streamtitle, streamurl[0], streambitrate, icon, False) streamtitles.append(streamtitle) # for caching streamurls.append(streamurl[0]) streamisnew.append(LABELCOLOR) else: xbmcgui.Dialog().ok(ADDON.getLocalizedString(30120), ADDON.getLocalizedString(30111), item) xbmc.log( "Connection timed out - Could not get the playlist from this url:" + item, xbmc.LOGERROR ) # Output premium favorite streams to XBMC elif ADDON.getSetting("usefavorites") == "true": xbmc.log("Refreshing premium favorite streams", xbmc.LOGNOTICE) playlist = HTTPCOMM.get(playlists[0]) if playlist: favstreamurls = streamurl_re.findall(playlist) favstreamtitles = channeltitle_re.findall(playlist) for index, item in enumerate(favstreamurls): LABELCOLOR = "FF0000" streamtitle = favstreamtitles[index] streamtitle = streamtitle.replace("Digitally Imported - ", "") icon = ART_DIR + string.lower(iconreplacement_re.sub("", streamtitle) + ".png") if not self.getStreamicon(icon, False): # if False is returned, use plugin icon icon = xbmc.translatePath(os.path.join(ADDON.getAddonInfo("path"), "")) + "icon.png" if LABELCOLOR != "FF0000": # will highlight new channels/is missing channelart self.addItem(streamtitle, favstreamurls[index], streambitrate, icon, LABELCOLOR) else: self.addItem(streamtitle, favstreamurls[index], streambitrate, icon, False) streamtitles.append(streamtitle) # for caching streamurls.append(favstreamurls[index]) streamisnew.append(LABELCOLOR) else: xbmcgui.Dialog().ok(ADDON.getLocalizedString(30120), ADDON.getLocalizedString(30111), item) xbmc.log( "Connection timed out - Could not get the playlist from this url:" + item, xbmc.LOGERROR ) xbmc.log("Found " + str(len(playlists)) + " streams", xbmc.LOGNOTICE) # Write channels to cache pickle.dump(streamurls, open(STREAMURLSCACHE, "w"), protocol=0) pickle.dump(streamtitles, open(STREAMTITLESCACHE, "w"), protocol=0) pickle.dump(streambitrate, open(STREAMBITRATECACHE, "w"), protocol=0) pickle.dump(streamisnew, open(STREAMLABELCOLORCACHE, "w"), protocol=0) if NEWSTREAMS > 0: # Yay! New channels found xbmc.log( "New channels found - There was found " + str(NEWSTREAMS) + " new piece(s) of channelart - Meaning there could be new channels", xbmc.LOGNOTICE, ) xbmcgui.Dialog().ok( ADDON.getLocalizedString(30130), ADDON.getLocalizedString(30131) + str(NEWSTREAMS) + ADDON.getLocalizedString(30132), ADDON.getLocalizedString(30133), ADDON.getLocalizedString(30134), ) # Resets the 'Force refresh' setting ADDON.setSetting(id="forceupdate", value="false") else: if ( not os.path.isfile(STREAMTITLESCACHE) or not os.path.isfile(STREAMURLSCACHE) or not os.path.isfile(STREAMBITRATECACHE) or not os.path.isfile(STREAMLABELCOLORCACHE) ): xbmc.log( 'Cachefiles are missing - At least one of the cachefiles is missing please go to the addon settings and select "Force cache refresh"', xbmc.LOGERROR, ) xbmcgui.Dialog().ok( ADDON.getLocalizedString(30140), ADDON.getLocalizedString(30141), ADDON.getLocalizedString(30142), ADDON.getLocalizedString(30143), ) return False streamurls = pickle.load(open(STREAMURLSCACHE, "r")) # load streams from cache streamtitles = pickle.load(open(STREAMTITLESCACHE, "r")) # load streamtitles from cache streambitrate = pickle.load(open(STREAMBITRATECACHE, "r")) # load stream bitrate from cache streamisnew = pickle.load(open(STREAMLABELCOLORCACHE, "r")) # load stream 'is new' from cache # Output cache list of streams to XBMC for index, item in enumerate(streamurls): playlist = item streamurl = str(item) streamtitle = streamtitles[index] icon = ART_DIR + string.lower(iconreplacement_re.sub("", streamtitle) + ".png") if not self.getStreamicon(icon, False): # if False is returned, use plugin icon icon = xbmc.translatePath(os.path.join(ADDON.getAddonInfo("path"), "")) + "icon.png" if streamisnew[index] != "FF0000": self.addItem(streamtitle, streamurl, streambitrate, icon, streamisnew[index]) else: self.addItem(streamtitle, streamurl, streambitrate, icon, False) if NEWSTREAMS < 0: # Missing channelart dialog xbmc.log( "Channelart missing - There is " + str(abs(NEWSTREAMS)) + " piece(s) of channelart missing - You should refresh your cache - Disable using 'My Favorites' to get new channelart", xbmc.LOGWARNING, ) xbmcgui.Dialog().ok( ADDON.getLocalizedString(30150), ADDON.getLocalizedString(30151) + str(abs(NEWSTREAMS)) + ADDON.getLocalizedString(30152), ADDON.getLocalizedString(30153), ADDON.getLocalizedString(30154), ) return True
def getStreams(self) : global LABELCOLOR global HTTPCOMM # will be cached streamurls = [] streamtitles = [] streamisnew = [] streambitrate = 40 channelicons = None # Precompiling regexes iconreplacement_re = re.compile('[ \'-]', re.I) # generic regex for iconnames streamurl_re = re.compile('File\d+=([^\n]*)', re.I) # first stream in .pls file channeltitle_re = re.compile('Title\d+=([^\n]*)', re.I) channelicon_re = re.compile('="[\d\w\s]+" src="([\d\w:\/\.]+)"', re.I) playlist_re = None HTTPCOMM = HTTPComm() # Init CURL thingy # Check if cachefiles has expired if ((int( ADDON.getSetting("cacheexpire") ) * 60) != 0 and self.checkFileTime(STREAMURLSCACHE, (int( ADDON.getSetting("cacheexpire") ) * 60)) == True) or ADDON.getSetting("forceupdate") == "true" : # If username NOT set, get public streams if ADDON.getSetting('username') == "" : xbmc.log( "Refreshing public streams", xbmc.LOGNOTICE ) # Get frontpage of di.fm - if it fails, show a dialog in XBMC try : htmlData = HTTPCOMM.get( BASEURL ) except Exception: xbmcgui.Dialog().ok( ADDON.getLocalizedString(30100), ADDON.getLocalizedString(30101), ADDON.getLocalizedString(30102) ) xbmc.log( 'Connection error - Could not connect to di.fm - Check your internet connection', xbmc.LOGERROR ) return False # precompiling regexes playlist_re = re.compile('AAC-HE</a>[\s\r\n]*<ul>(?:.+?(?!</ul>))<li><a href="([^"]+(?=(?:\.pls))[^"]+)">40k', re.DOTALL) playlists = playlist_re.findall(htmlData) xbmc.log( 'Found ' + str(len(playlists)) + ' streams', xbmc.LOGNOTICE ) channelicons = channelicon_re.findall(htmlData) xbmc.log( 'Found ' + str(len(channelicons)) + ' pieces of channelart', xbmc.LOGNOTICE ) if len(playlists) == 0 : xbmcgui.Dialog().ok( ADDON.getLocalizedString(30110), ADDON.getLocalizedString(30111), ADDON.getLocalizedString(30112) ) return False # output public streams to XBMC for index, item in enumerate(playlists): playlist = HTTPCOMM.get(item) if (playlist) : LABELCOLOR = 'FF0000' streamurl = streamurl_re.findall(playlist) streamtitle = channeltitle_re.findall(playlist) streamtitle = streamtitle[0] streamtitle = streamtitle.replace("Digitally Imported - ", "") icon = ART_DIR + string.lower(iconreplacement_re.sub('', streamtitle) + ".png") if(not self.getStreamicon( icon, channelicons[index] )) : # if False is returned, use plugin icon icon = xbmc.translatePath( os.path.join( ADDON.getAddonInfo('path'), '' ) ) + 'icon.png' # will highlight new channels/has new channelart if LABELCOLOR != 'FF0000' : self.addItem(streamtitle, streamurl[0], streambitrate, icon, LABELCOLOR) else : self.addItem(streamtitle, streamurl[0], streambitrate, icon, False) streamtitles.append(streamtitle) # for caching streamurls.append(streamurl[0]) streamisnew.append(LABELCOLOR) else : xbmcgui.Dialog().ok(ADDON.getLocalizedString(30120), ADDON.getLocalizedString(30111), item) xbmc.log( 'Connection timed out - Could not get the playlist from this url:' + item, xbmc.LOGERROR ) # Get premium streams elif ( ADDON.getSetting('username') != "" ) : logindata = urllib.urlencode({ 'member_session[username]': ADDON.getSetting('username'), 'member_session[password]': ADDON.getSetting('password') }) if ADDON.getSetting('bitrate') == '0' : streambitrate = 40 elif ADDON.getSetting('bitrate') == '1' : streambitrate = 64 else : streambitrate = 128 xbmc.log( 'Stream bitrate chosen: ' + str(streambitrate), xbmc.LOGNOTICE ) # Login and get frontpage of di.fm - if it fails, show a dialog in XBMC try : htmlData = HTTPCOMM.post( PREMIUMURL, logindata ) except Exception: xbmcgui.Dialog().ok( ADDON.getLocalizedString(30100), ADDON.getLocalizedString(30101), ADDON.getLocalizedString(30102) ) xbmc.log( 'Connection error - Could not connect to di.fm - Check your internet connection', xbmc.LOGERROR ) return False channelicons = channelicon_re.findall(htmlData) playlist_re = re.compile('AAC-HE</a>[\s\r\n]*<ul>(?:.+?(?!</ul>))<li><a href="([^"]+(?=(?:\.pls))[^"]+)">' + str(streambitrate) + 'k', re.DOTALL) playlists = playlist_re.findall(htmlData) if len(playlists) == 0 : xbmcgui.Dialog().ok( ADDON.getLocalizedString(30110), ADDON.getLocalizedString(30111), ADDON.getLocalizedString(30112) ) return False # output premium streams to XBMC if ADDON.getSetting("usefavorites") == 'false' : xbmc.log( "Refreshing premium streams", xbmc.LOGNOTICE ) playlists.pop(0) # removes the favorites playlist for index, item in enumerate(playlists): playlist = HTTPCOMM.get(item) if (playlist) : LABELCOLOR = 'FF0000' streamurl = streamurl_re.findall(playlist) streamtitle = channeltitle_re.findall(playlist) streamtitle = streamtitle[0] streamtitle = streamtitle.replace("Digitally Imported - ", "") icon = ART_DIR + string.lower(iconreplacement_re.sub('', streamtitle) + ".png") if(not self.getStreamicon( icon, channelicons[index] )) : # if False is returned, use plugin icon icon = xbmc.translatePath( os.path.join( ADDON.getAddonInfo('path'), '' ) ) + 'icon.png' # will highlight new channels/has new channelart if LABELCOLOR != 'FF0000' : self.addItem(streamtitle, streamurl[0], streambitrate, icon, LABELCOLOR) else : self.addItem(streamtitle, streamurl[0], streambitrate, icon, False) streamtitles.append(streamtitle) # for caching streamurls.append(streamurl[0]) streamisnew.append(LABELCOLOR) else : xbmcgui.Dialog().ok(ADDON.getLocalizedString(30120), ADDON.getLocalizedString(30111), item) xbmc.log( 'Connection timed out - Could not get the playlist from this url:' + item, xbmc.LOGERROR ) # Output premium favorite streams to XBMC elif ADDON.getSetting("usefavorites") == 'true' : xbmc.log( "Refreshing premium favorite streams", xbmc.LOGNOTICE ) playlist = HTTPCOMM.get(playlists[0]) if (playlist) : favstreamurls = streamurl_re.findall(playlist) favstreamtitles = channeltitle_re.findall(playlist) for index, item in enumerate(favstreamurls): LABELCOLOR = 'FF0000' streamtitle = favstreamtitles[index] streamtitle = streamtitle.replace("Digitally Imported - ", "") icon = ART_DIR + string.lower(iconreplacement_re.sub('', streamtitle) + ".png") if(not self.getStreamicon( icon, False )) : # if False is returned, use plugin icon icon = xbmc.translatePath( os.path.join( ADDON.getAddonInfo('path'), '' ) ) + 'icon.png' if LABELCOLOR != 'FF0000' : # will highlight new channels/is missing channelart self.addItem(streamtitle, favstreamurls[index], streambitrate, icon, LABELCOLOR) else : self.addItem(streamtitle, favstreamurls[index], streambitrate, icon, False) streamtitles.append(streamtitle) # for caching streamurls.append(favstreamurls[index]) streamisnew.append(LABELCOLOR) else : xbmcgui.Dialog().ok( ADDON.getLocalizedString(30120), ADDON.getLocalizedString(30111), item ) xbmc.log( 'Connection timed out - Could not get the playlist from this url:' + item, xbmc.LOGERROR ) xbmc.log( 'Found ' + str(len(playlists)) + ' streams', xbmc.LOGNOTICE ) # Write channels to cache pickle.dump(streamurls, open(STREAMURLSCACHE, "w"), protocol=0) pickle.dump(streamtitles, open(STREAMTITLESCACHE, "w"), protocol=0) pickle.dump(streambitrate, open(STREAMBITRATECACHE, "w"), protocol=0) pickle.dump(streamisnew, open(STREAMLABELCOLORCACHE, "w"), protocol=0) if (NEWSTREAMS > 0) : # Yay! New channels found xbmc.log( 'New channels found - There was found ' + str(NEWSTREAMS) + ' new piece(s) of channelart - Meaning there could be new channels', xbmc.LOGNOTICE ) xbmcgui.Dialog().ok( ADDON.getLocalizedString(30130), ADDON.getLocalizedString(30131) + str(NEWSTREAMS) + ADDON.getLocalizedString(30132), ADDON.getLocalizedString(30133),ADDON.getLocalizedString(30134) ) # Resets the 'Force refresh' setting ADDON.setSetting(id="forceupdate", value="false") else : if not os.path.isfile(STREAMTITLESCACHE) or not os.path.isfile(STREAMURLSCACHE) or not os.path.isfile(STREAMBITRATECACHE) or not os.path.isfile(STREAMLABELCOLORCACHE) : xbmc.log( 'Cachefiles are missing - At least one of the cachefiles is missing please go to the addon settings and select "Force cache refresh"', xbmc.LOGERROR ) xbmcgui.Dialog().ok( ADDON.getLocalizedString(30140), ADDON.getLocalizedString(30141), ADDON.getLocalizedString(30142), ADDON.getLocalizedString(30143) ) return False streamurls = pickle.load(open(STREAMURLSCACHE, "r")) # load streams from cache streamtitles = pickle.load(open(STREAMTITLESCACHE, "r")) # load streamtitles from cache streambitrate = pickle.load(open(STREAMBITRATECACHE, "r")) # load stream bitrate from cache streamisnew = pickle.load(open(STREAMLABELCOLORCACHE, "r")) # load stream 'is new' from cache # Output cache list of streams to XBMC for index, item in enumerate(streamurls): playlist = item streamurl = str(item) streamtitle = streamtitles[index] icon = ART_DIR + string.lower(iconreplacement_re.sub('', streamtitle) + ".png") if(not self.getStreamicon( icon, False )) : # if False is returned, use plugin icon icon = xbmc.translatePath( os.path.join( ADDON.getAddonInfo('path'), '' ) ) + 'icon.png' if streamisnew[index] != 'FF0000' : self.addItem(streamtitle, streamurl, streambitrate, icon, streamisnew[index]) else : self.addItem(streamtitle, streamurl, streambitrate, icon, False) if (NEWSTREAMS < 0) : # Missing channelart dialog xbmc.log( "Channelart missing - There is " + str(abs(NEWSTREAMS)) + " piece(s) of channelart missing - You should refresh your cache - Disable using 'My Favorites' to get new channelart", xbmc.LOGWARNING ) xbmcgui.Dialog().ok( ADDON.getLocalizedString(30150), ADDON.getLocalizedString(30151) + str(abs(NEWSTREAMS)) + ADDON.getLocalizedString(30152), ADDON.getLocalizedString(30153), ADDON.getLocalizedString(30154)) return True