diff options
| author | Tim Keller <tjk@tjkeller.xyz> | 2026-05-16 22:04:14 -0500 |
|---|---|---|
| committer | Tim Keller <tjk@tjkeller.xyz> | 2026-05-16 22:04:14 -0500 |
| commit | 1316aa7ca5e1668bbb7967264540bff3c8dbef86 (patch) | |
| tree | cc6a4121c13d31a96060e3fa9103ed314578c8cc /official-api.go | |
| parent | 3bfeed5a50cd1bb405cbe145611410fa4f628413 (diff) | |
| download | embedtube-1316aa7ca5e1668bbb7967264540bff3c8dbef86.tar.xz embedtube-1316aa7ca5e1668bbb7967264540bff3c8dbef86.zip | |
new api implemented server-side
Diffstat (limited to 'official-api.go')
| -rw-r--r-- | official-api.go | 125 |
1 files changed, 125 insertions, 0 deletions
diff --git a/official-api.go b/official-api.go new file mode 100644 index 0000000..39db1b8 --- /dev/null +++ b/official-api.go @@ -0,0 +1,125 @@ +package main + +import ( + "encoding/json" + "fmt" + "net/http" + "time" +) + +/* function to make an api request */ +const youtubeAPI = "https://www.googleapis.com/youtube/v3/" + +func apiRequest[T any](videoId string, endpoint string, videoIdParam string, part string) (T, error) { + var zero T + if apiKey == "" { + return zero, fmt.Errorf("API_KEY environment variable not set") + } + + url := fmt.Sprintf("%s?part=%s&%s=%s&key=%s", youtubeAPI + endpoint, part, videoIdParam, videoId, apiKey) + resp, err := http.Get(url) + if err != nil { + return zero, fmt.Errorf("Failed to fetch video info: " + err.Error()) + } + defer resp.Body.Close() + + if resp.StatusCode != http.StatusOK { + return zero, fmt.Errorf("YouTube API error: " + resp.Status) + } + + var data T + err = json.NewDecoder(resp.Body).Decode(&data) + if err != nil { + return zero, fmt.Errorf("Failed to decode YouTube API response: " + err.Error()) + } + + return data, nil +} + +/* youtube api types */ +type ytVideoDetails struct { + Items []struct { + Snippet struct { + Channel string `json:"channel"` + Description string `json:"description"` + PublishedAt time.Time `json:"publishedAt"` + Tags []string `json:"tags"` + } `json:"snippet"` + Statistics struct { + CommentCount int `json:"commentCount"` + LikeCount int `json:"likeCount"` + ViewCount int `json:"viewCount"` + } `json:"statistics"` + } `json:"items"` +} + +type ytComment struct { + Snippet struct { + AuthorDisplayName string `json:"authorDisplayName"` + TextDisplay string `json:"textDisplay"` + PublishedAt time.Time `json:"publishedAt"` + UpdatedAt time.Time `json:"updatedAt"` + LikeCount int `json:"likeCount"` + } `json:"snippet"` +} + +type ytComments struct { + Items []struct { + Snippet struct { + TopLevelComment ytComment `json:"topLevelComment"` + } `json:"snippet"` + Replies struct { + Comments []ytComment `json:"comments,omitempty"` + } `json:"replies"` + } `json:"items"` +} + +/* api source interface definition */ +type APISourceOfficial struct {} + +func (a *APISourceOfficial) getDetails(videoId string) (VideoDetails, error) { + d, err := apiRequest[ytVideoDetails](videoId, "videos", "id", "snippet,statistics,topicDetails") + + n := d.Items[0].Snippet + t := d.Items[0].Statistics + return VideoDetails{ + Channel: n.Channel, + DatePublished: n.PublishedAt, + Description: n.Description, + NumComments: t.CommentCount, + NumLikes: t.LikeCount, + NumViews: t.ViewCount, + Tags: n.Tags, + }, err +} + +func genComment(c ytComment, r []ytComment) Comment { + n := c.Snippet + var replies []*Comment + for _, rc := range r { + temp := genComment(rc, []ytComment{}) // NOTE temp variable required here + replies = append(replies, &temp) // cannot reference return value of genComment w/o temp var + } + + return Comment{ + Author: n.AuthorDisplayName, + Body: n.TextDisplay, + DatePublished: n.PublishedAt, + DateUpdated: n.UpdatedAt, + Likes: n.LikeCount, + Replies: replies, + } +} + +func (a *APISourceOfficial) getComments(videoId string) ([]Comment, error) { + const maxResults = 100 + d, err := apiRequest[ytComments](videoId, "commentThreads", "videoId", "snippet,replies&maxResults=" + fmt.Sprint(maxResults)) // TODO configure max results + + var comments []Comment + for _, c := range d.Items { + tlc := c.Snippet.TopLevelComment + r := c.Replies.Comments + comments = append(comments, genComment(tlc, r)) + } + return comments, err +} |
