Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Set trakt client per user and make it configurable via settings page #48

Merged
merged 1 commit into from
Jul 13, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 0 additions & 4 deletions .env.development.example
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,6 @@ DATABASE_CHARSET=utf8
# Tmdb api
TMDB_API_KEY=

# Trakt.tv api
TRAKT_USERNAME=
TRAKT_CLIENT_ID=

# Letterboxed
LETTERBOXD_RATINGS_CSV_PATH="tmp/ratings.csv"

Expand Down
4 changes: 0 additions & 4 deletions .env.production.example
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,6 @@ DATABASE_CHARSET=utf8
# Tmdb api
TMDB_API_KEY=

# Trakt.tv api
TRAKT_USERNAME=
TRAKT_CLIENT_ID=

# Logging
LOG_FILE="tmp/app.log"
LOG_LEVEL=warning
13 changes: 5 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -102,10 +102,6 @@ DATABASE_CHARSET=utf8
# https://www.themoviedb.org/settings/api
TMDB_API_KEY=

# https://trakt.tv/oauth/applications
TRAKT_USERNAME=
TRAKT_CLIENT_ID=

TIMEZONE="Europe/Berlin"

LOG_FILE="tmp/app.log"
Expand All @@ -129,12 +125,13 @@ Add the generated url as a [webhook to plex](https://support.plex.tv/articles/11
<a name="#trakttv-sync"></a>
### trakt.tv sync
You can sync your watchhistory and ratings from trakt.tv.
Make sure you have added the variables `TRAKT_USERNAME` and `TRAKT_CLIENT_ID` to the environment.
You can sync your watch history and ratings from trakt.tv.
Example:
The user used in the sync process must have a trakt client id set (can be set via web UI on the settings page or via cli `movary:user:change-trakt-client-id`).
Example (syncing history and ratings for user with id 1):
`docker exec movary php bin/console.php movary:sync-trakt --ratings --history`
`docker exec movary php bin/console.php movary:sync-trakt --ratings --history --userId=1`
**Flags:**
Expand Down
1 change: 1 addition & 0 deletions bin/console.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,5 +9,6 @@
$application->add($container->get(Movary\Command\CreateUser::class));
$application->add($container->get(Movary\Command\ChangeUserPassword::class));
$application->add($container->get(Movary\Command\DatabaseMigration::class));
$application->add($container->get(Movary\Command\ChangeUserTraktId::class));

$application->run();
1 change: 0 additions & 1 deletion bootstrap.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@
[
\Movary\ValueObject\Config::class => DI\factory([Factory::class, 'createConfig']),
\Movary\Api\Trakt\Api::class => DI\factory([Factory::class, 'createTraktApi']),
\Movary\Api\Trakt\Client::class => DI\factory([Factory::class, 'createTraktApiClient']),
\Movary\Api\Tmdb\Client::class => DI\factory([Factory::class, 'createTmdbApiClient']),
\Movary\HttpController\SettingsController::class => DI\factory([Factory::class, 'createSettingsController']),
\Movary\ValueObject\Http\Request::class => DI\factory([Factory::class, 'createCurrentHttpRequest']),
Expand Down
24 changes: 24 additions & 0 deletions db/migrations/20220713163724_AddTraktClientIdToUserTable.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
<?php declare(strict_types=1);

use Phinx\Migration\AbstractMigration;

final class AddTraktClientIdToUserTable extends AbstractMigration
{
public function down() : void
{
$this->execute(
<<<SQL
ALTER TABLE user DROP COLUMN trakt_client_id;
SQL
);
}

public function up() : void
{
$this->execute(
<<<SQL
ALTER TABLE user ADD COLUMN trakt_client_id VARCHAR(255) DEFAULT NULL AFTER plex_webhook_uuid;
SQL
);
}
}
2 changes: 0 additions & 2 deletions docker-compose.override.yml.example
Original file line number Diff line number Diff line change
Expand Up @@ -16,5 +16,3 @@ services:
DATABASE_USER: "${DATABASE_USER}"
DATABASE_PASSWORD: "${DATABASE_PASSWORD}"
TMDB_API_KEY: "${TMDB_API_KEY}"
TRAKT_USERNAME: "${TRAKT_USERNAME}"
TRAKT_CLIENT_ID: "${TRAKT_CLIENT_ID}"
5 changes: 5 additions & 0 deletions settings/routes.php
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,11 @@
'/user/plex-webhook-id',
[\Movary\HttpController\PlexController::class, 'regeneratePlexWebhookId']
);
$routeCollector->addRoute(
'POST',
'/user/trakt',
[\Movary\HttpController\SettingsController::class, 'updateTrakt']
);
$routeCollector->addRoute(
'DELETE',
'/user/plex-webhook-id',
Expand Down
12 changes: 6 additions & 6 deletions src/Api/Trakt/Api.php
Original file line number Diff line number Diff line change
Expand Up @@ -20,23 +20,23 @@ public function fetchUniqueCachedTraktIds(int $userId) : array
return $this->cacheWatchedService->fetchAllUniqueTraktIds($userId);
}

public function fetchUserMovieHistoryByMovieId(TraktId $traktId) : User\Movie\History\DtoList
public function fetchUserMovieHistoryByMovieId(string $clientId, TraktId $traktId) : User\Movie\History\DtoList
{
$responseData = $this->client->get(sprintf('/users/%s/history/movies/%d', $this->username, $traktId->asInt()));
$responseData = $this->client->get($clientId, sprintf('/users/%s/history/movies/%d', $this->username, $traktId->asInt()));

return User\Movie\History\DtoList::createFromArray($responseData);
}

public function fetchUserMoviesRatings() : User\Movie\Rating\DtoList
public function fetchUserMoviesRatings(string $clientId,) : User\Movie\Rating\DtoList
{
$responseData = $this->client->get(sprintf('/users/%s/ratings/movies', $this->username));
$responseData = $this->client->get($clientId, sprintf('/users/%s/ratings/movies', $this->username));

return User\Movie\Rating\DtoList::createFromArray($responseData);
}

public function fetchUserMoviesWatched() : User\Movie\Watched\DtoList
public function fetchUserMoviesWatched(string $clientId) : User\Movie\Watched\DtoList
{
$responseData = $this->client->get(sprintf('/users/%s/watched/movies', $this->username));
$responseData = $this->client->get($clientId, sprintf('/users/%s/watched/movies', $this->username));

return User\Movie\Watched\DtoList::createFromArray($responseData);
}
Expand Down
5 changes: 2 additions & 3 deletions src/Api/Trakt/Client.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,19 +14,18 @@ class Client

public function __construct(
private readonly ClientInterface $httpClient,
private readonly string $clientId
) {
}

public function get(string $relativeUrl) : array
public function get(string $clientId, string $relativeUrl) : array
{
$request = new Request(
'GET',
self::BASE_URL . $relativeUrl,
[
'Content-Type' => 'application/json',
'trakt-api-version' => self::TRAKT_API_VERSION,
'trakt-api-key' => $this->clientId,
'trakt-api-key' => $clientId,
]
);

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
<?php declare(strict_types=1);

namespace Movary\Application\Service\Trakt\Exception;

class TraktClientIdNotSet extends \Exception
{
}
4 changes: 2 additions & 2 deletions src/Application/Service/Trakt/PlaysPerDateFetcher.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,11 @@ public function __construct(private readonly Api $traktApi)
{
}

public function fetchTraktPlaysPerDate(TraktId $traktId) : PlaysPerDateDtoList
public function fetchTraktPlaysPerDate(string $traktClientId, TraktId $traktId) : PlaysPerDateDtoList
{
$playsPerDates = PlaysPerDateDtoList::create();

foreach ($this->traktApi->fetchUserMovieHistoryByMovieId($traktId) as $movieHistoryEntry) {
foreach ($this->traktApi->fetchUserMovieHistoryByMovieId($traktClientId, $traktId) as $movieHistoryEntry) {
$watchDate = Date::createFromDateTime($movieHistoryEntry->getWatchedAt());

$playsPerDates->incrementPlaysForDate($watchDate);
Expand Down
11 changes: 9 additions & 2 deletions src/Application/Service/Trakt/SyncRatings.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

use Movary\Api;
use Movary\Application;
use Movary\Application\Service\Trakt\Exception\TraktClientIdNotSet;
use Movary\ValueObject\PersonalRating;

class SyncRatings
Expand All @@ -12,13 +13,19 @@ public function __construct(
private readonly Application\Movie\Api $movieApi,
private readonly Api\Trakt\Api $traktApi,
private readonly Api\Trakt\Cache\User\Movie\Rating\Service $traktApiCacheUserMovieRatingService,
private readonly Application\SyncLog\Repository $scanLogRepository
private readonly Application\SyncLog\Repository $scanLogRepository,
private readonly Application\User\Api $userApi,
) {
}

public function execute(int $userId, bool $overwriteExistingData = false) : void
{
$this->traktApiCacheUserMovieRatingService->set($userId, $this->traktApi->fetchUserMoviesRatings());
$traktClientId = $this->userApi->findTraktClientId($userId);
if ($traktClientId === null) {
throw new TraktClientIdNotSet();
}

$this->traktApiCacheUserMovieRatingService->set($userId, $this->traktApi->fetchUserMoviesRatings($traktClientId));

foreach ($this->movieApi->fetchAll() as $movie) {
$traktId = $movie->getTraktId();
Expand Down
15 changes: 11 additions & 4 deletions src/Application/Service/Trakt/SyncWatchedMovies.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
use Movary\Api;
use Movary\Api\Trakt\ValueObject\Movie\TraktId;
use Movary\Application;
use Movary\Application\Service\Trakt\Exception\TraktClientIdNotSet;
use Movary\ValueObject\Date;
use Psr\Log\LoggerInterface;

Expand All @@ -18,12 +19,18 @@ public function __construct(
private readonly PlaysPerDateFetcher $playsPerDateFetcher,
private readonly Application\Service\Tmdb\SyncMovie $tmdbMovieSync,
private readonly Application\SyncLog\Repository $scanLogRepository,
private readonly Application\User\Api $userApi
) {
}

public function execute(int $userId, bool $overwriteExistingData = false, bool $ignoreCache = false) : void
{
$watchedMovies = $this->traktApi->fetchUserMoviesWatched();
$traktClientId = $this->userApi->findTraktClientId($userId);
if ($traktClientId === null) {
throw new TraktClientIdNotSet();
}

$watchedMovies = $this->traktApi->fetchUserMoviesWatched($traktClientId);

foreach ($watchedMovies as $watchedMovie) {
$traktId = $watchedMovie->getMovie()->getTraktId();
Expand All @@ -34,7 +41,7 @@ public function execute(int $userId, bool $overwriteExistingData = false, bool $
continue;
}

$this->syncMovieHistory($userId, $traktId, $movie, $overwriteExistingData);
$this->syncMovieHistory($traktClientId, $userId, $traktId, $movie, $overwriteExistingData);

$this->traktApiCacheUserMovieWatchedService->setOne($userId, $traktId, $watchedMovie->getLastUpdated());
}
Expand Down Expand Up @@ -88,9 +95,9 @@ private function isWatchedCacheUpToDate(int $userId, Api\Trakt\ValueObject\User\
return $cacheLastUpdated !== null && $watchedMovie->getLastUpdated()->isEqual($cacheLastUpdated) === true;
}

private function syncMovieHistory(int $userId, TraktId $traktId, Application\Movie\Entity $movie, bool $overwriteExistingData) : void
private function syncMovieHistory(string $traktClientId, int $userId, TraktId $traktId, Application\Movie\Entity $movie, bool $overwriteExistingData) : void
{
$traktHistoryEntries = $this->playsPerDateFetcher->fetchTraktPlaysPerDate($traktId);
$traktHistoryEntries = $this->playsPerDateFetcher->fetchTraktPlaysPerDate($traktClientId, $traktId);

foreach ($this->movieApi->fetchHistoryByMovieId($movie->getId(), $userId) as $localHistoryEntry) {
$localHistoryEntryDate = Date::createFromString($localHistoryEntry['watched_at']);
Expand Down
14 changes: 12 additions & 2 deletions src/Application/User/Api.php
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,14 @@ public function deletePlexWebhookId(int $userId) : void
$this->repository->setPlexWebhookId($userId, null);
}

public function findPlexWebhookIdByUserId(int $userId) : ?string
public function findPlexWebhookId(int $userId) : ?string
{
return $this->repository->findPlexWebhookIdByUserId($userId);
return $this->repository->findPlexWebhookId($userId);
}

public function findTraktClientId(int $userId) : ?string
{
return $this->repository->findTraktClientId($userId);
}

public function findUserIdByPlexWebhookId(string $webhookId) : ?int
Expand All @@ -49,4 +54,9 @@ public function updatePassword(int $userId, string $newPassword) : void

$this->repository->updatePassword($userId, $passwordHash);
}

public function updateTraktClientId(int $userId, ?string $traktClientId) : void
{
$this->repository->updateTraktClientId($userId, $traktClientId);
}
}
26 changes: 25 additions & 1 deletion src/Application/User/Repository.php
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ public function findAuthTokenExpirationDate(string $token) : ?DateTime
return DateTime::createFromString($expirationDate);
}

public function findPlexWebhookIdByUserId(int $userId) : ?string
public function findPlexWebhookId(int $userId) : ?string
{
$plexWebhookId = $this->dbConnection->fetchOne('SELECT `plex_webhook_uuid` FROM `user` WHERE `id` = ?', [$userId]);

Expand All @@ -67,6 +67,17 @@ public function findPlexWebhookIdByUserId(int $userId) : ?string
return $plexWebhookId;
}

public function findTraktClientId(int $userId) : ?string
{
$plexWebhookId = $this->dbConnection->fetchOne('SELECT `trakt_client_id` FROM `user` WHERE `id` = ?', [$userId]);

if ($plexWebhookId === false) {
return null;
}

return $plexWebhookId;
}

public function findUserByEmail(string $email) : ?Entity
{
$data = $this->dbConnection->fetchAssociative('SELECT * FROM `user` WHERE `email` = ?', [$email]);
Expand Down Expand Up @@ -136,4 +147,17 @@ public function updatePassword(int $userId, string $passwordHash) : void
]
);
}

public function updateTraktClientId(int $userId, ?string $traktClientId) : void
{
$this->dbConnection->update(
'user',
[
'trakt_client_id' => $traktClientId,
],
[
'id' => $userId,
]
);
}
}
7 changes: 4 additions & 3 deletions src/Command/ChangeUserPassword.php
Original file line number Diff line number Diff line change
Expand Up @@ -36,14 +36,15 @@ protected function execute(InputInterface $input, OutputInterface $output) : int
try {
$this->userApi->updatePassword($userId, $password);
} catch (\Throwable $t) {
$this->logger->error('Could not change admin password.', ['exception' => $t]);
$this->logger->error('Could not change password.', ['exception' => $t]);

$this->generateOutput($output, 'Could not update password');
$this->generateOutput($output, 'Could not update password.');

return Command::FAILURE;
}

$this->generateOutput($output, 'Updated password');
$this->generateOutput($output, 'Updated password.');

return Command::SUCCESS;
}
}
Loading