2023-08-24 00:42:01 +02:00
|
|
|
<template>
|
2023-12-24 10:18:21 +01:00
|
|
|
<div>
|
2023-12-23 22:49:23 +01:00
|
|
|
<Loading v-if="loading" />
|
2024-01-16 22:12:07 +01:00
|
|
|
<ul v-if="!loading">
|
2024-01-29 23:33:05 +01:00
|
|
|
<NcListItem v-for="episode in filteredEpisodes"
|
2024-01-11 00:14:15 +01:00
|
|
|
:key="episode.guid"
|
2023-08-30 09:27:12 +02:00
|
|
|
:active="isCurrentEpisode(episode)"
|
2024-01-16 10:23:23 +01:00
|
|
|
:class="hasEnded(episode) ? 'ended': ''"
|
2024-02-01 22:30:20 +01:00
|
|
|
:details="formatLocaleDate(new Date(episode.pubDate?.date))"
|
2023-08-24 20:53:54 +02:00
|
|
|
:force-display-actions="true"
|
2024-01-11 00:14:15 +01:00
|
|
|
:name="episode.name"
|
|
|
|
:title="episode.description"
|
2023-12-24 16:34:27 +01:00
|
|
|
@click="modalEpisode = episode">
|
2023-08-24 20:53:54 +02:00
|
|
|
<template #actions>
|
2024-03-04 16:55:16 +01:00
|
|
|
<NcActionButton v-if="!isCurrentEpisode(episode)"
|
|
|
|
:name="t('repod', 'Play')"
|
|
|
|
:title="t('repod', 'Play')"
|
|
|
|
@click="load(episode)">
|
2023-08-24 20:53:54 +02:00
|
|
|
<template #icon>
|
2023-08-29 11:43:17 +02:00
|
|
|
<PlayButton :size="20" />
|
2023-08-24 20:53:54 +02:00
|
|
|
</template>
|
2023-08-29 11:43:17 +02:00
|
|
|
</NcActionButton>
|
2024-03-04 16:55:16 +01:00
|
|
|
<NcActionButton v-if="isCurrentEpisode(episode)"
|
|
|
|
:name="t('repod', 'Stop')"
|
|
|
|
:title="t('repod', 'Stop')"
|
|
|
|
@click="load(null)">
|
2023-08-29 11:43:17 +02:00
|
|
|
<template #icon>
|
|
|
|
<StopButton :size="20" />
|
|
|
|
</template>
|
2023-08-24 20:53:54 +02:00
|
|
|
</NcActionButton>
|
|
|
|
</template>
|
2024-03-04 16:55:16 +01:00
|
|
|
<template #extra>
|
|
|
|
<NcActions>
|
|
|
|
<NcActionButton v-if="episode.duration && !hasEnded(episode)"
|
|
|
|
:disabled="loadingAction"
|
|
|
|
:name="t('repod', 'Mark as read')"
|
|
|
|
:title="t('repod', 'Mark as read')"
|
|
|
|
@click="markAs(episode, true)">
|
|
|
|
<template #icon>
|
|
|
|
<PlaylistPlay :size="20" />
|
|
|
|
</template>
|
|
|
|
</NcActionButton>
|
|
|
|
<NcActionButton v-if="episode.duration && hasEnded(episode)"
|
|
|
|
:disabled="loadingAction"
|
|
|
|
:name="t('repod', 'Mark as unread')"
|
|
|
|
:title="t('repod', 'Mark as unread')"
|
|
|
|
@click="markAs(episode, false)">
|
|
|
|
<template #icon>
|
|
|
|
<PlaylistRemove :size="20" />
|
|
|
|
</template>
|
|
|
|
</NcActionButton>
|
|
|
|
<NcActionLink v-if="episode.link"
|
|
|
|
:href="episode.link"
|
|
|
|
:name="t('repod', 'Episode webpage')"
|
|
|
|
target="_blank"
|
|
|
|
:title="t('repod', 'Episode webpage')">
|
|
|
|
<template #icon>
|
|
|
|
<OpenInNew :size="20" />
|
|
|
|
</template>
|
|
|
|
</NcActionLink>
|
|
|
|
<NcActionLink v-if="episode.url"
|
|
|
|
:href="episode.url"
|
|
|
|
:name="t('repod', 'Download')"
|
|
|
|
target="_blank"
|
|
|
|
:title="t('repod', 'Download')">
|
|
|
|
<template #icon>
|
|
|
|
<Download :size="20" />
|
|
|
|
</template>
|
|
|
|
</NcActionLink>
|
|
|
|
</NcActions>
|
|
|
|
</template>
|
2024-01-30 18:11:50 +01:00
|
|
|
<template #icon>
|
|
|
|
<NcAvatar :display-name="episode.name"
|
|
|
|
:is-no-user="true"
|
|
|
|
:url="episode.image" />
|
|
|
|
</template>
|
|
|
|
<template #indicator>
|
|
|
|
<NcProgressBar v-if="isListening(episode)"
|
|
|
|
class="progress"
|
|
|
|
:value="episode.action.position * 100 / episode.action.total" />
|
|
|
|
</template>
|
|
|
|
<template #subname>
|
|
|
|
{{ episode.duration }}
|
|
|
|
</template>
|
2023-08-24 20:53:54 +02:00
|
|
|
</NcListItem>
|
2024-01-16 22:12:07 +01:00
|
|
|
</ul>
|
2024-01-18 10:12:55 +01:00
|
|
|
<NcModal v-if="modalEpisode" @close="modalEpisode = null">
|
|
|
|
<Modal :description="modalEpisode.description"
|
|
|
|
:image="modalEpisode.image"
|
|
|
|
:link="modalEpisode.link"
|
|
|
|
:name="modalEpisode.name"
|
|
|
|
:size="modalEpisode.size"
|
|
|
|
:title="modalEpisode.title"
|
|
|
|
:url="modalEpisode.url" />
|
|
|
|
</NcModal>
|
2023-08-28 21:18:14 +02:00
|
|
|
</div>
|
2023-08-24 00:42:01 +02:00
|
|
|
</template>
|
|
|
|
|
|
|
|
<script>
|
2024-03-04 16:55:16 +01:00
|
|
|
import { NcActionButton, NcActionLink, NcActions, NcAvatar, NcListItem, NcModal, NcProgressBar } from '@nextcloud/vue'
|
|
|
|
import { durationToSeconds, formatEpisodeTimestamp, formatLocaleDate } from '../../utils/time.js'
|
|
|
|
import Download from 'vue-material-design-icons/Download.vue'
|
2023-12-23 22:49:23 +01:00
|
|
|
import Loading from '../Atoms/Loading.vue'
|
2024-01-17 22:18:32 +01:00
|
|
|
import Modal from '../Atoms/Modal.vue'
|
2024-03-04 16:55:16 +01:00
|
|
|
import OpenInNew from 'vue-material-design-icons/OpenInNew.vue'
|
2023-08-27 22:48:21 +02:00
|
|
|
import PlayButton from 'vue-material-design-icons/Play.vue'
|
2024-03-04 16:55:16 +01:00
|
|
|
import PlaylistPlay from 'vue-material-design-icons/PlaylistPlay.vue'
|
|
|
|
import PlaylistRemove from 'vue-material-design-icons/PlaylistRemove.vue'
|
2023-08-27 22:48:21 +02:00
|
|
|
import StopButton from 'vue-material-design-icons/Stop.vue'
|
2023-08-24 17:43:10 +02:00
|
|
|
import axios from '@nextcloud/axios'
|
2024-01-17 22:18:32 +01:00
|
|
|
import { decodeUrl } from '../../utils/url.js'
|
2023-08-24 17:43:10 +02:00
|
|
|
import { generateUrl } from '@nextcloud/router'
|
|
|
|
import { showError } from '@nextcloud/dialogs'
|
|
|
|
|
2023-08-24 00:42:01 +02:00
|
|
|
export default {
|
2023-08-30 09:27:12 +02:00
|
|
|
name: 'Episodes',
|
2023-08-24 17:43:10 +02:00
|
|
|
components: {
|
2024-03-04 16:55:16 +01:00
|
|
|
Download,
|
2023-12-23 22:49:23 +01:00
|
|
|
Loading,
|
2023-12-24 16:59:34 +01:00
|
|
|
Modal,
|
2023-08-24 20:53:54 +02:00
|
|
|
NcActionButton,
|
2024-03-04 16:55:16 +01:00
|
|
|
NcActionLink,
|
|
|
|
NcActions,
|
2023-08-24 20:53:54 +02:00
|
|
|
NcAvatar,
|
2023-08-24 17:43:10 +02:00
|
|
|
NcListItem,
|
2023-12-24 16:34:27 +01:00
|
|
|
NcModal,
|
2024-01-29 16:06:54 +01:00
|
|
|
NcProgressBar,
|
2024-03-04 16:55:16 +01:00
|
|
|
OpenInNew,
|
2023-08-27 22:48:21 +02:00
|
|
|
PlayButton,
|
2024-03-04 16:55:16 +01:00
|
|
|
PlaylistPlay,
|
|
|
|
PlaylistRemove,
|
2023-08-27 22:48:21 +02:00
|
|
|
StopButton,
|
2023-08-24 17:43:10 +02:00
|
|
|
},
|
|
|
|
data() {
|
|
|
|
return {
|
|
|
|
episodes: [],
|
|
|
|
loading: true,
|
2024-03-04 16:55:16 +01:00
|
|
|
loadingAction: false,
|
2023-12-24 16:34:27 +01:00
|
|
|
modalEpisode: null,
|
2023-08-24 17:43:10 +02:00
|
|
|
}
|
|
|
|
},
|
|
|
|
computed: {
|
2023-08-27 22:48:21 +02:00
|
|
|
currentEpisode() {
|
2023-08-24 23:59:55 +02:00
|
|
|
return this.$store.state.player.episode
|
|
|
|
},
|
2024-01-29 23:33:05 +01:00
|
|
|
filters() {
|
|
|
|
return this.$store.state.settings.filters
|
|
|
|
},
|
|
|
|
filteredEpisodes() {
|
|
|
|
return this.episodes.filter((episode) => {
|
|
|
|
if (!this.filters.listened && this.hasEnded(episode)) {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!this.filters.listening && this.isListening(episode)) {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
2024-01-30 16:05:58 +01:00
|
|
|
if (!this.filters.unlistened && !this.isListening(episode)) {
|
2024-01-29 23:33:05 +01:00
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
|
|
|
return true
|
|
|
|
})
|
|
|
|
},
|
2023-08-24 17:43:10 +02:00
|
|
|
url() {
|
2024-01-11 21:41:37 +01:00
|
|
|
return decodeUrl(this.$route.params.url)
|
2023-08-24 17:43:10 +02:00
|
|
|
},
|
|
|
|
},
|
|
|
|
async mounted() {
|
|
|
|
try {
|
|
|
|
this.loading = true
|
2023-08-29 08:48:54 +02:00
|
|
|
const episodes = await axios.get(generateUrl('/apps/repod/episodes/list?url={url}', { url: this.url }))
|
2024-02-01 22:30:20 +01:00
|
|
|
this.episodes = [...episodes.data].sort((a, b) => new Date(b.pubDate?.date) - new Date(a.pubDate?.date))
|
2023-08-24 17:43:10 +02:00
|
|
|
} catch (e) {
|
|
|
|
console.error(e)
|
2024-01-10 15:25:54 +01:00
|
|
|
showError(t('repod', 'Could not fetch episodes'))
|
2023-08-24 17:43:10 +02:00
|
|
|
} finally {
|
|
|
|
this.loading = false
|
|
|
|
}
|
|
|
|
},
|
2023-08-24 20:53:54 +02:00
|
|
|
methods: {
|
2024-01-29 15:47:50 +01:00
|
|
|
formatLocaleDate,
|
2024-01-16 10:23:23 +01:00
|
|
|
hasEnded(episode) {
|
2024-03-04 16:55:16 +01:00
|
|
|
return episode.action
|
|
|
|
&& episode.action.position > 0
|
2024-01-20 09:58:08 +01:00
|
|
|
&& episode.action.total > 0
|
2024-01-16 10:23:23 +01:00
|
|
|
&& episode.action.position >= episode.action.total
|
|
|
|
},
|
2023-08-27 22:48:21 +02:00
|
|
|
isCurrentEpisode(episode) {
|
2024-01-11 00:14:15 +01:00
|
|
|
return this.currentEpisode && this.currentEpisode.url === episode.url
|
2023-08-27 22:48:21 +02:00
|
|
|
},
|
2024-01-29 23:33:05 +01:00
|
|
|
isListening(episode) {
|
|
|
|
return episode.action && episode.action.action === 'PLAY' && !this.hasEnded(episode)
|
|
|
|
},
|
2023-08-29 00:47:22 +02:00
|
|
|
load(episode) {
|
2023-08-29 08:48:54 +02:00
|
|
|
this.$store.dispatch('player/load', episode)
|
2023-08-24 23:19:54 +02:00
|
|
|
},
|
2024-03-04 16:55:16 +01:00
|
|
|
async markAs(episode, read) {
|
|
|
|
try {
|
|
|
|
this.loadingAction = true
|
2024-03-05 00:14:21 +01:00
|
|
|
const action = {
|
2024-03-04 16:55:16 +01:00
|
|
|
podcast: this.url,
|
|
|
|
episode: episode.url,
|
|
|
|
guid: episode.guid,
|
|
|
|
action: 'play',
|
|
|
|
timestamp: formatEpisodeTimestamp(new Date()),
|
|
|
|
started: episode.action ? episode.action.started : 0,
|
|
|
|
position: read ? durationToSeconds(episode.duration) : 0,
|
|
|
|
total: durationToSeconds(episode.duration),
|
2024-03-05 00:14:21 +01:00
|
|
|
}
|
|
|
|
await axios.post(generateUrl('/apps/gpoddersync/episode_action/create'), [action])
|
|
|
|
this.episodes = this.episodes.map((e) => {
|
|
|
|
if (e.url === episode.url) {
|
|
|
|
e.action = action
|
|
|
|
}
|
|
|
|
return e
|
|
|
|
})
|
2024-03-04 16:55:16 +01:00
|
|
|
} catch (e) {
|
|
|
|
console.error(e)
|
|
|
|
showError(t('repod', 'Could not change the status of the episode'))
|
|
|
|
} finally {
|
|
|
|
this.loadingAction = false
|
|
|
|
}
|
|
|
|
},
|
2023-08-24 20:53:54 +02:00
|
|
|
},
|
2023-08-24 00:42:01 +02:00
|
|
|
}
|
|
|
|
</script>
|
2023-08-30 19:59:20 +02:00
|
|
|
|
|
|
|
<style scoped>
|
|
|
|
.ended {
|
2024-01-17 22:58:17 +01:00
|
|
|
opacity: .4;
|
2023-08-30 19:59:20 +02:00
|
|
|
}
|
2024-01-29 16:06:54 +01:00
|
|
|
|
|
|
|
.progress {
|
|
|
|
margin-top: .4rem;
|
|
|
|
}
|
2023-08-30 19:59:20 +02:00
|
|
|
</style>
|