put helpers last
This commit is contained in:
@@ -23,6 +23,12 @@ import (
|
||||
"go.senan.xyz/gonic/transcode"
|
||||
)
|
||||
|
||||
func TestMain(m *testing.M) {
|
||||
gonic.Version = ""
|
||||
log.SetOutput(io.Discard)
|
||||
os.Exit(m.Run())
|
||||
}
|
||||
|
||||
var testCamelExpr = regexp.MustCompile("([a-z0-9])([A-Z])")
|
||||
|
||||
const (
|
||||
@@ -156,9 +162,3 @@ func makec(tb testing.TB, roots []string, audio bool) *Controller {
|
||||
|
||||
return contr
|
||||
}
|
||||
|
||||
func TestMain(m *testing.M) {
|
||||
gonic.Version = ""
|
||||
log.SetOutput(io.Discard)
|
||||
os.Exit(m.Run())
|
||||
}
|
||||
|
||||
@@ -30,43 +30,46 @@ import (
|
||||
// b) return a non-nil spec.Response
|
||||
// _but not both_
|
||||
|
||||
func streamGetTransodePreference(dbc *db.DB, userID int, client string) (*db.TranscodePreference, error) {
|
||||
var pref db.TranscodePreference
|
||||
err := dbc.
|
||||
Where("user_id=?", userID).
|
||||
Where("client COLLATE NOCASE IN (?)", []string{"*", client}).
|
||||
Order("client DESC"). // ensure "*" is last if it's there
|
||||
First(&pref).
|
||||
Error
|
||||
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
return nil, nil
|
||||
}
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("find transcode preference: %w", err)
|
||||
}
|
||||
return &pref, nil
|
||||
}
|
||||
|
||||
func streamGetTranscodeMeta(dbc *db.DB, userID int, client string) spec.TranscodeMeta {
|
||||
pref, _ := streamGetTransodePreference(dbc, userID, client)
|
||||
if pref == nil {
|
||||
return spec.TranscodeMeta{}
|
||||
}
|
||||
profile, ok := transcode.UserProfiles[pref.Profile]
|
||||
if !ok {
|
||||
return spec.TranscodeMeta{}
|
||||
}
|
||||
return spec.TranscodeMeta{
|
||||
TranscodedContentType: profile.MIME(),
|
||||
TranscodedSuffix: profile.Suffix(),
|
||||
}
|
||||
}
|
||||
|
||||
const (
|
||||
coverDefaultSize = 600
|
||||
coverCacheFormat = "png"
|
||||
)
|
||||
|
||||
func (c *Controller) ServeGetCoverArt(w http.ResponseWriter, r *http.Request) *spec.Response {
|
||||
params := r.Context().Value(CtxParams).(params.Params)
|
||||
id, err := params.GetID("id")
|
||||
if err != nil {
|
||||
return spec.NewError(10, "please provide an `id` parameter")
|
||||
}
|
||||
size := params.GetOrInt("size", coverDefaultSize)
|
||||
cachePath := filepath.Join(
|
||||
c.cacheCoverPath,
|
||||
fmt.Sprintf("%s-%d.%s", id.String(), size, coverCacheFormat),
|
||||
)
|
||||
_, err = os.Stat(cachePath)
|
||||
switch {
|
||||
case os.IsNotExist(err):
|
||||
reader, err := coverFor(c.dbc, c.artistInfoCache, id)
|
||||
if err != nil {
|
||||
return spec.NewError(10, "couldn't find cover `%s`: %v", id, err)
|
||||
}
|
||||
defer reader.Close()
|
||||
|
||||
if err := coverScaleAndSave(reader, cachePath, size); err != nil {
|
||||
log.Printf("error scaling cover: %v", err)
|
||||
return nil
|
||||
}
|
||||
case err != nil:
|
||||
log.Printf("error stating `%s`: %v", cachePath, err)
|
||||
return nil
|
||||
}
|
||||
|
||||
w.Header().Set("Cache-Control", "public, max-age=3600")
|
||||
http.ServeFile(w, r, cachePath)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
var (
|
||||
errCoverNotFound = errors.New("could not find a cover with that id")
|
||||
errCoverEmpty = errors.New("no cover found")
|
||||
@@ -163,41 +166,6 @@ func coverScaleAndSave(reader io.Reader, cachePath string, size int) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *Controller) ServeGetCoverArt(w http.ResponseWriter, r *http.Request) *spec.Response {
|
||||
params := r.Context().Value(CtxParams).(params.Params)
|
||||
id, err := params.GetID("id")
|
||||
if err != nil {
|
||||
return spec.NewError(10, "please provide an `id` parameter")
|
||||
}
|
||||
size := params.GetOrInt("size", coverDefaultSize)
|
||||
cachePath := filepath.Join(
|
||||
c.cacheCoverPath,
|
||||
fmt.Sprintf("%s-%d.%s", id.String(), size, coverCacheFormat),
|
||||
)
|
||||
_, err = os.Stat(cachePath)
|
||||
switch {
|
||||
case os.IsNotExist(err):
|
||||
reader, err := coverFor(c.dbc, c.artistInfoCache, id)
|
||||
if err != nil {
|
||||
return spec.NewError(10, "couldn't find cover `%s`: %v", id, err)
|
||||
}
|
||||
defer reader.Close()
|
||||
|
||||
if err := coverScaleAndSave(reader, cachePath, size); err != nil {
|
||||
log.Printf("error scaling cover: %v", err)
|
||||
return nil
|
||||
}
|
||||
case err != nil:
|
||||
log.Printf("error stating `%s`: %v", cachePath, err)
|
||||
return nil
|
||||
}
|
||||
|
||||
w.Header().Set("Cache-Control", "public, max-age=3600")
|
||||
http.ServeFile(w, r, cachePath)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *Controller) ServeStream(w http.ResponseWriter, r *http.Request) *spec.Response {
|
||||
params := r.Context().Value(CtxParams).(params.Params)
|
||||
user := r.Context().Value(CtxUser).(*db.User)
|
||||
@@ -268,3 +236,35 @@ func (c *Controller) ServeGetAvatar(w http.ResponseWriter, r *http.Request) *spe
|
||||
http.ServeContent(w, r, "", time.Now(), bytes.NewReader(reqUser.Avatar))
|
||||
return nil
|
||||
}
|
||||
|
||||
func streamGetTransodePreference(dbc *db.DB, userID int, client string) (*db.TranscodePreference, error) {
|
||||
var pref db.TranscodePreference
|
||||
err := dbc.
|
||||
Where("user_id=?", userID).
|
||||
Where("client COLLATE NOCASE IN (?)", []string{"*", client}).
|
||||
Order("client DESC"). // ensure "*" is last if it's there
|
||||
First(&pref).
|
||||
Error
|
||||
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
return nil, nil
|
||||
}
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("find transcode preference: %w", err)
|
||||
}
|
||||
return &pref, nil
|
||||
}
|
||||
|
||||
func streamGetTranscodeMeta(dbc *db.DB, userID int, client string) spec.TranscodeMeta {
|
||||
pref, _ := streamGetTransodePreference(dbc, userID, client)
|
||||
if pref == nil {
|
||||
return spec.TranscodeMeta{}
|
||||
}
|
||||
profile, ok := transcode.UserProfiles[pref.Profile]
|
||||
if !ok {
|
||||
return spec.TranscodeMeta{}
|
||||
}
|
||||
return spec.TranscodeMeta{
|
||||
TranscodedContentType: profile.MIME(),
|
||||
TranscodedSuffix: profile.Suffix(),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -37,96 +37,6 @@ import (
|
||||
|
||||
var ErrNoValues = errors.New("no values provided")
|
||||
|
||||
// some thin wrappers
|
||||
// may be needed when cleaning up parse() below
|
||||
func parseStr(in string) (string, error) { return in, nil }
|
||||
func parseInt(in string) (int, error) { return strconv.Atoi(in) }
|
||||
func parseFloat(in string) (float64, error) { return strconv.ParseFloat(in, 64) }
|
||||
func parseID(in string) (specid.ID, error) { return specid.New(in) }
|
||||
func parseBool(in string) (bool, error) { return strconv.ParseBool(in) }
|
||||
|
||||
func parseTime(in string) (time.Time, error) {
|
||||
ms, err := strconv.Atoi(in)
|
||||
if err != nil {
|
||||
return time.Time{}, err
|
||||
}
|
||||
ns := int64(ms) * 1e6
|
||||
return time.Unix(0, ns), nil
|
||||
}
|
||||
|
||||
func parse(values []string, i interface{}) error {
|
||||
if len(values) == 0 {
|
||||
return ErrNoValues
|
||||
}
|
||||
var err error
|
||||
switch v := i.(type) {
|
||||
// *T
|
||||
case *string:
|
||||
*v, err = parseStr(values[0])
|
||||
case *int:
|
||||
*v, err = parseInt(values[0])
|
||||
case *float64:
|
||||
*v, err = parseFloat(values[0])
|
||||
case *specid.ID:
|
||||
*v, err = parseID(values[0])
|
||||
case *bool:
|
||||
*v, err = parseBool(values[0])
|
||||
case *time.Time:
|
||||
*v, err = parseTime(values[0])
|
||||
|
||||
// *[]T
|
||||
case *[]string:
|
||||
for _, value := range values {
|
||||
parsed, err := parseStr(value)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
*v = append(*v, parsed)
|
||||
}
|
||||
case *[]int:
|
||||
for _, value := range values {
|
||||
parsed, err := parseInt(value)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
*v = append(*v, parsed)
|
||||
}
|
||||
case *[]float64:
|
||||
for _, value := range values {
|
||||
parsed, err := parseFloat(value)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
*v = append(*v, parsed)
|
||||
}
|
||||
case *[]specid.ID:
|
||||
for _, value := range values {
|
||||
parsed, err := parseID(value)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
*v = append(*v, parsed)
|
||||
}
|
||||
case *[]bool:
|
||||
for _, value := range values {
|
||||
parsed, err := parseBool(value)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
*v = append(*v, parsed)
|
||||
}
|
||||
case *[]time.Time:
|
||||
for _, value := range values {
|
||||
parsed, err := parseTime(value)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
*v = append(*v, parsed)
|
||||
}
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
type Params url.Values
|
||||
|
||||
func New(r *http.Request) Params {
|
||||
@@ -461,3 +371,93 @@ func (p Params) GetFirstOrTime(or time.Time, keys ...string) time.Time {
|
||||
}
|
||||
return or
|
||||
}
|
||||
|
||||
// some thin wrappers
|
||||
// may be needed when cleaning up parse() below
|
||||
func parseStr(in string) (string, error) { return in, nil }
|
||||
func parseInt(in string) (int, error) { return strconv.Atoi(in) }
|
||||
func parseFloat(in string) (float64, error) { return strconv.ParseFloat(in, 64) }
|
||||
func parseID(in string) (specid.ID, error) { return specid.New(in) }
|
||||
func parseBool(in string) (bool, error) { return strconv.ParseBool(in) }
|
||||
|
||||
func parseTime(in string) (time.Time, error) {
|
||||
ms, err := strconv.Atoi(in)
|
||||
if err != nil {
|
||||
return time.Time{}, err
|
||||
}
|
||||
ns := int64(ms) * 1e6
|
||||
return time.Unix(0, ns), nil
|
||||
}
|
||||
|
||||
func parse(values []string, i interface{}) error {
|
||||
if len(values) == 0 {
|
||||
return ErrNoValues
|
||||
}
|
||||
var err error
|
||||
switch v := i.(type) {
|
||||
// *T
|
||||
case *string:
|
||||
*v, err = parseStr(values[0])
|
||||
case *int:
|
||||
*v, err = parseInt(values[0])
|
||||
case *float64:
|
||||
*v, err = parseFloat(values[0])
|
||||
case *specid.ID:
|
||||
*v, err = parseID(values[0])
|
||||
case *bool:
|
||||
*v, err = parseBool(values[0])
|
||||
case *time.Time:
|
||||
*v, err = parseTime(values[0])
|
||||
|
||||
// *[]T
|
||||
case *[]string:
|
||||
for _, value := range values {
|
||||
parsed, err := parseStr(value)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
*v = append(*v, parsed)
|
||||
}
|
||||
case *[]int:
|
||||
for _, value := range values {
|
||||
parsed, err := parseInt(value)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
*v = append(*v, parsed)
|
||||
}
|
||||
case *[]float64:
|
||||
for _, value := range values {
|
||||
parsed, err := parseFloat(value)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
*v = append(*v, parsed)
|
||||
}
|
||||
case *[]specid.ID:
|
||||
for _, value := range values {
|
||||
parsed, err := parseID(value)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
*v = append(*v, parsed)
|
||||
}
|
||||
case *[]bool:
|
||||
for _, value := range values {
|
||||
parsed, err := parseBool(value)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
*v = append(*v, parsed)
|
||||
}
|
||||
case *[]time.Time:
|
||||
for _, value := range values {
|
||||
parsed, err := parseTime(value)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
*v = append(*v, parsed)
|
||||
}
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user