2024-08-01 01:01:08 -04:00
|
|
|
package calls
|
|
|
|
|
|
|
|
import (
|
2024-11-17 21:46:10 -05:00
|
|
|
"context"
|
2024-08-11 13:46:43 -04:00
|
|
|
"fmt"
|
|
|
|
"time"
|
|
|
|
|
|
|
|
"dynatron.me/x/stillbox/internal/audio"
|
2024-11-03 07:19:03 -05:00
|
|
|
"dynatron.me/x/stillbox/pkg/auth"
|
2024-11-17 21:46:10 -05:00
|
|
|
"dynatron.me/x/stillbox/pkg/database"
|
2024-08-04 08:41:35 -04:00
|
|
|
"dynatron.me/x/stillbox/pkg/pb"
|
2024-11-03 07:58:41 -05:00
|
|
|
"dynatron.me/x/stillbox/pkg/talkgroups"
|
2024-08-01 01:01:08 -04:00
|
|
|
|
2024-11-06 20:47:10 -05:00
|
|
|
"github.com/google/uuid"
|
2024-08-11 13:46:43 -04:00
|
|
|
"google.golang.org/protobuf/types/known/timestamppb"
|
2024-08-01 01:01:08 -04:00
|
|
|
)
|
|
|
|
|
2024-08-11 13:46:43 -04:00
|
|
|
type CallDuration time.Duration
|
|
|
|
|
|
|
|
func (d CallDuration) Duration() time.Duration {
|
|
|
|
return time.Duration(d)
|
|
|
|
}
|
|
|
|
|
2024-11-06 20:55:48 -05:00
|
|
|
func (d CallDuration) MsInt32Ptr() *int32 {
|
2024-08-11 13:46:43 -04:00
|
|
|
if time.Duration(d) == 0 {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
i := int32(time.Duration(d).Milliseconds())
|
|
|
|
return &i
|
|
|
|
}
|
|
|
|
|
|
|
|
func (d CallDuration) Seconds() int32 {
|
|
|
|
return int32(time.Duration(d).Seconds())
|
|
|
|
}
|
|
|
|
|
2024-08-01 01:01:08 -04:00
|
|
|
type Call struct {
|
2024-11-18 14:27:12 -05:00
|
|
|
ID uuid.UUID `form:"-"`
|
|
|
|
Audio []byte `form:"audio" filenameField:"AudioName"`
|
|
|
|
AudioName string `form:"audioName"`
|
|
|
|
AudioType string `form:"audioType"`
|
|
|
|
Duration CallDuration `form:"-"`
|
|
|
|
DateTime time.Time `form:"dateTime"`
|
|
|
|
Frequencies []int `form:"frequencies"`
|
|
|
|
Frequency int `form:"frequency"`
|
|
|
|
Patches []int `form:"patches"`
|
|
|
|
Source int `form:"source"`
|
|
|
|
Sources []int `form:"sources"`
|
|
|
|
System int `form:"system"`
|
|
|
|
Submitter *auth.UserID `form:"-"`
|
|
|
|
SystemLabel string `form:"systemLabel"`
|
|
|
|
Talkgroup int `form:"talkgroup"`
|
|
|
|
TalkgroupGroup *string `form:"talkgroupGroup"`
|
|
|
|
TalkgroupLabel *string `form:"talkgroupLabel"`
|
|
|
|
TGAlphaTag *string `form:"talkgroupTag"` // not 1:1
|
|
|
|
|
|
|
|
shouldStore bool `form:"-"`
|
2024-08-18 08:44:44 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
func (c *Call) String() string {
|
|
|
|
return fmt.Sprintf("%s to %d from %d", c.AudioName, c.Talkgroup, c.Source)
|
2024-08-01 01:01:08 -04:00
|
|
|
}
|
2024-08-04 08:41:35 -04:00
|
|
|
|
2024-08-18 08:44:44 -04:00
|
|
|
func (c *Call) ShouldStore() bool {
|
|
|
|
return c.shouldStore
|
|
|
|
}
|
|
|
|
|
|
|
|
func Make(call *Call, dontStore bool) (*Call, error) {
|
2024-08-14 09:32:53 -04:00
|
|
|
err := call.computeLength()
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2024-08-18 08:44:44 -04:00
|
|
|
call.shouldStore = dontStore
|
2024-11-06 20:47:10 -05:00
|
|
|
call.ID = uuid.New()
|
2024-08-18 08:44:44 -04:00
|
|
|
|
2024-08-14 09:32:53 -04:00
|
|
|
return call, nil
|
|
|
|
}
|
|
|
|
|
2024-08-04 08:41:35 -04:00
|
|
|
func toInt64Slice(s []int) []int64 {
|
|
|
|
n := make([]int64, len(s))
|
|
|
|
for i := range s {
|
|
|
|
n[i] = int64(s[i])
|
|
|
|
}
|
|
|
|
|
|
|
|
return n
|
|
|
|
}
|
|
|
|
|
|
|
|
func toInt32Slice(s []int) []int32 {
|
|
|
|
n := make([]int32, len(s))
|
|
|
|
for i := range s {
|
|
|
|
n[i] = int32(s[i])
|
|
|
|
}
|
|
|
|
|
|
|
|
return n
|
|
|
|
}
|
|
|
|
|
|
|
|
func (c *Call) ToPB() *pb.Call {
|
|
|
|
return &pb.Call{
|
2024-11-06 20:47:10 -05:00
|
|
|
Id: c.ID.String(),
|
2024-08-04 08:41:35 -04:00
|
|
|
AudioName: c.AudioName,
|
|
|
|
AudioType: c.AudioType,
|
|
|
|
DateTime: timestamppb.New(c.DateTime),
|
|
|
|
System: int32(c.System),
|
|
|
|
Talkgroup: int32(c.Talkgroup),
|
|
|
|
Source: int32(c.Source),
|
|
|
|
Frequency: int64(c.Frequency),
|
|
|
|
Frequencies: toInt64Slice(c.Frequencies),
|
|
|
|
Patches: toInt32Slice(c.Patches),
|
|
|
|
Sources: toInt32Slice(c.Sources),
|
2024-11-06 20:55:48 -05:00
|
|
|
Duration: c.Duration.MsInt32Ptr(),
|
2024-08-04 08:41:35 -04:00
|
|
|
Audio: c.Audio,
|
|
|
|
}
|
|
|
|
}
|
2024-08-11 13:46:43 -04:00
|
|
|
|
2024-11-17 21:46:10 -05:00
|
|
|
func (c *Call) LearnTG(ctx context.Context, db database.Store) (learnedId int, err error) {
|
|
|
|
err = db.AddTalkgroupWithLearnedFlag(ctx, int32(c.System), int32(c.Talkgroup))
|
|
|
|
if err != nil {
|
|
|
|
return 0, fmt.Errorf("addTalkgroupWithLearnedFlag: %w", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
return db.AddLearnedTalkgroup(ctx, database.AddLearnedTalkgroupParams{
|
|
|
|
SystemID: c.System,
|
|
|
|
TGID: c.Talkgroup,
|
|
|
|
Name: c.TalkgroupLabel,
|
|
|
|
AlphaTag: c.TGAlphaTag,
|
|
|
|
TGGroup: c.TalkgroupGroup,
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2024-08-14 09:32:53 -04:00
|
|
|
func (c *Call) computeLength() (err error) {
|
2024-08-11 13:46:43 -04:00
|
|
|
var td time.Duration
|
|
|
|
|
|
|
|
switch c.AudioType {
|
|
|
|
case "audio/mpeg":
|
|
|
|
td, err = audio.MP3Duration(c.Audio)
|
|
|
|
if err != nil {
|
|
|
|
return fmt.Errorf("mp3: %w", err)
|
|
|
|
}
|
|
|
|
case "audio/wav":
|
|
|
|
td, err = audio.WAVDuration(c.Audio)
|
|
|
|
if err != nil {
|
|
|
|
return fmt.Errorf("wav: %w", err)
|
|
|
|
}
|
|
|
|
default:
|
|
|
|
return fmt.Errorf("length not implemented for mime type %s", c.AudioType)
|
|
|
|
}
|
|
|
|
|
|
|
|
c.Duration = CallDuration(td)
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
2024-11-03 07:58:41 -05:00
|
|
|
|
2024-11-03 08:09:49 -05:00
|
|
|
func (c *Call) TalkgroupTuple() talkgroups.ID {
|
2024-11-03 07:58:41 -05:00
|
|
|
return talkgroups.TG(c.System, c.Talkgroup)
|
|
|
|
}
|