AtlatestRepositorysigil-youtube
sigil-youtube / tree / src / youtubeplaylist.sgl
1
;;; (youtube playlist) - YouTube playlist management.2
;;;3
;;; CRUD operations for playlists and playlist items.4
;;; Write operations cost 50 quota units each; reads cost 1 unit.5
;;;6
;;; Tip: The uploads playlist for a channel is always7
;;; "UU" + channel_id (without the "UC" prefix).8
;;; Using playlistItems.list on this is much cheaper than search.list.10
(define-library (youtube playlist)11
(import (sigil core)12
(sigil dict)13
(sigil string)14
(sigil struct)15
(sigil json)16
(only (sigil http) build-query-string)17
(sigil http client)18
(youtube))20
(export ;; Records21
youtube-playlist22
youtube-playlist?23
youtube-playlist-id24
youtube-playlist-title25
youtube-playlist-description26
youtube-playlist-item-count27
youtube-playlist-privacy-status29
youtube-playlist-item30
youtube-playlist-item?31
youtube-playlist-item-id32
youtube-playlist-item-video-id33
youtube-playlist-item-title34
youtube-playlist-item-position36
;; Parsing37
parse-playlist38
parse-playlist-item40
;; Playlist operations41
youtube-playlists42
youtube-playlists-by-channel43
youtube-create-playlist44
youtube-update-playlist45
youtube-delete-playlist47
;; Playlist item operations48
youtube-playlist-items49
youtube-add-to-playlist50
youtube-remove-from-playlist51
youtube-reorder-playlist-item)53
(begin55
;; ---------------------------------------------------------------56
;; Records57
;; ---------------------------------------------------------------59
(define-struct youtube-playlist60
(id)61
(title default: "")62
(description default: "")63
(item-count default: 0)64
(privacy-status default: "public"))66
(define-struct youtube-playlist-item67
(id)68
(video-id default: "")69
(title default: "")70
(position default: 0))72
;; ---------------------------------------------------------------73
;; Parsing74
;; ---------------------------------------------------------------76
;;; Parse a playlist resource into a youtube-playlist record.77
(define (parse-playlist data)78
(let ((snippet (dict-ref data snippet: #{}))79
(content (dict-ref data contentDetails: #{}))80
(status (dict-ref data status: #{})))81
(youtube-playlist82
id: (dict-ref data id:)83
title: (dict-ref snippet title: "")84
description: (dict-ref snippet description: "")85
item-count: (dict-ref content itemCount: 0)86
privacy-status: (dict-ref status privacyStatus: "public"))))88
;;; Parse a playlistItem resource into a youtube-playlist-item record.89
(define (parse-playlist-item data)90
(let ((snippet (dict-ref data snippet: #{})))91
(let ((resource-id (dict-ref snippet resourceId: #{})))92
(youtube-playlist-item93
id: (dict-ref data id:)94
video-id: (dict-ref resource-id videoId: "")95
title: (dict-ref snippet title: "")96
position: (dict-ref snippet position: 0)))))98
;; ---------------------------------------------------------------99
;; Playlist operations100
;; ---------------------------------------------------------------102
;; Shared list-playlists implementation.103
(define (list-playlists-internal client filter-params . rest)104
(let ((opts (if (null? rest) #{} (car rest))))105
(let* ((params (maybe-add-api-key client106
(append107
(list (cons "part" "snippet,contentDetails,status"))108
filter-params109
(list (cons "maxResults"110
(number->string111
(dict-ref opts max-results: 25)))112
(cons "pageToken"113
(dict-ref opts page-token: #f))))))114
(url (string-append115
(youtube-api-url client "playlists")116
(build-query-string params)))117
(data (youtube-get/json client url))118
(items (dict-ref data items: #[])))119
(array->list (array-map parse-playlist items)))))121
;;; List playlists for the authenticated user.122
;;; Optional opts dict: max-results (default 25), page-token.123
;;; Quota cost: 1 unit.124
(define (youtube-playlists client . rest)125
(apply list-playlists-internal client126
(list (cons "mine" "true")) rest))128
;;; List playlists for a specific channel.129
;;; Quota cost: 1 unit.130
(define (youtube-playlists-by-channel client channel-id . rest)131
(apply list-playlists-internal client132
(list (cons "channelId" channel-id)) rest))134
;;; Create a new playlist.135
;;; Quota cost: 50 units.136
(define (youtube-create-playlist client title . rest)137
(let ((opts (if (null? rest) #{} (car rest))))138
(let* ((description (dict-ref opts description: ""))139
(privacy (dict-ref opts privacy-status: "public"))140
(body #{ snippet:141
#{ title: title142
description: description }143
status:144
#{ privacyStatus: privacy } })145
(url (string-append146
(youtube-api-url client "playlists")147
(build-query-string148
(list (cons "part" "snippet,status")))))149
(result (youtube-post/json client url body)))150
(parse-playlist result))))152
;;; Update a playlist's metadata.153
;;; All fields (title:, description:, privacy-status:) are required since154
;;; YouTube's PUT replaces the entire resource. Fetch first if you only155
;;; want to change one field.156
;;; Quota cost: 50 units.157
(define (youtube-update-playlist client playlist-id title description158
privacy-status)159
(let* ((body #{ id: playlist-id160
snippet:161
#{ title: title162
description: description }163
status:164
#{ privacyStatus: privacy-status } })165
(url (string-append166
(youtube-api-url client "playlists")167
(build-query-string168
(list (cons "part" "snippet,status")))))169
(result (youtube-put/json client url body)))170
(parse-playlist result)))172
;;; Delete a playlist.173
;;; Quota cost: 50 units.174
(define (youtube-delete-playlist client playlist-id)175
(let ((url (string-append176
(youtube-api-url client "playlists")177
(build-query-string178
(list (cons "id" playlist-id))))))179
(youtube-delete/json client url)))181
;; ---------------------------------------------------------------182
;; Playlist item operations183
;; ---------------------------------------------------------------185
;;; List items in a playlist.186
;;; Returns a list of youtube-playlist-item records.187
;;; Optional opts dict:188
;;; max-results: number (default: 50, max: 50)189
;;; page-token: pagination token190
;;;191
;;; Quota cost: 1 unit per page.192
(define (youtube-playlist-items client playlist-id . rest)193
(let ((opts (if (null? rest) #{} (car rest))))194
(let* ((params (maybe-add-api-key client195
(list (cons "part" "snippet,contentDetails")196
(cons "playlistId" playlist-id)197
(cons "maxResults"198
(number->string199
(dict-ref opts max-results: 50)))200
(cons "pageToken"201
(dict-ref opts page-token: #f)))))202
(url (string-append203
(youtube-api-url client "playlistItems")204
(build-query-string params)))205
(data (youtube-get/json client url))206
(items (dict-ref data items: #[])))207
(array->list (array-map parse-playlist-item items)))))209
;;; Add a video to a playlist.210
;;; Optional position parameter specifies the 0-based index.211
;;; Quota cost: 50 units.212
(define (youtube-add-to-playlist client playlist-id video-id . rest)213
(let ((position (if (null? rest) #f (car rest))))214
(let* ((snippet #{ playlistId: playlist-id215
resourceId:216
#{ kind: "youtube#video"217
videoId: video-id } })218
(snippet (if position219
(dict-set snippet position: position)220
snippet))221
(body #{ snippet: snippet })222
(url (string-append223
(youtube-api-url client "playlistItems")224
(build-query-string225
(list (cons "part" "snippet")))))226
(result (youtube-post/json client url body)))227
(parse-playlist-item result))))229
;;; Remove an item from a playlist by playlist item ID.230
;;; Note: this requires the playlist *item* ID, not the video ID.231
;;; Get item IDs from youtube-playlist-items.232
;;; Quota cost: 50 units.233
(define (youtube-remove-from-playlist client item-id)234
(let ((url (string-append235
(youtube-api-url client "playlistItems")236
(build-query-string237
(list (cons "id" item-id))))))238
(youtube-delete/json client url)))240
;;; Reorder a playlist item by updating its position.241
;;; item-id: the playlist item ID242
;;; playlist-id: the playlist this item belongs to243
;;; video-id: the video ID of this item244
;;; new-position: the new 0-based position245
;;; Quota cost: 50 units.246
(define (youtube-reorder-playlist-item client item-id playlist-id247
video-id new-position)248
(let* ((body #{ id: item-id249
snippet:250
#{ playlistId: playlist-id251
resourceId:252
#{ kind: "youtube#video"253
videoId: video-id }254
position: new-position } })255
(url (string-append256
(youtube-api-url client "playlistItems")257
(build-query-string258
(list (cons "part" "snippet")))))259
(result (youtube-put/json client url body)))260
(parse-playlist-item result)))262
))