immichpy 1.6.5__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (439) hide show
  1. immichpy/.openapi-generator/FILES +387 -0
  2. immichpy/.openapi-generator/VERSION +1 -0
  3. immichpy/__init__.py +3 -0
  4. immichpy/cli/__main__.py +12 -0
  5. immichpy/cli/commands/activities.py +110 -0
  6. immichpy/cli/commands/albums.py +317 -0
  7. immichpy/cli/commands/api_keys.py +125 -0
  8. immichpy/cli/commands/assets.py +610 -0
  9. immichpy/cli/commands/authentication.py +343 -0
  10. immichpy/cli/commands/authentication_admin.py +34 -0
  11. immichpy/cli/commands/download.py +76 -0
  12. immichpy/cli/commands/duplicates.py +67 -0
  13. immichpy/cli/commands/faces.py +107 -0
  14. immichpy/cli/commands/jobs.py +75 -0
  15. immichpy/cli/commands/libraries.py +183 -0
  16. immichpy/cli/commands/maintenance_admin.py +59 -0
  17. immichpy/cli/commands/map.py +79 -0
  18. immichpy/cli/commands/memories.py +231 -0
  19. immichpy/cli/commands/notifications.py +152 -0
  20. immichpy/cli/commands/notifications_admin.py +127 -0
  21. immichpy/cli/commands/partners.py +108 -0
  22. immichpy/cli/commands/people.py +293 -0
  23. immichpy/cli/commands/plugins.py +46 -0
  24. immichpy/cli/commands/queues.py +115 -0
  25. immichpy/cli/commands/search.py +835 -0
  26. immichpy/cli/commands/server.py +237 -0
  27. immichpy/cli/commands/sessions.py +132 -0
  28. immichpy/cli/commands/shared_links.py +273 -0
  29. immichpy/cli/commands/stacks.py +153 -0
  30. immichpy/cli/commands/sync.py +140 -0
  31. immichpy/cli/commands/system_config.py +681 -0
  32. immichpy/cli/commands/system_metadata.py +91 -0
  33. immichpy/cli/commands/tags.py +191 -0
  34. immichpy/cli/commands/timeline.py +196 -0
  35. immichpy/cli/commands/trash.py +63 -0
  36. immichpy/cli/commands/users.py +406 -0
  37. immichpy/cli/commands/users_admin.py +444 -0
  38. immichpy/cli/commands/views.py +54 -0
  39. immichpy/cli/commands/workflows.py +140 -0
  40. immichpy/cli/consts.py +19 -0
  41. immichpy/cli/main.py +248 -0
  42. immichpy/cli/runtime.py +97 -0
  43. immichpy/cli/types.py +17 -0
  44. immichpy/cli/utils.py +227 -0
  45. immichpy/cli/wrapper/assets.py +223 -0
  46. immichpy/cli/wrapper/config.py +76 -0
  47. immichpy/cli/wrapper/download.py +77 -0
  48. immichpy/cli/wrapper/setup.py +90 -0
  49. immichpy/cli/wrapper/users.py +47 -0
  50. immichpy/client/.openapi-generator/FILES +371 -0
  51. immichpy/client/.openapi-generator/VERSION +1 -0
  52. immichpy/client/.openapi-generator-ignore +48 -0
  53. immichpy/client/__init__.py +0 -0
  54. immichpy/client/consts.py +1 -0
  55. immichpy/client/generated/__init__.py +1344 -0
  56. immichpy/client/generated/api/__init__.py +41 -0
  57. immichpy/client/generated/api/activities_api.py +1092 -0
  58. immichpy/client/generated/api/albums_api.py +3228 -0
  59. immichpy/client/generated/api/api_keys_api.py +1486 -0
  60. immichpy/client/generated/api/assets_api.py +5846 -0
  61. immichpy/client/generated/api/authentication_admin_api.py +254 -0
  62. immichpy/client/generated/api/authentication_api.py +3887 -0
  63. immichpy/client/generated/api/deprecated_api.py +2207 -0
  64. immichpy/client/generated/api/download_api.py +603 -0
  65. immichpy/client/generated/api/duplicates_api.py +743 -0
  66. immichpy/client/generated/api/faces_api.py +1055 -0
  67. immichpy/client/generated/api/jobs_api.py +787 -0
  68. immichpy/client/generated/api/libraries_api.py +2006 -0
  69. immichpy/client/generated/api/maintenance_admin_api.py +535 -0
  70. immichpy/client/generated/api/map_api.py +642 -0
  71. immichpy/client/generated/api/memories_api.py +2250 -0
  72. immichpy/client/generated/api/notifications_admin_api.py +812 -0
  73. immichpy/client/generated/api/notifications_api.py +1570 -0
  74. immichpy/client/generated/api/partners_api.py +1278 -0
  75. immichpy/client/generated/api/people_api.py +2905 -0
  76. immichpy/client/generated/api/plugins_api.py +503 -0
  77. immichpy/client/generated/api/queues_api.py +1292 -0
  78. immichpy/client/generated/api/search_api.py +3200 -0
  79. immichpy/client/generated/api/server_api.py +3474 -0
  80. immichpy/client/generated/api/sessions_api.py +1474 -0
  81. immichpy/client/generated/api/shared_links_api.py +2163 -0
  82. immichpy/client/generated/api/stacks_api.py +1769 -0
  83. immichpy/client/generated/api/sync_api.py +1514 -0
  84. immichpy/client/generated/api/system_config_api.py +967 -0
  85. immichpy/client/generated/api/system_metadata_api.py +966 -0
  86. immichpy/client/generated/api/tags_api.py +2298 -0
  87. immichpy/client/generated/api/timeline_api.py +1195 -0
  88. immichpy/client/generated/api/trash_api.py +739 -0
  89. immichpy/client/generated/api/users_admin_api.py +2613 -0
  90. immichpy/client/generated/api/users_api.py +3583 -0
  91. immichpy/client/generated/api/views_api.py +503 -0
  92. immichpy/client/generated/api/workflows_api.py +1257 -0
  93. immichpy/client/generated/api_client.py +756 -0
  94. immichpy/client/generated/api_response.py +20 -0
  95. immichpy/client/generated/configuration.py +638 -0
  96. immichpy/client/generated/exceptions.py +222 -0
  97. immichpy/client/generated/models/__init__.py +561 -0
  98. immichpy/client/generated/models/activity_create_dto.py +94 -0
  99. immichpy/client/generated/models/activity_response_dto.py +121 -0
  100. immichpy/client/generated/models/activity_statistics_response_dto.py +85 -0
  101. immichpy/client/generated/models/add_users_dto.py +101 -0
  102. immichpy/client/generated/models/admin_onboarding_update_dto.py +82 -0
  103. immichpy/client/generated/models/album_response_dto.py +201 -0
  104. immichpy/client/generated/models/album_statistics_response_dto.py +90 -0
  105. immichpy/client/generated/models/album_user_add_dto.py +87 -0
  106. immichpy/client/generated/models/album_user_create_dto.py +87 -0
  107. immichpy/client/generated/models/album_user_response_dto.py +95 -0
  108. immichpy/client/generated/models/album_user_role.py +34 -0
  109. immichpy/client/generated/models/albums_add_assets_dto.py +86 -0
  110. immichpy/client/generated/models/albums_add_assets_response_dto.py +86 -0
  111. immichpy/client/generated/models/albums_response.py +83 -0
  112. immichpy/client/generated/models/albums_update.py +85 -0
  113. immichpy/client/generated/models/api_key_create_dto.py +87 -0
  114. immichpy/client/generated/models/api_key_create_response_dto.py +94 -0
  115. immichpy/client/generated/models/api_key_response_dto.py +102 -0
  116. immichpy/client/generated/models/api_key_update_dto.py +87 -0
  117. immichpy/client/generated/models/asset_bulk_delete_dto.py +84 -0
  118. immichpy/client/generated/models/asset_bulk_update_dto.py +143 -0
  119. immichpy/client/generated/models/asset_bulk_upload_check_dto.py +100 -0
  120. immichpy/client/generated/models/asset_bulk_upload_check_item.py +85 -0
  121. immichpy/client/generated/models/asset_bulk_upload_check_response_dto.py +101 -0
  122. immichpy/client/generated/models/asset_bulk_upload_check_result.py +126 -0
  123. immichpy/client/generated/models/asset_copy_dto.py +113 -0
  124. immichpy/client/generated/models/asset_delta_sync_dto.py +87 -0
  125. immichpy/client/generated/models/asset_delta_sync_response_dto.py +102 -0
  126. immichpy/client/generated/models/asset_face_create_dto.py +110 -0
  127. immichpy/client/generated/models/asset_face_delete_dto.py +82 -0
  128. immichpy/client/generated/models/asset_face_response_dto.py +125 -0
  129. immichpy/client/generated/models/asset_face_update_dto.py +96 -0
  130. immichpy/client/generated/models/asset_face_update_item.py +86 -0
  131. immichpy/client/generated/models/asset_face_without_person_response_dto.py +111 -0
  132. immichpy/client/generated/models/asset_full_sync_dto.py +95 -0
  133. immichpy/client/generated/models/asset_ids_dto.py +83 -0
  134. immichpy/client/generated/models/asset_ids_response_dto.py +109 -0
  135. immichpy/client/generated/models/asset_job_name.py +36 -0
  136. immichpy/client/generated/models/asset_jobs_dto.py +87 -0
  137. immichpy/client/generated/models/asset_media_response_dto.py +84 -0
  138. immichpy/client/generated/models/asset_media_size.py +35 -0
  139. immichpy/client/generated/models/asset_media_status.py +35 -0
  140. immichpy/client/generated/models/asset_metadata_key.py +33 -0
  141. immichpy/client/generated/models/asset_metadata_response_dto.py +92 -0
  142. immichpy/client/generated/models/asset_metadata_upsert_dto.py +101 -0
  143. immichpy/client/generated/models/asset_metadata_upsert_item_dto.py +84 -0
  144. immichpy/client/generated/models/asset_ocr_response_dto.py +145 -0
  145. immichpy/client/generated/models/asset_order.py +34 -0
  146. immichpy/client/generated/models/asset_response_dto.py +293 -0
  147. immichpy/client/generated/models/asset_stack_response_dto.py +90 -0
  148. immichpy/client/generated/models/asset_stats_response_dto.py +90 -0
  149. immichpy/client/generated/models/asset_type_enum.py +36 -0
  150. immichpy/client/generated/models/asset_visibility.py +36 -0
  151. immichpy/client/generated/models/audio_codec.py +36 -0
  152. immichpy/client/generated/models/auth_status_response_dto.py +100 -0
  153. immichpy/client/generated/models/avatar_update.py +83 -0
  154. immichpy/client/generated/models/bulk_id_error_reason.py +36 -0
  155. immichpy/client/generated/models/bulk_id_response_dto.py +102 -0
  156. immichpy/client/generated/models/bulk_ids_dto.py +83 -0
  157. immichpy/client/generated/models/cast_response.py +88 -0
  158. immichpy/client/generated/models/cast_update.py +82 -0
  159. immichpy/client/generated/models/change_password_dto.py +101 -0
  160. immichpy/client/generated/models/check_existing_assets_dto.py +91 -0
  161. immichpy/client/generated/models/check_existing_assets_response_dto.py +82 -0
  162. immichpy/client/generated/models/clip_config.py +85 -0
  163. immichpy/client/generated/models/colorspace.py +34 -0
  164. immichpy/client/generated/models/contributor_count_response_dto.py +85 -0
  165. immichpy/client/generated/models/cq_mode.py +35 -0
  166. immichpy/client/generated/models/create_album_dto.py +112 -0
  167. immichpy/client/generated/models/create_library_dto.py +103 -0
  168. immichpy/client/generated/models/create_profile_image_response_dto.py +95 -0
  169. immichpy/client/generated/models/database_backup_config.py +94 -0
  170. immichpy/client/generated/models/download_archive_info.py +85 -0
  171. immichpy/client/generated/models/download_info_dto.py +96 -0
  172. immichpy/client/generated/models/download_response.py +90 -0
  173. immichpy/client/generated/models/download_response_dto.py +100 -0
  174. immichpy/client/generated/models/download_update.py +93 -0
  175. immichpy/client/generated/models/duplicate_detection_config.py +89 -0
  176. immichpy/client/generated/models/duplicate_response_dto.py +98 -0
  177. immichpy/client/generated/models/email_notifications_response.py +90 -0
  178. immichpy/client/generated/models/email_notifications_update.py +90 -0
  179. immichpy/client/generated/models/exif_response_dto.py +284 -0
  180. immichpy/client/generated/models/face_dto.py +83 -0
  181. immichpy/client/generated/models/facial_recognition_config.py +107 -0
  182. immichpy/client/generated/models/folders_response.py +92 -0
  183. immichpy/client/generated/models/folders_update.py +85 -0
  184. immichpy/client/generated/models/image_format.py +34 -0
  185. immichpy/client/generated/models/job_create_dto.py +83 -0
  186. immichpy/client/generated/models/job_name.py +87 -0
  187. immichpy/client/generated/models/job_settings_dto.py +83 -0
  188. immichpy/client/generated/models/library_response_dto.py +118 -0
  189. immichpy/client/generated/models/library_stats_response_dto.py +92 -0
  190. immichpy/client/generated/models/license_key_dto.py +98 -0
  191. immichpy/client/generated/models/license_response_dto.py +101 -0
  192. immichpy/client/generated/models/log_level.py +38 -0
  193. immichpy/client/generated/models/login_credential_dto.py +85 -0
  194. immichpy/client/generated/models/login_response_dto.py +109 -0
  195. immichpy/client/generated/models/logout_response_dto.py +85 -0
  196. immichpy/client/generated/models/machine_learning_availability_checks_dto.py +90 -0
  197. immichpy/client/generated/models/maintenance_action.py +34 -0
  198. immichpy/client/generated/models/maintenance_auth_dto.py +82 -0
  199. immichpy/client/generated/models/maintenance_login_dto.py +82 -0
  200. immichpy/client/generated/models/manual_job_name.py +38 -0
  201. immichpy/client/generated/models/map_marker_response_dto.py +111 -0
  202. immichpy/client/generated/models/map_reverse_geocode_response_dto.py +105 -0
  203. immichpy/client/generated/models/memories_response.py +92 -0
  204. immichpy/client/generated/models/memories_update.py +86 -0
  205. immichpy/client/generated/models/memory_create_dto.py +112 -0
  206. immichpy/client/generated/models/memory_response_dto.py +142 -0
  207. immichpy/client/generated/models/memory_search_order.py +35 -0
  208. immichpy/client/generated/models/memory_statistics_response_dto.py +82 -0
  209. immichpy/client/generated/models/memory_type.py +33 -0
  210. immichpy/client/generated/models/memory_update_dto.py +91 -0
  211. immichpy/client/generated/models/merge_person_dto.py +83 -0
  212. immichpy/client/generated/models/metadata_search_dto.py +277 -0
  213. immichpy/client/generated/models/notification_create_dto.py +120 -0
  214. immichpy/client/generated/models/notification_delete_all_dto.py +83 -0
  215. immichpy/client/generated/models/notification_dto.py +112 -0
  216. immichpy/client/generated/models/notification_level.py +36 -0
  217. immichpy/client/generated/models/notification_type.py +38 -0
  218. immichpy/client/generated/models/notification_update_all_dto.py +90 -0
  219. immichpy/client/generated/models/notification_update_dto.py +88 -0
  220. immichpy/client/generated/models/o_auth_authorize_response_dto.py +82 -0
  221. immichpy/client/generated/models/o_auth_callback_dto.py +90 -0
  222. immichpy/client/generated/models/o_auth_config_dto.py +90 -0
  223. immichpy/client/generated/models/o_auth_token_endpoint_auth_method.py +34 -0
  224. immichpy/client/generated/models/ocr_config.py +109 -0
  225. immichpy/client/generated/models/on_this_day_dto.py +86 -0
  226. immichpy/client/generated/models/onboarding_dto.py +82 -0
  227. immichpy/client/generated/models/onboarding_response_dto.py +82 -0
  228. immichpy/client/generated/models/partner_create_dto.py +83 -0
  229. immichpy/client/generated/models/partner_direction.py +34 -0
  230. immichpy/client/generated/models/partner_response_dto.py +108 -0
  231. immichpy/client/generated/models/partner_update_dto.py +82 -0
  232. immichpy/client/generated/models/people_response.py +92 -0
  233. immichpy/client/generated/models/people_response_dto.py +104 -0
  234. immichpy/client/generated/models/people_update.py +85 -0
  235. immichpy/client/generated/models/people_update_dto.py +96 -0
  236. immichpy/client/generated/models/people_update_item.py +128 -0
  237. immichpy/client/generated/models/permission.py +177 -0
  238. immichpy/client/generated/models/person_create_dto.py +117 -0
  239. immichpy/client/generated/models/person_response_dto.py +115 -0
  240. immichpy/client/generated/models/person_statistics_response_dto.py +82 -0
  241. immichpy/client/generated/models/person_update_dto.py +125 -0
  242. immichpy/client/generated/models/person_with_faces_response_dto.py +133 -0
  243. immichpy/client/generated/models/pin_code_change_dto.py +90 -0
  244. immichpy/client/generated/models/pin_code_reset_dto.py +85 -0
  245. immichpy/client/generated/models/pin_code_setup_dto.py +82 -0
  246. immichpy/client/generated/models/places_response_dto.py +100 -0
  247. immichpy/client/generated/models/plugin_action_response_dto.py +112 -0
  248. immichpy/client/generated/models/plugin_context.py +35 -0
  249. immichpy/client/generated/models/plugin_filter_response_dto.py +112 -0
  250. immichpy/client/generated/models/plugin_response_dto.py +143 -0
  251. immichpy/client/generated/models/plugin_trigger_type.py +34 -0
  252. immichpy/client/generated/models/purchase_response.py +88 -0
  253. immichpy/client/generated/models/purchase_update.py +92 -0
  254. immichpy/client/generated/models/queue_command.py +37 -0
  255. immichpy/client/generated/models/queue_command_dto.py +86 -0
  256. immichpy/client/generated/models/queue_delete_dto.py +85 -0
  257. immichpy/client/generated/models/queue_job_response_dto.py +93 -0
  258. immichpy/client/generated/models/queue_job_status.py +38 -0
  259. immichpy/client/generated/models/queue_name.py +49 -0
  260. immichpy/client/generated/models/queue_response_dto.py +97 -0
  261. immichpy/client/generated/models/queue_response_legacy_dto.py +102 -0
  262. immichpy/client/generated/models/queue_statistics_dto.py +103 -0
  263. immichpy/client/generated/models/queue_status_legacy_dto.py +85 -0
  264. immichpy/client/generated/models/queue_update_dto.py +82 -0
  265. immichpy/client/generated/models/queues_response_legacy_dto.py +244 -0
  266. immichpy/client/generated/models/random_search_dto.py +234 -0
  267. immichpy/client/generated/models/ratings_response.py +84 -0
  268. immichpy/client/generated/models/ratings_update.py +82 -0
  269. immichpy/client/generated/models/reaction_level.py +34 -0
  270. immichpy/client/generated/models/reaction_type.py +34 -0
  271. immichpy/client/generated/models/reverse_geocoding_state_response_dto.py +101 -0
  272. immichpy/client/generated/models/search_album_response_dto.py +116 -0
  273. immichpy/client/generated/models/search_asset_response_dto.py +129 -0
  274. immichpy/client/generated/models/search_explore_item.py +94 -0
  275. immichpy/client/generated/models/search_explore_response_dto.py +98 -0
  276. immichpy/client/generated/models/search_facet_count_response_dto.py +85 -0
  277. immichpy/client/generated/models/search_facet_response_dto.py +103 -0
  278. immichpy/client/generated/models/search_response_dto.py +104 -0
  279. immichpy/client/generated/models/search_statistics_response_dto.py +82 -0
  280. immichpy/client/generated/models/search_suggestion_type.py +38 -0
  281. immichpy/client/generated/models/server_about_response_dto.py +156 -0
  282. immichpy/client/generated/models/server_apk_links_dto.py +97 -0
  283. immichpy/client/generated/models/server_config_dto.py +118 -0
  284. immichpy/client/generated/models/server_features_dto.py +130 -0
  285. immichpy/client/generated/models/server_media_types_response_dto.py +90 -0
  286. immichpy/client/generated/models/server_ping_response.py +87 -0
  287. immichpy/client/generated/models/server_stats_response_dto.py +119 -0
  288. immichpy/client/generated/models/server_storage_response_dto.py +108 -0
  289. immichpy/client/generated/models/server_theme_dto.py +82 -0
  290. immichpy/client/generated/models/server_version_history_response_dto.py +91 -0
  291. immichpy/client/generated/models/server_version_response_dto.py +90 -0
  292. immichpy/client/generated/models/session_create_dto.py +96 -0
  293. immichpy/client/generated/models/session_create_response_dto.py +120 -0
  294. immichpy/client/generated/models/session_response_dto.py +117 -0
  295. immichpy/client/generated/models/session_unlock_dto.py +85 -0
  296. immichpy/client/generated/models/session_update_dto.py +84 -0
  297. immichpy/client/generated/models/set_maintenance_mode_dto.py +83 -0
  298. immichpy/client/generated/models/shared_link_create_dto.py +142 -0
  299. immichpy/client/generated/models/shared_link_edit_dto.py +134 -0
  300. immichpy/client/generated/models/shared_link_response_dto.py +173 -0
  301. immichpy/client/generated/models/shared_link_type.py +34 -0
  302. immichpy/client/generated/models/shared_links_response.py +92 -0
  303. immichpy/client/generated/models/shared_links_update.py +85 -0
  304. immichpy/client/generated/models/sign_up_dto.py +90 -0
  305. immichpy/client/generated/models/smart_search_dto.py +245 -0
  306. immichpy/client/generated/models/source_type.py +35 -0
  307. immichpy/client/generated/models/stack_create_dto.py +86 -0
  308. immichpy/client/generated/models/stack_response_dto.py +100 -0
  309. immichpy/client/generated/models/stack_update_dto.py +83 -0
  310. immichpy/client/generated/models/statistics_search_dto.py +217 -0
  311. immichpy/client/generated/models/sync_ack_delete_dto.py +83 -0
  312. immichpy/client/generated/models/sync_ack_dto.py +84 -0
  313. immichpy/client/generated/models/sync_ack_set_dto.py +83 -0
  314. immichpy/client/generated/models/sync_album_delete_v1.py +82 -0
  315. immichpy/client/generated/models/sync_album_to_asset_delete_v1.py +85 -0
  316. immichpy/client/generated/models/sync_album_to_asset_v1.py +85 -0
  317. immichpy/client/generated/models/sync_album_user_delete_v1.py +85 -0
  318. immichpy/client/generated/models/sync_album_user_v1.py +91 -0
  319. immichpy/client/generated/models/sync_album_v1.py +122 -0
  320. immichpy/client/generated/models/sync_asset_delete_v1.py +82 -0
  321. immichpy/client/generated/models/sync_asset_exif_v1.py +296 -0
  322. immichpy/client/generated/models/sync_asset_face_delete_v1.py +82 -0
  323. immichpy/client/generated/models/sync_asset_face_v1.py +120 -0
  324. immichpy/client/generated/models/sync_asset_metadata_delete_v1.py +86 -0
  325. immichpy/client/generated/models/sync_asset_metadata_v1.py +91 -0
  326. immichpy/client/generated/models/sync_asset_v1.py +187 -0
  327. immichpy/client/generated/models/sync_auth_user_v1.py +154 -0
  328. immichpy/client/generated/models/sync_entity_type.py +79 -0
  329. immichpy/client/generated/models/sync_memory_asset_delete_v1.py +85 -0
  330. immichpy/client/generated/models/sync_memory_asset_v1.py +85 -0
  331. immichpy/client/generated/models/sync_memory_delete_v1.py +82 -0
  332. immichpy/client/generated/models/sync_memory_v1.py +143 -0
  333. immichpy/client/generated/models/sync_partner_delete_v1.py +88 -0
  334. immichpy/client/generated/models/sync_partner_v1.py +90 -0
  335. immichpy/client/generated/models/sync_person_delete_v1.py +82 -0
  336. immichpy/client/generated/models/sync_person_v1.py +131 -0
  337. immichpy/client/generated/models/sync_request_type.py +52 -0
  338. immichpy/client/generated/models/sync_stack_delete_v1.py +82 -0
  339. immichpy/client/generated/models/sync_stack_v1.py +101 -0
  340. immichpy/client/generated/models/sync_stream_dto.py +86 -0
  341. immichpy/client/generated/models/sync_user_delete_v1.py +82 -0
  342. immichpy/client/generated/models/sync_user_metadata_delete_v1.py +84 -0
  343. immichpy/client/generated/models/sync_user_metadata_v1.py +91 -0
  344. immichpy/client/generated/models/sync_user_v1.py +118 -0
  345. immichpy/client/generated/models/system_config_backups_dto.py +92 -0
  346. immichpy/client/generated/models/system_config_dto.py +324 -0
  347. immichpy/client/generated/models/system_config_f_fmpeg_dto.py +156 -0
  348. immichpy/client/generated/models/system_config_faces_dto.py +82 -0
  349. immichpy/client/generated/models/system_config_generated_fullsize_image_dto.py +92 -0
  350. immichpy/client/generated/models/system_config_generated_image_dto.py +92 -0
  351. immichpy/client/generated/models/system_config_image_dto.py +124 -0
  352. immichpy/client/generated/models/system_config_job_dto.py +194 -0
  353. immichpy/client/generated/models/system_config_library_dto.py +104 -0
  354. immichpy/client/generated/models/system_config_library_scan_dto.py +85 -0
  355. immichpy/client/generated/models/system_config_library_watch_dto.py +82 -0
  356. immichpy/client/generated/models/system_config_logging_dto.py +86 -0
  357. immichpy/client/generated/models/system_config_machine_learning_dto.py +151 -0
  358. immichpy/client/generated/models/system_config_map_dto.py +90 -0
  359. immichpy/client/generated/models/system_config_metadata_dto.py +94 -0
  360. immichpy/client/generated/models/system_config_new_version_check_dto.py +82 -0
  361. immichpy/client/generated/models/system_config_nightly_tasks_dto.py +103 -0
  362. immichpy/client/generated/models/system_config_notifications_dto.py +92 -0
  363. immichpy/client/generated/models/system_config_o_auth_dto.py +155 -0
  364. immichpy/client/generated/models/system_config_password_login_dto.py +82 -0
  365. immichpy/client/generated/models/system_config_reverse_geocoding_dto.py +82 -0
  366. immichpy/client/generated/models/system_config_server_dto.py +94 -0
  367. immichpy/client/generated/models/system_config_smtp_dto.py +100 -0
  368. immichpy/client/generated/models/system_config_smtp_transport_dto.py +107 -0
  369. immichpy/client/generated/models/system_config_storage_template_dto.py +94 -0
  370. immichpy/client/generated/models/system_config_template_emails_dto.py +94 -0
  371. immichpy/client/generated/models/system_config_template_storage_option_dto.py +109 -0
  372. immichpy/client/generated/models/system_config_templates_dto.py +94 -0
  373. immichpy/client/generated/models/system_config_theme_dto.py +82 -0
  374. immichpy/client/generated/models/system_config_trash_dto.py +86 -0
  375. immichpy/client/generated/models/system_config_user_dto.py +83 -0
  376. immichpy/client/generated/models/tag_bulk_assets_dto.py +86 -0
  377. immichpy/client/generated/models/tag_bulk_assets_response_dto.py +82 -0
  378. immichpy/client/generated/models/tag_create_dto.py +111 -0
  379. immichpy/client/generated/models/tag_response_dto.py +107 -0
  380. immichpy/client/generated/models/tag_update_dto.py +87 -0
  381. immichpy/client/generated/models/tag_upsert_dto.py +82 -0
  382. immichpy/client/generated/models/tags_response.py +92 -0
  383. immichpy/client/generated/models/tags_update.py +85 -0
  384. immichpy/client/generated/models/template_dto.py +82 -0
  385. immichpy/client/generated/models/template_response_dto.py +83 -0
  386. immichpy/client/generated/models/test_email_response_dto.py +82 -0
  387. immichpy/client/generated/models/time_bucket_asset_response_dto.py +195 -0
  388. immichpy/client/generated/models/time_buckets_response_dto.py +88 -0
  389. immichpy/client/generated/models/tone_mapping.py +36 -0
  390. immichpy/client/generated/models/transcode_hw_accel.py +37 -0
  391. immichpy/client/generated/models/transcode_policy.py +37 -0
  392. immichpy/client/generated/models/trash_response_dto.py +82 -0
  393. immichpy/client/generated/models/update_album_dto.py +106 -0
  394. immichpy/client/generated/models/update_album_user_dto.py +83 -0
  395. immichpy/client/generated/models/update_asset_dto.py +135 -0
  396. immichpy/client/generated/models/update_library_dto.py +95 -0
  397. immichpy/client/generated/models/usage_by_user_dto.py +117 -0
  398. immichpy/client/generated/models/user_admin_create_dto.py +136 -0
  399. immichpy/client/generated/models/user_admin_delete_dto.py +82 -0
  400. immichpy/client/generated/models/user_admin_response_dto.py +176 -0
  401. immichpy/client/generated/models/user_admin_update_dto.py +141 -0
  402. immichpy/client/generated/models/user_avatar_color.py +42 -0
  403. immichpy/client/generated/models/user_license.py +91 -0
  404. immichpy/client/generated/models/user_metadata_key.py +35 -0
  405. immichpy/client/generated/models/user_preferences_response_dto.py +188 -0
  406. immichpy/client/generated/models/user_preferences_update_dto.py +199 -0
  407. immichpy/client/generated/models/user_response_dto.py +105 -0
  408. immichpy/client/generated/models/user_status.py +35 -0
  409. immichpy/client/generated/models/user_update_me_dto.py +98 -0
  410. immichpy/client/generated/models/validate_access_token_response_dto.py +82 -0
  411. immichpy/client/generated/models/validate_library_dto.py +93 -0
  412. immichpy/client/generated/models/validate_library_import_path_response_dto.py +92 -0
  413. immichpy/client/generated/models/validate_library_response_dto.py +103 -0
  414. immichpy/client/generated/models/version_check_state_response_dto.py +98 -0
  415. immichpy/client/generated/models/video_codec.py +36 -0
  416. immichpy/client/generated/models/video_container.py +36 -0
  417. immichpy/client/generated/models/workflow_action_item_dto.py +89 -0
  418. immichpy/client/generated/models/workflow_action_response_dto.py +105 -0
  419. immichpy/client/generated/models/workflow_create_dto.py +132 -0
  420. immichpy/client/generated/models/workflow_filter_item_dto.py +89 -0
  421. immichpy/client/generated/models/workflow_filter_response_dto.py +105 -0
  422. immichpy/client/generated/models/workflow_response_dto.py +163 -0
  423. immichpy/client/generated/models/workflow_update_dto.py +128 -0
  424. immichpy/client/generated/py.typed +0 -0
  425. immichpy/client/generated/rest.py +199 -0
  426. immichpy/client/main.py +375 -0
  427. immichpy/client/types.py +65 -0
  428. immichpy/client/utils/download.py +249 -0
  429. immichpy/client/utils/upload.py +462 -0
  430. immichpy/client/wrapper/__init__.py +0 -0
  431. immichpy/client/wrapper/assets_api_wrapped.py +254 -0
  432. immichpy/client/wrapper/download_api_wrapped.py +118 -0
  433. immichpy/client/wrapper/users_api_wrapped.py +54 -0
  434. immichpy/py.typed +0 -0
  435. immichpy-1.6.5.dist-info/METADATA +67 -0
  436. immichpy-1.6.5.dist-info/RECORD +439 -0
  437. immichpy-1.6.5.dist-info/WHEEL +4 -0
  438. immichpy-1.6.5.dist-info/entry_points.txt +3 -0
  439. immichpy-1.6.5.dist-info/licenses/LICENSE +9 -0
immichpy/cli/main.py ADDED
@@ -0,0 +1,248 @@
1
+ """Main CLI entrypoint."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import sys
6
+ import typer
7
+ import click
8
+ from typing import Optional
9
+ from importlib.metadata import version
10
+
11
+ from rich.console import Console
12
+
13
+ from immichpy.cli.consts import (
14
+ API_KEY_URL,
15
+ DEFAULT_FORMAT,
16
+ DEFAULT_PROFILE,
17
+ IMMICH_API_KEY,
18
+ IMMICH_ACCESS_TOKEN,
19
+ IMMICH_API_URL,
20
+ IMMICH_FORMAT,
21
+ IMMICH_PROFILE,
22
+ )
23
+ from immichpy.cli.utils import resolve_client_config, mask, print_
24
+
25
+ from immichpy import AsyncClient
26
+ from immichpy.cli.types import FormatMode, ClientConfig
27
+
28
+ # Import command modules
29
+ from immichpy.cli.commands import api_keys as api_keys_commands
30
+ from immichpy.cli.commands import activities as activities_commands
31
+ from immichpy.cli.commands import albums as albums_commands
32
+ from immichpy.cli.wrapper import assets as assets_wrapper
33
+ from immichpy.cli.commands import authentication as authentication_commands
34
+ from immichpy.cli.commands import authentication_admin as authentication_admin_commands
35
+ from immichpy.cli.wrapper import download as download_wrapper
36
+ from immichpy.cli.wrapper import config as config_commands
37
+ from immichpy.cli.wrapper import setup as setup_commands
38
+ from immichpy.cli.commands import duplicates as duplicates_commands
39
+ from immichpy.cli.commands import faces as faces_commands
40
+ from immichpy.cli.commands import jobs as jobs_commands
41
+ from immichpy.cli.commands import libraries as libraries_commands
42
+ from immichpy.cli.commands import maintenance_admin as maintenance_admin_commands
43
+ from immichpy.cli.commands import map as map_commands
44
+ from immichpy.cli.commands import memories as memories_commands
45
+ from immichpy.cli.commands import notifications as notifications_commands
46
+ from immichpy.cli.commands import notifications_admin as notifications_admin_commands
47
+ from immichpy.cli.commands import partners as partners_commands
48
+ from immichpy.cli.commands import people as people_commands
49
+ from immichpy.cli.commands import plugins as plugins_commands
50
+ from immichpy.cli.commands import queues as queues_commands
51
+ from immichpy.cli.commands import search as search_commands
52
+ from immichpy.cli.commands import server as server_commands
53
+ from immichpy.cli.commands import sessions as sessions_commands
54
+ from immichpy.cli.commands import shared_links as shared_links_commands
55
+ from immichpy.cli.commands import stacks as stacks_commands
56
+ from immichpy.cli.commands import sync as sync_commands
57
+ from immichpy.cli.commands import system_config as system_config_commands
58
+ from immichpy.cli.commands import system_metadata as system_metadata_commands
59
+ from immichpy.cli.commands import tags as tags_commands
60
+ from immichpy.cli.commands import timeline as timeline_commands
61
+ from immichpy.cli.commands import trash as trash_commands
62
+ from immichpy.cli.wrapper import users as users_wrapper
63
+ from immichpy.cli.commands import users_admin as users_admin_commands
64
+ from immichpy.cli.commands import views as views_commands
65
+ from immichpy.cli.commands import workflows as workflows_commands
66
+
67
+ # Global state
68
+ app = typer.Typer(
69
+ context_settings={"help_option_names": ["-h", "--help"]}, no_args_is_help=True
70
+ )
71
+ console = Console()
72
+ stderr_console = Console(file=sys.stderr)
73
+
74
+ # Add command modules to the main app
75
+ app.add_typer(api_keys_commands.app, name="api-keys", rich_help_panel="API commands")
76
+ app.add_typer(
77
+ activities_commands.app, name="activities", rich_help_panel="API commands"
78
+ )
79
+ app.add_typer(albums_commands.app, name="albums", rich_help_panel="API commands")
80
+ app.add_typer(assets_wrapper.app, name="assets", rich_help_panel="API commands")
81
+ app.add_typer(authentication_commands.app, name="auth", rich_help_panel="API commands")
82
+ app.add_typer(
83
+ authentication_admin_commands.app,
84
+ name="auth-admin",
85
+ rich_help_panel="API commands",
86
+ )
87
+ app.add_typer(download_wrapper.app, name="download", rich_help_panel="API commands")
88
+ app.add_typer(config_commands.app, name="config", rich_help_panel="Custom commands")
89
+ app.command(rich_help_panel="Custom commands")(setup_commands.setup)
90
+ app.add_typer(
91
+ duplicates_commands.app, name="duplicates", rich_help_panel="API commands"
92
+ )
93
+ app.add_typer(faces_commands.app, name="faces", rich_help_panel="API commands")
94
+ app.add_typer(jobs_commands.app, name="jobs", rich_help_panel="API commands")
95
+ app.add_typer(libraries_commands.app, name="libraries", rich_help_panel="API commands")
96
+ app.add_typer(
97
+ maintenance_admin_commands.app,
98
+ name="maintenance-admin",
99
+ rich_help_panel="API commands",
100
+ )
101
+ app.add_typer(map_commands.app, name="map", rich_help_panel="API commands")
102
+ app.add_typer(memories_commands.app, name="memories", rich_help_panel="API commands")
103
+ app.add_typer(
104
+ notifications_commands.app, name="notifications", rich_help_panel="API commands"
105
+ )
106
+ app.add_typer(
107
+ notifications_admin_commands.app,
108
+ name="notifications-admin",
109
+ rich_help_panel="API commands",
110
+ )
111
+ app.add_typer(partners_commands.app, name="partners", rich_help_panel="API commands")
112
+ app.add_typer(people_commands.app, name="people", rich_help_panel="API commands")
113
+ app.add_typer(plugins_commands.app, name="plugins", rich_help_panel="API commands")
114
+ app.add_typer(queues_commands.app, name="queues", rich_help_panel="API commands")
115
+ app.add_typer(search_commands.app, name="search", rich_help_panel="API commands")
116
+ app.add_typer(server_commands.app, name="server", rich_help_panel="API commands")
117
+ app.add_typer(sessions_commands.app, name="sessions", rich_help_panel="API commands")
118
+ app.add_typer(
119
+ shared_links_commands.app, name="shared-links", rich_help_panel="API commands"
120
+ )
121
+ app.add_typer(stacks_commands.app, name="stacks", rich_help_panel="API commands")
122
+ app.add_typer(sync_commands.app, name="sync", rich_help_panel="API commands")
123
+ app.add_typer(
124
+ system_config_commands.app, name="system-config", rich_help_panel="API commands"
125
+ )
126
+ app.add_typer(
127
+ system_metadata_commands.app, name="system-metadata", rich_help_panel="API commands"
128
+ )
129
+ app.add_typer(tags_commands.app, name="tags", rich_help_panel="API commands")
130
+ app.add_typer(timeline_commands.app, name="timeline", rich_help_panel="API commands")
131
+ app.add_typer(trash_commands.app, name="trash", rich_help_panel="API commands")
132
+ app.add_typer(users_wrapper.app, name="users", rich_help_panel="API commands")
133
+ app.add_typer(
134
+ users_admin_commands.app, name="users-admin", rich_help_panel="API commands"
135
+ )
136
+ app.add_typer(views_commands.app, name="views", rich_help_panel="API commands")
137
+ app.add_typer(workflows_commands.app, name="workflows", rich_help_panel="API commands")
138
+
139
+
140
+ def version_callback(value: bool) -> None: # pragma: no cover
141
+ if value:
142
+ print_(f"immich CLI (unofficial) {version('immich')}", type="text")
143
+ raise typer.Exit(0)
144
+
145
+
146
+ @app.callback(invoke_without_command=False)
147
+ def callback(
148
+ ctx: typer.Context,
149
+ verbose: bool = typer.Option(
150
+ False,
151
+ "--verbose",
152
+ "-v",
153
+ help="Show verbose output.",
154
+ ),
155
+ format_mode: FormatMode = typer.Option(
156
+ DEFAULT_FORMAT,
157
+ "--format",
158
+ help="Output format of the CLI.",
159
+ envvar=IMMICH_FORMAT,
160
+ ),
161
+ api_key: Optional[str] = typer.Option(
162
+ None,
163
+ "--api-key",
164
+ help=f"Authorize via API key (get one [link={API_KEY_URL}]here[/link]).",
165
+ envvar=IMMICH_API_KEY,
166
+ ),
167
+ access_token: Optional[str] = typer.Option(
168
+ None,
169
+ "--access-token",
170
+ help="Authorize via access token.",
171
+ envvar=IMMICH_ACCESS_TOKEN,
172
+ ),
173
+ base_url: Optional[str] = typer.Option(
174
+ None,
175
+ "--base-url",
176
+ help="The server to connect to.",
177
+ envvar=IMMICH_API_URL,
178
+ ),
179
+ profile: str = typer.Option(
180
+ DEFAULT_PROFILE,
181
+ "--profile",
182
+ "-p",
183
+ envvar=IMMICH_PROFILE,
184
+ help="The profile to use.",
185
+ ),
186
+ _version: bool = typer.Option(
187
+ False,
188
+ "--version",
189
+ callback=version_callback,
190
+ is_eager=True,
191
+ help="Show version and exit.",
192
+ ),
193
+ ) -> None: # pragma: no cover
194
+ ctx.ensure_object(dict)
195
+ ctx.obj["format"] = format_mode
196
+ ctx.obj["verbose"] = verbose
197
+ if ctx.invoked_subcommand is not None and ctx.invoked_subcommand not in [
198
+ "setup",
199
+ "config",
200
+ ]:
201
+ config = resolve_client_config(
202
+ ClientConfig(
203
+ api_key=api_key,
204
+ access_token=access_token,
205
+ base_url=base_url,
206
+ ),
207
+ profile=profile,
208
+ # we only consider the profile explicit if it was set via the command line
209
+ # environment variables are not considered explicit
210
+ profile_explicit=ctx.get_parameter_source("profile")
211
+ == click.core.ParameterSource.COMMANDLINE,
212
+ )
213
+ if not config.base_url:
214
+ print_(
215
+ "No base URL provided. Run 'immich setup' to set up a profile or use '--base-url' to specify a base URL.",
216
+ type="error",
217
+ )
218
+ raise typer.Exit(code=1)
219
+ if ctx.obj["verbose"]:
220
+ cli_vars = {
221
+ k: v
222
+ for k, v in ctx.params.items()
223
+ if k in ClientConfig.model_fields.keys() and v is not None
224
+ }
225
+ print_("Configuration used:", type="debug", ctx=ctx)
226
+ for field in ClientConfig.model_fields.keys():
227
+ value = getattr(config, field)
228
+ if field in ("api_key", "access_token") and value:
229
+ value = mask(value)
230
+ elif value is None:
231
+ value = "None"
232
+ source = "cli/env" if field in cli_vars else f"profile '{profile}'"
233
+ print_(f"- {field}: {value} (from {source})", type="debug", ctx=ctx)
234
+ if omit_access_token := (config.api_key is not None):
235
+ print_(
236
+ "Omitting access token because API key is provided.",
237
+ type="debug",
238
+ ctx=ctx,
239
+ )
240
+ ctx.obj["client"] = AsyncClient(
241
+ api_key=config.api_key,
242
+ access_token=None if omit_access_token else config.access_token,
243
+ base_url=config.base_url,
244
+ )
245
+
246
+
247
+ if __name__ == "__main__": # pragma: no cover
248
+ app()
@@ -0,0 +1,97 @@
1
+ """Runtime helpers for executing async client calls and handling output."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import asyncio
6
+ import json
7
+ import traceback
8
+ from typing import Any, Awaitable, Callable
9
+
10
+ from immichpy.cli.utils import print_
11
+ from pydantic import BaseModel
12
+ from typer import Context, Exit
13
+
14
+ from immichpy.cli.types import MaybeBaseModel
15
+
16
+ from immichpy import AsyncClient
17
+ from immichpy.client.generated.exceptions import ApiException
18
+
19
+
20
+ def set_nested(d: dict[str, Any], path: list[str], value: Any) -> None:
21
+ """Set a nested dictionary value using a path list.
22
+
23
+ Example: set_nested({}, ['user', 'name'], 'John') -> {'user': {'name': 'John'}}
24
+ """
25
+ current = d
26
+ for part in path[:-1]:
27
+ if part not in current:
28
+ current[part] = {}
29
+ elif not isinstance(current[part], dict):
30
+ current[part] = {}
31
+ current = current[part]
32
+ current[path[-1]] = value
33
+
34
+
35
+ def print_response(data: MaybeBaseModel, ctx: Context) -> None:
36
+ """Print response data."""
37
+
38
+ def convert_to_dict(obj: MaybeBaseModel) -> Any:
39
+ """Recursively convert Pydantic models to dicts."""
40
+ if isinstance(obj, list):
41
+ return [convert_to_dict(item) for item in obj]
42
+ elif isinstance(obj, BaseModel):
43
+ return obj.model_dump()
44
+ else:
45
+ return obj
46
+
47
+ json_str = json.dumps(convert_to_dict(data), default=str)
48
+
49
+ print_(message=json_str, type="json", ctx=ctx)
50
+
51
+
52
+ def format_api_error(e: ApiException) -> tuple[str, int]:
53
+ """Return (message, exit_code) for an ApiException."""
54
+ exit_code = 1 if e.status is None else e.status // 100
55
+
56
+ if not e.body:
57
+ return ("API error", exit_code)
58
+
59
+ if isinstance(e.body, str):
60
+ return (e.body, exit_code)
61
+
62
+ return (json.dumps(e.body, default=str), exit_code)
63
+
64
+
65
+ async def run_async(coro: Awaitable[Any]) -> Any:
66
+ return await coro
67
+
68
+
69
+ def run_command(
70
+ client: AsyncClient,
71
+ api_group: Any,
72
+ method_name: str,
73
+ ctx: Context | None = None,
74
+ **kwargs: Any,
75
+ ) -> Any:
76
+ """Run a client API method and handle the result."""
77
+ method: Callable[..., Awaitable[Any]] = getattr(api_group, method_name)
78
+
79
+ async def _call_and_close() -> Any:
80
+ try:
81
+ return await method(**kwargs)
82
+ finally:
83
+ await client.close()
84
+
85
+ try:
86
+ return asyncio.run(_call_and_close())
87
+
88
+ except ApiException as e:
89
+ message, code = format_api_error(e)
90
+ print_(message, type="error", ctx=ctx)
91
+ print_(traceback.format_exc(), type="debug", ctx=ctx)
92
+ raise Exit(code=code)
93
+
94
+ except Exception as e:
95
+ print_(f"Unexpected error: {str(e).strip()}", type="error", ctx=ctx)
96
+ print_(traceback.format_exc(), type="debug", ctx=ctx)
97
+ raise Exit(code=1)
immichpy/cli/types.py ADDED
@@ -0,0 +1,17 @@
1
+ from typing import Literal, Optional, Union
2
+
3
+ from pydantic import BaseModel
4
+
5
+ MaybeBaseModel = Optional[Union[BaseModel, list[BaseModel], str]]
6
+ FormatMode = Literal["pretty", "json", "table"]
7
+ PrintType = Literal["info", "warning", "error", "debug", "success", "json", "text"]
8
+
9
+
10
+ class ClientConfig(BaseModel):
11
+ """
12
+ A configuration for a client to connect to an Immich server.
13
+ """
14
+
15
+ base_url: Optional[str]
16
+ api_key: Optional[str]
17
+ access_token: Optional[str]
immichpy/cli/utils.py ADDED
@@ -0,0 +1,227 @@
1
+ from typing import Any, Optional, cast, overload
2
+ import json
3
+
4
+ from rich import print as print_rich, print_json
5
+ from rich.table import Table
6
+ import rtoml
7
+ import typer
8
+
9
+ from immichpy.cli.consts import CONFIG_FILE, DEFAULT_FORMAT, SECRET_KEYS
10
+ from immichpy.cli.types import ClientConfig, FormatMode, PrintType
11
+
12
+
13
+ def set_path(data: dict[str, Any], path: str, value: Any) -> None:
14
+ """
15
+ Set a nested dictionary value using a path list.
16
+
17
+ Example: set_path({}, 'user.name', 'John') -> {'user': {'name': 'John'}}
18
+ """
19
+ parts = path.split(".")
20
+ cur = data
21
+ for p in parts[:-1]:
22
+ cur = cur.setdefault(p, {})
23
+ cur[parts[-1]] = value
24
+
25
+
26
+ def get_path(data: dict[str, Any], path: str) -> Any:
27
+ """
28
+ Get a nested dictionary value using a path list.
29
+ """
30
+ parts = path.split(".")
31
+ cur = data
32
+ for p in parts:
33
+ cur = cur[p]
34
+ return cur
35
+
36
+
37
+ def load_config() -> dict[str, Any]:
38
+ """
39
+ Load the config file. Returns an empty dict if the file does not exist.
40
+ """
41
+ if not CONFIG_FILE.exists():
42
+ return {}
43
+ return rtoml.load(CONFIG_FILE, none_value="")
44
+
45
+
46
+ def write_config(data: dict[str, Any]) -> None:
47
+ """
48
+ Write the config data to the config file and set permissions.
49
+ """
50
+ CONFIG_FILE.parent.mkdir(parents=True, exist_ok=True)
51
+ with CONFIG_FILE.open("w") as f:
52
+ rtoml.dump(data, f, none_value="")
53
+ CONFIG_FILE.chmod(0o600)
54
+
55
+
56
+ def check_config() -> None:
57
+ """
58
+ Check if the config file exists.
59
+ """
60
+ if not CONFIG_FILE.exists():
61
+ print_(
62
+ "Config file does not exist. Run [bold]immich config set[/bold] to create it.",
63
+ type="error",
64
+ )
65
+ raise typer.Exit(code=1)
66
+
67
+
68
+ def resolve_client_config(
69
+ config: ClientConfig, profile: str, profile_explicit: bool = False
70
+ ) -> ClientConfig:
71
+ """
72
+ Resolve the client config from the config file.
73
+
74
+ :param config: The config to resolve
75
+ :param profile: The profile to use
76
+ :param profile_explicit: Whether the profile was explicitly set by the user
77
+ :raises typer.Exit: If the profile is not found in the config
78
+ :return: The resolved config
79
+ """
80
+ data = load_config()
81
+ profiles: dict[str, Any] = data.get("profiles", {})
82
+
83
+ if profile_explicit and profile not in profiles:
84
+ print_(
85
+ f"Profile '{profile}' not found. Run immich config set --profile {profile}.",
86
+ type="error",
87
+ )
88
+ raise typer.Exit(code=1)
89
+
90
+ profile_data = profiles.get(profile, {})
91
+
92
+ return ClientConfig(
93
+ api_key=config.api_key or profile_data.get("api_key"),
94
+ access_token=config.access_token or profile_data.get("access_token"),
95
+ base_url=config.base_url or profile_data.get("base_url"),
96
+ )
97
+
98
+
99
+ def _is_secret_key(key: str) -> bool:
100
+ """Check if a key indicates a secret value."""
101
+ return any(secret in key.lower() for secret in SECRET_KEYS)
102
+
103
+
104
+ def _redact_secret(secret: str, start: int = 3, end: int = 3) -> str:
105
+ """
106
+ Redact a secret by showing only the first `start` and last `end` characters.
107
+
108
+ :param secret: The secret string to redact
109
+ :param start: Number of characters to show at the start (default: 3)
110
+ :param end: Number of characters to show at the end (default: 3)
111
+ :return: The redacted secret string
112
+ """
113
+ if not secret:
114
+ return ""
115
+
116
+ length = len(secret)
117
+ if length <= start + end:
118
+ return "*" * length
119
+
120
+ return secret[:start] + "*" * (length - start - end) + secret[-end:]
121
+
122
+
123
+ @overload
124
+ def mask(secret: str, start: int = 3, end: int = 3) -> str: ...
125
+
126
+
127
+ @overload
128
+ def mask(obj: Any, start: int = 3, end: int = 3, key: Optional[str] = None) -> Any: ...
129
+
130
+
131
+ def mask(obj: Any, start: int = 3, end: int = 3, key: Optional[str] = None) -> Any:
132
+ """
133
+ Recursively mask secret values in nested dictionaries and lists.
134
+
135
+ :param obj: The object to mask (dict, list, or any other type)
136
+ :param start: Number of characters to show at the start (default: 3)
137
+ :param end: Number of characters to show at the end (default: 3)
138
+ :param key: Optional key path for top-level values to check if they're secrets
139
+ :return: The masked object with secrets redacted using _redact_secret
140
+ """
141
+ if isinstance(obj, dict):
142
+ return {
143
+ k: (
144
+ _redact_secret(v, start, end)
145
+ if _is_secret_key(k) and isinstance(v, str)
146
+ else mask(v, start, end, k)
147
+ )
148
+ for k, v in obj.items()
149
+ }
150
+ if isinstance(obj, list):
151
+ return [mask(v, start, end) for v in obj]
152
+ if isinstance(obj, str):
153
+ if key is None or _is_secret_key(key):
154
+ return _redact_secret(obj, start, end)
155
+ return obj
156
+
157
+
158
+ def _print_table(data: Any) -> None: # pragma: no cover
159
+ """
160
+ Print data as a table using rich.
161
+ For dicts: creates a table with key/value columns.
162
+ For lists: prints multiple tables.
163
+ """
164
+ if isinstance(data, list):
165
+ for item in data:
166
+ _print_table(item)
167
+ elif isinstance(data, dict):
168
+ _print_dict_table(data)
169
+ else:
170
+ print_rich(str(data))
171
+
172
+
173
+ def _print_dict_table(data: dict[str, Any]) -> None: # pragma: no cover
174
+ """Print a dictionary as a table with key and value columns."""
175
+ table = Table(show_header=True, header_style="bold")
176
+ table.add_column("Key", style="cyan")
177
+ table.add_column("Value", style="green")
178
+
179
+ for key, value in data.items():
180
+ table.add_row(key, str(value))
181
+
182
+ print_rich(table)
183
+
184
+
185
+ def print_(
186
+ message: str,
187
+ *,
188
+ type: PrintType,
189
+ ctx: Optional[typer.Context] = None,
190
+ ) -> None:
191
+ """
192
+ Print a message in the given format as a rich print or a plain print.
193
+ :param message: The message to print
194
+ :param type: The type of the message
195
+ :param ctx: The context to use
196
+ """
197
+ match type:
198
+ case "json":
199
+ try:
200
+ format_mode = cast(FormatMode, ctx.obj.get("format", DEFAULT_FORMAT)) # type: ignore[possibly-missing-attribute]
201
+ except (AttributeError, KeyError):
202
+ format_mode = DEFAULT_FORMAT
203
+ match format_mode:
204
+ case "pretty":
205
+ print_json(message)
206
+ case "json":
207
+ print(message)
208
+ case "table": # pragma: no cover
209
+ try:
210
+ data = json.loads(message)
211
+ _print_table(data)
212
+ except json.JSONDecodeError:
213
+ print(message)
214
+ print_("The 'table' format is experimental.", type="warning")
215
+ case "text":
216
+ print(message)
217
+ case "info":
218
+ print_rich(message)
219
+ case "warning":
220
+ print_rich(f"[yellow][bold][Warning][/bold] {message}[/yellow]")
221
+ case "error":
222
+ print_rich(f"[red][bold][Error][/bold] {message}[/red]")
223
+ case "success":
224
+ print_rich(f"[green][bold][Success][/bold][/green] {message}")
225
+ case "debug":
226
+ if ctx is not None and ctx.obj["verbose"]:
227
+ print_rich(f"[blue][bold][Debug][/bold] {message}[/blue]")