Witaj Gościu! ( Zaloguj | Rejestruj )

Forum PHP.pl

 
Reply to this topicStart new topic
> [klasa][php5] Torrent - pobieranie informaci z trackera (seeds/peers)
crashu
post 24.07.2009, 21:52:44
Post #1





Grupa: Zarejestrowani
Postów: 6
Pomógł: 0
Dołączył: 14.04.2007

Ostrzeżenie: (0%)
-----


BEncode.php
  1. <?php
  2.  
  3. class BEncode {
  4.    
  5.    /**
  6.      * @var string
  7.      */
  8.    protected static $haystack = null;
  9.    /**
  10.      * @var int
  11.      */
  12.    protected static $offset = 0;
  13.    
  14.    /**
  15.      * Decode
  16.      *
  17.      * @param $haystack string
  18.      * @return mixed
  19.      */
  20.    public static function decode($haystack)
  21.    {
  22.        if( !isset($haystack) ) return null;
  23.        
  24.        self::$haystack = $haystack;
  25.        self::$offset = 0;
  26.  
  27.        $result = self::parse();
  28.  
  29.        self::$haystack = null;
  30.        
  31.        return $result;
  32.    }
  33.    
  34.    /**
  35.      *
  36.      * @return array
  37.      */
  38.    protected static function parseDictionarie()
  39.    {
  40.        self::$offset++;
  41.        
  42.        $result = array();
  43.        
  44.        while( !self::end() )
  45.        {
  46.            $key = self::parseString();
  47.            if( strlen( $key ) < 1 ) throw new Exception('Unknown key name at offset:'.self::$offset, 2);
  48.  
  49.            $result[ $key ] = self::parse();
  50.        }
  51.        
  52.        self::$offset += 1;
  53.        
  54.        return $result;
  55.    }
  56.    
  57.    /**
  58.      *
  59.      * @return array
  60.      */
  61.    protected static function parseList()
  62.    {
  63.        self::$offset++;
  64.        
  65.        $result = array();
  66.        
  67.        while( !self::end() )
  68.        {
  69.            $result[] = self::parse();
  70.        }
  71.        
  72.        self::$offset += 1;
  73.        
  74.        return $result;
  75.    }
  76.    
  77.    /**
  78.      *
  79.      * @return int
  80.      */
  81.    protected static function parseIntiger()
  82.    {
  83.        self::$offset += 1;
  84.  
  85.        $l = strpos(self::$haystack,'e', self::$offset) - self::$offset;
  86.        $value = (int)substr(self::$haystack, self::$offset, $l);
  87.        
  88.        if( !is_numeric($value) ) throw new Exception('Parsed data is not integer ''.$value.'' at offset:'.self::$offset, 3);
  89.        
  90.        self::$offset += $l + 1;
  91.  
  92.        return $value;
  93.    }
  94.    
  95.    /**
  96.      *
  97.      * @return string
  98.      */
  99.    protected static function parseString()
  100.    {
  101.        $string = '';
  102.  
  103.        $l = strpos(self::$haystack,':', self::$offset) - self::$offset;
  104.        $length = (int)substr(self::$haystack, self::$offset, $l);
  105.        self::$offset += $l+1;
  106.            
  107.        $string = (string)substr(self::$haystack, self::$offset, $length);
  108.        self::$offset += $length;
  109.            
  110.        if( !isset($string) ) $string = '';
  111.  
  112.        return $string;
  113.    }
  114.    
  115.    /**
  116.      *
  117.      * @return mixed
  118.      */
  119.    protected static function parse()
  120.    {
  121.        $result = null;
  122.  
  123.        if( self::end() ) return null;
  124.        
  125.        switch( self::$haystack[self::$offset] )
  126.        {
  127.            case 'd':
  128.                $result = self::parseDictionarie();
  129.                break;
  130.            case 'l':
  131.                $result = self::parseList();
  132.                break;
  133.            case 'i':
  134.                $result = self::parseIntiger();
  135.                break;
  136.            default:
  137.                if( is_numeric( self::$haystack[self::$offset] ) )
  138.                    $result = self::parseString();
  139.                else
  140.                    throw new Exception('Unknown type. ''.self::$haystack[self::$offset].'' at offset:'.self::$offset, 1);
  141.        }
  142.        
  143.        return $result;
  144.    }
  145.    
  146.    protected static function end()
  147.    {
  148.        if( @!isset( self::$haystack[self::$offset] ) ) return true;
  149.        if( self::$haystack[self::$offset] == 'e' ) return true;
  150.        
  151.        return false;
  152.    }
  153.    
  154.    /* ENCODE */
  155.    
  156.    /**
  157.      * Encode
  158.      * @param $mixed mixed
  159.      * @return string
  160.      */
  161.    public static function encode($mixed)
  162.    {
  163.        if( !isset($mixed) ) return '';
  164.  
  165.        $result = '';
  166.  
  167.        if( is_array($mixed) )
  168.        {
  169.            if( count($mixed) == 0 ) $type = 'd';     // empty array should be dictionarie
  170.            else
  171.            {
  172.                $type = 'l';
  173.                
  174.                // find what type of array we have
  175.                // if there's one(or more) none numeric key we have dictionarie
  176.                foreach($mixed as $key => $e)
  177.                {
  178.                    if( !is_int($key) )
  179.                    {
  180.                        $type = 'd';
  181.                        break;
  182.                    }
  183.                }
  184.            }
  185.            
  186.            $result .= $type;
  187.            
  188.            foreach($mixed as $key => $e)
  189.            {
  190.                if( $type == 'l' )
  191.                    $result .= self::encode($e);
  192.                else
  193.                {
  194.                    $result .= (int)strlen($key).':'.$key.self::encode($e);
  195.                }
  196.            }
  197.            
  198.            $result .= 'e';
  199.        }
  200.        else
  201.        {
  202.            if( is_int($mixed) )
  203.                $result .= 'i'.(int)$mixed.'e';
  204.            else
  205.            {
  206.                $result .= (int)strlen($mixed).':'.$mixed;
  207.            }
  208.        }
  209.        
  210.        return $result;
  211.    }
  212. }
  213. ?>


Clasa Torrent
  1. <?php
  2.  
  3. require 'BEncode.php';
  4.  
  5. /**
  6.  * hex2bin
  7.  *
  8.  * from php.net
  9.  *
  10.  * @param $h string
  11.  * @return string
  12.  */
  13. function hex2bin($h)
  14. {
  15.    if( !is_string($h) ) return null;
  16.    if( strlen($h)%2 != 0 ) return null;
  17.    $r='';
  18.    for( $a=0; $a<strlen($h); $a+=2) { $r.=chr(hexdec($h{$a}.$h{($a+1)})); }
  19.    return $r;
  20. }
  21.  
  22. class Tracker {
  23.    
  24.    /**
  25.      * Tracker url
  26.      * @var string
  27.      */
  28.    protected $url;
  29.    
  30.    public function __construct($url)
  31.    {
  32.        $this->url = $url;
  33.    }
  34.    
  35.    /**
  36.      * Get data from tracker
  37.      *
  38.      * @param $hash_info string
  39.      * @return mixed false if failed
  40.      */
  41.    public function ask( $hash_info )
  42.    {
  43.        $url = $this->url
  44.            .'?info_hash='. urlencode(hex2bin( $hash_info ))
  45.            .'&key='. urlencode(hex2bin( $hash_info ))
  46.            .'&peer_id='. self::getPeerId()
  47.            .'&port=0&uploaded=0&downloaded=0&left=100';
  48.            
  49.        $result = @file_get_contents($url);
  50.        
  51.        if( !isset( $result ) && empty($result) ) return false;
  52.        
  53.        try {
  54.            
  55.            $result = BEncode::decode($result);
  56.        }
  57.        catch(Exception $e)
  58.        {
  59.            return false;
  60.        }
  61.        
  62.        return $result;
  63.    }
  64.    
  65.    /**
  66.      * Validate the url
  67.      * @param $url string
  68.      * @return bool
  69.      */
  70.    public static function validUrl($url)
  71.    {
  72.        if( !isset( $url ) || empty($url) ) return false;
  73.  
  74.        return preg_match('|^http(s)?://[a-z0-9-]+(.[a-z0-9-]+)*(:[0-9]+)?(/.*)|i', $url);
  75.    }
  76.    
  77.    /**
  78.      * PeerID
  79.      * @var string
  80.      */
  81.    private static $sId = null;
  82.    
  83.    public static function getPeerId()
  84.    {
  85.        if( self::$sId === null )
  86.        {
  87.            self::$sId = urlencode( hex2bin( sha1( uniqid() ) ) );
  88.        }
  89.        return self::$sId;
  90.    }
  91. }
  92.  
  93. class Torrent {
  94.    
  95.    /**
  96.      * Torrent data
  97.      * @var array
  98.      */
  99.    protected $data;
  100.    /**
  101.      * Hash (sha1)
  102.      * @var string
  103.      */
  104.    protected $hash;
  105.    
  106.    /**
  107.      * @var int
  108.      */
  109.    protected $peers = -1;
  110.    /**
  111.      * @var int
  112.      */
  113.    protected $seeds = -1;
  114.  
  115.    /**
  116.      * Constructor
  117.      * @param $data string
  118.      */
  119.    public function __construct($data)
  120.    {
  121.        $this->data = BEncode::decode($data);
  122.  
  123.        $this->hash = sha1( BEncode::encode($this->data['info']) );
  124.    }
  125.    
  126.    /**
  127.      *
  128.      * @return string
  129.      */
  130.    public function getName()
  131.    {
  132.        return @$this->data['info']['name'];
  133.    }
  134.    
  135.    /**
  136.      *
  137.      * @return array
  138.      */
  139.    public function getFiles()
  140.    {
  141.        return @$this->data['info']['files'];
  142.    }
  143.    
  144.    public function getHash()
  145.    {
  146.        return $this->hash;
  147.    }
  148.    
  149.    public function getSeeds()
  150.    {
  151.        return $this->seeds;
  152.    }
  153.    
  154.    public function getPeers()
  155.    {
  156.        return $this->peers;
  157.    }
  158.  
  159.    /**
  160.      * Update tracker info
  161.      * @return bool
  162.      */
  163.    public function updateTracker()
  164.    {
  165.        if( !is_array($this->data) ) return false;
  166.        
  167.        $trackers = array();
  168.  
  169.        if( @Tracker::validUrl($this->data['announce']) )
  170.            $trackers[] = $this->data['announce'];
  171.        
  172.        if( is_array($this->data['announce-list']) )
  173.        {
  174.            foreach( $this->data['announce-list'] as $ar )
  175.            {
  176.                if( is_array($ar) )
  177.                    $trackers = array_merge($trackers, $ar);
  178.            }
  179.        }
  180.        
  181.        $trackers = array_unique($trackers);
  182.        
  183.        foreach( $trackers as $url )
  184.        {
  185.            if( @!Tracker::validUrl($url) ) continue;
  186.            
  187.            $t = new Tracker($url);
  188.            
  189.            $result = $t->ask( $this->getHash() );
  190.            
  191.            if( !is_array($result) || isset($result['failure reason']) ) continue;
  192.  
  193.            if( isset( $result['complete'] ) ) $this->seeds = (int)$result['complete'];
  194.            if( isset( $result['incomplete'] ) ) $this->peers = (int)$result['incomplete'];
  195.  
  196.            return true;
  197.        }
  198.        
  199.        return false;
  200.    }
  201.    
  202.    /**
  203.      * Create from torrent file
  204.      *
  205.      * @param $filename string
  206.      * @return Torrent
  207.      */
  208.    public static function fromFile($filename)
  209.    {
  210.        $content = file_get_contents($filename);
  211.        return new Torrent($content);
  212.    }
  213. }
  214. ?>


Klasa pobiera dane o torrencie(ilość seedów peerów) z trackera, niestety nie każdy tracker wysyła 'complete' & 'incomplete'
przykład:
  1. <?php
  2. $torrent = Torrent::fromFile('jakis.torrent');
  3.  
  4. $torrent->updateTracker();
  5.  
  6. var_dump( $torrent->getSeeds(), $torrent->getPeers() );
  7. ?>


--------------------
Black Tarantula
Go to the top of the page
+Quote Post
jancu
post 14.10.2009, 00:14:25
Post #2





Grupa: Zarejestrowani
Postów: 17
Pomógł: 0
Dołączył: 18.08.2007

Ostrzeżenie: (0%)
-----


Działa to fajnie i szybko, ale co jeśli torrent ma multitracker? Ta klasa pobiera dane tylko z jednego trackera. Jak ją przerobić aby pobierać dane ze wszystkich trackerów i wynik sumować?
Go to the top of the page
+Quote Post
crashu
post 17.10.2009, 13:13:44
Post #3





Grupa: Zarejestrowani
Postów: 6
Pomógł: 0
Dołączył: 14.04.2007

Ostrzeżenie: (0%)
-----


sumowanie da nierzeczywisty wynik, ale ....

  1. /**
  2.   * Update tracker info
  3.   * @return bool
  4.   */
  5. public function updateTracker()
  6. {
  7. if( !is_array($this->data) ) return false;
  8.  
  9. $trackers = array();
  10.  
  11. if( @Tracker::validUrl($this->data['announce']) )
  12. $trackers[] = $this->data['announce'];
  13.  
  14. if( is_array($this->data['announce-list']) )
  15. {
  16. foreach( $this->data['announce-list'] as $ar )
  17. {
  18. if( is_array($ar) )
  19. $trackers = array_merge($trackers, $ar);
  20. }
  21. }
  22.  
  23. $trackers = array_unique($trackers);
  24.  
  25. $this->seeds = 0;
  26. $this->peers = 0;
  27. $result = false;
  28.  
  29. foreach( $trackers as $url )
  30. {
  31. if( @!Tracker::validUrl($url) ) continue;
  32.  
  33. $t = new Tracker($url);
  34.  
  35. $result = $t->ask( $this->getHash() );
  36.  
  37. if( !is_array($result) || isset($result['failure reason']) ) continue;
  38.  
  39. if( isset( $result['complete'] ) ) $this->seeds += (int)$result['complete'];
  40. // if( isset( $result['complete'] ) && $this->seeds > (int)$result['complete'] ) $this->seeds = (int)$result['complete'];
  41. if( isset( $result['incomplete'] ) ) $this->peers += (int)$result['incomplete'];
  42. // if( isset( $result['incomplete'] ) && $this->peers > (int)$result['incomplete'] ) $this->peers = (int)$result['incomplete'];
  43.  
  44. $result = true;
  45. }
  46.  
  47. return $result;
  48. }


Ten post edytował crashu 17.10.2009, 13:14:54


--------------------
Black Tarantula
Go to the top of the page
+Quote Post
jancu
post 17.10.2009, 21:11:54
Post #4





Grupa: Zarejestrowani
Postów: 17
Pomógł: 0
Dołączył: 18.08.2007

Ostrzeżenie: (0%)
-----


Wielkie dzięki. O takie coś chodziło.
Go to the top of the page
+Quote Post
Cwirek1987
post 12.01.2010, 17:48:59
Post #5





Grupa: Zarejestrowani
Postów: 71
Pomógł: 0
Dołączył: 9.06.2007

Ostrzeżenie: (10%)
X----


mam pytanie pierwszy kod powinien zostać zapisany w BEncode.php
a pozstałe dwa kody pod jakimi nazwami je zapisać zeby to działało?
Go to the top of the page
+Quote Post
jancu
post 27.03.2010, 19:17:04
Post #6





Grupa: Zarejestrowani
Postów: 17
Pomógł: 0
Dołączył: 18.08.2007

Ostrzeżenie: (0%)
-----


dwa pozostałe w jakimś innym pliku.
Go to the top of the page
+Quote Post
busyboy
post 3.04.2013, 14:03:10
Post #7





Grupa: Zarejestrowani
Postów: 191
Pomógł: 0
Dołączył: 22.02.2004

Ostrzeżenie: (0%)
-----


Przepraszam ze odkopuje temat - czy czy ktos z kolegów może mi powiedzieć jak uruchomić ten skrypt ? Tworze plik BEncode.php i wrzucam zawartość do pliku a z tymi dwoma plikami ? jakie mają mieć nazwę - drugi plik nazwalem torrent.php a trzeci index.php. W pliku index.php dodałem :

  1. require('Torrent.php');
  2. require('BEncode.php');


I niestedy nie działa ....

Prodsze o wskazowki - dziekuje
Go to the top of the page
+Quote Post

Reply to this topicStart new topic
1 Użytkowników czyta ten temat (1 Gości i 0 Anonimowych użytkowników)
0 Zarejestrowanych:

 



RSS Wersja Lo-Fi Aktualny czas: 18.04.2024 - 05:02