The following code samples, which use the Google APIs Client Library for Go, are available for the YouTube Data API. You can download these code samples from the go
folder of the YouTube APIs code sample repository on GitHub.
- Authorize a request
- Post a channel bulletin (
activities.insert
) - Retrieve my uploads (
playlistItems.list
) - Search by keyword (
search.list
) - Search by topic (
search.list
) - Upload a video (
videos.insert
)
Authorize a request
The code sample below performs OAuth 2.0 authorization by checking for the presence of a local file that contains authorization credentials. If the file is not present, the script opens a browser and waits for a response, then saves the returned credentials locally.
package main import ( "encoding/json" "errors" "flag" "fmt" "io/ioutil" "net" "net/http" "os" "os/exec" "path/filepath" "runtime" "code.google.com/p/goauth2/oauth" ) const missingClientSecretsMessage = ` Please configure OAuth 2.0 To make this sample run, you need to populate the client_secrets.json file found at: %v with information from the Google Developers Console https://cloud.google.com/console For more information about the client_secrets.json file format, please visit: https://developers.google.com/api-client-library/python/guide/aaa_client_secrets ` var ( clientSecretsFile = flag.String("secrets", "client_secrets.json", "Client Secrets configuration") cacheFile = flag.String("cache", "request.token", "Token cache file") ) // ClientConfig is a data structure definition for the client_secrets.json file. // The code unmarshals the JSON configuration file into this structure. type ClientConfig struct { ClientID string `json:"client_id"` ClientSecret string `json:"client_secret"` RedirectURIs []string `json:"redirect_uris"` AuthURI string `json:"auth_uri"` TokenURI string `json:"token_uri"` } // Config is a root-level configuration object. type Config struct { Installed ClientConfig `json:"installed"` Web ClientConfig `json:"web"` } // openURL opens a browser window to the specified location. // This code originally appeared at: // http://stackoverflow.com/questions/10377243/how-can-i-launch-a-process-that-is-not-a-file-in-go func openURL(url string) error { var err error switch runtime.GOOS { case "linux": err = exec.Command("xdg-open", url).Start() case "windows": err = exec.Command("rundll32", "url.dll,FileProtocolHandler", "http://localhost:4001/").Start() case "darwin": err = exec.Command("open", url).Start() default: err = fmt.Errorf("Cannot open URL %s on this platform", url) } return err } // readConfig reads the configuration from clientSecretsFile. // It returns an oauth configuration object for use with the Google API client. func readConfig(scope string) (*oauth.Config, error) { // Read the secrets file data, err := ioutil.ReadFile(*clientSecretsFile) if err != nil { pwd, _ := os.Getwd() fullPath := filepath.Join(pwd, *clientSecretsFile) return nil, fmt.Errorf(missingClientSecretsMessage, fullPath) } cfg := new(Config) err = json.Unmarshal(data, &cfg;) if err != nil { return nil, err } var redirectUri string if len(cfg.Web.RedirectURIs) > 0 { redirectUri = cfg.Web.RedirectURIs[0] } else if len(cfg.Installed.RedirectURIs) > 0 { redirectUri = cfg.Installed.RedirectURIs[0] } else { return nil, errors.New("Must specify a redirect URI in config file or when creating OAuth client") } return &oauth.Config;{ ClientId: cfg.Installed.ClientID, ClientSecret: cfg.Installed.ClientSecret, Scope: scope, AuthURL: cfg.Installed.AuthURI, TokenURL: cfg.Installed.TokenURI, RedirectURL: redirectUri, TokenCache: oauth.CacheFile(*cacheFile), // Get a refresh token so we can use the access token indefinitely AccessType: "offline", // If we want a refresh token, we must set this attribute // to force an approval prompt or the code won't work. ApprovalPrompt: "force", }, nil } // startWebServer starts a web server that listens on http://localhost:8080. // The webserver waits for an oauth code in the three-legged auth flow. func startWebServer() (codeCh chan string, err error) { listener, err := net.Listen("tcp", "localhost:8080") if err != nil { return nil, err } codeCh = make(chan string) go http.Serve(listener, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { code := r.FormValue("code") codeCh <- code // send code to OAuth flow listener.Close() w.Header().Set("Content-Type", "text/plain") fmt.Fprintf(w, "Received code: %v\r\nYou can now safely close this browser window.", code) })) return codeCh, nil } // buildOAuthHTTPClient takes the user through the three-legged OAuth flow. // It opens a browser in the native OS or outputs a URL, then blocks until // the redirect completes to the /oauth2callback URI. // It returns an instance of an HTTP client that can be passed to the // constructor of the YouTube client. func buildOAuthHTTPClient(scope string) (*http.Client, error) { config, err := readConfig(scope) if err != nil { msg := fmt.Sprintf("Cannot read configuration file: %v", err) return nil, errors.New(msg) } transport := &oauth.Transport;{Config: config} // Try to read the token from the cache file. // If an error occurs, do the three-legged OAuth flow because // the token is invalid or doesn't exist. token, err := config.TokenCache.Token() if err != nil { // Start web server. // This is how this program receives the authorization code // when the browser redirects. codeCh, err := startWebServer() if err != nil { return nil, err } // Open url in browser url := config.AuthCodeURL("") err = openURL(url) if err != nil { fmt.Println("Visit the URL below to get a code.", " This program will pause until the site is visted.") } else { fmt.Println("Your browser has been opened to an authorization URL.", " This program will resume once authorization has been provided.\n") } fmt.Println(url) // Wait for the web server to get the code. code := <-codeCh // This code caches the authorization code on the local // filesystem, if necessary, as long as the TokenCache // attribute in the config is set. token, err = transport.Exchange(code) if err != nil { return nil, err } } transport.Token = token return transport.Client(), nil }
Post a channel bulletin
The code sample below calls the API's activities.insert
method to post a bulletin to the channel associated with the request.
package main import ( "flag" "fmt" "log" "code.google.com/p/google-api-go-client/youtube/v3" ) var ( message = flag.String("message", "", "Text message to post") videoID = flag.String("videoid", "", "ID of video to post") playlistID = flag.String("playlistid", "", "ID of playlist to post") ) func main() { flag.Parse() // A bulletin must contain a message and may also contain a video or a // playlist. You can post a message with or without an accompanying video // or playlist, but you can't post a video and playlist at the same time. if *message == "" { log.Fatalf("Please provide a message.") } if *videoID != "" && *playlistID != "" { log.Fatalf("You cannot post a video and a playlist at the same time.") } client, err := buildOAuthHTTPClient(youtube.YoutubeScope) if err != nil { log.Fatalf("Error building OAuth client: %v", err) } service, err := youtube.New(client) if err != nil { log.Fatalf("Error creating YouTube client: %v", err) } // Start making YouTube API calls. parts := "snippet" bulletin := &youtube.Activity;{ Snippet: &youtube.ActivitySnippet;{ Description: *message, }, } if *videoID != "" || *playlistID != "" { parts = "snippet,contentDetails" // The resource ID element value differs depending on // whether a playlist or a video is being posted. var resourceId *youtube.ResourceId switch { case *videoID != "": resourceId = &youtube.ResourceId;{ Kind: "youtube#video", VideoId: *videoID, } case *playlistID != "": resourceId = &youtube.ResourceId;{ Kind: "youtube#playlist", PlaylistId: *playlistID, } } bulletin.ContentDetails = &youtube.ActivityContentDetails;{ Bulletin: &youtube.ActivityContentDetailsBulletin;{ ResourceId: resourceId, }, } } call := service.Activities.Insert(parts, bulletin) _, err = call.Do() if err != nil { log.Fatalf("Error making API call to post bulletin: %v", err.Error()) } fmt.Println("The bulletin was posted to your channel.") }
Retrieve my uploads
The code sample below calls the API's playlistItems.list
method to retrieve a list of videos uploaded to the channel associated with the request. The code also calls the channels.list
method with the mine
parameter set to true
to retrieve the playlist ID that identifies the channel's uploaded videos.
package main import ( "flag" "fmt" "log" "code.google.com/p/google-api-go-client/youtube/v3" ) func main() { flag.Parse() client, err := buildOAuthHTTPClient(youtube.YoutubeReadonlyScope) if err != nil { log.Fatalf("Error building OAuth client: %v", err) } service, err := youtube.New(client) if err != nil { log.Fatalf("Error creating YouTube client: %v", err) } // Start making YouTube API calls. // Call the channels.list method. Set the mine parameter to true to // retrieve the playlist ID for uploads to the authenticated user's // channel. call := service.Channels.List("contentDetails").Mine(true) response, err := call.Do() if err != nil { // The channels.list method call returned an error. log.Fatalf("Error making API call to list channels: %v", err.Error()) } for _, channel := range response.Items { playlistId := channel.ContentDetails.RelatedPlaylists.Uploads // Print the playlist ID for the list of uploaded videos. fmt.Printf("Videos in list %s\r\n", playlistId) nextPageToken := "" for { // Call the playlistItems.list method to retrieve the // list of uploaded videos. Each request retrieves 50 // videos until all videos have been retrieved. playlistCall := service.PlaylistItems.List("snippet"). PlaylistId(playlistId). MaxResults(50). PageToken(nextPageToken) playlistResponse, err := playlistCall.Do() if err != nil { // The playlistItems.list method call returned an error. log.Fatalf("Error fetching playlist items: %v", err.Error()) } for _, playlistItem := range playlistResponse.Items { title := playlistItem.Snippet.Title videoId := playlistItem.Snippet.ResourceId.VideoId fmt.Printf("%v, (%v)\r\n", title, videoId) } // Set the token to retrieve the next page of results // or exit the loop if all results have been retrieved. nextPageToken = playlistResponse.NextPageToken if nextPageToken == "" { break } fmt.Println() } } }
Search by keyword
The code sample below calls the API's search.list
method to retrieve search results associated with a particular keyword.
package main import ( "flag" "fmt" "log" "net/http" "code.google.com/p/google-api-go-client/googleapi/transport" "code.google.com/p/google-api-go-client/youtube/v3" ) var ( query = flag.String("query", "Google", "Search term") maxResults = flag.Int64("max-results", 25, "Max YouTube results") ) const developerKey = "YOUR DEVELOPER KEY" func main() { flag.Parse() client := &http.Client;{ Transport: &transport.APIKey;{Key: developerKey}, } service, err := youtube.New(client) if err != nil { log.Fatalf("Error creating new YouTube client: %v", err) } // Make the API call to YouTube. call := service.Search.List("id,snippet"). Q(*query). MaxResults(*maxResults) response, err := call.Do() if err != nil { log.Fatalf("Error making search API call: %v", err) } // Group video, channel, and playlist results in separate lists. videos := make(map[string]string) channels := make(map[string]string) playlists := make(map[string]string) // Iterate through each item and add it to the correct list. for _, item := range response.Items { switch item.Id.Kind { case "youtube#video": videos[item.Id.VideoId] = item.Snippet.Title case "youtube#channel": channels[item.Id.ChannelId] = item.Snippet.Title case "youtube#playlist": playlists[item.Id.PlaylistId] = item.Snippet.Title } } printIDs("Videos", videos) printIDs("Channels", channels) printIDs("Playlists", playlists) } // Print the ID and title of each result in a list as well as a name that // identifies the list. For example, print the word section name "Videos" // above a list of video search results, followed by the video ID and title // of each matching video. func printIDs(sectionName string, matches map[string]string) { fmt.Printf("%v:\n", sectionName) for id, title := range matches { fmt.Printf("[%v] %v\n", id, title) } fmt.Printf("\n\n") }
Search by topic
The code sample below calls the API's search.list
method to retrieve search results associated with a particular Freebase topic.
package main import ( "encoding/json" "errors" "flag" "fmt" "io/ioutil" "log" "net/http" "net/url" "os" "code.google.com/p/google-api-go-client/googleapi/transport" "code.google.com/p/google-api-go-client/youtube/v3" ) var ( query = flag.String("query", "Google", "Freebase search term") maxResults = flag.Int64("max-results", 25, "Max YouTube results") resultType = flag.String("type", "channel", "YouTube result type: video, playlist, or channel") ) const developerKey = "YOUR DEVELOPER KEY HERE" const freebaseSearchURL = "https://www.googleapis.com/freebase/v1/search?%s" // Notable is struct for unmarshalling JSON values from the API. type Notable struct { Name string ID string } // FreebaseTopic is struct for unmarshalling JSON values from the API. type FreebaseTopic struct { Mid string ID string Name string Notable Notable Lang string Score float64 } // FreebaseResponse is struct for unmarshalling JSON values from the Freebase API. type FreebaseResponse struct { Status string Result []FreebaseTopic } func main() { flag.Parse() topicID, err := getTopicID(*query) if err != nil { log.Fatalf("Cannot fetch topic ID from Freebase: %v", err) } err = youtubeSearch(topicID) if err != nil { log.Fatalf("Cannot make YouTube API call: %v", err) } } // getTopicID queries Freebase with the given string. It then prompts the user // to select a topic, then returns the selected topic so that the topic can be // used to search YouTube for videos, channels or playlists. func getTopicID(topic string) (string, error) { urlParams := url.Values{ "query": []string{topic}, "key": []string{developerKey}, } apiURL := fmt.Sprintf(freebaseSearchURL, urlParams.Encode()) resp, err := http.Get(apiURL) if err != nil { return "", err } else if resp.StatusCode != http.StatusOK { errorMsg := fmt.Sprintf("Received HTTP status code %v using developer key: %v", resp.StatusCode, developerKey) return "", errors.New(errorMsg) } body, err := ioutil.ReadAll(resp.Body) if err != nil { return "", err } var data FreebaseResponse err = json.Unmarshal(body, &data;) if err != nil { return "", nil } if len(data.Result) == 0 { return "", errors.New("No matching terms were found in Freebase.") } // Print a list of topics for the user to select. fmt.Println("The following topics were found:") for index, topic := range data.Result { if topic.Notable.Name == "" { topic.Notable.Name = "Unknown" } fmt.Printf(" %2d. %s (%s)\r\n", index+1, topic.Name, topic.Notable.Name) } prompt := fmt.Sprintf("Enter a topic number to find related YouTube %s [1-%v]: ", *resultType, len(data.Result)) selection, err := readInt(prompt, 1, len(data.Result)) if err != nil { return "", nil } choice := data.Result[selection-1] return choice.Mid, nil } // readInt reads an integer from standard input and verifies that the value // is between the allowed min and max values (inclusive). func readInt(prompt string, min int, max int) (int, error) { // Loop until we have a valid input. for { fmt.Print(prompt) var i int _, err := fmt.Fscan(os.Stdin, &i;) if err != nil { return 0, err } if i < min || i > max { fmt.Println("Invalid input.") continue } return i, nil } } // youtubeSearch searches YouTube for the topic given in the query flag and // prints the results. This function takes a mid parameter, which specifies // a value retrieved using the Freebase API. func youtubeSearch(mid string) error { client := &http.Client;{ Transport: &transport.APIKey;{Key: developerKey}, } service, err := youtube.New(client) if err != nil { return err } // Make the API call to YouTube. call := service.Search.List("id,snippet"). TopicId(mid). Type(*resultType). MaxResults(*maxResults) response, err := call.Do() if err != nil { return err } // Iterate through each item and output it. for _, item := range response.Items { itemID := "" switch item.Id.Kind { case "youtube#video": itemID = item.Id.VideoId case "youtube#channel": itemID = item.Id.ChannelId case "youtube#playlist": itemID = item.Id.PlaylistId } fmt.Printf("%v (%v)\r\n", item.Snippet.Title, itemID) } return nil }
Upload a video
The code sample below calls the API's videos.insert
method to upload a video to the channel associated with the request.
package main import ( "flag" "fmt" "log" "os" "strings" "code.google.com/p/google-api-go-client/youtube/v3" ) var ( filename = flag.String("filename", "", "Name of video file to upload") title = flag.String("title", "Test Title", "Video title") description = flag.String("description", "Test Description", "Video description") category = flag.String("category", "22", "Video category") keywords = flag.String("keywords", "", "Comma separated list of video keywords") privacy = flag.String("privacy", "unlisted", "Video privacy status") ) func main() { flag.Parse() if *filename == "" { log.Fatalf("You must provide a filename of a video file to upload") } client, err := buildOAuthHTTPClient(youtube.YoutubeUploadScope) if err != nil { log.Fatalf("Error building OAuth client: %v", err) } service, err := youtube.New(client) if err != nil { log.Fatalf("Error creating YouTube client: %v", err) } upload := &youtube.Video;{ Snippet: &youtube.VideoSnippet;{ Title: *title, Description: *description, CategoryId: *category, }, Status: &youtube.VideoStatus;{PrivacyStatus: *privacy}, } // The API returns a 400 Bad Request response if tags is an empty string. if strings.Trim(*keywords, "") != "" { upload.Snippet.Tags = strings.Split(*keywords, ",") } call := service.Videos.Insert("snippet,status", upload) file, err := os.Open(*filename) defer file.Close() if err != nil { log.Fatalf("Error opening %v: %v", *filename, err) } response, err := call.Media(file).Do() if err != nil { log.Fatalf("Error making YouTube API call: %v", err) } fmt.Printf("Upload successful! Video ID: %v\n", response.Id) }