mirror of
https://github.com/antebudimir/feishin.git
synced 2025-12-31 18:13:31 +00:00
enable notify, simplify use-scrobble with types, remove unused check
This commit is contained in:
parent
b219c900ca
commit
e00aeb2b67
4 changed files with 76 additions and 12 deletions
|
|
@ -171,6 +171,7 @@
|
|||
"loginRateError": "too many login attempts, please try again in a few seconds",
|
||||
"mpvRequired": "MPV required",
|
||||
"networkError": "a network error occurred",
|
||||
"notificationDenied": "permissions for notifications were denied. this setting has no effect",
|
||||
"openError": "could not open file",
|
||||
"playbackError": "an error occurred when trying to play the media",
|
||||
"remoteDisableError": "an error occurred when trying to $t(common.disable) the remote server",
|
||||
|
|
@ -605,6 +606,8 @@
|
|||
"lyricFetchProvider_description": "select the providers to fetch lyrics from. the order of the providers is the order in which they will be queried",
|
||||
"lyricOffset": "lyric offset (ms)",
|
||||
"lyricOffset_description": "offset the lyric by the specified amount of milliseconds",
|
||||
"notify": "enable song notifications",
|
||||
"notify_description": "show notifications when changing the current song",
|
||||
"minimizeToTray": "minimize to tray",
|
||||
"minimizeToTray_description": "minimize the application to the system tray",
|
||||
"minimumScrobblePercentage": "minimum scrobble duration (percentage)",
|
||||
|
|
|
|||
|
|
@ -34,6 +34,8 @@ Progress Events (Jellyfin only):
|
|||
- Sends the 'progress' scrobble event on an interval
|
||||
*/
|
||||
|
||||
type SongEvent = [QueueSong | undefined, number, 1 | 2];
|
||||
|
||||
const checkScrobbleConditions = (args: {
|
||||
scrobbleAtDurationMs: number;
|
||||
scrobbleAtPercentage: number;
|
||||
|
|
@ -86,10 +88,21 @@ export const useScrobble = () => {
|
|||
const progressIntervalId = useRef<null | ReturnType<typeof setInterval>>(null);
|
||||
const songChangeTimeoutId = useRef<null | ReturnType<typeof setTimeout>>(null);
|
||||
const handleScrobbleFromSongChange = useCallback(
|
||||
(
|
||||
current: (number | QueueSong | undefined)[],
|
||||
previous: (number | QueueSong | undefined)[],
|
||||
) => {
|
||||
(current: SongEvent, previous: SongEvent) => {
|
||||
if (scrobbleSettings?.notify && current[0]) {
|
||||
const currentSong = current[0];
|
||||
|
||||
const artists =
|
||||
currentSong.artists?.length > 0
|
||||
? currentSong.artists.map((artist) => artist.name).join(', ')
|
||||
: currentSong.artistName;
|
||||
|
||||
new Notification(`Now playing ${currentSong.name}`, {
|
||||
body: `by ${artists} on ${currentSong.album}`,
|
||||
icon: currentSong.imageUrl || undefined,
|
||||
});
|
||||
}
|
||||
|
||||
if (!isScrobbleEnabled) return;
|
||||
|
||||
if (progressIntervalId.current) {
|
||||
|
|
@ -98,8 +111,8 @@ export const useScrobble = () => {
|
|||
}
|
||||
|
||||
// const currentSong = current[0] as QueueSong | undefined;
|
||||
const previousSong = previous[0] as QueueSong;
|
||||
const previousSongTimeSec = previous[1] as number;
|
||||
const previousSong = previous[0];
|
||||
const previousSongTimeSec = previous[1];
|
||||
|
||||
// Send completion scrobble when song changes and a previous song exists
|
||||
if (previousSong?.id) {
|
||||
|
|
@ -135,7 +148,7 @@ export const useScrobble = () => {
|
|||
// Use a timeout to prevent spamming the server with scrobble events when switching through songs quickly
|
||||
clearTimeout(songChangeTimeoutId.current as ReturnType<typeof setTimeout>);
|
||||
songChangeTimeoutId.current = setTimeout(() => {
|
||||
const currentSong = current[0] as QueueSong | undefined;
|
||||
const currentSong = current[0];
|
||||
// Get the current status from the state, not variable. This is because
|
||||
// of a timing issue where, when playing the first track, the first
|
||||
// event is song, and then the event is play
|
||||
|
|
@ -169,9 +182,10 @@ export const useScrobble = () => {
|
|||
}, 2000);
|
||||
},
|
||||
[
|
||||
isScrobbleEnabled,
|
||||
scrobbleSettings?.notify,
|
||||
scrobbleSettings?.scrobbleAtDuration,
|
||||
scrobbleSettings?.scrobbleAtPercentage,
|
||||
isScrobbleEnabled,
|
||||
isCurrentSongScrobbled,
|
||||
sendScrobble,
|
||||
handleScrobbleFromSeek,
|
||||
|
|
@ -332,7 +346,7 @@ export const useScrobble = () => {
|
|||
|
||||
useEffect(() => {
|
||||
const unsubSongChange = usePlayerStore.subscribe(
|
||||
(state) => [state.current.song, state.current.time, state.current.player],
|
||||
(state): SongEvent => [state.current.song, state.current.time, state.current.player],
|
||||
handleScrobbleFromSongChange,
|
||||
{
|
||||
// We need the current time to check the scrobble condition, but we only want to
|
||||
|
|
@ -345,10 +359,8 @@ export const useScrobble = () => {
|
|||
equalityFn: (a, b) =>
|
||||
// compute whether the song changed
|
||||
(a[0] as QueueSong)?.uniqueId === (b[0] as QueueSong)?.uniqueId &&
|
||||
// compute whether the position changed. This should imply 1
|
||||
a[2] === b[2] &&
|
||||
// compute whether the same player: relevant for repeat one and repeat all (one track)
|
||||
a[3] === b[3],
|
||||
a[2] === b[2],
|
||||
},
|
||||
);
|
||||
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ import { usePlaybackSettings, useSettingsStoreActions } from '/@/renderer/store/
|
|||
import { NumberInput } from '/@/shared/components/number-input/number-input';
|
||||
import { Slider } from '/@/shared/components/slider/slider';
|
||||
import { Switch } from '/@/shared/components/switch/switch';
|
||||
import { toast } from '/@/shared/components/toast/toast';
|
||||
|
||||
export const ScrobbleSettings = () => {
|
||||
const { t } = useTranslation();
|
||||
|
|
@ -95,6 +96,52 @@ export const ScrobbleSettings = () => {
|
|||
}),
|
||||
title: t('setting.minimumScrobbleSeconds', { postProcess: 'sentenceCase' }),
|
||||
},
|
||||
{
|
||||
control: (
|
||||
<Switch
|
||||
aria-label="Toggle notify"
|
||||
defaultChecked={settings.scrobble.notify}
|
||||
onChange={async (e) => {
|
||||
if (Notification.permission === 'denied') {
|
||||
toast.error({
|
||||
message: t('error.notificationDenied', {
|
||||
postProcess: 'sentenceCase',
|
||||
}),
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
if (Notification.permission !== 'granted') {
|
||||
const permissions = await Notification.requestPermission();
|
||||
if (permissions !== 'granted') {
|
||||
toast.error({
|
||||
message: t('error.notificationDenied', {
|
||||
postProcess: 'sentenceCase',
|
||||
}),
|
||||
});
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
setSettings({
|
||||
playback: {
|
||||
...settings,
|
||||
scrobble: {
|
||||
...settings.scrobble,
|
||||
notify: e.currentTarget.checked,
|
||||
},
|
||||
},
|
||||
});
|
||||
}}
|
||||
/>
|
||||
),
|
||||
description: t('setting.notify', {
|
||||
context: 'description',
|
||||
postProcess: 'sentenceCase',
|
||||
}),
|
||||
isHidden: !('Notification' in window),
|
||||
title: t('setting.notify', { postProcess: 'sentenceCase' }),
|
||||
},
|
||||
];
|
||||
|
||||
return <SettingsSection options={scrobbleOptions} />;
|
||||
|
|
|
|||
|
|
@ -285,6 +285,7 @@ export interface SettingsState {
|
|||
preservePitch: boolean;
|
||||
scrobble: {
|
||||
enabled: boolean;
|
||||
notify: boolean;
|
||||
scrobbleAtDuration: number;
|
||||
scrobbleAtPercentage: number;
|
||||
};
|
||||
|
|
@ -479,6 +480,7 @@ const initialState: SettingsState = {
|
|||
preservePitch: true,
|
||||
scrobble: {
|
||||
enabled: true,
|
||||
notify: false,
|
||||
scrobbleAtDuration: 240,
|
||||
scrobbleAtPercentage: 75,
|
||||
},
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue