././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1612990371.1848197 Mopidy-SomaFM-2.0.2/0000755000175000017500000000000000000000000014565 5ustar00alexandrealexandre././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1612990371.1814864 Mopidy-SomaFM-2.0.2/.circleci/0000755000175000017500000000000000000000000016420 5ustar00alexandrealexandre././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1612987304.0 Mopidy-SomaFM-2.0.2/.circleci/config.yml0000644000175000017500000000206500000000000020413 0ustar00alexandrealexandreversion: 2.1 orbs: codecov: codecov/codecov@1.0.5 workflows: version: 2 test: jobs: - py38 - py37 - black - check-manifest - flake8 jobs: py38: &test-template docker: - image: mopidy/ci-python:3.8 steps: - checkout - restore_cache: name: Restoring tox cache key: tox-v1-{{ .Environment.CIRCLE_JOB }}-{{ checksum "setup.cfg" }} - run: name: Run tests command: | tox -e $CIRCLE_JOB -- \ --junit-xml=test-results/pytest/results.xml \ --cov-report=xml - save_cache: name: Saving tox cache key: tox-v1-{{ .Environment.CIRCLE_JOB }}-{{ checksum "setup.cfg" }} paths: - ./.tox - ~/.cache/pip - codecov/upload: file: coverage.xml - store_test_results: path: test-results py37: <<: *test-template docker: - image: mopidy/ci-python:3.7 black: *test-template check-manifest: *test-template flake8: *test-template ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1612990213.0 Mopidy-SomaFM-2.0.2/CHANGES.rst0000644000175000017500000000344600000000000016376 0ustar00alexandrealexandreChangelog ========= v2.0.2 (2021-02-10) ------------------- - #40 Fix exception on URI parsing v2.0.1 (2021-01-07) ------------------- - #37 Fix image display (Thanks to @dreamlayers and @morithil) v2.0.0 (2020-03-11) ------------------- - #36 Ready for Mopidy 3.0 v2.0.0rc1 (2019-12-04) ---------------------- - #32 Migrate to Python 3.7 v1.1.0 (2017-10-14) ------------------- - #24: Graceful fallback - #28: Various fix (DJ as artist, station ordering) v1.0.1 (2016-01-19) ------------------- - Use httpclient helper from Mopidy >= 1.1 v0.8.0 (2015-11-09) ------------------- - #20: Replace HTTP with HTTPS for channels.xml v0.7.1 (2015-01-04) ------------------- - #11: Add Low Bitrate encoding (aacp) v0.7.0 (2014-07-29) ------------------- - #10: Remove playlists provider v0.6.0 (2014-03-15) ------------------- - Directly show PLS in browser - Add precision about 'quality' and 'encoding' couple v0.5.1 (2014-03-09) ------------------- - Fix doc typo v0.5.0 (2014-03-03) ------------------- - #5: Select prefered quality and format from config - Add tests and Travis-CI support v0.4.0 (2014-02-16) ------------------- - Add browse support for LibraryController v0.3.1 (2014-01-30) ------------------- - #3: Correct wrong subclassing v0.3.0 (2014-01-29) ------------------- - Require Mopidy >= 0.18 - Add proxy support for downloading SomaFM content - #1: handle 'requests' exceptions - Use builtin Mopidy's .pls support - Internal code cleanup v0.2.0 (2013-09-22) ------------------- - PLS files are downloaded to local temp directory - Implement library::lookup to allow adding tracks from playlist uri v0.1.1 (2013-09-14) ------------------- - Update Licence information v0.1.0 (2013-09-13) ------------------- - Initial release - Create SomaFM extension for Mopidy ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1612987304.0 Mopidy-SomaFM-2.0.2/LICENSE0000644000175000017500000000207600000000000015577 0ustar00alexandrealexandreThe MIT License (MIT) Copyright (c) 2019 Alexandre Petitjean Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1612987304.0 Mopidy-SomaFM-2.0.2/MANIFEST.in0000644000175000017500000000041200000000000016320 0ustar00alexandrealexandreinclude *.py include *.rst include .mailmap include LICENSE include MANIFEST.in include pyproject.toml include tox.ini recursive-include .circleci * recursive-include .github * include mopidy_*/ext.conf recursive-include tests *.py recursive-include tests/data * ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1612990371.1848197 Mopidy-SomaFM-2.0.2/Mopidy_SomaFM.egg-info/0000755000175000017500000000000000000000000020722 5ustar00alexandrealexandre././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1612990371.0 Mopidy-SomaFM-2.0.2/Mopidy_SomaFM.egg-info/PKG-INFO0000644000175000017500000000654200000000000022026 0ustar00alexandrealexandreMetadata-Version: 2.1 Name: Mopidy-SomaFM Version: 2.0.2 Summary: SomaFM extension for Mopidy Home-page: https://github.com/AlexandrePTJ/mopidy-somafm Author: Alexandre Petitjean Author-email: alpetitjean@gmail.com License: Apache License, Version 2.0 Description: ************* Mopidy-SomaFM ************* .. image:: https://img.shields.io/pypi/v/Mopidy-SomaFM :target: https://pypi.org/project/Mopidy-SomaFM/ :alt: Latest PyPI version .. image:: https://img.shields.io/circleci/build/gh/AlexandrePTJ/mopidy-somafm :target: https://circleci.com/gh/AlexandrePTJ/mopidy-somafm :alt: CircleCI build status .. image:: https://img.shields.io/codecov/c/gh/AlexandrePTJ/mopidy-somafm :target: https://codecov.io/gh/AlexandrePTJ/mopidy-somafm :alt: Test coverage SomaFM extension for Mopidy Installation ============ Debian/Ubuntu ------------- Install by running:: python3 -m pip install Mopidy-SomaFM Or, if available, install the Debian/Ubuntu package from `apt.mopidy.com `_. Configuration ============= Before starting Mopidy, you must add configuration for Mopidy-SomaFM to your Mopidy configuration file:: [somafm] encoding = aac quality = highest - ``encoding`` must be either ``aac``, ``mp3`` or ``aacp`` - ``quality`` must be one of ``highest``, ``fast``, ``slow``, ``firewall`` If the preferred quality is not available for a channel, the extension will fallback to ``fast``. And afterwards if the preferred encoding is not available for that quality, it will fallback to using ``mp3``. It seems that all channels support the combination ``fast`` + ``mp3`` You can also choose to use the channel DJ as the reported track artist (default behavior):: [somafm] dj_as_artist = true Project resources ================= - `Source code `_ - `Issue tracker `_ - `Changelog `_ Credits ======= - Original author: `Alexandre Petitjean `__ - Current maintainer: `Alexandre Petitjean `__ - `Contributors `_ Platform: UNKNOWN Classifier: Environment :: No Input/Output (Daemon) Classifier: Intended Audience :: End Users/Desktop Classifier: License :: OSI Approved :: Apache Software License Classifier: Operating System :: OS Independent Classifier: Programming Language :: Python :: 3 Classifier: Programming Language :: Python :: 3.7 Classifier: Programming Language :: Python :: 3.8 Classifier: Topic :: Multimedia :: Sound/Audio :: Players Requires-Python: >=3.7 Provides-Extra: lint Provides-Extra: release Provides-Extra: test Provides-Extra: dev ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1612990371.0 Mopidy-SomaFM-2.0.2/Mopidy_SomaFM.egg-info/SOURCES.txt0000644000175000017500000000101600000000000022604 0ustar00alexandrealexandreCHANGES.rst LICENSE MANIFEST.in README.rst pyproject.toml setup.cfg setup.py tox.ini .circleci/config.yml Mopidy_SomaFM.egg-info/PKG-INFO Mopidy_SomaFM.egg-info/SOURCES.txt Mopidy_SomaFM.egg-info/dependency_links.txt Mopidy_SomaFM.egg-info/entry_points.txt Mopidy_SomaFM.egg-info/not-zip-safe Mopidy_SomaFM.egg-info/requires.txt Mopidy_SomaFM.egg-info/top_level.txt mopidy_somafm/__init__.py mopidy_somafm/backend.py mopidy_somafm/ext.conf mopidy_somafm/somafm.py tests/__init__.py tests/test_extension.py tests/test_somafm.py././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1612990371.0 Mopidy-SomaFM-2.0.2/Mopidy_SomaFM.egg-info/dependency_links.txt0000644000175000017500000000000100000000000024770 0ustar00alexandrealexandre ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1612990371.0 Mopidy-SomaFM-2.0.2/Mopidy_SomaFM.egg-info/entry_points.txt0000644000175000017500000000005700000000000024222 0ustar00alexandrealexandre[mopidy.ext] somafm = mopidy_somafm:Extension ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1612987350.0 Mopidy-SomaFM-2.0.2/Mopidy_SomaFM.egg-info/not-zip-safe0000644000175000017500000000000100000000000023150 0ustar00alexandrealexandre ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1612990371.0 Mopidy-SomaFM-2.0.2/Mopidy_SomaFM.egg-info/requires.txt0000644000175000017500000000046200000000000023324 0ustar00alexandrealexandreMopidy>=3.0 Pykka>=2.0.1 requests>=2.0.0 setuptools [dev] black check-manifest flake8 flake8-bugbear flake8-import-order isort[pyproject] twine wheel pytest pytest-cov [lint] black check-manifest flake8 flake8-bugbear flake8-import-order isort[pyproject] [release] twine wheel [test] pytest pytest-cov ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1612990371.0 Mopidy-SomaFM-2.0.2/Mopidy_SomaFM.egg-info/top_level.txt0000644000175000017500000000001600000000000023451 0ustar00alexandrealexandremopidy_somafm ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1612990371.1848197 Mopidy-SomaFM-2.0.2/PKG-INFO0000644000175000017500000000654200000000000015671 0ustar00alexandrealexandreMetadata-Version: 2.1 Name: Mopidy-SomaFM Version: 2.0.2 Summary: SomaFM extension for Mopidy Home-page: https://github.com/AlexandrePTJ/mopidy-somafm Author: Alexandre Petitjean Author-email: alpetitjean@gmail.com License: Apache License, Version 2.0 Description: ************* Mopidy-SomaFM ************* .. image:: https://img.shields.io/pypi/v/Mopidy-SomaFM :target: https://pypi.org/project/Mopidy-SomaFM/ :alt: Latest PyPI version .. image:: https://img.shields.io/circleci/build/gh/AlexandrePTJ/mopidy-somafm :target: https://circleci.com/gh/AlexandrePTJ/mopidy-somafm :alt: CircleCI build status .. image:: https://img.shields.io/codecov/c/gh/AlexandrePTJ/mopidy-somafm :target: https://codecov.io/gh/AlexandrePTJ/mopidy-somafm :alt: Test coverage SomaFM extension for Mopidy Installation ============ Debian/Ubuntu ------------- Install by running:: python3 -m pip install Mopidy-SomaFM Or, if available, install the Debian/Ubuntu package from `apt.mopidy.com `_. Configuration ============= Before starting Mopidy, you must add configuration for Mopidy-SomaFM to your Mopidy configuration file:: [somafm] encoding = aac quality = highest - ``encoding`` must be either ``aac``, ``mp3`` or ``aacp`` - ``quality`` must be one of ``highest``, ``fast``, ``slow``, ``firewall`` If the preferred quality is not available for a channel, the extension will fallback to ``fast``. And afterwards if the preferred encoding is not available for that quality, it will fallback to using ``mp3``. It seems that all channels support the combination ``fast`` + ``mp3`` You can also choose to use the channel DJ as the reported track artist (default behavior):: [somafm] dj_as_artist = true Project resources ================= - `Source code `_ - `Issue tracker `_ - `Changelog `_ Credits ======= - Original author: `Alexandre Petitjean `__ - Current maintainer: `Alexandre Petitjean `__ - `Contributors `_ Platform: UNKNOWN Classifier: Environment :: No Input/Output (Daemon) Classifier: Intended Audience :: End Users/Desktop Classifier: License :: OSI Approved :: Apache Software License Classifier: Operating System :: OS Independent Classifier: Programming Language :: Python :: 3 Classifier: Programming Language :: Python :: 3.7 Classifier: Programming Language :: Python :: 3.8 Classifier: Topic :: Multimedia :: Sound/Audio :: Players Requires-Python: >=3.7 Provides-Extra: lint Provides-Extra: release Provides-Extra: test Provides-Extra: dev ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1612987304.0 Mopidy-SomaFM-2.0.2/README.rst0000644000175000017500000000376600000000000016270 0ustar00alexandrealexandre************* Mopidy-SomaFM ************* .. image:: https://img.shields.io/pypi/v/Mopidy-SomaFM :target: https://pypi.org/project/Mopidy-SomaFM/ :alt: Latest PyPI version .. image:: https://img.shields.io/circleci/build/gh/AlexandrePTJ/mopidy-somafm :target: https://circleci.com/gh/AlexandrePTJ/mopidy-somafm :alt: CircleCI build status .. image:: https://img.shields.io/codecov/c/gh/AlexandrePTJ/mopidy-somafm :target: https://codecov.io/gh/AlexandrePTJ/mopidy-somafm :alt: Test coverage SomaFM extension for Mopidy Installation ============ Debian/Ubuntu ------------- Install by running:: python3 -m pip install Mopidy-SomaFM Or, if available, install the Debian/Ubuntu package from `apt.mopidy.com `_. Configuration ============= Before starting Mopidy, you must add configuration for Mopidy-SomaFM to your Mopidy configuration file:: [somafm] encoding = aac quality = highest - ``encoding`` must be either ``aac``, ``mp3`` or ``aacp`` - ``quality`` must be one of ``highest``, ``fast``, ``slow``, ``firewall`` If the preferred quality is not available for a channel, the extension will fallback to ``fast``. And afterwards if the preferred encoding is not available for that quality, it will fallback to using ``mp3``. It seems that all channels support the combination ``fast`` + ``mp3`` You can also choose to use the channel DJ as the reported track artist (default behavior):: [somafm] dj_as_artist = true Project resources ================= - `Source code `_ - `Issue tracker `_ - `Changelog `_ Credits ======= - Original author: `Alexandre Petitjean `__ - Current maintainer: `Alexandre Petitjean `__ - `Contributors `_ ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1612990371.1848197 Mopidy-SomaFM-2.0.2/mopidy_somafm/0000755000175000017500000000000000000000000017430 5ustar00alexandrealexandre././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1612987304.0 Mopidy-SomaFM-2.0.2/mopidy_somafm/__init__.py0000644000175000017500000000155600000000000021550 0ustar00alexandrealexandreimport logging import pathlib import pkg_resources from mopidy import config, ext __version__ = pkg_resources.get_distribution("Mopidy-SomaFM").version logger = logging.getLogger(__name__) class Extension(ext.Extension): dist_name = "Mopidy-SomaFM" ext_name = "somafm" version = __version__ def get_default_config(self): return config.read(pathlib.Path(__file__).parent / "ext.conf") def get_config_schema(self): schema = super().get_config_schema() schema["encoding"] = config.String(choices=("aac", "mp3", "aacp")) schema["quality"] = config.String( choices=("highest", "fast", "slow", "firewall") ) schema["dj_as_artist"] = config.Boolean() return schema def setup(self, registry): from .backend import SomaFMBackend registry.add("backend", SomaFMBackend) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1612990213.0 Mopidy-SomaFM-2.0.2/mopidy_somafm/backend.py0000644000175000017500000001024600000000000021374 0ustar00alexandrealexandreimport logging import mopidy_somafm import pykka import requests import configparser import random from mopidy import backend, httpclient from mopidy.models import Album, Artist, Image, Ref, Track from .somafm import SomaFMClient, extract_somafm_channel_name_from_uri logger = logging.getLogger(__name__) class SomaFMBackend(pykka.ThreadingActor, backend.Backend): def __init__(self, config, audio): super().__init__() user_agent = "{}/{}".format( mopidy_somafm.Extension.dist_name, mopidy_somafm.__version__ ) self.somafm = SomaFMClient(config["proxy"], user_agent) self.library = SomaFMLibraryProvider(backend=self) self.playback = SomaFMPlayback( audio=audio, backend=self, proxy_config=config["proxy"], user_agent=user_agent, ) self.uri_schemes = ["somafm"] self.quality = config["somafm"]["quality"] self.encoding = config["somafm"]["encoding"] self.dj_as_artist = config["somafm"]["dj_as_artist"] def on_start(self): self.somafm.refresh(self.encoding, self.quality) class SomaFMLibraryProvider(backend.LibraryProvider): root_directory = Ref.directory(uri="somafm:root", name="SomaFM") def lookup(self, uri): # Whatever the uri, it will always contains one track # which is a url to a pls channel_name = extract_somafm_channel_name_from_uri(uri) if channel_name is None: return None channel_data = self.backend.somafm.channels[channel_name] # Artists if self.backend.dj_as_artist: artist = Artist(name=channel_data["dj"]) else: artist = Artist() # Build album (idem as playlist, but with more metada) album = Album( artists=[artist], name=channel_data["title"], ) track = Track( artists=[artist], album=album, last_modified=channel_data["updated"], comment=channel_data["description"], genre=channel_data["genre"], name=channel_data["title"], uri="somafm:channel:/%s" % (channel_name), ) return [track] def browse(self, uri): if uri != "somafm:root": return [] result = [] for channel in self.backend.somafm.channels: result.append( Ref.track( uri="somafm:channel:/%s" % (channel), name=self.backend.somafm.channels[channel]["title"], ) ) result.sort(key=lambda ref: ref.name.lower()) return result def get_images(self, uris): images = {} for uri in uris: channel_name = extract_somafm_channel_name_from_uri(uri) if channel_name is not None: image = Image(uri=self.backend.somafm.images[channel_name]) images[uri] = [image] return images class SomaFMPlayback(backend.PlaybackProvider): def __init__(self, audio, backend, proxy_config=None, user_agent=None): super().__init__(audio=audio, backend=backend) # Build requests session self.session = requests.Session() if proxy_config is not None: proxy = httpclient.format_proxy(proxy_config) self.session.proxies.update({"http": proxy, "https": proxy}) full_user_agent = httpclient.format_user_agent(user_agent) self.session.headers.update({"user-agent": full_user_agent}) def translate_uri(self, uri): try: channel_name = extract_somafm_channel_name_from_uri(uri) if channel_name is None: return None channel_data = self.backend.somafm.channels.get(channel_name) r = self.session.get(channel_data["pls"]) if r.status_code != 200: return None pls = configparser.ConfigParser() pls.read_string(r.text) playlist = pls["playlist"] num = int(playlist["numberofentries"]) return playlist["File" + str(random.randint(1, num))] except Exception: return None ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1612987304.0 Mopidy-SomaFM-2.0.2/mopidy_somafm/ext.conf0000644000175000017500000000011200000000000021071 0ustar00alexandrealexandre[somafm] enabled = true encoding = mp3 quality = fast dj_as_artist = true ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1612990213.0 Mopidy-SomaFM-2.0.2/mopidy_somafm/somafm.py0000644000175000017500000001243200000000000021266 0ustar00alexandrealexandreimport collections import logging import re from urllib.parse import urlsplit import requests from mopidy import httpclient try: import xml.etree.cElementTree as ET except ImportError: import xml.etree.ElementTree as ET logger = logging.getLogger(__name__) # # Channels are playlist and Album # PLS are tracks # PLS contents for internal use # def extract_somafm_channel_name_from_uri(uri): if not uri.startswith("somafm:"): return None channel_separator = uri.find("/") if channel_separator == -1: return None return uri[channel_separator + 1 :] class SomaFMClient: CHANNELS_URI = "https://api.somafm.com/channels.xml" # All channels seem to have this combination of quality/encoding available FALLBACK_QUALITY = "fast" FALLBACK_ENCODING = "mp3" channels = {} images = {} def __init__(self, proxy_config=None, user_agent=None): super().__init__() # Build requests session self.session = requests.Session() if proxy_config is not None: proxy = httpclient.format_proxy(proxy_config) self.session.proxies.update({"http": proxy, "https": proxy}) full_user_agent = httpclient.format_user_agent(user_agent) self.session.headers.update({"user-agent": full_user_agent}) def refresh(self, encoding, quality): # clean previous data self.channels = {} # download channels xml file channels_content = self._downloadContent(self.CHANNELS_URI) if channels_content is None: logger.error("Cannot fetch %s" % (self.CHANNELS_URI)) return # parse XML root = ET.fromstring(channels_content) for child_channel in root: pls_id = child_channel.attrib["id"] channel_data = {} channel_all_pls = collections.defaultdict(dict) for child_detail in child_channel: key = child_detail.tag val = child_detail.text if key in ["title", "image", "dj", "genre", "description"]: channel_data[key] = val elif key == "updated": channel_data["updated"] = int(val) elif "pls" in key: pls_quality = key[:-3] pls_format = child_detail.attrib["format"] channel_all_pls[pls_quality][pls_format] = val # firewall playlist are fastpls+mp3 but with fw path if pls_quality == "fast" and pls_format == "mp3": r1 = urlsplit(val) channel_all_pls["firewall"][ "mp3" ] = "{}://{}/{}".format( r1.scheme, r1.netloc, "fw" + r1.path ) channel_pls = self._choose_pls(channel_all_pls, encoding, quality) if channel_pls is not None: channel_data["pls"] = channel_pls self.channels[pls_id] = channel_data self.images[pls_id] = channel_data["image"] logger.info("Loaded %i SomaFM channels" % (len(self.channels))) def extractStreamUrlFromPls(self, pls_uri): pls_content = self._downloadContent(pls_uri) if pls_content is None: logger.error("Cannot fetch %s" % (pls_uri)) return pls_uri # try to find FileX= try: m = re.search(r"^(File\d)=(?P\S+)", pls_content, re.M) if m: return m.group("stream_url") else: return pls_uri except BaseException: return pls_uri def _choose_pls(self, all_pls, encoding, quality): if not all_pls: return None if quality in all_pls: quality_pls = all_pls[quality] elif self.FALLBACK_QUALITY in all_pls: quality_pls = all_pls[self.FALLBACK_QUALITY] else: quality_pls = all_pls[next(iter(all_pls))] if not quality_pls: return None if encoding in quality_pls: pls = quality_pls[encoding] elif self.FALLBACK_ENCODING in all_pls: pls = quality_pls[self.FALLBACK_ENCODING] else: pls = quality_pls[next(iter(quality_pls))] return pls def _downloadContent(self, url): try: r = self.session.get(url) logger.debug("Get %s : %i", url, r.status_code) if r.status_code != 200: logger.error( "SomaFM: %s is not reachable [http code:%i]", url, r.status_code, ) return None except requests.exceptions.RequestException as e: logger.error("SomaFM RequestException: %s", e) except requests.exceptions.ConnectionError as e: logger.error("SomaFM ConnectionError: %s", e) except requests.exceptions.URLRequired as e: logger.error("SomaFM URLRequired: %s", e) except requests.exceptions.TooManyRedirects as e: logger.error("SomaFM TooManyRedirects: %s", e) except Exception as e: logger.error("SomaFM exception: %s", e) else: return r.text return None ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1612987304.0 Mopidy-SomaFM-2.0.2/pyproject.toml0000644000175000017500000000052600000000000017504 0ustar00alexandrealexandre[build-system] requires = ["setuptools >= 30.3.0", "wheel"] [tool.black] target-version = ["py37", "py38"] line-length = 80 [tool.isort] multi_line_output = 3 include_trailing_comma = true force_grid_wrap = 0 use_parentheses = true line_length = 88 known_tests = "tests" sections = "FUTURE,STDLIB,THIRDPARTY,FIRSTPARTY,TESTS,LOCALFOLDER" ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1612990371.1848197 Mopidy-SomaFM-2.0.2/setup.cfg0000644000175000017500000000257500000000000016417 0ustar00alexandrealexandre[metadata] name = Mopidy-SomaFM version = 2.0.2 url = https://github.com/AlexandrePTJ/mopidy-somafm author = Alexandre Petitjean author_email = alpetitjean@gmail.com license = Apache License, Version 2.0 license_file = LICENSE description = SomaFM extension for Mopidy long_description = file: README.rst classifiers = Environment :: No Input/Output (Daemon) Intended Audience :: End Users/Desktop License :: OSI Approved :: Apache Software License Operating System :: OS Independent Programming Language :: Python :: 3 Programming Language :: Python :: 3.7 Programming Language :: Python :: 3.8 Topic :: Multimedia :: Sound/Audio :: Players [options] zip_safe = False include_package_data = True packages = find: python_requires = >= 3.7 install_requires = Mopidy >= 3.0 Pykka >= 2.0.1 requests >= 2.0.0 setuptools [options.extras_require] lint = black check-manifest flake8 flake8-bugbear flake8-import-order isort[pyproject] release = twine wheel test = pytest pytest-cov dev = %(lint)s %(release)s %(test)s [options.packages.find] exclude = tests tests.* [options.entry_points] mopidy.ext = somafm = mopidy_somafm:Extension [flake8] application-import-names = mopidy_somafm, tests max-line-length = 80 exclude = .git, .tox, build select = C, E, F, W B B950 N ignore = E203 E501 W503 B305 [wheel] universal = 1 [egg_info] tag_build = tag_date = 0 ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1612987304.0 Mopidy-SomaFM-2.0.2/setup.py0000644000175000017500000000004600000000000016277 0ustar00alexandrealexandrefrom setuptools import setup setup() ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1612990371.1848197 Mopidy-SomaFM-2.0.2/tests/0000755000175000017500000000000000000000000015727 5ustar00alexandrealexandre././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1612987304.0 Mopidy-SomaFM-2.0.2/tests/__init__.py0000644000175000017500000000000000000000000020026 0ustar00alexandrealexandre././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1612987304.0 Mopidy-SomaFM-2.0.2/tests/test_extension.py0000644000175000017500000000054500000000000021360 0ustar00alexandrealexandrefrom mopidy_somafm import Extension def test_get_default_config(): ext = Extension() config = ext.get_default_config() assert "[somafm]" in config assert "enabled = true" in config def test_get_config_schema(): ext = Extension() schema = ext.get_config_schema() assert "quality" in schema assert "encoding" in schema ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1612987304.0 Mopidy-SomaFM-2.0.2/tests/test_somafm.py0000644000175000017500000000265500000000000020632 0ustar00alexandrealexandreimport unittest from mopidy_somafm.somafm import SomaFMClient class SomaFMClientTest(unittest.TestCase): def test_refresh(self): sfmc = SomaFMClient() sfmc.refresh("mp3", "fast") self.assertIsNotNone(sfmc.channels) self.assertNotEqual(len(sfmc.channels), 0) def test_refresh_firewall(self): sfmc = SomaFMClient() sfmc.refresh("mp3", "firewall") self.assertIsNotNone(sfmc.channels) self.assertNotEqual(len(sfmc.channels), 0) def test_refresh_no_channels(self): sfmc = SomaFMClient() sfmc.CHANNELS_URI = "" sfmc.refresh("mp3", "fast") self.assertDictEqual(sfmc.channels, {}) self.assertEqual(len(sfmc.channels), 0) def test_downloadContent(self): url = "http://api.somafm.com/channels.xml" sfmc = SomaFMClient() data = sfmc._downloadContent(url) self.assertNotEqual(len(data), 0) def test_extractStreamUrlFromPls(self): url = "http://somafm.com/groovesalad.pls" sfmc = SomaFMClient() data = sfmc.extractStreamUrlFromPls(url) self.assertNotEqual(len(data), 0) self.assertNotEqual(data, url) def test_extractStreamUrlFromPls_unknown(self): url = "http://somafm.com/noneazerty.pls" sfmc = SomaFMClient() data = sfmc.extractStreamUrlFromPls(url) self.assertNotEqual(len(data), 0) self.assertEqual(data, url) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1612987304.0 Mopidy-SomaFM-2.0.2/tox.ini0000644000175000017500000000073700000000000016107 0ustar00alexandrealexandre[tox] envlist = py37, py38, black, check-manifest, flake8 [testenv] sitepackages = true deps = .[test] commands = python -m pytest \ --basetemp={envtmpdir} \ --cov=mopidy_somafm --cov-report=term-missing \ {posargs} [testenv:black] deps = .[lint] commands = python -m black --check . [testenv:check-manifest] deps = .[lint] commands = python -m check_manifest [testenv:flake8] deps = .[lint] commands = python -m flake8 --show-source --statistics