Compare commits

...

2 Commits

Author SHA1 Message Date
Seednode a87d9a26fd Update readme with --disable-buttons 2023-10-20 17:25:52 -05:00
Seednode d902368845 Add first/prev/next/last buttons to sorted content 2023-10-20 17:18:35 -05:00
8 changed files with 225 additions and 56 deletions

View File

@ -116,11 +116,12 @@ Flags:
--case-sensitive use case-sensitive matching for filters
--code enable support for source code files
--code-theme string theme for source code syntax highlighting (default "solarized-dark256")
--disable-buttons disable first/prev/next/last buttons
--exit-on-error shut down webserver on error, instead of just printing the error
--fallback serve files as application/octet-stream if no matching format is registered
-f, --filter enable filtering
--flash enable support for shockwave flash files (via ruffle.rs)
--fun adds a bit of excitement to your day
--fun add a bit of excitement to your day
--handlers display registered handlers (for debugging)
-h, --help help for roulette
--images enable support for image files

View File

@ -87,7 +87,7 @@ func newFile(list []string, sortOrder string, regexes *regexes, formats types.Ty
}
case sortOrder == "desc":
for {
splitPath.increment()
splitPath.number = splitPath.increment()
path, err = tryExtensions(splitPath, formats)
if err != nil {
@ -95,7 +95,7 @@ func newFile(list []string, sortOrder string, regexes *regexes, formats types.Ty
}
if path == "" {
splitPath.decrement()
splitPath.number = splitPath.decrement()
path, err = tryExtensions(splitPath, formats)
if err != nil {
@ -119,9 +119,9 @@ func nextFile(filePath, sortOrder string, regexes *regexes, formats types.Types)
switch {
case sortOrder == "asc":
splitPath.increment()
splitPath.number = splitPath.increment()
case sortOrder == "desc":
splitPath.decrement()
splitPath.number = splitPath.decrement()
default:
return "", nil
}

View File

@ -186,13 +186,15 @@ func registerIndexHandlers(mux *httprouter.Router, args []string, index *fileInd
func importIndex(args []string, index *fileIndex, formats types.Types) error {
if IndexFile != "" {
err := index.Import(IndexFile)
if err != nil {
_, err := fileList(args, &filters{}, "", index, formats)
if err != nil {
return err
}
if err == nil {
return nil
}
}
_, err := fileList(args, &filters{}, "", index, formats)
if err != nil {
return err
}
return nil
}

View File

@ -19,7 +19,7 @@ import (
"seedno.de/seednode/roulette/types"
)
func paginate(page int, fileCount int, ending bool) string {
func paginateIndex(page int, fileCount int, ending bool) string {
var html strings.Builder
var firstPage int = 1
@ -120,8 +120,8 @@ func serveIndexHtml(args []string, index *fileIndex, shouldPaginate bool) httpro
htmlBody.WriteString(`table,td,tr{border:none;}td{border-bottom:1px solid black;}td{white-space:nowrap;padding:.5em}</style>`)
htmlBody.WriteString(fmt.Sprintf("<title>Index contains %d files</title></head><body><table>", fileCount))
if shouldPaginate {
htmlBody.WriteString(paginate(page, fileCount, false))
if shouldPaginate && !DisableButtons {
htmlBody.WriteString(paginateIndex(page, fileCount, false))
}
if len(indexDump) > 0 {
@ -135,8 +135,8 @@ func serveIndexHtml(args []string, index *fileIndex, shouldPaginate bool) httpro
}
}
if shouldPaginate {
htmlBody.WriteString(paginate(page, fileCount, true))
if shouldPaginate && !DisableButtons {
htmlBody.WriteString(paginateIndex(page, fileCount, true))
}
htmlBody.WriteString(`</table></body></html>`)

View File

@ -12,40 +12,41 @@ import (
)
const (
ReleaseVersion string = "3.0.2"
ReleaseVersion string = "3.1.0"
)
var (
All bool
Audio bool
Bind string
CaseSensitive bool
Code bool
CodeTheme string
ExitOnError bool
Fallback bool
Filtering bool
Flash bool
Fun bool
Handlers bool
Images bool
Index bool
IndexFile string
Info bool
MaxFileCount int
MinFileCount int
PageLength int
Port int
Prefix string
Profile bool
Recursive bool
Refresh bool
Russian bool
Sorting bool
Text bool
Verbose bool
Version bool
Videos bool
All bool
Audio bool
Bind string
CaseSensitive bool
Code bool
CodeTheme string
DisableButtons bool
ExitOnError bool
Fallback bool
Filtering bool
Flash bool
Fun bool
Handlers bool
Images bool
Index bool
IndexFile string
Info bool
MaxFileCount int
MinFileCount int
PageLength int
Port int
Prefix string
Profile bool
Recursive bool
Refresh bool
Russian bool
Sorting bool
Text bool
Verbose bool
Version bool
Videos bool
rootCmd = &cobra.Command{
Use: "roulette <path> [path]...",
@ -88,6 +89,7 @@ func init() {
rootCmd.Flags().BoolVar(&CaseSensitive, "case-sensitive", false, "use case-sensitive matching for filters")
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().BoolVar(&DisableButtons, "disable-buttons", false, "disable first/prev/next/last buttons")
rootCmd.Flags().BoolVar(&ExitOnError, "exit-on-error", false, "shut down webserver on error, instead of just printing the error")
rootCmd.Flags().BoolVar(&Fallback, "fallback", false, "serve files as application/octet-stream if no matching format is registered")
rootCmd.Flags().BoolVarP(&Filtering, "filter", "f", false, "enable filtering")

View File

@ -6,8 +6,12 @@ package cmd
import (
"fmt"
"sort"
"strings"
"strconv"
"seedno.de/seednode/roulette/types"
)
type splitPath struct {
@ -16,22 +20,22 @@ type splitPath struct {
extension string
}
func (splitPath *splitPath) increment() {
func (splitPath *splitPath) increment() string {
asInt, err := strconv.Atoi(splitPath.number)
if err != nil {
return
return ""
}
splitPath.number = fmt.Sprintf("%0*d", len(splitPath.number), asInt+1)
return fmt.Sprintf("%0*d", len(splitPath.number), asInt+1)
}
func (splitPath *splitPath) decrement() {
func (splitPath *splitPath) decrement() string {
asInt, err := strconv.Atoi(splitPath.number)
if err != nil {
return
return ""
}
splitPath.number = fmt.Sprintf("%0*d", len(splitPath.number), asInt-1)
return fmt.Sprintf("%0*d", len(splitPath.number), asInt-1)
}
func split(path string, regexes *regexes) (*splitPath, error) {
@ -49,3 +53,126 @@ func split(path string, regexes *regexes) (*splitPath, error) {
return p, nil
}
func getRange(path string, regexes *regexes, index *fileIndex) (string, string, error) {
splitPath, err := split(path, regexes)
if err != nil {
return "", "", err
}
list := index.List()
sort.Slice(list, func(i, j int) bool {
return list[i] <= list[j]
})
var first, last, previous string
Loop:
for _, val := range list {
splitVal, err := split(val, regexes)
if err != nil {
return "", "", err
}
switch {
case splitVal.base == splitPath.base && first == "":
first = val
case splitVal.base != splitPath.base && first != "":
last = previous
break Loop
}
previous = val
}
return first, last, nil
}
func paginateSorted(path, first, last, queryParams string, regexes *regexes, formats types.Types) (string, error) {
split, err := split(path, regexes)
if err != nil {
return "", err
}
var firstStatus, prevStatus, nextStatus, lastStatus string = "", "", "", ""
if path <= first {
firstStatus = " disabled"
prevStatus = " disabled"
}
if path >= last {
nextStatus = " disabled"
lastStatus = " disabled"
}
prevPath := &splitPath{
base: split.base,
number: split.decrement(),
extension: split.extension,
}
prevPage, err := tryExtensions(prevPath, formats)
switch {
case err != nil:
return "", err
case prevPage == "":
prevStatus = " disabled"
case prevPage < first:
prevPage = first
}
nextPath := &splitPath{
base: split.base,
number: split.increment(),
extension: split.extension,
}
nextPage, err := tryExtensions(nextPath, formats)
switch {
case err != nil:
return "", err
case nextPage == "":
nextStatus = " disabled"
case nextPage > last:
nextPage = last
}
var html strings.Builder
html.WriteString(`<table style="margin-left:auto;margin-right:auto;"><tr><td>`)
html.WriteString(fmt.Sprintf(`<button onclick="window.location.href = '%s%s%s%s';"%s>First</button>`,
Prefix,
mediaPrefix,
first,
queryParams,
firstStatus))
html.WriteString(fmt.Sprintf(`<button onclick="window.location.href = '%s%s%s%s';"%s>Prev</button>`,
Prefix,
mediaPrefix,
prevPage,
queryParams,
prevStatus))
html.WriteString(fmt.Sprintf(`<button onclick="window.location.href = '%s%s%s%s';"%s>Next</button>`,
Prefix,
mediaPrefix,
nextPage,
queryParams,
nextStatus))
html.WriteString(fmt.Sprintf(`<button onclick="window.location.href = '%s%s%s%s';"%s>Last</button>`,
Prefix,
mediaPrefix,
last,
queryParams,
lastStatus))
html.WriteString("</td></tr></table>")
return html.String(), nil
}

View File

@ -307,7 +307,9 @@ func serveMedia(paths []string, regexes *regexes, index *fileIndex, formats type
refreshTimer, refreshInterval := refreshInterval(r)
rootUrl := Prefix + "/" + generateQueryParams(filters, sortOrder, refreshInterval)
queryParams := generateQueryParams(filters, sortOrder, refreshInterval)
rootUrl := Prefix + "/" + queryParams
var htmlBody strings.Builder
htmlBody.WriteString(`<!DOCTYPE html><html class="bg" lang="en"><head>`)
@ -324,6 +326,33 @@ func serveMedia(paths []string, regexes *regexes, index *fileIndex, formats type
}
htmlBody.WriteString(title)
htmlBody.WriteString(`</head><body>`)
var first, last string
if Index && sortOrder != "" {
first, last, err = getRange(path, regexes, index)
if err != nil {
errorChannel <- err
serverError(w, r, nil)
return
}
}
if Index && !DisableButtons && sortOrder != "" {
paginate, err := paginateSorted(path, first, last, queryParams, regexes, formats)
if err != nil {
errorChannel <- err
serverError(w, r, nil)
return
}
htmlBody.WriteString(paginate)
}
if refreshInterval != "0ms" {
htmlBody.WriteString(refreshFunction(rootUrl, refreshTimer))
}
@ -337,6 +366,7 @@ func serveMedia(paths []string, regexes *regexes, index *fileIndex, formats type
return
}
htmlBody.WriteString(body)
htmlBody.WriteString(`</body></html>`)
startTime := time.Now()
@ -456,7 +486,7 @@ func ServePage(args []string) error {
// enable image support if no other flags are passed, to retain backwards compatibility
// to be replaced with rootCmd.MarkFlagsOneRequired on next spf13/cobra update
if Images || All || len(formats) == 0 {
formats.Add(images.Format{Fun: Fun})
formats.Add(images.Format{DisableButtons: DisableButtons, Fun: Fun})
}
paths, err := validatePaths(args, formats)

View File

@ -26,14 +26,21 @@ type dimensions struct {
}
type Format struct {
Fun bool
DisableButtons bool
Fun bool
}
func (t Format) Css() string {
var css strings.Builder
css.WriteString(`html,body{margin:0;padding:0;height:100%;}`)
css.WriteString(`a{color:inherit;display:block;height:100%;width:100%;text-decoration:none;}`)
if t.DisableButtons {
css.WriteString(`a{color:inherit;display:block;height:100%;width:100%;text-decoration:none;}`)
} else {
css.WriteString(`a{color:inherit;display:block;height:97%;width:100%;text-decoration:none;}`)
}
css.WriteString(`img{margin:auto;display:block;max-width:97%;max-height:97%;`)
css.WriteString(`object-fit:scale-down;position:absolute;top:50%;left:50%;transform:translate(-50%,-50%)`)
if t.Fun {