Files
gonic/server/ctrlsubsonic/handlers_by_folder.go
2020-02-19 20:08:37 +00:00

218 lines
5.7 KiB
Go

package ctrlsubsonic
import (
"fmt"
"net/http"
"sort"
"strings"
"github.com/jinzhu/gorm"
"senan.xyz/g/gonic/db"
"senan.xyz/g/gonic/server/ctrlsubsonic/params"
"senan.xyz/g/gonic/server/ctrlsubsonic/spec"
)
// the subsonic spec metions "artist" a lot when talking about the
// browse by folder endpoints. but since we're not browsing by tag
// we can't access artists. so instead we'll consider the artist of
// an track to be the it's respective folder that comes directly
// under the root directory
func (c *Controller) ServeGetIndexes(r *http.Request) *spec.Response {
var folders []*db.Album
c.DB.
Select("*, count(sub.id) as child_count").
Joins(`
LEFT JOIN albums sub
ON albums.id = sub.parent_id
`).
Where("albums.parent_id = 1").
Group("albums.id").
Find(&folders)
// [a-z#] -> 27
indexMap := make(map[string]*spec.Index, 27)
resp := make([]*spec.Index, 0, 27)
for _, folder := range folders {
i := lowerUDecOrHash(folder.IndexRightPath())
index, ok := indexMap[i]
if !ok {
index = &spec.Index{
Name: i,
Artists: []*spec.Artist{},
}
indexMap[i] = index
resp = append(resp, index)
}
index.Artists = append(index.Artists,
spec.NewArtistByFolder(folder))
}
sort.Slice(resp, func(i, j int) bool {
return resp[i].Name < resp[j].Name
})
sub := spec.NewResponse()
sub.Indexes = &spec.Indexes{
LastModified: 0,
Index: resp,
}
return sub
}
func (c *Controller) ServeGetMusicDirectory(r *http.Request) *spec.Response {
params := r.Context().Value(CtxParams).(params.Params)
id, err := params.GetInt("id")
if err != nil {
return spec.NewError(10, "please provide an `id` parameter")
}
childrenObj := []*spec.TrackChild{}
folder := &db.Album{}
c.DB.First(folder, id)
//
// start looking for child childFolders in the current dir
var childFolders []*db.Album
c.DB.
Where("parent_id = ?", id).
Find(&childFolders)
for _, c := range childFolders {
childrenObj = append(childrenObj, spec.NewTCAlbumByFolder(c))
}
//
// start looking for child childTracks in the current dir
var childTracks []*db.Track
c.DB.
Where("album_id = ?", id).
Preload("Album").
Order("filename").
Find(&childTracks)
for _, c := range childTracks {
toAppend := spec.NewTCTrackByFolder(c, folder)
if params.Get("c") == "Jamstash" {
// jamstash thinks it can't play flacs
toAppend.ContentType = "audio/mpeg"
toAppend.Suffix = "mp3"
}
childrenObj = append(childrenObj, toAppend)
}
//
// respond section
sub := spec.NewResponse()
sub.Directory = spec.NewDirectoryByFolder(folder, childrenObj)
return sub
}
// changes to this function should be reflected in in _by_tags.go's
// getAlbumListTwo() function
func (c *Controller) ServeGetAlbumList(r *http.Request) *spec.Response {
params := r.Context().Value(CtxParams).(params.Params)
listType := params.Get("type")
if listType == "" {
return spec.NewError(10, "please provide a `type` parameter")
}
q := c.DB.DB
switch listType {
case "alphabeticalByArtist":
q = q.Joins(`
JOIN albums AS parent_albums
ON albums.parent_id = parent_albums.id`)
q = q.Order("parent_albums.right_path")
case "alphabeticalByName":
q = q.Order("right_path")
case "frequent":
user := r.Context().Value(CtxUser).(*db.User)
q = q.Joins(`
JOIN plays
ON albums.id = plays.album_id AND plays.user_id = ?`,
user.ID)
q = q.Order("plays.count DESC")
case "newest":
q = q.Order("modified_at DESC")
case "random":
q = q.Order(gorm.Expr("random()"))
case "recent":
user := r.Context().Value(CtxUser).(*db.User)
q = q.Joins(`
JOIN plays
ON albums.id = plays.album_id AND plays.user_id = ?`,
user.ID)
q = q.Order("plays.time DESC")
default:
return spec.NewError(10, "unknown value `%s` for parameter 'type'", listType)
}
var folders []*db.Album
q.
Where("albums.tag_artist_id IS NOT NULL").
Offset(params.GetIntOr("offset", 0)).
Limit(params.GetIntOr("size", 10)).
Preload("Parent").
Find(&folders)
sub := spec.NewResponse()
sub.Albums = &spec.Albums{
List: make([]*spec.Album, len(folders)),
}
for i, folder := range folders {
sub.Albums.List[i] = spec.NewAlbumByFolder(folder)
}
return sub
}
func (c *Controller) ServeSearchTwo(r *http.Request) *spec.Response {
params := r.Context().Value(CtxParams).(params.Params)
query := params.Get("query")
if query == "" {
return spec.NewError(10, "please provide a `query` parameter")
}
query = fmt.Sprintf("%%%s%%", strings.TrimSuffix(query, "*"))
results := &spec.SearchResultTwo{}
//
// search "artists"
var artists []*db.Album
c.DB.
Where(`
parent_id = 1
AND (right_path LIKE ? OR
right_path_u_dec LIKE ?)
`, query, query).
Offset(params.GetIntOr("artistOffset", 0)).
Limit(params.GetIntOr("artistCount", 20)).
Find(&artists)
for _, a := range artists {
results.Artists = append(results.Artists,
spec.NewDirectoryByFolder(a, nil))
}
//
// search "albums"
var albums []*db.Album
c.DB.
Where(`
tag_artist_id IS NOT NULL
AND (right_path LIKE ? OR
right_path_u_dec LIKE ?)
`, query, query).
Offset(params.GetIntOr("albumOffset", 0)).
Limit(params.GetIntOr("albumCount", 20)).
Find(&albums)
for _, a := range albums {
results.Albums = append(results.Albums, spec.NewTCAlbumByFolder(a))
}
//
// search tracks
var tracks []*db.Track
c.DB.
Preload("Album").
Where(`
filename LIKE ? OR
filename_u_dec LIKE ?
`, query, query).
Offset(params.GetIntOr("songOffset", 0)).
Limit(params.GetIntOr("songCount", 20)).
Find(&tracks)
for _, t := range tracks {
results.Tracks = append(results.Tracks,
spec.NewTCTrackByFolder(t, t.Album))
}
//
sub := spec.NewResponse()
sub.SearchResultTwo = results
return sub
}