97 lines
2.3 KiB
Go
97 lines
2.3 KiB
Go
![]() |
package shares
|
||
|
|
||
|
import (
|
||
|
"errors"
|
||
|
"net/http"
|
||
|
"path"
|
||
|
|
||
|
"github.com/go-chi/chi/v5"
|
||
|
"github.com/navidrome/navidrome/conf"
|
||
|
"github.com/navidrome/navidrome/consts"
|
||
|
"github.com/navidrome/navidrome/core"
|
||
|
"github.com/navidrome/navidrome/core/auth"
|
||
|
"github.com/navidrome/navidrome/log"
|
||
|
"github.com/navidrome/navidrome/model"
|
||
|
"github.com/navidrome/navidrome/server"
|
||
|
"github.com/navidrome/navidrome/ui"
|
||
|
)
|
||
|
|
||
|
type Router struct {
|
||
|
http.Handler
|
||
|
ds model.DataStore
|
||
|
share core.Share
|
||
|
assetsHandler http.Handler
|
||
|
streamer core.MediaStreamer
|
||
|
}
|
||
|
|
||
|
func New(ds model.DataStore, share core.Share) *Router {
|
||
|
p := &Router{ds: ds, share: share}
|
||
|
shareRoot := path.Join(conf.Server.BaseURL, consts.URLPathShares)
|
||
|
p.assetsHandler = http.StripPrefix(shareRoot, http.FileServer(http.FS(ui.BuildAssets())))
|
||
|
p.Handler = p.routes()
|
||
|
|
||
|
return p
|
||
|
}
|
||
|
|
||
|
func (p *Router) routes() http.Handler {
|
||
|
r := chi.NewRouter()
|
||
|
|
||
|
r.Group(func(r chi.Router) {
|
||
|
r.Use(server.URLParamsMiddleware)
|
||
|
r.HandleFunc("/{id}", p.handleShares)
|
||
|
r.Handle("/*", p.assetsHandler)
|
||
|
})
|
||
|
return r
|
||
|
}
|
||
|
|
||
|
func (p *Router) handleShares(w http.ResponseWriter, r *http.Request) {
|
||
|
id := r.URL.Query().Get(":id")
|
||
|
if id == "" {
|
||
|
http.Error(w, "invalid id", http.StatusBadRequest)
|
||
|
return
|
||
|
}
|
||
|
|
||
|
// If requested file is a UI asset, just serve it
|
||
|
_, err := ui.BuildAssets().Open(id)
|
||
|
if err == nil {
|
||
|
p.assetsHandler.ServeHTTP(w, r)
|
||
|
return
|
||
|
}
|
||
|
|
||
|
// If it is not, consider it a share ID
|
||
|
s, err := p.share.Load(r.Context(), id)
|
||
|
switch {
|
||
|
case errors.Is(err, model.ErrNotFound):
|
||
|
log.Error(r, "Share not found", "id", id, err)
|
||
|
http.Error(w, "Share not found", http.StatusNotFound)
|
||
|
case err != nil:
|
||
|
log.Error(r, "Error retrieving share", "id", id, err)
|
||
|
http.Error(w, "Error retrieving share", http.StatusInternalServerError)
|
||
|
}
|
||
|
if err != nil {
|
||
|
return
|
||
|
}
|
||
|
|
||
|
s = p.mapShareInfo(s)
|
||
|
server.IndexWithShare(p.ds, ui.BuildAssets(), s)(w, r)
|
||
|
}
|
||
|
|
||
|
func (p *Router) mapShareInfo(s *model.Share) *model.Share {
|
||
|
mapped := &model.Share{
|
||
|
Description: s.Description,
|
||
|
Tracks: s.Tracks,
|
||
|
}
|
||
|
for i := range s.Tracks {
|
||
|
claims := map[string]any{"id": s.Tracks[i].ID}
|
||
|
if s.Format != "" {
|
||
|
claims["f"] = s.Format
|
||
|
}
|
||
|
if s.MaxBitRate != 0 {
|
||
|
claims["b"] = s.MaxBitRate
|
||
|
}
|
||
|
id, _ := auth.CreateExpiringPublicToken(*s.ExpiresAt, claims)
|
||
|
mapped.Tracks[i].ID = id
|
||
|
}
|
||
|
return mapped
|
||
|
}
|