Foundation: config, schema/migrations, auth, stable IDs, metadata probing
This commit is contained in:
@@ -0,0 +1,125 @@
|
||||
package database
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"errors"
|
||||
"time"
|
||||
|
||||
"git.noroutes.net/NoRoutes/vibetunes/internal/idgen"
|
||||
)
|
||||
|
||||
type Playlist struct {
|
||||
ID string
|
||||
OwnerID string
|
||||
Name string
|
||||
Description string
|
||||
IsPublic bool
|
||||
CreatedAt time.Time
|
||||
UpdatedAt time.Time
|
||||
}
|
||||
|
||||
func CreatePlaylist(db *sql.DB, ownerID, name, description string, isPublic bool) (*Playlist, error) {
|
||||
id := idgen.New("pl")
|
||||
_, err := db.Exec(`INSERT INTO playlists (id, owner_id, name, description, is_public) VALUES (?, ?, ?, ?, ?)`,
|
||||
id, ownerID, name, description, boolInt(isPublic))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return GetPlaylist(db, id)
|
||||
}
|
||||
|
||||
const playlistSelect = `SELECT id, owner_id, name, description, is_public, created_at, updated_at FROM playlists`
|
||||
|
||||
func GetPlaylist(db *sql.DB, id string) (*Playlist, error) {
|
||||
return scanPlaylist(db.QueryRow(playlistSelect+` WHERE id = ?`, id))
|
||||
}
|
||||
|
||||
func scanPlaylist(row *sql.Row) (*Playlist, error) {
|
||||
var p Playlist
|
||||
var isPublic int
|
||||
var createdAt, updatedAt string
|
||||
err := row.Scan(&p.ID, &p.OwnerID, &p.Name, &p.Description, &isPublic, &createdAt, &updatedAt)
|
||||
if errors.Is(err, sql.ErrNoRows) {
|
||||
return nil, ErrNotFound
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
p.IsPublic = isPublic != 0
|
||||
p.CreatedAt, _ = parseTime(createdAt)
|
||||
p.UpdatedAt, _ = parseTime(updatedAt)
|
||||
return &p, nil
|
||||
}
|
||||
|
||||
// ListPlaylistsVisibleToUser returns playlists owned by the user plus any
|
||||
// public playlists owned by others.
|
||||
func ListPlaylistsVisibleToUser(db *sql.DB, userID string) ([]*Playlist, error) {
|
||||
rows, err := db.Query(playlistSelect+` WHERE owner_id = ? OR is_public = 1 ORDER BY name COLLATE NOCASE`, userID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
var out []*Playlist
|
||||
for rows.Next() {
|
||||
var p Playlist
|
||||
var isPublic int
|
||||
var createdAt, updatedAt string
|
||||
if err := rows.Scan(&p.ID, &p.OwnerID, &p.Name, &p.Description, &isPublic, &createdAt, &updatedAt); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
p.IsPublic = isPublic != 0
|
||||
p.CreatedAt, _ = parseTime(createdAt)
|
||||
p.UpdatedAt, _ = parseTime(updatedAt)
|
||||
out = append(out, &p)
|
||||
}
|
||||
return out, rows.Err()
|
||||
}
|
||||
|
||||
func DeletePlaylist(db *sql.DB, id string) error {
|
||||
_, err := db.Exec(`DELETE FROM playlists WHERE id = ?`, id)
|
||||
return err
|
||||
}
|
||||
|
||||
func RenamePlaylist(db *sql.DB, id, name, description string, isPublic bool) error {
|
||||
_, err := db.Exec(`UPDATE playlists SET name=?, description=?, is_public=?, updated_at=strftime('%Y-%m-%dT%H:%M:%fZ','now') WHERE id=?`,
|
||||
name, description, boolInt(isPublic), id)
|
||||
return err
|
||||
}
|
||||
|
||||
type PlaylistEntry struct {
|
||||
ID string
|
||||
SongID string
|
||||
Position int
|
||||
}
|
||||
|
||||
func ListPlaylistEntries(db *sql.DB, playlistID string) ([]*PlaylistEntry, error) {
|
||||
rows, err := db.Query(`SELECT id, song_id, position FROM playlist_entries WHERE playlist_id = ? ORDER BY position`, playlistID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
var out []*PlaylistEntry
|
||||
for rows.Next() {
|
||||
var e PlaylistEntry
|
||||
if err := rows.Scan(&e.ID, &e.SongID, &e.Position); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
out = append(out, &e)
|
||||
}
|
||||
return out, rows.Err()
|
||||
}
|
||||
|
||||
// AppendPlaylistEntry adds a song at the end of the playlist.
|
||||
func AppendPlaylistEntry(db *sql.DB, playlistID, songID string) error {
|
||||
var next int
|
||||
if err := db.QueryRow(`SELECT COALESCE(MAX(position), -1) + 1 FROM playlist_entries WHERE playlist_id = ?`, playlistID).Scan(&next); err != nil {
|
||||
return err
|
||||
}
|
||||
_, err := db.Exec(`INSERT INTO playlist_entries (id, playlist_id, song_id, position) VALUES (?, ?, ?, ?)`, idgen.New("pe"), playlistID, songID, next)
|
||||
return err
|
||||
}
|
||||
|
||||
func RemovePlaylistEntry(db *sql.DB, entryID string) error {
|
||||
_, err := db.Exec(`DELETE FROM playlist_entries WHERE id = ?`, entryID)
|
||||
return err
|
||||
}
|
||||
Reference in New Issue
Block a user