· 2 years ago · Apr 17, 2023, 02:30 PM
1package main
2
3import (
4 "crypto/md5"
5 "encoding/hex"
6 "fmt"
7 "io"
8 "log"
9 "net/http"
10 "os"
11 "path/filepath"
12 "strconv"
13 "strings"
14 "time"
15)
16
17const cacheFolder = "cache"
18const secretKey = "YOUR_SECRET_KEY"
19
20type cacheFile struct {
21 path string
22 modifiedAt time.Time
23}
24
25var cache = make(map[string]*cacheFile)
26
27func main() {
28 http.HandleFunc("/play", playHandler)
29 http.ListenAndServe(":3857", nil)
30}
31
32func playHandler(w http.ResponseWriter, r *http.Request) {
33 uri := r.URL.Query().Get("uri")
34 unixTimeStr := r.URL.Query().Get("time")
35 sign := r.URL.Query().Get("sign")
36
37 if uri == "" || unixTimeStr == "" || sign == "" {
38 http.Error(w, "Bad request", http.StatusBadRequest)
39 return
40 }
41
42 unixTime, err := strconv.ParseInt(unixTimeStr, 10, 64)
43 if err != nil {
44 http.Error(w, "Bad request", http.StatusBadRequest)
45 return
46 }
47
48 fileName := getFileName(uri)
49 if fileName == "" {
50 http.Error(w, "Bad request", http.StatusBadRequest)
51 return
52 }
53
54 cacheFile, ok := cache[fileName]
55 if ok && time.Since(cacheFile.modifiedAt).Seconds() < 3600 {
56 // Found in cache, play existing file
57 go playFile(cacheFile.path)
58 w.WriteHeader(http.StatusCreated)
59 fmt.Fprint(w, `{"success":true}`)
60 return
61 }
62
63 // Download file from uri
64 err = downloadFile(uri, fileName)
65 if err != nil {
66 http.Error(w, "Audio download error", 523)
67 return
68 }
69
70 // Validate sign
71 filePath := filepath.Join(cacheFolder, fileName)
72 fileMD5, err := getMD5(filePath)
73 if err != nil {
74 os.Remove(filePath)
75 http.Error(w, "Bad signature", http.StatusUnauthorized)
76 return
77 }
78
79 expectedSign := getSign(unixTime, fileMD5)
80 if expectedSign != sign {
81 http.Error(w, "Bad signature", http.StatusUnauthorized)
82 return
83 }
84
85 cache[fileName] = &cacheFile{
86 path: filePath,
87 modifiedAt: time.Now(),
88 }
89
90 go playFile(filePath)
91
92 w.WriteHeader(http.StatusCreated)
93 fmt.Fprint(w, `{"success":true}`)
94}
95
96func downloadFile(uri, fileName string) error {
97 res, err := http.Get(uri)
98 if err != nil {
99 return err
100 }
101 defer res.Body.Close()
102
103 if res.StatusCode != http.StatusOK {
104 return fmt.Errorf("HTTP status %d", res.StatusCode)
105 }
106
107 filePath := filepath.Join(cacheFolder, fileName)
108 file, err := os.Create(filePath)
109 if err != nil {
110 return err
111 }
112 defer file.Close()
113
114 _, err = io.Copy(file, res.Body)
115 if err != nil {
116 os.Remove(filePath)
117 return err
118 }
119
120 return nil
121}
122
123func playFile(filePath string) {
124 err := playsound.PlaySound(filePath, 1)
125 if err != nil {
126 log.Println("Failed to play file:", err)
127 }
128}
129
130func getFileName(uri string) string {
131 if !strings.HasSuffix(uri, ".mp3") {
132 return ""
133 }
134
135 parts := strings.Split(uri, "/")
136 if len(parts) == 0 {
137 return ""
138 }
139
140 hasher := md5.New()
141 io.WriteString(hasher, parts[len(parts)-1])
142 return hex.EncodeToString(hasher.Sum(nil)) + ".mp3"
143}
144
145func getMD5(filePath string) (string, error) {
146 file, err := os.Open(filePath)
147 if err != nil {
148 return "", err
149 }
150 defer file.Close()
151
152 hasher := md5.New()
153 if _, err := io.Copy(hasher, file); err != nil {
154 return "", err
155 }
156
157 return hex.EncodeToString(hasher.Sum(nil)), nil
158}
159
160func getSign(unixTime int64, fileMD5 string) string {
161 hasher := md5.New()
162 io.WriteString(hasher, strconv.FormatInt(unixTime, 10)+fileMD5+secretKey)
163 return hex.EncodeToString(hasher.Sum(nil))
164}