92 lines
3.8 KiB
Go
92 lines
3.8 KiB
Go
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)
|
|
}
|