-
Notifications
You must be signed in to change notification settings - Fork 0
/
client.go
153 lines (141 loc) · 3.31 KB
/
client.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
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
package tlapi
import (
"context"
"encoding/json"
"errors"
"fmt"
"io"
"net/http"
"net/http/cookiejar"
"net/url"
"time"
"golang.org/x/net/publicsuffix"
)
// Client is a TL client.
type Client struct {
cl *http.Client
Jar http.CookieJar
Transport http.RoundTripper
}
// New creates a TL client.
func New(opts ...Option) *Client {
cl := &Client{}
for _, o := range opts {
o(cl)
}
if cl.cl == nil {
cl.cl = &http.Client{
Jar: cl.Jar,
Transport: cl.Transport,
}
}
return cl
}
// Do executes a request.
func (cl *Client) Do(ctx context.Context, req *http.Request, result interface{}) error {
if cl.Jar == nil {
return errors.New("must supply cookie jar")
}
req.Header.Set("Content-Type", "application/json")
res, err := cl.cl.Do(req.WithContext(ctx))
if err != nil {
return err
}
defer res.Body.Close()
if res.StatusCode != http.StatusOK {
return fmt.Errorf("invalid http status %d", res.StatusCode)
}
dec := json.NewDecoder(res.Body)
dec.DisallowUnknownFields()
return dec.Decode(result)
}
// Search searches for a query.
func (cl *Client) Search(ctx context.Context, query ...string) (*SearchResponse, error) {
return Search(query...).Do(ctx, cl)
}
// Torrent retrieves a torrent for the id.
func (cl *Client) Torrent(ctx context.Context, id int) ([]byte, error) {
if cl.Jar == nil {
return nil, errors.New("must supply cookie jar")
}
req, err := http.NewRequest("GET", fmt.Sprintf("https://www.torrentleech.org/download/%d/%s", id, "a"), nil)
if err != nil {
return nil, err
}
res, err := cl.cl.Do(req.WithContext(ctx))
if err != nil {
return nil, err
}
defer res.Body.Close()
if res.StatusCode != http.StatusOK {
return nil, fmt.Errorf("invalid http status %d", res.StatusCode)
}
return io.ReadAll(res.Body)
}
// Option is a TL client option.
type Option func(cl *Client)
// WithJar is an option to set the cookie jar used by the TL client.
func WithJar(jar http.CookieJar) Option {
return func(cl *Client) {
cl.Jar = jar
}
}
// WithTransport is a TL client option to set the http transport used by the TL
// client.
func WithTransport(transport http.RoundTripper) Option {
return func(cl *Client) {
cl.Transport = transport
}
}
// WithCreds is a TL client option to set the PHPSESSID, tluid, and tlpass
// cookies used by the TL client.
func WithCreds(sessID, uid, pass string) Option {
return func(cl *Client) {
var err error
if cl.Jar, err = BuildJar(sessID, uid, pass); err != nil {
panic(err)
}
}
}
// BuildJar creates a jar.
func BuildJar(sessID, uid, pass string) (http.CookieJar, error) {
jar, err := cookiejar.New(&cookiejar.Options{
PublicSuffixList: publicsuffix.List,
})
if err != nil {
return nil, err
}
u, err := url.Parse("https://torrentleech.org/")
if err != nil {
return nil, err
}
expires := time.Now().Add(10 * 365 * 24 * time.Hour)
jar.SetCookies(u, []*http.Cookie{
{
Domain: "www.torrentleech.org",
Path: "/",
Name: "PHPSESSID",
Value: sessID,
Expires: expires,
Secure: true,
},
{
Domain: "torrentleech.org",
Path: "/",
Name: "tluid",
Value: uid,
Expires: expires,
HttpOnly: true,
Secure: true,
},
{
Domain: "torrentleech.org",
Path: "/",
Name: "tlpass",
Value: pass,
Expires: expires,
Secure: true,
},
})
return jar, nil
}