99 lines
2.7 KiB
Go
99 lines
2.7 KiB
Go
package subsonic
|
|
|
|
import (
|
|
"database/sql"
|
|
"strconv"
|
|
|
|
"git.noroutes.net/NoRoutes/vibetunes/internal/database"
|
|
)
|
|
|
|
// folderMap resolves between VibeSonic's opaque music-folder IDs and the
|
|
// small sequential integers the Subsonic protocol expects for
|
|
// musicFolderId (the official XSD types it as xs:int, and some clients
|
|
// build folder-picker UIs assuming small integers). The mapping is
|
|
// derived fresh per request from folder creation order, which is stable
|
|
// as long as folders aren't deleted/recreated — acceptable for how
|
|
// rarely music folders change.
|
|
type folderMap struct {
|
|
folders []*database.MusicFolder
|
|
toInt map[string]int
|
|
toFolder map[int]*database.MusicFolder
|
|
}
|
|
|
|
func loadFolderMap(db *sql.DB) (*folderMap, error) {
|
|
folders, err := database.ListMusicFolders(db)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
fm := &folderMap{
|
|
folders: folders,
|
|
toInt: make(map[string]int, len(folders)),
|
|
toFolder: make(map[int]*database.MusicFolder, len(folders)),
|
|
}
|
|
for i, f := range folders {
|
|
id := i + 1
|
|
fm.toInt[f.ID] = id
|
|
fm.toFolder[id] = f
|
|
}
|
|
return fm, nil
|
|
}
|
|
|
|
func (fm *folderMap) IntID(folderID string) string {
|
|
return strconv.Itoa(fm.toInt[folderID])
|
|
}
|
|
|
|
func (fm *folderMap) ByIntID(s string) *database.MusicFolder {
|
|
n, err := strconv.Atoi(s)
|
|
if err != nil {
|
|
return nil
|
|
}
|
|
return fm.toFolder[n]
|
|
}
|
|
|
|
// Allowed returns the folders a user may see: every folder, unless the
|
|
// user has an explicit restriction list configured.
|
|
func (fm *folderMap) Allowed(db *sql.DB, user *database.User) ([]*database.MusicFolder, error) {
|
|
allowedIDs, err := database.AllowedMusicFolderIDs(db, user.ID)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if len(allowedIDs) == 0 {
|
|
return fm.folders, nil
|
|
}
|
|
set := make(map[string]bool, len(allowedIDs))
|
|
for _, id := range allowedIDs {
|
|
set[id] = true
|
|
}
|
|
var out []*database.MusicFolder
|
|
for _, f := range fm.folders {
|
|
if set[f.ID] {
|
|
out = append(out, f)
|
|
}
|
|
}
|
|
return out, nil
|
|
}
|
|
|
|
// UserCanSeeFolder reports whether user is allowed to see folderID,
|
|
// given their restriction list (empty = unrestricted).
|
|
func UserCanSeeFolder(db *sql.DB, user *database.User, folderID string) (bool, error) {
|
|
allowedIDs, err := database.AllowedMusicFolderIDs(db, user.ID)
|
|
if err != nil {
|
|
return false, err
|
|
}
|
|
if len(allowedIDs) == 0 {
|
|
return true, nil
|
|
}
|
|
for _, id := range allowedIDs {
|
|
if id == folderID {
|
|
return true, nil
|
|
}
|
|
}
|
|
return false, nil
|
|
}
|
|
|
|
// UserCanSeeMediaFile reports whether user is allowed to see a song
|
|
// belonging to the given music folder (per their folder restriction).
|
|
func UserCanSeeMediaFile(db *sql.DB, user *database.User, mediaFile *database.MediaFile) (bool, error) {
|
|
return UserCanSeeFolder(db, user, mediaFile.MusicFolderID)
|
|
}
|