hushscript 1.0.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.
- hushscript/__init__.py +97 -0
- hushscript/_client.py +687 -0
- hushscript/_clock.py +36 -0
- hushscript/_config.py +139 -0
- hushscript/_errors.py +563 -0
- hushscript/_guard.py +61 -0
- hushscript/_http.py +624 -0
- hushscript/_json.py +55 -0
- hushscript/_log.py +155 -0
- hushscript/_models.py +144 -0
- hushscript/_pacing.py +51 -0
- hushscript/_redact.py +140 -0
- hushscript/_response.py +67 -0
- hushscript/_retry.py +205 -0
- hushscript/_shape.py +197 -0
- hushscript/_shapes.py +248 -0
- hushscript/_version.py +1 -0
- hushscript/format.py +224 -0
- hushscript/generated/__init__.py +8 -0
- hushscript/generated/api/__init__.py +1 -0
- hushscript/generated/api/default/__init__.py +1 -0
- hushscript/generated/api/default/buy_minutes.py +248 -0
- hushscript/generated/api/default/claim_agent_account.py +238 -0
- hushscript/generated/api/default/complete_upload.py +238 -0
- hushscript/generated/api/default/create_agent_account_signup.py +261 -0
- hushscript/generated/api/default/create_setup_session.py +244 -0
- hushscript/generated/api/default/create_upload.py +225 -0
- hushscript/generated/api/default/get_account.py +169 -0
- hushscript/generated/api/default/get_job.py +198 -0
- hushscript/generated/api/default/get_org.py +169 -0
- hushscript/generated/api/default/get_transcript.py +226 -0
- hushscript/generated/api/default/get_transcription_context.py +173 -0
- hushscript/generated/api/default/get_upload_part_urls.py +246 -0
- hushscript/generated/api/default/heartbeat_upload.py +197 -0
- hushscript/generated/api/default/lift_transcript_hold.py +206 -0
- hushscript/generated/api/default/list_credit_ledger.py +229 -0
- hushscript/generated/api/default/list_deleted_transcripts.py +237 -0
- hushscript/generated/api/default/list_holds.py +233 -0
- hushscript/generated/api/default/list_jobs.py +249 -0
- hushscript/generated/api/default/list_org_audit.py +221 -0
- hushscript/generated/api/default/list_org_invoices.py +221 -0
- hushscript/generated/api/default/list_org_ledger.py +221 -0
- hushscript/generated/api/default/list_org_matters.py +247 -0
- hushscript/generated/api/default/list_org_members.py +221 -0
- hushscript/generated/api/default/list_org_transcripts.py +233 -0
- hushscript/generated/api/default/list_packs.py +169 -0
- hushscript/generated/api/default/list_saved_cards.py +169 -0
- hushscript/generated/api/default/list_transactions.py +225 -0
- hushscript/generated/api/default/list_transcripts.py +225 -0
- hushscript/generated/api/default/org_audit_csv.py +163 -0
- hushscript/generated/api/default/org_ledger_csv.py +163 -0
- hushscript/generated/api/default/org_usage_report.py +247 -0
- hushscript/generated/api/default/org_usage_report_csv.py +229 -0
- hushscript/generated/api/default/patch_org_member.py +237 -0
- hushscript/generated/api/default/place_transcript_hold.py +206 -0
- hushscript/generated/api/default/rotate_agent_credentials.py +177 -0
- hushscript/generated/api/default/schedule_org_custody.py +215 -0
- hushscript/generated/api/default/set_default_card.py +223 -0
- hushscript/generated/api/default/upload_part.py +249 -0
- hushscript/generated/catalog.py +138 -0
- hushscript/generated/client.py +282 -0
- hushscript/generated/errors.py +16 -0
- hushscript/generated/hints.py +28 -0
- hushscript/generated/models/__init__.py +193 -0
- hushscript/generated/models/account_response.py +114 -0
- hushscript/generated/models/active_hold.py +88 -0
- hushscript/generated/models/active_hold_kind.py +18 -0
- hushscript/generated/models/agent_claim_request.py +61 -0
- hushscript/generated/models/agent_claim_response.py +85 -0
- hushscript/generated/models/agent_credentials_rotate_response.py +101 -0
- hushscript/generated/models/agent_signup_request.py +118 -0
- hushscript/generated/models/agent_signup_request_agent.py +80 -0
- hushscript/generated/models/agent_signup_response.py +111 -0
- hushscript/generated/models/buy_minutes_body.py +89 -0
- hushscript/generated/models/card.py +132 -0
- hushscript/generated/models/cards_response.py +90 -0
- hushscript/generated/models/complete_upload_body.py +107 -0
- hushscript/generated/models/complete_upload_body_parts_type_0_item.py +69 -0
- hushscript/generated/models/context_dictionary.py +143 -0
- hushscript/generated/models/context_prompt_preset.py +118 -0
- hushscript/generated/models/create_setup_session_body.py +62 -0
- hushscript/generated/models/create_upload_body.py +254 -0
- hushscript/generated/models/create_upload_body_transcription_options_type_0.py +178 -0
- hushscript/generated/models/credit_ledger_entry.py +113 -0
- hushscript/generated/models/credit_ledger_response.py +90 -0
- hushscript/generated/models/deleted_transcript.py +148 -0
- hushscript/generated/models/deleted_transcript_cause.py +18 -0
- hushscript/generated/models/deleted_transcript_receipt_type_0.py +107 -0
- hushscript/generated/models/deleted_transcript_receipt_type_0_items_item.py +69 -0
- hushscript/generated/models/deleted_transcripts_response.py +90 -0
- hushscript/generated/models/dictionary_entry.py +123 -0
- hushscript/generated/models/dictionary_entry_category.py +37 -0
- hushscript/generated/models/error.py +155 -0
- hushscript/generated/models/error_details.py +94 -0
- hushscript/generated/models/get_upload_part_urls_response_200_type_0.py +108 -0
- hushscript/generated/models/heartbeat_upload_response_200.py +61 -0
- hushscript/generated/models/holds_response.py +90 -0
- hushscript/generated/models/job.py +309 -0
- hushscript/generated/models/job_failure_kind_type_1.py +16 -0
- hushscript/generated/models/job_failure_kind_type_2_type_1.py +16 -0
- hushscript/generated/models/job_failure_kind_type_3_type_1.py +16 -0
- hushscript/generated/models/job_list.py +90 -0
- hushscript/generated/models/lift_transcript_hold_response_200.py +61 -0
- hushscript/generated/models/list_jobs_active.py +17 -0
- hushscript/generated/models/list_org_matters_status.py +16 -0
- hushscript/generated/models/options_summary.py +135 -0
- hushscript/generated/models/org_audit_entry.py +105 -0
- hushscript/generated/models/org_audit_response.py +90 -0
- hushscript/generated/models/org_invoice.py +175 -0
- hushscript/generated/models/org_invoices_response.py +90 -0
- hushscript/generated/models/org_ledger_entry.py +155 -0
- hushscript/generated/models/org_ledger_response.py +90 -0
- hushscript/generated/models/org_matter.py +79 -0
- hushscript/generated/models/org_matter_status.py +16 -0
- hushscript/generated/models/org_matters_response.py +75 -0
- hushscript/generated/models/org_member.py +123 -0
- hushscript/generated/models/org_members_response.py +90 -0
- hushscript/generated/models/org_summary_response.py +482 -0
- hushscript/generated/models/org_transcript.py +103 -0
- hushscript/generated/models/org_transcripts_response.py +90 -0
- hushscript/generated/models/org_usage_report_csv_group_by.py +17 -0
- hushscript/generated/models/org_usage_report_group_by.py +17 -0
- hushscript/generated/models/org_usage_response.py +112 -0
- hushscript/generated/models/org_usage_response_group_by.py +17 -0
- hushscript/generated/models/org_usage_row.py +86 -0
- hushscript/generated/models/pack.py +107 -0
- hushscript/generated/models/packs_response.py +75 -0
- hushscript/generated/models/patch_org_member_body.py +102 -0
- hushscript/generated/models/patch_org_member_response_200.py +61 -0
- hushscript/generated/models/place_transcript_hold_response_200.py +61 -0
- hushscript/generated/models/presigned_part_url.py +69 -0
- hushscript/generated/models/purchase_response.py +76 -0
- hushscript/generated/models/schedule_org_custody_body.py +66 -0
- hushscript/generated/models/schedule_org_custody_body_custody.py +17 -0
- hushscript/generated/models/schedule_org_custody_response_200.py +69 -0
- hushscript/generated/models/set_default_card_body.py +61 -0
- hushscript/generated/models/set_default_card_response_200.py +61 -0
- hushscript/generated/models/setup_session_response.py +77 -0
- hushscript/generated/models/transaction.py +183 -0
- hushscript/generated/models/transactions_response.py +98 -0
- hushscript/generated/models/transcript_detail.py +194 -0
- hushscript/generated/models/transcript_detail_speaker_names.py +47 -0
- hushscript/generated/models/transcript_envelope.py +89 -0
- hushscript/generated/models/transcript_envelope_translations.py +62 -0
- hushscript/generated/models/transcript_language.py +75 -0
- hushscript/generated/models/transcript_list.py +90 -0
- hushscript/generated/models/transcript_list_row.py +152 -0
- hushscript/generated/models/transcript_list_row_insights_state.py +20 -0
- hushscript/generated/models/transcription_context_response.py +95 -0
- hushscript/generated/models/upload_complete_response.py +100 -0
- hushscript/generated/models/upload_create_response.py +148 -0
- hushscript/generated/models/upload_create_response_funding.py +16 -0
- hushscript/generated/models/upload_create_response_upload_type_0.py +108 -0
- hushscript/generated/models/upload_limits.py +86 -0
- hushscript/generated/models/upload_part_response_200.py +69 -0
- hushscript/generated/models/utterance.py +85 -0
- hushscript/generated/scopes.py +42 -0
- hushscript/generated/types.py +54 -0
- hushscript/money.py +79 -0
- hushscript/paginate.py +78 -0
- hushscript/py.typed +0 -0
- hushscript/resources/__init__.py +1 -0
- hushscript/resources/_params.py +33 -0
- hushscript/resources/account.py +121 -0
- hushscript/resources/agent.py +421 -0
- hushscript/resources/billing.py +567 -0
- hushscript/resources/jobs.py +105 -0
- hushscript/resources/transcripts.py +209 -0
- hushscript/transcribe/__init__.py +1 -0
- hushscript/transcribe/apipeline.py +234 -0
- hushscript/transcribe/aupload.py +197 -0
- hushscript/transcribe/heartbeat.py +78 -0
- hushscript/transcribe/pipeline.py +312 -0
- hushscript/transcribe/preflight.py +272 -0
- hushscript/transcribe/probe.py +185 -0
- hushscript/transcribe/resume.py +115 -0
- hushscript/transcribe/segment.py +23 -0
- hushscript/transcribe/session.py +34 -0
- hushscript/transcribe/sniff.py +30 -0
- hushscript/transcribe/source.py +218 -0
- hushscript/transcribe/upload.py +498 -0
- hushscript/transcribe/wait.py +124 -0
- hushscript/transcript.py +197 -0
- hushscript-1.0.0.dist-info/METADATA +502 -0
- hushscript-1.0.0.dist-info/RECORD +187 -0
- hushscript-1.0.0.dist-info/WHEEL +4 -0
- hushscript-1.0.0.dist-info/licenses/LICENSE +21 -0
hushscript/format.py
ADDED
|
@@ -0,0 +1,224 @@
|
|
|
1
|
+
"""The SRT, VTT and text exporters, byte for byte what the Hushscript app produces (spec 6.2).
|
|
2
|
+
|
|
3
|
+
The conformance goldens pin the output. JavaScript's rules are kept where they differ from
|
|
4
|
+
Python's: its whitespace set, UTF-16 line lengths, `Math.round` rounding.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from __future__ import annotations
|
|
8
|
+
|
|
9
|
+
import math
|
|
10
|
+
import re
|
|
11
|
+
from dataclasses import dataclass
|
|
12
|
+
from typing import TYPE_CHECKING, Final
|
|
13
|
+
|
|
14
|
+
if TYPE_CHECKING:
|
|
15
|
+
from collections.abc import Sequence
|
|
16
|
+
|
|
17
|
+
from .transcript import Utterance
|
|
18
|
+
|
|
19
|
+
MAX_CAPTION_CHARS: Final = 42
|
|
20
|
+
MAX_CAPTION_LINES: Final = 2
|
|
21
|
+
MAX_CUE_MS: Final = 7000
|
|
22
|
+
_ASTRAL: Final = 0x10000
|
|
23
|
+
_MS_PER_HOUR: Final = 3_600_000
|
|
24
|
+
_MS_PER_MINUTE: Final = 60_000
|
|
25
|
+
_SECONDS_PER_HOUR: Final = 3600
|
|
26
|
+
_SECONDS_PER_MINUTE: Final = 60
|
|
27
|
+
|
|
28
|
+
# JavaScript's whitespace (the set `\s` and `String.prototype.trim` use): it has U+FEFF and lacks
|
|
29
|
+
# U+0085 and the C0 separators that Python's `str.split()` treats as space.
|
|
30
|
+
JS_SPACE: Final = "".join(
|
|
31
|
+
chr(code)
|
|
32
|
+
for code in (
|
|
33
|
+
*(9, 10, 11, 12, 13, 32, 0xA0, 0x1680),
|
|
34
|
+
*range(0x2000, 0x200B),
|
|
35
|
+
*(0x2028, 0x2029, 0x202F, 0x205F, 0x3000, 0xFEFF),
|
|
36
|
+
)
|
|
37
|
+
)
|
|
38
|
+
_WORD: Final = re.compile(f"[^{re.escape(JS_SPACE)}]+")
|
|
39
|
+
# An SRT reader takes a line holding "-->" for a timing line, so a run of dashes before ">" is cut
|
|
40
|
+
# to "->" (a plain "-->" swap would turn "--->" back into "-->").
|
|
41
|
+
_DASH_ARROW: Final = re.compile(r"-{2,}>")
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
@dataclass(frozen=True, slots=True)
|
|
45
|
+
class Cue:
|
|
46
|
+
start_ms: int
|
|
47
|
+
end_ms: int
|
|
48
|
+
speaker_name: str
|
|
49
|
+
"""Empty when the cue carries no speaker prefix."""
|
|
50
|
+
text: str
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
def js_strip(text: str) -> str:
|
|
54
|
+
return text.strip(JS_SPACE)
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
def utf16_length(text: str) -> int:
|
|
58
|
+
"""The length JavaScript reports for `text`; a lone surrogate counts as one unit."""
|
|
59
|
+
return len(text) + sum(1 for char in text if ord(char) >= _ASTRAL)
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
def round_half_up(value: float) -> int:
|
|
63
|
+
"""`Math.round` for a non-negative value (Python's `round` rounds halves to even)."""
|
|
64
|
+
floor = math.floor(value)
|
|
65
|
+
return floor + 1 if value - floor >= 0.5 else floor # noqa: PLR2004
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
def _wrap_state(words: list[str], max_chars: int, max_lines: int) -> list[str]:
|
|
69
|
+
cues: list[str] = []
|
|
70
|
+
lines: list[str] = []
|
|
71
|
+
line = ""
|
|
72
|
+
line_length = 0
|
|
73
|
+
|
|
74
|
+
def flush_line() -> None:
|
|
75
|
+
nonlocal line, line_length
|
|
76
|
+
if line:
|
|
77
|
+
lines.append(line)
|
|
78
|
+
line, line_length = "", 0
|
|
79
|
+
|
|
80
|
+
def flush_cue() -> None:
|
|
81
|
+
flush_line()
|
|
82
|
+
if lines:
|
|
83
|
+
cues.append("\n".join(lines))
|
|
84
|
+
lines.clear()
|
|
85
|
+
|
|
86
|
+
for word in words:
|
|
87
|
+
word_length = utf16_length(word)
|
|
88
|
+
candidate = f"{line} {word}" if line else word
|
|
89
|
+
candidate_length = line_length + 1 + word_length if line else word_length
|
|
90
|
+
if candidate_length <= max_chars:
|
|
91
|
+
line, line_length = candidate, candidate_length
|
|
92
|
+
continue
|
|
93
|
+
flush_line()
|
|
94
|
+
if len(lines) >= max_lines:
|
|
95
|
+
flush_cue()
|
|
96
|
+
line, line_length = word, word_length
|
|
97
|
+
flush_cue()
|
|
98
|
+
return cues
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
def wrap_into_cues(text: str, max_chars: int, max_lines: int) -> list[str]:
|
|
102
|
+
"""Word-wraps `text` into cues of at most `max_lines` lines of `max_chars`; an overlong word
|
|
103
|
+
keeps a line to itself. Text without a word gives one empty cue."""
|
|
104
|
+
words = _WORD.findall(text)
|
|
105
|
+
if not words:
|
|
106
|
+
return [""]
|
|
107
|
+
return _wrap_state(words, max_chars, max_lines) or [""]
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
def utterance_cues(utterances: Sequence[Utterance]) -> list[Cue]:
|
|
111
|
+
return [Cue(u.start_ms, u.end_ms, u.speaker_name, u.text) for u in utterances]
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
def segment_captions(utterances: Sequence[Utterance]) -> list[Cue]:
|
|
115
|
+
"""Re-cuts utterances into readable cues: lines of at most 42 characters, two lines to a cue,
|
|
116
|
+
the utterance span split by character share, and every cue, the last of an utterance included,
|
|
117
|
+
capped at 7000 ms. Positions come from the cumulative character share, so the cap never shifts
|
|
118
|
+
a later cue."""
|
|
119
|
+
out: list[Cue] = []
|
|
120
|
+
for utterance in utterances:
|
|
121
|
+
chunks = wrap_into_cues(utterance.text, MAX_CAPTION_CHARS, MAX_CAPTION_LINES)
|
|
122
|
+
span = max(0, utterance.end_ms - utterance.start_ms)
|
|
123
|
+
total = sum(utf16_length(chunk) for chunk in chunks) or 1
|
|
124
|
+
accumulated = 0
|
|
125
|
+
for index, text in enumerate(chunks):
|
|
126
|
+
start_ms = utterance.start_ms + round_half_up(span * accumulated / total)
|
|
127
|
+
accumulated += max(1, utf16_length(text))
|
|
128
|
+
end_ms = utterance.end_ms
|
|
129
|
+
if index != len(chunks) - 1:
|
|
130
|
+
end_ms = utterance.start_ms + round_half_up(span * accumulated / total)
|
|
131
|
+
end_ms = min(end_ms, start_ms + MAX_CUE_MS)
|
|
132
|
+
out.append(Cue(start_ms, max(end_ms, start_ms), utterance.speaker_name, text))
|
|
133
|
+
return out
|
|
134
|
+
|
|
135
|
+
|
|
136
|
+
def timestamp(milliseconds: int, separator: str) -> str:
|
|
137
|
+
"""`HH:MM:SS,mmm`, floored to whole milliseconds; VTT swaps the comma for a dot."""
|
|
138
|
+
total = max(0, milliseconds)
|
|
139
|
+
hours, rest = divmod(total, _MS_PER_HOUR)
|
|
140
|
+
minutes, rest = divmod(rest, _MS_PER_MINUTE)
|
|
141
|
+
seconds, millis = divmod(rest, 1000)
|
|
142
|
+
return f"{hours:02d}:{minutes:02d}:{seconds:02d}{separator}{millis:03d}"
|
|
143
|
+
|
|
144
|
+
|
|
145
|
+
def cue_text(text: str) -> str:
|
|
146
|
+
"""Keeps a cue's line breaks but drops its blank lines: SRT and VTT end a cue at the first."""
|
|
147
|
+
lines = text.replace("\r\n", "\n").replace("\r", "\n").split("\n")
|
|
148
|
+
return "\n".join(line for line in lines if js_strip(line))
|
|
149
|
+
|
|
150
|
+
|
|
151
|
+
def _with_speaker(cue: Cue) -> str:
|
|
152
|
+
return f"{cue.speaker_name}: {cue.text}" if cue.speaker_name else cue.text
|
|
153
|
+
|
|
154
|
+
|
|
155
|
+
def _cues(utterances: Sequence[Utterance], *, captions: bool) -> list[Cue]:
|
|
156
|
+
return segment_captions(utterances) if captions else utterance_cues(utterances)
|
|
157
|
+
|
|
158
|
+
|
|
159
|
+
def render_srt(utterances: Sequence[Utterance], *, captions: bool = True) -> str:
|
|
160
|
+
blocks = [
|
|
161
|
+
f"{number}\n{timestamp(cue.start_ms, ',')} --> {timestamp(cue.end_ms, ',')}\n"
|
|
162
|
+
+ _DASH_ARROW.sub("->", cue_text(_with_speaker(cue)))
|
|
163
|
+
for number, cue in enumerate(_cues(utterances, captions=captions), start=1)
|
|
164
|
+
]
|
|
165
|
+
return "\n\n".join(blocks)
|
|
166
|
+
|
|
167
|
+
|
|
168
|
+
def _vtt_text(text: str) -> str:
|
|
169
|
+
"""Escapes what WebVTT reads as markup (`&` and `<`) and as the end of a cue (`-->`)."""
|
|
170
|
+
return cue_text(text).replace("&", "&").replace("<", "<").replace("-->", "-->")
|
|
171
|
+
|
|
172
|
+
|
|
173
|
+
def render_vtt(utterances: Sequence[Utterance], *, captions: bool = True) -> str:
|
|
174
|
+
blocks = [
|
|
175
|
+
f"{number}\n{timestamp(cue.start_ms, '.')} --> {timestamp(cue.end_ms, '.')}\n"
|
|
176
|
+
+ _vtt_text(_with_speaker(cue))
|
|
177
|
+
for number, cue in enumerate(_cues(utterances, captions=captions), start=1)
|
|
178
|
+
]
|
|
179
|
+
return "WEBVTT\n\n" + "\n\n".join(blocks) + "\n"
|
|
180
|
+
|
|
181
|
+
|
|
182
|
+
@dataclass(slots=True)
|
|
183
|
+
class _Block:
|
|
184
|
+
speaker: str
|
|
185
|
+
speaker_name: str
|
|
186
|
+
start_ms: int
|
|
187
|
+
text: str
|
|
188
|
+
|
|
189
|
+
|
|
190
|
+
def _blocks(utterances: Sequence[Utterance], *, merge: bool) -> list[_Block]:
|
|
191
|
+
blocks: list[_Block] = []
|
|
192
|
+
for utterance in utterances:
|
|
193
|
+
if merge and blocks and blocks[-1].speaker == utterance.speaker:
|
|
194
|
+
blocks[-1].text += " " + utterance.text
|
|
195
|
+
continue
|
|
196
|
+
blocks.append(
|
|
197
|
+
_Block(utterance.speaker, utterance.speaker_name, utterance.start_ms, utterance.text)
|
|
198
|
+
)
|
|
199
|
+
return blocks
|
|
200
|
+
|
|
201
|
+
|
|
202
|
+
def _clock(milliseconds: int) -> str:
|
|
203
|
+
"""`M:SS` or `H:MM:SS`, floored to whole seconds."""
|
|
204
|
+
seconds = max(0, milliseconds) // 1000
|
|
205
|
+
hours, rest = divmod(seconds, _SECONDS_PER_HOUR)
|
|
206
|
+
minutes, secs = divmod(rest, _SECONDS_PER_MINUTE)
|
|
207
|
+
return f"{hours}:{minutes:02d}:{secs:02d}" if hours > 0 else f"{minutes}:{secs:02d}"
|
|
208
|
+
|
|
209
|
+
|
|
210
|
+
def render_text(
|
|
211
|
+
utterances: Sequence[Utterance],
|
|
212
|
+
*,
|
|
213
|
+
speakers: bool = True,
|
|
214
|
+
timestamps: bool = False,
|
|
215
|
+
merge: bool = True,
|
|
216
|
+
) -> str:
|
|
217
|
+
""" "Name: text" blocks joined by a blank line. `\\r` and whitespace-only lines are kept as they
|
|
218
|
+
come, as in the app."""
|
|
219
|
+
out: list[str] = []
|
|
220
|
+
for block in _blocks(utterances, merge=merge):
|
|
221
|
+
prefix = f"[{_clock(block.start_ms)}] " if timestamps else ""
|
|
222
|
+
who = block.speaker_name if speakers else ""
|
|
223
|
+
out.append(f"{prefix}{who}: {block.text}" if who else f"{prefix}{block.text}")
|
|
224
|
+
return "\n\n".join(out)
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"""Contains methods for accessing the API"""
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"""Contains endpoint functions for accessing the API"""
|
|
@@ -0,0 +1,248 @@
|
|
|
1
|
+
from http import HTTPStatus
|
|
2
|
+
from typing import Any
|
|
3
|
+
|
|
4
|
+
import httpx
|
|
5
|
+
|
|
6
|
+
from ... import errors
|
|
7
|
+
from ...client import AuthenticatedClient, Client
|
|
8
|
+
from ...models.buy_minutes_body import BuyMinutesBody
|
|
9
|
+
from ...models.error import Error
|
|
10
|
+
from ...models.purchase_response import PurchaseResponse
|
|
11
|
+
from ...types import Response
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def _get_kwargs(
|
|
15
|
+
*,
|
|
16
|
+
body: BuyMinutesBody,
|
|
17
|
+
) -> dict[str, Any]:
|
|
18
|
+
headers: dict[str, Any] = {}
|
|
19
|
+
|
|
20
|
+
_kwargs: dict[str, Any] = {
|
|
21
|
+
"method": "post",
|
|
22
|
+
"url": "/v1/billing/purchase",
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
_kwargs["json"] = body.to_dict()
|
|
26
|
+
|
|
27
|
+
headers["Content-Type"] = "application/json"
|
|
28
|
+
|
|
29
|
+
_kwargs["headers"] = headers
|
|
30
|
+
return _kwargs
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def _parse_response(
|
|
34
|
+
*, client: AuthenticatedClient | Client, response: httpx.Response
|
|
35
|
+
) -> Error | PurchaseResponse | None:
|
|
36
|
+
if response.status_code == 200:
|
|
37
|
+
response_200 = PurchaseResponse.from_dict(response.json())
|
|
38
|
+
|
|
39
|
+
return response_200
|
|
40
|
+
|
|
41
|
+
if response.status_code == 400:
|
|
42
|
+
response_400 = Error.from_dict(response.json())
|
|
43
|
+
|
|
44
|
+
return response_400
|
|
45
|
+
|
|
46
|
+
if response.status_code == 401:
|
|
47
|
+
response_401 = Error.from_dict(response.json())
|
|
48
|
+
|
|
49
|
+
return response_401
|
|
50
|
+
|
|
51
|
+
if response.status_code == 403:
|
|
52
|
+
response_403 = Error.from_dict(response.json())
|
|
53
|
+
|
|
54
|
+
return response_403
|
|
55
|
+
|
|
56
|
+
if response.status_code == 422:
|
|
57
|
+
response_422 = Error.from_dict(response.json())
|
|
58
|
+
|
|
59
|
+
return response_422
|
|
60
|
+
|
|
61
|
+
if response.status_code == 429:
|
|
62
|
+
response_429 = Error.from_dict(response.json())
|
|
63
|
+
|
|
64
|
+
return response_429
|
|
65
|
+
|
|
66
|
+
if response.status_code == 500:
|
|
67
|
+
response_500 = Error.from_dict(response.json())
|
|
68
|
+
|
|
69
|
+
return response_500
|
|
70
|
+
|
|
71
|
+
if response.status_code == 502:
|
|
72
|
+
response_502 = Error.from_dict(response.json())
|
|
73
|
+
|
|
74
|
+
return response_502
|
|
75
|
+
|
|
76
|
+
if client.raise_on_unexpected_status:
|
|
77
|
+
raise errors.UnexpectedStatus(response.status_code, response.content)
|
|
78
|
+
else:
|
|
79
|
+
return None
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
def _build_response(
|
|
83
|
+
*, client: AuthenticatedClient | Client, response: httpx.Response
|
|
84
|
+
) -> Response[Error | PurchaseResponse]:
|
|
85
|
+
return Response(
|
|
86
|
+
status_code=HTTPStatus(response.status_code),
|
|
87
|
+
content=response.content,
|
|
88
|
+
headers=response.headers,
|
|
89
|
+
parsed=_parse_response(client=client, response=response),
|
|
90
|
+
)
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
def sync_detailed(
|
|
94
|
+
*,
|
|
95
|
+
client: AuthenticatedClient,
|
|
96
|
+
body: BuyMinutesBody,
|
|
97
|
+
) -> Response[Error | PurchaseResponse]:
|
|
98
|
+
"""Charge the account's saved default card for a minute pack
|
|
99
|
+
|
|
100
|
+
Charges a saved, fast_checkout_eligible payment method for one pack and credits the account's
|
|
101
|
+
balance on success. quick_payment_method_id must be the account's current default, except when the
|
|
102
|
+
account has no default at all, in which case naming an eligible card here makes it the new default.
|
|
103
|
+
quick_payment_method_id is required: this API can only complete a purchase against a card already on
|
|
104
|
+
file and has no way to open an interactive Checkout session, so an agent cannot use it to attach a
|
|
105
|
+
new card. list_saved_cards can only list a card already on file, not create one; add a card first,
|
|
106
|
+
either through the Hushscript app or by opening the hosted Checkout session create_setup_session
|
|
107
|
+
returns. A request with no quick_payment_method_id, or a Quick Buy charge that needed
|
|
108
|
+
authentication, a different payment method, or was declined, is refused outright rather than opened
|
|
109
|
+
as a Checkout session nobody in this flow could complete. Needs the billing scope AND machine
|
|
110
|
+
billing switched on for the account in the Hushscript app; either one off is a pat_scope_missing
|
|
111
|
+
(code 2023) refusal, whichever it was.
|
|
112
|
+
|
|
113
|
+
Args:
|
|
114
|
+
body (BuyMinutesBody):
|
|
115
|
+
|
|
116
|
+
Raises:
|
|
117
|
+
errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True.
|
|
118
|
+
httpx.TimeoutException: If the request takes longer than Client.timeout.
|
|
119
|
+
|
|
120
|
+
Returns:
|
|
121
|
+
Response[Error | PurchaseResponse]
|
|
122
|
+
"""
|
|
123
|
+
|
|
124
|
+
kwargs = _get_kwargs(
|
|
125
|
+
body=body,
|
|
126
|
+
)
|
|
127
|
+
|
|
128
|
+
response = client.get_httpx_client().request(
|
|
129
|
+
**kwargs,
|
|
130
|
+
)
|
|
131
|
+
|
|
132
|
+
return _build_response(client=client, response=response)
|
|
133
|
+
|
|
134
|
+
|
|
135
|
+
def sync(
|
|
136
|
+
*,
|
|
137
|
+
client: AuthenticatedClient,
|
|
138
|
+
body: BuyMinutesBody,
|
|
139
|
+
) -> Error | PurchaseResponse | None:
|
|
140
|
+
"""Charge the account's saved default card for a minute pack
|
|
141
|
+
|
|
142
|
+
Charges a saved, fast_checkout_eligible payment method for one pack and credits the account's
|
|
143
|
+
balance on success. quick_payment_method_id must be the account's current default, except when the
|
|
144
|
+
account has no default at all, in which case naming an eligible card here makes it the new default.
|
|
145
|
+
quick_payment_method_id is required: this API can only complete a purchase against a card already on
|
|
146
|
+
file and has no way to open an interactive Checkout session, so an agent cannot use it to attach a
|
|
147
|
+
new card. list_saved_cards can only list a card already on file, not create one; add a card first,
|
|
148
|
+
either through the Hushscript app or by opening the hosted Checkout session create_setup_session
|
|
149
|
+
returns. A request with no quick_payment_method_id, or a Quick Buy charge that needed
|
|
150
|
+
authentication, a different payment method, or was declined, is refused outright rather than opened
|
|
151
|
+
as a Checkout session nobody in this flow could complete. Needs the billing scope AND machine
|
|
152
|
+
billing switched on for the account in the Hushscript app; either one off is a pat_scope_missing
|
|
153
|
+
(code 2023) refusal, whichever it was.
|
|
154
|
+
|
|
155
|
+
Args:
|
|
156
|
+
body (BuyMinutesBody):
|
|
157
|
+
|
|
158
|
+
Raises:
|
|
159
|
+
errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True.
|
|
160
|
+
httpx.TimeoutException: If the request takes longer than Client.timeout.
|
|
161
|
+
|
|
162
|
+
Returns:
|
|
163
|
+
Error | PurchaseResponse
|
|
164
|
+
"""
|
|
165
|
+
|
|
166
|
+
return sync_detailed(
|
|
167
|
+
client=client,
|
|
168
|
+
body=body,
|
|
169
|
+
).parsed
|
|
170
|
+
|
|
171
|
+
|
|
172
|
+
async def asyncio_detailed(
|
|
173
|
+
*,
|
|
174
|
+
client: AuthenticatedClient,
|
|
175
|
+
body: BuyMinutesBody,
|
|
176
|
+
) -> Response[Error | PurchaseResponse]:
|
|
177
|
+
"""Charge the account's saved default card for a minute pack
|
|
178
|
+
|
|
179
|
+
Charges a saved, fast_checkout_eligible payment method for one pack and credits the account's
|
|
180
|
+
balance on success. quick_payment_method_id must be the account's current default, except when the
|
|
181
|
+
account has no default at all, in which case naming an eligible card here makes it the new default.
|
|
182
|
+
quick_payment_method_id is required: this API can only complete a purchase against a card already on
|
|
183
|
+
file and has no way to open an interactive Checkout session, so an agent cannot use it to attach a
|
|
184
|
+
new card. list_saved_cards can only list a card already on file, not create one; add a card first,
|
|
185
|
+
either through the Hushscript app or by opening the hosted Checkout session create_setup_session
|
|
186
|
+
returns. A request with no quick_payment_method_id, or a Quick Buy charge that needed
|
|
187
|
+
authentication, a different payment method, or was declined, is refused outright rather than opened
|
|
188
|
+
as a Checkout session nobody in this flow could complete. Needs the billing scope AND machine
|
|
189
|
+
billing switched on for the account in the Hushscript app; either one off is a pat_scope_missing
|
|
190
|
+
(code 2023) refusal, whichever it was.
|
|
191
|
+
|
|
192
|
+
Args:
|
|
193
|
+
body (BuyMinutesBody):
|
|
194
|
+
|
|
195
|
+
Raises:
|
|
196
|
+
errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True.
|
|
197
|
+
httpx.TimeoutException: If the request takes longer than Client.timeout.
|
|
198
|
+
|
|
199
|
+
Returns:
|
|
200
|
+
Response[Error | PurchaseResponse]
|
|
201
|
+
"""
|
|
202
|
+
|
|
203
|
+
kwargs = _get_kwargs(
|
|
204
|
+
body=body,
|
|
205
|
+
)
|
|
206
|
+
|
|
207
|
+
response = await client.get_async_httpx_client().request(**kwargs)
|
|
208
|
+
|
|
209
|
+
return _build_response(client=client, response=response)
|
|
210
|
+
|
|
211
|
+
|
|
212
|
+
async def asyncio(
|
|
213
|
+
*,
|
|
214
|
+
client: AuthenticatedClient,
|
|
215
|
+
body: BuyMinutesBody,
|
|
216
|
+
) -> Error | PurchaseResponse | None:
|
|
217
|
+
"""Charge the account's saved default card for a minute pack
|
|
218
|
+
|
|
219
|
+
Charges a saved, fast_checkout_eligible payment method for one pack and credits the account's
|
|
220
|
+
balance on success. quick_payment_method_id must be the account's current default, except when the
|
|
221
|
+
account has no default at all, in which case naming an eligible card here makes it the new default.
|
|
222
|
+
quick_payment_method_id is required: this API can only complete a purchase against a card already on
|
|
223
|
+
file and has no way to open an interactive Checkout session, so an agent cannot use it to attach a
|
|
224
|
+
new card. list_saved_cards can only list a card already on file, not create one; add a card first,
|
|
225
|
+
either through the Hushscript app or by opening the hosted Checkout session create_setup_session
|
|
226
|
+
returns. A request with no quick_payment_method_id, or a Quick Buy charge that needed
|
|
227
|
+
authentication, a different payment method, or was declined, is refused outright rather than opened
|
|
228
|
+
as a Checkout session nobody in this flow could complete. Needs the billing scope AND machine
|
|
229
|
+
billing switched on for the account in the Hushscript app; either one off is a pat_scope_missing
|
|
230
|
+
(code 2023) refusal, whichever it was.
|
|
231
|
+
|
|
232
|
+
Args:
|
|
233
|
+
body (BuyMinutesBody):
|
|
234
|
+
|
|
235
|
+
Raises:
|
|
236
|
+
errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True.
|
|
237
|
+
httpx.TimeoutException: If the request takes longer than Client.timeout.
|
|
238
|
+
|
|
239
|
+
Returns:
|
|
240
|
+
Error | PurchaseResponse
|
|
241
|
+
"""
|
|
242
|
+
|
|
243
|
+
return (
|
|
244
|
+
await asyncio_detailed(
|
|
245
|
+
client=client,
|
|
246
|
+
body=body,
|
|
247
|
+
)
|
|
248
|
+
).parsed
|