Subsonic: internet radio, chat, bookmarks, play queue, scanner status endpoints
This commit is contained in:
@@ -144,6 +144,8 @@ type Bookmark struct {
|
||||
SongID string
|
||||
PositionMs int
|
||||
Comment string
|
||||
CreatedAt time.Time
|
||||
UpdatedAt time.Time
|
||||
}
|
||||
|
||||
func SetBookmark(db *sql.DB, userID, songID string, positionMs int, comment string) error {
|
||||
@@ -159,7 +161,7 @@ func DeleteBookmark(db *sql.DB, userID, songID string) error {
|
||||
}
|
||||
|
||||
func ListBookmarks(db *sql.DB, userID string) ([]*Bookmark, error) {
|
||||
rows, err := db.Query(`SELECT id, song_id, position_ms, comment FROM bookmarks WHERE user_id = ? ORDER BY updated_at DESC`, userID)
|
||||
rows, err := db.Query(`SELECT id, song_id, position_ms, comment, created_at, updated_at FROM bookmarks WHERE user_id = ? ORDER BY updated_at DESC`, userID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -167,9 +169,12 @@ func ListBookmarks(db *sql.DB, userID string) ([]*Bookmark, error) {
|
||||
var out []*Bookmark
|
||||
for rows.Next() {
|
||||
var b Bookmark
|
||||
if err := rows.Scan(&b.ID, &b.SongID, &b.PositionMs, &b.Comment); err != nil {
|
||||
var createdAt, updatedAt string
|
||||
if err := rows.Scan(&b.ID, &b.SongID, &b.PositionMs, &b.Comment, &createdAt, &updatedAt); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
b.CreatedAt, _ = parseTime(createdAt)
|
||||
b.UpdatedAt, _ = parseTime(updatedAt)
|
||||
out = append(out, &b)
|
||||
}
|
||||
return out, rows.Err()
|
||||
|
||||
@@ -0,0 +1,43 @@
|
||||
package database
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"time"
|
||||
|
||||
"git.noroutes.net/NoRoutes/vibetunes/internal/idgen"
|
||||
)
|
||||
|
||||
type ChatMessage struct {
|
||||
Username string
|
||||
Message string
|
||||
CreatedAt time.Time
|
||||
}
|
||||
|
||||
func AddChatMessage(db *sql.DB, userID, message string) error {
|
||||
_, err := db.Exec(`INSERT INTO chat_messages (id, user_id, message) VALUES (?, ?, ?)`, idgen.New("cm"), userID, message)
|
||||
return err
|
||||
}
|
||||
|
||||
// ListChatMessagesSince returns chat messages newer than since (pass the
|
||||
// zero time for all messages), oldest first, capped at 500 — a chat
|
||||
// room does not need unbounded history in one response.
|
||||
func ListChatMessagesSince(db *sql.DB, since time.Time) ([]ChatMessage, error) {
|
||||
rows, err := db.Query(`SELECT u.username, c.message, c.created_at FROM chat_messages c
|
||||
JOIN users u ON u.id = c.user_id
|
||||
WHERE c.created_at > ? ORDER BY c.created_at ASC LIMIT 500`, since.UTC().Format(timeLayout))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
var out []ChatMessage
|
||||
for rows.Next() {
|
||||
var m ChatMessage
|
||||
var createdAt string
|
||||
if err := rows.Scan(&m.Username, &m.Message, &createdAt); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
m.CreatedAt, _ = parseTime(createdAt)
|
||||
out = append(out, m)
|
||||
}
|
||||
return out, rows.Err()
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
package database
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"errors"
|
||||
|
||||
"git.noroutes.net/NoRoutes/vibetunes/internal/idgen"
|
||||
)
|
||||
|
||||
type RadioStation struct {
|
||||
ID string
|
||||
Name string
|
||||
StreamURL string
|
||||
HomepageURL string
|
||||
}
|
||||
|
||||
func CreateRadioStation(db *sql.DB, name, streamURL, homepageURL string) (*RadioStation, error) {
|
||||
id := idgen.New("rs")
|
||||
_, err := db.Exec(`INSERT INTO internet_radio_stations (id, name, stream_url, homepage_url) VALUES (?, ?, ?, ?)`,
|
||||
id, name, streamURL, homepageURL)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return GetRadioStation(db, id)
|
||||
}
|
||||
|
||||
func GetRadioStation(db *sql.DB, id string) (*RadioStation, error) {
|
||||
var r RadioStation
|
||||
err := db.QueryRow(`SELECT id, name, stream_url, homepage_url FROM internet_radio_stations WHERE id = ?`, id).
|
||||
Scan(&r.ID, &r.Name, &r.StreamURL, &r.HomepageURL)
|
||||
if errors.Is(err, sql.ErrNoRows) {
|
||||
return nil, ErrNotFound
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &r, nil
|
||||
}
|
||||
|
||||
func ListRadioStations(db *sql.DB) ([]*RadioStation, error) {
|
||||
rows, err := db.Query(`SELECT id, name, stream_url, homepage_url FROM internet_radio_stations ORDER BY name COLLATE NOCASE`)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
var out []*RadioStation
|
||||
for rows.Next() {
|
||||
var r RadioStation
|
||||
if err := rows.Scan(&r.ID, &r.Name, &r.StreamURL, &r.HomepageURL); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
out = append(out, &r)
|
||||
}
|
||||
return out, rows.Err()
|
||||
}
|
||||
|
||||
func UpdateRadioStation(db *sql.DB, id, name, streamURL, homepageURL string) error {
|
||||
_, err := db.Exec(`UPDATE internet_radio_stations SET name = ?, stream_url = ?, homepage_url = ? WHERE id = ?`,
|
||||
name, streamURL, homepageURL, id)
|
||||
return err
|
||||
}
|
||||
|
||||
func DeleteRadioStation(db *sql.DB, id string) error {
|
||||
_, err := db.Exec(`DELETE FROM internet_radio_stations WHERE id = ?`, id)
|
||||
return err
|
||||
}
|
||||
@@ -259,3 +259,19 @@ func FindSongByTitleArtist(db *sql.DB, title, artist string) (*Song, error) {
|
||||
}
|
||||
return GetSong(db, id)
|
||||
}
|
||||
|
||||
// TotalMediaFileCount returns how many (non-missing) media files are
|
||||
// indexed across all folders, for getScanStatus's idle "count".
|
||||
func TotalMediaFileCount(db *sql.DB) (int64, error) {
|
||||
var n int64
|
||||
err := db.QueryRow(`SELECT COUNT(*) FROM media_files WHERE missing = 0`).Scan(&n)
|
||||
return n, err
|
||||
}
|
||||
|
||||
// TotalFilesSeenAcrossScans sums files_seen for folders currently mid-
|
||||
// scan, for getScanStatus's in-progress "count".
|
||||
func TotalFilesSeenAcrossScans(db *sql.DB) (int64, error) {
|
||||
var n int64
|
||||
err := db.QueryRow(`SELECT COALESCE(SUM(files_seen), 0) FROM scan_state WHERE status = 'scanning'`).Scan(&n)
|
||||
return n, err
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user