Added video support

This commit is contained in:
Seednode 2023-05-08 20:05:10 -05:00
parent f92384f0ce
commit 3e1fb6be6a
4 changed files with 60 additions and 34 deletions

View File

@ -1,14 +1,14 @@
## About ## About
Sometimes, you just need a way to randomly display images from your filesystem. Sometimes, you just need a way to randomly display media from your filesystem.
Simply point this tool at one or more directories, and then open the specified port (default `8080`) in your browser. Simply point this tool at one or more directories, and then open the specified port (default `8080`) in your browser.
A new image will be selected if you open `/` directly, or if you click on any displayed image. A new file will be selected if you open `/` directly, or if you click on any displayed media.
Browser history is preserved, so you can always go back to any previously displayed image. Browser history is preserved, so you can always go back to any previously displayed media.
Supported file types and extensions are `bmp`, `gif`, `jp[e]g`, `png`, and `webp`. Supported file types and extensions are `bmp`, `gif`, `jp[e]g`, `png`, `webp`, `mp4`, `ogv`, and `webm`.
Feature requests, code criticism, bug reports, general chit-chat, and unrelated angst accepted at `roulette@seedno.de`. Feature requests, code criticism, bug reports, general chit-chat, and unrelated angst accepted at `roulette@seedno.de`.
@ -60,7 +60,7 @@ Note: These patterns require sequentially-numbered files matching the following
If a positive-value `refresh=<integer><unit>` query parameter is provided, the page will reload after that interval. If 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 images. This can be used to generate a sort of slideshow of files.
Supported units are `ns`, `us`/`µs`, `ms`, `s`, `m`, and `h`. Supported units are `ns`, `us`/`µs`, `ms`, `s`, `m`, and `h`.

View File

@ -43,7 +43,7 @@ type Concurrency struct {
var ( var (
ErrNoImagesFound = fmt.Errorf("no supported image formats found which match all criteria") ErrNoImagesFound = fmt.Errorf("no supported image formats found which match all criteria")
Extensions = [6]string{".jpg", ".jpeg", ".png", ".gif", ".webp", ".bmp"} Extensions = [9]string{".jpg", ".jpeg", ".png", ".gif", ".webp", ".bmp", ".mp4", ".ogv", ".webm"}
) )
type Dimensions struct { type Dimensions struct {
@ -168,12 +168,12 @@ func preparePath(path string) string {
func appendPath(directory, path string, files *Files, stats *ScanStats, shouldCache bool) error { func appendPath(directory, path string, files *Files, stats *ScanStats, shouldCache bool) error {
if shouldCache { if shouldCache {
image, err := isImage(path) image, video, err := isSupportedFileType(path)
if err != nil { if err != nil {
return err return err
} }
if !image { if !(image || video) {
return nil return nil
} }
} }
@ -382,20 +382,27 @@ func pathIsValid(filePath string, paths []string) bool {
} }
} }
func isImage(path string) (bool, error) { func isSupportedFileType(path string) (bool, bool, error) {
file, err := os.Open(path) file, err := os.Open(path)
switch { switch {
case errors.Is(err, os.ErrNotExist): case errors.Is(err, os.ErrNotExist):
return false, nil return false, false, nil
case err != nil: case err != nil:
return false, err return false, false, err
} }
defer file.Close() defer file.Close()
head := make([]byte, 261) head := make([]byte, 261)
file.Read(head) file.Read(head)
return filetype.IsImage(head), nil switch {
case filetype.IsImage(head):
return true, false, nil
case filetype.IsVideo(head):
return false, true, nil
default:
return false, false, nil
}
} }
func pathHasSupportedFiles(path string) (bool, error) { func pathHasSupportedFiles(path string) (bool, error) {
@ -410,12 +417,12 @@ func pathHasSupportedFiles(path string) (bool, error) {
case !recursive && info.IsDir() && p != path: case !recursive && info.IsDir() && p != path:
return filepath.SkipDir return filepath.SkipDir
case !info.IsDir(): case !info.IsDir():
image, err := isImage(p) image, video, err := isSupportedFileType(p)
if err != nil { if err != nil {
return err return err
} }
if image { if image || video {
hasSupportedFiles <- true hasSupportedFiles <- true
return filepath.SkipAll return filepath.SkipAll
} }
@ -609,12 +616,12 @@ func pickFile(args []string, filters *Filters, sort string, index *Index) (strin
filePath := fileList[r] filePath := fileList[r]
if !fromCache { if !fromCache {
isImage, err := isImage(filePath) image, video, err := isSupportedFileType(filePath)
if err != nil { if err != nil {
return "", err return "", err
} }
if isImage { if image || video {
return filePath, nil return filePath, nil
} }

View File

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

View File

@ -751,22 +751,27 @@ func serveImage(paths []string, Regexes *Regexes, index *Index) http.HandlerFunc
return return
} }
image, err := isImage(filePath) image, video, err := isSupportedFileType(filePath)
if err != nil { if err != nil {
fmt.Println(err) fmt.Println(err)
return return
} }
if !image {
if !(image || video) {
notFound(w, r, filePath) notFound(w, r, filePath)
return return
} }
dimensions, err := imageDimensions(filePath) var dimensions *Dimensions
if image {
dimensions, err = imageDimensions(filePath)
if err != nil { if err != nil {
fmt.Println(err) fmt.Println(err)
return return
} }
}
fileName := filepath.Base(filePath) fileName := filepath.Base(filePath)
@ -782,24 +787,38 @@ func serveImage(paths []string, Regexes *Regexes, index *Index) http.HandlerFunc
htmlBody.WriteString(`<!DOCTYPE html><html lang="en"><head>`) htmlBody.WriteString(`<!DOCTYPE html><html lang="en"><head>`)
htmlBody.WriteString(`<style>html,body{margin:0;padding:0;height:100%;}`) htmlBody.WriteString(`<style>html,body{margin:0;padding:0;height:100%;}`)
htmlBody.WriteString(`a{display:block;height:100%;width:100%;text-decoration:none;}`) htmlBody.WriteString(`a{display:block;height:100%;width:100%;text-decoration:none;}`)
htmlBody.WriteString(`img{margin:auto;display:block;max-width:97%;max-height:97%;object-fit:scale-down;`) htmlBody.WriteString(`img,video{margin:auto;display:block;max-width:97%;max-height:97%;object-fit:scale-down;`)
htmlBody.WriteString(`position:absolute;top:50%;left:50%;transform:translate(-50%,-50%);}</style>`) htmlBody.WriteString(`position:absolute;top:50%;left:50%;transform:translate(-50%,-50%);}</style>`)
switch {
case image:
htmlBody.WriteString(fmt.Sprintf(`<title>%s (%dx%d)</title>`, htmlBody.WriteString(fmt.Sprintf(`<title>%s (%dx%d)</title>`,
fileName, fileName,
dimensions.width, dimensions.width,
dimensions.height)) dimensions.height))
case video:
htmlBody.WriteString(fmt.Sprintf(`<title>%s</title>`,
fileName))
}
htmlBody.WriteString(`</head><body>`) htmlBody.WriteString(`</head><body>`)
if refreshInterval != "0ms" { if refreshInterval != "0ms" {
htmlBody.WriteString(fmt.Sprintf("<script>window.onload = function(){setInterval(function(){window.location.href = '/%s';}, %d);};</script>", htmlBody.WriteString(fmt.Sprintf("<script>window.onload = function(){setInterval(function(){window.location.href = '/%s';}, %d);};</script>",
queryParams, queryParams,
refreshTimer)) refreshTimer))
} }
switch {
case image:
htmlBody.WriteString(fmt.Sprintf(`<a href="/%s"><img src="%s" width="%d" height="%d" alt="Roulette selected: %s"></a>`, htmlBody.WriteString(fmt.Sprintf(`<a href="/%s"><img src="%s" width="%d" height="%d" alt="Roulette selected: %s"></a>`,
queryParams, queryParams,
generateFilePath(filePath), generateFilePath(filePath),
dimensions.width, dimensions.width,
dimensions.height, dimensions.height,
fileName)) fileName))
case video:
htmlBody.WriteString(fmt.Sprintf(`<a href="/%s"><video src="%s" alt="Roulette selected: %s" autoplay controls></a>`,
queryParams,
generateFilePath(filePath),
fileName))
}
htmlBody.WriteString(`</body></html>`) htmlBody.WriteString(`</body></html>`)
_, err = io.WriteString(w, gohtml.Format(htmlBody.String())) _, err = io.WriteString(w, gohtml.Format(htmlBody.String()))