From c202f2eca32e1ab2e313417168351df1c58ee062 Mon Sep 17 00:00:00 2001 From: Mitch Riedstra Date: Wed, 4 Aug 2021 23:53:36 -0400 Subject: More major changes. Web UI works. Downloading games works. Status works. extractFile needs work --- steam/package.go | 181 ++++++++++++++++++------------------------------------- 1 file changed, 58 insertions(+), 123 deletions(-) (limited to 'steam/package.go') diff --git a/steam/package.go b/steam/package.go index df6f1e8..5b0c618 100644 --- a/steam/package.go +++ b/steam/package.go @@ -2,10 +2,11 @@ package steam import ( "archive/tar" + "errors" + "fmt" "io" - "os" "path/filepath" - "strings" + "time" ) const ( @@ -13,177 +14,111 @@ const ( defaultFileMode = 0644 ) -// Package writes the package to wr, returning errors if any -func (l *Library) Package(game string, wr io.WriteCloser) error { - g, ok := l.games[game] - if !ok { +func (l *Library) Package(game string, wr io.Writer) error { + if _, ok := l.Games()[game]; !ok { return E_GameDoesNotExist } + g := l.Games()[game] j := newJob("package", g) defer j.done() l.status.addJob(j) - acf, err := FindACF(l.folder, g.Name) - if err != nil { - j.addError(err) - return err - } + j.setSize(g.GetSizeBytes()) - twriter := tar.NewWriter(wr) - - paths := []string{ - filepath.Join(l.folder, "common", g.Name), - acf, - } - for _, pth := range paths { - err := filepath.Walk(pth, tarWalkfn(twriter, l.folder)) + // Invert the writer so we can break up the copy and get progress + // information in here + rdr, pwrtr := io.Pipe() + go func() { + err := l.packagePrimitive(j, g, pwrtr) if err != nil { j.addError(err) - return err } - } - - err = twriter.Flush() - if err != nil { - j.addError(err) - return err - } - - err = twriter.Close() - if err != nil { - j.addError(err) - return err - } - - err = wr.Close() - if err != nil { - j.addError(err) - } - return err -} - -// Extract will read a tarball from the io.Reader and install the game into -// the current library path. This offers no visibility into the progress, -// as it does not update the job status on the progress, though it will -// populate errors. -// -// Most callers will want to use ExtractHTTP or ExtractFile instead -func (l *Library) Extract(r io.Reader) (*Game, error) { - g := &Game{LibraryPath: l.folder} - j := newJob("extract", g) - defer j.done() - - l.status.addJob(j) - - return l.extractPrimitive(j, g, r) -} - -func (l *Library) extractPrimitive(j *Job, g *Game, r io.Reader) (*Game, error) { - treader := tar.NewReader(r) + }() + var total int64 for { - hdr, err := treader.Next() + n, err := io.CopyN(wr, rdr, updateEveryNBytes) + if err == io.EOF { - // We've reached the end! Whoee break } if err != nil { j.addError(err) - return nil, err + return err } - fileName := filepath.ToSlash(hdr.Name) + total += n + j.setTransferred(total) - if g.Name == "" { - s := strings.Split(fileName, "/") - if len(s) >= 2 { - g.Name = s[1] - } - } + elapsedSeconds := float64(time.Since(*j.StartTime()).Seconds()) + + rate := float64(total) / elapsedSeconds - fileName = filepath.Join(l.folder, fileName) + fmt.Println("rate in bytes/second: ", formatBytes(int64(rate))) - info := hdr.FileInfo() - if info.IsDir() { - // I don't like hard-coded permissions but it - // it helps with overall platform compatibility - err = os.MkdirAll(fileName, defaultDirectoryMode) - if err != nil { - j.addError(err) - return nil, err - } + estSize := j.GetSize() + if estSize == nil { + j.addError(errors.New("Expected an estimated size, got nil")) continue } - err = os.MkdirAll(filepath.Dir(fileName), defaultDirectoryMode) - if err != nil { - j.addError(err) - return nil, err - } + remainingBytes := float64(*estSize - total) + fmt.Println("remaining bytes: ", formatBytes(int64(remainingBytes))) - // Create a file handle to work with - f, err := os.OpenFile(fileName, os.O_CREATE|os.O_TRUNC|os.O_WRONLY, - defaultFileMode) - if err != nil { - j.addError(err) - return nil, err - } - if _, err := io.Copy(f, treader); err != nil { - j.addError(err) - f.Close() - return nil, err - } - f.Close() + seconds := (remainingBytes / rate) - } + duration := time.Duration(seconds * 1000 * 1000 * 1000) + fmt.Println("Raw duration: ", duration) - err := g.SetSizeInfo() - if err != nil { - j.addError(err) - return nil, err + j.setETA(duration) } - l.m.Lock() - l.games[g.Name] = g - l.m.Unlock() - - return g, nil + return nil } -// Delete removes all of the game files and the ACF -func (l *Library) Delete(game string) error { - g, ok := l.games[game] - if !ok { - return E_GameDoesNotExist +// Package writes the package to wr, returning errors if any +func (l *Library) packagePrimitive(j *Job, g *Game, wr io.WriteCloser) error { + + acf, err := FindACF(l.folder, g.Name) + if err != nil { + j.addError(err) + return err } - j := newJob("delete", g) - defer j.done() + twriter := tar.NewWriter(wr) - l.status.addJob(j) + paths := []string{ + filepath.Join(l.folder, "common", g.Name), + acf, + } + for _, pth := range paths { + err := filepath.Walk(pth, tarWalkfn(twriter, l.folder)) + if err != nil { + j.addError(err) + return err + } + } - acf, err := FindACF(l.folder, game) + err = twriter.Flush() if err != nil { j.addError(err) return err } - if err := os.Remove(acf); err != nil { + + err = twriter.Close() + if err != nil { j.addError(err) return err } - err = os.RemoveAll(filepath.Join(l.folder, "common", g.Name)) + err = wr.Close() if err != nil { j.addError(err) return err } - l.m.Lock() - delete(l.games, game) - l.m.Unlock() - - return nil + return err } -- cgit v1.2.3