@@ -333,3 +333,76 @@ def get_viewable_properties(self, urn: str, guid: str) -> dict:
333333 # TODO: what about the EMEA endpoint?
334334 endpoint = "/designdata/{}/metadata/{}/properties" .format (urn , guid )
335335 return self ._get (endpoint , scopes = READ_SCOPES ).json ()
336+
337+ def get_derivative_info (self , urn : str , deriv_urn : str ) -> dict :
338+ """
339+ Return information about the specified derivative.
340+
341+ **Documentation**:
342+ https://forge.autodesk.com/en/docs/model-derivative/v2/reference/http/urn-manifest-derivativeurn-HEAD
343+
344+ Args:
345+ urn (str): Base64-encoded ID of the source file.
346+ deriv_urn (str): ID of one of the derivatives generated from the source file.
347+
348+ Returns:
349+ dict: Derivative information, currently with just a single property, "size",
350+ indicating the size of the derivative in bytes.
351+ """
352+ # TODO: what about the EMEA endpoint?
353+ endpoint = "/designdata/{}/manifest/{}" .format (urn , deriv_urn )
354+ resp = self ._head (endpoint , scopes = READ_SCOPES )
355+ return { "size" : int (resp .headers ["Content-Length" ]) }
356+
357+ def get_derivative (self , urn : str , deriv_urn : str , byte_range : tuple = None ) -> bytes :
358+ """
359+ Download a derivative generated from a specific source model. To download the derivative,
360+ you need to specify its URN which can be retrieved from the Model Derivative manifest.
361+
362+ **Documentation**:
363+ https://forge.autodesk.com/en/docs/model-derivative/v2/reference/http/urn-manifest-derivativeurn-GET
364+
365+ Args:
366+ urn (str): Base64-encoded ID of the source file.
367+ deriv_urn (str): ID of one of the derivatives generated from the source file.
368+ byte_range ((int,int), optional): Optional tuple with first and last byte
369+ of a range to download.
370+
371+ Returns:
372+ bytes: Derivative content.
373+ """
374+ # TODO: what about the EMEA endpoint?
375+ endpoint = "/designdata/{}/manifest/{}" .format (urn , deriv_urn )
376+ headers = {}
377+ if byte_range :
378+ headers ["Range" ] = "bytes={}-{}" .format (byte_range [0 ], byte_range [1 ])
379+ return self ._get (endpoint , scopes = READ_SCOPES , headers = headers ).content
380+
381+ def get_derivative_chunked (self , urn : str , deriv_urn : str , chunk_size : int = 1024 * 1024 ) -> bytes :
382+ """
383+ Download complete derivative in chunks of specific size.
384+
385+ **Documentation**:
386+ https://forge.autodesk.com/en/docs/model-derivative/v2/reference/http/urn-manifest-derivativeurn-GET
387+
388+ Args:
389+ urn (str): Base64-encoded ID of the source file.
390+ deriv_urn (str): ID of one of the derivatives generated from the source file.
391+ chunk_size (int, optional): Size of individual chunks (in bytes).
392+
393+ Returns:
394+ bytes: Derivative content.
395+ """
396+ deriv_info = self .get_derivative_info (urn , deriv_urn )
397+ buff = bytes ()
398+ # TODO: what about the EMEA endpoint?
399+ downloaded_bytes = 0
400+ while downloaded_bytes < deriv_info ["size" ]:
401+ byte_range = (
402+ downloaded_bytes ,
403+ min (downloaded_bytes + chunk_size - 1 , deriv_info ["size" ] - 1 )
404+ )
405+ # TODO: better way to concat buffers in memory?
406+ buff = buff + self .get_derivative (urn , deriv_urn , byte_range )
407+ downloaded_bytes += byte_range [1 ] - byte_range [0 ] + 1
408+ return buff
0 commit comments