npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

@foxxmd/chromecast-client

v1.0.4

Published

A robust Chromecast client written in Typescript

Downloads

182

Readme

Chromecast Client

NPM Version Build Types Patreon

FORK Changes

This is a fork of https://github.com/dantaylor3/chromecast-client with several quality of life improvements and fixes:

A Typescript based Chromecast client

This module is a mid-level library which uses castv2 as a basis for communicating with a Chromecast to provide a fully typed, promise-based api for interacting with the Chromecast. It provides

  • a persistent client that will keep your client connected to the Chromecast
  • controllers for common namespaces used by the Chromecast
  • a Platform implementing basic features of the Chromecast
  • a DefaultMedia application implementation to control Chromecast media
  • a Dashcast application implementation as another application example
  • TODO: a spotify application implementation

This module is intended to be composable and reusable for other use cases outside of those implemented and wouldn't have been possible without both castv2 and nodecastor

Installation

Install with Yarn

yarn add @foxxmd/chromecast-client

Install with NPM

npm install @foxxmd/chromecast-client

Usage

Platform

The platform provides basic controls for the Chromecast like changing the volume and launching applications.

import {createPlatform, Persistentclient} from 'chromecast-client'
import {PersistentClient} from './persistentClient'

const client = new PersistentClient({host: '192.168.1.150'})
await client.connect()
const platform = await createPlatform(client)
const status = await platform.getStatus()
console.log('current status', status)
platform.close()
client.close()
{
    "applications": [
        {
            "appId": "E8C28D3C",
            "appType": "WEB",
            "displayName": "Backdrop",
            "iconUrl": "",
            "isIdleScreen": true,
            "launchedFromCloud": false,
            "namespaces": [
                { "name": "urn:x-cast:com.google.cast.debugoverlay" },
                { "name": "urn:x-cast:com.google.cast.cac" },
                { "name": "urn:x-cast:com.google.cast.sse" },
                { "name": "urn:x-cast:com.google.cast.remotecontrol" }
            ],
            "sessionId": "########-####-####-####-############",
            "statusText": "",
            "transportId": "########-####-####-####-############",
            "universalAppId": "E8C28D3C"
        }
    ],
    "userEq": {},
    "volume": {
        "controlType": "attenuation",
        "level": 1,
        "muted": false,
        "stepInterval": 0.05000000074505806
    }
}

Using a Controller Directly

We're going to use the Media controller to get the current volume of the Chromecast.

import {PersistentClient, ReceiverController} from 'chromecast-client'

const client = new PersistentClient({host: '192.168.1.150'})
await client.connect()

// launch the media app on the Chromecast and join the session (so we can control the CC)
const controller = ReceiverController.createReceiver({client})

// get the volume from the chromecast and unwrap the result
const volume = (await controller.getVolume()).unwrapAndThrow()

// log the volume level since there weren't any errors (or it would've thrown)
console.log(volume)

// dispose of the controller and close the client
controller.dispose()
client.close()
{
    "controlType": "attenuation",
    "level": 1,
    "muted": false,
    "stepInterval": 0.05000000074505806
}

Using an Application

An application is an abstraction on top of one or more controllers that provides a friendly interface to work with. This way, you don't have to work directly with controllers, look up documentation on what commands the protocol supports, etc.

We're going to use the DefaultMediaApp to launch the media app on the chromecast, play some content, control the playback of that content, then stop playing the content

import {DefaultMediaApp, PersistentClient, Result} from 'chromecast-client'

// create a persistent client connected on a given host
const client = new PersistentClient({host: '192.168.1.150'})
await client.connect()

// launch the media app on the Chromecast and join the session (so we can control the CC)
const media = await DefaultMediaApp.launchAndJoin({client}).then(Result.unwrapWithErr)

// if the media app failed to load, log the error
if (!media.isOk) return console.error(media.value)

// queue up a couple of videos
await media.value.queueLoad({
  items: [
    {
      media: {
        contentId: 'http://commondatastorage.googleapis.com/gtv-videos-bucket/big_buck_bunny_1080p.mp4',
        contentType: 'video/mp4',
        streamType: 'BUFFERED',
      },
    },
    {
      media: {
        contentId: 'http://commondatastorage.googleapis.com/gtv-videos-bucket/sample/ElephantsDream.mp4',
        contentType: 'video/mp4',
        streamType: 'BUFFERED',
      },
    },
  ],
})

// after 8 seconds of playing video, forward the video by 60 seconds
setTimeout(() => media.value.seek({relativeTime: 60}), 8000)

// after 20 seconds of playing video, stop playing and close client
setTimeout(() => {
  media.value.stop() // stop playing video
  media.value.dispose() // clean up media app event handlers
  client.close()
}, 20000)

Error Handling

This library uses an implementation of Result to encapsulate either a successful value or an error. This is similar to std::result in Rust, LanguageExt.Common.Result in C#, FSharp.Core.Result in F#, and many more.

There is an awesome Typescript library implementing functional programming concepts called fp-ts which I use extensively. I chose not to use fp-ts in this project to make the library more approachable for those without FP experience.

For those who understand FP concepts, the Result class is roughly equivalent to the Either monad and can be easily integrated with fp-ts. For those who don't understand FP concepts or find it cumbersome in Typescript, simply call Result.unwrapWithErr or Result.unwrapAndThrow depending on how you'd like to handle errors.

Discovering Chromecasts on the Network

This library doesn't support chromecast discovery directly because it is well supported by other libraries. Any library that supports multicast DNS discovery will work (such as bonjour-service, mdns, or multicast-dns).

Example using bonjour-service

import Bonjour from 'bonjour-service'

const bonjour = new Bonjour()
bonjour.find({ type: 'googlecast' }, (service) => {
  console.log(`found chromecast named "${service.name}" at ${service.addresses?.[0]}`)
  bonjour.destroy()
})