ערכת ה-SDK של Programmatic Access Library (PAL) ל-Roku מאפשרת לבעלי תוכן דיגיטלי שקיבלו אישור לשימוש בקריאה ישירה ל-VAST (DVC) לייצר הכנסות מאפליקציות Roku שמבוססות על DVC. PAL SDK מאפשר לבקש מ-Google מחרוזות חד-פעמיות (nonces) מוצפנות, כדי שתוכלו לחתום על בקשות DVC. כל בקשה חדשה לשידור חייבת להיות מלווה במזהה חד-פעמי חדש שנוצר. עם זאת, אפשר לעשות שימוש חוזר באותו nonce במספר בקשות להצגת מודעות באותו מקור נתונים.
במדריך הזה מוסבר על דוגמה לשילוב של PAL SDK באפליקציה ל-Roku, לבקשת קוד חד-פעמי (nonce) ולרישום חשיפות של מודעות.
דרישות מוקדמות
לפני שמתחילים את המדריך הזה, צריך לבצע את הפעולות הבאות:
- סביבת פיתוח של Roku – מידע נוסף זמין במדריך להגדרת סביבת הפיתוח של Roku.
תיקיית פרויקט עם המבנה הבא:
./ components/ MainScene.xml PALInterface.xml SampleVideoPlayer.xml images/ icon_focus_hd.png icon_focus_sd.png icon_side_hd.png icon_side_sd.png splash_fhd.png splash_hd.png splash_sd.png source/ main.brs manifest
הגדרת הפרויקט
לפני שמשלבים את PAL SDK, צריך להגדיר את קובצי הפרויקט.
מניפסט
title=PAL for Roku Sample
subtitle=As seen in the PAL for Roku Get Started Guide
major_version=1
minor_version=0
build_version=00001
mm_icon_focus_hd=pkg:/images/icon_focus_hd.png
mm_icon_side_hd=pkg:/images/icon_side_hd.png
mm_icon_focus_sd=pkg:/images/icon_focus_sd.png
mm_icon_side_sd=pkg:/images/icon_side_sd.png
splash_screen_sd=pkg:/images/splash_sd.jpg
splash_screen_hd=pkg:/images/splash_hd.jpg
splash_screen_fhd=pkg:/images/splash_fhd.jpg
splash_color=#000000
splash_min_time=1000
ui_resolutions=hd
source/main.brs
sub Main()
showChannelSGScreen()
end sub
sub showChannelSGScreen()
screen = CreateObject("roSGScreen")
m.port = CreateObject("roMessagePort")
screen.setMessagePort(m.port)
m.scene = screen.CreateScene("MainScene")
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
יצירת נגן וידאו לדוגמה
הרכיב SampleVideoPlayer
פשוט עוטף רכיב וידאו כדי לתעד לחיצות על השלט הרחוק. משנים את ההגדרה של onKeyEvent
כך שכאשר המיקוד של השלט הרחוק מועבר לנגן הווידאו או לנגן המודעות, כל לחיצה נוספת על מקש (למעלה, למטה, שמאלה, ימינה, קליק וכו') תתועד ותישלח ל-PAL.
components/SampleVideoPlayer.xml
<?xml version="1.0" encoding="utf-8" ?>
<component name="SampleVideoPlayer" extends="Video">
<interface>
<field id="pressedKey" type="String" />
</interface>
<script type="text/brightscript">
<![CDATA[
Function onKeyEvent(key as String, press as Boolean) as Boolean
If press
m.top.pressedKey = key
End If
return True
End Function
]]>
</script>
<children>
<Label text="VIDEO" color="0xFFFFFFFF" font="font:MediumBoldSystemFont" horizAlign="center" vertAlign="center" width="720" height="480" />
</children>
</component>
יצירת ממשק בדיקה
הטמעת סצנה עם לחצנים לביצוע הפעולות הבאות:
- שולחים בקשה למזהה חד-פעמי.
- שולחים קליק על מודעה.
- שולחים אירוע של הפעלת ההפעלה.
- שולחים אירוע של סיום ההפעלה.
- מעבירים את המיקוד ללחצן הסרטון.
components/MainScene.xml
<?xml version="1.0" encoding="utf-8" ?>
<component name="MainScene" extends="Scene" initialFocus="requestNonceButton">
<children>
<ButtonGroup>
<button text="Request Nonce" id="requestNonceButton" />
<button text="Send Ad Click" id="sendAdClickButton" />
<button text="Send Playback Start" id="sendPlaybackStartButton" />
<button text="Send Playback End" id="sendPlaybackEndButton" />
<button text="Transfer Focus to Video" id="transferFocusToVideoButton" />
</ButtonGroup>
<SampleVideoPlayer id="YourVideoPlayer" width="720" height="480" focusable="true" />
</children>
</component>
יצירת רכיב של ממשק SDK
כדי לתקשר בין הסצנה הראשית ל-PAL SDK, צריך רכיב שמכיל קוד אסינכרוני. הדבר נחוץ כי PAL SDK שולח בקשות חיצוניות לרשת, שלא יכולות להתרחש בשרשור הראשי באפליקציית Roku. כדי לשלוח נתונים לרכיב הזה, צריך ממשק שמגדיר את הנתונים שהרכיב שולח ומקבל.
components/PALInterface.xml
<?xml version="1.0" encoding="utf-8" ?>
<component name="PALInterface" extends="Task">
<interface>
<!--Commands-->
<field id="requestNonce" type="Boolean" />
<field id="sendAdClick" type="Boolean" />
<field id="sendAdTouchKey" type="String" />
<field id="sendPlaybackStart" type="Boolean" />
<field id="sendPlaybackEnd" type="Boolean" />
<field id="endThread" type="Boolean" />
<!--Responses-->
<field id="errors" type="stringarray" />
<field id="nonce" type="String" />
</interface>
</component>
ייבוא של IMA SDK
כדי להשתמש בספריית PAL, צריך לדרוש את IMA SDK for Roku במניפסט של האפליקציה ולייבא אותו לרכיב PALInterface
.
מניפסט
... splash_color=#000000 splash_min_time=1000 ui_resolutions=hd bs_libs_required=googleima3
components/PALInterface.xml
<?xml version = "1.0" encoding = "utf-8" ?> <component name="PALInterface" extends="Task"> <interface> <!-- commands --> <field id="requestNonce" type="Boolean" /> <field id="sendAdClick" type="Boolean" /> <field id="sendAdTouchKey" type="String" /> <field id="sendPlaybackStart" type="Boolean" /> <field id="sendPlaybackEnd" type="Boolean" /> <field id="endThread" type="Boolean" /> <!-- responses --> <field id="errors" type="stringarray" /> <field id="nonce" type="String" /> </interface> <script type = "text/brightscript"> <![CDATA[ Library "IMA3.brs" ]]> </script> </component>
הפעלת רכיב ממשק מהסצנה
בשלב הבא מוסיפים את קוד BrightScript שמקשיב לאינטראקציות של משתמשים ומפעיל שינויים ברכיב הממשק:
כדי לקבל פלט מרכיב הממשק, מטמיעים משגיחי שדות בשדות הממשק שמשויכים לפלטים האלה ומצרפים אותם לפונקציות קריאה חוזרת (callback) ברכיב הראשי. צריך לעשות זאת בפעם הראשונה שמירת הרכיב.
כדי לשלוח אינטראקציות של משתמשים לרכיב הממשק, מטמיעים משגיחי שדות בלחצנים שיצרתם מקודם כדי להפעיל שינויים בשדות הממשק המשויכים לפקודות האלה.
components/MainScene.xml
<?xml version="1.0" encoding="utf-8" ?> <component name="MainScene" extends="Scene" initialFocus="requestNonceButton"> <children> <ButtonGroup> <button text="Request Nonce" id="requestNonceButton" /> <button text="Send Ad Click" id="sendAdClickButton" /> <button text="Send Ad Touch" id="sendAdTouchButton" /> <button text="Send Playback Start" id="sendPlaybackStartButton" /> <button text="Send Playback End" id="sendPlaybackEndButton" /> </ButtonGroup> <Video id="YourVideoPlayer" width="720" height="480" focusable="true" /> </children> <script type="text/brightscript"> <![CDATA[ Function init() requestNonceButton = m.top.findNode("requestNonceButton") requestNonceButton.observeField("buttonSelected", "requestNonce") sendAdClickButton = m.top.findNode("sendAdClickButton") sendAdClickButton.observeField("buttonSelected", "sendAdClick") sendPlaybackStart = m.top.findNode("sendPlaybackStartButton") sendPlaybackStart.observeField("buttonSelected", "sendPlaybackStart") sendPlaybackEnd = m.top.findNode("sendPlaybackEndButton") sendPlaybackEnd.observeField("buttonSelected", "sendPlaybackEnd") loadImaSdk() End Function ' Initialize SDK Interface component and attach callbacks to field observers. Function loadImaSdk() as Void m.sdkTask = createObject("roSGNode", "PALInterface") m.sdkTask.observeField("errors", "onSdkLoadedError") m.sdkTask.observeField("nonce", "onNonceLoaded") print "Running load IMA task." m.sdkTask.control = "RUN" End Function Sub onSdkLoadedError(message as Object) print "----- errors in the sdk loading process --- ";message.getData() End Sub ' Callback triggered when Nonce is loaded. Sub onNonceLoaded(message as Object) nonce = m.sdkTask.nonce print "onNonceLoaded ";nonce End Sub Function requestNonceButtonPressed() As Void print "Request Nonce" ' Inform the SDK interface component to request a nonce. m.sdkTask.requestNonce = True End Function ' Action triggered on player start, either from user action or autoplay. Function sendPlaybackStart() As Void m.sdkTask.sendPlaybackStart = True End Function ' Action triggered on player end, either when content ends or the user exits ' playback of this content. Function sendPlaybackEnd() As Void m.sdkTask.sendPlaybackEnd = True End Function ]]> </script> </component>
הוספת שיטות להעברת המיקוד
בשלב הבא, מתעדים את הקשות המשתמש כדי להעביר את המיקוד אל רכיב הווידאו וממנו.
components/MainScene.xml
... <script type="text/brightscript"> <![CDATA[ Function init() ... m.sendPlaybackStart = m.top.findNode("sendPlaybackStartButton") m.sendPlaybackStart.observeField("buttonSelected", "sendPlaybackStart") m.sendPlaybackEnd = m.top.findNode("sendPlaybackEndButton") m.sendPlaybackEnd.observeField("buttonSelected", "sendPlaybackEnd") m.transferFocusToVideoButton = m.top.findNode("transferFocusToVideoButton") m.transferFocusToVideoButton.observeField("buttonSelected", "transferFocusToVideo") ' Your video player set up to handle key press events. m.video = m.top.findNode("YourVideoPlayer") m.video.observeField("pressedKey", "onVideoKeyPress") loadImaSdk() End Function ... ' Action triggered on player end, either when content ends or the user exits ' playback of this content. Function sendPlaybackEnd() As Void m.sdkTask.sendPlaybackEnd = True End Function Function transferFocusToVideo() As Void m.video.setFocus(true) End Function Function onVideoKeyPress() As Void key = m.video.pressedKey If key = "" Return End If m.sdkTask.sendAdTouchKey = key ' If back or up is pressed, transfer focus back up to the buttons. If key = "back" or key = "up" m.transferFocusToVideoButton.setFocus(true) End If ' Reset so that we get the next key press, even if it's a repeat of the last ' key. m.video.pressedKey = "" End Function ]]> </script> </component>
אתחול ה-PAL SDK ויצירת nonceLoader
עכשיו אפשר להתחיל לפתח את הלוגיקה המרכזית של הטמעת PAL SDK. קודם כול, צריך לאתחל את ה-SDK משרשור נפרד.
components/PALInterface.xml
... <script type = "text/brightscript"> <![CDATA[ Library "IMA3.brs" Sub init() ' It is not possible to access roUrlTransfer on the main thread. Setting ' functionName to a function and then setting control to "RUN" causes that 'function to run on a separate thread. m.top.functionName = "runPalThread" ' Loads the SDK on the current thread if it is not yet loaded. ' This blocks execution of other functions on this thread until the SDK is loaded. If m.sdk = Invalid m.sdk = new_imaSdk() End If m.nonceLoader = m.sdk.CreateNonceLoader() End Sub ' Starts the player event loop. This loop only terminates when "endThread" is sent. Function runPalThread() as Void ' Used for the player life cycle loop. m.top.endThread = False port = CreateObject("roMessagePort") End Function ]]> </script> </component>
עיבוד בקשות למזהה חד-פעמי
אחרי שיוצרים את nonceLoader
, צריך לטפל בבקשות ל-nonce על ידי צירוף משתמש מעקב לשדה requestNonce
. אם מחברים את הצופה הזה רק אחרי שמפעילים את nonceLoader
, אפשר לוודא שבקשות למזהה חד-פעמי יטופלו בשרשור של ה-SDK, ושאפשר לשלוח בקשה למזהה חד-פעמי רק אם יש nonceLoader
תקין.
components/PALInterface.xml
... ' Starts the player event loop. This loop only terminates when "endThread" is sent. Function runPalThread() as Void ' Used for the player life cycle loop. m.top.endThread = False port = CreateObject("roMessagePort") ' Now that the nonceLoader exists, begin listening for nonce requests. m.top.observeField("requestNonce", m.port) End Function ' Requests a nonce from the PAL SDK. Function requestNonce() as Void nonceRequest = m.sdk.CreateNonceRequest() m.nonceManager = m.nonceLoader.loadNonceManager(nonceRequest) m.top.nonce = nonceManager.getNonce() End Function ]]> </script> </component>
איך אוספים מידע על הסכמה לאחסון
הערך שמוגדר כברירת מחדל ל-NonceRequest.storageAllowed
הוא true
, אבל אפשר לשנות את הערך הזה אחרי שמקבלים את ההסכמה המתאימה. השיטה getConsentToStorage()
היא placeholder לשיטה שלכם לקבלת הסכמה מהמשתמשים, באמצעות שילוב עם CMP או על סמך שיטות אחרות לטיפול בהסכמה לשמירה.
components/PALInterface.xml
... <script type = "text/brightscript"> <![CDATA[ Library "IMA3.brs" Sub init() ' It is not possible to access roUrlTransfer on the main thread. Setting ' functionName to a function and then setting control to "RUN" causes that 'function to run on a separate thread. m.top.functionName = "runPalThread" ' Loads the SDK on the current thread if it is not yet loaded. ' This blocks execution of other functions on this thread until the SDK is loaded. If m.sdk = Invalid m.sdk = new_imaSdk() End If m.isConsentToStorage = getConsentToStorage() m.nonceLoader = m.sdk.CreateNonceLoader() End Sub ... ' Requests a nonce from the PAL SDK. Function requestNonce() as Void nonceRequest = m.sdk.CreateNonceRequest() ' Include changes to storage consent here. nonceRequest.storageAllowed = m.isConsentToStorage m.nonceManager = m.nonceLoader.loadNonceManager(nonceRequest) m.top.nonce = nonceManager.getNonce() End Function
האזנה לאותות של מחזור החיים של הנגן
כדי לאפשר לשילוב של PAL לשלוח אותות בצורה תקינה, צריך להגדיר לולאה להאזנה לאותות של מחזור החיים של הנגן.
components/PALInterface.xml
... ' Now that the nonceLoader exists, begin listening for nonce requests. m.top.observeField("requestNonce", m.port) m.top.observeField("sendAdClick", m.port) m.top.observeField("sendAdTouchKey", m.port) m.top.observeField("sendPlaybackStart", m.port) m.top.observeField("sendPlaybackEnd", m.port) ' Setting endThread to true causes the while loop to exit. m.top.observeField("endThread", m.port) While Not m.top.endThread message = m.port.waitMessage(1000) If message = Invalid pollManager() Else If message.getField() = "requestNonce" And m.top.requestNonce = True requestNonce() m.top.requestNonce = False Else If message.getField() = "sendAdClick" And m.top.sendAdClick = True sendAdClick() m.top.sendAdClick = False Else If message.getField() = "sendAdTouchKey" And m.top.sendAdTouchKey <> "" sendAdTouch(m.top.sendAdTouchKey) m.top.sendAdTouchKey = "" Else If message.getField() = "sendPlaybackStart" And m.top.sendPlaybackStart = True sendPlaybackStart() m.top.sendPlaybackStart = False Else If message.getField() = "sendPlaybackEnd" And m.top.sendPlaybackEnd = True sendPlaybackEnd() m.top.sendPlaybackEnd = False End If End While End Function Function pollManager() as Void If m.nonceManager <> Invalid m.nonceManager.poll() End If End Function ' Requests a nonce from the PAL SDK. Function requestNonce() as Void nonceRequest = m.sdk.CreateNonceRequest() m.nonceManager = m.nonceLoader.loadNonceManager(nonceRequest) m.top.nonce = nonceManager.getNonce() End Function ]]> </script> </component>
רישום של מאזינים ל-sendPlaybackStart
, ל-sendPlaybackEnd
, ל-sendAdClick
ול-sendAdTouch
לאחר מכן, קוראים ל-sendPlaybackStart
ב-'video player start'. השיטה הזו מתחילה קריאות אסינכרוניות לשרתים של Google כדי לאסוף את האות הנדרש למעקב ולזיהוי של IVT. מחייגים למספר sendPlaybackEnd
בסיום ההפעלה.
קריאה ל-sendAdClick
בתגובה לקליק על מודעה. לאחר מכן, צריך להפעיל את sendAdTouch
לאירועי מגע או לחיצה של משתמשים שלא מובילים לקליק-תור.
components/PALInterface.xml
... ' Requests a nonce from the IMA SDK. Function requestNonce() as Void nonceRequest = m.sdk.CreateNonceRequest() m.nonceManager = m.nonceLoader.loadNonceManager(nonceRequest) m.top.nonce = nonceManager.getNonce() End Function ' Registers an ad click using the IMA SDK. Function sendAdClick() as Void If m.nonceManager <> Invalid m.nonceManager.sendAdClick() End If End Function ' Registers an ad touch event using the IMA SDK. Function sendAdTouch(touch as String) as Void If m.nonceManager <> Invalid m.nonceManager.sendAdTouch(touch) End If End Function ' Registers the start of playback using the IMA SDK. Function sendPlaybackStart() as Void If m.nonceManager <> Invalid m.nonceManager.sendPlaybackStart() End If End Function ' Registers the end of playback using the IMA SDK. Function sendPlaybackEnd() as Void If m.nonceManager <> Invalid m.nonceManager.sendPlaybackEnd() End If End Function ]]> </script> </component>
צירוף המזהה החד-פעמי לבקשות להצגת מודעות
כדי להשתמש במזהה החד-פעמי (nonce) שאתם מקבלים מספריית PAL באפליקציה בסביבת הייצור, צריך להתחיל את בקשות הצגת המודעות רק אחרי שיוצרים את המזהה החד-פעמי. לאחר מכן, מוסיפים את המזהה החד-פעמי לתג המודעה באמצעות הפרמטר u_paln
.
components/MainScene.xml
... ' Callback triggered when Nonce is loaded. Sub onNonceLoaded(message as Object) nonce = m.sdkTask.nonce print "onNonceLoaded ";nonce makeAdRequest(nonce) End Sub Sub makeAdRequest(nonce) ' Sample ad tag URL used in this sample. Your apps method of getting this ' URL will likely be different. adTag = "https://pubads.g.doubleclick.net/gampad/ads?iu=/124319096/external/single_ad_samples" preparedTag = adTag + "&u_paln=" + nonce ' Implement custom ad request logic here. Print "ad tag with nonce ";preparedTag End Sub ...
זהו! עכשיו יש לכם אפליקציה ל-Roku שיכולה לבקש מפתח PAL חד-פעמי ולרשום אירועים של סשן הפעלה באמצעות PAL SDK.
(אופציונלי) שליחת אותות מ-Google Ad Manager דרך שרתי מודעות של צד שלישי
מגדירים את הבקשה של שרת המודעות של הצד השלישי ל-Ad Manager.
מגדירים את שרת המודעות של הצד השלישי כך שיכלול את המזהה החד-פעמי בבקשה של השרת אל Ad Manager. דוגמה לתג מודעה שמוגדר בתוך שרת המודעות של הצד השלישי:
'https://pubads.serverside.net/gampad/ads?givn=%%custom_key_for_google_nonce%%&...'
פרטים נוספים זמינים במדריך להטמעה בצד השרת של Google Ad Manager.
מערכת Ad Manager מחפשת את givn=
כדי לזהות את ערך ה-nonce. שרת המודעות של הצד השלישי צריך לתמוך במאקרו משלו, כמו %%custom_key_for_google_nonce%%
, ולהחליף אותו בפרמטר השאילתה של המזהה החד-פעמי שסיפקתם בשלב הקודם. מידע נוסף על האופן שבו מבצעים את הפעולה הזו אמור להיות זמין במסמכי העזרה של שרת המודעות של הצד השלישי.
זהו! עכשיו הפרמטר של המזהה החד-פעמי אמור להופיע מ-PAL SDK, דרך השרתים המקשרים ואז ב-Google Ad Manager. כך תוכלו לייצר הכנסות טובות יותר דרך Google Ad Manager.