diff --git a/examples/bbciplayer_example.py b/examples/bbciplayer_example.py new file mode 100644 index 000000000..1224a9d7c --- /dev/null +++ b/examples/bbciplayer_example.py @@ -0,0 +1,95 @@ +""" +Example on how to use the BBC iPlayer Controller +""" +# pylint: disable=invalid-name + +import argparse +import logging +import sys +from time import sleep +import json + +import zeroconf +import pychromecast +from pychromecast import quick_play + +# Change to the name of your Chromecast +CAST_NAME = "Lounge Video" + +# Note: Media ID is NOT the 8 digit alpha-numeric in the URL +# it can be found by right clicking the playing video on the web interface +# e.g. https://www.bbc.co.uk/iplayer/episode/b09w7fd9/bitz-bob-series-1-1-castle-makeover shows: +# "2908kbps | dash (mf_cloudfront_dash_https) +# b09w70r2 | 960x540" +MEDIA_ID = "b09w70r2" +IS_LIVE = False +METADATA = { + "metadatatype": 0, + "title": "Bitz & Bob", + "subtitle": "Castle Makeover", + "images": [{"url": "https://ichef.bbci.co.uk/images/ic/1280x720/p07j4m3r.jpg"}], +} + +parser = argparse.ArgumentParser( + description="Example on how to use the BBC iPlayer Controller to play an media stream." +) +parser.add_argument( + "--cast", help='Name of cast device (default: "%(default)s")', default=CAST_NAME +) +parser.add_argument( + "--known-host", + help="Add known host (IP), can be used multiple times", + action="append", +) +parser.add_argument("--show-debug", help="Enable debug log", action="store_true") +parser.add_argument( + "--show-zeroconf-debug", help="Enable zeroconf debug log", action="store_true" +) +parser.add_argument( + "--media_id", help='MediaID (default: "%(default)s")', default=MEDIA_ID +) +parser.add_argument( + "--metadata", help='Metadata (default: "%(default)s")', default=json.dumps(METADATA) +) +parser.add_argument( + "--is_live", + help="Show 'live' and no current/end timestamps on UI", + action="store_true", + default=IS_LIVE, +) +args = parser.parse_args() + +app_name = "bbciplayer" +app_data = { + "media_id": args.media_id, + "is_live": args.is_live, + "metadata": json.loads(args.metadata), +} + +if args.show_debug: + logging.basicConfig(level=logging.DEBUG) +if args.show_zeroconf_debug: + print("Zeroconf version: " + zeroconf.__version__) + logging.getLogger("zeroconf").setLevel(logging.DEBUG) + +chromecasts, browser = pychromecast.get_listed_chromecasts( + friendly_names=[args.cast], known_hosts=args.known_host +) +if not chromecasts: + print('No chromecast with name "{}" discovered'.format(args.cast)) + sys.exit(1) + +cast = chromecasts[0] +# Start socket client's worker thread and wait for initial status update +cast.wait() +print( + 'Found chromecast with name "{}", attempting to play "{}"'.format( + args.cast, args.media_id + ) +) + +quick_play.quick_play(cast, app_name, app_data) + +sleep(10) + +browser.stop_discovery() diff --git a/examples/bbcsounds_example.py b/examples/bbcsounds_example.py new file mode 100644 index 000000000..77605a264 --- /dev/null +++ b/examples/bbcsounds_example.py @@ -0,0 +1,95 @@ +""" +Example on how to use the BBC iPlayer Controller +""" +# pylint: disable=invalid-name + +import argparse +import logging +import sys +from time import sleep +import json + +import zeroconf +import pychromecast +from pychromecast import quick_play + +# Change to the name of your Chromecast +CAST_NAME = "Lounge Video" + +# Media ID can be found in the URL +# e.g. https://www.bbc.co.uk/sounds/live:bbc_radio_one +MEDIA_ID = "bbc_radio_one" +IS_LIVE = True +METADATA = { + "metadatatype": 0, + "title": "Radio 1", + "images": [ + { + "url": "https://sounds.files.bbci.co.uk/2.3.0/networks/bbc_radio_one/background_1280x720.png" + } + ], +} + +parser = argparse.ArgumentParser( + description="Example on how to use the BBC Sounds Controller to play an media stream." +) +parser.add_argument( + "--cast", help='Name of cast device (default: "%(default)s")', default=CAST_NAME +) +parser.add_argument( + "--known-host", + help="Add known host (IP), can be used multiple times", + action="append", +) +parser.add_argument("--show-debug", help="Enable debug log", action="store_true") +parser.add_argument( + "--show-zeroconf-debug", help="Enable zeroconf debug log", action="store_true" +) +parser.add_argument( + "--media_id", help='MediaID (default: "%(default)s")', default=MEDIA_ID +) +parser.add_argument( + "--metadata", help='Metadata (default: "%(default)s")', default=json.dumps(METADATA) +) +parser.add_argument( + "--is_live", + help="Show 'live' and no current/end timestamps on UI", + action="store_true", + default=IS_LIVE, +) +args = parser.parse_args() + +app_name = "bbcsounds" +app_data = { + "media_id": args.media_id, + "is_live": args.is_live, + "metadata": json.loads(args.metadata), +} + +if args.show_debug: + logging.basicConfig(level=logging.DEBUG) +if args.show_zeroconf_debug: + print("Zeroconf version: " + zeroconf.__version__) + logging.getLogger("zeroconf").setLevel(logging.DEBUG) + +chromecasts, browser = pychromecast.get_listed_chromecasts( + friendly_names=[args.cast], known_hosts=args.known_host +) +if not chromecasts: + print('No chromecast with name "{}" discovered'.format(args.cast)) + sys.exit(1) + +cast = chromecasts[0] +# Start socket client's worker thread and wait for initial status update +cast.wait() +print( + 'Found chromecast with name "{}", attempting to play "{}"'.format( + args.cast, args.media_id + ) +) + +quick_play.quick_play(cast, app_name, app_data) + +sleep(10) + +browser.stop_discovery() diff --git a/pychromecast/config.py b/pychromecast/config.py index e92200b07..7033fbf6a 100644 --- a/pychromecast/config.py +++ b/pychromecast/config.py @@ -15,6 +15,8 @@ APP_SUPLA = "A41B766D" APP_YLEAREENA = "A9BCCB7C" APP_BUBBLEUPNP = "3927FA74" +APP_BBCSOUNDS = "03977A48" +APP_BBCIPLAYER = "5E81F6DB" def get_possible_app_ids(): diff --git a/pychromecast/controllers/bbciplayer.py b/pychromecast/controllers/bbciplayer.py new file mode 100644 index 000000000..905607f39 --- /dev/null +++ b/pychromecast/controllers/bbciplayer.py @@ -0,0 +1,46 @@ +""" +Controller to interface with BBC iPlayer. +""" +# Note: Media ID is NOT the 8 digit alpha-numeric in the URL +# it can be found by right clicking the playing video on the web interface +# e.g. https://www.bbc.co.uk/iplayer/episode/b09w7fd9/bitz-bob-series-1-1-castle-makeover shows: +# "2908kbps | dash (mf_cloudfront_dash_https) +# b09w70r2 | 960x540" + +import logging + +from . import BaseController +from ..config import APP_BBCIPLAYER +from .media import STREAM_TYPE_BUFFERED, STREAM_TYPE_LIVE + +APP_NAMESPACE = "urn:x-cast:com.google.cast.media" + + +class BbcIplayerController(BaseController): + """Controller to interact with BBC iPlayer namespace.""" + + def __init__(self): + super().__init__(APP_NAMESPACE, APP_BBCIPLAYER) + + self.logger = logging.getLogger(__name__) + + def play_media(self, media_id, is_live=False, **kwargs): + """Play BBC iPlayer media""" + stream_type = STREAM_TYPE_LIVE if is_live else STREAM_TYPE_BUFFERED + metadata = kwargs.get("metadata", {"metadataType": 0, "title": ""}) + subtitle = metadata.pop("subtitle", "") + + msg = { + "media": { + "contentId": media_id, + "customData": {"secondary_title": subtitle}, + "metadata": metadata, + "streamType": stream_type, + }, + "type": "LOAD", + } + self.send_message(msg, inc_session_id=False) + + def quick_play(self, media_id=None, is_live=False, **kwargs): + """Quick Play""" + self.play_media(media_id, is_live=is_live, **kwargs) diff --git a/pychromecast/controllers/bbcsounds.py b/pychromecast/controllers/bbcsounds.py new file mode 100644 index 000000000..0fcca7d9d --- /dev/null +++ b/pychromecast/controllers/bbcsounds.py @@ -0,0 +1,42 @@ +""" +Controller to interface with BBC Sounds. +""" +# Media ID can be found in the URL +# e.g. https://www.bbc.co.uk/sounds/live:bbc_radio_one + +import logging + +from . import BaseController +from ..config import APP_BBCSOUNDS +from .media import STREAM_TYPE_BUFFERED, STREAM_TYPE_LIVE + +APP_NAMESPACE = "urn:x-cast:com.google.cast.media" + + +class BbcSoundsController(BaseController): + """Controller to interact with BBC Sounds namespace.""" + + def __init__(self): + super().__init__(APP_NAMESPACE, APP_BBCSOUNDS) + + self.logger = logging.getLogger(__name__) + + def play_media(self, media_id, is_live=False, **kwargs): + """Play BBC Sounds media""" + stream_type = STREAM_TYPE_LIVE if is_live else STREAM_TYPE_BUFFERED + metadata_default = {"metadataType": 0, "title": ""} + + msg = { + "media": { + "contentId": media_id, + "metadata": kwargs.get("metadata", metadata_default), + "streamType": stream_type, + }, + "type": "LOAD", + } + + self.send_message(msg, inc_session_id=False) + + def quick_play(self, media_id=None, is_live=False, **kwargs): + """Quick Play""" + self.play_media(media_id, is_live=is_live, **kwargs) diff --git a/pychromecast/quick_play.py b/pychromecast/quick_play.py index d9c40e7ff..3a97b1413 100644 --- a/pychromecast/quick_play.py +++ b/pychromecast/quick_play.py @@ -5,6 +5,8 @@ from .controllers.yleareena import YleAreenaController from .controllers.spotify import SpotifyController from .controllers.bubbleupnp import BubbleUPNPController +from .controllers.bbciplayer import BbcIplayerController +from .controllers.bbcsounds import BbcSoundsController def quick_play(cast, app_name, data): @@ -60,8 +62,17 @@ def quick_play(cast, app_name, data): controller = SpotifyController() elif app_name == "bubbleupnp": controller = BubbleUPNPController() + elif app_name == "bbciplayer": + controller = BbcIplayerController() + elif app_name == "bbcsounds": + controller = BbcSoundsController() else: raise NotImplementedError() cast.register_handler(controller) - controller.quick_play(**data) + + def app_launched_callback(): + """Plays media after chromecast has switched to requested app.""" + controller.quick_play(**data) + + controller.launch(callback_function=app_launched_callback)