diff --git a/pkg/routes/routes.go b/cmd/ytdl-web/download.go similarity index 66% rename from pkg/routes/routes.go rename to cmd/ytdl-web/download.go index 94cccbf..cc15f3b 100644 --- a/pkg/routes/routes.go +++ b/cmd/ytdl-web/download.go @@ -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) { diff --git a/cmd/ytdl-web/logger.go b/cmd/ytdl-web/logger.go new file mode 100644 index 0000000..ceb7f77 --- /dev/null +++ b/cmd/ytdl-web/logger.go @@ -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...)) +} diff --git a/cmd/ytdl-web/main.go b/cmd/ytdl-web/main.go index f4e5fe8..e8e90da 100644 --- a/cmd/ytdl-web/main.go +++ b/cmd/ytdl-web/main.go @@ -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())) diff --git a/pkg/models/video.go b/pkg/models/video.go deleted file mode 100644 index 9bdc18f..0000000 --- a/pkg/models/video.go +++ /dev/null @@ -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, - }, - } -} diff --git a/pkg/utils/badgerlogger.go b/pkg/utils/badgerlogger.go deleted file mode 100644 index ea3493e..0000000 --- a/pkg/utils/badgerlogger.go +++ /dev/null @@ -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, - } -} diff --git a/pkg/utils/logwriter.go b/pkg/utils/logwriter.go deleted file mode 100644 index 14138e4..0000000 --- a/pkg/utils/logwriter.go +++ /dev/null @@ -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} -} diff --git a/pkg/views/render.go b/pkg/views/render.go new file mode 100644 index 0000000..03f0131 --- /dev/null +++ b/pkg/views/render.go @@ -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) +}