Migrate from XML feeds to the Content ID API

Note: The YouTube Content ID API is intended for use by YouTube content partners and is not accessible to all developers or to all YouTube users. If you do not see the YouTube Content ID API as one of the services listed in the Google API Console, see the YouTube Help Center to learn more about the YouTube Partner Program.

This guide explains how YouTube content partners can migrate from XML content feeds to the YouTube Content ID API for ingesting content into YouTube's rights management system.

Overview

To take advantage of YouTube's Content ID rights management system, you need to provide YouTube with metadata, ownership information, policy settings, and reference material for your assets.

YouTube offers a variety of options for delivering your asset data. Partners who regularly deliver a lot of content often use a batch upload method, specifying their asset information using either the YouTube DDEX feed or a YouTube-provided spreadsheet template. Alternatively, the YouTube Content ID API provides finer-grained control over the upload process.

Using the API, you create assets and other resources one by one, in contrast to the batch processing of XML feeds and spreadsheets. The individual approach of the API can result in faster and more reliable uploads. You manage the upload process in a transactional manner, monitoring the success of individual actions and reacting immediately to any issues that arise. You can update your content management system with YouTube-generated IDs as they become available, rather than using a post-processing batch job.

This document describes how to use the YouTube Content ID API to define your assets in the YouTube rights management system. It assumes that you are currently defining your assets using the YouTube DDEX feed, and explains how to recreate the functionality of the XML content feed using the API. More specifically, it uses a sample XML content feed and identifies the API commands that achieve the same effect, using the Python client library.

Ways to deliver asset data to YouTube

The end result of using the XML content feed or the Content ID API is the same: you create (or update) YouTube rights management resources in your partner account. In some cases, you also create (or update) YouTube videos.

The XML content feed and the API approach the task differently. With the XML content feed, you create a single file that contains all of the information about the resources and the relationships between them. YouTube uses the feed as a blueprint to create the actual resources during the batch upload process. Using the API, you create resources one by one rather than in a batch. You can monitor the progress and success of each individual resource and API call.

When you create resources directly using API methods, you have to explicitly perform certain actions that the YouTube batch upload process handles behind the scenes. Most notably, you need to upload your media files and respond to issues (by retrying actions or reporting errors). You also need to perform actions in the correct order; for example, you can't claim a video until after you've created the video.

Each of the root-level elements in the YouTube XML content feed format corresponds to a resource (or resource property) that you create using the YouTube APIs.

XML elements and corresponding API resources
<asset> asset
<file> videos.fileDetails (from the YouTube Data API) or
thumbnail (from the YouTube Data API) or
reference
<ownership> ownership
<rights_admin> assetMatchPolicy (for match policies)
claims.policy (for usage policies)
<rights_policy> policy
<video> videos (from the YouTube Data API)
<video_breaks> videoAdvertisingOption.adbreaks[ ]
<ad_policy> videoAdvertisingOption
<claim> claim
<playlist> playlist (from the YouTube Data API)
<relationship> Various

Note: The YouTube Content ID API does not currently support caption files, trailers, or album artwork files.

The treatment of <relationship> elements nicely illustrates the difference between the XML feed and the API.

  • An XML <relationship> element states that a relationship exists between the <item> and <related_item> elements, but it is not explicit about the nature of that relationship. The YouTube upload process infers the proper relationship from the types of the items. For example, a relationship between an asset and a video indicates that YouTube should use the video file as a reference for the asset, while a relationship between an ad policy and a video sets the policy for the video.

  • Using the YouTube Content ID API, you use specific API resources that explicitly set the proper relationship. To use a video as a reference for an asset, you create a reference resource. To set the ad policy for a video, you create a videoAdvertisingOptions resource. The asset must exist before you can set its properties.

Upload asset data via a script

To upload asset data using the APIs, you need to write a script that can send API requests, upload files, and create YouTube rights management resources. This section provides a tutorial for writing the script.

The major steps are:

  1. Create a basic script that uses YouTube APIs.
  2. Add code that uploads media files.
  3. Add code that creates rights management resources.
  4. Operationalize your script to work with your content delivery system.

A code sample is provided at the end of the document (excluding step 4). Though the code is written in Python, client libraries for other popular programming languages are also available.

Step 1: Make API calls

The Sending a First Request tutorial explains how to write a basic script that sends YouTube Content API requests. The sample script from that tutorial authenticates the user, authorizes the use of API requests, and constructs resources for interacting with the YouTube Content ID API.

That script serves as the foundation of the script used in this example. However, as noted above, this example explains how to create and claim YouTube videos, and you need to use the YouTube Data API to upload YouTube videos. To enable the script from the tutorial to also access the YouTube Data API, make these two additions:

  • Authorize access to the upload functionality in the YouTube Data API by adding its URI to the scope parameter in the flow_from_clientsecrets call (from step 2 in the tutorial).

    FLOW = flow_from_clientsecrets( 'client_secrets.json ',
               scope='https://www.googleapis.com/auth/youtubepartner https://www.googleapis.com/auth/youtube.upload',
               message= 'error message')
  • Construct a resource for interacting with the YouTube Data API (from Step 3 of the tutorial).

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

Step 2: Upload video files

The next step is to add a function that uploads video files. For maximum reliability, you'll use a resumable upload protocol. This protocol lets you resume an upload operation after a network interruption or other transmission failure, saving time and bandwidth in the event of network failures.

The Uploading a Video guide in the YouTube Data API documentation provides a sample upload script, upload_video.py. Update the basic script from step 1 by adding the resumable_upload function from upload_video.py. You also need to add the import statements and variable definitions that the function depends on.

Step 3: Create YouTube rights management resources

With the basic framework in place, you're ready to add the code that creates YouTube assets, videos, and associated resources.

The order in which you create resources is important. For example, you need to create an asset and upload a video before you can claim the video for the asset.

The general order of operations for movie, TV, music video, and web video assets is:

  1. Create the asset resource (assets.insert)
  2. Update the asset's ownership (ownership.update) and match policy (assetMatchPolicy.update)
  3. Create the video resource (videos.insert), setting its metadata and uploading the media file
  4. Update the video's ad policy (videoAdvertisingOptions.update)
  5. Claim the video on behalf of the asset (claims.insert)
  6. Create a reference for the asset using the claimed video (references.insert)

The general order of operations for sound recordings or reference-only assets is the same, omitting steps 3 through 5:

  1. Create the asset resource (assets.insert)
  2. Update the asset's ownership (ownership.update) and match policy (assetMatchPolicy.update)
  3. Create a reference for the asset (references.insert) and upload the media file

The rest of this section provides details about each of these steps, comparing the sample XML feed to the corresponding code in the sample script.

Step 3.1: Create an asset

The first call is to the API's assets.insert method. It corresponds to this portion of the XML feed:

<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>

To specify these values in an asset resource, you would set the following property values:

{
  "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."
  }
}

Extract the id property from the resource that the API returns. This property identifies the asset ID that YouTube assigned to uniquely identify the asset. This value is required in several ensuing API calls and is identified as the assetId later in this documentation.

Step 3.2: Update the asset's ownership

Once you've created an asset, you set your ownership of the asset using the API's ownership.update method. This step corresponds to this portion of the XML feed:

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

The XML uses an empty <ownership> tag, which indicates that you have worldwide ownership of the content.

Your API request will set the assetId request parameter to the assetId obtained in step 1. It will also specify an ownership resource with the property values shown below:

  • assetId (request parameter): Set to the assetId obtained in step 1.
  • 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": []
  ]
}

These properties indicate that the specified content owner – you need to replace your_content_owner_name with your actual content owner name – owns 100% (general[0].ratio) of the content everywhere. In fact, this ownership resource indicates that the owner owns the content everywhere except (general[0].type) the provided list of territories (general[0].territories), but since that property's value is an empty list, no territories are actually excluded.

Step 3.3: Set the asset's match policy

The YouTube rights management system provides two ways to associate a match policy with an asset or a usage policy with a claimed video:

  • Use a previously saved policy. In this approach, you use the API to retrieve your saved policies, find the policy you want to use, and then specify that policy's unique ID when setting an asset's match policy or creating a claim.

  • Define a policy resource when creating the asset or claim. In this case, the policy resource is not saved and, therefore, cannot be applied to other assets or claims.

We recommend that you use the first approach, which relies on saved policies. An important benefit to this approach is that if you update a saved policy, your change automatically affects all of the assets and claims that use that policy.

However, the code samples in this document use the second approach to set the match policy for the new asset, using the API's assetMatchPolicy.update method. (This document also uses the second approach to set the usage policy, which is defined when the video is claimed.) The document uses the second approach because the names of saved policies can vary between partners; this approach ensures that the same code will work for everyone.

This step corresponds to this portion of the XML feed:

<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>

Your API request sets the assetId parameter to the assetId obtained in step 1. It also sends an assetMatchPolicy resource that sets the property values listed below:

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

The example below shows how the assetMatchPolicy resource would be constructed if you were setting a saved policy to be an asset's match policy. Your code would need to replace the PolicyID string with the ID that uniquely identifies your saved policy.

{
  "policyId": "PolicyID"
}

Note: The XML feed, like the YouTube Content Manager's Policies page, specifies policies by name. To retrieve the ID for a saved policy, use the policies.list method. Policy IDs vary from partner to partner, even for the default policies.

Step 3.4: Create a video resource and upload the media file

You upload your video using the Data API's videos.insert method. The Resumable Uploads guide identifies the HTTP calls that you would make to upload videos using a resumable uploading process, and several of our API client libraries also support resumable uploads. (The sample code below uses a resumable uploading process with the Google APIs Python client library.)

This step corresponds to this portion of the XML feed:

<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>

If you are writing custom code to make the API calls, you send an initial request that creates the video resource and returns an upload URL, then send a second request to upload the video binary file data to that URL. If you are using the Python client library (as in the sample), you send the video resource and the video binary file data in the same request.

To create the video described in the XML sample, your API request would set the part parameter's value to snippet,status, and the video resource in the request body would set the following properties. The snippet.categoryId value (24) corresponds to the Entertainment category, which is the category associated with the video in the XML feed. Video categories are discussed in more detail in the appendixes.

{
  "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"
  }
}

Note: We recommend that you set a video's privacy status to private when you upload the video, then update it to public later in the process. If you would rather upload the video as a public video, set the status.privacyStatus property to public.

Extract the id property from the resource that the API returns. This property identifies the video ID that YouTube assigned to uniquely identify the video. This value is required in several ensuing API calls and is identified as the videoId later in this documentation.

Step 3.5: Poll the Data API to determine when the video has been processed

Certain actions, such as creating a reference, require that YouTube has finished processing your video. For that reason, the script's next step is to ensure that the video upload is complete.

To check on the upload status, call the Data API's videos.list method and set the following request parameters:

  • id: videoId (from the previous step)
  • part: 'processingDetails'

The API returns a list of video resources (containing exactly one resource), and you need to check the value of that resource's processingDetails.processingStatus property to determine whether YouTube is still processing the video. The property's value will change to something other than processing, such as succeeded or failed, when YouTube has finished processing the video.

The sample code calls the videos.list method every 12 seconds to determine whether the video has finished processing. This status check does not directly correspond to any element from the XML; it represents an action that a batch upload handles implicitly when it processes the XML.

Step 3.6: Set ad policy

Once YouTube has processed the video, you can update the video resource's ad settings. Call the Content ID API's videoAdvertisingOptions.update method to set the ad policy for the video. This action corresponds to this portion of the XML feed:

<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>

The policy shown in this sample enables YouTube to show TrueView in-stream ads or overlay ads, including "long" 30-second ads, at the beginning (preroll) or end (postroll) of the video.

To set the ad policy, send a request that sets the videoId parameter to the videoId obtained earlier. The body of the request is a videoAdvertisingOptions resource that sets the properties shown below:

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

Step 3.7: Claim the video

In this step, you claim the video that you uploaded – and set the usage policy for the video – by calling the Content ID API's claims.insert method. This step corresponds to this part of the XML feed:

<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]"/>

As with the asset match policy, the sample script defines a one-off policy rather than associating a saved policy with the video. However, as discussed earlier, you can and are encouraged to use saved policies when setting usage and match policies.

In the claim resource that you send with your request, set the properties shown in the resource below. Note that the assetId and videoId strings need to be replaced with the values that the script obtained in previous steps.

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

Extract the id property from the resource that the API returns. This property identifies the claim ID that YouTube assigned to uniquely identify the claim. This value is required in ensuing API calls and is identified as the claimId later in this documentation.

Step 3.8: Create a reference

Use the Content ID API's references.insert method to create a reference for the Content ID system. You can create a reference using a claimed video as the reference content, or you can upload a reference file as part of the API call. If you're creating a reference from a claimed video, as the sample script does, the claim must already exist.

In your API request, set the claimId request parameter to the claimid obtained in the previous step. Also send a reference resource that sets the properties shown below:

{
  "contentType": "audiovisual"
}

To create a reference-only asset (with no claimed video), make the following changes to the request above:

  1. Omit the claimId request parameter
  2. Add the assetId property to the uploaded reference resource.
  3. Upload the reference file as the media_body of the API request.

Step 3.9: Make the video public

If you followed best practices and set your video's privacy status to private when you created it, you can change the privacy status to public after the upload process is successful. Use the YouTube Data API's videos.list and videos.update methods to retrieve and then update the video resource for your newly uploaded video.

First, call the Data API's videos.list method with the following request parameters:

  • part=status
  • id=videoId

This method returns a list containing one resource, which describes the video with the specified videoId. Change the value of that resource's status.privacyStatus property to public, then call the Data API's videos.update method. Set the following request parameter:

  • part=status

The body of the request is the modified video resource.

Step 4: Integrate with your content delivery system

The sample code in the appendixes includes the specific metadata for the resources directly in the script. In practice, you're likely to want to integrate the script with your content management system. For a more complete content delivery system, you might add steps such as:

  • Poll your content management system to identify assets that need to be added or updated.
  • Retrieve asset metadata from the content management system.
  • Update the content management system with YouTube-provided IDs for the created assets, videos, references, and claims, and with timestamps to record when the assets were most recently updated.

Appendix

Sample XML Feed file

<?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>

Sample script

The sample code uses the Google APIs Python client library.

#!/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)

Other API actions

Retrieve a list of video categories using the Data API

The sample code sets the snippet.categoryID property's value to 24, which corresponds to the "Entertainment" genre. If you want your script to look up the ID for a given genre (rather than hardcoding the category IDs), you would call the Data API's videoCategories.list method and set the following request parameters:

  • part=snippet
  • regionCode=US (you could set regionCode to a different value to retrieve video categories for another country)

For each videoCategory resource in the API response, you would check the value of the snippet.title property to identify a category name, and extract the id property from the category you want. Here's what the videoCategory resource looks like for the "Entertainment" category:

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