Skip to content

Commit

Permalink
Merge pull request #125 from leepeuker/add-tmdb-image-cache-automation
Browse files Browse the repository at this point in the history
Setup basic job scheduling for tmdb image caching
  • Loading branch information
leepeuker authored Nov 29, 2022
2 parents aab8ed9 + c62eff1 commit 68bfd6d
Show file tree
Hide file tree
Showing 16 changed files with 214 additions and 133 deletions.
1 change: 0 additions & 1 deletion settings/psalm.xml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="https://getpsalm.org/schema/config"
xsi:schemaLocation="https://getpsalm.org/schema/config ../vendor/vimeo/psalm/config.xsd"
cacheDirectory="../tmp/cache/psalm"
>
<projectFiles>
<directory name="src" />
Expand Down
10 changes: 0 additions & 10 deletions settings/routes.php
Original file line number Diff line number Diff line change
Expand Up @@ -47,16 +47,6 @@
'/{username:[a-zA-Z0-9]+}/movies',
[\Movary\HttpController\MoviesController::class, 'renderPage']
);
$routeCollector->addRoute(
'POST',
'/refresh-trakt',
[\Movary\HttpController\SyncTraktController::class, 'execute']
);
$routeCollector->addRoute(
'POST',
'/refresh-tmdb',
[\Movary\HttpController\SyncTmdbController::class, 'execute']
);
$routeCollector->addRoute(
'POST',
'/login',
Expand Down
92 changes: 71 additions & 21 deletions src/Api/Tmdb/Cache/TmdbImageCache.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

use Movary\Api\Tmdb\TmdbUrlGenerator;
use Movary\Application\Service\ImageCacheService;
use Movary\ValueObject\Job;

class TmdbImageCache
{
Expand All @@ -14,55 +15,104 @@ public function __construct(
) {
}

public function cacheAllImagesByMovieId(int $movieId, bool $forceRefresh = false) : void
{
$this->cacheImages('movie', $forceRefresh, [$movieId]);

$statement = $this->pdo->prepare(
"SELECT DISTINCT (id)
FROM (
SELECT id
FROM person
JOIN movie_cast cast on person.id = cast.person_id
WHERE cast.movie_id = ?
UNION
SELECT id
FROM person
JOIN movie_crew crew on person.id = crew.person_id
WHERE crew.movie_id = ?
) personIdTable"
);
$statement->execute([$movieId, $movieId]);

$this->cacheImages('person', $forceRefresh, array_column($statement->fetchAll(), 'id'));
}

/**
* @return int Count of newly cached images
*/
public function cacheMovieImages(bool $forceRefresh = false) : int
public function cacheAllMovieImages(bool $forceRefresh = false) : int
{
return $this->cacheImages('movie', $forceRefresh);
}

/**
* @return int Count of newly cached images
*/
public function cachePersonImages(bool $forceRefresh = false) : int
public function cacheAllPersonImages(bool $forceRefresh = false) : int
{
return $this->cacheImages('person', $forceRefresh);
}

public function deleteCache() : void
{
$this->imageCacheService->deleteImages();
$this->pdo->prepare('UPDATE movie SET poster_path = null')->execute();
$this->pdo->prepare('UPDATE person SET poster_path = null')->execute();
}

public function executeJob(Job $job) : void
{
$movieIds = $job->getParameters()['movieIds'] ?? [];

foreach ($movieIds as $movieId) {
$this->cacheAllImagesByMovieId($movieId);
}
}

/**
* @return int Count of newly cached images
* @return bool True if image cache was re/generated, false otherwise
*/
private function cacheImages(string $tableName, bool $forceRefresh) : int
private function cacheImageDataByTableName(array $data, string $tableName, bool $forceRefresh = false) : bool
{
$cachedImages = 0;
if ($data['tmdb_poster_path'] === null) {
return false;
}

$statement = $this->pdo->prepare("SELECT id, tmdb_poster_path FROM $tableName");
$statement->execute();
$cachedImagePublicPath = $this->imageCacheService->cacheImage(
$this->tmdbUrlGenerator->generateImageUrl($data['tmdb_poster_path']),
$forceRefresh
);

foreach ($statement->getIterator() as $row) {
if ($row['tmdb_poster_path'] === null) {
continue;
}
if ($cachedImagePublicPath === null) {
return false;
}

$cachedImagePublicPath = $this->imageCacheService->cacheImage(
$this->tmdbUrlGenerator->generateImageUrl($row['tmdb_poster_path']),
$forceRefresh
);
$payload = [$cachedImagePublicPath, $data['id']];

if ($cachedImagePublicPath === null) {
continue;
}
return $this->pdo->prepare("UPDATE $tableName SET poster_path = ? WHERE id = ?")->execute($payload);
}

$payload = [$cachedImagePublicPath, $row['id']];
$this->pdo->prepare("UPDATE $tableName SET poster_path = ? WHERE id = ?")->execute($payload);
/**
* @return int Count of re/generated cached images
*/
private function cacheImages(string $tableName, bool $forceRefresh, array $filerIds = []) : int
{
$cachedImages = 0;

$query = "SELECT id, tmdb_poster_path FROM $tableName";
if (count($filerIds) > 0) {
$placeholders = str_repeat('?, ', count($filerIds));
$query .= ' WHERE id IN (' . trim($placeholders, ', ') . ')';
}

$cachedImages++;
$statement = $this->pdo->prepare($query);
$statement->execute($filerIds);

foreach ($statement->getIterator() as $row) {
if ($this->cacheImageDataByTableName($row, $tableName, $forceRefresh) === true) {
$cachedImages++;
}
}

return $cachedImages;
Expand Down
2 changes: 1 addition & 1 deletion src/Application/Service/ImageCacheService.php
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,6 @@ public function cacheImage(Url $imageUrl, bool $forceRefresh = false) : ?string

public function deleteImages() : void
{
$this->fileUtil->deleteDirectoryRecursively(self::CACHE_DIR);
$this->fileUtil->deleteDirectoryContent(self::CACHE_DIR);
}
}
12 changes: 11 additions & 1 deletion src/Application/Service/Tmdb/SyncMovie.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
use Movary\Api\Tmdb;
use Movary\Application\Movie;
use Movary\ValueObject\Date;
use Movary\Worker\JobScheduler;

class SyncMovie
{
Expand All @@ -14,7 +15,8 @@ public function __construct(
private readonly Movie\Api $movieApi,
private readonly GenreConverter $genreConverter,
private readonly ProductionCompanyConverter $productionCompanyConverter,
private readonly Connection $dbConnection
private readonly Connection $dbConnection,
private readonly JobScheduler $jobScheduler,
) {
}

Expand All @@ -40,7 +42,11 @@ public function syncMovie(int $tmdbId) : Movie\Entity
tmdbPosterPath: $tmdbMovie->getPosterPath(),
imdbId: $tmdbMovie->getImdbId(),
);

$this->jobScheduler->storeMovieIdForTmdbImageCacheJob($movie->getId());
} else {
$originalPosterPath = $movie->getPosterPath();

$movie = $this->movieApi->updateDetails(
movieId: $movie->getId(),
tagline: $tmdbMovie->getTagline(),
Expand All @@ -53,6 +59,10 @@ public function syncMovie(int $tmdbId) : Movie\Entity
tmdbPosterPath: $tmdbMovie->getPosterPath(),
imdbId: $movie->getImdbId(),
);

if ($originalPosterPath !== $movie->getPosterPath()) {
$this->jobScheduler->storeMovieIdForTmdbImageCacheJob($movie->getId());
}
}

$this->movieApi->updateGenres($movie->getId(), $this->genreConverter->getMovaryGenresFromTmdbMovie($tmdbMovie));
Expand Down
6 changes: 3 additions & 3 deletions src/Command/ImdbSync.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

use Movary\Application\Service\Imdb\SyncMovies;
use Movary\ValueObject\JobStatus;
use Movary\Worker\Service;
use Movary\Worker\JobScheduler;
use Psr\Log\LoggerInterface;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
Expand All @@ -20,7 +20,7 @@ class ImdbSync extends Command

public function __construct(
private readonly SyncMovies $syncMovieDetails,
private readonly Service $workerService,
private readonly JobScheduler $jobScheduler,
private readonly LoggerInterface $logger,
) {
parent::__construct();
Expand All @@ -47,7 +47,7 @@ protected function execute(InputInterface $input, OutputInterface $output) : int

$this->syncMovieDetails->syncMovies($maxAgeInHours, $movieCountSyncThreshold);

$this->workerService->addImdbSyncJob(JobStatus::createDone());
$this->jobScheduler->addImdbSyncJob(JobStatus::createDone());

$this->generateOutput($output, 'Syncing imdb movie ratings done.');
} catch (\Throwable $t) {
Expand Down
4 changes: 2 additions & 2 deletions src/Command/TmdbImageCacheRefresh.php
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ private function cacheMovieImages(OutputInterface $output, bool $forceRefresh =
{
$this->generateOutput($output, 'Caching movie images...');

$cachedImageCount = $this->imageCacheService->cacheMovieImages($forceRefresh);
$cachedImageCount = $this->imageCacheService->cacheAllMovieImages($forceRefresh);

$this->generateOutput($output, "Cached [$cachedImageCount] movie images.");
}
Expand All @@ -89,7 +89,7 @@ private function cachePersonImages(OutputInterface $output, bool $forceRefresh =
{
$this->generateOutput($output, 'Caching person images...');

$cachedImageCount = $this->imageCacheService->cachePersonImages($forceRefresh);
$cachedImageCount = $this->imageCacheService->cacheAllPersonImages($forceRefresh);

$this->generateOutput($output, "Cached [$cachedImageCount] person images.");
}
Expand Down
6 changes: 3 additions & 3 deletions src/Command/TmdbSync.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

use Movary\Application\Service\Tmdb\SyncMovies;
use Movary\ValueObject\JobStatus;
use Movary\Worker\Service;
use Movary\Worker\JobScheduler;
use Psr\Log\LoggerInterface;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
Expand All @@ -20,7 +20,7 @@ class TmdbSync extends Command

public function __construct(
private readonly SyncMovies $syncMovieDetails,
private readonly Service $workerService,
private readonly JobScheduler $jobScheduler,
private readonly LoggerInterface $logger,
) {
parent::__construct();
Expand All @@ -47,7 +47,7 @@ protected function execute(InputInterface $input, OutputInterface $output) : int

$this->syncMovieDetails->syncMovies($maxAgeInHours, $movieCountSyncThreshold);

$this->workerService->addTmdbSyncJob(JobStatus::createDone());
$this->jobScheduler->addTmdbSyncJob(JobStatus::createDone());

$this->generateOutput($output, 'Syncing movie meta data done.');
} catch (\Throwable $t) {
Expand Down
18 changes: 9 additions & 9 deletions src/Command/TraktImport.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
use Movary\Application\Service\Trakt\ImportRatings;
use Movary\Application\Service\Trakt\ImportWatchedMovies;
use Movary\ValueObject\JobStatus;
use Movary\Worker\Service;
use Movary\Worker\JobScheduler;
use Psr\Log\LoggerInterface;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
Expand All @@ -30,7 +30,7 @@ class TraktImport extends Command
public function __construct(
private readonly ImportRatings $importRatings,
private readonly ImportWatchedMovies $importWatchedMovies,
private readonly Service $workerService,
private readonly JobScheduler $jobScheduler,
private readonly LoggerInterface $logger,
) {
parent::__construct();
Expand All @@ -39,11 +39,11 @@ public function __construct(
protected function configure() : void
{
$this->setDescription('Import trakt.tv movie history and rating with local database.')
->addOption(self::OPTION_NAME_USER_ID, [], InputOption::VALUE_REQUIRED, 'Id of user to import to.')
->addOption(self::OPTION_NAME_HISTORY, [], InputOption::VALUE_NONE, 'Import movie history.')
->addOption(self::OPTION_NAME_RATINGS, [], InputOption::VALUE_NONE, 'Import movie ratings.')
->addOption(self::OPTION_NAME_OVERWRITE, [], InputOption::VALUE_NONE, 'Overwrite local data.')
->addOption(self::OPTION_NAME_IGNORE_CACHE, [], InputOption::VALUE_NONE, 'Ignore trakt cache and force import everything.');
->addOption(self::OPTION_NAME_USER_ID, [], InputOption::VALUE_REQUIRED, 'Id of user to import to.')
->addOption(self::OPTION_NAME_HISTORY, [], InputOption::VALUE_NONE, 'Import movie history.')
->addOption(self::OPTION_NAME_RATINGS, [], InputOption::VALUE_NONE, 'Import movie ratings.')
->addOption(self::OPTION_NAME_OVERWRITE, [], InputOption::VALUE_NONE, 'Overwrite local data.')
->addOption(self::OPTION_NAME_IGNORE_CACHE, [], InputOption::VALUE_NONE, 'Ignore trakt cache and force import everything.');
}

protected function execute(InputInterface $input, OutputInterface $output) : int
Expand Down Expand Up @@ -96,7 +96,7 @@ private function importHistory(OutputInterface $output, int $userId, bool $overw

$this->importWatchedMovies->execute($userId, $overwriteExistingData, $ignoreCache);

$this->workerService->addTraktImportHistoryJob($userId, JobStatus::createDone());
$this->jobScheduler->addTraktImportHistoryJob($userId, JobStatus::createDone());

$this->generateOutput($output, 'Importing history done.');
}
Expand All @@ -107,7 +107,7 @@ private function importRatings(OutputInterface $output, int $userId, bool $overw

$this->importRatings->execute($userId, $overwriteExistingData);

$this->workerService->addTraktImportRatingsJob($userId, JobStatus::createDone());
$this->jobScheduler->addTraktImportRatingsJob($userId, JobStatus::createDone());

$this->generateOutput($output, 'Importing ratings ratings.');
}
Expand Down
10 changes: 6 additions & 4 deletions src/HttpController/JobController.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
use Movary\ValueObject\Http\Request;
use Movary\ValueObject\Http\Response;
use Movary\ValueObject\Http\StatusCode;
use Movary\Worker\JobScheduler;
use Movary\Worker\Service;
use Twig\Environment;

Expand All @@ -17,6 +18,7 @@ class JobController
public function __construct(
private readonly Authentication $authenticationService,
private readonly Service $workerService,
private readonly JobScheduler $jobScheduler,
private readonly ImportHistoryFileValidator $letterboxdImportHistoryFileValidator,
private readonly ImportRatingsFileValidator $letterboxdImportRatingsFileValidator,
private readonly Environment $twig,
Expand Down Expand Up @@ -67,7 +69,7 @@ public function scheduleLetterboxdHistoryImport(Request $request) : Response
);
}

$this->workerService->addLetterboxdImportHistoryJob($userId, $targetFile);
$this->jobScheduler->addLetterboxdImportHistoryJob($userId, $targetFile);

$_SESSION['letterboxdHistorySyncSuccessful'] = true;

Expand Down Expand Up @@ -111,7 +113,7 @@ public function scheduleLetterboxdRatingsImport(Request $request) : Response
);
}

$this->workerService->addLetterboxdImportRatingsJob($userId, $targetFile);
$this->jobScheduler->addLetterboxdImportRatingsJob($userId, $targetFile);

$_SESSION['letterboxdRatingsSyncSuccessful'] = true;

Expand All @@ -128,7 +130,7 @@ public function scheduleTraktHistorySync() : Response
return Response::createFoundRedirect('/');
}

$this->workerService->addTraktImportHistoryJob($this->authenticationService->getCurrentUserId());
$this->jobScheduler->addTraktImportHistoryJob($this->authenticationService->getCurrentUserId());

$_SESSION['scheduledTraktHistoryImport'] = true;

Expand All @@ -145,7 +147,7 @@ public function scheduleTraktRatingsSync() : Response
return Response::createFoundRedirect('/');
}

$this->workerService->addTraktImportRatingsJob($this->authenticationService->getCurrentUserId());
$this->jobScheduler->addTraktImportRatingsJob($this->authenticationService->getCurrentUserId());

$_SESSION['scheduledTraktRatingsImport'] = true;

Expand Down
Loading

0 comments on commit 68bfd6d

Please sign in to comment.