شروع کنید

کتابخانه دسترسی برنامه‌نویسی (PAL) SDK برای Roku به ناشران با تأیید تماس مستقیم VAST (DVC) امکان کسب درآمد از برنامه‌های Roku مبتنی بر DVC را می‌دهد. PAL SDK به شما این امکان را می دهد که nonces را که رشته های رمزگذاری شده هستند از Google درخواست کنید تا بتوانید درخواست های DVC را امضا کنید. هر درخواست جریان جدید باید با یک nonce جدید تولید شده همراه باشد. با این حال، می‌توانید از همان 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>

یک رابط آزمایشی ایجاد کنید

یک صحنه را با دکمه ها برای انجام کارهای زیر پیاده سازی می کند:

  • درخواست یک nonce.
  • یک کلیک تبلیغاتی ارسال کنید.
  • یک رویداد شروع پخش را ارسال کنید.
  • یک رویداد با پایان پخش ارسال کنید.
  • فوکوس را به دکمه ویدیو منتقل کنید.

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 برای 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 را اضافه کنید که به تعاملات کاربر گوش می دهد و تغییراتی را در مؤلفه رابط ایجاد می کند:

  • برای دریافت خروجی از مؤلفه واسط، ناظرهای میدانی را روی فیلدهای رابط مرتبط با آن خروجی ها پیاده سازی کنید و آنها را به توابع پاسخ به تماس در مؤلفه اصلی متصل کنید. زمانی که کامپوننت برای اولین بار ثبت شد این کار را انجام دهید.

  • برای ارسال تعاملات کاربر به مؤلفه رابط، مشاهده‌گرهای میدانی را روی دکمه‌هایی که قبلاً ایجاد کرده‌اید پیاده‌سازی کنید تا تغییراتی را در فیلدهای رابط مرتبط با آن دستورات ایجاد کنید.

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 ، باید با پیوست کردن یک مشاهده‌گر به فیلد requestNonce درخواست‌های nonce را مدیریت کنید. با پیوست کردن این ناظر تنها پس از مقداردهی اولیه nonceLoader ، می‌توانید اطمینان حاصل کنید که درخواست‌های nonce در رشته SDK مدیریت می‌شوند، و درخواست nonce تنها در صورت وجود 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() یک مکان نگهدار برای روش خود شما برای کسب رضایت کاربر است، یا از طریق ادغام با یک 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 در "شروع پخش ویدئو" تماس بگیرید. این روش تماس‌های ناهمزمان با سرورهای 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 را به درخواست های آگهی پیوست کنید

برای استفاده از nonce که از کتابخانه PAL در یک برنامه تولیدی دریافت می‌کنید، درخواست‌های تبلیغاتی خود را فقط پس از ایجاد nonce آغاز کنید. سپس، nonce را با استفاده از پارامتر 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 nonce درخواست کند و رویدادهای جلسه پخش را با PAL SDK ثبت کند.

(اختیاری) سیگنال های Google Ad Manager را از طریق سرورهای تبلیغاتی شخص ثالث ارسال کنید

درخواست سرور آگهی شخص ثالث برای Ad Manager را پیکربندی کنید.

سرور تبلیغات شخص ثالث خود را پیکربندی کنید تا nonce را در درخواست سرور به Ad Manager لحاظ کند. در اینجا نمونه ای از تگ تبلیغاتی است که در داخل سرور تبلیغات شخص ثالث پیکربندی شده است:

'https://pubads.serverside.net/gampad/ads?givn=%%custom_key_for_google_nonce%%&...'

برای جزئیات بیشتر، راهنمای پیاده سازی سمت سرور Google Ad Manager را ببینید.

Ad Manager برای شناسایی مقدار nonce به دنبال givn= می گردد. سرور تبلیغات شخص ثالث باید از مقداری ماکرو خاص مانند %%custom_key_for_google_nonce%% پشتیبانی کند و آن را با پارامتر درخواست nonce که در مرحله قبل ارائه کردید جایگزین کند. اطلاعات بیشتر در مورد نحوه انجام این کار باید در مستندات سرور تبلیغات شخص ثالث موجود باشد.

همین! اکنون باید پارامتر nonce را از PAL SDK از طریق سرورهای واسطه خود و سپس به Google Ad Manager منتشر کنید. این امکان کسب درآمد بهتر از طریق Google Ad Manager را فراهم می کند.