diff --git a/cmd/files.go b/cmd/files.go index 3df6b0b..d7e75ea 100644 --- a/cmd/files.go +++ b/cmd/files.go @@ -35,6 +35,24 @@ var ( extensions = [6]string{".jpg", ".jpeg", ".png", ".gif", ".webp", ".bmp"} ) +type Index struct { + Mutex sync.RWMutex + List []string +} + +func (i *Index) GenerateCache(args []string) error { + filters := &Filters{} + + i.Mutex.Lock() + i.List = []string{} + i.Mutex.Unlock() + + fmt.Printf("%v | Preparing image cache...\n", time.Now().Format(LogDate)) + _, err := pickFile(args, filters, "", i) + + return err +} + type Dimensions struct { Width int Height int @@ -51,63 +69,6 @@ type ScanStats struct { DirectoriesMatched uint64 } -type TimesServed struct { - File string - Size string - Served uint64 - Timestamps []string -} - -type ServeStats struct { - Served uint64 - List []string - Count map[string]uint64 - FileSize map[string]string - Timestamps map[string][]string -} - -func (s *ServeStats) GetFilesTotal() uint64 { - return atomic.LoadUint64(&s.Served) -} - -func (s *ServeStats) IncrementCounter(image string, timestamp time.Time, filesize string) { - s.Served += 1 - - s.Count[image] += 1 - - s.Timestamps[image] = append(s.Timestamps[image], timestamp.Format(LogDate)) - - _, exists := s.FileSize[image] - if !exists { - s.FileSize[image] = filesize - } - - if !contains(s.List, image) { - s.List = append(s.List, image) - } -} - -func (s *ServeStats) ListImages() ([]byte, error) { - a := []TimesServed{} - - sortedList := s.List - - sort.SliceStable(sortedList, func(p, q int) bool { - return sortedList[p] < sortedList[q] - }) - - for _, image := range s.List { - a = append(a, TimesServed{image, s.FileSize[image], s.Count[image], s.Timestamps[image]}) - } - - r, err := json.MarshalIndent(a, "", " ") - if err != nil { - return []byte{}, err - } - - return r, nil -} - func (s *ScanStats) GetFilesTotal() uint64 { return atomic.LoadUint64(&s.FilesMatched) + atomic.LoadUint64(&s.FilesSkipped) } @@ -136,6 +97,65 @@ func (s *ScanStats) GetDirectoriesMatched() uint64 { return atomic.LoadUint64(&s.DirectoriesMatched) } +type ServeStats struct { + Mutex sync.RWMutex + List []string + Count map[string]uint64 + Size map[string]string + Times map[string][]string +} + +type TimesServed struct { + File string + Served uint64 + Size string + Times []string +} + +func (s *ServeStats) IncrementCounter(image string, timestamp time.Time, filesize string) { + s.Mutex.Lock() + + s.Count[image]++ + + s.Times[image] = append(s.Times[image], timestamp.Format(LogDate)) + + _, exists := s.Size[image] + if !exists { + s.Size[image] = filesize + } + + if !contains(s.List, image) { + s.List = append(s.List, image) + } + + s.Mutex.Unlock() +} + +func (s *ServeStats) ListImages() ([]byte, error) { + s.Mutex.RLock() + + sortedList := s.List + + sort.SliceStable(sortedList, func(p, q int) bool { + return sortedList[p] < sortedList[q] + }) + + a := []TimesServed{} + + for _, image := range s.List { + a = append(a, TimesServed{image, s.Count[image], s.Size[image], s.Times[image]}) + } + + s.Mutex.RUnlock() + + r, err := json.MarshalIndent(a, "", " ") + if err != nil { + return []byte{}, err + } + + return r, nil +} + type Path struct { Base string Number int @@ -279,8 +299,8 @@ func appendPaths(path string, files *Files, filters *Filters, stats *ScanStats) return nil } -func getNewFile(paths []string, filters *Filters, sortOrder string, regexes *Regexes, fileCache *[]string) (string, error) { - filePath, err := pickFile(paths, filters, sortOrder, fileCache) +func getNewFile(paths []string, filters *Filters, sortOrder string, regexes *Regexes, index *Index) (string, error) { + filePath, err := pickFile(paths, filters, sortOrder, index) if err != nil { return "", nil } @@ -547,11 +567,11 @@ func prepareDirectories(files *Files, sort string) []string { return directories } -func pickFile(args []string, filters *Filters, sort string, fileCache *[]string) (string, error) { +func pickFile(args []string, filters *Filters, sort string, index *Index) (string, error) { var fileList []string - if Cache && filters.IsEmpty() && len(*fileCache) != 0 { - fileList = *fileCache + if Cache && filters.IsEmpty() && len(index.List) != 0 { + fileList = index.List } else { files := &Files{ List: make(map[string][]string), @@ -585,8 +605,11 @@ func pickFile(args []string, filters *Filters, sort string, fileCache *[]string) fileList = prepareDirectories(files, sort) if Cache { - *fileCache = append(*fileCache, fileList...) + index.Mutex.Lock() + index.List = append(index.List, fileList...) + index.Mutex.Unlock() } + } fileCount := len(fileList) diff --git a/cmd/version.go b/cmd/version.go index 61a71c2..736e063 100644 --- a/cmd/version.go +++ b/cmd/version.go @@ -10,7 +10,7 @@ import ( "github.com/spf13/cobra" ) -var Version = "0.31.3" +var Version = "0.32.0" func init() { rootCmd.AddCommand(versionCmd) diff --git a/cmd/web.go b/cmd/web.go index c999a21..a335cb0 100644 --- a/cmd/web.go +++ b/cmd/web.go @@ -17,6 +17,7 @@ import ( "runtime" "strconv" "strings" + "sync" "time" "github.com/yosssi/gohtml" @@ -333,20 +334,9 @@ func serveStaticFile(w http.ResponseWriter, r *http.Request, paths []string, sta return nil } -func generateCache(args []string, fileCache *[]string) error { - filters := &Filters{} - - fileCache = &[]string{} - - fmt.Printf("%v | Preparing image cache...\n", time.Now().Format(LogDate)) - _, err := pickFile(args, filters, "", fileCache) - - return err -} - -func serveCacheClearHandler(args []string, fileCache *[]string) http.HandlerFunc { +func serveCacheClearHandler(args []string, index *Index) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { - err := generateCache(args, fileCache) + err := index.GenerateCache(args) if err != nil { fmt.Println(err) } @@ -357,17 +347,6 @@ func serveCacheClearHandler(args []string, fileCache *[]string) http.HandlerFunc } } -func serveStats(args []string, fileCache *[]string) error { - filters := &Filters{} - - fileCache = &[]string{} - - fmt.Printf("%v | Preparing image cache...\n", time.Now().Format(LogDate)) - _, err := pickFile(args, filters, "", fileCache) - - return err -} - func serveStatsHandler(args []string, stats *ServeStats) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { w.WriteHeader(http.StatusOK) @@ -391,7 +370,7 @@ func serveStaticFileHandler(paths []string, stats *ServeStats) http.HandlerFunc } } -func serveHtmlHandler(paths []string, regexes *Regexes, fileCache *[]string) http.HandlerFunc { +func serveHtmlHandler(paths []string, regexes *Regexes, index *Index) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { refererUri, err := stripQueryParams(refererToUri(r.Referer())) if err != nil { @@ -419,7 +398,7 @@ func serveHtmlHandler(paths []string, regexes *Regexes, fileCache *[]string) htt } if filePath == "" { - filePath, err = getNewFile(paths, filters, sortOrder, regexes, fileCache) + filePath, err = getNewFile(paths, filters, sortOrder, regexes, index) switch { case err != nil && err == ErrNoImagesFound: notFound(w, r, filePath) @@ -496,29 +475,32 @@ func ServePage(args []string) error { rand.Seed(time.Now().UnixNano()) - fileCache := &[]string{} - - stats := &ServeStats{ - Served: 0, - List: []string{}, - Count: make(map[string]uint64), - FileSize: make(map[string]string), - Timestamps: make(map[string][]string), + index := &Index{ + Mutex: sync.RWMutex{}, + List: []string{}, } - http.Handle("/", serveHtmlHandler(paths, regexes, fileCache)) - http.Handle(Prefix+"/", http.StripPrefix(Prefix, serveStaticFileHandler(paths, stats))) - http.HandleFunc("/favicon.ico", doNothing) - if Cache { - err := generateCache(args, fileCache) + err := index.GenerateCache(args) if err != nil { return err } - http.Handle("/clear_cache", serveCacheClearHandler(args, fileCache)) + http.Handle("/clear_cache", serveCacheClearHandler(args, index)) } + stats := &ServeStats{ + Mutex: sync.RWMutex{}, + List: []string{}, + Count: make(map[string]uint64), + Size: make(map[string]string), + Times: make(map[string][]string), + } + + http.Handle("/", serveHtmlHandler(paths, regexes, index)) + http.Handle(Prefix+"/", http.StripPrefix(Prefix, serveStaticFileHandler(paths, stats))) + http.HandleFunc("/favicon.ico", doNothing) + if Debug { http.Handle("/stats", serveStatsHandler(args, stats)) }