Add Meilisearch to media list function.

This commit is contained in:
Buster Neece 2023-01-29 10:32:06 -06:00
parent dc9b189a3f
commit 0a08a6fd4e
No known key found for this signature in database
GPG Key ID: F1D2E64A0005E80E
3 changed files with 86 additions and 25 deletions

View File

@ -12,6 +12,7 @@ use App\Http\RouterInterface;
use App\Http\ServerRequest;
use App\Media\MimeType;
use App\Paginator;
use App\Service\Meilisearch;
use App\Utilities;
use Doctrine\Common\Collections\Criteria;
use Doctrine\ORM\EntityManagerInterface;
@ -27,7 +28,8 @@ final class ListAction
public function __construct(
private readonly EntityManagerInterface $em,
private readonly CacheInterface $cache
private readonly CacheInterface $cache,
private readonly Meilisearch $meilisearch
) {
}
@ -104,7 +106,7 @@ final class ListAction
)->setParameter('storageLocation', $storageLocation)
->setParameter('path', $pathLike);
if (!empty($searchPhrase)) {
if ($isSearch) {
if ('special:unprocessable' === $searchPhrase) {
$mediaInDirRaw = [];
@ -130,29 +132,30 @@ final class ListAction
$mediaQueryBuilder->andWhere(
'sm.id NOT IN (SELECT spm2.media_id FROM App\Entity\StationPlaylistMedia spm2)'
);
} elseif (str_starts_with($searchPhrase, 'playlist:')) {
[, $playlistName] = explode(':', $searchPhrase, 2);
} else {
[$searchPhrase, $playlist] = $this->parseSearchQuery($station, $searchPhrase);
$playlist = $this->em->getRepository(Entity\StationPlaylist::class)
->findOneBy(
[
'station' => $station,
'name' => $playlistName,
]
);
if ($this->meilisearch->isSupported()) {
$ids = $this->meilisearch
->getIndex($storageLocation)
->searchMedia($searchPhrase, $playlist);
if (!$playlist instanceof Entity\StationPlaylist) {
return $response->withStatus(400)
->withJson(new Entity\Api\Error(400, 'Playlist not found.'));
$mediaQueryBuilder->andWhere(
'sm.id IN (:ids)'
)->setParameter('ids', $ids);
} else {
if (null !== $playlist) {
$mediaQueryBuilder->andWhere(
'sm.id IN (SELECT spm2.media_id FROM App\Entity\StationPlaylistMedia spm2 '
. 'WHERE spm2.playlist = :playlist)'
)->setParameter('playlist', $playlist);
}
if (!empty($searchPhrase)) {
$mediaQueryBuilder->andWhere('(sm.title LIKE :query OR sm.artist LIKE :query)')
->setParameter('query', '%' . $searchPhrase . '%');
}
}
$mediaQueryBuilder->andWhere(
'sm.id IN (SELECT spm2.media_id FROM App\Entity\StationPlaylistMedia spm2 '
. 'WHERE spm2.playlist = :playlist)'
)->setParameter('playlist', $playlist);
} elseif (!in_array($searchPhrase, ['*', '%'], true)) {
$mediaQueryBuilder->andWhere('(sm.title LIKE :query OR sm.artist LIKE :query)')
->setParameter('query', '%' . $searchPhrase . '%');
}
$mediaQuery = $mediaQueryBuilder->getQuery();
@ -246,7 +249,7 @@ final class ListAction
$unprocessableMedia[$unprocessableRow['path']] = $unprocessableRow['error'];
}
if (!empty($searchPhrase)) {
if ($isSearch) {
if ('special:unprocessable' === $searchPhrase) {
$files = array_keys($unprocessableMedia);
} else {
@ -284,7 +287,7 @@ final class ListAction
$row->size = ($row->is_dir) ? 0 : $fs->fileSize($row->path);
$shortname = (!empty($searchPhrase))
$shortname = ($isSearch)
? $row->path
: basename($row->path);
@ -353,6 +356,35 @@ final class ListAction
return $paginator->write($response);
}
private function parseSearchQuery(
Entity\Station $station,
string $query
): array {
$playlist = null;
if (str_contains($query, 'playlist:')) {
preg_match('/playlist:(\w*)/', $query, $matches, PREG_UNMATCHED_AS_NULL);
if ($matches[1]) {
$playlist = $this->em->getRepository(Entity\StationPlaylist::class)
->findOneBy(
[
'station' => $station,
'name' => $matches[1],
]
);
}
$query = trim(str_replace($matches[0] ?? '', '', $query));
}
if (in_array($query, ['*', '%'], true)) {
$query = '';
}
return [$query, $playlist];
}
private static function sortRows(
Entity\Api\FileList $a,
Entity\Api\FileList $b,

View File

@ -25,7 +25,7 @@ final class Meilisearch
public function isSupported(): bool
{
return $this->environment->isDocker();
return $this->environment->isDocker() && !$this->environment->isTesting();
}
public function getClient(): Client

View File

@ -7,6 +7,7 @@ namespace App\Service\Meilisearch;
use App\Doctrine\ReloadableEntityManagerInterface;
use App\Entity\Repository\CustomFieldRepository;
use App\Entity\Station;
use App\Entity\StationPlaylist;
use App\Entity\StorageLocation;
use App\Environment;
use App\Service\Meilisearch;
@ -14,6 +15,7 @@ use Doctrine\ORM\AbstractQuery;
use Meilisearch\Contracts\DocumentsQuery;
use Meilisearch\Endpoints\Indexes;
use Meilisearch\Exceptions\ApiException;
use Meilisearch\Search\SearchResult;
final class Index
{
@ -446,6 +448,33 @@ final class Index
);
}
public function searchMedia(
string $query,
?StationPlaylist $playlist = null
): array {
$searchParams = [
'hitsPerPage' => PHP_INT_MAX,
'page' => 1,
];
if (null !== $playlist) {
$station = $playlist->getStation();
$searchParams['filter'] = [
[
'station_' . $station->getIdRequired() . '_playlists = ' . $playlist->getIdRequired(),
],
];
}
/** @var SearchResult $searchResult */
$searchResult = $this->indexClient->search(
$query,
$searchParams
);
return array_column($searchResult->getHits(), 'id');
}
/** @return int[] */
private function getStationIds(): array
{