-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathservice.go
110 lines (95 loc) · 3.21 KB
/
service.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
package scraper
import (
"context"
"fmt"
"github.com/google/uuid"
"github.com/lazywitt/youtubetrending/db"
"github.com/lazywitt/youtubetrending/db/models"
"github.com/lazywitt/youtubetrending/types"
"google.golang.org/api/option"
"google.golang.org/api/youtube/v3"
"log"
"strings"
"time"
)
type YoutubeSeed struct { //
lastSearchTime time.Time
searchResource []string
searchKey string
orderBy string
maxResultPerCall int
apiKey []string
dbClient *db.Handler
}
type YoutubeApiService interface {
// InitialiseScraper orchestrates periodic call to scrape YouTube data after a fixed interval
InitialiseScraper(ctx context.Context) error
// MakeApiCall executes the api call to youtubeV3 api
MakeApiCall(ctx context.Context, service *youtube.SearchService) (*youtube.SearchListResponse, error)
// PersistVideoData to save the video meta data in postgres
PersistVideoData(ctx context.Context, searchRes *youtube.SearchListResponse) error
}
func GetYoutubeSeed(SearchResource []string, SearchKey string, OrderBy string, MaxResultPerCall int, ApiKey []string, LastSearchTime time.Time,
dbClient *db.Handler) *YoutubeSeed {
return &YoutubeSeed{
searchResource: SearchResource,
searchKey: SearchKey,
orderBy: OrderBy,
maxResultPerCall: MaxResultPerCall,
apiKey: ApiKey,
lastSearchTime: LastSearchTime,
dbClient: dbClient,
}
}
func (s *YoutubeSeed) MakeApiCall(ctx context.Context,
youtubeSearchService *youtube.SearchService) (*youtube.SearchListResponse, error) {
res := youtubeSearchService.List(s.searchResource)
res.Q(s.searchKey)
res.Order(s.orderBy)
res.MaxResults(int64(s.maxResultPerCall))
res.PublishedAfter(s.lastSearchTime.Format(time.RFC3339))
res.Do()
searchListRes, err := res.Do()
if err != nil {
log.Fatal("error fetching search results", err)
}
if searchListRes == nil {
log.Fatal("nil pointer returned from search api")
}
return searchListRes, err
}
func (s *YoutubeSeed) InitialiseScraper(ctx context.Context) error {
youtubeService, err := youtube.NewService(ctx, option.WithAPIKey(strings.Join(s.apiKey[:], ",")))
if err != nil {
log.Fatal("error creating youtube service object", err)
}
youtubeSearchService := youtube.NewSearchService(youtubeService)
for {
searchRes, err := s.MakeApiCall(ctx, youtubeSearchService)
if err != nil {
fmt.Printf("error when executing api call %v", err)
continue
}
err = s.PersistVideoData(ctx, searchRes)
s.lastSearchTime = time.Now()
time.Sleep(time.Second * 10) // wait for 10 seconds before triggering next api call
}
return nil
}
func (s *YoutubeSeed) PersistVideoData(ctx context.Context, searchRes *youtube.SearchListResponse) error {
var scrapedVideos []*models.Videos
for _, it := range searchRes.Items {
scrapedVideos = append(scrapedVideos, &models.Videos{
Id: uuid.New(),
YoutubeId: types.NewNullString(it.Id.VideoId),
Title: it.Snippet.Title,
Description: it.Snippet.Description,
})
}
err := s.dbClient.Create(ctx, scrapedVideos)
if err != nil {
return fmt.Errorf(fmt.Sprintf("error creating entry of scraper data: %v", err))
}
log.Println("successfully persisted the set of scraped entries")
return nil
}