Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@ type DownloadConfig struct {
Slskd Slskd
ExcludeLocal bool
DownloadLimiter int `env:"DOWNLOAD_LIMITER" env-default:"1"` // rate limit download operations
OverwriteMetadata bool `env:"OVERWRITE_METADATA" env-default:"false"` // overwrite metadata when migrating downloads
OverwriteMetadata bool `env:"OVERWRITE_METADATA" env-default:"true"` // rewrite clean tags on every finished download
KeepPermissions bool `env:"KEEP_PERMISSIONS" env-default:"true"` // keep original file permissions when migrating download
RenameTrack bool `env:"RENAME_TRACK" env-default:"false"` // Rename track in {title}-{artist} format
UseSubDir bool `env:"USE_SUBDIRECTORY" env-default:"true"`
Expand Down
73 changes: 49 additions & 24 deletions src/downloader/downloader.go
Original file line number Diff line number Diff line change
Expand Up @@ -246,20 +246,36 @@ func buildTrackPath(template string, track *models.Track) string {
return filepath.Clean(result)
}

func (c *DownloadClient) MoveDownload(srcDir, destDir, trackPath string, track *models.Track) error {
trackDir := filepath.Join(srcDir, trackPath)
srcFile := filepath.Join(trackDir, track.File)
func (c *DownloadClient) FinalizeDownload(monCfg MonitorConfig, trackPath string, track *models.Track) error {
srcFile := filepath.Join(monCfg.FromDir, trackPath, track.File)

if c.Cfg.RenameTrack { // Rename file to {title}-{artist} format
track.File = getFilename(track.CleanTitle, track.MainArtist) + filepath.Ext(track.File)
}
if c.Cfg.OverwriteMetadata {
metadata := util.BuildffmpegMetadata(*track)
if err := overwriteMetadata(metadata, srcFile); err != nil {
slog.Info(fmt.Sprintf("Writing clean metadata - %s", track.CleanTitle))
if err := overwriteMetadata(util.BuildffmpegMetadata(*track), track.CoverPath, srcFile); err != nil {
slog.Warn("problem overwriting metadata", "msg", err.Error())
}
}

if c.Cfg.RenameTrack {
newFile := getFilename(track.CleanTitle, track.MainArtist) + filepath.Ext(track.File)
if newFile != track.File {
if err := os.Rename(srcFile, filepath.Join(monCfg.FromDir, trackPath, newFile)); err != nil {
return fmt.Errorf("failed to rename track: %w", err)
}
track.File = newFile
}
}

if monCfg.MigrateDownload {
return c.MoveDownload(monCfg.FromDir, monCfg.ToDir, trackPath, track)
}
return nil
}

func (c *DownloadClient) MoveDownload(srcDir, destDir, trackPath string, track *models.Track) error {
trackDir := filepath.Join(srcDir, trackPath)
srcFile := filepath.Join(trackDir, track.File)

in, err := os.Open(srcFile)
if err != nil {
return fmt.Errorf("couldn't open source file: %s", err.Error())
Expand Down Expand Up @@ -338,26 +354,35 @@ func (c *DownloadClient) MoveDownload(srcDir, destDir, trackPath string, track *
return nil
}

func overwriteMetadata(metadata []string, srcFile string) error {
func overwriteMetadata(metadata []string, coverPath, srcFile string) error {
opts := ffmpeg.KwArgs{
"c": "copy",
"metadata": metadata,
"loglevel": "error",
}
streams := []*ffmpeg.Stream{
ffmpeg.Input(srcFile),
"c": "copy",
"map_metadata": "-1",

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think erasing all the file metadata is wrong at the moment, I'd prefer just overwriting the tags Explo has metadata for, the file itself could have some useful metadata already embedded (i.e custom playlist track could already have a MBID set) or some tracks might have lyrics embedded.

"metadata": metadata,
"loglevel": "error",
}
streams := []*ffmpeg.Stream{ffmpeg.Input(srcFile)}

if coverPath != "" {
if _, err := os.Stat(coverPath); err == nil {
streams = append(streams, ffmpeg.Input(coverPath))
opts["map"] = "-0:v?"
opts["disposition:v"] = "attached_pic"
if strings.EqualFold(filepath.Ext(srcFile), ".mp3") {
opts["id3v2_version"] = "3"
}
}
}

tmpFile := tempAudioFile(srcFile)
tmpFile := tempAudioFile(srcFile)

if err := util.WriteMetadata(streams, "", tmpFile, opts); err != nil {
return fmt.Errorf("failed to overwrite metadata: %w", err)
} else {
if err := os.Rename(tmpFile, srcFile); err != nil {
return fmt.Errorf("failed to rename tmp file: %w", err)
}
}
return nil
if err := util.WriteMetadata(streams, "", tmpFile, opts); err != nil {
return fmt.Errorf("failed to overwrite metadata: %w", err)
}
if err := os.Rename(tmpFile, srcFile); err != nil {
return fmt.Errorf("failed to rename tmp file: %w", err)
}
return nil
}

func tempAudioFile(path string) string {
Expand Down
21 changes: 13 additions & 8 deletions src/downloader/monitor.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import (
type Monitor interface {
GetDownloadStatus([]*models.Track) (map[string]FileStatus, error)
GetConf() (MonitorConfig, error)
RetryDownload(*models.Track) (bool, error)
Cleanup(models.Track, string) error
}

Expand Down Expand Up @@ -88,12 +89,8 @@ func (c *DownloadClient) MonitorDownloads(tracks []*models.Track, m Monitor) err
slog.Info("[monitor] file downloaded successfully", "service", monCfg.Service, "file", track.File)
var path string
track.File, path = parsePath(track.File)
if monCfg.MigrateDownload {
if err = c.MoveDownload(monCfg.FromDir, monCfg.ToDir, path, track); err != nil {
slog.Error("error while moving file", "err", err.Error())
} else {
slog.Info("track moved successfully", "service", monCfg.Service)
}
if err = c.FinalizeDownload(monCfg, path, track); err != nil {
slog.Error("error finalizing download", "service", monCfg.Service, "err", err.Error())
}
delete(progressMap, key)
successDownloads += 1
Expand All @@ -109,11 +106,19 @@ func (c *DownloadClient) MonitorDownloads(tracks []*models.Track, m Monitor) err
continue

} else if monitoredTime > monCfg.MonitorDuration || fileStatus.State == "Errored" {
slog.Info("[monitor] no download progress for file, skipping", "service", monCfg.Service, "file", track.File, "state", fileStatus.State, "duration", monitoredTime,)
tracker.Skipped = true
if err = m.Cleanup(*track, fileStatus.ID); err != nil {
slog.Debug("cleanup failed", logging.RuntimeAttr(err.Error()))
}
if retried, _ := m.RetryDownload(track); retried {
slog.Info("[monitor] source failed, trying next", "service", monCfg.Service, "title", track.CleanTitle, "state", fileStatus.State)
// track.File changed, re-track under the new key
delete(progressMap, key)
newKey := fmt.Sprintf("%s|%s", track.ID, track.File)
progressMap[newKey] = &DownloadMonitor{LastUpdated: currentTime}
continue
}
slog.Info("[monitor] no more sources, skipping", "service", monCfg.Service, "file", track.File, "state", fileStatus.State, "duration", monitoredTime)
tracker.Skipped = true
continue
}
}
Expand Down
Loading
Loading