118 lines
2.6 KiB
Go
118 lines
2.6 KiB
Go
package nexara
|
|
|
|
import (
|
|
"bytes"
|
|
"context"
|
|
"encoding/json"
|
|
"fmt"
|
|
"io"
|
|
"mime/multipart"
|
|
"net/http"
|
|
"os"
|
|
"path/filepath"
|
|
"strings"
|
|
"time"
|
|
|
|
"github.com/postmet/transcribe/internal/models"
|
|
)
|
|
|
|
type Client struct {
|
|
apiURL string
|
|
apiKey string
|
|
model string
|
|
httpClient *http.Client
|
|
}
|
|
|
|
func New(baseURL, apiKey, model string, timeout time.Duration) *Client {
|
|
baseURL = strings.TrimRight(baseURL, "/")
|
|
return &Client{
|
|
apiURL: baseURL + "/api/v1/audio/transcriptions",
|
|
apiKey: apiKey,
|
|
model: model,
|
|
httpClient: &http.Client{
|
|
Timeout: timeout,
|
|
},
|
|
}
|
|
}
|
|
|
|
func (c *Client) TranscribeFile(ctx context.Context, path string) (text, language string, segments []models.Segment, err error) {
|
|
f, err := os.Open(path)
|
|
if err != nil {
|
|
return "", "", nil, fmt.Errorf("open file: %w", err)
|
|
}
|
|
defer f.Close()
|
|
|
|
body := &bytes.Buffer{}
|
|
writer := multipart.NewWriter(body)
|
|
part, err := writer.CreateFormFile("file", filepath.Base(path))
|
|
if err != nil {
|
|
return "", "", nil, err
|
|
}
|
|
if _, err := io.Copy(part, f); err != nil {
|
|
return "", "", nil, err
|
|
}
|
|
if c.model != "" {
|
|
if err := writer.WriteField("model", c.model); err != nil {
|
|
return "", "", nil, err
|
|
}
|
|
}
|
|
if err := writer.WriteField("response_format", "json"); err != nil {
|
|
return "", "", nil, err
|
|
}
|
|
if err := writer.Close(); err != nil {
|
|
return "", "", nil, err
|
|
}
|
|
|
|
req, err := http.NewRequestWithContext(ctx, http.MethodPost, c.apiURL, body)
|
|
if err != nil {
|
|
return "", "", nil, err
|
|
}
|
|
req.Header.Set("Content-Type", writer.FormDataContentType())
|
|
req.Header.Set("Authorization", "Bearer "+c.apiKey)
|
|
|
|
resp, err := c.httpClient.Do(req)
|
|
if err != nil {
|
|
return "", "", nil, fmt.Errorf("request: %w", err)
|
|
}
|
|
defer resp.Body.Close()
|
|
|
|
respBody, err := io.ReadAll(resp.Body)
|
|
if err != nil {
|
|
return "", "", nil, err
|
|
}
|
|
if resp.StatusCode != http.StatusOK {
|
|
return "", "", nil, fmt.Errorf("status %d: %s", resp.StatusCode, string(respBody))
|
|
}
|
|
|
|
var raw map[string]any
|
|
if err := json.Unmarshal(respBody, &raw); err != nil {
|
|
return "", "", nil, fmt.Errorf("parse: %w", err)
|
|
}
|
|
if t, ok := raw["text"].(string); ok {
|
|
text = t
|
|
}
|
|
if lang, ok := raw["language"].(string); ok {
|
|
language = lang
|
|
}
|
|
if segs, ok := raw["segments"].([]any); ok {
|
|
for _, s := range segs {
|
|
m, ok := s.(map[string]any)
|
|
if !ok {
|
|
continue
|
|
}
|
|
var seg models.Segment
|
|
if v, ok := m["start"].(float64); ok {
|
|
seg.Start = v
|
|
}
|
|
if v, ok := m["end"].(float64); ok {
|
|
seg.End = v
|
|
}
|
|
if v, ok := m["text"].(string); ok {
|
|
seg.Text = v
|
|
}
|
|
segments = append(segments, seg)
|
|
}
|
|
}
|
|
return text, language, segments, nil
|
|
}
|