package qbittorrent import ( "context" "errors" "fmt" "log/slog" "os" "path" "slices" "git.kmsign.ru/royalcat/tstor/pkg/qbittorrent" "git.kmsign.ru/royalcat/tstor/pkg/rlog" ) func (d *Daemon) Cleanup(ctx context.Context, run bool) ([]string, error) { d.log.Info(ctx, "cleanup started") torrentInfos, err := d.client.qb.Torrent().GetTorrents(ctx, &qbittorrent.TorrentOption{}) if err != nil { d.log.Error(ctx, "failed to get torrents", rlog.Error(err)) return nil, fmt.Errorf("failed to get torrents: %w", err) } daemonsHashes := []string{} for _, info := range torrentInfos { daemonsHashes = append(daemonsHashes, info.Hash) } dataDirs, err := os.ReadDir(d.dataDir) if err != nil { d.log.Error(ctx, "failed to read data directory", slog.String("path", d.dataDir), rlog.Error(err)) return nil, fmt.Errorf("failed to read data directory: %w", err) } dataHashes := []string{} for _, entry := range dataDirs { dataHashes = append(dataHashes, entry.Name()) } hashToDelete := make([]string, 0, 5) for _, v := range dataHashes { if !slices.Contains(daemonsHashes, v) { hashToDelete = append(hashToDelete, v) } } d.log.Info(ctx, "marked torrents to delete", slog.Int("count", len(hashToDelete)), slog.Any("infohashes", hashToDelete), ) if !run { d.log.Info(ctx, "dry run, skipping deletion") return hashToDelete, nil } for _, hash := range hashToDelete { d.log.Info(ctx, "deleting stale torrent data", slog.String("infohash", hash)) err := os.RemoveAll(path.Join(d.dataDir, hash)) if err != nil { d.log.Error(ctx, "failed to delete torrent data", slog.String("infohash", hash), rlog.Error(err)) return nil, fmt.Errorf("failed to delete torrent data: %w", err) } } return hashToDelete, nil } func (d *Daemon) CleanupUnregistred(ctx context.Context, run bool) ([]string, error) { d.log.Info(ctx, "cleanup started") torrentInfos, err := d.client.qb.Torrent().GetTorrents(ctx, &qbittorrent.TorrentOption{}) if err != nil { d.log.Error(ctx, "failed to get torrents", rlog.Error(err)) return nil, fmt.Errorf("failed to get torrents: %w", err) } torrentToDelete := make([]string, 0, 5) for _, info := range torrentInfos { if d.registeredTorrents.Contains(info.Hash) { continue } d.log.Info(ctx, "torrent not found in registry", slog.String("infohash", info.Hash)) torrentToDelete = append(torrentToDelete, info.Hash) } d.log.Info(ctx, "marked torrents to delete", slog.Int("count", len(torrentToDelete)), slog.Any("infohashes", torrentToDelete), ) if !run { d.log.Info(ctx, "dry run, skipping deletion") return torrentToDelete, nil } err = d.client.qb.Torrent().DeleteTorrents(ctx, torrentToDelete, true) if err != nil { d.log.Error(ctx, "failed to delete torrents", slog.Any("infohashes", torrentToDelete), rlog.Error(err)) return nil, fmt.Errorf("failed to delete torrents: %w", err) } d.log.Info(ctx, "torrents deleted from qbittorrent", slog.Int("count", len(torrentToDelete))) for _, hash := range torrentToDelete { torrentPath := path.Join(d.dataDir, hash) _, err := os.Stat(torrentPath) if errors.Is(err, os.ErrNotExist) { continue } if err != nil { d.log.Error(ctx, "failed to get torrent path", slog.String("path", torrentPath), rlog.Error(err)) continue } d.log.Warn(ctx, "leftover data for torrent detected, cleaning up", slog.String("infohash", hash), slog.String("path", torrentPath)) } return torrentToDelete, nil }