Polish webhook integration row alignment in settings UI.
Shrink the enabled column, expand endpoint space, and slightly widen timeout numeric input to improve readability and alignment in the webhook integration controls. Made-with: Cursor
This commit is contained in:
parent
7f7d9909f6
commit
b79ffdbbcf
2 changed files with 502 additions and 598 deletions
|
|
@ -49,18 +49,13 @@
|
||||||
</div>
|
</div>
|
||||||
-->
|
-->
|
||||||
<div class="navbar-nav ms-auto">
|
<div class="navbar-nav ms-auto">
|
||||||
<button type="button" class="btn btn-link nav-link py-2 px-0 px-sm-2"
|
<button type="button" class="btn btn-link nav-link py-2 px-0 px-sm-2" aria-label="Open settings"
|
||||||
aria-label="Open settings"
|
title="UI settings" (click)="openSettingsModal()">
|
||||||
title="UI settings"
|
|
||||||
(click)="openSettingsModal()">
|
|
||||||
<fa-icon [icon]="faGear" />
|
<fa-icon [icon]="faGear" />
|
||||||
</button>
|
</button>
|
||||||
<div class="nav-item dropdown" ngbDropdown placement="bottom-end">
|
<div class="nav-item dropdown" ngbDropdown placement="bottom-end">
|
||||||
<button class="btn btn-link nav-link py-2 px-0 px-sm-2 dropdown-toggle d-flex align-items-center"
|
<button class="btn btn-link nav-link py-2 px-0 px-sm-2 dropdown-toggle d-flex align-items-center"
|
||||||
id="theme-select"
|
id="theme-select" type="button" aria-expanded="false" ngbDropdownToggle>
|
||||||
type="button"
|
|
||||||
aria-expanded="false"
|
|
||||||
ngbDropdownToggle>
|
|
||||||
@if(activeTheme){
|
@if(activeTheme){
|
||||||
<fa-icon [icon]="activeTheme.icon" />
|
<fa-icon [icon]="activeTheme.icon" />
|
||||||
}
|
}
|
||||||
|
|
@ -68,16 +63,13 @@
|
||||||
<ul class="dropdown-menu dropdown-menu-end position-absolute" aria-labelledby="theme-select" ngbDropdownMenu>
|
<ul class="dropdown-menu dropdown-menu-end position-absolute" aria-labelledby="theme-select" ngbDropdownMenu>
|
||||||
@for (theme of themes; track theme) {
|
@for (theme of themes; track theme) {
|
||||||
<li>
|
<li>
|
||||||
<button type="button" class="dropdown-item d-flex align-items-center"
|
<button type="button" class="dropdown-item d-flex align-items-center" [class.active]="activeTheme === theme"
|
||||||
[class.active]="activeTheme === theme"
|
ngbDropdownItem (click)="themeChanged(theme)">
|
||||||
ngbDropdownItem
|
|
||||||
(click)="themeChanged(theme)">
|
|
||||||
<span class="me-2 opacity-50">
|
<span class="me-2 opacity-50">
|
||||||
<fa-icon [icon]="theme.icon" />
|
<fa-icon [icon]="theme.icon" />
|
||||||
</span>
|
</span>
|
||||||
{{ theme.displayName }}
|
{{ theme.displayName }}
|
||||||
<span class="ms-auto"
|
<span class="ms-auto" [class.d-none]="activeTheme !== theme">
|
||||||
[class.d-none]="activeTheme !== theme">
|
|
||||||
<fa-icon [icon]="faCheck" />
|
<fa-icon [icon]="faCheck" />
|
||||||
</span>
|
</span>
|
||||||
</button>
|
</button>
|
||||||
|
|
@ -106,11 +98,8 @@
|
||||||
<span class="spinner-border spinner-border-sm me-2" role="status"></span>
|
<span class="spinner-border spinner-border-sm me-2" role="status"></span>
|
||||||
Adding...
|
Adding...
|
||||||
</button>
|
</button>
|
||||||
<button class="btn btn-outline-danger btn-lg px-3 add-cancel-btn"
|
<button class="btn btn-outline-danger btn-lg px-3 add-cancel-btn" type="button" (click)="cancelAdding()"
|
||||||
type="button"
|
aria-label="Cancel adding URL" title="Cancel adding URL">
|
||||||
(click)="cancelAdding()"
|
|
||||||
aria-label="Cancel adding URL"
|
|
||||||
title="Cancel adding URL">
|
|
||||||
<fa-icon [icon]="faTimesCircle" class="me-1" /> Cancel
|
<fa-icon [icon]="faTimesCircle" class="me-1" /> Cancel
|
||||||
</button>
|
</button>
|
||||||
} @else if (subscribeInProgress) {
|
} @else if (subscribeInProgress) {
|
||||||
|
|
@ -122,13 +111,11 @@
|
||||||
Subscribing...
|
Subscribing...
|
||||||
</button>
|
</button>
|
||||||
} @else {
|
} @else {
|
||||||
<button class="btn btn-primary btn-lg px-4" type="submit"
|
<button class="btn btn-primary btn-lg px-4" type="submit" (click)="addDownload()"
|
||||||
(click)="addDownload()"
|
|
||||||
[disabled]="downloads.loading">
|
[disabled]="downloads.loading">
|
||||||
Download
|
Download
|
||||||
</button>
|
</button>
|
||||||
<button class="btn btn-outline-secondary btn-lg px-3" type="button"
|
<button class="btn btn-outline-secondary btn-lg px-3" type="button" (click)="addSubscription()"
|
||||||
(click)="addSubscription()"
|
|
||||||
[disabled]="downloads.loading">
|
[disabled]="downloads.loading">
|
||||||
Subscribe
|
Subscribe
|
||||||
</button>
|
</button>
|
||||||
|
|
@ -137,12 +124,8 @@
|
||||||
|
|
||||||
<!-- Narrow viewports: full-width field, then Bootstrap btn-group (no faux input-group strip) -->
|
<!-- Narrow viewports: full-width field, then Bootstrap btn-group (no faux input-group strip) -->
|
||||||
<div class="vstack gap-2 d-md-none">
|
<div class="vstack gap-2 d-md-none">
|
||||||
<input type="text"
|
<input type="text" autocomplete="off" spellcheck="false" class="form-control form-control-lg"
|
||||||
autocomplete="off"
|
placeholder="Enter video, channel, or playlist URL" [(ngModel)]="addUrl"
|
||||||
spellcheck="false"
|
|
||||||
class="form-control form-control-lg"
|
|
||||||
placeholder="Enter video, channel, or playlist URL"
|
|
||||||
[(ngModel)]="addUrl"
|
|
||||||
[ngModelOptions]="{standalone: true}"
|
[ngModelOptions]="{standalone: true}"
|
||||||
[disabled]="addInProgress || subscribeInProgress || downloads.loading">
|
[disabled]="addInProgress || subscribeInProgress || downloads.loading">
|
||||||
<div class="btn-group w-100" role="group" aria-label="Download or subscribe">
|
<div class="btn-group w-100" role="group" aria-label="Download or subscribe">
|
||||||
|
|
@ -152,12 +135,8 @@
|
||||||
|
|
||||||
<!-- md and up: standard input-group so Bootstrap handles fused borders -->
|
<!-- md and up: standard input-group so Bootstrap handles fused borders -->
|
||||||
<div class="input-group input-group-lg shadow-sm d-none d-md-flex">
|
<div class="input-group input-group-lg shadow-sm d-none d-md-flex">
|
||||||
<input type="text"
|
<input type="text" autocomplete="off" spellcheck="false" class="form-control form-control-lg"
|
||||||
autocomplete="off"
|
placeholder="Enter video, channel, or playlist URL" [(ngModel)]="addUrl"
|
||||||
spellcheck="false"
|
|
||||||
class="form-control form-control-lg"
|
|
||||||
placeholder="Enter video, channel, or playlist URL"
|
|
||||||
[(ngModel)]="addUrl"
|
|
||||||
[ngModelOptions]="{standalone: true}"
|
[ngModelOptions]="{standalone: true}"
|
||||||
[disabled]="addInProgress || subscribeInProgress || downloads.loading">
|
[disabled]="addInProgress || subscribeInProgress || downloads.loading">
|
||||||
<ng-container [ngTemplateOutlet]="urlBarActions" />
|
<ng-container [ngTemplateOutlet]="urlBarActions" />
|
||||||
|
|
@ -171,10 +150,7 @@
|
||||||
<div class="col-md-3">
|
<div class="col-md-3">
|
||||||
<div class="input-group">
|
<div class="input-group">
|
||||||
<span class="input-group-text">Type</span>
|
<span class="input-group-text">Type</span>
|
||||||
<select class="form-select"
|
<select class="form-select" name="downloadType" [(ngModel)]="downloadType" (change)="downloadTypeChanged()"
|
||||||
name="downloadType"
|
|
||||||
[(ngModel)]="downloadType"
|
|
||||||
(change)="downloadTypeChanged()"
|
|
||||||
[disabled]="addInProgress || subscribeInProgress || downloads.loading">
|
[disabled]="addInProgress || subscribeInProgress || downloads.loading">
|
||||||
@for (type of downloadTypes; track type.id) {
|
@for (type of downloadTypes; track type.id) {
|
||||||
<option [ngValue]="type.id">{{ type.text }}</option>
|
<option [ngValue]="type.id">{{ type.text }}</option>
|
||||||
|
|
@ -185,10 +161,7 @@
|
||||||
<div class="col-md-3">
|
<div class="col-md-3">
|
||||||
<div class="input-group">
|
<div class="input-group">
|
||||||
<span class="input-group-text">Codec</span>
|
<span class="input-group-text">Codec</span>
|
||||||
<select class="form-select"
|
<select class="form-select" name="codec" [(ngModel)]="codec" (change)="codecChanged()"
|
||||||
name="codec"
|
|
||||||
[(ngModel)]="codec"
|
|
||||||
(change)="codecChanged()"
|
|
||||||
[disabled]="addInProgress || subscribeInProgress || downloads.loading">
|
[disabled]="addInProgress || subscribeInProgress || downloads.loading">
|
||||||
@for (vc of videoCodecs; track vc.id) {
|
@for (vc of videoCodecs; track vc.id) {
|
||||||
<option [ngValue]="vc.id">{{ vc.text }}</option>
|
<option [ngValue]="vc.id">{{ vc.text }}</option>
|
||||||
|
|
@ -199,10 +172,7 @@
|
||||||
<div class="col-md-3">
|
<div class="col-md-3">
|
||||||
<div class="input-group">
|
<div class="input-group">
|
||||||
<span class="input-group-text">Format</span>
|
<span class="input-group-text">Format</span>
|
||||||
<select class="form-select"
|
<select class="form-select" name="format" [(ngModel)]="format" (change)="formatChanged()"
|
||||||
name="format"
|
|
||||||
[(ngModel)]="format"
|
|
||||||
(change)="formatChanged()"
|
|
||||||
[disabled]="addInProgress || subscribeInProgress || downloads.loading">
|
[disabled]="addInProgress || subscribeInProgress || downloads.loading">
|
||||||
@for (f of formatOptions; track f.id) {
|
@for (f of formatOptions; track f.id) {
|
||||||
<option [ngValue]="f.id">{{ f.text }}</option>
|
<option [ngValue]="f.id">{{ f.text }}</option>
|
||||||
|
|
@ -213,10 +183,7 @@
|
||||||
<div class="col-md-3">
|
<div class="col-md-3">
|
||||||
<div class="input-group">
|
<div class="input-group">
|
||||||
<span class="input-group-text">Quality</span>
|
<span class="input-group-text">Quality</span>
|
||||||
<select class="form-select"
|
<select class="form-select" name="quality" [(ngModel)]="quality" (change)="qualityChanged()"
|
||||||
name="quality"
|
|
||||||
[(ngModel)]="quality"
|
|
||||||
(change)="qualityChanged()"
|
|
||||||
[disabled]="addInProgress || subscribeInProgress || downloads.loading || !showQualitySelector()">
|
[disabled]="addInProgress || subscribeInProgress || downloads.loading || !showQualitySelector()">
|
||||||
@for (q of qualities; track q.id) {
|
@for (q of qualities; track q.id) {
|
||||||
<option [ngValue]="q.id">{{ q.text }}</option>
|
<option [ngValue]="q.id">{{ q.text }}</option>
|
||||||
|
|
@ -228,10 +195,7 @@
|
||||||
<div class="col-md-4">
|
<div class="col-md-4">
|
||||||
<div class="input-group">
|
<div class="input-group">
|
||||||
<span class="input-group-text">Type</span>
|
<span class="input-group-text">Type</span>
|
||||||
<select class="form-select"
|
<select class="form-select" name="downloadType" [(ngModel)]="downloadType" (change)="downloadTypeChanged()"
|
||||||
name="downloadType"
|
|
||||||
[(ngModel)]="downloadType"
|
|
||||||
(change)="downloadTypeChanged()"
|
|
||||||
[disabled]="addInProgress || subscribeInProgress || downloads.loading">
|
[disabled]="addInProgress || subscribeInProgress || downloads.loading">
|
||||||
@for (type of downloadTypes; track type.id) {
|
@for (type of downloadTypes; track type.id) {
|
||||||
<option [ngValue]="type.id">{{ type.text }}</option>
|
<option [ngValue]="type.id">{{ type.text }}</option>
|
||||||
|
|
@ -242,10 +206,7 @@
|
||||||
<div class="col-md-4">
|
<div class="col-md-4">
|
||||||
<div class="input-group">
|
<div class="input-group">
|
||||||
<span class="input-group-text">Format</span>
|
<span class="input-group-text">Format</span>
|
||||||
<select class="form-select"
|
<select class="form-select" name="format" [(ngModel)]="format" (change)="formatChanged()"
|
||||||
name="format"
|
|
||||||
[(ngModel)]="format"
|
|
||||||
(change)="formatChanged()"
|
|
||||||
[disabled]="addInProgress || subscribeInProgress || downloads.loading">
|
[disabled]="addInProgress || subscribeInProgress || downloads.loading">
|
||||||
@for (f of formatOptions; track f.id) {
|
@for (f of formatOptions; track f.id) {
|
||||||
<option [ngValue]="f.id">{{ f.text }}</option>
|
<option [ngValue]="f.id">{{ f.text }}</option>
|
||||||
|
|
@ -256,10 +217,7 @@
|
||||||
<div class="col-md-4">
|
<div class="col-md-4">
|
||||||
<div class="input-group">
|
<div class="input-group">
|
||||||
<span class="input-group-text">Quality</span>
|
<span class="input-group-text">Quality</span>
|
||||||
<select class="form-select"
|
<select class="form-select" name="quality" [(ngModel)]="quality" (change)="qualityChanged()"
|
||||||
name="quality"
|
|
||||||
[(ngModel)]="quality"
|
|
||||||
(change)="qualityChanged()"
|
|
||||||
[disabled]="addInProgress || subscribeInProgress || downloads.loading">
|
[disabled]="addInProgress || subscribeInProgress || downloads.loading">
|
||||||
@for (q of qualities; track q.id) {
|
@for (q of qualities; track q.id) {
|
||||||
<option [ngValue]="q.id">{{ q.text }}</option>
|
<option [ngValue]="q.id">{{ q.text }}</option>
|
||||||
|
|
@ -272,10 +230,7 @@
|
||||||
<div class="col-12 col-md-6 col-lg-3">
|
<div class="col-12 col-md-6 col-lg-3">
|
||||||
<div class="input-group">
|
<div class="input-group">
|
||||||
<span class="input-group-text">Type</span>
|
<span class="input-group-text">Type</span>
|
||||||
<select class="form-select"
|
<select class="form-select" name="downloadType" [(ngModel)]="downloadType" (change)="downloadTypeChanged()"
|
||||||
name="downloadType"
|
|
||||||
[(ngModel)]="downloadType"
|
|
||||||
(change)="downloadTypeChanged()"
|
|
||||||
[disabled]="addInProgress || subscribeInProgress || downloads.loading">
|
[disabled]="addInProgress || subscribeInProgress || downloads.loading">
|
||||||
@for (type of downloadTypes; track type.id) {
|
@for (type of downloadTypes; track type.id) {
|
||||||
<option [ngValue]="type.id">{{ type.text }}</option>
|
<option [ngValue]="type.id">{{ type.text }}</option>
|
||||||
|
|
@ -286,10 +241,7 @@
|
||||||
<div class="col-12 col-md-6 col-lg-3">
|
<div class="col-12 col-md-6 col-lg-3">
|
||||||
<div class="input-group">
|
<div class="input-group">
|
||||||
<span class="input-group-text">Format</span>
|
<span class="input-group-text">Format</span>
|
||||||
<select class="form-select"
|
<select class="form-select" name="format" [(ngModel)]="format" (change)="formatChanged()"
|
||||||
name="format"
|
|
||||||
[(ngModel)]="format"
|
|
||||||
(change)="formatChanged()"
|
|
||||||
[disabled]="addInProgress || subscribeInProgress || downloads.loading"
|
[disabled]="addInProgress || subscribeInProgress || downloads.loading"
|
||||||
ngbTooltip="Subtitle output format for captions mode">
|
ngbTooltip="Subtitle output format for captions mode">
|
||||||
@for (f of formatOptions; track f.id) {
|
@for (f of formatOptions; track f.id) {
|
||||||
|
|
@ -301,14 +253,9 @@
|
||||||
<div class="col-12 col-md-6 col-lg-3">
|
<div class="col-12 col-md-6 col-lg-3">
|
||||||
<div class="input-group">
|
<div class="input-group">
|
||||||
<span class="input-group-text">Language</span>
|
<span class="input-group-text">Language</span>
|
||||||
<input class="form-control"
|
<input class="form-control" type="text" list="subtitleLanguageOptions" name="subtitleLanguage"
|
||||||
type="text"
|
[(ngModel)]="subtitleLanguage" (change)="subtitleLanguageChanged()"
|
||||||
list="subtitleLanguageOptions"
|
[disabled]="addInProgress || subscribeInProgress || downloads.loading" placeholder="e.g. en, es, zh-Hans"
|
||||||
name="subtitleLanguage"
|
|
||||||
[(ngModel)]="subtitleLanguage"
|
|
||||||
(change)="subtitleLanguageChanged()"
|
|
||||||
[disabled]="addInProgress || subscribeInProgress || downloads.loading"
|
|
||||||
placeholder="e.g. en, es, zh-Hans"
|
|
||||||
ngbTooltip="Subtitle language (you can type any language code)">
|
ngbTooltip="Subtitle language (you can type any language code)">
|
||||||
<datalist id="subtitleLanguageOptions">
|
<datalist id="subtitleLanguageOptions">
|
||||||
@for (lang of subtitleLanguages; track lang.id) {
|
@for (lang of subtitleLanguages; track lang.id) {
|
||||||
|
|
@ -320,10 +267,7 @@
|
||||||
<div class="col-12 col-md-6 col-lg-3">
|
<div class="col-12 col-md-6 col-lg-3">
|
||||||
<div class="input-group">
|
<div class="input-group">
|
||||||
<span class="input-group-text">Subtitle Source</span>
|
<span class="input-group-text">Subtitle Source</span>
|
||||||
<select class="form-select"
|
<select class="form-select" name="subtitleMode" [(ngModel)]="subtitleMode" (change)="subtitleModeChanged()"
|
||||||
name="subtitleMode"
|
|
||||||
[(ngModel)]="subtitleMode"
|
|
||||||
(change)="subtitleModeChanged()"
|
|
||||||
[disabled]="addInProgress || subscribeInProgress || downloads.loading"
|
[disabled]="addInProgress || subscribeInProgress || downloads.loading"
|
||||||
ngbTooltip="Choose manual, auto, or fallback preference for captions mode">
|
ngbTooltip="Choose manual, auto, or fallback preference for captions mode">
|
||||||
@for (mode of subtitleModes; track mode.id) {
|
@for (mode of subtitleModes; track mode.id) {
|
||||||
|
|
@ -336,10 +280,7 @@
|
||||||
<div class="col-md-6">
|
<div class="col-md-6">
|
||||||
<div class="input-group">
|
<div class="input-group">
|
||||||
<span class="input-group-text">Type</span>
|
<span class="input-group-text">Type</span>
|
||||||
<select class="form-select"
|
<select class="form-select" name="downloadType" [(ngModel)]="downloadType" (change)="downloadTypeChanged()"
|
||||||
name="downloadType"
|
|
||||||
[(ngModel)]="downloadType"
|
|
||||||
(change)="downloadTypeChanged()"
|
|
||||||
[disabled]="addInProgress || subscribeInProgress || downloads.loading">
|
[disabled]="addInProgress || subscribeInProgress || downloads.loading">
|
||||||
@for (type of downloadTypes; track type.id) {
|
@for (type of downloadTypes; track type.id) {
|
||||||
<option [ngValue]="type.id">{{ type.text }}</option>
|
<option [ngValue]="type.id">{{ type.text }}</option>
|
||||||
|
|
@ -358,15 +299,10 @@
|
||||||
|
|
||||||
<div class="row mb-3 g-3">
|
<div class="row mb-3 g-3">
|
||||||
<div class="col-12 text-start">
|
<div class="col-12 text-start">
|
||||||
<button type="button"
|
<button type="button" class="btn btn-link p-0 text-decoration-none" (click)="toggleAdvanced()"
|
||||||
class="btn btn-link p-0 text-decoration-none"
|
[attr.aria-expanded]="isAdvancedOpen" aria-controls="advancedOptions">
|
||||||
(click)="toggleAdvanced()"
|
|
||||||
[attr.aria-expanded]="isAdvancedOpen"
|
|
||||||
aria-controls="advancedOptions">
|
|
||||||
Advanced Options
|
Advanced Options
|
||||||
<fa-icon
|
<fa-icon [icon]="isAdvancedOpen ? faChevronDown : faChevronRight" class="ms-1" />
|
||||||
[icon]="isAdvancedOpen ? faChevronDown : faChevronRight"
|
|
||||||
class="ms-1" />
|
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -381,10 +317,7 @@
|
||||||
<div class="col-md-6">
|
<div class="col-md-6">
|
||||||
<div class="input-group">
|
<div class="input-group">
|
||||||
<span class="input-group-text">Auto Start</span>
|
<span class="input-group-text">Auto Start</span>
|
||||||
<select class="form-select"
|
<select class="form-select" name="autoStart" [(ngModel)]="autoStart" (change)="autoStartChanged()"
|
||||||
name="autoStart"
|
|
||||||
[(ngModel)]="autoStart"
|
|
||||||
(change)="autoStartChanged()"
|
|
||||||
[disabled]="addInProgress || subscribeInProgress || downloads.loading"
|
[disabled]="addInProgress || subscribeInProgress || downloads.loading"
|
||||||
ngbTooltip="Automatically start downloads when added">
|
ngbTooltip="Automatically start downloads when added">
|
||||||
<option [ngValue]="true">Yes</option>
|
<option [ngValue]="true">Yes</option>
|
||||||
|
|
@ -396,18 +329,10 @@
|
||||||
<div class="input-group">
|
<div class="input-group">
|
||||||
<span class="input-group-text">Download Folder</span>
|
<span class="input-group-text">Download Folder</span>
|
||||||
@if (customDirs$ | async; as customDirs) {
|
@if (customDirs$ | async; as customDirs) {
|
||||||
<ng-select [items]="customDirs"
|
<ng-select [items]="customDirs" placeholder="Default" [addTag]="allowCustomDir.bind(this)"
|
||||||
placeholder="Default"
|
addTagText="Create directory" bindLabel="folder" [(ngModel)]="folder"
|
||||||
[addTag]="allowCustomDir.bind(this)"
|
[disabled]="addInProgress || subscribeInProgress || downloads.loading" [virtualScroll]="true"
|
||||||
addTagText="Create directory"
|
[clearable]="true" [loading]="downloads.loading" [searchable]="true" [closeOnSelect]="true"
|
||||||
bindLabel="folder"
|
|
||||||
[(ngModel)]="folder"
|
|
||||||
[disabled]="addInProgress || subscribeInProgress || downloads.loading"
|
|
||||||
[virtualScroll]="true"
|
|
||||||
[clearable]="true"
|
|
||||||
[loading]="downloads.loading"
|
|
||||||
[searchable]="true"
|
|
||||||
[closeOnSelect]="true"
|
|
||||||
ngbTooltip="Choose where to save downloads. Type to create a new folder." />
|
ngbTooltip="Choose where to save downloads. Type to create a new folder." />
|
||||||
}
|
}
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -416,10 +341,7 @@
|
||||||
<div class="col-md-6">
|
<div class="col-md-6">
|
||||||
<div class="input-group">
|
<div class="input-group">
|
||||||
<span class="input-group-text">Custom Name Prefix</span>
|
<span class="input-group-text">Custom Name Prefix</span>
|
||||||
<input type="text"
|
<input type="text" class="form-control" placeholder="Default" name="customNamePrefix"
|
||||||
class="form-control"
|
|
||||||
placeholder="Default"
|
|
||||||
name="customNamePrefix"
|
|
||||||
[(ngModel)]="customNamePrefix"
|
[(ngModel)]="customNamePrefix"
|
||||||
[disabled]="addInProgress || subscribeInProgress || downloads.loading"
|
[disabled]="addInProgress || subscribeInProgress || downloads.loading"
|
||||||
ngbTooltip="Add a prefix to downloaded filenames">
|
ngbTooltip="Add a prefix to downloaded filenames">
|
||||||
|
|
@ -428,13 +350,8 @@
|
||||||
<div class="col-md-6">
|
<div class="col-md-6">
|
||||||
<div class="input-group">
|
<div class="input-group">
|
||||||
<span class="input-group-text">Items Limit</span>
|
<span class="input-group-text">Items Limit</span>
|
||||||
<input type="number"
|
<input type="number" min="0" class="form-control" placeholder="Default" name="playlistItemLimit"
|
||||||
min="0"
|
(keydown)="isNumber($event)" [(ngModel)]="playlistItemLimit"
|
||||||
class="form-control"
|
|
||||||
placeholder="Default"
|
|
||||||
name="playlistItemLimit"
|
|
||||||
(keydown)="isNumber($event)"
|
|
||||||
[(ngModel)]="playlistItemLimit"
|
|
||||||
[disabled]="addInProgress || subscribeInProgress || downloads.loading"
|
[disabled]="addInProgress || subscribeInProgress || downloads.loading"
|
||||||
ngbTooltip="Maximum number of items to download from a playlist or channel (0 = no limit)">
|
ngbTooltip="Maximum number of items to download from a playlist or channel (0 = no limit)">
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -442,12 +359,8 @@
|
||||||
<div class="col-md-6">
|
<div class="col-md-6">
|
||||||
<div class="input-group">
|
<div class="input-group">
|
||||||
<span class="input-group-text">Subscription Check (min)</span>
|
<span class="input-group-text">Subscription Check (min)</span>
|
||||||
<input type="number"
|
<input type="number" min="1" class="form-control" name="checkIntervalMinutes"
|
||||||
min="1"
|
(keydown)="isNumber($event)" [(ngModel)]="checkIntervalMinutes"
|
||||||
class="form-control"
|
|
||||||
name="checkIntervalMinutes"
|
|
||||||
(keydown)="isNumber($event)"
|
|
||||||
[(ngModel)]="checkIntervalMinutes"
|
|
||||||
(ngModelChange)="checkIntervalChanged()"
|
(ngModelChange)="checkIntervalChanged()"
|
||||||
[disabled]="addInProgress || subscribeInProgress || downloads.loading"
|
[disabled]="addInProgress || subscribeInProgress || downloads.loading"
|
||||||
ngbTooltip="How often to poll subscriptions for new videos">
|
ngbTooltip="How often to poll subscriptions for new videos">
|
||||||
|
|
@ -469,7 +382,8 @@
|
||||||
<div class="input-group">
|
<div class="input-group">
|
||||||
<span class="input-group-text">Template</span>
|
<span class="input-group-text">Template</span>
|
||||||
<input type="text" class="form-control" name="chapterTemplate" [(ngModel)]="chapterTemplate"
|
<input type="text" class="form-control" name="chapterTemplate" [(ngModel)]="chapterTemplate"
|
||||||
(change)="chapterTemplateChanged()" [disabled]="addInProgress || subscribeInProgress || downloads.loading"
|
(change)="chapterTemplateChanged()"
|
||||||
|
[disabled]="addInProgress || subscribeInProgress || downloads.loading"
|
||||||
ngbTooltip="Output template for chapter files">
|
ngbTooltip="Output template for chapter files">
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -486,7 +400,7 @@
|
||||||
<div class="col-12">
|
<div class="col-12">
|
||||||
<div class="action-group-label">Webhook Integration</div>
|
<div class="action-group-label">Webhook Integration</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-12 col-md-3 d-flex align-items-center">
|
<div class="col-12 col-md-2 d-flex align-items-center">
|
||||||
<div class="form-check form-switch mb-0">
|
<div class="form-check form-switch mb-0">
|
||||||
<input class="form-check-input" type="checkbox" role="switch" id="webhookEnabled"
|
<input class="form-check-input" type="checkbox" role="switch" id="webhookEnabled"
|
||||||
name="webhookEnabled" [(ngModel)]="webhookEnabled"
|
name="webhookEnabled" [(ngModel)]="webhookEnabled"
|
||||||
|
|
@ -494,24 +408,18 @@
|
||||||
<label class="form-check-label" for="webhookEnabled">Enabled</label>
|
<label class="form-check-label" for="webhookEnabled">Enabled</label>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-12 col-md-7">
|
<div class="col-12 col-md-8">
|
||||||
<div class="input-group">
|
<div class="input-group">
|
||||||
<span class="input-group-text">Endpoint</span>
|
<span class="input-group-text">Endpoint</span>
|
||||||
<input type="text"
|
<input type="text" class="form-control" placeholder="http://localhost:9995/api/hash-service/1"
|
||||||
class="form-control"
|
name="webhookEndpoint" [(ngModel)]="webhookEndpoint"
|
||||||
placeholder="http://localhost:9995/api/hash-service/1"
|
|
||||||
name="webhookEndpoint"
|
|
||||||
[(ngModel)]="webhookEndpoint"
|
|
||||||
[disabled]="addInProgress || subscribeInProgress || downloads.loading">
|
[disabled]="addInProgress || subscribeInProgress || downloads.loading">
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-6 col-md-2">
|
<div class="col-6 col-md-2">
|
||||||
<div class="input-group">
|
<div class="input-group">
|
||||||
<span class="input-group-text">Timeout</span>
|
<span class="input-group-text">Timeout</span>
|
||||||
<input type="number"
|
<input type="number" min="1" class="form-control webhook-timeout-input" name="webhookTimeoutSec"
|
||||||
min="1"
|
|
||||||
class="form-control"
|
|
||||||
name="webhookTimeoutSec"
|
|
||||||
[(ngModel)]="webhookTimeoutSec"
|
[(ngModel)]="webhookTimeoutSec"
|
||||||
[disabled]="addInProgress || subscribeInProgress || downloads.loading">
|
[disabled]="addInProgress || subscribeInProgress || downloads.loading">
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -519,7 +427,7 @@
|
||||||
<div class="col-12 col-md-7">
|
<div class="col-12 col-md-7">
|
||||||
<div class="webhook-action-row">
|
<div class="webhook-action-row">
|
||||||
<button type="button" class="btn btn-secondary" (click)="saveWebhookSettings()">
|
<button type="button" class="btn btn-secondary" (click)="saveWebhookSettings()">
|
||||||
Save Webhook Settings
|
Save Webhook
|
||||||
</button>
|
</button>
|
||||||
<button type="button" class="btn btn-outline-secondary" (click)="testWebhookService()">
|
<button type="button" class="btn btn-outline-secondary" (click)="testWebhookService()">
|
||||||
Test Service
|
Test Service
|
||||||
|
|
@ -546,13 +454,11 @@
|
||||||
<div class="col-md-4">
|
<div class="col-md-4">
|
||||||
<div class="action-group-label">Cookies</div>
|
<div class="action-group-label">Cookies</div>
|
||||||
<input type="file" id="cookie-upload" class="d-none" accept=".txt"
|
<input type="file" id="cookie-upload" class="d-none" accept=".txt"
|
||||||
(change)="onCookieFileSelect($event)"
|
(change)="onCookieFileSelect($event)" [disabled]="cookieUploadInProgress || addInProgress">
|
||||||
[disabled]="cookieUploadInProgress || addInProgress">
|
|
||||||
<div class="btn-group w-100" role="group">
|
<div class="btn-group w-100" role="group">
|
||||||
<label class="btn mb-0"
|
<label class="btn mb-0"
|
||||||
[class]="hasCookies ? 'btn cookie-active-btn mb-0' : 'btn cookie-btn mb-0'"
|
[class]="hasCookies ? 'btn cookie-active-btn mb-0' : 'btn cookie-btn mb-0'"
|
||||||
[class.disabled]="cookieUploadInProgress || addInProgress"
|
[class.disabled]="cookieUploadInProgress || addInProgress" for="cookie-upload"
|
||||||
for="cookie-upload"
|
|
||||||
ngbTooltip="Upload a cookies.txt file for authenticated downloads">
|
ngbTooltip="Upload a cookies.txt file for authenticated downloads">
|
||||||
@if (cookieUploadInProgress) {
|
@if (cookieUploadInProgress) {
|
||||||
<span class="spinner-border spinner-border-sm me-2" role="status"></span>
|
<span class="spinner-border spinner-border-sm me-2" role="status"></span>
|
||||||
|
|
@ -562,10 +468,8 @@
|
||||||
{{ hasCookies ? 'Replace Cookies' : 'Upload Cookies' }}
|
{{ hasCookies ? 'Replace Cookies' : 'Upload Cookies' }}
|
||||||
</label>
|
</label>
|
||||||
@if (hasCookies) {
|
@if (hasCookies) {
|
||||||
<button type="button" class="btn btn-outline-danger"
|
<button type="button" class="btn btn-outline-danger" (click)="deleteCookies()"
|
||||||
(click)="deleteCookies()"
|
[disabled]="cookieUploadInProgress || addInProgress" ngbTooltip="Remove uploaded cookies">
|
||||||
[disabled]="cookieUploadInProgress || addInProgress"
|
|
||||||
ngbTooltip="Remove uploaded cookies">
|
|
||||||
<fa-icon [icon]="faTrashAlt" />
|
<fa-icon [icon]="faTrashAlt" />
|
||||||
</button>
|
</button>
|
||||||
}
|
}
|
||||||
|
|
@ -583,25 +487,19 @@
|
||||||
<div class="action-group-label">Bulk Actions</div>
|
<div class="action-group-label">Bulk Actions</div>
|
||||||
<div class="row g-2">
|
<div class="row g-2">
|
||||||
<div class="col-4">
|
<div class="col-4">
|
||||||
<button type="button"
|
<button type="button" class="btn btn-secondary w-100" (click)="openBatchImportModal()">
|
||||||
class="btn btn-secondary w-100"
|
|
||||||
(click)="openBatchImportModal()">
|
|
||||||
<fa-icon [icon]="faFileImport" class="me-2" />
|
<fa-icon [icon]="faFileImport" class="me-2" />
|
||||||
Import URLs
|
Import URLs
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-4">
|
<div class="col-4">
|
||||||
<button type="button"
|
<button type="button" class="btn btn-secondary w-100" (click)="exportBatchUrls('all')">
|
||||||
class="btn btn-secondary w-100"
|
|
||||||
(click)="exportBatchUrls('all')">
|
|
||||||
<fa-icon [icon]="faFileExport" class="me-2" />
|
<fa-icon [icon]="faFileExport" class="me-2" />
|
||||||
Export URLs
|
Export URLs
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-4">
|
<div class="col-4">
|
||||||
<button type="button"
|
<button type="button" class="btn btn-secondary w-100" (click)="copyBatchUrls('all')">
|
||||||
class="btn btn-secondary w-100"
|
|
||||||
(click)="copyBatchUrls('all')">
|
|
||||||
<fa-icon [icon]="faCopy" class="me-2" />
|
<fa-icon [icon]="faCopy" class="me-2" />
|
||||||
Copy URLs
|
Copy URLs
|
||||||
</button>
|
</button>
|
||||||
|
|
@ -619,11 +517,8 @@
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
<!-- Batch Import Modal -->
|
<!-- Batch Import Modal -->
|
||||||
<div class="modal fade" tabindex="-1" role="dialog"
|
<div class="modal fade" tabindex="-1" role="dialog" aria-modal="true" aria-labelledby="batch-import-modal-title"
|
||||||
aria-modal="true"
|
[class.show]="batchImportModalOpen" [style.display]="batchImportModalOpen ? 'block' : 'none'">
|
||||||
aria-labelledby="batch-import-modal-title"
|
|
||||||
[class.show]="batchImportModalOpen"
|
|
||||||
[style.display]="batchImportModalOpen ? 'block' : 'none'">
|
|
||||||
<div class="modal-dialog" role="document">
|
<div class="modal-dialog" role="document">
|
||||||
<div class="modal-content">
|
<div class="modal-content">
|
||||||
<div class="modal-header">
|
<div class="modal-header">
|
||||||
|
|
@ -654,11 +549,8 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="modal fade" tabindex="-1" role="dialog"
|
<div class="modal fade" tabindex="-1" role="dialog" aria-modal="true" aria-labelledby="ui-settings-modal-title"
|
||||||
aria-modal="true"
|
[class.show]="settingsModalOpen" [style.display]="settingsModalOpen ? 'block' : 'none'">
|
||||||
aria-labelledby="ui-settings-modal-title"
|
|
||||||
[class.show]="settingsModalOpen"
|
|
||||||
[style.display]="settingsModalOpen ? 'block' : 'none'">
|
|
||||||
<div class="modal-dialog" role="document">
|
<div class="modal-dialog" role="document">
|
||||||
<div class="modal-content">
|
<div class="modal-content">
|
||||||
<div class="modal-header">
|
<div class="modal-header">
|
||||||
|
|
@ -670,16 +562,14 @@
|
||||||
<div class="col-12">
|
<div class="col-12">
|
||||||
<div class="input-group">
|
<div class="input-group">
|
||||||
<span class="input-group-text">Focused Refresh (ms)</span>
|
<span class="input-group-text">Focused Refresh (ms)</span>
|
||||||
<input type="number" min="500" max="10000" class="form-control"
|
<input type="number" min="500" max="10000" class="form-control" [(ngModel)]="focusedRefreshMs"
|
||||||
[(ngModel)]="focusedRefreshMs"
|
|
||||||
[ngModelOptions]="{standalone: true}">
|
[ngModelOptions]="{standalone: true}">
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-12">
|
<div class="col-12">
|
||||||
<div class="input-group">
|
<div class="input-group">
|
||||||
<span class="input-group-text">Background Refresh (ms)</span>
|
<span class="input-group-text">Background Refresh (ms)</span>
|
||||||
<input type="number" min="2000" max="120000" class="form-control"
|
<input type="number" min="2000" max="120000" class="form-control" [(ngModel)]="backgroundRefreshMs"
|
||||||
[(ngModel)]="backgroundRefreshMs"
|
|
||||||
[ngModelOptions]="{standalone: true}">
|
[ngModelOptions]="{standalone: true}">
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -701,15 +591,18 @@
|
||||||
}
|
}
|
||||||
<div class="metube-section-header">Downloading</div>
|
<div class="metube-section-header">Downloading</div>
|
||||||
<div class="px-2 py-3 border-bottom">
|
<div class="px-2 py-3 border-bottom">
|
||||||
<button type="button" class="btn btn-link text-decoration-none px-0 me-4" disabled #queueDelSelected (click)="delSelectedDownloads('queue')"><fa-icon [icon]="faTrashAlt" /> Cancel selected</button>
|
<button type="button" class="btn btn-link text-decoration-none px-0 me-4" disabled #queueDelSelected
|
||||||
<button type="button" class="btn btn-link text-decoration-none px-0 me-4" disabled #queueDownloadSelected (click)="startSelectedDownloads('queue')"><fa-icon [icon]="faDownload" /> Download selected</button>
|
(click)="delSelectedDownloads('queue')"><fa-icon [icon]="faTrashAlt" /> Cancel selected</button>
|
||||||
|
<button type="button" class="btn btn-link text-decoration-none px-0 me-4" disabled #queueDownloadSelected
|
||||||
|
(click)="startSelectedDownloads('queue')"><fa-icon [icon]="faDownload" /> Download selected</button>
|
||||||
</div>
|
</div>
|
||||||
<div class="overflow-auto">
|
<div class="overflow-auto">
|
||||||
<table class="table">
|
<table class="table">
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th scope="col" style="width: 1rem;">
|
<th scope="col" style="width: 1rem;">
|
||||||
<app-select-all-checkbox #queueMasterCheckboxRef [id]="'queue'" [list]="downloads.queue" (changed)="queueSelectionChanged($event)" />
|
<app-select-all-checkbox #queueMasterCheckboxRef [id]="'queue'" [list]="downloads.queue"
|
||||||
|
(changed)="queueSelectionChanged($event)" />
|
||||||
</th>
|
</th>
|
||||||
<th scope="col">Video</th>
|
<th scope="col">Video</th>
|
||||||
<th scope="col" style="width: 8rem;">Speed</th>
|
<th scope="col" style="width: 8rem;">Speed</th>
|
||||||
|
|
@ -726,8 +619,10 @@
|
||||||
<td title="{{ download.value.filename }}">
|
<td title="{{ download.value.filename }}">
|
||||||
<div class="d-flex flex-column flex-sm-row align-items-center row-gap-2 column-gap-3">
|
<div class="d-flex flex-column flex-sm-row align-items-center row-gap-2 column-gap-3">
|
||||||
<div>{{ download.value.title }} </div>
|
<div>{{ download.value.title }} </div>
|
||||||
<ngb-progressbar height="1.5rem" [showValue]="download.value.status !== 'preparing'" [striped]="download.value.status === 'preparing'" [animated]="download.value.status === 'preparing'" type="success"
|
<ngb-progressbar height="1.5rem" [showValue]="download.value.status !== 'preparing'"
|
||||||
[value]="download.value.status === 'preparing' ? 100 : download.value.percent" class="download-progressbar" />
|
[striped]="download.value.status === 'preparing'" [animated]="download.value.status === 'preparing'"
|
||||||
|
type="success" [value]="download.value.status === 'preparing' ? 100 : download.value.percent"
|
||||||
|
class="download-progressbar" />
|
||||||
</div>
|
</div>
|
||||||
</td>
|
</td>
|
||||||
<td>{{ download.value.speed | speed }}</td>
|
<td>{{ download.value.speed | speed }}</td>
|
||||||
|
|
@ -735,10 +630,16 @@
|
||||||
<td>
|
<td>
|
||||||
<div class="d-flex">
|
<div class="d-flex">
|
||||||
@if (download.value.status === 'pending') {
|
@if (download.value.status === 'pending') {
|
||||||
<button type="button" class="btn btn-link" [attr.aria-label]="'Start download for ' + download.value.title" (click)="downloadItemByKey(download.key)"><fa-icon [icon]="faDownload" /></button>
|
<button type="button" class="btn btn-link"
|
||||||
|
[attr.aria-label]="'Start download for ' + download.value.title"
|
||||||
|
(click)="downloadItemByKey(download.key)"><fa-icon [icon]="faDownload" /></button>
|
||||||
}
|
}
|
||||||
<button type="button" class="btn btn-link" [attr.aria-label]="'Remove ' + download.value.title + ' from queue'" (click)="delDownload('queue', download.key)"><fa-icon [icon]="faTrashAlt" /></button>
|
<button type="button" class="btn btn-link"
|
||||||
<a href="{{download.value.url}}" target="_blank" class="btn btn-link" [attr.aria-label]="'Open source URL for ' + download.value.title"><fa-icon [icon]="faExternalLinkAlt" /></a>
|
[attr.aria-label]="'Remove ' + download.value.title + ' from queue'"
|
||||||
|
(click)="delDownload('queue', download.key)"><fa-icon [icon]="faTrashAlt" /></button>
|
||||||
|
<a href="{{download.value.url}}" target="_blank" class="btn btn-link"
|
||||||
|
[attr.aria-label]="'Open source URL for ' + download.value.title"><fa-icon
|
||||||
|
[icon]="faExternalLinkAlt" /></a>
|
||||||
</div>
|
</div>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
|
@ -749,19 +650,28 @@
|
||||||
|
|
||||||
<div class="metube-section-header">Completed</div>
|
<div class="metube-section-header">Completed</div>
|
||||||
<div class="px-2 py-3 border-bottom">
|
<div class="px-2 py-3 border-bottom">
|
||||||
<button type="button" class="btn btn-link text-decoration-none px-0 me-4" (click)="toggleSortOrder()" ngbTooltip="{{ sortAscending ? 'Oldest first' : 'Newest first' }}"><fa-icon [icon]="sortAscending ? faSortAmountUp : faSortAmountDown" /> {{ sortAscending ? 'Oldest first' : 'Newest first' }}</button>
|
<button type="button" class="btn btn-link text-decoration-none px-0 me-4" (click)="toggleSortOrder()"
|
||||||
<button type="button" class="btn btn-link text-decoration-none px-0 me-4" disabled #doneDelSelected (click)="delSelectedDownloads('done')"><fa-icon [icon]="faTrashAlt" /> Clear selected</button>
|
ngbTooltip="{{ sortAscending ? 'Oldest first' : 'Newest first' }}"><fa-icon
|
||||||
<button type="button" class="btn btn-link text-decoration-none px-0 me-4" [disabled]="!hasCompletedDone" (click)="clearCompletedDownloads()"><fa-icon [icon]="faCheckCircle" /> Clear completed</button>
|
[icon]="sortAscending ? faSortAmountUp : faSortAmountDown" /> {{ sortAscending ? 'Oldest first' : 'Newest
|
||||||
<button type="button" class="btn btn-link text-decoration-none px-0 me-4" [disabled]="!hasFailedDone" (click)="clearFailedDownloads()"><fa-icon [icon]="faTimesCircle" /> Clear failed</button>
|
first' }}</button>
|
||||||
<button type="button" class="btn btn-link text-decoration-none px-0 me-4" [disabled]="!hasFailedDone" (click)="retryFailedDownloads()"><fa-icon [icon]="faRedoAlt" /> Retry failed</button>
|
<button type="button" class="btn btn-link text-decoration-none px-0 me-4" disabled #doneDelSelected
|
||||||
<button type="button" class="btn btn-link text-decoration-none px-0 me-4" disabled #doneDownloadSelected (click)="downloadSelectedFiles()"><fa-icon [icon]="faDownload" /> Download Selected</button>
|
(click)="delSelectedDownloads('done')"><fa-icon [icon]="faTrashAlt" /> Clear selected</button>
|
||||||
|
<button type="button" class="btn btn-link text-decoration-none px-0 me-4" [disabled]="!hasCompletedDone"
|
||||||
|
(click)="clearCompletedDownloads()"><fa-icon [icon]="faCheckCircle" /> Clear completed</button>
|
||||||
|
<button type="button" class="btn btn-link text-decoration-none px-0 me-4" [disabled]="!hasFailedDone"
|
||||||
|
(click)="clearFailedDownloads()"><fa-icon [icon]="faTimesCircle" /> Clear failed</button>
|
||||||
|
<button type="button" class="btn btn-link text-decoration-none px-0 me-4" [disabled]="!hasFailedDone"
|
||||||
|
(click)="retryFailedDownloads()"><fa-icon [icon]="faRedoAlt" /> Retry failed</button>
|
||||||
|
<button type="button" class="btn btn-link text-decoration-none px-0 me-4" disabled #doneDownloadSelected
|
||||||
|
(click)="downloadSelectedFiles()"><fa-icon [icon]="faDownload" /> Download Selected</button>
|
||||||
</div>
|
</div>
|
||||||
<div class="overflow-auto">
|
<div class="overflow-auto">
|
||||||
<table class="table">
|
<table class="table">
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th scope="col" style="width: 1rem;">
|
<th scope="col" style="width: 1rem;">
|
||||||
<app-select-all-checkbox #doneMasterCheckboxRef [id]="'done'" [list]="downloads.done" (changed)="doneSelectionChanged($event)" />
|
<app-select-all-checkbox #doneMasterCheckboxRef [id]="'done'" [list]="downloads.done"
|
||||||
|
(changed)="doneSelectionChanged($event)" />
|
||||||
</th>
|
</th>
|
||||||
<th scope="col">Video</th>
|
<th scope="col">Video</th>
|
||||||
<th scope="col">Type</th>
|
<th scope="col">Type</th>
|
||||||
|
|
@ -784,8 +694,7 @@
|
||||||
<fa-icon [icon]="faCheckCircle" class="text-success" />
|
<fa-icon [icon]="faCheckCircle" class="text-success" />
|
||||||
}
|
}
|
||||||
@if (entry[1].status === 'error') {
|
@if (entry[1].status === 'error') {
|
||||||
<button type="button" class="btn btn-link p-0"
|
<button type="button" class="btn btn-link p-0" (click)="toggleErrorDetail(entry[0])"
|
||||||
(click)="toggleErrorDetail(entry[0])"
|
|
||||||
[attr.aria-label]="'Toggle error details for ' + entry[1].title"
|
[attr.aria-label]="'Toggle error details for ' + entry[1].title"
|
||||||
[attr.aria-expanded]="isErrorExpanded(entry[0])">
|
[attr.aria-expanded]="isErrorExpanded(entry[0])">
|
||||||
<fa-icon [icon]="faTimesCircle" class="text-danger" />
|
<fa-icon [icon]="faTimesCircle" class="text-danger" />
|
||||||
|
|
@ -796,7 +705,8 @@
|
||||||
<a href="{{buildDownloadLink(entry[1])}}" target="_blank">{{ entry[1].title }}</a>
|
<a href="{{buildDownloadLink(entry[1])}}" target="_blank">{{ entry[1].title }}</a>
|
||||||
} @else {
|
} @else {
|
||||||
@if (entry[1].status === 'error') {
|
@if (entry[1].status === 'error') {
|
||||||
<button type="button" class="btn btn-link p-0 text-start align-baseline" (click)="toggleErrorDetail(entry[0])">
|
<button type="button" class="btn btn-link p-0 text-start align-baseline"
|
||||||
|
(click)="toggleErrorDetail(entry[0])">
|
||||||
{{entry[1].title}}
|
{{entry[1].title}}
|
||||||
@if (!isErrorExpanded(entry[0])) {
|
@if (!isErrorExpanded(entry[0])) {
|
||||||
<small class="text-danger ms-2">
|
<small class="text-danger ms-2">
|
||||||
|
|
@ -855,13 +765,17 @@
|
||||||
<td>
|
<td>
|
||||||
<div class="d-flex">
|
<div class="d-flex">
|
||||||
@if (entry[1].status === 'error') {
|
@if (entry[1].status === 'error') {
|
||||||
<button type="button" class="btn btn-link" [attr.aria-label]="'Retry download for ' + entry[1].title" (click)="retryDownload(entry[0], entry[1])"><fa-icon [icon]="faRedoAlt" /></button>
|
<button type="button" class="btn btn-link" [attr.aria-label]="'Retry download for ' + entry[1].title"
|
||||||
|
(click)="retryDownload(entry[0], entry[1])"><fa-icon [icon]="faRedoAlt" /></button>
|
||||||
}
|
}
|
||||||
@if (entry[1].filename) {
|
@if (entry[1].filename) {
|
||||||
<a href="{{buildDownloadLink(entry[1])}}" download class="btn btn-link" [attr.aria-label]="'Download result file for ' + entry[1].title"><fa-icon [icon]="faDownload" /></a>
|
<a href="{{buildDownloadLink(entry[1])}}" download class="btn btn-link"
|
||||||
|
[attr.aria-label]="'Download result file for ' + entry[1].title"><fa-icon [icon]="faDownload" /></a>
|
||||||
}
|
}
|
||||||
<a href="{{entry[1].url}}" target="_blank" class="btn btn-link" [attr.aria-label]="'Open source URL for ' + entry[1].title"><fa-icon [icon]="faExternalLinkAlt" /></a>
|
<a href="{{entry[1].url}}" target="_blank" class="btn btn-link"
|
||||||
<button type="button" class="btn btn-link" [attr.aria-label]="'Delete completed item ' + entry[1].title" (click)="delDownload('done', entry[0])"><fa-icon [icon]="faTrashAlt" /></button>
|
[attr.aria-label]="'Open source URL for ' + entry[1].title"><fa-icon [icon]="faExternalLinkAlt" /></a>
|
||||||
|
<button type="button" class="btn btn-link" [attr.aria-label]="'Delete completed item ' + entry[1].title"
|
||||||
|
(click)="delDownload('done', entry[0])"><fa-icon [icon]="faTrashAlt" /></button>
|
||||||
</div>
|
</div>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
|
@ -872,7 +786,8 @@
|
||||||
<td>
|
<td>
|
||||||
<div style="padding-left: 2rem;">
|
<div style="padding-left: 2rem;">
|
||||||
<fa-icon [icon]="faCheckCircle" class="text-success me-2" />
|
<fa-icon [icon]="faCheckCircle" class="text-success me-2" />
|
||||||
<a href="{{buildChapterDownloadLink(entry[1], chapterFile.filename)}}" target="_blank" [attr.aria-label]="'Open chapter file ' + getChapterFileName(chapterFile.filename)">{{
|
<a href="{{buildChapterDownloadLink(entry[1], chapterFile.filename)}}" target="_blank"
|
||||||
|
[attr.aria-label]="'Open chapter file ' + getChapterFileName(chapterFile.filename)">{{
|
||||||
getChapterFileName(chapterFile.filename) }}</a>
|
getChapterFileName(chapterFile.filename) }}</a>
|
||||||
</div>
|
</div>
|
||||||
</td>
|
</td>
|
||||||
|
|
@ -887,7 +802,8 @@
|
||||||
<td></td>
|
<td></td>
|
||||||
<td>
|
<td>
|
||||||
<div class="d-flex">
|
<div class="d-flex">
|
||||||
<a href="{{buildChapterDownloadLink(entry[1], chapterFile.filename)}}" download [attr.aria-label]="'Download chapter file ' + getChapterFileName(chapterFile.filename)"
|
<a href="{{buildChapterDownloadLink(entry[1], chapterFile.filename)}}" download
|
||||||
|
[attr.aria-label]="'Download chapter file ' + getChapterFileName(chapterFile.filename)"
|
||||||
class="btn btn-link"><fa-icon [icon]="faDownload" /></a>
|
class="btn btn-link"><fa-icon [icon]="faDownload" /></a>
|
||||||
</div>
|
</div>
|
||||||
</td>
|
</td>
|
||||||
|
|
@ -906,8 +822,7 @@
|
||||||
<span class="spinner-border spinner-border-sm me-2" role="status" aria-hidden="true"></span>Check all now
|
<span class="spinner-border spinner-border-sm me-2" role="status" aria-hidden="true"></span>Check all now
|
||||||
</button>
|
</button>
|
||||||
} @else {
|
} @else {
|
||||||
<button type="button" class="btn btn-link text-decoration-none px-0 me-4"
|
<button type="button" class="btn btn-link text-decoration-none px-0 me-4" (click)="checkAllSubscriptions()"
|
||||||
(click)="checkAllSubscriptions()"
|
|
||||||
[disabled]="downloads.loading || cachedSubs.length === 0 || checkingSelectedSubscriptions">
|
[disabled]="downloads.loading || cachedSubs.length === 0 || checkingSelectedSubscriptions">
|
||||||
<fa-icon [icon]="faRedoAlt" /> Check all now
|
<fa-icon [icon]="faRedoAlt" /> Check all now
|
||||||
</button>
|
</button>
|
||||||
|
|
@ -917,14 +832,12 @@
|
||||||
<span class="spinner-border spinner-border-sm me-2" role="status" aria-hidden="true"></span>Check selected
|
<span class="spinner-border spinner-border-sm me-2" role="status" aria-hidden="true"></span>Check selected
|
||||||
</button>
|
</button>
|
||||||
} @else {
|
} @else {
|
||||||
<button type="button" class="btn btn-link text-decoration-none px-0 me-4"
|
<button type="button" class="btn btn-link text-decoration-none px-0 me-4" (click)="checkSelectedSubscriptions()"
|
||||||
(click)="checkSelectedSubscriptions()"
|
|
||||||
[disabled]="downloads.loading || selectedSubscriptionIds.size === 0 || checkingAllSubscriptions">
|
[disabled]="downloads.loading || selectedSubscriptionIds.size === 0 || checkingAllSubscriptions">
|
||||||
<fa-icon [icon]="faRedoAlt" /> Check selected
|
<fa-icon [icon]="faRedoAlt" /> Check selected
|
||||||
</button>
|
</button>
|
||||||
}
|
}
|
||||||
<button type="button" class="btn btn-link text-decoration-none px-0 me-4"
|
<button type="button" class="btn btn-link text-decoration-none px-0 me-4" (click)="deleteSelectedSubscriptions()"
|
||||||
(click)="deleteSelectedSubscriptions()"
|
|
||||||
[disabled]="downloads.loading || selectedSubscriptionIds.size === 0">
|
[disabled]="downloads.loading || selectedSubscriptionIds.size === 0">
|
||||||
<fa-icon [icon]="faTrashAlt" /> Delete selected
|
<fa-icon [icon]="faTrashAlt" /> Delete selected
|
||||||
</button>
|
</button>
|
||||||
|
|
@ -934,10 +847,8 @@
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th scope="col" style="width: 1rem;">
|
<th scope="col" style="width: 1rem;">
|
||||||
<input type="checkbox" class="form-check-input"
|
<input type="checkbox" class="form-check-input" [checked]="allSubsSelected()"
|
||||||
[checked]="allSubsSelected()"
|
(change)="toggleSubMaster($event)" [disabled]="downloads.loading || cachedSubs.length === 0"
|
||||||
(change)="toggleSubMaster($event)"
|
|
||||||
[disabled]="downloads.loading || cachedSubs.length === 0"
|
|
||||||
aria-label="Select all subscriptions" />
|
aria-label="Select all subscriptions" />
|
||||||
</th>
|
</th>
|
||||||
<th scope="col">Name</th>
|
<th scope="col">Name</th>
|
||||||
|
|
@ -952,10 +863,8 @@
|
||||||
@for (entry of cachedSubs; track entry[0]) {
|
@for (entry of cachedSubs; track entry[0]) {
|
||||||
<tr>
|
<tr>
|
||||||
<td>
|
<td>
|
||||||
<input type="checkbox" class="form-check-input"
|
<input type="checkbox" class="form-check-input" [checked]="isSubSelected(entry[0])"
|
||||||
[checked]="isSubSelected(entry[0])"
|
(change)="toggleSubSelected(entry[0])" [disabled]="downloads.loading"
|
||||||
(change)="toggleSubSelected(entry[0])"
|
|
||||||
[disabled]="downloads.loading"
|
|
||||||
[attr.aria-label]="'Select subscription ' + entry[1].name" />
|
[attr.aria-label]="'Select subscription ' + entry[1].name" />
|
||||||
</td>
|
</td>
|
||||||
<td>{{ entry[1].name }}</td>
|
<td>{{ entry[1].name }}</td>
|
||||||
|
|
@ -980,23 +889,17 @@
|
||||||
<td>
|
<td>
|
||||||
<div class="d-flex flex-wrap gap-1">
|
<div class="d-flex flex-wrap gap-1">
|
||||||
@if (isSubscriptionChecking(entry[0])) {
|
@if (isSubscriptionChecking(entry[0])) {
|
||||||
<button type="button" class="btn btn-link btn-sm p-0 me-2"
|
<button type="button" class="btn btn-link btn-sm p-0 me-2" disabled
|
||||||
disabled
|
[attr.aria-label]="'Checking ' + entry[1].name" ngbTooltip="Checking now">
|
||||||
[attr.aria-label]="'Checking ' + entry[1].name"
|
|
||||||
ngbTooltip="Checking now">
|
|
||||||
<span class="spinner-border spinner-border-sm" role="status" aria-hidden="true"></span>
|
<span class="spinner-border spinner-border-sm" role="status" aria-hidden="true"></span>
|
||||||
</button>
|
</button>
|
||||||
} @else {
|
} @else {
|
||||||
<button type="button" class="btn btn-link btn-sm p-0 me-2"
|
<button type="button" class="btn btn-link btn-sm p-0 me-2" (click)="checkSubscriptionNow(entry[0])"
|
||||||
(click)="checkSubscriptionNow(entry[0])"
|
[disabled]="downloads.loading" [attr.aria-label]="'Check now ' + entry[1].name" ngbTooltip="Check now">
|
||||||
[disabled]="downloads.loading"
|
|
||||||
[attr.aria-label]="'Check now ' + entry[1].name"
|
|
||||||
ngbTooltip="Check now">
|
|
||||||
<fa-icon [icon]="faRedoAlt" />
|
<fa-icon [icon]="faRedoAlt" />
|
||||||
</button>
|
</button>
|
||||||
}
|
}
|
||||||
<button type="button" class="btn btn-link btn-sm p-0 me-2"
|
<button type="button" class="btn btn-link btn-sm p-0 me-2" (click)="toggleSubscriptionEnabled(entry[1])"
|
||||||
(click)="toggleSubscriptionEnabled(entry[1])"
|
|
||||||
[disabled]="downloads.loading"
|
[disabled]="downloads.loading"
|
||||||
[attr.aria-label]="(entry[1].enabled ? 'Pause ' : 'Resume ') + entry[1].name"
|
[attr.aria-label]="(entry[1].enabled ? 'Pause ' : 'Resume ') + entry[1].name"
|
||||||
[ngbTooltip]="entry[1].enabled ? 'Pause' : 'Resume'">
|
[ngbTooltip]="entry[1].enabled ? 'Pause' : 'Resume'">
|
||||||
|
|
@ -1006,10 +909,8 @@
|
||||||
<fa-icon [icon]="faPlay" />
|
<fa-icon [icon]="faPlay" />
|
||||||
}
|
}
|
||||||
</button>
|
</button>
|
||||||
<button type="button" class="btn btn-link btn-sm p-0 text-danger"
|
<button type="button" class="btn btn-link btn-sm p-0 text-danger" (click)="deleteSubscription(entry[0])"
|
||||||
(click)="deleteSubscription(entry[0])"
|
[disabled]="downloads.loading" [attr.aria-label]="'Delete subscription ' + entry[1].name">
|
||||||
[disabled]="downloads.loading"
|
|
||||||
[attr.aria-label]="'Delete subscription ' + entry[1].name">
|
|
||||||
<fa-icon [icon]="faTrashAlt" />
|
<fa-icon [icon]="faTrashAlt" />
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -201,6 +201,9 @@ main
|
||||||
.input-group
|
.input-group
|
||||||
margin-bottom: 0
|
margin-bottom: 0
|
||||||
|
|
||||||
|
.webhook-timeout-input
|
||||||
|
min-width: 66px
|
||||||
|
|
||||||
.webhook-action-row
|
.webhook-action-row
|
||||||
display: flex
|
display: flex
|
||||||
flex-wrap: wrap
|
flex-wrap: wrap
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue