AtlatestRepositorysigil-youtube

sigil-youtube / tree / src / youtubeanalytics.sgl

1;;; (youtube analytics) - YouTube Analytics API v2 client.
2;;;
3;;; Query YouTube Analytics for video performance metrics, audience data,
4;;; and revenue information.
5;;;
6;;; Uses a separate base URL from the Data API:
7;;; https://youtubeanalytics.googleapis.com/v2
8;;;
9;;; Requires OAuth2 scopes:
10;;; yt-analytics.readonly — for views, watch time, engagement
11;;; yt-analytics-monetary.readonly — for revenue, ad performance
13(define-library (youtube analytics)
14 (import (sigil core)
15 (sigil dict)
16 (sigil string)
17 (sigil json)
18 (only (sigil http) build-query-string)
19 (sigil http client)
20 (youtube))
22 (export ;; Configuration
23 youtube-analytics-base-url
25 ;; Core query
26 youtube-analytics-query
28 ;; Common queries
29 youtube-views-by-day
30 youtube-watch-time
31 youtube-subscriber-changes
32 youtube-top-videos
33 youtube-traffic-sources)
35 (begin
37 (define youtube-analytics-base-url
38 "https://youtubeanalytics.googleapis.com/v2")
40 ;; ---------------------------------------------------------------
41 ;; Core analytics query
42 ;; ---------------------------------------------------------------
44 ;;; Query the YouTube Analytics API.
45 ;;;
46 ;;; Required parameters:
47 ;;; start-date: "YYYY-MM-DD" format
48 ;;; end-date: "YYYY-MM-DD" format
49 ;;; metrics: comma-separated string (e.g., "views,estimatedMinutesWatched")
50 ;;;
51 ;;; Optional parameters via opts dict:
52 ;;; ids: channel filter (default: "channel==MINE")
53 ;;; dimensions: comma-separated string (e.g., "day", "video", "country")
54 ;;; filters: filter expression (e.g., "video==VIDEO_ID")
55 ;;; sort: sort order (e.g., "-views" for descending)
56 ;;; max-results: number of results to return
57 ;;; start-index: pagination offset
58 ;;; currency: ISO currency code for monetary metrics
59 ;;;
60 ;;; Returns a dict with:
61 ;;; column-headers: list of #{ name: "..." data-type: "..." column-type: "..." }
62 ;;; rows: list of row arrays
63 (define (youtube-analytics-query client start-date end-date metrics . rest)
64 (let ((opts (if (null? rest) #{} (car rest))))
65 (let* ((params (list
66 (cons "ids" (dict-ref opts ids: "channel==MINE"))
67 (cons "startDate" start-date)
68 (cons "endDate" end-date)
69 (cons "metrics" metrics)
70 (cons "dimensions" (dict-ref opts dimensions: #f))
71 (cons "filters" (dict-ref opts filters: #f))
72 (cons "sort" (dict-ref opts sort: #f))
73 (cons "maxResults"
74 (let ((mr (dict-ref opts max-results: #f)))
75 (if mr (number->string mr) #f)))
76 (cons "startIndex"
77 (let ((si (dict-ref opts start-index: #f)))
78 (if si (number->string si) #f)))
79 (cons "currency" (dict-ref opts currency: #f))))
80 (url (string-append
81 youtube-analytics-base-url "/reports"
82 (build-query-string params)))
83 (data (youtube-get/json client url)))
84 ;; Parse the response into a friendlier format
85 (let ((headers (dict-ref data columnHeaders: #[]))
86 (rows (dict-ref data rows: #[])))
87 #{ column-headers: (if (array? headers)
88 (array->list headers)
89 '())
90 rows: (if (array? rows)
91 (array->list (array-map array->list rows))
92 '()) }))))
94 ;; ---------------------------------------------------------------
95 ;; Common analytics queries
96 ;; ---------------------------------------------------------------
98 ;;; Get daily view counts for a date range.
99 ;;; Returns rows of (day views estimated-minutes-watched average-view-duration).
100 (define (youtube-views-by-day client start-date end-date)
101 (youtube-analytics-query client start-date end-date
102 "views,estimatedMinutesWatched,averageViewDuration"
103 #{ dimensions: "day"
104 sort: "day" }))
106 ;;; Get total watch time metrics for a date range.
107 ;;; Returns rows of (views estimated-minutes-watched average-view-duration
108 ;;; average-view-percentage).
109 (define (youtube-watch-time client start-date end-date)
110 (youtube-analytics-query client start-date end-date
111 "views,estimatedMinutesWatched,averageViewDuration,averageViewPercentage"))
113 ;;; Get daily subscriber changes for a date range.
114 ;;; Returns rows of (day subscribers-gained subscribers-lost).
115 (define (youtube-subscriber-changes client start-date end-date)
116 (youtube-analytics-query client start-date end-date
117 "subscribersGained,subscribersLost"
118 #{ dimensions: "day"
119 sort: "day" }))
121 ;;; Get top videos by views for a date range.
122 ;;; max-results defaults to 10.
123 (define (youtube-top-videos client start-date end-date . rest)
124 (let ((max-results (if (null? rest) 10 (car rest))))
125 (youtube-analytics-query client start-date end-date
126 "views,estimatedMinutesWatched,likes,comments"
127 #{ dimensions: "video"
128 sort: "-views"
129 max-results: max-results })))
131 ;;; Get traffic source breakdown for a date range.
132 (define (youtube-traffic-sources client start-date end-date)
133 (youtube-analytics-query client start-date end-date
134 "views,estimatedMinutesWatched"
135 #{ dimensions: "insightTrafficSourceType"
136 sort: "-views" }))
138 ))