hippius 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.
hippius_sdk/client.py ADDED
@@ -0,0 +1,246 @@
1
+ """
2
+ Main client for the Hippius SDK.
3
+ """
4
+
5
+ import os
6
+ from typing import Dict, Any, Optional, List, Union
7
+ from hippius_sdk.ipfs import IPFSClient
8
+ from hippius_sdk.substrate import SubstrateClient, FileInput
9
+
10
+
11
+ class HippiusClient:
12
+ """
13
+ Main client for interacting with the Hippius ecosystem.
14
+
15
+ Provides IPFS operations, with Substrate functionality for storage requests.
16
+ """
17
+
18
+ def __init__(
19
+ self,
20
+ ipfs_gateway: str = "https://ipfs.io",
21
+ ipfs_api_url: str = "https://relay-fr.hippius.network",
22
+ substrate_url: str = None,
23
+ substrate_seed_phrase: str = None,
24
+ encrypt_by_default: Optional[bool] = None,
25
+ encryption_key: Optional[bytes] = None,
26
+ ):
27
+ """
28
+ Initialize the Hippius client.
29
+
30
+ Args:
31
+ ipfs_gateway: IPFS gateway URL for downloading content
32
+ ipfs_api_url: IPFS API URL for uploading content. Defaults to Hippius relay node.
33
+ substrate_url: WebSocket URL of the Hippius substrate node
34
+ substrate_seed_phrase: Seed phrase for Substrate account
35
+ encrypt_by_default: Whether to encrypt files by default (from .env if None)
36
+ encryption_key: Encryption key for NaCl secretbox (from .env if None)
37
+ """
38
+ self.ipfs = IPFSClient(
39
+ gateway=ipfs_gateway,
40
+ api_url=ipfs_api_url,
41
+ encrypt_by_default=encrypt_by_default,
42
+ encryption_key=encryption_key,
43
+ )
44
+
45
+ # Initialize Substrate client
46
+ try:
47
+ self.substrate_client = SubstrateClient(
48
+ url=substrate_url, seed_phrase=substrate_seed_phrase
49
+ )
50
+ except Exception as e:
51
+ print(f"Warning: Could not initialize Substrate client: {e}")
52
+ self.substrate_client = None
53
+
54
+ def upload_file(
55
+ self, file_path: str, encrypt: Optional[bool] = None
56
+ ) -> Dict[str, Any]:
57
+ """
58
+ Upload a file to IPFS with optional encryption.
59
+
60
+ Args:
61
+ file_path: Path to the file to upload
62
+ encrypt: Whether to encrypt the file (overrides default)
63
+
64
+ Returns:
65
+ Dict[str, Any]: Dictionary containing file details including:
66
+ - cid: Content Identifier of the uploaded file
67
+ - filename: Name of the file
68
+ - size_bytes: Size of the file in bytes
69
+ - size_formatted: Human-readable file size
70
+ - encrypted: Whether the file was encrypted
71
+
72
+ Raises:
73
+ FileNotFoundError: If the file doesn't exist
74
+ ConnectionError: If no IPFS connection is available
75
+ ValueError: If encryption is requested but not available
76
+ """
77
+ # Use the enhanced IPFSClient method directly with encryption parameter
78
+ return self.ipfs.upload_file(file_path, encrypt=encrypt)
79
+
80
+ def upload_directory(
81
+ self, dir_path: str, encrypt: Optional[bool] = None
82
+ ) -> Dict[str, Any]:
83
+ """
84
+ Upload a directory to IPFS with optional encryption.
85
+
86
+ Args:
87
+ dir_path: Path to the directory to upload
88
+ encrypt: Whether to encrypt files (overrides default)
89
+
90
+ Returns:
91
+ Dict[str, Any]: Dictionary containing directory details including:
92
+ - cid: Content Identifier of the uploaded directory
93
+ - dirname: Name of the directory
94
+ - file_count: Number of files uploaded
95
+ - total_size_bytes: Total size in bytes
96
+ - size_formatted: Human-readable total size
97
+ - encrypted: Whether files were encrypted
98
+
99
+ Raises:
100
+ FileNotFoundError: If the directory doesn't exist
101
+ ConnectionError: If no IPFS connection is available
102
+ ValueError: If encryption is requested but not available
103
+ """
104
+ # Use the enhanced IPFSClient method directly with encryption parameter
105
+ return self.ipfs.upload_directory(dir_path, encrypt=encrypt)
106
+
107
+ def download_file(
108
+ self, cid: str, output_path: str, decrypt: Optional[bool] = None
109
+ ) -> Dict[str, Any]:
110
+ """
111
+ Download a file from IPFS with optional decryption.
112
+
113
+ Args:
114
+ cid: Content Identifier (CID) of the file to download
115
+ output_path: Path where the downloaded file will be saved
116
+ decrypt: Whether to decrypt the file (overrides default)
117
+
118
+ Returns:
119
+ Dict[str, Any]: Dictionary containing download details including:
120
+ - success: Whether the download was successful
121
+ - output_path: Path where the file was saved
122
+ - size_bytes: Size of the downloaded file in bytes
123
+ - size_formatted: Human-readable file size
124
+ - elapsed_seconds: Time taken for the download
125
+ - decrypted: Whether the file was decrypted
126
+
127
+ Raises:
128
+ requests.RequestException: If the download fails
129
+ ValueError: If decryption is requested but fails
130
+ """
131
+ return self.ipfs.download_file(cid, output_path, decrypt=decrypt)
132
+
133
+ def cat(
134
+ self,
135
+ cid: str,
136
+ max_display_bytes: int = 1024,
137
+ format_output: bool = True,
138
+ decrypt: Optional[bool] = None,
139
+ ) -> Dict[str, Any]:
140
+ """
141
+ Get the content of a file from IPFS with optional decryption.
142
+
143
+ Args:
144
+ cid: Content Identifier (CID) of the file
145
+ max_display_bytes: Maximum number of bytes to include in the preview
146
+ format_output: Whether to attempt to decode the content as text
147
+ decrypt: Whether to decrypt the file (overrides default)
148
+
149
+ Returns:
150
+ Dict[str, Any]: Dictionary containing content details including:
151
+ - content: Complete binary content of the file
152
+ - size_bytes: Size of the content in bytes
153
+ - size_formatted: Human-readable size
154
+ - is_text: Whether the content seems to be text
155
+ - text_preview/hex_preview: Preview of the content
156
+ - decrypted: Whether the file was decrypted
157
+ """
158
+ return self.ipfs.cat(cid, max_display_bytes, format_output, decrypt=decrypt)
159
+
160
+ def exists(self, cid: str) -> Dict[str, Any]:
161
+ """
162
+ Check if a CID exists on IPFS.
163
+
164
+ Args:
165
+ cid: Content Identifier (CID) to check
166
+
167
+ Returns:
168
+ Dict[str, Any]: Dictionary containing:
169
+ - exists: Boolean indicating if the CID exists
170
+ - cid: The CID that was checked
171
+ - formatted_cid: Formatted version of the CID
172
+ - gateway_url: URL to access the content if it exists
173
+ """
174
+ return self.ipfs.exists(cid)
175
+
176
+ def pin(self, cid: str) -> Dict[str, Any]:
177
+ """
178
+ Pin a CID to IPFS to keep it available.
179
+
180
+ Args:
181
+ cid: Content Identifier (CID) to pin
182
+
183
+ Returns:
184
+ Dict[str, Any]: Dictionary containing:
185
+ - success: Boolean indicating if pinning was successful
186
+ - cid: The CID that was pinned
187
+ - formatted_cid: Formatted version of the CID
188
+ - message: Status message
189
+ """
190
+ return self.ipfs.pin(cid)
191
+
192
+ def format_cid(self, cid: str) -> str:
193
+ """
194
+ Format a CID for display.
195
+
196
+ This is a convenience method that delegates to the IPFSClient.
197
+
198
+ Args:
199
+ cid: Content Identifier (CID) to format
200
+
201
+ Returns:
202
+ str: Formatted CID string
203
+ """
204
+ return self.ipfs.format_cid(cid)
205
+
206
+ def format_size(self, size_bytes: int) -> str:
207
+ """
208
+ Format a size in bytes to a human-readable string.
209
+
210
+ This is a convenience method that delegates to the IPFSClient.
211
+
212
+ Args:
213
+ size_bytes: Size in bytes
214
+
215
+ Returns:
216
+ str: Human-readable size string (e.g., '1.23 MB', '456.78 KB')
217
+ """
218
+ return self.ipfs.format_size(size_bytes)
219
+
220
+ def generate_encryption_key(self) -> str:
221
+ """
222
+ Generate a new random encryption key for use with the SDK.
223
+
224
+ Returns:
225
+ str: Base64-encoded encryption key ready for use in .env file
226
+ or directly as the encryption_key parameter (after base64 decoding).
227
+
228
+ Raises:
229
+ ImportError: If PyNaCl is not installed
230
+ """
231
+ try:
232
+ import nacl.utils
233
+ import nacl.secret
234
+ import base64
235
+
236
+ # Generate a random key
237
+ key = nacl.utils.random(nacl.secret.SecretBox.KEY_SIZE)
238
+
239
+ # Encode to base64 for storage in .env
240
+ encoded_key = base64.b64encode(key).decode()
241
+
242
+ return encoded_key
243
+ except ImportError:
244
+ raise ImportError(
245
+ "PyNaCl is required for encryption. Install it with: pip install pynacl"
246
+ )