Add download package

This commit is contained in:
NeilClack
2026-06-18 15:39:30 -05:00
parent 26e84a4d23
commit a5f9780f41
+160
View File
@@ -0,0 +1,160 @@
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!",
}
}