Channel Development Setup

I have started looking through all the info at Plex Dev Center to learn how to make channels and I have a couple questions about the basic setup I need to start writing channels.



I took some programming classes in college and designed some database driven Intranets, so I understand some basic concepts of programming, but that was quite a few years ago, so what I knew is obsolete. I tend to just learn the minumum info needed to take other’s good examples of the program I want and edit them to fit my needs. Why reinvent the wheel when you can just change a few spokes? And though it doesn’t sound like more than a basic understanding is necessary for writing Plex channels and changing a few spokes would work here, I figured I would take the time to understand and learn what I could about the languages, programs, and services you guys are using to make channels. I have the free time and curiosity. Not to mention some of it may help me if I decide to play with any other programming projects.



I did find a post listing the programming languages you use (python, xpath, javascript, and json). But there doesn’t seem to be a list of basic programs, tools, accounts, etc. that you need for writing channels. I have git loaded on my computer and have setup a github account and played with it some. Since I am on a PC, I will probably just use Notepad++ as my editor. I also added the basic Firefox addons suggested in the Wiki pages for scraping info. Is there any other programs I need or accounts I need to sign up for?



Also, it seems most people writing channels are doing it on Macs. Is the fact that I use Windows going to hamper my ability to write and test channels properly?

I love seeing new people getting involved in channel development. For the vast majority of plugin development all you need is a basic editor (Notepad++), a browser with developer tools (either built-in or add-ons) and git. I prefer Chrome’s built-in developer tools to Firefox and its extensions but, that’s a matter of personal preference. A couple other tools that can come in handy are an image editor for creating icons/artwork for new channels, and a copy of rtmpdump for testing args when dealing with RTMP-based streams.

You’re right that most of us that have been writing channels for a while do so on Macs. That has more to do with the fact that Plex has been available for Mac longer than it has for Windows. Developing on Windows should not put you at any disadvantage.

Thanks for the response. Glad to know that Windows won’t be a problem and I will get eventually add the rtmpdump.



Right now, I feel like an idiot. Everything I have read keeps mentioning how the Plex framework was setup to make channel programming easy and I cannot figure out where to even start and what I need to learn. I am just now understanding the basics of what all the individual pieces of the channel bundle do. I get the concept of most of it, including the URL services now, thanks to your response on another post Mikedm139, but I am still having trouble with the purpose and design of the Info.plist.



Also, I looked at the python code files (init.py) for a couple of video channels that look to be simpler and come from simpler designed websites to try to get a feel for the basics. Even referring to the couple of tutorials that walk you through the code line by line, I was still confused about what I was looking at. I think I am finally grasping the concept that the you use some basic python programming to create functions that call on APIs that used xpath to extract the needed data from the webpages. I am still having trouble figuring out which API is for what and how to properly call them. I keep referring back to the Framework Documentation on the Dev Center site, but I do not feel like I am grasping it. I think, since it has been so long since I have programmed, it is just gonna take me longer, since I will need to refresh myself on all the terminology in the explanations as I go along.



The only area where I do feel like I have some direction in what I need to learn more about (like coding and syntax) is the idea of xpath and pulling elements of a webpage or site needed to build your channel. But there is no point in learning that aspect until I understand the APIs more and what information I am trying to retrieve from the websites. When I get to that point, I definitely look at Chrome’s web development tools and and how they compare to Firefox.



I know it will get easier. Each day a new little light bulb in my head goes off as some other part of the puzzle starts to make sense to me. I am realizing now that what I am having the hardest time with is the flow. The flow of how all the functions, APIs, and programming in the bundles interact and call on each other and the order of that interaction. Are there any type of visuals like flowcharts out there showing Plex channel design? Do any of the channel developers out there make flowcharts when designing their channels?

I’m not really a flow-chart guy but perhaps one of the other devs is. Don’t feel bad if you’re having trouble wrapping your head around things. I had a lot of trouble making sense of the docs until I dove in to programming and referred back to them constantly. Ask lots of questions. I know I would never have gotten anywhere with Channel Development if people (particularly Sander1, thanks again man) hadn’t been around to answer my numerous questions.



The Info.plist basically a contains important information about the plugin which is loaded by PMS at startup. For example, looking at the Info.plist from the TWiT TV plugin:



<?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.twitlive</string><br />
	<key>PlexAudioCodec</key><br />
	<array><br />
		<string>AAC</string><br />
	</array><br />
	<key>PlexClientPlatformExclusions</key><br />
	<string>Roku</string><br />
	<key>PlexClientPlatforms</key><br />
	<string>*</string><br />
	<key>PlexFrameworkVersion</key><br />
	<string>2</string><br />
	<key>PlexMediaContainer</key><br />
	<array><br />
		<string>MP4</string><br />
	</array><br />
	<key>PlexVideoCodec</key><br />
	<array><br />
		<string>H.264</string><br />
	</array><br />
</dict><br />
</plist>



The first couple lines are just generic formatting lines. Don't worry about that. TBH, I don't think they're even necessary but don't quote me on that ;) The first important key/value pair is the CFBundleIdentifier. The value is the "string" element is what PMS uses as a unique identifier for the plugin and its logs. The convention is "com.plexapp.plugins." where the name of the plugin is used without capitals, spaces, or special characters. The key for "PlexAudioCodec" as well as the "PlexMediaContainer" and "PlexVideoCodec" are optional and used to provide a hint to PMS about what type of media the channel provides. The "PlexClientExclusions" key tells PMS that the channel is not supported on one (or more) specific client platforms. That's generally not something you need to worry about. If it turns out to be necessary, it'll be added in the code review stage prior to releasing the plugin or update via the Channel Directory. The "PlexClientPlatforms" key represents all the Plex clients on which the plugin is supported. Usually the value is "*" for all platforms. Then if there are one or two platforms that are not supported, the "PlexClientExclusions" key is used. The "PlexFrameworkVersion" key will usually have a value of "2" representing the major version of the plugin framework that the plugin was written for. While there is a fairly substantial difference in the code between version 2 and version 2.1, the major version is all that matters so we just use "2". There are (I think) still a couple plugins out there using version 1 but not many. You're likely to did more v1 plugin if you dig into the code of the unsupported plugins, but I wouldn't worry too much about that.

In terms of the flow for the code in the __init__.py file, the basics are fairly straightforward but they can get somewhat convoluted in practice. In the most basic plugin, you'll have a Start() function which is responsible for setting up some of the plugin defaults, a MainMenu() function which will either present a list of videos (or audio tracks, etc) or a list of submenus which will in turn present their own lists of media objects. Most often, the metadata for the media objects is parsed from the website using xpath in a FOR loop. Sometimes, we can make use of an API provided by the website but that tends to be the exception rather than the rule.

I hope that helps provide a little clarity for you. As I said before, don't be afraid to ask questions. The learning curve can be pretty steep, especially for someone who doesn't do much programming. I think you'll find that the more you do, the more you enjoy it ad it can be really addictive :)

Thank you. Especially for making me feel like I am not alone in being confused with how to get started with this stuff. I wanted to get a good idea of the basics before I jumped in to actually programming. I thought in the long run it would make it easier and make for cleaner code. And thank you for making me feel better about asking lots of questions.



Now the plist makes sense. Thanks for the explanation, but I do have a couple questions. I see, in the Framework Documentation from the Dev Center, it list three Services property keys that you can define (PlexURLServices, PlexSearchServices, and PlexRelatedContentServices). I still have to do some research to understand the Services.bundle you talked about on another post, but am I right in assuming that you would use these services keys in your plist if you are creating your own URL, search or container service that is not already available under the existing Plex Services.bundle?



Also, what is PlexPluginMode? I see this key/value pair used in examples I have been looking at from other bundles and in the Wiki example, but it is not mentioned in the Framework Documentation from the Dev Center.



I see that some use some additional CFBundle key/value pairs that are not mentioned in the Framework Documentation from the Dev Center, but are given in the plist example in the Wiki section. They are CFBundleDevelopmentRegion, CFBundleExecutable, CFBundleInfoDictionaryVersion, CFBundlePackageType, CFBundleSignature, CFBundleShortVersionString, and CFBundleVersion. I am assuming that they are not necessary for the plist file to work properly. Would that be a correct assumption?



As far as the init.py file, I can basically follow the idea that it is setting up the variables, calling those variables in and setting the defaults in the StartMenu() function, setting up the structure for menu of the channel in the MainMenu() function, and then you have functions to pull the data needed for the different elements of the menu from the website. The basic python elements are not throwing me nearly as much as the API functions are. It is just gonna take me some time to figure out all the different APIs that are being used and why.



The most confusing part for me is that I am having trouble understanding where and how the main source code file defines and calls on the website that you get the data for the menu items from. Maybe once I have a better understanding of the Service.bundle, it will make more sense.

Hi!



The documentation is not entirely up to date (sorry about that!). The mentioned keys are not used any longer. I personally learn best by looking at existing stuff, so here are some existing URL and Search services. I hope they make sense to you? The "Related Content" service is not yet implemented anywhere btw):

URL service example in Services.bundle
https://github.com/plexinc-plugins/Services.bundle/tree/master/Contents/Service%20Sets/com.plexapp.plugins.vimeo

URL service example included in a plugin bundle:
https://github.com/plexinc-plugins/HGTV.bundle/tree/master/Contents/Services

Search service example in a plugin bundle:
https://github.com/plexinc-plugins/Devour.bundle/tree/master/Contents/Services


You can ignore (or remove) the PlexPluginMode key. It's not necessary.


Yes, that's correct :) You can ignore or remove those too.

Thanks you guys. That explians plist for me. I am just going to use the TWiTTV plist example Mikedm139 gave as my template for any Plist I create. Can I create my template with all the possible keys and just leave the strings blank, or will I need to delete any keys that I will not be using? This would just make it easier when I am comparing it to plists for existing plugins that I am trying to figure out. Also, since there is only one possible value for the PlexVideoCodec key (at least according to the Framework Documentation from the Dev Center), is it best keep that value in the file?



Here is the template I created:



<!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 />
		<!-- Unique PMS identifier for the plugin and its logs.  Format should always be com.plexapp.plugins.<nameofplugin> with no capital letters, spaces, or special characters --><br />
        <string>com.plexapp.plugins.yourchannelname</string><br />
        <key>PlexClientPlatformExclusions</key><br />
		<!-- List any platforms not supported in the plugin. Not necessary in initial design. Any values are added in the code review stage prior to release -->	<br />
        <string></string><br />
        <key>PlexClientPlatforms</key><br />
		<!-- * sets this to all platforms.  Any platforms that are not supported will be added in the exclusions above -->	<br />
        <string>*</string><br />
        <key>PlexFrameworkVersion</key><br />
		<!-- Version should always be set to 2 -->		<br />
        <string>2</string><br />
        <key>PlexAudioCodec</key><br />
        <array><br />
		<!-- possible string values are AAC and MP3 --><br />
                <string></string><br />
        </array><br />
        <key>PlexMediaContainer</key><br />
        <array><br />
		<!-- possible string values are MP4, MKV, MOV, and AVI --><br />
                <string></string><br />
        </array><br />
        <key>PlexVideoCodec</key><br />
        <array><br />
		<!-- possible string values are H.264 --><br />
                <string>H.264</string><br />
        </array><br />
</dict><br />
</plist>



Now to figure out URL and other services.

Hi!

It’s better to remove the key/value than to have an empty value I think, but please don’t focus too much on those keys, they’re just there to give some basic idea to what the contents of a plugin is.

Also, there are more possible values for container, video codec and audio codec:



Container

[list]

[]Flash

[
]Silverlight

[]MP4

[
]M4V

[]MOV

[
]FLV

[]WMV

[
]MP3

[]JPG

[
]AVI

[]TS

[/list]

Video codec

[list]

[
]VP6F

[]H.264

[
]WMV3

[]MPEG4

[
]FLV1

[]H.263

[
]VC-1

[]MPEG2

[/list]

Audio codec

[list]

[
]MP3

[]WMAV2

[
]AAC

WMA2

[/list]

I will delete the unused keys in my plist. Since the audio and video codec and containers are optional, what is the benefit of including them? Will it increase the plugins speed?

I don’t believe there is any benefit to including those keys anymore.

I have question that probably sounds pretty stupid, but how do you choose the API to parse data from a page? Most coding I have looked at uses ElementfromURL(), but I have found some that use ElementfromString().  What exactly are these two commands providing you?

ElementFromURL is basically a convenience method - it's equivalent to fetching the data yourself and passing it to ElementFromString. For example:

HTML.ElementFromURL('http://www.google.com')

is equivalent to

HTML.ElementFromString(HTTP.Request('http://www.google.com').content)

Well that explains the use by some of the ElementFromString along with the HTTP.Request.  Do they use this long hand version because they are older programs and the ElementfromURL was only added recently or is there some additional advantages to using the long hand version?

Also, I sitll do not understand exactly what data is retrieved and how it is retrieved.  For example, what does the

HTML.ElementFromURL('http://www.google.com')

give you? Does it retrieve the entire HTML document (or the equvilent of the page source) or does it only retrieve certain data like items with id elements? Does it retrieve the whole document at once or retrieve the data line by line?

Do they use this long hand version because they are older programs and the ElementfromURL was only added recently or is there some additional advantages to using the long hand version?

No, this has nothing to do with older versions. Both methods have existed since the beginning. HTTP.Request just grabs the contents of the url. It's up to you what you do with the returned data. We usually use this to grab a text based document when we want to find a certain piece of data using a regular expression.

Also, I sitll do not understand exactly what data is retrieved and how it is retrieved.  For example, what does the

HTML.ElementFromURL('http://www.google.com')

give you? Does it retrieve the entire HTML document (or the equvilent of the page source) or does it only retrieve certain data like items with id elements? Does it retrieve the whole document at once or retrieve the data line by line?

This retrieves the entire HTML document and builds an HTML DOM object that you can use your xpath queries on.

Thank you Sanders. I think I understand it now.

So the ElementforURL just pulls the data in an element tree structure directly from the url. And if the HTTP.Request just pulls the raw data from the url and ElementforString along with the HTTP.Request is the same as the ElementforURL, that would mean the ElementforString puts the raw data pulled by the HTTP.Rquest into an element tree structure.

And that tree structure is necessary to use the xpath commands.

So those who use the ElementfromString with the HTTP.Request option, do that so they can have variables that hold the data in both the tree structure and raw data form from the URL and can use that raw data elswhere in their python code. For example by:

  page = HTTP.Request(url).content
  data = HTML.ElementFromString(page)

Did I get it right?
 

I probably should have asked this sooner, but what do I do once I have written a Plex channel plugin? 

I am not sure what the process is for what you should do once you have written the channel.

Should I have added my plugin to the Wiki "Plugins in Progress" Page when I started working on it? Once I have it working, is there some type of process you should follow for its testing phase or should I submit my channels to the Unsupported Appstore as its test phase?  And then, at what point do you decide your plugin is ready to be submitted to plexinc-plugins and how do you do that?

Whether or not you add to the wiki is up to you. There are some basic guidelines for channel submission available on the wiki (if you know where to look ;)  ). Feel free to add to the Unsupported Appstore although the vast majority of those plugins are ones that will never see official status. Before submitting to the Channel Directory, your channel should be stable enough for day-to-day use (your own and perhaps a few testers as well). Ideally when you're ready to submit, you should have a github repository for the plugin that follows the basic structure as the ones found on github.com/plexinc-plugins, with artwork and icon. Then you can create a ticket on the lighthouse site with a link to your github repo and a description of the channel (which will be used in the Channel Directory for users to read). Then, the channel will go through a code review and you may or may not be asked to make a few changes prior approval and addition to the Channel Directory.

Thanks for the advice.

I do have a question about submitting a URL services.  Elsewhere you mentioned :

if your URL Service is capable of catching URLs that a user might navigate to via a web browser, it's preferred to add the URL Service to the global Services.bundle (via a separate Pull Request on GitHub). That way, the PlexIt bookmarklet can function on the host website.

The pull request was a little confusing to me, so I want to make sure I did it correctly. This is what I did:

I forked the plexinc-plugins/Services.bundle. Cloned it locally on my computer. Locally, I created a folder for my URL service in the service sets and added my URL service.  Then I committed the changes. Then added a pull request. Is that correct

Sounds like you followed the right procedure to me :)

This topic was automatically closed 90 days after the last reply. New replies are no longer allowed.