feat: add notifications and author
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

This commit is contained in:
Michel Roux 2025-01-03 16:17:12 +01:00
parent 514a12d756
commit 2a3d30f018
8 changed files with 65 additions and 2 deletions

View File

@ -1,3 +1,9 @@
## 3.5.4 - Under the spotlight - 2025-01-03
### Added
- 🧑‍🎤 Added the episode author on the list and modal
- ✨ Added cover image and episode infos on desktop and mobile notifications
## 3.5.3 - Hangover - 2025-01-03 ## 3.5.3 - Hangover - 2025-01-03
### Fixed ### Fixed

View File

@ -14,7 +14,7 @@
## Requirements ## Requirements
You need to have [GPodderSync](https://apps.nextcloud.com/apps/gpoddersync) installed to use this app!]]></description> You need to have [GPodderSync](https://apps.nextcloud.com/apps/gpoddersync) installed to use this app!]]></description>
<version>3.5.3</version> <version>3.5.4</version>
<licence>agpl</licence> <licence>agpl</licence>
<author mail="xefir@crystalyx.net" homepage="https://crystalyx.net">Michel Roux</author> <author mail="xefir@crystalyx.net" homepage="https://crystalyx.net">Michel Roux</author>
<namespace>RePod</namespace> <namespace>RePod</namespace>

View File

@ -18,6 +18,7 @@ use OCA\GPodderSync\Core\EpisodeAction\EpisodeAction;
* name: string, * name: string,
* link: ?string, * link: ?string,
* image: ?string, * image: ?string,
* author: ?string,
* description: ?string, * description: ?string,
* fetchedAtUnix: int, * fetchedAtUnix: int,
* guid: string, * guid: string,
@ -36,6 +37,7 @@ class EpisodeActionExtraData implements \JsonSerializable, \Stringable
private readonly string $name, private readonly string $name,
private readonly ?string $link, private readonly ?string $link,
private readonly ?string $image, private readonly ?string $image,
private readonly ?string $author,
private readonly ?string $description, private readonly ?string $description,
private readonly int $fetchedAtUnix, private readonly int $fetchedAtUnix,
private readonly string $guid, private readonly string $guid,
@ -70,6 +72,10 @@ class EpisodeActionExtraData implements \JsonSerializable, \Stringable
return $this->image; return $this->image;
} }
public function getAuthor(): ?string {
return $this->author;
}
public function getDescription(): ?string { public function getDescription(): ?string {
return $this->description; return $this->description;
} }
@ -113,6 +119,7 @@ class EpisodeActionExtraData implements \JsonSerializable, \Stringable
'name' => $this->name, 'name' => $this->name,
'link' => $this->link, 'link' => $this->link,
'image' => $this->image, 'image' => $this->image,
'author' => $this->author,
'description' => $this->description, 'description' => $this->description,
'fetchedAtUnix' => $this->fetchedAtUnix, 'fetchedAtUnix' => $this->fetchedAtUnix,
'guid' => $this->guid, 'guid' => $this->guid,

View File

@ -81,6 +81,13 @@ class EpisodeActionReader extends CoreEpisodeActionReader
} }
} }
// Get episode author
if (isset($iTunesItemChildren)) {
$author = $this->stringOrNull($iTunesItemChildren->author);
} else {
$author = $this->stringOrNull($item->author);
}
// Get episode description // Get episode description
$itemContent = $item->children('content', true); $itemContent = $item->children('content', true);
if (isset($itemContent)) { if (isset($itemContent)) {
@ -116,6 +123,7 @@ class EpisodeActionReader extends CoreEpisodeActionReader
$name, $name,
$link, $link,
$image, $image,
$author,
$description, $description,
$fetchedAtUnix ?? (new \DateTime())->getTimestamp(), $fetchedAtUnix ?? (new \DateTime())->getTimestamp(),
$guid, $guid,

View File

@ -6,6 +6,8 @@
:size="256" :size="256"
:url="episode.image" /> :url="episode.image" />
<h2>{{ episode.name }}</h2> <h2>{{ episode.name }}</h2>
<i v-if="episode.author">{{ episode.author }}</i>
<br />
<SafeHtml :source="episode.description || ''" /> <SafeHtml :source="episode.description || ''" />
<div class="flex"> <div class="flex">
<NcButton v-if="episode.link" :href="episode.link" target="_blank"> <NcButton v-if="episode.link" :href="episode.link" target="_blank">

View File

@ -2,6 +2,7 @@
<NcListItem <NcListItem
:active="isCurrentEpisode(episode)" :active="isCurrentEpisode(episode)"
class="episode" class="episode"
:counter-number="episode.duration"
:details=" :details="
!oneLine && episode.pubDate !oneLine && episode.pubDate
? formatLocaleDate(new Date(episode.pubDate?.date)) ? formatLocaleDate(new Date(episode.pubDate?.date))
@ -92,7 +93,7 @@
:value="(episode.action.position * 100) / episode.action.total" /> :value="(episode.action.position * 100) / episode.action.total" />
</template> </template>
<template #subname> <template #subname>
{{ episode.duration }} {{ !oneLine ? episode.author : '' }}
</template> </template>
</NcListItem> </NcListItem>
</template> </template>
@ -218,4 +219,13 @@ export default {
flex-basis: auto; flex-basis: auto;
flex-grow: 0; flex-grow: 0;
} }
.episode .list-item-details__extra {
flex-direction: row-reverse;
gap: 1rem;
}
.episode .list-item-details__counter {
max-width: fit-content;
}
</style> </style>

View File

@ -48,6 +48,34 @@ export const usePlayer = defineStore('player', {
conflict() { conflict() {
this.playCount = 0 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) { async load(episode: EpisodeInterface | null, podcastUrl?: string) {
this.episode = episode this.episode = episode
this.podcastUrl = podcastUrl || null this.podcastUrl = podcastUrl || null
@ -75,6 +103,7 @@ export const usePlayer = defineStore('player', {
} }
audio.play() audio.play()
this.metadatas(this.episode)
} else { } else {
this.loaded = false this.loaded = false
this.podcastUrl = null this.podcastUrl = null

View File

@ -16,6 +16,7 @@ export interface EpisodeInterface {
name: string name: string
link?: string link?: string
image?: string image?: string
author?: string
description?: string description?: string
fetchedAtUnix: number fetchedAtUnix: number
guid: string guid: string