mirror of
https://github.com/zhaarey/apple-music-downloader.git
synced 2025-10-23 15:11:05 +00:00
Add post-download conversion feature
This commit is contained in:
119
main.go
119
main.go
@@ -635,6 +635,121 @@ func handleSearch(searchType string, queryParts []string, token string) (string,
|
||||
|
||||
// END: New functions for search functionality
|
||||
|
||||
// CONVERSION FEATURE: Determine if source codec is lossy (rough heuristic by extension/codec name).
|
||||
func isLossySource(ext string, codec string) bool {
|
||||
ext = strings.ToLower(ext)
|
||||
if ext == ".m4a" && (codec == "AAC" || strings.Contains(codec, "AAC") || strings.Contains(codec, "ATMOS")) {
|
||||
return true
|
||||
}
|
||||
if ext == ".mp3" || ext == ".opus" || ext == ".ogg" {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// CONVERSION FEATURE: Build ffmpeg arguments for desired target.
|
||||
func buildFFmpegArgs(ffmpegPath, inPath, outPath, targetFmt, extraArgs string) ([]string, error) {
|
||||
args := []string{"-y", "-i", inPath, "-vn"}
|
||||
switch targetFmt {
|
||||
case "flac":
|
||||
args = append(args, "-c:a", "flac")
|
||||
case "mp3":
|
||||
// VBR quality 2 ~ high quality
|
||||
args = append(args, "-c:a", "libmp3lame", "-qscale:a", "2")
|
||||
case "opus":
|
||||
// Medium/high quality
|
||||
args = append(args, "-c:a", "libopus", "-b:a", "192k", "-vbr", "on")
|
||||
case "wav":
|
||||
args = append(args, "-c:a", "pcm_s16le")
|
||||
case "copy":
|
||||
// Just container copy (probably pointless for same container)
|
||||
args = append(args, "-c", "copy")
|
||||
default:
|
||||
return nil, fmt.Errorf("unsupported convert-format: %s", targetFmt)
|
||||
}
|
||||
if extraArgs != "" {
|
||||
// naive split; for complex quoting you could enhance
|
||||
args = append(args, strings.Fields(extraArgs)...)
|
||||
}
|
||||
args = append(args, outPath)
|
||||
return args, nil
|
||||
}
|
||||
|
||||
// CONVERSION FEATURE: Perform conversion if enabled.
|
||||
func convertIfNeeded(track *task.Track) {
|
||||
if !Config.ConvertAfterDownload {
|
||||
return
|
||||
}
|
||||
if Config.ConvertFormat == "" {
|
||||
return
|
||||
}
|
||||
srcPath := track.SavePath
|
||||
if srcPath == "" {
|
||||
return
|
||||
}
|
||||
ext := strings.ToLower(filepath.Ext(srcPath))
|
||||
targetFmt := strings.ToLower(Config.ConvertFormat)
|
||||
|
||||
// Map extension for output
|
||||
if targetFmt == "copy" {
|
||||
fmt.Println("Convert (copy) requested; skipping because it produces no new format.")
|
||||
return
|
||||
}
|
||||
|
||||
if Config.ConvertSkipIfSourceMatch {
|
||||
if ext == "."+targetFmt {
|
||||
fmt.Printf("Conversion skipped (already %s)\n", targetFmt)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
outBase := strings.TrimSuffix(srcPath, ext)
|
||||
outPath := outBase + "." + targetFmt
|
||||
|
||||
// Warn about lossy -> lossless
|
||||
if Config.ConvertWarnLossyToLossless && (targetFmt == "flac" || targetFmt == "wav") &&
|
||||
isLossySource(ext, track.Codec) {
|
||||
fmt.Println("Warning: Converting lossy source to lossless container will not improve quality.")
|
||||
}
|
||||
|
||||
if _, err := exec.LookPath(Config.FFmpegPath); err != nil {
|
||||
fmt.Printf("ffmpeg not found at '%s'; skipping conversion.\n", Config.FFmpegPath)
|
||||
return
|
||||
}
|
||||
|
||||
args, err := buildFFmpegArgs(Config.FFmpegPath, srcPath, outPath, targetFmt, Config.ConvertExtraArgs)
|
||||
if err != nil {
|
||||
fmt.Println("Conversion config error:", err)
|
||||
return
|
||||
}
|
||||
|
||||
fmt.Printf("Converting -> %s ...\n", targetFmt)
|
||||
cmd := exec.Command(Config.FFmpegPath, args...)
|
||||
cmd.Stdout = nil
|
||||
cmd.Stderr = nil
|
||||
start := time.Now()
|
||||
if err := cmd.Run(); err != nil {
|
||||
fmt.Println("Conversion failed:", err)
|
||||
// leave original
|
||||
return
|
||||
}
|
||||
fmt.Printf("Conversion completed in %s: %s\n", time.Since(start).Truncate(time.Millisecond), filepath.Base(outPath))
|
||||
|
||||
if !Config.ConvertKeepOriginal {
|
||||
if err := os.Remove(srcPath); err != nil {
|
||||
fmt.Println("Failed to remove original after conversion:", err)
|
||||
} else {
|
||||
track.SavePath = outPath
|
||||
track.SaveName = filepath.Base(outPath)
|
||||
fmt.Println("Original removed.")
|
||||
}
|
||||
} else {
|
||||
// Keep both but point track to new file (optional decision)
|
||||
track.SavePath = outPath
|
||||
track.SaveName = filepath.Base(outPath)
|
||||
}
|
||||
}
|
||||
|
||||
func ripTrack(track *task.Track, token string, mediaUserToken string) {
|
||||
var err error
|
||||
counter.Total++
|
||||
@@ -837,6 +952,10 @@ func ripTrack(track *task.Track, token string, mediaUserToken string) {
|
||||
counter.Unavailable++
|
||||
return
|
||||
}
|
||||
|
||||
// CONVERSION FEATURE hook
|
||||
convertIfNeeded(track)
|
||||
|
||||
counter.Success++
|
||||
okDict[track.PreID] = append(okDict[track.PreID], track.TaskNum)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user