Skip to content

Commit

Permalink
Spotify controller creation (#230)
Browse files Browse the repository at this point in the history
* Initial Spotify commit

* Add app id to config

* Add token logic and starting session with token

Can't use spotipy's method of getting a token.
Seems there's no scope with enough permissions to start Spotify on CC

* Start spotipy client after app has been launched

* Add spotipy to requirements.txt

Unfortunately the version on PyPi is outdated.
See spotipy-dev/spotipy#211

* Add play_song and play_songs. Only create new token if the session hasn't been started.

* Add context playback mode

* Add docstrings to SpotifyController methods

* Add example on how to use SpotifyController

* Fix PEP8 errors on SpotifyController

* Remove access token logic from Spotify Controller code

This was moved to it's own repo: https://github.com/enriquegh/spotify-webplayer-token

* Fix importing BaseController

This was causing pylint to complain

* Add additional logic when retrieving device id

This will make sure that the device that is being obtained is a Chromecast

* No need for else statements since we are already raising at the beginning

* Poll received messages to know when Spotify is completely started

Instead of sleeping for just 5 seconds, we can check if Spotify responds with `setCredentialsResponse` and then go ahead and grab spotify id.

* Fixed errors for flake8

* Remove external dependencies

The example will now have how to get the access token yourself and how to use spotipy to avoid adding dependencies to pychromecast

* Fix flake8 error

* Fix spotify example
  • Loading branch information
enriquegh authored and balloob committed Apr 9, 2018
1 parent e280935 commit 2ccb453
Show file tree
Hide file tree
Showing 4 changed files with 92 additions and 1 deletion.
38 changes: 38 additions & 0 deletions examples/spotify_example.py
@@ -0,0 +1,38 @@
"""
Example on how to use the Spotify Controller.
NOTE: You need to install the spotipy and spotify-token dependencies.
This can be done by running the following:
pip install spotify-token
pip install git+https://github.com/plamere/spotipy.git
"""
import pychromecast
from pychromecast.controllers.spotify import SpotifyController
import spotify_token as st
import spotipy

chromecasts = pychromecast.get_chromecasts()
cast = chromecasts[0]

CAST_NAME = "My Chromecast"
device_id = None

if cast.name == CAST_NAME:

data = st.start_session("SPOTIFY_USERNAME", "SPOTIFY_PASSWORD")
access_token = data[0]

client = spotipy.Spotify(auth=access_token)

sp = SpotifyController(access_token)
cast.register_handler(sp)
sp.launch_app()

devices_available = client.devices()

for device in devices_available['devices']:
if device['name'] == CAST_NAME and device['type'] == 'CastVideo':
device_id = device['id']
break

client.start_playback(device_id=device_id, uris=["spotify:track:3Zwu2K0Qa5sT6teCCHPShP"])
1 change: 1 addition & 0 deletions pychromecast/config.py
Expand Up @@ -10,6 +10,7 @@
APP_MEDIA_RECEIVER = "CC1AD845"
APP_PLEX = "06ee44ee-e7e3-4249-83b6-f5d0b6f07f34_1"
APP_DASHCAST = "84912283"
APP_SPOTIFY = "CC32E753"


def get_possible_app_ids():
Expand Down
52 changes: 52 additions & 0 deletions pychromecast/controllers/spotify.py
@@ -0,0 +1,52 @@
"""
Controller to interface with the DashCast app namespace.
"""
import logging
import time

from . import BaseController
from ..config import APP_SPOTIFY

logging.basicConfig(level=logging.DEBUG)

APP_NAMESPACE = "urn:x-cast:com.spotify.chromecast.secure.v1"
TYPE_STATUS = "setCredentials"
TYPE_RESPONSE_STATUS = 'setCredentialsResponse'


# pylint: disable=too-many-instance-attributes
class SpotifyController(BaseController):
""" Controller to interact with Spotify namespace. """

# pylint: disable=useless-super-delegation
# The pylint rule useless-super-delegation doesn't realize
# we are setting default values here.
def __init__(self, access_token):
super(SpotifyController, self).__init__(APP_NAMESPACE, APP_SPOTIFY)

self.logger = logging.getLogger(__name__)
self.session_started = False
self.access_token = access_token
self.is_launched = False
# pylint: enable=useless-super-delegation

# pylint: disable=unused-argument,no-self-use
def receive_message(self, message, data):
""" Currently not doing anything with received messages. """
if data['type'] == TYPE_RESPONSE_STATUS:
self.is_launched = True
return True

def launch_app(self):
""" Launch main application """

def callback():
"""Callback function"""
self.send_message({"type": TYPE_STATUS,
"credentials": self.access_token})

self.launch(callback_function=callback)

# Need to wait for Spotify to be launched on Chromecast completely
while not self.is_launched:
time.sleep(1)
2 changes: 1 addition & 1 deletion requirements.txt
@@ -1,3 +1,3 @@
requests>=2.0
protobuf>=3.0.0
zeroconf>=0.17.7
zeroconf>=0.17.7

0 comments on commit 2ccb453

Please sign in to comment.