added multiple download support

lazy loaded images now reload after a new download
This commit is contained in:
Isaac Grynsztein
2020-02-27 01:06:32 -05:00
parent b646db4828
commit bcd879ebc8
13 changed files with 192 additions and 15 deletions

View File

@@ -18,7 +18,8 @@
"title_top": "Youtube Downloader",
"file_manager_enabled": true,
"allow_quality_select": true,
"download_only_mode": false
"download_only_mode": false,
"allow_multi_download_mode": true
},
"API": {
"use_youtube_API": false,

View File

@@ -18,7 +18,8 @@
"title_top": "Youtube Downloader",
"file_manager_enabled": true,
"allow_quality_select": true,
"download_only_mode": false
"download_only_mode": false,
"allow_multi_download_mode": true
},
"API": {
"use_youtube_API": false,

View File

@@ -56,6 +56,11 @@ let CONFIG_ITEMS = {
'key': 'ytdl_download_only_mode',
'path': 'YoutubeDLMaterial.Extra.download_only_mode'
},
'ytdl_allow_multi_download_mode': {
'key': 'ytdl_allow_multi_download_mode',
'path': 'YoutubeDLMaterial.Extra.allow_multi_download_mode'
},
// API
'ytdl_use_youtube_api': {

View File

@@ -17,6 +17,7 @@ services:
ytdl_file_manager_enabled: 'true'
ytdl_allow_quality_select: 'true'
ytdl_download_only_mode: 'false'
ytdl_allow_multi_download_mode: true
ytdl_use_youtube_api: 'false'
ytdl_youtube_api_key: 'false'
ytdl_default_theme: default

View File

@@ -25,11 +25,12 @@ import {VgBufferingModule} from 'videogular2/compiled/buffering';
import { InputDialogComponent } from './input-dialog/input-dialog.component';
import { LazyLoadImageModule, IsVisibleProps } from 'ng-lazyload-image';
import { NgxContentLoadingModule } from 'ngx-content-loading';
import { audioFilesMouseHovering, videoFilesMouseHovering } from './main/main.component';
import { audioFilesMouseHovering, videoFilesMouseHovering, audioFilesOpened, videoFilesOpened } from './main/main.component';
import { CreatePlaylistComponent } from './create-playlist/create-playlist.component';
import { DownloadItemComponent } from './download-item/download-item.component';
export function isVisible({ event, element, scrollContainer, offset }: IsVisibleProps<any>) {
return (element.id === 'video' ? videoFilesMouseHovering : audioFilesMouseHovering);
return (element.id === 'video' ? videoFilesMouseHovering || videoFilesOpened : audioFilesMouseHovering || audioFilesOpened);
}
@NgModule({
@@ -39,7 +40,8 @@ export function isVisible({ event, element, scrollContainer, offset }: IsVisible
MainComponent,
PlayerComponent,
InputDialogComponent,
CreatePlaylistComponent
CreatePlaylistComponent,
DownloadItemComponent
],
imports: [
BrowserModule,

View File

@@ -0,0 +1,16 @@
<div>
<mat-grid-list [rowHeight]="50" [cols]="24">
<mat-grid-tile [colspan]="2">
<h5 style="display: inline-block; margin-right: 5px; position: relative; top: 5px;">{{queueNumber}}.</h5>
</mat-grid-tile>
<mat-grid-tile [colspan]="6">
<div style="display: inline-block; text-align: center;">ID: {{url_id}}</div>
</mat-grid-tile>
<mat-grid-tile [colspan]="13">
<mat-progress-bar style="width: 80%" [value]="download.percent_complete" [mode]="(download.percent_complete === 0) ? 'indeterminate' : 'determinate'"></mat-progress-bar>
</mat-grid-tile>
<mat-grid-tile [colspan]="3">
<button (click)="cancelTheDownload()" mat-icon-button color="warn"><mat-icon fontSet="material-icons-outlined">cancel</mat-icon></button>
</mat-grid-tile>
</mat-grid-list>
</div>

View File

@@ -0,0 +1,25 @@
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { DownloadItemComponent } from './download-item.component';
describe('DownloadItemComponent', () => {
let component: DownloadItemComponent;
let fixture: ComponentFixture<DownloadItemComponent>;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [ DownloadItemComponent ]
})
.compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(DownloadItemComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});

View File

@@ -0,0 +1,41 @@
import { Component, OnInit, Output, EventEmitter, Input } from '@angular/core';
import { Download } from 'app/main/main.component';
@Component({
selector: 'app-download-item',
templateUrl: './download-item.component.html',
styleUrls: ['./download-item.component.scss']
})
export class DownloadItemComponent implements OnInit {
@Input() download: Download = {
uid: null,
type: 'audio',
percent_complete: 0,
url: 'http://youtube.com/watch?v=17848rufj',
downloading: true,
is_playlist: false
};
@Output() cancelDownload = new EventEmitter<Download>();
@Input() queueNumber = null;
url_id = null;
constructor() { }
ngOnInit() {
if (this.download && this.download.url && this.download.url.includes('youtube')) {
const string_id = (this.download.is_playlist ? '?list=' : '?v=')
const index_offset = (this.download.is_playlist ? 6 : 3);
const end_index = this.download.url.indexOf(string_id) + index_offset;
this.url_id = this.download.url.substring(end_index, this.download.url.length);
}
}
cancelTheDownload() {
this.cancelDownload.emit(this.download);
}
}

View File

@@ -50,6 +50,7 @@
</form>
<br/>
<mat-checkbox [disabled]="current_download" (change)="videoModeChanged($event)" [(ngModel)]="audioOnly" style="float: left; margin-top: -12px">Only Audio</mat-checkbox>
<mat-checkbox [disabled]="current_download" (change)="multiDownloadModeChanged($event)" [(ngModel)]="multiDownloadMode" style="float: right; margin-top: -12px">Multi-download mode</mat-checkbox>
</div>
</mat-card-content>
@@ -60,6 +61,18 @@
</mat-card-actions>
</mat-card>
</div>
<div *ngIf="downloads.length > 0 && !current_download" style="margin-top: 15px;" class="big demo-basic">
<mat-card id="card" style="margin-right: 20px; margin-left: 20px;">
<div class="container">
<div *ngFor="let download of downloads; let i = index;" class="row">
<ng-container *ngIf="current_download !== download">
<app-download-item style="width: 100%" [download]="download" [queueNumber]="i+1" (cancelDownload)="cancelDownload($event)"></app-download-item>
<mat-divider style="position: relative" *ngIf="i !== downloads.length - 1"></mat-divider>
</ng-container>
</div>
</div>
</mat-card>
</div>
<br/>
<div class="centered big" id="bar_div" *ngIf="current_download && current_download.downloading; else nofile">
<div class="margined">
@@ -81,7 +94,7 @@
</ng-template>
<div style="margin: 20px" *ngIf="fileManagerEnabled">
<mat-accordion>
<mat-expansion-panel (mouseleave)="accordionLeft('audio')" (mouseenter)="accordionEntered('audio')" class="big">
<mat-expansion-panel (opened)="accordionOpened('audio')" (closed)="accordionClosed('audio')" (mouseleave)="accordionLeft('audio')" (mouseenter)="accordionEntered('audio')" class="big">
<mat-expansion-panel-header>
<mat-panel-title>
Audio
@@ -116,7 +129,7 @@
</div>
</mat-expansion-panel>
<mat-expansion-panel (mouseleave)="accordionLeft('video')" (mouseenter)="accordionEntered('video')" class="big">
<mat-expansion-panel (opened)="accordionOpened('video')" (closed)="accordionClosed('video')" (mouseleave)="accordionLeft('video')" (mouseenter)="accordionEntered('video')" class="big">
<mat-expansion-panel-header>
<mat-panel-title>
Video

View File

@@ -22,6 +22,8 @@ import { v4 as uuid } from 'uuid';
export let audioFilesMouseHovering = false;
export let videoFilesMouseHovering = false;
export let audioFilesOpened = false;
export let videoFilesOpened = false;
export interface Download {
uid: string;
@@ -44,6 +46,7 @@ export class MainComponent implements OnInit {
determinateProgress = false;
downloadingfile = false;
audioOnly: boolean;
multiDownloadMode = false;
urlError = false;
path = '';
url = '';
@@ -179,11 +182,19 @@ export class MainComponent implements OnInit {
last_valid_url = '';
last_url_check = 0;
test_download: Download = {
uid: null,
type: 'audio',
percent_complete: 0,
url: 'http://youtube.com/watch?v=17848rufj',
downloading: true,
is_playlist: false
};
constructor(private postsService: PostsService, private youtubeSearch: YoutubeSearchService, public snackBar: MatSnackBar,
private router: Router, public dialog: MatDialog, private platform: Platform) {
this.audioOnly = false;
// loading config
this.postsService.loadNavItems().subscribe(result => { // loads settings
const backendUrl = result['YoutubeDLMaterial']['Host']['backendurl'];
@@ -350,6 +361,10 @@ export class MainComponent implements OnInit {
if (localStorage.getItem('audioOnly') !== null) {
this.audioOnly = localStorage.getItem('audioOnly') === 'true';
}
if (localStorage.getItem('multiDownloadMode') !== null) {
this.multiDownloadMode = localStorage.getItem('multiDownloadMode') === 'true';
}
}
// download helpers
@@ -359,7 +374,7 @@ export class MainComponent implements OnInit {
if (new_download && this.current_download !== new_download) {
// console.log('mismatched downloads');
} else {
} else if (!this.multiDownloadMode) {
// if download only mode, just download the file. no redirect
if (forceView === false && this.downloadOnlyMode && !this.iOS) {
if (is_playlist) {
@@ -379,9 +394,17 @@ export class MainComponent implements OnInit {
}
}
// remove download from current downloads
this.removeDownloadFromCurrentDownloads(new_download);
// reloads mp3s
if (this.fileManagerEnabled) {
this.getMp3s();
setTimeout(() => {
this.audioFileCards.forEach(filecard => {
filecard.onHoverResponse();
});
}, 200);
}
}
@@ -390,7 +413,7 @@ export class MainComponent implements OnInit {
if (new_download && this.current_download !== new_download) {
// console.log('mismatched downloads');
} else {
} else if (!this.multiDownloadMode) {
// if download only mode, just download the file. no redirect
if (forceView === false && this.downloadOnlyMode) {
if (is_playlist) {
@@ -410,9 +433,17 @@ export class MainComponent implements OnInit {
}
}
// remove download from current downloads
this.removeDownloadFromCurrentDownloads(new_download);
// reloads mp4s
if (this.fileManagerEnabled) {
this.getMp4s();
setTimeout(() => {
this.videoFileCards.forEach(filecard => {
filecard.onHoverResponse();
});
}, 200);
}
}
@@ -433,7 +464,7 @@ export class MainComponent implements OnInit {
is_playlist: this.url.includes('playlist')
};
this.downloads.push(new_download);
if (!this.current_download) { this.current_download = new_download };
if (!this.current_download && !this.multiDownloadMode) { this.current_download = new_download };
this.downloadingfile = true;
let customQualityConfiguration = null;
@@ -471,7 +502,7 @@ export class MainComponent implements OnInit {
is_playlist: this.url.includes('playlist')
};
this.downloads.push(new_download);
if (!this.current_download) { this.current_download = new_download };
if (!this.current_download && !this.multiDownloadMode) { this.current_download = new_download };
this.downloadingfile = true;
let customQualityConfiguration = null;
@@ -500,13 +531,23 @@ export class MainComponent implements OnInit {
this.openSnackBar('Download failed!', 'OK.');
});
}
if (this.multiDownloadMode) {
this.url = '';
this.downloadingfile = false;
}
} else {
this.urlError = true;
}
}
// download canceled handler
cancelDownload() {
cancelDownload(download_to_cancel = null) {
// if one is provided, cancel that one. otherwise, remove the current one
if (download_to_cancel) {
this.removeDownloadFromCurrentDownloads(download_to_cancel)
return;
}
this.downloadingfile = false;
this.current_download.downloading = false;
this.current_download = null;
@@ -521,6 +562,16 @@ export class MainComponent implements OnInit {
}
}
removeDownloadFromCurrentDownloads(download_to_remove) {
const index = this.downloads.indexOf(download_to_remove);
if (index !== -1) {
this.downloads.splice(index, 1);
return true;
} else {
return false;
}
}
downloadAudioFile(name) {
this.downloading_content['audio'][name] = true;
this.postsService.downloadFileFromServer(name, 'audio').subscribe(res => {
@@ -687,6 +738,10 @@ export class MainComponent implements OnInit {
localStorage.setItem('audioOnly', new_val.checked.toString());
}
multiDownloadModeChanged(new_val) {
localStorage.setItem('multiDownloadMode', new_val.checked.toString());
}
getAudioAndVideoFormats(formats): any[] {
const audio_formats = {};
const video_formats = {};
@@ -770,6 +825,22 @@ export class MainComponent implements OnInit {
}
}
accordionOpened(type) {
if (type === 'audio') {
audioFilesOpened = true;
} else if (type === 'video') {
videoFilesOpened = true;
}
}
accordionClosed(type) {
if (type === 'audio') {
audioFilesOpened = false;
} else if (type === 'video') {
videoFilesOpened = false;
}
}
// creating a playlist
openCreatePlaylistDialog(type) {
const dialogRef = this.dialog.open(CreatePlaylistComponent, {

View File

@@ -18,7 +18,8 @@
"title_top": "Youtube Downloader",
"file_manager_enabled": true,
"allow_quality_select": true,
"download_only_mode": false
"download_only_mode": false,
"allow_multi_download_mode": true
},
"API": {
"use_youtube_API": false,

View File

@@ -10,7 +10,7 @@
<link href="https://fonts.googleapis.com/css?family=Roboto&display=swap" rel="stylesheet">
<link rel="icon" type="image/x-icon" href="favicon.ico">
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/css/bootstrap.min.css" integrity="sha384-Gn5384xqQ1aoWXA+058RXPxPg6fy4IWvTNh0E263XmFcJlSAwiGgFAW/dAiS6JXm" crossorigin="anonymous">
<link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet">
<link href="https://fonts.googleapis.com/icon?family=Material+Icons|Material+Icons+Outlined" rel="stylesheet"/>
</head>
<body>
<app-root></app-root>