mirror of
https://github.com/navidrome/navidrome.git
synced 2025-07-17 08:52:01 +03:00
Implemented comprehensive library filtering for tag repositories to support the multi-library feature. This change ensures that users only see tags from libraries they have access to, while admin users can see all tags. Key changes: - Enhanced TagRepository.Add() method to accept libraryID parameter for proper library association - Updated baseTagRepository to implement library-aware queries with proper joins - Added library_tag table integration for per-library tag statistics - Implemented user permission-based filtering through user_library associations - Added comprehensive test coverage for library filtering scenarios - Updated UI data provider to include tag filtering by selected libraries - Modified scanner to pass library ID when adding tags during folder processing The implementation maintains backward compatibility while providing proper isolation between libraries for tag-based operations like genres and other metadata tags.
120 lines
4.3 KiB
Go
120 lines
4.3 KiB
Go
package migrations
|
|
|
|
import (
|
|
"context"
|
|
"database/sql"
|
|
|
|
"github.com/pressly/goose/v3"
|
|
)
|
|
|
|
func init() {
|
|
goose.AddMigrationContext(upAddMultiLibrarySupport, downAddMultiLibrarySupport)
|
|
}
|
|
|
|
func upAddMultiLibrarySupport(ctx context.Context, tx *sql.Tx) error {
|
|
_, err := tx.ExecContext(ctx, `
|
|
-- Create user_library association table
|
|
CREATE TABLE user_library (
|
|
user_id VARCHAR(255) NOT NULL,
|
|
library_id INTEGER NOT NULL,
|
|
PRIMARY KEY (user_id, library_id),
|
|
FOREIGN KEY (user_id) REFERENCES user(id) ON DELETE CASCADE,
|
|
FOREIGN KEY (library_id) REFERENCES library(id) ON DELETE CASCADE
|
|
);
|
|
-- Create indexes for performance
|
|
CREATE INDEX idx_user_library_user_id ON user_library(user_id);
|
|
CREATE INDEX idx_user_library_library_id ON user_library(library_id);
|
|
|
|
-- Populate with existing users having access to library ID 1 (existing setup)
|
|
-- Admin users get access to all libraries, regular users get access to library 1
|
|
INSERT INTO user_library (user_id, library_id)
|
|
SELECT u.id, 1
|
|
FROM user u;
|
|
|
|
-- Add total_duration column to library table
|
|
ALTER TABLE library ADD COLUMN total_duration real DEFAULT 0;
|
|
UPDATE library SET total_duration = (
|
|
SELECT IFNULL(SUM(duration),0) from album where album.library_id = library.id and missing = 0
|
|
);
|
|
|
|
-- Add default_new_users column to library table
|
|
ALTER TABLE library ADD COLUMN default_new_users boolean DEFAULT false;
|
|
-- Set library ID 1 (default library) as default for new users
|
|
UPDATE library SET default_new_users = true WHERE id = 1;
|
|
|
|
-- Add stats column to library_artist junction table for per-library artist statistics
|
|
ALTER TABLE library_artist ADD COLUMN stats text DEFAULT '{}';
|
|
|
|
-- Migrate existing global artist stats to per-library format in library_artist table
|
|
-- For each library_artist association, copy the artist's global stats
|
|
UPDATE library_artist
|
|
SET stats = (
|
|
SELECT COALESCE(artist.stats, '{}')
|
|
FROM artist
|
|
WHERE artist.id = library_artist.artist_id
|
|
);
|
|
|
|
-- Remove stats column from artist table to eliminate duplication
|
|
-- Stats are now stored per-library in library_artist table
|
|
ALTER TABLE artist DROP COLUMN stats;
|
|
|
|
-- Create library_tag table for per-library tag statistics
|
|
CREATE TABLE library_tag (
|
|
tag_id VARCHAR NOT NULL,
|
|
library_id INTEGER NOT NULL,
|
|
album_count INTEGER DEFAULT 0 NOT NULL,
|
|
media_file_count INTEGER DEFAULT 0 NOT NULL,
|
|
PRIMARY KEY (tag_id, library_id),
|
|
FOREIGN KEY (tag_id) REFERENCES tag(id) ON DELETE CASCADE,
|
|
FOREIGN KEY (library_id) REFERENCES library(id) ON DELETE CASCADE
|
|
);
|
|
|
|
-- Create indexes for optimal query performance
|
|
CREATE INDEX idx_library_tag_tag_id ON library_tag(tag_id);
|
|
CREATE INDEX idx_library_tag_library_id ON library_tag(library_id);
|
|
|
|
-- Migrate existing tag stats to per-library format in library_tag table
|
|
-- For existing installations, copy current global stats to library ID 1 (default library)
|
|
INSERT INTO library_tag (tag_id, library_id, album_count, media_file_count)
|
|
SELECT t.id, 1, t.album_count, t.media_file_count
|
|
FROM tag t
|
|
WHERE EXISTS (SELECT 1 FROM library WHERE id = 1);
|
|
|
|
-- Remove global stats from tag table as they are now per-library
|
|
ALTER TABLE tag DROP COLUMN album_count;
|
|
ALTER TABLE tag DROP COLUMN media_file_count;
|
|
`)
|
|
|
|
return err
|
|
}
|
|
|
|
func downAddMultiLibrarySupport(ctx context.Context, tx *sql.Tx) error {
|
|
_, err := tx.ExecContext(ctx, `
|
|
-- Restore stats column to artist table before removing from library_artist
|
|
ALTER TABLE artist ADD COLUMN stats text DEFAULT '{}';
|
|
|
|
-- Restore global stats by aggregating from library_artist (simplified approach)
|
|
-- In a real rollback scenario, this might need more sophisticated logic
|
|
UPDATE artist
|
|
SET stats = (
|
|
SELECT COALESCE(la.stats, '{}')
|
|
FROM library_artist la
|
|
WHERE la.artist_id = artist.id
|
|
LIMIT 1
|
|
);
|
|
|
|
ALTER TABLE library_artist DROP COLUMN IF EXISTS stats;
|
|
DROP INDEX IF EXISTS idx_user_library_library_id;
|
|
DROP INDEX IF EXISTS idx_user_library_user_id;
|
|
DROP TABLE IF EXISTS user_library;
|
|
ALTER TABLE library DROP COLUMN IF EXISTS total_duration;
|
|
ALTER TABLE library DROP COLUMN IF EXISTS default_new_users;
|
|
|
|
-- Drop library_tag table and its indexes
|
|
DROP INDEX IF EXISTS idx_library_tag_library_id;
|
|
DROP INDEX IF EXISTS idx_library_tag_tag_id;
|
|
DROP TABLE IF EXISTS library_tag;
|
|
`)
|
|
return err
|
|
}
|