102 lines
3.7 KiB
Go
102 lines
3.7 KiB
Go
package web
|
|
|
|
import (
|
|
"context"
|
|
"embed"
|
|
"html/template"
|
|
"io/fs"
|
|
"log"
|
|
"net/http"
|
|
"sync"
|
|
"time"
|
|
|
|
"gitea.kazu634.com/kazu634/tver/internal/store"
|
|
)
|
|
|
|
//go:embed templates/*.html static/*
|
|
var assets embed.FS
|
|
|
|
type page struct {
|
|
Title string
|
|
Newer *newerView
|
|
Queue *queueView
|
|
Subscriptions *subscriptionsView
|
|
Library *libraryView
|
|
Video *videoView
|
|
}
|
|
|
|
type JobStore interface {
|
|
Enqueue(context.Context, store.Episode, string) (store.Job, bool, error)
|
|
EpisodeStatuses(context.Context, []string) (map[string]string, error)
|
|
ListJobs(context.Context, string, int) ([]store.Job, error)
|
|
GetJob(context.Context, int64) (store.Job, error)
|
|
Retry(context.Context, int64) error
|
|
RetryEpisode(context.Context, string) error
|
|
Cancel(context.Context, int64) error
|
|
SubscribedSeries(context.Context, []string) (map[string]bool, error)
|
|
RegisterSubscription(context.Context, string, string, []store.Episode, bool) (bool, error)
|
|
ListSubscriptions(context.Context) ([]store.Subscription, error)
|
|
SetSubscriptionEnabled(context.Context, string, bool) error
|
|
DeleteSubscription(context.Context, string) (bool, error)
|
|
ListSucceededJobs(context.Context, int) ([]store.Job, error)
|
|
GetSucceededJob(context.Context, int64) (store.Job, error)
|
|
DeleteSucceededJob(context.Context, int64) error
|
|
}
|
|
|
|
type server struct {
|
|
fetcher NewerFetcher
|
|
jobs JobStore
|
|
videoDir string
|
|
now func() time.Time
|
|
logger *log.Logger
|
|
zone *time.Location
|
|
tmpl *template.Template
|
|
cacheMu sync.Mutex
|
|
cache map[string]cacheEntry
|
|
}
|
|
|
|
func NewHandler(fetcher NewerFetcher, jobs JobStore, videoDir string, now func() time.Time, logger *log.Logger) http.Handler {
|
|
tmpl := template.Must(template.ParseFS(assets, "templates/*.html"))
|
|
staticFiles, err := fs.Sub(assets, "static")
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
zone, err := time.LoadLocation("Asia/Tokyo")
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
if videoDir == "" {
|
|
videoDir = "/videos"
|
|
}
|
|
s := &server{fetcher: fetcher, jobs: jobs, videoDir: videoDir, now: now, logger: logger, zone: zone, tmpl: tmpl, cache: make(map[string]cacheEntry)}
|
|
mux := http.NewServeMux()
|
|
mux.Handle("GET /static/", http.StripPrefix("/static/", http.FileServerFS(staticFiles)))
|
|
mux.HandleFunc("GET /healthz", func(w http.ResponseWriter, _ *http.Request) {
|
|
w.Header().Set("Content-Type", "text/plain; charset=utf-8")
|
|
_, _ = w.Write([]byte("ok\n"))
|
|
})
|
|
mux.HandleFunc("GET /{$}", s.home)
|
|
mux.HandleFunc("GET /newer", s.newerFragment)
|
|
mux.HandleFunc("POST /episodes/enqueue", s.enqueue)
|
|
mux.HandleFunc("POST /episodes/retry", s.retryEpisode)
|
|
mux.HandleFunc("GET /queue", s.queuePage)
|
|
mux.HandleFunc("GET /queue/list", s.queueFragment)
|
|
mux.HandleFunc("POST /jobs/{id}/retry", s.retryJob)
|
|
mux.HandleFunc("POST /jobs/{id}/cancel", s.cancelJob)
|
|
mux.HandleFunc("GET /subscriptions", s.subscriptionsPage)
|
|
mux.HandleFunc("GET /subscriptions/list", s.subscriptionsFragment)
|
|
mux.HandleFunc("POST /subscriptions/from-url/confirm", s.confirmSubscriptionURL)
|
|
mux.HandleFunc("POST /subscriptions/from-url/register", s.registerSubscriptionURL)
|
|
mux.HandleFunc("GET /subscriptions/from-url/cancel", s.cancelSubscriptionURL)
|
|
mux.HandleFunc("GET /subscriptions/choice", s.subscriptionChoice)
|
|
mux.HandleFunc("POST /subscriptions/register", s.registerSubscription)
|
|
mux.HandleFunc("POST /subscriptions/{id}/pause", s.pauseSubscription)
|
|
mux.HandleFunc("POST /subscriptions/{id}/resume", s.resumeSubscription)
|
|
mux.HandleFunc("POST /subscriptions/{id}/delete", s.deleteSubscription)
|
|
mux.HandleFunc("GET /library", s.libraryPage)
|
|
mux.HandleFunc("GET /library/{id}", s.videoPage)
|
|
mux.HandleFunc("GET /videos/{id}", s.serveVideo)
|
|
mux.HandleFunc("POST /library/{id}/delete", s.deleteVideo)
|
|
return mux
|
|
}
|