-
Notifications
You must be signed in to change notification settings - Fork 11
/
name.go
82 lines (71 loc) · 1.78 KB
/
name.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
package imdb
import (
"encoding/json"
"errors"
"fmt"
"io/ioutil"
"net/http"
"regexp"
)
// A Name represents an IMDb name (actor, director, writer, etc.).
type Name struct {
ID string `json:",omitempty"`
URL string `json:",omitempty"`
FullName string `json:",omitempty"`
}
// String formats a Name.
func (n *Name) String() string {
return fmt.Sprintf("IMDb %s: %s", n.ID, n.FullName)
}
var nmRE = regexp.MustCompile(`^nm\d+$`)
const nameURL = "https://www.imdb.com/name/%s"
// NewName gets, parses and returns a Name by its ID.
func NewName(c *http.Client, id string) (*Name, error) {
if !nmRE.MatchString(id) {
return nil, ErrInvalidID
}
resp, err := c.Get(fmt.Sprintf(nameURL, id))
if err != nil {
return nil, err
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
if resp.StatusCode == http.StatusForbidden {
return nil, errors.New("forbidden (e.g. denied by AWS WAF)")
}
return nil, fmt.Errorf("imdb: status not ok: %v", resp.Status)
}
page, err := ioutil.ReadAll(resp.Body)
if err != nil {
return nil, err
}
n := Name{}
if err := n.Parse(page); err != nil {
return nil, err
}
return &n, nil
}
// Regular expressions to parse a Name.
var (
nameIDLinkRE = regexp.MustCompile(`<link rel="canonical" href="https://www.imdb.com/name/(nm\d+)/"`)
)
// Parse parses a Name from its page.
func (n *Name) Parse(page []byte) error {
// ID, URL
s := nameIDLinkRE.FindSubmatch(page)
if s == nil {
return NewErrParse("id")
}
n.ID = string(s[1])
n.URL = fmt.Sprintf(nameURL, n.ID)
s = schemaRE.FindSubmatch(page)
if s == nil {
return NewErrParse("schema")
}
var v schemaJSON
if err := json.Unmarshal(s[1], &v); err != nil {
return NewErrParse(err.Error() + "; schema was: " + string(s[1]))
}
n.FullName = decode(v.Name)
return nil
}