package torrent import ( "context" "fmt" "strings" "time" dockerclient "github.com/docker/docker/client" "github.com/rs/zerolog/log" ) type PathMapper struct { containerPath string hostPath string } func NewPathMapper(containerName string, torrentClient TorrentClient) (*PathMapper, error) { if containerName == "" { savePath, err := torrentClient.DefaultSavePath() if err != nil { return nil, fmt.Errorf("getting default save path: %w", err) } log.Info().Str("path", savePath).Msg("no container configured, using direct path") return &PathMapper{containerPath: savePath, hostPath: savePath}, nil } savePath, err := torrentClient.DefaultSavePath() if err != nil { return nil, fmt.Errorf("getting default save path: %w", err) } ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) defer cancel() cli, err := dockerclient.NewClientWithOpts(dockerclient.FromEnv, dockerclient.WithAPIVersionNegotiation()) if err != nil { return nil, fmt.Errorf("creating docker client: %w", err) } defer cli.Close() inspect, err := cli.ContainerInspect(ctx, containerName) if err != nil { return nil, fmt.Errorf("inspecting container %s: %w", containerName, err) } hostPath := "" for _, mount := range inspect.Mounts { if mount.Destination == savePath { hostPath = mount.Source break } } if hostPath == "" { return nil, fmt.Errorf("no mount found for %s in container %s", savePath, containerName) } log.Info(). Str("container", containerName). Str("container_path", savePath). Str("host_path", hostPath). Msg("resolved download path mapping") return &PathMapper{containerPath: savePath, hostPath: hostPath}, nil } func (m *PathMapper) ToHost(containerPath string) string { if m.containerPath == m.hostPath { return containerPath } return strings.Replace(containerPath, m.containerPath, m.hostPath, 1) } func (m *PathMapper) ToContainer(hostPath string) string { if m.containerPath == m.hostPath { return hostPath } return strings.Replace(hostPath, m.hostPath, m.containerPath, 1) } func (m *PathMapper) HostDownloadPath() string { return m.hostPath } func (m *PathMapper) ContainerDownloadPath() string { return m.containerPath }