feat: the library's folders are set in Admin › Library, and changing a folder moves its tracks with it, keeping their plays, likes and playlists (#6129)
release / govulncheck (push) Successful in 15s
release / web (push) Successful in 1m20s
release / go (push) Failing after 1m37s
release / integration (push) Failing after 4m56s
release / android (push) Successful in 5m1s
release / Build signed APK (releases and dev) (push) Successful in 5m13s
release / Attach APK to the Release (tag releases only) (push) Skipped
release / Build + push container image (push) Skipped
release / Verify release artifacts (tag releases only) (push) Skipped
release / govulncheck (push) Successful in 15s
release / web (push) Successful in 1m20s
release / go (push) Failing after 1m37s
release / integration (push) Failing after 4m56s
release / android (push) Successful in 5m1s
release / Build signed APK (releases and dev) (push) Successful in 5m13s
release / Attach APK to the Release (tag releases only) (push) Skipped
release / Build + push container image (push) Skipped
release / Verify release artifacts (tag releases only) (push) Skipped
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,162 @@
|
||||
// Code generated by sqlc. DO NOT EDIT.
|
||||
// versions:
|
||||
// sqlc v1.31.1
|
||||
// source: library_settings.sql
|
||||
|
||||
package dbq
|
||||
|
||||
import (
|
||||
"context"
|
||||
)
|
||||
|
||||
const countTracksUnder = `-- name: CountTracksUnder :one
|
||||
SELECT count(*) FROM tracks WHERE starts_with(file_path, $1::text || '/')
|
||||
`
|
||||
|
||||
func (q *Queries) CountTracksUnder(ctx context.Context, root string) (int64, error) {
|
||||
row := q.db.QueryRow(ctx, countTracksUnder, root)
|
||||
var count int64
|
||||
err := row.Scan(&count)
|
||||
return count, err
|
||||
}
|
||||
|
||||
const getLibrarySettings = `-- name: GetLibrarySettings :one
|
||||
SELECT id, scan_paths, updated_at FROM library_settings WHERE id = true
|
||||
`
|
||||
|
||||
func (q *Queries) GetLibrarySettings(ctx context.Context) (LibrarySetting, error) {
|
||||
row := q.db.QueryRow(ctx, getLibrarySettings)
|
||||
var i LibrarySetting
|
||||
err := row.Scan(&i.ID, &i.ScanPaths, &i.UpdatedAt)
|
||||
return i, err
|
||||
}
|
||||
|
||||
const rerootAlbumCoverPaths = `-- name: RerootAlbumCoverPaths :execrows
|
||||
UPDATE albums
|
||||
SET cover_art_path = $1::text || substr(cover_art_path, length($2::text) + 1)
|
||||
WHERE starts_with(cover_art_path, $2::text || '/')
|
||||
`
|
||||
|
||||
type RerootAlbumCoverPathsParams struct {
|
||||
NewRoot string
|
||||
OldRoot string
|
||||
}
|
||||
|
||||
func (q *Queries) RerootAlbumCoverPaths(ctx context.Context, arg RerootAlbumCoverPathsParams) (int64, error) {
|
||||
result, err := q.db.Exec(ctx, rerootAlbumCoverPaths, arg.NewRoot, arg.OldRoot)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return result.RowsAffected(), nil
|
||||
}
|
||||
|
||||
const rerootGrabDestinations = `-- name: RerootGrabDestinations :execrows
|
||||
UPDATE grabs
|
||||
SET destination = $1::text || substr(destination, length($2::text) + 1)
|
||||
WHERE destination = $2::text
|
||||
OR starts_with(destination, $2::text || '/')
|
||||
`
|
||||
|
||||
type RerootGrabDestinationsParams struct {
|
||||
NewRoot string
|
||||
OldRoot string
|
||||
}
|
||||
|
||||
func (q *Queries) RerootGrabDestinations(ctx context.Context, arg RerootGrabDestinationsParams) (int64, error) {
|
||||
result, err := q.db.Exec(ctx, rerootGrabDestinations, arg.NewRoot, arg.OldRoot)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return result.RowsAffected(), nil
|
||||
}
|
||||
|
||||
const rerootImportRoot = `-- name: RerootImportRoot :execrows
|
||||
UPDATE acquisition_settings
|
||||
SET import_root = $1::text
|
||||
WHERE import_root = $2::text
|
||||
`
|
||||
|
||||
type RerootImportRootParams struct {
|
||||
NewRoot string
|
||||
OldRoot string
|
||||
}
|
||||
|
||||
func (q *Queries) RerootImportRoot(ctx context.Context, arg RerootImportRootParams) (int64, error) {
|
||||
result, err := q.db.Exec(ctx, rerootImportRoot, arg.NewRoot, arg.OldRoot)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return result.RowsAffected(), nil
|
||||
}
|
||||
|
||||
const rerootTrackPaths = `-- name: RerootTrackPaths :execrows
|
||||
|
||||
UPDATE tracks
|
||||
SET file_path = $1::text || substr(file_path, length($2::text) + 1)
|
||||
WHERE starts_with(file_path, $2::text || '/')
|
||||
`
|
||||
|
||||
type RerootTrackPathsParams struct {
|
||||
NewRoot string
|
||||
OldRoot string
|
||||
}
|
||||
|
||||
// A library folder that moved (#6129): every path under old_root is rewritten
|
||||
// to the same place under new_root, so the tracks keep their ids and with them
|
||||
// their plays, likes and playlists. updated_at is left alone on purpose: the
|
||||
// scan compares it with each file's mtime, and the files have not changed.
|
||||
func (q *Queries) RerootTrackPaths(ctx context.Context, arg RerootTrackPathsParams) (int64, error) {
|
||||
result, err := q.db.Exec(ctx, rerootTrackPaths, arg.NewRoot, arg.OldRoot)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return result.RowsAffected(), nil
|
||||
}
|
||||
|
||||
const sampleTrackPathsUnder = `-- name: SampleTrackPathsUnder :many
|
||||
SELECT file_path FROM tracks
|
||||
WHERE starts_with(file_path, $1::text || '/') AND missing_since IS NULL
|
||||
ORDER BY random()
|
||||
LIMIT $2::int
|
||||
`
|
||||
|
||||
type SampleTrackPathsUnderParams struct {
|
||||
Root string
|
||||
N int32
|
||||
}
|
||||
|
||||
// A few present tracks under a folder, to check a move before rewriting it.
|
||||
func (q *Queries) SampleTrackPathsUnder(ctx context.Context, arg SampleTrackPathsUnderParams) ([]string, error) {
|
||||
rows, err := q.db.Query(ctx, sampleTrackPathsUnder, arg.Root, arg.N)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
var items []string
|
||||
for rows.Next() {
|
||||
var file_path string
|
||||
if err := rows.Scan(&file_path); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
items = append(items, file_path)
|
||||
}
|
||||
if err := rows.Err(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return items, nil
|
||||
}
|
||||
|
||||
const updateLibraryScanPaths = `-- name: UpdateLibraryScanPaths :one
|
||||
UPDATE library_settings
|
||||
SET scan_paths = $1::text[],
|
||||
updated_at = now()
|
||||
WHERE id = true
|
||||
RETURNING id, scan_paths, updated_at
|
||||
`
|
||||
|
||||
func (q *Queries) UpdateLibraryScanPaths(ctx context.Context, scanPaths []string) (LibrarySetting, error) {
|
||||
row := q.db.QueryRow(ctx, updateLibraryScanPaths, scanPaths)
|
||||
var i LibrarySetting
|
||||
err := row.Scan(&i.ID, &i.ScanPaths, &i.UpdatedAt)
|
||||
return i, err
|
||||
}
|
||||
@@ -506,6 +506,12 @@ type LibraryChange struct {
|
||||
ChangedAt pgtype.Timestamptz
|
||||
}
|
||||
|
||||
type LibrarySetting struct {
|
||||
ID bool
|
||||
ScanPaths []string
|
||||
UpdatedAt pgtype.Timestamptz
|
||||
}
|
||||
|
||||
type ListenbrainzSimilarRecording struct {
|
||||
SeedTrackID pgtype.UUID
|
||||
RecordingMbid string
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
DROP TABLE IF EXISTS library_settings;
|
||||
@@ -0,0 +1,11 @@
|
||||
-- The library's folders, set in Settings (#6129). Until someone saves them
|
||||
-- there, scan_paths is NULL and MINSTREL_LIBRARY_SCAN_PATHS names the folders,
|
||||
-- as it always has; from the first save the row is what the server reads.
|
||||
CREATE TABLE library_settings (
|
||||
id boolean PRIMARY KEY DEFAULT true,
|
||||
scan_paths text[],
|
||||
updated_at timestamptz NOT NULL DEFAULT now(),
|
||||
|
||||
CONSTRAINT library_settings_singleton CHECK (id = true)
|
||||
);
|
||||
INSERT INTO library_settings (id) VALUES (true) ON CONFLICT (id) DO NOTHING;
|
||||
@@ -0,0 +1,45 @@
|
||||
-- name: GetLibrarySettings :one
|
||||
SELECT * FROM library_settings WHERE id = true;
|
||||
|
||||
-- name: UpdateLibraryScanPaths :one
|
||||
UPDATE library_settings
|
||||
SET scan_paths = sqlc.arg(scan_paths)::text[],
|
||||
updated_at = now()
|
||||
WHERE id = true
|
||||
RETURNING *;
|
||||
|
||||
-- A library folder that moved (#6129): every path under old_root is rewritten
|
||||
-- to the same place under new_root, so the tracks keep their ids and with them
|
||||
-- their plays, likes and playlists. updated_at is left alone on purpose: the
|
||||
-- scan compares it with each file's mtime, and the files have not changed.
|
||||
|
||||
-- name: RerootTrackPaths :execrows
|
||||
UPDATE tracks
|
||||
SET file_path = sqlc.arg(new_root)::text || substr(file_path, length(sqlc.arg(old_root)::text) + 1)
|
||||
WHERE starts_with(file_path, sqlc.arg(old_root)::text || '/');
|
||||
|
||||
-- name: RerootAlbumCoverPaths :execrows
|
||||
UPDATE albums
|
||||
SET cover_art_path = sqlc.arg(new_root)::text || substr(cover_art_path, length(sqlc.arg(old_root)::text) + 1)
|
||||
WHERE starts_with(cover_art_path, sqlc.arg(old_root)::text || '/');
|
||||
|
||||
-- name: RerootGrabDestinations :execrows
|
||||
UPDATE grabs
|
||||
SET destination = sqlc.arg(new_root)::text || substr(destination, length(sqlc.arg(old_root)::text) + 1)
|
||||
WHERE destination = sqlc.arg(old_root)::text
|
||||
OR starts_with(destination, sqlc.arg(old_root)::text || '/');
|
||||
|
||||
-- name: RerootImportRoot :execrows
|
||||
UPDATE acquisition_settings
|
||||
SET import_root = sqlc.arg(new_root)::text
|
||||
WHERE import_root = sqlc.arg(old_root)::text;
|
||||
|
||||
-- name: SampleTrackPathsUnder :many
|
||||
-- A few present tracks under a folder, to check a move before rewriting it.
|
||||
SELECT file_path FROM tracks
|
||||
WHERE starts_with(file_path, sqlc.arg(root)::text || '/') AND missing_since IS NULL
|
||||
ORDER BY random()
|
||||
LIMIT sqlc.arg(n)::int;
|
||||
|
||||
-- name: CountTracksUnder :one
|
||||
SELECT count(*) FROM tracks WHERE starts_with(file_path, sqlc.arg(root)::text || '/');
|
||||
Reference in New Issue
Block a user