176 lines
4.3 KiB
Go
176 lines
4.3 KiB
Go
package download
|
|
|
|
import (
|
|
"fmt"
|
|
"io"
|
|
"net/http"
|
|
"os"
|
|
"path/filepath"
|
|
|
|
"rhemawavetech.com/rhemawave/internal/logger"
|
|
)
|
|
|
|
// DownloadStatus holds information related to downloads and is
|
|
// the container for information returned via channels within this
|
|
// package.
|
|
type DownloadStatus struct {
|
|
Max int64 // Total bytes to download
|
|
Cur int64 // Total 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/"
|
|
|
|
// First, calculate the total size of files that need to be downloaded
|
|
totalSize := int64(0)
|
|
filesToDownload := []string{}
|
|
|
|
for _, filename := range filenames {
|
|
destPath := filepath.Join(destDir, filename)
|
|
// Check if the file already exists
|
|
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
|
|
}
|
|
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
|
|
}
|
|
fileStat, err := os.Stat(destPath)
|
|
// If the file is incomplete, or does not exist
|
|
if os.IsNotExist(err) || fileStat.Size() != resp.ContentLength {
|
|
// Oh, and log a warning if the file is not complete
|
|
if !os.IsNotExist(err) {
|
|
logger.Warning("%s incomplete/corrupt. Adding to download queue.", filename)
|
|
} else {
|
|
logger.Info("%s not found. Adding to download queue.", filename)
|
|
}
|
|
|
|
// Append it to the list to download
|
|
filesToDownload = append(filesToDownload, filename)
|
|
// And increase the total bytes to download
|
|
totalSize += resp.ContentLength
|
|
} else if err != nil {
|
|
logger.Error("unknown error: %v", err)
|
|
} else {
|
|
logger.Error("File %s exists, skipping...", filename)
|
|
}
|
|
resp.Body.Close()
|
|
}
|
|
|
|
// Send initial status
|
|
progressChan <- DownloadStatus{
|
|
Max: totalSize,
|
|
Cur: 0,
|
|
Msg: "Starting download...",
|
|
}
|
|
|
|
totalDownloaded := int64(0)
|
|
|
|
// Download each file that doesn't exist
|
|
for _, filename := range filesToDownload {
|
|
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
|
|
}
|
|
|
|
// 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
|
|
totalDownloaded += int64(n)
|
|
progressChan <- DownloadStatus{
|
|
Max: totalSize,
|
|
Cur: totalDownloaded,
|
|
Msg: fmt.Sprintf("%s", filename),
|
|
}
|
|
}
|
|
|
|
out.Close()
|
|
|
|
// Send completion for this file
|
|
progressChan <- DownloadStatus{
|
|
Max: totalSize,
|
|
Cur: totalDownloaded,
|
|
Msg: "Finished Downloading",
|
|
}
|
|
}
|
|
|
|
// Send final status
|
|
progressChan <- DownloadStatus{
|
|
Max: totalSize,
|
|
Cur: totalDownloaded,
|
|
Msg: "All audio downloads completed!",
|
|
}
|
|
}
|