Newer
Older
# -*- coding: utf-8 -*-
# Copyright(C) 2013 Pierre Mazière
#
# This file is part of weboob.
#
# weboob is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# weboob is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with weboob. If not, see <http://www.gnu.org/licenses/>.
from weboob.capabilities.radio import CapRadio, Radio
from weboob.capabilities.audiostream import BaseAudioStream
from weboob.tools.capabilities.streaminfo import StreamInfo
from weboob.capabilities.collection import CapCollection, Collection
from weboob.tools.backend import Module, BackendConfig
from weboob.deprecated.browser import StandardBrowser
# AudioAddict playlists do not seem to be appreciated by mplayer
# VLC plays them successfully, therefore I advice to set the media_player
# option to another player in the ~/.config/weboob/radioob config file:
# [ROOT]
# media_player = your_non_mplayer_player
class AudioAddictModule(Module, CapRadio, CapCollection):
NAME = 'audioaddict'
MAINTAINER = u'Pierre Mazière'
EMAIL = 'pierre.maziere@gmx.com'
DESCRIPTION = u'Internet radios powered by audioaddict.com services'
LICENSE = 'AGPLv3+'
BROWSER = StandardBrowser
# Data extracted from http://tobiass.eu/api-doc.html
NETWORKS = {
'DI': {
'desc': 'Digitally Imported addictive electronic music',
'domain': 'di.fm',
'android': {'rate': 64, 'fmt': 'aac'},
'android_high': {'rate': 96, 'fmt': 'aac'},
'android_premium_low': {'rate': 40, 'fmt': 'aac'},
'android_premium_medium': {'rate': 64, 'fmt': 'aac'},
'android_premium': {'rate': 128, 'fmt': 'aac'},
'android_premium_high': {'rate': 256, 'fmt': 'aac'},
'public1': {'rate': 64, 'fmt': 'aac'},
'public2': {'rate': 40, 'fmt': 'aac'},
'public3': {'rate': 96, 'fmt': 'mp3'},
'premium_low': {'rate': 40, 'fmt': 'aac'},
'premium_medium': {'rate': 64, 'fmt': 'aac'},
'premium': {'rate': 128, 'fmt': 'aac'},
'premium_high': {'rate': 256, 'fmt': 'mp3'}
}
},
'RadioTunes': {
'desc': 'Radio Tunes',
'domain': 'radiotunes.com',
'streams': {'appleapp_low': {'rate': 40, 'fmt': 'aac'},
'appleapp': {'rate': 64, 'fmt': 'aac'},
'appleapp_high': {'rate': 96, 'fmt': 'mp3'},
'appleapp_premium_medium': {'rate': 64, 'fmt': 'aac'},
'appleapp_premium': {'rate': 128, 'fmt': 'aac'},
'appleapp_premium_high': {'rate': 256, 'fmt': 'mp3'},
'public1': {'rate': 40, 'fmt': 'aac'},
'public5': {'rate': 40, 'fmt': 'wma'},
'public3': {'rate': 96, 'fmt': 'mp3'},
'premium_low': {'rate': 40, 'fmt': 'aac'},
'premium_medium': {'rate': 64, 'fmt': 'aac'},
'premium': {'rate': 128, 'fmt': 'aac'},
'premium_high': {'rate': 256, 'fmt': 'mp3'}
}
},
'JazzRadio': {
'desc': 'Jazz Radio',
'domain': 'jazzradio.com',
'streams': {'appleapp_low': {'rate': 40, 'fmt': 'aac'},
'appleapp': {'rate': 64, 'fmt': 'aac'},
'appleapp_premium_medium': {'rate': 64, 'fmt': 'aac'},
'appleapp_premium': {'rate': 128, 'fmt': 'aac'},
'appleapp_premium_high': {'rate': 256, 'fmt': 'mp3'},
'public1': {'rate': 40, 'fmt': 'aac'},
'public3': {'rate': 64, 'fmt': 'mp3'},
'premium_low': {'rate': 40, 'fmt': 'aac'},
'premium_medium': {'rate': 64, 'fmt': 'aac'},
'premium': {'rate': 128, 'fmt': 'aac'},
'premium_high': {'rate': 256, 'fmt': 'mp3'}
}
},
'RockRadio': {
'desc': 'Rock Radio',
'domain': 'rockradio.com',
'streams': {'android_low': {'rate': 40, 'fmt': 'aac'},
'android': {'rate': 64, 'fmt': 'aac'},
'android_premium_medium': {'rate': 64, 'fmt': 'aac'},
'android_premium': {'rate': 128, 'fmt': 'aac'},
'android_premium_high': {'rate': 256, 'fmt': 'mp3'},
'public3': {'rate': 96, 'fmt': 'mp3'}
},
'FrescaRadio': {
'desc': 'Fresca Radio',
'domain': 'frescaradio.com',
'streams': {
'public3': {'rate': 96, 'fmt': 'mp3'}
}
' '.join(NETWORKS.keys()), default=''),
Value('quality', label='Radio streaming quality',
default='h')
)
def __init__(self, *a, **kw):
domain = self.NETWORKS[network]['domain']
url = 'http://api.audioaddict.com/v1/%s/track_history' %\
(domain[:domain.rfind('.')])
self.HISTORY[network] = self.browser.location(url)
return self.HISTORY
def create_default_browser(self):
return self.create_browser(parser='json')
def _get_stream_name(self, network, quality):
streamName = 'public3'
for name in self.NETWORKS[network]['streams'].keys():
if name.startswith('public') and \
self.NETWORKS[network]['streams'][name]['rate'] >= 64:
if quality == 'h':
streamName = name
def _fetch_radio_list(self, network=None):
quality = self.config['quality'].get()
for selectedNetwork in self.config['networks'].get().split():
if network is None or network == selectedNetwork:
if selectedNetwork not in self.RADIOS:
(self.NETWORKS[selectedNetwork]['domain'],
streamName))
for info in document:
radio = info['key']
self.RADIOS[selectedNetwork][radio] = {}
self.RADIOS[selectedNetwork][radio]['id'] = info['id']
self.RADIOS[selectedNetwork][radio]['name'] = info['name']
self.RADIOS[selectedNetwork][radio]['playlist'] = info['playlist']
return self.RADIOS
def iter_radios_search(self, pattern):
self._fetch_radio_list()
pattern = pattern.lower()
for network in self.config['networks'].get().split():
for radio in self.RADIOS[network]:
radio_dict = self.RADIOS[network][radio]
if pattern in radio_dict['name'].lower() :
yield self.get_radio(radio+"."+network)
def iter_resources(self, objs, split_path):
self._fetch_radio_list()
if Radio in objs:
for network in self.config['networks'].get().split():
if split_path == [network]:
for radio in self.RADIOS[network]:
yield self.get_radio(radio+"."+network)
return
for network in self.config['networks'].get().split():
if network not in self.HISTORY:
channel = self.HISTORY[network].get(str(self.RADIOS[network][radio]['id']))
channel = self.HISTORY[network].get(str(self.RADIOS[network][radio]['id']))
if channel is None:
return 'Unknown', 'Unknown'
if (channel.get('started')+channel.get('duration')) < now:
self._get_tracks_history(network)
channel=self.HISTORY[network].get(str(self.RADIOS[network][radio]['id']))
artist = u'' + (channel.get('artist', '') or 'Unknown')
title = u''+(channel.get('title', '') or 'Unknown')
if artist == 'Unknown':
track = u'' + (channel.get('track', '') or 'Unknown')
if track != 'Unknown':
artist = track[:track.find(' - ')]
if not isinstance(radio, Radio):
radio = Radio(radio)
if radioName not in self.RADIOS[network]:
radio_dict = self.RADIOS[network][radioName]
radio.description = radio_dict['name']
current.who = artist
current.what = title
defaultname = self._get_stream_name(network, self.config['quality'].get())
stream = BaseAudioStream(0)
stream.bitrate = self.NETWORKS[network]['streams'][defaultname]['rate']
stream.format = self.NETWORKS[network]['streams'][defaultname]['fmt']
stream.title = u'%s %skbps' % (stream.format, stream.bitrate)
stream.url = 'http://listen.%s/%s/%s.pls' %\
(self.NETWORKS[network]['domain'], defaultname, radioName)
for name in self.NETWORKS[network]['streams'].keys():
if name == defaultname:
continue
stream = BaseAudioStream(i)
stream.bitrate = self.NETWORKS[network]['streams'][name]['rate']
stream.format = self.NETWORKS[network]['streams'][name]['fmt']
stream.title = u'%s %skbps' % (stream.format, stream.bitrate)
stream.url = 'http://listen.%s/%s/%s.pls'%\
radio.streams.append(stream)
return radio
def fill_radio(self, radio, fields):
if 'current' in fields:
radio.current.who, radio.current.what = self.get_current(network, radioName)