"""Python 3 standard library example; no packages to install.""" import json import os from pathlib import Path from urllib.parse import quote from urllib.request import Request, urlopen API = 'https://api.apify.com/v2' ACTOR_INPUT = { 'urls': ['https://storage.googleapis.com/cloud-samples-data/speech/commercial_mono.wav'], 'timestamps': 'segment', } def main(): token = os.environ.get('APIFY_TOKEN') if not token: raise RuntimeError('Set APIFY_TOKEN first') def request(path, body=None): headers = {} data = None if body is not None: data = json.dumps(body).encode() headers['Content-Type'] = 'application/json' req = Request(API + path, data=data, headers=headers) # A storage download may redirect; never forward the account token to that host. req.add_unredirected_header('Authorization', f'Bearer {token}') with urlopen(req, timeout=30) as response: return response.read(), response.headers def get_json(path, body=None): return json.loads(request(path, body)[0]) def incomplete(item): billing = item.get('billing') or {} return item.get('error') or item.get('stop_processing') or billing.get('stop') or billing.get('status') in ('partial', 'unknown') # Send this POST once. A lost response requires checking Apify Console before another start. run = get_json('/actors/andronixmd~speech-to-text-transcriber/runs?maxTotalChargeUsd=0.1&memory=4096&timeout=180', ACTOR_INPUT)['data'] print(f"Run: {run['id']}") terminal = ('SUCCEEDED', 'FAILED', 'TIMED-OUT', 'ABORTED') for _ in range(12): if run['status'] in terminal: break run = get_json(f"/actor-runs/{run['id']}?waitForFinish=20")['data'] if run['status'] not in terminal: raise RuntimeError(f"Run {run['id']} is still {run['status']}; inspect it or continue GET polling, do not start again") if run['status'] != 'SUCCEEDED': raise RuntimeError(f"Run {run['id']}: {run['status']}; inspect its saved output") offset, failures = 0, 0 while True: body, headers = request(f"/datasets/{run['defaultDatasetId']}/items?format=json&offset={offset}&limit=100") rows = json.loads(body) for index, row in enumerate(rows, start=offset): item = row if not incomplete(row) and row.get('result_record_key'): record = get_json(f"/key-value-stores/{run['defaultKeyValueStoreId']}/records/{quote(row['result_record_key'], safe='')}") item = record.get('value') # Unwrap only; this example does not verify the signature. if item is None or incomplete(row) or incomplete(item) or not isinstance(item.get('text'), str) or item.get('artifact_errors'): print(f"Item {index} incomplete: {(item or row).get('error') or 'billing, content or subtitle publication requires inspection'}") failures += 1 continue print(item['text']) for extension in ('srt', 'vtt'): filename = f'transcript-{index:03d}.{extension}' body, _ = request(f"/key-value-stores/{run['defaultKeyValueStoreId']}/records/{filename}") Path(filename).write_bytes(body) print(f'Saved {filename}') offset += len(rows) if offset >= int(headers['X-Apify-Pagination-Total']): break if not rows: raise RuntimeError('Dataset pagination stopped before all results were read') if not offset: raise RuntimeError('No results; inspect the run budget and saved output') if failures or offset != len(ACTOR_INPUT['urls']): raise RuntimeError('One or more results are incomplete; inspect this run before retrying') if __name__ == '__main__': main()