Refacto API to be pseudo compatible with gpodder
This commit is contained in:
parent
6d6f8b4cf7
commit
098db9f09e
@ -13,7 +13,7 @@ declare(strict_types=1);
|
|||||||
return [
|
return [
|
||||||
'routes' => [
|
'routes' => [
|
||||||
['name' => 'page#index', 'url' => '/', 'verb' => 'GET'],
|
['name' => 'page#index', 'url' => '/', 'verb' => 'GET'],
|
||||||
['name' => 'top#index', 'url' => '/top', 'verb' => 'GET'],
|
['name' => 'toplist#index', 'url' => '/toplist/{count}', 'verb' => 'GET'],
|
||||||
['name' => 'search#index', 'url' => '/search/{value}', 'verb' => 'GET'],
|
['name' => 'search#index', 'url' => '/search/{value}', 'verb' => 'GET'],
|
||||||
],
|
],
|
||||||
];
|
];
|
||||||
|
@ -5,7 +5,7 @@
|
|||||||
"license": "AGPL-3.0-or-later",
|
"license": "AGPL-3.0-or-later",
|
||||||
"require-dev": {
|
"require-dev": {
|
||||||
"nextcloud/ocp": "^27.0.1",
|
"nextcloud/ocp": "^27.0.1",
|
||||||
"psalm/phar": "^5.13.1",
|
"psalm/phar": "^5.14.1",
|
||||||
"nextcloud/coding-standard": "^1.1.1"
|
"nextcloud/coding-standard": "^1.1.1"
|
||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
|
2
composer.lock
generated
2
composer.lock
generated
@ -4,7 +4,7 @@
|
|||||||
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
||||||
"This file is @generated automatically"
|
"This file is @generated automatically"
|
||||||
],
|
],
|
||||||
"content-hash": "a5d69457fcb9b9b73dd75bcd2b7c9791",
|
"content-hash": "817269c30922145fbaf4270b2cb22d8e",
|
||||||
"packages": [],
|
"packages": [],
|
||||||
"packages-dev": [
|
"packages-dev": [
|
||||||
{
|
{
|
||||||
|
@ -36,8 +36,8 @@ class SearchController extends Controller
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
usort($podcasts, fn (array $a, array $b) => new \DateTime((string) $b['last_pub']) <=> new \DateTime((string) $a['last_pub']));
|
usort($podcasts, fn (array $a, array $b) => new \DateTime((string) $b['lastpub']) <=> new \DateTime((string) $a['lastpub']));
|
||||||
$podcasts = array_intersect_key($podcasts, array_unique(array_map(fn (array $feed) => $feed['feed_url'], $podcasts)));
|
$podcasts = array_intersect_key($podcasts, array_unique(array_map(fn (array $feed) => $feed['url'], $podcasts)));
|
||||||
|
|
||||||
return new JSONResponse($podcasts);
|
return new JSONResponse($podcasts);
|
||||||
}
|
}
|
||||||
|
@ -11,7 +11,7 @@ use OCP\AppFramework\Http;
|
|||||||
use OCP\AppFramework\Http\JSONResponse;
|
use OCP\AppFramework\Http\JSONResponse;
|
||||||
use OCP\IRequest;
|
use OCP\IRequest;
|
||||||
|
|
||||||
class TopController extends Controller
|
class ToplistController extends Controller
|
||||||
{
|
{
|
||||||
public function __construct(
|
public function __construct(
|
||||||
IRequest $request,
|
IRequest $request,
|
||||||
@ -27,10 +27,7 @@ class TopController extends Controller
|
|||||||
public function index(): JSONResponse
|
public function index(): JSONResponse
|
||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
$response = $this->fyydService->hot();
|
return new JSONResponse($this->fyydService->hot());
|
||||||
$json = (array) json_decode((string) $response->getBody(), true, flags: JSON_THROW_ON_ERROR);
|
|
||||||
|
|
||||||
return new JSONResponse($json, $response->getStatusCode());
|
|
||||||
} catch (\Exception $e) {
|
} catch (\Exception $e) {
|
||||||
return new JSONResponse([$e->getMessage()], Http::STATUS_INTERNAL_SERVER_ERROR);
|
return new JSONResponse([$e->getMessage()], Http::STATUS_INTERNAL_SERVER_ERROR);
|
||||||
}
|
}
|
@ -5,11 +5,13 @@ declare(strict_types=1);
|
|||||||
namespace OCA\RePod\Service;
|
namespace OCA\RePod\Service;
|
||||||
|
|
||||||
use OCP\Http\Client\IClientService;
|
use OCP\Http\Client\IClientService;
|
||||||
use OCP\Http\Client\IResponse;
|
|
||||||
use OCP\IUserSession;
|
use OCP\IUserSession;
|
||||||
use OCP\L10N\IFactory;
|
use OCP\L10N\IFactory;
|
||||||
use Psr\Log\LoggerInterface;
|
use Psr\Log\LoggerInterface;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @psalm-import-type Podcast from IProvider
|
||||||
|
*/
|
||||||
class FyydService implements IProvider
|
class FyydService implements IProvider
|
||||||
{
|
{
|
||||||
private const BASE_URL = 'https://api.fyyd.de/0.2/';
|
private const BASE_URL = 'https://api.fyyd.de/0.2/';
|
||||||
@ -41,15 +43,18 @@ class FyydService implements IProvider
|
|||||||
/** @var string[] $feed */
|
/** @var string[] $feed */
|
||||||
foreach ($json['data'] as $feed) {
|
foreach ($json['data'] as $feed) {
|
||||||
$podcasts[] = [
|
$podcasts[] = [
|
||||||
'provider' => 'fyyd',
|
|
||||||
'id' => $feed['id'],
|
'id' => $feed['id'],
|
||||||
|
'provider' => 'fyyd',
|
||||||
|
'website' => $feed['htmlURL'],
|
||||||
|
'description' => $feed['description'],
|
||||||
'title' => $feed['title'],
|
'title' => $feed['title'],
|
||||||
'author' => $feed['author'],
|
'author' => $feed['author'],
|
||||||
'image' => $feed['imgURL'],
|
'url' => $feed['xmlURL'],
|
||||||
'provider_url' => $feed['htmlURL'],
|
'position_last_week' => $feed['rank'],
|
||||||
'feed_url' => $feed['xmlURL'],
|
'mygpo_link' => $feed['url_fyyd'],
|
||||||
'last_pub' => $feed['lastpub'],
|
'logo_url' => $feed['imgURL'],
|
||||||
'nb_episodes' => $feed['episode_count'],
|
'lastpub' => $feed['lastpub'],
|
||||||
|
'episode_count' => $feed['episode_count'],
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -57,8 +62,12 @@ class FyydService implements IProvider
|
|||||||
return $podcasts;
|
return $podcasts;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function hot(): IResponse
|
/**
|
||||||
|
* @return Podcast[]
|
||||||
|
*/
|
||||||
|
public function hot(int $count = 10): array
|
||||||
{
|
{
|
||||||
|
$podcasts = [];
|
||||||
$language = 'en';
|
$language = 'en';
|
||||||
$userLang = $this->userService->getLangCode();
|
$userLang = $this->userService->getLangCode();
|
||||||
|
|
||||||
@ -75,10 +84,34 @@ class FyydService implements IProvider
|
|||||||
|
|
||||||
$podcastClient = $this->clientService->newClient();
|
$podcastClient = $this->clientService->newClient();
|
||||||
|
|
||||||
return $podcastClient->get(self::BASE_URL.'feature/podcast/hot', [
|
$podcastResponse = $podcastClient->get(self::BASE_URL.'feature/podcast/hot', [
|
||||||
'query' => [
|
'query' => [
|
||||||
|
'count' => $count,
|
||||||
'language' => $language,
|
'language' => $language,
|
||||||
],
|
],
|
||||||
]);
|
]);
|
||||||
|
$postCastJson = (array) json_decode((string) $podcastResponse->getBody(), true, flags: JSON_THROW_ON_ERROR);
|
||||||
|
|
||||||
|
if (array_key_exists('data', $postCastJson) && is_array($postCastJson['data'])) {
|
||||||
|
/** @var string[] $feed */
|
||||||
|
foreach ($postCastJson['data'] as $feed) {
|
||||||
|
$podcasts[] = [
|
||||||
|
'id' => $feed['id'],
|
||||||
|
'provider' => 'fyyd',
|
||||||
|
'website' => $feed['htmlURL'],
|
||||||
|
'description' => $feed['description'],
|
||||||
|
'title' => $feed['title'],
|
||||||
|
'author' => $feed['author'],
|
||||||
|
'url' => $feed['xmlURL'],
|
||||||
|
'position_last_week' => $feed['rank'],
|
||||||
|
'mygpo_link' => $feed['url_fyyd'],
|
||||||
|
'logo_url' => $feed['imgURL'],
|
||||||
|
'lastpub' => $feed['lastpub'],
|
||||||
|
'episode_count' => $feed['episode_count'],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $podcasts;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -4,20 +4,26 @@ declare(strict_types=1);
|
|||||||
|
|
||||||
namespace OCA\RePod\Service;
|
namespace OCA\RePod\Service;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @psalm-type Podcast = array{
|
||||||
|
* id: string,
|
||||||
|
* provider: string,
|
||||||
|
* website: string,
|
||||||
|
* description: string,
|
||||||
|
* title: string,
|
||||||
|
* author: string,
|
||||||
|
* url: string,
|
||||||
|
* position_last_week: ?string,
|
||||||
|
* mygpo_link: string,
|
||||||
|
* logo_url: string,
|
||||||
|
* lastpub: string,
|
||||||
|
* episode_count: string
|
||||||
|
* }
|
||||||
|
*/
|
||||||
interface IProvider
|
interface IProvider
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* @return array<array{
|
* @return Podcast[]
|
||||||
* provider: string,
|
|
||||||
* id: string,
|
|
||||||
* title: string,
|
|
||||||
* author: string,
|
|
||||||
* image: string,
|
|
||||||
* provider_url: string,
|
|
||||||
* feed_url: string,
|
|
||||||
* last_pub: string,
|
|
||||||
* nb_episodes: string
|
|
||||||
* }>
|
|
||||||
*/
|
*/
|
||||||
public function search(string $value): array;
|
public function search(string $value): array;
|
||||||
}
|
}
|
||||||
|
@ -34,15 +34,18 @@ class ItunesService implements IProvider
|
|||||||
/** @var string[] $feed */
|
/** @var string[] $feed */
|
||||||
foreach ($json['results'] as $feed) {
|
foreach ($json['results'] as $feed) {
|
||||||
$podcasts[] = [
|
$podcasts[] = [
|
||||||
'provider' => 'itunes',
|
|
||||||
'id' => $feed['id'],
|
'id' => $feed['id'],
|
||||||
|
'provider' => 'itunes',
|
||||||
|
'website' => $feed['trackViewUrl'],
|
||||||
|
'description' => $feed['primaryGenreName'],
|
||||||
'title' => $feed['trackName'],
|
'title' => $feed['trackName'],
|
||||||
'author' => $feed['artistName'],
|
'author' => $feed['artistName'],
|
||||||
'image' => $feed['artworkUrl600'],
|
'url' => $feed['feedUrl'],
|
||||||
'provider_url' => $feed['trackViewUrl'],
|
'position_last_week' => null,
|
||||||
'feed_url' => $feed['feedUrl'],
|
'mygpo_link' => $feed['trackViewUrl'],
|
||||||
'last_pub' => $feed['releaseDate'],
|
'logo_url' => $feed['artworkUrl600'],
|
||||||
'nb_episodes' => $feed['trackCount'],
|
'lastpub' => $feed['releaseDate'],
|
||||||
|
'episode_count' => $feed['trackCount'],
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
6302
package-lock.json
generated
6302
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -40,7 +40,7 @@
|
|||||||
"@nextcloud/babel-config": "^1.0.0",
|
"@nextcloud/babel-config": "^1.0.0",
|
||||||
"@nextcloud/browserslist-config": "^2.3.0",
|
"@nextcloud/browserslist-config": "^2.3.0",
|
||||||
"@nextcloud/eslint-config": "^8.2.1",
|
"@nextcloud/eslint-config": "^8.2.1",
|
||||||
"@nextcloud/stylelint-config": "^2.3.0",
|
"@nextcloud/stylelint-config": "^2.3.1",
|
||||||
"@nextcloud/webpack-vue-config": "^5.5.1"
|
"@nextcloud/webpack-vue-config": "^5.5.1"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
<template>
|
<template>
|
||||||
<NcAppNavigationItem :loading="loading"
|
<NcAppNavigationItem :loading="loading"
|
||||||
:title="feed ? feed.title : subscriptionUrl"
|
:title="feed ? feed.title : url"
|
||||||
:to="`/${subscriptionUrl}`">
|
:to="`/${url}`">
|
||||||
<template #icon>
|
<template #icon>
|
||||||
<NcAvatar v-if="feed"
|
<NcAvatar v-if="feed"
|
||||||
:display-name="feed.author"
|
:display-name="feed.author"
|
||||||
@ -29,7 +29,7 @@ import { generateUrl } from '@nextcloud/router'
|
|||||||
import { showError } from '@nextcloud/dialogs'
|
import { showError } from '@nextcloud/dialogs'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'SubscriptionListItem',
|
name: 'FeedListItem',
|
||||||
components: {
|
components: {
|
||||||
Alert,
|
Alert,
|
||||||
Delete,
|
Delete,
|
||||||
@ -38,7 +38,7 @@ export default {
|
|||||||
NcAvatar,
|
NcAvatar,
|
||||||
},
|
},
|
||||||
props: {
|
props: {
|
||||||
subscriptionUrl: {
|
url: {
|
||||||
type: String,
|
type: String,
|
||||||
required: true,
|
required: true,
|
||||||
},
|
},
|
||||||
@ -52,7 +52,7 @@ export default {
|
|||||||
},
|
},
|
||||||
async mounted() {
|
async mounted() {
|
||||||
try {
|
try {
|
||||||
const podcastData = await axios.get(generateUrl('/apps/gpoddersync/personal_settings/podcast_data?url={url}', { url: this.subscriptionUrl }))
|
const podcastData = await axios.get(generateUrl('/apps/gpoddersync/personal_settings/podcast_data?url={url}', { url: this.url }))
|
||||||
this.feed = podcastData.data.data
|
this.feed = podcastData.data.data
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
this.failed = true
|
this.failed = true
|
||||||
@ -65,7 +65,7 @@ export default {
|
|||||||
async deleteSubscription() {
|
async deleteSubscription() {
|
||||||
try {
|
try {
|
||||||
this.loading = true
|
this.loading = true
|
||||||
await axios.post(generateUrl('/apps/gpoddersync/subscription_change/create'), { add: [], remove: [this.subscriptionUrl] })
|
await axios.post(generateUrl('/apps/gpoddersync/subscription_change/create'), { add: [], remove: [this.url] })
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error(e)
|
console.error(e)
|
||||||
showError(t('Error while removing the feed'))
|
showError(t('Error while removing the feed'))
|
@ -2,14 +2,14 @@
|
|||||||
<ul>
|
<ul>
|
||||||
<NcListItem v-for="feed in feeds"
|
<NcListItem v-for="feed in feeds"
|
||||||
:key="`${feed.provider}_${feed.id}`"
|
:key="`${feed.provider}_${feed.id}`"
|
||||||
:counter-number="feed.nb_episodes"
|
:counter-number="feed.episode_count"
|
||||||
:details="formatTimeAgo(new Date(feed.last_pub))"
|
:details="formatTimeAgo(new Date(feed.lastpub))"
|
||||||
:title="feed.title"
|
:title="feed.title"
|
||||||
:to="`/${feed.feed_url}`">
|
:to="`/${feed.url}`">
|
||||||
<template #icon>
|
<template #icon>
|
||||||
<NcAvatar :display-name="feed.author"
|
<NcAvatar :display-name="feed.author"
|
||||||
:is-no-user="true"
|
:is-no-user="true"
|
||||||
:url="feed.image" />
|
:url="feed.logo_url" />
|
||||||
</template>
|
</template>
|
||||||
<template #subtitle>
|
<template #subtitle>
|
||||||
{{ feed.author }}
|
{{ feed.author }}
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
<template>
|
<template>
|
||||||
<a @click="addSubscription">
|
<a @click="addSubscription">
|
||||||
<img :alt="author"
|
<img :alt="`${title} - ${author}`"
|
||||||
:src="imgUrl"
|
:src="logo"
|
||||||
:title="author">
|
:title="author">
|
||||||
</a>
|
</a>
|
||||||
</template>
|
</template>
|
||||||
@ -15,23 +15,27 @@ export default {
|
|||||||
name: 'TopItem',
|
name: 'TopItem',
|
||||||
components: {},
|
components: {},
|
||||||
props: {
|
props: {
|
||||||
xmlUrl: {
|
|
||||||
type: String,
|
|
||||||
required: true,
|
|
||||||
},
|
|
||||||
imgUrl: {
|
|
||||||
type: String,
|
|
||||||
required: true,
|
|
||||||
},
|
|
||||||
author: {
|
author: {
|
||||||
type: String,
|
type: String,
|
||||||
required: true,
|
required: true,
|
||||||
},
|
},
|
||||||
|
logo: {
|
||||||
|
type: String,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
title: {
|
||||||
|
type: String,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
url: {
|
||||||
|
type: String,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
async addSubscription() {
|
async addSubscription() {
|
||||||
try {
|
try {
|
||||||
await axios.post(generateUrl('/apps/gpoddersync/subscription_change/create'), { add: [this.xmlUrl], remove: [] })
|
await axios.post(generateUrl('/apps/gpoddersync/subscription_change/create'), { add: [this.url], remove: [] })
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error(e)
|
console.error(e)
|
||||||
showError(t('Error while adding the feed'))
|
showError(t('Error while adding the feed'))
|
||||||
|
@ -8,8 +8,9 @@
|
|||||||
<ul v-if="!loading" class="tops">
|
<ul v-if="!loading" class="tops">
|
||||||
<li v-for="top in tops" :key="top.id">
|
<li v-for="top in tops" :key="top.id">
|
||||||
<TopItem :author="top.author"
|
<TopItem :author="top.author"
|
||||||
:img-url="top.imgURL"
|
:logo="top.logo_url"
|
||||||
:xml-url="top.xmlURL" />
|
:title="top.title"
|
||||||
|
:url="top.url" />
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
<span class="caption">{{ t('Suggests by fyyd') }}</span>
|
<span class="caption">{{ t('Suggests by fyyd') }}</span>
|
||||||
@ -39,8 +40,8 @@ export default {
|
|||||||
async mounted() {
|
async mounted() {
|
||||||
try {
|
try {
|
||||||
this.loading = true
|
this.loading = true
|
||||||
const top = await axios.get(generateUrl('/apps/repod/top'))
|
const toplist = await axios.get(generateUrl('/apps/repod/toplist/10'))
|
||||||
this.tops = top.data.data
|
this.tops = toplist.data
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error(e)
|
console.error(e)
|
||||||
showError(t('Could not fetch tops'))
|
showError(t('Could not fetch tops'))
|
||||||
|
@ -11,9 +11,9 @@
|
|||||||
</router-link>
|
</router-link>
|
||||||
<NcLoadingIcon v-if="loading" />
|
<NcLoadingIcon v-if="loading" />
|
||||||
<ul v-if="!loading">
|
<ul v-if="!loading">
|
||||||
<SubscriptionListItem v-for="subscriptionUrl of subscriptions"
|
<FeedListItem v-for="subscriptionUrl of subscriptions"
|
||||||
:key="subscriptionUrl"
|
:key="subscriptionUrl"
|
||||||
:subscription-url="subscriptionUrl" />
|
:url="subscriptionUrl" />
|
||||||
</ul>
|
</ul>
|
||||||
</NcAppContentList>
|
</NcAppContentList>
|
||||||
</NcAppNavigation>
|
</NcAppNavigation>
|
||||||
@ -31,20 +31,20 @@ import {
|
|||||||
NcAppNavigationNew,
|
NcAppNavigationNew,
|
||||||
NcLoadingIcon,
|
NcLoadingIcon,
|
||||||
} from '@nextcloud/vue'
|
} from '@nextcloud/vue'
|
||||||
|
import FeedListItem from '../components/FeedListItem.vue'
|
||||||
import Plus from 'vue-material-design-icons/Plus.vue'
|
import Plus from 'vue-material-design-icons/Plus.vue'
|
||||||
import SubscriptionListItem from '../components/SubscriptionListItem.vue'
|
|
||||||
import { showError } from '@nextcloud/dialogs'
|
import { showError } from '@nextcloud/dialogs'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'Index',
|
name: 'Index',
|
||||||
components: {
|
components: {
|
||||||
|
FeedListItem,
|
||||||
NcAppContent,
|
NcAppContent,
|
||||||
NcAppContentList,
|
NcAppContentList,
|
||||||
NcAppNavigation,
|
NcAppNavigation,
|
||||||
NcAppNavigationNew,
|
NcAppNavigationNew,
|
||||||
NcLoadingIcon,
|
NcLoadingIcon,
|
||||||
Plus,
|
Plus,
|
||||||
SubscriptionListItem,
|
|
||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
|
Loading…
Reference in New Issue
Block a user