Class: BTScraper::UDPScrape

Inherits:
Object
  • Object
show all
Defined in:
lib/btscraper/udpscrape.rb

Overview

This class permits you to scrape an UDP torrent tracker according to BEP 15

See Also:

Author:

  • sherkix

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(tracker, info_hash) ⇒ UDPScrape

Create a new UDPScrape object

Examples:

Default usage

scrape_object = BTScraper::UDPScrape.new('udp://example.com:3000/announce', ['c22b5f9178342609428d6f51b2c5af4c0bde6a42'], ['aaf4c61ddcc5e8a2dabede0f3b482cd9aea9434d'])
scrape_object.scrape

Parameters:

  • tracker (String)

    Bittorrent tracker server

  • info_hash (Array<String>, String)

    Array of infohashes or single infohash

Raises:

  • (TypeError)

    if wrong type of argument is provided

  • (BTScraperError)

    if the infohashes provided are more than 74



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
# File 'lib/btscraper/udpscrape.rb', line 45

def initialize(tracker, info_hash)
  unless tracker.instance_of? String
    raise TypeError, "String excpected, got #{tracker.class}"
  end
  unless info_hash.instance_of? String or info_hash.instance_of? Array
    raise TypeError, "String or Array excpected, got #{info_hash.class}"
  end
  
  # Maximum number of infohashes is 74 according to BEP 15

  if info_hash.instance_of? Array and info_hash.count > 74
    raise BTScraperError, 'The number of infohashes must be less than 74'
  end

  if info_hash.instance_of? String
    info_hash.downcase!
    check_info_hash Array(info_hash)
  else
    info_hash.map(&:downcase!)
    check_info_hash info_hash
  end
  @tracker = tracker 
  @hostname = URI(@tracker).hostname
  @port = URI(@tracker).port 
  @info_hash = Array(info_hash) 
end

Instance Attribute Details

#hostnameString (readonly)

Returns tracker’s hostname

Returns:

  • (String)

    returns tracker’s hostname



45
46
47
# File 'lib/btscraper/udpscrape.rb', line 45

def hostname
  @hostname
end

#info_hashArray<String> (readonly)

Returns array of infohashes

Returns:

  • (Array<String>)

    returns array of infohashes



45
46
47
# File 'lib/btscraper/udpscrape.rb', line 45

def info_hash
  @info_hash
end

#portInteger (readonly)

Returns tracker’s port

Returns:

  • (Integer)

    returns tracker’s port



45
46
47
# File 'lib/btscraper/udpscrape.rb', line 45

def port
  @port
end

#trackerString (readonly)

Returns tracker full url

Returns:

  • (String)

    returns tracker full url



45
46
47
# File 'lib/btscraper/udpscrape.rb', line 45

def tracker
  @tracker
end

Instance Method Details

#scrapeHash

Returns The method returns an hash with the scraped data.

Examples:

Response example

{tracker: "udp://example.com:3000/announce", scraped_data: [{infohash: "c22b5f9178342609428d6f51b2c5af4c0bde6a42", seeders: 20, completed: 1000, leechers: 30}, {infohash: "aaf4c61ddcc5e8a2dabede0f3b482cd9aea9434d", seeders: 350, completed: 12000, leechers: 23}]}

Returns:

  • (Hash)

    The method returns an hash with the scraped data

Raises:

  • (BTScraperError)

    If the response is less than 8 bytes or if the scraping request fails

  • (BTScraperError)

    If the scraping request fails

  • (BTScraperError)

    If the tracker response with a different transaction_id provided by the client

  • (BTScraperError)

    After 8 timeouts



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
# File 'lib/btscraper/udpscrape.rb', line 77

def scrape
  attempt = 0
  client = connect_to_tracker
  transaction_id = rand_transaction_id
  buffer = [get_connection_id, Actionscrape, transaction_id].pack('Q>NN')
  @info_hash.each{|x| buffer << x.split.pack('H*')}
  begin
    client.send buffer, 0
    Timeout::timeout(Defaulttimeout * 2**attempt) do
      response = client.recvfrom(4096)
      if response[0].bytesize < 8
        raise BTScraperError, 'The response from the tracker is less than 8 bytes'
      end
      @unpacked_response = response[0].unpack('N*')
      if @unpacked_response[0] == Actionerr
        raise BTScraperError, 'Scrape request failed'
      end
      unless @unpacked_response[1] == transaction_id
        raise BTScraperError, 'Invalid transaction id got from tracker'
      end
    end
  rescue Timeout::Error
    attempt+=1
    puts "#{attempt} Request to #{@hostname} timed out, retying after #{Defaulttimeout * 2 ** attempt}s"
    retry if attempt <= Retries
    raise BTScraperError, 'Max retries exceeded'
  ensure
    client.close
  end
  hash = {tracker: @tracker, scraped_data:[]}
  create_scrape_hash @info_hash, @unpacked_response, hash
end