transcription.go (2697B)
1 package openai 2 3 import ( 4 "bytes" 5 "encoding/json" 6 "fmt" 7 "io" 8 "mime/multipart" 9 "net/http" 10 "os" 11 "strconv" 12 "strings" 13 ) 14 15 type Transcription struct { 16 Language string 17 Text string 18 Segments []struct { 19 Text string 20 Start float32 21 End float32 22 } 23 } 24 25 func (t Transcription) SubRip() string { 26 b := &strings.Builder{} 27 for i := 0; i < len(t.Segments); i++ { 28 fmt.Fprintln(b, strconv.Itoa(i+1)) 29 fmt.Fprintf(b, "%s --> %s\n", secondsToTimecode(t.Segments[i].Start), secondsToTimecode(t.Segments[i].End)) 30 // TODO: trim space? 31 fmt.Fprintln(b, t.Segments[i].Text) 32 fmt.Fprintln(b) 33 } 34 return b.String() 35 } 36 37 // TODO: use time.Duration with rounding to milliseconds instead. 38 func secondsToTimecode(seconds float32) string { 39 totalMs := int(seconds * 1000) 40 hours := totalMs / 3600000 41 totalMs %= 3600000 42 minutes := totalMs / 60000 43 totalMs %= 60000 44 secondsPart := totalMs / 1000 45 ms := totalMs % 1000 46 47 // Format with leading zeros 48 return fmt.Sprintf("%02d:%02d:%02d,%03d", hours, minutes, secondsPart, ms) 49 } 50 51 // language must be in [ISO-639-1] format such as "en" for English. 52 // 53 // [ISO-639-1]: https://en.wikipedia.org/wiki/List_of_ISO_639-1_codes 54 func (c *Client) Transcribe(r io.Reader, language, model string, timestamps bool) (*Transcription, error) { 55 buf := &bytes.Buffer{} 56 w := multipart.NewWriter(buf) 57 if err := w.WriteField("model", model); err != nil { 58 return nil, err 59 } 60 if err := w.WriteField("language", language); err != nil { 61 return nil, err 62 } 63 if timestamps { 64 if err := w.WriteField("timestamp_granularities", "segment"); err != nil { 65 return nil, err 66 } 67 } 68 field, err := w.CreateFormFile("file", "sample.mp3") 69 if err != nil { 70 return nil, err 71 } 72 if _, err = io.Copy(field, r); err != nil { 73 return nil, fmt.Errorf("copy audio to request body: %w", err) 74 } 75 if err := w.Close(); err != nil { 76 return nil, err 77 } 78 79 req, err := http.NewRequest(http.MethodPost, c.BaseURL+"/audio/transcriptions", buf) 80 if err != nil { 81 return nil, err 82 } 83 req.Header.Set("Content-Type", w.FormDataContentType()) 84 resp, err := c.do(req) 85 if err != nil { 86 return nil, err 87 } 88 defer resp.Body.Close() 89 if resp.StatusCode == http.StatusUnauthorized { 90 return nil, fmt.Errorf("unauthorised") 91 } else if resp.StatusCode >= 400 && resp.StatusCode <= 499 { 92 var aerr apiError 93 94 io.Copy(os.Stderr, resp.Body) 95 return nil, fmt.Errorf(resp.Status) 96 97 if err := json.NewDecoder(resp.Body).Decode(&aerr); err != nil { 98 return nil, fmt.Errorf(resp.Status) 99 } 100 return nil, aerr 101 } 102 103 var tscript Transcription 104 if err := json.NewDecoder(resp.Body).Decode(&tscript); err != nil { 105 return nil, fmt.Errorf("decode transciption response: %w", err) 106 } 107 return &tscript, nil 108 }