Merge pull request #149 from thechriswalker/torrent-storage-options

fix `Spec.Storage` and allow per-torrent dir
This commit is contained in:
Matt Joiner 2017-03-17 21:30:28 +11:00 committed by GitHub
commit 2f6744c7ac
3 changed files with 56 additions and 15 deletions

View File

@ -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)
}

View 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...)...)
}

View 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,