diff --git a/cmd/files.go b/cmd/files.go index a073c86..e0297f8 100644 --- a/cmd/files.go +++ b/cmd/files.go @@ -20,7 +20,7 @@ import ( "sync/atomic" "time" - "seedno.de/seednode/roulette/formats" + "seedno.de/seednode/roulette/types" ) type maxConcurrency int @@ -108,9 +108,9 @@ func preparePath(path string) string { return MediaPrefix + path } -func appendPath(directory, path string, files *Files, stats *ScanStats, registeredFormats *formats.SupportedFormats, shouldCache bool) error { +func appendPath(directory, path string, files *Files, stats *ScanStats, formats *types.Types, shouldCache bool) error { if shouldCache { - registered, _, _, err := formats.FileType(path, registeredFormats) + registered, _, _, err := types.FileType(path, formats) if err != nil { return err } @@ -127,7 +127,7 @@ func appendPath(directory, path string, files *Files, stats *ScanStats, register return nil } -func appendPaths(path string, files *Files, filters *Filters, stats *ScanStats, types *formats.SupportedFormats) error { +func appendPaths(path string, files *Files, filters *Filters, stats *ScanStats, formats *types.Types) error { shouldCache := Cache && filters.IsEmpty() absolutePath, err := filepath.Abs(path) @@ -158,7 +158,7 @@ func appendPaths(path string, files *Files, filters *Filters, stats *ScanStats, filename, filters.includes[i], ) { - err := appendPath(directory, path, files, stats, types, shouldCache) + err := appendPath(directory, path, files, stats, formats, shouldCache) if err != nil { return err } @@ -172,7 +172,7 @@ func appendPaths(path string, files *Files, filters *Filters, stats *ScanStats, return nil } - err = appendPath(directory, path, files, stats, types, shouldCache) + err = appendPath(directory, path, files, stats, formats, shouldCache) if err != nil { return err } @@ -180,8 +180,8 @@ func appendPaths(path string, files *Files, filters *Filters, stats *ScanStats, return nil } -func newFile(paths []string, filters *Filters, sortOrder string, Regexes *Regexes, index *FileIndex, registeredFormats *formats.SupportedFormats) (string, error) { - filePath, err := pickFile(paths, filters, sortOrder, index, registeredFormats) +func newFile(paths []string, filters *Filters, sortOrder string, Regexes *Regexes, index *FileIndex, formats *types.Types) (string, error) { + filePath, err := pickFile(paths, filters, sortOrder, index, formats) if err != nil { return "", nil } @@ -195,7 +195,7 @@ func newFile(paths []string, filters *Filters, sortOrder string, Regexes *Regexe switch { case sortOrder == "asc": - filePath, err = tryExtensions(path, registeredFormats) + filePath, err = tryExtensions(path, formats) if err != nil { return "", err } @@ -203,7 +203,7 @@ func newFile(paths []string, filters *Filters, sortOrder string, Regexes *Regexe for { path.increment() - filePath, err = tryExtensions(path, registeredFormats) + filePath, err = tryExtensions(path, formats) if err != nil { return "", err } @@ -211,7 +211,7 @@ func newFile(paths []string, filters *Filters, sortOrder string, Regexes *Regexe if filePath == "" { path.decrement() - filePath, err = tryExtensions(path, registeredFormats) + filePath, err = tryExtensions(path, formats) if err != nil { return "", err } @@ -224,7 +224,7 @@ func newFile(paths []string, filters *Filters, sortOrder string, Regexes *Regexe return filePath, nil } -func nextFile(filePath, sortOrder string, Regexes *Regexes, registeredFormats *formats.SupportedFormats) (string, error) { +func nextFile(filePath, sortOrder string, Regexes *Regexes, formats *types.Types) (string, error) { path, err := splitPath(filePath, Regexes) if err != nil { return "", err @@ -239,7 +239,7 @@ func nextFile(filePath, sortOrder string, Regexes *Regexes, registeredFormats *f return "", nil } - fileName, err := tryExtensions(path, registeredFormats) + fileName, err := tryExtensions(path, formats) if err != nil { return "", err } @@ -269,10 +269,10 @@ func splitPath(path string, Regexes *Regexes) (*Path, error) { return &p, nil } -func tryExtensions(p *Path, registeredFormats *formats.SupportedFormats) (string, error) { +func tryExtensions(p *Path, formats *types.Types) (string, error) { var fileName string - for _, format := range registeredFormats.Extensions { + for _, format := range formats.Extensions { for _, extension := range format.Extensions { fileName = fmt.Sprintf("%s%.3d%s", p.base, p.number, extension) } @@ -326,7 +326,7 @@ func pathIsValid(filePath string, paths []string) bool { } } -func pathHasSupportedFiles(path string, registeredFormats *formats.SupportedFormats) (bool, error) { +func pathHasSupportedFiles(path string, formats *types.Types) (bool, error) { hasRegisteredFiles := make(chan bool, 1) err := filepath.WalkDir(path, func(p string, info os.DirEntry, err error) error { @@ -338,7 +338,7 @@ func pathHasSupportedFiles(path string, registeredFormats *formats.SupportedForm case !Recursive && info.IsDir() && p != path: return filepath.SkipDir case !info.IsDir(): - registered, _, _, err := formats.FileType(p, registeredFormats) + registered, _, _, err := types.FileType(p, formats) if err != nil { return err } @@ -383,7 +383,7 @@ func pathCount(path string) (uint64, uint64, error) { return files, directories, nil } -func scanPath(path string, files *Files, filters *Filters, stats *ScanStats, concurrency *Concurrency, types *formats.SupportedFormats) error { +func scanPath(path string, files *Files, filters *Filters, stats *ScanStats, concurrency *Concurrency, formats *types.Types) error { var wg sync.WaitGroup err := filepath.WalkDir(path, func(p string, info os.DirEntry, err error) error { @@ -410,7 +410,7 @@ func scanPath(path string, files *Files, filters *Filters, stats *ScanStats, con fmt.Println(err) } - err = appendPaths(path, files, filters, stats, types) + err = appendPaths(path, files, filters, stats, formats) if err != nil { fmt.Println(err) } @@ -444,7 +444,7 @@ func scanPath(path string, files *Files, filters *Filters, stats *ScanStats, con return nil } -func fileList(paths []string, filters *Filters, sort string, index *FileIndex, types *formats.SupportedFormats) ([]string, bool) { +func fileList(paths []string, filters *Filters, sort string, index *FileIndex, formats *types.Types) ([]string, bool) { if Cache && filters.IsEmpty() && !index.IsEmpty() { return index.Index(), true } @@ -483,7 +483,7 @@ func fileList(paths []string, filters *Filters, sort string, index *FileIndex, t wg.Done() }() - err := scanPath(paths[i], files, filters, stats, concurrency, types) + err := scanPath(paths[i], files, filters, stats, concurrency, formats) if err != nil { fmt.Println(err) } @@ -558,8 +558,8 @@ func prepareDirectories(files *Files, sort string) []string { return directories } -func pickFile(args []string, filters *Filters, sort string, index *FileIndex, registeredFormats *formats.SupportedFormats) (string, error) { - fileList, fromCache := fileList(args, filters, sort, index, registeredFormats) +func pickFile(args []string, filters *Filters, sort string, index *FileIndex, formats *types.Types) (string, error) { + fileList, fromCache := fileList(args, filters, sort, index, formats) fileCount := len(fileList) if fileCount < 1 { @@ -586,7 +586,7 @@ func pickFile(args []string, filters *Filters, sort string, index *FileIndex, re filePath := fileList[val] if !fromCache { - registered, _, _, err := formats.FileType(filePath, registeredFormats) + registered, _, _, err := types.FileType(filePath, formats) if err != nil { return "", err } @@ -618,7 +618,7 @@ func normalizePath(path string) (string, error) { return absolutePath, nil } -func normalizePaths(args []string, types *formats.SupportedFormats) ([]string, error) { +func normalizePaths(args []string, formats *types.Types) ([]string, error) { var paths []string var pathList strings.Builder @@ -632,7 +632,7 @@ func normalizePaths(args []string, types *formats.SupportedFormats) ([]string, e pathMatches := (args[i] == path) - hasSupportedFiles, err := pathHasSupportedFiles(path, types) + hasSupportedFiles, err := pathHasSupportedFiles(path, formats) if err != nil { return nil, err } diff --git a/cmd/index.go b/cmd/index.go index 18a2cdc..0f6b5ba 100644 --- a/cmd/index.go +++ b/cmd/index.go @@ -20,7 +20,7 @@ import ( "github.com/julienschmidt/httprouter" "github.com/klauspost/compress/zstd" "github.com/yosssi/gohtml" - "seedno.de/seednode/roulette/formats" + "seedno.de/seednode/roulette/types" ) type FileIndex struct { @@ -66,12 +66,12 @@ func (i *FileIndex) setIndex(val []string) { i.mutex.Unlock() } -func (i *FileIndex) generateCache(args []string, registeredFormats *formats.SupportedFormats) { +func (i *FileIndex) generateCache(args []string, formats *types.Types) { i.mutex.Lock() i.list = []string{} i.mutex.Unlock() - fileList(args, &Filters{}, "", i, registeredFormats) + fileList(args, &Filters{}, "", i, formats) if Cache && CacheFile != "" { i.Export(CacheFile) @@ -138,9 +138,9 @@ func (i *FileIndex) Import(path string) error { return nil } -func serveCacheClear(args []string, index *FileIndex, registeredFormats *formats.SupportedFormats) httprouter.Handle { +func serveCacheClear(args []string, index *FileIndex, formats *types.Types) httprouter.Handle { return func(w http.ResponseWriter, r *http.Request, p httprouter.Params) { - index.generateCache(args, registeredFormats) + index.generateCache(args, formats) w.Header().Set("Content-Type", "text/plain") diff --git a/cmd/root.go b/cmd/root.go index 650608e..266a37e 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -12,7 +12,7 @@ import ( ) const ( - ReleaseVersion string = "0.70.3" + ReleaseVersion string = "0.71.0" ) var ( @@ -45,7 +45,7 @@ var ( Use: "roulette [path]...", Short: "Serves random media from the specified directories.", Args: cobra.MinimumNArgs(1), - PreRun: func(cmd *cobra.Command, args []string) { + PreRunE: func(cmd *cobra.Command, args []string) error { // enable image support if no other flags are passed, to retain backwards compatibility // to be replaced with MarkFlagsOneRequired on next spf13/cobra update if !(All || Audio || Flash || Images || Text || Videos) { @@ -59,9 +59,11 @@ var ( if RefreshInterval != "" { interval, err := time.ParseDuration(RefreshInterval) if err != nil || interval < 500*time.Millisecond { - log.Fatal(ErrIncorrectRefreshInterval) + return ErrIncorrectRefreshInterval } } + + return nil }, RunE: func(cmd *cobra.Command, args []string) error { err := ServePage(args) diff --git a/cmd/web.go b/cmd/web.go index cfb2d5d..2ebc1b7 100644 --- a/cmd/web.go +++ b/cmd/web.go @@ -26,7 +26,7 @@ import ( "github.com/julienschmidt/httprouter" "github.com/yosssi/gohtml" - "seedno.de/seednode/roulette/formats" + "seedno.de/seednode/roulette/types" ) const ( @@ -127,7 +127,7 @@ func serveStaticFile(paths []string, stats *ServeStats, index *FileIndex) httpro } } -func serveRoot(paths []string, Regexes *Regexes, index *FileIndex, registeredFormats *formats.SupportedFormats) httprouter.Handle { +func serveRoot(paths []string, Regexes *Regexes, index *FileIndex, formats *types.Types) httprouter.Handle { return func(w http.ResponseWriter, r *http.Request, p httprouter.Params) { refererUri, err := stripQueryParams(refererToUri(r.Referer())) if err != nil { @@ -152,7 +152,7 @@ func serveRoot(paths []string, Regexes *Regexes, index *FileIndex, registeredFor var filePath string if refererUri != "" { - filePath, err = nextFile(strippedRefererUri, sortOrder, Regexes, registeredFormats) + filePath, err = nextFile(strippedRefererUri, sortOrder, Regexes, formats) if err != nil { fmt.Println(err) @@ -174,7 +174,7 @@ func serveRoot(paths []string, Regexes *Regexes, index *FileIndex, registeredFor break loop } - filePath, err = newFile(paths, filters, sortOrder, Regexes, index, registeredFormats) + filePath, err = newFile(paths, filters, sortOrder, Regexes, index, formats) switch { case err != nil && err == ErrNoMediaFound: notFound(w, r, filePath) @@ -200,7 +200,7 @@ func serveRoot(paths []string, Regexes *Regexes, index *FileIndex, registeredFor } } -func serveMedia(paths []string, Regexes *Regexes, index *FileIndex, registeredFormats *formats.SupportedFormats) httprouter.Handle { +func serveMedia(paths []string, Regexes *Regexes, index *FileIndex, formats *types.Types) httprouter.Handle { return func(w http.ResponseWriter, r *http.Request, p httprouter.Params) { filters := &Filters{ includes: splitQueryParams(r.URL.Query().Get("include"), Regexes), @@ -229,7 +229,7 @@ func serveMedia(paths []string, Regexes *Regexes, index *FileIndex, registeredFo return } - registered, fileType, mimeType, err := formats.FileType(filePath, registeredFormats) + registered, fileType, mimeType, err := types.FileType(filePath, formats) if err != nil { fmt.Println(err) @@ -257,12 +257,7 @@ func serveMedia(paths []string, Regexes *Regexes, index *FileIndex, registeredFo var htmlBody strings.Builder htmlBody.WriteString(``) htmlBody.WriteString(FaviconHtml) - htmlBody.WriteString(``) + htmlBody.WriteString(fmt.Sprintf(``, fileType.Css())) htmlBody.WriteString((fileType.Title(queryParams, fileUri, filePath, fileName, mimeType))) htmlBody.WriteString(``) if refreshInterval != "0ms" { @@ -316,32 +311,32 @@ func ServePage(args []string) error { mux := httprouter.New() - registeredFormats := &formats.SupportedFormats{ - Extensions: make(map[string]*formats.SupportedFormat), - MimeTypes: make(map[string]*formats.SupportedFormat), + formats := &types.Types{ + Extensions: make(map[string]*types.Type), + MimeTypes: make(map[string]*types.Type), } if Audio || All { - registeredFormats.Add(formats.RegisterAudioFormats()) + formats.Add(types.RegisterAudio()) } if Flash || All { - registeredFormats.Add(formats.RegisterFlashFormats()) + formats.Add(types.RegisterFlash()) } if Images || All { - registeredFormats.Add(formats.RegisterImageFormats()) + formats.Add(types.RegisterImages()) } if Text || All { - registeredFormats.Add(formats.RegisterTextFormats()) + formats.Add(types.RegisterText()) } if Videos || All { - registeredFormats.Add(formats.RegisterVideoFormats()) + formats.Add(types.RegisterVideos()) } - paths, err := normalizePaths(args, registeredFormats) + paths, err := normalizePaths(args, formats) if err != nil { return err } @@ -382,13 +377,13 @@ func ServePage(args []string) error { mux.PanicHandler = serverErrorHandler() - mux.GET("/", serveRoot(paths, regexes, index, registeredFormats)) + mux.GET("/", serveRoot(paths, regexes, index, formats)) mux.GET("/favicons/*favicon", serveFavicons()) mux.GET("/favicon.ico", serveFavicons()) - mux.GET(MediaPrefix+"/*media", serveMedia(paths, regexes, index, registeredFormats)) + mux.GET(MediaPrefix+"/*media", serveMedia(paths, regexes, index, formats)) mux.GET(SourcePrefix+"/*static", serveStaticFile(paths, stats, index)) @@ -405,10 +400,10 @@ func ServePage(args []string) error { } if !skipIndex { - index.generateCache(args, registeredFormats) + index.generateCache(args, formats) } - mux.GET("/clear_cache", serveCacheClear(args, index, registeredFormats)) + mux.GET("/clear_cache", serveCacheClear(args, index, formats)) } if Index { diff --git a/formats/audio.go b/types/audio.go similarity index 70% rename from formats/audio.go rename to types/audio.go index 50d3401..3b34677 100644 --- a/formats/audio.go +++ b/types/audio.go @@ -2,15 +2,23 @@ Copyright © 2023 Seednode */ -package formats +package types import ( "fmt" + "strings" ) -func RegisterAudioFormats() *SupportedFormat { - return &SupportedFormat{ - Css: ``, +func RegisterAudio() *Type { + return &Type{ + Css: func() 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;}`) + + return css.String() + }, Title: func(queryParams, fileUri, filePath, fileName, mime string) string { return fmt.Sprintf(`%s`, fileName) }, diff --git a/formats/flash.go b/types/flash.go similarity index 73% rename from formats/flash.go rename to types/flash.go index 5bd93d3..824f742 100644 --- a/formats/flash.go +++ b/types/flash.go @@ -2,16 +2,23 @@ Copyright © 2023 Seednode */ -package formats +package types import ( "fmt" "strings" ) -func RegisterFlashFormats() *SupportedFormat { - return &SupportedFormat{ - Css: ``, +func RegisterFlash() *Type { + return &Type{ + Css: func() 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;}`) + + return css.String() + }, Title: func(queryParams, fileUri, filePath, fileName, mime string) string { return fmt.Sprintf(`%s`, fileName) }, diff --git a/formats/images.go b/types/images.go similarity index 80% rename from formats/images.go rename to types/images.go index 5f6d968..bea8af1 100644 --- a/formats/images.go +++ b/types/images.go @@ -2,7 +2,7 @@ Copyright © 2023 Seednode */ -package formats +package types import ( "errors" @@ -12,6 +12,7 @@ import ( _ "image/jpeg" _ "image/png" "os" + "strings" _ "golang.org/x/image/bmp" _ "golang.org/x/image/webp" @@ -22,9 +23,18 @@ type Dimensions struct { Height int } -func RegisterImageFormats() *SupportedFormat { - return &SupportedFormat{ - Css: ``, +func RegisterImages() *Type { + return &Type{ + Css: func() 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;}`) + 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%);}`) + + return css.String() + }, Title: func(queryParams, fileUri, filePath, fileName, mime string) string { dimensions, err := ImageDimensions(filePath) if err != nil { diff --git a/formats/text.go b/types/text.go similarity index 75% rename from formats/text.go rename to types/text.go index 52a6ca7..99d4824 100644 --- a/formats/text.go +++ b/types/text.go @@ -2,18 +2,27 @@ Copyright © 2023 Seednode */ -package formats +package types import ( "errors" "fmt" "os" + "strings" "unicode/utf8" ) -func RegisterTextFormats() *SupportedFormat { - return &SupportedFormat{ - Css: `pre{margin:.5rem;}`, +func RegisterText() *Type { + return &Type{ + Css: func() 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;}`) + css.WriteString(`pre{margin:.5rem;}`) + + return css.String() + }, Title: func(queryParams, fileUri, filePath, fileName, mime string) string { return fmt.Sprintf(`%s`, fileName) }, diff --git a/formats/types.go b/types/types.go similarity index 78% rename from formats/types.go rename to types/types.go index 41b45ba..33c3c3b 100644 --- a/formats/types.go +++ b/types/types.go @@ -2,7 +2,7 @@ Copyright © 2023 Seednode */ -package formats +package types import ( "errors" @@ -11,8 +11,8 @@ import ( "path/filepath" ) -type SupportedFormat struct { - Css string +type Type struct { + Css func() string Title func(queryParams, fileUri, filePath, fileName, mime string) string Body func(queryParams, fileUri, filePath, fileName, mime string) string Extensions []string @@ -20,12 +20,12 @@ type SupportedFormat struct { Validate func(filePath string) bool } -type SupportedFormats struct { - Extensions map[string]*SupportedFormat - MimeTypes map[string]*SupportedFormat +type Types struct { + Extensions map[string]*Type + MimeTypes map[string]*Type } -func (s *SupportedFormats) Add(t *SupportedFormat) { +func (s *Types) Add(t *Type) { for _, v := range t.Extensions { _, exists := s.Extensions[v] if !exists { @@ -41,7 +41,7 @@ func (s *SupportedFormats) Add(t *SupportedFormat) { } } -func FileType(path string, registeredFormats *SupportedFormats) (bool, *SupportedFormat, string, error) { +func FileType(path string, registeredFormats *Types) (bool, *Type, string, error) { file, err := os.Open(path) switch { case errors.Is(err, os.ErrNotExist): diff --git a/formats/video.go b/types/video.go similarity index 59% rename from formats/video.go rename to types/video.go index ccd718a..cdaaffc 100644 --- a/formats/video.go +++ b/types/video.go @@ -2,15 +2,25 @@ Copyright © 2023 Seednode */ -package formats +package types import ( "fmt" + "strings" ) -func RegisterVideoFormats() *SupportedFormat { - return &SupportedFormat{ - Css: ``, +func RegisterVideos() *Type { + return &Type{ + Css: func() 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;}`) + css.WriteString(`video{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%);}`) + + return css.String() + }, Title: func(queryParams, fileUri, filePath, fileName, mime string) string { return fmt.Sprintf(`%s`, fileName) },