161 lines
3.7 KiB
Go
161 lines
3.7 KiB
Go
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!",
|
|
}
|
|
}
|