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
|
package main
import (
"bytes"
"encoding/json"
"fmt"
"io"
"io/ioutil"
"log"
"os"
"net/http"
"regexp"
"strings"
)
func main() {
arg := os.Args[1]
img := make(chan *ImgFetchResult)
f := &DDGImgFetcher{}
go f.Fetch(img, arg)
result := <-img
if result.Error != nil {
log.Fatalln(result.Error)
}
createFile(result.Img, "/tmp/"+arg+"."+result.ContentType)
}
func createFile(content []byte, filePath string) {
file, err := os.Create(filePath)
if err != nil {
log.Fatalln(err)
}
defer file.Close()
_, err = io.Copy(file, bytes.NewReader(content))
if err != nil {
log.Fatalln(err)
}
}
// DDGImgFetcher fetches the image from duckduckgo
type DDGImgFetcher struct {
}
// ImgFetchResult is the representation of the result to fetch the image
// It wraps an error in case something went wrong
type ImgFetchResult struct {
Img []byte
ContentType string
Error error
}
const ddgURL = "https://duckduckgo.com"
// Fetch the image from duckduckgo
// Code inspired from https://github.com/deepanprabhu/duckduckgo-images-api/blob/master/duckduckgo_images_api/api.py
func (f *DDGImgFetcher) Fetch(result chan *ImgFetchResult, title string) {
token, err := fetchToken(title)
if err != nil {
result <- &ImgFetchResult{nil, "", err}
return
}
imgURL, err := fetchImgURL(title, token)
if err != nil {
result <- &ImgFetchResult{nil, "", err}
return
}
img, contentType, err := downloadImg(imgURL)
if err != nil {
result <- &ImgFetchResult{nil, "", err}
return
}
result <- &ImgFetchResult{img, contentType, nil}
}
// fetchToken needed to perform the image search
func fetchToken(title string) (string, error) {
req, err := http.NewRequest("GET", ddgURL, nil)
if err != nil {
return "", fmt.Errorf("Could not build the request for URL %s. Error was %s", ddgURL, err)
}
q := req.URL.Query()
q.Add("q", title)
req.URL.RawQuery = q.Encode()
resp, err := http.Get(req.URL.String())
if err != nil {
return "", fmt.Errorf("Could not fetch the result of %s", req.URL.String())
}
if resp.StatusCode != 200 {
return "", fmt.Errorf("Duckduckgo returns %v status code", resp.StatusCode)
}
defer resp.Body.Close()
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
return "", fmt.Errorf("Could not read the response body from duckduckgo. Error was %s", err)
}
content := string(body)
r := regexp.MustCompile("vqd=([\\d-]+)")
token := strings.ReplaceAll(r.FindString(content), "vqd=", "")
return token, nil
}
func fetchImgURL(title, token string) (string, error) {
url := ddgURL + "/i.js"
req, err := http.NewRequest("GET", url, nil)
if err != nil {
return "", fmt.Errorf("Could not build the request for URL %s. Error was %s", url, err)
}
q := req.URL.Query()
q.Add("l", "wt-wt")
q.Add("o", "json")
q.Add("q", title)
q.Add("vqd", token)
q.Add("f", ",,,")
q.Add("p", "2")
req.URL.RawQuery = q.Encode()
resp, err := http.Get(req.URL.String())
if err != nil {
return "", fmt.Errorf("Could not fetch the result of %s", req.URL.String())
}
if resp.StatusCode != 200 {
return "", fmt.Errorf("Duckduckgo returns %v status code", resp.StatusCode)
}
defer resp.Body.Close()
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
return "", fmt.Errorf("Could not read the response body from duckduckgo. Error was %s", err)
}
var data map[string]interface{}
err = json.Unmarshal(body, &data)
if err != nil {
return "", fmt.Errorf("Could not parse the response body from wikipedia. Error was %s", err.Error())
}
results := data["results"].([]interface{})
firstResult := results[0].(map[string]interface{})
imgURL := firstResult["image"].(string)
return imgURL, nil
}
func downloadImg(imgURL string) ([]byte, string, error) {
resp, err := http.Get(imgURL)
if err != nil {
return nil, "", fmt.Errorf("Could not download the image from %s", imgURL)
}
if resp.StatusCode != 200 {
return nil, "", fmt.Errorf("Could not download the image from %s. Status comde was %v", imgURL, resp.StatusCode)
}
defer resp.Body.Close()
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
return nil, "", fmt.Errorf("Could not read the response body from %s. Error was %s", imgURL, err.Error())
}
contentType := strings.ToLower(strings.ReplaceAll(resp.Header.Get("Content-Type"), "image/", ""))
return body, contentType, nil
}
|