Adds volume control
This commit is contained in:
parent
8022929dc1
commit
a0de1f0c5a
@ -47,6 +47,16 @@
|
|||||||
<!-- Controls right --->
|
<!-- Controls right --->
|
||||||
<div class="col-auto col-sm p-0">
|
<div class="col-auto col-sm p-0">
|
||||||
<div class="d-flex flex-nowrap justify-content-end pr-3">
|
<div class="d-flex flex-nowrap justify-content-end pr-3">
|
||||||
|
<div class="m-0 d-none d-sm-inline-flex align-items-center pr-3">
|
||||||
|
<Icon class="volume-icon" icon="volume-up-fill" />
|
||||||
|
<b-form-input
|
||||||
|
:value="volume"
|
||||||
|
type="range"
|
||||||
|
min="0"
|
||||||
|
max="1"
|
||||||
|
step="0.05"
|
||||||
|
@input="setVolume" />
|
||||||
|
</div>
|
||||||
<b-button variant="link"
|
<b-button variant="link"
|
||||||
class="m-0 d-none d-sm-inline-block"
|
class="m-0 d-none d-sm-inline-block"
|
||||||
:class="{ 'text-primary': shuffleActive }"
|
:class="{ 'text-primary': shuffleActive }"
|
||||||
@ -60,6 +70,18 @@
|
|||||||
<Icon icon="arrow-repeat" />
|
<Icon icon="arrow-repeat" />
|
||||||
</b-button>
|
</b-button>
|
||||||
<OverflowMenu class="d-sm-none">
|
<OverflowMenu class="d-sm-none">
|
||||||
|
<b-dropdown-text>
|
||||||
|
<div class="d-flex justify-content-between">
|
||||||
|
<strong class="pr-1">Volume</strong>
|
||||||
|
<b-form-input
|
||||||
|
:value="volume"
|
||||||
|
type="range"
|
||||||
|
min="0"
|
||||||
|
max="1"
|
||||||
|
step="0.05"
|
||||||
|
@input="setVolume" />
|
||||||
|
</div>
|
||||||
|
</b-dropdown-text>
|
||||||
<b-dropdown-text>
|
<b-dropdown-text>
|
||||||
<div class="d-flex justify-content-between">
|
<div class="d-flex justify-content-between">
|
||||||
<strong>Repeat</strong>
|
<strong>Repeat</strong>
|
||||||
@ -109,6 +131,7 @@
|
|||||||
repeatActive: (state: any) => state.repeat,
|
repeatActive: (state: any) => state.repeat,
|
||||||
shuffleActive: (state: any) => state.shuffle,
|
shuffleActive: (state: any) => state.shuffle,
|
||||||
visible: (state: any) => state.queue.length > 0,
|
visible: (state: any) => state.queue.length > 0,
|
||||||
|
volume: (state: any) => state.volume,
|
||||||
}),
|
}),
|
||||||
...mapGetters('player', [
|
...mapGetters('player', [
|
||||||
'track',
|
'track',
|
||||||
@ -130,6 +153,9 @@
|
|||||||
return this.$store.dispatch('player/seek', value)
|
return this.$store.dispatch('player/seek', value)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
setVolume(volume: any) {
|
||||||
|
return this.$store.dispatch('player/setVolume', parseFloat(volume))
|
||||||
|
},
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
|
@ -5,6 +5,7 @@ import { API } from '@/shared/api'
|
|||||||
const audio = new Audio()
|
const audio = new Audio()
|
||||||
const storedQueue = JSON.parse(localStorage.getItem('queue') || '[]')
|
const storedQueue = JSON.parse(localStorage.getItem('queue') || '[]')
|
||||||
const storedQueueIndex = parseInt(localStorage.getItem('queueIndex') || '-1')
|
const storedQueueIndex = parseInt(localStorage.getItem('queueIndex') || '-1')
|
||||||
|
const storedVolume = parseFloat(localStorage.getItem('player.volume') || '0.8')
|
||||||
if (storedQueueIndex > -1 && storedQueueIndex < storedQueue.length) {
|
if (storedQueueIndex > -1 && storedQueueIndex < storedQueue.length) {
|
||||||
audio.src = storedQueue[storedQueueIndex].url
|
audio.src = storedQueue[storedQueueIndex].url
|
||||||
}
|
}
|
||||||
@ -19,6 +20,7 @@ interface State {
|
|||||||
currentTime: number; // position of current track in seconds
|
currentTime: number; // position of current track in seconds
|
||||||
repeat: boolean;
|
repeat: boolean;
|
||||||
shuffle: boolean;
|
shuffle: boolean;
|
||||||
|
volume: number; // integer between 0 and 1 representing the volume of the player
|
||||||
}
|
}
|
||||||
|
|
||||||
export const playerModule: Module<State, any> = {
|
export const playerModule: Module<State, any> = {
|
||||||
@ -32,6 +34,7 @@ export const playerModule: Module<State, any> = {
|
|||||||
currentTime: 0,
|
currentTime: 0,
|
||||||
repeat: localStorage.getItem('player.repeat') !== 'false',
|
repeat: localStorage.getItem('player.repeat') !== 'false',
|
||||||
shuffle: localStorage.getItem('player.shuffle') === 'true',
|
shuffle: localStorage.getItem('player.shuffle') === 'true',
|
||||||
|
volume: storedVolume,
|
||||||
},
|
},
|
||||||
|
|
||||||
mutations: {
|
mutations: {
|
||||||
@ -102,6 +105,10 @@ export const playerModule: Module<State, any> = {
|
|||||||
setScrobbled(state) {
|
setScrobbled(state) {
|
||||||
state.scrobbled = true
|
state.scrobbled = true
|
||||||
},
|
},
|
||||||
|
setVolume(state, value: any) {
|
||||||
|
state.volume = value
|
||||||
|
localStorage.setItem('player.volume', String(value))
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
actions: {
|
actions: {
|
||||||
@ -176,6 +183,10 @@ export const playerModule: Module<State, any> = {
|
|||||||
setNextInQueue({ commit }, track) {
|
setNextInQueue({ commit }, track) {
|
||||||
commit('setNextInQueue', track)
|
commit('setNextInQueue', track)
|
||||||
},
|
},
|
||||||
|
setVolume({ commit }, value) {
|
||||||
|
audio.volume = value
|
||||||
|
commit('setVolume', value)
|
||||||
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
getters: {
|
getters: {
|
||||||
@ -207,6 +218,7 @@ export const playerModule: Module<State, any> = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function setupAudio(store: Store<any>, api: API) {
|
export function setupAudio(store: Store<any>, api: API) {
|
||||||
|
audio.volume = store.state.player.volume
|
||||||
audio.ontimeupdate = () => {
|
audio.ontimeupdate = () => {
|
||||||
store.commit('player/setCurrentTime', audio.currentTime)
|
store.commit('player/setCurrentTime', audio.currentTime)
|
||||||
|
|
||||||
|
@ -28,6 +28,7 @@
|
|||||||
BIconPersonCircle,
|
BIconPersonCircle,
|
||||||
BIconRss,
|
BIconRss,
|
||||||
BIconX,
|
BIconX,
|
||||||
|
BIconVolumeUpFill,
|
||||||
} from 'bootstrap-vue'
|
} from 'bootstrap-vue'
|
||||||
|
|
||||||
export default Vue.extend({
|
export default Vue.extend({
|
||||||
@ -56,6 +57,7 @@
|
|||||||
BIconPersonCircle,
|
BIconPersonCircle,
|
||||||
BIconRss,
|
BIconRss,
|
||||||
BIconX,
|
BIconX,
|
||||||
|
BIconVolumeUpFill,
|
||||||
},
|
},
|
||||||
props: {
|
props: {
|
||||||
icon: { type: String, required: true }
|
icon: { type: String, required: true }
|
||||||
|
@ -34,6 +34,9 @@ $dropdown-divider-bg: $theme-elevation-2;
|
|||||||
$input-bg: $theme-elevation-2;
|
$input-bg: $theme-elevation-2;
|
||||||
$input-border-color: $theme-elevation-2;
|
$input-border-color: $theme-elevation-2;
|
||||||
$input-color: $theme-text;
|
$input-color: $theme-text;
|
||||||
|
$custom-range-track-height: 0.1rem;
|
||||||
|
$custom-range-thumb-bg: $theme-text;
|
||||||
|
$custom-range-track-bg: $theme-text-muted;
|
||||||
|
|
||||||
// Other
|
// Other
|
||||||
$progress-bg: rgb(35, 35, 35);
|
$progress-bg: rgb(35, 35, 35);
|
||||||
@ -63,6 +66,11 @@ h1, h2, h3, h4, h5 {
|
|||||||
box-shadow: none !important;
|
box-shadow: none !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.volume-icon {
|
||||||
|
font-size: 2.25rem;
|
||||||
|
padding: 0.375rem;
|
||||||
|
}
|
||||||
|
|
||||||
@import './nav';
|
@import './nav';
|
||||||
@import './table';
|
@import './table';
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user