Skip to content

Commit

Permalink
Release v0.4.0
Browse files Browse the repository at this point in the history
  • Loading branch information
Nick Steel committed Feb 16, 2016
2 parents 750363f + 9721990 commit cf583ad
Show file tree
Hide file tree
Showing 6 changed files with 121 additions and 26 deletions.
16 changes: 10 additions & 6 deletions .travis.yml
Original file line number Diff line number Diff line change
@@ -1,22 +1,26 @@
sudo: false

language: python

python:
- "2.7_with_system_site_packages"

addons:
apt:
sources:
- mopidy-stable
packages:
- mopidy

env:
- TOX_ENV=py27
- TOX_ENV=flake8

install:
- "wget -O - http://apt.mopidy.com/mopidy.gpg | sudo apt-key add -"
- "sudo wget -O /etc/apt/sources.list.d/mopidy.list http://apt.mopidy.com/mopidy.list"
- "sudo apt-get update || true"
- "sudo apt-get install mopidy"
- "pip install tox"

script:
- "tox -e $TOX_ENV"

after_success:
- "if [ $TOX_ENV == 'py27' ]; then pip install coveralls; coveralls; fi"

- "if [ $TOX_ENV == 'py27' ]; then pip install --pre coveralls; coveralls; fi"
9 changes: 8 additions & 1 deletion README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -39,10 +39,11 @@ Install by running::
.. Or, if available, install the Debian/Ubuntu package from `apt.mopidy.com
.. <http://apt.mopidy.com/>`_.
Some radio streams may require additional audio plugins. These can be found in the following packages:
Some radio streams may require additional audio plugins. These can be found in the gstreamer plugin packages for your system. For versions of Mopidy prior to v2.0.0, these might include:
* `gstreamer0.10-plugins-ugly`
* `gstreamer0.10-plugins-bad`
* `gstreamer0.10-ffmpeg`
For Mopidy v2.0.0 and above, use the gstreamer1.0-plugins-* packages instead.


Known issues
Expand Down Expand Up @@ -74,6 +75,12 @@ Project resources
Changelog
=========

v0.4.0 (2016-02-16)
-------------------

- Borrow Mopidy's internal stream unwrapping to avoid incompatibilities with Mopidy v2.0.0 (PR: #28)
- Improved handling of malformed pls playlists.

v0.3.0 (2016-02-06)
-------------------

Expand Down
2 changes: 1 addition & 1 deletion mopidy_tunein/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

from mopidy import config, ext

__version__ = '0.3.0'
__version__ = '0.4.0'


class Extension(ext.Extension):
Expand Down
101 changes: 90 additions & 11 deletions mopidy_tunein/actor.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
from __future__ import unicode_literals

import logging
import time

from mopidy import backend, httpclient
from mopidy import backend, exceptions, httpclient
from mopidy.audio import scan
# TODO: Something else, using internal APIs is not cool.
from mopidy.internal import http, playlists
from mopidy.models import Ref, SearchResult
from mopidy.stream.actor import StreamPlaybackProvider

import pykka

Expand Down Expand Up @@ -34,20 +36,20 @@ class TuneInBackend(pykka.ThreadingActor, backend.Backend):
def __init__(self, config, audio):
super(TuneInBackend, self).__init__()

session = get_requests_session(
self._session = get_requests_session(
proxy_config=config['proxy'],
user_agent='%s/%s' % (
mopidy_tunein.Extension.dist_name,
mopidy_tunein.__version__))

self._timeout = config['tunein']['timeout']

self._scanner = scan.Scanner(
timeout=config['tunein']['timeout'],
proxy_config=config['proxy'])
self.tunein = tunein.TuneIn(config['tunein']['timeout'], session)
self.tunein = tunein.TuneIn(config['tunein']['timeout'], self._session)
self.library = TuneInLibrary(self)
self.playback = TuneInPlayback(audio=audio,
backend=self,
config=config)
self.playback = TuneInPlayback(audio=audio, backend=self)


class TuneInLibrary(backend.LibraryProvider):
Expand Down Expand Up @@ -123,9 +125,7 @@ def search(self, query=None, uris=None, exact=False):
return SearchResult(uri='tunein:search', tracks=tracks)


class TuneInPlayback(StreamPlaybackProvider):
def __init__(self, audio, backend, config):
super(TuneInPlayback, self).__init__(audio, backend, config)
class TuneInPlayback(backend.PlaybackProvider):

def translate_uri(self, uri):
variant, identifier = translator.parse_uri(uri)
Expand All @@ -136,7 +136,7 @@ def translate_uri(self, uri):
while stream_uris:
uri = stream_uris.pop(0)
logger.debug('Looking up URI: %s.' % uri)
new_uri = super(TuneInPlayback, self).translate_uri(uri)
new_uri = self.unwrap_stream(uri)
if new_uri:
return new_uri
else:
Expand All @@ -149,3 +149,82 @@ def translate_uri(self, uri):
stream_uris.extend(new_uris)
logger.debug('TuneIn lookup failed.')
return None

def unwrap_stream(self, uri):
unwrapped_uri, _ = _unwrap_stream(
uri, timeout=self.backend._timeout, scanner=self.backend._scanner,
requests_session=self.backend._session)
return unwrapped_uri


# Shamelessly taken from mopidy.stream.actor
def _unwrap_stream(uri, timeout, scanner, requests_session):
"""
Get a stream URI from a playlist URI, ``uri``.
Unwraps nested playlists until something that's not a playlist is found or
the ``timeout`` is reached.
"""

original_uri = uri
seen_uris = set()
deadline = time.time() + timeout

while time.time() < deadline:
if uri in seen_uris:
logger.info(
'Unwrapping stream from URI (%s) failed: '
'playlist referenced itself', uri)
return None, None
else:
seen_uris.add(uri)

logger.debug('Unwrapping stream from URI: %s', uri)

try:
scan_timeout = deadline - time.time()
if scan_timeout < 0:
logger.info(
'Unwrapping stream from URI (%s) failed: '
'timed out in %sms', uri, timeout)
return None, None
scan_result = scanner.scan(uri, timeout=scan_timeout)
except exceptions.ScannerError as exc:
logger.debug('GStreamer failed scanning URI (%s): %s', uri, exc)
scan_result = None

if scan_result is not None:
if scan_result.playable or (
not scan_result.mime.startswith('text/') and
not scan_result.mime.startswith('application/')
):
logger.debug(
'Unwrapped potential %s stream: %s', scan_result.mime, uri)
return uri, scan_result

download_timeout = deadline - time.time()
if download_timeout < 0:
logger.info(
'Unwrapping stream from URI (%s) failed: timed out in %sms',
uri, timeout)
return None, None
content = http.download(
requests_session, uri, timeout=download_timeout)

if content is None:
logger.info(
'Unwrapping stream from URI (%s) failed: '
'error downloading URI %s', original_uri, uri)
return None, None

uris = playlists.parse(content)
if not uris:
logger.debug(
'Failed parsing URI (%s) as playlist; found potential stream.',
uri)
return uri, None

# TODO Test streams and return first that seems to be playable
logger.debug(
'Parsed playlist (%s) and found new URI: %s', uri, uris[0])
uri = uris[0]
16 changes: 11 additions & 5 deletions mopidy_tunein/tunein.py
Original file line number Diff line number Diff line change
Expand Up @@ -88,11 +88,14 @@ def parse_pls(data):
continue
for i in xrange(cp.getint(section, 'numberofentries')):
try:
# TODO: Remove this horrible hack
if cp.get(section, 'length%d' % (i+1)) == '-1':
# TODO: Remove this horrible hack to avoid adverts
if cp.has_option(section, 'length%d' % (i+1)):
if cp.get(section, 'length%d' % (i+1)) == '-1':
yield cp.get(section, 'file%d' % (i+1))
else:
yield cp.get(section, 'file%d' % (i+1))
except configparser.NoOptionError:
yield cp.get(section, 'file%d' % (i+1))
return


def fix_asf_uri(uri):
Expand Down Expand Up @@ -305,8 +308,11 @@ def parse_stream_url(self, url):
parser = find_playlist_parser(extension, content_type)
if parser:
playlist_data = StringIO.StringIO(playlist)
results = [u for u in parser(playlist_data)
if u and u != url]
try:
results = [u for u in parser(playlist_data)
if u and u != url]
except Exception as e:
logger.error('TuneIn playlist parsing failed %s' % e)
if not results:
logger.debug('Parsing failure, '
'malformed playlist: %s' % playlist)
Expand Down
3 changes: 1 addition & 2 deletions tox.ini
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,11 @@ deps =
coverage
mock
nose
mopidy==dev
install_command = pip install --allow-unverified=mopidy --pre {opts} {packages}
commands = nosetests -v --with-xunit --xunit-file=xunit-{envname}.xml --with-coverage --cover-package=mopidy_tunein

[testenv:flake8]
deps =
flake8
flake8-import-order
skip_install = true
commands = flake8

0 comments on commit cf583ad

Please sign in to comment.