diff --git a/modules/somafm/__init__.py b/modules/somafm/__init__.py new file mode 100644 index 00000000..45e74cda --- /dev/null +++ b/modules/somafm/__init__.py @@ -0,0 +1,24 @@ +# -*- coding: utf-8 -*- + +# Copyright(C) 2013 Roger Philibert +# +# 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 . + + +from .backend import SomaFMBackend + + +__all__ = ['DIFMBackend'] diff --git a/modules/somafm/backend.py b/modules/somafm/backend.py new file mode 100644 index 00000000..51020491 --- /dev/null +++ b/modules/somafm/backend.py @@ -0,0 +1,120 @@ +# -*- coding: utf-8 -*- + +# Copyright(C) 2013 Roger Philibert +# +# 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 . + + +import lxml.etree +from weboob.capabilities.radio import ICapRadio, Radio, Stream, Emission +from weboob.capabilities.collection import ICapCollection +from weboob.tools.backend import BaseBackend +from weboob.tools.browser import StandardBrowser +from weboob.tools.misc import to_unicode +from weboob.tools.parsers.iparser import IParser + + +__all__ = ['SomaFMBackend'] + + +class LxmlParser(IParser): + def parse(self, data, encoding=None): + return lxml.etree.fromstring(data.read()) + + +class SomaFMBackend(BaseBackend, ICapRadio, ICapCollection): + NAME = 'somafm' + MAINTAINER = u'Roger Philibert' + EMAIL = 'roger.philibert@gmail.com' + VERSION = '0.h' + DESCRIPTION = u'SomaFM web radio' + LICENSE = 'AGPLv3+' + BROWSER = StandardBrowser + + QUALITIES = ['fast', 'slow', 'highest'] + + ALLINFO = 'http://api.somafm.com/channels.xml' + + def create_default_browser(self): + return self.create_browser(parser=LxmlParser()) + + def _parse_current(self, data): + current = data.split(' - ') + if len(current) == 2: + return current + else: + return ('Unknown', 'Unknown') + + def _fetch_radio_list(self): + radios = [] + + document = self.browser.location(self.ALLINFO) + for channel in document.iter('channel'): + radio = Radio(channel.get('id')) + radio.title = channel.findtext('title') + radio.description = channel.findtext('description') + + current_data = channel.findtext('lastPlaying') + current = Emission(0) + current.artist, current.title = self._parse_current(current_data) + radio.current = current + + radio.streams = [] + stream_id = 0 + for subtag in channel: + if subtag.tag.endswith('pls'): + stream = Stream(stream_id) + stream.title = '%s/%s' % (subtag.tag.replace('pls', ''), subtag.get('format')) + stream.url = subtag.text + radio.streams.append(stream) + stream_id += 1 + + radios.append(radio) + + return radios + + def iter_radios_search(self, pattern): + radios = self._fetch_radio_list() + + pattern = pattern.lower() + for radio in radios: + if pattern in radio.title.lower() or pattern in radio.description.lower(): + yield radio + + def iter_resources(self, objs, split_path): + radios = self._fetch_radio_list() + + if Radio in objs: + self._restrict_level(split_path) + + for radio in radios: + yield radio + + def get_radio(self, radio_id): + radios = self._fetch_radio_list() + for radio in radios: + if radio_id == radio.id: + return radio + + def fill_radio(self, radio, fields): + if 'current' in fields: + if not radio.current: + radio.current = Emission(0) + radio.current.artist, radio.current.title = self.get_current(radio.id) + return radio + + #OBJECTS = {Radio: fill_radio} + diff --git a/modules/somafm/favicon.png b/modules/somafm/favicon.png new file mode 100644 index 00000000..5ecbeda9 Binary files /dev/null and b/modules/somafm/favicon.png differ diff --git a/modules/somafm/test.py b/modules/somafm/test.py new file mode 100644 index 00000000..e343b694 --- /dev/null +++ b/modules/somafm/test.py @@ -0,0 +1,43 @@ +# -*- coding: utf-8 -*- + +# Copyright(C) 2013 Roger Philibert +# +# 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 . + + +from weboob.tools.test import BackendTest +from weboob.capabilities.radio import Radio + + +class SomaFMTest(BackendTest): + BACKEND = 'somafm' + + def test_difm(self): + ls = list(self.backend.iter_resources((Radio, ), [])) + self.assertTrue(len(ls) > 0) + + search = list(self.backend.iter_radios_search('doom')) + self.assertTrue(len(search) > 0) + self.assertTrue(len(search) < len(ls)) + + radio = self.backend.get_radio('doomed') + self.assertTrue(radio.title) + self.assertTrue(radio.description) + self.assertTrue(radio.current.title) + self.assertTrue(radio.current.artist) + self.assertTrue(radio.streams[0].url) + self.assertTrue(radio.streams[0].title) +