././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1608019804.5010643 Mopidy-SoundCloud-3.0.1/0000755000175100001710000000000000000000000014371 5ustar00runnerdocker././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1608019804.4970644 Mopidy-SoundCloud-3.0.1/.circleci/0000755000175100001710000000000000000000000016224 5ustar00runnerdocker././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1608019795.0 Mopidy-SoundCloud-3.0.1/.circleci/config.yml0000644000175100001710000000222200000000000020212 0ustar00runnerdockerversion: 2.1 orbs: codecov: codecov/codecov@1.0.5 workflows: version: 2 test: jobs: - py39 - py38 - py37 - black - check-manifest - flake8 jobs: py39: &test-template docker: - image: mopidy/ci-python:3.9 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 py38: <<: *test-template docker: - image: mopidy/ci-python:3.8 py37: <<: *test-template docker: - image: mopidy/ci-python:3.7 black: *test-template check-manifest: *test-template flake8: *test-template ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1608019804.4970644 Mopidy-SoundCloud-3.0.1/.github/0000755000175100001710000000000000000000000015731 5ustar00runnerdocker././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1608019804.4970644 Mopidy-SoundCloud-3.0.1/.github/workflows/0000755000175100001710000000000000000000000017766 5ustar00runnerdocker././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1608019795.0 Mopidy-SoundCloud-3.0.1/.github/workflows/release.yml0000644000175100001710000000073500000000000022136 0ustar00runnerdockername: Release on: release: types: [published] jobs: release: runs-on: ubuntu-20.04 steps: - uses: actions/checkout@v2 - uses: actions/setup-python@v2 with: python-version: '3.9' - name: "Install dependencies" run: python3 -m pip install build - name: "Build package" run: python3 -m build - uses: pypa/gh-action-pypi-publish@v1.4.1 with: user: __token__ password: ${{ secrets.PYPI_TOKEN }} ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1608019795.0 Mopidy-SoundCloud-3.0.1/.mailmap0000644000175100001710000000035400000000000016014 0ustar00runnerdockerJanez Troha Janez Troha Janez Troha Morten Minde Neergaard Tom Roth ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1608019795.0 Mopidy-SoundCloud-3.0.1/LICENSE0000644000175100001710000000205700000000000015402 0ustar00runnerdockerCopyright (C) 2013 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=1608019795.0 Mopidy-SoundCloud-3.0.1/MANIFEST.in0000644000175100001710000000046200000000000016131 0ustar00runnerdockerinclude *.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 * recursive-include tests/fixtures *.yaml ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1608019804.4970644 Mopidy-SoundCloud-3.0.1/Mopidy_SoundCloud.egg-info/0000755000175100001710000000000000000000000021463 5ustar00runnerdocker././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1608019804.0 Mopidy-SoundCloud-3.0.1/Mopidy_SoundCloud.egg-info/PKG-INFO0000644000175100001710000001046000000000000022561 0ustar00runnerdockerMetadata-Version: 2.1 Name: Mopidy-SoundCloud Version: 3.0.1 Summary: Mopidy extension for playing music from SoundCloud Home-page: https://github.com/mopidy/mopidy-soundcloud Author: Janez Troha Author-email: dz0ny@ubuntu.si License: MIT Description: ***************** Mopidy-SoundCloud ***************** .. image:: https://img.shields.io/pypi/v/Mopidy-SoundCloud :target: https://pypi.org/project/Mopidy-SoundCloud/ :alt: Latest PyPI version .. image:: https://img.shields.io/circleci/build/gh/mopidy/mopidy-soundcloud :target: https://circleci.com/gh/mopidy/mopidy-soundcloud :alt: CircleCI build status .. image:: https://img.shields.io/codecov/c/gh/mopidy/mopidy-soundcloud :target: https://codecov.io/gh/mopidy/mopidy-soundcloud :alt: Test coverage `Mopidy `_ extension for playing music from `SoundCloud `_. Maintainer wanted ================= Mopidy-SoundCloud is currently kept on life support by the Mopidy core developers. It is in need of a more dedicated maintainer. If you want to be the maintainer of Mopidy-SoundCloud, please: 1. Make 2-3 good pull requests improving any part of the project. 2. Read and get familiar with all of the project's open issues. 3. Send a pull request removing this section and adding yourself as the "Current maintainer" in the "Credits" section below. In the pull request description, please refer to the previous pull requests and state that you've familiarized yourself with the open issues. As a maintainer, you'll be given push access to the repo and the authority to make releases to PyPI when you see fit. Installation ============ Install by running:: sudo python3 -m pip install Mopidy-SoundCloud See https://mopidy.com/ext/soundcloud/ for alternative installation methods. Configuration ============= #. You must register for a user account at https://soundcloud.com/ #. You need a SoundCloud authentication token for Mopidy from https://mopidy.com/authenticate #. Add the authentication token to the ``mopidy.conf`` config file:: [soundcloud] auth_token = 1-1111-1111111 explore_songs = 25 #. Use ``explore_songs`` to restrict the number of items returned. Troubleshooting =============== If you're having trouble with audio playback from SoundCloud, make sure you have the "ugly" plugin set from GStreamer installed for MP3 support. The package is typically named ``gstreamer1.0-plugins-ugly`` or similar, depending on OS and distribution. The package isn't a strict requirement for Mopidy's core, so you may be missing it. Project resources ================= - `Source code `_ - `Issue tracker `_ - `Changelog `_ Credits ======= - Original author: `Janez Troha `_ - Current maintainer: None. Maintainer wanted, see section above. - `Contributors `_ Platform: UNKNOWN Classifier: Environment :: No Input/Output (Daemon) Classifier: Intended Audience :: End Users/Desktop Classifier: License :: OSI Approved :: MIT License Classifier: Operating System :: OS Independent Classifier: Programming Language :: Python :: 3 Classifier: Programming Language :: Python :: 3.7 Classifier: Programming Language :: Python :: 3.8 Classifier: Programming Language :: Python :: 3.9 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=1608019804.0 Mopidy-SoundCloud-3.0.1/Mopidy_SoundCloud.egg-info/SOURCES.txt0000644000175100001710000000235300000000000023352 0ustar00runnerdocker.mailmap LICENSE MANIFEST.in README.rst pyproject.toml setup.cfg setup.py tox.ini .circleci/config.yml .github/workflows/release.yml Mopidy_SoundCloud.egg-info/PKG-INFO Mopidy_SoundCloud.egg-info/SOURCES.txt Mopidy_SoundCloud.egg-info/dependency_links.txt Mopidy_SoundCloud.egg-info/entry_points.txt Mopidy_SoundCloud.egg-info/not-zip-safe Mopidy_SoundCloud.egg-info/requires.txt Mopidy_SoundCloud.egg-info/top_level.txt mopidy_soundcloud/__init__.py mopidy_soundcloud/actor.py mopidy_soundcloud/ext.conf mopidy_soundcloud/library.py mopidy_soundcloud/soundcloud.py tests/__init__.py tests/test_api.py tests/test_cache.py tests/test_extension.py tests/test_library.py tests/fixtures/sc-following.yaml tests/fixtures/sc-groups.yaml tests/fixtures/sc-liked.yaml tests/fixtures/sc-login-error.yaml tests/fixtures/sc-login.yaml tests/fixtures/sc-popular.yaml tests/fixtures/sc-resolve-http.yaml tests/fixtures/sc-resolve-set.yaml tests/fixtures/sc-resolve-track-id.yaml tests/fixtures/sc-resolve-track-none.yaml tests/fixtures/sc-resolve-track.yaml tests/fixtures/sc-search.yaml tests/fixtures/sc-set-invalid.yaml tests/fixtures/sc-set.yaml tests/fixtures/sc-sets.yaml tests/fixtures/sc-stream.yaml tests/fixtures/sc-tracks.yaml tests/fixtures/sc-user-tracks.yaml././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1608019804.0 Mopidy-SoundCloud-3.0.1/Mopidy_SoundCloud.egg-info/dependency_links.txt0000644000175100001710000000000100000000000025531 0ustar00runnerdocker ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1608019804.0 Mopidy-SoundCloud-3.0.1/Mopidy_SoundCloud.egg-info/entry_points.txt0000644000175100001710000000006700000000000024764 0ustar00runnerdocker[mopidy.ext] soundcloud = mopidy_soundcloud:Extension ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1608019804.0 Mopidy-SoundCloud-3.0.1/Mopidy_SoundCloud.egg-info/not-zip-safe0000644000175100001710000000000100000000000023711 0ustar00runnerdocker ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1608019804.0 Mopidy-SoundCloud-3.0.1/Mopidy_SoundCloud.egg-info/requires.txt0000644000175100001710000000050000000000000024056 0ustar00runnerdockerMopidy>=3.0.0 Pykka>=2.0.1 setuptools requests>=2.0.0 [dev] black check-manifest flake8 flake8-bugbear flake8-import-order isort[pyproject] twine wheel pytest pytest-cov vcrpy [lint] black check-manifest flake8 flake8-bugbear flake8-import-order isort[pyproject] [release] twine wheel [test] pytest pytest-cov vcrpy ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1608019804.0 Mopidy-SoundCloud-3.0.1/Mopidy_SoundCloud.egg-info/top_level.txt0000644000175100001710000000002200000000000024207 0ustar00runnerdockermopidy_soundcloud ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1608019804.5010643 Mopidy-SoundCloud-3.0.1/PKG-INFO0000644000175100001710000001046000000000000015467 0ustar00runnerdockerMetadata-Version: 2.1 Name: Mopidy-SoundCloud Version: 3.0.1 Summary: Mopidy extension for playing music from SoundCloud Home-page: https://github.com/mopidy/mopidy-soundcloud Author: Janez Troha Author-email: dz0ny@ubuntu.si License: MIT Description: ***************** Mopidy-SoundCloud ***************** .. image:: https://img.shields.io/pypi/v/Mopidy-SoundCloud :target: https://pypi.org/project/Mopidy-SoundCloud/ :alt: Latest PyPI version .. image:: https://img.shields.io/circleci/build/gh/mopidy/mopidy-soundcloud :target: https://circleci.com/gh/mopidy/mopidy-soundcloud :alt: CircleCI build status .. image:: https://img.shields.io/codecov/c/gh/mopidy/mopidy-soundcloud :target: https://codecov.io/gh/mopidy/mopidy-soundcloud :alt: Test coverage `Mopidy `_ extension for playing music from `SoundCloud `_. Maintainer wanted ================= Mopidy-SoundCloud is currently kept on life support by the Mopidy core developers. It is in need of a more dedicated maintainer. If you want to be the maintainer of Mopidy-SoundCloud, please: 1. Make 2-3 good pull requests improving any part of the project. 2. Read and get familiar with all of the project's open issues. 3. Send a pull request removing this section and adding yourself as the "Current maintainer" in the "Credits" section below. In the pull request description, please refer to the previous pull requests and state that you've familiarized yourself with the open issues. As a maintainer, you'll be given push access to the repo and the authority to make releases to PyPI when you see fit. Installation ============ Install by running:: sudo python3 -m pip install Mopidy-SoundCloud See https://mopidy.com/ext/soundcloud/ for alternative installation methods. Configuration ============= #. You must register for a user account at https://soundcloud.com/ #. You need a SoundCloud authentication token for Mopidy from https://mopidy.com/authenticate #. Add the authentication token to the ``mopidy.conf`` config file:: [soundcloud] auth_token = 1-1111-1111111 explore_songs = 25 #. Use ``explore_songs`` to restrict the number of items returned. Troubleshooting =============== If you're having trouble with audio playback from SoundCloud, make sure you have the "ugly" plugin set from GStreamer installed for MP3 support. The package is typically named ``gstreamer1.0-plugins-ugly`` or similar, depending on OS and distribution. The package isn't a strict requirement for Mopidy's core, so you may be missing it. Project resources ================= - `Source code `_ - `Issue tracker `_ - `Changelog `_ Credits ======= - Original author: `Janez Troha `_ - Current maintainer: None. Maintainer wanted, see section above. - `Contributors `_ Platform: UNKNOWN Classifier: Environment :: No Input/Output (Daemon) Classifier: Intended Audience :: End Users/Desktop Classifier: License :: OSI Approved :: MIT License Classifier: Operating System :: OS Independent Classifier: Programming Language :: Python :: 3 Classifier: Programming Language :: Python :: 3.7 Classifier: Programming Language :: Python :: 3.8 Classifier: Programming Language :: Python :: 3.9 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=1608019795.0 Mopidy-SoundCloud-3.0.1/README.rst0000644000175100001710000000541300000000000016063 0ustar00runnerdocker***************** Mopidy-SoundCloud ***************** .. image:: https://img.shields.io/pypi/v/Mopidy-SoundCloud :target: https://pypi.org/project/Mopidy-SoundCloud/ :alt: Latest PyPI version .. image:: https://img.shields.io/circleci/build/gh/mopidy/mopidy-soundcloud :target: https://circleci.com/gh/mopidy/mopidy-soundcloud :alt: CircleCI build status .. image:: https://img.shields.io/codecov/c/gh/mopidy/mopidy-soundcloud :target: https://codecov.io/gh/mopidy/mopidy-soundcloud :alt: Test coverage `Mopidy `_ extension for playing music from `SoundCloud `_. Maintainer wanted ================= Mopidy-SoundCloud is currently kept on life support by the Mopidy core developers. It is in need of a more dedicated maintainer. If you want to be the maintainer of Mopidy-SoundCloud, please: 1. Make 2-3 good pull requests improving any part of the project. 2. Read and get familiar with all of the project's open issues. 3. Send a pull request removing this section and adding yourself as the "Current maintainer" in the "Credits" section below. In the pull request description, please refer to the previous pull requests and state that you've familiarized yourself with the open issues. As a maintainer, you'll be given push access to the repo and the authority to make releases to PyPI when you see fit. Installation ============ Install by running:: sudo python3 -m pip install Mopidy-SoundCloud See https://mopidy.com/ext/soundcloud/ for alternative installation methods. Configuration ============= #. You must register for a user account at https://soundcloud.com/ #. You need a SoundCloud authentication token for Mopidy from https://mopidy.com/authenticate #. Add the authentication token to the ``mopidy.conf`` config file:: [soundcloud] auth_token = 1-1111-1111111 explore_songs = 25 #. Use ``explore_songs`` to restrict the number of items returned. Troubleshooting =============== If you're having trouble with audio playback from SoundCloud, make sure you have the "ugly" plugin set from GStreamer installed for MP3 support. The package is typically named ``gstreamer1.0-plugins-ugly`` or similar, depending on OS and distribution. The package isn't a strict requirement for Mopidy's core, so you may be missing it. Project resources ================= - `Source code `_ - `Issue tracker `_ - `Changelog `_ Credits ======= - Original author: `Janez Troha `_ - Current maintainer: None. Maintainer wanted, see section above. - `Contributors `_ ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1608019804.4970644 Mopidy-SoundCloud-3.0.1/mopidy_soundcloud/0000755000175100001710000000000000000000000020131 5ustar00runnerdocker././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1608019795.0 Mopidy-SoundCloud-3.0.1/mopidy_soundcloud/__init__.py0000644000175100001710000000242300000000000022243 0ustar00runnerdockerimport pathlib import pkg_resources from mopidy import config, ext from mopidy.exceptions import ExtensionError __version__ = pkg_resources.get_distribution("Mopidy-SoundCloud").version class Extension(ext.Extension): dist_name = "Mopidy-SoundCloud" ext_name = "soundcloud" 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["explore_songs"] = config.Integer(optional=True) schema["auth_token"] = config.Secret() schema["explore"] = config.Deprecated() schema["explore_pages"] = config.Deprecated() return schema def validate_config(self, config): # no_coverage if not config.getboolean("soundcloud", "enabled"): return if not config.get("soundcloud", "auth_token"): raise ExtensionError( "In order to use SoundCloud extension you must provide an " "auth token. For more information refer to " "https://github.com/mopidy/mopidy-soundcloud/" ) def setup(self, registry): from .actor import SoundCloudBackend registry.add("backend", SoundCloudBackend) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1608019795.0 Mopidy-SoundCloud-3.0.1/mopidy_soundcloud/actor.py0000644000175100001710000000207600000000000021620 0ustar00runnerdockerimport logging import pykka from mopidy import backend from mopidy_soundcloud.library import SoundCloudLibraryProvider from mopidy_soundcloud.soundcloud import SoundCloudClient logger = logging.getLogger(__name__) class SoundCloudBackend(pykka.ThreadingActor, backend.Backend): def __init__(self, config, audio): super().__init__() self.config = config self.remote = SoundCloudClient(config) self.library = SoundCloudLibraryProvider(backend=self) self.playback = SoundCloudPlaybackProvider(audio=audio, backend=self) self.uri_schemes = ["soundcloud", "sc"] def on_start(self): username = self.remote.user.get("username") if username is not None: logger.info(f"Logged in to SoundCloud as {username!r}") class SoundCloudPlaybackProvider(backend.PlaybackProvider): def translate_uri(self, uri): track_id = self.backend.remote.parse_track_uri(uri) track = self.backend.remote.get_track(track_id, True) if track is None: return None return track.uri ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1608019795.0 Mopidy-SoundCloud-3.0.1/mopidy_soundcloud/ext.conf0000644000175100001710000000030200000000000021573 0ustar00runnerdocker[soundcloud] enabled = True # Your SoundCloud auth token, you can get yours at http://www.mopidy.com/authenticate auth_token = # Number of songs to fetch in explore section explore_songs = 25././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1608019795.0 Mopidy-SoundCloud-3.0.1/mopidy_soundcloud/library.py0000644000175100001710000001245400000000000022155 0ustar00runnerdockerimport collections import logging import re import urllib.parse from mopidy import backend, models from mopidy.models import SearchResult, Track logger = logging.getLogger(__name__) def generate_uri(path): return f"soundcloud:directory:{urllib.parse.quote('/'.join(path))}" def new_folder(name, path): return models.Ref.directory(uri=generate_uri(path), name=name) def simplify_search_query(query): if isinstance(query, dict): r = [] for v in query.values(): if isinstance(v, list): r.extend(v) else: r.append(v) return " ".join(r) if isinstance(query, list): return " ".join(query) else: return query class SoundCloudLibraryProvider(backend.LibraryProvider): root_directory = models.Ref.directory( uri="soundcloud:directory", name="SoundCloud" ) def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self.vfs = {"soundcloud:directory": {}} self.add_to_vfs(new_folder("Following", ["following"])) self.add_to_vfs(new_folder("Liked", ["liked"])) self.add_to_vfs(new_folder("Sets", ["sets"])) self.add_to_vfs(new_folder("Stream", ["stream"])) def add_to_vfs(self, _model): self.vfs["soundcloud:directory"][_model.uri] = _model def list_sets(self): sets_vfs = collections.OrderedDict() for (name, set_id, _tracks) in self.backend.remote.get_sets(): sets_list = new_folder(name, ["sets", set_id]) logger.debug(f"Adding set {sets_list.name} to VFS") sets_vfs[set_id] = sets_list return list(sets_vfs.values()) def list_liked(self): vfs_list = collections.OrderedDict() for track in self.backend.remote.get_likes(): logger.debug(f"Adding liked track {track.name} to VFS") vfs_list[track.name] = models.Ref.track( uri=track.uri, name=track.name ) return list(vfs_list.values()) def list_user_follows(self): sets_vfs = collections.OrderedDict() for (name, user_id) in self.backend.remote.get_followings(): sets_list = new_folder(name, ["following", user_id]) logger.debug(f"Adding set {sets_list.name} to VFS") sets_vfs[user_id] = sets_list return list(sets_vfs.values()) def tracklist_to_vfs(self, track_list): vfs_list = collections.OrderedDict() for temp_track in track_list: if not isinstance(temp_track, Track): temp_track = self.backend.remote.parse_track(temp_track) if hasattr(temp_track, "uri"): vfs_list[temp_track.name] = models.Ref.track( uri=temp_track.uri, name=temp_track.name ) return list(vfs_list.values()) def browse(self, uri): if not self.vfs.get(uri): (req_type, res_id) = re.match(r".*:(\w*)(?:/(\d*))?", uri).groups() # Sets if "sets" == req_type: if res_id: return self.tracklist_to_vfs( self.backend.remote.get_set(res_id) ) else: return self.list_sets() # Following if "following" == req_type: if res_id: return self.tracklist_to_vfs( self.backend.remote.get_tracks(res_id) ) else: return self.list_user_follows() # Liked if "liked" == req_type: return self.list_liked() # User stream if "stream" == req_type: return self.tracklist_to_vfs( self.backend.remote.get_user_stream() ) # root directory return list(self.vfs.get(uri, {}).values()) def search(self, query=None, uris=None, exact=False): # TODO Support exact search if not query: return if "uri" in query: search_query = "".join(query["uri"]) url = urllib.parse.urlparse(search_query) if "soundcloud.com" in url.netloc: logger.info(f"Resolving SoundCloud for: {search_query}") return SearchResult( uri="soundcloud:search", tracks=self.backend.remote.resolve_url(search_query), ) else: search_query = simplify_search_query(query) logger.info(f"Searching SoundCloud for: {search_query}") return SearchResult( uri="soundcloud:search", tracks=self.backend.remote.search(search_query), ) def lookup(self, uri): if "sc:" in uri: uri = uri.replace("sc:", "") return self.backend.remote.resolve_url(uri) try: track_id = self.backend.remote.parse_track_uri(uri) track = self.backend.remote.get_track(track_id) if track is None: logger.info( f"Failed to lookup {uri}: SoundCloud track not found" ) return [] return [track] except Exception as error: logger.error(f"Failed to lookup {uri}: {error}") return [] ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1608019795.0 Mopidy-SoundCloud-3.0.1/mopidy_soundcloud/soundcloud.py0000644000175100001710000003001100000000000022655 0ustar00runnerdockerimport collections import datetime import logging import re import string import time import unicodedata from contextlib import closing from multiprocessing.pool import ThreadPool from urllib.parse import quote_plus import requests from requests.adapters import HTTPAdapter from requests.exceptions import HTTPError import mopidy_soundcloud from mopidy import httpclient from mopidy.models import Album, Artist, Track logger = logging.getLogger(__name__) def safe_url(uri): return quote_plus( unicodedata.normalize("NFKD", uri).encode("ASCII", "ignore") ) def readable_url(uri): valid_chars = f"-_.() {string.ascii_letters}{string.digits}" safe_uri = ( unicodedata.normalize("NFKD", uri).encode("ascii", "ignore").decode() ) return re.sub( r"\s+", " ", "".join(c for c in safe_uri if c in valid_chars) ).strip() def streamble_url(url, client_id): return f"{url}?client_id={client_id}" def get_user_url(user_id): if not user_id: return "me" else: return f"users/{user_id}" def get_requests_session(proxy_config, user_agent, token): proxy = httpclient.format_proxy(proxy_config) full_user_agent = httpclient.format_user_agent(user_agent) session = requests.Session() session.proxies.update({"http": proxy, "https": proxy}) session.headers.update({"user-agent": full_user_agent}) session.headers.update({"Authorization": f"OAuth {token}"}) return session class cache: # noqa # TODO: merge this to util library def __init__(self, ctl=8, ttl=3600): self.cache = {} self.ctl = ctl self.ttl = ttl self._call_count = 1 def __call__(self, func): def _memoized(*args): self.func = func now = time.time() try: value, last_update = self.cache[args] age = now - last_update if self._call_count >= self.ctl or age > self.ttl: self._call_count = 1 raise AttributeError self._call_count += 1 return value except (KeyError, AttributeError): value = self.func(*args) self.cache[args] = (value, now) return value except TypeError: return self.func(*args) return _memoized class ThrottlingHttpAdapter(HTTPAdapter): def __init__(self, burst_length, burst_window, wait_window): super().__init__() self.max_hits = burst_length self.hits = 0 self.rate = burst_length / burst_window self.burst_window = datetime.timedelta(seconds=burst_window) self.total_window = datetime.timedelta( seconds=burst_window + wait_window ) self.timestamp = datetime.datetime.min def _is_too_many_requests(self): now = datetime.datetime.utcnow() if now < self.timestamp + self.total_window: elapsed = now - self.timestamp self.hits += 1 if (now < self.timestamp + self.burst_window) and ( self.hits < self.max_hits ): return False else: logger.debug( f"Request throttling after {self.hits} hits in " f"{elapsed.microseconds} us " f"(window until {self.timestamp + self.total_window})" ) return True else: self.timestamp = now self.hits = 0 return False def send(self, request, **kwargs): if request.method == "HEAD" and self._is_too_many_requests(): resp = requests.Response() resp.request = request resp.url = request.url resp.status_code = 429 resp.reason = ( "Client throttled to {self.rate:.1f} requests per second" ) return resp else: return super().send(request, **kwargs) class SoundCloudClient: CLIENT_ID = "93e33e327fd8a9b77becd179652272e2" def __init__(self, config): super().__init__() self.explore_songs = config["soundcloud"].get("explore_songs", 25) self.http_client = get_requests_session( proxy_config=config["proxy"], user_agent=( f"{mopidy_soundcloud.Extension.dist_name}/" f"{mopidy_soundcloud.__version__}" ), token=config["soundcloud"]["auth_token"], ) adapter = ThrottlingHttpAdapter( burst_length=3, burst_window=1, wait_window=10 ) self.http_client.mount("https://api.soundcloud.com/", adapter) @property @cache() def user(self): return self._get("me") @cache(ttl=10) def get_user_stream(self): # https://developers.soundcloud.com/docs/api/reference#activities tracks = [] stream = self._get("me/activities", limit=True).get("collection", []) for data in stream: kind = data.get("origin") # multiple types of track with same data if kind: if kind["kind"] == "track": tracks.append(self.parse_track(kind)) elif kind["kind"] == "playlist": playlist = kind.get("tracks") if isinstance(playlist, collections.Iterable): tracks.extend(self.parse_results(playlist)) return self.sanitize_tracks(tracks) @cache(ttl=10) def get_followings(self, user_id=None): user_url = get_user_url(user_id) users = [] playlists = self._get(f"{user_url}/followings", limit=True) for playlist in playlists.get("collection", []): user_name = playlist.get("username") user_id = str(playlist.get("id")) logger.debug(f"Fetched user {user_name} with ID {user_id}") users.append((user_name, user_id)) return users @cache() def get_set(self, set_id): # https://developers.soundcloud.com/docs/api/reference#playlists playlist = self._get(f"playlists/{set_id}") return playlist.get("tracks", []) @cache(ttl=10) def get_sets(self, user_id=None): user_url = get_user_url(user_id) playable_sets = [] for playlist in self._get(f"{user_url}/playlists", limit=True): name = playlist.get("title") set_id = str(playlist.get("id")) tracks = playlist.get("tracks", []) logger.debug( f"Fetched set {name} with ID {set_id} ({len(tracks)} tracks)" ) playable_sets.append((name, set_id, tracks)) return playable_sets @cache(ttl=10) def get_likes(self, user_id=None): # https://developers.soundcloud.com/docs/api/reference#GET--users--id--favorites user_url = get_user_url(user_id) likes = self._get(f"{user_url}/favorites", limit=True) return self.parse_results(likes) @cache(ttl=10) def get_tracks(self, user_id=None): user_url = get_user_url(user_id) tracks = self._get(f"{user_url}/tracks", limit=True) return self.parse_results(tracks) # Public @cache() def get_track(self, track_id, streamable=False): logger.debug("Getting info for track with ID {track_id}") try: return self.parse_track(self._get(f"tracks/{track_id}"), streamable) except Exception: return None def parse_track_uri(self, track): logger.debug(f"Parsing track {track}") if hasattr(track, "uri"): track = track.uri return track.split(".")[-1] def search(self, query): # https://developers.soundcloud.com/docs/api/reference#tracks query = quote_plus(query.encode("utf-8")) search_results = self._get(f"tracks?q={query}", limit=True) tracks = [] for track in search_results: tracks.append(self.parse_track(track, False)) return self.sanitize_tracks(tracks) def parse_results(self, res): tracks = [] logger.debug(f"Parsing {len(res)} result item(s)...") for item in res: if item["kind"] == "track": tracks.append(self.parse_track(item)) elif item["kind"] == "playlist": playlist_tracks = item.get("tracks", []) logger.debug( f"Parsing {len(playlist_tracks)} playlist track(s)..." ) for track in playlist_tracks: tracks.append(self.parse_track(track)) else: logger.warning(f"Unknown item type {item['kind']!r}") return self.sanitize_tracks(tracks) def resolve_url(self, uri): return self.parse_results([self._get(f"resolve?url={uri}")]) def _get(self, url, limit=None): url = f"https://api.soundcloud.com/{url}" params = [("client_id", self.CLIENT_ID)] if limit: params.insert(0, ("limit", self.explore_songs)) try: with closing(self.http_client.get(url, params=params)) as res: logger.debug(f"Requested {res.url}") res.raise_for_status() return res.json() except Exception as e: if isinstance(e, HTTPError) and e.response.status_code == 401: logger.error( 'Invalid "auth_token" used for SoundCloud ' "authentication!" ) else: logger.error(f"SoundCloud API request failed: {e}") return {} def sanitize_tracks(self, tracks): return [t for t in tracks if t] @cache() def parse_track(self, data, remote_url=False): if not data: return None if not data.get("streamable"): logger.info( f"{data.get('title')!r} can't be streamed from SoundCloud" ) return None if not data.get("kind") == "track": logger.debug(f"{data.get('title')} is not track") return None track_kwargs = {} artist_kwargs = {} album_kwargs = {} if "title" in data: label_name = data.get("label_name") if not label_name: label_name = data.get("user", {}).get( "username", "Unknown label" ) track_kwargs["name"] = data["title"] artist_kwargs["name"] = label_name album_kwargs["name"] = "SoundCloud" if "date" in data: track_kwargs["date"] = data["date"] if remote_url: track_kwargs["uri"] = self.get_streamble_url(data["stream_url"]) if track_kwargs["uri"] is None: logger.info( f"{data.get('title')} can't be streamed from SoundCloud" ) return None else: track_kwargs[ "uri" ] = f"soundcloud:song/{readable_url(data.get('title'))}.{data.get('id')}" track_kwargs["length"] = int(data.get("duration", 0)) track_kwargs["comment"] = data.get("permalink_url", "") if artist_kwargs: track_kwargs["artists"] = [Artist(**artist_kwargs)] if album_kwargs: track_kwargs["album"] = Album(**album_kwargs) return Track(**track_kwargs) @cache() def get_streamble_url(self, url): req = self.http_client.head(streamble_url(url, self.CLIENT_ID)) if req.status_code == 302: return req.headers.get("Location", None) elif req.status_code == 429: reason = "" if req.reason == "Unknown" else f" ({req.reason})" logger.warning(f"SoundCloud daily rate limit exceeded{reason}") def resolve_tracks(self, track_ids): """Resolve tracks concurrently emulating browser :param track_ids:list of track ids :return:list `Track` """ pool = ThreadPool(processes=16) tracks = pool.map(self.get_track, track_ids) pool.close() return self.sanitize_tracks(tracks) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1608019795.0 Mopidy-SoundCloud-3.0.1/pyproject.toml0000644000175100001710000000052600000000000017310 0ustar00runnerdocker[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=1608019804.5010643 Mopidy-SoundCloud-3.0.1/setup.cfg0000644000175100001710000000260300000000000016213 0ustar00runnerdocker[metadata] name = Mopidy-SoundCloud version = 3.0.1 url = https://github.com/mopidy/mopidy-soundcloud author = Janez Troha author_email = dz0ny@ubuntu.si license = MIT license_file = LICENSE description = Mopidy extension for playing music from SoundCloud long_description = file: README.rst classifiers = Environment :: No Input/Output (Daemon) Intended Audience :: End Users/Desktop License :: OSI Approved :: MIT License Operating System :: OS Independent Programming Language :: Python :: 3 Programming Language :: Python :: 3.7 Programming Language :: Python :: 3.8 Programming Language :: Python :: 3.9 Topic :: Multimedia :: Sound/Audio :: Players [options] zip_safe = False include_package_data = True packages = find: python_requires = >= 3.7 install_requires = Mopidy >= 3.0.0 Pykka >= 2.0.1 setuptools requests >= 2.0.0 [options.extras_require] lint = black check-manifest flake8 flake8-bugbear flake8-import-order isort[pyproject] release = twine wheel test = pytest pytest-cov vcrpy dev = %(lint)s %(release)s %(test)s [options.packages.find] exclude = tests tests.* [options.entry_points] mopidy.ext = soundcloud = mopidy_soundcloud:Extension [flake8] application-import-names = mopidy_soundcloud, tests max-line-length = 80 exclude = .git, .tox, build select = C, E, F, W B B950 N ignore = E203 E501 W503 [egg_info] tag_build = tag_date = 0 ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1608019795.0 Mopidy-SoundCloud-3.0.1/setup.py0000644000175100001710000000004600000000000016103 0ustar00runnerdockerfrom setuptools import setup setup() ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1608019804.4970644 Mopidy-SoundCloud-3.0.1/tests/0000755000175100001710000000000000000000000015533 5ustar00runnerdocker././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1608019795.0 Mopidy-SoundCloud-3.0.1/tests/__init__.py0000644000175100001710000000000000000000000017632 0ustar00runnerdocker././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1608019804.5010643 Mopidy-SoundCloud-3.0.1/tests/fixtures/0000755000175100001710000000000000000000000017404 5ustar00runnerdocker././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1608019795.0 Mopidy-SoundCloud-3.0.1/tests/fixtures/sc-following.yaml0000644000175100001710000001452000000000000022675 0ustar00runnerdockerinteractions: - request: body: null headers: Accept: ['*/*'] Accept-Encoding: ['gzip, deflate'] Connection: [keep-alive] user-agent: [!!python/unicode 'Mopidy-SoundCloud/2.0.2 Mopidy/2.1.0 CPython/2.7.11+'] method: GET uri: https://api.soundcloud.com/me/followings?limit=10&client_id=93e33e327fd8a9b77becd179652272e2 response: body: string: !!binary | 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 k9HYD6HKUee3/wIAAP//AwD+wbFTjSoAAA== headers: access-control-allow-headers: ['Accept, Authorization, Content-Type, Origin'] access-control-allow-methods: ['GET, PUT, POST, DELETE'] access-control-allow-origin: ['*'] access-control-expose-headers: [Date] cache-control: ['private, max-age=0, must-revalidate'] connection: [keep-alive] content-encoding: [gzip] content-length: ['3559'] content-type: [application/json;charset=utf-8] date: ['Tue, 22 May 2018 22:30:35 GMT'] server: [am/2] via: [1.1 f76a5f650b7fe2be49bcae6cfe9c8215.cloudfront.net (CloudFront)] x-amz-cf-id: [lj1O0UGgoSQjfDfaGBoQXUv0nv2erwEQh-gW9c-aYAe_1b2paiZEZQ==] x-cache: [Miss from cloudfront] status: {code: 200, message: OK} version: 1 ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1608019795.0 Mopidy-SoundCloud-3.0.1/tests/fixtures/sc-groups.yaml0000644000175100001710000000211000000000000022204 0ustar00runnerdockerinteractions: - request: body: null headers: Accept: ['*/*'] Accept-Encoding: ['gzip, deflate'] Authorization: [!!python/unicode 'OAuth 1-35204-61921957-55796ebef403996'] Connection: [keep-alive] User-Agent: [python-requests/2.4.3 CPython/2.7.8 Linux/3.16.0-28-generic] method: GET uri: https://api.soundcloud.com:443/me/groups.json?client_id=93e33e327fd8a9b77becd179652272e2 response: body: string: !!binary | H4sIAAAAAAAEA4uOBQApu0wNAgAAAA== headers: access-control-allow-headers: ['Accept, Authorization, Content-Type, Origin'] access-control-allow-methods: ['GET, PUT, POST, DELETE'] access-control-allow-origin: ['*'] access-control-expose-headers: [Date] cache-control: ['private, max-age=0, must-revalidate'] content-encoding: [gzip] content-length: ['22'] content-type: [application/json; charset=utf-8] date: ['Fri, 02 Jan 2015 17:03:54 GMT'] etag: ['"d751713988987e9331980363e24189ce"'] server: [am/2] status: {code: 200, message: OK} version: 1 ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1608019795.0 Mopidy-SoundCloud-3.0.1/tests/fixtures/sc-liked.yaml0000644000175100001710000001012500000000000021762 0ustar00runnerdockerinteractions: - request: body: null headers: Accept: ['*/*'] Accept-Encoding: ['gzip, deflate'] Connection: [keep-alive] user-agent: [!!python/unicode 'Mopidy-SoundCloud/2.0.2 Mopidy/2.1.0 CPython/2.7.11+'] method: GET uri: https://api.soundcloud.com/me/favorites?limit=10&client_id=93e33e327fd8a9b77becd179652272e2 response: body: string: !!binary | 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 n0YZAAA= headers: access-control-allow-headers: ['Accept, Authorization, Content-Type, Origin'] access-control-allow-methods: ['GET, PUT, POST, DELETE'] access-control-allow-origin: ['*'] access-control-expose-headers: [Date] cache-control: ['private, max-age=0, must-revalidate'] connection: [keep-alive] content-encoding: [gzip] content-length: ['1943'] content-type: [application/json;charset=utf-8] date: ['Fri, 20 Apr 2018 00:13:26 GMT'] etag: ['"777a3fc587cfca0661a916a2db4f678b"'] server: [am/2] status: [200 OK] via: [1.1 1fcad26913988e10fd175698baa6f474.cloudfront.net (CloudFront)] x-amz-cf-id: [yFEE0naRUjvM9OVjV5XuMpFDLzAN_t8qC2ef-ESKsY3rmp6oLGJhlA==] x-cache: [Miss from cloudfront] x-frame-options: [SAMEORIGIN] status: {code: 200, message: OK} version: 1 ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1608019795.0 Mopidy-SoundCloud-3.0.1/tests/fixtures/sc-login-error.yaml0000644000175100001710000000114100000000000023127 0ustar00runnerdockerinteractions: - request: body: null headers: Accept: ['*/*'] Accept-Encoding: ['gzip, deflate'] Authorization: [!!python/unicode OAuth 1-fake-token] Connection: [keep-alive] User-Agent: [python-requests/2.11.1] method: GET uri: https://api.soundcloud.com/me?client_id=93e33e327fd8a9b77becd179652272e2 response: body: {string: !!python/unicode ''} headers: cache-control: ['private, max-age=0'] content-length: ['0'] date: ['Thu, 27 Oct 2016 20:34:06 GMT'] server: [am/2] status: {code: 401, message: Unauthorized} version: 1 ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1608019795.0 Mopidy-SoundCloud-3.0.1/tests/fixtures/sc-login.yaml0000644000175100001710000000363700000000000022014 0ustar00runnerdockerinteractions: - request: body: null headers: Accept: ['*/*'] Accept-Encoding: ['gzip, deflate'] Connection: [keep-alive] user-agent: [!!python/unicode 'Mopidy-SoundCloud/2.0.2 Mopidy/2.1.0 CPython/2.7.11+'] method: GET uri: https://api.soundcloud.com/me?client_id=93e33e327fd8a9b77becd179652272e2 response: body: string: !!binary | H4sIAAAAAAAAAHRSwXKbMBT8FY+uNQZjHDt8QI+95JjpaGRJuC8WEpZEMp5M/r0rDC6lU05o39t9 +1b6ZKRYvd/tn8rquVyzC1mcWR+0Z2vWad8KQ/YCyJK8ZCFqbbIKpdRhRatR+YHK6iVVVjtUjAiR t05RQzpplcX2KS+qvNyuyqIudzV+vhX4koondPyKsQt1nouONsH1VknjerWRrs3TmJBP/uaWeO/N yAV1QVuYFe8iCj9jpGm03QRwlB0Y95aQJWPV86GoDod9dr0eu7djZoQ/681bd4aUxKDob6y2vTFr 1pDHtrMgpgBGaIgFWINu/k9eKCgdpKcukrOTpqT40FcUpDuHkXrH2lvohNR/YR/6FCgujzxSNA8w eiEvfFiA1QWiNOJmCF5HCNfvLKIFoREm6KEDrth3r3WKvj8ZkrwR785DPEy8PdZzxrgPXNVM646R Pf9pBBr602PhwOrXn3gEnXFC8aClsypwoxv0botjAY/X3kXB6k/Ww1mLqYqP7WNldLrQwLNRw47/ 1/7CQp5w60gpBRPmyYyFKaFFrRX+xnUryAC3eAJtmhZ9DyPGSZEiZwjM687NyV+/AQAA//8DAGsU EO9uAwAA headers: access-control-allow-headers: ['Accept, Authorization, Content-Type, Origin'] access-control-allow-methods: ['GET, PUT, POST, DELETE'] access-control-allow-origin: ['*'] access-control-expose-headers: [Date] cache-control: ['private, max-age=0, must-revalidate'] connection: [keep-alive] content-encoding: [gzip] content-length: ['462'] content-type: [application/json;charset=utf-8] date: ['Fri, 20 Apr 2018 00:13:26 GMT'] etag: ['"590341da44a4f60dcf7172bdb525f874"'] server: [am/2] status: [200 OK] via: [1.1 14ab6568bfe30e99a79da2a071b3e971.cloudfront.net (CloudFront)] x-amz-cf-id: [oni7QrgwzILxoeDzfS2qLx0675uzDwe0evgW6BZnYVeo8EuyQDLm9A==] x-cache: [Miss from cloudfront] x-frame-options: [SAMEORIGIN] status: {code: 200, message: OK} version: 1 ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1608019795.0 Mopidy-SoundCloud-3.0.1/tests/fixtures/sc-popular.yaml0000644000175100001710000010336100000000000022361 0ustar00runnerdockerinteractions: - request: body: null headers: Accept: ['*/*'] Accept-Encoding: ['gzip, deflate'] Authorization: [!!python/unicode 'OAuth 1-35204-61921957-55796ebef403996'] Connection: [keep-alive] User-Agent: [python-requests/2.4.3 CPython/2.7.8 Linux/3.16.0-28-generic] method: GET uri: https://api-v2.soundcloud.com:443/explore/categories?client_id=93e33e327fd8a9b77becd179652272e2 response: body: string: !!binary | H4sIAAAAAAAAAExRwU7DMAz9l0qcOi4cOHBjK2gghqp1Ege0Q5ZaxVpqV07CtCH+nTjdOk5+fkme n19+ChNb5OLhs6h5iM5I+ZiJWZHrjnnvUzOPHgm8wgX30B4TeKIAEgxSDxRS/wZGCKlL8B0Ovry5 uy9rdhjQ6rs1OOyQKfPNgIIhmnSqUo1FIAuKBpag15vAcgzg3Ki4AftF7Lg7FttZ0Sc79r/pVSaS aZc8kQn4DeWa7V6pfoejwYUzPl0zLm8RKYjOrsw4uQIYyiVHnxv0VkOoJPbZ8Nzk5au48wEG3d6B DcJXRNnAMzsdujTSWhYoR9/KoKrry8uMF2pxcvlqTifNMFmnVFcQss0VEvbGXWVqNGPNUrVwJ+lb dN2LbB1JBdfJ9VyrGXL4XWf09DyuSamC3DZM3SF9BIhyHN056UlsmruRc0wJDLlM+3ywuLbY/v4B AAD//wMAP61+EEwCAAA= headers: cache-control: ['private, max-age=0'] content-encoding: [gzip] content-length: ['356'] content-type: [application/json] date: ['Fri, 02 Jan 2015 17:19:37 GMT'] server: [am/2] set-cookie: [_session_auth_key="iuo9QNN6oaPVa3gj9XyKTHZtsxE="] vary: [Origin] status: {code: 200, message: OK} - request: body: null headers: Accept: ['*/*'] Accept-Encoding: ['gzip, deflate'] Authorization: [!!python/unicode 'OAuth 1-35204-61921957-55796ebef403996'] Connection: [keep-alive] Cookie: [_session_auth_key="iuo9QNN6oaPVa3gj9XyKTHZtsxE="] User-Agent: [python-requests/2.4.3 CPython/2.7.8 Linux/3.16.0-28-generic] method: GET uri: https://api-v2.soundcloud.com:443/explore/Alternative+Rock?limit=10&offset=0&linked_partitioning=1&client_id=93e33e327fd8a9b77becd179652272e2 response: body: string: !!binary | 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 AP//AwC7AEBWjlwAAA== headers: cache-control: ['public, max-age=60'] content-encoding: [gzip] content-length: ['5599'] content-type: [application/json] date: ['Fri, 02 Jan 2015 17:19:38 GMT'] server: [am/2] vary: [Origin] status: {code: 200, message: OK} - request: body: null headers: Accept: ['*/*'] Accept-Encoding: ['gzip, deflate'] Authorization: [!!python/unicode 'OAuth 1-35204-61921957-55796ebef403996'] Connection: [keep-alive] User-Agent: [python-requests/2.4.3 CPython/2.7.8 Linux/3.16.0-28-generic] method: GET uri: https://api.soundcloud.com:443/tracks/170703457.json?client_id=93e33e327fd8a9b77becd179652272e2 response: body: string: !!binary | H4sIAAAAAAAEA51V247bNhD9FYJPLWpZki+SV28G0ssC202QbtGXAMJYGllcU6RKUnadIP/eoW5w jBZtsvDa1nBmPOfMnOEnfhKq5Bl3BooTX3BBD3EapdF6s00XvDAIDsscHPmsongTxlEYbVm0y6Ik 2yTsh4j+KK6zaHIfvNmk8Xa9W/CyM+CEVjxbJclDGlEy3TSoHBwk8syZDhfcOkpPqSuhhK2xpEza iKNQIPNCK0fuuRUfySWOkjTZrakmCdbljS5FJShgKmsVrlYsfqDis006l2VrMEIdyavtDlIUlN/B MZfCekD01KJpQAp1okf/IVTQgjkFVBDIoAH7Z4cGSgzgLMBpYwODjfiLIq0jbppbMNgcsCy9JT9c KR9ISX6lviipobdPsNvOFDVYzDsjeaY6KT2sA8qewuF59nHCecIG6xGV8YTtpUOjiOAzsve6b93o x596GOwdwWAB+8kDYb/OQNh3+xEJe++RfO9LRFsY0Q7d4i+1sIxerdEHAnNlzZVVcKa+OGRP75jV 6rigd/bIftm/YU6znhIm3JJlbz6oD+qlxslmGTAiRJVMV8y6q0TLLsLVTBMDSBxScUS4swv2LBSy R1XU7BmEtAF67hmNZ0dugvys7lRJjkDZ+n/pAkPYWYOSxgHtkj06dgH/m1WnPIBXLByrtGG2M7gk qAPLCpqZUYMSqRUTwb0QcndtZ8sJrzSDRyKbckxuwppi+n4WJerbVh7aZjobs+dXBHNva2jA63tj CTQ6Q69nJRCApldg0649BlGg8hX7CQtILrXzY0kKPPcK6oygs9q51mZhCK1Y9swVUnflkkQY9hht OOuccnr98uxTr/9ZwuNu6M++lMqkBsrWaiv85Ngxy8At/803621FozDNW8/+P0s32viNsk6z7WaW 7n+i8GXZcCyWks9SHlrRE0D477D/S+VwBgfmJtJTJ+KlVWVRqp61wcUGfuOtd6vkgbZkkH48R7tD IMEccfnaHvnncRe2Eq4H2qm0xjpFyyYe7ZOSeFaBtLQCv73s8OsWFqnsos3pdlAvcEY/XHe4L1/g fv39cV9UL+mP9fPPebNsaZ1O2+8u7n+MWjisTcow7cVvyDGF+qbf0byJknjnb59x6070p9tkRS0Y 2acrwU4nD9vIX3TD3TQZV/FqwcE5KGp/ZVkq8is1Fd5E+zo1qdZfCvunp7d/8M9/A1D87Dh5BwAA headers: access-control-allow-headers: ['Accept, Authorization, Content-Type, Origin'] access-control-allow-methods: ['GET, PUT, POST, DELETE'] access-control-allow-origin: ['*'] access-control-expose-headers: [Date] cache-control: ['private, max-age=0, must-revalidate'] content-encoding: [gzip] content-length: ['912'] content-type: [application/json; charset=utf-8] date: ['Fri, 02 Jan 2015 17:19:38 GMT'] etag: ['"7c52b4eaedea7f96c5ad520d99716a45"'] server: [am/2] set-cookie: [_session_auth_key="iuo9QNN6oaPVa3gj9XyKTHZtsxE="] status: {code: 200, message: OK} - request: body: null headers: Accept: ['*/*'] Accept-Encoding: ['gzip, deflate'] Authorization: [!!python/unicode 'OAuth 1-35204-61921957-55796ebef403996'] Connection: [keep-alive] User-Agent: [python-requests/2.4.3 CPython/2.7.8 Linux/3.16.0-28-generic] method: GET uri: https://api.soundcloud.com:443/tracks/170437996.json?client_id=93e33e327fd8a9b77becd179652272e2 response: body: string: !!binary | H4sIAAAAAAAEA5VVXa+bOBD9K8ivGwImEBLebnelqlKrrlaVrrq6EhrAATfGdm2TKK3633cMIeVm +xndh9zx8fHMmTOTz+TIZUMK4gzUR7IiHP+heZxu8v1+uyK1YeBYU4JDTBLTNKJxFG+COC+ybRHn wR8xfvDeYJkp/eV0s8G/Dd5tBgOOK0mKJE2yNEE21fdMOqgEI4UzA1sR65AfuQ9cctuxBqmU4S2X IMpaSYfw0vJPCEl3WRJv6YoIsK7sVcMPHPFjWlkU0yhOApoVWVpkyS0t24HhskWUHirBa6R30JaC W1/QE3kQjhmJaZ5Y8I+qj08keCEGZgMNAmoWPBEenEG64NyBCy5qCFrlEFShLJIfg4o7JAhOjEuL 4Dk8yoacTwQf1Mz0ILg84os89GyhZwuRLUS2kCLGOhS6XwrD+oo1jY+U1QVvghCIa9RZCgVjnBQH EBY11IOpO7CsHIxAZOectkUUcTdIZtegtWBrlD5qqwhENfSRUNaFXIauY6HkbedCpiPe7PZ5QndJ 7hMSUDExNlQOQizecNz57pG/rpkgtmXS+NBCTIzOwFfBoxfw0Qv4HgV8qZwvhNnacD35w6s0PSih 90wvrvL+6e2HOuK5YYJhjaSY8hn9WrqLvkWO7IJOabGZg89mgnFr6vn7iTdMTRpNh5Xu57Mre3lh YO5jPdqwuw82gE2ZaG5+PShstPdVrze+Il4z6TP2vQvR1J2zoWE4KKfR54PheDZ3CzRfWzXIphZq aMZ+jTXa6DaOyOnHjBSfxzH9OmnXGR4Pn/ttsnEFOOPT5avAf4/2LsvS5/n/ecIxTyK6DWhabJIi 2d3m6ac5+xxsNKeG7Df3L9yJ5rwr9VmecAIHZoEf3UzXFsVp5CjNBLEhLh8aZ9uM0j0NWx33DEIB pmXrD7olX657SQu4VLjfcKMMEhuES2TcVwc4YfP8/pknaR7VxeM/Sjb63jyDcWdljguab9QwYcYi 4v0mjXOa5KGozEfDFkWsyBlOzHvrju38TJF3h3/7ff32w54+vCn7tcalN6+Vu3u/4LRo2ke+fffS xVm+i78uollSDF3VxHVr52iy3WW3tT8HM5QfnIO68z8GFrP7zTmIFrd9igonza/Ih9ev3z6SL/8B eQBtr9QGAAA= headers: access-control-allow-headers: ['Accept, Authorization, Content-Type, Origin'] access-control-allow-methods: ['GET, PUT, POST, DELETE'] access-control-allow-origin: ['*'] access-control-expose-headers: [Date] cache-control: ['private, max-age=0, must-revalidate'] content-encoding: [gzip] content-length: ['863'] content-type: [application/json; charset=utf-8] date: ['Fri, 02 Jan 2015 17:19:38 GMT'] etag: ['"1da67a79619ce66a4b2cb813b646db5c"'] server: [am/2] set-cookie: [_session_auth_key="iuo9QNN6oaPVa3gj9XyKTHZtsxE="] status: {code: 200, message: OK} - request: body: null headers: Accept: ['*/*'] Accept-Encoding: ['gzip, deflate'] Authorization: [!!python/unicode 'OAuth 1-35204-61921957-55796ebef403996'] Connection: [keep-alive] User-Agent: [python-requests/2.4.3 CPython/2.7.8 Linux/3.16.0-28-generic] method: GET uri: https://api.soundcloud.com:443/tracks/172570926.json?client_id=93e33e327fd8a9b77becd179652272e2 response: body: string: !!binary | H4sIAAAAAAAEA5VV247TMBD9lcivtM1l02STt2pBCIn7cpHQSpGTTBNTxw6206is+HfGuW23gIA+ tePj8ZkzZ6b35MBESVJiFC0OZEUY/vDjYBt7SRCtSKGAGigzahATeH7o+p7rx44fpl6SbrfOEw8/ eK/ToDJ7OQi9rRfFK1J2ihomBYaCMNjaZLJpQBiacyCpUR2siDaYHlPvmWC6hhIzScUqJijPCikM wjPNviPkKomSOIqCFeFUm6yRJdszvDDTCtwgcPwk9a9TP1lo6ZoqJipEtV3OWYH5Da0yzrQt6I7s uAElkOcRnPeyONwRRLSgGsqZOCCkQJp8XTBzWgd4pA0K0pxXAE0OZWkjWX7CC5RzxJWyF1zSIU7S PeUai207VdRUQ9YpTlLRcW6LyYEPwo2/F4xhxso0RisQysp0RtdWMkLIjeXo3CBH+zLoQrF2VJ68 USUox9Tg3Dx1PzFx4u5TlNdQ7jg1KEid2pg2dd1WbrRxd13VacME6FsjFdwJe6rxuO/7zZ4WkEt5 2GAf3R4oKrHAH5CmZwY1HUCfYafgIecIwmxMYN8rRZs/p7JvPn5io6koc1rV34ZbWOqonaDNopMC DijwLNtg6syc2iVygBP6qcKOd1bQUV2mVTF/P7IS5HmD8raZz6bs2QmJXcYaNGt9GSwpOmJ8Y3H1 XqK5rPl6erQ1sAKEZWyNs0br10avFeA0HYdp6BTDs7kNtGUbLTtRFlx25aDDUKPGmZxmFnPaWSTp /TDLyzhOcz6cPbb4hc5TglFWsvv4/OPthxevn90Ogv9u8jzXu7aT512ngbdM3l+JWybanfhh8mXq RvWHmtEFF+X+SpYeqaHq7JL1K/M3GlUqxaDRCNFru6uiILyOkjBZQ3K84uGaU1XB5mtbkR/TFms5 PeW4DXEBdQI75U/xPT1iF+26mud53hNnj/+VsXuxUagyvVSHsxS/4T9ixgKSMIi9MI7XQWuotz0r YGU9BdZgF9n6R2rcvuP1t1fNl9cf3n7Nmk2L63FebBf3/sFu7rgRbf8uZIu3V/72YRPOanorMgmJ e1nPUT+KUObpD2IOhlcrQo2hRW3/NjSS+89ZcM9uW4YSp83u6N3Ll28+kx8/AShGE5b9BgAA headers: access-control-allow-headers: ['Accept, Authorization, Content-Type, Origin'] access-control-allow-methods: ['GET, PUT, POST, DELETE'] access-control-allow-origin: ['*'] access-control-expose-headers: [Date] cache-control: ['private, max-age=0, must-revalidate'] content-encoding: [gzip] content-length: ['852'] content-type: [application/json; charset=utf-8] date: ['Fri, 02 Jan 2015 17:19:38 GMT'] etag: ['"afae57b7f16e6013976ff05ce8d23fa4"'] server: [am/2] set-cookie: [_session_auth_key="iuo9QNN6oaPVa3gj9XyKTHZtsxE="] status: {code: 200, message: OK} - request: body: null headers: Accept: ['*/*'] Accept-Encoding: ['gzip, deflate'] Authorization: [!!python/unicode 'OAuth 1-35204-61921957-55796ebef403996'] Connection: [keep-alive] User-Agent: [python-requests/2.4.3 CPython/2.7.8 Linux/3.16.0-28-generic] method: GET uri: https://api.soundcloud.com:443/tracks/173263597.json?client_id=93e33e327fd8a9b77becd179652272e2 response: body: string: !!binary | H4sIAAAAAAAEA5VU2W7bMBD8FYGvta3bh95SIE2LHi6StEWBAAIlrWVWFCmQlF0nyL93qcNxnKKH 4Qeb3F3Ozs7sA6mYKEhCjKJ5RSaE4R9/EQbzMF4tJiRXQA0UKTUYE3h+5PqeGwSOFyTxPAmXzisP P5jXalBpn+zFK0yekKJV1DApSBIEkRd4WE3WNQhDMw4kMaqFCdEG62PtDRNMb6HAUlKxkgnK01wK g+GpZvcYEkVLf+7PJ4RTbdJaFmzDML6DFbuej18nQFhxEgdHWHpLFRMlRjVtxlmO5Q0tU860beiO 3L69dD6vb97dvlt/urkjzh254AaUQOA7cK5lXt0RzGlA1ZQzUWFSBgJ2koPIYaqYNBrvtUGe6tO+ oM6gKOxJmh0wi3KOcYXcCy5pd06SDeUaKWhalW+phrRVHCO3xjQ6cV1mWgF6RpuGwwyZc2nrUp61 tfsCwVRjjxxcVqzCcB5HkR/hY5xmwLuZiJbzk3cMM3YA/WkJQtkBXK2/Y85wRV4/NelcD00WoHPF mn6klpX+AUFrm/9R6oOjIJeqQDCWFAUcsK3xoU5hqTk0x5MKDjjbEsluLYQeD9MqH3/vWAGyp6W/ zJp6vBuqpwegChWG0jy+iOIQZos69p+OCopTQEEfxbWROFIrgroJbS8sB2HB2knhXMut0VMFqOpd J8pWMbwbZ0MbNtOyFUXOZVt00+na0+7RO1jTeoIkD4OnRlsMhusunyurpKKUm4NszdbyZyMGcq8u Pl056zfO9/WX27c3Fu5LD6A10QOhtWa4SoLo6IG/QrcPWeQ9Qqx+lPuJJFGRZx2fwaU7aqg6yehE 7M80slSIXsFdiJ7iyvB9bxl584U3XWhRqfspp6qE2Y+mJI/DNmk4PWS4lXAPtAInhdO0SNMN3eEU 7dYYDTS68+TxP8N9aSHsmiqzl6o6KfKbDvqYrgVvhRsp9oL59FDt5b46aWFC9nQHVmJn1fbP+Hi/ WlyKLz+X3levTutZg4tq3CVnef8gOLdfQnZ858TFcRCHT9tnJBQ38sCldex4ilbCJTus6vEwXiI/ xtB8axe4RnT/aQf3JNtClGg4uxcvPnxYfyOPvwAWJ2mziAYAAA== headers: access-control-allow-headers: ['Accept, Authorization, Content-Type, Origin'] access-control-allow-methods: ['GET, PUT, POST, DELETE'] access-control-allow-origin: ['*'] access-control-expose-headers: [Date] cache-control: ['private, max-age=0, must-revalidate'] content-encoding: [gzip] content-length: ['835'] content-type: [application/json; charset=utf-8] date: ['Fri, 02 Jan 2015 17:19:38 GMT'] etag: ['"f9f43353692c61f586759a81c789f79f"'] server: [am/2] set-cookie: [_session_auth_key="iuo9QNN6oaPVa3gj9XyKTHZtsxE="] status: {code: 200, message: OK} - request: body: null headers: Accept: ['*/*'] Accept-Encoding: ['gzip, deflate'] Authorization: [!!python/unicode 'OAuth 1-35204-61921957-55796ebef403996'] Connection: [keep-alive] User-Agent: [python-requests/2.4.3 CPython/2.7.8 Linux/3.16.0-28-generic] method: GET uri: https://api.soundcloud.com:443/tracks/157945262.json?client_id=93e33e327fd8a9b77becd179652272e2 response: body: string: !!binary | H4sIAAAAAAAEA5VV247bNhD9FZYveahlXSxZa71t0wBpsWgX3aB9CSDQ0thiTZEqL1a9Qf69Q9Hy ep0gbRcwsBzODM+cmTP6RA9ctrSiVrPmQBeU4yEtyk1eZOtsQRsNzEJbM4s+WZLmcVLGyYakabVK q/yOfJ/gH8Y5A7qegpPV3SrPVumCtk4zy5XEjGV+lySYTvU9SMu2AmhltYMFNRYfwOQ7LrnpoMVc SvM9l0zUjZIW3WvDn9ElKzeYebOgghlb96rlO47+E64iTtI4yUiaVWlSJekFl+mY5nKPXoPbCt5g esv2teDGV4SnAXTPBJcHPIJsBRgTGQGA9ijFe2ORgv4aMfRbaFtvqbcnjGJCoF+rRikUm+y02jFh sLjB6aZjBmqnBXp21g5VHO9B8qUz8SOwjt/37Bkp8lVtQUwUSifEVazl1tNFf3AnItVIlCSXIEyl /d29sKAlsn0E8puaOjmHvQtFkadQlIcKptF8CK2hH/VH6X8/ngsgtuOGTPNAdkqTnQYgzFZkHMel 7UCzIygJ1oJZYj996C8wEia2rieP797cv/+JKGc91O/m3PyDk2Aq8qryYPQuoZyb62D010+Dsnx3 qojpmbZI5ZLbQN7TgLPQne8vHErWe07wrEEA0h8OU0m1PQ3+PM8YOh3ghBO2R/bcxCWauNFNCDry FlRoX2jLduhpFf49Z69PwDSOJ6rj8iKOp7QdrcoXS8twWDLU1Px0jez2k7D6YeXB8wbkBBYnKkIN dNZEGlBYx0kWTnPE5GfI4BCxgS+NcrJthHKt70Q8FWjii3wxp5clrT4FWb8o8yz66fa1BLDB0VWH zynOjH7ocLpe2u8xfynFPE6TOF2TtKhWSZVtLlL8V/weDsKfYWL6izhfC+i27i8wsyOzTF8FecZ4 ujRIVysnsoKLifz+2uT5Zl0UeSQOTVkeIsH0HpZ/Dnv6+bzZcM5OW9yQuJKcxM2B223aeDt2xHb6 BTYrfl4nV4/j299GHH9t8eCoj0ofrvJ8pYjgE6q4y9clbu4iGtblX8P+qooFHbFrftxuso2vKHl8 OP7+8+757du/2/d1vxxwbc777ybuPwxfHBanb+INd4iyQBnMC3OmFD8PZzZxX5vZmhZ5cfluzEb/ EWDWsqbzXxOD4P6nMuKraI9Qofb8Kr9/ePj1D/r5H0foVmgWBwAA headers: access-control-allow-headers: ['Accept, Authorization, Content-Type, Origin'] access-control-allow-methods: ['GET, PUT, POST, DELETE'] access-control-allow-origin: ['*'] access-control-expose-headers: [Date] cache-control: ['private, max-age=0, must-revalidate'] content-encoding: [gzip] content-length: ['894'] content-type: [application/json; charset=utf-8] date: ['Fri, 02 Jan 2015 17:19:38 GMT'] etag: ['"3a2806d130e1fcdc7ba3f5f3559b2102"'] server: [am/2] set-cookie: [_session_auth_key="iuo9QNN6oaPVa3gj9XyKTHZtsxE="] status: {code: 200, message: OK} - request: body: null headers: Accept: ['*/*'] Accept-Encoding: ['gzip, deflate'] Authorization: [!!python/unicode 'OAuth 1-35204-61921957-55796ebef403996'] Connection: [keep-alive] User-Agent: [python-requests/2.4.3 CPython/2.7.8 Linux/3.16.0-28-generic] method: GET uri: https://api.soundcloud.com:443/tracks/180099816.json?client_id=93e33e327fd8a9b77becd179652272e2 response: body: string: !!binary | H4sIAAAAAAAEA5VUS2+bQBD+K2gvOdQ2LME2cIsUtYdGqtpLL5HQAmPYetml+7DjRvnvneXhOlaq phYHM8zjm5lvvmey57ImObGaVXuyIBxfaBpFWZbSzYJUGpiFumAWfeKIJiGNw2gd0CSPkzyJgg8R /jDOGdCFD463lKZpvCC108xyJTFftk3pFpOprgNpWSmA5FY7WBBjMT2m3nHJTQs1ZlKaN1wyUVRK WnQvDP+FLhndxHGMkAQztuhUzXcc/QdU6zCi+AQxzdfbfJ2eUZmWaS4b9OpdKXiF6S1rCsGN7+eR 7DRAUKujFIrVjyR4JBKOQecMr/CtYo1yAgJgOJwWHUEiFBlIZS1mbVmH+XrQHRNc7jFh40yLJmNx at1lm9CVUNfeUpQndGRCoN9cePTcMWFwIr3TVcsMFE4L9Gyt7fMwLLldiVO4HxEtayW489UFK0EM c5dOiItoy62fMvnoO7yfOsSABqT29jthQUtc0AGCb2pY/RzyaeyiBlNp3o8rJHOK4OZ+qH0T7JQO hvm1oCF/lO9CKlmH1UesGgRgo/PrwMDCnvqzZQ8nXH6DKJ3HPEZxo6v5/4HXoMZBjR/Lvpu/TdmL E27v2tYhs9prY81wM2OaMwWxx26gftff+mnzCqRH7Be4RJ621iw1IPUPA3Wd5tPKDO6M9XxllJN1 JZSrV8j+cOjRhOcDw5z+cEj+PBze+Xamoxy+vabYxIApcBwn+TzSwiP8+3FE6zze5DQ5H8c/0fry JpxAYfIz1V9z86rHPwjZgVmmL5z9VDhdGRxJLYeBjC5miSJC4yjeRGm6We5dlGRPS8F0A6sffUNe Jn3pBTuVqFMoDU7iBdPJvmMHXJkXkvmI5qO8KI6130YaTmfLtD0qvb8IeQPv6DMARo2MM5rE6VKo LP5pLwAvyJEdwLPnKtvxVffia5zyEp5spm6LbtWjUs3qcRX3Di6Fo+z4PV2NKUmiNcrvLDfz9KIF mQaHYmZma0bXZ6WebbfoyqxFEfT6bRDbf/I8vIj2AFFCKq+Ddw8PX76Tl9+iD6WChgYAAA== headers: access-control-allow-headers: ['Accept, Authorization, Content-Type, Origin'] access-control-allow-methods: ['GET, PUT, POST, DELETE'] access-control-allow-origin: ['*'] access-control-expose-headers: [Date] cache-control: ['private, max-age=0, must-revalidate'] content-encoding: [gzip] content-length: ['793'] content-type: [application/json; charset=utf-8] date: ['Fri, 02 Jan 2015 17:19:38 GMT'] etag: ['"38831204ed9ab3ebf49b380963c41d63"'] server: [am/2] set-cookie: [_session_auth_key="iuo9QNN6oaPVa3gj9XyKTHZtsxE="] status: {code: 200, message: OK} - request: body: null headers: Accept: ['*/*'] Accept-Encoding: ['gzip, deflate'] Authorization: [!!python/unicode 'OAuth 1-35204-61921957-55796ebef403996'] Connection: [keep-alive] User-Agent: [python-requests/2.4.3 CPython/2.7.8 Linux/3.16.0-28-generic] method: GET uri: https://api.soundcloud.com:443/tracks/172322693.json?client_id=93e33e327fd8a9b77becd179652272e2 response: body: string: !!binary | H4sIAAAAAAAEA5VVy67bRgz9FUGbLmpZb/tau/SRLhq0aG6AooABgdJQ9tSjGWEedtUg/x6OZPn6 ug2aemVxSA7PIQ/nY3jikoVVaDW0p3AVcvpIt1meZZtdvgpbjWCR1WDJJ0vSIk6TOC2DLKnKrCrT 4NuEfhTnDOp6Cs7zfFekFMucBsuVpIRPxWa7oWyq71FaaASGldUOV6GxlJ9yd1xyc0RGqZTmBy5B 1K2Sltxrw/8mlzwrN2VaJKtQgLF1rxjvOAVMdZVxksZJFqRFVWRV8nSryxxBc3kgr8E1greU38Kh Ftx4RD8aqyQHGezDZ8GHAfUYPAs14D4kR/rsQXB5Is8WNHZOkNVY4qS/x4B9g4x5S92M5AvC+zF1 kULBZA+rDoQhuIPT7REM1k4L8jxaO5gqjrl1Es0ahkHgmliKEWMQjetjpqJRuahDFJE9YmSgx5iz XZ6XRbIrc7pIQINi4l46Ie7usNx6osPv3BgoGfAP/hIKOKDU3v5GWNSSenTG4L2a2r+EfH+Dy9C0 mg9zI8NMsmAalaDTqieu7kn7xgQMG2eDqXTi9AcV/KFc8JaKDz4cMXim4vfhei/3ckF+uVzWHbTY KHWakJtrSuPboLqOtxw8nTNKSRmo8rcg27FRfwXvsVWaeVAaBRKxYTWTMBVZ23G4WU440iQdCK/z 6Gc3bnS7/D9zhmpuzHzYDP1yds1ejwj60dbTmB4fjQxoEuY0t3nuFM2TH7sLnD0i3qL0FfuBiWjo j9ZEGklI50kHTnM6W4iCga+NcpK1Qjk2UTVhNPFNrpTTyzCsPs4yvinxqvHp8PVYf4lt73rl+nWP p078Q360Fkh+myApq7yoshf5/ScGfxFBuJZK2W+iu5MIKeQB+pfqhjNY0Hehk7rStSHemJxYm11M RHsrTcrtJt2VZdQ3I8M0EqAPuP5zOISfrittEDA2tBppGTlJvUuv9g7O1Fe/uhZlL8vi7vKvrDt+ WS6g7UXp012OfwEw+0wIElq1yfZpm0bZBWy+uUOw8mOGfuYesl1e0fHLzz/9VoodT9kz1P16oF25 7LiHuK+YwHhejr6Nj7yVRVYQectWXPikdX6lkra0Waz5tnx5Lhbjlt4UsBbao39EDFX3P/UR30X7 EhUp0O/rN+/e/fp7+OkzkzBI/AwHAAA= headers: access-control-allow-headers: ['Accept, Authorization, Content-Type, Origin'] access-control-allow-methods: ['GET, PUT, POST, DELETE'] access-control-allow-origin: ['*'] access-control-expose-headers: [Date] cache-control: ['private, max-age=0, must-revalidate'] content-encoding: [gzip] content-length: ['878'] content-type: [application/json; charset=utf-8] date: ['Fri, 02 Jan 2015 17:19:38 GMT'] etag: ['"17967c5ed84d3b941bef99c44c95e9a3"'] server: [am/2] set-cookie: [_session_auth_key="iuo9QNN6oaPVa3gj9XyKTHZtsxE="] status: {code: 200, message: OK} - request: body: null headers: Accept: ['*/*'] Accept-Encoding: ['gzip, deflate'] Authorization: [!!python/unicode 'OAuth 1-35204-61921957-55796ebef403996'] Connection: [keep-alive] User-Agent: [python-requests/2.4.3 CPython/2.7.8 Linux/3.16.0-28-generic] method: GET uri: https://api.soundcloud.com:443/tracks/166785347.json?client_id=93e33e327fd8a9b77becd179652272e2 response: body: string: !!binary | H4sIAAAAAAAEA5VVyY7bOBD9FYbXsUwtlhfdMgmCBAgSYNDBXAwIlEjLjClSISl71EH+fYraWu2k ZzF8sIuviq+2x+/4IhTDGXaGlhe8wgL+RNvtbp8mm90Kl4ZTx1lOHWDiMNqQ8EDCPYrSLNlk4Rb9 FsIH/FrLTe6dE/CN9skKs9ZQJ7TCWZxE8T6GYLquuXK0kBxnzrR8ha2D8BD6JJSwZ84gkjaiEorK vNTKATy34hEgaZpCWKAkqXV5rZk4CcD3rFISRiSMUbTN0jALdzMre6ZGqApQTVtIUUJ4R6tcCuvz OWIqHTcKaF45Mrq8HDFiJYsB1nBTUynUxZOjUga6dUGhu6AESq0R3ALIOihPvcyH1wVnzFvyogNX 8AQc0zclNe3tOINwFlJvWlOeqeV5ayQgz841GSG2pgbiy7VwZHmVpAWXfYFVK+XC2wnnyzlYK66M L+frp7x8ygMEv1lQZ9yWRjRDg/AnfkMWCiU5Oj7BoBqQNVL6tkJTCkgrJB5axS36J8bIR6SyaGsE XYfIyGrwhO/bN29j8sFKqhj6g5faMPsKHdVRfWlGpNOtQQzmwmbTHb4DQAXqvwYQ6RHeZ6Tw/Hhh Lnmh9aX3ecLM5+4mHPT/peMObmwL/tKxUDC8laH1SwDbaCfWJ0Eeku5d9eGooBVDGxWt55YZLjlM wdTBfg9z1zWz5cI7WIEKprT1vR0aLawpp99Xwbgepmg4LJp6Ohuj5x2n5t5Ww36d742MwtgOYeZF PGnYBb8wdZP4HAQMpmfspzuAbT07GxgOAnDtFxi2Y5xnCwNNG7G2ulWshA6yvlZ9jpbMMgMxvXzg 7HsvP7OCjNLUn/28kcM8jL5DRfE7YPS5db/rrq/1T0KxIVFM4gTFCYhctglnofhXzp6EJSM1CD7r w936Ps/0aejAhV6po2aB9+UR0dpCbZjqKzNAbACaGsVRnOzSQxIwuf/LhoGkpuLrr02Ff4xy20ja FSDboJStgv5Eo/1Er9A7r6twfy81k5gtLvda8xJZ4nn/UvFAnG7aXBZxfpHEgOmzCA/hJtrF2zBo vj0Wu2+LLFb4Rq/cz9ZdtNuzkmwfuvfho7ymgn3J63UDaj4J753ff5g0Mii2799d7fbbw3aT+mdr 1OqpqOEK9L+vJ6iYnazR/pBGm/lJm8xJ6F8o6hwtz/6ps0Dxfy4DWXh7nhrWzb8krz9+/Pwn/vE3 DcQHabEHAAA= headers: access-control-allow-headers: ['Accept, Authorization, Content-Type, Origin'] access-control-allow-methods: ['GET, PUT, POST, DELETE'] access-control-allow-origin: ['*'] access-control-expose-headers: [Date] cache-control: ['private, max-age=0, must-revalidate'] content-encoding: [gzip] content-length: ['920'] content-type: [application/json; charset=utf-8] date: ['Fri, 02 Jan 2015 17:19:38 GMT'] etag: ['"32eed85c2543f08cb8eef0618e2399e7"'] server: [am/2] set-cookie: [_session_auth_key="iuo9QNN6oaPVa3gj9XyKTHZtsxE="] status: {code: 200, message: OK} - request: body: null headers: Accept: ['*/*'] Accept-Encoding: ['gzip, deflate'] Authorization: [!!python/unicode 'OAuth 1-35204-61921957-55796ebef403996'] Connection: [keep-alive] User-Agent: [python-requests/2.4.3 CPython/2.7.8 Linux/3.16.0-28-generic] method: GET uri: https://api.soundcloud.com:443/tracks/172120478.json?client_id=93e33e327fd8a9b77becd179652272e2 response: body: string: !!binary | H4sIAAAAAAAEA5VUa4sTMRT9KyUf/GLbedZ2BhZREQQFUUFhEYbMJG1jM8mQR5cq/ndP5lFnlxW1 LLR7cx/nnnvu/UFOQjFSEmdocyJLIvBPsk2TNM63uyVpDKeOs4o6+KRxkkdJHCX5InlWZmmZbxZP Y3wQ5y03VR+cptmuKIolYd5QJ7RCwk2WxTGy6bblytFaclI64/mSWIf8yL0XStgjZ0iljTgIRWXV aOXgXlnxHS7pNk6LZJcviaTWVa1mYi8Q0OPaRHGCv0WalJttudldcdkjNUId4NX5WooG+R09VFLY 0NFX8kI6bhRwnvnio25OXwk8Om5aKoU6weWsRcMtjNaBi3aOnbc1ZyxYqvoCVyol/Ji+U1LT3k7K PZUWbXbeNEdqeeWNhOfRua6MIuG84nZNu07yNciJvI2orH0bKb1qPVvhS2oHq2BFWmS77S5Onnt9 kz+h7iZJJH31GhUlrbnsyVdeylkxJ1xgerAeuDKB6dAlgsY38nnqj3HbGNENEwssDGkVba8pDJcc TUwZe81U7tJdLSd+wbQO4NOHWkNhYU0z/T4LxvVAwvBYd+30NmavLpwajBtiW5LJ1kIKRwgp+W1i FJynM7nsNaYWpkrFfh/wY3AqoA2DWUFUR2dXhkOo515n3ohxFBazoJ1YW+0Va6T2rJ9G35+NruuA nEHmpPwxrMlV6eMO9Y/31XOiDmI1egwdyCRvf1sf0TJ2LI3SfJFmZQ4551ct/xVwAAC8Iy7UvAr5 vu4e9DkDSc+AZmbegRqRrC14YapnZXCxK+x9EmMh0ywtVl4f+ea0ktQc+PpbdyA/x5PQSXqpcVqw zF5hOBhgfyr29Iw9D6s/bci0c7PiqP0HqNF1Lalxd9qcZlGPQB58esxxkRWbeFvEq/qCa9TMMC/J HT3zIKMH2e7uEVC82b+8/bC7ZZ/c66pdd7gu03V4EPcPmoqGsxJm9YCpZ7t4A3lP52QiEGd05A5n zU7WZJuD2fG+TsYMvtQ52hzD1bUA95+Cj2bRAaHGSoVD9+Ldu/dfyM9fmFC20j0GAAA= headers: access-control-allow-headers: ['Accept, Authorization, Content-Type, Origin'] access-control-allow-methods: ['GET, PUT, POST, DELETE'] access-control-allow-origin: ['*'] access-control-expose-headers: [Date] cache-control: ['private, max-age=0, must-revalidate'] content-encoding: [gzip] content-length: ['791'] content-type: [application/json; charset=utf-8] date: ['Fri, 02 Jan 2015 17:19:38 GMT'] etag: ['"b972a18ac1d4f79b31cdad19c3bc5e8e"'] server: [am/2] set-cookie: [_session_auth_key="iuo9QNN6oaPVa3gj9XyKTHZtsxE="] status: {code: 200, message: OK} - request: body: null headers: Accept: ['*/*'] Accept-Encoding: ['gzip, deflate'] Authorization: [!!python/unicode 'OAuth 1-35204-61921957-55796ebef403996'] Connection: [keep-alive] User-Agent: [python-requests/2.4.3 CPython/2.7.8 Linux/3.16.0-28-generic] method: GET uri: https://api.soundcloud.com:443/tracks/164767462.json?client_id=93e33e327fd8a9b77becd179652272e2 response: body: string: !!binary | H4sIAAAAAAAEA1xTTW/bMAz9K4IuOSyNv9Kk823YUBQYCgy97BLAkCUmJiJLBiWn8PrnR9ltltWA AZuiyPceH9/kGZ2RtYyk9FmuJfJPsdvud/vtrlxLTaAimEZFzinzYpvlD1l5L8qqru7rci++5Pzw vTEANelyVT7sqnK/XUszkoronaz5Yl7lXM33PbioWguyjjTCWobI9bn2ER2GDgyX8oQndMo22rvI 6U3AP5xS3W+3+yLnMlaF2PTe4BH5wjuuosyqQhS7uvha5w9XXKFThO7EWcPYWtRcP6pTYzEkRgf5 zUYgxzgvIF68Ph8kZwxAvbLozjMw4tQElKXob6FD34IxKdK0E2cqaznP+FdnvZrjsj4qG5jlMJLu VIBmJCtrN1qbWLRgZ8mW/2tOxJj0WaIncJT0ucGZKCwp8hEXcAaCJhwWteUj+V6snrw14nkST76H 9Uqoi0JuaUEUeVYWwjvxg6FGfsULaE8mbA7uF8EdfwKJ2IFQth17Eb0g0JAUWs0NVwIdz81FO9Wi i3Gosyz0iiKz22DMvv/+mZo/T6n1wTHehatT/ZUXgQUW5IPmbL8mTsM1coaJB3/i0YxJgEUNDKQ/ vi9owN8K2g79x9l79WYCRZ9jPbuq+xw0iie49Lja7+jZBcklr+qSOKAGlxCnQd+xR7sY7gjY95fZ tiMhnyU5AuuhBtwEPzqjrR/Nhp2fzRxDdt0urpm2RtZv89b9W5z3lZwP/zejZllfFZ3RBPkXAAD/ /5WVy5KCMBBFf8ViLa8QIbijZqlVLl1OZUZx8AGY4Dgs/PfppokGyprHNqSTm9t9D1RNnjov2Oy1 VJMFfXoaEYhuABFhcz6b8/gekV+FoxDtG31w8T0fZH/3aJyB4XuHYmH+GqmsAnSpCD0NFm3KziDa ol1EShSkLIxY6hZncdqX7lGq3dbb1zvn1sOmPsr2DaAFmLiU0KawX8/lJ7QQoWLCZ9JsXf6jWj/v YwUjfa3Uwap7Ipr2kGqRslSIJHCVrOK8tVRPcYq2OFKj065DC7Ii23yFB6GS9vXk1UAug55R3R8G zCdmYcPGXnEeB1w8aGVMBLr2/gE0tVllfNb9DAjfZjVKoqkjm0a+fyDVNQj8ZwJ8qxpVVpAxJGm2 XK7Wzu0b9S5Rp50GAAA= headers: access-control-allow-headers: ['Accept, Authorization, Content-Type, Origin'] access-control-allow-methods: ['GET, PUT, POST, DELETE'] access-control-allow-origin: ['*'] access-control-expose-headers: [Date] cache-control: ['private, max-age=0, must-revalidate'] content-encoding: [gzip] content-length: ['869'] content-type: [application/json; charset=utf-8] date: ['Fri, 02 Jan 2015 17:19:38 GMT'] etag: ['"66dbb4584916e51f1a18d35581065139"'] server: [am/2] set-cookie: [_session_auth_key="iuo9QNN6oaPVa3gj9XyKTHZtsxE="] status: {code: 200, message: OK} version: 1 ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1608019795.0 Mopidy-SoundCloud-3.0.1/tests/fixtures/sc-resolve-http.yaml0000644000175100001710000001614500000000000023336 0ustar00runnerdockerinteractions: - request: body: null headers: Accept: ['*/*'] Accept-Encoding: ['gzip, deflate'] Authorization: [!!python/unicode OAuth 1-35204-61921957-55796ebef403996] Connection: [keep-alive] User-Agent: [python-requests/2.11.1] method: GET uri: https://api.soundcloud.com/resolve?url=https://soundcloud.com/bbc-radio-4/m-w-cloud&client_id=93e33e327fd8a9b77becd179652272e2 response: body: string: !!binary | H4sIAAAAAAAAABTJSwrCMBAA0KuUWWtjJzSfgrjzGjKdRAzGpJjJSry7Fd7ufaAJSW+wgD7hcByu tZcAB8iVSVItezxEtrYoRVsa27851x5Gri8lb+JnUxOic96Y+cI5xSK3FM5eR71Dew+O/GrtGjlM 1psZ0WJE+P4AAAD//wMAI6hydXwAAAA= headers: access-control-allow-headers: ['Accept, Authorization, Content-Type, Origin'] access-control-allow-methods: ['GET, PUT, POST, DELETE'] access-control-allow-origin: ['*'] access-control-expose-headers: [Date] cache-control: [no-cache] content-encoding: [gzip] content-length: ['137'] content-type: [application/json; charset=utf-8] date: ['Thu, 27 Oct 2016 20:33:37 GMT'] location: ['https://api.soundcloud.com/tracks/122889665?client_id=93e33e327fd8a9b77becd179652272e2'] server: [am/2] status: [302 Found] status: {code: 302, message: Found} - request: body: null headers: Accept: ['*/*'] Accept-Encoding: ['gzip, deflate'] Authorization: [!!python/unicode OAuth 1-35204-61921957-55796ebef403996] Connection: [keep-alive] User-Agent: [python-requests/2.11.1] method: GET uri: https://api.soundcloud.com/tracks/122889665?client_id=93e33e327fd8a9b77becd179652272e2 response: body: string: !!binary | H4sIAAAAAAAAAJRVy67bNhD9FYIFuqn1tmxdLXPRxyJdtE3QpUBJY4k1RaokZUU3CJB/yKq/ly/p UJIdXSPIwytydGZ45szDb+mZy5rm1GpWnemOcrxEcZxlD4dDuqOVBmahLphFTBxGSRDFQRiT6JDv 4zwNyU8h/tBvMKAL55ylhyw5xjtaD5pZriTN0/TwkGEs1XUgLSsF0NzqAXbUWIyOkU9cctNCjYGU 5g2XTBSVkhbhheFPCHnIon0UYVjBjC06VfMTh/qzpKIbKdMyzWWDqH4oBa/QZFlTCG5cOlwRzWqC tKCeSMdt1YIQhMmajFCWxKgBjwjhao+ePeiOCS7P6Np5o1cJNTjCxqJG3TYr6Eqoa2cpygnRTAjE 1WqUQrF6QZ6YMAjtB121zEAxaEFzOQjhMixBzFou9xvGcutcF2sDUjvpXE6Lnb5qmSW/b/P42+Xx 15zHx/cfyKsWyONKuwZTad4vFUJXbkjJLXhO7psozqjV0LSWWEUmNZByIi9ePJI/nSpk75NflCad 0kAEQ5ghgp+B2BYwOcJlJYYayA+Pg+ZqMPvHNapEcgpBmtgRwK6Sf3z/H9I2gBdGJIykErx3j2oy ssl/iVUD6XjM75206sgXM8b4kJPW2j4PgnEc/bKs/Er5wznotWo0w3Y0QRmGR/GvbulVeMm6VVcN AlD3VWQ3IIWd+vV+hglbs5HMDtc6cKOr5XThNahtTcu+ux7XqMUETN/bOuz59t5Ys+lqug3HSWEz uiYe2cUx5xXImSn2moeg1hpPYxH0ZR4q1B+/OSkMasF67s/NPbcwStIFc3YmuE3+OtE0f0ufDfW6 LeZvz0cCxfXmWfH2q/Mq5KZdyI+3089vLP3sMKdBFAXxkURpHh3zKPm0Yb6Wg3vTBCvVLbulEHTt hDuv57zZhVmmNw7uLR75Bl1qOTssEOM5WockCpNjmHlD1oF+8gTTDfj/9A19t67EXrCpdJ1T4bNY sGi1n9gFi2k3m+B72QbbJcS0HZU+f5n4glmZ7+P0EKaZd6ySZOIb5jvXU+Aa7C7a+Cxa/8fr4c1v L399fcmeis7vZXPbhXd+39BuweLoinanV3J4SD7tzqs13NFVQFzv5mpNo9ufzEZuZi2rWmc0xXfP QbDxpu/+BwAA//8DALBQnT8uBwAA headers: access-control-allow-headers: ['Accept, Authorization, Content-Type, Origin'] access-control-allow-methods: ['GET, PUT, POST, DELETE'] access-control-allow-origin: ['*'] access-control-expose-headers: [Date] cache-control: ['private, max-age=0, must-revalidate'] content-encoding: [gzip] content-length: ['876'] content-type: [application/json; charset=utf-8] date: ['Thu, 27 Oct 2016 20:33:38 GMT'] etag: ['"62bfb211e440fb08b0ee3d22dac9cc2a"'] server: [am/2] status: [200 OK] status: {code: 200, message: OK} - request: body: null headers: Accept: ['*/*'] Accept-Encoding: ['gzip, deflate'] Authorization: [!!python/unicode OAuth 1-35204-61921957-55796ebef403996] Connection: [keep-alive] User-Agent: [python-requests/2.11.1] method: GET uri: https://api.soundcloud.com/resolve?url=https://soundcloud.com/bbc-radio-4/m-w-cloud&client_id=93e33e327fd8a9b77becd179652272e2 response: body: string: !!binary | H4sIAAAAAAAAABTJSwrCMBAA0KuUWWtjJzSfgrjzGjKdRAzGpJjJSry7Fd7ufaAJSW+wgD7hcByu tZcAB8iVSVItezxEtrYoRVsa27851x5Gri8lb+JnUxOic96Y+cI5xSK3FM5eR71Dew+O/GrtGjlM 1psZ0WJE+P4AAAD//wMAI6hydXwAAAA= headers: access-control-allow-headers: ['Accept, Authorization, Content-Type, Origin'] access-control-allow-methods: ['GET, PUT, POST, DELETE'] access-control-allow-origin: ['*'] access-control-expose-headers: [Date] cache-control: [no-cache] content-encoding: [gzip] content-length: ['137'] content-type: [application/json; charset=utf-8] date: ['Thu, 27 Oct 2016 20:34:10 GMT'] location: ['https://api.soundcloud.com/tracks/122889665?client_id=93e33e327fd8a9b77becd179652272e2'] server: [am/2] status: [302 Found] status: {code: 302, message: Found} - request: body: null headers: Accept: ['*/*'] Accept-Encoding: ['gzip, deflate'] Authorization: [!!python/unicode OAuth 1-35204-61921957-55796ebef403996] Connection: [keep-alive] User-Agent: [python-requests/2.11.1] method: GET uri: https://api.soundcloud.com/tracks/122889665?client_id=93e33e327fd8a9b77becd179652272e2 response: body: string: !!binary | H4sIAAAAAAAAAJRVy67bNhD9FYIFuqn1tmxdLXPRxyJdtE3QpUBJY4k1RaokZUU3CJB/yKq/ly/p UJIdXSPIwytydGZ45szDb+mZy5rm1GpWnemOcrxEcZxlD4dDuqOVBmahLphFTBxGSRDFQRiT6JDv 4zwNyU8h/tBvMKAL55ylhyw5xjtaD5pZriTN0/TwkGEs1XUgLSsF0NzqAXbUWIyOkU9cctNCjYGU 5g2XTBSVkhbhheFPCHnIon0UYVjBjC06VfMTh/qzpKIbKdMyzWWDqH4oBa/QZFlTCG5cOlwRzWqC tKCeSMdt1YIQhMmajFCWxKgBjwjhao+ePeiOCS7P6Np5o1cJNTjCxqJG3TYr6Eqoa2cpygnRTAjE 1WqUQrF6QZ6YMAjtB121zEAxaEFzOQjhMixBzFou9xvGcutcF2sDUjvpXE6Lnb5qmSW/b/P42+Xx 15zHx/cfyKsWyONKuwZTad4vFUJXbkjJLXhO7psozqjV0LSWWEUmNZByIi9ePJI/nSpk75NflCad 0kAEQ5ghgp+B2BYwOcJlJYYayA+Pg+ZqMPvHNapEcgpBmtgRwK6Sf3z/H9I2gBdGJIykErx3j2oy ssl/iVUD6XjM75206sgXM8b4kJPW2j4PgnEc/bKs/Er5wznotWo0w3Y0QRmGR/GvbulVeMm6VVcN AlD3VWQ3IIWd+vV+hglbs5HMDtc6cKOr5XThNahtTcu+ux7XqMUETN/bOuz59t5Ys+lqug3HSWEz uiYe2cUx5xXImSn2moeg1hpPYxH0ZR4q1B+/OSkMasF67s/NPbcwStIFc3YmuE3+OtE0f0ufDfW6 LeZvz0cCxfXmWfH2q/Mq5KZdyI+3089vLP3sMKdBFAXxkURpHh3zKPm0Yb6Wg3vTBCvVLbulEHTt hDuv57zZhVmmNw7uLR75Bl1qOTssEOM5WockCpNjmHlD1oF+8gTTDfj/9A19t67EXrCpdJ1T4bNY sGi1n9gFi2k3m+B72QbbJcS0HZU+f5n4glmZ7+P0EKaZd6ySZOIb5jvXU+Aa7C7a+Cxa/8fr4c1v L399fcmeis7vZXPbhXd+39BuweLoinanV3J4SD7tzqs13NFVQFzv5mpNo9ufzEZuZi2rWmc0xXfP QbDxpu/+BwAA//8DALBQnT8uBwAA headers: access-control-allow-headers: ['Accept, Authorization, Content-Type, Origin'] access-control-allow-methods: ['GET, PUT, POST, DELETE'] access-control-allow-origin: ['*'] access-control-expose-headers: [Date] cache-control: ['private, max-age=0, must-revalidate'] content-encoding: [gzip] content-length: ['876'] content-type: [application/json; charset=utf-8] date: ['Thu, 27 Oct 2016 20:34:10 GMT'] etag: ['"62bfb211e440fb08b0ee3d22dac9cc2a"'] server: [am/2] status: [200 OK] status: {code: 200, message: OK} version: 1 ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1608019795.0 Mopidy-SoundCloud-3.0.1/tests/fixtures/sc-resolve-set.yaml0000644000175100001710000001135400000000000023147 0ustar00runnerdockerinteractions: - request: body: null headers: Accept: ['*/*'] Accept-Encoding: ['gzip, deflate'] Connection: [keep-alive] user-agent: [!!python/unicode 'Mopidy-SoundCloud/2.0.2 Mopidy/2.1.0 CPython/2.7.11+'] method: GET uri: https://api.soundcloud.com/resolve?url=https://soundcloud.com/yndihalda/sets/dash-and-blast&client_id=93e33e327fd8a9b77becd179652272e2 response: body: string: !!binary | H4sIAAAAAAAAABTJQQrCMBAAwK+UPWtjN7XbFKQ3vyFpEjEQk8BuDlL8uxXmNjuwWGkMC+gLdufu Xlr2cIJUnJVY8hEvkcqLUrbGnv/tUmm+d+WtarKfFFlYjeOEs15diiHLI/qb0UEfkJ5+tmYj2oLz A5npikgYEL4/AAAA//8DAIz95ld8AAAA headers: access-control-allow-headers: ['Accept, Authorization, Content-Type, Origin'] access-control-allow-methods: ['GET, PUT, POST, DELETE'] access-control-allow-origin: ['*'] access-control-expose-headers: [Date] cache-control: [no-cache] connection: [keep-alive] content-encoding: [gzip] content-length: ['138'] content-type: [application/json;charset=utf-8] date: ['Tue, 22 May 2018 19:55:23 GMT'] location: ['https://api.soundcloud.com/playlists/446283?client_id=93e33e327fd8a9b77becd179652272e2'] server: [am/2] status: [302 Found] via: [1.1 8bc6ceb505a42af10a5f7fdd59805ef6.cloudfront.net (CloudFront)] x-amz-cf-id: [-XRt6hwO6GGYkgUG_6HcjKftGcT12xIHR0v2cYVuYpFgKsMsV-PGpQ==] x-cache: [Miss from cloudfront] x-frame-options: [SAMEORIGIN] status: {code: 302, message: Moved Temporarily} - request: body: null headers: Accept: ['*/*'] Accept-Encoding: ['gzip, deflate'] Connection: [keep-alive] user-agent: [!!python/unicode 'Mopidy-SoundCloud/2.0.2 Mopidy/2.1.0 CPython/2.7.11+'] method: GET uri: https://api.soundcloud.com/playlists/446283?client_id=93e33e327fd8a9b77becd179652272e2 response: body: string: !!binary | H4sIAAAAAAAAAOxZXW/bNhT9K4ReZ8UkJVkfb03boFu7AVuKdcAwGLRF22wkSiOpJGqR/75LS1ak 2PlwnBl9SJAH+Zq89+jq3HNA+buTVooZUUgn8WLfizxv5Ciecab5NGW1k5CRU3KVs0zIi2mlMidx VsaUyXisi0qm86yo0pN5kY9rmYoVy1I21tzoccr0ymUydWcZ08YZOUsuFYfdTi8hfNxaV1ZqvrLl B8UKVWim+LxQqT6ZieWcKcMzW9i5BZwX0qzWkFOu50qUzY3ZkpUSbTIN2VgpTu7ALzNWZ0IDct+f 0MiDPRmb8WwqWW5hn4olOoeqNfq1kNpwpdEYnVZKGvS5AOToTyHrDGJ/fX7zCTYbtpzafE35DcKa M+UkFOMQVig2v5jOAQYs8gGi5moqUviaTuiEYAtAG7ipVCwEh7hDMQnGhMA/IjTxg8SP0U8Y/ixY MedSW6Qsy1wlliujXcUh5yXsbatpJ/n7u3MhpM22jsA3tmToh5j48ciZK84MT6fMNPWwrUcjWy/w k4B29bbR3jKJYEJCEkC2Is+5NGyWATCjKj5ytIH8kHshpNCrNbQC0ArJMuiFNLB8qsU3WEJ9GvqR 7dTOPmB/THyEvYR4CZ50uPSKKSGXsKqsZtCUrUfRJx8m7hb/tIEW5H3IPJ/xNLWR6axuGgzr0uJK ZgVLm5ULlmn+LPI2LLOdlFWW9VIYYWzmJtqbnjYO4NE7AI/eyBSdtuC3eD/g8C0R20xrBpq67Mpc 8Bq6v5TMVKoLCq3mm+tLkfKiubsmMCvzzeWQ5MNYO5rD4FpgmlDHgUUBj8c+qrz0nkDrR+e64f24 JXhLXCf57gy4247E+rshRTpZa7e2rbRhtInv4Gc0xjA3AaI0wTgJgtu5eQywLaLHLTJnb/WFHeyS GaZ6y20lQU40bEjlenmzRLuAiWIaBRGhgat9tZjEbsbUkp98LZfODeQyhs1Xdoj1dM9ej3t7N4Jh VXbWkz3SxhfsEhhgeoO0p+fsmmSYsqtCXTzciGbNuhMYeyD8PgndcHa9ul71OjFyrtglt9y8k+1q kO2DyM6/fDwrPuqL62l+UsplJyh39j3evWabJcCdnuFb7emF2g6C9OnOU+Kwk+Cu4eRmdL8DBPiF HAByTSJ8oAFACm9CouD/NADqXnF3kRVF6vK8NDU88wuuj2EDwNQ5y8uDXYCiLxyd2TtA7+0doE/t Hbx6wUMTFuBXLziWFwT4uF5w71Af3xG+/ZuT/Pflx1y+jw5yBOjh4Y5A42jbEfA9jkA9GoRxTHd4 Qv8U4ifkiacCEkYxIQeaghcHQTyh3n2nIzqmPqIYqiU4eqYpeC5zdSGXLjxeF6CpTBh3xoHAL20N jeDtJ/4DuelswENv0DlgRmeFQucNZnTaYX7QDAZKvPn4YxuCz17QEDqmv1rCESyh6/ZxTeGRod7X GogXgJx5geeW0SLy4/2tIf7w6ex6dVn9ht9+ea413PbycHMgwbY5BA97A4ke9QbyxDdGAQloeKg3 hCH1fRrvHLmJPTCATJJJ4sHUec/0Bt8VWVblUN1wN6/dFeiZsRepZVTvGf5g/uCjnzvc6NcafbC4 R/bqXQf81SQeHzUSvZrE8UwCenBUk3jCdD/XKOBQgilT4TPeKtWSkOs/Kv+X8O2BRgH9fIH3Susf CoZGMbn5p8loZXMgCHZGmp9U7lG8Hfr75HdQg1cY7QBuUPTUj8uvRY24saOXISiiLZf6KHdL6oue FzlrVXXbHzZ6sv+c7isze0vHPrr0gBy+pJ7sdtOhSd78BwAA//8DAKanmJ7aHAAA headers: access-control-allow-headers: ['Accept, Authorization, Content-Type, Origin'] access-control-allow-methods: ['GET, PUT, POST, DELETE'] access-control-allow-origin: ['*'] access-control-expose-headers: [Date] cache-control: ['private, max-age=0, must-revalidate'] connection: [keep-alive] content-encoding: [gzip] content-length: ['1359'] content-type: [application/json;charset=utf-8] date: ['Tue, 22 May 2018 19:55:23 GMT'] etag: ['"89b37b085db65b30f5dfaed7755ec429"'] server: [am/2] status: [200 OK] via: [1.1 8bc6ceb505a42af10a5f7fdd59805ef6.cloudfront.net (CloudFront)] x-amz-cf-id: [ceXjpjN3UiEcoQSeaNZlV4cAWeMuwY1ForkRzAtIzBTEMgMggFkvjQ==] x-cache: [Miss from cloudfront] x-frame-options: [SAMEORIGIN] status: {code: 200, message: OK} version: 1 ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1608019795.0 Mopidy-SoundCloud-3.0.1/tests/fixtures/sc-resolve-track-id.yaml0000644000175100001710000001646300000000000024060 0ustar00runnerdockerinteractions: - request: body: null headers: Accept: ['*/*'] Accept-Encoding: ['gzip, deflate'] Authorization: [!!python/unicode OAuth 1-35204-61921957-55796ebef403996] Connection: [keep-alive] User-Agent: [python-requests/2.11.1] method: GET uri: https://api.soundcloud.com/tracks/13158665?client_id=93e33e327fd8a9b77becd179652272e2 response: body: string: !!binary | H4sIAAAAAAAAAJxVTZObMAz9K4yvDQEHkhJmemg7nZ46PWzvjIId8MbYjG2STXf2v1cOhBD6sW1z wrKe9KQnOc/kIBQjOXEGygNZEIEHmtB1ttmsF6Q0HBxnBTh0WcWURnEaxZuArvPkbZ4mwZsYfwjr LDeFxyab7ZbGdEFYZ8AJrTBcRuMtxtJNw5WDneQkd6bjC2IdRsfIe6GErTnDQNqISiiQRamVQ/fC iu/ogrnTLFkQCdYVjWZiLzjrOSVRvIpoFtAtZsopHTnZGoxQFXq13U6KEk0OqkIK66uxulOslLpj OX6akr8Tba0VDw0vtfFUWm4akEId0LvpVFljsBBc6AQoBTasNVbt8zjsUjOtizc7zpi3FLszokFK 9GP6pKQG1nvuQVp0bTF1DZYXnZEkV52Uvsgdl5du9ufRxwnnob214sqMh+GGfBl4BuCCbz3P4MqT cVsa0faiTDMpaMZAhkuOqca4fiwKd2598L4zvqULcuBnlKZS4LobC2FNef0+Csb1tKpd28ySFGcO Zm5rUPd6bmRwvprGAdlrlMcr2aSAhFBgrjxx3+0QnWpnUUucy+NlsDoj8K52rrV5FEErlrcJWOJs RpdSbXQd/mGoSf5M7uZ62JfL3f2MgORPoXX8iDywxT2+7y15j3fBw+TuF4OMy7XB9MEqyddZnqa3 5XqNuk9ko4HilFXf/wsUkTPUT3zhCA7MBOPTCbq0iGKqx1xcbOiZxSndJtk6Cx/VftWyUIKp+PKx rcjL8CC0Es47P0AlZkap6GDfwxFldLctuL4zJ+G17ztOV+nYbWhb5Df08sGX8dGXEYivD3/TH4Sj sKv0961h2AWp8dLOsYjhT84LKe8hwnWK22v8SXN9+f+jQPSnRwaMO2lzmC7UCY7cL8FMsNOdYPun qvv0+DmFp82Holm2l+XtX6wZ7vWViHqc7+JM2OztNrs9cKPcCSo4aI1l2at9Nf4ZXC0bLNA5KGtv tMW/7mo0AZOXHwAAAP//AwCnTeIU1AYAAA== headers: access-control-allow-headers: ['Accept, Authorization, Content-Type, Origin'] access-control-allow-methods: ['GET, PUT, POST, DELETE'] access-control-allow-origin: ['*'] access-control-expose-headers: [Date] cache-control: ['private, max-age=0, must-revalidate'] content-encoding: [gzip] content-length: ['766'] content-type: [application/json; charset=utf-8] date: ['Thu, 27 Oct 2016 20:33:38 GMT'] etag: ['"26aec1a94b637a0f3bbfb2614d7cbd90"'] server: [am/2] status: [200 OK] status: {code: 200, message: OK} - request: body: null headers: Accept: ['*/*'] Accept-Encoding: ['gzip, deflate'] Authorization: [!!python/unicode OAuth 1-35204-61921957-55796ebef403996] Connection: [keep-alive] User-Agent: [python-requests/2.11.1] method: HEAD uri: https://api.soundcloud.com/tracks/13158665/stream?client_id=93e33e327fd8a9b77becd179652272e2 response: body: {string: !!python/unicode ''} headers: access-control-allow-headers: ['Accept, Authorization, Content-Type, Origin'] access-control-allow-methods: ['GET, PUT, POST, DELETE'] access-control-allow-origin: ['*'] access-control-expose-headers: [Date] cache-control: [no-cache] content-length: ['0'] content-type: [application/json; charset=utf-8] date: ['Thu, 27 Oct 2016 20:33:39 GMT'] location: ['https://cf-media.sndcdn.com/fxguEjG4ax6B.128.mp3?Policy=eyJTdGF0ZW1lbnQiOlt7IlJlc291cmNlIjoiKjovL2NmLW1lZGlhLnNuZGNkbi5jb20vZnhndUVqRzRheDZCLjEyOC5tcDMiLCJDb25kaXRpb24iOnsiRGF0ZUxlc3NUaGFuIjp7IkFXUzpFcG9jaFRpbWUiOjE0Nzc2MDA1NTd9fX1dfQ__&Signature=u9bxAkZOtTTF1VqTmLGmw3ENrqbiSTFK-sMvZL-ZsQK85DOepHh5MfPA4MNooszUy~PZqiVyBn4YnElhWyb~4B7kS6y0VZ6t-qF78CfTMOimemafpqfWJ8nYXczhM9pUpAwiS--lkNjGks4Qxi-FZJDBPG99gAIU0eVW78CADcpuOKLugGpzHl6gRPN2Z4zZ9dVujZ5MlG2OWnPuNiBcE~wUFwcOxt9N6ePTff-wMFQR2PGpEK6wc6bWuB4WFNBkE0bmEke4cOQjWHa5FwYEidZN5rvv5lVT1r07zzifnADEipwMaZ2-QYdqzOYaM4jymFDhl7DklaU24PY5C5mH0A__&Key-Pair-Id=APKAJAGZ7VMH2PFPW6UQ'] server: [am/2] status: [302 Found] status: {code: 302, message: Found} - request: body: null headers: Accept: ['*/*'] Accept-Encoding: ['gzip, deflate'] Authorization: [!!python/unicode OAuth 1-35204-61921957-55796ebef403996] Connection: [keep-alive] User-Agent: [python-requests/2.11.1] method: GET uri: https://api.soundcloud.com/tracks/13158665?client_id=93e33e327fd8a9b77becd179652272e2 response: body: string: !!binary | H4sIAAAAAAAAAJxVTZObMAz9K4yvDQEHkhJmemg7nZ46PWzvjIId8MbYjG2STXf2v1cOhBD6sW1z wrKe9KQnOc/kIBQjOXEGygNZEIEHmtB1ttmsF6Q0HBxnBTh0WcWURnEaxZuArvPkbZ4mwZsYfwjr LDeFxyab7ZbGdEFYZ8AJrTBcRuMtxtJNw5WDneQkd6bjC2IdRsfIe6GErTnDQNqISiiQRamVQ/fC iu/ogrnTLFkQCdYVjWZiLzjrOSVRvIpoFtAtZsopHTnZGoxQFXq13U6KEk0OqkIK66uxulOslLpj OX6akr8Tba0VDw0vtfFUWm4akEId0LvpVFljsBBc6AQoBTasNVbt8zjsUjOtizc7zpi3FLszokFK 9GP6pKQG1nvuQVp0bTF1DZYXnZEkV52Uvsgdl5du9ufRxwnnob214sqMh+GGfBl4BuCCbz3P4MqT cVsa0faiTDMpaMZAhkuOqca4fiwKd2598L4zvqULcuBnlKZS4LobC2FNef0+Csb1tKpd28ySFGcO Zm5rUPd6bmRwvprGAdlrlMcr2aSAhFBgrjxx3+0QnWpnUUucy+NlsDoj8K52rrV5FEErlrcJWOJs RpdSbXQd/mGoSf5M7uZ62JfL3f2MgORPoXX8iDywxT2+7y15j3fBw+TuF4OMy7XB9MEqyddZnqa3 5XqNuk9ko4HilFXf/wsUkTPUT3zhCA7MBOPTCbq0iGKqx1xcbOiZxSndJtk6Cx/VftWyUIKp+PKx rcjL8CC0Es47P0AlZkap6GDfwxFldLctuL4zJ+G17ztOV+nYbWhb5Df08sGX8dGXEYivD3/TH4Sj sKv0961h2AWp8dLOsYjhT84LKe8hwnWK22v8SXN9+f+jQPSnRwaMO2lzmC7UCY7cL8FMsNOdYPun qvv0+DmFp82Holm2l+XtX6wZ7vWViHqc7+JM2OztNrs9cKPcCSo4aI1l2at9Nf4ZXC0bLNA5KGtv tMW/7mo0AZOXHwAAAP//AwCnTeIU1AYAAA== headers: access-control-allow-headers: ['Accept, Authorization, Content-Type, Origin'] access-control-allow-methods: ['GET, PUT, POST, DELETE'] access-control-allow-origin: ['*'] access-control-expose-headers: [Date] cache-control: ['private, max-age=0, must-revalidate'] content-encoding: [gzip] content-length: ['766'] content-type: [application/json; charset=utf-8] date: ['Thu, 27 Oct 2016 20:34:04 GMT'] etag: ['"26aec1a94b637a0f3bbfb2614d7cbd90"'] server: [am/2] status: [200 OK] status: {code: 200, message: OK} - request: body: null headers: Accept: ['*/*'] Accept-Encoding: ['gzip, deflate'] Authorization: [!!python/unicode OAuth 1-35204-61921957-55796ebef403996] Connection: [keep-alive] User-Agent: [python-requests/2.11.1] method: HEAD uri: https://api.soundcloud.com/tracks/13158665/stream?client_id=93e33e327fd8a9b77becd179652272e2 response: body: {string: !!python/unicode ''} headers: access-control-allow-headers: ['Accept, Authorization, Content-Type, Origin'] access-control-allow-methods: ['GET, PUT, POST, DELETE'] access-control-allow-origin: ['*'] access-control-expose-headers: [Date] cache-control: [no-cache] content-length: ['0'] content-type: [application/json; charset=utf-8] date: ['Thu, 27 Oct 2016 20:34:04 GMT'] location: ['https://cf-media.sndcdn.com/fxguEjG4ax6B.128.mp3?Policy=eyJTdGF0ZW1lbnQiOlt7IlJlc291cmNlIjoiKjovL2NmLW1lZGlhLnNuZGNkbi5jb20vZnhndUVqRzRheDZCLjEyOC5tcDMiLCJDb25kaXRpb24iOnsiRGF0ZUxlc3NUaGFuIjp7IkFXUzpFcG9jaFRpbWUiOjE0Nzc2MDA1ODJ9fX1dfQ__&Signature=j3FhlV0efnkCUciO8OLYpHHA2UugEd2~YuqkRFAsgvOoB9Zf1G5Jr5x1MQG2-7kH~OkJIozVvBOxzjhw959xbN4wEkAfTEfVpGWZHCQUYZ8UqbecilnqKROvMgPuLeDTlud~v5Mmhr03M4PXojYhjEYkSTMzkpPRqa2T6u~AiXsnZzFMhQVomU5L2gNGQxNvGx5~RIHNR3SvnFR5qJcX2HViB6HZZQ08PhriP~J3xE1-t55m31kInKQcGkxL-frqbj0s4jbp0duD15eW44ZaLy34YUqrwWrBEE4CETa0H1UvBOms~3wdHIgao-qPQhS7qTZtKmD6SUxMDlROWv2ygg__&Key-Pair-Id=APKAJAGZ7VMH2PFPW6UQ'] server: [am/2] status: [302 Found] status: {code: 302, message: Found} version: 1 ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1608019795.0 Mopidy-SoundCloud-3.0.1/tests/fixtures/sc-resolve-track-none.yaml0000644000175100001710000000212200000000000024406 0ustar00runnerdockerinteractions: - request: body: null headers: Accept: ['*/*'] Accept-Encoding: ['gzip, deflate'] Authorization: [!!python/unicode OAuth 1-35204-61921957-55796ebef403996] Connection: [keep-alive] User-Agent: [python-requests/2.11.1] method: GET uri: https://api.soundcloud.com/tracks/s38720262?client_id=93e33e327fd8a9b77becd179652272e2 response: body: string: !!binary | H4sIAAAAAAAAAKpWSi0qyi8qVrKKroYw43NTi4sT01OVrJRMDEwUdBX88ksU3PJL81KUamNrAQAA AP//AwBB7xS6MAAAAA== headers: access-control-allow-headers: ['Accept, Authorization, Content-Type, Origin'] access-control-allow-methods: ['GET, PUT, POST, DELETE'] access-control-allow-origin: ['*'] access-control-expose-headers: [Date] cache-control: ['private, max-age=0, must-revalidate', 'private, max-age=0'] content-encoding: [gzip] content-length: ['70'] content-type: [application/json; charset=utf-8] date: ['Thu, 27 Oct 2016 20:34:05 GMT'] server: [am/2] status: {code: 404, message: Not Found} version: 1 ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1608019795.0 Mopidy-SoundCloud-3.0.1/tests/fixtures/sc-resolve-track.yaml0000644000175100001710000003505400000000000023463 0ustar00runnerdockerinteractions: - request: body: null headers: Accept: ['*/*'] Accept-Encoding: ['gzip, deflate'] Authorization: [!!python/unicode OAuth 1-35204-61921957-55796ebef403996] Connection: [keep-alive] User-Agent: [python-requests/2.11.1] method: GET uri: https://api.soundcloud.com/tracks/38720262.json?client_id=93e33e327fd8a9b77becd179652272e2 response: body: {string: !!python/unicode ''} headers: access-control-allow-headers: ['Accept, Authorization, Content-Type, Origin'] access-control-allow-methods: ['GET, PUT, POST, DELETE'] access-control-allow-origin: ['*'] access-control-expose-headers: [Date] cache-control: ['private, max-age=0, must-revalidate', 'private, max-age=0'] content-length: ['0'] content-type: [application/json; charset=utf-8] date: ['Thu, 27 Oct 2016 20:27:25 GMT'] etag: ['"f13acb6314396a2db0a2f95ef0583417"'] server: [am/2] status: [200 OK] status: {code: 403, message: Forbidden} - request: body: null headers: Accept: ['*/*'] Accept-Encoding: ['gzip, deflate'] Authorization: [!!python/unicode OAuth 1-35204-61921957-55796ebef403996] Connection: [keep-alive] User-Agent: [python-requests/2.11.1] method: GET uri: https://api.soundcloud.com/tracks/38720262?client_id=93e33e327fd8a9b77becd179652272e2 response: body: {string: !!python/unicode ''} headers: access-control-allow-headers: ['Accept, Authorization, Content-Type, Origin'] access-control-allow-methods: ['GET, PUT, POST, DELETE'] access-control-allow-origin: ['*'] access-control-expose-headers: [Date] cache-control: ['private, max-age=0, must-revalidate', 'private, max-age=0'] content-length: ['0'] content-type: [application/json; charset=utf-8] date: ['Thu, 27 Oct 2016 20:29:23 GMT'] etag: ['"f13acb6314396a2db0a2f95ef0583417"'] server: [am/2] status: [200 OK] status: {code: 403, message: Forbidden} - request: body: null headers: Accept: ['*/*'] Accept-Encoding: ['gzip, deflate'] Authorization: [!!python/unicode OAuth 1-35204-61921957-55796ebef403996] Connection: [keep-alive] User-Agent: [python-requests/2.11.1] method: GET uri: https://api.soundcloud.com/tracks/38720262?client_id=93e33e327fd8a9b77becd179652272e2 response: body: {string: !!python/unicode ''} headers: access-control-allow-headers: ['Accept, Authorization, Content-Type, Origin'] access-control-allow-methods: ['GET, PUT, POST, DELETE'] access-control-allow-origin: ['*'] access-control-expose-headers: [Date] cache-control: ['private, max-age=0, must-revalidate', 'private, max-age=0'] content-length: ['0'] content-type: [application/json; charset=utf-8] date: ['Thu, 27 Oct 2016 20:29:40 GMT'] etag: ['"f13acb6314396a2db0a2f95ef0583417"'] server: [am/2] status: [200 OK] status: {code: 403, message: Forbidden} - request: body: null headers: Accept: ['*/*'] Accept-Encoding: ['gzip, deflate'] Authorization: [!!python/unicode OAuth 1-35204-61921957-55796ebef403996] Connection: [keep-alive] User-Agent: [python-requests/2.11.1] method: GET uri: https://api.soundcloud.com/tracks/38720262?client_id=93e33e327fd8a9b77becd179652272e2 response: body: {string: !!python/unicode ''} headers: access-control-allow-headers: ['Accept, Authorization, Content-Type, Origin'] access-control-allow-methods: ['GET, PUT, POST, DELETE'] access-control-allow-origin: ['*'] access-control-expose-headers: [Date] cache-control: ['private, max-age=0, must-revalidate', 'private, max-age=0'] content-length: ['0'] content-type: [application/json; charset=utf-8] date: ['Thu, 27 Oct 2016 20:31:09 GMT'] etag: ['"f13acb6314396a2db0a2f95ef0583417"'] server: [am/2] status: [200 OK] status: {code: 403, message: Forbidden} - request: body: null headers: Accept: ['*/*'] Accept-Encoding: ['gzip, deflate'] Authorization: [!!python/unicode OAuth 1-35204-61921957-55796ebef403996] Connection: [keep-alive] User-Agent: [python-requests/2.11.1] method: GET uri: https://api.soundcloud.com/tracks/13158665?client_id=93e33e327fd8a9b77becd179652272e2 response: body: string: !!binary | H4sIAAAAAAAAAJxVTZObMAz9K4yvDQEHkhJmemg7nZ46PWzvjIId8MbYjG2STXf2v1cOhBD6sW1z wrKe9KQnOc/kIBQjOXEGygNZEIEHmtB1ttmsF6Q0HBxnBTh0WcWURnEaxZuArvPkbZ4mwZsYfwjr LDeFxyab7ZbGdEFYZ8AJrTBcRuMtxtJNw5WDneQkd6bjC2IdRsfIe6GErTnDQNqISiiQRamVQ/fC iu/ogrnTLFkQCdYVjWZiLzjrOSVRvIpoFtAtZsopHTnZGoxQFXq13U6KEk0OqkIK66uxulOslLpj OX6akr8Tba0VDw0vtfFUWm4akEId0LvpVFljsBBc6AQoBTasNVbt8zjsUjOtizc7zpi3FLszokFK 9GP6pKQG1nvuQVp0bTF1DZYXnZEkV52Uvsgdl5du9ufRxwnnob214sqMh+GGfBl4BuCCbz3P4MqT cVsa0faiTDMpaMZAhkuOqca4fiwKd2598L4zvqULcuBnlKZS4LobC2FNef0+Csb1tKpd28ySFGcO Zm5rUPd6bmRwvprGAdlrlMcr2aSAhFBgrjxx3+0QnWpnUUucy+NlsDoj8K52rrV5FEErlrcJWOJs RpdSbXQd/mGoSf5M7uZ62JfL3f2MgORPoXX8iDywxT2+7y15j3fBw+TuF4OMy7XB9MEqyddZnqa3 5XqNuk9ko4HilFXf/wsUkTPUT3zhCA7MBOPTCbq0iGKqx1xcbOiZxSndJtk6Cx/VftWyUIKp+PKx rcjL8CC0Es47P0AlZkap6GDfwxFldLctuL4zJ+G17ztOV+nYbWhb5Df08sGX8dGXEYivD3/TH4Sj sKv0961h2AWp8dLOsYjhT84LKe8hwnWK22v8SXN9+f+jQPSnRwaMO2lzmC7UCY7cL8FMsNOdYPun qvv0+DmFp82Holm2l+XtX6wZ7vWViHqc7+JM2OztNrs9cKPcCSo4aI1l2at9Nf4ZXC0bLNA5KGtv tMW/7mo0AZOXHwAAAP//AwCnTeIU1AYAAA== headers: access-control-allow-headers: ['Accept, Authorization, Content-Type, Origin'] access-control-allow-methods: ['GET, PUT, POST, DELETE'] access-control-allow-origin: ['*'] access-control-expose-headers: [Date] cache-control: ['private, max-age=0, must-revalidate'] content-encoding: [gzip] content-length: ['766'] content-type: [application/json; charset=utf-8] date: ['Thu, 27 Oct 2016 20:31:42 GMT'] etag: ['"26aec1a94b637a0f3bbfb2614d7cbd90"'] server: [am/2] status: [200 OK] status: {code: 200, message: OK} - request: body: null headers: Accept: ['*/*'] Accept-Encoding: ['gzip, deflate'] Authorization: [!!python/unicode OAuth 1-35204-61921957-55796ebef403996] Connection: [keep-alive] User-Agent: [python-requests/2.11.1] method: GET uri: https://api.soundcloud.com/tracks/13158665?client_id=93e33e327fd8a9b77becd179652272e2 response: body: string: !!binary | H4sIAAAAAAAAAJxVTZObMAz9K4yvDQEHkhJmemg7nZ46PWzvjIId8MbYjG2STXf2v1cOhBD6sW1z wrKe9KQnOc/kIBQjOXEGygNZEIEHmtB1ttmsF6Q0HBxnBTh0WcWURnEaxZuArvPkbZ4mwZsYfwjr LDeFxyab7ZbGdEFYZ8AJrTBcRuMtxtJNw5WDneQkd6bjC2IdRsfIe6GErTnDQNqISiiQRamVQ/fC iu/ogrnTLFkQCdYVjWZiLzjrOSVRvIpoFtAtZsopHTnZGoxQFXq13U6KEk0OqkIK66uxulOslLpj OX6akr8Tba0VDw0vtfFUWm4akEId0LvpVFljsBBc6AQoBTasNVbt8zjsUjOtizc7zpi3FLszokFK 9GP6pKQG1nvuQVp0bTF1DZYXnZEkV52Uvsgdl5du9ufRxwnnob214sqMh+GGfBl4BuCCbz3P4MqT cVsa0faiTDMpaMZAhkuOqca4fiwKd2598L4zvqULcuBnlKZS4LobC2FNef0+Csb1tKpd28ySFGcO Zm5rUPd6bmRwvprGAdlrlMcr2aSAhFBgrjxx3+0QnWpnUUucy+NlsDoj8K52rrV5FEErlrcJWOJs RpdSbXQd/mGoSf5M7uZ62JfL3f2MgORPoXX8iDywxT2+7y15j3fBw+TuF4OMy7XB9MEqyddZnqa3 5XqNuk9ko4HilFXf/wsUkTPUT3zhCA7MBOPTCbq0iGKqx1xcbOiZxSndJtk6Cx/VftWyUIKp+PKx rcjL8CC0Es47P0AlZkap6GDfwxFldLctuL4zJ+G17ztOV+nYbWhb5Df08sGX8dGXEYivD3/TH4Sj sKv0961h2AWp8dLOsYjhT84LKe8hwnWK22v8SXN9+f+jQPSnRwaMO2lzmC7UCY7cL8FMsNOdYPun qvv0+DmFp82Holm2l+XtX6wZ7vWViHqc7+JM2OztNrs9cKPcCSo4aI1l2at9Nf4ZXC0bLNA5KGtv tMW/7mo0AZOXHwAAAP//AwCnTeIU1AYAAA== headers: access-control-allow-headers: ['Accept, Authorization, Content-Type, Origin'] access-control-allow-methods: ['GET, PUT, POST, DELETE'] access-control-allow-origin: ['*'] access-control-expose-headers: [Date] cache-control: ['private, max-age=0, must-revalidate'] content-encoding: [gzip] content-length: ['766'] content-type: [application/json; charset=utf-8] date: ['Thu, 27 Oct 2016 20:32:09 GMT'] etag: ['"26aec1a94b637a0f3bbfb2614d7cbd90"'] server: [am/2] status: [200 OK] status: {code: 200, message: OK} - request: body: null headers: Accept: ['*/*'] Accept-Encoding: ['gzip, deflate'] Authorization: [!!python/unicode OAuth 1-35204-61921957-55796ebef403996] Connection: [keep-alive] User-Agent: [python-requests/2.11.1] method: GET uri: https://api.soundcloud.com/tracks/13158665?client_id=93e33e327fd8a9b77becd179652272e2 response: body: string: !!binary | H4sIAAAAAAAAAJxVTZObMAz9K4yvDQEHkhJmemg7nZ46PWzvjIId8MbYjG2STXf2v1cOhBD6sW1z wrKe9KQnOc/kIBQjOXEGygNZEIEHmtB1ttmsF6Q0HBxnBTh0WcWURnEaxZuArvPkbZ4mwZsYfwjr LDeFxyab7ZbGdEFYZ8AJrTBcRuMtxtJNw5WDneQkd6bjC2IdRsfIe6GErTnDQNqISiiQRamVQ/fC iu/ogrnTLFkQCdYVjWZiLzjrOSVRvIpoFtAtZsopHTnZGoxQFXq13U6KEk0OqkIK66uxulOslLpj OX6akr8Tba0VDw0vtfFUWm4akEId0LvpVFljsBBc6AQoBTasNVbt8zjsUjOtizc7zpi3FLszokFK 9GP6pKQG1nvuQVp0bTF1DZYXnZEkV52Uvsgdl5du9ufRxwnnob214sqMh+GGfBl4BuCCbz3P4MqT cVsa0faiTDMpaMZAhkuOqca4fiwKd2598L4zvqULcuBnlKZS4LobC2FNef0+Csb1tKpd28ySFGcO Zm5rUPd6bmRwvprGAdlrlMcr2aSAhFBgrjxx3+0QnWpnUUucy+NlsDoj8K52rrV5FEErlrcJWOJs RpdSbXQd/mGoSf5M7uZ62JfL3f2MgORPoXX8iDywxT2+7y15j3fBw+TuF4OMy7XB9MEqyddZnqa3 5XqNuk9ko4HilFXf/wsUkTPUT3zhCA7MBOPTCbq0iGKqx1xcbOiZxSndJtk6Cx/VftWyUIKp+PKx rcjL8CC0Es47P0AlZkap6GDfwxFldLctuL4zJ+G17ztOV+nYbWhb5Df08sGX8dGXEYivD3/TH4Sj sKv0961h2AWp8dLOsYjhT84LKe8hwnWK22v8SXN9+f+jQPSnRwaMO2lzmC7UCY7cL8FMsNOdYPun qvv0+DmFp82Holm2l+XtX6wZ7vWViHqc7+JM2OztNrs9cKPcCSo4aI1l2at9Nf4ZXC0bLNA5KGtv tMW/7mo0AZOXHwAAAP//AwCnTeIU1AYAAA== headers: access-control-allow-headers: ['Accept, Authorization, Content-Type, Origin'] access-control-allow-methods: ['GET, PUT, POST, DELETE'] access-control-allow-origin: ['*'] access-control-expose-headers: [Date] cache-control: ['private, max-age=0, must-revalidate'] content-encoding: [gzip] content-length: ['766'] content-type: [application/json; charset=utf-8] date: ['Thu, 27 Oct 2016 20:32:54 GMT'] etag: ['"26aec1a94b637a0f3bbfb2614d7cbd90"'] server: [am/2] status: [200 OK] status: {code: 200, message: OK} - request: body: null headers: Accept: ['*/*'] Accept-Encoding: ['gzip, deflate'] Authorization: [!!python/unicode OAuth 1-35204-61921957-55796ebef403996] Connection: [keep-alive] User-Agent: [python-requests/2.11.1] method: GET uri: https://api.soundcloud.com/tracks/13158665?client_id=93e33e327fd8a9b77becd179652272e2 response: body: string: !!binary | H4sIAAAAAAAAAJxVTZObMAz9K4yvDQEHkhJmemg7nZ46PWzvjIId8MbYjG2STXf2v1cOhBD6sW1z wrKe9KQnOc/kIBQjOXEGygNZEIEHmtB1ttmsF6Q0HBxnBTh0WcWURnEaxZuArvPkbZ4mwZsYfwjr LDeFxyab7ZbGdEFYZ8AJrTBcRuMtxtJNw5WDneQkd6bjC2IdRsfIe6GErTnDQNqISiiQRamVQ/fC iu/ogrnTLFkQCdYVjWZiLzjrOSVRvIpoFtAtZsopHTnZGoxQFXq13U6KEk0OqkIK66uxulOslLpj OX6akr8Tba0VDw0vtfFUWm4akEId0LvpVFljsBBc6AQoBTasNVbt8zjsUjOtizc7zpi3FLszokFK 9GP6pKQG1nvuQVp0bTF1DZYXnZEkV52Uvsgdl5du9ufRxwnnob214sqMh+GGfBl4BuCCbz3P4MqT cVsa0faiTDMpaMZAhkuOqca4fiwKd2598L4zvqULcuBnlKZS4LobC2FNef0+Csb1tKpd28ySFGcO Zm5rUPd6bmRwvprGAdlrlMcr2aSAhFBgrjxx3+0QnWpnUUucy+NlsDoj8K52rrV5FEErlrcJWOJs RpdSbXQd/mGoSf5M7uZ62JfL3f2MgORPoXX8iDywxT2+7y15j3fBw+TuF4OMy7XB9MEqyddZnqa3 5XqNuk9ko4HilFXf/wsUkTPUT3zhCA7MBOPTCbq0iGKqx1xcbOiZxSndJtk6Cx/VftWyUIKp+PKx rcjL8CC0Es47P0AlZkap6GDfwxFldLctuL4zJ+G17ztOV+nYbWhb5Df08sGX8dGXEYivD3/TH4Sj sKv0961h2AWp8dLOsYjhT84LKe8hwnWK22v8SXN9+f+jQPSnRwaMO2lzmC7UCY7cL8FMsNOdYPun qvv0+DmFp82Holm2l+XtX6wZ7vWViHqc7+JM2OztNrs9cKPcCSo4aI1l2at9Nf4ZXC0bLNA5KGtv tMW/7mo0AZOXHwAAAP//AwCnTeIU1AYAAA== headers: access-control-allow-headers: ['Accept, Authorization, Content-Type, Origin'] access-control-allow-methods: ['GET, PUT, POST, DELETE'] access-control-allow-origin: ['*'] access-control-expose-headers: [Date] cache-control: ['private, max-age=0, must-revalidate'] content-encoding: [gzip] content-length: ['766'] content-type: [application/json; charset=utf-8] date: ['Thu, 27 Oct 2016 20:33:37 GMT'] etag: ['"26aec1a94b637a0f3bbfb2614d7cbd90"'] server: [am/2] status: [200 OK] status: {code: 200, message: OK} - request: body: null headers: Accept: ['*/*'] Accept-Encoding: ['gzip, deflate'] Authorization: [!!python/unicode OAuth 1-35204-61921957-55796ebef403996] Connection: [keep-alive] User-Agent: [python-requests/2.11.1] method: GET uri: https://api.soundcloud.com/tracks/13158665?client_id=93e33e327fd8a9b77becd179652272e2 response: body: string: !!binary | H4sIAAAAAAAAAJxVTZObMAz9K4yvDQEHkhJmemg7nZ46PWzvjIId8MbYjG2STXf2v1cOhBD6sW1z wrKe9KQnOc/kIBQjOXEGygNZEIEHmtB1ttmsF6Q0HBxnBTh0WcWURnEaxZuArvPkbZ4mwZsYfwjr LDeFxyab7ZbGdEFYZ8AJrTBcRuMtxtJNw5WDneQkd6bjC2IdRsfIe6GErTnDQNqISiiQRamVQ/fC iu/ogrnTLFkQCdYVjWZiLzjrOSVRvIpoFtAtZsopHTnZGoxQFXq13U6KEk0OqkIK66uxulOslLpj OX6akr8Tba0VDw0vtfFUWm4akEId0LvpVFljsBBc6AQoBTasNVbt8zjsUjOtizc7zpi3FLszokFK 9GP6pKQG1nvuQVp0bTF1DZYXnZEkV52Uvsgdl5du9ufRxwnnob214sqMh+GGfBl4BuCCbz3P4MqT cVsa0faiTDMpaMZAhkuOqca4fiwKd2598L4zvqULcuBnlKZS4LobC2FNef0+Csb1tKpd28ySFGcO Zm5rUPd6bmRwvprGAdlrlMcr2aSAhFBgrjxx3+0QnWpnUUucy+NlsDoj8K52rrV5FEErlrcJWOJs RpdSbXQd/mGoSf5M7uZ62JfL3f2MgORPoXX8iDywxT2+7y15j3fBw+TuF4OMy7XB9MEqyddZnqa3 5XqNuk9ko4HilFXf/wsUkTPUT3zhCA7MBOPTCbq0iGKqx1xcbOiZxSndJtk6Cx/VftWyUIKp+PKx rcjL8CC0Es47P0AlZkap6GDfwxFldLctuL4zJ+G17ztOV+nYbWhb5Df08sGX8dGXEYivD3/TH4Sj sKv0961h2AWp8dLOsYjhT84LKe8hwnWK22v8SXN9+f+jQPSnRwaMO2lzmC7UCY7cL8FMsNOdYPun qvv0+DmFp82Holm2l+XtX6wZ7vWViHqc7+JM2OztNrs9cKPcCSo4aI1l2at9Nf4ZXC0bLNA5KGtv tMW/7mo0AZOXHwAAAP//AwCnTeIU1AYAAA== headers: access-control-allow-headers: ['Accept, Authorization, Content-Type, Origin'] access-control-allow-methods: ['GET, PUT, POST, DELETE'] access-control-allow-origin: ['*'] access-control-expose-headers: [Date] cache-control: ['private, max-age=0, must-revalidate'] content-encoding: [gzip] content-length: ['766'] content-type: [application/json; charset=utf-8] date: ['Thu, 27 Oct 2016 20:34:09 GMT'] etag: ['"26aec1a94b637a0f3bbfb2614d7cbd90"'] server: [am/2] status: [200 OK] status: {code: 200, message: OK} version: 1 ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1608019795.0 Mopidy-SoundCloud-3.0.1/tests/fixtures/sc-search.yaml0000644000175100001710000001244000000000000022141 0ustar00runnerdockerinteractions: - request: body: null headers: Accept: ['*/*'] Accept-Encoding: ['gzip, deflate'] Connection: [keep-alive] user-agent: [!!python/unicode 'Mopidy-SoundCloud/2.0.2 Mopidy/2.1.0 CPython/2.7.11+'] method: GET uri: https://api.soundcloud.com/tracks?q=the+great+descent&limit=10&client_id=93e33e327fd8a9b77becd179652272e2 response: body: string: !!binary | H4sIAAAAAAAAAOyabXPbNhKA/wpOX5rOmRII8FX9cJNeWsdO1Thx+nI532ggEhJp8a0AKVnX6X+/ BSnJpMzYEuVcPakziScGdxcgdrEPFuC/f++Ffm9IXdOglJr2SW8eJtDQywXz5r2Tnic4y7k/Zjk0 Eqw7A0wG2EK6M6Q66KG/Y/gDghGT+ThO/XAacn9H1h1ie6jjrWzGRcyiMJmrjgqRaEykReJrcXij 1x+PCxGBSJDnmRwOBlIJeVFa+H0vjQd5wGdqdD6XHk/yQYulPMwjDhY+wCP0snyEXozg2dfw0C8E y8M06Q1113Qd56QnAybCZAYKWTGJQg+ElmzBp6mId4ay1PsSxuIn5Ug+sODcef2OX74io3Hcz8AE GMthcLt6LAv7u6+hZloOti4YVIpgoRDhYapKR3IxLl1qm47rEt066TGRL1OxO5th4xXWMlIDDxHX IZZDCNbMaQGmtYiJGe9fZ+q1QDiG2R57MBSICbxtYRM11bkoOEwtuESEWTW7PTXX6TKJUubX1DZN ld6URRIUeTzhftk0nqxAlUURaE/ZIhVhDq6RNQMznghemQ+l8HrDpIgiFYgTHpVTUP89YTHftoQQ L5JX5jURzoJcaoLDzC0gdE960NcsTFgEfSW5elcZ/hekTd2hJqZWTUBFRrkyIExU4EZsNQGX1AaZ FcILmOTjdShWI9i2li6p2gSPOJPrF1r/MvbZauc5rLEkD3YbV5yJ27YslXl9pmQOaxgMT8MklEH5 jlWQ1X2Ws9k4CmVeDaAMrXG+yrZjVpHVG/7eYwuWM1ELJhWbjVgKYzbjcuDzKSuifLxWqIII1sY/ dJMY2HSJayjXNWN1nX7KztozwYOJYJ+Vo+zDwtn0u144VZD0PgQcnSqD6NXWYiNl3elv/+T3hwq/ Od96R4flmefMC9QSkuODl/ygpg0DmWTxxl9zvoLAnSUM8uI6qsrksF5Mt2uubN0N3crGIvR5Wg/S 7Tretv1x0kAItQ5FiLM/QnA7QpaCe3N+LD52rGzQ8UvV3IYNgh1HxWxXbMz/+faHKT71vru04yOx Qa3O2KDW42JD17Wb1Ji61jM2KmyYhOrw4xkbz9h4NGzgo7AB2eKJYcM6FBsG3hcb1pBardiA8BYq OWjTEFaxBtXBLNB8waZ5d4jca3ODlMu1EPpeCaEX75UUeqWkdgCjm65xRF0SjFL600d7ii9+mx8J GKs7YKxHBgyhmpVNlvbqGTAVYGxdtzB9BswzYJ4KYKynBxj3MwJGd1oBM+F5zoWWQQ7T0qkWc016 LPJXms/jtDtjHjK7wcy3pRy6ADn0dopGHL24LAUhfOJ0hzSGYWK9O2lu4vfnFx/Txa+O/tOxpHG7 k8Z9ZNK4WuySeYGfSVOSxsbUcS3iPJPmmTRPhTTukyGN5RLLpvjhOxQy0PUBcRE2gE71e5GWSdyR 1dvvUAKulf7Q/Lvuuh8uy4Anms+WicayTKQwkRC3bfa2VyktoXHLEQtjx+6OkeUPqfMOW9/eWG9e d8XIxg2HU2SjWYcIcSk1DNc4nCGYmjqljm1r1+wmWRRPnCEjHkHgeeg1E76XQtP+TOkh1OvMFN0l pmPTNqTEGf3LIqUltkqRKrSIbeqGaWDtJlkaCW+EVjNqu9CkLSfsj5RN302i/AI20SuwiV7WbTaO 8du7vScpwk4dm0OdPu7dyjaBPBWwEGoTw7EfrmD0AXYHBCMM5YsxJOQ+sNRlzaFB9wOLKjhUoyyS /RkDv3AxK2/fPcg/bYBp2v0ka1QdoxovS7Fb7FCdGPoR5YvrXp9fjG6cl+c/h125s/HS4dzZaDa4 Q+GFHLMDdnTdtk1DN7Uo8ax4/sSxc5b4IUfv0zKeK+D0eg/g5gjYYNPBttFawMQG+4JocwH6mppW dJaAYFE6OULVdJc/tfWk16G0nZbeZ612tsHdhU53sskBaKr6bZKpzV49B7Y9fyCfktub6l0mkW5M 2iaXp8IknRKH6ASbD0LJKMtAinQ8NE2oBO+DkjnAIK4j3RgaVv3rsvuhFLMwUfiI+f5Qms/TQkjA RRuNGgY/TaMXI5BDpdzOB2XUpIrXXXl0/i4jlx9m369OR2+68mjrocOBtFVtHqcZ1OgCJMu1bdfB RLMklsH1sUB6u05RqBx7OWIEE4qu7jroqneCGCqjGs1gtaN1bCLwmuGgQEVAqZsrzSidgNVTJXfO YgThSPvoSlwl5b/SOiybhOdI5mmWgRnYpfqFp27y0psVEK2PLtOYpwlHcSFzJHia8TI+UBqBNJMS EiVaspVEOYwoRWp9qf+G+VcSqRFzgVjig2bGwmpYVZfwOAA+5H9DbyuDshBT5vGTynrZO4r4gkcS McHRjOflDaPPkhmH3CWjFYrSZb/fRxcwqgimgiVf5WiiJgQs8KS8g1R9n6nOJhz8oAxAAPtqhHG6 4Er7X2lRvVxQCLECOZg+Xh8E6IH5nEOHfihhJw/Dlv2rHvrMG4lbL/8/NhKEuthpPwj9sqrW2ex6 d49w397g/rLVcgxbtxxLCyKXLCZ3ytZNiumyLbjN54ecfqr+mtuBNysRsnkq0ZuavTp+Gh21MMwe YGegmwjjIaVDajxucXqb1Z/KToBahqnbpr7HsSdsBKBYx1C1P/TpeCVrqN0U1Ye4/X4tZnOugdcS X2pHHIFCghQrzS+ShA8eMrnZC4xADr0s5dCLO9zZ2Qw4lu6S7puB9G2Ufnc2is5/i047362tndTh am2tWd8K6Op036VOl+rUIbZjOq62KtxlYBy7GYCpl7wk3rWCkgT8wg8gBJqsUMz76rQAHkJuR0uI fcjg1QP0gt94PMvRiKuyaMT8hEt5Uhd6v2IJ+jH0gjSSX5dklBm0MOBojtIpKsEj0TIAEaTiRiKp UKI2F0xBHskw8aDYgm0C0Bh6LUBQ5YByOGqxSGUHaBp6/RY6kl06VhPQ6XCXJatDDnS7g5FihUb3 yy+wH/M4V8fEpBYxtcS0bXt5h4u3660LGWv5bX80brpswvFUmUKv1qbqubjZSXtGtwdUVxdZkNTJ p7DY9TZwk9/+TCoeMs6NZm9bUJu2DkWjTfYpqO2yoLaGBn3g+/lPyt5fUEPGWoR8qYX7UzQVqZwH LG0rp+vm7qmmLyoxdNbkp2lh6wh8JheRPLV+vJYL8aFzLb1xTodaeqPaACgmLjFhH7dD0CqqOnDw LAfiqLItUfVcWY9FKmUrwPzMkwIKzgCmeskVjKQqNKGqC0KJZlEYZyW0oMhTakvgHd9Kb6o5H5XH YFVFykBAFPADjKuGUZom30CluKjkBQ9gX9RQ4atSeAQJ7wSQK7wC0ohC6nmRwdoT31R8DRQYJ2C8 ytg+WoYwyHXBG0YqSNR3tcrUpVrHUHB/5qpSZiE8LGCLAH46gKBHfWbjOo6ByV/ge05+o6JRQggj WC2JSgM+V3um1POKKH9MxLrEcSg1Tc1Z8czL7yL2dkV2Yew2+x1A2E2PTcS+BUtv1pbqSbreQ0ui twYY/pLyYxv308fPtGPVuc1/T6XqLEdEXAMfgktqP3D+vCN7MC4fm5f7AnOHmDYkENqdmL9m5uq1 8fF1+srMjiKm8k9HYirVz03MUNbPL/NAqJNbxTRRAEOLDNJSVd2tKQUMyu+AtIIThwzulxCblmfT aw8qa3f8dgJPQ3XoXAE34Tc5EqFsYO0quUq2zlku++qsd5Km82boXIv0S+SfTl2iw67vLwXACnno SqUXVKYDpKL8qtdE4zMTnxQTywz3pzPxP/8DAAD//wMAf41fN8dDAAA= headers: access-control-allow-headers: ['Accept, Authorization, Content-Type, Origin'] access-control-allow-methods: ['GET, PUT, POST, DELETE'] access-control-allow-origin: ['*'] access-control-expose-headers: [Date] cache-control: ['public, max-age=60, must-revalidate'] connection: [keep-alive] content-encoding: [gzip] content-length: ['2834'] content-type: [application/json;charset=utf-8] date: ['Fri, 20 Apr 2018 00:13:27 GMT'] server: [am/2] via: [1.1 939414900b0e5d33a5f9bf346ea3a331.cloudfront.net (CloudFront)] x-amz-cf-id: [FNSEvMG1qATRx983FRIWRIKvY_8JU9HOf5_XtAc9z3xuUAc0tqwk8Q==] x-cache: [Miss from cloudfront] status: {code: 200, message: OK} version: 1 ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1608019795.0 Mopidy-SoundCloud-3.0.1/tests/fixtures/sc-set-invalid.yaml0000644000175100001710000000243700000000000023120 0ustar00runnerdockerinteractions: - request: body: null headers: Accept: ['*/*'] Accept-Encoding: ['gzip, deflate'] Connection: [keep-alive] user-agent: [!!python/unicode 'Mopidy-SoundCloud/2.0.2 Mopidy/2.1.0 CPython/2.7.11+'] method: GET uri: https://api.soundcloud.com/playlists/blahblahrubbosh?client_id=93e33e327fd8a9b77becd179652272e2 response: body: string: !!binary | H4sIAAAAAAAAAKpWSi0qyi8qVrKKroYw43NTi4sT01OVrJRMDEwUdBX88ksU3PJL81KUamNrAQAA AP//AwBB7xS6MAAAAA== headers: access-control-allow-headers: ['Accept, Authorization, Content-Type, Origin'] access-control-allow-methods: ['GET, PUT, POST, DELETE'] access-control-allow-origin: ['*'] access-control-expose-headers: [Date] cache-control: [no-cache, 'private, max-age=0'] connection: [keep-alive] content-encoding: [gzip] content-length: ['70'] content-type: [application/json;charset=utf-8] date: ['Fri, 20 Apr 2018 00:13:25 GMT'] server: [am/2] status: [404 Not Found] via: [1.1 87510893413a5a70f5cf33b727e70ad8.cloudfront.net (CloudFront)] x-amz-cf-id: [Pkhz8gFCLwtLDhPzSUE5Fuyl3nNV5abXxHoeJxaN94cwZp34gwyDmA==] x-cache: [Error from cloudfront] status: {code: 404, message: Not Found} version: 1 ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1608019795.0 Mopidy-SoundCloud-3.0.1/tests/fixtures/sc-set.yaml0000644000175100001710000000666200000000000021500 0ustar00runnerdockerinteractions: - request: body: null headers: Accept: ['*/*'] Accept-Encoding: ['gzip, deflate'] Connection: [keep-alive] user-agent: [!!python/unicode 'Mopidy-SoundCloud/2.0.2 Mopidy/2.1.0 CPython/2.7.11+'] method: GET uri: https://api.soundcloud.com/playlists/10961826?client_id=93e33e327fd8a9b77becd179652272e2 response: body: string: !!binary | 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 //8DAJ+/XH+/DgAA headers: access-control-allow-headers: ['Accept, Authorization, Content-Type, Origin'] access-control-allow-methods: ['GET, PUT, POST, DELETE'] access-control-allow-origin: ['*'] access-control-expose-headers: [Date] cache-control: ['private, max-age=0, must-revalidate'] connection: [keep-alive] content-encoding: [gzip] content-length: ['1494'] content-type: [application/json;charset=utf-8] date: ['Fri, 20 Apr 2018 00:13:25 GMT'] etag: ['"31ed6fd5775905cb46a198d5aa338c01"'] server: [am/2] status: [200 OK] via: [1.1 91c3e999c953fd1ba6874eebbc464012.cloudfront.net (CloudFront)] x-amz-cf-id: [UyjgWF55-1dmp2yRBIWgwdCnbU_WxWVTL_vZ-MgQqi8ySo_kXWhSPw==] x-cache: [Miss from cloudfront] x-frame-options: [SAMEORIGIN] status: {code: 200, message: OK} version: 1 ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1608019795.0 Mopidy-SoundCloud-3.0.1/tests/fixtures/sc-sets.yaml0000644000175100001710000000710200000000000021651 0ustar00runnerdockerinteractions: - request: body: null headers: Accept: ['*/*'] Accept-Encoding: ['gzip, deflate'] Connection: [keep-alive] user-agent: [!!python/unicode 'Mopidy-SoundCloud/2.0.2 Mopidy/2.1.0 CPython/2.7.11+'] method: GET uri: https://api.soundcloud.com/me/playlists?limit=10&client_id=93e33e327fd8a9b77becd179652272e2 response: body: string: !!binary | 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 headers: access-control-allow-headers: ['Accept, Authorization, Content-Type, Origin'] access-control-allow-methods: ['GET, PUT, POST, DELETE'] access-control-allow-origin: ['*'] access-control-expose-headers: [Date] cache-control: ['private, max-age=0, must-revalidate'] connection: [keep-alive] content-encoding: [gzip] content-length: ['1593'] content-type: [application/json;charset=utf-8] date: ['Tue, 22 May 2018 22:32:59 GMT'] etag: ['"79aff5ef0fa126d78a73385142abdf7b"'] server: [am/2] status: [200 OK] via: [1.1 69c4fecf3b313b4bea721d0c08afc356.cloudfront.net (CloudFront)] x-amz-cf-id: [tVzLi5XzWOrNu4GrBFe9m98TOUr0mVDMCqSB6nlkrcz7LMnozpL6nQ==] x-cache: [Miss from cloudfront] x-frame-options: [SAMEORIGIN] status: {code: 200, message: OK} version: 1 ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1608019795.0 Mopidy-SoundCloud-3.0.1/tests/fixtures/sc-stream.yaml0000644000175100001710000001612500000000000022173 0ustar00runnerdockerinteractions: - request: body: null headers: Accept: ['*/*'] Accept-Encoding: ['gzip, deflate'] Connection: [keep-alive] user-agent: [!!python/unicode 'Mopidy-SoundCloud/2.0.2 Mopidy/2.1.0 CPython/2.7.11+'] method: GET uri: https://api.soundcloud.com/me/activities?limit=10&client_id=93e33e327fd8a9b77becd179652272e2 response: body: string: !!binary | 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 headers: access-control-allow-headers: ['Accept, Authorization, Content-Type, Origin'] access-control-allow-methods: ['GET, PUT, POST, DELETE'] access-control-allow-origin: ['*'] access-control-expose-headers: [Date] cache-control: ['private, max-age=0, must-revalidate'] connection: [keep-alive] content-encoding: [gzip] content-length: ['4079'] content-type: [application/json;charset=utf-8] date: ['Tue, 22 May 2018 22:30:35 GMT'] server: [am/2] via: [1.1 d887f5d446ae9c64e0365e1b394dac21.cloudfront.net (CloudFront)] x-amz-cf-id: [jHovgDCGONYvabBQO2LbH04Bhus6RuegCPdeOYb9yKRvde6bdYlruw==] x-cache: [Miss from cloudfront] status: {code: 200, message: OK} version: 1 ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1608019795.0 Mopidy-SoundCloud-3.0.1/tests/fixtures/sc-tracks.yaml0000644000175100001710000000342100000000000022162 0ustar00runnerdockerinteractions: - request: body: null headers: Accept: ['*/*'] Accept-Encoding: ['gzip, deflate'] Authorization: [!!python/unicode OAuth 1-35204-61921957-55796ebef403996] Connection: [keep-alive] User-Agent: [python-requests/2.11.1] method: GET uri: https://api.soundcloud.com/groups/136/tracks.json?client_id=93e33e327fd8a9b77becd179652272e2 response: body: {string: !!python/unicode ''} headers: access-control-allow-headers: ['Accept, Authorization, Content-Type, Origin'] access-control-allow-methods: ['GET, PUT, POST, DELETE'] access-control-allow-origin: ['*'] access-control-expose-headers: [Date] cache-control: ['private, max-age=0, must-revalidate', 'private, max-age=0'] content-length: ['0'] date: ['Thu, 27 Oct 2016 20:26:06 GMT'] server: [am/2] status: {code: 404, message: Not Found} - request: body: null headers: Accept: ['*/*'] Accept-Encoding: ['gzip, deflate'] Authorization: [!!python/unicode OAuth 1-35204-61921957-55796ebef403996] Connection: [keep-alive] User-Agent: [python-requests/2.11.1] method: GET uri: https://api.soundcloud.com/groups/136/tracks.json?client_id=93e33e327fd8a9b77becd179652272e2 response: body: {string: !!python/unicode ''} headers: access-control-allow-headers: ['Accept, Authorization, Content-Type, Origin'] access-control-allow-methods: ['GET, PUT, POST, DELETE'] access-control-allow-origin: ['*'] access-control-expose-headers: [Date] cache-control: ['private, max-age=0, must-revalidate', 'private, max-age=0'] content-length: ['0'] date: ['Thu, 27 Oct 2016 20:27:10 GMT'] server: [am/2] status: {code: 404, message: Not Found} version: 1 ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1608019795.0 Mopidy-SoundCloud-3.0.1/tests/fixtures/sc-user-tracks.yaml0000644000175100001710000001266100000000000023144 0ustar00runnerdockerinteractions: - request: body: null headers: Accept: ['*/*'] Accept-Encoding: ['gzip, deflate'] Connection: [keep-alive] user-agent: [!!python/unicode 'Mopidy-SoundCloud/2.0.2 Mopidy/2.1.0 CPython/2.7.11+'] method: GET uri: https://api.soundcloud.com/users/27945548/tracks?limit=10&client_id=93e33e327fd8a9b77becd179652272e2 response: body: string: !!binary | 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 QbwPyOlr31P07/8DAAD//wMAGu2OuxFPAAA= headers: access-control-allow-headers: ['Accept, Authorization, Content-Type, Origin'] access-control-allow-methods: ['GET, PUT, POST, DELETE'] access-control-allow-origin: ['*'] access-control-expose-headers: [Date] cache-control: ['private, max-age=0, must-revalidate'] connection: [keep-alive] content-encoding: [gzip] content-length: ['2933'] content-type: [application/json;charset=utf-8] date: ['Sun, 27 May 2018 14:10:20 GMT'] server: [am/2] via: [1.1 82c788b76c300adf85164f03a403ff44.cloudfront.net (CloudFront)] x-amz-cf-id: [XbnpMa1uT0HB2YelR8rRkcfTu6y9KzXHlgI2huSeGMDcwcPdQYOk7Q==] x-cache: [Miss from cloudfront] status: {code: 200, message: OK} version: 1 ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1608019795.0 Mopidy-SoundCloud-3.0.1/tests/test_api.py0000644000175100001710000001640200000000000017720 0ustar00runnerdockerimport os.path import unittest from unittest import mock import vcr import mopidy_soundcloud from mopidy.models import Track from mopidy_soundcloud import Extension from mopidy_soundcloud.soundcloud import SoundCloudClient, readable_url local_path = os.path.abspath(os.path.dirname(__file__)) my_vcr = vcr.VCR( serializer="yaml", cassette_library_dir=local_path + "/fixtures", record_mode="once", match_on=["uri", "method"], decode_compressed_response=False, filter_headers=["Authorization"], ) class ApiTest(unittest.TestCase): @my_vcr.use_cassette("sc-login.yaml") def setUp(self): config = Extension().get_config_schema() config["auth_token"] = "1-35204-61921957-55796ebef403996" config["explore_songs"] = 10 self.api = SoundCloudClient({"soundcloud": config, "proxy": {}}) def test_sets_user_agent(self): agent = "Mopidy-SoundCloud/%s Mopidy/" % mopidy_soundcloud.__version__ assert agent in self.api.http_client.headers["user-agent"] def test_resolves_string(self): _id = self.api.parse_track_uri("soundcloud:song.38720262") assert _id == "38720262" @my_vcr.use_cassette("sc-login-error.yaml") def test_responds_with_error(self): with mock.patch("mopidy_soundcloud.soundcloud.logger.error") as d: config = Extension().get_config_schema() config["auth_token"] = "1-fake-token" SoundCloudClient({"soundcloud": config, "proxy": {}}).user d.assert_called_once_with( 'Invalid "auth_token" used for ' "SoundCloud authentication!" ) @my_vcr.use_cassette("sc-login.yaml") def test_returns_username(self): user = self.api.user.get("username") assert user == "Nick Steel 3" @my_vcr.use_cassette("sc-resolve-track.yaml") def test_resolves_object(self): trackc = {} trackc["uri"] = "soundcloud:song.38720262" track = Track(**trackc) id = self.api.parse_track_uri(track) assert id == "38720262" @my_vcr.use_cassette("sc-resolve-track-none.yaml") def test_resolves_unknown_track_to_none(self): track = self.api.get_track("s38720262") assert track is None @my_vcr.use_cassette("sc-resolve-track.yaml") def test_resolves_track(self): track = self.api.get_track("13158665") assert isinstance(track, Track) assert track.uri == "soundcloud:song/Munching at Tiannas house.13158665" @my_vcr.use_cassette("sc-resolve-http.yaml") def test_resolves_http_url(self): track = self.api.resolve_url( "https://soundcloud.com/bbc-radio-4/m-w-cloud" )[0] assert isinstance(track, Track) assert ( track.uri == "soundcloud:song/That Mitchell and Webb Sound The Cloud.122889665" ) @my_vcr.use_cassette("sc-resolve-set.yaml") def test_resolves_set_url(self): expected_tracks = [ "01 Dash And Blast", "02 We Flood Empty Lakes", "03 A Song For Starlit Beaches", "04 Illuminate My Heart, My Darling", ] tracks = self.api.resolve_url( "https://soundcloud.com/yndihalda/sets/dash-and-blast" ) assert len(tracks) == 4 for i, _ in enumerate(expected_tracks): assert isinstance(tracks[i], Track) assert tracks[i].name == expected_tracks[i] assert tracks[i].length > 500 assert len(tracks[i].artists) == 1 assert list(tracks[i].artists)[0].name == "yndi halda" @my_vcr.use_cassette("sc-liked.yaml") def test_get_user_likes(self): tracks = self.api.get_likes() assert len(tracks) == 3 assert isinstance(tracks[0], Track) assert tracks[1].name == "Pelican - Deny The Absolute" @my_vcr.use_cassette("sc-stream.yaml") def test_get_user_stream(self): tracks = self.api.get_user_stream() assert len(tracks) == 10 assert isinstance(tracks[0], Track) assert tracks[2].name == "JW Ep 20- Jeremiah Watkins" @my_vcr.use_cassette("sc-following.yaml") def test_get_followings(self): users = self.api.get_followings() assert len(users) == 10 assert users[0] == ("Young Legionnaire", "992503") assert users[1] == ("Tall Ships", "1710483") assert users[8] == ("Pelican Song", "27945548") assert users[9] == ("sleepmakeswaves", "1739693") @my_vcr.use_cassette("sc-user-tracks.yaml") def test_get_user_tracks(self): expected_tracks = [ "The Wait", "The Cliff (Palms Remix)", "The Cliff (Justin Broadrick Remix)", "The Cliff (Vocal Version)", "Pelican - The Creeper", "Pelican - Lathe Biosas", "Pelican - Ephemeral", "Pelican - Deny the Absolute", "Pelican - Immutable Dusk", "Pelican - Strung Up From The Sky", ] tracks = self.api.get_tracks(27945548) for i, _ in enumerate(expected_tracks): assert isinstance(tracks[i], Track) assert tracks[i].name == expected_tracks[i] assert tracks[i].length > 500 assert len(tracks[i].artists) == 1 @my_vcr.use_cassette("sc-set.yaml") def test_get_set(self): tracks = self.api.get_set("10961826") assert len(tracks) == 1 assert isinstance(tracks[0], dict) @my_vcr.use_cassette("sc-set-invalid.yaml") def test_get_invalid_set(self): tracks = self.api.get_set("blahblahrubbosh") assert tracks == [] @my_vcr.use_cassette("sc-sets.yaml") def test_get_sets(self): sets = self.api.get_sets() assert len(sets) == 2 name, set_id, tracks = sets[1] assert name == "Pelican" assert set_id == "10961826" assert len(tracks) == 1 def test_readeble_url(self): assert "Barsuk Records" == readable_url('"@"Barsuk Records') assert "_Barsuk Records" == readable_url("_Barsuk 'Records'") @my_vcr.use_cassette("sc-resolve-track-id.yaml") def test_resolves_stream_track(self): track = self.api.get_track("13158665", True) assert isinstance(track, Track) assert track.uri == ( "https://cf-media.sndcdn.com/fxguEjG4ax6B.128.mp3?Policy=" "eyJTdGF0ZW1lbnQiOlt7IlJlc291cmNlIjoiKjovL2NmLW1lZGlhLnNu" "ZGNkbi5jb20vZnhndUVqRzRheDZCLjEyOC5tcDMiLCJDb25kaXRpb24i" "OnsiRGF0ZUxlc3NUaGFuIjp7IkFXUzpFcG9jaFRpbWUiOjE0Nzc2MDA1" "NTd9fX1dfQ__&Signature=u9bxAkZOtTTF1VqTmLGmw3ENrqbiSTFK-" "sMvZL-ZsQK85DOepHh5MfPA4MNooszUy~PZqiVyBn4YnElhWyb~4B7kS" "6y0VZ6t-qF78CfTMOimemafpqfWJ8nYXczhM9pUpAwiS--lkNjGks4Qx" "i-FZJDBPG99gAIU0eVW78CADcpuOKLugGpzHl6gRPN2Z4zZ9dVujZ5Ml" "G2OWnPuNiBcE~wUFwcOxt9N6ePTff-wMFQR2PGpEK6wc6bWuB4WFNBkE" "0bmEke4cOQjWHa5FwYEidZN5rvv5lVT1r07zzifnADEipwMaZ2-QYdqz" "OYaM4jymFDhl7DklaU24PY5C5mH0A__&Key-Pair-Id=APKAJAGZ7VMH" "2PFPW6UQ" ) @my_vcr.use_cassette("sc-search.yaml") def test_search(self): tracks = self.api.search("the great descent") assert len(tracks) == 10 assert isinstance(tracks[0], Track) assert tracks[0].name == "Turn Around (Mix1)" ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1608019795.0 Mopidy-SoundCloud-3.0.1/tests/test_cache.py0000644000175100001710000000133500000000000020211 0ustar00runnerdockerimport unittest from unittest import mock from mopidy_soundcloud.soundcloud import cache class CacheTest(unittest.TestCase): def test_decorator(self): func = mock.Mock() decorated_func = cache() decorated_func(func) func() assert func.called is True assert decorated_func._call_count == 1 def test_set_default_cache(self): @cache() def returnstring(): return "ok" assert returnstring() == "ok" def test_set_ttl_cache(self): func = mock.Mock() decorated_func = cache(func, ttl=5) func() assert func.called is True assert decorated_func._call_count == 1 assert decorated_func.ttl == 5 ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1608019795.0 Mopidy-SoundCloud-3.0.1/tests/test_extension.py0000644000175100001710000000073500000000000021165 0ustar00runnerdockerimport unittest from mopidy_soundcloud import Extension class ExtensionTest(unittest.TestCase): def test_get_default_config(self): ext = Extension() config = ext.get_default_config() assert "[soundcloud]" in config assert "enabled = True" in config def test_get_config_schema(self): ext = Extension() schema = ext.get_config_schema() assert "auth_token" in schema assert "explore_songs" in schema ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1608019795.0 Mopidy-SoundCloud-3.0.1/tests/test_library.py0000644000175100001710000000554200000000000020616 0ustar00runnerdockerimport unittest import pykka from mopidy.models import Ref from mopidy_soundcloud import Extension, actor from mopidy_soundcloud.library import ( SoundCloudLibraryProvider, new_folder, simplify_search_query, ) from mopidy_soundcloud.soundcloud import safe_url class ApiTest(unittest.TestCase): def setUp(self): config = Extension().get_config_schema() config["auth_token"] = "1-35204-61921957-55796ebef403996" # using this user http://maildrop.cc/inbox/mopidytestuser self.backend = actor.SoundCloudBackend.start( config={"soundcloud": config, "proxy": {}}, audio=None ).proxy() self.library = SoundCloudLibraryProvider(backend=self.backend) def tearDown(self): pykka.ActorRegistry.stop_all() def test_add_folder(self): assert new_folder("Test", ["test"]) == Ref( name="Test", type="directory", uri="soundcloud:directory:test" ) def test_mpc_search(self): assert ( simplify_search_query({"any": ["explosions in the sky"]}) == "explosions in the sky" ) def test_moped_search(self): assert ( simplify_search_query( { "track_name": ["explosions in the sky"], "any": ["explosions in the sky"], } ) == "explosions in the sky explosions in the sky" ) def test_simple_search(self): assert ( simplify_search_query("explosions in the sky") == "explosions in the sky" ) def test_aria_search(self): assert ( simplify_search_query(["explosions", "in the sky"]) == "explosions in the sky" ) def test_only_resolves_soundcloud_uris(self): assert ( self.library.search( {"uri": "http://www.youtube.com/watch?v=wD6H6Yhluo8"} ) is None ) def test_returns_url_safe_string(self): assert ( safe_url("Alternative/Indie/rock/pop ") == "Alternative%2FIndie%2Frock%2Fpop+" ) assert ( safe_url("D∃∃P Hau⑀ iNDiE DᴬNCE | №➊ ²⁰¹⁴") == "DP+Hau+iNDiE+DANCE+%7C+No+2014" ) def test_default_folders(self): assert self.library.browse("soundcloud:directory") == [ Ref( name="Following", type="directory", uri="soundcloud:directory:following", ), Ref( name="Liked", type="directory", uri="soundcloud:directory:liked" ), Ref(name="Sets", type="directory", uri="soundcloud:directory:sets"), Ref( name="Stream", type="directory", uri="soundcloud:directory:stream", ), ] ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1608019795.0 Mopidy-SoundCloud-3.0.1/tox.ini0000644000175100001710000000075100000000000015707 0ustar00runnerdocker[tox] envlist = py37, py38, py39, black, check-manifest, flake8 [testenv] sitepackages = true deps = .[test] commands = python -m pytest \ --basetemp={envtmpdir} \ --cov=mopidy_soundcloud --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