[Announcement] Custom Metadata Providers

Okay, I understand. So the tmdb-season-4020-1 was created by the matching of the Provider, which is then obviously used for future requests. That makes sense.

Thanks for the clarifications.

Some examples of the media i deal with

- /variety_shows/Tokyo PC Club/Season 2025
-- '251024 Tokyo PC Club ep141 - title [1080p] [TVer-epazv886fi-].mkv'
-- '251031 Tokyo PC Club ep142 - title [1080p] [TVer-ep4agwe23h].mkv'
-- '251107 Tokyo PC Club ep143 - title [1080p] [TVer-eprp8osb5b].mkv'
-- '251114 Tokyo PC Club ep144 - title [1080p] [TVer-epdlo6727o].mkv'
- /variety_shows/Task have Fun Diary/Season 2025
-- 'Task have Fun Diary - Task 97 20250718.mkv'
-- 'Task have Fun Diary - Task 98 20250725.mkv'
-- 'Task have Fun Diary - Task 99 20250801.mkv'
- /variety_shows/Gaki no Tsukai/Season 2019/
-- 'Gaki no Tsukai - 2019-01-27 - (#1440) New Year's Party.mkv'
- /youtube/Nogizaka Official Channel [UCfvohDfHt1v5N8l3BzPRsWQ]/Season 2025 
-- '20251205 long title1 [youtube-lQLNFhb-Jgs].mkv'
-- '20251205 long title2 [youtube-8vUuD_YoQ3E].mkv'

any many more.

I have a couple of further questions:

How long are the request timeouts? Since the server is making a request to an API, I would assume that there is some sort of timeout when a request is forcefully cancelled if the response is taking too long to arrive. I couldn’t find anything in that regard in the documentation. There doesn’t seem to be a general default; it could be 10, 30, or 60 seconds. How long would that be for the Provider request?

For a bit of background, my current Metadata Agent requests the metadata from an API, which has rate limitations. However, to get all of the information, I already need to hit up quite a few individual API endpoints. Furthermore, I never really provided the synopsis of episodes because that information would only be available when I make a request for each individual episode. As you would imagine, the number of requests would stack up easily for that, and I quickly ran into the rate limitations.

While I probably will have to implement some form of request queue and cache the metadata source API responses, I was wondering how much time Plex gives the Provider to respond, instead of just saying" well, one minute is up and I got nothing" and cancelling the metadata update.

From what I can see in the example project, the incoming requests are split into separate requests to the provider: Show metadata, extras, images, and for each individual season. So, I wouldn’t necessarily need to provide ALL of the metadata right in the first request and could simply wait for the season request to be made. In turn, this wouldn’t mean that the show itself wouldn’t get updated with metadata if the requesting of episode metadata took too long to respond. Which is good.

What sort of caching does the Plex server do? I tried to find where the /images endpoint is being triggered and found that this is an “on the fly” request when you edit the show and click on poster or background, but this also doesn’t seem to be hit every time because now, 12 minutes later, there is still no new response being logged in the example project even though I clicked on all of the 4 image tabs. This tells me that Plex is doing some caching or reducing the number of requests made to the providers, which is generally a good thing, but this could be confusing if this isn’t mentioned somewhere; you might need to hit that up more frequently while developing that endpoint. But this doesn’t seem to apply to the other endpoints so it looks like it is specific or unique for the images?!

Last question: With how metadata providers are now structured, acting as an API instead of a local plugin, local file access would now also be impossible, right? What my current metadata Agent does is that it reads a “matching” file (similar to the plexmatch file) in the show/movie folder so that you can match the library item without having to add an ID to the folder title. This worked well because the old agents had access to the files through Plex. However, since this is now a separate application that could not even run on the same system, this wouldn’t really work anymore, since I wouldn’t be able to guarantee that the system has access to the file.

How would that need to work, or what would need to change on my side in the future? Especially if we consider the fact that 3rd party scanners will not be available in the future. Plex already can match by ID, but I don’t think that this is that “open” right now that we could simply add whatever source to it (so instead of {tmdb-10283} we have something like {mal-12345} that is then passed to the metadata Provider for matching.

Or maybe the plexmatch file with guid in [source]://[id] format?

That’s indeed a very important question. I plan to use a metadata source that takes “ages” to provide the metadata - since multiple requests need to be done by my metadata provider before I can offer a response to the requesting PMS.

Well, since the location of the agent could be the PMS itself, you could definitely have access to local files for existing entries to the library, but I am not sure how to get the path in case of a new media - but to roll this out for users, such a system would need to match the available library pathes to real world pathes in order to make educational guesses on SMB mapping in case it’s not on localhost. Complex and full of pitfalls.

Local file access should definitely be something that the pms itself would need to provide for the metadata provider (for txt nfo or similar files in the same folder). Or those matching files need to find their way into a database that the metadata provider has access to.

A wide field of interesting questions. Thank you for asking them.

Our current timeout on these requests looks to be 90 seconds, we could make this configurable - how long are you expecting to wait? Taking so long will make refreshes painfully slow.

Good question. We don’t generally do any caching for the provider calls in PMS, except in the instance of the images endpoint where we just don’t make another call for image if one was made less than an hour ago (this will be reset if you refresh the metadata for the item). This was just a measure to reduce API load.

In a sense yes, that’s no longer possible. However it wouldn’t be impossible if your provider was running locally and had access to the same files then you could in theory match them up if you wanted to.

I’m open to coming up with solutions for this, we already pass hints in the match requests with one of those being the filename so if the information is in the actual file then you’re already set. If it’s in some other place then I’m open to finding ways to accomodate this.

Thanks for the great questions, I’d really love to start making a list of community suggestions for changes and improvements so we can start prioritizing them.

That is the problem, I can’t really say. My MyAnimelist Metadata Agent will hit up an API that has a rate limit of 60/minute. But I would need to do one request for searching, one for the general information, one for the pictures, one for episodes (and that just for one page of 100) and one for characters and then for each of those characters one as well. So this would be 5 already without each individual character/staff/VA etc. If I now wanted to get the episode synopsis as well, I would have to trigger each episode. This wouldn’t be that bad for Anime with 12 episodes (but even that could be more than 30 requests with all of the staff), but shows that have hundreds or even One Piece with over 1100 episodes at the moment. This will take a while with 1 request every second. So yeah, I will need a request queue and cache the requests so that the season request to the Provider will simply timeout unless that metadata was already cached.

That isn’t much of a problem that Plex will timeout; it just is something that would be nice to have somewhere in the developer documentation.

Okay, good to know that the refresh resets that.

That is too bad, but also unavoidable, going the “separate application” kind of way.

However, this raises a question. In the fireside chats, it was stated that you are looking into bringing NFO support to the Plex Metadata Agents. Are you considering them as part of the Plex Series/Plex Movie Agents or rather as something more standalone?

I ask this because someone might only want to rely on local metadata through the created NFO files and not have to use the Plex Agents that might request some data from an online source. With the new Metadata Agents settings and being able to order and prioritise the metadata sources through the Agents, I think it would make sense to have it as a separate Agent to specifically only allow or prioritise NFO metadata. If a user now wants to only provide some metadata through an NFO file and fill the rest up from the Plex sources, they would only need to order them that way in the Agents Configuration.

I will have to play around with this a bit more to see what is actually going on there and what is passed to the Provider.

My idea would have been to convert my matching file to the .plexmatch and set the guid for MyAnimeList, which I would then use in the Provider to get the metadata. In the example project, I can already see that there is a request body of the match POST with the GUID for TVDB, so if that can also be provided or come from the .plexmatch file, this would be an acceptable solution.

Hi, I also have a few questions.

I’d like to code a couple of supportive metadata providers to replace the python ones.

a) I’d like to rely on the main metadata provider’s matching and only support with overwriting metadata to already matched items. Is that possible?

b) For movie support, I need the IMDB ID for each movie. How do I get it with the new structure?

b) For tv shows support, I’d either need the tvdb or tmdb id. Same questions as before.

c) I would like to add custom rating keys (not replace them) to items that went through my agents (like ofdb://
. or fernsehserien://
 ). Possible? Or will these be gone sooner or later?

d) The legacy python agents had the possibilities to have a configuration page within Plex. I used it to ask users to add a token to their agent and was able to hand it over to the agent. Will such a solution (with URL params or some other construction) be available for the new agents? In a multi-user agent scenario, no single user should use their own developer API key to provide other user’s data. How does it / will it work?

Thank you for taking the time to consider answers to my question.

Yes you can do this. When you create a new metadata agent you must set which provider is the “primary”, this provider will be used for matching and will get that provider’s guid/ratingKey.

When you configure a metadata agent, you’d make one with Plex Movie/Series being the primary then add your custom one as an additional one (plus “Plex Local Media” if you want to use any local metadata/assets). Then after creating it, go and edit it and drag your provider to the top - this will prioritise all the metadata from your provider and fill the gaps with the lower priority providers but still match with the “primary”.

Could you elaborate on where you need these as I’m not sure I understand correctly. These external ids will still get saved to the item like they do with the Plex agents if you use the Plex provider in your agent. Any of the “Guid” elements your own provider also returns will also get saved to the metadata item.

ratingKey or guid? You will not be able to keep guids like those with the new agents, firstly if you plan to do what you’re asking in question a, then the items will have Plex guids and ratingKeys. Secondly, if you use your provider as the primary then it must use the guid schema rules outlined in the docs (https://developer.plex.tv/pms/#guid-construction).

ratingKeys must be formatted correctly too, this is also explained in the link above.

This is covered in the “missing features” list in the first post. It is planned but not yet implemented.

Thank you very much for helping me out.

Regarding the supportive metadata provider:

If I understand it correctly, there’s no replacement to the following python code in the “old” agent any longer:

  languages = ['de']
  primary_provider = False
  contributes_to = ['com.plexapp.agents.imdb']


 where I could define which “old agent” it contributes to
 This is just a UI thing now? How do I make clear that it only produces German output?

In the old structure, matching would be limited to get the imdb id of the underlying agent and work with it in the agent


(for movies)

  def search(self, results, media, lang):

    # Use the IMDB id found by the Plex Movie agent
    results.Append(MetadataSearchResult(
      id = media.primary_metadata.id,
      score = 100
    ))
...

(or for tv shows)

  def search(self, results, media, lang):

    if Prefs['v3apitoken'] is None:
      return
    if Prefs['v3apitoken'] == '':
      return

    try:
      myconverter = JSON.ObjectFromURL(TVDB_TO_IMDB_URL, cacheTime=CACHE_1WEEK)
    except:
      myconverter = [ ]
      Log("Load Error: Private ID Converter")

    imdb_id = ''
    for i in myconverter:
      if i["tid"] == media.primary_metadata.id:
        imdb_id = i["iid"]
        Log("ID im Konverter gefunden: " + imdb_id)

    TVDB_TO_TMDB_URL = 'https://api.themoviedb.org/3/find/%%s?external_source=tvdb_id&api_key=%s' % (Prefs['v3apitoken'])
    TMDB_TO_IMDB_URL = 'https://api.themoviedb.org/3/tv/%%s/external_ids?api_key=%s' % (Prefs['v3apitoken'])

   # Use the TVDB id found by the TVDB agent to lookup IMDB id

    if imdb_id == '':
      try:
        json_obj = JSON.ObjectFromURL(TVDB_TO_TMDB_URL % (media.primary_metadata.id), cacheTime=CACHE_1WEEK)
        tmdb_id = json_obj['tv_results'][0]['id']
        Log(tmdb_id)
        json_obj = JSON.ObjectFromURL(TMDB_TO_IMDB_URL % (tmdb_id), cacheTime=CACHE_1WEEK)
        imdb_id = json_obj['imdb_id']
        Log(imdb_id)
      except:
        return

    results.Append(MetadataSearchResult(
      id = imdb_id,
      score = 100
    ))

I got it that I should have this in the matching part of my new agent’s code. But how does it get the external IDs from the main agent?

Not having found evidence (enough evidence for me) that the main Plex agents are matching and filling metadata for any objects before my own agent steps in, my questions may sound weird, but I have a gap in understanding here, obviously.

Speaking of GUIDs
 there are external databases references like imdb, themoviedb and tvdb. I would (for example) like to add another external references for further services to directly access them if they have been matched in my agent before (like ofdb://). My agent could then grab this in an update process to directly access metadata in the source database. Is that possible?

Yeah, thank you. It would really be great to have these back since I would love to have the users decide on which metadata exactly they want to update with my agents.

Okay, so you want the external ID available to your custom provider. I think we should be able to easily solve this. We currently pass guid along with match requests when it exists but only pass one of these along and you don’t have control over which one is passed exactly. This will also only happen once the item has been refreshed once already so not ideal for you use.

What we can do is fetch any external ids we have on the initial match and pass all of them along to subsequent match requests, this way if you have the Plex provider matching for you it could pass all the external guids along in the POST body to your provider, something like:

POST http://your_provider/library/metadata/matches

{
  "title": "A movie",
  "year": 2004,
  "guids": [
    "imdb://tt9603212",
    "tmdb://575264",
    "tvdb://30294"
  ]
}

Would something like that work for you?

For season matching it would probably be better to pass the show’s guids, with a parentGuids array instead of guids array. The body would also contain the season index so you would have what you need there.

You can add any external ids to your metadata response using the Guid element and those will get stored in the PMS database for that item.

These however would not get passed along right now with metadata requests (i.e. when refreshing an already matched item), we expect that the custom provider can get all the info it needs from the guid that your provider returns for that item.

Here is a simple explanation of the steps that happens when you match new content when you have an agent with multiple providers:

→ Files scanned and hints extracted (e.g. title, year)

→ Match request made to primary provider (hints passed in JSON POST body)

→ Primary provider returns response for best match and PMS database stores initial small subset of metadata (which includes the returned guid)

→ A refresh is done (a metadata call) for the item for each provider that’s part of the metadata agent. As we only have a guid/ratingKey for the primary provider at this point, we’ll make a metadata request for it and a match request to any other providers.

→ Once we have matched initially with the other providers, PMS will store the GUID that each provider returns for the item in the database so any time we refresh that item we only do a metadata GET request for it and don’t match again each time.

I hope this helps you understand how this process works a bit better, and not to think of it working like the legacy system.

That really would be great to have. I have no idea of how to implement a matching service that solely relies on hints like “title” and “year”, because there’s no interaction between my agent and the user (for them to confirm). I would be happy to be able to continue to match by using the imdb, tmdb, tvdb ids for the agents I am creating.

I will then add a new guid for any agent I am writing (like ofdb:// or fsde://<id in fernsehserien.de universe ). If the full array of guids becomes available, matching and/or metadata generating would work like a charm (without doing the mapping each and every time the metadata gets refreshed).

I am pressing the button on metadata provider development to see how things will develop. Thank you for your open mind.

Oh yes, thank you so much. This helps alot.

Appreciate your help.

I’ve added this to my task list as it’s a great improvement for everyone.

Also PMS won’t only send title and year, but also the filename (for shows and seasons it will pass the filename of the first episode). If this contains more info it can be helpful for matching.

Currently it doesn’t pass the full path, only the relative path to the base folder configured for the library.

If Discovery cast information supported multiple languages, I could choose not to scrape cast information from external sources. However, at the moment, scraping multilingual cast data from external sources causes Plex’s built-in Discovery cast information — along with its related recommendation features — to be lost, which is something I want to avoid.

Does Plex have any plans to add multi-language support for Discovery cast information in the future? What is the data source for Discovery cast information? Is it TMDB? TMDB already supports translations for cast information, and most of this data is available in multiple languages. Given this, why hasn’t Plex added multi-language support to Discovery cast information yet? Displaying Discovery cast information based on the user’s interface/library language seems like a better approach.

I’m running into the following issue: according to the documentation, both art and thumb images need to be publicly accessible URLs. These images appear to be processed by a transcoder hosted by Plex before being served to the user. In my case, the URLs can be accessed by PMS, but they are not publicly accessible over the internet. What options are there?

I maintain a audiobook agent that uses the file path of the music as a way to match items. is the file path going to be one of the things that will be visible to the agent, maybe a relative file path also the agent I’m working on reads ID3 tags directly from the files trying to get the ASIN of audiobook either from the Tag or from the file path so that I can look up the tags. I assume the Tag is not going to be possible to re-implement but what about the file path

Also, when you do implement the new protocol for Music, please make it possible to make a collection directly. I had to create an extremely hacky workaround. That is the only way I can define a series where I basically made a elevated type plug-in and then access the API from the loopback on the outside to add collections to my audiobooks as well as add labels

In the documentation of the example project it is possible to set the collection through the match request of a movie (tmdb-example-provider/docs/Metadata.md at main · plexinc/tmdb-example-provider · GitHub).

But I cannot add Type “18” - Collection to the MediaProvider info. When updating the agent I get the error “The provider supports unsupported metadata types”.

How can I set the collection of a movie through the agent?