Skip to content

Commit

Permalink
Merge pull request #62 from deepgram/synchronous-prerecorded
Browse files Browse the repository at this point in the history
Adding new feature non-async prerecorded
  • Loading branch information
geekchick authored Nov 10, 2022
2 parents 134ee8c + bb57b4d commit 399cbeb
Show file tree
Hide file tree
Showing 3 changed files with 135 additions and 2 deletions.
22 changes: 22 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,28 @@ pip install deepgram-sdk

#### Transcribe an Existing File or Pre-recorded Audio

```python
from deepgram import Deepgram
import json

DEEPGRAM_API_KEY = 'YOUR_API_KEY'
PATH_TO_FILE = 'some/file.wav'

def main():
# Initializes the Deepgram SDK
deepgram = Deepgram(DEEPGRAM_API_KEY)
# Open the audio file
with open(PATH_TO_FILE, 'rb') as audio:
# ...or replace mimetype as appropriate
source = {'buffer': audio, 'mimetype': 'audio/wav'}
response = deepgram.transcription.sync_prerecorded(source, {'punctuate': True})
print(json.dumps(response, indent=4))

main()
```

#### Transcribe an Existing File or Pre-recorded Audio Asynchronously

```python
from deepgram import Deepgram
import asyncio, json
Expand Down
44 changes: 44 additions & 0 deletions deepgram/_utils.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
from typing import Any, Union, Optional, IO, Mapping, Tuple, List, cast
import aiohttp
import urllib.parse
import urllib.request
import urllib.error
import io
import json
import re
Expand Down Expand Up @@ -117,6 +119,48 @@ async def attempt():
return await attempt()


def _sync_request(
path: str, options: Options,
method: str = 'GET', payload: Payload = None,
headers: Optional[Mapping[str, str]] = None
) -> Any:
if headers is None:
headers = {}
destination = cast(str, options.get('api_url', DEFAULT_ENDPOINT)) + path
updated_headers = _prepare_headers(options, headers)

def attempt():
req = urllib.request.Request(
destination,
data=_normalize_payload(payload),
headers=updated_headers,
method=method)
try:
with urllib.request.urlopen(req) as resp:
content = resp.read().strip()
if not content:
return None
body = json.loads(content)
if body.get('error'):
raise Exception(f'DG: {content}')
return body
except urllib.error.URLError as exc:
raise (Exception(f'DG: {exc}') if exc.status < 500 else exc)

tries = RETRY_COUNT
while tries > 0:
try:
return attempt()
except urllib.error.URLError as exc:
if isinstance(payload, io.IOBase):
raise exc # stream is now invalid as payload
# the way aiohttp handles streaming form data
# means that just seeking this back still runs into issues
tries -= 1
continue
return attempt()


async def _socket_connect(
path: str, options: Options, headers: Optional[Mapping[str, str]] = None
) -> websockets.client.WebSocketClientProtocol:
Expand Down
71 changes: 69 additions & 2 deletions deepgram/transcription.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,11 @@
TranscriptionSource, PrerecordedTranscriptionResponse,
LiveTranscriptionResponse, Metadata, EventHandler)
from ._enums import LiveTranscriptionEvent
from ._utils import _request, _make_query_string, _socket_connect
from ._utils import _request, _sync_request, _make_query_string, _socket_connect


class PrerecordedTranscription:
"""This class provides an interface for doing transcription on prerecorded audio files."""
"""This class provides an interface for doing transcription asynchronously on prerecorded audio files."""

_root = "/listen"

Expand Down Expand Up @@ -63,6 +63,58 @@ async def __call__(
)


class SyncPrerecordedTranscription:
"""This class provides an interface for doing transcription synchronously on prerecorded audio files."""

_root = "/listen"

def __init__(self, options: Options,
transcription_options: PrerecordedOptions) -> None:
"""
This function initializes the options and transcription_options for the PrerecordedTranscription class.
:param options:Options: Used to Pass in the options for the transcription.
:param transcription_options:PrerecordedOptions: Used to Specify the transcription options for a prerecorded audio file.
:return: Nothing.
"""

self.options = options
self.transcription_options = transcription_options

def __call__(
self, source: TranscriptionSource
) -> PrerecordedTranscriptionResponse:

"""
The __call__ function is a special method that allows the class to be called
as a function. This is useful for creating instances of the class, where we can
call `SyncPrerecordedTranscription()` and pass in arguments to set up an instance of
the class. For example:
sync_prerecorded_transcription = SyncPrerecordedTranscription(...)
:param source:TranscriptionSource: Used to Pass in the audio file.
:return: A `prerecordedtranscriptionresponse` object, which contains the transcription results.
"""

if 'buffer' in source and 'mimetype' not in source:
raise Exception(
'DG: Mimetype must be provided if the source is bytes'
)
payload = cast(
Union[bytes, Dict],
source.get('buffer', {'url': source.get('url')})
)
content_type = cast(str, source.get('mimetype', 'application/json'))
return _sync_request(
f'{self._root}{_make_query_string(self.transcription_options)}',
self.options, method='POST', payload=payload,
headers={'Content-Type': content_type}
)


class LiveTranscription:
"""
This class allows you to perform live transcription by connecting to Deepgram's Transcribe Streaming API.
Expand Down Expand Up @@ -283,6 +335,21 @@ async def prerecorded(
self.options, full_options
)(source)


def sync_prerecorded(
self, source: TranscriptionSource,
options: PrerecordedOptions = None, **kwargs
) -> PrerecordedTranscriptionResponse:
"""Retrieves a transcription for an already-existing audio file,
local or web-hosted."""
if options is None:
options = {}
full_options = cast(PrerecordedOptions, {**options, **kwargs})
return SyncPrerecordedTranscription(
self.options, full_options
)(source)


async def live(
self, options: LiveOptions = None, **kwargs
) -> LiveTranscription:
Expand Down

0 comments on commit 399cbeb

Please sign in to comment.