Added sorting options (ascending, descending, defaults to random)
This commit is contained in:
parent
7f27050595
commit
0d9e74fbca
15
README.md
15
README.md
|
@ -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.
|
||||||
|
|
132
cmd/files.go
132
cmd/files.go
|
@ -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())
|
||||||
|
|
||||||
|
|
|
@ -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)
|
||||||
|
|
67
cmd/web.go
67
cmd/web.go
|
@ -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
|
||||||
|
|
Loading…
Reference in New Issue