Compare commits
No commits in common. "81d7e60d0c89589eac0e80722c624e46b6d5b1b8" and "f36c0cc999617f1421a6b40c60b8a9e5ec11ff31" have entirely different histories.
81d7e60d0c
...
f36c0cc999
12
README.md
12
README.md
|
@ -35,27 +35,25 @@ If the `-i|--indexing` flag is passed, all specified paths will be indexed on st
|
||||||
|
|
||||||
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.
|
||||||
|
|
||||||
The index can be regenerated at any time by accessing the `/index/rebuild` endpoint.
|
The index can be regenerated at any time by accessing the `/rebuild_index` endpoint.
|
||||||
|
|
||||||
If `--index-file` is set, the index will be loaded from the specified file on start, and written to the file whenever it is re-generated.
|
If `--index-file` is set, the index will be loaded from the specified file on start, and written to the file whenever it is re-generated.
|
||||||
|
|
||||||
## Info
|
## Info
|
||||||
If the `-i|--info` flag is passed, six additional endpoints are registered.
|
If the `-i|--info` flag is passed, six additional endpoints are registered.
|
||||||
|
|
||||||
The first of these—`/index/html` and `/index/json`—return the contents of the index, in HTML and JSON formats respectively.
|
The first of these—`/html` and `/json`—return the contents of the index, in HTML and JSON formats respectively.
|
||||||
|
|
||||||
If `--page-length` is also set, these can be viewed in paginated form by appending a page number, e.g. `/index/html/5` for the fifth page.
|
If `--page-length` is also set, these can be viewed in paginated form by appending `/n`, e.g. `/html/5` for the fifth page.
|
||||||
|
|
||||||
This can prove useful when confirming whether the index is generated successfully, or whether a given file is in the index.
|
This can prove useful when confirming whether the index is generated successfully, or whether a given file is in the index.
|
||||||
|
|
||||||
The remaining four endpoints—`/extensions/available`, `/extensions/enabled`, `/types/available` and `/types/enabled`—return information about the registered file types.
|
The remaining four endpoints—`/available_extensions`, `/enabled_extensions`, `/available_mime_types` and `/enabled_mime_types`—return information about the registered file types.
|
||||||
|
|
||||||
## Refresh
|
## Refresh
|
||||||
If the `--refresh` flag is passed and a positive-value `refresh=<integer><unit>` query parameter is provided, the page will reload after that interval.
|
If the `--refresh` flag is passed and a positive-value `refresh=<integer><unit>` query parameter is provided, the page will reload after that interval.
|
||||||
|
|
||||||
This can be used to generate a sort of slideshow of files in any browser with Javascript support.
|
This can be used to generate a sort of slideshow of files. Pressing Space will pause automatic refreshing until the page is manually refreshed or a new page is loaded.
|
||||||
|
|
||||||
Pressing Spacebar will pause automatic refreshing until Spacebar is pressed again, the page is manually refreshed, or a new page is loaded.
|
|
||||||
|
|
||||||
Minimum accepted value is 500ms, as anything lower seems to cause inconsistent behavior. This might be changed in a future release.
|
Minimum accepted value is 500ms, as anything lower seems to cause inconsistent behavior. This might be changed in a future release.
|
||||||
|
|
||||||
|
|
|
@ -27,6 +27,13 @@ type regexes struct {
|
||||||
filename *regexp.Regexp
|
filename *regexp.Regexp
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// type scanStats struct {
|
||||||
|
// filesMatched int
|
||||||
|
// filesSkipped int
|
||||||
|
// directoriesMatched int
|
||||||
|
// directoriesSkipped int
|
||||||
|
// }
|
||||||
|
|
||||||
type scanStatsChannels struct {
|
type scanStatsChannels struct {
|
||||||
filesMatched chan int
|
filesMatched chan int
|
||||||
filesSkipped chan int
|
filesSkipped chan int
|
||||||
|
|
|
@ -178,7 +178,7 @@ func serveIndexRebuild(args []string, index *fileIndex, formats types.Types, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func registerIndexHandlers(mux *httprouter.Router, args []string, index *fileIndex, formats types.Types, errorChannel chan<- error) error {
|
func registerIndexHandlers(mux *httprouter.Router, args []string, index *fileIndex, formats types.Types, errorChannel chan<- error) error {
|
||||||
registerHandler(mux, Prefix+"/index/rebuild", serveIndexRebuild(args, index, formats, errorChannel))
|
registerHandler(mux, Prefix+"/rebuild_index", serveIndexRebuild(args, index, formats, errorChannel))
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
28
cmd/info.go
28
cmd/info.go
|
@ -255,19 +255,19 @@ func serveEnabledExtensions(formats types.Types, errorChannel chan<- error) http
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func serveAvailableMediaTypes(errorChannel chan<- error) httprouter.Handle {
|
func serveAvailableMimeTypes(errorChannel chan<- error) httprouter.Handle {
|
||||||
return func(w http.ResponseWriter, r *http.Request, p httprouter.Params) {
|
return func(w http.ResponseWriter, r *http.Request, p httprouter.Params) {
|
||||||
w.Header().Set("Content-Type", "text/plain")
|
w.Header().Set("Content-Type", "text/plain")
|
||||||
|
|
||||||
startTime := time.Now()
|
startTime := time.Now()
|
||||||
|
|
||||||
written, err := w.Write([]byte(types.SupportedFormats.GetMediaTypes()))
|
written, err := w.Write([]byte(types.SupportedFormats.GetMimeTypes()))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
errorChannel <- err
|
errorChannel <- err
|
||||||
}
|
}
|
||||||
|
|
||||||
if Verbose {
|
if Verbose {
|
||||||
fmt.Printf("%s | SERVE: Available media type list (%s) to %s in %s\n",
|
fmt.Printf("%s | SERVE: Available MIME type list (%s) to %s in %s\n",
|
||||||
startTime.Format(logDate),
|
startTime.Format(logDate),
|
||||||
humanReadableSize(written),
|
humanReadableSize(written),
|
||||||
realIP(r),
|
realIP(r),
|
||||||
|
@ -277,19 +277,19 @@ func serveAvailableMediaTypes(errorChannel chan<- error) httprouter.Handle {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func serveEnabledMediaTypes(formats types.Types, errorChannel chan<- error) httprouter.Handle {
|
func serveEnabledMimeTypes(formats types.Types, errorChannel chan<- error) httprouter.Handle {
|
||||||
return func(w http.ResponseWriter, r *http.Request, p httprouter.Params) {
|
return func(w http.ResponseWriter, r *http.Request, p httprouter.Params) {
|
||||||
w.Header().Set("Content-Type", "text/plain")
|
w.Header().Set("Content-Type", "text/plain")
|
||||||
|
|
||||||
startTime := time.Now()
|
startTime := time.Now()
|
||||||
|
|
||||||
written, err := w.Write([]byte(formats.GetMediaTypes()))
|
written, err := w.Write([]byte(formats.GetMimeTypes()))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
errorChannel <- err
|
errorChannel <- err
|
||||||
}
|
}
|
||||||
|
|
||||||
if Verbose {
|
if Verbose {
|
||||||
fmt.Printf("%s | SERVE: Registered media type list (%s) to %s in %s\n",
|
fmt.Printf("%s | SERVE: Registered MIME type list (%s) to %s in %s\n",
|
||||||
startTime.Format(logDate),
|
startTime.Format(logDate),
|
||||||
humanReadableSize(written),
|
humanReadableSize(written),
|
||||||
realIP(r),
|
realIP(r),
|
||||||
|
@ -301,19 +301,19 @@ func serveEnabledMediaTypes(formats types.Types, errorChannel chan<- error) http
|
||||||
|
|
||||||
func registerInfoHandlers(mux *httprouter.Router, args []string, index *fileIndex, formats types.Types, errorChannel chan<- error) {
|
func registerInfoHandlers(mux *httprouter.Router, args []string, index *fileIndex, formats types.Types, errorChannel chan<- error) {
|
||||||
if Index {
|
if Index {
|
||||||
registerHandler(mux, Prefix+"/index/html", serveIndexHtml(args, index, false))
|
registerHandler(mux, Prefix+"/html", serveIndexHtml(args, index, false))
|
||||||
if PageLength != 0 {
|
if PageLength != 0 {
|
||||||
registerHandler(mux, Prefix+"/index/html/:page", serveIndexHtml(args, index, true))
|
registerHandler(mux, Prefix+"/html/:page", serveIndexHtml(args, index, true))
|
||||||
}
|
}
|
||||||
|
|
||||||
registerHandler(mux, Prefix+"/index/json", serveIndexJson(args, index, errorChannel))
|
registerHandler(mux, Prefix+"/json", serveIndexJson(args, index, errorChannel))
|
||||||
if PageLength != 0 {
|
if PageLength != 0 {
|
||||||
registerHandler(mux, Prefix+"/index/json/:page", serveIndexJson(args, index, errorChannel))
|
registerHandler(mux, Prefix+"/json/:page", serveIndexJson(args, index, errorChannel))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
registerHandler(mux, Prefix+"/extensions/available", serveAvailableExtensions(errorChannel))
|
registerHandler(mux, Prefix+"/available_extensions", serveAvailableExtensions(errorChannel))
|
||||||
registerHandler(mux, Prefix+"/extensions/enabled", serveEnabledExtensions(formats, errorChannel))
|
registerHandler(mux, Prefix+"/enabled_extensions", serveEnabledExtensions(formats, errorChannel))
|
||||||
registerHandler(mux, Prefix+"/types/available", serveAvailableMediaTypes(errorChannel))
|
registerHandler(mux, Prefix+"/available_mime_types", serveAvailableMimeTypes(errorChannel))
|
||||||
registerHandler(mux, Prefix+"/types/enabled", serveEnabledMediaTypes(formats, errorChannel))
|
registerHandler(mux, Prefix+"/enabled_mime_types", serveEnabledMimeTypes(formats, errorChannel))
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,46 +0,0 @@
|
||||||
/*
|
|
||||||
Copyright © 2023 Seednode <seednode@seedno.de>
|
|
||||||
*/
|
|
||||||
|
|
||||||
package cmd
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"net/http"
|
|
||||||
"strings"
|
|
||||||
"time"
|
|
||||||
)
|
|
||||||
|
|
||||||
func refreshInterval(r *http.Request) (int64, string) {
|
|
||||||
interval := r.URL.Query().Get("refresh")
|
|
||||||
|
|
||||||
duration, err := time.ParseDuration(interval)
|
|
||||||
|
|
||||||
switch {
|
|
||||||
case err != nil || duration == 0 || !Refresh:
|
|
||||||
return 0, "0ms"
|
|
||||||
case duration < 500*time.Millisecond:
|
|
||||||
return 500, "500ms"
|
|
||||||
default:
|
|
||||||
return duration.Milliseconds(), interval
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func refreshFunction(rootUrl string, refreshTimer int64) string {
|
|
||||||
var htmlBody strings.Builder
|
|
||||||
|
|
||||||
htmlBody.WriteString(fmt.Sprintf("<script>window.onload = function(){ clear = setInterval(function() {window.location.href = '%s';}, %d)};",
|
|
||||||
rootUrl,
|
|
||||||
refreshTimer))
|
|
||||||
htmlBody.WriteString("document.body.onkeyup = function(e) { ")
|
|
||||||
htmlBody.WriteString(`if (e.key == " " || e.code == "Space" || e.keyCode == 32) { `)
|
|
||||||
htmlBody.WriteString(`if (typeof clear !== 'undefined') {`)
|
|
||||||
htmlBody.WriteString(`clearInterval(clear); delete clear;`)
|
|
||||||
htmlBody.WriteString(`} else {`)
|
|
||||||
htmlBody.WriteString(fmt.Sprintf("clear = setInterval(function(){window.location.href = '%s';}, %d);}}}",
|
|
||||||
rootUrl,
|
|
||||||
refreshTimer))
|
|
||||||
htmlBody.WriteString(`</script>`)
|
|
||||||
|
|
||||||
return htmlBody.String()
|
|
||||||
}
|
|
|
@ -12,7 +12,7 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
ReleaseVersion string = "3.0.0"
|
ReleaseVersion string = "2.8.0"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
|
16
cmd/uri.go
16
cmd/uri.go
|
@ -10,8 +10,24 @@ import (
|
||||||
"net/url"
|
"net/url"
|
||||||
"runtime"
|
"runtime"
|
||||||
"strings"
|
"strings"
|
||||||
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
func refreshInterval(r *http.Request) (int64, string) {
|
||||||
|
interval := r.URL.Query().Get("refresh")
|
||||||
|
|
||||||
|
duration, err := time.ParseDuration(interval)
|
||||||
|
|
||||||
|
switch {
|
||||||
|
case err != nil || duration == 0 || !Refresh:
|
||||||
|
return 0, "0ms"
|
||||||
|
case duration < 500*time.Millisecond:
|
||||||
|
return 500, "500ms"
|
||||||
|
default:
|
||||||
|
return duration.Milliseconds(), interval
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func sortOrder(r *http.Request) string {
|
func sortOrder(r *http.Request) string {
|
||||||
sortOrder := r.URL.Query().Get("sort")
|
sortOrder := r.URL.Query().Get("sort")
|
||||||
if sortOrder == "asc" || sortOrder == "desc" {
|
if sortOrder == "asc" || sortOrder == "desc" {
|
||||||
|
|
10
cmd/web.go
10
cmd/web.go
|
@ -297,7 +297,7 @@ func serveMedia(paths []string, regexes *regexes, index *fileIndex, formats type
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
mediaType := format.MediaType(path)
|
mimeType := format.MimeType(path)
|
||||||
|
|
||||||
fileUri := Prefix + generateFileUri(path)
|
fileUri := Prefix + generateFileUri(path)
|
||||||
|
|
||||||
|
@ -314,7 +314,7 @@ func serveMedia(paths []string, regexes *regexes, index *fileIndex, formats type
|
||||||
htmlBody.WriteString(faviconHtml)
|
htmlBody.WriteString(faviconHtml)
|
||||||
htmlBody.WriteString(fmt.Sprintf(`<style>%s</style>`, format.Css()))
|
htmlBody.WriteString(fmt.Sprintf(`<style>%s</style>`, format.Css()))
|
||||||
|
|
||||||
title, err := format.Title(rootUrl, fileUri, path, fileName, Prefix, mediaType)
|
title, err := format.Title(rootUrl, fileUri, path, fileName, Prefix, mimeType)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
errorChannel <- err
|
errorChannel <- err
|
||||||
|
|
||||||
|
@ -325,10 +325,12 @@ func serveMedia(paths []string, regexes *regexes, index *fileIndex, formats type
|
||||||
htmlBody.WriteString(title)
|
htmlBody.WriteString(title)
|
||||||
htmlBody.WriteString(`</head><body>`)
|
htmlBody.WriteString(`</head><body>`)
|
||||||
if refreshInterval != "0ms" {
|
if refreshInterval != "0ms" {
|
||||||
htmlBody.WriteString(refreshFunction(rootUrl, refreshTimer))
|
htmlBody.WriteString(fmt.Sprintf("<script>window.onload = function(){clear = setInterval(function(){window.location.href = '%s';}, %d); document.body.onkeyup = function(e) { if (e.key == \"\" || e.code == \"Space\" || e.keyCode == 32){clearInterval(clear)}}};</script>",
|
||||||
|
rootUrl,
|
||||||
|
refreshTimer))
|
||||||
}
|
}
|
||||||
|
|
||||||
body, err := format.Body(rootUrl, fileUri, path, fileName, Prefix, mediaType)
|
body, err := format.Body(rootUrl, fileUri, path, fileName, Prefix, mimeType)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
errorChannel <- err
|
errorChannel <- err
|
||||||
|
|
||||||
|
|
|
@ -44,7 +44,7 @@ func (t Format) Extensions() map[string]string {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t Format) MediaType(extension string) string {
|
func (t Format) MimeType(extension string) string {
|
||||||
extensions := t.Extensions()
|
extensions := t.Extensions()
|
||||||
|
|
||||||
value, exists := extensions[extension]
|
value, exists := extensions[extension]
|
||||||
|
|
|
@ -210,7 +210,7 @@ func (t Format) Extensions() map[string]string {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t Format) MediaType(extension string) string {
|
func (t Format) MimeType(extension string) string {
|
||||||
extensions := t.Extensions()
|
extensions := t.Extensions()
|
||||||
|
|
||||||
value, exists := extensions[extension]
|
value, exists := extensions[extension]
|
||||||
|
|
|
@ -43,7 +43,7 @@ func (t Format) Extensions() map[string]string {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t Format) MediaType(extension string) string {
|
func (t Format) MimeType(extension string) string {
|
||||||
extensions := t.Extensions()
|
extensions := t.Extensions()
|
||||||
|
|
||||||
value, exists := extensions[extension]
|
value, exists := extensions[extension]
|
||||||
|
|
|
@ -97,7 +97,7 @@ func (t Format) Validate(filePath string) bool {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t Format) MediaType(extension string) string {
|
func (t Format) MimeType(extension string) string {
|
||||||
extensions := t.Extensions()
|
extensions := t.Extensions()
|
||||||
|
|
||||||
value, exists := extensions[extension]
|
value, exists := extensions[extension]
|
||||||
|
|
|
@ -51,7 +51,7 @@ func (t Format) Extensions() map[string]string {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t Format) MediaType(extension string) string {
|
func (t Format) MimeType(extension string) string {
|
||||||
extensions := t.Extensions()
|
extensions := t.Extensions()
|
||||||
|
|
||||||
value, exists := extensions[extension]
|
value, exists := extensions[extension]
|
||||||
|
|
|
@ -31,7 +31,7 @@ type Type interface {
|
||||||
|
|
||||||
// Given a file extension, returns the corresponding MIME type,
|
// Given a file extension, returns the corresponding MIME type,
|
||||||
// if one exists. Otherwise, returns an empty string.
|
// if one exists. Otherwise, returns an empty string.
|
||||||
MediaType(extension string) string
|
MimeType(extension string) string
|
||||||
|
|
||||||
// Optional function used to validate whether a given file matches this format.
|
// Optional function used to validate whether a given file matches this format.
|
||||||
// If no validation checks are needed, this function should always return true.
|
// If no validation checks are needed, this function should always return true.
|
||||||
|
@ -92,23 +92,23 @@ func (t Types) GetExtensions() string {
|
||||||
return output.String()
|
return output.String()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t Types) GetMediaTypes() string {
|
func (t Types) GetMimeTypes() string {
|
||||||
var output strings.Builder
|
var output strings.Builder
|
||||||
|
|
||||||
var mediaTypes []string
|
var mimeTypes []string
|
||||||
|
|
||||||
for _, j := range t {
|
for _, j := range t {
|
||||||
extensions := j.Extensions()
|
extensions := j.Extensions()
|
||||||
for _, v := range extensions {
|
for _, v := range extensions {
|
||||||
mediaTypes = append(mediaTypes, v)
|
mimeTypes = append(mimeTypes, v)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
mediaTypes = removeDuplicateStr(mediaTypes)
|
mimeTypes = removeDuplicateStr(mimeTypes)
|
||||||
|
|
||||||
slices.Sort(mediaTypes)
|
slices.Sort(mimeTypes)
|
||||||
|
|
||||||
for _, v := range mediaTypes {
|
for _, v := range mimeTypes {
|
||||||
output.WriteString(v + "\n")
|
output.WriteString(v + "\n")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -48,7 +48,7 @@ func (t Format) Extensions() map[string]string {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t Format) MediaType(path string) string {
|
func (t Format) MimeType(path string) string {
|
||||||
extensions := t.Extensions()
|
extensions := t.Extensions()
|
||||||
|
|
||||||
extension := filepath.Ext(path)
|
extension := filepath.Ext(path)
|
||||||
|
|
Loading…
Reference in New Issue