"""Interface to ACRCloud de-duplication API.""" # https://docs.acrcloud.com/reference/console-api/buckets/dedup-files from acrcloud.acrcloud_extr_tool import create_fingerprint_by_filebuffer from ddtrace import tracer from src.common.exceptions import exceptions from requests.exceptions import ConnectionError from requests.exceptions import HTTPError import requests class TooSmallFingerprint(Exception): """Generated fingerprint was too small.""" pass class TooLargeFingerprint(Exception): """Generated fingerprint was too large.""" pass class UnableToFingerprint(Exception): """Generated fingerprint was None.""" pass class UnableToRegister(Exception): """ACRCloud API error in response.""" pass class ACRCloudClient(object): """Client for acr cloud api.""" def __init__(self, access_token, url, max_fingerprint_size=-1): """Client init. Args: access_token (str): JWT token for acr cloud api url (str): acr cloud url to send requests to """ self.access_token = access_token self.url = url self.max_fingerprint_size = max_fingerprint_size self.partner_id = 'orchard' def upload_fingerprint(self, bucket_id, identifier, raw_bytes): """Send identifier and fingerprint to API. Args: identifier (str): id for API to save fingerprint as raw_bytes (bytes): data to fingerprint Returns: str """ fingerprint = self._create_fingerprint(raw_bytes) try: response = self._call( 'POST', f'buckets/{bucket_id}/dedup-files', data={ 'partner': self.partner_id, 'id': identifier, 'db_id_nodup': 1 }, files=[ ('file', fingerprint) ] ) response.raise_for_status() body = response.json() response_data = body['data'] if 'error' in response_data: raise UnableToRegister(response_data['error']) return response_data['acr_id'] except HTTPError as e: if e.response.status_code in (504, 502, 500, 429): message_error = f'Unexpected response code from ACRCloudClient HTTP:{e.response.status_code}' # noqa:E501 raise exceptions.RetryableException(message_error) else: raise e except ConnectionError: raise exceptions.RetryableException('Connection Error from ACRCloudClient') # noqa:E501 @tracer.wrap(name='_create_fingerprint') def _create_fingerprint(self, raw_bytes): """Create "db" fingerprint from file. Endtime irrelevant, set to -1. https://github.com/acrcloud/acrcloud_sdk_python#module-acrcloud_extr_tool Args: raw_bytes (bytes): file loaded into memory Returns: bytes """ fingerprint = create_fingerprint_by_filebuffer( raw_bytes, 0, -1, True, 0 ) if fingerprint is None: raise UnableToFingerprint() elif len(fingerprint) == 0: raise TooSmallFingerprint() elif self.max_fingerprint_size > 0 and \ len(fingerprint) > self.max_fingerprint_size: raise TooLargeFingerprint() return fingerprint def _call(self, verb, endpoint, params={}, data={}, files=None): url = f'{self.url}/{endpoint}' headers = { 'Accept': 'application/json', 'Authorization': f'Bearer {self.access_token}' } return requests.request( verb, url, headers=headers, params=params, data=data, files=files )