diff --git a/assemblyai.go b/assemblyai.go index 31b73dc..74fce95 100644 --- a/assemblyai.go +++ b/assemblyai.go @@ -4,6 +4,7 @@ import ( "bytes" "context" "encoding/json" + "fmt" "io" "net/http" "net/url" @@ -11,10 +12,9 @@ import ( ) const ( - version = "1.5.0" + version = "1.5.1" defaultBaseURLScheme = "https" defaultBaseURLHost = "api.assemblyai.com" - defaultUserAgent = "assemblyai-go/" + version ) // Client manages the communication with the AssemblyAI API. @@ -41,7 +41,7 @@ func NewClientWithOptions(opts ...ClientOption) *Client { Scheme: defaultBaseURLScheme, Host: defaultBaseURLHost, }, - userAgent: defaultUserAgent, + userAgent: fmt.Sprintf("AssemblyAI/1.0 (sdk=Go/%s)", version), httpClient: &http.Client{}, apiKey: defaultAPIKey, } @@ -157,7 +157,12 @@ func (c *Client) do(req *http.Request, v interface{}) (*http.Response, error) { } if v != nil { - err = json.NewDecoder(resp.Body).Decode(v) + switch val := v.(type) { + case *[]byte: + *val, err = io.ReadAll(resp.Body) + default: + err = json.NewDecoder(resp.Body).Decode(v) + } } return resp, err diff --git a/examples/realtime/realtime.go b/examples/realtime/realtime.go index 022e867..8b70c84 100644 --- a/examples/realtime/realtime.go +++ b/examples/realtime/realtime.go @@ -3,12 +3,11 @@ package main import ( "context" "fmt" + "log/slog" "os" "os/signal" "syscall" - "log/slog" - "github.com/AssemblyAI/assemblyai-go-sdk" "github.com/gordonklaus/portaudio" ) @@ -36,7 +35,6 @@ func (h *realtimeHandler) Error(err error) { } func main() { - sigs := make(chan os.Signal, 1) signal.Notify(sigs, syscall.SIGINT, syscall.SIGTERM) diff --git a/testdata/transcript/subtitles.srt b/testdata/transcript/subtitles.srt new file mode 100644 index 0000000..87b09d3 --- /dev/null +++ b/testdata/transcript/subtitles.srt @@ -0,0 +1,199 @@ +1 +00:00:05,400 --> 00:00:12,550 +Runner's knee runner's + +2 +00:00:12,582 --> 00:00:16,086 +knee is a condition characterized by pain behind or around + +3 +00:00:16,150 --> 00:00:19,422 +the kneecap. It is caused by overuse, + +4 +00:00:19,518 --> 00:00:23,870 +muscle imbalance and inadequate stretching. Symptoms include + +5 +00:00:23,982 --> 00:00:27,294 +pain under or around the kneecap, pain when + +6 +00:00:27,334 --> 00:00:30,782 +walking sprained ankle one + +7 +00:00:30,798 --> 00:00:33,470 +nil here in the 37th minute she is between two guatemalan + +8 +00:00:33,502 --> 00:00:36,782 +defenders and then goes down and stays down and + +9 +00:00:36,798 --> 00:00:40,134 +you will see why. The ligaments of the ankle holds the ankle + +10 +00:00:40,174 --> 00:00:43,798 +bones and joint in position. They protect the ankle + +11 +00:00:43,846 --> 00:00:46,870 +from abnormal movements such as twisting, turning and + +12 +00:00:46,902 --> 00:00:50,710 +rolling of the foot. A sprained ankle happens when the + +13 +00:00:50,742 --> 00:00:54,434 +foot twists, rolls or turns beyond its normal motions. + +14 +00:00:55,484 --> 00:00:58,864 +If the force is too strong, the ligaments can tear. + +15 +00:00:59,404 --> 00:01:02,844 +Symptoms include pain and difficulty moving the ankle, + +16 +00:01:03,004 --> 00:01:05,784 +swelling around the ankle and bruising. + +17 +00:01:08,004 --> 00:01:11,252 +Meniscus tear I think some of it was just + +18 +00:01:11,308 --> 00:01:14,804 +being scared, but this guy doesn't like you. Want to go + +19 +00:01:14,844 --> 00:01:18,756 +after Patrick each of your knees has two menisci c shaped + +20 +00:01:18,780 --> 00:01:21,792 +pieces of cartilage that act like a cushion between your + +21 +00:01:21,808 --> 00:01:23,604 +shin bone and your thigh bone. + +22 +00:01:25,504 --> 00:01:28,888 +A meniscus tear happens when you forcibly twist or rotate + +23 +00:01:28,936 --> 00:01:31,952 +your knee, especially when putting the pressure of your full + +24 +00:01:32,008 --> 00:01:36,144 +weight on it, leading to a torn meniscus. Symptoms include + +25 +00:01:36,264 --> 00:01:39,288 +stiffness and swelling, pain in your knee, + +26 +00:01:39,456 --> 00:01:43,368 +catching or locking of your knee rotator + +27 +00:01:43,416 --> 00:01:46,996 +cuff tear has a torn rotator cuff cuff go be traveling + +28 +00:01:47,020 --> 00:01:50,676 +to Los Angeles today to be examined by teen doctors on the + +29 +00:01:50,700 --> 00:01:54,156 +rotator cuff attaches the humerus to the shoulder blade and + +30 +00:01:54,180 --> 00:01:56,344 +helps to lift and rotate your arm. + +31 +00:01:58,084 --> 00:02:01,940 +A rotator cuff tear is caused by a fall onto your arm or + +32 +00:02:01,972 --> 00:02:04,304 +if you lift a heavy object too fast, + +33 +00:02:06,284 --> 00:02:09,420 +the tendon can partially or completely tear off of the + +34 +00:02:09,452 --> 00:02:13,160 +humerus. Head symptoms + +35 +00:02:13,192 --> 00:02:16,364 +include pain when lifting and lowering your arm, + +36 +00:02:16,664 --> 00:02:19,364 +weakness when lifting or rotating your arm, + +37 +00:02:19,664 --> 00:02:22,284 +pain when lying on the affected shoulder. + +38 +00:02:25,544 --> 00:02:29,504 +ACL tear here's Rosario on the brake now and watch. + +39 +00:02:29,664 --> 00:02:32,888 +Nerls go up with the left hand, block the shot, and then on + +40 +00:02:32,936 --> 00:02:36,376 +landing, there came the the ACL runs diagonally in the + +41 +00:02:36,400 --> 00:02:38,484 +middle of the knee and provides stability. + +42 +00:02:40,034 --> 00:02:43,330 +Anterior cruciate ligament tear occurs when your foot + +43 +00:02:43,362 --> 00:02:46,714 +is firmly planted on the ground and a sudden force hits your + +44 +00:02:46,754 --> 00:02:49,574 +knee while your leg is straight or slightly bent. + +45 +00:02:52,194 --> 00:02:55,282 +This can happen when you are changing direction rapidly, + +46 +00:02:55,418 --> 00:02:58,294 +slowing down when running or landing from a jump. + +47 +00:02:59,834 --> 00:03:02,690 +The ligament completely tears into two pieces, + +48 +00:03:02,842 --> 00:03:04,574 +making the knee unstable. + +49 +00:03:06,274 --> 00:03:09,826 +Symptoms include severe pain and tenderness in knee, + +50 +00:03:10,010 --> 00:03:13,514 +loss of full range of motion, swelling around the knee. diff --git a/transcript.go b/transcript.go index 0eba5c7..36813ea 100644 --- a/transcript.go +++ b/transcript.go @@ -187,13 +187,15 @@ func (s *TranscriptService) GetSubtitles(ctx context.Context, transcriptID strin req.URL.RawQuery = values.Encode() } - resp, err := s.client.do(req, nil) + var res []byte + + resp, err := s.client.do(req, &res) if err != nil { return nil, err } defer resp.Body.Close() - return io.ReadAll(resp.Body) + return res, nil } // List returns a collection of transcripts based on a filter. diff --git a/transcript_test.go b/transcript_test.go index cf62cdb..a973f2b 100644 --- a/transcript_test.go +++ b/transcript_test.go @@ -1,6 +1,7 @@ package assemblyai import ( + "bufio" "bytes" "context" "encoding/json" @@ -238,6 +239,43 @@ func TestTranscripts_SearchWords(t *testing.T) { require.Equal(t, want, results) } +func TestTranscripts_GetSubtitles(t *testing.T) { + t.Parallel() + + client, handler, teardown := setup() + defer teardown() + + handler.HandleFunc("/v2/transcript/"+fakeTranscriptID+"/srt", func(w http.ResponseWriter, r *http.Request) { + require.Equal(t, "GET", r.Method) + require.Equal(t, "chars_per_caption=60", r.URL.RawQuery) + + writeFileResponse(t, w, "testdata/transcript/subtitles.srt") + }) + + ctx := context.Background() + + st, err := client.Transcripts.GetSubtitles(ctx, fakeTranscriptID, "srt", &TranscriptGetSubtitlesOptions{ + CharsPerCaption: 60, + }) + require.NoError(t, err) + + r := bufio.NewReader(bytes.NewReader(st)) + + var line []byte + + line, _, err = r.ReadLine() + require.NoError(t, err) + require.Equal(t, []byte("1"), line) + + line, _, err = r.ReadLine() + require.NoError(t, err) + require.Equal(t, []byte("00:00:05,400 --> 00:00:12,550"), line) + + line, _, err = r.ReadLine() + require.NoError(t, err) + require.Equal(t, []byte("Runner's knee runner's"), line) +} + func TestAPIError(t *testing.T) { t.Parallel() diff --git a/types.go b/types.go index 041f488..718ede3 100644 --- a/types.go +++ b/types.go @@ -1,7 +1,5 @@ package assemblyai -import "encoding/json" - // Either success, or unavailable in the rare case that the model failed type AudioIntelligenceModelStatus string @@ -24,6 +22,9 @@ type AutoHighlightResult struct { type AutoHighlightsResult struct { // A temporally-sequential array of Key Phrases Results []AutoHighlightResult `json:"results,omitempty"` + + // The status of the Key Phrases model. Either success, or unavailable in the rare case that the model failed. + Status AudioIntelligenceModelStatus `json:"status,omitempty"` } // Chapter of the audio file @@ -131,7 +132,7 @@ type LeMURActionItemsResponse struct { } type LeMURBaseParams struct { - // Context to provide the model. This can be a string or struct. + // Context to provide the model. This can be a string or a free-form JSON value. Context interface{} `json:"context,omitempty"` // The model that is used for the final prompt after compression is performed. @@ -171,7 +172,7 @@ type LeMURQuestion struct { AnswerOptions []string `json:"answer_options,omitempty"` // Any context about the transcripts you wish to provide. This can be a string or any object. - Context json.RawMessage `json:"context,omitempty"` + Context interface{} `json:"context,omitempty"` // The question you wish to ask. For more complex questions use default model. Question *string `json:"question,omitempty"` @@ -211,15 +212,17 @@ type LeMURSummaryResponse struct { } type LeMURTaskParams struct { - LeMURBaseParams // Your text to prompt the model to produce a desired output, including any context you want to pass into the model. Prompt *string `json:"prompt,omitempty"` + + LeMURBaseParams } type LeMURTaskResponse struct { - LeMURBaseResponse // The response generated by LeMUR. Response *string `json:"response,omitempty"` + + LeMURBaseResponse } type ListTranscriptParams struct { @@ -242,13 +245,16 @@ type ListTranscriptParams struct { ThrottledOnly *bool `url:"throttled_only,omitempty"` } +// Details of the transcript page. Transcripts are sorted from newest to oldest. The previous URL always points to a page with older transcripts. type PageDetails struct { CurrentURL *string `json:"current_url,omitempty"` Limit *int64 `json:"limit,omitempty"` + // The URL to the next page of transcripts. The next URL always points to a page with newer transcripts. NextURL *string `json:"next_url,omitempty"` + // The URL to the next page of transcripts. The previous URL always points to a page with older transcripts. PrevURL *string `json:"prev_url,omitempty"` ResultCount *int64 `json:"result_count,omitempty"` @@ -278,7 +284,7 @@ type PurgeLeMURRequestDataResponse struct { } type RealtimeTemporaryTokenResponse struct { - // The temporary authentication token for real-time transcription + // The temporary authentication token for Streaming Speech-to-Text Token *string `json:"token,omitempty"` } @@ -340,7 +346,7 @@ type SeverityScoreSummary struct { // The speech model to use for the transcription. type SpeechModel string -// The replacement logic for detected PII, can be "entity_type" or "hash". See [PII redaction](https://www.assemblyai.com/docs/models/pii-redaction) for more details. +// The replacement logic for detected PII, can be "entity_name" or "hash". See [PII redaction](https://www.assemblyai.com/docs/models/pii-redaction) for more details. type SubstitutionPolicy string // Format of the subtitles @@ -591,6 +597,7 @@ type TranscriptCustomSpelling struct { // The default value is 'en_us'. type TranscriptLanguageCode string +// A list of transcripts. Transcripts are sorted from newest to oldest. The previous URL always points to a page with older transcripts. type TranscriptList struct { PageDetails PageDetails `json:"page_details,omitempty"` @@ -604,6 +611,9 @@ type TranscriptListItem struct { Created *string `json:"created,omitempty"` + // Error message of why the transcript failed + Error *string `json:"error,omitempty"` + ID *string `json:"id,omitempty"` ResourceURL *string `json:"resource_url,omitempty"` @@ -741,11 +751,24 @@ type TranscriptParagraph struct { // The parameters for creating a transcript type TranscriptParams struct { - TranscriptOptionalParams // The URL of the audio or video file to transcribe. AudioURL *string `json:"audio_url,omitempty"` + + TranscriptOptionalParams } +// The notification when the transcript status is completed or error. +type TranscriptReadyNotification struct { + // The status of the transcript. Either completed or error. + Status TranscriptReadyStatus `json:"status,omitempty"` + + // The ID of the transcript + TranscriptID *string `json:"transcript_id,omitempty"` +} + +// The status of the transcript. Either completed or error. +type TranscriptReadyStatus string + type TranscriptSentence struct { Confidence *float64 `json:"confidence,omitempty"`