fix `Spec.Storage` and allow per-torrent dir
`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.
This commit is contained in:
parent
ba3e798b5f
commit
9d96cd659f
23
client.go
23
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)
|
||||
}
|
||||
|
|
|
@ -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...)...)
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
|
|
Loading…
Reference in New Issue