aboutsummaryrefslogtreecommitdiff
path: root/official-api.go
diff options
context:
space:
mode:
authorTim Keller <tjk@tjkeller.xyz>2026-05-16 22:04:14 -0500
committerTim Keller <tjk@tjkeller.xyz>2026-05-16 22:04:14 -0500
commit1316aa7ca5e1668bbb7967264540bff3c8dbef86 (patch)
treecc6a4121c13d31a96060e3fa9103ed314578c8cc /official-api.go
parent3bfeed5a50cd1bb405cbe145611410fa4f628413 (diff)
downloadembedtube-1316aa7ca5e1668bbb7967264540bff3c8dbef86.tar.xz
embedtube-1316aa7ca5e1668bbb7967264540bff3c8dbef86.zip
new api implemented server-side
Diffstat (limited to 'official-api.go')
-rw-r--r--official-api.go125
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
+}