From c951a93b8c1a0b3a3eb80bd388696dd1bc8eeb68 Mon Sep 17 00:00:00 2001 From: Michel Roux Date: Thu, 18 Jan 2024 11:43:58 +0100 Subject: [PATCH] feat: add unified search integration --- README.md | 2 +- lib/AppInfo/Application.php | 5 +- lib/Controller/EpisodesController.php | 2 +- lib/Controller/PageController.php | 5 +- lib/Controller/SearchController.php | 25 +------ lib/Service/FyydService.php | 10 +-- .../{IProvider.php => IPodProvider.php} | 2 +- lib/Service/ItunesService.php | 2 +- lib/Service/MultiPodService.php | 53 +++++++++++++++ lib/Service/SearchProvider.php | 65 +++++++++++++++++++ 10 files changed, 136 insertions(+), 35 deletions(-) rename lib/Service/{IProvider.php => IPodProvider.php} (89%) create mode 100644 lib/Service/MultiPodService.php create mode 100644 lib/Service/SearchProvider.php diff --git a/README.md b/README.md index 4567988..f712f3d 100644 --- a/README.md +++ b/README.md @@ -11,7 +11,7 @@ You need to have [GPodderSync](https://apps.nextcloud.com/apps/gpoddersync) inst - [x] Sync them with GPodderSync compatible clients - [ ] Import and export subscriptions - [x] Mobile friendly interface -- [ ] Unified search integration +- [x] Unified search integration - [x] Interface in multiple languages ## Screenshots diff --git a/lib/AppInfo/Application.php b/lib/AppInfo/Application.php index 56611b7..a5904dc 100644 --- a/lib/AppInfo/Application.php +++ b/lib/AppInfo/Application.php @@ -4,6 +4,7 @@ declare(strict_types=1); namespace OCA\RePod\AppInfo; +use OCA\RePod\Service\SearchProvider; use OCP\App\AppPathNotFoundException; use OCP\App\IAppManager; use OCP\AppFramework\App; @@ -43,5 +44,7 @@ class Application extends App implements IBootstrap $initialState->provideInitialState('gpodder', $gpoddersync); } - public function register(IRegistrationContext $context): void {} + public function register(IRegistrationContext $context): void { + $context->registerSearchProvider(SearchProvider::class); + } } diff --git a/lib/Controller/EpisodesController.php b/lib/Controller/EpisodesController.php index 088ff83..f292adc 100644 --- a/lib/Controller/EpisodesController.php +++ b/lib/Controller/EpisodesController.php @@ -19,9 +19,9 @@ class EpisodesController extends Controller { public function __construct( IRequest $request, - private IClientService $clientService, private EpisodeActionReader $episodeActionReader, private EpisodeActionRepository $episodeActionRepository, + private IClientService $clientService, private UserService $userService ) { parent::__construct(Application::APP_ID, $request); diff --git a/lib/Controller/PageController.php b/lib/Controller/PageController.php index 47390c0..4de3880 100644 --- a/lib/Controller/PageController.php +++ b/lib/Controller/PageController.php @@ -14,7 +14,10 @@ use OCP\Util; class PageController extends Controller { - public function __construct(IRequest $request, private IConfig $config) { + public function __construct( + IRequest $request, + private IConfig $config + ) { parent::__construct(Application::APP_ID, $request); } diff --git a/lib/Controller/SearchController.php b/lib/Controller/SearchController.php index 3c808c3..17d5685 100644 --- a/lib/Controller/SearchController.php +++ b/lib/Controller/SearchController.php @@ -4,41 +4,22 @@ declare(strict_types=1); namespace OCA\RePod\Controller; -use OCA\GPodderSync\Core\PodcastData\PodcastData; use OCA\RePod\AppInfo\Application; -use OCA\RePod\Service\FyydService; -use OCA\RePod\Service\ItunesService; +use OCA\RePod\Service\MultiPodService; use OCP\AppFramework\Controller; use OCP\AppFramework\Http\JSONResponse; use OCP\IRequest; -use Psr\Log\LoggerInterface; class SearchController extends Controller { public function __construct( IRequest $request, - private LoggerInterface $logger, - private FyydService $fyydService, - private ItunesService $itunesService + private MultiPodService $multiPodService ) { parent::__construct(Application::APP_ID, $request); } public function index(string $value): JSONResponse { - $podcasts = []; - $providers = [$this->fyydService, $this->itunesService]; - - foreach ($providers as $provider) { - try { - $podcasts = [...$podcasts, ...$provider->search($value)]; - } catch (\Exception $e) { - $this->logger->error($e->getMessage(), $e->getTrace()); - } - } - - usort($podcasts, fn (PodcastData $a, PodcastData $b) => $b->getFetchedAtUnix() <=> $a->getFetchedAtUnix()); - $podcasts = array_values(array_intersect_key($podcasts, array_unique(array_map(fn (PodcastData $feed) => $feed->getLink(), $podcasts)))); - - return new JSONResponse($podcasts); + return new JSONResponse($this->multiPodService->search($value)); } } diff --git a/lib/Service/FyydService.php b/lib/Service/FyydService.php index 0a2c1e1..f8caf73 100644 --- a/lib/Service/FyydService.php +++ b/lib/Service/FyydService.php @@ -6,20 +6,16 @@ namespace OCA\RePod\Service; use OCA\GPodderSync\Core\PodcastData\PodcastData; use OCP\Http\Client\IClientService; -use OCP\IUserSession; -use OCP\L10N\IFactory; use Psr\Log\LoggerInterface; -class FyydService implements IProvider +class FyydService implements IPodProvider { private const BASE_URL = 'https://api.fyyd.de/0.2/'; public function __construct( - private UserService $userService, private IClientService $clientService, - private IFactory $l10n, - private IUserSession $userSession, - private LoggerInterface $logger + private LoggerInterface $logger, + private UserService $userService ) {} public function search(string $value): array { diff --git a/lib/Service/IProvider.php b/lib/Service/IPodProvider.php similarity index 89% rename from lib/Service/IProvider.php rename to lib/Service/IPodProvider.php index 3476fc4..f780a47 100644 --- a/lib/Service/IProvider.php +++ b/lib/Service/IPodProvider.php @@ -6,7 +6,7 @@ namespace OCA\RePod\Service; use OCA\GPodderSync\Core\PodcastData\PodcastData; -interface IProvider +interface IPodProvider { /** * @return PodcastData[] diff --git a/lib/Service/ItunesService.php b/lib/Service/ItunesService.php index a969b9f..5e8d16f 100644 --- a/lib/Service/ItunesService.php +++ b/lib/Service/ItunesService.php @@ -7,7 +7,7 @@ namespace OCA\RePod\Service; use OCA\GPodderSync\Core\PodcastData\PodcastData; use OCP\Http\Client\IClientService; -class ItunesService implements IProvider +class ItunesService implements IPodProvider { private const BASE_URL = 'https://itunes.apple.com/'; diff --git a/lib/Service/MultiPodService.php b/lib/Service/MultiPodService.php new file mode 100644 index 0000000..5ca82cd --- /dev/null +++ b/lib/Service/MultiPodService.php @@ -0,0 +1,53 @@ +providers = [$fyydService, $itunesService]; + } + + /** + * @return PodcastData[] + */ + public function search(string $value): array { + $podcasts = []; + + foreach ($this->providers as $provider) { + try { + $podcasts = [...$podcasts, ...$provider->search($value)]; + } catch (\Exception $e) { + $this->logger->error($e->getMessage(), $e->getTrace()); + } + } + + usort($podcasts, fn (PodcastData $a, PodcastData $b) => $b->getFetchedAtUnix() <=> $a->getFetchedAtUnix()); + + return array_values( + array_intersect_key( + $podcasts, + array_unique( + array_map( + fn (PodcastData $feed) => $feed->getLink(), + array_filter($podcasts, fn (PodcastData $feed) => $feed->getLink()) + ) + ) + ) + ); + } +} diff --git a/lib/Service/SearchProvider.php b/lib/Service/SearchProvider.php new file mode 100644 index 0000000..9711f1e --- /dev/null +++ b/lib/Service/SearchProvider.php @@ -0,0 +1,65 @@ +l10n->t('Podcasts'); + } + + public function getOrder(string $route, array $routeParameters): ?int { + if (0 === strpos($route, Application::APP_ID.'.')) { + // Active app, prefer my results + return -1; + } + + return 25; + } + + public function search(IUser $user, ISearchQuery $query): SearchResult { + $podcasts = $this->multiPodService->search($query->getTerm()); + + $searchResults = []; + foreach ($podcasts as $podcast) { + $title = $podcast->getTitle(); + $link = $podcast->getLink(); + + if ($title && $link) { + $searchResults[] = new SearchResultEntry( + $podcast->getImageUrl() ?? $this->urlGenerator->linkTo(Application::APP_ID, 'img/app.svg'), + $title, + $podcast->getAuthor() ?? '', + $this->urlGenerator->linkToRoute('repod.page.index').'/#/'.urlencode(base64_encode($link)), + $this->urlGenerator->linkTo(Application::APP_ID, 'img/app.svg') + ); + } + } + + return SearchResult::complete( + $this->l10n->t('Podcasts'), + $searchResults + ); + } +}