feat: add unified search integration
This commit is contained in:
parent
d6a9eb0c31
commit
c951a93b8c
@ -11,7 +11,7 @@ You need to have [GPodderSync](https://apps.nextcloud.com/apps/gpoddersync) inst
|
|||||||
- [x] Sync them with GPodderSync compatible clients
|
- [x] Sync them with GPodderSync compatible clients
|
||||||
- [ ] Import and export subscriptions
|
- [ ] Import and export subscriptions
|
||||||
- [x] Mobile friendly interface
|
- [x] Mobile friendly interface
|
||||||
- [ ] Unified search integration
|
- [x] Unified search integration
|
||||||
- [x] Interface in multiple languages
|
- [x] Interface in multiple languages
|
||||||
|
|
||||||
## Screenshots
|
## Screenshots
|
||||||
|
@ -4,6 +4,7 @@ declare(strict_types=1);
|
|||||||
|
|
||||||
namespace OCA\RePod\AppInfo;
|
namespace OCA\RePod\AppInfo;
|
||||||
|
|
||||||
|
use OCA\RePod\Service\SearchProvider;
|
||||||
use OCP\App\AppPathNotFoundException;
|
use OCP\App\AppPathNotFoundException;
|
||||||
use OCP\App\IAppManager;
|
use OCP\App\IAppManager;
|
||||||
use OCP\AppFramework\App;
|
use OCP\AppFramework\App;
|
||||||
@ -43,5 +44,7 @@ class Application extends App implements IBootstrap
|
|||||||
$initialState->provideInitialState('gpodder', $gpoddersync);
|
$initialState->provideInitialState('gpodder', $gpoddersync);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function register(IRegistrationContext $context): void {}
|
public function register(IRegistrationContext $context): void {
|
||||||
|
$context->registerSearchProvider(SearchProvider::class);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -19,9 +19,9 @@ class EpisodesController extends Controller
|
|||||||
{
|
{
|
||||||
public function __construct(
|
public function __construct(
|
||||||
IRequest $request,
|
IRequest $request,
|
||||||
private IClientService $clientService,
|
|
||||||
private EpisodeActionReader $episodeActionReader,
|
private EpisodeActionReader $episodeActionReader,
|
||||||
private EpisodeActionRepository $episodeActionRepository,
|
private EpisodeActionRepository $episodeActionRepository,
|
||||||
|
private IClientService $clientService,
|
||||||
private UserService $userService
|
private UserService $userService
|
||||||
) {
|
) {
|
||||||
parent::__construct(Application::APP_ID, $request);
|
parent::__construct(Application::APP_ID, $request);
|
||||||
|
@ -14,7 +14,10 @@ use OCP\Util;
|
|||||||
|
|
||||||
class PageController extends Controller
|
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);
|
parent::__construct(Application::APP_ID, $request);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -4,41 +4,22 @@ declare(strict_types=1);
|
|||||||
|
|
||||||
namespace OCA\RePod\Controller;
|
namespace OCA\RePod\Controller;
|
||||||
|
|
||||||
use OCA\GPodderSync\Core\PodcastData\PodcastData;
|
|
||||||
use OCA\RePod\AppInfo\Application;
|
use OCA\RePod\AppInfo\Application;
|
||||||
use OCA\RePod\Service\FyydService;
|
use OCA\RePod\Service\MultiPodService;
|
||||||
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\IRequest;
|
use OCP\IRequest;
|
||||||
use Psr\Log\LoggerInterface;
|
|
||||||
|
|
||||||
class SearchController extends Controller
|
class SearchController extends Controller
|
||||||
{
|
{
|
||||||
public function __construct(
|
public function __construct(
|
||||||
IRequest $request,
|
IRequest $request,
|
||||||
private LoggerInterface $logger,
|
private MultiPodService $multiPodService
|
||||||
private FyydService $fyydService,
|
|
||||||
private ItunesService $itunesService
|
|
||||||
) {
|
) {
|
||||||
parent::__construct(Application::APP_ID, $request);
|
parent::__construct(Application::APP_ID, $request);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function index(string $value): JSONResponse {
|
public function index(string $value): JSONResponse {
|
||||||
$podcasts = [];
|
return new JSONResponse($this->multiPodService->search($value));
|
||||||
$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);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -6,20 +6,16 @@ namespace OCA\RePod\Service;
|
|||||||
|
|
||||||
use OCA\GPodderSync\Core\PodcastData\PodcastData;
|
use OCA\GPodderSync\Core\PodcastData\PodcastData;
|
||||||
use OCP\Http\Client\IClientService;
|
use OCP\Http\Client\IClientService;
|
||||||
use OCP\IUserSession;
|
|
||||||
use OCP\L10N\IFactory;
|
|
||||||
use Psr\Log\LoggerInterface;
|
use Psr\Log\LoggerInterface;
|
||||||
|
|
||||||
class FyydService implements IProvider
|
class FyydService implements IPodProvider
|
||||||
{
|
{
|
||||||
private const BASE_URL = 'https://api.fyyd.de/0.2/';
|
private const BASE_URL = 'https://api.fyyd.de/0.2/';
|
||||||
|
|
||||||
public function __construct(
|
public function __construct(
|
||||||
private UserService $userService,
|
|
||||||
private IClientService $clientService,
|
private IClientService $clientService,
|
||||||
private IFactory $l10n,
|
private LoggerInterface $logger,
|
||||||
private IUserSession $userSession,
|
private UserService $userService
|
||||||
private LoggerInterface $logger
|
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
public function search(string $value): array {
|
public function search(string $value): array {
|
||||||
|
@ -6,7 +6,7 @@ namespace OCA\RePod\Service;
|
|||||||
|
|
||||||
use OCA\GPodderSync\Core\PodcastData\PodcastData;
|
use OCA\GPodderSync\Core\PodcastData\PodcastData;
|
||||||
|
|
||||||
interface IProvider
|
interface IPodProvider
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* @return PodcastData[]
|
* @return PodcastData[]
|
@ -7,7 +7,7 @@ namespace OCA\RePod\Service;
|
|||||||
use OCA\GPodderSync\Core\PodcastData\PodcastData;
|
use OCA\GPodderSync\Core\PodcastData\PodcastData;
|
||||||
use OCP\Http\Client\IClientService;
|
use OCP\Http\Client\IClientService;
|
||||||
|
|
||||||
class ItunesService implements IProvider
|
class ItunesService implements IPodProvider
|
||||||
{
|
{
|
||||||
private const BASE_URL = 'https://itunes.apple.com/';
|
private const BASE_URL = 'https://itunes.apple.com/';
|
||||||
|
|
||||||
|
53
lib/Service/MultiPodService.php
Normal file
53
lib/Service/MultiPodService.php
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace OCA\RePod\Service;
|
||||||
|
|
||||||
|
use OCA\GPodderSync\Core\PodcastData\PodcastData;
|
||||||
|
use Psr\Log\LoggerInterface;
|
||||||
|
|
||||||
|
class MultiPodService implements IPodProvider
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @var IPodProvider[]
|
||||||
|
*/
|
||||||
|
private array $providers = [];
|
||||||
|
|
||||||
|
public function __construct(
|
||||||
|
FyydService $fyydService,
|
||||||
|
ItunesService $itunesService,
|
||||||
|
private LoggerInterface $logger
|
||||||
|
) {
|
||||||
|
$this->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())
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
65
lib/Service/SearchProvider.php
Normal file
65
lib/Service/SearchProvider.php
Normal file
@ -0,0 +1,65 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace OCA\RePod\Service;
|
||||||
|
|
||||||
|
use OCA\RePod\AppInfo\Application;
|
||||||
|
use OCP\IL10N;
|
||||||
|
use OCP\IURLGenerator;
|
||||||
|
use OCP\IUser;
|
||||||
|
use OCP\Search\IProvider;
|
||||||
|
use OCP\Search\ISearchQuery;
|
||||||
|
use OCP\Search\SearchResult;
|
||||||
|
use OCP\Search\SearchResultEntry;
|
||||||
|
|
||||||
|
class SearchProvider implements IProvider
|
||||||
|
{
|
||||||
|
public function __construct(
|
||||||
|
private IL10N $l10n,
|
||||||
|
private IURLGenerator $urlGenerator,
|
||||||
|
private MultiPodService $multiPodService
|
||||||
|
) {}
|
||||||
|
|
||||||
|
public function getId(): string {
|
||||||
|
return Application::APP_ID;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getName(): string {
|
||||||
|
return $this->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
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user