<!-- - @copyright Copyright (c) 2020 Jonas Heinrich - - @author Jonas Heinrich <onny@project-insanity.org> - - @license GNU AGPL version 3 or any later version - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU Affero General Public License as - published by the Free Software Foundation, either version 3 of the - License, or (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU Affero General Public License for more details. - - You should have received a copy of the GNU Affero General Public License - along with this program. If not, see <http://www.gnu.org/licenses/>. - --> <template> <Content app-name="podcast"> <Navigation :station-data="tableData" /> <AppContent> <div class="podcastHeader"> <div class="podcastImage" :style="{ backgroundImage: `url(${podcast.smallImageURL})` }" /> <div class="podcastDescription"> <h1>{{ podcast.title }}</h1> <div class="podcastAuthor"> by <a href="#">{{ podcast.author }}</a> </div> <div>{{ podcast.description }}</div> <ul v-for="(category, idx) in podcast.categories" :key="idx" :class="podcastCategory"> <li>{{ podcast.categories[idx] }}</li> </ul> </div> </div> <Table v-show="!pageLoading && podcast.episodes.length > 0" v-resize="onResize" :episodes="podcast.episodes" @doPlay="doPlay" /> <EmptyContent v-if="pageLoading" icon="icon-loading" /> <EmptyContent v-if="tableData.length === 0 && !pageLoading" :icon="emptyContentIcon"> {{ emptyContentMessage }} <template #desc> {{ emptyContentDesc }} </template> </EmptyContent> </AppContent> </Content> </template> <script> import Content from '@nextcloud/vue/dist/Components/Content' import AppContent from '@nextcloud/vue/dist/Components/AppContent' import EmptyContent from '@nextcloud/vue/dist/Components/EmptyContent' import Navigation from '../components/Navigation' import Table from '../components/Table' import { Howl, Howler } from 'howler' import { showError } from '@nextcloud/dialogs' import axios from '@nextcloud/axios' let audioPlayer = null export default { name: 'Show', components: { Navigation, Content, AppContent, Table, EmptyContent, }, data: () => ({ podcast: {}, tableData: [], pageLoading: false, queryParams: {}, }), computed: { player() { return this.$store.state.player }, emptyContentMessage() { if (this.$route.name === 'FAVORITES') { return t('podcast', 'No favorites yet') } else if (this.$route.name === 'RECENT') { return t('podcast', 'No recent stations yet') } else if (this.$route.name === 'SEARCH') { return t('podcast', 'No search results') } return 'No stations here' }, emptyContentIcon() { if (this.$route.name === 'FAVORITES') { return 'icon-star' } else if (this.$route.name === 'RECENT') { return 'icon-recent' } else if (this.$route.name === 'SEARCH') { return 'icon-search' } return 'icon-podcast' }, emptyContentDesc() { if (this.$route.name === 'FAVORITES') { return t('podcast', 'Stations you mark as favorite will show up here') } else if (this.$route.name === 'RECENT') { return t('podcast', 'Stations you recently played will show up here') } else if (this.$route.name === 'SEARCH') { return t('podcast', 'No stations were found matching your search term') } return t('podcast', 'No stations here') }, }, watch: { $route: 'onRoute', 'player.volume'(newVolume, oldVolume) { if (audioPlayer !== null) { audioPlayer.volume(newVolume) } }, 'player.isPaused'(newState, oldState) { if (newState === true && audioPlayer !== null) { audioPlayer.pause() } else if (newState === false && audioPlayer !== null) { audioPlayer.play() } }, }, created() { this.loadSettings() }, mounted() { this.onRoute() this.scroll() }, methods: { onResize({ width, height }) { const contentHeight = document.getElementById('app-content-vue').scrollHeight const tableHeight = height if (tableHeight < contentHeight) { this.preFill() } }, preFill() { const route = this.$route console.log(route) // this.queryPodcast(route.name) }, async onRoute() { this.tableData = [] this.pageLoading = true const podcastId = this.$route.params.id this.queryPodcast(podcastId) }, /** * Start playing a podcast episode * @param {Object} episode Episode object */ async doPlay(episode) { const vm = this vm.$store.dispatch('isBuffering', true) if (audioPlayer !== null) { audioPlayer.fade(vm.player.volume, 0, 500) } vm.$store.dispatch('setTitle', episode.title) Howler.unload() audioPlayer = new Howl({ src: episode.enclosure, html5: true, volume: vm.player.volume, onplay() { vm.$store.dispatch('isPlaying', true) vm.$store.dispatch('isBuffering', false) }, onpause() { vm.$store.dispatch('isPlaying', false) vm.$store.dispatch('isBuffering', false) }, onend() { showError(t('podcast', 'Lost connection to podcast station, retrying ...')) vm.$store.dispatch('isPlaying', false) vm.$store.dispatch('isBuffering', true) }, }) audioPlayer.unload() audioPlayer.play() audioPlayer.fade(0, vm.player.volume, 500) }, async queryPodcast(podcastId) { const vm = this podcastId = 1084 const queryURI = 'https://api.fyyd.de/0.2/podcast/episodes?podcast_id=' + podcastId try { delete axios.defaults.headers.requesttoken await axios.get(queryURI) .then(function(response) { vm.processPodcast(response.data) }) } catch (error) { showError(t('podcast', 'Could not fetch stations from remote API')) } }, processPodcast(podcast) { this.podcast = podcast.data this.pageLoading = false }, /** * On scroll event, load more stations if bottom reached */ scroll() { window.onscroll = () => { if ((window.innerHeight + window.scrollY) >= document.body.scrollHeight) { const route = this.$route console.log(route) // this.queryPodcast(route.name) } } }, loadSettings() { // axios.defaults.headers.common = { // 'User-Agent': 'Nextcloud Podcast App/' + this.$version, // } this.$store.dispatch('getVolumeState') }, }, } </script> <style lang="scss"> @media only screen and (min-width: 1024px) { .app-navigation-toggle { display: none; } } .podcastHeader { width: 100%; background: black; min-height: 300px; display: flex; color: white; justify-content: center; padding: 40px 20px; gap: 30px; .podcastImage { width: 230px; height: 230px; background: red; background-size: cover; background-position: center center; } .podcastDescription { max-width: 500px; max-height: 200px; overflow: hidden; text-overflow: ellipsis; color: #ddd; h1 { font-size: 30px; font-weight: bold; line-height: 1.2em; color: white; } .podcastAuthor { padding-bottom: 10px; a { color: white; } } ul.podcastCategory li { padding: 5px; background: red; } } } </style>