Commit c566175f authored by Stein Magnus Jodal's avatar Stein Magnus Jodal
Browse files

Release v2.0.1

parents 0b592c2e 81a73125
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:
- "pip install tox"
script:
- "tox -e $TOX_ENV"
after_success:
- "if [ $TOX_ENV == 'py27' ]; then pip install coveralls; coveralls; fi"
notifications:
irc:
channels:
- "irc.freenode.org#mopidy"
on_success: change
on_failure: change
use_notice: true
skip_join: true
**************
Mopidy-Youtube
Mopidy-YouTube
**************
.. image:: https://app.wercker.com/status/08a31153413287cd9a6965d8b7f26586/m
:target: https://app.wercker.com/project/bykey/08a31153413287cd9a6965d8b7f26586
:alt: CI build status
.. image:: https://img.shields.io/pypi/v/Mopidy-Youtube.svg?style=flat
:target: https://pypi.python.org/pypi/Mopidy-Youtube/
.. image:: https://img.shields.io/pypi/v/Mopidy-YouTube.svg?style=flat
:target: https://pypi.python.org/pypi/Mopidy-YouTube/
:alt: Latest PyPI version
.. image:: https://img.shields.io/pypi/dm/Mopidy-Youtube.svg?style=flat
:target: https://pypi.python.org/pypi/Mopidy-Youtube/
.. image:: https://img.shields.io/pypi/dm/Mopidy-YouTube.svg?style=flat
:target: https://pypi.python.org/pypi/Mopidy-YouTube/
:alt: Number of PyPI downloads
.. image:: https://img.shields.io/coveralls/mopidy/mopidy-youtube/master.svg?style=flat
:target: https://coveralls.io/r/mopidy/mopidy-youtube?branch=master
:alt: Test coverage
.. image:: https://img.shields.io/travis/mopidy/mopidy-youtube/develop.svg?style=flat
:target: https://travis-ci.org/mopidy/mopidy-youtube
:alt: Travis CI build status
.. image:: https://img.shields.io/coveralls/mopidy/mopidy-youtube/develop.svg?style=flat
:target: https://coveralls.io/r/mopidy/mopidy-youtube?branch=develop
:alt: Test coverage
Mopidy extension that plays sound from Youtube.
Mopidy extension that plays sound from YouTube.
Installation
============
Make sure you already have the GStreamer plugins, if not you can install it by
Make sure you already have the GStreamer plugins, especially the "bad"
collection of plugins. For example, on Debian/Ubuntu you can install it by
running::
$ sudo apt-get install gstreamer0.10-plugins-bad
sudo apt-get install gstreamer0.10-plugins-bad
Install by running::
$ pip install Mopidy-Youtube
pip install Mopidy-YouTube
Configuration
=============
No configuration needed. The only supported config value is ``youtube/enabled``
which can be set to ``false`` to disable the extension.
How to use
==========
Usage
=====
Simply use search for filename in your MPD client or add Youtube url to
Simply use search for filename in your MPD client or add YouTube URL to
playlist prefixed by ``yt:``.
Example: ``yt:http://www.youtube.com/watch?v=Njpw2PVb1c0``
Example video::
Example for playlist:
``yt:http://www.youtube.com/playlist?list=PLeCg_YDclAETQHa8VyFUHKC_Ly0HUWUnq``
yt:http://www.youtube.com/watch?v=Njpw2PVb1c0
Example for playlist::
If resolving stops working
==========================
yt:http://www.youtube.com/playlist?list=PLeCg_YDclAETQHa8VyFUHKC_Ly0HUWUnq
Update pafy library::
pip install pafy -U
Troubleshooting
===============
If resolving of URIs stops working, always try to update the pafy library
first::
pip install --upgrade pafy
Project resources
......@@ -61,12 +72,21 @@ Project resources
- `Source code <https://github.com/mopidy/mopidy-youtube>`_
- `Issue tracker <https://github.com/mopidy/mopidy-youtube/issues>`_
- `Download development snapshot <https://github.com/mopidy/mopidy-youtube/archive/master.tar.gz#egg=Mopidy-Youtube-dev>`_
- `Download development snapshot <https://github.com/mopidy/mopidy-youtube/archive/develop.tar.gz#egg=Mopidy-Youtube-dev>`_
Changelog
=========
v2.0.1 (2015-08-19)
-------------------
- Update links to GitHub repository.
- Don't return ``None`` values to Mopidy when lookup or search returns invalid
data. In Mopidy 1.0, this caused a crash. In Mopidy 1.1, this caused warnings
about the YouTube backend returning invalid data. (Fixes: #28, PR: #35)
v2.0.0 (2015-04-01)
-------------------
......
......@@ -6,14 +6,14 @@ import os
from mopidy import config, ext
__version__ = '2.0.0'
__version__ = '2.0.1'
logger = logging.getLogger(__name__)
class Extension(ext.Extension):
dist_name = 'Mopidy-Youtube'
dist_name = 'Mopidy-YouTube'
ext_name = 'youtube'
version = __version__
......@@ -26,5 +26,5 @@ class Extension(ext.Extension):
return schema
def setup(self, registry):
from .backend import YoutubeBackend
registry.add('backend', YoutubeBackend)
from .backend import YouTubeBackend
registry.add('backend', YouTubeBackend)
......@@ -25,7 +25,7 @@ session = requests.Session()
def resolve_track(track, stream=False):
logger.debug("Resolving Youtube for track '%s'", track)
logger.debug("Resolving YouTube for track '%s'", track)
if hasattr(track, 'uri'):
return resolve_url(track.comment, stream)
else:
......@@ -71,7 +71,7 @@ def resolve_url(url, stream=False):
comment=video.videoid,
length=video.length * 1000,
album=Album(
name='Youtube',
name='YouTube',
images=[video.bigthumb, video.bigthumbhd]
),
uri=uri
......@@ -100,7 +100,7 @@ def search_youtube(q):
def resolve_playlist(url):
resolve_pool = ThreadPool(processes=16)
logger.info("Resolving Youtube-Playlist '%s'", url)
logger.info("Resolving YouTube-Playlist '%s'", url)
playlist = []
page = 'first'
......@@ -112,7 +112,7 @@ def resolve_playlist(url):
'part': 'contentDetails'
}
if page and page != "first":
logger.debug("Get Youtube-Playlist '%s' page %s", url, page)
logger.debug("Get YouTube-Playlist '%s' page %s", url, page)
params['pageToken'] = page
result = session.get(yt_api_endpoint+'playlistItems', params=params)
......@@ -128,17 +128,17 @@ def resolve_playlist(url):
return [item for item in playlist if item]
class YoutubeBackend(pykka.ThreadingActor, backend.Backend):
class YouTubeBackend(pykka.ThreadingActor, backend.Backend):
def __init__(self, config, audio):
super(YoutubeBackend, self).__init__()
super(YouTubeBackend, self).__init__()
self.config = config
self.library = YoutubeLibraryProvider(backend=self)
self.playback = YoutubePlaybackProvider(audio=audio, backend=self)
self.library = YouTubeLibraryProvider(backend=self)
self.playback = YouTubePlaybackProvider(audio=audio, backend=self)
self.uri_schemes = ['youtube', 'yt']
class YoutubeLibraryProvider(backend.LibraryProvider):
class YouTubeLibraryProvider(backend.LibraryProvider):
def lookup(self, track):
if 'yt:' in track:
track = track.replace('yt:', '')
......@@ -149,9 +149,9 @@ class YoutubeLibraryProvider(backend.LibraryProvider):
if 'list' in req:
return resolve_playlist(req.get('list')[0])
else:
return [resolve_url(track)]
return [item for item in [resolve_url(track)] if item]
else:
return [resolve_url(track)]
return [item for item in [resolve_url(track)] if item]
def search(self, query=None, uris=None, exact=False):
# TODO Support exact search
......@@ -171,21 +171,21 @@ class YoutubeLibraryProvider(backend.LibraryProvider):
)
else:
logger.info(
"Resolving Youtube for track '%s'", search_query)
"Resolving YouTube for track '%s'", search_query)
return SearchResult(
uri='youtube:search',
tracks=[resolve_url(search_query)]
tracks=[t for t in [resolve_url(search_query)] if t]
)
else:
search_query = ' '.join(query.values()[0])
logger.info("Searching Youtube for query '%s'", search_query)
logger.info("Searching YouTube for query '%s'", search_query)
return SearchResult(
uri='youtube:search',
tracks=search_youtube(search_query)
)
class YoutubePlaybackProvider(backend.PlaybackProvider):
class YouTubePlaybackProvider(backend.PlaybackProvider):
def translate_uri(self, uri):
track = resolve_track(uri, True)
......
[youtube]
enabled = true
# TODO: Add additional config values and their default values here, or remove
# this comment entirely.
\ No newline at end of file
......@@ -12,13 +12,13 @@ def get_version(filename):
setup(
name='Mopidy-Youtube',
name='Mopidy-Youtube', # Casing as originally registrered on PyPI
version=get_version('mopidy_youtube/__init__.py'),
url='https://github.com/mopidy/mopidy-youtube',
license='Apache License, Version 2.0',
author='Janez Troha',
author_email='dz0ny@ubuntu.si',
description='Mopidy extension that plays sound from Youtube',
description='Mopidy extension that plays sound from YouTube',
long_description=open('README.rst').read(),
packages=find_packages(exclude=['tests', 'tests.*']),
zip_safe=False,
......@@ -30,12 +30,6 @@ setup(
'Mopidy >= 1.0',
'Pykka >= 1.1',
],
test_suite='nose.collector',
tests_require=[
'nose',
'mock >= 1.0',
'vcrpy',
],
entry_points={
'mopidy.ext': [
'youtube = mopidy_youtube:Extension',
......
from __future__ import unicode_literals
import mock
import pafy
import pytest
import vcr
from mopidy_youtube import backend
@pytest.yield_fixture
def pafy_mock():
patcher = mock.patch.object(backend, 'pafy', spec=pafy)
yield patcher.start()
patcher.stop()
@pytest.fixture
def pafy_mock_with_video(pafy_mock):
video_mock = pafy_mock.new.return_value
video_mock.bigthumb = 'big thumb'
video_mock.bigthumbhd = 'big thumb in hd'
video_mock.getbestaudio.return_value.url = 'http://example.com/'
video_mock.length = 2000
video_mock.title = 'a title'
video_mock.videoid = 'a video id'
return pafy_mock
@vcr.use_cassette('tests/fixtures/youtube_playlist_resolve.yaml')
def test_playlist_resolver(pafy_mock_with_video):
videos = backend.resolve_playlist('PLOxORm4jpOQfMU7bpfGCzDyLropIYEHuR')
assert len(videos) == 104
@vcr.use_cassette('tests/fixtures/youtube_search.yaml')
def test_search_yt(pafy_mock_with_video):
videos = backend.search_youtube('chvrches')
assert len(videos) == 15
@vcr.use_cassette('tests/fixtures/resolve_track.yaml')
def test_resolve_track(pafy_mock_with_video):
video = backend.resolve_track('TU3b1qyEGsE')
assert video
@vcr.use_cassette('tests/fixtures/resolve_track_failed.yaml')
def test_resolve_track_failed(pafy_mock):
pafy_mock.new.side_effect = Exception('Removed')
video = backend.resolve_track('unknown')
assert not video
@vcr.use_cassette('tests/fixtures/resolve_track_stream.yaml')
def test_resolve_track_stream(pafy_mock_with_video):
video = backend.resolve_track('TU3b1qyEGsE', stream=True)
assert video
from __future__ import unicode_literals
import unittest
import mock
import vcr
from mopidy_youtube import Extension
from mopidy_youtube.backend import (
resolve_playlist, resolve_track, search_youtube)
class ExtensionTest(unittest.TestCase):
def test_get_default_config(self):
ext = Extension()
config = ext.get_default_config()
self.assertIn('[youtube]', config)
self.assertIn('enabled = true', config)
@vcr.use_cassette('tests/fixtures/youtube_playlist_resolve.yaml')
def test_playlist_resolver(self):
with mock.patch('mopidy_youtube.backend.pafy'):
videos = resolve_playlist('PLOxORm4jpOQfMU7bpfGCzDyLropIYEHuR')
self.assertEquals(len(videos), 104)
@vcr.use_cassette('tests/fixtures/youtube_search.yaml')
def test_search_yt(self):
with mock.patch('mopidy_youtube.backend.pafy'):
videos = search_youtube('chvrches')
self.assertEquals(len(videos), 15)
@vcr.use_cassette('tests/fixtures/resolve_track.yaml')
def test_resolve_track(self):
with mock.patch('mopidy_youtube.backend.pafy'):
video = resolve_track('TU3b1qyEGsE')
self.assertTrue(video)
def test_get_default_config():
ext = Extension()
@vcr.use_cassette('tests/fixtures/resolve_track_failed.yaml')
def test_resolve_track_failed(self):
with mock.patch('mopidy_youtube.backend.pafy') as pafy:
pafy.new.side_effect = Exception('Removed')
video = resolve_track('unknown')
self.assertFalse(video)
config = ext.get_default_config()
@vcr.use_cassette('tests/fixtures/resolve_track_stream.yaml')
def test_resolve_track_stream(self):
with mock.patch('mopidy_youtube.backend.pafy'):
video = resolve_track('TU3b1qyEGsE', True)
self.assertTrue(video)
assert '[youtube]' in config
assert 'enabled = true' in config
......@@ -3,12 +3,14 @@ envlist = py27, flake8
[testenv]
sitepackages = true
# vcrpy tries to patch tornado, so if it is present, it must be recent.
deps =
mock
mopidy==dev
pytest
pytest-cov
pytest-xdist
tornado >= 4
vcrpy
install_command = pip install --allow-unverified=mopidy --pre {opts} {packages}
commands =
......@@ -21,4 +23,6 @@ commands =
[testenv:flake8]
deps =
flake8
commands = flake8
\ No newline at end of file
flake8-import-order
skip_install = true
commands = flake8
box: dz0ny/mopidy-box
# Build definition
build:
# The steps that will be executed on build
steps:
# A step that sets up the python virtual environment
- virtualenv:
name: setup virtual environment
install_wheel: true # Enable wheel to speed up builds (experimental)
- script:
name: Install tox
code: |
pip install tox
- script:
name: Run linter
code: |
tox -e flake8
- script:
name: Run tests
code: |
tox -e py27
after-steps:
- virtualenv:
name: setup virtual environment
install_wheel: true # Enable wheel to speed up builds (experimental)
- script:
name: Report code coverage
code: |
pip install requests coveralls
coveralls
deploy:
steps:
- script:
name: Deploy to PYPI
code: |
echo "[server-login]" > ~/.pypirc
echo "username:" $PYPI_USER >> ~/.pypirc
echo "password:" $PYPI_PASSWORD >> ~/.pypirc
python setup.py sdist upload
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment