Playback of internet radio stream

First time plug-in dev, need help
Hi all,

After pulling my hair out (there is still enough left), I thought I better ask for help. I am trying something really simple for now: A channel plug-in that allows playback of Radio Paradise (radioparadise.com) streams. Here is what I have:


<br />
<br />
@handler('/music/InternetRadio', 'Internet Radio')<br />
def MainMenu():<br />
    oc = ObjectContainer()<br />
    playlist = HTTP.Request('http://www.radioparadise.com/musiclinks/rp_128.m3u').content<br />
    i = 0<br />
    for track in playlist.splitlines():<br />
        oc.add(TrackObject(key=track, rating_key=i, title=track))<br />
        i += 1<br />
    <br />
    playlist = HTTP.Request('http://www.radioparadise.com/musiclinks/rp_128aac.m3u').content<br />
    for track in playlist.splitlines():<br />
        oc.add(TrackObject(key=track, rating_key=i, title=track))<br />
        i += 1<br />
    oc.add(TrackObject(key='http://192.168.178.39:8080/', rating_key=i, title='VLC stream'))<br />
    return oc<br />





That's the whole plug-in for now. Problem is it doesn't work properly and I cannot figure out what's wrong. Here is what's working:
[list=1][*]Fetching an m3u playlist (containing mp3 streams) and list each stream as a separate item[*]Fetching an m3u playlist (containing AAC streams) and list each stream as a separate item, appended to the above[*]Add a local mp3 stream (generated by VLC) added as a separate item, appended to the above[/list]
PMC, PHT (both Win7), Plex/Web as well as Plex/iOS show the list of streams. Good.

Unfortunately except for Plex/iOS, choosing any of these streams for playback doesn't work, nothing seems to happen. On iOS however, the mp3 streams play back nicely, the AAC streams don't. If there is a hint anywhere in any of the logs, I am just not finding it.:blink:

So my questions:
[list=1][*]What am I doing wrong that makes playback fail for all the clients except the iOS one?[*]Why aren't the AAC tracks playing back on iOS (or any of the other clients for that matter)?[*]Is there a way to just call the m3u file so Plex just plays back the first stream that works and if one fails just goes to the next?[*]Is there a better way to deal with the "rating_key" property of the TrackObject, ideally so I don't have to take care of it at all?[/list]
Any advice is much appreciated!

Thanks & Best Regards,
Gundy

This is strange. Just updated to PMS 0.9.7.12 today and Plex/Web is now playing back the mp3 streams (not the AAC ones). PMC is still not playing back anything and PHT even refuses to launch the Plugin, just sits there “waiting”. Just thought I mention that.

Help, help, help… I just don’t know where to look.



Could it be a problem with my Info.plist? Couldn’t find much info on it.


<br />
<br />
<?xml version="1.0" encoding="UTF-8"?><br />
<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"><br />
<plist version="1.0"><br />
<dict><br />
	<key>CFBundleIdentifier</key><br />
	<string>com.plexapp.plugins.InternetRadio</string><br />
	<br />
	<key>PlexFrameworkVersion</key><br />
	<string>2</string><br />
	<br />
	<key>PlexPluginClass</key><br />
	<string>Content</string><br />
	<br />
	<key>PlexClientPlatforms</key><br />
	<string>*</string><br />
	<br />
	<key>PlexAudioCodec</key><br />
	<array><br />
		<string>AAC</string><br />
	</array><br />
</dict><br />
</plist><br />



Wish I could help … but I know nothing of the audio channel end of things :frowning:

Does no one know how to properly use the Plex Framework to stream audio? I thought this was a good way to dip my toes into plug-in development.

Been playing a little more, but I am still unable to make things work in a reliable fashion:



<br />
<br />
@handler('/music/InternetRadio', 'Internet Radio')<br />
<br />
def MainMenu():<br />
    oc = ObjectContainer()<br />
<br />
    playlist = HTTP.Request('http://www.radioparadise.com/musiclinks/rp_128.m3u').content<br />
    for track in playlist.splitlines():<br />
        oc.add(TrackObject(<br />
            key = track,<br />
            rating_key = track,<br />
            title = track,<br />
            items = [MediaObject(<br />
                parts = [PartObject(key = track)],<br />
                container = Container.MP3,<br />
                audio_codec = AudioCodec.MP3<br />
            )]<br />
        ))<br />
  <br />
    playlist = HTTP.Request('http://www.radioparadise.com/musiclinks/rp_128aac.m3u').content<br />
    for track in playlist.splitlines():<br />
        oc.add(TrackObject(<br />
            key = track,<br />
            rating_key = track,<br />
            title = track,<br />
            items = [MediaObject(<br />
                parts = [PartObject(key = track)],<br />
                container = 'aac', # also tried Container.MP4, no difference<br />
                audio_codec = AudioCodec.AAC<br />
            )]<br />
        ))<br />
<br />
    track = 'http://stream-tx1.radioparadise.com:9000/rp_192.ogg'<br />
    oc.add(TrackObject(<br />
        key = track,<br />
        rating_key = track,<br />
        title = track,<br />
        items = [MediaObject(<br />
            parts = [PartObject(key = track)],<br />
            container = Container.OGG,<br />
            audio_codec = AudioCodec.VORBIS<br />
        )]<br />
    ))<br />
<br />
    return oc<br />





With the code above I am using three different stream formats. The first is MP3, second AAC and third is OGG-VORBIS. Here is how the different clients I was able to test behaved:
**Plex/Web**
MP3 plays fine
AAC nothing happens
OGG nothing happens

**Plex/iOS**
MP3 plays fine
AAC nothing happens
OGG nothing happens

**Plex/MediaCenter**
MP3 nothing happens
AAC nothing happens
OGG nothing happens

**Plex/HomeTheater**
MP3 plays fine
AAC client crashes
OGG plays fine


Not quite sure what is happening here, but I get the feeling that as soon as PMS figures out it needs to transcode playback won't work.

Hello Gundy,

The ‘key’ attribute should not return a URL, but a metadata object. This is usually doen through a URL Service, but can also be done without one. An example can be found in the Tagesschau channel: https://github.com/plexinc-plugins/Tagesschau.bundle/blob/master/Contents/Code/init.py or the Plex Podcast channel: https://github.com/plexinc-plugins/PlexPodcast.bundle/blob/master/Contents/Code/init.py

Hi Sander,



Thanks for your reply, much appreciated. I realize that this might sound stupid, but would you care to explain why this Callback is being used in the way it is? Not that I really know what this does, but it just looks like in both of your examples given it calls the function that it is being called from itself and somehow I would expect this to end up in an endless loop. Why doesn’t this happen? It makes my head spin…



How would you write that without the Callback, could you give an example should that be possible?



Best Regards,

Gundy

I have just copied the whole TrackObject generation from Sander’s examples. This is what I got right now:


<br />
@handler('/music/InternetRadio', 'Internet Radio')<br />
def MainMenu():<br />
    oc = ObjectContainer()<br />
<br />
    playlist = HTTP.Request('http://www.radioparadise.com/musiclinks/rp_128.m3u').content<br />
    for track in playlist.splitlines():<br />
        oc.add(CreateTrackObject(url=track, title=track, container=Container.MP3, audio_codec=AudioCodec.MP3))<br />
<br />
    playlist = HTTP.Request('http://www.radioparadise.com/musiclinks/rp_128aac.m3u').content<br />
    for track in playlist.splitlines():<br />
        oc.add(CreateTrackObject(url=track, title=track, container=Container.MP4, audio_codec=AudioCodec.AAC))<br />
<br />
    track = 'http://stream-tx1.radioparadise.com:9000/rp_192.ogg'<br />
    oc.add(CreateTrackObject(url=track, title=track, container=Container.OGG, audio_codec=AudioCodec.VORBIS))<br />
<br />
    return oc<br />
<br />
def CreateTrackObject(url, title, container, audio_codec, include_container=False):<br />
	track_object = TrackObject(<br />
		key = Callback(CreateTrackObject, url=url, title=title, container=container, audio_codec=audio_codec, include_container=True),<br />
		rating_key = url,<br />
		title = title,<br />
		items = [<br />
			MediaObject(<br />
				parts = [<br />
					PartObject(key=url)<br />
				],<br />
				container = container,<br />
				audio_codec = audio_codec,<br />
				audio_channels = 2<br />
			)<br />
		]<br />
	)<br />
<br />
	if include_container:<br />
		return ObjectContainer(objects=[track_object])<br />
	else:<br />
		return track_object


So, minor improvements on the behavior:
**Plex/Web**
MP3 plays fine
AAC nothing happens
OGG nothing happens

**Plex/iOS**
MP3 plays fine
AAC nothing happens
OGG nothing happens**
**
**Plex/HomeTheater **(on same Win7 PC as PMS)
MP3 plays fine
AAC plays fine (used to crash before)
OGG plays fine

Ignoring Plex/MediaCenter results for now. Does anyone have a hunch why AAC and OGG won't play on Plex/Web and Plex/iOS? I am still assuming that something goes sour when PMS is trying to transcode the audio stream, although I don't quite understand why PMS would want to transcode for the AAC stream, thought that was one of the few codecs iOS supports.

Still not understanding the whole Callback business, or why the TrackObject has to have a meta data object passed to it in the "key" property when it is a meta data object itself, containing the same information. I am not trying to criticize the method, I would just like to understand it. Any hints on that? :rolleyes:

Hmm, on my quest trying not to use the Callback function for now, I have come up with the following:


@handler('/music/InternetRadio', 'Internet Radio')<br />
def MainMenu():<br />
    oc = ObjectContainer()<br />
    <br />
    playlist = HTTP.Request('http://www.radioparadise.com/musiclinks/rp_128aac.m3u').content<br />
    for track in playlist.splitlines():<br />
        oc.add(TrackObject(<br />
            key = ObjectContainer(objects=[TrackObject()]),<br />
            rating_key = track,<br />
            title = track,<br />
            items = [MediaObject(<br />
                parts = [PartObject(key = track)],<br />
                container = Container.MP4,<br />
                audio_codec = AudioCodec.AAC,<br />
                audio_channels = 2<br />
            )]<br />
        ))<br />
<br />
    return oc



This seems to give me the same result with regards to the AAC stream as the previous example, but I am afraid that I might be making a mess of it... Please let me know.

Just to rephrase, here is what I don’t understand:

To your ObjectContainer you add a TrackObject. That TrackObject is according to the API documentation a metadata object and it contains all sort of information like title, description, duration, etc. It’s item property contains a MediaObject that points to playable media content with some description of container and codec formats. Lovely so far. But now you add a whole TrackObject (and in the examples given one that contains the exact same information, including the MediaObject) to its “key” property. Why? Is it that some properties from the main TrackObject are being used and then some others from the one contained in it’s “key”?



And then the other mystery remains still: How do I get an AAC or OGG audio stream to properly transcode for playback on Plex/iOS or Plex/Web clients?



Any help, tips, hints, pointers are much appreciated!

Okay, I have kept playing around with this a bit more, but I am not getting anywhere. At least I have pushed what I currently have to Git:

https://github.com/nsgundy/InternetRadio.bundle

My list of issues:

  • I can read meta data from Radio Paradise just fine when the plugin is first loaded and display it properly in the client. When the station changes its track, title, artist and album information gets automatically updated (tested with PHT, so its either PHT or PMS that does this). But how can I "hook" into that track change event in order to update both the thumb and the art?
  • The AAC version of the stream (first menu item) only plays back fine on PHT. Both Plex/Web and Plex/iOS won't play this (Plex/Web won't show an error message, Plex/iOS occasionally reports "Unable to play track").
  • As it stands Plex/Web is not playing back the MP3 stream either, it did at some point, not sure if it was something I changed or if it was any of the recent Plex/Web or PMS updates.
  • When I use Plex/iOS to send any of the streams to PHT, nothing happens in PHT. Tracks that are currently playing in PHT keep playing.
  • I was planning on adding all the backup streams included in the m3u playlists that I pull for both the AAC and MP3 streams in a way that allows the Plex client to automatically switch to the next stream if one of them fails. When I try to add multiple PartObjects to the same MediaObject, I cannot get the streams to play at all. When I try to add multiple MediaObjects instead (each with a single PartObject), playback works as good/bad as before, but if I block the currently playing stream from being accessed there is no automatic fall-back within the same TrackObject. Instead PHT moves on to the next TrackObject, so in my case switches from the AAC stream to the MP3 stream. So do I have to add one TrackObject per stream in the m3u? This would just clutter the interface, would like to avoid that.

Help? Have I just not spotted the right stuff in the documentation or is the Plex frame-work ill-equipped to work with traditional internet radio? I thought this would be a good starting point to enter Plex channel development as surely internet radio is a lot simpler than anything video related, right?

I can read meta data from Radio Paradise just fine when the plugin is first loaded and display it properly in the client. When the station changes its track, title, artist and album information gets automatically updated (tested with PHT, so its either PHT or PMS that does this). But how can I "hook" into that track change event in order to update both the thumb and the art?

At present, there is not any sort of track change (or other) "hooks" available to channels  :(

The AAC version of the stream (first menu item) only plays back fine on PHT. Both Plex/Web and Plex/iOS won't play this (Plex/Web won't show an error message, Plex/iOS occasionally reports "Unable to play track").

My first inclination is that the container, protocol, and/or extension is not set correctly for the media object so that the player is expecting a different format of media than it receives causing the error. *Just a guess*

As it stands Plex/Web is not playing back the MP3 stream either, it did at some point, not sure if it was something I changed or if it was any of the recent Plex/Web or PMS updates.

Possibly related to the previous issue?

When I use Plex/iOS to send any of the streams to PHT, nothing happens in PHT. Tracks that are currently playing in PHT keep playing.

I believe that the abililty to send audio/video channel media from one instance of Plex to another requires the use of URL Services, which is a whole nother ball of wax. In my experience, internet radio and url services are not exactly a match made in heaven.

I was planning on adding all the backup streams included in the m3u playlists that I pull for both the AAC and MP3 streams in a way that allows the Plex client to automatically switch to the next stream if one of them fails. When I try to add multiple PartObjects to the same MediaObject, I cannot get the streams to play at all. When I try to add multiple MediaObjects instead (each with a single PartObject), playback works as good/bad as before, but if I block the currently playing stream from being accessed there is no automatic fall-back within the same TrackObject. Instead PHT moves on to the next TrackObject, so in my case switches from the AAC stream to the MP3 stream. So do I have to add one TrackObject per stream in the m3u? This would just clutter the interface, would like to avoid that.

Have you tried just passing the m3u playlist directly to the Plex player? If that works, it would take a lot of the error handling off your plate and let the player handle it. I know that can work for video m3u8 playlists. Not sure about audio ones though. Since we can't really update the now-playing info, it's worth a shot IMO. In general, part obects are used for stacked media, like if a single song were cut into 3 or 4 segments for some reason. Each song segment should be added to a PartObject all contained within the same MediaObject for one TrackObject. Multiple media objects are used when there are multiple versions available. So, you could have one MediaObject for MP3, one for AAC, and one for OGG, each with their own appropriate PartObjects, all contained within one TrackObject.

At some point, I believe the plugin framework will have better support for radio-type streams. Until then, take a look at the ShoutCast plugin for reference. The Pandora plugin may provide a little perspective as well.

Hi Midke,

Thanks very much for your detailed explanation. Part/Media/TrackObject relationship is now a lot clearer. So if I understand correctly, using multiple MediaObjects with the same container + codec combination will not give me "automatic fallback" to an alternative stream and it might still fail playback if it happened to pick an unavailable stream. Any way you can think of for the channel plugin to check if a stream is available?

My first inclination is that the container, protocol, and/or extension is not set correctly for the media object so that the player is expecting a different format of media than it receives causing the error. *Just a guess*

I am having a hard time figuring out the container type of those streams. How do I do that for a live-stream? When I give VLC http://www.radioparadise.com/m3u/aac-128.m3u to play and look a its Codecdetails dialogue it lists "Codec: MPEG AAC Audio (mp4a), but no information on its container format. Is it possible that there is no "container"?

Have you tried just passing the m3u playlist directly to the Plex player?

That's what I tried in the beginning and it did't work, but at that time I had a lot of other issues, not properly constructing the TrackObject. Will give it another try.

I will also have another look at the ShoutCast and Pandora plugins.

Hi Gundy,

Has this effort been abandoned?  Have you seen the Radio Paradise channel on Roku?  It would be great if that channel could be ported to Plex with the HD screensavers and ratings info.

Regards,

Merrick

Hi Merrick,

Pretty much, at least for now. I really wanted to do this, but the channel framework documentation was lacking heavily (at least at the time) and even with help from the community, my attempts were lacking a lot of what I wanted to achieve. Namely the ability to update track artwork, automatically fall back to different stream addresses if the primary was down, playback of AAC streams on all players and the ability to remotely send the channel to players for playback (i.e. Plex/Web sending to Plex/HT). I am sure some of these goals could be achieved by someone more able / devoted / with time on their hands.

Thanks & Best Regards,

Gundy

Damn, I was looking for implementing the Paradise Radio too and I have bumped into this post. I just wanted to ask if anything has changed in the meantime? I'd really appreciate a response.

Are there any other working internet radio streaming plugins that we could look upon in order to make this one. Will take a look at the code of yours commited to the git repository from one o your earlier posts.

Hi Kaosmonk,

No, haven't done any more work on this. There are some radio channels around, even some recent ones, but I haven't had a look to see if any of them was similar to what RP would require.

The current code doesn't seem to work anymore either, but I am confident that with little tinkering one will at the minimum produce something that at least plays back on PHT.

Framework documentation sadly hadn't been improved, at least not in any way I could detect.

Best Regards,

Gundy

Hi there,

What about deconstructing the Shoutcast plugin and customising it?  I'd be happy to help as I want this solution as well.   

The whole debacle of streaming radio and people pointing to ancient UNRESOLVED forum posts just reminds of this epic XKCD comic:

wisdom_of_the_ancients.png

Hi Kaosmonk,

No, haven't done any more work on this. There are some radio channels around, even some recent ones, but I haven't had a look to see if any of them was similar to what RP would require.

The current code doesn't seem to work anymore either, but I am confident that with little tinkering one will at the minimum produce something that at least plays back on PHT.

Framework documentation sadly hadn't been improved, at least not in any way I could detect.

Best Regards,

Gundy

Hi!

I've submitted a pull request for this on your Github account that will fix playback.

Changes made:

  • The playlists are not actually playlists, just different URL:s to even out server load, so just pick the first URL in each variant(MP3 and AAC)
  • Removed the metadata_dict and replaced with single parameters instead since the framework seems a little bit buggy regarding this
  • Utilize the commented out section again + add two new routines with route decorators(this just to make it work on PHT, i.e. PHT needs to have extension .mp3 or .aac in order to properly determine correct demuxer)
  • ...(some other stuff I forgot now)

Currently, the art and thumb sticks to the ones that were when you started playback. However, PHT is smart enough to update the Artist and Song name when a new track starts playing(extracted from Shoutcast stream info).

Should now work on PHT(OS X) and Plex/Web(Safari on OS X) at least.