consume youtube api with golang

master
Chakib Benziane 9 years ago
commit af869235fc

@ -0,0 +1,229 @@
//TODO
// 1- Build model for basic youtube API answer
// We need to put the data into model using JSON Unmarshal
// 2- Test simple channel request based on user handle (we only need channel ID)
// 3- Now we need to get the content inside Items in each Answer
// 4- Get Playlists list from a channel
// 5- Get Videos(PlayListItems) lists from playlist
// DONE
//
// 6- Ignore empty results
// 7- Handle Pagination (not today :))
//
package main
import (
"encoding/json"
"fmt"
"io/ioutil"
"log"
"net/http"
"net/url"
)
// Global variables
var (
YOUTUBE_API_KEY = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
YOUTUBE_API_ENDPOINT = "https://www.googleapis.com/youtube/v3/"
TEST_CHANNEL_HANDLE = "TotalHalibut"
)
/////////////////////////////
//////////// MODELS
///////////////////////////
// model for a generic youtube answer
// We only define the fields we want to capture
// from the json answer from the API
type PageInfo struct {
TotalResults int
ResultsPerPage int
}
type YoutubeAnswer struct {
Kind string
Etag string
Items []Item
PageInfo
// Will be used to get the actual we need (resources)
// Now we need to get the content inside Items in each Answer
// Let's build a type(model) for one Item
// interface{} means any type like void* in C
// Now we can use the ChannelItem type
}
type PlayList struct {
}
type ChannelItem struct {
}
type ResourceId struct {
VideoId string
}
type PlayListItemSnippet struct {
ResourceId ResourceId
}
type PlayListItem struct {
Snippet PlayListItemSnippet
}
// Our container type Item
// We will embed other type of Items here
// to extract data we want from our resources with json.Unmarshal
// Unmarshal should set to nil json fields which are not defined
// There might be a cleaner way to do this
// Ref on embedding: https://github.com/luciotato/golang-notes/blob/master/OOP.md#golang-embedding-is-akin-to-multiple-inheritance-with-non-virtual-methods
type Item struct {
Id string
ChannelItem
PlayList
PlayListItem
}
////////////////////////////
////// API Functions
///////////////////////////
// Helper function to build our youtube query URL
func buildUrl(resource string, params map[string]string) string {
// we build a url.Values object from a map of key,values
queryParams := url.Values{}
// Automatically add the API key params to all our calls
queryParams.Set("key", YOUTUBE_API_KEY)
for k, v := range params {
queryParams.Set(k, v)
}
// Build URL
queryUrl := fmt.Sprint(YOUTUBE_API_ENDPOINT, resource, "?", queryParams.Encode())
return queryUrl
}
// We can make this function more generic and reuse it for other resources
// Since youtube API sends the actual data inside ITEMS we make a func
// that returns the list of Items based on the resource and params we pass
// This func returns a list of any kind of Item type (channel, playlist, video ...)
func getResource(resource string, params map[string]string) []Item {
queryUrl := buildUrl(resource, params)
resp, err := http.Get(queryUrl)
if err != nil {
log.Fatal(err)
}
data, _ := ioutil.ReadAll(resp.Body)
answer := &YoutubeAnswer{}
jsonErr := json.Unmarshal(data, answer)
if jsonErr != nil {
log.Fatal(err)
}
// We need to make an type embedding so we can have polymorphism
// Interfaces are used to implement methods , my mistake
// We want to return a list of any "kind" if Item
return answer.Items
}
func getChannelId(channelHandle string) string {
// Use our buildUrl func
params := map[string]string{
"key": YOUTUBE_API_KEY,
"forUsername": channelHandle,
"part": "id",
}
queryUrl := buildUrl("channels", params)
// Test request
resp, err := http.Get(queryUrl)
if err != nil {
log.Fatal(err)
}
data, _ := ioutil.ReadAll(resp.Body)
answer := &YoutubeAnswer{}
jsonErr := json.Unmarshal(data, answer)
if jsonErr != nil {
log.Fatal(err)
}
return answer.Items[0].Id
}
///////////////////////////////////
func main() {
// Get channel data
channelParams := map[string]string{
"forUsername": TEST_CHANNEL_HANDLE,
"part": "id",
}
channel := getResource("channels", channelParams)[0]
// Use channel Id to get list of playlists
playlistsParams := map[string]string{
"part": "id",
"channelId": channel.Id,
}
playlists := getResource("playlists", playlistsParams)
// Step 2 get list of playlists from channel
fmt.Printf("Id for channel %s is: %s\n", TEST_CHANNEL_HANDLE, channel.Id)
fmt.Println("_________Playlists IDS___________\n")
// Step 3 get list of videos inside each playlist
// Videos are PlayListItem objects
// We want the id of videos also (part=id,snippet)
for _, pl := range playlists {
fmt.Printf("\n%+v", pl)
fmt.Printf("playlist: %s\n", pl.Id)
playListItemsParams := map[string]string{
"part": "id,snippet",
"playlistId": pl.Id,
}
playListItems := getResource("playlistItems", playListItemsParams)
fmt.Printf("video IDs for playlist %s:\n", pl.Id)
for _, plI := range playListItems {
fmt.Printf("video id : %s\n", plI.PlayListItem.Snippet.ResourceId.VideoId)
}
}
return
}
Loading…
Cancel
Save