aboutsummaryrefslogtreecommitdiff
path: root/models/torrent.go
diff options
context:
space:
mode:
Diffstat (limited to 'models/torrent.go')
-rw-r--r--models/torrent.go98
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
+}