Simplify structure by moving routes into command package

This commit is contained in:
Evan Fiordeliso 2025-11-10 15:12:24 -05:00
parent f3f852fefd
commit 2630af4921
7 changed files with 78 additions and 149 deletions

View File

@ -1,4 +1,4 @@
package routes
package main
import (
"fmt"
@ -8,43 +8,11 @@ import (
"net/url"
"strconv"
"github.com/a-h/templ"
"github.com/go-chi/chi/v5"
"go.fifitido.net/ytdl-web/pkg/models"
"go.fifitido.net/ytdl-web/pkg/views"
"go.fifitido.net/ytdl-web/pkg/ytdl"
"go.fifitido.net/ytdl-web/pkg/ytdl/metadata"
)
func Router() chi.Router {
r := chi.NewRouter()
r.Get("/", home)
r.Route("/download", func(r chi.Router) {
r.Get("/", download)
r.Head("/proxy", proxyDownload)
r.Get("/proxy", proxyDownload)
})
return r
}
func renderPage(w http.ResponseWriter, r *http.Request, component templ.Component) {
isHtmx := r.Header.Get("HX-Request") == "true"
if isHtmx {
if err := templ.RenderFragments(r.Context(), w, component, "main-content"); err != nil {
slog.ErrorContext(r.Context(), "failed to render page", slog.Any("error", err))
http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
}
return
}
component.Render(r.Context(), w)
}
func home(w http.ResponseWriter, r *http.Request) {
renderPage(w, r, views.Home(nil))
}
func getUrlParam(r *http.Request) (string, bool) {
urlRaw := r.URL.Query().Get("url")
if urlRaw == "" {
@ -64,17 +32,17 @@ func download(w http.ResponseWriter, r *http.Request) {
videoUrl, ok := getUrlParam(r)
if !ok {
renderPage(w, r, views.Home(&views.Error{Message: "Invalid URL"}))
views.Render(w, r, views.Home(&views.Error{Message: "Invalid URL"}))
return
}
meta, err := ytdl.GetMetadata(videoUrl)
if err != nil {
renderPage(w, r, views.Home(&views.Error{Message: "Could not find a video at that url", RetryUrl: &videoUrl}))
views.Render(w, r, views.Home(&views.Error{Message: "Could not find a video at that url", RetryUrl: &videoUrl}))
return
}
renderPage(w, r, views.Downloads(&views.DownloadsViewModel{Url: videoUrl, Meta: meta}))
views.Render(w, r, views.Downloads(&views.DownloadsViewModel{Url: videoUrl, Meta: meta}))
}
func proxyDownload(w http.ResponseWriter, r *http.Request) {
@ -97,7 +65,7 @@ func proxyDownload(w http.ResponseWriter, r *http.Request) {
http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
return
}
videos := models.GetVideosFromMetadata(meta)
videos := views.GetVideosFromMetadata(meta)
index, err := strconv.Atoi(r.URL.Query().Get("index"))
if err != nil || index < 0 || index >= len(videos) {

35
cmd/ytdl-web/logger.go Normal file
View File

@ -0,0 +1,35 @@
package main
import (
"fmt"
"log/slog"
"github.com/dgraph-io/badger/v2"
)
type badgerLogger struct {
logger *slog.Logger
}
var _ badger.Logger = (*badgerLogger)(nil)
// Debugf implements badger.Logger
func (l *badgerLogger) Debugf(f string, a ...any) {
l.logger.Debug(fmt.Sprintf(f, a...))
}
// Errorf implements badger.Logger
func (l *badgerLogger) Errorf(f string, a ...any) {
l.logger.Error(fmt.Sprintf(f, a...))
}
// Infof implements badger.Logger
func (l *badgerLogger) Infof(f string, a ...any) {
l.logger.Info(fmt.Sprintf(f, a...))
}
// Warningf implements badger.Logger
func (l *badgerLogger) Warningf(f string, a ...any) {
l.logger.Warn(fmt.Sprintf(f, a...))
}

View File

@ -15,9 +15,8 @@ import (
"github.com/go-chi/chi/v5/middleware"
slogchi "github.com/samber/slog-chi"
"go.fifitido.net/ytdl-web/pkg/config"
"go.fifitido.net/ytdl-web/pkg/routes"
"go.fifitido.net/ytdl-web/pkg/serverctx"
"go.fifitido.net/ytdl-web/pkg/utils"
"go.fifitido.net/ytdl-web/pkg/views"
"go.fifitido.net/ytdl-web/pkg/ytdl"
"go.fifitido.net/ytdl-web/pkg/ytdl/cache"
)
@ -57,7 +56,7 @@ func main() {
db, err := badger.Open(
badger.
DefaultOptions(cacheDir()).
WithLogger(utils.NewBadgerLogger(logger.With("module", "badger"))),
WithLogger(&badgerLogger{logger: logger.With("module", "badger")}),
)
if err != nil {
os.Exit(1)
@ -87,7 +86,14 @@ func main() {
middleware.Recoverer,
)
r.Mount(basePath(), routes.Router())
r.Route(basePath(), func(r chi.Router) {
r.Get("/", views.Handler(views.Home(nil)))
r.Route("/download", func(r chi.Router) {
r.Get("/", download)
r.Head("/proxy", proxyDownload)
r.Get("/proxy", proxyDownload)
})
})
logger.Info("Starting HTTP server", slog.String("listen", listenAddr()))

View File

@ -1,46 +0,0 @@
package models
import (
"go.fifitido.net/ytdl-web/pkg/ytdl/metadata"
)
type Video struct {
Meta *metadata.Metadata
Formats []metadata.Format
OtherFormats []metadata.Format
}
func GetVideosFromMetadata(meta *metadata.Metadata) []Video {
if meta.IsPlaylist() {
videos := make([]Video, 0, len(meta.Entries))
for _, entry := range meta.Entries {
videos = append(videos, GetVideosFromMetadata(&entry)...)
}
return videos
}
formats := []metadata.Format{}
otherFormats := []metadata.Format{}
for _, format := range meta.Formats {
if format.ACodec != "none" && format.VCodec != "none" && format.Protocol != "m3u8_native" {
formats = append(formats, format)
} else {
otherFormats = append(otherFormats, format)
}
}
for i, j := 0, len(formats)-1; i < j; i, j = i+1, j-1 {
formats[i], formats[j] = formats[j], formats[i]
}
return []Video{
{
Meta: meta,
Formats: formats,
OtherFormats: otherFormats,
},
}
}

View File

@ -1,41 +0,0 @@
package utils
import (
"fmt"
"log/slog"
"github.com/dgraph-io/badger/v2"
)
type SlogLogger struct {
logger *slog.Logger
}
// Debugf implements badger.Logger
func (l *SlogLogger) Debugf(f string, a ...interface{}) {
l.logger.Debug(fmt.Sprintf(f, a...))
}
// Errorf implements badger.Logger
func (l *SlogLogger) Errorf(f string, a ...interface{}) {
l.logger.Error(fmt.Sprintf(f, a...))
}
// Infof implements badger.Logger
func (l *SlogLogger) Infof(f string, a ...interface{}) {
l.logger.Info(fmt.Sprintf(f, a...))
}
// Warningf implements badger.Logger
func (l *SlogLogger) Warningf(f string, a ...interface{}) {
l.logger.Warn(fmt.Sprintf(f, a...))
}
var _ badger.Logger = (*SlogLogger)(nil)
func NewBadgerLogger(logger *slog.Logger) badger.Logger {
return &SlogLogger{
logger: logger,
}
}

View File

@ -1,21 +0,0 @@
package utils
import (
"context"
"io"
"log/slog"
)
type loggerWriter struct {
logger *slog.Logger
logLevel slog.Level
}
func (lw *loggerWriter) Write(p []byte) (n int, err error) {
lw.logger.Log(context.Background(), lw.logLevel, string(p))
return len(p), nil
}
func LoggerWriter(logger *slog.Logger, level slog.Level) io.Writer {
return &loggerWriter{logger: logger, logLevel: level}
}

28
pkg/views/render.go Normal file
View File

@ -0,0 +1,28 @@
package views
import (
"log/slog"
"net/http"
"github.com/a-h/templ"
)
func Handler(component templ.Component) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
Render(w, r, component)
}
}
func Render(w http.ResponseWriter, r *http.Request, component templ.Component) {
isHtmx := r.Header.Get("HX-Request") == "true"
if isHtmx {
if err := templ.RenderFragments(r.Context(), w, component, "main-content"); err != nil {
slog.ErrorContext(r.Context(), "failed to render page", slog.Any("error", err))
http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
}
return
}
component.Render(r.Context(), w)
}