mirror of
https://github.com/Tzahi12345/YoutubeDL-Material.git
synced 2026-03-07 20:10:03 +03:00
Improved arg simulation -- now uses same method as the actual download
Added checkbox for advanced custom args to either replace all args or append
This commit is contained in:
@@ -987,8 +987,9 @@ app.post('/api/downloadFile', optionalJwt, async function(req, res) {
|
||||
const url = req.body.url;
|
||||
const type = req.body.type;
|
||||
const user_uid = req.isAuthenticated() ? req.user.uid : null;
|
||||
var options = {
|
||||
const options = {
|
||||
customArgs: req.body.customArgs,
|
||||
additionalArgs: req.body.additionalArgs,
|
||||
customOutput: req.body.customOutput,
|
||||
selectedHeight: req.body.selectedHeight,
|
||||
customQualityConfiguration: req.body.customQualityConfiguration,
|
||||
@@ -996,7 +997,7 @@ app.post('/api/downloadFile', optionalJwt, async function(req, res) {
|
||||
youtubePassword: req.body.youtubePassword,
|
||||
ui_uid: req.body.ui_uid,
|
||||
cropFileSettings: req.body.cropFileSettings
|
||||
}
|
||||
};
|
||||
|
||||
const download = await downloader_api.createDownload(url, type, options, user_uid);
|
||||
|
||||
@@ -1012,6 +1013,26 @@ app.post('/api/killAllDownloads', optionalJwt, async function(req, res) {
|
||||
res.send(result_obj);
|
||||
});
|
||||
|
||||
app.post('/api/generateArgs', optionalJwt, async function(req, res) {
|
||||
const url = req.body.url;
|
||||
const type = req.body.type;
|
||||
const user_uid = req.isAuthenticated() ? req.user.uid : null;
|
||||
const options = {
|
||||
customArgs: req.body.customArgs,
|
||||
additionalArgs: req.body.additionalArgs,
|
||||
customOutput: req.body.customOutput,
|
||||
selectedHeight: req.body.selectedHeight,
|
||||
customQualityConfiguration: req.body.customQualityConfiguration,
|
||||
youtubeUsername: req.body.youtubeUsername,
|
||||
youtubePassword: req.body.youtubePassword,
|
||||
ui_uid: req.body.ui_uid,
|
||||
cropFileSettings: req.body.cropFileSettings
|
||||
};
|
||||
|
||||
const args = await downloader_api.generateArgs(url, type, options, user_uid, true);
|
||||
res.send({args: args});
|
||||
});
|
||||
|
||||
// gets all download mp3s
|
||||
app.get('/api/getMp3s', optionalJwt, async function(req, res) {
|
||||
// TODO: simplify
|
||||
|
||||
@@ -369,7 +369,7 @@ async function downloadQueuedFile(download_uid) {
|
||||
|
||||
// helper functions
|
||||
|
||||
exports.generateArgs = async (url, type, options, user_uid = null) => {
|
||||
exports.generateArgs = async (url, type, options, user_uid = null, simulated = false) => {
|
||||
const audioFolderPath = config_api.getConfigItem('ytdl_audio_folder_path');
|
||||
const videoFolderPath = config_api.getConfigItem('ytdl_video_folder_path');
|
||||
|
||||
@@ -510,7 +510,7 @@ exports.generateArgs = async (url, type, options, user_uid = null) => {
|
||||
// filter out incompatible args
|
||||
downloadConfig = filterArgs(downloadConfig, is_audio);
|
||||
|
||||
logger.verbose(`youtube-dl args being used: ${downloadConfig.join(',')}`);
|
||||
if (!simulated) logger.verbose(`youtube-dl args being used: ${downloadConfig.join(',')}`);
|
||||
return downloadConfig;
|
||||
}
|
||||
|
||||
|
||||
@@ -129,7 +129,9 @@ mat-form-field.mat-form-field {
|
||||
}
|
||||
|
||||
.edit-button {
|
||||
margin-left: 10px;
|
||||
margin-left: 5px;
|
||||
margin-top: -6px;
|
||||
margin-bottom: -5px;
|
||||
top: -5px;
|
||||
}
|
||||
|
||||
|
||||
@@ -111,8 +111,13 @@
|
||||
</ng-container>
|
||||
</mat-checkbox>
|
||||
<button class="edit-button" (click)="openArgsModifierDialog()" mat-icon-button><mat-icon>edit</mat-icon></button>
|
||||
<mat-checkbox color="accent" [disabled]="!customArgsEnabled || current_download" (change)="replaceArgsChanged($event)" [(ngModel)]="replaceArgs" style="z-index: 999; margin-left: 10px" [ngModelOptions]="{standalone: true}">
|
||||
<ng-container i18n="Replace args">
|
||||
Replace args
|
||||
</ng-container>
|
||||
</mat-checkbox>
|
||||
<mat-form-field color="accent" style="margin-bottom: 42px;" class="advanced-input">
|
||||
<input [(ngModel)]="customArgs" [ngModelOptions]="{standalone: true}" [disabled]="!customArgsEnabled" matInput placeholder="Custom args" i18n-placeholder="Custom args placeholder">
|
||||
<input [(ngModel)]="customArgs" [ngModelOptions]="{standalone: true}" [disabled]="!customArgsEnabled" matInput (ngModelChange)="argChanged()" placeholder="Custom args" i18n-placeholder="Custom args placeholder">
|
||||
<mat-hint>
|
||||
<ng-container i18n="Custom Args input hint">
|
||||
No need to include URL, just everything after. Args are delimited using two commas like so: ,,
|
||||
@@ -127,7 +132,7 @@
|
||||
</ng-container>
|
||||
</mat-checkbox>
|
||||
<mat-form-field style="margin-bottom: 42px;" color="accent" class="advanced-input">
|
||||
<input [(ngModel)]="customOutput" [ngModelOptions]="{standalone: true}" [disabled]="!customOutputEnabled" matInput placeholder="Custom output" i18n-placeholder="Custom output placeholder">
|
||||
<input [(ngModel)]="customOutput" [ngModelOptions]="{standalone: true}" [disabled]="!customOutputEnabled" matInput (ngModelChange)="argChanged()" placeholder="Custom output" i18n-placeholder="Custom output placeholder">
|
||||
<mat-hint><a target="_blank" href="https://github.com/ytdl-org/youtube-dl/blob/master/README.md#output-template">
|
||||
<ng-container i18n="Youtube-dl 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>
|
||||
@@ -140,13 +145,13 @@
|
||||
Use authentication
|
||||
</ng-container>
|
||||
</mat-checkbox>
|
||||
<mat-form-field color="accent" class="advanced-input">
|
||||
<input [(ngModel)]="youtubeUsername" [ngModelOptions]="{standalone: true}" [disabled]="!youtubeAuthEnabled" matInput placeholder="Username" i18n-placeholder="YT Username placeholder">
|
||||
<mat-form-field *ngIf="youtubeAuthEnabled" color="accent" class="advanced-input">
|
||||
<input [(ngModel)]="youtubeUsername" [ngModelOptions]="{standalone: true}" matInput (ngModelChange)="argChanged()" placeholder="Username" i18n-placeholder="YT Username placeholder">
|
||||
</mat-form-field>
|
||||
</div>
|
||||
<div *ngIf="!youtubeAuthDisabledOverride" class="col-12 col-sm-6 mt-3">
|
||||
<mat-form-field style="margin-top: 31px;" color="accent" class="advanced-input">
|
||||
<input [(ngModel)]="youtubePassword" type="password" [ngModelOptions]="{standalone: true}" [disabled]="!youtubeAuthEnabled" matInput placeholder="Password" i18n-placeholder="YT Password placeholder">
|
||||
<mat-form-field *ngIf="youtubeAuthEnabled" style="margin-top: 31px;" color="accent" class="advanced-input">
|
||||
<input [(ngModel)]="youtubePassword" type="password" [ngModelOptions]="{standalone: true}" matInput (ngModelChange)="argChanged()" placeholder="Password" i18n-placeholder="YT Password placeholder">
|
||||
</mat-form-field>
|
||||
</div>
|
||||
<div class="col-12 col-sm-6 mt-3">
|
||||
@@ -155,13 +160,13 @@
|
||||
Crop file
|
||||
</ng-container>
|
||||
</mat-checkbox>
|
||||
<mat-form-field color="accent" class="advanced-input">
|
||||
<input [(ngModel)]="cropFileStart" type="number" [ngModelOptions]="{standalone: true}" [disabled]="!cropFile" matInput placeholder="Crop from (seconds)" i18n-placeholder="Crop from placeholder">
|
||||
<mat-form-field *ngIf="cropFile" color="accent" class="advanced-input">
|
||||
<input [(ngModel)]="cropFileStart" type="number" [ngModelOptions]="{standalone: true}" matInput placeholder="Crop from (seconds)" i18n-placeholder="Crop from placeholder">
|
||||
</mat-form-field>
|
||||
</div>
|
||||
<div class="col-12 col-sm-6 mt-3">
|
||||
<mat-form-field style="margin-top: 31px;" color="accent" class="advanced-input">
|
||||
<input [(ngModel)]="cropFileEnd" type="number" [ngModelOptions]="{standalone: true}" [disabled]="!cropFile" matInput placeholder="Crop to (seconds)" i18n-placeholder="Crop to placeholder">
|
||||
<mat-form-field *ngIf="cropFile" style="margin-top: 31px;" color="accent" class="advanced-input">
|
||||
<input [(ngModel)]="cropFileEnd" type="number" [ngModelOptions]="{standalone: true}" matInput placeholder="Crop to (seconds)" i18n-placeholder="Crop to placeholder">
|
||||
</mat-form-field>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { Component, OnInit, ElementRef, ViewChild, ViewChildren, QueryList } from '@angular/core';
|
||||
import {PostsService} from '../posts.services';
|
||||
import {FileCardComponent} from '../file-card/file-card.component';
|
||||
import { Observable } from 'rxjs';
|
||||
import { Observable, Subject } from 'rxjs';
|
||||
import {FormControl, Validators} from '@angular/forms';
|
||||
import { MatDialog } from '@angular/material/dialog';
|
||||
import { MatSnackBar } from '@angular/material/snack-bar';
|
||||
@@ -9,7 +9,6 @@ import { saveAs } from 'file-saver';
|
||||
import { YoutubeSearchService, Result } from '../youtube-search.service';
|
||||
import { Router, ActivatedRoute } from '@angular/router';
|
||||
import { Platform } from '@angular/cdk/platform';
|
||||
import { v4 as uuid } from 'uuid';
|
||||
import { ArgModifierDialogComponent } from 'app/dialogs/arg-modifier-dialog/arg-modifier-dialog.component';
|
||||
import { RecentVideosComponent } from 'app/components/recent-videos/recent-videos.component';
|
||||
|
||||
@@ -50,6 +49,7 @@ export class MainComponent implements OnInit {
|
||||
customArgsEnabled = false;
|
||||
customArgs = null;
|
||||
customOutputEnabled = false;
|
||||
replaceArgs = false;
|
||||
customOutput = null;
|
||||
youtubeAuthEnabled = false;
|
||||
youtubeUsername = null;
|
||||
@@ -212,6 +212,7 @@ export class MainComponent implements OnInit {
|
||||
error: false
|
||||
};
|
||||
|
||||
argsChangedSubject: Subject<boolean> = new Subject<boolean>(false);
|
||||
simulatedOutput = '';
|
||||
|
||||
constructor(public postsService: PostsService, private youtubeSearch: YoutubeSearchService, public snackBar: MatSnackBar,
|
||||
@@ -224,8 +225,6 @@ export class MainComponent implements OnInit {
|
||||
if (this.autoStartDownload) {
|
||||
this.downloadClicked();
|
||||
}
|
||||
|
||||
setInterval(() => this.getSimulatedOutput(), 1000);
|
||||
}
|
||||
|
||||
async loadConfig() {
|
||||
@@ -261,6 +260,10 @@ export class MainComponent implements OnInit {
|
||||
this.customOutputEnabled = localStorage.getItem('customOutputEnabled') === 'true';
|
||||
}
|
||||
|
||||
if (localStorage.getItem('replaceArgs') !== null) {
|
||||
this.replaceArgs = localStorage.getItem('replaceArgs') === 'true';
|
||||
}
|
||||
|
||||
if (localStorage.getItem('youtubeAuthEnabled') !== null) {
|
||||
this.youtubeAuthEnabled = localStorage.getItem('youtubeAuthEnabled') === 'true';
|
||||
}
|
||||
@@ -323,6 +326,12 @@ export class MainComponent implements OnInit {
|
||||
this.autoStartDownload = true;
|
||||
}
|
||||
|
||||
this.argsChangedSubject
|
||||
.debounceTime(500)
|
||||
.subscribe((should_simulate) => {
|
||||
if (should_simulate) this.getSimulatedOutput();
|
||||
});
|
||||
|
||||
this.setCols();
|
||||
}
|
||||
|
||||
@@ -412,7 +421,8 @@ export class MainComponent implements OnInit {
|
||||
this.urlError = false;
|
||||
|
||||
// get common args
|
||||
const customArgs = (this.customArgsEnabled ? this.customArgs : null);
|
||||
const customArgs = (this.customArgsEnabled && this.replaceArgs ? this.customArgs : null);
|
||||
const additionalArgs = (this.customArgsEnabled && !this.replaceArgs ? this.customArgs : null);
|
||||
const customOutput = (this.customOutputEnabled ? this.customOutput : null);
|
||||
const youtubeUsername = (this.youtubeAuthEnabled && this.youtubeUsername ? this.youtubeUsername : null);
|
||||
const youtubePassword = (this.youtubeAuthEnabled && this.youtubePassword ? this.youtubePassword : null);
|
||||
@@ -445,7 +455,7 @@ export class MainComponent implements OnInit {
|
||||
|
||||
this.downloadingfile = true;
|
||||
this.postsService.downloadFile(this.url, type, (this.selectedQuality === '' ? null : this.selectedQuality),
|
||||
customQualityConfiguration, customArgs, customOutput, youtubeUsername, youtubePassword, cropFileSettings).subscribe(res => {
|
||||
customQualityConfiguration, customArgs, additionalArgs, customOutput, youtubeUsername, youtubePassword, cropFileSettings).subscribe(res => {
|
||||
this.current_download = res['download'];
|
||||
this.downloads.push(res['download']);
|
||||
this.download_uids.push(res['download']['uid']);
|
||||
@@ -593,6 +603,7 @@ export class MainComponent implements OnInit {
|
||||
if (str !== this.last_valid_url && this.allowQualitySelect) {
|
||||
// get info
|
||||
this.getURLInfo(str);
|
||||
this.argsChangedSubject.next(true);
|
||||
}
|
||||
this.last_valid_url = str;
|
||||
}
|
||||
@@ -630,79 +641,44 @@ export class MainComponent implements OnInit {
|
||||
}
|
||||
}
|
||||
|
||||
getSimulatedOutput() {
|
||||
const customArgsExists = this.customArgsEnabled && this.customArgs;
|
||||
const globalArgsExists = this.globalCustomArgs && this.globalCustomArgs !== '';
|
||||
argChanged(): void {
|
||||
this.argsChangedSubject.next(true);
|
||||
}
|
||||
|
||||
let full_string_array: string[] = [];
|
||||
const base_string_array = ['youtube-dl', this.url];
|
||||
getSimulatedOutput(): void {
|
||||
// this function should be very similar to downloadFile()
|
||||
const customArgs = (this.customArgsEnabled && this.replaceArgs ? this.customArgs : null);
|
||||
const additionalArgs = (this.customArgsEnabled && !this.replaceArgs ? this.customArgs : null);
|
||||
const customOutput = (this.customOutputEnabled ? this.customOutput : null);
|
||||
const youtubeUsername = (this.youtubeAuthEnabled && this.youtubeUsername ? this.youtubeUsername : null);
|
||||
const youtubePassword = (this.youtubeAuthEnabled && this.youtubePassword ? this.youtubePassword : null);
|
||||
|
||||
if (customArgsExists) {
|
||||
this.simulatedOutput = base_string_array.join(' ') + ' ' + this.customArgs.split(',,').join(' ');
|
||||
return this.simulatedOutput;
|
||||
}
|
||||
const type = this.audioOnly ? 'audio' : 'video';
|
||||
|
||||
full_string_array.push(...base_string_array);
|
||||
const customQualityConfiguration = type === 'audio' ? this.getSelectedAudioFormat() : this.getSelectedVideoFormat();
|
||||
|
||||
const base_path = this.audioOnly ? this.audioFolderPath : this.videoFolderPath;
|
||||
const ext = this.audioOnly ? '.mp3' : '.mp4';
|
||||
// gets output
|
||||
let output_string_array = ['-o', base_path + '%(title)s' + ext];
|
||||
if (this.customOutputEnabled && this.customOutput) {
|
||||
output_string_array = ['-o', base_path + this.customOutput + ext];
|
||||
}
|
||||
// before pushing output, should check if using an external downloader
|
||||
if (!this.useDefaultDownloadingAgent && this.customDownloadingAgent === 'aria2c') {
|
||||
full_string_array.push('--external-downloader', 'aria2c');
|
||||
}
|
||||
// pushes output
|
||||
full_string_array.push(...output_string_array);
|
||||
let cropFileSettings = null;
|
||||
|
||||
// logic splits into audio and video modes
|
||||
if (this.audioOnly) {
|
||||
// adds base audio string
|
||||
const format_array = [];
|
||||
const audio_format = this.getSelectedAudioFormat();
|
||||
if (audio_format) {
|
||||
format_array.push('-f', audio_format);
|
||||
} else if (this.selectedQuality) {
|
||||
format_array.push('--audio-quality', this.selectedQuality['format_id']);
|
||||
if (this.cropFile) {
|
||||
cropFileSettings = {
|
||||
cropFileStart: this.cropFileStart,
|
||||
cropFileEnd: this.cropFileEnd
|
||||
}
|
||||
|
||||
// pushes formats
|
||||
full_string_array.splice(2, 0, ...format_array);
|
||||
|
||||
const additional_params = ['-x', '--audio-format', 'mp3', '--write-info-json', '--print-json'];
|
||||
|
||||
full_string_array.push(...additional_params);
|
||||
} else {
|
||||
// adds base video string
|
||||
let format_array = ['-f', 'bestvideo[ext=mp4]+bestaudio[ext=m4a]/mp4'];
|
||||
const video_format = this.getSelectedVideoFormat();
|
||||
if (video_format) {
|
||||
format_array = ['-f', video_format];
|
||||
} else if (this.selectedQuality) {
|
||||
format_array = [`bestvideo[height=${this.selectedQuality['format_id']}]+bestaudio/best[height=${this.selectedQuality}]`];
|
||||
}
|
||||
|
||||
// pushes formats
|
||||
full_string_array.splice(2, 0, ...format_array);
|
||||
|
||||
const additional_params = ['--write-info-json', '--print-json'];
|
||||
|
||||
full_string_array.push(...additional_params);
|
||||
}
|
||||
|
||||
if (this.use_youtubedl_archive) {
|
||||
full_string_array.push('--download-archive', 'archive.txt');
|
||||
}
|
||||
|
||||
if (globalArgsExists) {
|
||||
full_string_array = full_string_array.concat(this.globalCustomArgs.split(',,'));
|
||||
}
|
||||
|
||||
this.simulatedOutput = full_string_array.join(' ');
|
||||
return this.simulatedOutput;
|
||||
this.postsService.generateArgs(this.url, type, (this.selectedQuality === '' ? null : this.selectedQuality),
|
||||
customQualityConfiguration, customArgs, additionalArgs, customOutput, youtubeUsername, youtubePassword, cropFileSettings).subscribe(res => {
|
||||
const simulated_args = res['args'];
|
||||
if (simulated_args) {
|
||||
// hide password if needed
|
||||
const passwordIndex = simulated_args.indexOf('--password');
|
||||
console.log(passwordIndex);
|
||||
if (passwordIndex !== -1 && passwordIndex !== simulated_args.length - 1) {
|
||||
simulated_args[passwordIndex + 1] = simulated_args[passwordIndex + 1].replace(/./g, '*');
|
||||
}
|
||||
this.simulatedOutput = `youtube-dl ${this.url} ${simulated_args.join(' ')}`;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
errorFormats(url) {
|
||||
@@ -746,6 +722,7 @@ export class MainComponent implements OnInit {
|
||||
videoModeChanged(new_val) {
|
||||
this.selectedQuality = '';
|
||||
localStorage.setItem('audioOnly', new_val.checked.toString());
|
||||
this.argsChangedSubject.next(true);
|
||||
}
|
||||
|
||||
autoplayChanged(new_val) {
|
||||
@@ -754,29 +731,22 @@ export class MainComponent implements OnInit {
|
||||
|
||||
customArgsEnabledChanged(new_val) {
|
||||
localStorage.setItem('customArgsEnabled', new_val.checked.toString());
|
||||
if (new_val.checked === true && this.customOutputEnabled) {
|
||||
this.customOutputEnabled = false;
|
||||
localStorage.setItem('customOutputEnabled', 'false');
|
||||
this.argsChangedSubject.next(true);
|
||||
}
|
||||
|
||||
this.youtubeAuthEnabled = false;
|
||||
localStorage.setItem('youtubeAuthEnabled', 'false');
|
||||
}
|
||||
replaceArgsChanged(new_val) {
|
||||
localStorage.setItem('replaceArgs', new_val.checked.toString());
|
||||
this.argsChangedSubject.next(true);
|
||||
}
|
||||
|
||||
customOutputEnabledChanged(new_val) {
|
||||
localStorage.setItem('customOutputEnabled', new_val.checked.toString());
|
||||
if (new_val.checked === true && this.customArgsEnabled) {
|
||||
this.customArgsEnabled = false;
|
||||
localStorage.setItem('customArgsEnabled', 'false');
|
||||
}
|
||||
this.argsChangedSubject.next(true);
|
||||
}
|
||||
|
||||
youtubeAuthEnabledChanged(new_val) {
|
||||
localStorage.setItem('youtubeAuthEnabled', new_val.checked.toString());
|
||||
if (new_val.checked === true && this.customArgsEnabled) {
|
||||
this.customArgsEnabled = false;
|
||||
localStorage.setItem('customArgsEnabled', 'false');
|
||||
}
|
||||
this.argsChangedSubject.next(true);
|
||||
}
|
||||
|
||||
getAudioAndVideoFormats(formats) {
|
||||
|
||||
@@ -175,11 +175,25 @@ export class PostsService implements CanActivate {
|
||||
}
|
||||
|
||||
// tslint:disable-next-line: max-line-length
|
||||
downloadFile(url: string, type: string, selectedQuality: string, customQualityConfiguration: string, customArgs: string = null, customOutput: string = null, youtubeUsername: string = null, youtubePassword: string = null, cropFileSettings = null) {
|
||||
downloadFile(url: string, type: string, selectedQuality: string, customQualityConfiguration: string, customArgs: string = null, additionalArgs: string = null, customOutput: string = null, youtubeUsername: string = null, youtubePassword: string = null, cropFileSettings = null) {
|
||||
return this.http.post(this.path + 'downloadFile', {url: url,
|
||||
selectedHeight: selectedQuality,
|
||||
customQualityConfiguration: customQualityConfiguration,
|
||||
customArgs: customArgs,
|
||||
additionalArgs: additionalArgs,
|
||||
customOutput: customOutput,
|
||||
youtubeUsername: youtubeUsername,
|
||||
youtubePassword: youtubePassword,
|
||||
type: type,
|
||||
cropFileSettings: cropFileSettings}, this.httpOptions);
|
||||
}
|
||||
|
||||
generateArgs(url: string, type: string, selectedQuality: string, customQualityConfiguration: string, customArgs: string = null, additionalArgs: string = null, customOutput: string = null, youtubeUsername: string = null, youtubePassword: string = null, cropFileSettings = null) {
|
||||
return this.http.post(this.path + 'generateArgs', {url: url,
|
||||
selectedHeight: selectedQuality,
|
||||
customQualityConfiguration: customQualityConfiguration,
|
||||
customArgs: customArgs,
|
||||
additionalArgs: additionalArgs,
|
||||
customOutput: customOutput,
|
||||
youtubeUsername: youtubeUsername,
|
||||
youtubePassword: youtubePassword,
|
||||
|
||||
Reference in New Issue
Block a user