본문 바로가기

안드로이드/라이브러리 써보기

[안드로이드] Youtube Data 사용하기

1. Google Cloud Platform에서 API 사용 설정하기

앱에 내가 원하는 채널이 있다면, 해당 채널에 대한 Data가 당연히 필요하다. 그리고 이 Data를 받아오기 위해서는 Google API에서 제공하는 Youtube Data API v3를 사용해야 한다. 그럼 먼저 Google API에 들어가보자

 

구글 클라우드 플랫폼 바로가기

 

Google Cloud Platform

하나의 계정으로 모든 Google 서비스를 Google Cloud Platform을 사용하려면 로그인하세요.

accounts.google.com

 

위 링크를 클릭하면 아래 그림과 같은 화면이 나오게 된다. 아래 화면에서 우리가 들어가야할 곳은 왼쪽 메뉴바에 있는 '라이브러리' 섹션이다.

 

 

해당 섹션에 들어가면 무수히 많은 API들이 있는데, 스크롤을 살짝만 내려주면 아래 그림과 같이 Youtube 관련 API들이 옹기종기 모여 있다. 로고도 아주 이쁘게 되어있다. 여기서 오늘의 주인공인 Youtube Data API v3를 클릭

 

 

그리고 나면 아래랑 비슷한 화면이 나온다. 다만 나는 이미 사용 설정을 해두어서, '관리'라는 버튼이 나오는 것이다. 사용 설정을 안했으면 '관리'라고 나오는 글씨 대신에 '사용 설정'이라는 버튼이 나올 것이다. 사용 설정을 하면 '관리'라고 바뀌고 사용할 수 있게 될 것이다.

 

원래 '관리'버튼 대신에 '사용 설정' 이라고 나와야 해유. 전 이미 사용 설정을 눌러버려서 저래 나와유~

 

이제 사용 설정을 하고 나면, 해당 API를 이용할 수 있는 'API Key'를 발급받아야 한다. 그거를 발급받기 위해서 Google API 첫 페이지로 다시 돌아가자. 그리고 아래 그림의 순서대로 누르면 API가 생성된다.

 

순서대로 또이또이하게 하세요~

 

그리고 발급받은 API Key는 메모장에 잘 Copy&Paste를 해두자. 이따 써먹어야 하니까.

 

2. API 사용하기

본격적으로 API를 사용하기위해서 kotlin으로 프로젝트를 생성해준다. 그리고 API사용을 위하여 app수준의 build.gradle파일에다가 먼저 implementation을 해준다.

 

1
2
3
4
    implementation 'com.google.apis:google-api-services-youtube:v3-rev183-1.22.0'
    implementation 'com.google.http-client:google-http-client-android:1.38.1'
    implementation 'com.google.api-client:google-api-client-android:1.31.2'
    implementation 'com.google.api-client:google-api-client-gson:1.31.2'
cs

 

이렇게 하고 난 이후에 kotlin class파일을 만들어 주고 아래 코드를 넣어준다.

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
package kr.uni.temp.tools
 
import android.app.Application
import android.util.Log
import com.google.api.client.http.HttpTransport
import com.google.api.client.http.javanet.NetHttpTransport
import com.google.api.client.json.JsonFactory
import com.google.api.client.json.gson.GsonFactory
import com.google.api.services.youtube.YouTube
import com.google.api.services.youtube.model.SearchResult
import com.google.api.services.youtube.model.Thumbnail
import kr.uni.temp.midoTube.MidoTubeData
import kr.uni.temp.midoTube.MidoTubeViewModel
 
class CallMidoTube(private var application: Application) {
 
    private val API_KEY = "API Key 넣기"
    private val TAG = "TAG"
    private lateinit var result: String
    private val HTTP_TRANSPORT: HttpTransport = NetHttpTransport()
    private val JSON_FACTORY: JsonFactory = GsonFactory()
    private val NUMBER_OF_VIDEOS_RETURNED: Long = 50
 
    fun onCallYoutubeChannel() {
        try {
            val youtube = YouTube.Builder(
                HTTP_TRANSPORT, JSON_FACTORY
            ) { }.setApplicationName("youtube-search-sample").build()
            val search = youtube.search().list("id,snippet")
            val searchResultList = search.setKey(API_KEY)
                .setQ("미도")
                .setChannelId("UCXTyJZJW0kjz5lCI7g8wUKQ")
                .setOrder("date")
                .setType("video")
                .setFields("items(id/kind,id/videoId,snippet/title,snippet/thumbnails/default/url,snippet)")
                .setMaxResults(NUMBER_OF_VIDEOS_RETURNED)
                .execute()
                .items
            if (searchResultList != null) {
                printYoutubeResult(searchResultList.iterator(), "미도")
            }
        } catch (e: Throwable) {
            Log.e("Exception Occurred : ", e.toString())
        }
    }
 
    private fun printYoutubeResult(iteratorSearchResults: Iterator<SearchResult>, query: String?) {
        if (!iteratorSearchResults.hasNext()) {
            println(" There aren't any results for your query.")
        }
        val sb = StringBuilder()
        val dbIn = MidoTubeViewModel(application)
 
        while (iteratorSearchResults.hasNext()) {
            val singleVideo = iteratorSearchResults.next()
            val rId = singleVideo.id
 
                val thumbnail = singleVideo.snippet.thumbnails["default"] as Thumbnail?
                sb.append("ID : " + rId.videoId + " , 제목 : " + singleVideo.snippet.title + " , 썸네일 주소 : " + thumbnail!!.url)
                sb.append("\n")
 
            dbIn.insert(MidoTubeData(rId.videoId, singleVideo.snippet.title, thumbnail.url, "2021-03-04"))
        }
        result = sb.toString()
        Log.e("INFO", result)
    }
}
cs

 

이렇게 넣고나면 속성들이 하나하나 궁금할 것인데 먼저 API_KEY에 아까 메모장에 Copy & Paste 해 두었던 API 키를 먼저 넣어준다. 그리고 setResultList 변수에 정의된 속성들을 하나씩 보자.

 

  • setQ는 검색할 검색어를 지정하는 거다. 나는 '나는 미도' 채널을 검색하는거니까 '미도' 라고 넣었다.
  • setChannelId에는 해당 채널의 ID를 넣는 것인데, 이 ID는 내가 원하는 YouTube 채널에 들어가면, Url에 친절하게 나와있다.
    YouTube 채널 '나는 미도'의 Channel ID 값
  • setOrder의 경우 default값은 SEARCH_SORT_RELEVANCE이며, 원하는 값은 아래와 같다.

  • Type은 channel, playlist, video 3개 중에서 고를 수 있다.
  • setFields의 경우에는 API를 통해 가져올 Data를 결정하는 것이다. 필드값 경우에는 여러모로 찾아봤는데 링크가 없어서 따로 알 도리가 없다. 다만 'snippet' 필드를 넣으면 웬만한 정보는 다 넣어 주어서, 내가 입맛대로 골라 쓰면 더 좋다.
  • setMaxResults 값 같은 경우에는 아무리 수를 크게 넣어봤자, 최대 50개의 video를 가져온다. 고로 최대치로 넣자. 50 팍팍~

 

이렇게 코드를 가져왔으면, 이것을 실행해야 하는데, API를 사용한다는 것은 INTERNET을 사용한다는 의미이다. 따라서 manifest에다가 INTERNET permission을 부여해주고, 비동기로 진행을 할 것이다.

 

1
<uses-permission android:name="android.permission.INTERNET" />
cs

 

위 permission을 manifest의 application 태그 위에다가 작성해준다. 이렇게 되면 app에서 INTERNET을 사용할 수 있는 권한을 획득하게 된 것이다. 그리고 나서 이제 비동기 작업을 진행해 줄 것이다.

 

다만 비동기의 경우 AsyncTask가 Deprecated 되었음으로 Coroutine을 사용해서 비동기로 진행할 것이다.

먼저 app 수준의 build.gradle에서 아래 라이브러리들을 implementation 해준다.

1
2
3
4
    //coroutine
    implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.3.7'
    implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.3.0'
    implementation 'com.google.guava:guava:27.0.1-android'
cs

그 이후에는 YouTube API를 Call하고 싶은 곳에다가

 

1
2
3
4
5
6
7
8
9
CoroutineScope(Dispatchers.Main).launch {
 
    CoroutineScope(Dispatchers.Default).async {
    
        val youtube = CallMidoTube(application)
        youtube.onCallYoutubeChannel()
        
        }
}
cs

 

아래와 같이 코드를 넣으면 아래 그림과 같이 짜잔하고 내가 원하는 채널의 정보가 나오게 된다.

 

이렇게 되면, 기본적으로 내가 원하는 YouTube 채널만 볼 수 있는 앱을 만들기 위한 Data를 얻게 된 것이다. 야호~

 

 

참조

https://cheonjoosung.github.io/blog/an-youtube-api

 

Joo's IT

(안드로이드/코틀린) Service와 Fragment/Activity 사이에서 데이터 전달 및 통신방법 Android Kotlin

cheonjoosung.github.io

 

2022.05.22 추가

몇 분들의 요청이 있어서 다른 파일들 전체를 다 올렸습니다. 개발에 참고하세요~!

 

- MidoTubeData.kt

package kr.uni.temp.midoTube

import androidx.annotation.NonNull
import androidx.room.ColumnInfo
import androidx.room.Entity
import androidx.room.PrimaryKey


@Entity(tableName = "midoTube")
data class MidoTubeData(

    @PrimaryKey(autoGenerate = false)
    @NonNull
    var id: String,

    @ColumnInfo(name = "title")
    var title: String?,

    @ColumnInfo(name = "thumbnilAddr")
    var thumbnail: String?

) {
    constructor() : this("", "", "")
}

 

2. MidoTubeViewModel

package kr.uni.temp.midoTube

import android.app.Application
import androidx.lifecycle.AndroidViewModel
import androidx.lifecycle.LiveData
import kr.uni.temp.repository.MidotubeRepository

class MidoTubeViewModel(application: Application) : AndroidViewModel(application) {


    private val repository = MidotubeRepository(application)
    private val allData = repository.getAll()
    private val titleData = repository.getTitle()
    private val idData = repository.getId()
    private val thumbnailData = repository.getThumbnail()

    fun getAll(): LiveData<List<MidoTubeData>> {
        return this.allData
    }

    fun getId(): LiveData<List<String>> {
        return this.idData
    }

    fun getTitle(): LiveData<List<String>> {
        return this.titleData
    }

    fun getThumbnail(): LiveData<List<String>> {
        return this.thumbnailData
    }

    fun insert(data: MidoTubeData) {
        repository.insert(data)
    }

    fun delete(data: MidoTubeData) {
        repository.delete(data)
    }
}

 

3. MidoTubeRepository

package kr.uni.temp.repository

import android.app.Application
import androidx.lifecycle.LiveData
import kr.uni.temp.midoTube.MidoTubeData
import kr.uni.temp.midoTube.MidoTubeDao
import kr.uni.temp.midoTube.MidoTubeDatabase

class MidotubeRepository(application: Application) {

    private val midoTubeDatabase = MidoTubeDatabase.getInstance(application)!!
    private val midoTubeDao: MidoTubeDao = midoTubeDatabase.midoTubeDao()
    private val midoTubeAllData: LiveData<List<MidoTubeData>> = midoTubeDao.getAll()
    private val midoTubeIdData: LiveData<List<String>> = midoTubeDao.getId()
    private val midoTubeTitleData: LiveData<List<String>> = midoTubeDao.getTitle()
    private val midoTubeThumbnailData: LiveData<List<String>> = midoTubeDao.getThumbnailAddr()

    fun getAll(): LiveData<List<MidoTubeData>> {
        return midoTubeAllData
    }

    fun getId(): LiveData<List<String>> {
        return midoTubeIdData
    }

    fun getTitle(): LiveData<List<String>> {
        return midoTubeTitleData
    }

    fun getThumbnail(): LiveData<List<String>> {
        return midoTubeThumbnailData
    }

    fun insert(data: MidoTubeData) {
        try {
            val thread = Thread(Runnable {
                midoTubeDao.insert(data)
            })
            thread.start()
        } catch (e: Exception) {
            e.printStackTrace()
        }
    }

    fun delete(data: MidoTubeData) {
        try {
            val thread = Thread(Runnable {
                midoTubeDao.delete(data)
            })
            thread.start()
        } catch (e: Exception) {
            e.printStackTrace()
        }
    }
}

 

4. MidoTubeDao

package kr.uni.temp.midoTube

import androidx.lifecycle.LiveData
import androidx.room.*

@Dao
interface MidoTubeDao {

    @Query("SELECT * FROM midoTube")
    fun getAll(): LiveData<List<MidoTubeData>>

    @Query("SELECT id FROM midoTube")
    fun getId(): LiveData<List<String>>

    @Query("SELECT title FROM midoTube")
    fun getTitle(): LiveData<List<String>>

    @Query("SELECT thumbnilAddr FROM midoTube")
    fun getThumbnailAddr(): LiveData<List<String>>

    @Insert(onConflict = OnConflictStrategy.REPLACE)
    fun insert(midoTubeData: MidoTubeData)

    @Delete
    fun delete(midoTubeData: MidoTubeData)
}