diff --git a/ui/src/album/AlbumSongs.js b/ui/src/album/AlbumSongs.js
index 1ff5418ab..7b64a4fe1 100644
--- a/ui/src/album/AlbumSongs.js
+++ b/ui/src/album/AlbumSongs.js
@@ -2,7 +2,6 @@ import React from 'react'
import {
BulkActionsToolbar,
DatagridLoading,
- FunctionField,
ListToolbar,
TextField,
useListController,
@@ -15,9 +14,10 @@ import StarBorderIcon from '@material-ui/icons/StarBorder'
import { playTracks } from '../audioplayer'
import {
DurationField,
- SongDetails,
- SongDatagrid,
SongContextMenu,
+ SongDatagrid,
+ SongDetails,
+ SongTitleField,
} from '../common'
import AddToPlaylistDialog from '../dialogs/AddToPlaylistDialog'
@@ -62,14 +62,6 @@ const useStylesListToolbar = makeStyles({
},
})
-const trackName = (r) => {
- const name = r.title
- if (r.trackNumber) {
- return r.trackNumber.toString().padStart(2, '0') + ' ' + name
- }
- return name
-}
-
const AlbumSongs = (props) => {
const classes = useStyles(props)
const classesToolbar = useStylesListToolbar(props)
@@ -132,14 +124,11 @@ const AlbumSongs = (props) => {
sortable={false}
/>
)}
- {isDesktop && }
- {!isDesktop && (
-
- )}
+
{isDesktop && }
{
}
const OnAudioProgress = (info) => {
- const progress = (info.currentTime / info.duration) * 100
- if (isNaN(info.duration) || progress < 90) {
+ if (info.ended) {
+ document.title = 'Navidrome'
+ }
+ dispatch(progress(info))
+ const pos = (info.currentTime / info.duration) * 100
+ if (isNaN(info.duration) || pos < 90) {
return
}
const item = queue.queue.find((item) => item.trackId === info.trackId)
@@ -120,10 +124,6 @@ const Player = () => {
}
}
- const onAudioEnded = () => {
- document.title = 'Navidrome'
- }
-
if (authenticated && options.audioLists.length > 0) {
return (
{
onAudioListsChange={OnAudioListsChange}
onAudioProgress={OnAudioProgress}
onAudioPlay={OnAudioPlay}
- onAudioEnded={onAudioEnded}
/>
)
}
diff --git a/ui/src/audioplayer/queue.js b/ui/src/audioplayer/queue.js
index e5f3d74dd..27f245b5c 100644
--- a/ui/src/audioplayer/queue.js
+++ b/ui/src/audioplayer/queue.js
@@ -6,6 +6,7 @@ const PLAYER_SET_TRACK = 'PLAYER_SET_TRACK'
const PLAYER_SYNC_QUEUE = 'PLAYER_SYNC_QUEUE'
const PLAYER_SCROBBLE = 'PLAYER_SCROBBLE'
const PLAYER_PLAY_TRACKS = 'PLAYER_PLAY_TRACKS'
+const PLAYER_PROGRESS = 'PLAYER_PROGRESS'
const mapToAudioLists = (item) => {
// If item comes from a playlist, id is mediaFileId
@@ -88,13 +89,30 @@ const scrobble = (id, submit) => ({
submit,
})
+const progress = (audioInfo) => ({
+ type: PLAYER_PROGRESS,
+ data: audioInfo,
+})
+
const playQueueReducer = (
- previousState = { queue: [], clear: true, playing: false },
+ previousState = { queue: [], clear: true, playing: false, current: {} },
payload
) => {
- let queue
+ let queue, current
const { type, data } = payload
switch (type) {
+ case PLAYER_PROGRESS:
+ queue = previousState.queue
+ current = data.ended
+ ? {}
+ : {
+ trackId: data.trackId,
+ paused: data.paused,
+ }
+ return {
+ ...previousState,
+ current,
+ }
case PLAYER_ADD_TRACKS:
queue = previousState.queue
Object.keys(data).forEach((id) => {
@@ -109,10 +127,12 @@ const playQueueReducer = (
playing: true,
}
case PLAYER_SYNC_QUEUE:
+ current = data.length > 0 ? previousState.current : {}
return {
...previousState,
queue: data,
clear: false,
+ current,
}
case PLAYER_SCROBBLE:
const newQueue = previousState.queue.map((item) => {
@@ -156,6 +176,7 @@ export {
playTracks,
syncQueue,
scrobble,
+ progress,
shuffleTracks,
playQueueReducer,
}
diff --git a/ui/src/common/SongTitleField.js b/ui/src/common/SongTitleField.js
new file mode 100644
index 000000000..173a02078
--- /dev/null
+++ b/ui/src/common/SongTitleField.js
@@ -0,0 +1,58 @@
+import { makeStyles } from '@material-ui/core/styles'
+import React from 'react'
+import { useSelector } from 'react-redux'
+import get from 'lodash.get'
+import playing from '../icons/playing.gif'
+import { FunctionField } from 'react-admin'
+import PropTypes from 'prop-types'
+
+const useStyles = makeStyles({
+ playingIcon: {
+ width: '20px',
+ height: '20px',
+ verticalAlign: 'text-top',
+ marginTop: '-2px',
+ paddingRight: '3px',
+ },
+})
+
+const SongTitleField = ({ showTrackNumbers, ...props }) => {
+ const classes = useStyles()
+ const { record } = props
+ const currentTrack = useSelector((state) => get(state, 'queue.current', {}))
+ const currentId = currentTrack.trackId
+ const paused = currentTrack.paused
+ const isCurrent =
+ currentId &&
+ !paused &&
+ (currentId === record.id || currentId === record.mediaFileId)
+
+ const trackName = (r) => {
+ const name = r.title
+ if (r.trackNumber && showTrackNumbers) {
+ return r.trackNumber.toString().padStart(2, '0') + ' ' + name
+ }
+ return name
+ }
+
+ return (
+ <>
+ {isCurrent && (
+
+ )}
+
+ >
+ )
+}
+
+SongTitleField.propTypes = {
+ record: PropTypes.object,
+ showTrackNumbers: PropTypes.bool,
+}
+
+export default SongTitleField
diff --git a/ui/src/common/index.js b/ui/src/common/index.js
index ae720cc86..0ccf8336e 100644
--- a/ui/src/common/index.js
+++ b/ui/src/common/index.js
@@ -12,6 +12,7 @@ import DocLink from './DocLink'
import List from './List'
import { SongDatagrid, SongDatagridRow } from './SongDatagrid'
import SongContextMenu from './SongContextMenu'
+import SongTitleField from './SongTitleField'
import QuickFilter from './QuickFilter'
import useAlbumsPerPage from './useAlbumsPerPage'
@@ -28,6 +29,7 @@ export {
SongDetails,
SongDatagrid,
SongDatagridRow,
+ SongTitleField,
DocLink,
formatRange,
ArtistLinkField,
diff --git a/ui/src/icons/playing.gif b/ui/src/icons/playing.gif
new file mode 100644
index 000000000..40bb3c428
Binary files /dev/null and b/ui/src/icons/playing.gif differ
diff --git a/ui/src/playlist/PlaylistSongs.js b/ui/src/playlist/PlaylistSongs.js
index 4a03c35d3..6f4a57ceb 100644
--- a/ui/src/playlist/PlaylistSongs.js
+++ b/ui/src/playlist/PlaylistSongs.js
@@ -19,6 +19,7 @@ import {
SongDetails,
SongContextMenu,
SongDatagrid,
+ SongTitleField,
} from '../common'
import AddToPlaylistDialog from '../dialogs/AddToPlaylistDialog'
import { AlbumLinkField } from '../song/AlbumLinkField'
@@ -160,7 +161,7 @@ const PlaylistSongs = (props) => {
contextAlwaysVisible={!isDesktop}
>
{isDesktop && }
-
+
{isDesktop && }
{isDesktop && }
{
rowClick={handleRowClick}
contextAlwaysVisible={!isDesktop}
>
-
+
{isDesktop && }
{isDesktop && }