标题: [源代码] PHP 解析 Torrent 文件
时间: 2016-03-14发布,2016-03-16修改
<?php
class TorrentInfo{
public $Info;
private $fileBytes;
private $fileLength;
public function GetBytes($startPos, $length){
return substr($this->fileBytes, $startPos, $length);
}
private function readBytes($startPos) {
for($i = $startPos; $i < $this->fileLength; $i++){
if($this->fileBytes[$i] == ':'){
$valueLength = intval(substr($this->fileBytes, $startPos, $i - $startPos));
$value = substr($this->fileBytes, $i + 1, $valueLength);
return array($value, $startPos, $i + $valueLength + 1);
}
}
}
private function readInteger($startPos){
for($i = $startPos; $i < $this->fileLength; $i++){
if($this->fileBytes[$i] == 'e'){
$r0 = intval(substr($this->fileBytes, $startPos, $i - $startPos));
return array($r0, $startPos - 1, $i + 1);
}
}
}
private function readDict($startPos){
$ret = array(array(), $startPos - 1, 0);
$isTitle = TRUE;
$lastTitle = "ROOT";
while($startPos < $this->fileLength){
if($isTitle){
if ($this->fileBytes[$startPos] == 'e'){
$ret[2] = $startPos + 1;
return $ret;
}
$isTitle = FALSE;
$title = $this->readBytes($startPos);
$startPos = $title[2];
$lastTitle = $title[0];
}else{
$isTitle = TRUE;
switch($this->fileBytes[$startPos]){
case 'd':
$r0 = $this->readDict($startPos + 1);
$ret[0][$lastTitle] = $r0;
$startPos = $r0[2];
break;
case 'l':
$r0 = $this->readList($startPos + 1);
$ret[0][$lastTitle] = $r0;
$startPos = $r0[2];
break;
case 'i':
$r0 = $this->readInteger($startPos + 1);
$ret[0][$lastTitle] = $r0;
$startPos = $r0[2];
break;
default:
$r0 = $this->readBytes($startPos);
$startPos = $r0[2];
$ret[0][$lastTitle] = $r0;
break;
}
}
}
}
private function readList($startPos){
$ret = array(array(), $startPos - 1, 0);
while($startPos < $this->fileLength){
switch($this->fileBytes[$startPos]){
case 'e':
$ret[2] = $startPos + 1;
return $ret;
case 'd':
$r0 = $this->readDict($startPos + 1);
$ret[0][] = $r0;
$startPos = $r0[2];
break;
case 'l':
$r0 = $this->readList($startPos + 1);
$ret[0][] = $r0;
$startPos = $r0[2];
break;
case 'i':
$r0 = $this->readInteger($startPos + 1);
$ret[0][] = $r0;
$startPos = $r0[2];
break;
default:
$r0 = $this->readBytes($startPos);
$startPos = $r0[2];
$ret[0][] = $r0;
break;
}
}
}
public function __construct($fileContents){
$this->fileBytes = $fileContents;
$this->fileLength = strlen($fileContents);
$this->Info = $this->readDict(1);
}
}
查阅了各种资料,自己也着手分析了.torrent文件的结构,总算是弄明白了,<?php
include_once './xtorrent.php';
$xt = new TorrentInfo(file_get_contents('./test.torrent'));
header('Content-Type: text/plain; charset=ascii');
print_r($xt->Info);
可以用于转换磁力链接:
<?php
include_once './xtorrent.php';
$xt = new TorrentInfo(file_get_contents('./test.torrent'));
header('Content-Type: text/plain; charset=ascii');
echo 'magnet:?xt=urn:btih:';
echo sha1($xt->GetBytes($xt->Info[0]['info'][1], $xt->Info[0]['info'][2] - $xt->Info[0]['info'][1]));
/* 这种磁力链接有两种形式,一种就像上面这种直接是20个16进制字符组成的sha-1哈希计算值,
还有一种形式是经过Base32编码的版本:
echo base32_encode(sha1($xt->GetBytes($xt->Info[0]['info'][1], $xt->Info[0]['info'][2] - $xt->Info[0]['info'][1]), TRUE));
当然,第二种版本是不能直接用的,因为PHP并不内置base32编码功能。 */
Mar 16, 2016更新 添加Nodejs版本,用法与PHP版本类似,但是Info被改成了小写的info。
function torrentInfo(fileBuffer) {
torrentInfo.fileBuffer = fileBuffer;
torrentInfo.info = torrentInfo.readDict(0);
return torrentInfo;
}
torrentInfo.readBytes = function(startPos) {
for(var i = startPos; i < torrentInfo.fileBuffer.length; i++){
if (torrentInfo.fileBuffer[i] == 0x3a) {
var contentEndPos = parseInt(torrentInfo.fileBuffer.toString('ascii', startPos, i)) + i + 1;
return [torrentInfo.fileBuffer.slice(i + 1, contentEndPos), startPos, contentEndPos];
}
}
}
torrentInfo.readInteger = function(startPos) {
startPos++;
for (var i = startPos; i < torrentInfo.fileBuffer.length; i++) {
if (torrentInfo.fileBuffer[i] == 0x65) {
var r0 = parseInt(torrentInfo.fileBuffer.toString('ascii', startPos, i));
return [r0, startPos - 1, i + 1];
}
}
}
torrentInfo.readDict = function(startPos){
var ret = [{}, startPos, 0];
startPos++;
var isTitle = true;
var lastTitle = ':ROOT:';
while (startPos < torrentInfo.fileBuffer.length) {
var r0;
if (isTitle) {
if (torrentInfo.fileBuffer[startPos] == 0x65) {
ret[2] = startPos + 1;
return ret;
}
isTitle = false;
title = torrentInfo.readBytes(startPos);
lastTitle = title[0].toString();
startPos = title[2];
continue;
}
switch (torrentInfo.fileBuffer[startPos]) {
case 0x64:
r0 = torrentInfo.readDict(startPos);
ret[0][lastTitle] = r0;
startPos = r0[2];
break;
case 0x6c:
r0 = torrentInfo.readList(startPos);
ret[0][lastTitle] = r0;
startPos = r0[2];
break;
case 0x69:
r0 = torrentInfo.readInteger(startPos);
ret[0][lastTitle] = r0;
startPos = r0[2];
break;
default:
r0 = torrentInfo.readBytes(startPos);
ret[0][lastTitle] = r0;
startPos = r0[2];
break;
}
isTitle = true;
}
}
torrentInfo.readList = function(startPos){
var ret = [[], startPos, 0];
startPos++;
while (startPos < torrentInfo.fileBuffer.length) {
var r0;
switch (torrentInfo.fileBuffer[startPos]) {
case 0x65:
ret[2] = startPos + 1;
return ret;
case 0x64:
r0 = torrentInfo.readDict(startPos);
ret[0].push(r0);
startPos = r0[2];
break;
case 0x6c:
r0 = torrentInfo.readList(startPos);
ret[0].push(r0);
startPos = r0[2];
break;
case 0x69:
r0 = torrentInfo.readInteger(startPos);
ret[0].push(r0);
startPos = r0[2];
break;
default:
r0 = torrentInfo.readBytes(startPos);
ret[0].push(r0);
startPos = r0[2];
break;
}
}
}
『回复列表(6|显示机器人聊天)』