Skip to content

Commit cb19449

Browse files
committed
feat: add multi-file torrent saving support
1 parent 3ccd320 commit cb19449

File tree

1 file changed

+85
-2
lines changed

1 file changed

+85
-2
lines changed

pkg/file/file.go

Lines changed: 85 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -68,8 +68,7 @@ func (f *file) Save(pieces torrent.PieceManager, dst string) error {
6868
return f.saveSingleFile(pieces, dst)
6969
}
7070

71-
// TODO: add support for saving multifile torrents
72-
return nil
71+
return f.saveMultiFile(pieces, dst)
7372
}
7473

7574
// saveSingleFile saves a single-file torrent as a file, fetching the pieces
@@ -99,6 +98,90 @@ func (f *file) saveSingleFile(pieces torrent.PieceManager, dst string) error {
9998
return nil
10099
}
101100

101+
func (f *file) saveMultiFile(pieces torrent.PieceManager, dst string) error {
102+
fileIndex, left := 0, 0
103+
104+
var file *os.File
105+
defer file.Close()
106+
107+
var err error
108+
109+
// nextFile closes the current file and opens the next file
110+
nextFile := func() error {
111+
if file != nil {
112+
// close the current file
113+
file.Close()
114+
}
115+
116+
fileinfo := f.Info.Files[fileIndex]
117+
filepath := []string{dst}
118+
filepath = append(filepath, fileinfo.Path...)
119+
120+
// create new file
121+
file, err = os.Create(path.Join(filepath...))
122+
if err != nil {
123+
return err
124+
}
125+
126+
fileIndex++
127+
left = fileinfo.Length
128+
return nil
129+
}
130+
131+
// open the first file
132+
err = nextFile()
133+
if err != nil {
134+
return err
135+
}
136+
137+
pieceNum := len(f.Info.Pieces) / 20
138+
139+
pieceLoop:
140+
// loop through all the pieces
141+
for i := 0; i < pieceNum; i++ {
142+
143+
// get next piece
144+
piece, err := pieces.Get(i)
145+
if err != nil {
146+
return err
147+
}
148+
consumed := 0
149+
150+
// repeat until whole piece is consumed
151+
for {
152+
piece = piece[consumed:]
153+
length := len(piece)
154+
155+
switch {
156+
// current file will consume whole piece
157+
case left >= length:
158+
_, err := file.Write(piece)
159+
if err != nil {
160+
return err
161+
}
162+
163+
left -= length
164+
continue pieceLoop
165+
166+
// current file finished
167+
case left == 0:
168+
err := nextFile()
169+
if err != nil {
170+
return err
171+
}
172+
173+
// current file will consume a part of the piece
174+
case left < length:
175+
file.Write(piece[:left])
176+
consumed += left
177+
left = 0
178+
}
179+
}
180+
}
181+
182+
return nil
183+
}
184+
102185
// Torrent converts a file into a torrent.Torrent.
103186
func (f *file) Torrent() (*torrent.Torrent, error) {
104187
hash, err := f.Info.hash()

0 commit comments

Comments
 (0)