שילוב של התכונה 'המשך צפייה' ב-Android TV

book_path: /distribute/other-docs/_book.yaml project_path: /distribute/other-docs/_project.yaml

במדריך הזה נסביר איך לשלב את התכונה 'המשך צפייה' באפליקציה ל-Android TV באמצעות Engage SDK.

עבודה מקדימה

פועלים לפי ההוראות שבקטע עבודת הכנה במדריך לתחילת העבודה.

שילוב

יצירת ישויות

ב-SDK מוגדרות ישויות שונות שמייצגות כל סוג פריט. אוסף ההמשכים תומך בישויות הבאות:

  1. MovieEntity
  2. TvEpisodeEntity
  3. LiveStreamingVideoEntity
  4. VideoClipEntity

מציינים את כתובות ה-URI הספציפיות לפלטפורמה ואת תמונות הפוסטר של הישויות האלה.

בנוסף, אם עדיין לא עשיתם זאת, צריך ליצור כתובות URI להפעלה לכל פלטפורמה – כמו Android TV, ‏ Android או iOS. לכן, כשמשתמש ממשיך לצפות בכל פלטפורמה, האפליקציה משתמשת ב-URI ממוקד של הפעלה כדי להפעיל את תוכן הווידאו.

// Required. Set this when you want continue watching entities to show up on
// Google TV
val playbackUriTv = PlatformSpecificUri.Builder()
    .setPlatformType(PlatformType.TYPE_ANDROID_TV)
    .setActionUri(Uri.parse("https://www.example.com/entity_uri_for_tv"))
    .build()

// Required. Set this when you want continue watching entities to show up on
// Google TV Android app, Entertainment Space, Playstore Widget
val playbackUriAndroid = PlatformSpecificUri.Builder()
    .setPlatformType(PlatformType.TYPE_ANDROID_MOBILE)
    .setActionUri(Uri.parse("https://www.example.com/entity_uri_for_android"))
    .build()

// Optional. Set this when you want continue watching entities to show up on
// Google TV iOS app
val playbackUriIos = PlatformSpecificUri.Builder()
    .setPlatformType(PlatformType.TYPE_IOS)
    .setActionUri(Uri.parse("https://www.example.com/entity_uri_for_ios"))
    .build()

val platformSpecificPlaybackUris =
    Arrays.asList(playbackUriTv, playbackUriAndroid, playbackUriIos)

תמונות של פוסטרים צריכות לכלול URI ומידות בפיקסלים (גובה ורוחב). כדי להתאים את התוכן למכשירים שונים, כדאי לספק כמה תמונות פוסטר. עם זאת, חשוב לוודא שלכל התמונות יש יחס גובה-רוחב של 16:9 וגובה מינימלי של 200 פיקסלים, כדי שהישות "המשך צפייה" תוצג בצורה נכונה, במיוחד בחבילת הבידור של Google. יכול להיות שלא יוצגו תמונות שהגובה שלהן קטן מ-200 פיקסלים.

val images = Arrays.asList(
    Image.Builder()
        .setImageUri(Uri.parse("http://www.example.com/entity_image1.png"))
        .setImageHeightInPixel(300)
        .setImageWidthInPixel(169)
        .build(),
    Image.Builder()
        .setImageUri(Uri.parse("http://www.example.com/entity_image2.png"))
        .setImageHeightInPixel(640)
        .setImageWidthInPixel(360)
        .build()
    // Consider adding other images for different form factors
)

MovieEntity

בדוגמה הזו מוסבר איך ליצור MovieEntity עם כל שדות החובה:

val movieEntity = MovieEntity.Builder()
   .setWatchNextType(WatchNextType.TYPE_CONTINUE)
   .setName("Movie name")
   .addPlatformSpecificPlaybackUri(platformSpecificPlaybackUris)
   .addPosterImages(images)
   // Timestamp in millis for sample last engagement time 12/1/2023 00:00:00
   .setLastEngagementTimeMillis(1701388800000)
   // Suppose the duration is 2 hours, it is 72000000 in milliseconds
   .setDurationMills(72000000)
   // Suppose last playback offset is 1 hour, 36000000 in milliseconds
   .setLastPlayBackPositionTimeMillis(36000000)
   .build()

כשמספקים פרטים כמו ז'אנרים וסיווגי תוכן, מערכת Google TV יכולה להציג את התוכן שלכם בצורה דינמית יותר ולחבר אותו לצופים המתאימים.

val genres = Arrays.asList("Action", "Science fiction")
val rating1 = RatingSystem.Builder().setAgencyName("MPAA").setRating("PG-13").build()
val contentRatings = Arrays.asList(rating1)
val movieEntity = MovieEntity.Builder()
    ...
    .addGenres(genres)
    .addContentRatings(contentRatings)
    .build()

הישויות נשארות זמינות באופן אוטומטי למשך 60 יום, אלא אם מציינים זמן תפוגה קצר יותר. צריך להגדיר תאריך תפוגה מותאם אישית רק אם רוצים להסיר את הישות לפני תקופת ברירת המחדל הזו.

// Set the expiration time to be now plus 30 days in milliseconds
val expirationTime = DisplayTimeWindow.Builder()
    .setEndTimestampMillis(now().toMillis()+2592000000).build()
val movieEntity = MovieEntity.Builder()
    ...
    .addAvailabilityTimeWindow(expirationTime)
    .build()

TvEpisodeEntity

בדוגמה הזו מוסבר איך ליצור TvEpisodeEntity עם כל השדות הנדרשים:

val tvEpisodeEntity = TvEpisodeEntity.Builder()
    .setWatchNextType(WatchNextType.TYPE_CONTINUE)
    .setName("Episode name")
    .addPlatformSpecificPlaybackUri(platformSpecificPlaybackUris)
    .addPosterImages(images)
    // Timestamp in millis for sample last engagement time 12/1/2023 00:00:00
    .setLastEngagementTimeMillis(1701388800000)
    .setDurationMills(72000000) // 2 hours in milliseconds
    // 45 minutes and 15 seconds in milliseconds is 2715000
    .setLastPlayBackPositionTimeMillis(2715000)
    .setEpisodeNumber("2")
    .setSeasonNumber("1")
    .setShowTitle("Title of the show")
    .build()

מחרוזת מספר הפרק (למשל "2") ומחרוזת מספר העונה (למשל "1") יורחבו לפורמט המתאים לפני שיוצגו בכרטיס "המשך צפייה". שימו לב: הערכים צריכים להיות מחרוזת מספרית, ולא "e2", ‏ "episode 2", ‏ "s1" או "season 1".

אם לתוכנית טלוויזיה מסוימת יש עונה אחת בלבד, צריך להגדיר את מספר העונה כ-1.

כדי להגדיל את הסיכויים שהצופים ימצאו את התוכן שלכם ב-Google TV, כדאי לספק נתונים נוספים כמו ז'אנרים, סיווגי תוכן וחלונות זמן של זמינות, כי הפרטים האלה יכולים לשפר את אפשרויות התצוגה והסינון.

val genres = Arrays.asList("Action", "Science fiction")
val rating1 = RatingSystem.Builder().setAgencyName("MPAA").setRating("PG-13").build()
val contentRatings = Arrays.asList(rating1)
val tvEpisodeEntity = TvEpisodeEntity.Builder()
    ...
    .addGenres(genres)
    .addContentRatings(contentRatings)
    .setSeasonTitle("Season Title")
    .setShowTitle("Show Title")
    .build()

VideoClipEntity

דוגמה ליצירת VideoClipEntity עם כל שדות החובה.

VideoClipEntity מייצג קליפ שנוצר על ידי משתמש, כמו סרטון ב-YouTube.

val videoClipEntity = VideoClipEntity.Builder()
    .setPlaybackUri(Uri.parse("https://www.example.com/uri_for_current_platform"))
    .setWatchNextType(WatchNextType.TYPE_CONTINUE)
    .setName("Video clip name")
    .addPlatformSpecificPlaybackUri(platformSpecificPlaybackUris)
    .addPosterImages(images)
    // Timestamp in millis for sample last engagement time 12/1/2023 00:00:00
    .setLastEngagementTimeMillis(1701388800000)
    .setDurationMills(600000) //10 minutes in milliseconds
    .setLastPlayBackPositionTimeMillis(300000) //5 minutes in milliseconds
    .addContentRating(contentRating)
    .build()

אפשר גם להגדיר את היוצר, את תמונת היוצר, את זמן היצירה במילישניות או את חלון הזמן של הזמינות .

LiveStreamingVideoEntity

דוגמה ליצירת LiveStreamingVideoEntity עם כל שדות החובה.

val liveStreamingVideoEntity = LiveStreamingVideoEntity.Builder()
    .setPlaybackUri(Uri.parse("https://www.example.com/uri_for_current_platform"))
    .setWatchNextType(WatchNextType.TYPE_CONTINUE)
    .setName("Live streaming name")
    .addPlatformSpecificPlaybackUri(platformSpecificPlaybackUris)
    .addPosterImages(images)
    // Timestamp in millis for sample last engagement time 12/1/2023 00:00:00
    .setLastEngagementTimeMillis(1701388800000)
    .setDurationMills(72000000) //2 hours in milliseconds
    .setLastPlayBackPositionTimeMillis(36000000) //1 hour in milliseconds
    .addContentRating(contentRating)
    .build()

אפשר גם להגדיר את שעת ההתחלה, את השדרן, את סמל השדרן או את חלון הזמינות של ישות השידור החי.

למידע מפורט על מאפיינים ודרישות, אפשר לעיין בהפניית ה-API.

ציון נתונים של אוסף ההמשכים

AppEngagePublishClient אחראי לפרסום של אוסף ההמשכים. משתמשים ב-publishContinuationCluste method כדי לפרסם אובייקט ContinuationCluster.

חשוב לאתחל את הלקוח ולבדוק את זמינות השירות כמו שמתואר במדריך לתחילת העבודה.

client.publishContinuationCluster(
    PublishContinuationClusterRequest
        .Builder()
        .setContinuationCluster(
            ContinuationCluster.Builder()
                .setAccountProfile(accountProfile)
                .addEntity(movieEntity1)
                .addEntity(movieEntity2)
                .addEntity(tvEpisodeEntity1)
                .addEntity(tvEpisodeEntity2)
                .setSyncAcrossDevices(true)
                .build()
        )
        .build()
)

כשהשירות מקבל את הבקשה, הפעולות הבאות מתבצעות במסגרת עסקה אחת:

  • הנתונים הקיימים של ContinuationCluster משותף המפתחים יוסרו.
  • הנתונים מהבקשה מנותחים ומאוחסנים ב-ContinuationCluster המעודכן.

במקרה של שגיאה, הבקשה כולה נדחית והמצב הקיים נשמר.

ממשקי ה-API לפרסום הם ממשקי upsert API, והם מחליפים את התוכן הקיים. אם אתם צריכים לעדכן ישות ספציפית באשכול ההמשכי, תצטרכו לפרסם מחדש את כל הישויות.

צריך לספק נתונים של אוסף ההמשכים רק לחשבונות למבוגרים. הפרסום יתבצע רק אם הפרופיל בחשבון שייך למבוגר.

סנכרון בין מכשירים

הדגל SyncAcrossDevices קובע אם נתוני ContinuationCluster של המשתמש מסונכרנים בין מכשירים כמו טלוויזיה, טלפון, טאבלטים וכו'. סנכרון בין מכשירים מושבת כברירת מחדל.

ערכים:

  • true: נתוני המשך הצפייה משותפים בכל המכשירים של המשתמש כדי לאפשר חוויית צפייה חלקה. מומלץ מאוד לבחור באפשרות הזו כדי ליהנות מחוויית השימוש הטובה ביותר בכל המכשירים.
  • false: הגישה לנתונים של אוסף ההמשכים מוגבלת למכשיר הנוכחי.

אפליקציית המדיה צריכה לספק הגדרה ברורה להפעלה או להשבתה של סנכרון בין מכשירים, להסביר למשתמש את היתרונות של ההגדרה ולאחסן את ההעדפה שלו פעם אחת, ואז להחיל אותה ב-publishContinuationCluster בהתאם.

// Example to allow cross device syncing.
client.publishContinuationCluster(
    PublishContinuationClusterRequest
        .Builder()
        .setContinuationCluster(
            ContinuationCluster.Builder()
                .setAccountProfile(accountProfile)
                .setSyncAcrossDevices(true)
                .build()
        )
        .build()
)

כדי להפיק את המרב מהתכונה שלנו למעקב המרות בכמה מכשירים, צריך לוודא שהאפליקציה מקבלת את הסכמת המשתמשים ולהפעיל את האפשרות SyncAcrossDevices כדי true. כך התוכן יכול להסתנכרן בצורה חלקה בין מכשירים, מה שמוביל לחוויית משתמש טובה יותר ולמעורבות מוגברת. לדוגמה, שותף שהטמיע את התכונה הזו נהנה מעלייה של 40% במספר הקליקים על "המשך צפייה", כי התוכן שלו הוצג במספר מכשירים.

מחיקת נתוני הגילוי של סרטונים

כדי למחוק באופן ידני את נתוני המשתמש מהשרת של Google TV לפני תקופת השמירה הרגילה של 60 יום, משתמשים בשיטה deleteClusters. לאחר קבלת הבקשה, השירות ימחק את כל נתוני הגילוי הקיימים של סרטונים בפרופיל החשבון או בכל החשבון.

ה-enum‏ DeleteReason מגדיר את הסיבה למחיקת הנתונים. הקוד הבא מסיר את הנתונים של "המשך צפייה" אחרי התנתקות.


// If the user logs out from your media app, you must make the following call
// to remove continue watching data from the current google TV device,
// otherwise, the continue watching data will persist on the current
// google TV device until 60 days later.
client.deleteClusters(
    DeleteClustersRequest.Builder()
        .setAccountProfile(AccountProfile())
        .setReason(DeleteReason.DELETE_REASON_USER_LOG_OUT)
        .setSyncAcrossDevices(true)
        .build()
)

בדיקה

משתמשים באפליקציית האימות כדי לוודא שהשילוב של Engage SDK פועל בצורה תקינה.

אחרי שמפעילים את ה-API של הפרסום, בודקים באפליקציית האימות שהנתונים מתפרסמים בצורה נכונה. אשכול ההמשך אמור להופיע כשורה נפרדת בממשק של האפליקציה.

  • בודקים את הפעולות הבאות באפליקציה:
    • מתחברים לחשבון.
    • מעבר בין פרופילים(אם רלוונטי).
    • מפעילים סרטון, ואז עוצרים אותו או חוזרים לדף הבית.
    • סוגרים את האפליקציה במהלך הפעלת הסרטון.
    • להסיר פריט מהשורה "המשך צפייה" (אם האפשרות הזו נתמכת).
  • אחרי כל פעולה, מוודאים שהאפליקציה הפעילה את ה-API‏ publishContinuationClusters ושהנתונים מוצגים בצורה נכונה באפליקציית האימות.
  • באפליקציית האימות יוצג סימן וי ירוק עם הכיתוב "הכול תקין" ליד ישויות שהוטמעו בצורה נכונה.

    צילום מסך של אפליקציית האימות
    איור 1. אימות האפליקציה בוצע בהצלחה
  • אפליקציית האימות תסמן ישויות בעייתיות.

    צילום מסך של שגיאה באפליקציית האימות
    איור 2. שגיאה באפליקציית האימות
  • כדי לפתור בעיות בישויות, משתמשים בשלט הרחוק של הטלוויזיה כדי לבחור את הישות באפליקציית האימות וללחוץ עליה. הבעיות הספציפיות יוצגו ויודגשו באדום לבדיקה (ראו דוגמה בהמשך).

    פרטי שגיאה באפליקציית האימות
    איור 3. פרטי שגיאה באפליקציית האימות