Subsonic: browsing endpoints (getMusicFolders, getIndexes, getMusicDirectory, getGenres, getArtists/Artist, getAlbum, getSong, getVideos/VideoInfo, getArtistInfo/2, getAlbumInfo/2, getSimilarSongs/2, getTopSongs)
This commit is contained in:
@@ -0,0 +1,91 @@
|
||||
package database
|
||||
|
||||
import "database/sql"
|
||||
|
||||
// ListVideoMediaFiles returns every media file the scanner identified as
|
||||
// a video (as opposed to audio) track.
|
||||
func ListVideoMediaFiles(db *sql.DB) ([]*MediaFile, error) {
|
||||
rows, err := db.Query(mediaFileSelect + ` WHERE kind = 'video' AND missing = 0 ORDER BY relative_path`)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
return scanMediaFiles(rows)
|
||||
}
|
||||
|
||||
// FindArtistByName looks up an artist by exact, case-insensitive name —
|
||||
// used by endpoints that take an artist name rather than ID (getTopSongs).
|
||||
func FindArtistByName(db *sql.DB, name string) (*Artist, error) {
|
||||
var id string
|
||||
err := db.QueryRow(`SELECT id FROM artists WHERE name = ? COLLATE NOCASE LIMIT 1`, name).Scan(&id)
|
||||
if err == sql.ErrNoRows {
|
||||
return nil, ErrNotFound
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return GetArtist(db, id)
|
||||
}
|
||||
|
||||
// TopSongsByArtist returns an artist's songs ordered by total play count
|
||||
// (across all users) descending, falling back to any order for songs
|
||||
// that have never been played, up to limit.
|
||||
func TopSongsByArtist(db *sql.DB, artistID string, limit int) ([]*Song, error) {
|
||||
rows, err := db.Query(`SELECT s.id, s.media_file_id, s.album_id, s.title, s.sort_title, s.track_number, s.track_total, s.disc_number, s.disc_total,
|
||||
s.composer, s.conductor, s.isrc, s.bpm, s.musicbrainz_track_id, s.musicbrainz_recording_id,
|
||||
s.replaygain_track_gain_db, s.replaygain_track_peak, s.replaygain_album_gain_db, s.replaygain_album_peak
|
||||
FROM songs s
|
||||
JOIN song_artists sa ON sa.song_id = s.id
|
||||
LEFT JOIN (SELECT song_id, COUNT(*) AS plays FROM playback_history GROUP BY song_id) ph ON ph.song_id = s.id
|
||||
WHERE sa.artist_id = ?
|
||||
GROUP BY s.id
|
||||
ORDER BY COALESCE(MAX(ph.plays), 0) DESC, s.title COLLATE NOCASE
|
||||
LIMIT ?`, artistID, limit)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
return scanSongs(rows)
|
||||
}
|
||||
|
||||
// SimilarSongsByGenre returns up to limit random songs sharing at least
|
||||
// one genre with seedSongID, excluding the seed itself. A simple,
|
||||
// honest local substitute for an external recommendation service.
|
||||
func SimilarSongsByGenre(db *sql.DB, seedSongID string, limit int) ([]*Song, error) {
|
||||
rows, err := db.Query(`SELECT DISTINCT s.id, s.media_file_id, s.album_id, s.title, s.sort_title, s.track_number, s.track_total, s.disc_number, s.disc_total,
|
||||
s.composer, s.conductor, s.isrc, s.bpm, s.musicbrainz_track_id, s.musicbrainz_recording_id,
|
||||
s.replaygain_track_gain_db, s.replaygain_track_peak, s.replaygain_album_gain_db, s.replaygain_album_peak
|
||||
FROM songs s
|
||||
JOIN song_genres sg ON sg.song_id = s.id
|
||||
WHERE sg.genre_id IN (SELECT genre_id FROM song_genres WHERE song_id = ?)
|
||||
AND s.id != ?
|
||||
ORDER BY RANDOM() LIMIT ?`, seedSongID, seedSongID, limit)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
return scanSongs(rows)
|
||||
}
|
||||
|
||||
// SimilarSongsByArtist returns up to limit random songs by the given
|
||||
// artist (or sharing a genre with them if that artist has few songs),
|
||||
// excluding none in particular — used for getSimilarSongs2's artist
|
||||
// seed.
|
||||
func SimilarSongsByArtist(db *sql.DB, artistID string, limit int) ([]*Song, error) {
|
||||
rows, err := db.Query(`SELECT DISTINCT s.id, s.media_file_id, s.album_id, s.title, s.sort_title, s.track_number, s.track_total, s.disc_number, s.disc_total,
|
||||
s.composer, s.conductor, s.isrc, s.bpm, s.musicbrainz_track_id, s.musicbrainz_recording_id,
|
||||
s.replaygain_track_gain_db, s.replaygain_track_peak, s.replaygain_album_gain_db, s.replaygain_album_peak
|
||||
FROM songs s
|
||||
JOIN song_genres sg ON sg.song_id = s.id
|
||||
WHERE sg.genre_id IN (
|
||||
SELECT DISTINCT sg2.genre_id FROM song_artists sa2
|
||||
JOIN song_genres sg2 ON sg2.song_id = sa2.song_id
|
||||
WHERE sa2.artist_id = ?
|
||||
)
|
||||
ORDER BY RANDOM() LIMIT ?`, artistID, limit)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
return scanSongs(rows)
|
||||
}
|
||||
Reference in New Issue
Block a user