mirror of
https://github.com/Ombi-app/Ombi.git
synced 2025-07-05 20:51:13 -07:00
rework the pages
This commit is contained in:
parent
6344ae98cd
commit
cb6d441ccd
5 changed files with 565 additions and 180 deletions
31
src/.idea/.idea.Ombi/.idea/workspace.xml
generated
31
src/.idea/.idea.Ombi/.idea/workspace.xml
generated
|
@ -5,20 +5,6 @@
|
|||
</component>
|
||||
<component name="ChangeListManager">
|
||||
<list default="true" id="57001998-efde-494a-80b3-d7acfc91f770" name="Default Changelist" comment="">
|
||||
<change beforePath="$PROJECT_DIR$/.idea/.idea.Ombi/.idea/contentModel.xml" beforeDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/.idea/.idea.Ombi/.idea/modules.xml" beforeDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/.idea/.idea.Ombi/.idea/workspace.xml" beforeDir="false" afterPath="$PROJECT_DIR$/.idea/.idea.Ombi/.idea/workspace.xml" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/.idea/.idea.Ombi/riderModule.iml" beforeDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/.vscode/tasks.json" beforeDir="false" afterPath="$PROJECT_DIR$/.vscode/tasks.json" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/Ombi.Helpers/NotificationType.cs" beforeDir="false" afterPath="$PROJECT_DIR$/Ombi.Helpers/NotificationType.cs" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/Ombi.Schedule.Tests/Ombi.Schedule.Tests.csproj" beforeDir="false" afterPath="$PROJECT_DIR$/Ombi.Schedule.Tests/Ombi.Schedule.Tests.csproj" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/Ombi.Schedule.Tests/PlexWatchlistImportTests.cs" beforeDir="false" afterPath="$PROJECT_DIR$/Ombi.Schedule.Tests/PlexWatchlistImportTests.cs" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/Ombi.Schedule.Tests/Properties/launchSettings.json" beforeDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/Ombi.Schedule/Jobs/Plex/PlexWatchlistImport.cs" beforeDir="false" afterPath="$PROJECT_DIR$/Ombi.Schedule/Jobs/Plex/PlexWatchlistImport.cs" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/Ombi.Settings/Settings/Models/External/PlexSettings.cs" beforeDir="false" afterPath="$PROJECT_DIR$/Ombi.Settings/Settings/Models/External/PlexSettings.cs" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/Ombi.Store/Context/OmbiContext.cs" beforeDir="false" afterPath="$PROJECT_DIR$/Ombi.Store/Context/OmbiContext.cs" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/Ombi/ClientApp/src/app/interfaces/INotificationSettings.ts" beforeDir="false" afterPath="$PROJECT_DIR$/Ombi/ClientApp/src/app/interfaces/INotificationSettings.ts" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/Ombi/ClientApp/src/app/interfaces/ISettings.ts" beforeDir="false" afterPath="$PROJECT_DIR$/Ombi/ClientApp/src/app/interfaces/ISettings.ts" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/Ombi/ClientApp/src/app/settings/plex/plex.component.html" beforeDir="false" afterPath="$PROJECT_DIR$/Ombi/ClientApp/src/app/settings/plex/plex.component.html" afterDir="false" />
|
||||
</list>
|
||||
<option name="SHOW_DIALOG" value="false" />
|
||||
|
@ -416,7 +402,7 @@
|
|||
"RunOnceActivity.ShowReadmeOnStart": "true",
|
||||
"RunOnceActivity.git.unshallow": "true",
|
||||
"fb34c741-04ca-4b4f-8ea1-651a011b42c8.executor": "Debug",
|
||||
"git-widget-placeholder": "develop",
|
||||
"git-widget-placeholder": "watchlist-expired-notification",
|
||||
"node.js.detected.package.eslint": "true",
|
||||
"node.js.selected.package.eslint": "(autodetect)",
|
||||
"node.js.selected.package.tslint": "(autodetect)",
|
||||
|
@ -512,7 +498,7 @@
|
|||
<workItem from="1563957162999" duration="5401000" />
|
||||
<workItem from="1745681294313" duration="1814000" />
|
||||
<workItem from="1747080279165" duration="838000" />
|
||||
<workItem from="1747082180432" duration="1399000" />
|
||||
<workItem from="1747082180432" duration="1994000" />
|
||||
</task>
|
||||
<servers />
|
||||
</component>
|
||||
|
@ -649,6 +635,19 @@
|
|||
</properties>
|
||||
<option name="timeStamp" value="11" />
|
||||
</line-breakpoint>
|
||||
<line-breakpoint enabled="true" type="DotNet Breakpoints">
|
||||
<url>file://$PROJECT_DIR$/Ombi.Schedule/Jobs/Plex/PlexWatchlistImport.cs</url>
|
||||
<line>100</line>
|
||||
<properties documentPath="$PROJECT_DIR$/Ombi.Schedule/Jobs/Plex/PlexWatchlistImport.cs" containingFunctionPresentation="Method 'Execute'">
|
||||
<startOffsets>
|
||||
<option value="4602" />
|
||||
</startOffsets>
|
||||
<endOffsets>
|
||||
<option value="4636" />
|
||||
</endOffsets>
|
||||
</properties>
|
||||
<option name="timeStamp" value="12" />
|
||||
</line-breakpoint>
|
||||
</breakpoints>
|
||||
</breakpoint-manager>
|
||||
<watches-manager>
|
||||
|
|
|
@ -1,38 +1,40 @@
|
|||
<div class="small-middle-container">
|
||||
<fieldset style="fieldset">
|
||||
<legend mat-dialog-title>Watchlist User Errors</legend>
|
||||
<div mat-dialog-content>
|
||||
<p>
|
||||
If there is an authentication error, this is because of an authentication issue with Plex (Token has expired).
|
||||
If this happens the user needs to re-login to Ombi.
|
||||
</p>
|
||||
<p>
|
||||
<em class="fa-solid fa-check key"></em> Successfully syncing the watchlist
|
||||
<br>
|
||||
<em class="fa-solid fa-times key"></em> Authentication error syncing the watchlist
|
||||
<br>
|
||||
<em class="fas fa-user-alt-slash key"></em> Not enabled for user (They need to log into Ombi via Plex)
|
||||
</p>
|
||||
<table mat-table *ngIf="dataSource" [dataSource]="dataSource" matSort class="mat-elevation-z8">
|
||||
<ng-container matColumnDef="userName">
|
||||
<th mat-header-cell *matHeaderCellDef> Username </th>
|
||||
<td mat-cell *matCellDef="let element"> {{element.userName}} </td>
|
||||
</ng-container>
|
||||
<ng-container matColumnDef="syncStatus">
|
||||
<th mat-header-cell *matHeaderCellDef> Watchlist Sync Result </th>
|
||||
<td mat-cell *matCellDef="let element">
|
||||
<em *ngIf="element.syncStatus === WatchlistSyncStatus.Successful" class="fa-solid fa-check"></em>
|
||||
<em *ngIf="element.syncStatus === WatchlistSyncStatus.Failed" class="fa-solid fa-times"></em>
|
||||
<em *ngIf="element.syncStatus === WatchlistSyncStatus.NotEnabled" class="fas fa-user-alt-slash"></em>
|
||||
</td>
|
||||
</ng-container>
|
||||
|
||||
<tr mat-header-row *matHeaderRowDef="displayedColumns"></tr>
|
||||
<tr mat-row *matRowDef="let row; columns: displayedColumns;"></tr>
|
||||
</table>
|
||||
</div>
|
||||
<mat-dialog-actions align="end">
|
||||
<button mat-button mat-dialog-close>Close</button>
|
||||
</mat-dialog-actions>
|
||||
</fieldset>
|
||||
<div class="watchlist-dialog-container">
|
||||
<mat-card class="watchlist-dialog-card">
|
||||
<mat-card-header>
|
||||
<mat-card-title>Watchlist User Errors</mat-card-title>
|
||||
</mat-card-header>
|
||||
<mat-card-content>
|
||||
<div class="watchlist-info-section">
|
||||
<mat-icon color="warn" class="info-icon">error_outline</mat-icon>
|
||||
<span>
|
||||
If there is an authentication error, this is because of an authentication issue with Plex (Token has expired).
|
||||
If this happens the user needs to re-login to Ombi.
|
||||
</span>
|
||||
</div>
|
||||
<div class="watchlist-legend">
|
||||
<span><mat-icon color="primary">check_circle</mat-icon> Successfully syncing the watchlist</span>
|
||||
<span><mat-icon color="warn">cancel</mat-icon> Authentication error syncing the watchlist</span>
|
||||
<span><mat-icon color="accent">person_off</mat-icon> Not enabled for user (They need to log into Ombi via Plex)</span>
|
||||
</div>
|
||||
<table mat-table *ngIf="dataSource" [dataSource]="dataSource" matSort class="mat-elevation-z8 modern-table">
|
||||
<ng-container matColumnDef="userName">
|
||||
<th mat-header-cell *matHeaderCellDef> Username </th>
|
||||
<td mat-cell *matCellDef="let element"> {{element.userName}} </td>
|
||||
</ng-container>
|
||||
<ng-container matColumnDef="syncStatus">
|
||||
<th mat-header-cell *matHeaderCellDef> Watchlist Sync Result </th>
|
||||
<td mat-cell *matCellDef="let element">
|
||||
<mat-icon *ngIf="element.syncStatus === WatchlistSyncStatus.Successful" color="primary">check_circle</mat-icon>
|
||||
<mat-icon *ngIf="element.syncStatus === WatchlistSyncStatus.Failed" color="warn">cancel</mat-icon>
|
||||
<mat-icon *ngIf="element.syncStatus === WatchlistSyncStatus.NotEnabled" color="accent">person_off</mat-icon>
|
||||
</td>
|
||||
</ng-container>
|
||||
<tr mat-header-row *matHeaderRowDef="displayedColumns"></tr>
|
||||
<tr mat-row *matRowDef="let row; columns: displayedColumns;"></tr>
|
||||
</table>
|
||||
</mat-card-content>
|
||||
<mat-card-actions align="end">
|
||||
<button mat-stroked-button mat-dialog-close color="accent">Close</button>
|
||||
</mat-card-actions>
|
||||
</mat-card>
|
||||
</div>
|
|
@ -10,4 +10,96 @@
|
|||
|
||||
.key {
|
||||
width: 40px;
|
||||
}
|
||||
|
||||
.watchlist-dialog-container {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: flex-start;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
.watchlist-dialog-card {
|
||||
background: #23272f;
|
||||
color: #f1f3f6;
|
||||
border-radius: 12px;
|
||||
border: 1px solid #353a45;
|
||||
box-shadow: 0 2px 12px 0 rgba(0,0,0,0.25);
|
||||
min-width: 420px;
|
||||
max-width: 600px;
|
||||
width: 100%;
|
||||
max-height: 70vh;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
mat-card-content {
|
||||
flex: 1 1 auto;
|
||||
overflow-y: auto;
|
||||
min-height: 0;
|
||||
}
|
||||
|
||||
mat-card-header {
|
||||
border-bottom: 1px solid #353a45;
|
||||
margin-bottom: 12px;
|
||||
flex: 0 0 auto;
|
||||
}
|
||||
|
||||
mat-card-title {
|
||||
color: #fff !important;
|
||||
font-weight: 700 !important;
|
||||
letter-spacing: 0.5px;
|
||||
}
|
||||
|
||||
.watchlist-info-section {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
background: #23272f;
|
||||
color: #e0e3ea;
|
||||
padding: 12px 0 8px 0;
|
||||
font-size: 15px;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.info-icon {
|
||||
font-size: 28px;
|
||||
color: #ffb300;
|
||||
}
|
||||
|
||||
.watchlist-legend {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 4px;
|
||||
margin-bottom: 18px;
|
||||
margin-top: 8px;
|
||||
font-size: 14px;
|
||||
color: #b0b6c3;
|
||||
}
|
||||
|
||||
.watchlist-legend mat-icon {
|
||||
vertical-align: middle;
|
||||
margin-right: 6px;
|
||||
}
|
||||
|
||||
.modern-table {
|
||||
background: transparent;
|
||||
color: #f1f3f6;
|
||||
border-radius: 8px;
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
.modern-table th, .modern-table td {
|
||||
color: #f1f3f6;
|
||||
font-size: 15px;
|
||||
}
|
||||
|
||||
mat-card-actions {
|
||||
padding-top: 16px;
|
||||
flex: 0 0 auto;
|
||||
background: #23272f;
|
||||
}
|
||||
|
||||
button[mat-stroked-button] {
|
||||
font-weight: 600;
|
||||
border-radius: 6px;
|
||||
}
|
|
@ -1,141 +1,179 @@
|
|||
<settings-menu></settings-menu>
|
||||
<div class="small-middle-container" *ngIf="settings">
|
||||
<fieldset style="width:100%;">
|
||||
<legend>Plex Configuration</legend>
|
||||
<div class="col-12">
|
||||
<div class="md-form-field align-right">
|
||||
<button (click)="openWatchlistUserLog()" type="button" class="mat-focus-indicator mat-flat-button mat-button-base mat-accent">Watchlist User Errors</button>
|
||||
|
||||
<div class="plex-settings-container" *ngIf="settings">
|
||||
<mat-card class="settings-card">
|
||||
<mat-card-header>
|
||||
<mat-card-title>Plex Configuration</mat-card-title>
|
||||
</mat-card-header>
|
||||
|
||||
<mat-card-content>
|
||||
<!-- Watchlist Settings Section -->
|
||||
<div class="settings-section">
|
||||
<div class="section-header">
|
||||
<h2>Watchlist Settings</h2>
|
||||
</div>
|
||||
|
||||
<div class="settings-grid">
|
||||
<mat-card class="setting-card">
|
||||
<mat-card-content>
|
||||
<div class="setting-header">
|
||||
<h3>Enable Plex</h3>
|
||||
<mat-slide-toggle [id]="'enable'" [(ngModel)]="settings.enable"></mat-slide-toggle>
|
||||
</div>
|
||||
</mat-card-content>
|
||||
</mat-card>
|
||||
|
||||
<mat-card class="setting-card">
|
||||
<mat-card-content>
|
||||
<div class="setting-header">
|
||||
<h3>Enable User Watchlist Requests</h3>
|
||||
<mat-slide-toggle [id]="'enableWatchlistImport'" [(ngModel)]="settings.enableWatchlistImport"></mat-slide-toggle>
|
||||
</div>
|
||||
<p class="setting-description">
|
||||
When a Plex User adds something to their watchlist in Plex, it will turn up in Ombi as a Request if enabled.
|
||||
This <strong>only</strong> applies to users that are logging in with their Plex Account.
|
||||
<br>Request limits if set are all still applied
|
||||
</p>
|
||||
</mat-card-content>
|
||||
</mat-card>
|
||||
|
||||
<mat-card class="setting-card" [class.disabled]="!settings.enableWatchlistImport">
|
||||
<mat-card-content>
|
||||
<div class="setting-header">
|
||||
<h3>Request Whole Show</h3>
|
||||
<mat-slide-toggle [id]="'monitorAll'" [(ngModel)]="settings.monitorAll" [disabled]="!settings.enableWatchlistImport"></mat-slide-toggle>
|
||||
</div>
|
||||
<p class="setting-description">
|
||||
If enabled then watchlist requests for TV Shows will request the <strong>whole</strong> show.
|
||||
If not enabled it will only request the latest season.
|
||||
</p>
|
||||
</mat-card-content>
|
||||
</mat-card>
|
||||
|
||||
<mat-card class="setting-card" [class.disabled]="!settings.enableWatchlistImport">
|
||||
<mat-card-content>
|
||||
<div class="setting-header">
|
||||
<h3>Notify on Token Expiration</h3>
|
||||
<mat-slide-toggle [id]="'notifyOnWatchlistTokenExpiration'" [(ngModel)]="settings.notifyOnWatchlistTokenExpiration" [disabled]="!settings.enableWatchlistImport"></mat-slide-toggle>
|
||||
</div>
|
||||
<p class="setting-description">
|
||||
When enabled, users will receive a notification if their Plex watchlist token expires and they need to log into Ombi again to continue using the watchlist feature.
|
||||
<br><strong>Note:</strong> This requires email notifications to be configured in the notification settings, and users must have an email address set on their account to receive these notifications.
|
||||
</p>
|
||||
</mat-card-content>
|
||||
</mat-card>
|
||||
</div>
|
||||
<mat-card class="info-banner">
|
||||
<mat-icon color="primary" style="margin-right: 12px;">info</mat-icon>
|
||||
<span style="flex:1;">
|
||||
Some users may need to re-log in to use the watchlist feature.
|
||||
</span>
|
||||
<button mat-button color="accent" (click)="openWatchlistUserLog()">
|
||||
View Users
|
||||
</button>
|
||||
</mat-card>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<settings-plex-form-field [label]="'Enable'" [type]="'checkbox'" [id]="'enable'" [(value)]="settings.enable"></settings-plex-form-field>
|
||||
<!-- Main Content Area -->
|
||||
<div class="main-content">
|
||||
<!-- Left Column - Servers and Actions -->
|
||||
<div class="content-column">
|
||||
<!-- Servers Section -->
|
||||
<div class="settings-section">
|
||||
<h2>Plex Servers</h2>
|
||||
<div class="servers-grid">
|
||||
<mat-card class="server-card" *ngFor="let server of settings.servers">
|
||||
<mat-card-content>
|
||||
<button mat-button (click)="edit(server)" [id]="server.name + '-button'">
|
||||
<mat-icon>dns</mat-icon>
|
||||
<span>{{server.name}}</span>
|
||||
</button>
|
||||
</mat-card-content>
|
||||
</mat-card>
|
||||
|
||||
<settings-plex-form-field [label]="'Enable User Watchlist Requests'" [type]="'checkbox'" [id]="'enableWatchlistImport'" [(value)]="settings.enableWatchlistImport">
|
||||
<small bottom>When a Plex User adds something to their watchlist in Plex, it will turn up in Ombi as a Request if enabled. This <b>only</b> applies to users that are logging in with their Plex Account
|
||||
<br>Request limits if set are all still applied
|
||||
</small>
|
||||
</settings-plex-form-field>
|
||||
<mat-card class="server-card new-server">
|
||||
<mat-card-content>
|
||||
<button mat-button (click)="newServer()" id="newServer">
|
||||
<mat-icon>add_circle</mat-icon>
|
||||
<span>Add Server</span>
|
||||
</button>
|
||||
</mat-card-content>
|
||||
</mat-card>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<settings-plex-form-field [disabled]="!settings.enableWatchlistImport" [label]="'Watchlist - Request Whole Show'" disabled [type]="'checkbox'" [id]="'monitorAll'" [(value)]="settings.monitorAll">
|
||||
<small bottom>If enabled then watchlist requests for TV Shows will request the <strong><em>whole</em></strong> show. If not enabled it will only request the latest season.
|
||||
</small>
|
||||
</settings-plex-form-field>
|
||||
<!-- Sync Actions Section -->
|
||||
<div class="settings-section">
|
||||
<h2>Sync Actions</h2>
|
||||
<div class="sync-actions-grid">
|
||||
<button mat-stroked-button (click)="runSync(PlexSyncType.Full)" id="fullSync">
|
||||
<mat-icon>sync</mat-icon>
|
||||
Full Sync
|
||||
</button>
|
||||
<button mat-stroked-button (click)="runSync(PlexSyncType.RecentlyAdded)" id="recentlyAddedSync">
|
||||
<mat-icon>update</mat-icon>
|
||||
Partial Sync
|
||||
</button>
|
||||
<button mat-stroked-button (click)="runSync(PlexSyncType.ClearAndReSync)" id="clearData">
|
||||
<mat-icon>cleaning_services</mat-icon>
|
||||
Clear & Resync
|
||||
</button>
|
||||
<button mat-stroked-button (click)="runSync(PlexSyncType.WatchlistImport)" id="watchlistImport">
|
||||
<mat-icon>playlist_add</mat-icon>
|
||||
Run Watchlist Import
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<settings-plex-form-field [disabled]="!settings.enableWatchlistImport" [label]="'Notify Users on Watchlist Token Expiration'" [type]="'checkbox'" [id]="'notifyOnWatchlistTokenExpiration'" [(value)]="settings.notifyOnWatchlistTokenExpiration">
|
||||
<small bottom>When enabled, users will receive a notification if their Plex watchlist token expires and they need to log into Ombi again to continue using the watchlist feature.
|
||||
<br><strong>Note:</strong> This requires email notifications to be configured in the notification settings, and users must have an email address set on their account to receive these notifications.
|
||||
</small>
|
||||
</settings-plex-form-field>
|
||||
<!-- Right Column - Plex Credentials -->
|
||||
<div class="content-column">
|
||||
<div class="settings-section">
|
||||
<h2>Plex Credentials</h2>
|
||||
<mat-card class="credentials-card">
|
||||
<mat-card-content>
|
||||
<p class="credentials-description">
|
||||
These fields are optional to automatically fill in your Plex server settings.
|
||||
<br>This will pass your username and password to the Plex.tv API to grab the servers associated with this user.
|
||||
<br>If you have 2FA enabled on your account, you need to append the 2FA code to the end of your password.
|
||||
</p>
|
||||
|
||||
<hr>
|
||||
<mat-form-field appearance="outline" class="full-width">
|
||||
<mat-label>Username</mat-label>
|
||||
<input matInput [id]="'username'" [(ngModel)]="username">
|
||||
</mat-form-field>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-7">
|
||||
<h2 style="margin: 1em 0 0 0;">Servers</h2>
|
||||
<mat-list style="display:flex; flex-flow: wrap;">
|
||||
<mat-card class="server-card" *ngFor="let server of settings.servers">
|
||||
<button mat-button (click)="edit(server)" id="{{server.name}}-button">
|
||||
<h3>{{server.name}}</h3>
|
||||
</button>
|
||||
</mat-card>
|
||||
<mat-form-field appearance="outline" class="full-width">
|
||||
<mat-label>Password</mat-label>
|
||||
<input matInput [id]="'password'" type="password" [(ngModel)]="password">
|
||||
</mat-form-field>
|
||||
|
||||
<mat-card class="server-card new-server-card">
|
||||
<button mat-button (click)="newServer()" id="newServer">
|
||||
<i class="fas fa-plus fa-xl"></i>
|
||||
<h3>Manually Add Server</h3>
|
||||
</button>
|
||||
</mat-card>
|
||||
</mat-list>
|
||||
<button mat-raised-button color="primary" id="loadServers" (click)="requestServers()" class="full-width">
|
||||
<mat-icon>key</mat-icon>
|
||||
Load Servers
|
||||
</button>
|
||||
|
||||
|
||||
<div class="row">
|
||||
|
||||
<br />
|
||||
<div class="form-group col-2">
|
||||
<button mat-raised-button (click)="runSync(PlexSyncType.Full)" type="button" id="fullSync"
|
||||
class="mat-focus-indicator mat-stroked-button mat-button-base">Full
|
||||
Sync</button><br />
|
||||
</div>
|
||||
<div class="form-group col-2">
|
||||
<button mat-raised-button (click)="runSync(PlexSyncType.RecentlyAdded)" type="button" id="recentlyAddedSync"
|
||||
class="mat-focus-indicator mat-stroked-button mat-button-base">Partial Sync</button>
|
||||
</div>
|
||||
<div class="form-group col-2">
|
||||
<button mat-raised-button (click)="runSync(PlexSyncType.ClearAndReSync)" type="button" id="clearData"
|
||||
class="mat-focus-indicator mat-stroked-button mat-button-base">
|
||||
Clear Data And Resync
|
||||
</button>
|
||||
</div>
|
||||
<div class="form-group col-12">
|
||||
<button mat-raised-button (click)="runSync(PlexSyncType.WatchlistImport)" type="button" id="watchlistImport"
|
||||
class="mat-focus-indicator mat-stroked-button mat-button-base">
|
||||
Run Watchlist Import
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
|
||||
<div class="col-md-2">
|
||||
<div class="form-group">
|
||||
<div>
|
||||
<button mat-raised-button (click)="save()" type="submit" id="save"
|
||||
class="mat-focus-indicator mat-raised-button mat-button-base mat-accent">Submit</button>
|
||||
<mat-form-field appearance="outline" class="full-width mt-3">
|
||||
<mat-label>Select Server</mat-label>
|
||||
<mat-select [id]="'servers'" *ngIf="loadedServers">
|
||||
<mat-option (click)="selectServer(s)" *ngFor="let s of loadedServers.servers.server" [value]="s.server">
|
||||
{{s.name}}
|
||||
</mat-option>
|
||||
</mat-select>
|
||||
<input matInput disabled placeholder="No Servers Loaded" *ngIf="!loadedServers">
|
||||
</mat-form-field>
|
||||
</mat-card-content>
|
||||
</mat-card>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</mat-card-content>
|
||||
|
||||
<div class="col-md-5">
|
||||
<div class="md-form-field">
|
||||
<label for="username" class="control-label">
|
||||
<h3>Plex Credentials</h3>
|
||||
<small>These fields are optional to automatically fill in your Plex server settings. <br>
|
||||
This will pass your username and password to the Plex.tv API to grab the servers associated with this user.
|
||||
<br>
|
||||
If you have 2FA enabled on your account, you need to append the 2FA code to the end of your password.</small>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<settings-plex-form-field [label]="'Username'" [id]="'username'" [(value)]="username"></settings-plex-form-field>
|
||||
<settings-plex-form-field [label]="'Password'" [id]="'password'" [type]="'password'" [(value)]="password"></settings-plex-form-field>
|
||||
|
||||
<div class="md-form-field">
|
||||
<div class="right">
|
||||
<button mat-raised-button id="loadServers" (click)="requestServers()"
|
||||
class="mat-stroked-button">Load Servers
|
||||
<i class="fas fa-key"></i>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-2 align-self-center">
|
||||
Please select the server:
|
||||
</div>
|
||||
<div class="md-form-field col-10">
|
||||
<div *ngIf="!loadedServers">
|
||||
<mat-form-field appearance="outline" floatLabel=auto>
|
||||
<input disabled matInput placeholder="No Servers Loaded" id="servers">
|
||||
</mat-form-field>
|
||||
</div>
|
||||
|
||||
<div *ngIf="loadedServers">
|
||||
<mat-form-field appearance="outline">
|
||||
<mat-select placeholder="Servers Loaded! Please Select" id="servers">
|
||||
<mat-option (click)="selectServer(s)"
|
||||
*ngFor="let s of loadedServers.servers.server" [value]="s.server">
|
||||
{{s.name}}</mat-option>
|
||||
</mat-select>
|
||||
</mat-form-field>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<mat-card-actions align="end">
|
||||
<button mat-raised-button color="accent" (click)="save()" id="save">
|
||||
<mat-icon>save</mat-icon>
|
||||
Save Changes
|
||||
</button>
|
||||
</mat-card-actions>
|
||||
</mat-card>
|
||||
</div>
|
||||
|
||||
</fieldset>
|
||||
</div>
|
||||
<!--(){{settings|json}}-->
|
||||
|
|
|
@ -43,4 +43,258 @@
|
|||
h3 {
|
||||
margin: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.plex-settings-container {
|
||||
padding: 20px;
|
||||
max-width: 1400px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
.settings-card {
|
||||
margin-bottom: 20px;
|
||||
background: transparent;
|
||||
box-shadow: 0 2px 12px 0 rgba(0,0,0,0.25);
|
||||
border-radius: 12px;
|
||||
border: 1px solid #353a45;
|
||||
}
|
||||
|
||||
.section-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.settings-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(320px, 1fr));
|
||||
gap: 24px;
|
||||
margin-bottom: 30px;
|
||||
}
|
||||
|
||||
.setting-card {
|
||||
height: 100%;
|
||||
background: transparent;
|
||||
border-radius: 10px;
|
||||
border: 1px solid #353a45;
|
||||
box-shadow: 0 1px 6px 0 rgba(0,0,0,0.18);
|
||||
color: #f1f3f6;
|
||||
}
|
||||
|
||||
.setting-card.disabled {
|
||||
opacity: 0.6;
|
||||
}
|
||||
|
||||
.setting-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 10px;
|
||||
font-weight: 600;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.setting-description {
|
||||
color: #e0e3ea;
|
||||
font-size: 15px;
|
||||
margin: 0;
|
||||
font-weight: 400;
|
||||
line-height: 1.6;
|
||||
}
|
||||
|
||||
.main-content {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
gap: 36px;
|
||||
margin-top: 36px;
|
||||
}
|
||||
|
||||
.servers-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fill, minmax(220px, 1fr));
|
||||
gap: 18px;
|
||||
}
|
||||
|
||||
.server-card {
|
||||
text-align: center;
|
||||
background: transparent;
|
||||
border-radius: 10px;
|
||||
border: 1px solid #353a45;
|
||||
box-shadow: 0 1px 6px 0 rgba(0,0,0,0.18);
|
||||
color: #f1f3f6;
|
||||
}
|
||||
|
||||
.server-card button {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 24px;
|
||||
color: #f1f3f6;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.server-card mat-icon {
|
||||
font-size: 32px;
|
||||
height: 32px;
|
||||
width: 32px;
|
||||
margin-bottom: 10px;
|
||||
color: #90caf9;
|
||||
}
|
||||
|
||||
.sync-actions-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
|
||||
gap: 18px;
|
||||
}
|
||||
|
||||
.sync-actions-grid button {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
padding: 24px;
|
||||
color: #f1f3f6;
|
||||
background: transparent;
|
||||
border: 1px solid #353a45;
|
||||
border-radius: 10px;
|
||||
font-weight: 500;
|
||||
box-shadow: 0 1px 6px 0 rgba(0,0,0,0.18);
|
||||
}
|
||||
|
||||
.sync-actions-grid mat-icon {
|
||||
font-size: 32px;
|
||||
height: 32px;
|
||||
width: 32px;
|
||||
margin-bottom: 10px;
|
||||
color: #90caf9;
|
||||
}
|
||||
|
||||
.credentials-card {
|
||||
padding: 24px;
|
||||
background: transparent;
|
||||
border-radius: 10px;
|
||||
border: 1px solid #353a45;
|
||||
box-shadow: 0 1px 6px 0 rgba(0,0,0,0.18);
|
||||
color: #f1f3f6;
|
||||
}
|
||||
|
||||
.credentials-description {
|
||||
color: #e0e3ea;
|
||||
margin-bottom: 20px;
|
||||
font-size: 15px;
|
||||
font-weight: 400;
|
||||
}
|
||||
|
||||
mat-card-title, h2, h3 {
|
||||
color: #fff !important;
|
||||
font-weight: 700 !important;
|
||||
letter-spacing: 0.5px;
|
||||
}
|
||||
|
||||
mat-card-header {
|
||||
border-bottom: 1px solid #353a45;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
mat-form-field {
|
||||
color: #f1f3f6 !important;
|
||||
}
|
||||
|
||||
mat-label, .mat-form-field-label {
|
||||
color: #b0b6c3 !important;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
input[matInput], .mat-input-element {
|
||||
color: #f1f3f6 !important;
|
||||
background: transparent !important;
|
||||
}
|
||||
|
||||
mat-select {
|
||||
color: #f1f3f6 !important;
|
||||
background: transparent !important;
|
||||
}
|
||||
|
||||
.full-width {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.mt-3 {
|
||||
margin-top: 1rem;
|
||||
}
|
||||
|
||||
.mat-slide-toggle.mat-checked .mat-slide-toggle-bar {
|
||||
background-color: #90caf9 !important;
|
||||
}
|
||||
|
||||
.mat-slide-toggle-thumb {
|
||||
background-color: #2196f3 !important;
|
||||
}
|
||||
|
||||
button[mat-flat-button], button[mat-raised-button], button[mat-stroked-button], button[mat-button] {
|
||||
font-weight: 600;
|
||||
letter-spacing: 0.2px;
|
||||
color: #f1f3f6;
|
||||
background: #2196f3;
|
||||
border-radius: 6px;
|
||||
box-shadow: 0 1px 4px 0 rgba(0,0,0,0.12);
|
||||
transition: background 0.2s;
|
||||
}
|
||||
button[mat-flat-button]:hover, button[mat-raised-button]:hover, button[mat-stroked-button]:hover, button[mat-button]:hover {
|
||||
background: #42a5f5;
|
||||
}
|
||||
|
||||
@media (max-width: 1200px) {
|
||||
.main-content {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.settings-grid {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
|
||||
.servers-grid {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
|
||||
.sync-actions-grid {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
}
|
||||
|
||||
.watchlist-errors-btn-row {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
margin-top: 18px;
|
||||
}
|
||||
|
||||
.info-banner {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
background: #23272f;
|
||||
color: #e0e3ea;
|
||||
border: 1px solid #1976d2;
|
||||
box-shadow: 0 1px 6px 0 rgba(0,0,0,0.12);
|
||||
border-radius: 8px;
|
||||
padding: 16px 20px;
|
||||
margin-top: 18px;
|
||||
margin-bottom: 0;
|
||||
font-size: 16px;
|
||||
font-weight: 500;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.info-banner mat-icon {
|
||||
font-size: 28px;
|
||||
color: #42a5f5;
|
||||
}
|
||||
|
||||
.info-banner button[mat-button] {
|
||||
margin-left: 16px;
|
||||
font-weight: 600;
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue