mirror of
https://github.com/GopeedLab/gopeed.git
synced 2026-11-09 18:10:29 +00:00
145 lines
3.3 KiB
Go
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
|
|
}
|
|
}
|
|
}
|