Added sorting options (ascending, descending, defaults to random)

This commit is contained in:
Seednode 2022-10-18 13:42:32 -05:00
parent 7f27050595
commit 0d9e74fbca
4 changed files with 150 additions and 68 deletions

View File

@ -10,20 +10,6 @@ Browser history is preserved, so you can always go back to any previously displa
Builds available [here](https://cdn.seedno.de/builds/roulette). Builds available [here](https://cdn.seedno.de/builds/roulette).
## [-s, --successive]
This option is tailored specifically for my own use case.
When loading a new image, it checks for a sequentially-numbered file in the same directory.
For example, let's say `/mnt/photos/MyVacation001.jpg` is being displayed.
If you click on the image, `roulette` will attempt to display `/mnt/photos/MyVacation002.jpg`.
If that matching file is not found, it will select a random file as usual.
The expected format is `filename###.extension`.
## Usage output ## Usage output
``` ```
Usage: Usage:
@ -40,7 +26,6 @@ Flags:
-h, --help help for roulette -h, --help help for roulette
-p, --port int port to listen on (default 8080) -p, --port int port to listen on (default 8080)
-r, --recursive recurse into subdirectories -r, --recursive recurse into subdirectories
-s, --successive load the next sequential file, if possible
-v, --verbose log accessed files to stdout -v, --verbose log accessed files to stdout
Use "roulette [command] --help" for more information about a command. Use "roulette [command] --help" for more information about a command.

View File

@ -52,17 +52,45 @@ func getFirstFile(path string) (string, error) {
number := 1 number := 1
extension := split[0][3] extension := split[0][3]
fileName := fmt.Sprintf("%v%.3d%v", base, number, extension) fileName, err := tryExtensions(base, number, extension)
nextFile, err := nextFileExists(fileName)
if err != nil { if err != nil {
return "", err return "", err
} }
if !nextFile { return fileName, nil
}
func getLastFile(path string) (string, error) {
re := regexp.MustCompile(`(.+)([0-9]{3})(\..+)`)
split := re.FindAllStringSubmatch(path, -1)
if len(split) < 1 || len(split[0]) < 3 {
return "", nil return "", nil
} }
base := split[0][1]
number := 1
extension := split[0][3]
var fileName string
var err error
for {
fileName, err = tryExtensions(base, number, extension)
if err != nil {
return "", err
}
if fileName == "" {
fileName, err = tryExtensions(base, number-1, extension)
break
}
number = number + 1
}
return fileName, nil return fileName, nil
} }
@ -82,20 +110,69 @@ func getNextFile(path string) (string, error) {
} }
extension := split[0][3] extension := split[0][3]
incremented := number + 1 fileName, err := tryExtensions(base, number+1, extension)
fileName := fmt.Sprintf("%v%.3d%v", base, incremented, extension)
nextFile, err := nextFileExists(fileName)
if err != nil { if err != nil {
return "", err return "", err
} }
if !nextFile { return fileName, err
}
func getPreviousFile(path string) (string, error) {
re := regexp.MustCompile(`(.+)([0-9]{3})(\..+)`)
split := re.FindAllStringSubmatch(path, -1)
if len(split) < 1 || len(split[0]) < 3 {
return "", nil return "", nil
} }
base := split[0][1]
number, err := strconv.Atoi(split[0][2])
if err != nil {
return "", err
}
extension := split[0][3]
fileName, err := tryExtensions(base, number-1, extension)
if err != nil {
return "", err
}
return fileName, err
}
func tryExtensions(base string, number int, extension string) (string, error) {
extensions := [6]string{extension, ".jpg", ".jpeg", ".png", ".gif", ".webp"}
var fileName string
for _, i := range extensions {
fileName = fmt.Sprintf("%v%.3d%v", base, number, i)
exists, err := fileExists(fileName)
if err != nil {
return "", err
}
if exists {
return fileName, nil return fileName, nil
}
}
return "", nil
}
func fileExists(path string) (bool, error) {
_, err := os.Stat(path)
switch {
case err == nil:
return true, nil
case errors.Is(err, os.ErrNotExist):
return false, nil
default:
return false, err
}
} }
func pathIsValid(filePath string, paths []string) bool { func pathIsValid(filePath string, paths []string) bool {
@ -116,33 +193,6 @@ func pathIsValid(filePath string, paths []string) bool {
return true return true
} }
func fileExists(filePath string) (bool, error) {
_, err := os.Stat(filePath)
if errors.Is(err, os.ErrNotExist) {
if Verbose {
fmt.Printf("%v Failed to serve non-existent file: %v\n", time.Now().Format(LOGDATE), filePath)
}
return false, nil
} else if !errors.Is(err, os.ErrNotExist) && err != nil {
return false, err
}
return true, nil
}
func nextFileExists(path string) (bool, error) {
_, err := os.Stat(path)
switch {
case err == nil:
return true, nil
case errors.Is(err, os.ErrNotExist):
return false, nil
default:
return false, err
}
}
func isImage(path string) (bool, error) { func isImage(path string) (bool, error) {
file, err := os.Open(path) file, err := os.Open(path)
if err != nil { if err != nil {
@ -219,7 +269,7 @@ func prepareDirectory(directory []string) []string {
} }
} }
func prepareDirectories(m map[string][]string, successive string) []string { func prepareDirectories(m map[string][]string, sorting string) []string {
directories := []string{} directories := []string{}
keys := make([]string, len(m)) keys := make([]string, len(m))
@ -230,7 +280,7 @@ func prepareDirectories(m map[string][]string, successive string) []string {
i++ i++
} }
if successive != "" { if sorting == "asc" || sorting == "desc" {
for i := 0; i < len(keys); i++ { for i := 0; i < len(keys); i++ {
directories = append(directories, prepareDirectory(m[keys[i]])...) directories = append(directories, prepareDirectory(m[keys[i]])...)
} }
@ -243,13 +293,13 @@ func prepareDirectories(m map[string][]string, successive string) []string {
return directories return directories
} }
func pickFile(args []string, filter, successive string) (string, error) { func pickFile(args []string, filter, sorting string) (string, error) {
fileMap, err := getFileList(args, filter) fileMap, err := getFileList(args, filter)
if err != nil { if err != nil {
return "", err return "", err
} }
fileList := prepareDirectories(fileMap, successive) fileList := prepareDirectories(fileMap, sorting)
rand.Seed(time.Now().UnixNano()) rand.Seed(time.Now().UnixNano())

View File

@ -10,7 +10,7 @@ import (
"github.com/spf13/cobra" "github.com/spf13/cobra"
) )
var Version = "0.12.5" var Version = "0.13.0"
func init() { func init() {
rootCmd.AddCommand(versionCmd) rootCmd.AddCommand(versionCmd)

View File

@ -142,10 +142,10 @@ func serveHtmlHandler(paths []string) appHandler {
} }
filter := r.URL.Query().Get("f") filter := r.URL.Query().Get("f")
successive := r.URL.Query().Get("s") sorting := r.URL.Query().Get("s")
switch { switch {
case r.URL.Path == "/" && successive == "true" && refererUri != "": case r.URL.Path == "/" && sorting == "asc" && refererUri != "":
query, err := url.QueryUnescape(refererUri) query, err := url.QueryUnescape(refererUri)
if err != nil { if err != nil {
return err return err
@ -157,7 +157,7 @@ func serveHtmlHandler(paths []string) appHandler {
} }
if filePath == "" { if filePath == "" {
filePath, err = pickFile(paths, filter, successive) filePath, err = pickFile(paths, filter, sorting)
switch { switch {
case err != nil && err == ErrNoImagesFound: case err != nil && err == ErrNoImagesFound:
http.NotFound(w, r) http.NotFound(w, r)
@ -173,10 +173,10 @@ func serveHtmlHandler(paths []string) appHandler {
} }
} }
newUrl := fmt.Sprintf("%v%v?f=%v&s=%v", r.URL.Host, filePath, filter, successive) newUrl := fmt.Sprintf("%v%v?f=%v&s=%v", r.URL.Host, filePath, filter, sorting)
http.Redirect(w, r, newUrl, http.StatusSeeOther) http.Redirect(w, r, newUrl, http.StatusSeeOther)
case r.URL.Path == "/" && successive == "true" && refererUri == "": case r.URL.Path == "/" && sorting == "asc" && refererUri == "":
filePath, err := pickFile(paths, filter, successive) filePath, err := pickFile(paths, filter, sorting)
if err != nil && err == ErrNoImagesFound { if err != nil && err == ErrNoImagesFound {
http.NotFound(w, r) http.NotFound(w, r)
@ -190,10 +190,40 @@ func serveHtmlHandler(paths []string) appHandler {
return err return err
} }
newUrl := fmt.Sprintf("%v%v?f=%v&s=%v", r.URL.Host, filePath, filter, successive) newUrl := fmt.Sprintf("%v%v?f=%v&s=%v", r.URL.Host, filePath, filter, sorting)
http.Redirect(w, r, newUrl, http.StatusSeeOther) http.Redirect(w, r, newUrl, http.StatusSeeOther)
case r.URL.Path == "/": case r.URL.Path == "/" && sorting == "desc" && refererUri != "":
filePath, err := pickFile(paths, filter, successive) query, err := url.QueryUnescape(refererUri)
if err != nil {
return err
}
filePath, err := getPreviousFile(query)
if err != nil {
return err
}
if filePath == "" {
filePath, err = pickFile(paths, filter, sorting)
switch {
case err != nil && err == ErrNoImagesFound:
http.NotFound(w, r)
return nil
case err != nil:
return err
}
filePath, err = getLastFile(filePath)
if err != nil {
return err
}
}
newUrl := fmt.Sprintf("%v%v?f=%v&s=%v", r.URL.Host, filePath, filter, sorting)
http.Redirect(w, r, newUrl, http.StatusSeeOther)
case r.URL.Path == "/" && sorting == "desc" && refererUri == "":
filePath, err := pickFile(paths, filter, sorting)
if err != nil && err == ErrNoImagesFound { if err != nil && err == ErrNoImagesFound {
http.NotFound(w, r) http.NotFound(w, r)
@ -202,7 +232,24 @@ func serveHtmlHandler(paths []string) appHandler {
return err return err
} }
newUrl := fmt.Sprintf("%v%v?f=%v&s=%v", r.URL.Host, filePath, filter, successive) filePath, err = getLastFile(filePath)
if err != nil {
return err
}
newUrl := fmt.Sprintf("%v%v?f=%v&s=%v", r.URL.Host, filePath, filter, sorting)
http.Redirect(w, r, newUrl, http.StatusSeeOther)
case r.URL.Path == "/":
filePath, err := pickFile(paths, filter, sorting)
if err != nil && err == ErrNoImagesFound {
http.NotFound(w, r)
return nil
} else if err != nil {
return err
}
newUrl := fmt.Sprintf("%v%v?f=%v&s=%v", r.URL.Host, filePath, filter, sorting)
http.Redirect(w, r, newUrl, http.StatusSeeOther) http.Redirect(w, r, newUrl, http.StatusSeeOther)
default: default:
filePath := r.URL.Path filePath := r.URL.Path