123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176 |
- package youtube
- import (
- "encoding/json"
- "fmt"
- "net/http"
- "time"
- "git.linuxforward.com/byom/byom-trends/config"
- "git.linuxforward.com/byom/byom-trends/logger"
- "github.com/sirupsen/logrus"
- )
- type Client struct {
- httpClient *http.Client
- apiKey string
- baseURL string
- logger *logrus.Entry
- }
- type ChannelStats struct {
- Username string `json:"username"`
- ChannelID string `json:"channel_id"`
- SubscriberCount int64 `json:"subscriber_count"`
- VideoCount int64 `json:"video_count"`
- ViewCount int64 `json:"view_count"`
- Engagement float64 `json:"engagement"`
- Country string `json:"country"`
- JoinDate string `json:"join_date"`
- }
- type Video struct {
- ID string `json:"id"`
- Title string `json:"title"`
- Description string `json:"description"`
- PublishedAt time.Time `json:"published_at"`
- Duration string `json:"duration"`
- ViewCount int64 `json:"view_count"`
- LikeCount int64 `json:"like_count"`
- CommentCount int64 `json:"comment_count"`
- ThumbnailURL string `json:"thumbnail_url"`
- Tags []string `json:"tags"`
- Category string `json:"category"`
- Language string `json:"language"`
- }
- type PlaylistStats struct {
- ID string `json:"id"`
- Title string `json:"title"`
- VideoCount int64 `json:"video_count"`
- ViewCount int64 `json:"view_count"`
- Description string `json:"description"`
- }
- func NewClient(cfg *config.YouTubeConfig) *Client {
- return &Client{
- httpClient: &http.Client{
- Timeout: time.Second * 10,
- },
- apiKey: cfg.APIKey,
- baseURL: cfg.BaseURL,
- logger: logger.NewLogger("youtube-client"),
- }
- }
- func (c *Client) GetChannelStats(channelID string) (*ChannelStats, error) {
- url := fmt.Sprintf("%s/channels?part=statistics,snippet&id=%s&key=%s",
- c.baseURL, channelID, c.apiKey)
- resp, err := c.httpClient.Get(url)
- if err != nil {
- return nil, fmt.Errorf("do request: %w", err)
- }
- defer resp.Body.Close()
- if resp.StatusCode != http.StatusOK {
- return nil, fmt.Errorf("unexpected status code: %d", resp.StatusCode)
- }
- var stats ChannelStats
- if err := json.NewDecoder(resp.Body).Decode(&stats); err != nil {
- return nil, fmt.Errorf("decode response: %w", err)
- }
- return &stats, nil
- }
- func (c *Client) GetRecentVideos(channelID string, maxResults int) ([]Video, error) {
- url := fmt.Sprintf("%s/search?part=snippet&channelId=%s&order=date&maxResults=%d&key=%s",
- c.baseURL, channelID, maxResults, c.apiKey)
- resp, err := c.httpClient.Get(url)
- if err != nil {
- return nil, fmt.Errorf("do request: %w", err)
- }
- defer resp.Body.Close()
- if resp.StatusCode != http.StatusOK {
- return nil, fmt.Errorf("unexpected status code: %d", resp.StatusCode)
- }
- var videos []Video
- if err := json.NewDecoder(resp.Body).Decode(&videos); err != nil {
- return nil, fmt.Errorf("decode response: %w", err)
- }
- return videos, nil
- }
- func (c *Client) GetVideoStats(videoID string) (*Video, error) {
- url := fmt.Sprintf("%s/videos?part=statistics,snippet,contentDetails&id=%s&key=%s",
- c.baseURL, videoID, c.apiKey)
- resp, err := c.httpClient.Get(url)
- if err != nil {
- return nil, fmt.Errorf("do request: %w", err)
- }
- defer resp.Body.Close()
- if resp.StatusCode != http.StatusOK {
- return nil, fmt.Errorf("unexpected status code: %d", resp.StatusCode)
- }
- var video Video
- if err := json.NewDecoder(resp.Body).Decode(&video); err != nil {
- return nil, fmt.Errorf("decode response: %w", err)
- }
- return &video, nil
- }
- func (c *Client) GetPlaylists(channelID string) ([]PlaylistStats, error) {
- url := fmt.Sprintf("%s/playlists?part=snippet,contentDetails&channelId=%s&key=%s",
- c.baseURL, channelID, c.apiKey)
- resp, err := c.httpClient.Get(url)
- if err != nil {
- return nil, fmt.Errorf("do request: %w", err)
- }
- defer resp.Body.Close()
- if resp.StatusCode != http.StatusOK {
- return nil, fmt.Errorf("unexpected status code: %d", resp.StatusCode)
- }
- var playlists []PlaylistStats
- if err := json.NewDecoder(resp.Body).Decode(&playlists); err != nil {
- return nil, fmt.Errorf("decode response: %w", err)
- }
- return playlists, nil
- }
- func (c *Client) CalculateEngagement(videos []Video) float64 {
- if len(videos) == 0 {
- return 0
- }
- var totalEngagement int64
- for _, video := range videos {
- // YouTube engagement includes views, likes, and comments
- totalEngagement += video.ViewCount + video.LikeCount + video.CommentCount
- }
- return float64(totalEngagement) / float64(len(videos))
- }
- func (c *Client) GetTrendingTags(videos []Video) map[string]int {
- tags := make(map[string]int)
- for _, video := range videos {
- for _, tag := range video.Tags {
- tags[tag]++
- }
- }
- return tags
- }
|