diff --git a/cmd/torrent_import.go b/cmd/torrent_import.go index d07f91b..46abfd0 100644 --- a/cmd/torrent_import.go +++ b/cmd/torrent_import.go @@ -18,11 +18,12 @@ import ( // RunTorrentImport cmd import torrents func RunTorrentImport() *cobra.Command { var command = &cobra.Command{ - Use: "import {rtorrent | deluge} --source-dir dir --qbit-dir dir2 [--skip-backup] [--dry-run]", + Use: "import {rtorrent | deluge | qbittorrent} --source-dir dir --qbit-dir dir2 [--skip-backup] [--dry-run]", Short: "Import torrents", - Long: `Import torrents with state from other clients [rtorrent, deluge]`, + Long: `Import torrents with state from other clients [rtorrent, deluge, qbittorrent]`, Example: ` qbt torrent import deluge --source-dir ~/.config/deluge/state/ --qbit-dir ~/.local/share/data/qBittorrent/BT_backup --dry-run - qbt torrent import rtorrent --source-dir ~/.sessions --qbit-dir ~/.local/share/data/qBittorrent/BT_backup --dry-run`, + qbt torrent import rtorrent --source-dir ~/.sessions --qbit-dir ~/.local/share/data/qBittorrent/BT_backup --dry-run + qbt torrent import qbittorrent --source-dir ./BT_backup --qbit-dir ~/.local/share/data/qBittorrent/BT_backup --dry-run`, Args: func(cmd *cobra.Command, args []string) error { if len(args) < 1 { return errors.New("requires a source client [rtorrent, deluge] as first argument") @@ -30,7 +31,7 @@ func RunTorrentImport() *cobra.Command { return cobra.OnlyValidArgs(cmd, args) }, - ValidArgs: []string{"rtorrent", "deluge"}, + ValidArgs: []string{"rtorrent", "deluge", "qbittorrent"}, } var ( @@ -63,6 +64,9 @@ func RunTorrentImport() *cobra.Command { case "rtorrent": imp = importer.NewRTorrentImporter() + case "qbittorrent": + imp = importer.NewQbittorrentImporter() + default: return fmt.Errorf("error: unsupported client: %s\n", source) } diff --git a/internal/importer/qbittorrent.go b/internal/importer/qbittorrent.go new file mode 100644 index 0000000..a2f5c5f --- /dev/null +++ b/internal/importer/qbittorrent.go @@ -0,0 +1,74 @@ +package importer + +import ( + "io/fs" + "log" + "os" + "path/filepath" + "strings" + + fsutil "github.com/ludviglundgren/qbittorrent-cli/internal/fs" + + "github.com/pkg/errors" +) + +type QbittorrentImport struct{} + +func NewQbittorrentImporter() Importer { + return &QbittorrentImport{} +} + +func (im *QbittorrentImport) Import(opts Options) error { + sourceDir := opts.SourceDir + + sourceDirInfo, err := os.Stat(sourceDir) + if err != nil { + if os.IsNotExist(err) { + return errors.Errorf("source directory does not exist: %s", sourceDir) + } + + return errors.Wrapf(err, "source directory error: %s\n", sourceDir) + } + + if !sourceDirInfo.IsDir() { + return errors.Errorf("source is a file, not a directory: %s\n", sourceDir) + } + + if err := fsutil.MkDirIfNotExists(opts.QbitDir); err != nil { + return errors.Wrapf(err, "qbit directory error: %s\n", opts.QbitDir) + } + + // TODO read from manifest.json to add categories and tags to client + + // TODO pause for execution and wait until qbit is killed + + err = filepath.WalkDir(sourceDir, func(path string, d fs.DirEntry, err error) error { + if err != nil { + return err + } + + if d.IsDir() { + return nil + } + + if !strings.HasSuffix(d.Name(), ".torrent") || !strings.HasSuffix(d.Name(), ".fastresume") { + return nil + } + + fileName := d.Name() + + outfile := filepath.Join(sourceDir, fileName) + + if err = fsutil.CopyFile(path, outfile); err != nil { + log.Printf("Could not copy file %s error %q\n", outfile, err) + return err + } + + return nil + }) + if err != nil { + return errors.Wrapf(err, "walk directory failure: %s\n", sourceDir) + } + + return nil +}