Developing Channel using JSON

Hi there,

I’m new to python development - I’m usually a C# and t-sql dev so i’m still getting my head around the syntax, terminology and structure of python and I would really appreciate some help getting something started here.

I’ve been asked by someone to have a look at developing some additional functionality within their channel which they use - I believe someone else developed it for them using the IPTV channel as a base starter. What he’s asked me to look at is to see whether i can create a new container (? is that the right terminology?) which then has sub-items which are populated from a website. I’ve created an API from that website but that does not contain video files or links. What I then need to do is look those items up as they already exist elsewhere and then create a pointer to them.

So, for example, you have a list of football fixtures on a website which are live today. We have converted them into JSON which can be obtained via a URL. The JSON also contains a list of channel names where these fixtures are being shown. What’s been asked is if we can then look up these channel names in the playlist.m3u file and display a pointer to those channels. They still exist elsewhere in the channel but produces a different view of them.

I’ve looked through some of the documentation, forum posts and examples i’ve found online and tried to write something which i expected to work but the plugin fails to load. Furthermore, I’m struggling to find any reference in the log files to see why. I attempted to tweak the BBC iPlayer plugin as a test as it seems one of the most easily structured channels to see what’s happening in it.

here’s the code i’ve written - at this point, i’ve not even got to trying to look up the channel name so i’m just using a url in the JSON just to get something appearing but with no success. Any guidance or help anyone can offer, i’d be eternally grateful.

@route(PREFIX + '/live')
def Live(title):
    oc = ObjectContainer(title2 = title)

        json_url = 'http://api.import.io/store/connector/c59fa8cc-1d7a-4823-9d2d-cdc555c1218b/_query?input=webpage/url:http%3A%2F%2Fwww.getyourfixtures.com%2Fall%2Ffixtures%2Ftoday%2Ffootball%2F2-EnglishPremierLeague&&_apikey=75e89fac15d74ce5a75b7af4c3f39a6cfdaaf56ebb7e158a18658686c8894c8e4866df29b5cc8ee566fff1e0317f7daf2069a4c028b546ccbdd19f5e4c01de0f713169d604f3e363046152a3e3b031c7'
            

            fixtures = JSON.ObjectFromURL(json_url)
                for fixture in fixtures['results']:
                    mdo = fixture['OFFICIAL_LINK']
                    mdo.title = fixture['HOME_VALUE']
                        oc.add(mdo)

#    for channel_id in content.ordered_tv_channels:
#        channel = content.tv_channels[channel_id]
#    
#        if channel.has_live_broadcasts():
#            try:
#                mdo = URLService.MetadataObjectForURL(channel.live_url())
#                mdo.title = channel.title + " - " + mdo.title
#                oc.add(mdo)
#            
#            except:
#                pass # Live stream not currently available

    if len(oc) < 1:
        return NoProgrammesFound(oc, title)

First, if a plugin fails to load, make sure you have the right bundle folder structure and that you have a info.plist file with a unique name for that channel. A lot of times the reason it will not show up is because the value of your PREFIX in your init.py and CFBundleIdentifier in your info.plist are not unique. The PREFIX value also has to include the type, like ‘/video/mychannelsuniquename’

Then reboot PMS, since changes to a plist often require rebooting PMS. Then check the com.plexapp.system.log file. It can often show errors for why it is not producing a channel or creating a com.plexapp.plugins.mychannelsuniquename.log for your plugin bundle.

Also do not use PlexWeb for channel design. The cache of a web browser often keeps it from showing you any changes you make to your channel right away. See forums.plex.tv/discussion/208369/whats-your-channel-development-and-debugging-workflow-and-why-is-mine-so-painful

Thanks for your advice @shopgirl284

I’ve spent the last week or so reading, trying, reading trying and i’m still struggling. I’ve even tried taking your RSSFeed channel and tweaking that to the requirements but again I hit a brick wall.

Would anyone else be able to offer any support or assistance? I’m sure what I’m trying to do is possible and probably wouldn’t take someone who knew what they were doing very long at all - I’m starting to lose the will to live with it right now.

Seems like your example code has some issues with it, but leaving that alone for now and focusing on utilizing json with the URL you provided. I wrote up a simple example https://gist.github.com/Twoure/68e086af04532dd8e40d using the URL you provided. I added some notes in my example explaining my process and usage for PMS.

Edit: The Python included with PMS is based off of Python 2.7. Also Json functions return a dict or dictionary object. More info on dictionary usage can be found here.

Thanks Twoure - I’ve been working on this all afternoon and believe I’ve actually now made a breakthrough.

However, I’m getting errors relating to your ‘Import requests’ statement - is that available in Plex as it doesn’t seem to be…is there any alternatives to this or am I misinterpreting the error?

@hatton33 said:
However, I’m getting errors relating to your ‘Import requests’ statement - is that available in Plex as it doesn’t seem to be…is there any alternatives to this or am I misinterpreting the error?

You do not need the following in Plex

import requests
import json

page = requests.get("http://example.com").text
d = json.loads(page)

instead use

d = JSON.ObjectFromURL("http://example.com")

It will do all that the first code block does and is supported by Plex Framework. I only used the requests and json libs to create a working example outside the Plex Framework.

Edit: Sorry, tell a lie, it’s adding a new item in the menu called ‘Todays Live EPL Fixtures’ but there are no sub-items under that…

Thank you!!!

So close now. The only bit i seem to be failing on right now is getting this thing to appear as an option in the channel itself. I was hoping that by doing what I’m doing, it would appear in the list but it seems not. Any pointers for what i’m missing.

Thank you so much for your help, I really feel like i’m starting to get a good handle on what’s going on now, whereas before, I couldn’t even see where to start. Big learning curve in python for me.

TITLE = 'IPTV'
PREFIX = '/video/iptv'
#ICON = 'icon-default.png'
#ART = 'art-default.jpg'

def Start():
    ObjectContainer.title1 = TITLE
    ObjectContainer.art = R('art-default.jpg')
    DirectoryObject.thumb = R('icon-folder.png')
    DirectoryObject.art = R('art-default.jpg')
    VideoClipObject.art = R('art-default.jpg')

@handler(PREFIX, TITLE)
def MainMenu():
    empty_group = False
    groups_list = []
    items_dict = {} # using dictionary because Plex sometimes has issues when passing list to a procedure
    if Prefs['playlist'].startswith('http://') or Prefs['playlist'].startswith('https://'):
        playlist = HTTP.Request(Prefs['playlist']).content
    else:
        playlist = Resource.Load(Prefs['playlist'], binary = True)
    if playlist <> None:
        lines = playlist.splitlines()
        count = 0
        for i in range(len(lines) - 1):
            line = lines*.strip()
            if line.startswith('#EXTINF'):
                url = lines[i + 1].strip()
                title = line[line.rfind(',') + 1:len(line)].strip()
                thumb = GetAttribute(line, 'tvg-logo')
                group = GetAttribute(line, 'group-title')
                if group == '':
                    empty_group = True
                    group = 'No Category'
                elif not group in groups_list:
                    groups_list.append(group)
                count = count + 1
                items_dict[count] = {'url': url, 'title': title, 'thumb': thumb, 'group': group, 'order': count}
                i = i + 1 # skip the url line fot next cycle
        if Prefs['sort_groups']:
            groups_list.sort(key = lambda s: s.lower())
        #if Prefs['sort_lists']:
        #    items_dict = OrderedDict(sorted(items_dict.items(), key = lambda d: d[1]['title']))
        #else:
        #    items_dict = OrderedDict(sorted(items_dict.items(), key = lambda d: d[1]['order']))
        # OrderedDict passes to other procedures unordered and required reordering, no need for this
        groups_list.insert(0, 'All')
        if empty_group:
            groups_list.append('No Category')

# Code added by Rob below this line #

    URL = 'http://api.import.io/store/connector/c59fa8cc-1d7a-4823-9d2d-cdc555c1218b/_query?input=webpage/url:http%3A%2F%2Fwww.getyourfixtures.com%2Fall%2Ffixtures%2Ftoday%2Ffootball%2F2-EnglishPremierLeague&&_apikey=75e89fac15d74ce5a75b7af4c3f39a6cfdaaf56ebb7e158a18658686c8894c8e4866df29b5cc8ee566fff1e0317f7daf2069a4c028b546ccbdd19f5e4c01de0f713169d604f3e363046152a3e3b031c7'
    
    testtomorrowURL = 'https://api.import.io/store/connector/faebc7af-bada-4349-bab5-45a3d8c0594d/_query?input=webpage/url:http%3A%2F%2Fwww.getyourfixtures.com%2Fall%2Ffixtures%2Ftomorrow%2Ffootball%2F2-EnglishPremierLeague&&_apikey=75e89fac15d74ce5a75b7af4c3f39a6cfdaaf56ebb7e158a18658686c8894c8e4866df29b5cc8ee566fff1e0317f7daf2069a4c028b546ccbdd19f5e4c01de0f713169d604f3e363046152a3e3b031c7'

    d = JSON.ObjectFromURL(testtomorrowURL)
    
    for f in d['results']:
        
        home = f['HOME_VALUE']
        
        if 'AWAY_VALUE' in f.keys():
            away = f['AWAY_VALUE']
        
        if ['STATION_CONTENTS'] in f.keys():
            stations = f['STATION_CONTENTS']
        
        if 'OFFICIAL_LINK' in f.keys():
            dummylink = f['OFFICIAL_LINK']
        elif 'COMPETITION_LINK' in f.keys():
            dummylink = f['COMPETITION_LINK']
        
        try:
            away
        except NameError:
            print 'No Fixtures'
        else:
            fixture = home + ' v ' + away
            
            if "BBC Match of the Day" not in fixture:
                if "BBC Final Score" not in fixture:
                    if "Sunday Supplement" not in fixture:
                        if "Goals on Sunday" not in fixture:
                            LiveFixture = fixture
                            
                            url = dummylink.strip()
                            title = LiveFixture.strip()
                            thumb = "http://s5.postimg.org/vbjux77xj/FOOTY123.png"
                            group = "Todays Live EPL Fixtures"
                            groups_list.append(group)
#                        else:
#                           LiveFixture = "No Live EPL Fixtures"


# Code added by Rob above this line #

    oc = ObjectContainer()
    for group in groups_list:
        oc.add(DirectoryObject(
            key = Callback(ListItems, items_dict = items_dict, group = group),
            title = group
        ))
    oc.add(PrefsObject(title = L('Preferences'), thumb = R('icon-prefs.png')))
    return oc

@hatton33 said:
So close now. The only bit i seem to be failing on right now is getting this thing to appear as an option in the channel itself. I was hoping that by doing what I’m doing, it would appear in the list but it seems not. Any pointers for what i’m missing.

In case you’re not, make sure to utilize Plex Framework Log function and watch the log files as you use the channel. This will give you insight into where a problem may be.

Another issue, be careful to not reuse the same naming convention where applicable. i.e. if a variable is defined higher in the program and then later you set a new value to the old variable, then it will be replaced. This is fine if you want that behavior.

So, it looks like the ListItems function needs the items_dict variable (which I am assuming is a list of dictionary values aka [{'title': 'example title1'}, {'title': 'example title2'}]) and the group variable (which seems to be a string). In the code you added, you append the group name to the groups_list but don’t update the items_dict with your new information. I’m guessing that’s why you are not seeing it when you selected the new Todays Live EPL Fixtures.

Since the items_dict is a list of dictionary items, you should be able to append the new dict to the list. The items_dict includes the 'order' so you will need to add that as well, I guess, depending on what the ListItems function needs.

Edit: for testing purposes I would add HTTP.CacheTime = 0 within the Start function. This will force Plex to Call ever URL and @route within the channel each time it is used. That way you’re not having to clear Plex’s cache to see your coding changes.

Also Plex Web is horrid for testing, because of caching issues. If the channel give an error and says something like “No soup” or whatever is says, then even after you update the code the Plex Web interface will still use the old cached response and give you the same error. So instead set cache time to 0. This will solve that issue, but only after the browser session has cleared its cache as well.

Bottom line, set cache to 0 and test with a mobile app or some other client other than Plex Web. Then when the time comes to release your code, make sure to remove the 0 cache time or change it to something more meaningful, i.e. an hour or 12 hours or 24 hours or whatever you decide. If you remove it then Plex Framework will use it’s default, which I don’t remember right now but is a long time and bad for debugging code.

Thanks again @Twoure - again you’ve boosted my knowledge considerably. One final thing i’m struggling with is the menu system, so I get that we have a main_menu item and that adding object containers to that main menu produces links to each of those media items. The question is, how easy is it to create a sub menu? I can’t find any examples of this anywhere online. The idea is, Main Menu, Sub Menu, Media Containers…e.g.

Todays Live Fixtures (Main Menu Item)
–Manchester United vs Manchester City (Sub Menu Item)
----Sky Sports 1 (Object Container)
----Astro Sport 1 (Object Container)
----etc (Object Container)
----etc (Object Container)
----etc (Object Container)

First off, try looking at my LihatTV channel code here to get an idea of how I set up the menu system.

Next, you have some options.

  1. Send the generated Live Fixtures list to a new DirectoryObject before going to ListItems
  2. Create new DirectoryObject to handle Live Fixtures only, then send to ListItems or another DirectoryObject before sending to ListItems
  3. Create a sub menu within ListItems keyed to Live Fixtures only
  4. Always another way, you may find something you like better

Without seeing how the channel is used normally, I would suggest trying something like Option 2.
Option 2
Move your code into a new function

@route(PREFIX + '/livelist')
def ListLiveFixtures():
    #move your code here from the Main menu here
    #After generating the list of live items, send to ListItems, or if another sub menu is needed then send the list there before finally sending to ListItems

then add to your main menu somehting like the following

oc.add(DirectoryObject(
    key=Callback(ListLiveFixtures), title='Today\'s Live Fixtures'
    ))

To add, I suggest you read over the Plex Framework Documentation, Section 6.1.2. Framework documentation can be found here and the “Plex Missing From Dev Docs” can be found here. Both are good resources to fall back on.