24.02.08 쇼츠 페이지 구현하기

KSang·2024년 2월 12일
0

TIL

목록 보기
54/101

유튜브 쇼츠 영상처럼 영상이 재생되고 가득찬화면에서 반복되는 쇼츠영상을 만들려고한다.

유튜브 쇼츠 영상을 보면 아이콘들이 있고 채널 프로필, 채널명, 영상 타이틀 그리고 전체적인 화면을 채우는 영상이 있다.

그리고 화면을 넘기면 일정이상 드래그 했을때 휙하고 다음 화면으로 넘어간다.

이부분은 뷰페이저를 사용하면 될 것 같다.

레이아웃 배치를 할때 영상부분을 어떻게 할까 고민을 했는데

유튜브에선 YouTube Android Player API 라이브러리를 지원을 했었다.
https://developers.google.com/youtube/android/player?hl=ko

그러나 더 이상 지원되지않게 되었는데

이젠 유튜브에서 IFrame Player API를 이용해 웹뷰에서 영상을 트는걸 권장하고 있다.
https://developers.google.com/youtube/iframe_api_reference?hl=ko

약간 번거롭다는 생각이 들었는데

사용하기 편하게 라이브러리로 만들어논 android-youtube-player 라는게 있었다.
https://github.com/PierfrancescoSoffritti/android-youtube-player

설명을 보면

라이브러리는 WebView 내부에서 실행되는 IFrame Player API를 둘러싼 래퍼입니다 . 따라서 YouTube 서비스 약관에는 문제가 없습니다 .

라는 문구가 있는데 약관 문제도 없고
https://www.appbrain.com/stats/libraries/details/android_youtube_player/android-youtube-player
여기서 보면 굉장히 많은 앱에서 폭 넓게 사용하고 있어 사용하기로 했다.

item_short.xml

쇼츠기 때문에 위아래 길이를 0dp로 설정해 주었다.

나머지 북마크 버튼과 댓글 버튼을 추가해주고 부모 레이아웃에서 상단 버튼 (뒤로가기, 검색)을 만들어 줬다.

adapter

뷰페이저를 쓰기 때문에 어댑터를 만들어주는데 여기서 유튜브 플레이어를 설정해준다.

class ShortsItemViewHolder (
        private val binding: ItemShortsBinding,
        private val onBookmarkChecked: (ShortsItem) -> Unit,
        private val onSharedChecked: (ShortsItem) -> Unit,
        private val onCommentChecked: (ShortsItem.Item) -> Unit,
    ) : ShortsViewHolder(binding.root) {
        override fun onBind(item: ShortsItem) = with(binding) {
            if (item !is ShortsItem.Item) {
                return@with
            }
            ...
            
            vvShortsVideo.layoutParams = layoutParams
            vvShortsVideo.setBackgroundColor(Color.parseColor("#000000"))
            vvShortsVideo.addYouTubePlayerListener(
                object : AbstractYouTubePlayerListener() {
                    override fun onReady(youTubePlayer: YouTubePlayer) {
                        item.id?.let { youTubePlayer.loadVideo(it, 0f) }
                    }

                    override fun onStateChange(
                        youTubePlayer: YouTubePlayer,
                        state: PlayerConstants.PlayerState
                    ) {
                        if (state == PlayerConstants.PlayerState.ENDED) {
                            youTubePlayer.seekTo(0f)
                            youTubePlayer.play()
                        }
                    }
                }
            )
        }

    }

넓이를 화면에 맞게 matchParent()를 사용해준다

무한 재생이 되도록할건데 리스너를 달아주고

loadVideo를 사용해 로딩되자마자 비디오가 재생되게 했다.

재생되지 않게 하려면 cueVideo를 이용하면 로딩이 완료가 되도 비디오를 재생하지 않는다

비디오가 재생됬으니 이제 끝날때를 감지해야 한다.

리스너에선 onStateChange로 상태변화를 감지 할수 있는데

PlayerConstants.PlayerState엔

  enum class PlayerState {
    UNKNOWN, UNSTARTED, ENDED, PLAYING, PAUSED, BUFFERING, VIDEO_CUED
  }

이렇게 상태가 정의되어 있었다.

여기서 플레이어가 ENDED 됬을때 seekTo를 사용해 영상 처음으로 이동하고
play로 재생되게 하면 사용자가 따로 터치할때까지 무한으로 재생되게 만든다.

data

이제 쇼츠 데이터를 얻어야한다

그런데 아무리 찾아봐도 shorts에 대한 건 유튜브 문서에 보이지가 않았다.

그래서 생각한게 태그를 검색해서 구별 하는 것이다,

interface YoutubeSearchService {

    @GET("search")
    suspend fun searchResult (
        @Query("forContentOwner") forContentOwner: String? = null, // 콘텐츠 소유자 동영상 검색 아래는 내 동영상
        @Query("forMine") forMine: String? = null, // 위랑 아래랑 둘중하나 혹은 0
        @Query("channelId") channelId: String? = null, //
        @Query("eventType") eventType: String? = null, // completed - 완료된 브로드캐스트, live , upcoming - 예정된 방송
        @Query("maxResults") maxResults: Int? = 50,
        @Query("part") part: String = "snippet",
        @Query("order") order: String = "relevance", //date – 리소스를 만든 날짜를 기준, rating – 높은 평가부터 낮은 평가순, relevance – 검색어와의 관련성을 기준, title – 제목알파벳순, viewCount – 리소스가 조회수가 높은 순
        @Query("regionCode") regionCode: String = "kr", // 국가에서 볼 수 있는 동영상
        @Query("pageToken") pageToken: String? = null,
        @Query("relevanceLanguage") relevanceLanguage: String = "ko", // 언어랑 관련성 높은 영상
        @Query("q") query: String = "react", // 검색어
        @Query("videoDuration") videoDuration: String = "any", // long – 20분보다 긴 동영상, medium – 4분에서 20분 사이의 동영상, short – 4분 미만의 동영상
        @Query("type") type: String = "video", // channel, playlist, video
        @Query("videoType") videoType: String = "any", // any – 모든 동영상을 반환합니다, episode - 프로그램의 에피소드만 검색합니다., movie - 영화만 검색합니다
        @Query("videoSyndicated") videoSyndicated: String = "true", // any – 배급 여부에 관계 없이 모든 동영상을 반환합니다.,true – 배급된 동영상만 검색합니다. 외부에서 재생할 수 있는 동영상
    ) : GenericApiResponse<SearchItemResponse>

현재 내 api검색 서비스 인터페이스는 이렇게 구성되어 있는데, relevanceLanguage ="ko" 로 한국이랑 관련성 높은 영상을 골라주고 검색어에 "#쇼츠 #shorts"를 입력하게 해서 걸러냈다.

이러면 쇼츠 태그에 있는 영상들이 반환되기 때문에 쇼츠영상들만 얻을 수 있었다.

0개의 댓글