D artist.go => artist.go +0 -61
@@ 1,61 0,0 @@
-package main
-
-import (
- "fmt"
- "net/http"
- "os"
- "path/filepath"
- "strings"
-)
-
-func (app *application) artist(w http.ResponseWriter, r *http.Request) {
- path := filepath.Join(app.dir, filepath.Clean(r.URL.Path))
- info, err := os.Stat(path)
- if err != nil {
- app.errLog.Println(err)
- http.NotFound(w, r)
- return
- }
- fmt.Println("STATED", path)
- if !info.IsDir() {
- f, err := os.Open(path)
- if err != nil {
- app.errLog.Println(err)
- http.NotFound(w, r)
- return
- }
- http.ServeContent(w, r, path, info.ModTime(), f)
- return
- }
-
- artist := strings.TrimPrefix(path, filepath.Clean(app.dir)+"/")
- entries, err := ListDir(path)
- if err != nil {
- app.errLog.Println(err)
- http.NotFound(w, r)
- return
- }
-
- tsName := "artist.tmpl"
- ts, ok := app.templateCache[tsName]
- if !ok {
- app.errLog.Println(fmt.Errorf(
- "the template %s is missing",
- tsName,
- ))
- http.NotFound(w, r)
- return
- }
- err = ts.ExecuteTemplate(w, "base", ArtistPage{
- Artist: artist,
- Entries: entries,
- })
- if err != nil {
- app.errLog.Println(err)
- http.Error(
- w,
- http.StatusText(http.StatusInternalServerError),
- http.StatusInternalServerError,
- )
- }
-}
M helpers.go => helpers.go +5 -1
@@ 5,6 5,7 @@ import (
"io"
"os"
"path/filepath"
+ "strings"
"time"
)
@@ 15,7 16,7 @@ type DirEntry struct {
Time string
}
-func ListDir(path string) ([]DirEntry, error) {
+func ListVideos(path string) ([]DirEntry, error) {
dirEntries, err := os.ReadDir(path)
if err != nil {
return nil, err
@@ 24,6 25,9 @@ func ListDir(path string) ([]DirEntry, error) {
var entries []DirEntry
for _, e := range dirEntries {
name := e.Name()
+ if !strings.HasSuffix(name, ".mp4") {
+ continue
+ }
if len(name) > 28 {
name = name[:25] + "..."
}
D home.go => home.go +0 -40
@@ 1,40 0,0 @@
-package main
-
-import (
- "fmt"
- "net/http"
- "path/filepath"
-)
-
-func (app *application) home(w http.ResponseWriter, r *http.Request) {
- released, err := Released(filepath.Join(app.dir, "released.txt"))
- if err != nil {
- app.errLog.Println(err)
- http.Error(
- w,
- http.StatusText(http.StatusInternalServerError),
- http.StatusInternalServerError,
- )
- return
- }
-
- tsName := "home.tmpl"
- ts, ok := app.templateCache[tsName]
- if !ok {
- app.errLog.Println(fmt.Errorf(
- "the template %s is missing",
- tsName,
- ))
- http.NotFound(w, r)
- return
- }
- err = ts.ExecuteTemplate(w, "base", released)
- if err != nil {
- app.errLog.Println(err)
- http.Error(
- w,
- http.StatusText(http.StatusInternalServerError),
- http.StatusInternalServerError,
- )
- }
-}
M main.go => main.go +0 -5
@@ 16,11 16,6 @@ type application struct {
errLog *log.Logger
}
-type ArtistPage struct {
- Artist string
- Entries []DirEntry
-}
-
func main() {
addr := flag.String("addr", ":4000", "HTTP network address")
content := flag.String("path", "/var/www", "Path to serve")
M routes.go => routes.go +167 -2
@@ 1,7 1,11 @@
package main
import (
+ "fmt"
"net/http"
+ "os"
+ "path/filepath"
+ "strings"
"github.com/julienschmidt/httprouter"
)
@@ 9,8 13,169 @@ import (
func (app *application) routes() http.Handler {
router := httprouter.New()
router.HandlerFunc(http.MethodGet, "/", app.home)
- router.HandlerFunc(http.MethodGet, "/:artist", app.artist)
- router.HandlerFunc(http.MethodGet, "/:artist/:file", app.artist)
+ router.HandlerFunc(http.MethodGet, "/:path", app.path)
+ router.HandlerFunc(http.MethodGet, "/:path/:subpath", app.path)
return app.logRequest(router)
}
+
+// home is an http.HandlerFunc which displays the home page.
+// The home page is a list of all "released" videos, which are loaded on each
+// request from "released.txt".
+func (app *application) home(w http.ResponseWriter, r *http.Request) {
+ released, err := Released(filepath.Join(app.dir, "released.txt"))
+ if err != nil {
+ app.errLog.Println(err)
+ http.Error(
+ w,
+ http.StatusText(http.StatusInternalServerError),
+ http.StatusInternalServerError,
+ )
+ return
+ }
+
+ tsName := "home.tmpl"
+ ts, ok := app.templateCache[tsName]
+ if !ok {
+ app.errLog.Println(fmt.Errorf(
+ "the template %s is missing",
+ tsName,
+ ))
+ http.NotFound(w, r)
+ return
+ }
+ err = ts.ExecuteTemplate(w, "base", released)
+ if err != nil {
+ app.errLog.Println(err)
+ http.Error(
+ w,
+ http.StatusText(http.StatusInternalServerError),
+ http.StatusInternalServerError,
+ )
+ }
+}
+
+// path is an http.HandlerFunc which passes the request to either artist,
+// video, or file depending on if the request is for a file, video file, or
+// directory.
+func (app *application) path(w http.ResponseWriter, r *http.Request) {
+ path := filepath.Join(app.dir, filepath.Clean(r.URL.Path))
+ info, err := os.Stat(path)
+ if err != nil {
+ app.errLog.Println(err)
+ http.NotFound(w, r)
+ return
+ }
+ if !info.IsDir() {
+ q := r.URL.Query()
+ _, direct := q["direct"]
+ if strings.HasSuffix(path, ".mp4") && !direct {
+ app.video(w, r)
+ return
+ }
+ app.file(w, r)
+ return
+ }
+
+ app.artist(w, r)
+}
+
+// ArtistPage is the datastructure used in the artist handler for the artist
+// template.
+type ArtistPage struct {
+ Artist string
+ Entries []DirEntry
+}
+
+// artist is an http.HandlerFunc which displays a page for the requested artist.
+// The artist's page is a listing of all their videos, including unreleased
+// videos.
+func (app *application) artist(w http.ResponseWriter, r *http.Request) {
+ path := filepath.Join(app.dir, filepath.Clean(r.URL.Path))
+ artist := strings.TrimPrefix(path, filepath.Clean(app.dir)+"/")
+ entries, err := ListVideos(path)
+ if err != nil {
+ app.errLog.Println(err)
+ http.NotFound(w, r)
+ return
+ }
+
+ tsName := "artist.tmpl"
+ ts, ok := app.templateCache[tsName]
+ if !ok {
+ app.errLog.Println(fmt.Errorf(
+ "the template %s is missing",
+ tsName,
+ ))
+ http.NotFound(w, r)
+ return
+ }
+ err = ts.ExecuteTemplate(w, "base", ArtistPage{
+ Artist: artist,
+ Entries: entries,
+ })
+ if err != nil {
+ app.errLog.Println(err)
+ http.Error(
+ w,
+ http.StatusText(http.StatusInternalServerError),
+ http.StatusInternalServerError,
+ )
+ }
+}
+
+// file is an http.HandlerFunc for files.
+func (app *application) file(w http.ResponseWriter, r *http.Request) {
+ path := filepath.Join(app.dir, filepath.Clean(r.URL.Path))
+ info, err := os.Stat(path)
+ if err != nil {
+ app.errLog.Println(err)
+ http.NotFound(w, r)
+ return
+ }
+ f, err := os.Open(path)
+ if err != nil {
+ app.errLog.Println(err)
+ http.NotFound(w, r)
+ return
+ }
+ http.ServeContent(w, r, path, info.ModTime(), f)
+ return
+}
+
+// VideoPage is the datastructure used in the video handler for the video
+// template.
+type VideoPage struct {
+ Artist string
+ Video string
+}
+
+// video is an http.HandlerFunc for videos.
+// The video is displayed in browser with a helpful player and download link.
+func (app *application) video(w http.ResponseWriter, r *http.Request) {
+ tsName := "video.tmpl"
+ ts, ok := app.templateCache[tsName]
+ if !ok {
+ app.errLog.Println(fmt.Errorf(
+ "the template %s is missing",
+ tsName,
+ ))
+ http.NotFound(w, r)
+ return
+ }
+ err := ts.ExecuteTemplate(w, "base", VideoPage{
+ Artist: strings.TrimPrefix(
+ filepath.Dir(filepath.Clean(r.URL.Path)),
+ "/",
+ ),
+ Video: filepath.Clean(r.URL.Path),
+ })
+ if err != nil {
+ app.errLog.Println(err)
+ http.Error(
+ w,
+ http.StatusText(http.StatusInternalServerError),
+ http.StatusInternalServerError,
+ )
+ }
+}
M templates.go => templates.go +3 -3
@@ 7,13 7,13 @@ import (
"text/template"
)
-//go:embed "html"
+//go:embed "ui"
var EmbededFiles embed.FS
func newTemplateCache() (map[string]*template.Template, error) {
cache := map[string]*template.Template{}
- pages, err := fs.Glob(EmbededFiles, "html/pages/*.tmpl")
+ pages, err := fs.Glob(EmbededFiles, "ui/pages/*.tmpl")
if err != nil {
return nil, err
}
@@ 21,7 21,7 @@ func newTemplateCache() (map[string]*template.Template, error) {
for _, page := range pages {
name := filepath.Base(page)
files := []string{
- "html/base.tmpl",
+ "ui/base.tmpl",
page,
}
R html/base.tmpl => ui/base.tmpl +0 -3
@@ 6,9 6,6 @@
<title>evids</title>
<link rel='stylesheet' href='/main.css'>
<link rel='shortcut icon' href='/favicon.png'>
- </head>
- <body>
{{template "main" .}}
- </body>
</html>
{{end}}
R html/pages/artist.tmpl => ui/pages/artist.tmpl +3 -0
@@ 1,4 1,6 @@
{{define "main"}}
+</head>
+<body>
<header>
<span><a href="/">
<img
@@ 35,4 37,5 @@
</a>
{{end}}
</main>
+</body>
{{end}}
R html/pages/home.tmpl => ui/pages/home.tmpl +3 -0
@@ 1,4 1,6 @@
{{define "main"}}
+</head>
+<body>
<header>
<span><a href="/">
<img
@@ 24,4 26,5 @@
</a>
{{end}}
</main>
+</body>
{{end}}
A ui/pages/video.tmpl => ui/pages/video.tmpl +37 -0
@@ 0,0 1,37 @@
+{{define "main"}}
+</head>
+<header>
+ <span><a href="/">
+ <img
+ id="evids"
+ src="/logo.png"
+ alt="EVIDS"
+ onmouseover="evids.src='/bold-logo.png'"
+ onmouseout="evids.src='/logo.png'"
+ ></img>
+ </a></span>
+ {{ if .Artist }}
+ <span class="artist">
+ <img
+ class="artist"
+ onclick="history.back()"
+ src="/{{.Artist}}/{{.Artist}}.png"
+ alt="{{.Artist}}">
+ </span>
+ {{ end }}
+</header>
+<main>
+ <video
+ controls
+ width="100%"
+ height="auto"
+ preload="auto"
+ >
+ <source src="{{.Video}}?direct" type="video/mp4" />
+ </video>
+ <span class="download"><a download href="{{.Video}}?direct">
+ DOWNLOAD
+ </span></a>
+</main>
+</body>
+{{end}}