deepctl-cmd-transcribe 0.1.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- deepctl_cmd_transcribe/__init__.py +6 -0
- deepctl_cmd_transcribe/command.py +242 -0
- deepctl_cmd_transcribe/models.py +14 -0
- deepctl_cmd_transcribe-0.1.0.dist-info/METADATA +26 -0
- deepctl_cmd_transcribe-0.1.0.dist-info/RECORD +8 -0
- deepctl_cmd_transcribe-0.1.0.dist-info/WHEEL +5 -0
- deepctl_cmd_transcribe-0.1.0.dist-info/entry_points.txt +2 -0
- deepctl_cmd_transcribe-0.1.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,242 @@
|
|
|
1
|
+
"""Transcribe command for deepctl."""
|
|
2
|
+
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
from typing import Any
|
|
5
|
+
|
|
6
|
+
from deepctl_core import (
|
|
7
|
+
AuthManager,
|
|
8
|
+
BaseCommand,
|
|
9
|
+
BaseResult,
|
|
10
|
+
Config,
|
|
11
|
+
DeepgramClient,
|
|
12
|
+
)
|
|
13
|
+
from deepctl_shared_utils import validate_audio_file, validate_url
|
|
14
|
+
from rich.console import Console
|
|
15
|
+
|
|
16
|
+
from .models import TranscribeResult
|
|
17
|
+
|
|
18
|
+
console = Console()
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class TranscribeCommand(BaseCommand):
|
|
22
|
+
"""Command for transcribing audio files and URLs."""
|
|
23
|
+
|
|
24
|
+
name = "transcribe"
|
|
25
|
+
help = "Transcribe audio files or URLs using Deepgram"
|
|
26
|
+
short_help = "Transcribe audio"
|
|
27
|
+
|
|
28
|
+
# Transcription requires authentication
|
|
29
|
+
requires_auth = True
|
|
30
|
+
requires_project = False # Project ID is optional for transcription
|
|
31
|
+
ci_friendly = True
|
|
32
|
+
|
|
33
|
+
def get_arguments(self) -> list[dict[str, Any]]:
|
|
34
|
+
"""Get command arguments and options."""
|
|
35
|
+
return [
|
|
36
|
+
{
|
|
37
|
+
"name": "source",
|
|
38
|
+
"help": "Audio file path or URL to transcribe",
|
|
39
|
+
"type": str,
|
|
40
|
+
"required": True,
|
|
41
|
+
"nargs": 1,
|
|
42
|
+
},
|
|
43
|
+
{
|
|
44
|
+
"names": ["--model", "-m"],
|
|
45
|
+
"help": "Deepgram model to use",
|
|
46
|
+
"type": str,
|
|
47
|
+
"default": "nova-2",
|
|
48
|
+
"is_option": True,
|
|
49
|
+
},
|
|
50
|
+
{
|
|
51
|
+
"names": ["--language", "-l"],
|
|
52
|
+
"help": "Language code (e.g., en-US, es-ES)",
|
|
53
|
+
"type": str,
|
|
54
|
+
"default": "en-US",
|
|
55
|
+
"is_option": True,
|
|
56
|
+
},
|
|
57
|
+
{
|
|
58
|
+
"names": ["--smart-format"],
|
|
59
|
+
"help": "Enable smart formatting",
|
|
60
|
+
"is_flag": True,
|
|
61
|
+
"default": True,
|
|
62
|
+
"is_option": True,
|
|
63
|
+
},
|
|
64
|
+
{
|
|
65
|
+
"names": ["--punctuate"],
|
|
66
|
+
"help": "Enable punctuation",
|
|
67
|
+
"is_flag": True,
|
|
68
|
+
"default": True,
|
|
69
|
+
"is_option": True,
|
|
70
|
+
},
|
|
71
|
+
{
|
|
72
|
+
"names": ["--diarize"],
|
|
73
|
+
"help": "Enable speaker diarization",
|
|
74
|
+
"is_flag": True,
|
|
75
|
+
"is_option": True,
|
|
76
|
+
},
|
|
77
|
+
{
|
|
78
|
+
"names": ["--summarize"],
|
|
79
|
+
"help": "Enable summarization",
|
|
80
|
+
"is_flag": True,
|
|
81
|
+
"is_option": True,
|
|
82
|
+
},
|
|
83
|
+
{
|
|
84
|
+
"names": ["--detect-topics"],
|
|
85
|
+
"help": "Enable topic detection",
|
|
86
|
+
"is_flag": True,
|
|
87
|
+
"is_option": True,
|
|
88
|
+
},
|
|
89
|
+
{
|
|
90
|
+
"names": ["--save-to", "-s"],
|
|
91
|
+
"help": "Save transcription to file",
|
|
92
|
+
"type": str,
|
|
93
|
+
"is_option": True,
|
|
94
|
+
},
|
|
95
|
+
{
|
|
96
|
+
"names": ["--no-validate"],
|
|
97
|
+
"help": "Skip input validation",
|
|
98
|
+
"is_flag": True,
|
|
99
|
+
"is_option": True,
|
|
100
|
+
},
|
|
101
|
+
]
|
|
102
|
+
|
|
103
|
+
def handle(
|
|
104
|
+
self,
|
|
105
|
+
config: Config,
|
|
106
|
+
auth_manager: AuthManager,
|
|
107
|
+
client: DeepgramClient,
|
|
108
|
+
**kwargs: Any,
|
|
109
|
+
) -> BaseResult:
|
|
110
|
+
"""Handle transcribe command."""
|
|
111
|
+
source = kwargs.get("source")
|
|
112
|
+
model = kwargs.get("model", "nova-2")
|
|
113
|
+
language = kwargs.get("language", "en-US")
|
|
114
|
+
smart_format = kwargs.get("smart_format", True)
|
|
115
|
+
punctuate = kwargs.get("punctuate", True)
|
|
116
|
+
diarize = kwargs.get("diarize", False)
|
|
117
|
+
summarize = kwargs.get("summarize", False)
|
|
118
|
+
detect_topics = kwargs.get("detect_topics", False)
|
|
119
|
+
save_to = kwargs.get("save_to")
|
|
120
|
+
no_validate = kwargs.get("no_validate", False)
|
|
121
|
+
|
|
122
|
+
# Check if source is provided
|
|
123
|
+
if not source:
|
|
124
|
+
return BaseResult(
|
|
125
|
+
status="error", message="No audio source provided"
|
|
126
|
+
)
|
|
127
|
+
|
|
128
|
+
# Validate input if not skipped
|
|
129
|
+
if not no_validate and not self._validate_source(source):
|
|
130
|
+
return BaseResult(status="error", message="Invalid audio source")
|
|
131
|
+
|
|
132
|
+
# Build transcription options
|
|
133
|
+
options = {
|
|
134
|
+
"model": model,
|
|
135
|
+
"language": language,
|
|
136
|
+
"smart_format": str(smart_format).lower(),
|
|
137
|
+
"punctuate": str(punctuate).lower(),
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
# Only add optional features if explicitly enabled
|
|
141
|
+
if diarize:
|
|
142
|
+
options["diarize"] = "true"
|
|
143
|
+
if summarize:
|
|
144
|
+
options["summarize"] = "true"
|
|
145
|
+
if detect_topics:
|
|
146
|
+
options["detect_topics"] = "true"
|
|
147
|
+
|
|
148
|
+
try:
|
|
149
|
+
console.print(f"[blue]Transcribing:[/blue] {source}")
|
|
150
|
+
console.print(f"[dim]Model:[/dim] {model}")
|
|
151
|
+
console.print(f"[dim]Language:[/dim] {language}")
|
|
152
|
+
|
|
153
|
+
# Determine if source is file or URL
|
|
154
|
+
if self._is_url(source):
|
|
155
|
+
console.print("[dim]Processing URL...[/dim]")
|
|
156
|
+
result = client.transcribe_url(source, options)
|
|
157
|
+
else:
|
|
158
|
+
console.print("[dim]Processing file...[/dim]")
|
|
159
|
+
result = client.transcribe_file(source, options)
|
|
160
|
+
|
|
161
|
+
# Convert SDK response object to dict
|
|
162
|
+
result_dict = result
|
|
163
|
+
if hasattr(result, "to_dict"):
|
|
164
|
+
result_dict = result.to_dict()
|
|
165
|
+
elif hasattr(result, "dict"):
|
|
166
|
+
result_dict = result.dict()
|
|
167
|
+
elif hasattr(result, "__dict__"):
|
|
168
|
+
result_dict = result.__dict__
|
|
169
|
+
|
|
170
|
+
# Extract transcript text
|
|
171
|
+
transcript = self._extract_transcript(result_dict)
|
|
172
|
+
|
|
173
|
+
# Save to file if requested
|
|
174
|
+
if save_to:
|
|
175
|
+
self._save_transcript(transcript, save_to)
|
|
176
|
+
console.print(
|
|
177
|
+
f"[green]✓[/green] Transcript saved to: {save_to}"
|
|
178
|
+
)
|
|
179
|
+
|
|
180
|
+
# Return structured result
|
|
181
|
+
return TranscribeResult(
|
|
182
|
+
status="success",
|
|
183
|
+
source=source,
|
|
184
|
+
model=model,
|
|
185
|
+
language=language,
|
|
186
|
+
transcript=transcript,
|
|
187
|
+
full_result=result_dict,
|
|
188
|
+
saved_to=save_to,
|
|
189
|
+
)
|
|
190
|
+
|
|
191
|
+
except Exception as e:
|
|
192
|
+
console.print(f"[red]Transcription failed:[/red] {e}")
|
|
193
|
+
return BaseResult(status="error", message=str(e))
|
|
194
|
+
|
|
195
|
+
def _validate_source(self, source: str) -> bool:
|
|
196
|
+
"""Validate audio source (file or URL)."""
|
|
197
|
+
if self._is_url(source):
|
|
198
|
+
return validate_url(source, check_accessibility=True)
|
|
199
|
+
else:
|
|
200
|
+
return validate_audio_file(source)
|
|
201
|
+
|
|
202
|
+
def _is_url(self, source: str) -> bool:
|
|
203
|
+
"""Check if source is a URL."""
|
|
204
|
+
return source.startswith(("http://", "https://"))
|
|
205
|
+
|
|
206
|
+
def _extract_transcript(self, result: dict[str, Any]) -> str:
|
|
207
|
+
"""Extract transcript text from API result."""
|
|
208
|
+
try:
|
|
209
|
+
# Handle different response formats
|
|
210
|
+
if "results" in result and "channels" in result["results"]:
|
|
211
|
+
# Standard format
|
|
212
|
+
channels = result["results"]["channels"]
|
|
213
|
+
if channels and "alternatives" in channels[0]:
|
|
214
|
+
return str(channels[0]["alternatives"][0]["transcript"])
|
|
215
|
+
|
|
216
|
+
# Fallback to looking for transcript in other locations
|
|
217
|
+
if "transcript" in result:
|
|
218
|
+
return str(result["transcript"])
|
|
219
|
+
|
|
220
|
+
return "No transcript found in response"
|
|
221
|
+
|
|
222
|
+
except Exception as e:
|
|
223
|
+
console.print(
|
|
224
|
+
f"[yellow]Warning:[/yellow] Could not extract transcript: {e}"
|
|
225
|
+
)
|
|
226
|
+
return str(result)
|
|
227
|
+
|
|
228
|
+
def _save_transcript(self, transcript: str, file_path: str) -> None:
|
|
229
|
+
"""Save transcript to file."""
|
|
230
|
+
try:
|
|
231
|
+
path = Path(file_path)
|
|
232
|
+
|
|
233
|
+
# Create directory if it doesn't exist
|
|
234
|
+
path.parent.mkdir(parents=True, exist_ok=True)
|
|
235
|
+
|
|
236
|
+
# Write transcript
|
|
237
|
+
with open(path, "w", encoding="utf-8") as f:
|
|
238
|
+
f.write(transcript)
|
|
239
|
+
|
|
240
|
+
except Exception as e:
|
|
241
|
+
console.print(f"[red]Error saving transcript:[/red] {e}")
|
|
242
|
+
raise
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
"""Models for transcribe command."""
|
|
2
|
+
|
|
3
|
+
from typing import Any
|
|
4
|
+
|
|
5
|
+
from deepctl_core import BaseResult
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class TranscribeResult(BaseResult):
|
|
9
|
+
source: str
|
|
10
|
+
model: str
|
|
11
|
+
language: str
|
|
12
|
+
transcript: str
|
|
13
|
+
saved_to: str | None = None
|
|
14
|
+
full_result: dict[str, Any]
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: deepctl-cmd-transcribe
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Transcribe command for deepctl
|
|
5
|
+
Author-email: Deepgram <devrel@deepgram.com>
|
|
6
|
+
Maintainer-email: Deepgram <devrel@deepgram.com>
|
|
7
|
+
License-Expression: MIT
|
|
8
|
+
Keywords: deepgram,cli,transcribe,speech,audio
|
|
9
|
+
Classifier: Development Status :: 3 - Alpha
|
|
10
|
+
Classifier: Intended Audience :: Developers
|
|
11
|
+
Classifier: Programming Language :: Python :: 3
|
|
12
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
15
|
+
Requires-Python: >=3.10
|
|
16
|
+
Description-Content-Type: text/markdown
|
|
17
|
+
Requires-Dist: deepctl-core>=0.1.0
|
|
18
|
+
Requires-Dist: deepctl-shared-utils>=0.1.0
|
|
19
|
+
Requires-Dist: click>=8.0.0
|
|
20
|
+
Requires-Dist: rich>=13.0.0
|
|
21
|
+
Requires-Dist: deepgram-sdk>=3.0.0
|
|
22
|
+
Requires-Dist: pydantic>=2.0.0
|
|
23
|
+
|
|
24
|
+
# deepctl-cmd-transcribe
|
|
25
|
+
|
|
26
|
+
Transcribe command for deepctl. This package is an internal component of deepctl and is not intended for standalone use.
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
deepctl_cmd_transcribe/__init__.py,sha256=YjxxH5OpXiofMv-DALDUWRgfAFV6_PfR0xIswssBoik,176
|
|
2
|
+
deepctl_cmd_transcribe/command.py,sha256=XUf9sP0DOGo_rn4WejbF6saKVcLK3sItGAHkU1u59Co,8297
|
|
3
|
+
deepctl_cmd_transcribe/models.py,sha256=vxK8HFNKx9H5pZytDG9O3Vhd5z3NNpdg5pdNs2AeNjc,269
|
|
4
|
+
deepctl_cmd_transcribe-0.1.0.dist-info/METADATA,sha256=voVNVg-T32C56f1zs3OqLkdBNVFAtmAE_X5iCBoJaC0,972
|
|
5
|
+
deepctl_cmd_transcribe-0.1.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
6
|
+
deepctl_cmd_transcribe-0.1.0.dist-info/entry_points.txt,sha256=GWHb0MSVh5cSohFLIeKU6WY_TyrrQYMp0N1JabvGnfk,81
|
|
7
|
+
deepctl_cmd_transcribe-0.1.0.dist-info/top_level.txt,sha256=TzgA-pJuwekPmBWH9zDfckR1SFLB0_X1szopaCayyq4,23
|
|
8
|
+
deepctl_cmd_transcribe-0.1.0.dist-info/RECORD,,
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
deepctl_cmd_transcribe
|