]> Sergey Matveev's repositories - btrtrc.git/commitdiff
fix `Spec.Storage` and allow per-torrent dir
authorChris Walker <chris@thechriswalker.net>
Thu, 16 Mar 2017 14:24:54 +0000 (14:24 +0000)
committerChris Walker <chris@thechriswalker.net>
Thu, 16 Mar 2017 14:40:21 +0000 (14:40 +0000)
`TorrentSpec.Storage` was not honored when calling `Client.AddTorrentSpec`
and the configured `cfg.DefaultStorage` was always used. Now if you construct
your `TorrentSpec` you can specify any `StorageImpl`

Also, the most common use case for custom storage being per-torrent paths for
FileStorage, this adds a `pathMaker` function to the File implementation that
allows customization, along with the default (always use base path) and my use
case (which seemed common enough from the Gitter chat) which is infohash based
subdirectories.

All Public methods have not changed signature, but 1 private method did, hence
the test update.

client.go
storage/file.go
torrent_test.go

index 53188a582458eda353ca5b411f769d7c2d0df82d..37637244408bcc6314ad203eaca4217a562b0cf1 100644 (file)
--- a/client.go
+++ b/client.go
@@ -1124,7 +1124,13 @@ func (cl *Client) badPeerIPPort(ip net.IP, port int) bool {
 }
 
 // Return a Torrent ready for insertion into a Client.
-func (cl *Client) newTorrent(ih metainfo.Hash) (t *Torrent) {
+func (cl *Client) newTorrent(ih metainfo.Hash, specStorage storage.ClientImpl) (t *Torrent) {
+       // use provided storage, if provided
+       storageClient := cl.defaultStorage
+       if specStorage != nil {
+               storageClient = storage.NewClient(specStorage)
+       }
+
        t = &Torrent{
                cl:       cl,
                infoHash: ih,
@@ -1134,7 +1140,7 @@ func (cl *Client) newTorrent(ih metainfo.Hash) (t *Torrent) {
                halfOpen:          make(map[string]struct{}),
                pieceStateChanges: pubsub.NewPubSub(),
 
-               storageOpener:       cl.defaultStorage,
+               storageOpener:       storageClient,
                maxEstablishedConns: defaultEstablishedConnsPerTorrent,
        }
        t.setChunkSize(defaultChunkSize)
@@ -1150,6 +1156,13 @@ type Handle interface {
 }
 
 func (cl *Client) AddTorrentInfoHash(infoHash metainfo.Hash) (t *Torrent, new bool) {
+       return cl.AddTorrentInfoHashWithStorage(infoHash, nil)
+}
+
+// Adds a torrent by InfoHash with a custom Storage implementation.
+// If the torrent already exists then this Storage is ignored and the
+// existing torrent returned with `new` set to `false`
+func (cl *Client) AddTorrentInfoHashWithStorage(infoHash metainfo.Hash, specStorage storage.ClientImpl) (t *Torrent, new bool) {
        cl.mu.Lock()
        defer cl.mu.Unlock()
        t, ok := cl.torrents[infoHash]
@@ -1157,7 +1170,7 @@ func (cl *Client) AddTorrentInfoHash(infoHash metainfo.Hash) (t *Torrent, new bo
                return
        }
        new = true
-       t = cl.newTorrent(infoHash)
+       t = cl.newTorrent(infoHash, specStorage)
        if cl.dHT != nil {
                go t.dhtAnnouncer()
        }
@@ -1172,8 +1185,10 @@ func (cl *Client) AddTorrentInfoHash(infoHash metainfo.Hash) (t *Torrent, new bo
 // trackers will be merged with the existing ones. If the Info isn't yet
 // known, it will be set. The display name is replaced if the new spec
 // provides one. Returns new if the torrent wasn't already in the client.
+// Note that any `Storage` defined on the spec will be ignored if the
+// torrent is already present (i.e. `new` return value is `true`)
 func (cl *Client) AddTorrentSpec(spec *TorrentSpec) (t *Torrent, new bool, err error) {
-       t, new = cl.AddTorrentInfoHash(spec.InfoHash)
+       t, new = cl.AddTorrentInfoHashWithStorage(spec.InfoHash, spec.Storage)
        if spec.DisplayName != "" {
                t.SetDisplayName(spec.DisplayName)
        }
index 19e547564ac5fde487a91cc2df253f4d46e6bdce..b0a9ca1034ec933973cd989fe9b72f6f49e925b0 100644 (file)
@@ -13,14 +13,39 @@ import (
 // File-based storage for torrents, that isn't yet bound to a particular
 // torrent.
 type fileClientImpl struct {
-       baseDir string
-       pc      pieceCompletion
+       baseDir   string
+       pathMaker func(baseDir string, info *metainfo.Info, infoHash metainfo.Hash) string
+       pc        pieceCompletion
 }
 
+// The Default path maker just returns the current path
+func defaultPathMaker(baseDir string, info *metainfo.Info, infoHash metainfo.Hash) string {
+       return baseDir
+}
+
+func infoHashPathMaker(baseDir string, info *metainfo.Info, infoHash metainfo.Hash) string {
+       return filepath.Join(baseDir, infoHash.HexString())
+}
+
+// All Torrent data stored in this baseDir
 func NewFile(baseDir string) ClientImpl {
+       return NewFileWithCustomPathMaker(baseDir, nil)
+}
+
+// All Torrent data stored in subdirectorys by infohash
+func NewFileByInfoHash(baseDir string) ClientImpl {
+       return NewFileWithCustomPathMaker(baseDir, infoHashPathMaker)
+}
+
+// Allows passing a function to determine the path for storing torrent data
+func NewFileWithCustomPathMaker(baseDir string, pathMaker func(baseDir string, info *metainfo.Info, infoHash metainfo.Hash) string) ClientImpl {
+       if pathMaker == nil {
+               pathMaker = defaultPathMaker
+       }
        return &fileClientImpl{
-               baseDir: baseDir,
-               pc:      pieceCompletionForDir(baseDir),
+               baseDir:   baseDir,
+               pathMaker: pathMaker,
+               pc:        pieceCompletionForDir(baseDir),
        }
 }
 
@@ -29,12 +54,13 @@ func (me *fileClientImpl) Close() error {
 }
 
 func (fs *fileClientImpl) OpenTorrent(info *metainfo.Info, infoHash metainfo.Hash) (TorrentImpl, error) {
-       err := CreateNativeZeroLengthFiles(info, fs.baseDir)
+       dir := fs.pathMaker(fs.baseDir, info, infoHash)
+       err := CreateNativeZeroLengthFiles(info, dir)
        if err != nil {
                return nil, err
        }
        return &fileTorrentImpl{
-               fs,
+               dir,
                info,
                infoHash,
                fs.pc,
@@ -43,7 +69,7 @@ func (fs *fileClientImpl) OpenTorrent(info *metainfo.Info, infoHash metainfo.Has
 
 // File-based torrent storage, not yet bound to a Torrent.
 type fileTorrentImpl struct {
-       fs         *fileClientImpl
+       dir        string
        info       *metainfo.Info
        infoHash   metainfo.Hash
        completion pieceCompletion
@@ -68,12 +94,12 @@ func (fs *fileTorrentImpl) Close() error {
 // Creates natives files for any zero-length file entries in the info. This is
 // a helper for file-based storages, which don't address or write to zero-
 // length files because they have no corresponding pieces.
-func CreateNativeZeroLengthFiles(info *metainfo.Info, baseDir string) (err error) {
+func CreateNativeZeroLengthFiles(info *metainfo.Info, dir string) (err error) {
        for _, fi := range info.UpvertedFiles() {
                if fi.Length != 0 {
                        continue
                }
-               name := filepath.Join(append([]string{baseDir, info.Name}, fi.Path...)...)
+               name := filepath.Join(append([]string{dir, info.Name}, fi.Path...)...)
                os.MkdirAll(filepath.Dir(name), 0750)
                var f io.Closer
                f, err = os.Create(name)
@@ -181,5 +207,5 @@ func (fst fileTorrentImplIO) WriteAt(p []byte, off int64) (n int, err error) {
 }
 
 func (fts *fileTorrentImpl) fileInfoName(fi metainfo.FileInfo) string {
-       return filepath.Join(append([]string{fts.fs.baseDir, fts.info.Name}, fi.Path...)...)
+       return filepath.Join(append([]string{fts.dir, fts.info.Name}, fi.Path...)...)
 }
index 51c26c67443f3145c740e578ff263751810cea40..5811b359516deb58631750f2e7c7ff43f3805840 100644 (file)
@@ -75,7 +75,7 @@ func TestTorrentString(t *testing.T) {
 // piece priorities everytime a reader (possibly in another Torrent) changed.
 func BenchmarkUpdatePiecePriorities(b *testing.B) {
        cl := &Client{}
-       t := cl.newTorrent(metainfo.Hash{})
+       t := cl.newTorrent(metainfo.Hash{}, nil)
        t.info = &metainfo.Info{
                Pieces:      make([]byte, 20*13410),
                PieceLength: 256 << 10,