Parent directory

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}