ServiceCode.pys Template:
This is the result of trying to make a small change that completely removed this template.
# IMPORTANT NOTE: AS WITH ALL PYTHON CODING, PROPER INDENTATION IS NECESSARY TO PREVENT ERRORS.
# PROGRAMS LIKE NOTEPAD++ DO NOT ALWAYS LINE UP PROPERLY OR CATCH THESE INDENTION ERRORS. TO
# PREVENT THESE AND OTHER PYTHON ERRORS, BEFORE RUNNING THE PROGRAM IN PLEX OR AFTER MAKING CHANGES
# TO THE FILE, I OPEN IT FIRST IN A LOCAL VERSION OF PYTHON I HAVE LOADED AND CHOOSE
# RUN > CHECK MODULE TO FIND ANY POSSIBLE PROBLEMS.
# These are python variables you set up for use later in this file
# The naming and values are based on the where and how you choose to use them in your code
# For the most part, you are defining variables you will use in the PlayVideo function
BASE_URL = 'base_http_address_here'
# It is best practice to use regex when possible and avoid importing any Python modules, so below is a regex statement I use
# later in my PlayVideo function to find the video info xml address within the page that allows me to easily pull the
# web pages corresponding video file location
RE_XML_URL = Regex("/xml/video(.+?)',")
# The variable below is basic regex to pull a video from an html page. I show its use in a optional version of the PlayVideo function
# RE_VIDEO_URL = Regex('videofile:"(?P[^"]+)"')
# IMPORTANT NOTE: THE VALUE OF 'URL' THAT IS PASSED TO THE DIFFERENT FUNCTIONS IN THIS PROGRAM IS DETERMINED EITHER
# WITHIN THE PROGRAMMING OF THE INDIVIDUAL CHANNEL PLUGIN THAT USES THIS URL SERVICE OR BY THE END USER CHOOSING THE PLEXIT BUTTON
########################################################################################################
# BELOW IS AN OPTIONAL CODE FOR CONVERTING HTML VIDEO PAGES TO THEIR CORRESPONDING VIDEO INFO XML PAGES.
# IF YOU CAN FIND REFERENCE TO THESE XML VIDEO INFO FILES ON THE VIDEO WEBPAGES, THESE XML PAGES CAN BE
# USED FOR EASY RETRIEVAL OF METADATA AND VIDEO FILES USING XML.ElementFromURL.
# We use string.replace() to create a new url that adds /xml/video-folder/ to middle and changes the extension to .xml.
########################################################################################################
XML_URL = url.replace('http://www.anywebsite.com/','http://www.anywebsite.com/xml/videos-page/').replace('.html','.xml')
####################################################################################################
# This pulls the metadata that is used for Plexit, suggested videos (to friends), the pre-play screen on iOS, Roku, Android and Plex/Web,
# as well as if you get the "info" or "details" within the OSX/Windows/Linux desktop client.
# Afer you pull the data you should save it to preset variables. These basic variables are title, summary, and thumb.
# but some also include duration, and originally_available_at, content_rating, show, tags and index
def MetadataObjectForURL(url):
Here you are using the ElementFromURL() API to parse or pull up all the data from the webpage.
See the Framework documentation API reference for all the choices for parsing data
page = HTML.ElementFromURL(url)
# Extract the data available on the page using xpath commands.
# Think about what will access the metadata from this URL service to determine what info you want to extract here
# Below is a basic example that pulls the the title, description and thumb from the head of a html document that makes a request of this URL Service
title = page.xpath("//head//meta[@property='og:title']")[0].get('content')
description = page.xpath("//head//meta[@property='og:description']")[0].get('content')
thumb = page.xpath("//head//meta[@property='og:image']")[0].get('url')
# This command returns the metadata values you extracted as a video clip object.
# See the Framework documentation API reference for all the choices of objects to return metadata
return VideoClipObject(
title = title,
summary = description,
thumb = thumb,
)
####################################################################################################
Here you will define the different types of videos available on this website and the values for their quality
You will need to search the source of the web pages that play a video to determine the type and location of videos offered thru the site
You can see if the name and location is available through an embed link, but you may have to look into the subpages for a web page
like javascript or style sheets to find this information. You will also need this information later when
writing the code for the PlayVideo function that pulls these specific video files from the webpage
def MediaObjectsForURL(url):
return [
First you are calling a MediaObject() for each type of video the website offers using the MediaObject API command
Most separate these types of videos by the resolution, for example a site may offer a high and low quality option for each video on its site
or they may offer an .flv format and an .mp4 format version of the video file. You can choose to only offer one type of video file
or give the user the option of choosing the type of video file they want to use if there are several different types and qualities.
MediaObject(
Then within each MediaObject you define the values for that particular type of video
The options most used are video_codec, audio_codec, parts, container, video_resolution, audio_channels, and bitrate
See the Framework documentation API reference for a lists all possible attributes for MediaObject()
Audio Codecs are AAC and .MP3; Video Codecs are .H264; Container File Types are .MP4, .MKV, .MOV, and .AVI¶
And for audio channels stereo equals 2
I have found the best way to determine these attributes is to use VLC player and open the network stream URL of a few of the
videos available on the site. And then use the tools to view the media information esp. codec info
video_codec = VideoCodec.H264,
audio_codec = AudioCodec.MP3,
video_resolution = '720',
audio_channels = 2,
container = 'f4v',
This section of the code allows you to peice together videos that come in 2 or more parts it also uses a callback function
that calls the PlayVideo function below. This done to separate the playing of the video from the call for the media object
that way it will only play the video if the user selects that particular video.
The code below is the basic format that all URL Serivces use and then they define PlayVideo if the video only has one part, it only needs one PartObject
Note that we are also sending a fmt variable to tell the PlayVideo function whether this is a high or low quality video. This is only
necessary when you have more than one choice for the video files available.
parts = [PartObject(key=Callback(PlayVideo, url = url, fmt='hi')]
)
Below we are doing the same as above, and defining a second MediaObject that is a low quality version of the video file that is available.
The fmt variable is sent to the PlayVideo function to help you determine which of the available video types to send back to the Media Object.
MediaObject(
video_codec = VideoCodec.H264,
audio_codec = AudioCodec.MP3,
video_resolution = '420',
audio_channels = 2,
container = 'f4v',
parts = [PartObject(key=Callback(PlayVideo, url = url, fmt='lo')]
)
]
####################################################################################################
Here we are defining the PlayVideo function we called in the callback function above. This function defines the pattern for
the location and naming scheme of the video so we can play the video file directly. You use HTML request, regular expressions,
and predefined variables to create the path or http address of the video associated with the html or xml page that was sent
to this service through the “URL” value. The programming here will vary greatly based on the location of the
video file related to your video pages. This is where you will be doing the majority to the programming.
It is best to refer to other services already created for examples of how to pull the video file.
First we define the function taking the the variables for the url entered into the service and the
fmt variable we established above in MediaObjects
@indirect
def PlayVideo(url, fmt):
Below I have included a basic example of how to program the PlayVideo function for pulling the video location on the web page
whose URL was sent to the service and only has ONE video on the page. The code pulls the raw data from the web page
using the URL and then uses a simple page search that returns an f4v video file url that is located in each html video page.
It uses both of the variables we established at the top of the service.
EXAMPLE:
page = HTTP.Request(url).content
video = RE_VIDEO_URL.search(page).group(‘video_url’) + “.f4v”
video = BASE_URL + video
The example I chose to use for this function uses the optional xml video information file to pull the high and low
quality version of a video from the url sent to this URL service.
It basically pulls the content from the URL, looks through the page to find a mention of the regex value I defined as a
global variable at the beginning of this document. Because the line of data I was using contained a parenthesis right before
the address, I had to get a little creative in pulling it and then replacing the ending single quote to properly pull
the xml address out of the page. The function then opens that xml video info page and uses xpath commands to extract the
video URL and returns those values in the form of a high and low quality video. Then it uses the fmt variable to determine
which of these videoss should be the value of video.
content = HTTP.Request(url).content
xmlurl = RE_XML_URL.search(content).group(0)
xmlurl = xmlurl.replace("'", '')
xmlurl = base_url + xmlurl
xml = XML.ElementFromURL(xmlurl)
if xml:
# Here we are extracting the video file addresses from the xml video file metadata
videoHi = xml.xpath('//video/videoHi//text()')[0]
videoLo = xml.xpath('//video/videoLo')[0].text
videoHi = videoHi.replace('.f4v', '.mp4')
videoLo = videoLo.replace('.f4v', '.mp4')
if fmt == 'hi':
video = videoHi
else:
video = videoLo
We then return the address or value of the video. This video URL is then sent back to the MediObject above with return Redirect(video)
The IndirectResponse() is a Plex Framework function that tells the client to only execute this function when the user hits play so there is no delays.
return IndirectResponse(VideoClipObject, key=video)
####################################################################################################
def TestURLs():
This is for testing the URL, so you are making sure the URL service is working.
YOU DO NOT NEED THIS IF YOU PUT A TEST URL IN YOUR SERVICEINFO.PLIST FILE. THIS IS AN ALTERNATIVE WAY IF YOU THINK THE VIDEO PAGES WILL CHANGE
ALOT AND YOU DO NOT THINK YOU CAN DETERMINE A SPECIFIC URL THAT WILL NOT CHANGE
This coding below is a basic example that allows you to make the test url more dynamic so the service will not fail just because the
test url no longer exists. Enter the main domain for urls that will be sent to this service in place of anydomain.com
test_urls = []
page = HTML.ElementFromURL('http://www.anydomain.com/')
# here in this link command you need to edit this xpath to reflect the id or class is used to represent
# a video player container on pages for this url service and enter the correct domain name
for link in page.xpath("//a/span[@class='vid']/.."):
if len(test_urls) < 3:
url = link.get('href')
url = "http://www.anydomain.com" + url
if url not in test_urls:
test_urls.append(url)
else:
break
return test_urls
See last post for alternative location