diff --git a/src/components/ContextMenu.tsx b/src/components/ContextMenu.tsx
index 53bc724e..4518932b 100644
--- a/src/components/ContextMenu.tsx
+++ b/src/components/ContextMenu.tsx
@@ -1,9 +1,10 @@
import React, { useEffect, useLayoutEffect, useRef, useState } from 'react';
import { Play, ListPlus, Radio, Heart, Download, ChevronRight, User, Disc3, ListMusic, Plus, Info } from 'lucide-react';
import LastfmIcon from './LastfmIcon';
+import StarRating from './StarRating';
import { lastfmLoveTrack, lastfmUnloveTrack } from '../api/lastfm';
import { usePlayerStore, Track, songToTrack } from '../store/playerStore';
-import { SubsonicAlbum, SubsonicArtist, star, unstar, getSimilarSongs2, getTopSongs, buildDownloadUrl, getAlbum, getPlaylists, getPlaylist, createPlaylist, updatePlaylist, SubsonicPlaylist } from '../api/subsonic';
+import { SubsonicAlbum, SubsonicArtist, star, unstar, getSimilarSongs2, getTopSongs, buildDownloadUrl, getAlbum, getPlaylists, getPlaylist, createPlaylist, updatePlaylist, SubsonicPlaylist, setRating } from '../api/subsonic';
import { useNavigate } from 'react-router-dom';
import { useAuthStore } from '../store/authStore';
import { useDownloadModalStore } from '../store/downloadModalStore';
@@ -163,7 +164,7 @@ function AlbumToPlaylistSubmenu({ albumId, onDone }: { albumId: string; onDone:
export default function ContextMenu() {
const { t } = useTranslation();
- const { contextMenu, closeContextMenu, playTrack, enqueue, queue, currentTrack, removeTrack, lastfmLovedCache, setLastfmLovedForSong, starredOverrides, setStarredOverride, openSongInfo } = usePlayerStore();
+ const { contextMenu, closeContextMenu, playTrack, enqueue, queue, currentTrack, removeTrack, lastfmLovedCache, setLastfmLovedForSong, starredOverrides, setStarredOverride, openSongInfo, userRatingOverrides, setUserRatingOverride } = usePlayerStore();
const auth = useAuthStore();
const requestDownloadFolder = useDownloadModalStore(s => s.requestFolder);
const navigate = useNavigate();
@@ -381,6 +382,13 @@ export default function ContextMenu() {
);
})()}
+
e.stopPropagation()}>
+ { setUserRatingOverride(song.id, r); setRating(song.id, r).catch(() => {}); }}
+ ariaLabel={t('albumDetail.ratingLabel')}
+ />
+
handleAction(() => openSongInfo(song.id))}>
{t('contextMenu.songInfo')}
@@ -501,6 +509,13 @@ export default function ContextMenu() {
{t('contextMenu.startRadio')}
diff --git a/src/components/PlayerBar.tsx b/src/components/PlayerBar.tsx
index a92a0a56..b2677740 100644
--- a/src/components/PlayerBar.tsx
+++ b/src/components/PlayerBar.tsx
@@ -6,10 +6,11 @@ import {
} from 'lucide-react';
import { usePlayerStore } from '../store/playerStore';
import { useAuthStore } from '../store/authStore';
-import { buildCoverArtUrl, coverArtCacheKey, star, unstar } from '../api/subsonic';
+import { buildCoverArtUrl, coverArtCacheKey, star, unstar, setRating } from '../api/subsonic';
import CachedImage from './CachedImage';
import WaveformSeek from './WaveformSeek';
import Equalizer from './Equalizer';
+import StarRating from './StarRating';
import { useTranslation } from 'react-i18next';
import { useNavigate } from 'react-router-dom';
import { useLyricsStore } from '../store/lyricsStore';
@@ -37,6 +38,7 @@ export default function PlayerBar() {
lastfmLoved, toggleLastfmLove,
isQueueVisible, toggleQueue,
starredOverrides, setStarredOverride,
+ userRatingOverrides, setUserRatingOverride,
} = usePlayerStore();
const { lastfmSessionKey } = useAuthStore();
@@ -138,6 +140,14 @@ export default function PlayerBar() {
style={{ cursor: !isRadio && currentTrack?.artistId ? 'pointer' : 'default' }}
onClick={() => !isRadio && currentTrack?.artistId && navigate(`/artist/${currentTrack.artistId}`)}
/>
+ {currentTrack && !isRadio && (
+