mirror of
https://github.com/navidrome/navidrome.git
synced 2026-05-03 06:51:16 +00:00
refactor: simplify and improve playlist provider and matcher code
Eliminate redundant work and minor issues found during code review: - Replace manual PlaylistTrack construction in syncPlaylist with the existing Playlist.AddMediaFiles helper, removing duplicated logic - Pre-sanitize track fields once per artist batch in the matcher's fuzzy matching loop, avoiding redundant sanitization in both findBestMatch and computeSpecificityLevel on every iteration - Cache resolved usernames in discoverAndSync to avoid N+1 DB lookups when multiple playlists share the same owner - Use the local loadedPlugin variable instead of reading m.plugins[p.ID] after releasing the lock in loadPluginWithConfig - Fix misleading uint32 comparison (<=0 to ==0) in durationProximity - Update stale comment on checkTracksEditable to mention plugin playlists
This commit is contained in:
parent
9053a4ffe9
commit
73203eeef0
@ -212,7 +212,7 @@ func (s *playlists) checkWritable(ctx context.Context, id string) (*model.Playli
|
|||||||
return pls, nil
|
return pls, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// checkTracksEditable verifies the user can modify tracks (ownership + not smart playlist).
|
// checkTracksEditable verifies the user can modify tracks (ownership + not smart/plugin playlist).
|
||||||
func (s *playlists) checkTracksEditable(ctx context.Context, playlistID string) (*model.Playlist, error) {
|
func (s *playlists) checkTracksEditable(ctx context.Context, playlistID string) (*model.Playlist, error) {
|
||||||
pls, err := s.checkWritable(ctx, playlistID)
|
pls, err := s.checkWritable(ctx, playlistID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
@ -390,7 +390,7 @@ func (m *Manager) loadPluginWithConfig(p *model.Plugin) error {
|
|||||||
m.mu.Unlock()
|
m.mu.Unlock()
|
||||||
|
|
||||||
// Call plugin init function
|
// Call plugin init function
|
||||||
callPluginInit(ctx, m.plugins[p.ID])
|
callPluginInit(ctx, loadedPlugin)
|
||||||
|
|
||||||
// Start PlaylistProvider syncer if capability is detected
|
// Start PlaylistProvider syncer if capability is detected
|
||||||
if hasCapability(loadedPlugin.capabilities, CapabilityPlaylistProvider) {
|
if hasCapability(loadedPlugin.capabilities, CapabilityPlaylistProvider) {
|
||||||
|
|||||||
@ -2,7 +2,6 @@ package plugins
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
|
||||||
"slices"
|
"slices"
|
||||||
"strings"
|
"strings"
|
||||||
"sync/atomic"
|
"sync/atomic"
|
||||||
@ -125,15 +124,20 @@ func (o *playlistSyncer) discoverAndSync() {
|
|||||||
o.retryInterval.Store(int64(time.Duration(resp.RetryInterval) * time.Second))
|
o.retryInterval.Store(int64(time.Duration(resp.RetryInterval) * time.Second))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
resolvedUsers := map[string]string{} // username -> userID cache
|
||||||
for _, info := range resp.Playlists {
|
for _, info := range resp.Playlists {
|
||||||
// Resolve username to user ID
|
// Resolve username to user ID (cached)
|
||||||
user, err := o.ds.User(adminContext(ctx)).FindByUsername(info.OwnerUsername)
|
ownerID, ok := resolvedUsers[info.OwnerUsername]
|
||||||
if err != nil {
|
if !ok {
|
||||||
log.Error(ctx, "Failed to resolve playlist owner", "plugin", o.pluginName,
|
user, err := o.ds.User(adminContext(ctx)).FindByUsername(info.OwnerUsername)
|
||||||
"playlistID", info.ID, "username", info.OwnerUsername, err)
|
if err != nil {
|
||||||
continue
|
log.Error(ctx, "Failed to resolve playlist owner", "plugin", o.pluginName,
|
||||||
|
"playlistID", info.ID, "username", info.OwnerUsername, err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
ownerID = user.ID
|
||||||
|
resolvedUsers[info.OwnerUsername] = ownerID
|
||||||
}
|
}
|
||||||
ownerID := user.ID
|
|
||||||
|
|
||||||
// Validate that the plugin is permitted to create playlists for this user
|
// Validate that the plugin is permitted to create playlists for this user
|
||||||
if !o.plugin.allUsers && !slices.Contains(o.plugin.allowedUserIDs, ownerID) {
|
if !o.plugin.allUsers && !slices.Contains(o.plugin.allowedUserIDs, ownerID) {
|
||||||
@ -198,16 +202,7 @@ func (o *playlistSyncer) syncPlaylist(info capabilities.PlaylistInfo, dbID strin
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Set tracks from matched media files
|
// Set tracks from matched media files
|
||||||
tracks := make(model.PlaylistTracks, len(matched))
|
pls.AddMediaFiles(matched)
|
||||||
for i, mf := range matched {
|
|
||||||
tracks[i] = model.PlaylistTrack{
|
|
||||||
ID: fmt.Sprintf("%d", i+1),
|
|
||||||
MediaFileID: mf.ID,
|
|
||||||
PlaylistID: dbID,
|
|
||||||
MediaFile: mf,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
pls.SetTracks(tracks)
|
|
||||||
|
|
||||||
// Upsert via repository
|
// Upsert via repository
|
||||||
plsRepo := o.ds.Playlist(ctx)
|
plsRepo := o.ds.Playlist(ctx)
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user