Properly save audio and image notes when uploaded
Also fix the audio processing pipeline.
This commit is contained in:
parent
4d02357671
commit
39d9f6d258
11 changed files with 145 additions and 138 deletions
63
api/api.go
63
api/api.go
|
|
@ -16,6 +16,8 @@ import (
|
|||
"github.com/Gleipnir-Technology/nidus-sync/platform"
|
||||
"github.com/Gleipnir-Technology/nidus-sync/queue"
|
||||
"github.com/Gleipnir-Technology/nidus-sync/userfile"
|
||||
"github.com/aarondl/opt/omit"
|
||||
"github.com/aarondl/opt/omitnull"
|
||||
"github.com/go-chi/chi/v5"
|
||||
"github.com/go-chi/render"
|
||||
"github.com/google/uuid"
|
||||
|
|
@ -41,7 +43,18 @@ func apiAudioPost(w http.ResponseWriter, r *http.Request, u *models.User) {
|
|||
http.Error(w, "Failed to decode the payload", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
if err := db.NoteAudioCreate(context.Background(), noteUUID, db.NoteAudio{}, u.ID); err != nil {
|
||||
setter := models.NoteAudioSetter{
|
||||
Created: omit.From(payload.Created),
|
||||
CreatorID: omit.From(u.ID),
|
||||
Deleted: omitnull.FromPtr(payload.Deleted),
|
||||
DeletorID: omitnull.FromPtr(payload.DeletorID),
|
||||
Duration: omit.From(payload.Duration),
|
||||
Transcription: omitnull.FromPtr(payload.Transcription),
|
||||
TranscriptionUserEdited: omit.From(payload.TranscriptionUserEdited),
|
||||
Version: omit.From(payload.Version),
|
||||
UUID: omit.From(noteUUID),
|
||||
}
|
||||
if err := db.NoteAudioCreate(context.Background(), u.R.Organization, u.ID, setter); err != nil {
|
||||
render.Render(w, r, errRender(err))
|
||||
return
|
||||
}
|
||||
|
|
@ -92,8 +105,15 @@ func handleClientIos(w http.ResponseWriter, r *http.Request, u *models.User) {
|
|||
return
|
||||
}
|
||||
|
||||
var since_used time.Time
|
||||
if since == nil {
|
||||
since_used = time.Unix(0, 0)
|
||||
} else {
|
||||
since_used = *since
|
||||
}
|
||||
response := ResponseClientIos{
|
||||
Fieldseeker: toResponseFieldseeker(csync.Fieldseeker),
|
||||
Since: since_used,
|
||||
}
|
||||
if err := render.Render(w, r, response); err != nil {
|
||||
render.Render(w, r, errRender(err))
|
||||
|
|
@ -101,31 +121,6 @@ func handleClientIos(w http.ResponseWriter, r *http.Request, u *models.User) {
|
|||
}
|
||||
}
|
||||
|
||||
func apiClientIosNotePut(w http.ResponseWriter, r *http.Request, u *models.User) {
|
||||
id := chi.URLParam(r, "uuid")
|
||||
noteUUID, err := uuid.Parse(id)
|
||||
if err != nil {
|
||||
http.Error(w, "Failed to decode the uuid", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
var payload NidusNotePayload
|
||||
body, err := ioutil.ReadAll(r.Body)
|
||||
if err != nil {
|
||||
http.Error(w, "Failed to read the payload", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
if err := json.Unmarshal(body, &payload); err != nil {
|
||||
debugSaveRequest(body, err, "Note PUT JSON decode error")
|
||||
http.Error(w, "Failed to decode the payload", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
if err := db.NoteUpdate(context.Background(), noteUUID, db.NidusNotePayload{}); err != nil {
|
||||
render.Render(w, r, errRender(err))
|
||||
return
|
||||
}
|
||||
w.WriteHeader(http.StatusAccepted)
|
||||
}
|
||||
|
||||
func apiImagePost(w http.ResponseWriter, r *http.Request, u *models.User) {
|
||||
id := chi.URLParam(r, "uuid")
|
||||
noteUUID, err := uuid.Parse(id)
|
||||
|
|
@ -145,7 +140,15 @@ func apiImagePost(w http.ResponseWriter, r *http.Request, u *models.User) {
|
|||
http.Error(w, "Failed to decode the payload", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
err = db.NoteImageCreate(context.Background(), noteUUID, db.NoteImage{}, u.ID)
|
||||
setter := models.NoteImageSetter{
|
||||
Created: omit.From(payload.Created),
|
||||
CreatorID: omit.From(u.ID),
|
||||
Deleted: omitnull.FromPtr(payload.Deleted),
|
||||
DeletorID: omitnull.FromPtr(payload.DeletorID),
|
||||
Version: omit.From(payload.Version),
|
||||
UUID: omit.From(noteUUID),
|
||||
}
|
||||
err = db.NoteImageCreate(context.Background(), u.R.Organization, u.ID, setter)
|
||||
if err != nil {
|
||||
render.Render(w, r, errRender(err))
|
||||
return
|
||||
|
|
@ -187,7 +190,7 @@ func apiMosquitoSource(w http.ResponseWriter, r *http.Request, u *models.User) {
|
|||
}
|
||||
|
||||
data := []render.Renderer{}
|
||||
for _, s := range *sources {
|
||||
for _, s := range sources {
|
||||
data = append(data, NewResponseMosquitoSource(s))
|
||||
}
|
||||
if err := render.RenderList(w, r, data); err != nil {
|
||||
|
|
@ -212,7 +215,7 @@ func apiTrapData(w http.ResponseWriter, r *http.Request, u *models.User) {
|
|||
}
|
||||
|
||||
data := []render.Renderer{}
|
||||
for _, td := range *trap_data {
|
||||
for _, td := range trap_data {
|
||||
data = append(data, NewResponseTrapDatum(td))
|
||||
}
|
||||
if err := render.RenderList(w, r, data); err != nil {
|
||||
|
|
@ -236,7 +239,7 @@ func apiServiceRequest(w http.ResponseWriter, r *http.Request, u *models.User) {
|
|||
}
|
||||
|
||||
data := []render.Renderer{}
|
||||
for _, sr := range *requests {
|
||||
for _, sr := range requests {
|
||||
data = append(data, NewResponseServiceRequest(sr))
|
||||
}
|
||||
if err := render.RenderList(w, r, data); err != nil {
|
||||
|
|
|
|||
|
|
@ -14,7 +14,6 @@ func AddRoutes(r chi.Router) {
|
|||
r.Method("GET", "/service-request", auth.NewEnsureAuth(apiServiceRequest))
|
||||
r.Method("GET", "/trap-data", auth.NewEnsureAuth(apiTrapData))
|
||||
r.Method("GET", "/client/ios", auth.NewEnsureAuth(handleClientIos))
|
||||
r.Method("PUT", "/client/ios/note/{uuid}", auth.NewEnsureAuth(apiClientIosNotePut))
|
||||
r.Method("POST", "/audio/{uuid}", auth.NewEnsureAuth(apiAudioPost))
|
||||
r.Method("POST", "/audio/{uuid}/content", auth.NewEnsureAuth(apiAudioContentPost))
|
||||
r.Method("POST", "/image/{uuid}", auth.NewEnsureAuth(apiImagePost))
|
||||
|
|
|
|||
93
api/types.go
93
api/types.go
|
|
@ -32,44 +32,34 @@ func NewBounds() Bounds {
|
|||
}
|
||||
}
|
||||
|
||||
type LatLong interface {
|
||||
Latitude() float64
|
||||
Longitude() float64
|
||||
}
|
||||
|
||||
/* not sure if used
|
||||
type Location struct {
|
||||
Latitude float64
|
||||
Longitude float64
|
||||
}
|
||||
*/
|
||||
|
||||
type NoteImagePayload struct {
|
||||
UUID string `json:"uuid"`
|
||||
Cell H3Cell `json:"cell"`
|
||||
Created time.Time `json:"created"`
|
||||
}
|
||||
|
||||
type NoteAudio struct {
|
||||
UUID string `db:"uuid"`
|
||||
Breadcrumbs []NoteAudioBreadcrumbPayload
|
||||
Created time.Time `db:"created"`
|
||||
Creator int `db:"creator"`
|
||||
Deleted *time.Time `db:"deleted"`
|
||||
Duration int `db:"duration"`
|
||||
IsAudioNormalized bool `db:"is_audio_normalized"`
|
||||
IsTranscodedeToOgg bool `db:"is_transcoded_to_ogg"`
|
||||
Transcription *string `db:"transcription"`
|
||||
TranscriptionUserEdited bool `db:"transcription_user_edited"`
|
||||
Version int `db:"version"`
|
||||
UUID string `json:"uuid"`
|
||||
Cell H3Cell `json:"cell"`
|
||||
Created time.Time `json:"created"`
|
||||
CreatorID int `db:"creator_id"`
|
||||
Deleted *time.Time `json:"deleted"`
|
||||
DeletorID *int32 `json:"deletor_id"`
|
||||
Version int32 `json:"version"`
|
||||
}
|
||||
|
||||
type NoteAudioPayload struct {
|
||||
UUID string `json:"uuid"`
|
||||
Breadcrumbs []NoteAudioBreadcrumbPayload `json:"breadcrumbs"`
|
||||
Created time.Time `json:"created"`
|
||||
Duration int `json:"duration"`
|
||||
CreatorID int `json:"creator_id"`
|
||||
Deleted *time.Time `json:"deleted"`
|
||||
DeletorID *int32 `json:"deletor_id"`
|
||||
Duration float32 `json:"duration"`
|
||||
Transcription *string `json:"transcription"`
|
||||
TranscriptionUserEdited bool `json:"transcriptionUserEdited"`
|
||||
Version int `json:"version"`
|
||||
Version int32 `json:"version"`
|
||||
}
|
||||
|
||||
type ResponseMosquitoSource struct {
|
||||
|
|
@ -97,14 +87,6 @@ type NoteAudioBreadcrumbPayload struct {
|
|||
ManuallySelected bool `json:"manuallySelected"`
|
||||
}
|
||||
|
||||
type NidusNotePayload struct {
|
||||
UUID string `json:"uuid"`
|
||||
Timestamp time.Time `json:"timestamp"`
|
||||
H3Cell int64 `json:"h3cell"`
|
||||
Images []string `json:"images"`
|
||||
Text string `json:"text"`
|
||||
}
|
||||
|
||||
type ResponseFieldseeker struct {
|
||||
MosquitoSources []ResponseMosquitoSource `json:"sources"`
|
||||
ServiceRequests []ResponseServiceRequest `json:"requests"`
|
||||
|
|
@ -114,6 +96,7 @@ type ResponseFieldseeker struct {
|
|||
// ResponseErr renderer type for handling all sorts of errors.
|
||||
type ResponseClientIos struct {
|
||||
Fieldseeker ResponseFieldseeker `json:"fieldseeker"`
|
||||
Since time.Time `json:"since"`
|
||||
}
|
||||
|
||||
func (i ResponseClientIos) Render(w http.ResponseWriter, r *http.Request) error {
|
||||
|
|
@ -163,9 +146,9 @@ func NewResponseMosquitoInspection(i *models.FieldseekerMosquitoinspection) Resp
|
|||
SiteCondition: i.Sitecond.GetOr(""),
|
||||
}
|
||||
}
|
||||
func NewResponseMosquitoInspections(inspections *models.FieldseekerMosquitoinspectionSlice) []ResponseMosquitoInspection {
|
||||
func NewResponseMosquitoInspections(inspections models.FieldseekerMosquitoinspectionSlice) []ResponseMosquitoInspection {
|
||||
results := make([]ResponseMosquitoInspection, 0)
|
||||
for _, i := range *inspections {
|
||||
for _, i := range inspections {
|
||||
results = append(results, NewResponseMosquitoInspection(i))
|
||||
}
|
||||
return results
|
||||
|
|
@ -175,7 +158,7 @@ func (rtd ResponseMosquitoSource) Render(w http.ResponseWriter, r *http.Request)
|
|||
return nil
|
||||
}
|
||||
|
||||
func NewResponseMosquitoSource(ms *platform.MosquitoSource) ResponseMosquitoSource {
|
||||
func NewResponseMosquitoSource(ms platform.MosquitoSource) ResponseMosquitoSource {
|
||||
pl := ms.PointLocation
|
||||
return ResponseMosquitoSource{
|
||||
Active: toBool16(pl.Active),
|
||||
|
|
@ -196,9 +179,9 @@ func NewResponseMosquitoSource(ms *platform.MosquitoSource) ResponseMosquitoSour
|
|||
Zone: pl.Zone.GetOr(""),
|
||||
}
|
||||
}
|
||||
func NewResponseMosquitoSources(sources *[]*platform.MosquitoSource) []ResponseMosquitoSource {
|
||||
func NewResponseMosquitoSources(sources []platform.MosquitoSource) []ResponseMosquitoSource {
|
||||
results := make([]ResponseMosquitoSource, 0)
|
||||
for _, i := range *sources {
|
||||
for _, i := range sources {
|
||||
results = append(results, NewResponseMosquitoSource(i))
|
||||
}
|
||||
return results
|
||||
|
|
@ -224,24 +207,22 @@ func (rtd ResponseMosquitoTreatment) Render(w http.ResponseWriter, r *http.Reque
|
|||
}
|
||||
func NewResponseMosquitoTreatment(i *models.FieldseekerTreatment) ResponseMosquitoTreatment {
|
||||
return ResponseMosquitoTreatment{
|
||||
/*
|
||||
Comments: i.Comments(),
|
||||
Created: i.Created().Format("2006-01-02T15:04:05.000Z"),
|
||||
FieldTechnician: i.FieldTechnician(),
|
||||
Habitat: i.Habitat(),
|
||||
ID: i.ID(),
|
||||
Product: i.Product(),
|
||||
Quantity: i.Quantity(),
|
||||
QuantityUnit: i.QuantityUnit(),
|
||||
SiteCondition: i.SiteCondition(),
|
||||
TreatAcres: i.TreatAcres(),
|
||||
TreatHectares: i.TreatHectares(),
|
||||
*/
|
||||
Comments: i.Comments.GetOr(""),
|
||||
Created: formatTime(i.Creationdate),
|
||||
FieldTechnician: i.Fieldtech.GetOr(""),
|
||||
Habitat: i.Habitat.GetOr(""),
|
||||
ID: i.Globalid.String(),
|
||||
Product: i.Product.GetOr(""),
|
||||
Quantity: i.Qty.GetOr(0),
|
||||
QuantityUnit: i.Qtyunit.GetOr(""),
|
||||
SiteCondition: i.Sitecond.GetOr(""),
|
||||
TreatAcres: i.Treatacres.GetOr(0.0),
|
||||
TreatHectares: i.Treathectares.GetOr(0.0),
|
||||
}
|
||||
}
|
||||
func NewResponseMosquitoTreatments(treatments *models.FieldseekerTreatmentSlice) []ResponseMosquitoTreatment {
|
||||
func NewResponseMosquitoTreatments(treatments models.FieldseekerTreatmentSlice) []ResponseMosquitoTreatment {
|
||||
results := make([]ResponseMosquitoTreatment, 0)
|
||||
for _, i := range *treatments {
|
||||
for _, i := range treatments {
|
||||
results = append(results, NewResponseMosquitoTreatment(i))
|
||||
}
|
||||
return results
|
||||
|
|
@ -298,9 +279,9 @@ func NewResponseServiceRequest(sr *models.FieldseekerServicerequest) ResponseSer
|
|||
Zip: sr.Reqzip.GetOr(""),
|
||||
}
|
||||
}
|
||||
func NewResponseServiceRequests(requests *models.FieldseekerServicerequestSlice) []ResponseServiceRequest {
|
||||
func NewResponseServiceRequests(requests models.FieldseekerServicerequestSlice) []ResponseServiceRequest {
|
||||
results := make([]ResponseServiceRequest, 0)
|
||||
for _, i := range *requests {
|
||||
for _, i := range requests {
|
||||
results = append(results, NewResponseServiceRequest(i))
|
||||
}
|
||||
return results
|
||||
|
|
@ -326,9 +307,9 @@ func NewResponseTrapDatum(td *models.FieldseekerTraplocation) ResponseTrapData {
|
|||
Name: td.Name.GetOr(""),
|
||||
}
|
||||
}
|
||||
func NewResponseTrapData(data *models.FieldseekerTraplocationSlice) []ResponseTrapData {
|
||||
func NewResponseTrapData(data models.FieldseekerTraplocationSlice) []ResponseTrapData {
|
||||
results := make([]ResponseTrapData, 0)
|
||||
for _, i := range *data {
|
||||
for _, i := range data {
|
||||
results = append(results, NewResponseTrapDatum(i))
|
||||
}
|
||||
return results
|
||||
|
|
|
|||
|
|
@ -173,6 +173,7 @@ func findUser(ctx context.Context, user_id int) (*models.User, error) {
|
|||
return nil, err
|
||||
}
|
||||
}
|
||||
log.Info().Int32("user_id", user.ID).Int32("org_id", user.OrganizationID).Msg("Found user")
|
||||
return user, err
|
||||
}
|
||||
|
||||
|
|
|
|||
43
db/query.go
43
db/query.go
|
|
@ -3,20 +3,25 @@ package db
|
|||
import (
|
||||
"context"
|
||||
|
||||
"github.com/Gleipnir-Technology/nidus-sync/db/models"
|
||||
"github.com/google/uuid"
|
||||
"github.com/rs/zerolog/log"
|
||||
)
|
||||
type NidusNotePayload struct {}
|
||||
type NoteAudio struct {
|
||||
Transcription string
|
||||
Version int
|
||||
UUID uuid.UUID
|
||||
}
|
||||
type NoteImage struct {}
|
||||
|
||||
func NoteAudioCreate(ctx context.Context, noteUUID uuid.UUID, payload NoteAudio, userID int32) error {
|
||||
return nil
|
||||
func NoteAudioCreate(ctx context.Context, org *models.Organization, userID int32, setter models.NoteAudioSetter) error {
|
||||
err := org.InsertNoteAudios(ctx, PGInstance.BobDB, &setter)
|
||||
if err == nil {
|
||||
return nil
|
||||
}
|
||||
// Just ignore this failure, it means we already have this content
|
||||
if err.Error() == "insertOrganizationNoteAudios0: ERROR: duplicate key value violates unique constraint \"note_audio_pkey\" (SQLSTATE 23505)" {
|
||||
return nil
|
||||
}
|
||||
log.Warn().Err(err).Msg("Unrecognized error creating note audio")
|
||||
return err
|
||||
}
|
||||
func NoteAudioGetLatest(ctx context.Context, uuid string) (*NoteAudio, error) {
|
||||
|
||||
func NoteAudioGetLatest(ctx context.Context, uuid string) (*models.NoteAudio, error) {
|
||||
return nil, nil
|
||||
}
|
||||
func NoteAudioNormalized(uuid string) error {
|
||||
|
|
@ -25,9 +30,19 @@ func NoteAudioNormalized(uuid string) error {
|
|||
func NoteAudioTranscodedToOgg(uuid string) error {
|
||||
return nil
|
||||
}
|
||||
func NoteImageCreate(ctx context.Context, noteUUID uuid.UUID, payload NoteImage, userID int32) error {
|
||||
return nil
|
||||
}
|
||||
func NoteUpdate(ctx context.Context, noteUUID uuid.UUID, payload NidusNotePayload) error {
|
||||
func NoteImageCreate(ctx context.Context, org *models.Organization, userID int32, setter models.NoteImageSetter) error {
|
||||
err := org.InsertNoteImages(ctx, PGInstance.BobDB, &setter)
|
||||
if err == nil {
|
||||
return nil
|
||||
}
|
||||
// Just ignore this failure, it means we already have this content
|
||||
if err.Error() == "insertOrganizationNoteImages0: ERROR: duplicate key value violates unique constraint \"note_image_pkey\" (SQLSTATE 23505)" {
|
||||
return nil
|
||||
}
|
||||
log.Warn().Err(err).Msg("Unrecognized error creating note audio")
|
||||
return err
|
||||
}
|
||||
|
||||
func NoteUpdate(ctx context.Context, noteUUID uuid.UUID, setter models.NoteAudioSetter) error {
|
||||
return nil
|
||||
}
|
||||
|
|
|
|||
7
main.go
7
main.go
|
|
@ -14,6 +14,7 @@ import (
|
|||
"github.com/Gleipnir-Technology/nidus-sync/api"
|
||||
"github.com/Gleipnir-Technology/nidus-sync/auth"
|
||||
"github.com/Gleipnir-Technology/nidus-sync/db"
|
||||
"github.com/Gleipnir-Technology/nidus-sync/queue"
|
||||
"github.com/Gleipnir-Technology/nidus-sync/userfile"
|
||||
"github.com/go-chi/chi/v5"
|
||||
"github.com/go-chi/chi/v5/middleware"
|
||||
|
|
@ -162,6 +163,12 @@ func main() {
|
|||
refreshFieldseekerData(ctx, NewOAuthTokenChannel)
|
||||
}()
|
||||
|
||||
waitGroup.Add(1)
|
||||
go func() {
|
||||
defer waitGroup.Done()
|
||||
queue.StartAudioWorker(ctx)
|
||||
}()
|
||||
|
||||
server := &http.Server{
|
||||
Addr: bind,
|
||||
Handler: r,
|
||||
|
|
|
|||
|
|
@ -4,17 +4,17 @@ import (
|
|||
"github.com/Gleipnir-Technology/nidus-sync/db/models"
|
||||
)
|
||||
|
||||
func MosquitoSourceQuery() (*[]*MosquitoSource, error) {
|
||||
results := make([]*MosquitoSource, 0)
|
||||
return &results, nil
|
||||
func MosquitoSourceQuery() ([]MosquitoSource, error) {
|
||||
results := make([]MosquitoSource, 0)
|
||||
return results, nil
|
||||
}
|
||||
|
||||
func ServiceRequestQuery() (*models.FieldseekerServicerequestSlice, error) {
|
||||
func ServiceRequestQuery() (models.FieldseekerServicerequestSlice, error) {
|
||||
results := make(models.FieldseekerServicerequestSlice, 0)
|
||||
return &results, nil
|
||||
return results, nil
|
||||
}
|
||||
|
||||
func TrapDataQuery() (*models.FieldseekerTraplocationSlice, error) {
|
||||
func TrapDataQuery() (models.FieldseekerTraplocationSlice, error) {
|
||||
results := make(models.FieldseekerTraplocationSlice, 0)
|
||||
return &results, nil
|
||||
return results, nil
|
||||
}
|
||||
|
|
|
|||
|
|
@ -57,7 +57,7 @@ func fieldseeker(ctx context.Context, u *models.User, since *time.Time) (fsync F
|
|||
ts = append(ts, t)
|
||||
treatments_by_location[locid] = ts
|
||||
}
|
||||
sources := make([]*MosquitoSource, 0)
|
||||
sources := make([]MosquitoSource, 0)
|
||||
for _, p := range pl {
|
||||
inspections, ok := inspections_by_location[p.Globalid]
|
||||
if !ok {
|
||||
|
|
@ -68,13 +68,13 @@ func fieldseeker(ctx context.Context, u *models.User, since *time.Time) (fsync F
|
|||
treatments = make(models.FieldseekerTreatmentSlice, 0)
|
||||
}
|
||||
ms := MosquitoSource{
|
||||
PointLocation: p,
|
||||
Inspections: &inspections,
|
||||
Treatments: &treatments,
|
||||
PointLocation: *p,
|
||||
Inspections: inspections,
|
||||
Treatments: treatments,
|
||||
}
|
||||
sources = append(sources, &ms)
|
||||
sources = append(sources, ms)
|
||||
}
|
||||
fsync.MosquitoSources = &sources
|
||||
fsync.MosquitoSources = sources
|
||||
return fsync, err
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -12,13 +12,13 @@ type ClientSync struct {
|
|||
}
|
||||
|
||||
type FieldseekerRecordsSync struct {
|
||||
MosquitoSources *[]*MosquitoSource
|
||||
ServiceRequests *models.FieldseekerServicerequestSlice
|
||||
TrapData *models.FieldseekerTraplocationSlice
|
||||
MosquitoSources []MosquitoSource
|
||||
ServiceRequests models.FieldseekerServicerequestSlice
|
||||
TrapData models.FieldseekerTraplocationSlice
|
||||
}
|
||||
|
||||
type MosquitoSource struct {
|
||||
PointLocation *models.FieldseekerPointlocation
|
||||
Inspections *models.FieldseekerMosquitoinspectionSlice
|
||||
Treatments *models.FieldseekerTreatmentSlice
|
||||
PointLocation models.FieldseekerPointlocation
|
||||
Inspections models.FieldseekerMosquitoinspectionSlice
|
||||
Treatments models.FieldseekerTreatmentSlice
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,13 +4,13 @@ import (
|
|||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
"os/exec"
|
||||
|
||||
"github.com/Gleipnir-Technology/nidus-sync/db"
|
||||
"github.com/Gleipnir-Technology/nidus-sync/userfile"
|
||||
"github.com/google/uuid"
|
||||
"github.com/rs/zerolog/log"
|
||||
)
|
||||
|
||||
// AudioJob represents a job to process an audio file.
|
||||
|
|
@ -25,18 +25,18 @@ var audioJobChannel chan AudioJob
|
|||
func StartAudioWorker(ctx context.Context) {
|
||||
buffer := 100
|
||||
audioJobChannel = make(chan AudioJob, buffer) // Buffered channel to prevent blocking
|
||||
log.Printf("Started audio worker with buffer depth %d", buffer)
|
||||
log.Info().Int("buffer depth", buffer).Msg("Started audio worker")
|
||||
go func() {
|
||||
for {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
log.Println("Audio worker shutting down.")
|
||||
log.Info().Msg("Audio worker shutting down.")
|
||||
return
|
||||
case job := <-audioJobChannel:
|
||||
log.Printf("Processing audio job for UUID: %s", job.AudioUUID)
|
||||
log.Info().Str("uuid", job.AudioUUID.String()).Msg("Processing audio job")
|
||||
err := processAudioFile(job.AudioUUID)
|
||||
if err != nil {
|
||||
log.Printf("Error processing audio file %s: %v", job.AudioUUID, err)
|
||||
log.Error().Err(err).Str("uuid", job.AudioUUID.String()).Msg("Error processing audio file")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -47,9 +47,9 @@ func StartAudioWorker(ctx context.Context) {
|
|||
func EnqueueAudioJob(job AudioJob) {
|
||||
select {
|
||||
case audioJobChannel <- job:
|
||||
log.Printf("Enqueued audio job for UUID: %s", job.AudioUUID)
|
||||
log.Info().Str("uuid", job.AudioUUID.String()).Msg("Enqueued audio job")
|
||||
default:
|
||||
log.Printf("Audio job channel is full, dropping job for UUID: %s", job.AudioUUID)
|
||||
log.Warn().Str("uuid", job.AudioUUID.String()).Msg("Audio job channel is full, dropping job")
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -76,10 +76,10 @@ func normalizeAudio(audioUUID uuid.UUID) error {
|
|||
source := userfile.AudioFileContentPathRaw(audioUUID.String())
|
||||
_, err := os.Stat(source)
|
||||
if errors.Is(err, os.ErrNotExist) {
|
||||
log.Printf("%s doesn't exist, skipping normalization", source)
|
||||
log.Warn().Str("source", source).Msg("file doesn't exist, skipping normalization")
|
||||
return nil
|
||||
}
|
||||
log.Printf("Normalizing %s", source)
|
||||
log.Info().Str("sourcce", source).Msg("Normalizing")
|
||||
destination := userfile.AudioFileContentPathNormalized(audioUUID.String())
|
||||
// Use "ffmpeg" directly, assuming it's in the system PATH
|
||||
cmd := exec.Command("ffmpeg", "-i", source, "-filter:a", "loudnorm", destination)
|
||||
|
|
@ -92,7 +92,7 @@ func normalizeAudio(audioUUID uuid.UUID) error {
|
|||
if err != nil {
|
||||
return fmt.Errorf("failed to update database for normalized audio %s: %v", audioUUID, err)
|
||||
}
|
||||
log.Printf("Normalized audio to %s", destination)
|
||||
log.Info().Str("destination", destination).Msg("Normalized audio")
|
||||
return nil
|
||||
}
|
||||
|
||||
|
|
@ -100,22 +100,22 @@ func transcodeToOgg(audioUUID uuid.UUID) error {
|
|||
source := userfile.AudioFileContentPathNormalized(audioUUID.String())
|
||||
_, err := os.Stat(source)
|
||||
if errors.Is(err, os.ErrNotExist) {
|
||||
log.Printf("%s doesn't exist, skipping OGG transcoding", source)
|
||||
log.Warn().Str("source", source).Msg("file doesn't exist, skipping OGG transcoding")
|
||||
return nil
|
||||
}
|
||||
log.Printf("Transcoding %s to ogg", source)
|
||||
log.Info().Str("source", source).Msg("Transcoding to ogg")
|
||||
destination := userfile.AudioFileContentPathOgg(audioUUID.String())
|
||||
// Use "ffmpeg" directly, assuming it's in the system PATH
|
||||
cmd := exec.Command("ffmpeg", "-i", source, "-vn", "-acodec", "libvorbis", destination)
|
||||
out, err := cmd.CombinedOutput()
|
||||
if err != nil {
|
||||
log.Printf("FFmpeg output for OGG transcoding: %s", out)
|
||||
log.Error().Err(err).Bytes("out", out).Msg("FFmpeg output for OGG transcoding")
|
||||
return fmt.Errorf("ffmpeg OGG transcoding failed: %v", err)
|
||||
}
|
||||
err = db.NoteAudioTranscodedToOgg(audioUUID.String())
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to update database for OGG transcoded audio %s: %v", audioUUID, err)
|
||||
}
|
||||
log.Printf("Transcoded audio to %s", destination)
|
||||
log.Info().Str("destination", destination).Msg("Transcoded audio")
|
||||
return nil
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ import (
|
|||
"os"
|
||||
|
||||
"github.com/Gleipnir-Technology/nidus-sync/db"
|
||||
"github.com/Gleipnir-Technology/nidus-sync/db/models"
|
||||
"github.com/Gleipnir-Technology/nidus-sync/label-studio"
|
||||
"github.com/Gleipnir-Technology/nidus-sync/minio"
|
||||
"github.com/Gleipnir-Technology/nidus-sync/userfile"
|
||||
|
|
@ -127,7 +128,7 @@ func processLabelTask(ctx context.Context, minioClient *minio.Client, minioBucke
|
|||
return nil
|
||||
}
|
||||
|
||||
func createTask(client *labelstudio.Client, project *labelstudio.Project, minioClient *minio.Client, bucket string, customer string, note *db.NoteAudio) error {
|
||||
func createTask(client *labelstudio.Client, project *labelstudio.Project, minioClient *minio.Client, bucket string, customer string, note *models.NoteAudio) error {
|
||||
audioRef := fmt.Sprintf("s3://%s/%s-normalized.m4a", bucket, note.UUID)
|
||||
audioFile := fmt.Sprintf("%s/%s-normalized.m4a", userfile.UserFilesDirectory, note.UUID)
|
||||
uploadPath := fmt.Sprintf("%s-normalized.m4a", note.UUID)
|
||||
|
|
@ -140,9 +141,9 @@ func createTask(client *labelstudio.Client, project *labelstudio.Project, minioC
|
|||
}
|
||||
var transcription string = ""
|
||||
//if note.Transcription.IsValue() {
|
||||
//transcription = note.Transcription.MustGet()
|
||||
//transcription = note.Transcription.MustGet()
|
||||
//}
|
||||
transcription = note.Transcription
|
||||
transcription = note.Transcription.GetOr("")
|
||||
simpleTasks := []map[string]interface{}{
|
||||
{
|
||||
"data": map[string]string{
|
||||
|
|
@ -184,7 +185,7 @@ func findLabelStudioProject(client *labelstudio.Client, title string) (*labelstu
|
|||
return nil, fmt.Errorf("No such project '%s'", title)
|
||||
}
|
||||
|
||||
func findMatchingTask(client *labelstudio.Client, project *labelstudio.Project, customer string, note *db.NoteAudio) (*labelstudio.Task, error) {
|
||||
func findMatchingTask(client *labelstudio.Client, project *labelstudio.Project, customer string, note *models.NoteAudio) (*labelstudio.Task, error) {
|
||||
/*meta := map[string]string{
|
||||
"customer": customer,
|
||||
"note_uuid": note.UUID,
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue