mirror of
https://github.com/navidrome/navidrome.git
synced 2025-04-01 19:47:37 +03:00
refactor: small improvements and clean up (#3423)
* refactor: replace custom map functions with slice.Map * refactor: extract StringerValue function * refactor: removed unnecessary if * chore: removed invalid comment * refactor: replace more map functions * chore: fix FFmpeg typo
This commit is contained in:
parent
0a650de357
commit
a557f37834
17 changed files with 124 additions and 134 deletions
|
@ -1,7 +1,9 @@
|
|||
package log
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"reflect"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
@ -24,6 +26,14 @@ func ShortDur(d time.Duration) string {
|
|||
return strings.TrimSuffix(s, "0m")
|
||||
}
|
||||
|
||||
func StringerValue(s fmt.Stringer) string {
|
||||
v := reflect.ValueOf(s)
|
||||
if v.Kind() == reflect.Pointer && v.IsNil() {
|
||||
return "nil"
|
||||
}
|
||||
return s.String()
|
||||
}
|
||||
|
||||
func CRLFWriter(w io.Writer) io.Writer {
|
||||
return &crlfWriter{w: w}
|
||||
}
|
||||
|
|
|
@ -28,6 +28,16 @@ var _ = DescribeTable("ShortDur",
|
|||
Entry("4h2m", 4*time.Hour+2*time.Minute+5*time.Second+200*time.Millisecond, "4h2m"),
|
||||
)
|
||||
|
||||
var _ = Describe("StringerValue", func() {
|
||||
It("should return the string representation of a fmt.Stringer", func() {
|
||||
Expect(log.StringerValue(time.Second)).To(Equal("1s"))
|
||||
})
|
||||
It("should return 'nil' for a nil fmt.Stringer", func() {
|
||||
v := (*time.Time)(nil)
|
||||
Expect(log.StringerValue(v)).To(Equal("nil"))
|
||||
})
|
||||
})
|
||||
|
||||
var _ = Describe("CRLFWriter", func() {
|
||||
var (
|
||||
buffer *bytes.Buffer
|
||||
|
|
|
@ -7,7 +7,6 @@ import (
|
|||
"io"
|
||||
"net/http"
|
||||
"os"
|
||||
"reflect"
|
||||
"runtime"
|
||||
"sort"
|
||||
"strings"
|
||||
|
@ -277,12 +276,7 @@ func addFields(logger *logrus.Entry, keyValuePairs []interface{}) *logrus.Entry
|
|||
case time.Duration:
|
||||
logger = logger.WithField(name, ShortDur(v))
|
||||
case fmt.Stringer:
|
||||
vOf := reflect.ValueOf(v)
|
||||
if vOf.Kind() == reflect.Pointer && vOf.IsNil() {
|
||||
logger = logger.WithField(name, "nil")
|
||||
} else {
|
||||
logger = logger.WithField(name, v.String())
|
||||
}
|
||||
logger = logger.WithField(name, StringerValue(v))
|
||||
default:
|
||||
logger = logger.WithField(name, v)
|
||||
}
|
||||
|
|
|
@ -25,17 +25,14 @@ func (r sqlRepository) doSearch(q string, offset, size int, results interface{},
|
|||
filter := fullTextExpr(q)
|
||||
if filter != nil {
|
||||
sq = sq.Where(filter)
|
||||
if len(orderBys) > 0 {
|
||||
sq = sq.OrderBy(orderBys...)
|
||||
}
|
||||
sq = sq.OrderBy(orderBys...)
|
||||
} else {
|
||||
// If the filter is empty, we sort by id.
|
||||
// This is to speed up the results of `search3?query=""`, for OpenSubsonic
|
||||
sq = sq.OrderBy("id")
|
||||
}
|
||||
sq = sq.Limit(uint64(size)).Offset(uint64(offset))
|
||||
err := r.queryAll(sq, results, model.QueryOptions{Offset: offset})
|
||||
return err
|
||||
return r.queryAll(sq, results, model.QueryOptions{Offset: offset})
|
||||
}
|
||||
|
||||
func fullTextExpr(value string) Sqlizer {
|
||||
|
|
|
@ -83,7 +83,7 @@ func createJWTSecret(ds model.DataStore) error {
|
|||
return err
|
||||
}
|
||||
|
||||
func checkFfmpegInstallation() {
|
||||
func checkFFmpegInstallation() {
|
||||
f := ffmpeg.New()
|
||||
_, err := f.CmdPath()
|
||||
if err == nil {
|
||||
|
|
|
@ -40,7 +40,7 @@ func New(ds model.DataStore, broker events.Broker) *Server {
|
|||
s.initRoutes()
|
||||
s.mountAuthenticationRoutes()
|
||||
s.mountRootRedirector()
|
||||
checkFfmpegInstallation()
|
||||
checkFFmpegInstallation()
|
||||
checkExternalCredentials()
|
||||
return s
|
||||
}
|
||||
|
|
|
@ -6,11 +6,13 @@ import (
|
|||
"strconv"
|
||||
"time"
|
||||
|
||||
"github.com/navidrome/navidrome/core/scrobbler"
|
||||
"github.com/navidrome/navidrome/log"
|
||||
"github.com/navidrome/navidrome/model"
|
||||
"github.com/navidrome/navidrome/server/subsonic/filter"
|
||||
"github.com/navidrome/navidrome/server/subsonic/responses"
|
||||
"github.com/navidrome/navidrome/utils/req"
|
||||
"github.com/navidrome/navidrome/utils/slice"
|
||||
)
|
||||
|
||||
func (api *Router) getAlbumList(r *http.Request) (model.Albums, int64, error) {
|
||||
|
@ -86,7 +88,9 @@ func (api *Router) GetAlbumList(w http.ResponseWriter, r *http.Request) (*respon
|
|||
w.Header().Set("x-total-count", strconv.Itoa(int(count)))
|
||||
|
||||
response := newResponse()
|
||||
response.AlbumList = &responses.AlbumList{Album: childrenFromAlbums(r.Context(), albums)}
|
||||
response.AlbumList = &responses.AlbumList{
|
||||
Album: slice.MapWithArg(albums, r.Context(), childFromAlbum),
|
||||
}
|
||||
return response, nil
|
||||
}
|
||||
|
||||
|
@ -99,7 +103,9 @@ func (api *Router) GetAlbumList2(w http.ResponseWriter, r *http.Request) (*respo
|
|||
w.Header().Set("x-total-count", strconv.FormatInt(pageCount, 10))
|
||||
|
||||
response := newResponse()
|
||||
response.AlbumList2 = &responses.AlbumList{Album: childrenFromAlbums(r.Context(), albums)}
|
||||
response.AlbumList2 = &responses.AlbumList{
|
||||
Album: slice.MapWithArg(albums, r.Context(), childFromAlbum),
|
||||
}
|
||||
return response, nil
|
||||
}
|
||||
|
||||
|
@ -124,9 +130,9 @@ func (api *Router) GetStarred(r *http.Request) (*responses.Subsonic, error) {
|
|||
|
||||
response := newResponse()
|
||||
response.Starred = &responses.Starred{}
|
||||
response.Starred.Artist = toArtists(r, artists)
|
||||
response.Starred.Album = childrenFromAlbums(r.Context(), albums)
|
||||
response.Starred.Song = childrenFromMediaFiles(r.Context(), mediaFiles)
|
||||
response.Starred.Artist = slice.MapWithArg(artists, r, toArtist)
|
||||
response.Starred.Album = slice.MapWithArg(albums, ctx, childFromAlbum)
|
||||
response.Starred.Song = slice.MapWithArg(mediaFiles, ctx, childFromMediaFile)
|
||||
return response, nil
|
||||
}
|
||||
|
||||
|
@ -151,14 +157,16 @@ func (api *Router) GetNowPlaying(r *http.Request) (*responses.Subsonic, error) {
|
|||
|
||||
response := newResponse()
|
||||
response.NowPlaying = &responses.NowPlaying{}
|
||||
response.NowPlaying.Entry = make([]responses.NowPlayingEntry, len(npInfo))
|
||||
for i, np := range npInfo {
|
||||
response.NowPlaying.Entry[i].Child = childFromMediaFile(ctx, np.MediaFile)
|
||||
response.NowPlaying.Entry[i].UserName = np.Username
|
||||
response.NowPlaying.Entry[i].MinutesAgo = int32(time.Since(np.Start).Minutes())
|
||||
response.NowPlaying.Entry[i].PlayerId = int32(i + 1) // Fake numeric playerId, it does not seem to be used for anything
|
||||
response.NowPlaying.Entry[i].PlayerName = np.PlayerName
|
||||
}
|
||||
var i int32
|
||||
response.NowPlaying.Entry = slice.Map(npInfo, func(np scrobbler.NowPlayingInfo) responses.NowPlayingEntry {
|
||||
return responses.NowPlayingEntry{
|
||||
Child: childFromMediaFile(ctx, np.MediaFile),
|
||||
UserName: np.Username,
|
||||
MinutesAgo: int32(time.Since(np.Start).Minutes()),
|
||||
PlayerId: i + 1, // Fake numeric playerId, it does not seem to be used for anything
|
||||
PlayerName: np.PlayerName,
|
||||
}
|
||||
})
|
||||
return response, nil
|
||||
}
|
||||
|
||||
|
@ -177,7 +185,7 @@ func (api *Router) GetRandomSongs(r *http.Request) (*responses.Subsonic, error)
|
|||
|
||||
response := newResponse()
|
||||
response.RandomSongs = &responses.Songs{}
|
||||
response.RandomSongs.Songs = childrenFromMediaFiles(r.Context(), songs)
|
||||
response.RandomSongs.Songs = slice.MapWithArg(songs, r.Context(), childFromMediaFile)
|
||||
return response, nil
|
||||
}
|
||||
|
||||
|
@ -195,7 +203,7 @@ func (api *Router) GetSongsByGenre(r *http.Request) (*responses.Subsonic, error)
|
|||
|
||||
response := newResponse()
|
||||
response.SongsByGenre = &responses.Songs{}
|
||||
response.SongsByGenre.Songs = childrenFromMediaFiles(r.Context(), songs)
|
||||
response.SongsByGenre.Songs = slice.MapWithArg(songs, r.Context(), childFromMediaFile)
|
||||
return response, nil
|
||||
}
|
||||
|
||||
|
|
|
@ -9,21 +9,22 @@ import (
|
|||
"github.com/navidrome/navidrome/model/request"
|
||||
"github.com/navidrome/navidrome/server/subsonic/responses"
|
||||
"github.com/navidrome/navidrome/utils/req"
|
||||
"github.com/navidrome/navidrome/utils/slice"
|
||||
)
|
||||
|
||||
func (api *Router) GetBookmarks(r *http.Request) (*responses.Subsonic, error) {
|
||||
user, _ := request.UserFrom(r.Context())
|
||||
|
||||
repo := api.ds.MediaFile(r.Context())
|
||||
bmks, err := repo.GetBookmarks()
|
||||
bookmarks, err := repo.GetBookmarks()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
response := newResponse()
|
||||
response.Bookmarks = &responses.Bookmarks{}
|
||||
for _, bmk := range bmks {
|
||||
b := responses.Bookmark{
|
||||
response.Bookmarks.Bookmark = slice.Map(bookmarks, func(bmk model.Bookmark) responses.Bookmark {
|
||||
return responses.Bookmark{
|
||||
Entry: childFromMediaFile(r.Context(), bmk.Item),
|
||||
Position: bmk.Position,
|
||||
Username: user.UserName,
|
||||
|
@ -31,8 +32,7 @@ func (api *Router) GetBookmarks(r *http.Request) (*responses.Subsonic, error) {
|
|||
Created: bmk.CreatedAt,
|
||||
Changed: bmk.UpdatedAt,
|
||||
}
|
||||
response.Bookmarks.Bookmark = append(response.Bookmarks.Bookmark, b)
|
||||
}
|
||||
})
|
||||
return response, nil
|
||||
}
|
||||
|
||||
|
@ -83,7 +83,7 @@ func (api *Router) GetPlayQueue(r *http.Request) (*responses.Subsonic, error) {
|
|||
|
||||
response := newResponse()
|
||||
response.PlayQueue = &responses.PlayQueue{
|
||||
Entry: childrenFromMediaFiles(r.Context(), pq.Items),
|
||||
Entry: slice.MapWithArg(pq.Items, r.Context(), childFromMediaFile),
|
||||
Current: pq.Current,
|
||||
Position: pq.Position,
|
||||
Username: user.UserName,
|
||||
|
|
|
@ -13,6 +13,7 @@ import (
|
|||
"github.com/navidrome/navidrome/server/subsonic/filter"
|
||||
"github.com/navidrome/navidrome/server/subsonic/responses"
|
||||
"github.com/navidrome/navidrome/utils/req"
|
||||
"github.com/navidrome/navidrome/utils/slice"
|
||||
)
|
||||
|
||||
func (api *Router) GetMusicFolders(r *http.Request) (*responses.Subsonic, error) {
|
||||
|
@ -61,7 +62,7 @@ func (api *Router) getArtistIndex(r *http.Request, libId int, ifModifiedSince ti
|
|||
res.Index = make([]responses.Index, len(indexes))
|
||||
for i, idx := range indexes {
|
||||
res.Index[i].Name = idx.ID
|
||||
res.Index[i].Artists = toArtists(r, idx.Artists)
|
||||
res.Index[i].Artists = slice.MapWithArg(idx.Artists, r, toArtist)
|
||||
}
|
||||
return res, nil
|
||||
}
|
||||
|
@ -80,7 +81,7 @@ func (api *Router) getArtistIndexID3(r *http.Request, libId int, ifModifiedSince
|
|||
res.Index = make([]responses.IndexID3, len(indexes))
|
||||
for i, idx := range indexes {
|
||||
res.Index[i].Name = idx.ID
|
||||
res.Index[i].Artists = toArtistsID3(r, idx.Artists)
|
||||
res.Index[i].Artists = slice.MapWithArg(idx.Artists, r, toArtistID3)
|
||||
}
|
||||
return res, nil
|
||||
}
|
||||
|
@ -336,7 +337,7 @@ func (api *Router) GetSimilarSongs(r *http.Request) (*responses.Subsonic, error)
|
|||
|
||||
response := newResponse()
|
||||
response.SimilarSongs = &responses.SimilarSongs{
|
||||
Song: childrenFromMediaFiles(ctx, songs),
|
||||
Song: slice.MapWithArg(songs, ctx, childFromMediaFile),
|
||||
}
|
||||
return response, nil
|
||||
}
|
||||
|
@ -370,7 +371,7 @@ func (api *Router) GetTopSongs(r *http.Request) (*responses.Subsonic, error) {
|
|||
|
||||
response := newResponse()
|
||||
response.TopSongs = &responses.TopSongs{
|
||||
Song: childrenFromMediaFiles(ctx, songs),
|
||||
Song: slice.MapWithArg(songs, ctx, childFromMediaFile),
|
||||
}
|
||||
return response, nil
|
||||
}
|
||||
|
@ -394,7 +395,7 @@ func (api *Router) buildArtistDirectory(ctx context.Context, artist *model.Artis
|
|||
return nil, err
|
||||
}
|
||||
|
||||
dir.Child = childrenFromAlbums(ctx, albums)
|
||||
dir.Child = slice.MapWithArg(albums, ctx, childFromAlbum)
|
||||
return dir, nil
|
||||
}
|
||||
|
||||
|
@ -408,7 +409,7 @@ func (api *Router) buildArtist(r *http.Request, artist *model.Artist) (*response
|
|||
return nil, err
|
||||
}
|
||||
|
||||
a.Album = childrenFromAlbums(r.Context(), albums)
|
||||
a.Album = slice.MapWithArg(albums, ctx, childFromAlbum)
|
||||
return a, nil
|
||||
}
|
||||
|
||||
|
@ -433,13 +434,13 @@ func (api *Router) buildAlbumDirectory(ctx context.Context, album *model.Album)
|
|||
return nil, err
|
||||
}
|
||||
|
||||
dir.Child = childrenFromMediaFiles(ctx, mfs)
|
||||
dir.Child = slice.MapWithArg(mfs, ctx, childFromMediaFile)
|
||||
return dir, nil
|
||||
}
|
||||
|
||||
func (api *Router) buildAlbum(ctx context.Context, album *model.Album, mfs model.MediaFiles) *responses.AlbumWithSongsID3 {
|
||||
dir := &responses.AlbumWithSongsID3{}
|
||||
dir.AlbumID3 = buildAlbumID3(ctx, *album)
|
||||
dir.Song = childrenFromMediaFiles(ctx, mfs)
|
||||
dir.Song = slice.MapWithArg(mfs, ctx, childFromMediaFile)
|
||||
return dir
|
||||
}
|
||||
|
|
|
@ -64,14 +64,6 @@ func getUser(ctx context.Context) model.User {
|
|||
return model.User{}
|
||||
}
|
||||
|
||||
func toArtists(r *http.Request, artists model.Artists) []responses.Artist {
|
||||
as := make([]responses.Artist, len(artists))
|
||||
for i, artist := range artists {
|
||||
as[i] = toArtist(r, artist)
|
||||
}
|
||||
return as
|
||||
}
|
||||
|
||||
func toArtist(r *http.Request, a model.Artist) responses.Artist {
|
||||
artist := responses.Artist{
|
||||
Id: a.ID,
|
||||
|
@ -104,14 +96,6 @@ func toArtistID3(r *http.Request, a model.Artist) responses.ArtistID3 {
|
|||
return artist
|
||||
}
|
||||
|
||||
func toArtistsID3(r *http.Request, artists model.Artists) []responses.ArtistID3 {
|
||||
as := make([]responses.ArtistID3, len(artists))
|
||||
for i, artist := range artists {
|
||||
as[i] = toArtistID3(r, artist)
|
||||
}
|
||||
return as
|
||||
}
|
||||
|
||||
func toGenres(genres model.Genres) *responses.Genres {
|
||||
response := make([]responses.Genre, len(genres))
|
||||
for i, g := range genres {
|
||||
|
@ -124,6 +108,14 @@ func toGenres(genres model.Genres) *responses.Genres {
|
|||
return &responses.Genres{Genre: response}
|
||||
}
|
||||
|
||||
func toItemGenres(genres model.Genres) []responses.ItemGenre {
|
||||
itemGenres := make([]responses.ItemGenre, len(genres))
|
||||
for i, g := range genres {
|
||||
itemGenres[i] = responses.ItemGenre{Name: g.Name}
|
||||
}
|
||||
return itemGenres
|
||||
}
|
||||
|
||||
func getTranscoding(ctx context.Context) (format string, bitRate int) {
|
||||
if trc, ok := request.TranscodingFrom(ctx); ok {
|
||||
format = trc.TargetFormat
|
||||
|
@ -134,8 +126,6 @@ func getTranscoding(ctx context.Context) (format string, bitRate int) {
|
|||
return
|
||||
}
|
||||
|
||||
// This seems to be duplicated, but it is an initial step into merging `engine` and the `subsonic` packages,
|
||||
// In the future there won't be any conversion to/from `engine. Entry` anymore
|
||||
func childFromMediaFile(ctx context.Context, mf model.MediaFile) responses.Child {
|
||||
child := responses.Child{}
|
||||
child.Id = mf.ID
|
||||
|
@ -146,7 +136,7 @@ func childFromMediaFile(ctx context.Context, mf model.MediaFile) responses.Child
|
|||
child.Year = int32(mf.Year)
|
||||
child.Artist = mf.Artist
|
||||
child.Genre = mf.Genre
|
||||
child.Genres = buildItemGenres(mf.Genres)
|
||||
child.Genres = toItemGenres(mf.Genres)
|
||||
child.Track = int32(mf.TrackNumber)
|
||||
child.Duration = int32(mf.Duration)
|
||||
child.Size = mf.Size
|
||||
|
@ -208,14 +198,6 @@ func mapSlashToDash(target string) string {
|
|||
return strings.ReplaceAll(target, "/", "_")
|
||||
}
|
||||
|
||||
func childrenFromMediaFiles(ctx context.Context, mfs model.MediaFiles) []responses.Child {
|
||||
children := make([]responses.Child, len(mfs))
|
||||
for i, mf := range mfs {
|
||||
children[i] = childFromMediaFile(ctx, mf)
|
||||
}
|
||||
return children
|
||||
}
|
||||
|
||||
func childFromAlbum(_ context.Context, al model.Album) responses.Child {
|
||||
child := responses.Child{}
|
||||
child.Id = al.ID
|
||||
|
@ -226,7 +208,7 @@ func childFromAlbum(_ context.Context, al model.Album) responses.Child {
|
|||
child.Artist = al.AlbumArtist
|
||||
child.Year = int32(al.MaxYear)
|
||||
child.Genre = al.Genre
|
||||
child.Genres = buildItemGenres(al.Genres)
|
||||
child.Genres = toItemGenres(al.Genres)
|
||||
child.CoverArt = al.CoverArtID().String()
|
||||
child.Created = &al.CreatedAt
|
||||
child.Parent = al.AlbumArtistID
|
||||
|
@ -247,14 +229,6 @@ func childFromAlbum(_ context.Context, al model.Album) responses.Child {
|
|||
return child
|
||||
}
|
||||
|
||||
func childrenFromAlbums(ctx context.Context, als model.Albums) []responses.Child {
|
||||
children := make([]responses.Child, len(als))
|
||||
for i, al := range als {
|
||||
children[i] = childFromAlbum(ctx, al)
|
||||
}
|
||||
return children
|
||||
}
|
||||
|
||||
// toItemDate converts a string date in the formats 'YYYY-MM-DD', 'YYYY-MM' or 'YYYY' to an OS ItemDate
|
||||
func toItemDate(date string) responses.ItemDate {
|
||||
itemDate := responses.ItemDate{}
|
||||
|
@ -273,15 +247,7 @@ func toItemDate(date string) responses.ItemDate {
|
|||
return itemDate
|
||||
}
|
||||
|
||||
func buildItemGenres(genres model.Genres) []responses.ItemGenre {
|
||||
itemGenres := make([]responses.ItemGenre, len(genres))
|
||||
for i, g := range genres {
|
||||
itemGenres[i] = responses.ItemGenre{Name: g.Name}
|
||||
}
|
||||
return itemGenres
|
||||
}
|
||||
|
||||
func buildDiscSubtitles(_ context.Context, a model.Album) responses.DiscTitles {
|
||||
func buildDiscSubtitles(a model.Album) responses.DiscTitles {
|
||||
if len(a.Discs) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
@ -295,14 +261,6 @@ func buildDiscSubtitles(_ context.Context, a model.Album) responses.DiscTitles {
|
|||
return discTitles
|
||||
}
|
||||
|
||||
func buildAlbumsID3(ctx context.Context, albums model.Albums) []responses.AlbumID3 {
|
||||
res := make([]responses.AlbumID3, len(albums))
|
||||
for i, album := range albums {
|
||||
res[i] = buildAlbumID3(ctx, album)
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
func buildAlbumID3(ctx context.Context, album model.Album) responses.AlbumID3 {
|
||||
dir := responses.AlbumID3{}
|
||||
dir.Id = album.ID
|
||||
|
@ -318,8 +276,8 @@ func buildAlbumID3(ctx context.Context, album model.Album) responses.AlbumID3 {
|
|||
}
|
||||
dir.Year = int32(album.MaxYear)
|
||||
dir.Genre = album.Genre
|
||||
dir.Genres = buildItemGenres(album.Genres)
|
||||
dir.DiscTitles = buildDiscSubtitles(ctx, album)
|
||||
dir.Genres = toItemGenres(album.Genres)
|
||||
dir.DiscTitles = buildDiscSubtitles(album)
|
||||
dir.UserRating = int32(album.Rating)
|
||||
if !album.CreatedAt.IsZero() {
|
||||
dir.Created = &album.CreatedAt
|
||||
|
|
|
@ -1,8 +1,6 @@
|
|||
package subsonic
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/navidrome/navidrome/model"
|
||||
"github.com/navidrome/navidrome/server/subsonic/responses"
|
||||
. "github.com/onsi/ginkgo/v2"
|
||||
|
@ -40,7 +38,7 @@ var _ = Describe("helpers", func() {
|
|||
Describe("buildDiscTitles", func() {
|
||||
It("should return nil when album has no discs", func() {
|
||||
album := model.Album{}
|
||||
Expect(buildDiscSubtitles(context.Background(), album)).To(BeNil())
|
||||
Expect(buildDiscSubtitles(album)).To(BeNil())
|
||||
})
|
||||
|
||||
It("should return correct disc titles when album has discs with valid disc numbers", func() {
|
||||
|
@ -54,7 +52,7 @@ var _ = Describe("helpers", func() {
|
|||
{Disc: 1, Title: "Disc 1"},
|
||||
{Disc: 2, Title: "Disc 2"},
|
||||
}
|
||||
Expect(buildDiscSubtitles(context.Background(), album)).To(Equal(expected))
|
||||
Expect(buildDiscSubtitles(album)).To(Equal(expected))
|
||||
})
|
||||
})
|
||||
|
||||
|
|
|
@ -9,6 +9,7 @@ import (
|
|||
"github.com/navidrome/navidrome/log"
|
||||
"github.com/navidrome/navidrome/server/subsonic/responses"
|
||||
"github.com/navidrome/navidrome/utils/req"
|
||||
"github.com/navidrome/navidrome/utils/slice"
|
||||
)
|
||||
|
||||
const (
|
||||
|
@ -58,7 +59,7 @@ func (api *Router) JukeboxControl(r *http.Request) (*responses.Subsonic, error)
|
|||
|
||||
playlist := responses.JukeboxPlaylist{
|
||||
JukeboxStatus: *deviceStatusToJukeboxStatus(status),
|
||||
Entry: childrenFromMediaFiles(ctx, mediafiles),
|
||||
Entry: slice.MapWithArg(mediafiles, ctx, childFromMediaFile),
|
||||
}
|
||||
|
||||
response := newResponse()
|
||||
|
|
|
@ -11,6 +11,7 @@ import (
|
|||
"github.com/navidrome/navidrome/model"
|
||||
"github.com/navidrome/navidrome/server/subsonic/responses"
|
||||
"github.com/navidrome/navidrome/utils/req"
|
||||
"github.com/navidrome/navidrome/utils/slice"
|
||||
)
|
||||
|
||||
func (api *Router) GetPlaylists(r *http.Request) (*responses.Subsonic, error) {
|
||||
|
@ -20,12 +21,10 @@ func (api *Router) GetPlaylists(r *http.Request) (*responses.Subsonic, error) {
|
|||
log.Error(r, err)
|
||||
return nil, err
|
||||
}
|
||||
playlists := make([]responses.Playlist, len(allPls))
|
||||
for i, p := range allPls {
|
||||
playlists[i] = *api.buildPlaylist(p)
|
||||
}
|
||||
response := newResponse()
|
||||
response.Playlists = &responses.Playlists{Playlist: playlists}
|
||||
response.Playlists = &responses.Playlists{
|
||||
Playlist: slice.Map(allPls, api.buildPlaylist),
|
||||
}
|
||||
return response, nil
|
||||
}
|
||||
|
||||
|
@ -51,7 +50,10 @@ func (api *Router) getPlaylist(ctx context.Context, id string) (*responses.Subso
|
|||
}
|
||||
|
||||
response := newResponse()
|
||||
response.Playlist = api.buildPlaylistWithSongs(ctx, pls)
|
||||
response.Playlist = &responses.PlaylistWithSongs{
|
||||
Playlist: api.buildPlaylist(*pls),
|
||||
}
|
||||
response.Playlist.Entry = slice.MapWithArg(pls.MediaFiles(), ctx, childFromMediaFile)
|
||||
return response, nil
|
||||
}
|
||||
|
||||
|
@ -156,16 +158,8 @@ func (api *Router) UpdatePlaylist(r *http.Request) (*responses.Subsonic, error)
|
|||
return newResponse(), nil
|
||||
}
|
||||
|
||||
func (api *Router) buildPlaylistWithSongs(ctx context.Context, p *model.Playlist) *responses.PlaylistWithSongs {
|
||||
pls := &responses.PlaylistWithSongs{
|
||||
Playlist: *api.buildPlaylist(*p),
|
||||
}
|
||||
pls.Entry = childrenFromMediaFiles(ctx, p.MediaFiles())
|
||||
return pls
|
||||
}
|
||||
|
||||
func (api *Router) buildPlaylist(p model.Playlist) *responses.Playlist {
|
||||
pls := &responses.Playlist{}
|
||||
func (api *Router) buildPlaylist(p model.Playlist) responses.Playlist {
|
||||
pls := responses.Playlist{}
|
||||
pls.Id = p.ID
|
||||
pls.Name = p.Name
|
||||
pls.Comment = p.Comment
|
||||
|
|
|
@ -14,6 +14,7 @@ import (
|
|||
"github.com/navidrome/navidrome/server/public"
|
||||
"github.com/navidrome/navidrome/server/subsonic/responses"
|
||||
"github.com/navidrome/navidrome/utils/req"
|
||||
"github.com/navidrome/navidrome/utils/slice"
|
||||
"golang.org/x/sync/errgroup"
|
||||
)
|
||||
|
||||
|
@ -89,9 +90,8 @@ func (api *Router) Search2(r *http.Request) (*responses.Subsonic, error) {
|
|||
|
||||
response := newResponse()
|
||||
searchResult2 := &responses.SearchResult2{}
|
||||
searchResult2.Artist = make([]responses.Artist, len(as))
|
||||
for i, artist := range as {
|
||||
searchResult2.Artist[i] = responses.Artist{
|
||||
searchResult2.Artist = slice.Map(as, func(artist model.Artist) responses.Artist {
|
||||
a := responses.Artist{
|
||||
Id: artist.ID,
|
||||
Name: artist.Name,
|
||||
AlbumCount: int32(artist.AlbumCount),
|
||||
|
@ -100,11 +100,12 @@ func (api *Router) Search2(r *http.Request) (*responses.Subsonic, error) {
|
|||
ArtistImageUrl: public.ImageURL(r, artist.CoverArtID(), 600),
|
||||
}
|
||||
if artist.Starred {
|
||||
searchResult2.Artist[i].Starred = as[i].StarredAt
|
||||
a.Starred = artist.StarredAt
|
||||
}
|
||||
}
|
||||
searchResult2.Album = childrenFromAlbums(ctx, als)
|
||||
searchResult2.Song = childrenFromMediaFiles(ctx, mfs)
|
||||
return a
|
||||
})
|
||||
searchResult2.Album = slice.MapWithArg(als, ctx, childFromAlbum)
|
||||
searchResult2.Song = slice.MapWithArg(mfs, ctx, childFromMediaFile)
|
||||
response.SearchResult2 = searchResult2
|
||||
return response, nil
|
||||
}
|
||||
|
@ -119,12 +120,9 @@ func (api *Router) Search3(r *http.Request) (*responses.Subsonic, error) {
|
|||
|
||||
response := newResponse()
|
||||
searchResult3 := &responses.SearchResult3{}
|
||||
searchResult3.Artist = make([]responses.ArtistID3, len(as))
|
||||
for i, artist := range as {
|
||||
searchResult3.Artist[i] = toArtistID3(r, artist)
|
||||
}
|
||||
searchResult3.Album = buildAlbumsID3(ctx, als)
|
||||
searchResult3.Song = childrenFromMediaFiles(ctx, mfs)
|
||||
searchResult3.Artist = slice.MapWithArg(as, r, toArtistID3)
|
||||
searchResult3.Album = slice.MapWithArg(als, ctx, buildAlbumID3)
|
||||
searchResult3.Song = slice.MapWithArg(mfs, ctx, childFromMediaFile)
|
||||
response.SearchResult3 = searchResult3
|
||||
return response, nil
|
||||
}
|
||||
|
|
|
@ -11,6 +11,7 @@ import (
|
|||
"github.com/navidrome/navidrome/server/subsonic/responses"
|
||||
. "github.com/navidrome/navidrome/utils/gg"
|
||||
"github.com/navidrome/navidrome/utils/req"
|
||||
"github.com/navidrome/navidrome/utils/slice"
|
||||
)
|
||||
|
||||
func (api *Router) GetShares(r *http.Request) (*responses.Subsonic, error) {
|
||||
|
@ -43,9 +44,9 @@ func (api *Router) buildShare(r *http.Request, share model.Share) responses.Shar
|
|||
resp.Description = share.Contents
|
||||
}
|
||||
if len(share.Albums) > 0 {
|
||||
resp.Entry = childrenFromAlbums(r.Context(), share.Albums)
|
||||
resp.Entry = slice.MapWithArg(share.Albums, r.Context(), childFromAlbum)
|
||||
} else {
|
||||
resp.Entry = childrenFromMediaFiles(r.Context(), share.Tracks)
|
||||
resp.Entry = slice.MapWithArg(share.Tracks, r.Context(), childFromMediaFile)
|
||||
}
|
||||
return resp
|
||||
}
|
||||
|
|
|
@ -15,6 +15,12 @@ func Map[T any, R any](t []T, mapFunc func(T) R) []R {
|
|||
return r
|
||||
}
|
||||
|
||||
func MapWithArg[I any, O any, A any](t []I, arg A, mapFunc func(A, I) O) []O {
|
||||
return Map(t, func(e I) O {
|
||||
return mapFunc(arg, e)
|
||||
})
|
||||
}
|
||||
|
||||
func Group[T any, K comparable](s []T, keyFunc func(T) K) map[K][]T {
|
||||
m := map[K][]T{}
|
||||
for _, item := range s {
|
||||
|
|
|
@ -33,6 +33,20 @@ var _ = Describe("Slice Utils", func() {
|
|||
})
|
||||
})
|
||||
|
||||
Describe("MapWithArg", func() {
|
||||
It("returns empty slice for an empty input", func() {
|
||||
mapFunc := func(a int, v int) string { return strconv.Itoa(a + v) }
|
||||
result := slice.MapWithArg([]int{}, 10, mapFunc)
|
||||
Expect(result).To(BeEmpty())
|
||||
})
|
||||
|
||||
It("returns a new slice with elements mapped", func() {
|
||||
mapFunc := func(a int, v int) string { return strconv.Itoa(a + v) }
|
||||
result := slice.MapWithArg([]int{1, 2, 3, 4}, 10, mapFunc)
|
||||
Expect(result).To(ConsistOf("11", "12", "13", "14"))
|
||||
})
|
||||
})
|
||||
|
||||
Describe("Group", func() {
|
||||
It("returns empty map for an empty input", func() {
|
||||
keyFunc := func(v int) int { return v % 2 }
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue