XML 피드에서 Content ID API로 이전

참고: YouTube Content ID API는 YouTube 콘텐츠 파트너가 사용하도록 설계되었으며 일부 개발자나 YouTube 사용자는 액세스할 수 없습니다. Google API 콘솔에 나열된 서비스 중 하나로 YouTube Content ID API가 표시되지 않으면 YouTube 고객센터에서 YouTube 파트너 프로그램에 대해 자세히 알아보세요.

이 가이드에서는 YouTube 콘텐츠 파트너가 YouTube의 권한 관리 시스템에서 콘텐츠를 처리하기 위해 XML 콘텐츠 피드에서 YouTube Content ID API로 마이그레이션할 수 있는 방법에 대해 설명합니다.

개요

YouTube의 콘텐츠 ID 권한 관리 시스템을 활용하려면 저작물에 대한 메타데이터, 소유권 정보, 정책 설정 및 참조 자료를 YouTube에 제공해야 합니다.

YouTube는 저작물 데이터를 전송하기 위한 다양한 옵션을 제공합니다. 많은 콘텐츠를 정기적으로 전송하는 파트너는 YouTube DDEX 피드 또는 YouTube에서 제공하는 스프레드시트 템플릿으로 저작물 정보를 지정하는 일괄 업로드 방법을 사용하는 경우가 많습니다. 또는 YouTube Content ID API를 사용하여 업로드 절차를 더 세밀하게 제어할 수 있습니다.

API를 사용하면 XML 피드 및 스프레드시트의 일괄 처리와 달리 저작물과 기타 리소스를 하나씩 만들 수 있습니다. API에 개별적으로 접근하면 더 빠르고 안정적으로 업로드할 수 있습니다. 개별 작업의 성공 여부를 모니터링하고 발생하는 문제에 즉시 대응하여 트랜잭션 방식으로 업로드 프로세스를 관리합니다. YouTube에서 생성한 ID를 사용할 수 있으므로 후처리 일괄 작업을 사용하는 대신 이 ID로 콘텐츠 관리 시스템을 업데이트할 수 있습니다.

이 문서에서는 YouTube Content ID API를 사용하여 YouTube 권한 관리 시스템에서 저작물을 정의하는 방법을 설명합니다. 여기서는 현재 YouTube DDEX 피드를 사용해 저작물을 정의하고 있다고 가정하고 API를 사용하여 XML 콘텐츠 피드의 기능을 다시 만드는 방법을 설명합니다. 더 구체적으로 설명하자면, 샘플 XML 콘텐츠 피드를 사용하고 Python 클라이언트 라이브러리를 사용하여 동일한 효과를 달성하는 API 명령어를 식별합니다.

YouTube에 저작물 데이터를 전송하는 방법

XML 콘텐츠 피드 또는 Content ID API를 사용하면 최종 결과는 동일합니다. 즉, 파트너 계정에서 YouTube 권한 관리 리소스를 만들거나 업데이트합니다. 경우에 따라 YouTube 동영상도 만들거나 업데이트합니다.

XML 콘텐츠 피드와 API는 작업에 다르게 접근합니다. XML 콘텐츠 피드를 사용하여 리소스 및 리소스 간의 관계에 대한 모든 정보를 포함하는 단일 파일을 만듭니다. YouTube는 피드를 청사진으로 사용하여 일괄 업로드 과정에서 실제 리소스를 만듭니다. API를 사용하면 리소스를 일괄로 만들지 않고 하나씩 만들 수 있습니다. 각 개별 리소스 및 API 호출의 진행 상태와 성공 여부를 모니터링할 수 있습니다.

API 메서드를 사용하여 리소스를 직접 만들 때는 YouTube 일괄 업로드 프로세스에서 내부적으로 처리하는 특정 작업을 명시적으로 수행해야 합니다. 특히 미디어 파일을 업로드하고 작업을 다시 시도하거나 오류를 보고하여 문제에 대응해야 합니다. 또한 올바른 순서로 작업을 수행해야 합니다. 예를 들어 동영상을 만들어야 동영상의 소유권을 주장할 수 있습니다.

YouTube XML 콘텐츠 피드 형식에 있는 루트 수준의 각 요소는 YouTube API를 사용하여 만드는 리소스 또는 리소스 속성에 해당합니다.

XML 요소 및 해당 API 리소스
<asset> asset
<file> videos.fileDetails (YouTube Data API) 또는
thumbnail (YouTube Data API) 또는
reference
<ownership> ownership
<rights_admin> assetMatchPolicy (동영상 일치 관련 정책용)
claims.policy (사용 정책용)
<rights_policy> policy
<video> videos (위치: YouTube Data API)
<video_breaks> videoAdvertisingOption.adbreaks[ ]
<ad_policy> videoAdvertisingOption
<claim> claim
<playlist> playlist (위치: YouTube Data API)
<relationship> 다양함

참고: YouTube Content ID API는 현재 캡션 파일, 예고편 또는 앨범 아트워크 파일을 지원하지 않습니다.

<relationship> 요소의 처리는 XML 피드와 API의 차이를 잘 보여줍니다.

  • XML <relationship> 요소는 <item><related_item> 요소 사이에 관계가 있음을 나타내지만 이 관계의 특성은 명시하지 않습니다. YouTube 업로드 절차에서는 항목 유형에서 적절한 관계를 추론합니다. 예를 들어 저작물과 동영상의 관계는 YouTube에서 동영상 파일을 저작물에 대한 참조로 사용해야 함을 나타내는 반면에 광고 정책과 동영상의 관계는 동영상에 대한 정책을 설정합니다.

  • YouTube Content ID API를 사용하면 적절한 관계를 명시적으로 설정하는 특정 API 리소스를 사용할 수 있습니다. 동영상을 저작물의 참조로 사용하려면 reference 리소스를 만듭니다. 동영상에 대한 광고 정책을 설정하려면 videoAdvertisingOptions 리소스를 만듭니다. 저작물의 속성을 설정하려면 먼저 저작물이 있어야 합니다.

스크립트를 통해 애셋 데이터 업로드

API를 사용하여 저작물 데이터를 업로드하려면 API 요청을 보내고 파일을 업로드하며 YouTube 권한 관리 리소스를 만들 수 있는 스크립트를 작성해야 합니다. 이 섹션에서는 스크립트 작성 가이드를 제공합니다.

주요 단계는 다음과 같습니다.

  1. YouTube API를 사용하는 기본 스크립트를 만듭니다.
  2. 미디어 파일을 업로드하는 코드를 추가합니다.
  3. 권한 관리 리소스를 만드는 코드를 추가합니다.
  4. 콘텐츠 전달 시스템에서 작동하도록 스크립트를 조작합니다.

문서의 마지막 부분에 코드 샘플이 제공됩니다 (4단계 제외). 코드는 Python으로 작성되지만 기타 인기 프로그래밍 언어에 대한 클라이언트 라이브러리도 제공됩니다.

1단계: API 호출하기

첫 번째 요청 보내기 가이드에서는 YouTube Content API 요청을 보내는 기본 스크립트를 작성하는 방법을 설명합니다. 가이드의 샘플 스크립트는 사용자를 인증하고, API 요청을 사용할 권한을 부여하고, YouTube Content ID API와 상호작용하기 위한 리소스를 구축합니다.

이 스크립트는 이 예에서 사용된 스크립트의 기반으로 사용됩니다. 하지만 위에서 설명한 것처럼 이 예에서는 YouTube 동영상을 만들고 소유권을 주장하는 방법을 설명하므로 YouTube 동영상을 업로드하려면 YouTube Data API를 사용해야 합니다. YouTube Data API에도 액세스하기 위해 가이드의 스크립트를 사용 설정하려면 다음 2가지 항목을 추가합니다.

  • 튜토리얼의 2단계에서 flow_from_clientsecrets 호출의 scope 매개변수에 URI를 추가하여 YouTube Data API의 업로드 기능에 대한 액세스를 승인합니다.

    FLOW = flow_from_clientsecrets( 'client_secrets.json ',
               scope='https://www.googleapis.com/auth/youtubepartner https://www.googleapis.com/auth/youtube.upload',
               message= 'error message')
  • 튜토리얼의 3단계에서 YouTube Data API와 상호작용하기 위한 리소스를 구성합니다.

    service = build("youtubePartner", "v1", http=http, static_discovery=False)
    dataApi = build("youtube", "v3", http=http)

2단계: 동영상 파일 업로드

다음 단계는 동영상 파일을 업로드하는 함수를 추가하는 것입니다. 안정성을 최대화하기 위해 재개 가능한 업로드 프로토콜을 사용합니다. 이 프로토콜은 네트워크 중단 또는 기타 전송 실패 후 업로드 작업을 재개할 수 있게 해주므로, 네트워크 오류가 발생할 때 시간과 대역폭을 절약할 수 있습니다.

YouTube Data API 문서의 동영상 업로드 가이드에서는 샘플 업로드 스크립트 upload_video.py를 제공합니다. upload_video.pyresumable_upload 함수를 추가하여 1단계의 기본 스크립트를 업데이트합니다. 또한 함수가 종속된 import 문과 변수 정의도 추가해야 합니다.

3단계: YouTube 권한 관리 리소스 만들기

기본 프레임워크가 준비되면 YouTube 저작물, 동영상 및 관련 리소스를 만드는 코드를 추가할 준비가 된 것입니다.

리소스를 만드는 순서가 중요합니다. 예를 들어 저작물에 대한 동영상 소유권을 주장하려면 먼저 저작물을 만들고 동영상을 업로드해야 합니다.

영화, TV, 뮤직 비디오 및 웹 동영상 저작물에 대한 작업의 일반적인 순서는 다음과 같습니다.

  1. 애셋 리소스 (assets.insert) 만들기
  2. 저작물의 소유권 (ownership.update) 및 동영상 일치 관련 정책 (assetMatchPolicy.update) 업데이트
  3. 메타데이터를 설정하고 미디어 파일을 업로드하여 동영상 리소스 (videos.insert)를 만듭니다.
  4. 동영상의 광고 정책 업데이트 (videoAdvertisingOptions.update)
  5. 저작물을 대신하여 동영상 소유권 주장 (claims.insert)
  6. 소유권 주장이 제기된 동영상 (references.insert)을 사용하여 저작물에 대한 참조 만들기

음원 또는 참조 전용 저작물에 대한 작업의 일반적인 순서는 위와 동일하며 3~5단계가 생략됩니다.

  1. 애셋 리소스 (assets.insert) 만들기
  2. 저작물의 소유권 (ownership.update) 및 동영상 일치 관련 정책 (assetMatchPolicy.update) 업데이트
  3. 애셋 (references.insert)에 대한 참조를 만들고 미디어 파일을 업로드합니다.

이 섹션의 나머지 부분에서는 샘플 XML 피드를 샘플 스크립트에 있는 해당 코드와 비교하여 각 단계에 대한 세부정보를 제공합니다.

3.1단계: 애셋 만들기

첫 번째 호출은 API의 assets.insert 메서드를 호출하는 것입니다. XML 피드의 다음 부분에 해당합니다.

<asset type="web">
  <title>Broadcast Yourself!: Using the YouTube Live APIs to stream to the world</title>
  <description>See a preview of the Google I/O presentation.</description>
</asset>

asset 리소스에서 이러한 값을 지정하려면 다음 속성 값을 설정합니다.

{
  "type": "web",
  "metadata": {
    "title": "Broadcast Yourself!: Using the YouTube Live APIs to stream to the world",
    "description": "See a preview of the Google I/O presentation."
  }
}

API가 반환한 리소스에서 id 속성을 추출합니다. 이 속성은 YouTube가 저작물을 고유하게 식별하기 위해 할당한 저작물 ID를 식별합니다. 이 값은 몇 가지 후속 API 호출에 필요하며 이 문서의 뒷부분에서 assetId로 식별됩니다.

3.2단계: 저작물 소유권 업데이트

저작물을 만들었으면 API의 ownership.update 메서드를 사용해 저작물의 소유권을 설정합니다. 이 단계는 XML 피드의 다음 부분에 해당합니다.

<ownership/>
<relationship>
  <item path="/feed/asset[1]"/>
  <related_item path="/feed/ownership[1]"/>
</relationship>

XML은 콘텐츠의 전 세계 소유권을 가지고 있음을 나타내는 빈 <ownership> 태그를 사용합니다.

API 요청은 assetId 요청 매개변수를 1단계에서 얻은 assetId로 설정합니다. 또한 아래에 표시된 속성 값으로 ownership 리소스를 지정합니다.

  • assetId (요청 매개변수): 1단계에서 얻은 assetId로 설정합니다.
  • general[0].ratio: 100
  • general[0].owner: 'your_content_owner_name'
  • general[0].type: 'exclude'
  • general[0].territories: []

{
  "general": [
    "ratio": 100,
    "owner": "your_content_owner_name",
    "type": "exclude",
    "territories": []
  ]
}

이러한 속성은 지정된 콘텐츠 소유자가 모든 위치에서 콘텐츠를 100% (general[0].ratio) 소유하고 있음을 나타냅니다. 콘텐츠 소유자는 your_content_owner_name을 실제 콘텐츠 소유자 이름으로 바꿔야 합니다. 실제로 이 ownership 리소스는 소유자가 제공된 지역 목록 (general[0].territories)을 제외한 모든 곳에서 콘텐츠를 소유하고 있음을 나타냅니다 (general[0].type). 하지만 이 속성의 값이 빈 목록이므로 실제로 제외된 지역이 없습니다.

3.3단계: 저작물의 동영상 일치 관련 정책 설정

YouTube 권한 관리 시스템은 동영상 일치 관련 정책을 저작물과 연결하거나 사용 정책을 소유권이 주장된 동영상과 연결하는 데 2가지 방법을 제공합니다.

  • 이전에 저장된 정책을 사용합니다. 이 접근 방식에서는 저작물의 동영상 일치 관련 정책을 설정하거나 소유권 주장을 만들 때 API를 사용하여 저장된 정책을 가져오고 사용할 정책을 찾은 다음 정책의 고유 ID를 지정합니다.

  • 저작물 또는 소유권 주장을 만들 때 policy 리소스를 정의합니다. 이 경우 정책 리소스가 저장되지 않으므로 다른 저작물이나 소유권 주장에 적용할 수 없습니다.

저장된 정책을 사용하는 첫 번째 방법을 사용하는 것이 좋습니다. 이 접근 방식을 사용하면 저장된 정책을 업데이트하는 경우 정책을 사용하는 모든 저작물과 소유권 주장에 변경사항이 자동으로 적용되는 중요한 장점이 있습니다.

그러나 이 문서의 코드 샘플은 두 번째 접근 방식을 사용하여 API의 assetMatchPolicy.update 메서드로 새 저작물에 대한 동영상 일치 관련 정책을 설정합니다. (또한 이 문서에서는 두 번째 접근 방식을 사용하여 동영상에 소유권 주장이 제기될 때 정의되는 사용 정책을 설정합니다.) 저장된 정책의 이름은 파트너 간에 다를 수 있기 때문에 도움말에서 두 번째 접근 방식을 사용합니다. 이 접근 방식을 사용하면 모든 파트너에게 동일한 코드가 작동합니다.

이 단계는 XML 피드의 다음 부분에 해당합니다.

<rights_policy>
  <name>Monetize developer videos</name>
</rights_policy>
<rights_admin owner="True" type="match"/>
<relationship>
  <item path="/feed/rights_admin[1]"/>
  <item path="/feed/rights_policy[1]"/>
  <related_item path="/feed/asset[1]"/>
</relationship>

API 요청은 assetId 매개변수를 1단계에서 얻은 assetId로 설정합니다. 또한 아래에 나열된 속성 값을 설정하는 assetMatchPolicy 리소스를 전송합니다.

{
  "rules": [
    {
      "action": "monetize",
      "conditions": {
        "requiredTerritories": {
          "type": "exclude",
          "territories": []
        }
      }
    }
  ]
}

아래 예는 저장된 정책을 저작물의 동영상 일치 관련 정책으로 설정하는 경우 assetMatchPolicy 리소스가 구성되는 방식을 보여줍니다. 코드에서 PolicyID 문자열을 저장된 정책을 고유하게 식별하는 ID로 대체해야 합니다.

{
  "policyId": "PolicyID"
}

참고: YouTube 콘텐츠 관리자의 정책 페이지와 같이 XML 피드는 이름으로 정책을 지정합니다. 저장된 정책의 ID를 검색하려면 policies.list 메서드를 사용합니다. 정책 ID는 파트너마다 다르며, 기본 정책의 경우에도 마찬가지입니다.

3.4단계: 동영상 리소스 만들기 및 미디어 파일 업로드

Data API의 videos.insert 메서드를 사용하여 동영상을 업로드합니다. 재개 가능한 업로드 가이드에서는 재개 가능한 업로드 절차를 사용하여 동영상을 업로드하기 위한 HTTP 호출을 식별하며, 일부 API 클라이언트 라이브러리도 재개 가능한 업로드를 지원합니다. (아래 샘플 코드는 Google API Python 클라이언트 라이브러리를 통해 재개 가능한 업로드 프로세스를 사용합니다.)

이 단계는 XML 피드의 다음 부분에 해당합니다.

<video>
  <title>Broadcast Yourself!: Using the YouTube Live APIs to stream to the world</title>
  <description>See a preview of the Google I/O presentation.</description>
  <genre>Entertainment</genre>
  <keyword>”Google I/O” “YouTube Live APIs”</keyword>
  <public>True</public>
</video>
<file type="video">
  <filename>GOOG_IO_Broadcast_Yourself.mov</filename>
</file>
<relationship>
  <item path="/feed/file[1]"/>
  <related_item path="/feed/video[1]"/>
</relationship>

API 호출을 위한 커스텀 코드를 작성하는 경우 video 리소스를 만들고 업로드 URL을 반환하는 초기 요청을 전송한 다음 두 번째 요청을 전송하여 동영상 바이너리 파일 데이터를 해당 URL에 업로드합니다. 샘플에서와 같이 Python 클라이언트 라이브러리를 사용하는 경우 동일한 요청으로 video 리소스와 동영상 바이너리 파일 데이터를 전송합니다.

XML 샘플에 설명된 동영상을 만들려면 API 요청에서 part 매개변수의 값을 snippet,status로 설정하고 요청 본문의 video 리소스가 다음 속성을 설정합니다. snippet.categoryId 값 (24)은 XML 피드의 동영상과 연결된 카테고리인 Entertainment 카테고리에 해당합니다. 동영상 카테고리는 부록에 더 자세히 설명되어 있습니다.

{
  "snippet": {
    "title": "Broadcast Yourself!: Using the YouTube Live APIs to stream to the world",
    "description": "See a preview of the Google I/O presentation.",
    "tags": ["Google I/O", "YouTube Live APIs"],
    "categoryId": 24
  },
  "status": {
    "privacyStatus": "private"
  }
}

참고: 동영상을 업로드할 때 동영상의 공개 범위 설정을 private로 설정한 다음 나중에 public로 업데이트하는 것이 좋습니다. 동영상을 공개 동영상으로 업로드하려면 status.privacyStatus 속성을 public로 설정하세요.

API가 반환한 리소스에서 id 속성을 추출합니다. 이 속성은 YouTube가 동영상을 고유하게 식별하기 위해 할당한 동영상 ID를 식별합니다. 이 값은 몇 가지 후속 API 호출에 필요하며 이 문서의 뒷부분에서 videoId로 식별됩니다.

3.5단계: Data API를 폴링하여 동영상이 처리된 시점 확인

참조 만들기와 같은 특정 작업을 수행하려면 YouTube에서 동영상 처리를 완료해야 합니다. 따라서 스크립트의 다음 단계는 동영상 업로드가 완료되었는지 확인하는 것입니다.

업로드 상태를 확인하려면 Data API의 videos.list 메서드를 호출하고 다음 요청 매개변수를 설정합니다.

  • id: videoId (이전 단계에서)
  • part: 'processingDetails'

API는 정확히 하나의 리소스를 포함하는 video 리소스 목록을 반환하며 개발자는 이 리소스의 processingDetails.processingStatus 속성 값을 확인하여 YouTube가 아직 동영상을 처리 중인지 확인해야 합니다. YouTube에서 동영상 처리를 완료하면 속성 값이 processing 이외의 값으로 변경됩니다(예: succeeded 또는 failed).

샘플 코드는 12초마다 videos.list 메서드를 호출하여 동영상 처리가 완료되었는지 확인합니다. 이러한 상태 확인은 XML의 요소에 직접적으로 대응되지 않으며 일괄 업로드로 XML을 처리할 때 암시적으로 처리하는 작업을 나타냅니다.

3.6단계: 광고 정책 설정

YouTube에서 동영상을 처리한 후에는 video 리소스의 광고 설정을 업데이트할 수 있습니다. Content ID API의 videoAdvertisingOptions.update 메서드를 호출하여 동영상에 광고 정책을 설정합니다. 이 작업은 XML 피드의 다음 부분에 해당합니다.

<ad_policy>
  <instream standard="long" trueview="true">
    <prerolls>Allow</prerolls>
    <postrolls>Allow</postrolls>
    <midrolls>Deny</midrolls>
  </instream>
  <overlay>
    <adsense_for_video>Allow</adsense_for_video>
    <invideo>Allow</invideo>
  </overlay>
</ad_policy>
<relationship>
  <item path="/feed/ad_policy[1]"/>
  <related_item path="/feed/video[1]"/>
</relationship>

이 샘플에 표시된 정책을 통해 YouTube는 동영상의 시작 (프리롤) 또는 끝 (포스트롤)에 '긴' 30초 광고를 포함한 TrueView 인스트림 광고 또는 오버레이 광고를 표시할 수 있습니다.

광고 정책을 설정하려면 videoId 매개변수를 이전에 얻은 videoId로 설정하는 요청을 전송합니다. 요청 본문은 아래와 같은 속성을 설정하는 videoAdvertisingOptions 리소스입니다.

{
  "breakPosition": ["preroll", "postroll"],
  "adFormats": ["long", "trueview_instream", "overlay"]
}

3.7단계: 동영상 소유권 주장

이 단계에서는 Content ID API의 claims.insert 메서드를 호출하여 업로드한 동영상의 소유권을 주장하고 동영상의 사용 정책을 설정합니다. 이 단계는 XML 피드의 다음 부분에 해당합니다.

<rights_policy>
  <rule action="monetize"/>
</rights_policy>
<rights_admin owner="True" type="match"/>
<claim type="audiovisual"
       asset="/feed/asset[1]"
       rights_admin="/feed/rights_admin[1]"
       rights_policy="/feed/rights_policy[1]"
       video="/feed/video[1]"/>

저작물 동영상 일치 관련 정책과 마찬가지로 샘플 스크립트는 저장된 정책을 동영상에 연결하는 대신 일회성 정책을 정의합니다. 그러나 이전에 설명한 것처럼 사용 정책 및 동영상 일치 관련 정책을 설정할 때 저장된 정책을 사용할 수 있으며 이 방법이 권장됩니다.

요청과 함께 전송하는 claim 리소스에서 아래 리소스에 표시된 속성을 설정합니다. assetIdvideoId 문자열은 스크립트에서 이전 단계에서 얻은 값으로 대체해야 합니다.

{
  "assetId": assetId,
  "videoId": videoId,
  "contentType": "audiovisual",
  "policy": {
    "rules": [
      {
        "action": "monetize"
      }
    ]
  }
}

API가 반환한 리소스에서 id 속성을 추출합니다. 이 속성은 YouTube가 소유권 주장을 고유하게 식별하기 위해 할당한 소유권 주장 ID를 식별합니다. 이 값은 후속 API 호출에 필요하며 이 문서의 뒷부분에서 claimId로 식별됩니다.

3.8단계: 참조 만들기

Content ID API의 references.insert 메서드를 사용하여 Content ID 시스템의 참조를 만듭니다. 소유권 주장이 제기된 동영상을 참조 콘텐츠로 사용하여 참조를 만들거나 API 호출의 일부로 참조 파일을 업로드할 수 있습니다. 소유권이 주장된 동영상에서 참조를 만드는 경우 샘플 스크립트와 마찬가지로 소유권 주장이 이미 존재해야 합니다.

API 요청에서 claimId 요청 매개변수를 이전 단계에서 얻은 claimid로 설정합니다. 또한 아래에 표시된 속성을 설정하는 reference 리소스를 전송합니다.

{
  "contentType": "audiovisual"
}

소유권 주장이 제기된 동영상이 없는 참조 전용 저작물을 만들려면 위 요청을 다음과 같이 변경합니다.

  1. claimId 요청 매개변수 생략
  2. 업로드된 reference 리소스에 assetId 속성을 추가합니다.
  3. 참조 파일을 API 요청의 media_body로 업로드합니다.

3.9단계: 동영상 공개로 설정

동영상을 만들 때 권장사항을 따랐고 동영상의 공개 설정 상태를 private로 설정했다면 업로드가 완료된 후 공개 범위 설정을 public로 변경할 수 있습니다. YouTube Data API의 videos.listvideos.update 메서드를 사용하여 새로 업로드한 동영상의 video 리소스를 가져온 다음 업데이트합니다.

먼저 다음 요청 매개변수를 사용하여 Data API의 videos.list 메서드를 호출합니다.

  • part=status
  • id=videoId

이 메서드는 지정된 videoId로 동영상을 설명하는 리소스 한 개가 포함된 목록을 반환합니다. 리소스의 status.privacyStatus 속성 값을 public로 변경한 다음 Data API의 videos.update 메서드를 호출합니다. 다음 요청 매개변수를 설정합니다.

  • part=status

요청 본문은 수정된 동영상 리소스입니다.

4단계: 콘텐츠 전송 시스템과 통합

부록의 샘플 코드에는 스크립트에 직접 포함된 리소스에 대한 특정 메타데이터가 포함되어 있습니다. 실제로는 스크립트를 콘텐츠 관리 시스템과 통합하고자 할 수 있습니다. 보다 완전한 콘텐츠 전달 시스템을 위해 다음과 같은 단계를 추가할 수 있습니다.

  • 콘텐츠 관리 시스템을 폴링하여 추가하거나 업데이트해야 하는 저작물을 식별합니다.
  • 콘텐츠 관리 시스템에서 저작물 메타데이터를 가져옵니다.
  • 생성한 저작물, 동영상, 참조 및 소유권 주장을 위해 YouTube에서 제공한 ID와 타임스탬프로 콘텐츠 관리 시스템을 업데이트하여 저작물이 가장 최근에 업데이트되었을 때를 기록합니다.

부록

샘플 XML 피드 파일

<?xml version="1.0" encoding="UTF-8"?>
<feed xmlns="http://www.youtube.com/schemas/cms/2.0"
      notification_email="[email protected]"
      channel="your_channel" content_owner="your_name">
 <asset type="web">
   <title>Broadcast Yourself!: Using the YouTube Live APIs to stream to the world</title>
   <description>See a preview of the Google I/O presentation.</description>
 </asset>
 <video>
   <title>Broadcast Yourself!: Using the YouTube Live APIs to stream to the world</title>
   <description>See a preview of the Google I/O presentation.</description>
   <genre>Entertainment</genre>
   <keyword>”Google I/O” “YouTube Live APIs”</keyword>
   <public>True</public>
 </video>
 <file type="video">
   <filename>GOOG_IO_Broadcast_Yourself.mov</filename>
 </file>
  <relationship>
   <item path="/feed/file[1]"/>
   <related_item path="/feed/video[1]"/>
 </relationship>
 <content_rating system="youtube">L0 N0 S0 V0 D0 F0</content_rating>
 <relationship>
   <item path="/feed/content_rating[1]"/>
   <related_item path="/feed/video[1]"/>
 </relationship>
 <ownership/>
 <relationship>
   <item path="/feed/asset[1]"/>
   <related_item path="/feed/ownership[1]"/>
 </relationship>
 <rights_policy>
   <name>Monetize developer videos</name>
 </rights_policy>
 <rights_admin owner="True" type="match"/>
 <relationship>
   <item path="/feed/rights_admin[1]"/>
   <item path="/feed/rights_policy[1]"/>
   <related_item path="/feed/asset[1]"/>
 </relationship>
 <ad_policy>
   <instream standard="long" trueview="true">
     <prerolls>Allow</prerolls>
     <postrolls>Allow</postrolls>
     <midrolls>Deny</midrolls>
   </instream>
   <overlay>
     <adsense_for_video>Allow</adsense_for_video>
     <invideo>Allow</invideo>
   </overlay>
 </ad_policy>
 <relationship>
   <item path="/feed/ad_policy[1]"/>
   <related_item path="/feed/video[1]"/>
 </relationship>
 <claim type="audiovisual" asset="/feed/asset[1]" rights_admin="/feed/rights_admin[1]" rights_policy="/feed/rights_policy[1]" video="/feed/video[1]"/>
</feed>

샘플 스크립트

샘플 코드는 Google API Python 클라이언트 라이브러리를 사용합니다.

#!/usr/bin/python2.6
# -*- coding: utf-8 -*-
#
# Copyright (C) 2012 Google Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#      http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

"""Simple command-line sample for YouTube Content ID API.

Command-line application that retrieves the information
about given content owner.

Usage:
  $ python yt_partner_api.py --file="/path/to/reference/file"

You can also get help on all the command-line flags the program understands
by running:

  $ python yt_partner_api.py --help

To get detailed log output run:

  $ python yt_partner_api.py --logging_level=DEBUG \
    --file="/path/to/reference/file"
"""

import gflags
import httplib
import httplib2
import json
import logging
import sys
import time
import os

from apiclient.discovery import build
from apiclient.errors import HttpError
from apiclient.http import MediaFileUpload
from oauth2client.file import Storage
from oauth2client.client import AccessTokenRefreshError
from oauth2client.client import flow_from_clientsecrets
from oauth2client.tools import run

# Explicitly tell the underlying HTTP transport library not to retry, since
# we are handling retry logic ourselves.
httplib2.RETRIES = 1

# Maximum number of times to retry before giving up.
MAX_RETRIES = 10

# Always retry when these exceptions are raised.
RETRIABLE_EXCEPTIONS = (httplib2.HttpLib2Error, IOError, httplib.NotConnected,
  httplib.IncompleteRead, httplib.ImproperConnectionState,
  httplib.CannotSendRequest, httplib.CannotSendHeader,
  httplib.ResponseNotReady, httplib.BadStatusLine)

# Always retry when an apiclient.errors.HttpError with one of these status
# codes is raised.
RETRIABLE_STATUS_CODES = [500, 502, 503, 504]

#httplib2.debuglevel = 4
FLAGS = gflags.FLAGS

# The CLIENT_SECRETS_FILE variable specifies the name of a file that contains
# the OAuth 2.0 information for this application, including its client_id and
# client_secret. You can acquire an OAuth 2.0 client ID and client secret from
# the Google API Console at
# https://console.cloud.google.com/.
# See the "Registering your application" instructions for an explanation
# of how to find these values:
# https://developers.google.com/youtube/partner/guides/registering_an_application
# For more information about using OAuth2 to access Google APIs, please visit:
#   https://developers.google.com/accounts/docs/OAuth2
# For more information about the client_secrets.json file format, please visit:
#   https://developers.google.com/api-client-library/python/guide/aaa_client_secrets
CLIENT_SECRETS = 'client_secrets.json'

# Helpful message to display if the CLIENT_SECRETS file is missing.
MISSING_CLIENT_SECRETS_MESSAGE = """
WARNING: Please configure OAuth 2.0

To make this sample run you will need to populate the client_secrets.json file
found at:

%s

with information from the API Console
<https://console.cloud.google.com/ >

""" % os.path.join(os.path.dirname(__file__), CLIENT_SECRETS)

# Flags definition
#
# The gflags module makes defining command-line options easy for
# applications. Run this program with the '--help' argument to see
# all the flags that it understands.
gflags.DEFINE_enum('logging_level', 'ERROR',
    ['DEBUG', 'INFO', 'WARNING', 'ERROR', 'CRITICAL'],
    'Set the level of logging detail.')

gflags.DEFINE_string('file', None, 'The video file to upload.')

def resumable_upload(insert_request):
  response = None
  error = None
  retry = 0
  while response is None:
    try:
      print "Uploading file..."
      status, response = insert_request.next_chunk()
      if 'id' in response:
        print "'video id: %s was successfully uploaded." % (response['id'])
        return response['id']
      else:
        exit("The upload failed with an unexpected response: %s" % response)
    except HttpError, e:
      if e.resp.status in RETRIABLE_STATUS_CODES:
        error = "A retriable HTTP error %d occurred:\n%s" % (e.resp.status,
                                                             e.content)
      else:
        raise
    except RETRIABLE_EXCEPTIONS, e:
      error = "A retriable error occurred: %s" % e

    if error is not None:
      print error
      retry += 1
      if retry > MAX_RETRIES:
        exit("No longer attempting to retry.")

      max_sleep = 2 ** retry
      sleep_seconds = random.random() * max_sleep
      print "Sleeping %f seconds and then retrying..." % sleep_seconds
      time.sleep(sleep_seconds)
  return None

def createRequest(service, resource, operation, **kwargs):
  request = getattr(service, resource)()
  request = getattr(request, operation)(**kwargs)
  return request

def executeOperation(service, resource, operation, **kwargs):
  request = getattr(service, resource)()
  request = getattr(request, operation)(**kwargs)
  return_value = request.execute()
  print json.dumps(return_value)
  return return_value

def main(argv):
  # Let the gflags module process the command-line arguments
  try:
    argv = FLAGS(argv)
  except gflags.FlagsError, e:
    print '%s\nUsage: %s ARGS\n%s' % (e, argv[0], FLAGS)
    sys.exit(1)

  # Set up a Flow object to be used if we need to authenticate.
  FLOW = flow_from_clientsecrets(CLIENT_SECRETS,
             scope='https://www.googleapis.com/auth/youtubepartner https://www.googleapis.com/auth/youtube.upload',
             message=MISSING_CLIENT_SECRETS_MESSAGE)

  # Set the logging according to the command-line flag
  logging.getLogger().setLevel(getattr(logging, FLAGS.logging_level))

  # If the Credentials don't exist or are invalid run through the native client
  # flow. The Storage object will ensure that if successful the good
  # Credentials will get written back to a file.
  storage = Storage('yt_partner_api.dat')
  credentials = storage.get()
  if credentials is None or credentials.invalid:
    credentials = run(FLOW, storage)

  # Create an httplib2.Http object to handle our HTTP requests and authorize it
  # with our good Credentials.
  http = httplib2.Http()
  http = credentials.authorize(http)

  # Create service and retrieve content owner service.
  partnerApi = build("youtubePartner", "v1", http=http)
  dataApi = build("youtube", "v3", http=http)

  try:
    title = 'Top Ten Ridiculous test #u',
    monetize_policy = {'rules': [{'action': 'monetize'}]}

    # Create the asset
    kwargs = {}
    metadata = {'title': title,
                'description': 'Wow this is a really long description'}
    kwargs['body'] = {'metadata': metadata, 'type': 'web'}
    insert_asset = executeOperation(partnerApi, 'assets', 'insert', **kwargs)
    asset_id = insert_asset['id']
    print 'Asset ID is ' + asset_id

    # Set asset ownership
    kwargs = {'assetId': asset_id}
    ownership = {'ratio': 100,
                 'owner': 'psomusictest',
                 'type': 'exclude',
                 'territories': []}
    body = {'general': [ownership], 'id': asset_id}
    kwargs['body'] = body
    set_ownership = executeOperation(partnerApi, 'ownership', 'update',
                                     **kwargs)

    # Set match policy
    kwargs = {'assetId': asset_id, 'body': monetize_policy}
    set_match_policy = executeOperation(partnerApi, 'assetMatchPolicy',
                                        'update', **kwargs)

    # Insert video using resumable upload
    snippet = {'title': title,
               'description': 'Wow this is a really long description',
               'tags': ['fizzle', 'sizzle', 'razzle dazzle'],
               'categoryId': '24'}
    status = {'privacyStatus': 'private'}
    body = { 'snippet': snippet, 'status': status }
    kwargs = {'part': 'snippet,status',
              'body': body,
              'media_body': MediaFileUpload(FLAGS.file,
                                            chunksize=-1,
                                            resumable=True)}

    insert_video = createRequest(dataApi, 'videos', 'insert', **kwargs)
    video_id = resumable_upload(insert_video)

    if not video_id:
      print 'video upload failed, so the rest of this exercise is pointless'
      return
   # Poll to see when video is processed
    kwargs = {'id': video_id, 'part': 'processingDetails'}
    check_video_status = createRequest(dataApi, 'videos', 'list', **kwargs)
    video_processed = False
    sleep_seconds = 12
    while not video_processed:
      status = check_video_status.execute()
      processingDetails = status['items'][0]['processingDetails']
      if processingDetails['processingStatus'] != 'processing':
        print 'hooray, it ' + processingDetails['processingStatus']
        video_processed = True
      elif not 'processingProgress' in processingDetails:
        time.sleep(sleep_seconds)
      else:
        print ('so far, we processed %d/%d parts' % (
            processingDetails['processingProgress']['partsProcessed'],
            processingDetails['processingProgress']['partsTotal']))
        time.sleep(sleep_seconds)

    # Claim the video
    body = {'assetId': asset_id,
            'videoId': video_id,
            'policy': monetize_policy,
            'contentType': 'audiovisual'}
    kwargs = {'body': body}
    claim_video = executeOperation(partnerApi, 'claims', 'insert', **kwargs)
    claim_id = claim_video['id']
    print 'claim ID is ' + claim_id

    # Create the reference
    body = {'assetId': asset_id,
            'videoId': video_id,
            'contentType': 'audiovisual'}
    kwargs = {'claimId': claim_id, 'body': body}
    create_reference = executeOperation(partnerApi, 'references', 'insert',
                                        **kwargs)

    # Set ad policy (update video advertising options)
    ads = {'breakPosition': ['preroll','postroll'],
           'adFormats': ['standard_instream','trueview_instream','overlay']}
    kwargs = {'videoId': video_id, 'body': ads}
    ads = executeOperation(partnerApi, 'videoAdvertisingOptions',
                           'update', **kwargs)

    #9 Update video's privacy status to public
    kwargs = {'part': 'status', 'id': video_id}
    video = executeOperation(dataApi, 'videos', 'list', **kwargs)
    video['items'][0]['status']['privacyStatus'] = 'public'
    kwargs = {'part': 'status', 'body': video['items'][0]}
    video = executeOperation(dataApi, 'videos', 'update', **kwargs)

  except AccessTokenRefreshError:
    print ("The credentials have been revoked or expired, please re-run"
      " the application to re-authorize")

if __name__ == '__main__':
  main(sys.argv)

기타 API 작업

Data API를 사용하여 동영상 카테고리 목록 가져오기

샘플 코드는 snippet.categoryID 속성의 값을 '엔터테인먼트' 장르에 해당하는 24로 설정합니다. 카테고리 ID를 하드코딩하지 않고 스크립트에서 특정 장르의 ID를 조회하도록 하려면 Data API의 videoCategories.list 메서드를 호출하고 다음 요청 매개변수를 설정합니다.

  • part=snippet
  • regionCode=US (regionCode을 다른 값으로 설정하여 다른 국가의 동영상 카테고리를 가져올 수 있음)

API 응답에 있는 각 videoCategory 리소스의 경우 snippet.title 속성의 값을 확인하여 카테고리 이름을 식별하고 원하는 카테고리에서 id 속성을 추출합니다. '엔터테인먼트' 카테고리에 대한 videoCategory 리소스는 다음과 같습니다.

{
  "id": "24",
  "kind": "youtube#videoCategory",
  "etag": "\"idnvT0N6oxG_2o6LCWUdZsqtqtk/I5rstjIK5PCItZFyWV-uw\"",
  "snippet": {
    "channelId": "UCBR8-60-B28hp2BmDPdntcQ",
    "title": "Entertainment"
  }
}