improve progress

This commit is contained in:
Celogeek 2021-12-27 11:39:14 +01:00
parent 222db9a4da
commit 423d99aaf6
Signed by: celogeek
GPG Key ID: E6B7BDCFC446233A
5 changed files with 187 additions and 68 deletions

View File

@ -0,0 +1,57 @@
package piwigo
import (
"os"
"path/filepath"
"strings"
)
type FileUploadResult struct {
ImageId int `json:"image_id"`
Url string `json:"url"`
}
type FileToUpload struct {
Dir string
Name string
CategoryId int
md5 *string
size *int64
ext *string
}
func (f *FileToUpload) FullPath() string {
return filepath.Join(f.Dir, f.Name)
}
func (f *FileToUpload) MD5() string {
if f.md5 == nil {
md5, err := Md5File(f.FullPath())
if err != nil {
return ""
}
f.md5 = &md5
}
return *f.md5
}
func (f *FileToUpload) Size() int64 {
if f.size == nil {
st, err := os.Stat(f.FullPath())
if err != nil {
return -1
}
size := st.Size()
f.size = &size
}
return *f.size
}
func (f *FileToUpload) Ext() string {
if f.ext == nil {
ext := strings.ToLower(filepath.Ext(f.Name)[1:])
f.ext = &ext
}
return *f.ext
}

View File

@ -0,0 +1,72 @@
package piwigo
import (
"fmt"
"sync"
"github.com/schollz/progressbar/v3"
)
type FileToUploadStat struct {
Checked int64
Total int64
TotalBytes int64
Uploaded int64
UploadedBytes int64
Skipped int64
Failed int64
Progress *progressbar.ProgressBar
mu sync.Mutex
}
func (s *FileToUploadStat) Refresh() {
s.Progress.Describe(fmt.Sprintf("check:%d, upload:%d, skip:%d, failed:%d, total:%d", s.Checked, s.Uploaded, s.Skipped, s.Failed, s.Total))
}
func (s *FileToUploadStat) Check() {
s.mu.Lock()
s.Checked++
s.Refresh()
s.mu.Unlock()
}
func (s *FileToUploadStat) Add(filesize int64) {
s.mu.Lock()
s.Total++
s.TotalBytes += filesize
s.Progress.ChangeMax64(s.TotalBytes)
s.Refresh()
s.mu.Unlock()
}
func (s *FileToUploadStat) Commit(filereaded int64) {
s.mu.Lock()
s.UploadedBytes += filereaded
s.Progress.Set64(s.UploadedBytes)
s.mu.Unlock()
}
func (s *FileToUploadStat) Done() {
s.mu.Lock()
s.Uploaded++
s.Refresh()
s.mu.Unlock()
}
func (s *FileToUploadStat) Close() {
s.Progress.Close()
}
func (s *FileToUploadStat) Fail() {
s.mu.Lock()
s.Failed++
s.Refresh()
s.mu.Unlock()
}
func (s *FileToUploadStat) Skip() {
s.mu.Lock()
s.Skipped++
s.Refresh()
s.mu.Unlock()
}

View File

@ -5,20 +5,13 @@ import (
"fmt" "fmt"
"io/ioutil" "io/ioutil"
"net/url" "net/url"
"os"
"path/filepath" "path/filepath"
"strings" "strings"
"sync" "sync"
"github.com/schollz/progressbar/v3"
"golang.org/x/text/unicode/norm" "golang.org/x/text/unicode/norm"
) )
type FileUploadResult struct {
ImageId int `json:"image_id"`
Url string `json:"url"`
}
func (p *Piwigo) FileExists(md5 string) bool { func (p *Piwigo) FileExists(md5 string) bool {
var resp map[string]*string var resp map[string]*string
@ -31,35 +24,32 @@ func (p *Piwigo) FileExists(md5 string) bool {
return resp[md5] != nil return resp[md5] != nil
} }
func (p *Piwigo) UploadChunks(filename string, nbJobs int, categoryId int) (*FileUploadResult, error) { func (p *Piwigo) Upload(file *FileToUpload, stat *FileToUploadStat, nbJobs int, hasVideoJS bool) error {
md5, err := Md5File(filename, false) if file.MD5() == "" {
if err != nil { stat.Fail()
return nil, err return errors.New("checksum error")
} }
if p.FileExists(md5) { stat.Check()
return nil, errors.New("file already exists")
if p.FileExists(file.MD5()) {
stat.Skip()
return errors.New("file already exists")
} }
st, _ := os.Stat(filename)
wg := &sync.WaitGroup{} wg := &sync.WaitGroup{}
chunks, err := Base64Chunker(filename) chunks, err := Base64Chunker(file.FullPath())
errout := make(chan error) errout := make(chan error)
bar := progressbar.DefaultBytes(
st.Size(),
"uploading",
)
if err != nil { if err != nil {
return nil, err return err
} }
for j := 0; j < nbJobs; j++ { for j := 0; j < nbJobs; j++ {
wg.Add(1) wg.Add(1)
go p.UploadChunk(md5, chunks, errout, wg, bar) go p.UploadChunk(file.MD5(), chunks, errout, wg, stat)
} }
go func() { go func() {
wg.Wait() wg.Wait()
bar.Close()
close(errout) close(errout)
}() }()
@ -68,30 +58,40 @@ func (p *Piwigo) UploadChunks(filename string, nbJobs int, categoryId int) (*Fil
errstring += err.Error() + "\n" errstring += err.Error() + "\n"
} }
if errstring != "" { if errstring != "" {
return nil, errors.New(errstring) stat.Fail()
return errors.New(errstring)
} }
exif, _ := Exif(filename) exif, _ := Exif(file.FullPath())
var resp *FileUploadResult var resp *FileUploadResult
data := &url.Values{} data := &url.Values{}
data.Set("original_sum", md5) data.Set("original_sum", file.MD5())
data.Set("original_filename", filepath.Base(filename)) data.Set("original_filename", file.Name)
data.Set("check_uniqueness", "true") data.Set("check_uniqueness", "true")
if exif != nil && exif.CreatedAt != nil { if exif != nil && exif.CreatedAt != nil {
data.Set("date_creation", exif.CreatedAt.String()) data.Set("date_creation", exif.CreatedAt.String())
} }
if categoryId > 0 { if file.CategoryId > 0 {
data.Set("categories", fmt.Sprint(categoryId)) data.Set("categories", fmt.Sprint(file.CategoryId))
} }
err = p.Post("pwg.images.add", data, &resp) err = p.Post("pwg.images.add", data, &resp)
if err != nil { if err != nil {
return nil, err stat.Fail()
return err
} }
return resp, nil if hasVideoJS {
switch file.Ext() {
case "ogg", "ogv", "mp4", "m4v", "webm", "webmv":
p.VideoJSSync(resp.ImageId)
}
}
stat.Done()
return nil
} }
func (p *Piwigo) UploadChunk(md5 string, chunks chan *Base64ChunkResult, errout chan error, wg *sync.WaitGroup, bar *progressbar.ProgressBar) { func (p *Piwigo) UploadChunk(md5 string, chunks chan *Base64ChunkResult, errout chan error, wg *sync.WaitGroup, progress *FileToUploadStat) {
defer wg.Done() defer wg.Done()
for chunk := range chunks { for chunk := range chunks {
var err error var err error
@ -107,7 +107,7 @@ func (p *Piwigo) UploadChunk(md5 string, chunks chan *Base64ChunkResult, errout
break break
} }
} }
bar.Add64(chunk.Size) progress.Commit(chunk.Size)
if err != nil { if err != nil {
errout <- fmt.Errorf("error on chunk %d: %v", chunk.Position, err) errout <- fmt.Errorf("error on chunk %d: %v", chunk.Position, err)
continue continue
@ -115,12 +115,6 @@ func (p *Piwigo) UploadChunk(md5 string, chunks chan *Base64ChunkResult, errout
} }
} }
type FileToUpload struct {
Filename string
Md5 string
CategoryId int
}
func (p *Piwigo) UploadTree(rootPath string, parentCategoryId int, level int, filter UploadFileType) ([]FileToUpload, error) { func (p *Piwigo) UploadTree(rootPath string, parentCategoryId int, level int, filter UploadFileType) ([]FileToUpload, error) {
rootPath, err := filepath.Abs(rootPath) rootPath, err := filepath.Abs(rootPath)
if err != nil { if err != nil {
@ -147,7 +141,7 @@ func (p *Piwigo) UploadTree(rootPath string, parentCategoryId int, level int, fi
continue continue
} }
filename := filepath.Join(rootPath, dir.Name()) filename := filepath.Join(rootPath, dir.Name())
md5, err := Md5File(filename, false) md5, err := Md5File(filename)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -158,8 +152,8 @@ func (p *Piwigo) UploadTree(rootPath string, parentCategoryId int, level int, fi
fmt.Printf("%s - %s %s - %s\n", levelStr, dir.Name(), md5, status) fmt.Printf("%s - %s %s - %s\n", levelStr, dir.Name(), md5, status)
if status == "OK" { if status == "OK" {
files = append(files, FileToUpload{ files = append(files, FileToUpload{
Filename: filename, Dir: rootPath,
Md5: md5, Name: dir.Name(),
CategoryId: parentCategoryId, CategoryId: parentCategoryId,
}) })
} }

View File

@ -11,8 +11,6 @@ import (
"net/url" "net/url"
"os" "os"
"strings" "strings"
"github.com/schollz/progressbar/v3"
) )
var CHUNK_BUFF_SIZE int64 = 32 * 1024 var CHUNK_BUFF_SIZE int64 = 32 * 1024
@ -39,26 +37,20 @@ func ArgsToForm(args []string) (*url.Values, error) {
return params, nil return params, nil
} }
func Md5File(filename string, progress bool) (string, error) { func Md5File(filename string) (result string, err error) {
file, err := os.Open(filename) file, err := os.Open(filename)
if err != nil { if err != nil {
return "", err return
} }
defer file.Close() defer file.Close()
st, _ := file.Stat()
hash := md5.New() hash := md5.New()
_, err = io.Copy(hash, file)
if progress {
bar := progressbar.DefaultBytes(st.Size(), "checksumming")
_, err = io.Copy(io.MultiWriter(hash, bar), file)
} else {
_, err = io.Copy(hash, file)
}
if err != nil { if err != nil {
return "", err return
} }
return fmt.Sprintf("%x", hash.Sum(nil)), nil result = fmt.Sprintf("%x", hash.Sum(nil))
return
} }
type Base64ChunkResult struct { type Base64ChunkResult struct {

View File

@ -2,11 +2,11 @@ package piwigocli
import ( import (
"errors" "errors"
"fmt"
"path/filepath" "path/filepath"
"strings" "strings"
"github.com/celogeek/piwigo-cli/internal/piwigo" "github.com/celogeek/piwigo-cli/internal/piwigo"
"github.com/schollz/progressbar/v3"
) )
type ImagesUploadCommand struct { type ImagesUploadCommand struct {
@ -31,21 +31,25 @@ func (c *ImagesUploadCommand) Execute(args []string) error {
return errors.New("unsupported file extension") return errors.New("unsupported file extension")
} }
resp, err := p.UploadChunks(c.Filename, c.NbJobs, c.CategoryId) _, hasVideoJS := status.Plugins["piwigo-videojs"]
file := &piwigo.FileToUpload{
Dir: filepath.Dir(c.Filename),
Name: filepath.Base(c.Filename),
CategoryId: c.CategoryId,
}
stat := &piwigo.FileToUploadStat{
Progress: progressbar.DefaultBytes(1, "prepare"),
}
defer stat.Close()
stat.Add(file.Size())
stat.Refresh()
err = p.Upload(file, stat, c.NbJobs, hasVideoJS)
if err != nil { if err != nil {
return err return err
} }
if _, ok := status.Plugins["piwigo-videojs"]; ok {
switch ext {
case "ogg", "ogv", "mp4", "m4v", "webm", "webmv":
fmt.Println("syncing metadata with videojs")
err = p.VideoJSSync(resp.ImageId)
if err != nil {
return err
}
}
}
return nil return nil
} }