Skip to content

Instantly share code, notes, and snippets.

@0xTenable
Last active August 4, 2016 16:55
Show Gist options
  • Save 0xTenable/f8df067cbc179f80d309be296f1a35d5 to your computer and use it in GitHub Desktop.
Save 0xTenable/f8df067cbc179f80d309be296f1a35d5 to your computer and use it in GitHub Desktop.
Simpler version of RokuFeed.brs
'********************************************************************
'
' NewVideoPlayer -- Example Multi-Level Roku Channel
'
' Copyright (c) 2015, belltown. All rights reserved. See LICENSE.txt
'
'*********************************************************************
'
' Parse a Roku <feed> Xml document.
'
' See parseRokuItem () for a list of supported Xml elements.
' Note that for a Roku <feed>, only Roku elements are supported (No RSS, MRSS, iTunes extensions).
'
' Parse a Roku <feed> Xml document returning a Content List of <item> Content Items.
' Returns a feed Content Item containing an xxChildContentList of all subordinate <item> elements.
' Only a single <feed> node is supported for each category.
'
' The Roku Xml document is processed in two stages:
' First, the document is parsed, and a 'roku' structure is created containing an array of item data fields.
' Second, the 'roku' structure is read and used to populate the ContentItem structures.
' This could be done in a single step; however, separating the Xml file parsing logic and the
' mapping of parsed data to content item attributes seemed at the time to make the implementation easier.
'
Function getRokuContent (xml As Object) As Object
contentItem = {}
' Parse the entire Roku feed returning a temporary 'roku' data structure that
' contains the header info (e.g "title), and an array, itemList, containing
' an element for each <item>
roku = parseRokuFeedXml (xml)
' Use a fixed-length array for storing the content item list
contentItem.xxChildContentList = CreateObject ("roArray", roku.itemList.Count (), False)
' Set up content item Meta-Data
contentItem.Title = roku.title
contentItem.xxFeedContentType = roku.feedContentType ' Only "video" supported currently
contentItem.xxIsCached = True
contentItem.xxFeedType = "items"
' Set up each content item
' Limit the maximum number of <item> elements handled (0 = no limit)
For index = 0 To roku.itemList.Count () - 1
If index < MAX_ITEMS () Or MAX_ITEMS () = 0
contentItem.xxChildContentList.Push (getRokuItemContent (roku, index))
End If
End For
Return contentItem
End Function
'
' Take the item data from a specified roku.itemList element and use it to
' populate a ContentItem structure
'
Function getRokuItemContent (roku As Object, index As Integer) As Object
contentItem = {}
item = roku.itemList [index]
' Set image based on item's sdImg/hdImg atributes
sdImg = item.sdImg
hdImg = item.hdImg
If sdImg = "" Then sdImg = hdImg
If hdImg = "" Then hdImg = sdImg
' If no sdImg/hdImg attributes specified for the item, then default to the <feed> sd_img/hd_img, if any
If sdImg = "" Then sdImg = roku.sdImg
If hdImg = "" Then hdImg = roku.hdImg
streamFormat = item.streamFormat
switchingStrategy = item.switchingStrategy
contentType = item.contentType
contentId = item.contentId
title = item.title
If title = "" Then title = roku.title
' synopsis (if present) is used for the Description field on the roSpringboardScreen
synopsis = item.synopsis
' description (if present) is used for ShortDescriptionLine2 on the roPosterScreen
description = item.description
' Make sure we have something for the Description field on the roSpringboardScreen
If synopsis = "" Then synopsis = description
runtime = item.runtime
director = item.director
actorList = item.actorList
genreList = item.genreList
rating = item.rating
releaseDate = item.releaseDate
starRating = item.starRating
srt = item.srt
sdBifUrl = item.sdBifUrl
hdBifUrl = item.hdBifUrl
If sdBifUrl = "" Then sdBifUrl = hdBifUrl
If hdBifUrl = "" Then hdBifUrl = sdBifUrl
cc = item.cc
' If the device is running in SD mode, there must be at least one SD stream specified.
' If the content contains no SD streams, but the device is running in SD mode,
' then add an SD stream corresponding to the lowest bitrate HD stream,
' to ensure there is at least one playable stream.
hasHD = False
hasSD = False
streamList = item.streamList
defaultSDStream = {bitrate: 9999999}
For Each stream In streamList
If stream.quality = True
hasHD = True
Else
hasSD = True
End If
If stream.bitrate < defaultSDStream.bitrate
' Save the stream having the lowest bitrate
defaultSDStream = stream
' Make it an SD stream
defaultSDStream.quality = False
End If
End For
' Ensure that if running in SD mode, there is at least one SD stream specified
If Not _isHD () And hasSD = False And streamList.Count () > 0
streamList.Push (defaultSDStream)
End If
fullHD = item.fullHD
' If no contentId was specified, then make one out of the hash of the first stream url
If contentId = "" And streamList.Count () > 0
contentId = _hash (streamList [0].url)
End If
' Set the content id for each media stream
For Each stream In streamList
stream.contentId = contentId
End For
' Sanitize text fields
title = _xmlEntityDecode (_stripHtmlTags (title))
synopsis = _xmlEntityDecode (_stripHtmlTags (synopsis))
description = _xmlEntityDecode (_stripHtmlTags (description))
For index = 0 To genreList.Count () - 1
genreList [index] = _xmlEntityDecode (_stripHtmlTags (genreList [index]))
End For
For index = 0 To actorList.Count () - 1
actorList [index] = _xmlEntityDecode (_stripHtmlTags (actorList [index]))
End For
live = item.live
' Set up the Content Meta-Data, only setting fields that have data, to minimize Content Item size
contentItem.HDBranded = hasHD Or fullHD
contentItem.Live = live
contentItem.IsHD = hasHD Or fullHD
contentItem.FullHD = fullHD
contentItem.ContentId = contentId
contentItem.Streams = streamList
contentItem.ContentType = contentType
contentItem.StreamFormat = streamFormat
If cc = True Then contentItem.ClosedCaptions = True
If srt <> "" Then contentItem.SubtitleUrl = srt
If title <> "" Then contentItem.Title = title
If title <> "" Then contentItem.ShortDescriptionLine1 = title
If sdImg <> "" Then contentItem.SDPosterUrl = sdImg
If hdImg <> "" Then contentItem.HDPosterUrl = hdImg
If rating <> "" Then contentItem.Rating = rating
If runtime > 0 Then contentItem.Length = runtime
If synopsis <> "" Then contentItem.Description = synopsis
If director <> "" Then contentItem.Director = director
If sdBifUrl <> "" Then contentItem.SDBifUrl = sdBifUrl
If hdBifUrl <> "" Then contentItem.HDBifUrl = hdBifUrl
If starRating <> "" Then contentItem.StarRating = starRating
If releaseDate <> "" Then contentItem.ReleaseDate = releaseDate
If description <> "" Then contentItem.ShortDescriptionLine2 = description
If switchingStrategy <> "" Then contentItem.SwitchingStrategy = switchingStrategy
If genreList.Count () > 0 Then contentItem.Categories = genreList
If actorList.Count () > 0 Then contentItem.Actors = actorList
Return contentItem
End Function
'
' The first step in processing a Roku feed is to parse the feed document, using the
' parsed data to populate a 'roku' structure, which contains some header data and
' and itemList with an entry for each parsed item.
' getRokuItemContent () will later be called to take this data and construct
' a ContentItem structure.
'
Function parseRokuFeedXml (xml As Object) As Object
' Elements and attributes that are not within an <item> element
feed = parseRokuHeader (xml)
' Handle each <item>
feed.itemList = []
For Each item in xml.GetNamedElementsCI ("item")
feed.itemList.Push (parseRokuItem (item))
End For
Return feed
End Function
'
' Parse the header items for a Roku <feed>.
'
' The Roku videoplayer SDK example has no supported attributes,
' and defines two elements <resultLength> and <endIndex>, neither of which are used.
'
' Additional <feed> attributes supported by this example channel are:
' content_type - "video" is the only supported feed content type for now
' sd_img/sdImg - SD image path (if not specified for the <item> elements)
' hd_img/hdImg - HD image path (if not specified for the <item> elements)
'
Function parseRokuHeader (xml As Object) As Object
header = {}
' <feed contentType="video">
' Not used in the Roku SDK videoplayer example, but provides a hook to
' allow future support for feeds other that video feeds
header.feedContentType = _getXmlAttrString (xml, "content_type", "video")
' <feed title="Feed title"> - Used as the right breadcrumb on items' roSpringboardScreen
header.title = _getXmlAttrString (xml, "title")
' Allow <feed> element to contain image attributes to act as defaults for the subordinate <item> elements
header.sdImg = _getXmlAttrString (xml, "sd_img")
If header.sdImg = "" Then header.sdImg = _getXmlAttrString (xml, "sdImg")
header.hdImg = _getXmlAttrString (xml, "hd_img")
If header.hdImg = "" Then header.hdImg = _getXmlAttrString (xml, "hdImg")
' If sdImg missing then use hdImg and vice-versa
If header.sdImg = "" Then header.sdImg = header.hdImg
If header.hdImg = "" Then header.hdImg = header.sdImg
' <resultLength> - not currently used - feed paging is not currently implemented
' <endIndex> - not currently used
Return header
End Function
'
' Parse a single <item> element for a Roku <feed>
'
' Standard item attributes and elements (from Roku videoplayer SDK example):
'
' <item> attributes:
' sdImg - SD image path (if missing, defaults to hdImg)
' hdImg - HD image path (if missing, defaults to sdImg)
'
' <item> elements:
' <title> - Item title
' <contentId> - Uniquely identifies the item for bookmarking and Roku logging (defaults to hash of media streamUrl)
' <contentType> - "episode" (or "Talk") for roSpringboardScreen artwork in landscape, otherwise "movie" for portrait
' <contentQuality> - SD (default) or HD - used if media streamQuality is not set
' <media> - One element per media stream. Several media streams of different bitrates may be specified
' <streamFormat> - "mp4" or "hls"; used if the streamFormat additional item element not specified
' <streamQuality> - SD or HD
' <streamBitrate> - An integer bitrate in kbps for this stream
' <streamUrl> - The url for the media stream
' <synopsis> - Used as the Description on the item's roSpringboardScreen
' <genres> - One element per genre, used to set the Categories on the item's roSpringboardScreen
' <runtime> - An integer length of the content in seconds
'
' Additional item elements allowed:
'
' <description> - Used as the item's ShortDescriptionLine2 (and Description if <synopsis> is missing)
' <streamFormat> - "mp4" or "hls"
' <switchingStrategy> - The SwitchingStrategy used when <streamFormat> is "hls". Default is "full-adaptation"
' <fullHD> - "True" if the item was encoded at 1080p resolution
' <rating> - The Rating, e.g. "PG-13"
' <starRating> - The StarRating, an integer from 1 to 100
' <releaseDate> - The ReleaseDate, a string item in any date format
' <director> - The Director
' <srt> - The path to a subtitle's file; sets the SubtitleUrl attribute
' <cc> - "True" to show the closed-captions indicator
' <sdBifUrl> - Url for SD trick modes (not yet tested)
' <hdBifUrl> - Url for HD trick modes (not yet tested)
' <actors> - One element per actor, used to set the Actors on the item's roSpringboardScreen
' <live> - "True" if content is for a live stream
'
Function parseRokuItem (xml As Object) As Object
item = {}
' Extract <item> attributes
item.sdImg = _getXmlAttrString (xml, "sdImg")
item.hdImg = _getXmlAttrString (xml, "hdImg")
' Just in case the same attribute names as in <feed> are used
If item.sdImg = "" Then item.sdImg = _getXmlAttrString (xml, "sd_img")
If item.hdImg = "" Then item.hdImg = _getXmlAttrString (xml, "hd_img")
' Extract <item> elements
item.title = _getXmlString (xml, "title")
item.contentId = _getXmlString (xml, "contentId")
item.contentType = _getXmlString (xml, "contentType", "episode")
If LCase (item.contentType) = "talk" Then item.contentType = "episode" ' SDK videoplayer example xml files use 'Talk' for their videos
' Default contentQuality for <item> is SD. The <media> contentQuality overrides the <item> contentQuality
item.contentQuality = UCase (_getXmlString (xml, "contentQuality", "SD"))
' Build an array of streams from the <media> elements
mediaStreamUrl = ""
mediaStreamFormat = ""
item.streamList = []
'-------------------- Changes ----------------------------'
url = "http://www.filmon.com/tv/channel/info/345"
IDArray = getStreamId(url)
'-------------------- Changes ----------------------------'
For Each media In xml.GetNamedElementsCI ("media")
stream = {}
' Use the first media streamFormat ("mp4"/"hls") as the item's streamFormat
If mediaStreamFormat = ""
mediaStreamFormat = _getXmlString (media, "streamFormat")
End If
stream.quality = UCase (_getXmlString (media, "streamQuality", item.contentQuality)) <> "SD"
stream.bitrate = _getXmlInteger (media, "streamBitrate", 0)
stream.url = _getXmlString (media, "streamUrl")
'IDArray holds an array of string IDs
'Replace 0 in IDArray[0] with whatever index you want from the array'
if item.title <> "FO"
stream.url = stream.url + "?id=" + IDArray[0]
end if
If mediaStreamUrl = ""
mediaStreamUrl = stream.url
End If
' stream.contentId is set up later
item.streamList.Push(stream)
End For
item.synopsis = _getXmlString (xml, "synopsis")
' Extract the genres data from the Xml (allow for either <genres> or <genre> elements to be used)
item.genreList = []
genreItemList = xml.GetNamedElementsCI ("genres")
For Each genre In genreItemList
genreItem = genre.GetText ().Trim ()
If genreItem <> ""
item.genreList.Push (genreItem)
End If
End For
genreItemList = xml.GetNamedElementsCI ("genre")
For Each genre In genreItemList
genreItem = genre.GetText ().Trim ()
If genreItem <> ""
item.genreList.Push (genreItem)
End If
End For
item.runtime = _getXmlInteger (xml, "runtime")
' The following elements are custom elements, not used in the Roku SDK videoplayer example ...
item.description = _getXmlString (xml, "description")
' Use the media streamFormat (e.g. "hls") if one was specified
item.streamFormat = mediaStreamFormat
item.fullHD = _getXmlBoolean (xml, "fullHD")
' If there was no media streamFormat specified, then use the item streamFormat
If item.streamFormat = ""
item.streamFormat = LCase (_getXmlString (xml, "streamFormat"))
End If
' If no streamFormat specified, examine the url
If item.streamFormat = ""
If LCase (Right (mediaStreamUrl, 5)) = ".m3u8"
item.streamFormat = "hls"
Else
item.streamFormat = "mp4"
End If
End If
' For streamFormat of "hls", specify switchingStrategy
If item.streamFormat = "hls"
item.switchingStrategy = LCase(_getXmlString (xml, "switchingStrategy", "full-adaptation"))
Else
item.switchingStrategy = ""
End If
item.rating = _getXmlString (xml, "rating")
item.starRating = _getXmlString (xml, "starRating")
item.releaseDate = _getXmlString (xml, "releaseDate")
item.director = _getXmlString (xml, "director")
item.srt = _getXmlString (xml, "srt")
item.cc = _getXmlBoolean (xml, "cc")
item.sdBifUrl = _getXmlString (xml, "sdBifUrl")
item.hdBifUrl = _getXmlString (xml, "hdBifUrl")
' Extract the actors data from the Xml (allow for either <actors> or <actor> elements to be used)
item.actorList = []
actorItemList = xml.GetNamedElementsCI ("actors")
For Each actor In actorItemList
actorItem = actor.GetText ().Trim ()
If actorItem <> ""
item.actorList.Push (actorItem)
End If
End For
actorItemList = xml.GetNamedElementsCI ("actor")
For Each actor In actorItemList
actorItem = actor.GetText ().Trim ()
If actorItem <> ""
item.actorList.Push (actorItem)
End If
End For
item.live = _getXmlBoolean (xml, "live", False)
Return item
End Function
' Simplified getStreamID() function for matrixebiz
' @Params:
' url: the url to parse id's from
' Result: an array of streamids
function getStreamID(url as String)
urlTransfer = CreateObject("roUrlTransfer")
urlTransfer.setUrl(url)
result = urlTransfer.getToString()
json = ParseJSON(result)
'The stream id (i.e. everything after "?id=" ....)'
StreamIDs = []
for each stream in json.data.streams
r = CreateObject("roRegex", "id=", "")
streamid = r.Split(stream.url)
StreamIDs.push(streamid[1])
end for
return StreamIDs
end Function
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment