10 "github.com/anacrolix/torrent/metainfo"
11 "github.com/anacrolix/torrent/webtorrent/buffer"
12 "github.com/gorilla/websocket"
13 "github.com/pion/datachannel"
14 "github.com/pion/webrtc/v2"
18 trackerURL = `wss://tracker.openwebtorrent.com/` // For simplicity
21 // Client represents the webtorrent client
30 offeredPeers map[string]Peer // OfferID to Peer
32 tracker *websocket.Conn
37 // Peer represents a remote peer
43 func NewClient() (*Client, error) {
45 offeredPeers: make(map[string]Peer),
49 randPeerID, err := buffer.RandomBytes(9)
51 return nil, fmt.Errorf("failed to generate bytes: %v", err)
53 peerIDBuffer := buffer.From("-WW0007-" + randPeerID.ToStringBase64())
54 c.peerID = peerIDBuffer.ToStringHex()
55 c.peerIDBinary = peerIDBuffer.ToStringLatin1()
60 func (c *Client) LoadFile(p string) error {
61 meta, err := metainfo.LoadFromFile(p)
63 return fmt.Errorf("failed to load meta info: %v\n", err)
66 info, err := meta.UnmarshalInfo()
68 return fmt.Errorf("failed to unmarshal info: %v\n", err)
70 c.totalLength = int(info.TotalLength())
72 c.infoHash = meta.HashInfoBytes().String()
73 b, err := buffer.FromHex(c.infoHash)
75 return fmt.Errorf("failed to create buffer: %v\n", err)
77 c.infoHashBinary = b.ToStringLatin1()
82 func (c *Client) Run() error {
83 t, _, err := websocket.DefaultDialer.Dial(trackerURL, nil)
85 return fmt.Errorf("failed to dial tracker: %v", err)
96 func (c *Client) announce() {
97 transpot, offer, err := NewTransport()
99 log.Fatalf("failed to create transport: %v\n", err)
102 randOfferID, err := buffer.RandomBytes(20)
104 log.Fatalf("failed to generate bytes: %v\n", err)
106 // OfferID := randOfferID.ToStringHex()
107 offerIDBinary := randOfferID.ToStringLatin1()
110 c.offeredPeers[offerIDBinary] = Peer{transport: transpot}
113 req := AnnounceRequest{
114 Numwant: 1, // If higher we need to create equal amount of offers
117 Left: int(c.totalLength),
120 InfoHash: c.infoHashBinary,
121 PeerID: c.peerIDBinary,
124 OfferID: offerIDBinary,
129 data, err := json.Marshal(req)
131 log.Fatal("failed to marshal request:", err)
135 err = tracker.WriteMessage(websocket.TextMessage, data)
137 log.Fatal("write AnnounceRequest:", err)
143 func (c *Client) trackerReadLoop() {
149 _, message, err := tracker.ReadMessage()
151 log.Fatalf("read error: %v", err)
153 log.Printf("recv: %s", message)
155 var ar AnnounceResponse
156 if err := json.Unmarshal(message, &ar); err != nil {
157 log.Printf("error unmarshaling announce response: %v", err)
160 if ar.InfoHash != c.infoHashBinary {
161 log.Printf("announce response for different hash: %s", ar.InfoHash)
165 case ar.Offer != nil:
166 t, answer, err := NewTransportFromOffer(*ar.Offer, c.handleDataChannel)
168 log.Fatal("write AnnounceResponse:", err)
171 req := AnnounceResponse{
173 InfoHash: c.infoHashBinary,
174 PeerID: c.peerIDBinary,
179 data, err := json.Marshal(req)
181 log.Fatal("failed to marshal request:", err)
185 err = tracker.WriteMessage(websocket.TextMessage, data)
187 log.Fatal("write AnnounceResponse:", err)
192 // Do something with the peer
193 _ = Peer{peerID: ar.PeerID, transport: t}
194 case ar.Answer != nil:
196 peer, ok := c.offeredPeers[ar.OfferID]
199 fmt.Printf("could not find peer for offer %s", ar.OfferID)
202 err = peer.transport.SetAnswer(*ar.Answer, c.handleDataChannel)
204 log.Fatalf("failed to sent answer: %v", err)
210 func (c *Client) handleDataChannel(dc datachannel.ReadWriteCloser) {
212 //go c.dcWriteLoop(dc)
215 func (c *Client) dcReadLoop(d io.Reader) {
217 buffer := make([]byte, 1024)
218 n, err := d.Read(buffer)
220 log.Fatal("Datachannel closed; Exit the readloop:", err)
223 fmt.Printf("Message from DataChannel: %s\n", string(buffer[:n]))
227 type AnnounceRequest struct {
228 Numwant int `json:"numwant"`
229 Uploaded int `json:"uploaded"`
230 Downloaded int `json:"downloaded"`
231 Left int `json:"left"`
232 Event string `json:"event"`
233 Action string `json:"action"`
234 InfoHash string `json:"info_hash"`
235 PeerID string `json:"peer_id"`
236 Offers []Offer `json:"offers"`
240 OfferID string `json:"offer_id"`
241 Offer webrtc.SessionDescription `json:"offer"`
244 type AnnounceResponse struct {
245 InfoHash string `json:"info_hash"`
246 Action string `json:"action"`
247 Interval *int `json:"interval,omitempty"`
248 Complete *int `json:"complete,omitempty"`
249 Incomplete *int `json:"incomplete,omitempty"`
250 PeerID string `json:"peer_id,omitempty"`
251 ToPeerID string `json:"to_peer_id,omitempty"`
252 Answer *webrtc.SessionDescription `json:"answer,omitempty"`
253 Offer *webrtc.SessionDescription `json:"offer,omitempty"`
254 OfferID string `json:"offer_id,omitempty"`