AtlatestRepositorytube
1
;;; (tube main) - Entry point for tube CLI and MCP server.2
;;;3
;;; Dispatches between MCP server mode (`tube serve`) and CLI mode4
;;; (`tube <command> <args>`). Uses (sigil args) for CLI parsing.6
(define-library (tube main)7
(import (sigil core)8
(sigil string)9
(sigil dict)10
(sigil process)11
(sigil args)12
(sigil mcp server)13
(sigil time)14
(sigil oauth server)15
(youtube oauth)16
(twitch oauth)17
(tube config)18
(tube youtube)19
(tube twitch)20
(tube tools))21
(export main)22
(begin24
(define (run-server config)25
(let ((server (mcp-server name: "tube" version: "0.1.0")))26
(register-tube-tools! server config)27
(mcp-server-run server)))29
;; ============================================================30
;; Shared option builders31
;; ============================================================33
(define (platform-option default)34
(option name: 'platform short: #\p long: "platform"35
value: "PLATFORM"36
description: "Platform: youtube, twitch, or both"37
choices: '("youtube" "twitch" "both")38
default: default))40
;; ============================================================41
;; CLI Command Handlers42
;; ============================================================44
(define (cli-upload config opts args)45
(let ((file (if (pair? args) (car args) #f))46
(title (alist-get 'title opts))47
(description (alist-get 'description opts))48
(privacy (alist-get 'privacy opts)))49
(unless file50
(display "Error: file path is required.\n")51
(exit 1))52
;; Read file data53
(let* ((port (open-binary-input-file file))54
(data (read-bytevector-all port)))55
(close-input-port port)56
(let ((result (tube-upload-video config data title description privacy)))57
(display (string-append58
"Uploaded: " (youtube-video-title result) "\n"59
"Video ID: " (youtube-video-id result) "\n"60
"Privacy: " privacy "\n"))))))62
(define (cli-videos config opts args)63
(let* ((limit (alist-get 'limit opts))64
(videos (tube-list-videos config limit)))65
(if (null? videos)66
(display "No videos found.\n")67
(begin68
(display (string-append69
"Recent videos (" (number->string (length videos)) "):\n\n"))70
(for-each (lambda (v)71
(display (format-video v))72
(display "\n\n"))73
videos)))))75
(define (cli-analytics config opts start-default end-default76
now-timestamp)77
(let* ((days (alist-get 'days opts))78
(platform (alist-get 'platform opts))79
(params #{ platform: platform80
end_date: end-default })81
(params (if days82
(dict-set params days: days)83
params)))84
;; Reuse the tool handler85
(display (tool-analytics config params start-default end-default86
now-timestamp))87
(newline)))89
(define (cli-schedule config opts)90
(let ((params #{ platform: (alist-get 'platform opts) }))91
(display (tool-schedule config params))92
(newline)))94
(define (cli-go-live config opts)95
(let ((params #{ title: (alist-get 'title opts)96
platform: (alist-get 'platform opts) }))97
(display (tool-go-live config params))98
(newline)))100
(define (cli-chat config opts args)101
(let ((message (if (pair? args)102
(string-join args " ")103
#f))104
(platform (alist-get 'platform opts)))105
(unless message106
(display "Error: chat message is required.\n")107
(exit 1))108
(let ((params #{ message: message platform: platform }))109
(display (tool-chat config params))110
(newline))))112
(define (cli-status config opts)113
(let ((params #{ platform: (alist-get 'platform opts) }))114
(display (tool-status config params))115
(newline)))117
;; ============================================================118
;; Auth Command119
;; ============================================================121
(define (cli-auth-youtube config)122
(let ((client-id (tube-config-youtube-client-id config))123
(client-secret (tube-config-youtube-client-secret config)))124
(unless (and client-id client-secret)125
(display "Error: YOUTUBE_CLIENT_ID and YOUTUBE_CLIENT_SECRET must be set.\n")126
(exit 1))127
(let* ((oauth-cfg (youtube-oauth-config client-id client-secret))128
(tokens (oauth-run-authorization-flow oauth-cfg129
(current-second))))130
(youtube-save-tokens tokens)131
(display "YouTube authorization successful. Tokens saved.\n"))))133
(define (cli-auth-twitch config)134
(let ((client-id (tube-config-twitch-client-id config))135
(client-secret (tube-config-twitch-client-secret config)))136
(unless (and client-id client-secret)137
(display "Error: TWITCH_CLIENT_ID and TWITCH_CLIENT_SECRET must be set.\n")138
(exit 1))139
(let* ((oauth-cfg (twitch-oauth-config client-id client-secret))140
(tokens (oauth-run-authorization-flow oauth-cfg141
(current-second))))142
(twitch-save-tokens tokens)143
(display "Twitch authorization successful. Tokens saved.\n"))))145
(define (cli-auth config opts args)146
(let ((platform (if (pair? args) (car args) #f)))147
(cond148
((equal? platform "youtube") (cli-auth-youtube config))149
((equal? platform "twitch") (cli-auth-twitch config))150
(else151
(display "Usage: tube auth <youtube|twitch>\n\n")152
(display "Run the OAuth authorization flow for a platform.\n")153
(display "Requires CLIENT_ID and CLIENT_SECRET env vars.\n")154
(display "Tokens are stored in ~/.config/sigil/<service>/tokens.json\n")))))156
;; ============================================================157
;; CLI Definition158
;; ============================================================160
(define (make-cli config start-default end-default now-ts)161
(let ((default-plat (tube-config-default-platform config)))162
(command163
name: "tube"164
description: "Video platform management tool (YouTube + Twitch)"165
subcommands: (list166
(command167
name: "serve"168
description: "Start MCP server"169
handler: (lambda (opts args) (run-server config)))171
(command172
name: "upload"173
description: "Upload video to YouTube"174
options: (list175
(option name: 'title short: #\t long: "title"176
value: "TITLE" default: "Untitled"177
description: "Video title")178
(option name: 'description short: #\d long: "description"179
value: "DESC" default: ""180
description: "Video description")181
(option name: 'privacy long: "privacy"182
value: "PRIVACY" default: "private"183
description: "Privacy status"184
choices: '("public" "private" "unlisted")))185
handler: (lambda (opts args) (cli-upload config opts args)))187
(command188
name: "videos"189
description: "List recent YouTube videos"190
options: (list191
(option name: 'limit short: #\n long: "limit"192
value: "N" default: 10193
parse: string->number194
description: "Number of videos to return"))195
handler: (lambda (opts args) (cli-videos config opts args)))197
(command198
name: "analytics"199
description: "Analytics summary"200
options: (list201
(option name: 'days long: "days"202
value: "N" default: #f203
parse: string->number204
description: "Number of days to look back")205
(platform-option default-plat))206
handler: (lambda (opts args)207
(cli-analytics config opts start-default208
end-default now-ts)))210
(command211
name: "stream-schedule"212
description: "Show upcoming streams"213
options: (list (platform-option default-plat))214
handler: (lambda (opts args) (cli-schedule config opts)))216
(command217
name: "go-live"218
description: "Start a broadcast"219
options: (list220
(option name: 'title short: #\t long: "title"221
value: "TITLE" default: "Live Stream"222
description: "Stream title")223
(platform-option default-plat))224
handler: (lambda (opts args) (cli-go-live config opts)))226
(command227
name: "chat"228
description: "Send chat message"229
options: (list230
(platform-option "twitch"))231
handler: (lambda (opts args) (cli-chat config opts args)))233
(command234
name: "status"235
description: "Platform overview"236
options: (list (platform-option default-plat))237
handler: (lambda (opts args) (cli-status config opts)))239
(command240
name: "auth"241
description: "Authorize a platform via OAuth"242
handler: (lambda (opts args) (cli-auth config opts args)))))))244
;; ============================================================245
;; Entry Point246
;; ============================================================248
(define (main)249
(let* ((config (load-tube-config))250
(start-default (default-start-date))251
(end-default (default-end-date))252
(now-ts (default-now-timestamp))253
(cli (make-cli config start-default end-default now-ts)))254
(run-command cli (cdr (command-line)))))256
))