need help with MovieObject

I have a urlservice that works fine to retrieve the media data but can't get metadata (it is not available).

 

Now I'm trying to make a channel which is able to retrive the metadata from somewhere else and I want to use the media from the urlservice.

 

So far, I tried this:

def list():

  oc = ObjectContainer()

movies = #### DATA IN JSON ####

  for movie in movies:
oc.add(MovieObject(
      key = Callback(movie, id = movie[‘id’]),
      rating_key = MOVIE_URL.format(movie[‘id’]),
      genres = movie[‘genres’],
      tags = movie[‘tags’],
      duration = movie[‘duration’],
      title = movie[‘title’],
      original_title = movie[‘original_title’],
      year = movie[‘year’],
      studio = movie[‘studio’],
      thumb = Resource.ContentsOfURLWithFallback(url=urlparse.urljoin(BASE_URL, movie[‘thumb’])),
      art = Resource.ContentsOfURLWithFallback(url=urlparse.urljoin(BASE_URL, movie[‘art’])),
      tagline = movie[‘tagline’],
      summary = movie[‘summary’],
      # content_rating = ‘NC-17’,
      # content_rating_age = 17,
      rating = movie[‘rating’],
      writers = movie[‘writers’],
      directors = movie[‘directors’],
      producers = movie[‘producers’],
      countries = movie[‘countries’],
      roles = movie[‘roles’],
      items = URLService.MediaObjectsForURL(movie[‘stream’])
    ))

  return oc

def movie(id):

  movie = #### DATA IN JSON ####

  oc = ObjectContainer(
    title2 = movie[‘title’]
  )

  oc.add(MovieObject(
    key = Callback(movie, id = movie[‘id’]),
    rating_key = MOVIE_URL.format(movie[‘id’]),
    genres = movie[‘genres’],
    tags = movie[‘tags’],
    duration = movie[‘duration’],
    title = movie[‘title’],
    original_title = movie[‘original_title’],
    year = movie[‘year’],
    studio = movie[‘studio’],
    thumb = Resource.ContentsOfURLWithFallback(url=urlparse.urljoin(BASE_URL, movie[‘thumb’])),
    art = Resource.ContentsOfURLWithFallback(url=urlparse.urljoin(BASE_URL, movie[‘art’])),
    tagline = movie[‘tagline’],
    summary = movie[‘summary’],
    # content_rating = ‘NC-17’,
    # content_rating_age = 17,
    rating = movie[‘rating’],
    writers = movie[‘writers’],
    directors = movie[‘directors’],
    producers = movie[‘producers’],
    countries = movie[‘countries’],
    roles = movie[‘roles’],
    items = URLService.MediaObjectsForURL(movie[‘stream’])
  ))

  return oc

movie['stream'] contains a url for which there is a URL Service that works correctly but can't almost serve any metadata.

 

In Plex Web this works correctly. It shows a list of clips and on clicking on a clip it shows the clip detais and if you click play it plays the clip. In the list you can even click play and start playing the clip without going into the details page.

 

But in Plex Home Theater it shows correctly the list of clips. And on clicking on a clip it shows the details page but there is no way to play the clip. If I click enter it shows the details page again.

 

Anyone knows what must I change for it to work properly? What I'm not taking into account?

 

Thanks.

Just to try to figure out if the URLService is what makes that code wrong I have decided to try directly a mp4 sample.

def list():

oc = ObjectContainer()

movies = [ “movie1”, “movie2” ]

for movie in movies:
oc.add(MovieObject(
key = Callback(movieCB, movie),
rating_key = movie,
genres = [ “genre1”, “genre2” ],
tags = [ “tag1”, “tag2” ],
duration = 5000,
title = movie,
original_title = movie,
year = 2015,
studio = “Vetro Polding Meier”,
thumb = Resource.ContentsOfURLWithFallback(‘https://image.tmdb.org/t/p/w396/9O7gLzmreU0nGkIB6K3BsJbzvNv.jpg’),
art = Resource.ContentsOfURLWithFallback(‘https://image.tmdb.org/t/p/w780/xBKGJQsAIeweesB79KC89FpBrVr.jpg’),
tagline = “Amazing Clip”,
summary = “Beep, beep, beep, beep”,
content_rating = ‘NC-17’,
content_rating_age = 17,
rating = 9.9,
writers = [ “George R. R. King” ],
directors = [ “Francis Ford Kubrik” ],
producers = [ “Walt de Laurentiis” ],
countries = [ “Orbis Alia” ],
roles = [
{ ‘role’: ‘Harrison Cage’ },
{ ‘role’: ‘Mary-Louise Hepburn’ }
],
items = [
MediaObject(
parts = [
PartObject( key = ‘http://techslides.com/demos/sample-videos/small.mp4’ )
]
)
]
))

return oc

def movieCB(name):

oc = ObjectContainer(
title2 = name
)

oc.add(MovieObject(
key = Callback(movieCB, name),
rating_key = name,
genres = [ “genre1”, “genre2” ],
tags = [ “tag1”, “tag2” ],
duration = 5000,
title = name,
original_title = name,
year = 2015,
studio = “Vetro Polding Meier”,
thumb = Resource.ContentsOfURLWithFallback(‘https://image.tmdb.org/t/p/w396/9O7gLzmreU0nGkIB6K3BsJbzvNv.jpg’),
art = Resource.ContentsOfURLWithFallback(‘https://image.tmdb.org/t/p/w780/xBKGJQsAIeweesB79KC89FpBrVr.jpg’),
tagline = “Amazing Clip”,
summary = “Beep, beep, beep, beep”,
content_rating = ‘NC-17’,
content_rating_age = 17,
rating = 9.9,
writers = [ “George R. R. King” ],
directors = [ “Francis Ford Kubrik” ],
producers = [ “Walt de Laurentiis” ],
countries = [ “Orbis Alia” ],
roles = [
{ ‘role’: ‘Harrison Cage’ },
{ ‘role’: ‘Mary-Louise Hepburn’ }
],
items = [
MediaObject(
parts = [
PartObject( key = ‘http://techslides.com/demos/sample-videos/small.mp4’ )
]
)
]
))

return oc

Same problem. It works correctly in Plex Web but doesn't work in Plex Home Theater. In Plex Home Theater I can see the list and the details page but there is no way to play the clip. So, what I'm doing wrong?

Added contaner and codecs just in case but same result. It is not working in Plex Home Theater

def list():

oc = ObjectContainer()

movies = [ “movie1”, “movie2” ]

for movie in movies:
oc.add(MovieObject(
key = Callback(movieCB, movie),
rating_key = movie,
genres = [ “genre1”, “genre2” ],
tags = [ “tag1”, “tag2” ],
duration = 5000,
title = movie,
original_title = movie,
year = 2015,
studio = “Vetro Polding Meier”,
thumb = Resource.ContentsOfURLWithFallback(‘https://image.tmdb.org/t/p/w396/9O7gLzmreU0nGkIB6K3BsJbzvNv.jpg’),
art = Resource.ContentsOfURLWithFallback(‘https://image.tmdb.org/t/p/w780/xBKGJQsAIeweesB79KC89FpBrVr.jpg’),
tagline = “Amazing Clip”,
summary = “Beep, beep, beep, beep”,
content_rating = ‘NC-17’,
content_rating_age = 17,
rating = 9.9,
writers = [ “George R. R. King” ],
directors = [ “Francis Ford Kubrik” ],
producers = [ “Walt de Laurentiis” ],
countries = [ “Orbis Alia” ],
roles = [
{ ‘role’: ‘Harrison Cage’ },
{ ‘role’: ‘Mary-Louise Hepburn’ }
],
items = [
MediaObject(
parts = [
PartObject( key = ‘http://techslides.com/demos/sample-videos/small.mp4’ )
],
container = Container.MP4,
video_codec = VideoCodec.H264,
audio_codec = AudioCodec.AAC
)
]
))

return oc

def movieCB(name):

oc = ObjectContainer(
title2 = name
)

oc.add(MovieObject(
key = Callback(movieCB, name),
rating_key = name,
genres = [ “genre1”, “genre2” ],
tags = [ “tag1”, “tag2” ],
duration = 5000,
title = name,
original_title = name,
year = 2015,
studio = “Vetro Polding Meier”,
thumb = Resource.ContentsOfURLWithFallback(‘https://image.tmdb.org/t/p/w396/9O7gLzmreU0nGkIB6K3BsJbzvNv.jpg’),
art = Resource.ContentsOfURLWithFallback(‘https://image.tmdb.org/t/p/w780/xBKGJQsAIeweesB79KC89FpBrVr.jpg’),
tagline = “Amazing Clip”,
summary = “Beep, beep, beep, beep”,
content_rating = ‘NC-17’,
content_rating_age = 17,
rating = 9.9,
writers = [ “George R. R. King” ],
directors = [ “Francis Ford Kubrik” ],
producers = [ “Walt de Laurentiis” ],
countries = [ “Orbis Alia” ],
roles = [
{ ‘role’: ‘Harrison Cage’ },
{ ‘role’: ‘Mary-Louise Hepburn’ }
],
items = [
MediaObject(
parts = [
PartObject( key = ‘http://techslides.com/demos/sample-videos/small.mp4’ )
],
container = Container.MP4,
video_codec = VideoCodec.H264,
audio_codec = AudioCodec.AAC
)
]
))

return oc

Have a look at the Seinfeld plugin:

https://github.com/plexinc-plugins/JerrySeinfeld.bundle/blob/master/Contents/Code/__init__.py

Thanks. Looking to that example I have no doubt about the correct implementation.

And in fact is exactly what I have in the example. A list of MovieObjects inside an ObjectContainer and then a Object Container with only one MovieObject.

Anyway, I have tried just now again with code like in that plugin and my best bet is that Plex Home Theater has a bug. If we use MovieObjects directly in code (I mean not being the response from a URLService, because I know that way it works) they won't play in Plex Home Theater. Once you reach the details page (preplay page) you have no way to play the MovieObject.

If we use VideoClipObject instead then it works. (Exactly same code changing MovieObject to VideoClipObject)

Plex Web works fine no matter if it is a VideoClipObject or a MovieObject.

The poster on the VideoClip preplay screen doesn't look as good as the Movie preplay screen but at least with VideoClipObject works on both.

And I tried using URLService.MediaObjectsForURL with a youtube video link to populate the items and it actually works.

Sadly I tested other URL Service I coded which actually is the reason why I wanted to know how to do this and it works in Plex Web but it doesn't in Plex Home Theater. So I will have to debug why.

Your example adds MovieObjects to an ObjectContainer, this is not what the linked example does.

So, in your case, it would look something like this(not tested):

def list():

oc = ObjectContainer()

movies = [ “movie1”, “movie2” ]

for movie in movies:
oc.add(CreateMovieObject(
video_url = ‘http://techslides.com/demos/sample-videos/small.mp4’,
genres = [ “genre1”, “genre2” ],
tags = [ “tag1”, “tag2” ],
duration = 5000,
title = movie,
original_title = movie,
year = 2015,
studio = “Vetro Polding Meier”,
thumb = Resource.ContentsOfURLWithFallback(‘https://image.tmdb.org/t/p/w396/9O7gLzmreU0nGkIB6K3BsJbzvNv.jpg’),
art = Resource.ContentsOfURLWithFallback(‘https://image.tmdb.org/t/p/w780/xBKGJQsAIeweesB79KC89FpBrVr.jpg’),
tagline = “Amazing Clip”,
summary = “Beep, beep, beep, beep”,
content_rating = ‘NC-17’,
content_rating_age = 17,
rating = 9.9,
writers = [ “George R. R. King” ],
directors = [ “Francis Ford Kubrik” ],
producers = [ “Walt de Laurentiis” ],
countries = [ “Orbis Alia” ],
roles = [
{ ‘role’: ‘Harrison Cage’ },
{ ‘role’: ‘Mary-Louise Hepburn’ }
]
))

return oc

@route(YOUR_ROUTE_PREFIX, genres=list, tags=list, duration=int, , include_container=bool)
def CreateMovieObject(video_url, genres, tags, duration, title, original_title, , include_container=False):

movie_object = MovieObject(
  key = Callback(CreateMovieObject, video_url=video_url, <etc>, include_container=True),
  rating_key = video_url,
  title = title,
  duration = duration,
  <etc>,
  items = [
    MediaObject(
      parts = [
        PartObject( key = video_url )
      ],
      container = Container.MP4,
      video_codec = VideCodec.H264,
      audio_codec = AudioCodec.AAC
    )
)

if include_container:
  return ObjectContainer(objects=[movie_object])
else:
  return movie_object

Sorry, your example is correct and does the same thing as above.

I wonder if it has something to do with the routes? I.e. you need to exactly specify the types via the route decorator. Try adding routes and see if it still doesn't work ...

It shouldn't be anything about the routes because just changing MovieObject to VideoClipObject makes it work fine.

We don't need to worry about the list. I was complicating the example because I was not sure if I were doing things correctly. I didn't understand why Plex Home Theater was not working. But the example can be a lot more simple. This should be a full working channel.

# -*- coding: utf-8 -*-
TITLE  = u'Test Channel'
PREFIX = '/video/testchannel'

################################################################################
def Start():
ObjectContainer.title1 = TITLE
HTTP.CacheTime = CACHE_1HOUR

################################################################################
@handler(PREFIX, TITLE)
def testchannel_main_menu():
oc = ObjectContainer()

oc.add(DirectoryObject(
key = Callback(testchannel_amovie),
title = ‘A Movie’
))

return oc

################################################################################
@route(PREFIX + ‘/movie’)
def testchannel_amovie():
oc = ObjectContainer()

oc.add(MovieObject(
key = Callback(testchannel_amovie),
rating_key = ‘TestChannel_AMovie’,
genres = [ “genre1”, “genre2” ],
tags = [ “tag1”, “tag2” ],
duration = 5000,
title = ‘A Movie’,
original_title = ‘A Movie’,
year = 2015,
studio = “Vetro Polding Meier”,
thumb = Resource.ContentsOfURLWithFallback(‘https://image.tmdb.org/t/p/w396/9O7gLzmreU0nGkIB6K3BsJbzvNv.jpg’),
art = Resource.ContentsOfURLWithFallback(‘https://image.tmdb.org/t/p/w780/xBKGJQsAIeweesB79KC89FpBrVr.jpg’),
tagline = “Amazing Clip”,
summary = “Beep, beep, beep, beep”,
content_rating = ‘NC-17’,
content_rating_age = 17,
rating = 9.9,
writers = [ “George R. R. King” ],
directors = [ “Francis Ford Kubrik” ],
producers = [ “Walt de Laurentiis” ],
countries = [ “Orbis Alia” ],
roles = [
{ ‘role’: ‘Harrison Cage’ },
{ ‘role’: ‘Mary-Louise Hepburn’ }
],
items = [
MediaObject(
parts = [
PartObject( key = ‘http://techslides.com/demos/sample-videos/small.mp4’ )
],
container = Container.MP4,
video_codec = VideoCodec.H264,
audio_codec = AudioCodec.AAC
)
]
))

return oc

It works fine in Plex Web and in Plex for Samsung. But it doesn't in Plex Home Theater. And if on that code we just change MovieObject to VideoClipObject (nothing else, only that) then it works in Plex Home Theater too.

In my opinion it is a bug or at least I'm not able to find out what I'm missing.

The only thing that comes to my mind is if I miss something that I should include in the MediaObject. For example the protocol. But then why it works if it is a VideoClipObject? Or why it works if exactly that same MovieObject comes from a URLService?

So my only bet is that this is a real bug in Plex Home Theater and that it is valid code. I don't mind if Plex Home Theater can't play it really. I'm only using Plex Home Theater as a second testing platform because I have it installed on the development computer and mostly works the same as Plex for Samsung.

Better I use VideoClipObjects while developing and replace them with MovieObject later.

I preface this by saying I am not great with the playing media from the __init__.py.  I tend to use a URL service when possible and stick to the always used "CreateObject" function format you see in all the ones that do not have a URL service.

But looking at the code you posted, it looks like you are missing the if/else for the include container that is shown in that seinfeild example and others.

  if include_container:
    return ObjectContainer(objects=[videoclip_obj])
  else:
    return videoclip_obj

I think it has to be able to run through that function twice, once to get the metadata and then a second time to get the media. And different Plex players pull the metadata and media different ways, so that is why it may work on some players and not others. For example, the Roku app does not even require a separate CreateObject function to play media.

This is the reason you tend to see people use the "CreateObject" named function so often. They have copied it and then just made changes to the existing copy where they need to make sure it is structured right. 

Another tip: I have also spent way too much time debugging because I did not make sure the rating key was picking up a unique identifier for each item.

I preface this by saying I am not great with the playing media from the __init__.py.  I tend to use a URL service when possible and stick to the always used "CreateObject" function format you see in all the ones that do not have a URL service.

But looking at the code you posted, it looks like you are missing the if/else for the include container that is shown in that seinfeild example and others.

  if include_container:
    return ObjectContainer(objects=[videoclip_obj])
  else:
    return videoclip_obj

I think it has to be able to run through that function twice, once to get the metadata and then a second time to get the media. And different Plex players pull the metadata and media different ways, so that is why it may work on some players and not others. For example, the Roku app does not even require a separate CreateObject function to play media.

This is the reason you tend to see people use the "CreateObject" named function so often. They have copied it and then just made changes to the existing copy where they need to make sure it is structured right. 

Another tip: I have also spent way too much time debugging because I did not make sure the rating key was picking up a unique identifier for each item.

Thanks for the tip but sadly I think that's not the reason. Look that I'm using a DirectoryObject and that's why I don't need that pattern.

I'm going to put another example to understand that pattern you refer to.

# -*- coding: utf-8 -*-
TITLE  = u'Test Channel'
PREFIX = '/video/testchannel'

################################################################################
def Start():
ObjectContainer.title1 = TITLE
HTTP.CacheTime = CACHE_1HOUR

################################################################################
@handler(PREFIX, TITLE)
def testchannel_main_menu():
oc = ObjectContainer()

oc.add(DirectoryObject(

# key = Callback(testchannel_amovie, container = True),
# title = 'A Movie'

))

Here we are directly adding a MovieObject to the ObjectContainer

So the container is not needed

oc.add(
testchannel_amovie()
)

return oc

################################################################################
@route(PREFIX + ‘/movie’)
def testchannel_amovie(container = False):

mo = MovieObject(
key = Callback(testchannel_amovie, container = True),
rating_key = ‘TestChannel_AMovie’,
genres = [ “genre1”, “genre2” ],
tags = [ “tag1”, “tag2” ],
duration = 5000,
title = ‘A Movie’,
original_title = ‘A Movie’,
year = 2015,
studio = “Vetro Polding Meier”,
thumb = Resource.ContentsOfURLWithFallback(‘https://image.tmdb.org/t/p/w396/9O7gLzmreU0nGkIB6K3BsJbzvNv.jpg’),
art = Resource.ContentsOfURLWithFallback(‘https://image.tmdb.org/t/p/w780/xBKGJQsAIeweesB79KC89FpBrVr.jpg’),
tagline = “Amazing Clip”,
summary = “Beep, beep, beep, beep”,
content_rating = ‘NC-17’,
content_rating_age = 17,
rating = 9.9,
writers = [ “George R. R. King” ],
directors = [ “Francis Ford Kubrik” ],
producers = [ “Walt de Laurentiis” ],
countries = [ “Orbis Alia” ],
roles = [
{ ‘role’: ‘Harrison Cage’ },
{ ‘role’: ‘Mary-Louise Hepburn’ }
],
items = [
MediaObject(
parts = [
PartObject( key = ‘http://techslides.com/demos/sample-videos/small.mp4’ )
],
container = Container.MP4,
video_codec = VideoCodec.H264,
audio_codec = AudioCodec.AAC
)
]
)

if container:
return ObjectContainer( objects = [ mo ] )
else:
return mo

This is the code using that pattern you refer to.  It is used when you want to create a list of MovieObjects (or VideoClipObjects). On the list you need to have the Movie/VideoClipObject without container and that's why you need the if/else pattern in the function that creates the object (we can call it CreateObject. In my case I called it testchannel_amovie).

But anyone can test this new example to see it is not working in Plex Home Theater either. When you reach the full details preplay page you don't have a play button to actually play the media. But it works in other clients.

And once again if you just change the MovieObject to VideoClipObject it works in Plex Home Theater and the rest of clients.

Well just to see it clear I repeat the code with that minor change:

# -*- coding: utf-8 -*-
TITLE  = u'Test Channel'
PREFIX = '/video/testchannel'

################################################################################
def Start():
ObjectContainer.title1 = TITLE
HTTP.CacheTime = CACHE_1HOUR

################################################################################
@handler(PREFIX, TITLE)
def testchannel_main_menu():
oc = ObjectContainer()

oc.add(DirectoryObject(

# key = Callback(testchannel_amovie, container = True),
# title = 'A Movie'

))

Here we are directly adding a MovieObject to the ObjectContainer

So the container is not needed

oc.add(
testchannel_amovie()
)

return oc

################################################################################
@route(PREFIX + ‘/movie’)
def testchannel_amovie(container = False):

mo = VideoClipObject(
key = Callback(testchannel_amovie, container = True),
rating_key = ‘TestChannel_AMovie’,
genres = [ “genre1”, “genre2” ],
tags = [ “tag1”, “tag2” ],
duration = 5000,
title = ‘A Movie’,
original_title = ‘A Movie’,
year = 2015,
studio = “Vetro Polding Meier”,
thumb = Resource.ContentsOfURLWithFallback(‘https://image.tmdb.org/t/p/w396/9O7gLzmreU0nGkIB6K3BsJbzvNv.jpg’),
art = Resource.ContentsOfURLWithFallback(‘https://image.tmdb.org/t/p/w780/xBKGJQsAIeweesB79KC89FpBrVr.jpg’),
tagline = “Amazing Clip”,
summary = “Beep, beep, beep, beep”,
content_rating = ‘NC-17’,
content_rating_age = 17,
rating = 9.9,
writers = [ “George R. R. King” ],
directors = [ “Francis Ford Kubrik” ],
producers = [ “Walt de Laurentiis” ],
countries = [ “Orbis Alia” ],
roles = [
{ ‘role’: ‘Harrison Cage’ },
{ ‘role’: ‘Mary-Louise Hepburn’ }
],
items = [
MediaObject(
parts = [
PartObject( key = ‘http://techslides.com/demos/sample-videos/small.mp4’ )
],
container = Container.MP4,
video_codec = VideoCodec.H264,
audio_codec = AudioCodec.AAC
)
]
)

if container:
return ObjectContainer( objects = [ mo ] )
else:
return mo

-------------------------

When that pattern is used sometimes (or all of the times not sure) the createObject function receives as parameters some or all of the metadata to create the object. And this must be for whatever reason, for example because they only can gather the metadata from the list of clips page but they don't have specific pages to gather the metadata and media for each clip to be able to create a URL Service.

-------------------------

Maybe I'm missing something that I'm not able to find but it all indicates this is a bug in Plex Home Theater.