mirror of
https://github.com/Tzahi12345/YoutubeDL-Material.git
synced 2026-03-24 05:30:57 +03:00
Improved archive viewer
Added archive importing
This commit is contained in:
@@ -1,20 +1,42 @@
|
||||
<!-- (selectionChange)="sidePanelModeChanged($event.value)" -->
|
||||
<mat-form-field class="filter">
|
||||
<mat-icon matPrefix>search</mat-icon>
|
||||
<mat-label i18n="Filter">Filter</mat-label>
|
||||
<input matInput (keyup)="applyFilter($event)" #input>
|
||||
</mat-form-field>
|
||||
|
||||
<div [hidden]="!(archives && archives.length > 0)">
|
||||
<div style="overflow: hidden;" class="mat-elevation-z8">
|
||||
<mat-table style="overflow: hidden" matSort [dataSource]="dataSource">
|
||||
<div class="mat-elevation-z8">
|
||||
<mat-table matSort [dataSource]="dataSource">
|
||||
|
||||
<!-- Select Column -->
|
||||
<!-- Checkbox Column -->
|
||||
<ng-container matColumnDef="select">
|
||||
<mat-header-cell *matHeaderCellDef>
|
||||
<mat-checkbox (change)="$event ? toggleAllRows() : null"
|
||||
[checked]="selection.hasValue() && isAllSelected()"
|
||||
[indeterminate]="selection.hasValue() && !isAllSelected()">
|
||||
</mat-checkbox>
|
||||
</mat-header-cell>
|
||||
<mat-cell *matCellDef="let row">
|
||||
<mat-checkbox (click)="$event.stopPropagation()"
|
||||
(change)="$event ? selection.toggle(row) : null"
|
||||
[checked]="selection.isSelected(row)">
|
||||
</mat-checkbox>
|
||||
<mat-icon class="audio-video-icon">{{(row.type === 'audio') ? 'audiotrack' : 'movie'}}</mat-icon>
|
||||
</mat-cell>
|
||||
</ng-container>
|
||||
|
||||
<!-- Date Column -->
|
||||
<ng-container matColumnDef="timestamp">
|
||||
<mat-header-cell *matHeaderCellDef mat-sort-header> <ng-container i18n="Date">Date</ng-container> </mat-header-cell>
|
||||
<mat-cell *matCellDef="let element"> {{element.timestamp | date: 'short'}} </mat-cell>
|
||||
<mat-cell *matCellDef="let element"> {{element.timestamp*1000 | date: 'short'}} </mat-cell>
|
||||
</ng-container>
|
||||
|
||||
<!-- Title Column -->
|
||||
<ng-container matColumnDef="title">
|
||||
<mat-header-cell *matHeaderCellDef mat-sort-header> <ng-container i18n="Title">Title</ng-container> </mat-header-cell>
|
||||
<mat-cell *matCellDef="let element">
|
||||
<span class="one-line" [matTooltip]="element.title ? element.title : null">
|
||||
<span class="max-two-lines" [matTooltip]="element.title ? element.title : null">
|
||||
{{element.title}}
|
||||
</span>
|
||||
</mat-cell>
|
||||
@@ -40,7 +62,7 @@
|
||||
</mat-cell>
|
||||
</ng-container>
|
||||
|
||||
<mat-header-row *matHeaderRowDef="displayedColumns"></mat-header-row>
|
||||
<mat-header-row *matHeaderRowDef="displayedColumns; sticky: true"></mat-header-row>
|
||||
<mat-row *matRowDef="let row; columns: displayedColumns;"></mat-row>
|
||||
</mat-table>
|
||||
</div>
|
||||
@@ -48,4 +70,71 @@
|
||||
|
||||
<div *ngIf="(!archives || archives.length === 0)">
|
||||
<h4 style="text-align: center; margin-top: 10px;" i18n="Archives empty">Archives empty</h4>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div style="margin: 10px 10px 10px 0px;">
|
||||
<button [disabled]="selection.selected.length === 0" color="warn" style="margin: 10px;" mat-stroked-button i18n="Delete selected" (click)="openDeleteSelectedArchivesDialog()">Delete selected</button>
|
||||
<span style="float: right">
|
||||
<mat-form-field style="width: 150px;">
|
||||
<mat-label i18n="Subscription">Subscription</mat-label>
|
||||
<mat-select [ngModel]="sub_id" (ngModelChange)="subFilterSelectionChanged($event)">
|
||||
<mat-option [value]="'none'" i18n="None">None</mat-option>
|
||||
<mat-option *ngFor="let sub of postsService.subscriptions" [value]="sub.id">{{sub.name}}</mat-option>
|
||||
</mat-select>
|
||||
</mat-form-field>
|
||||
<mat-form-field style="width: 100px; margin-left: 10px;">
|
||||
<mat-label i18n="File type">File type</mat-label>
|
||||
<mat-select [ngModel]="type" (ngModelChange)="typeFilterSelectionChanged($event)" [disabled]="sub_id !== 'none'">
|
||||
<mat-option [value]="'both'" i18n="Both">Both</mat-option>
|
||||
<mat-option [value]="'video'" i18n="Video">Video</mat-option>
|
||||
<mat-option [value]="'audio'" i18n="Audio">Audio</mat-option>
|
||||
</mat-select>
|
||||
</mat-form-field>
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<ngx-file-drop [multiple]="false" accept=".txt" dropZoneLabel="Drop file here" (onFileDrop)="dropped($event)">
|
||||
<ng-template class="file-drop" ngx-file-drop-content-tmp let-openFileSelector="openFileSelector">
|
||||
<div style="text-align: center">
|
||||
<div>
|
||||
<ng-container i18n="Drag and Drop">Drag and Drop</ng-container>
|
||||
</div>
|
||||
<div style="margin-top: 6px;">
|
||||
<button mat-stroked-button (click)="openFileSelector()">Browse Files</button>
|
||||
</div>
|
||||
</div>
|
||||
</ng-template>
|
||||
</ngx-file-drop>
|
||||
</div>
|
||||
|
||||
<div style="margin-top: 10px; color: white">
|
||||
<table class="table">
|
||||
<tbody class="upload-name-style">
|
||||
<tr *ngFor="let item of files; let i=index">
|
||||
<td style="vertical-align: middle; border-top: unset">
|
||||
<strong>{{ item.relativePath }}</strong>
|
||||
</td>
|
||||
<td style="border-top: unset">
|
||||
<div style="float: right">
|
||||
<mat-form-field style="width: 150px;">
|
||||
<mat-label i18n="Subscription">Subscription</mat-label>
|
||||
<mat-select [ngModel]="upload_sub_id" (ngModelChange)="subUploadFilterSelectionChanged($event)">
|
||||
<mat-option [value]="'none'" i18n="None">None</mat-option>
|
||||
<mat-option *ngFor="let sub of postsService.subscriptions" [value]="sub.id">{{sub.name}}</mat-option>
|
||||
</mat-select>
|
||||
</mat-form-field>
|
||||
<mat-form-field style="width: 100px; margin-left: 10px">
|
||||
<mat-label i18n="File type">File type</mat-label>
|
||||
<mat-select [(ngModel)]="upload_type" [value]="upload_type" [disabled]="upload_sub_id !== 'none'">
|
||||
<mat-option [value]="'video'" i18n="Video">Video</mat-option>
|
||||
<mat-option [value]="'audio'" i18n="Audio">Audio</mat-option>
|
||||
</mat-select>
|
||||
</mat-form-field>
|
||||
<button style="margin-left: 10px" [disabled]="uploading_archive || uploaded_archive" (click)="importArchive()" matTooltip="Upload" i18n-matTooltip="Upload" mat-mini-fab><mat-icon>publish</mat-icon><mat-spinner *ngIf="uploading_archive" class="spinner" [diameter]="38"></mat-spinner></button>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
.filter {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.spinner {
|
||||
bottom: 1px;
|
||||
left: 0.5px;
|
||||
position: absolute;
|
||||
}
|
||||
|
||||
.mat-mdc-table {
|
||||
width: 100%;
|
||||
max-height: 60vh;
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
.max-two-lines {
|
||||
display: -webkit-box;
|
||||
display: -moz-box;
|
||||
max-height: 2.4em;
|
||||
line-height: 1.2em;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
-webkit-box-orient: vertical;
|
||||
-webkit-line-clamp: 2;
|
||||
}
|
||||
|
||||
::ng-deep .ngx-file-drop__content {
|
||||
width: 100%;
|
||||
top: -12px;
|
||||
position: relative;
|
||||
}
|
||||
@@ -1,8 +1,13 @@
|
||||
import { SelectionModel } from '@angular/cdk/collections';
|
||||
import { Component, ViewChild } from '@angular/core';
|
||||
import { MatDialog } from '@angular/material/dialog';
|
||||
import { MatSort } from '@angular/material/sort';
|
||||
import { MatTableDataSource } from '@angular/material/table';
|
||||
import { FileType } from 'api-types';
|
||||
import { Archive } from 'api-types/models/Archive';
|
||||
import { ConfirmDialogComponent } from 'app/dialogs/confirm-dialog/confirm-dialog.component';
|
||||
import { PostsService } from 'app/posts.services';
|
||||
import { NgxFileDropEntry } from 'ngx-file-drop';
|
||||
|
||||
@Component({
|
||||
selector: 'app-archive-viewer',
|
||||
@@ -10,23 +15,89 @@ import { PostsService } from 'app/posts.services';
|
||||
styleUrls: ['./archive-viewer.component.scss']
|
||||
})
|
||||
export class ArchiveViewerComponent {
|
||||
archives = null;
|
||||
displayedColumns: string[] = ['timestamp', 'title', 'id', 'extractor'];
|
||||
// table
|
||||
displayedColumns: string[] = ['select', 'timestamp', 'title', 'id', 'extractor'];
|
||||
dataSource = null;
|
||||
selection = new SelectionModel<Archive>(true, []);
|
||||
|
||||
// general
|
||||
archives = null;
|
||||
archives_retrieved = false;
|
||||
sub_id = 'none';
|
||||
upload_sub_id = 'none';
|
||||
type: FileType | 'both' = 'both';
|
||||
upload_type: FileType = FileType.VIDEO;
|
||||
|
||||
// importing
|
||||
uploading_archive = false;
|
||||
uploaded_archive = false;
|
||||
files = [];
|
||||
|
||||
typeSelectOptions = {
|
||||
video: {
|
||||
key: 'video',
|
||||
label: $localize`Video`
|
||||
},
|
||||
audio: {
|
||||
key: 'audio',
|
||||
label: $localize`Audio`
|
||||
}
|
||||
};
|
||||
|
||||
@ViewChild(MatSort) sort: MatSort;
|
||||
|
||||
constructor(private postsService: PostsService) {
|
||||
constructor(public postsService: PostsService, private dialog: MatDialog) {
|
||||
|
||||
}
|
||||
|
||||
filterSelectionChanged(value: string): void {
|
||||
this.getArchives(value);
|
||||
ngOnInit() {
|
||||
this.getArchives();
|
||||
}
|
||||
|
||||
getArchives(sub_id: string = null): void {
|
||||
this.postsService.getArchives(sub_id).subscribe(res => {
|
||||
applyFilter(event: Event) {
|
||||
const filterValue = (event.target as HTMLInputElement).value;
|
||||
this.dataSource.filter = filterValue.trim().toLowerCase();
|
||||
}
|
||||
|
||||
/** Whether the number of selected elements matches the total number of rows. */
|
||||
isAllSelected() {
|
||||
const numSelected = this.selection.selected.length;
|
||||
const numRows = this.dataSource.data.length;
|
||||
return numSelected === numRows;
|
||||
}
|
||||
|
||||
/** Selects all rows if they are not all selected; otherwise clear selection. */
|
||||
toggleAllRows() {
|
||||
if (this.isAllSelected()) {
|
||||
this.selection.clear();
|
||||
return;
|
||||
}
|
||||
|
||||
this.selection.select(...this.dataSource.data);
|
||||
}
|
||||
|
||||
typeFilterSelectionChanged(value): void {
|
||||
this.type = value;
|
||||
this.getArchives();
|
||||
}
|
||||
|
||||
subFilterSelectionChanged(value): void {
|
||||
this.sub_id = value;
|
||||
if (this.sub_id !== 'none') {
|
||||
this.type = this.postsService.getSubscriptionByID(this.sub_id)['type'];
|
||||
}
|
||||
this.getArchives();
|
||||
}
|
||||
|
||||
subUploadFilterSelectionChanged(value): void {
|
||||
this.upload_sub_id = value;
|
||||
if (this.upload_sub_id !== 'none') {
|
||||
this.upload_type = this.postsService.getSubscriptionByID(this.upload_sub_id)['type'];
|
||||
}
|
||||
}
|
||||
|
||||
getArchives(): void {
|
||||
this.postsService.getArchives(this.type === 'both' ? null : this.type, this.sub_id === 'none' ? null : this.sub_id).subscribe(res => {
|
||||
if (res['archives'] !== null
|
||||
&& res['archives'] !== undefined
|
||||
&& JSON.stringify(this.archives) !== JSON.stringify(res['archives'])) {
|
||||
@@ -38,4 +109,78 @@ export class ArchiveViewerComponent {
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
importArchive(): void {
|
||||
this.uploading_archive = true;
|
||||
for (const droppedFile of this.files) {
|
||||
// Is it a file?
|
||||
if (droppedFile.fileEntry.isFile) {
|
||||
const fileEntry = droppedFile.fileEntry as FileSystemFileEntry;
|
||||
fileEntry.file(async (file: File) => {
|
||||
const archive_base64 = await blobToBase64(file);
|
||||
this.postsService.importArchive(archive_base64 as string, this.upload_type, this.upload_sub_id === 'none' ? null : this.upload_sub_id).subscribe(res => {
|
||||
this.uploading_archive = false;
|
||||
if (res['success']) {
|
||||
this.uploaded_archive = true;
|
||||
this.postsService.openSnackBar($localize`Archive successfully imported!`);
|
||||
}
|
||||
this.getArchives();
|
||||
}, err => {
|
||||
console.error(err);
|
||||
this.uploading_archive = false;
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
openDeleteSelectedArchivesDialog(): void {
|
||||
const dialogRef = this.dialog.open(ConfirmDialogComponent, {
|
||||
data: {
|
||||
dialogTitle: $localize`Delete archives`,
|
||||
dialogText: $localize`Would you like to delete ${this.selection.selected.length}:selected archives amount: archive(s)?`,
|
||||
submitText: $localize`Delete`,
|
||||
warnSubmitColor: true
|
||||
}
|
||||
});
|
||||
dialogRef.afterClosed().subscribe(confirmed => {
|
||||
if (confirmed) {
|
||||
this.deleteSelectedArchives();
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
deleteSelectedArchives(): void {
|
||||
for (const archive of this.selection.selected) {
|
||||
this.archives = this.archives.filter((_archive: Archive) => !(archive['extractor'] === _archive['extractor'] && archive['id'] !== _archive['id']));
|
||||
}
|
||||
this.postsService.deleteArchiveItems(this.selection.selected).subscribe(res => {
|
||||
if (res['success']) {
|
||||
this.postsService.openSnackBar($localize`Successfully deleted archive items!`);
|
||||
} else {
|
||||
this.postsService.openSnackBar($localize`Failed to delete archive items!`);
|
||||
}
|
||||
this.getArchives();
|
||||
});
|
||||
this.selection.clear();
|
||||
}
|
||||
|
||||
public dropped(files: NgxFileDropEntry[]) {
|
||||
this.files = files;
|
||||
this.uploading_archive = false;
|
||||
this.uploaded_archive = false;
|
||||
}
|
||||
|
||||
originalOrder = (): number => {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
function blobToBase64(blob: Blob) {
|
||||
return new Promise((resolve, _) => {
|
||||
const reader = new FileReader();
|
||||
reader.onloadend = () => resolve(reader.result);
|
||||
reader.readAsDataURL(blob);
|
||||
});
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user