Refacto to services
All checks were successful
repod / nextcloud (push) Successful in 44s
repod / nodejs (push) Successful in 1m15s

This commit is contained in:
Michel Roux 2023-07-28 02:37:57 +02:00
parent 5a590bd210
commit 4503df9d33
8 changed files with 184 additions and 139 deletions

View File

@ -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/{count}', 'verb' => 'GET'], ['name' => 'top#index', 'url' => '/top', 'verb' => 'GET'],
['name' => 'search#index', 'url' => '/search/{value}', 'verb' => 'GET'], ['name' => 'search#index', 'url' => '/search/{value}', 'verb' => 'GET'],
], ],
]; ];

View File

@ -5,20 +5,18 @@ declare(strict_types=1);
namespace OCA\RePod\Controller; namespace OCA\RePod\Controller;
use OCA\RePod\AppInfo\Application; use OCA\RePod\AppInfo\Application;
use OCA\RePod\Service\FyydService;
use OCA\RePod\Service\ItunesService;
use OCP\AppFramework\Controller; use OCP\AppFramework\Controller;
use OCP\AppFramework\Http\JSONResponse; use OCP\AppFramework\Http\JSONResponse;
use OCP\Http\Client\IClientService;
use OCP\IRequest; use OCP\IRequest;
use OCP\IUserSession;
use OCP\L10N\IFactory;
class SearchController extends Controller class SearchController extends Controller
{ {
public function __construct( public function __construct(
IRequest $request, IRequest $request,
private IClientService $clientService, private FyydService $fyydService,
private IFactory $l10n, private ItunesService $itunesService
private IUserSession $userSession
) { ) {
parent::__construct(Application::APP_ID, $request); parent::__construct(Application::APP_ID, $request);
} }
@ -26,71 +24,13 @@ class SearchController extends Controller
public function index(string $value): JSONResponse public function index(string $value): JSONResponse
{ {
$podcasts = []; $podcasts = [];
$providers = [$this->fyydService, $this->itunesService];
try { foreach ($providers as $provider) {
$fyydClient = $this->clientService->newClient(); try {
$fyydReponse = $fyydClient->get('https://api.fyyd.de/0.2/search/podcast', [ $podcasts = [...$podcasts, ...$provider->search($value)];
'query' => [ } catch (\Exception $e) {
'title' => $value,
'term' => $value,
],
]);
$fyydJson = (array) json_decode((string) $fyydReponse->getBody(), true, flags: JSON_THROW_ON_ERROR);
if (array_key_exists('data', $fyydJson) && is_array($fyydJson['data'])) {
/** @var string[] $fyydFeed */
foreach ($fyydJson['data'] as $fyydFeed) {
$lastPub = date_format(new \DateTime($fyydFeed['lastpub']), DATE_ATOM);
if ($lastPub) {
$podcasts[] = [
'provider' => 'fyyd',
'id' => $fyydFeed['id'],
'title' => $fyydFeed['title'],
'author' => $fyydFeed['author'],
'image' => $fyydFeed['imgURL'],
'provider_url' => $fyydFeed['htmlURL'],
'feed_url' => $fyydFeed['xmlURL'],
'last_pub' => $lastPub,
'nb_episodes' => $fyydFeed['episode_count'],
];
}
}
} }
} catch (\Exception $e) {
}
try {
$itunesClient = $this->clientService->newClient();
$itunesResponse = $itunesClient->get('https://itunes.apple.com/search', [
'query' => [
'media' => 'podcast',
'term' => $value,
'lang' => $this->l10n->getUserLanguage($this->userSession->getUser()),
],
]);
$itunesJson = (array) json_decode((string) $itunesResponse->getBody(), true, flags: JSON_THROW_ON_ERROR);
if (array_key_exists('data', $itunesJson) && is_array($itunesJson['data'])) {
/** @var string[] $itunesFeed */
foreach ($itunesJson['results'] as $itunesFeed) {
$lastPub = date_format(new \DateTime($itunesFeed['releaseDate']), DATE_ATOM);
if ($lastPub) {
$podcasts[] = [
'id' => $itunesFeed['id'],
'title' => $itunesFeed['trackName'],
'author' => $itunesFeed['artistName'],
'image' => $itunesFeed['artworkUrl600'],
'provider_url' => $itunesFeed['trackViewUrl'],
'feed_url' => $itunesFeed['feedUrl'],
'last_pub' => $lastPub,
'nb_episodes' => $itunesFeed['trackCount'],
];
}
}
}
} catch (\Exception $e) {
} }
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['last_pub']) <=> new \DateTime((string) $a['last_pub']));

View File

@ -5,21 +5,17 @@ declare(strict_types=1);
namespace OCA\RePod\Controller; namespace OCA\RePod\Controller;
use OCA\RePod\AppInfo\Application; use OCA\RePod\AppInfo\Application;
use OCA\RePod\Service\FyydService;
use OCP\AppFramework\Controller; use OCP\AppFramework\Controller;
use OCP\AppFramework\Http; use OCP\AppFramework\Http;
use OCP\AppFramework\Http\JSONResponse; use OCP\AppFramework\Http\JSONResponse;
use OCP\Http\Client\IClientService;
use OCP\IRequest; use OCP\IRequest;
use OCP\IUserSession;
use OCP\L10N\IFactory;
class TopController extends Controller class TopController extends Controller
{ {
public function __construct( public function __construct(
IRequest $request, IRequest $request,
private IClientService $clientService, private FyydService $fyydService
private IFactory $l10n,
private IUserSession $userSession
) { ) {
parent::__construct(Application::APP_ID, $request); parent::__construct(Application::APP_ID, $request);
} }
@ -28,34 +24,13 @@ class TopController extends Controller
* @NoAdminRequired * @NoAdminRequired
* @NoCSRFRequired * @NoCSRFRequired
*/ */
public function index(int $count = 10): JSONResponse public function index(): JSONResponse
{ {
$language = 'en';
try { try {
$langClient = $this->clientService->newClient(); $response = $this->fyydService->hot();
$langResponse = $langClient->get('https://api.fyyd.de/0.2/feature/podcast/hot/languages'); $json = (array) json_decode((string) $response->getBody(), true, flags: JSON_THROW_ON_ERROR);
$langJson = (array) json_decode((string) $langResponse->getBody(), true, flags: JSON_THROW_ON_ERROR);
if (array_key_exists('data', $langJson) && is_array($langJson['data'])) {
$language = $this->l10n->getUserLanguage($this->userSession->getUser());
$language = explode('_', $language);
$language = count($language) > 1 ? $language[1] : $language[0];
$language = in_array($language, $langJson['data']) ? $language : 'en';
}
} catch (\Exception $e) {
}
try { return new JSONResponse($json, $response->getStatusCode());
$podcastClient = $this->clientService->newClient();
$podcastReponse = $podcastClient->get('https://api.fyyd.de/0.2/feature/podcast/hot', [
'query' => [
'count' => $count,
'language' => $language,
],
]);
$podcastJson = (array) json_decode((string) $podcastReponse->getBody(), true, flags: JSON_THROW_ON_ERROR);
return new JSONResponse($podcastJson, $podcastReponse->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);
} }

View File

@ -0,0 +1,79 @@
<?php
declare(strict_types=1);
namespace OCA\RePod\Service;
use OCP\Http\Client\IClientService;
use OCP\Http\Client\IResponse;
use OCP\IUserSession;
use OCP\L10N\IFactory;
class FyydService implements IProvider
{
public function __construct(
private IClientService $clientService,
private IFactory $l10n,
private IUserSession $userSession
) {
}
public function search(string $value): array
{
$podcasts = [];
$client = $this->clientService->newClient();
$response = $client->get('https://api.fyyd.de/0.2/search/podcast', [
'query' => [
'title' => $value,
'term' => $value,
],
]);
$json = (array) json_decode((string) $response->getBody(), true, flags: JSON_THROW_ON_ERROR);
if (array_key_exists('data', $json) && is_array($json['data'])) {
/** @var string[] $feed */
foreach ($json['data'] as $feed) {
$podcasts[] = [
'provider' => 'fyyd',
'id' => $feed['id'],
'title' => $feed['title'],
'author' => $feed['author'],
'image' => $feed['imgURL'],
'provider_url' => $feed['htmlURL'],
'feed_url' => $feed['xmlURL'],
'last_pub' => $feed['lastpub'],
'nb_episodes' => $feed['episode_count'],
];
}
}
return $podcasts;
}
public function hot(): IResponse
{
$language = 'en';
try {
$langClient = $this->clientService->newClient();
$langResponse = $langClient->get('https://api.fyyd.de/0.2/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 = $this->l10n->getUserLanguage($this->userSession->getUser());
$language = explode('_', $language);
$language = count($language) > 1 ? $language[1] : $language[0];
$language = in_array($language, $langJson['data']) ? $language : 'en';
}
} catch (\Exception $e) {
}
$podcastClient = $this->clientService->newClient();
return $podcastClient->get('https://api.fyyd.de/0.2/feature/podcast/hot', [
'query' => [
'language' => $language,
],
]);
}
}

23
lib/Service/IProvider.php Normal file
View File

@ -0,0 +1,23 @@
<?php
declare(strict_types=1);
namespace OCA\RePod\Service;
interface IProvider
{
/**
* @return array<array{
* 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;
}

View File

@ -0,0 +1,53 @@
<?php
declare(strict_types=1);
namespace OCA\RePod\Service;
use OCP\Http\Client\IClientService;
use OCP\IUserSession;
use OCP\L10N\IFactory;
class ItunesService implements IProvider
{
public function __construct(
private IClientService $clientService,
private IFactory $l10n,
private IUserSession $userSession
) {
}
public function search(string $value): array
{
$podcasts = [];
$client = $this->clientService->newClient();
$response = $client->get('https://itunes.apple.com/search', [
'query' => [
'media' => 'podcast',
'term' => $value,
'lang' => $this->l10n->getUserLanguage($this->userSession->getUser()),
],
]);
$json = (array) json_decode((string) $response->getBody(), true, flags: JSON_THROW_ON_ERROR);
if (array_key_exists('data', $json) && is_array($json['data'])) {
/** @var string[] $feed */
foreach ($json['results'] as $feed) {
$podcasts[] = [
'provider' => 'itunes',
'id' => $feed['id'],
'title' => $feed['trackName'],
'author' => $feed['artistName'],
'image' => $feed['artworkUrl600'],
'provider_url' => $feed['trackViewUrl'],
'feed_url' => $feed['feedUrl'],
'last_pub' => $feed['releaseDate'],
'nb_episodes' => $feed['trackCount'],
];
}
}
return $podcasts;
}
}

View File

@ -1,12 +1,7 @@
<template> <template>
<fragment> <fragment>
<p class="more"> <p>
<span>{{ t('Discover') }}</span> <span>{{ t('Discover') }}</span>
<NcSelect v-model="count"
class="select"
:components="Deselect"
:options="[...Array(10).keys()].map(x=>(x+1)*10)"
@input="fetch" />
</p> </p>
<p> <p>
<NcLoadingIcon v-if="loading" /> <NcLoadingIcon v-if="loading" />
@ -21,7 +16,7 @@
</template> </template>
<script> <script>
import { NcLoadingIcon, NcSelect } from '@nextcloud/vue' import { NcLoadingIcon } from '@nextcloud/vue'
import TopItem from './TopItem.vue' import TopItem from './TopItem.vue'
import axios from '@nextcloud/axios' import axios from '@nextcloud/axios'
import { generateUrl } from '@nextcloud/router' import { generateUrl } from '@nextcloud/router'
@ -31,35 +26,25 @@ export default {
name: 'TopList', name: 'TopList',
components: { components: {
NcLoadingIcon, NcLoadingIcon,
NcSelect,
TopItem, TopItem,
}, },
data() { data() {
return { return {
tops: [], tops: [],
loading: true, loading: true,
count: 10,
Deselect: {
render: null,
},
} }
}, },
async mounted() { async mounted() {
await this.fetch() try {
}, this.loading = true
methods: { const top = await axios.get(generateUrl('/apps/repod/top'))
async fetch() { this.tops = top.data.data
try { } catch (e) {
this.loading = true console.error(e)
const top = await axios.get(generateUrl('/apps/repod/top/{count}', { count: this.count })) showError(t('Could not fetch tops'))
this.tops = top.data.data } finally {
} catch (e) { this.loading = false
console.error(e) }
showError(t('Could not fetch tops'))
} finally {
this.loading = false
}
},
}, },
} }
</script> </script>
@ -76,16 +61,6 @@ export default {
flex-basis: 15%; flex-basis: 15%;
} }
.select {
min-width: 160px;
}
.more {
display: flex;
justify-content: space-between;
align-items: center;
}
.caption { .caption {
float: right; float: right;
font-size: small; font-size: small;

View File

@ -37,7 +37,7 @@ export default {
<style scoped> <style scoped>
.main { .main {
margin: 22px 55px; margin: 15px 51px;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
gap: 1rem; gap: 1rem;