catt icon indicating copy to clipboard operation
catt copied to clipboard

Re-play what's playing through a streaming service

Open bjesus opened this issue 4 years ago • 23 comments

Hi! Thanks for catt :)

I'm trying to get my computer to auto play music on my Chromecast every morning, basically putting catt in a crontab. However, I'd like it to use my streaming service, like Spotify or Tidal. I noticed that I can go with my browser to to listen.tidal.com for example, start a playlist, press the Cast button, and then the playlist keeps playing even after I close the browser. I'm assuming then that there's some kind of a request going from my computer to the Chromecast passing the playlist to my Chromecast, and then Chromecast takes it from there. I'm trying to understand what's the URL of that playlist, and how can I replicate that request with catt. catt info shows some information regarding the currently playing track, but it doesn't seem to show any playlist URL like I would have hoped to pass later through catt:

current_time: 108.397868
content_id: 135947568
content_type: audio/flac
duration: 299.96
stream_type: BUFFERED
idle_reason: None
media_session_id: 1
playback_rate: 1
player_state: PLAYING
supported_media_commands: 274447
volume_level: 0.20999999344348907
volume_muted: False
media_custom_data: {'accessToken': 'eyJhbGciOiJIUzI1NiJ9.eyJ0eXBlIjoibzJfYWNjZXNzIiwidWlkIjoxNzIw91c3IiLCJnVmVyIjowLCJzVmVyIjowLCJjaWQiOjIzMTAsImN1ayI6ImViYWY2ZDc2LTEwYjMtNGRjMi05ZTg3LTllOTAwMmViNzRhZCIsImV4cCI6MTU5Mzc1OkIjoiZTVjNjBlNzEtNDI3NS00YmRkLTliMjQtNTc0NjJkNzBjMzFjIn0.JImp9jFaMvBLmaBiAkKP63af1bDJVZKQ087awJWdW5k', 'albumId': 135937567, 'imageResourceId': '90f279c9-2a74-42f7-b047-d2aee30b89ec', 'isActive': False, 'isExplicit': False, 'isLive': False, 'quality': 'LOSSLESS', 'senderType': 'BROWSER', 'playingQuality': 'LOSSLESS'}
media_metadata: {'type': 3, 'metadataType': 3, 'title': 'Arisen My Senses', 'artist': 'Björk, Arca', 'images': [{'url': 'https://resources.tidal.com/images/90f279c9/2a74/42f7/b047/d5aee30b89ec/1280x1280.jpg'}]}
subtitle_tracks: [{'trackId': 1, 'type': 'AUDIO'}]
current_subtitle_tracks: []
last_updated: 2020-07-02 07:14:48.744708
is_active_input: False
is_stand_by: True
app_id: 020DC8A4
display_name: TIDAL
namespaces: ['urn:x-cast:com.google.cast.cac', 'urn:x-cast:com.google.cast.debugoverlay', 'urn:x-cast:com.tidal.cast', 'urn:x-cast:com.google.cast.broadcast', 'urn:x-cast:com.google.cast.media']
session_id: beb34d95-5d4b-4ad4-a266-416210e779f6
transport_id: bebf4d45-2d3b-4ad4-a265-426810e779f6
status_text: Casting: Arisen My Senses
icon_url: https://lh3.googleusercontent.com/bAcozJNRRvlDMqtCyeCHgjRZRRmnLORO5qM00vPQJ_dD2K06HSnTQxNco7iV8BMT1qcoM45vLHHKHRGG

Is this even possible? To replicate a a playlist played by a streaming service using catt?

Thank you very much!

bjesus avatar Jul 02 '20 07:07 bjesus

Hmm, that's an interesting use case, I'm not sure if that's possible off the top of my head, but I will look into it and report back.

skorokithakis avatar Jul 03 '20 19:07 skorokithakis

@bjesus Have you looked into something that uses the Spotify Web API instead?

Cant contribute too much to this, but I did notice that if you do the same with Spotify, it actually gives you the Spotify ID, which if you try to open in say Chrome, it deep links into the Spotify app

current_time: 209.661
content_id: spotify:track:3s4hxwmmzhCT0Z4g94cY3N
content_type: application/x-spotify.track
duration: 403.2
stream_type: BUFFERED
idle_reason: None
media_session_id: 1
playback_rate: 1
player_state: PLAYING
supported_media_commands: 514511
volume_level: 1
volume_muted: False
media_custom_data: {}
media_metadata: {'metadataType': 3, 'title': 'Warp Drive', 'songName': 'Warp Drive', 'artist': 'Tonebox', 'albumName': 'Nocturn', 'images': [{'url': 'https://i.scdn.co/image/ab67616d00001e0244d66e01a3bd0c5d478a0aaa', 'height': 300, 'width': 300}, {'url': 'https://i.scdn.co/image/ab67616d0000485144d66e01a3bd0c5d478a0aaa', 'height': 64, 'width': 64}, {'url': 'https://i.scdn.co/image/ab67616d0000b27344d66e01a3bd0c5d478a0aaa', 'height': 640, 'width': 640}]}
subtitle_tracks: []
current_subtitle_tracks: []
last_updated: 2020-08-11 17:39:06.355814
is_active_input: False
is_stand_by: True
app_id: CC32E753
display_name: Spotify
namespaces: ['urn:x-cast:com.google.cast.cac', 'urn:x-cast:com.google.cast.debugoverlay', 'urn:x-cast:com.spotify.chromecast.secure.v1', 'urn:x-cast:com.google.cast.test', 'urn:x-cast:com.google.cast.broadcast', 'urn:x-cast:com.google.cast.media']
session_id: a13ebc58-3cad-4e2d-aa17-c248cfc48cdc
transport_id: a13ebc58-3cad-4e2d-aa17-c248cfc48cdc
status_text: Casting: Warp Drive
icon_url: https://lh3.googleusercontent.com/HOX9yqNu6y87Chb1lHYqhKVTQW43oFAFFe2ojx94yCLh0yMzgygTrM0RweAexApRWqq6UahgrWYimVgK

greghesp avatar Aug 11 '20 17:08 greghesp

Thanks @greghesp ! I'm not sure how to use this though... Did you manage to get Catt to start casting the Spotify playlist using this track id? or am I missing something? I'm trying to programmatically start casting a playlist.

bjesus avatar Aug 11 '20 17:08 bjesus

Something tells me you can't. The way casting works for services like Spotify, is it sends the API connection to the casting device, which makes the connection to the service's server.

This is why it will continue to play when you close the browser etc

greghesp avatar Aug 11 '20 18:08 greghesp

What technically prevents Catt from sending the API connection to the casting device then? I mean, how is that different from casting a youtube video with Catt?

bjesus avatar Aug 11 '20 18:08 bjesus

So I've tried a couple of things - first I tried using wireshark to see what kind of requests the browser sends to my Google Home when it makes it start playing, but that failed because it seems to be encrypted and I couldn't decrypt it. Then I used https://github.com/tamland/python-tidal/ to get the media URL for a track and send it through catt cast - and that worked, so I can basically write a script that takes a playlist, gets the first song media URL and plays it with catt cast. Problem is, I can't make this into a playlist, because catt add only works with youtubes for some reason?

I can easily generate a local file listing all media URLs for all tracks in the playlist, but I wonder how can I cast this whole file once with catt, and not just one song at a time. I could theoretically write a script that runs catt status all the time to see when the track is done, and then submit the next one. It's just a little... inelegant?

bjesus avatar Aug 15 '20 11:08 bjesus

I'm not entirely up to speed with how specific Chromecast apps work, but I think it would depend on the app. IIRC we only have support for the YouTube app at the moment, if support for the Spotify or Tidal apps are added, we could potentially support playing other types, but I'm not sure what that would entail.

skorokithakis avatar Aug 15 '20 11:08 skorokithakis

Just to clarify, I was trying to Wireshark the chrome browser when casting from the tidal website, not some native app.

However, I'm not suggesting adding any Spotify/Tidal specific support. I can already gather a list of URLs that catt can successfully cast. I just currently can only feed cast one song at a time, and then constantly check the status so I'll know when to cast the next one. What I'm wondering is why can't I queue any other URL except YouTube when using catt add. If I could add the URLs, or cast a file that is a list of URLs, my problem would be pretty much solved.

bjesus avatar Aug 15 '20 12:08 bjesus

AFAIK the generic app doesn't support that, so catt would have to stay running and watch the status constantly so it can switch to the next track. It's not something I think would be a good addition currently, but you should be able to fairly easily implement it through the catt API.

skorokithakis avatar Aug 16 '20 09:08 skorokithakis

Updating that I managed to decrypt the data sent using the instructions on the PyChromecast project (https://github.com/home-assistant-libs/pychromecast/issues/403) and added a pull request that allows queuing media (https://github.com/home-assistant-libs/pychromecast/pull/404). Hopefully once this is merged, and once Catt moved to PyChromecast 7 again, catt add can use it to queue any URL.

Then, one could generate a playlist using whatever API they want (eg. python-tidal) and send it to their Chromecast using catt add.

bjesus avatar Aug 16 '20 09:08 bjesus

Ahh, that's great news, thanks! That's definitely something we could add.

skorokithakis avatar Aug 16 '20 09:08 skorokithakis

Do you think we can add catt next and catt prev to go along media_controller.queue_next() and media_controller.queue_prev()? Let me know if you want me to open a new issue for this (I assume it's a pretty minor addition?).

https://github.com/home-assistant-libs/pychromecast/blob/0d6c4565f26649d8904cbc8f70d5db7220cd2d95/pychromecast/controllers/media.py#L430

bjesus avatar Aug 16 '20 11:08 bjesus

Yes, certainly, though I'm not sure whether it should be standalone or as part of a "queue management" feature (which should include "add").

skorokithakis avatar Aug 16 '20 11:08 skorokithakis

The "enqueue" support was now released with Pychromecast 7.3.0. Should I submit a pull request for "add", "next" and "prev"? It seems like the whole Pychromecast 7 support is on hold for some reason?

bjesus avatar Sep 05 '20 14:09 bjesus

It seems like the whole Pychromecast 7 support is on hold for some reason?

I plan on finishing up that PR next weekend.

The "enqueue" support was now released with Pychromecast 7.3.0

Great work! Is this working with Tidal as is?

theychx avatar Sep 05 '20 15:09 theychx

Awesome! The enqueue isn't specific to Tidal, you can send it any media you want exactly like the play_media() was working before. It's just play_media(URL, enqueue=True), which makes the Chromecast not play the song immediately but add it to the queue. However, I use it successfully with Tidal by getting the media URLs using python-tidal and feeding them to enqueue. Great morning alarm clock ;-)

bjesus avatar Sep 05 '20 15:09 bjesus

The enqueue isn't specific to Tidal

Indeed, but as I recall, queuing functionality is only part of the base receiver, from receiver API V3+. And given that Google still supports the V2 API, there's bound to be a lot of cc apps, that doesn't support V3 queuing. If you have some time to test which cc apps respond to these cmds, then that would be amazing!

theychx avatar Sep 05 '20 15:09 theychx

I'm sorry but I'm not sure what you mean... I honestly don't know much about the spec versions. All I know is that Pychromecast's play_media() method takes a URL as an input. The way I'm playing music from Tidal has nothing to do with the Tidal app (or any other app). I just query Tidal using python-tidal and get a URL. I can use this URL with wget, mplayer, vlc, whatever. and I can feed it to play_media, with or without enqueue=True.

bjesus avatar Sep 05 '20 16:09 bjesus

Ah, I see. The point about some/most cc apps not supporting this kind of queuing is still valid though. So your new functionality should be implemented as a new mixin in controllers.py. And then as a start, DefaultCastController could use that. That way, we're not adding functionality that fails silently with many cc apps.

theychx avatar Sep 05 '20 16:09 theychx

Hi! now that Pychromecast 7.5 is merged (🥳🥳🥳 @theychx), can we add support for catt next and catt prev? Should I send a PR?

bjesus avatar Oct 12 '20 08:10 bjesus

Should I send a PR?

Please do.

theychx avatar Oct 12 '20 08:10 theychx

Did this ever get added? @bjesus @theychx

fivestones avatar Jan 20 '23 03:01 fivestones

Did this ever get added?

Nope

theychx avatar Jan 20 '23 08:01 theychx