168 lines
		
	
	
		
			4.4 KiB
		
	
	
	
		
			Go
		
	
	
	
			
		
		
	
	
			168 lines
		
	
	
		
			4.4 KiB
		
	
	
	
		
			Go
		
	
	
	
package app
 | 
						|
 | 
						|
import (
 | 
						|
	"fmt"
 | 
						|
	"net/http"
 | 
						|
	"net/url"
 | 
						|
 | 
						|
	"github.com/go-chi/chi/v5"
 | 
						|
	"github.com/go-chi/chi/v5/middleware"
 | 
						|
	"github.com/samber/lo"
 | 
						|
	"github.com/spf13/viper"
 | 
						|
	"go.fifitido.net/ytdl-web/pkg/httpx"
 | 
						|
	"go.fifitido.net/ytdl-web/pkg/middlewarex"
 | 
						|
	"go.fifitido.net/ytdl-web/pkg/view"
 | 
						|
	"go.fifitido.net/ytdl-web/version"
 | 
						|
	"go.fifitido.net/ytdl-web/ytdl"
 | 
						|
	"go.fifitido.net/ytdl-web/ytdl/metadata"
 | 
						|
	"golang.org/x/exp/slog"
 | 
						|
)
 | 
						|
 | 
						|
func Router(ytdl ytdl.Ytdl) http.Handler {
 | 
						|
	r := chi.NewRouter()
 | 
						|
 | 
						|
	r.Use(middleware.RequestID)
 | 
						|
	r.Use(middleware.RealIP)
 | 
						|
	r.Use(middlewarex.SlogRequestLogger(slog.Default().With("module", "app")))
 | 
						|
	r.Use(middleware.Recoverer)
 | 
						|
 | 
						|
	r.Get("/", indexHandler(ytdl))
 | 
						|
	r.Get("/home", homeIndex)
 | 
						|
 | 
						|
	r.Route("/download", func(r chi.Router) {
 | 
						|
		r.Get("/", listDownloads(ytdl))
 | 
						|
		r.Head("/proxy", proxyDownload(ytdl))
 | 
						|
		r.Get("/proxy", proxyDownload(ytdl))
 | 
						|
	})
 | 
						|
 | 
						|
	return r
 | 
						|
}
 | 
						|
 | 
						|
func indexHandler(ytdl ytdl.Ytdl) http.HandlerFunc {
 | 
						|
	return func(w http.ResponseWriter, r *http.Request) {
 | 
						|
		views.Render(w, "index", view.Data{
 | 
						|
			"BasePath":      viper.GetString("base_path"),
 | 
						|
			"Version":       version.Version,
 | 
						|
			"Build":         version.Build,
 | 
						|
			"BinaryVersion": ytdl.Version(),
 | 
						|
		}, "layouts/main")
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
func homeIndex(w http.ResponseWriter, r *http.Request) {
 | 
						|
	views.Render(w, "index", view.Data{
 | 
						|
		"BasePath": viper.GetString("base_path"),
 | 
						|
	})
 | 
						|
}
 | 
						|
 | 
						|
func listDownloads(ytdl ytdl.Ytdl) http.HandlerFunc {
 | 
						|
	return func(w http.ResponseWriter, r *http.Request) {
 | 
						|
		urlBytes, err := url.QueryUnescape(r.URL.Query()["url"][0])
 | 
						|
		if err != nil {
 | 
						|
			views.Render(w, "index", view.Data{
 | 
						|
				"BasePath": viper.GetString("base_path"),
 | 
						|
				"Error": view.Data{
 | 
						|
					"Message": "Invalid URL",
 | 
						|
				},
 | 
						|
			})
 | 
						|
			return
 | 
						|
		}
 | 
						|
		url := string(urlBytes)
 | 
						|
		if len(url) < 1 {
 | 
						|
			views.Render(w, "index", view.Data{
 | 
						|
				"BasePath": viper.GetString("base_path"),
 | 
						|
				"Error": view.Data{
 | 
						|
					"Message": "Invalid URL",
 | 
						|
				},
 | 
						|
			})
 | 
						|
			return
 | 
						|
		}
 | 
						|
 | 
						|
		meta, err := ytdl.GetMetadata(url)
 | 
						|
		if err != nil {
 | 
						|
			views.Render(w, "index", view.Data{
 | 
						|
				"BasePath": viper.GetString("base_path"),
 | 
						|
				"Error": view.Data{
 | 
						|
					"Message": "Could not find a video at that url, maybe try again?",
 | 
						|
				},
 | 
						|
			})
 | 
						|
			return
 | 
						|
		}
 | 
						|
 | 
						|
		views.Render(w, "download", view.Data{
 | 
						|
			"BasePath": viper.GetString("base_path"),
 | 
						|
			"Url":      url,
 | 
						|
			"Meta":     meta,
 | 
						|
			"Videos":   GetVideos(meta),
 | 
						|
		})
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
func proxyDownload(ytdl ytdl.Ytdl) http.HandlerFunc {
 | 
						|
	return func(w http.ResponseWriter, r *http.Request) {
 | 
						|
		rawUrl, err := httpx.Query(r, "url")
 | 
						|
		if err != nil {
 | 
						|
			http.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest)
 | 
						|
			return
 | 
						|
		}
 | 
						|
 | 
						|
		urlBytes, err := url.QueryUnescape(rawUrl)
 | 
						|
		if err != nil {
 | 
						|
			slog.Error("Failed to decode url param", slog.String("error", err.Error()))
 | 
						|
			http.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest)
 | 
						|
			return
 | 
						|
		}
 | 
						|
		url := string(urlBytes)
 | 
						|
		if len(url) < 1 {
 | 
						|
			http.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest)
 | 
						|
			return
 | 
						|
		}
 | 
						|
 | 
						|
		formatId, err := httpx.Query(r, "format")
 | 
						|
		if err != nil {
 | 
						|
			http.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest)
 | 
						|
			return
 | 
						|
		}
 | 
						|
 | 
						|
		meta, err := ytdl.GetMetadata(url)
 | 
						|
		if err != nil {
 | 
						|
			slog.Error("Failed to get metadata", slog.String("error", err.Error()))
 | 
						|
			http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
 | 
						|
			return
 | 
						|
		}
 | 
						|
		videos := GetVideos(meta)
 | 
						|
 | 
						|
		index, err := httpx.QueryInt(r, "index")
 | 
						|
		if err != nil || index < 0 || index >= len(videos) {
 | 
						|
			http.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest)
 | 
						|
			return
 | 
						|
		}
 | 
						|
 | 
						|
		video := videos[index]
 | 
						|
		format, ok := lo.Find(video.Formats, func(format metadata.Format) bool {
 | 
						|
			return format.FormatID == formatId
 | 
						|
		})
 | 
						|
		if !ok {
 | 
						|
			http.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest)
 | 
						|
			return
 | 
						|
		}
 | 
						|
 | 
						|
		w.Header().Set("Content-Disposition", fmt.Sprintf("attachment; filename=\"%s-%s.%s\"", meta.ID, format.Resolution, format.Ext))
 | 
						|
		if format.Filesize != nil {
 | 
						|
			w.Header().Set("Content-Length", fmt.Sprint(*format.Filesize))
 | 
						|
		} else if format.FilesizeApprox != nil {
 | 
						|
			w.Header().Set("Content-Length", fmt.Sprint(*format.FilesizeApprox))
 | 
						|
		}
 | 
						|
 | 
						|
		if len(videos) == 1 {
 | 
						|
			index = -1
 | 
						|
		}
 | 
						|
 | 
						|
		if err := ytdl.Download(w, url, format.FormatID, index); err != nil {
 | 
						|
			slog.Error("Failed to download", slog.String("error", err.Error()))
 | 
						|
			http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
 | 
						|
			return
 | 
						|
		}
 | 
						|
	}
 | 
						|
}
 |