@@ -1358,6 +1358,10 @@ def __init__(self,
13581358 # we may have opened ourselves, and that we need to close later
13591359 self ._backing_file = None
13601360
1361+ # Caches for manifest JSON string and parsed data
1362+ self ._manifest_json_str_cache = None
1363+ self ._manifest_data_cache = None
1364+
13611365 if stream is None :
13621366 # If we don't get a stream as param:
13631367 # Create a stream from the file path in format_or_path
@@ -1600,6 +1604,33 @@ def _cleanup_resources(self):
16001604 # Ensure we don't raise exceptions during cleanup
16011605 pass
16021606
1607+ def _get_cached_manifest_data (self ) -> Optional [dict ]:
1608+ """Get the cached manifest data, fetching and parsing if not cached.
1609+
1610+ Returns:
1611+ A dictionary containing the parsed manifest data, or None if
1612+ JSON parsing fails
1613+
1614+ Raises:
1615+ C2paError: If there was an error getting the JSON
1616+ """
1617+ if self ._manifest_data_cache is None :
1618+ if self ._manifest_json_str_cache is None :
1619+ self ._manifest_json_str_cache = self .json ()
1620+
1621+ try :
1622+ self ._manifest_data_cache = json .loads (
1623+ self ._manifest_json_str_cache
1624+ )
1625+ except json .JSONDecodeError :
1626+ # Reset cache to reattempt read, possibly
1627+ self ._manifest_data_cache = None
1628+ self ._manifest_json_str_cache = None
1629+ # Failed to parse manifest JSON
1630+ return None
1631+
1632+ return self ._manifest_data_cache
1633+
16031634 def close (self ):
16041635 """Release the reader resources.
16051636
@@ -1620,6 +1651,9 @@ def close(self):
16201651 Reader ._ERROR_MESSAGES ['cleanup_error' ].format (
16211652 str (e )))
16221653 finally :
1654+ # Clear the cache when closing
1655+ self ._manifest_json_str_cache = None
1656+ self ._manifest_data_cache = None
16231657 self ._closed = True
16241658
16251659 def json (self ) -> str :
@@ -1634,6 +1668,10 @@ def json(self) -> str:
16341668
16351669 self ._ensure_valid_state ()
16361670
1671+ # Return cached result if available
1672+ if self ._manifest_json_str_cache is not None :
1673+ return self ._manifest_json_str_cache
1674+
16371675 result = _lib .c2pa_reader_json (self ._reader )
16381676
16391677 if result is None :
@@ -1642,7 +1680,128 @@ def json(self) -> str:
16421680 raise C2paError (error )
16431681 raise C2paError ("Error during manifest parsing in Reader" )
16441682
1645- return _convert_to_py_string (result )
1683+ # Cache the result and return it
1684+ self ._manifest_json_str_cache = _convert_to_py_string (result )
1685+ return self ._manifest_json_str_cache
1686+
1687+ def get_active_manifest (self ) -> Optional [dict ]:
1688+ """Get the active manifest from the manifest store.
1689+
1690+ This method retrieves the full manifest JSON and extracts the active
1691+ manifest based on the active_manifest key.
1692+
1693+ Returns:
1694+ A dictionary containing the active manifest data, including claims,
1695+ assertions, ingredients, and signature information, or None if no
1696+ manifest is found or if there was an error parsing the JSON.
1697+
1698+ Raises:
1699+ KeyError: If the active_manifest key is missing from the JSON
1700+ """
1701+ try :
1702+ # Get cached manifest data
1703+ manifest_data = self ._get_cached_manifest_data ()
1704+ if manifest_data is None :
1705+ # raise C2paError("Failed to parse manifest JSON")
1706+ return None
1707+
1708+ # Get the active manfiest id/label
1709+ if "active_manifest" not in manifest_data :
1710+ raise KeyError ("No 'active_manifest' key found" )
1711+
1712+ active_manifest_id = manifest_data ["active_manifest" ]
1713+
1714+ # Retrieve the active manifest data using manifest id/label
1715+ if "manifests" not in manifest_data :
1716+ raise KeyError ("No 'manifests' key found in manifest data" )
1717+
1718+ manifests = manifest_data ["manifests" ]
1719+ if active_manifest_id not in manifests :
1720+ raise KeyError ("Active manifest not found in manifest store" )
1721+
1722+ return manifests [active_manifest_id ]
1723+ except C2paError .ManifestNotFound :
1724+ return None
1725+
1726+ def get_manifest (self , label : str ) -> Optional [dict ]:
1727+ """Get a specific manifest from the manifest store by its label.
1728+
1729+ This method retrieves the manifest JSON and extracts the manifest
1730+ that corresponds to the provided manifest label/ID.
1731+
1732+ Args:
1733+ label: The manifest label/ID to look up in the manifest store
1734+
1735+ Returns:
1736+ A dictionary containing the manifest data for the specified label,
1737+ or None if no manifest is found or if there was an error parsing
1738+ the JSON.
1739+
1740+ Raises:
1741+ KeyError: If the manifests key is missing from the JSON
1742+ """
1743+ try :
1744+ # Get cached manifest data
1745+ manifest_data = self ._get_cached_manifest_data ()
1746+ if manifest_data is None :
1747+ # raise C2paError("Failed to parse manifest JSON")
1748+ return None
1749+
1750+ if "manifests" not in manifest_data :
1751+ raise KeyError ("No 'manifests' key found in manifest data" )
1752+
1753+ manifests = manifest_data ["manifests" ]
1754+ if label not in manifests :
1755+ raise KeyError (f"Manifest { label } not found in manifest store" )
1756+
1757+ return manifests [label ]
1758+ except C2paError .ManifestNotFound :
1759+ return None
1760+
1761+ def get_validation_state (self ) -> Optional [str ]:
1762+ """Get the validation state of the manifest store.
1763+
1764+ This method retrieves the full manifest JSON and extracts the
1765+ validation_state field, which indicates the overall validation
1766+ status of the C2PA manifest.
1767+
1768+ Returns:
1769+ The validation state as a string,
1770+ or None if the validation_state field is not present or if no
1771+ manifest is found or if there was an error parsing the JSON.
1772+ """
1773+ try :
1774+ # Get cached manifest data
1775+ manifest_data = self ._get_cached_manifest_data ()
1776+ if manifest_data is None :
1777+ return None
1778+
1779+ return manifest_data .get ("validation_state" )
1780+ except C2paError .ManifestNotFound :
1781+ return None
1782+
1783+ def get_validation_results (self ) -> Optional [dict ]:
1784+ """Get the validation results of the manifest store.
1785+
1786+ This method retrieves the full manifest JSON and extracts
1787+ the validation_results object, which contains detailed
1788+ validation information.
1789+
1790+ Returns:
1791+ The validation results as a dictionary containing
1792+ validation details, or None if the validation_results
1793+ field is not present or if no manifest is found or if
1794+ there was an error parsing the JSON.
1795+ """
1796+ try :
1797+ # Get cached manifest data
1798+ manifest_data = self ._get_cached_manifest_data ()
1799+ if manifest_data is None :
1800+ return None
1801+
1802+ return manifest_data .get ("validation_results" )
1803+ except C2paError .ManifestNotFound :
1804+ return None
16461805
16471806 def resource_to_stream (self , uri : str , stream : Any ) -> int :
16481807 """Write a resource to a stream.
0 commit comments