Colours for tracks
This commit is contained in:
21
src/db.rs
21
src/db.rs
@@ -111,6 +111,7 @@ pub struct TopTrack {
|
|||||||
pub title: String,
|
pub title: String,
|
||||||
pub plays: i64,
|
pub plays: i64,
|
||||||
pub listen_time_secs: i64,
|
pub listen_time_secs: i64,
|
||||||
|
pub dominant_source: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A row in the "top genres" ranking.
|
/// A row in the "top genres" ranking.
|
||||||
@@ -521,13 +522,28 @@ pub fn top_tracks(conn: &Connection, period: &str, limit: i64) -> Result<Vec<Top
|
|||||||
// In SQLite, the non-aggregated `album` column in a GROUP BY returns an
|
// In SQLite, the non-aggregated `album` column in a GROUP BY returns an
|
||||||
// arbitrary row's value, but wrapping it in a subquery with its own
|
// arbitrary row's value, but wrapping it in a subquery with its own
|
||||||
// GROUP BY and ORDER BY ensures we get the most frequent album.
|
// GROUP BY and ORDER BY ensures we get the most frequent album.
|
||||||
|
let subquery_period_filter = if whr.is_empty() {
|
||||||
|
String::new()
|
||||||
|
} else {
|
||||||
|
" AND s2.scrobbled_at BETWEEN ?1 AND ?2".to_string()
|
||||||
|
};
|
||||||
|
|
||||||
let sql = format!(
|
let sql = format!(
|
||||||
"SELECT artist, album, title, COUNT(*) as plays, COALESCE(SUM(played_duration_secs), 0) as listen_time
|
"SELECT artist, album, title, COUNT(*) as plays,
|
||||||
|
COALESCE(SUM(played_duration_secs), 0) as listen_time,
|
||||||
|
(SELECT s2.source
|
||||||
|
FROM scrobbles s2
|
||||||
|
WHERE s2.artist = scrobbles.artist
|
||||||
|
AND s2.title = scrobbles.title
|
||||||
|
AND s2.source IS NOT NULL{}
|
||||||
|
GROUP BY s2.source
|
||||||
|
ORDER BY COUNT(*) DESC
|
||||||
|
LIMIT 1) AS dominant_source
|
||||||
FROM scrobbles{}
|
FROM scrobbles{}
|
||||||
GROUP BY artist, title
|
GROUP BY artist, title
|
||||||
ORDER BY plays DESC, listen_time DESC, artist ASC, title ASC
|
ORDER BY plays DESC, listen_time DESC, artist ASC, title ASC
|
||||||
LIMIT {}",
|
LIMIT {}",
|
||||||
whr, limit
|
subquery_period_filter, whr, limit
|
||||||
);
|
);
|
||||||
|
|
||||||
let mut stmt = conn.prepare(&sql)?;
|
let mut stmt = conn.prepare(&sql)?;
|
||||||
@@ -538,6 +554,7 @@ pub fn top_tracks(conn: &Connection, period: &str, limit: i64) -> Result<Vec<Top
|
|||||||
title: row.get(2)?,
|
title: row.get(2)?,
|
||||||
plays: row.get(3)?,
|
plays: row.get(3)?,
|
||||||
listen_time_secs: row.get(4)?,
|
listen_time_secs: row.get(4)?,
|
||||||
|
dominant_source: row.get(5)?,
|
||||||
})
|
})
|
||||||
};
|
};
|
||||||
if p.is_empty() {
|
if p.is_empty() {
|
||||||
|
|||||||
@@ -1062,12 +1062,33 @@ pub fn render_html_report(conn: &Connection, limit: i64, all_time_limit: i64) ->
|
|||||||
.flatten()
|
.flatten()
|
||||||
.and_then(|m| m.cover_url);
|
.and_then(|m| m.cover_url);
|
||||||
let cover = resolve_cover(cover_url, &mut cover_files);
|
let cover = resolve_cover(cover_url, &mut cover_files);
|
||||||
|
// Prepend a subtle source dot to the album name if we know
|
||||||
|
// the dominant source for this album.
|
||||||
|
let album_cell = if let Some(src) = a.dominant_source.as_deref() {
|
||||||
|
if let Some((bg, _)) = source_colours(src, &ordered_sources) {
|
||||||
|
format!(
|
||||||
|
"<span style=\"display:inline-block;width:9px;height:9px;\
|
||||||
|
border-radius:50%;background:{};margin-right:5px;\
|
||||||
|
vertical-align:middle\"></span>{}",
|
||||||
|
bg,
|
||||||
|
html_escape(&a.album)
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
html_escape(&a.album)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
html_escape(&a.album)
|
||||||
|
};
|
||||||
BarRow {
|
BarRow {
|
||||||
cells: vec![(i + 1).to_string(), a.artist.clone(), a.album.clone()],
|
cells: vec![
|
||||||
|
(i + 1).to_string(),
|
||||||
|
html_escape(&a.artist),
|
||||||
|
album_cell,
|
||||||
|
],
|
||||||
value: a.plays,
|
value: a.plays,
|
||||||
suffix: format_duration(a.listen_time_secs),
|
suffix: format_duration(a.listen_time_secs),
|
||||||
cover,
|
cover,
|
||||||
raw_cells: false,
|
raw_cells: true,
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.collect();
|
.collect();
|
||||||
@@ -1138,12 +1159,27 @@ pub fn render_html_report(conn: &Connection, limit: i64, all_time_limit: i64) ->
|
|||||||
.flatten()
|
.flatten()
|
||||||
.and_then(|m| m.cover_url);
|
.and_then(|m| m.cover_url);
|
||||||
let cover = resolve_cover(cover_url, &mut cover_files);
|
let cover = resolve_cover(cover_url, &mut cover_files);
|
||||||
|
let title_cell = if let Some(src) = t.dominant_source.as_deref() {
|
||||||
|
if let Some((bg, _)) = source_colours(src, &ordered_sources) {
|
||||||
|
format!(
|
||||||
|
"<span style=\"display:inline-block;width:9px;height:9px;\
|
||||||
|
border-radius:50%;background:{};margin-right:5px;\
|
||||||
|
vertical-align:middle\"></span>{}",
|
||||||
|
bg,
|
||||||
|
html_escape(&t.title)
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
html_escape(&t.title)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
html_escape(&t.title)
|
||||||
|
};
|
||||||
BarRow {
|
BarRow {
|
||||||
cells: vec![(i + 1).to_string(), t.artist.clone(), t.title.clone()],
|
cells: vec![(i + 1).to_string(), html_escape(&t.artist), title_cell],
|
||||||
value: t.plays,
|
value: t.plays,
|
||||||
suffix: format_duration(t.listen_time_secs),
|
suffix: format_duration(t.listen_time_secs),
|
||||||
cover,
|
cover,
|
||||||
raw_cells: false,
|
raw_cells: true,
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.collect();
|
.collect();
|
||||||
|
|||||||
Reference in New Issue
Block a user