]> code.octet-stream.net Git - broadcaster/blob - radio/files_machine.go
Configure CI
[broadcaster] / radio / files_machine.go
1 package main
2
3 import (
4 "crypto/sha256"
5 "encoding/hex"
6 "io"
7 "log"
8 "net/http"
9 "os"
10 "path/filepath"
11
12 "code.octet-stream.net/broadcaster/internal/protocol"
13 )
14
15 type FilesMachine struct {
16 specs []protocol.FileSpec
17 cachePath string
18 missing []string
19 }
20
21 func NewFilesMachine(cachePath string) FilesMachine {
22 if err := os.MkdirAll(cachePath, 0750); err != nil {
23 log.Fatal(err)
24 }
25 return FilesMachine{
26 cachePath: cachePath,
27 }
28 }
29
30 func (m *FilesMachine) UpdateSpecs(specs []protocol.FileSpec) {
31 m.specs = specs
32 m.RefreshMissing()
33 }
34
35 func (m *FilesMachine) RefreshMissing() {
36 // Delete any files in the cache dir who are not in the spec
37 entries, err := os.ReadDir(m.cachePath)
38 if err != nil {
39 log.Fatal(err)
40 }
41 okay := make([]string, 0)
42 for _, file := range entries {
43 hash := ""
44 for _, spec := range m.specs {
45 if file.Name() == spec.Name {
46 hash = spec.Hash
47 break
48 }
49 }
50 // if we have an extraneous file, delete it
51 if hash == "" {
52 log.Println("Deleting extraneous cached audio file:", file.Name())
53 os.Remove(filepath.Join(m.cachePath, file.Name()))
54 continue
55 }
56 // if the hash isn't right, delete it
57 f, err := os.Open(filepath.Join(m.cachePath, file.Name()))
58 if err != nil {
59 log.Fatal(err)
60 }
61 hasher := sha256.New()
62 io.Copy(hasher, f)
63 if hex.EncodeToString(hasher.Sum(nil)) != hash {
64 log.Println("Deleting cached audio file with incorrect hash:", file.Name())
65 os.Remove(filepath.Join(m.cachePath, file.Name()))
66 } else {
67 okay = append(okay, file.Name())
68 }
69 }
70 m.missing = nil
71 for _, spec := range m.specs {
72 missing := true
73 for _, file := range okay {
74 if spec.Name == file {
75 missing = false
76 }
77 }
78 if missing {
79 m.missing = append(m.missing, spec.Name)
80 }
81 }
82 if len(m.missing) > 1 {
83 log.Println(len(m.missing), "missing files")
84 } else if len(m.missing) == 1 {
85 log.Println("1 missing file")
86 } else {
87 log.Println("All files are in sync with server")
88 }
89 statusCollector.FilesInSync <- len(m.missing) == 0
90 }
91
92 func (m *FilesMachine) IsCacheComplete() bool {
93 return len(m.missing) == 0
94 }
95
96 func (m *FilesMachine) NextFile() string {
97 next, remainder := m.missing[0], m.missing[1:]
98 m.missing = remainder
99 return next
100 }
101
102 func (m *FilesMachine) DownloadSingle(filename string, downloadResult chan<- error) {
103 log.Println("Downloading", filename)
104 out, err := os.Create(filepath.Join(m.cachePath, filename))
105 if err != nil {
106 downloadResult <- err
107 return
108 }
109 defer out.Close()
110 resp, err := http.Get(config.ServerURL + "/file-downloads/" + filename)
111 if err != nil {
112 downloadResult <- err
113 return
114 }
115 defer resp.Body.Close()
116 _, err = io.Copy(out, resp.Body)
117 downloadResult <- err
118 }