Roku Developer Program

Developers and content creators—a complete solution for growing an audience directly.
cancel
Showing results for 
Show  only  | Search instead for 
Did you mean: 
PhotoRonin
Level 8

Transitioning Screensaver to Externally-Pulled Images

Howdy developers!  I'm excited to get some feedback on my (hopefully) simple issue.  I've built a few screensavers based on the originally-posted code from a couple of years ago (https://github.com/rokudev/samples/tree/master/screen%20savers), but would like to transition to pulling my images from my server instead of having to mash them into the package.

I need to place code in there which goes out and pulls the external content, but am struggling with where this code would go (which BRS file?).

The note that I got from the support folks was that it may need to be a "task node," but I'm still not understanding the concept they're trying to put across after watching the videos and reading their help content.

I believe I've coded this correctly, but just need to know which file to place it in, and how to call it...  

Here is the code that I've put together, into source/main.brs (below).  You will see that I've created a sub called "getPhotoRoninContent" and am calling it.  

I'm not sure this is correct Smiley Happy

Function RunScreenSaver(params As Object) As Object 'Required entry point for screensavers
    getPhotoRoninContent()
    Main()
End Function


sub Main()
    screen = createObject("roSGScreen")
    port = createObject("roMessagePort")
    port2 =  createObject("roMessagePort")
    screen.setMessagePort(port)

    m.global = screen.getGlobalNode() 'Creates (Global) variable MyField
    m.global.AddField("MyField", "int", true)
    m.global.MyField = 0
    m.global.AddField("PicSwap", "int", true) 'Creates (Global) variable PicSwap
    m.global.PicSwap = 0

    scene = screen.createScene("ScreensaverFade") 'Creates scene ScreensaverFade
    screen.show()

     while(true) 'Message Port that fires every 16 seconds to change value of MyField if the screen isn't closed
        msg = wait(16000, port)
        if (msg <> invalid)
            msgType = type(msg)
            if msgType = "roSGScreenEvent"
                if msg.isScreenClosed() then return
            end if
        else
            m.global.MyField += 10
            msg = wait(2500, port2) 'Message port that fires 4 seconds after MyField is changed. Must be set to different port than other wait function or it will interfere.
            m.global.PicSwap += 10
        end if
    end while
end sub

sub getPhotoRoninContent()
    fs=createobject("rofilesystem")
    xfer=createobject("roUrlTransfer")
    xfer.SetCertificatesFile("common:/certs/ca-bundle.crt")
    if not fs.exists("cachefs:/photoroninpic-01.jpg") then
        xfer.seturl("https://photos.smugmug.com/photos/i-DZ3RNCh/0/c6888ff8/X3/i-DZ3RNCh-X3.jpg")
        xfer.gettofile("cachefs:/photoroninpic-01.jpg")
    end if
end sub

 

Tags (1)
0 Kudos
8 REPLIES 8\
RokuJoel
Roku Employee
Roku Employee

Re: Transitioning Screensaver to Externally-Pulled Images

1. you have a Main in function RunScreensaver()   - RunScreensaver is Main() for screensavers, put your Main() code into RunScreensaver. You can rename RunScreensaver to Main() while testing and rename back   later.  

2. It looks like you are trying to download your images before running the Scenegraph portion of your screensaver. That can be done the way you are doing it but may slow down the launch time of your screensaver, and you won't be able to update content dynamically after launch. 

3. Task node is separate .brs file you would put in the /components folder, you would typically launch it from your scene (ScreensaverFade), it could be a running task that sits there in the background waiting for a list of images to download, or a task that doesn't execute until you send it a run command. I suggest taking a look at the URL fetcher in our On Device Authentication sample on Github. Note that task nodes must rename themselves in order to run in a separate thread from the Render thread, as you don't want to slow down the Render thread:

as soon as it is instantiated in MainScene.brs:
 
m.uriFetcher = createObject("roSGNode", "UriFetcher")
 
This task node renames itself:
 
m.top.functionName = "go"
and then sets itself to run:

m.top.control = "RUN"
 
 - all of this is done in the Init()
 
 
 
 
 
PhotoRonin
Level 8

Re: Transitioning Screensaver to Externally-Pulled Images

Thank you for the advice! Some of it I'm not entirely clear on yet, but am figuring it out.  I usually work in PowerShell or VBScript, so I'm not yet familiar with BrightScript (and very confused about it at the same time).

What I gleaned from above was a few things:


@RokuJoel wrote:

1. you have a Main in function RunScreensaver()   - RunScreensaver is Main() for screensavers, put your Main() code into RunScreensaver. You can rename RunScreensaver to Main() while testing and rename back   later.  


 - Got it.  I'm using the Roku-built screensaver "template", and they have it set this way.  I will consider changing it!


2. It looks like you are trying to download your images before running the Scenegraph portion of your screensaver. That can be done the way you are doing it but may slow down the launch time of your screensaver, and you won't be able to update content dynamically after launch. 


 -  I'm aware that the way I had it put the tasks in serial and caused performance degradation.  Hence why I am looking for advice on how to separate the task Smiley Happy


3. Task node is separate .brs file you would put in the /components folder, you would typically launch it from your scene (ScreensaverFade), it could be a running task that sits there in the background waiting for a list of images to download, or a task that doesn't execute until you send it a run command. I suggest taking a look at the URL fetcher in our On Device Authentication sample on Github. Note that task nodes must rename themselves in order to run in a separate thread from the Render thread, as you don't want to slow down the Render thread:

as soon as it is instantiated in MainScene.brs:
 
m.uriFetcher = createObject("roSGNode", "UriFetcher")
 
This task node renames itself:
 
m.top.functionName = "go"
and then sets itself to run:

m.top.control = "RUN"
 
 - all of this is done in the Init()

This is where I'm struggling, and lack some key understanding. 

Here's my intended result:

  • The screensaver launches using an image that it has stored in the package file (to reduce the first screen load).
  • While first launched and starting on that screen, it grabs the other images that may or may not still be in the cache (checking for each before going to retrieve).
  • This allows it to then move on to the next image, now downloaded (hopefully!)

Based on what you've written and what I gleaned from the examples you sent, I've made the following adjustments, which I'm not sure are accurate:

  • Created 2 files in /components:
    • UriFetcher.brs
      function init()
          fs=createobject("rofilesystem")
          xfer=createobject("roUrlTransfer")
          xfer.SetCertificatesFile("common:/certs/ca-bundle.crt")
          if not fs.exists("cachefs:/photoroninpic-01.jpg") then
              xfer.seturl("https://photos.smugmug.com/photos/i-DZ3RNCh/0/c6888ff8/X3/i-DZ3RNCh-X3.jpg")
              xfer.gettofile("cachefs:/photoroninpic-01.jpg")
          end if
      end function
    • UriFetcher.xml
      <?xml version="1.0" encoding="utf-8" ?>
      
      <component name="UriFetcher" extends="Task" >
      
      <interface>
      	<field id="request" type="assocarray"/>
      	<field id="jobsByIdField" type="int" />
      	<field id="urlTransferPoolField" type="int" /> 
      </interface>
      
      <script type="text/brightscript" uri="pkg://components/UriFetcher.brs"/>
      
      <children/>
      
      </component>

...but I think there's an issue with this, as I'm not understanding the renamed task?

0 Kudos
necrotek
Level 12

Re: Transitioning Screensaver to Externally-Pulled Images

If you have a list of image urls you can make this simpler by creating a timer task and swapping out the poster uri on timer task fire and let the poster node handle downloading and displaying images. Unless the point this exercise is to get more familiar with setting up url transfer.

0 Kudos
necrotek
Level 12

Re: Transitioning Screensaver to Externally-Pulled Images

<script type="text/brightscript" uri="pkg://components/UriFetcher.brs"/>
should be
<script type="text/brightscript" uri="pkg:/components/UriFetcher.brs"/>

extra '/' makes it not the correct path. 

 

UriFetcher.brs init should have the line 

function init()
m.top.functionName = "go"
end function
outside of init should have
function go()
'do your url tranfer and any other processing here
end function

in your scene
task=createobject("rosgnode","UriFetcher") 'initiate the task node
task.observefield("state", "onTaskDone") 'observe state of task
task.control="RUN" ' run the task this will call go()

function onTaskDone(event)
state=event.getdata()
if state="done"
'do something
end if
end function

 

PhotoRonin
Level 8

Re: Transitioning Screensaver to Externally-Pulled Images

Thank you for the feedback! I'm learning more and more thanks to you folks Smiley Happy My main intention/concerns around the screensaver are:

  • Keeping the package under 4MB
  • Ensuring that the images are there when the screensaver starts
    • Downloading them all in the background would be ideal, but then ensuring that the images are actually available when the screensaver looks for them!

@necrotek wrote:
<script type="text/brightscript" uri="pkg://components/UriFetcher.brs"/>
should be
<script type="text/brightscript" uri="pkg:/components/UriFetcher.brs"/>

extra '/' makes it not the correct path. 

While I would agree with you, that is a direct copy of the other instances that come from the template.  When I remove it I get brightscript errors.  My assumption is that like with other coding languages, that end "/" closes off that <script> component, so that </script> isn't needed.


UriFetcher.brs init should have the line 

function init()
m.top.functionName = "go"
end function
outside of init should have
function go()
'do your url tranfer and any other processing here
end function

Done!


in your scene 
task=createobject("rosgnode","UriFetcher") 'initiate the task node
task.observefield("state", "onTaskDone") 'observe state of task
task.control="RUN" ' run the task this will call go()

function onTaskDone(event)
state=event.getdata()
if state="done"
'do something
end if
end function

 


When you say "in your scene", are you referring to "main.brs"?  If so, would this be a separate "sub" in there besides "Main()"? (see below for the current file contents)

Function RunScreenSaver(params As Object) As Object 'Required entry point for screensavers
    Main()
End Function


sub Main()
    screen = createObject("roSGScreen")
    port = createObject("roMessagePort")
    port2 =  createObject("roMessagePort")
    screen.setMessagePort(port)

    m.global = screen.getGlobalNode() 'Creates (Global) variable MyField
    m.global.AddField("MyField", "int", true)
    m.global.MyField = 0
    m.global.AddField("PicSwap", "int", true) 'Creates (Global) variable PicSwap
    m.global.PicSwap = 0

    scene = screen.createScene("ScreensaverFade") 'Creates scene ScreensaverFade
    screen.show()

     while(true) 'Message Port that fires every 16 seconds to change value of MyField if the screen isn't closed
        msg = wait(16000, port)
        if (msg <> invalid)
            msgType = type(msg)
            if msgType = "roSGScreenEvent"
                if msg.isScreenClosed() then return
            end if
        else
            m.global.MyField += 10
            msg = wait(2500, port2) 'Message port that fires 4 seconds after MyField is changed. Must be set to different port than other wait function or it will interfere.
            m.global.PicSwap += 10
        end if
    end while
end sub

I would also be interested in finding someone whose time I could pay for to help me with a remote session and answer my questions Smiley Happy  I learn much better hands-on and have pretty extensive coding background...just not this...and would love to pick someone's brain!

0 Kudos
PhotoRonin
Level 8

Re: Transitioning Screensaver to Externally-Pulled Images


@necrotek wrote:

If you have a list of image urls you can make this simpler by creating a timer task and swapping out the poster uri on timer task fire and let the poster node handle downloading and displaying images. Unless the point this exercise is to get more familiar with setting up url transfer.


I don't understand the implementation of this, though the concept is something that I'm vaguely familiar with.  Ideally it would be great to have an array with the image URL's in it that can be actioned...is there an array example that I would use as a starting point?

0 Kudos
necrotek
Level 12

Re: Transitioning Screensaver to Externally-Pulled Images

remove the extra '/' from here

uri="pkg:/components/UriFetcher.brs"

you had

uri="pkg://components/UriFetcher.brs" 'this path does not exist in your file structure

this is not an http:// path

 

sub RunScreenSaver() 'this is the equivalent of main() for a screensaver.
screen = createObject("roSGScreen") ' port = createObject("roMessagePort") screen.setMessagePort(port)
scene = screen.createScene("YourSceneName") 'this is your scene this will be loaded from components folder and where you would create the task 'scene = screen.createScene("ScreensaverFade") is the "scene" from your example if you want to add the task there
screen.show()
while(true)
msg = wait(0, m.port)
msgType = type(msg)
if msgType = "roSGScreenEvent"
if msg.isScreenClosed() then return
end if
end while

end sub

Learn about scenegraph here.

https://developer.roku.com/docs/developer-program/core-concepts/core-concepts.md

0 Kudos
necrotek
Level 12

Re: Transitioning Screensaver to Externally-Pulled Images

In the screensaver fade example you are using is using a poster node 'm.BackgroundArt' 

https://developer.roku.com/docs/references/scenegraph/renderable-nodes/poster.md

the poster node can load images included in the package or from a url

(note make sure your images are not larger than the display size. limit to 1080p/720p as the device supports by setting correct loadwidth , loadheight and  poster.loadDisplayMode.limitSize )

 

Function changeBackground() as Void 'Function that changes the background image to the next image in the m.pictures array
    if (m.count=4)
        m.count = -1
    end if
    m.count += 1
    m.BackgroundArt.uri = m.pictures[m.count]
End Function

Function FadeAnimation() as Void 'Function that starts the FadeAnimation transition animation
    m.FadeAnimation.control = "start"
End Function

Function init()
    m.pictures = [] ' For loop to load images into m.pictures array

'This creates a list of images that are included in the package and populates the above array that is then used to display in the screensaver for i = 1 to 5 m.pictures.push("pkg:/images/" + i.toStr() +".jpg") 'Loads images 1-5 in image folder into m.pictures array end for

'you can replace m.pictures with a list of items you want to display
m.pictures = ["https://yourImageHost.com/image1.jpg"
"https://yourImageHost.com/image2.jpg"
"https://yourImageHost.com/image3.jpg"
"https://yourImageHost.com/image4.jpg"
]

'the poster.uri will be updated when the screensaver fires on port wait time
m.count = 0 m.FadeAnimation = m.top.findNode("FadeAnimation") 'Sets pointer to FadeAnimation node m.BackgroundArt = m.top.findNode("BackgroundArt") 'Sets pointer to BackgroundArt node m.BackgroundArt.uri = m.pictures[0] 'Sets Background art to first picture m.global.observeField("PicSwap", "changeBackground") 'field Observer that calls changeBackground() function everytime the value of PicSwap is changed m.global.observeField("MyField", "FadeAnimation") 'field Observer that calls FadeAnimation() function everytime the value of MyField is changed End Function

  

0 Kudos