Compare commits

...

9 Commits

Author SHA1 Message Date
Bel LaPointe
5b67d5c5f0 sort desc by date 2024-09-19 12:24:00 -04:00
Bel LaPointe
93aecf47c6 more files 2024-09-19 12:17:25 -04:00
Bel LaPointe
69e2789b9b 38080 2024-09-19 12:16:31 -04:00
Bel LaPointe
3e6d2874d5 ui mvp ish 2024-09-19 12:16:11 -04:00
Bel LaPointe
e377a4f5db ignore testdata 2024-09-19 12:07:40 -04:00
Bel LaPointe
2668cbed7d refactor /cmd/prune/main.go#ls to /pkg/fs/ls.go 2024-09-19 11:25:32 -04:00
Bel LaPointe
bea85bc736 no binary 2024-09-19 11:25:09 -04:00
Bel LaPointe
b5c146bd1d gitignore 2024-09-19 11:25:03 -04:00
Bel LaPointe
c761ea38b8 promote 2024-09-19 10:56:09 -04:00
47 changed files with 195 additions and 19 deletions

6
.gitignore vendored Normal file
View File

@@ -0,0 +1,6 @@
/cmd/prune/prune
/prune
/cmd/ui/ui
/ui
**/*.sw*
**/testdata

View File

@@ -16,6 +16,8 @@ import (
"strings" "strings"
"syscall" "syscall"
"time" "time"
"ffmpeg.d/pkg/fs"
) )
func main() { func main() {
@@ -244,23 +246,9 @@ func mv(wPath, rPath string) error {
} }
func lsd(d string) ([]string, error) { func lsd(d string) ([]string, error) {
return ls(d, true) return fs.LsD(d)
} }
func lsf(d string) ([]string, error) { func lsf(d string) ([]string, error) {
return ls(d, false) return fs.LsF(d)
}
func ls(d string, dirs bool) ([]string, error) {
entries, err := os.ReadDir(d)
results := make([]string, 0, len(entries))
for i := range entries {
if dirs == entries[i].IsDir() {
results = append(results, path.Join(d, entries[i].Name()))
}
}
sort.Strings(results)
return results, err
} }

View File

Before

Width:  |  Height:  |  Size: 12 KiB

After

Width:  |  Height:  |  Size: 12 KiB

View File

Before

Width:  |  Height:  |  Size: 9.2 KiB

After

Width:  |  Height:  |  Size: 9.2 KiB

View File

Before

Width:  |  Height:  |  Size: 9.7 KiB

After

Width:  |  Height:  |  Size: 9.7 KiB

View File

Before

Width:  |  Height:  |  Size: 10 KiB

After

Width:  |  Height:  |  Size: 10 KiB

View File

Before

Width:  |  Height:  |  Size: 9.9 KiB

After

Width:  |  Height:  |  Size: 9.9 KiB

View File

Before

Width:  |  Height:  |  Size: 9.8 KiB

After

Width:  |  Height:  |  Size: 9.8 KiB

View File

Before

Width:  |  Height:  |  Size: 10 KiB

After

Width:  |  Height:  |  Size: 10 KiB

View File

Before

Width:  |  Height:  |  Size: 9.9 KiB

After

Width:  |  Height:  |  Size: 9.9 KiB

View File

Before

Width:  |  Height:  |  Size: 10 KiB

After

Width:  |  Height:  |  Size: 10 KiB

View File

Before

Width:  |  Height:  |  Size: 10 KiB

After

Width:  |  Height:  |  Size: 10 KiB

View File

Before

Width:  |  Height:  |  Size: 10 KiB

After

Width:  |  Height:  |  Size: 10 KiB

View File

Before

Width:  |  Height:  |  Size: 10 KiB

After

Width:  |  Height:  |  Size: 10 KiB

View File

Before

Width:  |  Height:  |  Size: 13 KiB

After

Width:  |  Height:  |  Size: 13 KiB

View File

Before

Width:  |  Height:  |  Size: 13 KiB

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

View File

Before

Width:  |  Height:  |  Size: 9.1 MiB

After

Width:  |  Height:  |  Size: 9.1 MiB

View File

Before

Width:  |  Height:  |  Size: 8.3 MiB

After

Width:  |  Height:  |  Size: 8.3 MiB

21
cmd/ui/index.html.tmpl Normal file
View File

@@ -0,0 +1,21 @@
<!DOCTYPE html>
<html>
<header>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/water.css@2/out/dark.css">
</header>
<body>
<div>
{{ range .Series }}
<div style="display: inline-block; max-width: 12em; max-height: 9em; margin-bottom: .7em;">
<a href="/media/{{ .HREF }}">
<img src="/media/{{ .Thumbnail }}" alt="{{ .Thumbnail }}"/>
<span>{{ .Thumbnail }}</span>
<br>
</a>
</div>
{{ end }}
</div>
</body>
<footer>
</footer>
</html>

123
cmd/ui/main.go Normal file
View File

@@ -0,0 +1,123 @@
package main
import (
"context"
"embed"
_ "embed"
"flag"
"fmt"
"html/template"
"log"
"net"
"net/http"
"os"
"os/signal"
"path"
"slices"
"strings"
"syscall"
"ffmpeg.d/pkg/fs"
)
//go:embed *.tmpl
var TMPL embed.FS
func main() {
ctx, can := signal.NotifyContext(context.Background(), syscall.SIGINT)
defer can()
if err := Run(ctx, os.Args[1:]); err != nil {
panic(err)
}
}
func Run(ctx context.Context, args []string) error {
flags := flag.NewFlagSet(os.Args[0], flag.ContinueOnError)
d := flags.String("d", "./testdata/", "directory containing directories of (x.jpg,x.mp4)")
p := flags.Int("p", 38080, "port to listen on")
if err := flags.Parse(args); err != nil {
return err
}
tmpl, err := template.ParseFS(TMPL, "*.tmpl")
if err != nil {
return err
}
s := &http.Server{
Addr: fmt.Sprintf(":%d", *p),
Handler: http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if strings.HasPrefix(r.URL.Path, "/media/") {
http.StripPrefix("/media/", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
http.FileServer(http.Dir(*d)).ServeHTTP(w, r)
})).ServeHTTP(w, r)
return
}
type Series struct {
HREF string
Thumbnail string
}
ds, err := fs.LsD(*d)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
seriesByKey := map[string]Series{}
for _, d := range ds {
files, err := fs.LsF(d)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
for _, f := range files {
key := strings.Split(path.Base(f), ".")[0]
v := seriesByKey[key]
switch path.Ext(f) {
case ".jpg":
v.Thumbnail = path.Join(path.Base(d), path.Base(f))
case ".mp4":
v.HREF = path.Join(path.Base(d), path.Base(f))
}
seriesByKey[key] = v
}
}
series := []Series{}
for _, v := range seriesByKey {
series = append(series, v)
}
slices.SortFunc(series, func(a, b Series) int {
return -1 * strings.Compare(path.Base(a.Thumbnail), path.Base(b.Thumbnail))
})
if err := tmpl.Execute(w, map[string]any{
"Series": series,
}); err != nil {
log.Println(err)
}
}),
BaseContext: func(net.Listener) context.Context {
return ctx
},
}
defer s.Shutdown(ctx)
errs := make(chan error)
go func() {
defer close(errs)
errs <- s.ListenAndServe()
}()
select {
case err := <-errs:
return err
case <-ctx.Done():
s.Shutdown(ctx)
for range errs {
}
}
return nil
}

1
cmd/ui/testdata/cam/1.jpg vendored Symbolic link
View File

@@ -0,0 +1 @@
LAdN7KVw-Ig.jpg

1
cmd/ui/testdata/cam/1.mp4 vendored Symbolic link
View File

@@ -0,0 +1 @@
LAdN7KVw-Ig.mp4

1
cmd/ui/testdata/cam/2.jpg vendored Symbolic link
View File

@@ -0,0 +1 @@
LAdN7KVw-Ig.jpg

1
cmd/ui/testdata/cam/2.mp4 vendored Symbolic link
View File

@@ -0,0 +1 @@
LAdN7KVw-Ig.mp4

1
cmd/ui/testdata/cam/3.jpg vendored Symbolic link
View File

@@ -0,0 +1 @@
LAdN7KVw-Ig.jpg

1
cmd/ui/testdata/cam/3.mp4 vendored Symbolic link
View File

@@ -0,0 +1 @@
LAdN7KVw-Ig.mp4

1
cmd/ui/testdata/cam/4.jpg vendored Symbolic link
View File

@@ -0,0 +1 @@
LAdN7KVw-Ig.jpg

1
cmd/ui/testdata/cam/4.mp4 vendored Symbolic link
View File

@@ -0,0 +1 @@
LAdN7KVw-Ig.mp4

1
cmd/ui/testdata/cam/5.jpg vendored Symbolic link
View File

@@ -0,0 +1 @@
LAdN7KVw-Ig.jpg

1
cmd/ui/testdata/cam/5.mp4 vendored Symbolic link
View File

@@ -0,0 +1 @@
LAdN7KVw-Ig.mp4

1
cmd/ui/testdata/cam/6.jpg vendored Symbolic link
View File

@@ -0,0 +1 @@
LAdN7KVw-Ig.jpg

1
cmd/ui/testdata/cam/6.mp4 vendored Symbolic link
View File

@@ -0,0 +1 @@
LAdN7KVw-Ig.mp4

BIN
cmd/ui/testdata/cam/LAdN7KVw-Ig.jpg vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

BIN
cmd/ui/testdata/cam/LAdN7KVw-Ig.mp4 vendored Normal file

Binary file not shown.

29
pkg/fs/ls.go Normal file
View File

@@ -0,0 +1,29 @@
package fs
import (
"os"
"path"
"sort"
)
func LsD(d string) ([]string, error) {
return ls(d, true)
}
func LsF(d string) ([]string, error) {
return ls(d, false)
}
func ls(d string, dirs bool) ([]string, error) {
entries, err := os.ReadDir(d)
results := make([]string, 0, len(entries))
for i := range entries {
if dirs == entries[i].IsDir() {
results = append(results, path.Join(d, entries[i].Name()))
}
}
sort.Strings(results)
return results, err
}

View File

@@ -1,3 +0,0 @@
**/*.sw*
/cmd/prune/prune
/cmd/prune/testdata/**

Binary file not shown.