Skip to content

Commit 6455059

Browse files
authored
Merge pull request #21 from mutablelogic/ffmpeg61
Added chromaprint client
2 parents 35a34fe + e41a36d commit 6455059

File tree

10 files changed

+105
-209
lines changed

10 files changed

+105
-209
lines changed

Makefile

+1
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ test: go-dep
4444
@echo Test
4545
@${GO} mod tidy
4646
@${GO} test ./sys/ffmpeg61
47+
@${GO} test ./sys/chromaprint
4748
@${GO} test ./pkg/...
4849
@${GO} test .
4950

decoder.go

+3-1
Original file line numberDiff line numberDiff line change
@@ -319,7 +319,9 @@ FOR_LOOP:
319319

320320
// Flush the decoders
321321
for _, decoder := range d.decoders {
322-
if err := decoder.decode(nil, demuxfn, framefn); err != nil {
322+
if err := decoder.decode(nil, demuxfn, framefn); errors.Is(err, io.EOF) {
323+
// no-op
324+
} else if err != nil {
323325
return err
324326
}
325327
}

go.mod

+1
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ require (
88
github.com/alecthomas/kong v0.9.0
99
github.com/djthorpe/go-errors v1.0.3
1010
github.com/djthorpe/go-tablewriter v0.0.8
11+
github.com/mutablelogic/go-client v1.0.8
1112
github.com/stretchr/testify v1.9.0
1213
)
1314

manager.go

+14-6
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package media
22

33
import (
4+
"fmt"
45
"io"
56
"runtime"
67

@@ -311,13 +312,13 @@ func (manager *manager) Write(w io.Writer, format Format, metadata []Metadata, p
311312
// Return version information for the media manager as a set of metadata
312313
func (manager *manager) Version() []Metadata {
313314
metadata := []Metadata{
314-
newMetadata("libavcodec_version", ff.AVCodec_version()),
315-
newMetadata("libavformat_version", ff.AVFormat_version()),
316-
newMetadata("libavutil_version", ff.AVUtil_version()),
317-
newMetadata("libavdevice_version", ff.AVDevice_version()),
315+
newMetadata("libavcodec_version", ffVersionAsString(ff.AVCodec_version())),
316+
newMetadata("libavformat_version", ffVersionAsString(ff.AVFormat_version())),
317+
newMetadata("libavutil_version", ffVersionAsString(ff.AVUtil_version())),
318+
newMetadata("libavdevice_version", ffVersionAsString(ff.AVDevice_version())),
318319
// newMetadata("libavfilter_version", ff.AVFilter_version()),
319-
newMetadata("libswscale_version", ff.SWScale_version()),
320-
newMetadata("libswresample_version", ff.SWResample_version()),
320+
newMetadata("libswscale_version", ffVersionAsString(ff.SWScale_version())),
321+
newMetadata("libswresample_version", ffVersionAsString(ff.SWResample_version())),
321322
}
322323
if version.GitSource != "" {
323324
metadata = append(metadata, newMetadata("git_source", version.GitSource))
@@ -352,3 +353,10 @@ func (manager *manager) Warningf(f string, args ...any) {
352353
func (manager *manager) Infof(f string, args ...any) {
353354
ff.AVUtil_log(nil, ff.AV_LOG_INFO, f, args...)
354355
}
356+
357+
////////////////////////////////////////////////////////////////////////////
358+
// PRIVATE METHODS
359+
360+
func ffVersionAsString(version uint) string {
361+
return fmt.Sprintf("%d.%d.%d", version&0xFF0000>>16, version&0xFF00>>8, version&0xFF)
362+
}

metadata.go

+2-1
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,8 @@ type metadata struct {
1515
}
1616

1717
const (
18-
MetaArtwork = "artwork"
18+
MetaArtwork = "artwork" // Metadata key for artwork, sets the value as []byte
19+
MetaDuration = "duration" // Metadata key for duration, sets the value as float64 (seconds)
1920
)
2021

2122
////////////////////////////////////////////////////////////////////////////////

pkg/chromaprint/client.go

+32-171
Original file line numberDiff line numberDiff line change
@@ -1,225 +1,86 @@
11
package chromaprint
22

33
import (
4-
"encoding/json"
5-
"errors"
64
"fmt"
7-
"io/ioutil"
8-
"mime"
9-
"net/http"
105
"net/url"
11-
"os"
12-
"path/filepath"
136
"time"
147

8+
// Packages
9+
"github.com/mutablelogic/go-client"
10+
1511
// Namespace imports
1612
. "github.com/djthorpe/go-errors"
1713
)
1814

19-
////////////////////////////////////////////////////////////////////////////////
15+
///////////////////////////////////////////////////////////////////////////////
2016
// TYPES
2117

22-
type Config struct {
23-
Key string `yaml:"key"` // AcuostId Web Service Key
24-
Timeout time.Duration `yaml:"timeout"` // AcoustId Client timeout
25-
Rate uint `yaml:"rate"` // Maximum requests per second
26-
Base string `yaml:"base"` // Base URL
27-
}
28-
2918
type Client struct {
30-
Config
31-
*http.Client
32-
*url.URL
33-
last time.Time
19+
*client.Client
20+
key string
3421
}
3522

3623
////////////////////////////////////////////////////////////////////////////////
3724
// GLOBALS
3825

3926
const (
4027
// Register a clientId: https://acoustid.org/login
41-
defaultClientId = "-YectPtndAM"
42-
43-
// Timeout requests after 15 seconds
44-
defaultTimeout = 15 * time.Second
28+
defaultApiKey = "-YectPtndAM"
4529

4630
// The API endpoint
47-
baseUrl = "https://api.acoustid.org/v2"
31+
endPoint = "https://api.acoustid.org/v2"
4832

4933
// defaultQps rate limits number of requests per second
5034
defaultQps = 3
5135
)
5236

53-
var (
54-
ErrQueryRateExceeded = errors.New("query rate exceeded")
55-
)
37+
///////////////////////////////////////////////////////////////////////////////
38+
// LIFECYCLE
5639

57-
var (
58-
DefaultConfig = Config{
59-
Key: defaultClientId,
60-
Timeout: defaultTimeout,
61-
Rate: defaultQps,
62-
Base: baseUrl,
40+
// Create a new client
41+
func NewClient(ApiKey string, opts ...client.ClientOpt) (*Client, error) {
42+
// Check for missing API key
43+
if ApiKey == "" {
44+
ApiKey = defaultApiKey
6345
}
64-
)
65-
66-
////////////////////////////////////////////////////////////////////////////////
67-
// NEW
68-
69-
func NewClient(key string) *Client {
70-
config := DefaultConfig
71-
if key != "" {
72-
config.Key = os.ExpandEnv(key)
73-
}
74-
if client, err := NewClientWithConfig(config); err != nil {
75-
return nil
76-
} else {
77-
return client
78-
}
79-
}
80-
81-
func NewClientWithConfig(cfg Config) (*Client, error) {
82-
client := new(Client)
83-
client.Config = cfg
8446

85-
// Set parameters
86-
if client.Config.Timeout == 0 {
87-
client.Config.Timeout = DefaultConfig.Timeout
88-
}
89-
if client.Key == "" {
90-
client.Key = os.ExpandEnv(DefaultConfig.Key)
91-
}
92-
if client.Base == "" {
93-
client.Base = DefaultConfig.Base
94-
}
95-
if client.Rate == 0 {
96-
client.Rate = DefaultConfig.Rate
97-
}
98-
99-
// Create HTTP client
100-
client.Client = &http.Client{
101-
Timeout: client.Config.Timeout,
102-
}
103-
url, err := url.Parse(client.Base)
47+
// Create client
48+
opts = append(opts, client.OptEndpoint(endPoint))
49+
client, err := client.New(opts...)
10450
if err != nil {
10551
return nil, err
106-
} else {
107-
client.URL = url
10852
}
109-
// Fudge key into URL
110-
v := url.Query()
111-
v.Set("client", client.Key)
112-
url.RawQuery = v.Encode()
11353

114-
// Return success
115-
return client, nil
116-
}
117-
118-
////////////////////////////////////////////////////////////////////////////////
119-
// STRINGIFY
120-
121-
func (client *Client) String() string {
122-
str := "<chromaprint"
123-
if u := client.URL; u != nil {
124-
str += fmt.Sprintf(" url=%q", u.String())
125-
}
126-
if rate := client.Rate; rate > 0 {
127-
str += fmt.Sprintf(" rate=%dops/s", rate)
128-
}
129-
if timeout := client.Client.Timeout; timeout > 0 {
130-
str += fmt.Sprintf(" timeout=%v", timeout)
131-
}
132-
return str + ">"
54+
// Return the client
55+
return &Client{
56+
Client: client,
57+
key: ApiKey,
58+
}, nil
13359
}
13460

13561
////////////////////////////////////////////////////////////////////////////////
13662
// LOOKUP
13763

138-
func (client *Client) Lookup(fingerprint string, duration time.Duration, flags Meta) ([]*ResponseMatch, error) {
64+
// Lookup a fingerprint with a duration and the metadata that needs to be
65+
// returned
66+
func (c *Client) Lookup(fingerprint string, duration time.Duration, flags Meta) ([]*ResponseMatch, error) {
13967
// Check incoming parameters
14068
if fingerprint == "" || duration == 0 || flags == META_NONE {
14169
return nil, ErrBadParameter.With("Lookup")
14270
}
14371

144-
// Check Qps
145-
if client.last.IsZero() {
146-
if time.Since(client.last) < (time.Second / defaultQps) {
147-
return nil, ErrQueryRateExceeded
148-
}
149-
}
150-
15172
// Set URL parameters
15273
params := url.Values{}
74+
params.Set("client", c.key)
15375
params.Set("fingerprint", fingerprint)
15476
params.Set("duration", fmt.Sprint(duration.Truncate(time.Second).Seconds()))
15577
params.Set("meta", flags.String())
156-
url := client.requestUrl("lookup", params)
157-
if url == nil {
158-
return nil, ErrBadParameter.With("Lookup")
159-
}
16078

161-
//fmt.Println(url.String())
162-
163-
// Perform request
164-
now := time.Now()
165-
req, err := http.NewRequest(http.MethodGet, url.String(), nil)
166-
if err != nil {
79+
// Request -> Response
80+
var response Response
81+
if err := c.Do(nil, &response, client.OptPath("lookup"), client.OptQuery(params)); err != nil {
16782
return nil, err
83+
} else {
84+
return response.Results, nil
16885
}
169-
response, err := client.Client.Do(req)
170-
if err != nil {
171-
return nil, err
172-
}
173-
defer response.Body.Close()
174-
175-
// Read response body
176-
body, err := ioutil.ReadAll(response.Body)
177-
if err != nil {
178-
return nil, err
179-
}
180-
181-
// Decode response body
182-
var r Response
183-
if mimeType, _, err := mime.ParseMediaType(response.Header.Get("Content-type")); err != nil {
184-
return nil, ErrUnexpectedResponse.With(err)
185-
} else if mimeType != "application/json" {
186-
return nil, ErrUnexpectedResponse.With(mimeType)
187-
} else if err := json.Unmarshal(body, &r); err != nil {
188-
return nil, ErrUnexpectedResponse.With(err)
189-
}
190-
191-
// Check for errors
192-
if r.Status != "ok" {
193-
return nil, ErrBadParameter.Withf("%v (code %v)", r.Error.Message, r.Error.Code)
194-
} else if response.StatusCode != http.StatusOK {
195-
return nil, ErrBadParameter.Withf("%v (code %v)", response.Status, response.StatusCode)
196-
}
197-
198-
// Set response time for calculating qps
199-
client.last = now
200-
201-
// Return success
202-
return r.Results, nil
203-
}
204-
205-
////////////////////////////////////////////////////////////////////////////////
206-
// PRIVATE METHODS
207-
208-
func (client *Client) requestUrl(path string, v url.Values) *url.URL {
209-
url, err := url.Parse(client.URL.String())
210-
if err != nil {
211-
return nil
212-
}
213-
// Copy params
214-
params := client.URL.Query()
215-
for k := range v {
216-
params[k] = v[k]
217-
}
218-
url.RawQuery = params.Encode()
219-
220-
// Set path
221-
url.Path = filepath.Join(url.Path, path)
222-
223-
// Return URL
224-
return url
22586
}

0 commit comments

Comments
 (0)