mirror of
https://github.com/Tzahi12345/YoutubeDL-Material.git
synced 2026-04-19 09:01:29 +03:00
Updated templates to new Angular control flow
This commit is contained in:
@@ -5,13 +5,23 @@
|
||||
<div class="row" width="100%" height="100%">
|
||||
<div class="col-6" style="text-align: left; margin-top: 1px;">
|
||||
<div style="display: flex; align-items: center;">
|
||||
<button #hamburgerMenu style="outline: none" *ngIf="router.url.split(';')[0] !== '/player'" mat-icon-button aria-label="Toggle side navigation" (click)="toggleSidenav()"><mat-icon>menu</mat-icon></button>
|
||||
<button (click)="goBack()" *ngIf="router.url.split(';')[0] === '/player'" mat-icon-button><mat-icon>arrow_back</mat-icon></button>
|
||||
@if (router.url.split(';')[0] !== '/player') {
|
||||
<button #hamburgerMenu style="outline: none" mat-icon-button aria-label="Toggle side navigation" (click)="toggleSidenav()"><mat-icon>menu</mat-icon></button>
|
||||
} @else {
|
||||
<button (click)="goBack()" mat-icon-button><mat-icon>arrow_back</mat-icon></button>
|
||||
}
|
||||
<div style="margin-left: 8px; display: inline-block;"><button mat-icon-button routerLink='/home'><img style="width: 32px;" src="assets/images/logo_128px.png"></button></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-6" style="text-align: right; align-items: flex-end; display: inline-block">
|
||||
<button *ngIf="postsService.config?.Extra.enable_notifications" [matMenuTriggerFor]="notificationsMenu" (menuOpened)="notificationMenuOpened()" mat-icon-button><mat-icon [matBadge]="notification_count" matBadgeColor="warn" matBadgeSize="small" *ngIf="notification_count > 0">notifications</mat-icon><mat-icon *ngIf="notification_count === 0">notifications_none</mat-icon></button>
|
||||
@if (postsService.config?.Extra.enable_notifications) {
|
||||
<button [matMenuTriggerFor]="notificationsMenu" (menuOpened)="notificationMenuOpened()" mat-icon-button>
|
||||
@if (notification_count > 0) {
|
||||
<mat-icon [matBadge]="notification_count" matBadgeColor="warn" matBadgeSize="small">notifications</mat-icon>
|
||||
} @else {
|
||||
<mat-icon>notifications_none</mat-icon>
|
||||
}</button>
|
||||
}
|
||||
<mat-menu [classList]="'notifications-menu'" (close)="notificationMenuClosed()" #notificationsMenu="matMenu">
|
||||
<app-notifications #notifications (notificationCount)="notificationCountUpdate($event)" (click)="$event.stopPropagation()"></app-notifications>
|
||||
</mat-menu>
|
||||
@@ -21,15 +31,19 @@
|
||||
<mat-icon>person</mat-icon>
|
||||
<span i18n="Profile menu label">Profile</span>
|
||||
</button>
|
||||
<button *ngIf="!postsService.config?.Advanced.multi_user_mode || postsService.isLoggedIn" class="top-menu-button" (click)="openArchivesDialog()" mat-menu-item>
|
||||
@if (!postsService.config?.Advanced.multi_user_mode || postsService.isLoggedIn) {
|
||||
<button class="top-menu-button" (click)="openArchivesDialog()" mat-menu-item>
|
||||
<mat-icon>topic</mat-icon>
|
||||
<span i18n="Archives menu label">Archives</span>
|
||||
</button>
|
||||
<button class="top-menu-button" (click)="themeMenuItemClicked($event)" *ngIf="allowThemeChange" mat-menu-item>
|
||||
}
|
||||
@if (allowThemeChange) {
|
||||
<button class="top-menu-button" (click)="themeMenuItemClicked($event)" mat-menu-item>
|
||||
<mat-icon>{{(postsService.theme.key === 'default') ? 'brightness_5' : 'brightness_2'}}</mat-icon>
|
||||
<span i18n="Dark mode toggle label">Dark</span>
|
||||
<mat-slide-toggle class="theme-slide-toggle" [checked]="postsService.theme.key === 'dark'"></mat-slide-toggle>
|
||||
</button>
|
||||
}
|
||||
<button class="top-menu-button" (click)="openAboutDialog()" mat-menu-item>
|
||||
<mat-icon>info</mat-icon>
|
||||
<span i18n="About menu label">About</span>
|
||||
@@ -44,19 +58,33 @@
|
||||
<mat-sidenav-container style="height: 100%">
|
||||
<mat-sidenav [opened]="postsService.sidepanel_mode === 'side' && !window.location.href.includes('/player')" [mode]="postsService.sidepanel_mode" #sidenav>
|
||||
<mat-nav-list>
|
||||
<a *ngIf="postsService.config && (!postsService.config.Advanced.multi_user_mode || postsService.isLoggedIn)" mat-list-item (click)="postsService.sidepanel_mode === 'over' ? sidenav.close() : null" routerLink='/home'><ng-container i18n="Navigation menu Home Page title">Home</ng-container></a>
|
||||
<a *ngIf="postsService.config && postsService.config.Advanced.multi_user_mode && !postsService.isLoggedIn" mat-list-item (click)="sidenav.close()" routerLink='/login'><ng-container i18n="Navigation menu Login Page title">Login</ng-container></a>
|
||||
<a *ngIf="postsService.config && allowSubscriptions && postsService.hasPermission('subscriptions')" mat-list-item (click)="postsService.sidepanel_mode === 'over' ? sidenav.close() : null" routerLink='/subscriptions'><ng-container i18n="Navigation menu Subscriptions Page title">Subscriptions</ng-container></a>
|
||||
<a *ngIf="postsService.config && enableDownloadsManager && postsService.hasPermission('downloads_manager')" mat-list-item (click)="postsService.sidepanel_mode === 'over' ? sidenav.close() : null" routerLink='/downloads'><ng-container i18n="Navigation menu Downloads Page title">Downloads</ng-container></a>
|
||||
<a *ngIf="postsService.config && postsService.hasPermission('tasks_manager')" mat-list-item (click)="postsService.sidepanel_mode === 'over' ? sidenav.close() : null" routerLink='/tasks'><ng-container i18n="Navigation menu Tasks Page title">Tasks</ng-container></a>
|
||||
<ng-container *ngIf="postsService.config && postsService.hasPermission('settings')">
|
||||
@if (postsService.config && (!postsService.config.Advanced.multi_user_mode || postsService.isLoggedIn)) {
|
||||
<a mat-list-item (click)="postsService.sidepanel_mode === 'over' ? sidenav.close() : null" routerLink='/home'><ng-container i18n="Navigation menu Home Page title">Home</ng-container></a>
|
||||
}
|
||||
@if (postsService.config && postsService.config.Advanced.multi_user_mode && !postsService.isLoggedIn) {
|
||||
<a mat-list-item (click)="sidenav.close()" routerLink='/login'><ng-container i18n="Navigation menu Login Page title">Login</ng-container></a>
|
||||
}
|
||||
@if (postsService.config && allowSubscriptions && postsService.hasPermission('subscriptions')) {
|
||||
<a mat-list-item (click)="postsService.sidepanel_mode === 'over' ? sidenav.close() : null" routerLink='/subscriptions'><ng-container i18n="Navigation menu Subscriptions Page title">Subscriptions</ng-container></a>
|
||||
}
|
||||
@if (postsService.config && enableDownloadsManager && postsService.hasPermission('downloads_manager')) {
|
||||
<a mat-list-item (click)="postsService.sidepanel_mode === 'over' ? sidenav.close() : null" routerLink='/downloads'><ng-container i18n="Navigation menu Downloads Page title">Downloads</ng-container></a>
|
||||
}
|
||||
@if (postsService.config && postsService.hasPermission('tasks_manager')) {
|
||||
<a mat-list-item (click)="postsService.sidepanel_mode === 'over' ? sidenav.close() : null" routerLink='/tasks'><ng-container i18n="Navigation menu Tasks Page title">Tasks</ng-container></a>
|
||||
}
|
||||
@if (postsService.config && postsService.hasPermission('settings')) {
|
||||
<mat-divider></mat-divider>
|
||||
<a mat-list-item (click)="postsService.sidepanel_mode === 'over' ? sidenav.close() : null" routerLink='/settings'><ng-container i18n="Settings menu label">Settings</ng-container></a>
|
||||
</ng-container>
|
||||
<ng-container *ngIf="postsService.config && allowSubscriptions && postsService.subscriptions && postsService.hasPermission('subscriptions')">
|
||||
<mat-divider *ngIf="postsService.subscriptions.length > 0"></mat-divider>
|
||||
<a *ngFor="let subscription of postsService.subscriptions" mat-list-item (click)="postsService.sidepanel_mode === 'over' ? sidenav.close() : null" [routerLink]="['/subscription', { id: subscription.id }]"><ngx-avatars [style.display]="'inline-block'" [style.margin-right]="'10px'" size="32" [name]="subscription.name"></ngx-avatars>{{subscription.name}}</a>
|
||||
</ng-container>
|
||||
}
|
||||
@if (postsService.config && allowSubscriptions && postsService.subscriptions && postsService.hasPermission('subscriptions')) {
|
||||
@if (postsService.subscriptions.length > 0) {
|
||||
<mat-divider></mat-divider>
|
||||
}
|
||||
@for (subscription of postsService.subscriptions; track subscription) {
|
||||
<a mat-list-item (click)="postsService.sidepanel_mode === 'over' ? sidenav.close() : null" [routerLink]="['/subscription', { id: subscription.id }]"><ngx-avatars [style.display]="'inline-block'" [style.margin-right]="'10px'" size="32" [name]="subscription.name"></ngx-avatars>{{subscription.name}}</a>
|
||||
}
|
||||
}
|
||||
</mat-nav-list>
|
||||
</mat-sidenav>
|
||||
<mat-sidenav-content [style.background]="postsService.theme ? postsService.theme.background_color : null">
|
||||
|
||||
@@ -3,11 +3,9 @@
|
||||
<mat-label i18n="Filter">Filter</mat-label>
|
||||
<input matInput [(ngModel)]="text_filter" (keyup)="applyFilter($event)" #input>
|
||||
</mat-form-field>
|
||||
|
||||
<div [hidden]="!(archives && archives.length > 0)">
|
||||
<div class="mat-elevation-z8">
|
||||
<mat-table matSort [dataSource]="dataSource">
|
||||
|
||||
<!-- Select Column -->
|
||||
<!-- Checkbox Column -->
|
||||
<ng-container matColumnDef="select">
|
||||
@@ -25,13 +23,11 @@
|
||||
<mat-icon class="audio-video-icon">{{(row.type === 'audio') ? 'audiotrack' : 'movie'}}</mat-icon>
|
||||
</mat-cell>
|
||||
</ng-container>
|
||||
|
||||
<!-- Date Column -->
|
||||
<ng-container matColumnDef="timestamp">
|
||||
<mat-header-cell *matHeaderCellDef mat-sort-header> <ng-container i18n="Date">Date</ng-container> </mat-header-cell>
|
||||
<mat-cell *matCellDef="let element"> {{element.timestamp*1000 | date: 'short'}} </mat-cell>
|
||||
</ng-container>
|
||||
|
||||
<!-- Title Column -->
|
||||
<ng-container matColumnDef="title">
|
||||
<mat-header-cell *matHeaderCellDef mat-sort-header> <ng-container i18n="Title">Title</ng-container> </mat-header-cell>
|
||||
@@ -41,7 +37,6 @@
|
||||
</span>
|
||||
</mat-cell>
|
||||
</ng-container>
|
||||
|
||||
<!-- ID Column -->
|
||||
<ng-container matColumnDef="id">
|
||||
<mat-header-cell *matHeaderCellDef mat-sort-header> <ng-container i18n="ID">ID</ng-container> </mat-header-cell>
|
||||
@@ -51,7 +46,6 @@
|
||||
</span>
|
||||
</mat-cell>
|
||||
</ng-container>
|
||||
|
||||
<!-- Extractor Column -->
|
||||
<ng-container matColumnDef="extractor">
|
||||
<mat-header-cell *matHeaderCellDef mat-sort-header> <ng-container i18n="Extractor">Extractor</ng-container> </mat-header-cell>
|
||||
@@ -61,17 +55,16 @@
|
||||
</span>
|
||||
</mat-cell>
|
||||
</ng-container>
|
||||
|
||||
<mat-header-row *matHeaderRowDef="displayedColumns; sticky: true"></mat-header-row>
|
||||
<mat-row *matRowDef="let row; columns: displayedColumns;"></mat-row>
|
||||
</mat-table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div *ngIf="(!archives || archives.length === 0)">
|
||||
@if ((!archives || archives.length === 0)) {
|
||||
<div>
|
||||
<h4 style="text-align: center; margin-top: 10px;" i18n="Archives empty">Archives empty</h4>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
}
|
||||
<div style="margin: 10px 10px 10px 0px; display: flex;">
|
||||
<span style="flex-grow: 1;" class="flex-items">
|
||||
<button [disabled]="selection.selected.length === 0" color="warn" style="margin: 10px;" mat-stroked-button i18n="Delete selected" (click)="openDeleteSelectedArchivesDialog()">Delete selected</button>
|
||||
@@ -82,7 +75,9 @@
|
||||
<mat-label i18n="Subscription">Subscription</mat-label>
|
||||
<mat-select [ngModel]="sub_id" (ngModelChange)="subFilterSelectionChanged($event)">
|
||||
<mat-option [value]="'none'" i18n="None">None</mat-option>
|
||||
<mat-option *ngFor="let sub of postsService.subscriptions" [value]="sub.id">{{sub.name}}</mat-option>
|
||||
@for (sub of postsService.subscriptions; track sub) {
|
||||
<mat-option [value]="sub.id">{{sub.name}}</mat-option>
|
||||
}
|
||||
</mat-select>
|
||||
</mat-form-field>
|
||||
<mat-form-field style="width: 100px; margin-bottom: -1.25em; margin-left: 10px;">
|
||||
@@ -95,7 +90,6 @@
|
||||
</mat-form-field>
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div class="file-drop-parent">
|
||||
<ngx-file-drop [multiple]="false" accept=".txt" dropZoneLabel="Drop file here" (onFileDrop)="dropped($event)">
|
||||
<ng-template class="file-drop" ngx-file-drop-content-tmp let-openFileSelector="openFileSelector">
|
||||
@@ -110,11 +104,11 @@
|
||||
</ng-template>
|
||||
</ngx-file-drop>
|
||||
</div>
|
||||
|
||||
<div style="margin-top: 10px; color: white">
|
||||
<table class="table">
|
||||
<tbody class="upload-name-style">
|
||||
<tr *ngFor="let item of files; let i=index">
|
||||
@for (item of files; track item; let i = $index) {
|
||||
<tr>
|
||||
<td style="vertical-align: middle; border-top: unset">
|
||||
<strong>{{ item.relativePath }}</strong>
|
||||
</td>
|
||||
@@ -124,7 +118,9 @@
|
||||
<mat-label i18n="Subscription">Subscription</mat-label>
|
||||
<mat-select [ngModel]="upload_sub_id" (ngModelChange)="subUploadFilterSelectionChanged($event)">
|
||||
<mat-option [value]="'none'" i18n="None">None</mat-option>
|
||||
<mat-option *ngFor="let sub of postsService.subscriptions" [value]="sub.id">{{sub.name}}</mat-option>
|
||||
@for (sub of postsService.subscriptions; track sub) {
|
||||
<mat-option [value]="sub.id">{{sub.name}}</mat-option>
|
||||
}
|
||||
</mat-select>
|
||||
</mat-form-field>
|
||||
<mat-form-field style="width: 100px; margin-left: 10px">
|
||||
@@ -134,10 +130,15 @@
|
||||
<mat-option [value]="'audio'" i18n="Audio">Audio</mat-option>
|
||||
</mat-select>
|
||||
</mat-form-field>
|
||||
<button style="margin-left: 10px" [disabled]="uploading_archive || uploaded_archive" (click)="importArchive()" matTooltip="Upload" i18n-matTooltip="Upload" mat-mini-fab><mat-icon>publish</mat-icon><mat-spinner *ngIf="uploading_archive" class="spinner" [diameter]="38"></mat-spinner></button>
|
||||
<button style="margin-left: 10px" [disabled]="uploading_archive || uploaded_archive" (click)="importArchive()" matTooltip="Upload" i18n-matTooltip="Upload" mat-mini-fab><mat-icon>publish</mat-icon>
|
||||
@if (uploading_archive) {
|
||||
<mat-spinner class="spinner" [diameter]="38"></mat-spinner>
|
||||
}
|
||||
</button>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
}
|
||||
</tbody>
|
||||
</table>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
@@ -1,6 +1,14 @@
|
||||
<div class="buttons-container">
|
||||
<button (click)="startWatching()" *ngIf="!watch_together_clicked" mat-flat-button>Watch together</button>
|
||||
<button (click)="startServer()" *ngIf="watch_together_clicked && !started && server_mode && server_already_exists === false" mat-flat-button>Start stream</button>
|
||||
<button (click)="startClient()" *ngIf="watch_together_clicked && !started && server_already_exists === true" mat-flat-button>Join stream</button>
|
||||
<button style="margin-left: 10px;" (click)="stop()" *ngIf="watch_together_clicked" mat-flat-button>Stop</button>
|
||||
@if (!watch_together_clicked) {
|
||||
<button (click)="startWatching()" mat-flat-button>Watch together</button>
|
||||
} @else {
|
||||
@if (!started) {
|
||||
@if (server_already_exists) {
|
||||
<button (click)="startClient()" mat-flat-button>Join stream</button>
|
||||
} @else if (server_mode) {
|
||||
<button (click)="startServer()" mat-flat-button>Start stream</button>
|
||||
}
|
||||
}
|
||||
<button style="margin-left: 10px;" (click)="stop()" mat-flat-button>Stop</button>
|
||||
}
|
||||
</div>
|
||||
@@ -1,13 +1,18 @@
|
||||
<div *ngIf="playlists && playlists.length > 0">
|
||||
@if (playlists) {
|
||||
<div>
|
||||
<div class="container">
|
||||
<div class="row justify-content-center">
|
||||
<div *ngFor="let playlist of playlists; let i = index" class="mb-2 mt-2" [ngClass]="[ postsService.card_size === 'small' ? 'col-2 small-col' : '', postsService.card_size === 'medium' ? 'col-6 col-lg-4 medium-col' : '', postsService.card_size === 'large' ? 'col-12 large-col' : '' ]">
|
||||
@for (playlist of playlists; track playlist; let i = $index) {
|
||||
<div class="mb-2 mt-2" [ngClass]="[ postsService.card_size === 'small' ? 'col-2 small-col' : '', postsService.card_size === 'medium' ? 'col-6 col-lg-4 medium-col' : '', postsService.card_size === 'large' ? 'col-12 large-col' : '' ]">
|
||||
<app-unified-file-card [index]="i" [card_size]="postsService.card_size" [locale]="postsService.locale" (goToFile)="goToPlaylist($event)" [file_obj]="playlist" [is_playlist]="true" (editPlaylist)="editPlaylistDialog($event)" (deleteFile)="deletePlaylist($event)" [baseStreamPath]="postsService.path" [jwtString]="postsService.isLoggedIn ? this.postsService.token : ''" [loading]="false"></app-unified-file-card>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div *ngIf="playlists && playlists.length === 0" style="text-align: center;">
|
||||
} @empty {
|
||||
<div style="text-align: center;">
|
||||
No playlists available. Create one from your downloading files by clicking the blue plus button.
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
<div class="add-playlist-button"><button (click)="openCreatePlaylistDialog()" mat-fab><mat-icon>add</mat-icon></button></div>
|
||||
@@ -1,13 +1,11 @@
|
||||
<div [hidden]="!(downloads && downloads.length > 0)">
|
||||
<div style="overflow: hidden;" [ngClass]="uids ? 'rounded mat-elevation-z2' : 'mat-elevation-z8'">
|
||||
<mat-table style="overflow: hidden" [ngClass]="uids ? 'rounded-top' : null" matSort [dataSource]="dataSource">
|
||||
|
||||
<!-- Date Column -->
|
||||
<ng-container matColumnDef="timestamp_start">
|
||||
<mat-header-cell *matHeaderCellDef mat-sort-header> <ng-container i18n="Date">Date</ng-container> </mat-header-cell>
|
||||
<mat-cell *matCellDef="let element"> {{element.timestamp_start | date: 'short'}} </mat-cell>
|
||||
</ng-container>
|
||||
|
||||
<!-- Title Column -->
|
||||
<ng-container matColumnDef="title">
|
||||
<mat-header-cell *matHeaderCellDef mat-sort-header style="flex: 2"> <ng-container i18n="Title">Title</ng-container> </mat-header-cell>
|
||||
@@ -17,81 +15,88 @@
|
||||
</span>
|
||||
</mat-cell>
|
||||
</ng-container>
|
||||
|
||||
<!-- Subscription Column -->
|
||||
<ng-container matColumnDef="sub_name">
|
||||
<mat-header-cell *matHeaderCellDef mat-sort-header> <ng-container i18n="Subscription">Subscription</ng-container> </mat-header-cell>
|
||||
<mat-cell *matCellDef="let element">
|
||||
<ng-container *ngIf="element.sub_name">
|
||||
@if (element.sub_name) {
|
||||
{{element.sub_name}}
|
||||
</ng-container>
|
||||
<ng-container *ngIf="!element.sub_name">
|
||||
N/A
|
||||
</ng-container>
|
||||
} @else {
|
||||
<ng-container i18n="N/A">N/A</ng-container>
|
||||
}
|
||||
</mat-cell>
|
||||
</ng-container>
|
||||
|
||||
<!-- Progress Column -->
|
||||
<ng-container matColumnDef="percent_complete">
|
||||
<mat-header-cell *matHeaderCellDef mat-sort-header> <ng-container i18n="Progress">Progress</ng-container> </mat-header-cell>
|
||||
<mat-cell *matCellDef="let element">
|
||||
<ng-container *ngIf="!element.error && element.step_index !== 2">
|
||||
@if (!element.error) {
|
||||
@if (element.step_index !== 2) {
|
||||
{{STEP_INDEX_TO_LABEL[element.step_index]}}
|
||||
</ng-container>
|
||||
<ng-container *ngIf="!element.error && element.step_index === 2">
|
||||
<ng-container *ngIf="element.percent_complete">
|
||||
} @else {
|
||||
@if (element.percent_complete) {
|
||||
{{+(element.percent_complete) > 100 ? '100' : element.percent_complete}}%
|
||||
</ng-container>
|
||||
<ng-container *ngIf="!element.percent_complete">
|
||||
N/A
|
||||
</ng-container>
|
||||
</ng-container>
|
||||
<ng-container *ngIf="element.error" i18n="Error">Error</ng-container>
|
||||
} @else {
|
||||
<ng-container i18n="N/A">N/A</ng-container>
|
||||
}
|
||||
}
|
||||
} @else {
|
||||
<ng-container i18n="Error">Error</ng-container>
|
||||
}
|
||||
</mat-cell>
|
||||
</ng-container>
|
||||
|
||||
<!-- Actions Column -->
|
||||
<ng-container matColumnDef="actions">
|
||||
<mat-header-cell *matHeaderCellDef [ngStyle]="{flex: actionsFlex}"> <ng-container i18n="Actions">Actions</ng-container> </mat-header-cell>
|
||||
<mat-cell *matCellDef="let element" [ngStyle]="{flex: actionsFlex}">
|
||||
<div *ngIf="!minimizeButtons">
|
||||
<ng-container *ngFor="let downloadAction of downloadActions">
|
||||
@if (!minimizeButtons) {
|
||||
<div>
|
||||
@for (downloadAction of downloadActions; track downloadAction) {
|
||||
<span class="button-span">
|
||||
<mat-spinner [diameter]="28" *ngIf="downloadAction.loading && downloadAction.loading(element)" class="icon-button-spinner"></mat-spinner>
|
||||
<button *ngIf="downloadAction.show(element)" (click)="downloadAction.action(element)" [disabled]="downloadAction.loading && downloadAction.loading(element)" [matTooltip]="downloadAction.tooltip" mat-icon-button><mat-icon>{{downloadAction.icon}}</mat-icon></button>
|
||||
@if (downloadAction.loading && downloadAction.loading(element)) {
|
||||
<mat-spinner [diameter]="28" class="icon-button-spinner"></mat-spinner>
|
||||
}
|
||||
@if (downloadAction.show(element)) {
|
||||
<button (click)="downloadAction.action(element)" [disabled]="downloadAction.loading && downloadAction.loading(element)" [matTooltip]="downloadAction.tooltip" mat-icon-button><mat-icon>{{downloadAction.icon}}</mat-icon></button>
|
||||
}
|
||||
</span>
|
||||
</ng-container>
|
||||
}
|
||||
</div>
|
||||
<div *ngIf="minimizeButtons">
|
||||
} @else {
|
||||
<div>
|
||||
<button [matMenuTriggerFor]="download_actions" mat-icon-button><mat-icon>more_vert</mat-icon></button>
|
||||
<mat-menu #download_actions="matMenu">
|
||||
<ng-container *ngFor="let downloadAction of downloadActions">
|
||||
<button *ngIf="downloadAction.show(element)" (click)="downloadAction.action(element)" [disabled]="downloadAction.loading && downloadAction.loading(element)" mat-menu-item>
|
||||
@for (downloadAction of downloadActions; track downloadAction) {
|
||||
@if (downloadAction.show(element)) {
|
||||
<button (click)="downloadAction.action(element)" [disabled]="downloadAction.loading && downloadAction.loading(element)" mat-menu-item>
|
||||
<mat-icon>{{downloadAction.icon}}</mat-icon>
|
||||
<span>{{downloadAction.tooltip}}</span>
|
||||
</button>
|
||||
</ng-container>
|
||||
}
|
||||
}
|
||||
</mat-menu>
|
||||
</div>
|
||||
}
|
||||
</mat-cell>
|
||||
</ng-container>
|
||||
|
||||
<mat-header-row [ngClass]="uids ? 'rounded-top' : null" *matHeaderRowDef="displayedColumns"></mat-header-row>
|
||||
<mat-row *matRowDef="let row; columns: displayedColumns;"></mat-row>
|
||||
</mat-table>
|
||||
|
||||
<mat-paginator [ngClass]="uids ? 'rounded-bottom' : null" [pageSizeOptions]="[5, 10, 20]"
|
||||
showFirstLastButtons
|
||||
aria-label="Select page of downloads">
|
||||
</mat-paginator>
|
||||
</div>
|
||||
<div *ngIf="!uids" class="downloads-action-button-div">
|
||||
@if (!uids) {
|
||||
<div class="downloads-action-button-div">
|
||||
<button class="downloads-action-button" [disabled]="!running_download_exists" mat-stroked-button (click)="pauseAllDownloads()"><ng-container i18n="Pause all downloads">Pause all downloads</ng-container></button>
|
||||
<button class="downloads-action-button" [disabled]="!paused_download_exists" mat-stroked-button (click)="resumeAllDownloads()"><ng-container i18n="Resume all downloads">Resume all downloads</ng-container></button>
|
||||
<button class="downloads-action-button" color="warn" mat-stroked-button (click)="clearDownloadsByType()"><ng-container i18n="Clear downloads">Clear downloads</ng-container></button>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
|
||||
<div *ngIf="(!downloads || downloads.length === 0) && downloads_retrieved && !uids">
|
||||
@if ((!downloads || downloads.length === 0) && downloads_retrieved && !uids) {
|
||||
<div>
|
||||
<h4 style="text-align: center; margin-top: 10px;" i18n="No downloads label">No downloads available!</h4>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
@@ -14,7 +14,8 @@
|
||||
</mat-form-field>
|
||||
</div>
|
||||
</mat-tab>
|
||||
<mat-tab *ngIf="registrationEnabled" label="Register" i18n-label="Register">
|
||||
@if (registrationEnabled) {
|
||||
<mat-tab label="Register" i18n-label="Register">
|
||||
<div style="margin-top: 10px;">
|
||||
<mat-form-field style="width: 100%">
|
||||
<mat-label i18n="User name">User name</mat-label>
|
||||
@@ -34,13 +35,21 @@
|
||||
</mat-form-field>
|
||||
</div>
|
||||
</mat-tab>
|
||||
}
|
||||
</mat-tab-group>
|
||||
<div *ngIf="selectedTabIndex === 0" class="login-button-div">
|
||||
@if (selectedTabIndex === 0) {
|
||||
<div class="login-button-div">
|
||||
<button [disabled]="loggingIn" color="primary" (click)="login()" mat-raised-button><ng-container i18n="Login">Login</ng-container></button>
|
||||
<mat-progress-bar *ngIf="loggingIn" class="login-progress-bar" mode="indeterminate"></mat-progress-bar>
|
||||
@if (loggingIn) {
|
||||
<mat-progress-bar class="login-progress-bar" mode="indeterminate"></mat-progress-bar>
|
||||
}
|
||||
</div>
|
||||
<div *ngIf="selectedTabIndex === 1" class="login-button-div">
|
||||
} @else {
|
||||
<div class="login-button-div">
|
||||
<button [disabled]="registering" color="primary" (click)="register()" mat-raised-button><ng-container i18n="Register">Register</ng-container></button>
|
||||
<mat-progress-bar *ngIf="registering" class="login-progress-bar" mode="indeterminate"></mat-progress-bar>
|
||||
@if (registering) {
|
||||
<mat-progress-bar class="login-progress-bar" mode="indeterminate"></mat-progress-bar>
|
||||
}
|
||||
</div>
|
||||
}
|
||||
</mat-card>
|
||||
@@ -1,22 +1,24 @@
|
||||
<div style="height: 100%;">
|
||||
<div *ngIf="logs_loading" style="z-index: 999; position: absolute; top: 40%; left: 50%">
|
||||
@if (logs_loading) {
|
||||
<div style="z-index: 999; position: absolute; top: 40%; left: 50%">
|
||||
<mat-spinner [diameter]="32"></mat-spinner>
|
||||
</div>
|
||||
}
|
||||
<!-- Virtual mode (fast, select text buggy) -->
|
||||
<!--<cdk-virtual-scroll-viewport style="height: 274px;" itemSize="50" class="example-viewport">
|
||||
<div *cdkVirtualFor="let log of logs; let i = index" class="example-item">
|
||||
<span [ngStyle]="{'color':log.color}">{{log.text}}</span>
|
||||
</div>
|
||||
</cdk-virtual-scroll-viewport>-->
|
||||
|
||||
<!-- Non-virtual mode (slow, bug-free) -->
|
||||
<div style="height: 100%; overflow-y: auto">
|
||||
<div *ngFor="let log of logs; let i = index" class="example-item">
|
||||
</cdk-virtual-scroll-viewport>-->
|
||||
<!-- Non-virtual mode (slow, bug-free) -->
|
||||
<div style="height: 100%; overflow-y: auto">
|
||||
@for (log of logs; track log) {
|
||||
<div class="example-item">
|
||||
<span [ngStyle]="{'color':log.color}">{{log.text}}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
}
|
||||
</div>
|
||||
<div>
|
||||
<button style="position: absolute; right: 0px; top: 12px;" [cdkCopyToClipboard]="logs_text" (click)="copiedLogsToClipboard()" mat-mini-fab color="primary"><mat-icon style="font-size: 22px !important;">content_copy</mat-icon></button>
|
||||
<div style="display: inline-block;">
|
||||
<ng-container i18n="Label for lines select in logger view">Lines:</ng-container>
|
||||
@@ -32,6 +34,5 @@
|
||||
</div>
|
||||
<span class="spacer"></span>
|
||||
<button style="float: right; margin-top: 12px;" (click)="clearLogs()" mat-stroked-button color="warn"><ng-container i18n="Clear logs button">Clear logs</ng-container></button>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
@@ -1,7 +1,8 @@
|
||||
<h4 *ngIf="role" mat-dialog-title><ng-container i18n="Manage role dialog title">Manage role</ng-container> - {{role.key}}</h4>
|
||||
|
||||
<mat-dialog-content *ngIf="role">
|
||||
<div *ngFor="let permission of available_permissions">
|
||||
@if (role) {
|
||||
<h4 mat-dialog-title><ng-container i18n="Manage role dialog title">Manage role</ng-container> - {{role.key}}</h4>
|
||||
<mat-dialog-content>
|
||||
@for (permission of available_permissions; track permission) {
|
||||
<div>
|
||||
<div matListItemTitle>{{permissionToLabel[permission] ? permissionToLabel[permission] : permission}}</div>
|
||||
<div matListItemLine>
|
||||
<mat-radio-group [disabled]="permission === 'settings' && role.key === 'admin'" (change)="changeRolePermissions($event, permission)" [(ngModel)]="permissions[permission]" [attr.aria-label]="'Give role permission for ' + permission">
|
||||
@@ -10,8 +11,9 @@
|
||||
</mat-radio-group>
|
||||
</div>
|
||||
</div>
|
||||
</mat-dialog-content>
|
||||
|
||||
}
|
||||
</mat-dialog-content>
|
||||
}
|
||||
<mat-dialog-actions>
|
||||
<button mat-button mat-dialog-close><ng-container i18n="Close">Close</ng-container></button>
|
||||
</mat-dialog-actions>
|
||||
@@ -1,8 +1,7 @@
|
||||
<h4 *ngIf="user" mat-dialog-title><ng-container i18n="Manage user dialog title">Manage user</ng-container> - {{user.name}}</h4>
|
||||
|
||||
<mat-dialog-content *ngIf="user">
|
||||
@if (user) {
|
||||
<h4 mat-dialog-title><ng-container i18n="Manage user dialog title">Manage user</ng-container> - {{user.name}}</h4>
|
||||
<mat-dialog-content>
|
||||
<p><ng-container i18n="User UID">User UID:</ng-container> {{user.uid}}</p>
|
||||
|
||||
<div>
|
||||
<mat-form-field style="margin-right: 15px;">
|
||||
<mat-label i18n="New password">New password</mat-label>
|
||||
@@ -10,9 +9,9 @@
|
||||
</mat-form-field>
|
||||
<button mat-raised-button color="accent" (click)="setNewPassword()" [disabled]="newPasswordInput.length === 0"><ng-container i18n="Set new password">Set new password</ng-container></button>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<div *ngFor="let permission of available_permissions">
|
||||
@for (permission of available_permissions; track permission) {
|
||||
<div>
|
||||
<div matListItemTitle>{{permissionToLabel[permission] ? permissionToLabel[permission] : permission}}</div>
|
||||
<div matListItemLine>
|
||||
<mat-radio-group [disabled]="permission === 'settings' && postsService.user.uid === user.uid" (change)="changeUserPermissions($event, permission)" [(ngModel)]="permissions[permission]" [attr.aria-label]="'Give user permission for ' + permission">
|
||||
@@ -22,9 +21,10 @@
|
||||
</mat-radio-group>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
</mat-dialog-content>
|
||||
|
||||
</mat-dialog-content>
|
||||
}
|
||||
<mat-dialog-actions>
|
||||
<button style="margin-bottom: 5px;" mat-stroked-button mat-dialog-close><ng-container i18n="Close">Close</ng-container></button>
|
||||
</mat-dialog-actions>
|
||||
@@ -1,4 +1,5 @@
|
||||
<div *ngIf="dataSource; else loading">
|
||||
@if (dataSource) {
|
||||
<div>
|
||||
<div style="padding: 15px">
|
||||
<div class="row">
|
||||
<div class="table table-responsive pb-4 pt-4">
|
||||
@@ -8,34 +9,31 @@
|
||||
<input matInput (keyup)="applyFilter($event)">
|
||||
</mat-form-field>
|
||||
</div>
|
||||
|
||||
<div class="mat-elevation-z8" style="margin-right: 15px;">
|
||||
|
||||
<mat-table #table [dataSource]="dataSource" matSort>
|
||||
|
||||
<!-- Name Column -->
|
||||
<ng-container matColumnDef="name">
|
||||
<mat-header-cell *matHeaderCellDef mat-sort-header><ng-container i18n="Username users table header"> User name </ng-container></mat-header-cell>
|
||||
|
||||
<mat-cell *matCellDef="let row">
|
||||
<span *ngIf="editObject && editObject.uid === row.uid; else noteditingname">
|
||||
@if (editObject && editObject.uid === row.uid) {
|
||||
<span>
|
||||
<span style="width: 80%;">
|
||||
<mat-form-field>
|
||||
<input matInput [(ngModel)]="constructedObject['name']" type="text" style="font-size: 12px">
|
||||
</mat-form-field>
|
||||
</span>
|
||||
</span>
|
||||
<ng-template #noteditingname>
|
||||
} @else {
|
||||
{{row.name}}
|
||||
</ng-template>
|
||||
}
|
||||
</mat-cell>
|
||||
</ng-container>
|
||||
|
||||
<!-- Email Column -->
|
||||
<ng-container matColumnDef="role">
|
||||
<mat-header-cell *matHeaderCellDef mat-sort-header><ng-container i18n="Role users table header"> Role </ng-container></mat-header-cell>
|
||||
<mat-cell *matCellDef="let row">
|
||||
<span *ngIf="editObject && editObject.uid === row.uid; else noteditingemail">
|
||||
@if (editObject && editObject.uid === row.uid) {
|
||||
<span>
|
||||
<span style="width: 80%;">
|
||||
<mat-form-field>
|
||||
<mat-select [(ngModel)]="constructedObject['role']">
|
||||
@@ -45,17 +43,17 @@
|
||||
</mat-form-field>
|
||||
</span>
|
||||
</span>
|
||||
<ng-template #noteditingemail>
|
||||
} @else {
|
||||
{{row.role}}
|
||||
</ng-template>
|
||||
}
|
||||
</mat-cell>
|
||||
</ng-container>
|
||||
|
||||
<!-- Actions Column -->
|
||||
<ng-container matColumnDef="actions">
|
||||
<mat-header-cell *matHeaderCellDef mat-sort-header><ng-container i18n="Actions users table header"> Actions </ng-container></mat-header-cell>
|
||||
<mat-cell *matCellDef="let row">
|
||||
<span *ngIf="editObject && editObject.uid === row.uid; else notediting">
|
||||
@if (editObject && editObject.uid === row.uid) {
|
||||
<span>
|
||||
<button mat-icon-button color="primary" (click)="finishEditing(row.uid)" matTooltip="Save" i18n-matTooltip="save user edit action button tooltip">
|
||||
<mat-icon>done</mat-icon>
|
||||
</button>
|
||||
@@ -63,11 +61,11 @@
|
||||
<mat-icon>cancel</mat-icon>
|
||||
</button>
|
||||
</span>
|
||||
<ng-template #notediting>
|
||||
} @else {
|
||||
<button mat-icon-button (click)="enableEditMode(row.uid)" matTooltip="Edit user" i18n-matTooltip="edit user action button tooltip">
|
||||
<mat-icon>edit</mat-icon>
|
||||
</button>
|
||||
</ng-template>
|
||||
}
|
||||
<button (click)="manageUser(row.uid)" mat-icon-button [disabled]="editObject && editObject.uid === row.uid" matTooltip="Manage user" i18n-matTooltip="manage user action button tooltip">
|
||||
<mat-icon>settings</mat-icon>
|
||||
</button>
|
||||
@@ -76,17 +74,14 @@
|
||||
</button>
|
||||
</mat-cell>
|
||||
</ng-container>
|
||||
|
||||
<mat-header-row *matHeaderRowDef="displayedColumns"></mat-header-row>
|
||||
<mat-row *matRowDef="let row; columns: displayedColumns;">
|
||||
</mat-row>
|
||||
</mat-table>
|
||||
|
||||
<mat-paginator #paginator [length]="length"
|
||||
[pageSize]="pageSize"
|
||||
[pageSizeOptions]="pageSizeOptions">
|
||||
</mat-paginator>
|
||||
|
||||
<button color="primary" [disabled]="!this.users" mat-raised-button (click)="openAddUserDialog()" style="float: left; top: -45px; left: 15px">
|
||||
<ng-container i18n="Add users button">Add Users</ng-container>
|
||||
</button>
|
||||
@@ -95,14 +90,14 @@
|
||||
</div>
|
||||
<button color="primary" [matMenuTriggerFor]="edit_roles_menu" class="edit-role" mat-raised-button><ng-container i18n="Edit role">Edit Role</ng-container></button>
|
||||
<mat-menu #edit_roles_menu="matMenu">
|
||||
<button (click)="openModifyRole(role)" mat-menu-item *ngFor="let role of roles">{{role.key}}</button>
|
||||
@for (role of roles; track role) {
|
||||
<button (click)="openModifyRole(role)" mat-menu-item>{{role.key}}</button>
|
||||
}
|
||||
</mat-menu>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
} @else {
|
||||
<div style="position: absolute" class="centered">
|
||||
<ng-template #loading>
|
||||
<mat-spinner></mat-spinner>
|
||||
</ng-template>
|
||||
</div>
|
||||
}
|
||||
|
||||
@@ -8,25 +8,31 @@
|
||||
</div>
|
||||
</mat-card-subtitle>
|
||||
<mat-card-title>
|
||||
<ng-container *ngIf="NOTIFICATION_PREFIX[notification.type]">
|
||||
@if (NOTIFICATION_PREFIX[notification.type]) {
|
||||
{{NOTIFICATION_PREFIX[notification.type]}}
|
||||
</ng-container>
|
||||
}
|
||||
</mat-card-title>
|
||||
</mat-card-header>
|
||||
<mat-card-content>
|
||||
<ng-container *ngIf="NOTIFICATION_SUFFIX_KEY[notification.type]">
|
||||
@if (NOTIFICATION_SUFFIX_KEY[notification.type]) {
|
||||
<div style="word-break: break-word">
|
||||
{{notification['data'][NOTIFICATION_SUFFIX_KEY[notification.type]]}}
|
||||
</div>
|
||||
</ng-container>
|
||||
}
|
||||
</mat-card-content>
|
||||
<mat-card-actions class="notification-actions" *ngIf="notification.actions?.length > 0">
|
||||
@if (notification.actions?.length > 0) {
|
||||
<mat-card-actions class="notification-actions">
|
||||
<button matTooltip="Remove" i18n-matTooltip="Remove" (click)="emitDeleteNotification(notification.uid)" mat-icon-button><mat-icon>close</mat-icon></button>
|
||||
<span *ngFor="let action of notification.actions">
|
||||
@for (action of notification.actions; track action) {
|
||||
<span>
|
||||
<button [matTooltip]="NOTIFICATION_ACTION_TO_STRING[action]" (click)="emitNotificationAction(notification, action)" mat-icon-button><mat-icon>{{NOTIFICATION_ICON[action]}}</mat-icon></button>
|
||||
</span>
|
||||
}
|
||||
</mat-card-actions>
|
||||
<span *ngIf="!notification.read" class="dot"></span>
|
||||
}
|
||||
@if (!notification.read) {
|
||||
<span class="dot"></span>
|
||||
}
|
||||
</mat-card>
|
||||
</div>
|
||||
</cdk-virtual-scroll-viewport>
|
||||
@@ -1,10 +1,16 @@
|
||||
<div *ngIf="notifications !== null && notifications.length === 0" style="text-align: center; margin: 10px;" i18n="No notifications available">No notifications available</div>
|
||||
<div *ngIf="notifications?.length > 0">
|
||||
@if (notifications !== null && notifications.length === 0) {
|
||||
<div style="text-align: center; margin: 10px;" i18n="No notifications available">No notifications available</div>
|
||||
}
|
||||
@if (notifications?.length > 0) {
|
||||
<div>
|
||||
<div class="notifications-list-parent">
|
||||
<mat-chip-listbox [value]="selectedFilters" [multiple]="true" (change)="selectedFiltersChanged($event)">
|
||||
<mat-chip-option *ngFor="let filter of notificationFilters | keyvalue: originalOrder" [value]="filter.key" [selected]="selectedFilters.includes(filter.key)" color="accent">{{filter.value.label}}</mat-chip-option>
|
||||
@for (filter of notificationFilters | keyvalue: originalOrder; track filter) {
|
||||
<mat-chip-option [value]="filter.key" [selected]="selectedFilters.includes(filter.key)" color="accent">{{filter.value.label}}</mat-chip-option>
|
||||
}
|
||||
</mat-chip-listbox>
|
||||
<app-notifications-list class="notifications-list" [style.height]="list_height" (notificationAction)="notificationAction($event)" (deleteNotification)="deleteNotification($event)" [notifications]="filtered_notifications"></app-notifications-list>
|
||||
</div>
|
||||
<button style="margin: 10px 0px 2px 10px;" *ngIf="notifications?.length > 0" color="warn" (click)="deleteAllNotifications()" mat-stroked-button>Remove all</button>
|
||||
</div>
|
||||
<button style="margin: 10px 0px 2px 10px;" color="warn" (click)="deleteAllNotifications()" mat-stroked-button>Remove all</button>
|
||||
</div>
|
||||
}
|
||||
|
||||
@@ -6,8 +6,11 @@
|
||||
</div>
|
||||
<!-- Files title -->
|
||||
<div class="col-12 order-1 col-sm-4 order-sm-2 d-flex justify-content-center">
|
||||
<h4 *ngIf="!customHeader" class="my-videos-title" i18n="My files title">My files</h4>
|
||||
<h4 *ngIf="customHeader" class="my-videos-title">{{customHeader}}</h4>
|
||||
@if (!customHeader) {
|
||||
<h4 class="my-videos-title" i18n="My files title">My files</h4>
|
||||
} @else {
|
||||
<h4 class="my-videos-title">{{customHeader}}</h4>
|
||||
}
|
||||
</div>
|
||||
<!-- Search -->
|
||||
<div class="col-12 order-3 col-sm-4 order-sm-3 d-flex justify-content-center">
|
||||
@@ -21,57 +24,77 @@
|
||||
<!-- Filters -->
|
||||
<div class="row justify-content-center">
|
||||
<mat-chip-listbox class="filter-list" [value]="selectedFilters" [multiple]="true" (change)="selectedFiltersChanged($event)">
|
||||
<mat-chip-option *ngFor="let filter of fileFilters | keyvalue: originalOrder" [value]="filter.key" [selected]="selectedFilters.includes(filter.key)" color="accent">{{filter.value.label}}</mat-chip-option>
|
||||
@for (filter of fileFilters | keyvalue: originalOrder; track filter) {
|
||||
<mat-chip-option [value]="filter.key" [selected]="selectedFilters.includes(filter.key)" color="accent">{{filter.value.label}}</mat-chip-option>
|
||||
}
|
||||
</mat-chip-listbox>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<!-- Files -->
|
||||
<div *ngIf="!selectMode" class="container" style="margin-bottom: 16px">
|
||||
@if (!selectMode) {
|
||||
<div class="container" style="margin-bottom: 16px">
|
||||
<div class="row justify-content-center">
|
||||
<!-- Real cards -->
|
||||
<ng-container *ngIf="normal_files_received && paged_data">
|
||||
<div style="display: flex; align-items: center;" *ngFor="let file of paged_data; let i = index" class="mb-2 mt-2 d-flex justify-content-center" [ngClass]="[ postsService.card_size === 'small' ? 'col-2 small-col' : '', postsService.card_size === 'medium' ? 'col-6 col-lg-4 medium-col' : '', postsService.card_size === 'large' ? 'col-12 large-col' : '' ]">
|
||||
@if (normal_files_received && paged_data) {
|
||||
@for (file of paged_data; track file; let i = $index) {
|
||||
<div style="display: flex; align-items: center;" class="mb-2 mt-2 d-flex justify-content-center" [ngClass]="[ postsService.card_size === 'small' ? 'col-2 small-col' : '', postsService.card_size === 'medium' ? 'col-6 col-lg-4 medium-col' : '', postsService.card_size === 'large' ? 'col-12 large-col' : '' ]">
|
||||
<app-unified-file-card [ngClass]="downloading_content[file.uid] ? 'blurred' : ''" [index]="i" [card_size]="postsService.card_size" [locale]="postsService.locale" (goToFile)="goToFile($event)" (goToSubscription)="goToSubscription($event)" (toggleFavorite)="toggleFavorite($event)" [file_obj]="file" [use_youtubedl_archive]="postsService.config['Downloader']['use_youtubedl_archive']" [availablePlaylists]="playlists" (addFileToPlaylist)="addFileToPlaylist($event)" [loading]="false" (deleteFile)="deleteFile($event)" [baseStreamPath]="postsService.path" [jwtString]="postsService.isLoggedIn ? this.postsService.token : ''"></app-unified-file-card>
|
||||
<mat-spinner *ngIf="downloading_content[file.uid]" class="downloading-spinner" [diameter]="32"></mat-spinner>
|
||||
@if (downloading_content[file.uid]) {
|
||||
<mat-spinner class="downloading-spinner" [diameter]="32"></mat-spinner>
|
||||
}
|
||||
</div>
|
||||
<div *ngIf="paged_data.length === 0">
|
||||
} @empty {
|
||||
<div>
|
||||
<ng-container i18n="No files found">No files found.</ng-container>
|
||||
</div>
|
||||
</ng-container>
|
||||
}
|
||||
}
|
||||
<!-- Fake cards -->
|
||||
<ng-container>
|
||||
<div *ngFor="let file of loading_files; let i = index" class="mb-2 mt-2 d-flex justify-content-center" [ngClass]="[normal_files_received ? 'hide' : '', postsService.card_size === 'small' ? 'col-2 small-col' : '', postsService.card_size === 'medium' ? 'col-6 col-lg-4 medium-col' : '', postsService.card_size === 'large' ? 'col-12 large-col' : '' ]">
|
||||
@for (file of loading_files; track file; let i = $index) {
|
||||
<div class="mb-2 mt-2 d-flex justify-content-center" [ngClass]="[normal_files_received ? 'hide' : '', postsService.card_size === 'small' ? 'col-2 small-col' : '', postsService.card_size === 'medium' ? 'col-6 col-lg-4 medium-col' : '', postsService.card_size === 'large' ? 'col-12 large-col' : '' ]">
|
||||
<app-unified-file-card [index]="i" [card_size]="postsService.card_size" [locale]="postsService.locale" [loading]="true" [theme]="postsService.theme"></app-unified-file-card>
|
||||
</div>
|
||||
}
|
||||
</ng-container>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div *ngIf="selectMode">
|
||||
} @else {
|
||||
<div>
|
||||
<!-- If selected files e.g. for creating a playlist -->
|
||||
<mat-tab-group [(selectedIndex)]="selectedIndex">
|
||||
<mat-tab label="Order" i18n-label="Order">
|
||||
<div *ngIf="selected_data.length">
|
||||
<span *ngIf="reverse_order === false" i18n="Normal order">Normal order </span>
|
||||
<span *ngIf="reverse_order === true" i18n="Reverse order">Reverse order </span>
|
||||
@if (selected_data.length) {
|
||||
<div>
|
||||
@if (reverse_order === false) {
|
||||
<span i18n="Normal order">Normal order </span>
|
||||
}
|
||||
@if (reverse_order === true) {
|
||||
<span i18n="Reverse order">Reverse order </span>
|
||||
}
|
||||
<button (click)="toggleSelectionOrder()" mat-icon-button><mat-icon>{{!reverse_order ? 'arrow_downward' : 'arrow_upward'}}</mat-icon></button>
|
||||
</div>
|
||||
|
||||
}
|
||||
<!-- Selection order -->
|
||||
<mat-button-toggle-group *ngIf="selected_data.length" class="media-list" cdkDropList (cdkDropListDropped)="drop($event)" style="width: 80%; left: 9%" vertical #group="matButtonToggleGroup">
|
||||
@if (selected_data.length) {
|
||||
<mat-button-toggle-group class="media-list" cdkDropList (cdkDropListDropped)="drop($event)" style="width: 80%; left: 9%" vertical #group="matButtonToggleGroup">
|
||||
<!-- The following for loop can be optimized but it requires a pipe https://stackoverflow.com/a/35703364/8088021 -->
|
||||
<mat-button-toggle class="media-box" cdkDrag *ngFor="let file of (reverse_order ? selected_data_objs.slice().reverse() : selected_data_objs); let i = index" [checked]="false"><div><div class="playlist-item-text">{{file.title}}</div> <button (click)="removeSelectedFile(i)" class="remove-item-button" mat-icon-button><mat-icon>cancel</mat-icon></button></div></mat-button-toggle>
|
||||
@for (file of (reverse_order ? selected_data_objs.slice().reverse() : selected_data_objs); track file; let i = $index) {
|
||||
<mat-button-toggle class="media-box" cdkDrag [checked]="false"><div><div class="playlist-item-text">{{file.title}}</div> <button (click)="removeSelectedFile(i)" class="remove-item-button" mat-icon-button><mat-icon>cancel</mat-icon></button></div></mat-button-toggle>
|
||||
}
|
||||
</mat-button-toggle-group>
|
||||
|
||||
<div style="margin-top: 20px;" *ngIf="!selected_data.length">
|
||||
} @else {
|
||||
<div style="margin-top: 20px;">
|
||||
<h4 style="text-align: center;">No files selected!</h4>
|
||||
</div>
|
||||
|
||||
}
|
||||
</mat-tab>
|
||||
<mat-tab label="Select files" i18n-label="Select files">
|
||||
<mat-selection-list *ngIf="normal_files_received" (selectionChange)="fileSelectionChanged($event)">
|
||||
<mat-list-option [selected]="selected_data.includes(file.uid)" *ngFor="let file of paged_data" [value]="file">
|
||||
@if (normal_files_received) {
|
||||
<mat-selection-list (selectionChange)="fileSelectionChanged($event)">
|
||||
@for (file of paged_data; track file) {
|
||||
<mat-list-option [selected]="selected_data.includes(file.uid)" [value]="file">
|
||||
<div class="container">
|
||||
<div class="row justify-content-center">
|
||||
<div class="col-10 select-file-title">
|
||||
@@ -81,25 +104,31 @@
|
||||
<div class="col-2">{{file.registered | date:'shortDate'}}</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</mat-list-option>
|
||||
}
|
||||
</mat-selection-list>
|
||||
|
||||
<ng-container *ngIf="!normal_files_received && loading_files && loading_files.length > 0">
|
||||
<mat-selection-list *ngIf="!normal_files_received">
|
||||
<mat-list-option *ngFor="let file of paged_data">
|
||||
}
|
||||
@if (!normal_files_received && loading_files && loading_files.length > 0) {
|
||||
@if (!normal_files_received) {
|
||||
<mat-selection-list>
|
||||
@for (file of paged_data; track file) {
|
||||
<mat-list-option>
|
||||
<content-loader class="list-ghosts" [backgroundColor]="postsService.theme.ghost_primary" [foregroundColor]="postsService.theme.ghost_secondary" viewBox="0 0 250 8"><svg:rect x="0" y="0" rx="3" ry="3" width="250" height="8" /></content-loader>
|
||||
</mat-list-option>
|
||||
}
|
||||
</mat-selection-list>
|
||||
</ng-container>
|
||||
}
|
||||
}
|
||||
</mat-tab>
|
||||
</mat-tab-group>
|
||||
</div>
|
||||
|
||||
<div style="position: relative;" *ngIf="usePaginator && selectedIndex > 0">
|
||||
}
|
||||
@if (usePaginator && selectedIndex > 0) {
|
||||
<div style="position: relative;">
|
||||
<mat-paginator class="paginator" #paginator (page)="pageChangeEvent($event)" [length]="file_count"
|
||||
[pageSize]="pageSize"
|
||||
[pageSizeOptions]="[5, 10, 25, 100, this.paged_data && this.paged_data.length > 100 ? this.paged_data.length : 250]">
|
||||
</mat-paginator>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
|
||||
@@ -1,11 +1,14 @@
|
||||
<span class="text" [ngStyle]="{'-webkit-line-clamp': !see_more_active ? line_limit : null}" [innerHTML]="text | linkify"></span>
|
||||
<span>
|
||||
<a [routerLink]="[]" (click)="toggleSeeMore()">
|
||||
<ng-container *ngIf="!see_more_active" i18n="See more">
|
||||
@if (!see_more_active) {
|
||||
<ng-container i18n="See more">
|
||||
See more.
|
||||
</ng-container>
|
||||
<ng-container *ngIf="see_more_active" i18n="See less">
|
||||
} @else {
|
||||
<ng-container i18n="See less">
|
||||
See less.
|
||||
</ng-container>
|
||||
}
|
||||
</a>
|
||||
</span>
|
||||
@@ -1 +1,3 @@
|
||||
<button *ngIf="show_skip_ad_button" (click)="skipAdButtonClicked()" mat-flat-button><ng-container i18n="Skip ad button">Skip ad</ng-container></button>
|
||||
@if (show_skip_ad_button) {
|
||||
<button (click)="skipAdButtonClicked()" mat-flat-button><ng-container i18n="Skip ad button">Skip ad</ng-container></button>
|
||||
}
|
||||
@@ -2,9 +2,11 @@
|
||||
<div style="display: inline-block;">
|
||||
<mat-form-field appearance="outline" style="width: 165px;">
|
||||
<mat-select [(ngModel)]="this.sortProperty" (selectionChange)="emitSortOptionChanged()">
|
||||
<mat-option *ngFor="let sortOption of sortProperties | keyvalue" [value]="sortOption.key">
|
||||
@for (sortOption of sortProperties | keyvalue; track sortOption) {
|
||||
<mat-option [value]="sortOption.key">
|
||||
{{sortOption['value']['label']}}
|
||||
</mat-option>
|
||||
}
|
||||
</mat-select>
|
||||
</mat-form-field>
|
||||
</div>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<h4 mat-dialog-title><ng-container i18n="Task settings">Task settings - {{task.title}}</ng-container></h4>
|
||||
|
||||
<mat-dialog-content>
|
||||
<div *ngIf="task_key === 'delete_old_files'">
|
||||
@if (task_key === 'delete_old_files') {
|
||||
<div>
|
||||
<mat-form-field color="accent">
|
||||
<mat-label i18n="Delete files older than">Delete files older than</mat-label>
|
||||
<input [(ngModel)]="new_options['threshold_days']" matInput onlyNumber required>
|
||||
@@ -14,16 +14,18 @@
|
||||
<mat-checkbox [disabled]="new_options['blacklist_files']" [(ngModel)]="new_options['blacklist_subscription_files']" i18n="Blacklist deleted subscription files" placeholder="Archive mode must be enabled" placeholder-i18n>Blacklist deleted subscription files</mat-checkbox>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
}
|
||||
<div>
|
||||
<mat-checkbox [(ngModel)]="new_options['auto_confirm']" i18n="Do not ask for confirmation">Do not ask for confirmation</mat-checkbox>
|
||||
</div>
|
||||
</mat-dialog-content>
|
||||
|
||||
<mat-dialog-actions>
|
||||
<button mat-button mat-dialog-close>
|
||||
<ng-container *ngIf="optionsChanged()" i18n="Task settings cancel button">Cancel</ng-container>
|
||||
<ng-container *ngIf="!optionsChanged()" i18n="Task settings close button">Close</ng-container>
|
||||
@if (optionsChanged()) {
|
||||
<ng-container i18n="Task settings cancel button">Cancel</ng-container>
|
||||
} @else {
|
||||
<ng-container i18n="Task settings close button">Close</ng-container>
|
||||
}
|
||||
</button>
|
||||
<button mat-button [disabled]="!optionsChanged()" (click)="saveSettings()"><ng-container i18n="Save button">Save</ng-container></button>
|
||||
</mat-dialog-actions>
|
||||
@@ -10,64 +10,74 @@
|
||||
</span>
|
||||
</mat-cell>
|
||||
</ng-container>
|
||||
|
||||
<!-- Last Ran Column -->
|
||||
<ng-container matColumnDef="last_ran">
|
||||
<mat-header-cell *matHeaderCellDef mat-sort-header> <ng-container i18n="Last ran">Last ran</ng-container> </mat-header-cell>
|
||||
<mat-cell *matCellDef="let element">
|
||||
<ng-container *ngIf="element.last_ran">{{element.last_ran*1000 | date: 'short'}}</ng-container>
|
||||
<ng-container i18n="N/A" *ngIf="!element.last_ran">N/A</ng-container>
|
||||
@if (element.last_ran) {
|
||||
{{element.last_ran*1000 | date: 'short'}}
|
||||
} @else {
|
||||
<ng-container i18n="N/A">N/A</ng-container>
|
||||
}
|
||||
</mat-cell>
|
||||
</ng-container>
|
||||
|
||||
<!-- Last Confirmed Column -->
|
||||
<ng-container matColumnDef="last_confirmed">
|
||||
<mat-header-cell *matHeaderCellDef mat-sort-header> <ng-container i18n="Last confirmed">Last confirmed</ng-container> </mat-header-cell>
|
||||
<mat-cell *matCellDef="let element">
|
||||
<ng-container *ngIf="element.last_confirmed">{{element.last_confirmed*1000 | date: 'short'}}</ng-container>
|
||||
<ng-container i18n="N/A" *ngIf="!element.last_confirmed">N/A</ng-container>
|
||||
@if (element.last_confirmed) {
|
||||
{{element.last_confirmed*1000 | date: 'short'}}
|
||||
} @else {
|
||||
<ng-container i18n="N/A">N/A</ng-container>
|
||||
}
|
||||
</mat-cell>
|
||||
</ng-container>
|
||||
|
||||
<!-- Status Column -->
|
||||
<ng-container matColumnDef="status">
|
||||
<mat-header-cell *matHeaderCellDef mat-sort-header> <ng-container i18n="Status">Status</ng-container> </mat-header-cell>
|
||||
<mat-cell *matCellDef="let element">
|
||||
<span *ngIf="element.running || element.confirming"><mat-spinner matTooltip="Busy" i18n-matTooltip="Busy" [diameter]="25"></mat-spinner></span>
|
||||
<span *ngIf="!(element.running || element.confirming) && element.schedule" style="display: flex">
|
||||
@if (element.running || element.confirming) {
|
||||
<span><mat-spinner matTooltip="Busy" i18n-matTooltip="Busy" [diameter]="25"></mat-spinner></span>
|
||||
} @else if (element.schedule) {
|
||||
<span style="display: flex">
|
||||
<ng-container i18n="Scheduled">Scheduled for</ng-container>
|
||||
{{element.next_invocation | date: 'short'}}<mat-icon style="font-size: 16px; display: inline-flex; align-items: center; padding-left: 5px; padding-bottom: 6px;" *ngIf="element.schedule.type === 'recurring'">repeat</mat-icon>
|
||||
{{element.next_invocation | date: 'short'}}
|
||||
@if (element.schedule.type === 'recurring') {
|
||||
<mat-icon style="font-size: 16px; display: inline-flex; align-items: center; padding-left: 5px; padding-bottom: 6px;">repeat</mat-icon>
|
||||
}
|
||||
</span>
|
||||
<span *ngIf="!(element.running || element.confirming) && !element.schedule">
|
||||
} @else {
|
||||
<span>
|
||||
<ng-container i18n="Not scheduled">Not scheduled</ng-container>
|
||||
</span>
|
||||
}
|
||||
</mat-cell>
|
||||
</ng-container>
|
||||
|
||||
<!-- Actions Column -->
|
||||
<ng-container matColumnDef="actions">
|
||||
<mat-header-cell *matHeaderCellDef> <ng-container i18n="Actions">Actions</ng-container> </mat-header-cell>
|
||||
<mat-cell *matCellDef="let element">
|
||||
<div class="container">
|
||||
<div class="row justify-content-start">
|
||||
<div *ngIf="element.data?.uids?.length > 0 || (!element.data?.uids && element.data)" class="col-12 mt-2" style="display: flex; justify-content: center;">
|
||||
@if (element.data?.uids?.length > 0 || (!element.data?.uids && element.data)) {
|
||||
<div class="col-12 mt-2" style="display: flex; justify-content: center;">
|
||||
<ng-container>
|
||||
<button (click)="confirmTask(element.key)" [disabled]="element.running || element.confirming" mat-stroked-button>
|
||||
<ng-container *ngIf="element.key == 'missing_files_check'">
|
||||
@switch(element.key) {
|
||||
@case ('missing_files_check') {
|
||||
<ng-container i18n="Clear missing files from DB">Clear missing files from DB:</ng-container>{{element.data.uids.length}}
|
||||
</ng-container>
|
||||
<ng-container *ngIf="element.key == 'duplicate_files_check'">
|
||||
} @case ('duplicate_files_check') {
|
||||
<ng-container i18n="Clear duplicate files from DB">Clear duplicate files from DB:</ng-container> {{element.data.uids.length}}
|
||||
</ng-container>
|
||||
<ng-container *ngIf="element.key == 'youtubedl_update_check'">
|
||||
} @case ('youtubedl_update_check') {
|
||||
<ng-container i18n="Update binary to">Update binary to:</ng-container> {{element.data}}
|
||||
</ng-container>
|
||||
<ng-container *ngIf="element.key == 'delete_old_files'">
|
||||
} @case ('delete_old_files') {
|
||||
<ng-container i18n="Delete old files">Delete old files:</ng-container> {{element.data.files_to_remove.length}}
|
||||
</ng-container>
|
||||
}
|
||||
}
|
||||
</button>
|
||||
</ng-container>
|
||||
</div>
|
||||
}
|
||||
<div class="col-3">
|
||||
<button (click)="runTask(element.key)" [disabled]="element.running || element.confirming" mat-icon-button matTooltip="Run" i18n-matTooltip="Run"><mat-icon>play_arrow</mat-icon></button>
|
||||
</div>
|
||||
@@ -77,28 +87,28 @@
|
||||
<div class="col-3">
|
||||
<button (click)="openTaskSettings(element)" mat-icon-button matTooltip="Settings" i18n-matTooltip="Settings"><mat-icon>settings</mat-icon></button>
|
||||
</div>
|
||||
<div *ngIf="element.error" class="col-3">
|
||||
@if (element.error) {
|
||||
<div class="col-3">
|
||||
<button (click)="showError(element)" mat-icon-button matTooltip="Show error" i18n-matTooltip="Show error"><mat-icon>warning</mat-icon></button>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
</mat-cell>
|
||||
</ng-container>
|
||||
|
||||
<mat-header-row *matHeaderRowDef="displayedColumns"></mat-header-row>
|
||||
<mat-row *matRowDef="let row; columns: displayedColumns;"></mat-row>
|
||||
</mat-table>
|
||||
|
||||
<mat-paginator [pageSizeOptions]="[10, 20]"
|
||||
showFirstLastButtons
|
||||
aria-label="Select page of tasks">
|
||||
</mat-paginator>
|
||||
</div>
|
||||
|
||||
<button style="margin-top: 10px; margin-left: 5px;" mat-stroked-button (click)="openRestoreDBBackupDialog()" i18n="Restore DB from backup button">Restore DB from backup</button>
|
||||
<button style="margin-top: 10px; margin-left: 5px;" mat-stroked-button (click)="resetTasks()" color="warn" i18n="Reset tasks button">Reset tasks</button>
|
||||
</div>
|
||||
|
||||
<div *ngIf="(!tasks || tasks.length === 0) && tasks_retrieved">
|
||||
<button style="margin-top: 10px; margin-left: 5px;" mat-stroked-button (click)="openRestoreDBBackupDialog()" i18n="Restore DB from backup button">Restore DB from backup</button>
|
||||
<button style="margin-top: 10px; margin-left: 5px;" mat-stroked-button (click)="resetTasks()" color="warn" i18n="Reset tasks button">Reset tasks</button>
|
||||
</div>
|
||||
@if ((!tasks || tasks.length === 0) && tasks_retrieved) {
|
||||
<div>
|
||||
<h4 style="text-align: center; margin-top: 10px;" i18n="No tasks label">No tasks available!</h4>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
@@ -1,12 +1,17 @@
|
||||
<div class="chat-container" #scrollContainer *ngIf="visible_chat">
|
||||
@if (visible_chat) {
|
||||
<div class="chat-container" #scrollContainer>
|
||||
<div style="width: 250px; text-align: center;"><strong>Twitch Chat</strong></div>
|
||||
<div #chat style="max-width: 250px" *ngFor="let chat of visible_chat; let last = last">
|
||||
@for (chat of visible_chat; track chat; let last = $last) {
|
||||
<div #chat style="max-width: 250px">
|
||||
{{chat.timestamp_str}} - <strong [style.color]="chat.user_color ? chat.user_color : null">{{chat.name}}</strong>: {{chat.message}}
|
||||
{{last ? scrollToBottom() : ''}}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<ng-container *ngIf="chat_response_received && !full_chat">
|
||||
}
|
||||
</div>
|
||||
}
|
||||
@if (chat_response_received && !full_chat) {
|
||||
<button [disabled]="downloading_chat" (click)="downloadTwitchChat()" class="download-button" mat-raised-button color="accent"><ng-container i18n="Download Twitch Chat button">Download Twitch Chat</ng-container></button>
|
||||
<mat-spinner *ngIf="downloading_chat" class="downloading-spinner" [diameter]="30"></mat-spinner>
|
||||
</ng-container>
|
||||
@if (downloading_chat) {
|
||||
<mat-spinner class="downloading-spinner" [diameter]="30"></mat-spinner>
|
||||
}
|
||||
}
|
||||
@@ -1,73 +1,103 @@
|
||||
<div (mouseenter)="onMouseOver()" (mouseleave)="onMouseOut()" (contextmenu)="onRightClick($event)" style="position: relative; width: fit-content;">
|
||||
<div *ngIf="!loading" class="download-time">
|
||||
@if (!loading) {
|
||||
<div class="download-time">
|
||||
<mat-icon class="audio-video-icon">{{(file_obj.type === 'audio' || file_obj.isAudio) ? 'audiotrack' : 'movie'}}</mat-icon>
|
||||
|
||||
<ng-container i18n="Auto-generated label" *ngIf="file_obj.auto">Auto-generated</ng-container>
|
||||
<ng-container *ngIf="!file_obj.auto">{{file_obj.registered | date:'shortDate' : undefined : locale.ngID}}</ng-container>
|
||||
@if (file_obj.auto) {
|
||||
<ng-container i18n="Auto-generated label">Auto-generated</ng-container>
|
||||
}
|
||||
@else {
|
||||
{{file_obj.registered | date:'shortDate' : undefined : locale.ngID}}
|
||||
}
|
||||
</div>
|
||||
<div *ngIf="loading" class="download-time" style="width: 75%; margin-top: 5px;"><content-loader [backgroundColor]="theme.ghost_primary" [foregroundColor]="theme.ghost_secondary" viewBox="0 0 250 30"><svg:rect x="0" y="0" rx="3" ry="3" width="250" height="30" /></content-loader></div>
|
||||
}
|
||||
@else {
|
||||
<div class="download-time" style="width: 75%; margin-top: 5px;"><content-loader [backgroundColor]="theme.ghost_primary" [foregroundColor]="theme.ghost_secondary" viewBox="0 0 250 30"><svg:rect x="0" y="0" rx="3" ry="3" width="250" height="30" /></content-loader></div>
|
||||
}
|
||||
<!-- The context menu trigger must be kept above the "more info" menu -->
|
||||
<div style="visibility: hidden; position: fixed"
|
||||
[style.left]="contextMenuPosition.x"
|
||||
[style.top]="contextMenuPosition.y"
|
||||
[matMenuTriggerFor]="context_menu">
|
||||
</div>
|
||||
<button *ngIf="!file_obj || !file_obj.auto" [disabled]="loading" [matMenuTriggerFor]="action_menu" class="menuButton" mat-icon-button><mat-icon>more_vert</mat-icon></button>
|
||||
@if (!file_obj || !file_obj.auto) {
|
||||
<button [disabled]="loading" [matMenuTriggerFor]="action_menu" class="menuButton" mat-icon-button><mat-icon>more_vert</mat-icon></button>
|
||||
}
|
||||
<mat-menu #context_menu>
|
||||
<ng-container *ngIf="!loading">
|
||||
@if (!loading) {
|
||||
<button (click)="navigateToFile($event)" mat-menu-item><mat-icon>open_in_browser</mat-icon><ng-container i18n="Open file button">Open file</ng-container></button>
|
||||
<button (click)="navigateToFile({ctrlKey: true})" mat-menu-item><mat-icon>open_in_new</mat-icon><ng-container i18n="Open file in new tab">Open file in new tab</ng-container></button>
|
||||
</ng-container>
|
||||
}
|
||||
</mat-menu>
|
||||
<mat-menu #action_menu="matMenu">
|
||||
<ng-container *ngIf="!is_playlist && !loading">
|
||||
@if (!is_playlist && !loading) {
|
||||
<button (click)="emitToggleFavorite()" mat-menu-item>
|
||||
<mat-icon>{{file_obj.favorite ? 'favorite_filled' : 'favorite_outline'}}</mat-icon>
|
||||
<ng-container *ngIf="!file_obj.favorite" i18n="Favorite button">Favorite</ng-container>
|
||||
<ng-container *ngIf="file_obj.favorite" i18n="Unfavorite button">Unfavorite</ng-container>
|
||||
@if (!file_obj.favorite) {
|
||||
<ng-container i18n="Favorite button">Favorite</ng-container>
|
||||
}
|
||||
@else {
|
||||
<ng-container i18n="Unfavorite button">Unfavorite</ng-container>
|
||||
}
|
||||
</button>
|
||||
<button (click)="openFileInfoDialog()" mat-menu-item><mat-icon>info</mat-icon><ng-container i18n="Video info button">Info</ng-container></button>
|
||||
<button (click)="navigateToSubscription()" mat-menu-item *ngIf="file_obj.sub_id"><mat-icon>{{file_obj.isAudio ? 'library_music' : 'video_library'}}</mat-icon> <ng-container i18n="Go to subscription menu item">Go to subscription</ng-container></button>
|
||||
@if (file_obj.sub_id) {
|
||||
<button (click)="navigateToSubscription()" mat-menu-item><mat-icon>{{file_obj.isAudio ? 'library_music' : 'video_library'}}</mat-icon> <ng-container i18n="Go to subscription menu item">Go to subscription</ng-container></button>
|
||||
}
|
||||
<button [disabled]="!availablePlaylists || availablePlaylists.length === 0" [matMenuTriggerFor]="addtoplaylist" mat-menu-item><mat-icon>playlist_add</mat-icon> <ng-container i18n="Add to playlist menu item">Add to playlist</ng-container></button>
|
||||
<mat-menu #addtoplaylist="matMenu">
|
||||
<ng-container *ngFor="let playlist of availablePlaylists">
|
||||
<button *ngIf="(playlist.type === 'audio') === file_obj.isAudio" [disabled]="playlist.uids?.includes(file_obj.uid)" (click)="emitAddFileToPlaylist(playlist.id)" mat-menu-item>{{playlist.name}}</button>
|
||||
</ng-container>
|
||||
@for (playlist of availablePlaylists; track playlist) {
|
||||
@if ((playlist.type === 'audio') === file_obj.isAudio) {
|
||||
<button [disabled]="playlist.uids?.includes(file_obj.uid)" (click)="emitAddFileToPlaylist(playlist.id)" mat-menu-item>{{playlist.name}}</button>
|
||||
}
|
||||
}
|
||||
</mat-menu>
|
||||
<mat-divider></mat-divider>
|
||||
<button *ngIf="file_obj.sub_id" (click)="emitDeleteFile()" mat-menu-item><mat-icon>restore</mat-icon><ng-container i18n="Delete and redownload subscription video button">Delete and redownload</ng-container></button>
|
||||
<button *ngIf="!file_obj.sub_id" (click)="emitDeleteFile()" mat-menu-item><mat-icon>delete</mat-icon><ng-container i18n="Delete video button">Delete</ng-container></button>
|
||||
<button *ngIf="file_obj.sub_id || use_youtubedl_archive" (click)="emitDeleteFile(true)" mat-menu-item><mat-icon>delete_forever</mat-icon><ng-container i18n="Delete and don't download again">Delete and don't download again</ng-container></button>
|
||||
</ng-container>
|
||||
<ng-container *ngIf="is_playlist && !loading">
|
||||
@if (file_obj.sub_id) {
|
||||
<button (click)="emitDeleteFile()" mat-menu-item><mat-icon>restore</mat-icon><ng-container i18n="Delete and redownload subscription video button">Delete and redownload</ng-container></button>
|
||||
} @else {
|
||||
<button (click)="emitDeleteFile()" mat-menu-item><mat-icon>delete</mat-icon><ng-container i18n="Delete video button">Delete</ng-container></button>
|
||||
}
|
||||
@if (file_obj.sub_id || use_youtubedl_archive) {
|
||||
<button (click)="emitDeleteFile(true)" mat-menu-item><mat-icon>delete_forever</mat-icon><ng-container i18n="Delete and don't download again">Delete and don't download again</ng-container></button>
|
||||
}
|
||||
}
|
||||
@if (is_playlist && !loading) {
|
||||
<button (click)="emitEditPlaylist()" mat-menu-item><mat-icon>edit</mat-icon><ng-container i18n="Playlist edit button">Edit</ng-container></button>
|
||||
<mat-divider></mat-divider>
|
||||
<button (click)="emitDeleteFile()" mat-menu-item><mat-icon>delete_forever</mat-icon><ng-container i18n="Delete playlist">Delete</ng-container></button>
|
||||
</ng-container>
|
||||
<ng-container *ngIf="loading">
|
||||
} @else if (loading) {
|
||||
<button mat-menu-item>Placeholder</button>
|
||||
</ng-container>
|
||||
}
|
||||
</mat-menu>
|
||||
<mat-card [matTooltip]="null" (click)="navigateToFile($event)" matRipple class="file-mat-card" [ngClass]="{'small-mat-card': card_size === 'small', 'file-mat-card': card_size === 'medium', 'large-mat-card': card_size === 'large', 'mat-elevation-z4': !elevated, 'mat-elevation-z8': elevated}">
|
||||
<div style="padding:5px">
|
||||
<div *ngIf="!loading && file_obj.thumbnailURL" class="img-div">
|
||||
@if (!loading && file_obj.thumbnailURL) {
|
||||
<div class="img-div">
|
||||
<div [ngClass]="{'image-small': card_size === 'small', 'image': card_size === 'medium', 'image-large': card_size === 'large'}" style="position: relative">
|
||||
<img *ngIf="!hide_image || is_playlist || (file_obj.type === 'audio' || file_obj.isAudio)" [ngClass]="{'image-small': card_size === 'small', 'image': card_size === 'medium', 'image-large': card_size === 'large'}" [src]="file_obj.thumbnailPath ? thumbnailBlobURL : file_obj.thumbnailURL" alt="Thumbnail">
|
||||
<video *ngIf="elevated && !is_playlist && !(file_obj.type === 'audio' || file_obj.isAudio)" autoplay loop muted [muted]="true" [ngClass]="{'video-small': card_size === 'small', 'video': card_size === 'medium', 'video-large': card_size === 'large'}" [src]="streamURL">
|
||||
@if (!hide_image || is_playlist || (file_obj.type === 'audio' || file_obj.isAudio)) {
|
||||
<img [ngClass]="{'image-small': card_size === 'small', 'image': card_size === 'medium', 'image-large': card_size === 'large'}" [src]="file_obj.thumbnailPath ? thumbnailBlobURL : file_obj.thumbnailURL" alt="Thumbnail">
|
||||
}
|
||||
@if (elevated && !is_playlist && !(file_obj.type === 'audio' || file_obj.isAudio)) {
|
||||
<video autoplay loop muted [muted]="true" [ngClass]="{'video-small': card_size === 'small', 'video': card_size === 'medium', 'video-large': card_size === 'large'}" [src]="streamURL">
|
||||
</video>
|
||||
}
|
||||
<div class="duration-time">
|
||||
{{file_length}}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<div *ngIf="loading" class="img-div">
|
||||
}
|
||||
@if (loading) {
|
||||
<div class="img-div">
|
||||
<content-loader [backgroundColor]="theme.ghost_primary" [foregroundColor]="theme.ghost_secondary" viewBox="0 0 100 55"><svg:rect x="0" y="0" rx="0" ry="0" width="100" height="55" /></content-loader>
|
||||
</div>
|
||||
|
||||
<span *ngIf="!loading" [ngClass]="{'max-two-lines': card_size !== 'small', 'max-one-line': card_size === 'small' }">{{card_size === 'large' && file_obj.uploader ? file_obj.uploader + ' - ' : ''}}<strong>{{!is_playlist ? file_obj.title : file_obj.name}}</strong></span>
|
||||
<span *ngIf="loading" class="title-loading"><content-loader [backgroundColor]="theme.ghost_primary" [foregroundColor]="theme.ghost_secondary" viewBox="0 0 250 30"><svg:rect x="0" y="0" rx="3" ry="3" width="250" height="30" /></content-loader></span>
|
||||
} @else {
|
||||
<span [ngClass]="{'max-two-lines': card_size !== 'small', 'max-one-line': card_size === 'small' }">{{card_size === 'large' && file_obj.uploader ? file_obj.uploader + ' - ' : ''}}<strong>{{!is_playlist ? file_obj.title : file_obj.name}}</strong></span>
|
||||
}
|
||||
@if (loading) {
|
||||
<span class="title-loading"><content-loader [backgroundColor]="theme.ghost_primary" [foregroundColor]="theme.ghost_secondary" viewBox="0 0 250 30"><svg:rect x="0" y="0" rx="3" ry="3" width="250" height="30" /></content-loader></span>
|
||||
}
|
||||
</div>
|
||||
</mat-card>
|
||||
</div>
|
||||
|
||||
@@ -1,9 +1,12 @@
|
||||
<h4 mat-dialog-title *ngIf="create_mode" ><ng-container i18n="Create a playlist dialog title">Create a playlist</ng-container></h4>
|
||||
<h4 mat-dialog-title *ngIf="!create_mode"><ng-container i18n="Modify playlist dialog title">Modify playlist</ng-container></h4>
|
||||
|
||||
@if (create_mode) {
|
||||
<h4 mat-dialog-title ><ng-container i18n="Create a playlist dialog title">Create a playlist</ng-container></h4>
|
||||
} @else {
|
||||
<h4 mat-dialog-title><ng-container i18n="Modify playlist dialog title">Modify playlist</ng-container></h4>
|
||||
}
|
||||
<mat-dialog-content style="max-height: 85vh;">
|
||||
<form>
|
||||
<div *ngIf="create_mode || playlist">
|
||||
@if (create_mode || playlist) {
|
||||
<div>
|
||||
<div>
|
||||
<mat-form-field color="accent">
|
||||
<mat-label i18n="Playlist name">Name</mat-label>
|
||||
@@ -12,17 +15,21 @@
|
||||
</div>
|
||||
<app-recent-videos [selectMode]="true" [defaultSelected]="preselected_files" [customHeader]="'Select files'" (fileSelectionEmitter)="fileSelectionChanged($event)" [selectedIndex]="create_mode ? 1 : 0"></app-recent-videos>
|
||||
</div>
|
||||
}
|
||||
</form>
|
||||
</mat-dialog-content>
|
||||
|
||||
<div class="spacer"></div>
|
||||
|
||||
<mat-dialog-actions>
|
||||
<button *ngIf="create_mode" (click)="createPlaylist()" [disabled]="!name || !filesSelect.value || filesSelect.value.length === 0" color="primary" style="float: right" mat-button>
|
||||
@if (create_mode) {
|
||||
<button (click)="createPlaylist()" [disabled]="!name || !filesSelect.value || filesSelect.value.length === 0" color="primary" style="float: right" mat-button>
|
||||
<ng-container i18n="Create button">Create</ng-container>
|
||||
</button>
|
||||
<button *ngIf="!create_mode" (click)="updatePlaylist()" [disabled]="!name || !playlistChanged()" color="primary" style="float: right" mat-button>
|
||||
} @else {
|
||||
<button (click)="updatePlaylist()" [disabled]="!name || !playlistChanged()" color="primary" style="float: right" mat-button>
|
||||
<ng-container i18n="Save button">Save</ng-container>
|
||||
</button>
|
||||
<div *ngIf="create_in_progress" style="margin-left: 10px"><mat-spinner [diameter]="25"></mat-spinner></div>
|
||||
}
|
||||
@if (create_in_progress) {
|
||||
<div style="margin-left: 10px"><mat-spinner [diameter]="25"></mat-spinner></div>
|
||||
}
|
||||
</mat-dialog-actions>
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
<h4 style="position: relative" mat-dialog-title><ng-container i18n="About dialog title">About YoutubeDL-Material</ng-container>
|
||||
<span class="logo-image">
|
||||
<span class="logo-image">
|
||||
<a [href]="projectLink" target="_blank">
|
||||
<img style="width: 32px;" src="assets/images/GitHub-64px.png">
|
||||
</a>
|
||||
<img style="width: 32px; margin-left: 15px;" src="assets/images/logo_128px.png">
|
||||
</span>
|
||||
</span>
|
||||
</h4>
|
||||
<mat-dialog-content>
|
||||
<div style="margin-bottom: 5px;">
|
||||
@@ -17,17 +17,25 @@
|
||||
<mat-divider></mat-divider>
|
||||
<h5 style="margin-top: 10px;">Installation details:</h5>
|
||||
<p>
|
||||
<ng-container i18n="Version label">Installed version:</ng-container> {{current_version_tag}} - <span style="display: inline-block" *ngIf="checking_for_updates"><mat-spinner class="version-spinner" [diameter]="22"></mat-spinner> <ng-container i18n="Checking for updates text">Checking for updates...</ng-container></span>
|
||||
<mat-icon *ngIf="!checking_for_updates" class="version-checked-icon">done</mat-icon> <ng-container *ngIf="!checking_for_updates && latestGithubRelease['tag_name'] !== current_version_tag"><a [href]="latestUpdateLink" target="_blank"><ng-container i18n="View latest update">Update available</ng-container> - {{latestGithubRelease['tag_name']}}</a>. <ng-container i18n="Update through settings menu hint">You can update from the settings menu.</ng-container></ng-container>
|
||||
<span *ngIf="!checking_for_updates && latestGithubRelease['tag_name'] === current_version_tag">You are up to date.</span>
|
||||
<ng-container i18n="Version label">Installed version:</ng-container> {{current_version_tag}} -
|
||||
@if (checking_for_updates) {
|
||||
<span style="display: inline-block"><mat-spinner class="version-spinner" [diameter]="22"></mat-spinner> <ng-container i18n="Checking for updates text">Checking for updates...</ng-container></span>
|
||||
} @else {
|
||||
<mat-icon class="version-checked-icon">done</mat-icon>
|
||||
@if (latestGithubRelease['tag_name'] !== current_version_tag) {
|
||||
<a [href]="latestUpdateLink" target="_blank"><ng-container i18n="View latest update">Update available</ng-container> - {{latestGithubRelease['tag_name']}}</a>. <ng-container i18n="Update through settings menu hint">You can update from the settings menu.</ng-container>
|
||||
} @else {
|
||||
<span>You are up to date.</span>
|
||||
}
|
||||
}
|
||||
</p>
|
||||
<p>
|
||||
<ng-container i18n="Installation type">Installation type:</ng-container> {{postsService.version_info.type}}
|
||||
<br>
|
||||
<ng-container *ngIf="postsService.version_info.type === 'docker'">
|
||||
@if (postsService.version_info.type === 'docker') {
|
||||
<ng-container i18n="Docker tag">Docker tag:</ng-container> {{postsService.version_info.tag}}
|
||||
<br>
|
||||
</ng-container>
|
||||
}
|
||||
<ng-container i18n="Commit hash">Commit hash:</ng-container> {{postsService.version_info.commit}}
|
||||
<br>
|
||||
<ng-container i18n="Build date">Build date:</ng-container> {{postsService.version_info.date}}
|
||||
@@ -36,8 +44,8 @@
|
||||
<ng-container i18n="About bug prefix">Found a bug or have a suggestion?</ng-container> <a [href]="issuesLink" target="_blank"><ng-container i18n="About bug click here">Click here</ng-container></a> <ng-container i18n="About bug suffix">to create an issue!</ng-container>
|
||||
</p>
|
||||
</div>
|
||||
</mat-dialog-content>
|
||||
|
||||
<mat-dialog-actions>
|
||||
</mat-dialog-content>
|
||||
<mat-dialog-actions>
|
||||
<button style="margin-bottom: 5px;" mat-stroked-button mat-dialog-close><ng-container i18n="Close">Close</ng-container></button>
|
||||
</mat-dialog-actions>
|
||||
</mat-dialog-actions>
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
<h4 i18n="Modify args title" mat-dialog-title>Modify youtube-dl args</h4>
|
||||
|
||||
<mat-dialog-content>
|
||||
<div class="container">
|
||||
<div class="row">
|
||||
@@ -10,23 +9,28 @@
|
||||
<mat-chip-grid class="example-chip" #chipList aria-label="Args array" cdkDropList cdkDropListDisabled
|
||||
cdkDropListOrientation="horizontal"
|
||||
(cdkDropListDropped)="drop($event)">
|
||||
<mat-chip-row [matTooltip]="argsByKey[arg] ? argsByKey[arg]['description'] : null" *ngFor="let arg of args_array; let i = index;" [removable]="removable" (removed)="remove(i)" cdkDrag>
|
||||
@for (arg of args_array; track arg; let i = $index) {
|
||||
<mat-chip-row [matTooltip]="argsByKey[arg] ? argsByKey[arg]['description'] : null" [removable]="removable" (removed)="remove(i)" cdkDrag>
|
||||
{{arg}}
|
||||
<mat-icon matChipRemove *ngIf="removable">cancel</mat-icon>
|
||||
@if (removable) {
|
||||
<mat-icon matChipRemove>cancel</mat-icon>
|
||||
}
|
||||
</mat-chip-row>
|
||||
}
|
||||
</mat-chip-grid>
|
||||
<mat-form-field style="width: 100%" color="accent">
|
||||
|
||||
<input #chipper style="width: 100%;" [formControl]="chipCtrl" matInput [matAutocomplete]="autochip" [matChipInputFor]="chipList"
|
||||
[matChipInputSeparatorKeyCodes]="separatorKeysCodes"
|
||||
[matChipInputAddOnBlur]="addOnBlur"
|
||||
(matChipInputTokenEnd)="add($event)">
|
||||
</mat-form-field>
|
||||
<mat-autocomplete #autochip="matAutocomplete">
|
||||
<mat-option *ngFor="let arg of filteredChipOptions | async" [value]="arg.key">
|
||||
@for (arg of filteredChipOptions | async; track arg) {
|
||||
<mat-option [value]="arg.key">
|
||||
<span [innerHTML]="arg.key | highlight : chipCtrl.value"></span>
|
||||
<button class="info-autocomplete-icon" [matTooltip]="arg.description" mat-icon-button><mat-icon>info</mat-icon></button>
|
||||
</mat-option>
|
||||
}
|
||||
</mat-autocomplete>
|
||||
</mat-card-content>
|
||||
</mat-card>
|
||||
@@ -42,37 +46,38 @@
|
||||
<input matInput [matAutocomplete]="auto" [formControl]="stateCtrl">
|
||||
</mat-form-field>
|
||||
<mat-autocomplete #auto="matAutocomplete">
|
||||
<mat-option *ngFor="let arg of filteredOptions | async" [value]="arg.key">
|
||||
@for (arg of filteredOptions | async; track arg) {
|
||||
<mat-option [value]="arg.key">
|
||||
<span [innerHTML]="arg.key | highlight : stateCtrl.value"></span>
|
||||
<button class="info-autocomplete-icon" [matTooltip]="arg.description" mat-icon-button><mat-icon>info</mat-icon></button>
|
||||
</mat-option>
|
||||
}
|
||||
</mat-autocomplete>
|
||||
|
||||
<div>
|
||||
<mat-menu #argsByCategoryMenu="matMenu">
|
||||
<ng-container *ngFor="let argsInCategory of argsByCategory | keyvalue">
|
||||
@for (argsInCategory of argsByCategory | keyvalue; track argsInCategory) {
|
||||
<button mat-menu-item [matMenuTriggerFor]="subMenu">{{argsInfo[argsInCategory.key].label}}</button>
|
||||
<mat-menu #subMenu="matMenu">
|
||||
<button mat-menu-item *ngFor="let arg of argsInCategory.value" (click)="setFirstArg(arg.key)"><div style="display: inline-block;">{{arg.key}}</div> <div class="info-menu-icon"><mat-icon [matTooltip]="arg.description">info</mat-icon></div></button>
|
||||
@for (arg of argsInCategory.value; track arg) {
|
||||
<button mat-menu-item (click)="setFirstArg(arg.key)"><div style="display: inline-block;">{{arg.key}}</div> <div class="info-menu-icon"><mat-icon [matTooltip]="arg.description">info</mat-icon></div></button>
|
||||
}
|
||||
</mat-menu>
|
||||
</ng-container>
|
||||
|
||||
}
|
||||
</mat-menu>
|
||||
|
||||
|
||||
|
||||
<button style="margin-bottom: 15px" mat-stroked-button [matMenuTriggerFor]="argsByCategoryMenu"><ng-container i18n="Search args by category button">Search by category</ng-container></button>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<mat-checkbox color="accent" [ngModelOptions]="{standalone: true}" [(ngModel)]="secondArgEnabled"><ng-container i18n="Use arg value checkbox">Use arg value</ng-container></mat-checkbox>
|
||||
</div>
|
||||
<div *ngIf="secondArgEnabled">
|
||||
@if (secondArgEnabled) {
|
||||
<div>
|
||||
<mat-form-field style="width: 75%" color="accent">
|
||||
<mat-label i18n="Arg value">Arg value</mat-label>
|
||||
<input [ngModelOptions]="{standalone: true}" matInput [disabled]="!secondArgEnabled" [(ngModel)]="secondArg">
|
||||
</mat-form-field>
|
||||
</div>
|
||||
}
|
||||
</form>
|
||||
<div>
|
||||
<button (click)="addArg()" [disabled]="!canAddArg()" mat-stroked-button color="accent"><ng-container i18n="Search args by category button">Add arg</ng-container></button>
|
||||
@@ -82,11 +87,8 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
</mat-dialog-content>
|
||||
|
||||
<mat-dialog-actions>
|
||||
</mat-dialog-content>
|
||||
<mat-dialog-actions>
|
||||
<button mat-button [mat-dialog-close]="null"><ng-container i18n="Arg modifier cancel button">Cancel</ng-container></button>
|
||||
<button mat-button color="accent" [mat-dialog-close]="modified_args"><ng-container i18n="Arg modifier modify button">Modify</ng-container></button>
|
||||
</mat-dialog-actions>
|
||||
</mat-dialog-actions>
|
||||
@@ -2,24 +2,27 @@
|
||||
<mat-dialog-content>
|
||||
<div style="margin-bottom: 10px;">
|
||||
<!-- We can support text dialogs or dialogs where users must select items from a list -->
|
||||
<ng-container *ngIf="dialogType === 'text'">
|
||||
@if (dialogType === 'text') {
|
||||
{{dialogText}}
|
||||
</ng-container>
|
||||
<ng-container *ngIf="dialogType === 'selection_list'">
|
||||
} @else if (dialogType === 'selection_list') {
|
||||
<mat-selection-list [(ngModel)]="selected_items">
|
||||
<mat-list-option *ngFor="let item of list" [value]="item.key">
|
||||
@for (item of list; track item) {
|
||||
<mat-list-option [value]="item.key">
|
||||
{{item.title}}
|
||||
</mat-list-option>
|
||||
}
|
||||
</mat-selection-list>
|
||||
</ng-container>
|
||||
}
|
||||
</div>
|
||||
</mat-dialog-content>
|
||||
<mat-dialog-actions>
|
||||
<!-- The mat-dialog-close directive optionally accepts a value as a result for the dialog. -->
|
||||
<button [disabled]="dialogType === 'selection_list' && selected_items.length === 0" [color]="warnSubmitColor ? 'warn' : 'primary'" mat-flat-button type="submit" (click)="confirmClicked()">{{submitText}}</button>
|
||||
<div class="mat-spinner" *ngIf="submitClicked">
|
||||
@if (submitClicked) {
|
||||
<div class="mat-spinner">
|
||||
<mat-spinner [diameter]="25"></mat-spinner>
|
||||
</div>
|
||||
}
|
||||
<span class="spacer"></span>
|
||||
<button style="float: right;" mat-stroked-button mat-dialog-close>
|
||||
{{cancelText}}
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
<h4 mat-dialog-title i18n="Cookies uploader dialog title">Upload new cookies</h4>
|
||||
|
||||
<mat-dialog-content>
|
||||
<div>
|
||||
<div class="center">
|
||||
@@ -22,19 +21,24 @@
|
||||
<div style="margin-top: 10px;">
|
||||
<table class="table">
|
||||
<tbody class="upload-name-style">
|
||||
<tr *ngFor="let item of files; let i=index">
|
||||
@for (item of files; track item; let i = $index) {
|
||||
<tr>
|
||||
<td style="vertical-align: middle;">
|
||||
<strong>{{ item.relativePath }}</strong>
|
||||
</td>
|
||||
<td>
|
||||
<button [disabled]="uploading || uploaded" (click)="uploadFile()" style="float: right" matTooltip="Upload" i18n-matTooltip="Upload" mat-mini-fab><mat-icon>publish</mat-icon><mat-spinner *ngIf="uploading" class="spinner" [diameter]="38"></mat-spinner></button>
|
||||
<button [disabled]="uploading || uploaded" (click)="uploadFile()" style="float: right" matTooltip="Upload" i18n-matTooltip="Upload" mat-mini-fab><mat-icon>publish</mat-icon>
|
||||
@if (uploading) {
|
||||
<mat-spinner class="spinner" [diameter]="38"></mat-spinner>
|
||||
}
|
||||
</button>
|
||||
</td>
|
||||
</tr>
|
||||
}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</mat-dialog-content>
|
||||
|
||||
<mat-dialog-actions><button style="margin-bottom: 5px;" mat-dialog-close mat-stroked-button><ng-container i18n="Close">Close</ng-container></button></mat-dialog-actions>
|
||||
@@ -1,48 +1,47 @@
|
||||
<h4 mat-dialog-title><ng-container i18n="Editing category dialog title">Editing category</ng-container> {{category['name']}}</h4>
|
||||
|
||||
<mat-dialog-content style="max-height: 50vh">
|
||||
<mat-form-field style="width: 250px; margin-bottom: 5px;">
|
||||
<mat-label i18n="Category name">Name</mat-label>
|
||||
<input matInput [(ngModel)]="category['name']" required>
|
||||
</mat-form-field>
|
||||
|
||||
<mat-divider></mat-divider>
|
||||
|
||||
<h6 style="margin-top: 20px;" i18n="Rules">Rules</h6>
|
||||
|
||||
<mat-list>
|
||||
<mat-list-item *ngFor="let rule of category['rules']; let i = index">
|
||||
<mat-form-field [style.visibility]="i === 0 ? 'hidden' : null" class="operator-select">
|
||||
<mat-select [disabled]="i === 0" [(ngModel)]="rule['preceding_operator']">
|
||||
@for (rule of category['rules']; track rule) {
|
||||
<mat-list-item>
|
||||
<mat-form-field [style.visibility]="$index === 0 ? 'hidden' : null" class="operator-select">
|
||||
<mat-select [disabled]="$index === 0" [(ngModel)]="rule['preceding_operator']">
|
||||
<mat-option value="or">OR</mat-option>
|
||||
<mat-option value="and">AND</mat-option>
|
||||
</mat-select>
|
||||
</mat-form-field>
|
||||
<mat-form-field class="property-select">
|
||||
<mat-select [(ngModel)]="rule['property']">
|
||||
<mat-option *ngFor="let propertyOption of propertyOptions" [value]="propertyOption.value">{{propertyOption.label}}</mat-option>
|
||||
@for (propertyOption of propertyOptions; track propertyOption) {
|
||||
<mat-option [value]="propertyOption.value">{{propertyOption.label}}</mat-option>
|
||||
}
|
||||
</mat-select>
|
||||
</mat-form-field>
|
||||
<mat-form-field class="comparator-select">
|
||||
<mat-select [(ngModel)]="rule['comparator']">
|
||||
<mat-option *ngFor="let comparatorOption of comparatorOptions" [value]="comparatorOption.value">{{comparatorOption.label}}</mat-option>
|
||||
@for (comparatorOption of comparatorOptions; track comparatorOption) {
|
||||
<mat-option [value]="comparatorOption.value">{{comparatorOption.label}}</mat-option>
|
||||
}
|
||||
</mat-select>
|
||||
</mat-form-field>
|
||||
<mat-form-field class="value-input">
|
||||
<input matInput [(ngModel)]="rule['value']">
|
||||
</mat-form-field>
|
||||
<span class="rule-buttons">
|
||||
<button [disabled]="i === category['rules'].length-1" (click)="swapRules(i, i+1)" mat-icon-button><mat-icon>arrow_downward</mat-icon></button>
|
||||
<button [disabled]="i === 0" (click)="swapRules(i, i-1)" mat-icon-button><mat-icon>arrow_upward</mat-icon></button>
|
||||
<button (click)="removeRule(i)" mat-icon-button><mat-icon>cancel</mat-icon></button>
|
||||
<button [disabled]="$index === category['rules'].length-1" (click)="swapRules($index, $index+1)" mat-icon-button><mat-icon>arrow_downward</mat-icon></button>
|
||||
<button [disabled]="$index === 0" (click)="swapRules($index, $index-1)" mat-icon-button><mat-icon>arrow_upward</mat-icon></button>
|
||||
<button (click)="removeRule($index)" mat-icon-button><mat-icon>cancel</mat-icon></button>
|
||||
</span>
|
||||
</mat-list-item>
|
||||
}
|
||||
</mat-list>
|
||||
|
||||
<button style="margin-bottom: 8px;" mat-icon-button (click)="addNewRule()" matTooltip="Add new rule" i18n-matTooltip="Add new rule tooltip"><mat-icon>add</mat-icon></button>
|
||||
|
||||
<mat-divider></mat-divider>
|
||||
|
||||
<mat-form-field style="width: 250px; margin-top: 10px;">
|
||||
<mat-label i18n="Custom file output">Custom file output</mat-label>
|
||||
<input matInput [(ngModel)]="category['custom_output']">
|
||||
@@ -52,13 +51,13 @@
|
||||
<ng-container i18n="Custom Output input hint">Path is relative to the config download path. Don't include extension.</ng-container>
|
||||
</mat-hint>
|
||||
</mat-form-field>
|
||||
</mat-dialog-content>
|
||||
|
||||
<mat-dialog-actions>
|
||||
</mat-dialog-content>
|
||||
<mat-dialog-actions>
|
||||
<button mat-button mat-dialog-close><ng-container i18n="Cancel">Cancel</ng-container></button>
|
||||
|
||||
<button mat-button [disabled]="categoryChanged()" type="submit" (click)="saveClicked()"><ng-container i18n="Save button">Save</ng-container></button>
|
||||
<div class="mat-spinner" *ngIf="updating">
|
||||
@if (updating) {
|
||||
<div class="mat-spinner">
|
||||
<mat-spinner [diameter]="25"></mat-spinner>
|
||||
</div>
|
||||
</mat-dialog-actions>
|
||||
}
|
||||
</mat-dialog-actions>
|
||||
@@ -1,5 +1,8 @@
|
||||
<h4 mat-dialog-title><ng-container i18n="Edit subscription dialog title prefix">Editing</ng-container> {{sub.name}} <ng-container *ngIf="sub.paused" i18n="Paused suffix">(Paused)</ng-container></h4>
|
||||
|
||||
<h4 mat-dialog-title><ng-container i18n="Edit subscription dialog title prefix">Editing</ng-container> {{sub.name}}
|
||||
@if (sub.paused) {
|
||||
<ng-container i18n="Paused suffix">(Paused)</ng-container>
|
||||
}
|
||||
</h4>
|
||||
<mat-dialog-content>
|
||||
<div class="container-fluid">
|
||||
<div class="row">
|
||||
@@ -9,19 +12,23 @@
|
||||
<div class="col-12 mt-3">
|
||||
<mat-checkbox (change)="downloadAllToggled()" [(ngModel)]="download_all"><ng-container i18n="Download all uploads subscription setting">Download all uploads</ng-container></mat-checkbox>
|
||||
</div>
|
||||
<div class="col-12" *ngIf="editor_initialized">
|
||||
@if (editor_initialized) {
|
||||
<div class="col-12">
|
||||
<ng-container i18n="Download time range prefix">Download videos uploaded in the last</ng-container>
|
||||
<mat-form-field color="accent" class="amount-select">
|
||||
<input type="number" matInput [(ngModel)]="timerange_amount" (ngModelChange)="timerangeChanged($event, false)" [disabled]="download_all">
|
||||
</mat-form-field>
|
||||
<mat-form-field class="unit-select">
|
||||
<mat-select color="accent" [(ngModel)]="timerange_unit" (ngModelChange)="timerangeChanged($event, true)" [disabled]="download_all">
|
||||
<mat-option *ngFor="let time_unit of time_units" [value]="time_unit + (timerange_amount === 1 ? '' : 's')">
|
||||
@for (time_unit of time_units; track time_unit) {
|
||||
<mat-option [value]="time_unit + (timerange_amount === 1 ? '' : 's')">
|
||||
{{time_unit + (timerange_amount === 1 ? '' : 's')}}
|
||||
</mat-option>
|
||||
}
|
||||
</mat-select>
|
||||
</mat-form-field>
|
||||
</div>
|
||||
}
|
||||
<div class="col-12">
|
||||
<div>
|
||||
<mat-checkbox [disabled]="true" [(ngModel)]="audioOnlyMode"><ng-container i18n="Streaming-only mode">Audio-only mode</ng-container></mat-checkbox>
|
||||
@@ -31,7 +38,9 @@
|
||||
<mat-form-field>
|
||||
<mat-label i18n="Max quality">Max quality</mat-label>
|
||||
<mat-select [disabled]="new_sub.type === 'audio'" [(ngModel)]="new_sub.maxQuality">
|
||||
<mat-option *ngFor="let available_quality of available_qualities" [value]="available_quality['value']">{{available_quality['label']}}</mat-option>
|
||||
@for (available_quality of available_qualities; track available_quality) {
|
||||
<mat-option [value]="available_quality['value']">{{available_quality['label']}}</mat-option>
|
||||
}
|
||||
</mat-select>
|
||||
</mat-form-field>
|
||||
</div>
|
||||
@@ -58,13 +67,14 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</mat-dialog-content>
|
||||
|
||||
<mat-dialog-actions>
|
||||
</mat-dialog-content>
|
||||
<mat-dialog-actions>
|
||||
<button mat-button mat-dialog-close><ng-container i18n="Subscribe cancel button">Cancel</ng-container></button>
|
||||
<!-- The mat-dialog-close directive optionally accepts a value as a result for the dialog. -->
|
||||
<button mat-button [disabled]="updating || !subChanged()" type="submit" (click)="saveClicked()"><ng-container i18n="Save button">Save</ng-container></button>
|
||||
<div class="mat-spinner" *ngIf="updating">
|
||||
@if (updating) {
|
||||
<div class="mat-spinner">
|
||||
<mat-spinner [diameter]="25"></mat-spinner>
|
||||
</div>
|
||||
</mat-dialog-actions>
|
||||
}
|
||||
</mat-dialog-actions>
|
||||
@@ -1,5 +1,4 @@
|
||||
<h4 mat-dialog-title><ng-container i18n="Generate RSS URL">Generate RSS URL</ng-container></h4>
|
||||
|
||||
<mat-dialog-content>
|
||||
<div class="container-fluid">
|
||||
<div class="row justify-content-center">
|
||||
@@ -25,7 +24,9 @@
|
||||
<mat-label><ng-container i18n="User">User</ng-container></mat-label>
|
||||
<mat-select color="accent" [(ngModel)]="userFilter" (selectionChange)="rebuildURL()" [disabled]="!usersList">
|
||||
<mat-option [value]="''"><ng-container i18n="None">None</ng-container></mat-option>
|
||||
<mat-option *ngFor="let user of usersList" [value]="user.uid"><ng-container>{{user.name}}</ng-container></mat-option>
|
||||
@for (user of usersList; track user) {
|
||||
<mat-option [value]="user.uid"><ng-container>{{user.name}}</ng-container></mat-option>
|
||||
}
|
||||
</mat-select>
|
||||
</mat-form-field>
|
||||
</div>
|
||||
@@ -34,7 +35,9 @@
|
||||
<mat-label><ng-container i18n="Subscription">Subscription</ng-container></mat-label>
|
||||
<mat-select color="accent" [(ngModel)]="subscriptionFilter" (selectionChange)="rebuildURL()">
|
||||
<mat-option [value]="''"><ng-container i18n="None">None</ng-container></mat-option>
|
||||
<mat-option *ngFor="let sub of postsService.subscriptions" [value]="sub.id"><ng-container>{{sub.name}}</ng-container></mat-option>
|
||||
@for (sub of postsService.subscriptions; track sub) {
|
||||
<mat-option [value]="sub.id"><ng-container>{{sub.name}}</ng-container></mat-option>
|
||||
}
|
||||
</mat-select>
|
||||
</mat-form-field>
|
||||
</div>
|
||||
@@ -52,7 +55,6 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<mat-form-field style="width: 100%;">
|
||||
<mat-label i18n="URL">URL</mat-label>
|
||||
<input readonly [(ngModel)]="url" matInput>
|
||||
@@ -61,7 +63,6 @@
|
||||
</button>
|
||||
</mat-form-field>
|
||||
</mat-dialog-content>
|
||||
|
||||
<mat-dialog-actions>
|
||||
<button mat-button mat-dialog-close><ng-container i18n="Close">Close</ng-container></button>
|
||||
</mat-dialog-actions>
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
<h4 mat-dialog-title><ng-container i18n="Restore DB from backup">Restore DB from backup</ng-container></h4>
|
||||
|
||||
<mat-dialog-content>
|
||||
<mat-selection-list [multiple]="false" [(ngModel)]="selected_backup">
|
||||
<mat-list-option *ngFor="let db_backup of db_backups" [value]="db_backup.name" [matTooltip]="db_backup.name">
|
||||
@for (db_backup of db_backups; track db_backup) {
|
||||
<mat-list-option [value]="db_backup.name" [matTooltip]="db_backup.name">
|
||||
<div class="container-fluid">
|
||||
<div class="row">
|
||||
<div class="col-4">
|
||||
@@ -17,13 +17,15 @@
|
||||
</div>
|
||||
</div>
|
||||
</mat-list-option>
|
||||
}
|
||||
</mat-selection-list>
|
||||
</mat-dialog-content>
|
||||
|
||||
<mat-dialog-actions>
|
||||
<button mat-button mat-dialog-close><ng-container i18n="Restore DB cancel button">Cancel</ng-container></button>
|
||||
<button mat-button [disabled]="restoring" (click)="restoreClicked()" [disabled]="!selected_backup || selected_backup.length !== 1"><ng-container i18n="Restore button">Restore</ng-container></button>
|
||||
<div class="mat-spinner" *ngIf="restoring">
|
||||
@if (restoring) {
|
||||
<div class="mat-spinner">
|
||||
<mat-spinner [diameter]="25"></mat-spinner>
|
||||
</div>
|
||||
}
|
||||
</mat-dialog-actions>
|
||||
@@ -1,5 +1,4 @@
|
||||
<h4 mat-dialog-title><ng-container i18n="Create admin account dialog title">Create admin account</ng-container></h4>
|
||||
|
||||
<mat-dialog-content>
|
||||
<div>
|
||||
<p i18n="No default admin detected explanation">No default admin account detected. This will create and set the password for an admin account with the user name as 'admin'.</p>
|
||||
@@ -13,8 +12,11 @@
|
||||
</div>
|
||||
</div>
|
||||
</mat-dialog-content>
|
||||
|
||||
<mat-dialog-actions>
|
||||
<button [disabled]="input.length === 0" color="accent" style="margin-bottom: 12px;" (click)="create()" mat-raised-button><ng-container i18n="Create">Create</ng-container></button>
|
||||
<div class="spinner-div"><mat-spinner [diameter]="25" *ngIf="creating"></mat-spinner></div>
|
||||
<div class="spinner-div">
|
||||
@if (creating) {
|
||||
<mat-spinner [diameter]="25"></mat-spinner>
|
||||
}
|
||||
</div>
|
||||
</mat-dialog-actions>
|
||||
@@ -1,8 +1,10 @@
|
||||
<h4 mat-dialog-title>
|
||||
<ng-container *ngIf="is_playlist" i18n="Share playlist dialog title">Share playlist</ng-container>
|
||||
<ng-container *ngIf="!is_playlist" i18n="Share video dialog title">Share file</ng-container>
|
||||
@if (is_playlist) {
|
||||
<ng-container i18n="Share playlist dialog title">Share playlist</ng-container>
|
||||
} @else {
|
||||
<ng-container i18n="Share video dialog title">Share file</ng-container>
|
||||
}
|
||||
</h4>
|
||||
|
||||
<mat-dialog-content>
|
||||
<div>
|
||||
<div>
|
||||
@@ -25,7 +27,6 @@
|
||||
</div>
|
||||
</div>
|
||||
</mat-dialog-content>
|
||||
|
||||
<mat-dialog-actions>
|
||||
<button mat-button mat-dialog-close><ng-container i18n="Close button">Close</ng-container></button>
|
||||
</mat-dialog-actions>
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
<h4 mat-dialog-title i18n="Subscribe dialog title">Subscribe to playlist or channel</h4>
|
||||
|
||||
<mat-dialog-content>
|
||||
<div class="container-fluid">
|
||||
<div class="row">
|
||||
@@ -32,9 +31,11 @@
|
||||
</mat-form-field>
|
||||
<mat-form-field class="unit-select">
|
||||
<mat-select color="accent" [(ngModel)]="timerange_unit" [disabled]="download_all">
|
||||
<mat-option *ngFor="let time_unit of time_units" [value]="time_unit + (timerange_amount === 1 ? '' : 's')">
|
||||
@for (time_unit of time_units; track time_unit) {
|
||||
<mat-option [value]="time_unit + (timerange_amount === 1 ? '' : 's')">
|
||||
{{time_unit + (timerange_amount === 1 ? '' : 's')}}
|
||||
</mat-option>
|
||||
}
|
||||
</mat-select>
|
||||
</mat-form-field>
|
||||
</div>
|
||||
@@ -43,7 +44,9 @@
|
||||
<mat-form-field>
|
||||
<mat-label i18n="Max quality">Max quality</mat-label>
|
||||
<mat-select [disabled]="audioOnlyMode" [(ngModel)]="maxQuality">
|
||||
<mat-option *ngFor="let available_quality of available_qualities" [value]="available_quality['value']">{{available_quality['label']}}</mat-option>
|
||||
@for (available_quality of available_qualities; track available_quality) {
|
||||
<mat-option [value]="available_quality['value']">{{available_quality['label']}}</mat-option>
|
||||
}
|
||||
</mat-select>
|
||||
</mat-form-field>
|
||||
</div>
|
||||
@@ -75,13 +78,14 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</mat-dialog-content>
|
||||
|
||||
<mat-dialog-actions>
|
||||
</mat-dialog-content>
|
||||
<mat-dialog-actions>
|
||||
<button mat-button mat-dialog-close><ng-container i18n="Subscribe cancel button">Cancel</ng-container></button>
|
||||
<!-- The mat-dialog-close directive optionally accepts a value as a result for the dialog. -->
|
||||
<button mat-button [disabled]="!url" type="submit" (click)="subscribeClicked()"><ng-container i18n="Subscribe button">Subscribe</ng-container></button>
|
||||
<div class="mat-spinner" *ngIf="subscribing">
|
||||
@if (subscribing) {
|
||||
<div class="mat-spinner">
|
||||
<mat-spinner [diameter]="25"></mat-spinner>
|
||||
</div>
|
||||
</mat-dialog-actions>
|
||||
}
|
||||
</mat-dialog-actions>
|
||||
@@ -1,5 +1,8 @@
|
||||
<h4 mat-dialog-title>{{sub.name}} <ng-container *ngIf="sub.paused" i18n="Paused suffix">(Paused)</ng-container></h4>
|
||||
|
||||
<h4 mat-dialog-title>{{sub.name}}
|
||||
@if (sub.paused) {
|
||||
<ng-container i18n="Paused suffix">(Paused)</ng-container>
|
||||
}
|
||||
</h4>
|
||||
<mat-dialog-content>
|
||||
<div class="info-item">
|
||||
<strong><ng-container i18n="Subscription type property">Type:</ng-container> </strong>
|
||||
@@ -13,12 +16,13 @@
|
||||
<strong><ng-container i18n="Subscription ID property">ID:</ng-container> </strong>
|
||||
<span class="info-item-value">{{sub.id}}</span>
|
||||
</div>
|
||||
<div class="info-item" *ngIf="sub.archive">
|
||||
@if (sub.archive) {
|
||||
<div class="info-item">
|
||||
<strong><ng-container i18n="Subscription ID property">Archive:</ng-container> </strong>
|
||||
<span class="info-item-value">{{sub.archive}}</span>
|
||||
</div>
|
||||
}
|
||||
</mat-dialog-content>
|
||||
|
||||
<mat-dialog-actions>
|
||||
<button mat-button mat-dialog-close><ng-container i18n="Close subscription info button">Close</ng-container></button>
|
||||
<button mat-stroked-button (click)="downloadArchive()" color="accent"><ng-container i18n="Export Archive button">Export Archive</ng-container></button>
|
||||
|
||||
@@ -1,18 +1,26 @@
|
||||
<h4 i18n="Update progress dialog title" mat-dialog-title>Updater</h4>
|
||||
|
||||
<mat-dialog-content>
|
||||
<div *ngIf="updateStatus">
|
||||
@if (updateStatus) {
|
||||
<div>
|
||||
<div style="margin-bottom: 8px;">
|
||||
<h6 *ngIf="updateStatus['updating']">Update in progress</h6>
|
||||
<h6 *ngIf="!updateStatus['updating'] && updateStatus['error']">Update failed</h6>
|
||||
<h6 *ngIf="!updateStatus['updating'] && !updateStatus['error']">Update succeeded!</h6>
|
||||
@if (updateStatus['updating']) {
|
||||
<h6>Update in progress</h6>
|
||||
<mat-progress-bar mode="indeterminate"></mat-progress-bar>
|
||||
} @else {
|
||||
@if (updateStatus['error']) {
|
||||
<h6>Update failed</h6>
|
||||
} @else {
|
||||
<h6>Update succeeded!</h6>
|
||||
}
|
||||
<mat-progress-bar mode="determinate" value="100"></mat-progress-bar>
|
||||
}
|
||||
</div>
|
||||
<mat-progress-bar *ngIf="updateStatus['updating']" mode="indeterminate"></mat-progress-bar>
|
||||
<mat-progress-bar *ngIf="!updateStatus['updating']" mode="determinate" value="100"></mat-progress-bar>
|
||||
<p style="margin-top: 4px; font-size: 13px;" *ngIf="updateStatus['details']">{{updateStatus['details']}}</p>
|
||||
@if (updateStatus['details']) {
|
||||
<p style="margin-top: 4px; font-size: 13px;">{{updateStatus['details']}}</p>
|
||||
}
|
||||
</div>
|
||||
}
|
||||
</mat-dialog-content>
|
||||
|
||||
<mat-dialog-actions>
|
||||
<button mat-button mat-dialog-close><ng-container i18n="Close update progress dialog">Close</ng-container></button>
|
||||
</mat-dialog-actions>
|
||||
@@ -1,5 +1,4 @@
|
||||
<h4 mat-dialog-title><ng-container i18n="Update task schedule">Update task schedule</ng-container></h4>
|
||||
|
||||
<mat-dialog-content>
|
||||
<div class="container-fluid">
|
||||
<div class="row">
|
||||
@@ -9,7 +8,8 @@
|
||||
<div class="col-12 mt-2">
|
||||
<mat-checkbox [(ngModel)]="recurring" [disabled]="!enabled"><ng-container i18n="Recurring">Recurring</ng-container></mat-checkbox>
|
||||
</div>
|
||||
<div class="col-12 mt-2" *ngIf="recurring">
|
||||
@if (recurring) {
|
||||
<div class="col-12 mt-2">
|
||||
<mat-form-field>
|
||||
<mat-select placeholder="Interval" [(ngModel)]="interval" [disabled]="!enabled">
|
||||
<mat-option value="weekly">Weekly</mat-option>
|
||||
@@ -17,15 +17,8 @@
|
||||
</mat-select>
|
||||
</mat-form-field>
|
||||
</div>
|
||||
<div *ngIf="!recurring" class="col-12 mt-2">
|
||||
<mat-form-field>
|
||||
<mat-label i18n="Choose a date">Choose a date</mat-label>
|
||||
<input [(ngModel)]="date" [min]="today" matInput [matDatepicker]="picker" [disabled]="!enabled">
|
||||
<mat-datepicker-toggle matSuffix [for]="picker"></mat-datepicker-toggle>
|
||||
<mat-datepicker #picker></mat-datepicker>
|
||||
</mat-form-field>
|
||||
</div>
|
||||
<div *ngIf="recurring && interval === 'weekly'" class="col-12 mt-2">
|
||||
@if (interval === 'weekly') {
|
||||
<div class="col-12 mt-2">
|
||||
<mat-button-toggle-group [(ngModel)]="days_of_week" [multiple]="true" [disabled]="!enabled" aria-label="Week day">
|
||||
<!-- TODO: support translation -->
|
||||
<mat-button-toggle [value]="0">M</mat-button-toggle>
|
||||
@@ -37,17 +30,29 @@
|
||||
<mat-button-toggle [value]="6">S</mat-button-toggle>
|
||||
</mat-button-toggle-group>
|
||||
</div>
|
||||
}
|
||||
} @else {
|
||||
<div class="col-12 mt-2">
|
||||
<mat-form-field>
|
||||
<mat-label i18n="Choose a date">Choose a date</mat-label>
|
||||
<input [(ngModel)]="date" [min]="today" matInput [matDatepicker]="picker" [disabled]="!enabled">
|
||||
<mat-datepicker-toggle matSuffix [for]="picker"></mat-datepicker-toggle>
|
||||
<mat-datepicker #picker></mat-datepicker>
|
||||
</mat-form-field>
|
||||
</div>
|
||||
}
|
||||
<div class="col-12 mt-2">
|
||||
<mat-form-field>
|
||||
<mat-label>Time</mat-label>
|
||||
<input type="time" matInput [(ngModel)]="time" [disabled]="!enabled">
|
||||
<mat-hint *ngIf="Intl?.DateTimeFormat().resolvedOptions().timeZone">{{Intl.DateTimeFormat().resolvedOptions().timeZone}}</mat-hint>
|
||||
@if (Intl?.DateTimeFormat().resolvedOptions().timeZone) {
|
||||
<mat-hint>{{Intl.DateTimeFormat().resolvedOptions().timeZone}}</mat-hint>
|
||||
}
|
||||
</mat-form-field>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</mat-dialog-content>
|
||||
|
||||
<mat-dialog-actions>
|
||||
<button mat-button mat-dialog-close><ng-container i18n="Update task schedule cancel button">Cancel</ng-container></button>
|
||||
<button mat-button (click)="updateTaskSchedule()"><ng-container i18n="Update button">Update</ng-container></button>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<h4 mat-dialog-title i18n="User profile dialog title">Your Profile</h4>
|
||||
|
||||
<mat-dialog-content>
|
||||
<div *ngIf="postsService.isLoggedIn && postsService.user">
|
||||
@if (postsService.isLoggedIn && postsService.user) {
|
||||
<div>
|
||||
<div>
|
||||
<strong><ng-container i18n="Name">Name:</ng-container></strong> {{postsService.user.name}}
|
||||
</div>
|
||||
@@ -15,14 +15,17 @@
|
||||
</div>
|
||||
<mat-divider style="margin-bottom: 20px"></mat-divider>
|
||||
</div>
|
||||
}
|
||||
<mat-form-field color="accent">
|
||||
<mat-label><ng-container i18n="Language select label">Language</ng-container></mat-label>
|
||||
<mat-select (selectionChange)="localeSelectChanged($event.value)" [(value)]="initialLocale">
|
||||
<mat-option *ngFor="let locale of supported_locales" [value]="locale">
|
||||
<ng-container *ngIf="all_locales[locale]">
|
||||
@for (locale of supported_locales; track locale) {
|
||||
<mat-option [value]="locale">
|
||||
@if (all_locales[locale]) {
|
||||
{{all_locales[locale]['nativeName']}}
|
||||
</ng-container>
|
||||
}
|
||||
</mat-option>
|
||||
}
|
||||
</mat-select>
|
||||
</mat-form-field>
|
||||
<br/>
|
||||
@@ -53,12 +56,13 @@
|
||||
</mat-select>
|
||||
</mat-form-field>
|
||||
</mat-dialog-content>
|
||||
|
||||
<mat-dialog-actions>
|
||||
<div style="width: 100%">
|
||||
<div style="position: relative">
|
||||
<button mat-stroked-button mat-dialog-close color="primary"><ng-container i18n="Close">Close</ng-container></button>
|
||||
<button *ngIf="postsService.isLoggedIn" style="position: absolute; right: 0px;" (click)="logoutClicked()" mat-stroked-button color="warn"><ng-container i18n="Logout">Logout</ng-container></button>
|
||||
@if (postsService.isLoggedIn) {
|
||||
<button style="position: absolute; right: 0px;" (click)="logoutClicked()" mat-stroked-button color="warn"><ng-container i18n="Logout">Logout</ng-container></button>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
</mat-dialog-actions>
|
||||
|
||||
@@ -5,7 +5,6 @@
|
||||
<button [disabled]="!initialized || retrieving_file || !write_access" (click)="toggleFavorite()" mat-icon-button ><mat-icon>{{file.favorite ? 'favorite_filled' : 'favorite_outline'}}</mat-icon></button>
|
||||
</div>
|
||||
</h4>
|
||||
|
||||
<mat-dialog-content>
|
||||
<mat-form-field class="info-field">
|
||||
<mat-label i18n="Name">Name</mat-label>
|
||||
@@ -36,17 +35,21 @@
|
||||
<mat-label i18n="Thumbnail URL">Thumbnail URL</mat-label>
|
||||
<input [(ngModel)]="new_file.thumbnailURL" matInput [disabled]="!editing || new_file.thumbnailPath">
|
||||
</mat-form-field>
|
||||
<mat-form-field *ngIf="initialized && postsService.categories" class="info-field">
|
||||
@if (initialized && postsService.categories) {
|
||||
<mat-form-field class="info-field">
|
||||
<mat-label i18n="Category">Category</mat-label>
|
||||
<mat-select [value]="category" (selectionChange)="categoryChanged($event)" [compareWith]="categoryComparisonFunction" [disabled]="!editing">
|
||||
<mat-option [value]="{}">
|
||||
N/A
|
||||
</mat-option>
|
||||
<mat-option *ngFor="let available_category of postsService.categories | keyvalue" [value]="available_category.value">
|
||||
@for (available_category of postsService.categories | keyvalue; track available_category) {
|
||||
<mat-option [value]="available_category.value">
|
||||
{{available_category.value['name']}}
|
||||
</mat-option>
|
||||
}
|
||||
</mat-select>
|
||||
</mat-form-field>
|
||||
}
|
||||
<mat-form-field class="info-field">
|
||||
<mat-label i18n="View count">View count</mat-label>
|
||||
<input type="number" [(ngModel)]="new_file.view_count" matInput [disabled]="!editing">
|
||||
@@ -55,13 +58,13 @@
|
||||
<mat-label i18n="Local view count">Local view count</mat-label>
|
||||
<input type="number" [(ngModel)]="new_file.local_view_count" matInput [disabled]="!editing">
|
||||
</mat-form-field>
|
||||
|
||||
<mat-divider style="margin-bottom: 16px;"></mat-divider>
|
||||
|
||||
<div *ngIf="!new_file.isAudio" class="info-item">
|
||||
@if (!new_file.isAudio) {
|
||||
<div class="info-item">
|
||||
<div class="info-item-label"><strong><ng-container i18n="Video resolution property">Resolution:</ng-container> </strong></div>
|
||||
<div class="info-item-value">{{new_file.height ? new_file.height + 'p' : 'N/A'}}</div>
|
||||
</div>
|
||||
}
|
||||
<div class="info-item">
|
||||
<div class="info-item-label"><strong><ng-container i18n="Video audio bitrate property">Audio bitrate:</ng-container> </strong></div>
|
||||
<div class="info-item-value">{{new_file.abr ? new_file.abr + ' Kbps' : 'N/A'}}</div>
|
||||
@@ -74,9 +77,7 @@
|
||||
<div class="info-item-label"><strong><ng-container i18n="Video path property">Path:</ng-container> </strong></div>
|
||||
<div class="info-item-value">{{new_file.path ? new_file.path : 'N/A'}}</div>
|
||||
</div>
|
||||
|
||||
</mat-dialog-content>
|
||||
|
||||
<mat-dialog-actions>
|
||||
<button mat-button mat-dialog-close><ng-container i18n="Close video info button">Close</ng-container></button>
|
||||
<button mat-button [disabled]="!metadataChanged()" (click)="saveChanges()"><ng-container i18n="Save video info button">Save</ng-container></button>
|
||||
|
||||
@@ -11,7 +11,9 @@
|
||||
<button mat-button mat-dialog-close>Cancel</button>
|
||||
<!-- The mat-dialog-close directive optionally accepts a value as a result for the dialog. -->
|
||||
<button mat-button [disabled]="!inputText" type="submit" (click)="enterPressed()">{{submitText}}</button>
|
||||
<div class="mat-spinner" *ngIf="inputSubmitted">
|
||||
@if (inputSubmitted) {
|
||||
<div class="mat-spinner">
|
||||
<mat-spinner [diameter]="25"></mat-spinner>
|
||||
</div>
|
||||
}
|
||||
</mat-dialog-actions>
|
||||
@@ -12,7 +12,8 @@
|
||||
</mat-form-field>
|
||||
<!--<button type="button" class="input-clear-button" mat-icon-button (click)="clearInput()"><mat-icon>clear</mat-icon></button>-->
|
||||
</div>
|
||||
<div *ngIf="allowQualitySelect" class="col-7 col-sm-3">
|
||||
@if (allowQualitySelect) {
|
||||
<div class="col-7 col-sm-3">
|
||||
<mat-form-field color="accent" style="display: inline-block; width: inherit; min-width: 120px;">
|
||||
<mat-label>
|
||||
<ng-container i18n="Quality select label">
|
||||
@@ -23,30 +24,37 @@
|
||||
<mat-option i18n="Best" [value]="''">
|
||||
Best
|
||||
</mat-option>
|
||||
<ng-container *ngIf="url && cachedAvailableFormats && cachedAvailableFormats[url]?.formats && !cachedAvailableFormats[url]?.formats_failed">
|
||||
<ng-container *ngFor="let option of cachedAvailableFormats[url]['formats'][audioOnly ? 'audio' : 'video']">
|
||||
<mat-option [matTooltip]="option.expected_filesize ? humanFileSize(option.expected_filesize) : null" *ngIf="option.key !== 'best_audio_format'" [value]="option">
|
||||
@if (url && cachedAvailableFormats && cachedAvailableFormats[url]?.formats && !cachedAvailableFormats[url]?.formats_failed) {
|
||||
@for (option of cachedAvailableFormats[url]['formats'][audioOnly ? 'audio' : 'video']; track option) {
|
||||
@if (option.key !== 'best_audio_format') {
|
||||
<mat-option [matTooltip]="option.expected_filesize ? humanFileSize(option.expected_filesize) : null" [value]="option">
|
||||
{{option.key}}
|
||||
</mat-option>
|
||||
</ng-container>
|
||||
</ng-container>
|
||||
<ng-container *ngIf="url && cachedAvailableFormats && cachedAvailableFormats[url]?.formats_failed">
|
||||
<ng-container *ngFor="let option of qualityOptions[audioOnly ? 'audio' : 'video']">
|
||||
}
|
||||
}
|
||||
}
|
||||
@if (url && cachedAvailableFormats && cachedAvailableFormats[url]?.formats_failed) {
|
||||
@for (option of qualityOptions[audioOnly ? 'audio' : 'video']; track option) {
|
||||
<mat-option [value]="option.value">
|
||||
{{option.label}}
|
||||
</mat-option>
|
||||
</ng-container>
|
||||
</ng-container>
|
||||
}
|
||||
}
|
||||
</mat-select>
|
||||
</mat-form-field>
|
||||
<div class="spinner-div" *ngIf="url !== '' && cachedAvailableFormats[url] && cachedAvailableFormats[url]['formats_loading']">
|
||||
@if (url !== '' && cachedAvailableFormats[url] && cachedAvailableFormats[url]['formats_loading']) {
|
||||
<div class="spinner-div">
|
||||
<mat-spinner [diameter]="25"></mat-spinner>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="results-div" *ngIf="results_showing">
|
||||
<span *ngFor="let result of results; let i = index">
|
||||
@if (results_showing) {
|
||||
<div class="results-div">
|
||||
@for (result of results; track result; let i = $index) {
|
||||
<span>
|
||||
<mat-card appearance="outlined" class="result-card mat-elevation-z7" [ngClass]="[(i === 0 && results.length > 1) ? 'first-result-card' : '', ((i === results.length-1) && results.length > 1) ? 'last-result-card' : '', (results.length === 1) ? 'only-result-card' : '']">
|
||||
<div class="search-card-title">
|
||||
{{result.title}}
|
||||
@@ -64,7 +72,9 @@
|
||||
</button>
|
||||
</mat-card>
|
||||
</span>
|
||||
}
|
||||
</div>
|
||||
}
|
||||
</form>
|
||||
<br/>
|
||||
<mat-checkbox [disabled]="autoplay && !!current_download" (change)="videoModeChanged($event)" [(ngModel)]="audioOnly" style="float: left; margin-top: -12px; margin-left: 4px;">
|
||||
@@ -72,12 +82,13 @@
|
||||
Only Audio
|
||||
</ng-container>
|
||||
</mat-checkbox>
|
||||
<mat-checkbox *ngIf="!forceAutoplay" [disabled]="getURLArray(url).length > 1" (change)="autoplayChanged($event)" [(ngModel)]="autoplay" style="float: right; margin-top: -12px">
|
||||
@if (!forceAutoplay) {
|
||||
<mat-checkbox [disabled]="getURLArray(url).length > 1" (change)="autoplayChanged($event)" [(ngModel)]="autoplay" style="float: right; margin-top: -12px">
|
||||
<ng-container i18n="Autoplay checkbox">
|
||||
Autoplay
|
||||
</ng-container>
|
||||
</mat-checkbox>
|
||||
|
||||
}
|
||||
</div>
|
||||
</mat-card-content>
|
||||
<mat-card-actions>
|
||||
@@ -86,15 +97,18 @@
|
||||
Download
|
||||
</ng-container>
|
||||
</button>
|
||||
<button (click)="cancelDownload()" style="margin-left: 8px; margin-bottom: 8px" *ngIf="!!current_download" mat-stroked-button color="warn">
|
||||
@if (!!current_download) {
|
||||
<button (click)="cancelDownload()" style="margin-left: 8px; margin-bottom: 8px" mat-stroked-button color="warn">
|
||||
<ng-container i18n="Cancel download button">
|
||||
Cancel
|
||||
</ng-container>
|
||||
</button>
|
||||
}
|
||||
</mat-card-actions>
|
||||
</mat-card>
|
||||
</div>
|
||||
<div *ngIf="allowAdvancedDownload" class="big demo-basic">
|
||||
@if (allowAdvancedDownload) {
|
||||
<div class="big demo-basic">
|
||||
<form style="margin-left: 20px; margin-right: 20px;">
|
||||
<mat-expansion-panel class="big no-border-radius-top">
|
||||
<mat-expansion-panel-header>
|
||||
@@ -104,11 +118,13 @@
|
||||
</ng-container>
|
||||
</mat-panel-title>
|
||||
</mat-expansion-panel-header>
|
||||
<p *ngIf="this.simulatedOutput">
|
||||
@if (this.simulatedOutput) {
|
||||
<p>
|
||||
<ng-container i18n="Simulated command label">
|
||||
Simulated command:
|
||||
</ng-container>
|
||||
<i>{{this.simulatedOutput}}</i></p>
|
||||
}
|
||||
<div class="container" style="padding-bottom: 20px;">
|
||||
<div class="row">
|
||||
<div class="col-12 col-sm-6">
|
||||
@@ -148,69 +164,86 @@
|
||||
</mat-hint>
|
||||
</mat-form-field>
|
||||
</div>
|
||||
<div *ngIf="!youtubeAuthDisabledOverride" class="col-12 col-sm-6 mt-3">
|
||||
@if (!youtubeAuthDisabledOverride) {
|
||||
<div class="col-12 col-sm-6 mt-3">
|
||||
<mat-checkbox color="accent" [disabled]="!!current_download" (change)="youtubeAuthEnabledChanged($event)" [(ngModel)]="youtubeAuthEnabled" style="z-index: 999" [ngModelOptions]="{standalone: true}">
|
||||
<ng-container i18n="Use authentication checkbox">
|
||||
Use authentication
|
||||
</ng-container>
|
||||
</mat-checkbox>
|
||||
<mat-form-field *ngIf="youtubeAuthEnabled" color="accent" class="advanced-input">
|
||||
@if (youtubeAuthEnabled) {
|
||||
<mat-form-field color="accent" class="advanced-input">
|
||||
<mat-label i18n="Username">Username</mat-label>
|
||||
<input [(ngModel)]="youtubeUsername" [ngModelOptions]="{standalone: true}" matInput (ngModelChange)="argsChanged()">
|
||||
</mat-form-field>
|
||||
}
|
||||
</div>
|
||||
<div *ngIf="!youtubeAuthDisabledOverride" class="col-12 col-sm-6 mt-3">
|
||||
<mat-form-field *ngIf="youtubeAuthEnabled" style="margin-top: 40px;" color="accent" class="advanced-input">
|
||||
<div class="col-12 col-sm-6 mt-3">
|
||||
@if (youtubeAuthEnabled) {
|
||||
<mat-form-field style="margin-top: 40px;" color="accent" class="advanced-input">
|
||||
<mat-label i18n="Password">Password</mat-label>
|
||||
<input [(ngModel)]="youtubePassword" type="password" [ngModelOptions]="{standalone: true}" matInput (ngModelChange)="argsChanged()">
|
||||
</mat-form-field>
|
||||
}
|
||||
</div>
|
||||
}
|
||||
<div class="col-12 col-sm-6 mt-3">
|
||||
<mat-checkbox color="accent" [disabled]="!!current_download" [(ngModel)]="cropFile" style="z-index: 999" [ngModelOptions]="{standalone: true}">
|
||||
<ng-container i18n="Crop video checkbox">
|
||||
Crop file
|
||||
</ng-container>
|
||||
</mat-checkbox>
|
||||
<mat-form-field *ngIf="cropFile" color="accent" class="advanced-input">
|
||||
@if (cropFile) {
|
||||
<mat-form-field color="accent" class="advanced-input">
|
||||
<mat-label i18n="Crop from (seconds)">Crop from (seconds)</mat-label>
|
||||
<input [(ngModel)]="cropFileStart" type="number" [ngModelOptions]="{standalone: true}" matInput>
|
||||
</mat-form-field>
|
||||
}
|
||||
</div>
|
||||
<div class="col-12 col-sm-6 mt-3">
|
||||
<mat-form-field *ngIf="cropFile" style="margin-top: 40px;" color="accent" class="advanced-input">
|
||||
@if (cropFile) {
|
||||
<mat-form-field style="margin-top: 40px;" color="accent" class="advanced-input">
|
||||
<mat-label i18n="Crop to (seconds)">Crop to (seconds)</mat-label>
|
||||
<input [(ngModel)]="cropFileEnd" type="number" [ngModelOptions]="{standalone: true}" matInput>
|
||||
</mat-form-field>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</mat-expansion-panel>
|
||||
</form>
|
||||
</div>
|
||||
<br/>
|
||||
<div class="centered big" id="bar_div" *ngIf="current_download && autoplay">
|
||||
</div>
|
||||
}
|
||||
<br/>
|
||||
@if (current_download && autoplay) {
|
||||
<div class="centered big" id="bar_div">
|
||||
<div class="margined">
|
||||
<div [ngClass]="(+percentDownloaded > 99)?'make-room-for-spinner':'equal-sizes'" style="display: inline-block; width: 100%; padding-left: 20px" *ngIf="current_download.percent_complete && current_download.percent_complete > 1;else indeterminateprogress">
|
||||
@if (current_download.percent_complete && current_download.percent_complete > 1) {
|
||||
<div [ngClass]="(+percentDownloaded > 99)?'make-room-for-spinner':'equal-sizes'" style="display: inline-block; width: 100%; padding-left: 20px">
|
||||
<mat-progress-bar style="border-radius: 5px;" mode="determinate" value="{{percentDownloaded}}"></mat-progress-bar>
|
||||
<br/>
|
||||
</div>
|
||||
<div *ngIf="+percentDownloaded > 99" class="spinner">
|
||||
} @else {
|
||||
<mat-progress-bar style="border-radius: 5px;" mode="indeterminate"></mat-progress-bar>
|
||||
}
|
||||
@if (+percentDownloaded > 99) {
|
||||
<div class="spinner">
|
||||
<mat-spinner [diameter]="25"></mat-spinner>
|
||||
</div>
|
||||
<ng-template #indeterminateprogress>
|
||||
<mat-progress-bar style="border-radius: 5px;" mode="indeterminate"></mat-progress-bar>
|
||||
</ng-template>
|
||||
}
|
||||
</div>
|
||||
<br/>
|
||||
</div>
|
||||
|
||||
<div style="display: flex; justify-content: center;" *ngIf="downloads && downloads.length > 0 && !autoplay">
|
||||
</div>
|
||||
}
|
||||
@if (downloads && downloads.length > 0 && !autoplay) {
|
||||
<div style="display: flex; justify-content: center;">
|
||||
<app-downloads style="width: 80%; min-width: 350px; margin-bottom: 10px" [uids]="download_uids"></app-downloads>
|
||||
</div>
|
||||
|
||||
<ng-container *ngIf="cachedFileManagerEnabled || fileManagerEnabled">
|
||||
</div>
|
||||
}
|
||||
@if (cachedFileManagerEnabled || fileManagerEnabled) {
|
||||
<app-recent-videos #recentVideos></app-recent-videos>
|
||||
<br/>
|
||||
<h4 style="text-align: center">Custom playlists</h4>
|
||||
<app-custom-playlists></app-custom-playlists>
|
||||
</ng-container>
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
<div style="height: 100%" *ngIf="playlist.length > 0 && show_player">
|
||||
@if (playlist.length > 0 && show_player) {
|
||||
<div style="height: 100%">
|
||||
<div style="height: 100%" [ngClass]="(currentItem.type === 'audio/mp3') ? null : 'container-video'">
|
||||
<div style="max-width: 100%; margin-left: 0px; height: 100%">
|
||||
<mat-drawer-container style="height: 100%" class="example-container" autosize>
|
||||
@@ -6,59 +7,80 @@
|
||||
<vg-player style="height: fit-content; max-height: 75vh" (onPlayerReady)="onPlayerReady($event)" [style.background-color]="(currentItem.type === 'audio/mp3') ? postsService.theme.drawer_color : 'black'">
|
||||
<video [ngClass]="(currentItem.type === 'audio/mp3') ? 'audio-styles' : 'video-styles'" #media class="video-player" [vgMedia]="$any(media)" [src]="currentItem.src" id="singleVideo" preload="auto" controls playsinline>
|
||||
</video>
|
||||
<app-skip-ad-button *ngIf="postsService['config']['API']['use_sponsorblock_API'] && api && playlist?.length > 0 && playlist[currentIndex]['type'] === 'video/mp4'" (setPlaybackTimestamp)="setPlaybackTimestamp($event)" [current_video]="playlist[currentIndex]" [playback_timestamp]="api.currentTime" class="skip-ad-button"></app-skip-ad-button>
|
||||
@if (postsService['config']['API']['use_sponsorblock_API'] && api && playlist?.length > 0 && playlist[currentIndex]['type'] === 'video/mp4') {
|
||||
<app-skip-ad-button (setPlaybackTimestamp)="setPlaybackTimestamp($event)" [current_video]="playlist[currentIndex]" [playback_timestamp]="api.currentTime" class="skip-ad-button"></app-skip-ad-button>
|
||||
}
|
||||
</vg-player>
|
||||
</div>
|
||||
<div style="height: fit-content; width: 100%; margin-top: 10px;">
|
||||
<div class="container">
|
||||
<div class="row">
|
||||
<div class="col-2 col-lg-1">
|
||||
<ng-container *ngIf="db_file">{{db_file['local_view_count'] ? db_file['local_view_count']+1 : 1}} <ng-container i18n="View count label">views</ng-container></ng-container>
|
||||
@if (db_file) {
|
||||
{{db_file['local_view_count'] ? db_file['local_view_count']+1 : 1}} <ng-container i18n="View count label">views</ng-container>
|
||||
}
|
||||
</div>
|
||||
<div style="white-space: pre-line;" class="col-8 col-lg-9">
|
||||
<ng-container *ngIf="db_file && db_file['description']">
|
||||
@if (db_file && db_file['description']) {
|
||||
<p>
|
||||
<app-see-more [text]="db_file['description']"></app-see-more>
|
||||
</p>
|
||||
</ng-container>
|
||||
<ng-container *ngIf="!db_file || !db_file['description']">
|
||||
} @else {
|
||||
<p i18n="No description" style="text-align: center;">
|
||||
No description available.
|
||||
</p>
|
||||
</ng-container>
|
||||
}
|
||||
</div>
|
||||
<div class="col-2">
|
||||
<span class="buttons" *ngIf="db_playlist">
|
||||
@if (db_playlist) {
|
||||
<span class="buttons">
|
||||
<button (click)="downloadContent()" [disabled]="downloading" mat-icon-button><mat-icon>save</mat-icon></button>
|
||||
<mat-spinner *ngIf="downloading" class="spinner" [diameter]="35"></mat-spinner>
|
||||
<button *ngIf="(!postsService.isLoggedIn || postsService.permissions.includes('sharing')) && !auto" (click)="openShareDialog()" mat-icon-button><mat-icon>share</mat-icon></button>
|
||||
@if (downloading) {
|
||||
<mat-spinner class="spinner" [diameter]="35"></mat-spinner>
|
||||
}
|
||||
@if ((!postsService.isLoggedIn || postsService.permissions.includes('sharing')) && !auto) {
|
||||
<button (click)="openShareDialog()" mat-icon-button><mat-icon>share</mat-icon></button>
|
||||
}
|
||||
</span>
|
||||
<span class="buttons" *ngIf="db_file">
|
||||
}
|
||||
@if (db_file) {
|
||||
<span class="buttons">
|
||||
<button (click)="downloadFile()" [disabled]="downloading" mat-icon-button><mat-icon>cloud_download</mat-icon></button>
|
||||
<mat-spinner *ngIf="downloading" class="spinner" [diameter]="35"></mat-spinner>
|
||||
<button *ngIf="!postsService.isLoggedIn || postsService.permissions.includes('sharing')" (click)="openShareDialog()" mat-icon-button><mat-icon>share</mat-icon></button>
|
||||
@if (downloading) {
|
||||
<mat-spinner class="spinner" [diameter]="35"></mat-spinner>
|
||||
}
|
||||
@if (!postsService.isLoggedIn || postsService.permissions.includes('sharing')) {
|
||||
<button (click)="openShareDialog()" mat-icon-button><mat-icon>share</mat-icon></button>
|
||||
}
|
||||
</span>
|
||||
<ng-container *ngIf="db_file || playlist[currentIndex]"></ng-container>
|
||||
<button (click)="openFileInfoDialog()" *ngIf="db_file || db_playlist" mat-icon-button><mat-icon>info</mat-icon></button>
|
||||
<button *ngIf="db_file && db_file.url.includes('twitch.tv')" (click)="drawer.toggle()" mat-icon-button><mat-icon>chat</mat-icon></button>
|
||||
}
|
||||
@if (db_file || db_playlist) {
|
||||
<button (click)="openFileInfoDialog()" mat-icon-button><mat-icon>info</mat-icon></button>
|
||||
}
|
||||
@if (db_file && db_file.url.includes('twitch.tv')) {
|
||||
<button (click)="drawer.toggle()" mat-icon-button><mat-icon>chat</mat-icon></button>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div style="height: fit-content; width: 100%; margin-top: 10px;">
|
||||
<mat-button-toggle-group cdkDropList [cdkDropListSortingDisabled]="true" (cdkDropListDropped)="drop($event)" style="width: 80%; left: 9%" vertical name="videoSelect" aria-label="Video Select" #group="matButtonToggleGroup">
|
||||
<mat-button-toggle cdkDrag *ngFor="let playlist_item of playlist; let i = index" [checked]="currentItem.title === playlist_item.title" (click)="onClickPlaylistItem(playlist_item, i)" class="toggle-button" [value]="playlist_item.title">{{playlist_item.label}}</mat-button-toggle>
|
||||
@for (playlist_item of playlist; track playlist_item; let i = $index) {
|
||||
<mat-button-toggle cdkDrag [checked]="currentItem.title === playlist_item.title" (click)="onClickPlaylistItem(playlist_item, i)" class="toggle-button" [value]="playlist_item.title">{{playlist_item.label}}</mat-button-toggle>
|
||||
}
|
||||
</mat-button-toggle-group>
|
||||
</div>
|
||||
|
||||
<app-concurrent-stream *ngIf="db_file && api && postsService.config" (setPlaybackRate)="setPlaybackRate($event)" (togglePlayback)="togglePlayback($event)" (setPlaybackTimestamp)="setPlaybackTimestamp($event)" [playing]="api.state === 'playing'" [uid]="uid" [playback_timestamp]="api.time.current/1000" [server_mode]="!postsService.config.Advanced.multi_user_mode || postsService.isLoggedIn"></app-concurrent-stream>
|
||||
|
||||
@if (db_file && api && postsService.config) {
|
||||
<app-concurrent-stream (setPlaybackRate)="setPlaybackRate($event)" (togglePlayback)="togglePlayback($event)" (setPlaybackTimestamp)="setPlaybackTimestamp($event)" [playing]="api.state === 'playing'" [uid]="uid" [playback_timestamp]="api.time.current/1000" [server_mode]="!postsService.config.Advanced.multi_user_mode || postsService.isLoggedIn"></app-concurrent-stream>
|
||||
}
|
||||
<mat-drawer #drawer class="example-sidenav" mode="side" position="end" [opened]="db_file && db_file['chat_exists']">
|
||||
<ng-container *ngIf="api_ready && db_file && db_file.url.includes('twitch.tv')">
|
||||
@if (api_ready && db_file && db_file.url.includes('twitch.tv')) {
|
||||
<app-twitch-chat #twitchchat [db_file]="db_file" [current_timestamp]="api.currentTime" [sub]="subscription"></app-twitch-chat>
|
||||
</ng-container>
|
||||
}
|
||||
</mat-drawer>
|
||||
</mat-drawer-container>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
@@ -3,7 +3,8 @@
|
||||
<!-- Server -->
|
||||
<mat-tab label="Main" i18n-label="Main settings label">
|
||||
<ng-template matTabContent style="padding: 15px;">
|
||||
<div *ngIf="new_config" class="container-fluid">
|
||||
@if (new_config) {
|
||||
<div class="container-fluid">
|
||||
<div class="row">
|
||||
<div class="col-12 mt-3">
|
||||
<mat-form-field class="text-field" color="accent">
|
||||
@@ -22,7 +23,7 @@
|
||||
</div>
|
||||
</div>
|
||||
<mat-divider></mat-divider>
|
||||
<div *ngIf="new_config" class="container-fluid">
|
||||
<div class="container-fluid">
|
||||
<div class="row">
|
||||
<div class="col-12 mt-3">
|
||||
<mat-checkbox color="accent" [(ngModel)]="new_config['Advanced']['multi_user_mode']"><ng-container i18n="Multi user mode setting">Multi-user mode</ng-container></mat-checkbox>
|
||||
@@ -37,7 +38,7 @@
|
||||
</div>
|
||||
</div>
|
||||
<mat-divider></mat-divider>
|
||||
<div *ngIf="new_config" class="container-fluid">
|
||||
<div class="container-fluid">
|
||||
<div class="row">
|
||||
<div class="col-12 mt-3">
|
||||
<mat-checkbox color="accent" [(ngModel)]="new_config['Subscriptions']['allow_subscriptions']"><ng-container i18n="Allow subscriptions setting">Allow subscriptions</ng-container></mat-checkbox>
|
||||
@@ -62,7 +63,7 @@
|
||||
</div>
|
||||
</div>
|
||||
<mat-divider></mat-divider>
|
||||
<div *ngIf="new_config" class="container-fluid">
|
||||
<div class="container-fluid">
|
||||
<div class="row">
|
||||
<div class="col-12 mt-3">
|
||||
<mat-form-field>
|
||||
@@ -78,13 +79,15 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
</ng-template>
|
||||
</mat-tab>
|
||||
<!-- Downloader -->
|
||||
<mat-tab label="Downloader" i18n-label="Downloader settings label">
|
||||
<ng-template matTabContent>
|
||||
<!-- Downloader -->
|
||||
<div *ngIf="new_config" class="container-fluid">
|
||||
@if (new_config) {
|
||||
<div class="container-fluid">
|
||||
<div class="row">
|
||||
<div class="col-12 mt-3">
|
||||
<mat-form-field class="text-field" color="accent">
|
||||
@@ -93,7 +96,6 @@
|
||||
<mat-hint><ng-container i18n="Aduio path setting input hint">Path for audio only downloads. It is relative to YTDL-Material's root folder.</ng-container></mat-hint>
|
||||
</mat-form-field>
|
||||
</div>
|
||||
|
||||
<div class="col-12 mt-3">
|
||||
<mat-form-field class="text-field" color="accent">
|
||||
<mat-label i18n="Video folder path">Video folder path</mat-label>
|
||||
@@ -101,7 +103,6 @@
|
||||
<mat-hint><ng-container i18n="Video path setting input hint">Path for video downloads. It is relative to YTDL-Material's root folder.</ng-container></mat-hint>
|
||||
</mat-form-field>
|
||||
</div>
|
||||
|
||||
<div class="col-12 mt-3 mb-1">
|
||||
<mat-form-field class="text-field" color="accent">
|
||||
<mat-label i18n="Default file output">Default file output</mat-label>
|
||||
@@ -112,7 +113,6 @@
|
||||
</mat-hint>
|
||||
</mat-form-field>
|
||||
</div>
|
||||
|
||||
<div class="col-12 mt-4 mb-5">
|
||||
<mat-form-field class="text-field" style="margin-right: 12px;" color="accent">
|
||||
<mat-label i18n="Global custom args">Global custom args</mat-label>
|
||||
@@ -124,12 +124,14 @@
|
||||
</div>
|
||||
</div>
|
||||
<mat-divider></mat-divider>
|
||||
<div *ngIf="new_config" class="container-fluid">
|
||||
<div class="container-fluid">
|
||||
<div class="row">
|
||||
<div class="col-12 mt-3">
|
||||
<h6 i18n="Categories">Categories</h6>
|
||||
<div *ngIf="postsService.categories && postsService.categories.length > 0" cdkDropList class="category-list" (cdkDropListDropped)="dropCategory($event)">
|
||||
<div class="category-box" *ngFor="let category of postsService.categories" cdkDrag>
|
||||
@if (postsService.categories && postsService.categories.length > 0) {
|
||||
<div cdkDropList class="category-list" (cdkDropListDropped)="dropCategory($event)">
|
||||
@for (category of postsService.categories; track category) {
|
||||
<div class="category-box" cdkDrag>
|
||||
<div class="category-custom-placeholder" *cdkDragPlaceholder></div>
|
||||
{{category['name']}}
|
||||
<span style="float: right">
|
||||
@@ -137,7 +139,9 @@
|
||||
<button mat-icon-button (click)="deleteCategory(category)"><mat-icon>cancel</mat-icon></button>
|
||||
</span>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
}
|
||||
<button style="margin-top: 10px;" mat-mini-fab (click)="openAddCategoryDialog()"><mat-icon>add</mat-icon></button>
|
||||
</div>
|
||||
<div class="col-12 mt-2 mb-2">
|
||||
@@ -146,23 +150,21 @@
|
||||
</div>
|
||||
</div>
|
||||
<mat-divider></mat-divider>
|
||||
<div *ngIf="new_config" class="container-fluid">
|
||||
<div class="container-fluid">
|
||||
<div class="row">
|
||||
<div class="col-12 mt-3">
|
||||
<mat-checkbox color="accent" [(ngModel)]="new_config['Downloader']['use_youtubedl_archive']"><ng-container i18n="Use youtubedl archive setting">Use youtube-dl archive</ng-container></mat-checkbox>
|
||||
</div>
|
||||
|
||||
<div class="col-12 mt-2">
|
||||
<mat-checkbox color="accent" [(ngModel)]="new_config['Downloader']['include_thumbnail']"><ng-container i18n="Include thumbnail setting">Include thumbnail</ng-container></mat-checkbox>
|
||||
</div>
|
||||
|
||||
<div class="col-12 mt-2 mb-2">
|
||||
<mat-checkbox color="accent" [(ngModel)]="new_config['Downloader']['include_metadata']"><ng-container i18n="Include metadata setting">Include metadata</ng-container></mat-checkbox>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<mat-divider></mat-divider>
|
||||
<div *ngIf="new_config" class="container-fluid">
|
||||
<div class="container-fluid">
|
||||
<div class="row">
|
||||
<div class="col-12 mt-3 mb-4">
|
||||
<mat-form-field class="text-field" color="accent">
|
||||
@@ -181,19 +183,21 @@
|
||||
</div>
|
||||
</div>
|
||||
<mat-divider></mat-divider>
|
||||
<div *ngIf="new_config" class="container-fluid">
|
||||
<div class="container-fluid">
|
||||
<div class="row">
|
||||
<div class="col-12 mt-3">
|
||||
<button (click)="killAllDownloads()" mat-stroked-button color="warn"><ng-container i18n="Kill all downloads button">Kill all downloads</ng-container></button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
</ng-template>
|
||||
</mat-tab>
|
||||
<!-- Extra -->
|
||||
<mat-tab label="Extra" i18n-label="Extra settings label">
|
||||
<ng-template matTabContent>
|
||||
<div *ngIf="new_config" class="container-fluid">
|
||||
@if (new_config) {
|
||||
<div class="container-fluid">
|
||||
<div class="row">
|
||||
<div class="col-12 mt-3">
|
||||
<mat-form-field class="text-field" color="accent">
|
||||
@@ -220,7 +224,7 @@
|
||||
</div>
|
||||
</div>
|
||||
<mat-divider></mat-divider>
|
||||
<div *ngIf="new_config" class="container-fluid">
|
||||
<div class="container-fluid">
|
||||
<div class="row">
|
||||
<div class="col-12 mt-3">
|
||||
<mat-checkbox color="accent" [(ngModel)]="new_config['API']['use_API_key']"><ng-container i18n="Enable Public API key setting">Enable Public API</ng-container></mat-checkbox>
|
||||
@@ -240,7 +244,7 @@
|
||||
</div>
|
||||
</div>
|
||||
<mat-divider></mat-divider>
|
||||
<div *ngIf="new_config" class="container-fluid">
|
||||
<div class="container-fluid">
|
||||
<div class="row">
|
||||
<div class="col-12 mt-3">
|
||||
<mat-checkbox color="accent" [(ngModel)]="new_config['API']['use_youtube_API']"><ng-container i18n="Use YouTube API setting">Use YouTube API</ng-container></mat-checkbox>
|
||||
@@ -264,7 +268,7 @@
|
||||
</div>
|
||||
</div>
|
||||
<mat-divider></mat-divider>
|
||||
<div *ngIf="new_config" class="container-fluid">
|
||||
<div class="container-fluid">
|
||||
<div class="row">
|
||||
<div class="col-12 mt-3">
|
||||
<h6>RSS Feed</h6>
|
||||
@@ -276,7 +280,7 @@
|
||||
</div>
|
||||
</div>
|
||||
<mat-divider></mat-divider>
|
||||
<div *ngIf="new_config" class="container-fluid">
|
||||
<div class="container-fluid">
|
||||
<div class="row">
|
||||
<div class="col-12 mt-3">
|
||||
<h6>Chrome</h6>
|
||||
@@ -299,50 +303,58 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
</ng-template>
|
||||
</mat-tab>
|
||||
<!-- Database -->
|
||||
<mat-tab label="Database" i18n-label="Database settings label">
|
||||
<ng-template matTabContent>
|
||||
<div *ngIf="new_config" class="container-fluid">
|
||||
@if (new_config) {
|
||||
<div class="container-fluid">
|
||||
<div class="row">
|
||||
<div class="col-12 mt-3">
|
||||
<div *ngIf="db_info">
|
||||
@if (db_info) {
|
||||
<div>
|
||||
<p><ng-container i18n="Database location label">Database location:</ng-container> <strong>{{db_info['using_local_db'] ? 'Local' : 'MongoDB'}}</strong></p>
|
||||
<h6 i18n="Records per table label">Records per table</h6>
|
||||
<mat-list style="padding-top: 0px">
|
||||
<mat-list-item style="height: 28px" *ngFor="let table_stats of db_info['stats_by_table'] | keyvalue">
|
||||
@for (table_stats of db_info['stats_by_table'] | keyvalue; track table_stats) {
|
||||
<mat-list-item style="height: 28px">
|
||||
{{table_stats.key}}: {{table_stats.value.records_count}}
|
||||
</mat-list-item>
|
||||
}
|
||||
</mat-list>
|
||||
|
||||
<mat-form-field style="width: 100%; margin-top: 15px; margin-bottom: 10px" color="accent">
|
||||
<mat-label i18n="MongoDB Connection String">MongoDB Connection String</mat-label>
|
||||
<input [(ngModel)]="new_config['Database']['mongodb_connection_string']" matInput required>
|
||||
<mat-hint><ng-container i18n="MongoDB Connection String setting hint AKA preamble">Example:</ng-container> mongodb://127.0.0.1:27017/?compressors=zlib<br>Docker: mongodb://<container name>:27017/?compressors=zlib</mat-hint>
|
||||
</mat-form-field>
|
||||
|
||||
<div class="test-connection-div">
|
||||
<button (click)="testConnectionString(new_config['Database']['mongodb_connection_string'])" [disabled]="testing_connection_string" mat-flat-button color="accent"><ng-container i18n="Test connection string button">Test connection string</ng-container></button>
|
||||
<mat-spinner class="test-connection-spinner" style="margin-left: 10px" *ngIf="testing_connection_string" [diameter]="25"></mat-spinner>
|
||||
@if (testing_connection_string) {
|
||||
<mat-spinner class="test-connection-spinner" style="margin-left: 10px" [diameter]="25"></mat-spinner>
|
||||
}
|
||||
</div>
|
||||
|
||||
<div class="transfer-db-div">
|
||||
<button [disabled]="db_transferring" color="accent" (click)="transferDB()" mat-raised-button><ng-container i18n="Transfer DB button">Transfer DB to </ng-container>{{db_info['using_local_db'] ? 'MongoDB' : 'Local'}}</button>
|
||||
</div>
|
||||
</div>
|
||||
<div *ngIf="!db_info">
|
||||
} @else {
|
||||
<div>
|
||||
<ng-container i18n="Database info not retrieved error message">Database information could not be retrieved. Check the server logs for more information.</ng-container>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
</ng-template>
|
||||
</mat-tab>
|
||||
<!-- Notifications -->
|
||||
<mat-tab label="Notifications" i18n-label="Notifications settings label">
|
||||
<ng-template matTabContent>
|
||||
<div *ngIf="new_config" class="container-fluid">
|
||||
@if (new_config) {
|
||||
<div class="container-fluid">
|
||||
<div class="row">
|
||||
<div class="col-12 mt-3">
|
||||
<div><a target="_blank" href="https://github.com/Tzahi12345/YoutubeDL-Material/wiki/Notifications"><ng-container i18n="Documentation">Documentation</ng-container></a></div>
|
||||
@@ -435,12 +447,14 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
</ng-template>
|
||||
</mat-tab>
|
||||
<!-- Advanced -->
|
||||
<mat-tab label="Advanced" i18n-label="Host settings label">
|
||||
<ng-template matTabContent>
|
||||
<div *ngIf="new_config" class="container-fluid">
|
||||
@if (new_config) {
|
||||
<div class="container-fluid">
|
||||
<div class="row">
|
||||
<div class="col-12 mt-3">
|
||||
<mat-form-field>
|
||||
@@ -500,7 +514,7 @@
|
||||
</div>
|
||||
</div>
|
||||
<mat-divider></mat-divider>
|
||||
<div *ngIf="new_config" class="container-fluid">
|
||||
<div class="container-fluid">
|
||||
<div class="row">
|
||||
<div class="col-12 mt-2 mb-2">
|
||||
<mat-checkbox color="accent" [(ngModel)]="new_config['Advanced']['use_cookies']"><ng-container i18n="Use cookies setting">Use Cookies</ng-container></mat-checkbox>
|
||||
@@ -509,17 +523,18 @@
|
||||
</div>
|
||||
</div>
|
||||
<mat-divider></mat-divider>
|
||||
<div *ngIf="new_config" class="container-fluid mt-3">
|
||||
<div class="container-fluid mt-3">
|
||||
<app-updater></app-updater>
|
||||
</div>
|
||||
<mat-divider></mat-divider>
|
||||
<div *ngIf="new_config" class="container-fluid">
|
||||
<div class="container-fluid">
|
||||
<div class="row">
|
||||
<div class="col-12 mt-4">
|
||||
<button (click)="restartServer()" mat-stroked-button color="warn"><ng-container i18n="Restart server button">Restart server</ng-container></button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
</ng-template>
|
||||
</mat-tab>
|
||||
<mat-tab [disabled]="!postsService.config?.Advanced.multi_user_mode">
|
||||
@@ -528,8 +543,9 @@
|
||||
<ng-container i18n="Users settings label">Users</ng-container>
|
||||
</div>
|
||||
</ng-template>
|
||||
<ng-container *ngIf="postsService.config?.Advanced.multi_user_mode">
|
||||
<div *ngIf="new_config" style="margin-top: 24px; margin-bottom: -25px;">
|
||||
@if (postsService.config?.Advanced.multi_user_mode) {
|
||||
@if (new_config) {
|
||||
<div style="margin-top: 24px; margin-bottom: -25px;">
|
||||
<div>
|
||||
<mat-checkbox color="accent" [(ngModel)]="new_config['Users']['allow_registration']"><ng-container i18n="Allow registration setting">Allow user registration</ng-container></mat-checkbox>
|
||||
</div>
|
||||
@@ -545,7 +561,8 @@
|
||||
</mat-option>
|
||||
</mat-select>
|
||||
</mat-form-field>
|
||||
<div *ngIf="new_config['Users']['auth_method'] === 'ldap'">
|
||||
@if (new_config['Users']['auth_method'] === 'ldap') {
|
||||
<div>
|
||||
<div>
|
||||
<mat-form-field>
|
||||
<mat-label i18n="LDAP URL">LDAP URL</mat-label>
|
||||
@@ -577,26 +594,28 @@
|
||||
</mat-form-field>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
<mat-divider></mat-divider>
|
||||
</div>
|
||||
<app-modify-users *ngIf="new_config"></app-modify-users>
|
||||
</ng-container>
|
||||
<app-modify-users></app-modify-users>
|
||||
}
|
||||
}
|
||||
</mat-tab>
|
||||
<mat-tab *ngIf="postsService.config" label="Logs" i18n-label="Logs settings label">
|
||||
@if (postsService.config) {
|
||||
<mat-tab label="Logs" i18n-label="Logs settings label">
|
||||
<ng-template matTabContent>
|
||||
<div style="margin-top: 15px; height: 84%;">
|
||||
<app-logs-viewer></app-logs-viewer>
|
||||
</div>
|
||||
</ng-template>
|
||||
</mat-tab>
|
||||
}
|
||||
</mat-tab-group>
|
||||
|
||||
<div class="action-buttons">
|
||||
<button style="margin-left: 10px; height: 37.3px" color="accent" (click)="saveSettings()" [disabled]="settingsSame()" mat-raised-button><mat-icon>done</mat-icon>
|
||||
<ng-container i18n="Settings save button">Save</ng-container>
|
||||
</button>
|
||||
<button style="margin-left: 10px;" mat-flat-button (click)="cancelSettings()" [disabled]="settingsSame()"><mat-icon>cancel</mat-icon>
|
||||
<span i18n="Settings cancel button">Cancel</span>
|
||||
</button>
|
||||
</button>
|
||||
<button style="margin-left: 10px;" mat-flat-button (click)="cancelSettings()" [disabled]="settingsSame()"><mat-icon>cancel</mat-icon>
|
||||
<span i18n="Settings cancel button">Cancel</span>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -1,27 +1,38 @@
|
||||
<div style="margin-top: 14px;">
|
||||
<button class="back-button" (click)="goBack()" mat-icon-button><mat-icon>arrow_back</mat-icon></button>
|
||||
<div style="margin-bottom: 15px;">
|
||||
<h2 style="text-align: center;" *ngIf="subscription">
|
||||
{{subscription.name}} <ng-container *ngIf="subscription.paused" i18n="Paused suffix">(Paused)</ng-container>
|
||||
@if (subscription) {
|
||||
<h2 style="text-align: center;">
|
||||
{{subscription.name}}
|
||||
@if (subscription.paused) {
|
||||
<ng-container i18n="Paused suffix">(Paused)</ng-container>
|
||||
}
|
||||
<button class="edit-button" (click)="editSubscription()" [disabled]="downloading" matTooltip="Edit" i18n-matTooltip="Edit" mat-icon-button><mat-icon class="save-icon">edit</mat-icon></button>
|
||||
</h2>
|
||||
<mat-progress-bar style="width: 80%; margin: 0 auto; margin-top: 15px;" *ngIf="subscription && subscription.downloading" mode="indeterminate"></mat-progress-bar>
|
||||
</div>
|
||||
<mat-divider style="width: 80%; margin: 0 auto"></mat-divider>
|
||||
<br/>
|
||||
|
||||
<!-- Extra margin added for floating buttons to have room -->
|
||||
<div style="margin-bottom: 100px;" *ngIf="subscription">
|
||||
}
|
||||
@if (subscription && subscription.downloading) {
|
||||
<mat-progress-bar style="width: 80%; margin: 0 auto; margin-top: 15px;" mode="indeterminate"></mat-progress-bar>
|
||||
}
|
||||
</div>
|
||||
<mat-divider style="width: 80%; margin: 0 auto"></mat-divider>
|
||||
<br/>
|
||||
<!-- Extra margin added for floating buttons to have room -->
|
||||
@if (subscription) {
|
||||
<div style="margin-bottom: 100px;">
|
||||
<app-recent-videos #recentVideos [sub_id]="subscription.id"></app-recent-videos>
|
||||
</div>
|
||||
<div class="check-button">
|
||||
<ng-container *ngIf="subscription.downloading">
|
||||
}
|
||||
<div class="check-button">
|
||||
@if (subscription.downloading) {
|
||||
<button color="primary" (click)="cancelCheckSubscription()" [disabled]="cancel_clicked" matTooltip="Cancel subscription check" i18n-matTooltip="Cancel subscription check" mat-fab><mat-icon class="save-icon">cancel</mat-icon></button>
|
||||
</ng-container>
|
||||
<ng-container *ngIf="!subscription.downloading">
|
||||
} @else {
|
||||
<button color="primary" (click)="checkSubscription()" [disabled]="check_clicked" matTooltip="Check subscription" i18n-matTooltip="Check subscription" mat-fab><mat-icon class="save-icon">youtube_searched_for</mat-icon></button>
|
||||
</ng-container>
|
||||
</div>
|
||||
<button class="watch-button" color="primary" (click)="watchSubscription()" matTooltip="Play all" i18n-matTooltip="Play all" mat-fab><mat-icon class="save-icon">video_library</mat-icon></button>
|
||||
<button class="save-button" color="primary" (click)="downloadContent()" [disabled]="downloading" matTooltip="Download zip" i18n-matTooltip="Download zip" mat-fab><mat-icon class="save-icon">save</mat-icon><mat-spinner *ngIf="downloading" class="spinner" [diameter]="50"></mat-spinner></button>
|
||||
}
|
||||
</div>
|
||||
<button class="watch-button" color="primary" (click)="watchSubscription()" matTooltip="Play all" i18n-matTooltip="Play all" mat-fab><mat-icon class="save-icon">video_library</mat-icon></button>
|
||||
<button class="save-button" color="primary" (click)="downloadContent()" [disabled]="downloading" matTooltip="Download zip" i18n-matTooltip="Download zip" mat-fab><mat-icon class="save-icon">save</mat-icon>
|
||||
@if (downloading) {
|
||||
<mat-spinner class="spinner" [diameter]="50"></mat-spinner>
|
||||
}
|
||||
</button>
|
||||
</div>
|
||||
@@ -1,18 +1,23 @@
|
||||
<br/>
|
||||
|
||||
<h2 i18n="Subscriptions title" style="text-align: center; margin-bottom: 15px;">Your subscriptions</h2>
|
||||
|
||||
<mat-divider style="width: 80%; margin: 0 auto"></mat-divider>
|
||||
<br/>
|
||||
|
||||
<h4 i18n="Subscriptions channels title" style="text-align: center;">Channels</h4>
|
||||
<mat-nav-list class="sub-nav-list">
|
||||
<mat-list-item *ngFor="let sub of channel_subscriptions" style="pointer-events: none">
|
||||
@for (sub of channel_subscriptions; track sub) {
|
||||
<mat-list-item style="pointer-events: none">
|
||||
<a style="pointer-events: auto;" class="a-list-item" matListItemTitle (click)="goToSubscription(sub)">
|
||||
<strong *ngIf="sub.name">{{ sub.name }} <ng-container *ngIf="sub.paused" i18n="Paused suffix">(Paused)</ng-container></strong>
|
||||
<div *ngIf="!sub.name">
|
||||
@if (sub.name) {
|
||||
<strong>{{ sub.name }}
|
||||
@if (sub.paused) {
|
||||
<ng-container i18n="Paused suffix">(Paused)</ng-container>
|
||||
}
|
||||
</strong>
|
||||
} @else {
|
||||
<div>
|
||||
<ng-container i18n="Subscription playlist not available text">Name not available. Channel retrieval in progress.</ng-container>
|
||||
</div>
|
||||
}
|
||||
</a>
|
||||
<div style="pointer-events: auto; color: unset" matListItemMeta>
|
||||
<button matTooltip="Edit" i18n-matTooltip="Edit" mat-icon-button (click)="editSubscription(sub)">
|
||||
@@ -23,20 +28,29 @@
|
||||
</button>
|
||||
</div>
|
||||
</mat-list-item>
|
||||
</mat-nav-list>
|
||||
|
||||
<div style="width: 80%; margin: 0 auto; padding-left: 15px;" *ngIf="channel_subscriptions.length === 0 && subscriptions">
|
||||
}
|
||||
</mat-nav-list>
|
||||
@if (channel_subscriptions.length === 0 && subscriptions) {
|
||||
<div style="width: 80%; margin: 0 auto; padding-left: 15px;">
|
||||
<p i18n="No channel subscriptions text">You have no channel subscriptions.</p>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
}
|
||||
<h4 i18n="Subscriptions playlists title" style="text-align: center; margin-top: 10px;">Playlists</h4>
|
||||
<mat-nav-list class="sub-nav-list">
|
||||
<mat-list-item *ngFor="let sub of playlist_subscriptions" style="pointer-events: none">
|
||||
@for (sub of playlist_subscriptions; track sub) {
|
||||
<mat-list-item style="pointer-events: none">
|
||||
<a style="pointer-events: auto;" class="a-list-item" matListItemTitle (click)="goToSubscription(sub)">
|
||||
<strong *ngIf="sub.name">{{ sub.name }} <ng-container *ngIf="sub.paused" i18n="Paused suffix">(Paused)</ng-container></strong>
|
||||
<div *ngIf="!sub.name">
|
||||
@if (sub.name) {
|
||||
<strong>{{ sub.name }}
|
||||
@if (sub.paused) {
|
||||
<ng-container i18n="Paused suffix">(Paused)</ng-container>
|
||||
}
|
||||
</strong>
|
||||
} @else {
|
||||
<div>
|
||||
<ng-container i18n="Subscription playlist not available text">Name not available. Channel retrieval in progress.</ng-container>
|
||||
</div>
|
||||
}
|
||||
</a>
|
||||
<div style="pointer-events: auto; color: unset" matListItemMeta>
|
||||
<button matTooltip="Edit" i18n-matTooltip="Edit" mat-icon-button (click)="editSubscription(sub)">
|
||||
@@ -47,14 +61,16 @@
|
||||
</button>
|
||||
</div>
|
||||
</mat-list-item>
|
||||
}
|
||||
</mat-nav-list>
|
||||
|
||||
<div style="width: 80%; margin: 0 auto; padding-left: 15px;" *ngIf="playlist_subscriptions.length === 0 && subscriptions">
|
||||
@if (playlist_subscriptions.length === 0 && subscriptions) {
|
||||
<div style="width: 80%; margin: 0 auto; padding-left: 15px;">
|
||||
<p i18n="No playlist subscriptions text">You have no playlist subscriptions.</p>
|
||||
</div>
|
||||
|
||||
<div style="margin: 0 auto; width: 80%" *ngIf="subscriptions_loading">
|
||||
</div>
|
||||
}
|
||||
@if (subscriptions_loading) {
|
||||
<div style="margin: 0 auto; width: 80%">
|
||||
<mat-progress-bar mode="indeterminate"></mat-progress-bar>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
}
|
||||
<button class="add-subscription-button" (click)="openSubscribeDialog()" matTooltip="Add subscription" i18n-matTooltip="Add subscription" mat-fab><mat-icon>add</mat-icon></button>
|
||||
@@ -2,17 +2,27 @@
|
||||
<div style="display: inline-block">
|
||||
<ng-container i18n="Select a version">Select a version:</ng-container>
|
||||
</div>
|
||||
<div *ngIf="availableVersions" style="display: inline-block; margin-left: 15px;">
|
||||
@if (availableVersions) {
|
||||
<div style="display: inline-block; margin-left: 15px;">
|
||||
<mat-form-field>
|
||||
<mat-select [(ngModel)]="selectedVersion">
|
||||
<mat-option *ngFor="let version of availableVersionsFiltered" [value]="version['tag_name']">
|
||||
@for (version of availableVersionsFiltered; track version) {
|
||||
<mat-option [value]="version['tag_name']">
|
||||
{{version['tag_name'] + (version === latestStableRelease ? ' - Latest Stable' : '') + (version['tag_name'] === CURRENT_VERSION ? ' - Current Version' : '')}}
|
||||
</mat-option>
|
||||
}
|
||||
</mat-select>
|
||||
</mat-form-field>
|
||||
</div>
|
||||
<div *ngIf="selectedVersion && selectedVersion !== CURRENT_VERSION" style="display: inline-block; margin-left: 15px;">
|
||||
}
|
||||
@if (selectedVersion && selectedVersion !== CURRENT_VERSION) {
|
||||
<div style="display: inline-block; margin-left: 15px;">
|
||||
<button (click)="updateServer()" color="accent" mat-raised-button><mat-icon>update</mat-icon>
|
||||
<ng-container *ngIf="selectedVersion > CURRENT_VERSION">Upgrade to</ng-container><ng-container *ngIf="selectedVersion < CURRENT_VERSION">Downgrade to</ng-container> {{selectedVersion}}</button>
|
||||
@if (selectedVersion > CURRENT_VERSION) {
|
||||
<ng-container i18n="Upgrade to">Upgrade to</ng-container>
|
||||
} @else {
|
||||
<ng-container i18n="Downgrade to">Downgrade to</ng-container>
|
||||
} {{selectedVersion}}</button>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
Reference in New Issue
Block a user