-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathpex_search.go
252 lines (202 loc) · 6.89 KB
/
pex_search.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
// Copyright 2020 Pexeso Inc. All rights reserved.
package pex
// #include <pex/sdk/lock.h>
// #include <pex/sdk/client.h>
// #include <pex/sdk/search.h>
// #include <stdlib.h>
import "C"
import (
"encoding/json"
"fmt"
"unsafe"
)
// PexSearchType can optionally be specified in the PexSearchRequest and will
// allow to retrieve results that are more relevant to the given use-case.
type PexSearchType C.Pex_CheckSearchType
const (
// IdentifyMusic is a type of PexSearch that will return results that will
// help identify the music in the provided media file.
IdentifyMusic = C.Pex_CheckSearchType_IdentifyMusic
// FindMatches is a type of PexSearch that will return all assets that
// matched against the given media file.
FindMatches = C.Pex_CheckSearchType_FindMatches
)
// Holds all data necessary to perform a pex search. A search can only be
// performed using a fingerprint, but additional parameters may be supported in
// the future.
type PexSearchRequest struct {
// A fingerprint obtained by calling either NewFingerprintFromFile
// or NewFingerprintFromBuffer. This field is required.
Fingerprint *Fingerprint
// Type is optional and when specified will allow to retrieve results that
// are more relevant to the given use-case.
Type PexSearchType
}
// This object is returned from PexSearchFuture.Get upon successful
// completion.
type PexSearchResult struct {
// IDs that uniquely identify a particular search. Can be used for diagnostics.
LookupIDs []string `json:"lookup_ids"`
// The assets which the query matched against.
Matches []*PexSearchMatch `json:"matches"`
QueryFileDurationSeconds float32 `json:"query_file_duration_seconds"`
}
type PexSearchAsset struct {
ID string `json:"id"`
// The title of the asset.
Title string `json:"title"`
// The artist who contributed to the asset.
Artist string `json:"artist"`
// International Standard Recording Code.
ISRC string `json:"isrc"`
// The label that owns the asset (e.g. Sony Music Entertainment).
Label string `json:"label"`
// The total duration of the asset in seconds.
DurationSeconds float32 `json:"duration_seconds"`
Barcode string `json:"barcode"`
Distributor string `json:"distributor"`
Subtitle string `json:"subtitle"`
AlbumName string `json:"album_name"`
ReleaseDate struct {
Year int `json:"year"`
Month int `json:"month"`
Day int `json:"day"`
} `json:"release_date"`
DSP []*DSP `json:"dsp"`
}
// PexSearchMatch contains detailed information about the match,
// including information about the matched asset, and the matching
// segments.
type PexSearchMatch struct {
// The asset whose fingerprint matches the query.
Asset *PexSearchAsset `json:"asset"`
// The matching time segments on the query and asset respectively.
MatchDetails MatchDetails `json:"match_details"`
}
// PexSearchFuture object is returned by the PexSearchClient.StartSearch
// function and is used to retrieve a search result.
type PexSearchFuture struct {
client *PexSearchClient
LookupIDs []string
Type PexSearchType
}
// Get blocks until the search result is ready and then returns it. It
// also releases all the allocated resources, so it will return an
// error when called multiple times.
func (x *PexSearchFuture) Get() (*PexSearchResult, error) {
return x.client.CheckSearch(x.LookupIDs, x.Type)
}
// PexSearchClient serves as an entry point to all operations that
// communicate with Pex backend services. It
// automatically handles the connection and authentication with the
// service.
type PexSearchClient struct {
fingerprinter
c *C.Pex_Client
}
func NewPexSearchClient(clientID, clientSecret string) (*PexSearchClient, error) {
cClient, err := newClient(C.Pex_PEX_SEARCH, clientID, clientSecret)
if err != nil {
return nil, err
}
return &PexSearchClient{
c: cClient,
}, nil
}
// Close closes all connections to the backend service and releases
// the memory manually allocated by the core library.
func (x *PexSearchClient) Close() error {
return closeClient(&x.c)
}
func (x *PexSearchClient) getCClient() *C.Pex_Client {
return x.c
}
// StartSearch starts a Pex search. This operation does not block until
// the search is finished, it does however perform a network operation
// to initiate the search on the backend service.
func (x *PexSearchClient) StartSearch(req *PexSearchRequest) (*PexSearchFuture, error) {
C.Pex_Lock()
defer C.Pex_Unlock()
cStatus := C.Pex_Status_New()
if cStatus == nil {
panic("out of memory")
}
defer C.Pex_Status_Delete(&cStatus)
cRequest := C.Pex_StartSearchRequest_New()
if cRequest == nil {
panic("out of memory")
}
defer C.Pex_StartSearchRequest_Delete(&cRequest)
cResult := C.Pex_StartSearchResult_New()
if cResult == nil {
panic("out of memory")
}
defer C.Pex_StartSearchResult_Delete(&cResult)
cBuffer := C.Pex_Buffer_New()
if cBuffer == nil {
panic("out of memory")
}
defer C.Pex_Buffer_Delete(&cBuffer)
ftData := unsafe.Pointer(&req.Fingerprint.b[0])
ftSize := C.size_t(len(req.Fingerprint.b))
C.Pex_Buffer_Set(cBuffer, ftData, ftSize)
C.Pex_StartSearchRequest_SetFingerprint(cRequest, cBuffer, cStatus)
if err := statusToError(cStatus); err != nil {
return nil, err
}
C.Pex_StartSearch(x.c, cRequest, cResult, cStatus)
if err := statusToError(cStatus); err != nil {
return nil, err
}
var cLookupIDPos C.size_t = 0
var lookupIDs []string
var cLookupID *C.char
for C.Pex_StartSearchResult_NextLookupID(cResult, &cLookupIDPos, &cLookupID) {
lookupIDs = append(lookupIDs, C.GoString(cLookupID))
}
return &PexSearchFuture{
client: x,
LookupIDs: lookupIDs,
Type: req.Type,
}, nil
}
func (x *PexSearchClient) CheckSearch(lookupIDs []string, searchType PexSearchType) (*PexSearchResult, error) {
C.Pex_Lock()
defer C.Pex_Unlock()
cStatus := C.Pex_Status_New()
if cStatus == nil {
panic("out of memory")
}
defer C.Pex_Status_Delete(&cStatus)
cRequest := C.Pex_CheckSearchRequest_New()
if cRequest == nil {
panic("out of memory")
}
defer C.Pex_CheckSearchRequest_Delete(&cRequest)
cResult := C.Pex_CheckSearchResult_New()
if cResult == nil {
panic("out of memory")
}
defer C.Pex_CheckSearchResult_Delete(&cResult)
for _, lookupID := range lookupIDs {
cLookupID := C.CString(lookupID)
defer C.free(unsafe.Pointer(cLookupID))
C.Pex_CheckSearchRequest_AddLookupID(cRequest, cLookupID)
}
C.Pex_CheckSearchRequest_SetType(cRequest, C.Pex_CheckSearchType(searchType))
C.Pex_CheckSearch(x.c, cRequest, cResult, cStatus)
if err := statusToError(cStatus); err != nil {
return nil, err
}
return x.processResult(cResult, lookupIDs)
}
func (x *PexSearchClient) processResult(cResult *C.Pex_CheckSearchResult, lookupIDs []string) (*PexSearchResult, error) {
cJSON := C.Pex_CheckSearchResult_GetJSON(cResult)
j := C.GoString(cJSON)
res := new(PexSearchResult)
if err := json.Unmarshal([]byte(j), res); err != nil {
return nil, fmt.Errorf("failed to unmarshal result: %w", err)
}
res.LookupIDs = lookupIDs
return res, nil
}