Add backend support for serving lower quality videos (if available)

This commit is contained in:
James Mills 2020-03-28 15:59:16 +10:00
parent ca5969379a
commit d21c06c465
No known key found for this signature in database
GPG key ID: AC4C014F1440EBD6
5 changed files with 60 additions and 7 deletions

View file

@ -178,12 +178,15 @@ func (a *App) indexHandler(w http.ResponseWriter, r *http.Request) {
http.Redirect(w, r, fmt.Sprintf("/v/%s?%s", pl[0].ID, r.URL.RawQuery), 302) http.Redirect(w, r, fmt.Sprintf("/v/%s?%s", pl[0].ID, r.URL.RawQuery), 302)
} else { } else {
sort := strings.ToLower(r.URL.Query().Get("sort")) sort := strings.ToLower(r.URL.Query().Get("sort"))
quality := strings.ToLower(r.URL.Query().Get("quality"))
ctx := &struct { ctx := &struct {
Sort string Sort string
Quality string
Playing *media.Video Playing *media.Video
Playlist media.Playlist Playlist media.Playlist
}{ }{
Sort: sort, Sort: sort,
Quality: quality,
Playing: &media.Video{ID: ""}, Playing: &media.Video{ID: ""},
Playlist: a.Library.Playlist(), Playlist: a.Library.Playlist(),
} }
@ -449,10 +452,16 @@ func (a *App) pageHandler(w http.ResponseWriter, r *http.Request) {
log.Printf("/v/%s", id) log.Printf("/v/%s", id)
playing, ok := a.Library.Videos[id] playing, ok := a.Library.Videos[id]
if !ok { if !ok {
sort := strings.ToLower(r.URL.Query().Get("sort"))
quality := strings.ToLower(r.URL.Query().Get("quality"))
ctx := &struct { ctx := &struct {
Sort string
Quality string
Playing *media.Video Playing *media.Video
Playlist media.Playlist Playlist media.Playlist
}{ }{
Sort: sort,
Quality: quality,
Playing: &media.Video{ID: ""}, Playing: &media.Video{ID: ""},
Playlist: a.Library.Playlist(), Playlist: a.Library.Playlist(),
} }
@ -491,13 +500,23 @@ func (a *App) pageHandler(w http.ResponseWriter, r *http.Request) {
log.Warnf("invalid sort critiera: %s", sort) log.Warnf("invalid sort critiera: %s", sort)
} }
quality := strings.ToLower(r.URL.Query().Get("quality"))
switch quality {
case "", "720p", "480p", "360p", "240p":
default:
log.WithField("quality", quality).Warn("invalid quality")
quality = ""
}
w.Header().Set("Content-Type", "text/html; charset=utf-8") w.Header().Set("Content-Type", "text/html; charset=utf-8")
ctx := &struct { ctx := &struct {
Sort string Sort string
Quality string
Playing *media.Video Playing *media.Video
Playlist media.Playlist Playlist media.Playlist
}{ }{
Sort: sort, Sort: sort,
Quality: quality,
Playing: playing, Playing: playing,
Playlist: playlist, Playlist: playlist,
} }
@ -508,16 +527,43 @@ func (a *App) pageHandler(w http.ResponseWriter, r *http.Request) {
func (a *App) videoHandler(w http.ResponseWriter, r *http.Request) { func (a *App) videoHandler(w http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r) vars := mux.Vars(r)
id := vars["id"] id := vars["id"]
prefix, ok := vars["prefix"] prefix, ok := vars["prefix"]
if ok { if ok {
id = path.Join(prefix, id) id = path.Join(prefix, id)
} }
log.Printf("/v/%s", id) log.Printf("/v/%s", id)
m, ok := a.Library.Videos[id] m, ok := a.Library.Videos[id]
if !ok { if !ok {
return return
} }
var videoPath string
quality := strings.ToLower(r.URL.Query().Get("quality"))
switch quality {
case "720p", "480p", "360p", "240p":
videoPath = fmt.Sprintf(
"%s#%s.mp4",
strings.TrimSuffix(m.Path, filepath.Ext(m.Path)),
quality,
)
if !utils.FileExists(videoPath) {
log.
WithField("quality", quality).
WithField("videoPath", videoPath).
Warn("video with specified quality does not exist (defaulting to default quality)")
videoPath = m.Path
}
case "":
videoPath = m.Path
default:
log.WithField("quality", quality).Warn("invalid quality")
videoPath = m.Path
}
if err := a.Store.Migrate(prefix, id); err != nil { if err := a.Store.Migrate(prefix, id); err != nil {
err := fmt.Errorf("error migrating store data: %w", err) err := fmt.Errorf("error migrating store data: %w", err)
log.Warn(err) log.Warn(err)
@ -532,7 +578,7 @@ func (a *App) videoHandler(w http.ResponseWriter, r *http.Request) {
disposition := "attachment; filename=\"" + title + ".mp4\"" disposition := "attachment; filename=\"" + title + ".mp4\""
w.Header().Set("Content-Disposition", disposition) w.Header().Set("Content-Disposition", disposition)
w.Header().Set("Content-Type", "video/mp4") w.Header().Set("Content-Type", "video/mp4")
http.ServeFile(w, r, m.Path) http.ServeFile(w, r, videoPath)
} }
// HTTP handler for /t/id // HTTP handler for /t/id

View file

@ -23,9 +23,9 @@ func init() {
} }
file4 := &embedded.EmbeddedFile{ file4 := &embedded.EmbeddedFile{
Filename: "index.html", Filename: "index.html",
FileModTime: time.Unix(1585371597, 0), FileModTime: time.Unix(1585374250, 0),
Content: string("{{ define \"content\" }}\r\n{{ $playing := .Playing }}\r\n<div id=\"player\">\r\n {{ if $playing.ID }}\r\n <video id=\"video\" controls preload=\"metadata\" poster=\"/t/{{ $playing.ID}}\" src=\"/v/{{ $playing.ID }}.mp4\" type=\"video/mp4\"></video>\r\n <h1>{{ $playing.Title }}</h1>\r\n <h2>{{ $playing.Views }} views • {{ $playing.Modified }}<br />{{ $playing.Size | bytes }}</h2>\r\n <p>{{ $playing.Description }}</p>\r\n {{ else }}\r\n <video id=\"video\" controls></video>\r\n {{ end }}\r\n</div>\r\n<div id=\"playlist\">\r\n <div class=\"nav\">\r\n <ul>\r\n <li><a {{ if or (eq $.Sort \"timestamp\") (eq $.Sort \"\") }}class=\"active\"{{ end }} href=\"?sort=timestamp\">Recent</a></li>\r\n <li><a {{ if eq $.Sort \"views\" }}class=\"active\"{{ end }} href=\"?sort=views\">Views</a></li>\r\n </ul>\r\n </div>\r\n {{ range $m := .Playlist }}\r\n {{ if eq $m.ID $playing.ID }}\r\n <a href=\"/v/{{ $m.ID }}\" class=\"playing\">\r\n {{ else }}\r\n <a href=\"/v/{{ $m.ID }}\">\r\n {{ end }}\r\n <img src=\"/t/{{ $m.ID }}\">\r\n <div>\r\n <h1>{{ $m.Title }}</h1>\r\n <h2>{{ $m.Views }} views • {{ $m.Modified }}<br />{{ $m.Size | bytes }}</h2>\r\n </div>\r\n </a>\r\n {{ end }}\r\n</div>\r\n{{end}}\r\n"), Content: string("{{ define \"content\" }}\r\n{{ $playing := .Playing }}\r\n<div id=\"player\">\r\n {{ if $playing.ID }}\r\n <video id=\"video\" controls preload=\"metadata\" poster=\"/t/{{ $playing.ID}}\">\r\n <source src=\"/v/{{ $playing.ID }}.mp4?quality={{ $.Quality }}\" type=\"video/mp4\" />\r\n </video>\r\n <h1>{{ $playing.Title }}</h1>\r\n <h2>{{ $playing.Views }} views • {{ $playing.Modified }}<br />{{ $playing.Size | bytes }}</h2>\r\n <p>{{ $playing.Description }}</p>\r\n {{ else }}\r\n <video id=\"video\" controls></video>\r\n {{ end }}\r\n</div>\r\n<div id=\"playlist\">\r\n <div class=\"nav\">\r\n <ul>\r\n <li><a {{ if or (eq $.Sort \"timestamp\") (eq $.Sort \"\") }}class=\"active\"{{ end }} href=\"?sort=timestamp\">Recent</a></li>\r\n <li><a {{ if eq $.Sort \"views\" }}class=\"active\"{{ end }} href=\"?sort=views\">Views</a></li>\r\n </ul>\r\n </div>\r\n {{ range $m := .Playlist }}\r\n {{ if eq $m.ID $playing.ID }}\r\n <a href=\"/v/{{ $m.ID }}\" class=\"playing\">\r\n {{ else }}\r\n <a href=\"/v/{{ $m.ID }}\">\r\n {{ end }}\r\n <img src=\"/t/{{ $m.ID }}\">\r\n <div>\r\n <h1>{{ $m.Title }}</h1>\r\n <h2>{{ $m.Views }} views • {{ $m.Modified }}<br />{{ $m.Size | bytes }}</h2>\r\n </div>\r\n </a>\r\n {{ end }}\r\n</div>\r\n{{end}}\r\n"),
} }
file5 := &embedded.EmbeddedFile{ file5 := &embedded.EmbeddedFile{
Filename: "upload.html", Filename: "upload.html",
@ -37,7 +37,7 @@ func init() {
// define dirs // define dirs
dir1 := &embedded.EmbeddedDir{ dir1 := &embedded.EmbeddedDir{
Filename: "", Filename: "",
DirModTime: time.Unix(1585371597, 0), DirModTime: time.Unix(1585374250, 0),
ChildFiles: []*embedded.EmbeddedFile{ ChildFiles: []*embedded.EmbeddedFile{
file2, // "base.html" file2, // "base.html"
file3, // "import.html" file3, // "import.html"
@ -53,7 +53,7 @@ func init() {
// register embeddedBox // register embeddedBox
embedded.RegisterEmbeddedBox(`../templates`, &embedded.EmbeddedBox{ embedded.RegisterEmbeddedBox(`../templates`, &embedded.EmbeddedBox{
Name: `../templates`, Name: `../templates`,
Time: time.Unix(1585371597, 0), Time: time.Unix(1585374250, 0),
Dirs: map[string]*embedded.EmbeddedDir{ Dirs: map[string]*embedded.EmbeddedDir{
"": dir1, "": dir1,
}, },

View file

@ -2,6 +2,7 @@ package app
import ( import (
"path/filepath" "path/filepath"
"strings"
"time" "time"
fs "github.com/fsnotify/fsnotify" fs "github.com/fsnotify/fsnotify"
@ -29,7 +30,7 @@ func startWatcher(a *App) {
for { for {
select { select {
case e := <-a.Watcher.Events: case e := <-a.Watcher.Events:
if filepath.Ext(e.Name) != ".mp4" { if strings.ContainsAny(e.Name, "#") || filepath.Ext(e.Name) != ".mp4" {
continue continue
} }
log.Debugf("fsnotify event: %s", e) log.Debugf("fsnotify event: %s", e)

View file

@ -50,6 +50,10 @@ func (lib *Library) Import(p *Path) error {
return err return err
} }
for _, info := range files { for _, info := range files {
if strings.ContainsAny(info.Name(), "#") {
// ignore resized videos e.g: #240p.mp4
continue
}
err = lib.Add(path.Join(p.Path, info.Name())) err = lib.Add(path.Join(p.Path, info.Name()))
if err != nil { if err != nil {
// Ignore files that can't be parsed // Ignore files that can't be parsed

View file

@ -2,7 +2,9 @@
{{ $playing := .Playing }} {{ $playing := .Playing }}
<div id="player"> <div id="player">
{{ if $playing.ID }} {{ if $playing.ID }}
<video id="video" controls preload="metadata" poster="/t/{{ $playing.ID}}" src="/v/{{ $playing.ID }}.mp4" type="video/mp4"></video> <video id="video" controls preload="metadata" poster="/t/{{ $playing.ID}}">
<source src="/v/{{ $playing.ID }}.mp4?quality={{ $.Quality }}" type="video/mp4" />
</video>
<h1>{{ $playing.Title }}</h1> <h1>{{ $playing.Title }}</h1>
<h2>{{ $playing.Views }} views • {{ $playing.Modified }}<br />{{ $playing.Size | bytes }}</h2> <h2>{{ $playing.Views }} views • {{ $playing.Modified }}<br />{{ $playing.Size | bytes }}</h2>
<p>{{ $playing.Description }}</p> <p>{{ $playing.Description }}</p>