Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions src-tauri/build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@ fn main() {
"update_track",
"get_artists",
"get_artist_tracks",
"get_all_albums",
"get_album_tracks",
"get_compilation_albums",
"has_compilations",
"get_all_playlists",
Expand Down
2 changes: 2 additions & 0 deletions src-tauri/capabilities/main.json
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,8 @@
"database:allow-remove-tracks",
"database:allow-get-artists",
"database:allow-get-artist-tracks",
"database:allow-get-all-albums",
"database:allow-get-album-tracks",
"database:allow-get-compilation-albums",
"database:allow-has-compilations",
"database:allow-get-all-playlists",
Expand Down
48 changes: 48 additions & 0 deletions src-tauri/src/libs/database.rs
Original file line number Diff line number Diff line change
Expand Up @@ -289,6 +289,23 @@ impl DB {
Ok(result)
}

/**
* Get the list of albums registered in the database.
*/
pub async fn get_all_albums(&mut self) -> AnyResult<Vec<String>> {
let result: Vec<String> = sqlx::query_scalar(
"SELECT DISTINCT album
FROM tracks
ORDER BY
CASE WHEN upper(substr(album, 1, 1)) BETWEEN 'A' AND 'Z' THEN 0 ELSE 1 END,
album COLLATE NOCASE;",
)
.fetch_all(&mut self.connection)
.await?;

Ok(result)
}

/**
* Returns true if any tracks are flagged as compilations.
*/
Expand Down Expand Up @@ -343,6 +360,37 @@ impl DB {
Ok(track_groups)
}

/**
* Get all tracks for a given album.
*/
pub async fn get_album_tracks(&mut self, album: String) -> AnyResult<Vec<TrackGroup>> {
let tracks = sqlx::query_as::<_, Track>(
"SELECT * FROM tracks WHERE album = ? ORDER BY disk_no, track_no",
)
.bind(&album)
.fetch_all(&mut self.connection)
.await?;

if tracks.is_empty() {
return Ok(Vec::new());
}

let track_group = TrackGroup {
label: album,
genres: tracks
.iter()
.flat_map(|s| &s.genres)
.cloned()
.unique()
.collect(),
duration: tracks.iter().map(|t| t.duration).sum(),
year: tracks.first().and_then(|t| t.year),
tracks,
};

Ok(vec![track_group])
}

/**
* Get all compilation albums, grouped by album name, sorted by year.
*/
Expand Down
1 change: 1 addition & 0 deletions src-tauri/src/plugins/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ pub enum DefaultView {
Library,
Artists,
Playlists,
Albums,
}

#[derive(Serialize, Deserialize, Debug, Clone, TS)]
Expand Down
15 changes: 15 additions & 0 deletions src-tauri/src/plugins/db.rs
Original file line number Diff line number Diff line change
Expand Up @@ -318,6 +318,19 @@ async fn get_artist_tracks(
db_state.get_lock().await.get_artist_tracks(artist).await
}

#[tauri::command]
async fn get_all_albums(db_state: State<'_, DBState>) -> AnyResult<Vec<String>> {
db_state.get_lock().await.get_all_albums().await
}

#[tauri::command]
async fn get_album_tracks(
db_state: State<'_, DBState>,
album: String,
) -> AnyResult<Vec<TrackGroup>> {
db_state.get_lock().await.get_album_tracks(album).await
}

#[tauri::command]
async fn get_compilation_albums(db_state: State<'_, DBState>) -> AnyResult<Vec<TrackGroup>> {
db_state.get_lock().await.get_compilation_albums().await
Expand Down Expand Up @@ -459,6 +472,8 @@ pub fn init<R: Runtime>() -> TauriPlugin<R> {
update_track,
get_artists,
get_artist_tracks,
get_all_albums,
get_album_tracks,
get_compilation_albums,
has_compilations,
get_all_playlists,
Expand Down
1 change: 1 addition & 0 deletions src-tauri/src/plugins/default_view.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ pub fn init<R: Runtime>() -> TauriPlugin<R> {
DefaultView::Library => "/library",
DefaultView::Artists => "/artists",
DefaultView::Playlists => "/playlists",
DefaultView::Albums => "/albums",
};

info!("Navigating to '{}'", fragment);
Expand Down
1 change: 1 addition & 0 deletions src/assets/icons/album.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 2 additions & 0 deletions src/components/Icon.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import * as stylex from '@stylexjs/stylex';
import { error } from '@tauri-apps/plugin-log';

import album from '../assets/icons/album.svg?react';
import chevronDown from '../assets/icons/chevron-down.svg?react';
import chevronUp from '../assets/icons/chevron-up.svg?react';
import globe from '../assets/icons/globe.svg?react';
Expand All @@ -27,6 +28,7 @@ const icons: Record<
string,
React.FunctionComponent<React.SVGProps<SVGSVGElement>>
> = {
album,
chevronDown,
chevronUp,
globe,
Expand Down
3 changes: 3 additions & 0 deletions src/components/Navigation.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,9 @@ export default function Navigation() {
<NavItem to="/artists" title={t`Artists`}>
<Icon name="microphone" size={16} />
</NavItem>
<NavItem to="/albums" title={t`Albums`}>
<Icon name="album" size={16} />
</NavItem>
<NavItem to="/playlists" title={t`Playlists`}>
<Icon name="playlist" size={16} />
</NavItem>
Expand Down
2 changes: 2 additions & 0 deletions src/components/TrackList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ interface TrackListGroupedLayoutProps extends TrackListProps {
layout: 'grouped';
data: Array<TrackGroup>;
showArtistInTitle?: boolean;
showArtistLabel?: boolean;
}

type Props = TrackListDefaultLayoutProps | TrackListGroupedLayoutProps;
Expand Down Expand Up @@ -459,6 +460,7 @@ export default function TrackList(props: Props) {
onTrackSelect={onTrackSelect}
onContextMenu={onContextMenu}
onPlaybackStart={onPlaybackStart}
showArtistLabel={props.showArtistLabel}
/>
)}
</div>
Expand Down
23 changes: 22 additions & 1 deletion src/components/TrackListGrouped.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,10 +24,18 @@ type Props = {
initialOffset: number;
rowHeight: number;
showArtistInTitle?: boolean;
showArtistLabel?: boolean;
} & TrackRowEvents;

export default function TrackListGroupedLayout(props: Props) {
const { ref, trackGroups, rowHeight, showArtistInTitle, ...rest } = props;
const {
ref,
trackGroups,
rowHeight,
showArtistInTitle,
showArtistLabel,
...rest
} = props;
const { t } = useLingui();

const tracks = useAllTracks(trackGroups);
Expand Down Expand Up @@ -66,6 +74,7 @@ export default function TrackListGroupedLayout(props: Props) {
tracksGroup={tracksGroup}
rowHeight={rowHeight}
showArtistInTitle={showArtistInTitle}
showArtistLabel={showArtistLabel}
{...rest}
/>
);
Expand All @@ -79,13 +88,15 @@ type TrackListGroupProps = {
selectedTracks: Set<string>;
rowHeight: number;
showArtistInTitle?: boolean;
showArtistLabel?: boolean;
} & TrackRowEvents;

function TrackListGroup(props: TrackListGroupProps) {
const {
selectedTracks,
rowHeight,
showArtistInTitle,
showArtistLabel,
onTrackSelect,
onContextMenu,
onPlaybackStart,
Expand All @@ -97,6 +108,10 @@ function TrackListGroup(props: TrackListGroupProps) {
return null;
}

const artistName = showArtistLabel
? tracks[0]?.album_artist || tracks[0]?.artists[0] || null
: null;

return (
<div
{...stylex.props(styles.group)}
Expand All @@ -106,6 +121,7 @@ function TrackListGroup(props: TrackListGroupProps) {
{/** Instead of the first one, maybe get the first track within the album to hold a cover? */}
<Cover track={tracks[0]} iconSize={36} />
<h3 {...stylex.props(styles.label)}>{label}</h3>
{artistName && <p {...stylex.props(styles.artist)}>{artistName}</p>}
<div {...stylex.props(styles.metadata)}>
<div>
{year}
Expand Down Expand Up @@ -169,6 +185,11 @@ const styles = stylex.create({
fontWeight: 'bold',
margin: 0,
},
artist: {
color: 'var(--text-muted)',
margin: 0,
fontSize: '0.95rem',
},
metadata: {
color: 'var(--text-muted)',
display: 'flex',
Expand Down
Loading