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/v28
;;;9
;;; Requires OAuth2 scopes:10
;;; yt-analytics.readonly — for views, watch time, engagement11
;;; yt-analytics-monetary.readonly — for revenue, ad performance13
(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 ;; Configuration23
youtube-analytics-base-url25
;; Core query26
youtube-analytics-query28
;; Common queries29
youtube-views-by-day30
youtube-watch-time31
youtube-subscriber-changes32
youtube-top-videos33
youtube-traffic-sources)35
(begin37
(define youtube-analytics-base-url38
"https://youtubeanalytics.googleapis.com/v2")40
;; ---------------------------------------------------------------41
;; Core analytics query42
;; ---------------------------------------------------------------44
;;; Query the YouTube Analytics API.45
;;;46
;;; Required parameters:47
;;; start-date: "YYYY-MM-DD" format48
;;; end-date: "YYYY-MM-DD" format49
;;; 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 return57
;;; start-index: pagination offset58
;;; currency: ISO currency code for monetary metrics59
;;;60
;;; Returns a dict with:61
;;; column-headers: list of #{ name: "..." data-type: "..." column-type: "..." }62
;;; rows: list of row arrays63
(define (youtube-analytics-query client start-date end-date metrics . rest)64
(let ((opts (if (null? rest) #{} (car rest))))65
(let* ((params (list66
(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-append81
youtube-analytics-base-url "/reports"82
(build-query-string params)))83
(data (youtube-get/json client url)))84
;; Parse the response into a friendlier format85
(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 queries96
;; ---------------------------------------------------------------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-date102
"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-duration108
;;; average-view-percentage).109
(define (youtube-watch-time client start-date end-date)110
(youtube-analytics-query client start-date end-date111
"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-date117
"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-date126
"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-date134
"views,estimatedMinutesWatched"135
#{ dimensions: "insightTrafficSourceType"136
sort: "-views" }))138
))