diff options
Diffstat (limited to 'models/torrent.go')
| -rw-r--r-- | models/torrent.go | 98 |
1 files changed, 98 insertions, 0 deletions
diff --git a/models/torrent.go b/models/torrent.go new file mode 100644 index 0000000..960a6de --- /dev/null +++ b/models/torrent.go @@ -0,0 +1,98 @@ +package models + +import ( + "bytes" + "crypto/sha1" + "encoding/hex" + "fmt" + "os" + "strings" + "time" + + "github.com/felix/dhtsearch/bencode" + "github.com/felix/dhtsearch/dht" + "github.com/felix/dhtsearch/krpc" +) + +// Data for persistent storage +type Torrent struct { + ID int `json:"-"` + InfoHash string `json:"infohash"` + Name string `json:"name"` + Files []File `json:"files" db:"-"` + Size int `json:"size"` + Seen time.Time `json:"seen"` + Tags []string `json:"tags" db:"-"` +} + +type File struct { + ID int `json:"-"` + Path string `json:"path"` + Size int `json:"size"` + TorrentID int `json:"torrent_id" db:"torrent_id"` +} + +type torrentStore interface { + saveTorrent(*Torrent) error + torrentsByHash(hashes dht.Infohash, offset, limit int) (*Torrent, error) + torrentsByName(query string, offset, limit int) ([]*Torrent, error) + torrentsByTags(tags []string, offset, limit int) ([]*Torrent, error) +} + +func validMetadata(ih dht.Infohash, md []byte) bool { + info := sha1.Sum(md) + return bytes.Equal([]byte(ih), info[:]) +} + +func TorrentFromMetadata(ih dht.Infohash, md []byte) (*Torrent, error) { + if !validMetadata(ih, md) { + return nil, fmt.Errorf("infohash does not match metadata") + } + info, _, err := bencode.DecodeDict(md, 0) + if err != nil { + return nil, err + } + + // Get the directory or advisory filename + name, err := krpc.GetString(info, "name") + if err != nil { + return nil, err + } + + bt := Torrent{ + InfoHash: hex.EncodeToString([]byte(ih)), + Name: name, + } + + if files, err := krpc.GetList(info, "files"); err == nil { + // Multiple file mode + bt.Files = make([]File, len(files)) + + // Files is a list of dicts + for i, item := range files { + file := item.(map[string]interface{}) + + // Paths is a list of strings + paths := file["path"].([]interface{}) + path := make([]string, len(paths)) + for j, p := range paths { + path[j] = p.(string) + } + + fSize := file["length"].(int) + bt.Files[i] = File{ + // Assume Unix path sep? + Path: strings.Join(path[:], string(os.PathSeparator)), + Size: fSize, + } + // Ensure the torrent size totals all files' + bt.Size = bt.Size + fSize + } + } else if length, err := krpc.GetInt(info, "length"); err == nil { + // Single file mode + bt.Size = length + } else { + return nil, fmt.Errorf("found neither length or files") + } + return &bt, nil +} |
