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) }