Files
gopeed/protocol/bt/metainfo/metainfo.go
T
2020-03-01 23:35:51 +08:00

145 lines
3.3 KiB
Go

package metainfo
import (
"crypto/sha1"
"encoding/json"
"os"
"github.com/marksamman/bencode"
)
type MetaInfo struct {
// tracker URL
// http://bittorrent.org/beps/bep_0003.html#metainfo-files
Announce string `json:"announce"`
// tracker URL
// http://bittorrent.org/beps/bep_0012.html
AnnounceList [][]string `json:"announce-list"`
Comment string `json:"comment"`
CreatedBy string `json:"created by"`
CreationDate int64 `json:"creation date"`
Encoding string `json:"encoding"`
// http://bittorrent.org/beps/bep_0019.html
UrlList []string `json:"url-list"`
Info *Info `json:"info"`
infoHash [20]byte
totalSize uint64
fileDetails []FileDetail
}
type Info struct {
Name string `json:"name"`
PieceLength int `json:"piece length"`
Pieces [][20]byte
// 单个文件时,length为该文件的长度
Length uint64 `json:"length"`
// 多个文件
Files []File `json:"files"`
}
type File struct {
Length uint64 `json:"length"`
Path []string `json:"path"`
}
type FileDetail struct {
Length uint64
Path []string
Begin int64
End int64
}
type PieceMapping struct {
file int
begin int64
end int64
}
func ParseFromFile(path string) (*MetaInfo, error) {
file, err := os.Open(path)
if err != nil {
return nil, err
}
// http://bittorrent.org/beps/bep_0003.html#bencoding
btDecode, err := bencode.Decode(file)
if err != nil {
return nil, err
}
bytes, err := json.Marshal(btDecode)
if err != nil {
return nil, err
}
var metaInfo MetaInfo
json.Unmarshal(bytes, &metaInfo)
if metaInfo.Info != nil {
// encode info hash
info := btDecode["info"].(map[string]interface{})
infoBtEncode := bencode.Encode(info)
metaInfo.infoHash = sha1.Sum(infoBtEncode[:])
// Split pieces hash
pieces := info["pieces"].(string)
if pieces != "" {
metaInfo.Info.Pieces = make([][20]byte, len(pieces)/20)
// pieces parse
for i := range metaInfo.Info.Pieces {
begin := i * 20
metaInfo.Info.Pieces[i] = [20]byte{}
copy(metaInfo.Info.Pieces[i][:], pieces[begin:begin+20])
}
}
}
calcFileSize(&metaInfo)
return &metaInfo, nil
}
// 获取某个分片的大小
func (metaInfo *MetaInfo) GetPieceLength(index int) int {
// 是否为最后一个分片
if index == len(metaInfo.Info.Pieces)-1 {
size := metaInfo.totalSize % uint64(metaInfo.Info.PieceLength)
if size > 0 {
return int(size)
}
}
return metaInfo.Info.PieceLength
}
// 获取所有文件的大小
func (metaInfo *MetaInfo) GetTotalSize() uint64 {
return metaInfo.totalSize
}
// 获取info hash
func (metaInfo *MetaInfo) GetInfoHash() [20]byte {
return metaInfo.infoHash
}
// 获取所有文件的开始偏移字节
func (metaInfo *MetaInfo) GetFileDetails() []FileDetail {
return metaInfo.fileDetails
}
// 计算所有文件的大小
func calcFileSize(metaInfo *MetaInfo) {
if metaInfo.Info != nil {
fileCount := len(metaInfo.Info.Files)
if fileCount > 0 {
metaInfo.fileDetails = make([]FileDetail, fileCount)
var length uint64
for i, f := range metaInfo.Info.Files {
metaInfo.fileDetails[i] = FileDetail{
Length: f.Length,
Path: f.Path,
Begin: int64(length),
End: int64(length + f.Length),
}
length += f.Length
}
metaInfo.totalSize = length
} else {
metaInfo.totalSize = metaInfo.Info.Length
}
}
}