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