feat(M002): smart playlists — rule engine, editor UI, sidebar integration
Recovered from orphaned worktree commits (complete-milestone failed to merge). Backend: - Migration 9: is_smart + smart_rules columns on playlists table - smartplaylist package: parameterized WHERE clause builder, field whitelist, genre subquery - playlist.Service: Create/Update/Evaluate/Preview/GetRules smart playlist methods - 65 tests (49 rule engine + 15 service + 1 migration) Frontend: - yj-combobox: reusable typeable dropdown with keyboard nav, ARIA, blur-race fix - smart-playlist-editor: row-based rule builder with live preview - smart-playlist-details: evaluate, refresh, play, shuffle, edit rules - Sidebar: filter icon, Smart badge, create button, routing - Queue snapshot on play/shuffle
This commit is contained in:
@@ -332,6 +332,15 @@ func runMigrations(
|
||||
}
|
||||
}
|
||||
|
||||
// Migration 9: add smart playlist columns to playlists.
|
||||
if version < 9 {
|
||||
if err := migration9SmartPlaylists(
|
||||
ctx, db, logger,
|
||||
); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -1154,6 +1163,54 @@ func migration8ContentlessDelete(
|
||||
return nil
|
||||
}
|
||||
|
||||
// migration9SmartPlaylists adds the is_smart and smart_rules
|
||||
// columns to the playlists table for smart playlist support.
|
||||
func migration9SmartPlaylists(
|
||||
ctx context.Context,
|
||||
db *sql.DB,
|
||||
logger *slog.Logger,
|
||||
) error {
|
||||
logger.Info(
|
||||
"applying migration 9: smart playlist columns",
|
||||
)
|
||||
|
||||
if _, err := db.ExecContext(ctx,
|
||||
`ALTER TABLE playlists
|
||||
ADD COLUMN is_smart INTEGER NOT NULL DEFAULT 0`,
|
||||
); err != nil {
|
||||
if !isDuplicateColumnErr(err) {
|
||||
return fmt.Errorf(
|
||||
"migration 9: could not add is_smart column: %w",
|
||||
err,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
if _, err := db.ExecContext(ctx,
|
||||
`ALTER TABLE playlists
|
||||
ADD COLUMN smart_rules TEXT`,
|
||||
); err != nil {
|
||||
if !isDuplicateColumnErr(err) {
|
||||
return fmt.Errorf(
|
||||
"migration 9: could not add smart_rules column: %w",
|
||||
err,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
if _, err := db.ExecContext(
|
||||
ctx, "PRAGMA user_version = 9",
|
||||
); err != nil {
|
||||
return fmt.Errorf(
|
||||
"could not set user_version to 9: %w", err,
|
||||
)
|
||||
}
|
||||
|
||||
logger.Info("migration 9 complete")
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// readLibraryDirFromTOML reads the TOML config file and returns
|
||||
// the Library.DirectoryPath value, or "" if not configured.
|
||||
func readLibraryDirFromTOML(logger *slog.Logger) string {
|
||||
|
||||
Reference in New Issue
Block a user