feat: add playback speed setting (close #39)
All checks were successful
repod / xml (push) Successful in 22s
repod / php (push) Successful in 1m7s
repod / nodejs (push) Successful in 2m11s
repod / release (push) Has been skipped

This commit is contained in:
Michel Roux 2024-01-16 22:12:07 +01:00
parent 31bf193543
commit 9ebd96cec1
14 changed files with 175 additions and 68 deletions

View File

@ -19,6 +19,7 @@ OC.L10N.register(
"Delete" : "Supprimer", "Delete" : "Supprimer",
"Are you sure you want to delete this subscription?" : "Êtes-vous sûr de vouloir supprimer ce flux ?", "Are you sure you want to delete this subscription?" : "Êtes-vous sûr de vouloir supprimer ce flux ?",
"Error while removing the feed" : "Erreur lors de la suppression du flux", "Error while removing the feed" : "Erreur lors de la suppression du flux",
"Playback speed" : "Vitesse de lecture",
"Add a podcast" : "Ajouter un podcast", "Add a podcast" : "Ajouter un podcast",
"Could not fetch subscriptions" : "Impossible de récupérer les flux", "Could not fetch subscriptions" : "Impossible de récupérer les flux",
"Find a podcast" : "Chercher un podcast", "Find a podcast" : "Chercher un podcast",

View File

@ -17,6 +17,7 @@
"Delete" : "Supprimer", "Delete" : "Supprimer",
"Are you sure you want to delete this subscription?" : "Êtes-vous sûr de vouloir supprimer ce flux ?", "Are you sure you want to delete this subscription?" : "Êtes-vous sûr de vouloir supprimer ce flux ?",
"Error while removing the feed" : "Erreur lors de la suppression du flux", "Error while removing the feed" : "Erreur lors de la suppression du flux",
"Playback speed" : "Vitesse de lecture",
"Add a podcast" : "Ajouter un podcast", "Add a podcast" : "Ajouter un podcast",
"Could not fetch subscriptions" : "Impossible de récupérer les flux", "Could not fetch subscriptions" : "Impossible de récupérer les flux",
"Find a podcast" : "Chercher un podcast", "Find a podcast" : "Chercher un podcast",

View File

@ -1,22 +0,0 @@
<template>
<ul :class="episode ? 'margin' : ''">
<slot />
</ul>
</template>
<script>
export default {
name: 'AdaptativeList',
computed: {
episode() {
return this.$store.state.player.episode
},
},
}
</script>
<style scoped>
.margin {
margin-bottom: 6rem;
}
</style>

View File

@ -0,0 +1,27 @@
<template>
<NcAppContent :class="episode ? 'margin' : ''">
<slot />
</NcAppContent>
</template>
<script>
import { NcAppContent } from '@nextcloud/vue'
export default {
name: 'AppContent',
components: {
NcAppContent,
},
computed: {
episode() {
return this.$store.state.player.episode
},
},
}
</script>
<style scoped>
.margin {
padding-bottom: 6rem;
}
</style>

View File

@ -0,0 +1,33 @@
<template>
<NcAppNavigation :class="episode ? 'margin' : ''">
<slot />
<template #list>
<slot name="list" />
</template>
<template #footer>
<slot name="footer" />
</template>
</NcAppNavigation>
</template>
<script>
import { NcAppNavigation } from '@nextcloud/vue'
export default {
name: 'AppNavigation',
components: {
NcAppNavigation,
},
computed: {
episode() {
return this.$store.state.player.episode
},
},
}
</script>
<style scoped>
.margin {
padding-bottom: 6rem;
}
</style>

View File

@ -1,7 +1,7 @@
<template> <template>
<div> <div>
<Loading v-if="loading" /> <Loading v-if="loading" />
<AdaptativeList v-if="!loading"> <ul v-if="!loading">
<NcListItem v-for="feed in feeds" <NcListItem v-for="feed in feeds"
:key="feed.link" :key="feed.link"
:details="moment(feed.fetchedAtUnix*1000).fromNow()" :details="moment(feed.fetchedAtUnix*1000).fromNow()"
@ -16,13 +16,12 @@
{{ feed.author }} {{ feed.author }}
</template> </template>
</NcListItem> </NcListItem>
</AdaptativeList> </ul>
</div> </div>
</template> </template>
<script> <script>
import { NcAvatar, NcListItem } from '@nextcloud/vue' import { NcAvatar, NcListItem } from '@nextcloud/vue'
import AdaptativeList from '../Atoms/AdaptativeList.vue'
import Loading from '../Atoms/Loading.vue' import Loading from '../Atoms/Loading.vue'
import axios from '@nextcloud/axios' import axios from '@nextcloud/axios'
import { debounce } from '../../utils/debounce.js' import { debounce } from '../../utils/debounce.js'
@ -34,7 +33,6 @@ import { toUrl } from '../../utils/url.js'
export default { export default {
name: 'Search', name: 'Search',
components: { components: {
AdaptativeList,
Loading, Loading,
NcAvatar, NcAvatar,
NcListItem, NcListItem,

View File

@ -1,7 +1,7 @@
<template> <template>
<div> <div>
<Loading v-if="loading" /> <Loading v-if="loading" />
<AdaptativeList v-if="!loading"> <ul v-if="!loading">
<NcListItem v-for="episode in episodes" <NcListItem v-for="episode in episodes"
:key="episode.guid" :key="episode.guid"
:active="isCurrentEpisode(episode)" :active="isCurrentEpisode(episode)"
@ -34,7 +34,7 @@
</NcActionButton> </NcActionButton>
</template> </template>
</NcListItem> </NcListItem>
</AdaptativeList> </ul>
<NcModal v-if="modalEpisode" @close="closeModal"> <NcModal v-if="modalEpisode" @close="closeModal">
<Modal :description="modalEpisode.description" <Modal :description="modalEpisode.description"
:image="modalEpisode.image" :image="modalEpisode.image"
@ -50,7 +50,6 @@
<script> <script>
import { NcActionButton, NcAvatar, NcListItem, NcModal } from '@nextcloud/vue' import { NcActionButton, NcAvatar, NcListItem, NcModal } from '@nextcloud/vue'
import { decodeUrl, encodeUrl } from '../../utils/url.js' import { decodeUrl, encodeUrl } from '../../utils/url.js'
import AdaptativeList from '../Atoms/AdaptativeList.vue'
import Loading from '../Atoms/Loading.vue' import Loading from '../Atoms/Loading.vue'
import Modal from './Modal.vue' import Modal from './Modal.vue'
import PlayButton from 'vue-material-design-icons/Play.vue' import PlayButton from 'vue-material-design-icons/Play.vue'
@ -63,7 +62,6 @@ import { showError } from '@nextcloud/dialogs'
export default { export default {
name: 'Episodes', name: 'Episodes',
components: { components: {
AdaptativeList,
Loading, Loading,
Modal, Modal,
NcActionButton, NcActionButton,

View File

@ -0,0 +1,50 @@
<template>
<NcAppNavigationSettings>
<div class="setting">
<label id="">{{ t('repod', 'Playback speed') }}</label>
<div class="buttons">
<Minus :size="20" @click="changeRate(-.5)" />
<NcCounterBubble>x{{ player.rate }}</NcCounterBubble>
<Plus :size="20" @click="changeRate(.5)" />
</div>
</div>
</NcAppNavigationSettings>
</template>
<script>
import { NcAppNavigationSettings, NcCounterBubble } from '@nextcloud/vue'
import Minus from 'vue-material-design-icons/Minus.vue'
import Plus from 'vue-material-design-icons/Plus.vue'
export default {
name: 'Settings',
components: {
Minus,
NcAppNavigationSettings,
NcCounterBubble,
Plus,
},
computed: {
player() {
return this.$store.state.player
},
},
methods: {
changeRate(diff) {
this.$store.dispatch('player/rate', this.player.rate + diff)
},
},
}
</script>
<style scoped>
.buttons {
display: flex;
gap: .5rem;
}
.setting {
display: flex;
justify-content: space-between;
}
</style>

View File

@ -1,5 +1,6 @@
<template> <template>
<NcAppNavigation> <AppNavigation>
<template #list>
<NcAppContentList> <NcAppContentList>
<router-link to="/"> <router-link to="/">
<NcAppNavigationNew :text="t('repod', 'Add a podcast')"> <NcAppNavigationNew :text="t('repod', 'Add a podcast')">
@ -9,33 +10,38 @@
</NcAppNavigationNew> </NcAppNavigationNew>
</router-link> </router-link>
<Loading v-if="loading" /> <Loading v-if="loading" />
<AdaptativeList v-if="!loading"> <ul v-if="!loading">
<Item v-for="subscriptionUrl of subscriptions" <Item v-for="subscriptionUrl of subscriptions"
:key="subscriptionUrl" :key="subscriptionUrl"
:url="subscriptionUrl" /> :url="subscriptionUrl" />
</AdaptativeList> </ul>
</NcAppContentList> </NcAppContentList>
</NcAppNavigation> </template>
<template #footer>
<Settings />
</template>
</AppNavigation>
</template> </template>
<script> <script>
import { NcAppContentList, NcAppNavigation, NcAppNavigationNew } from '@nextcloud/vue' import { NcAppContentList, NcAppNavigationNew } from '@nextcloud/vue'
import AdaptativeList from '../Atoms/AdaptativeList.vue' import AppNavigation from '../Atoms/AppNavigation.vue'
import Item from './Item.vue' import Item from './Item.vue'
import Loading from '../Atoms/Loading.vue' import Loading from '../Atoms/Loading.vue'
import Plus from 'vue-material-design-icons/Plus.vue' import Plus from 'vue-material-design-icons/Plus.vue'
import Settings from './Settings.vue'
import { showError } from '@nextcloud/dialogs' import { showError } from '@nextcloud/dialogs'
export default { export default {
name: 'Subscriptions', name: 'Subscriptions',
components: { components: {
AdaptativeList, AppNavigation,
Item, Item,
Loading, Loading,
NcAppContentList, NcAppContentList,
NcAppNavigation,
NcAppNavigationNew, NcAppNavigationNew,
Plus, Plus,
Settings,
}, },
data() { data() {
return { return {
@ -43,9 +49,6 @@ export default {
} }
}, },
computed: { computed: {
currentEpisode() {
return this.$store.state.player.episode
},
subscriptions() { subscriptions() {
return this.$store.state.subscriptions.subscriptions return this.$store.state.subscriptions.subscriptions
}, },

View File

@ -10,6 +10,7 @@ audio.onended = () => store.dispatch('player/stop')
audio.onloadeddata = () => store.commit('player/loaded', true) audio.onloadeddata = () => store.commit('player/loaded', true)
audio.onplay = () => store.commit('player/paused', false) audio.onplay = () => store.commit('player/paused', false)
audio.onpause = () => store.commit('player/paused', true) audio.onpause = () => store.commit('player/paused', true)
audio.onratechange = () => store.commit('player/rate', audio.playbackRate)
audio.onseeked = () => store.commit('player/currentTime', audio.currentTime) audio.onseeked = () => store.commit('player/currentTime', audio.currentTime)
audio.ontimeupdate = () => store.commit('player/currentTime', audio.currentTime) audio.ontimeupdate = () => store.commit('player/currentTime', audio.currentTime)
audio.onvolumechange = () => store.commit('player/volume', audio.volume) audio.onvolumechange = () => store.commit('player/volume', audio.volume)
@ -24,6 +25,7 @@ export const player = {
paused: null, paused: null,
podcastUrl: null, podcastUrl: null,
volume: 1, volume: 1,
rate: 1,
}, },
mutations: { mutations: {
action: (state, action) => { action: (state, action) => {
@ -66,6 +68,9 @@ export const player = {
volume: (state, volume) => { volume: (state, volume) => {
state.volume = volume state.volume = volume
}, },
rate: (state, rate) => {
state.rate = rate
},
}, },
actions: { actions: {
load: async (context, episode) => { load: async (context, episode) => {
@ -101,6 +106,9 @@ export const player = {
volume: (_, volume) => { volume: (_, volume) => {
audio.volume = volume audio.volume = volume
}, },
rate: (_, rate) => {
audio.playbackRate = rate
},
}, },
} }

View File

@ -1,5 +1,5 @@
<template> <template>
<NcAppContent class="main"> <AppContent class="main">
<NcTextField :label="t('repod', 'Find a podcast')" :value.sync="search"> <NcTextField :label="t('repod', 'Find a podcast')" :value.sync="search">
<Magnify :size="20" /> <Magnify :size="20" />
</NcTextField> </NcTextField>
@ -7,13 +7,14 @@
<Tops v-if="!search" type="hot" /> <Tops v-if="!search" type="hot" />
<Tops v-if="!search" type="new" /> <Tops v-if="!search" type="new" />
<AddRss v-if="!search" /> <AddRss v-if="!search" />
</NcAppContent> </AppContent>
</template> </template>
<script> <script>
import { NcAppContent, NcTextField } from '@nextcloud/vue'
import AddRss from '../components/Discover/AddRss.vue' import AddRss from '../components/Discover/AddRss.vue'
import AppContent from '../components/Atoms/AppContent.vue'
import Magnify from 'vue-material-design-icons/Magnify.vue' import Magnify from 'vue-material-design-icons/Magnify.vue'
import { NcTextField } from '@nextcloud/vue'
import Search from '../components/Discover/Search.vue' import Search from '../components/Discover/Search.vue'
import Tops from '../components/Discover/Tops.vue' import Tops from '../components/Discover/Tops.vue'
@ -21,8 +22,8 @@ export default {
name: 'Discover', name: 'Discover',
components: { components: {
AddRss, AddRss,
AppContent,
Magnify, Magnify,
NcAppContent,
NcTextField, NcTextField,
Search, Search,
Tops, Tops,

View File

@ -1,5 +1,5 @@
<template> <template>
<NcAppContent> <AppContent>
<Loading v-if="loading" /> <Loading v-if="loading" />
<NcEmptyContent v-if="failed" <NcEmptyContent v-if="failed"
class="error" class="error"
@ -15,15 +15,16 @@
:link="feed.link" :link="feed.link"
:title="feed.title" /> :title="feed.title" />
<Episodes v-if="feed" /> <Episodes v-if="feed" />
</NcAppContent> </AppContent>
</template> </template>
<script> <script>
import { NcAppContent, NcEmptyContent } from '@nextcloud/vue'
import Alert from 'vue-material-design-icons/Alert.vue' import Alert from 'vue-material-design-icons/Alert.vue'
import AppContent from '../components/Atoms/AppContent.vue'
import Banner from '../components/Feed/Banner.vue' import Banner from '../components/Feed/Banner.vue'
import Episodes from '../components/Feed/Episodes.vue' import Episodes from '../components/Feed/Episodes.vue'
import Loading from '../components/Atoms/Loading.vue' import Loading from '../components/Atoms/Loading.vue'
import { NcEmptyContent } from '@nextcloud/vue'
import axios from '@nextcloud/axios' import axios from '@nextcloud/axios'
import { generateUrl } from '@nextcloud/router' import { generateUrl } from '@nextcloud/router'
@ -31,10 +32,10 @@ export default {
name: 'Feed', name: 'Feed',
components: { components: {
Alert, Alert,
AppContent,
Banner, Banner,
Episodes, Episodes,
Loading, Loading,
NcAppContent,
NcEmptyContent, NcEmptyContent,
}, },
data() { data() {

View File

@ -86,6 +86,10 @@ msgstr "Êtes-vous sûr de vouloir supprimer ce flux ?"
msgid "Error while removing the feed" msgid "Error while removing the feed"
msgstr "Erreur lors de la suppression du flux" msgstr "Erreur lors de la suppression du flux"
#: /app/specialVueFakeDummyForL10nScript.js:15
msgid "Playback speed"
msgstr "Vitesse de lecture"
msgid "Add a podcast" msgid "Add a podcast"
msgstr "Ajouter un podcast" msgstr "Ajouter un podcast"

View File

@ -8,7 +8,7 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: Nextcloud 3.14159\n" "Project-Id-Version: Nextcloud 3.14159\n"
"Report-Msgid-Bugs-To: translations\\@example.com\n" "Report-Msgid-Bugs-To: translations\\@example.com\n"
"POT-Creation-Date: 2024-01-15 23:19+0000\n" "POT-Creation-Date: 2024-01-16 21:09+0000\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n" "Language-Team: LANGUAGE <LL@li.org>\n"
@ -98,25 +98,29 @@ msgid "Error while removing the feed"
msgstr "" msgstr ""
#: /app/specialVueFakeDummyForL10nScript.js:15 #: /app/specialVueFakeDummyForL10nScript.js:15
msgid "Add a podcast" msgid "Playback speed"
msgstr "" msgstr ""
#: /app/specialVueFakeDummyForL10nScript.js:16 #: /app/specialVueFakeDummyForL10nScript.js:16
msgid "Could not fetch subscriptions" msgid "Add a podcast"
msgstr "" msgstr ""
#: /app/specialVueFakeDummyForL10nScript.js:17 #: /app/specialVueFakeDummyForL10nScript.js:17
msgid "Find a podcast" msgid "Could not fetch subscriptions"
msgstr "" msgstr ""
#: /app/specialVueFakeDummyForL10nScript.js:18 #: /app/specialVueFakeDummyForL10nScript.js:18
msgid "Error loading feed" msgid "Find a podcast"
msgstr "" msgstr ""
#: /app/specialVueFakeDummyForL10nScript.js:19 #: /app/specialVueFakeDummyForL10nScript.js:19
msgid "Missing required app" msgid "Error loading feed"
msgstr "" msgstr ""
#: /app/specialVueFakeDummyForL10nScript.js:20 #: /app/specialVueFakeDummyForL10nScript.js:20
msgid "Missing required app"
msgstr ""
#: /app/specialVueFakeDummyForL10nScript.js:21
msgid "Install GPodder Sync" msgid "Install GPodder Sync"
msgstr "" msgstr ""