33
44namespace OCA \GPodderSync \Core \PodcastData ;
55
6+ use DateTime ;
67use Exception ;
78use OCA \GPodderSync \Db \SubscriptionChange \SubscriptionChangeRepository ;
89use OCP \Http \Client \IClient ;
910use OCP \Http \Client \IClientService ;
1011use OCP \Http \Client \IResponse ;
1112use OCP \ICache ;
1213use OCP \ICacheFactory ;
14+ use OCP \ILogger ;
1315
1416class 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