feat: import subscriptions
This commit is contained in:
parent
c28abc7564
commit
751c3c1e01
@ -16,6 +16,7 @@ return [
|
||||
['name' => 'episodes#action', 'url' => '/episodes/action', 'verb' => 'GET'],
|
||||
['name' => 'episodes#list', 'url' => '/episodes/list', 'verb' => 'GET'],
|
||||
['name' => 'opml#export', 'url' => '/opml/export', 'verb' => 'GET'],
|
||||
['name' => 'opml#import', 'url' => '/opml/import', 'verb' => 'POST'],
|
||||
['name' => 'podcast#index', 'url' => '/podcast', 'verb' => 'GET'],
|
||||
['name' => 'search#index', 'url' => '/search', 'verb' => 'GET'],
|
||||
['name' => 'tops#hot', 'url' => '/tops/hot', 'verb' => 'GET'],
|
||||
|
@ -30,9 +30,7 @@ class EpisodesController extends Controller
|
||||
public function list(string $url): JSONResponse {
|
||||
$client = $this->clientService->newClient();
|
||||
$feed = $client->get($url);
|
||||
|
||||
$episodes = $this->episodeActionReader->parseRssXml((string) $feed->getBody());
|
||||
|
||||
usort($episodes, fn (EpisodeActionExtraData $a, EpisodeActionExtraData $b) => $b->getPubDate() <=> $a->getPubDate());
|
||||
$episodes = array_values(array_intersect_key($episodes, array_unique(array_map(fn (EpisodeActionExtraData $episode) => $episode->getGuid(), $episodes))));
|
||||
|
||||
|
@ -6,10 +6,12 @@ namespace OCA\RePod\Controller;
|
||||
|
||||
use OCA\GPodderSync\Core\PodcastData\PodcastDataReader;
|
||||
use OCA\GPodderSync\Core\PodcastData\PodcastMetricsReader;
|
||||
use OCA\GPodderSync\Core\SubscriptionChange\SubscriptionChangeSaver;
|
||||
use OCA\RePod\AppInfo\Application;
|
||||
use OCA\RePod\Service\UserService;
|
||||
use OCP\AppFramework\Controller;
|
||||
use OCP\AppFramework\Http\DataDownloadResponse;
|
||||
use OCP\AppFramework\Http\Response;
|
||||
use OCP\IL10N;
|
||||
use OCP\IRequest;
|
||||
|
||||
@ -20,6 +22,7 @@ class OpmlController extends Controller
|
||||
private IL10N $l10n,
|
||||
private PodcastDataReader $podcastDataReader,
|
||||
private PodcastMetricsReader $podcastMetricsReader,
|
||||
private SubscriptionChangeSaver $subscriptionChangeSaver,
|
||||
private UserService $userService
|
||||
) {
|
||||
parent::__construct(Application::APP_ID, $request);
|
||||
@ -54,6 +57,8 @@ class OpmlController extends Controller
|
||||
$outline = $body->addChild('outline');
|
||||
|
||||
if (isset($outline)) {
|
||||
$outline->addAttribute('xmlUrl', $subscription->getUrl());
|
||||
|
||||
$title = $podcast->getTitle();
|
||||
$link = $podcast->getLink();
|
||||
|
||||
@ -63,7 +68,7 @@ class OpmlController extends Controller
|
||||
}
|
||||
|
||||
if ($link) {
|
||||
$outline->addAttribute('xmlUrl', $link);
|
||||
$outline->addAttribute('htmlUrl', $link);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -72,4 +77,28 @@ class OpmlController extends Controller
|
||||
|
||||
return new DataDownloadResponse((string) $xml->asXML(), 'repod-'.$dateCreated->getTimestamp().'.opml', ' application/xml');
|
||||
}
|
||||
|
||||
/**
|
||||
* @NoAdminRequired
|
||||
* @NoCSRFRequired
|
||||
*/
|
||||
public function import(): Response {
|
||||
$file = $this->request->getUploadedFile('import');
|
||||
|
||||
if ($file) {
|
||||
$xml = new \SimpleXMLElement(file_get_contents((string) $file['tmp_name']));
|
||||
|
||||
/** @var \SimpleXMLElement[] $outlines */
|
||||
$outlines = $xml->body->children();
|
||||
|
||||
$toSubscribe = [];
|
||||
foreach ($outlines as $outline) {
|
||||
$toSubscribe[] = (string) $outline['xmlUrl'];
|
||||
}
|
||||
|
||||
$this->subscriptionChangeSaver->saveSubscriptionChanges($toSubscribe, [], $this->userService->getUserUID());
|
||||
}
|
||||
|
||||
return new Response();
|
||||
}
|
||||
}
|
||||
|
@ -86,6 +86,7 @@ class FyydService implements IPodProvider
|
||||
$langClient = $this->clientService->newClient();
|
||||
$langResponse = $langClient->get(self::BASE_URL.'feature/podcast/hot/languages');
|
||||
$langJson = (array) json_decode((string) $langResponse->getBody(), true, flags: JSON_THROW_ON_ERROR);
|
||||
|
||||
if (array_key_exists('data', $langJson) && is_array($langJson['data'])) {
|
||||
$language = in_array($userLang, $langJson['data']) ? $userLang : 'en';
|
||||
}
|
||||
|
@ -16,6 +16,31 @@
|
||||
</div>
|
||||
</template>
|
||||
</NcAppNavigationItem>
|
||||
<NcAppNavigationItem :name="t('repod', 'Import subscriptions')" @click="importModal = true">
|
||||
<template #icon>
|
||||
<Import :size="20" />
|
||||
</template>
|
||||
<template #extra>
|
||||
<NcModal v-if="importModal" @close="importModal = false">
|
||||
<div class="importModal">
|
||||
<h2>{{ t('repod', 'Import OPML file') }}</h2>
|
||||
<form v-if="!importLoading"
|
||||
id="import"
|
||||
:action="generateUrl('/apps/repod/opml/import')"
|
||||
enctype="multipart/form-data"
|
||||
method="post"
|
||||
@submit.prevent="importOpml">
|
||||
<input accept="application/xml,.opml"
|
||||
name="import"
|
||||
required
|
||||
type="file">
|
||||
<input type="submit">
|
||||
</form>
|
||||
<Loading v-if="importLoading" />
|
||||
</div>
|
||||
</NcModal>
|
||||
</template>
|
||||
</NcAppNavigationItem>
|
||||
<NcAppNavigationItem :href="generateUrl('/apps/repod/opml/export')"
|
||||
:name="t('repod', 'Export subscriptions')">
|
||||
<template #icon>
|
||||
@ -26,28 +51,40 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { NcAppNavigationItem, NcAppNavigationSettings, NcCounterBubble } from '@nextcloud/vue'
|
||||
import { NcAppNavigationItem, NcAppNavigationSettings, NcCounterBubble, NcModal } from '@nextcloud/vue'
|
||||
import Export from 'vue-material-design-icons/Export.vue'
|
||||
import Import from 'vue-material-design-icons/Import.vue'
|
||||
import Loading from '../Atoms/Loading.vue'
|
||||
import Minus from 'vue-material-design-icons/Minus.vue'
|
||||
import Plus from 'vue-material-design-icons/Plus.vue'
|
||||
import Speedometer from 'vue-material-design-icons/Speedometer.vue'
|
||||
import SpeedometerMedium from 'vue-material-design-icons/SpeedometerMedium.vue'
|
||||
import SpeedometerSlow from 'vue-material-design-icons/SpeedometerSlow.vue'
|
||||
import axios from '@nextcloud/axios'
|
||||
import { generateUrl } from '@nextcloud/router'
|
||||
|
||||
export default {
|
||||
name: 'Settings',
|
||||
components: {
|
||||
Export,
|
||||
Import,
|
||||
Loading,
|
||||
Minus,
|
||||
NcAppNavigationItem,
|
||||
NcAppNavigationSettings,
|
||||
NcCounterBubble,
|
||||
NcModal,
|
||||
Plus,
|
||||
Speedometer,
|
||||
SpeedometerMedium,
|
||||
SpeedometerSlow,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
importModal: false,
|
||||
importLoading: false,
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
player() {
|
||||
return this.$store.state.player
|
||||
@ -60,6 +97,17 @@ export default {
|
||||
this.$store.dispatch('player/rate', this.player.rate + diff)
|
||||
}
|
||||
},
|
||||
async importOpml(event) {
|
||||
try {
|
||||
const formData = new FormData(event.target)
|
||||
this.importLoading = true
|
||||
await axios.post(event.target.action, formData)
|
||||
} catch (e) {
|
||||
console.error(e)
|
||||
} finally {
|
||||
location.reload()
|
||||
}
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
@ -75,6 +123,13 @@ export default {
|
||||
gap: .5rem;
|
||||
}
|
||||
|
||||
.importModal {
|
||||
align-items: center;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
margin: 2rem;
|
||||
}
|
||||
|
||||
.pointer {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user