package download import ( "fmt" "io" "net/http" "os" "path/filepath" ) // DownloadStatus holds information related to downloads and is // the container for information returned via channels within this // package. type DownloadStatus struct { Min int64 // Current file's bytes downloaded Max int64 // Current file's total size Cur int64 // Overall bytes downloaded Msg string // Status message Err error // Error, if any } // DownloadAudioFiles downloads multiple audio files and sends progress updates // to the provided channel. The channel is closed when all downloads are complete. func DownloadAudioFiles(progressChan chan<- DownloadStatus, filenames []string, destDir string) { defer close(progressChan) urlBase := "https://files.rhemawavetech.com/audio/" totalDownloaded := int64(0) totalSize := int64(0) // First, get the total size of all files for _, filename := range filenames { url := urlBase + filename resp, err := http.Head(url) if err != nil { progressChan <- DownloadStatus{ Msg: fmt.Sprintf("Failed to get size for %s: %v", filename, err), Err: err, } return } resp.Body.Close() if resp.StatusCode != http.StatusOK { progressChan <- DownloadStatus{ Msg: fmt.Sprintf("Failed to get size for %s: status %s", filename, resp.Status), Err: fmt.Errorf("bad status: %s", resp.Status), } return } totalSize += resp.ContentLength } // Send initial status progressChan <- DownloadStatus{ Min: 0, Max: totalSize, Cur: 0, Msg: "Starting download...", } // Download each file for _, filename := range filenames { url := urlBase + filename destPath := filepath.Join(destDir, filename) // Create the destination file out, err := os.Create(destPath) if err != nil { progressChan <- DownloadStatus{ Msg: fmt.Sprintf("Failed to create file %s: %v", destPath, err), Err: err, } return } // Get the file resp, err := http.Get(url) if err != nil { out.Close() os.Remove(destPath) progressChan <- DownloadStatus{ Msg: fmt.Sprintf("Failed to download %s: %v", filename, err), Err: err, } return } defer resp.Body.Close() if resp.StatusCode != http.StatusOK { out.Close() os.Remove(destPath) progressChan <- DownloadStatus{ Msg: fmt.Sprintf("Failed to download %s: status %s", filename, resp.Status), Err: fmt.Errorf("bad status: %s", resp.Status), } return } // Track progress for this file fileSize := resp.ContentLength downloaded := int64(0) // Use a buffered reader for efficiency buf := make([]byte, 8192) for { n, err := resp.Body.Read(buf) if err != nil && err != io.EOF { out.Close() os.Remove(destPath) progressChan <- DownloadStatus{ Msg: fmt.Sprintf("Failed to read %s: %v", filename, err), Err: err, } return } if n == 0 { break } // Write to file if _, err := out.Write(buf[:n]); err != nil { out.Close() os.Remove(destPath) progressChan <- DownloadStatus{ Msg: fmt.Sprintf("Failed to write %s: %v", filename, err), Err: err, } return } // Update progress downloaded += int64(n) totalDownloaded += int64(n) progressChan <- DownloadStatus{ Min: downloaded, Max: fileSize, Cur: totalDownloaded, Msg: fmt.Sprintf("Downloading %s...", filename), } } out.Close() // Send completion for this file progressChan <- DownloadStatus{ Min: fileSize, Max: fileSize, Cur: totalDownloaded, Msg: fmt.Sprintf("Finished downloading %s", filename), } } // Send final status progressChan <- DownloadStatus{ Min: totalSize, Max: totalSize, Cur: totalDownloaded, Msg: "All downloads completed!", } }