light-phone-api 0.1.0__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.
- light_api/__init__.py +11 -0
- light_api/client.py +273 -0
- light_api/music.py +552 -0
- light_api/notes.py +228 -0
- light_api/podcast.py +177 -0
- light_api/tools.py +175 -0
- light_phone_api-0.1.0.dist-info/METADATA +11 -0
- light_phone_api-0.1.0.dist-info/RECORD +294 -0
- light_phone_api-0.1.0.dist-info/WHEEL +4 -0
- open_api_specification_client/__init__.py +8 -0
- open_api_specification_client/api/__init__.py +1 -0
- open_api_specification_client/api/default/__init__.py +1 -0
- open_api_specification_client/api/default/delete_api_audios_audio_id.py +102 -0
- open_api_specification_client/api/default/delete_api_device_tools_device_tool_id.py +102 -0
- open_api_specification_client/api/default/delete_api_followed_podcasts_followed_podcast_id.py +165 -0
- open_api_specification_client/api/default/delete_api_notes_note_id.py +102 -0
- open_api_specification_client/api/default/get_api_audio_capacity.py +168 -0
- open_api_specification_client/api/default/get_api_devices.py +136 -0
- open_api_specification_client/api/default/get_api_followed_podcasts.py +168 -0
- open_api_specification_client/api/default/get_api_notes.py +166 -0
- open_api_specification_client/api/default/get_api_notes_note_id.py +192 -0
- open_api_specification_client/api/default/get_api_notes_note_id_generate_presigned_get_url.py +165 -0
- open_api_specification_client/api/default/get_api_notes_note_id_generate_presigned_put_url.py +165 -0
- open_api_specification_client/api/default/get_api_playlist_items.py +183 -0
- open_api_specification_client/api/default/get_api_playlists.py +168 -0
- open_api_specification_client/api/default/get_api_podcasts.py +168 -0
- open_api_specification_client/api/default/get_api_tools.py +166 -0
- open_api_specification_client/api/default/get_api_tools_tool_id.py +163 -0
- open_api_specification_client/api/default/get_api_users_current.py +136 -0
- open_api_specification_client/api/default/patch_api_audios_audio_id.py +184 -0
- open_api_specification_client/api/default/patch_api_notes_note_id.py +192 -0
- open_api_specification_client/api/default/patch_api_playlist_items_playlist_item_id.py +186 -0
- open_api_specification_client/api/default/post_api_audios.py +168 -0
- open_api_specification_client/api/default/post_api_audios_delete_all.py +103 -0
- open_api_specification_client/api/default/post_api_authorizations.py +160 -0
- open_api_specification_client/api/default/post_api_device_tools.py +168 -0
- open_api_specification_client/api/default/post_api_followed_podcasts.py +168 -0
- open_api_specification_client/api/default/post_api_notes.py +168 -0
- open_api_specification_client/api/default/post_api_playlists_sort_mode.py +168 -0
- open_api_specification_client/api/default/post_api_podcasts.py +168 -0
- open_api_specification_client/client.py +268 -0
- open_api_specification_client/errors.py +16 -0
- open_api_specification_client/models/__init__.py +719 -0
- open_api_specification_client/models/delete_api_followed_podcasts_followed_podcast_id_response_200.py +117 -0
- open_api_specification_client/models/delete_api_followed_podcasts_followed_podcast_id_response_200_data_item.py +105 -0
- open_api_specification_client/models/delete_api_followed_podcasts_followed_podcast_id_response_200_data_item_attributes.py +69 -0
- open_api_specification_client/models/delete_api_followed_podcasts_followed_podcast_id_response_200_data_item_relationships.py +89 -0
- open_api_specification_client/models/delete_api_followed_podcasts_followed_podcast_id_response_200_data_item_relationships_device_tool.py +73 -0
- open_api_specification_client/models/delete_api_followed_podcasts_followed_podcast_id_response_200_data_item_relationships_device_tool_data.py +69 -0
- open_api_specification_client/models/delete_api_followed_podcasts_followed_podcast_id_response_200_data_item_relationships_podcast.py +73 -0
- open_api_specification_client/models/delete_api_followed_podcasts_followed_podcast_id_response_200_data_item_relationships_podcast_data.py +69 -0
- open_api_specification_client/models/delete_api_followed_podcasts_followed_podcast_id_response_200_included_item.py +115 -0
- open_api_specification_client/models/delete_api_followed_podcasts_followed_podcast_id_response_200_included_item_attributes.py +134 -0
- open_api_specification_client/models/delete_api_followed_podcasts_followed_podcast_id_response_200_included_item_attributes_config.py +47 -0
- open_api_specification_client/models/delete_api_followed_podcasts_followed_podcast_id_response_200_included_item_relationships.py +89 -0
- open_api_specification_client/models/delete_api_followed_podcasts_followed_podcast_id_response_200_included_item_relationships_device.py +73 -0
- open_api_specification_client/models/delete_api_followed_podcasts_followed_podcast_id_response_200_included_item_relationships_device_data.py +69 -0
- open_api_specification_client/models/delete_api_followed_podcasts_followed_podcast_id_response_200_included_item_relationships_tool.py +73 -0
- open_api_specification_client/models/delete_api_followed_podcasts_followed_podcast_id_response_200_included_item_relationships_tool_data.py +69 -0
- open_api_specification_client/models/delete_api_followed_podcasts_followed_podcast_id_response_200_jsonapi.py +61 -0
- open_api_specification_client/models/get_api_audio_capacity_response_200.py +85 -0
- open_api_specification_client/models/get_api_devices_response_200.py +103 -0
- open_api_specification_client/models/get_api_devices_response_200_data_item.py +99 -0
- open_api_specification_client/models/get_api_devices_response_200_data_item_attributes.py +85 -0
- open_api_specification_client/models/get_api_devices_response_200_data_item_relationships.py +115 -0
- open_api_specification_client/models/get_api_devices_response_200_data_item_relationships_device_tool_location.py +71 -0
- open_api_specification_client/models/get_api_devices_response_200_data_item_relationships_device_tool_location_data.py +69 -0
- open_api_specification_client/models/get_api_devices_response_200_data_item_relationships_device_tools.py +79 -0
- open_api_specification_client/models/get_api_devices_response_200_data_item_relationships_device_tools_data_item.py +69 -0
- open_api_specification_client/models/get_api_devices_response_200_data_item_relationships_sim.py +71 -0
- open_api_specification_client/models/get_api_devices_response_200_data_item_relationships_sim_data.py +69 -0
- open_api_specification_client/models/get_api_devices_response_200_data_item_relationships_user.py +71 -0
- open_api_specification_client/models/get_api_devices_response_200_data_item_relationships_user_data.py +69 -0
- open_api_specification_client/models/get_api_devices_response_200_included_item.py +101 -0
- open_api_specification_client/models/get_api_devices_response_200_included_item_attributes.py +132 -0
- open_api_specification_client/models/get_api_devices_response_200_included_item_attributes_config.py +114 -0
- open_api_specification_client/models/get_api_devices_response_200_included_item_attributes_config_address.py +117 -0
- open_api_specification_client/models/get_api_devices_response_200_included_item_relationships.py +117 -0
- open_api_specification_client/models/get_api_devices_response_200_included_item_relationships_device.py +71 -0
- open_api_specification_client/models/get_api_devices_response_200_included_item_relationships_device_data.py +69 -0
- open_api_specification_client/models/get_api_devices_response_200_included_item_relationships_tool.py +71 -0
- open_api_specification_client/models/get_api_devices_response_200_included_item_relationships_tool_data.py +69 -0
- open_api_specification_client/models/get_api_devices_response_200_included_item_relationships_user.py +71 -0
- open_api_specification_client/models/get_api_devices_response_200_included_item_relationships_user_data.py +69 -0
- open_api_specification_client/models/get_api_devices_response_200_jsonapi.py +61 -0
- open_api_specification_client/models/get_api_followed_podcasts_response_200.py +107 -0
- open_api_specification_client/models/get_api_followed_podcasts_response_200_data_item.py +101 -0
- open_api_specification_client/models/get_api_followed_podcasts_response_200_data_item_attributes.py +69 -0
- open_api_specification_client/models/get_api_followed_podcasts_response_200_data_item_relationships.py +85 -0
- open_api_specification_client/models/get_api_followed_podcasts_response_200_data_item_relationships_device_tool.py +71 -0
- open_api_specification_client/models/get_api_followed_podcasts_response_200_data_item_relationships_device_tool_data.py +69 -0
- open_api_specification_client/models/get_api_followed_podcasts_response_200_data_item_relationships_podcast.py +71 -0
- open_api_specification_client/models/get_api_followed_podcasts_response_200_data_item_relationships_podcast_data.py +69 -0
- open_api_specification_client/models/get_api_followed_podcasts_response_200_included_item.py +111 -0
- open_api_specification_client/models/get_api_followed_podcasts_response_200_included_item_attributes.py +155 -0
- open_api_specification_client/models/get_api_followed_podcasts_response_200_included_item_attributes_config.py +47 -0
- open_api_specification_client/models/get_api_followed_podcasts_response_200_included_item_relationships.py +85 -0
- open_api_specification_client/models/get_api_followed_podcasts_response_200_included_item_relationships_device.py +71 -0
- open_api_specification_client/models/get_api_followed_podcasts_response_200_included_item_relationships_device_data.py +69 -0
- open_api_specification_client/models/get_api_followed_podcasts_response_200_included_item_relationships_tool.py +71 -0
- open_api_specification_client/models/get_api_followed_podcasts_response_200_included_item_relationships_tool_data.py +69 -0
- open_api_specification_client/models/get_api_followed_podcasts_response_200_jsonapi.py +61 -0
- open_api_specification_client/models/get_api_notes_note_id_generate_presigned_get_url_response_200.py +61 -0
- open_api_specification_client/models/get_api_notes_note_id_generate_presigned_put_url_response_200.py +61 -0
- open_api_specification_client/models/get_api_notes_note_id_response_200.py +97 -0
- open_api_specification_client/models/get_api_notes_note_id_response_200_data.py +94 -0
- open_api_specification_client/models/get_api_notes_note_id_response_200_data_attributes.py +88 -0
- open_api_specification_client/models/get_api_notes_note_id_response_200_included_item.py +96 -0
- open_api_specification_client/models/get_api_notes_note_id_response_200_included_item_attributes.py +61 -0
- open_api_specification_client/models/get_api_notes_response_200.py +110 -0
- open_api_specification_client/models/get_api_notes_response_200_data_item.py +95 -0
- open_api_specification_client/models/get_api_notes_response_200_data_item_attributes.py +93 -0
- open_api_specification_client/models/get_api_notes_response_200_data_item_relationships.py +71 -0
- open_api_specification_client/models/get_api_notes_response_200_data_item_relationships_file.py +71 -0
- open_api_specification_client/models/get_api_notes_response_200_data_item_relationships_file_data.py +69 -0
- open_api_specification_client/models/get_api_notes_response_200_included_item.py +87 -0
- open_api_specification_client/models/get_api_notes_response_200_included_item_attributes.py +113 -0
- open_api_specification_client/models/get_api_notes_response_200_jsonapi.py +61 -0
- open_api_specification_client/models/get_api_playlist_items_response_200.py +112 -0
- open_api_specification_client/models/get_api_playlist_items_response_200_data_item.py +101 -0
- open_api_specification_client/models/get_api_playlist_items_response_200_data_item_attributes.py +77 -0
- open_api_specification_client/models/get_api_playlist_items_response_200_data_item_relationships.py +71 -0
- open_api_specification_client/models/get_api_playlist_items_response_200_data_item_relationships_audio.py +71 -0
- open_api_specification_client/models/get_api_playlist_items_response_200_data_item_relationships_audio_data.py +69 -0
- open_api_specification_client/models/get_api_playlist_items_response_200_included_item.py +111 -0
- open_api_specification_client/models/get_api_playlist_items_response_200_included_item_attributes.py +153 -0
- open_api_specification_client/models/get_api_playlist_items_response_200_included_item_relationships.py +87 -0
- open_api_specification_client/models/get_api_playlist_items_response_200_included_item_relationships_processed_file.py +71 -0
- open_api_specification_client/models/get_api_playlist_items_response_200_included_item_relationships_processed_file_data.py +69 -0
- open_api_specification_client/models/get_api_playlist_items_response_200_included_item_relationships_raw_file.py +71 -0
- open_api_specification_client/models/get_api_playlist_items_response_200_included_item_relationships_raw_file_data.py +69 -0
- open_api_specification_client/models/get_api_playlist_items_response_200_jsonapi.py +61 -0
- open_api_specification_client/models/get_api_playlists_response_200.py +103 -0
- open_api_specification_client/models/get_api_playlists_response_200_data_item.py +101 -0
- open_api_specification_client/models/get_api_playlists_response_200_data_item_attributes.py +101 -0
- open_api_specification_client/models/get_api_playlists_response_200_data_item_relationships.py +99 -0
- open_api_specification_client/models/get_api_playlists_response_200_data_item_relationships_device_tool.py +71 -0
- open_api_specification_client/models/get_api_playlists_response_200_data_item_relationships_device_tool_data.py +69 -0
- open_api_specification_client/models/get_api_playlists_response_200_data_item_relationships_playlist_items.py +71 -0
- open_api_specification_client/models/get_api_playlists_response_200_data_item_relationships_playlist_items_links.py +61 -0
- open_api_specification_client/models/get_api_playlists_response_200_data_item_relationships_user.py +71 -0
- open_api_specification_client/models/get_api_playlists_response_200_data_item_relationships_user_data.py +69 -0
- open_api_specification_client/models/get_api_playlists_response_200_included_item.py +111 -0
- open_api_specification_client/models/get_api_playlists_response_200_included_item_attributes.py +134 -0
- open_api_specification_client/models/get_api_playlists_response_200_included_item_attributes_config.py +47 -0
- open_api_specification_client/models/get_api_playlists_response_200_included_item_relationships.py +85 -0
- open_api_specification_client/models/get_api_playlists_response_200_included_item_relationships_device.py +71 -0
- open_api_specification_client/models/get_api_playlists_response_200_included_item_relationships_device_data.py +69 -0
- open_api_specification_client/models/get_api_playlists_response_200_included_item_relationships_tool.py +71 -0
- open_api_specification_client/models/get_api_playlists_response_200_included_item_relationships_tool_data.py +69 -0
- open_api_specification_client/models/get_api_playlists_response_200_jsonapi.py +61 -0
- open_api_specification_client/models/get_api_podcasts_response_200.py +85 -0
- open_api_specification_client/models/get_api_podcasts_response_200_data_item.py +85 -0
- open_api_specification_client/models/get_api_podcasts_response_200_data_item_attributes.py +99 -0
- open_api_specification_client/models/get_api_podcasts_response_200_jsonapi.py +61 -0
- open_api_specification_client/models/get_api_tools_response_200.py +85 -0
- open_api_specification_client/models/get_api_tools_response_200_data_item.py +83 -0
- open_api_specification_client/models/get_api_tools_response_200_data_item_attributes.py +93 -0
- open_api_specification_client/models/get_api_tools_response_200_jsonapi.py +61 -0
- open_api_specification_client/models/get_api_tools_tool_id_response_200.py +77 -0
- open_api_specification_client/models/get_api_tools_tool_id_response_200_data.py +85 -0
- open_api_specification_client/models/get_api_tools_tool_id_response_200_data_attributes.py +93 -0
- open_api_specification_client/models/get_api_tools_tool_id_response_200_jsonapi.py +61 -0
- open_api_specification_client/models/get_api_users_current_response_200.py +77 -0
- open_api_specification_client/models/get_api_users_current_response_200_data.py +99 -0
- open_api_specification_client/models/get_api_users_current_response_200_data_attributes.py +93 -0
- open_api_specification_client/models/get_api_users_current_response_200_data_relationships.py +85 -0
- open_api_specification_client/models/get_api_users_current_response_200_data_relationships_affiliate.py +61 -0
- open_api_specification_client/models/get_api_users_current_response_200_data_relationships_subscriptions.py +61 -0
- open_api_specification_client/models/get_api_users_current_response_200_jsonapi.py +61 -0
- open_api_specification_client/models/patch_api_audios_audio_id_body.py +67 -0
- open_api_specification_client/models/patch_api_audios_audio_id_body_data.py +85 -0
- open_api_specification_client/models/patch_api_audios_audio_id_body_data_attributes.py +70 -0
- open_api_specification_client/models/patch_api_audios_audio_id_body_data_type.py +8 -0
- open_api_specification_client/models/patch_api_audios_audio_id_response_200.py +99 -0
- open_api_specification_client/models/patch_api_audios_audio_id_response_200_data.py +101 -0
- open_api_specification_client/models/patch_api_audios_audio_id_response_200_data_attributes.py +85 -0
- open_api_specification_client/models/patch_api_audios_audio_id_response_200_data_relationships.py +87 -0
- open_api_specification_client/models/patch_api_audios_audio_id_response_200_data_relationships_processed_file.py +71 -0
- open_api_specification_client/models/patch_api_audios_audio_id_response_200_data_relationships_processed_file_data.py +69 -0
- open_api_specification_client/models/patch_api_audios_audio_id_response_200_data_relationships_raw_file.py +71 -0
- open_api_specification_client/models/patch_api_audios_audio_id_response_200_data_relationships_raw_file_data.py +69 -0
- open_api_specification_client/models/patch_api_audios_audio_id_response_200_included_item.py +87 -0
- open_api_specification_client/models/patch_api_audios_audio_id_response_200_included_item_attributes.py +101 -0
- open_api_specification_client/models/patch_api_audios_audio_id_response_200_jsonapi.py +61 -0
- open_api_specification_client/models/patch_api_notes_note_id_body.py +74 -0
- open_api_specification_client/models/patch_api_notes_note_id_body_data.py +110 -0
- open_api_specification_client/models/patch_api_notes_note_id_body_data_attributes.py +79 -0
- open_api_specification_client/models/patch_api_notes_note_id_body_data_relationships.py +78 -0
- open_api_specification_client/models/patch_api_notes_note_id_body_data_relationships_file.py +78 -0
- open_api_specification_client/models/patch_api_notes_note_id_body_data_relationships_file_data.py +70 -0
- open_api_specification_client/models/patch_api_notes_note_id_response_200.py +74 -0
- open_api_specification_client/models/patch_api_notes_note_id_response_200_data.py +96 -0
- open_api_specification_client/models/patch_api_notes_note_id_response_200_data_attributes.py +79 -0
- open_api_specification_client/models/patch_api_playlist_items_playlist_item_id_body.py +69 -0
- open_api_specification_client/models/patch_api_playlist_items_playlist_item_id_body_data.py +91 -0
- open_api_specification_client/models/patch_api_playlist_items_playlist_item_id_body_data_attributes.py +61 -0
- open_api_specification_client/models/patch_api_playlist_items_playlist_item_id_body_data_type.py +8 -0
- open_api_specification_client/models/patch_api_playlist_items_playlist_item_id_response_200.py +85 -0
- open_api_specification_client/models/patch_api_playlist_items_playlist_item_id_response_200_data.py +103 -0
- open_api_specification_client/models/patch_api_playlist_items_playlist_item_id_response_200_data_attributes.py +85 -0
- open_api_specification_client/models/patch_api_playlist_items_playlist_item_id_response_200_data_relationships.py +87 -0
- open_api_specification_client/models/patch_api_playlist_items_playlist_item_id_response_200_data_relationships_processed_file.py +61 -0
- open_api_specification_client/models/patch_api_playlist_items_playlist_item_id_response_200_data_relationships_raw_file.py +61 -0
- open_api_specification_client/models/patch_api_playlist_items_playlist_item_id_response_200_jsonapi.py +61 -0
- open_api_specification_client/models/post_api_audios_body.py +67 -0
- open_api_specification_client/models/post_api_audios_body_data.py +77 -0
- open_api_specification_client/models/post_api_audios_body_data_attributes.py +69 -0
- open_api_specification_client/models/post_api_audios_body_data_type.py +8 -0
- open_api_specification_client/models/post_api_audios_delete_all_body.py +61 -0
- open_api_specification_client/models/post_api_audios_response_201.py +95 -0
- open_api_specification_client/models/post_api_audios_response_201_data.py +83 -0
- open_api_specification_client/models/post_api_audios_response_201_data_relationships.py +85 -0
- open_api_specification_client/models/post_api_audios_response_201_data_relationships_processed_file.py +61 -0
- open_api_specification_client/models/post_api_audios_response_201_data_relationships_raw_file.py +71 -0
- open_api_specification_client/models/post_api_audios_response_201_data_relationships_raw_file_data.py +69 -0
- open_api_specification_client/models/post_api_audios_response_201_included_item.py +87 -0
- open_api_specification_client/models/post_api_audios_response_201_included_item_attributes.py +101 -0
- open_api_specification_client/models/post_api_audios_response_201_jsonapi.py +61 -0
- open_api_specification_client/models/post_api_authorizations_body.py +69 -0
- open_api_specification_client/models/post_api_authorizations_response_200.py +99 -0
- open_api_specification_client/models/post_api_authorizations_response_200_data.py +96 -0
- open_api_specification_client/models/post_api_authorizations_response_200_data_attributes.py +79 -0
- open_api_specification_client/models/post_api_authorizations_response_200_included_item.py +96 -0
- open_api_specification_client/models/post_api_authorizations_response_200_included_item_attributes.py +70 -0
- open_api_specification_client/models/post_api_device_tools_body.py +67 -0
- open_api_specification_client/models/post_api_device_tools_body_data.py +77 -0
- open_api_specification_client/models/post_api_device_tools_body_data_attributes.py +80 -0
- open_api_specification_client/models/post_api_device_tools_body_data_type.py +8 -0
- open_api_specification_client/models/post_api_device_tools_response_201.py +87 -0
- open_api_specification_client/models/post_api_device_tools_response_201_data.py +117 -0
- open_api_specification_client/models/post_api_device_tools_response_201_data_attributes.py +98 -0
- open_api_specification_client/models/post_api_device_tools_response_201_data_attributes_config.py +47 -0
- open_api_specification_client/models/post_api_device_tools_response_201_data_relationships.py +100 -0
- open_api_specification_client/models/post_api_device_tools_response_201_data_relationships_device.py +78 -0
- open_api_specification_client/models/post_api_device_tools_response_201_data_relationships_device_data.py +69 -0
- open_api_specification_client/models/post_api_device_tools_response_201_data_relationships_tool.py +78 -0
- open_api_specification_client/models/post_api_device_tools_response_201_data_relationships_tool_data.py +69 -0
- open_api_specification_client/models/post_api_device_tools_response_201_jsonapi.py +61 -0
- open_api_specification_client/models/post_api_followed_podcasts_body.py +67 -0
- open_api_specification_client/models/post_api_followed_podcasts_body_data.py +79 -0
- open_api_specification_client/models/post_api_followed_podcasts_body_data_relationships.py +85 -0
- open_api_specification_client/models/post_api_followed_podcasts_body_data_relationships_device_tool.py +71 -0
- open_api_specification_client/models/post_api_followed_podcasts_body_data_relationships_device_tool_data.py +73 -0
- open_api_specification_client/models/post_api_followed_podcasts_body_data_relationships_device_tool_data_type.py +8 -0
- open_api_specification_client/models/post_api_followed_podcasts_body_data_relationships_podcast.py +71 -0
- open_api_specification_client/models/post_api_followed_podcasts_body_data_relationships_podcast_data.py +73 -0
- open_api_specification_client/models/post_api_followed_podcasts_body_data_relationships_podcast_data_type.py +8 -0
- open_api_specification_client/models/post_api_followed_podcasts_body_data_type.py +8 -0
- open_api_specification_client/models/post_api_followed_podcasts_response_201.py +99 -0
- open_api_specification_client/models/post_api_followed_podcasts_response_201_data.py +101 -0
- open_api_specification_client/models/post_api_followed_podcasts_response_201_data_attributes.py +69 -0
- open_api_specification_client/models/post_api_followed_podcasts_response_201_data_relationships.py +85 -0
- open_api_specification_client/models/post_api_followed_podcasts_response_201_data_relationships_device_tool.py +71 -0
- open_api_specification_client/models/post_api_followed_podcasts_response_201_data_relationships_device_tool_data.py +69 -0
- open_api_specification_client/models/post_api_followed_podcasts_response_201_data_relationships_podcast.py +71 -0
- open_api_specification_client/models/post_api_followed_podcasts_response_201_data_relationships_podcast_data.py +69 -0
- open_api_specification_client/models/post_api_followed_podcasts_response_201_included_item.py +111 -0
- open_api_specification_client/models/post_api_followed_podcasts_response_201_included_item_attributes.py +155 -0
- open_api_specification_client/models/post_api_followed_podcasts_response_201_included_item_attributes_config.py +47 -0
- open_api_specification_client/models/post_api_followed_podcasts_response_201_included_item_relationships.py +85 -0
- open_api_specification_client/models/post_api_followed_podcasts_response_201_included_item_relationships_device.py +71 -0
- open_api_specification_client/models/post_api_followed_podcasts_response_201_included_item_relationships_device_data.py +69 -0
- open_api_specification_client/models/post_api_followed_podcasts_response_201_included_item_relationships_tool.py +71 -0
- open_api_specification_client/models/post_api_followed_podcasts_response_201_included_item_relationships_tool_data.py +69 -0
- open_api_specification_client/models/post_api_followed_podcasts_response_201_jsonapi.py +61 -0
- open_api_specification_client/models/post_api_notes_body.py +67 -0
- open_api_specification_client/models/post_api_notes_body_data.py +77 -0
- open_api_specification_client/models/post_api_notes_body_data_attributes.py +87 -0
- open_api_specification_client/models/post_api_notes_body_data_attributes_note_type.py +9 -0
- open_api_specification_client/models/post_api_notes_body_data_type.py +8 -0
- open_api_specification_client/models/post_api_notes_response_201.py +95 -0
- open_api_specification_client/models/post_api_notes_response_201_data.py +93 -0
- open_api_specification_client/models/post_api_notes_response_201_data_attributes.py +93 -0
- open_api_specification_client/models/post_api_notes_response_201_data_relationships.py +71 -0
- open_api_specification_client/models/post_api_notes_response_201_data_relationships_file.py +71 -0
- open_api_specification_client/models/post_api_notes_response_201_data_relationships_file_data.py +69 -0
- open_api_specification_client/models/post_api_notes_response_201_included_item.py +87 -0
- open_api_specification_client/models/post_api_notes_response_201_included_item_attributes.py +101 -0
- open_api_specification_client/models/post_api_notes_response_201_jsonapi.py +61 -0
- open_api_specification_client/models/post_api_playlists_sort_mode_body.py +80 -0
- open_api_specification_client/models/post_api_playlists_sort_mode_body_sort_mode.py +10 -0
- open_api_specification_client/models/post_api_playlists_sort_mode_response_400.py +67 -0
- open_api_specification_client/models/post_api_playlists_sort_mode_response_400_errors.py +61 -0
- open_api_specification_client/models/post_api_podcasts_body.py +67 -0
- open_api_specification_client/models/post_api_podcasts_body_data.py +77 -0
- open_api_specification_client/models/post_api_podcasts_body_data_attributes.py +143 -0
- open_api_specification_client/models/post_api_podcasts_body_data_type.py +8 -0
- open_api_specification_client/models/post_api_podcasts_response_201.py +77 -0
- open_api_specification_client/models/post_api_podcasts_response_201_data.py +83 -0
- open_api_specification_client/models/post_api_podcasts_response_201_data_attributes.py +93 -0
- open_api_specification_client/models/post_api_podcasts_response_201_jsonapi.py +61 -0
- open_api_specification_client/py.typed +1 -0
- open_api_specification_client/types.py +54 -0
light_api/__init__.py
ADDED
light_api/client.py
ADDED
|
@@ -0,0 +1,273 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import httpx
|
|
4
|
+
import json
|
|
5
|
+
import keyring
|
|
6
|
+
import logging
|
|
7
|
+
import os
|
|
8
|
+
|
|
9
|
+
from typing import TYPE_CHECKING, Any, Callable, final
|
|
10
|
+
|
|
11
|
+
from open_api_specification_client.api.default import get_api_playlists
|
|
12
|
+
from open_api_specification_client.client import AuthenticatedClient
|
|
13
|
+
|
|
14
|
+
if TYPE_CHECKING:
|
|
15
|
+
from light_api.music import LightMusic
|
|
16
|
+
from light_api.podcast import LightPodcasts
|
|
17
|
+
from light_api.notes import LightNotes
|
|
18
|
+
from light_api.tools import LightTools
|
|
19
|
+
|
|
20
|
+
KEYRING_SERVICE = "unofficial-light-api"
|
|
21
|
+
KEYRING_USER = "session"
|
|
22
|
+
API_BASE = "https://production.lightphonecloud.com"
|
|
23
|
+
API_HEADERS = {"Accept": "application/vnd.api+json"}
|
|
24
|
+
|
|
25
|
+
log = logging.getLogger(f"light.{__name__}")
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
@final
|
|
29
|
+
class Light:
|
|
30
|
+
"""Methods for interfacing with Light devices."""
|
|
31
|
+
|
|
32
|
+
def __init__(
|
|
33
|
+
self,
|
|
34
|
+
email: str | None = None,
|
|
35
|
+
email_file: str | None = None,
|
|
36
|
+
password: str | None = None,
|
|
37
|
+
password_file: str | None = None,
|
|
38
|
+
phone: str | None = None,
|
|
39
|
+
phone_file: str | None = None,
|
|
40
|
+
) -> None:
|
|
41
|
+
self.email: str | None = email or self._resolve(email_file, "LIGHT_EMAIL")
|
|
42
|
+
self.password: str | None = password or self._resolve(
|
|
43
|
+
password_file, "LIGHT_PASSWORD"
|
|
44
|
+
)
|
|
45
|
+
self.phone: str | None = phone or self._resolve(
|
|
46
|
+
phone_file, "LIGHT_PHONE_NUMBER"
|
|
47
|
+
)
|
|
48
|
+
self._api_token: str | None = None
|
|
49
|
+
self._api_client: AuthenticatedClient | None = None
|
|
50
|
+
self._device_tool_ids: dict[str, str] = {}
|
|
51
|
+
self._playlist_id: str | None = None
|
|
52
|
+
|
|
53
|
+
self.music: LightMusic
|
|
54
|
+
self.podcast: LightPodcasts
|
|
55
|
+
self.notes: LightNotes
|
|
56
|
+
self.tools: LightTools
|
|
57
|
+
|
|
58
|
+
def login(self) -> None:
|
|
59
|
+
"""Authenticate via the authorizations API and store the bearer token."""
|
|
60
|
+
if self._api_token is not None:
|
|
61
|
+
return
|
|
62
|
+
|
|
63
|
+
if not self.email or not self.password:
|
|
64
|
+
raise RuntimeError("No cached session - provide email and password")
|
|
65
|
+
|
|
66
|
+
resp = httpx.post(
|
|
67
|
+
f"{API_BASE}/api/authorizations",
|
|
68
|
+
json={"email": self.email, "password": self.password},
|
|
69
|
+
headers={**API_HEADERS, "Content-Type": "application/vnd.api+json"},
|
|
70
|
+
timeout=30,
|
|
71
|
+
)
|
|
72
|
+
|
|
73
|
+
if not resp.is_success:
|
|
74
|
+
raise RuntimeError(f"Login failed: {resp.status_code}")
|
|
75
|
+
|
|
76
|
+
included = resp.json()["included"][0]
|
|
77
|
+
token = included["attributes"]["token"]
|
|
78
|
+
|
|
79
|
+
if token is None:
|
|
80
|
+
raise RuntimeError("Login succeeded but no token found in response")
|
|
81
|
+
|
|
82
|
+
self._api_token = token
|
|
83
|
+
|
|
84
|
+
def reauth(self) -> None:
|
|
85
|
+
"""Re-authenticate and refresh the API client. Called on 401."""
|
|
86
|
+
log.info("Re-authenticating")
|
|
87
|
+
self._api_token = None
|
|
88
|
+
self.login()
|
|
89
|
+
self._save_cache()
|
|
90
|
+
self._api_client = AuthenticatedClient(
|
|
91
|
+
base_url=API_BASE,
|
|
92
|
+
token=self._api_token,
|
|
93
|
+
headers=API_HEADERS,
|
|
94
|
+
)
|
|
95
|
+
|
|
96
|
+
def call_api(self, func: Callable[..., Any], **kwargs: Any) -> Any:
|
|
97
|
+
"""Call an API function, re-authenticating once on 401."""
|
|
98
|
+
resp = func(**kwargs)
|
|
99
|
+
if resp.status_code == 401:
|
|
100
|
+
self.reauth()
|
|
101
|
+
resp = func(**kwargs)
|
|
102
|
+
return resp
|
|
103
|
+
|
|
104
|
+
def __enter__(self) -> Light:
|
|
105
|
+
"""Sets up API session."""
|
|
106
|
+
log.info("Authenticating")
|
|
107
|
+
|
|
108
|
+
if self._load_cache() and self._validate_cache():
|
|
109
|
+
log.info("Using cached session")
|
|
110
|
+
else:
|
|
111
|
+
self.login()
|
|
112
|
+
self._save_cache()
|
|
113
|
+
|
|
114
|
+
self._api_client = AuthenticatedClient(
|
|
115
|
+
base_url=API_BASE,
|
|
116
|
+
token=self._api_token,
|
|
117
|
+
headers=API_HEADERS,
|
|
118
|
+
)
|
|
119
|
+
|
|
120
|
+
expected = {"music", "notes", "podcast"}
|
|
121
|
+
if not expected.issubset(self._device_tool_ids) or not self._playlist_id:
|
|
122
|
+
self._fetch_device_tool_ids()
|
|
123
|
+
self._fetch_playlist_id()
|
|
124
|
+
self._save_cache()
|
|
125
|
+
|
|
126
|
+
from light_api.music import LightMusic
|
|
127
|
+
from light_api.podcast import LightPodcasts
|
|
128
|
+
from light_api.notes import LightNotes
|
|
129
|
+
from light_api.tools import LightTools
|
|
130
|
+
|
|
131
|
+
self.music = LightMusic(self)
|
|
132
|
+
self.podcast = LightPodcasts(self)
|
|
133
|
+
self.notes = LightNotes(self)
|
|
134
|
+
self.tools = LightTools(self)
|
|
135
|
+
|
|
136
|
+
log.info("Authentication complete")
|
|
137
|
+
return self
|
|
138
|
+
|
|
139
|
+
def __exit__(self, *_: object) -> None:
|
|
140
|
+
pass
|
|
141
|
+
|
|
142
|
+
def _load_cache(self) -> bool:
|
|
143
|
+
"""Load cached data from keyring."""
|
|
144
|
+
log.debug("Loading cache")
|
|
145
|
+
|
|
146
|
+
try:
|
|
147
|
+
raw = keyring.get_password(KEYRING_SERVICE, KEYRING_USER)
|
|
148
|
+
except (keyring.errors.NoKeyringError, keyring.errors.KeyringLocked) as e:
|
|
149
|
+
log.debug(f"Keyring error: {e}")
|
|
150
|
+
return False
|
|
151
|
+
|
|
152
|
+
if raw is None:
|
|
153
|
+
log.debug("Keyring: no entry found")
|
|
154
|
+
return False
|
|
155
|
+
|
|
156
|
+
try:
|
|
157
|
+
data = json.loads(raw)
|
|
158
|
+
self._api_token = data["api_token"]
|
|
159
|
+
self._device_tool_ids = data["device_tool_ids"]
|
|
160
|
+
self._playlist_id = data["playlist_id"]
|
|
161
|
+
log.debug("Cache loaded successfully")
|
|
162
|
+
return True
|
|
163
|
+
except (KeyError, json.JSONDecodeError) as e:
|
|
164
|
+
log.debug(f"Error: {e}")
|
|
165
|
+
return False
|
|
166
|
+
|
|
167
|
+
def _save_cache(self) -> None:
|
|
168
|
+
"""Cache data to keyring."""
|
|
169
|
+
try:
|
|
170
|
+
keyring.set_password(
|
|
171
|
+
KEYRING_SERVICE,
|
|
172
|
+
KEYRING_USER,
|
|
173
|
+
json.dumps(
|
|
174
|
+
{
|
|
175
|
+
"api_token": self._api_token,
|
|
176
|
+
"device_tool_ids": self._device_tool_ids,
|
|
177
|
+
"playlist_id": self._playlist_id,
|
|
178
|
+
}
|
|
179
|
+
),
|
|
180
|
+
)
|
|
181
|
+
except (keyring.errors.NoKeyringError, keyring.errors.KeyringLocked) as e:
|
|
182
|
+
log.warning(f"Keyring error: {e}")
|
|
183
|
+
|
|
184
|
+
def _validate_cache(self) -> bool:
|
|
185
|
+
"""Check if cached auth token is valid."""
|
|
186
|
+
client = AuthenticatedClient(
|
|
187
|
+
base_url=API_BASE,
|
|
188
|
+
token=self._api_token,
|
|
189
|
+
headers=API_HEADERS,
|
|
190
|
+
)
|
|
191
|
+
resp = get_api_playlists.sync_detailed(
|
|
192
|
+
client=client,
|
|
193
|
+
device_tool_id=self._device_tool_ids.get("music"),
|
|
194
|
+
)
|
|
195
|
+
return resp.status_code == 200
|
|
196
|
+
|
|
197
|
+
def _fetch_playlist_id(self) -> None:
|
|
198
|
+
music_id = self._device_tool_ids.get("music")
|
|
199
|
+
if not music_id:
|
|
200
|
+
raise RuntimeError("Could not find music device_tool_id in /api/devices")
|
|
201
|
+
resp = get_api_playlists.sync_detailed(
|
|
202
|
+
client=self._api_client,
|
|
203
|
+
device_tool_id=music_id,
|
|
204
|
+
)
|
|
205
|
+
if resp.status_code != 200 or not resp.parsed or not resp.parsed.data:
|
|
206
|
+
raise RuntimeError(f"Could not fetch playlists: {resp.status_code}")
|
|
207
|
+
self._playlist_id = resp.parsed.data[0].id
|
|
208
|
+
|
|
209
|
+
def _fetch_device_tool_ids(self) -> None:
|
|
210
|
+
"""Populate _device_tool_ids for all installed tools.
|
|
211
|
+
|
|
212
|
+
The Light API has two relevant endpoints:
|
|
213
|
+
- GET /api/devices: returns the device record plus "included" items which are
|
|
214
|
+
device_tool records (one per installed tool). Each has a unique `id` (the
|
|
215
|
+
device_tool_id used in subsequent API calls) and a relationship pointing to
|
|
216
|
+
its global tool ID.
|
|
217
|
+
- GET /api/tools?device_id=...: returns the global tool catalog with human-readable
|
|
218
|
+
namespace strings (e.g. "com.light.music", "com.light.notes").
|
|
219
|
+
|
|
220
|
+
To get each device tool id, cross-reference the two: build a map of global_tool_id -> namespace
|
|
221
|
+
from /api/tools, then walk the /api/devices included items, look up each item's global tool ID in
|
|
222
|
+
that map, and classify it as "music", "notes", or "podcast" based on the namespace string.
|
|
223
|
+
"""
|
|
224
|
+
from open_api_specification_client.api.default import (
|
|
225
|
+
get_api_devices,
|
|
226
|
+
get_api_tools,
|
|
227
|
+
)
|
|
228
|
+
from open_api_specification_client.types import Unset
|
|
229
|
+
|
|
230
|
+
devices_resp = get_api_devices.sync_detailed(client=self._api_client)
|
|
231
|
+
if (
|
|
232
|
+
devices_resp.status_code != 200
|
|
233
|
+
or not devices_resp.parsed
|
|
234
|
+
or not devices_resp.parsed.data
|
|
235
|
+
):
|
|
236
|
+
raise RuntimeError(f"Could not fetch devices: {devices_resp.status_code}")
|
|
237
|
+
device_id = devices_resp.parsed.data[0].id
|
|
238
|
+
|
|
239
|
+
tools_resp = get_api_tools.sync_detailed(
|
|
240
|
+
client=self._api_client, device_id=device_id
|
|
241
|
+
)
|
|
242
|
+
if tools_resp.status_code != 200 or not tools_resp.parsed:
|
|
243
|
+
raise RuntimeError(f"Could not fetch tools: {tools_resp.status_code}")
|
|
244
|
+
|
|
245
|
+
tool_ns: dict[str, str] = {
|
|
246
|
+
t.id: t.attributes.namespace.lower() for t in tools_resp.parsed.data
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
for item in devices_resp.parsed.included:
|
|
250
|
+
if isinstance(item.relationships.tool, Unset):
|
|
251
|
+
continue
|
|
252
|
+
ns = tool_ns.get(item.relationships.tool.data.id, "")
|
|
253
|
+
if "note" in ns:
|
|
254
|
+
self._device_tool_ids["notes"] = item.id
|
|
255
|
+
elif "podcast" in ns:
|
|
256
|
+
self._device_tool_ids["podcast"] = item.id
|
|
257
|
+
elif "music" in ns or "playlist" in ns:
|
|
258
|
+
self._device_tool_ids["music"] = item.id
|
|
259
|
+
|
|
260
|
+
@staticmethod
|
|
261
|
+
def _resolve(filepath: str | None, env_key: str) -> str | None:
|
|
262
|
+
if filepath:
|
|
263
|
+
try:
|
|
264
|
+
with open(filepath) as f:
|
|
265
|
+
return f.read().strip()
|
|
266
|
+
except OSError as e:
|
|
267
|
+
raise RuntimeError(f"Could not read {filepath}: {e}") from e
|
|
268
|
+
return os.environ.get(env_key)
|
|
269
|
+
|
|
270
|
+
@staticmethod
|
|
271
|
+
def _format_phone(number: str) -> str:
|
|
272
|
+
digits: str = "".join(c for c in number if c.isdigit())[-10:]
|
|
273
|
+
return f"+1 {digits[0:3]} {digits[3:6]} {digits[6:10]}"
|