mirror of
https://github.com/navidrome/navidrome.git
synced 2026-10-26 18:27:09 +01:00
feat(library): streamline library ID filtering in repositories and update resource filtering logic
Signed-off-by: Deluan <deluan@navidrome.org>
This commit is contained in:
parent
d44292cdc7
commit
93059204f3
6 changed files with 47 additions and 63 deletions
|
|
@ -6,7 +6,6 @@ import (
|
|||
"fmt"
|
||||
"maps"
|
||||
"slices"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
|
@ -185,42 +184,6 @@ func allRolesFilter(_ string, value interface{}) Sqlizer {
|
|||
return Like{"participants": fmt.Sprintf(`%%"%s"%%`, value)}
|
||||
}
|
||||
|
||||
func libraryIdFilter(field string, value interface{}) Sqlizer {
|
||||
switch v := value.(type) {
|
||||
case string:
|
||||
// Handle single library ID as string
|
||||
if libID, err := strconv.Atoi(v); err == nil {
|
||||
return Eq{"library_id": libID}
|
||||
}
|
||||
return Eq{"library_id": v}
|
||||
case int:
|
||||
return Eq{"library_id": v}
|
||||
case int64:
|
||||
return Eq{"library_id": v}
|
||||
case float64:
|
||||
return Eq{"library_id": int(v)}
|
||||
case []interface{}:
|
||||
// Handle multiple library IDs
|
||||
var ids []interface{}
|
||||
for _, id := range v {
|
||||
switch idVal := id.(type) {
|
||||
case string:
|
||||
if libID, err := strconv.Atoi(idVal); err == nil {
|
||||
ids = append(ids, libID)
|
||||
} else {
|
||||
ids = append(ids, idVal)
|
||||
}
|
||||
case int, int64, float64:
|
||||
ids = append(ids, idVal)
|
||||
}
|
||||
}
|
||||
if len(ids) > 0 {
|
||||
return Eq{"library_id": ids}
|
||||
}
|
||||
}
|
||||
return Eq{"library_id": value}
|
||||
}
|
||||
|
||||
func (r *albumRepository) CountAll(options ...model.QueryOptions) (int64, error) {
|
||||
query := r.newSelect()
|
||||
query = r.withAnnotation(query, "album.id")
|
||||
|
|
|
|||
|
|
@ -96,6 +96,7 @@ var mediaFileFilter = sync.OnceValue(func() map[string]filterFunc {
|
|||
"genre_id": tagIDFilter,
|
||||
"missing": booleanFilter,
|
||||
"artists_id": artistFilter,
|
||||
"library_id": libraryIdFilter,
|
||||
}
|
||||
// Add all album tags as filters
|
||||
for tag := range model.TagMappings() {
|
||||
|
|
|
|||
|
|
@ -379,6 +379,7 @@ func (r *playlistRepository) refreshCounters(pls *model.Playlist) error {
|
|||
}
|
||||
|
||||
func (r *playlistRepository) loadTracks(sel SelectBuilder, id string) (model.PlaylistTracks, error) {
|
||||
sel = r.applyLibraryFilter(sel, "f")
|
||||
tracksQuery := sel.
|
||||
Columns(
|
||||
"coalesce(starred, 0) as starred",
|
||||
|
|
|
|||
|
|
@ -47,7 +47,8 @@ func (r *playlistRepository) Tracks(playlistId string, refreshSmartPlaylist bool
|
|||
p.db = r.db
|
||||
p.tableName = "playlist_tracks"
|
||||
p.registerModel(&model.PlaylistTrack{}, map[string]filterFunc{
|
||||
"missing": booleanFilter,
|
||||
"missing": booleanFilter,
|
||||
"library_id": libraryIdFilter,
|
||||
})
|
||||
p.setSortMappings(
|
||||
map[string]string{
|
||||
|
|
|
|||
|
|
@ -199,9 +199,14 @@ func (r sqlRepository) applyFilters(sq SelectBuilder, options ...model.QueryOpti
|
|||
return sq
|
||||
}
|
||||
|
||||
// libraryIdFilter is a filter function to be added to resources that have a library_id column.
|
||||
func libraryIdFilter(_ string, value interface{}) Sqlizer {
|
||||
return Eq{"library_id": value}
|
||||
}
|
||||
|
||||
// applyLibraryFilter adds library filtering to queries for tables that have a library_id column
|
||||
// This ensures users only see content from libraries they have access to
|
||||
func (r sqlRepository) applyLibraryFilter(sq SelectBuilder) SelectBuilder {
|
||||
func (r sqlRepository) applyLibraryFilter(sq SelectBuilder, tableName ...string) SelectBuilder {
|
||||
user := loggedUser(r.ctx)
|
||||
|
||||
// Admin users see all content
|
||||
|
|
@ -216,9 +221,14 @@ func (r sqlRepository) applyLibraryFilter(sq SelectBuilder) SelectBuilder {
|
|||
return sq.Where(Eq{"1": "0"})
|
||||
}
|
||||
|
||||
table := r.tableName
|
||||
if len(tableName) > 0 {
|
||||
table = tableName[0]
|
||||
}
|
||||
|
||||
// Use subquery to filter by user's library access
|
||||
// This approach doesn't require DataStore in context
|
||||
return sq.Where(Expr(r.tableName+".library_id IN ("+
|
||||
return sq.Where(Expr(table+".library_id IN ("+
|
||||
"SELECT ul.library_id FROM user_library ul WHERE ul.user_id = ?)", userID))
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -18,40 +18,48 @@ const getSelectedLibraries = () => {
|
|||
}
|
||||
}
|
||||
|
||||
// Function to apply library filtering to appropriate resources
|
||||
const applyLibraryFilter = (resource, params) => {
|
||||
// Content resources that should be filtered by selected libraries
|
||||
const filteredResources = ['album', 'song', 'playlistTrack']
|
||||
|
||||
// Get selected libraries from localStorage
|
||||
const selectedLibraries = getSelectedLibraries()
|
||||
|
||||
// Add library filter for content resources if libraries are selected
|
||||
if (filteredResources.includes(resource) && selectedLibraries.length > 0) {
|
||||
if (!params.filter) {
|
||||
params.filter = {}
|
||||
}
|
||||
params.filter.library_id = selectedLibraries
|
||||
}
|
||||
|
||||
return params
|
||||
}
|
||||
|
||||
const mapResource = (resource, params) => {
|
||||
switch (resource) {
|
||||
// /api/playlistTrack?playlist_id=123 => /api/playlist/123/tracks
|
||||
case 'playlistTrack': {
|
||||
// /api/playlistTrack?playlist_id=123 => /api/playlist/123/tracks
|
||||
params.filter = params.filter || {}
|
||||
|
||||
let plsId = '0'
|
||||
if (params.filter) {
|
||||
plsId = params.filter.playlist_id
|
||||
if (!isAdmin()) {
|
||||
params.filter.missing = false
|
||||
}
|
||||
plsId = params.filter.playlist_id
|
||||
if (!isAdmin()) {
|
||||
params.filter.missing = false
|
||||
}
|
||||
params = applyLibraryFilter(resource, params)
|
||||
|
||||
return [`playlist/${plsId}/tracks`, params]
|
||||
}
|
||||
case 'album':
|
||||
case 'song':
|
||||
case 'artist':
|
||||
case 'playlist': {
|
||||
// Content resources that should be filtered by selected libraries
|
||||
const contentResources = ['album', 'song', 'artist', 'playlist']
|
||||
|
||||
// Get selected libraries from localStorage
|
||||
const selectedLibraries = getSelectedLibraries()
|
||||
|
||||
if (params.filter && !isAdmin()) {
|
||||
case 'artist': {
|
||||
params.filter = params.filter || {}
|
||||
if (!isAdmin()) {
|
||||
params.filter.missing = false
|
||||
}
|
||||
|
||||
// Add library filter for content resources if libraries are selected
|
||||
if (contentResources.includes(resource) && selectedLibraries.length > 0) {
|
||||
if (!params.filter) {
|
||||
params.filter = {}
|
||||
}
|
||||
params.filter.library_id = selectedLibraries
|
||||
}
|
||||
params = applyLibraryFilter(resource, params)
|
||||
|
||||
return [resource, params]
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue