mirror of
https://github.com/EvolutionAPI/evolution-audio-converter.git
synced 2025-07-13 07:04:51 -06:00
feat: add S3 storage support and update configuration
- Enhanced .env.example to include S3 storage configuration options. - Updated main.go to initialize S3 client and handle audio uploads to S3. - Modified processAudio function to return S3 URL when storage is enabled. - Updated README.md with new S3 storage instructions and examples.
This commit is contained in:
parent
30a4990e53
commit
5f3a073d76
18
.env.example
18
.env.example
@ -2,7 +2,17 @@ PORT=4040
|
||||
CORS_ALLOW_ORIGINS=*
|
||||
API_KEY=429683C4C977415CAAFCCE10F7D57E11
|
||||
ENABLE_TRANSCRIPTION=true
|
||||
TRANSCRIPTION_PROVIDER=openai # ou groq
|
||||
OPENAI_API_KEY=sua_chave_openai_aqui
|
||||
GROK_API_KEY=sua_chave_groq_aqui
|
||||
TRANSCRIPTION_LANGUAGE=pt
|
||||
TRANSCRIPTION_PROVIDER=openai # or groq
|
||||
OPENAI_API_KEY=your_openai_key_here
|
||||
GROK_API_KEY=your_groq_key_here
|
||||
TRANSCRIPTION_LANGUAGE=en # Default transcription language (optional)
|
||||
|
||||
# S3 Storage Settings
|
||||
ENABLE_S3_STORAGE=true
|
||||
S3_ENDPOINT=play.min.io
|
||||
S3_ACCESS_KEY=your_access_key_here
|
||||
S3_SECRET_KEY=your_secret_key_here
|
||||
S3_BUCKET_NAME=audio-files
|
||||
S3_REGION=us-east-1
|
||||
S3_USE_SSL=true
|
||||
S3_URL_EXPIRATION=24h # Duration format: 1h, 24h, 7d, etc.
|
||||
|
167
README.md
167
README.md
@ -1,6 +1,6 @@
|
||||
# Evolution Audio Converter
|
||||
|
||||
This project is a microservice in Go that processes audio files, converts them to **opus** or **mp3** format, and returns both the duration of the audio and the converted file in base64. The service accepts audio files sent as **form-data**, **base64**, or **URL**.
|
||||
This project is a microservice in Go that processes audio files, converts them to **opus** or **mp3** format, and returns both the duration of the audio and the converted file (as base64 or S3 URL). The service accepts audio files sent as **form-data**, **base64**, or **URL**.
|
||||
|
||||
## Requirements
|
||||
|
||||
@ -50,16 +50,16 @@ The service depends on **FFmpeg** to convert the audio. Make sure FFmpeg is inst
|
||||
|
||||
### Configuration
|
||||
|
||||
Create a `.env` file in the project's root directory with the following configuration:
|
||||
Create a `.env` file in the project's root directory. Here are the available configuration options:
|
||||
|
||||
#### Basic Configuration
|
||||
|
||||
```env
|
||||
PORT=4040
|
||||
API_KEY=your_secret_api_key_here
|
||||
```
|
||||
|
||||
### Transcription Configuration
|
||||
|
||||
To enable audio transcription, configure the following variables in the `.env` file:
|
||||
#### Transcription Configuration
|
||||
|
||||
```env
|
||||
ENABLE_TRANSCRIPTION=true
|
||||
@ -69,17 +69,44 @@ GROQ_API_KEY=your_groq_key_here
|
||||
TRANSCRIPTION_LANGUAGE=en # Default transcription language (optional)
|
||||
```
|
||||
|
||||
- `ENABLE_TRANSCRIPTION`: Enables or disables the transcription feature
|
||||
- `TRANSCRIPTION_PROVIDER`: Chooses the AI provider for transcription (openai or groq)
|
||||
- `OPENAI_API_KEY`: Your OpenAI API key (required if using openai)
|
||||
- `GROQ_API_KEY`: Your Groq API key (required if using groq)
|
||||
- `TRANSCRIPTION_LANGUAGE`: Sets the default transcription language (optional)
|
||||
#### Storage Configuration
|
||||
|
||||
```env
|
||||
ENABLE_S3_STORAGE=true
|
||||
S3_ENDPOINT=play.min.io
|
||||
S3_ACCESS_KEY=your_access_key_here
|
||||
S3_SECRET_KEY=your_secret_key_here
|
||||
S3_BUCKET_NAME=audio-files
|
||||
S3_REGION=us-east-1
|
||||
S3_USE_SSL=true
|
||||
S3_URL_EXPIRATION=24h
|
||||
```
|
||||
|
||||
### Storage Options
|
||||
|
||||
The service supports two storage modes for the converted audio:
|
||||
|
||||
1. **Base64 (default)**: Returns the audio file encoded in base64 format
|
||||
2. **S3 Compatible Storage**: Uploads to S3-compatible storage (AWS S3, MinIO, etc.) and returns a presigned URL
|
||||
|
||||
When S3 storage is enabled, the response will include a `url` instead of the `audio` field:
|
||||
|
||||
```json
|
||||
{
|
||||
"duration": 120,
|
||||
"format": "ogg",
|
||||
"url": "https://your-s3-endpoint/bucket/file.ogg?signature...",
|
||||
"transcription": "Transcribed text here..." // if transcription was requested
|
||||
}
|
||||
```
|
||||
|
||||
If S3 upload fails, the service automatically falls back to base64 encoding.
|
||||
|
||||
## Running the Project
|
||||
|
||||
### Locally
|
||||
|
||||
To run the service locally, use the following command:
|
||||
To run the service locally:
|
||||
|
||||
```bash
|
||||
go run main.go -dev
|
||||
@ -89,8 +116,6 @@ The server will be available at `http://localhost:4040`.
|
||||
|
||||
### Using Docker
|
||||
|
||||
If you prefer to run the service in a Docker container, follow the steps below:
|
||||
|
||||
1. **Build the Docker image**:
|
||||
|
||||
```bash
|
||||
@ -103,113 +128,97 @@ If you prefer to run the service in a Docker container, follow the steps below:
|
||||
docker run -p 4040:4040 --env-file=.env audio-service
|
||||
```
|
||||
|
||||
This will start the container on the port specified in the `.env` file.
|
||||
|
||||
## How to Use
|
||||
|
||||
You can send `POST` requests to the `/process-audio` endpoint with an audio file in the following formats:
|
||||
|
||||
- **Form-data** (to upload files)
|
||||
- **Base64** (to send the audio encoded in base64)
|
||||
- **URL** (to send the link to the audio file)
|
||||
## API Usage
|
||||
|
||||
### Authentication
|
||||
|
||||
All requests must include the `apikey` header with the value of the `API_KEY` configured in the `.env` file.
|
||||
All requests must include the `apikey` header with your API key.
|
||||
|
||||
### Optional Parameters
|
||||
### Endpoints
|
||||
|
||||
- **`format`**: You can specify the format for conversion by passing the `format` parameter in the request. Supported values:
|
||||
- `mp3`
|
||||
- `ogg` (default)
|
||||
#### Process Audio
|
||||
|
||||
### Audio Transcription
|
||||
`POST /process-audio`
|
||||
|
||||
You can get the audio transcription in two ways:
|
||||
Accepts audio files in these formats:
|
||||
|
||||
1. Along with audio processing by adding the `transcribe=true` parameter:
|
||||
- Form-data
|
||||
- Base64
|
||||
- URL
|
||||
|
||||
Optional parameters:
|
||||
|
||||
- `format`: Output format (`mp3` or `ogg`, default: `ogg`)
|
||||
- `transcribe`: Enable transcription (`true` or `false`)
|
||||
- `language`: Transcription language code (e.g., "en", "es", "pt")
|
||||
|
||||
#### Transcribe Only
|
||||
|
||||
`POST /transcribe`
|
||||
|
||||
Transcribes audio without format conversion.
|
||||
|
||||
Optional parameters:
|
||||
|
||||
- `language`: Transcription language code
|
||||
|
||||
### Example Requests
|
||||
|
||||
#### Form-data Upload
|
||||
|
||||
```bash
|
||||
curl -X POST -F "file=@audio.mp3" \
|
||||
-F "format=ogg" \
|
||||
-F "transcribe=true" \
|
||||
-F "language=en" \
|
||||
http://localhost:4040/process-audio \
|
||||
-H "apikey: your_secret_api_key_here"
|
||||
```
|
||||
|
||||
2. Using the specific transcription endpoint:
|
||||
#### Base64 Upload
|
||||
|
||||
```bash
|
||||
curl -X POST -F "file=@audio.mp3" \
|
||||
-F "language=en" \
|
||||
http://localhost:4040/transcribe \
|
||||
curl -X POST \
|
||||
-d "base64=$(base64 audio.mp3)" \
|
||||
-d "format=ogg" \
|
||||
http://localhost:4040/process-audio \
|
||||
-H "apikey: your_secret_api_key_here"
|
||||
```
|
||||
|
||||
Optional parameters:
|
||||
- `language`: Audio language code (e.g., "en", "es", "pt"). If not specified, it will use the value defined in `TRANSCRIPTION_LANGUAGE` in `.env`. If neither is defined, the system will try to automatically detect the language.
|
||||
#### URL Upload
|
||||
|
||||
The response will include the `transcription` field with the transcribed text:
|
||||
|
||||
```json
|
||||
{
|
||||
"transcription": "Transcribed text here..."
|
||||
}
|
||||
```bash
|
||||
curl -X POST \
|
||||
-d "url=https://example.com/audio.mp3" \
|
||||
-d "format=ogg" \
|
||||
http://localhost:4040/process-audio \
|
||||
-H "apikey: your_secret_api_key_here"
|
||||
```
|
||||
|
||||
When used with audio processing (`/process-audio`), the response will include both audio data and transcription:
|
||||
### Response Format
|
||||
|
||||
With S3 storage disabled (default):
|
||||
|
||||
```json
|
||||
{
|
||||
"duration": 120,
|
||||
"audio": "UklGR... (base64 of the file)",
|
||||
"format": "ogg",
|
||||
"transcription": "Transcribed text here..."
|
||||
"transcription": "Transcribed text here..." // if requested
|
||||
}
|
||||
```
|
||||
|
||||
### Example Requests Using cURL
|
||||
|
||||
#### Sending as Form-data
|
||||
|
||||
```bash
|
||||
curl -X POST -F "file=@path/to/audio.mp3" http://localhost:4040/process-audio \
|
||||
-F "format=ogg" \
|
||||
-H "apikey: your_secret_api_key_here"
|
||||
```
|
||||
|
||||
#### Sending as Base64
|
||||
|
||||
```bash
|
||||
curl -X POST -d "base64=$(base64 path/to/audio.mp3)" http://localhost:4040/process-audio \
|
||||
-d "format=ogg" \
|
||||
-H "apikey: your_secret_api_key_here"
|
||||
```
|
||||
|
||||
#### Sending as URL
|
||||
|
||||
```bash
|
||||
curl -X POST -d "url=https://example.com/path/to/audio.mp3" http://localhost:4040/process-audio \
|
||||
-d "format=ogg" \
|
||||
-H "apikey: your_secret_api_key_here"
|
||||
```
|
||||
|
||||
### Response
|
||||
|
||||
The response will be a JSON object containing the audio duration and the converted audio file in base64:
|
||||
With S3 storage enabled:
|
||||
|
||||
```json
|
||||
{
|
||||
"duration": 120,
|
||||
"audio": "UklGR... (base64 of the file)",
|
||||
"format": "ogg"
|
||||
"url": "https://your-s3-endpoint/bucket/file.ogg?signature...",
|
||||
"format": "ogg",
|
||||
"transcription": "Transcribed text here..." // if requested
|
||||
}
|
||||
```
|
||||
|
||||
- `duration`: The audio duration in seconds.
|
||||
- `audio`: The converted audio file encoded in base64.
|
||||
- `format`: The format of the converted file (`mp3` or `ogg`).
|
||||
|
||||
## License
|
||||
|
||||
This project is licensed under the [MIT](LICENSE) license.
|
32
go.mod
32
go.mod
@ -1,36 +1,48 @@
|
||||
module github.com/EvolutionAPI/evolution-audio-converter
|
||||
|
||||
go 1.21.1
|
||||
go 1.22
|
||||
|
||||
require github.com/gin-gonic/gin v1.10.0
|
||||
toolchain go1.22.9
|
||||
|
||||
require (
|
||||
github.com/gin-contrib/cors v1.7.2
|
||||
github.com/gin-gonic/gin v1.10.0
|
||||
github.com/joho/godotenv v1.5.1
|
||||
github.com/minio/minio-go/v7 v7.0.81
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/bytedance/sonic v1.11.6 // indirect
|
||||
github.com/bytedance/sonic/loader v0.1.1 // indirect
|
||||
github.com/cloudwego/base64x v0.1.4 // indirect
|
||||
github.com/cloudwego/iasm v0.2.0 // indirect
|
||||
github.com/dustin/go-humanize v1.0.1 // indirect
|
||||
github.com/gabriel-vasile/mimetype v1.4.3 // indirect
|
||||
github.com/gin-contrib/cors v1.7.2 // indirect
|
||||
github.com/gin-contrib/sse v0.1.0 // indirect
|
||||
github.com/go-ini/ini v1.67.0 // indirect
|
||||
github.com/go-playground/locales v0.14.1 // indirect
|
||||
github.com/go-playground/universal-translator v0.18.1 // indirect
|
||||
github.com/go-playground/validator/v10 v10.20.0 // indirect
|
||||
github.com/goccy/go-json v0.10.2 // indirect
|
||||
github.com/joho/godotenv v1.5.1 // indirect
|
||||
github.com/goccy/go-json v0.10.3 // indirect
|
||||
github.com/google/uuid v1.6.0 // indirect
|
||||
github.com/json-iterator/go v1.1.12 // indirect
|
||||
github.com/klauspost/cpuid/v2 v2.2.7 // indirect
|
||||
github.com/klauspost/compress v1.17.11 // indirect
|
||||
github.com/klauspost/cpuid/v2 v2.2.8 // indirect
|
||||
github.com/kr/text v0.2.0 // indirect
|
||||
github.com/leodido/go-urn v1.4.0 // indirect
|
||||
github.com/mattn/go-isatty v0.0.20 // indirect
|
||||
github.com/minio/md5-simd v1.1.2 // indirect
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
||||
github.com/modern-go/reflect2 v1.0.2 // indirect
|
||||
github.com/pelletier/go-toml/v2 v2.2.2 // indirect
|
||||
github.com/rs/xid v1.6.0 // indirect
|
||||
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
|
||||
github.com/ugorji/go/codec v1.2.12 // indirect
|
||||
golang.org/x/arch v0.8.0 // indirect
|
||||
golang.org/x/crypto v0.23.0 // indirect
|
||||
golang.org/x/net v0.25.0 // indirect
|
||||
golang.org/x/sys v0.20.0 // indirect
|
||||
golang.org/x/text v0.15.0 // indirect
|
||||
golang.org/x/crypto v0.28.0 // indirect
|
||||
golang.org/x/net v0.30.0 // indirect
|
||||
golang.org/x/sys v0.26.0 // indirect
|
||||
golang.org/x/text v0.19.0 // indirect
|
||||
google.golang.org/protobuf v1.34.1 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
)
|
||||
|
49
go.sum
49
go.sum
@ -6,9 +6,12 @@ github.com/cloudwego/base64x v0.1.4 h1:jwCgWpFanWmN8xoIUHa2rtzmkd5J2plF/dnLS6Xd/
|
||||
github.com/cloudwego/base64x v0.1.4/go.mod h1:0zlkT4Wn5C6NdauXdJRhSKRlJvmclQ1hhJgA0rcu/8w=
|
||||
github.com/cloudwego/iasm v0.2.0 h1:1KNIy1I1H9hNNFEEH3DVnI4UujN+1zjpuk6gwHLTssg=
|
||||
github.com/cloudwego/iasm v0.2.0/go.mod h1:8rXZaNYT2n95jn+zTI1sDr+IgcD2GVs0nlbbQPiEFhY=
|
||||
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY=
|
||||
github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto=
|
||||
github.com/gabriel-vasile/mimetype v1.4.3 h1:in2uUcidCuFcDKtdcBxlR0rJ1+fsokWf+uqxgUFjbI0=
|
||||
github.com/gabriel-vasile/mimetype v1.4.3/go.mod h1:d8uq/6HKRL6CGdk+aubisF/M5GcPfT7nKyLpA0lbSSk=
|
||||
github.com/gin-contrib/cors v1.7.2 h1:oLDHxdg8W/XDoN/8zamqk/Drgt4oVZDvaV0YmvVICQw=
|
||||
@ -17,6 +20,8 @@ github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE
|
||||
github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI=
|
||||
github.com/gin-gonic/gin v1.10.0 h1:nTuyha1TYqgedzytsKYqna+DfLos46nTv2ygFy86HFU=
|
||||
github.com/gin-gonic/gin v1.10.0/go.mod h1:4PMNQiOhvDRa013RKVbsiNwoyezlm2rm0uX/T7kzp5Y=
|
||||
github.com/go-ini/ini v1.67.0 h1:z6ZrTEZqSWOTyH2FlglNbNgARyHG8oLW9gMELqKr06A=
|
||||
github.com/go-ini/ini v1.67.0/go.mod h1:ByCAeIL28uOIIG0E3PJtZPDL8WnHpFKFOtgjp+3Ies8=
|
||||
github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s=
|
||||
github.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
|
||||
github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA=
|
||||
@ -25,23 +30,36 @@ github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJn
|
||||
github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY=
|
||||
github.com/go-playground/validator/v10 v10.20.0 h1:K9ISHbSaI0lyB2eWMPJo+kOS/FBExVwjEviJTixqxL8=
|
||||
github.com/go-playground/validator/v10 v10.20.0/go.mod h1:dbuPbCMFw/DrkbEynArYaCwl3amGuJotoKCe95atGMM=
|
||||
github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU=
|
||||
github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
|
||||
github.com/goccy/go-json v0.10.3 h1:KZ5WoDbxAIgm2HNbYckL0se1fHD6rz5j4ywS6ebzDqA=
|
||||
github.com/goccy/go-json v0.10.3/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M=
|
||||
github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU=
|
||||
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
||||
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0=
|
||||
github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4=
|
||||
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
|
||||
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
|
||||
github.com/klauspost/compress v1.17.11 h1:In6xLpyWOi1+C7tXUUWv2ot1QvBjxevKAaI6IXrJmUc=
|
||||
github.com/klauspost/compress v1.17.11/go.mod h1:pMDklpSncoRMuLFrf1W9Ss9KT+0rH90U12bZKk7uwG0=
|
||||
github.com/klauspost/cpuid/v2 v2.0.1/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
|
||||
github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
|
||||
github.com/klauspost/cpuid/v2 v2.2.7 h1:ZWSB3igEs+d0qvnxR/ZBzXVmxkgt8DdzP6m9pfuVLDM=
|
||||
github.com/klauspost/cpuid/v2 v2.2.7/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws=
|
||||
github.com/klauspost/cpuid/v2 v2.2.8 h1:+StwCXwm9PdpiEkPyzBXIy+M9KUb4ODm0Zarf1kS5BM=
|
||||
github.com/klauspost/cpuid/v2 v2.2.8/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws=
|
||||
github.com/knz/go-libedit v1.10.1/go.mod h1:MZTVkCWyz0oBc7JOWP3wNAzd002ZbM/5hgShxwh4x8M=
|
||||
github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0=
|
||||
github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk=
|
||||
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||
github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ=
|
||||
github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI=
|
||||
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
|
||||
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
||||
github.com/minio/md5-simd v1.1.2 h1:Gdi1DZK69+ZVMoNHRXJyNcxrMA4dSxoYHZSQbirFg34=
|
||||
github.com/minio/md5-simd v1.1.2/go.mod h1:MzdKDxYpY2BT9XQFocsiZf/NKVtR7nkE4RoEpN+20RM=
|
||||
github.com/minio/minio-go/v7 v7.0.81 h1:SzhMN0TQ6T/xSBu6Nvw3M5M8voM+Ht8RH3hE8S7zxaA=
|
||||
github.com/minio/minio-go/v7 v7.0.81/go.mod h1:84gmIilaX4zcvAWWzJ5Z1WI5axN+hAbM5w25xf8xvC0=
|
||||
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
@ -51,6 +69,10 @@ github.com/pelletier/go-toml/v2 v2.2.2 h1:aYUidT7k73Pcl9nb2gScu7NSrKCSHIDE89b3+6
|
||||
github.com/pelletier/go-toml/v2 v2.2.2/go.mod h1:1t835xjRzz80PqgE6HHgN2JOsmgYu/h4qDAS4n929Rs=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/rogpeppe/go-internal v1.8.0 h1:FCbCCtXNOY3UtUuHUYaghJg4y7Fd14rXifAYUAtL9R8=
|
||||
github.com/rogpeppe/go-internal v1.8.0/go.mod h1:WmiCO8CzOY8rg0OYDC4/i/2WRWAB6poM+XZ2dLUbcbE=
|
||||
github.com/rs/xid v1.6.0 h1:fV591PaemRlL6JfRxGDEPl69wICngIQ3shQtzfy2gxU=
|
||||
github.com/rs/xid v1.6.0/go.mod h1:7XoLgs4eV+QndskICGsho+ADou8ySMSjJKDIan90Nz0=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
||||
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
|
||||
@ -70,22 +92,23 @@ github.com/ugorji/go/codec v1.2.12/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZ
|
||||
golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8=
|
||||
golang.org/x/arch v0.8.0 h1:3wRIsP3pM4yUptoR96otTUOXI367OS0+c9eeRi9doIc=
|
||||
golang.org/x/arch v0.8.0/go.mod h1:FEVrYAQjsQXMVJ1nsMoVVXPZg6p2JE2mx8psSWTDQys=
|
||||
golang.org/x/crypto v0.23.0 h1:dIJU/v2J8Mdglj/8rJ6UUOM3Zc9zLZxVZwwxMooUSAI=
|
||||
golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8=
|
||||
golang.org/x/net v0.25.0 h1:d/OCCoBEUq33pjydKrGQhw7IlUPI2Oylr+8qLx49kac=
|
||||
golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM=
|
||||
golang.org/x/crypto v0.28.0 h1:GBDwsMXVQi34v5CCYUm2jkJvu4cbtru2U4TN2PSyQnw=
|
||||
golang.org/x/crypto v0.28.0/go.mod h1:rmgy+3RHxRZMyY0jjAJShp2zgEdOqj2AO7U0pYmeQ7U=
|
||||
golang.org/x/net v0.30.0 h1:AcW1SDZMkb8IpzCdQUaIq2sP4sZ4zw+55h6ynffypl4=
|
||||
golang.org/x/net v0.30.0/go.mod h1:2wGyMJ5iFasEhkwi13ChkO/t1ECNC4X4eBKkVFyYFlU=
|
||||
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.20.0 h1:Od9JTbYCk261bKm4M/mw7AklTlFYIa0bIp9BgSm1S8Y=
|
||||
golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/text v0.15.0 h1:h1V/4gjBv8v9cjcR6+AR5+/cIYK5N/WAgiv4xlsEtAk=
|
||||
golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
|
||||
golang.org/x/sys v0.26.0 h1:KHjCJyddX0LoSTb3J+vWpupP9p0oznkqVk/IfjymZbo=
|
||||
golang.org/x/sys v0.26.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/text v0.19.0 h1:kTxAhCbGbxhK0IwgSKiMO5awPoDQ0RpfiVYBfK860YM=
|
||||
golang.org/x/text v0.19.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
google.golang.org/protobuf v1.34.1 h1:9ddQBjfCyZPOHPUiPxpYESBLc+T8P3E+Vo4IbKZgFWg=
|
||||
google.golang.org/protobuf v1.34.1/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
|
144
main.go
144
main.go
@ -2,6 +2,7 @@ package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
@ -17,9 +18,13 @@ import (
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"time"
|
||||
|
||||
"github.com/gin-contrib/cors"
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/joho/godotenv"
|
||||
"github.com/minio/minio-go/v7"
|
||||
"github.com/minio/minio-go/v7/pkg/credentials"
|
||||
)
|
||||
|
||||
var (
|
||||
@ -36,6 +41,15 @@ var (
|
||||
openaiAPIKey string
|
||||
groqAPIKey string
|
||||
defaultTranscriptionLanguage string
|
||||
enableS3Storage bool
|
||||
s3Endpoint string
|
||||
s3AccessKey string
|
||||
s3SecretKey string
|
||||
s3BucketName string
|
||||
s3Region string
|
||||
s3UseSSL bool
|
||||
s3Client *minio.Client
|
||||
s3URLExpiration time.Duration
|
||||
)
|
||||
|
||||
func init() {
|
||||
@ -70,6 +84,57 @@ func init() {
|
||||
openaiAPIKey = os.Getenv("OPENAI_API_KEY")
|
||||
groqAPIKey = os.Getenv("GROQ_API_KEY")
|
||||
defaultTranscriptionLanguage = os.Getenv("TRANSCRIPTION_LANGUAGE")
|
||||
|
||||
// Configuração do S3
|
||||
enableS3Storage = os.Getenv("ENABLE_S3_STORAGE") == "true"
|
||||
if enableS3Storage {
|
||||
s3Endpoint = os.Getenv("S3_ENDPOINT")
|
||||
s3AccessKey = os.Getenv("S3_ACCESS_KEY")
|
||||
s3SecretKey = os.Getenv("S3_SECRET_KEY")
|
||||
s3BucketName = os.Getenv("S3_BUCKET_NAME")
|
||||
s3Region = os.Getenv("S3_REGION")
|
||||
s3UseSSL = os.Getenv("S3_USE_SSL") == "true"
|
||||
|
||||
// Parse URL expiration duration, default to 24 hours
|
||||
expiration := os.Getenv("S3_URL_EXPIRATION")
|
||||
if expiration == "" {
|
||||
expiration = "24h"
|
||||
}
|
||||
var err error
|
||||
s3URLExpiration, err = time.ParseDuration(expiration)
|
||||
if err != nil {
|
||||
fmt.Printf("Invalid S3_URL_EXPIRATION format, using default 24h: %v\n", err)
|
||||
s3URLExpiration = 24 * time.Hour
|
||||
}
|
||||
|
||||
// Initialize MinIO client
|
||||
minioClient, err := minio.New(s3Endpoint, &minio.Options{
|
||||
Creds: credentials.NewStaticV4(s3AccessKey, s3SecretKey, ""),
|
||||
Secure: s3UseSSL,
|
||||
Region: s3Region,
|
||||
})
|
||||
if err != nil {
|
||||
fmt.Printf("Error initializing S3 client: %v\n", err)
|
||||
return
|
||||
}
|
||||
s3Client = minioClient
|
||||
|
||||
// Create bucket if it doesn't exist
|
||||
exists, err := s3Client.BucketExists(context.Background(), s3BucketName)
|
||||
if err != nil {
|
||||
fmt.Printf("Error checking bucket existence: %v\n", err)
|
||||
return
|
||||
}
|
||||
|
||||
if !exists {
|
||||
err = s3Client.MakeBucket(context.Background(), s3BucketName, minio.MakeBucketOptions{Region: s3Region})
|
||||
if err != nil {
|
||||
fmt.Printf("Error creating bucket: %v\n", err)
|
||||
return
|
||||
}
|
||||
fmt.Printf("Created bucket: %s\n", s3BucketName)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func validateAPIKey(c *gin.Context) bool {
|
||||
@ -175,6 +240,11 @@ func transcribeAudio(audioData []byte, language string) (string, error) {
|
||||
return "", errors.New("transcription is not enabled")
|
||||
}
|
||||
|
||||
// Se nenhum idioma foi especificado, use o padrão do .env
|
||||
if language == "" {
|
||||
language = defaultTranscriptionLanguage
|
||||
}
|
||||
|
||||
switch transcriptionProvider {
|
||||
case "openai":
|
||||
return transcribeWithOpenAI(audioData, language)
|
||||
@ -339,6 +409,43 @@ func transcribeWithGroq(audioData []byte, language string) (string, error) {
|
||||
return result.Text, nil
|
||||
}
|
||||
|
||||
func uploadToS3(data []byte, format string) (string, error) {
|
||||
if !enableS3Storage || s3Client == nil {
|
||||
return "", errors.New("S3 storage is not enabled or properly configured")
|
||||
}
|
||||
|
||||
// Generate unique filename
|
||||
filename := fmt.Sprintf("%d.%s", time.Now().UnixNano(), format)
|
||||
contentType := fmt.Sprintf("audio/%s", format)
|
||||
|
||||
// Upload to S3
|
||||
_, err := s3Client.PutObject(
|
||||
context.Background(),
|
||||
s3BucketName,
|
||||
filename,
|
||||
bytes.NewReader(data),
|
||||
int64(len(data)),
|
||||
minio.PutObjectOptions{ContentType: contentType},
|
||||
)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("error uploading to S3: %v", err)
|
||||
}
|
||||
|
||||
// Generate presigned URL
|
||||
url, err := s3Client.PresignedGetObject(
|
||||
context.Background(),
|
||||
s3BucketName,
|
||||
filename,
|
||||
s3URLExpiration,
|
||||
nil,
|
||||
)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("error generating presigned URL: %v", err)
|
||||
}
|
||||
|
||||
return url.String(), nil
|
||||
}
|
||||
|
||||
func processAudio(c *gin.Context) {
|
||||
if !validateAPIKey(c) {
|
||||
return
|
||||
@ -358,27 +465,35 @@ func processAudio(c *gin.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
var transcription string
|
||||
if c.DefaultPostForm("transcribe", "false") == "true" {
|
||||
language := c.DefaultPostForm("language", "")
|
||||
trans, err := transcribeAudio(convertedData, language)
|
||||
if err != nil {
|
||||
fmt.Printf("Erro na transcrição: %v\n", err)
|
||||
// Continua sem a transcrição
|
||||
} else {
|
||||
transcription = trans
|
||||
}
|
||||
}
|
||||
|
||||
response := gin.H{
|
||||
"duration": duration,
|
||||
"audio": base64.StdEncoding.EncodeToString(convertedData),
|
||||
"format": format,
|
||||
}
|
||||
|
||||
if transcription != "" {
|
||||
// Handle S3 upload if enabled
|
||||
if enableS3Storage {
|
||||
url, err := uploadToS3(convertedData, format)
|
||||
if err != nil {
|
||||
fmt.Printf("Error uploading to S3: %v\n", err)
|
||||
// Fallback to base64 if S3 upload fails
|
||||
response["audio"] = base64.StdEncoding.EncodeToString(convertedData)
|
||||
} else {
|
||||
response["url"] = url
|
||||
}
|
||||
} else {
|
||||
response["audio"] = base64.StdEncoding.EncodeToString(convertedData)
|
||||
}
|
||||
|
||||
// Handle transcription if requested
|
||||
if c.DefaultPostForm("transcribe", "false") == "true" {
|
||||
language := c.DefaultPostForm("language", "")
|
||||
transcription, err := transcribeAudio(convertedData, language)
|
||||
if err != nil {
|
||||
fmt.Printf("Error in transcription: %v\n", err)
|
||||
} else {
|
||||
response["transcription"] = transcription
|
||||
}
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, response)
|
||||
}
|
||||
@ -456,6 +571,7 @@ func transcribeOnly(c *gin.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
// Pega o idioma da requisição ou usa vazio para usar o padrão do .env
|
||||
language := c.DefaultPostForm("language", "")
|
||||
transcription, err := transcribeAudio(convertedData, language)
|
||||
if err != nil {
|
||||
|
Loading…
Reference in New Issue
Block a user