feat: add export opml
This commit is contained in:
parent
2ed16a316e
commit
c28abc7564
@ -15,6 +15,7 @@ return [
|
|||||||
['name' => 'page#index', 'url' => '/', 'verb' => 'GET'],
|
['name' => 'page#index', 'url' => '/', 'verb' => 'GET'],
|
||||||
['name' => 'episodes#action', 'url' => '/episodes/action', 'verb' => 'GET'],
|
['name' => 'episodes#action', 'url' => '/episodes/action', 'verb' => 'GET'],
|
||||||
['name' => 'episodes#list', 'url' => '/episodes/list', 'verb' => 'GET'],
|
['name' => 'episodes#list', 'url' => '/episodes/list', 'verb' => 'GET'],
|
||||||
|
['name' => 'opml#export', 'url' => '/opml/export', 'verb' => 'GET'],
|
||||||
['name' => 'podcast#index', 'url' => '/podcast', 'verb' => 'GET'],
|
['name' => 'podcast#index', 'url' => '/podcast', 'verb' => 'GET'],
|
||||||
['name' => 'search#index', 'url' => '/search', 'verb' => 'GET'],
|
['name' => 'search#index', 'url' => '/search', 'verb' => 'GET'],
|
||||||
['name' => 'tops#hot', 'url' => '/tops/hot', 'verb' => 'GET'],
|
['name' => 'tops#hot', 'url' => '/tops/hot', 'verb' => 'GET'],
|
||||||
|
75
lib/Controller/OpmlController.php
Normal file
75
lib/Controller/OpmlController.php
Normal file
@ -0,0 +1,75 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace OCA\RePod\Controller;
|
||||||
|
|
||||||
|
use OCA\GPodderSync\Core\PodcastData\PodcastDataReader;
|
||||||
|
use OCA\GPodderSync\Core\PodcastData\PodcastMetricsReader;
|
||||||
|
use OCA\RePod\AppInfo\Application;
|
||||||
|
use OCA\RePod\Service\UserService;
|
||||||
|
use OCP\AppFramework\Controller;
|
||||||
|
use OCP\AppFramework\Http\DataDownloadResponse;
|
||||||
|
use OCP\IL10N;
|
||||||
|
use OCP\IRequest;
|
||||||
|
|
||||||
|
class OpmlController extends Controller
|
||||||
|
{
|
||||||
|
public function __construct(
|
||||||
|
IRequest $request,
|
||||||
|
private IL10N $l10n,
|
||||||
|
private PodcastDataReader $podcastDataReader,
|
||||||
|
private PodcastMetricsReader $podcastMetricsReader,
|
||||||
|
private UserService $userService
|
||||||
|
) {
|
||||||
|
parent::__construct(Application::APP_ID, $request);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @NoAdminRequired
|
||||||
|
* @NoCSRFRequired
|
||||||
|
*/
|
||||||
|
public function export(): DataDownloadResponse {
|
||||||
|
// https://github.com/AntennaPod/AntennaPod/blob/master/core/src/main/java/de/danoeh/antennapod/core/export/opml/OpmlWriter.java
|
||||||
|
$xml = new \SimpleXMLElement('<opml/>', namespaceOrPrefix: 'http://xmlpull.org/v1/doc/features.html#indent-output');
|
||||||
|
$xml->addAttribute('version', '2.0');
|
||||||
|
|
||||||
|
$dateCreated = new \DateTime();
|
||||||
|
$head = $xml->addChild('head');
|
||||||
|
|
||||||
|
if (isset($head)) {
|
||||||
|
$head->addChild('title', $this->l10n->t('RePod Subscriptions'));
|
||||||
|
$head->addChild('dateCreated', $dateCreated->format(\DateTime::RFC822));
|
||||||
|
}
|
||||||
|
|
||||||
|
$body = $xml->addChild('body');
|
||||||
|
|
||||||
|
if (isset($body)) {
|
||||||
|
$subscriptions = $this->podcastMetricsReader->metrics($this->userService->getUserUID());
|
||||||
|
|
||||||
|
foreach ($subscriptions as $subscription) {
|
||||||
|
$podcast = $this->podcastDataReader->getCachedOrFetchPodcastData($subscription->getUrl(), $this->userService->getUserUID());
|
||||||
|
|
||||||
|
if ($podcast) {
|
||||||
|
$outline = $body->addChild('outline');
|
||||||
|
|
||||||
|
if (isset($outline)) {
|
||||||
|
$title = $podcast->getTitle();
|
||||||
|
$link = $podcast->getLink();
|
||||||
|
|
||||||
|
if ($title) {
|
||||||
|
$outline->addAttribute('text', $title);
|
||||||
|
$outline->addAttribute('title', $title);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($link) {
|
||||||
|
$outline->addAttribute('xmlUrl', $link);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return new DataDownloadResponse((string) $xml->asXML(), 'repod-'.$dateCreated->getTimestamp().'.opml', ' application/xml');
|
||||||
|
}
|
||||||
|
}
|
@ -1,33 +1,46 @@
|
|||||||
<template>
|
<template>
|
||||||
<NcAppNavigationSettings>
|
<NcAppNavigationSettings>
|
||||||
<div class="setting">
|
<NcAppNavigationItem :name="t('repod', 'Playback speed')">
|
||||||
<label>
|
<template #icon>
|
||||||
<SpeedometerSlow v-if="player.rate < 1" :size="20" />
|
<SpeedometerSlow v-if="player.rate < 1" :size="20" />
|
||||||
<SpeedometerMedium v-if="player.rate === 1" :size="20" />
|
<SpeedometerMedium v-if="player.rate === 1" :size="20" />
|
||||||
<Speedometer v-if="player.rate > 1" :size="20" />
|
<Speedometer v-if="player.rate > 1" :size="20" />
|
||||||
{{ t('repod', 'Playback speed') }}
|
</template>
|
||||||
</label>
|
<template #extra>
|
||||||
<div>
|
<div class="extra">
|
||||||
<Minus class="pointer" :size="20" @click="changeRate(-.5)" />
|
<Minus class="pointer" :size="20" @click="changeRate(-.5)" />
|
||||||
<NcCounterBubble>x{{ player.rate }}</NcCounterBubble>
|
<NcCounterBubble class="counter">
|
||||||
|
x{{ player.rate }}
|
||||||
|
</NcCounterBubble>
|
||||||
<Plus class="pointer" :size="20" @click="changeRate(.5)" />
|
<Plus class="pointer" :size="20" @click="changeRate(.5)" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</template>
|
||||||
|
</NcAppNavigationItem>
|
||||||
|
<NcAppNavigationItem :href="generateUrl('/apps/repod/opml/export')"
|
||||||
|
:name="t('repod', 'Export subscriptions')">
|
||||||
|
<template #icon>
|
||||||
|
<Export :size="20" />
|
||||||
|
</template>
|
||||||
|
</NcAppNavigationItem>
|
||||||
</NcAppNavigationSettings>
|
</NcAppNavigationSettings>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import { NcAppNavigationSettings, NcCounterBubble } from '@nextcloud/vue'
|
import { NcAppNavigationItem, NcAppNavigationSettings, NcCounterBubble } from '@nextcloud/vue'
|
||||||
|
import Export from 'vue-material-design-icons/Export.vue'
|
||||||
import Minus from 'vue-material-design-icons/Minus.vue'
|
import Minus from 'vue-material-design-icons/Minus.vue'
|
||||||
import Plus from 'vue-material-design-icons/Plus.vue'
|
import Plus from 'vue-material-design-icons/Plus.vue'
|
||||||
import Speedometer from 'vue-material-design-icons/Speedometer.vue'
|
import Speedometer from 'vue-material-design-icons/Speedometer.vue'
|
||||||
import SpeedometerMedium from 'vue-material-design-icons/SpeedometerMedium.vue'
|
import SpeedometerMedium from 'vue-material-design-icons/SpeedometerMedium.vue'
|
||||||
import SpeedometerSlow from 'vue-material-design-icons/SpeedometerSlow.vue'
|
import SpeedometerSlow from 'vue-material-design-icons/SpeedometerSlow.vue'
|
||||||
|
import { generateUrl } from '@nextcloud/router'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'Settings',
|
name: 'Settings',
|
||||||
components: {
|
components: {
|
||||||
|
Export,
|
||||||
Minus,
|
Minus,
|
||||||
|
NcAppNavigationItem,
|
||||||
NcAppNavigationSettings,
|
NcAppNavigationSettings,
|
||||||
NcCounterBubble,
|
NcCounterBubble,
|
||||||
Plus,
|
Plus,
|
||||||
@ -41,6 +54,7 @@ export default {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
|
generateUrl,
|
||||||
changeRate(diff) {
|
changeRate(diff) {
|
||||||
if (this.player.rate + diff > 0) {
|
if (this.player.rate + diff > 0) {
|
||||||
this.$store.dispatch('player/rate', this.player.rate + diff)
|
this.$store.dispatch('player/rate', this.player.rate + diff)
|
||||||
@ -51,18 +65,17 @@ export default {
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
.pointer {
|
.counter {
|
||||||
cursor: pointer;
|
height: 20px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.setting {
|
.extra {
|
||||||
display: flex;
|
align-items: center;
|
||||||
justify-content: space-between;
|
|
||||||
}
|
|
||||||
|
|
||||||
.setting > label,
|
|
||||||
.setting > div {
|
|
||||||
display: flex;
|
display: flex;
|
||||||
gap: .5rem;
|
gap: .5rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.pointer {
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
Loading…
Reference in New Issue
Block a user