diff --git a/lib/Controller/PersonalSettingsController.php b/lib/Controller/PersonalSettingsController.php
index 4cb07f4..44de8b9 100644
--- a/lib/Controller/PersonalSettingsController.php
+++ b/lib/Controller/PersonalSettingsController.php
@@ -4,34 +4,43 @@ declare(strict_types=1);
namespace OCA\GPodderSync\Controller;
use DateTime;
+
+use OCA\GPodderSync\Service\PodcastCacheService;
use OCA\GPodderSync\Db\EpisodeAction\EpisodeActionRepository;
use OCA\GPodderSync\Db\SubscriptionChange\SubscriptionChangeEntity;
use OCA\GPodderSync\Db\SubscriptionChange\SubscriptionChangeRepository;
-use OCP\AppFramework\Http\TemplateResponse;
+
+use OCP\AppFramework\Controller;
+use OCP\AppFramework\Http\JSONResponse;
use OCP\IL10N;
use OCP\IRequest;
use OCP\Settings\ISettings;
-class EpisodeActionController extends Controller {
+use Psr\Log\LoggerInterface;
- private IL10N $l;
+class PersonalSettingsController extends Controller {
+
+ private LoggerInterface $logger;
+ private string $userId;
private SubscriptionChangeRepository $subscriptionChangeRepository;
private EpisodeActionRepository $episodeActionRepository;
- private string $userId;
+ private PodcastCacheService $podcastCacheService;
public function __construct(
string $AppName,
IRequest $request,
- $UserId,
- IL10N $l,
+ LoggerInterface $logger,
+ string $UserId,
SubscriptionChangeRepository $subscriptionChangeRepository,
EpisodeActionRepository $episodeActionRepository,
+ PodcastCacheService $podcastCacheService,
) {
parent::__construct($AppName, $request);
- $this->l = $l;
+ $this->logger = $logger;
+ $this->userId = $UserId ?? '';
$this->subscriptionChangeRepository = $subscriptionChangeRepository;
$this->episodeActionRepository = $episodeActionRepository;
- $this->userId = $UserId ?? '';
+ $this->podcastCacheService = $podcastCacheService;
}
/**
@@ -43,28 +52,64 @@ class EpisodeActionController extends Controller {
*/
public function metrics(): JSONResponse {
$sinceDatetime = (new DateTime)->setTimestamp(0);
- $subscriptions = $this->extractUrlList($this->subscriptionChangeRepository->findAllSubscribed($sinceDatetime, $this->userId));
+ $subscriptionChanges = $this->subscriptionChangeRepository->findAllSubscribed($sinceDatetime, $this->userId);
$episodeActions = $this->episodeActionRepository->findAll(0, $this->userId);
+
$subStats = array();
- foreach ($episodeActions as $action) {
- $pod = $action->getPodcast();
- $sub = $subStats[$pod] ?? array();
- $sub['started']++;
- $subStats[$pod] = $sub;
+ foreach ($episodeActions as $ep) {
+ $url = $ep->getPodcast();
+ $stats = $subStats[$url] ?? [
+ 'listenedSeconds' => 0,
+ 'actionCounts' => $this->defaultActionCounts(),
+ ];
+ $actionCounts = $stats['actionCounts'];
+ $actionLower = strtolower($ep->getAction());
+ if (array_key_exists($actionLower, $actionCounts)) {
+ $actionCounts[$actionLower]++;
+ }
+ $stats['actionCounts'] = $actionCounts;
+ if ($actionLower == 'play') {
+ $seconds = $ep->getPosition();
+ if ($seconds && $seconds != -1) {
+ $stats['listenedSeconds'] += $seconds;
+ }
+ }
+ $subStats[$url] = $stats;
}
+
+ $subscriptions = array_map(function (SubscriptionChangeEntity $sub) use ($subStats) {
+ $url = $sub->getUrl();
+ $stats = $subStats[$url] ?? array();
+ $sub = [
+ 'url' => $url ?? '',
+ 'listenedSeconds' => $stats['listenedSeconds'],
+ 'actionCounts' => $stats['actionCounts'],
+ ];
+ try {
+ $podcast = $this->podcastCacheService->getCachedOrFetchPodcastData($url);
+ $sub['podcast'] = $podcast;
+ } catch (Exception $e) {
+ $sub['podcast'] = null;
+ $this->logger->error("Failed to get podcast data.", [
+ 'exception' => $e,
+ 'podcastUrl' => $url,
+ ]);
+ }
+ return $sub;
+ }, $subscriptionChanges);
+
return new JSONResponse([
'subscriptions' => $subscriptions,
- 'subStats' => $subStats,
]);
}
- /**
- * @param array $allSubscribed
- * @return mixed
- */
- private function extractUrlList(array $allSubscribed): array {
- return array_map(static function (SubscriptionChangeEntity $subscription) {
- return $subscription->getUrl();
- }, $allSubscribed);
+ private function defaultActionCounts(): array {
+ return [
+ 'download' => 0,
+ 'delete' => 0,
+ 'play' => 0,
+ 'new' => 0,
+ 'flattr' => 0,
+ ];
}
}
diff --git a/lib/Service/PodcastCacheService.php b/lib/Service/PodcastCacheService.php
new file mode 100644
index 0000000..a3eceb6
--- /dev/null
+++ b/lib/Service/PodcastCacheService.php
@@ -0,0 +1,77 @@
+isLocalCacheAvailable()) {
+ $this->cache = $cacheFactory->createLocal('GPodderSync-Podcasts');
+ }
+ $this->httpClient = $httpClientService->newClient();
+ }
+
+ public function getCachedOrFetchPodcastData(string $url) {
+ if ($this->cache == null) {
+ return $this->fetchPodcastData($url);
+ }
+ $oldData = $this->cache->get($url);
+ if ($oldData) {
+ return $oldData;
+ }
+ $newData = $this->fetchPodcastData($url);
+ $this->cache->set($url, $newData);
+ return $newData;
+ }
+
+ public function fetchPodcastData(string $url) {
+ $resp = $this->httpClient->get($url);
+ $statusCode = $resp->getStatusCode();
+ if ($statusCode < 200 || $statusCode >= 300) {
+ throw new ErrorException("Podcast RSS URL returned non-2xx status code: $statusCode");
+ }
+ $body = $resp->getBody();
+ $xml = new SimpleXMLElement($body);
+ $channel = $xml->channel;
+ return [
+ 'title' => (string)$channel->title,
+ 'author' => self::getXPathContent($xml, '/rss/channel/itunes:author'),
+ 'link' => (string)$channel->link,
+ 'description' => (string)$channel->description,
+ 'image' =>
+ self::getXPathContent($xml, '/rss/channel/image/url')
+ ?? self::getXPathAttribute($xml, '/rss/channel/itunes:image/@href'),
+ 'fetchedAtUnix' => (new DateTime())->getTimestamp(),
+ ];
+ }
+
+ private static function getXPathContent(SimpleXMLElement $xml, string $xpath) {
+ $match = $xml->xpath($xpath);
+ if ($match) {
+ return (string)$match[0];
+ }
+ return null;
+ }
+
+ private static function getXPathAttribute(SimpleXMLElement $xml, string $xpath) {
+ $match = $xml->xpath($xpath);
+ if ($match) {
+ return (string)$match[0][0];
+ }
+ return null;
+ }
+}
diff --git a/src/personalSettings.js b/src/personalSettings.js
index ac45176..140a35e 100644
--- a/src/personalSettings.js
+++ b/src/personalSettings.js
@@ -8,6 +8,10 @@ __webpack_public_path__ = generateFilePath(appName, '', 'js/')
Vue.mixin({ methods: { t, n } })
+// https://nextcloud-vue-components.netlify.app/#/Introduction
+Vue.prototype.OC = window.OC
+Vue.prototype.OCA = window.OCA
+
export default new Vue({
el: '#personal_settings',
render: h => h(PersonalSettingsPage),
diff --git a/src/views/PersonalSettingsPage.vue b/src/views/PersonalSettingsPage.vue
index b617ad8..c031d09 100644
--- a/src/views/PersonalSettingsPage.vue
+++ b/src/views/PersonalSettingsPage.vue
@@ -3,17 +3,60 @@
Hello world :)
+
+
+
+
+
+
+ {{ sub.podcast?.description }}
+
+
+