xgfone / go-bt

Another pure golang implementation of BitTorrent library.
Apache License 2.0
52 stars 13 forks source link

Using metainfo.NewInfoFromFilePath on a directory results in "error copying" #1

Closed eyedeekay closed 3 years ago

eyedeekay commented 3 years ago

I am attempting to learn your library and am getting started by writing a .torrent file generation tool for your bttools suite. I am attempting to produce the bytes required to populate the metainfo.MetaInfo.InfoBytes field by creating the metainfo.Info object using metainfo.NewInfoFromFilePath function on a directory, then loop over the pieces and concatenate them together. This approach works if I'm creating a single-file torrent, but when I try to create a multi-file torrent from a directory, I get:

    error generating pieces: error copying .: read www: is a directory

You can see the code here:

https://github.com/xgfone/bttools/compare/master...eyedeekay:create

func CreateTorrent(ctx *cli.Context) error {
    dirs := ctx.Args().Slice()
    if len(dirs) > 1 {
        return fmt.Errorf("Input invalid, please use only one file or directory at ta time.")
    }
    info, err := metainfo.NewInfoFromFilePath(dirs[0], int64(ctx.Int("length")))
    if err != nil {
        return err
    }
    log.Println(info.CountPieces(), "pieces")
    var pieces []byte
    for n := 0; n < info.CountPieces(); n++ {
        pieces = append(pieces, info.Piece(n).Hash().Bytes()...)
    }
    meta := &metainfo.MetaInfo{
        InfoBytes: pieces,
    }
    infoc, err := meta.Info()
    if err != nil {
        return err
    }
    log.Println("Info generated", infoc.Name)
    return nil
}

The documentation says NewInfoFromFilePath returns a new Info from a file or directory so I think this must be unintentional, or I am misusing the function.

Thanks in advance for your help.

xgfone commented 3 years ago

Thank you for your test!

It is a bug when reading a directory to create a new Info instance. And It has been fixed @fd3825a.


  1. The function NewInfoFromFilePath() makes a file or directory into a Info struct, then you can get the torrent info bytes by using bencode.EncodeBytes(info), that's the value of the field InfoBytes of the struct type MetaInfo.
  2. The function MetaInfo.Info() decodes the torrent info bytes to the struct type Info.
  3. They are the opposite operations.

For the new command create, you want to generate a .torrent file from a file or directory. Right? You can do it like this:

func CreateTorrent(ctx *cli.Context) error {
    dirs := ctx.Args().Slice()
    if len(dirs) > 1 {
        return fmt.Errorf("Input invalid, please use only one file or directory at ta time.")
    }

    info, err := metainfo.NewInfoFromFilePath(dirs[0], int64(ctx.Int("length")))
    if err != nil {
        return err
    }

    var mi metainfo.MetaInfo
    mi.InfoBytes, err = bencode.EncodeBytes(info)
    if err != nil {
        return err
    }

    // Set the announce information.
    announces := ctx.StringSlice("announce")
    switch len(announces) {
    case 0:
    case 1:
        mi.Announce = announces[0]
    default:
        mi.AnnounceList = metainfo.AnnounceList{announces}
    }
    /// TODO: set the webseed information like above.
    /// TODO: set other fields of MetaInfo.

    var output io.WriteCloser = os.Stdout
    if o := ctx.String("output"); o != "" {
        output, err = os.OpenFile(o, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0600)
        if err != nil {
            return err
        }
        defer output.Close()
    }

    return mi.Write(output)
}
eyedeekay commented 3 years ago

I see, that helps a lot. Changed my implementation to match the correct way to use the Info and now the create command works! Sent a PR over in bttools, looking forward to hearing from you there.

xgfone commented 3 years ago

The bug has been fixed. See Release v0.4.1.