}
// 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,
halfOpen: make(map[string]struct{}),
pieceStateChanges: pubsub.NewPubSub(),
- storageOpener: cl.defaultStorage,
+ storageOpener: storageClient,
maxEstablishedConns: defaultEstablishedConnsPerTorrent,
}
t.setChunkSize(defaultChunkSize)
}
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]
return
}
new = true
- t = cl.newTorrent(infoHash)
+ t = cl.newTorrent(infoHash, specStorage)
if cl.dHT != nil {
go t.dhtAnnouncer()
}
// 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)
}
// 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),
}
}
}
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,
// 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
// 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)
}
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...)...)
}