diff --git a/src/components/Player.vue b/src/components/Player.vue index ef89800d134aa3121a8d7624ff4809a9ee7b03a7..17cffe246333f35ade42c6a077542338d8d13b7f 100644 --- a/src/components/Player.vue +++ b/src/components/Player.vue @@ -22,7 +22,9 @@ <template> <div id="app-settings"> - <div class="playerThumb" /> + <div + v-lazy:background-image="getEpisodeImage" + class="playerThumb" /> <input class="seek" type="range" @@ -31,7 +33,7 @@ max="1" step=".05" value="0"> - <span>Hallo Test</span> + <span class="playerTitle">{{ getEpisodeTitle }}</span> <div class="playerControls"> <div class="seekButton seekPrev" /> <div @@ -64,11 +66,30 @@ </template> <script> +import { mapGetters } from 'vuex' + export default { computed: { + ...mapGetters([ + 'episodeTitle', + 'episodeImage', + 'playerVolume', + ]), player() { return this.$store.state.player }, + getEpisodeTitle() { + const title = this.episodeTitle + return title + }, + getEpisodeImage() { + const image = this.episodeImage + return image + }, + getPlayerVolume() { + const volume = this.playerVolume + return volume + }, }, methods: { changeVolume() { @@ -99,12 +120,21 @@ export default { width: 200px; height: 200px; background: #ddd; + background-size: cover; + background-position: center; } .seek { width: 200px; } + .playerTitle { + max-width: 200px; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + } + .playerControls { display: flex; align-items: center; @@ -117,6 +147,17 @@ export default { border: 2px solid #0082c9; border-radius: 50%; cursor: pointer; + background-repeat: no-repeat; + background-position: 40% 55%; + } + + .seekNext { + background-image: var(--icon-play-next-000); + background-position: 50% 55%; + } + + .seekPrev { + background-image: var(--icon-play-previous-000); } .wrap { diff --git a/src/components/Table.vue b/src/components/Table.vue index fd1ad98c6638fac2890429379deff3489cace6b2..80730fa87ee0412a349ed0c26a4eeced5db5b60b 100644 --- a/src/components/Table.vue +++ b/src/components/Table.vue @@ -41,7 +41,7 @@ <tr v-for="(episode, idx) in episodes" :key="idx" - :class="{ selected: idx === activeItem}"> + :class="{ selected: isPlaying(episode.id)}"> <td class="iconColumn"> <div v-lazy:background-image="episode.imgURL" class="episodeImage" /> @@ -77,9 +77,9 @@ <td class="actionColumn"> <Actions> <ActionButton - icon="icon-play" + :icon="isPlaying(episode.id) ? 'icon-pause' : 'icon-play'" :close-after-click="true" - @click="doPlay(idx, episode)"> + @click="doPlay(episode)"> {{ t('podcast', 'Play') }} </ActionButton> <ActionButton @@ -141,10 +141,10 @@ export default { default() { return [] }, }, }, - data: () => ({ - activeItem: null, - }), methods: { + isPlaying(episodeId) { + return this.$store.getters.isPlaying(episodeId) + }, escapedEpisodeDescription(episodeDescription) { episodeDescription = episodeDescription.replace(/\n/g, '') return episodeDescription @@ -159,8 +159,7 @@ export default { downloadFile(episodeURL) { window.open(episodeURL, 'download') }, - doPlay(idx, episode) { - this.activeItem = idx + doPlay(episode) { this.$emit('doPlay', episode) }, changeRoute(path) { diff --git a/src/services/Player.js b/src/services/Player.js index 93ea8a254158890658db7aa56b7111f22219702e..d52ce507769077cfb2f03ab40dd21d66470f5bac 100644 --- a/src/services/Player.js +++ b/src/services/Player.js @@ -22,39 +22,40 @@ import { Howl, Howler } from 'howler' import { showError } from '@nextcloud/dialogs' +import store from './../store/main.js' let audioPlayer = null export class Player { - play(src) { - this.$store.dispatch('isBuffering', true) + doPlay(src) { + if (audioPlayer !== null) { - audioPlayer.fade(this.player.volume, 0, 500) + audioPlayer.fade(0, 0, 500) } - this.$store.dispatch('setTitle', 'test') Howler.unload() audioPlayer = new Howl({ src, html5: true, - volume: this.player.volume, + volume: 1, onplay() { - this.$store.dispatch('isPlaying', true) - this.$store.dispatch('isBuffering', false) + store.dispatch('setPlaying', true) + store.dispatch('setBuffering', false) }, onpause() { - this.$store.dispatch('isPlaying', false) - this.$store.dispatch('isBuffering', false) + store.dispatch('setPlaying', false) + store.dispatch('isBuffering', false) }, onend() { showError(t('podcast', 'Lost connection to podcast station, retrying ...')) - this.$store.dispatch('isPlaying', false) - this.$store.dispatch('isBuffering', true) + store.dispatch('setPlaying', false) + store.dispatch('setBuffering', true) }, }) audioPlayer.unload() audioPlayer.play() - audioPlayer.fade(0, this.player.volume, 500) + audioPlayer.fade(0, 1, 500) + } } diff --git a/src/store/player.js b/src/store/player.js index 45944b0b0df1fa87bdc69ba9cc164bdbc6dc57a8..ade841be387816b6016c2895f481f0ff80254641 100644 --- a/src/store/player.js +++ b/src/store/player.js @@ -22,6 +22,8 @@ import axios from '@nextcloud/axios' import { generateUrl } from '@nextcloud/router' +import { Player } from './../services/Player' +const player = new Player() const requesttoken = axios.defaults.headers.requesttoken export default { @@ -34,38 +36,51 @@ export default { oldVolume: 0, title: '', episodePlaying: null, + image: '', + episodeId: null, + }, + getters: { + episodeTitle: state => { + return state.title + }, + episodeImage: state => { + return state.image + }, + isPlaying: state => (id) => { + return state.episodeId === id + }, }, mutations: { isPlaying(state, playerState) { - state.player.isPlaying = playerState + state.isPlaying = playerState }, isBuffering(state, bufferingState) { - state.player.isBuffering = bufferingState + state.isBuffering = bufferingState }, changeVolume(state, volume) { - state.player.volume = volume + state.volume = volume }, toggleMute(state) { - if (state.player.isMute) { - state.player.volume = state.player.oldVolume - state.player.isMute = false + if (state.isMute) { + state.volume = state.oldVolume + state.isMute = false } else { - state.player.oldVolume = state.player.volume - state.player.volume = 0 - state.player.isMute = true + state.oldVolume = state.volume + state.volume = 0 + state.isMute = true } }, togglePlay(state) { - if (state.player.isPlaying) { - state.player.isPlaying = false - state.player.isPaused = true + if (state.isPlaying) { + state.isPlaying = false + state.isPaused = true } else { - state.player.isPlaying = true - state.player.isPaused = false + state.isPlaying = true + state.isPaused = false } }, setTitle(state, title) { - state.player.title = title + state.title = title }, setVolumeState(state, volumeState) { axios.defaults.headers.requesttoken = requesttoken @@ -81,18 +96,27 @@ export default { const { data: { volumeState: value }, } = response - state.player.volume = value + state.volume = value }) }, + setBuffering(state, bufferingState) { + state.isBuffering = bufferingState + }, + setImage(state, image) { + state.image = image + }, + setEpisodeId(state, episodeId) { + state.episodeId = episodeId + }, }, actions: { - isPlaying(context, playerState) { + setPlaying(context, playerState) { context.commit('isPlaying', playerState) }, - isBuffering(context, bufferingState) { + setBuffering(context, bufferingState) { context.commit('isBuffering', bufferingState) }, - changeVolume(context, volume) { + setVolume(context, volume) { context.commit('changeVolume', volume) }, toggleMute(context) { @@ -101,14 +125,19 @@ export default { togglePlay(context) { context.commit('togglePlay') }, - setTitle(context, title) { - context.commit('setTitle', title) - }, setVolumeState(context, volumeState) { context.commit('setVolumeState', volumeState) }, getVolumeState(context) { context.commit('getVolumeState') }, + playEpisode(context, episode) { + console.log(episode) + context.commit('setBuffering', true) + context.commit('setTitle', episode.title) + context.commit('setImage', episode.imgURL) + context.commit('setEpisodeId', episode.id) + player.doPlay(episode.enclosure) + }, }, } diff --git a/src/store/show.js b/src/store/show.js index 82615542d1ab4267c3772ecfe4ca54388983739d..4ba703ae1b2e780c4c717baf7e3f787cd0a62bb0 100644 --- a/src/store/show.js +++ b/src/store/show.js @@ -38,7 +38,6 @@ export default { showExists: state => (id) => { return state.shows.some((show) => show.id === id) }, - }, mutations: { addShow(state, show) { diff --git a/src/views/Episode.vue b/src/views/Episode.vue index 61f04677e3f54428784a441f7ccaa318f39bf69e..911fe77e0e843246fbe12de8a0fe8d315339dc82 100644 --- a/src/views/Episode.vue +++ b/src/views/Episode.vue @@ -31,9 +31,10 @@ :htmlurl="`#/browse/show/${podcastId}`" :isshow="false"> <button - class="icon-play-white podcastButton button primary new-button" + class="podcastButton button primary new-button" + :class="isPlaying(episode.id) ? 'icon-pause-white' : 'icon-play-white'" @click="doPlay"> - {{ t('podcast', 'Play episode') }} + {{ playButtonText }} </button> <div class="episodeDetails"> <span> @@ -86,10 +87,8 @@ import MediaHeader from '../components/MediaHeader' import TimeAgo from 'javascript-time-ago' import { FyydApi } from './../services/FyydApi' -import { Player } from './../services/Player' const fyydClient = new FyydApi() -const player = new Player() const timeAgo = new TimeAgo('en-US') @@ -107,6 +106,15 @@ export default { podcastId: null, podcastName: null, }), + computed: { + playButtonText() { + if (this.isPlaying(this.episode.id)) { + return t('podcast', 'Pause episode') + } else { + return t('podcast', 'Play episode') + } + }, + }, mounted() { this.episodeId = this.$route.params.episodeId this.podcastId = this.$route.params.id @@ -115,8 +123,12 @@ export default { }, methods: { + isPlaying(episodeId) { + return this.$store.getters.isPlaying(episodeId) + }, + doPlay() { - player.play(this.episode.enclosure) + this.$store.dispatch('playEpisode', this.episode) }, readableDuration(timestamp) { diff --git a/src/views/Show.vue b/src/views/Show.vue index 112e27e0839deb348ea18f53d66d4032ddfd45ab..932fc6f99834052628f432f6cb2d08ba8be75301 100644 --- a/src/views/Show.vue +++ b/src/views/Show.vue @@ -73,9 +73,6 @@ export default { loading: true, }), computed: { - player() { - return this.$store.state.player - }, isSubscribed() { return this.$store.getters.showExists(this.podcastId) }, @@ -162,6 +159,10 @@ export default { } } }, + + doPlay(episode) { + this.$store.dispatch('playEpisode', episode) + }, }, } </script>