Initial commit
This commit is contained in:
commit
50dcef8631
|
@ -0,0 +1,28 @@
|
||||||
|
package goboru
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io"
|
||||||
|
"net/http"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Module interface {
|
||||||
|
Query([]string) ([]Media, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
type Media struct {
|
||||||
|
Source string
|
||||||
|
MD5 string
|
||||||
|
Tags []string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m Media) Content() (rc io.ReadCloser, err error) {
|
||||||
|
client := http.Client{Timeout: 0, Transport: &http.Transport{ResponseHeaderTimeout: 10 * time.Second}}
|
||||||
|
|
||||||
|
var resp *http.Response
|
||||||
|
if resp, err = client.Get(m.Source); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
rc = resp.Body
|
||||||
|
return
|
||||||
|
}
|
|
@ -0,0 +1,173 @@
|
||||||
|
package gelbooru
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"log"
|
||||||
|
"net/http"
|
||||||
|
"net/url"
|
||||||
|
"sort"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
. "git.redxen.eu/caskd/goboru"
|
||||||
|
)
|
||||||
|
|
||||||
|
type gelbooru_API struct {
|
||||||
|
Attributes struct {
|
||||||
|
Limit float64 `json:"limit"`
|
||||||
|
Offset float64 `json:"offset"`
|
||||||
|
Count float64 `json:"count"`
|
||||||
|
} `json:"@attributes"`
|
||||||
|
Posts []struct {
|
||||||
|
Id uint64 `json:"id"`
|
||||||
|
Created_at string `json:"created_at"`
|
||||||
|
Score int64 `json:"score"`
|
||||||
|
Width uint64 `json:"width"`
|
||||||
|
Height uint64 `json:"height"`
|
||||||
|
MD5 string `json:"md5"`
|
||||||
|
Directory string `json:"directory"`
|
||||||
|
Image string `json:"image"`
|
||||||
|
Rating string `json:"rating"`
|
||||||
|
Source string `json:"source"`
|
||||||
|
Change uint64 `json:"change"`
|
||||||
|
Owner string `json:"owner"`
|
||||||
|
Creator_id uint64 `json:"creator_id"`
|
||||||
|
Parent_id uint64 `json:"parent_id"`
|
||||||
|
Sample uint64 `json:"sample"`
|
||||||
|
Preview_height uint64 `json:"preview_height"`
|
||||||
|
Preview_width uint64 `json:"preview_width"`
|
||||||
|
Tags string `json:"tags"`
|
||||||
|
Title string `json:"title"`
|
||||||
|
Has_notes string `json:"has_notes"`
|
||||||
|
Has_comments string `json:"has_comments"`
|
||||||
|
File_url string `json:"file_url"`
|
||||||
|
Preview_url string `json:"preview_url"`
|
||||||
|
Sample_url string `json:"sample_url"`
|
||||||
|
Sample_height uint64 `json:"sample_height"`
|
||||||
|
Sample_width uint64 `json:"sample_width"`
|
||||||
|
Status string `json:"status"`
|
||||||
|
Post_locked uint64 `json:"post_locked"`
|
||||||
|
Has_children string `json:"has_children"`
|
||||||
|
} `json:"post"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type result struct {
|
||||||
|
media []Media
|
||||||
|
err error
|
||||||
|
pid uint
|
||||||
|
}
|
||||||
|
|
||||||
|
func Query(tags []string, j_max uint) (mr []Media, err error) {
|
||||||
|
res_chan := make(chan result)
|
||||||
|
var r_arr []result
|
||||||
|
|
||||||
|
for pid, rpid, ppid := uint(0), uint(0), uint(0); ; {
|
||||||
|
if pid <= 200 { // API only allows to fetch up to 200 pages per query
|
||||||
|
go run_job(tags, pid, res_chan)
|
||||||
|
pid++
|
||||||
|
}
|
||||||
|
|
||||||
|
if pid < j_max {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if rpid < pid {
|
||||||
|
r := <-res_chan
|
||||||
|
rpid++
|
||||||
|
r_arr = append(r_arr, r)
|
||||||
|
}
|
||||||
|
|
||||||
|
if ppid < pid {
|
||||||
|
sort.Slice(r_arr, func(i, j int) bool {
|
||||||
|
return r_arr[i].pid < r_arr[j].pid
|
||||||
|
})
|
||||||
|
|
||||||
|
if c := r_arr[0]; c.pid == ppid {
|
||||||
|
ppid++
|
||||||
|
r_arr = r_arr[1:]
|
||||||
|
if c.err != nil {
|
||||||
|
err = c.err
|
||||||
|
break
|
||||||
|
}
|
||||||
|
if len(c.media) == 0 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
mr = append(mr, c.media...)
|
||||||
|
log.Print("Added ", len(c.media), "/", len(mr), " elements")
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
break // Break when no more pages have been fetched
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func run_job(tags []string, pid uint, res chan result) {
|
||||||
|
r := result{pid: pid}
|
||||||
|
|
||||||
|
var rc io.ReadCloser
|
||||||
|
if rc, r.err = fetch(tags, pid); r.err != nil {
|
||||||
|
res <- r
|
||||||
|
return
|
||||||
|
}
|
||||||
|
defer rc.Close()
|
||||||
|
|
||||||
|
if r.media, r.err = parse(rc); r.err != nil {
|
||||||
|
res <- r
|
||||||
|
return
|
||||||
|
}
|
||||||
|
res <- r
|
||||||
|
}
|
||||||
|
|
||||||
|
func fetch(tags []string, pid uint) (rc io.ReadCloser, err error) {
|
||||||
|
client := http.Client{Timeout: 10 * time.Second}
|
||||||
|
req := &http.Request{
|
||||||
|
URL: &url.URL{
|
||||||
|
Scheme: "https",
|
||||||
|
Host: "gelbooru.com",
|
||||||
|
Path: "/index.php",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
query := req.URL.Query()
|
||||||
|
query.Add("page", "dapi")
|
||||||
|
query.Add("s", "post")
|
||||||
|
query.Add("q", "index")
|
||||||
|
query.Add("json", "1")
|
||||||
|
query.Add("pid", strconv.FormatUint(uint64(pid), 10))
|
||||||
|
query.Add("tags", strings.Join(tags, " "))
|
||||||
|
req.URL.RawQuery = query.Encode()
|
||||||
|
|
||||||
|
resp, err := client.Do(req)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
rc = resp.Body
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func parse(r io.Reader) (m []Media, err error) {
|
||||||
|
var api_resp gelbooru_API
|
||||||
|
|
||||||
|
d := json.NewDecoder(r)
|
||||||
|
if err = d.Decode(&api_resp); err != nil {
|
||||||
|
err = fmt.Errorf("JSON parse: %s", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(api_resp.Posts) == 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, v := range api_resp.Posts {
|
||||||
|
cur := Media{
|
||||||
|
Source: v.File_url,
|
||||||
|
MD5: v.MD5,
|
||||||
|
Tags: strings.Split(v.Tags, " "),
|
||||||
|
}
|
||||||
|
m = append(m, cur)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
Loading…
Reference in New Issue