X-Git-Url: https://code.octet-stream.net/broadcaster/blobdiff_plain/7423c6c97eb5d6dc063e7185c50137bbb5e25a23..7b615b3c71825b5b229b78509a16db37e1d3f38d:/radio/files_machine.go diff --git a/radio/files_machine.go b/radio/files_machine.go new file mode 100644 index 0000000..143397a --- /dev/null +++ b/radio/files_machine.go @@ -0,0 +1,117 @@ +package main + +import ( + "code.octet-stream.net/broadcaster/internal/protocol" + "crypto/sha256" + "encoding/hex" + "io" + "log" + "net/http" + "os" + "path/filepath" +) + +type FilesMachine struct { + specs []protocol.FileSpec + cachePath string + missing []string +} + +func NewFilesMachine(cachePath string) FilesMachine { + if err := os.MkdirAll(cachePath, 0750); err != nil { + log.Fatal(err) + } + return FilesMachine{ + cachePath: cachePath, + } +} + +func (m *FilesMachine) UpdateSpecs(specs []protocol.FileSpec) { + m.specs = specs + m.RefreshMissing() +} + +func (m *FilesMachine) RefreshMissing() { + // Delete any files in the cache dir who are not in the spec + entries, err := os.ReadDir(m.cachePath) + if err != nil { + log.Fatal(err) + } + okay := make([]string, 0) + for _, file := range entries { + hash := "" + for _, spec := range m.specs { + if file.Name() == spec.Name { + hash = spec.Hash + break + } + } + // if we have an extraneous file, delete it + if hash == "" { + log.Println("Deleting extraneous cached audio file:", file.Name()) + os.Remove(filepath.Join(m.cachePath, file.Name())) + continue + } + // if the hash isn't right, delete it + f, err := os.Open(filepath.Join(m.cachePath, file.Name())) + if err != nil { + log.Fatal(err) + } + hasher := sha256.New() + io.Copy(hasher, f) + if hex.EncodeToString(hasher.Sum(nil)) != hash { + log.Println("Deleting cached audio file with incorrect hash:", file.Name()) + os.Remove(filepath.Join(m.cachePath, file.Name())) + } else { + okay = append(okay, file.Name()) + } + } + m.missing = nil + for _, spec := range m.specs { + missing := true + for _, file := range okay { + if spec.Name == file { + missing = false + } + } + if missing { + m.missing = append(m.missing, spec.Name) + } + } + if len(m.missing) > 1 { + log.Println(len(m.missing), "missing files") + } else if len(m.missing) == 1 { + log.Println("1 missing file") + } else { + log.Println("All files are in sync with server") + } + statusCollector.FilesInSync <- len(m.missing) == 0 +} + +func (m *FilesMachine) IsCacheComplete() bool { + return len(m.missing) == 0 +} + +func (m *FilesMachine) NextFile() string { + next, remainder := m.missing[0], m.missing[1:] + m.missing = remainder + return next +} + +func (m *FilesMachine) DownloadSingle(filename string, downloadResult chan<- error) { + log.Println("Downloading", filename) + out, err := os.Create(filepath.Join(m.cachePath, filename)) + if err != nil { + downloadResult <- err + return + } + defer out.Close() + resp, err := http.Get(config.ServerURL + "/file-downloads/" + filename) + if err != nil { + downloadResult <- err + return + } + defer resp.Body.Close() + _, err = io.Copy(out, resp.Body) + downloadResult <- err +}