Skip to content

Commit 636ff1a

Browse files
committed
show title and descripton in backend ui also for audiothek content
1 parent a91c131 commit 636ff1a

4 files changed

Lines changed: 69 additions & 4 deletions

File tree

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,3 +2,4 @@ vendor/*
22
tests/.phpunit.result.cache
33
node_modules/
44
js/
5+
.idea/

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,8 @@
11
# Changelog
2+
## 3.14.0 - 2026-02-17
3+
### Added
4+
- Resolve title, description, and imagery for subscriptions pointing to api.ardaudiothek.de programsets
5+
26
## 3.13.3 - 2025-11-05
37
### Changed
48
- #168 ignore actions DELETE and DOWNLOAD

appinfo/info.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
<name>GPodder Sync</name>
66
<summary>replicate basic GPodder.net API</summary>
77
<description><![CDATA[Expose GPodder API to sync podcast consumer apps like AntennaPod]]></description>
8-
<version>3.13.3</version>
8+
<version>3.14.0</version>
99
<licence>agpl</licence>
1010
<author mail="thrillfall@disroot.org">Thrillfall</author>
1111
<namespace>GPodderSync</namespace>

lib/Core/PodcastData/PodcastDataReader.php

Lines changed: 63 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,29 +3,37 @@
33

44
namespace OCA\GPodderSync\Core\PodcastData;
55

6+
use DateTime;
67
use Exception;
78
use OCA\GPodderSync\Db\SubscriptionChange\SubscriptionChangeRepository;
89
use OCP\Http\Client\IClient;
910
use OCP\Http\Client\IClientService;
1011
use OCP\Http\Client\IResponse;
1112
use OCP\ICache;
1213
use OCP\ICacheFactory;
14+
use OCP\ILogger;
1315

1416
class PodcastDataReader {
1517
private ?ICache $cache = null;
1618
private IClient $httpClient;
1719
private SubscriptionChangeRepository $subscriptionChangeRepository;
20+
private ILogger $logger;
21+
22+
private const ARD_AUDIOTHEK_HOST = 'api.ardaudiothek.de';
23+
private const ARD_PROGRAMSET_REGEX = '#https?://api\.ardaudiothek\.de/programsets/(?P<id>[^/?]+)#i';
1824

1925
public function __construct(
2026
ICacheFactory $cacheFactory,
2127
IClientService $httpClientService,
22-
SubscriptionChangeRepository $subscriptionChangeRepository
28+
SubscriptionChangeRepository $subscriptionChangeRepository,
29+
ILogger $logger
2330
) {
2431
if ($cacheFactory->isLocalCacheAvailable()) {
2532
$this->cache = $cacheFactory->createLocal('GPodderSync-Podcasts');
2633
}
2734
$this->httpClient = $httpClientService->newClient();
2835
$this->subscriptionChangeRepository = $subscriptionChangeRepository;
36+
$this->logger = $logger;
2937
}
3038

3139
public function getCachedOrFetchPodcastData(string $url, string $userId): ?PodcastData {
@@ -50,15 +58,67 @@ public function fetchPodcastData(string $url, string $userId): ?PodcastData {
5058
if (!$this->userHasPodcast($url, $userId)) {
5159
return null;
5260
}
53-
$resp = $this->fetchUrl($url);
54-
$data = PodcastData::parseRssXml($resp->getBody());
61+
$data = $this->fetchPodcastDataForUrl($url);
5562
$blob = $this->tryFetchImageBlob($data);
5663
if ($blob) {
5764
$data->setImageBlob($blob);
5865
}
5966
return $data;
6067
}
6168

69+
private function fetchPodcastDataForUrl(string $url): PodcastData {
70+
if ($this->isArdAudiothekUrl($url)) {
71+
return $this->fetchArdAudiothekData($url);
72+
}
73+
$resp = $this->fetchUrl($url);
74+
return PodcastData::parseRssXml($resp->getBody());
75+
}
76+
77+
private function isArdAudiothekUrl(string $url): bool {
78+
return (bool)preg_match(self::ARD_PROGRAMSET_REGEX, $url);
79+
}
80+
81+
private function fetchArdAudiothekData(string $url): PodcastData {
82+
$programId = $this->extractArdProgramId($url);
83+
if ($programId === null) {
84+
throw new \InvalidArgumentException('Could not extract ARD Audiothek program id from URL');
85+
}
86+
$resp = $this->fetchUrl("https://" . self::ARD_AUDIOTHEK_HOST . "/programsets/$programId");
87+
$body = $resp->getBody();
88+
$decoded = json_decode($body, true);
89+
if (!is_array($decoded)) {
90+
$this->logger->warning('Invalid JSON returned from ARD Audiothek.', ['responseBody' => $body]);
91+
throw new \RuntimeException('Invalid JSON returned from ARD Audiothek');
92+
}
93+
$programSet = $decoded['data']['programSet'] ?? null;
94+
if (!is_array($programSet)) {
95+
$this->logger->warning('programSet missing in ARD Audiothek response.', ['responseBody' => $body]);
96+
throw new \RuntimeException('programSet missing in ARD Audiothek response');
97+
}
98+
return new PodcastData(
99+
$programSet['title'] ?? null,
100+
$programSet['publicationService']['title'] ?? null,
101+
$programSet['sharingUrl'] ?? $url,
102+
$programSet['synopsis'] ?? ($programSet['description'] ?? null),
103+
$this->resolveArdImageUrl($programSet['image'] ?? null),
104+
(new DateTime())->getTimestamp()
105+
);
106+
}
107+
108+
private function resolveArdImageUrl($image): ?string {
109+
if (!is_array($image)) {
110+
return null;
111+
}
112+
return $image['url'] ?? ($image['url1X1'] ?? null);
113+
}
114+
115+
private function extractArdProgramId(string $url): ?string {
116+
if (preg_match(self::ARD_PROGRAMSET_REGEX, $url, $matches)) {
117+
return $matches['id'];
118+
}
119+
return null;
120+
}
121+
62122
private function tryFetchImageBlob(PodcastData $data): ?string {
63123
if (!$data->getImageUrl()) {
64124
return null;

0 commit comments

Comments
 (0)