package main import ( "encoding/json" "fmt" "net/url" "strings" "tweetdistributor/output" ) // findYouTubeURL returns the first YouTube video link in content, or "" if // there is none. func findYouTubeURL(content string) string { for _, match := range urlPattern.FindAllString(content, -1) { raw := trimURL(match) u, err := url.Parse(raw) if err != nil { continue } switch strings.ToLower(u.Hostname()) { case "youtu.be": if strings.Trim(u.Path, "/") != "" { return raw } case "youtube.com", "www.youtube.com", "m.youtube.com", "music.youtube.com": if u.Path == "/watch" && u.Query().Get("v") != "" { return raw } if strings.HasPrefix(u.Path, "/shorts/") || strings.HasPrefix(u.Path, "/live/") { return raw } } } return "" } // oEmbedResponse is the part of YouTube's oEmbed document we care about. type oEmbedResponse struct { Title string `json:"title"` AuthorName string `json:"author_name"` ThumbnailURL string `json:"thumbnail_url"` } // youtubePreview builds the preview card for a YouTube link by asking // YouTube's oEmbed endpoint for the title, channel and thumbnail. func youtubePreview(videoURL string) (*output.Preview, error) { endpoint := "https://www.youtube.com/oembed?format=json&url=" + url.QueryEscape(videoURL) body, err := fetch(endpoint) if err != nil { return nil, fmt.Errorf("fetching preview for %s: %w", videoURL, err) } var oembed oEmbedResponse if err := json.Unmarshal(body, &oembed); err != nil { return nil, fmt.Errorf("parsing preview for %s: %w", videoURL, err) } preview := &output.Preview{ URL: videoURL, Title: oembed.Title, Description: oembed.AuthorName, } if oembed.ThumbnailURL != "" { thumb, err := downloadImage(oembed.ThumbnailURL, "thumbnail.jpg", "image/jpeg") if err != nil { // A card without its thumbnail is still worth posting. return preview, nil } preview.Thumb = &thumb } return preview, nil }