Updating category feed to point to production

'******************************************************
'**  Video Player Example Application -- Category Feed
'**  November 2009
'**  Copyright (c) 2009 Roku Inc. All Rights Reserved.
'******************************************************

'******************************************************
' Set up the category feed connection object
' This feed provides details about top level categories
'******************************************************
Function InitCategoryFeedConnection() As Object

    conn = CreateObject("roAssociativeArray")

    ' This also must be updated in analytics brs
    conn.UrlPrefix   = "http://newspring.cc/roku"

    conn.UrlCategoryFeed = conn.UrlPrefix + "/kidspring"

    conn.Timer = CreateObject("roTimespan")

    conn.LoadCategoryFeed    = load_category_feed
    conn.GetCategoryNames    = get_category_names

    print "created feed connection for " + conn.UrlCategoryFeed
    return conn

End Function

'*********************************************************
'** Create an array of names representing the children
'** for the current list of categories. This is useful
'** for filling in the filter banner with the names of
'** all the categories at the next level in the hierarchy
'*********************************************************
Function get_category_names(categories As Object) As Dynamic

    categoryNames = CreateObject("roArray", 100, true)

    for each category in categories.kids
        'print category.Title
        categoryNames.Push(category.Title)
    next

    return categoryNames

End Function

'******************************************************************
'** Given a connection object for a category feed, fetch,
'** parse and build the tree for the feed.  the results are
'** stored hierarchically with parent/child relationships
'** with a single default node named Root at the root of the tree
'******************************************************************
Function load_category_feed(conn As Object) As Dynamic

    http = NewHttp(conn.UrlCategoryFeed)

    Dbg("url: ", http.Http.GetUrl())

    m.Timer.Mark()
    rsp = http.GetToStringWithRetry()
    Dbg("Took: ", m.Timer)

    m.Timer.Mark()
    xml=CreateObject("roXMLElement")
    if not xml.Parse(rsp) then
         print "Can't parse feed"
        return invalid
    endif
    Dbg("Parse Took: ", m.Timer)

    m.Timer.Mark()
    if xml.category = invalid then
        print "no categories tag"
        return invalid
    endif

    if islist(xml.category) = false then
        print "invalid feed body"
        return invalid
    endif

    if xml.category[0].GetName() <> "category" then
        print "no initial category tag"
        return invalid
    endif

    topNode = MakeEmptyCatNode()
    topNode.Title = "root"
    topNode.isapphome = true

    print "begin category node parsing"

    categories = xml.GetChildElements()
    print "number of categories: " + itostr(categories.Count())
    for each e in categories
        o = ParseCategoryNode(e)
        if o <> invalid then
            topNode.AddKid(o)
            print "added new child node"
        else
            print "parse returned no child node"
        endif
    next
    Dbg("Traversing: ", m.Timer)

    return topNode

End Function

'******************************************************
'MakeEmptyCatNode - use to create top node in the tree
'******************************************************
Function MakeEmptyCatNode() As Object
    return init_category_item()
End Function

'***********************************************************
'Given the xml element to an <Category> tag in the category
'feed, walk it and return the top level node to its tree
'***********************************************************
Function ParseCategoryNode(xml As Object) As dynamic
    o = init_category_item()

    print "ParseCategoryNode: " + xml.GetName()
    'PrintXML(xml, 5)

    'parse the curent node to determine the type. everything except
    'special categories are considered normal, others have unique types
    if xml.GetName() = "category" then
        print "category: " + xml@title + " | " + xml@description
        o.Type = "normal"
        o.Title = xml@title
        o.Description = xml@Description
        o.ShortDescriptionLine1 = xml@Title
        o.ShortDescriptionLine2 = xml@Description
        o.SDPosterURL = xml@sd_img
        o.HDPosterURL = xml@hd_img
    elseif xml.GetName() = "categoryLeaf" then
        o.Type = "normal"
    elseif xml.GetName() = "specialCategory" then
        if invalid <> xml.GetAttributes() then
            for each a in xml.GetAttributes()
                if a = "type" then
                    o.Type = xml.GetAttributes()[a]
                    print "specialCategory: " + xml@type + "|" + xml@title + " | " + xml@description
                    o.Title = xml@title
                    o.Description = xml@Description
                    o.ShortDescriptionLine1 = xml@Title
                    o.ShortDescriptionLine2 = xml@Description
                    o.SDPosterURL = xml@sd_img
                    o.HDPosterURL = xml@hd_img
                endif
            next
        endif
    else
        print "ParseCategoryNode skip: " + xml.GetName()
        return invalid
    endif

    'only continue processing if we are dealing with a known type
    'if new types are supported, make sure to add them to the list
    'and parse them correctly further downstream in the parser
    while true
        if o.Type = "normal" exit while
        if o.Type = "special_category" exit while
        print "ParseCategoryNode unrecognized feed type"
        return invalid
    end while

    'get the list of child nodes and recursed
    'through everything under the current node
    for each e in xml.GetBody()
        name = e.GetName()
        if name = "category" then
            ' print "category: " + e@title + " [" + e@description + "]"
            kid = ParseCategoryNode(e)
            kid.Title = e@title
            kid.Description = e@Description
            kid.ShortDescriptionLine1 = xml@Description
            kid.SDPosterURL = xml@sd_img
            kid.HDPosterURL = xml@hd_img
            o.AddKid(kid)
        elseif name = "categoryLeaf" then
            ' print "categoryLeaf: " + e@title + " [" + e@description + "]"
            kid = ParseCategoryNode(e)
            kid.Title = e@title
            kid.Description = e@Description
            kid.Feed = e@feed
            o.AddKid(kid)
        elseif name = "specialCategory" then
            ' print "specialCategory: " + e@title + " [" + e@description + "]"
            kid = ParseCategoryNode(e)
            kid.Title = e@title
            kid.Description = e@Description
            kid.sd_img = e@sd_img
            kid.hd_img = e@hd_img
            kid.Feed = e@feed
            o.AddKid(kid)
        endif
    next

    return o
End Function

'******************************************************
'Initialize a Category Item
'******************************************************
Function init_category_item() As Object
    o = CreateObject("roAssociativeArray")
    o.Title       = ""
    o.Type        = "normal"
    o.Description = ""
    o.Kids        = CreateObject("roArray", 100, true)
    o.Parent      = invalid
    o.Feed        = ""
    o.IsLeaf      = cn_is_leaf
    o.AddKid      = cn_add_kid
    return o
End Function

'********************************************************
'** Helper function for each node, returns true/false
'** indicating that this node is a leaf node in the tree
'********************************************************
Function cn_is_leaf() As Boolean
    if m.Kids.Count() > 0 return true
    if m.Feed <> "" return false
    return true
End Function

'*********************************************************
'** Helper function for each node in the tree to add a
'** new node as a child to this node.
'*********************************************************
Sub cn_add_kid(kid As Object)
    if kid = invalid then
        print "skipping: attempt to add invalid kid failed"
        return
     endif

    kid.Parent = m
    m.Kids.Push(kid)
End Sub

'******************************************************
'**  Video Player Example Application -- Category Feed
'**  November 2009
'**  Copyright (c) 2009 Roku Inc. All Rights Reserved.
'******************************************************

'******************************************************
' Set up the category feed connection object
' This feed provides details about top level categories
'******************************************************
Function InitCategoryFeedConnection() As Object

    conn = CreateObject("roAssociativeArray")

    ' This also must be updated in analytics brs
    conn.UrlPrefix   = "http://newspring.dev.10.0.200.164.xip.io/roku"

    conn.UrlCategoryFeed = conn.UrlPrefix + "/kidspring"

    conn.Timer = CreateObject("roTimespan")

    conn.LoadCategoryFeed    = load_category_feed
    conn.GetCategoryNames    = get_category_names

    print "created feed connection for " + conn.UrlCategoryFeed
    return conn

End Function

'*********************************************************
'** Create an array of names representing the children
'** for the current list of categories. This is useful
'** for filling in the filter banner with the names of
'** all the categories at the next level in the hierarchy
'*********************************************************
Function get_category_names(categories As Object) As Dynamic

    categoryNames = CreateObject("roArray", 100, true)

    for each category in categories.kids
        'print category.Title
        categoryNames.Push(category.Title)
    next

    return categoryNames

End Function

'******************************************************************
'** Given a connection object for a category feed, fetch,
'** parse and build the tree for the feed.  the results are
'** stored hierarchically with parent/child relationships
'** with a single default node named Root at the root of the tree
'******************************************************************
Function load_category_feed(conn As Object) As Dynamic

    http = NewHttp(conn.UrlCategoryFeed)

    Dbg("url: ", http.Http.GetUrl())

    m.Timer.Mark()
    rsp = http.GetToStringWithRetry()
    Dbg("Took: ", m.Timer)

    m.Timer.Mark()
    xml=CreateObject("roXMLElement")
    if not xml.Parse(rsp) then
         print "Can't parse feed"
        return invalid
    endif
    Dbg("Parse Took: ", m.Timer)

    m.Timer.Mark()
    if xml.category = invalid then
        print "no categories tag"
        return invalid
    endif

    if islist(xml.category) = false then
        print "invalid feed body"
        return invalid
    endif

    if xml.category[0].GetName() <> "category" then
        print "no initial category tag"
        return invalid
    endif

    topNode = MakeEmptyCatNode()
    topNode.Title = "root"
    topNode.isapphome = true

    print "begin category node parsing"

    categories = xml.GetChildElements()
    print "number of categories: " + itostr(categories.Count())
    for each e in categories
        o = ParseCategoryNode(e)
        if o <> invalid then
            topNode.AddKid(o)
            print "added new child node"
        else
            print "parse returned no child node"
        endif
    next
    Dbg("Traversing: ", m.Timer)

    return topNode

End Function

'******************************************************
'MakeEmptyCatNode - use to create top node in the tree
'******************************************************
Function MakeEmptyCatNode() As Object
    return init_category_item()
End Function

'***********************************************************
'Given the xml element to an <Category> tag in the category
'feed, walk it and return the top level node to its tree
'***********************************************************
Function ParseCategoryNode(xml As Object) As dynamic
    o = init_category_item()

    print "ParseCategoryNode: " + xml.GetName()
    'PrintXML(xml, 5)

    'parse the curent node to determine the type. everything except
    'special categories are considered normal, others have unique types
    if xml.GetName() = "category" then
        print "category: " + xml@title + " | " + xml@description
        o.Type = "normal"
        o.Title = xml@title
        o.Description = xml@Description
        o.ShortDescriptionLine1 = xml@Title
        o.ShortDescriptionLine2 = xml@Description
        o.SDPosterURL = xml@sd_img
        o.HDPosterURL = xml@hd_img
    elseif xml.GetName() = "categoryLeaf" then
        o.Type = "normal"
    elseif xml.GetName() = "specialCategory" then
        if invalid <> xml.GetAttributes() then
            for each a in xml.GetAttributes()
                if a = "type" then
                    o.Type = xml.GetAttributes()[a]
                    print "specialCategory: " + xml@type + "|" + xml@title + " | " + xml@description
                    o.Title = xml@title
                    o.Description = xml@Description
                    o.ShortDescriptionLine1 = xml@Title
                    o.ShortDescriptionLine2 = xml@Description
                    o.SDPosterURL = xml@sd_img
                    o.HDPosterURL = xml@hd_img
                endif
            next
        endif
    else
        print "ParseCategoryNode skip: " + xml.GetName()
        return invalid
    endif

    'only continue processing if we are dealing with a known type
    'if new types are supported, make sure to add them to the list
    'and parse them correctly further downstream in the parser
    while true
        if o.Type = "normal" exit while
        if o.Type = "special_category" exit while
        print "ParseCategoryNode unrecognized feed type"
        return invalid
    end while

    'get the list of child nodes and recursed
    'through everything under the current node
    for each e in xml.GetBody()
        name = e.GetName()
        if name = "category" then
            ' print "category: " + e@title + " [" + e@description + "]"
            kid = ParseCategoryNode(e)
            kid.Title = e@title
            kid.Description = e@Description
            kid.ShortDescriptionLine1 = xml@Description
            kid.SDPosterURL = xml@sd_img
            kid.HDPosterURL = xml@hd_img
            o.AddKid(kid)
        elseif name = "categoryLeaf" then
            ' print "categoryLeaf: " + e@title + " [" + e@description + "]"
            kid = ParseCategoryNode(e)
            kid.Title = e@title
            kid.Description = e@Description
            kid.Feed = e@feed
            o.AddKid(kid)
        elseif name = "specialCategory" then
            ' print "specialCategory: " + e@title + " [" + e@description + "]"
            kid = ParseCategoryNode(e)
            kid.Title = e@title
            kid.Description = e@Description
            kid.sd_img = e@sd_img
            kid.hd_img = e@hd_img
            kid.Feed = e@feed
            o.AddKid(kid)
        endif
    next

    return o
End Function

'******************************************************
'Initialize a Category Item
'******************************************************
Function init_category_item() As Object
    o = CreateObject("roAssociativeArray")
    o.Title       = ""
    o.Type        = "normal"
    o.Description = ""
    o.Kids        = CreateObject("roArray", 100, true)
    o.Parent      = invalid
    o.Feed        = ""
    o.IsLeaf      = cn_is_leaf
    o.AddKid      = cn_add_kid
    return o
End Function

'********************************************************
'** Helper function for each node, returns true/false
'** indicating that this node is a leaf node in the tree
'********************************************************
Function cn_is_leaf() As Boolean
    if m.Kids.Count() > 0 return true
    if m.Feed <> "" return false
    return true
End Function

'*********************************************************
'** Helper function for each node in the tree to add a
'** new node as a child to this node.
'*********************************************************
Sub cn_add_kid(kid As Object)
    if kid = invalid then
        print "skipping: attempt to add invalid kid failed"
        return
     endif

    kid.Parent = m
    m.Kids.Push(kid)
End Sub


Added 12hr clock to "Next Live Show" display. Commented calendar capture and display.

--HG--
extra : convert_revision : svn%3A1a989651-ce6e-ed51-1896-c0ee68ac2431/trunk%4018

function ShowLivestreamScreen( livestream )
	screen = CreateObject( "roSpringboardScreen" )
	screen.SetMessagePort( CreateObject( "roMessagePort" ) )

	'screen.setAdURL( sdAd, hdAd )
	'screen.setAdSelectable( false )

	' Disable star ratings
	screen.SetStaticRatingEnabled( false )
	' Add "Play" button
	screen.AddButton( 1, "Play" )
	' Set content to live stream (automatically parses array)
	screen.SetContent( livestream )
	' Show screen
	screen.Show()

	' Fetch calendar feed
	raw = NWM_UT_GetStringFromURL( livestream.calendar )
	' Parse calendar feed
	calendar = CreateObject( "roXMLElement" )
	if calendar.Parse( raw )
		islive = false
		nextevent = invalid
		now = CreateObject( "roDateTime" )
		now.mark()
		now_s = now.asSeconds()

		for each event in calendar.Event
			' Parse start and end times into ints
			estart = strtoi( ValidStr( event.start.GetText() ) )
			efinish = strtoi( ValidStr( event.finish.GetText() ) )
			' If nextevent is not set, set to current event (first run)
			if nextevent = invalid
				nextevent = event
			else
				' Parse event's start time
				nestart = strtoi( ValidStr( nextevent.start.GetText() ) )
				' If event starts before nextevent and finishes after current time
				if estart < nestart AND efinish > now_s
					nextevent = event
				end if
			end if
		next
		nestart = strtoi( ValidStr( nextevent.start.GetText() ) )
		nefinish = strtoi( ValidStr( nextevent.finish.GetText() ) )

		islive = ( nestart < now_s AND nefinish > now_s ) ' If event starts before now and ends after now, it is live
		desc = strReplace( nextevent.summary.GetText(), "LIVE: ", "" ) ' Remove "Live:" portions from description
		if islive
			desc = "LIVE NOW: " + desc
		else
			nextlive = CreateObject( "roDateTime" )
			nextlive.fromSeconds( nestart )
			nextlive.toLocalTime()

			' Show next live show name and date
			desc = "Next Live Show: " + desc + nl()
			desc = desc + nextlive.asDateString( "long-date" ) + " "

			' Show next live show time (24hr)
			desc = desc + itostr( nextlive.getHours() ) + ":"
			if nextlive.getMinutes() < 10 ' If less than 10 minutes, add leading zero
				desc = desc + "0"
			end if
			desc = desc + itostr( nextlive.getMinutes() )

			' Show next live show time (12hr)
			ispm = nextlive.getHours() > 11 ' If hours is more than 11, is PM
			desc = desc + " ("
			if ispm ' If is PM, subtract 12 and show hours
				if nextlive.getHours() = 12 ' If is PM and is 12, show 12
					desc = desc + "12:"
				else
					desc = desc + itostr( nextlive.getHours() - 12 ) + ":"
				end if
			else ' If is AM show hours
				if nextlive.getHours() = 0 ' If is AM and is 0, show 12
					desc = desc + "12:"
				else
					desc = desc + itostr( nextlive.getHours() ) + ":"
				end if
			end if
			if nextlive.getMinutes() < 10 ' If less than 10 minutes, add leading zero
				desc = desc + "0"
			end if
			desc = desc + itostr( nextlive.getMinutes() ) + " "
			if ispm ' If is PM, show PM
				desc = desc + "PM"
			else ' If is AM, show AM
				desc = desc + "AM"
			end if
			desc = desc + ")"
		end if

		desc = desc + nl() + nl() + "Live pre-shows generally start approximately 15 minutes before shows."

		livestream.shortDescriptionLine2 = desc
		livestream.description = desc
		screen.SetContent( livestream )
		screen.Show()
	end if

	while true
		msg = wait( 0, screen.GetMessagePort() )

		if msg <> invalid
			if msg.isScreenClosed()
				exit while
			else if msg.isButtonPressed()
				ShowVideoScreen( livestream )
			endif
		end if
	end while

	return selectedEpisode
end function

function ShowLivestreamScreen( livestream )
	screen = CreateObject( "roSpringboardScreen" )
	screen.SetMessagePort( CreateObject( "roMessagePort" ) )

	'screen.setAdURL( sdAd, hdAd )
	'screen.setAdSelectable( false )

	screen.SetStaticRatingEnabled( false )
	screen.AddButton( 1, "Play" )
	screen.SetContent( livestream )
	screen.Show()

	raw = NWM_UT_GetStringFromURL( livestream.calendar )
	calendar = CreateObject( "roXMLElement" )
	if calendar.Parse( raw )
		islive = false
		nextevent = invalid
		now = CreateObject( "roDateTime" )
		now.mark()
		now_s = now.asSeconds()

		for each event in calendar.Event
			estart = strtoi( ValidStr( event.start.GetText() ) )
			efinish = strtoi( ValidStr( event.finish.GetText() ) )
			if nextevent = invalid
				nextevent = event
			else
				nestart = strtoi( ValidStr( nextevent.start.GetText() ) )
				if estart < nestart AND efinish > now_s
					nextevent = event
				end if
			end if
		next
		nestart = strtoi( ValidStr( nextevent.start.GetText() ) )
		nefinish = strtoi( ValidStr( nextevent.finish.GetText() ) )
		islive = ( nestart < now_s AND nefinish > now_s )
		desc = strReplace( nextevent.summary.GetText(), "LIVE: ", "" )
		if islive
			desc = "LIVE NOW: " + desc
		else
			nextlive = CreateObject( "roDateTime" )
			nextlive.fromSeconds( nestart )
			nextlive.toLocalTime()
			desc = "Next Live Show:" + nl() + desc + " ("
			desc = desc + nextlive.asDateString( "long-date" ) + " "
			desc = desc + itostr( nextlive.getHours() ) + ":"
			if nextlive.getMinutes() < 10
				desc = desc + "0"
			end if
			desc = desc + itostr( nextlive.getMinutes() ) + ")"
		end if

		desc = desc + nl() + nl() + "Live pre-shows generally start approximately 15 minutes before shows."

		livestream.shortDescriptionLine2 = desc
		livestream.description = desc
		screen.SetContent( livestream )
		screen.Show()
	end if

	while true
		msg = wait( 0, screen.GetMessagePort() )

		if msg <> invalid
			if msg.isScreenClosed()
				exit while
			else if msg.isButtonPressed()
				ShowVideoScreen( livestream )
			endif
		end if
	end while

	return selectedEpisode
end function


Added simple loading dialog.

Added simple loading dialog.

Function ShowLoadingDialog( title="", showBusy = false ) As Object
	dialog = CreateObject( "roOneLineDialog" )

	dialog.SetTitle( title )
	if showBusy
		dialog.ShowBusyAnimation()
	end if
	dialog.Show()

	return dialog
End Function

error: pathspec 'source/dialogs/LoadingDialog.brs' did not match any file(s) known to git


Create WatchedStatusTracker to encapsulate tracking all watch progress.

function WatchedStatusTracker()
	instance = CreateObject("roAssociativeArray")

	instance.saveProgress = function(title as String, currentPosition as Integer)
		if(currentPosition < 30)
			return false
		end if

		RegWrite(title, currentPosition.toStr())
		return true
	end function

	instance.getProgress = function(title as String)
		savedProgress = RegRead(title)
		if savedProgress = invalid
			return 0
		end if

		return savedProgress.toInt()
	end function

	instance.hasProgress = function(title as String)
		savedProgress = RegRead(title)
		return savedProgress <> invalid
	end function

	instance.removeProgress = function(title as String)
		RegDelete(title)
	end function

	return instance
end function

error: pathspec 'source/lib/WatchedStatusTracker.brs' did not match any file(s) known to git


updates roku SG app to allow theming of hud

remote: Support for password authentication was removed on August 13, 2021.
remote: Please see https://docs.github.com/en/get-started/getting-started-with-git/about-remote-repositories#cloning-with-https-urls for information on currently recommended modes of authentication.
fatal: Authentication failed for 'https://github.com/oddnetworks/odd-sample-apps.git/'