docs
This commit is contained in:
parent
0979457d75
commit
a7a5eee66f
6 changed files with 36 additions and 0 deletions
|
@ -17,6 +17,7 @@ import (
|
||||||
"github.com/wybiral/tube/pkg/media"
|
"github.com/wybiral/tube/pkg/media"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// App represents main application.
|
||||||
type App struct {
|
type App struct {
|
||||||
Config *Config
|
Config *Config
|
||||||
Library *media.Library
|
Library *media.Library
|
||||||
|
@ -26,6 +27,7 @@ type App struct {
|
||||||
Router *mux.Router
|
Router *mux.Router
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// NewApp returns a new instance of App from Config.
|
||||||
func NewApp(cfg *Config) (*App, error) {
|
func NewApp(cfg *Config) (*App, error) {
|
||||||
if cfg == nil {
|
if cfg == nil {
|
||||||
cfg = DefaultConfig()
|
cfg = DefaultConfig()
|
||||||
|
@ -33,24 +35,30 @@ func NewApp(cfg *Config) (*App, error) {
|
||||||
a := &App{
|
a := &App{
|
||||||
Config: cfg,
|
Config: cfg,
|
||||||
}
|
}
|
||||||
|
// Setup Library
|
||||||
a.Library = media.NewLibrary()
|
a.Library = media.NewLibrary()
|
||||||
|
// Setup Watcher
|
||||||
w, err := fsnotify.NewWatcher()
|
w, err := fsnotify.NewWatcher()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
a.Watcher = w
|
a.Watcher = w
|
||||||
|
// Setup Listener
|
||||||
ln, err := newListener(cfg.Server)
|
ln, err := newListener(cfg.Server)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
a.Listener = ln
|
a.Listener = ln
|
||||||
|
// Setup Templates
|
||||||
a.Templates = template.Must(template.ParseGlob("templates/*"))
|
a.Templates = template.Must(template.ParseGlob("templates/*"))
|
||||||
|
// Setup Router
|
||||||
r := mux.NewRouter().StrictSlash(true)
|
r := mux.NewRouter().StrictSlash(true)
|
||||||
r.HandleFunc("/", a.indexHandler).Methods("GET")
|
r.HandleFunc("/", a.indexHandler).Methods("GET")
|
||||||
r.HandleFunc("/v/{id}.mp4", a.videoHandler).Methods("GET")
|
r.HandleFunc("/v/{id}.mp4", a.videoHandler).Methods("GET")
|
||||||
r.HandleFunc("/t/{id}", a.thumbHandler).Methods("GET")
|
r.HandleFunc("/t/{id}", a.thumbHandler).Methods("GET")
|
||||||
r.HandleFunc("/v/{id}", a.pageHandler).Methods("GET")
|
r.HandleFunc("/v/{id}", a.pageHandler).Methods("GET")
|
||||||
r.HandleFunc("/feed.xml", a.rssHandler).Methods("GET")
|
r.HandleFunc("/feed.xml", a.rssHandler).Methods("GET")
|
||||||
|
// Static file handler
|
||||||
fsHandler := http.StripPrefix(
|
fsHandler := http.StripPrefix(
|
||||||
"/static/",
|
"/static/",
|
||||||
http.FileServer(http.Dir("./static/")),
|
http.FileServer(http.Dir("./static/")),
|
||||||
|
@ -60,6 +68,7 @@ func NewApp(cfg *Config) (*App, error) {
|
||||||
return a, nil
|
return a, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Run imports the library and starts server.
|
||||||
func (a *App) Run() error {
|
func (a *App) Run() error {
|
||||||
path := a.Config.LibraryPath
|
path := a.Config.LibraryPath
|
||||||
err := a.Library.Import(path)
|
err := a.Library.Import(path)
|
||||||
|
@ -71,6 +80,7 @@ func (a *App) Run() error {
|
||||||
return http.Serve(a.Listener, a.Router)
|
return http.Serve(a.Listener, a.Router)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Watch the library path and update Library with changes.
|
||||||
func (a *App) watch() {
|
func (a *App) watch() {
|
||||||
for {
|
for {
|
||||||
e, ok := <-a.Watcher.Events
|
e, ok := <-a.Watcher.Events
|
||||||
|
@ -92,6 +102,7 @@ func (a *App) watch() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// HTTP handler for /
|
||||||
func (a *App) indexHandler(w http.ResponseWriter, r *http.Request) {
|
func (a *App) indexHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
log.Printf("/")
|
log.Printf("/")
|
||||||
pl := a.Library.Playlist()
|
pl := a.Library.Playlist()
|
||||||
|
@ -108,6 +119,7 @@ func (a *App) indexHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// HTTP handler for /v/id
|
||||||
func (a *App) pageHandler(w http.ResponseWriter, r *http.Request) {
|
func (a *App) pageHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
vars := mux.Vars(r)
|
vars := mux.Vars(r)
|
||||||
id := vars["id"]
|
id := vars["id"]
|
||||||
|
@ -133,6 +145,7 @@ func (a *App) pageHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// HTTP handler for /v/id.mp4
|
||||||
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"]
|
||||||
|
@ -149,6 +162,7 @@ func (a *App) videoHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
http.ServeFile(w, r, path)
|
http.ServeFile(w, r, path)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// HTTP handler for /t/id
|
||||||
func (a *App) thumbHandler(w http.ResponseWriter, r *http.Request) {
|
func (a *App) thumbHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
vars := mux.Vars(r)
|
vars := mux.Vars(r)
|
||||||
id := vars["id"]
|
id := vars["id"]
|
||||||
|
@ -167,6 +181,7 @@ func (a *App) thumbHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// HTTP handler for /feed.xml
|
||||||
func (a *App) rssHandler(w http.ResponseWriter, r *http.Request) {
|
func (a *App) rssHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
cfg := a.Config.Feed
|
cfg := a.Config.Feed
|
||||||
now := time.Now()
|
now := time.Now()
|
||||||
|
|
|
@ -5,17 +5,20 @@ import (
|
||||||
"os"
|
"os"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Config settings for main App.
|
||||||
type Config struct {
|
type Config struct {
|
||||||
LibraryPath string `json:"library"`
|
LibraryPath string `json:"library"`
|
||||||
Server *ServerConfig `json:"server"`
|
Server *ServerConfig `json:"server"`
|
||||||
Feed *FeedConfig `json:"feed"`
|
Feed *FeedConfig `json:"feed"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ServerConfig settings for App Server.
|
||||||
type ServerConfig struct {
|
type ServerConfig struct {
|
||||||
Host string `json:"host"`
|
Host string `json:"host"`
|
||||||
Port int `json:"port"`
|
Port int `json:"port"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// FeedConfig settings for App Feed.
|
||||||
type FeedConfig struct {
|
type FeedConfig struct {
|
||||||
ExternalURL string `json:"external_url"`
|
ExternalURL string `json:"external_url"`
|
||||||
Title string `json:"title"`
|
Title string `json:"title"`
|
||||||
|
@ -28,6 +31,7 @@ type FeedConfig struct {
|
||||||
Copyright string `json:"copyright"`
|
Copyright string `json:"copyright"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// DefaultConfig returns Config initialized with default values.
|
||||||
func DefaultConfig() *Config {
|
func DefaultConfig() *Config {
|
||||||
return &Config{
|
return &Config{
|
||||||
LibraryPath: "videos",
|
LibraryPath: "videos",
|
||||||
|
@ -41,6 +45,7 @@ func DefaultConfig() *Config {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ReadFile reads a JSON file into Config.
|
||||||
func (c *Config) ReadFile(path string) error {
|
func (c *Config) ReadFile(path string) error {
|
||||||
f, err := os.Open(path)
|
f, err := os.Open(path)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
@ -1,3 +1,7 @@
|
||||||
|
// Instead of using the default new.Listener this file will construct a custom
|
||||||
|
// one. The main purpose for this is to have more control over the settings
|
||||||
|
// (like keep-alive) and to retrieve the assigned port when using port 0.
|
||||||
|
|
||||||
package app
|
package app
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
|
|
@ -9,11 +9,13 @@ import (
|
||||||
"sync"
|
"sync"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Library manages importing and retrieving video data.
|
||||||
type Library struct {
|
type Library struct {
|
||||||
mu sync.RWMutex
|
mu sync.RWMutex
|
||||||
Videos map[string]*Video
|
Videos map[string]*Video
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// NewLibrary returns new instance of Library.
|
||||||
func NewLibrary() *Library {
|
func NewLibrary() *Library {
|
||||||
lib := &Library{
|
lib := &Library{
|
||||||
Videos: make(map[string]*Video),
|
Videos: make(map[string]*Video),
|
||||||
|
@ -21,6 +23,7 @@ func NewLibrary() *Library {
|
||||||
return lib
|
return lib
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Import adds all valid videos from a given path.
|
||||||
func (lib *Library) Import(path string) error {
|
func (lib *Library) Import(path string) error {
|
||||||
files, err := ioutil.ReadDir(path)
|
files, err := ioutil.ReadDir(path)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -36,6 +39,7 @@ func (lib *Library) Import(path string) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Add adds a single video from a given file path.
|
||||||
func (lib *Library) Add(path string) error {
|
func (lib *Library) Add(path string) error {
|
||||||
v, err := ParseVideo(path)
|
v, err := ParseVideo(path)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -48,6 +52,7 @@ func (lib *Library) Add(path string) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Remove removes a single video from a given file path.
|
||||||
func (lib *Library) Remove(path string) {
|
func (lib *Library) Remove(path string) {
|
||||||
name := filepath.Base(path)
|
name := filepath.Base(path)
|
||||||
// ID is name without extension
|
// ID is name without extension
|
||||||
|
@ -65,6 +70,7 @@ func (lib *Library) Remove(path string) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Playlist returns a sorted Playlist of all videos.
|
||||||
func (lib *Library) Playlist() Playlist {
|
func (lib *Library) Playlist() Playlist {
|
||||||
lib.mu.RLock()
|
lib.mu.RLock()
|
||||||
defer lib.mu.RUnlock()
|
defer lib.mu.RUnlock()
|
||||||
|
|
|
@ -1,15 +1,19 @@
|
||||||
package media
|
package media
|
||||||
|
|
||||||
|
// Playlist holds an array of videos capable of sorting by Timestamp.
|
||||||
type Playlist []*Video
|
type Playlist []*Video
|
||||||
|
|
||||||
|
// Len returns length of array (for sorting).
|
||||||
func (p Playlist) Len() int {
|
func (p Playlist) Len() int {
|
||||||
return len(p)
|
return len(p)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Swap swaps two values in array by index (for sorting).
|
||||||
func (p Playlist) Swap(i, j int) {
|
func (p Playlist) Swap(i, j int) {
|
||||||
p[i], p[j] = p[j], p[i]
|
p[i], p[j] = p[j], p[i]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Less returns true if p[i] Timestamp is after p[j] (for sorting).
|
||||||
func (p Playlist) Less(i, j int) bool {
|
func (p Playlist) Less(i, j int) bool {
|
||||||
return p[i].Timestamp.After(p[j].Timestamp)
|
return p[i].Timestamp.After(p[j].Timestamp)
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,6 +8,7 @@ import (
|
||||||
"github.com/dhowden/tag"
|
"github.com/dhowden/tag"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Video represents metadata for a single video.
|
||||||
type Video struct {
|
type Video struct {
|
||||||
ID string
|
ID string
|
||||||
Title string
|
Title string
|
||||||
|
@ -20,6 +21,7 @@ type Video struct {
|
||||||
Timestamp time.Time
|
Timestamp time.Time
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ParseVideo parses a video file's metadata and returns a Video.
|
||||||
func ParseVideo(path string) (*Video, error) {
|
func ParseVideo(path string) (*Video, error) {
|
||||||
f, err := os.Open(path)
|
f, err := os.Open(path)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
Loading…
Reference in a new issue