Immich bindings and CLI in OCaml
1(*---------------------------------------------------------------------------
2 Copyright (c) 2025 Anil Madhavapeddy. All rights reserved.
3 SPDX-License-Identifier: ISC
4 ---------------------------------------------------------------------------*)
5
6open Cmdliner
7
8(* Styled output helpers *)
9let header_style = Fmt.(styled `Bold string)
10let id_style = Fmt.(styled `Faint string)
11let name_style = Fmt.(styled (`Fg `Cyan) string)
12let count_style = Fmt.(styled (`Fg `Green) int)
13let shared_style = Fmt.(styled (`Fg `Yellow) string)
14
15(* List albums - using low-level API since get_all_albums returns array but typed as single *)
16
17let list_action ~requests_config ~profile ~shared env =
18 Immich_auth.Error.wrap (fun () ->
19 Immich_auth.Cmd.with_client ~requests_config ?profile (fun _fs client ->
20 let api = Immich_auth.Client.client client in
21 let session = Immich.session api in
22 let base_url = Immich.base_url api in
23 let query = match shared with
24 | None -> ""
25 | Some true -> "?shared=true"
26 | Some false -> "?shared=false"
27 in
28 let url = base_url ^ "/albums" ^ query in
29 let response = Requests.get session url in
30 if Requests.Response.ok response then begin
31 let json = Requests.Response.json response in
32 let albums = Openapi.Runtime.Json.decode_json_exn
33 (Jsont.list Immich.Album.ResponseDto.jsont) json in
34 if albums = [] then
35 Fmt.pr "%a@." Fmt.(styled `Faint string) "No albums found."
36 else begin
37 Fmt.pr "%a@." header_style "Albums:";
38 List.iter (fun album ->
39 let is_shared = Immich.Album.ResponseDto.shared album in
40 Fmt.pr " %a %a (%a assets)%a@."
41 id_style (Immich.Album.ResponseDto.id album)
42 name_style (Immich.Album.ResponseDto.album_name album)
43 count_style (Immich.Album.ResponseDto.asset_count album)
44 shared_style (if is_shared then " (shared)" else "")
45 ) albums
46 end
47 end else begin
48 raise (Openapi.Runtime.Api_error {
49 operation = "get_all_albums";
50 method_ = "GET";
51 url;
52 status = Requests.Response.status_code response;
53 body = Requests.Response.text response;
54 parsed_body = None;
55 })
56 end
57 ) env
58 )
59
60let shared_arg =
61 let doc = "Filter by shared status. Use --shared for shared albums, --no-shared for owned only." in
62 Arg.(value & opt (some bool) None & info ["shared"] ~doc)
63
64let list_cmd env fs =
65 let doc = "List all albums." in
66 let info = Cmd.info "list" ~doc in
67 let list' (style_renderer, level) requests_config profile shared =
68 Immich_auth.Cmd.setup_logging_with_config style_renderer level requests_config;
69 list_action ~requests_config ~profile ~shared env
70 in
71 Cmd.v info Term.(const list' $ Immich_auth.Cmd.setup_logging $ Immich_auth.Cmd.requests_config_term fs $ Immich_auth.Cmd.profile_arg $ shared_arg)
72
73(* Albums command group *)
74
75let albums_cmd env fs =
76 let doc = "Album commands." in
77 let info = Cmd.info "albums" ~doc in
78 Cmd.group info
79 [ list_cmd env fs
80 ]