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
|
// Package steam is designed to be a rather simplistic library to help pull
// information from a local steam library and run basic operations like
// archiving, restore and deleting games
package steam
import (
"errors"
"fmt"
"io/ioutil"
"regexp"
"sync"
)
var (
E_GameDoesNotExist = errors.New("Game does not exist")
E_BadURI = errors.New("The URI supplied is not understood")
E_OperationConflict = errors.New("Another conflicting job is running on this game right now")
)
// Library is used to represent the steam library, the Games map is populated
// by NewLibrary when called or when ProcessLibrary is called directly
// The key in the map is the same as the Game's `Name` property.
//
// Status contains various bits of information about previous jobs
// and the status of any currently running jobs
type Library struct {
folder string
games map[string]*Game
status *Jobs
m sync.Mutex
}
// Game represents an actual game in the steam Library. The purpose is only
// to provide info on a game.
type Game struct {
Name string `json:"Name" example:"Doom"`
LibraryPath string `json:"LibraryPath" example:"C:\\Program Files (x86)\\Steam\\steamapps"`
Size int64 `json:"Size" example:"12345"`
}
var slugregexp = regexp.MustCompile(`[^-0-9A-Za-z_:.]`)
// Slug returns a safer version of the name with spaces and other chars
// transformed into dashes for use in HTML element ids and such.
func (g Game) Slug() string {
return slugregexp.ReplaceAllString(g.Name, "-")
}
// NewLibrary returns a pointer to a processed library and an error
// if any
func NewLibrary(path string) (*Library, error) {
l := &Library{
status: &Jobs{
running: make([]*Job, 0),
previous: make([]*Job, 0),
},
}
err := l.ProcessLibrary(path)
if err != nil {
return nil, err
}
return l, err
}
// NewLibraryMust is the same as NewLibrary but calls panic if there
// is any error
func NewLibraryMust(path string) *Library {
l, err := NewLibrary(path)
if err != nil {
panic(err)
}
return l
}
// Folder returns the current folder on the disk that contains the steam library
func (l *Library) Folder() string {
l.m.Lock()
defer l.m.Unlock()
return l.folder
}
// Games returns a map of string[*Game] for the current library
func (l *Library) Games() map[string]*Game {
l.m.Lock()
defer l.m.Unlock()
return l.games
}
// Jobs returns the current *Jobs struct which can be used to keep track
// of any long running operations on the library as well as any errors
// encountered along the way
func (l *Library) Status() Jobs {
l.m.Lock()
defer l.m.Unlock()
return *l.status
}
// Refresh simply calls ProcessLibrary to refresh the entire contents of the
// steam library. Will return an error if any jobs are running
func (l *Library) Refresh() error {
return l.ProcessLibrary(l.folder)
}
// ProcessLibrary Populates the "Folder" and "Games" fields based on the
// provided directory. Returns an error if any jobs are currently running
func (s *Library) ProcessLibrary(r string) error {
if s.status.Running() {
return errors.New("Cannot process library with actions running")
}
if !hasCommon(r) {
return fmt.Errorf("No common directory in: %s", r)
}
s.m.Lock()
defer s.m.Unlock()
s.games = make(map[string]*Game)
dirs, err := ioutil.ReadDir(r + "/common")
if err != nil {
return err
}
s.folder = r
for _, f := range dirs {
if f.IsDir() {
g := &Game{
Name: f.Name(),
LibraryPath: r,
}
g.SetSizeInfo()
s.games[f.Name()] = g
}
}
return nil
}
func (s *Library) String() (str string) {
str = fmt.Sprintf("Library: %s\n", s.folder)
str = str + "----\n"
for _, v := range s.games {
str = str + fmt.Sprintf("%s\n", v.Name)
}
return
}
func hasCommon(d string) bool {
dirs, err := ioutil.ReadDir(d)
if err != nil {
return false
}
for _, f := range dirs {
if f.Name() == "common" && f.IsDir() {
return true
}
}
return false
}
|