/
server.go
182 lines (155 loc) · 5.94 KB
/
server.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
package hammer
import (
"errors"
"fmt"
"net/url"
"os"
"github.com/curtisnewbie/hammer/api"
fstore "github.com/curtisnewbie/mini-fstore/client"
"github.com/curtisnewbie/miso/miso"
)
func BootstrapServer(args []string) {
miso.PreServerBootstrap(func(rail miso.Rail) error {
miso.SubEventBus(api.CompressImageTriggerEventBus, 2, ListenCompressImageEvent)
miso.SubEventBus(api.GenVideoThumbnailTriggerEventBus, 2, ListenGenVideoThumbnailEvent)
return nil
})
miso.BootstrapServer(os.Args)
}
func ListenCompressImageEvent(rail miso.Rail, evt api.ImageCompressTriggerEvent) error {
rail.Infof("Received CompressImageEvent: %+v", evt)
generatedFileId, err := CompressImage(rail, evt)
if err != nil {
return err
}
if evt.ReplyTo == "" {
rail.Warn("ImageCompressTriggerEvent.ReplyTo is empty")
return nil
}
// reply to the specified event bus
return miso.PubEventBus(rail,
api.ImageCompressReplyEvent{Identifier: evt.Identifier, FileId: generatedFileId},
evt.ReplyTo)
}
func ListenGenVideoThumbnailEvent(rail miso.Rail, evt api.GenVideoThumbnailTriggerEvent) error {
rail.Infof("Received GenVideoThumbnailTriggerEvent: %+v", evt)
generatedFileId, err := GenerateVideoThumbnail(rail, evt)
if err != nil {
return err
}
if evt.ReplyTo == "" {
rail.Warn("GenVideoThumbnailTriggerEvent.ReplyTo is empty")
return nil
}
// reply to the specified event bus
return miso.PubEventBus(rail,
api.GenVideoThumbnailReplyEvent{Identifier: evt.Identifier, FileId: generatedFileId},
evt.ReplyTo)
}
func CompressImage(rail miso.Rail, evt api.ImageCompressTriggerEvent) (string, error) {
originFile, err := fstore.FetchFileInfo(rail, fstore.FetchFileInfoReq{FileId: evt.FileId})
if err != nil {
if errors.Is(err, fstore.ErrFileDeleted) || errors.Is(err, fstore.ErrFileNotFound) {
rail.Warnf("File %v is not found or deleted, %v", evt.FileId, evt.Identifier)
return "", nil
}
return "", fmt.Errorf("failed to fetch fstore file info: %v, %v", evt.FileId, err)
}
// generate temp token for downloading file from mini-fstore
originFileToken, e := fstore.GenTempFileKey(rail, evt.FileId, "")
if e != nil {
rail.Errorf("Failed to GetFstoreTmpToken, %v", e)
return "", nil
}
rail.Infof("tkn: %v", originFileToken)
// download the origin file from mini-fstore
downloadPath := "/tmp/" + miso.RandNum(20)
downloadFile, err := os.Create(downloadPath)
if err != nil {
return "", err
}
defer downloadFile.Close()
if e := fstore.DownloadFile(rail, originFileToken, downloadFile); e != nil {
rail.Errorf("Failed to DownloadFstoreFile, %v", e)
return "", nil
}
rail.Infof("File downloaded to %v", downloadPath)
defer os.Remove(downloadPath)
// compress the origin image, if the compression failed, we just give up
compressPath := downloadPath + "_compressed"
if e := GiftCompressImage(rail, downloadPath, compressPath); e != nil {
rail.Errorf("Failed to compress image, giving up, %v", e)
return "", nil // don't retry
}
defer os.Remove(compressPath)
rail.Infof("Image %v compressed to %v", evt.Identifier, compressPath)
// upload the compressed image to mini-fstore
compressedFile, err := os.Open(compressPath)
if err != nil {
return "", fmt.Errorf("failed to open compressed file, file: %v, %v", compressPath, err)
}
defer compressedFile.Close()
uploadFileId, e := fstore.UploadFile(rail, originFile.Name+"_thumbnail", compressedFile)
if e != nil {
rail.Errorf("Failed to UploadFstoreFile, %v", e)
return "", fmt.Errorf("failed to upload fstore file, %v", e)
}
// exchange the uploadFileId with the real fileId
thumbnailFile, e := fstore.FetchFileInfo(rail, fstore.FetchFileInfoReq{UploadFileId: uploadFileId})
if e != nil {
rail.Errorf("Failed to FetchFstoreFileInfo, %v", e)
return "", fmt.Errorf("failed to fetch fstore file info, %v", e)
}
return thumbnailFile.FileId, nil
}
func GenerateVideoThumbnail(rail miso.Rail, evt api.GenVideoThumbnailTriggerEvent) (string, error) {
rail.Infof("Received GenVideoThumbnailTriggerEvent: %+v", evt)
originFile, err := fstore.FetchFileInfo(rail, fstore.FetchFileInfoReq{FileId: evt.FileId})
if err != nil {
if errors.Is(err, fstore.ErrFileDeleted) || errors.Is(err, fstore.ErrFileNotFound) {
rail.Warnf("File %v is not found or deleted, %v", evt.FileId, evt.Identifier)
return "", nil
}
return "", fmt.Errorf("failed to fetch fstore file info: %v, %v", evt.FileId, err)
}
// generate temp token for downloading file from mini-fstore
originFileToken, e := fstore.GenTempFileKey(rail, evt.FileId, "")
if e != nil {
rail.Errorf("Failed to GenTempFileKey, %v", e)
return "", nil
}
rail.Infof("tkn: %v", originFileToken)
// temp path for ffmpeg to extract first frame of the video
genPath := "/tmp/" + miso.RandNum(20) + ".png"
defer os.Remove(genPath)
// build url to stream the original file from mini-fstore
server, err := miso.SelectAnyServer(rail, "fstore")
if err != nil {
return "", err
}
baseUrl := server.BuildUrl("/file/stream")
streamUrl := baseUrl + "?key=" + url.QueryEscape(originFileToken)
if err := ExtractFirstFrame(rail, streamUrl, genPath); err != nil {
rail.Errorf("Failed to generate video thumbnail, giving up, %v", err)
return "", nil
}
rail.Infof("Video (%v)'s first frame is generated to %v", evt.Identifier, genPath)
// upload the compressed image to mini-fstore
genFile, err := os.Open(genPath)
if err != nil {
return "", fmt.Errorf("failed to open generated video thumbnail file: %v, %w", genFile, err)
}
defer genFile.Close()
uploadFileId, e := fstore.UploadFile(rail, originFile.Name+"_thumbnail", genFile)
if e != nil {
rail.Errorf("Failed to UploadFile, %v", e)
return "", fmt.Errorf("failed to upload fstore file, %v", e)
}
// exchange the uploadFileId with the real fileId
thumbnailFile, e := fstore.FetchFileInfo(rail, fstore.FetchFileInfoReq{UploadFileId: uploadFileId})
if e != nil {
rail.Errorf("Failed to FetchFileInfo, %v", e)
return "", fmt.Errorf("failed to fetch fstore file info, %w", e)
}
return thumbnailFile.FileId, nil
}