package main import ( "encoding/json" "fmt" "net/http" "net/url" ) type videoMeta struct { Title string `json:"title"` Description string `json:"description"` } type ytVideosResponse struct { Items []struct { Snippet struct { Title string `json:"title"` Description string `json:"description"` } `json:"snippet"` } `json:"items"` } type searchResult struct { VideoID string `json:"videoId"` Title string `json:"title"` ChannelTitle string `json:"channelTitle"` Thumbnail string `json:"thumbnail"` } type ytSearchResponse struct { Items []struct { ID struct { VideoID string `json:"videoId"` } `json:"id"` Snippet struct { Title string `json:"title"` ChannelTitle string `json:"channelTitle"` Thumbnails struct { Medium struct { URL string `json:"url"` } `json:"medium"` } `json:"thumbnails"` } `json:"snippet"` } `json:"items"` } func searchVideos(query, apiKey string) ([]searchResult, error) { q := url.Values{} q.Set("part", "snippet") q.Set("type", "video") q.Set("maxResults", "12") q.Set("q", query) q.Set("key", apiKey) resp, err := http.Get("https://www.googleapis.com/youtube/v3/search?" + q.Encode()) if err != nil { return nil, err } defer resp.Body.Close() if resp.StatusCode != http.StatusOK { return nil, fmt.Errorf("youtube search status %d", resp.StatusCode) } var parsed ytSearchResponse if err := json.NewDecoder(resp.Body).Decode(&parsed); err != nil { return nil, err } results := make([]searchResult, 0, len(parsed.Items)) for _, item := range parsed.Items { if item.ID.VideoID == "" { continue } results = append(results, searchResult{ VideoID: item.ID.VideoID, Title: item.Snippet.Title, ChannelTitle: item.Snippet.ChannelTitle, Thumbnail: item.Snippet.Thumbnails.Medium.URL, }) } return results, nil } func fetchVideoMeta(videoID, apiKey string) (*videoMeta, error) { q := url.Values{} q.Set("part", "snippet") q.Set("id", videoID) q.Set("key", apiKey) resp, err := http.Get("https://www.googleapis.com/youtube/v3/videos?" + q.Encode()) if err != nil { return nil, err } defer resp.Body.Close() if resp.StatusCode != http.StatusOK { return nil, fmt.Errorf("youtube api status %d", resp.StatusCode) } var parsed ytVideosResponse if err := json.NewDecoder(resp.Body).Decode(&parsed); err != nil { return nil, err } if len(parsed.Items) == 0 { return nil, fmt.Errorf("video not found: %s", videoID) } return &videoMeta{ Title: parsed.Items[0].Snippet.Title, Description: parsed.Items[0].Snippet.Description, }, nil }