funkwhale/front/src/store/radios.ts

172 lines
4.6 KiB
TypeScript

import type { BackendError, Track } from '~/types'
import type { RootState } from '~/store/index'
import type { Module } from 'vuex'
import { useQueue } from '~/composables/audio/queue'
import { usePlayer } from '~/composables/audio/player'
import { CLIENT_RADIOS } from '~/utils/clientRadios'
import axios from 'axios'
import useLogger from '~/composables/useLogger'
export interface State {
current: null | CurrentRadio
running: boolean
populating: boolean
}
export interface ObjectId {
username: string
fullUsername: string
}
export interface CurrentRadio {
clientOnly: boolean
session: null
type: 'account'
customRadioId: number
config: RadioConfig
objectId: ObjectId | null
}
export type RadioConfig = { type: 'tag', names: string[] } | { type: 'artist' | 'playlist', ids: string[] }
const logger = useLogger()
const store: Module<State, RootState> = {
namespaced: true,
state: {
current: null,
running: false,
populating: false
},
getters: {
types: () => {
return {
'actor-content': {
name: 'Your content',
description: 'Picks from your own libraries'
},
random: {
name: 'Random',
description: "Totally random picks, maybe you'll discover new things?"
},
random_library: {
name: 'Random',
description: 'Random picks from your library, be surprise by yourself ?'
},
favorites: {
name: 'Favorites',
description: 'Play your favorites tunes in a never-ending happiness loop.'
},
'less-listened': {
name: 'Less listened',
description: "Listen to tracks you usually don't. It's time to restore some balance."
},
'less-listened_library': {
name: 'Less listened',
description: "Listen to tracks from your library you usually don't. It's time to restore some balance."
},
'recently-added': {
name: 'Recently Added',
description: 'Newest content on the network. Get some fresh air.'
}
}
}
},
mutations: {
reset (state) {
state.running = false
state.current = null
state.populating = false
},
current: (state, value) => {
state.current = value
},
running: (state, value) => {
state.running = value
}
},
actions: {
start ({ commit, dispatch }, { type, objectId, customRadioId, clientOnly, config }) {
const params = {
radio_type: type,
related_object_id: objectId,
custom_radio: customRadioId,
config
}
if (clientOnly) {
commit('current', { type, objectId, customRadioId, clientOnly, config })
commit('running', true)
dispatch('populateQueue', true)
return
}
return axios.post('radios/sessions/', params).then((response) => {
logger.info('Successfully started radio ', type)
commit('current', { type, objectId, session: response.data.id, customRadioId })
commit('running', true)
dispatch('populateQueue', true)
}, () => {
logger.error('Error while starting radio', type)
})
},
stop ({ commit, state }) {
if (state.current?.clientOnly) {
CLIENT_RADIOS[state.current.type].stop()
}
commit('current', null)
commit('running', false)
},
async populateQueue ({ commit, state }, playNow) {
if (!state.running || state.populating) {
return
}
state.populating = true
const { enqueue, playTrack, tracks } = useQueue()
const { isPlaying } = usePlayer()
const params = { session: state.current?.session }
try {
logger.info('Adding track to queue from radio')
const track = state.current?.clientOnly
? await CLIENT_RADIOS[state.current.type].fetchNextTrack(state.current)
: await axios.post('radios/tracks/', params).then(response => response.data.track as Track)
if (track === undefined) {
isPlaying.value = false
return
}
await enqueue(track)
if (playNow) {
await playTrack(tracks.value.length - 1)
isPlaying.value = true
}
} catch (error) {
if ((error as BackendError).backendErrors?.[0] === 'Radio doesn\'t have more candidates') {
commit('ui/addMessage', {
content: (error as BackendError).backendErrors?.[0]
}, { root: true })
} else {
logger.error('Error while adding track to queue from radio', error)
}
commit('reset')
} finally {
state.populating = false
}
}
}
}
export default store