repod/src/store/player.ts
Michel Roux 2a3d30f018
All checks were successful
repod / xml (push) Successful in 12s
repod / php (push) Successful in 1m0s
repod / nodejs (push) Successful in 1m38s
repod / release (push) Successful in 1m16s
feat: add notifications and author
2025-01-03 16:17:12 +01:00

167 lines
4.5 KiB
TypeScript

import type { EpisodeActionInterface, EpisodeInterface } from '../utils/types.ts'
import { getCookie, setCookie } from '../utils/cookies.ts'
import axios from '@nextcloud/axios'
import { defineStore } from 'pinia'
import { formatEpisodeTimestamp } from '../utils/time.ts'
import { generateUrl } from '@nextcloud/router'
import { showError } from '../utils/toast.ts'
import { t } from '@nextcloud/l10n'
const audio = new Audio()
export const usePlayer = defineStore('player', {
state: () => ({
currentTime: null as number | null,
duration: null as number | null,
episode: null as EpisodeInterface | null,
loaded: false,
paused: true,
playCount: 0,
podcastUrl: null as string | null,
volume: 1,
rate: 1,
started: 0,
}),
actions: {
init() {
audio.playbackRate = parseFloat(getCookie('repod.rate') || '1')
audio.volume = parseFloat(getCookie('repod.volume') || '1')
audio.ondurationchange = () => (this.duration = audio.duration)
audio.onended = () => this.stop()
audio.onloadeddata = () => (this.loaded = true)
audio.onpause = () => this.pause()
audio.onplay = () => this.play()
audio.onratechange = () => (this.rate = audio.playbackRate)
audio.onseeked = () => (this.currentTime = audio.currentTime)
audio.ontimeupdate = () => (this.currentTime = audio.currentTime)
audio.onvolumechange = () => (this.volume = audio.volume)
setInterval(this.act, 40000)
setInterval(this.conflict, 1000)
},
act() {
if (this.paused === false) {
this.time()
}
},
conflict() {
this.playCount = 0
},
metadatas(episode: EpisodeInterface) {
// https://developer.mozilla.org/en-US/docs/Web/API/Media_Session_API
navigator.mediaSession.metadata = new MediaMetadata({
title: episode.name,
artist: episode.author,
album: episode.title,
artwork: episode.image ? [{ src: episode.image }] : [],
})
navigator.mediaSession.setActionHandler('play', this.play)
navigator.mediaSession.setActionHandler('pause', this.pause)
navigator.mediaSession.setActionHandler('stop', this.stop)
navigator.mediaSession.setActionHandler(
'seekbackward',
(details: MediaSessionActionDetails) =>
this.seek(audio.currentTime - (details.seekOffset || 10)),
)
navigator.mediaSession.setActionHandler(
'seekforward',
(details: MediaSessionActionDetails) =>
this.seek(audio.currentTime + (details.seekOffset || 30)),
)
navigator.mediaSession.setActionHandler(
'seekto',
(details: MediaSessionActionDetails) =>
!details.fastSeek && this.seek(details.seekTime || 0),
)
},
async load(episode: EpisodeInterface | null, podcastUrl?: string) {
this.episode = episode
this.podcastUrl = podcastUrl || null
if (this.episode?.url) {
audio.src = this.episode.url
audio.load()
try {
const action = await axios.get<EpisodeActionInterface>(
generateUrl('/apps/repod/episodes/action?url={url}', {
url: this.episode.url,
}),
)
this.episode.action = action.data
} catch {}
if (
this.episode.action &&
this.episode.action.position < this.episode.action.total
) {
audio.currentTime = this.episode.action.position
this.started = audio.currentTime
}
audio.play()
this.metadatas(this.episode)
} else {
this.loaded = false
this.podcastUrl = null
audio.src = ''
}
},
pause() {
audio.pause()
this.paused = true
this.time()
},
play() {
this.playCount++
if (this.playCount > 10) {
showError(t('repod', 'A browser extension conflict with RePod'))
} else {
audio.play()
this.paused = false
this.started = audio.currentTime
}
},
seek(currentTime: number) {
audio.currentTime = currentTime
this.time()
},
stop() {
this.pause()
this.episode = null
},
time() {
if (!this.podcastUrl || !this.episode?.url) {
return
}
this.episode.action = {
podcast: this.podcastUrl,
episode: this.episode.url,
guid: this.episode.guid,
action: 'play',
timestamp: formatEpisodeTimestamp(new Date()),
started: Math.round(this.started),
position: Math.round(audio.currentTime),
total: Math.round(audio.duration),
}
axios.post(generateUrl('/apps/gpoddersync/episode_action/create'), [
this.episode.action,
])
},
setVolume(volume: number) {
audio.volume = volume
setCookie('repod.volume', volume.toString(), 365)
},
setRate(rate: number) {
audio.playbackRate = rate
setCookie('repod.rate', rate.toString(), 365)
},
},
})