anacrolix / torrent

Full-featured BitTorrent client package and utilities
Mozilla Public License 2.0
5.39k stars 615 forks source link

Unable to selectively download files within a torrent #940

Closed sweetbbak closed 2 months ago

sweetbbak commented 2 months ago

I'm trying to selectively download files within a multi-file torrent and even with a minimal example all files are still downloaded. I need to do more testing, but I also had tried to set the priority of all pieces to None, and then tried to download a single file like in the example below and even in that case it seems inconsistent on whether all or no files are downloaded... and the zeroed out dummy files are created regardless.

Minimal example

package main

import (
    "fmt"
    "log"
    "os"

    "github.com/anacrolix/torrent"
)

func Run() {
    cfg := torrent.NewDefaultClientConfig()
    cfg.Seed = true

    client, err := torrent.NewClient(cfg)
    if err != nil {
        log.Println(err)
    }

    t, err := client.AddMagnet(os.Args[1])
    if err != nil {
        log.Println(err)
    }

    <-t.GotInfo()

    for i, f := range t.Files() {
        fmt.Println(f.Path())

        // bad example but you get the idea
                if i == 3 {
            f.Download()
        }
    }

    for {
        client.WriteStatus(os.Stdout)
    }
}

func main() {
    Run()
}
anacrolix commented 2 months ago

With v1 BitTorrent, more than 1 file can share a piece. Those files will be created, and the data for overlapping pieces written to them in those cases. Zero-length files are always created by storages, since there's no actual data download that would otherwise trigger their creation.

sweetbbak commented 2 months ago

Thank you for the insight. That possibility didn't occur to me.

I tested it out with something like this

    fi := t.Files()[1]
    b := fi.BeginPieceIndex()
    e := fi.EndPieceIndex()
    t.DownloadPieces(b, e)

    for {
        fmt.Printf("pieces [%v - %v]\n", b, e)

        for i := b; i < e; i++ {
            ps := t.PieceState(i)

            if ps.Complete {
                fmt.Print("\x1b[33m|\x1b[0m")
            } else {
                fmt.Print("\x1b[31m|\x1b[0m")
            }
        }

        time.Sleep(time.Millisecond * 99)
        println("\x1b[2J\x1b[H")
    }

and got

pieces [53 - 54]

which ended up being associated with 3 different files, one of which was complete, one incomplete but mostly put together and one that was mostly zeroed out and had a small chunk of data way at the bottom. thats really interesting how that works. I'll have to go and try to figure out how other torrent clients handle this problem. Thanks.

anacrolix commented 2 months ago

They may stage the data in a different location and copy it out on demand, but I doubt it.

Interestingly in BitTorrent v2 this doesn't occur, but it's hardly a misfeature in the first place.