OCaml bindings to the Peertube ActivityPub video sharing API

init

+1350 -893
+38
.github/workflows/build.yml
··· 1 + name: Build 2 + 3 + on: 4 + push: 5 + branches: [ main, master ] 6 + pull_request: 7 + branches: [ main, master ] 8 + 9 + jobs: 10 + build: 11 + strategy: 12 + fail-fast: false 13 + matrix: 14 + os: 15 + - ubuntu-latest 16 + - macos-latest 17 + ocaml-compiler: 18 + - "5.2.0" 19 + 20 + runs-on: ${{ matrix.os }} 21 + 22 + steps: 23 + - name: Checkout code 24 + uses: actions/checkout@v4 25 + 26 + - name: Set up OCaml 27 + uses: ocaml/setup-ocaml@v3 28 + with: 29 + ocaml-compiler: ${{ matrix.ocaml-compiler }} 30 + 31 + - name: Install dependencies 32 + run: opam install . --deps-only --with-doc 33 + 34 + - name: Build 35 + run: opam exec -- dune build 36 + 37 + - name: Build documentation 38 + run: opam exec -- dune build @doc
+38 -2
.gitignore
··· 1 - _build 2 - peertube-src 1 + # OCaml build artifacts 2 + _build/ 3 + *.install 4 + *.byte 5 + *.native 6 + *.cmo 7 + *.cmi 8 + *.cma 9 + *.cmx 10 + *.cmxa 11 + *.cmxs 12 + *.o 13 + *.a 14 + 15 + # Dune 16 + _opam/ 17 + 18 + # Reference sources 19 + peertube-src/ 20 + 21 + # Editor files 22 + *.swp 23 + *.swo 24 + *~ 25 + .vscode/ 26 + .idea/ 27 + *.sublime-* 28 + 29 + # OS files 30 + .DS_Store 31 + Thumbs.db 32 + 33 + # Merlin (generated by dune) 34 + .merlin 35 + 36 + # Coverage 37 + _coverage/ 38 + *.coverage
+1
.ocamlformat
··· 1 + version = 0.28.1
+15
LICENSE.md
··· 1 + # ISC License 2 + 3 + Copyright (c) 2025 Anil Madhavapeddy <anil@recoil.org> 4 + 5 + Permission to use, copy, modify, and/or distribute this software for any 6 + purpose with or without fee is hereby granted, provided that the above 7 + copyright notice and this permission notice appear in all copies. 8 + 9 + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH 10 + REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY 11 + AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, 12 + INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM 13 + LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR 14 + OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR 15 + PERFORMANCE OF THIS SOFTWARE.
+112
README.md
··· 1 + # ocaml-peertube 2 + 3 + An OCaml client library for the PeerTube video platform API, built on Eio for effect-based I/O. 4 + 5 + ## Features 6 + 7 + - Browse, search, and fetch videos from any PeerTube instance 8 + - Access channels, accounts, and playlists 9 + - Retrieve server configuration and statistics 10 + - Automatic pagination support 11 + - Full JSON serialization with Jsont 12 + - Command-line client (`opeertube`) included 13 + 14 + ## Installation 15 + 16 + ### From source 17 + 18 + ```bash 19 + git clone https://git.recoil.org/anil.recoil.org/ocaml-peertube.git 20 + cd ocaml-peertube 21 + opam install . --deps-only 22 + dune build 23 + ``` 24 + 25 + ## Usage 26 + 27 + ### Library 28 + 29 + ```ocaml 30 + open Peertube 31 + 32 + let () = 33 + Eio_main.run @@ fun env -> 34 + Eio.Switch.run @@ fun sw -> 35 + let session = Requests.create ~sw env in 36 + let client = Client.create ~session ~base_url:"https://video.example.com" in 37 + let videos = Client.list_videos client ~count:10 () in 38 + List.iter (fun v -> 39 + Printf.printf "%s: %s\n" (Video.uuid v) (Video.name v) 40 + ) (Paginated.data videos) 41 + ``` 42 + 43 + ### Command-line client 44 + 45 + ```bash 46 + # Browse videos 47 + opeertube browse -u https://video.example.com 48 + 49 + # Search videos 50 + opeertube search -u https://video.example.com "search query" 51 + 52 + # Get video details 53 + opeertube get -u https://video.example.com <uuid> 54 + 55 + # List channels 56 + opeertube channels list -u https://video.example.com 57 + 58 + # Get server info 59 + opeertube server info -u https://video.example.com 60 + 61 + # JSON output 62 + opeertube browse -u https://video.example.com --json 63 + ``` 64 + 65 + ## API Coverage 66 + 67 + ### Videos 68 + - `Client.list_videos` - Browse videos with filtering and sorting 69 + - `Client.search_videos` - Full-text search with date/duration filters 70 + - `Client.fetch_video_details` - Get details for a specific video 71 + - `Client.fetch_channel_videos` - List videos from a channel 72 + - `Client.fetch_all_channel_videos` - Auto-paginate all channel videos 73 + 74 + ### Channels & Accounts 75 + - `Client.list_channels` / `Client.search_channels` / `Client.get_channel` 76 + - `Client.list_accounts` / `Client.get_account` 77 + - `Client.get_account_videos` / `Client.get_account_channels` 78 + 79 + ### Playlists 80 + - `Client.list_playlists` / `Client.search_playlists` / `Client.get_playlist` 81 + - `Client.get_playlist_videos` / `Client.get_account_playlists` 82 + 83 + ### Server 84 + - `Client.get_config` - Server configuration 85 + - `Client.get_stats` - Server statistics 86 + 87 + ## Documentation 88 + 89 + Build locally with: 90 + 91 + ```bash 92 + dune build @doc 93 + open _build/default/_doc/_html/index.html 94 + ``` 95 + 96 + ## Development 97 + 98 + ### Building 99 + 100 + ```bash 101 + dune build 102 + ``` 103 + 104 + ### Formatting 105 + 106 + ```bash 107 + dune fmt 108 + ``` 109 + 110 + ## License 111 + 112 + ISC License. See [LICENSE.md](LICENSE.md) for details.
+9 -1
bin/dune
··· 2 2 (name opeertube) 3 3 (public_name opeertube) 4 4 (package peertube) 5 - (libraries peertube eio_main cmdliner logs logs.cli logs.fmt fmt.tty fmt.cli)) 5 + (libraries 6 + peertube 7 + eio_main 8 + cmdliner 9 + logs 10 + logs.cli 11 + logs.fmt 12 + fmt.tty 13 + fmt.cli))
+133 -131
bin/opeertube.ml
··· 11 11 Term.(const setup_log $ Fmt_cli.style_renderer () $ Logs_cli.level ()) 12 12 13 13 let base_url_arg = 14 - let doc = "Base URL of the PeerTube instance (e.g., https://video.example.com)" in 14 + let doc = 15 + "Base URL of the PeerTube instance (e.g., https://video.example.com)" 16 + in 15 17 Arg.(required & opt (some string) None & info [ "u"; "url" ] ~docv:"URL" ~doc) 16 18 17 19 let channel_arg = ··· 64 66 Fmt.epr "Error: %s (HTTP %d)@." msg status; 65 67 exit 1 66 68 69 + let print_json codec value = 70 + match Jsont_bytesrw.encode_string codec value with 71 + | Ok s -> print_endline s 72 + | Error e -> Fmt.epr "JSON encode error: %s@." e 73 + 67 74 (* Video printing *) 68 75 69 76 let print_video ?(json = false) (v : Peertube.Video.t) = 70 - if json then 71 - let json_str = Jsont_bytesrw.encode_string Peertube.Video.jsont v in 72 - match json_str with 73 - | Ok s -> print_endline s 74 - | Error e -> Fmt.epr "JSON encode error: %s@." e 77 + if json then print_json Peertube.Video.jsont v 75 78 else 76 - Fmt.pr "@[<v>%s@, UUID: %s@, ID: %d@, URL: %s@, Duration: %ds@, Views: %d@, Published: %a@, Tags: [%a]@]@." 79 + Fmt.pr 80 + "@[<v>%s@,\ 81 + \ UUID: %s@,\ 82 + \ ID: %d@,\ 83 + \ URL: %s@,\ 84 + \ Duration: %ds@,\ 85 + \ Views: %d@,\ 86 + \ Published: %a@,\ 87 + \ Tags: [%a]@]@." 77 88 (Peertube.Video.name v) (Peertube.Video.uuid v) (Peertube.Video.id v) 78 - (Peertube.Video.url v) (Peertube.Video.duration v) 79 - (Peertube.Video.views v) 80 - (Ptime.pp_rfc3339 ()) (Peertube.Video.published_at v) 81 - Fmt.(list ~sep:(any ", ") string) (Peertube.Video.tags v) 89 + (Peertube.Video.url v) 90 + (Peertube.Video.duration v) 91 + (Peertube.Video.views v) (Ptime.pp_rfc3339 ()) 92 + (Peertube.Video.published_at v) 93 + Fmt.(list ~sep:(any ", ") string) 94 + (Peertube.Video.tags v) 82 95 83 96 let print_videos ?(json = false) videos = 84 - if json then begin 85 - let codec = Jsont.(list Peertube.Video.jsont) in 86 - let json_str = Jsont_bytesrw.encode_string codec videos in 87 - match json_str with 88 - | Ok s -> print_endline s 89 - | Error e -> Fmt.epr "JSON encode error: %s@." e 90 - end 91 - else 92 - List.iter (print_video ~json:false) videos 97 + if json then print_json Jsont.(list Peertube.Video.jsont) videos 98 + else List.iter (print_video ~json:false) videos 93 99 94 100 (* Channel printing *) 95 101 ··· 101 107 (Peertube.Channel_summary.url c) 102 108 103 109 let print_channel ?(json = false) (c : Peertube.Channel.t) = 104 - if json then 105 - let json_str = Jsont_bytesrw.encode_string Peertube.Channel.jsont c in 106 - match json_str with 107 - | Ok s -> print_endline s 108 - | Error e -> Fmt.epr "JSON encode error: %s@." e 110 + if json then print_json Peertube.Channel.jsont c 109 111 else begin 110 112 let summary = Peertube.Channel.summary c in 111 - Fmt.pr "@[<v>%s (@%s)@, ID: %d@, URL: %s@, Followers: %d@, Created: %a@]@." 113 + Fmt.pr 114 + "@[<v>%s (@%s)@, ID: %d@, URL: %s@, Followers: %d@, Created: %a@]@." 112 115 (Peertube.Channel_summary.display_name summary) 113 116 (Peertube.Channel_summary.name summary) 114 117 (Peertube.Channel_summary.id summary) 115 118 (Peertube.Channel_summary.url summary) 116 119 (Peertube.Channel.followers_count c) 117 - (Ptime.pp_rfc3339 ()) (Peertube.Channel.created_at c); 118 - Option.iter (fun d -> Fmt.pr " Description: %s@." d) 120 + (Ptime.pp_rfc3339 ()) 121 + (Peertube.Channel.created_at c); 122 + Option.iter 123 + (fun d -> Fmt.pr " Description: %s@." d) 119 124 (Peertube.Channel.description c) 120 125 end 121 126 122 127 let print_channels ?(json = false) channels = 123 - if json then begin 124 - let codec = Jsont.(list Peertube.Channel.jsont) in 125 - let json_str = Jsont_bytesrw.encode_string codec channels in 126 - match json_str with 127 - | Ok s -> print_endline s 128 - | Error e -> Fmt.epr "JSON encode error: %s@." e 129 - end 128 + if json then print_json Jsont.(list Peertube.Channel.jsont) channels 130 129 else 131 - List.iter (fun c -> print_channel_summary (Peertube.Channel.summary c)) channels 130 + List.iter 131 + (fun c -> print_channel_summary (Peertube.Channel.summary c)) 132 + channels 132 133 133 134 (* Account printing *) 134 135 ··· 140 141 (Peertube.Account_summary.url a) 141 142 142 143 let print_account ?(json = false) (a : Peertube.Account.t) = 143 - if json then 144 - let json_str = Jsont_bytesrw.encode_string Peertube.Account.jsont a in 145 - match json_str with 146 - | Ok s -> print_endline s 147 - | Error e -> Fmt.epr "JSON encode error: %s@." e 144 + if json then print_json Peertube.Account.jsont a 148 145 else begin 149 146 let summary = Peertube.Account.summary a in 150 - Fmt.pr "@[<v>%s (@%s)@, ID: %d@, URL: %s@, Followers: %d@, Created: %a@]@." 147 + Fmt.pr 148 + "@[<v>%s (@%s)@, ID: %d@, URL: %s@, Followers: %d@, Created: %a@]@." 151 149 (Peertube.Account_summary.display_name summary) 152 150 (Peertube.Account_summary.name summary) 153 151 (Peertube.Account_summary.id summary) 154 152 (Peertube.Account_summary.url summary) 155 153 (Peertube.Account.followers_count a) 156 - (Ptime.pp_rfc3339 ()) (Peertube.Account.created_at a); 157 - Option.iter (fun d -> Fmt.pr " Description: %s@." d) 154 + (Ptime.pp_rfc3339 ()) 155 + (Peertube.Account.created_at a); 156 + Option.iter 157 + (fun d -> Fmt.pr " Description: %s@." d) 158 158 (Peertube.Account.description a) 159 159 end 160 160 161 161 let print_accounts ?(json = false) accounts = 162 - if json then begin 163 - let codec = Jsont.(list Peertube.Account.jsont) in 164 - let json_str = Jsont_bytesrw.encode_string codec accounts in 165 - match json_str with 166 - | Ok s -> print_endline s 167 - | Error e -> Fmt.epr "JSON encode error: %s@." e 168 - end 162 + if json then print_json Jsont.(list Peertube.Account.jsont) accounts 169 163 else 170 - List.iter (fun a -> print_account_summary (Peertube.Account.summary a)) accounts 164 + List.iter 165 + (fun a -> print_account_summary (Peertube.Account.summary a)) 166 + accounts 171 167 172 168 (* Playlist printing *) 173 169 174 170 let print_playlist ?(json = false) (p : Peertube.Playlist.t) = 175 - if json then 176 - let json_str = Jsont_bytesrw.encode_string Peertube.Playlist.jsont p in 177 - match json_str with 178 - | Ok s -> print_endline s 179 - | Error e -> Fmt.epr "JSON encode error: %s@." e 171 + if json then print_json Peertube.Playlist.jsont p 180 172 else 181 - Fmt.pr "@[<v>%s@, UUID: %s@, ID: %d@, Videos: %d@, URL: %s@, Created: %a@]@." 173 + Fmt.pr 174 + "@[<v>%s@,\ 175 + \ UUID: %s@,\ 176 + \ ID: %d@,\ 177 + \ Videos: %d@,\ 178 + \ URL: %s@,\ 179 + \ Created: %a@]@." 182 180 (Peertube.Playlist.display_name p) 183 - (Peertube.Playlist.uuid p) 184 - (Peertube.Playlist.id p) 181 + (Peertube.Playlist.uuid p) (Peertube.Playlist.id p) 185 182 (Peertube.Playlist.videos_length p) 186 - (Peertube.Playlist.url p) 187 - (Ptime.pp_rfc3339 ()) (Peertube.Playlist.created_at p) 183 + (Peertube.Playlist.url p) (Ptime.pp_rfc3339 ()) 184 + (Peertube.Playlist.created_at p) 188 185 189 186 let print_playlists ?(json = false) playlists = 190 - if json then begin 191 - let codec = Jsont.(list Peertube.Playlist.jsont) in 192 - let json_str = Jsont_bytesrw.encode_string codec playlists in 193 - match json_str with 194 - | Ok s -> print_endline s 195 - | Error e -> Fmt.epr "JSON encode error: %s@." e 196 - end 197 - else 198 - List.iter (print_playlist ~json:false) playlists 187 + if json then print_json Jsont.(list Peertube.Playlist.jsont) playlists 188 + else List.iter (print_playlist ~json:false) playlists 199 189 200 190 (* Video commands *) 201 191 ··· 207 197 let client = Peertube.Client.create ~session ~base_url in 208 198 if all then begin 209 199 let videos = 210 - Peertube.Client.fetch_all_channel_videos client ?max_pages ~page_size:count 211 - ~channel () 200 + Peertube.Client.fetch_all_channel_videos client ?max_pages 201 + ~page_size:count ~channel () 212 202 in 213 203 Fmt.pr "Found %d videos@.@." (List.length videos); 214 204 print_videos ~json videos ··· 219 209 in 220 210 Fmt.pr "Showing %d of %d videos (offset %d)@.@." 221 211 (List.length (Peertube.Paginated.data response)) 222 - (Peertube.Paginated.total response) start; 212 + (Peertube.Paginated.total response) 213 + start; 223 214 print_videos ~json (Peertube.Paginated.data response) 224 215 end 225 216 ··· 240 231 let response = Peertube.Client.list_videos client ~count ~start () in 241 232 Fmt.pr "Showing %d of %d videos (offset %d)@.@." 242 233 (List.length (Peertube.Paginated.data response)) 243 - (Peertube.Paginated.total response) start; 234 + (Peertube.Paginated.total response) 235 + start; 244 236 print_videos ~json (Peertube.Paginated.data response) 245 237 246 238 let browse_videos_cmd = ··· 248 240 let info = Cmd.info "browse" ~doc in 249 241 Cmd.v info 250 242 Term.( 251 - const browse_videos $ setup_log_term $ base_url_arg $ count_arg $ start_arg 252 - $ json_flag) 243 + const browse_videos $ setup_log_term $ base_url_arg $ count_arg 244 + $ start_arg $ json_flag) 253 245 254 246 let search_videos () base_url query count start json = 255 247 handle_api_error @@ fun () -> ··· 257 249 Eio.Switch.run @@ fun sw -> 258 250 let session = Requests.create ~sw env in 259 251 let client = Peertube.Client.create ~session ~base_url in 260 - let response = 261 - Peertube.Client.search_videos client ~query ~count ~start () 262 - in 252 + let response = Peertube.Client.search_videos client ~query ~count ~start () in 263 253 Fmt.pr "Found %d results for '%s' (showing %d, offset %d)@.@." 264 - (Peertube.Paginated.total response) query 265 - (List.length (Peertube.Paginated.data response)) start; 254 + (Peertube.Paginated.total response) 255 + query 256 + (List.length (Peertube.Paginated.data response)) 257 + start; 266 258 print_videos ~json (Peertube.Paginated.data response) 267 259 268 260 let search_videos_cmd = ··· 270 262 let info = Cmd.info "search" ~doc in 271 263 Cmd.v info 272 264 Term.( 273 - const search_videos $ setup_log_term $ base_url_arg $ query_arg $ count_arg 274 - $ start_arg $ json_flag) 265 + const search_videos $ setup_log_term $ base_url_arg $ query_arg 266 + $ count_arg $ start_arg $ json_flag) 275 267 276 268 let get_video () base_url uuid json = 277 269 handle_api_error @@ fun () -> ··· 286 278 let doc = "Get details for a specific video by UUID" in 287 279 let info = Cmd.info "get" ~doc in 288 280 Cmd.v info 289 - Term.(const get_video $ setup_log_term $ base_url_arg $ uuid_arg $ json_flag) 281 + Term.( 282 + const get_video $ setup_log_term $ base_url_arg $ uuid_arg $ json_flag) 290 283 291 284 let download_thumbnail () base_url uuid output = 292 285 handle_api_error @@ fun () -> ··· 319 312 Eio.Switch.run @@ fun sw -> 320 313 let session = Requests.create ~sw env in 321 314 let client = Peertube.Client.create ~session ~base_url in 322 - let response = 323 - Peertube.Client.list_channels client ~count ~start () 324 - in 315 + let response = Peertube.Client.list_channels client ~count ~start () in 325 316 Fmt.pr "Showing %d of %d channels (offset %d)@.@." 326 317 (List.length (Peertube.Paginated.data response)) 327 - (Peertube.Paginated.total response) start; 318 + (Peertube.Paginated.total response) 319 + start; 328 320 print_channels ~json (Peertube.Paginated.data response) 329 321 330 322 let list_channels_cmd = ··· 332 324 let info = Cmd.info "list" ~doc in 333 325 Cmd.v info 334 326 Term.( 335 - const list_channels $ setup_log_term $ base_url_arg $ count_arg $ start_arg 336 - $ json_flag) 327 + const list_channels $ setup_log_term $ base_url_arg $ count_arg 328 + $ start_arg $ json_flag) 337 329 338 330 let search_channels () base_url query count start json = 339 331 handle_api_error @@ fun () -> ··· 345 337 Peertube.Client.search_channels client ~query ~count ~start () 346 338 in 347 339 Fmt.pr "Found %d channels for '%s' (showing %d, offset %d)@.@." 348 - (Peertube.Paginated.total response) query 349 - (List.length (Peertube.Paginated.data response)) start; 340 + (Peertube.Paginated.total response) 341 + query 342 + (List.length (Peertube.Paginated.data response)) 343 + start; 350 344 print_channels ~json (Peertube.Paginated.data response) 351 345 352 346 let search_channels_cmd = ··· 386 380 Eio.Switch.run @@ fun sw -> 387 381 let session = Requests.create ~sw env in 388 382 let client = Peertube.Client.create ~session ~base_url in 389 - let response = 390 - Peertube.Client.list_accounts client ~count ~start () 391 - in 383 + let response = Peertube.Client.list_accounts client ~count ~start () in 392 384 Fmt.pr "Showing %d of %d accounts (offset %d)@.@." 393 385 (List.length (Peertube.Paginated.data response)) 394 - (Peertube.Paginated.total response) start; 386 + (Peertube.Paginated.total response) 387 + start; 395 388 print_accounts ~json (Peertube.Paginated.data response) 396 389 397 390 let list_accounts_cmd = ··· 399 392 let info = Cmd.info "list" ~doc in 400 393 Cmd.v info 401 394 Term.( 402 - const list_accounts $ setup_log_term $ base_url_arg $ count_arg $ start_arg 403 - $ json_flag) 395 + const list_accounts $ setup_log_term $ base_url_arg $ count_arg 396 + $ start_arg $ json_flag) 404 397 405 398 let get_account () base_url handle json = 406 399 handle_api_error @@ fun () -> ··· 429 422 in 430 423 Fmt.pr "Showing %d of %d videos (offset %d)@.@." 431 424 (List.length (Peertube.Paginated.data response)) 432 - (Peertube.Paginated.total response) start; 425 + (Peertube.Paginated.total response) 426 + start; 433 427 print_videos ~json (Peertube.Paginated.data response) 434 428 435 429 let get_account_videos_cmd = ··· 453 447 Eio.Switch.run @@ fun sw -> 454 448 let session = Requests.create ~sw env in 455 449 let client = Peertube.Client.create ~session ~base_url in 456 - let response = 457 - Peertube.Client.list_playlists client ~count ~start () 458 - in 450 + let response = Peertube.Client.list_playlists client ~count ~start () in 459 451 Fmt.pr "Showing %d of %d playlists (offset %d)@.@." 460 452 (List.length (Peertube.Paginated.data response)) 461 - (Peertube.Paginated.total response) start; 453 + (Peertube.Paginated.total response) 454 + start; 462 455 print_playlists ~json (Peertube.Paginated.data response) 463 456 464 457 let list_playlists_cmd = ··· 479 472 Peertube.Client.search_playlists client ~query ~count ~start () 480 473 in 481 474 Fmt.pr "Found %d playlists for '%s' (showing %d, offset %d)@.@." 482 - (Peertube.Paginated.total response) query 483 - (List.length (Peertube.Paginated.data response)) start; 475 + (Peertube.Paginated.total response) 476 + query 477 + (List.length (Peertube.Paginated.data response)) 478 + start; 484 479 print_playlists ~json (Peertube.Paginated.data response) 485 480 486 481 let search_playlists_cmd = ··· 518 513 in 519 514 Fmt.pr "Showing %d of %d playlist elements (offset %d)@.@." 520 515 (List.length (Peertube.Paginated.data response)) 521 - (Peertube.Paginated.total response) start; 516 + (Peertube.Paginated.total response) 517 + start; 522 518 List.iter 523 519 (fun (e : Peertube.Playlist.Element.t) -> 524 520 match Peertube.Playlist.Element.video e with ··· 540 536 let doc = "Playlist operations" in 541 537 let info = Cmd.info "playlists" ~doc in 542 538 Cmd.group info 543 - [ list_playlists_cmd; search_playlists_cmd; get_playlist_cmd; 544 - get_playlist_videos_cmd ] 539 + [ 540 + list_playlists_cmd; 541 + search_playlists_cmd; 542 + get_playlist_cmd; 543 + get_playlist_videos_cmd; 544 + ] 545 545 546 546 (* Server commands *) 547 547 ··· 552 552 let session = Requests.create ~sw env in 553 553 let client = Peertube.Client.create ~session ~base_url in 554 554 let config = Peertube.Client.get_config client () in 555 - if json then 556 - let json_str = 557 - Jsont_bytesrw.encode_string Peertube.Server_config.jsont config 558 - in 559 - match json_str with 560 - | Ok s -> print_endline s 561 - | Error e -> Fmt.epr "JSON encode error: %s@." e 555 + if json then print_json Peertube.Server_config.jsont config 562 556 else begin 563 557 let instance = Peertube.Server_config.instance config in 564 558 Fmt.pr 565 - "@[<v>Instance: %s@, %s@, Version: %s@, Signup allowed: %b@, \ 566 - Transcoding: %b@]@." 559 + "@[<v>Instance: %s@,\ 560 + \ %s@,\ 561 + \ Version: %s@,\ 562 + \ Signup allowed: %b@,\ 563 + \ Transcoding: %b@]@." 567 564 (Peertube.Instance_info.name instance) 568 565 (Peertube.Instance_info.short_description instance) 569 566 (Peertube.Server_config.server_version config) ··· 584 581 let session = Requests.create ~sw env in 585 582 let client = Peertube.Client.create ~session ~base_url in 586 583 let stats = Peertube.Client.get_stats client () in 587 - if json then 588 - let json_str = 589 - Jsont_bytesrw.encode_string Peertube.Server_stats.jsont stats 590 - in 591 - match json_str with 592 - | Ok s -> print_endline s 593 - | Error e -> Fmt.epr "JSON encode error: %s@." e 584 + if json then print_json Peertube.Server_stats.jsont stats 594 585 else begin 595 586 Fmt.pr 596 - "@[<v>Users: %d (daily active: %d, weekly: %d, monthly: %d)@,Local \ 597 - videos: %d (views: %d)@,Total videos: %d@,Local channels: %d@,Local \ 598 - playlists: %d@,Instance followers: %d / following: %d@]@." 587 + "@[<v>Users: %d (daily active: %d, weekly: %d, monthly: %d)@,\ 588 + Local videos: %d (views: %d)@,\ 589 + Total videos: %d@,\ 590 + Local channels: %d@,\ 591 + Local playlists: %d@,\ 592 + Instance followers: %d / following: %d@]@." 599 593 (Peertube.Server_stats.total_users stats) 600 594 (Peertube.Server_stats.total_daily_active_users stats) 601 595 (Peertube.Server_stats.total_weekly_active_users stats) ··· 626 620 let doc = "PeerTube API command-line client" in 627 621 let info = Cmd.info "opeertube" ~version:"0.1.0" ~doc in 628 622 Cmd.group info 629 - [ browse_videos_cmd; search_videos_cmd; get_video_cmd; 630 - list_channel_videos_cmd; download_thumbnail_cmd; channels_cmd; 631 - accounts_cmd; playlists_cmd; server_cmd ] 623 + [ 624 + browse_videos_cmd; 625 + search_videos_cmd; 626 + get_video_cmd; 627 + list_channel_videos_cmd; 628 + download_thumbnail_cmd; 629 + channels_cmd; 630 + accounts_cmd; 631 + playlists_cmd; 632 + server_cmd; 633 + ] 632 634 633 635 let () = exit (Cmd.eval main_cmd)
+7 -3
dune-project
··· 3 3 4 4 (generate_opam_files true) 5 5 6 - (source (github avsm/ocaml-peertube)) 6 + (source (uri "https://git.recoil.org/anil.recoil.org/ocaml-peertube")) 7 7 (license ISC) 8 8 (authors "Anil Madhavapeddy") 9 9 (maintainers "anil@recoil.org") 10 + (homepage "https://git.recoil.org/anil.recoil.org/ocaml-peertube") 11 + (bug_reports "https://git.recoil.org/anil.recoil.org/ocaml-peertube/issues") 10 12 11 13 (package 12 14 (name peertube) 13 15 (synopsis "PeerTube API client for OCaml using Eio") 14 - (description "An OCaml client library for the PeerTube video platform API, built on Eio for effect-based I/O") 16 + (description 17 + "An OCaml client library for the PeerTube video platform API, built on Eio for effect-based I/O. Includes a command-line client (opeertube) for browsing videos, channels, accounts, and playlists.") 15 18 (depends 16 19 (ocaml (>= 5.1.0)) 17 20 (eio (>= 1.0)) ··· 21 24 (ptime (>= 1.0)) 22 25 (fmt (>= 0.9)) 23 26 (logs (>= 0.7)) 24 - (cmdliner (>= 1.2)))) 27 + (cmdliner (>= 1.2)) 28 + (odoc :with-doc)))
+326 -264
lib/peertube.mli
··· 1 1 (** PeerTube API client for OCaml. 2 2 3 3 This library provides a typed client for the 4 - {{:https://docs.joinpeertube.org/api-rest-reference.html}PeerTube video platform API}, 5 - using {{:https://github.com/ocaml-multicore/eio}Eio} for effect-based I/O 6 - and {{:https://erratique.ch/software/jsont}jsont} for JSON serialization. 4 + {{:https://docs.joinpeertube.org/api-rest-reference.html}PeerTube video 5 + platform API}, using {{:https://github.com/ocaml-multicore/eio}Eio} for 6 + effect-based I/O and {{:https://erratique.ch/software/jsont}jsont} for JSON 7 + serialization. 7 8 8 9 {1 Quick Start} 9 10 ··· 18 19 19 20 (* List recent videos *) 20 21 let videos = Client.list_videos client () in 21 - List.iter (fun v -> 22 - Printf.printf "%s (%s)\n" (Video.name v) (Video.uuid v) 23 - ) (Paginated.data videos) 22 + List.iter 23 + (fun v -> Printf.printf "%s (%s)\n" (Video.name v) (Video.uuid v)) 24 + (Paginated.data videos) 24 25 ]} 25 26 26 27 {1 Module Organization} 27 28 28 - The library is organized into focused modules, each with an abstract type [t], 29 - a JSON codec [jsont], accessors, and a pretty printer [pp]: 29 + The library is organized into focused modules, each with an abstract type 30 + [t], a JSON codec [jsont], accessors, and a pretty printer [pp]: 30 31 31 32 {2 Common Types} 32 33 - {!Labeled} - ID-label pairs for categories, licences, languages ··· 44 45 - {!Video} - Video records with metadata 45 46 46 47 {2 Playlists} 47 - - {!Playlist} - Video playlists with nested {!Playlist.Privacy}, {!Playlist.Type}, and {!Playlist.Element} 48 + - {!Playlist} - Video playlists with nested {!Playlist.Privacy}, 49 + {!Playlist.Type}, and {!Playlist.Element} 48 50 49 51 {2 Server Information} 50 52 - {!Instance_info} - Instance name and description ··· 59 61 (** Labeled values with ID and display label. 60 62 61 63 Used for categories, licences, languages, and other enumerated values 62 - returned by the PeerTube API. Each labeled value has an identifier 63 - and a human-readable label. *) 64 + returned by the PeerTube API. Each labeled value has an identifier and a 65 + human-readable label. *) 64 66 module Labeled : sig 65 - (** A labeled value with an identifier and human-readable label. *) 66 67 type 'a t 68 + (** A labeled value with an identifier and human-readable label. *) 67 69 68 - (** [make ~id ~label] creates a labeled value. *) 69 70 val make : id:'a -> label:string -> 'a t 71 + (** [make ~id ~label] creates a labeled value. *) 70 72 71 - (** [id t] returns the identifier. *) 72 73 val id : 'a t -> 'a 74 + (** [id t] returns the identifier. *) 73 75 76 + val label : 'a t -> string 74 77 (** [label t] returns the human-readable label. *) 75 - val label : 'a t -> string 76 78 79 + val int_jsont : int t Jsont.t 77 80 (** JSON codec for labeled values with integer IDs. *) 78 - val int_jsont : int t Jsont.t 79 81 80 - (** JSON codec for labeled values with string IDs. *) 81 82 val string_jsont : string t Jsont.t 83 + (** JSON codec for labeled values with string IDs. *) 82 84 83 - (** [pp pp_id] is a pretty printer for labeled values, using [pp_id] for the identifier. *) 84 85 val pp : (Format.formatter -> 'a -> unit) -> Format.formatter -> 'a t -> unit 86 + (** [pp pp_id] is a pretty printer for labeled values, using [pp_id] for the 87 + identifier. *) 85 88 end 86 89 87 90 (** Video privacy levels. 88 91 89 - PeerTube videos can have different privacy settings that control 90 - who can view them. *) 92 + PeerTube videos can have different privacy settings that control who can 93 + view them. *) 91 94 module Privacy : sig 92 95 (** Video privacy level. 93 96 - [Public] - Visible to everyone ··· 96 99 - [Internal] - Only visible to instance users *) 97 100 type t = Public | Unlisted | Private | Internal 98 101 99 - (** [to_int t] converts privacy to its API integer representation. *) 100 102 val to_int : t -> int 103 + (** [to_int t] converts privacy to its API integer representation. *) 101 104 102 - (** [of_int n] converts an API integer to privacy level. *) 103 105 val of_int : int -> t 106 + (** [of_int n] converts an API integer to privacy level. *) 104 107 105 - (** JSON codec for privacy. *) 106 108 val jsont : t Jsont.t 109 + (** JSON codec for privacy. *) 107 110 108 - (** Pretty printer for privacy. *) 109 111 val pp : Format.formatter -> t -> unit 112 + (** Pretty printer for privacy. *) 110 113 end 111 114 112 115 (** Video sort options for API queries. 113 116 114 - When listing or searching videos, you can specify how results should 115 - be sorted. *) 117 + When listing or searching videos, you can specify how results should be 118 + sorted. *) 116 119 module Video_sort : sig 117 120 (** Video sort order. 118 121 - [Newest] - Most recently published first ··· 125 128 - [Best] - Best match (for search) *) 126 129 type t = Newest | Oldest | Views | Likes | Trending | Hot | Random | Best 127 130 131 + val to_string : t -> string 128 132 (** [to_string t] converts sort option to API query string. *) 129 - val to_string : t -> string 130 133 134 + val pp : Format.formatter -> t -> unit 131 135 (** Pretty printer for sort options. *) 132 - val pp : Format.formatter -> t -> unit 133 136 end 134 137 135 138 (** Paginated API responses. 136 139 137 - Most list endpoints return paginated results with a total count 138 - and a page of data. Use [count] and [start] parameters in API 139 - calls to navigate through pages. *) 140 + Most list endpoints return paginated results with a total count and a page 141 + of data. Use [count] and [start] parameters in API calls to navigate through 142 + pages. *) 140 143 module Paginated : sig 141 - (** A paginated response containing total count and a page of data. *) 142 144 type 'a t 145 + (** A paginated response containing total count and a page of data. *) 143 146 147 + val make : total:int -> data:'a list -> 'a t 144 148 (** [make ~total ~data] creates a paginated response. *) 145 - val make : total:int -> data:'a list -> 'a t 146 149 150 + val total : 'a t -> int 147 151 (** [total t] returns the total number of items available across all pages. *) 148 - val total : 'a t -> int 149 152 150 - (** [data t] returns the items in this page. *) 151 153 val data : 'a t -> 'a list 154 + (** [data t] returns the items in this page. *) 152 155 153 - (** [jsont ~kind item_jsont] creates a JSON codec for paginated responses 154 - of items encoded with [item_jsont]. *) 155 156 val jsont : kind:string -> 'a Jsont.t -> 'a t Jsont.t 157 + (** [jsont ~kind item_jsont] creates a JSON codec for paginated responses of 158 + items encoded with [item_jsont]. *) 156 159 157 - (** [pp pp_item] is a pretty printer for paginated responses. *) 158 160 val pp : (Format.formatter -> 'a -> unit) -> Format.formatter -> 'a t -> unit 161 + (** [pp pp_item] is a pretty printer for paginated responses. *) 159 162 end 160 163 161 164 (** {1 Account Types} *) ··· 163 166 (** Summary of a PeerTube account. 164 167 165 168 Account summaries are embedded in other responses (videos, channels, etc.) 166 - and contain basic identifying information. For full account details, 167 - use {!Client.get_account}. *) 169 + and contain basic identifying information. For full account details, use 170 + {!Client.get_account}. *) 168 171 module Account_summary : sig 172 + type t 169 173 (** Account summary type. *) 170 - type t 171 174 172 - (** Create an account summary. *) 173 175 val make : 174 176 id:int -> 175 177 name:string -> ··· 178 180 host:string -> 179 181 avatar_path:string option -> 180 182 t 183 + (** Create an account summary. *) 181 184 185 + val id : t -> int 182 186 (** Numeric account ID. *) 183 - val id : t -> int 184 187 185 - (** Account name (handle without host, e.g., "username"). *) 186 188 val name : t -> string 189 + (** Account name (handle without host, e.g., "username"). *) 187 190 188 - (** Human-readable display name. *) 189 191 val display_name : t -> string 192 + (** Human-readable display name. *) 190 193 191 - (** Full account URL. *) 192 194 val url : t -> string 195 + (** Full account URL. *) 193 196 197 + val host : t -> string 194 198 (** Instance host (e.g., "framatube.org"). *) 195 - val host : t -> string 196 199 200 + val avatar_path : t -> string option 197 201 (** Avatar image path (relative to instance URL). *) 198 - val avatar_path : t -> string option 199 202 200 - (** JSON codec. *) 201 203 val jsont : t Jsont.t 204 + (** JSON codec. *) 202 205 203 - (** Pretty printer. *) 204 206 val pp : Format.formatter -> t -> unit 207 + (** Pretty printer. *) 205 208 end 206 209 207 210 (** Full PeerTube account details. 208 211 209 - Contains complete account information including statistics 210 - and the account summary. *) 212 + Contains complete account information including statistics and the account 213 + summary. *) 211 214 module Account : sig 212 - (** Account type. *) 213 215 type t 216 + (** Account type. *) 214 217 215 - (** Create an account. *) 216 218 val make : 217 219 summary:Account_summary.t -> 218 220 description:string option -> ··· 221 223 following_count:int -> 222 224 following_hosts_count:int option -> 223 225 t 226 + (** Create an account. *) 224 227 225 - (** Basic account information. *) 226 228 val summary : t -> Account_summary.t 229 + (** Basic account information. *) 227 230 231 + val description : t -> string option 228 232 (** Account description/bio. *) 229 - val description : t -> string option 230 233 231 - (** Account creation timestamp. *) 232 234 val created_at : t -> Ptime.t 235 + (** Account creation timestamp. *) 233 236 237 + val followers_count : t -> int 234 238 (** Number of followers. *) 235 - val followers_count : t -> int 236 239 237 - (** Number of accounts being followed. *) 238 240 val following_count : t -> int 241 + (** Number of accounts being followed. *) 239 242 240 - (** Number of federated hosts being followed. *) 241 243 val following_hosts_count : t -> int option 244 + (** Number of federated hosts being followed. *) 242 245 246 + val jsont : t Jsont.t 243 247 (** JSON codec. *) 244 - val jsont : t Jsont.t 245 248 249 + val pp : Format.formatter -> t -> unit 246 250 (** Pretty printer. *) 247 - val pp : Format.formatter -> t -> unit 248 251 end 249 252 250 253 (** {1 Channel Types} *) ··· 252 255 (** Summary of a PeerTube channel. 253 256 254 257 Channel summaries are embedded in other responses (videos, playlists, etc.) 255 - and contain basic identifying information. For full channel details, 256 - use {!Client.get_channel}. *) 258 + and contain basic identifying information. For full channel details, use 259 + {!Client.get_channel}. *) 257 260 module Channel_summary : sig 258 - (** Channel summary type. *) 259 261 type t 262 + (** Channel summary type. *) 260 263 261 - (** Create a channel summary. *) 262 264 val make : 263 265 id:int -> 264 266 name:string -> ··· 267 269 host:string -> 268 270 avatar_path:string option -> 269 271 t 272 + (** Create a channel summary. *) 270 273 271 - (** Numeric channel ID. *) 272 274 val id : t -> int 275 + (** Numeric channel ID. *) 273 276 274 - (** Channel name (handle without host). *) 275 277 val name : t -> string 278 + (** Channel name (handle without host). *) 276 279 277 - (** Human-readable display name. *) 278 280 val display_name : t -> string 281 + (** Human-readable display name. *) 279 282 283 + val url : t -> string 280 284 (** Full channel URL. *) 281 - val url : t -> string 282 285 286 + val host : t -> string 283 287 (** Instance host. *) 284 - val host : t -> string 285 288 286 - (** Avatar image path (relative to instance URL). *) 287 289 val avatar_path : t -> string option 290 + (** Avatar image path (relative to instance URL). *) 288 291 292 + val jsont : t Jsont.t 289 293 (** JSON codec. *) 290 - val jsont : t Jsont.t 291 294 295 + val pp : Format.formatter -> t -> unit 292 296 (** Pretty printer. *) 293 - val pp : Format.formatter -> t -> unit 294 297 end 295 298 296 299 (** Full PeerTube channel details. 297 300 298 - Contains complete channel information including statistics, 299 - the owner account, and the channel summary. *) 301 + Contains complete channel information including statistics, the owner 302 + account, and the channel summary. *) 300 303 module Channel : sig 304 + type t 301 305 (** Channel type. *) 302 - type t 303 306 304 - (** Create a channel. *) 305 307 val make : 306 308 summary:Channel_summary.t -> 307 309 description:string option -> ··· 312 314 banner_path:string option -> 313 315 owner_account:Account_summary.t option -> 314 316 t 317 + (** Create a channel. *) 315 318 319 + val summary : t -> Channel_summary.t 316 320 (** Basic channel information. *) 317 - val summary : t -> Channel_summary.t 318 321 319 - (** Channel description. *) 320 322 val description : t -> string option 323 + (** Channel description. *) 321 324 322 - (** Support text or links (e.g., donation info). *) 323 325 val support : t -> string option 326 + (** Support text or links (e.g., donation info). *) 324 327 325 - (** Channel creation timestamp. *) 326 328 val created_at : t -> Ptime.t 329 + (** Channel creation timestamp. *) 327 330 328 - (** Number of followers. *) 329 331 val followers_count : t -> int 332 + (** Number of followers. *) 330 333 334 + val following_count : t -> int 331 335 (** Number of accounts being followed. *) 332 - val following_count : t -> int 333 336 337 + val banner_path : t -> string option 334 338 (** Banner image path (relative to instance URL). *) 335 - val banner_path : t -> string option 336 339 337 - (** Account that owns this channel. *) 338 340 val owner_account : t -> Account_summary.t option 341 + (** Account that owns this channel. *) 339 342 340 - (** JSON codec. *) 341 343 val jsont : t Jsont.t 344 + (** JSON codec. *) 342 345 346 + val pp : Format.formatter -> t -> unit 343 347 (** Pretty printer. *) 344 - val pp : Format.formatter -> t -> unit 345 348 end 346 349 347 350 (** {1 Video Types} *) ··· 351 354 Contains all metadata for a video including identifiers, timestamps, 352 355 statistics, and references to the channel and account that published it. *) 353 356 module Video : sig 354 - (** Video type. *) 355 357 type t 358 + (** Video type. *) 356 359 357 - (** Create a video. *) 358 360 val make : 359 361 id:int -> 360 362 uuid:string -> ··· 382 384 channel:Channel_summary.t option -> 383 385 account:Account_summary.t option -> 384 386 t 387 + (** Create a video. *) 385 388 389 + val id : t -> int 386 390 (** Numeric video ID. *) 387 - val id : t -> int 388 391 389 - (** Video UUID (standard format). *) 390 392 val uuid : t -> string 393 + (** Video UUID (standard format). *) 391 394 395 + val short_uuid : t -> string option 392 396 (** Short UUID for compact URLs. *) 393 - val short_uuid : t -> string option 394 397 395 - (** Video title. *) 396 398 val name : t -> string 399 + (** Video title. *) 397 400 401 + val description : t -> string option 398 402 (** Video description (may contain markdown). *) 399 - val description : t -> string option 400 403 401 - (** Full video URL. *) 402 404 val url : t -> string 405 + (** Full video URL. *) 403 406 404 - (** Embed iframe path. *) 405 407 val embed_path : t -> string 408 + (** Embed iframe path. *) 406 409 410 + val published_at : t -> Ptime.t 407 411 (** Publication timestamp. *) 408 - val published_at : t -> Ptime.t 409 412 413 + val originally_published_at : t -> Ptime.t option 410 414 (** Original publication timestamp (for re-uploads). *) 411 - val originally_published_at : t -> Ptime.t option 412 415 413 - (** Last update timestamp. *) 414 416 val updated_at : t -> Ptime.t option 417 + (** Last update timestamp. *) 415 418 419 + val thumbnail_path : t -> string option 416 420 (** Thumbnail image path (relative to instance URL). *) 417 - val thumbnail_path : t -> string option 418 421 422 + val preview_path : t -> string option 419 423 (** Preview image path (relative to instance URL). *) 420 - val preview_path : t -> string option 421 424 422 - (** Video tags. *) 423 425 val tags : t -> string list 426 + (** Video tags. *) 424 427 425 - (** Duration in seconds. *) 426 428 val duration : t -> int 429 + (** Duration in seconds. *) 427 430 428 - (** View count. *) 429 431 val views : t -> int 432 + (** View count. *) 430 433 431 - (** Like count. *) 432 434 val likes : t -> int 435 + (** Like count. *) 433 436 437 + val dislikes : t -> int 434 438 (** Dislike count. *) 435 - val dislikes : t -> int 436 439 440 + val is_local : t -> bool 437 441 (** Whether the video is local to this instance (vs federated). *) 438 - val is_local : t -> bool 439 442 440 - (** Whether this is a live stream. *) 441 443 val is_live : t -> bool 444 + (** Whether this is a live stream. *) 442 445 446 + val privacy : t -> Privacy.t 443 447 (** Privacy level. *) 444 - val privacy : t -> Privacy.t 445 448 449 + val category : t -> int Labeled.t option 446 450 (** Video category (e.g., Music, Education). *) 447 - val category : t -> int Labeled.t option 448 451 449 - (** Video licence (e.g., Creative Commons). *) 450 452 val licence : t -> int Labeled.t option 453 + (** Video licence (e.g., Creative Commons). *) 451 454 452 - (** Video language. *) 453 455 val language : t -> string Labeled.t option 456 + (** Video language. *) 454 457 458 + val channel : t -> Channel_summary.t option 455 459 (** Channel that published the video. *) 456 - val channel : t -> Channel_summary.t option 457 460 461 + val account : t -> Account_summary.t option 458 462 (** Account that published the video. *) 459 - val account : t -> Account_summary.t option 460 463 464 + val jsont : t Jsont.t 461 465 (** JSON codec. *) 462 - val jsont : t Jsont.t 463 466 464 - (** Pretty printer. *) 465 467 val pp : Format.formatter -> t -> unit 468 + (** Pretty printer. *) 466 469 end 467 470 468 471 (** {1 Playlist Types} *) ··· 483 486 - [Private] - Only visible to the owner *) 484 487 type t = Public | Unlisted | Private 485 488 486 - (** [to_int t] converts privacy to its API integer representation. *) 487 489 val to_int : t -> int 490 + (** [to_int t] converts privacy to its API integer representation. *) 488 491 489 - (** [of_int n] converts an API integer to privacy level. *) 490 492 val of_int : int -> t 493 + (** [of_int n] converts an API integer to privacy level. *) 491 494 492 - (** JSON codec. *) 493 495 val jsont : t Jsont.t 496 + (** JSON codec. *) 494 497 495 - (** Pretty printer. *) 496 498 val pp : Format.formatter -> t -> unit 499 + (** Pretty printer. *) 497 500 end 498 501 499 502 (** {2 Playlist Type} *) ··· 505 508 - [WatchLater] - Special "Watch Later" playlist *) 506 509 type t = Regular | WatchLater 507 510 511 + val to_int : t -> int 508 512 (** [to_int t] converts type to its API integer representation. *) 509 - val to_int : t -> int 510 513 511 - (** [of_int n] converts an API integer to playlist type. *) 512 514 val of_int : int -> t 515 + (** [of_int n] converts an API integer to playlist type. *) 513 516 514 - (** JSON codec. *) 515 517 val jsont : t Jsont.t 518 + (** JSON codec. *) 516 519 520 + val pp : Format.formatter -> t -> unit 517 521 (** Pretty printer. *) 518 - val pp : Format.formatter -> t -> unit 519 522 end 520 523 521 524 (** {2 Playlist Element} *) 522 525 523 526 (** An element in a PeerTube playlist. 524 527 525 - Playlist elements wrap videos with positioning and optional 526 - timestamp information. *) 528 + Playlist elements wrap videos with positioning and optional timestamp 529 + information. *) 527 530 module Element : sig 528 - (** Playlist element type. *) 529 531 type t 532 + (** Playlist element type. *) 530 533 531 - (** Create a playlist element. *) 532 534 val make : 533 535 id:int -> 534 536 position:int -> ··· 536 538 stop_timestamp:int option -> 537 539 video:Video.t option -> 538 540 t 541 + (** Create a playlist element. *) 539 542 540 - (** Element ID. *) 541 543 val id : t -> int 544 + (** Element ID. *) 542 545 546 + val position : t -> int 543 547 (** Position in playlist (1-indexed). *) 544 - val position : t -> int 545 548 546 - (** Start timestamp in seconds (for partial playback). *) 547 549 val start_timestamp : t -> int option 550 + (** Start timestamp in seconds (for partial playback). *) 548 551 549 - (** Stop timestamp in seconds (for partial playback). *) 550 552 val stop_timestamp : t -> int option 553 + (** Stop timestamp in seconds (for partial playback). *) 551 554 555 + val video : t -> Video.t option 552 556 (** The video (may be [None] if video was deleted). *) 553 - val video : t -> Video.t option 554 557 555 - (** JSON codec. *) 556 558 val jsont : t Jsont.t 559 + (** JSON codec. *) 557 560 558 - (** Pretty printer. *) 559 561 val pp : Format.formatter -> t -> unit 562 + (** Pretty printer. *) 560 563 end 561 564 562 565 (** {2 Playlist} *) 563 566 564 - (** Playlist type. *) 565 567 type t 568 + (** Playlist type. *) 566 569 567 - (** Create a playlist. *) 568 570 val make : 569 571 id:int -> 570 572 uuid:string -> ··· 581 583 owner_account:Account_summary.t option -> 582 584 video_channel:Channel_summary.t option -> 583 585 t 586 + (** Create a playlist. *) 584 587 585 - (** Numeric playlist ID. *) 586 588 val id : t -> int 589 + (** Numeric playlist ID. *) 587 590 588 - (** Playlist UUID. *) 589 591 val uuid : t -> string 592 + (** Playlist UUID. *) 590 593 591 - (** Short UUID for compact URLs. *) 592 594 val short_uuid : t -> string option 595 + (** Short UUID for compact URLs. *) 593 596 594 - (** Display name. *) 595 597 val display_name : t -> string 598 + (** Display name. *) 596 599 600 + val description : t -> string option 597 601 (** Playlist description. *) 598 - val description : t -> string option 599 602 600 - (** Privacy level. *) 601 603 val privacy : t -> Privacy.t 604 + (** Privacy level. *) 602 605 603 - (** Full playlist URL. *) 604 606 val url : t -> string 607 + (** Full playlist URL. *) 605 608 606 - (** Thumbnail image path (relative to instance URL). *) 607 609 val thumbnail_path : t -> string option 610 + (** Thumbnail image path (relative to instance URL). *) 608 611 609 - (** Number of videos in playlist. *) 610 612 val videos_length : t -> int 613 + (** Number of videos in playlist. *) 611 614 615 + val playlist_type : t -> Type.t 612 616 (** Playlist type (regular or watch later). *) 613 - val playlist_type : t -> Type.t 614 617 615 - (** Creation timestamp. *) 616 618 val created_at : t -> Ptime.t 619 + (** Creation timestamp. *) 617 620 621 + val updated_at : t -> Ptime.t 618 622 (** Last update timestamp. *) 619 - val updated_at : t -> Ptime.t 620 623 621 - (** Account that owns this playlist. *) 622 624 val owner_account : t -> Account_summary.t option 625 + (** Account that owns this playlist. *) 623 626 624 - (** Associated video channel (if any). *) 625 627 val video_channel : t -> Channel_summary.t option 628 + (** Associated video channel (if any). *) 626 629 630 + val jsont : t Jsont.t 627 631 (** JSON codec. *) 628 - val jsont : t Jsont.t 629 632 633 + val pp : Format.formatter -> t -> unit 630 634 (** Pretty printer. *) 631 - val pp : Format.formatter -> t -> unit 632 635 end 633 636 634 637 (** {1 Server Types} *) ··· 637 640 638 641 Contains the instance name, description, and basic configuration. *) 639 642 module Instance_info : sig 640 - (** Instance info type. *) 641 643 type t 644 + (** Instance info type. *) 642 645 643 - (** Create instance info. *) 644 646 val make : 645 647 name:string -> 646 648 short_description:string -> ··· 650 652 default_nsfw_policy:string -> 651 653 default_client_route:string -> 652 654 t 655 + (** Create instance info. *) 653 656 657 + val name : t -> string 654 658 (** Instance name. *) 655 - val name : t -> string 656 659 657 - (** Short description (one line). *) 658 660 val short_description : t -> string 661 + (** Short description (one line). *) 659 662 660 - (** Full description (may contain markdown). *) 661 663 val description : t -> string option 664 + (** Full description (may contain markdown). *) 662 665 663 - (** Terms of service. *) 664 666 val terms : t -> string option 667 + (** Terms of service. *) 665 668 666 - (** Whether the instance is NSFW-focused. *) 667 669 val is_nsfw : t -> bool 670 + (** Whether the instance is NSFW-focused. *) 668 671 672 + val default_nsfw_policy : t -> string 669 673 (** Default NSFW display policy ("display", "blur", "do_not_list"). *) 670 - val default_nsfw_policy : t -> string 671 674 675 + val default_client_route : t -> string 672 676 (** Default client route (e.g., "/videos/trending"). *) 673 - val default_client_route : t -> string 674 677 675 - (** JSON codec. *) 676 678 val jsont : t Jsont.t 679 + (** JSON codec. *) 677 680 678 - (** Pretty printer. *) 679 681 val pp : Format.formatter -> t -> unit 682 + (** Pretty printer. *) 680 683 end 681 684 682 685 (** PeerTube server configuration. 683 686 684 687 Contains server version, enabled features, and instance information. *) 685 688 module Server_config : sig 686 - (** Server config type. *) 687 689 type t 690 + (** Server config type. *) 688 691 689 - (** Create server config. *) 690 692 val make : 691 693 instance:Instance_info.t -> 692 694 server_version:string -> ··· 697 699 transcoding_enabled:bool -> 698 700 contact_form_enabled:bool -> 699 701 t 702 + (** Create server config. *) 700 703 701 - (** Instance information. *) 702 704 val instance : t -> Instance_info.t 705 + (** Instance information. *) 703 706 707 + val server_version : t -> string 704 708 (** PeerTube server version (e.g., "5.2.0"). *) 705 - val server_version : t -> string 706 709 710 + val server_commit : t -> string option 707 711 (** Git commit hash (if available). *) 708 - val server_commit : t -> string option 709 712 713 + val signup_allowed : t -> bool 710 714 (** Whether new user signup is allowed. *) 711 - val signup_allowed : t -> bool 712 715 713 - (** Whether signup is allowed for the current IP address. *) 714 716 val signup_allowed_for_current_ip : t -> bool 717 + (** Whether signup is allowed for the current IP address. *) 715 718 716 - (** Whether signup requires email verification. *) 717 719 val signup_requires_email_verification : t -> bool 720 + (** Whether signup requires email verification. *) 718 721 719 - (** Whether video transcoding is enabled. *) 720 722 val transcoding_enabled : t -> bool 723 + (** Whether video transcoding is enabled. *) 721 724 722 - (** Whether the contact form is enabled. *) 723 725 val contact_form_enabled : t -> bool 726 + (** Whether the contact form is enabled. *) 724 727 728 + val jsont : t Jsont.t 725 729 (** JSON codec. *) 726 - val jsont : t Jsont.t 727 730 731 + val pp : Format.formatter -> t -> unit 728 732 (** Pretty printer. *) 729 - val pp : Format.formatter -> t -> unit 730 733 end 731 734 732 735 (** PeerTube server statistics. 733 736 734 - Contains usage statistics for the instance including user counts, 735 - video counts, and federation information. *) 737 + Contains usage statistics for the instance including user counts, video 738 + counts, and federation information. *) 736 739 module Server_stats : sig 737 - (** Server stats type. *) 738 740 type t 741 + (** Server stats type. *) 739 742 740 - (** Create server stats. *) 741 743 val make : 742 744 total_users:int -> 743 745 total_daily_active_users:int -> ··· 754 756 total_instance_followers:int -> 755 757 total_instance_following:int -> 756 758 t 759 + (** Create server stats. *) 757 760 761 + val total_users : t -> int 758 762 (** Total registered users. *) 759 - val total_users : t -> int 760 763 761 - (** Users active in the last day. *) 762 764 val total_daily_active_users : t -> int 765 + (** Users active in the last day. *) 763 766 764 - (** Users active in the last week. *) 765 767 val total_weekly_active_users : t -> int 768 + (** Users active in the last week. *) 766 769 770 + val total_monthly_active_users : t -> int 767 771 (** Users active in the last month. *) 768 - val total_monthly_active_users : t -> int 769 772 770 - (** Videos hosted on this instance. *) 771 773 val total_local_videos : t -> int 774 + (** Videos hosted on this instance. *) 772 775 776 + val total_local_video_views : t -> int 773 777 (** Total views on local videos. *) 774 - val total_local_video_views : t -> int 775 778 776 - (** Total comments on local videos. *) 777 779 val total_local_video_comments : t -> int 780 + (** Total comments on local videos. *) 778 781 779 - (** Total size of local video files in bytes. *) 780 782 val total_local_video_files_size : t -> int64 783 + (** Total size of local video files in bytes. *) 781 784 785 + val total_videos : t -> int 782 786 (** Total videos including federated content. *) 783 - val total_videos : t -> int 784 787 788 + val total_video_comments : t -> int 785 789 (** Total comments including federated content. *) 786 - val total_video_comments : t -> int 787 790 788 - (** Video channels on this instance. *) 789 791 val total_local_video_channels : t -> int 792 + (** Video channels on this instance. *) 790 793 794 + val total_local_playlists : t -> int 791 795 (** Playlists on this instance. *) 792 - val total_local_playlists : t -> int 793 796 797 + val total_instance_followers : t -> int 794 798 (** Instances following this instance. *) 795 - val total_instance_followers : t -> int 796 799 797 - (** Instances this instance follows. *) 798 800 val total_instance_following : t -> int 801 + (** Instances this instance follows. *) 799 802 800 - (** JSON codec. *) 801 803 val jsont : t Jsont.t 804 + (** JSON codec. *) 802 805 803 - (** Pretty printer. *) 804 806 val pp : Format.formatter -> t -> unit 807 + (** Pretty printer. *) 805 808 end 806 809 807 810 (** {1 API Client} *) ··· 819 822 module Client : sig 820 823 (** {2 Session} *) 821 824 825 + type t 822 826 (** A PeerTube API session. 823 827 824 - Encapsulates the HTTP client and base URL for making API requests. 825 - In future, this may also contain authentication credentials. *) 826 - type t 828 + Encapsulates the HTTP client and base URL for making API requests. In 829 + future, this may also contain authentication credentials. *) 827 830 831 + val create : session:Requests.t -> base_url:string -> t 828 832 (** [create ~session ~base_url] creates a new PeerTube API session. 829 833 830 834 @param session HTTP client session from the Requests library 831 - @param base_url Base URL of the PeerTube instance (e.g., "https://framatube.org") *) 832 - val create : session:Requests.t -> base_url:string -> t 835 + @param base_url 836 + Base URL of the PeerTube instance (e.g., "https://framatube.org") *) 833 837 838 + val base_url : t -> string 834 839 (** [base_url t] returns the base URL of the PeerTube instance. *) 835 - val base_url : t -> string 836 840 837 - (** [http_session t] returns the underlying HTTP session. *) 838 841 val http_session : t -> Requests.t 842 + (** [http_session t] returns the underlying HTTP session. *) 839 843 840 844 (** {2 Errors} *) 841 845 842 - (** Log source for debug/info messages. *) 843 846 val log_src : Logs.src 847 + (** Log source for debug/info messages. *) 844 848 845 - (** API error with HTTP status code and message. 846 - Raised when the server returns an error response. *) 847 849 exception Api_error of int * string 850 + (** API error with HTTP status code and message. Raised when the server 851 + returns an error response. *) 848 852 849 853 (** {2 Video Operations} *) 850 854 855 + val list_videos : 856 + t -> 857 + ?count:int -> 858 + ?start:int -> 859 + ?sort:Video_sort.t -> 860 + ?nsfw:bool -> 861 + ?is_local:bool -> 862 + ?is_live:bool -> 863 + ?category_id:int -> 864 + ?tags:string -> 865 + unit -> 866 + Video.t Paginated.t 851 867 (** List videos with optional filtering and sorting. 852 868 853 869 @param count Number of videos per page (default: 20) ··· 858 874 @param is_live Only live streams 859 875 @param category_id Filter by category ID 860 876 @param tags Filter by tags (comma-separated) *) 861 - val list_videos : 877 + 878 + val search_videos : 862 879 t -> 880 + query:string -> 863 881 ?count:int -> 864 882 ?start:int -> 865 883 ?sort:Video_sort.t -> 866 - ?nsfw:bool -> 867 - ?is_local:bool -> 868 - ?is_live:bool -> 869 - ?category_id:int -> 870 - ?tags:string -> 884 + ?search_target:[ `Local | `Search_index ] -> 885 + ?duration_min:int -> 886 + ?duration_max:int -> 887 + ?published_after:Ptime.t -> 888 + ?published_before:Ptime.t -> 871 889 unit -> 872 890 Video.t Paginated.t 873 - 874 891 (** Search for videos. 875 892 876 893 @param query Search query string 877 894 @param count Number of results per page (default: 20) 878 895 @param start Starting offset (default: 0) 879 896 @param sort Sort order (default: {!Video_sort.Best}) 880 - @param search_target [`Local] for this instance, [`Search_index] for federated search 897 + @param search_target 898 + [`Local] for this instance, [`Search_index] for federated search 881 899 @param duration_min Minimum duration in seconds 882 900 @param duration_max Maximum duration in seconds 883 901 @param published_after Only videos published after this date 884 902 @param published_before Only videos published before this date *) 885 - val search_videos : 903 + 904 + val fetch_channel_videos : 886 905 t -> 887 - query:string -> 888 906 ?count:int -> 889 907 ?start:int -> 890 - ?sort:Video_sort.t -> 891 - ?search_target:[ `Local | `Search_index ] -> 892 - ?duration_min:int -> 893 - ?duration_max:int -> 894 - ?published_after:Ptime.t -> 895 - ?published_before:Ptime.t -> 908 + channel:string -> 896 909 unit -> 897 910 Video.t Paginated.t 898 - 899 911 (** Fetch videos from a channel with pagination. 900 912 901 913 @param channel Channel name (e.g., "my_channel") *) 902 - val fetch_channel_videos : 903 - t -> ?count:int -> ?start:int -> channel:string -> unit -> Video.t Paginated.t 904 914 915 + val fetch_all_channel_videos : 916 + t -> 917 + ?page_size:int -> 918 + ?max_pages:int -> 919 + channel:string -> 920 + unit -> 921 + Video.t list 905 922 (** Fetch all videos from a channel using automatic pagination. 906 923 907 - Repeatedly fetches pages until all videos are retrieved or 908 - [max_pages] is reached. 924 + Repeatedly fetches pages until all videos are retrieved or [max_pages] is 925 + reached. 909 926 910 927 @param page_size Videos per page (default: 20) 911 928 @param max_pages Maximum pages to fetch (default: unlimited) *) 912 - val fetch_all_channel_videos : 913 - t -> ?page_size:int -> ?max_pages:int -> channel:string -> unit -> Video.t list 914 929 930 + val fetch_video_details : t -> uuid:string -> unit -> Video.t 915 931 (** Fetch detailed information for a single video. 916 932 917 933 @param uuid Video UUID *) 918 - val fetch_video_details : t -> uuid:string -> unit -> Video.t 919 934 935 + val get_categories : t -> unit -> (int * string) list 920 936 (** Get available video categories. 921 937 922 938 Returns a list of [(id, label)] pairs. *) 923 - val get_categories : t -> unit -> (int * string) list 924 939 940 + val get_licences : t -> unit -> (int * string) list 925 941 (** Get available video licences. 926 942 927 943 Returns a list of [(id, label)] pairs. *) 928 - val get_licences : t -> unit -> (int * string) list 929 944 945 + val get_languages : t -> unit -> (string * string) list 930 946 (** Get available video languages. 931 947 932 948 Returns a list of [(code, label)] pairs. *) 933 - val get_languages : t -> unit -> (string * string) list 934 949 935 950 (** {2 Channel Operations} *) 936 951 952 + val list_channels : 953 + t -> 954 + ?count:int -> 955 + ?start:int -> 956 + ?sort:string -> 957 + unit -> 958 + Channel.t Paginated.t 937 959 (** List all channels on the instance. 938 960 939 961 @param sort Sort order (e.g., "-createdAt" for newest first) *) 940 - val list_channels : 941 - t -> ?count:int -> ?start:int -> ?sort:string -> unit -> Channel.t Paginated.t 942 962 963 + val search_channels : 964 + t -> 965 + query:string -> 966 + ?count:int -> 967 + ?start:int -> 968 + unit -> 969 + Channel.t Paginated.t 943 970 (** Search for channels. 944 971 945 972 @param query Search query string *) 946 - val search_channels : 947 - t -> query:string -> ?count:int -> ?start:int -> unit -> Channel.t Paginated.t 948 973 974 + val get_channel : t -> handle:string -> unit -> Channel.t 949 975 (** Get details for a specific channel. 950 976 951 - @param handle Channel handle (e.g., "my_channel" or "my_channel\@example.com") *) 952 - val get_channel : t -> handle:string -> unit -> Channel.t 977 + @param handle 978 + Channel handle (e.g., "my_channel" or "my_channel@example.com") *) 953 979 954 980 (** {2 Account Operations} *) 955 981 982 + val list_accounts : 983 + t -> 984 + ?count:int -> 985 + ?start:int -> 986 + ?sort:string -> 987 + unit -> 988 + Account.t Paginated.t 956 989 (** List accounts on the instance. 957 990 958 991 @param sort Sort order (e.g., "-createdAt" for newest first) *) 959 - val list_accounts : 960 - t -> ?count:int -> ?start:int -> ?sort:string -> unit -> Account.t Paginated.t 961 992 993 + val get_account : t -> handle:string -> unit -> Account.t 962 994 (** Get details for a specific account. 963 995 964 - @param handle Account handle (e.g., "username" or "username\@example.com") *) 965 - val get_account : t -> handle:string -> unit -> Account.t 996 + @param handle Account handle (e.g., "username" or "username@example.com") 997 + *) 966 998 999 + val get_account_videos : 1000 + t -> 1001 + ?count:int -> 1002 + ?start:int -> 1003 + handle:string -> 1004 + unit -> 1005 + Video.t Paginated.t 967 1006 (** Get videos from a specific account. *) 968 - val get_account_videos : 969 - t -> ?count:int -> ?start:int -> handle:string -> unit -> Video.t Paginated.t 970 1007 1008 + val get_account_channels : 1009 + t -> 1010 + ?count:int -> 1011 + ?start:int -> 1012 + handle:string -> 1013 + unit -> 1014 + Channel.t Paginated.t 971 1015 (** Get channels owned by an account. *) 972 - val get_account_channels : 973 - t -> ?count:int -> ?start:int -> handle:string -> unit -> Channel.t Paginated.t 974 1016 975 1017 (** {2 Playlist Operations} *) 976 1018 1019 + val list_playlists : 1020 + t -> ?count:int -> ?start:int -> unit -> Playlist.t Paginated.t 977 1021 (** List playlists on the instance. *) 978 - val list_playlists : t -> ?count:int -> ?start:int -> unit -> Playlist.t Paginated.t 979 1022 1023 + val search_playlists : 1024 + t -> 1025 + query:string -> 1026 + ?count:int -> 1027 + ?start:int -> 1028 + unit -> 1029 + Playlist.t Paginated.t 980 1030 (** Search for playlists. 981 1031 982 1032 @param query Search query string *) 983 - val search_playlists : 984 - t -> query:string -> ?count:int -> ?start:int -> unit -> Playlist.t Paginated.t 985 1033 1034 + val get_playlist : t -> id:string -> unit -> Playlist.t 986 1035 (** Get details for a specific playlist. 987 1036 988 1037 @param id Playlist ID or UUID *) 989 - val get_playlist : t -> id:string -> unit -> Playlist.t 990 1038 1039 + val get_playlist_videos : 1040 + t -> 1041 + ?count:int -> 1042 + ?start:int -> 1043 + id:string -> 1044 + unit -> 1045 + Playlist.Element.t Paginated.t 991 1046 (** Get videos in a playlist. 992 1047 993 1048 @param id Playlist ID or UUID *) 994 - val get_playlist_videos : 995 - t -> ?count:int -> ?start:int -> id:string -> unit -> Playlist.Element.t Paginated.t 996 1049 997 - (** Get playlists owned by an account. *) 998 1050 val get_account_playlists : 999 - t -> ?count:int -> ?start:int -> handle:string -> unit -> Playlist.t Paginated.t 1051 + t -> 1052 + ?count:int -> 1053 + ?start:int -> 1054 + handle:string -> 1055 + unit -> 1056 + Playlist.t Paginated.t 1057 + (** Get playlists owned by an account. *) 1000 1058 1001 1059 (** {2 Server Operations} *) 1002 1060 1003 - (** Get server configuration. *) 1004 1061 val get_config : t -> unit -> Server_config.t 1062 + (** Get server configuration. *) 1005 1063 1006 - (** Get server statistics. *) 1007 1064 val get_stats : t -> unit -> Server_stats.t 1065 + (** Get server statistics. *) 1008 1066 1009 1067 (** {2 Utilities} *) 1010 1068 1069 + val thumbnail_url : t -> Video.t -> string option 1011 1070 (** Get the full thumbnail URL for a video. 1012 1071 1013 - Combines the session's base URL with the video's thumbnail path. 1014 - Returns [None] if the video has no thumbnail. *) 1015 - val thumbnail_url : t -> Video.t -> string option 1072 + Combines the session's base URL with the video's thumbnail path. Returns 1073 + [None] if the video has no thumbnail. *) 1016 1074 1075 + val download_thumbnail : 1076 + t -> 1077 + video:Video.t -> 1078 + output_path:string -> 1079 + unit -> 1080 + (unit, [ `Msg of string ]) result 1017 1081 (** Download a video's thumbnail to a file. 1018 1082 1019 1083 @param output_path Path to save the thumbnail image *) 1020 - val download_thumbnail : 1021 - t -> video:Video.t -> output_path:string -> unit -> (unit, [ `Msg of string ]) result 1022 1084 1085 + val to_tuple : Video.t -> string * Ptime.t * string * string * string * string 1023 1086 (** Convert a video to a tuple for external use. 1024 1087 1025 1088 Returns [(description, published_date, title, url, uuid, id_string)]. *) 1026 - val to_tuple : Video.t -> string * Ptime.t * string * string * string * string 1027 1089 end
+41 -29
lib/peertube_account.ml
··· 11 11 12 12 let make ~summary ~description ~created_at ~followers_count ~following_count 13 13 ~following_hosts_count = 14 - { summary; description; created_at; followers_count; following_count; 15 - following_hosts_count } 14 + { 15 + summary; 16 + description; 17 + created_at; 18 + followers_count; 19 + following_count; 20 + following_hosts_count; 21 + } 16 22 17 23 let summary t = t.summary 18 24 let description t = t.description ··· 28 34 Peertube_account_summary.make ~id ~name ~display_name ~url ~host 29 35 ~avatar_path 30 36 in 31 - { summary; description; created_at; followers_count; following_count; 32 - following_hosts_count } 37 + { 38 + summary; 39 + description; 40 + created_at; 41 + followers_count; 42 + following_count; 43 + following_hosts_count; 44 + } 33 45 in 34 46 Jsont.Object.map ~kind:"account" make 35 - |> Jsont.Object.mem "id" Jsont.int 36 - ~enc:(fun a -> Peertube_account_summary.id a.summary) 37 - |> Jsont.Object.mem "name" Jsont.string 38 - ~enc:(fun a -> Peertube_account_summary.name a.summary) 39 - |> Jsont.Object.mem "displayName" Jsont.string 40 - ~enc:(fun a -> Peertube_account_summary.display_name a.summary) 41 - |> Jsont.Object.mem "url" Jsont.string 42 - ~enc:(fun a -> Peertube_account_summary.url a.summary) 43 - |> Jsont.Object.mem "host" Jsont.string 44 - ~enc:(fun a -> Peertube_account_summary.host a.summary) 47 + |> Jsont.Object.mem "id" Jsont.int ~enc:(fun a -> 48 + Peertube_account_summary.id a.summary) 49 + |> Jsont.Object.mem "name" Jsont.string ~enc:(fun a -> 50 + Peertube_account_summary.name a.summary) 51 + |> Jsont.Object.mem "displayName" Jsont.string ~enc:(fun a -> 52 + Peertube_account_summary.display_name a.summary) 53 + |> Jsont.Object.mem "url" Jsont.string ~enc:(fun a -> 54 + Peertube_account_summary.url a.summary) 55 + |> Jsont.Object.mem "host" Jsont.string ~enc:(fun a -> 56 + Peertube_account_summary.host a.summary) 45 57 |> Jsont.Object.mem "avatars" Peertube_avatar.jsont ~dec_absent:None 46 - ~enc_omit:Option.is_none 47 - ~enc:(fun a -> Peertube_account_summary.avatar_path a.summary) 48 - |> Jsont.Object.mem "description" (Jsont.option Jsont.string) ~dec_absent:None 49 - ~enc_omit:Option.is_none ~enc:(fun a -> a.description) 50 - |> Jsont.Object.mem "createdAt" Peertube_ptime.jsont ~enc:(fun a -> a.created_at) 51 - |> Jsont.Object.mem "followersCount" Jsont.int ~dec_absent:0 52 - ~enc:(fun a -> a.followers_count) 53 - |> Jsont.Object.mem "followingCount" Jsont.int ~dec_absent:0 54 - ~enc:(fun a -> a.following_count) 58 + ~enc_omit:Option.is_none ~enc:(fun a -> 59 + Peertube_account_summary.avatar_path a.summary) 60 + |> Jsont.Object.mem "description" (Jsont.option Jsont.string) 61 + ~dec_absent:None ~enc_omit:Option.is_none ~enc:(fun a -> a.description) 62 + |> Jsont.Object.mem "createdAt" Peertube_ptime.jsont ~enc:(fun a -> 63 + a.created_at) 64 + |> Jsont.Object.mem "followersCount" Jsont.int ~dec_absent:0 ~enc:(fun a -> 65 + a.followers_count) 66 + |> Jsont.Object.mem "followingCount" Jsont.int ~dec_absent:0 ~enc:(fun a -> 67 + a.following_count) 55 68 |> Jsont.Object.mem "hostRedundancyAllowed" (Jsont.option Jsont.int) 56 - ~dec_absent:None ~enc_omit:Option.is_none 57 - ~enc:(fun a -> a.following_hosts_count) 58 - |> Jsont.Object.skip_unknown 59 - |> Jsont.Object.finish 69 + ~dec_absent:None ~enc_omit:Option.is_none ~enc:(fun a -> 70 + a.following_hosts_count) 71 + |> Jsont.Object.skip_unknown |> Jsont.Object.finish 60 72 61 73 let pp ppf t = 62 74 Fmt.pf ppf 63 - "@[<hov 2>{ summary = %a;@ description = %a;@ created_at = %a;@ \ 64 - followers = %d;@ following = %d }@]" 75 + "@[<hov 2>{ summary = %a;@ description = %a;@ created_at = %a;@ followers \ 76 + = %d;@ following = %d }@]" 65 77 Peertube_account_summary.pp t.summary 66 78 Fmt.(option ~none:(any "None") (fmt "%S")) 67 79 t.description Peertube_ptime.pp t.created_at t.followers_count
+10 -10
lib/peertube_account.mli
··· 1 1 (** Full PeerTube account details. *) 2 2 3 - (** Account type. *) 4 3 type t 4 + (** Account type. *) 5 5 6 - (** Create an account. *) 7 6 val make : 8 7 summary:Peertube_account_summary.t -> 9 8 description:string option -> ··· 12 11 following_count:int -> 13 12 following_hosts_count:int option -> 14 13 t 14 + (** Create an account. *) 15 15 16 - (** Account summary (basic info). *) 17 16 val summary : t -> Peertube_account_summary.t 17 + (** Account summary (basic info). *) 18 18 19 + val description : t -> string option 19 20 (** Account description/bio. *) 20 - val description : t -> string option 21 21 22 - (** Account creation timestamp. *) 23 22 val created_at : t -> Ptime.t 23 + (** Account creation timestamp. *) 24 24 25 - (** Number of followers. *) 26 25 val followers_count : t -> int 26 + (** Number of followers. *) 27 27 28 + val following_count : t -> int 28 29 (** Number of accounts being followed. *) 29 - val following_count : t -> int 30 30 31 - (** Number of hosts being followed (federation). *) 32 31 val following_hosts_count : t -> int option 32 + (** Number of hosts being followed (federation). *) 33 33 34 + val jsont : t Jsont.t 34 35 (** JSON codec. *) 35 - val jsont : t Jsont.t 36 36 37 - (** Pretty printer. *) 38 37 val pp : Format.formatter -> t -> unit 38 + (** Pretty printer. *)
+4 -4
lib/peertube_account_summary.ml
··· 31 31 |> Jsont.Object.mem "host" Jsont.string ~enc:(fun a -> a.host) 32 32 |> Jsont.Object.mem "avatars" Peertube_avatar.jsont ~dec_absent:None 33 33 ~enc_omit:Option.is_none ~enc:(fun a -> a.avatar_path) 34 - |> Jsont.Object.skip_unknown 35 - |> Jsont.Object.finish 34 + |> Jsont.Object.skip_unknown |> Jsont.Object.finish 36 35 37 36 let pp ppf t = 38 - Fmt.pf ppf "@[<hov 2>{ id = %d;@ name = %S;@ display_name = %S;@ host = %S }@]" 39 - t.id t.name t.display_name t.host 37 + Fmt.pf ppf 38 + "@[<hov 2>{ id = %d;@ name = %S;@ display_name = %S;@ host = %S }@]" t.id 39 + t.name t.display_name t.host
+10 -10
lib/peertube_account_summary.mli
··· 1 1 (** Summary of a PeerTube account (embedded in other responses). *) 2 2 3 - (** Account summary type. *) 4 3 type t 4 + (** Account summary type. *) 5 5 6 - (** Create an account summary. *) 7 6 val make : 8 7 id:int -> 9 8 name:string -> ··· 12 11 host:string -> 13 12 avatar_path:string option -> 14 13 t 14 + (** Create an account summary. *) 15 15 16 - (** Account ID. *) 17 16 val id : t -> int 17 + (** Account ID. *) 18 18 19 + val name : t -> string 19 20 (** Account name (handle without host). *) 20 - val name : t -> string 21 21 22 - (** Display name. *) 23 22 val display_name : t -> string 23 + (** Display name. *) 24 24 25 - (** Account URL. *) 26 25 val url : t -> string 26 + (** Account URL. *) 27 27 28 + val host : t -> string 28 29 (** Instance host. *) 29 - val host : t -> string 30 30 31 - (** Avatar image path (relative). *) 32 31 val avatar_path : t -> string option 32 + (** Avatar image path (relative). *) 33 33 34 + val jsont : t Jsont.t 34 35 (** JSON codec. *) 35 - val jsont : t Jsont.t 36 36 37 - (** Pretty printer. *) 38 37 val pp : Format.formatter -> t -> unit 38 + (** Pretty printer. *)
+1 -2
lib/peertube_avatar.ml
··· 5 5 Jsont.Object.map ~kind:"avatar" make 6 6 |> Jsont.Object.mem "path" Jsont.string ~enc:Fun.id 7 7 |> Jsont.Object.mem "width" Jsont.int ~dec_absent:0 ~enc:(Fun.const 0) 8 - |> Jsont.Object.skip_unknown 9 - |> Jsont.Object.finish 8 + |> Jsont.Object.skip_unknown |> Jsont.Object.finish 10 9 11 10 let jsont : string option Jsont.t = 12 11 Jsont.map ~kind:"avatar_path"
+3 -3
lib/peertube_avatar.mli
··· 1 1 (** Avatar path extraction from PeerTube API responses. 2 2 3 - PeerTube returns avatars as an array of objects with path and width. 4 - This module extracts the first avatar path. *) 3 + PeerTube returns avatars as an array of objects with path and width. This 4 + module extracts the first avatar path. *) 5 5 6 + val jsont : string option Jsont.t 6 7 (** JSON codec that extracts the first avatar path from an avatars array. *) 7 - val jsont : string option Jsont.t
+45 -29
lib/peertube_channel.ml
··· 13 13 14 14 let make ~summary ~description ~support ~created_at ~followers_count 15 15 ~following_count ~banner_path ~owner_account = 16 - { summary; description; support; created_at; followers_count; following_count; 17 - banner_path; owner_account } 16 + { 17 + summary; 18 + description; 19 + support; 20 + created_at; 21 + followers_count; 22 + following_count; 23 + banner_path; 24 + owner_account; 25 + } 18 26 19 27 let summary t = t.summary 20 28 let description t = t.description ··· 32 40 Peertube_channel_summary.make ~id ~name ~display_name ~url ~host 33 41 ~avatar_path 34 42 in 35 - { summary; description; support; created_at; followers_count; 36 - following_count; banner_path; owner_account } 43 + { 44 + summary; 45 + description; 46 + support; 47 + created_at; 48 + followers_count; 49 + following_count; 50 + banner_path; 51 + owner_account; 52 + } 37 53 in 38 54 Jsont.Object.map ~kind:"channel" make 39 - |> Jsont.Object.mem "id" Jsont.int 40 - ~enc:(fun c -> Peertube_channel_summary.id c.summary) 41 - |> Jsont.Object.mem "name" Jsont.string 42 - ~enc:(fun c -> Peertube_channel_summary.name c.summary) 43 - |> Jsont.Object.mem "displayName" Jsont.string 44 - ~enc:(fun c -> Peertube_channel_summary.display_name c.summary) 45 - |> Jsont.Object.mem "url" Jsont.string 46 - ~enc:(fun c -> Peertube_channel_summary.url c.summary) 47 - |> Jsont.Object.mem "host" Jsont.string 48 - ~enc:(fun c -> Peertube_channel_summary.host c.summary) 55 + |> Jsont.Object.mem "id" Jsont.int ~enc:(fun c -> 56 + Peertube_channel_summary.id c.summary) 57 + |> Jsont.Object.mem "name" Jsont.string ~enc:(fun c -> 58 + Peertube_channel_summary.name c.summary) 59 + |> Jsont.Object.mem "displayName" Jsont.string ~enc:(fun c -> 60 + Peertube_channel_summary.display_name c.summary) 61 + |> Jsont.Object.mem "url" Jsont.string ~enc:(fun c -> 62 + Peertube_channel_summary.url c.summary) 63 + |> Jsont.Object.mem "host" Jsont.string ~enc:(fun c -> 64 + Peertube_channel_summary.host c.summary) 49 65 |> Jsont.Object.mem "avatars" Peertube_avatar.jsont ~dec_absent:None 50 - ~enc_omit:Option.is_none 51 - ~enc:(fun c -> Peertube_channel_summary.avatar_path c.summary) 52 - |> Jsont.Object.mem "description" (Jsont.option Jsont.string) ~dec_absent:None 53 - ~enc_omit:Option.is_none ~enc:(fun c -> c.description) 54 - |> Jsont.Object.mem "support" (Jsont.option Jsont.string) ~dec_absent:None 55 - ~enc_omit:Option.is_none ~enc:(fun c -> c.support) 56 - |> Jsont.Object.mem "createdAt" Peertube_ptime.jsont ~enc:(fun c -> c.created_at) 57 - |> Jsont.Object.mem "followersCount" Jsont.int ~dec_absent:0 58 - ~enc:(fun c -> c.followers_count) 59 - |> Jsont.Object.mem "followingCount" Jsont.int ~dec_absent:0 60 - ~enc:(fun c -> c.following_count) 66 + ~enc_omit:Option.is_none ~enc:(fun c -> 67 + Peertube_channel_summary.avatar_path c.summary) 68 + |> Jsont.Object.mem "description" (Jsont.option Jsont.string) 69 + ~dec_absent:None ~enc_omit:Option.is_none ~enc:(fun c -> c.description) 70 + |> Jsont.Object.mem "support" (Jsont.option Jsont.string) 71 + ~dec_absent:None ~enc_omit:Option.is_none ~enc:(fun c -> c.support) 72 + |> Jsont.Object.mem "createdAt" Peertube_ptime.jsont ~enc:(fun c -> 73 + c.created_at) 74 + |> Jsont.Object.mem "followersCount" Jsont.int ~dec_absent:0 ~enc:(fun c -> 75 + c.followers_count) 76 + |> Jsont.Object.mem "followingCount" Jsont.int ~dec_absent:0 ~enc:(fun c -> 77 + c.following_count) 61 78 |> Jsont.Object.mem "banners" Peertube_avatar.jsont ~dec_absent:None 62 79 ~enc_omit:Option.is_none ~enc:(fun c -> c.banner_path) 63 80 |> Jsont.Object.mem "ownerAccount" 64 81 (Jsont.option Peertube_account_summary.jsont) 65 82 ~dec_absent:None ~enc_omit:Option.is_none ~enc:(fun c -> c.owner_account) 66 - |> Jsont.Object.skip_unknown 67 - |> Jsont.Object.finish 83 + |> Jsont.Object.skip_unknown |> Jsont.Object.finish 68 84 69 85 let pp ppf t = 70 86 Fmt.pf ppf 71 - "@[<hov 2>{ summary = %a;@ description = %a;@ created_at = %a;@ \ 72 - followers = %d }@]" 87 + "@[<hov 2>{ summary = %a;@ description = %a;@ created_at = %a;@ followers \ 88 + = %d }@]" 73 89 Peertube_channel_summary.pp t.summary 74 90 Fmt.(option ~none:(any "None") (fmt "%S")) 75 91 t.description Peertube_ptime.pp t.created_at t.followers_count
+12 -12
lib/peertube_channel.mli
··· 1 1 (** Full PeerTube channel details. *) 2 2 3 - (** Channel type. *) 4 3 type t 4 + (** Channel type. *) 5 5 6 - (** Create a channel. *) 7 6 val make : 8 7 summary:Peertube_channel_summary.t -> 9 8 description:string option -> ··· 14 13 banner_path:string option -> 15 14 owner_account:Peertube_account_summary.t option -> 16 15 t 16 + (** Create a channel. *) 17 17 18 + val summary : t -> Peertube_channel_summary.t 18 19 (** Channel summary (basic info). *) 19 - val summary : t -> Peertube_channel_summary.t 20 20 21 - (** Channel description. *) 22 21 val description : t -> string option 22 + (** Channel description. *) 23 23 24 - (** Support text/links. *) 25 24 val support : t -> string option 25 + (** Support text/links. *) 26 26 27 + val created_at : t -> Ptime.t 27 28 (** Channel creation timestamp. *) 28 - val created_at : t -> Ptime.t 29 29 30 - (** Number of followers. *) 31 30 val followers_count : t -> int 31 + (** Number of followers. *) 32 32 33 - (** Number of accounts being followed. *) 34 33 val following_count : t -> int 34 + (** Number of accounts being followed. *) 35 35 36 + val banner_path : t -> string option 36 37 (** Banner image path (relative). *) 37 - val banner_path : t -> string option 38 38 39 - (** Owner account. *) 40 39 val owner_account : t -> Peertube_account_summary.t option 40 + (** Owner account. *) 41 41 42 - (** JSON codec. *) 43 42 val jsont : t Jsont.t 43 + (** JSON codec. *) 44 44 45 + val pp : Format.formatter -> t -> unit 45 46 (** Pretty printer. *) 46 - val pp : Format.formatter -> t -> unit
+4 -4
lib/peertube_channel_summary.ml
··· 31 31 |> Jsont.Object.mem "host" Jsont.string ~enc:(fun c -> c.host) 32 32 |> Jsont.Object.mem "avatars" Peertube_avatar.jsont ~dec_absent:None 33 33 ~enc_omit:Option.is_none ~enc:(fun c -> c.avatar_path) 34 - |> Jsont.Object.skip_unknown 35 - |> Jsont.Object.finish 34 + |> Jsont.Object.skip_unknown |> Jsont.Object.finish 36 35 37 36 let pp ppf t = 38 - Fmt.pf ppf "@[<hov 2>{ id = %d;@ name = %S;@ display_name = %S;@ host = %S }@]" 39 - t.id t.name t.display_name t.host 37 + Fmt.pf ppf 38 + "@[<hov 2>{ id = %d;@ name = %S;@ display_name = %S;@ host = %S }@]" t.id 39 + t.name t.display_name t.host
+10 -10
lib/peertube_channel_summary.mli
··· 1 1 (** Summary of a PeerTube channel (embedded in other responses). *) 2 2 3 - (** Channel summary type. *) 4 3 type t 4 + (** Channel summary type. *) 5 5 6 - (** Create a channel summary. *) 7 6 val make : 8 7 id:int -> 9 8 name:string -> ··· 12 11 host:string -> 13 12 avatar_path:string option -> 14 13 t 14 + (** Create a channel summary. *) 15 15 16 - (** Channel ID. *) 17 16 val id : t -> int 17 + (** Channel ID. *) 18 18 19 + val name : t -> string 19 20 (** Channel name (handle without host). *) 20 - val name : t -> string 21 21 22 - (** Display name. *) 23 22 val display_name : t -> string 23 + (** Display name. *) 24 24 25 - (** Channel URL. *) 26 25 val url : t -> string 26 + (** Channel URL. *) 27 27 28 + val host : t -> string 28 29 (** Instance host. *) 29 - val host : t -> string 30 30 31 - (** Avatar image path (relative). *) 32 31 val avatar_path : t -> string option 32 + (** Avatar image path (relative). *) 33 33 34 + val jsont : t Jsont.t 34 35 (** JSON codec. *) 35 - val jsont : t Jsont.t 36 36 37 - (** Pretty printer. *) 38 37 val pp : Format.formatter -> t -> unit 38 + (** Pretty printer. *)
+76 -53
lib/peertube_client.ml
··· 1 1 (** PeerTube API client functions. *) 2 2 3 - (** Session type encapsulating HTTP client and base URL. *) 4 3 type t = { session : Requests.t; base_url : string } 4 + (** Session type encapsulating HTTP client and base URL. *) 5 5 6 6 let create ~session ~base_url = { session; base_url } 7 7 let base_url t = t.base_url 8 8 let http_session t = t.session 9 - 10 9 let log_src = Logs.Src.create "peertube" ~doc:"PeerTube API client" 11 10 12 11 module Log = (val Logs.src_log log_src : Logs.LOG) ··· 23 22 in 24 23 Jsont.Object.map ~kind:"error_response" make 25 24 |> Jsont.Object.mem "type" Jsont.string ~dec_absent:"" ~enc:(Fun.const "") 26 - |> Jsont.Object.mem "error" (Jsont.option Jsont.string) ~dec_absent:None 27 - ~enc_omit:Option.is_none ~enc:(Fun.const None) 25 + |> Jsont.Object.mem "error" 26 + (Jsont.option Jsont.string) 27 + ~dec_absent:None ~enc_omit:Option.is_none ~enc:(Fun.const None) 28 28 |> Jsont.Object.mem "status" Jsont.int ~dec_absent:0 ~enc:(Fun.const 0) 29 - |> Jsont.Object.mem "detail" (Jsont.option Jsont.string) ~dec_absent:None 30 - ~enc_omit:Option.is_none ~enc:Option.some 31 - |> Jsont.Object.skip_unknown 32 - |> Jsont.Object.finish 29 + |> Jsont.Object.mem "detail" 30 + (Jsont.option Jsont.string) 31 + ~dec_absent:None ~enc_omit:Option.is_none ~enc:Option.some 32 + |> Jsont.Object.skip_unknown |> Jsont.Object.finish 33 33 34 34 let raise_api_error status body = 35 35 let msg = ··· 74 74 let video_paginated_jsont = 75 75 Peertube_paginated.jsont ~kind:"video_paginated" Peertube_video.jsont 76 76 77 - let list_videos t ?(count = 20) ?(start = 0) ?(sort = Peertube_video_sort.Newest) 78 - ?(nsfw = false) ?is_local ?is_live ?category_id ?tags () = 77 + let list_videos t ?(count = 20) ?(start = 0) 78 + ?(sort = Peertube_video_sort.Newest) ?(nsfw = false) ?is_local ?is_live 79 + ?category_id ?tags () = 79 80 let url = 80 81 build_url t.base_url [ "api"; "v1"; "videos" ] 81 82 [ ··· 83 84 Some ("start", string_of_int start); 84 85 Some ("sort", Peertube_video_sort.to_string sort); 85 86 Some ("nsfw", if nsfw then "true" else "false"); 86 - Option.map (fun b -> ("isLocal", if b then "true" else "false")) is_local; 87 + Option.map 88 + (fun b -> ("isLocal", if b then "true" else "false")) 89 + is_local; 87 90 Option.map (fun b -> ("isLive", if b then "true" else "false")) is_live; 88 91 Option.map (fun i -> ("categoryOneOf", string_of_int i)) category_id; 89 92 Option.map (fun t -> ("tagsOneOf", t)) tags; ··· 100 103 ?(sort = Peertube_video_sort.Best) ?(search_target = `Local) ?duration_min 101 104 ?duration_max ?published_after ?published_before () = 102 105 let url = 103 - build_url t.base_url [ "api"; "v1"; "search"; "videos" ] 106 + build_url t.base_url 107 + [ "api"; "v1"; "search"; "videos" ] 104 108 [ 105 109 Some ("search", query); 106 110 Some ("count", string_of_int count); ··· 126 130 127 131 let fetch_channel_videos t ?(count = 20) ?(start = 0) ~channel () = 128 132 let url = 129 - build_url t.base_url [ "api"; "v1"; "video-channels"; channel; "videos" ] 133 + build_url t.base_url 134 + [ "api"; "v1"; "video-channels"; channel; "videos" ] 130 135 [ 131 - Some ("count", string_of_int count); 132 - Some ("start", string_of_int start); 136 + Some ("count", string_of_int count); Some ("start", string_of_int start); 133 137 ] 134 138 in 135 139 let result = get_json ~session:t.session ~url video_paginated_jsont in 136 140 Log.info (fun m -> 137 141 m "Fetched %d videos from channel %s (total: %d)" 138 142 (List.length (Peertube_paginated.data result)) 139 - channel (Peertube_paginated.total result)); 143 + channel 144 + (Peertube_paginated.total result)); 140 145 result 141 146 142 147 let fetch_all_channel_videos t ?(page_size = 20) ?max_pages ~channel () = ··· 147 152 let rec fetch_pages page start acc = 148 153 let response = fetch_channel_videos t ~count:page_size ~start ~channel () in 149 154 let all_videos = acc @ Peertube_paginated.data response in 150 - let fetched_count = start + List.length (Peertube_paginated.data response) in 155 + let fetched_count = 156 + start + List.length (Peertube_paginated.data response) 157 + in 151 158 let more_available = fetched_count < Peertube_paginated.total response in 152 159 let under_max_pages = 153 160 match max_pages with None -> true | Some max -> page < max ··· 155 162 Log.debug (fun m -> 156 163 m "Page %d: fetched %d, total so far %d/%d" page 157 164 (List.length (Peertube_paginated.data response)) 158 - fetched_count (Peertube_paginated.total response)); 165 + fetched_count 166 + (Peertube_paginated.total response)); 159 167 if more_available && under_max_pages then 160 168 fetch_pages (page + 1) fetched_count all_videos 161 169 else begin ··· 225 233 226 234 let list_channels t ?(count = 20) ?(start = 0) ?(sort = "-createdAt") () = 227 235 let url = 228 - build_url t.base_url [ "api"; "v1"; "video-channels" ] 236 + build_url t.base_url 237 + [ "api"; "v1"; "video-channels" ] 229 238 [ 230 239 Some ("count", string_of_int count); 231 240 Some ("start", string_of_int start); ··· 241 250 242 251 let search_channels t ~query ?(count = 20) ?(start = 0) () = 243 252 let url = 244 - build_url t.base_url [ "api"; "v1"; "search"; "video-channels" ] 253 + build_url t.base_url 254 + [ "api"; "v1"; "search"; "video-channels" ] 245 255 [ 246 256 Some ("search", query); 247 257 Some ("count", string_of_int count); ··· 260 270 let channel = get_json ~session:t.session ~url Peertube_channel.jsont in 261 271 Log.info (fun m -> 262 272 m "Fetched channel: %s" 263 - (Peertube_channel_summary.display_name (Peertube_channel.summary channel))); 273 + (Peertube_channel_summary.display_name 274 + (Peertube_channel.summary channel))); 264 275 channel 265 276 266 277 (* Account operations *) ··· 270 281 271 282 let list_accounts t ?(count = 20) ?(start = 0) ?(sort = "-createdAt") () = 272 283 let url = 273 - build_url t.base_url [ "api"; "v1"; "accounts" ] 284 + build_url t.base_url 285 + [ "api"; "v1"; "accounts" ] 274 286 [ 275 287 Some ("count", string_of_int count); 276 288 Some ("start", string_of_int start); ··· 289 301 let account = get_json ~session:t.session ~url Peertube_account.jsont in 290 302 Log.info (fun m -> 291 303 m "Fetched account: %s" 292 - (Peertube_account_summary.display_name (Peertube_account.summary account))); 304 + (Peertube_account_summary.display_name 305 + (Peertube_account.summary account))); 293 306 account 294 307 295 308 let get_account_videos t ?(count = 20) ?(start = 0) ~handle () = 296 309 let url = 297 - build_url t.base_url [ "api"; "v1"; "accounts"; handle; "videos" ] 310 + build_url t.base_url 311 + [ "api"; "v1"; "accounts"; handle; "videos" ] 298 312 [ 299 - Some ("count", string_of_int count); 300 - Some ("start", string_of_int start); 313 + Some ("count", string_of_int count); Some ("start", string_of_int start); 301 314 ] 302 315 in 303 316 let result = get_json ~session:t.session ~url video_paginated_jsont in 304 317 Log.info (fun m -> 305 318 m "Fetched %d videos from account %s (total: %d)" 306 319 (List.length (Peertube_paginated.data result)) 307 - handle (Peertube_paginated.total result)); 320 + handle 321 + (Peertube_paginated.total result)); 308 322 result 309 323 310 324 let get_account_channels t ?(count = 20) ?(start = 0) ~handle () = 311 325 let url = 312 - build_url t.base_url [ "api"; "v1"; "accounts"; handle; "video-channels" ] 326 + build_url t.base_url 327 + [ "api"; "v1"; "accounts"; handle; "video-channels" ] 313 328 [ 314 - Some ("count", string_of_int count); 315 - Some ("start", string_of_int start); 329 + Some ("count", string_of_int count); Some ("start", string_of_int start); 316 330 ] 317 331 in 318 332 let result = get_json ~session:t.session ~url channel_paginated_jsont in 319 333 Log.info (fun m -> 320 334 m "Fetched %d channels from account %s (total: %d)" 321 335 (List.length (Peertube_paginated.data result)) 322 - handle (Peertube_paginated.total result)); 336 + handle 337 + (Peertube_paginated.total result)); 323 338 result 324 339 325 340 (* Playlist operations *) ··· 333 348 334 349 let list_playlists t ?(count = 20) ?(start = 0) () = 335 350 let url = 336 - build_url t.base_url [ "api"; "v1"; "video-playlists" ] 351 + build_url t.base_url 352 + [ "api"; "v1"; "video-playlists" ] 337 353 [ 338 - Some ("count", string_of_int count); 339 - Some ("start", string_of_int start); 354 + Some ("count", string_of_int count); Some ("start", string_of_int start); 340 355 ] 341 356 in 342 357 let result = get_json ~session:t.session ~url playlist_paginated_jsont in ··· 348 363 349 364 let search_playlists t ~query ?(count = 20) ?(start = 0) () = 350 365 let url = 351 - build_url t.base_url [ "api"; "v1"; "search"; "video-playlists" ] 366 + build_url t.base_url 367 + [ "api"; "v1"; "search"; "video-playlists" ] 352 368 [ 353 369 Some ("search", query); 354 370 Some ("count", string_of_int count); ··· 371 387 372 388 let get_playlist_videos t ?(count = 20) ?(start = 0) ~id () = 373 389 let url = 374 - build_url t.base_url [ "api"; "v1"; "video-playlists"; id; "videos" ] 390 + build_url t.base_url 391 + [ "api"; "v1"; "video-playlists"; id; "videos" ] 375 392 [ 376 - Some ("count", string_of_int count); 377 - Some ("start", string_of_int start); 393 + Some ("count", string_of_int count); Some ("start", string_of_int start); 378 394 ] 379 395 in 380 - let result = get_json ~session:t.session ~url playlist_element_paginated_jsont in 396 + let result = 397 + get_json ~session:t.session ~url playlist_element_paginated_jsont 398 + in 381 399 Log.info (fun m -> 382 400 m "Fetched %d videos from playlist (total: %d)" 383 401 (List.length (Peertube_paginated.data result)) ··· 386 404 387 405 let get_account_playlists t ?(count = 20) ?(start = 0) ~handle () = 388 406 let url = 389 - build_url t.base_url [ "api"; "v1"; "accounts"; handle; "video-playlists" ] 407 + build_url t.base_url 408 + [ "api"; "v1"; "accounts"; handle; "video-playlists" ] 390 409 [ 391 - Some ("count", string_of_int count); 392 - Some ("start", string_of_int start); 410 + Some ("count", string_of_int count); Some ("start", string_of_int start); 393 411 ] 394 412 in 395 413 let result = get_json ~session:t.session ~url playlist_paginated_jsont in 396 414 Log.info (fun m -> 397 415 m "Fetched %d playlists from account %s (total: %d)" 398 416 (List.length (Peertube_paginated.data result)) 399 - handle (Peertube_paginated.total result)); 417 + handle 418 + (Peertube_paginated.total result)); 400 419 result 401 420 402 421 (* Server operations *) ··· 422 441 (* Utilities *) 423 442 424 443 let thumbnail_url t video = 425 - match Peertube_video.thumbnail_path video with 426 - | Some path -> Some (t.base_url ^ path) 427 - | None -> None 444 + Option.map 445 + (fun path -> t.base_url ^ path) 446 + (Peertube_video.thumbnail_path video) 428 447 429 448 let download_thumbnail t ~video ~output_path () = 430 449 match thumbnail_url t video with ··· 433 452 m "No thumbnail available for video %s" (Peertube_video.uuid video)); 434 453 Error 435 454 (`Msg 436 - (Printf.sprintf "No thumbnail available for video %s" 437 - (Peertube_video.uuid video))) 455 + (Printf.sprintf "No thumbnail available for video %s" 456 + (Peertube_video.uuid video))) 438 457 | Some url -> 439 458 Log.debug (fun m -> m "Downloading thumbnail from %s" url); 440 459 let response = Requests.get t.session url in ··· 446 465 output_string oc body; 447 466 close_out oc; 448 467 Log.info (fun m -> 449 - m "Downloaded thumbnail for %s to %s" (Peertube_video.uuid video) 468 + m "Downloaded thumbnail for %s to %s" 469 + (Peertube_video.uuid video) 450 470 output_path); 451 471 Ok () 452 472 with exn -> ··· 455 475 (Printexc.to_string exn)); 456 476 Error 457 477 (`Msg 458 - (Printf.sprintf "Failed to write thumbnail: %s" 459 - (Printexc.to_string exn))) 478 + (Printf.sprintf "Failed to write thumbnail: %s" 479 + (Printexc.to_string exn))) 460 480 end 461 481 else begin 462 482 Log.err (fun m -> ··· 466 486 end 467 487 468 488 let to_tuple video = 469 - let description = Option.value ~default:"" (Peertube_video.description video) in 489 + let description = 490 + Option.value ~default:"" (Peertube_video.description video) 491 + in 470 492 let published_date = 471 - Option.value ~default:(Peertube_video.published_at video) 493 + Option.value 494 + ~default:(Peertube_video.published_at video) 472 495 (Peertube_video.originally_published_at video) 473 496 in 474 497 ( description,
+66 -60
lib/peertube_client.mli
··· 1 1 (** PeerTube API client functions. 2 2 3 - This module provides functions for interacting with the PeerTube REST API. *) 3 + This module provides functions for interacting with the PeerTube REST API. 4 + *) 4 5 5 6 (** {1 Session} *) 6 7 8 + type t 7 9 (** A PeerTube API session. 8 10 9 - Encapsulates the HTTP client and base URL for making API requests. 10 - In future, this may also contain authentication credentials. *) 11 - type t 11 + Encapsulates the HTTP client and base URL for making API requests. In 12 + future, this may also contain authentication credentials. *) 12 13 14 + val create : session:Requests.t -> base_url:string -> t 13 15 (** [create ~session ~base_url] creates a new PeerTube API session. 14 16 15 17 @param session HTTP client session from the Requests library 16 - @param base_url Base URL of the PeerTube instance (e.g., "https://framatube.org") *) 17 - val create : session:Requests.t -> base_url:string -> t 18 + @param base_url 19 + Base URL of the PeerTube instance (e.g., "https://framatube.org") *) 18 20 19 - (** [base_url t] returns the base URL of the PeerTube instance. *) 20 21 val base_url : t -> string 22 + (** [base_url t] returns the base URL of the PeerTube instance. *) 21 23 22 - (** [http_session t] returns the underlying HTTP session. *) 23 24 val http_session : t -> Requests.t 25 + (** [http_session t] returns the underlying HTTP session. *) 24 26 25 27 (** {1 Logging and Errors} *) 26 28 27 - (** Log source for the PeerTube client. *) 28 29 val log_src : Logs.src 30 + (** Log source for the PeerTube client. *) 29 31 30 - (** API error with HTTP status code and message. *) 31 32 exception Api_error of int * string 33 + (** API error with HTTP status code and message. *) 32 34 33 35 (** {1 Video Operations} *) 34 36 37 + val list_videos : 38 + t -> 39 + ?count:int -> 40 + ?start:int -> 41 + ?sort:Peertube_video_sort.t -> 42 + ?nsfw:bool -> 43 + ?is_local:bool -> 44 + ?is_live:bool -> 45 + ?category_id:int -> 46 + ?tags:string -> 47 + unit -> 48 + Peertube_video.t Peertube_paginated.t 35 49 (** List videos with optional filtering and sorting. 36 50 37 51 @param count Number of videos per page (default: 20) ··· 42 56 @param is_live Only live videos (default: None = both) 43 57 @param category_id Filter by category ID 44 58 @param tags Filter by tags (comma-separated) *) 45 - val list_videos : 59 + 60 + val search_videos : 46 61 t -> 62 + query:string -> 47 63 ?count:int -> 48 64 ?start:int -> 49 65 ?sort:Peertube_video_sort.t -> 50 - ?nsfw:bool -> 51 - ?is_local:bool -> 52 - ?is_live:bool -> 53 - ?category_id:int -> 54 - ?tags:string -> 66 + ?search_target:[ `Local | `Search_index ] -> 67 + ?duration_min:int -> 68 + ?duration_max:int -> 69 + ?published_after:Ptime.t -> 70 + ?published_before:Ptime.t -> 55 71 unit -> 56 72 Peertube_video.t Peertube_paginated.t 57 - 58 73 (** Search for videos. 59 74 60 75 @param query Search query string ··· 66 81 @param duration_max Maximum duration in seconds 67 82 @param published_after Only videos published after this date 68 83 @param published_before Only videos published before this date *) 69 - val search_videos : 70 - t -> 71 - query:string -> 72 - ?count:int -> 73 - ?start:int -> 74 - ?sort:Peertube_video_sort.t -> 75 - ?search_target:[ `Local | `Search_index ] -> 76 - ?duration_min:int -> 77 - ?duration_max:int -> 78 - ?published_after:Ptime.t -> 79 - ?published_before:Ptime.t -> 80 - unit -> 81 - Peertube_video.t Peertube_paginated.t 82 84 83 - (** Fetch videos from a channel with pagination. *) 84 85 val fetch_channel_videos : 85 86 t -> 86 87 ?count:int -> ··· 88 89 channel:string -> 89 90 unit -> 90 91 Peertube_video.t Peertube_paginated.t 92 + (** Fetch videos from a channel with pagination. *) 91 93 92 - (** Fetch all videos from a channel using automatic pagination. *) 93 94 val fetch_all_channel_videos : 94 95 t -> 95 96 ?page_size:int -> ··· 97 98 channel:string -> 98 99 unit -> 99 100 Peertube_video.t list 101 + (** Fetch all videos from a channel using automatic pagination. *) 100 102 101 - (** Fetch detailed information for a single video by UUID. *) 102 103 val fetch_video_details : t -> uuid:string -> unit -> Peertube_video.t 104 + (** Fetch detailed information for a single video by UUID. *) 103 105 104 - (** Get available video categories. *) 105 106 val get_categories : t -> unit -> (int * string) list 107 + (** Get available video categories. *) 106 108 107 - (** Get available video licences. *) 108 109 val get_licences : t -> unit -> (int * string) list 110 + (** Get available video licences. *) 109 111 110 - (** Get available video languages. *) 111 112 val get_languages : t -> unit -> (string * string) list 113 + (** Get available video languages. *) 112 114 113 115 (** {1 Channel Operations} *) 114 116 115 - (** List all channels on the instance. 116 - 117 - @param count Number per page (default: 20) 118 - @param start Starting offset (default: 0) 119 - @param sort Sort order: createdAt, -createdAt, etc. *) 120 117 val list_channels : 121 118 t -> 122 119 ?count:int -> ··· 124 121 ?sort:string -> 125 122 unit -> 126 123 Peertube_channel.t Peertube_paginated.t 124 + (** List all channels on the instance. 127 125 128 - (** Search for channels. 126 + @param count Number per page (default: 20) 127 + @param start Starting offset (default: 0) 128 + @param sort Sort order: createdAt, -createdAt, etc. *) 129 129 130 - @param query Search query string *) 131 130 val search_channels : 132 131 t -> 133 132 query:string -> ··· 135 134 ?start:int -> 136 135 unit -> 137 136 Peertube_channel.t Peertube_paginated.t 137 + (** Search for channels. 138 138 139 - (** Get details for a specific channel. *) 139 + @param query Search query string *) 140 + 140 141 val get_channel : t -> handle:string -> unit -> Peertube_channel.t 142 + (** Get details for a specific channel. *) 141 143 142 144 (** {1 Account Operations} *) 143 145 144 - (** List accounts on the instance. *) 145 146 val list_accounts : 146 147 t -> 147 148 ?count:int -> ··· 149 150 ?sort:string -> 150 151 unit -> 151 152 Peertube_account.t Peertube_paginated.t 153 + (** List accounts on the instance. *) 152 154 153 - (** Get details for a specific account. *) 154 155 val get_account : t -> handle:string -> unit -> Peertube_account.t 156 + (** Get details for a specific account. *) 155 157 156 - (** Get videos from a specific account. *) 157 158 val get_account_videos : 158 159 t -> 159 160 ?count:int -> ··· 161 162 handle:string -> 162 163 unit -> 163 164 Peertube_video.t Peertube_paginated.t 165 + (** Get videos from a specific account. *) 164 166 165 - (** Get channels owned by an account. *) 166 167 val get_account_channels : 167 168 t -> 168 169 ?count:int -> ··· 170 171 handle:string -> 171 172 unit -> 172 173 Peertube_channel.t Peertube_paginated.t 174 + (** Get channels owned by an account. *) 173 175 174 176 (** {1 Playlist Operations} *) 175 177 176 - (** List playlists on the instance. *) 177 178 val list_playlists : 178 - t -> ?count:int -> ?start:int -> unit -> Peertube_playlist.t Peertube_paginated.t 179 + t -> 180 + ?count:int -> 181 + ?start:int -> 182 + unit -> 183 + Peertube_playlist.t Peertube_paginated.t 184 + (** List playlists on the instance. *) 179 185 180 - (** Search for playlists. *) 181 186 val search_playlists : 182 187 t -> 183 188 query:string -> ··· 185 190 ?start:int -> 186 191 unit -> 187 192 Peertube_playlist.t Peertube_paginated.t 193 + (** Search for playlists. *) 188 194 189 - (** Get details for a specific playlist. *) 190 195 val get_playlist : t -> id:string -> unit -> Peertube_playlist.t 196 + (** Get details for a specific playlist. *) 191 197 192 - (** Get videos in a playlist. *) 193 198 val get_playlist_videos : 194 199 t -> 195 200 ?count:int -> ··· 197 202 id:string -> 198 203 unit -> 199 204 Peertube_playlist_element.t Peertube_paginated.t 205 + (** Get videos in a playlist. *) 200 206 201 - (** Get playlists owned by an account. *) 202 207 val get_account_playlists : 203 208 t -> 204 209 ?count:int -> ··· 206 211 handle:string -> 207 212 unit -> 208 213 Peertube_playlist.t Peertube_paginated.t 214 + (** Get playlists owned by an account. *) 209 215 210 216 (** {1 Server Operations} *) 211 217 212 - (** Get server configuration. *) 213 218 val get_config : t -> unit -> Peertube_server_config.t 219 + (** Get server configuration. *) 214 220 215 - (** Get server statistics. *) 216 221 val get_stats : t -> unit -> Peertube_server_stats.t 222 + (** Get server statistics. *) 217 223 218 224 (** {1 Utilities} *) 219 225 226 + val thumbnail_url : t -> Peertube_video.t -> string option 220 227 (** Get the full thumbnail URL for a video. *) 221 - val thumbnail_url : t -> Peertube_video.t -> string option 222 228 223 - (** Download a video's thumbnail to a file. *) 224 229 val download_thumbnail : 225 230 t -> 226 231 video:Peertube_video.t -> 227 232 output_path:string -> 228 233 unit -> 229 234 (unit, [ `Msg of string ]) result 235 + (** Download a video's thumbnail to a file. *) 230 236 231 - (** Convert a video to a tuple for external use. 232 - Returns [(description, published_date, title, url, uuid, id_string)]. *) 233 237 val to_tuple : 234 238 Peertube_video.t -> string * Ptime.t * string * string * string * string 239 + (** Convert a video to a tuple for external use. Returns 240 + [(description, published_date, title, url, uuid, id_string)]. *)
+25 -12
lib/peertube_instance_info.ml
··· 12 12 13 13 let make ~name ~short_description ~description ~terms ~is_nsfw 14 14 ~default_nsfw_policy ~default_client_route = 15 - { name; short_description; description; terms; is_nsfw; default_nsfw_policy; 16 - default_client_route } 15 + { 16 + name; 17 + short_description; 18 + description; 19 + terms; 20 + is_nsfw; 21 + default_nsfw_policy; 22 + default_client_route; 23 + } 17 24 18 25 let name t = t.name 19 26 let short_description t = t.short_description ··· 26 33 let jsont : t Jsont.t = 27 34 let make name short_description description terms is_nsfw default_nsfw_policy 28 35 default_client_route = 29 - { name; short_description; description; terms; is_nsfw; default_nsfw_policy; 30 - default_client_route } 36 + { 37 + name; 38 + short_description; 39 + description; 40 + terms; 41 + is_nsfw; 42 + default_nsfw_policy; 43 + default_client_route; 44 + } 31 45 in 32 46 Jsont.Object.map ~kind:"instance_info" make 33 47 |> Jsont.Object.mem "name" Jsont.string ~enc:(fun i -> i.name) 34 48 |> Jsont.Object.mem "shortDescription" Jsont.string ~dec_absent:"" 35 49 ~enc:(fun i -> i.short_description) 36 - |> Jsont.Object.mem "description" (Jsont.option Jsont.string) ~dec_absent:None 37 - ~enc_omit:Option.is_none ~enc:(fun i -> i.description) 38 - |> Jsont.Object.mem "terms" (Jsont.option Jsont.string) ~dec_absent:None 39 - ~enc_omit:Option.is_none ~enc:(fun i -> i.terms) 40 - |> Jsont.Object.mem "isNSFW" Jsont.bool ~dec_absent:false 41 - ~enc:(fun i -> i.is_nsfw) 50 + |> Jsont.Object.mem "description" (Jsont.option Jsont.string) 51 + ~dec_absent:None ~enc_omit:Option.is_none ~enc:(fun i -> i.description) 52 + |> Jsont.Object.mem "terms" (Jsont.option Jsont.string) 53 + ~dec_absent:None ~enc_omit:Option.is_none ~enc:(fun i -> i.terms) 54 + |> Jsont.Object.mem "isNSFW" Jsont.bool ~dec_absent:false ~enc:(fun i -> 55 + i.is_nsfw) 42 56 |> Jsont.Object.mem "defaultNSFWPolicy" Jsont.string ~dec_absent:"display" 43 57 ~enc:(fun i -> i.default_nsfw_policy) 44 58 |> Jsont.Object.mem "defaultClientRoute" Jsont.string 45 59 ~dec_absent:"/videos/trending" ~enc:(fun i -> i.default_client_route) 46 - |> Jsont.Object.skip_unknown 47 - |> Jsont.Object.finish 60 + |> Jsont.Object.skip_unknown |> Jsont.Object.finish 48 61 49 62 let pp ppf t = 50 63 Fmt.pf ppf "@[<hov 2>{ name = %S;@ short_description = %S;@ is_nsfw = %b }@]"
+11 -11
lib/peertube_instance_info.mli
··· 1 1 (** Basic PeerTube server/instance information. *) 2 2 3 - (** Instance info type. *) 4 3 type t 4 + (** Instance info type. *) 5 5 6 - (** Create instance info. *) 7 6 val make : 8 7 name:string -> 9 8 short_description:string -> ··· 13 12 default_nsfw_policy:string -> 14 13 default_client_route:string -> 15 14 t 15 + (** Create instance info. *) 16 16 17 + val name : t -> string 17 18 (** Instance name. *) 18 - val name : t -> string 19 19 20 - (** Short description. *) 21 20 val short_description : t -> string 21 + (** Short description. *) 22 22 23 + val description : t -> string option 23 24 (** Full description. *) 24 - val description : t -> string option 25 25 26 - (** Terms of service. *) 27 26 val terms : t -> string option 27 + (** Terms of service. *) 28 28 29 + val is_nsfw : t -> bool 29 30 (** Whether the instance is NSFW-focused. *) 30 - val is_nsfw : t -> bool 31 31 32 - (** Default NSFW display policy. *) 33 32 val default_nsfw_policy : t -> string 33 + (** Default NSFW display policy. *) 34 34 35 + val default_client_route : t -> string 35 36 (** Default client route. *) 36 - val default_client_route : t -> string 37 37 38 + val jsont : t Jsont.t 38 39 (** JSON codec. *) 39 - val jsont : t Jsont.t 40 40 41 - (** Pretty printer. *) 42 41 val pp : Format.formatter -> t -> unit 42 + (** Pretty printer. *)
+2 -4
lib/peertube_labeled.ml
··· 11 11 Jsont.Object.map ~kind:"int_labeled" make 12 12 |> Jsont.Object.mem "id" Jsont.int ~enc:(fun l -> l.id) 13 13 |> Jsont.Object.mem "label" Jsont.string ~enc:(fun l -> l.label) 14 - |> Jsont.Object.skip_unknown 15 - |> Jsont.Object.finish 14 + |> Jsont.Object.skip_unknown |> Jsont.Object.finish 16 15 17 16 let string_jsont : string t Jsont.t = 18 17 let make id label = { id; label } in 19 18 Jsont.Object.map ~kind:"string_labeled" make 20 19 |> Jsont.Object.mem "id" Jsont.string ~enc:(fun l -> l.id) 21 20 |> Jsont.Object.mem "label" Jsont.string ~enc:(fun l -> l.label) 22 - |> Jsont.Object.skip_unknown 23 - |> Jsont.Object.finish 21 + |> Jsont.Object.skip_unknown |> Jsont.Object.finish 24 22 25 23 let pp pp_id ppf t = 26 24 Fmt.pf ppf "@[<hov 2>{ id = %a;@ label = %S }@]" pp_id t.id t.label
+9 -9
lib/peertube_labeled.mli
··· 1 1 (** Labeled values with an ID and display label. 2 2 3 - Used for categories, licences, languages, and other enumerated values 4 - in the PeerTube API. *) 3 + Used for categories, licences, languages, and other enumerated values in the 4 + PeerTube API. *) 5 5 6 + type 'a t 6 7 (** A labeled value with an ID and human-readable label. *) 7 - type 'a t 8 8 9 - (** Create a labeled value. *) 10 9 val make : id:'a -> label:string -> 'a t 10 + (** Create a labeled value. *) 11 11 12 - (** Get the ID. *) 13 12 val id : 'a t -> 'a 13 + (** Get the ID. *) 14 14 15 + val label : 'a t -> string 15 16 (** Get the label. *) 16 - val label : 'a t -> string 17 17 18 - (** JSON codec for labeled values with int IDs. *) 19 18 val int_jsont : int t Jsont.t 19 + (** JSON codec for labeled values with int IDs. *) 20 20 21 - (** JSON codec for labeled values with string IDs. *) 22 21 val string_jsont : string t Jsont.t 22 + (** JSON codec for labeled values with string IDs. *) 23 23 24 - (** Pretty printer for labeled values. *) 25 24 val pp : (Format.formatter -> 'a -> unit) -> Format.formatter -> 'a t -> unit 25 + (** Pretty printer for labeled values. *)
+1 -2
lib/peertube_paginated.ml
··· 11 11 Jsont.Object.map ~kind make 12 12 |> Jsont.Object.mem "total" Jsont.int ~enc:(fun r -> r.total) 13 13 |> Jsont.Object.mem "data" (Jsont.list item_jsont) ~enc:(fun r -> r.data) 14 - |> Jsont.Object.skip_unknown 15 - |> Jsont.Object.finish 14 + |> Jsont.Object.skip_unknown |> Jsont.Object.finish 16 15 17 16 let pp pp_item ppf t = 18 17 Fmt.pf ppf "@[<hov 2>{ total = %d;@ data = %a }@]" t.total
+6 -6
lib/peertube_paginated.mli
··· 1 1 (** Paginated API responses. *) 2 2 3 - (** A paginated response containing total count and data list. *) 4 3 type 'a t 4 + (** A paginated response containing total count and data list. *) 5 5 6 - (** Create a paginated response. *) 7 6 val make : total:int -> data:'a list -> 'a t 7 + (** Create a paginated response. *) 8 8 9 + val total : 'a t -> int 9 10 (** Total number of items available. *) 10 - val total : 'a t -> int 11 11 12 - (** Items in this page. *) 13 12 val data : 'a t -> 'a list 13 + (** Items in this page. *) 14 14 15 - (** Create a JSON codec for paginated responses. *) 16 15 val jsont : kind:string -> 'a Jsont.t -> 'a t Jsont.t 16 + (** Create a JSON codec for paginated responses. *) 17 17 18 + val pp : (Format.formatter -> 'a -> unit) -> Format.formatter -> 'a t -> unit 18 19 (** Pretty printer for paginated responses. *) 19 - val pp : (Format.formatter -> 'a -> unit) -> Format.formatter -> 'a t -> unit
+51 -22
lib/peertube_playlist.ml
··· 20 20 let make ~id ~uuid ~short_uuid ~display_name ~description ~privacy ~url 21 21 ~thumbnail_path ~videos_length ~playlist_type ~created_at ~updated_at 22 22 ~owner_account ~video_channel = 23 - { id; uuid; short_uuid; display_name; description; privacy; url; 24 - thumbnail_path; videos_length; playlist_type; created_at; updated_at; 25 - owner_account; video_channel } 23 + { 24 + id; 25 + uuid; 26 + short_uuid; 27 + display_name; 28 + description; 29 + privacy; 30 + url; 31 + thumbnail_path; 32 + videos_length; 33 + playlist_type; 34 + created_at; 35 + updated_at; 36 + owner_account; 37 + video_channel; 38 + } 26 39 27 40 let id t = t.id 28 41 let uuid t = t.uuid ··· 43 56 let make id uuid short_uuid display_name description privacy url 44 57 thumbnail_path videos_length playlist_type created_at updated_at 45 58 owner_account video_channel = 46 - { id; uuid; short_uuid; display_name; description; privacy; url; 47 - thumbnail_path; videos_length; playlist_type; created_at; updated_at; 48 - owner_account; video_channel } 59 + { 60 + id; 61 + uuid; 62 + short_uuid; 63 + display_name; 64 + description; 65 + privacy; 66 + url; 67 + thumbnail_path; 68 + videos_length; 69 + playlist_type; 70 + created_at; 71 + updated_at; 72 + owner_account; 73 + video_channel; 74 + } 49 75 in 50 76 Jsont.Object.map ~kind:"playlist" make 51 77 |> Jsont.Object.mem "id" Jsont.int ~enc:(fun p -> p.id) 52 78 |> Jsont.Object.mem "uuid" Jsont.string ~enc:(fun p -> p.uuid) 53 - |> Jsont.Object.mem "shortUUID" (Jsont.option Jsont.string) ~dec_absent:None 54 - ~enc_omit:Option.is_none ~enc:(fun p -> p.short_uuid) 79 + |> Jsont.Object.mem "shortUUID" (Jsont.option Jsont.string) 80 + ~dec_absent:None ~enc_omit:Option.is_none ~enc:(fun p -> p.short_uuid) 55 81 |> Jsont.Object.mem "displayName" Jsont.string ~enc:(fun p -> p.display_name) 56 - |> Jsont.Object.mem "description" (Jsont.option Jsont.string) ~dec_absent:None 57 - ~enc_omit:Option.is_none ~enc:(fun p -> p.description) 58 - |> Jsont.Object.mem "privacy" Peertube_playlist_privacy.jsont 59 - ~enc:(fun p -> p.privacy) 82 + |> Jsont.Object.mem "description" (Jsont.option Jsont.string) 83 + ~dec_absent:None ~enc_omit:Option.is_none ~enc:(fun p -> p.description) 84 + |> Jsont.Object.mem "privacy" Peertube_playlist_privacy.jsont ~enc:(fun p -> 85 + p.privacy) 60 86 |> Jsont.Object.mem "url" Jsont.string ~enc:(fun p -> p.url) 61 87 |> Jsont.Object.mem "thumbnailPath" (Jsont.option Jsont.string) 62 - ~dec_absent:None ~enc_omit:Option.is_none ~enc:(fun p -> p.thumbnail_path) 63 - |> Jsont.Object.mem "videosLength" Jsont.int ~dec_absent:0 64 - ~enc:(fun p -> p.videos_length) 65 - |> Jsont.Object.mem "type" Peertube_playlist_type.jsont 66 - ~enc:(fun p -> p.playlist_type) 67 - |> Jsont.Object.mem "createdAt" Peertube_ptime.jsont ~enc:(fun p -> p.created_at) 68 - |> Jsont.Object.mem "updatedAt" Peertube_ptime.jsont ~enc:(fun p -> p.updated_at) 88 + ~dec_absent:None ~enc_omit:Option.is_none ~enc:(fun p -> 89 + p.thumbnail_path) 90 + |> Jsont.Object.mem "videosLength" Jsont.int ~dec_absent:0 ~enc:(fun p -> 91 + p.videos_length) 92 + |> Jsont.Object.mem "type" Peertube_playlist_type.jsont ~enc:(fun p -> 93 + p.playlist_type) 94 + |> Jsont.Object.mem "createdAt" Peertube_ptime.jsont ~enc:(fun p -> 95 + p.created_at) 96 + |> Jsont.Object.mem "updatedAt" Peertube_ptime.jsont ~enc:(fun p -> 97 + p.updated_at) 69 98 |> Jsont.Object.mem "ownerAccount" 70 99 (Jsont.option Peertube_account_summary.jsont) 71 100 ~dec_absent:None ~enc_omit:Option.is_none ~enc:(fun p -> p.owner_account) 72 101 |> Jsont.Object.mem "videoChannel" 73 102 (Jsont.option Peertube_channel_summary.jsont) 74 103 ~dec_absent:None ~enc_omit:Option.is_none ~enc:(fun p -> p.video_channel) 75 - |> Jsont.Object.skip_unknown 76 - |> Jsont.Object.finish 104 + |> Jsont.Object.skip_unknown |> Jsont.Object.finish 77 105 78 106 let pp ppf t = 79 107 Fmt.pf ppf 80 - "@[<hov 2>{ id = %d;@ uuid = %S;@ display_name = %S;@ videos_length = %d }@]" 108 + "@[<hov 2>{ id = %d;@ uuid = %S;@ display_name = %S;@ videos_length = %d \ 109 + }@]" 81 110 t.id t.uuid t.display_name t.videos_length
+18 -18
lib/peertube_playlist.mli
··· 1 1 (** PeerTube video playlist. *) 2 2 3 - (** Playlist type. *) 4 3 type t 4 + (** Playlist type. *) 5 5 6 - (** Create a playlist. *) 7 6 val make : 8 7 id:int -> 9 8 uuid:string -> ··· 20 19 owner_account:Peertube_account_summary.t option -> 21 20 video_channel:Peertube_channel_summary.t option -> 22 21 t 22 + (** Create a playlist. *) 23 23 24 + val id : t -> int 24 25 (** Playlist ID. *) 25 - val id : t -> int 26 26 27 - (** Playlist UUID. *) 28 27 val uuid : t -> string 28 + (** Playlist UUID. *) 29 29 30 - (** Short UUID. *) 31 30 val short_uuid : t -> string option 31 + (** Short UUID. *) 32 32 33 + val display_name : t -> string 33 34 (** Display name. *) 34 - val display_name : t -> string 35 35 36 - (** Description. *) 37 36 val description : t -> string option 37 + (** Description. *) 38 38 39 - (** Privacy level. *) 40 39 val privacy : t -> Peertube_playlist_privacy.t 40 + (** Privacy level. *) 41 41 42 - (** Playlist URL. *) 43 42 val url : t -> string 43 + (** Playlist URL. *) 44 44 45 - (** Thumbnail path (relative). *) 46 45 val thumbnail_path : t -> string option 46 + (** Thumbnail path (relative). *) 47 47 48 + val videos_length : t -> int 48 49 (** Number of videos in playlist. *) 49 - val videos_length : t -> int 50 50 51 + val playlist_type : t -> Peertube_playlist_type.t 51 52 (** Playlist type. *) 52 - val playlist_type : t -> Peertube_playlist_type.t 53 53 54 - (** Creation timestamp. *) 55 54 val created_at : t -> Ptime.t 55 + (** Creation timestamp. *) 56 56 57 - (** Last update timestamp. *) 58 57 val updated_at : t -> Ptime.t 58 + (** Last update timestamp. *) 59 59 60 + val owner_account : t -> Peertube_account_summary.t option 60 61 (** Owner account. *) 61 - val owner_account : t -> Peertube_account_summary.t option 62 62 63 - (** Associated video channel. *) 64 63 val video_channel : t -> Peertube_channel_summary.t option 64 + (** Associated video channel. *) 65 65 66 - (** JSON codec. *) 67 66 val jsont : t Jsont.t 67 + (** JSON codec. *) 68 68 69 - (** Pretty printer. *) 70 69 val pp : Format.formatter -> t -> unit 70 + (** Pretty printer. *)
+1 -2
lib/peertube_playlist_element.ml
··· 30 30 ~enc_omit:Option.is_none ~enc:(fun e -> e.stop_timestamp) 31 31 |> Jsont.Object.mem "video" (Jsont.option Peertube_video.jsont) 32 32 ~dec_absent:None ~enc_omit:Option.is_none ~enc:(fun e -> e.video) 33 - |> Jsont.Object.skip_unknown 34 - |> Jsont.Object.finish 33 + |> Jsont.Object.skip_unknown |> Jsont.Object.finish 35 34 36 35 let pp ppf t = 37 36 Fmt.pf ppf "@[<hov 2>{ id = %d;@ position = %d;@ video = %a }@]" t.id
+9 -9
lib/peertube_playlist_element.mli
··· 1 1 (** An element in a PeerTube playlist. *) 2 2 3 - (** Playlist element type. *) 4 3 type t 4 + (** Playlist element type. *) 5 5 6 - (** Create a playlist element. *) 7 6 val make : 8 7 id:int -> 9 8 position:int -> ··· 11 10 stop_timestamp:int option -> 12 11 video:Peertube_video.t option -> 13 12 t 13 + (** Create a playlist element. *) 14 14 15 + val id : t -> int 15 16 (** Element ID. *) 16 - val id : t -> int 17 17 18 - (** Position in playlist (0-indexed). *) 19 18 val position : t -> int 19 + (** Position in playlist (0-indexed). *) 20 20 21 - (** Start timestamp in seconds. *) 22 21 val start_timestamp : t -> int option 22 + (** Start timestamp in seconds. *) 23 23 24 + val stop_timestamp : t -> int option 24 25 (** Stop timestamp in seconds. *) 25 - val stop_timestamp : t -> int option 26 26 27 - (** The video. *) 28 27 val video : t -> Peertube_video.t option 28 + (** The video. *) 29 29 30 - (** JSON codec. *) 31 30 val jsont : t Jsont.t 31 + (** JSON codec. *) 32 32 33 - (** Pretty printer. *) 34 33 val pp : Format.formatter -> t -> unit 34 + (** Pretty printer. *)
+1 -2
lib/peertube_playlist_privacy.ml
··· 10 10 Jsont.Object.map ~kind:"playlist_privacy" make 11 11 |> Jsont.Object.mem "id" Jsont.int ~enc:to_int 12 12 |> Jsont.Object.mem "label" Jsont.string ~dec_absent:"" ~enc:(Fun.const "") 13 - |> Jsont.Object.skip_unknown 14 - |> Jsont.Object.finish 13 + |> Jsont.Object.skip_unknown |> Jsont.Object.finish 15 14 16 15 let pp ppf = function 17 16 | Public -> Fmt.string ppf "Public"
+4 -4
lib/peertube_playlist_privacy.mli
··· 3 3 (** Playlist privacy level. *) 4 4 type t = Public | Unlisted | Private 5 5 6 - (** Convert privacy to its integer representation. *) 7 6 val to_int : t -> int 7 + (** Convert privacy to its integer representation. *) 8 8 9 - (** Convert integer to privacy level. *) 10 9 val of_int : int -> t 10 + (** Convert integer to privacy level. *) 11 11 12 - (** JSON codec for playlist privacy. *) 13 12 val jsont : t Jsont.t 13 + (** JSON codec for playlist privacy. *) 14 14 15 - (** Pretty printer for playlist privacy. *) 16 15 val pp : Format.formatter -> t -> unit 16 + (** Pretty printer for playlist privacy. *)
+1 -2
lib/peertube_playlist_type.ml
··· 10 10 Jsont.Object.map ~kind:"playlist_type" make 11 11 |> Jsont.Object.mem "id" Jsont.int ~enc:to_int 12 12 |> Jsont.Object.mem "label" Jsont.string ~dec_absent:"" ~enc:(Fun.const "") 13 - |> Jsont.Object.skip_unknown 14 - |> Jsont.Object.finish 13 + |> Jsont.Object.skip_unknown |> Jsont.Object.finish 15 14 16 15 let pp ppf = function 17 16 | Regular -> Fmt.string ppf "Regular"
+4 -4
lib/peertube_playlist_type.mli
··· 3 3 (** Playlist type. *) 4 4 type t = Regular | WatchLater 5 5 6 - (** Convert type to its integer representation. *) 7 6 val to_int : t -> int 7 + (** Convert type to its integer representation. *) 8 8 9 - (** Convert integer to playlist type. *) 10 9 val of_int : int -> t 10 + (** Convert integer to playlist type. *) 11 11 12 - (** JSON codec for playlist type. *) 13 12 val jsont : t Jsont.t 13 + (** JSON codec for playlist type. *) 14 14 15 - (** Pretty printer for playlist type. *) 16 15 val pp : Format.formatter -> t -> unit 16 + (** Pretty printer for playlist type. *)
+6 -3
lib/peertube_privacy.ml
··· 2 2 3 3 type t = Public | Unlisted | Private | Internal 4 4 5 - let to_int = function Public -> 1 | Unlisted -> 2 | Private -> 3 | Internal -> 4 5 + let to_int = function 6 + | Public -> 1 7 + | Unlisted -> 2 8 + | Private -> 3 9 + | Internal -> 4 6 10 7 11 let of_int = function 8 12 | 1 -> Public ··· 16 20 Jsont.Object.map ~kind:"privacy" make 17 21 |> Jsont.Object.mem "id" Jsont.int ~enc:to_int 18 22 |> Jsont.Object.mem "label" Jsont.string ~dec_absent:"" ~enc:(Fun.const "") 19 - |> Jsont.Object.skip_unknown 20 - |> Jsont.Object.finish 23 + |> Jsont.Object.skip_unknown |> Jsont.Object.finish 21 24 22 25 let pp ppf = function 23 26 | Public -> Fmt.string ppf "Public"
+4 -4
lib/peertube_privacy.mli
··· 3 3 (** Video privacy level. *) 4 4 type t = Public | Unlisted | Private | Internal 5 5 6 - (** Convert privacy to its integer representation. *) 7 6 val to_int : t -> int 7 + (** Convert privacy to its integer representation. *) 8 8 9 - (** Convert integer to privacy level. *) 10 9 val of_int : int -> t 10 + (** Convert integer to privacy level. *) 11 11 12 - (** JSON codec for privacy. *) 13 12 val jsont : t Jsont.t 13 + (** JSON codec for privacy. *) 14 14 15 - (** Pretty printer for privacy. *) 16 15 val pp : Format.formatter -> t -> unit 16 + (** Pretty printer for privacy. *)
+2 -2
lib/peertube_ptime.mli
··· 1 1 (** Ptime JSON codec for RFC3339 date strings. *) 2 2 3 - (** JSON codec for Ptime.t values encoded as RFC3339 strings. *) 4 3 val jsont : Ptime.t Jsont.t 4 + (** JSON codec for Ptime.t values encoded as RFC3339 strings. *) 5 5 6 - (** Pretty printer for Ptime.t values. *) 7 6 val pp : Format.formatter -> Ptime.t -> unit 7 + (** Pretty printer for Ptime.t values. *)
+42 -30
lib/peertube_server_config.ml
··· 14 14 let make ~instance ~server_version ~server_commit ~signup_allowed 15 15 ~signup_allowed_for_current_ip ~signup_requires_email_verification 16 16 ~transcoding_enabled ~contact_form_enabled = 17 - { instance; server_version; server_commit; signup_allowed; 18 - signup_allowed_for_current_ip; signup_requires_email_verification; 19 - transcoding_enabled; contact_form_enabled } 17 + { 18 + instance; 19 + server_version; 20 + server_commit; 21 + signup_allowed; 22 + signup_allowed_for_current_ip; 23 + signup_requires_email_verification; 24 + transcoding_enabled; 25 + contact_form_enabled; 26 + } 20 27 21 28 let instance t = t.instance 22 29 let server_version t = t.server_version ··· 38 45 ~enc:(fun (_, a, _) -> a) 39 46 |> Jsont.Object.mem "requiresEmailVerification" Jsont.bool ~dec_absent:false 40 47 ~enc:(fun (_, _, r) -> r) 41 - |> Jsont.Object.skip_unknown 42 - |> Jsont.Object.finish 48 + |> Jsont.Object.skip_unknown |> Jsont.Object.finish 43 49 44 50 let transcoding_jsont = 45 51 Jsont.Object.map ~kind:"transcoding" Fun.id 46 52 |> Jsont.Object.mem "enabled" Jsont.bool ~dec_absent:false ~enc:Fun.id 47 - |> Jsont.Object.skip_unknown 48 - |> Jsont.Object.finish 53 + |> Jsont.Object.skip_unknown |> Jsont.Object.finish 49 54 50 55 let contact_form_jsont = 51 56 Jsont.Object.map ~kind:"contact_form" Fun.id 52 57 |> Jsont.Object.mem "enabled" Jsont.bool ~dec_absent:false ~enc:Fun.id 53 - |> Jsont.Object.skip_unknown 54 - |> Jsont.Object.finish 58 + |> Jsont.Object.skip_unknown |> Jsont.Object.finish 55 59 56 60 let jsont : t Jsont.t = 57 - let make instance server_version server_commit signup transcoding 58 - contact_form = 59 - let signup_allowed, signup_allowed_for_current_ip, 60 - signup_requires_email_verification = signup 61 + let make instance server_version server_commit signup transcoding contact_form 62 + = 63 + let ( signup_allowed, 64 + signup_allowed_for_current_ip, 65 + signup_requires_email_verification ) = 66 + signup 61 67 in 62 - { instance; server_version; server_commit; signup_allowed; 63 - signup_allowed_for_current_ip; signup_requires_email_verification; 64 - transcoding_enabled = transcoding; contact_form_enabled = contact_form } 68 + { 69 + instance; 70 + server_version; 71 + server_commit; 72 + signup_allowed; 73 + signup_allowed_for_current_ip; 74 + signup_requires_email_verification; 75 + transcoding_enabled = transcoding; 76 + contact_form_enabled = contact_form; 77 + } 65 78 in 66 79 Jsont.Object.map ~kind:"server_config" make 67 - |> Jsont.Object.mem "instance" Peertube_instance_info.jsont 68 - ~enc:(fun c -> c.instance) 80 + |> Jsont.Object.mem "instance" Peertube_instance_info.jsont ~enc:(fun c -> 81 + c.instance) 69 82 |> Jsont.Object.mem "serverVersion" Jsont.string ~dec_absent:"unknown" 70 83 ~enc:(fun c -> c.server_version) 71 - |> Jsont.Object.mem "serverCommit" (Jsont.option Jsont.string) ~dec_absent:None 72 - ~enc_omit:Option.is_none ~enc:(fun c -> c.server_commit) 73 - |> Jsont.Object.mem "signup" signup_jsont 74 - ~enc:(fun c -> 75 - (c.signup_allowed, c.signup_allowed_for_current_ip, 76 - c.signup_requires_email_verification)) 77 - |> Jsont.Object.mem "transcoding" transcoding_jsont 78 - ~enc:(fun c -> c.transcoding_enabled) 79 - |> Jsont.Object.mem "contactForm" contact_form_jsont 80 - ~enc:(fun c -> c.contact_form_enabled) 81 - |> Jsont.Object.skip_unknown 82 - |> Jsont.Object.finish 84 + |> Jsont.Object.mem "serverCommit" (Jsont.option Jsont.string) 85 + ~dec_absent:None ~enc_omit:Option.is_none ~enc:(fun c -> c.server_commit) 86 + |> Jsont.Object.mem "signup" signup_jsont ~enc:(fun c -> 87 + ( c.signup_allowed, 88 + c.signup_allowed_for_current_ip, 89 + c.signup_requires_email_verification )) 90 + |> Jsont.Object.mem "transcoding" transcoding_jsont ~enc:(fun c -> 91 + c.transcoding_enabled) 92 + |> Jsont.Object.mem "contactForm" contact_form_jsont ~enc:(fun c -> 93 + c.contact_form_enabled) 94 + |> Jsont.Object.skip_unknown |> Jsont.Object.finish 83 95 84 96 let pp ppf t = 85 97 Fmt.pf ppf
+12 -12
lib/peertube_server_config.mli
··· 1 1 (** PeerTube server configuration. *) 2 2 3 - (** Server config type. *) 4 3 type t 4 + (** Server config type. *) 5 5 6 - (** Create server config. *) 7 6 val make : 8 7 instance:Peertube_instance_info.t -> 9 8 server_version:string -> ··· 14 13 transcoding_enabled:bool -> 15 14 contact_form_enabled:bool -> 16 15 t 16 + (** Create server config. *) 17 17 18 + val instance : t -> Peertube_instance_info.t 18 19 (** Instance information. *) 19 - val instance : t -> Peertube_instance_info.t 20 20 21 - (** Server software version. *) 22 21 val server_version : t -> string 22 + (** Server software version. *) 23 23 24 - (** Server commit hash. *) 25 24 val server_commit : t -> string option 25 + (** Server commit hash. *) 26 26 27 + val signup_allowed : t -> bool 27 28 (** Whether signup is allowed. *) 28 - val signup_allowed : t -> bool 29 29 30 - (** Whether signup is allowed for current IP. *) 31 30 val signup_allowed_for_current_ip : t -> bool 31 + (** Whether signup is allowed for current IP. *) 32 32 33 - (** Whether signup requires email verification. *) 34 33 val signup_requires_email_verification : t -> bool 34 + (** Whether signup requires email verification. *) 35 35 36 + val transcoding_enabled : t -> bool 36 37 (** Whether transcoding is enabled. *) 37 - val transcoding_enabled : t -> bool 38 38 39 - (** Whether contact form is enabled. *) 40 39 val contact_form_enabled : t -> bool 40 + (** Whether contact form is enabled. *) 41 41 42 - (** JSON codec. *) 43 42 val jsont : t Jsont.t 43 + (** JSON codec. *) 44 44 45 + val pp : Format.formatter -> t -> unit 45 46 (** Pretty printer. *) 46 - val pp : Format.formatter -> t -> unit
+41 -19
lib/peertube_server_stats.ml
··· 22 22 ~total_local_video_comments ~total_local_video_files_size ~total_videos 23 23 ~total_video_comments ~total_local_video_channels ~total_local_playlists 24 24 ~total_instance_followers ~total_instance_following = 25 - { total_users; total_daily_active_users; total_weekly_active_users; 26 - total_monthly_active_users; total_local_videos; total_local_video_views; 27 - total_local_video_comments; total_local_video_files_size; total_videos; 28 - total_video_comments; total_local_video_channels; total_local_playlists; 29 - total_instance_followers; total_instance_following } 25 + { 26 + total_users; 27 + total_daily_active_users; 28 + total_weekly_active_users; 29 + total_monthly_active_users; 30 + total_local_videos; 31 + total_local_video_views; 32 + total_local_video_comments; 33 + total_local_video_files_size; 34 + total_videos; 35 + total_video_comments; 36 + total_local_video_channels; 37 + total_local_playlists; 38 + total_instance_followers; 39 + total_instance_following; 40 + } 30 41 31 42 let total_users t = t.total_users 32 43 let total_daily_active_users t = t.total_daily_active_users ··· 49 60 total_local_video_comments total_local_video_files_size total_videos 50 61 total_video_comments total_local_video_channels total_local_playlists 51 62 total_instance_followers total_instance_following = 52 - { total_users; total_daily_active_users; total_weekly_active_users; 53 - total_monthly_active_users; total_local_videos; total_local_video_views; 54 - total_local_video_comments; total_local_video_files_size; total_videos; 55 - total_video_comments; total_local_video_channels; total_local_playlists; 56 - total_instance_followers; total_instance_following } 63 + { 64 + total_users; 65 + total_daily_active_users; 66 + total_weekly_active_users; 67 + total_monthly_active_users; 68 + total_local_videos; 69 + total_local_video_views; 70 + total_local_video_comments; 71 + total_local_video_files_size; 72 + total_videos; 73 + total_video_comments; 74 + total_local_video_channels; 75 + total_local_playlists; 76 + total_instance_followers; 77 + total_instance_following; 78 + } 57 79 in 58 80 Jsont.Object.map ~kind:"server_stats" make 59 - |> Jsont.Object.mem "totalUsers" Jsont.int ~dec_absent:0 60 - ~enc:(fun s -> s.total_users) 81 + |> Jsont.Object.mem "totalUsers" Jsont.int ~dec_absent:0 ~enc:(fun s -> 82 + s.total_users) 61 83 |> Jsont.Object.mem "totalDailyActiveUsers" Jsont.int ~dec_absent:0 62 84 ~enc:(fun s -> s.total_daily_active_users) 63 85 |> Jsont.Object.mem "totalWeeklyActiveUsers" Jsont.int ~dec_absent:0 64 86 ~enc:(fun s -> s.total_weekly_active_users) 65 87 |> Jsont.Object.mem "totalMonthlyActiveUsers" Jsont.int ~dec_absent:0 66 88 ~enc:(fun s -> s.total_monthly_active_users) 67 - |> Jsont.Object.mem "totalLocalVideos" Jsont.int ~dec_absent:0 68 - ~enc:(fun s -> s.total_local_videos) 89 + |> Jsont.Object.mem "totalLocalVideos" Jsont.int ~dec_absent:0 ~enc:(fun s -> 90 + s.total_local_videos) 69 91 |> Jsont.Object.mem "totalLocalVideoViews" Jsont.int ~dec_absent:0 70 92 ~enc:(fun s -> s.total_local_video_views) 71 93 |> Jsont.Object.mem "totalLocalVideoComments" Jsont.int ~dec_absent:0 72 94 ~enc:(fun s -> s.total_local_video_comments) 73 95 |> Jsont.Object.mem "totalLocalVideoFilesSize" Jsont.int64 ~dec_absent:0L 74 96 ~enc:(fun s -> s.total_local_video_files_size) 75 - |> Jsont.Object.mem "totalVideos" Jsont.int ~dec_absent:0 76 - ~enc:(fun s -> s.total_videos) 97 + |> Jsont.Object.mem "totalVideos" Jsont.int ~dec_absent:0 ~enc:(fun s -> 98 + s.total_videos) 77 99 |> Jsont.Object.mem "totalVideoComments" Jsont.int ~dec_absent:0 78 100 ~enc:(fun s -> s.total_video_comments) 79 101 |> Jsont.Object.mem "totalLocalVideoChannels" Jsont.int ~dec_absent:0 ··· 84 106 ~enc:(fun s -> s.total_instance_followers) 85 107 |> Jsont.Object.mem "totalInstanceFollowing" Jsont.int ~dec_absent:0 86 108 ~enc:(fun s -> s.total_instance_following) 87 - |> Jsont.Object.skip_unknown 88 - |> Jsont.Object.finish 109 + |> Jsont.Object.skip_unknown |> Jsont.Object.finish 89 110 90 111 let pp ppf t = 91 112 Fmt.pf ppf 92 - "@[<hov 2>{ total_users = %d;@ total_videos = %d;@ total_local_videos = %d }@]" 113 + "@[<hov 2>{ total_users = %d;@ total_videos = %d;@ total_local_videos = %d \ 114 + }@]" 93 115 t.total_users t.total_videos t.total_local_videos
+18 -18
lib/peertube_server_stats.mli
··· 1 1 (** PeerTube server statistics. *) 2 2 3 - (** Server stats type. *) 4 3 type t 4 + (** Server stats type. *) 5 5 6 - (** Create server stats. *) 7 6 val make : 8 7 total_users:int -> 9 8 total_daily_active_users:int -> ··· 20 19 total_instance_followers:int -> 21 20 total_instance_following:int -> 22 21 t 22 + (** Create server stats. *) 23 23 24 + val total_users : t -> int 24 25 (** Total registered users. *) 25 - val total_users : t -> int 26 26 27 - (** Daily active users. *) 28 27 val total_daily_active_users : t -> int 28 + (** Daily active users. *) 29 29 30 - (** Weekly active users. *) 31 30 val total_weekly_active_users : t -> int 31 + (** Weekly active users. *) 32 32 33 + val total_monthly_active_users : t -> int 33 34 (** Monthly active users. *) 34 - val total_monthly_active_users : t -> int 35 35 36 - (** Total local videos. *) 37 36 val total_local_videos : t -> int 37 + (** Total local videos. *) 38 38 39 - (** Total local video views. *) 40 39 val total_local_video_views : t -> int 40 + (** Total local video views. *) 41 41 42 - (** Total local video comments. *) 43 42 val total_local_video_comments : t -> int 43 + (** Total local video comments. *) 44 44 45 - (** Total local video files size in bytes. *) 46 45 val total_local_video_files_size : t -> int64 46 + (** Total local video files size in bytes. *) 47 47 48 + val total_videos : t -> int 48 49 (** Total videos (including federated). *) 49 - val total_videos : t -> int 50 50 51 + val total_video_comments : t -> int 51 52 (** Total video comments (including federated). *) 52 - val total_video_comments : t -> int 53 53 54 - (** Total local video channels. *) 55 54 val total_local_video_channels : t -> int 55 + (** Total local video channels. *) 56 56 57 - (** Total local playlists. *) 58 57 val total_local_playlists : t -> int 58 + (** Total local playlists. *) 59 59 60 + val total_instance_followers : t -> int 60 61 (** Total instance followers. *) 61 - val total_instance_followers : t -> int 62 62 63 - (** Total instances being followed. *) 64 63 val total_instance_following : t -> int 64 + (** Total instances being followed. *) 65 65 66 - (** JSON codec. *) 67 66 val jsont : t Jsont.t 67 + (** JSON codec. *) 68 68 69 - (** Pretty printer. *) 70 69 val pp : Format.formatter -> t -> unit 70 + (** Pretty printer. *)
+86 -36
lib/peertube_video.ml
··· 32 32 ~originally_published_at ~updated_at ~thumbnail_path ~preview_path ~tags 33 33 ~duration ~views ~likes ~dislikes ~is_local ~is_live ~privacy ~category 34 34 ~licence ~language ~channel ~account = 35 - { id; uuid; short_uuid; name; description; url; embed_path; published_at; 36 - originally_published_at; updated_at; thumbnail_path; preview_path; tags; 37 - duration; views; likes; dislikes; is_local; is_live; privacy; category; 38 - licence; language; channel; account } 35 + { 36 + id; 37 + uuid; 38 + short_uuid; 39 + name; 40 + description; 41 + url; 42 + embed_path; 43 + published_at; 44 + originally_published_at; 45 + updated_at; 46 + thumbnail_path; 47 + preview_path; 48 + tags; 49 + duration; 50 + views; 51 + likes; 52 + dislikes; 53 + is_local; 54 + is_live; 55 + privacy; 56 + category; 57 + licence; 58 + language; 59 + channel; 60 + account; 61 + } 39 62 40 63 let id t = t.id 41 64 let uuid t = t.uuid ··· 68 91 originally_published_at updated_at thumbnail_path preview_path tags 69 92 duration views likes dislikes is_local is_live privacy category licence 70 93 language channel account = 71 - { id; uuid; short_uuid; name; description; url; embed_path; published_at; 72 - originally_published_at; updated_at; thumbnail_path; preview_path; tags; 73 - duration; views; likes; dislikes; is_local; is_live; privacy; category; 74 - licence; language; channel; account } 94 + { 95 + id; 96 + uuid; 97 + short_uuid; 98 + name; 99 + description; 100 + url; 101 + embed_path; 102 + published_at; 103 + originally_published_at; 104 + updated_at; 105 + thumbnail_path; 106 + preview_path; 107 + tags; 108 + duration; 109 + views; 110 + likes; 111 + dislikes; 112 + is_local; 113 + is_live; 114 + privacy; 115 + category; 116 + licence; 117 + language; 118 + channel; 119 + account; 120 + } 75 121 in 76 122 Jsont.Object.map ~kind:"video" make 77 123 |> Jsont.Object.mem "id" Jsont.int ~enc:(fun v -> v.id) 78 124 |> Jsont.Object.mem "uuid" Jsont.string ~enc:(fun v -> v.uuid) 79 - |> Jsont.Object.mem "shortUUID" (Jsont.option Jsont.string) ~dec_absent:None 80 - ~enc_omit:Option.is_none ~enc:(fun v -> v.short_uuid) 125 + |> Jsont.Object.mem "shortUUID" (Jsont.option Jsont.string) 126 + ~dec_absent:None ~enc_omit:Option.is_none ~enc:(fun v -> v.short_uuid) 81 127 |> Jsont.Object.mem "name" Jsont.string ~enc:(fun v -> v.name) 82 - |> Jsont.Object.mem "description" (Jsont.option Jsont.string) ~dec_absent:None 83 - ~enc_omit:Option.is_none ~enc:(fun v -> v.description) 128 + |> Jsont.Object.mem "description" (Jsont.option Jsont.string) 129 + ~dec_absent:None ~enc_omit:Option.is_none ~enc:(fun v -> v.description) 84 130 |> Jsont.Object.mem "url" Jsont.string ~enc:(fun v -> v.url) 85 131 |> Jsont.Object.mem "embedPath" Jsont.string ~enc:(fun v -> v.embed_path) 86 - |> Jsont.Object.mem "publishedAt" Peertube_ptime.jsont 87 - ~enc:(fun v -> v.published_at) 88 - |> Jsont.Object.mem "originallyPublishedAt" (Jsont.option Peertube_ptime.jsont) 89 - ~dec_absent:None ~enc_omit:Option.is_none 90 - ~enc:(fun v -> v.originally_published_at) 132 + |> Jsont.Object.mem "publishedAt" Peertube_ptime.jsont ~enc:(fun v -> 133 + v.published_at) 134 + |> Jsont.Object.mem "originallyPublishedAt" 135 + (Jsont.option Peertube_ptime.jsont) 136 + ~dec_absent:None ~enc_omit:Option.is_none ~enc:(fun v -> 137 + v.originally_published_at) 91 138 |> Jsont.Object.mem "updatedAt" (Jsont.option Peertube_ptime.jsont) 92 139 ~dec_absent:None ~enc_omit:Option.is_none ~enc:(fun v -> v.updated_at) 93 140 |> Jsont.Object.mem "thumbnailPath" (Jsont.option Jsont.string) 94 - ~dec_absent:None ~enc_omit:Option.is_none ~enc:(fun v -> v.thumbnail_path) 95 - |> Jsont.Object.mem "previewPath" (Jsont.option Jsont.string) ~dec_absent:None 96 - ~enc_omit:Option.is_none ~enc:(fun v -> v.preview_path) 97 - |> Jsont.Object.mem "tags" Jsont.(list string) ~dec_absent:[] 98 - ~enc_omit:(fun l -> l = []) ~enc:(fun v -> v.tags) 99 - |> Jsont.Object.mem "duration" Jsont.int ~dec_absent:0 ~enc:(fun v -> v.duration) 141 + ~dec_absent:None ~enc_omit:Option.is_none ~enc:(fun v -> 142 + v.thumbnail_path) 143 + |> Jsont.Object.mem "previewPath" (Jsont.option Jsont.string) 144 + ~dec_absent:None ~enc_omit:Option.is_none ~enc:(fun v -> v.preview_path) 145 + |> Jsont.Object.mem "tags" 146 + Jsont.(list string) 147 + ~dec_absent:[] 148 + ~enc_omit:(fun l -> l = []) 149 + ~enc:(fun v -> v.tags) 150 + |> Jsont.Object.mem "duration" Jsont.int ~dec_absent:0 ~enc:(fun v -> 151 + v.duration) 100 152 |> Jsont.Object.mem "views" Jsont.int ~dec_absent:0 ~enc:(fun v -> v.views) 101 153 |> Jsont.Object.mem "likes" Jsont.int ~dec_absent:0 ~enc:(fun v -> v.likes) 102 - |> Jsont.Object.mem "dislikes" Jsont.int ~dec_absent:0 ~enc:(fun v -> v.dislikes) 103 - |> Jsont.Object.mem "isLocal" Jsont.bool ~dec_absent:true 104 - ~enc:(fun v -> v.is_local) 105 - |> Jsont.Object.mem "isLive" Jsont.bool ~dec_absent:false 106 - ~enc:(fun v -> v.is_live) 154 + |> Jsont.Object.mem "dislikes" Jsont.int ~dec_absent:0 ~enc:(fun v -> 155 + v.dislikes) 156 + |> Jsont.Object.mem "isLocal" Jsont.bool ~dec_absent:true ~enc:(fun v -> 157 + v.is_local) 158 + |> Jsont.Object.mem "isLive" Jsont.bool ~dec_absent:false ~enc:(fun v -> 159 + v.is_live) 107 160 |> Jsont.Object.mem "privacy" Peertube_privacy.jsont 108 161 ~dec_absent:Peertube_privacy.Public ~enc:(fun v -> v.privacy) 109 162 |> Jsont.Object.mem "category" (Jsont.option Peertube_labeled.int_jsont) ··· 112 165 ~dec_absent:None ~enc_omit:Option.is_none ~enc:(fun v -> v.licence) 113 166 |> Jsont.Object.mem "language" (Jsont.option Peertube_labeled.string_jsont) 114 167 ~dec_absent:None ~enc_omit:Option.is_none ~enc:(fun v -> v.language) 115 - |> Jsont.Object.mem "channel" 116 - (Jsont.option Peertube_channel_summary.jsont) 168 + |> Jsont.Object.mem "channel" (Jsont.option Peertube_channel_summary.jsont) 117 169 ~dec_absent:None ~enc_omit:Option.is_none ~enc:(fun v -> v.channel) 118 - |> Jsont.Object.mem "account" 119 - (Jsont.option Peertube_account_summary.jsont) 170 + |> Jsont.Object.mem "account" (Jsont.option Peertube_account_summary.jsont) 120 171 ~dec_absent:None ~enc_omit:Option.is_none ~enc:(fun v -> v.account) 121 - |> Jsont.Object.skip_unknown 122 - |> Jsont.Object.finish 172 + |> Jsont.Object.skip_unknown |> Jsont.Object.finish 123 173 124 174 let pp ppf t = 125 175 Fmt.pf ppf 126 - "@[<hov 2>{ id = %d;@ uuid = %S;@ name = %S;@ published_at = %a;@ \ 127 - duration = %d;@ views = %d;@ likes = %d }@]" 176 + "@[<hov 2>{ id = %d;@ uuid = %S;@ name = %S;@ published_at = %a;@ duration \ 177 + = %d;@ views = %d;@ likes = %d }@]" 128 178 t.id t.uuid t.name Peertube_ptime.pp t.published_at t.duration t.views 129 179 t.likes
+29 -29
lib/peertube_video.mli
··· 1 1 (** PeerTube video record. *) 2 2 3 - (** Video type. *) 4 3 type t 4 + (** Video type. *) 5 5 6 - (** Create a video. *) 7 6 val make : 8 7 id:int -> 9 8 uuid:string -> ··· 31 30 channel:Peertube_channel_summary.t option -> 32 31 account:Peertube_account_summary.t option -> 33 32 t 33 + (** Create a video. *) 34 34 35 + val id : t -> int 35 36 (** Video ID. *) 36 - val id : t -> int 37 37 38 - (** Video UUID. *) 39 38 val uuid : t -> string 39 + (** Video UUID. *) 40 40 41 - (** Short UUID. *) 42 41 val short_uuid : t -> string option 42 + (** Short UUID. *) 43 43 44 + val name : t -> string 44 45 (** Video title. *) 45 - val name : t -> string 46 46 47 - (** Video description. *) 48 47 val description : t -> string option 48 + (** Video description. *) 49 49 50 + val url : t -> string 50 51 (** Video URL. *) 51 - val url : t -> string 52 52 53 - (** Embed path. *) 54 53 val embed_path : t -> string 54 + (** Embed path. *) 55 55 56 + val published_at : t -> Ptime.t 56 57 (** Publication timestamp. *) 57 - val published_at : t -> Ptime.t 58 58 59 + val originally_published_at : t -> Ptime.t option 59 60 (** Original publication timestamp. *) 60 - val originally_published_at : t -> Ptime.t option 61 61 62 - (** Last update timestamp. *) 63 62 val updated_at : t -> Ptime.t option 63 + (** Last update timestamp. *) 64 64 65 - (** Thumbnail path (relative). *) 66 65 val thumbnail_path : t -> string option 66 + (** Thumbnail path (relative). *) 67 67 68 - (** Preview path (relative). *) 69 68 val preview_path : t -> string option 69 + (** Preview path (relative). *) 70 70 71 - (** Video tags. *) 72 71 val tags : t -> string list 72 + (** Video tags. *) 73 73 74 - (** Duration in seconds. *) 75 74 val duration : t -> int 75 + (** Duration in seconds. *) 76 76 77 - (** View count. *) 78 77 val views : t -> int 78 + (** View count. *) 79 79 80 - (** Like count. *) 81 80 val likes : t -> int 81 + (** Like count. *) 82 82 83 + val dislikes : t -> int 83 84 (** Dislike count. *) 84 - val dislikes : t -> int 85 85 86 - (** Whether the video is local to this instance. *) 87 86 val is_local : t -> bool 87 + (** Whether the video is local to this instance. *) 88 88 89 + val is_live : t -> bool 89 90 (** Whether this is a live stream. *) 90 - val is_live : t -> bool 91 91 92 + val privacy : t -> Peertube_privacy.t 92 93 (** Privacy level. *) 93 - val privacy : t -> Peertube_privacy.t 94 94 95 - (** Video category. *) 96 95 val category : t -> int Peertube_labeled.t option 96 + (** Video category. *) 97 97 98 - (** Video licence. *) 99 98 val licence : t -> int Peertube_labeled.t option 99 + (** Video licence. *) 100 100 101 - (** Video language. *) 102 101 val language : t -> string Peertube_labeled.t option 102 + (** Video language. *) 103 103 104 - (** Channel that published the video. *) 105 104 val channel : t -> Peertube_channel_summary.t option 105 + (** Channel that published the video. *) 106 106 107 - (** Account that published the video. *) 108 107 val account : t -> Peertube_account_summary.t option 108 + (** Account that published the video. *) 109 109 110 + val jsont : t Jsont.t 110 111 (** JSON codec. *) 111 - val jsont : t Jsont.t 112 112 113 + val pp : Format.formatter -> t -> unit 113 114 (** Pretty printer. *) 114 - val pp : Format.formatter -> t -> unit
+2 -2
lib/peertube_video_sort.mli
··· 3 3 (** Video sort order. *) 4 4 type t = Newest | Oldest | Views | Likes | Trending | Hot | Random | Best 5 5 6 - (** Convert sort option to API query string. *) 7 6 val to_string : t -> string 7 + (** Convert sort option to API query string. *) 8 8 9 - (** Pretty printer for sort options. *) 10 9 val pp : Format.formatter -> t -> unit 10 + (** Pretty printer for sort options. *)
+4 -4
peertube.opam
··· 2 2 opam-version: "2.0" 3 3 synopsis: "PeerTube API client for OCaml using Eio" 4 4 description: 5 - "An OCaml client library for the PeerTube video platform API, built on Eio for effect-based I/O" 5 + "An OCaml client library for the PeerTube video platform API, built on Eio for effect-based I/O. Includes a command-line client (opeertube) for browsing videos, channels, accounts, and playlists." 6 6 maintainer: ["anil@recoil.org"] 7 7 authors: ["Anil Madhavapeddy"] 8 8 license: "ISC" 9 - homepage: "https://github.com/avsm/ocaml-peertube" 10 - bug-reports: "https://github.com/avsm/ocaml-peertube/issues" 9 + homepage: "https://git.recoil.org/anil.recoil.org/ocaml-peertube" 10 + bug-reports: "https://git.recoil.org/anil.recoil.org/ocaml-peertube/issues" 11 11 depends: [ 12 12 "dune" {>= "3.16"} 13 13 "ocaml" {>= "5.1.0"} ··· 35 35 "@doc" {with-doc} 36 36 ] 37 37 ] 38 - dev-repo: "git+https://github.com/avsm/ocaml-peertube.git" 38 + dev-repo: "https://git.recoil.org/anil.recoil.org/ocaml-peertube"