Android TV에서 제공되는 영화/TV 에피소드의 다음 볼만한 동영상에 사용자의 참여 강화

다음 볼만한 동영상

Android TV 홈 화면에 표시되는 다음 볼만한 동영상 채널은 사용자가 다음에 볼만한 동영상이 포함된 행입니다. 다음 볼만한 동영상 행은 시스템 버전에 따라 명칭이 '관심 목록' 또는 '이어서 시청하기'일 수 있습니다.

b0ca4ea7c72ba8f6.png

시스템에서 이 채널을 만들고 유지합니다. 이 채널의 각 항목을 프로그램이라고 합니다. 앱은 사용자가 도중에 시청을 중단한 콘텐츠 또는 사용자가 상호작용한 콘텐츠(예: 시리즈의 다음 에피소드 또는 프로그램의 다음 시즌) 같은 프로그램을 다음 볼만한 동영상 채널에 추가, 업데이트하거나 채널에서 삭제할 수 있습니다.

개념

다음 볼만한 동영상 채널은 앱에서 사용자의 재참여를 유도하는 방법을 제공합니다.

개발자는 사용자가 이미 상호작용한 콘텐츠를 다음 볼만한 동영상 채널에 추가, 업데이트하거나 채널에서 삭제할 수 있습니다. 시청이 끝나지 않은 동영상 또는 다음 에피소드/시리즈 등을 추천할 수 있습니다.

또한 앱은 시청이 끝난 에피소드를 삭제하고 새 시즌의 다음 에피소드를 추가할 수도 있습니다.

다음 볼만한 동영상 채널의 사용 사례에는 4가지 유형이 있습니다.

  • 사용자가 완료하지 않은 동영상을 계속 시청합니다.
  • 볼만한 다음 동영상을 추천합니다. 예를 들어 사용자가 에피소드 1을 시청했다면 에피소드 2를 추천할 수 있습니다.
  • 사용자가 시청 중인 시리즈의 에피소드를 표시합니다.
  • 사용자가 추가한 흥미로운 동영상 관심 목록을 유지합니다.

이 Codelab에서는 사용자가 동영상을 일시중지할 때 그 동영상을 다음 볼만한 동영상 채널에 넣는 방법을 알아봅니다. 또한 끝까지 재생된 동영상을 다음 볼만한 동영상에서 삭제하는 방법과 다음 에피소드가 있는 경우 이를 추가하는 방법도 설명합니다.

이 Codelab에서는 새로 출시된 에피소드와 관심 목록에 다음 볼만한 동영상을 사용하는 사례는 다루지 않습니다.

영화와 TV 에피소드의 다음 볼만한 동영상

다음 볼만한 동영상 채널은 Android TV 홈 화면의 매우 중요한 기능으로, 사용자가 아직 다 보지 못한 영화와 TV 프로그램을 놓치지 않고 시청할 수 있게 해줍니다. 특히 TV 에피소드를 시청하는 사용자에게 중요합니다. 대개 TV 시리즈에는 에피소드가 많고 사용자가 시청을 중단한 지점부터 여러 번 이어서 시청하기 때문입니다.

사용자가 TV 앞으로 돌아와 무엇을 볼지 결정한다고 상상해 보세요. 앱에 다음 볼만한 동영상을 사용하면 사용자는 홈 화면에서 바로 시청을 중단한 지점부터 TV 에피소드를 다시 볼 수 있습니다. 이는 사용자 재참여를 유도하여 앱은 물론 사용자에게도 도움이 됩니다.

이 Codelab에서는 TV 참조 앱을 살펴보고, 다음 볼만한 동영상 채널의 다양한 사례를 처리하는 방법을 알려주며, 다음 볼만한 동영상 기능에 관한 Google의 품질 가이드라인을 설명합니다. 이 Codelab에서는 특별히 TV 에피소드 처리에 중점을 두지만 유사한 규칙을 영화에도 적용할 수 있습니다.

사용 가능 여부

이 Codelab의 코드는 Google TV 환경을 실행하는 기기를 포함해 Android TV 기기에서 작동합니다.

빌드할 항목

이 Codelab에서는 TV 영화/에피소드의 다음 볼만한 동영상 채널을 추가, 삭제 및 업데이트합니다. 이 앱에는 아래의 기능이 있습니다.

  • 영화 처리를 위한 여러 사용 사례 구현
  • TV 에피소드 처리를 위한 여러 사용 사례 구현

학습할 내용

  • 다음 볼만한 동영상에 관한 Google 품질 가이드라인

필요한 항목

  • Android 애플리케이션 개발에 관한 기본 지식
  • Android 스튜디오 4.1 이상(여기에서 다운로드)

시작 프로젝트 클론

GitHub 저장소에서 소스 코드를 다운로드할 수 있습니다.

git clone https://github.com/googlecodelabs/watchnext-for-movie-tv-episodes.git

또는 아래 링크에서 직접 다운로드할 수 있습니다.

소스 코드 다운로드

Android 스튜디오를 열고 메뉴 바에서 File > Open을 클릭하거나 시작 화면에서 Open an Existing Android Studio Project를 클릭한 후 최근에 클론한 폴더를 선택합니다.

8271a7b581390845.png

시작 프로젝트 이해

34641bc2c9f71f0f.png

프로젝트에는 4단계가 있습니다. 각 단계에서 관련 섹션의 안내에 따라 코드를 추가할 것입니다. 섹션을 완료하면 작성한 코드를 step_x_completed의 코드와 비교할 수 있습니다.

편의상 앱 내부의 콘텐츠를 시청할 수 있도록 동영상 목록과 exoplayer의 기본 코드를 추가했습니다. 그러면 TV 앱의 기본 뼈대가 만들어집니다(이에 관해서는 이 Codelab에서 다루지 않음).

여기서의 목표는 아직 시청이 끝나지 않은 동영상이나 시청이 끝난 동영상을 다음 볼만한 동영상 채널에 추가, 업데이트하거나 채널에서 삭제하는 방법과 앱 재참여를 유도하는 방법을 배우는 것입니다.

앱의 기본 구성요소는 다음과 같습니다.

  • FileVideoRepository는 동영상 메타데이터를 로드하고 쿼리하는 클래스입니다.
  • PlaybackFragment는 동영상 재생 프래그먼트입니다.
  • WatchNextPlaybackStateListener는 재생 상태 변경 리스너입니다. 재생 이벤트를 수신 대기하고 관련 작업을 트리거합니다.
  • WatchNextWorker는 다음 볼만한 동영상 채널을 업데이트하는 작업자입니다.
  • WatchNextHelper는 다음 볼만한 동영상 채널을 간단히 사용하도록 해주는 도우미 클래스입니다.

이 Codelab에서는 res/raw/api.json의 미디어 데이터를 사용해 다음 볼만한 동영상 항목을 채웁니다.

시작 프로젝트 실행

step_1을 실행합니다. 문제가 발생하는 경우 이 문서에서 시작하는 방법을 참고하세요.

  1. Android TV를 연결하거나 에뮬레이터를 시작합니다.
  1. step_1 구성을 선택하고 Android 기기를 선택한 후 메뉴 바에서 run 버튼을 누릅니다.249ea91a556fbd61.png
  2. 아래 스크린샷과 비슷한 동영상 모음 4개가 나와 있는 간단한 TV 앱이 표시됩니다.
  3. 앱의 기본 화면을 탐색하고 TV 참조 앱을 익혀 보세요. 동영상 카테고리에는 네 가지가 있습니다.
  4. 더 강화된 클립
  5. 기타 클립
  6. 비버리 힐빌리즈: 서로 다른 두 TV 시즌의 몇 가지 TV 에피소드
  7. 찰리 채플린 영화
  8. 비버리 힐빌리즈 카테고리에서 TV 에피소드 하나를 클릭하고 시청해 보세요. 이 Codelab에서는 이러한 TV 에피소드와 관련된 다음 볼만한 동영상 프로그램을 추가하는 방법을 설명합니다.

d7b51de54f4a1199.png

학습한 내용

소개 섹션에서는 다음에 관해 알아봤습니다.

  • 이 Codelab에 사용된 코드 구조와 주요 클래스
  • 샘플 앱 설정 및 실행 방법

다음 단계

다음 볼만한 동영상에 관한 품질 가이드라인

홈 화면 환경을 개선하려면 콘텐츠를 다음 볼만한 동영상에 추가하는 앱의 동작이 모두 일관되어야 합니다.

구체적으로 말하자면 개발자에게는 TV 에피소드와 관련해 처리해야 하는 시나리오가 있습니다. Google에서는 다음 볼만한 동영상의 품질 가이드라인을 요약해 두었습니다. 이 가이드라인은 다음 볼만한 동영상 채널의 품질을 보장하기 위해 반드시 따라야 합니다.

Google의 품질 표준을 충족하도록 다음 볼만한 동영상 기능을 빌드하는 방법

Google에서는 다음 볼만한 동영상 기능을 평가할 때 다음 사항을 확인합니다.

  1. 프로그램이 일시중지/중지되면 다음 볼만한 동영상에 추가되는지
  2. 앱이 다음 볼만한 동영상 항목에서 재생을 다시 시작할 수 있는지
  3. 앱이 다음 볼만한 동영상의 재생 위치를 적시에 업데이트하는지
  4. 재생이 끝나면 프로그램이 다음 볼만한 동영상에서 삭제되는지
  5. 현재 에피소드가 끝나면 다음 에피소드가 추가되는지
  6. 사용자가 상호작용하지 않은 콘텐츠는 앱이 다음 볼만한 동영상에 추가하지 않는지
  7. 시청이 끝나지 않은 모든 콘텐츠를 다음 볼만한 동영상에 푸시하는지
  8. 앱이 정확하고 완벽한 메타데이터(예: 시즌/에피소드 번호)를 설정하는지
  9. 앱이 동일한 TV 시리즈의 에피소드를 여러 개 추가하지 않는지

각 품질 요구사항에 관한 설명

  1. 프로그램이 일시중지/중지되면 다음 볼만한 동영상에 추가되어야 합니다.
  • 재생이 끝나지 않은 경우 앱은 기존 영화와 TV 프로그램을 다음 볼만한 동영상 행에 추가해야 합니다.
  1. 앱은 다음 볼만한 동영상 항목에서 재생을 다시 시작할 수 있어야 합니다.
  • 다음 볼만한 동영상 채널에 추가된 콘텐츠는 마지막 재생 위치에서 다시 재생되어야 합니다. 콘텐츠가 로드되면 즉시 동영상이 재생되어야 합니다.
  1. 앱은 다음 볼만한 동영상의 재생 위치를 적시에 업데이트해야 합니다.
  • 사용자가 동영상을 중단하는 경우 앱은 재생 진행률을 추적하고 다음 볼만한 동영상 프로그램을 마지막 재생 위치로 업데이트해야 합니다.
  1. 재생이 끝나면 프로그램이 다음 볼만한 동영상에서 삭제되어야 합니다.
  • 앱은 양질의 구성요소여야 하고 뒷정리를 해야 합니다. 다음 볼만한 동영상 행은 모든 앱에 공유되는 행입니다. 사용자의 신뢰를 얻으려면 행의 콘텐츠가 정확해야 합니다.
  1. 현재 에피소드가 끝나면 다음 에피소드가 추가되어야 합니다.
  • 사용자가 TV 시리즈를 시청할 경우 앱은 시리즈의 다음 에피소드를 다음 볼만한 동영상 행에 추가하여 사용자가 편안하게 이어서 시청할 수 있도록 해야 합니다.
  1. 앱은 사용자가 상호작용하지 않은 콘텐츠를 다음 볼만한 동영상에 추가하지 않아야 합니다.
  • 다음 볼만한 동영상 가이드라인에 따라 앱은 사용자가 시청을 '시작'한 경우에만 다음 볼만한 동영상 채널에 영화나 TV 에피소드를 추가해야 합니다.
  • 트레일러 같은 짧은 동영상 클립은 다음 볼만한 동영상 채널에 추가하지 않는 것이 좋습니다. 그러한 클립에는 재참여가 거의 없기 때문입니다.
  1. 시청이 끝나지 않은 모든 콘텐츠를 다음 볼만한 동영상에 푸시해야 합니다.
  • 제공업체는 다음 볼만한 동영상에 직접 푸시하는 카드 수를 인위적으로 제한해서는 안 됩니다. 사용자에게 시청이 끝나지 않은 콘텐츠가 있다면 다음 볼만한 동영상 행에 그 콘텐츠를 푸시해야 합니다.
  1. 앱은 정확하고 완벽한 메타데이터를 설정해야 합니다.
  • 에피소드와 관련된 메타데이터가 정확해야 합니다.
  • 에피소드 번호, 시즌 번호 및 제목이 정확해야 합니다.
  • 진행률 표시줄은 시청량에 비례해야 합니다.
  • 타일에 에피소드 또는 시리즈 이미지가 있어야 합니다.
  1. 앱은 동일한 TV 시리즈의 에피소드를 여러 개 추가해서는 안 됩니다.
  • 앱은 각 TV 시리즈의 다음 볼만한 동영상 항목을 최대 1개씩 보관해야 합니다. 사용자가 두 에피소드 중 절반을 본 경우 다음 볼만한 동영상에는 최근에 시청한 에피소드만 표시되어야 합니다.

이러한 품질 요구사항은 앱에서 다음 볼만한 동영상에 관한 우수한 사용자 환경을 제공하는 데 도움이 됩니다.

좋습니다. 이제 이러한 품질 가이드를 염두에 두고 다음 볼만한 동영상 기능을 빌드하는 작업으로 넘어가 보겠습니다.

학습한 내용

이 섹션에서는 다음에 관해 알아봤습니다.

  • 다음 볼만한 동영상 품질 요구사항

다음 단계

시청이 끝나지 않은 에피소드를 다음 볼만한 동영상 채널에 추가

시청이 끝나지 않은 에피소드를 다음 볼만한 동영상에 추가하는 기본 기능부터 시작해 보겠습니다.

이 Codelab에서는 WatchNextProgram을 생성하고 에피소드 번호, 시즌 번호, 동영상 유형 등 에피소드의 올바른 메타데이터를 입력하는 방법을 안내합니다. 다음 볼만한 동영상 항목은 업데이트 가능하며 사용자의 마지막 재생 위치를 추적합니다. 따라서 사용자는 프로그램을 클릭해 재생을 다시 시작할 수 있습니다.

이 섹션에서 다루는 내용은 다음과 같습니다.

  • 프로그램이 일시중지/중지되면 다음 볼만한 동영상에 추가됩니다.
  • 앱이 다음 볼만한 동영상 항목에서 재생을 다시 시작할 수 있습니다.
  • 앱이 다음 볼만한 동영상의 재생 위치를 적시에 업데이트합니다.
  • 앱이 정확하고 완벽한 메타데이터를 설정합니다.

시청이 끝나지 않은 동영상 추가 - 영화와 에피소드의 차이점

다음 볼만한 동영상 채널에 영화와 에피소드를 추가하는 절차는 매우 유사합니다. 유일한 차이점은 메타데이터가 서로 다르다는 점입니다.

예를 들어 에피소드의 경우 WatchNextProgram.BuildersetEpisodeNumber, setSeasonNumber(), setSeasonTitle(), setEpisodeTitle() 같은 특정 메타데이터 메서드가 있습니다.

시청이 끝나지 않은 동영상(영화/에피소드)을 다음 볼만한 동영상 채널에 추가

시청이 끝나지 않은 동영상을 다음 볼만한 동영상 채널에 추가하려면 개발자는 WatchNextProgram.Builder를 사용하여 WatchNextProgram 인스턴스를 빌드한 다음 PreviewChannelHelper.publishWatchNextProgram를 호출하여 다음 볼만한 동영상 채널에 인스턴스를 게시하면 됩니다.

먼저 WatchNextProgram의 Builder 인스턴스를 만들고 동영상을 설명하는 모든 메타데이터를 설정합니다.

step_1PlayNextHelper.kt에서 setBuilderMetadata 메서드를 검색한 후 다음 코드를 복사하여 'Step 1.1 - Set video metadata for WatchNextProgram.' 주석 사이에 붙여넣습니다.

WatchNextHelper.kt

builder.setType(type)
   .setWatchNextType(watchNextType)
   .setLastPlaybackPositionMillis(watchPosition)
   .setLastEngagementTimeUtcMillis(System.currentTimeMillis())
   .setTitle(video.name)
   .setDurationMillis(duration.toMillis().toInt())
   .setPreviewVideoUri(Uri.parse(video.videoUri))
   .setDescription(video.description)
   .setPosterArtUri(Uri.parse(video.thumbnailUri))
   // Intent uri used to deep link video when the user clicks on watch next item.
   .setIntentUri(Uri.parse(video.uri))
   .setInternalProviderId(video.id)
   // Use the contentId to recognize the same content across different channels.
   .setContentId(video.id)

if (type == TYPE_TV_EPISODE) {
   builder.setEpisodeNumber(video.episodeNumber.toInt())
       .setSeasonNumber(video.seasonNumber.toInt())
       // User TV series name and season number to generate a fake season name.
       .setSeasonTitle(context.getString(
           R.string.season, video.category, video.seasonNumber))
       // Use the name of the video as the episode name.
       .setEpisodeTitle(video.name)
       // Use TV series name as the tile, in this sample,
       // we use category as a fake TV series.
       .setTitle(video.category)
}

step 1.1의 코드를 자세히 읽고 이러한 메타데이터를 설정하는 이유를 파악해 봅니다.

  1. setLastPlaybackPositionMillis()setDurationMillis()는 재생 진행률을 올바로 표시하고 사용자가 동영상과 상호작용할 때 진행률을 업데이트하는 데 유용합니다.
  2. setLastEngagementTimeUtcMillis()는 사용자가 동영상을 시청할 때 타임스탬프를 설정합니다. 이는 다음 볼만한 동영상 채널에서 항목의 우선순위를 정하는 데 도움이 됩니다.

시청이 끝나지 않은 영화를 다음 볼만한 동영상에 추가

WATCH_NEXT_TYPE_NEXT를 사용하여, 시청이 끝나지 않은 영화를 다음 볼만한 동영상 채널에 추가할 수 있습니다.

영화 메타데이터(제목 및 설명) 설정

영화에는 제목과 설명뿐 아니라 다른 속성도 설정합니다. 그러면 사용자가 동영상을 클릭하지 않아도 올바른 콘텐츠를 시청하고 있음을 인식할 수 있습니다.

builder.setType(type)
   .setWatchNextType(watchNextType)
   .setLastPlaybackPositionMillis(watchPosition)
   .setLastEngagementTimeUtcMillis(System.currentTimeMillis())
   .setTitle(video.name)
   .setDurationMillis(duration.toMillis().toInt())
   .setPreviewVideoUri(Uri.parse(video.videoUri))
   .setDescription(video.description)
   .setPosterArtUri(Uri.parse(video.thumbnailUri))
...

영화의 샘플 스크린샷

99a21ecd22268f2d.png

시청이 끝나지 않은 에피소드를 다음 볼만한 동영상에 추가

setWatchNextType()에는 4가지 유형을 사용할 수 있습니다. 시청이 끝나지 않은 TV 에피소드에는 WATCH_NEXT_TYPE_CONTINUE를, 다음 에피소드에는 WATCH_NEXT_TYPE_NEXT를 사용합니다.

에피소드 메타데이터(에피소드 및 시즌 번호/제목) 설정

TV 에피소드에는 에피소드 번호와 시즌 번호를 설정합니다. 그러면 사용자가 동영상을 클릭하지 않아도 올바른 에피소드를 시청하고 있음을 인식할 수 있습니다.

if (type == TYPE_TV_EPISODE) {
   Builder.setType(PreviewPrograms.TYPE_EPISODE)
       .setEpisodeNumber(video.episodeNumber.toInt())
       .setSeasonNumber(video.seasonNumber.toInt())
       // Use TV series name and season number to generate a fake season name.
       .setSeasonTitle(context.getString(
           R.string.season, video.category, video.seasonNumber))
       // Use the name of the video as the episode name.
       .setEpisodeTitle(video.name)
       // Use TV series name as the tile, in this sample,
       // we use category as a fake TV series.
       .setTitle(video.category)
}

SeasonTitle, EpisodeTitle 및 Title을 정확하게 설정해야 합니다. 각 에피소드에는 에피소드의 콘텐츠를 설명하는 자체 제목이 있습니다. 이 제목을 EpisodeTitle에 사용하면 됩니다. 사용자가 에피소드 내용을 알 수 있도록 TV 프로그램의 제목 속성에 TV 시리즈 제목을 사용합니다. 시즌 제목이 있는 경우 대신 이 제목을 SeasonTitle에 사용합니다. 시리즈 이름과 시즌 번호를 함께 사용할 수도 있습니다(예: <TV 시리즈 이름> 시즌 <시즌 번호>).

에피소드의 샘플 스크린샷

658c430b13bcb3a6.png

재생 다시 시작

setLastPlaybackPositionMillis(watchPosition)는 사용자가 영화/에피소드를 중단한 지점을 전달하는 데 사용됩니다. 이 진행률은 다음 볼만한 동영상 카드에 표시됩니다. TV 참조 앱에서는 코드에 WatchProgressDatabase가 사용되어 각 동영상의 재생 진행률을 추적합니다. 이를 통해 사용자는 동영상으로 어떻게 이동하든 이전 지점부터 이어서 시청할 수 있습니다.

시청 작업 재생 가이드라인에 따라 동영상 콘텐츠가 로드되면 바로 에피소드가 재생되어야 합니다. TV 시리즈 정보는 사용자가 이미 시청했으므로 다시 표시하지 않아도 됩니다.

다음으로, PreviewChannelHelper.publishWatchNextProgram을 호출하여 다음 볼만한 동영상 채널에 게시합니다. 같은 파일에서 'Step 1.2'를 검색하고 다음 코드를 붙여넣습니다.

WatchNextHelper.kt

try {
   programId = PreviewChannelHelper(context)
       .publishWatchNextProgram(updatedProgram)
   Timber.v("Added New program to Watch Next row: ${updatedProgram.title}")
} catch (exc: IllegalArgumentException) {
   Timber.e(
       exc, "Unable to add program to Watch Next row. ${exc.localizedMessage}"
   )
   exc.printStackTrace()
}

재생 진행률 새로고침

다음 볼만한 동영상 카드가 이미 다음 볼만한 동영상 채널에 있는 경우 사용자가 동영상을 여러 번 보았다면 앱은 마지막 시청 진행 상황이 반영되도록 다음 볼만한 동영상 카드를 업데이트해야 합니다.

WatchNextProgram을 업데이트할 경우 동일한 빌더 클래스를 사용하여 WatchNextProgram을 빌드하고 updateWatchNextProgramPreviewChannelHelper을 호출하여 기존 항목을 업데이트합니다. 다음 코드를 WatchNextHelper.kt의 'Step 1.3'에 붙여넣습니다.

WatchNextHelper.kt

programId = existingProgram.id
PreviewChannelHelper(context).updateWatchNextProgram(updatedProgram, programId)

샘플 스크린샷

결과 확인

코드를 살펴보고 변경사항을 step_1_completed의 소스와 비교합니다. 그런 다음 step_1_completed를 실행하고 에피소드 일부를 시청한 다음 에피소드가 다음 볼만한 동영상 채널에 추가되었는지 확인합니다.

확인

  • ✅ 통과: 프로그램이 일시중지/중지되면 다음 볼만한 동영상에 추가됨
  • ✅ 통과: 앱이 다음 볼만한 동영상 항목에서 재생을 다시 시작할 수 있음
  • ✅ 통과: 앱이 다음 볼만한 동영상의 재생 위치를 적시에 업데이트함
  • ✅ 통과: 앱이 정확하고 완벽한 메타데이터를 설정함
  • ✅ 통과: 시청이 끝나지 않은 모든 콘텐츠를 다음 볼만한 동영상으로 푸시함
  • ❗ 실패: 재생이 끝나면 프로그램이 다음 볼만한 동영상에서 삭제됨
  • ❗ 실패: 현재 에피소드가 끝나면 다음 에피소드가 추가됨
  • ❗ 실패: 사용자가 상호작용하지 않은 콘텐츠는 앱이 다음 볼만한 동영상에 추가하지 않음
  • ❗ 실패: 앱이 동일한 TV 시리즈의 에피소드를 여러 개 추가하지 않음

학습한 내용

이 섹션에서는 다음 방법을 배웠습니다.

  • WatchNextProgram 만들기
  • 다음 볼만한 동영상 채널에 WatchNextProgram 삽입 또는 업데이트
  • 재생 진행률 업데이트
  • 재생 다시 시작
  • TV 에피소드의 올바른 메타데이터 설정

다음 단계

시청이 끝나지 않은 에피소드를 다음 볼만한 동영상 채널에 추가

다음 볼만한 동영상 채널의 카드는 마지막 참여 시간을 기준으로 정렬되고, 마지막으로 참여한 동영상이 채널 맨 앞에 배치됩니다. 사용자가 시청을 끝내면 앱은 다음 볼만한 동영상 채널에서 그 프로그램을 삭제하고 사용자에게 더 관련성 높은 콘텐츠를 홍보해야 합니다.

개발자는 동영상의 재생 진행률을 모니터링해야 합니다. 사용자가 동영상 시청을 마치면 앱은 다음 볼만한 동영상 채널에서 그 동영상을 삭제해야 합니다.

참고: 다음 볼만한 동영상 채널에서 영화나 에피소드를 삭제할 때도 로직을 적용할 수 있습니다.

WatchNextProgram 삭제

다음 볼만한 동영상 채널에서 항목을 삭제하려면 개발자는 올바른 WatchNextProgram을 찾고 프로그램 URI를 사용하여 콘텐츠 제공업체에서 그 항목을 삭제해야 합니다. 그렇게 하려면 개발자는 WatchNextProgram을 자체 데이터베이스의 동영상 항목과 일치시켜야 합니다. Google에서는 internalProviderId 필드를 활용하여 고유한 동영상 식별자를 설정하고 이 식별자를 개발자의 자체 데이터베이스에 있는 항목 중 하나와 연결할 수 있습니다.

먼저 동영상 ID를 조회하여 올바른 WatchNextProgram을 찾습니다. WatchNextProgram. getInternalProviderId에서 또는 WatchNextProgram 콘텐츠 제공업체를 통해 internalProviderId에 액세스할 수 있습니다. 그런 다음 URI를 사용하여 다음 볼만한 동영상 채널에서 그 ID를 삭제하면 됩니다.

'Step 2.1'을 검색하고 다음을 복사하여 붙여넣으세요.

WatchNextHelper.kt

val foundProgram = getWatchNextProgramByVideoId(video.id, context)
if (foundProgram == null) {
   Timber.e(
       "Unable to delete. No program found with videoID ${video.id}"
   )
   return null
}

// Use the found program's URI to delete it from the content resolver
return foundProgram.let {
   val programUri = TvContractCompat.buildWatchNextProgramUri(it.id)
   // delete returns the number of rows deleted.
   val deleteCount = context.contentResolver.delete(
       programUri, null, null
   )

   if (deleteCount == 1) {
       Timber.v("Content successfully removed from Watch Next")
       programUri
   } else {
       Timber.e("Content failed to be removed from Watch Next, delete count $deleteCount")
       null
   }
}

여러 WatchNextProgram을 한 번에 삭제하려면 항상 일괄 작업을 요청하여 TV 콘텐츠 제공업체와의 접촉을 최대한 활용하는 것이 좋습니다. 'Step 2.2'를 검색하고 다음 코드 스니펫을 복사하여 WatchNextHelper.kt에 붙여넣습니다.

WatchNextHelper.kt

val foundPrograms = getWatchNextProgramByVideoIds(videos.map { it.id }, context)
val operations = foundPrograms.map {
   val programUri = TvContractCompat.buildWatchNextProgramUri(it.id)
   ContentProviderOperation.newDelete(programUri).build()
} as ArrayList<ContentProviderOperation>

val results = context.contentResolver.applyBatch(TvContractCompat.AUTHORITY, operations)

results.forEach { result ->
   if (result.count != 1) {
       Timber.e("Content failed to be removed from Watch Next: ${result.uri}")
   }
}

다음 볼만한 동영상 가이드라인에 따라 사용자가 시청을 완료한 에피소드는 삭제해야 합니다. 엔딩 크레딧이 시작되면 사용자가 에피소드 시청을 '완료'한 것입니다. 이 경우 다음 볼만한 동영상 채널에 에피소드를 추가하지 마세요. 또는 이미 추가된 경우에는 삭제하세요. 엔딩 크레딧 자동 감지 기술을 사용하여 이 상태를 파악할 수도 있고, 콘텐츠 길이를 기반으로 한 근사치를 사용할 수도 있습니다(예: 에피소드의 남은 분량이 3분 미만인 경우).

WatchNextHelper.kthandleWatchNextForEpisodes() 메서드를 살펴보면 다음 스니펫을 찾을 수 있습니다.

WatchNextHelper.kt

video.isAfterEndCreditsPosition(watchPosition.toLong()) -> {
   removeVideoFromWatchNext(context, video)

   ...
}

이 Codelab에서는 VIDEO_COMPLETED_DURATION_MAX_PERCENTAGE를 사용하여 크레딧 장면 위치를 시뮬레이션합니다. isAfterEndCreditsPosition()의 코드를 각자의 로직으로 대체해도 됩니다.

결과 확인

코드를 살펴보고 변경사항을 step_2_completed의 소스와 비교합니다. 그런 다음 step_2_completed를 실행하고 에피소드를 시청한 다음 시청이 끝나면 에피소드가 다음 볼만한 동영상 채널에서 삭제되었는지 확인합니다.

확인

  • ✅ 통과: 프로그램이 일시중지/중지되면 다음 볼만한 동영상에 추가됨
  • ✅ 통과: 앱이 다음 볼만한 동영상 항목에서 재생을 다시 시작할 수 있음
  • ✅ 통과: 앱이 다음 볼만한 동영상의 재생 위치를 적시에 업데이트함
  • ✅ 통과: 앱이 정확하고 완벽한 메타데이터를 설정함
  • ✅ 통과: 시청이 끝나지 않은 모든 콘텐츠를 다음 볼만한 동영상으로 푸시함
  • ✅ 통과: 재생이 끝나면 프로그램이 다음 볼만한 동영상에서 삭제됨
  • ❗ 실패: 현재 에피소드가 끝나면 다음 에피소드가 추가됨
  • ❗ 실패: 사용자가 상호작용하지 않은 콘텐츠는 앱이 다음 볼만한 동영상에 추가하지 않음
  • ❗ 실패: 앱이 동일한 TV 시리즈의 에피소드를 여러 개 추가하지 않음

학습한 내용

소개 섹션에서는 다음에 관해 알아봤습니다.

  • TV 에피소드의 크레딧 장면 확인
  • 동영상 ID로 WatchNextProgram 찾기
  • 단일 WatchNextProgram 삭제
  • 여러 WatchNextProgram 삭제

다음 단계

다음 볼만한 동영상 채널에 다음 에피소드 추가

영화와 달리 TV 프로그램에는 시즌이 2개 이상 있으며 시즌마다 에피소드도 많습니다. 사용자가 한 에피소드 시청을 끝냈다면 에피소드를 다음 볼만한 동영상 채널에서 삭제하기보다는 다음 에피소드로 바꾸는 것이 좋습니다. 같은 시즌에서 이미 시청한 에피소드의 바로 뒤에 있는 에피소드, 또는 시청이 끝난 에피소드가 현재 시즌의 마지막 에피소드인 경우에는 다음 시즌의 첫 번째 에피소드가 다음 에피소드가 될 수 있습니다.

다음 볼만한 동영상 채널에 다음 에피소드를 추가할 때 다음 볼만한 동영상 유형을 WATCH_NEXT_TYPE_NEXT으로 설정합니다. 이는 이 에피소드가 이전에 시청한 프로그램이 아니라 사용자가 따라갈 만한 완전히 새로운 에피소드임을 나타냅니다. 앱에서 사용자는 다음 에피소드를 처음부터 시청할 수 있어야 합니다. 'TODO: Step 3.1 - Add next episode from TV series.'를 검색한 후 다음 코드를 복사하여 step 3.1에 붙여넣습니다.

WatchNextHelper.kt

videoRepository.getNextEpisodeInSeries(video)?.let {
       insertOrUpdateVideoToWatchNext(
           it,
           0,
           WATCH_NEXT_TYPE_NEXT,
           context
       )
       newWatchNextVideo = it
   }

다음 에피소드가 getNextEpisodeInSeries() 메서드로 캡처됩니다.

같은 시즌의 다음 에피소드

현재 시즌에 아직 더 많은 에피소드가 있다면 앱은 사용 가능한 다음 에피소드를 선택해 다음 에피소드로 설정해야 합니다.

다음 시즌의 첫 번째 에피소드

사용자가 현재 시즌의 시청을 마쳤지만 최신 시즌이 제공되는 경우 앱은 다음 시즌의 첫 번째 에피소드를 다음 에피소드로 선택해야 합니다.

출시된 새 에피소드

에피소드가 더 이상 없으면 앱은 다음 에피소드를 추가할 필요가 없습니다. 하지만 새 에피소드가 제공되면 그 에피소드를 클라이언트 측에 푸시하여 다음 볼만한 동영상 채널에 추가하는 것이 좋습니다. 새 에피소드에서는 WATCH_NEXT_TYPE_NEW를 WatchNextProgram 유형으로 사용해야 합니다. 이 방법에는 서버 푸시 알림이 필요하지만, 이 Codelab에서는 이에 관해 다루지 않습니다.

결과 확인

코드를 살펴보고 변경사항을 step_3_completed의 소스와 비교합니다. 그런 다음 step_3_completed를 실행하고 에피소드를 시청한 다음 시청이 끝나면 다음 에피소드가 다음 볼만한 동영상 채널에 추가되었는지 확인합니다.

확인

  • ✅ 통과: 프로그램이 일시중지/중지되면 다음 볼만한 동영상에 추가됨
  • ✅ 통과: 앱이 다음 볼만한 동영상 항목에서 재생을 다시 시작할 수 있음
  • ✅ 통과: 앱이 다음 볼만한 동영상의 재생 위치를 적시에 업데이트함
  • ✅ 통과: 앱이 정확하고 완벽한 메타데이터를 설정함
  • ✅ 통과: 시청이 끝나지 않은 모든 콘텐츠를 다음 볼만한 동영상으로 푸시함
  • ✅ 통과: 재생이 끝나면 프로그램이 다음 볼만한 동영상에서 삭제됨
  • ✅ 통과: 현재 에피소드가 끝나면 다음 에피소드가 추가됨
  • ❗ 실패: 사용자가 상호작용하지 않은 콘텐츠는 앱이 다음 볼만한 동영상에 추가하지 않음
  • ❗ 실패: 앱이 동일한 TV 시리즈의 에피소드를 여러 개 추가하지 않음

학습한 내용

이 섹션에서는 다음에 관해 알아봤습니다.

  • 다음 에피소드를 선택하는 방법
  • TV 시리즈의 다음 에피소드 추가

다음 단계

사용자의 관심분야를 파악하세요.

다음 볼만한 동영상 채널에서 가장 관련 있는 콘텐츠에 사용자가 계속 관심을 가지도록 하려면, 다음 볼만한 동영상 채널에 TV 에피소드를 추가하거나 그 채널에서 삭제할 때 앱에서 몇 가지 사항을 더 고려해야 합니다.

같은 시리즈의 여러 에피소드

사용자가 특정 시점에 시청을 끝내지 않은 에피소드가 2개 이상 있는 데에는 다양한 이유가 있습니다. 예:

  1. 관련 TV 시리즈가 여러 앱 또는 방송에서 제공되는 경우
  2. 사용자가 속도를 높여 콘텐츠를 건너뛰려는 경우

홈 화면의 다음 볼만한 동영상 채널에는 항목 수가 극히 제한됩니다. 따라서 앱은 다음 볼만한 동영상 채널에 각 TV 시리즈의 에피소드를 최대 한 개씩 보관하는 것이 좋습니다. 남은 에피소드가 사용자가 마지막으로 시청한 에피소드여야 합니다.

이는 WatchNextHelper 클래스의 handlePlayNextForEpisode()에서 처리합니다. 'Step 4.1'을 검색한 후 다음 코드를 복사하여 step 4.1의 빈 영역에 붙여넣습니다.

WatchNextHelper.kt

newWatchNextVideo?.let { videoToKeep ->
   videoRepository.getAllVideosFromSeries(videoToKeep.seriesUri)?.let { allEpisodes ->
           filterWatchNextVideos(allEpisodes, context)
               ?.let { watchedEpisodes ->
                   removeVideosFromWatchNext(
                       context, watchedEpisodes.filter { it.id != videoToKeep.id })
               }
       }
}

step 4.1에서 사용자가 마지막으로 시청한 에피소드를 추적하고 같은 TV 시리즈의 다른 모든 에피소드를 삭제합니다. 이 단계에서 여러 에피소드를 한 번에 삭제하기 때문에, Android 콘텐츠 제공업체의 일괄 작업을 활용하기 위한 removeVideosFromWatchNext() 메서드를 새로 만들었습니다.

상호작용이 적은 콘텐츠

다음 볼만한 동영상 가이드라인에 따라, 사용자가 시청을 시작한 에피소드만 다음 볼만한 동영상 채널에 추가해야 합니다. 사용자가 2분 넘게 시청하면 에피소드를 '시작'한 것입니다. 'Step 4.2'를 검색한 후 다음 코드를 복사하여 step 4.2의 영역에 붙여넣습니다.

WatchNextHelper.kt

val durationInMilliSeconds = duration.toMillis().toInt()
// Return true if either X minutes or Y % have passed
// Following formatting spans over multiple lines to accommodate max 100 limit
val watchNextMinStartedMillis = TimeUnit.MINUTES.toMillis(WATCH_NEXT_STARTED_MIN_MINUTES)
// Check if either X minutes or Y% has passed
val hasVideoStarted =
   (currentPosition >= (durationInMilliSeconds * WATCH_NEXT_STARTED_MIN_PERCENTAGE)) or
           (currentPosition >= watchNextMinStartedMillis)
val hasVideoStartedWithValidPosition =
   ((currentPosition <= durationInMilliSeconds) and hasVideoStarted)
Timber.v(
   "Has video started: %s, duration: %s, watchPosition: %s",
   hasVideoStartedWithValidPosition,
   duration,
   currentPosition
)
return hasVideoStartedWithValidPosition

결과 확인

코드를 살펴보고 변경사항을 step_4_completed의 소스와 비교합니다. 그런 다음 step_4_completed를 실행하고 에피소드를 시청한 후 다음 사항을 확인합니다.

  1. 코드를 통해 TV 시리즈에서 추가 에피소드가 삭제됨
  2. 사용자가 시청을 시작한 경우에만 다음 볼만한 동영상 채널에 추가됨

확인

  • ✅ 통과: 프로그램이 일시중지/중지되면 다음 볼만한 동영상에 추가됨
  • ✅ 통과: 앱이 다음 볼만한 동영상 항목에서 재생을 다시 시작할 수 있음
  • ✅ 통과: 앱이 다음 볼만한 동영상의 재생 위치를 적시에 업데이트함
  • ✅ 통과: 앱이 정확하고 완벽한 메타데이터를 설정함
  • ✅ 통과: 시청이 끝나지 않은 모든 콘텐츠를 다음 볼만한 동영상으로 푸시함
  • ✅ 통과: 재생이 끝나면 프로그램이 다음 볼만한 동영상에서 삭제됨
  • ✅ 통과: 현재 에피소드가 끝나면 다음 에피소드가 추가됨
  • ✅ 통과: 사용자가 상호작용하지 않은 콘텐츠는 앱이 다음 볼만한 동영상에 추가하지 않음
  • ✅ 통과: 앱이 동일한 TV 시리즈의 에피소드를 여러 개 추가하지 않음

학습한 내용

이 섹션에서는 다음에 관해 알아봤습니다.

  • 동일한 TV 시리즈의 여러 에피소드 추가 방지
  • 상호작용이 적은 콘텐츠 추가 방지

다음 단계

축하합니다

축하합니다. TV 에피소드를 위한 다음 볼만한 동영상을 빌드하고, 다음 볼만한 동영상에 관련된 모든 품질 요구사항을 배웠습니다.

잘하셨습니다.

추가 자료

참조 문서