OCaml bindings to the Peertube ActivityPub video sharing API

trim

-511
-310
PLAN.md
··· 1 - # PeerTube OCaml API Coverage Plan 2 - 3 - ## Current Implementation 4 - 5 - **Implemented (5 endpoints):** 6 - - `GET /api/v1/video-channels/{channel}/videos` - List channel videos with pagination 7 - - `GET /api/v1/videos/{uuid}` - Get video details 8 - - Thumbnail download (static files) 9 - 10 - **Types:** 11 - - `video` - Basic video metadata 12 - - `video_response` - Paginated response wrapper 13 - 14 - ## Proposed Expansion 15 - 16 - ### Phase 1: Enhanced Video Discovery (Priority: High) 17 - 18 - Add types and functions for browsing and searching videos. 19 - 20 - #### New Types 21 - ```ocaml 22 - type sort_order = 23 - | Newest | Oldest | Views | Likes | Trending | Hot | Random 24 - 25 - type video_filter = { 26 - category_id : int option; 27 - licence_id : int option; 28 - language : string option; 29 - nsfw : bool option; 30 - is_local : bool option; 31 - has_hls : bool option; 32 - has_webtorrent : bool option; 33 - skip_count : bool option; (* skip total count for performance *) 34 - } 35 - 36 - type search_params = { 37 - search : string; 38 - start : int; 39 - count : int; 40 - sort : sort_order; 41 - search_target : [`Local | `Everywhere] option; 42 - duration_min : int option; 43 - duration_max : int option; 44 - published_after : Ptime.t option; 45 - published_before : Ptime.t option; 46 - } 47 - ``` 48 - 49 - #### New Endpoints 50 - | Function | Endpoint | Description | 51 - |----------|----------|-------------| 52 - | `list_videos` | `GET /api/v1/videos` | Browse all videos with filters | 53 - | `search_videos` | `GET /api/v1/search/videos` | Full-text search | 54 - | `search_channels` | `GET /api/v1/search/video-channels` | Search channels | 55 - | `get_categories` | `GET /api/v1/videos/categories` | Video category list | 56 - | `get_languages` | `GET /api/v1/videos/languages` | Available languages | 57 - | `get_licences` | `GET /api/v1/videos/licences` | License types | 58 - 59 - ### Phase 2: Channels & Accounts (Priority: High) 60 - 61 - #### New Types 62 - ```ocaml 63 - type channel = { 64 - id : int; 65 - name : string; 66 - display_name : string; 67 - description : string option; 68 - url : string; 69 - host : string; 70 - followers_count : int; 71 - following_count : int; 72 - created_at : Ptime.t; 73 - banner_path : string option; 74 - avatar_path : string option; 75 - owner_account : account_summary option; 76 - } 77 - 78 - type account_summary = { 79 - id : int; 80 - name : string; 81 - display_name : string; 82 - url : string; 83 - host : string; 84 - } 85 - 86 - type account = { 87 - id : int; 88 - name : string; 89 - display_name : string; 90 - description : string option; 91 - url : string; 92 - host : string; 93 - followers_count : int; 94 - following_count : int; 95 - created_at : Ptime.t; 96 - avatar_path : string option; 97 - } 98 - ``` 99 - 100 - #### New Endpoints 101 - | Function | Endpoint | Description | 102 - |----------|----------|-------------| 103 - | `list_channels` | `GET /api/v1/video-channels` | Browse all channels | 104 - | `get_channel` | `GET /api/v1/video-channels/{handle}` | Channel details | 105 - | `list_accounts` | `GET /api/v1/accounts` | Browse accounts | 106 - | `get_account` | `GET /api/v1/accounts/{handle}` | Account details | 107 - | `get_account_videos` | `GET /api/v1/accounts/{handle}/videos` | Videos by account | 108 - | `get_account_channels` | `GET /api/v1/accounts/{handle}/video-channels` | Account's channels | 109 - 110 - ### Phase 3: Playlists (Priority: Medium) 111 - 112 - #### New Types 113 - ```ocaml 114 - type playlist_privacy = Public | Unlisted | Private 115 - 116 - type playlist = { 117 - id : int; 118 - uuid : string; 119 - display_name : string; 120 - description : string option; 121 - privacy : playlist_privacy; 122 - url : string; 123 - videos_length : int; 124 - thumbnail_path : string option; 125 - created_at : Ptime.t; 126 - updated_at : Ptime.t; 127 - owner_account : account_summary; 128 - video_channel : channel option; 129 - } 130 - 131 - type playlist_element = { 132 - id : int; 133 - position : int; 134 - start_timestamp : int option; 135 - stop_timestamp : int option; 136 - video : video; 137 - } 138 - ``` 139 - 140 - #### New Endpoints 141 - | Function | Endpoint | Description | 142 - |----------|----------|-------------| 143 - | `list_playlists` | `GET /api/v1/video-playlists` | Browse playlists | 144 - | `get_playlist` | `GET /api/v1/video-playlists/{id}` | Playlist details | 145 - | `get_playlist_videos` | `GET /api/v1/video-playlists/{id}/videos` | Videos in playlist | 146 - | `get_account_playlists` | `GET /api/v1/accounts/{handle}/video-playlists` | Account's playlists | 147 - 148 - ### Phase 4: Server Information (Priority: Medium) 149 - 150 - #### New Types 151 - ```ocaml 152 - type server_config = { 153 - instance_name : string; 154 - instance_short_description : string; 155 - instance_description : string; 156 - instance_terms : string; 157 - instance_default_nsfw_policy : string; 158 - signup_allowed : bool; 159 - signup_allowed_for_current_ip : bool; 160 - signup_requires_email_verification : bool; 161 - transcoding_enabled : bool; 162 - contact_form_enabled : bool; 163 - } 164 - 165 - type server_stats = { 166 - total_users : int; 167 - total_daily_active_users : int; 168 - total_weekly_active_users : int; 169 - total_monthly_active_users : int; 170 - total_local_videos : int; 171 - total_local_video_views : int; 172 - total_local_video_comments : int; 173 - total_local_video_files_size : int64; 174 - total_videos : int; 175 - total_video_comments : int; 176 - total_local_video_channels : int; 177 - total_local_playlists : int; 178 - total_instance_followers : int; 179 - total_instance_following : int; 180 - } 181 - ``` 182 - 183 - #### New Endpoints 184 - | Function | Endpoint | Description | 185 - |----------|----------|-------------| 186 - | `get_config` | `GET /api/v1/config` | Server configuration | 187 - | `get_about` | `GET /api/v1/config/about` | Instance about page | 188 - | `get_stats` | `GET /api/v1/server/stats` | Server statistics | 189 - 190 - ### Phase 5: Authentication (Priority: Low for read-only clients) 191 - 192 - #### New Types 193 - ```ocaml 194 - type oauth_client = { 195 - client_id : string; 196 - client_secret : string; 197 - } 198 - 199 - type auth_token = { 200 - access_token : string; 201 - token_type : string; 202 - expires_in : int; 203 - refresh_token : string; 204 - } 205 - 206 - type authenticated_session = { 207 - session : Requests.t; 208 - token : auth_token; 209 - expires_at : Ptime.t; 210 - } 211 - ``` 212 - 213 - #### New Functions 214 - | Function | Endpoint | Description | 215 - |----------|----------|-------------| 216 - | `get_oauth_client` | `GET /api/v1/oauth-clients/local` | Get OAuth credentials | 217 - | `login` | `POST /api/v1/users/token` | Authenticate user | 218 - | `refresh_token` | `POST /api/v1/users/token` | Refresh access token | 219 - | `logout` | `POST /api/v1/users/revoke-token` | Revoke token | 220 - 221 - ### Phase 6: User Interactions (Priority: Low, requires auth) 222 - 223 - #### New Endpoints (require authentication) 224 - | Function | Endpoint | Description | 225 - |----------|----------|-------------| 226 - | `rate_video` | `POST /api/v1/videos/{id}/rate` | Like/dislike video | 227 - | `get_my_rating` | `GET /api/v1/videos/{id}/rating` | Get my rating | 228 - | `get_comments` | `GET /api/v1/videos/{id}/comments` | Video comments | 229 - | `post_comment` | `POST /api/v1/videos/{id}/comment-threads` | Add comment | 230 - | `subscribe` | `POST /api/v1/users/me/subscriptions` | Subscribe to channel | 231 - | `unsubscribe` | `DELETE /api/v1/users/me/subscriptions/{uri}` | Unsubscribe | 232 - | `get_subscriptions` | `GET /api/v1/users/me/subscriptions` | My subscriptions | 233 - | `get_subscription_videos` | `GET /api/v1/users/me/subscriptions/videos` | Subscription feed | 234 - | `get_notifications` | `GET /api/v1/users/me/notifications` | My notifications | 235 - | `get_watch_history` | `GET /api/v1/users/me/history/videos` | Watch history | 236 - 237 - ## Implementation Strategy 238 - 239 - ### Step 1: Extend Video Type 240 - Add missing fields to the existing `video` type that are commonly returned: 241 - - `duration` (int, seconds) 242 - - `views` (int) 243 - - `likes` (int) 244 - - `dislikes` (int) 245 - - `category` (category record) 246 - - `licence` (licence record) 247 - - `language` (language record) 248 - - `privacy` (privacy record) 249 - - `is_local` (bool) 250 - - `channel` (channel_summary) 251 - - `account` (account_summary) 252 - 253 - ### Step 2: Create Common Pagination Module 254 - ```ocaml 255 - module Pagination : sig 256 - type 'a response = { 257 - total : int; 258 - data : 'a list; 259 - } 260 - 261 - val fetch_all : 262 - ?page_size:int -> 263 - ?max_pages:int -> 264 - (start:int -> count:int -> 'a response) -> 265 - 'a list 266 - end 267 - ``` 268 - 269 - ### Step 3: Create Module Structure 270 - ``` 271 - lib/ 272 - peertube.ml - Main entry, re-exports submodules 273 - peertube.mli - Public interface 274 - video.ml - Video types and operations 275 - channel.ml - Channel types and operations 276 - account.ml - Account types and operations 277 - playlist.ml - Playlist types and operations 278 - search.ml - Search functionality 279 - server.ml - Server config/stats 280 - auth.ml - Authentication (optional) 281 - ``` 282 - 283 - ### Step 4: Add CLI Commands 284 - Extend `opeertube` with new subcommands: 285 - - `opeertube search <query>` - Search videos 286 - - `opeertube channels` - List/search channels 287 - - `opeertube channel <name>` - Channel details 288 - - `opeertube playlists` - List playlists 289 - - `opeertube playlist <id>` - Playlist videos 290 - - `opeertube server info` - Server configuration 291 - - `opeertube server stats` - Server statistics 292 - 293 - ## File Changes Summary 294 - 295 - | File | Changes | 296 - |------|---------| 297 - | `lib/dune` | No changes needed | 298 - | `lib/peertube.mli` | Add new types and functions | 299 - | `lib/peertube.ml` | Add implementations | 300 - | `bin/opeertube.ml` | Add new CLI commands | 301 - | `dune-project` | No changes needed | 302 - 303 - ## Testing Plan 304 - 305 - 1. Unit tests for JSON codec round-trips 306 - 2. Integration tests against public PeerTube instances: 307 - - https://framatube.org 308 - - https://peertube.social 309 - - https://video.ploud.fr 310 - 3. CLI smoke tests for each command
···
-201
TODO.md
··· 1 - # PeerTube OCaml Client - Remaining Features 2 - 3 - ## Completed 4 - 5 - - [x] Phase 1: Video Discovery (list, search, categories, languages, licences) 6 - - [x] Phase 2: Channels & Accounts (list, search, get details) 7 - - [x] Phase 3: Playlists (list, search, get details, videos) 8 - - [x] Phase 4: Server Information (config, stats) 9 - - [x] CLI with all read-only operations 10 - 11 - ## Phase 5: Authentication 12 - 13 - The Requests library will have OAuth support, which should simplify this phase. 14 - 15 - ### Types 16 - 17 - ```ocaml 18 - type oauth_client = { 19 - client_id : string; 20 - client_secret : string; 21 - } 22 - 23 - type auth_token = { 24 - access_token : string; 25 - token_type : string; 26 - expires_in : int; 27 - refresh_token : string; 28 - } 29 - ``` 30 - 31 - ### Endpoints 32 - 33 - - [ ] `get_oauth_client` - `GET /api/v1/oauth-clients/local` 34 - - Fetches the instance's OAuth client credentials 35 - - Required before user authentication 36 - 37 - - [ ] `login` - `POST /api/v1/users/token` 38 - - OAuth2 password grant flow 39 - - Parameters: username, password, client_id, client_secret 40 - - Returns access_token and refresh_token 41 - - Use Requests OAuth support for token management 42 - 43 - - [ ] `refresh_token` - `POST /api/v1/users/token` 44 - - OAuth2 refresh token grant 45 - - Automatically refresh expired tokens 46 - - Requests OAuth support should handle this 47 - 48 - - [ ] `logout` - `POST /api/v1/users/revoke-token` 49 - - Revoke the current access token 50 - 51 - ### Implementation Notes 52 - 53 - - Requests OAuth support will handle token storage and automatic refresh 54 - - Consider storing tokens in `~/.config/opeertube/` for CLI persistence 55 - - Add `--login` flag to CLI for interactive authentication 56 - 57 - ## Phase 6: User Interactions (requires auth) 58 - 59 - ### Video Interactions 60 - 61 - - [ ] `rate_video` - `POST /api/v1/videos/{id}/rate` 62 - - Body: `{ "rating": "like" | "dislike" | "none" }` 63 - 64 - - [ ] `get_my_rating` - `GET /api/v1/videos/{id}/rating` 65 - - Returns user's rating for a video 66 - 67 - ### Comments 68 - 69 - - [ ] `get_comments` - `GET /api/v1/videos/{id}/comment-threads` 70 - - Paginated list of top-level comments 71 - - Each comment has nested replies 72 - 73 - - [ ] `get_comment_thread` - `GET /api/v1/videos/{id}/comment-threads/{threadId}` 74 - - Full thread with all replies 75 - 76 - - [ ] `post_comment` - `POST /api/v1/videos/{id}/comment-threads` 77 - - Body: `{ "text": "comment content" }` 78 - 79 - - [ ] `reply_to_comment` - `POST /api/v1/videos/{id}/comments/{commentId}` 80 - - Body: `{ "text": "reply content" }` 81 - 82 - - [ ] `delete_comment` - `DELETE /api/v1/videos/{id}/comments/{commentId}` 83 - 84 - ### Subscriptions 85 - 86 - - [ ] `subscribe` - `POST /api/v1/users/me/subscriptions` 87 - - Body: `{ "uri": "channel@host" }` 88 - 89 - - [ ] `unsubscribe` - `DELETE /api/v1/users/me/subscriptions/{subscriptionHandle}` 90 - 91 - - [ ] `get_subscriptions` - `GET /api/v1/users/me/subscriptions` 92 - - Paginated list of subscribed channels 93 - 94 - - [ ] `get_subscription_videos` - `GET /api/v1/users/me/subscriptions/videos` 95 - - Subscription feed (videos from subscribed channels) 96 - 97 - - [ ] `subscription_exists` - `GET /api/v1/users/me/subscriptions/exist` 98 - - Check if subscribed to specific channels 99 - 100 - ### User Profile 101 - 102 - - [ ] `get_my_info` - `GET /api/v1/users/me` 103 - - Current user's account info 104 - 105 - - [ ] `update_my_info` - `PUT /api/v1/users/me` 106 - - Update display name, description, etc. 107 - 108 - - [ ] `get_my_videos` - `GET /api/v1/users/me/videos` 109 - - Videos uploaded by current user 110 - 111 - ### Watch History & Notifications 112 - 113 - - [ ] `get_watch_history` - `GET /api/v1/users/me/history/videos` 114 - - Paginated watch history 115 - 116 - - [ ] `delete_watch_history` - `POST /api/v1/users/me/history/videos/remove` 117 - - Clear watch history 118 - 119 - - [ ] `get_notifications` - `GET /api/v1/users/me/notifications` 120 - - User notifications 121 - 122 - - [ ] `mark_notifications_read` - `POST /api/v1/users/me/notifications/read` 123 - - Mark notifications as read 124 - 125 - - [ ] `mark_all_notifications_read` - `POST /api/v1/users/me/notifications/read-all` 126 - 127 - ## Phase 7: Content Upload (requires auth) 128 - 129 - ### Video Upload 130 - 131 - - [ ] `upload_video` - `POST /api/v1/videos/upload` 132 - - Multipart form upload 133 - - Required fields: channelId, name, videofile 134 - - Optional: description, tags, privacy, etc. 135 - 136 - - [ ] `upload_video_resumable_init` - `POST /api/v1/videos/upload-resumable` 137 - - Initialize resumable upload for large files 138 - 139 - - [ ] `upload_video_resumable` - `PUT /api/v1/videos/upload-resumable` 140 - - Continue resumable upload 141 - 142 - - [ ] `update_video` - `PUT /api/v1/videos/{id}` 143 - - Update video metadata 144 - 145 - - [ ] `delete_video` - `DELETE /api/v1/videos/{id}` 146 - 147 - ### Playlist Management 148 - 149 - - [ ] `create_playlist` - `POST /api/v1/video-playlists` 150 - - Create a new playlist 151 - 152 - - [ ] `update_playlist` - `PUT /api/v1/video-playlists/{playlistId}` 153 - 154 - - [ ] `delete_playlist` - `DELETE /api/v1/video-playlists/{playlistId}` 155 - 156 - - [ ] `add_video_to_playlist` - `POST /api/v1/video-playlists/{playlistId}/videos` 157 - - Body: `{ "videoId": id }` 158 - 159 - - [ ] `remove_video_from_playlist` - `DELETE /api/v1/video-playlists/{playlistId}/videos/{playlistElementId}` 160 - 161 - - [ ] `reorder_playlist` - `POST /api/v1/video-playlists/{playlistId}/videos/reorder` 162 - 163 - ## CLI Enhancements 164 - 165 - ### Authentication Commands 166 - 167 - - [ ] `opeertube login` - Interactive login, store token 168 - - [ ] `opeertube logout` - Revoke and delete stored token 169 - - [ ] `opeertube whoami` - Show current user info 170 - 171 - ### Authenticated Operations 172 - 173 - - [ ] `opeertube like VIDEO_UUID` - Like a video 174 - - [ ] `opeertube dislike VIDEO_UUID` - Dislike a video 175 - - [ ] `opeertube subscribe CHANNEL` - Subscribe to channel 176 - - [ ] `opeertube unsubscribe CHANNEL` - Unsubscribe 177 - - [ ] `opeertube subscriptions` - List subscriptions 178 - - [ ] `opeertube feed` - Show subscription feed 179 - - [ ] `opeertube history` - Show watch history 180 - - [ ] `opeertube notifications` - Show notifications 181 - 182 - ### Upload Commands 183 - 184 - - [ ] `opeertube upload FILE --channel CHANNEL --name NAME [options]` 185 - - [ ] `opeertube update VIDEO_UUID [options]` 186 - - [ ] `opeertube delete VIDEO_UUID` 187 - 188 - ## Code Quality 189 - 190 - - [ ] Add unit tests for JSON codec round-trips 191 - - [ ] Add integration tests against public instances 192 - - [ ] Add CI configuration (GitHub Actions) 193 - - [ ] Generate opam file for release 194 - - [ ] Add rate limiting / retry logic for API calls 195 - - [ ] Consider caching for frequently accessed data 196 - 197 - ## Documentation 198 - 199 - - [ ] Add examples to mli documentation 200 - - [ ] Create tutorial for common use cases 201 - - [ ] Document error handling patterns
···