Skip to content

Commit

Permalink
Update UI, wip
Browse files Browse the repository at this point in the history
  • Loading branch information
leepeuker committed Jul 29, 2023
1 parent 0e2580b commit 35cc131
Show file tree
Hide file tree
Showing 10 changed files with 255 additions and 192 deletions.
35 changes: 17 additions & 18 deletions docs/configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,18 +15,16 @@ The `Web UI` column is set to yes if an environment variable can alternatively b

### General

| NAME | DEFAULT VALUE | INFO | Web UI |
|:--------------------------------------------|:---------------:|:-------------------------------------------------------------------------------|:------:|
| `TMDB_API_KEY` | - | **Required** (get key [here](https://www.themoviedb.org/settings/api)) | yes |
| `APPLICATION_URL` | - | Public base url of the application (e.g. `htttp://localhost`) | yes |
| `TMDB_ENABLE_IMAGE_CACHING` | `0` | More info [here](features/tmdb-data.md#image-cache) | |
| `PLEX_IDENTIFIER` | - | Required for Plex Authentication. Generate with e.g. `openssl rand -base64 32` | |
| `PLEX_APP_NAME` | `Movary` | Required for Plex Authentication | |
| `ENABLE_REGISTRATION` | `0` | Enables public user registration | |
| `MIN_RUNTIME_IN_SECONDS_FOR_JOB_PROCESSING` | `15` | Minimum time between background jobs processing | |
| `TIMEZONE` | `Europe/Berlin` | Supported timezones [here](https://www.php.net/manual/en/timezones.php) | |
| `DEFAULT_LOGIN_EMAIL` | - | Email address to always autofill on login page | |
| `DEFAULT_LOGIN_PASSWORD` | - | Password to always autofill on login page | |
| NAME | DEFAULT VALUE | INFO | Web UI |
|:--------------------------------------------|:---------------:|:------------------------------------------------------------------------|:------:|
| `TMDB_API_KEY` | - | **Required** (get key [here](https://www.themoviedb.org/settings/api)) | yes |
| `APPLICATION_URL` | - | Public base url of the application (e.g. `htttp://localhost`) | yes |
| `TMDB_ENABLE_IMAGE_CACHING` | `0` | More info [here](features/tmdb-data.md#image-cache) | |
| `ENABLE_REGISTRATION` | `0` | Enables public user registration | |
| `MIN_RUNTIME_IN_SECONDS_FOR_JOB_PROCESSING` | `15` | Minimum time between background jobs processing | |
| `TIMEZONE` | `Europe/Berlin` | Supported timezones [here](https://www.php.net/manual/en/timezones.php) | |
| `DEFAULT_LOGIN_EMAIL` | - | Email address to always autofill on login page | |
| `DEFAULT_LOGIN_PASSWORD` | - | Password to always autofill on login page | |

### Database

Expand All @@ -45,13 +43,14 @@ Required to run the application

### Third party integrations

Required for some third party integrations. Only necessary if the relevant third party integrations should be enabled.
Required for some third party integrations. Only necessary if the relevant third party integrations should be enabled.

| NAME | DEFAULT VALUE | INFO | Web UI |
|:--------------------------------------------|:-------------:|:-------------------------------------------------------------------------------|:------:|
| `PLEX_IDENTIFIER` | - | Required for Plex authentication. Generate with e.g. `openssl rand -base64 32` | |
| `PLEX_APP_NAME` | `Movary` | Required for Plex authentication | |
| `JELLYFIN_DEVICE_ID` | - | Required for Jellyfin authentication | |
| NAME | DEFAULT VALUE | INFO | Web UI |
|:---------------------|:-------------:|:-------------------------------------------------------------------------------|:------:|
| `PLEX_IDENTIFIER` | - | Required for Plex Authentication. Generate with e.g. `openssl rand -base64 32` | |
| `PLEX_APP_NAME` | `Movary` | Used for Plex Authentication | |
| `JELLYFIN_DEVICE_ID` | - | Required for Jellyfin Authentication | |
| `JELLYFIN_APP_NAME` | `Movary` | Used for Jellyfin Authentication | |

### Email

Expand Down
96 changes: 48 additions & 48 deletions public/js/settings-integration-jellyfin.js
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
function disableElement(el) {
el.classList.add('disabled');
el.disabled = true;
el.disabled = true;
}

function enableElement(el) {
el.classList.remove('disabled');
el.disabled = false;
el.disabled = false;
}

function insertAuthenticationForm() {
Expand All @@ -19,7 +19,7 @@ function insertAuthenticationForm() {
jellyfinUsernameInput.type = 'text';
jellyfinUsernameInput.classList.add('form-control', 'disabled');
jellyfinUsernameInput.id = 'JellyfinUsernameInput';

jellyfinPasswordInput.type = 'password';
jellyfinPasswordInput.classList.add('form-control', 'disabled');
jellyfinPasswordInput.id = 'JellyfinPasswordInput';
Expand Down Expand Up @@ -135,40 +135,46 @@ async function updateScrobbleOptions() {
});
}

async function saveServerUrl() {
async function saveJellyfinServerUrl() {
document.getElementById('alertJellyfinServerUrlLoadingSpinner').classList.remove('d-none')
removeAlert('alertJellyfinServerUrlDiv');

const jellyfinServerUrl = document.getElementById('jellyfinServerUrlInput').value;

await fetch('/settings/jellyfin/server-url-save', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
'JellyfinServerUrl': document.getElementById('jellyfinServerUrlInput').value
'JellyfinServerUrl': jellyfinServerUrl
})
}).then(response => {
if(!response.ok) {
addAlert('alertJellyfinImportDiv', 'Could not save server URL', 'danger');
document.getElementById('alertJellyfinServerUrlLoadingSpinner').classList.add('d-none')

if (!response.ok) {
addAlert('alertJellyfinServerUrlDiv', 'Could not save server url', 'danger');
return;
}
addAlert('alertJellyfinImportDiv', 'Succesfully saved the server URL!', 'success');

if(document.getElementById('jellyfinServerUrlInput').value.length > 0) {
enableElement(document.getElementById('verifyServerUrlBtn'));
enableElement(document.getElementById('JellyfinUsernameInput'));
enableElement(document.getElementById('JellyfinPasswordInput'));
enableElement(document.getElementById('jellyfinAuthenticationBtn'));

document.getElementById('alertJellyfinServerUrlLoadingSpinner').classList.add('d-none')
addAlert('alertJellyfinServerUrlDiv', 'Server url was updated', 'success');
if (jellyfinServerUrl !== '') {
document.getElementById('authenticateWithJellyfinButton').disabled = false
} else {
disableElement(document.getElementById('verifyServerUrlBtn'));
disableElement(document.getElementById('JellyfinUsernameInput'));
disableElement(document.getElementById('JellyfinPasswordInput'));
disableElement(document.getElementById('jellyfinAuthenticationBtn'));
document.getElementById('authenticateWithJellyfinButton').disabled = true
}
}).catch(function (error) {
console.log(error);
addAlert('alertJellyfinImportDiv', 'Could not save server URL', 'danger');
document.getElementById('alertJellyfinServerUrlLoadingSpinner').classList.add('d-none')
addAlert('alertJellyfinServerUrlDiv', 'Could not save server url', 'danger');
});
}

async function verifyServerUrl() {
async function verifyJellyfinServerUrl() {
document.getElementById('alertJellyfinServerUrlLoadingSpinner').classList.remove('d-none')
removeAlert('alertJellyfinServerUrlDiv');

await fetch('/settings/jellyfin/server-url-verify', {
method: 'POST',
headers: {
Expand All @@ -178,46 +184,42 @@ async function verifyServerUrl() {
'JellyfinServerUrl': document.getElementById('jellyfinServerUrlInput').value
})
}).then(response => {
if(!response.ok) {
addAlert('alertJellyfinImportDiv', 'Could not verify server URL', 'danger');
document.getElementById('alertJellyfinServerUrlLoadingSpinner').classList.add('d-none')
if (!response.ok) {
addAlert('alertJellyfinServerUrlDiv', 'Could not verify server url', 'danger');

return;
}
addAlert('alertJellyfinImportDiv', 'Succesfully verified the server URL!', 'success');
addAlert('alertJellyfinServerUrlDiv', 'Server url was verified', 'success');
}).catch(function (error) {
console.log(error)
addAlert('alertJellyfinImportDiv', 'Could not verify server URL', 'danger');
document.getElementById('alertJellyfinServerUrlLoadingSpinner').classList.add('d-none')
addAlert('alertJellyfinServerUrlDiv', 'Could not verify server url', 'danger');
});
}

async function authenticateJellyfinAccount() {
console.log(document.getElementById('jellyfinUsernameInput').value)
await fetch('/settings/jellyfin/authenticate', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
'username': document.getElementById('JellyfinUsernameInput').value,
'password': document.getElementById('JellyfinPasswordInput').value,
'username': document.getElementById('jellyfinUsernameInput').value,
'password': document.getElementById('jellyfinPasswordInput').value,
})
}).then(response => {
if(!response.ok) {
addAlert('alertJellyfinImportDiv', 'Could not authenticate with Jellyfin', 'danger');
if (!response.ok) {
addAlert('alertJellyfinAuthenticationModalDiv', 'Could not authenticate with Jellyfin', 'danger', 0);

return;
}
addAlert('alertJellyfinImportDiv', 'Succesfully authenticated with Jellyfin!', 'success');

document.getElementById('jellyfinModalBody').getElementsByClassName('input-group')[0].remove();
document.getElementById('jellyfinAuthenticationBtn').remove();

let removeAuthenticationBtn = document.createElement('button');
removeAuthenticationBtn.classList.add('btn', 'btn-danger', 'mb-3');
removeAuthenticationBtn.addEventListener('click', removeJellyfinAuthentication);
removeAuthenticationBtn.innerText = 'Remove Jellyfin authentication';
removeAuthenticationBtn.id = 'jellyfinRemoveAuthenticationBtn';
document.getElementById('jellyfinModalBody').append(removeAuthenticationBtn);

location.reload();
}).catch(function (error) {
console.error(error)
addAlert('alertJellyfinImportDiv', 'Could not authenticate with Jellyfin', 'danger');
addAlert('alertJellyfinAuthenticationModalDiv', 'Could not authenticate with Jellyfin', 'danger', 0);
});
}

Expand All @@ -228,17 +230,15 @@ async function removeJellyfinAuthentication() {
'Content-Type': 'application/json',
}
}).then(response => {
if(!response.ok) {
if (!response.ok) {
addAlert('alertJellyfinImportDiv', 'The authentication could not be removed', 'danger');
return;
} else {
addAlert('alertJellyfinImportDiv', 'Authentication succesfully removed!', 'success');
document.getElementById('jellyfinRemoveAuthenticationBtn').remove();
insertAuthenticationForm();


return
}

location.reload();
}).catch(function (error) {
console.error(error)
addAlert('alertJellyfinImportDiv', 'The authentication could not be removed', 'danger');
});
}
}
8 changes: 0 additions & 8 deletions public/js/settings-integration-plex.js
Original file line number Diff line number Diff line change
Expand Up @@ -248,11 +248,3 @@ async function importPlexWatchlist() {

addAlert('alertPlexWatchlistImportDiv', 'Watchlist import scheduled', 'success')
}

async function authenticateWithJellyfin() {
// TODO
}

async function removeJellyfinAuthentication() {
// TODO
}
39 changes: 29 additions & 10 deletions src/Api/Jellyfin/JellyfinApi.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,24 +6,32 @@
use Movary\Api\Jellyfin\Dto\JellyfinAuthenticationData;
use Movary\Api\Jellyfin\Dto\JellyfinUser;
use Movary\Api\Jellyfin\Dto\JellyfinUserId;
use Movary\Domain\User\UserApi;
use Movary\Service\ServerSettings;
use Movary\ValueObject\RelativeUrl;
use Psr\Log\LoggerInterface;

class JellyfinApi
{
public function __construct(
private readonly JellyfinClient $jellyfinClient,
private readonly ServerSettings $serverSettings,
private readonly LoggerInterface $logger
private readonly UserApi $userApi,
private readonly LoggerInterface $logger,
) {
}

public function deleteJellyfinAccessToken() : void
public function deleteJellyfinAccessToken(int $userId) : void
{
$accessToken = $this->userApi->findJellyfinAccessToken($userId);
$jellyfinServerUrl = $this->userApi->findJellyfinServerUrl($userId);

$url = $jellyfinServerUrl->appendRelativeUrl(RelativeUrl::create('/Users/'));

$query = [
'id' => $this->serverSettings->getJellyfinDeviceId()
'id' => $this->serverSettings->requireJellyfinDeviceId()
];
$this->jellyfinClient->delete('/Devices', $query);
$this->jellyfinClient->delete($url, $query, $accessToken);
$this->logger->info('Jellyfin access token has been invalidated');
}

Expand All @@ -32,27 +40,38 @@ public function fetchJellyfinServerInfo() : ?array
return $this->jellyfinClient->get('/system/info/public');
}

public function fetchJellyfinUser(JellyfinUserId $jellyfinUserId) : ?JellyfinUser
public function fetchJellyfinUser(int $userId) : ?JellyfinUser
{
$userInformation = $this->jellyfinClient->get('/Users/'.$jellyfinUserId);
if($userInformation === null) {
$jellyfinUserId = $this->userApi->fetchJellyfinUserId($userId);
$jellyfinServerUrl = $this->userApi->findJellyfinServerUrl($userId);

$url = $jellyfinServerUrl->appendRelativeUrl(RelativeUrl::create('/Users/' . $jellyfinUserId));

$userInformation = $this->jellyfinClient->get($url);

if ($userInformation === null) {
return null;
}

return JellyfinUser::create(JellyfinUserId::create($userInformation['Id']), $userInformation['Name']);
}

public function createJellyfinAuthentication(string $username, string $password) : ?JellyfinAuthenticationData
public function createJellyfinAuthentication(int $userId, string $username, string $password) : ?JellyfinAuthenticationData
{
$jellyfinServerUrl = $this->userApi->findJellyfinServerUrl($userId);

$url = $jellyfinServerUrl->appendRelativeUrl(RelativeUrl::create('/Users/authenticatebyname'));

$data = [
'Username' => $username,
'Pw' => $password
];
$response = $this->jellyfinClient->post('/Users/authenticatebyname', data: $data);
$response = $this->jellyfinClient->post($url, data: $data);
if ($response === null) {
return null;
}

$this->logger->info('Jellyfin account has been authenticated');
$this->logger->info('Jellyfin account has been authenticated for user: ' . $userId);

return JellyfinAuthenticationData::create(
JellyfinAccessToken::create((string)$response['AccessToken']),
Expand Down
Loading

0 comments on commit 35cc131

Please sign in to comment.