I’m trying to develop a channel for my own personal use. When I click on my channel, Plex gives me an error saying “This channel is not responding.”
Checking the server logs, I see:
Jul 01, 2016 10:38:08.305 [0x7fac047fc700] DEBUG - Plug-in com.plexapp.plugins.soccermatches has been used 5 times.
Jul 01, 2016 10:38:08.328 [0x7fac047fc700] DEBUG - [com.plexapp.plugins.soccermatches] Sending command over HTTP (GET): /video/soccermatches
Jul 01, 2016 10:38:08.329 [0x7fac047fc700] DEBUG - HTTP requesting GET http://127.0.0.1:37456/video/soccermatches
Jul 01, 2016 10:38:08.390 [0x7fac047fc700] DEBUG - HTTP success requesting GET http://127.0.0.1:37456/video/soccermatches
Jul 01, 2016 10:38:08.391 [0x7fac047fc700] DEBUG - [com.plexapp.plugins.soccermatches] HTTP reply status 500, with 2301 bytes of content.
Jul 01, 2016 10:38:08.396 [0x7fac163fe700] DEBUG - Completed: [10.0.0.6:61928] 500 GET /video/soccermatches (4 live) TLS GZIP 94ms 1264 bytes (pipelined: 16)
Here’s my _ init _.py:
#####################################################################
# Plugin to package downloaded soccer games for easy streaming via
# Plex.
TITLE = 'Soccer Matches'
RSS_FEED = 'http://<WAN IP>/soccer/matches.rss'
ART = 'art-default.jpg'
ICON = 'icon-default.png'
ICON_SEARCH = 'icon-search.png'
#####################################################################
# This (optional) function is initially called by the PMS framework to
# initialize the plug-in. This includes setting up the Plug-in static
# instance along with the displayed artwork.
def Start(): # Initialize the plug-in
Plugin.AddViewGroup("Details", viewMode="InfoList", mediaType="items")
Plugin.AddViewGroup("List", viewMode="List", mediaType="items")
# Setup the default attributes for the ObjectContainer
ObjectContainer.title1 = TITLE
ObjectContainer.view_group = 'List'
ObjectContainer.art = R(ART)
# Setup the default attributes for the other objects
DirectoryObject.thumb = R(ICON)
DirectoryObject.art = R(ART)
VideoClipObject.thumb = R(ICON)
VideoClipObject.art = R(ART)
# Plugin's unique identifier
# Type: Video
# Name: soccermatches
@handler('/video/soccermatches', TITLE)
def MainMenu():
oc = ObjectContainer()
for match in XML.ElementFromURL(RSS_FEED).xpath('//match'):
competition = match.xpath('./competition')[0].text
competitor1 = match.xpath('./competitors')[0].text
competitor2 = match.xpath('./competitors')[1].text
title = competition + " : " + competitor1 + " vs " + competitor2
url = match.xpath('./link')[0].text
date = match.xpath('./date')[0].text
date = Datetime.ParseDate(date)
summary = match.xpath('./description')[0].text
data = HTTP.Request(match.xpath('./thumb')[0].get('url'))
thumb = DataObject(data, 'image/jpeg')
oc.add(VideoClipObject(
url = url,
title = title,
summary = summary,
thumb = thumb,
originally_available_at = date
))
return oc
thumb = DataObject(data, 'image/jpeg')
DataObject is not a thing withing Plex Framework. I assume you’re trying to download and save the image as a binary file? The thumb/art attribute of VideoClipObject can accept either a direct URL to the image or a file name within the channel’s Resources directory to load.
Examples:
FALLBACK = "icon-fallback.png"
#######################
thumb_url = "http://i1.wp.com/vanillicon.com/3c28bcfed9b40ac6bd578445e0be988f_200.png"
## Example directly setting thumb URL
oc.add(VideoClipObject(
url = url,
title = title,
summary = summary,
thumb = thumb_url,
originally_available_at = date
))
## Example loading thumb icon from channel's Resources directory, (Note: R() is the same as Resource.Load())
oc.add(VideoClipObject(
url = url,
title = title,
summary = summary,
thumb = R(FALLBACK),
originally_available_at = date
))
## Example usage of Resource.ContentsOfURLWithFallback(url=, fallback=)
## url can take either single URL or list of URLs to try before resorting to loading the fallback icon located within the channel's Resources directory
oc.add(VideoClipObject(
url = url,
title = title,
summary = summary,
thumb = Resource.ContentsOfURLWithFallback(thumb_url, FALLBACK),
originally_available_at = date
))
Also this line data = HTTP.Request(match.xpath('./thumb')[0].get('url')) is returning a Request object that can only return the following:
data = HTTP.Request("http://example.com")
# get request as string
data.content
# get response headers from request
data.headers
# load request in Plex Framework background in case the request has yet to be loaded
data.load()
For further debugging, I suggest you watch your channel’s log files to catch errors as they are produced.
Thank you so much for your quick response Twoure! I corrected the errors you pointed out, as well as some others in my .rss file.
It’s great to know about the logs just for my plugin. Very helpful!
In them, I am getting this error:
DEBUG (services:603) - No service found for URL 'http://<WAN IP>/<path-to-file>/video.mp4'
The file exists, if I enter the address in my web browser it plays. I get this error regardless of video format.
@tomasagray said:
In them, I am getting this error:
DEBUG (services:603) - No service found for URL 'http://<WAN IP>/<path-to-file>/video.mp4'
Unless you created Service Code to go with your channel (or if Plex already has created Service code for your URL), then the VideoClipObject will now work. You can either create the appropriate Service Code to go with your channel, or do something similar to what I did when creating the GreekTV.bundle. github.com/Twoure/GreekTV.bundle/blob/master/Contents/Code/__init__.py
Regardless of how you handle the video URLs, you must have some code that tells Plex how to deal with the video URL. Look to the Services.bundle for examples of how to write service code.
Further reading/info on channel service code (and channel development) can be found within the PlexPlug-inFramework.pdf document (link), and the Plex Missing From Dev Docs document (link).
OK, making progress.
I more or less copied your code from GreekTV. I can now click on my channel, have it list entries and click on those entries. However, when I try to play the file, I get a blank black box telling me the HTML5 player cannot be found (web client). Cannot play at all via Kodi. I can play .mp4 files via the iOS client.
Here’s the new complete _ _ init _ _.py:
#####################################################################
# Plugin to package downloaded soccer games for easy streaming via
# Plex.
PREFIX = '/video/soccermatches'
TITLE = 'Soccer Matches'
ART = 'art-default.jpg'
ICON = 'icon-default.png'
RSS_FEED = 'http://<WAN IP>/soccer/matches.rss'
#####################################################################
# This (optional) function is initially called by the PMS framework to
# initialize the plug-in. This includes setting up the Plug-in static
# instance along with the displayed artwork.
def Start(): # Initialize the plug-in
# Setup the default attributes for the ObjectContainer
ObjectContainer.title1 = TITLE
ObjectContainer.art = R(ART)
# Setup the default attributes for the other objects
DirectoryObject.thumb = R(ICON)
DirectoryObject.art = R(ART)
VideoClipObject.thumb = R(ICON)
VideoClipObject.art = R(ART)
# Plugin's unique identifier
# Type: Video
# Name: soccermatches
@handler(PREFIX, TITLE)
def MainMenu():
oc = ObjectContainer()
for match in XML.ElementFromURL(RSS_FEED).xpath('//match'):
competition = match.xpath('./competition')[0].text
competitor1 = match.xpath('./competitors/competitor')[0].text
competitor2 = match.xpath('./competitors/competitor')[1].text
title = competition + ": " + competitor1 + " vs. " + competitor2
Log('-------------------------------')
Log(title)
Log('-------------------------------')
url = match.xpath('./link')[0].text
date = match.xpath('./date')[0].text
summary = match.xpath('./description')[0].text
thumb_url = match.xpath('./thumb')[0].get('url')
oc.add(CreateVideoClipObject(
title = title,
thumb = thumb_url,
url = url,
summary = summary,
date = date
))
return oc
###########################################################################################
@route(PREFIX + '/createvideoclipobject', include_container=bool)
def CreateVideoClipObject(title, thumb, url, summary, date, include_container=False, *args, **kwargs):
date = Datetime.ParseDate(date)
video_object = VideoClipObject(
key=Callback(CreateVideoClipObject, title=title, thumb=thumb, url=url, summary=summary, date=date, include_container=True),
rating_key=url,
title=title,
thumb=thumb,
content_rating='NR',
url=url,
summary=summary,
originally_available_at=date,
items=GetMediaObject(url)
)
if include_container:
return ObjectContainer(objects=[video_object])
else:
return video_object
##########################################################################################
def GetMediaObject(url):
mo = [
MediaObject(
parts=[
PartObject(
key=HTTPLiveStreamURL(Callback(PlayVideo, url=url))
)
]
)
]
return mo
#########################################################################################
@indirect
@route(PREFIX + '/playvideo.m3u8')
def PlayVideo(url):
return IndirectResponse(VideoClipObject, key=url)
GetMediaObject(url) is setup for only m3u8 URLs in the code above. You need to change this for other stream types. Also you should probably change def PlayVideo(url): to def PlayVideo(url, **kwargs): to account for variations in Plex clients.
@Twoure said:
GetMediaObject(url) is setup for only m3u8 URLs in the code above. You need to change this for other stream types. Also you should probably change def PlayVideo(url): to def PlayVideo(url, **kwargs): to account for variations in Plex clients.
I got it to work!!! Huzzah! It ended up being this line:
date = Datetime.ParseDate(date)
So I just decided to drop the originally_available_at bit. It was complaining about some kind of typecast problem. I also added the **kwargs and commented out:
@route(PREFIX + '/playvideo.m3u8')
Now I have 2 final questions:
- How can I specify artwork (background art) for each video?
- Is it possible to give PMS a list of URLs and have it treat them as a single file (i.e., play them one after the other)?
@tomasagray said:
Sorry @Twoure, I’m a bit mystified as to what you mean. I added **kwargs and commented out the @route(PREFIX + '/playvideo.m3u8')
line which allowed me to play .mp4 files on some clients (though not Kodi, which is my ultimate aim).
Try replacing @route(PREFIX + '/playvideo.m3u8') with @route(PREFIX + '/playvideo'). Then you need to change your GetMediaObject(url) to something like the following:
def GetMediaObject(url):
return [
MediaObject(
video_resolution='sd',
audio_channels=2,
optimized_for_streaming=False,
parts=[
PartObject(key=Callback(PlayVideo, url=url))
]
)
]
The above MediaObject provides no info about the file type or what codecs to use to play the file. Because this information is omitted, Plex will transcode the stream to be compatible with the client accessing the media. If you want direct play supported, then you will have to specify the container, video_codec, and audio_codec information within the MediaObject. The container, video_codec, and audio_codec values must match the videos attributes, otherwise your Plex Client cannot auto-correct this info and will fail to play the video.
You may also want to review this URL Service For Dummies document. It may be helpful in explaining how to build the media objects for your video links, so Plex knows how to play the media in your channels.