Add "Play Next" action (finally)

This commit is contained in:
Deluan 2020-09-21 20:10:52 -04:00
parent aa133e6b00
commit 7305e3aa17
14 changed files with 130 additions and 23 deletions

View File

@ -22,8 +22,9 @@
},
"actions": {
"addToQueue": "Adicionar à fila",
"playNow": "Tocar agora",
"addToPlaylist": "Adicionar à playlist",
"playNow": "Tocar agora",
"playNext": "Toca a seguir",
"shuffleAll": "Aleatório",
"download": "Baixar"
}

27
ui/package-lock.json generated
View File

@ -14031,6 +14031,13 @@
"tough-cookie": "~2.5.0",
"tunnel-agent": "^0.6.0",
"uuid": "^3.3.2"
},
"dependencies": {
"uuid": {
"version": "3.4.0",
"resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz",
"integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A=="
}
}
},
"request-promise-core": {
@ -14842,6 +14849,13 @@
"faye-websocket": "^0.10.0",
"uuid": "^3.4.0",
"websocket-driver": "0.6.5"
},
"dependencies": {
"uuid": {
"version": "3.4.0",
"resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz",
"integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A=="
}
}
},
"sockjs-client": {
@ -16135,9 +16149,9 @@
"integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM="
},
"uuid": {
"version": "3.4.0",
"resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz",
"integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A=="
"version": "8.3.0",
"resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.0.tgz",
"integrity": "sha512-fX6Z5o4m6XsXBdli9g7DtWgAx+osMsRRZFKma1mIUsLCz6vRvv+pz5VNbyu9UEDzpMWulZfvpgb/cmDXVulYFQ=="
},
"v8-compile-cache": {
"version": "2.1.1",
@ -16685,6 +16699,13 @@
"requires": {
"ansi-colors": "^3.0.0",
"uuid": "^3.3.2"
},
"dependencies": {
"uuid": {
"version": "3.4.0",
"resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz",
"integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A=="
}
}
},
"webpack-manifest-plugin": {

View File

@ -17,11 +17,13 @@
"react-dom": "^16.13.1",
"react-drag-listview": "^0.1.7",
"react-ga": "^3.1.2",
"react-icons": "^3.11.0",
"react-image-lightbox": "^5.1.1",
"react-jinke-music-player": "^4.18.2",
"react-measure": "^2.5.2",
"react-redux": "^7.2.1",
"react-scripts": "^3.4.3"
"react-scripts": "^3.4.3",
"uuid": "^8.3.0"
},
"devDependencies": {
"@testing-library/jest-dom": "^5.11.4",

View File

@ -10,8 +10,8 @@ import {
import PlayArrowIcon from '@material-ui/icons/PlayArrow'
import ShuffleIcon from '@material-ui/icons/Shuffle'
import CloudDownloadOutlinedIcon from '@material-ui/icons/CloudDownloadOutlined'
import AddToQueueIcon from '@material-ui/icons/AddToQueue'
import { addTracks, playTracks, shuffleTracks } from '../audioplayer'
import { RiPlayListAddFill, RiPlayList2Fill } from 'react-icons/ri'
import { playNext, addTracks, playTracks, shuffleTracks } from '../audioplayer'
import subsonic from '../subsonic'
const AlbumActions = ({ className, ids, data, record, ...rest }) => {
@ -22,6 +22,10 @@ const AlbumActions = ({ className, ids, data, record, ...rest }) => {
dispatch(playTracks(data, ids))
}, [dispatch, data, ids])
const handlePlayNext = React.useCallback(() => {
dispatch(playNext(data, ids))
}, [dispatch, data, ids])
const handlePlayLater = React.useCallback(() => {
dispatch(addTracks(data, ids))
}, [dispatch, data, ids])
@ -48,11 +52,17 @@ const AlbumActions = ({ className, ids, data, record, ...rest }) => {
>
<ShuffleIcon />
</Button>
<Button
onClick={handlePlayNext}
label={translate('resources.album.actions.playNext')}
>
<RiPlayList2Fill />
</Button>
<Button
onClick={handlePlayLater}
label={translate('resources.album.actions.addToQueue')}
>
<AddToQueueIcon />
<RiPlayListAddFill />
</Button>
<Button
onClick={handleDownload}

View File

@ -1,5 +1,6 @@
import React, { Fragment, useEffect } from 'react'
import { useUnselectAll } from 'react-admin'
import { playNext } from '../audioplayer'
import AddToQueueButton from '../song/AddToQueueButton'
import AddToPlaylistButton from '../song/AddToPlaylistButton'
@ -11,6 +12,11 @@ export const AlbumSongBulkActions = (props) => {
}, [])
return (
<Fragment>
<AddToQueueButton
{...props}
action={playNext}
label={'resources.song.actions.playNext'}
/>
<AddToQueueButton {...props} />
<AddToPlaylistButton {...props} />
</Fragment>

View File

@ -167,6 +167,7 @@ const Player = () => {
return (
<ReactJkMusicPlayer
{...options}
quietUpdate
onAudioListsChange={OnAudioListsChange}
onAudioProgress={OnAudioProgress}
onAudioPlay={OnAudioPlay}

View File

@ -4,6 +4,7 @@ import {
setTrack,
playQueueReducer,
playTracks,
playNext,
shuffleTracks,
clearQueue,
} from './queue'
@ -13,6 +14,7 @@ export {
addTracks,
setTrack,
playTracks,
playNext,
playQueueReducer,
shuffleTracks,
clearQueue,

View File

@ -1,7 +1,10 @@
import 'react-jinke-music-player/assets/index.css'
import get from 'lodash.get'
import { v4 as uuidv4 } from 'uuid'
import subsonic from '../subsonic'
const PLAYER_ADD_TRACKS = 'PLAYER_ADD_TRACKS'
const PLAYER_PLAY_NEXT = 'PLAYER_PLAY_NEXT'
const PLAYER_SET_TRACK = 'PLAYER_SET_TRACK'
const PLAYER_SYNC_QUEUE = 'PLAYER_SYNC_QUEUE'
const PLAYER_CLEAR_QUEUE = 'PLAYER_CLEAR_QUEUE'
@ -23,6 +26,7 @@ const mapToAudioLists = (item) => {
cover: subsonic.url('getCoverArt', id, { size: 300 }),
musicSrc: subsonic.url('stream', id, { ts: true }),
scrobbled: false,
uuid: uuidv4(),
}
}
@ -46,6 +50,14 @@ const addTracks = (data, ids) => {
}
}
const playNext = (data, ids) => {
const songs = filterSongs(data, ids)
return {
type: PLAYER_PLAY_NEXT,
data: songs,
}
}
const shuffle = (data) => {
const ids = Object.keys(data)
for (let i = ids.length - 1; i > 0; i--) {
@ -109,6 +121,7 @@ const initialState = { queue: [], clear: true, current: {}, volume: 1 }
const playQueueReducer = (previousState = initialState, payload) => {
let queue, current
let newQueue
const { type, data } = payload
switch (type) {
case PLAYER_CLEAR_QUEUE:
@ -124,6 +137,7 @@ const playQueueReducer = (previousState = initialState, payload) => {
? {}
: {
trackId: data.trackId,
uuid: data.uuid,
paused: data.paused,
}
return {
@ -137,6 +151,25 @@ const playQueueReducer = (previousState = initialState, payload) => {
queue.push(mapToAudioLists(data[id]))
})
return { ...previousState, queue, clear: false }
case PLAYER_PLAY_NEXT:
current = get(previousState.current, 'uuid', '')
newQueue = []
let foundPos = false
previousState.queue.forEach((item) => {
newQueue.push(item)
if (item.uuid === current) {
foundPos = true
Object.keys(data).forEach((id) => {
newQueue.push(mapToAudioLists(data[id]))
})
}
})
if (!foundPos) {
Object.keys(data).forEach((id) => {
newQueue.push(mapToAudioLists(data[id]))
})
}
return { ...previousState, queue: newQueue, clear: true }
case PLAYER_SET_TRACK:
return {
...previousState,
@ -152,7 +185,7 @@ const playQueueReducer = (previousState = initialState, payload) => {
current,
}
case PLAYER_SCROBBLE:
const newQueue = previousState.queue.map((item) => {
newQueue = previousState.queue.map((item) => {
return {
...item,
scrobbled:
@ -189,6 +222,7 @@ export {
addTracks,
setTrack,
playTracks,
playNext,
syncQueue,
clearQueue,
scrobble,

View File

@ -7,7 +7,7 @@ import MenuItem from '@material-ui/core/MenuItem'
import MoreVertIcon from '@material-ui/icons/MoreVert'
import { makeStyles } from '@material-ui/core/styles'
import { useDataProvider, useNotify, useTranslate } from 'react-admin'
import { addTracks, playTracks, shuffleTracks } from '../audioplayer'
import { playNext, addTracks, playTracks, shuffleTracks } from '../audioplayer'
import { openAddToPlaylist } from '../dialogs/dialogState'
import subsonic from '../subsonic'
import StarButton from './StarButton'
@ -43,6 +43,11 @@ const ContextMenu = ({
label: 'resources.album.actions.playAll',
action: (data, ids) => dispatch(playTracks(data, ids)),
},
playNext: {
needData: true,
label: 'resources.album.actions.playNext',
action: (data, ids) => dispatch(playNext(data, ids)),
},
addToQueue: {
needData: true,
label: 'resources.album.actions.addToQueue',

View File

@ -5,7 +5,7 @@ import { useTranslate } from 'react-admin'
import { IconButton, Menu, MenuItem } from '@material-ui/core'
import { makeStyles } from '@material-ui/core/styles'
import MoreVertIcon from '@material-ui/icons/MoreVert'
import { addTracks, setTrack } from '../audioplayer'
import { playNext, addTracks, setTrack } from '../audioplayer'
import { openAddToPlaylist } from '../dialogs/dialogState'
import subsonic from '../subsonic'
import StarButton from './StarButton'
@ -35,6 +35,10 @@ const SongContextMenu = ({
label: 'resources.song.actions.playNow',
action: (record) => dispatch(setTrack(record)),
},
playNext: {
label: 'resources.song.actions.playNext',
action: (record) => dispatch(playNext({ [record.id]: record })),
},
addToQueue: {
label: 'resources.song.actions.addToQueue',
action: (record) => dispatch(addTracks({ [record.id]: record })),

View File

@ -25,6 +25,7 @@
"addToQueue": "Play Later",
"addToPlaylist": "Add to Playlist",
"playNow": "Play Now",
"playNext": "Play Next",
"shuffleAll": "Shuffle All",
"download": "Download"
}

View File

@ -9,10 +9,10 @@ import {
import PlayArrowIcon from '@material-ui/icons/PlayArrow'
import ShuffleIcon from '@material-ui/icons/Shuffle'
import CloudDownloadOutlinedIcon from '@material-ui/icons/CloudDownloadOutlined'
import AddToQueueIcon from '@material-ui/icons/AddToQueue'
import { RiPlayListAddFill, RiPlayList2Fill } from 'react-icons/ri'
import QueueMusicIcon from '@material-ui/icons/QueueMusic'
import { httpClient } from '../dataProvider'
import { addTracks, playTracks, shuffleTracks } from '../audioplayer'
import { playNext, addTracks, playTracks, shuffleTracks } from '../audioplayer'
import { M3U_MIME_TYPE, REST_URL } from '../consts'
import subsonic from '../subsonic'
import PropTypes from 'prop-types'
@ -25,6 +25,10 @@ const PlaylistActions = ({ className, ids, data, record, ...rest }) => {
dispatch(playTracks(data, ids))
}, [dispatch, data, ids])
const handlePlayNext = React.useCallback(() => {
dispatch(playNext(data, ids))
}, [dispatch, data, ids])
const handlePlayLater = React.useCallback(() => {
dispatch(addTracks(data, ids))
}, [dispatch, data, ids])
@ -69,11 +73,17 @@ const PlaylistActions = ({ className, ids, data, record, ...rest }) => {
>
<ShuffleIcon />
</Button>
<Button
onClick={handlePlayNext}
label={translate('resources.album.actions.playNext')}
>
<RiPlayList2Fill />
</Button>
<Button
onClick={handlePlayLater}
label={translate('resources.album.actions.addToQueue')}
>
<AddToQueueIcon />
<RiPlayListAddFill />
</Button>
<Button
onClick={handleDownload}

View File

@ -8,9 +8,9 @@ import {
} from 'react-admin'
import { useDispatch } from 'react-redux'
import { addTracks } from '../audioplayer'
import AddToQueueIcon from '@material-ui/icons/AddToQueue'
import { RiPlayListAddFill } from 'react-icons/ri'
const AddToQueueButton = ({ resource, selectedIds }) => {
const AddToQueueButton = ({ resource, selectedIds, action, label, icon }) => {
const dispatch = useDispatch()
const translate = useTranslate()
const dataProvider = useDataProvider()
@ -27,7 +27,7 @@ const AddToQueueButton = ({ resource, selectedIds }) => {
{}
)
// Add the tracks to the queue in the selection order
dispatch(addTracks(tracks, selectedIds))
dispatch(action(tracks, selectedIds))
})
.catch(() => {
notify('ra.page.error', 'warning')
@ -36,14 +36,16 @@ const AddToQueueButton = ({ resource, selectedIds }) => {
}
return (
<Button
color="secondary"
onClick={addToQueue}
label={translate('resources.song.actions.addToQueue')}
>
<AddToQueueIcon />
<Button color="secondary" onClick={addToQueue} label={translate(label)}>
{icon}
</Button>
)
}
AddToQueueButton.defaultProps = {
action: addTracks,
label: 'resources.song.actions.addToQueue',
icon: <RiPlayListAddFill />,
}
export default AddToQueueButton

View File

@ -1,10 +1,18 @@
import React, { Fragment } from 'react'
import AddToQueueButton from './AddToQueueButton'
import AddToPlaylistButton from './AddToPlaylistButton'
import { RiPlayList2Fill } from 'react-icons/ri'
import { playNext } from '../audioplayer'
export const SongBulkActions = (props) => {
return (
<Fragment>
<AddToQueueButton
{...props}
action={playNext}
label={'resources.song.actions.playNext'}
icon={<RiPlayList2Fill />}
/>
<AddToQueueButton {...props} />
<AddToPlaylistButton {...props} />
</Fragment>