get_video_metadata.go
2528 bytes
1package youtube
2
3import (
4 "context"
5 "encoding/json"
6 "fmt"
7 "io"
8 "math"
9 "net/http"
10 "net/url"
11 "time"
12
13 "github.com/TheEdgeOfRage/ytrssil-api/models"
14)
15
16func (c *youTubeClient) GetVideoMetadata(ctx context.Context, videoID string) (*models.Video, error) {
17 query := url.Values{}
18 query.Add("id", videoID)
19 query.Add("part", "snippet,contentDetails,player,liveStreamingDetails")
20 query.Add("key", c.apiKey)
21
22 req, err := http.NewRequestWithContext(ctx, http.MethodGet, "", nil)
23 if err != nil {
24 return nil, fmt.Errorf("failed to set up request: %w", err)
25 }
26 req.URL = &url.URL{
27 Scheme: "https",
28 Host: "www.googleapis.com",
29 Path: "/youtube/v3/videos",
30 RawQuery: query.Encode(),
31 }
32
33 c.log.Info("Making request to YouTube API for video metadata", "videoID", videoID)
34 resp, err := http.DefaultClient.Do(req)
35 if err != nil {
36 return nil, fmt.Errorf("failed to fetch video details: %w", err)
37 }
38 defer resp.Body.Close()
39
40 if resp.StatusCode != http.StatusOK {
41 body, err := io.ReadAll(resp.Body)
42 var bodyStr string
43 if err != nil {
44 c.log.Error("failed to decode error body", "error", err)
45 bodyStr = "failed to decode body"
46 } else {
47 bodyStr = string(body)
48 }
49 return nil, fmt.Errorf("got non-200 status from YouTube API [%d]: %v", resp.StatusCode, bodyStr)
50 }
51
52 decoder := json.NewDecoder(resp.Body)
53 var respData APIVideoListResponse
54 err = decoder.Decode(&respData)
55 if err != nil {
56 return nil, fmt.Errorf("failed to decode video details: %w", err)
57 }
58
59 if len(respData.Items) == 0 {
60 return nil, fmt.Errorf("video not found [%s]", videoID)
61 }
62
63 videoData := respData.Items[0]
64 video := &models.Video{
65 ID: videoData.ID,
66 Title: videoData.Snippet.Title,
67 ChannelName: videoData.Snippet.ChannelName,
68 ChannelID: videoData.Snippet.ChannelID,
69 }
70
71 publishedTime, err := time.Parse(time.RFC3339, videoData.Snippet.PublishedAt)
72 if err != nil {
73 return nil, fmt.Errorf("failed to parse published time [%v]: %w", videoData.Snippet.PublishedAt, err)
74 }
75 video.PublishedTime = publishedTime
76
77 duration, err := parseISO8601Duration(videoData.ContentDetails.Duration)
78 if err != nil {
79 return nil, fmt.Errorf("failed to parse video duration [%v]: %w", videoData.ContentDetails.Duration, err)
80 }
81 video.DurationSeconds = int(math.Round(duration.Seconds()))
82
83 video.IsShort, err = c.isShort(ctx, videoID)
84 if err != nil {
85 return nil, err
86 }
87
88 video.IsLive = videoData.LiveStreamingDetails != nil && videoData.LiveStreamingDetails.ActualEndTime == ""
89
90 return video, nil
91}