forked from Elara6331/infinitime
210 lines
4.4 KiB
Go
210 lines
4.4 KiB
Go
package infinitime
|
|
|
|
import (
|
|
"archive/zip"
|
|
"encoding/json"
|
|
"io"
|
|
"os"
|
|
"path/filepath"
|
|
|
|
"go.elara.ws/infinitime/blefs"
|
|
)
|
|
|
|
// ResourceOperation represents an operation performed during
|
|
// resource loading
|
|
type ResourceOperation uint8
|
|
|
|
const (
|
|
// ResourceOperationUpload represents the upload phase
|
|
// of resource loading
|
|
ResourceOperationUpload = iota
|
|
// ResourceOperationRemoveObsolete represents the obsolete
|
|
// file removal phase of resource loading
|
|
ResourceOperationRemoveObsolete
|
|
)
|
|
|
|
// ResourceManifest is the structure of the resource manifest file
|
|
type ResourceManifest struct {
|
|
Resources []Resource `json:"resources"`
|
|
Obsolete []ObsoleteResource `json:"obsolete_files"`
|
|
}
|
|
|
|
// Resource represents a resource entry in the manifest
|
|
type Resource struct {
|
|
Name string `json:"filename"`
|
|
Path string `json:"path"`
|
|
}
|
|
|
|
// ObsoleteResource represents an obsolete file entry in the manifest
|
|
type ObsoleteResource struct {
|
|
Path string `json:"path"`
|
|
Since string `json:"since"`
|
|
}
|
|
|
|
// ResourceLoadProgress contains information on the progress of
|
|
// a resource load
|
|
type ResourceLoadProgress struct {
|
|
Operation ResourceOperation
|
|
Name string
|
|
Total int64
|
|
Sent int64
|
|
Err error
|
|
}
|
|
|
|
// LoadResources accepts a resources zip file and a BLE FS.
|
|
// It loads the resources from the zip onto the FS.
|
|
func LoadResources(file *os.File, fs *blefs.FS) (<-chan ResourceLoadProgress, error) {
|
|
out := make(chan ResourceLoadProgress, 10)
|
|
|
|
fi, err := file.Stat()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
r, err := zip.NewReader(file, fi.Size())
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
m, err := r.Open("resources.json")
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
defer m.Close()
|
|
|
|
var manifest ResourceManifest
|
|
err = json.NewDecoder(m).Decode(&manifest)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
m.Close()
|
|
|
|
log.Debug("Decoded manifest file").Send()
|
|
|
|
go func() {
|
|
defer close(out)
|
|
|
|
for _, file := range manifest.Obsolete {
|
|
name := filepath.Base(file.Path)
|
|
|
|
log.Debug("Removing file").Str("file", file.Path).Send()
|
|
|
|
err := fs.RemoveAll(file.Path)
|
|
if err != nil {
|
|
out <- ResourceLoadProgress{
|
|
Name: name,
|
|
Operation: ResourceOperationRemoveObsolete,
|
|
Err: err,
|
|
}
|
|
return
|
|
}
|
|
|
|
log.Debug("Removed file").Str("file", file.Path).Send()
|
|
|
|
out <- ResourceLoadProgress{
|
|
Name: name,
|
|
Operation: ResourceOperationRemoveObsolete,
|
|
}
|
|
}
|
|
|
|
for _, file := range manifest.Resources {
|
|
src, err := r.Open(file.Name)
|
|
if err != nil {
|
|
out <- ResourceLoadProgress{
|
|
Name: file.Name,
|
|
Operation: ResourceOperationUpload,
|
|
Err: err,
|
|
}
|
|
return
|
|
}
|
|
|
|
srcFi, err := src.Stat()
|
|
if err != nil {
|
|
out <- ResourceLoadProgress{
|
|
Name: file.Name,
|
|
Operation: ResourceOperationUpload,
|
|
Total: srcFi.Size(),
|
|
Err: err,
|
|
}
|
|
src.Close()
|
|
return
|
|
}
|
|
|
|
log.Debug("Making directories").Str("file", file.Path).Send()
|
|
|
|
err = fs.MkdirAll(filepath.Dir(file.Path))
|
|
if err != nil {
|
|
log.Debug("Error making directories").Err(err).Send()
|
|
out <- ResourceLoadProgress{
|
|
Name: file.Name,
|
|
Operation: ResourceOperationUpload,
|
|
Total: srcFi.Size(),
|
|
Err: err,
|
|
}
|
|
src.Close()
|
|
return
|
|
}
|
|
|
|
log.Debug("Creating file").
|
|
Str("file", file.Path).
|
|
Int64("size", srcFi.Size()).
|
|
Send()
|
|
|
|
dst, err := fs.Create(file.Path, uint32(srcFi.Size()))
|
|
if err != nil {
|
|
log.Debug("Error creating file").Err(err).Send()
|
|
out <- ResourceLoadProgress{
|
|
Name: file.Name,
|
|
Operation: ResourceOperationUpload,
|
|
Total: srcFi.Size(),
|
|
Err: err,
|
|
}
|
|
src.Close()
|
|
return
|
|
}
|
|
|
|
progCh := dst.Progress()
|
|
go func() {
|
|
for sent := range progCh {
|
|
log.Debug("Progress event sent").
|
|
Int64("total", srcFi.Size()).
|
|
Uint32("sent", sent).
|
|
Send()
|
|
|
|
out <- ResourceLoadProgress{
|
|
Name: file.Name,
|
|
Operation: ResourceOperationUpload,
|
|
Total: srcFi.Size(),
|
|
Sent: int64(sent),
|
|
}
|
|
|
|
if sent == uint32(srcFi.Size()) {
|
|
return
|
|
}
|
|
}
|
|
}()
|
|
|
|
n, err := io.Copy(dst, src)
|
|
if err != nil {
|
|
log.Debug("Error writing to file").Err(err).Send()
|
|
out <- ResourceLoadProgress{
|
|
Name: file.Name,
|
|
Operation: ResourceOperationUpload,
|
|
Total: srcFi.Size(),
|
|
Sent: n,
|
|
Err: err,
|
|
}
|
|
src.Close()
|
|
dst.Close()
|
|
return
|
|
}
|
|
|
|
src.Close()
|
|
dst.Close()
|
|
}
|
|
}()
|
|
|
|
return out, nil
|
|
}
|