Skip to content

Commit

Permalink
Added API endpoint for person- and cover images.
Browse files Browse the repository at this point in the history
  • Loading branch information
JVT038 committed Feb 25, 2024
1 parent a367d20 commit 2d8be77
Show file tree
Hide file tree
Showing 8 changed files with 181 additions and 0 deletions.
1 change: 1 addition & 0 deletions bootstrap.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
\Movary\ValueObject\Config::class => DI\factory([Factory::class, 'createConfig']),
\Movary\Api\Trakt\TraktApi::class => DI\factory([Factory::class, 'createTraktApi']),
\Movary\Service\ImageCacheService::class => DI\factory([Factory::class, 'createImageCacheService']),
\Movary\HttpController\Api\ImagesController::class => DI\factory([Factory::class, 'createImagesController']),
\Movary\JobQueue\JobQueueScheduler::class => DI\factory([Factory::class, 'createJobQueueScheduler']),
\Movary\Api\Tmdb\TmdbClient::class => DI\factory([Factory::class, 'createTmdbApiClient']),
\Movary\Service\UrlGenerator::class => DI\factory([Factory::class, 'createUrlGenerator']),
Expand Down
74 changes: 74 additions & 0 deletions docs/openapi.json
Original file line number Diff line number Diff line change
Expand Up @@ -1066,6 +1066,80 @@
}
}
},
"/images/person/{id}": {
"get": {
"tags": [
"Images"
],
"summary": "Get person image.",
"description": "Get image of an actor, actress or directory based on their Movary ID.",
"parameters": [
{
"name": "ID",
"in": "path",
"description": "ID of the person in Movary",
"required": true,
"schema": {
"type": "integer"
},
"example": 1
}
],
"responses": {
"200": {
"description": "Person was found and has an image in Movary.",
"content": {
"image/jpeg": {
"schema": {
"type": "string",
"format": "binary"
}
}
}
},
"404": {
"description": "Either no person exists with the given ID or the person exists, but doesn't have an image in Movary."
}
}
}
},
"/images/movie/{id}": {
"get": {
"tags": [
"Images"
],
"summary": "Get movie cover image.",
"description": "Get cover image of a movie based on their Movary ID.",
"parameters": [
{
"name": "ID",
"in": "path",
"description": "ID of the movie in Movary",
"required": true,
"schema": {
"type": "integer"
},
"example": 1
}
],
"responses": {
"200": {
"description": "Movie was found and has a cover in Movary.",
"content": {
"image/jpeg": {
"schema": {
"type": "string",
"format": "binary"
}
}
}
},
"404": {
"description": "Either no movie exists with the given ID or the movie exists, but doesn't have a cover image in Movary."
}
}
}
},
"/authentication/token": {
"post": {
"tags": [
Expand Down
3 changes: 3 additions & 0 deletions settings/routes.php
Original file line number Diff line number Diff line change
Expand Up @@ -229,5 +229,8 @@ function addApiRoutes(RouterService $routerService, FastRoute\RouteCollector $ro

$routes->add('GET', '/feed/radarr/{id:.+}', [Api\RadarrController::class, 'renderRadarrFeed']);

$routes->add('GET', '/images/movies/{id:\d+}', [Api\ImagesController::class, 'getMovieImage']);
$routes->add('GET', '/images/person/{id:\d+}', [Api\ImagesController::class, 'getPersonImage']);

$routerService->addRoutesToRouteCollector($routeCollector, $routes);
}
11 changes: 11 additions & 0 deletions src/Factory.php
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,11 @@
use Movary\Command\CreatePublicStorageLink;
use Movary\Domain\Movie\MovieApi;
use Movary\Domain\Movie\Watchlist\MovieWatchlistApi;
use Movary\Domain\Person\PersonApi;
use Movary\Domain\User;
use Movary\Domain\User\Service\Authentication;
use Movary\Domain\User\UserApi;
use Movary\HttpController\Api\ImagesController;
use Movary\HttpController\Api\OpenApiController;
use Movary\HttpController\Web\CreateUserController;
use Movary\HttpController\Web\JobController;
Expand Down Expand Up @@ -184,6 +186,15 @@ public static function createImageCacheService(ContainerInterface $container) :
);
}

public static function createImagesController(ContainerInterface $container) : ImagesController
{
return new ImagesController(
$container->get(PersonApi::class),
$container->get(MovieApi::class),
self::createDirectoryAppRoot() . 'public'
);
}

public static function createJobController(ContainerInterface $container) : JobController
{
return new JobController(
Expand Down
53 changes: 53 additions & 0 deletions src/HttpController/Api/ImagesController.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
<?php declare(strict_types=1);

namespace Movary\HttpController\Api;

use Movary\Domain\Movie\MovieApi;
use Movary\Domain\Person\PersonApi;
use Movary\ValueObject\Http\Request;
use Movary\ValueObject\Http\Response;

readonly class ImagesController
{
public function __construct(
private PersonApi $personApi,
private MovieApi $movieApi,
private string $publicDirectory
){}

public function getMovieImage(Request $request) : Response
{
$resourceId = (int)$request->getRouteParameters()['id'];
$movie = $this->movieApi->findById($resourceId);
if($movie === null) {
return Response::createNotFound();
}
$posterPath = $movie->getPosterPath();
if($posterPath === null) {
return Response::createNotFound();
}
$image = file_get_contents($this->publicDirectory . $posterPath);
if($image === false) {
return Response::createNotFound();
}
return Response::createJpeg($image);
}

public function getPersonImage(Request $request) : Response
{
$resourceId = (int)$request->getRouteParameters()['id'];
$person = $this->personApi->findById($resourceId);
if($person === null) {
return Response::createNotFound();
}
$posterPath = $person->getPosterPath();
if($posterPath === null) {
return Response::createNotFound();
}
$image = file_get_contents($this->publicDirectory . $posterPath);
if($image === false) {
return Response::createNotFound();
}
return Response::createJpeg($image);
}
}
8 changes: 8 additions & 0 deletions src/ValueObject/Http/Header.php
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,14 @@ public static function createContentTypeJson() : self
return new self('Content-Type', 'application/json');
}

public static function createContentTypeJpeg(int $contentLength) : array
{
return [
new self('Content-Type', 'image/jpeg'),
new self('Content-Length', (string)$contentLength)
];
}

public static function createLocation(string $value) : self
{
return new self('Location', $value);
Expand Down
5 changes: 5 additions & 0 deletions src/ValueObject/Http/Response.php
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,11 @@ public static function createForbiddenRedirect(string $redirectTarget) : self
return new self(StatusCode::createForbidden(), null, [Header::createLocation('/login?redirect='.$query)]);
}

public static function createJpeg(string $image) : self
{
return new self(StatusCode::createOk(), $image, Header::createContentTypeJpeg(strlen($image)));
}

public static function createJson(string $body, StatusCode $statusCode = null) : self
{
return new self($statusCode ?? StatusCode::createOk(), $body, [Header::createContentTypeJson()]);
Expand Down
26 changes: 26 additions & 0 deletions tests/rest/api/image.http
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
GET http://127.0.0.1/api/images/person/1
Accept: */*
Cache-Control: no-cache
Content-Type: application/json

###

GET http://127.0.0.1/api/images/movies/1
Accept: */*
Cache-Control: no-cache
Content-Type: application/json

###

GET http://127.0.0.1/api/images/invalidresourcetype/1
Accept: */*
Cache-Control: no-cache
Content-Type: application/json

###

GET http://127.0.0.1/api/images/person/invalidresourceid
Accept: */*
Cache-Control: no-cache
Content-Type: application/json

0 comments on commit 2d8be77

Please sign in to comment.