hippius 0.2.3__py3-none-any.whl → 0.2.4__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/cli_parser.py CHANGED
@@ -520,6 +520,11 @@ def add_account_commands(subparsers):
520
520
  help="Account name to show info for (uses active account if not specified)",
521
521
  )
522
522
 
523
+ # Account login
524
+ login_account_parser = account_subparsers.add_parser(
525
+ "login", help="Login with an account address and seed phrase"
526
+ )
527
+
523
528
  # Account balance
524
529
  balance_account_parser = account_subparsers.add_parser(
525
530
  "balance", help="Check account balance"
hippius_sdk/cli_rich.py CHANGED
@@ -6,14 +6,8 @@ from typing import Any, Dict, List, Optional, Union
6
6
 
7
7
  from rich.console import Console
8
8
  from rich.panel import Panel
9
- from rich.progress import (
10
- BarColumn,
11
- Progress,
12
- SpinnerColumn,
13
- TextColumn,
14
- TimeElapsedColumn,
15
- TimeRemainingColumn,
16
- )
9
+ from rich.progress import (BarColumn, Progress, SpinnerColumn, TextColumn,
10
+ TimeElapsedColumn, TimeRemainingColumn)
17
11
  from rich.table import Table
18
12
  from rich.text import Text
19
13
 
hippius_sdk/client.py CHANGED
@@ -64,8 +64,8 @@ class HippiusClient:
64
64
  "substrate", "url", "wss://rpc.hippius.network"
65
65
  )
66
66
 
67
- if substrate_seed_phrase is None:
68
- substrate_seed_phrase = get_config_value("substrate", "seed_phrase")
67
+ # Don't try to get a seed phrase from the legacy location
68
+ # The substrate_client will handle getting it from the active account
69
69
 
70
70
  if encrypt_by_default is None:
71
71
  encrypt_by_default = get_config_value(
@@ -82,18 +82,13 @@ class HippiusClient:
82
82
  encrypt_by_default=encrypt_by_default,
83
83
  encryption_key=encryption_key,
84
84
  )
85
-
86
85
  # Initialize Substrate client
87
- try:
88
- self.substrate_client = SubstrateClient(
89
- url=substrate_url,
90
- seed_phrase=substrate_seed_phrase,
91
- password=seed_phrase_password,
92
- account_name=account_name,
93
- )
94
- except Exception as e:
95
- print(f"Warning: Could not initialize Substrate client: {e}")
96
- self.substrate_client = None
86
+ self.substrate_client = SubstrateClient(
87
+ url=substrate_url,
88
+ seed_phrase=substrate_seed_phrase,
89
+ password=seed_phrase_password,
90
+ account_name=account_name,
91
+ )
97
92
 
98
93
  async def upload_file(
99
94
  self, file_path: str, encrypt: Optional[bool] = None
@@ -375,6 +370,7 @@ class HippiusClient:
375
370
  max_retries: int = 3,
376
371
  verbose: bool = True,
377
372
  progress_callback: Optional[Callable[[str, int, int], None]] = None,
373
+ publish: bool = True,
378
374
  ) -> Dict[str, Any]:
379
375
  """
380
376
  Erasure code a file, upload the chunks to IPFS, and store in the Hippius marketplace.
@@ -392,9 +388,12 @@ class HippiusClient:
392
388
  verbose: Whether to print progress information
393
389
  progress_callback: Optional callback function for progress updates
394
390
  Function receives (stage_name, current, total)
391
+ publish: Whether to publish to the blockchain (True) or just perform local
392
+ erasure coding without publishing (False). When False, no password
393
+ is needed for seed phrase access.
395
394
 
396
395
  Returns:
397
- dict: Result including metadata CID and transaction hash
396
+ dict: Result including metadata CID and transaction hash (if published)
398
397
 
399
398
  Raises:
400
399
  ValueError: If parameters are invalid
@@ -411,6 +410,7 @@ class HippiusClient:
411
410
  max_retries=max_retries,
412
411
  verbose=verbose,
413
412
  progress_callback=progress_callback,
413
+ publish=publish,
414
414
  )
415
415
 
416
416
  async def delete_file(
hippius_sdk/config.py CHANGED
@@ -278,34 +278,30 @@ def decrypt_with_password(encrypted_data: str, salt: str, password: str) -> str:
278
278
  Returns:
279
279
  str: Decrypted data
280
280
  """
281
- try:
282
- # Decode the encrypted data and salt
283
- encrypted_bytes = base64.b64decode(encrypted_data)
284
- salt_bytes = base64.b64decode(salt)
281
+ # Decode the encrypted data and salt
282
+ encrypted_bytes = base64.b64decode(encrypted_data)
283
+ salt_bytes = base64.b64decode(salt)
285
284
 
286
- # Derive the key from the password and salt
287
- key, _ = _derive_key_from_password(password, salt_bytes)
285
+ # Derive the key from the password and salt
286
+ key, _ = _derive_key_from_password(password, salt_bytes)
288
287
 
289
- # Verify NaCl is available (imported at the top)
290
- try:
291
- if not hasattr(nacl, "secret") or not hasattr(nacl, "utils"):
292
- raise ImportError("NaCl modules not available")
293
- except ImportError:
294
- raise ValueError(
295
- "PyNaCl is required for decryption. Install it with: pip install pynacl"
296
- )
297
-
298
- # Create a SecretBox with our derived key
299
- box = nacl.secret.SecretBox(key)
288
+ # Verify NaCl is available (imported at the top)
289
+ try:
290
+ if not hasattr(nacl, "secret") or not hasattr(nacl, "utils"):
291
+ raise ImportError("NaCl modules not available")
292
+ except ImportError:
293
+ raise ValueError(
294
+ "PyNaCl is required for decryption. Install it with: pip install pynacl"
295
+ )
300
296
 
301
- # Decrypt the data
302
- decrypted_data = box.decrypt(encrypted_bytes)
297
+ # Create a SecretBox with our derived key
298
+ box = nacl.secret.SecretBox(key)
303
299
 
304
- # Return the decrypted string
305
- return decrypted_data.decode("utf-8")
300
+ # Decrypt the data
301
+ decrypted_data = box.decrypt(encrypted_bytes)
306
302
 
307
- except Exception as e:
308
- raise ValueError(f"Error decrypting data with password: {e}")
303
+ # Return the decrypted string
304
+ return decrypted_data.decode("utf-8")
309
305
 
310
306
 
311
307
  def encrypt_seed_phrase(
@@ -317,7 +313,7 @@ def encrypt_seed_phrase(
317
313
  Args:
318
314
  seed_phrase: The plain text seed phrase to encrypt
319
315
  password: Optional password (if None, will prompt)
320
- account_name: Optional name for the account (if None, uses legacy mode or active account)
316
+ account_name: Optional name for the account (if None, uses active account)
321
317
 
322
318
  Returns:
323
319
  bool: True if encryption and saving was successful, False otherwise
@@ -344,30 +340,31 @@ def encrypt_seed_phrase(
344
340
 
345
341
  config = load_config()
346
342
 
347
- # Check if we're using the new multi-account system
348
- if account_name is not None:
349
- # Multi-account mode
350
- if "accounts" not in config["substrate"]:
351
- config["substrate"]["accounts"] = {}
352
-
353
- # Store the account data
354
- config["substrate"]["accounts"][account_name] = {
355
- "seed_phrase": encrypted_data,
356
- "seed_phrase_encoded": True,
357
- "seed_phrase_salt": salt,
358
- "ss58_address": ss58_address,
359
- }
360
-
361
- # Set as active account if no active account exists
362
- if not config["substrate"].get("active_account"):
363
- config["substrate"]["active_account"] = account_name
364
-
365
- else:
366
- # Legacy mode - single account
367
- config["substrate"]["seed_phrase"] = encrypted_data
368
- config["substrate"]["seed_phrase_encoded"] = True
369
- config["substrate"]["seed_phrase_salt"] = salt
370
- config["substrate"]["ss58_address"] = ss58_address
343
+ # Only use multi-account mode
344
+ # Use active account if no account specified
345
+ name_to_use = account_name
346
+ if name_to_use is None:
347
+ name_to_use = config["substrate"].get("active_account")
348
+ if not name_to_use:
349
+ # If no active account, we need a name
350
+ print("Error: No account name specified and no active account")
351
+ return False
352
+
353
+ # Ensure accounts structure exists
354
+ if "accounts" not in config["substrate"]:
355
+ config["substrate"]["accounts"] = {}
356
+
357
+ # Store the account data
358
+ config["substrate"]["accounts"][name_to_use] = {
359
+ "seed_phrase": encrypted_data,
360
+ "seed_phrase_encoded": True,
361
+ "seed_phrase_salt": salt,
362
+ "ss58_address": ss58_address,
363
+ }
364
+
365
+ # Set as active account if no active account exists
366
+ if not config["substrate"].get("active_account"):
367
+ config["substrate"]["active_account"] = name_to_use
371
368
 
372
369
  return save_config(config)
373
370
 
@@ -384,60 +381,43 @@ def decrypt_seed_phrase(
384
381
 
385
382
  Args:
386
383
  password: Optional password (if None, will prompt)
387
- account_name: Optional account name (if None, uses active account or legacy mode)
384
+ account_name: Optional account name (if None, uses active account)
388
385
 
389
386
  Returns:
390
387
  Optional[str]: The decrypted seed phrase, or None if decryption failed
391
388
  """
392
- try:
393
- config = load_config()
394
-
395
- # Determine if we're using multi-account mode
396
- if account_name is not None or config["substrate"].get("active_account"):
397
- # Multi-account mode
398
- name_to_use = account_name or config["substrate"].get("active_account")
399
-
400
- if not name_to_use:
401
- print("Error: No account specified and no active account")
402
- return None
403
-
404
- if name_to_use not in config["substrate"].get("accounts", {}):
405
- print(f"Error: Account '{name_to_use}' not found")
406
- return None
407
-
408
- account_data = config["substrate"]["accounts"][name_to_use]
409
- is_encoded = account_data.get("seed_phrase_encoded", False)
389
+ config = load_config()
410
390
 
411
- if not is_encoded:
412
- return account_data.get("seed_phrase")
391
+ # Only use the multi-account system
392
+ name_to_use = account_name or config["substrate"].get("active_account")
413
393
 
414
- encrypted_data = account_data.get("seed_phrase")
415
- salt = account_data.get("seed_phrase_salt")
394
+ if not name_to_use:
395
+ print("Error: No account specified and no active account")
396
+ return None
416
397
 
417
- else:
418
- # Legacy mode - single account
419
- is_encoded = config["substrate"].get("seed_phrase_encoded", False)
398
+ if name_to_use not in config["substrate"].get("accounts", {}):
399
+ print(f"Error: Account '{name_to_use}' not found")
400
+ return None
420
401
 
421
- if not is_encoded:
422
- return config["substrate"].get("seed_phrase")
402
+ account_data = config["substrate"]["accounts"][name_to_use]
403
+ is_encoded = account_data.get("seed_phrase_encoded", False)
423
404
 
424
- encrypted_data = config["substrate"].get("seed_phrase")
425
- salt = config["substrate"].get("seed_phrase_salt")
405
+ if not is_encoded:
406
+ return account_data.get("seed_phrase")
426
407
 
427
- if not encrypted_data or not salt:
428
- print("Error: No encrypted seed phrase found or missing salt")
429
- return None
408
+ encrypted_data = account_data.get("seed_phrase")
409
+ salt = account_data.get("seed_phrase_salt")
430
410
 
431
- # Get password from user if not provided
432
- if password is None:
433
- password = getpass.getpass("Enter password to decrypt seed phrase: ")
411
+ if not encrypted_data or not salt:
412
+ print("Error: No encrypted seed phrase found or missing salt")
413
+ return None
434
414
 
435
- # Decrypt the seed phrase
436
- return decrypt_with_password(encrypted_data, salt, password)
415
+ # Get password from user if not provided
416
+ if password is None:
417
+ password = getpass.getpass("Enter password to decrypt seed phrase: \n\n")
437
418
 
438
- except Exception as e:
439
- print(f"Error decrypting seed phrase: {e}")
440
- return None
419
+ # Decrypt the seed phrase
420
+ return decrypt_with_password(encrypted_data, salt, password)
441
421
 
442
422
 
443
423
  def get_seed_phrase(
@@ -448,45 +428,31 @@ def get_seed_phrase(
448
428
 
449
429
  Args:
450
430
  password: Optional password for decryption (if None and needed, will prompt)
451
- account_name: Optional account name (if None, uses active account or legacy mode)
431
+ account_name: Optional account name (if None, uses active account)
452
432
 
453
433
  Returns:
454
434
  Optional[str]: The seed phrase, or None if not available
455
435
  """
456
436
  config = load_config()
457
437
 
458
- # Determine if we're using multi-account mode
459
- if account_name is not None or config["substrate"].get("active_account"):
460
- # Multi-account mode
461
- name_to_use = account_name or config["substrate"].get("active_account")
438
+ # Only use the multi-account system
439
+ name_to_use = account_name or config["substrate"].get("active_account")
462
440
 
463
- if not name_to_use:
464
- print("Error: No account specified and no active account")
465
- return None
466
-
467
- if name_to_use not in config["substrate"].get("accounts", {}):
468
- print(f"Error: Account '{name_to_use}' not found")
469
- return None
441
+ if not name_to_use:
442
+ print("Error: No account specified and no active account")
443
+ return None
470
444
 
471
- account_data = config["substrate"]["accounts"][name_to_use]
472
- is_encoded = account_data.get("seed_phrase_encoded", False)
445
+ if name_to_use not in config["substrate"].get("accounts", {}):
446
+ print(f"Error: Account '{name_to_use}' not found")
447
+ return None
473
448
 
474
- else:
475
- # Legacy mode - single account
476
- is_encoded = config["substrate"].get("seed_phrase_encoded", False)
449
+ account_data = config["substrate"]["accounts"][name_to_use]
450
+ is_encoded = account_data.get("seed_phrase_encoded", False)
477
451
 
478
452
  if is_encoded:
479
- # If encoded, decrypt it
480
- return decrypt_seed_phrase(password, account_name)
453
+ return decrypt_seed_phrase(password, name_to_use)
481
454
  else:
482
- # If not encoded, just return the plain text seed phrase
483
- if account_name is not None or config["substrate"].get("active_account"):
484
- # Multi-account mode
485
- name_to_use = account_name or config["substrate"].get("active_account")
486
- return config["substrate"]["accounts"][name_to_use].get("seed_phrase")
487
- else:
488
- # Legacy mode
489
- return config["substrate"].get("seed_phrase")
455
+ return account_data.get("seed_phrase")
490
456
 
491
457
 
492
458
  def set_seed_phrase(
@@ -502,7 +468,7 @@ def set_seed_phrase(
502
468
  seed_phrase: The seed phrase to store
503
469
  encode: Whether to encrypt the seed phrase (requires password)
504
470
  password: Optional password for encryption (if None and encode=True, will prompt)
505
- account_name: Optional name for the account (if None, uses legacy mode or active account)
471
+ account_name: Optional name for the account (if None, uses active account)
506
472
 
507
473
  Returns:
508
474
  bool: True if saving was successful, False otherwise
@@ -520,30 +486,31 @@ def set_seed_phrase(
520
486
  except Exception as e:
521
487
  print(f"Warning: Could not derive SS58 address: {e}")
522
488
 
523
- # Determine if we're using multi-account mode
524
- if account_name is not None:
525
- # Multi-account mode
526
- if "accounts" not in config["substrate"]:
527
- config["substrate"]["accounts"] = {}
528
-
529
- # Store the account data
530
- config["substrate"]["accounts"][account_name] = {
531
- "seed_phrase": seed_phrase,
532
- "seed_phrase_encoded": False,
533
- "seed_phrase_salt": None,
534
- "ss58_address": ss58_address,
535
- }
536
-
537
- # Set as active account if no active account exists
538
- if not config["substrate"].get("active_account"):
539
- config["substrate"]["active_account"] = account_name
540
-
541
- else:
542
- # Legacy mode - single account
543
- config["substrate"]["seed_phrase"] = seed_phrase
544
- config["substrate"]["seed_phrase_encoded"] = False
545
- config["substrate"]["seed_phrase_salt"] = None
546
- config["substrate"]["ss58_address"] = ss58_address
489
+ # Only use multi-account mode
490
+ # Use active account if no account specified
491
+ name_to_use = account_name
492
+ if name_to_use is None:
493
+ name_to_use = config["substrate"].get("active_account")
494
+ if not name_to_use:
495
+ # If no active account, we need a name
496
+ print("Error: No account name specified and no active account")
497
+ return False
498
+
499
+ # Ensure accounts structure exists
500
+ if "accounts" not in config["substrate"]:
501
+ config["substrate"]["accounts"] = {}
502
+
503
+ # Store the account data
504
+ config["substrate"]["accounts"][name_to_use] = {
505
+ "seed_phrase": seed_phrase,
506
+ "seed_phrase_encoded": False,
507
+ "seed_phrase_salt": None,
508
+ "ss58_address": ss58_address,
509
+ }
510
+
511
+ # Set as active account if no active account exists
512
+ if not config["substrate"].get("active_account"):
513
+ config["substrate"]["active_account"] = name_to_use
547
514
 
548
515
  return save_config(config)
549
516
 
hippius_sdk/ipfs.py CHANGED
@@ -816,6 +816,9 @@ class IPFSClient:
816
816
  file_data = self.encrypt_data(file_data)
817
817
 
818
818
  # Step 2: Split the file into chunks for erasure coding
819
+ chunk_size = int(chunk_size)
820
+ chunk_size = max(1, chunk_size) # Ensure it's at least 1 byte
821
+
819
822
  chunks = []
820
823
  chunk_positions = []
821
824
  for i in range(0, len(file_data), chunk_size):
@@ -825,7 +828,7 @@ class IPFSClient:
825
828
 
826
829
  # Pad the last chunk if necessary
827
830
  if chunks and len(chunks[-1]) < chunk_size:
828
- pad_size = chunk_size - len(chunks[-1])
831
+ pad_size = int(chunk_size - len(chunks[-1]))
829
832
  chunks[-1] = chunks[-1] + b"\0" * pad_size
830
833
 
831
834
  # If we don't have enough chunks for the requested parameters, adjust
@@ -1386,6 +1389,7 @@ class IPFSClient:
1386
1389
  max_retries: int = 3,
1387
1390
  verbose: bool = True,
1388
1391
  progress_callback: Optional[Callable[[str, int, int], None]] = None,
1392
+ publish: bool = True,
1389
1393
  ) -> Dict[str, Any]:
1390
1394
  """
1391
1395
  Erasure code a file, upload the chunks to IPFS, and store in the Hippius marketplace.
@@ -1404,15 +1408,21 @@ class IPFSClient:
1404
1408
  verbose: Whether to print progress information
1405
1409
  progress_callback: Optional callback function for progress updates
1406
1410
  Function receives (stage_name, current, total)
1411
+ publish: Whether to publish to the blockchain (True) or just perform local
1412
+ erasure coding without publishing (False). When False, no password
1413
+ is needed for seed phrase access.
1407
1414
 
1408
1415
  Returns:
1409
- dict: Result including metadata CID and transaction hash
1416
+ dict: Result including metadata CID and transaction hash (if published)
1410
1417
 
1411
1418
  Raises:
1412
1419
  ValueError: If parameters are invalid
1413
1420
  RuntimeError: If processing fails
1414
1421
  """
1415
- # Step 1: Erasure code the file and upload chunks
1422
+ # Step 1: Create substrate client if we need it and are publishing
1423
+ if substrate_client is None and publish:
1424
+ substrate_client = SubstrateClient()
1425
+ # Step 2: Erasure code the file and upload chunks
1416
1426
  metadata = await self.erasure_code_file(
1417
1427
  file_path=file_path,
1418
1428
  k=k,
@@ -1424,50 +1434,52 @@ class IPFSClient:
1424
1434
  progress_callback=progress_callback,
1425
1435
  )
1426
1436
 
1427
- # Step 2: Create substrate client if we need it
1428
- if substrate_client is None:
1429
- substrate_client = SubstrateClient()
1430
-
1431
1437
  original_file = metadata["original_file"]
1432
1438
  metadata_cid = metadata["metadata_cid"]
1433
1439
 
1434
- # Create a list to hold all the file inputs (metadata + all chunks)
1435
- all_file_inputs = []
1440
+ # Initialize transaction hash variable
1441
+ tx_hash = None
1436
1442
 
1437
- # Step 3: Prepare metadata file for storage
1438
- if verbose:
1439
- print(
1440
- f"Preparing to store metadata and {len(metadata['chunks'])} chunks in the Hippius marketplace..."
1441
- )
1443
+ # Only proceed with blockchain storage if publish is True
1444
+ if publish:
1445
+ # Create a list to hold all the file inputs (metadata + all chunks)
1446
+ all_file_inputs = []
1442
1447
 
1443
- # Create a file input for the metadata file
1444
- metadata_file_input = FileInput(
1445
- file_hash=metadata_cid, file_name=f"{original_file['name']}.ec_metadata"
1446
- )
1447
- all_file_inputs.append(metadata_file_input)
1448
+ # Step 3: Prepare metadata file for storage
1449
+ if verbose:
1450
+ print(
1451
+ f"Preparing to store metadata and {len(metadata['chunks'])} chunks in the Hippius marketplace..."
1452
+ )
1448
1453
 
1449
- # Step 4: Add all chunks to the storage request
1450
- if verbose:
1451
- print("Adding all chunks to storage request...")
1452
-
1453
- for i, chunk in enumerate(metadata["chunks"]):
1454
- # Extract the CID string from the chunk's cid dictionary
1455
- chunk_cid = (
1456
- chunk["cid"]["cid"]
1457
- if isinstance(chunk["cid"], dict) and "cid" in chunk["cid"]
1458
- else chunk["cid"]
1454
+ # Create a file input for the metadata file
1455
+ metadata_file_input = FileInput(
1456
+ file_hash=metadata_cid, file_name=f"{original_file['name']}.ec_metadata"
1459
1457
  )
1460
- chunk_file_input = FileInput(file_hash=chunk_cid, file_name=chunk["name"])
1461
- all_file_inputs.append(chunk_file_input)
1458
+ all_file_inputs.append(metadata_file_input)
1462
1459
 
1463
- # Print progress for large numbers of chunks
1464
- if verbose and (i + 1) % 50 == 0:
1465
- print(
1466
- f" Prepared {i + 1}/{len(metadata['chunks'])} chunks for storage"
1460
+ # Step 4: Add all chunks to the storage request
1461
+ if verbose:
1462
+ print("Adding all chunks to storage request...")
1463
+
1464
+ for i, chunk in enumerate(metadata["chunks"]):
1465
+ # Extract the CID string from the chunk's cid dictionary
1466
+ chunk_cid = (
1467
+ chunk["cid"]["cid"]
1468
+ if isinstance(chunk["cid"], dict) and "cid" in chunk["cid"]
1469
+ else chunk["cid"]
1470
+ )
1471
+ chunk_file_input = FileInput(
1472
+ file_hash=chunk_cid, file_name=chunk["name"]
1467
1473
  )
1474
+ all_file_inputs.append(chunk_file_input)
1468
1475
 
1469
- # Step 5: Submit the storage request for all files
1470
- try:
1476
+ # Print progress for large numbers of chunks
1477
+ if verbose and (i + 1) % 50 == 0:
1478
+ print(
1479
+ f" Prepared {i + 1}/{len(metadata['chunks'])} chunks for storage"
1480
+ )
1481
+
1482
+ # Step 5: Submit the storage request for all files
1471
1483
  if verbose:
1472
1484
  print(
1473
1485
  f"Submitting storage request for 1 metadata file and {len(metadata['chunks'])} chunks..."
@@ -1476,7 +1488,6 @@ class IPFSClient:
1476
1488
  tx_hash = await substrate_client.storage_request(
1477
1489
  files=all_file_inputs, miner_ids=miner_ids
1478
1490
  )
1479
-
1480
1491
  if verbose:
1481
1492
  print("Successfully stored all files in marketplace!")
1482
1493
  print(f"Transaction hash: {tx_hash}")
@@ -1485,17 +1496,27 @@ class IPFSClient:
1485
1496
  f"Total files stored: {len(all_file_inputs)} (1 metadata + {len(metadata['chunks'])} chunks)"
1486
1497
  )
1487
1498
 
1488
- return {
1499
+ result = {
1489
1500
  "metadata": metadata,
1490
1501
  "metadata_cid": metadata_cid,
1491
1502
  "transaction_hash": tx_hash,
1492
1503
  "total_files_stored": len(all_file_inputs),
1493
1504
  }
1505
+ else:
1506
+ # Not publishing to blockchain (--no-publish flag used)
1507
+ if verbose:
1508
+ print("Not publishing to blockchain (--no-publish flag used)")
1509
+ print(f"Metadata CID: {metadata_cid}")
1510
+ print(f"Total chunks: {len(metadata['chunks'])}")
1494
1511
 
1495
- except Exception as e:
1496
- print(f"Error storing files in marketplace: {str(e)}")
1497
- # Return the metadata even if storage fails
1498
- return {"metadata": metadata, "metadata_cid": metadata_cid, "error": str(e)}
1512
+ result = {
1513
+ "metadata": metadata,
1514
+ "metadata_cid": metadata_cid,
1515
+ "total_files_stored": len(metadata["chunks"])
1516
+ + 1, # +1 for metadata file
1517
+ }
1518
+
1519
+ return result
1499
1520
 
1500
1521
  async def delete_file(
1501
1522
  self, cid: str, cancel_from_blockchain: bool = True