Files
go-flaresolverr/flaresolverr.go
T

222 lines
4.9 KiB
Go

package flaresolverr
import (
"encoding/json"
"fmt"
"net/http"
"strings"
"time"
"resty.dev/v3"
)
type instanceKey struct {
url string
sessionID string
proxy string
}
type instanceOptions struct {
sessionID string
proxy string
}
type InstanceOption func(*instanceOptions)
func WithSessionID(sessionID string) InstanceOption {
return func(opts *instanceOptions) {
opts.sessionID = sessionID
}
}
func WithProxy(proxy string) InstanceOption {
return func(opts *instanceOptions) {
opts.proxy = proxy
}
}
var instances map[instanceKey]*Flaresolverr = make(map[instanceKey]*Flaresolverr)
type Flaresolverr struct {
url string
v1Url string
sessionID string
proxy string
}
func GetInstance(flaresolverrURL string, opts ...InstanceOption) (*Flaresolverr, error) {
config := instanceOptions{}
for _, opt := range opts {
if opt != nil {
opt(&config)
}
}
key := instanceKey{
url: flaresolverrURL,
sessionID: config.sessionID,
proxy: config.proxy,
}
if instance, ok := instances[key]; ok {
return instance, nil
}
flareSolverr := &Flaresolverr{
url: flaresolverrURL,
v1Url: strings.TrimSuffix(flaresolverrURL, "/") + "/v1",
sessionID: config.sessionID,
proxy: config.proxy,
}
instances[key] = flareSolverr
return flareSolverr, nil
}
func (f *Flaresolverr) requestV1(req *V1RequestBase) (*V1ResponseBase, error) {
resp, err := NewRestyClient().R().SetBody(req).Post(f.v1Url)
if err != nil {
return nil, err
}
if resp.StatusCode() != 200 {
return nil, fmt.Errorf("request failed: status code: %v", resp.StatusCode())
}
var res V1ResponseBase
err = json.Unmarshal(resp.Bytes(), &res)
if err != nil {
return nil, err
}
if res.Solution != nil {
if res.Solution.RawCookies != nil {
for _, cookie := range res.Solution.RawCookies {
sec := int64(cookie.Expires)
nsec := int64((cookie.Expires - float64(sec)) * 1e9)
res.Solution.Cookies = append(res.Solution.Cookies, &http.Cookie{
Name: cookie.Name,
Value: cookie.Value,
Expires: time.Unix(sec, nsec),
Domain: cookie.Domain,
Path: cookie.Path,
Secure: cookie.Secure,
HttpOnly: cookie.HttpOnly,
})
}
}
if res.Solution.RawResponse != nil {
switch v := res.Solution.RawResponse.(type) {
case string:
res.Solution.Response = res.Solution.RawResponse.(string)
case map[string]any:
jsonBytes, err := json.Marshal(v)
if err != nil {
return nil, err
}
res.Solution.Response = string(jsonBytes)
}
}
}
return &res, nil
}
func (f *Flaresolverr) GetV1(reqURL string, req *V1RequestBase) (*V1ResponseBase, error) {
if req == nil {
req = &V1RequestBase{
MaxTimeout: 60000, // 60 秒
}
}
req.Cmd = "request.get"
req.URL = reqURL
req.Session = f.sessionID
if f.proxy != "" {
req.Proxy.Url = f.proxy
}
return f.requestV1(req)
}
func (f *Flaresolverr) PostV1(reqURL string, req *V1RequestBase) (*V1ResponseBase, error) {
if req == nil {
req = &V1RequestBase{
MaxTimeout: 60000, // 60 秒
}
}
req.Cmd = "request.post"
req.URL = reqURL
req.Session = f.sessionID
if f.proxy != "" {
req.Proxy.Url = f.proxy
}
return f.requestV1(req)
}
func (f *Flaresolverr) CreateSessionV1(sessionID string) (*V1ResponseBase, error) {
req := &V1RequestBase{
Session: sessionID,
Cmd: "sessions.create",
}
if f.proxy != "" {
req.Proxy.Url = f.proxy
}
return f.requestV1(req)
}
func (f *Flaresolverr) DestroySessionV1(sessionID string) (*V1ResponseBase, error) {
req := &V1RequestBase{
Session: sessionID,
Cmd: "sessions.destroy",
}
return f.requestV1(req)
}
func (f *Flaresolverr) ListSessionsV1(sessionID string) (*V1ResponseBase, error) {
req := &V1RequestBase{
Session: sessionID,
Cmd: "sessions.list",
}
return f.requestV1(req)
}
func ConvertCookiesToStr(cookies []*http.Cookie) string {
c := []string{}
for _, cookie := range cookies {
c = append(c, fmt.Sprintf("%s=%s", cookie.Name, cookie.Value))
}
return strings.Join(c, "; ")
}
func (f *Flaresolverr) simulateRequest(method string, url string, ua string, cookies []*http.Cookie, body any) (*resty.Response, error) {
var resp *resty.Response
var err error
c := NewRestyClient()
if f.proxy != "" {
c = c.SetProxy(f.proxy)
} else {
c.RemoveProxy()
}
req := c.R().
SetHeader("User-Agent", ua).
SetHeader("Cookie", ConvertCookiesToStr(cookies)).
SetHeader("Accept-Encoding", "gzip, deflate")
switch strings.ToUpper(method) {
case "GET":
resp, err = req.Get(url)
case "POST":
resp, err = req.SetBody(body).Post(url)
}
if err != nil {
return nil, err
}
return resp, nil
}
func (f *Flaresolverr) SimulateGet(url string, ua string, cookies []*http.Cookie) (*resty.Response, error) {
return f.simulateRequest("GET", url, ua, cookies, nil)
}
func (f *Flaresolverr) SimulatePost(url string, ua string, cookies []*http.Cookie, body any) (*resty.Response, error) {
return f.simulateRequest("POST", url, ua, cookies, body)
}