diff --git a/src/config/config.go b/src/config/config.go index 2927cf8..972029b 100644 --- a/src/config/config.go +++ b/src/config/config.go @@ -78,6 +78,8 @@ type Slskd struct { URL string `env:"SLSKD_URL"` Retry int `env:"SLSKD_RETRY" env-default:"5"` // Number of times to check search status before skipping the track DownloadAttempts int `env:"SLSKD_DL_ATTEMPTS" env-default:"3"` // Max number of files to attempt downloading per track + SlskdDir string `env:"SLSKD_DIR" env-default:"/slskd/"` + MigrateDL bool `env:"MIGRATE_DOWNLOADS" env-default:"false"` // Move downloads from SlskdDir to DownloadDir Timeout time.Duration `env:"SLSKD_TIMEOUT" env-default:"20s"` Filters Filters } diff --git a/src/downloader/downloader.go b/src/downloader/downloader.go index 41d7d35..4bba759 100644 --- a/src/downloader/downloader.go +++ b/src/downloader/downloader.go @@ -7,6 +7,8 @@ import ( "strings" "regexp" "fmt" + "path/filepath" + "io" "golang.org/x/sync/errgroup" cfg "explo/src/config" @@ -33,7 +35,7 @@ func NewDownloader(cfg *cfg.DownloadConfig, httpClient *util.HttpClient) *Downlo case "youtube": downloader = append(downloader, NewYoutube(cfg.Youtube, cfg.Discovery, cfg.DownloadDir, httpClient)) case "slskd": - slskdClient := NewSlskd(cfg.Slskd) + slskdClient := NewSlskd(cfg.Slskd, cfg.DownloadDir) slskdClient.AddHeader() downloader = append(downloader, slskdClient) default: @@ -128,4 +130,60 @@ func getFilename(title, artist string) string { a := re.ReplaceAllString(artist, "_") return fmt.Sprintf("%s-%s",t,a) +} + +func moveDownload(srcDir, destDir, trackPath, file string) error { // Move download from the source dir to the dest dir (download dir) + trackDir := filepath.Join(srcDir, trackPath) + srcFile := filepath.Join(trackDir, file) + + info, err := os.Stat(srcFile) + if err != nil { + return fmt.Errorf("stat error: %s", err.Error()) + } + + in, err := os.Open(srcFile) + if err != nil { + return fmt.Errorf("couldn't open source file: %s", err.Error()) + } + + defer func() { + if cerr := in.Close(); cerr != nil { + log.Printf("warning: failed to close source file: %s", cerr) + } + }() + + dstDir := filepath.Join(destDir, trackPath) + if err = os.MkdirAll(dstDir, os.ModePerm); err != nil { + return fmt.Errorf("couldn't make download directory: %s", err.Error()) + } + + dstFile := filepath.Join(dstDir, file) + out, err := os.Create(dstFile) + if err != nil { + return fmt.Errorf("couldn't create destination file: %s", err.Error()) + } + + defer func() { + if err = out.Close(); err != nil { + log.Printf("failed to close destination file: %s", err.Error()) + } + }() + + if _, err = io.Copy(out, in); err != nil { + return fmt.Errorf("copy failed: %s", err.Error()) + } + + if err = out.Sync(); err != nil { + return fmt.Errorf("sync failed: %s", err.Error()) + } + + if err = os.Chmod(dstFile, info.Mode()); err != nil { + return fmt.Errorf("chmod failed: %s", err.Error()) + } + + if err = os.Remove(srcFile); err != nil { + return fmt.Errorf("failed to delete original file: %s", err.Error()) + } + + return nil } \ No newline at end of file diff --git a/src/downloader/slskd.go b/src/downloader/slskd.go index 5f6a48c..3459cae 100644 --- a/src/downloader/slskd.go +++ b/src/downloader/slskd.go @@ -96,12 +96,14 @@ type DownloadMonitor struct { type Slskd struct { Headers map[string]string HttpClient *util.HttpClient + DownloadDir string Cfg config.Slskd } -func NewSlskd(cfg config.Slskd) *Slskd { +func NewSlskd(cfg config.Slskd, downloadDir string) *Slskd { return &Slskd{Cfg: cfg, - HttpClient: util.NewHttp(util.HttpClientConfig{Timeout: cfg.Timeout})} + HttpClient: util.NewHttp(util.HttpClientConfig{Timeout: cfg.Timeout}), + DownloadDir: downloadDir,} } func (c *Slskd) AddHeader() { @@ -399,8 +401,14 @@ func (c *Slskd) MonitorDownloads(tracks []*models.Track) error { if fileStatus.BytesRemaining == 0 || fileStatus.PercentComplete == 100 || strings.Contains(fileStatus.State, "Succeeded") { track.Present = true log.Printf("[slskd] %s downloaded successfully", track.File) - track.File = parsePath(track.File) + file, path := parsePath(track.File) + if c.Cfg.MigrateDL { + if err = moveDownload(c.Cfg.SlskdDir, c.DownloadDir, path, file); err != nil { + debug.Debug(err.Error()) + } + } delete(progressMap, key) + track.File = file successDownloads += 1 continue @@ -479,8 +487,8 @@ func (c Slskd) deleteDownload(user, ID string) error { return nil } -func parsePath(p string) string { // parse filepath to downloaded format and return filename +func parsePath(p string) (string, string) { // parse filepath to downloaded format, return filename and parent dir p = strings.ReplaceAll(p, `\`, `/`) - return filepath.Base(p) + return filepath.Base(p), filepath.Base(filepath.Dir(p)) } \ No newline at end of file