mirror of
https://github.com/Tzahi12345/YoutubeDL-Material.git
synced 2026-03-21 12:10:58 +03:00
Merge branch 'master' of https://github.com/Tzahi12345/YoutubeDL-Material into api-generator
This commit is contained in:
@@ -21,6 +21,17 @@
|
||||
<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>
|
||||
</p>
|
||||
<p>
|
||||
<ng-container i18n="Installation type">Installation type:</ng-container> {{postsService.version_info.type}}
|
||||
<br>
|
||||
<ng-container *ngIf="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}}
|
||||
</p>
|
||||
<p>
|
||||
<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>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing';
|
||||
|
||||
import { AboutDialogComponent } from './about-dialog.component';
|
||||
|
||||
@@ -6,7 +6,7 @@ describe('AboutDialogComponent', () => {
|
||||
let component: AboutDialogComponent;
|
||||
let fixture: ComponentFixture<AboutDialogComponent>;
|
||||
|
||||
beforeEach(async(() => {
|
||||
beforeEach(waitForAsync(() => {
|
||||
TestBed.configureTestingModule({
|
||||
declarations: [ AboutDialogComponent ]
|
||||
})
|
||||
|
||||
@@ -19,7 +19,7 @@ export class AboutDialogComponent implements OnInit {
|
||||
sidepanel_mode = this.postsService.sidepanel_mode;
|
||||
card_size = this.postsService.card_size;
|
||||
|
||||
constructor(private postsService: PostsService) { }
|
||||
constructor(public postsService: PostsService) { }
|
||||
|
||||
ngOnInit(): void {
|
||||
this.getLatestGithubRelease();
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing';
|
||||
|
||||
import { AddUserDialogComponent } from './add-user-dialog.component';
|
||||
|
||||
@@ -6,7 +6,7 @@ describe('AddUserDialogComponent', () => {
|
||||
let component: AddUserDialogComponent;
|
||||
let fixture: ComponentFixture<AddUserDialogComponent>;
|
||||
|
||||
beforeEach(async(() => {
|
||||
beforeEach(waitForAsync(() => {
|
||||
TestBed.configureTestingModule({
|
||||
declarations: [ AddUserDialogComponent ]
|
||||
})
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing';
|
||||
|
||||
import { ArgModifierDialogComponent } from './arg-modifier-dialog.component';
|
||||
|
||||
@@ -6,7 +6,7 @@ describe('ArgModifierDialogComponent', () => {
|
||||
let component: ArgModifierDialogComponent;
|
||||
let fixture: ComponentFixture<ArgModifierDialogComponent>;
|
||||
|
||||
beforeEach(async(() => {
|
||||
beforeEach(waitForAsync(() => {
|
||||
TestBed.configureTestingModule({
|
||||
declarations: [ ArgModifierDialogComponent ]
|
||||
})
|
||||
|
||||
@@ -6,10 +6,13 @@
|
||||
</mat-dialog-content>
|
||||
<mat-dialog-actions>
|
||||
<!-- The mat-dialog-close directive optionally accepts a value as a result for the dialog. -->
|
||||
<button color="primary" mat-flat-button type="submit" (click)="confirmClicked()">{{submitText}}</button>
|
||||
<button [color]="warnSubmitColor ? 'warn' : 'primary'" mat-flat-button type="submit" (click)="confirmClicked()">{{submitText}}</button>
|
||||
<div class="mat-spinner" *ngIf="submitClicked">
|
||||
<mat-spinner [diameter]="25"></mat-spinner>
|
||||
</div>
|
||||
<span class="spacer"></span>
|
||||
<button style="float: right;" mat-stroked-button mat-dialog-close>Cancel</button>
|
||||
<button style="float: right;" mat-stroked-button mat-dialog-close>
|
||||
<ng-container *ngIf="cancelText">{{cancelText}}</ng-container>
|
||||
<ng-container *ngIf="!cancelText" i18n="Cancel">Cancel</ng-container>
|
||||
</button>
|
||||
</mat-dialog-actions>
|
||||
@@ -1,4 +1,4 @@
|
||||
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing';
|
||||
|
||||
import { ConfirmDialogComponent } from './confirm-dialog.component';
|
||||
|
||||
@@ -6,7 +6,7 @@ describe('ConfirmDialogComponent', () => {
|
||||
let component: ConfirmDialogComponent;
|
||||
let fixture: ComponentFixture<ConfirmDialogComponent>;
|
||||
|
||||
beforeEach(async(() => {
|
||||
beforeEach(waitForAsync(() => {
|
||||
TestBed.configureTestingModule({
|
||||
declarations: [ ConfirmDialogComponent ]
|
||||
})
|
||||
|
||||
@@ -11,16 +11,23 @@ export class ConfirmDialogComponent implements OnInit {
|
||||
dialogTitle = 'Confirm';
|
||||
dialogText = 'Would you like to confirm?';
|
||||
submitText = 'Yes'
|
||||
cancelText = null;
|
||||
submitClicked = false;
|
||||
closeOnSubmit = true;
|
||||
|
||||
doneEmitter: EventEmitter<any> = null;
|
||||
doneEmitter: EventEmitter<boolean> = null;
|
||||
onlyEmitOnDone = false;
|
||||
|
||||
|
||||
warnSubmitColor = false;
|
||||
|
||||
constructor(@Inject(MAT_DIALOG_DATA) public data: any, public dialogRef: MatDialogRef<ConfirmDialogComponent>) {
|
||||
if (this.data.dialogTitle) { this.dialogTitle = this.data.dialogTitle };
|
||||
if (this.data.dialogText) { this.dialogText = this.data.dialogText };
|
||||
if (this.data.submitText) { this.submitText = this.data.submitText };
|
||||
if (this.data.dialogTitle !== undefined) { this.dialogTitle = this.data.dialogTitle }
|
||||
if (this.data.dialogText !== undefined) { this.dialogText = this.data.dialogText }
|
||||
if (this.data.submitText !== undefined) { this.submitText = this.data.submitText }
|
||||
if (this.data.cancelText !== undefined) { this.cancelText = this.data.cancelText }
|
||||
if (this.data.warnSubmitColor !== undefined) { this.warnSubmitColor = this.data.warnSubmitColor }
|
||||
if (this.data.warnSubmitColor !== undefined) { this.warnSubmitColor = this.data.warnSubmitColor }
|
||||
if (this.data.closeOnSubmit !== undefined) { this.closeOnSubmit = this.data.closeOnSubmit }
|
||||
|
||||
// checks if emitter exists, if so don't autoclose as it should be handled by caller
|
||||
if (this.data.doneEmitter) {
|
||||
@@ -32,9 +39,9 @@ export class ConfirmDialogComponent implements OnInit {
|
||||
confirmClicked() {
|
||||
if (this.onlyEmitOnDone) {
|
||||
this.doneEmitter.emit(true);
|
||||
this.submitClicked = true;
|
||||
if (this.closeOnSubmit) this.submitClicked = true;
|
||||
} else {
|
||||
this.dialogRef.close(true);
|
||||
if (this.closeOnSubmit) this.dialogRef.close(true);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing';
|
||||
|
||||
import { CookiesUploaderDialogComponent } from './cookies-uploader-dialog.component';
|
||||
|
||||
@@ -6,7 +6,7 @@ describe('CookiesUploaderDialogComponent', () => {
|
||||
let component: CookiesUploaderDialogComponent;
|
||||
let fixture: ComponentFixture<CookiesUploaderDialogComponent>;
|
||||
|
||||
beforeEach(async(() => {
|
||||
beforeEach(waitForAsync(() => {
|
||||
TestBed.configureTestingModule({
|
||||
declarations: [ CookiesUploaderDialogComponent ]
|
||||
})
|
||||
|
||||
@@ -0,0 +1,60 @@
|
||||
<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;">
|
||||
<input matInput placeholder="Name" i18n-placeholder="Name" [(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']">
|
||||
<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>
|
||||
</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>
|
||||
</mat-select>
|
||||
</mat-form-field>
|
||||
<mat-form-field class="value-input">
|
||||
<input matInput [(ngModel)]="rule['value']">
|
||||
</mat-form-field>
|
||||
<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>
|
||||
</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;">
|
||||
<input matInput [(ngModel)]="category['custom_output']" placeholder="Custom file output" i18n-placeholder="Category custom file output placeholder">
|
||||
<mat-hint>
|
||||
<a target="_blank" href="https://github.com/ytdl-org/youtube-dl/blob/master/README.md#output-template">
|
||||
<ng-container i18n="Custom output template documentation link">Documentation</ng-container></a>.
|
||||
<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>
|
||||
<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">
|
||||
<mat-spinner [diameter]="25"></mat-spinner>
|
||||
</div>
|
||||
</mat-dialog-actions>
|
||||
@@ -0,0 +1,16 @@
|
||||
.operator-select {
|
||||
width: 55px;
|
||||
}
|
||||
|
||||
.property-select {
|
||||
margin-left: 10px;
|
||||
width: 110px;
|
||||
}
|
||||
|
||||
.comparator-select {
|
||||
margin-left: 10px;
|
||||
}
|
||||
|
||||
.value-input {
|
||||
margin-left: 10px;
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing';
|
||||
|
||||
import { EditCategoryDialogComponent } from './edit-category-dialog.component';
|
||||
|
||||
describe('EditCategoryDialogComponent', () => {
|
||||
let component: EditCategoryDialogComponent;
|
||||
let fixture: ComponentFixture<EditCategoryDialogComponent>;
|
||||
|
||||
beforeEach(waitForAsync(() => {
|
||||
TestBed.configureTestingModule({
|
||||
declarations: [ EditCategoryDialogComponent ]
|
||||
})
|
||||
.compileComponents();
|
||||
}));
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(EditCategoryDialogComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,111 @@
|
||||
import { Component, OnInit, Inject } from '@angular/core';
|
||||
import { MAT_DIALOG_DATA } from '@angular/material/dialog';
|
||||
import { PostsService } from 'app/posts.services';
|
||||
|
||||
@Component({
|
||||
selector: 'app-edit-category-dialog',
|
||||
templateUrl: './edit-category-dialog.component.html',
|
||||
styleUrls: ['./edit-category-dialog.component.scss']
|
||||
})
|
||||
export class EditCategoryDialogComponent implements OnInit {
|
||||
|
||||
updating = false;
|
||||
original_category = null;
|
||||
category = null;
|
||||
|
||||
propertyOptions = [
|
||||
{
|
||||
value: 'fulltitle',
|
||||
label: 'Title'
|
||||
},
|
||||
{
|
||||
value: 'id',
|
||||
label: 'ID'
|
||||
},
|
||||
{
|
||||
value: 'webpage_url',
|
||||
label: 'URL'
|
||||
},
|
||||
{
|
||||
value: 'view_count',
|
||||
label: 'Views'
|
||||
},
|
||||
{
|
||||
value: 'uploader',
|
||||
label: 'Uploader'
|
||||
},
|
||||
{
|
||||
value: '_filename',
|
||||
label: 'File Name'
|
||||
},
|
||||
{
|
||||
value: 'tags',
|
||||
label: 'Tags'
|
||||
}
|
||||
];
|
||||
|
||||
comparatorOptions = [
|
||||
{
|
||||
value: 'includes',
|
||||
label: 'includes'
|
||||
},
|
||||
{
|
||||
value: 'not_includes',
|
||||
label: 'not includes'
|
||||
},
|
||||
{
|
||||
value: 'equals',
|
||||
label: 'equals'
|
||||
},
|
||||
{
|
||||
value: 'not_equals',
|
||||
label: 'not equals'
|
||||
},
|
||||
|
||||
];
|
||||
|
||||
constructor(@Inject(MAT_DIALOG_DATA) public data: any, private postsService: PostsService) {
|
||||
if (this.data) {
|
||||
this.original_category = this.data.category;
|
||||
this.category = JSON.parse(JSON.stringify(this.original_category));
|
||||
}
|
||||
}
|
||||
|
||||
ngOnInit(): void {
|
||||
}
|
||||
|
||||
addNewRule() {
|
||||
this.category['rules'].push({
|
||||
preceding_operator: 'or',
|
||||
property: 'fulltitle',
|
||||
comparator: 'includes',
|
||||
value: ''
|
||||
});
|
||||
}
|
||||
|
||||
saveClicked() {
|
||||
this.updating = true;
|
||||
this.postsService.updateCategory(this.category).subscribe(res => {
|
||||
this.updating = false;
|
||||
this.original_category = JSON.parse(JSON.stringify(this.category));
|
||||
this.postsService.reloadCategories();
|
||||
}, err => {
|
||||
this.updating = false;
|
||||
console.error(err);
|
||||
});
|
||||
}
|
||||
|
||||
categoryChanged() {
|
||||
return JSON.stringify(this.category) === JSON.stringify(this.original_category);
|
||||
}
|
||||
|
||||
swapRules(original_index, new_index) {
|
||||
[this.category.rules[original_index], this.category.rules[new_index]] = [this.category.rules[new_index],
|
||||
this.category.rules[original_index]];
|
||||
}
|
||||
|
||||
removeRule(index) {
|
||||
this.category['rules'].splice(index, 1);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,8 +1,11 @@
|
||||
<h4 mat-dialog-title i18n="Edit subscription dialog title prefix">Editing</h4> {{sub.name}}
|
||||
<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>
|
||||
|
||||
<mat-dialog-content>
|
||||
<div class="container-fluid">
|
||||
<div class="row">
|
||||
<div class="col-12 mt-3">
|
||||
<mat-checkbox [(ngModel)]="new_sub.paused"><ng-container i18n="Paused subscription setting">Paused</ng-container></mat-checkbox>
|
||||
</div>
|
||||
<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>
|
||||
@@ -24,7 +27,14 @@
|
||||
<mat-checkbox [disabled]="true" [(ngModel)]="audioOnlyMode"><ng-container i18n="Streaming-only mode">Audio-only mode</ng-container></mat-checkbox>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-12">
|
||||
<div class="col-12 mt-2">
|
||||
<mat-form-field>
|
||||
<mat-select placeholder="Max quality" i18n-placeholder="Max quality placeholder" [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>
|
||||
</mat-select>
|
||||
</mat-form-field>
|
||||
</div>
|
||||
<div class="col-12 mt-1">
|
||||
<div>
|
||||
<mat-checkbox [disabled]="new_sub.type === 'audio'" [(ngModel)]="new_sub.streamingOnly"><ng-container i18n="Streaming-only mode">Streaming-only mode</ng-container></mat-checkbox>
|
||||
</div>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing';
|
||||
|
||||
import { EditSubscriptionDialogComponent } from './edit-subscription-dialog.component';
|
||||
|
||||
@@ -6,7 +6,7 @@ describe('EditSubscriptionDialogComponent', () => {
|
||||
let component: EditSubscriptionDialogComponent;
|
||||
let fixture: ComponentFixture<EditSubscriptionDialogComponent>;
|
||||
|
||||
beforeEach(async(() => {
|
||||
beforeEach(waitForAsync(() => {
|
||||
TestBed.configureTestingModule({
|
||||
declarations: [ EditSubscriptionDialogComponent ]
|
||||
})
|
||||
|
||||
@@ -22,6 +22,36 @@ export class EditSubscriptionDialogComponent implements OnInit {
|
||||
audioOnlyMode = null;
|
||||
download_all = null;
|
||||
|
||||
available_qualities = [
|
||||
{
|
||||
'label': 'Best',
|
||||
'value': 'best'
|
||||
},
|
||||
{
|
||||
'label': '4K',
|
||||
'value': '2160'
|
||||
},
|
||||
{
|
||||
'label': '1440p',
|
||||
'value': '1440'
|
||||
},
|
||||
{
|
||||
'label': '1080p',
|
||||
'value': '1080'
|
||||
},
|
||||
{
|
||||
'label': '720p',
|
||||
'value': '720'
|
||||
},
|
||||
{
|
||||
'label': '480p',
|
||||
'value': '480'
|
||||
},
|
||||
{
|
||||
'label': '360p',
|
||||
'value': '360'
|
||||
}
|
||||
];
|
||||
|
||||
time_units = [
|
||||
'day',
|
||||
@@ -31,24 +61,24 @@ export class EditSubscriptionDialogComponent implements OnInit {
|
||||
];
|
||||
|
||||
constructor(@Inject(MAT_DIALOG_DATA) public data: any, private dialog: MatDialog, private postsService: PostsService) {
|
||||
this.sub = this.data.sub;
|
||||
this.sub = JSON.parse(JSON.stringify(this.data.sub));
|
||||
this.new_sub = JSON.parse(JSON.stringify(this.sub));
|
||||
|
||||
// ignore videos to keep requests small
|
||||
delete this.sub['videos'];
|
||||
delete this.new_sub['videos'];
|
||||
|
||||
this.audioOnlyMode = this.sub.type === 'audio';
|
||||
this.download_all = !this.sub.timerange;
|
||||
|
||||
if (this.sub.timerange) {
|
||||
const timerange_str = this.sub.timerange.split('-')[1];
|
||||
console.log(timerange_str);
|
||||
const number = timerange_str.replace(/\D/g,'');
|
||||
let units = timerange_str.replace(/[0-9]/g, '');
|
||||
|
||||
console.log(units);
|
||||
|
||||
// // remove plural on units
|
||||
// if (units[units.length-1] === 's') {
|
||||
// units = units.substring(0, units.length-1);
|
||||
// }
|
||||
if (+number === 1) {
|
||||
units = units.replace('s', '');
|
||||
}
|
||||
|
||||
this.timerange_amount = parseInt(number);
|
||||
this.timerange_unit = units;
|
||||
@@ -71,9 +101,10 @@ export class EditSubscriptionDialogComponent implements OnInit {
|
||||
}
|
||||
|
||||
saveSubscription() {
|
||||
this.postsService.updateSubscription(this.sub).subscribe(res => {
|
||||
this.postsService.updateSubscription(this.new_sub).subscribe(res => {
|
||||
this.sub = this.new_sub;
|
||||
this.new_sub = JSON.parse(JSON.stringify(this.sub));
|
||||
this.postsService.reloadSubscriptions();
|
||||
})
|
||||
}
|
||||
|
||||
@@ -85,12 +116,16 @@ export class EditSubscriptionDialogComponent implements OnInit {
|
||||
}
|
||||
|
||||
timerangeChanged(value, select_changed) {
|
||||
console.log(this.timerange_amount);
|
||||
console.log(this.timerange_unit);
|
||||
if (+this.timerange_amount === 1) {
|
||||
this.timerange_unit = this.timerange_unit.replace('s', '');
|
||||
} else {
|
||||
if (!this.timerange_unit.includes('s')) {
|
||||
this.timerange_unit += 's';
|
||||
}
|
||||
}
|
||||
|
||||
if (this.timerange_amount && this.timerange_unit && !this.download_all) {
|
||||
this.new_sub.timerange = 'now-' + this.timerange_amount.toString() + this.timerange_unit;
|
||||
console.log(this.new_sub.timerange);
|
||||
} else {
|
||||
this.new_sub.timerange = null;
|
||||
}
|
||||
|
||||
@@ -1,28 +1,44 @@
|
||||
<h4 mat-dialog-title><ng-container i18n="Modify playlist dialog title">Modify playlist</ng-container></h4>
|
||||
|
||||
<mat-dialog-content>
|
||||
<!-- Playlist info -->
|
||||
<div>
|
||||
<mat-form-field color="accent">
|
||||
<input matInput placeholder="Name" i18n-placeholder="Name" [(ngModel)]="playlist.name">
|
||||
</mat-form-field>
|
||||
</div>
|
||||
<div *ngIf="playlist">
|
||||
<!-- Playlist info -->
|
||||
<div>
|
||||
<mat-form-field color="accent">
|
||||
<input matInput placeholder="Name" i18n-placeholder="Name" [(ngModel)]="playlist.name">
|
||||
</mat-form-field>
|
||||
</div>
|
||||
|
||||
<!-- Playlist order -->
|
||||
<mat-button-toggle-group class="media-list" cdkDropList (cdkDropListDropped)="drop($event)" style="width: 80%; left: 9%" vertical name="videoSelect" aria-label="Video Select" #group="matButtonToggleGroup">
|
||||
<mat-button-toggle class="media-box" cdkDrag *ngFor="let playlist_item of playlist.fileNames; let i = index" [checked]="false"><div><div class="playlist-item-text">{{playlist_item}}</div> <button (click)="removeContent(i)" class="remove-item-button" mat-icon-button><mat-icon>cancel</mat-icon></button></div></mat-button-toggle>
|
||||
</mat-button-toggle-group>
|
||||
<div>
|
||||
<mat-checkbox [(ngModel)]="playlist.randomize_order"><ng-container i18n="Randomize order when playing checkbox label">Randomize order when playing</ng-container></mat-checkbox>
|
||||
</div>
|
||||
|
||||
<div class="add-content-button">
|
||||
<button [disabled]="available_files.length === 0" mat-stroked-button [matMenuTriggerFor]="menu"><ng-container i18n="Add more content">Add more content</ng-container></button>
|
||||
<div style="margin-bottom: 10px; height: 40px;">
|
||||
<div style="float: left">
|
||||
<span *ngIf="reverse_order === false" i18n="Normal order">Normal order </span>
|
||||
<span *ngIf="reverse_order === true" i18n="Reverse order">Reverse order </span>
|
||||
<button (click)="togglePlaylistOrder()" mat-icon-button><mat-icon>{{!reverse_order ? 'arrow_downward' : 'arrow_upward'}}</mat-icon></button>
|
||||
</div>
|
||||
|
||||
<div style="float: right">
|
||||
<button [disabled]="available_files.length === 0" mat-stroked-button [matMenuTriggerFor]="menu"><ng-container i18n="Add content">Add content</ng-container></button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Playlist order -->
|
||||
<mat-button-toggle-group class="media-list" cdkDropList (cdkDropListDropped)="drop($event)" style="width: 80%; left: 9%" vertical name="videoSelect" aria-label="Video Select" #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 playlist_item of (reverse_order ? playlist_file_objs.slice().reverse() : playlist_file_objs); let i = index" [checked]="false"><div><div class="playlist-item-text">{{playlist_item.title}}</div> <button (click)="removeContent(i)" class="remove-item-button" mat-icon-button><mat-icon>cancel</mat-icon></button></div></mat-button-toggle>
|
||||
</mat-button-toggle-group>
|
||||
|
||||
<mat-menu #menu="matMenu">
|
||||
<button *ngFor="let file of available_files" (click)="addContent(file)" mat-menu-item>{{file.title}}</button>
|
||||
</mat-menu>
|
||||
</div>
|
||||
<mat-menu #menu="matMenu">
|
||||
<button *ngFor="let file of available_files" (click)="addContent(file)" mat-menu-item>{{file}}</button>
|
||||
</mat-menu>
|
||||
|
||||
</mat-dialog-content>
|
||||
|
||||
<mat-dialog-actions>
|
||||
<!-- Save -->
|
||||
<button [disabled]="!playlistChanged()" (click)="updatePlaylist()" [disabled]="playlistChanged()" mat-raised-button color="accent"><ng-container i18n="Save">Save</ng-container></button>
|
||||
<button [disabled]="!playlist || !playlistChanged()" (click)="updatePlaylist()" mat-raised-button color="accent"><ng-container i18n="Save">Save</ng-container></button>
|
||||
</mat-dialog-actions>
|
||||
@@ -30,11 +30,6 @@ border: none;
|
||||
transition: transform 250ms cubic-bezier(0, 0, 0.2, 1);
|
||||
}
|
||||
|
||||
.add-content-button {
|
||||
margin-top: 15px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.remove-item-button {
|
||||
right: 10px;
|
||||
position: absolute;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing';
|
||||
|
||||
import { ModifyPlaylistComponent } from './modify-playlist.component';
|
||||
|
||||
@@ -6,7 +6,7 @@ describe('ModifyPlaylistComponent', () => {
|
||||
let component: ModifyPlaylistComponent;
|
||||
let fixture: ComponentFixture<ModifyPlaylistComponent>;
|
||||
|
||||
beforeEach(async(() => {
|
||||
beforeEach(waitForAsync(() => {
|
||||
TestBed.configureTestingModule({
|
||||
declarations: [ ModifyPlaylistComponent ]
|
||||
})
|
||||
|
||||
@@ -10,11 +10,16 @@ import { PostsService } from 'app/posts.services';
|
||||
})
|
||||
export class ModifyPlaylistComponent implements OnInit {
|
||||
|
||||
playlist_id = null;
|
||||
|
||||
original_playlist = null;
|
||||
playlist = null;
|
||||
playlist_file_objs = null;
|
||||
|
||||
available_files = [];
|
||||
all_files = [];
|
||||
playlist_updated = false;
|
||||
reverse_order = false;
|
||||
|
||||
constructor(@Inject(MAT_DIALOG_DATA) public data: any,
|
||||
private postsService: PostsService,
|
||||
@@ -22,10 +27,11 @@ export class ModifyPlaylistComponent implements OnInit {
|
||||
|
||||
ngOnInit(): void {
|
||||
if (this.data) {
|
||||
this.playlist = JSON.parse(JSON.stringify(this.data.playlist));
|
||||
this.original_playlist = JSON.parse(JSON.stringify(this.data.playlist));
|
||||
this.getFiles();
|
||||
this.playlist_id = this.data.playlist_id;
|
||||
this.getPlaylist();
|
||||
}
|
||||
|
||||
this.reverse_order = localStorage.getItem('default_playlist_order_reversed') === 'true';
|
||||
}
|
||||
|
||||
getFiles() {
|
||||
@@ -41,43 +47,61 @@ export class ModifyPlaylistComponent implements OnInit {
|
||||
}
|
||||
|
||||
processFiles(new_files = null) {
|
||||
if (new_files) { this.all_files = new_files.map(file => file.id); }
|
||||
this.available_files = this.all_files.filter(e => !this.playlist.fileNames.includes(e))
|
||||
if (new_files) { this.all_files = new_files; }
|
||||
this.available_files = this.all_files.filter(e => !this.playlist_file_objs.includes(e))
|
||||
}
|
||||
|
||||
updatePlaylist() {
|
||||
this.playlist['uids'] = this.playlist_file_objs.map(playlist_file_obj => playlist_file_obj['uid'])
|
||||
this.postsService.updatePlaylist(this.playlist).subscribe(res => {
|
||||
this.playlist_updated = true;
|
||||
this.postsService.openSnackBar('Playlist updated successfully.');
|
||||
this.getPlaylist();
|
||||
this.postsService.playlists_changed.next(true);
|
||||
});
|
||||
}
|
||||
|
||||
playlistChanged() {
|
||||
return JSON.stringify(this.playlist) === JSON.stringify(this.original_playlist);
|
||||
return JSON.stringify(this.playlist) !== JSON.stringify(this.original_playlist);
|
||||
}
|
||||
|
||||
getPlaylist() {
|
||||
this.postsService.getPlaylist(this.playlist.id, this.playlist.type, null).subscribe(res => {
|
||||
this.postsService.getPlaylist(this.playlist_id, null, true).subscribe(res => {
|
||||
if (res['playlist']) {
|
||||
this.playlist = res['playlist'];
|
||||
this.playlist_file_objs = res['file_objs'];
|
||||
this.original_playlist = JSON.parse(JSON.stringify(this.playlist));
|
||||
this.getFiles();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
addContent(file) {
|
||||
this.playlist.fileNames.push(file);
|
||||
this.playlist_file_objs.push(file);
|
||||
this.playlist.uids.push(file.uid);
|
||||
this.processFiles();
|
||||
}
|
||||
|
||||
removeContent(index) {
|
||||
this.playlist.fileNames.splice(index, 1);
|
||||
if (this.reverse_order) {
|
||||
index = this.playlist_file_objs.length - 1 - index;
|
||||
}
|
||||
this.playlist_file_objs.splice(index, 1);
|
||||
this.playlist.uids.splice(index, 1);
|
||||
this.processFiles();
|
||||
}
|
||||
|
||||
togglePlaylistOrder() {
|
||||
this.reverse_order = !this.reverse_order;
|
||||
localStorage.setItem('default_playlist_order_reversed', '' + this.reverse_order);
|
||||
}
|
||||
|
||||
drop(event: CdkDragDrop<string[]>) {
|
||||
moveItemInArray(this.playlist.fileNames, event.previousIndex, event.currentIndex);
|
||||
if (this.reverse_order) {
|
||||
event.previousIndex = this.playlist_file_objs.length - 1 - event.previousIndex;
|
||||
event.currentIndex = this.playlist_file_objs.length - 1 - event.currentIndex;
|
||||
}
|
||||
moveItemInArray(this.playlist_file_objs, event.previousIndex, event.currentIndex);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing';
|
||||
|
||||
import { SetDefaultAdminDialogComponent } from './set-default-admin-dialog.component';
|
||||
|
||||
@@ -6,7 +6,7 @@ describe('SetDefaultAdminDialogComponent', () => {
|
||||
let component: SetDefaultAdminDialogComponent;
|
||||
let fixture: ComponentFixture<SetDefaultAdminDialogComponent>;
|
||||
|
||||
beforeEach(async(() => {
|
||||
beforeEach(waitForAsync(() => {
|
||||
TestBed.configureTestingModule({
|
||||
declarations: [ SetDefaultAdminDialogComponent ]
|
||||
})
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
<h4 mat-dialog-title>
|
||||
<ng-container *ngIf="is_playlist" i18n="Share playlist dialog title">Share playlist</ng-container>
|
||||
<ng-container *ngIf="!is_playlist && type === 'video'" i18n="Share video dialog title">Share video</ng-container>
|
||||
<ng-container *ngIf="!is_playlist && type === 'audio'" i18n="Share audio dialog title">Share audio</ng-container>
|
||||
<ng-container *ngIf="!is_playlist" i18n="Share video dialog title">Share file</ng-container>
|
||||
</h4>
|
||||
|
||||
<mat-dialog-content>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing';
|
||||
|
||||
import { ShareMediaDialogComponent } from './share-media-dialog.component';
|
||||
|
||||
@@ -6,7 +6,7 @@ describe('ShareMediaDialogComponent', () => {
|
||||
let component: ShareMediaDialogComponent;
|
||||
let fixture: ComponentFixture<ShareMediaDialogComponent>;
|
||||
|
||||
beforeEach(async(() => {
|
||||
beforeEach(waitForAsync(() => {
|
||||
TestBed.configureTestingModule({
|
||||
declarations: [ ShareMediaDialogComponent ]
|
||||
})
|
||||
|
||||
@@ -11,7 +11,6 @@ import { PostsService } from 'app/posts.services';
|
||||
})
|
||||
export class ShareMediaDialogComponent implements OnInit {
|
||||
|
||||
type = null;
|
||||
uid = null;
|
||||
uuid = null;
|
||||
share_url = null;
|
||||
@@ -26,14 +25,13 @@ export class ShareMediaDialogComponent implements OnInit {
|
||||
|
||||
ngOnInit(): void {
|
||||
if (this.data) {
|
||||
this.type = this.data.type;
|
||||
this.uid = this.data.uid;
|
||||
this.uuid = this.data.uuid;
|
||||
this.sharing_enabled = this.data.sharing_enabled;
|
||||
this.is_playlist = this.data.is_playlist;
|
||||
this.current_timestamp = (this.data.current_timestamp / 1000).toFixed(2);
|
||||
|
||||
const arg = (this.is_playlist ? ';id=' : ';uid=');
|
||||
const arg = (this.is_playlist ? ';playlist_id=' : ';uid=');
|
||||
this.default_share_url = window.location.href.split(';')[0] + arg + this.uid;
|
||||
if (this.uuid) {
|
||||
this.default_share_url += ';uuid=' + this.uuid;
|
||||
@@ -65,7 +63,7 @@ export class ShareMediaDialogComponent implements OnInit {
|
||||
|
||||
sharingChanged(event) {
|
||||
if (event.checked) {
|
||||
this.postsService.enableSharing(this.uid, this.type, this.is_playlist).subscribe(res => {
|
||||
this.postsService.enableSharing(this.uid, this.is_playlist).subscribe(res => {
|
||||
if (res['success']) {
|
||||
this.openSnackBar('Sharing enabled.');
|
||||
this.sharing_enabled = true;
|
||||
@@ -76,7 +74,7 @@ export class ShareMediaDialogComponent implements OnInit {
|
||||
this.openSnackBar('Failed to enable sharing - server error.');
|
||||
});
|
||||
} else {
|
||||
this.postsService.disableSharing(this.uid, this.type, this.is_playlist).subscribe(res => {
|
||||
this.postsService.disableSharing(this.uid, this.is_playlist).subscribe(res => {
|
||||
if (res['success']) {
|
||||
this.openSnackBar('Sharing disabled.');
|
||||
this.sharing_enabled = false;
|
||||
|
||||
@@ -35,6 +35,13 @@
|
||||
</mat-select>
|
||||
</mat-form-field>
|
||||
</div>
|
||||
<div class="col-12 mt-2">
|
||||
<mat-form-field>
|
||||
<mat-select placeholder="Max quality" i18n-placeholder="Max quality placeholder" [disabled]="audioOnlyMode" [(ngModel)]="maxQuality">
|
||||
<mat-option *ngFor="let available_quality of available_qualities" [value]="available_quality['value']">{{available_quality['label']}}</mat-option>
|
||||
</mat-select>
|
||||
</mat-form-field>
|
||||
</div>
|
||||
<div class="col-12">
|
||||
<div>
|
||||
<mat-checkbox [(ngModel)]="audioOnlyMode"><ng-container i18n="Streaming-only mode">Audio-only mode</ng-container></mat-checkbox>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing';
|
||||
|
||||
import { SubscribeDialogComponent } from './subscribe-dialog.component';
|
||||
|
||||
@@ -6,7 +6,7 @@ describe('SubscribeDialogComponent', () => {
|
||||
let component: SubscribeDialogComponent;
|
||||
let fixture: ComponentFixture<SubscribeDialogComponent>;
|
||||
|
||||
beforeEach(async(() => {
|
||||
beforeEach(waitForAsync(() => {
|
||||
TestBed.configureTestingModule({
|
||||
declarations: [ SubscribeDialogComponent ]
|
||||
})
|
||||
|
||||
@@ -17,6 +17,8 @@ export class SubscribeDialogComponent implements OnInit {
|
||||
url = null;
|
||||
name = null;
|
||||
|
||||
maxQuality = 'best';
|
||||
|
||||
// state
|
||||
subscribing = false;
|
||||
|
||||
@@ -29,12 +31,43 @@ export class SubscribeDialogComponent implements OnInit {
|
||||
customFileOutput = '';
|
||||
customArgs = '';
|
||||
|
||||
available_qualities = [
|
||||
{
|
||||
'label': 'Best',
|
||||
'value': 'best'
|
||||
},
|
||||
{
|
||||
'label': '4K',
|
||||
'value': '2160'
|
||||
},
|
||||
{
|
||||
'label': '1440p',
|
||||
'value': '1440'
|
||||
},
|
||||
{
|
||||
'label': '1080p',
|
||||
'value': '1080'
|
||||
},
|
||||
{
|
||||
'label': '720p',
|
||||
'value': '720'
|
||||
},
|
||||
{
|
||||
'label': '480p',
|
||||
'value': '480'
|
||||
},
|
||||
{
|
||||
'label': '360p',
|
||||
'value': '360'
|
||||
}
|
||||
];
|
||||
|
||||
time_units = [
|
||||
'day',
|
||||
'week',
|
||||
'month',
|
||||
'year'
|
||||
]
|
||||
];
|
||||
|
||||
constructor(private postsService: PostsService,
|
||||
private snackBar: MatSnackBar,
|
||||
@@ -57,7 +90,7 @@ export class SubscribeDialogComponent implements OnInit {
|
||||
if (!this.download_all) {
|
||||
timerange = 'now-' + this.timerange_amount.toString() + this.timerange_unit;
|
||||
}
|
||||
this.postsService.createSubscription(this.url, this.name, timerange, this.streamingOnlyMode,
|
||||
this.postsService.createSubscription(this.url, this.name, timerange, this.streamingOnlyMode, this.maxQuality,
|
||||
this.audioOnlyMode, this.customArgs, this.customFileOutput).subscribe(res => {
|
||||
this.subscribing = false;
|
||||
if (res['new_sub']) {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
<h4 mat-dialog-title>{{sub.name}}</h4>
|
||||
<h4 mat-dialog-title>{{sub.name}} <ng-container *ngIf="sub.paused" i18n="Paused suffix">(Paused)</ng-container></h4>
|
||||
|
||||
<mat-dialog-content>
|
||||
<div class="info-item">
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing';
|
||||
|
||||
import { SubscriptionInfoDialogComponent } from './subscription-info-dialog.component';
|
||||
|
||||
@@ -6,7 +6,7 @@ describe('SubscriptionInfoDialogComponent', () => {
|
||||
let component: SubscriptionInfoDialogComponent;
|
||||
let fixture: ComponentFixture<SubscriptionInfoDialogComponent>;
|
||||
|
||||
beforeEach(async(() => {
|
||||
beforeEach(waitForAsync(() => {
|
||||
TestBed.configureTestingModule({
|
||||
declarations: [ SubscriptionInfoDialogComponent ]
|
||||
})
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing';
|
||||
|
||||
import { UpdateProgressDialogComponent } from './update-progress-dialog.component';
|
||||
|
||||
@@ -6,7 +6,7 @@ describe('UpdateProgressDialogComponent', () => {
|
||||
let component: UpdateProgressDialogComponent;
|
||||
let fixture: ComponentFixture<UpdateProgressDialogComponent>;
|
||||
|
||||
beforeEach(async(() => {
|
||||
beforeEach(waitForAsync(() => {
|
||||
TestBed.configureTestingModule({
|
||||
declarations: [ UpdateProgressDialogComponent ]
|
||||
})
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing';
|
||||
|
||||
import { UserProfileDialogComponent } from './user-profile-dialog.component';
|
||||
|
||||
@@ -6,7 +6,7 @@ describe('UserProfileDialogComponent', () => {
|
||||
let component: UserProfileDialogComponent;
|
||||
let fixture: ComponentFixture<UserProfileDialogComponent>;
|
||||
|
||||
beforeEach(async(() => {
|
||||
beforeEach(waitForAsync(() => {
|
||||
TestBed.configureTestingModule({
|
||||
declarations: [ UserProfileDialogComponent ]
|
||||
})
|
||||
|
||||
@@ -25,6 +25,10 @@
|
||||
<div class="info-item-label"><strong><ng-container i18n="Video upload date property">Upload Date:</ng-container> </strong></div>
|
||||
<div class="info-item-value">{{file.upload_date ? file.upload_date : 'N/A'}}</div>
|
||||
</div>
|
||||
<div class="info-item">
|
||||
<div class="info-item-label"><strong><ng-container i18n="Category property">Category:</ng-container> </strong></div>
|
||||
<div class="info-item-value"><ng-container *ngIf="file.category"><mat-chip-list><mat-chip>{{file.category.name}}</mat-chip></mat-chip-list></ng-container><ng-container *ngIf="!file.category">N/A</ng-container></div>
|
||||
</div>
|
||||
</mat-dialog-content>
|
||||
|
||||
<mat-dialog-actions>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing';
|
||||
|
||||
import { VideoInfoDialogComponent } from './video-info-dialog.component';
|
||||
|
||||
@@ -6,7 +6,7 @@ describe('VideoInfoDialogComponent', () => {
|
||||
let component: VideoInfoDialogComponent;
|
||||
let fixture: ComponentFixture<VideoInfoDialogComponent>;
|
||||
|
||||
beforeEach(async(() => {
|
||||
beforeEach(waitForAsync(() => {
|
||||
TestBed.configureTestingModule({
|
||||
declarations: [ VideoInfoDialogComponent ]
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user