Compare commits
No commits in common. "ba827430bb6fa57ff33c5ed0db8264680ece4162" and "76b4053240be22a5571fdd039209915436a6ee65" have entirely different histories.
ba827430bb
...
76b4053240
|
@ -24,6 +24,8 @@ If the `-c|--cache` flag is passed, the indices of all specified paths will be c
|
||||||
|
|
||||||
This will slightly increase the delay before the application begins responding to requests, but should significantly speed up subsequent requests.
|
This will slightly increase the delay before the application begins responding to requests, but should significantly speed up subsequent requests.
|
||||||
|
|
||||||
|
If any `include=`/`exclude=` filters are specified in a given request, the cache will be bypassed for that specific request.
|
||||||
|
|
||||||
The cache can be regenerated at any time by accessing the `/clear_cache` endpoint.
|
The cache can be regenerated at any time by accessing the `/clear_cache` endpoint.
|
||||||
|
|
||||||
If `--cache-file` is set, the cache will be loaded from the specified file on start, and written to the file whenever it is re-generated.
|
If `--cache-file` is set, the cache will be loaded from the specified file on start, and written to the file whenever it is re-generated.
|
||||||
|
@ -118,7 +120,6 @@ Flags:
|
||||||
-c, --cache generate directory cache at startup
|
-c, --cache generate directory cache at startup
|
||||||
--cache-file string path to optional persistent cache file
|
--cache-file string path to optional persistent cache file
|
||||||
--code enable support for source code files
|
--code enable support for source code files
|
||||||
--code-theme string theme for source code syntax highlighting (default "solarized-dark256")
|
|
||||||
-f, --filter enable filtering
|
-f, --filter enable filtering
|
||||||
--flash enable support for shockwave flash files (via ruffle.rs)
|
--flash enable support for shockwave flash files (via ruffle.rs)
|
||||||
--handlers display registered handlers (for debugging)
|
--handlers display registered handlers (for debugging)
|
||||||
|
@ -136,6 +137,7 @@ Flags:
|
||||||
--russian remove selected images after serving
|
--russian remove selected images after serving
|
||||||
-s, --sort enable sorting
|
-s, --sort enable sorting
|
||||||
--text enable support for text files
|
--text enable support for text files
|
||||||
|
--theme string theme for source code syntax highlighting (default "solarized-dark256")
|
||||||
-v, --verbose log accessed files and other information to stdout
|
-v, --verbose log accessed files and other information to stdout
|
||||||
-V, --version display version and exit
|
-V, --version display version and exit
|
||||||
--video enable support for video files
|
--video enable support for video files
|
||||||
|
|
12
cmd/cache.go
12
cmd/cache.go
|
@ -22,8 +22,7 @@ type fileCache struct {
|
||||||
|
|
||||||
func (cache *fileCache) List() []string {
|
func (cache *fileCache) List() []string {
|
||||||
cache.mutex.RLock()
|
cache.mutex.RLock()
|
||||||
val := make([]string, len(cache.list))
|
val := cache.list
|
||||||
copy(val, cache.list)
|
|
||||||
cache.mutex.RUnlock()
|
cache.mutex.RUnlock()
|
||||||
|
|
||||||
return val
|
return val
|
||||||
|
@ -54,15 +53,8 @@ func (cache *fileCache) remove(path string) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (cache *fileCache) set(val []string) {
|
func (cache *fileCache) set(val []string) {
|
||||||
length := len(val)
|
|
||||||
|
|
||||||
if length < 1 {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
cache.mutex.Lock()
|
cache.mutex.Lock()
|
||||||
cache.list = make([]string, length)
|
cache.list = val
|
||||||
copy(cache.list, val)
|
|
||||||
cache.mutex.Unlock()
|
cache.mutex.Unlock()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
156
cmd/files.go
156
cmd/files.go
|
@ -43,12 +43,12 @@ type concurrency struct {
|
||||||
|
|
||||||
type files struct {
|
type files struct {
|
||||||
mutex sync.RWMutex
|
mutex sync.RWMutex
|
||||||
list []string
|
list map[string][]string
|
||||||
}
|
}
|
||||||
|
|
||||||
func (f *files) append(path string) {
|
func (f *files) append(directory, path string) {
|
||||||
f.mutex.Lock()
|
f.mutex.Lock()
|
||||||
f.list = append(f.list, path)
|
f.list[directory] = append(f.list[directory], path)
|
||||||
f.mutex.Unlock()
|
f.mutex.Unlock()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -99,6 +99,63 @@ func preparePath(path string) string {
|
||||||
return mediaPrefix + path
|
return mediaPrefix + path
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func appendPath(directory, path string, files *files, stats *scanStats, formats *types.Types) {
|
||||||
|
if !formats.Validate(path) {
|
||||||
|
stats.filesSkipped.Add(1)
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
files.append(directory, path)
|
||||||
|
|
||||||
|
stats.filesMatched.Add(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
func appendPaths(path string, files *files, filters *filters, stats *scanStats, formats *types.Types) error {
|
||||||
|
absolutePath, err := filepath.Abs(path)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
directory, filename := filepath.Split(absolutePath)
|
||||||
|
|
||||||
|
filename = strings.ToLower(filename)
|
||||||
|
|
||||||
|
if filters.hasExcludes() {
|
||||||
|
for i := 0; i < len(filters.excluded); i++ {
|
||||||
|
if strings.Contains(
|
||||||
|
filename,
|
||||||
|
filters.excluded[i],
|
||||||
|
) {
|
||||||
|
stats.filesSkipped.Add(1)
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if filters.hasIncludes() {
|
||||||
|
for i := 0; i < len(filters.included); i++ {
|
||||||
|
if strings.Contains(
|
||||||
|
filename,
|
||||||
|
filters.included[i],
|
||||||
|
) {
|
||||||
|
appendPath(directory, path, files, stats, formats)
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
stats.filesSkipped.Add(1)
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
appendPath(directory, path, files, stats, formats)
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func newFile(paths []string, filters *filters, sortOrder string, regexes *regexes, cache *fileCache, formats *types.Types) (string, error) {
|
func newFile(paths []string, filters *filters, sortOrder string, regexes *regexes, cache *fileCache, formats *types.Types) (string, error) {
|
||||||
path, err := pickFile(paths, filters, sortOrder, cache, formats)
|
path, err := pickFile(paths, filters, sortOrder, cache, formats)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -294,7 +351,7 @@ func pathCount(path string) (uint32, uint32, error) {
|
||||||
return files, directories, nil
|
return files, directories, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func scanPath(path string, files *files, stats *scanStats, concurrency *concurrency, formats *types.Types) error {
|
func scanPath(path string, files *files, filters *filters, stats *scanStats, concurrency *concurrency, formats *types.Types) error {
|
||||||
var wg sync.WaitGroup
|
var wg sync.WaitGroup
|
||||||
|
|
||||||
err := filepath.WalkDir(path, func(p string, info os.DirEntry, err error) error {
|
err := filepath.WalkDir(path, func(p string, info os.DirEntry, err error) error {
|
||||||
|
@ -321,15 +378,10 @@ func scanPath(path string, files *files, stats *scanStats, concurrency *concurre
|
||||||
fmt.Println(err)
|
fmt.Println(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if !formats.Validate(path) {
|
err = appendPaths(path, files, filters, stats, formats)
|
||||||
stats.filesSkipped.Add(1)
|
if err != nil {
|
||||||
|
fmt.Println(err)
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
files.append(path)
|
|
||||||
|
|
||||||
stats.filesMatched.Add(1)
|
|
||||||
}()
|
}()
|
||||||
case info.IsDir():
|
case info.IsDir():
|
||||||
files, directories, err := pathCount(p)
|
files, directories, err := pathCount(p)
|
||||||
|
@ -360,10 +412,16 @@ func scanPath(path string, files *files, stats *scanStats, concurrency *concurre
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func scanPaths(paths []string, sort string, cache *fileCache, formats *types.Types) []string {
|
func fileList(paths []string, filters *filters, sort string, cache *fileCache, formats *types.Types) ([]string, bool) {
|
||||||
|
if Cache && filters.isEmpty() && !cache.isEmpty() {
|
||||||
|
return cache.List(), true
|
||||||
|
}
|
||||||
|
|
||||||
|
var fileList []string
|
||||||
|
|
||||||
files := &files{
|
files := &files{
|
||||||
mutex: sync.RWMutex{},
|
mutex: sync.RWMutex{},
|
||||||
list: []string{},
|
list: make(map[string][]string),
|
||||||
}
|
}
|
||||||
|
|
||||||
stats := &scanStats{
|
stats := &scanStats{
|
||||||
|
@ -393,7 +451,7 @@ func scanPaths(paths []string, sort string, cache *fileCache, formats *types.Typ
|
||||||
wg.Done()
|
wg.Done()
|
||||||
}()
|
}()
|
||||||
|
|
||||||
err := scanPath(paths[i], files, stats, concurrency, formats)
|
err := scanPath(paths[i], files, filters, stats, concurrency, formats)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Println(err)
|
fmt.Println(err)
|
||||||
}
|
}
|
||||||
|
@ -402,9 +460,10 @@ func scanPaths(paths []string, sort string, cache *fileCache, formats *types.Typ
|
||||||
|
|
||||||
wg.Wait()
|
wg.Wait()
|
||||||
|
|
||||||
|
fileList = prepareDirectories(files, sort)
|
||||||
|
|
||||||
if stats.filesMatched.Load() < 1 {
|
if stats.filesMatched.Load() < 1 {
|
||||||
fmt.Println("No files matched")
|
return []string{}, false
|
||||||
return []string{}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if Verbose {
|
if Verbose {
|
||||||
|
@ -418,32 +477,35 @@ func scanPaths(paths []string, sort string, cache *fileCache, formats *types.Typ
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
return files.list
|
if Cache && filters.isEmpty() {
|
||||||
|
cache.set(fileList)
|
||||||
}
|
}
|
||||||
|
|
||||||
func fileList(paths []string, filters *filters, sort string, cache *fileCache, formats *types.Types) []string {
|
return fileList, false
|
||||||
switch {
|
|
||||||
case Cache && !cache.isEmpty() && filters.isEmpty():
|
|
||||||
return cache.List()
|
|
||||||
case Cache && !cache.isEmpty() && !filters.isEmpty():
|
|
||||||
return filters.apply(cache.List())
|
|
||||||
case Cache && cache.isEmpty() && !filters.isEmpty():
|
|
||||||
cache.set(scanPaths(paths, sort, cache, formats))
|
|
||||||
return filters.apply(cache.List())
|
|
||||||
case Cache && cache.isEmpty() && filters.isEmpty():
|
|
||||||
cache.set(scanPaths(paths, sort, cache, formats))
|
|
||||||
return cache.List()
|
|
||||||
case !Cache && !filters.isEmpty():
|
|
||||||
return filters.apply(scanPaths(paths, sort, cache, formats))
|
|
||||||
default:
|
|
||||||
return scanPaths(paths, sort, cache, formats)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func prepareDirectories(files *files, sort string) []string {
|
||||||
|
directories := []string{}
|
||||||
|
|
||||||
|
keys := make([]string, len(files.list))
|
||||||
|
|
||||||
|
i := 0
|
||||||
|
for k := range files.list {
|
||||||
|
keys[i] = k
|
||||||
|
i++
|
||||||
|
}
|
||||||
|
|
||||||
|
for i := 0; i < len(keys); i++ {
|
||||||
|
directories = append(directories, files.list[keys[i]]...)
|
||||||
|
}
|
||||||
|
|
||||||
|
return directories
|
||||||
}
|
}
|
||||||
|
|
||||||
func pickFile(args []string, filters *filters, sort string, cache *fileCache, formats *types.Types) (string, error) {
|
func pickFile(args []string, filters *filters, sort string, cache *fileCache, formats *types.Types) (string, error) {
|
||||||
list := fileList(args, filters, sort, cache, formats)
|
fileList, fromCache := fileList(args, filters, sort, cache, formats)
|
||||||
|
|
||||||
fileCount := len(list)
|
fileCount := len(fileList)
|
||||||
|
|
||||||
if fileCount < 1 {
|
if fileCount < 1 {
|
||||||
return "", ErrNoMediaFound
|
return "", ErrNoMediaFound
|
||||||
|
@ -459,7 +521,27 @@ func pickFile(args []string, filters *filters, sort string, cache *fileCache, fo
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
|
|
||||||
return list[val], nil
|
for i := 0; i < fileCount; i++ {
|
||||||
|
switch {
|
||||||
|
case val >= fileCount:
|
||||||
|
val = 0
|
||||||
|
case val < fileCount-1:
|
||||||
|
val++
|
||||||
|
}
|
||||||
|
|
||||||
|
path := fileList[val]
|
||||||
|
|
||||||
|
switch {
|
||||||
|
case !fromCache && formats.Validate(path):
|
||||||
|
return path, nil
|
||||||
|
case !fromCache:
|
||||||
|
continue
|
||||||
|
default:
|
||||||
|
return path, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return "", ErrNoMediaFound
|
||||||
}
|
}
|
||||||
|
|
||||||
func normalizePath(path string) (string, error) {
|
func normalizePath(path string) (string, error) {
|
||||||
|
|
|
@ -4,10 +4,7 @@ Copyright © 2023 Seednode <seednode@seedno.de>
|
||||||
|
|
||||||
package cmd
|
package cmd
|
||||||
|
|
||||||
import (
|
import "strings"
|
||||||
"slices"
|
|
||||||
"strings"
|
|
||||||
)
|
|
||||||
|
|
||||||
type filters struct {
|
type filters struct {
|
||||||
included []string
|
included []string
|
||||||
|
@ -33,27 +30,3 @@ func (filters *filters) hasExcludes() bool {
|
||||||
func (filters *filters) excludes() string {
|
func (filters *filters) excludes() string {
|
||||||
return strings.Join(filters.excluded, ",")
|
return strings.Join(filters.excluded, ",")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (filters *filters) apply(fileList []string) []string {
|
|
||||||
result := make([]string, len(fileList))
|
|
||||||
|
|
||||||
copy(result, fileList)
|
|
||||||
|
|
||||||
if filters.hasExcludes() {
|
|
||||||
for _, exclude := range filters.excluded {
|
|
||||||
result = slices.DeleteFunc(fileList, func(s string) bool {
|
|
||||||
return strings.Contains(strings.ToLower(s), strings.ToLower(exclude))
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if filters.hasIncludes() {
|
|
||||||
for _, include := range filters.included {
|
|
||||||
result = slices.DeleteFunc(fileList, func(s string) bool {
|
|
||||||
return !strings.Contains(strings.ToLower(s), strings.ToLower(include))
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
|
@ -11,7 +11,7 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
ReleaseVersion string = "0.87.0"
|
ReleaseVersion string = "0.85.1"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
@ -21,7 +21,6 @@ var (
|
||||||
Cache bool
|
Cache bool
|
||||||
CacheFile string
|
CacheFile string
|
||||||
Code bool
|
Code bool
|
||||||
CodeTheme string
|
|
||||||
Filtering bool
|
Filtering bool
|
||||||
Flash bool
|
Flash bool
|
||||||
Handlers bool
|
Handlers bool
|
||||||
|
@ -38,6 +37,7 @@ var (
|
||||||
Russian bool
|
Russian bool
|
||||||
Sorting bool
|
Sorting bool
|
||||||
Text bool
|
Text bool
|
||||||
|
Theme string
|
||||||
Verbose bool
|
Verbose bool
|
||||||
Version bool
|
Version bool
|
||||||
Videos bool
|
Videos bool
|
||||||
|
@ -71,7 +71,6 @@ func init() {
|
||||||
rootCmd.Flags().BoolVarP(&Cache, "cache", "c", false, "generate directory cache at startup")
|
rootCmd.Flags().BoolVarP(&Cache, "cache", "c", false, "generate directory cache at startup")
|
||||||
rootCmd.Flags().StringVar(&CacheFile, "cache-file", "", "path to optional persistent cache file")
|
rootCmd.Flags().StringVar(&CacheFile, "cache-file", "", "path to optional persistent cache file")
|
||||||
rootCmd.Flags().BoolVar(&Code, "code", false, "enable support for source code files")
|
rootCmd.Flags().BoolVar(&Code, "code", false, "enable support for source code files")
|
||||||
rootCmd.Flags().StringVar(&CodeTheme, "code-theme", "solarized-dark256", "theme for source code syntax highlighting")
|
|
||||||
rootCmd.Flags().BoolVarP(&Filtering, "filter", "f", false, "enable filtering")
|
rootCmd.Flags().BoolVarP(&Filtering, "filter", "f", false, "enable filtering")
|
||||||
rootCmd.Flags().BoolVar(&Flash, "flash", false, "enable support for shockwave flash files (via ruffle.rs)")
|
rootCmd.Flags().BoolVar(&Flash, "flash", false, "enable support for shockwave flash files (via ruffle.rs)")
|
||||||
rootCmd.Flags().BoolVar(&Handlers, "handlers", false, "display registered handlers (for debugging)")
|
rootCmd.Flags().BoolVar(&Handlers, "handlers", false, "display registered handlers (for debugging)")
|
||||||
|
@ -88,6 +87,7 @@ func init() {
|
||||||
rootCmd.Flags().BoolVar(&Russian, "russian", false, "remove selected images after serving")
|
rootCmd.Flags().BoolVar(&Russian, "russian", false, "remove selected images after serving")
|
||||||
rootCmd.Flags().BoolVarP(&Sorting, "sort", "s", false, "enable sorting")
|
rootCmd.Flags().BoolVarP(&Sorting, "sort", "s", false, "enable sorting")
|
||||||
rootCmd.Flags().BoolVar(&Text, "text", false, "enable support for text files")
|
rootCmd.Flags().BoolVar(&Text, "text", false, "enable support for text files")
|
||||||
|
rootCmd.Flags().StringVar(&Theme, "theme", "solarized-dark256", "theme for source code syntax highlighting")
|
||||||
rootCmd.Flags().BoolVarP(&Verbose, "verbose", "v", false, "log accessed files and other information to stdout")
|
rootCmd.Flags().BoolVarP(&Verbose, "verbose", "v", false, "log accessed files and other information to stdout")
|
||||||
rootCmd.Flags().BoolVarP(&Version, "version", "V", false, "display version and exit")
|
rootCmd.Flags().BoolVarP(&Version, "version", "V", false, "display version and exit")
|
||||||
rootCmd.Flags().BoolVar(&Videos, "video", false, "enable support for video files")
|
rootCmd.Flags().BoolVar(&Videos, "video", false, "enable support for video files")
|
||||||
|
|
|
@ -331,7 +331,7 @@ func ServePage(args []string) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
if Code || All {
|
if Code || All {
|
||||||
formats.Add(code.New(CodeTheme))
|
formats.Add(code.New(Theme))
|
||||||
}
|
}
|
||||||
|
|
||||||
if Flash || All {
|
if Flash || All {
|
||||||
|
|
BIN
default.pgo
BIN
default.pgo
Binary file not shown.
Loading…
Reference in New Issue