URL Services¶
URL services are responsible for taking a website’s URL and returning standardized Plex metadata objects. For example, given the URL “http://youtube.com/watch?v=vzKbhxY1eQU”, the URL service would return a video clip object with appropriate title, summary, and other relevant details.
The magic of a URL service is its ability to convert arbitrarily formatted web content into a normalized schema for a piece of media, with all the richness of the Plex library metadata (e.g. tags, cast, media format/codec information, etc.).
Defining a URL service¶
To add a URL service to a plug-in, the developer just to make a small addition to the plug-in’s Info.plist file. The following example illustrates a definition for a URL service handling content from YouTube:
<key>PlexURLServices</key>
<dict>
<key>YouTube</key>
<dict>
<key>Identifier</key>
<string>com.plexapp.plugins.youtube</string>
<key>URLPattern</key>
<string>http://.*youtube\.com/(watch)?\?v=</string>
<key>TestURLs</key>
<array>
<string>http://youtube.com/watch?v=vzKbhxY1eQU</string>
</array>
</dict>
</dict>
PlexURLServices is a dictionary that can define multiple URL services. Services must be uniquely named.
Each service must have a URLPattern defined. The value should be a regular expression that matches URLs that can be handled by the service. Plex uses this expression to determine which service should be used to handle a given URL.
The Identifier of the service must be a globally unique string, and cannot be shared with any other URL service, even ones in different bundles. While optional, this string must be provided in order to link a URL service with a related content service.
Additionally, each service can optionally define an array of TestURLs. This array should contain a list of URLs that the service is capable of handling. Plex’s automated testing system will use these URLs to verify that the service is functioning correctly and generate an alert if any problems occur. Developers are strongly encouraged to add a list of testable URLs to allow errors to be caught quickly.
Creating a source file¶
Once a service has been defined in the Info.plist file, the developer needs to add a file containing the service’s source code. The file should be named ServiceCode.pys (the .pys extension indicates that the file contains service code) and added to a subdirectory with the same name as the key used in the service definition. Continuing the YouTube example above, the path of the file would be as follows:
Contents/URL Services/YouTube/ServiceCode.pys
Writing the code¶
URL services should define the following functions:
- MetadataObjectForURL(url)¶
This function should create and return a metadata object (for example, a VideoClipObject) and populate it with metadata from the given URL. Only the metadata should be added here - the object’s key and rating_key properties will be synthesised based on the URL.
Note
Although the object’s media items can be created here, this isn’t necessary. If no items are provided, they will be populated using the function defined below.
Parameters: url (str) – The URL of the web page to use as a source for generating the metadata object. Returns: A metadata object representing the media available at the given URL.
- MediaObjectsForURL(url)¶
This function should create and return a list of media objects and part objects representing the media available at the given URL. Callbacks may be used if obtaining the final media location requires additional computation.
Note
This function is expected to execute and return very quickly, as it could be called several times for different URLs when building a container. As such, developers should avoid making any API calls that could delay execution (e.g. HTTP requests).
Parameters: url (str) – The URL of the web page to use as a source for generating the metadata object. Returns: Media objects representing the media available at the given URL. Return type: list
- NormalizeURL(url)¶
This function should return a “normalised” version of the given URL. Plex uses the URL as a unique identifier for a given piece of content, but the same media will often appear at different URLs, even simply due to additional query string arguments. For example, the following URLs all point to the same video:
In this case, the normalised version of this URL would be:
This function is called automatically before passing the URL to the above two functions. They will always receive normalised URLs.
Note
This function is expected to execute and return very quickly, as it could be called several times for different URLs when building a container. As such, developers should avoid making any API calls that could delay execution (e.g. HTTP requests).
Note
This function is optional. If each piece of media is only accessible via a single URL, the developer may omit this function.
Parameters: url (str) – The URL to be normalised. Returns: A normalised version of the URL. Return type: str
Testing the service¶
The simplest way of testing a URL service is to use the built-in lookup function. Using the example URL given above, the service could be tested by making the following HTTP request:
$ curl "http://localhost:32400/system/services/url/lookup?url=http%3A%2F%2Fwww.youtube.com%2Fwatch%3Fv%3DdQw4w9WgXcQ"
<?xml version='1.0' encoding='utf-8'?>
<MediaContainer size="1" identifier="com.plexapp.system" mediaTagPrefix="/system/bundle/media/flags/" mediaTagVersion="1300301082">
<Video url="http://youtube.com/?v=dQw4w9WgXcQ" key="/system/services/url/lookup?url=http%3A//youtube.com/%3Fv%3DdQw4w9WgXcQ" type="clip" rating="9.3569088" duration="213000" title="Rick Astley - Never Gonna Give You Up" originallyAvailableAt="2009-10-25" summary="Music video by Rick Astley performing Never Gonna Give You Up. YouTube view counts pre-VEVO: 2,573,462 (C) 1987 PWL" ratingKey="http://youtube.com/?v=dQw4w9WgXcQ">
<Media indirect="1">
<Part file="" key="/:/plugins/com.plexapp.system/urlservice_function/Y2VyZWFsMQowCnMzMwpodHRwOi8veW91dHViZS5jb20vP3Y9ZFF3NHc5V2dYY1E_/PlayVideo?args=Y2VyZWFsMQoxCnR1cGxlCjAKcjAK&kwargs=Y2VyZWFsMQoxCmRpY3QKMQpzMzMKaHR0cDovL3lvdXR1YmUuY29tLz92PWRRdzR3OVdnWGNRczMKdXJscjAK&indirect=1"/>
</Media>
<Media indirect="1">
<Part file="" key="/:/plugins/com.plexapp.system/urlservice_function/Y2VyZWFsMQowCnMzMwpodHRwOi8veW91dHViZS5jb20vP3Y9ZFF3NHc5V2dYY1E_/PlayVideo?args=Y2VyZWFsMQoxCnR1cGxlCjAKcjAK&kwargs=Y2VyZWFsMQoxCmRpY3QKMgpzMzMKaHR0cDovL3lvdXR1YmUuY29tLz92PWRRdzR3OVdnWGNRczMKdXJsczQKNzIwcHMxMQpkZWZhdWx0X2ZtdHIwCg__&indirect=1"/>
</Media>
<Media indirect="1">
<Part file="" key="/:/plugins/com.plexapp.system/urlservice_function/Y2VyZWFsMQowCnMzMwpodHRwOi8veW91dHViZS5jb20vP3Y9ZFF3NHc5V2dYY1E_/PlayVideo?args=Y2VyZWFsMQoxCnR1cGxlCjAKcjAK&kwargs=Y2VyZWFsMQoxCmRpY3QKMgpzMzMKaHR0cDovL3lvdXR1YmUuY29tLz92PWRRdzR3OVdnWGNRczMKdXJsczQKSGlnaHMxMQpkZWZhdWx0X2ZtdHIwCg__&indirect=1"/>
</Media>
<Genre tag="Music"/>
<Tag tag="Rick"/>
<Tag tag="Astley"/>
<Tag tag="Sony"/>
<Tag tag="BMG"/>
<Tag tag="Music"/>
<Tag tag="UK"/>
<Tag tag="Pop"/>
</Video>
</MediaContainer>
Calling the service from plug-in code¶
The URL service can be invoked using the Service API. However, this is rarely the best course of action. Using the URL service to generate a full metadata object is often slow, and since plug-ins are usually providing a large container of items to be returned to a client, calling the service for each item in the container would cause an unacceptable delay for the user.
The most beneficial use of URL services within plug-ins is to use them to generate the media objects, but not the metadata. Since creating the media objects should always be fast, and the plug-in will usually have access to the metadata from the web page used to generate the container, this hybrid approach offers the best of both worlds - fast execution and use of the extra features enabled by using the service.
In order to simplify implementation of this common case, the framework will automatically call the URL service to populate the list of media items and set the metadata object’s key and rating_key properties if the developer sets the metadata object’s url property:
video = VideoClipObject(
title = video_title,
summary = video_summary,
originally_available_at = video_date,
rating = video_rating,
url = 'http://www.youtube.com/watch?v=dQw4w9WgXcQ'
)